Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: KML Driver
4 : * Purpose: Implementation of OGR -> KML geometries writer.
5 : * Author: Christopher Condit, condit@sdsc.edu
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2006, Christopher Condit
9 : * Copyright (c) 2007-2010, 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
22 : * OR 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 : #include "cpl_port.h"
31 : #include "ogr_api.h"
32 :
33 : #include <stddef.h>
34 : #include <stdio.h>
35 : #include <string.h>
36 : #include <algorithm>
37 :
38 : #include "cpl_conv.h"
39 : #include "cpl_error.h"
40 : #include "cpl_minixml.h"
41 : #include "ogr_core.h"
42 : #include "ogr_geometry.h"
43 : #include "ogr_p.h"
44 :
45 : constexpr double EPSILON = 1e-8;
46 :
47 : /************************************************************************/
48 : /* MakeKMLCoordinate() */
49 : /************************************************************************/
50 :
51 270 : static void MakeKMLCoordinate(char *pszTarget, size_t nTargetLen, double x,
52 : double y, double z, bool b3D)
53 :
54 : {
55 270 : if (y < -90 || y > 90)
56 : {
57 0 : if (y > 90 && y < 90 + EPSILON)
58 : {
59 0 : y = 90;
60 : }
61 0 : else if (y > -90 - EPSILON && y < -90)
62 : {
63 0 : y = -90;
64 : }
65 : else
66 : {
67 : static bool bFirstWarning = true;
68 0 : if (bFirstWarning)
69 : {
70 0 : CPLError(CE_Failure, CPLE_AppDefined,
71 : "Latitude %f is invalid. Valid range is [-90,90]. "
72 : "This warning will not be issued any more",
73 : y);
74 0 : bFirstWarning = false;
75 : }
76 : }
77 : }
78 :
79 270 : if (x < -180 || x > 180)
80 : {
81 0 : if (x > 180 && x < 180 + EPSILON)
82 : {
83 0 : x = 180;
84 : }
85 0 : else if (x > -180 - EPSILON && x < -180)
86 : {
87 0 : x = -180;
88 : }
89 : else
90 : {
91 : static bool bFirstWarning = true;
92 0 : if (bFirstWarning)
93 : {
94 0 : CPLError(CE_Warning, CPLE_AppDefined,
95 : "Longitude %f has been modified to fit into "
96 : "range [-180,180]. This warning will not be "
97 : "issued any more",
98 : x);
99 0 : bFirstWarning = false;
100 : }
101 :
102 : // Trash drastically non-sensical values.
103 0 : if (x > 1.0e6 || x < -1.0e6 || CPLIsNan(x))
104 : {
105 : static bool bFirstWarning2 = true;
106 0 : if (bFirstWarning2)
107 : {
108 0 : CPLError(CE_Failure, CPLE_AppDefined,
109 : "Longitude %lf is unreasonable. Setting to 0."
110 : "This warning will not be issued any more",
111 : x);
112 0 : bFirstWarning2 = false;
113 : }
114 0 : x = 0.0;
115 : }
116 :
117 0 : if (x > 180)
118 0 : x -= (static_cast<int>((x + 180) / 360) * 360);
119 0 : else if (x < -180)
120 0 : x += (static_cast<int>(180 - x) / 360) * 360;
121 : }
122 : }
123 :
124 270 : OGRMakeWktCoordinate(pszTarget, x, y, z, b3D ? 3 : 2);
125 3842 : while (*pszTarget != '\0')
126 : {
127 3572 : if (*pszTarget == ' ')
128 346 : *pszTarget = ',';
129 3572 : pszTarget++;
130 3572 : nTargetLen--;
131 : }
132 :
133 270 : CPL_IGNORE_RET_VAL(nTargetLen);
134 :
135 : #if 0
136 : if( !b3D )
137 : {
138 : if( x == static_cast<int>(x) && y == static_cast<int>(y) )
139 : snprintf( pszTarget, nTargetLen, "%d,%d",
140 : static_cast<int>(x), static_cast<int>(y) );
141 : else if( fabs(x) < 370 && fabs(y) < 370 )
142 : CPLsnprintf( pszTarget, nTargetLen, "%.16g,%.16g", x, y );
143 : else if( fabs(x) > 100000000.0 || fabs(y) > 100000000.0 )
144 : CPLsnprintf( pszTarget, nTargetLen, "%.16g,%.16g", x, y );
145 : else
146 : CPLsnprintf( pszTarget, nTargetLen, "%.3f,%.3f", x, y );
147 : }
148 : else
149 : {
150 : if( x == static_cast<int>(x) &&
151 : y == static_cast<int>(y) &&
152 : z == static_cast<int>(z) )
153 : snprintf( pszTarget, nTargetLen, "%d,%d,%d",
154 : static_cast<int>(x), static_cast<int>(y),
155 : static_cast<int>(z) );
156 : else if( fabs(x) < 370 && fabs(y) < 370 )
157 : CPLsnprintf( pszTarget, nTargetLen, "%.16g,%.16g,%.16g", x, y, z );
158 : else if( fabs(x) > 100000000.0 || fabs(y) > 100000000.0
159 : || fabs(z) > 100000000.0 )
160 : CPLsnprintf( pszTarget, nTargetLen, "%.16g,%.16g,%.16g", x, y, z );
161 : else
162 : CPLsnprintf( pszTarget, nTargetLen, "%.3f,%.3f,%.3f", x, y, z );
163 : }
164 : #endif
165 270 : }
166 :
167 : /************************************************************************/
168 : /* _GrowBuffer() */
169 : /************************************************************************/
170 :
171 842 : static void _GrowBuffer(size_t nNeeded, char **ppszText, size_t *pnMaxLength)
172 :
173 : {
174 842 : if (nNeeded + 1 >= *pnMaxLength)
175 : {
176 359 : *pnMaxLength = std::max(*pnMaxLength * 2, nNeeded + 1);
177 359 : *ppszText = static_cast<char *>(CPLRealloc(*ppszText, *pnMaxLength));
178 : }
179 842 : }
180 :
181 : /************************************************************************/
182 : /* AppendString() */
183 : /************************************************************************/
184 :
185 410 : static void AppendString(char **ppszText, size_t *pnLength, size_t *pnMaxLength,
186 : const char *pszTextToAppend)
187 :
188 : {
189 410 : _GrowBuffer(*pnLength + strlen(pszTextToAppend) + 1, ppszText, pnMaxLength);
190 :
191 410 : strcat(*ppszText + *pnLength, pszTextToAppend);
192 410 : *pnLength += strlen(*ppszText + *pnLength);
193 410 : }
194 :
195 : /************************************************************************/
196 : /* AppendCoordinateList() */
197 : /************************************************************************/
198 :
199 81 : static void AppendCoordinateList(OGRLineString *poLine, char **ppszText,
200 : size_t *pnLength, size_t *pnMaxLength)
201 :
202 : {
203 81 : char szCoordinate[256] = {0};
204 81 : const bool b3D = CPL_TO_BOOL(wkbHasZ(poLine->getGeometryType()));
205 :
206 81 : *pnLength += strlen(*ppszText + *pnLength);
207 81 : _GrowBuffer(*pnLength + 20, ppszText, pnMaxLength);
208 :
209 81 : strcat(*ppszText + *pnLength, "<coordinates>");
210 81 : *pnLength += strlen(*ppszText + *pnLength);
211 :
212 322 : for (int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++)
213 : {
214 241 : MakeKMLCoordinate(szCoordinate, sizeof(szCoordinate),
215 : poLine->getX(iPoint), poLine->getY(iPoint),
216 : poLine->getZ(iPoint), b3D);
217 241 : _GrowBuffer(*pnLength + strlen(szCoordinate) + 1, ppszText,
218 : pnMaxLength);
219 :
220 241 : if (iPoint != 0)
221 160 : strcat(*ppszText + *pnLength, " ");
222 :
223 241 : strcat(*ppszText + *pnLength, szCoordinate);
224 241 : *pnLength += strlen(*ppszText + *pnLength);
225 : }
226 :
227 81 : _GrowBuffer(*pnLength + 20, ppszText, pnMaxLength);
228 81 : strcat(*ppszText + *pnLength, "</coordinates>");
229 81 : *pnLength += strlen(*ppszText + *pnLength);
230 81 : }
231 :
232 : /************************************************************************/
233 : /* OGR2KMLGeometryAppend() */
234 : /************************************************************************/
235 :
236 159 : static bool OGR2KMLGeometryAppend(OGRGeometry *poGeometry, char **ppszText,
237 : size_t *pnLength, size_t *pnMaxLength,
238 : char *szAltitudeMode)
239 :
240 : {
241 : /* -------------------------------------------------------------------- */
242 : /* 2D Point */
243 : /* -------------------------------------------------------------------- */
244 159 : if (poGeometry->getGeometryType() == wkbPoint)
245 : {
246 18 : OGRPoint *poPoint = poGeometry->toPoint();
247 :
248 18 : if (poPoint->getCoordinateDimension() == 0)
249 : {
250 0 : _GrowBuffer(*pnLength + 10, ppszText, pnMaxLength);
251 0 : strcat(*ppszText + *pnLength, "<Point/>");
252 0 : *pnLength += strlen(*ppszText + *pnLength);
253 : }
254 : else
255 : {
256 18 : char szCoordinate[256] = {0};
257 18 : MakeKMLCoordinate(szCoordinate, sizeof(szCoordinate),
258 : poPoint->getX(), poPoint->getY(), 0.0, false);
259 :
260 18 : _GrowBuffer(*pnLength + strlen(szCoordinate) + 60, ppszText,
261 : pnMaxLength);
262 :
263 18 : snprintf(*ppszText + *pnLength, *pnMaxLength - *pnLength,
264 : "<Point><coordinates>%s</coordinates></Point>",
265 : szCoordinate);
266 :
267 18 : *pnLength += strlen(*ppszText + *pnLength);
268 : }
269 : }
270 : /* -------------------------------------------------------------------- */
271 : /* 3D Point */
272 : /* -------------------------------------------------------------------- */
273 141 : else if (poGeometry->getGeometryType() == wkbPoint25D)
274 : {
275 11 : char szCoordinate[256] = {0};
276 11 : OGRPoint *poPoint = poGeometry->toPoint();
277 :
278 11 : MakeKMLCoordinate(szCoordinate, sizeof(szCoordinate), poPoint->getX(),
279 : poPoint->getY(), poPoint->getZ(), true);
280 :
281 11 : if (nullptr == szAltitudeMode)
282 : {
283 0 : _GrowBuffer(*pnLength + strlen(szCoordinate) + 70, ppszText,
284 : pnMaxLength);
285 :
286 0 : snprintf(*ppszText + *pnLength, *pnMaxLength - *pnLength,
287 : "<Point><coordinates>%s</coordinates></Point>",
288 : szCoordinate);
289 : }
290 : else
291 : {
292 11 : _GrowBuffer(*pnLength + strlen(szCoordinate) +
293 11 : strlen(szAltitudeMode) + 70,
294 : ppszText, pnMaxLength);
295 :
296 11 : snprintf(*ppszText + *pnLength, *pnMaxLength - *pnLength,
297 : "<Point>%s<coordinates>%s</coordinates></Point>",
298 : szAltitudeMode, szCoordinate);
299 : }
300 :
301 11 : *pnLength += strlen(*ppszText + *pnLength);
302 : }
303 : /* -------------------------------------------------------------------- */
304 : /* LineString and LinearRing */
305 : /* -------------------------------------------------------------------- */
306 198 : else if (poGeometry->getGeometryType() == wkbLineString ||
307 68 : poGeometry->getGeometryType() == wkbLineString25D)
308 : {
309 81 : const bool bRing = EQUAL(poGeometry->getGeometryName(), "LINEARRING");
310 :
311 81 : if (bRing)
312 24 : AppendString(ppszText, pnLength, pnMaxLength, "<LinearRing>");
313 : else
314 57 : AppendString(ppszText, pnLength, pnMaxLength, "<LineString>");
315 :
316 81 : if (nullptr != szAltitudeMode)
317 : {
318 81 : AppendString(ppszText, pnLength, pnMaxLength, szAltitudeMode);
319 : }
320 :
321 81 : AppendCoordinateList(poGeometry->toLineString(), ppszText, pnLength,
322 : pnMaxLength);
323 :
324 81 : if (bRing)
325 24 : AppendString(ppszText, pnLength, pnMaxLength, "</LinearRing>");
326 : else
327 57 : AppendString(ppszText, pnLength, pnMaxLength, "</LineString>");
328 : }
329 :
330 : /* -------------------------------------------------------------------- */
331 : /* Polygon */
332 : /* -------------------------------------------------------------------- */
333 86 : else if (poGeometry->getGeometryType() == wkbPolygon ||
334 37 : poGeometry->getGeometryType() == wkbPolygon25D)
335 : {
336 21 : OGRPolygon *poPolygon = poGeometry->toPolygon();
337 :
338 21 : AppendString(ppszText, pnLength, pnMaxLength, "<Polygon>");
339 :
340 21 : if (nullptr != szAltitudeMode)
341 : {
342 21 : AppendString(ppszText, pnLength, pnMaxLength, szAltitudeMode);
343 : }
344 :
345 21 : if (poPolygon->getExteriorRing() != nullptr)
346 : {
347 21 : AppendString(ppszText, pnLength, pnMaxLength, "<outerBoundaryIs>");
348 :
349 21 : if (!OGR2KMLGeometryAppend(poPolygon->getExteriorRing(), ppszText,
350 : pnLength, pnMaxLength, szAltitudeMode))
351 : {
352 0 : return false;
353 : }
354 21 : AppendString(ppszText, pnLength, pnMaxLength, "</outerBoundaryIs>");
355 : }
356 :
357 24 : for (int iRing = 0; iRing < poPolygon->getNumInteriorRings(); iRing++)
358 : {
359 3 : OGRLinearRing *poRing = poPolygon->getInteriorRing(iRing);
360 :
361 3 : AppendString(ppszText, pnLength, pnMaxLength, "<innerBoundaryIs>");
362 :
363 3 : if (!OGR2KMLGeometryAppend(poRing, ppszText, pnLength, pnMaxLength,
364 : szAltitudeMode))
365 : {
366 0 : return false;
367 : }
368 3 : AppendString(ppszText, pnLength, pnMaxLength, "</innerBoundaryIs>");
369 : }
370 :
371 21 : AppendString(ppszText, pnLength, pnMaxLength, "</Polygon>");
372 : }
373 :
374 : /* -------------------------------------------------------------------- */
375 : /* MultiPolygon */
376 : /* -------------------------------------------------------------------- */
377 28 : else if (wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon ||
378 21 : wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString ||
379 56 : wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint ||
380 7 : wkbFlatten(poGeometry->getGeometryType()) == wkbGeometryCollection)
381 : {
382 28 : OGRGeometryCollection *poGC = poGeometry->toGeometryCollection();
383 :
384 28 : AppendString(ppszText, pnLength, pnMaxLength, "<MultiGeometry>");
385 :
386 : // XXX - mloskot
387 : // if (NULL != szAltitudeMode)
388 : //{
389 : // AppendString( ppszText, pnLength, pnMaxLength, szAltitudeMode);
390 : //}
391 :
392 72 : for (auto &&poMember : poGC)
393 : {
394 44 : if (!OGR2KMLGeometryAppend(poMember, ppszText, pnLength,
395 : pnMaxLength, szAltitudeMode))
396 : {
397 0 : return false;
398 : }
399 : }
400 :
401 28 : AppendString(ppszText, pnLength, pnMaxLength, "</MultiGeometry>");
402 : }
403 : else
404 : {
405 0 : return false;
406 : }
407 :
408 159 : return true;
409 : }
410 :
411 : /************************************************************************/
412 : /* OGR_G_ExportEnvelopeToKMLTree() */
413 : /* */
414 : /* Export the envelope of a geometry as a KML:Box. */
415 : /************************************************************************/
416 :
417 : #if 0
418 : CPLXMLNode* OGR_G_ExportEnvelopeToKMLTree( OGRGeometryH hGeometry )
419 : {
420 : VALIDATE_POINTER1( hGeometry, "OGR_G_ExportEnvelopeToKMLTree", NULL );
421 :
422 : OGREnvelope sEnvelope;
423 :
424 : memset( &sEnvelope, 0, sizeof(sEnvelope) );
425 : ((OGRGeometry*)(hGeometry))->getEnvelope( &sEnvelope );
426 :
427 : if( sEnvelope.MinX == 0 && sEnvelope.MaxX == 0
428 : && sEnvelope.MaxX == 0 && sEnvelope.MaxY == 0 )
429 : {
430 : /* There is apparently a special way of representing a null box
431 : geometry ... we should use it here eventually. */
432 :
433 : return NULL;
434 : }
435 :
436 : CPLXMLNode* psBox = CPLCreateXMLNode( NULL, CXT_Element, "Box" );
437 :
438 : /* -------------------------------------------------------------------- */
439 : /* Add minxy coordinate. */
440 : /* -------------------------------------------------------------------- */
441 : CPLXMLNode* psCoord = CPLCreateXMLNode( psBox, CXT_Element, "coord" );
442 :
443 : char szCoordinate[256] = { 0 };
444 : MakeKMLCoordinate( szCoordinate, sEnvelope.MinX, sEnvelope.MinY, 0.0,
445 : false );
446 : char* pszY = strstr(szCoordinate,",") + 1;
447 : pszY[-1] = '\0';
448 :
449 : CPLCreateXMLElementAndValue( psCoord, "X", szCoordinate );
450 : CPLCreateXMLElementAndValue( psCoord, "Y", pszY );
451 :
452 : /* -------------------------------------------------------------------- */
453 : /* Add maxxy coordinate. */
454 : /* -------------------------------------------------------------------- */
455 : psCoord = CPLCreateXMLNode( psBox, CXT_Element, "coord" );
456 :
457 : MakeKMLCoordinate( szCoordinate, sEnvelope.MaxX, sEnvelope.MaxY, 0.0,
458 : false );
459 : pszY = strstr(szCoordinate,",") + 1;
460 : pszY[-1] = '\0';
461 :
462 : CPLCreateXMLElementAndValue( psCoord, "X", szCoordinate );
463 : CPLCreateXMLElementAndValue( psCoord, "Y", pszY );
464 :
465 : return psBox;
466 : }
467 : #endif
468 :
469 : /************************************************************************/
470 : /* OGR_G_ExportToKML() */
471 : /************************************************************************/
472 :
473 : /**
474 : * \brief Convert a geometry into KML format.
475 : *
476 : * The returned string should be freed with CPLFree() when no longer required.
477 : *
478 : * This method is the same as the C++ method OGRGeometry::exportToKML().
479 : *
480 : * @param hGeometry handle to the geometry.
481 : * @param pszAltitudeMode value to write in altitudeMode element, or NULL.
482 : * @return A KML fragment or NULL in case of error.
483 : */
484 :
485 91 : char *OGR_G_ExportToKML(OGRGeometryH hGeometry, const char *pszAltitudeMode)
486 : {
487 : char szAltitudeMode[128];
488 :
489 : // TODO - mloskot: Should we use VALIDATE_POINTER1 here?
490 91 : if (hGeometry == nullptr)
491 0 : return CPLStrdup("");
492 :
493 91 : size_t nMaxLength = 1;
494 91 : char *pszText = static_cast<char *>(CPLMalloc(nMaxLength));
495 91 : pszText[0] = '\0';
496 :
497 91 : if (nullptr != pszAltitudeMode && strlen(pszAltitudeMode) < 128 - (29 + 1))
498 : {
499 0 : snprintf(szAltitudeMode, sizeof(szAltitudeMode),
500 : "<altitudeMode>%s</altitudeMode>", pszAltitudeMode);
501 : }
502 : else
503 : {
504 91 : szAltitudeMode[0] = 0;
505 : }
506 :
507 91 : size_t nLength = 0;
508 91 : if (!OGR2KMLGeometryAppend(OGRGeometry::FromHandle(hGeometry), &pszText,
509 : &nLength, &nMaxLength, szAltitudeMode))
510 : {
511 0 : CPLFree(pszText);
512 0 : return nullptr;
513 : }
514 :
515 91 : return pszText;
516 : }
517 :
518 : /************************************************************************/
519 : /* OGR_G_ExportToKMLTree() */
520 : /************************************************************************/
521 :
522 : #if 0
523 : CPLXMLNode *OGR_G_ExportToKMLTree( OGRGeometryH hGeometry )
524 : {
525 : // TODO - mloskot: If passed geometry is null the pszText is non-null,
526 : // so the condition below is false.
527 : char *pszText = OGR_G_ExportToKML( hGeometry, NULL );
528 : if( pszText == NULL )
529 : return NULL;
530 :
531 : CPLXMLNode *psTree = CPLParseXMLString( pszText );
532 :
533 : CPLFree( pszText );
534 :
535 : return psTree;
536 : }
537 : #endif
|