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