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