/src/gdal/ogr/ogrsf_frmts/nas/nasreader.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: NAS Reader |
4 | | * Purpose: Implementation of NASReader class. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2002, Frank Warmerdam |
9 | | * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "gmlreaderp.h" |
15 | | #include "gmlreader.h" |
16 | | |
17 | | #include <algorithm> |
18 | | |
19 | | #include "cpl_conv.h" |
20 | | #include "cpl_error.h" |
21 | | #include "cpl_multiproc.h" |
22 | | #include "cpl_string.h" |
23 | | #include "gmlutils.h" |
24 | | #include "ogr_geometry.h" |
25 | | |
26 | | /************************************************************************/ |
27 | | /* ==================================================================== */ |
28 | | /* With XERCES Library */ |
29 | | /* ==================================================================== */ |
30 | | /************************************************************************/ |
31 | | |
32 | | #include "nasreaderp.h" |
33 | | |
34 | | /************************************************************************/ |
35 | | /* CreateNASReader() */ |
36 | | /************************************************************************/ |
37 | | |
38 | | IGMLReader *CreateNASReader() |
39 | | |
40 | 0 | { |
41 | 0 | return new NASReader(); |
42 | 0 | } |
43 | | |
44 | | /************************************************************************/ |
45 | | /* NASReader() */ |
46 | | /************************************************************************/ |
47 | | |
48 | | NASReader::NASReader() |
49 | 0 | : m_bClassListLocked(false), m_nClassCount(0), m_papoClass(nullptr), |
50 | 0 | m_pszFilename(nullptr), m_poNASHandler(nullptr), m_poSAXReader(nullptr), |
51 | 0 | m_bReadStarted(false), m_bXercesInitialized(false), m_poState(nullptr), |
52 | 0 | m_poCompleteFeature(nullptr), m_fp(nullptr), m_GMLInputSource(nullptr), |
53 | 0 | m_pszFilteredClassName(nullptr) |
54 | 0 | { |
55 | 0 | } |
56 | | |
57 | | /************************************************************************/ |
58 | | /* ~NASReader() */ |
59 | | /************************************************************************/ |
60 | | |
61 | | NASReader::~NASReader() |
62 | | |
63 | 0 | { |
64 | 0 | NASReader::ClearClasses(); |
65 | |
|
66 | 0 | CPLFree(m_pszFilename); |
67 | |
|
68 | 0 | CleanupParser(); |
69 | |
|
70 | 0 | if (m_fp) |
71 | 0 | VSIFCloseL(m_fp); |
72 | |
|
73 | 0 | if (m_bXercesInitialized) |
74 | 0 | OGRDeinitializeXerces(); |
75 | |
|
76 | 0 | CPLFree(m_pszFilteredClassName); |
77 | 0 | } |
78 | | |
79 | | /************************************************************************/ |
80 | | /* SetSourceFile() */ |
81 | | /************************************************************************/ |
82 | | |
83 | | void NASReader::SetSourceFile(const char *pszFilename) |
84 | | |
85 | 0 | { |
86 | 0 | CPLFree(m_pszFilename); |
87 | 0 | m_pszFilename = CPLStrdup(pszFilename); |
88 | 0 | } |
89 | | |
90 | | /************************************************************************/ |
91 | | /* GetSourceFileName() */ |
92 | | /************************************************************************/ |
93 | | |
94 | | const char *NASReader::GetSourceFileName() |
95 | 0 | { |
96 | 0 | return m_pszFilename; |
97 | 0 | } |
98 | | |
99 | | /************************************************************************/ |
100 | | /* SetupParser() */ |
101 | | /************************************************************************/ |
102 | | |
103 | | bool NASReader::SetupParser() |
104 | | |
105 | 0 | { |
106 | 0 | if (m_fp == nullptr) |
107 | 0 | m_fp = VSIFOpenL(m_pszFilename, "rb"); |
108 | 0 | if (m_fp == nullptr) |
109 | 0 | return false; |
110 | 0 | VSIFSeekL(m_fp, 0, SEEK_SET); |
111 | |
|
112 | 0 | if (!m_bXercesInitialized) |
113 | 0 | { |
114 | 0 | if (!OGRInitializeXerces()) |
115 | 0 | return false; |
116 | 0 | m_bXercesInitialized = true; |
117 | 0 | } |
118 | | |
119 | | // Cleanup any old parser. |
120 | 0 | if (m_poSAXReader != nullptr) |
121 | 0 | CleanupParser(); |
122 | | |
123 | | // Create and initialize parser. |
124 | 0 | XMLCh *xmlUriValid = nullptr; |
125 | 0 | XMLCh *xmlUriNS = nullptr; |
126 | |
|
127 | 0 | try |
128 | 0 | { |
129 | 0 | m_poSAXReader = XMLReaderFactory::createXMLReader(); |
130 | |
|
131 | 0 | m_poNASHandler = new NASHandler(this); |
132 | |
|
133 | 0 | m_poSAXReader->setContentHandler(m_poNASHandler); |
134 | 0 | m_poSAXReader->setErrorHandler(m_poNASHandler); |
135 | 0 | m_poSAXReader->setLexicalHandler(m_poNASHandler); |
136 | 0 | m_poSAXReader->setEntityResolver(m_poNASHandler); |
137 | 0 | m_poSAXReader->setDTDHandler(m_poNASHandler); |
138 | 0 | m_poSAXReader->setFeature( |
139 | 0 | XMLUni::fgXercesDisableDefaultEntityResolution, true); |
140 | |
|
141 | 0 | xmlUriValid = |
142 | 0 | XMLString::transcode("http://xml.org/sax/features/validation"); |
143 | 0 | xmlUriNS = |
144 | 0 | XMLString::transcode("http://xml.org/sax/features/namespaces"); |
145 | |
|
146 | | #if (OGR_GML_VALIDATION) |
147 | | m_poSAXReader->setFeature(xmlUriValid, true); |
148 | | m_poSAXReader->setFeature(xmlUriNS, true); |
149 | | |
150 | | m_poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true); |
151 | | m_poSAXReader->setFeature(XMLUni::fgXercesSchema, true); |
152 | | |
153 | | // m_poSAXReader->setDoSchema(true); |
154 | | // m_poSAXReader->setValidationSchemaFullChecking(true); |
155 | | #else |
156 | 0 | m_poSAXReader->setFeature(XMLUni::fgSAX2CoreValidation, false); |
157 | |
|
158 | 0 | m_poSAXReader->setFeature(XMLUni::fgXercesSchema, false); |
159 | |
|
160 | 0 | #endif |
161 | 0 | XMLString::release(&xmlUriValid); |
162 | 0 | XMLString::release(&xmlUriNS); |
163 | 0 | } |
164 | 0 | catch (...) |
165 | 0 | { |
166 | 0 | XMLString::release(&xmlUriValid); |
167 | 0 | XMLString::release(&xmlUriNS); |
168 | |
|
169 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
170 | 0 | "NAS: Exception initializing Xerces based GML reader.\n"); |
171 | 0 | return false; |
172 | 0 | } |
173 | | |
174 | 0 | m_bReadStarted = false; |
175 | | |
176 | | // Push an empty state. |
177 | 0 | PushState(new GMLReadState()); |
178 | |
|
179 | 0 | if (m_GMLInputSource == nullptr) |
180 | 0 | { |
181 | 0 | m_GMLInputSource = OGRCreateXercesInputSource(m_fp); |
182 | 0 | } |
183 | |
|
184 | 0 | return true; |
185 | 0 | } |
186 | | |
187 | | /************************************************************************/ |
188 | | /* CleanupParser() */ |
189 | | /************************************************************************/ |
190 | | |
191 | | void NASReader::CleanupParser() |
192 | | |
193 | 0 | { |
194 | 0 | if (m_poSAXReader == nullptr) |
195 | 0 | return; |
196 | | |
197 | 0 | while (m_poState) |
198 | 0 | PopState(); |
199 | |
|
200 | 0 | delete m_poSAXReader; |
201 | 0 | m_poSAXReader = nullptr; |
202 | |
|
203 | 0 | delete m_poNASHandler; |
204 | 0 | m_poNASHandler = nullptr; |
205 | |
|
206 | 0 | delete m_poCompleteFeature; |
207 | 0 | m_poCompleteFeature = nullptr; |
208 | |
|
209 | 0 | OGRDestroyXercesInputSource(m_GMLInputSource); |
210 | 0 | m_GMLInputSource = nullptr; |
211 | |
|
212 | 0 | m_bReadStarted = false; |
213 | 0 | } |
214 | | |
215 | | /************************************************************************/ |
216 | | /* NextFeature() */ |
217 | | /************************************************************************/ |
218 | | |
219 | | GMLFeature *NASReader::NextFeature() |
220 | | |
221 | 0 | { |
222 | 0 | GMLFeature *poReturn = nullptr; |
223 | |
|
224 | 0 | try |
225 | 0 | { |
226 | 0 | if (!m_bReadStarted) |
227 | 0 | { |
228 | 0 | if (m_poSAXReader == nullptr) |
229 | 0 | SetupParser(); |
230 | |
|
231 | 0 | if (m_poSAXReader == nullptr) |
232 | 0 | return nullptr; |
233 | | |
234 | 0 | if (!m_poSAXReader->parseFirst(*m_GMLInputSource, m_oToFill)) |
235 | 0 | return nullptr; |
236 | 0 | m_bReadStarted = true; |
237 | 0 | } |
238 | | |
239 | 0 | while (m_poCompleteFeature == nullptr && !m_bStopParsing && |
240 | 0 | m_poSAXReader->parseNext(m_oToFill)) |
241 | 0 | { |
242 | 0 | } |
243 | |
|
244 | 0 | poReturn = m_poCompleteFeature; |
245 | 0 | m_poCompleteFeature = nullptr; |
246 | 0 | } |
247 | 0 | catch (const XMLException &toCatch) |
248 | 0 | { |
249 | 0 | m_bStopParsing = true; |
250 | 0 | CPLDebug("NAS", "Error during NextFeature()! Message:\n%s", |
251 | 0 | transcode(toCatch.getMessage()).c_str()); |
252 | 0 | } |
253 | 0 | catch (const SAXException &toCatch) |
254 | 0 | { |
255 | 0 | CPLString osErrMsg; |
256 | 0 | transcode(toCatch.getMessage(), osErrMsg); |
257 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrMsg.c_str()); |
258 | 0 | m_bStopParsing = true; |
259 | 0 | } |
260 | | |
261 | 0 | return poReturn; |
262 | 0 | } |
263 | | |
264 | | /************************************************************************/ |
265 | | /* PushFeature() */ |
266 | | /* */ |
267 | | /* Create a feature based on the named element. If the */ |
268 | | /* corresponding feature class doesn't exist yet, then create */ |
269 | | /* it now. A new GMLReadState will be created for the feature, */ |
270 | | /* and it will be placed within that state. The state is */ |
271 | | /* pushed onto the readstate stack. */ |
272 | | /************************************************************************/ |
273 | | |
274 | | void NASReader::PushFeature(const char *pszElement, const Attributes &attrs) |
275 | | |
276 | 0 | { |
277 | | /* -------------------------------------------------------------------- */ |
278 | | /* Find the class of this element. */ |
279 | | /* -------------------------------------------------------------------- */ |
280 | 0 | int iClass = 0; |
281 | 0 | for (; iClass < GetClassCount(); iClass++) |
282 | 0 | { |
283 | 0 | if (strcmp(pszElement, GetClass(iClass)->GetElementName()) == 0) |
284 | 0 | break; |
285 | 0 | } |
286 | | |
287 | | /* -------------------------------------------------------------------- */ |
288 | | /* Create a new feature class for this element, if there is no */ |
289 | | /* existing class for it. */ |
290 | | /* -------------------------------------------------------------------- */ |
291 | 0 | if (iClass == GetClassCount()) |
292 | 0 | { |
293 | 0 | CPLAssert(!IsClassListLocked()); |
294 | |
|
295 | 0 | GMLFeatureClass *poNewClass = new GMLFeatureClass(pszElement); |
296 | |
|
297 | 0 | if (EQUAL(pszElement, "Delete")) |
298 | 0 | { |
299 | 0 | const struct |
300 | 0 | { |
301 | 0 | const char *pszName; |
302 | 0 | GMLPropertyType eType; |
303 | 0 | int width; |
304 | 0 | } types[] = { |
305 | 0 | {"typeName", GMLPT_String, -1}, |
306 | 0 | {"FeatureId", GMLPT_String, -1}, |
307 | 0 | {"context", GMLPT_String, -1}, |
308 | 0 | {"safeToIgnore", GMLPT_String, -1}, |
309 | 0 | {"replacedBy", GMLPT_String, -1}, |
310 | 0 | {"anlass", GMLPT_StringList, -1}, |
311 | 0 | {"endet", GMLPT_String, 20}, |
312 | 0 | {"ignored", GMLPT_String, -1}, |
313 | 0 | }; |
314 | |
|
315 | 0 | for (unsigned int i = 0; i < CPL_ARRAYSIZE(types); i++) |
316 | 0 | { |
317 | 0 | GMLPropertyDefn *poPDefn = |
318 | 0 | new GMLPropertyDefn(types[i].pszName, types[i].pszName); |
319 | |
|
320 | 0 | poPDefn->SetType(types[i].eType); |
321 | 0 | if (types[i].width > 0) |
322 | 0 | poPDefn->SetWidth(types[i].width); |
323 | |
|
324 | 0 | poNewClass->AddProperty(poPDefn); |
325 | 0 | } |
326 | 0 | } |
327 | |
|
328 | 0 | iClass = AddClass(poNewClass); |
329 | 0 | } |
330 | | |
331 | | /* -------------------------------------------------------------------- */ |
332 | | /* Create a feature of this feature class. */ |
333 | | /* -------------------------------------------------------------------- */ |
334 | 0 | GMLFeature *poFeature = new GMLFeature(GetClass(iClass)); |
335 | | |
336 | | /* -------------------------------------------------------------------- */ |
337 | | /* Create and push a new read state. */ |
338 | | /* -------------------------------------------------------------------- */ |
339 | 0 | GMLReadState *poState = new GMLReadState(); |
340 | 0 | poState->m_poFeature = poFeature; |
341 | 0 | PushState(poState); |
342 | | |
343 | | /* -------------------------------------------------------------------- */ |
344 | | /* Check for gml:id, and if found push it as an attribute named */ |
345 | | /* gml_id. */ |
346 | | /* -------------------------------------------------------------------- */ |
347 | 0 | const XMLCh achGmlId[] = {'g', 'm', 'l', ':', 'i', 'd', 0}; |
348 | 0 | int nFIDIndex = attrs.getIndex(achGmlId); |
349 | 0 | if (nFIDIndex != -1) |
350 | 0 | { |
351 | 0 | char *pszFID = CPLStrdup(transcode(attrs.getValue(nFIDIndex))); |
352 | 0 | SetFeaturePropertyDirectly("gml_id", pszFID); |
353 | 0 | } |
354 | 0 | } |
355 | | |
356 | | /************************************************************************/ |
357 | | /* IsFeatureElement() */ |
358 | | /* */ |
359 | | /* Based on context and the element name, is this element a new */ |
360 | | /* GML feature element? */ |
361 | | /************************************************************************/ |
362 | | |
363 | | bool NASReader::IsFeatureElement(const char *pszElement) |
364 | | |
365 | 0 | { |
366 | 0 | CPLAssert(m_poState != nullptr); |
367 | |
|
368 | 0 | const char *pszLast = m_poState->GetLastComponent(); |
369 | 0 | const int nLen = static_cast<int>(strlen(pszLast)); |
370 | | |
371 | | // There seem to be two major NAS classes of feature identifiers |
372 | | // -- either a wfs:Insert or a gml:featureMember/wfs:member |
373 | |
|
374 | 0 | if ((nLen < 6 || !EQUAL(pszLast + nLen - 6, "Insert")) && |
375 | 0 | (nLen < 13 || !EQUAL(pszLast + nLen - 13, "featureMember")) && |
376 | 0 | (nLen < 6 || !EQUAL(pszLast + nLen - 6, "member")) && |
377 | 0 | (nLen < 7 || !EQUAL(pszLast + nLen - 7, "Replace"))) |
378 | 0 | return false; |
379 | | |
380 | | // If the class list isn't locked, any element that is a featureMember |
381 | | // will do. |
382 | 0 | if (EQUAL(pszElement, "Filter")) |
383 | 0 | return false; |
384 | | |
385 | 0 | if (!IsClassListLocked()) |
386 | 0 | return true; |
387 | | |
388 | 0 | if (EQUAL(pszElement, "Delete")) |
389 | 0 | return false; |
390 | | |
391 | | // otherwise, find a class with the desired element name. |
392 | 0 | for (int i = 0; i < GetClassCount(); i++) |
393 | 0 | { |
394 | 0 | if (EQUAL(pszElement, GetClass(i)->GetElementName())) |
395 | 0 | return true; |
396 | 0 | } |
397 | | |
398 | 0 | return false; |
399 | 0 | } |
400 | | |
401 | | /************************************************************************/ |
402 | | /* IsAttributeElement() */ |
403 | | /************************************************************************/ |
404 | | |
405 | | bool NASReader::IsAttributeElement(const char *pszElement, |
406 | | const Attributes &attrs) |
407 | | |
408 | 0 | { |
409 | 0 | if (m_poState->m_poFeature == nullptr) |
410 | 0 | return false; |
411 | | |
412 | 0 | GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass(); |
413 | | |
414 | | // If the schema is not yet locked, then any simple element |
415 | | // is potentially an attribute. |
416 | 0 | if (!poClass->IsSchemaLocked()) |
417 | 0 | return true; |
418 | | |
419 | | // Otherwise build the path to this element into a single string |
420 | | // and compare against known attributes. |
421 | 0 | CPLString osElemPath; |
422 | |
|
423 | 0 | if (m_poState->m_nPathLength == 0) |
424 | 0 | osElemPath = pszElement; |
425 | 0 | else |
426 | 0 | { |
427 | 0 | osElemPath = m_poState->osPath; |
428 | 0 | osElemPath += "|"; |
429 | 0 | osElemPath += pszElement; |
430 | 0 | } |
431 | |
|
432 | 0 | if (poClass->GetPropertyIndexBySrcElement( |
433 | 0 | osElemPath.c_str(), static_cast<int>(osElemPath.size())) >= 0) |
434 | 0 | return true; |
435 | | |
436 | 0 | for (unsigned int idx = 0; idx < attrs.getLength(); ++idx) |
437 | 0 | { |
438 | 0 | CPLString osAttrName = transcode(attrs.getQName(idx)); |
439 | 0 | CPLString osAttrPath; |
440 | |
|
441 | 0 | const char *pszName = strchr(osAttrName.c_str(), ':'); |
442 | 0 | if (pszName) |
443 | 0 | { |
444 | 0 | osAttrPath = osElemPath + "@" + (pszName + 1); |
445 | 0 | if (poClass->GetPropertyIndexBySrcElement( |
446 | 0 | osAttrPath.c_str(), static_cast<int>(osAttrPath.size())) >= |
447 | 0 | 0) |
448 | 0 | return true; |
449 | 0 | } |
450 | | |
451 | 0 | osAttrPath = osElemPath + "@" + osAttrName; |
452 | 0 | if (poClass->GetPropertyIndexBySrcElement( |
453 | 0 | osAttrPath.c_str(), static_cast<int>(osAttrPath.size())) >= 0) |
454 | 0 | return true; |
455 | 0 | } |
456 | | |
457 | 0 | return false; |
458 | 0 | } |
459 | | |
460 | | /************************************************************************/ |
461 | | /* PopState() */ |
462 | | /************************************************************************/ |
463 | | |
464 | | void NASReader::PopState() |
465 | | |
466 | 0 | { |
467 | 0 | if (m_poState != nullptr) |
468 | 0 | { |
469 | 0 | if (m_poState->m_poFeature != nullptr && m_poCompleteFeature == nullptr) |
470 | 0 | { |
471 | 0 | m_poCompleteFeature = m_poState->m_poFeature; |
472 | 0 | m_poState->m_poFeature = nullptr; |
473 | 0 | } |
474 | 0 | else if (m_poState->m_poFeature != nullptr) |
475 | 0 | { |
476 | 0 | delete m_poState->m_poFeature; |
477 | 0 | m_poState->m_poFeature = nullptr; |
478 | 0 | } |
479 | |
|
480 | 0 | GMLReadState *poParent = m_poState->m_poParentState; |
481 | |
|
482 | 0 | delete m_poState; |
483 | 0 | m_poState = poParent; |
484 | 0 | } |
485 | 0 | } |
486 | | |
487 | | /************************************************************************/ |
488 | | /* PushState() */ |
489 | | /************************************************************************/ |
490 | | |
491 | | void NASReader::PushState(GMLReadState *poState) |
492 | | |
493 | 0 | { |
494 | 0 | poState->m_poParentState = m_poState; |
495 | 0 | m_poState = poState; |
496 | 0 | } |
497 | | |
498 | | /************************************************************************/ |
499 | | /* GetClass() */ |
500 | | /************************************************************************/ |
501 | | |
502 | | GMLFeatureClass *NASReader::GetClass(int iClass) const |
503 | | |
504 | 0 | { |
505 | 0 | if (iClass < 0 || iClass >= m_nClassCount) |
506 | 0 | return nullptr; |
507 | | |
508 | 0 | return m_papoClass[iClass]; |
509 | 0 | } |
510 | | |
511 | | /************************************************************************/ |
512 | | /* GetClass() */ |
513 | | /************************************************************************/ |
514 | | |
515 | | GMLFeatureClass *NASReader::GetClass(const char *pszName) const |
516 | | |
517 | 0 | { |
518 | 0 | for (int iClass = 0; iClass < m_nClassCount; iClass++) |
519 | 0 | { |
520 | 0 | if (strcmp(m_papoClass[iClass]->GetName(), pszName) == 0) |
521 | 0 | return m_papoClass[iClass]; |
522 | 0 | } |
523 | | |
524 | 0 | return nullptr; |
525 | 0 | } |
526 | | |
527 | | /************************************************************************/ |
528 | | /* AddClass() */ |
529 | | /************************************************************************/ |
530 | | |
531 | | int NASReader::AddClass(GMLFeatureClass *poNewClass) |
532 | | |
533 | 0 | { |
534 | 0 | CPLAssert(poNewClass != nullptr && |
535 | 0 | GetClass(poNewClass->GetName()) == nullptr); |
536 | |
|
537 | 0 | m_nClassCount++; |
538 | 0 | m_papoClass = static_cast<GMLFeatureClass **>( |
539 | 0 | CPLRealloc(m_papoClass, sizeof(void *) * m_nClassCount)); |
540 | | |
541 | | // keep delete the last entry |
542 | 0 | if (m_nClassCount > 1 && |
543 | 0 | EQUAL(m_papoClass[m_nClassCount - 2]->GetName(), "Delete")) |
544 | 0 | { |
545 | 0 | m_papoClass[m_nClassCount - 1] = m_papoClass[m_nClassCount - 2]; |
546 | 0 | m_papoClass[m_nClassCount - 2] = poNewClass; |
547 | 0 | return m_nClassCount - 2; |
548 | 0 | } |
549 | 0 | else |
550 | 0 | { |
551 | 0 | m_papoClass[m_nClassCount - 1] = poNewClass; |
552 | 0 | return m_nClassCount - 1; |
553 | 0 | } |
554 | 0 | } |
555 | | |
556 | | /************************************************************************/ |
557 | | /* ClearClasses() */ |
558 | | /************************************************************************/ |
559 | | |
560 | | void NASReader::ClearClasses() |
561 | | |
562 | 0 | { |
563 | 0 | CPLDebug("NAS", "Clearing classes."); |
564 | |
|
565 | 0 | for (int i = 0; i < m_nClassCount; i++) |
566 | 0 | delete m_papoClass[i]; |
567 | 0 | CPLFree(m_papoClass); |
568 | |
|
569 | 0 | m_nClassCount = 0; |
570 | 0 | m_papoClass = nullptr; |
571 | 0 | } |
572 | | |
573 | | /************************************************************************/ |
574 | | /* SetFeaturePropertyDirectly() */ |
575 | | /* */ |
576 | | /* Set the property value on the current feature, adding the */ |
577 | | /* property name to the GMLFeatureClass if required. */ |
578 | | /* The pszValue ownership is passed to this function. */ |
579 | | /************************************************************************/ |
580 | | |
581 | | void NASReader::SetFeaturePropertyDirectly(const char *pszElement, |
582 | | char *pszValue) |
583 | | |
584 | 0 | { |
585 | 0 | GMLFeature *poFeature = GetState()->m_poFeature; |
586 | |
|
587 | 0 | CPLAssert(poFeature != nullptr); |
588 | | |
589 | | /* -------------------------------------------------------------------- */ |
590 | | /* Does this property exist in the feature class? If not, add */ |
591 | | /* it. */ |
592 | | /* -------------------------------------------------------------------- */ |
593 | 0 | GMLFeatureClass *poClass = poFeature->GetClass(); |
594 | 0 | int iProperty = poClass->GetPropertyIndexBySrcElement( |
595 | 0 | pszElement, static_cast<int>(strlen(pszElement))); |
596 | |
|
597 | 0 | if (iProperty < 0) |
598 | 0 | { |
599 | 0 | if (poClass->IsSchemaLocked()) |
600 | 0 | { |
601 | | // CPLDebug("NAS", "Encountered property %s missing from class %s schema [%s].", pszElement, poClass->GetName(), pszValue); |
602 | 0 | CPLFree(pszValue); |
603 | 0 | return; |
604 | 0 | } |
605 | | |
606 | 0 | iProperty = poClass->GetPropertyCount(); |
607 | |
|
608 | 0 | CPLString osFieldName; |
609 | |
|
610 | 0 | if (strchr(pszElement, '|') == nullptr) |
611 | 0 | osFieldName = pszElement; |
612 | 0 | else |
613 | 0 | { |
614 | 0 | osFieldName = strrchr(pszElement, '|') + 1; |
615 | 0 | if (poClass->GetPropertyIndex(osFieldName) != -1) |
616 | 0 | osFieldName = pszElement; |
617 | 0 | } |
618 | | |
619 | | // Does this conflict with an existing property name? |
620 | 0 | while (poClass->GetProperty(osFieldName) != nullptr) |
621 | 0 | { |
622 | 0 | osFieldName += "_"; |
623 | 0 | } |
624 | |
|
625 | 0 | GMLPropertyDefn *poPDefn = new GMLPropertyDefn(osFieldName, pszElement); |
626 | |
|
627 | 0 | if (EQUAL(CPLGetConfigOption("GML_FIELDTYPES", ""), "ALWAYS_STRING")) |
628 | 0 | poPDefn->SetType(GMLPT_String); |
629 | |
|
630 | 0 | poClass->AddProperty(poPDefn); |
631 | 0 | } |
632 | | |
633 | 0 | if (GMLPropertyDefn::IsSimpleType( |
634 | 0 | poClass->GetProperty(iProperty)->GetType())) |
635 | 0 | { |
636 | 0 | const GMLProperty *poProp = poFeature->GetProperty(iProperty); |
637 | 0 | if (poProp && poProp->nSubProperties > 0) |
638 | 0 | { |
639 | 0 | int iId = poClass->GetPropertyIndex("gml_id"); |
640 | 0 | const GMLProperty *poIdProp = poFeature->GetProperty(iId); |
641 | |
|
642 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
643 | 0 | "NAS: Overwriting existing property %s.%s of value '%s' " |
644 | 0 | "with '%s' (gml_id: %s; type:%d).", |
645 | 0 | poClass->GetName(), pszElement, |
646 | 0 | poProp->papszSubProperties[0], pszValue, |
647 | 0 | poIdProp && poIdProp->nSubProperties > 0 && |
648 | 0 | poIdProp->papszSubProperties && |
649 | 0 | poIdProp->papszSubProperties[0] |
650 | 0 | ? poIdProp->papszSubProperties[0] |
651 | 0 | : "(null)", |
652 | 0 | poClass->GetProperty(iProperty)->GetType()); |
653 | 0 | } |
654 | 0 | } |
655 | | |
656 | | /* -------------------------------------------------------------------- */ |
657 | | /* Set the property */ |
658 | | /* -------------------------------------------------------------------- */ |
659 | 0 | poFeature->SetPropertyDirectly(iProperty, pszValue); |
660 | | |
661 | | /* -------------------------------------------------------------------- */ |
662 | | /* Do we need to update the property type? */ |
663 | | /* -------------------------------------------------------------------- */ |
664 | 0 | if (!poClass->IsSchemaLocked()) |
665 | 0 | { |
666 | 0 | auto poClassProperty = poClass->GetProperty(iProperty); |
667 | 0 | if (poClassProperty) |
668 | 0 | { |
669 | 0 | const GMLProperty *poProp = poFeature->GetProperty(iProperty); |
670 | 0 | if (poProp) |
671 | 0 | { |
672 | 0 | poClassProperty->AnalysePropertyValue(poProp); |
673 | 0 | } |
674 | 0 | } |
675 | 0 | else |
676 | 0 | { |
677 | 0 | CPLAssert(false); |
678 | 0 | } |
679 | 0 | } |
680 | 0 | } |
681 | | |
682 | | /************************************************************************/ |
683 | | /* LoadClasses() */ |
684 | | /************************************************************************/ |
685 | | |
686 | | bool NASReader::LoadClasses(const char *pszFile) |
687 | | |
688 | 0 | { |
689 | | // Add logic later to determine reasonable default schema file. |
690 | 0 | if (pszFile == nullptr) |
691 | 0 | return false; |
692 | | |
693 | 0 | CPLDebug("NAS", "Loading classes from %s", pszFile); |
694 | | |
695 | | /* -------------------------------------------------------------------- */ |
696 | | /* Load the raw XML file. */ |
697 | | /* -------------------------------------------------------------------- */ |
698 | 0 | VSILFILE *fp = VSIFOpenL(pszFile, "rb"); |
699 | |
|
700 | 0 | if (fp == nullptr) |
701 | 0 | { |
702 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, "NAS: Failed to open file %s.", |
703 | 0 | pszFile); |
704 | 0 | return false; |
705 | 0 | } |
706 | | |
707 | 0 | VSIFSeekL(fp, 0, SEEK_END); |
708 | 0 | int nLength = static_cast<int>(VSIFTellL(fp)); |
709 | 0 | VSIFSeekL(fp, 0, SEEK_SET); |
710 | |
|
711 | 0 | char *pszWholeText = static_cast<char *>(VSIMalloc(nLength + 1)); |
712 | 0 | if (pszWholeText == nullptr) |
713 | 0 | { |
714 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
715 | 0 | "NAS: Failed to allocate %d byte buffer for %s,\n" |
716 | 0 | "is this really a GMLFeatureClassList file?", |
717 | 0 | nLength, pszFile); |
718 | 0 | VSIFCloseL(fp); |
719 | 0 | return false; |
720 | 0 | } |
721 | | |
722 | 0 | if (VSIFReadL(pszWholeText, nLength, 1, fp) != 1) |
723 | 0 | { |
724 | 0 | VSIFree(pszWholeText); |
725 | 0 | VSIFCloseL(fp); |
726 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "NAS: Read failed on %s.", |
727 | 0 | pszFile); |
728 | 0 | return false; |
729 | 0 | } |
730 | 0 | pszWholeText[nLength] = '\0'; |
731 | |
|
732 | 0 | VSIFCloseL(fp); |
733 | |
|
734 | 0 | if (strstr(pszWholeText, "<GMLFeatureClassList") == nullptr) |
735 | 0 | { |
736 | 0 | VSIFree(pszWholeText); |
737 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
738 | 0 | "NAS: File %s does not contain a GMLFeatureClassList tree.", |
739 | 0 | pszFile); |
740 | 0 | return false; |
741 | 0 | } |
742 | | |
743 | | /* -------------------------------------------------------------------- */ |
744 | | /* Convert to XML parse tree. */ |
745 | | /* -------------------------------------------------------------------- */ |
746 | 0 | CPLXMLTreeCloser psRoot(CPLParseXMLString(pszWholeText)); |
747 | 0 | VSIFree(pszWholeText); |
748 | | |
749 | | // We assume parser will report errors via CPL. |
750 | 0 | if (psRoot.get() == nullptr) |
751 | 0 | return false; |
752 | | |
753 | 0 | if (psRoot->eType != CXT_Element || |
754 | 0 | !EQUAL(psRoot->pszValue, "GMLFeatureClassList")) |
755 | 0 | { |
756 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
757 | 0 | "NAS: File %s is not a GMLFeatureClassList document.", |
758 | 0 | pszFile); |
759 | 0 | return false; |
760 | 0 | } |
761 | | |
762 | | /* -------------------------------------------------------------------- */ |
763 | | /* Extract feature classes for all definitions found. */ |
764 | | /* -------------------------------------------------------------------- */ |
765 | 0 | for (CPLXMLNode *psThis = psRoot->psChild; psThis != nullptr; |
766 | 0 | psThis = psThis->psNext) |
767 | 0 | { |
768 | 0 | if (psThis->eType == CXT_Element && |
769 | 0 | EQUAL(psThis->pszValue, "GMLFeatureClass")) |
770 | 0 | { |
771 | 0 | GMLFeatureClass *poClass = new GMLFeatureClass(); |
772 | |
|
773 | 0 | if (!poClass->InitializeFromXML(psThis)) |
774 | 0 | { |
775 | 0 | delete poClass; |
776 | 0 | return false; |
777 | 0 | } |
778 | | |
779 | 0 | poClass->SetSchemaLocked(true); |
780 | |
|
781 | 0 | AddClass(poClass); |
782 | 0 | } |
783 | 0 | } |
784 | | |
785 | 0 | SetClassListLocked(true); |
786 | |
|
787 | 0 | return true; |
788 | 0 | } |
789 | | |
790 | | /************************************************************************/ |
791 | | /* SaveClasses() */ |
792 | | /************************************************************************/ |
793 | | |
794 | | bool NASReader::SaveClasses(const char *pszFile) |
795 | | |
796 | 0 | { |
797 | | // Add logic later to determine reasonable default schema file. |
798 | 0 | if (pszFile == nullptr) |
799 | 0 | return false; |
800 | | |
801 | | /* -------------------------------------------------------------------- */ |
802 | | /* Create in memory schema tree. */ |
803 | | /* -------------------------------------------------------------------- */ |
804 | 0 | CPLXMLNode *psRoot = |
805 | 0 | CPLCreateXMLNode(nullptr, CXT_Element, "GMLFeatureClassList"); |
806 | |
|
807 | 0 | for (int iClass = 0; iClass < GetClassCount(); iClass++) |
808 | 0 | { |
809 | 0 | GMLFeatureClass *poClass = GetClass(iClass); |
810 | |
|
811 | 0 | CPLAddXMLChild(psRoot, poClass->SerializeToXML()); |
812 | 0 | } |
813 | | |
814 | | /* -------------------------------------------------------------------- */ |
815 | | /* Serialize to disk. */ |
816 | | /* -------------------------------------------------------------------- */ |
817 | 0 | char *pszWholeText = CPLSerializeXMLTree(psRoot); |
818 | |
|
819 | 0 | CPLDestroyXMLNode(psRoot); |
820 | |
|
821 | 0 | VSILFILE *fp = VSIFOpenL(pszFile, "wb"); |
822 | |
|
823 | 0 | bool bSuccess = true; |
824 | 0 | if (fp == nullptr) |
825 | 0 | bSuccess = false; |
826 | 0 | else if (VSIFWriteL(pszWholeText, strlen(pszWholeText), 1, fp) != 1) |
827 | 0 | { |
828 | 0 | VSIFCloseL(fp); |
829 | 0 | bSuccess = false; |
830 | 0 | } |
831 | 0 | else |
832 | 0 | { |
833 | 0 | if (VSIFWriteL(pszWholeText, strlen(pszWholeText), 1, fp) != 1) |
834 | 0 | bSuccess = false; |
835 | 0 | VSIFCloseL(fp); |
836 | 0 | } |
837 | |
|
838 | 0 | CPLFree(pszWholeText); |
839 | |
|
840 | 0 | return bSuccess; |
841 | 0 | } |
842 | | |
843 | | /************************************************************************/ |
844 | | /* PrescanForSchema() */ |
845 | | /* */ |
846 | | /* For now we use a pretty dumb approach of just doing a normal */ |
847 | | /* scan of the whole file, building up the schema information. */ |
848 | | /* Eventually we hope to do a more efficient scan when just */ |
849 | | /* looking for schema information. */ |
850 | | /************************************************************************/ |
851 | | |
852 | | bool NASReader::PrescanForSchema(bool bGetExtents, bool /*bOnlyDetectSRS*/) |
853 | 0 | { |
854 | 0 | if (m_pszFilename == nullptr) |
855 | 0 | return false; |
856 | | |
857 | 0 | CPLDebug("NAS", "Prescanning %s.", m_pszFilename); |
858 | |
|
859 | 0 | SetClassListLocked(false); |
860 | |
|
861 | 0 | if (!SetupParser()) |
862 | 0 | return false; |
863 | | |
864 | 0 | std::string osWork; |
865 | |
|
866 | 0 | GMLFeature *poFeature = nullptr; |
867 | 0 | while ((poFeature = NextFeature()) != nullptr) |
868 | 0 | { |
869 | 0 | GMLFeatureClass *poClass = poFeature->GetClass(); |
870 | |
|
871 | 0 | if (poClass->GetFeatureCount() == -1) |
872 | 0 | poClass->SetFeatureCount(1); |
873 | 0 | else |
874 | 0 | poClass->SetFeatureCount(poClass->GetFeatureCount() + 1); |
875 | |
|
876 | 0 | if (bGetExtents) |
877 | 0 | { |
878 | 0 | OGRGeometry *poGeometry = nullptr; |
879 | |
|
880 | 0 | const CPLXMLNode *const *papsGeometry = |
881 | 0 | poFeature->GetGeometryList(); |
882 | 0 | if (papsGeometry[0] != nullptr) |
883 | 0 | { |
884 | 0 | poGeometry = |
885 | 0 | (OGRGeometry *)OGR_G_CreateFromGMLTree(papsGeometry[0]); |
886 | 0 | poGeometry = |
887 | 0 | ConvertGeometry(std::unique_ptr<OGRGeometry>(poGeometry)) |
888 | 0 | .release(); |
889 | 0 | } |
890 | |
|
891 | 0 | if (poGeometry != nullptr) |
892 | 0 | { |
893 | 0 | OGREnvelope sEnvelope; |
894 | |
|
895 | 0 | if (poClass->GetGeometryPropertyCount() == 0) |
896 | 0 | poClass->AddGeometryProperty(new GMLGeometryPropertyDefn( |
897 | 0 | "", "", wkbUnknown, -1, true)); |
898 | |
|
899 | 0 | OGRwkbGeometryType eGType = |
900 | 0 | (OGRwkbGeometryType)poClass->GetGeometryProperty(0) |
901 | 0 | ->GetType(); |
902 | | |
903 | | // Merge SRSName into layer. |
904 | 0 | const char *pszSRSName = |
905 | 0 | GML_ExtractSrsNameFromGeometry(papsGeometry, osWork, false); |
906 | | // if (pszSRSName != NULL) |
907 | | // m_bCanUseGlobalSRSName = FALSE; |
908 | 0 | poClass->MergeSRSName(pszSRSName); |
909 | | |
910 | | // Merge geometry type into layer. |
911 | 0 | if (poClass->GetFeatureCount() == 1 && eGType == wkbUnknown) |
912 | 0 | eGType = wkbNone; |
913 | |
|
914 | 0 | poClass->GetGeometryProperty(0)->SetType( |
915 | 0 | OGRMergeGeometryTypesEx( |
916 | 0 | eGType, poGeometry->getGeometryType(), TRUE)); |
917 | | |
918 | | // merge extents. |
919 | 0 | poGeometry->getEnvelope(&sEnvelope); |
920 | 0 | delete poGeometry; |
921 | 0 | double dfXMin = 0.0; |
922 | 0 | double dfXMax = 0.0; |
923 | 0 | double dfYMin = 0.0; |
924 | 0 | double dfYMax = 0.0; |
925 | 0 | if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax)) |
926 | 0 | { |
927 | 0 | dfXMin = std::min(dfXMin, sEnvelope.MinX); |
928 | 0 | dfXMax = std::max(dfXMax, sEnvelope.MaxX); |
929 | 0 | dfYMin = std::min(dfYMin, sEnvelope.MinY); |
930 | 0 | dfYMax = std::max(dfYMax, sEnvelope.MaxY); |
931 | 0 | } |
932 | 0 | else |
933 | 0 | { |
934 | 0 | dfXMin = sEnvelope.MinX; |
935 | 0 | dfXMax = sEnvelope.MaxX; |
936 | 0 | dfYMin = sEnvelope.MinY; |
937 | 0 | dfYMax = sEnvelope.MaxY; |
938 | 0 | } |
939 | |
|
940 | 0 | poClass->SetExtents(dfXMin, dfXMax, dfYMin, dfYMax); |
941 | 0 | } |
942 | 0 | else |
943 | 0 | { |
944 | 0 | if (poClass->GetGeometryPropertyCount() == 1 && |
945 | 0 | poClass->GetGeometryProperty(0)->GetType() == |
946 | 0 | (int)wkbUnknown && |
947 | 0 | poClass->GetFeatureCount() == 1) |
948 | 0 | { |
949 | 0 | poClass->ClearGeometryProperties(); |
950 | 0 | } |
951 | 0 | } |
952 | 0 | } |
953 | |
|
954 | 0 | delete poFeature; |
955 | 0 | } |
956 | |
|
957 | 0 | CleanupParser(); |
958 | | |
959 | | // Skip empty classes |
960 | 0 | int j = 0; |
961 | 0 | for (int i = 0, n = m_nClassCount; i < n; i++) |
962 | 0 | { |
963 | 0 | if (m_papoClass[i]->GetFeatureCount() > 0) |
964 | 0 | { |
965 | 0 | m_papoClass[j++] = m_papoClass[i]; |
966 | 0 | continue; |
967 | 0 | } |
968 | | |
969 | 0 | CPLDebug("NAS", "Skipping empty layer %s.", m_papoClass[i]->GetName()); |
970 | |
|
971 | 0 | delete m_papoClass[i]; |
972 | 0 | m_papoClass[i] = nullptr; |
973 | 0 | } |
974 | |
|
975 | 0 | m_nClassCount = j; |
976 | |
|
977 | 0 | CPLDebug("NAS", "%d remaining classes after prescan.", m_nClassCount); |
978 | |
|
979 | 0 | for (int i = 0; i < m_nClassCount; i++) |
980 | 0 | { |
981 | 0 | CPLDebug("NAS", "%s: " CPL_FRMT_GIB " features.", |
982 | 0 | m_papoClass[i]->GetName(), m_papoClass[i]->GetFeatureCount()); |
983 | 0 | } |
984 | |
|
985 | 0 | return GetClassCount() > 0; |
986 | 0 | } |
987 | | |
988 | | /************************************************************************/ |
989 | | /* ResetReading() */ |
990 | | /************************************************************************/ |
991 | | |
992 | | void NASReader::ResetReading() |
993 | | |
994 | 0 | { |
995 | 0 | CleanupParser(); |
996 | 0 | SetFilteredClassName(nullptr); |
997 | 0 | } |
998 | | |
999 | | /************************************************************************/ |
1000 | | /* GetAttributeElementIndex() */ |
1001 | | /************************************************************************/ |
1002 | | |
1003 | | int NASReader::GetAttributeElementIndex(const char *pszElement, int nLen, |
1004 | | const char *pszAttrKey) |
1005 | | |
1006 | 0 | { |
1007 | 0 | GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass(); |
1008 | | |
1009 | | // Otherwise build the path to this element into a single string |
1010 | | // and compare against known attributes. |
1011 | 0 | if (m_poState->m_nPathLength == 0) |
1012 | 0 | { |
1013 | 0 | if (pszAttrKey == nullptr) |
1014 | 0 | return poClass->GetPropertyIndexBySrcElement(pszElement, nLen); |
1015 | 0 | else |
1016 | 0 | { |
1017 | 0 | CPLString osElemPath; |
1018 | 0 | int nFullLen = nLen + 1 + static_cast<int>(strlen(pszAttrKey)); |
1019 | 0 | osElemPath.reserve(nFullLen); |
1020 | 0 | osElemPath.assign(pszElement, nLen); |
1021 | 0 | osElemPath.append(1, '@'); |
1022 | 0 | osElemPath.append(pszAttrKey); |
1023 | 0 | return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(), |
1024 | 0 | nFullLen); |
1025 | 0 | } |
1026 | 0 | } |
1027 | 0 | else |
1028 | 0 | { |
1029 | 0 | int nFullLen = nLen + static_cast<int>(m_poState->osPath.size()) + 1; |
1030 | 0 | if (pszAttrKey != nullptr) |
1031 | 0 | nFullLen += 1 + static_cast<int>(strlen(pszAttrKey)); |
1032 | |
|
1033 | 0 | CPLString osElemPath; |
1034 | 0 | osElemPath.reserve(nFullLen); |
1035 | 0 | osElemPath.assign(m_poState->osPath); |
1036 | 0 | osElemPath.append(1, '|'); |
1037 | 0 | osElemPath.append(pszElement, nLen); |
1038 | 0 | if (pszAttrKey != nullptr) |
1039 | 0 | { |
1040 | 0 | osElemPath.append(1, '@'); |
1041 | 0 | osElemPath.append(pszAttrKey); |
1042 | 0 | } |
1043 | 0 | return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(), |
1044 | 0 | nFullLen); |
1045 | 0 | } |
1046 | 0 | } |
1047 | | |
1048 | | /************************************************************************/ |
1049 | | /* DealWithAttributes() */ |
1050 | | /************************************************************************/ |
1051 | | |
1052 | | void NASReader::DealWithAttributes(const char *pszName, int nLenName, |
1053 | | const Attributes &attrs) |
1054 | | |
1055 | 0 | { |
1056 | 0 | GMLFeature *poFeature = GetState()->m_poFeature; |
1057 | 0 | CPLAssert(poFeature != nullptr); |
1058 | |
|
1059 | 0 | for (unsigned int idx = 0; idx < attrs.getLength(); ++idx) |
1060 | 0 | { |
1061 | 0 | CPLString osAttrKey = transcode(attrs.getQName(idx)); |
1062 | 0 | CPLString osAttrVal = transcode(attrs.getValue(idx)); |
1063 | |
|
1064 | 0 | int nAttrIndex = 0; |
1065 | 0 | const char *pszAttrKeyNoNS = strchr(osAttrKey, ':'); |
1066 | 0 | if (pszAttrKeyNoNS) |
1067 | 0 | pszAttrKeyNoNS++; |
1068 | |
|
1069 | 0 | if ((pszAttrKeyNoNS && |
1070 | 0 | (nAttrIndex = GetAttributeElementIndex(pszName, nLenName, |
1071 | 0 | pszAttrKeyNoNS)) != -1) || |
1072 | 0 | ((nAttrIndex = GetAttributeElementIndex(pszName, nLenName, |
1073 | 0 | osAttrKey)) != -1)) |
1074 | 0 | { |
1075 | 0 | const char *pszAttrVal = osAttrVal; |
1076 | 0 | if (osAttrKey == "xlink:href" || |
1077 | 0 | (pszAttrKeyNoNS && EQUAL(pszAttrKeyNoNS, "href"))) |
1078 | 0 | { |
1079 | 0 | if (STARTS_WITH_CI(pszAttrVal, "urn:adv:oid:")) |
1080 | 0 | pszAttrVal += 12; |
1081 | 0 | else if (STARTS_WITH_CI( |
1082 | 0 | pszAttrVal, |
1083 | 0 | "https://registry.gdi-de.org/codelist/")) |
1084 | 0 | pszAttrVal = strrchr(pszAttrVal, '/') + 1; |
1085 | 0 | } |
1086 | |
|
1087 | 0 | poFeature->SetPropertyDirectly(nAttrIndex, CPLStrdup(pszAttrVal)); |
1088 | 0 | pszAttrVal = nullptr; |
1089 | 0 | } |
1090 | 0 | } |
1091 | 0 | } |
1092 | | |
1093 | | /************************************************************************/ |
1094 | | /* HugeFileResolver() */ |
1095 | | /* Returns true for success */ |
1096 | | /************************************************************************/ |
1097 | | |
1098 | | bool NASReader::HugeFileResolver(const char * /*pszFile */, |
1099 | | bool /* bSqliteIsTempFile */, |
1100 | | int /* iSqliteCacheMB */) |
1101 | 0 | { |
1102 | 0 | CPLDebug("NAS", "HugeFileResolver() not currently implemented for NAS."); |
1103 | 0 | return false; |
1104 | 0 | } |
1105 | | |
1106 | | /************************************************************************/ |
1107 | | /* PrescanForTemplate() */ |
1108 | | /* Returns true for success */ |
1109 | | /************************************************************************/ |
1110 | | |
1111 | | bool NASReader::PrescanForTemplate(void) |
1112 | | |
1113 | 0 | { |
1114 | 0 | CPLDebug("NAS", "PrescanForTemplate() not currently implemented for NAS."); |
1115 | 0 | return false; |
1116 | 0 | } |
1117 | | |
1118 | | /************************************************************************/ |
1119 | | /* ResolveXlinks() */ |
1120 | | /* Returns true for success */ |
1121 | | /************************************************************************/ |
1122 | | |
1123 | | bool NASReader::ResolveXlinks(const char * /*pszFile */, |
1124 | | bool * /*pbOutIsTempFile */, |
1125 | | char ** /*papszSkip */, const bool /*bStrict */) |
1126 | 0 | { |
1127 | 0 | CPLDebug("NAS", "ResolveXlinks() not currently implemented for NAS."); |
1128 | 0 | return false; |
1129 | 0 | } |
1130 | | |
1131 | | /************************************************************************/ |
1132 | | /* SetFilteredClassName() */ |
1133 | | /************************************************************************/ |
1134 | | |
1135 | | bool NASReader::SetFilteredClassName(const char *pszClassName) |
1136 | 0 | { |
1137 | 0 | CPLFree(m_pszFilteredClassName); |
1138 | 0 | m_pszFilteredClassName = pszClassName ? CPLStrdup(pszClassName) : nullptr; |
1139 | 0 | return true; |
1140 | 0 | } |
1141 | | |
1142 | | /************************************************************************/ |
1143 | | /* ConvertGeometry() */ |
1144 | | /************************************************************************/ |
1145 | | |
1146 | | std::unique_ptr<OGRGeometry> |
1147 | | NASReader::ConvertGeometry(std::unique_ptr<OGRGeometry> poGeom) |
1148 | 0 | { |
1149 | 0 | if (poGeom == nullptr || |
1150 | 0 | wkbFlatten(poGeom->getGeometryType()) != wkbMultiLineString) |
1151 | 0 | { |
1152 | 0 | return poGeom; |
1153 | 0 | } |
1154 | | |
1155 | 0 | return OGRGeometryFactory::forceTo(std::move(poGeom), wkbLineString); |
1156 | 0 | } |