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