1 : /******************************************************************************
2 : * $Id: ogrnasdatasource.cpp 25120 2012-10-13 22:38:57Z 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 25120 2012-10-13 22:38:57Z 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 : NULL, NULL
42 : };
43 :
44 : /************************************************************************/
45 : /* OGRNASDataSource() */
46 : /************************************************************************/
47 :
48 459 : OGRNASDataSource::OGRNASDataSource()
49 :
50 : {
51 459 : pszName = NULL;
52 459 : papoLayers = NULL;
53 459 : nLayers = 0;
54 :
55 459 : poReader = NULL;
56 459 : }
57 :
58 : /************************************************************************/
59 : /* ~OGRNASDataSource() */
60 : /************************************************************************/
61 :
62 459 : OGRNASDataSource::~OGRNASDataSource()
63 :
64 : {
65 459 : CPLFree( pszName );
66 :
67 591 : for( int i = 0; i < nLayers; i++ )
68 132 : delete papoLayers[i];
69 :
70 459 : CPLFree( papoLayers );
71 :
72 459 : if( poReader )
73 5 : delete poReader;
74 459 : }
75 :
76 : /************************************************************************/
77 : /* Open() */
78 : /************************************************************************/
79 :
80 459 : 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 459 : fp = VSIFOpen( pszNewName, "r" );
90 459 : if( fp == NULL )
91 : {
92 164 : if( !bTestOpen )
93 : CPLError( CE_Failure, CPLE_OpenFailed,
94 : "Failed to open NAS file `%s'.",
95 0 : pszNewName );
96 :
97 164 : 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 295 : if( bTestOpen )
105 : {
106 295 : size_t nRead = VSIFRead( szHeader, 1, sizeof(szHeader), fp );
107 295 : if (nRead <= 0)
108 : {
109 11 : VSIFClose( fp );
110 11 : return FALSE;
111 : }
112 284 : 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 284 : char* szPtr = szHeader;
123 :
124 288 : 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 284 : if( szPtr[0] != '<'
135 : || strstr(szPtr,"opengis.net/gml") == NULL
136 : || strstr(szPtr,"NAS-Operationen.xsd") == NULL )
137 : {
138 279 : VSIFClose( fp );
139 279 : 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 :
241 : // keep delete the last layer
242 9 : if( nLayers>0 && EQUAL( papoLayers[nLayers-1]->GetName(), "Delete" ) )
243 : {
244 2 : papoLayers[nLayers] = papoLayers[nLayers-1];
245 2 : papoLayers[nLayers-1] = poRelationLayer;
246 : }
247 : else
248 : {
249 3 : papoLayers[nLayers] = poRelationLayer;
250 : }
251 :
252 5 : nLayers++;
253 :
254 5 : return TRUE;
255 : }
256 :
257 : /************************************************************************/
258 : /* TranslateNASSchema() */
259 : /************************************************************************/
260 :
261 127 : OGRNASLayer *OGRNASDataSource::TranslateNASSchema( GMLFeatureClass *poClass )
262 :
263 : {
264 : OGRNASLayer *poLayer;
265 : OGRwkbGeometryType eGType
266 127 : = (OGRwkbGeometryType) poClass->GetGeometryType();
267 :
268 127 : if( poClass->GetFeatureCount() == 0 )
269 0 : eGType = wkbUnknown;
270 :
271 : /* -------------------------------------------------------------------- */
272 : /* Translate SRS. */
273 : /* -------------------------------------------------------------------- */
274 127 : const char* pszSRSName = poClass->GetSRSName();
275 127 : OGRSpatialReference* poSRS = NULL;
276 127 : if (pszSRSName)
277 : {
278 : int i;
279 :
280 0 : poSRS = new OGRSpatialReference();
281 :
282 0 : const char *pszHandle = strrchr( pszSRSName, ':' );
283 0 : if( pszHandle != NULL )
284 0 : pszHandle += 1;
285 :
286 0 : for( i = 0; apszURNNames[i*2+0] != NULL; i++ )
287 : {
288 0 : const char *pszTarget = apszURNNames[i*2+0];
289 0 : int nTLen = strlen(pszTarget);
290 :
291 : // Are we just looking for a prefix match?
292 0 : if( pszTarget[nTLen-1] == '*' )
293 : {
294 0 : if( EQUALN(pszTarget,pszHandle,nTLen-1) )
295 0 : pszSRSName = apszURNNames[i*2+1];
296 : }
297 : else
298 : {
299 0 : if( EQUAL(pszTarget,pszHandle) )
300 0 : pszSRSName = apszURNNames[i*2+1];
301 : }
302 : }
303 :
304 0 : if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE)
305 : {
306 : CPLDebug( "NAS", "Failed to translate srsName='%s'",
307 0 : pszSRSName );
308 0 : delete poSRS;
309 0 : poSRS = NULL;
310 : }
311 : }
312 :
313 : /* -------------------------------------------------------------------- */
314 : /* Create an empty layer. */
315 : /* -------------------------------------------------------------------- */
316 127 : poLayer = new OGRNASLayer( poClass->GetName(), poSRS, eGType, this );
317 127 : delete poSRS;
318 :
319 : /* -------------------------------------------------------------------- */
320 : /* Added attributes (properties). */
321 : /* -------------------------------------------------------------------- */
322 1092 : for( int iField = 0; iField < poClass->GetPropertyCount(); iField++ )
323 : {
324 965 : GMLPropertyDefn *poProperty = poClass->GetProperty( iField );
325 : OGRFieldType eFType;
326 :
327 965 : if( poProperty->GetType() == GMLPT_Untyped )
328 34 : eFType = OFTString;
329 931 : else if( poProperty->GetType() == GMLPT_String )
330 573 : eFType = OFTString;
331 358 : else if( poProperty->GetType() == GMLPT_Integer )
332 331 : eFType = OFTInteger;
333 27 : else if( poProperty->GetType() == GMLPT_Real )
334 12 : eFType = OFTReal;
335 15 : else if( poProperty->GetType() == GMLPT_StringList )
336 11 : eFType = OFTStringList;
337 4 : else if( poProperty->GetType() == GMLPT_IntegerList )
338 4 : eFType = OFTIntegerList;
339 0 : else if( poProperty->GetType() == GMLPT_RealList )
340 0 : eFType = OFTRealList;
341 : else
342 0 : eFType = OFTString;
343 :
344 965 : OGRFieldDefn oField( poProperty->GetName(), eFType );
345 965 : if ( EQUALN(oField.GetNameRef(), "ogr:", 4) )
346 0 : oField.SetName(poProperty->GetName()+4);
347 965 : if( poProperty->GetWidth() > 0 )
348 573 : oField.SetWidth( poProperty->GetWidth() );
349 :
350 965 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
351 : }
352 :
353 127 : return poLayer;
354 : }
355 :
356 : /************************************************************************/
357 : /* GetLayer() */
358 : /************************************************************************/
359 :
360 118 : OGRLayer *OGRNASDataSource::GetLayer( int iLayer )
361 :
362 : {
363 118 : if( iLayer < 0 || iLayer >= nLayers )
364 0 : return NULL;
365 : else
366 118 : return papoLayers[iLayer];
367 : }
368 :
369 : /************************************************************************/
370 : /* TestCapability() */
371 : /************************************************************************/
372 :
373 0 : int OGRNASDataSource::TestCapability( const char * pszCap )
374 :
375 : {
376 0 : return FALSE;
377 : }
378 :
379 : /************************************************************************/
380 : /* PopulateRelations() */
381 : /************************************************************************/
382 :
383 1 : void OGRNASDataSource::PopulateRelations()
384 :
385 : {
386 : GMLFeature *poFeature;
387 :
388 1 : poReader->ResetReading();
389 8394 : while( (poFeature = poReader->NextFeature()) != NULL )
390 : {
391 8392 : char **papszOBProperties = poFeature->GetOBProperties();
392 : int i;
393 :
394 10228 : for( i = 0; papszOBProperties != NULL && papszOBProperties[i] != NULL;
395 : i++ )
396 : {
397 1836 : int nGMLIdIndex = poFeature->GetClass()->GetPropertyIndex( "gml_id" );
398 1836 : const GMLProperty *psGMLId = (nGMLIdIndex >= 0) ? poFeature->GetProperty(nGMLIdIndex ) : NULL;
399 1836 : char *pszName = NULL;
400 1836 : const char *pszValue = CPLParseNameValue( papszOBProperties[i],
401 3672 : &pszName );
402 :
403 1836 : if( EQUALN(pszValue,"urn:adv:oid:",12)
404 : && psGMLId != NULL && psGMLId->nSubProperties == 1 )
405 : {
406 1836 : poRelationLayer->AddRelation( psGMLId->papszSubProperties[0],
407 : pszName,
408 3672 : pszValue + 12 );
409 : }
410 1836 : CPLFree( pszName );
411 : }
412 :
413 8392 : delete poFeature;
414 : }
415 :
416 1 : poRelationLayer->MarkRelationsPopulated();
417 1 : }
|