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