1 : /******************************************************************************
2 : * $Id: parsexsd.cpp 23315 2011-11-04 20:00:38Z rouault $
3 : *
4 : * Project: GML Reader
5 : * Purpose: Implementation of GMLParseXSD()
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2005, 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 "parsexsd.h"
31 : #include "cpl_error.h"
32 : #include "cpl_conv.h"
33 : #include "ogr_core.h"
34 :
35 : /************************************************************************/
36 : /* StripNS() */
37 : /* */
38 : /* Return potentially shortened form of string with namespace */
39 : /* stripped off if there is one. Returns pointer into */
40 : /* original string. */
41 : /************************************************************************/
42 :
43 925 : const char *StripNS( const char *pszFullValue )
44 :
45 : {
46 925 : const char *pszColon = strstr( pszFullValue, ":" );
47 925 : if( pszColon == NULL )
48 257 : return pszFullValue;
49 : else
50 668 : return pszColon + 1;
51 : }
52 :
53 : /************************************************************************/
54 : /* GetSimpleTypeProperties() */
55 : /************************************************************************/
56 :
57 : static
58 24 : int GetSimpleTypeProperties(CPLXMLNode *psTypeNode,
59 : GMLPropertyType *pGMLType,
60 : int *pnWidth,
61 : int *pnPrecision)
62 : {
63 : const char *pszBase =
64 : StripNS( CPLGetXMLValue( psTypeNode,
65 24 : "restriction.base", "" ));
66 :
67 24 : if( EQUAL(pszBase,"decimal") )
68 : {
69 8 : *pGMLType = GMLPT_Real;
70 : const char *pszWidth =
71 : CPLGetXMLValue( psTypeNode,
72 8 : "restriction.totalDigits.value", "0" );
73 : const char *pszPrecision =
74 : CPLGetXMLValue( psTypeNode,
75 8 : "restriction.fractionDigits.value", "0" );
76 8 : *pnWidth = atoi(pszWidth);
77 8 : *pnPrecision = atoi(pszPrecision);
78 8 : return TRUE;
79 : }
80 :
81 16 : else if( EQUAL(pszBase,"float")
82 : || EQUAL(pszBase,"double") )
83 : {
84 0 : *pGMLType = GMLPT_Real;
85 0 : return TRUE;
86 : }
87 :
88 16 : else if( EQUAL(pszBase,"integer") )
89 : {
90 4 : *pGMLType = GMLPT_Integer;
91 : const char *pszWidth =
92 : CPLGetXMLValue( psTypeNode,
93 4 : "restriction.totalDigits.value", "0" );
94 4 : *pnWidth = atoi(pszWidth);
95 4 : return TRUE;
96 : }
97 :
98 12 : else if( EQUAL(pszBase,"string") )
99 : {
100 12 : *pGMLType = GMLPT_String;
101 : const char *pszWidth =
102 : CPLGetXMLValue( psTypeNode,
103 12 : "restriction.maxLength.value", "0" );
104 12 : *pnWidth = atoi(pszWidth);
105 12 : return TRUE;
106 : }
107 :
108 0 : return FALSE;
109 : }
110 :
111 : /************************************************************************/
112 : /* LookForSimpleType() */
113 : /************************************************************************/
114 :
115 : static
116 4 : int LookForSimpleType(CPLXMLNode *psSchemaNode,
117 : const char* pszStrippedNSType,
118 : GMLPropertyType *pGMLType,
119 : int *pnWidth,
120 : int *pnPrecision)
121 : {
122 : CPLXMLNode *psThis;
123 92 : for( psThis = psSchemaNode->psChild;
124 : psThis != NULL; psThis = psThis->psNext )
125 : {
126 88 : if( psThis->eType == CXT_Element
127 : && EQUAL(psThis->pszValue,"simpleType")
128 : && EQUAL(CPLGetXMLValue(psThis,"name",""),pszStrippedNSType) )
129 : {
130 0 : break;
131 : }
132 : }
133 4 : if (psThis == NULL)
134 4 : return FALSE;
135 :
136 0 : return GetSimpleTypeProperties(psThis, pGMLType, pnWidth, pnPrecision);
137 : }
138 :
139 : /************************************************************************/
140 : /* ParseFeatureType() */
141 : /************************************************************************/
142 :
143 : typedef struct
144 : {
145 : const char* pszName;
146 : OGRwkbGeometryType eType;
147 : } AssocNameType;
148 :
149 : static const AssocNameType apsPropertyTypes [] =
150 : {
151 : {"GeometryPropertyType", wkbUnknown},
152 : {"PointPropertyType", wkbPoint},
153 : {"LineStringPropertyType", wkbLineString},
154 : {"CurvePropertyType", wkbLineString},
155 : {"PolygonPropertyType", wkbPolygon},
156 : {"SurfacePropertyType", wkbPolygon},
157 : {"MultiPointPropertyType", wkbMultiPoint},
158 : {"MultiLineStringPropertyType", wkbMultiLineString},
159 : {"MultiCurvePropertyType", wkbMultiLineString},
160 : {"MultiPolygonPropertyType", wkbMultiPolygon},
161 : {"MultiSurfacePropertyType", wkbMultiPolygon},
162 : {"MultiGeometryPropertyType", wkbGeometryCollection},
163 : {NULL, wkbUnknown},
164 : };
165 :
166 : /* Found in FME .xsd (e.g. <element ref="gml:curveProperty" minOccurs="0"/>) */
167 : static const AssocNameType apsRefTypes [] =
168 : {
169 : {"pointProperty", wkbPoint},
170 : {"curveProperty", wkbLineString},
171 : {"surfaceProperty", wkbPolygon},
172 : {"multiPointProperty", wkbMultiPoint},
173 : {"multiCurveProperty", wkbMultiLineString},
174 : {"multiSurfaceProperty", wkbMultiPolygon},
175 : {NULL, wkbUnknown},
176 : };
177 :
178 : static
179 : GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode,
180 : const char* pszName,
181 : CPLXMLNode *psThis);
182 :
183 : static
184 82 : GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode,
185 : const char* pszName,
186 : const char *pszType)
187 : {
188 : CPLXMLNode *psThis;
189 1190 : for( psThis = psSchemaNode->psChild;
190 : psThis != NULL; psThis = psThis->psNext )
191 : {
192 1190 : if( psThis->eType == CXT_Element
193 : && EQUAL(psThis->pszValue,"complexType")
194 : && EQUAL(CPLGetXMLValue(psThis,"name",""),pszType) )
195 : {
196 82 : break;
197 : }
198 : }
199 82 : if (psThis == NULL)
200 0 : return NULL;
201 :
202 82 : return GMLParseFeatureType(psSchemaNode, pszName, psThis);
203 : }
204 :
205 :
206 : static
207 120 : GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode,
208 : const char* pszName,
209 : CPLXMLNode *psComplexType)
210 : {
211 :
212 : /* -------------------------------------------------------------------- */
213 : /* Grab the sequence of extensions greatgrandchild. */
214 : /* -------------------------------------------------------------------- */
215 : CPLXMLNode *psAttrSeq =
216 : CPLGetXMLNode( psComplexType,
217 120 : "complexContent.extension.sequence" );
218 :
219 120 : if( psAttrSeq == NULL )
220 : {
221 0 : return NULL;
222 : }
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* We are pretty sure this going to be a valid Feature class */
226 : /* now, so create it. */
227 : /* -------------------------------------------------------------------- */
228 120 : GMLFeatureClass *poClass = new GMLFeatureClass( pszName );
229 :
230 : /* -------------------------------------------------------------------- */
231 : /* Loop over each of the attribute elements being defined for */
232 : /* this feature class. */
233 : /* -------------------------------------------------------------------- */
234 : CPLXMLNode *psAttrDef;
235 120 : int nAttributeIndex = 0;
236 :
237 120 : int bGotUnrecognizedType = FALSE;
238 :
239 927 : for( psAttrDef = psAttrSeq->psChild;
240 : psAttrDef != NULL;
241 : psAttrDef = psAttrDef->psNext )
242 : {
243 809 : if( strcmp(psAttrDef->pszValue,"group") == 0 )
244 : {
245 : /* Too complex schema for us. Aborts parsing */
246 0 : delete poClass;
247 0 : return NULL;
248 : }
249 :
250 809 : if( !EQUAL(psAttrDef->pszValue,"element") )
251 0 : continue;
252 :
253 : /* MapServer WFS writes element type as an attribute of element */
254 : /* not as a simpleType definition */
255 809 : const char* pszType = CPLGetXMLValue( psAttrDef, "type", NULL );
256 809 : const char* pszElementName = CPLGetXMLValue( psAttrDef, "name", NULL );
257 809 : if (pszType != NULL)
258 : {
259 767 : const char* pszStrippedNSType = StripNS(pszType);
260 767 : int nWidth = 0, nPrecision = 0;
261 :
262 767 : GMLPropertyType gmlType = GMLPT_Untyped;
263 1093 : if (EQUAL(pszStrippedNSType, "string") ||
264 : EQUAL(pszStrippedNSType, "Character"))
265 326 : gmlType = GMLPT_String;
266 635 : else if (EQUAL(pszStrippedNSType, "real") ||
267 : EQUAL(pszStrippedNSType, "double") ||
268 : EQUAL(pszStrippedNSType, "float") ||
269 : EQUAL(pszStrippedNSType, "decimal"))
270 194 : gmlType = GMLPT_Real;
271 380 : else if (EQUAL(pszStrippedNSType, "short") ||
272 : EQUAL(pszStrippedNSType, "int") ||
273 : EQUAL(pszStrippedNSType, "integer") ||
274 : EQUAL(pszStrippedNSType, "long"))
275 133 : gmlType = GMLPT_Integer;
276 114 : else if (strncmp(pszType, "gml:", 4) == 0)
277 : {
278 110 : const AssocNameType* psIter = apsPropertyTypes;
279 672 : while(psIter->pszName)
280 : {
281 560 : if (strncmp(pszType + 4, psIter->pszName, strlen(psIter->pszName)) == 0)
282 : {
283 108 : if (poClass->GetGeometryAttributeIndex() != -1)
284 : {
285 0 : CPLDebug("GML", "Geometry field already found ! Ignoring the following ones");
286 : }
287 : else
288 : {
289 108 : poClass->SetGeometryElement(pszElementName);
290 108 : poClass->SetGeometryType(psIter->eType);
291 108 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
292 :
293 108 : nAttributeIndex ++;
294 : }
295 :
296 108 : break;
297 : }
298 :
299 452 : psIter ++;
300 : }
301 :
302 110 : if (psIter->pszName == NULL)
303 : {
304 : /* Can be a non geometry gml type */
305 : /* Too complex schema for us. Aborts parsing */
306 2 : delete poClass;
307 2 : return NULL;
308 : }
309 :
310 108 : if (poClass->GetGeometryAttributeIndex() == -1)
311 0 : bGotUnrecognizedType = TRUE;
312 :
313 108 : continue;
314 : }
315 :
316 : /* Integraph stuff */
317 4 : else if (strcmp(pszType, "G:Point_MultiPointPropertyType") == 0 ||
318 : strcmp(pszType, "gmgml:Point_MultiPointPropertyType") == 0)
319 : {
320 0 : poClass->SetGeometryElement(pszElementName);
321 0 : poClass->SetGeometryType(wkbMultiPoint);
322 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
323 :
324 0 : nAttributeIndex ++;
325 0 : continue;
326 : }
327 4 : else if (strcmp(pszType, "G:LineString_MultiLineStringPropertyType") == 0 ||
328 : strcmp(pszType, "gmgml:LineString_MultiLineStringPropertyType") == 0)
329 : {
330 0 : poClass->SetGeometryElement(pszElementName);
331 0 : poClass->SetGeometryType(wkbMultiLineString);
332 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
333 :
334 0 : nAttributeIndex ++;
335 0 : continue;
336 : }
337 4 : else if (strcmp(pszType, "G:Polygon_MultiPolygonPropertyType") == 0 ||
338 : strcmp(pszType, "gmgml:Polygon_MultiPolygonPropertyType") == 0 ||
339 : strcmp(pszType, "gmgml:Polygon_Surface_MultiSurface_CompositeSurfacePropertyType") == 0)
340 : {
341 0 : poClass->SetGeometryElement(pszElementName);
342 0 : poClass->SetGeometryType(wkbMultiPolygon);
343 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
344 :
345 0 : nAttributeIndex ++;
346 0 : continue;
347 : }
348 :
349 : /* ERDAS Apollo stuff (like in http://apollo.erdas.com/erdas-apollo/vector/WORLDWIDE?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=wfs:cntry98) */
350 4 : else if (strcmp(pszType, "wfs:MixedPolygonPropertyType") == 0)
351 : {
352 0 : poClass->SetGeometryElement(pszElementName);
353 0 : poClass->SetGeometryType(wkbMultiPolygon);
354 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
355 :
356 0 : nAttributeIndex ++;
357 0 : continue;
358 : }
359 :
360 : else
361 : {
362 4 : gmlType = GMLPT_Untyped;
363 4 : if ( ! LookForSimpleType(psSchemaNode, pszStrippedNSType,
364 : &gmlType, &nWidth, &nPrecision) )
365 : {
366 4 : bGotUnrecognizedType = TRUE;
367 : //CPLDebug("GML", "Unknown type (%s).", pszType);
368 : }
369 : else
370 : {
371 : /* Too complex schema for us. Aborts parsing */
372 0 : delete poClass;
373 0 : return NULL;
374 : }
375 : }
376 :
377 657 : if (pszElementName == NULL)
378 0 : pszElementName = "unnamed";
379 : GMLPropertyDefn *poProp = new GMLPropertyDefn(
380 657 : pszElementName, pszElementName );
381 :
382 657 : poProp->SetType( gmlType );
383 657 : poProp->SetAttributeIndex( nAttributeIndex );
384 657 : poProp->SetWidth( nWidth );
385 657 : poProp->SetPrecision( nPrecision );
386 :
387 657 : if (poClass->AddProperty( poProp ) < 0)
388 0 : delete poProp;
389 : else
390 657 : nAttributeIndex ++;
391 :
392 657 : continue;
393 : }
394 :
395 : // For now we skip geometries .. fixup later.
396 42 : CPLXMLNode* psSimpleType = CPLGetXMLNode( psAttrDef, "simpleType" );
397 42 : if( psSimpleType == NULL )
398 : {
399 18 : const char* pszRef = CPLGetXMLValue( psAttrDef, "ref", NULL );
400 :
401 : /* FME .xsd */
402 18 : if (pszRef != NULL && strncmp(pszRef, "gml:", 4) == 0)
403 : {
404 18 : const AssocNameType* psIter = apsRefTypes;
405 81 : while(psIter->pszName)
406 : {
407 63 : if (strncmp(pszRef + 4, psIter->pszName, strlen(psIter->pszName)) == 0)
408 : {
409 18 : if (poClass->GetGeometryAttributeIndex() != -1)
410 : {
411 9 : OGRwkbGeometryType eNewType = psIter->eType;
412 9 : OGRwkbGeometryType eOldType = (OGRwkbGeometryType)poClass->GetGeometryType();
413 18 : if ((eNewType == wkbMultiPoint && eOldType == wkbPoint) ||
414 : (eNewType == wkbMultiLineString && eOldType == wkbLineString) ||
415 : (eNewType == wkbMultiPolygon && eOldType == wkbPolygon))
416 : {
417 9 : poClass->SetGeometryType(eNewType);
418 : }
419 : else
420 : {
421 0 : CPLDebug("GML", "Geometry field already found ! Ignoring the following ones");
422 : }
423 : }
424 : else
425 : {
426 9 : poClass->SetGeometryElement(pszElementName);
427 9 : poClass->SetGeometryType(psIter->eType);
428 9 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
429 :
430 9 : nAttributeIndex ++;
431 : }
432 :
433 18 : break;
434 : }
435 :
436 45 : psIter ++;
437 : }
438 :
439 18 : if (psIter->pszName == NULL)
440 : {
441 : /* Can be a non geometry gml type */
442 : /* Too complex schema for us. Aborts parsing */
443 0 : delete poClass;
444 0 : return NULL;
445 : }
446 :
447 18 : if (poClass->GetGeometryAttributeIndex() == -1)
448 0 : bGotUnrecognizedType = TRUE;
449 :
450 18 : continue;
451 : }
452 : else
453 : {
454 : /* Too complex schema for us. Aborts parsing */
455 0 : delete poClass;
456 0 : return NULL;
457 : }
458 : }
459 :
460 24 : if (pszElementName == NULL)
461 0 : pszElementName = "unnamed";
462 : GMLPropertyDefn *poProp = new GMLPropertyDefn(
463 24 : pszElementName, pszElementName );
464 :
465 24 : GMLPropertyType eType = GMLPT_Untyped;
466 24 : int nWidth = 0, nPrecision = 0;
467 24 : GetSimpleTypeProperties(psSimpleType, &eType, &nWidth, &nPrecision);
468 24 : poProp->SetType( eType );
469 24 : poProp->SetWidth( nWidth );
470 24 : poProp->SetPrecision( nPrecision );
471 24 : poProp->SetAttributeIndex( nAttributeIndex );
472 :
473 24 : if (poClass->AddProperty( poProp ) < 0)
474 0 : delete poProp;
475 : else
476 24 : nAttributeIndex ++;
477 : }
478 :
479 : /* Only report wkbNone if we didn't find a known geometry type */
480 : /* and there were not any unknown types (in case this unknown type */
481 : /* would be a geometry type) */
482 118 : if (poClass->GetGeometryAttributeIndex() == -1 &&
483 : !bGotUnrecognizedType)
484 : {
485 1 : poClass->SetGeometryType(wkbNone);
486 : }
487 :
488 : /* -------------------------------------------------------------------- */
489 : /* Class complete, add to reader class list. */
490 : /* -------------------------------------------------------------------- */
491 118 : poClass->SetSchemaLocked( TRUE );
492 :
493 118 : return poClass;
494 : }
495 :
496 : /************************************************************************/
497 : /* GMLParseXSD() */
498 : /************************************************************************/
499 :
500 84 : int GMLParseXSD( const char *pszFile,
501 : std::vector<GMLFeatureClass*> & aosClasses)
502 :
503 : {
504 84 : if( pszFile == NULL )
505 0 : return FALSE;
506 :
507 : /* -------------------------------------------------------------------- */
508 : /* Load the raw XML file. */
509 : /* -------------------------------------------------------------------- */
510 84 : CPLXMLNode *psXSDTree = CPLParseXMLFile( pszFile );
511 :
512 84 : if( psXSDTree == NULL )
513 0 : return FALSE;
514 :
515 : /* -------------------------------------------------------------------- */
516 : /* Strip off any namespace qualifiers. */
517 : /* -------------------------------------------------------------------- */
518 84 : CPLStripXMLNamespace( psXSDTree, NULL, TRUE );
519 :
520 : /* -------------------------------------------------------------------- */
521 : /* Find <schema> root element. */
522 : /* -------------------------------------------------------------------- */
523 84 : CPLXMLNode *psSchemaNode = CPLGetXMLNode( psXSDTree, "=schema" );
524 84 : if( psSchemaNode == NULL )
525 : {
526 0 : CPLDestroyXMLNode( psXSDTree );
527 0 : return FALSE;
528 : }
529 :
530 : /* ==================================================================== */
531 : /* Process each feature class definition. */
532 : /* ==================================================================== */
533 : CPLXMLNode *psThis;
534 :
535 1224 : for( psThis = psSchemaNode->psChild;
536 : psThis != NULL; psThis = psThis->psNext )
537 : {
538 : /* -------------------------------------------------------------------- */
539 : /* Check for <xs:element> node. */
540 : /* -------------------------------------------------------------------- */
541 1140 : if( psThis->eType != CXT_Element
542 : || !EQUAL(psThis->pszValue,"element") )
543 1006 : continue;
544 :
545 : /* -------------------------------------------------------------------- */
546 : /* Check the substitution group. */
547 : /* -------------------------------------------------------------------- */
548 : const char *pszSubGroup =
549 134 : StripNS(CPLGetXMLValue(psThis,"substitutionGroup",""));
550 :
551 : // Old OGR produced elements for the feature collection.
552 134 : if( EQUAL(pszSubGroup, "_FeatureCollection") )
553 8 : continue;
554 :
555 126 : if( !EQUAL(pszSubGroup, "_Feature") &&
556 : !EQUAL(pszSubGroup, "AbstractFeature") /* AbstractFeature used by GML 3.2 */ )
557 : {
558 3 : continue;
559 : }
560 :
561 : /* -------------------------------------------------------------------- */
562 : /* Get name */
563 : /* -------------------------------------------------------------------- */
564 : const char *pszName;
565 :
566 123 : pszName = CPLGetXMLValue( psThis, "name", NULL );
567 123 : if( pszName == NULL )
568 : {
569 0 : continue;
570 : }
571 :
572 : /* -------------------------------------------------------------------- */
573 : /* Get type and verify relationship with name. */
574 : /* -------------------------------------------------------------------- */
575 : const char *pszType;
576 :
577 123 : pszType = CPLGetXMLValue( psThis, "type", NULL );
578 123 : if (pszType == NULL)
579 : {
580 38 : CPLXMLNode *psComplexType = CPLGetXMLNode( psThis, "complexType" );
581 38 : if (psComplexType)
582 : {
583 : GMLFeatureClass* poClass =
584 38 : GMLParseFeatureType(psSchemaNode, pszName, psComplexType);
585 38 : if (poClass)
586 38 : aosClasses.push_back(poClass);
587 : }
588 38 : continue;
589 : }
590 85 : if( strstr( pszType, ":" ) != NULL )
591 85 : pszType = strstr( pszType, ":" ) + 1;
592 85 : if( EQUAL(pszType, pszName) )
593 : {
594 : /* A few WFS servers return a type name which is the element name */
595 : /* without any _Type or Type suffix */
596 : /* e.g. : http://apollo.erdas.com/erdas-apollo/vector/Cherokee?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=iwfs:Air */
597 : }
598 68 : else if( !EQUALN(pszType,pszName,strlen(pszName))
599 : || !(EQUAL(pszType+strlen(pszName),"_Type") ||
600 : EQUAL(pszType+strlen(pszName),"Type")) )
601 : {
602 0 : continue;
603 : }
604 :
605 : /* CanVec .xsd contains weird types that are not used in the related GML */
606 85 : if (strncmp(pszName, "XyZz", 4) == 0 ||
607 : strncmp(pszName, "XyZ1", 4) == 0 ||
608 : strncmp(pszName, "XyZ2", 4) == 0)
609 3 : continue;
610 :
611 : GMLFeatureClass* poClass =
612 82 : GMLParseFeatureType(psSchemaNode, pszName, pszType);
613 82 : if (poClass)
614 80 : aosClasses.push_back(poClass);
615 : }
616 :
617 84 : CPLDestroyXMLNode( psXSDTree );
618 :
619 84 : if( aosClasses.size() > 0 )
620 : {
621 81 : return TRUE;
622 : }
623 : else
624 3 : return FALSE;
625 : }
|