1 : /******************************************************************************
2 : * $Id: ogrgmldatasource.cpp 12743 2007-11-13 13:59:37Z dron $
3 : *
4 : * Project: OGR
5 : * Purpose: Implements OGRNASDataSource 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_nas.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 :
34 : CPL_CVSID("$Id: ogrgmldatasource.cpp 12743 2007-11-13 13:59:37Z dron $");
35 :
36 : static const char *apszURNNames[] =
37 : {
38 : "DE_DHDN_3GK2_*", "EPSG:31466",
39 : "DE_DHDN_3GK3_*", "EPSG:31467",
40 : "ETRS89_UTM32", "EPSG:25832",
41 : NULL, NULL
42 : };
43 :
44 : /************************************************************************/
45 : /* OGRNASDataSource() */
46 : /************************************************************************/
47 :
48 304 : OGRNASDataSource::OGRNASDataSource()
49 :
50 : {
51 304 : pszName = NULL;
52 304 : papoLayers = NULL;
53 304 : nLayers = 0;
54 :
55 304 : poReader = NULL;
56 304 : }
57 :
58 : /************************************************************************/
59 : /* ~OGRNASDataSource() */
60 : /************************************************************************/
61 :
62 304 : OGRNASDataSource::~OGRNASDataSource()
63 :
64 : {
65 304 : CPLFree( pszName );
66 :
67 436 : for( int i = 0; i < nLayers; i++ )
68 132 : delete papoLayers[i];
69 :
70 304 : CPLFree( papoLayers );
71 :
72 304 : if( poReader )
73 5 : delete poReader;
74 304 : }
75 :
76 : /************************************************************************/
77 : /* Open() */
78 : /************************************************************************/
79 :
80 304 : int OGRNASDataSource::Open( const char * pszNewName, int bTestOpen )
81 :
82 : {
83 : FILE *fp;
84 : char szHeader[8192];
85 :
86 : /* -------------------------------------------------------------------- */
87 : /* Open the source file. */
88 : /* -------------------------------------------------------------------- */
89 304 : fp = VSIFOpen( pszNewName, "r" );
90 304 : if( fp == NULL )
91 : {
92 106 : if( !bTestOpen )
93 : CPLError( CE_Failure, CPLE_OpenFailed,
94 : "Failed to open NAS file `%s'.",
95 0 : pszNewName );
96 :
97 106 : return FALSE;
98 : }
99 :
100 : /* -------------------------------------------------------------------- */
101 : /* If we aren't sure it is NAS, load a header chunk and check */
102 : /* for signs it is NAS */
103 : /* -------------------------------------------------------------------- */
104 198 : if( bTestOpen )
105 : {
106 198 : size_t nRead = VSIFRead( szHeader, 1, sizeof(szHeader), fp );
107 198 : if (nRead <= 0)
108 : {
109 4 : VSIFClose( fp );
110 4 : return FALSE;
111 : }
112 194 : szHeader[MIN(nRead, sizeof(szHeader))-1] = '\0';
113 :
114 : /* -------------------------------------------------------------------- */
115 : /* Check for a UTF-8 BOM and skip if found */
116 : /* */
117 : /* TODO: BOM is variable-lenght parameter and depends on encoding. */
118 : /* Add BOM detection for other encodings. */
119 : /* -------------------------------------------------------------------- */
120 :
121 : // Used to skip to actual beginning of XML data
122 194 : char* szPtr = szHeader;
123 :
124 198 : if( ( (unsigned char)szHeader[0] == 0xEF )
125 2 : && ( (unsigned char)szHeader[1] == 0xBB )
126 2 : && ( (unsigned char)szHeader[2] == 0xBF) )
127 : {
128 2 : szPtr += 3;
129 : }
130 :
131 : /* -------------------------------------------------------------------- */
132 : /* Here, we expect the opening chevrons of NAS tree root element */
133 : /* -------------------------------------------------------------------- */
134 194 : if( szPtr[0] != '<'
135 : || strstr(szPtr,"opengis.net/gml") == NULL
136 : || strstr(szPtr,"NAS-Operationen.xsd") == NULL )
137 : {
138 189 : VSIFClose( fp );
139 189 : return FALSE;
140 : }
141 : }
142 :
143 : /* -------------------------------------------------------------------- */
144 : /* We assume now that it is NAS. Close and instantiate a */
145 : /* NASReader on it. */
146 : /* -------------------------------------------------------------------- */
147 5 : VSIFClose( fp );
148 :
149 5 : poReader = CreateNASReader();
150 5 : if( poReader == NULL )
151 : {
152 : CPLError( CE_Failure, CPLE_AppDefined,
153 : "File %s appears to be NAS but the NAS reader can't\n"
154 : "be instantiated, likely because Xerces support wasn't\n"
155 : "configured in.",
156 0 : pszNewName );
157 0 : return FALSE;
158 : }
159 :
160 5 : poReader->SetSourceFile( pszNewName );
161 :
162 5 : pszName = CPLStrdup( pszNewName );
163 :
164 : /* -------------------------------------------------------------------- */
165 : /* Can we find a NAS Feature Schema (.gfs) for the input file? */
166 : /* -------------------------------------------------------------------- */
167 : const char *pszGFSFilename;
168 : VSIStatBuf sGFSStatBuf, sNASStatBuf;
169 5 : int bHaveSchema = FALSE;
170 :
171 5 : pszGFSFilename = CPLResetExtension( pszNewName, "gfs" );
172 5 : if( CPLStat( pszGFSFilename, &sGFSStatBuf ) == 0 )
173 : {
174 2 : CPLStat( pszNewName, &sNASStatBuf );
175 :
176 2 : if( sNASStatBuf.st_mtime > sGFSStatBuf.st_mtime )
177 : {
178 : CPLDebug( "NAS",
179 : "Found %s but ignoring because it appears\n"
180 : "be older than the associated NAS file.",
181 0 : pszGFSFilename );
182 : }
183 : else
184 : {
185 2 : bHaveSchema = poReader->LoadClasses( pszGFSFilename );
186 : }
187 : }
188 :
189 : /* -------------------------------------------------------------------- */
190 : /* Force a first pass to establish the schema. Eventually we */
191 : /* will have mechanisms for remembering the schema and related */
192 : /* information. */
193 : /* -------------------------------------------------------------------- */
194 5 : CPLErrorReset();
195 8 : if( !bHaveSchema
196 3 : && !poReader->PrescanForSchema( TRUE )
197 : && CPLGetLastErrorType() == CE_Failure )
198 : {
199 : // we assume an errors have been reported.
200 0 : return FALSE;
201 : }
202 :
203 : /* -------------------------------------------------------------------- */
204 : /* Save the schema file if possible. Don't make a fuss if we */
205 : /* can't ... could be read-only directory or something. */
206 : /* -------------------------------------------------------------------- */
207 5 : if( !bHaveSchema && poReader->GetClassCount() > 0 )
208 : {
209 2 : FILE *fp = NULL;
210 :
211 2 : pszGFSFilename = CPLResetExtension( pszNewName, "gfs" );
212 2 : if( CPLStat( pszGFSFilename, &sGFSStatBuf ) != 0
213 : && (fp = VSIFOpen( pszGFSFilename, "wt" )) != NULL )
214 : {
215 2 : VSIFClose( fp );
216 2 : poReader->SaveClasses( pszGFSFilename );
217 : }
218 : else
219 : {
220 : CPLDebug("NAS",
221 : "Not saving %s files already exists or can't be created.",
222 0 : pszGFSFilename );
223 : }
224 : }
225 :
226 : /* -------------------------------------------------------------------- */
227 : /* Translate the NASFeatureClasses into layers. */
228 : /* -------------------------------------------------------------------- */
229 : papoLayers = (OGRLayer **)
230 5 : CPLCalloc( sizeof(OGRNASLayer *), poReader->GetClassCount()+1 );
231 5 : nLayers = 0;
232 :
233 137 : while( nLayers < poReader->GetClassCount() )
234 : {
235 127 : papoLayers[nLayers] = TranslateNASSchema(poReader->GetClass(nLayers));
236 127 : nLayers++;
237 : }
238 :
239 5 : poRelationLayer = new OGRNASRelationLayer( this );
240 5 : papoLayers[nLayers++] = poRelationLayer;
241 :
242 5 : return TRUE;
243 : }
244 :
245 : /************************************************************************/
246 : /* TranslateNASSchema() */
247 : /************************************************************************/
248 :
249 127 : OGRNASLayer *OGRNASDataSource::TranslateNASSchema( GMLFeatureClass *poClass )
250 :
251 : {
252 : OGRNASLayer *poLayer;
253 : OGRwkbGeometryType eGType
254 127 : = (OGRwkbGeometryType) poClass->GetGeometryType();
255 :
256 127 : if( poClass->GetFeatureCount() == 0 )
257 0 : eGType = wkbUnknown;
258 :
259 : /* -------------------------------------------------------------------- */
260 : /* Translate SRS. */
261 : /* -------------------------------------------------------------------- */
262 127 : const char* pszSRSName = poClass->GetSRSName();
263 127 : OGRSpatialReference* poSRS = NULL;
264 127 : if (pszSRSName)
265 : {
266 : int i;
267 :
268 0 : poSRS = new OGRSpatialReference();
269 :
270 0 : const char *pszHandle = strrchr( pszSRSName, ':' );
271 0 : if( pszHandle != NULL )
272 0 : pszHandle += 1;
273 :
274 0 : for( i = 0; apszURNNames[i*2+0] != NULL; i++ )
275 : {
276 0 : const char *pszTarget = apszURNNames[i*2+0];
277 0 : int nTLen = strlen(pszTarget);
278 :
279 : // Are we just looking for a prefix match?
280 0 : if( pszTarget[nTLen-1] == '*' )
281 : {
282 0 : if( EQUALN(pszTarget,pszHandle,nTLen-1) )
283 0 : pszSRSName = apszURNNames[i*2+1];
284 : }
285 : else
286 : {
287 0 : if( EQUAL(pszTarget,pszHandle) )
288 0 : pszSRSName = apszURNNames[i*2+1];
289 : }
290 : }
291 :
292 0 : if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE)
293 : {
294 : CPLDebug( "NAS", "Failed to translate srsName='%s'",
295 0 : pszSRSName );
296 0 : delete poSRS;
297 0 : poSRS = NULL;
298 : }
299 : }
300 :
301 : /* -------------------------------------------------------------------- */
302 : /* Create an empty layer. */
303 : /* -------------------------------------------------------------------- */
304 127 : poLayer = new OGRNASLayer( poClass->GetName(), poSRS, eGType, this );
305 127 : delete poSRS;
306 :
307 : /* -------------------------------------------------------------------- */
308 : /* Added attributes (properties). */
309 : /* -------------------------------------------------------------------- */
310 1091 : for( int iField = 0; iField < poClass->GetPropertyCount(); iField++ )
311 : {
312 964 : GMLPropertyDefn *poProperty = poClass->GetProperty( iField );
313 : OGRFieldType eFType;
314 :
315 964 : if( poProperty->GetType() == GMLPT_Untyped )
316 34 : eFType = OFTString;
317 930 : else if( poProperty->GetType() == GMLPT_String )
318 571 : eFType = OFTString;
319 359 : else if( poProperty->GetType() == GMLPT_Integer )
320 330 : eFType = OFTInteger;
321 29 : else if( poProperty->GetType() == GMLPT_Real )
322 12 : eFType = OFTReal;
323 17 : else if( poProperty->GetType() == GMLPT_StringList )
324 13 : eFType = OFTStringList;
325 4 : else if( poProperty->GetType() == GMLPT_IntegerList )
326 4 : eFType = OFTIntegerList;
327 0 : else if( poProperty->GetType() == GMLPT_RealList )
328 0 : eFType = OFTRealList;
329 : else
330 0 : eFType = OFTString;
331 :
332 964 : OGRFieldDefn oField( poProperty->GetName(), eFType );
333 964 : if ( EQUALN(oField.GetNameRef(), "ogr:", 4) )
334 0 : oField.SetName(poProperty->GetName()+4);
335 964 : if( poProperty->GetWidth() > 0 )
336 571 : oField.SetWidth( poProperty->GetWidth() );
337 :
338 964 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
339 : }
340 :
341 127 : return poLayer;
342 : }
343 :
344 : /************************************************************************/
345 : /* GetLayer() */
346 : /************************************************************************/
347 :
348 116 : OGRLayer *OGRNASDataSource::GetLayer( int iLayer )
349 :
350 : {
351 116 : if( iLayer < 0 || iLayer >= nLayers )
352 0 : return NULL;
353 : else
354 116 : return papoLayers[iLayer];
355 : }
356 :
357 : /************************************************************************/
358 : /* TestCapability() */
359 : /************************************************************************/
360 :
361 0 : int OGRNASDataSource::TestCapability( const char * pszCap )
362 :
363 : {
364 0 : return FALSE;
365 : }
366 :
367 : /************************************************************************/
368 : /* PopulateRelations() */
369 : /************************************************************************/
370 :
371 1 : void OGRNASDataSource::PopulateRelations()
372 :
373 : {
374 : GMLFeature *poFeature;
375 :
376 1 : poReader->ResetReading();
377 8394 : while( (poFeature = poReader->NextFeature()) != NULL )
378 : {
379 8392 : char **papszOBProperties = poFeature->GetOBProperties();
380 : int i;
381 :
382 10228 : for( i = 0; papszOBProperties != NULL && papszOBProperties[i] != NULL;
383 : i++ )
384 : {
385 1836 : int nGMLIdIndex = poFeature->GetClass()->GetPropertyIndex( "gml_id" );
386 1836 : const GMLProperty *psGMLId = (nGMLIdIndex >= 0) ? poFeature->GetProperty(nGMLIdIndex ) : NULL;
387 1836 : char *pszName = NULL;
388 1836 : const char *pszValue = CPLParseNameValue( papszOBProperties[i],
389 3672 : &pszName );
390 :
391 1836 : if( EQUALN(pszValue,"urn:adv:oid:",12)
392 : && psGMLId != NULL && psGMLId->nSubProperties == 1 )
393 : {
394 1836 : poRelationLayer->AddRelation( psGMLId->papszSubProperties[0],
395 : pszName,
396 3672 : pszValue + 12 );
397 : }
398 1836 : CPLFree( pszName );
399 : }
400 :
401 8392 : delete poFeature;
402 : }
403 :
404 1 : poRelationLayer->MarkRelationsPopulated();
405 1 : }
|