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