1 : /******************************************************************************
2 : * $Id: nasreader.cpp 25120 2012-10-13 22:38:57Z 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 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 : m_poSAXReader->setFeature( XMLUni::fgXercesSchema, false);
185 : #else
186 9 : 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( "NAS",
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 : iClass = 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 221593 : int NASReader::IsFeatureElement( const char *pszElement )
351 :
352 : {
353 221593 : CPLAssert( m_poState != NULL );
354 :
355 221593 : const char *pszLast = m_poState->GetLastComponent();
356 221593 : 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 221593 : 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 151947 : 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 151940 : int NASReader::IsAttributeElement( const char *pszElement )
386 :
387 : {
388 151940 : if( m_poState->m_poFeature == NULL )
389 69849 : return FALSE;
390 :
391 82091 : 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 82091 : if( !poClass->IsSchemaLocked() )
396 76 : return TRUE;
397 :
398 : // Otherwise build the path to this element into a single string
399 : // and compare against known attributes.
400 82015 : CPLString osElemPath;
401 :
402 82015 : if( m_poState->m_nPathLength == 0 )
403 41887 : osElemPath = pszElement;
404 : else
405 : {
406 40128 : osElemPath = m_poState->osPath;
407 40128 : osElemPath += "|";
408 40128 : osElemPath += pszElement;
409 : }
410 :
411 566143 : for( int i = 0; i < poClass->GetPropertyCount(); i++ )
412 521929 : if( EQUAL(poClass->GetProperty(i)->GetSrcElement(),osElemPath) )
413 37801 : return TRUE;
414 :
415 44214 : 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 :
495 : // keep delete the last entry
496 127 : 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 127 : m_papoClass[m_nClassCount-1] = poNewClass;
505 127 : return m_nClassCount-1;
506 : }
507 : }
508 :
509 : /************************************************************************/
510 : /* ClearClasses() */
511 : /************************************************************************/
512 :
513 8 : void NASReader::ClearClasses()
514 :
515 : {
516 135 : for( int i = 0; i < m_nClassCount; i++ )
517 127 : delete m_papoClass[i];
518 8 : CPLFree( m_papoClass );
519 :
520 8 : m_nClassCount = 0;
521 8 : m_papoClass = NULL;
522 8 : }
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 46595 : void NASReader::SetFeaturePropertyDirectly( const char *pszElement,
533 : char *pszValue )
534 :
535 : {
536 46595 : GMLFeature *poFeature = GetState()->m_poFeature;
537 :
538 46595 : CPLAssert( poFeature != NULL );
539 :
540 : /* -------------------------------------------------------------------- */
541 : /* Does this property exist in the feature class? If not, add */
542 : /* it. */
543 : /* -------------------------------------------------------------------- */
544 46595 : GMLFeatureClass *poClass = poFeature->GetClass();
545 : int iProperty;
546 :
547 197341 : for( iProperty=0; iProperty < poClass->GetPropertyCount(); iProperty++ )
548 : {
549 197311 : if( EQUAL(poClass->GetProperty( iProperty )->GetSrcElement(),
550 : pszElement ) )
551 46565 : break;
552 : }
553 :
554 46595 : if( iProperty == poClass->GetPropertyCount() )
555 : {
556 30 : if( poClass->IsSchemaLocked() )
557 : {
558 0 : CPLDebug("NAS", "Encountered property missing from class schema.");
559 0 : CPLFree(pszValue);
560 0 : return;
561 : }
562 :
563 30 : CPLString osFieldName;
564 :
565 30 : if( strchr(pszElement,'|') == NULL )
566 19 : osFieldName = pszElement;
567 : else
568 : {
569 11 : osFieldName = strrchr(pszElement,'|') + 1;
570 11 : if( poClass->GetPropertyIndex(osFieldName) != -1 )
571 2 : osFieldName = pszElement;
572 : }
573 :
574 : // Does this conflict with an existing property name?
575 60 : while( poClass->GetProperty(osFieldName) != NULL )
576 : {
577 0 : osFieldName += "_";
578 : }
579 :
580 30 : GMLPropertyDefn *poPDefn = new GMLPropertyDefn(osFieldName,pszElement);
581 :
582 60 : if( EQUAL(CPLGetConfigOption( "GML_FIELDTYPES", ""), "ALWAYS_STRING") )
583 0 : poPDefn->SetType( GMLPT_String );
584 :
585 30 : poClass->AddProperty( poPDefn );
586 : }
587 :
588 46595 : if ( GMLPropertyDefn::IsSimpleType( poClass->GetProperty( iProperty )->GetType() ) )
589 : {
590 43277 : const GMLProperty *poProp = poFeature->GetProperty(iProperty);
591 43277 : if ( poProp && poProp->nSubProperties > 0 )
592 : {
593 0 : int iId = poClass->GetPropertyIndex( "gml_id" );
594 0 : const GMLProperty *poIdProp = poFeature->GetProperty(iId);
595 :
596 : CPLDebug("NAS",
597 : "Overwriting existing property %s.%s of value '%s' with '%s' (gml_id: %s).",
598 : poClass->GetName(), pszElement,
599 : poProp->papszSubProperties[0], pszValue,
600 0 : poIdProp && poIdProp->nSubProperties>0 && poIdProp->papszSubProperties[0] ? poIdProp->papszSubProperties[0] : "(null)" );
601 : }
602 : }
603 :
604 : /* -------------------------------------------------------------------- */
605 : /* We want to handle <lage> specially to ensure it is zero */
606 : /* filled, and treated as a string depspite the numeric */
607 : /* content. https://trac.wheregroup.com/PostNAS/ticket/9 */
608 : /* -------------------------------------------------------------------- */
609 46595 : if( strcmp(poClass->GetProperty(iProperty)->GetName(),"lage") == 0 )
610 : {
611 315 : if( strlen(pszValue) < 5 )
612 : {
613 0 : CPLString osValue = "00000";
614 0 : osValue += pszValue;
615 0 : poFeature->SetPropertyDirectly( iProperty, CPLStrdup(osValue + osValue.size() - 5) );
616 0 : CPLFree(pszValue);
617 : }
618 : else
619 315 : poFeature->SetPropertyDirectly( iProperty, pszValue );
620 :
621 315 : if( !poClass->IsSchemaLocked() )
622 : {
623 0 : poClass->GetProperty(iProperty)->SetWidth( 5 );
624 0 : poClass->GetProperty(iProperty)->SetType( GMLPT_String );
625 : }
626 315 : return;
627 : }
628 : #if 0
629 : else if( strcmp(poClass->GetProperty(iProperty)->GetName(),"signaturnummer") == 0 ||
630 : strcmp(poClass->GetProperty(iProperty)->GetName(),"anlass") == 0 )
631 : {
632 : if( isalpha( pszValue[0] ) && isalpha( pszValue[1] ) && isdigit( pszValue[2] ) )
633 : {
634 : poFeature->SetPropertyDirectly( iProperty, CPLStrdup( pszValue+2 ) );
635 : #ifdef DEBUG_VERBOSE
636 : CPLDebug( "NAS", "Skipping two letter prefix of '%s'", pszValue );
637 : #endif
638 : CPLFree(pszValue);
639 : return;
640 : }
641 : }
642 : #endif
643 46280 : else if( strcmp(poClass->GetProperty(iProperty)->GetName(),"kartendarstellung") == 0 ||
644 : strcmp(poClass->GetProperty(iProperty)->GetName(),"rechtsbehelfsverfahren") == 0 )
645 : {
646 : poFeature->SetPropertyDirectly( iProperty,
647 335 : CPLStrdup( EQUAL( pszValue, "true" ) ? "1" : "0" ) );
648 335 : CPLFree(pszValue);
649 :
650 335 : if( !poClass->IsSchemaLocked() )
651 : {
652 2 : poClass->GetProperty(iProperty)->SetType( GMLPT_Integer );
653 : }
654 335 : return;
655 : }
656 :
657 : /* -------------------------------------------------------------------- */
658 : /* Set the property */
659 : /* -------------------------------------------------------------------- */
660 45945 : poFeature->SetPropertyDirectly( iProperty, pszValue );
661 :
662 : /* -------------------------------------------------------------------- */
663 : /* Do we need to update the property type? */
664 : /* -------------------------------------------------------------------- */
665 45945 : if( !poClass->IsSchemaLocked() )
666 : {
667 : // Special handling for punktkennung per NAS #12
668 66 : if( strcmp(poClass->GetProperty(iProperty)->GetName(),
669 : "punktkennung") == 0)
670 : {
671 0 : poClass->GetProperty(iProperty)->SetWidth( 15 );
672 0 : poClass->GetProperty(iProperty)->SetType( GMLPT_String );
673 : }
674 : // Special handling for artDerFlurstuecksgrenze per http://trac.osgeo.org/gdal/ticket/4255
675 66 : else if( strcmp(poClass->GetProperty(iProperty)->GetName(),
676 : "artDerFlurstuecksgrenze") == 0)
677 : {
678 0 : poClass->GetProperty(iProperty)->SetType( GMLPT_IntegerList );
679 : }
680 : else
681 : poClass->GetProperty(iProperty)->AnalysePropertyValue(
682 66 : poFeature->GetProperty(iProperty));
683 : }
684 : }
685 :
686 : /************************************************************************/
687 : /* LoadClasses() */
688 : /************************************************************************/
689 :
690 2 : int NASReader::LoadClasses( const char *pszFile )
691 :
692 : {
693 : // Add logic later to determine reasonable default schema file.
694 2 : if( pszFile == NULL )
695 0 : return FALSE;
696 :
697 : /* -------------------------------------------------------------------- */
698 : /* Load the raw XML file. */
699 : /* -------------------------------------------------------------------- */
700 : FILE *fp;
701 : int nLength;
702 : char *pszWholeText;
703 :
704 2 : fp = VSIFOpen( pszFile, "rb" );
705 :
706 2 : if( fp == NULL )
707 : {
708 : CPLError( CE_Failure, CPLE_OpenFailed,
709 0 : "Failed to open file %s.", pszFile );
710 0 : return FALSE;
711 : }
712 :
713 2 : VSIFSeek( fp, 0, SEEK_END );
714 2 : nLength = VSIFTell( fp );
715 2 : VSIFSeek( fp, 0, SEEK_SET );
716 :
717 2 : pszWholeText = (char *) VSIMalloc(nLength+1);
718 2 : if( pszWholeText == NULL )
719 : {
720 : CPLError( CE_Failure, CPLE_AppDefined,
721 : "Failed to allocate %d byte buffer for %s,\n"
722 : "is this really a GMLFeatureClassList file?",
723 0 : nLength, pszFile );
724 0 : VSIFClose( fp );
725 0 : return FALSE;
726 : }
727 :
728 2 : if( VSIFRead( pszWholeText, nLength, 1, fp ) != 1 )
729 : {
730 0 : VSIFree( pszWholeText );
731 0 : VSIFClose( fp );
732 : CPLError( CE_Failure, CPLE_AppDefined,
733 0 : "Read failed on %s.", pszFile );
734 0 : return FALSE;
735 : }
736 2 : pszWholeText[nLength] = '\0';
737 :
738 2 : VSIFClose( fp );
739 :
740 2 : if( strstr( pszWholeText, "<GMLFeatureClassList>" ) == NULL )
741 : {
742 0 : VSIFree( pszWholeText );
743 : CPLError( CE_Failure, CPLE_AppDefined,
744 : "File %s does not contain a GMLFeatureClassList tree.",
745 0 : pszFile );
746 0 : return FALSE;
747 : }
748 :
749 : /* -------------------------------------------------------------------- */
750 : /* Convert to XML parse tree. */
751 : /* -------------------------------------------------------------------- */
752 : CPLXMLNode *psRoot;
753 :
754 2 : psRoot = CPLParseXMLString( pszWholeText );
755 2 : VSIFree( pszWholeText );
756 :
757 : // We assume parser will report errors via CPL.
758 2 : if( psRoot == NULL )
759 0 : return FALSE;
760 :
761 2 : if( psRoot->eType != CXT_Element
762 : || !EQUAL(psRoot->pszValue,"GMLFeatureClassList") )
763 : {
764 0 : CPLDestroyXMLNode(psRoot);
765 : CPLError( CE_Failure, CPLE_AppDefined,
766 : "File %s is not a GMLFeatureClassList document.",
767 0 : pszFile );
768 0 : return FALSE;
769 : }
770 :
771 : /* -------------------------------------------------------------------- */
772 : /* Extract feature classes for all definitions found. */
773 : /* -------------------------------------------------------------------- */
774 : CPLXMLNode *psThis;
775 :
776 126 : for( psThis = psRoot->psChild; psThis != NULL; psThis = psThis->psNext )
777 : {
778 124 : if( psThis->eType == CXT_Element
779 : && EQUAL(psThis->pszValue,"GMLFeatureClass") )
780 : {
781 : GMLFeatureClass *poClass;
782 :
783 124 : poClass = new GMLFeatureClass();
784 :
785 124 : if( !poClass->InitializeFromXML( psThis ) )
786 : {
787 0 : delete poClass;
788 0 : CPLDestroyXMLNode( psRoot );
789 0 : return FALSE;
790 : }
791 :
792 124 : poClass->SetSchemaLocked( TRUE );
793 :
794 124 : AddClass( poClass );
795 : }
796 : }
797 :
798 2 : CPLDestroyXMLNode( psRoot );
799 :
800 2 : SetClassListLocked( TRUE );
801 :
802 2 : return TRUE;
803 : }
804 :
805 : /************************************************************************/
806 : /* SaveClasses() */
807 : /************************************************************************/
808 :
809 2 : int NASReader::SaveClasses( const char *pszFile )
810 :
811 : {
812 : // Add logic later to determine reasonable default schema file.
813 2 : if( pszFile == NULL )
814 0 : return FALSE;
815 :
816 : /* -------------------------------------------------------------------- */
817 : /* Create in memory schema tree. */
818 : /* -------------------------------------------------------------------- */
819 : CPLXMLNode *psRoot;
820 :
821 2 : psRoot = CPLCreateXMLNode( NULL, CXT_Element, "GMLFeatureClassList" );
822 :
823 5 : for( int iClass = 0; iClass < GetClassCount(); iClass++ )
824 : {
825 3 : GMLFeatureClass *poClass = GetClass( iClass );
826 :
827 3 : CPLAddXMLChild( psRoot, poClass->SerializeToXML() );
828 : }
829 :
830 : /* -------------------------------------------------------------------- */
831 : /* Serialize to disk. */
832 : /* -------------------------------------------------------------------- */
833 : FILE *fp;
834 2 : int bSuccess = TRUE;
835 2 : char *pszWholeText = CPLSerializeXMLTree( psRoot );
836 :
837 2 : CPLDestroyXMLNode( psRoot );
838 :
839 2 : fp = VSIFOpen( pszFile, "wb" );
840 :
841 2 : if( fp == NULL )
842 0 : bSuccess = FALSE;
843 2 : else if( VSIFWrite( pszWholeText, strlen(pszWholeText), 1, fp ) != 1 )
844 0 : bSuccess = FALSE;
845 : else
846 2 : VSIFClose( fp );
847 :
848 2 : CPLFree( pszWholeText );
849 :
850 2 : return bSuccess;
851 : }
852 :
853 : /************************************************************************/
854 : /* PrescanForSchema() */
855 : /* */
856 : /* For now we use a pretty dumb approach of just doing a normal */
857 : /* scan of the whole file, building up the schema information. */
858 : /* Eventually we hope to do a more efficient scan when just */
859 : /* looking for schema information. */
860 : /************************************************************************/
861 :
862 3 : int NASReader::PrescanForSchema( int bGetExtents )
863 :
864 : {
865 : GMLFeature *poFeature;
866 :
867 3 : if( m_pszFilename == NULL )
868 0 : return FALSE;
869 :
870 3 : SetClassListLocked( FALSE );
871 :
872 3 : ClearClasses();
873 3 : if( !SetupParser() )
874 0 : return FALSE;
875 :
876 3 : std::string osWork;
877 :
878 11 : while( (poFeature = NextFeature()) != NULL )
879 : {
880 5 : GMLFeatureClass *poClass = poFeature->GetClass();
881 :
882 5 : if( poClass->GetFeatureCount() == -1 )
883 3 : poClass->SetFeatureCount( 1 );
884 : else
885 2 : poClass->SetFeatureCount( poClass->GetFeatureCount() + 1 );
886 :
887 : #ifdef SUPPORT_GEOMETRY
888 5 : if( bGetExtents )
889 : {
890 5 : OGRGeometry *poGeometry = NULL;
891 :
892 5 : const CPLXMLNode* const * papsGeometry = poFeature->GetGeometryList();
893 5 : if( papsGeometry[0] != NULL )
894 : {
895 1 : poGeometry = (OGRGeometry*) OGR_G_CreateFromGMLTree(papsGeometry[0]);
896 : }
897 :
898 5 : if( poGeometry != NULL )
899 : {
900 : double dfXMin, dfXMax, dfYMin, dfYMax;
901 1 : OGREnvelope sEnvelope;
902 : OGRwkbGeometryType eGType = (OGRwkbGeometryType)
903 1 : poClass->GetGeometryType();
904 :
905 : // Merge SRSName into layer.
906 1 : const char* pszSRSName = GML_ExtractSrsNameFromGeometry(papsGeometry, osWork, FALSE);
907 : // if (pszSRSName != NULL)
908 : // m_bCanUseGlobalSRSName = FALSE;
909 1 : poClass->MergeSRSName(pszSRSName);
910 :
911 : // Merge geometry type into layer.
912 1 : if( poClass->GetFeatureCount() == 1 && eGType == wkbUnknown )
913 1 : eGType = wkbNone;
914 :
915 : poClass->SetGeometryType(
916 : (int) OGRMergeGeometryTypes(
917 1 : eGType, poGeometry->getGeometryType() ) );
918 :
919 : // merge extents.
920 1 : poGeometry->getEnvelope( &sEnvelope );
921 1 : delete poGeometry;
922 1 : if( poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax) )
923 : {
924 0 : dfXMin = MIN(dfXMin,sEnvelope.MinX);
925 0 : dfXMax = MAX(dfXMax,sEnvelope.MaxX);
926 0 : dfYMin = MIN(dfYMin,sEnvelope.MinY);
927 0 : dfYMax = MAX(dfYMax,sEnvelope.MaxY);
928 : }
929 : else
930 : {
931 1 : dfXMin = sEnvelope.MinX;
932 1 : dfXMax = sEnvelope.MaxX;
933 1 : dfYMin = sEnvelope.MinY;
934 1 : dfYMax = sEnvelope.MaxY;
935 : }
936 :
937 1 : poClass->SetExtents( dfXMin, dfXMax, dfYMin, dfYMax );
938 : }
939 : else
940 : {
941 4 : if( poClass->GetGeometryType() == (int) wkbUnknown
942 : && poClass->GetFeatureCount() == 1 )
943 2 : poClass->SetGeometryType( wkbNone );
944 : }
945 : #endif /* def SUPPORT_GEOMETRY */
946 : }
947 :
948 5 : delete poFeature;
949 : }
950 :
951 3 : CleanupParser();
952 :
953 3 : return GetClassCount() > 0;
954 : }
955 :
956 : /************************************************************************/
957 : /* ResetReading() */
958 : /************************************************************************/
959 :
960 9 : void NASReader::ResetReading()
961 :
962 : {
963 9 : CleanupParser();
964 9 : SetFilteredClassName(NULL);
965 9 : }
966 :
967 : /************************************************************************/
968 : /* CheckForFID() */
969 : /* */
970 : /* Merge the fid attribute into the current field text. */
971 : /************************************************************************/
972 :
973 6 : void NASReader::CheckForFID( const Attributes &attrs,
974 : char **ppszCurField )
975 :
976 : {
977 : int nIndex;
978 : XMLCh Name[100];
979 :
980 6 : tr_strcpy( Name, "fid" );
981 6 : nIndex = attrs.getIndex( Name );
982 :
983 6 : if( nIndex != -1 )
984 : {
985 6 : char *pszFID = tr_strdup( attrs.getValue( nIndex ) );
986 6 : CPLString osCurField = *ppszCurField;
987 :
988 6 : osCurField += pszFID;
989 6 : CPLFree( pszFID );
990 :
991 6 : CPLFree( *ppszCurField );
992 6 : *ppszCurField = CPLStrdup(osCurField);
993 : }
994 6 : }
995 :
996 : /************************************************************************/
997 : /* CheckForRelations() */
998 : /************************************************************************/
999 :
1000 37877 : void NASReader::CheckForRelations( const char *pszElement,
1001 : const Attributes &attrs )
1002 :
1003 : {
1004 37877 : GMLFeature *poFeature = GetState()->m_poFeature;
1005 :
1006 37877 : CPLAssert( poFeature != NULL );
1007 :
1008 : int nIndex;
1009 : XMLCh Name[100];
1010 :
1011 37877 : tr_strcpy( Name, "xlink:href" );
1012 37877 : nIndex = attrs.getIndex( Name );
1013 :
1014 37877 : if( nIndex != -1 )
1015 : {
1016 2508 : char *pszHRef = tr_strdup( attrs.getValue( nIndex ) );
1017 :
1018 2508 : if( EQUALN(pszHRef,"urn:adv:oid:", 12 ) )
1019 2508 : poFeature->AddOBProperty( pszElement, pszHRef );
1020 :
1021 2508 : CPLFree( pszHRef );
1022 : }
1023 37877 : }
1024 :
1025 : /************************************************************************/
1026 : /* HugeFileResolver() */
1027 : /* Returns TRUE for success */
1028 : /************************************************************************/
1029 :
1030 0 : int NASReader::HugeFileResolver( const char *pszFile,
1031 : int bSqliteIsTempFile,
1032 : int iSqliteCacheMB )
1033 :
1034 : {
1035 0 : CPLDebug( "NAS", "HugeFileResolver() not currently implemented for NAS." );
1036 0 : return FALSE;
1037 : }
1038 :
1039 : /************************************************************************/
1040 : /* PrescanForTemplate() */
1041 : /* Returns TRUE for success */
1042 : /************************************************************************/
1043 :
1044 0 : int NASReader::PrescanForTemplate( void )
1045 :
1046 : {
1047 0 : CPLDebug( "NAS", "PrescanForTemplate() not currently implemented for NAS." );
1048 0 : return FALSE;
1049 : }
1050 :
1051 : /************************************************************************/
1052 : /* ResolveXlinks() */
1053 : /* Returns TRUE for success */
1054 : /************************************************************************/
1055 :
1056 0 : int NASReader::ResolveXlinks( const char *pszFile,
1057 : int* pbOutIsTempFile,
1058 : char **papszSkip,
1059 : const int bStrict )
1060 :
1061 : {
1062 0 : CPLDebug( "NAS", "ResolveXlinks() not currently implemented for NAS." );
1063 0 : return FALSE;
1064 : }
1065 :
1066 : /************************************************************************/
1067 : /* SetFilteredClassName() */
1068 : /************************************************************************/
1069 :
1070 17 : int NASReader::SetFilteredClassName(const char* pszClassName)
1071 : {
1072 17 : CPLFree(m_pszFilteredClassName);
1073 17 : m_pszFilteredClassName = (pszClassName) ? CPLStrdup(pszClassName) : NULL;
1074 17 : return TRUE;
1075 : }
|