1 : /******************************************************************************
2 : * $Id: ogrgmldatasource.cpp 24983 2012-09-26 19:48:37Z rouault $
3 : *
4 : * Project: OGR
5 : * Purpose: Implements OGRGMLDataSource class.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
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
22 : * OR 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 : * Contributor: Alessandro Furieri, a.furieri@lqt.it
31 : * Portions of this module implenting GML_SKIP_RESOLVE_ELEMS HUGE
32 : * Developed for Faunalia ( http://www.faunalia.it) with funding from
33 : * Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE
34 : *
35 : ****************************************************************************/
36 :
37 : #include "ogr_gml.h"
38 : #include "parsexsd.h"
39 : #include "cpl_conv.h"
40 : #include "cpl_string.h"
41 : #include "cpl_http.h"
42 : #include "gmlutils.h"
43 : #include "ogr_p.h"
44 :
45 : #include <vector>
46 :
47 : CPL_CVSID("$Id: ogrgmldatasource.cpp 24983 2012-09-26 19:48:37Z rouault $");
48 :
49 : /************************************************************************/
50 : /* ReplaceSpaceByPct20IfNeeded() */
51 : /************************************************************************/
52 :
53 46 : static CPLString ReplaceSpaceByPct20IfNeeded(const char* pszURL)
54 : {
55 : /* Replace ' ' by '%20' */
56 46 : CPLString osRet = pszURL;
57 46 : const char* pszNeedle = strstr(pszURL, "text/xml; subtype=");
58 46 : if (pszNeedle)
59 : {
60 2 : char* pszTmp = (char*)CPLMalloc(strlen(pszURL) + 2 +1);
61 2 : int nBeforeNeedle = (int)(pszNeedle - pszURL);
62 2 : memcpy(pszTmp, pszURL, nBeforeNeedle);
63 2 : strcpy(pszTmp + nBeforeNeedle, "text/xml;%20subtype=");
64 2 : strcpy(pszTmp + nBeforeNeedle + strlen("text/xml;%20subtype="), pszNeedle + strlen("text/xml; subtype="));
65 2 : osRet = pszTmp;
66 2 : CPLFree(pszTmp);
67 : }
68 0 : return osRet;
69 : }
70 :
71 : /************************************************************************/
72 : /* OGRGMLDataSource() */
73 : /************************************************************************/
74 :
75 542 : OGRGMLDataSource::OGRGMLDataSource()
76 :
77 : {
78 542 : pszName = NULL;
79 542 : papoLayers = NULL;
80 542 : nLayers = 0;
81 :
82 542 : poReader = NULL;
83 542 : fpOutput = NULL;
84 542 : bFpOutputIsNonSeekable = FALSE;
85 542 : bFpOutputSingleFile = FALSE;
86 542 : bIsOutputGML3 = FALSE;
87 542 : bIsOutputGML3Deegree = FALSE;
88 542 : bIsOutputGML32 = FALSE;
89 542 : bIsLongSRSRequired = FALSE;
90 542 : bWriteSpaceIndentation = TRUE;
91 :
92 542 : papszCreateOptions = NULL;
93 542 : bOutIsTempFile = FALSE;
94 :
95 542 : bExposeGMLId = FALSE;
96 542 : bExposeFid = FALSE;
97 542 : nSchemaInsertLocation = -1;
98 542 : nBoundedByLocation = -1;
99 542 : bBBOX3D = FALSE;
100 :
101 542 : poGlobalSRS = NULL;
102 542 : bIsWFS = FALSE;
103 :
104 542 : eReadMode = STANDARD;
105 542 : poStoredGMLFeature = NULL;
106 542 : poLastReadLayer = NULL;
107 :
108 542 : m_bInvertAxisOrderIfLatLong = FALSE;
109 542 : m_bConsiderEPSGAsURN = FALSE;
110 542 : m_bGetSecondaryGeometryOption = FALSE;
111 542 : }
112 :
113 : /************************************************************************/
114 : /* ~OGRGMLDataSource() */
115 : /************************************************************************/
116 :
117 542 : OGRGMLDataSource::~OGRGMLDataSource()
118 :
119 : {
120 :
121 542 : if( fpOutput != NULL )
122 : {
123 50 : const char* pszPrefix = GetAppPrefix();
124 :
125 50 : PrintLine( fpOutput, "</%s:FeatureCollection>", pszPrefix );
126 :
127 50 : if( bFpOutputIsNonSeekable)
128 : {
129 0 : VSIFCloseL( fpOutput );
130 0 : fpOutput = NULL;
131 : }
132 :
133 50 : InsertHeader();
134 :
135 50 : if( !bFpOutputIsNonSeekable
136 : && nBoundedByLocation != -1
137 : && VSIFSeekL( fpOutput, nBoundedByLocation, SEEK_SET ) == 0 )
138 : {
139 50 : if (sBoundingRect.IsInit() && IsGML3Output())
140 : {
141 25 : int bCoordSwap = FALSE;
142 : char* pszSRSName;
143 25 : if (poGlobalSRS)
144 25 : pszSRSName = GML_GetSRSName(poGlobalSRS, IsLongSRSRequired(), &bCoordSwap);
145 : else
146 0 : pszSRSName = CPLStrdup("");
147 : char szLowerCorner[75], szUpperCorner[75];
148 25 : if (bCoordSwap)
149 : {
150 24 : OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinY, sBoundingRect.MinX, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2);
151 24 : OGRMakeWktCoordinate(szUpperCorner, sBoundingRect.MaxY, sBoundingRect.MaxX, sBoundingRect.MaxZ, (bBBOX3D) ? 3 : 2);
152 : }
153 : else
154 : {
155 1 : OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinX, sBoundingRect.MinY, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2);
156 1 : OGRMakeWktCoordinate(szUpperCorner, sBoundingRect.MaxX, sBoundingRect.MaxY, sBoundingRect.MaxZ, (bBBOX3D) ? 3 : 2);
157 : }
158 25 : if (bWriteSpaceIndentation)
159 25 : VSIFPrintfL( fpOutput, " ");
160 : PrintLine( fpOutput, "<gml:boundedBy><gml:Envelope%s%s><gml:lowerCorner>%s</gml:lowerCorner><gml:upperCorner>%s</gml:upperCorner></gml:Envelope></gml:boundedBy>",
161 25 : (bBBOX3D) ? " srsDimension=\"3\"" : "", pszSRSName, szLowerCorner, szUpperCorner);
162 25 : CPLFree(pszSRSName);
163 : }
164 25 : else if (sBoundingRect.IsInit())
165 : {
166 16 : if (bWriteSpaceIndentation)
167 16 : VSIFPrintfL( fpOutput, " ");
168 16 : PrintLine( fpOutput, "<gml:boundedBy>" );
169 16 : if (bWriteSpaceIndentation)
170 16 : VSIFPrintfL( fpOutput, " ");
171 16 : PrintLine( fpOutput, "<gml:Box>" );
172 16 : if (bWriteSpaceIndentation)
173 16 : VSIFPrintfL( fpOutput, " ");
174 : VSIFPrintfL( fpOutput,
175 : "<gml:coord><gml:X>%.16g</gml:X>"
176 : "<gml:Y>%.16g</gml:Y>",
177 16 : sBoundingRect.MinX, sBoundingRect.MinY );
178 16 : if (bBBOX3D)
179 : VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
180 1 : sBoundingRect.MinZ );
181 16 : PrintLine( fpOutput, "</gml:coord>");
182 16 : if (bWriteSpaceIndentation)
183 16 : VSIFPrintfL( fpOutput, " ");
184 : VSIFPrintfL( fpOutput,
185 : "<gml:coord><gml:X>%.16g</gml:X>"
186 : "<gml:Y>%.16g</gml:Y>",
187 16 : sBoundingRect.MaxX, sBoundingRect.MaxY );
188 16 : if (bBBOX3D)
189 : VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
190 1 : sBoundingRect.MaxZ );
191 16 : PrintLine( fpOutput, "</gml:coord>");
192 16 : if (bWriteSpaceIndentation)
193 16 : VSIFPrintfL( fpOutput, " ");
194 16 : PrintLine( fpOutput, "</gml:Box>" );
195 16 : if (bWriteSpaceIndentation)
196 16 : VSIFPrintfL( fpOutput, " ");
197 16 : PrintLine( fpOutput, "</gml:boundedBy>" );
198 : }
199 : else
200 : {
201 9 : if (bWriteSpaceIndentation)
202 9 : VSIFPrintfL( fpOutput, " ");
203 9 : if (IsGML3Output())
204 3 : PrintLine( fpOutput, "<gml:boundedBy><gml:Null /></gml:boundedBy>" );
205 : else
206 6 : PrintLine( fpOutput, "<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>" );
207 : }
208 : }
209 :
210 50 : if (fpOutput)
211 50 : VSIFCloseL( fpOutput );
212 : }
213 :
214 542 : CSLDestroy( papszCreateOptions );
215 542 : CPLFree( pszName );
216 :
217 762 : for( int i = 0; i < nLayers; i++ )
218 220 : delete papoLayers[i];
219 :
220 542 : CPLFree( papoLayers );
221 :
222 542 : if( poReader )
223 : {
224 145 : if (bOutIsTempFile)
225 0 : VSIUnlink(poReader->GetSourceFileName());
226 145 : delete poReader;
227 : }
228 :
229 542 : delete poGlobalSRS;
230 :
231 542 : delete poStoredGMLFeature;
232 :
233 542 : if (osXSDFilename.compare(CPLSPrintf("/vsimem/tmp_gml_xsd_%p.xsd", this)) == 0)
234 0 : VSIUnlink(osXSDFilename);
235 542 : }
236 :
237 : /************************************************************************/
238 : /* Open() */
239 : /************************************************************************/
240 :
241 492 : int OGRGMLDataSource::Open( const char * pszNameIn, int bTestOpen )
242 :
243 : {
244 : VSILFILE *fp;
245 : char szHeader[2048];
246 492 : int nNumberOfFeatures = 0;
247 492 : CPLString osWithVsiGzip;
248 492 : const char *pszSchemaLocation = NULL;
249 492 : int bCheckAuxFile = TRUE;
250 :
251 : /* -------------------------------------------------------------------- */
252 : /* Extract xsd filename from connexion string if present. */
253 : /* -------------------------------------------------------------------- */
254 492 : osFilename = pszNameIn;
255 492 : const char *pszXSDFilenameTmp = strstr(pszNameIn, ",xsd=");
256 492 : if (pszXSDFilenameTmp != NULL)
257 : {
258 39 : osFilename.resize(pszXSDFilenameTmp - pszNameIn);
259 39 : osXSDFilename = pszXSDFilenameTmp + strlen(",xsd=");
260 : }
261 492 : const char *pszFilename = osFilename.c_str();
262 :
263 492 : pszName = CPLStrdup( pszNameIn );
264 :
265 : /* -------------------------------------------------------------------- */
266 : /* Open the source file. */
267 : /* -------------------------------------------------------------------- */
268 492 : fp = VSIFOpenL( pszFilename, "r" );
269 492 : if( fp == NULL )
270 : {
271 95 : if( !bTestOpen )
272 : CPLError( CE_Failure, CPLE_OpenFailed,
273 : "Failed to open GML file `%s'.",
274 0 : pszFilename );
275 :
276 95 : return FALSE;
277 : }
278 :
279 397 : int bExpatCompatibleEncoding = FALSE;
280 397 : int bHas3D = FALSE;
281 397 : int bHintConsiderEPSGAsURN = FALSE;
282 :
283 : /* -------------------------------------------------------------------- */
284 : /* If we aren't sure it is GML, load a header chunk and check */
285 : /* for signs it is GML */
286 : /* -------------------------------------------------------------------- */
287 397 : if( bTestOpen )
288 : {
289 397 : size_t nRead = VSIFReadL( szHeader, 1, sizeof(szHeader)-1, fp );
290 397 : if (nRead <= 0)
291 : {
292 11 : VSIFCloseL( fp );
293 11 : return FALSE;
294 : }
295 386 : szHeader[nRead] = '\0';
296 :
297 : /* Might be a OS-Mastermap gzipped GML, so let be nice and try to open */
298 : /* it transparently with /vsigzip/ */
299 386 : if ( ((GByte*)szHeader)[0] == 0x1f && ((GByte*)szHeader)[1] == 0x8b &&
300 : EQUAL(CPLGetExtension(pszFilename), "gz") &&
301 : strncmp(pszFilename, "/vsigzip/", strlen("/vsigzip/")) != 0 )
302 : {
303 0 : VSIFCloseL( fp );
304 0 : osWithVsiGzip = "/vsigzip/";
305 0 : osWithVsiGzip += pszFilename;
306 :
307 0 : pszFilename = osWithVsiGzip;
308 :
309 0 : fp = VSIFOpenL( pszFilename, "r" );
310 0 : if( fp == NULL )
311 0 : return FALSE;
312 :
313 0 : nRead = VSIFReadL( szHeader, 1, sizeof(szHeader) - 1, fp );
314 0 : if (nRead <= 0)
315 : {
316 0 : VSIFCloseL( fp );
317 0 : return FALSE;
318 : }
319 0 : szHeader[nRead] = '\0';
320 : }
321 :
322 : /* -------------------------------------------------------------------- */
323 : /* Check for a UTF-8 BOM and skip if found */
324 : /* */
325 : /* TODO: BOM is variable-lenght parameter and depends on encoding. */
326 : /* Add BOM detection for other encodings. */
327 : /* -------------------------------------------------------------------- */
328 :
329 : // Used to skip to actual beginning of XML data
330 386 : char* szPtr = szHeader;
331 :
332 388 : if( ( (unsigned char)szHeader[0] == 0xEF )
333 1 : && ( (unsigned char)szHeader[1] == 0xBB )
334 1 : && ( (unsigned char)szHeader[2] == 0xBF) )
335 : {
336 1 : szPtr += 3;
337 : }
338 :
339 386 : const char* pszEncoding = strstr(szPtr, "encoding=");
340 386 : if (pszEncoding)
341 320 : bExpatCompatibleEncoding = (pszEncoding[9] == '\'' || pszEncoding[9] == '"') &&
342 : (EQUALN(pszEncoding + 10, "UTF-8", 5) ||
343 : EQUALN(pszEncoding + 10, "ISO-8859-15", 11) ||
344 : (EQUALN(pszEncoding + 10, "ISO-8859-1", 10) &&
345 484 : pszEncoding[20] == pszEncoding[9])) ;
346 : else
347 222 : bExpatCompatibleEncoding = TRUE; /* utf-8 is the default */
348 :
349 386 : bHas3D = strstr(szPtr, "srsDimension=\"3\"") != NULL || strstr(szPtr, "<gml:Z>") != NULL;
350 :
351 : /* -------------------------------------------------------------------- */
352 : /* Here, we expect the opening chevrons of GML tree root element */
353 : /* -------------------------------------------------------------------- */
354 386 : if( szPtr[0] != '<'
355 : || strstr(szPtr,"opengis.net/gml") == NULL )
356 : {
357 237 : VSIFCloseL( fp );
358 237 : return FALSE;
359 : }
360 :
361 : /* Ignore GeoRSS documents. They will be recognized by the GeoRSS driver */
362 149 : if( strstr(szPtr, "<rss") != NULL && strstr(szPtr, "xmlns:georss") != NULL )
363 : {
364 4 : VSIFCloseL( fp );
365 4 : return FALSE;
366 : }
367 :
368 : /* Small optimization: if we parse a <wfs:FeatureCollection> and */
369 : /* that numberOfFeatures is set, we can use it to set the FeatureCount */
370 : /* but *ONLY* if there's just one class ! */
371 145 : const char* pszFeatureCollection = strstr(szPtr, "wfs:FeatureCollection");
372 145 : if (pszFeatureCollection == NULL)
373 97 : pszFeatureCollection = strstr(szPtr, "gml:FeatureCollection"); /* GML 3.2.1 output */
374 145 : if (pszFeatureCollection == NULL)
375 : {
376 87 : pszFeatureCollection = strstr(szPtr, "<FeatureCollection"); /* Deegree WFS 1.0.0 output */
377 87 : if (pszFeatureCollection && strstr(szPtr, "xmlns:wfs=\"http://www.opengis.net/wfs\"") == NULL)
378 1 : pszFeatureCollection = NULL;
379 : }
380 145 : if (pszFeatureCollection)
381 : {
382 60 : bExposeGMLId = TRUE;
383 60 : bIsWFS = TRUE;
384 60 : const char* pszNumberOfFeatures = strstr(szPtr, "numberOfFeatures=");
385 60 : if (pszNumberOfFeatures)
386 : {
387 36 : pszNumberOfFeatures += 17;
388 36 : char ch = pszNumberOfFeatures[0];
389 36 : if ((ch == '\'' || ch == '"') && strchr(pszNumberOfFeatures + 1, ch) != NULL)
390 : {
391 36 : nNumberOfFeatures = atoi(pszNumberOfFeatures + 1);
392 : }
393 : }
394 24 : else if ((pszNumberOfFeatures = strstr(szPtr, "numberReturned=")) != NULL) /* WFS 2.0.0 */
395 : {
396 7 : pszNumberOfFeatures += 15;
397 7 : char ch = pszNumberOfFeatures[0];
398 7 : if ((ch == '\'' || ch == '"') && strchr(pszNumberOfFeatures + 1, ch) != NULL)
399 : {
400 : /* 'unknown' might be a valid value in a corrected version of WFS 2.0 */
401 : /* but it will also evaluate to 0, that is considered as unknown, so nothing */
402 : /* particular to do */
403 7 : nNumberOfFeatures = atoi(pszNumberOfFeatures + 1);
404 : }
405 : }
406 : }
407 85 : else if (strncmp(pszFilename, "/vsimem/tempwfs_", strlen("/vsimem/tempwfs_")) == 0)
408 : {
409 : /* http://regis.intergraph.com/wfs/dcmetro/request.asp? returns a <G:FeatureCollection> */
410 : /* Who knows what servers can return ? Ok, so when in the context of the WFS driver */
411 : /* always expose the gml:id to avoid later crashes */
412 0 : bExposeGMLId = TRUE;
413 0 : bIsWFS = TRUE;
414 : }
415 : else
416 : {
417 : bExposeGMLId = strstr(szPtr, " gml:id=\"") != NULL ||
418 85 : strstr(szPtr, " gml:id='") != NULL;
419 : bExposeFid = strstr(szPtr, " fid=\"") != NULL ||
420 85 : strstr(szPtr, " fid='") != NULL;
421 :
422 85 : const char* pszExposeGMLId = CPLGetConfigOption("GML_EXPOSE_GML_ID", NULL);
423 85 : if (pszExposeGMLId)
424 0 : bExposeGMLId = CSLTestBoolean(pszExposeGMLId);
425 :
426 85 : const char* pszExposeFid = CPLGetConfigOption("GML_EXPOSE_FID", NULL);
427 85 : if (pszExposeFid)
428 1 : bExposeFid = CSLTestBoolean(pszExposeFid);
429 : }
430 :
431 145 : bHintConsiderEPSGAsURN = strstr(szPtr, "xmlns:fme=\"http://www.safe.com/gml/fme\"") != NULL;
432 :
433 145 : pszSchemaLocation = strstr(szPtr, "schemaLocation=");
434 145 : if (pszSchemaLocation)
435 121 : pszSchemaLocation += strlen("schemaLocation=");
436 :
437 145 : if (bIsWFS && EQUALN(pszFilename, "/vsicurl_streaming/", strlen("/vsicurl_streaming/")))
438 36 : bCheckAuxFile = FALSE;
439 : }
440 :
441 : /* -------------------------------------------------------------------- */
442 : /* We assume now that it is GML. Instantiate a GMLReader on it. */
443 : /* -------------------------------------------------------------------- */
444 :
445 145 : const char* pszReadMode = CPLGetConfigOption("GML_READ_MODE", NULL);
446 287 : if (pszReadMode == NULL || EQUAL(pszReadMode, "STANDARD"))
447 142 : eReadMode = STANDARD;
448 3 : else if (EQUAL(pszReadMode, "SEQUENTIAL_LAYERS"))
449 2 : eReadMode = SEQUENTIAL_LAYERS;
450 1 : else if (EQUAL(pszReadMode, "INTERLEAVED_LAYERS"))
451 1 : eReadMode = INTERLEAVED_LAYERS;
452 : else
453 : {
454 0 : CPLDebug("GML", "Unrecognized value for GML_READ_MODE configuration option.");
455 : }
456 :
457 : m_bInvertAxisOrderIfLatLong = CSLTestBoolean(
458 145 : CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES"));
459 :
460 : const char* pszConsiderEPSGAsURN =
461 145 : CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", NULL);
462 145 : if (pszConsiderEPSGAsURN != NULL)
463 0 : m_bConsiderEPSGAsURN = CSLTestBoolean(pszConsiderEPSGAsURN);
464 145 : else if (bHintConsiderEPSGAsURN)
465 : {
466 : /* GML produced by FME (at least CanVec GML) seem to honour EPSG axis ordering */
467 5 : CPLDebug("GML", "FME-produced GML --> consider that GML_CONSIDER_EPSG_AS_URN is set to YES");
468 5 : m_bConsiderEPSGAsURN = TRUE;
469 : }
470 : else
471 140 : m_bConsiderEPSGAsURN = FALSE;
472 :
473 145 : m_bGetSecondaryGeometryOption = CSLTestBoolean(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO"));
474 :
475 : /* EXPAT is faster than Xerces, so when it is safe to use it, use it ! */
476 : /* The only interest of Xerces is for rare encodings that Expat doesn't handle */
477 : /* but UTF-8 is well handled by Expat */
478 145 : int bUseExpatParserPreferably = bExpatCompatibleEncoding;
479 :
480 : /* Override default choice */
481 145 : const char* pszGMLParser = CPLGetConfigOption("GML_PARSER", NULL);
482 145 : if (pszGMLParser)
483 : {
484 0 : if (EQUAL(pszGMLParser, "EXPAT"))
485 0 : bUseExpatParserPreferably = TRUE;
486 0 : else if (EQUAL(pszGMLParser, "XERCES"))
487 0 : bUseExpatParserPreferably = FALSE;
488 : }
489 :
490 : poReader = CreateGMLReader( bUseExpatParserPreferably,
491 : m_bInvertAxisOrderIfLatLong,
492 : m_bConsiderEPSGAsURN,
493 145 : m_bGetSecondaryGeometryOption );
494 145 : if( poReader == NULL )
495 : {
496 : CPLError( CE_Failure, CPLE_AppDefined,
497 : "File %s appears to be GML but the GML reader can't\n"
498 : "be instantiated, likely because Xerces or Expat support wasn't\n"
499 : "configured in.",
500 0 : pszFilename );
501 0 : VSIFCloseL( fp );
502 0 : return FALSE;
503 : }
504 :
505 145 : poReader->SetSourceFile( pszFilename );
506 :
507 : /* -------------------------------------------------------------------- */
508 : /* Find <gml:boundedBy> */
509 : /* -------------------------------------------------------------------- */
510 :
511 145 : FindAndParseBoundedBy(fp);
512 :
513 : /* -------------------------------------------------------------------- */
514 : /* Resolve the xlinks in the source file and save it with the */
515 : /* extension ".resolved.gml". The source file will to set to that. */
516 : /* -------------------------------------------------------------------- */
517 :
518 145 : char *pszXlinkResolvedFilename = NULL;
519 145 : const char *pszOption = CPLGetConfigOption("GML_SAVE_RESOLVED_TO", NULL);
520 145 : int bResolve = TRUE;
521 145 : int bHugeFile = FALSE;
522 145 : if( pszOption != NULL && EQUALN( pszOption, "SAME", 4 ) )
523 : {
524 : // "SAME" will overwrite the existing gml file
525 0 : pszXlinkResolvedFilename = CPLStrdup( pszFilename );
526 : }
527 145 : else if( pszOption != NULL &&
528 : CPLStrnlen( pszOption, 5 ) >= 5 &&
529 : EQUALN( pszOption - 4 + strlen( pszOption ), ".gml", 4 ) )
530 : {
531 : // Any string ending with ".gml" will try and write to it
532 2 : pszXlinkResolvedFilename = CPLStrdup( pszOption );
533 : }
534 : else
535 : {
536 : // When no option is given or is not recognised,
537 : // use the same file name with the extension changed to .resolved.gml
538 : pszXlinkResolvedFilename = CPLStrdup(
539 143 : CPLResetExtension( pszFilename, "resolved.gml" ) );
540 :
541 : // Check if the file already exists.
542 : VSIStatBufL sResStatBuf, sGMLStatBuf;
543 143 : if( bCheckAuxFile && VSIStatL( pszXlinkResolvedFilename, &sResStatBuf ) == 0 )
544 : {
545 3 : VSIStatL( pszFilename, &sGMLStatBuf );
546 3 : if( sGMLStatBuf.st_mtime > sResStatBuf.st_mtime )
547 : {
548 : CPLDebug( "GML",
549 : "Found %s but ignoring because it appears\n"
550 : "be older than the associated GML file.",
551 0 : pszXlinkResolvedFilename );
552 : }
553 : else
554 : {
555 3 : poReader->SetSourceFile( pszXlinkResolvedFilename );
556 3 : bResolve = FALSE;
557 : }
558 : }
559 : }
560 :
561 : const char *pszSkipOption = CPLGetConfigOption( "GML_SKIP_RESOLVE_ELEMS",
562 145 : "ALL");
563 145 : char **papszSkip = NULL;
564 145 : if( EQUAL( pszSkipOption, "ALL" ) )
565 138 : bResolve = FALSE;
566 7 : else if( EQUAL( pszSkipOption, "HUGE" ) )//exactly as NONE, but intended for HUGE files
567 2 : bHugeFile = TRUE;
568 5 : else if( !EQUAL( pszSkipOption, "NONE" ) )//use this to resolve everything
569 : papszSkip = CSLTokenizeString2( pszSkipOption, ",",
570 : CSLT_STRIPLEADSPACES |
571 1 : CSLT_STRIPENDSPACES );
572 145 : int bHaveSchema = FALSE;
573 145 : int bSchemaDone = FALSE;
574 :
575 : /* -------------------------------------------------------------------- */
576 : /* Is some GML Feature Schema (.gfs) TEMPLATE required ? */
577 : /* -------------------------------------------------------------------- */
578 : const char *pszGFSTemplateName =
579 145 : CPLGetConfigOption( "GML_GFS_TEMPLATE", NULL);
580 145 : if( pszGFSTemplateName != NULL )
581 : {
582 : /* attempting to load the GFS TEMPLATE */
583 0 : bHaveSchema = poReader->LoadClasses( pszGFSTemplateName );
584 : }
585 :
586 145 : if( bResolve )
587 : {
588 7 : if ( bHugeFile )
589 : {
590 2 : bSchemaDone = TRUE;
591 : int bSqliteIsTempFile =
592 2 : CSLTestBoolean(CPLGetConfigOption( "GML_HUGE_TEMPFILE", "YES"));
593 2 : int iSqliteCacheMB = atoi(CPLGetConfigOption( "OGR_SQLITE_CACHE", "0"));
594 4 : if( poReader->HugeFileResolver( pszXlinkResolvedFilename,
595 : bSqliteIsTempFile,
596 2 : iSqliteCacheMB ) == FALSE )
597 : {
598 : // we assume an errors have been reported.
599 0 : VSIFCloseL(fp);
600 0 : return FALSE;
601 : }
602 : }
603 : else
604 : {
605 : poReader->ResolveXlinks( pszXlinkResolvedFilename,
606 : &bOutIsTempFile,
607 5 : papszSkip );
608 : }
609 : }
610 :
611 145 : CPLFree( pszXlinkResolvedFilename );
612 145 : pszXlinkResolvedFilename = NULL;
613 145 : CSLDestroy( papszSkip );
614 145 : papszSkip = NULL;
615 :
616 : /* If the source filename for the reader is still the GML filename, then */
617 : /* we can directly provide the file pointer. Otherwise we close it */
618 145 : if (strcmp(poReader->GetSourceFileName(), pszFilename) == 0)
619 135 : poReader->SetFP(fp);
620 : else
621 10 : VSIFCloseL(fp);
622 145 : fp = NULL;
623 :
624 : /* Is a prescan required ? */
625 145 : if( bHaveSchema && !bSchemaDone )
626 : {
627 : /* We must detect which layers are actually present in the .gml */
628 : /* and how many features they have */
629 0 : if( !poReader->PrescanForTemplate() )
630 : {
631 : // we assume an errors have been reported.
632 0 : return FALSE;
633 : }
634 : }
635 :
636 145 : CPLString osGFSFilename = CPLResetExtension( pszFilename, "gfs" );
637 145 : if (strncmp(osGFSFilename, "/vsigzip/", strlen("/vsigzip/")) == 0)
638 0 : osGFSFilename = osGFSFilename.substr(strlen("/vsigzip/"));
639 :
640 : /* -------------------------------------------------------------------- */
641 : /* Can we find a GML Feature Schema (.gfs) for the input file? */
642 : /* -------------------------------------------------------------------- */
643 145 : if( !bHaveSchema && osXSDFilename.size() == 0)
644 : {
645 : VSIStatBufL sGFSStatBuf;
646 108 : if( bCheckAuxFile && VSIStatL( osGFSFilename, &sGFSStatBuf ) == 0 )
647 : {
648 : VSIStatBufL sGMLStatBuf;
649 19 : VSIStatL( pszFilename, &sGMLStatBuf );
650 19 : if( sGMLStatBuf.st_mtime > sGFSStatBuf.st_mtime )
651 : {
652 : CPLDebug( "GML",
653 : "Found %s but ignoring because it appears\n"
654 : "be older than the associated GML file.",
655 0 : osGFSFilename.c_str() );
656 : }
657 : else
658 : {
659 19 : bHaveSchema = poReader->LoadClasses( osGFSFilename );
660 19 : if (bHaveSchema)
661 : {
662 : const char *pszXSDFilenameTmp;
663 19 : pszXSDFilenameTmp = CPLResetExtension( pszFilename, "xsd" );
664 19 : if( VSIStatExL( pszXSDFilenameTmp, &sGMLStatBuf,
665 : VSI_STAT_EXISTS_FLAG ) == 0 )
666 : {
667 : CPLDebug("GML", "Using %s file, ignoring %s",
668 0 : osGFSFilename.c_str(), pszXSDFilenameTmp);
669 : }
670 : }
671 : }
672 : }
673 : }
674 :
675 : /* -------------------------------------------------------------------- */
676 : /* Can we find an xsd which might conform to tbe GML3 Level 0 */
677 : /* profile? We really ought to look for it based on the rules */
678 : /* schemaLocation in the GML feature collection but for now we */
679 : /* just hopes it is in the same director with the same name. */
680 : /* -------------------------------------------------------------------- */
681 145 : int bHasFoundXSD = FALSE;
682 :
683 145 : if( !bHaveSchema )
684 : {
685 126 : char** papszTypeNames = NULL;
686 :
687 : VSIStatBufL sXSDStatBuf;
688 126 : if (osXSDFilename.size() == 0)
689 : {
690 89 : osXSDFilename = CPLResetExtension( pszFilename, "xsd" );
691 89 : if( bCheckAuxFile && VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
692 : {
693 61 : bHasFoundXSD = TRUE;
694 : }
695 : }
696 : else
697 : {
698 37 : if ( VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
699 : {
700 37 : bHasFoundXSD = TRUE;
701 : }
702 : }
703 :
704 : /* For WFS, try to fetch the application schema */
705 264 : if( bIsWFS && pszSchemaLocation != NULL &&
706 92 : (pszSchemaLocation[0] == '\'' || pszSchemaLocation[0] == '"') &&
707 46 : strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) != NULL )
708 : {
709 46 : char* pszSchemaLocationTmp1 = CPLStrdup(pszSchemaLocation + 1);
710 46 : int nTruncLen = (int)(strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) - (pszSchemaLocation + 1));
711 46 : pszSchemaLocationTmp1[nTruncLen] = '\0';
712 : char* pszSchemaLocationTmp2 = CPLUnescapeString(
713 46 : pszSchemaLocationTmp1, NULL, CPLES_XML);
714 46 : CPLString osEscaped = ReplaceSpaceByPct20IfNeeded(pszSchemaLocationTmp2);
715 46 : CPLFree(pszSchemaLocationTmp2);
716 46 : pszSchemaLocationTmp2 = CPLStrdup(osEscaped);
717 46 : if (pszSchemaLocationTmp2)
718 : {
719 : /* pszSchemaLocationTmp2 is of the form : */
720 : /* http://namespace1 http://namespace1_schema_location http://namespace2 http://namespace1_schema_location2 */
721 : /* So we try to find http://namespace1_schema_location that contains hints that it is the WFS application */
722 : /* schema, i.e. if it contains typename= and request=DescribeFeatureType */
723 46 : char** papszTokens = CSLTokenizeString2(pszSchemaLocationTmp2, " \r\n", 0);
724 46 : int nTokens = CSLCount(papszTokens);
725 46 : if ((nTokens % 2) == 0)
726 : {
727 136 : for(int i=0;i<nTokens;i+=2)
728 : {
729 90 : char* pszLocation = CPLUnescapeString(papszTokens[i+1], NULL, CPLES_URL);
730 90 : CPLString osLocation = pszLocation;
731 90 : CPLFree(pszLocation);
732 90 : if (osLocation.ifind("typename=") != std::string::npos &&
733 : osLocation.ifind("request=DescribeFeatureType") != std::string::npos)
734 : {
735 41 : CPLString osTypeName = CPLURLGetValue(osLocation, "typename");
736 41 : papszTypeNames = CSLTokenizeString2( osTypeName, ",", 0);
737 :
738 41 : if (!bHasFoundXSD && CPLHTTPEnabled() &&
739 : CSLTestBoolean(CPLGetConfigOption("GML_DOWNLOAD_WFS_SCHEMA", "YES")))
740 : {
741 0 : CPLString osEscaped = ReplaceSpaceByPct20IfNeeded(osLocation);
742 0 : CPLHTTPResult* psResult = CPLHTTPFetch(osEscaped, NULL);
743 0 : if (psResult)
744 : {
745 0 : if (psResult->nStatus == 0 && psResult->pabyData != NULL)
746 : {
747 0 : bHasFoundXSD = TRUE;
748 : osXSDFilename =
749 0 : CPLSPrintf("/vsimem/tmp_gml_xsd_%p.xsd", this);
750 : VSILFILE* fpMem = VSIFileFromMemBuffer(
751 : osXSDFilename, psResult->pabyData,
752 0 : psResult->nDataLen, TRUE);
753 0 : VSIFCloseL(fpMem);
754 0 : psResult->pabyData = NULL;
755 : }
756 0 : CPLHTTPDestroyResult(psResult);
757 0 : }
758 41 : }
759 : }
760 : }
761 : }
762 46 : CSLDestroy(papszTokens);
763 : }
764 46 : CPLFree(pszSchemaLocationTmp2);
765 46 : CPLFree(pszSchemaLocationTmp1);
766 : }
767 :
768 126 : if( bHasFoundXSD )
769 : {
770 98 : std::vector<GMLFeatureClass*> aosClasses;
771 98 : bHaveSchema = GMLParseXSD( osXSDFilename, aosClasses );
772 98 : if (bHaveSchema)
773 : {
774 98 : CPLDebug("GML", "Using %s", osXSDFilename.c_str());
775 :
776 98 : std::vector<GMLFeatureClass*>::const_iterator iter = aosClasses.begin();
777 98 : std::vector<GMLFeatureClass*>::const_iterator eiter = aosClasses.end();
778 317 : while (iter != eiter)
779 : {
780 121 : GMLFeatureClass* poClass = *iter;
781 121 : iter ++;
782 :
783 : /* We have no way of knowing if the geometry type is 25D */
784 : /* when examining the xsd only, so if there was a hint */
785 : /* it is, we force to 25D */
786 121 : if (bHas3D)
787 : {
788 : poClass->SetGeometryType(
789 2 : poClass->GetGeometryType() | wkb25DBit);
790 : }
791 :
792 121 : int bAddClass = TRUE;
793 : /* If typenames are declared, only register the matching classes, in case */
794 : /* the XSD contains more layers */
795 121 : if (papszTypeNames != NULL)
796 : {
797 58 : bAddClass = FALSE;
798 58 : char** papszIter = papszTypeNames;
799 174 : while (*papszIter && !bAddClass)
800 : {
801 58 : const char* pszTypeName = *papszIter;
802 58 : if (strcmp(pszTypeName, poClass->GetName()) == 0)
803 2 : bAddClass = TRUE;
804 58 : papszIter ++;
805 : }
806 :
807 : /* Retry by removing prefixes */
808 58 : if (!bAddClass)
809 : {
810 56 : papszIter = papszTypeNames;
811 168 : while (*papszIter && !bAddClass)
812 : {
813 56 : const char* pszTypeName = *papszIter;
814 56 : const char* pszColon = strchr(pszTypeName, ':');
815 56 : if (pszColon)
816 : {
817 56 : pszTypeName = pszColon + 1;
818 56 : if (strcmp(pszTypeName, poClass->GetName()) == 0)
819 : {
820 39 : poClass->SetName(pszTypeName);
821 39 : bAddClass = TRUE;
822 : }
823 : }
824 56 : papszIter ++;
825 : }
826 : }
827 :
828 : }
829 :
830 121 : if (bAddClass)
831 104 : poReader->AddClass( poClass );
832 : else
833 17 : delete poClass;
834 : }
835 98 : poReader->SetClassListLocked( TRUE );
836 98 : }
837 : }
838 :
839 126 : if (bHaveSchema && bIsWFS)
840 : {
841 : /* For WFS, we can assume sequential layers */
842 48 : if (poReader->GetClassCount() > 1 && pszReadMode == NULL)
843 : {
844 1 : CPLDebug("GML", "WFS output. Using SEQUENTIAL_LAYERS read mode");
845 1 : eReadMode = SEQUENTIAL_LAYERS;
846 : }
847 : /* Sometimes the returned schema contains only <xs:include> that we don't resolve */
848 : /* so ignore it */
849 47 : else if (poReader->GetClassCount() == 0)
850 0 : bHaveSchema = FALSE;
851 : }
852 :
853 126 : CSLDestroy(papszTypeNames);
854 : }
855 :
856 : /* -------------------------------------------------------------------- */
857 : /* Force a first pass to establish the schema. Eventually we */
858 : /* will have mechanisms for remembering the schema and related */
859 : /* information. */
860 : /* -------------------------------------------------------------------- */
861 145 : if( !bHaveSchema )
862 : {
863 28 : if( !poReader->PrescanForSchema( TRUE ) )
864 : {
865 : // we assume an errors have been reported.
866 3 : return FALSE;
867 : }
868 :
869 25 : if( bHasFoundXSD )
870 : {
871 : CPLDebug("GML", "Generating %s file, ignoring %s",
872 0 : osGFSFilename.c_str(), osXSDFilename.c_str());
873 : }
874 : }
875 :
876 142 : if (poReader->GetClassCount() > 1 && poReader->IsSequentialLayers() &&
877 : pszReadMode == NULL)
878 : {
879 11 : CPLDebug("GML", "Layers are monoblock. Using SEQUENTIAL_LAYERS read mode");
880 11 : eReadMode = SEQUENTIAL_LAYERS;
881 : }
882 :
883 : /* -------------------------------------------------------------------- */
884 : /* Save the schema file if possible. Don't make a fuss if we */
885 : /* can't ... could be read-only directory or something. */
886 : /* -------------------------------------------------------------------- */
887 142 : if( !bHaveSchema && !poReader->HasStoppedParsing() &&
888 : !EQUALN(pszFilename, "/vsitar/", strlen("/vsitar/")) &&
889 : !EQUALN(pszFilename, "/vsizip/", strlen("/vsizip/")) &&
890 : !EQUALN(pszFilename, "/vsigzip/vsi", strlen("/vsigzip/vsi")) &&
891 : !EQUALN(pszFilename, "/vsigzip//vsi", strlen("/vsigzip//vsi")) &&
892 : !EQUALN(pszFilename, "/vsicurl/", strlen("/vsicurl/")) &&
893 : !EQUALN(pszFilename, "/vsicurl_streaming/", strlen("/vsicurl_streaming/")))
894 : {
895 25 : VSILFILE *fp = NULL;
896 :
897 : VSIStatBufL sGFSStatBuf;
898 25 : if( VSIStatExL( osGFSFilename, &sGFSStatBuf, VSI_STAT_EXISTS_FLAG ) != 0
899 : && (fp = VSIFOpenL( osGFSFilename, "wt" )) != NULL )
900 : {
901 25 : VSIFCloseL( fp );
902 25 : poReader->SaveClasses( osGFSFilename );
903 : }
904 : else
905 : {
906 : CPLDebug("GML",
907 : "Not saving %s files already exists or can't be created.",
908 0 : osGFSFilename.c_str() );
909 : }
910 : }
911 :
912 : /* -------------------------------------------------------------------- */
913 : /* Translate the GMLFeatureClasses into layers. */
914 : /* -------------------------------------------------------------------- */
915 : papoLayers = (OGRGMLLayer **)
916 142 : CPLCalloc( sizeof(OGRGMLLayer *), poReader->GetClassCount());
917 142 : nLayers = 0;
918 :
919 142 : if (poReader->GetClassCount() == 1 && nNumberOfFeatures != 0)
920 : {
921 36 : GMLFeatureClass *poClass = poReader->GetClass(0);
922 36 : int nFeatureCount = poClass->GetFeatureCount();
923 36 : if (nFeatureCount < 0)
924 : {
925 34 : poClass->SetFeatureCount(nNumberOfFeatures);
926 : }
927 2 : else if (nFeatureCount != nNumberOfFeatures)
928 : {
929 0 : CPLDebug("GML", "Feature count in header, and actual feature count don't match");
930 : }
931 : }
932 :
933 454 : while( nLayers < poReader->GetClassCount() )
934 : {
935 170 : papoLayers[nLayers] = TranslateGMLSchema(poReader->GetClass(nLayers));
936 170 : nLayers++;
937 : }
938 :
939 :
940 :
941 142 : return TRUE;
942 : }
943 :
944 : /************************************************************************/
945 : /* TranslateGMLSchema() */
946 : /************************************************************************/
947 :
948 170 : OGRGMLLayer *OGRGMLDataSource::TranslateGMLSchema( GMLFeatureClass *poClass )
949 :
950 : {
951 : OGRGMLLayer *poLayer;
952 : OGRwkbGeometryType eGType
953 170 : = (OGRwkbGeometryType) poClass->GetGeometryType();
954 :
955 170 : if( poClass->GetFeatureCount() == 0 )
956 0 : eGType = wkbUnknown;
957 :
958 : /* -------------------------------------------------------------------- */
959 : /* Create an empty layer. */
960 : /* -------------------------------------------------------------------- */
961 :
962 170 : const char* pszSRSName = poClass->GetSRSName();
963 170 : OGRSpatialReference* poSRS = NULL;
964 170 : if (pszSRSName)
965 : {
966 19 : poSRS = new OGRSpatialReference();
967 19 : if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE)
968 : {
969 0 : delete poSRS;
970 0 : poSRS = NULL;
971 : }
972 : }
973 151 : else if (bIsWFS && poReader->GetClassCount() == 1)
974 : {
975 51 : pszSRSName = GetGlobalSRSName();
976 51 : if (pszSRSName)
977 : {
978 35 : poSRS = new OGRSpatialReference();
979 35 : if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE)
980 : {
981 0 : delete poSRS;
982 0 : poSRS = NULL;
983 : }
984 :
985 35 : if (poSRS != NULL && m_bInvertAxisOrderIfLatLong &&
986 : GML_IsSRSLatLongOrder(pszSRSName))
987 : {
988 5 : OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode( "GEOGCS" );
989 5 : if( poGEOGCS != NULL )
990 : {
991 5 : poGEOGCS->StripNodes( "AXIS" );
992 :
993 5 : if (!poClass->HasExtents() &&
994 : sBoundingRect.IsInit())
995 : {
996 : poClass->SetExtents(sBoundingRect.MinY,
997 : sBoundingRect.MaxY,
998 : sBoundingRect.MinX,
999 5 : sBoundingRect.MaxX);
1000 : }
1001 : }
1002 : }
1003 : }
1004 :
1005 51 : if (!poClass->HasExtents() &&
1006 : sBoundingRect.IsInit())
1007 : {
1008 : poClass->SetExtents(sBoundingRect.MinX,
1009 : sBoundingRect.MaxX,
1010 : sBoundingRect.MinY,
1011 30 : sBoundingRect.MaxY);
1012 : }
1013 : }
1014 :
1015 : poLayer = new OGRGMLLayer( poClass->GetName(), poSRS, FALSE,
1016 170 : eGType, this );
1017 224 : delete poSRS;
1018 :
1019 : /* -------------------------------------------------------------------- */
1020 : /* Added attributes (properties). */
1021 : /* -------------------------------------------------------------------- */
1022 170 : if (bExposeGMLId)
1023 : {
1024 127 : OGRFieldDefn oField( "gml_id", OFTString );
1025 127 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
1026 : }
1027 43 : else if (bExposeFid)
1028 : {
1029 31 : OGRFieldDefn oField( "fid", OFTString );
1030 31 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
1031 : }
1032 :
1033 692 : for( int iField = 0; iField < poClass->GetPropertyCount(); iField++ )
1034 : {
1035 522 : GMLPropertyDefn *poProperty = poClass->GetProperty( iField );
1036 : OGRFieldType eFType;
1037 :
1038 522 : if( poProperty->GetType() == GMLPT_Untyped )
1039 0 : eFType = OFTString;
1040 522 : else if( poProperty->GetType() == GMLPT_String )
1041 197 : eFType = OFTString;
1042 325 : else if( poProperty->GetType() == GMLPT_Integer )
1043 209 : eFType = OFTInteger;
1044 116 : else if( poProperty->GetType() == GMLPT_Real )
1045 104 : eFType = OFTReal;
1046 12 : else if( poProperty->GetType() == GMLPT_StringList )
1047 6 : eFType = OFTStringList;
1048 6 : else if( poProperty->GetType() == GMLPT_IntegerList )
1049 3 : eFType = OFTIntegerList;
1050 3 : else if( poProperty->GetType() == GMLPT_RealList )
1051 3 : eFType = OFTRealList;
1052 : else
1053 0 : eFType = OFTString;
1054 :
1055 522 : OGRFieldDefn oField( poProperty->GetName(), eFType );
1056 522 : if ( EQUALN(oField.GetNameRef(), "ogr:", 4) )
1057 0 : oField.SetName(poProperty->GetName()+4);
1058 522 : if( poProperty->GetWidth() > 0 )
1059 125 : oField.SetWidth( poProperty->GetWidth() );
1060 522 : if( poProperty->GetPrecision() > 0 )
1061 8 : oField.SetPrecision( poProperty->GetPrecision() );
1062 :
1063 522 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
1064 : }
1065 :
1066 170 : return poLayer;
1067 : }
1068 :
1069 : /************************************************************************/
1070 : /* GetGlobalSRSName() */
1071 : /************************************************************************/
1072 :
1073 1199 : const char *OGRGMLDataSource::GetGlobalSRSName()
1074 : {
1075 1199 : if (poReader->CanUseGlobalSRSName() || bIsWFS)
1076 1072 : return poReader->GetGlobalSRSName();
1077 : else
1078 127 : return NULL;
1079 : }
1080 :
1081 : /************************************************************************/
1082 : /* Create() */
1083 : /************************************************************************/
1084 :
1085 50 : int OGRGMLDataSource::Create( const char *pszFilename,
1086 : char **papszOptions )
1087 :
1088 : {
1089 50 : if( fpOutput != NULL || poReader != NULL )
1090 : {
1091 0 : CPLAssert( FALSE );
1092 0 : return FALSE;
1093 : }
1094 :
1095 50 : if( strcmp(pszFilename,"/dev/stdout") == 0 )
1096 0 : pszFilename = "/vsistdout/";
1097 :
1098 : /* -------------------------------------------------------------------- */
1099 : /* Read options */
1100 : /* -------------------------------------------------------------------- */
1101 :
1102 50 : CSLDestroy(papszCreateOptions);
1103 50 : papszCreateOptions = CSLDuplicate(papszOptions);
1104 :
1105 50 : const char* pszFormat = CSLFetchNameValue(papszCreateOptions, "FORMAT");
1106 50 : bIsOutputGML3 = pszFormat && EQUAL(pszFormat, "GML3");
1107 50 : bIsOutputGML3Deegree = pszFormat && EQUAL(pszFormat, "GML3Deegree");
1108 50 : bIsOutputGML32 = pszFormat && EQUAL(pszFormat, "GML3.2");
1109 50 : if (bIsOutputGML3Deegree || bIsOutputGML32)
1110 18 : bIsOutputGML3 = TRUE;
1111 :
1112 : bIsLongSRSRequired =
1113 50 : CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "GML3_LONGSRS", "YES"));
1114 :
1115 : bWriteSpaceIndentation =
1116 50 : CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "SPACE_INDENTATION", "YES"));
1117 :
1118 : /* -------------------------------------------------------------------- */
1119 : /* Create the output file. */
1120 : /* -------------------------------------------------------------------- */
1121 50 : pszName = CPLStrdup( pszFilename );
1122 50 : osFilename = pszName;
1123 :
1124 100 : if( strcmp(pszFilename,"/vsistdout/") == 0 ||
1125 : strncmp(pszFilename,"/vsigzip/", 9) == 0 )
1126 : {
1127 0 : fpOutput = VSIFOpenL(pszFilename, "wb");
1128 0 : bFpOutputIsNonSeekable = TRUE;
1129 0 : bFpOutputSingleFile = TRUE;
1130 : }
1131 50 : else if ( strncmp(pszFilename,"/vsizip/", 8) == 0)
1132 : {
1133 0 : if (EQUAL(CPLGetExtension(pszFilename), "zip"))
1134 : {
1135 0 : CPLFree(pszName);
1136 0 : pszName = CPLStrdup(CPLFormFilename(pszFilename, "out.gml", NULL));
1137 : }
1138 :
1139 0 : fpOutput = VSIFOpenL(pszName, "wb");
1140 0 : bFpOutputIsNonSeekable = TRUE;
1141 : }
1142 : else
1143 50 : fpOutput = VSIFOpenL( pszFilename, "wb+" );
1144 50 : if( fpOutput == NULL )
1145 : {
1146 : CPLError( CE_Failure, CPLE_OpenFailed,
1147 : "Failed to create GML file %s.",
1148 0 : pszFilename );
1149 0 : return FALSE;
1150 : }
1151 :
1152 : /* -------------------------------------------------------------------- */
1153 : /* Write out "standard" header. */
1154 : /* -------------------------------------------------------------------- */
1155 : PrintLine( fpOutput, "%s",
1156 50 : "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" );
1157 :
1158 50 : if (!bFpOutputIsNonSeekable)
1159 50 : nSchemaInsertLocation = (int) VSIFTellL( fpOutput );
1160 :
1161 50 : const char* pszPrefix = GetAppPrefix();
1162 50 : const char* pszTargetNameSpace = CSLFetchNameValueDef(papszOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/");
1163 :
1164 50 : PrintLine( fpOutput, "<%s:FeatureCollection", pszPrefix );
1165 :
1166 50 : if (IsGML32Output())
1167 : PrintLine( fpOutput, "%s",
1168 9 : " gml:id=\"aFeatureCollection\"" );
1169 :
1170 : /* -------------------------------------------------------------------- */
1171 : /* Write out schema info if provided in creation options. */
1172 : /* -------------------------------------------------------------------- */
1173 50 : const char *pszSchemaURI = CSLFetchNameValue(papszOptions,"XSISCHEMAURI");
1174 50 : const char *pszSchemaOpt = CSLFetchNameValue( papszOptions, "XSISCHEMA" );
1175 :
1176 50 : if( pszSchemaURI != NULL )
1177 : {
1178 : PrintLine( fpOutput,
1179 0 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
1180 : PrintLine( fpOutput,
1181 : " xsi:schemaLocation=\"%s\"",
1182 0 : pszSchemaURI );
1183 : }
1184 50 : else if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
1185 : {
1186 50 : char *pszBasename = CPLStrdup(CPLGetBasename( pszName ));
1187 :
1188 : PrintLine( fpOutput,
1189 50 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
1190 : PrintLine( fpOutput,
1191 : " xsi:schemaLocation=\"%s %s\"",
1192 : pszTargetNameSpace,
1193 50 : CPLResetExtension( pszBasename, "xsd" ) );
1194 50 : CPLFree( pszBasename );
1195 : }
1196 :
1197 : PrintLine( fpOutput,
1198 50 : " xmlns:%s=\"%s\"", pszPrefix, pszTargetNameSpace );
1199 50 : if (IsGML32Output())
1200 : PrintLine( fpOutput, "%s",
1201 9 : " xmlns:gml=\"http://www.opengis.net/gml/3.2\">" );
1202 : else
1203 : PrintLine( fpOutput, "%s",
1204 41 : " xmlns:gml=\"http://www.opengis.net/gml\">" );
1205 :
1206 : /* -------------------------------------------------------------------- */
1207 : /* Should we initialize an area to place the boundedBy element? */
1208 : /* We will need to seek back to fill it in. */
1209 : /* -------------------------------------------------------------------- */
1210 50 : nBoundedByLocation = -1;
1211 50 : if( CSLFetchBoolean( papszOptions, "BOUNDEDBY", TRUE ))
1212 : {
1213 50 : if (!bFpOutputIsNonSeekable )
1214 : {
1215 50 : nBoundedByLocation = (int) VSIFTellL( fpOutput );
1216 :
1217 50 : if( nBoundedByLocation != -1 )
1218 50 : PrintLine( fpOutput, "%350s", "" );
1219 : }
1220 : else
1221 : {
1222 0 : if (bWriteSpaceIndentation)
1223 0 : VSIFPrintfL( fpOutput, " ");
1224 0 : if (IsGML3Output())
1225 0 : PrintLine( fpOutput, "<gml:boundedBy><gml:Null /></gml:boundedBy>" );
1226 : else
1227 0 : PrintLine( fpOutput, "<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>" );
1228 : }
1229 : }
1230 :
1231 50 : return TRUE;
1232 : }
1233 :
1234 : /************************************************************************/
1235 : /* CreateLayer() */
1236 : /************************************************************************/
1237 :
1238 : OGRLayer *
1239 50 : OGRGMLDataSource::CreateLayer( const char * pszLayerName,
1240 : OGRSpatialReference *poSRS,
1241 : OGRwkbGeometryType eType,
1242 : char ** papszOptions )
1243 :
1244 : {
1245 : /* -------------------------------------------------------------------- */
1246 : /* Verify we are in update mode. */
1247 : /* -------------------------------------------------------------------- */
1248 50 : if( fpOutput == NULL )
1249 : {
1250 : CPLError( CE_Failure, CPLE_NoWriteAccess,
1251 : "Data source %s opened for read access.\n"
1252 : "New layer %s cannot be created.\n",
1253 0 : pszName, pszLayerName );
1254 :
1255 0 : return NULL;
1256 : }
1257 :
1258 : /* -------------------------------------------------------------------- */
1259 : /* Ensure name is safe as an element name. */
1260 : /* -------------------------------------------------------------------- */
1261 50 : char *pszCleanLayerName = CPLStrdup( pszLayerName );
1262 :
1263 50 : CPLCleanXMLElementName( pszCleanLayerName );
1264 50 : if( strcmp(pszCleanLayerName,pszLayerName) != 0 )
1265 : {
1266 : CPLError( CE_Warning, CPLE_AppDefined,
1267 : "Layer name '%s' adjusted to '%s' for XML validity.",
1268 0 : pszLayerName, pszCleanLayerName );
1269 : }
1270 :
1271 : /* -------------------------------------------------------------------- */
1272 : /* Set or check validity of global SRS. */
1273 : /* -------------------------------------------------------------------- */
1274 50 : if (nLayers == 0)
1275 : {
1276 50 : if (poSRS)
1277 36 : poGlobalSRS = poSRS->Clone();
1278 : }
1279 : else
1280 : {
1281 0 : if (poSRS == NULL ||
1282 : (poGlobalSRS != NULL && poSRS->IsSame(poGlobalSRS)))
1283 : {
1284 0 : delete poGlobalSRS;
1285 0 : poGlobalSRS = NULL;
1286 : }
1287 : }
1288 :
1289 : /* -------------------------------------------------------------------- */
1290 : /* Create the layer object. */
1291 : /* -------------------------------------------------------------------- */
1292 : OGRGMLLayer *poLayer;
1293 :
1294 50 : poLayer = new OGRGMLLayer( pszCleanLayerName, poSRS, TRUE, eType, this );
1295 :
1296 50 : CPLFree( pszCleanLayerName );
1297 :
1298 : /* -------------------------------------------------------------------- */
1299 : /* Add layer to data source layer list. */
1300 : /* -------------------------------------------------------------------- */
1301 : papoLayers = (OGRGMLLayer **)
1302 50 : CPLRealloc( papoLayers, sizeof(OGRGMLLayer *) * (nLayers+1) );
1303 :
1304 50 : papoLayers[nLayers++] = poLayer;
1305 :
1306 50 : return poLayer;
1307 : }
1308 :
1309 : /************************************************************************/
1310 : /* TestCapability() */
1311 : /************************************************************************/
1312 :
1313 12 : int OGRGMLDataSource::TestCapability( const char * pszCap )
1314 :
1315 : {
1316 12 : if( EQUAL(pszCap,ODsCCreateLayer) )
1317 11 : return TRUE;
1318 : else
1319 1 : return FALSE;
1320 : }
1321 :
1322 : /************************************************************************/
1323 : /* GetLayer() */
1324 : /************************************************************************/
1325 :
1326 230 : OGRLayer *OGRGMLDataSource::GetLayer( int iLayer )
1327 :
1328 : {
1329 230 : if( iLayer < 0 || iLayer >= nLayers )
1330 2 : return NULL;
1331 : else
1332 228 : return papoLayers[iLayer];
1333 : }
1334 :
1335 : /************************************************************************/
1336 : /* GrowExtents() */
1337 : /************************************************************************/
1338 :
1339 70 : void OGRGMLDataSource::GrowExtents( OGREnvelope3D *psGeomBounds, int nCoordDimension )
1340 :
1341 : {
1342 70 : sBoundingRect.Merge( *psGeomBounds );
1343 70 : if (nCoordDimension == 3)
1344 20 : bBBOX3D = TRUE;
1345 70 : }
1346 :
1347 : /************************************************************************/
1348 : /* InsertHeader() */
1349 : /* */
1350 : /* This method is used to update boundedby info for a */
1351 : /* dataset, and insert schema descriptions depending on */
1352 : /* selection options in effect. */
1353 : /************************************************************************/
1354 :
1355 50 : void OGRGMLDataSource::InsertHeader()
1356 :
1357 : {
1358 : VSILFILE *fpSchema;
1359 50 : int nSchemaStart = 0;
1360 :
1361 50 : if( bFpOutputSingleFile )
1362 0 : return;
1363 :
1364 : /* -------------------------------------------------------------------- */
1365 : /* Do we want to write the schema within the GML instance doc */
1366 : /* or to a separate file? For now we only support external. */
1367 : /* -------------------------------------------------------------------- */
1368 : const char *pszSchemaURI = CSLFetchNameValue(papszCreateOptions,
1369 50 : "XSISCHEMAURI");
1370 : const char *pszSchemaOpt = CSLFetchNameValue( papszCreateOptions,
1371 50 : "XSISCHEMA" );
1372 :
1373 50 : if( pszSchemaURI != NULL )
1374 0 : return;
1375 :
1376 100 : if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
1377 : {
1378 50 : const char *pszXSDFilename = CPLResetExtension( pszName, "xsd" );
1379 :
1380 50 : fpSchema = VSIFOpenL( pszXSDFilename, "wt" );
1381 50 : if( fpSchema == NULL )
1382 : {
1383 : CPLError( CE_Failure, CPLE_OpenFailed,
1384 : "Failed to open file %.500s for schema output.",
1385 0 : pszXSDFilename );
1386 0 : return;
1387 : }
1388 50 : PrintLine( fpSchema, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" );
1389 : }
1390 0 : else if( EQUAL(pszSchemaOpt,"INTERNAL") )
1391 : {
1392 0 : if (fpOutput == NULL)
1393 0 : return;
1394 0 : nSchemaStart = (int) VSIFTellL( fpOutput );
1395 0 : fpSchema = fpOutput;
1396 : }
1397 : else
1398 0 : return;
1399 :
1400 : /* ==================================================================== */
1401 : /* Write the schema section at the end of the file. Once */
1402 : /* complete, we will read it back in, and then move the whole */
1403 : /* file "down" enough to insert the schema at the beginning. */
1404 : /* ==================================================================== */
1405 :
1406 : /* -------------------------------------------------------------------- */
1407 : /* Emit the start of the schema section. */
1408 : /* -------------------------------------------------------------------- */
1409 50 : const char* pszPrefix = GetAppPrefix();
1410 50 : const char* pszTargetNameSpace = CSLFetchNameValueDef(papszCreateOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/");
1411 :
1412 50 : if (IsGML3Output())
1413 : {
1414 : PrintLine( fpSchema,
1415 28 : "<xs:schema ");
1416 : PrintLine( fpSchema,
1417 28 : " targetNamespace=\"%s\"", pszTargetNameSpace );
1418 : PrintLine( fpSchema,
1419 : " xmlns:%s=\"%s\"",
1420 28 : pszPrefix, pszTargetNameSpace );
1421 : PrintLine( fpSchema,
1422 28 : " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"");
1423 28 : if (IsGML32Output())
1424 : {
1425 : PrintLine( fpSchema,
1426 9 : " xmlns:gml=\"http://www.opengis.net/gml/3.2\"");
1427 : PrintLine( fpSchema,
1428 9 : " xmlns:gmlsf=\"http://www.opengis.net/gmlsf/2.0\"");
1429 : }
1430 : else
1431 : {
1432 : PrintLine( fpSchema,
1433 19 : " xmlns:gml=\"http://www.opengis.net/gml\"");
1434 19 : if (!IsGML3DeegreeOutput())
1435 : {
1436 : PrintLine( fpSchema,
1437 10 : " xmlns:gmlsf=\"http://www.opengis.net/gmlsf\"");
1438 : }
1439 : }
1440 : PrintLine( fpSchema,
1441 28 : " elementFormDefault=\"qualified\"");
1442 : PrintLine( fpSchema,
1443 28 : " version=\"1.0\">");
1444 :
1445 28 : if (IsGML32Output())
1446 : {
1447 : PrintLine( fpSchema,
1448 9 : "<xs:annotation>");
1449 : PrintLine( fpSchema,
1450 9 : " <xs:appinfo source=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\">");
1451 : PrintLine( fpSchema,
1452 9 : " <gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>");
1453 : PrintLine( fpSchema,
1454 9 : " </xs:appinfo>");
1455 : PrintLine( fpSchema,
1456 9 : "</xs:annotation>");
1457 :
1458 : PrintLine( fpSchema,
1459 9 : "<xs:import namespace=\"http://www.opengis.net/gml/3.2\" schemaLocation=\"http://schemas.opengis.net/gml/3.2.1/gml.xsd\"/>" );
1460 : PrintLine( fpSchema,
1461 9 : "<xs:import namespace=\"http://www.opengis.net/gmlsf/2.0\" schemaLocation=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\"/>" );
1462 : }
1463 : else
1464 : {
1465 19 : if (!IsGML3DeegreeOutput())
1466 : {
1467 : PrintLine( fpSchema,
1468 10 : "<xs:annotation>");
1469 : PrintLine( fpSchema,
1470 10 : " <xs:appinfo source=\"http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsfLevels.xsd\">");
1471 : PrintLine( fpSchema,
1472 10 : " <gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>");
1473 : PrintLine( fpSchema,
1474 10 : " <gmlsf:GMLProfileSchema>http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd</gmlsf:GMLProfileSchema>");
1475 : PrintLine( fpSchema,
1476 10 : " </xs:appinfo>");
1477 : PrintLine( fpSchema,
1478 10 : "</xs:annotation>");
1479 : }
1480 :
1481 : PrintLine( fpSchema,
1482 19 : "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/base/gml.xsd\"/>" );
1483 19 : if (!IsGML3DeegreeOutput())
1484 : {
1485 : PrintLine( fpSchema,
1486 10 : "<xs:import namespace=\"http://www.opengis.net/gmlsf\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsfLevels.xsd\"/>" );
1487 : }
1488 : }
1489 : }
1490 : else
1491 : {
1492 : PrintLine( fpSchema,
1493 : "<xs:schema targetNamespace=\"%s\" xmlns:%s=\"%s\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:gml=\"http://www.opengis.net/gml\" elementFormDefault=\"qualified\" version=\"1.0\">",
1494 22 : pszTargetNameSpace, pszPrefix, pszTargetNameSpace );
1495 :
1496 : PrintLine( fpSchema,
1497 22 : "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/2.1.2/feature.xsd\"/>" );
1498 : }
1499 :
1500 : /* -------------------------------------------------------------------- */
1501 : /* Define the FeatureCollection */
1502 : /* -------------------------------------------------------------------- */
1503 50 : if (IsGML3Output())
1504 : {
1505 28 : if (IsGML32Output())
1506 : {
1507 : PrintLine( fpSchema,
1508 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:AbstractGML\"/>",
1509 9 : pszPrefix );
1510 : }
1511 19 : else if (IsGML3DeegreeOutput())
1512 : {
1513 : PrintLine( fpSchema,
1514 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
1515 9 : pszPrefix );
1516 : }
1517 : else
1518 : {
1519 : PrintLine( fpSchema,
1520 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_GML\"/>",
1521 10 : pszPrefix );
1522 : }
1523 :
1524 28 : PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
1525 28 : PrintLine( fpSchema, " <xs:complexContent>" );
1526 28 : if (IsGML3DeegreeOutput())
1527 : {
1528 9 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
1529 9 : PrintLine( fpSchema, " <xs:sequence>" );
1530 9 : PrintLine( fpSchema, " <xs:element name=\"featureMember\" minOccurs=\"0\" maxOccurs=\"unbounded\">" );
1531 : }
1532 : else
1533 : {
1534 19 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureType\">" );
1535 19 : PrintLine( fpSchema, " <xs:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">" );
1536 19 : PrintLine( fpSchema, " <xs:element name=\"featureMember\">" );
1537 : }
1538 28 : PrintLine( fpSchema, " <xs:complexType>" );
1539 28 : if (IsGML32Output())
1540 : {
1541 9 : PrintLine( fpSchema, " <xs:complexContent>" );
1542 9 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureMemberType\">" );
1543 9 : PrintLine( fpSchema, " <xs:sequence>" );
1544 9 : PrintLine( fpSchema, " <xs:element ref=\"gml:AbstractFeature\"/>" );
1545 9 : PrintLine( fpSchema, " </xs:sequence>" );
1546 9 : PrintLine( fpSchema, " </xs:extension>" );
1547 9 : PrintLine( fpSchema, " </xs:complexContent>" );
1548 : }
1549 : else
1550 : {
1551 19 : PrintLine( fpSchema, " <xs:sequence>" );
1552 19 : PrintLine( fpSchema, " <xs:element ref=\"gml:_Feature\"/>" );
1553 19 : PrintLine( fpSchema, " </xs:sequence>" );
1554 : }
1555 28 : PrintLine( fpSchema, " </xs:complexType>" );
1556 28 : PrintLine( fpSchema, " </xs:element>" );
1557 28 : PrintLine( fpSchema, " </xs:sequence>" );
1558 28 : PrintLine( fpSchema, " </xs:extension>" );
1559 28 : PrintLine( fpSchema, " </xs:complexContent>" );
1560 28 : PrintLine( fpSchema, "</xs:complexType>" );
1561 : }
1562 : else
1563 : {
1564 : PrintLine( fpSchema,
1565 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
1566 22 : pszPrefix );
1567 :
1568 22 : PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
1569 22 : PrintLine( fpSchema, " <xs:complexContent>" );
1570 22 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
1571 22 : PrintLine( fpSchema, " <xs:attribute name=\"lockId\" type=\"xs:string\" use=\"optional\"/>" );
1572 22 : PrintLine( fpSchema, " <xs:attribute name=\"scope\" type=\"xs:string\" use=\"optional\"/>" );
1573 22 : PrintLine( fpSchema, " </xs:extension>" );
1574 22 : PrintLine( fpSchema, " </xs:complexContent>" );
1575 22 : PrintLine( fpSchema, "</xs:complexType>" );
1576 : }
1577 :
1578 : /* ==================================================================== */
1579 : /* Define the schema for each layer. */
1580 : /* ==================================================================== */
1581 : int iLayer;
1582 :
1583 100 : for( iLayer = 0; iLayer < GetLayerCount(); iLayer++ )
1584 : {
1585 50 : OGRFeatureDefn *poFDefn = GetLayer(iLayer)->GetLayerDefn();
1586 :
1587 : /* -------------------------------------------------------------------- */
1588 : /* Emit initial stuff for a feature type. */
1589 : /* -------------------------------------------------------------------- */
1590 50 : if (IsGML32Output())
1591 : {
1592 : PrintLine(
1593 : fpSchema,
1594 : "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:AbstractFeature\"/>",
1595 9 : poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
1596 : }
1597 : else
1598 : {
1599 : PrintLine(
1600 : fpSchema,
1601 : "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:_Feature\"/>",
1602 41 : poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
1603 : }
1604 :
1605 50 : PrintLine( fpSchema, "<xs:complexType name=\"%s_Type\">", poFDefn->GetName());
1606 50 : PrintLine( fpSchema, " <xs:complexContent>");
1607 50 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureType\">");
1608 50 : PrintLine( fpSchema, " <xs:sequence>");
1609 :
1610 : /* -------------------------------------------------------------------- */
1611 : /* Define the geometry attribute. */
1612 : /* -------------------------------------------------------------------- */
1613 50 : const char* pszGeometryTypeName = "GeometryPropertyType";
1614 50 : switch(wkbFlatten(poFDefn->GetGeomType()))
1615 : {
1616 : case wkbPoint:
1617 10 : pszGeometryTypeName = "PointPropertyType";
1618 10 : break;
1619 : case wkbLineString:
1620 4 : if (IsGML3Output())
1621 3 : pszGeometryTypeName = "CurvePropertyType";
1622 : else
1623 1 : pszGeometryTypeName = "LineStringPropertyType";
1624 4 : break;
1625 : case wkbPolygon:
1626 6 : if (IsGML3Output())
1627 4 : pszGeometryTypeName = "SurfacePropertyType";
1628 : else
1629 2 : pszGeometryTypeName = "PolygonPropertyType";
1630 6 : break;
1631 : case wkbMultiPoint:
1632 4 : pszGeometryTypeName = "MultiPointPropertyType";
1633 4 : break;
1634 : case wkbMultiLineString:
1635 5 : if (IsGML3Output())
1636 3 : pszGeometryTypeName = "MultiCurvePropertyType";
1637 : else
1638 2 : pszGeometryTypeName = "MultiLineStringPropertyType";
1639 5 : break;
1640 : case wkbMultiPolygon:
1641 5 : if (IsGML3Output())
1642 3 : pszGeometryTypeName = "MultiSurfacePropertyType";
1643 : else
1644 2 : pszGeometryTypeName = "MultiPolygonPropertyType";
1645 5 : break;
1646 : case wkbGeometryCollection:
1647 4 : pszGeometryTypeName = "MultiGeometryPropertyType";
1648 : break;
1649 : default:
1650 : break;
1651 : }
1652 :
1653 50 : if (poFDefn->GetGeomType() != wkbNone)
1654 : {
1655 : PrintLine( fpSchema,
1656 45 : " <xs:element name=\"geometryProperty\" type=\"gml:%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\"/>", pszGeometryTypeName );
1657 : }
1658 :
1659 : /* -------------------------------------------------------------------- */
1660 : /* Emit each of the attributes. */
1661 : /* -------------------------------------------------------------------- */
1662 174 : for( int iField = 0; iField < poFDefn->GetFieldCount(); iField++ )
1663 : {
1664 124 : OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField);
1665 :
1666 124 : if( poFieldDefn->GetType() == OFTInteger )
1667 : {
1668 : int nWidth;
1669 :
1670 35 : if( poFieldDefn->GetWidth() > 0 )
1671 2 : nWidth = poFieldDefn->GetWidth();
1672 : else
1673 33 : nWidth = 16;
1674 :
1675 35 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1676 35 : PrintLine( fpSchema, " <xs:simpleType>");
1677 35 : PrintLine( fpSchema, " <xs:restriction base=\"xs:integer\">");
1678 35 : PrintLine( fpSchema, " <xs:totalDigits value=\"%d\"/>", nWidth);
1679 35 : PrintLine( fpSchema, " </xs:restriction>");
1680 35 : PrintLine( fpSchema, " </xs:simpleType>");
1681 35 : PrintLine( fpSchema, " </xs:element>");
1682 : }
1683 89 : else if( poFieldDefn->GetType() == OFTReal )
1684 : {
1685 : int nWidth, nDecimals;
1686 :
1687 46 : nWidth = poFieldDefn->GetWidth();
1688 46 : nDecimals = poFieldDefn->GetPrecision();
1689 :
1690 46 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1691 46 : PrintLine( fpSchema, " <xs:simpleType>");
1692 46 : PrintLine( fpSchema, " <xs:restriction base=\"xs:decimal\">");
1693 46 : if (nWidth > 0)
1694 : {
1695 12 : PrintLine( fpSchema, " <xs:totalDigits value=\"%d\"/>", nWidth);
1696 12 : PrintLine( fpSchema, " <xs:fractionDigits value=\"%d\"/>", nDecimals);
1697 : }
1698 46 : PrintLine( fpSchema, " </xs:restriction>");
1699 46 : PrintLine( fpSchema, " </xs:simpleType>");
1700 46 : PrintLine( fpSchema, " </xs:element>");
1701 : }
1702 43 : else if( poFieldDefn->GetType() == OFTString )
1703 : {
1704 42 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1705 42 : PrintLine( fpSchema, " <xs:simpleType>");
1706 42 : PrintLine( fpSchema, " <xs:restriction base=\"xs:string\">");
1707 42 : if( poFieldDefn->GetWidth() != 0 )
1708 : {
1709 4 : PrintLine( fpSchema, " <xs:maxLength value=\"%d\"/>", poFieldDefn->GetWidth());
1710 : }
1711 42 : PrintLine( fpSchema, " </xs:restriction>");
1712 42 : PrintLine( fpSchema, " </xs:simpleType>");
1713 42 : PrintLine( fpSchema, " </xs:element>");
1714 : }
1715 1 : else if( poFieldDefn->GetType() == OFTDate || poFieldDefn->GetType() == OFTDateTime )
1716 : {
1717 1 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1718 1 : PrintLine( fpSchema, " <xs:simpleType>");
1719 1 : PrintLine( fpSchema, " <xs:restriction base=\"xs:string\">");
1720 1 : PrintLine( fpSchema, " </xs:restriction>");
1721 1 : PrintLine( fpSchema, " </xs:simpleType>");
1722 1 : PrintLine( fpSchema, " </xs:element>");
1723 : }
1724 : else
1725 : {
1726 : /* TODO */
1727 : }
1728 : } /* next field */
1729 :
1730 : /* -------------------------------------------------------------------- */
1731 : /* Finish off feature type. */
1732 : /* -------------------------------------------------------------------- */
1733 50 : PrintLine( fpSchema, " </xs:sequence>");
1734 50 : PrintLine( fpSchema, " </xs:extension>");
1735 50 : PrintLine( fpSchema, " </xs:complexContent>");
1736 50 : PrintLine( fpSchema, "</xs:complexType>" );
1737 : } /* next layer */
1738 :
1739 50 : PrintLine( fpSchema, "</xs:schema>" );
1740 :
1741 : /* ==================================================================== */
1742 : /* Move schema to the start of the file. */
1743 : /* ==================================================================== */
1744 50 : if( fpSchema == fpOutput )
1745 : {
1746 : /* -------------------------------------------------------------------- */
1747 : /* Read the schema into memory. */
1748 : /* -------------------------------------------------------------------- */
1749 0 : int nSchemaSize = (int) VSIFTellL( fpOutput ) - nSchemaStart;
1750 0 : char *pszSchema = (char *) CPLMalloc(nSchemaSize+1);
1751 :
1752 0 : VSIFSeekL( fpOutput, nSchemaStart, SEEK_SET );
1753 :
1754 0 : VSIFReadL( pszSchema, 1, nSchemaSize, fpOutput );
1755 0 : pszSchema[nSchemaSize] = '\0';
1756 :
1757 : /* -------------------------------------------------------------------- */
1758 : /* Move file data down by "schema size" bytes from after <?xml> */
1759 : /* header so we have room insert the schema. Move in pretty */
1760 : /* big chunks. */
1761 : /* -------------------------------------------------------------------- */
1762 0 : int nChunkSize = MIN(nSchemaStart-nSchemaInsertLocation,250000);
1763 0 : char *pszChunk = (char *) CPLMalloc(nChunkSize);
1764 0 : int nEndOfUnmovedData = nSchemaStart;
1765 :
1766 0 : for( nEndOfUnmovedData = nSchemaStart;
1767 : nEndOfUnmovedData > nSchemaInsertLocation; )
1768 : {
1769 : int nBytesToMove =
1770 0 : MIN(nChunkSize, nEndOfUnmovedData - nSchemaInsertLocation );
1771 :
1772 0 : VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove, SEEK_SET );
1773 0 : VSIFReadL( pszChunk, 1, nBytesToMove, fpOutput );
1774 : VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove + nSchemaSize,
1775 0 : SEEK_SET );
1776 0 : VSIFWriteL( pszChunk, 1, nBytesToMove, fpOutput );
1777 :
1778 0 : nEndOfUnmovedData -= nBytesToMove;
1779 : }
1780 :
1781 0 : CPLFree( pszChunk );
1782 :
1783 : /* -------------------------------------------------------------------- */
1784 : /* Write the schema in the opened slot. */
1785 : /* -------------------------------------------------------------------- */
1786 0 : VSIFSeekL( fpOutput, nSchemaInsertLocation, SEEK_SET );
1787 0 : VSIFWriteL( pszSchema, 1, nSchemaSize, fpOutput );
1788 :
1789 0 : VSIFSeekL( fpOutput, 0, SEEK_END );
1790 :
1791 0 : nBoundedByLocation += nSchemaSize;
1792 :
1793 0 : CPLFree(pszSchema);
1794 : }
1795 : /* -------------------------------------------------------------------- */
1796 : /* Close external schema files. */
1797 : /* -------------------------------------------------------------------- */
1798 : else
1799 50 : VSIFCloseL( fpSchema );
1800 : }
1801 :
1802 :
1803 : /************************************************************************/
1804 : /* PrintLine() */
1805 : /************************************************************************/
1806 :
1807 3664 : void OGRGMLDataSource::PrintLine(VSILFILE* fp, const char *fmt, ...)
1808 : {
1809 3664 : CPLString osWork;
1810 : va_list args;
1811 :
1812 3664 : va_start( args, fmt );
1813 3664 : osWork.vPrintf( fmt, args );
1814 3664 : va_end( args );
1815 :
1816 : #ifdef WIN32
1817 : const char* pszEOL = "\r\n";
1818 : #else
1819 3664 : const char* pszEOL = "\n";
1820 : #endif
1821 :
1822 3664 : VSIFPrintfL(fp, "%s%s", osWork.c_str(), pszEOL);
1823 3664 : }
1824 :
1825 :
1826 : /************************************************************************/
1827 : /* OGRGMLSingleFeatureLayer */
1828 : /************************************************************************/
1829 :
1830 : class OGRGMLSingleFeatureLayer : public OGRLayer
1831 : {
1832 : private:
1833 : int nVal;
1834 : OGRFeatureDefn *poFeatureDefn;
1835 : int iNextShapeId;
1836 :
1837 : public:
1838 : OGRGMLSingleFeatureLayer(int nVal );
1839 38 : ~OGRGMLSingleFeatureLayer() { poFeatureDefn->Release(); }
1840 :
1841 0 : virtual void ResetReading() { iNextShapeId = 0; }
1842 : virtual OGRFeature *GetNextFeature();
1843 0 : virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; }
1844 0 : virtual int TestCapability( const char * ) { return FALSE; }
1845 : };
1846 :
1847 : /************************************************************************/
1848 : /* OGRGMLSingleFeatureLayer() */
1849 : /************************************************************************/
1850 :
1851 38 : OGRGMLSingleFeatureLayer::OGRGMLSingleFeatureLayer( int nVal )
1852 : {
1853 38 : poFeatureDefn = new OGRFeatureDefn( "SELECT" );
1854 38 : poFeatureDefn->Reference();
1855 38 : OGRFieldDefn oField( "Validates", OFTInteger );
1856 38 : poFeatureDefn->AddFieldDefn( &oField );
1857 :
1858 38 : this->nVal = nVal;
1859 38 : iNextShapeId = 0;
1860 38 : }
1861 :
1862 : /************************************************************************/
1863 : /* GetNextFeature() */
1864 : /************************************************************************/
1865 :
1866 38 : OGRFeature * OGRGMLSingleFeatureLayer::GetNextFeature()
1867 : {
1868 38 : if (iNextShapeId != 0)
1869 0 : return NULL;
1870 :
1871 38 : OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
1872 38 : poFeature->SetField(0, nVal);
1873 38 : poFeature->SetFID(iNextShapeId ++);
1874 38 : return poFeature;
1875 : }
1876 :
1877 : /************************************************************************/
1878 : /* ExecuteSQL() */
1879 : /************************************************************************/
1880 :
1881 41 : OGRLayer * OGRGMLDataSource::ExecuteSQL( const char *pszSQLCommand,
1882 : OGRGeometry *poSpatialFilter,
1883 : const char *pszDialect )
1884 : {
1885 41 : if (poReader != NULL && EQUAL(pszSQLCommand, "SELECT ValidateSchema()"))
1886 : {
1887 38 : int bIsValid = FALSE;
1888 38 : if (osXSDFilename.size())
1889 : {
1890 38 : CPLErrorReset();
1891 38 : bIsValid = CPLValidateXML(osFilename, osXSDFilename, NULL);
1892 : }
1893 38 : return new OGRGMLSingleFeatureLayer(bIsValid);
1894 : }
1895 :
1896 3 : return OGRDataSource::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
1897 : }
1898 :
1899 : /************************************************************************/
1900 : /* ReleaseResultSet() */
1901 : /************************************************************************/
1902 :
1903 40 : void OGRGMLDataSource::ReleaseResultSet( OGRLayer * poResultsSet )
1904 : {
1905 40 : delete poResultsSet;
1906 40 : }
1907 :
1908 : /************************************************************************/
1909 : /* FindAndParseBoundedBy() */
1910 : /************************************************************************/
1911 :
1912 145 : void OGRGMLDataSource::FindAndParseBoundedBy(VSILFILE* fp)
1913 : {
1914 : /* Build a shortened XML file that contain only the global */
1915 : /* boundedBy element, so as to be able to parse it easily */
1916 :
1917 : char szStartTag[128];
1918 145 : char* pszXML = (char*)CPLMalloc(8192 + 128 + 3 + 1);
1919 145 : VSIFSeekL(fp, 0, SEEK_SET);
1920 145 : int nRead = (int)VSIFReadL(pszXML, 1, 8192, fp);
1921 145 : pszXML[nRead] = 0;
1922 :
1923 145 : const char* pszStartTag = strchr(pszXML, '<');
1924 145 : if (pszStartTag != NULL)
1925 : {
1926 428 : while (pszStartTag != NULL && pszStartTag[1] == '?')
1927 138 : pszStartTag = strchr(pszStartTag + 1, '<');
1928 :
1929 145 : if (pszStartTag != NULL)
1930 : {
1931 145 : pszStartTag ++;
1932 145 : const char* pszEndTag = strchr(pszStartTag, ' ');
1933 290 : if (pszEndTag != NULL && pszEndTag - pszStartTag < 128 )
1934 : {
1935 145 : memcpy(szStartTag, pszStartTag, pszEndTag - pszStartTag);
1936 145 : szStartTag[pszEndTag - pszStartTag] = '\0';
1937 : }
1938 : else
1939 0 : pszStartTag = NULL;
1940 : }
1941 : }
1942 :
1943 145 : char* pszEndBoundedBy = strstr(pszXML, "</wfs:boundedBy>");
1944 145 : int bWFSBoundedBy = FALSE;
1945 145 : if (pszEndBoundedBy != NULL)
1946 2 : bWFSBoundedBy = TRUE;
1947 : else
1948 143 : pszEndBoundedBy = strstr(pszXML, "</gml:boundedBy>");
1949 145 : if (pszStartTag != NULL && pszEndBoundedBy != NULL)
1950 : {
1951 121 : const char* pszSRSName = NULL;
1952 : char szSRSName[128];
1953 :
1954 121 : szSRSName[0] = '\0';
1955 :
1956 : /* Find a srsName somewhere for some WFS 2.0 documents */
1957 : /* that have not it set at the <wfs:boundedBy> element */
1958 : /* e.g. http://geoserv.weichand.de:8080/geoserver/wfs?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAME=bvv:gmd_ex */
1959 121 : if( bIsWFS )
1960 : {
1961 48 : pszSRSName = strstr(pszXML, "srsName=\"");
1962 48 : if( pszSRSName != NULL )
1963 : {
1964 47 : pszSRSName += 9;
1965 47 : const char* pszEndQuote = strchr(pszSRSName, '"');
1966 47 : if (pszEndQuote != NULL &&
1967 : (int)(pszEndQuote - pszSRSName) < sizeof(szSRSName))
1968 : {
1969 47 : memcpy(szSRSName, pszSRSName, pszEndQuote - pszSRSName);
1970 47 : szSRSName[pszEndQuote - pszSRSName] = '\0';
1971 : }
1972 47 : pszSRSName = NULL;
1973 : }
1974 : }
1975 :
1976 121 : pszEndBoundedBy[strlen("</gml:boundedBy>")] = '\0';
1977 121 : strcat(pszXML, "</");
1978 121 : strcat(pszXML, szStartTag);
1979 121 : strcat(pszXML, ">");
1980 :
1981 121 : CPLPushErrorHandler(CPLQuietErrorHandler);
1982 121 : CPLXMLNode* psXML = CPLParseXMLString(pszXML);
1983 121 : CPLPopErrorHandler();
1984 121 : CPLErrorReset();
1985 121 : if (psXML != NULL)
1986 : {
1987 120 : CPLXMLNode* psBoundedBy = NULL;
1988 120 : CPLXMLNode* psIter = psXML;
1989 361 : while(psIter != NULL)
1990 : {
1991 : psBoundedBy = CPLGetXMLNode(psIter, bWFSBoundedBy ?
1992 241 : "wfs:boundedBy" : "gml:boundedBy");
1993 241 : if (psBoundedBy != NULL)
1994 120 : break;
1995 121 : psIter = psIter->psNext;
1996 : }
1997 :
1998 120 : const char* pszLowerCorner = NULL;
1999 120 : const char* pszUpperCorner = NULL;
2000 120 : if (psBoundedBy != NULL)
2001 : {
2002 120 : CPLXMLNode* psEnvelope = CPLGetXMLNode(psBoundedBy, "gml:Envelope");
2003 120 : if (psEnvelope)
2004 : {
2005 80 : pszSRSName = CPLGetXMLValue(psEnvelope, "srsName", NULL);
2006 80 : pszLowerCorner = CPLGetXMLValue(psEnvelope, "gml:lowerCorner", NULL);
2007 80 : pszUpperCorner = CPLGetXMLValue(psEnvelope, "gml:upperCorner", NULL);
2008 : }
2009 : }
2010 :
2011 122 : if( bIsWFS && pszSRSName == NULL &&
2012 : pszLowerCorner != NULL && pszUpperCorner != NULL &&
2013 2 : szSRSName[0] != '\0' )
2014 : {
2015 2 : pszSRSName = szSRSName;
2016 : }
2017 :
2018 120 : if (pszSRSName != NULL && pszLowerCorner != NULL && pszUpperCorner != NULL)
2019 : {
2020 79 : char** papszLC = CSLTokenizeString(pszLowerCorner);
2021 79 : char** papszUC = CSLTokenizeString(pszUpperCorner);
2022 79 : if (CSLCount(papszLC) >= 2 && CSLCount(papszUC) >= 2)
2023 : {
2024 79 : CPLDebug("GML", "Global SRS = %s", pszSRSName);
2025 :
2026 79 : if (strncmp(pszSRSName, "http://www.opengis.net/gml/srs/epsg.xml#", 40) == 0)
2027 : {
2028 0 : std::string osWork;
2029 0 : osWork.assign("EPSG:", 5);
2030 0 : osWork.append(pszSRSName+40);
2031 0 : poReader->SetGlobalSRSName(osWork.c_str());
2032 : }
2033 : else
2034 79 : poReader->SetGlobalSRSName(pszSRSName);
2035 :
2036 79 : double dfMinX = CPLAtofM(papszLC[0]);
2037 79 : double dfMinY = CPLAtofM(papszLC[1]);
2038 79 : double dfMaxX = CPLAtofM(papszUC[0]);
2039 79 : double dfMaxY = CPLAtofM(papszUC[1]);
2040 :
2041 79 : SetExtents(dfMinX, dfMinY, dfMaxX, dfMaxY);
2042 : }
2043 79 : CSLDestroy(papszLC);
2044 79 : CSLDestroy(papszUC);
2045 : }
2046 :
2047 120 : CPLDestroyXMLNode(psXML);
2048 : }
2049 : }
2050 :
2051 145 : CPLFree(pszXML);
2052 145 : }
2053 :
2054 : /************************************************************************/
2055 : /* SetExtents() */
2056 : /************************************************************************/
2057 :
2058 79 : void OGRGMLDataSource::SetExtents(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY)
2059 : {
2060 79 : sBoundingRect.MinX = dfMinX;
2061 79 : sBoundingRect.MinY = dfMinY;
2062 79 : sBoundingRect.MaxX = dfMaxX;
2063 79 : sBoundingRect.MaxY = dfMaxY;
2064 79 : }
2065 :
2066 : /************************************************************************/
2067 : /* GetAppPrefix() */
2068 : /************************************************************************/
2069 :
2070 235 : const char* OGRGMLDataSource::GetAppPrefix()
2071 : {
2072 235 : return CSLFetchNameValueDef(papszCreateOptions, "PREFIX", "ogr");
2073 : }
|