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