Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OGR
4 : * Purpose: Implements OGRGMLLayer class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.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 "gmlutils.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_port.h"
34 : #include "cpl_string.h"
35 : #include "ogr_p.h"
36 : #include "ogr_api.h"
37 :
38 : /************************************************************************/
39 : /* OGRGMLLayer() */
40 : /************************************************************************/
41 :
42 631 : OGRGMLLayer::OGRGMLLayer(const char *pszName, bool bWriterIn,
43 631 : OGRGMLDataSource *poDSIn)
44 : : poFeatureDefn(new OGRFeatureDefn(
45 631 : pszName + (STARTS_WITH_CI(pszName, "ogr:") ? 4 : 0))),
46 : iNextGMLId(0), bInvalidFIDFound(false), pszFIDPrefix(nullptr),
47 : bWriter(bWriterIn), bSameSRS(false), poDS(poDSIn),
48 631 : poFClass(!bWriter ? poDS->GetReader()->GetClass(pszName) : nullptr),
49 : // Reader's should get the corresponding GMLFeatureClass and cache it.
50 1262 : hCacheSRS(GML_BuildOGRGeometryFromList_CreateCache()),
51 : // Compatibility option. Not advertized, because hopefully won't be
52 : // needed. Just put here in case.
53 : bUseOldFIDFormat(
54 631 : CPLTestBool(CPLGetConfigOption("GML_USE_OLD_FID_FORMAT", "FALSE"))),
55 : // Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer()
56 : // and GMLReader::GMLReader().
57 : bFaceHoleNegative(
58 2524 : CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")))
59 : {
60 631 : SetDescription(poFeatureDefn->GetName());
61 631 : poFeatureDefn->Reference();
62 631 : poFeatureDefn->SetGeomType(wkbNone);
63 631 : }
64 :
65 : /************************************************************************/
66 : /* ~OGRGMLLayer() */
67 : /************************************************************************/
68 :
69 1262 : OGRGMLLayer::~OGRGMLLayer()
70 :
71 : {
72 631 : CPLFree(pszFIDPrefix);
73 :
74 631 : if (poFeatureDefn)
75 631 : poFeatureDefn->Release();
76 :
77 631 : GML_BuildOGRGeometryFromList_DestroyCache(hCacheSRS);
78 1262 : }
79 :
80 : /************************************************************************/
81 : /* ResetReading() */
82 : /************************************************************************/
83 :
84 712 : void OGRGMLLayer::ResetReading()
85 :
86 : {
87 712 : if (bWriter)
88 16 : return;
89 :
90 1392 : if (poDS->GetReadMode() == INTERLEAVED_LAYERS ||
91 696 : poDS->GetReadMode() == SEQUENTIAL_LAYERS)
92 : {
93 : // Does the last stored feature belong to our layer ? If so, no
94 : // need to reset the reader.
95 88 : if (iNextGMLId == 0 && poDS->PeekStoredGMLFeature() != nullptr &&
96 0 : poDS->PeekStoredGMLFeature()->GetClass() == poFClass)
97 0 : return;
98 :
99 88 : delete poDS->PeekStoredGMLFeature();
100 88 : poDS->SetStoredGMLFeature(nullptr);
101 : }
102 :
103 696 : iNextGMLId = 0;
104 696 : poDS->GetReader()->ResetReading();
105 696 : CPLDebug("GML", "ResetReading()");
106 696 : if (poDS->GetLayerCount() > 1 && poDS->GetReadMode() == STANDARD)
107 : {
108 78 : const char *pszElementName = poFClass->GetElementName();
109 78 : const char *pszLastPipe = strrchr(pszElementName, '|');
110 78 : if (pszLastPipe != nullptr)
111 33 : pszElementName = pszLastPipe + 1;
112 78 : poDS->GetReader()->SetFilteredClassName(pszElementName);
113 : }
114 : }
115 :
116 : /************************************************************************/
117 : /* Increment() */
118 : /************************************************************************/
119 :
120 529 : static GIntBig Increment(GIntBig nVal)
121 : {
122 529 : if (nVal <= GINTBIG_MAX - 1)
123 529 : return nVal + 1;
124 0 : return nVal;
125 : }
126 :
127 : /************************************************************************/
128 : /* GetNextFeature() */
129 : /************************************************************************/
130 :
131 788 : OGRFeature *OGRGMLLayer::GetNextFeature()
132 :
133 : {
134 788 : if (bWriter)
135 : {
136 16 : CPLError(CE_Failure, CPLE_NotSupported,
137 : "Cannot read features when writing a GML file");
138 16 : return nullptr;
139 : }
140 :
141 772 : if (poDS->GetLastReadLayer() != this)
142 : {
143 345 : if (poDS->GetReadMode() != INTERLEAVED_LAYERS)
144 336 : ResetReading();
145 345 : poDS->SetLastReadLayer(this);
146 : }
147 :
148 : /* ==================================================================== */
149 : /* Loop till we find and translate a feature meeting all our */
150 : /* requirements. */
151 : /* ==================================================================== */
152 : while (true)
153 : {
154 1427 : GMLFeature *poGMLFeature = poDS->PeekStoredGMLFeature();
155 1427 : if (poGMLFeature != nullptr)
156 : {
157 3 : poDS->SetStoredGMLFeature(nullptr);
158 : }
159 : else
160 : {
161 1424 : poGMLFeature = poDS->GetReader()->NextFeature();
162 1424 : if (poGMLFeature == nullptr)
163 772 : return nullptr;
164 :
165 : // We count reading low level GML features as a feature read for
166 : // work checking purposes, though at least we didn't necessary
167 : // have to turn it into an OGRFeature.
168 1297 : m_nFeaturesRead++;
169 : }
170 :
171 : /* --------------------------------------------------------------------
172 : */
173 : /* Is it of the proper feature class? */
174 : /* --------------------------------------------------------------------
175 : */
176 :
177 1300 : if (poGMLFeature->GetClass() != poFClass)
178 : {
179 1827 : if (poDS->GetReadMode() == INTERLEAVED_LAYERS ||
180 1216 : (poDS->GetReadMode() == SEQUENTIAL_LAYERS && iNextGMLId != 0))
181 : {
182 4 : CPLAssert(poDS->PeekStoredGMLFeature() == nullptr);
183 4 : poDS->SetStoredGMLFeature(poGMLFeature);
184 4 : return nullptr;
185 : }
186 : else
187 : {
188 607 : delete poGMLFeature;
189 607 : continue;
190 : }
191 : }
192 :
193 : /* --------------------------------------------------------------------
194 : */
195 : /* Extract the fid: */
196 : /* -Assumes the fids are non-negative integers with an optional */
197 : /* prefix */
198 : /* -If a prefix differs from the prefix of the first feature from
199 : */
200 : /* the poDS then the fids from the poDS are ignored and are */
201 : /* assigned serially thereafter */
202 : /* --------------------------------------------------------------------
203 : */
204 689 : GIntBig nFID = -1;
205 689 : const char *pszGML_FID = poGMLFeature->GetFID();
206 689 : if (bInvalidFIDFound)
207 : {
208 43 : nFID = iNextGMLId;
209 43 : iNextGMLId = Increment(iNextGMLId);
210 : }
211 646 : else if (pszGML_FID == nullptr)
212 : {
213 108 : bInvalidFIDFound = true;
214 108 : nFID = iNextGMLId;
215 108 : iNextGMLId = Increment(iNextGMLId);
216 : }
217 538 : else if (iNextGMLId == 0)
218 : {
219 261 : int j = 0;
220 261 : int i = static_cast<int>(strlen(pszGML_FID)) - 1;
221 645 : while (i >= 0 && pszGML_FID[i] >= '0' && pszGML_FID[i] <= '9' &&
222 : j < 20)
223 : {
224 384 : i--;
225 384 : j++;
226 : }
227 : // i points the last character of the fid.
228 261 : if (i >= 0 && j < 20 && pszFIDPrefix == nullptr)
229 : {
230 198 : pszFIDPrefix = static_cast<char *>(CPLMalloc(i + 2));
231 198 : pszFIDPrefix[i + 1] = '\0';
232 198 : strncpy(pszFIDPrefix, pszGML_FID, i + 1);
233 : }
234 : // pszFIDPrefix now contains the prefix or NULL if no prefix is
235 : // found.
236 261 : if (j < 20 && sscanf(pszGML_FID + i + 1, CPL_FRMT_GIB, &nFID) == 1)
237 : {
238 255 : if (iNextGMLId <= nFID)
239 255 : iNextGMLId = Increment(nFID);
240 : }
241 : else
242 : {
243 6 : bInvalidFIDFound = true;
244 6 : nFID = iNextGMLId;
245 6 : iNextGMLId = Increment(iNextGMLId);
246 : }
247 : }
248 : else // if( iNextGMLId != 0 ).
249 : {
250 277 : const char *pszFIDPrefix_notnull = pszFIDPrefix;
251 277 : if (pszFIDPrefix_notnull == nullptr)
252 6 : pszFIDPrefix_notnull = "";
253 277 : int nLenPrefix = static_cast<int>(strlen(pszFIDPrefix_notnull));
254 :
255 827 : if (strncmp(pszGML_FID, pszFIDPrefix_notnull, nLenPrefix) == 0 &&
256 550 : strlen(pszGML_FID + nLenPrefix) < 20 &&
257 273 : sscanf(pszGML_FID + nLenPrefix, CPL_FRMT_GIB, &nFID) == 1)
258 : {
259 : // fid with the prefix. Using its numerical part.
260 272 : if (iNextGMLId < nFID)
261 112 : iNextGMLId = Increment(nFID);
262 : }
263 : else
264 : {
265 : // fid without the aforementioned prefix or a valid numerical
266 : // part.
267 5 : bInvalidFIDFound = true;
268 5 : nFID = iNextGMLId;
269 5 : iNextGMLId = Increment(iNextGMLId);
270 : }
271 : }
272 :
273 : /* --------------------------------------------------------------------
274 : */
275 : /* Does it satisfy the spatial query, if there is one? */
276 : /* --------------------------------------------------------------------
277 : */
278 :
279 689 : OGRGeometry **papoGeometries = nullptr;
280 689 : const CPLXMLNode *const *papsGeometry = poGMLFeature->GetGeometryList();
281 :
282 689 : const CPLXMLNode *apsGeometries[2] = {nullptr, nullptr};
283 : const CPLXMLNode *psBoundedByGeometry =
284 689 : poGMLFeature->GetBoundedByGeometry();
285 689 : if (psBoundedByGeometry && !(papsGeometry && papsGeometry[0]))
286 : {
287 6 : apsGeometries[0] = psBoundedByGeometry;
288 6 : papsGeometry = apsGeometries;
289 : }
290 :
291 689 : OGRGeometry *poGeom = nullptr;
292 :
293 689 : if (poFeatureDefn->GetGeomFieldCount() > 1)
294 : {
295 188 : papoGeometries = static_cast<OGRGeometry **>(CPLCalloc(
296 94 : poFeatureDefn->GetGeomFieldCount(), sizeof(OGRGeometry *)));
297 94 : const char *pszSRSName = poDS->GetGlobalSRSName();
298 301 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
299 : {
300 207 : const CPLXMLNode *psGeom = poGMLFeature->GetGeometryRef(i);
301 207 : if (psGeom != nullptr)
302 : {
303 163 : const CPLXMLNode *myGeometryList[2] = {psGeom, nullptr};
304 163 : poGeom = GML_BuildOGRGeometryFromList(
305 : myGeometryList, true,
306 163 : poDS->GetInvertAxisOrderIfLatLong(), pszSRSName,
307 163 : poDS->GetConsiderEPSGAsURN(),
308 163 : poDS->GetSwapCoordinates(),
309 163 : poDS->GetSecondaryGeometryOption(), hCacheSRS,
310 163 : bFaceHoleNegative);
311 :
312 : // Do geometry type changes if needed to match layer
313 : // geometry type.
314 163 : if (poGeom != nullptr)
315 : {
316 163 : papoGeometries[i] = OGRGeometryFactory::forceTo(
317 : poGeom,
318 163 : poFeatureDefn->GetGeomFieldDefn(i)->GetType());
319 163 : poGeom = nullptr;
320 : }
321 : else
322 : {
323 : // We assume the createFromGML() function would have
324 : // already reported the error.
325 0 : for (int j = 0; j < poFeatureDefn->GetGeomFieldCount();
326 : j++)
327 : {
328 0 : delete papoGeometries[j];
329 : }
330 0 : CPLFree(papoGeometries);
331 0 : delete poGMLFeature;
332 0 : return nullptr;
333 : }
334 : }
335 : }
336 :
337 0 : if (m_poFilterGeom != nullptr && m_iGeomFieldFilter >= 0 &&
338 0 : m_iGeomFieldFilter < poFeatureDefn->GetGeomFieldCount() &&
339 94 : papoGeometries[m_iGeomFieldFilter] &&
340 0 : !FilterGeometry(papoGeometries[m_iGeomFieldFilter]))
341 : {
342 0 : for (int j = 0; j < poFeatureDefn->GetGeomFieldCount(); j++)
343 : {
344 0 : delete papoGeometries[j];
345 : }
346 0 : CPLFree(papoGeometries);
347 0 : delete poGMLFeature;
348 0 : continue;
349 : }
350 : }
351 595 : else if (papsGeometry[0] &&
352 497 : strcmp(papsGeometry[0]->pszValue, "null") == 0)
353 : {
354 : // do nothing
355 : }
356 591 : else if (papsGeometry[0] != nullptr)
357 : {
358 493 : const char *pszSRSName = poDS->GetGlobalSRSName();
359 493 : CPLPushErrorHandler(CPLQuietErrorHandler);
360 493 : poGeom = GML_BuildOGRGeometryFromList(
361 493 : papsGeometry, true, poDS->GetInvertAxisOrderIfLatLong(),
362 493 : pszSRSName, poDS->GetConsiderEPSGAsURN(),
363 493 : poDS->GetSwapCoordinates(), poDS->GetSecondaryGeometryOption(),
364 493 : hCacheSRS, bFaceHoleNegative);
365 493 : CPLPopErrorHandler();
366 :
367 : // Do geometry type changes if needed to match layer geometry type.
368 493 : if (poGeom != nullptr)
369 : {
370 493 : poGeom = OGRGeometryFactory::forceTo(poGeom, GetGeomType());
371 : }
372 : else
373 : {
374 0 : const CPLString osLastErrorMsg(CPLGetLastErrorMsg());
375 :
376 0 : const bool bGoOn = CPLTestBool(
377 : CPLGetConfigOption("GML_SKIP_CORRUPTED_FEATURES", "NO"));
378 :
379 0 : CPLError(
380 : bGoOn ? CE_Warning : CE_Failure, CPLE_AppDefined,
381 : "Geometry of feature " CPL_FRMT_GIB
382 : " %scannot be parsed: %s%s",
383 0 : nFID, pszGML_FID ? CPLSPrintf("%s ", pszGML_FID) : "",
384 : osLastErrorMsg.c_str(),
385 : bGoOn ? ". Skipping to next feature."
386 : : ". You may set the GML_SKIP_CORRUPTED_FEATURES "
387 : "configuration option to YES to skip to the next "
388 : "feature");
389 0 : delete poGMLFeature;
390 0 : if (bGoOn)
391 0 : continue;
392 0 : return nullptr;
393 : }
394 :
395 493 : if (m_poFilterGeom != nullptr && !FilterGeometry(poGeom))
396 : {
397 20 : delete poGMLFeature;
398 20 : delete poGeom;
399 20 : continue;
400 : }
401 : }
402 :
403 : /* --------------------------------------------------------------------
404 : */
405 : /* Convert the whole feature into an OGRFeature. */
406 : /* --------------------------------------------------------------------
407 : */
408 669 : int iDstField = 0;
409 669 : OGRFeature *poOGRFeature = new OGRFeature(poFeatureDefn);
410 :
411 669 : poOGRFeature->SetFID(nFID);
412 669 : if (poDS->ExposeId())
413 : {
414 561 : if (pszGML_FID)
415 525 : poOGRFeature->SetField(iDstField, pszGML_FID);
416 561 : iDstField++;
417 : }
418 :
419 669 : const int nPropertyCount = poFClass->GetPropertyCount();
420 3204 : for (int iField = 0; iField < nPropertyCount; iField++, iDstField++)
421 : {
422 : const GMLProperty *psGMLProperty =
423 2535 : poGMLFeature->GetProperty(iField);
424 2535 : if (psGMLProperty == nullptr || psGMLProperty->nSubProperties == 0)
425 557 : continue;
426 :
427 1978 : if (EQUAL(psGMLProperty->papszSubProperties[0], OGR_GML_NULL))
428 : {
429 1 : poOGRFeature->SetFieldNull(iDstField);
430 1 : continue;
431 : }
432 :
433 1977 : switch (poFClass->GetProperty(iField)->GetType())
434 : {
435 325 : case GMLPT_Real:
436 : {
437 325 : poOGRFeature->SetField(
438 : iDstField,
439 325 : CPLAtof(psGMLProperty->papszSubProperties[0]));
440 : }
441 325 : break;
442 :
443 13 : case GMLPT_IntegerList:
444 : {
445 13 : const int nCount = psGMLProperty->nSubProperties;
446 : int *panIntList =
447 13 : static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
448 :
449 39 : for (int i = 0; i < nCount; i++)
450 26 : panIntList[i] =
451 26 : atoi(psGMLProperty->papszSubProperties[i]);
452 :
453 13 : poOGRFeature->SetField(iDstField, nCount, panIntList);
454 13 : CPLFree(panIntList);
455 : }
456 13 : break;
457 :
458 6 : case GMLPT_Integer64List:
459 : {
460 6 : const int nCount = psGMLProperty->nSubProperties;
461 : GIntBig *panIntList = static_cast<GIntBig *>(
462 6 : CPLMalloc(sizeof(GIntBig) * nCount));
463 :
464 15 : for (int i = 0; i < nCount; i++)
465 18 : panIntList[i] =
466 9 : CPLAtoGIntBig(psGMLProperty->papszSubProperties[i]);
467 :
468 6 : poOGRFeature->SetField(iDstField, nCount, panIntList);
469 6 : CPLFree(panIntList);
470 : }
471 6 : break;
472 :
473 25 : case GMLPT_RealList:
474 : {
475 25 : const int nCount = psGMLProperty->nSubProperties;
476 : double *padfList = static_cast<double *>(
477 25 : CPLMalloc(sizeof(double) * nCount));
478 :
479 59 : for (int i = 0; i < nCount; i++)
480 68 : padfList[i] =
481 34 : CPLAtof(psGMLProperty->papszSubProperties[i]);
482 :
483 25 : poOGRFeature->SetField(iDstField, nCount, padfList);
484 25 : CPLFree(padfList);
485 : }
486 25 : break;
487 :
488 106 : case GMLPT_StringList:
489 : case GMLPT_FeaturePropertyList:
490 : {
491 106 : poOGRFeature->SetField(iDstField,
492 106 : psGMLProperty->papszSubProperties);
493 : }
494 106 : break;
495 :
496 46 : case GMLPT_Boolean:
497 : {
498 46 : if (strcmp(psGMLProperty->papszSubProperties[0], "true") ==
499 7 : 0 ||
500 7 : strcmp(psGMLProperty->papszSubProperties[0], "1") == 0)
501 : {
502 39 : poOGRFeature->SetField(iDstField, 1);
503 : }
504 7 : else if (strcmp(psGMLProperty->papszSubProperties[0],
505 0 : "false") == 0 ||
506 0 : strcmp(psGMLProperty->papszSubProperties[0],
507 : "0") == 0)
508 : {
509 7 : poOGRFeature->SetField(iDstField, 0);
510 : }
511 : else
512 : {
513 0 : poOGRFeature->SetField(
514 0 : iDstField, psGMLProperty->papszSubProperties[0]);
515 : }
516 46 : break;
517 : }
518 :
519 3 : case GMLPT_BooleanList:
520 : {
521 3 : const int nCount = psGMLProperty->nSubProperties;
522 : int *panIntList =
523 3 : static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
524 :
525 9 : for (int i = 0; i < nCount; i++)
526 : {
527 6 : panIntList[i] =
528 6 : (strcmp(psGMLProperty->papszSubProperties[i],
529 9 : "true") == 0 ||
530 3 : strcmp(psGMLProperty->papszSubProperties[i],
531 : "1") == 0);
532 : }
533 :
534 3 : poOGRFeature->SetField(iDstField, nCount, panIntList);
535 3 : CPLFree(panIntList);
536 3 : break;
537 : }
538 :
539 1453 : default:
540 1453 : poOGRFeature->SetField(
541 1453 : iDstField, psGMLProperty->papszSubProperties[0]);
542 1453 : break;
543 : }
544 : }
545 :
546 669 : delete poGMLFeature;
547 669 : poGMLFeature = nullptr;
548 :
549 : // Assign the geometry before the attribute filter because
550 : // the attribute filter may use a special field like OGR_GEOMETRY.
551 669 : if (papoGeometries != nullptr)
552 : {
553 301 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
554 : {
555 207 : poOGRFeature->SetGeomFieldDirectly(i, papoGeometries[i]);
556 : }
557 94 : CPLFree(papoGeometries);
558 94 : papoGeometries = nullptr;
559 : }
560 : else
561 : {
562 575 : poOGRFeature->SetGeometryDirectly(poGeom);
563 : }
564 :
565 : // Assign SRS.
566 1392 : for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
567 : {
568 723 : poGeom = poOGRFeature->GetGeomFieldRef(i);
569 723 : if (poGeom != nullptr)
570 : {
571 : const OGRSpatialReference *poSRS =
572 636 : poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef();
573 636 : if (poSRS != nullptr)
574 132 : poGeom->assignSpatialReference(poSRS);
575 : }
576 : }
577 :
578 : /* --------------------------------------------------------------------
579 : */
580 : /* Test against the attribute query. */
581 : /* --------------------------------------------------------------------
582 : */
583 669 : if (m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poOGRFeature))
584 : {
585 28 : delete poOGRFeature;
586 28 : continue;
587 : }
588 :
589 : // Got the desired feature.
590 641 : return poOGRFeature;
591 655 : }
592 :
593 : return nullptr;
594 : }
595 :
596 : /************************************************************************/
597 : /* GetFeatureCount() */
598 : /************************************************************************/
599 :
600 62 : GIntBig OGRGMLLayer::GetFeatureCount(int bForce)
601 :
602 : {
603 62 : if (poFClass == nullptr)
604 0 : return 0;
605 :
606 62 : if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
607 6 : return OGRLayer::GetFeatureCount(bForce);
608 :
609 : // If the schema is read from a .xsd file, we haven't read
610 : // the feature count, so compute it now.
611 56 : GIntBig nFeatureCount = poFClass->GetFeatureCount();
612 56 : if (nFeatureCount < 0)
613 : {
614 26 : nFeatureCount = OGRLayer::GetFeatureCount(bForce);
615 26 : poFClass->SetFeatureCount(nFeatureCount);
616 : }
617 :
618 56 : return nFeatureCount;
619 : }
620 :
621 : /************************************************************************/
622 : /* GetExtent() */
623 : /************************************************************************/
624 :
625 10 : OGRErr OGRGMLLayer::GetExtent(OGREnvelope *psExtent, int bForce)
626 :
627 : {
628 10 : if (GetGeomType() == wkbNone)
629 0 : return OGRERR_FAILURE;
630 :
631 10 : double dfXMin = 0.0;
632 10 : double dfXMax = 0.0;
633 10 : double dfYMin = 0.0;
634 10 : double dfYMax = 0.0;
635 20 : if (poFClass != nullptr &&
636 10 : poFClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
637 : {
638 8 : psExtent->MinX = dfXMin;
639 8 : psExtent->MaxX = dfXMax;
640 8 : psExtent->MinY = dfYMin;
641 8 : psExtent->MaxY = dfYMax;
642 :
643 8 : return OGRERR_NONE;
644 : }
645 :
646 2 : return OGRLayer::GetExtent(psExtent, bForce);
647 : }
648 :
649 : /************************************************************************/
650 : /* GetExtent() */
651 : /************************************************************************/
652 :
653 505 : static void GMLWriteField(OGRGMLDataSource *poDS, VSILFILE *fp,
654 : bool bWriteSpaceIndentation, const char *pszPrefix,
655 : bool bRemoveAppPrefix, OGRFieldDefn *poFieldDefn,
656 : const char *pszVal)
657 :
658 : {
659 505 : const char *pszFieldName = poFieldDefn->GetNameRef();
660 :
661 505 : while (*pszVal == ' ')
662 0 : pszVal++;
663 :
664 505 : if (bWriteSpaceIndentation)
665 505 : VSIFPrintfL(fp, " ");
666 :
667 505 : if (bRemoveAppPrefix)
668 60 : poDS->PrintLine(fp, "<%s>%s</%s>", pszFieldName, pszVal, pszFieldName);
669 : else
670 445 : poDS->PrintLine(fp, "<%s:%s>%s</%s:%s>", pszPrefix, pszFieldName,
671 : pszVal, pszPrefix, pszFieldName);
672 505 : }
673 :
674 : /************************************************************************/
675 : /* ICreateFeature() */
676 : /************************************************************************/
677 :
678 253 : OGRErr OGRGMLLayer::ICreateFeature(OGRFeature *poFeature)
679 :
680 : {
681 253 : const bool bIsGML3Output = poDS->IsGML3Output();
682 253 : VSILFILE *fp = poDS->GetOutputFP();
683 253 : const bool bWriteSpaceIndentation = poDS->WriteSpaceIndentation();
684 253 : const char *pszPrefix = poDS->GetAppPrefix();
685 253 : const bool bRemoveAppPrefix = poDS->RemoveAppPrefix();
686 253 : const bool bGMLFeatureCollection = poDS->GMLFeatureCollection();
687 :
688 253 : if (!bWriter)
689 0 : return OGRERR_FAILURE;
690 :
691 253 : poFeature->FillUnsetWithDefault(TRUE, nullptr);
692 253 : if (!poFeature->Validate(OGR_F_VAL_ALL & ~OGR_F_VAL_GEOM_TYPE &
693 : ~OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT,
694 : TRUE))
695 2 : return OGRERR_FAILURE;
696 :
697 251 : if (bWriteSpaceIndentation)
698 251 : VSIFPrintfL(fp, " ");
699 251 : if (bIsGML3Output && !bGMLFeatureCollection)
700 : {
701 207 : if (bRemoveAppPrefix)
702 10 : poDS->PrintLine(fp, "<featureMember>");
703 : else
704 197 : poDS->PrintLine(fp, "<%s:featureMember>", pszPrefix);
705 : }
706 : else
707 : {
708 44 : poDS->PrintLine(fp, "<gml:featureMember>");
709 : }
710 :
711 251 : if (iNextGMLId == 0)
712 : {
713 109 : bSameSRS = true;
714 114 : for (int iGeomField = 1;
715 114 : iGeomField < poFeatureDefn->GetGeomFieldCount(); iGeomField++)
716 : {
717 5 : OGRGeomFieldDefn *poFieldDefn0 = poFeatureDefn->GetGeomFieldDefn(0);
718 : OGRGeomFieldDefn *poFieldDefn =
719 5 : poFeatureDefn->GetGeomFieldDefn(iGeomField);
720 5 : const OGRSpatialReference *poSRS0 = poFieldDefn0->GetSpatialRef();
721 5 : const OGRSpatialReference *poSRS = poFieldDefn->GetSpatialRef();
722 5 : if (poSRS0 != nullptr && poSRS == nullptr)
723 : {
724 0 : bSameSRS = false;
725 : }
726 5 : else if (poSRS0 == nullptr && poSRS != nullptr)
727 : {
728 0 : bSameSRS = false;
729 : }
730 7 : else if (poSRS0 != nullptr && poSRS != nullptr && poSRS0 != poSRS &&
731 2 : !poSRS0->IsSame(poSRS))
732 : {
733 1 : bSameSRS = false;
734 : }
735 : }
736 : }
737 :
738 251 : if (poFeature->GetFID() == OGRNullFID)
739 238 : poFeature->SetFID(iNextGMLId++);
740 :
741 251 : if (bWriteSpaceIndentation)
742 251 : VSIFPrintfL(fp, " ");
743 251 : VSIFPrintfL(fp, "<");
744 251 : if (!bRemoveAppPrefix)
745 231 : VSIFPrintfL(fp, "%s:", pszPrefix);
746 :
747 251 : int nGMLIdIndex = -1;
748 251 : if (bIsGML3Output)
749 : {
750 217 : nGMLIdIndex = poFeatureDefn->GetFieldIndex("gml_id");
751 217 : if (nGMLIdIndex >= 0 && poFeature->IsFieldSetAndNotNull(nGMLIdIndex))
752 3 : poDS->PrintLine(fp, "%s gml:id=\"%s\">", poFeatureDefn->GetName(),
753 : poFeature->GetFieldAsString(nGMLIdIndex));
754 : else
755 428 : poDS->PrintLine(fp, "%s gml:id=\"%s." CPL_FRMT_GIB "\">",
756 214 : poFeatureDefn->GetName(), poFeatureDefn->GetName(),
757 : poFeature->GetFID());
758 : }
759 : else
760 : {
761 34 : nGMLIdIndex = poFeatureDefn->GetFieldIndex("fid");
762 34 : if (bUseOldFIDFormat)
763 : {
764 0 : poDS->PrintLine(fp, "%s fid=\"F" CPL_FRMT_GIB "\">",
765 0 : poFeatureDefn->GetName(), poFeature->GetFID());
766 : }
767 44 : else if (nGMLIdIndex >= 0 &&
768 10 : poFeature->IsFieldSetAndNotNull(nGMLIdIndex))
769 : {
770 10 : poDS->PrintLine(fp, "%s fid=\"%s\">", poFeatureDefn->GetName(),
771 : poFeature->GetFieldAsString(nGMLIdIndex));
772 : }
773 : else
774 : {
775 48 : poDS->PrintLine(fp, "%s fid=\"%s." CPL_FRMT_GIB "\">",
776 24 : poFeatureDefn->GetName(), poFeatureDefn->GetName(),
777 : poFeature->GetFID());
778 : }
779 : }
780 :
781 491 : for (int iGeomField = 0; iGeomField < poFeatureDefn->GetGeomFieldCount();
782 : iGeomField++)
783 : {
784 : const OGRGeomFieldDefn *poFieldDefn =
785 240 : poFeatureDefn->GetGeomFieldDefn(iGeomField);
786 :
787 : // Write out Geometry - for now it isn't indented properly.
788 : // GML geometries don't like very much the concept of empty geometry.
789 240 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iGeomField);
790 240 : if (poGeom != nullptr && !poGeom->IsEmpty())
791 : {
792 203 : OGREnvelope3D sGeomBounds;
793 :
794 203 : const int nCoordDimension = poGeom->getCoordinateDimension();
795 :
796 203 : poGeom->getEnvelope(&sGeomBounds);
797 203 : if (bSameSRS)
798 201 : poDS->GrowExtents(&sGeomBounds, nCoordDimension);
799 :
800 338 : if (poGeom->getSpatialReference() == nullptr &&
801 135 : poFieldDefn->GetSpatialRef() != nullptr)
802 18 : poGeom->assignSpatialReference(poFieldDefn->GetSpatialRef());
803 :
804 203 : const auto &oCoordPrec = poFieldDefn->GetCoordinatePrecision();
805 :
806 203 : if (bIsGML3Output && poDS->WriteFeatureBoundedBy())
807 : {
808 159 : bool bCoordSwap = false;
809 :
810 : char *pszSRSName =
811 159 : GML_GetSRSName(poGeom->getSpatialReference(),
812 159 : poDS->GetSRSNameFormat(), &bCoordSwap);
813 159 : char szLowerCorner[75] = {};
814 159 : char szUpperCorner[75] = {};
815 :
816 159 : OGRWktOptions coordOpts;
817 :
818 159 : if (oCoordPrec.dfXYResolution !=
819 : OGRGeomCoordinatePrecision::UNKNOWN)
820 : {
821 3 : coordOpts.format = OGRWktFormat::F;
822 3 : coordOpts.xyPrecision =
823 3 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
824 3 : oCoordPrec.dfXYResolution);
825 : }
826 159 : if (oCoordPrec.dfZResolution !=
827 : OGRGeomCoordinatePrecision::UNKNOWN)
828 : {
829 3 : coordOpts.format = OGRWktFormat::F;
830 3 : coordOpts.zPrecision =
831 3 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
832 3 : oCoordPrec.dfZResolution);
833 : }
834 :
835 318 : std::string wkt;
836 159 : if (bCoordSwap)
837 : {
838 24 : wkt = OGRMakeWktCoordinate(
839 : sGeomBounds.MinY, sGeomBounds.MinX, sGeomBounds.MinZ,
840 12 : nCoordDimension, coordOpts);
841 12 : memcpy(szLowerCorner, wkt.data(), wkt.size() + 1);
842 :
843 24 : wkt = OGRMakeWktCoordinate(
844 : sGeomBounds.MaxY, sGeomBounds.MaxX, sGeomBounds.MaxZ,
845 12 : nCoordDimension, coordOpts);
846 12 : memcpy(szUpperCorner, wkt.data(), wkt.size() + 1);
847 : }
848 : else
849 : {
850 294 : wkt = OGRMakeWktCoordinate(
851 : sGeomBounds.MinX, sGeomBounds.MinY, sGeomBounds.MinZ,
852 147 : nCoordDimension, coordOpts);
853 147 : memcpy(szLowerCorner, wkt.data(), wkt.size() + 1);
854 :
855 294 : wkt = OGRMakeWktCoordinate(
856 : sGeomBounds.MaxX, sGeomBounds.MaxY, sGeomBounds.MaxZ,
857 147 : nCoordDimension, coordOpts);
858 147 : memcpy(szUpperCorner, wkt.data(), wkt.size() + 1);
859 : }
860 159 : if (bWriteSpaceIndentation)
861 159 : VSIFPrintfL(fp, " ");
862 159 : poDS->PrintLine(
863 : fp,
864 : "<gml:boundedBy><gml:Envelope%s%s><gml:lowerCorner>%s"
865 : "</gml:lowerCorner><gml:upperCorner>%s</gml:upperCorner>"
866 : "</gml:Envelope></gml:boundedBy>",
867 : (nCoordDimension == 3) ? " srsDimension=\"3\"" : "",
868 : pszSRSName, szLowerCorner, szUpperCorner);
869 159 : CPLFree(pszSRSName);
870 : }
871 :
872 203 : char **papszOptions = nullptr;
873 203 : if (bIsGML3Output)
874 : {
875 169 : papszOptions = CSLAddString(papszOptions, "FORMAT=GML3");
876 169 : if (poDS->GetSRSNameFormat() == SRSNAME_SHORT)
877 : papszOptions =
878 1 : CSLAddString(papszOptions, "SRSNAME_FORMAT=SHORT");
879 168 : else if (poDS->GetSRSNameFormat() == SRSNAME_OGC_URN)
880 : papszOptions =
881 155 : CSLAddString(papszOptions, "SRSNAME_FORMAT=OGC_URN");
882 13 : else if (poDS->GetSRSNameFormat() == SRSNAME_OGC_URL)
883 : papszOptions =
884 13 : CSLAddString(papszOptions, "SRSNAME_FORMAT=OGC_URL");
885 : }
886 203 : const char *pszSRSDimensionLoc = poDS->GetSRSDimensionLoc();
887 203 : if (pszSRSDimensionLoc != nullptr)
888 3 : papszOptions = CSLSetNameValue(papszOptions, "SRSDIMENSION_LOC",
889 : pszSRSDimensionLoc);
890 203 : if (poDS->IsGML32Output())
891 : {
892 103 : if (poFeatureDefn->GetGeomFieldCount() > 1)
893 18 : papszOptions = CSLAddString(
894 : papszOptions, CPLSPrintf("GMLID=%s.%s." CPL_FRMT_GIB,
895 9 : poFeatureDefn->GetName(),
896 : poFieldDefn->GetNameRef(),
897 : poFeature->GetFID()));
898 : else
899 188 : papszOptions = CSLAddString(
900 : papszOptions, CPLSPrintf("GMLID=%s.geom." CPL_FRMT_GIB,
901 94 : poFeatureDefn->GetName(),
902 : poFeature->GetFID()));
903 : }
904 :
905 203 : if (oCoordPrec.dfXYResolution !=
906 : OGRGeomCoordinatePrecision::UNKNOWN)
907 : {
908 3 : papszOptions = CSLAddString(
909 : papszOptions, CPLSPrintf("XY_COORD_RESOLUTION=%g",
910 3 : oCoordPrec.dfXYResolution));
911 : }
912 203 : if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
913 : {
914 3 : papszOptions = CSLAddString(
915 : papszOptions, CPLSPrintf("Z_COORD_RESOLUTION=%g",
916 3 : oCoordPrec.dfZResolution));
917 : }
918 :
919 203 : char *pszGeometry = nullptr;
920 203 : if (!bIsGML3Output && OGR_GT_IsNonLinear(poGeom->getGeometryType()))
921 : {
922 0 : OGRGeometry *poGeomTmp = OGRGeometryFactory::forceTo(
923 0 : poGeom->clone(),
924 0 : OGR_GT_GetLinear(poGeom->getGeometryType()));
925 0 : pszGeometry = poGeomTmp->exportToGML(papszOptions);
926 0 : delete poGeomTmp;
927 : }
928 : else
929 : {
930 203 : if (wkbFlatten(poGeom->getGeometryType()) == wkbTriangle)
931 : {
932 0 : pszGeometry = poGeom->exportToGML(papszOptions);
933 :
934 : const char *pszGMLID =
935 0 : poDS->IsGML32Output()
936 0 : ? CPLSPrintf(
937 : " gml:id=\"%s\"",
938 : CSLFetchNameValue(papszOptions, "GMLID"))
939 0 : : "";
940 0 : char *pszNewGeom = CPLStrdup(
941 : CPLSPrintf("<gml:TriangulatedSurface%s><gml:patches>%s<"
942 : "/gml:patches></gml:TriangulatedSurface>",
943 : pszGMLID, pszGeometry));
944 0 : CPLFree(pszGeometry);
945 0 : pszGeometry = pszNewGeom;
946 : }
947 : else
948 : {
949 203 : pszGeometry = poGeom->exportToGML(papszOptions);
950 : }
951 : }
952 203 : CSLDestroy(papszOptions);
953 203 : if (pszGeometry)
954 : {
955 202 : if (bWriteSpaceIndentation)
956 202 : VSIFPrintfL(fp, " ");
957 202 : if (bRemoveAppPrefix)
958 20 : poDS->PrintLine(fp, "<%s>%s</%s>",
959 : poFieldDefn->GetNameRef(), pszGeometry,
960 : poFieldDefn->GetNameRef());
961 : else
962 182 : poDS->PrintLine(fp, "<%s:%s>%s</%s:%s>", pszPrefix,
963 : poFieldDefn->GetNameRef(), pszGeometry,
964 : pszPrefix, poFieldDefn->GetNameRef());
965 : }
966 : else
967 : {
968 1 : CPLError(CE_Failure, CPLE_AppDefined,
969 : "Export of geometry to GML failed");
970 : }
971 203 : CPLFree(pszGeometry);
972 : }
973 : }
974 :
975 : // Write all "set" fields.
976 872 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
977 : {
978 621 : if (iField == nGMLIdIndex)
979 13 : continue;
980 608 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
981 :
982 608 : if (poFeature->IsFieldNull(iField))
983 : {
984 1 : const char *pszFieldName = poFieldDefn->GetNameRef();
985 :
986 1 : if (bWriteSpaceIndentation)
987 1 : VSIFPrintfL(fp, " ");
988 :
989 1 : if (bRemoveAppPrefix)
990 0 : poDS->PrintLine(fp, "<%s xsi:nil=\"true\"/>", pszFieldName);
991 : else
992 1 : poDS->PrintLine(fp, "<%s:%s xsi:nil=\"true\"/>", pszPrefix,
993 : pszFieldName);
994 : }
995 607 : else if (poFeature->IsFieldSet(iField))
996 : {
997 500 : OGRFieldType eType = poFieldDefn->GetType();
998 500 : if (eType == OFTStringList)
999 : {
1000 1 : char **papszIter = poFeature->GetFieldAsStringList(iField);
1001 3 : while (papszIter != nullptr && *papszIter != nullptr)
1002 : {
1003 2 : char *pszEscaped = OGRGetXML_UTF8_EscapedString(*papszIter);
1004 2 : GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
1005 : bRemoveAppPrefix, poFieldDefn, pszEscaped);
1006 2 : CPLFree(pszEscaped);
1007 :
1008 2 : papszIter++;
1009 : }
1010 : }
1011 499 : else if (eType == OFTIntegerList)
1012 : {
1013 2 : int nCount = 0;
1014 : const int *panVals =
1015 2 : poFeature->GetFieldAsIntegerList(iField, &nCount);
1016 2 : if (poFieldDefn->GetSubType() == OFSTBoolean)
1017 : {
1018 3 : for (int i = 0; i < nCount; i++)
1019 : {
1020 : // 0 and 1 are OK, but the canonical representation is
1021 : // false and true.
1022 2 : GMLWriteField(poDS, fp, bWriteSpaceIndentation,
1023 : pszPrefix, bRemoveAppPrefix, poFieldDefn,
1024 2 : panVals[i] ? "true" : "false");
1025 : }
1026 : }
1027 : else
1028 : {
1029 3 : for (int i = 0; i < nCount; i++)
1030 : {
1031 2 : GMLWriteField(poDS, fp, bWriteSpaceIndentation,
1032 : pszPrefix, bRemoveAppPrefix, poFieldDefn,
1033 2 : CPLSPrintf("%d", panVals[i]));
1034 : }
1035 : }
1036 : }
1037 497 : else if (eType == OFTInteger64List)
1038 : {
1039 2 : int nCount = 0;
1040 : const GIntBig *panVals =
1041 2 : poFeature->GetFieldAsInteger64List(iField, &nCount);
1042 2 : if (poFieldDefn->GetSubType() == OFSTBoolean)
1043 : {
1044 0 : for (int i = 0; i < nCount; i++)
1045 : {
1046 : // 0 and 1 are OK, but the canonical representation is
1047 : // false and true.
1048 0 : GMLWriteField(poDS, fp, bWriteSpaceIndentation,
1049 : pszPrefix, bRemoveAppPrefix, poFieldDefn,
1050 0 : panVals[i] ? "true" : "false");
1051 : }
1052 : }
1053 : else
1054 : {
1055 5 : for (int i = 0; i < nCount; i++)
1056 : {
1057 3 : GMLWriteField(poDS, fp, bWriteSpaceIndentation,
1058 : pszPrefix, bRemoveAppPrefix, poFieldDefn,
1059 3 : CPLSPrintf(CPL_FRMT_GIB, panVals[i]));
1060 : }
1061 : }
1062 : }
1063 495 : else if (eType == OFTRealList)
1064 : {
1065 1 : int nCount = 0;
1066 : const double *padfVals =
1067 1 : poFeature->GetFieldAsDoubleList(iField, &nCount);
1068 3 : for (int i = 0; i < nCount; i++)
1069 : {
1070 2 : char szBuffer[80] = {};
1071 2 : CPLsnprintf(szBuffer, sizeof(szBuffer), "%.15g",
1072 2 : padfVals[i]);
1073 2 : GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
1074 : bRemoveAppPrefix, poFieldDefn, szBuffer);
1075 : }
1076 : }
1077 625 : else if ((eType == OFTInteger || eType == OFTInteger64) &&
1078 131 : poFieldDefn->GetSubType() == OFSTBoolean)
1079 : {
1080 : // 0 and 1 are OK, but the canonical representation is false and
1081 : // true.
1082 2 : GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
1083 : bRemoveAppPrefix, poFieldDefn,
1084 2 : (poFeature->GetFieldAsInteger(iField)) ? "true"
1085 : : "false");
1086 : }
1087 492 : else if (eType == OFTDate)
1088 : {
1089 49 : const OGRField *poField = poFeature->GetRawFieldRef(iField);
1090 : const char *pszXML =
1091 98 : CPLSPrintf("%04d-%02d-%02d", poField->Date.Year,
1092 49 : poField->Date.Month, poField->Date.Day);
1093 49 : GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
1094 : bRemoveAppPrefix, poFieldDefn, pszXML);
1095 : }
1096 443 : else if (eType == OFTDateTime)
1097 : {
1098 : char *pszXML =
1099 49 : OGRGetXMLDateTime(poFeature->GetRawFieldRef(iField));
1100 49 : GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
1101 : bRemoveAppPrefix, poFieldDefn, pszXML);
1102 49 : CPLFree(pszXML);
1103 : }
1104 : else
1105 : {
1106 394 : const char *pszRaw = poFeature->GetFieldAsString(iField);
1107 :
1108 394 : char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
1109 :
1110 394 : GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
1111 : bRemoveAppPrefix, poFieldDefn, pszEscaped);
1112 394 : CPLFree(pszEscaped);
1113 : }
1114 : }
1115 : }
1116 :
1117 251 : if (bWriteSpaceIndentation)
1118 251 : VSIFPrintfL(fp, " ");
1119 251 : if (bRemoveAppPrefix)
1120 20 : poDS->PrintLine(fp, "</%s>", poFeatureDefn->GetName());
1121 : else
1122 231 : poDS->PrintLine(fp, "</%s:%s>", pszPrefix, poFeatureDefn->GetName());
1123 251 : if (bWriteSpaceIndentation)
1124 251 : VSIFPrintfL(fp, " ");
1125 251 : if (bIsGML3Output && !bGMLFeatureCollection)
1126 : {
1127 207 : if (bRemoveAppPrefix)
1128 10 : poDS->PrintLine(fp, "</featureMember>");
1129 : else
1130 197 : poDS->PrintLine(fp, "</%s:featureMember>", pszPrefix);
1131 : }
1132 : else
1133 : {
1134 44 : poDS->PrintLine(fp, "</gml:featureMember>");
1135 : }
1136 :
1137 251 : return OGRERR_NONE;
1138 : }
1139 :
1140 : /************************************************************************/
1141 : /* TestCapability() */
1142 : /************************************************************************/
1143 :
1144 314 : int OGRGMLLayer::TestCapability(const char *pszCap)
1145 :
1146 : {
1147 314 : if (EQUAL(pszCap, OLCSequentialWrite))
1148 19 : return bWriter;
1149 :
1150 295 : else if (EQUAL(pszCap, OLCCreateField))
1151 18 : return bWriter && iNextGMLId == 0;
1152 :
1153 277 : else if (EQUAL(pszCap, OLCCreateGeomField))
1154 4 : return bWriter && iNextGMLId == 0;
1155 :
1156 273 : else if (EQUAL(pszCap, OLCFastGetExtent))
1157 : {
1158 4 : if (poFClass == nullptr)
1159 0 : return FALSE;
1160 :
1161 4 : double dfXMin = 0.0;
1162 4 : double dfXMax = 0.0;
1163 4 : double dfYMin = 0.0;
1164 4 : double dfYMax = 0.0;
1165 :
1166 4 : return poFClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax);
1167 : }
1168 :
1169 269 : else if (EQUAL(pszCap, OLCFastFeatureCount))
1170 : {
1171 1 : if (poFClass == nullptr || m_poFilterGeom != nullptr ||
1172 1 : m_poAttrQuery != nullptr)
1173 0 : return FALSE;
1174 :
1175 1 : return poFClass->GetFeatureCount() != -1;
1176 : }
1177 :
1178 268 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
1179 14 : return TRUE;
1180 :
1181 254 : else if (EQUAL(pszCap, OLCCurveGeometries))
1182 129 : return poDS->IsGML3Output();
1183 :
1184 125 : else if (EQUAL(pszCap, OLCZGeometries))
1185 3 : return TRUE;
1186 :
1187 : else
1188 122 : return FALSE;
1189 : }
1190 :
1191 : /************************************************************************/
1192 : /* CreateField() */
1193 : /************************************************************************/
1194 :
1195 181 : OGRErr OGRGMLLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
1196 :
1197 : {
1198 181 : if (!bWriter || iNextGMLId != 0)
1199 0 : return OGRERR_FAILURE;
1200 :
1201 : /* -------------------------------------------------------------------- */
1202 : /* Enforce XML naming semantics on element name. */
1203 : /* -------------------------------------------------------------------- */
1204 362 : OGRFieldDefn oCleanCopy(poField);
1205 181 : char *pszName = CPLStrdup(poField->GetNameRef());
1206 181 : CPLCleanXMLElementName(pszName);
1207 :
1208 181 : if (strcmp(pszName, poField->GetNameRef()) != 0)
1209 : {
1210 0 : if (!bApproxOK)
1211 : {
1212 0 : CPLFree(pszName);
1213 0 : CPLError(CE_Failure, CPLE_AppDefined,
1214 : "Unable to create field with name '%s', it would not\n"
1215 : "be valid as an XML element name.",
1216 : poField->GetNameRef());
1217 0 : return OGRERR_FAILURE;
1218 : }
1219 :
1220 0 : oCleanCopy.SetName(pszName);
1221 0 : CPLError(CE_Warning, CPLE_AppDefined,
1222 : "Field name '%s' adjusted to '%s' to be a valid\n"
1223 : "XML element name.",
1224 : poField->GetNameRef(), pszName);
1225 : }
1226 :
1227 181 : CPLFree(pszName);
1228 :
1229 181 : poFeatureDefn->AddFieldDefn(&oCleanCopy);
1230 :
1231 181 : return OGRERR_NONE;
1232 : }
1233 :
1234 : /************************************************************************/
1235 : /* CreateGeomField() */
1236 : /************************************************************************/
1237 :
1238 17 : OGRErr OGRGMLLayer::CreateGeomField(const OGRGeomFieldDefn *poField,
1239 : int bApproxOK)
1240 :
1241 : {
1242 17 : if (!bWriter || iNextGMLId != 0)
1243 0 : return OGRERR_FAILURE;
1244 :
1245 : /* -------------------------------------------------------------------- */
1246 : /* Enforce XML naming semantics on element name. */
1247 : /* -------------------------------------------------------------------- */
1248 34 : OGRGeomFieldDefn oCleanCopy(poField);
1249 17 : auto poSRSOri = poField->GetSpatialRef();
1250 17 : if (poSRSOri)
1251 : {
1252 5 : auto poSRS = poSRSOri->Clone();
1253 5 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1254 5 : oCleanCopy.SetSpatialRef(poSRS);
1255 5 : poSRS->Release();
1256 : }
1257 17 : char *pszName = CPLStrdup(poField->GetNameRef());
1258 17 : CPLCleanXMLElementName(pszName);
1259 :
1260 17 : if (strcmp(pszName, poField->GetNameRef()) != 0)
1261 : {
1262 0 : if (!bApproxOK)
1263 : {
1264 0 : CPLFree(pszName);
1265 0 : CPLError(CE_Failure, CPLE_AppDefined,
1266 : "Unable to create field with name '%s', it would not\n"
1267 : "be valid as an XML element name.",
1268 : poField->GetNameRef());
1269 0 : return OGRERR_FAILURE;
1270 : }
1271 :
1272 0 : oCleanCopy.SetName(pszName);
1273 0 : CPLError(CE_Warning, CPLE_AppDefined,
1274 : "Field name '%s' adjusted to '%s' to be a valid\n"
1275 : "XML element name.",
1276 : poField->GetNameRef(), pszName);
1277 : }
1278 :
1279 17 : CPLFree(pszName);
1280 :
1281 17 : poFeatureDefn->AddGeomFieldDefn(&oCleanCopy);
1282 :
1283 17 : return OGRERR_NONE;
1284 : }
1285 :
1286 : /************************************************************************/
1287 : /* GetDataset() */
1288 : /************************************************************************/
1289 :
1290 18 : GDALDataset *OGRGMLLayer::GetDataset()
1291 : {
1292 18 : return poDS;
1293 : }
|