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