1 : /******************************************************************************
2 : * $Id: ogrnasdatasource.cpp 24105 2012-03-10 12:08:04Z 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 24105 2012-03-10 12:08:04Z 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 710 : OGRNASDataSource::OGRNASDataSource()
49 :
50 : {
51 710 : pszName = NULL;
52 710 : papoLayers = NULL;
53 710 : nLayers = 0;
54 :
55 710 : poReader = NULL;
56 710 : }
57 :
58 : /************************************************************************/
59 : /* ~OGRNASDataSource() */
60 : /************************************************************************/
61 :
62 710 : OGRNASDataSource::~OGRNASDataSource()
63 :
64 : {
65 710 : CPLFree( pszName );
66 :
67 974 : for( int i = 0; i < nLayers; i++ )
68 264 : delete papoLayers[i];
69 :
70 710 : CPLFree( papoLayers );
71 :
72 710 : if( poReader )
73 10 : delete poReader;
74 710 : }
75 :
76 : /************************************************************************/
77 : /* Open() */
78 : /************************************************************************/
79 :
80 710 : 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 710 : fp = VSIFOpen( pszNewName, "r" );
90 710 : if( fp == NULL )
91 : {
92 220 : if( !bTestOpen )
93 : CPLError( CE_Failure, CPLE_OpenFailed,
94 : "Failed to open NAS file `%s'.",
95 0 : pszNewName );
96 :
97 220 : 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 490 : if( bTestOpen )
105 : {
106 490 : size_t nRead = VSIFRead( szHeader, 1, sizeof(szHeader), fp );
107 490 : if (nRead <= 0)
108 : {
109 16 : VSIFClose( fp );
110 16 : return FALSE;
111 : }
112 474 : 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 474 : char* szPtr = szHeader;
123 :
124 482 : if( ( (unsigned char)szHeader[0] == 0xEF )
125 4 : && ( (unsigned char)szHeader[1] == 0xBB )
126 4 : && ( (unsigned char)szHeader[2] == 0xBF) )
127 : {
128 4 : szPtr += 3;
129 : }
130 :
131 : /* -------------------------------------------------------------------- */
132 : /* Here, we expect the opening chevrons of NAS tree root element */
133 : /* -------------------------------------------------------------------- */
134 474 : if( szPtr[0] != '<'
135 : || strstr(szPtr,"opengis.net/gml") == NULL
136 : || strstr(szPtr,"NAS-Operationen.xsd") == NULL )
137 : {
138 464 : VSIFClose( fp );
139 464 : return FALSE;
140 : }
141 : }
142 :
143 : /* -------------------------------------------------------------------- */
144 : /* We assume now that it is NAS. Close and instantiate a */
145 : /* NASReader on it. */
146 : /* -------------------------------------------------------------------- */
147 10 : VSIFClose( fp );
148 :
149 10 : poReader = CreateNASReader();
150 10 : 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 10 : poReader->SetSourceFile( pszNewName );
161 :
162 10 : 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 10 : int bHaveSchema = FALSE;
170 :
171 10 : pszGFSFilename = CPLResetExtension( pszNewName, "gfs" );
172 10 : if( CPLStat( pszGFSFilename, &sGFSStatBuf ) == 0 )
173 : {
174 4 : CPLStat( pszNewName, &sNASStatBuf );
175 :
176 4 : 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 4 : 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 10 : CPLErrorReset();
195 16 : if( !bHaveSchema
196 6 : && !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 10 : if( !bHaveSchema && poReader->GetClassCount() > 0 )
208 : {
209 4 : FILE *fp = NULL;
210 :
211 4 : pszGFSFilename = CPLResetExtension( pszNewName, "gfs" );
212 4 : if( CPLStat( pszGFSFilename, &sGFSStatBuf ) != 0
213 : && (fp = VSIFOpen( pszGFSFilename, "wt" )) != NULL )
214 : {
215 4 : VSIFClose( fp );
216 4 : 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 10 : CPLCalloc( sizeof(OGRNASLayer *), poReader->GetClassCount()+1 );
231 10 : nLayers = 0;
232 :
233 274 : while( nLayers < poReader->GetClassCount() )
234 : {
235 254 : papoLayers[nLayers] = TranslateNASSchema(poReader->GetClass(nLayers));
236 254 : nLayers++;
237 : }
238 :
239 10 : poRelationLayer = new OGRNASRelationLayer( this );
240 :
241 : // keep delete the last layer
242 18 : if( nLayers>0 && EQUAL( papoLayers[nLayers-1]->GetName(), "Delete" ) )
243 : {
244 4 : papoLayers[nLayers] = papoLayers[nLayers-1];
245 4 : papoLayers[nLayers-1] = poRelationLayer;
246 : }
247 : else
248 : {
249 6 : papoLayers[nLayers] = poRelationLayer;
250 : }
251 :
252 10 : nLayers++;
253 :
254 10 : return TRUE;
255 : }
256 :
257 : /************************************************************************/
258 : /* TranslateNASSchema() */
259 : /************************************************************************/
260 :
261 254 : OGRNASLayer *OGRNASDataSource::TranslateNASSchema( GMLFeatureClass *poClass )
262 :
263 : {
264 : OGRNASLayer *poLayer;
265 : OGRwkbGeometryType eGType
266 254 : = (OGRwkbGeometryType) poClass->GetGeometryType();
267 :
268 254 : if( poClass->GetFeatureCount() == 0 )
269 0 : eGType = wkbUnknown;
270 :
271 : /* -------------------------------------------------------------------- */
272 : /* Translate SRS. */
273 : /* -------------------------------------------------------------------- */
274 254 : const char* pszSRSName = poClass->GetSRSName();
275 254 : OGRSpatialReference* poSRS = NULL;
276 254 : 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 254 : poLayer = new OGRNASLayer( poClass->GetName(), poSRS, eGType, this );
317 254 : delete poSRS;
318 :
319 : /* -------------------------------------------------------------------- */
320 : /* Added attributes (properties). */
321 : /* -------------------------------------------------------------------- */
322 2190 : for( int iField = 0; iField < poClass->GetPropertyCount(); iField++ )
323 : {
324 1936 : GMLPropertyDefn *poProperty = poClass->GetProperty( iField );
325 : OGRFieldType eFType;
326 :
327 1936 : if( poProperty->GetType() == GMLPT_Untyped )
328 68 : eFType = OFTString;
329 1868 : else if( poProperty->GetType() == GMLPT_String )
330 1150 : eFType = OFTString;
331 718 : else if( poProperty->GetType() == GMLPT_Integer )
332 660 : eFType = OFTInteger;
333 58 : else if( poProperty->GetType() == GMLPT_Real )
334 24 : eFType = OFTReal;
335 34 : else if( poProperty->GetType() == GMLPT_StringList )
336 26 : eFType = OFTStringList;
337 8 : else if( poProperty->GetType() == GMLPT_IntegerList )
338 8 : eFType = OFTIntegerList;
339 0 : else if( poProperty->GetType() == GMLPT_RealList )
340 0 : eFType = OFTRealList;
341 : else
342 0 : eFType = OFTString;
343 :
344 1936 : OGRFieldDefn oField( poProperty->GetName(), eFType );
345 1936 : if ( EQUALN(oField.GetNameRef(), "ogr:", 4) )
346 0 : oField.SetName(poProperty->GetName()+4);
347 1936 : if( poProperty->GetWidth() > 0 )
348 1150 : oField.SetWidth( poProperty->GetWidth() );
349 :
350 1936 : poLayer->GetLayerDefn()->AddFieldDefn( &oField );
351 : }
352 :
353 254 : return poLayer;
354 : }
355 :
356 : /************************************************************************/
357 : /* GetLayer() */
358 : /************************************************************************/
359 :
360 236 : OGRLayer *OGRNASDataSource::GetLayer( int iLayer )
361 :
362 : {
363 236 : if( iLayer < 0 || iLayer >= nLayers )
364 0 : return NULL;
365 : else
366 236 : 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 2 : void OGRNASDataSource::PopulateRelations()
384 :
385 : {
386 : GMLFeature *poFeature;
387 :
388 2 : poReader->ResetReading();
389 16788 : while( (poFeature = poReader->NextFeature()) != NULL )
390 : {
391 16784 : char **papszOBProperties = poFeature->GetOBProperties();
392 : int i;
393 :
394 20456 : for( i = 0; papszOBProperties != NULL && papszOBProperties[i] != NULL;
395 : i++ )
396 : {
397 3672 : int nGMLIdIndex = poFeature->GetClass()->GetPropertyIndex( "gml_id" );
398 3672 : const GMLProperty *psGMLId = (nGMLIdIndex >= 0) ? poFeature->GetProperty(nGMLIdIndex ) : NULL;
399 3672 : char *pszName = NULL;
400 3672 : const char *pszValue = CPLParseNameValue( papszOBProperties[i],
401 7344 : &pszName );
402 :
403 3672 : if( EQUALN(pszValue,"urn:adv:oid:",12)
404 : && psGMLId != NULL && psGMLId->nSubProperties == 1 )
405 : {
406 3672 : poRelationLayer->AddRelation( psGMLId->papszSubProperties[0],
407 : pszName,
408 7344 : pszValue + 12 );
409 : }
410 3672 : CPLFree( pszName );
411 : }
412 :
413 16784 : delete poFeature;
414 : }
415 :
416 2 : poRelationLayer->MarkRelationsPopulated();
417 2 : }
|