1 /******************************************************************************
2 * Project: Selafin importer
3 * Purpose: Implementation of OGR driver for Selafin files.
4 * Author: François Hissel, francois.hissel@gmail.com
5 *
6 ******************************************************************************
7 * Copyright (c) 2014, François Hissel <francois.hissel@gmail.com>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 ****************************************************************************/
27
28 #include "ogr_selafin.h"
29 #include "cpl_conv.h"
30 #include "cpl_string.h"
31 #include "io_selafin.h"
32
33 CPL_CVSID("$Id: ogrselafindriver.cpp 1761acd90777d5bcc49eddbc13c193098f0ed40b 2020-10-01 12:12:00 +0200 Even Rouault $")
34
35 /************************************************************************/
36 /* OGRSelafinDriverIdentify() */
37 /************************************************************************/
38
OGRSelafinDriverIdentify(GDALOpenInfo * poOpenInfo)39 static int OGRSelafinDriverIdentify( GDALOpenInfo* poOpenInfo )
40 {
41 if( poOpenInfo->fpL != nullptr )
42 {
43 if( poOpenInfo->nHeaderBytes < 84 + 8 )
44 return FALSE;
45 if (poOpenInfo->pabyHeader[0]!=0 || poOpenInfo->pabyHeader[1]!=0 ||
46 poOpenInfo->pabyHeader[2]!=0 || poOpenInfo->pabyHeader[3]!=0x50)
47 return FALSE;
48
49 if (poOpenInfo->pabyHeader[84+0]!=0 || poOpenInfo->pabyHeader[84+1]!=0 ||
50 poOpenInfo->pabyHeader[84+2]!=0 || poOpenInfo->pabyHeader[84+3]!=0x50 ||
51 poOpenInfo->pabyHeader[84+4]!=0 || poOpenInfo->pabyHeader[84+5]!=0 ||
52 poOpenInfo->pabyHeader[84+6]!=0 || poOpenInfo->pabyHeader[84+7]!=8)
53 return FALSE;
54
55 return TRUE;
56 }
57 return -1;
58 }
59
60 /************************************************************************/
61 /* OGRSelafinDriverOpen() */
62 /************************************************************************/
63
OGRSelafinDriverOpen(GDALOpenInfo * poOpenInfo)64 static GDALDataset *OGRSelafinDriverOpen( GDALOpenInfo* poOpenInfo ) {
65
66 if( OGRSelafinDriverIdentify(poOpenInfo) == 0 )
67 return nullptr;
68
69 OGRSelafinDataSource *poDS = new OGRSelafinDataSource();
70 if( !poDS->Open(poOpenInfo->pszFilename, poOpenInfo->eAccess == GA_Update,
71 FALSE) )
72 {
73 delete poDS;
74 poDS = nullptr;
75 }
76 return poDS;
77 }
78
79 /************************************************************************/
80 /* OGRSelafinDriverCreate() */
81 /************************************************************************/
82
OGRSelafinDriverCreate(const char * pszName,CPL_UNUSED int nXSize,CPL_UNUSED int nYSize,CPL_UNUSED int nBands,CPL_UNUSED GDALDataType eDT,char ** papszOptions)83 static GDALDataset *OGRSelafinDriverCreate( const char * pszName,
84 CPL_UNUSED int nXSize,
85 CPL_UNUSED int nYSize,
86 CPL_UNUSED int nBands,
87 CPL_UNUSED GDALDataType eDT,
88 char **papszOptions ) {
89 // First, ensure there isn't any such file yet.
90 VSIStatBufL sStatBuf;
91 if (strcmp(pszName, "/dev/stdout") == 0) pszName = "/vsistdout/";
92 if( VSIStatL( pszName, &sStatBuf ) == 0 ) {
93 CPLError(CE_Failure, CPLE_AppDefined,"It seems a file system object called '%s' already exists.",pszName);
94 return nullptr;
95 }
96 // Parse options
97 const char *pszTemp=CSLFetchNameValue(papszOptions,"TITLE");
98 char pszTitle[81];
99 int pnDate[6]={-1,0};
100 if (pszTemp!=nullptr) strncpy(pszTitle,pszTemp,72); else memset(pszTitle,' ',72);
101 pszTemp=CSLFetchNameValue(papszOptions,"DATE");
102 if (pszTemp!=nullptr) {
103 const char* pszErrorMessage="Wrong format for date parameter: must be \"%%Y-%%m-%%d_%%H:%%M:%%S\", ignored";
104 const char *pszc=pszTemp;
105 pnDate[0]=atoi(pszTemp);
106 if (pnDate[0]<=0) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage); else {
107 if (pnDate[0]<100) pnDate[0]+=2000;
108 }
109 while (*pszc!=0 && *pszc!='-') ++pszc;
110 pnDate[1]=atoi(pszc);
111 if (pnDate[1]<0 || pnDate[1]>12) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage);
112 while (*pszc!=0 && *pszc!='_') ++pszc;
113 pnDate[2]=atoi(pszc);
114 if (pnDate[2]<0 || pnDate[2]>59) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage);
115 while (*pszc!=0 && *pszc!='_') ++pszc;
116 pnDate[3]=atoi(pszc);
117 if (pnDate[3]<0 || pnDate[3]>23) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage);
118 while (*pszc!=0 && *pszc!=':') ++pszc;
119 pnDate[4]=atoi(pszc);
120 if (pnDate[4]<0 || pnDate[4]>59) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage);
121 while (*pszc!=0 && *pszc!=':') ++pszc;
122 pnDate[5]=atoi(pszc);
123 if (pnDate[5]<0 || pnDate[5]>59) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage);
124 }
125 // Create the skeleton of a Selafin file
126 VSILFILE *fp=VSIFOpenL(pszName,"wb");
127 if (fp==nullptr) {
128 CPLError(CE_Failure, CPLE_AppDefined,"Unable to open %s with write access.",pszName);
129 return nullptr;
130 }
131 strncpy(pszTitle+72,"SERAPHIN",9);
132 bool bError=false;
133 if (Selafin::write_string(fp,pszTitle,80)==0) bError=true;
134 int pnTemp[10]={0};
135 if (Selafin::write_intarray(fp,pnTemp,2)==0) bError=true;
136 if (pnDate[0]>=0) pnTemp[9]=1;
137 if (Selafin::write_intarray(fp,pnTemp,10)==0) bError=true;
138 if (pnDate[0]>=0) {
139 if (Selafin::write_intarray(fp,pnTemp,6)==0) bError=true;
140 }
141 pnTemp[3]=1;
142 if (Selafin::write_intarray(fp,pnTemp,4)==0) bError=true;
143 if (Selafin::write_intarray(fp,pnTemp,0)==0) bError=true;
144 if (Selafin::write_intarray(fp,pnTemp,0)==0) bError=true;
145 if (Selafin::write_floatarray(fp,nullptr,0)==0) bError=true;
146 if (Selafin::write_floatarray(fp,nullptr,0)==0) bError=true;
147 VSIFCloseL(fp);
148 if (bError) {
149 CPLError(CE_Failure, CPLE_AppDefined,"Error writing to file %s.",pszName);
150 return nullptr;
151 }
152 // Force it to open as a datasource
153 OGRSelafinDataSource *poDS = new OGRSelafinDataSource();
154 if( !poDS->Open( pszName, TRUE, TRUE ) )
155 {
156 delete poDS;
157 return nullptr;
158 }
159 return poDS;
160 }
161
162 /************************************************************************/
163 /* OGRSelafinDriverDelete() */
164 /************************************************************************/
OGRSelafinDriverDelete(const char * pszFilename)165 static CPLErr OGRSelafinDriverDelete( const char *pszFilename ) {
166 if( CPLUnlinkTree( pszFilename ) == 0 ) return CE_None;
167 else return CE_Failure;
168 }
169
170 /************************************************************************/
171 /* RegisterOGRSelafin() */
172 /************************************************************************/
173
RegisterOGRSelafin()174 void RegisterOGRSelafin() {
175
176 if( GDALGetDriverByName( "Selafin" ) != nullptr )
177 return;
178
179 GDALDriver *poDriver = new GDALDriver();
180
181 poDriver->SetDescription( "Selafin" );
182 poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "Selafin" );
183 poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "Selafin" );
184 poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "drivers/vector/selafin.html" );
185
186 poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
187 "<CreationOptionList>"
188 " <Option name='TITLE' type='string' description='Title of the datasource, stored in the Selafin file. The title must not hold more than 72 characters.'/>"
189 " <Option name='DATE' type='string' description='Starting date of the simulation. Each layer in a Selafin file is characterized by a date, counted in seconds since a reference date. This option allows providing the reference date. The format of this field must be YYYY-MM-DD_hh:mm:ss'/>"
190 "</CreationOptionList>");
191 poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST,
192 "<LayerCreationOptionList>"
193 " <Option name='DATE' type='float' description='Date of the time step, in seconds, relative to the starting date of the simulation.'/>"
194 "</LayerCreationOptionList>");
195
196 poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
197
198 poDriver->pfnOpen = OGRSelafinDriverOpen;
199 poDriver->pfnIdentify = OGRSelafinDriverIdentify;
200 poDriver->pfnCreate = OGRSelafinDriverCreate;
201 poDriver->pfnDelete = OGRSelafinDriverDelete;
202
203 GetGDALDriverManager()->RegisterDriver( poDriver );
204 }
205