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