1 : /******************************************************************************
2 : * $Id: ogrgmllayer.cpp 20027 2010-07-11 17:58:47Z rouault $
3 : *
4 : * Project: OGR
5 : * Purpose: Implements OGRGMLLayer 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_gml.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_port.h"
33 : #include "cpl_string.h"
34 : #include "ogr_p.h"
35 :
36 : CPL_CVSID("$Id: ogrgmllayer.cpp 20027 2010-07-11 17:58:47Z rouault $");
37 :
38 : /************************************************************************/
39 : /* OGRGMLLayer() */
40 : /************************************************************************/
41 :
42 : OGRGMLLayer::OGRGMLLayer( const char * pszName,
43 : OGRSpatialReference *poSRSIn, int bWriterIn,
44 : OGRwkbGeometryType eReqType,
45 19 : OGRGMLDataSource *poDSIn )
46 :
47 : {
48 19 : if( poSRSIn == NULL )
49 19 : poSRS = NULL;
50 : else
51 0 : poSRS = poSRSIn->Clone();
52 :
53 19 : iNextGMLId = 0;
54 19 : nTotalGMLCount = -1;
55 19 : bInvalidFIDFound = FALSE;
56 19 : pszFIDPrefix = NULL;
57 :
58 19 : poDS = poDSIn;
59 :
60 19 : if ( EQUALN(pszName, "ogr:", 4) )
61 0 : poFeatureDefn = new OGRFeatureDefn( pszName+4 );
62 : else
63 19 : poFeatureDefn = new OGRFeatureDefn( pszName );
64 19 : poFeatureDefn->Reference();
65 19 : poFeatureDefn->SetGeomType( eReqType );
66 :
67 19 : bWriter = bWriterIn;
68 :
69 : /* -------------------------------------------------------------------- */
70 : /* Reader's should get the corresponding GMLFeatureClass and */
71 : /* cache it. */
72 : /* -------------------------------------------------------------------- */
73 19 : if( !bWriter )
74 17 : poFClass = poDS->GetReader()->GetClass( pszName );
75 : else
76 2 : poFClass = NULL;
77 19 : }
78 :
79 : /************************************************************************/
80 : /* ~OGRGMLLayer() */
81 : /************************************************************************/
82 :
83 19 : OGRGMLLayer::~OGRGMLLayer()
84 :
85 : {
86 19 : CPLFree(pszFIDPrefix);
87 :
88 19 : if( poFeatureDefn )
89 19 : poFeatureDefn->Release();
90 :
91 19 : if( poSRS != NULL )
92 0 : poSRS->Release();
93 19 : }
94 :
95 : /************************************************************************/
96 : /* ResetReading() */
97 : /************************************************************************/
98 :
99 18 : void OGRGMLLayer::ResetReading()
100 :
101 : {
102 18 : iNextGMLId = 0;
103 18 : poDS->GetReader()->ResetReading();
104 18 : }
105 :
106 : /************************************************************************/
107 : /* GetNextFeature() */
108 : /************************************************************************/
109 :
110 42 : OGRFeature *OGRGMLLayer::GetNextFeature()
111 :
112 : {
113 42 : GMLFeature *poGMLFeature = NULL;
114 42 : OGRGeometry *poGeom = NULL;
115 :
116 42 : if (bWriter)
117 : {
118 : CPLError(CE_Failure, CPLE_NotSupported,
119 0 : "Cannot read features when writing a GML file");
120 0 : return NULL;
121 : }
122 :
123 42 : if( iNextGMLId == 0 )
124 15 : ResetReading();
125 :
126 : /* ==================================================================== */
127 : /* Loop till we find and translate a feature meeting all our */
128 : /* requirements. */
129 : /* ==================================================================== */
130 2 : while( TRUE )
131 : {
132 : /* -------------------------------------------------------------------- */
133 : /* Cleanup last feature, and get a new raw gml feature. */
134 : /* -------------------------------------------------------------------- */
135 44 : if( poGMLFeature != NULL )
136 2 : delete poGMLFeature;
137 :
138 44 : if( poGeom != NULL )
139 : {
140 1 : delete poGeom;
141 1 : poGeom = NULL;
142 : }
143 :
144 44 : poGMLFeature = poDS->GetReader()->NextFeature();
145 44 : if( poGMLFeature == NULL )
146 9 : return NULL;
147 :
148 : /* -------------------------------------------------------------------- */
149 : /* Is it of the proper feature class? */
150 : /* -------------------------------------------------------------------- */
151 :
152 : // We count reading low level GML features as a feature read for
153 : // work checking purposes, though at least we didn't necessary
154 : // have to turn it into an OGRFeature.
155 35 : m_nFeaturesRead++;
156 :
157 35 : if( poGMLFeature->GetClass() != poFClass )
158 1 : continue;
159 :
160 : /* -------------------------------------------------------------------- */
161 : /* Extract the fid: */
162 : /* -Assumes the fids are non-negative integers with an optional */
163 : /* prefix */
164 : /* -If a prefix differs from the prefix of the first feature from */
165 : /* the poDS then the fids from the poDS are ignored and are */
166 : /* assigned serially thereafter */
167 : /* -------------------------------------------------------------------- */
168 : int nFID;
169 34 : const char * pszGML_FID = poGMLFeature->GetFID();
170 34 : if( bInvalidFIDFound )
171 : {
172 6 : nFID = iNextGMLId++;
173 : }
174 28 : else if( pszGML_FID == NULL )
175 : {
176 3 : bInvalidFIDFound = TRUE;
177 3 : nFID = iNextGMLId++;
178 : }
179 25 : else if( iNextGMLId == 0 )
180 : {
181 12 : int i = strlen( pszGML_FID )-1, j = 0;
182 61 : while( i >= 0 && pszGML_FID[i] >= '0'
183 : && pszGML_FID[i] <= '9' && j<8)
184 37 : i--, j++;
185 : /* i points the last character of the fid */
186 12 : if( i >= 0 && j < 8 && pszFIDPrefix == NULL)
187 : {
188 9 : pszFIDPrefix = (char *) CPLMalloc(i+2);
189 9 : pszFIDPrefix[i+1] = '\0';
190 9 : strncpy(pszFIDPrefix, pszGML_FID, i+1);
191 : }
192 : /* pszFIDPrefix now contains the prefix or NULL if no prefix is found */
193 12 : if( j < 8 && sscanf(pszGML_FID+i+1, "%d", &nFID)==1)
194 : {
195 10 : if( iNextGMLId <= nFID )
196 10 : iNextGMLId = nFID + 1;
197 : }
198 : else
199 : {
200 2 : bInvalidFIDFound = TRUE;
201 2 : nFID = iNextGMLId++;
202 : }
203 : }
204 13 : else if( iNextGMLId != 0 )
205 : {
206 13 : const char* pszFIDPrefix_notnull = pszFIDPrefix;
207 13 : if (pszFIDPrefix_notnull == NULL) pszFIDPrefix_notnull = "";
208 13 : int nLenPrefix = strlen(pszFIDPrefix_notnull);
209 :
210 13 : if( strncmp(pszGML_FID, pszFIDPrefix_notnull, nLenPrefix) == 0 &&
211 : strlen(pszGML_FID+nLenPrefix) <= 9 &&
212 : sscanf(pszGML_FID+nLenPrefix, "%d", &nFID) == 1 )
213 : { /* fid with the prefix. Using its numerical part */
214 11 : if( iNextGMLId < nFID )
215 5 : iNextGMLId = nFID + 1;
216 : }
217 : else
218 : { /* fid without the aforementioned prefix or a valid numerical part */
219 2 : bInvalidFIDFound = TRUE;
220 2 : nFID = iNextGMLId++;
221 : }
222 : }
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* Does it satisfy the spatial query, if there is one? */
226 : /* -------------------------------------------------------------------- */
227 :
228 34 : if( poGMLFeature->GetGeometry() != NULL )
229 : {
230 32 : poGeom = OGRGeometryFactory::createFromGML( poGMLFeature->GetGeometry() );
231 : // We assume the createFromGML() function would have already
232 : // reported the error.
233 32 : if( poGeom == NULL )
234 : {
235 0 : delete poGMLFeature;
236 0 : return NULL;
237 : }
238 :
239 32 : if( m_poFilterGeom != NULL && !FilterGeometry( poGeom ) )
240 0 : continue;
241 : }
242 :
243 : /* -------------------------------------------------------------------- */
244 : /* Convert the whole feature into an OGRFeature. */
245 : /* -------------------------------------------------------------------- */
246 : int iField;
247 34 : OGRFeature *poOGRFeature = new OGRFeature( GetLayerDefn() );
248 :
249 34 : poOGRFeature->SetFID( nFID );
250 :
251 155 : for( iField = 0; iField < poFClass->GetPropertyCount(); iField++ )
252 : {
253 121 : const char *pszProperty = poGMLFeature->GetProperty( iField );
254 121 : if( pszProperty == NULL )
255 3 : continue;
256 :
257 118 : switch( poFClass->GetProperty(iField)->GetType() )
258 : {
259 : case GMLPT_Real:
260 : {
261 27 : poOGRFeature->SetField( iField, CPLAtof(pszProperty) );
262 : }
263 27 : break;
264 :
265 : case GMLPT_IntegerList:
266 : {
267 : char **papszItems =
268 0 : CSLTokenizeString2( pszProperty, ",", 0 );
269 0 : int nCount = CSLCount(papszItems);
270 0 : int *panIntList = (int *) CPLMalloc(sizeof(int) * nCount );
271 : int i;
272 :
273 0 : for( i = 0; i < nCount; i++ )
274 0 : panIntList[i] = atoi(papszItems[i]);
275 :
276 0 : poOGRFeature->SetField( iField, nCount, panIntList );
277 0 : CPLFree( panIntList );
278 0 : CSLDestroy( papszItems );
279 : }
280 0 : break;
281 :
282 : case GMLPT_RealList:
283 : {
284 : char **papszItems =
285 0 : CSLTokenizeString2( pszProperty, ",", 0 );
286 0 : int nCount = CSLCount(papszItems);
287 0 : double *padfList = (double *)CPLMalloc(sizeof(double)*nCount);
288 : int i;
289 :
290 0 : for( i = 0; i < nCount; i++ )
291 0 : padfList[i] = CPLAtof(papszItems[i]);
292 :
293 0 : poOGRFeature->SetField( iField, nCount, padfList );
294 0 : CPLFree( padfList );
295 0 : CSLDestroy( papszItems );
296 : }
297 0 : break;
298 :
299 : case GMLPT_StringList:
300 : {
301 : char **papszItems =
302 0 : CSLTokenizeString2( pszProperty, ",", 0 );
303 :
304 0 : poOGRFeature->SetField( iField, papszItems );
305 0 : CSLDestroy( papszItems );
306 : }
307 0 : break;
308 :
309 : default:
310 91 : poOGRFeature->SetField( iField, pszProperty );
311 : break;
312 : }
313 : }
314 :
315 : /* -------------------------------------------------------------------- */
316 : /* Test against the attribute query. */
317 : /* -------------------------------------------------------------------- */
318 34 : if( m_poAttrQuery != NULL
319 : && !m_poAttrQuery->Evaluate( poOGRFeature ) )
320 : {
321 1 : delete poOGRFeature;
322 1 : continue;
323 : }
324 :
325 : /* -------------------------------------------------------------------- */
326 : /* Wow, we got our desired feature. Return it. */
327 : /* -------------------------------------------------------------------- */
328 33 : delete poGMLFeature;
329 :
330 33 : poOGRFeature->SetGeometryDirectly( poGeom );
331 :
332 33 : return poOGRFeature;
333 : }
334 :
335 : return NULL;
336 : }
337 :
338 : /************************************************************************/
339 : /* GetFeatureCount() */
340 : /************************************************************************/
341 :
342 2 : int OGRGMLLayer::GetFeatureCount( int bForce )
343 :
344 : {
345 2 : if( poFClass == NULL )
346 0 : return 0;
347 :
348 2 : if( m_poFilterGeom != NULL || m_poAttrQuery != NULL )
349 0 : return OGRLayer::GetFeatureCount( bForce );
350 : else
351 : {
352 : /* If the schema is read from a .xsd file, we haven't read */
353 : /* the feature count, so compute it now */
354 2 : int nFeatureCount = poFClass->GetFeatureCount();
355 2 : if (nFeatureCount < 0)
356 : {
357 1 : nFeatureCount = OGRLayer::GetFeatureCount(bForce);
358 1 : poFClass->SetFeatureCount(nFeatureCount);
359 : }
360 2 : return nFeatureCount;
361 : }
362 : }
363 :
364 : /************************************************************************/
365 : /* GetExtent() */
366 : /************************************************************************/
367 :
368 0 : OGRErr OGRGMLLayer::GetExtent(OGREnvelope *psExtent, int bForce )
369 :
370 : {
371 : double dfXMin, dfXMax, dfYMin, dfYMax;
372 :
373 0 : if( poFClass != NULL &&
374 : poFClass->GetExtents( &dfXMin, &dfXMax, &dfYMin, &dfYMax ) )
375 : {
376 0 : psExtent->MinX = dfXMin;
377 0 : psExtent->MaxX = dfXMax;
378 0 : psExtent->MinY = dfYMin;
379 0 : psExtent->MaxY = dfYMax;
380 :
381 0 : return OGRERR_NONE;
382 : }
383 : else
384 0 : return OGRLayer::GetExtent( psExtent, bForce );
385 : }
386 :
387 : /************************************************************************/
388 : /* CreateFeature() */
389 : /************************************************************************/
390 :
391 2 : OGRErr OGRGMLLayer::CreateFeature( OGRFeature *poFeature )
392 :
393 : {
394 2 : FILE *fp = poDS->GetOutputFP();
395 :
396 2 : if( !bWriter )
397 0 : return OGRERR_FAILURE;
398 :
399 2 : poDS->PrintLine( fp, " <gml:featureMember>" );
400 :
401 2 : if( poFeature->GetFID() == OGRNullFID )
402 2 : poFeature->SetFID( iNextGMLId++ );
403 :
404 : poDS->PrintLine( fp, " <ogr:%s fid=\"F%ld\">",
405 : poFeatureDefn->GetName(),
406 2 : poFeature->GetFID() );
407 :
408 : // Write out Geometry - for now it isn't indented properly.
409 2 : if( poFeature->GetGeometryRef() != NULL )
410 : {
411 : char *pszGeometry;
412 0 : OGREnvelope sGeomBounds;
413 :
414 0 : pszGeometry = poFeature->GetGeometryRef()->exportToGML();
415 : poDS->PrintLine( fp, " <ogr:geometryProperty>%s</ogr:geometryProperty>",
416 0 : pszGeometry );
417 0 : CPLFree( pszGeometry );
418 :
419 0 : poFeature->GetGeometryRef()->getEnvelope( &sGeomBounds );
420 0 : poDS->GrowExtents( &sGeomBounds );
421 : }
422 :
423 : // Write all "set" fields.
424 8 : for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
425 : {
426 :
427 6 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn( iField );
428 :
429 6 : if( poFeature->IsFieldSet( iField ) )
430 : {
431 6 : const char *pszRaw = poFeature->GetFieldAsString( iField );
432 :
433 12 : while( *pszRaw == ' ' )
434 0 : pszRaw++;
435 :
436 6 : char *pszEscaped = OGRGetXML_UTF8_EscapedString( pszRaw );
437 :
438 6 : if (poFieldDefn->GetType() == OFTReal)
439 : {
440 : /* Use point as decimal separator */
441 2 : char* pszComma = strchr(pszEscaped, ',');
442 2 : if (pszComma)
443 0 : *pszComma = '.';
444 : }
445 :
446 : poDS->PrintLine( fp, " <ogr:%s>%s</ogr:%s>",
447 : poFieldDefn->GetNameRef(), pszEscaped,
448 6 : poFieldDefn->GetNameRef() );
449 6 : CPLFree( pszEscaped );
450 : }
451 : }
452 :
453 2 : poDS->PrintLine( fp, " </ogr:%s>", poFeatureDefn->GetName() );
454 2 : poDS->PrintLine( fp, " </gml:featureMember>" );
455 :
456 2 : return OGRERR_NONE;
457 : }
458 :
459 : /************************************************************************/
460 : /* TestCapability() */
461 : /************************************************************************/
462 :
463 0 : int OGRGMLLayer::TestCapability( const char * pszCap )
464 :
465 : {
466 0 : if( EQUAL(pszCap,OLCSequentialWrite) )
467 0 : return bWriter;
468 :
469 0 : else if( EQUAL(pszCap,OLCCreateField) )
470 0 : return bWriter && iNextGMLId == 0;
471 :
472 0 : else if( EQUAL(pszCap,OLCFastGetExtent) )
473 : {
474 : double dfXMin, dfXMax, dfYMin, dfYMax;
475 :
476 0 : if( poFClass == NULL )
477 0 : return FALSE;
478 :
479 0 : return poFClass->GetExtents( &dfXMin, &dfXMax, &dfYMin, &dfYMax );
480 : }
481 :
482 0 : else if( EQUAL(pszCap,OLCFastFeatureCount) )
483 : {
484 0 : if( poFClass == NULL
485 : || m_poFilterGeom != NULL
486 : || m_poAttrQuery != NULL )
487 0 : return FALSE;
488 :
489 0 : return poFClass->GetFeatureCount() != -1;
490 : }
491 :
492 0 : else if( EQUAL(pszCap,OLCStringsAsUTF8) )
493 0 : return TRUE;
494 :
495 : else
496 0 : return FALSE;
497 : }
498 :
499 : /************************************************************************/
500 : /* CreateField() */
501 : /************************************************************************/
502 :
503 6 : OGRErr OGRGMLLayer::CreateField( OGRFieldDefn *poField, int bApproxOK )
504 :
505 : {
506 6 : if( !bWriter || iNextGMLId != 0 )
507 0 : return OGRERR_FAILURE;
508 :
509 : /* -------------------------------------------------------------------- */
510 : /* Enforce XML naming semantics on element name. */
511 : /* -------------------------------------------------------------------- */
512 6 : OGRFieldDefn oCleanCopy( poField );
513 6 : char *pszName = CPLStrdup( poField->GetNameRef() );
514 6 : CPLCleanXMLElementName( pszName );
515 :
516 6 : if( strcmp(pszName,poField->GetNameRef()) != 0 )
517 : {
518 0 : if( !bApproxOK )
519 : {
520 0 : CPLFree( pszName );
521 : CPLError( CE_Failure, CPLE_AppDefined,
522 : "Unable to create field with name '%s', it would not\n"
523 : "be valid as an XML element name.",
524 0 : poField->GetNameRef() );
525 6 : return OGRERR_FAILURE;
526 : }
527 :
528 0 : oCleanCopy.SetName( pszName );
529 : CPLError( CE_Warning, CPLE_AppDefined,
530 : "Field name '%s' adjusted to '%s' to be a valid\n"
531 : "XML element name.",
532 0 : poField->GetNameRef(), pszName );
533 : }
534 :
535 6 : CPLFree( pszName );
536 :
537 :
538 6 : poFeatureDefn->AddFieldDefn( &oCleanCopy );
539 :
540 6 : return OGRERR_NONE;
541 : }
542 :
543 : /************************************************************************/
544 : /* GetSpatialRef() */
545 : /************************************************************************/
546 :
547 0 : OGRSpatialReference *OGRGMLLayer::GetSpatialRef()
548 :
549 : {
550 0 : return poSRS;
551 : }
552 :
553 : /************************************************************************/
554 : /* GetGeometryColumn() */
555 : /************************************************************************/
556 :
557 2 : const char* OGRGMLLayer::GetGeometryColumn()
558 : {
559 2 : if( poFClass == NULL || poFClass->GetGeometryElement() == NULL )
560 0 : return "";
561 :
562 2 : return poFClass->GetGeometryElement();
563 : }
|