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