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