1 /******************************************************************************
2 * $Id: ogr2gmlgeometry.cpp 12008 2007-08-30 15:44:44Z warmerdam $
3 *
4 * Project: GML Translator
5 * Purpose: Code to translate OGRGeometry to GML string representation.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
7 *
8 ******************************************************************************
9 * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.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 "../port/cpl_minixml.h"
43 #include "../ogr/ogr_geometry.h"
44 #include "../ogr/ogr_api.h"
45 #include "../ogr/ogr_p.h"
46 #include "../port/cpl_error.h"
47 #include "../port/cpl_conv.h"
48
49 /************************************************************************/
50 /* MakeGMLCoordinate() */
51 /************************************************************************/
52
MakeGMLCoordinate(char * pszTarget,double x,double y,double z,int b3D)53 static void MakeGMLCoordinate( char *pszTarget,
54 double x, double y, double z, int b3D )
55
56 {
57 OGRMakeWktCoordinate( pszTarget, x, y, z, b3D ? 3 : 2 );
58 while( *pszTarget != '\0' )
59 {
60 if( *pszTarget == ' ' )
61 *pszTarget = ',';
62 pszTarget++;
63 }
64
65 #ifdef notdef
66 if( !b3D )
67 {
68 if( x == (int) x && y == (int) y )
69 sprintf( pszTarget, "%d,%d", (int) x, (int) y );
70 else if( fabs(x) < 370 && fabs(y) < 370 )
71 sprintf( pszTarget, "%.16g,%.16g", x, y );
72 else if( fabs(x) > 100000000.0 || fabs(y) > 100000000.0 )
73 sprintf( pszTarget, "%.16g,%.16g", x, y );
74 else
75 sprintf( pszTarget, "%.3f,%.3f", x, y );
76 }
77 else
78 {
79 if( x == (int) x && y == (int) y && z == (int) z )
80 sprintf( pszTarget, "%d,%d,%d", (int) x, (int) y, (int) z );
81 else if( fabs(x) < 370 && fabs(y) < 370 )
82 sprintf( pszTarget, "%.16g,%.16g,%.16g", x, y, z );
83 else if( fabs(x) > 100000000.0 || fabs(y) > 100000000.0
84 || fabs(z) > 100000000.0 )
85 sprintf( pszTarget, "%.16g,%.16g,%.16g", x, y, z );
86 else
87 sprintf( pszTarget, "%.3f,%.3f,%.3f", x, y, z );
88 }
89 #endif
90 }
91
92 /************************************************************************/
93 /* _GrowBuffer() */
94 /************************************************************************/
95
_GrowBuffer(int nNeeded,char ** ppszText,int * pnMaxLength)96 static void _GrowBuffer( int nNeeded, char **ppszText, int *pnMaxLength )
97
98 {
99 if( nNeeded+1 >= *pnMaxLength )
100 {
101 *pnMaxLength = MAX(*pnMaxLength * 2,nNeeded+1);
102 *ppszText = (char *) CPLRealloc(*ppszText, *pnMaxLength);
103 }
104 }
105
106 /************************************************************************/
107 /* AppendString() */
108 /************************************************************************/
109
AppendString(char ** ppszText,int * pnLength,int * pnMaxLength,const char * pszTextToAppend)110 static void AppendString( char **ppszText, int *pnLength, int *pnMaxLength,
111 const char *pszTextToAppend )
112
113 {
114 _GrowBuffer( *pnLength + strlen(pszTextToAppend) + 1,
115 ppszText, pnMaxLength );
116
117 strcat( *ppszText + *pnLength, pszTextToAppend );
118 *pnLength += strlen( *ppszText + *pnLength );
119 }
120
121
122 /************************************************************************/
123 /* AppendCoordinateList() */
124 /************************************************************************/
125
AppendCoordinateList(OGRLineString * poLine,char ** ppszText,int * pnLength,int * pnMaxLength)126 static void AppendCoordinateList( OGRLineString *poLine,
127 char **ppszText, int *pnLength,
128 int *pnMaxLength )
129
130 {
131 char szCoordinate[256];
132 int b3D = (poLine->getGeometryType() & wkb25DBit);
133
134 *pnLength += strlen(*ppszText + *pnLength);
135 _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength );
136
137 strcat( *ppszText + *pnLength, "<gml:coordinates>" );
138 *pnLength += strlen(*ppszText + *pnLength);
139
140
141 for( int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++ )
142 {
143 MakeGMLCoordinate( szCoordinate,
144 poLine->getX(iPoint),
145 poLine->getY(iPoint),
146 poLine->getZ(iPoint),
147 b3D );
148 _GrowBuffer( *pnLength + strlen(szCoordinate)+1,
149 ppszText, pnMaxLength );
150
151 if( iPoint != 0 )
152 strcat( *ppszText + *pnLength, " " );
153
154 strcat( *ppszText + *pnLength, szCoordinate );
155 *pnLength += strlen(*ppszText + *pnLength);
156 }
157
158 _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength );
159 strcat( *ppszText + *pnLength, "</gml:coordinates>" );
160 *pnLength += strlen(*ppszText + *pnLength);
161 }
162
163 /************************************************************************/
164 /* OGR2GMLGeometryAppend() */
165 /************************************************************************/
166
OGR2GMLGeometryAppend(OGRGeometry * poGeometry,char ** ppszText,int * pnLength,int * pnMaxLength,int bIsSubGeometry)167 static int OGR2GMLGeometryAppend( OGRGeometry *poGeometry,
168 char **ppszText, int *pnLength,
169 int *pnMaxLength,
170 int bIsSubGeometry )
171
172 {
173
174 /* -------------------------------------------------------------------- */
175 /* Check for Spatial Reference System attached to given geometry */
176 /* -------------------------------------------------------------------- */
177
178 // Buffer for srsName attribute (srsName="...")
179 char szSrsName[30] = { 0 };
180 int nSrsNameLength = 0;
181 OGRBoolean bAddSrsName = FALSE;
182
183 const OGRSpatialReference* poSRS = NULL;
184 poSRS = poGeometry->getSpatialReference();
185
186 if( NULL != poSRS && !bIsSubGeometry )
187 {
188 const char* pszAuthName = NULL;
189 const char* pszAuthCode = NULL;
190 const char* pszTarget = NULL;
191
192 if (poSRS->IsProjected())
193 pszTarget = "PROJCS";
194 else
195 pszTarget = "GEOGCS";
196
197 pszAuthName = poSRS->GetAuthorityName( pszTarget );
198 if( NULL != pszAuthName )
199 {
200 if( EQUAL( pszAuthName, "EPSG" ) )
201 {
202 pszAuthCode = poSRS->GetAuthorityCode( pszTarget );
203 if( NULL != pszAuthCode )
204 {
205 sprintf( szSrsName, " srsName=\"%s:%s\"",
206 pszAuthName, pszAuthCode );
207
208 /* Yes, attach srsName attribute per geometry. */
209 bAddSrsName = TRUE;
210 }
211 }
212 }
213 }
214
215 /* Include srsName attribute in new buffer allocation. */
216 if( bAddSrsName )
217 {
218 nSrsNameLength = strlen(szSrsName);
219 }
220
221 /* -------------------------------------------------------------------- */
222 /* 2D Point */
223 /* -------------------------------------------------------------------- */
224 if( poGeometry->getGeometryType() == wkbPoint )
225 {
226 char szCoordinate[256];
227 OGRPoint *poPoint = (OGRPoint *) poGeometry;
228
229 MakeGMLCoordinate( szCoordinate,
230 poPoint->getX(), poPoint->getY(), 0.0, FALSE );
231
232 _GrowBuffer( *pnLength + strlen(szCoordinate) + 60 + nSrsNameLength,
233 ppszText, pnMaxLength );
234
235 sprintf( *ppszText + *pnLength,
236 "<gml:Point%s><gml:coordinates>%s</gml:coordinates></gml:Point>",
237 szSrsName, szCoordinate );
238
239 *pnLength += strlen( *ppszText + *pnLength );
240 }
241 /* -------------------------------------------------------------------- */
242 /* 3D Point */
243 /* -------------------------------------------------------------------- */
244 else if( poGeometry->getGeometryType() == wkbPoint25D )
245 {
246 char szCoordinate[256];
247 OGRPoint *poPoint = (OGRPoint *) poGeometry;
248
249 MakeGMLCoordinate( szCoordinate,
250 poPoint->getX(), poPoint->getY(), poPoint->getZ(),
251 TRUE );
252
253 _GrowBuffer( *pnLength + strlen(szCoordinate) + 70 + nSrsNameLength,
254 ppszText, pnMaxLength );
255
256 sprintf( *ppszText + *pnLength,
257 "<gml:Point%s><gml:coordinates>%s</gml:coordinates></gml:Point>",
258 szSrsName, szCoordinate );
259
260 *pnLength += strlen( *ppszText + *pnLength );
261 }
262
263 /* -------------------------------------------------------------------- */
264 /* LineString and LinearRing */
265 /* -------------------------------------------------------------------- */
266 else if( poGeometry->getGeometryType() == wkbLineString
267 || poGeometry->getGeometryType() == wkbLineString25D )
268 {
269 int bRing = EQUAL(poGeometry->getGeometryName(),"LINEARRING");
270
271 // Buffer for tag name + srsName attribute if set
272 const size_t nLineTagLength = 16;
273 char* pszLineTagName = NULL;
274 pszLineTagName = (char *) CPLMalloc( nLineTagLength + nSrsNameLength + 1 );
275
276 if( bRing )
277 {
278 sprintf( pszLineTagName, "<gml:LinearRing%s>", szSrsName );
279
280 AppendString( ppszText, pnLength, pnMaxLength,
281 pszLineTagName );
282 }
283 else
284 {
285 sprintf( pszLineTagName, "<gml:LineString%s>", szSrsName );
286
287 AppendString( ppszText, pnLength, pnMaxLength,
288 pszLineTagName );
289 }
290
291 // FREE TAG BUFFER
292 CPLFree( pszLineTagName );
293
294 AppendCoordinateList( (OGRLineString *) poGeometry,
295 ppszText, pnLength, pnMaxLength );
296
297 if( bRing )
298 AppendString( ppszText, pnLength, pnMaxLength,
299 "</gml:LinearRing>" );
300 else
301 AppendString( ppszText, pnLength, pnMaxLength,
302 "</gml:LineString>" );
303 }
304
305 /* -------------------------------------------------------------------- */
306 /* Polygon */
307 /* -------------------------------------------------------------------- */
308 else if( poGeometry->getGeometryType() == wkbPolygon
309 || poGeometry->getGeometryType() == wkbPolygon25D )
310 {
311 OGRPolygon *poPolygon = (OGRPolygon *) poGeometry;
312
313 // Buffer for polygon tag name + srsName attribute if set
314 const size_t nPolyTagLength = 13;
315 char* pszPolyTagName = NULL;
316 pszPolyTagName = (char *) CPLMalloc( nPolyTagLength + nSrsNameLength + 1 );
317
318 // Compose Polygon tag with or without srsName attribute
319 sprintf( pszPolyTagName, "<gml:Polygon%s>", szSrsName );
320
321 AppendString( ppszText, pnLength, pnMaxLength,
322 pszPolyTagName );
323
324 // FREE TAG BUFFER
325 CPLFree( pszPolyTagName );
326
327 // Don't add srsName to polygon rings
328
329 if( poPolygon->getExteriorRing() != NULL )
330 {
331 AppendString( ppszText, pnLength, pnMaxLength,
332 "<gml:outerBoundaryIs>" );
333
334 if( !OGR2GMLGeometryAppend( poPolygon->getExteriorRing(),
335 ppszText, pnLength, pnMaxLength,
336 TRUE ) )
337 {
338 return FALSE;
339 }
340
341 AppendString( ppszText, pnLength, pnMaxLength,
342 "</gml:outerBoundaryIs>" );
343 }
344
345 for( int iRing = 0; iRing < poPolygon->getNumInteriorRings(); iRing++ )
346 {
347 OGRLinearRing *poRing = poPolygon->getInteriorRing(iRing);
348
349 AppendString( ppszText, pnLength, pnMaxLength,
350 "<gml:innerBoundaryIs>" );
351
352 if( !OGR2GMLGeometryAppend( poRing, ppszText, pnLength,
353 pnMaxLength, TRUE ) )
354 return FALSE;
355
356 AppendString( ppszText, pnLength, pnMaxLength,
357 "</gml:innerBoundaryIs>" );
358 }
359
360 AppendString( ppszText, pnLength, pnMaxLength,
361 "</gml:Polygon>" );
362 }
363
364 /* -------------------------------------------------------------------- */
365 /* MultiPolygon */
366 /* -------------------------------------------------------------------- */
367 else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon
368 || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString
369 || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint
370 || wkbFlatten(poGeometry->getGeometryType()) == wkbGeometryCollection )
371 {
372 OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeometry;
373 int iMember;
374 const char *pszElemClose = NULL;
375 const char *pszMemberElem = NULL;
376
377 // Buffer for opening tag + srsName attribute
378 char* pszElemOpen = NULL;
379
380 if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon )
381 {
382 pszElemOpen = (char *) CPLMalloc( 13 + nSrsNameLength + 1 );
383 sprintf( pszElemOpen, "MultiPolygon%s>", szSrsName );
384
385 pszElemClose = "MultiPolygon>";
386 pszMemberElem = "polygonMember>";
387 }
388 else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString )
389 {
390 pszElemOpen = (char *) CPLMalloc( 16 + nSrsNameLength + 1 );
391 sprintf( pszElemOpen, "MultiLineString%s>", szSrsName );
392
393 pszElemClose = "MultiLineString>";
394 pszMemberElem = "lineStringMember>";
395 }
396 else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint )
397 {
398 pszElemOpen = (char *) CPLMalloc( 11 + nSrsNameLength + 1 );
399 sprintf( pszElemOpen, "MultiPoint%s>", szSrsName );
400
401 pszElemClose = "MultiPoint>";
402 pszMemberElem = "pointMember>";
403 }
404 else
405 {
406 pszElemOpen = (char *) CPLMalloc( 19 + nSrsNameLength + 1 );
407 sprintf( pszElemOpen, "GeometryCollection%s>", szSrsName );
408
409 pszElemClose = "GeometryCollection>";
410 pszMemberElem = "geometryMember>";
411 }
412
413 AppendString( ppszText, pnLength, pnMaxLength, "<gml:" );
414 AppendString( ppszText, pnLength, pnMaxLength, pszElemOpen );
415
416 for( iMember = 0; iMember < poGC->getNumGeometries(); iMember++)
417 {
418 OGRGeometry *poMember = poGC->getGeometryRef( iMember );
419
420 AppendString( ppszText, pnLength, pnMaxLength, "<gml:" );
421 AppendString( ppszText, pnLength, pnMaxLength, pszMemberElem );
422
423 if( !OGR2GMLGeometryAppend( poMember,
424 ppszText, pnLength, pnMaxLength,
425 TRUE ) )
426 {
427 return FALSE;
428 }
429
430 AppendString( ppszText, pnLength, pnMaxLength, "</gml:" );
431 AppendString( ppszText, pnLength, pnMaxLength, pszMemberElem );
432 }
433
434 AppendString( ppszText, pnLength, pnMaxLength, "</gml:" );
435 AppendString( ppszText, pnLength, pnMaxLength, pszElemClose );
436
437 // FREE TAG BUFFER
438 CPLFree( pszElemOpen );
439 }
440 else
441 {
442 return FALSE;
443 }
444
445 return TRUE;
446 }
447
448 /************************************************************************/
449 /* OGR_G_ExportEnvelopeToGMLTree() */
450 /* */
451 /* Export the envelope of a geometry as a gml:Box. */
452 /************************************************************************/
453
OGR_G_ExportEnvelopeToGMLTree(OGRGeometryH hGeometry)454 CPLXMLNode *OGR_G_ExportEnvelopeToGMLTree( OGRGeometryH hGeometry )
455
456 {
457 CPLXMLNode *psBox, *psCoord;
458 OGREnvelope sEnvelope;
459 char szCoordinate[256];
460 char *pszY;
461
462 memset( &sEnvelope, 0, sizeof(sEnvelope) );
463 ((OGRGeometry *) hGeometry)->getEnvelope( &sEnvelope );
464
465 if( sEnvelope.MinX == 0 && sEnvelope.MaxX == 0
466 && sEnvelope.MaxX == 0 && sEnvelope.MaxY == 0 )
467 {
468 /* there is apparently a special way of representing a null box
469 geometry ... we should use it here eventually. */
470
471 return NULL;
472 }
473
474 psBox = CPLCreateXMLNode( NULL, CXT_Element, "gml:Box" );
475
476 /* -------------------------------------------------------------------- */
477 /* Add minxy coordinate. */
478 /* -------------------------------------------------------------------- */
479 psCoord = CPLCreateXMLNode( psBox, CXT_Element, "gml:coord" );
480
481 MakeGMLCoordinate( szCoordinate, sEnvelope.MinX, sEnvelope.MinY, 0.0,
482 FALSE );
483 pszY = strstr(szCoordinate,",") + 1;
484 pszY[-1] = '\0';
485
486 CPLCreateXMLElementAndValue( psCoord, "gml:X", szCoordinate );
487 CPLCreateXMLElementAndValue( psCoord, "gml:Y", pszY );
488
489 /* -------------------------------------------------------------------- */
490 /* Add maxxy coordinate. */
491 /* -------------------------------------------------------------------- */
492 psCoord = CPLCreateXMLNode( psBox, CXT_Element, "gml:coord" );
493
494 MakeGMLCoordinate( szCoordinate, sEnvelope.MaxX, sEnvelope.MaxY, 0.0,
495 FALSE );
496 pszY = strstr(szCoordinate,",") + 1;
497 pszY[-1] = '\0';
498
499 CPLCreateXMLElementAndValue( psCoord, "gml:X", szCoordinate );
500 CPLCreateXMLElementAndValue( psCoord, "gml:Y", pszY );
501
502 return psBox;
503 }
504
505 /************************************************************************/
506 /* OGR_G_ExportToGMLTree() */
507 /************************************************************************/
508
OGR_G_ExportToGMLTree(OGRGeometryH hGeometry)509 CPLXMLNode *OGR_G_ExportToGMLTree( OGRGeometryH hGeometry )
510
511 {
512 char *pszText;
513 CPLXMLNode *psTree;
514
515 pszText = OGR_G_ExportToGML( hGeometry );
516 if( pszText == NULL )
517 return NULL;
518
519 psTree = CPLParseXMLString( pszText );
520
521 CPLFree( pszText );
522
523 return psTree;
524 }
525
526 /************************************************************************/
527 /* OGR_G_ExportToGML() */
528 /************************************************************************/
529
OGR_G_ExportToGML(OGRGeometryH hGeometry)530 char *OGR_G_ExportToGML( OGRGeometryH hGeometry )
531
532 {
533 char *pszText;
534 int nLength = 0, nMaxLength = 1;
535
536 if( hGeometry == NULL )
537 return CPLStrdup( "" );
538
539 pszText = (char *) CPLMalloc(nMaxLength);
540 pszText[0] = '\0';
541
542 if( !OGR2GMLGeometryAppend( (OGRGeometry *) hGeometry, &pszText,
543 &nLength, &nMaxLength, FALSE ))
544 {
545 CPLFree( pszText );
546 return NULL;
547 }
548 else
549 return pszText;
550 }
551