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