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