1 : /******************************************************************************
2 : * $Id: gmlreader.cpp 10645 2007-01-18 02:22:39Z warmerdam $
3 : *
4 : * Project: NAS Reader
5 : * Purpose: Implementation of NASReader class.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2002, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "gmlreader.h"
31 : #include "cpl_error.h"
32 : #include "cpl_string.h"
33 : #include "gmlutils.h"
34 :
35 : #define SUPPORT_GEOMETRY
36 :
37 : #ifdef SUPPORT_GEOMETRY
38 : # include "ogr_geometry.h"
39 : #endif
40 :
41 : /************************************************************************/
42 : /* ==================================================================== */
43 : /* With XERCES Library */
44 : /* ==================================================================== */
45 : /************************************************************************/
46 :
47 : #include "nasreaderp.h"
48 : #include "cpl_conv.h"
49 :
50 : /************************************************************************/
51 : /* CreateGMLReader() */
52 : /************************************************************************/
53 :
54 5 : IGMLReader *CreateNASReader()
55 :
56 : {
57 5 : return new NASReader();
58 : }
59 :
60 : /************************************************************************/
61 : /* GMLReader() */
62 : /************************************************************************/
63 :
64 5 : NASReader::NASReader()
65 :
66 : {
67 5 : m_nClassCount = 0;
68 5 : m_papoClass = NULL;
69 :
70 5 : m_bClassListLocked = FALSE;
71 :
72 5 : m_poNASHandler = NULL;
73 5 : m_poSAXReader = NULL;
74 5 : m_bReadStarted = FALSE;
75 :
76 5 : m_poState = NULL;
77 5 : m_poCompleteFeature = NULL;
78 :
79 5 : m_pszFilename = NULL;
80 5 : m_pszFilteredClassName = NULL;
81 5 : }
82 :
83 : /************************************************************************/
84 : /* ~NASReader() */
85 : /************************************************************************/
86 :
87 5 : NASReader::~NASReader()
88 :
89 : {
90 5 : ClearClasses();
91 :
92 5 : CPLFree( m_pszFilename );
93 :
94 5 : CleanupParser();
95 :
96 5 : if (CSLTestBoolean(CPLGetConfigOption("NAS_XERCES_TERMINATE", "FALSE")))
97 0 : XMLPlatformUtils::Terminate();
98 :
99 5 : CPLFree( m_pszFilteredClassName );
100 5 : }
101 :
102 : /************************************************************************/
103 : /* SetSourceFile() */
104 : /************************************************************************/
105 :
106 5 : void NASReader::SetSourceFile( const char *pszFilename )
107 :
108 : {
109 5 : CPLFree( m_pszFilename );
110 5 : m_pszFilename = CPLStrdup( pszFilename );
111 5 : }
112 :
113 : /************************************************************************/
114 : /* GetSourceFileName() */
115 : /************************************************************************/
116 :
117 0 : const char* NASReader::GetSourceFileName()
118 :
119 : {
120 0 : return m_pszFilename;
121 : }
122 :
123 : /************************************************************************/
124 : /* SetupParser() */
125 : /************************************************************************/
126 :
127 9 : int NASReader::SetupParser()
128 :
129 : {
130 : static int bXercesInitialized = FALSE;
131 :
132 9 : if( !bXercesInitialized )
133 : {
134 : try
135 : {
136 1 : XMLPlatformUtils::Initialize();
137 : }
138 :
139 0 : catch (const XMLException& toCatch)
140 : {
141 : CPLError( CE_Warning, CPLE_AppDefined,
142 : "Exception initializing Xerces based GML reader.\n%s",
143 0 : tr_strdup(toCatch.getMessage()) );
144 0 : return FALSE;
145 : }
146 1 : bXercesInitialized = TRUE;
147 : }
148 :
149 : // Cleanup any old parser.
150 9 : if( m_poSAXReader != NULL )
151 0 : CleanupParser();
152 :
153 : // Create and initialize parser.
154 9 : XMLCh* xmlUriValid = NULL;
155 9 : XMLCh* xmlUriNS = NULL;
156 :
157 : try{
158 9 : m_poSAXReader = XMLReaderFactory::createXMLReader();
159 :
160 9 : m_poNASHandler = new NASHandler( this );
161 :
162 18 : m_poSAXReader->setContentHandler( m_poNASHandler );
163 9 : m_poSAXReader->setErrorHandler( m_poNASHandler );
164 9 : m_poSAXReader->setLexicalHandler( m_poNASHandler );
165 9 : m_poSAXReader->setEntityResolver( m_poNASHandler );
166 9 : m_poSAXReader->setDTDHandler( m_poNASHandler );
167 :
168 9 : xmlUriValid = XMLString::transcode("http://xml.org/sax/features/validation");
169 9 : xmlUriNS = XMLString::transcode("http://xml.org/sax/features/namespaces");
170 :
171 : #if (OGR_GML_VALIDATION)
172 : m_poSAXReader->setFeature( xmlUriValid, true);
173 : m_poSAXReader->setFeature( xmlUriNS, true);
174 :
175 : m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, true );
176 : m_poSAXReader->setFeature( XMLUni::fgXercesSchema, true );
177 :
178 : // m_poSAXReader->setDoSchema(true);
179 : // m_poSAXReader->setValidationSchemaFullChecking(true);
180 : #else
181 9 : m_poSAXReader->setFeature( XMLUni::fgSAX2CoreValidation, false);
182 :
183 : #if XERCES_VERSION_MAJOR >= 3
184 9 : m_poSAXReader->setFeature( XMLUni::fgXercesSchema, false);
185 : #else
186 : m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, false);
187 : #endif
188 :
189 : #endif
190 9 : XMLString::release( &xmlUriValid );
191 9 : XMLString::release( &xmlUriNS );
192 : }
193 0 : catch (...)
194 : {
195 0 : XMLString::release( &xmlUriValid );
196 0 : XMLString::release( &xmlUriNS );
197 :
198 : CPLError( CE_Warning, CPLE_AppDefined,
199 0 : "Exception initializing Xerces based GML reader.\n" );
200 0 : return FALSE;
201 : }
202 :
203 9 : m_bReadStarted = FALSE;
204 :
205 : // Push an empty state.
206 18 : PushState( new GMLReadState() );
207 :
208 9 : return TRUE;
209 : }
210 :
211 : /************************************************************************/
212 : /* CleanupParser() */
213 : /************************************************************************/
214 :
215 17 : void NASReader::CleanupParser()
216 :
217 : {
218 17 : if( m_poSAXReader == NULL )
219 8 : return;
220 :
221 27 : while( m_poState )
222 9 : PopState();
223 :
224 9 : delete m_poSAXReader;
225 9 : m_poSAXReader = NULL;
226 :
227 9 : delete m_poNASHandler;
228 9 : m_poNASHandler = NULL;
229 :
230 9 : delete m_poCompleteFeature;
231 9 : m_poCompleteFeature = NULL;
232 :
233 9 : m_bReadStarted = FALSE;
234 : }
235 :
236 : /************************************************************************/
237 : /* NextFeature() */
238 : /************************************************************************/
239 :
240 8738 : GMLFeature *NASReader::NextFeature()
241 :
242 : {
243 8738 : GMLFeature *poReturn = NULL;
244 :
245 : try
246 : {
247 8738 : if( !m_bReadStarted )
248 : {
249 9 : if( m_poSAXReader == NULL )
250 6 : SetupParser();
251 :
252 9 : if( !m_poSAXReader->parseFirst( m_pszFilename, m_oToFill ) )
253 0 : return NULL;
254 9 : m_bReadStarted = TRUE;
255 : }
256 :
257 17661102 : while( m_poCompleteFeature == NULL
258 8826184 : && m_poSAXReader->parseNext( m_oToFill ) ) {}
259 :
260 8738 : poReturn = m_poCompleteFeature;
261 8738 : m_poCompleteFeature = NULL;
262 :
263 : }
264 0 : catch (const XMLException& toCatch)
265 : {
266 : CPLDebug( "GML",
267 : "Error during NextFeature()! Message:\n%s",
268 0 : tr_strdup( toCatch.getMessage() ) );
269 : }
270 :
271 8738 : return poReturn;
272 : }
273 :
274 : /************************************************************************/
275 : /* PushFeature() */
276 : /* */
277 : /* Create a feature based on the named element. If the */
278 : /* corresponding feature class doesn't exist yet, then create */
279 : /* it now. A new GMLReadState will be created for the feature, */
280 : /* and it will be placed within that state. The state is */
281 : /* pushed onto the readstate stack. */
282 : /************************************************************************/
283 :
284 8734 : void NASReader::PushFeature( const char *pszElement,
285 : const Attributes &attrs )
286 :
287 : {
288 : int iClass;
289 :
290 : /* -------------------------------------------------------------------- */
291 : /* Find the class of this element. */
292 : /* -------------------------------------------------------------------- */
293 150035 : for( iClass = 0; iClass < GetClassCount(); iClass++ )
294 : {
295 150032 : if( EQUAL(pszElement,GetClass(iClass)->GetElementName()) )
296 8731 : break;
297 : }
298 :
299 : /* -------------------------------------------------------------------- */
300 : /* Create a new feature class for this element, if there is no */
301 : /* existing class for it. */
302 : /* -------------------------------------------------------------------- */
303 8734 : if( iClass == GetClassCount() )
304 : {
305 3 : CPLAssert( !IsClassListLocked() );
306 :
307 3 : GMLFeatureClass *poNewClass = new GMLFeatureClass( pszElement );
308 :
309 3 : AddClass( poNewClass );
310 : }
311 :
312 : /* -------------------------------------------------------------------- */
313 : /* Create a feature of this feature class. */
314 : /* -------------------------------------------------------------------- */
315 8734 : GMLFeature *poFeature = new GMLFeature( GetClass( iClass ) );
316 :
317 : /* -------------------------------------------------------------------- */
318 : /* Create and push a new read state. */
319 : /* -------------------------------------------------------------------- */
320 : GMLReadState *poState;
321 :
322 17468 : poState = new GMLReadState();
323 8734 : poState->m_poFeature = poFeature;
324 8734 : PushState( poState );
325 :
326 : /* -------------------------------------------------------------------- */
327 : /* Check for gml:id, and if found push it as an attribute named */
328 : /* gml_id. */
329 : /* -------------------------------------------------------------------- */
330 : int nFIDIndex;
331 : XMLCh anFID[100];
332 :
333 8734 : tr_strcpy( anFID, "gml:id" );
334 8734 : nFIDIndex = attrs.getIndex( anFID );
335 8734 : if( nFIDIndex != -1 )
336 : {
337 8728 : char *pszFID = tr_strdup( attrs.getValue( nFIDIndex ) );
338 8728 : SetFeaturePropertyDirectly( "gml_id", pszFID );
339 : }
340 :
341 8734 : }
342 :
343 : /************************************************************************/
344 : /* IsFeatureElement() */
345 : /* */
346 : /* Based on context and the element name, is this element a new */
347 : /* GML feature element? */
348 : /************************************************************************/
349 :
350 223627 : int NASReader::IsFeatureElement( const char *pszElement )
351 :
352 : {
353 223627 : CPLAssert( m_poState != NULL );
354 :
355 223627 : const char *pszLast = m_poState->GetLastComponent();
356 223627 : int nLen = strlen(pszLast);
357 :
358 : // There seem to be two major NAS classes of feature identifiers
359 : // -- either a wfs:Insert or a gml:featureMember.
360 :
361 223627 : if( (nLen < 6 || !EQUAL(pszLast+nLen-6,"Insert"))
362 : && (nLen < 13 || !EQUAL(pszLast+nLen-13,"featureMember"))
363 : && (nLen < 6 || !EQUAL(pszLast+nLen-7,"Replace")) )
364 153981 : return FALSE;
365 :
366 : // If the class list isn't locked, any element that is a featureMember
367 : // will do.
368 69646 : if( !IsClassListLocked() )
369 3 : return TRUE;
370 :
371 : // otherwise, find a class with the desired element name.
372 1090803 : for( int i = 0; i < GetClassCount(); i++ )
373 : {
374 1090803 : if( EQUAL(pszElement,GetClass(i)->GetElementName()) )
375 69643 : return TRUE;
376 : }
377 :
378 0 : return FALSE;
379 : }
380 :
381 : /************************************************************************/
382 : /* IsAttributeElement() */
383 : /************************************************************************/
384 :
385 153977 : int NASReader::IsAttributeElement( const char *pszElement )
386 :
387 : {
388 153977 : if( m_poState->m_poFeature == NULL )
389 69852 : return FALSE;
390 :
391 84125 : GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
392 :
393 : // If the schema is not yet locked, then any simple element
394 : // is potentially an attribute.
395 84125 : if( !poClass->IsSchemaLocked() )
396 112 : return TRUE;
397 :
398 : // Otherwise build the path to this element into a single string
399 : // and compare against known attributes.
400 84013 : CPLString osElemPath;
401 :
402 84013 : if( m_poState->m_nPathLength == 0 )
403 42220 : osElemPath = pszElement;
404 : else
405 : {
406 41793 : osElemPath = m_poState->osPath;
407 41793 : osElemPath += "|";
408 41793 : osElemPath += pszElement;
409 : }
410 :
411 603772 : for( int i = 0; i < poClass->GetPropertyCount(); i++ )
412 558226 : if( EQUAL(poClass->GetProperty(i)->GetSrcElement(),osElemPath) )
413 38467 : return TRUE;
414 :
415 45546 : return FALSE;
416 : }
417 :
418 : /************************************************************************/
419 : /* PopState() */
420 : /************************************************************************/
421 :
422 8743 : void NASReader::PopState()
423 :
424 : {
425 8743 : if( m_poState != NULL )
426 : {
427 8743 : if( m_poState->m_poFeature != NULL && m_poCompleteFeature == NULL )
428 : {
429 8734 : m_poCompleteFeature = m_poState->m_poFeature;
430 8734 : m_poState->m_poFeature = NULL;
431 : }
432 :
433 : GMLReadState *poParent;
434 :
435 8743 : poParent = m_poState->m_poParentState;
436 :
437 8743 : delete m_poState;
438 8743 : m_poState = poParent;
439 : }
440 8743 : }
441 :
442 : /************************************************************************/
443 : /* PushState() */
444 : /************************************************************************/
445 :
446 8743 : void NASReader::PushState( GMLReadState *poState )
447 :
448 : {
449 8743 : poState->m_poParentState = m_poState;
450 8743 : m_poState = poState;
451 8743 : }
452 :
453 : /************************************************************************/
454 : /* GetClass() */
455 : /************************************************************************/
456 :
457 1258487 : GMLFeatureClass *NASReader::GetClass( int iClass ) const
458 :
459 : {
460 1258487 : if( iClass < 0 || iClass >= m_nClassCount )
461 0 : return NULL;
462 : else
463 1258487 : return m_papoClass[iClass];
464 : }
465 :
466 : /************************************************************************/
467 : /* GetClass() */
468 : /************************************************************************/
469 :
470 254 : GMLFeatureClass *NASReader::GetClass( const char *pszName ) const
471 :
472 : {
473 8788 : for( int iClass = 0; iClass < m_nClassCount; iClass++ )
474 : {
475 8661 : if( EQUAL(GetClass(iClass)->GetName(),pszName) )
476 127 : return GetClass(iClass);
477 : }
478 :
479 127 : return NULL;
480 : }
481 :
482 : /************************************************************************/
483 : /* AddClass() */
484 : /************************************************************************/
485 :
486 127 : int NASReader::AddClass( GMLFeatureClass *poNewClass )
487 :
488 : {
489 127 : CPLAssert( GetClass( poNewClass->GetName() ) == NULL );
490 :
491 127 : m_nClassCount++;
492 : m_papoClass = (GMLFeatureClass **)
493 127 : CPLRealloc( m_papoClass, sizeof(void*) * m_nClassCount );
494 127 : m_papoClass[m_nClassCount-1] = poNewClass;
495 :
496 127 : return m_nClassCount-1;
497 : }
498 :
499 : /************************************************************************/
500 : /* ClearClasses() */
501 : /************************************************************************/
502 :
503 8 : void NASReader::ClearClasses()
504 :
505 : {
506 135 : for( int i = 0; i < m_nClassCount; i++ )
507 127 : delete m_papoClass[i];
508 8 : CPLFree( m_papoClass );
509 :
510 8 : m_nClassCount = 0;
511 8 : m_papoClass = NULL;
512 8 : }
513 :
514 : /************************************************************************/
515 : /* SetFeatureProperty() */
516 : /* */
517 : /* Set the property value on the current feature, adding the */
518 : /* property name to the GMLFeatureClass if required. */
519 : /* The pszValue ownership is passed to this function. */
520 : /************************************************************************/
521 :
522 47263 : void NASReader::SetFeaturePropertyDirectly( const char *pszElement,
523 : char *pszValue )
524 :
525 : {
526 47263 : GMLFeature *poFeature = GetState()->m_poFeature;
527 :
528 47263 : CPLAssert( poFeature != NULL );
529 :
530 : /* -------------------------------------------------------------------- */
531 : /* Does this property exist in the feature class? If not, add */
532 : /* it. */
533 : /* -------------------------------------------------------------------- */
534 47263 : GMLFeatureClass *poClass = poFeature->GetClass();
535 : int iProperty;
536 :
537 201822 : for( iProperty=0; iProperty < poClass->GetPropertyCount(); iProperty++ )
538 : {
539 201793 : if( EQUAL(poClass->GetProperty( iProperty )->GetSrcElement(),
540 : pszElement ) )
541 47234 : break;
542 : }
543 :
544 47263 : if( iProperty == poClass->GetPropertyCount() )
545 : {
546 29 : if( poClass->IsSchemaLocked() )
547 : {
548 0 : CPLDebug("GML","Encountered property missing from class schema.");
549 0 : CPLFree(pszValue);
550 0 : return;
551 : }
552 :
553 29 : CPLString osFieldName;
554 :
555 29 : if( strchr(pszElement,'|') == NULL )
556 15 : osFieldName = pszElement;
557 : else
558 : {
559 14 : osFieldName = strrchr(pszElement,'|') + 1;
560 14 : if( poClass->GetPropertyIndex(osFieldName) != -1 )
561 2 : osFieldName = pszElement;
562 : }
563 :
564 : // Does this conflict with an existing property name?
565 58 : while( poClass->GetProperty(osFieldName) != NULL )
566 : {
567 0 : osFieldName += "_";
568 : }
569 :
570 29 : GMLPropertyDefn *poPDefn = new GMLPropertyDefn(osFieldName,pszElement);
571 :
572 58 : if( EQUAL(CPLGetConfigOption( "GML_FIELDTYPES", ""), "ALWAYS_STRING") )
573 0 : poPDefn->SetType( GMLPT_String );
574 :
575 29 : poClass->AddProperty( poPDefn );
576 : }
577 :
578 : /* -------------------------------------------------------------------- */
579 : /* We want to handle <lage> specially to ensure it is zero */
580 : /* filled, and treated as a string depspite the numeric */
581 : /* content. https://trac.wheregroup.com/PostNAS/ticket/9 */
582 : /* -------------------------------------------------------------------- */
583 47263 : if( strcmp(poClass->GetProperty(iProperty)->GetName(),"lage") == 0 )
584 : {
585 315 : if( strlen(pszValue) < 5 )
586 : {
587 0 : CPLString osValue = "00000";
588 0 : osValue += pszValue;
589 0 : poFeature->SetPropertyDirectly( iProperty, CPLStrdup(osValue + osValue.size() - 5) );
590 0 : CPLFree(pszValue);
591 : }
592 : else
593 315 : poFeature->SetPropertyDirectly( iProperty, pszValue );
594 :
595 :
596 315 : if( !poClass->IsSchemaLocked() )
597 : {
598 0 : poClass->GetProperty(iProperty)->SetWidth( 5 );
599 0 : poClass->GetProperty(iProperty)->SetType( GMLPT_String );
600 : }
601 315 : return;
602 : }
603 :
604 : /* -------------------------------------------------------------------- */
605 : /* Set the property */
606 : /* -------------------------------------------------------------------- */
607 46948 : poFeature->SetPropertyDirectly( iProperty, pszValue );
608 :
609 : /* -------------------------------------------------------------------- */
610 : /* Do we need to update the property type? */
611 : /* -------------------------------------------------------------------- */
612 46948 : if( !poClass->IsSchemaLocked() )
613 : {
614 : // Special handling for punktkennung per NAS #12
615 70 : if( strcmp(poClass->GetProperty(iProperty)->GetName(),
616 : "punktkennung") == 0)
617 : {
618 0 : poClass->GetProperty(iProperty)->SetWidth( 15 );
619 0 : poClass->GetProperty(iProperty)->SetType( GMLPT_String );
620 : }
621 : // Special handling for artDerFlurstuecksgrenze per http://trac.osgeo.org/gdal/ticket/4255
622 70 : else if( strcmp(poClass->GetProperty(iProperty)->GetName(),
623 : "artDerFlurstuecksgrenze") == 0)
624 : {
625 0 : poClass->GetProperty(iProperty)->SetType( GMLPT_IntegerList );
626 : }
627 : else
628 : poClass->GetProperty(iProperty)->AnalysePropertyValue(
629 70 : poFeature->GetProperty(iProperty));
630 : }
631 : }
632 :
633 : /************************************************************************/
634 : /* LoadClasses() */
635 : /************************************************************************/
636 :
637 2 : int NASReader::LoadClasses( const char *pszFile )
638 :
639 : {
640 : // Add logic later to determine reasonable default schema file.
641 2 : if( pszFile == NULL )
642 0 : return FALSE;
643 :
644 : /* -------------------------------------------------------------------- */
645 : /* Load the raw XML file. */
646 : /* -------------------------------------------------------------------- */
647 : FILE *fp;
648 : int nLength;
649 : char *pszWholeText;
650 :
651 2 : fp = VSIFOpen( pszFile, "rb" );
652 :
653 2 : if( fp == NULL )
654 : {
655 : CPLError( CE_Failure, CPLE_OpenFailed,
656 0 : "Failed to open file %s.", pszFile );
657 0 : return FALSE;
658 : }
659 :
660 2 : VSIFSeek( fp, 0, SEEK_END );
661 2 : nLength = VSIFTell( fp );
662 2 : VSIFSeek( fp, 0, SEEK_SET );
663 :
664 2 : pszWholeText = (char *) VSIMalloc(nLength+1);
665 2 : if( pszWholeText == NULL )
666 : {
667 : CPLError( CE_Failure, CPLE_AppDefined,
668 : "Failed to allocate %d byte buffer for %s,\n"
669 : "is this really a GMLFeatureClassList file?",
670 0 : nLength, pszFile );
671 0 : VSIFClose( fp );
672 0 : return FALSE;
673 : }
674 :
675 2 : if( VSIFRead( pszWholeText, nLength, 1, fp ) != 1 )
676 : {
677 0 : VSIFree( pszWholeText );
678 0 : VSIFClose( fp );
679 : CPLError( CE_Failure, CPLE_AppDefined,
680 0 : "Read failed on %s.", pszFile );
681 0 : return FALSE;
682 : }
683 2 : pszWholeText[nLength] = '\0';
684 :
685 2 : VSIFClose( fp );
686 :
687 2 : if( strstr( pszWholeText, "<GMLFeatureClassList>" ) == NULL )
688 : {
689 0 : VSIFree( pszWholeText );
690 : CPLError( CE_Failure, CPLE_AppDefined,
691 : "File %s does not contain a GMLFeatureClassList tree.",
692 0 : pszFile );
693 0 : return FALSE;
694 : }
695 :
696 : /* -------------------------------------------------------------------- */
697 : /* Convert to XML parse tree. */
698 : /* -------------------------------------------------------------------- */
699 : CPLXMLNode *psRoot;
700 :
701 2 : psRoot = CPLParseXMLString( pszWholeText );
702 2 : VSIFree( pszWholeText );
703 :
704 : // We assume parser will report errors via CPL.
705 2 : if( psRoot == NULL )
706 0 : return FALSE;
707 :
708 2 : if( psRoot->eType != CXT_Element
709 : || !EQUAL(psRoot->pszValue,"GMLFeatureClassList") )
710 : {
711 0 : CPLDestroyXMLNode(psRoot);
712 : CPLError( CE_Failure, CPLE_AppDefined,
713 : "File %s is not a GMLFeatureClassList document.",
714 0 : pszFile );
715 0 : return FALSE;
716 : }
717 :
718 : /* -------------------------------------------------------------------- */
719 : /* Extract feature classes for all definitions found. */
720 : /* -------------------------------------------------------------------- */
721 : CPLXMLNode *psThis;
722 :
723 126 : for( psThis = psRoot->psChild; psThis != NULL; psThis = psThis->psNext )
724 : {
725 124 : if( psThis->eType == CXT_Element
726 : && EQUAL(psThis->pszValue,"GMLFeatureClass") )
727 : {
728 : GMLFeatureClass *poClass;
729 :
730 124 : poClass = new GMLFeatureClass();
731 :
732 124 : if( !poClass->InitializeFromXML( psThis ) )
733 : {
734 0 : delete poClass;
735 0 : CPLDestroyXMLNode( psRoot );
736 0 : return FALSE;
737 : }
738 :
739 124 : poClass->SetSchemaLocked( TRUE );
740 :
741 124 : AddClass( poClass );
742 : }
743 : }
744 :
745 2 : CPLDestroyXMLNode( psRoot );
746 :
747 2 : SetClassListLocked( TRUE );
748 :
749 2 : return TRUE;
750 : }
751 :
752 : /************************************************************************/
753 : /* SaveClasses() */
754 : /************************************************************************/
755 :
756 2 : int NASReader::SaveClasses( const char *pszFile )
757 :
758 : {
759 : // Add logic later to determine reasonable default schema file.
760 2 : if( pszFile == NULL )
761 0 : return FALSE;
762 :
763 : /* -------------------------------------------------------------------- */
764 : /* Create in memory schema tree. */
765 : /* -------------------------------------------------------------------- */
766 : CPLXMLNode *psRoot;
767 :
768 2 : psRoot = CPLCreateXMLNode( NULL, CXT_Element, "GMLFeatureClassList" );
769 :
770 5 : for( int iClass = 0; iClass < GetClassCount(); iClass++ )
771 : {
772 3 : GMLFeatureClass *poClass = GetClass( iClass );
773 :
774 3 : CPLAddXMLChild( psRoot, poClass->SerializeToXML() );
775 : }
776 :
777 : /* -------------------------------------------------------------------- */
778 : /* Serialize to disk. */
779 : /* -------------------------------------------------------------------- */
780 : FILE *fp;
781 2 : int bSuccess = TRUE;
782 2 : char *pszWholeText = CPLSerializeXMLTree( psRoot );
783 :
784 2 : CPLDestroyXMLNode( psRoot );
785 :
786 2 : fp = VSIFOpen( pszFile, "wb" );
787 :
788 2 : if( fp == NULL )
789 0 : bSuccess = FALSE;
790 2 : else if( VSIFWrite( pszWholeText, strlen(pszWholeText), 1, fp ) != 1 )
791 0 : bSuccess = FALSE;
792 : else
793 2 : VSIFClose( fp );
794 :
795 2 : CPLFree( pszWholeText );
796 :
797 2 : return bSuccess;
798 : }
799 :
800 : /************************************************************************/
801 : /* PrescanForSchema() */
802 : /* */
803 : /* For now we use a pretty dumb approach of just doing a normal */
804 : /* scan of the whole file, building up the schema information. */
805 : /* Eventually we hope to do a more efficient scan when just */
806 : /* looking for schema information. */
807 : /************************************************************************/
808 :
809 3 : int NASReader::PrescanForSchema( int bGetExtents )
810 :
811 : {
812 : GMLFeature *poFeature;
813 :
814 3 : if( m_pszFilename == NULL )
815 0 : return FALSE;
816 :
817 3 : SetClassListLocked( FALSE );
818 :
819 3 : ClearClasses();
820 3 : if( !SetupParser() )
821 0 : return FALSE;
822 :
823 3 : std::string osWork;
824 :
825 11 : while( (poFeature = NextFeature()) != NULL )
826 : {
827 5 : GMLFeatureClass *poClass = poFeature->GetClass();
828 :
829 5 : if( poClass->GetFeatureCount() == -1 )
830 3 : poClass->SetFeatureCount( 1 );
831 : else
832 2 : poClass->SetFeatureCount( poClass->GetFeatureCount() + 1 );
833 :
834 : #ifdef SUPPORT_GEOMETRY
835 5 : if( bGetExtents )
836 : {
837 5 : OGRGeometry *poGeometry = NULL;
838 :
839 5 : const CPLXMLNode* const * papsGeometry = poFeature->GetGeometryList();
840 5 : if( papsGeometry[0] != NULL )
841 : {
842 1 : poGeometry = (OGRGeometry*) OGR_G_CreateFromGMLTree(papsGeometry[0]);
843 : }
844 :
845 5 : if( poGeometry != NULL )
846 : {
847 : double dfXMin, dfXMax, dfYMin, dfYMax;
848 1 : OGREnvelope sEnvelope;
849 : OGRwkbGeometryType eGType = (OGRwkbGeometryType)
850 1 : poClass->GetGeometryType();
851 :
852 : // Merge SRSName into layer.
853 1 : const char* pszSRSName = GML_ExtractSrsNameFromGeometry(papsGeometry, osWork, FALSE);
854 : // if (pszSRSName != NULL)
855 : // m_bCanUseGlobalSRSName = FALSE;
856 1 : poClass->MergeSRSName(pszSRSName);
857 :
858 : // Merge geometry type into layer.
859 1 : if( poClass->GetFeatureCount() == 1 && eGType == wkbUnknown )
860 1 : eGType = wkbNone;
861 :
862 : poClass->SetGeometryType(
863 : (int) OGRMergeGeometryTypes(
864 1 : eGType, poGeometry->getGeometryType() ) );
865 :
866 : // merge extents.
867 1 : poGeometry->getEnvelope( &sEnvelope );
868 1 : delete poGeometry;
869 1 : if( poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax) )
870 : {
871 0 : dfXMin = MIN(dfXMin,sEnvelope.MinX);
872 0 : dfXMax = MAX(dfXMax,sEnvelope.MaxX);
873 0 : dfYMin = MIN(dfYMin,sEnvelope.MinY);
874 0 : dfYMax = MAX(dfYMax,sEnvelope.MaxY);
875 : }
876 : else
877 : {
878 1 : dfXMin = sEnvelope.MinX;
879 1 : dfXMax = sEnvelope.MaxX;
880 1 : dfYMin = sEnvelope.MinY;
881 1 : dfYMax = sEnvelope.MaxY;
882 : }
883 :
884 1 : poClass->SetExtents( dfXMin, dfXMax, dfYMin, dfYMax );
885 : }
886 : else
887 : {
888 4 : if( poClass->GetGeometryType() == (int) wkbUnknown
889 : && poClass->GetFeatureCount() == 1 )
890 2 : poClass->SetGeometryType( wkbNone );
891 : }
892 : #endif /* def SUPPORT_GEOMETRY */
893 : }
894 :
895 5 : delete poFeature;
896 : }
897 :
898 3 : CleanupParser();
899 :
900 3 : return GetClassCount() > 0;
901 : }
902 :
903 : /************************************************************************/
904 : /* ResetReading() */
905 : /************************************************************************/
906 :
907 9 : void NASReader::ResetReading()
908 :
909 : {
910 9 : CleanupParser();
911 9 : SetFilteredClassName(NULL);
912 9 : }
913 :
914 : /************************************************************************/
915 : /* CheckForFID() */
916 : /* */
917 : /* Merge the fid attribute into the current field text. */
918 : /************************************************************************/
919 :
920 6 : void NASReader::CheckForFID( const Attributes &attrs,
921 : char **ppszCurField )
922 :
923 : {
924 : int nIndex;
925 : XMLCh Name[100];
926 :
927 6 : tr_strcpy( Name, "fid" );
928 6 : nIndex = attrs.getIndex( Name );
929 :
930 6 : if( nIndex != -1 )
931 : {
932 6 : char *pszFID = tr_strdup( attrs.getValue( nIndex ) );
933 6 : CPLString osCurField = *ppszCurField;
934 :
935 6 : osCurField += pszFID;
936 6 : CPLFree( pszFID );
937 :
938 6 : CPLFree( *ppszCurField );
939 6 : *ppszCurField = CPLStrdup(osCurField);
940 : }
941 6 : }
942 :
943 : /************************************************************************/
944 : /* CheckForRelations() */
945 : /************************************************************************/
946 :
947 38579 : void NASReader::CheckForRelations( const char *pszElement,
948 : const Attributes &attrs )
949 :
950 : {
951 38579 : GMLFeature *poFeature = GetState()->m_poFeature;
952 :
953 38579 : CPLAssert( poFeature != NULL );
954 :
955 : int nIndex;
956 : XMLCh Name[100];
957 :
958 38579 : tr_strcpy( Name, "xlink:href" );
959 38579 : nIndex = attrs.getIndex( Name );
960 :
961 38579 : if( nIndex != -1 )
962 : {
963 2508 : char *pszHRef = tr_strdup( attrs.getValue( nIndex ) );
964 :
965 2508 : if( EQUALN(pszHRef,"urn:adv:oid:", 12 ) )
966 2508 : poFeature->AddOBProperty( pszElement, pszHRef );
967 :
968 2508 : CPLFree( pszHRef );
969 : }
970 38579 : }
971 :
972 : /************************************************************************/
973 : /* HugeFileResolver() */
974 : /* Returns TRUE for success */
975 : /************************************************************************/
976 :
977 0 : int NASReader::HugeFileResolver( const char *pszFile,
978 : int bSqliteIsTempFile,
979 : int iSqliteCacheMB )
980 :
981 : {
982 0 : CPLDebug( "NAS", "HugeFileResolver() not currently implemented for NAS." );
983 0 : return FALSE;
984 : }
985 :
986 : /************************************************************************/
987 : /* PrescanForTemplate() */
988 : /* Returns TRUE for success */
989 : /************************************************************************/
990 :
991 0 : int NASReader::PrescanForTemplate( void )
992 :
993 : {
994 0 : CPLDebug( "NAS", "PrescanForTemplate() not currently implemented for NAS." );
995 0 : return FALSE;
996 : }
997 :
998 : /************************************************************************/
999 : /* ResolveXlinks() */
1000 : /* Returns TRUE for success */
1001 : /************************************************************************/
1002 :
1003 0 : int NASReader::ResolveXlinks( const char *pszFile,
1004 : int* pbOutIsTempFile,
1005 : char **papszSkip,
1006 : const int bStrict )
1007 :
1008 : {
1009 0 : CPLDebug( "NAS", "ResolveXlinks() not currently implemented for NAS." );
1010 0 : return FALSE;
1011 : }
1012 :
1013 : /************************************************************************/
1014 : /* SetFilteredClassName() */
1015 : /************************************************************************/
1016 :
1017 17 : int NASReader::SetFilteredClassName(const char* pszClassName)
1018 : {
1019 17 : CPLFree(m_pszFilteredClassName);
1020 17 : m_pszFilteredClassName = (pszClassName) ? CPLStrdup(pszClassName) : NULL;
1021 17 : return TRUE;
1022 : }
|