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