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