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