1 /******************************************************************************
2  *
3  * Project:  VDV Translator
4  * Purpose:  Implements OGRVDVFDriver.
5  * Author:   Even Rouault, even.rouault at spatialys.com
6  *
7  ******************************************************************************
8  * Copyright (c) 2015, Even Rouault <even.rouault at spatialys.com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #include "ogr_vdv.h"
30 #include "cpl_conv.h"
31 #include "cpl_time.h"
32 #include <map>
33 
34 CPL_CVSID("$Id: ogrvdvdatasource.cpp 1761acd90777d5bcc49eddbc13c193098f0ed40b 2020-10-01 12:12:00 +0200 Even Rouault $")
35 
36 #ifndef STARTS_WITH_CI
37 #define STARTS_WITH(a,b)               (strncmp(a,b,strlen(b)) == 0)
38 #define STARTS_WITH_CI(a,b)            EQUALN(a,b,strlen(b))
39 #endif
40 
41 typedef enum
42 {
43     LAYER_OTHER,
44     LAYER_NODE,
45     LAYER_LINK,
46     LAYER_LINKCOORDINATE
47 } IDFLayerType;
48 
49 /************************************************************************/
50 /*                          OGRVDVParseAtrFrm()                         */
51 /************************************************************************/
52 
OGRVDVParseAtrFrm(OGRFeatureDefn * poFeatureDefn,char ** papszAtr,char ** papszFrm)53 static void OGRVDVParseAtrFrm(OGRFeatureDefn* poFeatureDefn,
54                               char** papszAtr,
55                               char** papszFrm)
56 {
57     for(int i=0; papszAtr[i]; i++)
58     {
59         OGRFieldType eType = OFTString;
60         int nWidth = 0;
61         OGRFieldSubType eSubType = OFSTNone;
62         if( STARTS_WITH_CI(papszFrm[i], "decimal") )
63         {
64             if( papszFrm[i][strlen("decimal")] == '(' )
65             {
66                 if( strchr(papszFrm[i], ',') &&
67                     atoi(strchr(papszFrm[i], ',')+1) > 0 )
68                 {
69                     eType = OFTReal;
70                 }
71                 else
72                 {
73                     nWidth = atoi(papszFrm[i] + strlen("decimal") + 1);
74                     if( nWidth >= 10 )
75                         eType = OFTInteger64;
76                     else
77                         eType = OFTInteger;
78                 }
79             }
80             else
81                 eType = OFTInteger;
82         }
83         else if( STARTS_WITH_CI(papszFrm[i], "num") )
84         {
85             if( papszFrm[i][strlen("num")] == '[' )
86             {
87                 if( strchr(papszFrm[i], '.') &&
88                     atoi(strchr(papszFrm[i], '.')+1) > 0 )
89                 {
90                     eType = OFTReal;
91                 }
92                 else
93                 {
94                     nWidth = atoi(papszFrm[i] + strlen("num") + 1);
95                     if( nWidth < 0 || nWidth >= 100 )
96                     {
97                         nWidth = 0;
98                         eType = OFTInteger;
99                     }
100                     else
101                     {
102                         nWidth += 1; /* VDV-451 width is without sign */
103                         if( nWidth >= 10 )
104                             eType = OFTInteger64;
105                         else
106                             eType = OFTInteger;
107                     }
108                 }
109             }
110             else
111                 eType = OFTInteger;
112         }
113         else if( STARTS_WITH_CI(papszFrm[i], "char") )
114         {
115             if( papszFrm[i][strlen("char")] == '[' )
116             {
117                 nWidth = atoi(papszFrm[i] + strlen("char") + 1);
118                 if( nWidth < 0 )
119                     nWidth = 0;
120             }
121         }
122         else if( STARTS_WITH_CI(papszFrm[i], "boolean") )
123         {
124             eType = OFTInteger;
125             eSubType = OFSTBoolean;
126         }
127         OGRFieldDefn oFieldDefn(papszAtr[i], eType);
128         oFieldDefn.SetSubType(eSubType);
129         oFieldDefn.SetWidth(nWidth);
130         poFeatureDefn->AddFieldDefn(&oFieldDefn);
131     }
132 }
133 
134 /************************************************************************/
135 /*                           OGRIDFDataSource()                         */
136 /************************************************************************/
137 
OGRIDFDataSource(const char * pszFilename,VSILFILE * fpLIn)138 OGRIDFDataSource::OGRIDFDataSource(const char* pszFilename, VSILFILE* fpLIn) :
139     m_osFilename(pszFilename),
140     m_fpL(fpLIn),
141     m_bHasParsed(false),
142     m_poTmpDS(nullptr)
143 {}
144 
145 /************************************************************************/
146 /*                          ~OGRIDFDataSource()                         */
147 /************************************************************************/
148 
~OGRIDFDataSource()149 OGRIDFDataSource::~OGRIDFDataSource()
150 {
151     CPLString osTmpFilename;
152     if( m_bDestroyTmpDS && m_poTmpDS )
153     {
154         osTmpFilename = m_poTmpDS->GetDescription();
155     }
156     delete m_poTmpDS;
157     if( m_bDestroyTmpDS )
158     {
159         VSIUnlink(osTmpFilename);
160     }
161     if( m_fpL )
162     {
163         VSIFCloseL(m_fpL);
164     }
165 }
166 
167 /************************************************************************/
168 /*                                Parse()                               */
169 /************************************************************************/
170 
Parse()171 void OGRIDFDataSource::Parse()
172 {
173     m_bHasParsed = true;
174 
175     GDALDriver* poMEMDriver = reinterpret_cast<GDALDriver*>(
176         GDALGetDriverByName("MEMORY"));
177     if( poMEMDriver == nullptr )
178         return;
179 
180     VSIStatBufL sStatBuf;
181     bool bGPKG = false;
182     vsi_l_offset nFileSize = 0;
183     bool bSpatialIndex = false;
184     if( VSIStatL(m_osFilename, &sStatBuf) == 0 &&
185         sStatBuf.st_size > CPLAtoGIntBig(
186             CPLGetConfigOption("OGR_IDF_TEMP_DB_THRESHOLD", "100000000")) )
187     {
188         nFileSize = sStatBuf.st_size;
189 
190         GDALDriver* poGPKGDriver = reinterpret_cast<GDALDriver*>(
191             GDALGetDriverByName("GPKG"));
192         if( poGPKGDriver )
193         {
194             CPLString osTmpFilename(m_osFilename + "_tmp.gpkg");
195             VSILFILE* fp = VSIFOpenL(osTmpFilename, "wb");
196             if( fp )
197             {
198                 VSIFCloseL(fp);
199             }
200             else
201             {
202                 osTmpFilename =
203                     CPLGenerateTempFilename(CPLGetBasename(m_osFilename));
204                 osTmpFilename += ".gpkg";
205             }
206             VSIUnlink(osTmpFilename);
207             CPLString osOldVal = CPLGetConfigOption("OGR_SQLITE_JOURNAL", "");
208             CPLSetThreadLocalConfigOption("OGR_SQLITE_JOURNAL", "OFF");
209             m_poTmpDS = poGPKGDriver->Create(
210                 osTmpFilename, 0, 0, 0, GDT_Unknown, nullptr);
211             CPLSetThreadLocalConfigOption("OGR_SQLITE_JOURNAL",
212                 !osOldVal.empty() ? osOldVal.c_str() : nullptr);
213             bGPKG = m_poTmpDS != nullptr;
214             m_bDestroyTmpDS =
215                 CPLTestBool(
216                     CPLGetConfigOption("OGR_IDF_DELETE_TEMP_DB", "YES")) &&
217                 m_poTmpDS != nullptr;
218             if( m_bDestroyTmpDS )
219             {
220                 CPLPushErrorHandler(CPLQuietErrorHandler);
221                 m_bDestroyTmpDS = VSIUnlink(osTmpFilename) != 0;
222                 CPLPopErrorHandler();
223             }
224             else
225             {
226                 bSpatialIndex = true;
227             }
228         }
229     }
230 
231     if( m_poTmpDS == nullptr )
232     {
233         m_poTmpDS = poMEMDriver->Create("", 0, 0, 0, GDT_Unknown, nullptr);
234     }
235 
236     m_poTmpDS->StartTransaction();
237 
238     OGRLayer* poCurLayer = nullptr;
239     struct Point
240     {
241         double x;
242         double y;
243         double z;
244         Point(double xIn = 0, double yIn = 0, double zIn = 0):
245             x(xIn), y(yIn), z(zIn) {}
246     };
247     std::map<GIntBig, Point > oMapNode; // map from NODE_ID to Point
248     std::map<GIntBig, OGRLineString*> oMapLinkCoordinate; // map from LINK_ID to OGRLineString*
249     CPLString osTablename, osAtr, osFrm;
250     int iX = -1, iY = -1, iZ = -1;
251     bool bAdvertiseUTF8 = false;
252     bool bRecodeFromLatin1 = false;
253     int iNodeID = -1;
254     int iLinkID = -1;
255     int iFromNode = -1;
256     int iToNode = -1;
257     IDFLayerType eLayerType = LAYER_OTHER;
258 
259     // We assume that layers are in the order Node, Link, LinkCoordinate
260 
261     GUIntBig nLineCount = 0;
262     while( true )
263     {
264         if( nFileSize )
265         {
266             ++ nLineCount;
267             if( (nLineCount % 32768) == 0 )
268             {
269                 const vsi_l_offset nPos = VSIFTellL(m_fpL);
270                 CPLDebug("IDF", "Reading progress: %.2f %%",
271                          100.0 * nPos / nFileSize);
272             }
273         }
274 
275         const char* pszLine = CPLReadLineL(m_fpL);
276         if( pszLine == nullptr )
277             break;
278 
279         if( strcmp(pszLine, "chs;ISO_LATIN_1") == 0)
280         {
281             bAdvertiseUTF8 = true;
282             bRecodeFromLatin1 = true;
283         }
284         else if( STARTS_WITH(pszLine, "tbl;") )
285         {
286             poCurLayer = nullptr;
287             osTablename = pszLine + 4;
288             osAtr = "";
289             osFrm = "";
290             iX = iY = iNodeID = iLinkID = iFromNode = iToNode = -1;
291             eLayerType = LAYER_OTHER;
292         }
293         else if( STARTS_WITH(pszLine, "atr;") )
294         {
295             osAtr = pszLine + 4;
296             osAtr.Trim();
297         }
298         else if( STARTS_WITH(pszLine, "frm;") )
299         {
300             osFrm = pszLine + 4;
301             osFrm.Trim();
302         }
303         else if( STARTS_WITH(pszLine, "rec;") )
304         {
305             if( poCurLayer == nullptr )
306             {
307                 char** papszAtr = CSLTokenizeString2(osAtr,";",
308                         CSLT_ALLOWEMPTYTOKENS|CSLT_STRIPLEADSPACES|CSLT_STRIPENDSPACES);
309                 char** papszFrm = CSLTokenizeString2(osFrm,";",
310                         CSLT_ALLOWEMPTYTOKENS|CSLT_STRIPLEADSPACES|CSLT_STRIPENDSPACES);
311                 char* apszOptions[2] = { nullptr, nullptr };
312                 if( bAdvertiseUTF8 && !bGPKG )
313                     apszOptions[0] = (char*)"ADVERTIZE_UTF8=YES";
314                 else if( bGPKG && !bSpatialIndex )
315                     apszOptions[0] = (char*)"SPATIAL_INDEX=NO";
316 
317                 if( EQUAL(osTablename, "Node") &&
318                     (iX = CSLFindString(papszAtr, "X")) >= 0 &&
319                     (iY = CSLFindString(papszAtr, "Y")) >= 0 )
320                 {
321                     iZ = CSLFindString(papszAtr, "Z");
322                     eLayerType = LAYER_NODE;
323                     iNodeID = CSLFindString(papszAtr, "NODE_ID");
324                     OGRSpatialReference* poSRS = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
325                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
326                     poCurLayer = m_poTmpDS->CreateLayer(osTablename, poSRS,
327                             iZ < 0 ? wkbPoint : wkbPoint25D, apszOptions);
328                     poSRS->Release();
329                 }
330                 else if( EQUAL(osTablename, "Link") &&
331                         (iLinkID = CSLFindString(papszAtr, "LINK_ID")) >= 0 &&
332                         ((iFromNode = CSLFindString(papszAtr, "FROM_NODE")) >= 0) &&
333                         ((iToNode = CSLFindString(papszAtr, "TO_NODE")) >= 0) )
334                 {
335                     eLayerType = LAYER_LINK;
336                     OGRSpatialReference* poSRS = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
337                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
338                     poCurLayer = m_poTmpDS->CreateLayer(osTablename, poSRS,
339                         iZ < 0 ? wkbLineString : wkbLineString25D, apszOptions);
340                     poSRS->Release();
341                 }
342                 else if( EQUAL(osTablename, "LinkCoordinate") &&
343                         (iLinkID = CSLFindString(papszAtr, "LINK_ID")) >= 0 &&
344                         CSLFindString(papszAtr, "COUNT") >= 0 &&
345                         (iX = CSLFindString(papszAtr, "X")) >= 0 &&
346                         (iY = CSLFindString(papszAtr, "Y")) >= 0 )
347                 {
348                     iZ = CSLFindString(papszAtr, "Z");
349                     eLayerType = LAYER_LINKCOORDINATE;
350                     OGRSpatialReference* poSRS = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
351                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
352                     poCurLayer = m_poTmpDS->CreateLayer(osTablename, poSRS,
353                             iZ < 0 ? wkbPoint : wkbPoint25D, apszOptions);
354                     poSRS->Release();
355                 }
356                 else
357                 {
358                     poCurLayer = m_poTmpDS->CreateLayer(osTablename, nullptr, wkbNone, apszOptions);
359                 }
360                 if( poCurLayer == nullptr )
361                 {
362                     CSLDestroy(papszAtr);
363                     CSLDestroy(papszFrm);
364                     break;
365                 }
366 
367                 if( !osAtr.empty() && CSLCount(papszAtr) == CSLCount(papszFrm) )
368                 {
369                     /* Note: we use AddFieldDefn() directly on the layer defn */
370                     /* This works with the current implementation of the MEM driver */
371                     /* but beware of future changes... */
372                     OGRVDVParseAtrFrm(poCurLayer->GetLayerDefn(), papszAtr, papszFrm);
373                 }
374                 CSLDestroy(papszAtr);
375                 CSLDestroy(papszFrm);
376             }
377 
378             OGRErr eErr = OGRERR_NONE;
379             char** papszTokens = CSLTokenizeStringComplex(pszLine + 4,";",TRUE,TRUE);
380             OGRFeatureDefn* poFDefn = poCurLayer->GetLayerDefn();
381             OGRFeature* poFeature = new OGRFeature(poFDefn);
382             for(int i=0; i < poFDefn->GetFieldCount() && papszTokens[i] != nullptr ;i++)
383             {
384                 if( papszTokens[i][0] )
385                 {
386                     if( bRecodeFromLatin1 &&
387                         poFDefn->GetFieldDefn(i)->GetType() == OFTString )
388                     {
389                         char* pszRecoded = CPLRecode(papszTokens[i],
390                                             CPL_ENC_ISO8859_1, CPL_ENC_UTF8);
391                         poFeature->SetField(i, pszRecoded);
392                         CPLFree(pszRecoded);
393                     }
394                     else
395                     {
396                         poFeature->SetField(i, papszTokens[i]);
397                     }
398                 }
399             }
400 
401             if( eLayerType == LAYER_NODE && iX >= 0 && iY >= 0 && iNodeID >= 0 )
402             {
403                 double dfX = poFeature->GetFieldAsDouble(iX);
404                 double dfY = poFeature->GetFieldAsDouble(iY);
405                 OGRGeometry* poGeom;
406                 if( iZ >= 0 )
407                 {
408                     double dfZ = poFeature->GetFieldAsDouble(iZ);
409                     oMapNode[ poFeature->GetFieldAsInteger64(iNodeID) ] = Point(dfX, dfY, dfZ);
410                     poGeom = new OGRPoint( dfX, dfY, dfZ );
411                 }
412                 else
413                 {
414                     oMapNode[ poFeature->GetFieldAsInteger64(iNodeID) ] = Point(dfX, dfY);
415                     poGeom = new OGRPoint( dfX, dfY );
416                 }
417                 poGeom->assignSpatialReference(poFDefn->GetGeomFieldDefn(0)->GetSpatialRef());
418                 poFeature->SetGeometryDirectly(poGeom);
419             }
420             else if( eLayerType == LAYER_LINK && iFromNode >= 0 &&
421                      iToNode >= 0 )
422             {
423                 GIntBig nFromNode = poFeature->GetFieldAsInteger64(iFromNode);
424                 GIntBig nToNode = poFeature->GetFieldAsInteger64(iToNode);
425                 std::map<GIntBig, Point >::iterator
426                                         oIterFrom = oMapNode.find(nFromNode);
427                 std::map<GIntBig, Point >::iterator
428                                         oIterTo = oMapNode.find(nToNode);
429                 if( oIterFrom != oMapNode.end() && oIterTo != oMapNode.end() )
430                 {
431                     OGRLineString* poLS = new OGRLineString();
432                     if( iZ >= 0 )
433                     {
434                         poLS->addPoint( oIterFrom->second.x,
435                                         oIterFrom->second.y,
436                                         oIterFrom->second.z );
437                         poLS->addPoint( oIterTo->second.x,
438                                         oIterTo->second.y,
439                                         oIterTo->second.z );
440                     }
441                     else
442                     {
443                         poLS->addPoint( oIterFrom->second.x,
444                                         oIterFrom->second.y );
445                         poLS->addPoint( oIterTo->second.x,
446                                         oIterTo->second.y );
447                     }
448                     poLS->assignSpatialReference(poFDefn->GetGeomFieldDefn(0)->GetSpatialRef());
449                     poFeature->SetGeometryDirectly(poLS);
450                 }
451             }
452             else if( eLayerType == LAYER_LINKCOORDINATE &&
453                      iX >= 0 && iY >= 0 && iLinkID >= 0 )
454             {
455                 double dfX = poFeature->GetFieldAsDouble(iX);
456                 double dfY = poFeature->GetFieldAsDouble(iY);
457                 double dfZ = 0.0;
458                 OGRGeometry* poGeom;
459                 if( iZ >= 0 )
460                 {
461                     dfZ = poFeature->GetFieldAsDouble(iZ);
462                     poGeom = new OGRPoint( dfX, dfY, dfZ );
463                 }
464                 else
465                 {
466                     poGeom = new OGRPoint( dfX, dfY );
467                 }
468                 poGeom->assignSpatialReference(poFDefn->GetGeomFieldDefn(0)->GetSpatialRef());
469                 poFeature->SetGeometryDirectly(poGeom);
470 
471                 GIntBig nCurLinkID = poFeature->GetFieldAsInteger64(iLinkID);
472                 std::map<GIntBig, OGRLineString*>::iterator
473                     oMapLinkCoordinateIter = oMapLinkCoordinate.find(nCurLinkID);
474                 if( oMapLinkCoordinateIter == oMapLinkCoordinate.end() )
475                 {
476                     OGRLineString* poLS = new OGRLineString();
477                     if( iZ >= 0 )
478                         poLS->addPoint(dfX, dfY, dfZ);
479                     else
480                         poLS->addPoint(dfX, dfY);
481                     oMapLinkCoordinate[nCurLinkID] = poLS;
482                 }
483                 else
484                 {
485                     if( iZ >= 0 )
486                     {
487                         oMapLinkCoordinateIter->second->addPoint(dfX, dfY, dfZ);
488                     }
489                     else
490                     {
491                         oMapLinkCoordinateIter->second->addPoint(dfX, dfY);
492                     }
493                 }
494             }
495             eErr = poCurLayer->CreateFeature(poFeature);
496             delete poFeature;
497 
498             CSLDestroy(papszTokens);
499 
500             if( eErr == OGRERR_FAILURE )
501                 break;
502         }
503     }
504 
505     oMapNode.clear();
506 
507     // Patch Link geometries with the intermediate points of LinkCoordinate
508     OGRLayer* poLinkLyr = m_poTmpDS->GetLayerByName("Link");
509     if( poLinkLyr && poLinkLyr->GetLayerDefn()->GetGeomFieldCount() )
510     {
511         iLinkID = poLinkLyr->GetLayerDefn()->GetFieldIndex("LINK_ID");
512         if( iLinkID >= 0 )
513         {
514             poLinkLyr->ResetReading();
515             OGRSpatialReference* poSRS =
516                 poLinkLyr->GetLayerDefn()->GetGeomFieldDefn(0)->GetSpatialRef();
517             for( auto&& poFeat: poLinkLyr )
518             {
519                 GIntBig nLinkID = poFeat->GetFieldAsInteger64(iLinkID);
520                 std::map<GIntBig, OGRLineString*>::iterator
521                     oMapLinkCoordinateIter = oMapLinkCoordinate.find(nLinkID);
522                 OGRGeometry* poGeom = poFeat->GetGeometryRef();
523                 if( poGeom && oMapLinkCoordinateIter != oMapLinkCoordinate.end() )
524                 {
525                     OGRLineString* poLS = poGeom->toLineString();
526                     if( poLS )
527                     {
528                         OGRLineString* poLSIntermediate = oMapLinkCoordinateIter->second;
529                         OGRLineString* poLSNew = new OGRLineString();
530                         if( poLS->getGeometryType() == wkbLineString25D )
531                         {
532                             poLSNew->addPoint(poLS->getX(0), poLS->getY(0),
533                                               poLS->getZ(0));
534                             for(int i=0;i<poLSIntermediate->getNumPoints();i++)
535                             {
536                                 poLSNew->addPoint(poLSIntermediate->getX(i),
537                                                   poLSIntermediate->getY(i),
538                                                   poLSIntermediate->getZ(i));
539                             }
540                             poLSNew->addPoint(poLS->getX(1), poLS->getY(1),
541                                               poLS->getZ(1));
542                         }
543                         else
544                         {
545                             poLSNew->addPoint(poLS->getX(0), poLS->getY(0));
546                             for(int i=0;i<poLSIntermediate->getNumPoints();i++)
547                             {
548                                 poLSNew->addPoint(poLSIntermediate->getX(i),
549                                                   poLSIntermediate->getY(i));
550                             }
551                             poLSNew->addPoint(poLS->getX(1), poLS->getY(1));
552                         }
553                         poLSNew->assignSpatialReference(poSRS);
554                         poFeat->SetGeometryDirectly(poLSNew);
555                         CPL_IGNORE_RET_VAL(poLinkLyr->SetFeature(poFeat.get()));
556                     }
557                 }
558             }
559             poLinkLyr->ResetReading();
560         }
561     }
562 
563     m_poTmpDS->CommitTransaction();
564 
565     std::map<GIntBig, OGRLineString*>::iterator oMapLinkCoordinateIter =
566                                                     oMapLinkCoordinate.begin();
567     for(; oMapLinkCoordinateIter != oMapLinkCoordinate.end(); ++oMapLinkCoordinateIter)
568         delete oMapLinkCoordinateIter->second;
569 }
570 
571 /************************************************************************/
572 /*                           GetLayerCount()                            */
573 /************************************************************************/
574 
GetLayerCount()575 int OGRIDFDataSource::GetLayerCount()
576 {
577     if( !m_bHasParsed )
578         Parse();
579     if( m_poTmpDS == nullptr )
580         return 0;
581     return m_poTmpDS->GetLayerCount();
582 }
583 
584 /************************************************************************/
585 /*                              GetLayer()                              */
586 /************************************************************************/
587 
GetLayer(int iLayer)588 OGRLayer* OGRIDFDataSource::GetLayer( int iLayer )
589 {
590     if( iLayer < 0 || iLayer >= GetLayerCount() )
591         return nullptr;
592     if( m_poTmpDS == nullptr )
593         return nullptr;
594     return m_poTmpDS->GetLayer(iLayer);
595 }
596 
597 /************************************************************************/
598 /*                           OGRVDVDataSource()                         */
599 /************************************************************************/
600 
OGRVDVDataSource(const char * pszFilename,VSILFILE * fpL,bool bUpdate,bool bSingleFile,bool bNew)601 OGRVDVDataSource::OGRVDVDataSource(const char* pszFilename,
602                                    VSILFILE* fpL,
603                                    bool bUpdate,
604                                    bool bSingleFile,
605                                    bool bNew) :
606     m_osFilename(pszFilename),
607     m_fpL(fpL),
608     m_bUpdate(bUpdate),
609     m_bSingleFile(bSingleFile),
610     m_bNew(bNew),
611     m_bLayersDetected(bNew || fpL == nullptr),
612     m_nLayerCount(0),
613     m_papoLayers(nullptr),
614     m_poCurrentWriterLayer(nullptr),
615     m_bMustWriteEof(false),
616     m_bVDV452Loaded(false)
617 {}
618 
619 /************************************************************************/
620 /*                          ~OGRVDVDataSource()                         */
621 /************************************************************************/
622 
~OGRVDVDataSource()623 OGRVDVDataSource::~OGRVDVDataSource()
624 {
625     if( m_poCurrentWriterLayer )
626     {
627         m_poCurrentWriterLayer->StopAsCurrentLayer();
628         m_poCurrentWriterLayer = nullptr;
629     }
630 
631     for(int i=0;i<m_nLayerCount;i++)
632         delete m_papoLayers[i];
633     CPLFree(m_papoLayers);
634 
635     // Close after destroying layers since they might use it (single file write)
636     if( m_fpL )
637     {
638         if( m_bMustWriteEof )
639         {
640             VSIFPrintfL( m_fpL, "eof; %d\n", m_nLayerCount );
641         }
642         VSIFCloseL(m_fpL);
643     }
644 }
645 
646 /************************************************************************/
647 /*                           GetLayerCount()                            */
648 /************************************************************************/
649 
GetLayerCount()650 int OGRVDVDataSource::GetLayerCount()
651 {
652     if( !m_bLayersDetected )
653         DetectLayers();
654     return m_nLayerCount;
655 }
656 
657 /************************************************************************/
658 /*                              GetLayer()                              */
659 /************************************************************************/
660 
GetLayer(int iLayer)661 OGRLayer* OGRVDVDataSource::GetLayer( int iLayer )
662 {
663     if( iLayer < 0 || iLayer >= GetLayerCount() )
664         return nullptr;
665     return m_papoLayers[iLayer];
666 }
667 
668 /************************************************************************/
669 /*                         DetectLayers()                               */
670 /************************************************************************/
671 
DetectLayers()672 void OGRVDVDataSource::DetectLayers()
673 {
674     m_bLayersDetected = true;
675 
676     char szBuffer[1+1024+1];
677     char chNextExpected = 't';
678     char chNextExpected2 = 'r';
679     char chNextExpected3 = 'e';
680     bool bInTableName = false;
681     CPLString osTableName;
682     GIntBig nFeatureCount = 0;
683     vsi_l_offset nStartOffset = 0;
684     OGRVDVLayer* poLayer = nullptr;
685     bool bFirstBuffer = true;
686     bool bRecodeFromLatin1 = false;
687 
688     VSIFSeekL(m_fpL, 0, SEEK_SET);
689 
690     while( true )
691     {
692         size_t nRead = VSIFReadL(szBuffer, 1, 1024, m_fpL);
693         szBuffer[nRead] = '\0';
694         if( bFirstBuffer )
695         {
696             const char* pszChs = strstr(szBuffer, "\nchs;");
697             if( pszChs )
698             {
699                 pszChs += 5;
700                 CPLString osChs;
701                 for(; *pszChs != '\0' && *pszChs != '\r' && *pszChs != '\n';
702                     ++pszChs)
703                 {
704                     if( *pszChs != ' ' && *pszChs != '"' )
705                         osChs += *pszChs;
706                 }
707                 bRecodeFromLatin1 = EQUAL(osChs, "ISO8859-1") ||
708                                     EQUAL(osChs, "ISO_LATIN_1");
709             }
710             bFirstBuffer = false;
711         }
712         for(size_t i=0;i<nRead;i++)
713         {
714             if( bInTableName )
715             {
716                 if( szBuffer[i] == '\r' || szBuffer[i] == '\n' )
717                 {
718                     bInTableName = false;
719                     poLayer = new OGRVDVLayer(osTableName,
720                                               m_fpL,
721                                               false,
722                                               bRecodeFromLatin1,
723                                               nStartOffset);
724                     m_papoLayers = static_cast<OGRLayer**>(CPLRealloc(
725                         m_papoLayers, sizeof(OGRLayer*) * (m_nLayerCount + 1) ));
726                     m_papoLayers[m_nLayerCount] = poLayer;
727                     m_nLayerCount ++;
728                 }
729                 else if( szBuffer[i] != ' ' )
730                 {
731                     osTableName += szBuffer[i];
732                     continue;
733                 }
734             }
735 
736             // Reset state on end of line characters
737             if( szBuffer[i] == '\n' || szBuffer[i] == '\r' )
738             {
739                 chNextExpected = szBuffer[i];
740                 chNextExpected2 = szBuffer[i];
741                 chNextExpected3 = szBuffer[i];
742             }
743 
744             // Detect tbl;
745             if( szBuffer[i] == chNextExpected )
746             {
747                 if( chNextExpected == '\n' || chNextExpected == '\r' )
748                     chNextExpected = 't';
749                 else if( chNextExpected == 't' )
750                     chNextExpected = 'b';
751                 else if( chNextExpected == 'b' )
752                     chNextExpected = 'l';
753                 else if( chNextExpected == 'l' )
754                     chNextExpected = ';';
755                 else if( chNextExpected == ';' )
756                 {
757                     if( poLayer != nullptr )
758                         poLayer->SetFeatureCount(nFeatureCount);
759                     poLayer = nullptr;
760                     nFeatureCount = 0;
761                     nStartOffset = VSIFTellL(m_fpL) + i + 1 - nRead - 4;
762                     bInTableName = true;
763                     osTableName.resize(0);
764                     chNextExpected = 0;
765                 }
766             }
767             else
768                 chNextExpected = 0;
769 
770             // Detect rec;
771             if( szBuffer[i] == chNextExpected2 )
772             {
773                 if( chNextExpected2 == '\n' || chNextExpected2 == '\r' )
774                     chNextExpected2 = 'r';
775                 else if( chNextExpected2 == 'r' )
776                     chNextExpected2 = 'e';
777                 else if( chNextExpected2 == 'e' )
778                     chNextExpected2 = 'c';
779                 else if( chNextExpected2 == 'c' )
780                     chNextExpected2 = ';';
781                 else if( chNextExpected2 == ';' )
782                 {
783                     nFeatureCount ++;
784                     chNextExpected2 = 0;
785                 }
786             }
787             else
788                 chNextExpected2 = 0;
789 
790             // Detect end;
791             if( szBuffer[i] == chNextExpected3 )
792             {
793                 if( chNextExpected3 == '\n' || chNextExpected3 == '\r' )
794                     chNextExpected3 = 'e';
795                 else if( chNextExpected3 == 'e' )
796                     chNextExpected3 = 'n';
797                 else if( chNextExpected3 == 'n' )
798                     chNextExpected3 = 'd';
799                 else if( chNextExpected3 == 'd' )
800                     chNextExpected3 = ';';
801                 else if( chNextExpected3 == ';' )
802                 {
803                     if( poLayer != nullptr )
804                         poLayer->SetFeatureCount(nFeatureCount);
805                     poLayer = nullptr;
806                     chNextExpected3 = 0;
807                 }
808             }
809             else
810                 chNextExpected3 = 0;
811         }
812         if( nRead < 1024 )
813             break;
814     }
815     if( poLayer != nullptr )
816         poLayer->SetFeatureCount(nFeatureCount);
817 }
818 
819 /************************************************************************/
820 /*                           OGRVDVLayer()                              */
821 /************************************************************************/
822 
OGRVDVLayer(const CPLString & osTableName,VSILFILE * fpL,bool bOwnFP,bool bRecodeFromLatin1,vsi_l_offset nStartOffset)823 OGRVDVLayer::OGRVDVLayer(const CPLString& osTableName,
824                          VSILFILE* fpL,
825                          bool bOwnFP,
826                          bool bRecodeFromLatin1,
827                          vsi_l_offset nStartOffset) :
828     m_fpL(fpL),
829     m_bOwnFP(bOwnFP),
830     m_bRecodeFromLatin1(bRecodeFromLatin1),
831     m_nStartOffset(nStartOffset),
832     m_nCurOffset(0),
833     m_nTotalFeatureCount(0),
834     m_nFID(0),
835     m_bEOF(false),
836     m_iLongitudeVDV452(-1),
837     m_iLatitudeVDV452(-1)
838 {
839     m_poFeatureDefn = new OGRFeatureDefn(osTableName);
840     m_poFeatureDefn->SetGeomType(wkbNone);
841     m_poFeatureDefn->Reference();
842     SetDescription(osTableName);
843     vsi_l_offset nCurOffset = VSIFTellL(fpL);
844     VSIFSeekL(m_fpL, m_nStartOffset, SEEK_SET);
845     CPLString osAtr, osFrm;
846 
847     /* skip until first tbl; */
848     bool bFoundTbl = false;
849     for(int i=0;i<20;i++)
850     {
851         const char* pszLine = CPLReadLineL(m_fpL);
852         if( pszLine == nullptr )
853             break;
854         if( STARTS_WITH(pszLine, "chs;") )
855         {
856             CPLString osChs(pszLine+4);
857             osChs.Trim();
858             if( osChs.size() >= 2 && osChs[0] == '"' && osChs.back() == '"' )
859                 osChs = osChs.substr(1, osChs.size()-2);
860             m_bRecodeFromLatin1 = EQUAL(osChs, "ISO8859-1") ||
861                                   EQUAL(osChs, "ISO_LATIN_1");
862         }
863         else if( STARTS_WITH(pszLine, "tbl;") )
864         {
865             if( bFoundTbl )
866                 break; /* shouldn't happen in correctly formed files */
867             bFoundTbl = true;
868             m_nStartOffset = VSIFTellL(fpL);
869         }
870         else if( STARTS_WITH(pszLine, "atr;") )
871         {
872             osAtr = pszLine + 4;
873             osAtr.Trim();
874         }
875         else if( STARTS_WITH(pszLine, "frm;") )
876         {
877             osFrm = pszLine + 4;
878             osFrm.Trim();
879         }
880         else if( STARTS_WITH(pszLine, "rec;") ||
881                  STARTS_WITH(pszLine, "end;") )
882             break;
883     }
884     if( !bFoundTbl )
885         CPLDebug("VDV", "Didn't find tbl; line");
886 
887     VSIFSeekL(m_fpL, nCurOffset, SEEK_SET);
888     if( !osAtr.empty() && !osFrm.empty() )
889     {
890         char** papszAtr = CSLTokenizeString2(osAtr,";",
891                     CSLT_ALLOWEMPTYTOKENS|CSLT_STRIPLEADSPACES|CSLT_STRIPENDSPACES);
892         char** papszFrm = CSLTokenizeString2(osFrm,";",
893                     CSLT_ALLOWEMPTYTOKENS|CSLT_STRIPLEADSPACES|CSLT_STRIPENDSPACES);
894         if( CSLCount(papszAtr) == CSLCount(papszFrm) )
895         {
896             OGRVDVParseAtrFrm(m_poFeatureDefn, papszAtr, papszFrm);
897         }
898         CSLDestroy(papszAtr);
899         CSLDestroy(papszFrm);
900     }
901 
902     // Identify longitude, latitude columns of VDV-452 STOP table
903     if( EQUAL(osTableName, "STOP") ) /* English */
904     {
905         m_iLongitudeVDV452 = m_poFeatureDefn->GetFieldIndex("POINT_LONGITUDE");
906         m_iLatitudeVDV452 = m_poFeatureDefn->GetFieldIndex("POINT_LATITUDE");
907     }
908     else if( EQUAL(osTableName, "REC_ORT") ) /* German */
909     {
910         m_iLongitudeVDV452 = m_poFeatureDefn->GetFieldIndex("ORT_POS_LAENGE");
911         m_iLatitudeVDV452 = m_poFeatureDefn->GetFieldIndex("ORT_POS_BREITE");
912     }
913     if( m_iLongitudeVDV452 >= 0 && m_iLatitudeVDV452 >= 0 )
914     {
915         m_poFeatureDefn->SetGeomType(wkbPoint);
916         OGRSpatialReference* poSRS = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
917         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
918         m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
919         poSRS->Release();
920     }
921     else
922         m_iLongitudeVDV452 = m_iLatitudeVDV452 = -1;
923 }
924 
925 /************************************************************************/
926 /*                          ~OGRVDVLayer()                              */
927 /************************************************************************/
928 
~OGRVDVLayer()929 OGRVDVLayer::~OGRVDVLayer()
930 {
931     m_poFeatureDefn->Release();
932     if( m_bOwnFP )
933         VSIFCloseL(m_fpL);
934 }
935 
936 /************************************************************************/
937 /*                          ResetReading()                              */
938 /************************************************************************/
939 
ResetReading()940 void OGRVDVLayer::ResetReading()
941 {
942     VSIFSeekL(m_fpL, m_nStartOffset, SEEK_SET);
943     m_nCurOffset = m_nStartOffset;
944     m_nFID = 1;
945     m_bEOF = false;
946 }
947 
948 /************************************************************************/
949 /*                         OGRVDVUnescapeString()                       */
950 /************************************************************************/
951 
OGRVDVUnescapeString(const char * pszValue)952 static CPLString OGRVDVUnescapeString(const char* pszValue)
953 {
954     CPLString osRet;
955     for(; *pszValue != '\0'; ++pszValue )
956     {
957         if( *pszValue == '"' && pszValue[1] == '"' )
958         {
959             osRet += '"';
960             ++pszValue;
961         }
962         else
963         {
964             osRet += *pszValue;
965         }
966     }
967     return osRet;
968 }
969 
970 /************************************************************************/
971 /*                          GetNextFeature()                            */
972 /************************************************************************/
973 
GetNextFeature()974 OGRFeature* OGRVDVLayer::GetNextFeature()
975 {
976     if( m_nFID == 0 )
977         ResetReading();
978     VSIFSeekL(m_fpL, m_nCurOffset, SEEK_SET);
979     OGRFeature* poFeature = nullptr;
980     while( !m_bEOF )
981     {
982         const char* pszLine = CPLReadLineL(m_fpL);
983         if( pszLine == nullptr )
984             break;
985         if( strncmp(pszLine, "end;", 4) == 0 ||
986             strncmp(pszLine, "tbl;", 4) == 0 )
987         {
988             m_bEOF = true;
989             break;
990         }
991         if( strncmp(pszLine, "rec;", 4) != 0 )
992             continue;
993 
994         char** papszTokens = CSLTokenizeString2(pszLine+4,";",
995                 CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES| CSLT_STRIPENDSPACES);
996         poFeature = new OGRFeature(m_poFeatureDefn);
997         poFeature->SetFID( m_nFID ++ );
998         for(int i=0; i < m_poFeatureDefn->GetFieldCount() &&
999                      papszTokens[i] != nullptr ;i++)
1000         {
1001             if( papszTokens[i][0] && !EQUAL(papszTokens[i], "NULL") )
1002             {
1003                 size_t nLen = strlen(papszTokens[i]);
1004                 CPLString osToken;
1005                 if( nLen >= 2 &&
1006                     papszTokens[i][0] == '"' && papszTokens[i][nLen-1] == '"' )
1007                 {
1008                     papszTokens[i][nLen-1] = 0;
1009                     osToken = OGRVDVUnescapeString(papszTokens[i]+1);
1010                 }
1011                 else
1012                     osToken = papszTokens[i];
1013                 // Strip trailing spaces
1014                 while( !osToken.empty() && osToken.back() == ' ' )
1015                     osToken.resize(osToken.size()-1);
1016                 OGRFieldType eFieldType = m_poFeatureDefn->GetFieldDefn(i)->GetType();
1017                 if( m_bRecodeFromLatin1 && eFieldType == OFTString )
1018                 {
1019                     char* pszRecoded = CPLRecode(osToken,
1020                                         CPL_ENC_ISO8859_1, CPL_ENC_UTF8);
1021                     poFeature->SetField(i, pszRecoded);
1022                     CPLFree(pszRecoded);
1023                 }
1024                 else if( eFieldType == OFTString || !EQUAL(osToken, "NULL") )
1025                 {
1026                     poFeature->SetField(i, osToken);
1027                 }
1028             }
1029         }
1030         CSLDestroy(papszTokens);
1031 
1032         if( m_iLongitudeVDV452 >= 0 && m_iLatitudeVDV452 >= 0 )
1033         {
1034             int nLongDegMinMS = poFeature->GetFieldAsInteger(m_iLongitudeVDV452);
1035             int nLongSign = 1;
1036             if( nLongDegMinMS < 0 )
1037             {
1038                 nLongSign = -1;
1039                 nLongDegMinMS = -nLongDegMinMS;
1040             }
1041             const int nLongDeg = nLongDegMinMS / (100 * 100000);
1042             const int nLongMin = (nLongDegMinMS / 100000) % 100;
1043             const int nLongMS = nLongDegMinMS % 100000;
1044             const double dfLong =
1045                 (nLongDeg + nLongMin / 60.0 + nLongMS / (3600.0 * 1000.0)) * nLongSign;
1046 
1047             int nLatDegMinMS = poFeature->GetFieldAsInteger(m_iLatitudeVDV452);
1048             int nLatSign = 1;
1049             if( nLatDegMinMS < 0 )
1050             {
1051                 nLatSign = -1;
1052                 nLatDegMinMS = -nLatDegMinMS;
1053             }
1054             const int nLatDeg = nLatDegMinMS / (100 * 100000);
1055             const int nLatMin = (nLatDegMinMS / 100000) % 100;
1056             const int nLatMS = nLatDegMinMS % 100000;
1057             const double dfLat =
1058                 (nLatDeg + nLatMin / 60.0 + nLatMS / (3600.0 * 1000.0)) * nLatSign;
1059 
1060             if( dfLong != 0.0 || dfLat != 0.0 )
1061             {
1062                 OGRPoint* poPoint = new OGRPoint(dfLong, dfLat);
1063                 poPoint->assignSpatialReference(
1064                     m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef());
1065                 poFeature->SetGeometryDirectly( poPoint );
1066             }
1067         }
1068 
1069         if( (m_poFilterGeom == nullptr
1070             || FilterGeometry( poFeature->GetGeomFieldRef(m_iGeomFieldFilter) ) )
1071             && (m_poAttrQuery == nullptr
1072                 || m_poAttrQuery->Evaluate( poFeature )) )
1073         {
1074             break;
1075         }
1076         delete poFeature;
1077         poFeature = nullptr;
1078     }
1079     m_nCurOffset = VSIFTellL(m_fpL);
1080     return poFeature;
1081 }
1082 
1083 /************************************************************************/
1084 /*                          TestCapability()                            */
1085 /************************************************************************/
1086 
TestCapability(const char * pszCap)1087 int OGRVDVLayer::TestCapability(const char* pszCap)
1088 {
1089     if( EQUAL(pszCap, OLCFastFeatureCount) &&  m_nTotalFeatureCount > 0 &&
1090         m_poFilterGeom == nullptr && m_poAttrQuery == nullptr )
1091     {
1092         return TRUE;
1093     }
1094     if( EQUAL(pszCap, OLCStringsAsUTF8) )
1095     {
1096         return m_bRecodeFromLatin1;
1097     }
1098     return FALSE;
1099 }
1100 
1101 /************************************************************************/
1102 /*                          GetFeatureCount()                           */
1103 /************************************************************************/
1104 
GetFeatureCount(int bForce)1105 GIntBig OGRVDVLayer::GetFeatureCount(int bForce)
1106 {
1107     if( m_nTotalFeatureCount == 0 ||
1108         m_poFilterGeom != nullptr || m_poAttrQuery != nullptr )
1109     {
1110         return OGRLayer::GetFeatureCount(bForce);
1111     }
1112     return m_nTotalFeatureCount;
1113 }
1114 
1115 /************************************************************************/
1116 /*                              Identify()                              */
1117 /************************************************************************/
1118 
OGRVDVDriverIdentify(GDALOpenInfo * poOpenInfo)1119 static int OGRVDVDriverIdentify( GDALOpenInfo* poOpenInfo )
1120 
1121 {
1122     if( poOpenInfo->bIsDirectory )
1123         return -1; /* perhaps... */
1124     return (poOpenInfo->nHeaderBytes > 0 &&
1125             (strstr((const char*)poOpenInfo->pabyHeader, "\ntbl;") != nullptr ||
1126              strncmp((const char*)poOpenInfo->pabyHeader, "tbl;", 4) == 0) &&
1127             strstr((const char*)poOpenInfo->pabyHeader, "\natr;") != nullptr &&
1128             strstr((const char*)poOpenInfo->pabyHeader, "\nfrm;") != nullptr);
1129 }
1130 
1131 /************************************************************************/
1132 /*                                Open()                                */
1133 /************************************************************************/
1134 
Open(GDALOpenInfo * poOpenInfo)1135 GDALDataset *OGRVDVDataSource::Open( GDALOpenInfo* poOpenInfo )
1136 
1137 {
1138     if( !OGRVDVDriverIdentify(poOpenInfo) )
1139     {
1140         return nullptr;
1141     }
1142     if( poOpenInfo->bIsDirectory )
1143     {
1144         char** papszFiles = VSIReadDir(poOpenInfo->pszFilename);
1145 
1146         // Identify the extension with the most occurrences
1147         std::map<CPLString, int> oMapOtherExtensions;
1148         CPLString osMajorityExtension, osMajorityFile;
1149         int nFiles = 0;
1150         for(char** papszIter = papszFiles; papszIter && *papszIter; ++papszIter)
1151         {
1152             if( EQUAL(*papszIter, ".") || EQUAL(*papszIter, "..") )
1153                 continue;
1154             nFiles ++;
1155             CPLString osExtension(CPLGetExtension(*papszIter));
1156             int nCount = ++oMapOtherExtensions[osExtension];
1157             if( osMajorityExtension == "" ||
1158                 nCount > oMapOtherExtensions[osMajorityExtension] )
1159             {
1160                 osMajorityExtension = osExtension;
1161                 osMajorityFile = *papszIter;
1162             }
1163         }
1164 
1165         // Check it is at least 50% of the files in the directory
1166         if( osMajorityExtension == "" ||
1167             2 * oMapOtherExtensions[osMajorityExtension] < nFiles )
1168         {
1169             CSLDestroy(papszFiles);
1170             return nullptr;
1171         }
1172 
1173         // And check that one of those files is a VDV one if it isn't .x10
1174         if( osMajorityExtension != "x10" )
1175         {
1176             GDALOpenInfo oOpenInfo( CPLFormFilename(poOpenInfo->pszFilename,
1177                                                     osMajorityFile,
1178                                                     nullptr), GA_ReadOnly );
1179             if( OGRVDVDriverIdentify(&oOpenInfo) != TRUE )
1180             {
1181                 CSLDestroy(papszFiles);
1182                 return nullptr;
1183             }
1184         }
1185 
1186         OGRVDVDataSource* poDS = new OGRVDVDataSource(poOpenInfo->pszFilename,
1187                                                       nullptr, /* fp */
1188                                                       poOpenInfo->eAccess == GA_Update,
1189                                                       false, /* single file */
1190                                                       false /* new */);
1191 
1192         // Instantiate the layers.
1193         for(char** papszIter = papszFiles; papszIter && *papszIter; ++papszIter)
1194         {
1195             if( !EQUAL(CPLGetExtension(*papszIter), osMajorityExtension) )
1196                 continue;
1197             VSILFILE* fp = VSIFOpenL( CPLFormFilename(poOpenInfo->pszFilename,
1198                                                       *papszIter,
1199                                                       nullptr), "rb");
1200             if( fp == nullptr )
1201                 continue;
1202             poDS->m_papoLayers = static_cast<OGRLayer**>(CPLRealloc(
1203                 poDS->m_papoLayers, sizeof(OGRLayer*) * (poDS->m_nLayerCount + 1) ));
1204             poDS->m_papoLayers[poDS->m_nLayerCount] = new OGRVDVLayer(
1205                                                             CPLGetBasename(*papszIter),
1206                                                             fp,
1207                                                             true,
1208                                                             false,
1209                                                             0 );
1210             poDS->m_nLayerCount ++;
1211         }
1212         CSLDestroy(papszFiles);
1213 
1214         if( poDS->m_nLayerCount == 0 )
1215         {
1216             delete poDS;
1217             poDS = nullptr;
1218         }
1219         return poDS;
1220     }
1221 
1222     VSILFILE* fpL = poOpenInfo->fpL;
1223     poOpenInfo->fpL = nullptr;
1224     const char* pszHeader = (const char*)poOpenInfo->pabyHeader;
1225     if( strstr(pszHeader, "tbl;Node\r\natr;NODE_ID;") != nullptr ||
1226         strstr(pszHeader, "tbl;Node\natr;NODE_ID;") != nullptr ||
1227         strstr(pszHeader, "tbl;Link\r\natr;LINK_ID;") != nullptr ||
1228         strstr(pszHeader, "tbl;Link\natr;LINK_ID;") != nullptr ||
1229         strstr(pszHeader, "tbl;LinkCoordinate\r\natr;LINK_ID;") != nullptr ||
1230         strstr(pszHeader, "tbl;LinkCoordinate\natr;LINK_ID;") != nullptr )
1231     {
1232         return new OGRIDFDataSource(poOpenInfo->pszFilename, fpL);
1233     }
1234     else
1235     {
1236         return new OGRVDVDataSource(poOpenInfo->pszFilename,
1237                                     fpL,
1238                                     poOpenInfo->eAccess == GA_Update,
1239                                     true, /* single file */
1240                                     false /* new */);
1241     }
1242 }
1243 
1244 /************************************************************************/
1245 /*                         OGRVDVWriterLayer                            */
1246 /************************************************************************/
1247 
OGRVDVWriterLayer(OGRVDVDataSource * poDS,const char * pszName,VSILFILE * fpL,bool bOwnFP,OGRVDV452Table * poVDV452Table,const CPLString & osVDV452Lang,bool bProfileStrict)1248 OGRVDVWriterLayer::OGRVDVWriterLayer( OGRVDVDataSource *poDS,
1249                                       const char* pszName,
1250                                       VSILFILE* fpL,
1251                                       bool bOwnFP,
1252                                       OGRVDV452Table* poVDV452Table,
1253                                       const CPLString& osVDV452Lang,
1254                                       bool bProfileStrict):
1255     m_poDS(poDS),
1256     m_poFeatureDefn(new OGRFeatureDefn(pszName)),
1257     m_bWritePossible(true),
1258     m_fpL(fpL),
1259     m_bOwnFP(bOwnFP),
1260     m_nFeatureCount(-1),
1261     m_poVDV452Table(poVDV452Table),
1262     m_osVDV452Lang(osVDV452Lang),
1263     m_bProfileStrict(bProfileStrict),
1264     m_iLongitudeVDV452(-1),
1265     m_iLatitudeVDV452(-1)
1266 {
1267     m_poFeatureDefn->SetGeomType(wkbNone);
1268     m_poFeatureDefn->Reference();
1269     SetDescription(pszName);
1270 }
1271 
1272 /************************************************************************/
1273 /*                        ~OGRVDVWriterLayer                            */
1274 /************************************************************************/
1275 
~OGRVDVWriterLayer()1276 OGRVDVWriterLayer::~OGRVDVWriterLayer()
1277 {
1278     StopAsCurrentLayer();
1279 
1280     m_poFeatureDefn->Release();
1281     if( m_bOwnFP )
1282     {
1283         VSIFPrintfL( m_fpL, "eof; %d\n", 1 );
1284         VSIFCloseL(m_fpL);
1285     }
1286 }
1287 
1288 /************************************************************************/
1289 /*                          ResetReading()                              */
1290 /************************************************************************/
1291 
ResetReading()1292 void OGRVDVWriterLayer::ResetReading()
1293 {
1294 }
1295 
1296 /************************************************************************/
1297 /*                          GetNextFeature()                            */
1298 /************************************************************************/
1299 
GetNextFeature()1300 OGRFeature* OGRVDVWriterLayer::GetNextFeature()
1301 {
1302     CPLError(CE_Failure, CPLE_NotSupported,
1303              "GetNextFeature() not supported on write-only layer");
1304     return nullptr;
1305 }
1306 
1307 /************************************************************************/
1308 /*                         OGRVDVEscapeString()                         */
1309 /************************************************************************/
1310 
OGRVDVEscapeString(const char * pszValue)1311 static CPLString OGRVDVEscapeString(const char* pszValue)
1312 {
1313     CPLString osRet;
1314     for( ; *pszValue != '\0'; ++pszValue )
1315     {
1316         if( *pszValue == '"' )
1317             osRet += "\"\"";
1318         else
1319             osRet += *pszValue;
1320     }
1321     return osRet;
1322 }
1323 
1324 /************************************************************************/
1325 /*                          WriteSchemaIfNeeded()                       */
1326 /************************************************************************/
1327 
WriteSchemaIfNeeded()1328 bool OGRVDVWriterLayer::WriteSchemaIfNeeded()
1329 {
1330     if( m_nFeatureCount < 0 )
1331     {
1332         m_nFeatureCount = 0;
1333 
1334         bool bOK = VSIFPrintfL(m_fpL, "tbl; %s\n", m_poFeatureDefn->GetName()) > 0;
1335         bOK &= VSIFPrintfL(m_fpL, "atr;") > 0;
1336         for( int i=0; i < m_poFeatureDefn->GetFieldCount(); i++ )
1337         {
1338             if( i > 0)
1339                 bOK &= VSIFPrintfL(m_fpL, ";") > 0;
1340             bOK &= VSIFPrintfL(m_fpL, " %s",
1341                                m_poFeatureDefn->GetFieldDefn(i)->GetNameRef()) > 0;
1342         }
1343         bOK &= VSIFPrintfL(m_fpL, "\n") > 0;
1344         bOK &= VSIFPrintfL(m_fpL, "frm;") > 0;
1345         for( int i=0; i < m_poFeatureDefn->GetFieldCount(); i++ )
1346         {
1347             if( i > 0)
1348                 bOK &= VSIFPrintfL(m_fpL, ";") > 0;
1349             bOK &= VSIFPrintfL(m_fpL, " ") > 0;
1350             int nWidth = m_poFeatureDefn->GetFieldDefn(i)->GetWidth();
1351             const OGRFieldType eType = m_poFeatureDefn->GetFieldDefn(i)->GetType();
1352             switch( eType )
1353             {
1354                 case OFTInteger:
1355                 case OFTInteger64:
1356                     if( m_poFeatureDefn->GetFieldDefn(i)->GetSubType() == OFSTBoolean )
1357                     {
1358                         bOK &= VSIFPrintfL(m_fpL, "boolean") > 0;
1359                     }
1360                     else
1361                     {
1362                         if( nWidth == 0 )
1363                         {
1364                             if( eType == OFTInteger )
1365                                 nWidth = 11;
1366                             else
1367                                 nWidth = 20;
1368                         }
1369                         nWidth --; /* VDV 451 is without sign */
1370                         bOK &= VSIFPrintfL(m_fpL, "num[%d.0]", nWidth) > 0;
1371                     }
1372                     break;
1373 
1374                 default:
1375                     if( nWidth == 0 )
1376                     {
1377                         nWidth = 80;
1378                     }
1379                     bOK &= VSIFPrintfL(m_fpL, "char[%d]", nWidth) > 0;
1380                     break;
1381             }
1382         }
1383         bOK &= VSIFPrintfL(m_fpL, "\n") > 0;
1384 
1385         if( !bOK )
1386             return false;
1387     }
1388 
1389     return true;
1390 }
1391 
1392 /************************************************************************/
1393 /*                         ICreateFeature()                             */
1394 /************************************************************************/
1395 
ICreateFeature(OGRFeature * poFeature)1396 OGRErr OGRVDVWriterLayer::ICreateFeature(OGRFeature* poFeature)
1397 {
1398     if( !m_bWritePossible )
1399     {
1400         CPLError(CE_Failure, CPLE_NotSupported,
1401                  "Layer %s is no longer the active layer. "
1402                  "Writing in it is no longer possible",
1403                  m_poFeatureDefn->GetName());
1404         return OGRERR_FAILURE;
1405     }
1406     m_poDS->SetCurrentWriterLayer(this);
1407 
1408     WriteSchemaIfNeeded();
1409 
1410     bool bOK = VSIFPrintfL(m_fpL, "rec; ") > 0;
1411     for( int i=0; i < m_poFeatureDefn->GetFieldCount(); i++ )
1412     {
1413         if( i > 0)
1414             bOK &= VSIFPrintfL(m_fpL, "; ") > 0;
1415         auto poGeom = poFeature->GetGeometryRef();
1416         if( poFeature->IsFieldSetAndNotNull(i) )
1417         {
1418             const OGRFieldType eType = m_poFeatureDefn->GetFieldDefn(i)->GetType();
1419             if( eType == OFTInteger || eType == OFTInteger64 )
1420             {
1421                 bOK &= VSIFPrintfL(m_fpL, CPL_FRMT_GIB,
1422                                    poFeature->GetFieldAsInteger64(i)) > 0;
1423             }
1424             else
1425             {
1426                 char* pszRecoded = CPLRecode(poFeature->GetFieldAsString(i),
1427                                               CPL_ENC_UTF8, CPL_ENC_ISO8859_1);
1428                 bOK &= VSIFPrintfL(m_fpL, "\"%s\"",
1429                                    OGRVDVEscapeString(pszRecoded).c_str()) > 0;
1430                 CPLFree(pszRecoded);
1431             }
1432         }
1433         else if( i == m_iLongitudeVDV452 &&
1434                  poGeom != nullptr && poGeom->getGeometryType() == wkbPoint )
1435         {
1436             OGRPoint* poPoint = poGeom->toPoint();
1437             const double dfDeg = poPoint->getX();
1438             const double dfAbsDeg = fabs(dfDeg);
1439             const int nDeg = static_cast<int>(dfAbsDeg);
1440             const int nMin = static_cast<int>((dfAbsDeg - nDeg) * 60);
1441             const double dfSec = (dfAbsDeg - nDeg)*3600 - nMin * 60;
1442             const int nSec = static_cast<int>(dfSec);
1443             int nMS = static_cast<int>((dfSec - nSec) * 1000 + 0.5);
1444             if( nMS == 1000 ) nMS = 999;
1445             if( dfDeg < 0 )
1446                 bOK &= VSIFPrintfL(m_fpL, "-") > 0;
1447             bOK &= VSIFPrintfL(m_fpL, "%03d%02d%02d%03d", nDeg, nMin, nSec, nMS) > 0;
1448         }
1449         else if( i == m_iLatitudeVDV452 &&
1450                  poGeom != nullptr && poGeom->getGeometryType() == wkbPoint )
1451         {
1452             OGRPoint* poPoint = poGeom->toPoint();
1453             const double dfDeg = poPoint->getY();
1454             const double dfAbsDeg = fabs(dfDeg);
1455             const int nDeg = static_cast<int>(dfAbsDeg);
1456             const int nMin = static_cast<int>((dfAbsDeg - nDeg) * 60);
1457             const double dfSec = (dfAbsDeg - nDeg)*3600 - nMin * 60;
1458             const int nSec = static_cast<int>(dfSec);
1459             int nMS = static_cast<int>((dfSec - nSec) * 1000 + 0.5);
1460             if( nMS == 1000 ) nMS = 999;
1461             if( dfDeg < 0 )
1462                 bOK &= VSIFPrintfL(m_fpL, "-") > 0;
1463             bOK &= VSIFPrintfL(m_fpL, "%02d%02d%02d%03d", nDeg, nMin, nSec, nMS) > 0;
1464         }
1465         else
1466         {
1467             bOK &= VSIFPrintfL(m_fpL, "NULL") > 0;
1468         }
1469     }
1470     bOK &= VSIFPrintfL(m_fpL, "\n") > 0;
1471 
1472     if( !bOK )
1473         return OGRERR_FAILURE;
1474 
1475     m_nFeatureCount ++;
1476     return OGRERR_NONE;
1477 }
1478 
1479 /************************************************************************/
1480 /*                         GetFeatureCount()                            */
1481 /************************************************************************/
1482 
GetFeatureCount(int)1483 GIntBig OGRVDVWriterLayer::GetFeatureCount( int )
1484 {
1485     return m_nFeatureCount >= 0 ? m_nFeatureCount : 0;
1486 }
1487 
1488 /************************************************************************/
1489 /*                          CreateField()                               */
1490 /************************************************************************/
1491 
CreateField(OGRFieldDefn * poFieldDefn,int)1492 OGRErr OGRVDVWriterLayer::CreateField(OGRFieldDefn* poFieldDefn, int /* bApprox */)
1493 {
1494     if( m_nFeatureCount >= 0 )
1495     {
1496         CPLError(CE_Failure, CPLE_NotSupported,
1497                  "Fields can no longer by added to layer %s",m_poFeatureDefn->GetName());
1498         return OGRERR_FAILURE;
1499     }
1500 
1501     if( m_poVDV452Table != nullptr )
1502     {
1503         bool bFound = false;
1504         for(size_t i=0;i<m_poVDV452Table->aosFields.size();i++)
1505         {
1506             const char* pszFieldName = poFieldDefn->GetNameRef();
1507             if( (m_osVDV452Lang == "en" &&
1508                  EQUAL(m_poVDV452Table->aosFields[i].osEnglishName, pszFieldName)) ||
1509                 (m_osVDV452Lang == "de" &&
1510                  EQUAL(m_poVDV452Table->aosFields[i].osGermanName, pszFieldName)) )
1511             {
1512                 bFound = true;
1513                 break;
1514             }
1515         }
1516         if( !bFound )
1517         {
1518             CPLError(m_bProfileStrict ? CE_Failure : CE_Warning, CPLE_AppDefined,
1519                      "Field %s is not an allowed field for table %s",
1520                      poFieldDefn->GetNameRef(), m_poFeatureDefn->GetName());
1521             if( m_bProfileStrict )
1522                 return OGRERR_FAILURE;
1523         }
1524         if( EQUAL(m_poFeatureDefn->GetName(), "STOP") ||
1525             EQUAL(m_poFeatureDefn->GetName(), "REC_ORT") )
1526         {
1527             if( EQUAL(poFieldDefn->GetNameRef(), "POINT_LONGITUDE") ||
1528                 EQUAL(poFieldDefn->GetNameRef(), "ORT_POS_LAENGE") )
1529             {
1530                 m_iLongitudeVDV452 = m_poFeatureDefn->GetFieldCount();
1531             }
1532             else if( EQUAL(poFieldDefn->GetNameRef(), "POINT_LATITUDE") ||
1533                 EQUAL(poFieldDefn->GetNameRef(), "ORT_POS_BREITE") )
1534             {
1535                 m_iLatitudeVDV452 = m_poFeatureDefn->GetFieldCount();
1536             }
1537         }
1538     }
1539 
1540     m_poFeatureDefn->AddFieldDefn(poFieldDefn);
1541     return OGRERR_NONE;
1542 }
1543 
1544 /************************************************************************/
1545 /*                         TestCapability()                             */
1546 /************************************************************************/
1547 
TestCapability(const char * pszCap)1548 int OGRVDVWriterLayer::TestCapability(const char* pszCap)
1549 {
1550     if( EQUAL(pszCap, OLCSequentialWrite) )
1551         return m_bWritePossible;
1552     if( EQUAL(pszCap, OLCCreateField) )
1553         return m_nFeatureCount < 0;
1554     return FALSE;
1555 }
1556 
1557 /************************************************************************/
1558 /*                         StopAsCurrentLayer()                         */
1559 /************************************************************************/
1560 
StopAsCurrentLayer()1561 void OGRVDVWriterLayer::StopAsCurrentLayer()
1562 {
1563     if( m_bWritePossible )
1564     {
1565         m_bWritePossible = false;
1566         if( m_fpL != nullptr )
1567         {
1568             WriteSchemaIfNeeded();
1569             VSIFPrintfL(m_fpL, "end; " CPL_FRMT_GIB "\n", m_nFeatureCount);
1570         }
1571     }
1572 }
1573 
1574 /************************************************************************/
1575 /*                         OGRVDVWriteHeader()                          */
1576 /************************************************************************/
1577 
OGRVDVWriteHeader(VSILFILE * fpL,char ** papszOptions)1578 static bool OGRVDVWriteHeader(VSILFILE* fpL, char** papszOptions)
1579 {
1580     bool bRet = true;
1581     const bool bStandardHeader =
1582             CPLFetchBool(papszOptions, "STANDARD_HEADER", true);
1583 
1584     struct tm tm;
1585     CPLUnixTimeToYMDHMS(time(nullptr), &tm);
1586     const char* pszSrc = CSLFetchNameValueDef(papszOptions, "HEADER_SRC",
1587         (bStandardHeader ) ? "UNKNOWN" : nullptr);
1588     const char* pszSrcDate = CSLFetchNameValueDef(papszOptions, "HEADER_SRC_DATE",
1589         (pszSrc) ? CPLSPrintf("%02d.%02d.%04d", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900) : nullptr);
1590     const char* pszSrcTime = CSLFetchNameValueDef(papszOptions, "HEADER_SRC_TIME",
1591         (pszSrc) ? CPLSPrintf("%02d.%02d.%02d", tm.tm_hour, tm.tm_min, tm.tm_sec) : nullptr);
1592 
1593     if( pszSrc && pszSrcDate && pszSrcTime )
1594     {
1595         bRet &= VSIFPrintfL(fpL, "mod; DD.MM.YYYY; HH:MM:SS; free\n") > 0;
1596         bRet &= VSIFPrintfL(fpL, "src; \"%s\"; \"%s\"; \"%s\"\n",
1597                             OGRVDVEscapeString(pszSrc).c_str(),
1598                             OGRVDVEscapeString(pszSrcDate).c_str(),
1599                             OGRVDVEscapeString(pszSrcTime).c_str()) > 0;
1600     }
1601 
1602     if( bStandardHeader)
1603     {
1604         const char* pszChs = CSLFetchNameValueDef(papszOptions, "HEADER_CHS", "ISO8859-1");
1605         const char* pszVer = CSLFetchNameValueDef(papszOptions, "HEADER_VER", "1.4");
1606         const char* pszIfv = CSLFetchNameValueDef(papszOptions, "HEADER_IFV", "1.4");
1607         const char* pszDve = CSLFetchNameValueDef(papszOptions, "HEADER_DVE", "1.4");
1608         const char* pszFft = CSLFetchNameValueDef(papszOptions, "HEADER_FFT", "");
1609 
1610         bRet &= VSIFPrintfL(fpL, "chs; \"%s\"\n", OGRVDVEscapeString(pszChs).c_str()) > 0;
1611         bRet &= VSIFPrintfL(fpL, "ver; \"%s\"\n", OGRVDVEscapeString(pszVer).c_str()) > 0;
1612         bRet &= VSIFPrintfL(fpL, "ifv; \"%s\"\n", OGRVDVEscapeString(pszIfv).c_str()) > 0;
1613         bRet &= VSIFPrintfL(fpL, "dve; \"%s\"\n", OGRVDVEscapeString(pszDve).c_str()) > 0;
1614         bRet &= VSIFPrintfL(fpL, "fft; \"%s\"\n", OGRVDVEscapeString(pszFft).c_str()) > 0;
1615     }
1616 
1617     for(char** papszIter = papszOptions;
1618                papszIter != nullptr && *papszIter != nullptr;
1619                papszIter++)
1620     {
1621         if( STARTS_WITH_CI(*papszIter, "HEADER_") &&
1622             !STARTS_WITH_CI(*papszIter, "HEADER_SRC") &&
1623             (!bStandardHeader ||
1624              (!EQUAL(*papszIter, "HEADER_CHS") &&
1625               !EQUAL(*papszIter, "HEADER_VER") &&
1626               !EQUAL(*papszIter, "HEADER_IFV") &&
1627               !EQUAL(*papszIter, "HEADER_DVE") &&
1628               !EQUAL(*papszIter, "HEADER_FFT"))) )
1629         {
1630             char* pszKey = nullptr;
1631             const char* pszValue = CPLParseNameValue(*papszIter, &pszKey);
1632             if( pszKey && strlen(pszKey) > strlen("HEADER_") && pszValue )
1633             {
1634                 bRet &= VSIFPrintfL(fpL, "%s; \"%s\"\n",
1635                                     pszKey + strlen("HEADER_"),
1636                                     OGRVDVEscapeString(pszValue).c_str()) > 0;
1637             }
1638             CPLFree(pszKey);
1639         }
1640     }
1641 
1642     return bRet;
1643 }
1644 
1645 /************************************************************************/
1646 /*                      OGRVDVLoadVDV452Tables()                        */
1647 /************************************************************************/
1648 
OGRVDVLoadVDV452Tables(OGRVDV452Tables & oTables)1649 static bool OGRVDVLoadVDV452Tables(OGRVDV452Tables& oTables)
1650 {
1651     const char* pszXMLDescFilename = CPLFindFile("gdal", "vdv452.xml");
1652     if (pszXMLDescFilename == nullptr)
1653     {
1654         CPLDebug("VDV", "Cannot find XML file : %s", "vdv452.xml");
1655         return false;
1656     }
1657 
1658     CPLXMLNode* psRoot = CPLParseXMLFile(pszXMLDescFilename);
1659     if( psRoot == nullptr)
1660     {
1661         return false;
1662     }
1663     CPLXMLNode* psTables = CPLGetXMLNode(psRoot, "=Layers");
1664     if( psTables != nullptr )
1665     {
1666         for(CPLXMLNode* psTable = psTables->psChild;
1667                         psTable != nullptr; psTable = psTable->psNext )
1668         {
1669             if( psTable->eType != CXT_Element || strcmp(psTable->pszValue, "Layer") != 0 )
1670                 continue;
1671             OGRVDV452Table* poTable = new OGRVDV452Table();
1672             poTable->osEnglishName = CPLGetXMLValue(psTable, "name_en", "");
1673             poTable->osGermanName = CPLGetXMLValue(psTable, "name_de", "");
1674             oTables.aosTables.push_back(poTable);
1675             oTables.oMapEnglish[poTable->osEnglishName] = poTable;
1676             oTables.oMapGerman[poTable->osGermanName] = poTable;
1677             for(CPLXMLNode* psField = psTable->psChild;
1678                         psField != nullptr; psField = psField->psNext )
1679             {
1680                 if( psField->eType != CXT_Element || strcmp(psField->pszValue, "Field") != 0 )
1681                     continue;
1682                 OGRVDV452Field oField;
1683                 oField.osEnglishName = CPLGetXMLValue(psField, "name_en", "");
1684                 oField.osGermanName = CPLGetXMLValue(psField, "name_de", "");
1685                 oField.osType = CPLGetXMLValue(psField, "type", "");
1686                 oField.nWidth = atoi(CPLGetXMLValue(psField, "width", "0"));
1687                 poTable->aosFields.push_back(oField);
1688             }
1689         }
1690     }
1691 
1692     CPLDestroyXMLNode(psRoot);
1693     return true;
1694 }
1695 
1696 /************************************************************************/
1697 /*                           ICreateLayer()                             */
1698 /************************************************************************/
1699 
1700 OGRLayer *
ICreateLayer(const char * pszLayerName,OGRSpatialReference *,OGRwkbGeometryType eGType,char ** papszOptions)1701 OGRVDVDataSource::ICreateLayer( const char *pszLayerName,
1702                                 OGRSpatialReference * /*poSpatialRef*/,
1703                                 OGRwkbGeometryType eGType,
1704                                 char ** papszOptions  )
1705 {
1706     if( !m_bUpdate )
1707         return nullptr;
1708 
1709     const char* pszProfile = CSLFetchNameValueDef(papszOptions, "PROFILE", "GENERIC");
1710     if( STARTS_WITH_CI(pszProfile, "VDV-452") && !m_bVDV452Loaded )
1711     {
1712         m_bVDV452Loaded = true;
1713         OGRVDVLoadVDV452Tables(m_oVDV452Tables);
1714     }
1715     const bool bProfileStrict =
1716         CPLFetchBool(papszOptions, "PROFILE_STRICT", false);
1717     const bool bCreateAllFields =
1718         CPLFetchBool(papszOptions, "CREATE_ALL_FIELDS", true);
1719 
1720     CPLString osUpperLayerName(pszLayerName);
1721     osUpperLayerName.toupper();
1722 
1723     OGRVDV452Table* poVDV452Table = nullptr;
1724     CPLString osVDV452Lang;
1725     bool bOKTable = true;
1726     if( EQUAL(pszProfile, "VDV-452") )
1727     {
1728         if( m_oVDV452Tables.oMapEnglish.find(osUpperLayerName) !=
1729             m_oVDV452Tables.oMapEnglish.end() )
1730         {
1731             poVDV452Table = m_oVDV452Tables.oMapEnglish[osUpperLayerName];
1732             osVDV452Lang = "en";
1733         }
1734         else if( m_oVDV452Tables.oMapGerman.find(osUpperLayerName) !=
1735                  m_oVDV452Tables.oMapGerman.end() )
1736         {
1737             poVDV452Table = m_oVDV452Tables.oMapGerman[osUpperLayerName];
1738             osVDV452Lang = "de";
1739         }
1740         else
1741         {
1742             bOKTable = false;
1743         }
1744     }
1745     else if( EQUAL(pszProfile, "VDV-452-ENGLISH") )
1746     {
1747         if( m_oVDV452Tables.oMapEnglish.find(osUpperLayerName) !=
1748             m_oVDV452Tables.oMapEnglish.end() )
1749         {
1750             poVDV452Table = m_oVDV452Tables.oMapEnglish[osUpperLayerName];
1751             osVDV452Lang = "en";
1752         }
1753         else
1754         {
1755             bOKTable = false;
1756         }
1757     }
1758     else if( EQUAL(pszProfile, "VDV-452-GERMAN") )
1759     {
1760         if( m_oVDV452Tables.oMapGerman.find(osUpperLayerName) !=
1761             m_oVDV452Tables.oMapGerman.end() )
1762         {
1763             poVDV452Table = m_oVDV452Tables.oMapGerman[osUpperLayerName];
1764             osVDV452Lang = "de";
1765         }
1766         else
1767         {
1768             bOKTable = false;
1769         }
1770     }
1771     if( !bOKTable )
1772     {
1773         CPLError(bProfileStrict ? CE_Failure : CE_Warning,
1774                  CPLE_AppDefined, "%s is not a VDV-452 table",
1775                  pszLayerName);
1776         if( bProfileStrict )
1777             return nullptr;
1778     }
1779 
1780     VSILFILE* fpL = nullptr;
1781     if( m_bSingleFile )
1782     {
1783         fpL = m_fpL;
1784         if( !m_bNew && m_nLayerCount == 0 )
1785         {
1786             // Find last non-empty line in the file
1787             VSIFSeekL(fpL, 0, SEEK_END);
1788             vsi_l_offset nFileSize = VSIFTellL(fpL);
1789             vsi_l_offset nOffset = nFileSize;
1790             bool bTerminatingEOL = true;
1791             while( nOffset > 0 )
1792             {
1793                 VSIFSeekL(fpL, nOffset - 1, SEEK_SET);
1794                 char ch = '\0';
1795                 VSIFReadL(&ch, 1, 1, fpL);
1796                 if( bTerminatingEOL )
1797                 {
1798                     if( !(ch == '\r' || ch == '\n') )
1799                     {
1800                         bTerminatingEOL = false;
1801                     }
1802                 }
1803                 else
1804                 {
1805                     if( ch == '\r' || ch == '\n' )
1806                         break;
1807                 }
1808                 nOffset --;
1809             }
1810 
1811             // If it is "eof;..." then overwrite it with new content
1812             const char* pszLine = CPLReadLineL(fpL);
1813             if( pszLine != nullptr && STARTS_WITH(pszLine, "eof;") )
1814             {
1815                 VSIFSeekL(fpL, nOffset, SEEK_SET);
1816                 VSIFTruncateL(fpL, VSIFTellL(fpL));
1817             }
1818             else if( nFileSize > 0 )
1819             {
1820                 // Otherwise make sure the file ends with an eol character
1821                 VSIFSeekL(fpL, nFileSize - 1, SEEK_SET);
1822                 char ch = '\0';
1823                 VSIFReadL(&ch, 1, 1, fpL);
1824                 VSIFSeekL(fpL, nFileSize, SEEK_SET);
1825                 if( !(ch == '\r' || ch == '\n') )
1826                 {
1827                     ch = '\n';
1828                     VSIFWriteL(&ch, 1, 1, fpL);
1829                 }
1830             }
1831         }
1832     }
1833     else
1834     {
1835         CPLString osExtension = CSLFetchNameValueDef(papszOptions, "EXTENSION", "x10");
1836         CPLString osFilename = CPLFormFilename(m_osFilename, pszLayerName,
1837                                                osExtension);
1838         fpL = VSIFOpenL(osFilename, "wb");
1839         if( fpL == nullptr )
1840         {
1841             CPLError( CE_Failure, CPLE_FileIO, "Cannot create %s",
1842                       osFilename.c_str() );
1843             return nullptr;
1844         }
1845     }
1846 
1847     GetLayerCount();
1848 
1849     if( m_nLayerCount == 0 || !m_bSingleFile )
1850     {
1851         if( !OGRVDVWriteHeader(fpL, papszOptions) )
1852         {
1853             if( !m_bSingleFile )
1854                 VSIFCloseL(fpL);
1855             return nullptr;
1856         }
1857     }
1858 
1859     m_bMustWriteEof = true;
1860 
1861     OGRVDVWriterLayer* poLayer = new OGRVDVWriterLayer(this,
1862                                                        pszLayerName,
1863                                                        fpL,
1864                                                        !m_bSingleFile,
1865                                                        poVDV452Table,
1866                                                        osVDV452Lang,
1867                                                        bProfileStrict);
1868     m_papoLayers = static_cast<OGRLayer**>(CPLRealloc(
1869                 m_papoLayers, sizeof(OGRLayer*) * (m_nLayerCount + 1) ));
1870     m_papoLayers[m_nLayerCount] = poLayer;
1871     m_nLayerCount ++;
1872 
1873     if( eGType == wkbPoint && poVDV452Table != nullptr &&
1874         (EQUAL(pszLayerName, "STOP") || EQUAL(pszLayerName, "REC_ORT")) )
1875     {
1876         poLayer->GetLayerDefn()->SetGeomType(wkbPoint);
1877     }
1878 
1879     if( bCreateAllFields && poVDV452Table != nullptr )
1880     {
1881         for(size_t i=0; i<poVDV452Table->aosFields.size();i++)
1882         {
1883             const char* pszFieldName = (osVDV452Lang == "en") ?
1884                             poVDV452Table->aosFields[i].osEnglishName.c_str() :
1885                             poVDV452Table->aosFields[i].osGermanName.c_str();
1886             OGRFieldType eType = OFTString;
1887             int nWidth = poVDV452Table->aosFields[i].nWidth;
1888             if( poVDV452Table->aosFields[i].osType == "num" ||
1889                 poVDV452Table->aosFields[i].osType == "boolean" )
1890                 eType = OFTInteger;
1891             if( poVDV452Table->aosFields[i].osType == "num" )
1892             {
1893                 /* VDV 451 is without sign */
1894                 nWidth ++;
1895                 if( nWidth >= 10 )
1896                     eType = OFTInteger64;
1897             }
1898             OGRFieldDefn oField( pszFieldName, eType );
1899             if( poVDV452Table->aosFields[i].osType == "boolean" )
1900                 oField.SetSubType(OFSTBoolean);
1901             oField.SetWidth( nWidth );
1902             poLayer->CreateField(&oField);
1903         }
1904     }
1905 
1906     return poLayer;
1907 }
1908 
1909 /************************************************************************/
1910 /*                       SetCurrentWriterLayer()                        */
1911 /************************************************************************/
1912 
SetCurrentWriterLayer(OGRVDVWriterLayer * poLayer)1913 void OGRVDVDataSource::SetCurrentWriterLayer(OGRVDVWriterLayer* poLayer)
1914 {
1915     if( !m_bSingleFile )
1916         return;
1917     if( m_poCurrentWriterLayer != nullptr && m_poCurrentWriterLayer != poLayer )
1918     {
1919         m_poCurrentWriterLayer->StopAsCurrentLayer();
1920     }
1921     m_poCurrentWriterLayer = poLayer;
1922 }
1923 
1924 /************************************************************************/
1925 /*                           TestCapability()                           */
1926 /************************************************************************/
1927 
TestCapability(const char * pszCap)1928 int OGRVDVDataSource::TestCapability( const char * pszCap )
1929 
1930 {
1931     if( EQUAL(pszCap,ODsCCreateLayer) )
1932         return m_bUpdate;
1933     return FALSE;
1934 }
1935 
1936 /************************************************************************/
1937 /*                                 Create()                             */
1938 /************************************************************************/
1939 
Create(const char * pszName,int,int,int,GDALDataType,char ** papszOptions)1940 GDALDataset* OGRVDVDataSource::Create( const char * pszName,
1941                                         int /*nXSize*/, int /*nYSize*/, int /*nBands*/,
1942                                         GDALDataType /*eType*/,
1943                                         char ** papszOptions )
1944 
1945 {
1946 /* -------------------------------------------------------------------- */
1947 /*      First, ensure there isn't any such file yet.                    */
1948 /* -------------------------------------------------------------------- */
1949     VSIStatBufL sStatBuf;
1950     if( VSIStatL( pszName, &sStatBuf ) == 0 )
1951     {
1952         CPLError( CE_Failure, CPLE_AppDefined,
1953                   "It seems a file system object called '%s' already exists.",
1954                   pszName );
1955 
1956         return nullptr;
1957     }
1958 
1959     const bool bSingleFile = CPLFetchBool(papszOptions, "SINGLE_FILE", true);
1960     if( !bSingleFile )
1961     {
1962         if( VSIMkdir( pszName, 0755 ) != 0 )
1963         {
1964             CPLError( CE_Failure, CPLE_AppDefined,
1965                       "Failed to create directory %s:\n%s",
1966                       pszName, VSIStrerror( errno ) );
1967             return nullptr;
1968         }
1969     }
1970 
1971     VSILFILE* fpL = nullptr;
1972     if( bSingleFile )
1973     {
1974         fpL = VSIFOpenL(pszName, "wb");
1975         if( fpL == nullptr )
1976         {
1977             CPLError( CE_Failure, CPLE_FileIO, "Cannot create %s", pszName );
1978             return nullptr;
1979         }
1980     }
1981     OGRVDVDataSource* poDS = new OGRVDVDataSource(pszName, fpL, true,
1982                                                   bSingleFile,
1983                                                   true /* new */);
1984     return poDS;
1985 }
1986 
1987 /************************************************************************/
1988 /*                         RegisterOGRVDV()                             */
1989 /************************************************************************/
1990 
RegisterOGRVDV()1991 void RegisterOGRVDV()
1992 
1993 {
1994     if( GDALGetDriverByName( "VDV" ) != nullptr )
1995         return;
1996 
1997     GDALDriver *poDriver = new GDALDriver();
1998 
1999     poDriver->SetDescription( "VDV" );
2000     poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" );
2001     poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
2002                                "VDV-451/VDV-452/INTREST Data Format" );
2003     poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "drivers/vector/vdv.html" );
2004     poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "txt x10");
2005     poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
2006     poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES,
2007                                "Integer Integer64 String" );
2008 
2009     poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
2010 "<CreationOptionList>"
2011 "  <Option name='SINGLE_FILE' type='boolean' description='Whether several layers "
2012 "should be put in the same file. If no, the name is assumed to be a directory name' default='YES'/>"
2013 "</CreationOptionList>");
2014 
2015     poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST,
2016 "<LayerCreationOptionList>"
2017 "  <Option name='EXTENSION' type='string' description='Layer file extension. Only used for SINGLE_FILE=NO' default='x10'/>"
2018 "  <Option name='PROFILE' type='string-select' description='Profile' default='GENERIC'>"
2019 "       <Value>GENERIC</Value>"
2020 "       <Value>VDV-452</Value>"
2021 "       <Value>VDV-452-ENGLISH</Value>"
2022 "       <Value>VDV-452-GERMAN</Value>"
2023 "  </Option>"
2024 "  <Option name='PROFILE_STRICT' type='boolean' description='Whether checks of profile should be strict' default='NO'/>"
2025 "  <Option name='CREATE_ALL_FIELDS' type='boolean' description="
2026     "'Whether all fields of predefined profiles should be created at layer creation' default='YES'/>"
2027 "  <Option name='STANDARD_HEADER' type='boolean' description='Whether to write standard header fields' default='YES'/>"
2028 "  <Option name='HEADER_SRC' type='string' description='Value of the src header field' default='UNKNOWN'/>"
2029 "  <Option name='HEADER_SRC_DATE' type='string' description='Value of the date of the src header field as DD.MM.YYYY'/>"
2030 "  <Option name='HEADER_SRC_TIME' type='string' description='Value of the time of the src header field as HH.MM.SS'/>"
2031 "  <Option name='HEADER_CHS' type='string' description='Value of the chs header field' default='ISO8859-1'/>"
2032 "  <Option name='HEADER_VER' type='string' description='Value of the ver header field' default='1.4'/>"
2033 "  <Option name='HEADER_IFV' type='string' description='Value of the ifv header field' default='1.4'/>"
2034 "  <Option name='HEADER_DVE' type='string' description='Value of the dve header field' default='1.4'/>"
2035 "  <Option name='HEADER_FFT' type='string' description='Value of the fft header field' default=''/>"
2036 "  <Option name='HEADER_*' type='string' description='Value of another header field'/>"
2037 "</LayerCreationOptionList>");
2038     poDriver->pfnIdentify = OGRVDVDriverIdentify;
2039     poDriver->pfnOpen = OGRVDVDataSource::Open;
2040     poDriver->pfnCreate = OGRVDVDataSource::Create;
2041 
2042     GetGDALDriverManager()->RegisterDriver( poDriver );
2043 }
2044