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