1 : /******************************************************************************
2 : * $Id: ogrgmldatasource.cpp 23573 2011-12-14 12:13:48Z 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 "gmlutils.h"
42 : #include "ogr_p.h"
43 :
44 : #include <vector>
45 :
46 : CPL_CVSID("$Id: ogrgmldatasource.cpp 23573 2011-12-14 12:13:48Z rouault $");
47 :
48 : /************************************************************************/
49 : /* OGRGMLDataSource() */
50 : /************************************************************************/
51 :
52 308 : OGRGMLDataSource::OGRGMLDataSource()
53 :
54 : {
55 308 : pszName = NULL;
56 308 : papoLayers = NULL;
57 308 : nLayers = 0;
58 :
59 308 : poReader = NULL;
60 308 : fpOutput = NULL;
61 308 : bFpOutputIsNonSeekable = FALSE;
62 308 : bFpOutputSingleFile = FALSE;
63 308 : bIsOutputGML3 = FALSE;
64 308 : bIsOutputGML3Deegree = FALSE;
65 308 : bIsOutputGML32 = FALSE;
66 308 : bIsLongSRSRequired = FALSE;
67 308 : bWriteSpaceIndentation = TRUE;
68 :
69 308 : papszCreateOptions = NULL;
70 308 : bOutIsTempFile = FALSE;
71 :
72 308 : bExposeGMLId = FALSE;
73 308 : bExposeFid = FALSE;
74 308 : nSchemaInsertLocation = -1;
75 308 : nBoundedByLocation = -1;
76 308 : bBBOX3D = FALSE;
77 :
78 308 : poGlobalSRS = NULL;
79 308 : bIsWFS = FALSE;
80 :
81 308 : eReadMode = STANDARD;
82 308 : poStoredGMLFeature = NULL;
83 308 : poLastReadLayer = NULL;
84 :
85 308 : m_bInvertAxisOrderIfLatLong = FALSE;
86 308 : m_bConsiderEPSGAsURN = FALSE;
87 308 : m_bGetSecondaryGeometryOption = FALSE;
88 308 : }
89 :
90 : /************************************************************************/
91 : /* ~OGRGMLDataSource() */
92 : /************************************************************************/
93 :
94 308 : OGRGMLDataSource::~OGRGMLDataSource()
95 :
96 : {
97 :
98 308 : if( fpOutput != NULL )
99 : {
100 : PrintLine( fpOutput, "%s",
101 9 : "</ogr:FeatureCollection>" );
102 :
103 9 : if( bFpOutputIsNonSeekable)
104 : {
105 0 : VSIFCloseL( fpOutput );
106 0 : fpOutput = NULL;
107 : }
108 :
109 9 : InsertHeader();
110 :
111 9 : if( !bFpOutputIsNonSeekable
112 : && nBoundedByLocation != -1
113 : && sBoundingRect.IsInit()
114 : && VSIFSeekL( fpOutput, nBoundedByLocation, SEEK_SET ) == 0 )
115 : {
116 5 : if (IsGML3Output())
117 : {
118 4 : int bCoordSwap = FALSE;
119 : char* pszSRSName;
120 4 : if (poGlobalSRS)
121 4 : pszSRSName = GML_GetSRSName(poGlobalSRS, IsLongSRSRequired(), &bCoordSwap);
122 : else
123 0 : pszSRSName = CPLStrdup("");
124 : char szLowerCorner[75], szUpperCorner[75];
125 4 : if (bCoordSwap)
126 : {
127 3 : OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinY, sBoundingRect.MinX, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2);
128 3 : OGRMakeWktCoordinate(szUpperCorner, sBoundingRect.MaxY, sBoundingRect.MaxX, sBoundingRect.MaxZ, (bBBOX3D) ? 3 : 2);
129 : }
130 : else
131 : {
132 1 : OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinX, sBoundingRect.MinY, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2);
133 1 : OGRMakeWktCoordinate(szUpperCorner, sBoundingRect.MaxX, sBoundingRect.MaxY, sBoundingRect.MaxZ, (bBBOX3D) ? 3 : 2);
134 : }
135 4 : if (bWriteSpaceIndentation)
136 4 : VSIFPrintfL( fpOutput, " ");
137 : PrintLine( fpOutput, "<gml:boundedBy><gml:Envelope%s%s><gml:lowerCorner>%s</gml:lowerCorner><gml:upperCorner>%s</gml:upperCorner></gml:Envelope></gml:boundedBy>",
138 4 : (bBBOX3D) ? " srsDimension=\"3\"" : "", pszSRSName, szLowerCorner, szUpperCorner);
139 4 : CPLFree(pszSRSName);
140 : }
141 : else
142 : {
143 1 : if (bWriteSpaceIndentation)
144 1 : VSIFPrintfL( fpOutput, " ");
145 1 : PrintLine( fpOutput, "<gml:boundedBy>" );
146 1 : if (bWriteSpaceIndentation)
147 1 : VSIFPrintfL( fpOutput, " ");
148 1 : PrintLine( fpOutput, "<gml:Box>" );
149 1 : if (bWriteSpaceIndentation)
150 1 : VSIFPrintfL( fpOutput, " ");
151 : VSIFPrintfL( fpOutput,
152 : "<gml:coord><gml:X>%.16g</gml:X>"
153 : "<gml:Y>%.16g</gml:Y>",
154 1 : sBoundingRect.MinX, sBoundingRect.MinY );
155 1 : if (bBBOX3D)
156 : VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
157 1 : sBoundingRect.MinZ );
158 1 : PrintLine( fpOutput, "</gml:coord>");
159 1 : if (bWriteSpaceIndentation)
160 1 : VSIFPrintfL( fpOutput, " ");
161 : VSIFPrintfL( fpOutput,
162 : "<gml:coord><gml:X>%.16g</gml:X>"
163 : "<gml:Y>%.16g</gml:Y>",
164 1 : sBoundingRect.MaxX, sBoundingRect.MaxY );
165 1 : if (bBBOX3D)
166 : VSIFPrintfL( fpOutput, "<gml:Z>%.16g</gml:Z>",
167 1 : sBoundingRect.MaxZ );
168 1 : PrintLine( fpOutput, "</gml:coord>");
169 1 : if (bWriteSpaceIndentation)
170 1 : VSIFPrintfL( fpOutput, " ");
171 1 : PrintLine( fpOutput, "</gml:Box>" );
172 1 : if (bWriteSpaceIndentation)
173 1 : VSIFPrintfL( fpOutput, " ");
174 1 : PrintLine( fpOutput, "</gml:boundedBy>" );
175 : }
176 : }
177 :
178 9 : if (fpOutput)
179 9 : VSIFCloseL( fpOutput );
180 : }
181 :
182 308 : CSLDestroy( papszCreateOptions );
183 308 : CPLFree( pszName );
184 :
185 406 : for( int i = 0; i < nLayers; i++ )
186 98 : delete papoLayers[i];
187 :
188 308 : CPLFree( papoLayers );
189 :
190 308 : if( poReader )
191 : {
192 67 : if (bOutIsTempFile)
193 0 : VSIUnlink(poReader->GetSourceFileName());
194 67 : delete poReader;
195 : }
196 :
197 308 : delete poGlobalSRS;
198 :
199 308 : delete poStoredGMLFeature;
200 308 : }
201 :
202 : /************************************************************************/
203 : /* Open() */
204 : /************************************************************************/
205 :
206 299 : int OGRGMLDataSource::Open( const char * pszNewName, int bTestOpen )
207 :
208 : {
209 : VSILFILE *fp;
210 : char szHeader[2048];
211 299 : int nNumberOfFeatures = 0;
212 299 : CPLString osWithVsiGzip;
213 :
214 299 : pszName = CPLStrdup( pszNewName );
215 :
216 : /* -------------------------------------------------------------------- */
217 : /* Open the source file. */
218 : /* -------------------------------------------------------------------- */
219 299 : fp = VSIFOpenL( pszNewName, "r" );
220 299 : if( fp == NULL )
221 : {
222 67 : if( !bTestOpen )
223 : CPLError( CE_Failure, CPLE_OpenFailed,
224 : "Failed to open GML file `%s'.",
225 0 : pszNewName );
226 :
227 67 : return FALSE;
228 : }
229 :
230 232 : int bExpatCompatibleEncoding = FALSE;
231 232 : int bHas3D = FALSE;
232 232 : int bHintConsiderEPSGAsURN = FALSE;
233 :
234 : /* -------------------------------------------------------------------- */
235 : /* If we aren't sure it is GML, load a header chunk and check */
236 : /* for signs it is GML */
237 : /* -------------------------------------------------------------------- */
238 232 : if( bTestOpen )
239 : {
240 232 : size_t nRead = VSIFReadL( szHeader, 1, sizeof(szHeader), fp );
241 232 : if (nRead <= 0)
242 : {
243 4 : VSIFCloseL( fp );
244 4 : return FALSE;
245 : }
246 228 : szHeader[MIN(nRead, sizeof(szHeader))-1] = '\0';
247 :
248 : /* Might be a OS-Mastermap gzipped GML, so let be nice and try to open */
249 : /* it transparently with /vsigzip/ */
250 228 : if ( ((GByte*)szHeader)[0] == 0x1f && ((GByte*)szHeader)[1] == 0x8b &&
251 : EQUAL(CPLGetExtension(pszNewName), "gz") &&
252 : strncmp(pszNewName, "/vsigzip/", strlen("/vsigzip/")) != 0 )
253 : {
254 0 : VSIFCloseL( fp );
255 0 : osWithVsiGzip = "/vsigzip/";
256 0 : osWithVsiGzip += pszNewName;
257 :
258 0 : pszNewName = osWithVsiGzip;
259 :
260 0 : fp = VSIFOpenL( pszNewName, "r" );
261 0 : if( fp == NULL )
262 0 : return FALSE;
263 :
264 0 : nRead = VSIFReadL( szHeader, 1, sizeof(szHeader), fp );
265 0 : if (nRead <= 0)
266 : {
267 0 : VSIFCloseL( fp );
268 0 : return FALSE;
269 : }
270 0 : szHeader[MIN(nRead, sizeof(szHeader))-1] = '\0';
271 : }
272 :
273 : /* -------------------------------------------------------------------- */
274 : /* Check for a UTF-8 BOM and skip if found */
275 : /* */
276 : /* TODO: BOM is variable-lenght parameter and depends on encoding. */
277 : /* Add BOM detection for other encodings. */
278 : /* -------------------------------------------------------------------- */
279 :
280 : // Used to skip to actual beginning of XML data
281 228 : char* szPtr = szHeader;
282 :
283 230 : if( ( (unsigned char)szHeader[0] == 0xEF )
284 1 : && ( (unsigned char)szHeader[1] == 0xBB )
285 1 : && ( (unsigned char)szHeader[2] == 0xBF) )
286 : {
287 1 : szPtr += 3;
288 : }
289 :
290 228 : const char* pszEncoding = strstr(szPtr, "encoding=");
291 228 : if (pszEncoding)
292 158 : bExpatCompatibleEncoding = (pszEncoding[9] == '\'' || pszEncoding[9] == '"') &&
293 : (EQUALN(pszEncoding + 10, "UTF-8", 5) ||
294 239 : EQUALN(pszEncoding + 10, "ISO-8859-1", 10));
295 : else
296 147 : bExpatCompatibleEncoding = TRUE; /* utf-8 is the default */
297 :
298 228 : bHas3D = strstr(szPtr, "srsDimension=\"3\"") != NULL || strstr(szPtr, "<gml:Z>") != NULL;
299 :
300 : /* -------------------------------------------------------------------- */
301 : /* Here, we expect the opening chevrons of GML tree root element */
302 : /* -------------------------------------------------------------------- */
303 228 : if( szPtr[0] != '<'
304 : || strstr(szPtr,"opengis.net/gml") == NULL )
305 : {
306 157 : VSIFCloseL( fp );
307 157 : return FALSE;
308 : }
309 :
310 : /* Ignore GeoRSS documents. They will be recognized by the GeoRSS driver */
311 71 : if( strstr(szPtr, "<rss") != NULL && strstr(szPtr, "xmlns:georss") != NULL )
312 : {
313 4 : VSIFCloseL( fp );
314 4 : return FALSE;
315 : }
316 :
317 : /* Small optimization: if we parse a <wfs:FeatureCollection> and */
318 : /* that numberOfFeatures is set, we can use it to set the FeatureCount */
319 : /* but *ONLY* if there's just one class ! */
320 67 : const char* pszFeatureCollection = strstr(szPtr, "wfs:FeatureCollection");
321 67 : if (pszFeatureCollection == NULL)
322 45 : pszFeatureCollection = strstr(szPtr, "gml:FeatureCollection"); /* GML 3.2.1 output */
323 67 : if (pszFeatureCollection)
324 : {
325 32 : bExposeGMLId = TRUE;
326 32 : bIsWFS = TRUE;
327 32 : const char* pszNumberOfFeatures = strstr(szPtr, "numberOfFeatures=");
328 32 : if (pszNumberOfFeatures)
329 : {
330 14 : pszNumberOfFeatures += 17;
331 14 : char ch = pszNumberOfFeatures[0];
332 14 : if ((ch == '\'' || ch == '"') && strchr(pszNumberOfFeatures + 1, ch) != NULL)
333 : {
334 14 : nNumberOfFeatures = atoi(pszNumberOfFeatures + 1);
335 : }
336 : }
337 18 : else if ((pszNumberOfFeatures = strstr(szPtr, "numberReturned=")) != NULL) /* WFS 2.0.0 */
338 : {
339 2 : pszNumberOfFeatures += 15;
340 2 : char ch = pszNumberOfFeatures[0];
341 2 : if ((ch == '\'' || ch == '"') && strchr(pszNumberOfFeatures + 1, ch) != NULL)
342 : {
343 : /* 'unknown' might be a valid value in a corrected version of WFS 2.0 */
344 : /* but it will also evaluate to 0, that is considered as unknown, so nothing */
345 : /* particular to do */
346 2 : nNumberOfFeatures = atoi(pszNumberOfFeatures + 1);
347 : }
348 : }
349 : }
350 35 : else if (strncmp(pszNewName, "/vsimem/tempwfs_", strlen("/vsimem/tempwfs_")) == 0)
351 : {
352 : /* http://regis.intergraph.com/wfs/dcmetro/request.asp? returns a <G:FeatureCollection> */
353 : /* Who knows what servers can return ? Ok, so when in the context of the WFS driver */
354 : /* always expose the gml:id to avoid later crashes */
355 0 : bExposeGMLId = TRUE;
356 0 : bIsWFS = TRUE;
357 : }
358 : else
359 : {
360 : bExposeGMLId = strstr(szPtr, " gml:id=\"") != NULL ||
361 35 : strstr(szPtr, " gml:id='") != NULL;
362 : bExposeFid = strstr(szPtr, " fid=\"") != NULL ||
363 35 : strstr(szPtr, " fid='") != NULL;
364 :
365 35 : const char* pszExposeGMLId = CPLGetConfigOption("GML_EXPOSE_GML_ID", NULL);
366 35 : if (pszExposeGMLId)
367 0 : bExposeGMLId = CSLTestBoolean(pszExposeGMLId);
368 :
369 35 : const char* pszExposeFid = CPLGetConfigOption("GML_EXPOSE_FID", NULL);
370 35 : if (pszExposeFid)
371 1 : bExposeFid = CSLTestBoolean(pszExposeFid);
372 : }
373 :
374 67 : bHintConsiderEPSGAsURN = strstr(szPtr, "xmlns:fme=\"http://www.safe.com/gml/fme\"") != NULL;
375 : }
376 :
377 : /* -------------------------------------------------------------------- */
378 : /* We assume now that it is GML. Close and instantiate a */
379 : /* GMLReader on it. */
380 : /* -------------------------------------------------------------------- */
381 67 : VSIFCloseL( fp );
382 :
383 67 : const char* pszReadMode = CPLGetConfigOption("GML_READ_MODE", NULL);
384 131 : if (pszReadMode == NULL || EQUAL(pszReadMode, "STANDARD"))
385 64 : eReadMode = STANDARD;
386 3 : else if (EQUAL(pszReadMode, "SEQUENTIAL_LAYERS"))
387 2 : eReadMode = SEQUENTIAL_LAYERS;
388 1 : else if (EQUAL(pszReadMode, "INTERLEAVED_LAYERS"))
389 1 : eReadMode = INTERLEAVED_LAYERS;
390 : else
391 : {
392 0 : CPLDebug("GML", "Unrecognized value for GML_READ_MODE configuration option.");
393 : }
394 :
395 : m_bInvertAxisOrderIfLatLong = CSLTestBoolean(
396 67 : CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES"));
397 :
398 : const char* pszConsiderEPSGAsURN =
399 67 : CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", NULL);
400 67 : if (pszConsiderEPSGAsURN != NULL)
401 0 : m_bConsiderEPSGAsURN = CSLTestBoolean(pszConsiderEPSGAsURN);
402 67 : else if (bHintConsiderEPSGAsURN)
403 : {
404 : /* GML produced by FME (at least CanVec GML) seem to honour EPSG axis ordering */
405 5 : CPLDebug("GML", "FME-produced GML --> consider that GML_CONSIDER_EPSG_AS_URN is set to YES");
406 5 : m_bConsiderEPSGAsURN = TRUE;
407 : }
408 : else
409 62 : m_bConsiderEPSGAsURN = FALSE;
410 :
411 67 : m_bGetSecondaryGeometryOption = CSLTestBoolean(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO"));
412 :
413 : /* EXPAT is faster than Xerces, so when it is safe to use it, use it ! */
414 : /* The only interest of Xerces is for rare encodings that Expat doesn't handle */
415 : /* but UTF-8 is well handled by Expat */
416 67 : int bUseExpatParserPreferably = bExpatCompatibleEncoding;
417 :
418 : /* Override default choice */
419 67 : const char* pszGMLParser = CPLGetConfigOption("GML_PARSER", NULL);
420 67 : if (pszGMLParser)
421 : {
422 0 : if (EQUAL(pszGMLParser, "EXPAT"))
423 0 : bUseExpatParserPreferably = TRUE;
424 0 : else if (EQUAL(pszGMLParser, "XERCES"))
425 0 : bUseExpatParserPreferably = FALSE;
426 : }
427 :
428 : poReader = CreateGMLReader( bUseExpatParserPreferably,
429 : m_bInvertAxisOrderIfLatLong,
430 : m_bConsiderEPSGAsURN,
431 67 : m_bGetSecondaryGeometryOption );
432 67 : if( poReader == NULL )
433 : {
434 : CPLError( CE_Failure, CPLE_AppDefined,
435 : "File %s appears to be GML but the GML reader can't\n"
436 : "be instantiated, likely because Xerces or Expat support wasn't\n"
437 : "configured in.",
438 0 : pszNewName );
439 0 : return FALSE;
440 : }
441 :
442 67 : poReader->SetSourceFile( pszNewName );
443 :
444 : /* -------------------------------------------------------------------- */
445 : /* Resolve the xlinks in the source file and save it with the */
446 : /* extension ".resolved.gml". The source file will to set to that. */
447 : /* -------------------------------------------------------------------- */
448 :
449 67 : char *pszXlinkResolvedFilename = NULL;
450 67 : const char *pszOption = CPLGetConfigOption("GML_SAVE_RESOLVED_TO", NULL);
451 67 : int bResolve = TRUE;
452 67 : int bHugeFile = FALSE;
453 67 : if( pszOption != NULL && EQUALN( pszOption, "SAME", 4 ) )
454 : {
455 : // "SAME" will overwrite the existing gml file
456 0 : pszXlinkResolvedFilename = CPLStrdup( pszNewName );
457 : }
458 67 : else if( pszOption != NULL &&
459 : CPLStrnlen( pszOption, 5 ) >= 5 &&
460 : EQUALN( pszOption - 4 + strlen( pszOption ), ".gml", 4 ) )
461 : {
462 : // Any string ending with ".gml" will try and write to it
463 2 : pszXlinkResolvedFilename = CPLStrdup( pszOption );
464 : }
465 : else
466 : {
467 : // When no option is given or is not recognised,
468 : // use the same file name with the extension changed to .resolved.gml
469 : pszXlinkResolvedFilename = CPLStrdup(
470 65 : CPLResetExtension( pszNewName, "resolved.gml" ) );
471 :
472 : // Check if the file already exists.
473 : VSIStatBufL sResStatBuf, sGMLStatBuf;
474 65 : if( VSIStatL( pszXlinkResolvedFilename, &sResStatBuf ) == 0 )
475 : {
476 2 : VSIStatL( pszNewName, &sGMLStatBuf );
477 2 : if( sGMLStatBuf.st_mtime > sResStatBuf.st_mtime )
478 : {
479 : CPLDebug( "GML",
480 : "Found %s but ignoring because it appears\n"
481 : "be older than the associated GML file.",
482 0 : pszXlinkResolvedFilename );
483 : }
484 : else
485 : {
486 2 : poReader->SetSourceFile( pszXlinkResolvedFilename );
487 2 : bResolve = FALSE;
488 : }
489 : }
490 : }
491 :
492 : const char *pszSkipOption = CPLGetConfigOption( "GML_SKIP_RESOLVE_ELEMS",
493 67 : "ALL");
494 67 : char **papszSkip = NULL;
495 67 : if( EQUAL( pszSkipOption, "ALL" ) )
496 63 : bResolve = FALSE;
497 4 : else if( EQUAL( pszSkipOption, "HUGE" ) )//exactly as NONE, but intended for HUGE files
498 1 : bHugeFile = TRUE;
499 3 : else if( !EQUAL( pszSkipOption, "NONE" ) )//use this to resolve everything
500 : papszSkip = CSLTokenizeString2( pszSkipOption, ",",
501 : CSLT_STRIPLEADSPACES |
502 1 : CSLT_STRIPENDSPACES );
503 : const char *pszGFSFilename;
504 : VSIStatBufL sGFSStatBuf, sGMLStatBuf;
505 67 : int bHaveSchema = FALSE;
506 67 : int bSchemaDone = FALSE;
507 :
508 : /* -------------------------------------------------------------------- */
509 : /* Is some GML Feature Schema (.gfs) TEMPLATE required ? */
510 : /* -------------------------------------------------------------------- */
511 : const char *pszGFSTemplateName =
512 67 : CPLGetConfigOption( "GML_GFS_TEMPLATE", NULL);
513 67 : if( pszGFSTemplateName != NULL )
514 : {
515 : /* attempting to load the GFS TEMPLATE */
516 0 : bHaveSchema = poReader->LoadClasses( pszGFSTemplateName );
517 : }
518 :
519 67 : if( bResolve )
520 : {
521 4 : if ( bHugeFile )
522 : {
523 1 : bSchemaDone = TRUE;
524 : int bSqliteIsTempFile =
525 1 : CSLTestBoolean(CPLGetConfigOption( "GML_HUGE_TEMPFILE", "YES"));
526 1 : int iSqliteCacheMB = atoi(CPLGetConfigOption( "OGR_SQLITE_CACHE", "0"));
527 2 : if( poReader->HugeFileResolver( pszXlinkResolvedFilename,
528 : bSqliteIsTempFile,
529 1 : iSqliteCacheMB ) == FALSE )
530 : {
531 : // we assume an errors have been reported.
532 0 : return FALSE;
533 : }
534 : }
535 : else
536 : {
537 : poReader->ResolveXlinks( pszXlinkResolvedFilename,
538 : &bOutIsTempFile,
539 3 : papszSkip );
540 : }
541 : }
542 :
543 67 : CPLFree( pszXlinkResolvedFilename );
544 67 : pszXlinkResolvedFilename = NULL;
545 67 : CSLDestroy( papszSkip );
546 67 : papszSkip = NULL;
547 :
548 : /* Is a prescan required ? */
549 67 : if( bHaveSchema && !bSchemaDone )
550 : {
551 : /* We must detect which layers are actually present in the .gml */
552 : /* and how many features they have */
553 0 : if( !poReader->PrescanForTemplate() )
554 : {
555 : // we assume an errors have been reported.
556 0 : return FALSE;
557 : }
558 : }
559 :
560 : /* -------------------------------------------------------------------- */
561 : /* Can we find a GML Feature Schema (.gfs) for the input file? */
562 : /* -------------------------------------------------------------------- */
563 67 : if( !bHaveSchema )
564 : {
565 67 : pszGFSFilename = CPLResetExtension( pszNewName, "gfs" );
566 67 : if (strncmp(pszGFSFilename, "/vsigzip/", strlen("/vsigzip/")) == 0)
567 0 : pszGFSFilename += strlen("/vsigzip/");
568 67 : if( VSIStatL( pszGFSFilename, &sGFSStatBuf ) == 0 )
569 : {
570 15 : VSIStatL( pszNewName, &sGMLStatBuf );
571 15 : if( sGMLStatBuf.st_mtime > sGFSStatBuf.st_mtime )
572 : {
573 : CPLDebug( "GML",
574 : "Found %s but ignoring because it appears\n"
575 : "be older than the associated GML file.",
576 0 : pszGFSFilename );
577 : }
578 : else
579 : {
580 15 : bHaveSchema = poReader->LoadClasses( pszGFSFilename );
581 15 : if (bHaveSchema)
582 : {
583 : const char *pszXSDFilename;
584 15 : pszXSDFilename = CPLResetExtension( pszNewName, "xsd" );
585 15 : if( VSIStatExL( pszXSDFilename, &sGMLStatBuf,
586 : VSI_STAT_EXISTS_FLAG ) == 0 )
587 : {
588 : CPLDebug("GML", "Using %s file, ignoring %s",
589 0 : pszGFSFilename, pszXSDFilename);
590 : }
591 : }
592 : }
593 : }
594 : }
595 :
596 : /* -------------------------------------------------------------------- */
597 : /* Can we find an xsd which might conform to tbe GML3 Level 0 */
598 : /* profile? We really ought to look for it based on the rules */
599 : /* schemaLocation in the GML feature collection but for now we */
600 : /* just hopes it is in the same director with the same name. */
601 : /* -------------------------------------------------------------------- */
602 : const char *pszXSDFilename;
603 67 : int bHasFoundXSD = FALSE;
604 :
605 67 : if( !bHaveSchema )
606 : {
607 52 : pszXSDFilename = CPLResetExtension( pszNewName, "xsd" );
608 52 : if( VSIStatL( pszXSDFilename, &sGMLStatBuf ) == 0 )
609 : {
610 31 : bHasFoundXSD = TRUE;
611 :
612 31 : std::vector<GMLFeatureClass*> aosClasses;
613 31 : bHaveSchema = GMLParseXSD( pszXSDFilename, aosClasses );
614 31 : if (bHaveSchema)
615 : {
616 31 : std::vector<GMLFeatureClass*>::const_iterator iter = aosClasses.begin();
617 31 : std::vector<GMLFeatureClass*>::const_iterator eiter = aosClasses.end();
618 99 : while (iter != eiter)
619 : {
620 37 : GMLFeatureClass* poClass = *iter;
621 37 : iter ++;
622 :
623 : /* We have no way of knowing if the geometry type is 25D */
624 : /* when examining the xsd only, so if there was a hint */
625 : /* it is, we force to 25D */
626 37 : if (bHas3D)
627 : {
628 : poClass->SetGeometryType(
629 2 : poClass->GetGeometryType() | wkb25DBit);
630 : }
631 37 : poReader->AddClass( poClass );
632 : }
633 31 : poReader->SetClassListLocked( TRUE );
634 31 : }
635 : }
636 : }
637 :
638 : /* -------------------------------------------------------------------- */
639 : /* Force a first pass to establish the schema. Eventually we */
640 : /* will have mechanisms for remembering the schema and related */
641 : /* information. */
642 : /* -------------------------------------------------------------------- */
643 67 : if( !bHaveSchema )
644 : {
645 21 : if( !poReader->PrescanForSchema( TRUE ) )
646 : {
647 : // we assume an errors have been reported.
648 1 : return FALSE;
649 : }
650 :
651 20 : if( bHasFoundXSD )
652 : {
653 : CPLDebug("GML", "Generating %s file, ignoring %s",
654 0 : pszGFSFilename, pszXSDFilename);
655 : }
656 : }
657 :
658 66 : if (poReader->GetClassCount() > 1 && poReader->IsSequentialLayers() &&
659 : pszReadMode == NULL)
660 : {
661 7 : CPLDebug("GML", "Layers are monoblock. Using SEQUENTIAL_LAYERS read mode");
662 7 : eReadMode = SEQUENTIAL_LAYERS;
663 : }
664 :
665 : /* -------------------------------------------------------------------- */
666 : /* Save the schema file if possible. Don't make a fuss if we */
667 : /* can't ... could be read-only directory or something. */
668 : /* -------------------------------------------------------------------- */
669 66 : if( !bHaveSchema && !poReader->HasStoppedParsing() &&
670 : !EQUALN(pszNewName, "/vsitar/", strlen("/vsitar/")) &&
671 : !EQUALN(pszNewName, "/vsizip/", strlen("/vsizip/")) &&
672 : !EQUALN(pszNewName, "/vsigzip/vsi", strlen("/vsigzip/vsi")) &&
673 : !EQUALN(pszNewName, "/vsigzip//vsi", strlen("/vsigzip//vsi")) &&
674 : !EQUALN(pszNewName, "/vsicurl/", strlen("/vsicurl/")))
675 : {
676 20 : VSILFILE *fp = NULL;
677 :
678 20 : pszGFSFilename = CPLResetExtension( pszNewName, "gfs" );
679 20 : if (strncmp(pszGFSFilename, "/vsigzip/", strlen("/vsigzip/")) == 0)
680 0 : pszGFSFilename += strlen("/vsigzip/");
681 :
682 20 : if( VSIStatL( pszGFSFilename, &sGFSStatBuf ) != 0
683 : && (fp = VSIFOpenL( pszGFSFilename, "wt" )) != NULL )
684 : {
685 20 : VSIFCloseL( fp );
686 20 : poReader->SaveClasses( pszGFSFilename );
687 : }
688 : else
689 : {
690 : CPLDebug("GML",
691 : "Not saving %s files already exists or can't be created.",
692 0 : pszGFSFilename );
693 : }
694 : }
695 :
696 : /* -------------------------------------------------------------------- */
697 : /* Translate the GMLFeatureClasses into layers. */
698 : /* -------------------------------------------------------------------- */
699 : papoLayers = (OGRGMLLayer **)
700 66 : CPLCalloc( sizeof(OGRGMLLayer *), poReader->GetClassCount());
701 66 : nLayers = 0;
702 :
703 66 : if (poReader->GetClassCount() == 1 && nNumberOfFeatures != 0)
704 : {
705 14 : GMLFeatureClass *poClass = poReader->GetClass(0);
706 14 : int nFeatureCount = poClass->GetFeatureCount();
707 14 : if (nFeatureCount < 0)
708 : {
709 12 : poClass->SetFeatureCount(nNumberOfFeatures);
710 : }
711 2 : else if (nFeatureCount != nNumberOfFeatures)
712 : {
713 0 : CPLDebug("GML", "Feature count in header, and actual feature count don't match");
714 : }
715 : }
716 :
717 221 : while( nLayers < poReader->GetClassCount() )
718 : {
719 89 : papoLayers[nLayers] = TranslateGMLSchema(poReader->GetClass(nLayers));
720 89 : nLayers++;
721 : }
722 :
723 :
724 :
725 66 : return TRUE;
726 : }
727 :
728 : /************************************************************************/
729 : /* TranslateGMLSchema() */
730 : /************************************************************************/
731 :
732 89 : OGRGMLLayer *OGRGMLDataSource::TranslateGMLSchema( GMLFeatureClass *poClass )
733 :
734 : {
735 : OGRGMLLayer *poLayer;
736 : OGRwkbGeometryType eGType
737 89 : = (OGRwkbGeometryType) poClass->GetGeometryType();
738 :
739 89 : if( poClass->GetFeatureCount() == 0 )
740 0 : eGType = wkbUnknown;
741 :
742 : /* -------------------------------------------------------------------- */
743 : /* Create an empty layer. */
744 : /* -------------------------------------------------------------------- */
745 :
746 89 : const char* pszSRSName = poClass->GetSRSName();
747 89 : OGRSpatialReference* poSRS = NULL;
748 89 : if (pszSRSName)
749 : {
750 21 : poSRS = new OGRSpatialReference();
751 21 : if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE)
752 : {
753 0 : delete poSRS;
754 0 : poSRS = NULL;
755 : }
756 : }
757 :
758 : poLayer = new OGRGMLLayer( poClass->GetName(), poSRS, FALSE,
759 89 : eGType, this );
760 110 : delete poSRS;
761 :
762 : /* -------------------------------------------------------------------- */
763 : /* Added attributes (properties). */
764 : /* -------------------------------------------------------------------- */
765 89 : if (bExposeGMLId)
766 : {
767 64 : OGRFieldDefn oField( "gml_id", OFTString );
768 64 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
769 : }
770 25 : else if (bExposeFid)
771 : {
772 18 : OGRFieldDefn oField( "fid", OFTString );
773 18 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
774 : }
775 :
776 338 : for( int iField = 0; iField < poClass->GetPropertyCount(); iField++ )
777 : {
778 249 : GMLPropertyDefn *poProperty = poClass->GetProperty( iField );
779 : OGRFieldType eFType;
780 :
781 249 : if( poProperty->GetType() == GMLPT_Untyped )
782 0 : eFType = OFTString;
783 249 : else if( poProperty->GetType() == GMLPT_String )
784 112 : eFType = OFTString;
785 137 : else if( poProperty->GetType() == GMLPT_Integer )
786 89 : eFType = OFTInteger;
787 48 : else if( poProperty->GetType() == GMLPT_Real )
788 36 : eFType = OFTReal;
789 12 : else if( poProperty->GetType() == GMLPT_StringList )
790 6 : eFType = OFTStringList;
791 6 : else if( poProperty->GetType() == GMLPT_IntegerList )
792 3 : eFType = OFTIntegerList;
793 3 : else if( poProperty->GetType() == GMLPT_RealList )
794 3 : eFType = OFTRealList;
795 : else
796 0 : eFType = OFTString;
797 :
798 249 : OGRFieldDefn oField( poProperty->GetName(), eFType );
799 249 : if ( EQUALN(oField.GetNameRef(), "ogr:", 4) )
800 0 : oField.SetName(poProperty->GetName()+4);
801 249 : if( poProperty->GetWidth() > 0 )
802 74 : oField.SetWidth( poProperty->GetWidth() );
803 249 : if( poProperty->GetPrecision() > 0 )
804 5 : oField.SetPrecision( poProperty->GetPrecision() );
805 :
806 249 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
807 : }
808 :
809 89 : return poLayer;
810 : }
811 :
812 : /************************************************************************/
813 : /* GetGlobalSRSName() */
814 : /************************************************************************/
815 :
816 307 : const char *OGRGMLDataSource::GetGlobalSRSName()
817 : {
818 307 : if (poReader->CanUseGlobalSRSName() || bIsWFS)
819 232 : return poReader->GetGlobalSRSName();
820 : else
821 75 : return NULL;
822 : }
823 :
824 : /************************************************************************/
825 : /* Create() */
826 : /************************************************************************/
827 :
828 9 : int OGRGMLDataSource::Create( const char *pszFilename,
829 : char **papszOptions )
830 :
831 : {
832 9 : if( fpOutput != NULL || poReader != NULL )
833 : {
834 0 : CPLAssert( FALSE );
835 0 : return FALSE;
836 : }
837 :
838 9 : if( strcmp(pszFilename,"/dev/stdout") == 0 )
839 0 : pszFilename = "/vsistdout/";
840 :
841 : /* -------------------------------------------------------------------- */
842 : /* Read options */
843 : /* -------------------------------------------------------------------- */
844 :
845 9 : CSLDestroy(papszCreateOptions);
846 9 : papszCreateOptions = CSLDuplicate(papszOptions);
847 :
848 9 : const char* pszFormat = CSLFetchNameValue(papszCreateOptions, "FORMAT");
849 9 : bIsOutputGML3 = pszFormat && EQUAL(pszFormat, "GML3");
850 9 : bIsOutputGML3Deegree = pszFormat && EQUAL(pszFormat, "GML3Deegree");
851 9 : bIsOutputGML32 = pszFormat && EQUAL(pszFormat, "GML3.2");
852 9 : if (bIsOutputGML3Deegree || bIsOutputGML32)
853 2 : bIsOutputGML3 = TRUE;
854 :
855 : bIsLongSRSRequired =
856 9 : CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "GML3_LONGSRS", "YES"));
857 :
858 : bWriteSpaceIndentation =
859 9 : CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "SPACE_INDENTATION", "YES"));
860 :
861 : /* -------------------------------------------------------------------- */
862 : /* Create the output file. */
863 : /* -------------------------------------------------------------------- */
864 9 : pszName = CPLStrdup( pszFilename );
865 :
866 9 : if( strcmp(pszFilename,"/vsistdout/") == 0 ||
867 : strncmp(pszFilename,"/vsigzip/", 9) == 0 )
868 : {
869 0 : fpOutput = VSIFOpenL(pszFilename, "wb");
870 0 : bFpOutputIsNonSeekable = TRUE;
871 0 : bFpOutputSingleFile = TRUE;
872 : }
873 9 : else if ( strncmp(pszFilename,"/vsizip/", 8) == 0)
874 : {
875 0 : if (EQUAL(CPLGetExtension(pszFilename), "zip"))
876 : {
877 0 : CPLFree(pszName);
878 0 : pszName = CPLStrdup(CPLFormFilename(pszFilename, "out.gml", NULL));
879 : }
880 :
881 0 : fpOutput = VSIFOpenL(pszName, "wb");
882 0 : bFpOutputIsNonSeekable = TRUE;
883 : }
884 : else
885 9 : fpOutput = VSIFOpenL( pszFilename, "wb+" );
886 9 : if( fpOutput == NULL )
887 : {
888 : CPLError( CE_Failure, CPLE_OpenFailed,
889 : "Failed to create GML file %s.",
890 0 : pszFilename );
891 0 : return FALSE;
892 : }
893 :
894 : /* -------------------------------------------------------------------- */
895 : /* Write out "standard" header. */
896 : /* -------------------------------------------------------------------- */
897 : PrintLine( fpOutput, "%s",
898 9 : "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" );
899 :
900 9 : if (!bFpOutputIsNonSeekable)
901 9 : nSchemaInsertLocation = (int) VSIFTellL( fpOutput );
902 :
903 : PrintLine( fpOutput, "%s",
904 9 : "<ogr:FeatureCollection" );
905 :
906 9 : if (IsGML32Output())
907 : PrintLine( fpOutput, "%s",
908 1 : " gml:id=\"aFeatureCollection\"" );
909 :
910 : /* -------------------------------------------------------------------- */
911 : /* Write out schema info if provided in creation options. */
912 : /* -------------------------------------------------------------------- */
913 9 : const char *pszSchemaURI = CSLFetchNameValue(papszOptions,"XSISCHEMAURI");
914 9 : const char *pszSchemaOpt = CSLFetchNameValue( papszOptions, "XSISCHEMA" );
915 :
916 9 : if( pszSchemaURI != NULL )
917 : {
918 : PrintLine( fpOutput,
919 0 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
920 : PrintLine( fpOutput,
921 : " xsi:schemaLocation=\"%s\"",
922 0 : CSLFetchNameValue( papszOptions, "XSISCHEMAURI" ) );
923 : }
924 9 : else if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
925 : {
926 9 : char *pszBasename = CPLStrdup(CPLGetBasename( pszName ));
927 :
928 : PrintLine( fpOutput,
929 9 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
930 : PrintLine( fpOutput,
931 : " xsi:schemaLocation=\"http://ogr.maptools.org/ %s\"",
932 9 : CPLResetExtension( pszBasename, "xsd" ) );
933 9 : CPLFree( pszBasename );
934 : }
935 :
936 : PrintLine( fpOutput, "%s",
937 9 : " xmlns:ogr=\"http://ogr.maptools.org/\"" );
938 9 : if (IsGML32Output())
939 : PrintLine( fpOutput, "%s",
940 1 : " xmlns:gml=\"http://www.opengis.net/gml/3.2\">" );
941 : else
942 : PrintLine( fpOutput, "%s",
943 8 : " xmlns:gml=\"http://www.opengis.net/gml\">" );
944 :
945 : /* -------------------------------------------------------------------- */
946 : /* Should we initialize an area to place the boundedBy element? */
947 : /* We will need to seek back to fill it in. */
948 : /* -------------------------------------------------------------------- */
949 9 : if( CSLFetchBoolean( papszOptions, "BOUNDEDBY", TRUE ) &&
950 : !bFpOutputIsNonSeekable )
951 : {
952 9 : nBoundedByLocation = (int) VSIFTellL( fpOutput );
953 :
954 9 : if( nBoundedByLocation != -1 )
955 9 : PrintLine( fpOutput, "%350s", "" );
956 : }
957 : else
958 0 : nBoundedByLocation = -1;
959 :
960 9 : return TRUE;
961 : }
962 :
963 : /************************************************************************/
964 : /* CreateLayer() */
965 : /************************************************************************/
966 :
967 : OGRLayer *
968 9 : OGRGMLDataSource::CreateLayer( const char * pszLayerName,
969 : OGRSpatialReference *poSRS,
970 : OGRwkbGeometryType eType,
971 : char ** papszOptions )
972 :
973 : {
974 : /* -------------------------------------------------------------------- */
975 : /* Verify we are in update mode. */
976 : /* -------------------------------------------------------------------- */
977 9 : if( fpOutput == NULL )
978 : {
979 : CPLError( CE_Failure, CPLE_NoWriteAccess,
980 : "Data source %s opened for read access.\n"
981 : "New layer %s cannot be created.\n",
982 0 : pszName, pszLayerName );
983 :
984 0 : return NULL;
985 : }
986 :
987 : /* -------------------------------------------------------------------- */
988 : /* Ensure name is safe as an element name. */
989 : /* -------------------------------------------------------------------- */
990 9 : char *pszCleanLayerName = CPLStrdup( pszLayerName );
991 :
992 9 : CPLCleanXMLElementName( pszCleanLayerName );
993 9 : if( strcmp(pszCleanLayerName,pszLayerName) != 0 )
994 : {
995 : CPLError( CE_Warning, CPLE_AppDefined,
996 : "Layer name '%s' adjusted to '%s' for XML validity.",
997 0 : pszLayerName, pszCleanLayerName );
998 : }
999 :
1000 : /* -------------------------------------------------------------------- */
1001 : /* Set or check validity of global SRS. */
1002 : /* -------------------------------------------------------------------- */
1003 9 : if (nLayers == 0)
1004 : {
1005 9 : if (poSRS)
1006 5 : poGlobalSRS = poSRS->Clone();
1007 : }
1008 : else
1009 : {
1010 0 : if (poSRS == NULL ||
1011 : (poGlobalSRS != NULL && poSRS->IsSame(poGlobalSRS)))
1012 : {
1013 0 : delete poGlobalSRS;
1014 0 : poGlobalSRS = NULL;
1015 : }
1016 : }
1017 :
1018 : /* -------------------------------------------------------------------- */
1019 : /* Create the layer object. */
1020 : /* -------------------------------------------------------------------- */
1021 : OGRGMLLayer *poLayer;
1022 :
1023 9 : poLayer = new OGRGMLLayer( pszCleanLayerName, poSRS, TRUE, eType, this );
1024 :
1025 9 : CPLFree( pszCleanLayerName );
1026 :
1027 : /* -------------------------------------------------------------------- */
1028 : /* Add layer to data source layer list. */
1029 : /* -------------------------------------------------------------------- */
1030 : papoLayers = (OGRGMLLayer **)
1031 9 : CPLRealloc( papoLayers, sizeof(OGRGMLLayer *) * (nLayers+1) );
1032 :
1033 9 : papoLayers[nLayers++] = poLayer;
1034 :
1035 9 : return poLayer;
1036 : }
1037 :
1038 : /************************************************************************/
1039 : /* TestCapability() */
1040 : /************************************************************************/
1041 :
1042 3 : int OGRGMLDataSource::TestCapability( const char * pszCap )
1043 :
1044 : {
1045 3 : if( EQUAL(pszCap,ODsCCreateLayer) )
1046 3 : return TRUE;
1047 : else
1048 0 : return FALSE;
1049 : }
1050 :
1051 : /************************************************************************/
1052 : /* GetLayer() */
1053 : /************************************************************************/
1054 :
1055 115 : OGRLayer *OGRGMLDataSource::GetLayer( int iLayer )
1056 :
1057 : {
1058 115 : if( iLayer < 0 || iLayer >= nLayers )
1059 0 : return NULL;
1060 : else
1061 115 : return papoLayers[iLayer];
1062 : }
1063 :
1064 : /************************************************************************/
1065 : /* GrowExtents() */
1066 : /************************************************************************/
1067 :
1068 26 : void OGRGMLDataSource::GrowExtents( OGREnvelope3D *psGeomBounds, int nCoordDimension )
1069 :
1070 : {
1071 26 : sBoundingRect.Merge( *psGeomBounds );
1072 26 : if (nCoordDimension == 3)
1073 20 : bBBOX3D = TRUE;
1074 26 : }
1075 :
1076 : /************************************************************************/
1077 : /* InsertHeader() */
1078 : /* */
1079 : /* This method is used to update boundedby info for a */
1080 : /* dataset, and insert schema descriptions depending on */
1081 : /* selection options in effect. */
1082 : /************************************************************************/
1083 :
1084 9 : void OGRGMLDataSource::InsertHeader()
1085 :
1086 : {
1087 : VSILFILE *fpSchema;
1088 9 : int nSchemaStart = 0;
1089 :
1090 9 : if( bFpOutputSingleFile )
1091 0 : return;
1092 :
1093 : /* -------------------------------------------------------------------- */
1094 : /* Do we want to write the schema within the GML instance doc */
1095 : /* or to a separate file? For now we only support external. */
1096 : /* -------------------------------------------------------------------- */
1097 : const char *pszSchemaURI = CSLFetchNameValue(papszCreateOptions,
1098 9 : "XSISCHEMAURI");
1099 : const char *pszSchemaOpt = CSLFetchNameValue( papszCreateOptions,
1100 9 : "XSISCHEMA" );
1101 :
1102 9 : if( pszSchemaURI != NULL )
1103 0 : return;
1104 :
1105 18 : if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
1106 : {
1107 9 : const char *pszXSDFilename = CPLResetExtension( pszName, "xsd" );
1108 :
1109 9 : fpSchema = VSIFOpenL( pszXSDFilename, "wt" );
1110 9 : if( fpSchema == NULL )
1111 : {
1112 : CPLError( CE_Failure, CPLE_OpenFailed,
1113 : "Failed to open file %.500s for schema output.",
1114 0 : pszXSDFilename );
1115 0 : return;
1116 : }
1117 9 : PrintLine( fpSchema, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" );
1118 : }
1119 0 : else if( EQUAL(pszSchemaOpt,"INTERNAL") )
1120 : {
1121 0 : if (fpOutput == NULL)
1122 0 : return;
1123 0 : nSchemaStart = (int) VSIFTellL( fpOutput );
1124 0 : fpSchema = fpOutput;
1125 : }
1126 : else
1127 0 : return;
1128 :
1129 : /* ==================================================================== */
1130 : /* Write the schema section at the end of the file. Once */
1131 : /* complete, we will read it back in, and then move the whole */
1132 : /* file "down" enough to insert the schema at the beginning. */
1133 : /* ==================================================================== */
1134 :
1135 : /* -------------------------------------------------------------------- */
1136 : /* Emit the start of the schema section. */
1137 : /* -------------------------------------------------------------------- */
1138 9 : const char *pszTargetNameSpace = "http://ogr.maptools.org/";
1139 9 : const char *pszPrefix = "ogr";
1140 :
1141 9 : if (IsGML3Output())
1142 : {
1143 : PrintLine( fpSchema,
1144 4 : "<xs:schema ");
1145 : PrintLine( fpSchema,
1146 4 : " targetNamespace=\"%s\"", pszTargetNameSpace );
1147 : PrintLine( fpSchema,
1148 : " xmlns:%s=\"%s\"",
1149 4 : pszPrefix, pszTargetNameSpace );
1150 : PrintLine( fpSchema,
1151 4 : " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"");
1152 4 : if (IsGML32Output())
1153 : {
1154 : PrintLine( fpSchema,
1155 1 : " xmlns:gml=\"http://www.opengis.net/gml/3.2\"");
1156 : PrintLine( fpSchema,
1157 1 : " xmlns:gmlsf=\"http://www.opengis.net/gmlsf/2.0\"");
1158 : }
1159 : else
1160 : {
1161 : PrintLine( fpSchema,
1162 3 : " xmlns:gml=\"http://www.opengis.net/gml\"");
1163 3 : if (!IsGML3DeegreeOutput())
1164 : {
1165 : PrintLine( fpSchema,
1166 2 : " xmlns:gmlsf=\"http://www.opengis.net/gmlsf\"");
1167 : }
1168 : }
1169 : PrintLine( fpSchema,
1170 4 : " elementFormDefault=\"qualified\"");
1171 : PrintLine( fpSchema,
1172 4 : " version=\"1.0\">");
1173 :
1174 4 : if (IsGML32Output())
1175 : {
1176 : PrintLine( fpSchema,
1177 1 : "<xs:annotation>");
1178 : PrintLine( fpSchema,
1179 1 : " <xs:appinfo source=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\">");
1180 : PrintLine( fpSchema,
1181 1 : " <gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>");
1182 : PrintLine( fpSchema,
1183 1 : " </xs:appinfo>");
1184 : PrintLine( fpSchema,
1185 1 : "</xs:annotation>");
1186 :
1187 : PrintLine( fpSchema,
1188 1 : "<xs:import namespace=\"http://www.opengis.net/gml/3.2\" schemaLocation=\"http://schemas.opengis.net/gml/3.2.1/gml.xsd\"/>" );
1189 : PrintLine( fpSchema,
1190 1 : "<xs:import namespace=\"http://www.opengis.net/gmlsf/2.0\" schemaLocation=\"http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd\"/>" );
1191 : }
1192 : else
1193 : {
1194 3 : if (!IsGML3DeegreeOutput())
1195 : {
1196 : PrintLine( fpSchema,
1197 2 : "<xs:annotation>");
1198 : PrintLine( fpSchema,
1199 2 : " <xs:appinfo source=\"http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsfLevels.xsd\">");
1200 : PrintLine( fpSchema,
1201 2 : " <gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>");
1202 : PrintLine( fpSchema,
1203 2 : " <gmlsf:GMLProfileSchema>http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd</gmlsf:GMLProfileSchema>");
1204 : PrintLine( fpSchema,
1205 2 : " </xs:appinfo>");
1206 : PrintLine( fpSchema,
1207 2 : "</xs:annotation>");
1208 : }
1209 :
1210 : PrintLine( fpSchema,
1211 3 : "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/3.1.1/base/gml.xsd\"/>" );
1212 3 : if (!IsGML3DeegreeOutput())
1213 : {
1214 : PrintLine( fpSchema,
1215 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\"/>" );
1216 : }
1217 : }
1218 : }
1219 : else
1220 : {
1221 : PrintLine( fpSchema,
1222 : "<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\">",
1223 5 : pszTargetNameSpace, pszPrefix, pszTargetNameSpace );
1224 :
1225 : PrintLine( fpSchema,
1226 5 : "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengis.net/gml/2.1.2/feature.xsd\"/>" );
1227 : }
1228 :
1229 : /* -------------------------------------------------------------------- */
1230 : /* Define the FeatureCollection */
1231 : /* -------------------------------------------------------------------- */
1232 9 : if (IsGML3Output())
1233 : {
1234 4 : if (IsGML32Output())
1235 : {
1236 : PrintLine( fpSchema,
1237 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:AbstractGML\"/>",
1238 1 : pszPrefix );
1239 : }
1240 3 : else if (IsGML3DeegreeOutput())
1241 : {
1242 : PrintLine( fpSchema,
1243 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
1244 1 : pszPrefix );
1245 : }
1246 : else
1247 : {
1248 : PrintLine( fpSchema,
1249 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_GML\"/>",
1250 2 : pszPrefix );
1251 : }
1252 :
1253 4 : PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
1254 4 : PrintLine( fpSchema, " <xs:complexContent>" );
1255 4 : if (IsGML3DeegreeOutput())
1256 : {
1257 1 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
1258 1 : PrintLine( fpSchema, " <xs:sequence>" );
1259 1 : PrintLine( fpSchema, " <xs:element name=\"featureMember\" minOccurs=\"0\" maxOccurs=\"unbounded\">" );
1260 : }
1261 : else
1262 : {
1263 3 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureType\">" );
1264 3 : PrintLine( fpSchema, " <xs:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">" );
1265 3 : PrintLine( fpSchema, " <xs:element name=\"featureMember\">" );
1266 : }
1267 4 : PrintLine( fpSchema, " <xs:complexType>" );
1268 4 : if (IsGML32Output())
1269 : {
1270 1 : PrintLine( fpSchema, " <xs:complexContent>" );
1271 1 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureMemberType\">" );
1272 1 : PrintLine( fpSchema, " <xs:sequence>" );
1273 1 : PrintLine( fpSchema, " <xs:element ref=\"gml:AbstractFeature\"/>" );
1274 1 : PrintLine( fpSchema, " </xs:sequence>" );
1275 1 : PrintLine( fpSchema, " </xs:extension>" );
1276 1 : PrintLine( fpSchema, " </xs:complexContent>" );
1277 : }
1278 : else
1279 : {
1280 3 : PrintLine( fpSchema, " <xs:sequence>" );
1281 3 : PrintLine( fpSchema, " <xs:element ref=\"gml:_Feature\"/>" );
1282 3 : PrintLine( fpSchema, " </xs:sequence>" );
1283 : }
1284 4 : PrintLine( fpSchema, " </xs:complexType>" );
1285 4 : PrintLine( fpSchema, " </xs:element>" );
1286 4 : PrintLine( fpSchema, " </xs:sequence>" );
1287 4 : PrintLine( fpSchema, " </xs:extension>" );
1288 4 : PrintLine( fpSchema, " </xs:complexContent>" );
1289 4 : PrintLine( fpSchema, "</xs:complexType>" );
1290 : }
1291 : else
1292 : {
1293 : PrintLine( fpSchema,
1294 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>",
1295 5 : pszPrefix );
1296 :
1297 5 : PrintLine( fpSchema, "<xs:complexType name=\"FeatureCollectionType\">");
1298 5 : PrintLine( fpSchema, " <xs:complexContent>" );
1299 5 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureCollectionType\">" );
1300 5 : PrintLine( fpSchema, " <xs:attribute name=\"lockId\" type=\"xs:string\" use=\"optional\"/>" );
1301 5 : PrintLine( fpSchema, " <xs:attribute name=\"scope\" type=\"xs:string\" use=\"optional\"/>" );
1302 5 : PrintLine( fpSchema, " </xs:extension>" );
1303 5 : PrintLine( fpSchema, " </xs:complexContent>" );
1304 5 : PrintLine( fpSchema, "</xs:complexType>" );
1305 : }
1306 :
1307 : /* ==================================================================== */
1308 : /* Define the schema for each layer. */
1309 : /* ==================================================================== */
1310 : int iLayer;
1311 :
1312 18 : for( iLayer = 0; iLayer < GetLayerCount(); iLayer++ )
1313 : {
1314 9 : OGRFeatureDefn *poFDefn = GetLayer(iLayer)->GetLayerDefn();
1315 :
1316 : /* -------------------------------------------------------------------- */
1317 : /* Emit initial stuff for a feature type. */
1318 : /* -------------------------------------------------------------------- */
1319 9 : if (IsGML32Output())
1320 : {
1321 : PrintLine(
1322 : fpSchema,
1323 : "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:AbstractFeature\"/>",
1324 1 : poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
1325 : }
1326 : else
1327 : {
1328 : PrintLine(
1329 : fpSchema,
1330 : "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:_Feature\"/>",
1331 8 : poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
1332 : }
1333 :
1334 9 : PrintLine( fpSchema, "<xs:complexType name=\"%s_Type\">", poFDefn->GetName());
1335 9 : PrintLine( fpSchema, " <xs:complexContent>");
1336 9 : PrintLine( fpSchema, " <xs:extension base=\"gml:AbstractFeatureType\">");
1337 9 : PrintLine( fpSchema, " <xs:sequence>");
1338 :
1339 : /* -------------------------------------------------------------------- */
1340 : /* Define the geometry attribute. */
1341 : /* -------------------------------------------------------------------- */
1342 9 : const char* pszGeometryTypeName = "GeometryPropertyType";
1343 9 : switch(wkbFlatten(poFDefn->GetGeomType()))
1344 : {
1345 : case wkbPoint:
1346 0 : pszGeometryTypeName = "PointPropertyType";
1347 0 : break;
1348 : case wkbLineString:
1349 0 : if (IsGML3Output())
1350 0 : pszGeometryTypeName = "CurvePropertyType";
1351 : else
1352 0 : pszGeometryTypeName = "LineStringPropertyType";
1353 0 : break;
1354 : case wkbPolygon:
1355 2 : if (IsGML3Output())
1356 1 : pszGeometryTypeName = "SurfacePropertyType";
1357 : else
1358 1 : pszGeometryTypeName = "PolygonPropertyType";
1359 2 : break;
1360 : case wkbMultiPoint:
1361 0 : pszGeometryTypeName = "MultiPointPropertyType";
1362 0 : break;
1363 : case wkbMultiLineString:
1364 0 : if (IsGML3Output())
1365 0 : pszGeometryTypeName = "MutliCurvePropertyType";
1366 : else
1367 0 : pszGeometryTypeName = "MultiLineStringPropertyType";
1368 0 : break;
1369 : case wkbMultiPolygon:
1370 0 : if (IsGML3Output())
1371 0 : pszGeometryTypeName = "MultiSurfacePropertyType";
1372 : else
1373 0 : pszGeometryTypeName = "MultiPolygonPropertyType";
1374 0 : break;
1375 : case wkbGeometryCollection:
1376 0 : pszGeometryTypeName = "MultiGeometryPropertyType";
1377 : break;
1378 : default:
1379 : break;
1380 : }
1381 :
1382 9 : if (poFDefn->GetGeomType() != wkbNone)
1383 : {
1384 : PrintLine( fpSchema,
1385 8 : " <xs:element name=\"geometryProperty\" type=\"gml:%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\"/>", pszGeometryTypeName );
1386 : }
1387 :
1388 : /* -------------------------------------------------------------------- */
1389 : /* Emit each of the attributes. */
1390 : /* -------------------------------------------------------------------- */
1391 27 : for( int iField = 0; iField < poFDefn->GetFieldCount(); iField++ )
1392 : {
1393 18 : OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField);
1394 :
1395 18 : if( poFieldDefn->GetType() == OFTInteger )
1396 : {
1397 : int nWidth;
1398 :
1399 2 : if( poFieldDefn->GetWidth() > 0 )
1400 2 : nWidth = poFieldDefn->GetWidth();
1401 : else
1402 0 : nWidth = 16;
1403 :
1404 2 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1405 2 : PrintLine( fpSchema, " <xs:simpleType>");
1406 2 : PrintLine( fpSchema, " <xs:restriction base=\"xs:integer\">");
1407 2 : PrintLine( fpSchema, " <xs:totalDigits value=\"%d\"/>", nWidth);
1408 2 : PrintLine( fpSchema, " </xs:restriction>");
1409 2 : PrintLine( fpSchema, " </xs:simpleType>");
1410 2 : PrintLine( fpSchema, " </xs:element>");
1411 : }
1412 16 : else if( poFieldDefn->GetType() == OFTReal )
1413 : {
1414 : int nWidth, nDecimals;
1415 :
1416 6 : nWidth = poFieldDefn->GetWidth();
1417 6 : nDecimals = poFieldDefn->GetPrecision();
1418 :
1419 6 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1420 6 : PrintLine( fpSchema, " <xs:simpleType>");
1421 6 : PrintLine( fpSchema, " <xs:restriction base=\"xs:decimal\">");
1422 6 : if (nWidth > 0)
1423 : {
1424 5 : PrintLine( fpSchema, " <xs:totalDigits value=\"%d\"/>", nWidth);
1425 5 : PrintLine( fpSchema, " <xs:fractionDigits value=\"%d\"/>", nDecimals);
1426 : }
1427 6 : PrintLine( fpSchema, " </xs:restriction>");
1428 6 : PrintLine( fpSchema, " </xs:simpleType>");
1429 6 : PrintLine( fpSchema, " </xs:element>");
1430 : }
1431 10 : else if( poFieldDefn->GetType() == OFTString )
1432 : {
1433 9 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1434 9 : PrintLine( fpSchema, " <xs:simpleType>");
1435 9 : PrintLine( fpSchema, " <xs:restriction base=\"xs:string\">");
1436 9 : if( poFieldDefn->GetWidth() != 0 )
1437 : {
1438 4 : PrintLine( fpSchema, " <xs:maxLength value=\"%d\"/>", poFieldDefn->GetWidth());
1439 : }
1440 9 : PrintLine( fpSchema, " </xs:restriction>");
1441 9 : PrintLine( fpSchema, " </xs:simpleType>");
1442 9 : PrintLine( fpSchema, " </xs:element>");
1443 : }
1444 1 : else if( poFieldDefn->GetType() == OFTDate || poFieldDefn->GetType() == OFTDateTime )
1445 : {
1446 1 : PrintLine( fpSchema, " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">", poFieldDefn->GetNameRef());
1447 1 : PrintLine( fpSchema, " <xs:simpleType>");
1448 1 : PrintLine( fpSchema, " <xs:restriction base=\"xs:string\">");
1449 1 : PrintLine( fpSchema, " </xs:restriction>");
1450 1 : PrintLine( fpSchema, " </xs:simpleType>");
1451 1 : PrintLine( fpSchema, " </xs:element>");
1452 : }
1453 : else
1454 : {
1455 : /* TODO */
1456 : }
1457 : } /* next field */
1458 :
1459 : /* -------------------------------------------------------------------- */
1460 : /* Finish off feature type. */
1461 : /* -------------------------------------------------------------------- */
1462 9 : PrintLine( fpSchema, " </xs:sequence>");
1463 9 : PrintLine( fpSchema, " </xs:extension>");
1464 9 : PrintLine( fpSchema, " </xs:complexContent>");
1465 9 : PrintLine( fpSchema, "</xs:complexType>" );
1466 : } /* next layer */
1467 :
1468 9 : PrintLine( fpSchema, "</xs:schema>" );
1469 :
1470 : /* ==================================================================== */
1471 : /* Move schema to the start of the file. */
1472 : /* ==================================================================== */
1473 9 : if( fpSchema == fpOutput )
1474 : {
1475 : /* -------------------------------------------------------------------- */
1476 : /* Read the schema into memory. */
1477 : /* -------------------------------------------------------------------- */
1478 0 : int nSchemaSize = (int) VSIFTellL( fpOutput ) - nSchemaStart;
1479 0 : char *pszSchema = (char *) CPLMalloc(nSchemaSize+1);
1480 :
1481 0 : VSIFSeekL( fpOutput, nSchemaStart, SEEK_SET );
1482 :
1483 0 : VSIFReadL( pszSchema, 1, nSchemaSize, fpOutput );
1484 0 : pszSchema[nSchemaSize] = '\0';
1485 :
1486 : /* -------------------------------------------------------------------- */
1487 : /* Move file data down by "schema size" bytes from after <?xml> */
1488 : /* header so we have room insert the schema. Move in pretty */
1489 : /* big chunks. */
1490 : /* -------------------------------------------------------------------- */
1491 0 : int nChunkSize = MIN(nSchemaStart-nSchemaInsertLocation,250000);
1492 0 : char *pszChunk = (char *) CPLMalloc(nChunkSize);
1493 0 : int nEndOfUnmovedData = nSchemaStart;
1494 :
1495 0 : for( nEndOfUnmovedData = nSchemaStart;
1496 : nEndOfUnmovedData > nSchemaInsertLocation; )
1497 : {
1498 : int nBytesToMove =
1499 0 : MIN(nChunkSize, nEndOfUnmovedData - nSchemaInsertLocation );
1500 :
1501 0 : VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove, SEEK_SET );
1502 0 : VSIFReadL( pszChunk, 1, nBytesToMove, fpOutput );
1503 : VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove + nSchemaSize,
1504 0 : SEEK_SET );
1505 0 : VSIFWriteL( pszChunk, 1, nBytesToMove, fpOutput );
1506 :
1507 0 : nEndOfUnmovedData -= nBytesToMove;
1508 : }
1509 :
1510 0 : CPLFree( pszChunk );
1511 :
1512 : /* -------------------------------------------------------------------- */
1513 : /* Write the schema in the opened slot. */
1514 : /* -------------------------------------------------------------------- */
1515 0 : VSIFSeekL( fpOutput, nSchemaInsertLocation, SEEK_SET );
1516 0 : VSIFWriteL( pszSchema, 1, nSchemaSize, fpOutput );
1517 :
1518 0 : VSIFSeekL( fpOutput, 0, SEEK_END );
1519 :
1520 0 : nBoundedByLocation += nSchemaSize;
1521 :
1522 0 : CPLFree(pszSchema);
1523 : }
1524 : /* -------------------------------------------------------------------- */
1525 : /* Close external schema files. */
1526 : /* -------------------------------------------------------------------- */
1527 : else
1528 9 : VSIFCloseL( fpSchema );
1529 : }
1530 :
1531 :
1532 : /************************************************************************/
1533 : /* PrintLine() */
1534 : /************************************************************************/
1535 :
1536 762 : void OGRGMLDataSource::PrintLine(VSILFILE* fp, const char *fmt, ...)
1537 : {
1538 762 : CPLString osWork;
1539 : va_list args;
1540 :
1541 762 : va_start( args, fmt );
1542 762 : osWork.vPrintf( fmt, args );
1543 762 : va_end( args );
1544 :
1545 : #ifdef WIN32
1546 : const char* pszEOL = "\r\n";
1547 : #else
1548 762 : const char* pszEOL = "\n";
1549 : #endif
1550 :
1551 762 : VSIFPrintfL(fp, "%s%s", osWork.c_str(), pszEOL);
1552 762 : }
|