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