1 : /******************************************************************************
2 : * $Id: ogrgmldatasource.cpp 25727 2013-03-10 14:56:33Z 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 25727 2013-03-10 14:56:33Z rouault $");
48 :
49 : /************************************************************************/
50 : /* ReplaceSpaceByPct20IfNeeded() */
51 : /************************************************************************/
52 :
53 50 : static CPLString ReplaceSpaceByPct20IfNeeded(const char* pszURL)
54 : {
55 : /* Replace ' ' by '%20' */
56 50 : CPLString osRet = pszURL;
57 50 : const char* pszNeedle = strstr(pszURL, "text/xml; subtype=");
58 50 : 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 507 : OGRGMLDataSource::OGRGMLDataSource()
76 :
77 : {
78 507 : pszName = NULL;
79 507 : papoLayers = NULL;
80 507 : nLayers = 0;
81 :
82 507 : poReader = NULL;
83 507 : fpOutput = NULL;
84 507 : bFpOutputIsNonSeekable = FALSE;
85 507 : bFpOutputSingleFile = FALSE;
86 507 : bIsOutputGML3 = FALSE;
87 507 : bIsOutputGML3Deegree = FALSE;
88 507 : bIsOutputGML32 = FALSE;
89 507 : bIsLongSRSRequired = FALSE;
90 507 : bWriteSpaceIndentation = TRUE;
91 :
92 507 : papszCreateOptions = NULL;
93 507 : bOutIsTempFile = FALSE;
94 :
95 507 : bExposeGMLId = FALSE;
96 507 : bExposeFid = FALSE;
97 507 : nSchemaInsertLocation = -1;
98 507 : nBoundedByLocation = -1;
99 507 : bBBOX3D = FALSE;
100 :
101 507 : poGlobalSRS = NULL;
102 507 : bIsWFS = FALSE;
103 :
104 507 : eReadMode = STANDARD;
105 507 : poStoredGMLFeature = NULL;
106 507 : poLastReadLayer = NULL;
107 :
108 507 : m_bInvertAxisOrderIfLatLong = FALSE;
109 507 : m_bConsiderEPSGAsURN = FALSE;
110 507 : m_bGetSecondaryGeometryOption = FALSE;
111 507 : }
112 :
113 : /************************************************************************/
114 : /* ~OGRGMLDataSource() */
115 : /************************************************************************/
116 :
117 507 : OGRGMLDataSource::~OGRGMLDataSource()
118 :
119 : {
120 :
121 507 : if( fpOutput != NULL )
122 : {
123 18 : const char* pszPrefix = GetAppPrefix();
124 :
125 18 : PrintLine( fpOutput, "</%s:FeatureCollection>", pszPrefix );
126 :
127 18 : if( bFpOutputIsNonSeekable)
128 : {
129 0 : VSIFCloseL( fpOutput );
130 0 : fpOutput = NULL;
131 : }
132 :
133 18 : InsertHeader();
134 :
135 18 : if( !bFpOutputIsNonSeekable
136 : && nBoundedByLocation != -1
137 : && VSIFSeekL( fpOutput, nBoundedByLocation, SEEK_SET ) == 0 )
138 : {
139 18 : if (sBoundingRect.IsInit() && IsGML3Output())
140 : {
141 4 : int bCoordSwap = FALSE;
142 : char* pszSRSName;
143 4 : if (poGlobalSRS)
144 4 : pszSRSName = GML_GetSRSName(poGlobalSRS, IsLongSRSRequired(), &bCoordSwap);
145 : else
146 0 : pszSRSName = CPLStrdup("");
147 : char szLowerCorner[75], szUpperCorner[75];
148 4 : if (bCoordSwap)
149 : {
150 3 : OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinY, sBoundingRect.MinX, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2);
151 3 : 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 4 : if (bWriteSpaceIndentation)
159 4 : 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 4 : (bBBOX3D) ? " srsDimension=\"3\"" : "", pszSRSName, szLowerCorner, szUpperCorner);
162 4 : CPLFree(pszSRSName);
163 : }
164 14 : else if (sBoundingRect.IsInit())
165 : {
166 9 : if (bWriteSpaceIndentation)
167 9 : VSIFPrintfL( fpOutput, " ");
168 9 : PrintLine( fpOutput, "<gml:boundedBy>" );
169 9 : if (bWriteSpaceIndentation)
170 9 : VSIFPrintfL( fpOutput, " ");
171 9 : PrintLine( fpOutput, "<gml:Box>" );
172 9 : if (bWriteSpaceIndentation)
173 9 : VSIFPrintfL( fpOutput, " ");
174 : VSIFPrintfL( fpOutput,
175 : "<gml:coord><gml:X>%.16g</gml:X>"
176 : "<gml:Y>%.16g</gml:Y>",
177 9 : sBoundingRect.MinX, sBoundingRect.MinY );
178 9 : if (bBBOX3D)
179 : VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
180 1 : sBoundingRect.MinZ );
181 9 : PrintLine( fpOutput, "</gml:coord>");
182 9 : if (bWriteSpaceIndentation)
183 9 : VSIFPrintfL( fpOutput, " ");
184 : VSIFPrintfL( fpOutput,
185 : "<gml:coord><gml:X>%.16g</gml:X>"
186 : "<gml:Y>%.16g</gml:Y>",
187 9 : sBoundingRect.MaxX, sBoundingRect.MaxY );
188 9 : if (bBBOX3D)
189 : VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
190 1 : sBoundingRect.MaxZ );
191 9 : PrintLine( fpOutput, "</gml:coord>");
192 9 : if (bWriteSpaceIndentation)
193 9 : VSIFPrintfL( fpOutput, " ");
194 9 : PrintLine( fpOutput, "</gml:Box>" );
195 9 : if (bWriteSpaceIndentation)
196 9 : VSIFPrintfL( fpOutput, " ");
197 9 : PrintLine( fpOutput, "</gml:boundedBy>" );
198 : }
199 : else
200 : {
201 5 : if (bWriteSpaceIndentation)
202 5 : VSIFPrintfL( fpOutput, " ");
203 5 : if (IsGML3Output())
204 0 : PrintLine( fpOutput, "<gml:boundedBy><gml:Null /></gml:boundedBy>" );
205 : else
206 5 : PrintLine( fpOutput, "<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>" );
207 : }
208 : }
209 :
210 18 : if (fpOutput)
211 18 : VSIFCloseL( fpOutput );
212 : }
213 :
214 507 : CSLDestroy( papszCreateOptions );
215 507 : CPLFree( pszName );
216 :
217 665 : for( int i = 0; i < nLayers; i++ )
218 158 : delete papoLayers[i];
219 :
220 507 : CPLFree( papoLayers );
221 :
222 507 : if( poReader )
223 : {
224 115 : if (bOutIsTempFile)
225 0 : VSIUnlink(poReader->GetSourceFileName());
226 115 : delete poReader;
227 : }
228 :
229 507 : delete poGlobalSRS;
230 :
231 507 : delete poStoredGMLFeature;
232 :
233 507 : if (osXSDFilename.compare(CPLSPrintf("/vsimem/tmp_gml_xsd_%p.xsd", this)) == 0)
234 0 : VSIUnlink(osXSDFilename);
235 507 : }
236 :
237 : /************************************************************************/
238 : /* Open() */
239 : /************************************************************************/
240 :
241 489 : int OGRGMLDataSource::Open( const char * pszNameIn, int bTestOpen )
242 :
243 : {
244 : VSILFILE *fp;
245 : char szHeader[2048];
246 489 : int nNumberOfFeatures = 0;
247 489 : CPLString osWithVsiGzip;
248 489 : const char *pszSchemaLocation = NULL;
249 489 : int bCheckAuxFile = TRUE;
250 :
251 : /* -------------------------------------------------------------------- */
252 : /* Extract xsd filename from connexion string if present. */
253 : /* -------------------------------------------------------------------- */
254 489 : osFilename = pszNameIn;
255 489 : const char *pszXSDFilenameTmp = strstr(pszNameIn, ",xsd=");
256 489 : if (pszXSDFilenameTmp != NULL)
257 : {
258 46 : osFilename.resize(pszXSDFilenameTmp - pszNameIn);
259 46 : osXSDFilename = pszXSDFilenameTmp + strlen(",xsd=");
260 : }
261 489 : const char *pszFilename = osFilename.c_str();
262 :
263 489 : pszName = CPLStrdup( pszNameIn );
264 :
265 : /* -------------------------------------------------------------------- */
266 : /* Open the source file. */
267 : /* -------------------------------------------------------------------- */
268 489 : fp = VSIFOpenL( pszFilename, "r" );
269 489 : if( fp == NULL )
270 : {
271 103 : if( !bTestOpen )
272 : CPLError( CE_Failure, CPLE_OpenFailed,
273 : "Failed to open GML file `%s'.",
274 0 : pszFilename );
275 :
276 103 : return FALSE;
277 : }
278 :
279 386 : int bExpatCompatibleEncoding = FALSE;
280 386 : int bHas3D = FALSE;
281 386 : 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 386 : if( bTestOpen )
288 : {
289 386 : size_t nRead = VSIFReadL( szHeader, 1, sizeof(szHeader)-1, fp );
290 386 : if (nRead <= 0)
291 : {
292 15 : VSIFCloseL( fp );
293 15 : return FALSE;
294 : }
295 371 : 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 371 : 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 371 : char* szPtr = szHeader;
331 :
332 373 : 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 371 : const char* pszEncoding = strstr(szPtr, "encoding=");
340 371 : if (pszEncoding)
341 260 : 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 392 : pszEncoding[20] == pszEncoding[9])) ;
346 : else
347 239 : bExpatCompatibleEncoding = TRUE; /* utf-8 is the default */
348 :
349 371 : 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 371 : if( szPtr[0] != '<'
355 : || strstr(szPtr,"opengis.net/gml") == NULL )
356 : {
357 252 : VSIFCloseL( fp );
358 252 : return FALSE;
359 : }
360 :
361 : /* Ignore GeoRSS documents. They will be recognized by the GeoRSS driver */
362 119 : 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 115 : const char* pszFeatureCollection = strstr(szPtr, "wfs:FeatureCollection");
372 115 : if (pszFeatureCollection == NULL)
373 62 : pszFeatureCollection = strstr(szPtr, "gml:FeatureCollection"); /* GML 3.2.1 output */
374 115 : if (pszFeatureCollection == NULL)
375 : {
376 52 : pszFeatureCollection = strstr(szPtr, "<FeatureCollection"); /* Deegree WFS 1.0.0 output */
377 52 : if (pszFeatureCollection && strstr(szPtr, "xmlns:wfs=\"http://www.opengis.net/wfs\"") == NULL)
378 1 : pszFeatureCollection = NULL;
379 : }
380 115 : if (pszFeatureCollection)
381 : {
382 64 : bExposeGMLId = TRUE;
383 64 : bIsWFS = TRUE;
384 64 : const char* pszNumberOfFeatures = strstr(szPtr, "numberOfFeatures=");
385 64 : if (pszNumberOfFeatures)
386 : {
387 39 : pszNumberOfFeatures += 17;
388 39 : char ch = pszNumberOfFeatures[0];
389 39 : if ((ch == '\'' || ch == '"') && strchr(pszNumberOfFeatures + 1, ch) != NULL)
390 : {
391 39 : nNumberOfFeatures = atoi(pszNumberOfFeatures + 1);
392 : }
393 : }
394 25 : else if ((pszNumberOfFeatures = strstr(szPtr, "numberReturned=")) != NULL) /* WFS 2.0.0 */
395 : {
396 3 : pszNumberOfFeatures += 15;
397 3 : char ch = pszNumberOfFeatures[0];
398 3 : 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 3 : nNumberOfFeatures = atoi(pszNumberOfFeatures + 1);
404 : }
405 : }
406 : }
407 51 : 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 51 : strstr(szPtr, " gml:id='") != NULL;
419 : bExposeFid = strstr(szPtr, " fid=\"") != NULL ||
420 51 : strstr(szPtr, " fid='") != NULL;
421 :
422 51 : const char* pszExposeGMLId = CPLGetConfigOption("GML_EXPOSE_GML_ID", NULL);
423 51 : if (pszExposeGMLId)
424 0 : bExposeGMLId = CSLTestBoolean(pszExposeGMLId);
425 :
426 51 : const char* pszExposeFid = CPLGetConfigOption("GML_EXPOSE_FID", NULL);
427 51 : if (pszExposeFid)
428 1 : bExposeFid = CSLTestBoolean(pszExposeFid);
429 : }
430 :
431 115 : bHintConsiderEPSGAsURN = strstr(szPtr, "xmlns:fme=\"http://www.safe.com/gml/fme\"") != NULL;
432 :
433 115 : pszSchemaLocation = strstr(szPtr, "schemaLocation=");
434 115 : if (pszSchemaLocation)
435 91 : pszSchemaLocation += strlen("schemaLocation=");
436 :
437 115 : if (bIsWFS && EQUALN(pszFilename, "/vsicurl_streaming/", strlen("/vsicurl_streaming/")))
438 43 : bCheckAuxFile = FALSE;
439 : }
440 :
441 : /* -------------------------------------------------------------------- */
442 : /* We assume now that it is GML. Instantiate a GMLReader on it. */
443 : /* -------------------------------------------------------------------- */
444 :
445 115 : const char* pszReadMode = CPLGetConfigOption("GML_READ_MODE", NULL);
446 227 : if (pszReadMode == NULL || EQUAL(pszReadMode, "STANDARD"))
447 112 : 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 115 : CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES"));
459 :
460 : const char* pszConsiderEPSGAsURN =
461 115 : CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", NULL);
462 115 : if (pszConsiderEPSGAsURN != NULL)
463 0 : m_bConsiderEPSGAsURN = CSLTestBoolean(pszConsiderEPSGAsURN);
464 115 : 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 110 : m_bConsiderEPSGAsURN = FALSE;
472 :
473 115 : 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 115 : int bUseExpatParserPreferably = bExpatCompatibleEncoding;
479 :
480 : /* Override default choice */
481 115 : const char* pszGMLParser = CPLGetConfigOption("GML_PARSER", NULL);
482 115 : 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 115 : m_bGetSecondaryGeometryOption );
494 115 : 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 115 : poReader->SetSourceFile( pszFilename );
506 :
507 : /* -------------------------------------------------------------------- */
508 : /* Find <gml:boundedBy> */
509 : /* -------------------------------------------------------------------- */
510 :
511 115 : 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 115 : char *pszXlinkResolvedFilename = NULL;
519 115 : const char *pszOption = CPLGetConfigOption("GML_SAVE_RESOLVED_TO", NULL);
520 115 : int bResolve = TRUE;
521 115 : int bHugeFile = FALSE;
522 115 : if( pszOption != NULL && EQUALN( pszOption, "SAME", 4 ) )
523 : {
524 : // "SAME" will overwrite the existing gml file
525 0 : pszXlinkResolvedFilename = CPLStrdup( pszFilename );
526 : }
527 115 : 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 113 : CPLResetExtension( pszFilename, "resolved.gml" ) );
540 :
541 : // Check if the file already exists.
542 : VSIStatBufL sResStatBuf, sGMLStatBuf;
543 113 : 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 115 : "ALL");
563 115 : char **papszSkip = NULL;
564 115 : if( EQUAL( pszSkipOption, "ALL" ) )
565 108 : 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 115 : int bHaveSchema = FALSE;
573 115 : int bSchemaDone = FALSE;
574 :
575 : /* -------------------------------------------------------------------- */
576 : /* Is some GML Feature Schema (.gfs) TEMPLATE required ? */
577 : /* -------------------------------------------------------------------- */
578 : const char *pszGFSTemplateName =
579 115 : CPLGetConfigOption( "GML_GFS_TEMPLATE", NULL);
580 115 : if( pszGFSTemplateName != NULL )
581 : {
582 : /* attempting to load the GFS TEMPLATE */
583 0 : bHaveSchema = poReader->LoadClasses( pszGFSTemplateName );
584 : }
585 :
586 115 : 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 115 : CPLFree( pszXlinkResolvedFilename );
612 115 : pszXlinkResolvedFilename = NULL;
613 115 : CSLDestroy( papszSkip );
614 115 : 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 115 : if (strcmp(poReader->GetSourceFileName(), pszFilename) == 0)
619 105 : poReader->SetFP(fp);
620 : else
621 10 : VSIFCloseL(fp);
622 115 : fp = NULL;
623 :
624 : /* Is a prescan required ? */
625 115 : 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 115 : CPLString osGFSFilename = CPLResetExtension( pszFilename, "gfs" );
637 115 : 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 115 : if( !bHaveSchema && osXSDFilename.size() == 0)
644 : {
645 : VSIStatBufL sGFSStatBuf;
646 71 : 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 115 : int bHasFoundXSD = FALSE;
682 :
683 115 : if( !bHaveSchema )
684 : {
685 96 : char** papszTypeNames = NULL;
686 :
687 : VSIStatBufL sXSDStatBuf;
688 96 : if (osXSDFilename.size() == 0)
689 : {
690 52 : osXSDFilename = CPLResetExtension( pszFilename, "xsd" );
691 52 : if( bCheckAuxFile && VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
692 : {
693 24 : bHasFoundXSD = TRUE;
694 : }
695 : }
696 : else
697 : {
698 44 : if ( VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
699 : {
700 44 : bHasFoundXSD = TRUE;
701 : }
702 : }
703 :
704 : /* For WFS, try to fetch the application schema */
705 246 : if( bIsWFS && pszSchemaLocation != NULL &&
706 100 : (pszSchemaLocation[0] == '\'' || pszSchemaLocation[0] == '"') &&
707 50 : strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) != NULL )
708 : {
709 50 : char* pszSchemaLocationTmp1 = CPLStrdup(pszSchemaLocation + 1);
710 50 : int nTruncLen = (int)(strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) - (pszSchemaLocation + 1));
711 50 : pszSchemaLocationTmp1[nTruncLen] = '\0';
712 : char* pszSchemaLocationTmp2 = CPLUnescapeString(
713 50 : pszSchemaLocationTmp1, NULL, CPLES_XML);
714 50 : CPLString osEscaped = ReplaceSpaceByPct20IfNeeded(pszSchemaLocationTmp2);
715 50 : CPLFree(pszSchemaLocationTmp2);
716 50 : pszSchemaLocationTmp2 = CPLStrdup(osEscaped);
717 50 : 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 50 : char** papszTokens = CSLTokenizeString2(pszSchemaLocationTmp2, " \r\n", 0);
724 50 : int nTokens = CSLCount(papszTokens);
725 50 : if ((nTokens % 2) == 0)
726 : {
727 147 : for(int i=0;i<nTokens;i+=2)
728 : {
729 97 : char* pszLocation = CPLUnescapeString(papszTokens[i+1], NULL, CPLES_URL);
730 97 : CPLString osLocation = pszLocation;
731 97 : CPLFree(pszLocation);
732 97 : if (osLocation.ifind("typename=") != std::string::npos &&
733 : osLocation.ifind("request=DescribeFeatureType") != std::string::npos)
734 : {
735 43 : CPLString osTypeName = CPLURLGetValue(osLocation, "typename");
736 43 : papszTypeNames = CSLTokenizeString2( osTypeName, ",", 0);
737 :
738 43 : 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 43 : }
759 : }
760 : }
761 : }
762 50 : CSLDestroy(papszTokens);
763 : }
764 50 : CPLFree(pszSchemaLocationTmp2);
765 50 : CPLFree(pszSchemaLocationTmp1);
766 : }
767 :
768 96 : if( bHasFoundXSD )
769 : {
770 68 : std::vector<GMLFeatureClass*> aosClasses;
771 68 : bHaveSchema = GMLParseXSD( osXSDFilename, aosClasses );
772 68 : if (bHaveSchema)
773 : {
774 68 : CPLDebug("GML", "Using %s", osXSDFilename.c_str());
775 :
776 68 : std::vector<GMLFeatureClass*>::const_iterator iter = aosClasses.begin();
777 68 : std::vector<GMLFeatureClass*>::const_iterator eiter = aosClasses.end();
778 210 : while (iter != eiter)
779 : {
780 74 : GMLFeatureClass* poClass = *iter;
781 74 : 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 74 : if (bHas3D)
787 : {
788 : poClass->SetGeometryType(
789 2 : poClass->GetGeometryType() | wkb25DBit);
790 : }
791 :
792 74 : int bAddClass = TRUE;
793 : /* If typenames are declared, only register the matching classes, in case */
794 : /* the XSD contains more layers */
795 74 : if (papszTypeNames != NULL)
796 : {
797 43 : bAddClass = FALSE;
798 43 : char** papszIter = papszTypeNames;
799 129 : while (*papszIter && !bAddClass)
800 : {
801 43 : const char* pszTypeName = *papszIter;
802 43 : if (strcmp(pszTypeName, poClass->GetName()) == 0)
803 2 : bAddClass = TRUE;
804 43 : papszIter ++;
805 : }
806 :
807 : /* Retry by removing prefixes */
808 43 : if (!bAddClass)
809 : {
810 41 : papszIter = papszTypeNames;
811 123 : while (*papszIter && !bAddClass)
812 : {
813 41 : const char* pszTypeName = *papszIter;
814 41 : const char* pszColon = strchr(pszTypeName, ':');
815 41 : if (pszColon)
816 : {
817 41 : pszTypeName = pszColon + 1;
818 41 : if (strcmp(pszTypeName, poClass->GetName()) == 0)
819 : {
820 41 : poClass->SetName(pszTypeName);
821 41 : bAddClass = TRUE;
822 : }
823 : }
824 41 : papszIter ++;
825 : }
826 : }
827 :
828 : }
829 :
830 74 : if (bAddClass)
831 74 : poReader->AddClass( poClass );
832 : else
833 0 : delete poClass;
834 : }
835 68 : poReader->SetClassListLocked( TRUE );
836 68 : }
837 : }
838 :
839 96 : if (bHaveSchema && bIsWFS)
840 : {
841 : /* For WFS, we can assume sequential layers */
842 52 : 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 51 : else if (poReader->GetClassCount() == 0)
850 0 : bHaveSchema = FALSE;
851 : }
852 :
853 96 : 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 115 : 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 112 : 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 112 : 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 112 : CPLCalloc( sizeof(OGRGMLLayer *), poReader->GetClassCount());
917 112 : nLayers = 0;
918 :
919 112 : if (poReader->GetClassCount() == 1 && nNumberOfFeatures != 0)
920 : {
921 39 : GMLFeatureClass *poClass = poReader->GetClass(0);
922 39 : int nFeatureCount = poClass->GetFeatureCount();
923 39 : if (nFeatureCount < 0)
924 : {
925 37 : 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 364 : while( nLayers < poReader->GetClassCount() )
934 : {
935 140 : papoLayers[nLayers] = TranslateGMLSchema(poReader->GetClass(nLayers));
936 140 : nLayers++;
937 : }
938 :
939 :
940 :
941 112 : return TRUE;
942 : }
943 :
944 : /************************************************************************/
945 : /* TranslateGMLSchema() */
946 : /************************************************************************/
947 :
948 140 : OGRGMLLayer *OGRGMLDataSource::TranslateGMLSchema( GMLFeatureClass *poClass )
949 :
950 : {
951 : OGRGMLLayer *poLayer;
952 : OGRwkbGeometryType eGType
953 140 : = (OGRwkbGeometryType) poClass->GetGeometryType();
954 :
955 140 : if( poClass->GetFeatureCount() == 0 )
956 0 : eGType = wkbUnknown;
957 :
958 : /* -------------------------------------------------------------------- */
959 : /* Create an empty layer. */
960 : /* -------------------------------------------------------------------- */
961 :
962 140 : const char* pszSRSName = poClass->GetSRSName();
963 140 : OGRSpatialReference* poSRS = NULL;
964 140 : 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 121 : else if (bIsWFS && poReader->GetClassCount() == 1)
974 : {
975 55 : pszSRSName = GetGlobalSRSName();
976 55 : if (pszSRSName)
977 : {
978 38 : poSRS = new OGRSpatialReference();
979 38 : if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE)
980 : {
981 0 : delete poSRS;
982 0 : poSRS = NULL;
983 : }
984 :
985 38 : if (poSRS != NULL && m_bInvertAxisOrderIfLatLong &&
986 : GML_IsSRSLatLongOrder(pszSRSName))
987 : {
988 4 : OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode( "GEOGCS" );
989 4 : if( poGEOGCS != NULL )
990 4 : poGEOGCS->StripNodes( "AXIS" );
991 :
992 4 : OGR_SRSNode *poPROJCS = poSRS->GetAttrNode( "PROJCS" );
993 4 : if (poPROJCS != NULL && poSRS->EPSGTreatsAsNorthingEasting())
994 0 : poPROJCS->StripNodes( "AXIS" );
995 :
996 4 : if (!poClass->HasExtents() &&
997 : sBoundingRect.IsInit())
998 : {
999 : poClass->SetExtents(sBoundingRect.MinY,
1000 : sBoundingRect.MaxY,
1001 : sBoundingRect.MinX,
1002 4 : sBoundingRect.MaxX);
1003 : }
1004 : }
1005 : }
1006 :
1007 55 : if (!poClass->HasExtents() &&
1008 : sBoundingRect.IsInit())
1009 : {
1010 : poClass->SetExtents(sBoundingRect.MinX,
1011 : sBoundingRect.MaxX,
1012 : sBoundingRect.MinY,
1013 34 : sBoundingRect.MaxY);
1014 : }
1015 : }
1016 :
1017 : poLayer = new OGRGMLLayer( poClass->GetName(), poSRS, FALSE,
1018 140 : eGType, this );
1019 197 : delete poSRS;
1020 :
1021 : /* -------------------------------------------------------------------- */
1022 : /* Added attributes (properties). */
1023 : /* -------------------------------------------------------------------- */
1024 140 : if (bExposeGMLId)
1025 : {
1026 106 : OGRFieldDefn oField( "gml_id", OFTString );
1027 106 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
1028 : }
1029 34 : else if (bExposeFid)
1030 : {
1031 22 : OGRFieldDefn oField( "fid", OFTString );
1032 22 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
1033 : }
1034 :
1035 543 : for( int iField = 0; iField < poClass->GetPropertyCount(); iField++ )
1036 : {
1037 403 : GMLPropertyDefn *poProperty = poClass->GetProperty( iField );
1038 : OGRFieldType eFType;
1039 :
1040 403 : if( poProperty->GetType() == GMLPT_Untyped )
1041 0 : eFType = OFTString;
1042 403 : else if( poProperty->GetType() == GMLPT_String )
1043 157 : eFType = OFTString;
1044 246 : else if( poProperty->GetType() == GMLPT_Integer )
1045 183 : eFType = OFTInteger;
1046 63 : else if( poProperty->GetType() == GMLPT_Real )
1047 51 : eFType = OFTReal;
1048 12 : else if( poProperty->GetType() == GMLPT_StringList )
1049 6 : eFType = OFTStringList;
1050 6 : else if( poProperty->GetType() == GMLPT_IntegerList )
1051 3 : eFType = OFTIntegerList;
1052 3 : else if( poProperty->GetType() == GMLPT_RealList )
1053 3 : eFType = OFTRealList;
1054 : else
1055 0 : eFType = OFTString;
1056 :
1057 403 : OGRFieldDefn oField( poProperty->GetName(), eFType );
1058 403 : if ( EQUALN(oField.GetNameRef(), "ogr:", 4) )
1059 0 : oField.SetName(poProperty->GetName()+4);
1060 403 : if( poProperty->GetWidth() > 0 )
1061 92 : oField.SetWidth( poProperty->GetWidth() );
1062 403 : if( poProperty->GetPrecision() > 0 )
1063 8 : oField.SetPrecision( poProperty->GetPrecision() );
1064 :
1065 403 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
1066 : }
1067 :
1068 140 : return poLayer;
1069 : }
1070 :
1071 : /************************************************************************/
1072 : /* GetGlobalSRSName() */
1073 : /************************************************************************/
1074 :
1075 653 : const char *OGRGMLDataSource::GetGlobalSRSName()
1076 : {
1077 653 : if (poReader->CanUseGlobalSRSName() || bIsWFS)
1078 552 : return poReader->GetGlobalSRSName();
1079 : else
1080 101 : return NULL;
1081 : }
1082 :
1083 : /************************************************************************/
1084 : /* Create() */
1085 : /************************************************************************/
1086 :
1087 18 : int OGRGMLDataSource::Create( const char *pszFilename,
1088 : char **papszOptions )
1089 :
1090 : {
1091 18 : if( fpOutput != NULL || poReader != NULL )
1092 : {
1093 0 : CPLAssert( FALSE );
1094 0 : return FALSE;
1095 : }
1096 :
1097 18 : if( strcmp(pszFilename,"/dev/stdout") == 0 )
1098 0 : pszFilename = "/vsistdout/";
1099 :
1100 : /* -------------------------------------------------------------------- */
1101 : /* Read options */
1102 : /* -------------------------------------------------------------------- */
1103 :
1104 18 : CSLDestroy(papszCreateOptions);
1105 18 : papszCreateOptions = CSLDuplicate(papszOptions);
1106 :
1107 18 : const char* pszFormat = CSLFetchNameValue(papszCreateOptions, "FORMAT");
1108 18 : bIsOutputGML3 = pszFormat && EQUAL(pszFormat, "GML3");
1109 18 : bIsOutputGML3Deegree = pszFormat && EQUAL(pszFormat, "GML3Deegree");
1110 18 : bIsOutputGML32 = pszFormat && EQUAL(pszFormat, "GML3.2");
1111 18 : if (bIsOutputGML3Deegree || bIsOutputGML32)
1112 2 : bIsOutputGML3 = TRUE;
1113 :
1114 : bIsLongSRSRequired =
1115 18 : CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "GML3_LONGSRS", "YES"));
1116 :
1117 : bWriteSpaceIndentation =
1118 18 : CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "SPACE_INDENTATION", "YES"));
1119 :
1120 : /* -------------------------------------------------------------------- */
1121 : /* Create the output file. */
1122 : /* -------------------------------------------------------------------- */
1123 18 : pszName = CPLStrdup( pszFilename );
1124 18 : osFilename = pszName;
1125 :
1126 36 : if( strcmp(pszFilename,"/vsistdout/") == 0 ||
1127 : strncmp(pszFilename,"/vsigzip/", 9) == 0 )
1128 : {
1129 0 : fpOutput = VSIFOpenL(pszFilename, "wb");
1130 0 : bFpOutputIsNonSeekable = TRUE;
1131 0 : bFpOutputSingleFile = TRUE;
1132 : }
1133 18 : else if ( strncmp(pszFilename,"/vsizip/", 8) == 0)
1134 : {
1135 0 : if (EQUAL(CPLGetExtension(pszFilename), "zip"))
1136 : {
1137 0 : CPLFree(pszName);
1138 0 : pszName = CPLStrdup(CPLFormFilename(pszFilename, "out.gml", NULL));
1139 : }
1140 :
1141 0 : fpOutput = VSIFOpenL(pszName, "wb");
1142 0 : bFpOutputIsNonSeekable = TRUE;
1143 : }
1144 : else
1145 18 : fpOutput = VSIFOpenL( pszFilename, "wb+" );
1146 18 : if( fpOutput == NULL )
1147 : {
1148 : CPLError( CE_Failure, CPLE_OpenFailed,
1149 : "Failed to create GML file %s.",
1150 0 : pszFilename );
1151 0 : return FALSE;
1152 : }
1153 :
1154 : /* -------------------------------------------------------------------- */
1155 : /* Write out "standard" header. */
1156 : /* -------------------------------------------------------------------- */
1157 : PrintLine( fpOutput, "%s",
1158 18 : "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" );
1159 :
1160 18 : if (!bFpOutputIsNonSeekable)
1161 18 : nSchemaInsertLocation = (int) VSIFTellL( fpOutput );
1162 :
1163 18 : const char* pszPrefix = GetAppPrefix();
1164 18 : const char* pszTargetNameSpace = CSLFetchNameValueDef(papszOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/");
1165 :
1166 18 : PrintLine( fpOutput, "<%s:FeatureCollection", pszPrefix );
1167 :
1168 18 : if (IsGML32Output())
1169 : PrintLine( fpOutput, "%s",
1170 1 : " gml:id=\"aFeatureCollection\"" );
1171 :
1172 : /* -------------------------------------------------------------------- */
1173 : /* Write out schema info if provided in creation options. */
1174 : /* -------------------------------------------------------------------- */
1175 18 : const char *pszSchemaURI = CSLFetchNameValue(papszOptions,"XSISCHEMAURI");
1176 18 : const char *pszSchemaOpt = CSLFetchNameValue( papszOptions, "XSISCHEMA" );
1177 :
1178 18 : if( pszSchemaURI != NULL )
1179 : {
1180 : PrintLine( fpOutput,
1181 0 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
1182 : PrintLine( fpOutput,
1183 : " xsi:schemaLocation=\"%s\"",
1184 0 : pszSchemaURI );
1185 : }
1186 18 : else if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
1187 : {
1188 18 : char *pszBasename = CPLStrdup(CPLGetBasename( pszName ));
1189 :
1190 : PrintLine( fpOutput,
1191 18 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
1192 : PrintLine( fpOutput,
1193 : " xsi:schemaLocation=\"%s %s\"",
1194 : pszTargetNameSpace,
1195 18 : CPLResetExtension( pszBasename, "xsd" ) );
1196 18 : CPLFree( pszBasename );
1197 : }
1198 :
1199 : PrintLine( fpOutput,
1200 18 : " xmlns:%s=\"%s\"", pszPrefix, pszTargetNameSpace );
1201 18 : if (IsGML32Output())
1202 : PrintLine( fpOutput, "%s",
1203 1 : " xmlns:gml=\"http://www.opengis.net/gml/3.2\">" );
1204 : else
1205 : PrintLine( fpOutput, "%s",
1206 17 : " xmlns:gml=\"http://www.opengis.net/gml\">" );
1207 :
1208 : /* -------------------------------------------------------------------- */
1209 : /* Should we initialize an area to place the boundedBy element? */
1210 : /* We will need to seek back to fill it in. */
1211 : /* -------------------------------------------------------------------- */
1212 18 : nBoundedByLocation = -1;
1213 18 : if( CSLFetchBoolean( papszOptions, "BOUNDEDBY", TRUE ))
1214 : {
1215 18 : if (!bFpOutputIsNonSeekable )
1216 : {
1217 18 : nBoundedByLocation = (int) VSIFTellL( fpOutput );
1218 :
1219 18 : if( nBoundedByLocation != -1 )
1220 18 : PrintLine( fpOutput, "%350s", "" );
1221 : }
1222 : else
1223 : {
1224 0 : if (bWriteSpaceIndentation)
1225 0 : VSIFPrintfL( fpOutput, " ");
1226 0 : if (IsGML3Output())
1227 0 : PrintLine( fpOutput, "<gml:boundedBy><gml:Null /></gml:boundedBy>" );
1228 : else
1229 0 : PrintLine( fpOutput, "<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>" );
1230 : }
1231 : }
1232 :
1233 18 : return TRUE;
1234 : }
1235 :
1236 : /************************************************************************/
1237 : /* CreateLayer() */
1238 : /************************************************************************/
1239 :
1240 : OGRLayer *
1241 18 : OGRGMLDataSource::CreateLayer( const char * pszLayerName,
1242 : OGRSpatialReference *poSRS,
1243 : OGRwkbGeometryType eType,
1244 : char ** papszOptions )
1245 :
1246 : {
1247 : /* -------------------------------------------------------------------- */
1248 : /* Verify we are in update mode. */
1249 : /* -------------------------------------------------------------------- */
1250 18 : if( fpOutput == NULL )
1251 : {
1252 : CPLError( CE_Failure, CPLE_NoWriteAccess,
1253 : "Data source %s opened for read access.\n"
1254 : "New layer %s cannot be created.\n",
1255 0 : pszName, pszLayerName );
1256 :
1257 0 : return NULL;
1258 : }
1259 :
1260 : /* -------------------------------------------------------------------- */
1261 : /* Ensure name is safe as an element name. */
1262 : /* -------------------------------------------------------------------- */
1263 18 : char *pszCleanLayerName = CPLStrdup( pszLayerName );
1264 :
1265 18 : CPLCleanXMLElementName( pszCleanLayerName );
1266 18 : if( strcmp(pszCleanLayerName,pszLayerName) != 0 )
1267 : {
1268 : CPLError( CE_Warning, CPLE_AppDefined,
1269 : "Layer name '%s' adjusted to '%s' for XML validity.",
1270 0 : pszLayerName, pszCleanLayerName );
1271 : }
1272 :
1273 : /* -------------------------------------------------------------------- */
1274 : /* Set or check validity of global SRS. */
1275 : /* -------------------------------------------------------------------- */
1276 18 : if (nLayers == 0)
1277 : {
1278 18 : if (poSRS)
1279 8 : poGlobalSRS = poSRS->Clone();
1280 : }
1281 : else
1282 : {
1283 0 : if (poSRS == NULL ||
1284 : (poGlobalSRS != NULL && poSRS->IsSame(poGlobalSRS)))
1285 : {
1286 0 : delete poGlobalSRS;
1287 0 : poGlobalSRS = NULL;
1288 : }
1289 : }
1290 :
1291 : /* -------------------------------------------------------------------- */
1292 : /* Create the layer object. */
1293 : /* -------------------------------------------------------------------- */
1294 : OGRGMLLayer *poLayer;
1295 :
1296 18 : poLayer = new OGRGMLLayer( pszCleanLayerName, poSRS, TRUE, eType, this );
1297 :
1298 18 : CPLFree( pszCleanLayerName );
1299 :
1300 : /* -------------------------------------------------------------------- */
1301 : /* Add layer to data source layer list. */
1302 : /* -------------------------------------------------------------------- */
1303 : papoLayers = (OGRGMLLayer **)
1304 18 : CPLRealloc( papoLayers, sizeof(OGRGMLLayer *) * (nLayers+1) );
1305 :
1306 18 : papoLayers[nLayers++] = poLayer;
1307 :
1308 18 : return poLayer;
1309 : }
1310 :
1311 : /************************************************************************/
1312 : /* TestCapability() */
1313 : /************************************************************************/
1314 :
1315 12 : int OGRGMLDataSource::TestCapability( const char * pszCap )
1316 :
1317 : {
1318 12 : if( EQUAL(pszCap,ODsCCreateLayer) )
1319 11 : return TRUE;
1320 : else
1321 1 : return FALSE;
1322 : }
1323 :
1324 : /************************************************************************/
1325 : /* GetLayer() */
1326 : /************************************************************************/
1327 :
1328 176 : OGRLayer *OGRGMLDataSource::GetLayer( int iLayer )
1329 :
1330 : {
1331 176 : if( iLayer < 0 || iLayer >= nLayers )
1332 2 : return NULL;
1333 : else
1334 174 : return papoLayers[iLayer];
1335 : }
1336 :
1337 : /************************************************************************/
1338 : /* GrowExtents() */
1339 : /************************************************************************/
1340 :
1341 42 : void OGRGMLDataSource::GrowExtents( OGREnvelope3D *psGeomBounds, int nCoordDimension )
1342 :
1343 : {
1344 42 : sBoundingRect.Merge( *psGeomBounds );
1345 42 : if (nCoordDimension == 3)
1346 20 : bBBOX3D = TRUE;
1347 42 : }
1348 :
1349 : /************************************************************************/
1350 : /* InsertHeader() */
1351 : /* */
1352 : /* This method is used to update boundedby info for a */
1353 : /* dataset, and insert schema descriptions depending on */
1354 : /* selection options in effect. */
1355 : /************************************************************************/
1356 :
1357 18 : void OGRGMLDataSource::InsertHeader()
1358 :
1359 : {
1360 : VSILFILE *fpSchema;
1361 18 : int nSchemaStart = 0;
1362 :
1363 18 : if( bFpOutputSingleFile )
1364 0 : return;
1365 :
1366 : /* -------------------------------------------------------------------- */
1367 : /* Do we want to write the schema within the GML instance doc */
1368 : /* or to a separate file? For now we only support external. */
1369 : /* -------------------------------------------------------------------- */
1370 : const char *pszSchemaURI = CSLFetchNameValue(papszCreateOptions,
1371 18 : "XSISCHEMAURI");
1372 : const char *pszSchemaOpt = CSLFetchNameValue( papszCreateOptions,
1373 18 : "XSISCHEMA" );
1374 :
1375 18 : if( pszSchemaURI != NULL )
1376 0 : return;
1377 :
1378 36 : if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
1379 : {
1380 18 : const char *pszXSDFilename = CPLResetExtension( pszName, "xsd" );
1381 :
1382 18 : fpSchema = VSIFOpenL( pszXSDFilename, "wt" );
1383 18 : if( fpSchema == NULL )
1384 : {
1385 : CPLError( CE_Failure, CPLE_OpenFailed,
1386 : "Failed to open file %.500s for schema output.",
1387 0 : pszXSDFilename );
1388 0 : return;
1389 : }
1390 18 : PrintLine( fpSchema, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" );
1391 : }
1392 0 : else if( EQUAL(pszSchemaOpt,"INTERNAL") )
1393 : {
1394 0 : if (fpOutput == NULL)
1395 0 : return;
1396 0 : nSchemaStart = (int) VSIFTellL( fpOutput );
1397 0 : fpSchema = fpOutput;
1398 : }
1399 : else
1400 0 : return;
1401 :
1402 : /* ==================================================================== */
1403 : /* Write the schema section at the end of the file. Once */
1404 : /* complete, we will read it back in, and then move the whole */
1405 : /* file "down" enough to insert the schema at the beginning. */
1406 : /* ==================================================================== */
1407 :
1408 : /* -------------------------------------------------------------------- */
1409 : /* Emit the start of the schema section. */
1410 : /* -------------------------------------------------------------------- */
1411 18 : const char* pszPrefix = GetAppPrefix();
1412 18 : const char* pszTargetNameSpace = CSLFetchNameValueDef(papszCreateOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/");
1413 :
1414 18 : if (IsGML3Output())
1415 : {
1416 : PrintLine( fpSchema,
1417 4 : "<xs:schema ");
1418 : PrintLine( fpSchema,
1419 4 : " targetNamespace=\"%s\"", pszTargetNameSpace );
1420 : PrintLine( fpSchema,
1421 : " xmlns:%s=\"%s\"",
1422 4 : pszPrefix, pszTargetNameSpace );
1423 : PrintLine( fpSchema,
1424 4 : " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"");
1425 4 : if (IsGML32Output())
1426 : {
1427 : PrintLine( fpSchema,
1428 1 : " xmlns:gml=\"http://www.opengis.net/gml/3.2\"");
1429 : PrintLine( fpSchema,
1430 1 : " xmlns:gmlsf=\"http://www.opengis.net/gmlsf/2.0\"");
1431 : }
1432 : else
1433 : {
1434 : PrintLine( fpSchema,
1435 3 : " xmlns:gml=\"http://www.opengis.net/gml\"");
1436 3 : if (!IsGML3DeegreeOutput())
1437 : {
1438 : PrintLine( fpSchema,
1439 2 : " xmlns:gmlsf=\"http://www.opengis.net/gmlsf\"");
1440 : }
1441 : }
1442 : PrintLine( fpSchema,
1443 4 : " elementFormDefault=\"qualified\"");
1444 : PrintLine( fpSchema,
1445 4 : " version=\"1.0\">");
1446 :
1447 4 : if (IsGML32Output())
1448 : {
1449 : PrintLine( fpSchema,
1450 1 : "<xs:annotation>");
1451 : PrintLine( fpSchema,
1452 1 : " <xs:appinfo source=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\">");
1453 : PrintLine( fpSchema,
1454 1 : " <gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>");
1455 : PrintLine( fpSchema,
1456 1 : " </xs:appinfo>");
1457 : PrintLine( fpSchema,
1458 1 : "</xs:annotation>");
1459 :
1460 : PrintLine( fpSchema,
1461 1 : "<xs:import namespace=\"http://www.opengis.net/gml/3.2\" schemaLocation=\"http://schemas.opengis.net/gml/3.2.1/gml.xsd\"/>" );
1462 : PrintLine( fpSchema,
1463 1 : "<xs:import namespace=\"http://www.opengis.net/gmlsf/2.0\" schemaLocation=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\"/>" );
1464 : }
1465 : else
1466 : {
1467 3 : if (!IsGML3DeegreeOutput())
1468 : {
1469 : PrintLine( fpSchema,
1470 2 : "<xs:annotation>");
1471 : PrintLine( fpSchema,
1472 2 : " <xs:appinfo source=\"http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsfLevels.xsd\">");
1473 : PrintLine( fpSchema,
1474 2 : " <gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>");
1475 : PrintLine( fpSchema,
1476 2 : " <gmlsf:GMLProfileSchema>http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd</gmlsf:GMLProfileSchema>");
1477 : PrintLine( fpSchema,
1478 2 : " </xs:appinfo>");
1479 : PrintLine( fpSchema,
1480 2 : "</xs:annotation>");
1481 : }
1482 :
1483 : PrintLine( fpSchema,
1484 3 : "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/base/gml.xsd\"/>" );
1485 3 : if (!IsGML3DeegreeOutput())
1486 : {
1487 : PrintLine( fpSchema,
1488 2 : "<xs:import namespace=\"http://www.opengis.net/gmlsf\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsfLevels.xsd\"/>" );
1489 : }
1490 : }
1491 : }
1492 : else
1493 : {
1494 : PrintLine( fpSchema,
1495 : "<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\">",
1496 14 : pszTargetNameSpace, pszPrefix, pszTargetNameSpace );
1497 :
1498 : PrintLine( fpSchema,
1499 14 : "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/2.1.2/feature.xsd\"/>" );
1500 : }
1501 :
1502 : /* -------------------------------------------------------------------- */
1503 : /* Define the FeatureCollection */
1504 : /* -------------------------------------------------------------------- */
1505 18 : if (IsGML3Output())
1506 : {
1507 4 : if (IsGML32Output())
1508 : {
1509 : PrintLine( fpSchema,
1510 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:AbstractGML\"/>",
1511 1 : pszPrefix );
1512 : }
1513 3 : else if (IsGML3DeegreeOutput())
1514 : {
1515 : PrintLine( fpSchema,
1516 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
1517 1 : pszPrefix );
1518 : }
1519 : else
1520 : {
1521 : PrintLine( fpSchema,
1522 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_GML\"/>",
1523 2 : pszPrefix );
1524 : }
1525 :
1526 4 : PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
1527 4 : PrintLine( fpSchema, " <xs:complexContent>" );
1528 4 : if (IsGML3DeegreeOutput())
1529 : {
1530 1 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
1531 1 : PrintLine( fpSchema, " <xs:sequence>" );
1532 1 : PrintLine( fpSchema, " <xs:element name=\"featureMember\" minOccurs=\"0\" maxOccurs=\"unbounded\">" );
1533 : }
1534 : else
1535 : {
1536 3 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureType\">" );
1537 3 : PrintLine( fpSchema, " <xs:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">" );
1538 3 : PrintLine( fpSchema, " <xs:element name=\"featureMember\">" );
1539 : }
1540 4 : PrintLine( fpSchema, " <xs:complexType>" );
1541 4 : if (IsGML32Output())
1542 : {
1543 1 : PrintLine( fpSchema, " <xs:complexContent>" );
1544 1 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureMemberType\">" );
1545 1 : PrintLine( fpSchema, " <xs:sequence>" );
1546 1 : PrintLine( fpSchema, " <xs:element ref=\"gml:AbstractFeature\"/>" );
1547 1 : PrintLine( fpSchema, " </xs:sequence>" );
1548 1 : PrintLine( fpSchema, " </xs:extension>" );
1549 1 : PrintLine( fpSchema, " </xs:complexContent>" );
1550 : }
1551 : else
1552 : {
1553 3 : PrintLine( fpSchema, " <xs:sequence>" );
1554 3 : PrintLine( fpSchema, " <xs:element ref=\"gml:_Feature\"/>" );
1555 3 : PrintLine( fpSchema, " </xs:sequence>" );
1556 : }
1557 4 : PrintLine( fpSchema, " </xs:complexType>" );
1558 4 : PrintLine( fpSchema, " </xs:element>" );
1559 4 : PrintLine( fpSchema, " </xs:sequence>" );
1560 4 : PrintLine( fpSchema, " </xs:extension>" );
1561 4 : PrintLine( fpSchema, " </xs:complexContent>" );
1562 4 : PrintLine( fpSchema, "</xs:complexType>" );
1563 : }
1564 : else
1565 : {
1566 : PrintLine( fpSchema,
1567 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
1568 14 : pszPrefix );
1569 :
1570 14 : PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
1571 14 : PrintLine( fpSchema, " <xs:complexContent>" );
1572 14 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
1573 14 : PrintLine( fpSchema, " <xs:attribute name=\"lockId\" type=\"xs:string\" use=\"optional\"/>" );
1574 14 : PrintLine( fpSchema, " <xs:attribute name=\"scope\" type=\"xs:string\" use=\"optional\"/>" );
1575 14 : PrintLine( fpSchema, " </xs:extension>" );
1576 14 : PrintLine( fpSchema, " </xs:complexContent>" );
1577 14 : PrintLine( fpSchema, "</xs:complexType>" );
1578 : }
1579 :
1580 : /* ==================================================================== */
1581 : /* Define the schema for each layer. */
1582 : /* ==================================================================== */
1583 : int iLayer;
1584 :
1585 36 : for( iLayer = 0; iLayer < GetLayerCount(); iLayer++ )
1586 : {
1587 18 : OGRFeatureDefn *poFDefn = GetLayer(iLayer)->GetLayerDefn();
1588 :
1589 : /* -------------------------------------------------------------------- */
1590 : /* Emit initial stuff for a feature type. */
1591 : /* -------------------------------------------------------------------- */
1592 18 : if (IsGML32Output())
1593 : {
1594 : PrintLine(
1595 : fpSchema,
1596 : "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:AbstractFeature\"/>",
1597 1 : poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
1598 : }
1599 : else
1600 : {
1601 : PrintLine(
1602 : fpSchema,
1603 : "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:_Feature\"/>",
1604 17 : poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
1605 : }
1606 :
1607 18 : PrintLine( fpSchema, "<xs:complexType name=\"%s_Type\">", poFDefn->GetName());
1608 18 : PrintLine( fpSchema, " <xs:complexContent>");
1609 18 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureType\">");
1610 18 : PrintLine( fpSchema, " <xs:sequence>");
1611 :
1612 : /* -------------------------------------------------------------------- */
1613 : /* Define the geometry attribute. */
1614 : /* -------------------------------------------------------------------- */
1615 18 : const char* pszGeometryTypeName = "GeometryPropertyType";
1616 18 : switch(wkbFlatten(poFDefn->GetGeomType()))
1617 : {
1618 : case wkbPoint:
1619 6 : pszGeometryTypeName = "PointPropertyType";
1620 6 : break;
1621 : case wkbLineString:
1622 0 : if (IsGML3Output())
1623 0 : pszGeometryTypeName = "CurvePropertyType";
1624 : else
1625 0 : pszGeometryTypeName = "LineStringPropertyType";
1626 0 : break;
1627 : case wkbPolygon:
1628 2 : if (IsGML3Output())
1629 1 : pszGeometryTypeName = "SurfacePropertyType";
1630 : else
1631 1 : pszGeometryTypeName = "PolygonPropertyType";
1632 2 : break;
1633 : case wkbMultiPoint:
1634 0 : pszGeometryTypeName = "MultiPointPropertyType";
1635 0 : break;
1636 : case wkbMultiLineString:
1637 1 : if (IsGML3Output())
1638 0 : pszGeometryTypeName = "MultiCurvePropertyType";
1639 : else
1640 1 : pszGeometryTypeName = "MultiLineStringPropertyType";
1641 1 : break;
1642 : case wkbMultiPolygon:
1643 1 : if (IsGML3Output())
1644 0 : pszGeometryTypeName = "MultiSurfacePropertyType";
1645 : else
1646 1 : pszGeometryTypeName = "MultiPolygonPropertyType";
1647 1 : break;
1648 : case wkbGeometryCollection:
1649 0 : pszGeometryTypeName = "MultiGeometryPropertyType";
1650 : break;
1651 : default:
1652 : break;
1653 : }
1654 :
1655 18 : if (poFDefn->GetGeomType() != wkbNone)
1656 : {
1657 : PrintLine( fpSchema,
1658 17 : " <xs:element name=\"geometryProperty\" type=\"gml:%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\"/>", pszGeometryTypeName );
1659 : }
1660 :
1661 : /* -------------------------------------------------------------------- */
1662 : /* Emit each of the attributes. */
1663 : /* -------------------------------------------------------------------- */
1664 46 : for( int iField = 0; iField < poFDefn->GetFieldCount(); iField++ )
1665 : {
1666 28 : OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField);
1667 :
1668 28 : if( poFieldDefn->GetType() == OFTInteger )
1669 : {
1670 : int nWidth;
1671 :
1672 3 : if( poFieldDefn->GetWidth() > 0 )
1673 2 : nWidth = poFieldDefn->GetWidth();
1674 : else
1675 1 : nWidth = 16;
1676 :
1677 3 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1678 3 : PrintLine( fpSchema, " <xs:simpleType>");
1679 3 : PrintLine( fpSchema, " <xs:restriction base=\"xs:integer\">");
1680 3 : PrintLine( fpSchema, " <xs:totalDigits value=\"%d\"/>", nWidth);
1681 3 : PrintLine( fpSchema, " </xs:restriction>");
1682 3 : PrintLine( fpSchema, " </xs:simpleType>");
1683 3 : PrintLine( fpSchema, " </xs:element>");
1684 : }
1685 25 : else if( poFieldDefn->GetType() == OFTReal )
1686 : {
1687 : int nWidth, nDecimals;
1688 :
1689 14 : nWidth = poFieldDefn->GetWidth();
1690 14 : nDecimals = poFieldDefn->GetPrecision();
1691 :
1692 14 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1693 14 : PrintLine( fpSchema, " <xs:simpleType>");
1694 14 : PrintLine( fpSchema, " <xs:restriction base=\"xs:decimal\">");
1695 14 : if (nWidth > 0)
1696 : {
1697 12 : PrintLine( fpSchema, " <xs:totalDigits value=\"%d\"/>", nWidth);
1698 12 : PrintLine( fpSchema, " <xs:fractionDigits value=\"%d\"/>", nDecimals);
1699 : }
1700 14 : PrintLine( fpSchema, " </xs:restriction>");
1701 14 : PrintLine( fpSchema, " </xs:simpleType>");
1702 14 : PrintLine( fpSchema, " </xs:element>");
1703 : }
1704 11 : else if( poFieldDefn->GetType() == OFTString )
1705 : {
1706 10 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1707 10 : PrintLine( fpSchema, " <xs:simpleType>");
1708 10 : PrintLine( fpSchema, " <xs:restriction base=\"xs:string\">");
1709 10 : if( poFieldDefn->GetWidth() != 0 )
1710 : {
1711 4 : PrintLine( fpSchema, " <xs:maxLength value=\"%d\"/>", poFieldDefn->GetWidth());
1712 : }
1713 10 : PrintLine( fpSchema, " </xs:restriction>");
1714 10 : PrintLine( fpSchema, " </xs:simpleType>");
1715 10 : PrintLine( fpSchema, " </xs:element>");
1716 : }
1717 1 : else if( poFieldDefn->GetType() == OFTDate || poFieldDefn->GetType() == OFTDateTime )
1718 : {
1719 1 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1720 1 : PrintLine( fpSchema, " <xs:simpleType>");
1721 1 : PrintLine( fpSchema, " <xs:restriction base=\"xs:string\">");
1722 1 : PrintLine( fpSchema, " </xs:restriction>");
1723 1 : PrintLine( fpSchema, " </xs:simpleType>");
1724 1 : PrintLine( fpSchema, " </xs:element>");
1725 : }
1726 : else
1727 : {
1728 : /* TODO */
1729 : }
1730 : } /* next field */
1731 :
1732 : /* -------------------------------------------------------------------- */
1733 : /* Finish off feature type. */
1734 : /* -------------------------------------------------------------------- */
1735 18 : PrintLine( fpSchema, " </xs:sequence>");
1736 18 : PrintLine( fpSchema, " </xs:extension>");
1737 18 : PrintLine( fpSchema, " </xs:complexContent>");
1738 18 : PrintLine( fpSchema, "</xs:complexType>" );
1739 : } /* next layer */
1740 :
1741 18 : PrintLine( fpSchema, "</xs:schema>" );
1742 :
1743 : /* ==================================================================== */
1744 : /* Move schema to the start of the file. */
1745 : /* ==================================================================== */
1746 18 : if( fpSchema == fpOutput )
1747 : {
1748 : /* -------------------------------------------------------------------- */
1749 : /* Read the schema into memory. */
1750 : /* -------------------------------------------------------------------- */
1751 0 : int nSchemaSize = (int) VSIFTellL( fpOutput ) - nSchemaStart;
1752 0 : char *pszSchema = (char *) CPLMalloc(nSchemaSize+1);
1753 :
1754 0 : VSIFSeekL( fpOutput, nSchemaStart, SEEK_SET );
1755 :
1756 0 : VSIFReadL( pszSchema, 1, nSchemaSize, fpOutput );
1757 0 : pszSchema[nSchemaSize] = '\0';
1758 :
1759 : /* -------------------------------------------------------------------- */
1760 : /* Move file data down by "schema size" bytes from after <?xml> */
1761 : /* header so we have room insert the schema. Move in pretty */
1762 : /* big chunks. */
1763 : /* -------------------------------------------------------------------- */
1764 0 : int nChunkSize = MIN(nSchemaStart-nSchemaInsertLocation,250000);
1765 0 : char *pszChunk = (char *) CPLMalloc(nChunkSize);
1766 0 : int nEndOfUnmovedData = nSchemaStart;
1767 :
1768 0 : for( nEndOfUnmovedData = nSchemaStart;
1769 : nEndOfUnmovedData > nSchemaInsertLocation; )
1770 : {
1771 : int nBytesToMove =
1772 0 : MIN(nChunkSize, nEndOfUnmovedData - nSchemaInsertLocation );
1773 :
1774 0 : VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove, SEEK_SET );
1775 0 : VSIFReadL( pszChunk, 1, nBytesToMove, fpOutput );
1776 : VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove + nSchemaSize,
1777 0 : SEEK_SET );
1778 0 : VSIFWriteL( pszChunk, 1, nBytesToMove, fpOutput );
1779 :
1780 0 : nEndOfUnmovedData -= nBytesToMove;
1781 : }
1782 :
1783 0 : CPLFree( pszChunk );
1784 :
1785 : /* -------------------------------------------------------------------- */
1786 : /* Write the schema in the opened slot. */
1787 : /* -------------------------------------------------------------------- */
1788 0 : VSIFSeekL( fpOutput, nSchemaInsertLocation, SEEK_SET );
1789 0 : VSIFWriteL( pszSchema, 1, nSchemaSize, fpOutput );
1790 :
1791 0 : VSIFSeekL( fpOutput, 0, SEEK_END );
1792 :
1793 0 : nBoundedByLocation += nSchemaSize;
1794 :
1795 0 : CPLFree(pszSchema);
1796 : }
1797 : /* -------------------------------------------------------------------- */
1798 : /* Close external schema files. */
1799 : /* -------------------------------------------------------------------- */
1800 : else
1801 18 : VSIFCloseL( fpSchema );
1802 : }
1803 :
1804 :
1805 : /************************************************************************/
1806 : /* PrintLine() */
1807 : /************************************************************************/
1808 :
1809 1256 : void OGRGMLDataSource::PrintLine(VSILFILE* fp, const char *fmt, ...)
1810 : {
1811 1256 : CPLString osWork;
1812 : va_list args;
1813 :
1814 1256 : va_start( args, fmt );
1815 1256 : osWork.vPrintf( fmt, args );
1816 1256 : va_end( args );
1817 :
1818 : #ifdef WIN32
1819 : const char* pszEOL = "\r\n";
1820 : #else
1821 1256 : const char* pszEOL = "\n";
1822 : #endif
1823 :
1824 1256 : VSIFPrintfL(fp, "%s%s", osWork.c_str(), pszEOL);
1825 1256 : }
1826 :
1827 :
1828 : /************************************************************************/
1829 : /* OGRGMLSingleFeatureLayer */
1830 : /************************************************************************/
1831 :
1832 : class OGRGMLSingleFeatureLayer : public OGRLayer
1833 : {
1834 : private:
1835 : int nVal;
1836 : OGRFeatureDefn *poFeatureDefn;
1837 : int iNextShapeId;
1838 :
1839 : public:
1840 : OGRGMLSingleFeatureLayer(int nVal );
1841 1 : ~OGRGMLSingleFeatureLayer() { poFeatureDefn->Release(); }
1842 :
1843 0 : virtual void ResetReading() { iNextShapeId = 0; }
1844 : virtual OGRFeature *GetNextFeature();
1845 0 : virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; }
1846 0 : virtual int TestCapability( const char * ) { return FALSE; }
1847 : };
1848 :
1849 : /************************************************************************/
1850 : /* OGRGMLSingleFeatureLayer() */
1851 : /************************************************************************/
1852 :
1853 1 : OGRGMLSingleFeatureLayer::OGRGMLSingleFeatureLayer( int nVal )
1854 : {
1855 1 : poFeatureDefn = new OGRFeatureDefn( "SELECT" );
1856 1 : poFeatureDefn->Reference();
1857 1 : OGRFieldDefn oField( "Validates", OFTInteger );
1858 1 : poFeatureDefn->AddFieldDefn( &oField );
1859 :
1860 1 : this->nVal = nVal;
1861 1 : iNextShapeId = 0;
1862 1 : }
1863 :
1864 : /************************************************************************/
1865 : /* GetNextFeature() */
1866 : /************************************************************************/
1867 :
1868 1 : OGRFeature * OGRGMLSingleFeatureLayer::GetNextFeature()
1869 : {
1870 1 : if (iNextShapeId != 0)
1871 0 : return NULL;
1872 :
1873 1 : OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
1874 1 : poFeature->SetField(0, nVal);
1875 1 : poFeature->SetFID(iNextShapeId ++);
1876 1 : return poFeature;
1877 : }
1878 :
1879 : /************************************************************************/
1880 : /* ExecuteSQL() */
1881 : /************************************************************************/
1882 :
1883 5 : OGRLayer * OGRGMLDataSource::ExecuteSQL( const char *pszSQLCommand,
1884 : OGRGeometry *poSpatialFilter,
1885 : const char *pszDialect )
1886 : {
1887 5 : if (poReader != NULL && EQUAL(pszSQLCommand, "SELECT ValidateSchema()"))
1888 : {
1889 1 : int bIsValid = FALSE;
1890 1 : if (osXSDFilename.size())
1891 : {
1892 1 : CPLErrorReset();
1893 1 : bIsValid = CPLValidateXML(osFilename, osXSDFilename, NULL);
1894 : }
1895 1 : return new OGRGMLSingleFeatureLayer(bIsValid);
1896 : }
1897 :
1898 4 : return OGRDataSource::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
1899 : }
1900 :
1901 : /************************************************************************/
1902 : /* ReleaseResultSet() */
1903 : /************************************************************************/
1904 :
1905 4 : void OGRGMLDataSource::ReleaseResultSet( OGRLayer * poResultsSet )
1906 : {
1907 4 : delete poResultsSet;
1908 4 : }
1909 :
1910 : /************************************************************************/
1911 : /* FindAndParseBoundedBy() */
1912 : /************************************************************************/
1913 :
1914 115 : void OGRGMLDataSource::FindAndParseBoundedBy(VSILFILE* fp)
1915 : {
1916 : /* Build a shortened XML file that contain only the global */
1917 : /* boundedBy element, so as to be able to parse it easily */
1918 :
1919 : char szStartTag[128];
1920 115 : char* pszXML = (char*)CPLMalloc(8192 + 128 + 3 + 1);
1921 115 : VSIFSeekL(fp, 0, SEEK_SET);
1922 115 : int nRead = (int)VSIFReadL(pszXML, 1, 8192, fp);
1923 115 : pszXML[nRead] = 0;
1924 :
1925 115 : const char* pszStartTag = strchr(pszXML, '<');
1926 115 : if (pszStartTag != NULL)
1927 : {
1928 338 : while (pszStartTag != NULL && pszStartTag[1] == '?')
1929 108 : pszStartTag = strchr(pszStartTag + 1, '<');
1930 :
1931 115 : if (pszStartTag != NULL)
1932 : {
1933 115 : pszStartTag ++;
1934 115 : const char* pszEndTag = strchr(pszStartTag, ' ');
1935 230 : if (pszEndTag != NULL && pszEndTag - pszStartTag < 128 )
1936 : {
1937 115 : memcpy(szStartTag, pszStartTag, pszEndTag - pszStartTag);
1938 115 : szStartTag[pszEndTag - pszStartTag] = '\0';
1939 : }
1940 : else
1941 0 : pszStartTag = NULL;
1942 : }
1943 : }
1944 :
1945 115 : char* pszEndBoundedBy = strstr(pszXML, "</wfs:boundedBy>");
1946 115 : int bWFSBoundedBy = FALSE;
1947 115 : if (pszEndBoundedBy != NULL)
1948 2 : bWFSBoundedBy = TRUE;
1949 : else
1950 113 : pszEndBoundedBy = strstr(pszXML, "</gml:boundedBy>");
1951 115 : if (pszStartTag != NULL && pszEndBoundedBy != NULL)
1952 : {
1953 93 : const char* pszSRSName = NULL;
1954 : char szSRSName[128];
1955 :
1956 93 : szSRSName[0] = '\0';
1957 :
1958 : /* Find a srsName somewhere for some WFS 2.0 documents */
1959 : /* that have not it set at the <wfs:boundedBy> element */
1960 : /* e.g. http://geoserv.weichand.de:8080/geoserver/wfs?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAME=bvv:gmd_ex */
1961 93 : if( bIsWFS )
1962 : {
1963 54 : pszSRSName = strstr(pszXML, "srsName=\"");
1964 54 : if( pszSRSName != NULL )
1965 : {
1966 53 : pszSRSName += 9;
1967 53 : const char* pszEndQuote = strchr(pszSRSName, '"');
1968 53 : if (pszEndQuote != NULL &&
1969 : (size_t)(pszEndQuote - pszSRSName) < sizeof(szSRSName))
1970 : {
1971 53 : memcpy(szSRSName, pszSRSName, pszEndQuote - pszSRSName);
1972 53 : szSRSName[pszEndQuote - pszSRSName] = '\0';
1973 : }
1974 53 : pszSRSName = NULL;
1975 : }
1976 : }
1977 :
1978 93 : pszEndBoundedBy[strlen("</gml:boundedBy>")] = '\0';
1979 93 : strcat(pszXML, "</");
1980 93 : strcat(pszXML, szStartTag);
1981 93 : strcat(pszXML, ">");
1982 :
1983 93 : CPLPushErrorHandler(CPLQuietErrorHandler);
1984 93 : CPLXMLNode* psXML = CPLParseXMLString(pszXML);
1985 93 : CPLPopErrorHandler();
1986 93 : CPLErrorReset();
1987 93 : if (psXML != NULL)
1988 : {
1989 92 : CPLXMLNode* psBoundedBy = NULL;
1990 92 : CPLXMLNode* psIter = psXML;
1991 277 : while(psIter != NULL)
1992 : {
1993 : psBoundedBy = CPLGetXMLNode(psIter, bWFSBoundedBy ?
1994 185 : "wfs:boundedBy" : "gml:boundedBy");
1995 185 : if (psBoundedBy != NULL)
1996 92 : break;
1997 93 : psIter = psIter->psNext;
1998 : }
1999 :
2000 92 : const char* pszLowerCorner = NULL;
2001 92 : const char* pszUpperCorner = NULL;
2002 92 : if (psBoundedBy != NULL)
2003 : {
2004 92 : CPLXMLNode* psEnvelope = CPLGetXMLNode(psBoundedBy, "gml:Envelope");
2005 92 : if (psEnvelope)
2006 : {
2007 61 : pszSRSName = CPLGetXMLValue(psEnvelope, "srsName", NULL);
2008 61 : pszLowerCorner = CPLGetXMLValue(psEnvelope, "gml:lowerCorner", NULL);
2009 61 : pszUpperCorner = CPLGetXMLValue(psEnvelope, "gml:upperCorner", NULL);
2010 : }
2011 : }
2012 :
2013 94 : if( bIsWFS && pszSRSName == NULL &&
2014 : pszLowerCorner != NULL && pszUpperCorner != NULL &&
2015 2 : szSRSName[0] != '\0' )
2016 : {
2017 2 : pszSRSName = szSRSName;
2018 : }
2019 :
2020 92 : if (pszSRSName != NULL && pszLowerCorner != NULL && pszUpperCorner != NULL)
2021 : {
2022 60 : char** papszLC = CSLTokenizeString(pszLowerCorner);
2023 60 : char** papszUC = CSLTokenizeString(pszUpperCorner);
2024 60 : if (CSLCount(papszLC) >= 2 && CSLCount(papszUC) >= 2)
2025 : {
2026 60 : CPLDebug("GML", "Global SRS = %s", pszSRSName);
2027 :
2028 60 : if (strncmp(pszSRSName, "http://www.opengis.net/gml/srs/epsg.xml#", 40) == 0)
2029 : {
2030 0 : std::string osWork;
2031 0 : osWork.assign("EPSG:", 5);
2032 0 : osWork.append(pszSRSName+40);
2033 0 : poReader->SetGlobalSRSName(osWork.c_str());
2034 : }
2035 : else
2036 60 : poReader->SetGlobalSRSName(pszSRSName);
2037 :
2038 60 : double dfMinX = CPLAtofM(papszLC[0]);
2039 60 : double dfMinY = CPLAtofM(papszLC[1]);
2040 60 : double dfMaxX = CPLAtofM(papszUC[0]);
2041 60 : double dfMaxY = CPLAtofM(papszUC[1]);
2042 :
2043 60 : SetExtents(dfMinX, dfMinY, dfMaxX, dfMaxY);
2044 : }
2045 60 : CSLDestroy(papszLC);
2046 60 : CSLDestroy(papszUC);
2047 : }
2048 :
2049 92 : CPLDestroyXMLNode(psXML);
2050 : }
2051 : }
2052 :
2053 115 : CPLFree(pszXML);
2054 115 : }
2055 :
2056 : /************************************************************************/
2057 : /* SetExtents() */
2058 : /************************************************************************/
2059 :
2060 60 : void OGRGMLDataSource::SetExtents(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY)
2061 : {
2062 60 : sBoundingRect.MinX = dfMinX;
2063 60 : sBoundingRect.MinY = dfMinY;
2064 60 : sBoundingRect.MaxX = dfMaxX;
2065 60 : sBoundingRect.MaxY = dfMaxY;
2066 60 : }
2067 :
2068 : /************************************************************************/
2069 : /* GetAppPrefix() */
2070 : /************************************************************************/
2071 :
2072 107 : const char* OGRGMLDataSource::GetAppPrefix()
2073 : {
2074 107 : return CSLFetchNameValueDef(papszCreateOptions, "PREFIX", "ogr");
2075 : }
|