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