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