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());
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 1152 : std::string osAttributes;
549 :
550 576 : if (pszNamespaceDecl != nullptr)
551 : {
552 2 : osAttributes += " xmlns:gml=\"";
553 2 : osAttributes += pszNamespaceDecl;
554 2 : osAttributes += '"';
555 : }
556 :
557 576 : if (!bIsSubGeometry && poSRS)
558 : {
559 115 : const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
560 115 : const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
561 115 : if (pszAuthName && pszAuthCode &&
562 115 : strlen(pszAuthName) + strlen(pszAuthCode) < 30)
563 : {
564 115 : if (eSRSNameFormat == SRSNAME_OGC_URN)
565 : {
566 70 : osAttributes += " srsName=\"urn:ogc:def:crs:";
567 70 : osAttributes += pszAuthName;
568 70 : osAttributes += "::";
569 70 : osAttributes += pszAuthCode;
570 70 : osAttributes += '"';
571 : }
572 45 : else if (eSRSNameFormat == SRSNAME_SHORT)
573 : {
574 7 : osAttributes += " srsName=\"";
575 7 : osAttributes += pszAuthName;
576 7 : osAttributes += ":";
577 7 : osAttributes += pszAuthCode;
578 7 : osAttributes += '"';
579 : }
580 38 : else if (eSRSNameFormat == SRSNAME_OGC_URL)
581 : {
582 38 : osAttributes += " srsName=\"http://www.opengis.net/def/crs/";
583 38 : osAttributes += pszAuthName;
584 38 : osAttributes += "/0/";
585 38 : osAttributes += pszAuthCode;
586 38 : osAttributes += '"';
587 : }
588 : }
589 : }
590 :
591 578 : if ((nSRSDimensionLocFlags & SRSDIM_LOC_GEOMETRY) != 0 &&
592 2 : wkbHasZ(poGeometry->getGeometryType()))
593 : {
594 2 : osAttributes += " srsDimension=\"3\"";
595 :
596 2 : nSRSDimensionLocFlags &= ~SRSDIM_LOC_GEOMETRY;
597 : }
598 :
599 576 : if (pszGMLId)
600 : {
601 360 : osAttributes += " gml:id=\"";
602 360 : osAttributes += pszGMLId;
603 360 : osAttributes += '"';
604 : }
605 :
606 576 : const OGRwkbGeometryType eType = poGeometry->getGeometryType();
607 576 : const OGRwkbGeometryType eFType = wkbFlatten(eType);
608 :
609 : /* -------------------------------------------------------------------- */
610 : /* 2D Point */
611 : /* -------------------------------------------------------------------- */
612 576 : if (eType == wkbPoint)
613 : {
614 118 : const auto poPoint = poGeometry->toPoint();
615 :
616 118 : char szCoordinate[256] = {};
617 118 : if (bCoordSwap)
618 : {
619 : const auto wkt = OGRMakeWktCoordinate(
620 31 : poPoint->getY(), poPoint->getX(), 0.0, 2, coordOpts);
621 31 : memcpy(szCoordinate, wkt.data(), wkt.size() + 1);
622 : }
623 : else
624 : {
625 : const auto wkt = OGRMakeWktCoordinate(
626 87 : poPoint->getX(), poPoint->getY(), 0.0, 2, coordOpts);
627 87 : memcpy(szCoordinate, wkt.data(), wkt.size() + 1);
628 : }
629 118 : _GrowBuffer(*pnLength + strlen(szCoordinate) + 60 + osAttributes.size(),
630 : ppszText, pnMaxLength);
631 :
632 118 : snprintf(*ppszText + *pnLength, *pnMaxLength - *pnLength,
633 : "<gml:Point%s><gml:pos>%s</gml:pos></gml:Point>",
634 : osAttributes.c_str(), szCoordinate);
635 :
636 118 : *pnLength += strlen(*ppszText + *pnLength);
637 : }
638 : /* -------------------------------------------------------------------- */
639 : /* 3D Point */
640 : /* -------------------------------------------------------------------- */
641 458 : else if (eType == wkbPoint25D)
642 : {
643 11 : const auto poPoint = poGeometry->toPoint();
644 :
645 11 : char szCoordinate[256] = {};
646 11 : if (bCoordSwap)
647 : {
648 : const auto wkt =
649 : OGRMakeWktCoordinate(poPoint->getY(), poPoint->getX(),
650 0 : poPoint->getZ(), 3, coordOpts);
651 0 : memcpy(szCoordinate, wkt.data(), wkt.size() + 1);
652 : }
653 : else
654 : {
655 : const auto wkt =
656 : OGRMakeWktCoordinate(poPoint->getX(), poPoint->getY(),
657 11 : poPoint->getZ(), 3, coordOpts);
658 11 : memcpy(szCoordinate, wkt.data(), wkt.size() + 1);
659 : }
660 :
661 11 : _GrowBuffer(*pnLength + strlen(szCoordinate) + 70 + osAttributes.size(),
662 : ppszText, pnMaxLength);
663 :
664 11 : snprintf(*ppszText + *pnLength, *pnMaxLength - *pnLength,
665 : "<gml:Point%s><gml:pos>%s</gml:pos></gml:Point>",
666 : osAttributes.c_str(), szCoordinate);
667 :
668 11 : *pnLength += strlen(*ppszText + *pnLength);
669 : }
670 :
671 : /* -------------------------------------------------------------------- */
672 : /* LineString and LinearRing */
673 : /* -------------------------------------------------------------------- */
674 447 : else if (eFType == wkbLineString)
675 : {
676 196 : const bool bRing = EQUAL(poGeometry->getGeometryName(), "LINEARRING") ||
677 196 : bForceLineStringAsLinearRing;
678 196 : if (!bRing && bLineStringAsCurve)
679 : {
680 4 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:Curve");
681 4 : AppendString(ppszText, pnLength, pnMaxLength, osAttributes.c_str());
682 4 : AppendString(ppszText, pnLength, pnMaxLength,
683 : "><gml:segments><gml:LineStringSegment>");
684 4 : const auto poLineString = poGeometry->toLineString();
685 4 : AppendGML3CoordinateList(poLineString, bCoordSwap, ppszText,
686 : pnLength, pnMaxLength,
687 : nSRSDimensionLocFlags, coordOpts);
688 4 : AppendString(ppszText, pnLength, pnMaxLength,
689 4 : "</gml:LineStringSegment></gml:segments></gml:Curve>");
690 : }
691 : else
692 : {
693 : // Buffer for tag name + srsName attribute if set.
694 192 : const size_t nLineTagLength = 16;
695 : const size_t nLineTagNameBufLen =
696 192 : nLineTagLength + osAttributes.size() + 1;
697 : char *pszLineTagName =
698 192 : static_cast<char *>(CPLMalloc(nLineTagNameBufLen));
699 :
700 192 : if (bRing)
701 : {
702 : // LinearRing isn't supposed to have srsName attribute according
703 : // to GML3 SF-0.
704 129 : AppendString(ppszText, pnLength, pnMaxLength,
705 : "<gml:LinearRing>");
706 : }
707 : else
708 : {
709 63 : snprintf(pszLineTagName, nLineTagNameBufLen,
710 : "<gml:LineString%s>", osAttributes.c_str());
711 :
712 63 : AppendString(ppszText, pnLength, pnMaxLength, pszLineTagName);
713 : }
714 :
715 : // Free tag buffer.
716 192 : CPLFree(pszLineTagName);
717 :
718 192 : const auto poLineString = poGeometry->toLineString();
719 :
720 192 : AppendGML3CoordinateList(poLineString, bCoordSwap, ppszText,
721 : pnLength, pnMaxLength,
722 : nSRSDimensionLocFlags, coordOpts);
723 :
724 192 : if (bRing)
725 129 : AppendString(ppszText, pnLength, pnMaxLength,
726 : "</gml:LinearRing>");
727 : else
728 63 : AppendString(ppszText, pnLength, pnMaxLength,
729 : "</gml:LineString>");
730 : }
731 : }
732 :
733 : /* -------------------------------------------------------------------- */
734 : /* ArcString or Circle */
735 : /* -------------------------------------------------------------------- */
736 251 : else if (eFType == wkbCircularString)
737 : {
738 24 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:Curve");
739 24 : AppendString(ppszText, pnLength, pnMaxLength, osAttributes.c_str());
740 24 : const auto poSC = poGeometry->toCircularString();
741 :
742 : // SQL MM has a unique type for arc and circle, GML does not.
743 39 : if (poSC->getNumPoints() == 3 && poSC->getX(0) == poSC->getX(2) &&
744 15 : poSC->getY(0) == poSC->getY(2))
745 : {
746 13 : const double dfMidX = (poSC->getX(0) + poSC->getX(1)) / 2.0;
747 13 : const double dfMidY = (poSC->getY(0) + poSC->getY(1)) / 2.0;
748 13 : const double dfDirX = (poSC->getX(1) - poSC->getX(0)) / 2.0;
749 13 : const double dfDirY = (poSC->getY(1) - poSC->getY(0)) / 2.0;
750 13 : const double dfNormX = -dfDirY;
751 13 : const double dfNormY = dfDirX;
752 13 : const double dfNewX = dfMidX + dfNormX;
753 13 : const double dfNewY = dfMidY + dfNormY;
754 13 : OGRLineString *poLS = new OGRLineString();
755 26 : OGRPoint p;
756 13 : poSC->getPoint(0, &p);
757 13 : poLS->addPoint(&p);
758 13 : poSC->getPoint(1, &p);
759 13 : if (poSC->getCoordinateDimension() == 3)
760 1 : poLS->addPoint(dfNewX, dfNewY, p.getZ());
761 : else
762 12 : poLS->addPoint(dfNewX, dfNewY);
763 13 : poLS->addPoint(&p);
764 13 : AppendString(ppszText, pnLength, pnMaxLength,
765 : "><gml:segments><gml:Circle>");
766 13 : AppendGML3CoordinateList(poLS, bCoordSwap, ppszText, pnLength,
767 : pnMaxLength, nSRSDimensionLocFlags,
768 : coordOpts);
769 13 : AppendString(ppszText, pnLength, pnMaxLength,
770 : "</gml:Circle></gml:segments></gml:Curve>");
771 13 : delete poLS;
772 : }
773 : else
774 : {
775 11 : AppendString(ppszText, pnLength, pnMaxLength,
776 : "><gml:segments><gml:ArcString>");
777 11 : AppendGML3CoordinateList(poSC, bCoordSwap, ppszText, pnLength,
778 : pnMaxLength, nSRSDimensionLocFlags,
779 : coordOpts);
780 11 : AppendString(ppszText, pnLength, pnMaxLength,
781 : "</gml:ArcString></gml:segments></gml:Curve>");
782 : }
783 : }
784 :
785 : /* -------------------------------------------------------------------- */
786 : /* CompositeCurve */
787 : /* -------------------------------------------------------------------- */
788 227 : else if (eFType == wkbCompoundCurve)
789 : {
790 4 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:CompositeCurve");
791 4 : AppendString(ppszText, pnLength, pnMaxLength, osAttributes.c_str());
792 4 : AppendString(ppszText, pnLength, pnMaxLength, ">");
793 :
794 4 : const auto poCC = poGeometry->toCompoundCurve();
795 8 : for (int i = 0; i < poCC->getNumCurves(); i++)
796 : {
797 4 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:curveMember>");
798 :
799 4 : char *pszGMLIdSub = nullptr;
800 4 : if (pszGMLId != nullptr)
801 0 : pszGMLIdSub = CPLStrdup(CPLSPrintf("%s.%d", pszGMLId, i));
802 :
803 4 : CPL_IGNORE_RET_VAL(OGR2GML3GeometryAppend(
804 4 : poCC->getCurve(i), poSRS, ppszText, pnLength, pnMaxLength, true,
805 : eSRSNameFormat, bCoordSwap, bLineStringAsCurve, pszGMLIdSub,
806 : nSRSDimensionLocFlags, false, nullptr, nullptr, coordOpts));
807 :
808 4 : CPLFree(pszGMLIdSub);
809 :
810 4 : AppendString(ppszText, pnLength, pnMaxLength, "</gml:curveMember>");
811 : }
812 4 : AppendString(ppszText, pnLength, pnMaxLength, "</gml:CompositeCurve>");
813 : }
814 :
815 : /* -------------------------------------------------------------------- */
816 : /* Polygon */
817 : /* -------------------------------------------------------------------- */
818 223 : else if (eFType == wkbPolygon || eFType == wkbCurvePolygon)
819 : {
820 135 : const auto poCP = poGeometry->toCurvePolygon();
821 :
822 : // Buffer for polygon tag name + srsName attribute if set.
823 135 : const char *pszElemName =
824 135 : pszOverriddenElementName ? pszOverriddenElementName : "Polygon";
825 135 : const size_t nPolyTagLength = 7 + strlen(pszElemName);
826 : const size_t nPolyTagNameBufLen =
827 135 : nPolyTagLength + osAttributes.size() + 1;
828 : char *pszPolyTagName =
829 135 : static_cast<char *>(CPLMalloc(nPolyTagNameBufLen));
830 :
831 : // Compose Polygon tag with or without srsName attribute.
832 135 : snprintf(pszPolyTagName, nPolyTagNameBufLen, "<gml:%s%s>", pszElemName,
833 : osAttributes.c_str());
834 :
835 135 : AppendString(ppszText, pnLength, pnMaxLength, pszPolyTagName);
836 :
837 : // Free tag buffer.
838 135 : CPLFree(pszPolyTagName);
839 :
840 : const auto AppendCompoundCurveMembers =
841 139 : [&](const OGRGeometry *poRing, const char *pszGMLIdRing)
842 : {
843 139 : const auto eRingType = wkbFlatten(poRing->getGeometryType());
844 139 : if (eRingType == wkbCompoundCurve)
845 : {
846 2 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:Ring>");
847 2 : const auto poCC = poRing->toCompoundCurve();
848 2 : const int nNumCurves = poCC->getNumCurves();
849 6 : for (int i = 0; i < nNumCurves; i++)
850 : {
851 4 : AppendString(ppszText, pnLength, pnMaxLength,
852 : "<gml:curveMember>");
853 :
854 4 : char *pszGMLIdSub = nullptr;
855 4 : if (pszGMLIdRing != nullptr)
856 : pszGMLIdSub =
857 2 : CPLStrdup(CPLSPrintf("%s.%d", pszGMLIdRing, i));
858 :
859 4 : CPL_IGNORE_RET_VAL(OGR2GML3GeometryAppend(
860 4 : poCC->getCurve(i), poSRS, ppszText, pnLength,
861 141 : pnMaxLength, true, eSRSNameFormat, bCoordSwap,
862 141 : bLineStringAsCurve, pszGMLIdSub, nSRSDimensionLocFlags,
863 4 : false, nullptr, nullptr, coordOpts));
864 :
865 4 : CPLFree(pszGMLIdSub);
866 :
867 4 : AppendString(ppszText, pnLength, pnMaxLength,
868 : "</gml:curveMember>");
869 : }
870 2 : AppendString(ppszText, pnLength, pnMaxLength, "</gml:Ring>");
871 : }
872 : else
873 : {
874 137 : if (eRingType != wkbLineString)
875 : {
876 11 : AppendString(ppszText, pnLength, pnMaxLength,
877 : "<gml:Ring><gml:curveMember>");
878 : }
879 :
880 137 : CPL_IGNORE_RET_VAL(OGR2GML3GeometryAppend(
881 : poRing, poSRS, ppszText, pnLength, pnMaxLength, true,
882 137 : eSRSNameFormat, bCoordSwap, bLineStringAsCurve,
883 : pszGMLIdRing, nSRSDimensionLocFlags, true, nullptr, nullptr,
884 : coordOpts));
885 :
886 137 : if (eRingType != wkbLineString)
887 : {
888 11 : AppendString(ppszText, pnLength, pnMaxLength,
889 : "</gml:curveMember></gml:Ring>");
890 : }
891 : }
892 274 : };
893 :
894 : // Don't add srsName to polygon rings.
895 :
896 135 : const auto poExteriorRing = poCP->getExteriorRingCurve();
897 135 : if (poExteriorRing != nullptr)
898 : {
899 135 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:exterior>");
900 :
901 209 : AppendCompoundCurveMembers(
902 : poExteriorRing,
903 209 : pszGMLId ? (std::string(pszGMLId) + ".exterior").c_str()
904 : : nullptr);
905 :
906 135 : AppendString(ppszText, pnLength, pnMaxLength, "</gml:exterior>");
907 :
908 139 : for (int iRing = 0; iRing < poCP->getNumInteriorRings(); iRing++)
909 : {
910 4 : const OGRCurve *poRing = poCP->getInteriorRingCurve(iRing);
911 :
912 4 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:interior>");
913 :
914 5 : AppendCompoundCurveMembers(
915 5 : poRing, pszGMLId ? (std::string(pszGMLId) + ".interior." +
916 5 : std::to_string(iRing))
917 1 : .c_str()
918 : : nullptr);
919 :
920 4 : AppendString(ppszText, pnLength, pnMaxLength,
921 : "</gml:interior>");
922 : }
923 : }
924 :
925 135 : AppendString(ppszText, pnLength, pnMaxLength, "</gml:");
926 135 : AppendString(ppszText, pnLength, pnMaxLength, pszElemName);
927 135 : AppendString(ppszText, pnLength, pnMaxLength, ">");
928 : }
929 :
930 : /* -------------------------------------------------------------------- */
931 : /* Triangle */
932 : /* -------------------------------------------------------------------- */
933 88 : else if (eFType == wkbTriangle)
934 : {
935 3 : const auto poTri = poGeometry->toPolygon();
936 :
937 3 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:Triangle>");
938 :
939 3 : if (poTri->getExteriorRingCurve() != nullptr)
940 : {
941 3 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:exterior>");
942 :
943 3 : CPL_IGNORE_RET_VAL(OGR2GML3GeometryAppend(
944 3 : poTri->getExteriorRingCurve(), poSRS, ppszText, pnLength,
945 : pnMaxLength, true, eSRSNameFormat, bCoordSwap,
946 : bLineStringAsCurve, nullptr, nSRSDimensionLocFlags, true,
947 : nullptr, nullptr, coordOpts));
948 :
949 3 : AppendString(ppszText, pnLength, pnMaxLength, "</gml:exterior>");
950 : }
951 :
952 3 : AppendString(ppszText, pnLength, pnMaxLength, "</gml:Triangle>");
953 : }
954 :
955 : /* -------------------------------------------------------------------- */
956 : /* MultiSurface, MultiCurve, MultiPoint, MultiGeometry */
957 : /* -------------------------------------------------------------------- */
958 85 : else if (eFType == wkbMultiPolygon || eFType == wkbMultiSurface ||
959 31 : eFType == wkbMultiLineString || eFType == wkbMultiCurve ||
960 15 : eFType == wkbMultiPoint || eFType == wkbGeometryCollection)
961 : {
962 82 : const auto poGC = poGeometry->toGeometryCollection();
963 82 : const char *pszElemClose = nullptr;
964 82 : const char *pszMemberElem = nullptr;
965 :
966 : // Buffer for opening tag + srsName attribute.
967 82 : char *pszElemOpen = nullptr;
968 :
969 82 : if (eFType == wkbMultiPolygon || eFType == wkbMultiSurface)
970 : {
971 31 : const size_t nBufLen = 13 + osAttributes.size() + 1;
972 31 : pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen));
973 31 : snprintf(pszElemOpen, nBufLen, "MultiSurface%s>",
974 : osAttributes.c_str());
975 :
976 31 : pszElemClose = "MultiSurface>";
977 31 : pszMemberElem = "surfaceMember>";
978 : }
979 51 : else if (eFType == wkbMultiLineString || eFType == wkbMultiCurve)
980 : {
981 27 : const size_t nBufLen = 16 + osAttributes.size() + 1;
982 27 : pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen));
983 27 : snprintf(pszElemOpen, nBufLen, "MultiCurve%s>",
984 : osAttributes.c_str());
985 :
986 27 : pszElemClose = "MultiCurve>";
987 27 : pszMemberElem = "curveMember>";
988 : }
989 24 : else if (eFType == wkbMultiPoint)
990 : {
991 12 : const size_t nBufLen = 11 + osAttributes.size() + 1;
992 12 : pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen));
993 12 : snprintf(pszElemOpen, nBufLen, "MultiPoint%s>",
994 : osAttributes.c_str());
995 :
996 12 : pszElemClose = "MultiPoint>";
997 12 : pszMemberElem = "pointMember>";
998 : }
999 : else
1000 : {
1001 12 : const size_t nBufLen = 19 + osAttributes.size() + 1;
1002 12 : pszElemOpen = static_cast<char *>(CPLMalloc(nBufLen));
1003 12 : snprintf(pszElemOpen, nBufLen, "MultiGeometry%s>",
1004 : osAttributes.c_str());
1005 :
1006 12 : pszElemClose = "MultiGeometry>";
1007 12 : pszMemberElem = "geometryMember>";
1008 : }
1009 :
1010 82 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:");
1011 82 : AppendString(ppszText, pnLength, pnMaxLength, pszElemOpen);
1012 :
1013 : // Free tag buffer.
1014 82 : CPLFree(pszElemOpen);
1015 :
1016 188 : for (int iMember = 0; iMember < poGC->getNumGeometries(); iMember++)
1017 : {
1018 106 : const OGRGeometry *poMember = poGC->getGeometryRef(iMember);
1019 :
1020 106 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:");
1021 106 : AppendString(ppszText, pnLength, pnMaxLength, pszMemberElem);
1022 :
1023 106 : char *pszGMLIdSub = nullptr;
1024 106 : if (pszGMLId != nullptr)
1025 78 : pszGMLIdSub = CPLStrdup(CPLSPrintf("%s.%d", pszGMLId, iMember));
1026 :
1027 106 : if (!OGR2GML3GeometryAppend(
1028 : poMember, poSRS, ppszText, pnLength, pnMaxLength, true,
1029 : eSRSNameFormat, bCoordSwap, bLineStringAsCurve, pszGMLIdSub,
1030 : nSRSDimensionLocFlags, false, nullptr, nullptr, coordOpts))
1031 : {
1032 0 : CPLFree(pszGMLIdSub);
1033 0 : return false;
1034 : }
1035 :
1036 106 : CPLFree(pszGMLIdSub);
1037 :
1038 106 : AppendString(ppszText, pnLength, pnMaxLength, "</gml:");
1039 106 : AppendString(ppszText, pnLength, pnMaxLength, pszMemberElem);
1040 : }
1041 :
1042 82 : AppendString(ppszText, pnLength, pnMaxLength, "</gml:");
1043 82 : AppendString(ppszText, pnLength, pnMaxLength, pszElemClose);
1044 : }
1045 :
1046 : /* -------------------------------------------------------------------- */
1047 : /* Polyhedral Surface */
1048 : /* -------------------------------------------------------------------- */
1049 3 : else if (eFType == wkbPolyhedralSurface)
1050 : {
1051 : // The patches enclosed in a single <gml:polygonPatches> tag need to be
1052 : // co-planar.
1053 : // TODO - enforce the condition within this implementation
1054 2 : const auto poPS = poGeometry->toPolyhedralSurface();
1055 :
1056 2 : AppendString(ppszText, pnLength, pnMaxLength, "<gml:PolyhedralSurface");
1057 2 : AppendString(ppszText, pnLength, pnMaxLength, osAttributes.c_str());
1058 2 : AppendString(ppszText, pnLength, pnMaxLength, "><gml:polygonPatches>");
1059 :
1060 8 : for (int iMember = 0; iMember < poPS->getNumGeometries(); iMember++)
1061 : {
1062 6 : const OGRGeometry *poMember = poPS->getGeometryRef(iMember);
1063 6 : char *pszGMLIdSub = nullptr;
1064 6 : if (pszGMLId != nullptr)
1065 0 : pszGMLIdSub = CPLStrdup(CPLSPrintf("%s.%d", pszGMLId, iMember));
1066 :
1067 6 : if (!OGR2GML3GeometryAppend(poMember, poSRS, ppszText, pnLength,
1068 : pnMaxLength, true, eSRSNameFormat,
1069 : bCoordSwap, bLineStringAsCurve, nullptr,
1070 : nSRSDimensionLocFlags, false, nullptr,
1071 : "PolygonPatch", coordOpts))
1072 : {
1073 0 : CPLFree(pszGMLIdSub);
1074 0 : return false;
1075 : }
1076 :
1077 6 : CPLFree(pszGMLIdSub);
1078 : }
1079 :
1080 2 : AppendString(ppszText, pnLength, pnMaxLength, "</gml:polygonPatches>");
1081 2 : AppendString(ppszText, pnLength, pnMaxLength,
1082 : "</gml:PolyhedralSurface>");
1083 : }
1084 :
1085 : /* -------------------------------------------------------------------- */
1086 : /* TIN */
1087 : /* -------------------------------------------------------------------- */
1088 1 : else if (eFType == wkbTIN)
1089 : {
1090 : // OGR uses the following hierarchy for TriangulatedSurface -
1091 :
1092 : // <gml:TriangulatedSurface>
1093 : // <gml:patches>
1094 : // <gml:Triangle>
1095 : // <gml:exterior>
1096 : // <gml:LinearRing>
1097 : // <gml:posList srsDimension=...>...</gml:posList>
1098 : // </gml:LinearRing>
1099 : // </gml:exterior>
1100 : // </gml:Triangle>
1101 : // </gml:patches>
1102 : // </gml:TriangulatedSurface>
1103 :
1104 : // <gml:trianglePatches> is deprecated, so write feature is not enabled
1105 : // for <gml:trianglePatches>
1106 1 : const auto poTIN = poGeometry->toPolyhedralSurface();
1107 :
1108 1 : AppendString(ppszText, pnLength, pnMaxLength,
1109 : "<gml:TriangulatedSurface");
1110 1 : AppendString(ppszText, pnLength, pnMaxLength, osAttributes.c_str());
1111 1 : AppendString(ppszText, pnLength, pnMaxLength, "><gml:patches>");
1112 :
1113 3 : for (int iMember = 0; iMember < poTIN->getNumGeometries(); iMember++)
1114 : {
1115 2 : const OGRGeometry *poMember = poTIN->getGeometryRef(iMember);
1116 :
1117 2 : char *pszGMLIdSub = nullptr;
1118 2 : if (pszGMLId != nullptr)
1119 0 : pszGMLIdSub = CPLStrdup(CPLSPrintf("%s.%d", pszGMLId, iMember));
1120 :
1121 2 : CPL_IGNORE_RET_VAL(OGR2GML3GeometryAppend(
1122 : poMember, poSRS, ppszText, pnLength, pnMaxLength, true,
1123 : eSRSNameFormat, bCoordSwap, bLineStringAsCurve, nullptr,
1124 : nSRSDimensionLocFlags, false, nullptr, nullptr, coordOpts));
1125 :
1126 2 : CPLFree(pszGMLIdSub);
1127 : }
1128 :
1129 1 : AppendString(ppszText, pnLength, pnMaxLength, "</gml:patches>");
1130 1 : AppendString(ppszText, pnLength, pnMaxLength,
1131 : "</gml:TriangulatedSurface>");
1132 : }
1133 :
1134 : else
1135 : {
1136 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported geometry type %s",
1137 : OGRGeometryTypeToName(eType));
1138 0 : return false;
1139 : }
1140 :
1141 576 : return true;
1142 : }
1143 :
1144 : /************************************************************************/
1145 : /* OGR_G_ExportToGMLTree() */
1146 : /************************************************************************/
1147 :
1148 : /** Convert a geometry into GML format. */
1149 0 : CPLXMLNode *OGR_G_ExportToGMLTree(OGRGeometryH hGeometry)
1150 :
1151 : {
1152 0 : char *pszText = OGR_G_ExportToGML(hGeometry);
1153 0 : if (pszText == nullptr)
1154 0 : return nullptr;
1155 :
1156 0 : CPLXMLNode *psTree = CPLParseXMLString(pszText);
1157 :
1158 0 : CPLFree(pszText);
1159 :
1160 0 : return psTree;
1161 : }
1162 :
1163 : /************************************************************************/
1164 : /* OGR_G_ExportToGML() */
1165 : /************************************************************************/
1166 :
1167 : /**
1168 : * \brief Convert a geometry into GML format.
1169 : *
1170 : * The GML geometry is expressed directly in terms of GML basic data
1171 : * types assuming the this is available in the gml namespace. The returned
1172 : * string should be freed with CPLFree() when no longer required.
1173 : *
1174 : * This method is the same as the C++ method OGRGeometry::exportToGML().
1175 : *
1176 : * @param hGeometry handle to the geometry.
1177 : * @return A GML fragment or NULL in case of error.
1178 : */
1179 :
1180 1 : char *OGR_G_ExportToGML(OGRGeometryH hGeometry)
1181 :
1182 : {
1183 1 : return OGR_G_ExportToGMLEx(hGeometry, nullptr);
1184 : }
1185 :
1186 : /************************************************************************/
1187 : /* OGR_G_ExportToGMLEx() */
1188 : /************************************************************************/
1189 :
1190 : /**
1191 : * \brief Convert a geometry into GML format.
1192 : *
1193 : * The GML geometry is expressed directly in terms of GML basic data
1194 : * types assuming the this is available in the gml namespace. The returned
1195 : * string should be freed with CPLFree() when no longer required.
1196 : *
1197 : * The supported options are :
1198 : * <ul>
1199 : * <li> FORMAT=GML2/GML3/GML32
1200 : * If not set, it will default to GML 2.1.2 output.
1201 : * </li>
1202 : * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
1203 : * To use gml:Curve element for linestrings.
1204 : * Otherwise gml:LineString will be used .
1205 : * </li>
1206 : * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
1207 : * SRSNAME_FORMAT in GDAL >=2.2). Defaults to YES.
1208 : * If YES, SRS with EPSG authority will be written with the
1209 : * "urn:ogc:def:crs:EPSG::" prefix.
1210 : * In the case the SRS should be treated as lat/long or
1211 : * northing/easting, then the function will take care of coordinate order
1212 : * swapping if the data axis to CRS axis mapping indicates it.
1213 : * If set to NO, SRS with EPSG authority will be written with the "EPSG:"
1214 : * prefix, even if they are in lat/long order.
1215 : * </li>
1216 : * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3).
1217 : * Defaults to OGC_URN. If SHORT, then srsName will be in
1218 : * the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
1219 : * in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
1220 : * then srsName will be in the form
1221 : * http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
1222 : * OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
1223 : * or northing/easting, then the function will take care of coordinate
1224 : * order swapping if the data axis to CRS axis mapping indicates it.
1225 : * </li>
1226 : * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
1227 : * top-level geometry element with the provided value.
1228 : * Required for GML 3.2 compatibility.
1229 : * </li>
1230 : * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
1231 : * FORMAT=GML3/GML32) Default to POSLIST.
1232 : * For 2.5D geometries, define the location where to attach the
1233 : * srsDimension attribute.
1234 : * There are diverging implementations. Some put in on the
1235 : * <gml:posList> element, other on the top geometry element.
1236 : * </li>
1237 : * <li> NAMESPACE_DECL=YES/NO. If set to YES,
1238 : * xmlns:gml="http://www.opengis.net/gml" will be added to the root node
1239 : * for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
1240 : * </li>
1241 : * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
1242 : * Resolution for the coordinate precision of the X and Y coordinates.
1243 : * Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
1244 : * to 5 decimal digits. 0 for the default behavior.
1245 : * </li>
1246 : * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
1247 : * Resolution for the coordinate precision of the Z coordinates.
1248 : * Expressed in the units of the Z axis of the SRS.
1249 : * 0 for the default behavior.
1250 : * </li>
1251 : * </ul>
1252 : *
1253 : * Note that curve geometries like CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
1254 : * MULTICURVE or MULTISURFACE are not supported in GML 2.
1255 : *
1256 : * This method is the same as the C++ method OGRGeometry::exportToGML().
1257 : *
1258 : * @param hGeometry handle to the geometry.
1259 : * @param papszOptions NULL-terminated list of options.
1260 : * @return A GML fragment or NULL in case of error.
1261 : *
1262 : */
1263 :
1264 410 : char *OGR_G_ExportToGMLEx(OGRGeometryH hGeometry, CSLConstList papszOptions)
1265 :
1266 : {
1267 410 : if (hGeometry == nullptr)
1268 0 : return CPLStrdup("");
1269 :
1270 : // Do not use hGeometry after here.
1271 410 : OGRGeometry *poGeometry = OGRGeometry::FromHandle(hGeometry);
1272 :
1273 410 : OGRWktOptions coordOpts;
1274 :
1275 : const char *pszXYCoordRes =
1276 410 : CSLFetchNameValue(papszOptions, "XY_COORD_RESOLUTION");
1277 410 : if (pszXYCoordRes)
1278 : {
1279 3 : coordOpts.format = OGRWktFormat::F;
1280 3 : coordOpts.xyPrecision =
1281 3 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
1282 : CPLAtof(pszXYCoordRes));
1283 : }
1284 :
1285 : const char *pszZCoordRes =
1286 410 : CSLFetchNameValue(papszOptions, "Z_COORD_RESOLUTION");
1287 410 : if (pszZCoordRes)
1288 : {
1289 3 : coordOpts.format = OGRWktFormat::F;
1290 3 : coordOpts.zPrecision =
1291 3 : OGRGeomCoordinatePrecision::ResolutionToPrecision(
1292 : CPLAtof(pszZCoordRes));
1293 : }
1294 :
1295 410 : size_t nLength = 0;
1296 410 : size_t nMaxLength = 1;
1297 :
1298 410 : char *pszText = static_cast<char *>(CPLMalloc(nMaxLength));
1299 410 : pszText[0] = '\0';
1300 :
1301 410 : const char *pszFormat = CSLFetchNameValue(papszOptions, "FORMAT");
1302 : const bool bNamespaceDecl =
1303 410 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "NAMESPACE_DECL",
1304 410 : "NO")) != FALSE;
1305 410 : if (pszFormat && (EQUAL(pszFormat, "GML3") || EQUAL(pszFormat, "GML32")))
1306 : {
1307 : const char *pszLineStringElement =
1308 314 : CSLFetchNameValue(papszOptions, "GML3_LINESTRING_ELEMENT");
1309 314 : const bool bLineStringAsCurve =
1310 314 : pszLineStringElement && EQUAL(pszLineStringElement, "curve");
1311 : const char *pszLongSRS =
1312 314 : CSLFetchNameValue(papszOptions, "GML3_LONGSRS");
1313 : const char *pszSRSNameFormat =
1314 314 : CSLFetchNameValue(papszOptions, "SRSNAME_FORMAT");
1315 314 : OGRGMLSRSNameFormat eSRSNameFormat = SRSNAME_OGC_URN;
1316 314 : if (pszSRSNameFormat)
1317 : {
1318 259 : if (pszLongSRS)
1319 : {
1320 0 : CPLError(CE_Warning, CPLE_NotSupported,
1321 : "Both GML3_LONGSRS and SRSNAME_FORMAT specified. "
1322 : "Ignoring GML3_LONGSRS");
1323 : }
1324 259 : if (EQUAL(pszSRSNameFormat, "SHORT"))
1325 2 : eSRSNameFormat = SRSNAME_SHORT;
1326 257 : else if (EQUAL(pszSRSNameFormat, "OGC_URN"))
1327 183 : eSRSNameFormat = SRSNAME_OGC_URN;
1328 74 : else if (EQUAL(pszSRSNameFormat, "OGC_URL"))
1329 74 : eSRSNameFormat = SRSNAME_OGC_URL;
1330 : else
1331 : {
1332 0 : CPLError(CE_Warning, CPLE_NotSupported,
1333 : "Invalid value for SRSNAME_FORMAT. "
1334 : "Using SRSNAME_OGC_URN");
1335 : }
1336 : }
1337 55 : else if (pszLongSRS && !CPLTestBool(pszLongSRS))
1338 5 : eSRSNameFormat = SRSNAME_SHORT;
1339 :
1340 314 : const char *pszGMLId = CSLFetchNameValue(papszOptions, "GMLID");
1341 314 : if (pszGMLId == nullptr && EQUAL(pszFormat, "GML32"))
1342 0 : CPLError(CE_Warning, CPLE_AppDefined,
1343 : "FORMAT=GML32 specified but not GMLID set");
1344 : const char *pszSRSDimensionLoc =
1345 314 : CSLFetchNameValueDef(papszOptions, "SRSDIMENSION_LOC", "POSLIST");
1346 : char **papszSRSDimensionLoc =
1347 314 : CSLTokenizeString2(pszSRSDimensionLoc, ",", 0);
1348 314 : int nSRSDimensionLocFlags = 0;
1349 629 : for (int i = 0; papszSRSDimensionLoc[i] != nullptr; i++)
1350 : {
1351 315 : if (EQUAL(papszSRSDimensionLoc[i], "POSLIST"))
1352 313 : nSRSDimensionLocFlags |= SRSDIM_LOC_POSLIST;
1353 2 : else if (EQUAL(papszSRSDimensionLoc[i], "GEOMETRY"))
1354 2 : nSRSDimensionLocFlags |= SRSDIM_LOC_GEOMETRY;
1355 : else
1356 0 : CPLDebug("OGR", "Unrecognized location for srsDimension : %s",
1357 0 : papszSRSDimensionLoc[i]);
1358 : }
1359 314 : CSLDestroy(papszSRSDimensionLoc);
1360 314 : const char *pszNamespaceDecl = nullptr;
1361 314 : if (bNamespaceDecl && EQUAL(pszFormat, "GML32"))
1362 1 : pszNamespaceDecl = "http://www.opengis.net/gml/3.2";
1363 313 : else if (bNamespaceDecl)
1364 1 : pszNamespaceDecl = "http://www.opengis.net/gml";
1365 :
1366 314 : bool bCoordSwap = false;
1367 : const char *pszCoordSwap =
1368 314 : CSLFetchNameValue(papszOptions, "COORD_SWAP");
1369 314 : if (pszCoordSwap)
1370 : {
1371 0 : bCoordSwap = CPLTestBool(pszCoordSwap);
1372 : }
1373 : else
1374 : {
1375 : const OGRSpatialReference *poSRS =
1376 314 : poGeometry->getSpatialReference();
1377 314 : if (poSRS != nullptr && eSRSNameFormat != SRSNAME_SHORT)
1378 : {
1379 108 : const auto &map = poSRS->GetDataAxisToSRSAxisMapping();
1380 108 : if (map.size() >= 2 && map[0] == 2 && map[1] == 1)
1381 : {
1382 45 : bCoordSwap = true;
1383 : }
1384 : }
1385 : }
1386 :
1387 314 : if (!OGR2GML3GeometryAppend(poGeometry, nullptr, &pszText, &nLength,
1388 : &nMaxLength, false, eSRSNameFormat,
1389 : bCoordSwap, bLineStringAsCurve, pszGMLId,
1390 : nSRSDimensionLocFlags, false,
1391 : pszNamespaceDecl, nullptr, coordOpts))
1392 : {
1393 0 : CPLFree(pszText);
1394 0 : return nullptr;
1395 : }
1396 :
1397 314 : return pszText;
1398 : }
1399 :
1400 96 : const char *pszNamespaceDecl = nullptr;
1401 96 : if (bNamespaceDecl)
1402 1 : pszNamespaceDecl = "http://www.opengis.net/gml";
1403 96 : if (!OGR2GMLGeometryAppend(poGeometry, &pszText, &nLength, &nMaxLength,
1404 : false, pszNamespaceDecl, coordOpts))
1405 : {
1406 1 : CPLFree(pszText);
1407 1 : return nullptr;
1408 : }
1409 :
1410 95 : return pszText;
1411 : }
|