/src/gdal/ogr/ogr2gmlgeometry.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GML Translator |
4 | | * Purpose: Code to translate OGRGeometry to GML string representation. |
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 | | * Independent Security Audit 2003/04/17 Andrey Kiselev: |
15 | | * Completed audit of this module. All functions may be used without buffer |
16 | | * overflows and stack corruptions if caller could be trusted. |
17 | | * |
18 | | * Security Audit 2003/03/28 warmerda: |
19 | | * Completed security audit. I believe that this module may be safely used |
20 | | * to generate GML from arbitrary but well formed OGRGeomety objects that |
21 | | * come from a potentially hostile source, but through a trusted OGR importer |
22 | | * without compromising the system. |
23 | | * |
24 | | */ |
25 | | |
26 | | #include "cpl_port.h" |
27 | | #include "ogr_api.h" |
28 | | |
29 | | #include <cstddef> |
30 | | #include <cstdio> |
31 | | #include <cstdlib> |
32 | | #include <cstring> |
33 | | #include <algorithm> |
34 | | |
35 | | #include "cpl_conv.h" |
36 | | #include "cpl_error.h" |
37 | | #include "cpl_minixml.h" |
38 | | #include "cpl_string.h" |
39 | | #include "ogr_core.h" |
40 | | #include "ogr_geometry.h" |
41 | | #include "ogr_p.h" |
42 | | #include "ogr_spatialref.h" |
43 | | |
44 | | constexpr int SRSDIM_LOC_GEOMETRY = 1 << 0; |
45 | | constexpr int SRSDIM_LOC_POSLIST = 1 << 1; |
46 | | |
47 | | enum GMLSRSNameFormat |
48 | | { |
49 | | SRSNAME_SHORT, |
50 | | SRSNAME_OGC_URN, |
51 | | SRSNAME_OGC_URL |
52 | | }; |
53 | | |
54 | | /************************************************************************/ |
55 | | /* MakeGMLCoordinate() */ |
56 | | /************************************************************************/ |
57 | | |
58 | | static void MakeGMLCoordinate(char *pszTarget, double x, double y, double z, |
59 | | bool b3D, const OGRWktOptions &coordOpts) |
60 | | |
61 | 0 | { |
62 | 0 | std::string wkt = OGRMakeWktCoordinate(x, y, z, b3D ? 3 : 2, coordOpts); |
63 | 0 | memcpy(pszTarget, wkt.data(), wkt.size() + 1); |
64 | |
|
65 | 0 | while (*pszTarget != '\0') |
66 | 0 | { |
67 | 0 | if (*pszTarget == ' ') |
68 | 0 | *pszTarget = ','; |
69 | 0 | pszTarget++; |
70 | 0 | } |
71 | 0 | } |
72 | | |
73 | | /************************************************************************/ |
74 | | /* _GrowBuffer() */ |
75 | | /************************************************************************/ |
76 | | |
77 | | static void _GrowBuffer(size_t nNeeded, char **ppszText, size_t *pnMaxLength) |
78 | | |
79 | 0 | { |
80 | 0 | if (nNeeded + 1 >= *pnMaxLength) |
81 | 0 | { |
82 | 0 | *pnMaxLength = std::max(*pnMaxLength * 2, nNeeded + 1); |
83 | 0 | *ppszText = static_cast<char *>(CPLRealloc(*ppszText, *pnMaxLength)); |
84 | 0 | } |
85 | 0 | } |
86 | | |
87 | | /************************************************************************/ |
88 | | /* AppendString() */ |
89 | | /************************************************************************/ |
90 | | |
91 | | static void AppendString(char **ppszText, size_t *pnLength, size_t *pnMaxLength, |
92 | | const char *pszTextToAppend) |
93 | | |
94 | 0 | { |
95 | 0 | _GrowBuffer(*pnLength + strlen(pszTextToAppend) + 1, ppszText, pnMaxLength); |
96 | |
|
97 | 0 | strcat(*ppszText + *pnLength, pszTextToAppend); |
98 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
99 | 0 | } |
100 | | |
101 | | /************************************************************************/ |
102 | | /* AppendCoordinateList() */ |
103 | | /************************************************************************/ |
104 | | |
105 | | static void AppendCoordinateList(const OGRLineString *poLine, char **ppszText, |
106 | | size_t *pnLength, size_t *pnMaxLength, |
107 | | const OGRWktOptions &coordOpts) |
108 | | |
109 | 0 | { |
110 | 0 | const bool b3D = wkbHasZ(poLine->getGeometryType()) != FALSE; |
111 | |
|
112 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
113 | 0 | _GrowBuffer(*pnLength + 20, ppszText, pnMaxLength); |
114 | |
|
115 | 0 | strcat(*ppszText + *pnLength, "<gml:coordinates>"); |
116 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
117 | |
|
118 | 0 | char szCoordinate[256] = {}; |
119 | 0 | for (int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++) |
120 | 0 | { |
121 | 0 | MakeGMLCoordinate(szCoordinate, poLine->getX(iPoint), |
122 | 0 | poLine->getY(iPoint), poLine->getZ(iPoint), b3D, |
123 | 0 | coordOpts); |
124 | 0 | _GrowBuffer(*pnLength + strlen(szCoordinate) + 1, ppszText, |
125 | 0 | pnMaxLength); |
126 | |
|
127 | 0 | if (iPoint != 0) |
128 | 0 | strcat(*ppszText + *pnLength, " "); |
129 | |
|
130 | 0 | strcat(*ppszText + *pnLength, szCoordinate); |
131 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
132 | 0 | } |
133 | |
|
134 | 0 | _GrowBuffer(*pnLength + 20, ppszText, pnMaxLength); |
135 | 0 | strcat(*ppszText + *pnLength, "</gml:coordinates>"); |
136 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
137 | 0 | } |
138 | | |
139 | | /************************************************************************/ |
140 | | /* OGR2GMLGeometryAppend() */ |
141 | | /************************************************************************/ |
142 | | |
143 | | static bool OGR2GMLGeometryAppend(const OGRGeometry *poGeometry, |
144 | | char **ppszText, size_t *pnLength, |
145 | | size_t *pnMaxLength, bool bIsSubGeometry, |
146 | | const char *pszNamespaceDecl, |
147 | | const OGRWktOptions &coordOpts) |
148 | | |
149 | 0 | { |
150 | | /* -------------------------------------------------------------------- */ |
151 | | /* Check for Spatial Reference System attached to given geometry */ |
152 | | /* -------------------------------------------------------------------- */ |
153 | | |
154 | | // Buffer for xmlns:gml and srsName attributes (srsName="...") |
155 | 0 | char szAttributes[128] = {}; |
156 | 0 | size_t nAttrsLength = 0; |
157 | |
|
158 | 0 | szAttributes[0] = 0; |
159 | |
|
160 | 0 | const OGRSpatialReference *poSRS = poGeometry->getSpatialReference(); |
161 | |
|
162 | 0 | if (pszNamespaceDecl != nullptr) |
163 | 0 | { |
164 | 0 | snprintf(szAttributes + nAttrsLength, |
165 | 0 | sizeof(szAttributes) - nAttrsLength, " xmlns:gml=\"%s\"", |
166 | 0 | pszNamespaceDecl); |
167 | 0 | nAttrsLength += strlen(szAttributes + nAttrsLength); |
168 | 0 | pszNamespaceDecl = nullptr; |
169 | 0 | } |
170 | |
|
171 | 0 | if (nullptr != poSRS && !bIsSubGeometry) |
172 | 0 | { |
173 | 0 | const char *pszTarget = poSRS->IsProjected() ? "PROJCS" : "GEOGCS"; |
174 | 0 | const char *pszAuthName = poSRS->GetAuthorityName(pszTarget); |
175 | 0 | const char *pszAuthCode = poSRS->GetAuthorityCode(pszTarget); |
176 | 0 | if (nullptr != pszAuthName && strlen(pszAuthName) < 10 && |
177 | 0 | nullptr != pszAuthCode && strlen(pszAuthCode) < 10) |
178 | 0 | { |
179 | 0 | snprintf(szAttributes + nAttrsLength, |
180 | 0 | sizeof(szAttributes) - nAttrsLength, " srsName=\"%s:%s\"", |
181 | 0 | pszAuthName, pszAuthCode); |
182 | |
|
183 | 0 | nAttrsLength += strlen(szAttributes + nAttrsLength); |
184 | 0 | } |
185 | 0 | } |
186 | |
|
187 | 0 | OGRwkbGeometryType eType = poGeometry->getGeometryType(); |
188 | 0 | OGRwkbGeometryType eFType = wkbFlatten(eType); |
189 | | |
190 | | /* -------------------------------------------------------------------- */ |
191 | | /* 2D Point */ |
192 | | /* -------------------------------------------------------------------- */ |
193 | 0 | if (eType == wkbPoint) |
194 | 0 | { |
195 | 0 | const auto poPoint = poGeometry->toPoint(); |
196 | |
|
197 | 0 | char szCoordinate[256] = {}; |
198 | 0 | MakeGMLCoordinate(szCoordinate, poPoint->getX(), poPoint->getY(), 0.0, |
199 | 0 | false, coordOpts); |
200 | |
|
201 | 0 | _GrowBuffer(*pnLength + strlen(szCoordinate) + 60 + nAttrsLength, |
202 | 0 | ppszText, pnMaxLength); |
203 | |
|
204 | 0 | snprintf( |
205 | 0 | *ppszText + *pnLength, *pnMaxLength - *pnLength, |
206 | 0 | "<gml:Point%s><gml:coordinates>%s</gml:coordinates></gml:Point>", |
207 | 0 | szAttributes, szCoordinate); |
208 | |
|
209 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
210 | 0 | } |
211 | | /* -------------------------------------------------------------------- */ |
212 | | /* 3D Point */ |
213 | | /* -------------------------------------------------------------------- */ |
214 | 0 | else if (eType == wkbPoint25D) |
215 | 0 | { |
216 | 0 | const auto poPoint = poGeometry->toPoint(); |
217 | |
|
218 | 0 | char szCoordinate[256] = {}; |
219 | 0 | MakeGMLCoordinate(szCoordinate, poPoint->getX(), poPoint->getY(), |
220 | 0 | poPoint->getZ(), true, coordOpts); |
221 | |
|
222 | 0 | _GrowBuffer(*pnLength + strlen(szCoordinate) + 70 + nAttrsLength, |
223 | 0 | ppszText, pnMaxLength); |
224 | |
|
225 | 0 | snprintf( |
226 | 0 | *ppszText + *pnLength, *pnMaxLength - *pnLength, |
227 | 0 | "<gml:Point%s><gml:coordinates>%s</gml:coordinates></gml:Point>", |
228 | 0 | szAttributes, szCoordinate); |
229 | |
|
230 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
231 | 0 | } |
232 | | |
233 | | /* -------------------------------------------------------------------- */ |
234 | | /* LineString and LinearRing */ |
235 | | /* -------------------------------------------------------------------- */ |
236 | 0 | else if (eFType == wkbLineString) |
237 | 0 | { |
238 | 0 | bool bRing = EQUAL(poGeometry->getGeometryName(), "LINEARRING"); |
239 | | |
240 | | // Buffer for tag name + srsName attribute if set |
241 | 0 | const size_t nLineTagLength = 16; |
242 | 0 | const size_t nLineTagNameBufLen = nLineTagLength + nAttrsLength + 1; |
243 | 0 | char *pszLineTagName = |
244 | 0 | static_cast<char *>(CPLMalloc(nLineTagNameBufLen)); |
245 | |
|
246 | 0 | if (bRing) |
247 | 0 | { |
248 | 0 | snprintf(pszLineTagName, nLineTagNameBufLen, "<gml:LinearRing%s>", |
249 | 0 | szAttributes); |
250 | |
|
251 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszLineTagName); |
252 | 0 | } |
253 | 0 | else |
254 | 0 | { |
255 | 0 | snprintf(pszLineTagName, nLineTagNameBufLen, "<gml:LineString%s>", |
256 | 0 | szAttributes); |
257 | |
|
258 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszLineTagName); |
259 | 0 | } |
260 | | |
261 | | // Free tag buffer. |
262 | 0 | CPLFree(pszLineTagName); |
263 | |
|
264 | 0 | const auto poLineString = poGeometry->toLineString(); |
265 | 0 | AppendCoordinateList(poLineString, ppszText, pnLength, pnMaxLength, |
266 | 0 | coordOpts); |
267 | |
|
268 | 0 | if (bRing) |
269 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:LinearRing>"); |
270 | 0 | else |
271 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:LineString>"); |
272 | 0 | } |
273 | | |
274 | | /* -------------------------------------------------------------------- */ |
275 | | /* Polygon */ |
276 | | /* -------------------------------------------------------------------- */ |
277 | 0 | else if (eFType == wkbPolygon) |
278 | 0 | { |
279 | 0 | const auto poPolygon = poGeometry->toPolygon(); |
280 | | |
281 | | // Buffer for polygon tag name + srsName attribute if set. |
282 | 0 | const size_t nPolyTagLength = 13; |
283 | 0 | const size_t nPolyTagNameBufLen = nPolyTagLength + nAttrsLength + 1; |
284 | 0 | char *pszPolyTagName = |
285 | 0 | static_cast<char *>(CPLMalloc(nPolyTagNameBufLen)); |
286 | | |
287 | | // Compose Polygon tag with or without srsName attribute. |
288 | 0 | snprintf(pszPolyTagName, nPolyTagNameBufLen, "<gml:Polygon%s>", |
289 | 0 | szAttributes); |
290 | |
|
291 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszPolyTagName); |
292 | | |
293 | | // Free tag buffer. |
294 | 0 | CPLFree(pszPolyTagName); |
295 | | |
296 | | // Don't add srsName to polygon rings. |
297 | 0 | if (poPolygon->getExteriorRing() != nullptr) |
298 | 0 | { |
299 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
300 | 0 | "<gml:outerBoundaryIs>"); |
301 | |
|
302 | 0 | CPL_IGNORE_RET_VAL(OGR2GMLGeometryAppend( |
303 | 0 | poPolygon->getExteriorRing(), ppszText, pnLength, pnMaxLength, |
304 | 0 | true, nullptr, coordOpts)); |
305 | |
|
306 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
307 | 0 | "</gml:outerBoundaryIs>"); |
308 | 0 | } |
309 | |
|
310 | 0 | for (int iRing = 0; iRing < poPolygon->getNumInteriorRings(); iRing++) |
311 | 0 | { |
312 | 0 | const OGRLinearRing *poRing = poPolygon->getInteriorRing(iRing); |
313 | |
|
314 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
315 | 0 | "<gml:innerBoundaryIs>"); |
316 | |
|
317 | 0 | CPL_IGNORE_RET_VAL(OGR2GMLGeometryAppend(poRing, ppszText, pnLength, |
318 | 0 | pnMaxLength, true, nullptr, |
319 | 0 | coordOpts)); |
320 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
321 | 0 | "</gml:innerBoundaryIs>"); |
322 | 0 | } |
323 | |
|
324 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:Polygon>"); |
325 | 0 | } |
326 | | |
327 | | /* -------------------------------------------------------------------- */ |
328 | | /* MultiPolygon, MultiLineString, MultiPoint, MultiGeometry */ |
329 | | /* -------------------------------------------------------------------- */ |
330 | 0 | else if (eFType == wkbMultiPolygon || eFType == wkbMultiLineString || |
331 | 0 | eFType == wkbMultiPoint || eFType == wkbGeometryCollection) |
332 | 0 | { |
333 | 0 | const auto poGC = poGeometry->toGeometryCollection(); |
334 | 0 | const char *pszElemClose = nullptr; |
335 | 0 | const char *pszMemberElem = nullptr; |
336 | | |
337 | | // Buffer for opening tag + srsName attribute. |
338 | 0 | char *pszElemOpen = nullptr; |
339 | |
|
340 | 0 | if (eFType == wkbMultiPolygon) |
341 | 0 | { |
342 | 0 | const size_t nBufLen = 13 + nAttrsLength + 1; |
343 | 0 | pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen)); |
344 | 0 | snprintf(pszElemOpen, nBufLen, "MultiPolygon%s>", szAttributes); |
345 | |
|
346 | 0 | pszElemClose = "MultiPolygon>"; |
347 | 0 | pszMemberElem = "polygonMember>"; |
348 | 0 | } |
349 | 0 | else if (eFType == wkbMultiLineString) |
350 | 0 | { |
351 | 0 | const size_t nBufLen = 16 + nAttrsLength + 1; |
352 | 0 | pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen)); |
353 | 0 | snprintf(pszElemOpen, nBufLen, "MultiLineString%s>", szAttributes); |
354 | |
|
355 | 0 | pszElemClose = "MultiLineString>"; |
356 | 0 | pszMemberElem = "lineStringMember>"; |
357 | 0 | } |
358 | 0 | else if (eFType == wkbMultiPoint) |
359 | 0 | { |
360 | 0 | const size_t nBufLen = 11 + nAttrsLength + 1; |
361 | 0 | pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen)); |
362 | 0 | snprintf(pszElemOpen, nBufLen, "MultiPoint%s>", szAttributes); |
363 | |
|
364 | 0 | pszElemClose = "MultiPoint>"; |
365 | 0 | pszMemberElem = "pointMember>"; |
366 | 0 | } |
367 | 0 | else |
368 | 0 | { |
369 | 0 | const size_t nBufLen = 19 + nAttrsLength + 1; |
370 | 0 | pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen)); |
371 | 0 | snprintf(pszElemOpen, nBufLen, "MultiGeometry%s>", szAttributes); |
372 | |
|
373 | 0 | pszElemClose = "MultiGeometry>"; |
374 | 0 | pszMemberElem = "geometryMember>"; |
375 | 0 | } |
376 | |
|
377 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:"); |
378 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszElemOpen); |
379 | |
|
380 | 0 | for (int iMember = 0; iMember < poGC->getNumGeometries(); iMember++) |
381 | 0 | { |
382 | 0 | const auto poMember = poGC->getGeometryRef(iMember); |
383 | |
|
384 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:"); |
385 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszMemberElem); |
386 | |
|
387 | 0 | if (!OGR2GMLGeometryAppend(poMember, ppszText, pnLength, |
388 | 0 | pnMaxLength, true, nullptr, coordOpts)) |
389 | 0 | { |
390 | 0 | CPLFree(pszElemOpen); |
391 | 0 | return false; |
392 | 0 | } |
393 | | |
394 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:"); |
395 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszMemberElem); |
396 | 0 | } |
397 | | |
398 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:"); |
399 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszElemClose); |
400 | | |
401 | | // Free tag buffer. |
402 | 0 | CPLFree(pszElemOpen); |
403 | 0 | } |
404 | 0 | else |
405 | 0 | { |
406 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Unsupported geometry type %s", |
407 | 0 | OGRGeometryTypeToName(eType)); |
408 | 0 | return false; |
409 | 0 | } |
410 | | |
411 | 0 | return true; |
412 | 0 | } |
413 | | |
414 | | /************************************************************************/ |
415 | | /* OGR_G_ExportEnvelopeToGMLTree() */ |
416 | | /************************************************************************/ |
417 | | |
418 | | /** Export the envelope of a geometry as a gml:Box. */ |
419 | | CPLXMLNode *OGR_G_ExportEnvelopeToGMLTree(OGRGeometryH hGeometry) |
420 | | |
421 | 0 | { |
422 | 0 | OGREnvelope sEnvelope; |
423 | |
|
424 | 0 | OGRGeometry::FromHandle(hGeometry)->getEnvelope(&sEnvelope); |
425 | |
|
426 | 0 | if (!sEnvelope.IsInit()) |
427 | 0 | { |
428 | | // TODO: There is apparently a special way of representing a null box |
429 | | // geometry. Should use it here eventually. |
430 | 0 | return nullptr; |
431 | 0 | } |
432 | | |
433 | 0 | CPLXMLNode *psBox = CPLCreateXMLNode(nullptr, CXT_Element, "gml:Box"); |
434 | | |
435 | | /* -------------------------------------------------------------------- */ |
436 | | /* Add minxy coordinate. */ |
437 | | /* -------------------------------------------------------------------- */ |
438 | 0 | CPLXMLNode *psCoord = CPLCreateXMLNode(psBox, CXT_Element, "gml:coord"); |
439 | |
|
440 | 0 | OGRWktOptions coordOpts; |
441 | |
|
442 | 0 | char szCoordinate[256] = {}; |
443 | 0 | MakeGMLCoordinate(szCoordinate, sEnvelope.MinX, sEnvelope.MinY, 0.0, false, |
444 | 0 | coordOpts); |
445 | 0 | char *pszY = strstr(szCoordinate, ","); |
446 | | // There must be more after the comma or we have an internal consistency |
447 | | // bug in MakeGMLCoordinate. |
448 | 0 | if (pszY == nullptr || strlen(pszY) < 2) |
449 | 0 | { |
450 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, "MakeGMLCoordinate failed."); |
451 | 0 | return nullptr; |
452 | 0 | } |
453 | 0 | *pszY = '\0'; |
454 | 0 | pszY++; |
455 | |
|
456 | 0 | CPLCreateXMLElementAndValue(psCoord, "gml:X", szCoordinate); |
457 | 0 | CPLCreateXMLElementAndValue(psCoord, "gml:Y", pszY); |
458 | | |
459 | | /* -------------------------------------------------------------------- */ |
460 | | /* Add maxxy coordinate. */ |
461 | | /* -------------------------------------------------------------------- */ |
462 | 0 | psCoord = CPLCreateXMLNode(psBox, CXT_Element, "gml:coord"); |
463 | |
|
464 | 0 | MakeGMLCoordinate(szCoordinate, sEnvelope.MaxX, sEnvelope.MaxY, 0.0, false, |
465 | 0 | coordOpts); |
466 | 0 | pszY = strstr(szCoordinate, ",") + 1; |
467 | 0 | pszY[-1] = '\0'; |
468 | |
|
469 | 0 | CPLCreateXMLElementAndValue(psCoord, "gml:X", szCoordinate); |
470 | 0 | CPLCreateXMLElementAndValue(psCoord, "gml:Y", pszY); |
471 | |
|
472 | 0 | return psBox; |
473 | 0 | } |
474 | | |
475 | | /************************************************************************/ |
476 | | /* AppendGML3CoordinateList() */ |
477 | | /************************************************************************/ |
478 | | |
479 | | static void AppendGML3CoordinateList(const OGRSimpleCurve *poLine, |
480 | | bool bCoordSwap, char **ppszText, |
481 | | size_t *pnLength, size_t *pnMaxLength, |
482 | | int nSRSDimensionLocFlags, |
483 | | const OGRWktOptions &coordOpts) |
484 | | |
485 | 0 | { |
486 | 0 | bool b3D = wkbHasZ(poLine->getGeometryType()); |
487 | |
|
488 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
489 | 0 | _GrowBuffer(*pnLength + 40, ppszText, pnMaxLength); |
490 | |
|
491 | 0 | if (b3D && (nSRSDimensionLocFlags & SRSDIM_LOC_POSLIST) != 0) |
492 | 0 | strcat(*ppszText + *pnLength, "<gml:posList srsDimension=\"3\">"); |
493 | 0 | else |
494 | 0 | strcat(*ppszText + *pnLength, "<gml:posList>"); |
495 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
496 | |
|
497 | 0 | char szCoordinate[256] = {}; |
498 | |
|
499 | 0 | for (int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++) |
500 | 0 | { |
501 | 0 | if (bCoordSwap) |
502 | 0 | { |
503 | 0 | const std::string wkt = OGRMakeWktCoordinate( |
504 | 0 | poLine->getY(iPoint), poLine->getX(iPoint), |
505 | 0 | poLine->getZ(iPoint), b3D ? 3 : 2, coordOpts); |
506 | 0 | memcpy(szCoordinate, wkt.data(), wkt.size() + 1); |
507 | 0 | } |
508 | 0 | else |
509 | 0 | { |
510 | 0 | const std::string wkt = OGRMakeWktCoordinate( |
511 | 0 | poLine->getX(iPoint), poLine->getY(iPoint), |
512 | 0 | poLine->getZ(iPoint), b3D ? 3 : 2, coordOpts); |
513 | 0 | memcpy(szCoordinate, wkt.data(), wkt.size() + 1); |
514 | 0 | } |
515 | 0 | _GrowBuffer(*pnLength + strlen(szCoordinate) + 1, ppszText, |
516 | 0 | pnMaxLength); |
517 | |
|
518 | 0 | if (iPoint != 0) |
519 | 0 | strcat(*ppszText + *pnLength, " "); |
520 | |
|
521 | 0 | strcat(*ppszText + *pnLength, szCoordinate); |
522 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
523 | 0 | } |
524 | |
|
525 | 0 | _GrowBuffer(*pnLength + 20, ppszText, pnMaxLength); |
526 | 0 | strcat(*ppszText + *pnLength, "</gml:posList>"); |
527 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
528 | 0 | } |
529 | | |
530 | | /************************************************************************/ |
531 | | /* OGR2GML3GeometryAppend() */ |
532 | | /************************************************************************/ |
533 | | |
534 | | static bool OGR2GML3GeometryAppend( |
535 | | const OGRGeometry *poGeometry, const OGRSpatialReference *poParentSRS, |
536 | | char **ppszText, size_t *pnLength, size_t *pnMaxLength, bool bIsSubGeometry, |
537 | | GMLSRSNameFormat eSRSNameFormat, bool bCoordSwap, bool bLineStringAsCurve, |
538 | | const char *pszGMLId, int nSRSDimensionLocFlags, |
539 | | bool bForceLineStringAsLinearRing, const char *pszNamespaceDecl, |
540 | | const char *pszOverriddenElementName, const OGRWktOptions &coordOpts) |
541 | | |
542 | 0 | { |
543 | | |
544 | | /* -------------------------------------------------------------------- */ |
545 | | /* Check for Spatial Reference System attached to given geometry */ |
546 | | /* -------------------------------------------------------------------- */ |
547 | | |
548 | | // Buffer for srsName, xmlns:gml, srsDimension and gml:id attributes |
549 | | // (srsName="..." gml:id="..."). |
550 | |
|
551 | 0 | const OGRSpatialReference *poSRS = |
552 | 0 | poParentSRS ? poParentSRS : poGeometry->getSpatialReference(); |
553 | |
|
554 | 0 | char szAttributes[256] = {}; |
555 | 0 | size_t nAttrsLength = 0; |
556 | |
|
557 | 0 | if (pszNamespaceDecl != nullptr) |
558 | 0 | { |
559 | 0 | snprintf(szAttributes + nAttrsLength, |
560 | 0 | sizeof(szAttributes) - nAttrsLength, " xmlns:gml=\"%s\"", |
561 | 0 | pszNamespaceDecl); |
562 | 0 | pszNamespaceDecl = nullptr; |
563 | 0 | nAttrsLength += strlen(szAttributes + nAttrsLength); |
564 | 0 | } |
565 | |
|
566 | 0 | if (nullptr != poSRS) |
567 | 0 | { |
568 | 0 | const char *pszTarget = poSRS->IsProjected() ? "PROJCS" : "GEOGCS"; |
569 | 0 | const char *pszAuthName = poSRS->GetAuthorityName(pszTarget); |
570 | 0 | const char *pszAuthCode = poSRS->GetAuthorityCode(pszTarget); |
571 | 0 | if (nullptr != pszAuthName && strlen(pszAuthName) < 10 && |
572 | 0 | nullptr != pszAuthCode && strlen(pszAuthCode) < 10) |
573 | 0 | { |
574 | 0 | if (!bIsSubGeometry) |
575 | 0 | { |
576 | 0 | if (eSRSNameFormat == SRSNAME_OGC_URN) |
577 | 0 | { |
578 | 0 | snprintf(szAttributes + nAttrsLength, |
579 | 0 | sizeof(szAttributes) - nAttrsLength, |
580 | 0 | " srsName=\"urn:ogc:def:crs:%s::%s\"", pszAuthName, |
581 | 0 | pszAuthCode); |
582 | 0 | } |
583 | 0 | else if (eSRSNameFormat == SRSNAME_SHORT) |
584 | 0 | { |
585 | 0 | snprintf(szAttributes + nAttrsLength, |
586 | 0 | sizeof(szAttributes) - nAttrsLength, |
587 | 0 | " srsName=\"%s:%s\"", pszAuthName, pszAuthCode); |
588 | 0 | } |
589 | 0 | else if (eSRSNameFormat == SRSNAME_OGC_URL) |
590 | 0 | { |
591 | 0 | snprintf( |
592 | 0 | szAttributes + nAttrsLength, |
593 | 0 | sizeof(szAttributes) - nAttrsLength, |
594 | 0 | " srsName=\"http://www.opengis.net/def/crs/%s/0/%s\"", |
595 | 0 | pszAuthName, pszAuthCode); |
596 | 0 | } |
597 | 0 | nAttrsLength += strlen(szAttributes + nAttrsLength); |
598 | 0 | } |
599 | 0 | } |
600 | 0 | } |
601 | |
|
602 | 0 | if ((nSRSDimensionLocFlags & SRSDIM_LOC_GEOMETRY) != 0 && |
603 | 0 | wkbHasZ(poGeometry->getGeometryType())) |
604 | 0 | { |
605 | 0 | snprintf(szAttributes + nAttrsLength, |
606 | 0 | sizeof(szAttributes) - nAttrsLength, " srsDimension=\"3\""); |
607 | 0 | nAttrsLength += strlen(szAttributes + nAttrsLength); |
608 | |
|
609 | 0 | nSRSDimensionLocFlags &= ~SRSDIM_LOC_GEOMETRY; |
610 | 0 | } |
611 | |
|
612 | 0 | if (pszGMLId != nullptr && |
613 | 0 | nAttrsLength + 9 + strlen(pszGMLId) + 1 < sizeof(szAttributes)) |
614 | 0 | { |
615 | 0 | snprintf(szAttributes + nAttrsLength, |
616 | 0 | sizeof(szAttributes) - nAttrsLength, " gml:id=\"%s\"", |
617 | 0 | pszGMLId); |
618 | 0 | nAttrsLength += strlen(szAttributes + nAttrsLength); |
619 | 0 | } |
620 | |
|
621 | 0 | const OGRwkbGeometryType eType = poGeometry->getGeometryType(); |
622 | 0 | const OGRwkbGeometryType eFType = wkbFlatten(eType); |
623 | | |
624 | | /* -------------------------------------------------------------------- */ |
625 | | /* 2D Point */ |
626 | | /* -------------------------------------------------------------------- */ |
627 | 0 | if (eType == wkbPoint) |
628 | 0 | { |
629 | 0 | const auto poPoint = poGeometry->toPoint(); |
630 | |
|
631 | 0 | char szCoordinate[256] = {}; |
632 | 0 | if (bCoordSwap) |
633 | 0 | { |
634 | 0 | const auto wkt = OGRMakeWktCoordinate( |
635 | 0 | poPoint->getY(), poPoint->getX(), 0.0, 2, coordOpts); |
636 | 0 | memcpy(szCoordinate, wkt.data(), wkt.size() + 1); |
637 | 0 | } |
638 | 0 | else |
639 | 0 | { |
640 | 0 | const auto wkt = OGRMakeWktCoordinate( |
641 | 0 | poPoint->getX(), poPoint->getY(), 0.0, 2, coordOpts); |
642 | 0 | memcpy(szCoordinate, wkt.data(), wkt.size() + 1); |
643 | 0 | } |
644 | 0 | _GrowBuffer(*pnLength + strlen(szCoordinate) + 60 + nAttrsLength, |
645 | 0 | ppszText, pnMaxLength); |
646 | |
|
647 | 0 | snprintf(*ppszText + *pnLength, *pnMaxLength - *pnLength, |
648 | 0 | "<gml:Point%s><gml:pos>%s</gml:pos></gml:Point>", szAttributes, |
649 | 0 | szCoordinate); |
650 | |
|
651 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
652 | 0 | } |
653 | | /* -------------------------------------------------------------------- */ |
654 | | /* 3D Point */ |
655 | | /* -------------------------------------------------------------------- */ |
656 | 0 | else if (eType == wkbPoint25D) |
657 | 0 | { |
658 | 0 | const auto poPoint = poGeometry->toPoint(); |
659 | |
|
660 | 0 | char szCoordinate[256] = {}; |
661 | 0 | if (bCoordSwap) |
662 | 0 | { |
663 | 0 | const auto wkt = |
664 | 0 | OGRMakeWktCoordinate(poPoint->getY(), poPoint->getX(), |
665 | 0 | poPoint->getZ(), 3, coordOpts); |
666 | 0 | memcpy(szCoordinate, wkt.data(), wkt.size() + 1); |
667 | 0 | } |
668 | 0 | else |
669 | 0 | { |
670 | 0 | const auto wkt = |
671 | 0 | OGRMakeWktCoordinate(poPoint->getX(), poPoint->getY(), |
672 | 0 | poPoint->getZ(), 3, coordOpts); |
673 | 0 | memcpy(szCoordinate, wkt.data(), wkt.size() + 1); |
674 | 0 | } |
675 | |
|
676 | 0 | _GrowBuffer(*pnLength + strlen(szCoordinate) + 70 + nAttrsLength, |
677 | 0 | ppszText, pnMaxLength); |
678 | |
|
679 | 0 | snprintf(*ppszText + *pnLength, *pnMaxLength - *pnLength, |
680 | 0 | "<gml:Point%s><gml:pos>%s</gml:pos></gml:Point>", szAttributes, |
681 | 0 | szCoordinate); |
682 | |
|
683 | 0 | *pnLength += strlen(*ppszText + *pnLength); |
684 | 0 | } |
685 | | |
686 | | /* -------------------------------------------------------------------- */ |
687 | | /* LineString and LinearRing */ |
688 | | /* -------------------------------------------------------------------- */ |
689 | 0 | else if (eFType == wkbLineString) |
690 | 0 | { |
691 | 0 | const bool bRing = EQUAL(poGeometry->getGeometryName(), "LINEARRING") || |
692 | 0 | bForceLineStringAsLinearRing; |
693 | 0 | if (!bRing && bLineStringAsCurve) |
694 | 0 | { |
695 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:Curve"); |
696 | 0 | AppendString(ppszText, pnLength, pnMaxLength, szAttributes); |
697 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
698 | 0 | "><gml:segments><gml:LineStringSegment>"); |
699 | 0 | const auto poLineString = poGeometry->toLineString(); |
700 | 0 | AppendGML3CoordinateList(poLineString, bCoordSwap, ppszText, |
701 | 0 | pnLength, pnMaxLength, |
702 | 0 | nSRSDimensionLocFlags, coordOpts); |
703 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
704 | 0 | "</gml:LineStringSegment></gml:segments></gml:Curve>"); |
705 | 0 | } |
706 | 0 | else |
707 | 0 | { |
708 | | // Buffer for tag name + srsName attribute if set. |
709 | 0 | const size_t nLineTagLength = 16; |
710 | 0 | const size_t nLineTagNameBufLen = nLineTagLength + nAttrsLength + 1; |
711 | 0 | char *pszLineTagName = |
712 | 0 | static_cast<char *>(CPLMalloc(nLineTagNameBufLen)); |
713 | |
|
714 | 0 | if (bRing) |
715 | 0 | { |
716 | | // LinearRing isn't supposed to have srsName attribute according |
717 | | // to GML3 SF-0. |
718 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
719 | 0 | "<gml:LinearRing>"); |
720 | 0 | } |
721 | 0 | else |
722 | 0 | { |
723 | 0 | snprintf(pszLineTagName, nLineTagNameBufLen, |
724 | 0 | "<gml:LineString%s>", szAttributes); |
725 | |
|
726 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszLineTagName); |
727 | 0 | } |
728 | | |
729 | | // Free tag buffer. |
730 | 0 | CPLFree(pszLineTagName); |
731 | |
|
732 | 0 | const auto poLineString = poGeometry->toLineString(); |
733 | |
|
734 | 0 | AppendGML3CoordinateList(poLineString, bCoordSwap, ppszText, |
735 | 0 | pnLength, pnMaxLength, |
736 | 0 | nSRSDimensionLocFlags, coordOpts); |
737 | |
|
738 | 0 | if (bRing) |
739 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
740 | 0 | "</gml:LinearRing>"); |
741 | 0 | else |
742 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
743 | 0 | "</gml:LineString>"); |
744 | 0 | } |
745 | 0 | } |
746 | | |
747 | | /* -------------------------------------------------------------------- */ |
748 | | /* ArcString or Circle */ |
749 | | /* -------------------------------------------------------------------- */ |
750 | 0 | else if (eFType == wkbCircularString) |
751 | 0 | { |
752 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:Curve"); |
753 | 0 | AppendString(ppszText, pnLength, pnMaxLength, szAttributes); |
754 | 0 | const auto poSC = poGeometry->toCircularString(); |
755 | | |
756 | | // SQL MM has a unique type for arc and circle, GML does not. |
757 | 0 | if (poSC->getNumPoints() == 3 && poSC->getX(0) == poSC->getX(2) && |
758 | 0 | poSC->getY(0) == poSC->getY(2)) |
759 | 0 | { |
760 | 0 | const double dfMidX = (poSC->getX(0) + poSC->getX(1)) / 2.0; |
761 | 0 | const double dfMidY = (poSC->getY(0) + poSC->getY(1)) / 2.0; |
762 | 0 | const double dfDirX = (poSC->getX(1) - poSC->getX(0)) / 2.0; |
763 | 0 | const double dfDirY = (poSC->getY(1) - poSC->getY(0)) / 2.0; |
764 | 0 | const double dfNormX = -dfDirY; |
765 | 0 | const double dfNormY = dfDirX; |
766 | 0 | const double dfNewX = dfMidX + dfNormX; |
767 | 0 | const double dfNewY = dfMidY + dfNormY; |
768 | 0 | OGRLineString *poLS = new OGRLineString(); |
769 | 0 | OGRPoint p; |
770 | 0 | poSC->getPoint(0, &p); |
771 | 0 | poLS->addPoint(&p); |
772 | 0 | poSC->getPoint(1, &p); |
773 | 0 | if (poSC->getCoordinateDimension() == 3) |
774 | 0 | poLS->addPoint(dfNewX, dfNewY, p.getZ()); |
775 | 0 | else |
776 | 0 | poLS->addPoint(dfNewX, dfNewY); |
777 | 0 | poLS->addPoint(&p); |
778 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
779 | 0 | "><gml:segments><gml:Circle>"); |
780 | 0 | AppendGML3CoordinateList(poLS, bCoordSwap, ppszText, pnLength, |
781 | 0 | pnMaxLength, nSRSDimensionLocFlags, |
782 | 0 | coordOpts); |
783 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
784 | 0 | "</gml:Circle></gml:segments></gml:Curve>"); |
785 | 0 | delete poLS; |
786 | 0 | } |
787 | 0 | else |
788 | 0 | { |
789 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
790 | 0 | "><gml:segments><gml:ArcString>"); |
791 | 0 | AppendGML3CoordinateList(poSC, bCoordSwap, ppszText, pnLength, |
792 | 0 | pnMaxLength, nSRSDimensionLocFlags, |
793 | 0 | coordOpts); |
794 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
795 | 0 | "</gml:ArcString></gml:segments></gml:Curve>"); |
796 | 0 | } |
797 | 0 | } |
798 | | |
799 | | /* -------------------------------------------------------------------- */ |
800 | | /* CompositeCurve */ |
801 | | /* -------------------------------------------------------------------- */ |
802 | 0 | else if (eFType == wkbCompoundCurve) |
803 | 0 | { |
804 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:CompositeCurve"); |
805 | 0 | AppendString(ppszText, pnLength, pnMaxLength, szAttributes); |
806 | 0 | AppendString(ppszText, pnLength, pnMaxLength, ">"); |
807 | |
|
808 | 0 | const auto poCC = poGeometry->toCompoundCurve(); |
809 | 0 | for (int i = 0; i < poCC->getNumCurves(); i++) |
810 | 0 | { |
811 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:curveMember>"); |
812 | |
|
813 | 0 | char *pszGMLIdSub = nullptr; |
814 | 0 | if (pszGMLId != nullptr) |
815 | 0 | pszGMLIdSub = CPLStrdup(CPLSPrintf("%s.%d", pszGMLId, i)); |
816 | |
|
817 | 0 | CPL_IGNORE_RET_VAL(OGR2GML3GeometryAppend( |
818 | 0 | poCC->getCurve(i), poSRS, ppszText, pnLength, pnMaxLength, true, |
819 | 0 | eSRSNameFormat, bCoordSwap, bLineStringAsCurve, pszGMLIdSub, |
820 | 0 | nSRSDimensionLocFlags, false, nullptr, nullptr, coordOpts)); |
821 | |
|
822 | 0 | CPLFree(pszGMLIdSub); |
823 | |
|
824 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:curveMember>"); |
825 | 0 | } |
826 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:CompositeCurve>"); |
827 | 0 | } |
828 | | |
829 | | /* -------------------------------------------------------------------- */ |
830 | | /* Polygon */ |
831 | | /* -------------------------------------------------------------------- */ |
832 | 0 | else if (eFType == wkbPolygon || eFType == wkbCurvePolygon) |
833 | 0 | { |
834 | 0 | const auto poCP = poGeometry->toCurvePolygon(); |
835 | | |
836 | | // Buffer for polygon tag name + srsName attribute if set. |
837 | 0 | const char *pszElemName = |
838 | 0 | pszOverriddenElementName ? pszOverriddenElementName : "Polygon"; |
839 | 0 | const size_t nPolyTagLength = 7 + strlen(pszElemName); |
840 | 0 | const size_t nPolyTagNameBufLen = nPolyTagLength + nAttrsLength + 1; |
841 | 0 | char *pszPolyTagName = |
842 | 0 | static_cast<char *>(CPLMalloc(nPolyTagNameBufLen)); |
843 | | |
844 | | // Compose Polygon tag with or without srsName attribute. |
845 | 0 | snprintf(pszPolyTagName, nPolyTagNameBufLen, "<gml:%s%s>", pszElemName, |
846 | 0 | szAttributes); |
847 | |
|
848 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszPolyTagName); |
849 | | |
850 | | // Free tag buffer. |
851 | 0 | CPLFree(pszPolyTagName); |
852 | |
|
853 | 0 | const auto AppendCompoundCurveMembers = |
854 | 0 | [&](const OGRGeometry *poRing, const char *pszGMLIdRing) |
855 | 0 | { |
856 | 0 | const auto eRingType = wkbFlatten(poRing->getGeometryType()); |
857 | 0 | if (eRingType == wkbCompoundCurve) |
858 | 0 | { |
859 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:Ring>"); |
860 | 0 | const auto poCC = poRing->toCompoundCurve(); |
861 | 0 | const int nNumCurves = poCC->getNumCurves(); |
862 | 0 | for (int i = 0; i < nNumCurves; i++) |
863 | 0 | { |
864 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
865 | 0 | "<gml:curveMember>"); |
866 | |
|
867 | 0 | char *pszGMLIdSub = nullptr; |
868 | 0 | if (pszGMLIdRing != nullptr) |
869 | 0 | pszGMLIdSub = |
870 | 0 | CPLStrdup(CPLSPrintf("%s.%d", pszGMLIdRing, i)); |
871 | |
|
872 | 0 | CPL_IGNORE_RET_VAL(OGR2GML3GeometryAppend( |
873 | 0 | poCC->getCurve(i), poSRS, ppszText, pnLength, |
874 | 0 | pnMaxLength, true, eSRSNameFormat, bCoordSwap, |
875 | 0 | bLineStringAsCurve, pszGMLIdSub, nSRSDimensionLocFlags, |
876 | 0 | false, nullptr, nullptr, coordOpts)); |
877 | |
|
878 | 0 | CPLFree(pszGMLIdSub); |
879 | |
|
880 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
881 | 0 | "</gml:curveMember>"); |
882 | 0 | } |
883 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:Ring>"); |
884 | 0 | } |
885 | 0 | else |
886 | 0 | { |
887 | 0 | if (eRingType != wkbLineString) |
888 | 0 | { |
889 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
890 | 0 | "<gml:Ring><gml:curveMember>"); |
891 | 0 | } |
892 | |
|
893 | 0 | CPL_IGNORE_RET_VAL(OGR2GML3GeometryAppend( |
894 | 0 | poRing, poSRS, ppszText, pnLength, pnMaxLength, true, |
895 | 0 | eSRSNameFormat, bCoordSwap, bLineStringAsCurve, |
896 | 0 | pszGMLIdRing, nSRSDimensionLocFlags, true, nullptr, nullptr, |
897 | 0 | coordOpts)); |
898 | |
|
899 | 0 | if (eRingType != wkbLineString) |
900 | 0 | { |
901 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
902 | 0 | "</gml:curveMember></gml:Ring>"); |
903 | 0 | } |
904 | 0 | } |
905 | 0 | }; |
906 | | |
907 | | // Don't add srsName to polygon rings. |
908 | |
|
909 | 0 | const auto poExteriorRing = poCP->getExteriorRingCurve(); |
910 | 0 | if (poExteriorRing != nullptr) |
911 | 0 | { |
912 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:exterior>"); |
913 | |
|
914 | 0 | AppendCompoundCurveMembers( |
915 | 0 | poExteriorRing, |
916 | 0 | pszGMLId ? (std::string(pszGMLId) + ".exterior").c_str() |
917 | 0 | : nullptr); |
918 | |
|
919 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:exterior>"); |
920 | |
|
921 | 0 | for (int iRing = 0; iRing < poCP->getNumInteriorRings(); iRing++) |
922 | 0 | { |
923 | 0 | const OGRCurve *poRing = poCP->getInteriorRingCurve(iRing); |
924 | |
|
925 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:interior>"); |
926 | |
|
927 | 0 | AppendCompoundCurveMembers( |
928 | 0 | poRing, pszGMLId ? (std::string(pszGMLId) + ".interior." + |
929 | 0 | std::to_string(iRing)) |
930 | 0 | .c_str() |
931 | 0 | : nullptr); |
932 | |
|
933 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
934 | 0 | "</gml:interior>"); |
935 | 0 | } |
936 | 0 | } |
937 | |
|
938 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:"); |
939 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszElemName); |
940 | 0 | AppendString(ppszText, pnLength, pnMaxLength, ">"); |
941 | 0 | } |
942 | | |
943 | | /* -------------------------------------------------------------------- */ |
944 | | /* Triangle */ |
945 | | /* -------------------------------------------------------------------- */ |
946 | 0 | else if (eFType == wkbTriangle) |
947 | 0 | { |
948 | 0 | const auto poTri = poGeometry->toPolygon(); |
949 | |
|
950 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:Triangle>"); |
951 | |
|
952 | 0 | if (poTri->getExteriorRingCurve() != nullptr) |
953 | 0 | { |
954 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:exterior>"); |
955 | |
|
956 | 0 | CPL_IGNORE_RET_VAL(OGR2GML3GeometryAppend( |
957 | 0 | poTri->getExteriorRingCurve(), poSRS, ppszText, pnLength, |
958 | 0 | pnMaxLength, true, eSRSNameFormat, bCoordSwap, |
959 | 0 | bLineStringAsCurve, nullptr, nSRSDimensionLocFlags, true, |
960 | 0 | nullptr, nullptr, coordOpts)); |
961 | |
|
962 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:exterior>"); |
963 | 0 | } |
964 | |
|
965 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:Triangle>"); |
966 | 0 | } |
967 | | |
968 | | /* -------------------------------------------------------------------- */ |
969 | | /* MultiSurface, MultiCurve, MultiPoint, MultiGeometry */ |
970 | | /* -------------------------------------------------------------------- */ |
971 | 0 | else if (eFType == wkbMultiPolygon || eFType == wkbMultiSurface || |
972 | 0 | eFType == wkbMultiLineString || eFType == wkbMultiCurve || |
973 | 0 | eFType == wkbMultiPoint || eFType == wkbGeometryCollection) |
974 | 0 | { |
975 | 0 | const auto poGC = poGeometry->toGeometryCollection(); |
976 | 0 | const char *pszElemClose = nullptr; |
977 | 0 | const char *pszMemberElem = nullptr; |
978 | | |
979 | | // Buffer for opening tag + srsName attribute. |
980 | 0 | char *pszElemOpen = nullptr; |
981 | |
|
982 | 0 | if (eFType == wkbMultiPolygon || eFType == wkbMultiSurface) |
983 | 0 | { |
984 | 0 | const size_t nBufLen = 13 + nAttrsLength + 1; |
985 | 0 | pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen)); |
986 | 0 | snprintf(pszElemOpen, nBufLen, "MultiSurface%s>", szAttributes); |
987 | |
|
988 | 0 | pszElemClose = "MultiSurface>"; |
989 | 0 | pszMemberElem = "surfaceMember>"; |
990 | 0 | } |
991 | 0 | else if (eFType == wkbMultiLineString || eFType == wkbMultiCurve) |
992 | 0 | { |
993 | 0 | const size_t nBufLen = 16 + nAttrsLength + 1; |
994 | 0 | pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen)); |
995 | 0 | snprintf(pszElemOpen, nBufLen, "MultiCurve%s>", szAttributes); |
996 | |
|
997 | 0 | pszElemClose = "MultiCurve>"; |
998 | 0 | pszMemberElem = "curveMember>"; |
999 | 0 | } |
1000 | 0 | else if (eFType == wkbMultiPoint) |
1001 | 0 | { |
1002 | 0 | const size_t nBufLen = 11 + nAttrsLength + 1; |
1003 | 0 | pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen)); |
1004 | 0 | snprintf(pszElemOpen, nBufLen, "MultiPoint%s>", szAttributes); |
1005 | |
|
1006 | 0 | pszElemClose = "MultiPoint>"; |
1007 | 0 | pszMemberElem = "pointMember>"; |
1008 | 0 | } |
1009 | 0 | else |
1010 | 0 | { |
1011 | 0 | const size_t nBufLen = 19 + nAttrsLength + 1; |
1012 | 0 | pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen)); |
1013 | 0 | snprintf(pszElemOpen, nBufLen, "MultiGeometry%s>", szAttributes); |
1014 | |
|
1015 | 0 | pszElemClose = "MultiGeometry>"; |
1016 | 0 | pszMemberElem = "geometryMember>"; |
1017 | 0 | } |
1018 | |
|
1019 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:"); |
1020 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszElemOpen); |
1021 | | |
1022 | | // Free tag buffer. |
1023 | 0 | CPLFree(pszElemOpen); |
1024 | |
|
1025 | 0 | for (int iMember = 0; iMember < poGC->getNumGeometries(); iMember++) |
1026 | 0 | { |
1027 | 0 | const OGRGeometry *poMember = poGC->getGeometryRef(iMember); |
1028 | |
|
1029 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:"); |
1030 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszMemberElem); |
1031 | |
|
1032 | 0 | char *pszGMLIdSub = nullptr; |
1033 | 0 | if (pszGMLId != nullptr) |
1034 | 0 | pszGMLIdSub = CPLStrdup(CPLSPrintf("%s.%d", pszGMLId, iMember)); |
1035 | |
|
1036 | 0 | if (!OGR2GML3GeometryAppend( |
1037 | 0 | poMember, poSRS, ppszText, pnLength, pnMaxLength, true, |
1038 | 0 | eSRSNameFormat, bCoordSwap, bLineStringAsCurve, pszGMLIdSub, |
1039 | 0 | nSRSDimensionLocFlags, false, nullptr, nullptr, coordOpts)) |
1040 | 0 | { |
1041 | 0 | CPLFree(pszGMLIdSub); |
1042 | 0 | return false; |
1043 | 0 | } |
1044 | | |
1045 | 0 | CPLFree(pszGMLIdSub); |
1046 | |
|
1047 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:"); |
1048 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszMemberElem); |
1049 | 0 | } |
1050 | | |
1051 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:"); |
1052 | 0 | AppendString(ppszText, pnLength, pnMaxLength, pszElemClose); |
1053 | 0 | } |
1054 | | |
1055 | | /* -------------------------------------------------------------------- */ |
1056 | | /* Polyhedral Surface */ |
1057 | | /* -------------------------------------------------------------------- */ |
1058 | 0 | else if (eFType == wkbPolyhedralSurface) |
1059 | 0 | { |
1060 | | // The patches enclosed in a single <gml:polygonPatches> tag need to be |
1061 | | // co-planar. |
1062 | | // TODO - enforce the condition within this implementation |
1063 | 0 | const auto poPS = poGeometry->toPolyhedralSurface(); |
1064 | |
|
1065 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "<gml:PolyhedralSurface"); |
1066 | 0 | AppendString(ppszText, pnLength, pnMaxLength, szAttributes); |
1067 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "><gml:polygonPatches>"); |
1068 | |
|
1069 | 0 | for (int iMember = 0; iMember < poPS->getNumGeometries(); iMember++) |
1070 | 0 | { |
1071 | 0 | const OGRGeometry *poMember = poPS->getGeometryRef(iMember); |
1072 | 0 | char *pszGMLIdSub = nullptr; |
1073 | 0 | if (pszGMLId != nullptr) |
1074 | 0 | pszGMLIdSub = CPLStrdup(CPLSPrintf("%s.%d", pszGMLId, iMember)); |
1075 | |
|
1076 | 0 | if (!OGR2GML3GeometryAppend(poMember, poSRS, ppszText, pnLength, |
1077 | 0 | pnMaxLength, true, eSRSNameFormat, |
1078 | 0 | bCoordSwap, bLineStringAsCurve, nullptr, |
1079 | 0 | nSRSDimensionLocFlags, false, nullptr, |
1080 | 0 | "PolygonPatch", coordOpts)) |
1081 | 0 | { |
1082 | 0 | CPLFree(pszGMLIdSub); |
1083 | 0 | return false; |
1084 | 0 | } |
1085 | | |
1086 | 0 | CPLFree(pszGMLIdSub); |
1087 | 0 | } |
1088 | | |
1089 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:polygonPatches>"); |
1090 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
1091 | 0 | "</gml:PolyhedralSurface>"); |
1092 | 0 | } |
1093 | | |
1094 | | /* -------------------------------------------------------------------- */ |
1095 | | /* TIN */ |
1096 | | /* -------------------------------------------------------------------- */ |
1097 | 0 | else if (eFType == wkbTIN) |
1098 | 0 | { |
1099 | | // OGR uses the following hierarchy for TriangulatedSurface - |
1100 | | |
1101 | | // <gml:TriangulatedSurface> |
1102 | | // <gml:patches> |
1103 | | // <gml:Triangle> |
1104 | | // <gml:exterior> |
1105 | | // <gml:LinearRing> |
1106 | | // <gml:posList srsDimension=...>...</gml:posList> |
1107 | | // </gml:LinearRing> |
1108 | | // </gml:exterior> |
1109 | | // </gml:Triangle> |
1110 | | // </gml:patches> |
1111 | | // </gml:TriangulatedSurface> |
1112 | | |
1113 | | // <gml:trianglePatches> is deprecated, so write feature is not enabled |
1114 | | // for <gml:trianglePatches> |
1115 | 0 | const auto poTIN = poGeometry->toPolyhedralSurface(); |
1116 | |
|
1117 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
1118 | 0 | "<gml:TriangulatedSurface"); |
1119 | 0 | AppendString(ppszText, pnLength, pnMaxLength, szAttributes); |
1120 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "><gml:patches>"); |
1121 | |
|
1122 | 0 | for (int iMember = 0; iMember < poTIN->getNumGeometries(); iMember++) |
1123 | 0 | { |
1124 | 0 | const OGRGeometry *poMember = poTIN->getGeometryRef(iMember); |
1125 | |
|
1126 | 0 | char *pszGMLIdSub = nullptr; |
1127 | 0 | if (pszGMLId != nullptr) |
1128 | 0 | pszGMLIdSub = CPLStrdup(CPLSPrintf("%s.%d", pszGMLId, iMember)); |
1129 | |
|
1130 | 0 | CPL_IGNORE_RET_VAL(OGR2GML3GeometryAppend( |
1131 | 0 | poMember, poSRS, ppszText, pnLength, pnMaxLength, true, |
1132 | 0 | eSRSNameFormat, bCoordSwap, bLineStringAsCurve, nullptr, |
1133 | 0 | nSRSDimensionLocFlags, false, nullptr, nullptr, coordOpts)); |
1134 | |
|
1135 | 0 | CPLFree(pszGMLIdSub); |
1136 | 0 | } |
1137 | |
|
1138 | 0 | AppendString(ppszText, pnLength, pnMaxLength, "</gml:patches>"); |
1139 | 0 | AppendString(ppszText, pnLength, pnMaxLength, |
1140 | 0 | "</gml:TriangulatedSurface>"); |
1141 | 0 | } |
1142 | | |
1143 | 0 | else |
1144 | 0 | { |
1145 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Unsupported geometry type %s", |
1146 | 0 | OGRGeometryTypeToName(eType)); |
1147 | 0 | return false; |
1148 | 0 | } |
1149 | | |
1150 | 0 | return true; |
1151 | 0 | } |
1152 | | |
1153 | | /************************************************************************/ |
1154 | | /* OGR_G_ExportToGMLTree() */ |
1155 | | /************************************************************************/ |
1156 | | |
1157 | | /** Convert a geometry into GML format. */ |
1158 | | CPLXMLNode *OGR_G_ExportToGMLTree(OGRGeometryH hGeometry) |
1159 | | |
1160 | 0 | { |
1161 | 0 | char *pszText = OGR_G_ExportToGML(hGeometry); |
1162 | 0 | if (pszText == nullptr) |
1163 | 0 | return nullptr; |
1164 | | |
1165 | 0 | CPLXMLNode *psTree = CPLParseXMLString(pszText); |
1166 | |
|
1167 | 0 | CPLFree(pszText); |
1168 | |
|
1169 | 0 | return psTree; |
1170 | 0 | } |
1171 | | |
1172 | | /************************************************************************/ |
1173 | | /* OGR_G_ExportToGML() */ |
1174 | | /************************************************************************/ |
1175 | | |
1176 | | /** |
1177 | | * \brief Convert a geometry into GML format. |
1178 | | * |
1179 | | * The GML geometry is expressed directly in terms of GML basic data |
1180 | | * types assuming the this is available in the gml namespace. The returned |
1181 | | * string should be freed with CPLFree() when no longer required. |
1182 | | * |
1183 | | * This method is the same as the C++ method OGRGeometry::exportToGML(). |
1184 | | * |
1185 | | * @param hGeometry handle to the geometry. |
1186 | | * @return A GML fragment or NULL in case of error. |
1187 | | */ |
1188 | | |
1189 | | char *OGR_G_ExportToGML(OGRGeometryH hGeometry) |
1190 | | |
1191 | 0 | { |
1192 | 0 | return OGR_G_ExportToGMLEx(hGeometry, nullptr); |
1193 | 0 | } |
1194 | | |
1195 | | /************************************************************************/ |
1196 | | /* OGR_G_ExportToGMLEx() */ |
1197 | | /************************************************************************/ |
1198 | | |
1199 | | /** |
1200 | | * \brief Convert a geometry into GML format. |
1201 | | * |
1202 | | * The GML geometry is expressed directly in terms of GML basic data |
1203 | | * types assuming the this is available in the gml namespace. The returned |
1204 | | * string should be freed with CPLFree() when no longer required. |
1205 | | * |
1206 | | * The supported options are : |
1207 | | * <ul> |
1208 | | * <li> FORMAT=GML2/GML3/GML32 (GML2 or GML32 added in GDAL 2.1). |
1209 | | * If not set, it will default to GML 2.1.2 output. |
1210 | | * </li> |
1211 | | * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3) |
1212 | | * To use gml:Curve element for linestrings. |
1213 | | * Otherwise gml:LineString will be used . |
1214 | | * </li> |
1215 | | * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by |
1216 | | * SRSNAME_FORMAT in GDAL >=2.2). Defaults to YES. |
1217 | | * If YES, SRS with EPSG authority will be written with the |
1218 | | * "urn:ogc:def:crs:EPSG::" prefix. |
1219 | | * In the case the SRS should be treated as lat/long or |
1220 | | * northing/easting, then the function will take care of coordinate order |
1221 | | * swapping if the data axis to CRS axis mapping indicates it. |
1222 | | * If set to NO, SRS with EPSG authority will be written with the "EPSG:" |
1223 | | * prefix, even if they are in lat/long order. |
1224 | | * </li> |
1225 | | * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3, added |
1226 | | * in GDAL 2.2). Defaults to OGC_URN. If SHORT, then srsName will be in |
1227 | | * the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be |
1228 | | * in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL, |
1229 | | * then srsName will be in the form |
1230 | | * http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For |
1231 | | * OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long |
1232 | | * or northing/easting, then the function will take care of coordinate |
1233 | | * order swapping if the data axis to CRS axis mapping indicates it. |
1234 | | * </li> |
1235 | | * <li> GMLID=astring. If specified, a gml:id attribute will be written in the |
1236 | | * top-level geometry element with the provided value. |
1237 | | * Required for GML 3.2 compatibility. |
1238 | | * </li> |
1239 | | * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for |
1240 | | * FORMAT=GML3/GML32, GDAL >= 2.0) Default to POSLIST. |
1241 | | * For 2.5D geometries, define the location where to attach the |
1242 | | * srsDimension attribute. |
1243 | | * There are diverging implementations. Some put in on the |
1244 | | * <gml:posList> element, other on the top geometry element. |
1245 | | * </li> |
1246 | | * <li> NAMESPACE_DECL=YES/NO. If set to YES, |
1247 | | * xmlns:gml="http://www.opengis.net/gml" will be added to the root node |
1248 | | * for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2 |
1249 | | * </li> |
1250 | | * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9): |
1251 | | * Resolution for the coordinate precision of the X and Y coordinates. |
1252 | | * Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up |
1253 | | * to 5 decimal digits. 0 for the default behavior. |
1254 | | * </li> |
1255 | | * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9): |
1256 | | * Resolution for the coordinate precision of the Z coordinates. |
1257 | | * Expressed in the units of the Z axis of the SRS. |
1258 | | * 0 for the default behavior. |
1259 | | * </li> |
1260 | | * </ul> |
1261 | | * |
1262 | | * Note that curve geometries like CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, |
1263 | | * MULTICURVE or MULTISURFACE are not supported in GML 2. |
1264 | | * |
1265 | | * This method is the same as the C++ method OGRGeometry::exportToGML(). |
1266 | | * |
1267 | | * @param hGeometry handle to the geometry. |
1268 | | * @param papszOptions NULL-terminated list of options. |
1269 | | * @return A GML fragment or NULL in case of error. |
1270 | | * |
1271 | | * @since OGR 1.8.0 |
1272 | | */ |
1273 | | |
1274 | | char *OGR_G_ExportToGMLEx(OGRGeometryH hGeometry, char **papszOptions) |
1275 | | |
1276 | 0 | { |
1277 | 0 | if (hGeometry == nullptr) |
1278 | 0 | return CPLStrdup(""); |
1279 | | |
1280 | | // Do not use hGeometry after here. |
1281 | 0 | OGRGeometry *poGeometry = OGRGeometry::FromHandle(hGeometry); |
1282 | |
|
1283 | 0 | OGRWktOptions coordOpts; |
1284 | |
|
1285 | 0 | const char *pszXYCoordRes = |
1286 | 0 | CSLFetchNameValue(papszOptions, "XY_COORD_RESOLUTION"); |
1287 | 0 | if (pszXYCoordRes) |
1288 | 0 | { |
1289 | 0 | coordOpts.format = OGRWktFormat::F; |
1290 | 0 | coordOpts.xyPrecision = |
1291 | 0 | OGRGeomCoordinatePrecision::ResolutionToPrecision( |
1292 | 0 | CPLAtof(pszXYCoordRes)); |
1293 | 0 | } |
1294 | |
|
1295 | 0 | const char *pszZCoordRes = |
1296 | 0 | CSLFetchNameValue(papszOptions, "Z_COORD_RESOLUTION"); |
1297 | 0 | if (pszZCoordRes) |
1298 | 0 | { |
1299 | 0 | coordOpts.format = OGRWktFormat::F; |
1300 | 0 | coordOpts.zPrecision = |
1301 | 0 | OGRGeomCoordinatePrecision::ResolutionToPrecision( |
1302 | 0 | CPLAtof(pszZCoordRes)); |
1303 | 0 | } |
1304 | |
|
1305 | 0 | size_t nLength = 0; |
1306 | 0 | size_t nMaxLength = 1; |
1307 | |
|
1308 | 0 | char *pszText = static_cast<char *>(CPLMalloc(nMaxLength)); |
1309 | 0 | pszText[0] = '\0'; |
1310 | |
|
1311 | 0 | const char *pszFormat = CSLFetchNameValue(papszOptions, "FORMAT"); |
1312 | 0 | const bool bNamespaceDecl = |
1313 | 0 | CPLTestBool(CSLFetchNameValueDef(papszOptions, "NAMESPACE_DECL", |
1314 | 0 | "NO")) != FALSE; |
1315 | 0 | if (pszFormat && (EQUAL(pszFormat, "GML3") || EQUAL(pszFormat, "GML32"))) |
1316 | 0 | { |
1317 | 0 | const char *pszLineStringElement = |
1318 | 0 | CSLFetchNameValue(papszOptions, "GML3_LINESTRING_ELEMENT"); |
1319 | 0 | const bool bLineStringAsCurve = |
1320 | 0 | pszLineStringElement && EQUAL(pszLineStringElement, "curve"); |
1321 | 0 | const char *pszLongSRS = |
1322 | 0 | CSLFetchNameValue(papszOptions, "GML3_LONGSRS"); |
1323 | 0 | const char *pszSRSNameFormat = |
1324 | 0 | CSLFetchNameValue(papszOptions, "SRSNAME_FORMAT"); |
1325 | 0 | GMLSRSNameFormat eSRSNameFormat = SRSNAME_OGC_URN; |
1326 | 0 | if (pszSRSNameFormat) |
1327 | 0 | { |
1328 | 0 | if (pszLongSRS) |
1329 | 0 | { |
1330 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
1331 | 0 | "Both GML3_LONGSRS and SRSNAME_FORMAT specified. " |
1332 | 0 | "Ignoring GML3_LONGSRS"); |
1333 | 0 | } |
1334 | 0 | if (EQUAL(pszSRSNameFormat, "SHORT")) |
1335 | 0 | eSRSNameFormat = SRSNAME_SHORT; |
1336 | 0 | else if (EQUAL(pszSRSNameFormat, "OGC_URN")) |
1337 | 0 | eSRSNameFormat = SRSNAME_OGC_URN; |
1338 | 0 | else if (EQUAL(pszSRSNameFormat, "OGC_URL")) |
1339 | 0 | eSRSNameFormat = SRSNAME_OGC_URL; |
1340 | 0 | else |
1341 | 0 | { |
1342 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
1343 | 0 | "Invalid value for SRSNAME_FORMAT. " |
1344 | 0 | "Using SRSNAME_OGC_URN"); |
1345 | 0 | } |
1346 | 0 | } |
1347 | 0 | else if (pszLongSRS && !CPLTestBool(pszLongSRS)) |
1348 | 0 | eSRSNameFormat = SRSNAME_SHORT; |
1349 | |
|
1350 | 0 | const char *pszGMLId = CSLFetchNameValue(papszOptions, "GMLID"); |
1351 | 0 | if (pszGMLId == nullptr && EQUAL(pszFormat, "GML32")) |
1352 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1353 | 0 | "FORMAT=GML32 specified but not GMLID set"); |
1354 | 0 | const char *pszSRSDimensionLoc = |
1355 | 0 | CSLFetchNameValueDef(papszOptions, "SRSDIMENSION_LOC", "POSLIST"); |
1356 | 0 | char **papszSRSDimensionLoc = |
1357 | 0 | CSLTokenizeString2(pszSRSDimensionLoc, ",", 0); |
1358 | 0 | int nSRSDimensionLocFlags = 0; |
1359 | 0 | for (int i = 0; papszSRSDimensionLoc[i] != nullptr; i++) |
1360 | 0 | { |
1361 | 0 | if (EQUAL(papszSRSDimensionLoc[i], "POSLIST")) |
1362 | 0 | nSRSDimensionLocFlags |= SRSDIM_LOC_POSLIST; |
1363 | 0 | else if (EQUAL(papszSRSDimensionLoc[i], "GEOMETRY")) |
1364 | 0 | nSRSDimensionLocFlags |= SRSDIM_LOC_GEOMETRY; |
1365 | 0 | else |
1366 | 0 | CPLDebug("OGR", "Unrecognized location for srsDimension : %s", |
1367 | 0 | papszSRSDimensionLoc[i]); |
1368 | 0 | } |
1369 | 0 | CSLDestroy(papszSRSDimensionLoc); |
1370 | 0 | const char *pszNamespaceDecl = nullptr; |
1371 | 0 | if (bNamespaceDecl && EQUAL(pszFormat, "GML32")) |
1372 | 0 | pszNamespaceDecl = "http://www.opengis.net/gml/3.2"; |
1373 | 0 | else if (bNamespaceDecl) |
1374 | 0 | pszNamespaceDecl = "http://www.opengis.net/gml"; |
1375 | |
|
1376 | 0 | bool bCoordSwap = false; |
1377 | 0 | const char *pszCoordSwap = |
1378 | 0 | CSLFetchNameValue(papszOptions, "COORD_SWAP"); |
1379 | 0 | if (pszCoordSwap) |
1380 | 0 | { |
1381 | 0 | bCoordSwap = CPLTestBool(pszCoordSwap); |
1382 | 0 | } |
1383 | 0 | else |
1384 | 0 | { |
1385 | 0 | const OGRSpatialReference *poSRS = |
1386 | 0 | poGeometry->getSpatialReference(); |
1387 | 0 | if (poSRS != nullptr && eSRSNameFormat != SRSNAME_SHORT) |
1388 | 0 | { |
1389 | 0 | const auto &map = poSRS->GetDataAxisToSRSAxisMapping(); |
1390 | 0 | if (map.size() >= 2 && map[0] == 2 && map[1] == 1) |
1391 | 0 | { |
1392 | 0 | bCoordSwap = true; |
1393 | 0 | } |
1394 | 0 | } |
1395 | 0 | } |
1396 | |
|
1397 | 0 | if (!OGR2GML3GeometryAppend(poGeometry, nullptr, &pszText, &nLength, |
1398 | 0 | &nMaxLength, false, eSRSNameFormat, |
1399 | 0 | bCoordSwap, bLineStringAsCurve, pszGMLId, |
1400 | 0 | nSRSDimensionLocFlags, false, |
1401 | 0 | pszNamespaceDecl, nullptr, coordOpts)) |
1402 | 0 | { |
1403 | 0 | CPLFree(pszText); |
1404 | 0 | return nullptr; |
1405 | 0 | } |
1406 | | |
1407 | 0 | return pszText; |
1408 | 0 | } |
1409 | | |
1410 | 0 | const char *pszNamespaceDecl = nullptr; |
1411 | 0 | if (bNamespaceDecl) |
1412 | 0 | pszNamespaceDecl = "http://www.opengis.net/gml"; |
1413 | 0 | if (!OGR2GMLGeometryAppend(poGeometry, &pszText, &nLength, &nMaxLength, |
1414 | 0 | false, pszNamespaceDecl, coordOpts)) |
1415 | 0 | { |
1416 | 0 | CPLFree(pszText); |
1417 | 0 | return nullptr; |
1418 | 0 | } |
1419 | | |
1420 | 0 | return pszText; |
1421 | 0 | } |