1 : /******************************************************************************
2 : * $Id: nasreader.cpp 24105 2012-03-10 12:08:04Z rouault $
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 10 : IGMLReader *CreateNASReader()
55 :
56 : {
57 10 : return new NASReader();
58 : }
59 :
60 : /************************************************************************/
61 : /* GMLReader() */
62 : /************************************************************************/
63 :
64 10 : NASReader::NASReader()
65 :
66 : {
67 10 : m_nClassCount = 0;
68 10 : m_papoClass = NULL;
69 :
70 10 : m_bClassListLocked = FALSE;
71 :
72 10 : m_poNASHandler = NULL;
73 10 : m_poSAXReader = NULL;
74 10 : m_bReadStarted = FALSE;
75 :
76 10 : m_poState = NULL;
77 10 : m_poCompleteFeature = NULL;
78 :
79 10 : m_pszFilename = NULL;
80 10 : m_pszFilteredClassName = NULL;
81 10 : }
82 :
83 : /************************************************************************/
84 : /* ~NASReader() */
85 : /************************************************************************/
86 :
87 10 : NASReader::~NASReader()
88 :
89 : {
90 10 : ClearClasses();
91 :
92 10 : CPLFree( m_pszFilename );
93 :
94 10 : CleanupParser();
95 :
96 10 : if (CSLTestBoolean(CPLGetConfigOption("NAS_XERCES_TERMINATE", "FALSE")))
97 0 : XMLPlatformUtils::Terminate();
98 :
99 10 : CPLFree( m_pszFilteredClassName );
100 10 : }
101 :
102 : /************************************************************************/
103 : /* SetSourceFile() */
104 : /************************************************************************/
105 :
106 10 : void NASReader::SetSourceFile( const char *pszFilename )
107 :
108 : {
109 10 : CPLFree( m_pszFilename );
110 10 : m_pszFilename = CPLStrdup( pszFilename );
111 10 : }
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 18 : int NASReader::SetupParser()
128 :
129 : {
130 : static int bXercesInitialized = FALSE;
131 :
132 18 : if( !bXercesInitialized )
133 : {
134 : try
135 : {
136 2 : 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 2 : bXercesInitialized = TRUE;
147 : }
148 :
149 : // Cleanup any old parser.
150 18 : if( m_poSAXReader != NULL )
151 0 : CleanupParser();
152 :
153 : // Create and initialize parser.
154 18 : XMLCh* xmlUriValid = NULL;
155 18 : XMLCh* xmlUriNS = NULL;
156 :
157 : try{
158 18 : m_poSAXReader = XMLReaderFactory::createXMLReader();
159 :
160 18 : m_poNASHandler = new NASHandler( this );
161 :
162 36 : m_poSAXReader->setContentHandler( m_poNASHandler );
163 18 : m_poSAXReader->setErrorHandler( m_poNASHandler );
164 18 : m_poSAXReader->setLexicalHandler( m_poNASHandler );
165 18 : m_poSAXReader->setEntityResolver( m_poNASHandler );
166 18 : m_poSAXReader->setDTDHandler( m_poNASHandler );
167 :
168 18 : xmlUriValid = XMLString::transcode("http://xml.org/sax/features/validation");
169 18 : 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 18 : m_poSAXReader->setFeature( XMLUni::fgSAX2CoreValidation, false);
182 :
183 : #if XERCES_VERSION_MAJOR >= 3
184 : m_poSAXReader->setFeature( XMLUni::fgXercesSchema, false);
185 : #else
186 18 : m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, false);
187 : #endif
188 :
189 : #endif
190 18 : XMLString::release( &xmlUriValid );
191 18 : 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 18 : m_bReadStarted = FALSE;
204 :
205 : // Push an empty state.
206 36 : PushState( new GMLReadState() );
207 :
208 18 : return TRUE;
209 : }
210 :
211 : /************************************************************************/
212 : /* CleanupParser() */
213 : /************************************************************************/
214 :
215 34 : void NASReader::CleanupParser()
216 :
217 : {
218 34 : if( m_poSAXReader == NULL )
219 16 : return;
220 :
221 54 : while( m_poState )
222 18 : PopState();
223 :
224 18 : delete m_poSAXReader;
225 18 : m_poSAXReader = NULL;
226 :
227 18 : delete m_poNASHandler;
228 18 : m_poNASHandler = NULL;
229 :
230 18 : delete m_poCompleteFeature;
231 18 : m_poCompleteFeature = NULL;
232 :
233 18 : m_bReadStarted = FALSE;
234 : }
235 :
236 : /************************************************************************/
237 : /* NextFeature() */
238 : /************************************************************************/
239 :
240 17476 : GMLFeature *NASReader::NextFeature()
241 :
242 : {
243 17476 : GMLFeature *poReturn = NULL;
244 :
245 : try
246 : {
247 17476 : if( !m_bReadStarted )
248 : {
249 18 : if( m_poSAXReader == NULL )
250 12 : SetupParser();
251 :
252 18 : if( !m_poSAXReader->parseFirst( m_pszFilename, m_oToFill ) )
253 0 : return NULL;
254 18 : m_bReadStarted = TRUE;
255 : }
256 :
257 35322204 : while( m_poCompleteFeature == NULL
258 17652368 : && m_poSAXReader->parseNext( m_oToFill ) ) {}
259 :
260 17476 : poReturn = m_poCompleteFeature;
261 17476 : m_poCompleteFeature = NULL;
262 :
263 : }
264 0 : catch (const XMLException& toCatch)
265 : {
266 : CPLDebug( "NAS",
267 : "Error during NextFeature()! Message:\n%s",
268 0 : tr_strdup( toCatch.getMessage() ) );
269 : }
270 :
271 17476 : 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 17468 : 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 300070 : for( iClass = 0; iClass < GetClassCount(); iClass++ )
294 : {
295 300064 : if( EQUAL(pszElement,GetClass(iClass)->GetElementName()) )
296 17462 : break;
297 : }
298 :
299 : /* -------------------------------------------------------------------- */
300 : /* Create a new feature class for this element, if there is no */
301 : /* existing class for it. */
302 : /* -------------------------------------------------------------------- */
303 17468 : if( iClass == GetClassCount() )
304 : {
305 6 : CPLAssert( !IsClassListLocked() );
306 :
307 6 : GMLFeatureClass *poNewClass = new GMLFeatureClass( pszElement );
308 :
309 6 : iClass = AddClass( poNewClass );
310 : }
311 :
312 : /* -------------------------------------------------------------------- */
313 : /* Create a feature of this feature class. */
314 : /* -------------------------------------------------------------------- */
315 17468 : GMLFeature *poFeature = new GMLFeature( GetClass( iClass ) );
316 :
317 : /* -------------------------------------------------------------------- */
318 : /* Create and push a new read state. */
319 : /* -------------------------------------------------------------------- */
320 : GMLReadState *poState;
321 :
322 34936 : poState = new GMLReadState();
323 17468 : poState->m_poFeature = poFeature;
324 17468 : 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 17468 : tr_strcpy( anFID, "gml:id" );
334 17468 : nFIDIndex = attrs.getIndex( anFID );
335 17468 : if( nFIDIndex != -1 )
336 : {
337 17456 : char *pszFID = tr_strdup( attrs.getValue( nFIDIndex ) );
338 17456 : SetFeaturePropertyDirectly( "gml_id", pszFID );
339 : }
340 :
341 17468 : }
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 447254 : int NASReader::IsFeatureElement( const char *pszElement )
351 :
352 : {
353 447254 : CPLAssert( m_poState != NULL );
354 :
355 447254 : const char *pszLast = m_poState->GetLastComponent();
356 447254 : 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 447254 : if( (nLen < 6 || !EQUAL(pszLast+nLen-6,"Insert"))
362 : && (nLen < 13 || !EQUAL(pszLast+nLen-13,"featureMember"))
363 : && (nLen < 7 || !EQUAL(pszLast+nLen-7,"Replace")) )
364 307962 : return FALSE;
365 :
366 : // If the class list isn't locked, any element that is a featureMember
367 : // will do.
368 139292 : if( !IsClassListLocked() )
369 6 : return TRUE;
370 :
371 : // otherwise, find a class with the desired element name.
372 2181606 : for( int i = 0; i < GetClassCount(); i++ )
373 : {
374 2181606 : if( EQUAL(pszElement,GetClass(i)->GetElementName()) )
375 139286 : return TRUE;
376 : }
377 :
378 0 : return FALSE;
379 : }
380 :
381 : /************************************************************************/
382 : /* IsAttributeElement() */
383 : /************************************************************************/
384 :
385 307948 : int NASReader::IsAttributeElement( const char *pszElement )
386 :
387 : {
388 307948 : if( m_poState->m_poFeature == NULL )
389 139698 : return FALSE;
390 :
391 168250 : 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 168250 : if( !poClass->IsSchemaLocked() )
396 224 : return TRUE;
397 :
398 : // Otherwise build the path to this element into a single string
399 : // and compare against known attributes.
400 168026 : CPLString osElemPath;
401 :
402 168026 : if( m_poState->m_nPathLength == 0 )
403 84440 : osElemPath = pszElement;
404 : else
405 : {
406 83586 : osElemPath = m_poState->osPath;
407 83586 : osElemPath += "|";
408 83586 : osElemPath += pszElement;
409 : }
410 :
411 1207544 : for( int i = 0; i < poClass->GetPropertyCount(); i++ )
412 1116452 : if( EQUAL(poClass->GetProperty(i)->GetSrcElement(),osElemPath) )
413 76934 : return TRUE;
414 :
415 91092 : return FALSE;
416 : }
417 :
418 : /************************************************************************/
419 : /* PopState() */
420 : /************************************************************************/
421 :
422 17486 : void NASReader::PopState()
423 :
424 : {
425 17486 : if( m_poState != NULL )
426 : {
427 17486 : if( m_poState->m_poFeature != NULL && m_poCompleteFeature == NULL )
428 : {
429 17468 : m_poCompleteFeature = m_poState->m_poFeature;
430 17468 : m_poState->m_poFeature = NULL;
431 : }
432 :
433 : GMLReadState *poParent;
434 :
435 17486 : poParent = m_poState->m_poParentState;
436 :
437 17486 : delete m_poState;
438 17486 : m_poState = poParent;
439 : }
440 17486 : }
441 :
442 : /************************************************************************/
443 : /* PushState() */
444 : /************************************************************************/
445 :
446 17486 : void NASReader::PushState( GMLReadState *poState )
447 :
448 : {
449 17486 : poState->m_poParentState = m_poState;
450 17486 : m_poState = poState;
451 17486 : }
452 :
453 : /************************************************************************/
454 : /* GetClass() */
455 : /************************************************************************/
456 :
457 2516974 : GMLFeatureClass *NASReader::GetClass( int iClass ) const
458 :
459 : {
460 2516974 : if( iClass < 0 || iClass >= m_nClassCount )
461 0 : return NULL;
462 : else
463 2516974 : return m_papoClass[iClass];
464 : }
465 :
466 : /************************************************************************/
467 : /* GetClass() */
468 : /************************************************************************/
469 :
470 508 : GMLFeatureClass *NASReader::GetClass( const char *pszName ) const
471 :
472 : {
473 17576 : for( int iClass = 0; iClass < m_nClassCount; iClass++ )
474 : {
475 17322 : if( EQUAL(GetClass(iClass)->GetName(),pszName) )
476 254 : return GetClass(iClass);
477 : }
478 :
479 254 : return NULL;
480 : }
481 :
482 : /************************************************************************/
483 : /* AddClass() */
484 : /************************************************************************/
485 :
486 254 : int NASReader::AddClass( GMLFeatureClass *poNewClass )
487 :
488 : {
489 254 : CPLAssert( GetClass( poNewClass->GetName() ) == NULL );
490 :
491 254 : m_nClassCount++;
492 : m_papoClass = (GMLFeatureClass **)
493 254 : CPLRealloc( m_papoClass, sizeof(void*) * m_nClassCount );
494 :
495 : // keep delete the last entry
496 254 : if( m_nClassCount > 1 && EQUAL( m_papoClass[m_nClassCount-2]->GetName(), "Delete" ) )
497 : {
498 0 : m_papoClass[m_nClassCount-1] = m_papoClass[m_nClassCount-2];
499 0 : m_papoClass[m_nClassCount-2] = poNewClass;
500 0 : return m_nClassCount-2;
501 : }
502 : else
503 : {
504 254 : m_papoClass[m_nClassCount-1] = poNewClass;
505 254 : return m_nClassCount-1;
506 : }
507 : }
508 :
509 : /************************************************************************/
510 : /* ClearClasses() */
511 : /************************************************************************/
512 :
513 16 : void NASReader::ClearClasses()
514 :
515 : {
516 270 : for( int i = 0; i < m_nClassCount; i++ )
517 254 : delete m_papoClass[i];
518 16 : CPLFree( m_papoClass );
519 :
520 16 : m_nClassCount = 0;
521 16 : m_papoClass = NULL;
522 16 : }
523 :
524 : /************************************************************************/
525 : /* SetFeatureProperty() */
526 : /* */
527 : /* Set the property value on the current feature, adding the */
528 : /* property name to the GMLFeatureClass if required. */
529 : /* The pszValue ownership is passed to this function. */
530 : /************************************************************************/
531 :
532 94546 : void NASReader::SetFeaturePropertyDirectly( const char *pszElement,
533 : char *pszValue )
534 :
535 : {
536 94546 : GMLFeature *poFeature = GetState()->m_poFeature;
537 :
538 94546 : CPLAssert( poFeature != NULL );
539 :
540 : /* -------------------------------------------------------------------- */
541 : /* Does this property exist in the feature class? If not, add */
542 : /* it. */
543 : /* -------------------------------------------------------------------- */
544 94546 : GMLFeatureClass *poClass = poFeature->GetClass();
545 : int iProperty;
546 :
547 403716 : for( iProperty=0; iProperty < poClass->GetPropertyCount(); iProperty++ )
548 : {
549 403650 : if( EQUAL(poClass->GetProperty( iProperty )->GetSrcElement(),
550 : pszElement ) )
551 94480 : break;
552 : }
553 :
554 94546 : if( iProperty == poClass->GetPropertyCount() )
555 : {
556 66 : if( poClass->IsSchemaLocked() )
557 : {
558 0 : CPLDebug("NAS","Encountered property missing from class schema.");
559 0 : CPLFree(pszValue);
560 0 : return;
561 : }
562 :
563 66 : CPLString osFieldName;
564 :
565 66 : if( strchr(pszElement,'|') == NULL )
566 38 : osFieldName = pszElement;
567 : else
568 : {
569 28 : osFieldName = strrchr(pszElement,'|') + 1;
570 28 : if( poClass->GetPropertyIndex(osFieldName) != -1 )
571 4 : osFieldName = pszElement;
572 : }
573 :
574 : // Does this conflict with an existing property name?
575 132 : while( poClass->GetProperty(osFieldName) != NULL )
576 : {
577 0 : osFieldName += "_";
578 : }
579 :
580 66 : GMLPropertyDefn *poPDefn = new GMLPropertyDefn(osFieldName,pszElement);
581 :
582 132 : if( EQUAL(CPLGetConfigOption( "GML_FIELDTYPES", ""), "ALWAYS_STRING") )
583 0 : poPDefn->SetType( GMLPT_String );
584 :
585 66 : poClass->AddProperty( poPDefn );
586 : }
587 :
588 : /* -------------------------------------------------------------------- */
589 : /* We want to handle <lage> specially to ensure it is zero */
590 : /* filled, and treated as a string depspite the numeric */
591 : /* content. https://trac.wheregroup.com/PostNAS/ticket/9 */
592 : /* -------------------------------------------------------------------- */
593 94546 : if( strcmp(poClass->GetProperty(iProperty)->GetName(),"lage") == 0 )
594 : {
595 630 : if( strlen(pszValue) < 5 )
596 : {
597 0 : CPLString osValue = "00000";
598 0 : osValue += pszValue;
599 0 : poFeature->SetPropertyDirectly( iProperty, CPLStrdup(osValue + osValue.size() - 5) );
600 0 : CPLFree(pszValue);
601 : }
602 : else
603 630 : poFeature->SetPropertyDirectly( iProperty, pszValue );
604 :
605 :
606 630 : if( !poClass->IsSchemaLocked() )
607 : {
608 0 : poClass->GetProperty(iProperty)->SetWidth( 5 );
609 0 : poClass->GetProperty(iProperty)->SetType( GMLPT_String );
610 : }
611 630 : return;
612 : }
613 :
614 : /* -------------------------------------------------------------------- */
615 : /* Set the property */
616 : /* -------------------------------------------------------------------- */
617 93916 : poFeature->SetPropertyDirectly( iProperty, pszValue );
618 :
619 : /* -------------------------------------------------------------------- */
620 : /* Do we need to update the property type? */
621 : /* -------------------------------------------------------------------- */
622 93916 : if( !poClass->IsSchemaLocked() )
623 : {
624 : // Special handling for punktkennung per NAS #12
625 160 : if( strcmp(poClass->GetProperty(iProperty)->GetName(),
626 : "punktkennung") == 0)
627 : {
628 0 : poClass->GetProperty(iProperty)->SetWidth( 15 );
629 0 : poClass->GetProperty(iProperty)->SetType( GMLPT_String );
630 : }
631 : // Special handling for artDerFlurstuecksgrenze per http://trac.osgeo.org/gdal/ticket/4255
632 160 : else if( strcmp(poClass->GetProperty(iProperty)->GetName(),
633 : "artDerFlurstuecksgrenze") == 0)
634 : {
635 0 : poClass->GetProperty(iProperty)->SetType( GMLPT_IntegerList );
636 : }
637 : else
638 : poClass->GetProperty(iProperty)->AnalysePropertyValue(
639 160 : poFeature->GetProperty(iProperty));
640 : }
641 : }
642 :
643 : /************************************************************************/
644 : /* LoadClasses() */
645 : /************************************************************************/
646 :
647 4 : int NASReader::LoadClasses( const char *pszFile )
648 :
649 : {
650 : // Add logic later to determine reasonable default schema file.
651 4 : if( pszFile == NULL )
652 0 : return FALSE;
653 :
654 : /* -------------------------------------------------------------------- */
655 : /* Load the raw XML file. */
656 : /* -------------------------------------------------------------------- */
657 : FILE *fp;
658 : int nLength;
659 : char *pszWholeText;
660 :
661 4 : fp = VSIFOpen( pszFile, "rb" );
662 :
663 4 : if( fp == NULL )
664 : {
665 : CPLError( CE_Failure, CPLE_OpenFailed,
666 0 : "Failed to open file %s.", pszFile );
667 0 : return FALSE;
668 : }
669 :
670 4 : VSIFSeek( fp, 0, SEEK_END );
671 4 : nLength = VSIFTell( fp );
672 4 : VSIFSeek( fp, 0, SEEK_SET );
673 :
674 4 : pszWholeText = (char *) VSIMalloc(nLength+1);
675 4 : if( pszWholeText == NULL )
676 : {
677 : CPLError( CE_Failure, CPLE_AppDefined,
678 : "Failed to allocate %d byte buffer for %s,\n"
679 : "is this really a GMLFeatureClassList file?",
680 0 : nLength, pszFile );
681 0 : VSIFClose( fp );
682 0 : return FALSE;
683 : }
684 :
685 4 : if( VSIFRead( pszWholeText, nLength, 1, fp ) != 1 )
686 : {
687 0 : VSIFree( pszWholeText );
688 0 : VSIFClose( fp );
689 : CPLError( CE_Failure, CPLE_AppDefined,
690 0 : "Read failed on %s.", pszFile );
691 0 : return FALSE;
692 : }
693 4 : pszWholeText[nLength] = '\0';
694 :
695 4 : VSIFClose( fp );
696 :
697 4 : if( strstr( pszWholeText, "<GMLFeatureClassList>" ) == NULL )
698 : {
699 0 : VSIFree( pszWholeText );
700 : CPLError( CE_Failure, CPLE_AppDefined,
701 : "File %s does not contain a GMLFeatureClassList tree.",
702 0 : pszFile );
703 0 : return FALSE;
704 : }
705 :
706 : /* -------------------------------------------------------------------- */
707 : /* Convert to XML parse tree. */
708 : /* -------------------------------------------------------------------- */
709 : CPLXMLNode *psRoot;
710 :
711 4 : psRoot = CPLParseXMLString( pszWholeText );
712 4 : VSIFree( pszWholeText );
713 :
714 : // We assume parser will report errors via CPL.
715 4 : if( psRoot == NULL )
716 0 : return FALSE;
717 :
718 4 : if( psRoot->eType != CXT_Element
719 : || !EQUAL(psRoot->pszValue,"GMLFeatureClassList") )
720 : {
721 0 : CPLDestroyXMLNode(psRoot);
722 : CPLError( CE_Failure, CPLE_AppDefined,
723 : "File %s is not a GMLFeatureClassList document.",
724 0 : pszFile );
725 0 : return FALSE;
726 : }
727 :
728 : /* -------------------------------------------------------------------- */
729 : /* Extract feature classes for all definitions found. */
730 : /* -------------------------------------------------------------------- */
731 : CPLXMLNode *psThis;
732 :
733 252 : for( psThis = psRoot->psChild; psThis != NULL; psThis = psThis->psNext )
734 : {
735 248 : if( psThis->eType == CXT_Element
736 : && EQUAL(psThis->pszValue,"GMLFeatureClass") )
737 : {
738 : GMLFeatureClass *poClass;
739 :
740 248 : poClass = new GMLFeatureClass();
741 :
742 248 : if( !poClass->InitializeFromXML( psThis ) )
743 : {
744 0 : delete poClass;
745 0 : CPLDestroyXMLNode( psRoot );
746 0 : return FALSE;
747 : }
748 :
749 248 : poClass->SetSchemaLocked( TRUE );
750 :
751 248 : AddClass( poClass );
752 : }
753 : }
754 :
755 4 : CPLDestroyXMLNode( psRoot );
756 :
757 4 : SetClassListLocked( TRUE );
758 :
759 4 : return TRUE;
760 : }
761 :
762 : /************************************************************************/
763 : /* SaveClasses() */
764 : /************************************************************************/
765 :
766 4 : int NASReader::SaveClasses( const char *pszFile )
767 :
768 : {
769 : // Add logic later to determine reasonable default schema file.
770 4 : if( pszFile == NULL )
771 0 : return FALSE;
772 :
773 : /* -------------------------------------------------------------------- */
774 : /* Create in memory schema tree. */
775 : /* -------------------------------------------------------------------- */
776 : CPLXMLNode *psRoot;
777 :
778 4 : psRoot = CPLCreateXMLNode( NULL, CXT_Element, "GMLFeatureClassList" );
779 :
780 10 : for( int iClass = 0; iClass < GetClassCount(); iClass++ )
781 : {
782 6 : GMLFeatureClass *poClass = GetClass( iClass );
783 :
784 6 : CPLAddXMLChild( psRoot, poClass->SerializeToXML() );
785 : }
786 :
787 : /* -------------------------------------------------------------------- */
788 : /* Serialize to disk. */
789 : /* -------------------------------------------------------------------- */
790 : FILE *fp;
791 4 : int bSuccess = TRUE;
792 4 : char *pszWholeText = CPLSerializeXMLTree( psRoot );
793 :
794 4 : CPLDestroyXMLNode( psRoot );
795 :
796 4 : fp = VSIFOpen( pszFile, "wb" );
797 :
798 4 : if( fp == NULL )
799 0 : bSuccess = FALSE;
800 4 : else if( VSIFWrite( pszWholeText, strlen(pszWholeText), 1, fp ) != 1 )
801 0 : bSuccess = FALSE;
802 : else
803 4 : VSIFClose( fp );
804 :
805 4 : CPLFree( pszWholeText );
806 :
807 4 : return bSuccess;
808 : }
809 :
810 : /************************************************************************/
811 : /* PrescanForSchema() */
812 : /* */
813 : /* For now we use a pretty dumb approach of just doing a normal */
814 : /* scan of the whole file, building up the schema information. */
815 : /* Eventually we hope to do a more efficient scan when just */
816 : /* looking for schema information. */
817 : /************************************************************************/
818 :
819 6 : int NASReader::PrescanForSchema( int bGetExtents )
820 :
821 : {
822 : GMLFeature *poFeature;
823 :
824 6 : if( m_pszFilename == NULL )
825 0 : return FALSE;
826 :
827 6 : SetClassListLocked( FALSE );
828 :
829 6 : ClearClasses();
830 6 : if( !SetupParser() )
831 0 : return FALSE;
832 :
833 6 : std::string osWork;
834 :
835 22 : while( (poFeature = NextFeature()) != NULL )
836 : {
837 10 : GMLFeatureClass *poClass = poFeature->GetClass();
838 :
839 10 : if( poClass->GetFeatureCount() == -1 )
840 6 : poClass->SetFeatureCount( 1 );
841 : else
842 4 : poClass->SetFeatureCount( poClass->GetFeatureCount() + 1 );
843 :
844 : #ifdef SUPPORT_GEOMETRY
845 10 : if( bGetExtents )
846 : {
847 10 : OGRGeometry *poGeometry = NULL;
848 :
849 10 : const CPLXMLNode* const * papsGeometry = poFeature->GetGeometryList();
850 10 : if( papsGeometry[0] != NULL )
851 : {
852 2 : poGeometry = (OGRGeometry*) OGR_G_CreateFromGMLTree(papsGeometry[0]);
853 : }
854 :
855 10 : if( poGeometry != NULL )
856 : {
857 : double dfXMin, dfXMax, dfYMin, dfYMax;
858 2 : OGREnvelope sEnvelope;
859 : OGRwkbGeometryType eGType = (OGRwkbGeometryType)
860 2 : poClass->GetGeometryType();
861 :
862 : // Merge SRSName into layer.
863 2 : const char* pszSRSName = GML_ExtractSrsNameFromGeometry(papsGeometry, osWork, FALSE);
864 : // if (pszSRSName != NULL)
865 : // m_bCanUseGlobalSRSName = FALSE;
866 2 : poClass->MergeSRSName(pszSRSName);
867 :
868 : // Merge geometry type into layer.
869 2 : if( poClass->GetFeatureCount() == 1 && eGType == wkbUnknown )
870 2 : eGType = wkbNone;
871 :
872 : poClass->SetGeometryType(
873 : (int) OGRMergeGeometryTypes(
874 2 : eGType, poGeometry->getGeometryType() ) );
875 :
876 : // merge extents.
877 2 : poGeometry->getEnvelope( &sEnvelope );
878 2 : delete poGeometry;
879 2 : if( poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax) )
880 : {
881 0 : dfXMin = MIN(dfXMin,sEnvelope.MinX);
882 0 : dfXMax = MAX(dfXMax,sEnvelope.MaxX);
883 0 : dfYMin = MIN(dfYMin,sEnvelope.MinY);
884 0 : dfYMax = MAX(dfYMax,sEnvelope.MaxY);
885 : }
886 : else
887 : {
888 2 : dfXMin = sEnvelope.MinX;
889 2 : dfXMax = sEnvelope.MaxX;
890 2 : dfYMin = sEnvelope.MinY;
891 2 : dfYMax = sEnvelope.MaxY;
892 : }
893 :
894 2 : poClass->SetExtents( dfXMin, dfXMax, dfYMin, dfYMax );
895 : }
896 : else
897 : {
898 8 : if( poClass->GetGeometryType() == (int) wkbUnknown
899 : && poClass->GetFeatureCount() == 1 )
900 4 : poClass->SetGeometryType( wkbNone );
901 : }
902 : #endif /* def SUPPORT_GEOMETRY */
903 : }
904 :
905 10 : delete poFeature;
906 : }
907 :
908 6 : CleanupParser();
909 :
910 6 : return GetClassCount() > 0;
911 : }
912 :
913 : /************************************************************************/
914 : /* ResetReading() */
915 : /************************************************************************/
916 :
917 18 : void NASReader::ResetReading()
918 :
919 : {
920 18 : CleanupParser();
921 18 : SetFilteredClassName(NULL);
922 18 : }
923 :
924 : /************************************************************************/
925 : /* CheckForFID() */
926 : /* */
927 : /* Merge the fid attribute into the current field text. */
928 : /************************************************************************/
929 :
930 12 : void NASReader::CheckForFID( const Attributes &attrs,
931 : char **ppszCurField )
932 :
933 : {
934 : int nIndex;
935 : XMLCh Name[100];
936 :
937 12 : tr_strcpy( Name, "fid" );
938 12 : nIndex = attrs.getIndex( Name );
939 :
940 12 : if( nIndex != -1 )
941 : {
942 12 : char *pszFID = tr_strdup( attrs.getValue( nIndex ) );
943 12 : CPLString osCurField = *ppszCurField;
944 :
945 12 : osCurField += pszFID;
946 12 : CPLFree( pszFID );
947 :
948 12 : CPLFree( *ppszCurField );
949 12 : *ppszCurField = CPLStrdup(osCurField);
950 : }
951 12 : }
952 :
953 : /************************************************************************/
954 : /* CheckForRelations() */
955 : /************************************************************************/
956 :
957 77158 : void NASReader::CheckForRelations( const char *pszElement,
958 : const Attributes &attrs )
959 :
960 : {
961 77158 : GMLFeature *poFeature = GetState()->m_poFeature;
962 :
963 77158 : CPLAssert( poFeature != NULL );
964 :
965 : int nIndex;
966 : XMLCh Name[100];
967 :
968 77158 : tr_strcpy( Name, "xlink:href" );
969 77158 : nIndex = attrs.getIndex( Name );
970 :
971 77158 : if( nIndex != -1 )
972 : {
973 5016 : char *pszHRef = tr_strdup( attrs.getValue( nIndex ) );
974 :
975 5016 : if( EQUALN(pszHRef,"urn:adv:oid:", 12 ) )
976 5016 : poFeature->AddOBProperty( pszElement, pszHRef );
977 :
978 5016 : CPLFree( pszHRef );
979 : }
980 77158 : }
981 :
982 : /************************************************************************/
983 : /* HugeFileResolver() */
984 : /* Returns TRUE for success */
985 : /************************************************************************/
986 :
987 0 : int NASReader::HugeFileResolver( const char *pszFile,
988 : int bSqliteIsTempFile,
989 : int iSqliteCacheMB )
990 :
991 : {
992 0 : CPLDebug( "NAS", "HugeFileResolver() not currently implemented for NAS." );
993 0 : return FALSE;
994 : }
995 :
996 : /************************************************************************/
997 : /* PrescanForTemplate() */
998 : /* Returns TRUE for success */
999 : /************************************************************************/
1000 :
1001 0 : int NASReader::PrescanForTemplate( void )
1002 :
1003 : {
1004 0 : CPLDebug( "NAS", "PrescanForTemplate() not currently implemented for NAS." );
1005 0 : return FALSE;
1006 : }
1007 :
1008 : /************************************************************************/
1009 : /* ResolveXlinks() */
1010 : /* Returns TRUE for success */
1011 : /************************************************************************/
1012 :
1013 0 : int NASReader::ResolveXlinks( const char *pszFile,
1014 : int* pbOutIsTempFile,
1015 : char **papszSkip,
1016 : const int bStrict )
1017 :
1018 : {
1019 0 : CPLDebug( "NAS", "ResolveXlinks() not currently implemented for NAS." );
1020 0 : return FALSE;
1021 : }
1022 :
1023 : /************************************************************************/
1024 : /* SetFilteredClassName() */
1025 : /************************************************************************/
1026 :
1027 34 : int NASReader::SetFilteredClassName(const char* pszClassName)
1028 : {
1029 34 : CPLFree(m_pszFilteredClassName);
1030 34 : m_pszFilteredClassName = (pszClassName) ? CPLStrdup(pszClassName) : NULL;
1031 34 : return TRUE;
1032 : }
|