1 : /******************************************************************************
2 : * $Id: ogrgmldatasource.cpp 17629 2009-09-10 14:51:45Z chaitanya $
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 : #include "ogr_gml.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 :
34 : CPL_CVSID("$Id: ogrgmldatasource.cpp 17629 2009-09-10 14:51:45Z chaitanya $");
35 :
36 : /************************************************************************/
37 : /* OGRGMLDataSource() */
38 : /************************************************************************/
39 :
40 80 : OGRGMLDataSource::OGRGMLDataSource()
41 :
42 : {
43 80 : pszName = NULL;
44 80 : papoLayers = NULL;
45 80 : nLayers = 0;
46 :
47 80 : poReader = NULL;
48 80 : fpOutput = NULL;
49 :
50 80 : papszCreateOptions = NULL;
51 80 : }
52 :
53 : /************************************************************************/
54 : /* ~OGRGMLDataSource() */
55 : /************************************************************************/
56 :
57 160 : OGRGMLDataSource::~OGRGMLDataSource()
58 :
59 : {
60 :
61 80 : if( fpOutput != NULL )
62 : {
63 : VSIFPrintf( fpOutput, "%s",
64 2 : "</ogr:FeatureCollection>\n" );
65 :
66 2 : InsertHeader();
67 :
68 2 : if( nBoundedByLocation != -1
69 : && sBoundingRect.IsInit()
70 : && VSIFSeek( fpOutput, nBoundedByLocation, SEEK_SET ) == 0 )
71 : {
72 0 : VSIFPrintf( fpOutput, " <gml:boundedBy>\n" );
73 0 : VSIFPrintf( fpOutput, " <gml:Box>\n" );
74 : VSIFPrintf( fpOutput,
75 : " <gml:coord><gml:X>%.16g</gml:X>"
76 : "<gml:Y>%.16g</gml:Y></gml:coord>\n",
77 0 : sBoundingRect.MinX, sBoundingRect.MinY );
78 : VSIFPrintf( fpOutput,
79 : " <gml:coord><gml:X>%.16g</gml:X>"
80 : "<gml:Y>%.16g</gml:Y></gml:coord>\n",
81 0 : sBoundingRect.MaxX, sBoundingRect.MaxY );
82 0 : VSIFPrintf( fpOutput, " </gml:Box>\n" );
83 0 : VSIFPrintf( fpOutput, " </gml:boundedBy>" );
84 : }
85 :
86 2 : if( fpOutput != stdout )
87 2 : VSIFClose( fpOutput );
88 : }
89 :
90 80 : CSLDestroy( papszCreateOptions );
91 80 : CPLFree( pszName );
92 :
93 97 : for( int i = 0; i < nLayers; i++ )
94 17 : delete papoLayers[i];
95 :
96 80 : CPLFree( papoLayers );
97 :
98 80 : if( poReader )
99 17 : delete poReader;
100 160 : }
101 :
102 : /************************************************************************/
103 : /* Open() */
104 : /************************************************************************/
105 :
106 78 : int OGRGMLDataSource::Open( const char * pszNewName, int bTestOpen )
107 :
108 : {
109 : FILE *fp;
110 : char szHeader[1000];
111 :
112 : /* -------------------------------------------------------------------- */
113 : /* Open the source file. */
114 : /* -------------------------------------------------------------------- */
115 78 : fp = VSIFOpen( pszNewName, "r" );
116 78 : if( fp == NULL )
117 : {
118 6 : if( !bTestOpen )
119 : CPLError( CE_Failure, CPLE_OpenFailed,
120 : "Failed to open GML file `%s'.",
121 0 : pszNewName );
122 :
123 6 : return FALSE;
124 : }
125 :
126 : /* -------------------------------------------------------------------- */
127 : /* If we aren't sure it is GML, load a header chunk and check */
128 : /* for signs it is GML */
129 : /* -------------------------------------------------------------------- */
130 72 : if( bTestOpen )
131 : {
132 72 : size_t nRead = VSIFRead( szHeader, 1, sizeof(szHeader), fp );
133 72 : if (nRead <= 0)
134 : {
135 1 : VSIFClose( fp );
136 1 : return FALSE;
137 : }
138 71 : szHeader[MIN(nRead, sizeof(szHeader))-1] = '\0';
139 :
140 : /* -------------------------------------------------------------------- */
141 : /* Check for a UTF-8 BOM and skip if found */
142 : /* */
143 : /* TODO: BOM is variable-lenght parameter and depends on encoding. */
144 : /* Add BOM detection for other encodings. */
145 : /* -------------------------------------------------------------------- */
146 :
147 : // Used to skip to actual beginning of XML data
148 71 : char* szPtr = szHeader;
149 :
150 73 : if( ( (unsigned char)szHeader[0] == 0xEF )
151 1 : && ( (unsigned char)szHeader[1] == 0xBB )
152 1 : && ( (unsigned char)szHeader[2] == 0xBF) )
153 : {
154 1 : szPtr += 3;
155 : }
156 :
157 : /* -------------------------------------------------------------------- */
158 : /* Here, we expect the opening chevrons of GML tree root element */
159 : /* -------------------------------------------------------------------- */
160 71 : if( szPtr[0] != '<'
161 : || strstr(szPtr,"opengis.net/gml") == NULL )
162 : {
163 54 : VSIFClose( fp );
164 54 : return FALSE;
165 : }
166 : }
167 :
168 : /* -------------------------------------------------------------------- */
169 : /* We assume now that it is GML. Close and instantiate a */
170 : /* GMLReader on it. */
171 : /* -------------------------------------------------------------------- */
172 17 : VSIFClose( fp );
173 :
174 17 : poReader = CreateGMLReader();
175 17 : if( poReader == NULL )
176 : {
177 : CPLError( CE_Failure, CPLE_AppDefined,
178 : "File %s appears to be GML but the GML reader can't\n"
179 : "be instantiated, likely because Xerces or Expat support wasn't\n"
180 : "configured in.",
181 0 : pszNewName );
182 0 : return FALSE;
183 : }
184 :
185 17 : poReader->SetSourceFile( pszNewName );
186 :
187 17 : pszName = CPLStrdup( pszNewName );
188 :
189 : /* -------------------------------------------------------------------- */
190 : /* Can we find a GML Feature Schema (.gfs) for the input file? */
191 : /* -------------------------------------------------------------------- */
192 : const char *pszGFSFilename;
193 : VSIStatBuf sGFSStatBuf, sGMLStatBuf;
194 17 : int bHaveSchema = FALSE;
195 :
196 17 : pszGFSFilename = CPLResetExtension( pszNewName, "gfs" );
197 17 : if( CPLStat( pszGFSFilename, &sGFSStatBuf ) == 0 )
198 : {
199 4 : CPLStat( pszNewName, &sGMLStatBuf );
200 :
201 4 : if( sGMLStatBuf.st_mtime > sGFSStatBuf.st_mtime )
202 : {
203 : CPLDebug( "GML",
204 : "Found %s but ignoring because it appears\n"
205 : "be older than the associated GML file.",
206 0 : pszGFSFilename );
207 : }
208 : else
209 : {
210 4 : bHaveSchema = poReader->LoadClasses( pszGFSFilename );
211 : }
212 : }
213 :
214 : /* -------------------------------------------------------------------- */
215 : /* Can we find an xsd which might conform to tbe GML3 Level 0 */
216 : /* profile? We really ought to look for it based on the rules */
217 : /* schemaLocation in the GML feature collection but for now we */
218 : /* just hopes it is in the same director with the same name. */
219 : /* -------------------------------------------------------------------- */
220 : const char *pszXSDFilename;
221 :
222 17 : if( !bHaveSchema )
223 : {
224 13 : pszXSDFilename = CPLResetExtension( pszNewName, "xsd" );
225 13 : if( CPLStat( pszXSDFilename, &sGMLStatBuf ) == 0 )
226 : {
227 3 : bHaveSchema = poReader->ParseXSD( pszXSDFilename );
228 : }
229 : }
230 :
231 : /* -------------------------------------------------------------------- */
232 : /* Force a first pass to establish the schema. Eventually we */
233 : /* will have mechanisms for remembering the schema and related */
234 : /* information. */
235 : /* -------------------------------------------------------------------- */
236 17 : if( !bHaveSchema && !poReader->PrescanForSchema( TRUE ) )
237 : {
238 : // we assume an errors have been reported.
239 3 : return FALSE;
240 : }
241 :
242 : /* -------------------------------------------------------------------- */
243 : /* Save the schema file if possible. Don't make a fuss if we */
244 : /* can't ... could be read-only directory or something. */
245 : /* -------------------------------------------------------------------- */
246 14 : if( !bHaveSchema && !poReader->HasStoppedParsing())
247 : {
248 7 : FILE *fp = NULL;
249 :
250 7 : pszGFSFilename = CPLResetExtension( pszNewName, "gfs" );
251 7 : if( CPLStat( pszGFSFilename, &sGFSStatBuf ) != 0
252 : && (fp = VSIFOpen( pszGFSFilename, "wt" )) != NULL )
253 : {
254 7 : VSIFClose( fp );
255 7 : poReader->SaveClasses( pszGFSFilename );
256 : }
257 : else
258 : {
259 : CPLDebug("GML",
260 : "Not saving %s files already exists or can't be created.",
261 0 : pszGFSFilename );
262 : }
263 : }
264 :
265 : /* -------------------------------------------------------------------- */
266 : /* Translate the GMLFeatureClasses into layers. */
267 : /* -------------------------------------------------------------------- */
268 : papoLayers = (OGRGMLLayer **)
269 14 : CPLCalloc( sizeof(OGRGMLLayer *), poReader->GetClassCount());
270 14 : nLayers = 0;
271 :
272 43 : while( nLayers < poReader->GetClassCount() )
273 : {
274 15 : papoLayers[nLayers] = TranslateGMLSchema(poReader->GetClass(nLayers));
275 15 : nLayers++;
276 : }
277 :
278 :
279 :
280 14 : return TRUE;
281 : }
282 :
283 : /************************************************************************/
284 : /* TranslateGMLSchema() */
285 : /************************************************************************/
286 :
287 15 : OGRGMLLayer *OGRGMLDataSource::TranslateGMLSchema( GMLFeatureClass *poClass )
288 :
289 : {
290 : OGRGMLLayer *poLayer;
291 :
292 : /* -------------------------------------------------------------------- */
293 : /* Create an empty layer. */
294 : /* -------------------------------------------------------------------- */
295 : poLayer = new OGRGMLLayer( poClass->GetName(), NULL, FALSE,
296 15 : wkbUnknown, this );
297 :
298 : /* -------------------------------------------------------------------- */
299 : /* Added attributes (properties). */
300 : /* -------------------------------------------------------------------- */
301 64 : for( int iField = 0; iField < poClass->GetPropertyCount(); iField++ )
302 : {
303 49 : GMLPropertyDefn *poProperty = poClass->GetProperty( iField );
304 : OGRFieldType eFType;
305 :
306 49 : if( poProperty->GetType() == GMLPT_Untyped )
307 0 : eFType = OFTString;
308 49 : else if( poProperty->GetType() == GMLPT_String )
309 20 : eFType = OFTString;
310 29 : else if( poProperty->GetType() == GMLPT_Integer )
311 22 : eFType = OFTInteger;
312 7 : else if( poProperty->GetType() == GMLPT_Real )
313 7 : eFType = OFTReal;
314 : else
315 0 : eFType = OFTString;
316 :
317 49 : OGRFieldDefn oField( poProperty->GetName(), eFType );
318 49 : if ( EQUALN(oField.GetNameRef(), "ogr:", 4) )
319 0 : oField.SetName(poProperty->GetName()+4);
320 49 : if( poProperty->GetWidth() > 0 )
321 18 : oField.SetWidth( poProperty->GetWidth() );
322 49 : if( poProperty->GetPrecision() > 0 )
323 2 : oField.SetPrecision( poProperty->GetPrecision() );
324 :
325 49 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
326 : }
327 :
328 15 : return poLayer;
329 : }
330 :
331 : /************************************************************************/
332 : /* Create() */
333 : /************************************************************************/
334 :
335 2 : int OGRGMLDataSource::Create( const char *pszFilename,
336 : char **papszOptions )
337 :
338 : {
339 2 : if( fpOutput != NULL || poReader != NULL )
340 : {
341 : CPLAssert( FALSE );
342 0 : return FALSE;
343 : }
344 :
345 : /* -------------------------------------------------------------------- */
346 : /* Create the output file. */
347 : /* -------------------------------------------------------------------- */
348 2 : pszName = CPLStrdup( pszFilename );
349 :
350 2 : if( EQUAL(pszFilename,"stdout") )
351 0 : fpOutput = stdout;
352 : else
353 2 : fpOutput = VSIFOpen( pszFilename, "wt+" );
354 2 : if( fpOutput == NULL )
355 : {
356 : CPLError( CE_Failure, CPLE_OpenFailed,
357 : "Failed to create GML file %s.",
358 0 : pszFilename );
359 0 : return FALSE;
360 : }
361 :
362 : /* -------------------------------------------------------------------- */
363 : /* Write out "standard" header. */
364 : /* -------------------------------------------------------------------- */
365 : VSIFPrintf( fpOutput, "%s",
366 2 : "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" );
367 :
368 2 : nSchemaInsertLocation = VSIFTell( fpOutput );
369 :
370 : VSIFPrintf( fpOutput, "%s",
371 2 : "<ogr:FeatureCollection\n" );
372 :
373 : /* -------------------------------------------------------------------- */
374 : /* Write out schema info if provided in creation options. */
375 : /* -------------------------------------------------------------------- */
376 2 : const char *pszSchemaURI = CSLFetchNameValue(papszOptions,"XSISCHEMAURI");
377 2 : const char *pszSchemaOpt = CSLFetchNameValue( papszOptions, "XSISCHEMA" );
378 :
379 2 : if( pszSchemaURI != NULL )
380 : {
381 : VSIFPrintf( fpOutput,
382 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
383 : " xsi:schemaLocation=\"%s\"\n",
384 0 : CSLFetchNameValue( papszOptions, "XSISCHEMAURI" ) );
385 : }
386 2 : else if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
387 : {
388 2 : char *pszBasename = CPLStrdup(CPLGetBasename( pszName ));
389 :
390 : VSIFPrintf( fpOutput,
391 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
392 : " xsi:schemaLocation=\"http://ogr.maptools.org/ %s\"\n",
393 2 : CPLResetExtension( pszBasename, "xsd" ) );
394 2 : CPLFree( pszBasename );
395 : }
396 :
397 : VSIFPrintf( fpOutput, "%s",
398 2 : " xmlns:ogr=\"http://ogr.maptools.org/\"\n" );
399 : VSIFPrintf( fpOutput, "%s",
400 2 : " xmlns:gml=\"http://www.opengis.net/gml\">\n" );
401 :
402 : /* -------------------------------------------------------------------- */
403 : /* Should we initialize an area to place the boundedBy element? */
404 : /* We will need to seek back to fill it in. */
405 : /* -------------------------------------------------------------------- */
406 2 : if( CSLFetchBoolean( papszOptions, "BOUNDEDBY", TRUE ) )
407 : {
408 2 : nBoundedByLocation = VSIFTell( fpOutput );
409 :
410 2 : if( nBoundedByLocation != -1 )
411 2 : VSIFPrintf( fpOutput, "%280s\n", "" );
412 : }
413 : else
414 0 : nBoundedByLocation = -1;
415 :
416 2 : return TRUE;
417 : }
418 :
419 : /************************************************************************/
420 : /* CreateLayer() */
421 : /************************************************************************/
422 :
423 : OGRLayer *
424 2 : OGRGMLDataSource::CreateLayer( const char * pszLayerName,
425 : OGRSpatialReference *poSRS,
426 : OGRwkbGeometryType eType,
427 : char ** papszOptions )
428 :
429 : {
430 : /* -------------------------------------------------------------------- */
431 : /* Verify we are in update mode. */
432 : /* -------------------------------------------------------------------- */
433 2 : if( fpOutput == NULL )
434 : {
435 : CPLError( CE_Failure, CPLE_NoWriteAccess,
436 : "Data source %s opened for read access.\n"
437 : "New layer %s cannot be created.\n",
438 0 : pszName, pszLayerName );
439 :
440 0 : return NULL;
441 : }
442 :
443 : /* -------------------------------------------------------------------- */
444 : /* Ensure name is safe as an element name. */
445 : /* -------------------------------------------------------------------- */
446 2 : char *pszCleanLayerName = CPLStrdup( pszLayerName );
447 :
448 2 : CPLCleanXMLElementName( pszCleanLayerName );
449 2 : if( strcmp(pszCleanLayerName,pszLayerName) != 0 )
450 : {
451 : CPLError( CE_Warning, CPLE_AppDefined,
452 : "Layer name '%s' adjusted to '%s' for XML validity.",
453 0 : pszLayerName, pszCleanLayerName );
454 : }
455 :
456 : /* -------------------------------------------------------------------- */
457 : /* Create the layer object. */
458 : /* -------------------------------------------------------------------- */
459 : OGRGMLLayer *poLayer;
460 :
461 2 : poLayer = new OGRGMLLayer( pszCleanLayerName, poSRS, TRUE, eType, this );
462 :
463 2 : CPLFree( pszCleanLayerName );
464 :
465 : /* -------------------------------------------------------------------- */
466 : /* Add layer to data source layer list. */
467 : /* -------------------------------------------------------------------- */
468 : papoLayers = (OGRGMLLayer **)
469 2 : CPLRealloc( papoLayers, sizeof(OGRGMLLayer *) * (nLayers+1) );
470 :
471 2 : papoLayers[nLayers++] = poLayer;
472 :
473 2 : return poLayer;
474 : }
475 :
476 : /************************************************************************/
477 : /* TestCapability() */
478 : /************************************************************************/
479 :
480 0 : int OGRGMLDataSource::TestCapability( const char * pszCap )
481 :
482 : {
483 0 : if( EQUAL(pszCap,ODsCCreateLayer) )
484 0 : return TRUE;
485 : else
486 0 : return FALSE;
487 : }
488 :
489 : /************************************************************************/
490 : /* GetLayer() */
491 : /************************************************************************/
492 :
493 15 : OGRLayer *OGRGMLDataSource::GetLayer( int iLayer )
494 :
495 : {
496 15 : if( iLayer < 0 || iLayer >= nLayers )
497 0 : return NULL;
498 : else
499 15 : return papoLayers[iLayer];
500 : }
501 :
502 : /************************************************************************/
503 : /* GrowExtents() */
504 : /************************************************************************/
505 :
506 0 : void OGRGMLDataSource::GrowExtents( OGREnvelope *psGeomBounds )
507 :
508 : {
509 0 : sBoundingRect.Merge( *psGeomBounds );
510 0 : }
511 :
512 : /************************************************************************/
513 : /* InsertHeader() */
514 : /* */
515 : /* This method is used to update boundedby info for a */
516 : /* dataset, and insert schema descriptions depending on */
517 : /* selection options in effect. */
518 : /************************************************************************/
519 :
520 2 : void OGRGMLDataSource::InsertHeader()
521 :
522 : {
523 : FILE *fpSchema;
524 2 : int nSchemaStart = 0;
525 :
526 2 : if( fpOutput == NULL || fpOutput == stdout )
527 0 : return;
528 :
529 : /* -------------------------------------------------------------------- */
530 : /* Do we want to write the schema within the GML instance doc */
531 : /* or to a separate file? For now we only support external. */
532 : /* -------------------------------------------------------------------- */
533 : const char *pszSchemaURI = CSLFetchNameValue(papszCreateOptions,
534 2 : "XSISCHEMAURI");
535 : const char *pszSchemaOpt = CSLFetchNameValue( papszCreateOptions,
536 2 : "XSISCHEMA" );
537 :
538 2 : if( pszSchemaURI != NULL )
539 0 : return;
540 :
541 4 : if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") )
542 : {
543 2 : const char *pszXSDFilename = CPLResetExtension( pszName, "xsd" );
544 :
545 2 : fpSchema = VSIFOpen( pszXSDFilename, "wt" );
546 2 : if( fpSchema == NULL )
547 : {
548 : CPLError( CE_Failure, CPLE_OpenFailed,
549 : "Failed to open file %.500s for schema output.",
550 0 : pszXSDFilename );
551 0 : return;
552 : }
553 2 : fprintf( fpSchema, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
554 : }
555 0 : else if( EQUAL(pszSchemaOpt,"INTERNAL") )
556 : {
557 0 : nSchemaStart = VSIFTell( fpOutput );
558 0 : fpSchema = fpOutput;
559 : }
560 : else
561 0 : return;
562 :
563 : /* ==================================================================== */
564 : /* Write the schema section at the end of the file. Once */
565 : /* complete, we will read it back in, and then move the whole */
566 : /* file "down" enough to insert the schema at the beginning. */
567 : /* ==================================================================== */
568 :
569 : /* -------------------------------------------------------------------- */
570 : /* Emit the start of the schema section. */
571 : /* -------------------------------------------------------------------- */
572 2 : const char *pszTargetNameSpace = "http://ogr.maptools.org/";
573 2 : const char *pszPrefix = "ogr";
574 :
575 : VSIFPrintf( fpSchema,
576 : "<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\">\n",
577 2 : pszTargetNameSpace, pszPrefix, pszTargetNameSpace );
578 :
579 : VSIFPrintf( fpSchema,
580 2 : "<xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://schemas.opengeospatial.net/gml/2.1.2/feature.xsd\"/>" );
581 :
582 : /* -------------------------------------------------------------------- */
583 : /* Define the FeatureCollection */
584 : /* -------------------------------------------------------------------- */
585 : VSIFPrintf( fpSchema,
586 : "<xs:element name=\"FeatureCollection\" type=\"%s:FeatureCollectionType\" substitutionGroup=\"gml:_FeatureCollection\"/>\n",
587 2 : pszPrefix );
588 :
589 : VSIFPrintf(
590 : fpSchema,
591 : "<xs:complexType name=\"FeatureCollectionType\">\n"
592 : " <xs:complexContent>\n"
593 : " <xs:extension base=\"gml:AbstractFeatureCollectionType\">\n"
594 : " <xs:attribute name=\"lockId\" type=\"xs:string\" use=\"optional\"/>\n"
595 : " <xs:attribute name=\"scope\" type=\"xs:string\" use=\"optional\"/>\n"
596 : " </xs:extension>\n"
597 : " </xs:complexContent>\n"
598 2 : "</xs:complexType>\n" );
599 :
600 : /* ==================================================================== */
601 : /* Define the schema for each layer. */
602 : /* ==================================================================== */
603 : int iLayer;
604 :
605 4 : for( iLayer = 0; iLayer < GetLayerCount(); iLayer++ )
606 : {
607 2 : OGRFeatureDefn *poFDefn = GetLayer(iLayer)->GetLayerDefn();
608 :
609 : /* -------------------------------------------------------------------- */
610 : /* Emit initial stuff for a feature type. */
611 : /* -------------------------------------------------------------------- */
612 : VSIFPrintf(
613 : fpSchema,
614 : "<xs:element name=\"%s\" type=\"%s:%s_Type\" substitutionGroup=\"gml:_Feature\"/>\n",
615 2 : poFDefn->GetName(), pszPrefix, poFDefn->GetName() );
616 :
617 : VSIFPrintf(
618 : fpSchema,
619 : "<xs:complexType name=\"%s_Type\">\n"
620 : " <xs:complexContent>\n"
621 : " <xs:extension base=\"gml:AbstractFeatureType\">\n"
622 : " <xs:sequence>\n",
623 2 : poFDefn->GetName() );
624 :
625 : /* -------------------------------------------------------------------- */
626 : /* Define the geometry attribute. For now I always use the */
627 : /* generic geometry type, but eventually we should express */
628 : /* particulars if available. */
629 : /* -------------------------------------------------------------------- */
630 : VSIFPrintf(
631 : fpSchema,
632 2 : "<xs:element name=\"geometryProperty\" type=\"gml:GeometryPropertyType\" nillable=\"true\" minOccurs=\"1\" maxOccurs=\"1\"/>\n" );
633 :
634 : /* -------------------------------------------------------------------- */
635 : /* Emit each of the attributes. */
636 : /* -------------------------------------------------------------------- */
637 8 : for( int iField = 0; iField < poFDefn->GetFieldCount(); iField++ )
638 : {
639 6 : OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField);
640 :
641 6 : if( poFieldDefn->GetType() == OFTInteger )
642 : {
643 : int nWidth;
644 :
645 1 : if( poFieldDefn->GetWidth() > 0 )
646 1 : nWidth = poFieldDefn->GetWidth();
647 : else
648 0 : nWidth = 16;
649 :
650 : VSIFPrintf( fpSchema,
651 : " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">\n"
652 : " <xs:simpleType>\n"
653 : " <xs:restriction base=\"xs:integer\">\n"
654 : " <xs:totalDigits value=\"%d\"/>\n"
655 : " </xs:restriction>\n"
656 : " </xs:simpleType>\n"
657 : " </xs:element>\n",
658 1 : poFieldDefn->GetNameRef(), nWidth );
659 : }
660 5 : else if( poFieldDefn->GetType() == OFTReal )
661 : {
662 : int nWidth, nDecimals;
663 :
664 2 : if( poFieldDefn->GetPrecision() == 0 )
665 1 : nDecimals = 0;
666 : else
667 1 : nDecimals = poFieldDefn->GetPrecision();
668 :
669 2 : if( poFieldDefn->GetWidth() > 0 )
670 1 : nWidth = poFieldDefn->GetWidth();
671 : else
672 1 : nWidth = 33;
673 :
674 : VSIFPrintf( fpSchema,
675 : " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">\n"
676 : " <xs:simpleType>\n"
677 : " <xs:restriction base=\"xs:decimal\">\n"
678 : " <xs:totalDigits value=\"%d\"/>\n"
679 : " <xs:fractionDigits value=\"%d\"/>\n"
680 : " </xs:restriction>\n"
681 : " </xs:simpleType>\n"
682 : " </xs:element>\n",
683 2 : poFieldDefn->GetNameRef(), nWidth, nDecimals );
684 : }
685 3 : else if( poFieldDefn->GetType() == OFTString )
686 : {
687 : char szMaxLength[48];
688 :
689 2 : if( poFieldDefn->GetWidth() == 0 )
690 1 : sprintf( szMaxLength, "unbounded" );
691 : else
692 1 : sprintf( szMaxLength, "%d", poFieldDefn->GetWidth() );
693 :
694 : VSIFPrintf( fpSchema,
695 : " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">\n"
696 : " <xs:simpleType>\n"
697 : " <xs:restriction base=\"xs:string\">\n"
698 : " <xs:maxLength value=\"%s\"/>\n"
699 : " </xs:restriction>\n"
700 : " </xs:simpleType>\n"
701 : " </xs:element>\n",
702 2 : poFieldDefn->GetNameRef(), szMaxLength );
703 : }
704 1 : else if( poFieldDefn->GetType() == OFTDate || poFieldDefn->GetType() == OFTDateTime )
705 : {
706 : VSIFPrintf( fpSchema,
707 : " <xs:element name=\"%s\" nillable=\"true\" minOccurs=\"0\" maxOccurs=\"1\">\n"
708 : " <xs:simpleType>\n"
709 : " <xs:restriction base=\"xs:string\">\n"
710 : " <xs:maxLength value=\"unbounded\"/>\n"
711 : " </xs:restriction>\n"
712 : " </xs:simpleType>\n"
713 : " </xs:element>\n",
714 1 : poFieldDefn->GetNameRef() );
715 : }
716 : else
717 : {
718 : /* TODO */
719 : }
720 : } /* next field */
721 :
722 : /* -------------------------------------------------------------------- */
723 : /* Finish off feature type. */
724 : /* -------------------------------------------------------------------- */
725 : VSIFPrintf( fpSchema,
726 : " </xs:sequence>\n"
727 : " </xs:extension>\n"
728 : " </xs:complexContent>\n"
729 2 : "</xs:complexType>\n" );
730 : } /* next layer */
731 :
732 2 : VSIFPrintf( fpSchema, "</xs:schema>\n" );
733 :
734 : /* ==================================================================== */
735 : /* Move schema to the start of the file. */
736 : /* ==================================================================== */
737 2 : if( fpSchema == fpOutput )
738 : {
739 : /* -------------------------------------------------------------------- */
740 : /* Read the schema into memory. */
741 : /* -------------------------------------------------------------------- */
742 0 : int nSchemaSize = VSIFTell( fpOutput ) - nSchemaStart;
743 0 : char *pszSchema = (char *) CPLMalloc(nSchemaSize+1);
744 :
745 0 : VSIFSeek( fpOutput, nSchemaStart, SEEK_SET );
746 :
747 0 : VSIFRead( pszSchema, 1, nSchemaSize, fpOutput );
748 0 : pszSchema[nSchemaSize] = '\0';
749 :
750 : /* -------------------------------------------------------------------- */
751 : /* Move file data down by "schema size" bytes from after <?xml> */
752 : /* header so we have room insert the schema. Move in pretty */
753 : /* big chunks. */
754 : /* -------------------------------------------------------------------- */
755 0 : int nChunkSize = MIN(nSchemaStart-nSchemaInsertLocation,250000);
756 0 : char *pszChunk = (char *) CPLMalloc(nChunkSize);
757 0 : int nEndOfUnmovedData = nSchemaStart;
758 :
759 0 : for( nEndOfUnmovedData = nSchemaStart;
760 : nEndOfUnmovedData > nSchemaInsertLocation; )
761 : {
762 : int nBytesToMove =
763 0 : MIN(nChunkSize, nEndOfUnmovedData - nSchemaInsertLocation );
764 :
765 0 : VSIFSeek( fpOutput, nEndOfUnmovedData - nBytesToMove, SEEK_SET );
766 0 : VSIFRead( pszChunk, 1, nBytesToMove, fpOutput );
767 : VSIFSeek( fpOutput, nEndOfUnmovedData - nBytesToMove + nSchemaSize,
768 0 : SEEK_SET );
769 0 : VSIFWrite( pszChunk, 1, nBytesToMove, fpOutput );
770 :
771 0 : nEndOfUnmovedData -= nBytesToMove;
772 : }
773 :
774 0 : CPLFree( pszChunk );
775 :
776 : /* -------------------------------------------------------------------- */
777 : /* Write the schema in the opened slot. */
778 : /* -------------------------------------------------------------------- */
779 0 : VSIFSeek( fpOutput, nSchemaInsertLocation, SEEK_SET );
780 0 : VSIFWrite( pszSchema, 1, nSchemaSize, fpOutput );
781 :
782 0 : VSIFSeek( fpOutput, 0, SEEK_END );
783 :
784 0 : nBoundedByLocation += nSchemaSize;
785 : }
786 : /* -------------------------------------------------------------------- */
787 : /* Close external schema files. */
788 : /* -------------------------------------------------------------------- */
789 : else
790 2 : VSIFClose( fpSchema );
791 : }
|