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