LCOV - code coverage report
Current view: top level - ogr - ogr2kmlgeometry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 106 142 74.6 %
Date: 2024-11-21 22:18:42 Functions: 6 6 100.0 %

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

Generated by: LCOV version 1.14