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