1 /******************************************************************************
2  * $Id: ogrgmldriver.cpp 29240 2015-05-24 10:58:38Z rouault $
3  *
4  * Project:  OGR
5  * Purpose:  OGRGMLDriver implementation
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_multiproc.h"
33 #include "gmlreaderp.h"
34 
35 CPL_CVSID("$Id: ogrgmldriver.cpp 29240 2015-05-24 10:58:38Z rouault $");
36 
37 /************************************************************************/
38 /*                        OGRGMLDriverUnload()                          */
39 /************************************************************************/
40 
OGRGMLDriverUnload(CPL_UNUSED GDALDriver * poDriver)41 static void OGRGMLDriverUnload(CPL_UNUSED GDALDriver* poDriver)
42 {
43     if( GMLReader::hMutex != NULL )
44         CPLDestroyMutex( GMLReader::hMutex );
45     GMLReader::hMutex = NULL;
46 }
47 
48 /************************************************************************/
49 /*                         OGRGMLDriverIdentify()                       */
50 /************************************************************************/
51 
OGRGMLDriverIdentify(GDALOpenInfo * poOpenInfo)52 static int OGRGMLDriverIdentify( GDALOpenInfo* poOpenInfo )
53 
54 {
55     if( poOpenInfo->fpL == NULL )
56     {
57         if( strstr(poOpenInfo->pszFilename, "xsd=") != NULL )
58             return -1; /* must be later checked */
59         return FALSE;
60     }
61     /* Might be a OS-Mastermap gzipped GML, so let be nice and try to open */
62     /* it transparently with /vsigzip/ */
63     else
64     if ( poOpenInfo->pabyHeader[0] == 0x1f && poOpenInfo->pabyHeader[1] == 0x8b &&
65          EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "gz") &&
66          strncmp(poOpenInfo->pszFilename, "/vsigzip/", strlen("/vsigzip/")) != 0 )
67     {
68         return -1; /* must be later checked */
69     }
70     else
71     {
72         const char* szPtr = (const char*)poOpenInfo->pabyHeader;
73 
74         if( ( (unsigned char)szPtr[0] == 0xEF )
75             && ( (unsigned char)szPtr[1] == 0xBB )
76             && ( (unsigned char)szPtr[2] == 0xBF) )
77         {
78             szPtr += 3;
79         }
80 /* -------------------------------------------------------------------- */
81 /*      Here, we expect the opening chevrons of GML tree root element   */
82 /* -------------------------------------------------------------------- */
83         if( szPtr[0] != '<' )
84             return FALSE;
85 
86         if( !poOpenInfo->TryToIngest(4096) )
87             return FALSE;
88 
89         return OGRGMLDataSource::CheckHeader((const char*)poOpenInfo->pabyHeader);
90     }
91 }
92 
93 /************************************************************************/
94 /*                                Open()                                */
95 /************************************************************************/
96 
OGRGMLDriverOpen(GDALOpenInfo * poOpenInfo)97 static GDALDataset *OGRGMLDriverOpen( GDALOpenInfo* poOpenInfo )
98 
99 {
100     OGRGMLDataSource    *poDS;
101 
102     if( poOpenInfo->eAccess == GA_Update )
103         return NULL;
104 
105     if( OGRGMLDriverIdentify( poOpenInfo ) == FALSE )
106         return NULL;
107 
108     poDS = new OGRGMLDataSource();
109 
110     if( !poDS->Open(  poOpenInfo ) )
111     {
112         delete poDS;
113         return NULL;
114     }
115     else
116         return poDS;
117 }
118 
119 /************************************************************************/
120 /*                             Create()                                 */
121 /************************************************************************/
122 
OGRGMLDriverCreate(const char * pszName,CPL_UNUSED int nBands,CPL_UNUSED int nXSize,CPL_UNUSED int nYSize,CPL_UNUSED GDALDataType eDT,char ** papszOptions)123 static GDALDataset *OGRGMLDriverCreate( const char * pszName,
124                                         CPL_UNUSED int nBands,
125                                         CPL_UNUSED int nXSize,
126                                         CPL_UNUSED int nYSize,
127                                         CPL_UNUSED GDALDataType eDT,
128                                         char **papszOptions )
129 {
130     OGRGMLDataSource    *poDS = new OGRGMLDataSource();
131 
132     if( !poDS->Create( pszName, papszOptions ) )
133     {
134         delete poDS;
135         return NULL;
136     }
137     else
138         return poDS;
139 }
140 
141 /************************************************************************/
142 /*                           RegisterOGRGML()                           */
143 /************************************************************************/
144 
RegisterOGRGML()145 void RegisterOGRGML()
146 
147 {
148     GDALDriver  *poDriver;
149 
150     if( GDALGetDriverByName( "GML" ) == NULL )
151     {
152         poDriver = new GDALDriver();
153 
154         poDriver->SetDescription( "GML" );
155         poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" );
156         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
157                                    "Geography Markup Language (GML)" );
158         poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gml" );
159         poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "gml xml" );
160         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
161                                    "drv_gml.html" );
162 
163         poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST,
164 "<OpenOptionList>"
165 "  <Option name='XSD' type='string' description='Name of the related application schema file (.xsd).'/>"
166 "  <Option name='GFS_TEMPLATE' type='string' description='Filename of a .gfs template file to appli.'/>"
167 "  <Option name='FORCE_SRS_DETECTION' type='boolean' description='Force a full scan to detect the SRS of layers.' default='NO'/>"
168 "  <Option name='EMPTY_AS_NULL' type='boolean' description='Force empty fields to be reported as NULL. Set to NO so that not-nullable fields can be exposed' default='YES'/>"
169 "  <Option name='GML_ATTRIBUTES_TO_OGR_FIELDS' type='boolean' description='Whether GML attributes should be reported as OGR fields' default='NO'/>"
170 "  <Option name='INVERT_AXIS_ORDER_IF_LAT_LONG' type='boolean' description='Whether to present SRS and coordinate ordering in traditional GIS order' default='YES'/>"
171 "  <Option name='CONSIDER_EPSG_AS_URN' type='string-select' description='Whether to consider srsName like EPSG:XXXX as respecting EPSG axis order' default='AUTO'>"
172 "    <Value>AUTO</Value>"
173 "    <Value>YES</Value>"
174 "    <Value>NO</Value>"
175 "  </Option>"
176 "  <Option name='READ_MODE' type='string-select' description='Read mode' default='AUTO'>"
177 "    <Value>AUTO</Value>"
178 "    <Value>STANDARD</Value>"
179 "    <Value>SEQUENTIAL_LAYERS</Value>"
180 "    <Value>INTERLEAVED_LAYERS</Value>"
181 "  </Option>"
182 "  <Option name='EXPOSE_GML_ID' type='string-select' description='Whether to make feature gml:id as a gml_id attribute' default='AUTO'>"
183 "    <Value>AUTO</Value>"
184 "    <Value>YES</Value>"
185 "    <Value>NO</Value>"
186 "  </Option>"
187 "  <Option name='EXPOSE_FID' type='string-select' description='Whether to make feature fid as a fid attribute' default='AUTO'>"
188 "    <Value>AUTO</Value>"
189 "    <Value>YES</Value>"
190 "    <Value>NO</Value>"
191 "  </Option>"
192 "  <Option name='DOWNLOAD_SCHEMA' type='boolean' description='Whether to download the remote application schema if needed (only for WFS currently)' default='YES'/>"
193 "  <Option name='REGISTRY' type='string' description='Filename of the registry with application schemas.'/>"
194 "</OpenOptionList>" );
195 
196         poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
197 "<CreationOptionList>"
198 "  <Option name='XSISCHEMAURI' type='string' description='URI to be inserted as the schema location.'/>"
199 "  <Option name='XSISCHEMA' type='string-select' description='where to write a .xsd application schema. INTERNAL should not normally be used' default='EXTERNAL'>"
200 "    <Value>EXTERNAL</Value>"
201 "    <Value>INTERNAL</Value>"
202 "    <Value>OFF</Value>"
203 "  </Option>"
204 "  <Option name='PREFIX' type='string' description='Prefix for the application target namespace.' default='ogr'/>"
205 "  <Option name='STRIP_PREFIX' type='boolean' description='Whether to avoid writing the prefix of the application target namespace in the GML file.' default='NO'/>"
206 "  <Option name='TARGET_NAMESPACE' type='string' description='Application target namespace.' default='http://ogr.maptools.org/'/>"
207 "  <Option name='FORMAT' type='string-select' description='Version of GML to use' default='GML2'>"
208 "    <Value>GML2</Value>"
209 "    <Value>GML3</Value>"
210 "    <Value>GML3.2</Value>"
211 "    <Value>GML3Deegree</Value>"
212 "  </Option>"
213 "  <Option name='GML3_LONGSRS' type='boolean' description='Whether to write SRS with \"urn:ogc:def:crs:EPSG::\" prefix with GML3* versions' default='YES'/>"
214 "  <Option name='WRITE_FEATURE_BOUNDED_BY' type='boolean' description='Whether to write <gml:boundedBy> element for each feature with GML3* versions' default='YES'/>"
215 "  <Option name='SPACE_INDENTATION' type='boolean' description='Whether to indentate the output for readability' default='YES'/>"
216 "  <Option name='SRSDIMENSION_LOC' type='string-select' description='(only valid for FORMAT=GML3xx) Location where to put srsDimension attribute' default='POSLIST'>"
217 "    <Value>POSLIST</Value>"
218 "    <Value>GEOMETRY</Value>"
219 "    <Value>GEOMETRY,POSLIST</Value>"
220 "  </Option>"
221 "  <Option name='GML_ID' type='string' description='Value of feature collection gml:id (GML 3.2 only)' default='aFeatureCollection'/>"
222 "  <Option name='NAME' type='string' description='Content of GML name element'/>"
223 "  <Option name='DESCRIPTION' type='string' description='Content of GML description element'/>"
224 "</CreationOptionList>");
225 
226         poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, "<LayerCreationOptionList/>");
227 
228         poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime IntegerList Integer64List RealList StringList" );
229         poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_FIELDS, "YES" );
230         poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES" );
231 
232         poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
233 
234         poDriver->pfnOpen = OGRGMLDriverOpen;
235         poDriver->pfnIdentify = OGRGMLDriverIdentify;
236         poDriver->pfnCreate = OGRGMLDriverCreate;
237         poDriver->pfnUnloadDriver = OGRGMLDriverUnload;
238 
239         GetGDALDriverManager()->RegisterDriver( poDriver );
240     }
241 }
242