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