LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mssqlspatial - ogrmssqlgeometryvalidator.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 251 0.0 %
Date: 2025-09-10 17:48:50 Functions: 0 30 0.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  MSSQL Spatial driver
       4             :  * Purpose:  Implements OGRMSSQLGeometryValidator class to create valid
       5             :  *SqlGeometries. Author:   Tamas Szekeres, szekerest at gmail.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010, Tamas Szekeres
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_conv.h"
      14             : #include "ogr_mssqlspatial.h"
      15             : 
      16             : /************************************************************************/
      17             : /*                   OGRMSSQLGeometryValidator()                        */
      18             : /************************************************************************/
      19             : 
      20           0 : OGRMSSQLGeometryValidator::OGRMSSQLGeometryValidator(const OGRGeometry *poGeom,
      21           0 :                                                      int geomColumnType)
      22             :     : poOriginalGeometry(poGeom), nGeomColumnType(geomColumnType),
      23           0 :       bIsValid(IsValid(poGeom))
      24             : {
      25           0 : }
      26             : 
      27             : /************************************************************************/
      28             : /*                         IsValidLatLon()                             */
      29             : /************************************************************************/
      30             : 
      31           0 : static double MakeValidLatitude(double latitude)
      32             : {
      33           0 :     if (latitude < -90)
      34           0 :         return -90;
      35             : 
      36           0 :     if (latitude > 90.0)
      37           0 :         return 90.0;
      38             : 
      39           0 :     return latitude;
      40             : }
      41             : 
      42           0 : static double MakeValidLongitude(double longitude)
      43             : {
      44           0 :     if (longitude < -15069.0)
      45           0 :         return -15069.0;
      46             : 
      47           0 :     if (longitude > 15069.0)
      48           0 :         return 15069.0;
      49             : 
      50           0 :     return longitude;
      51             : }
      52             : 
      53           0 : bool OGRMSSQLGeometryValidator::IsValidLatLon(double longitude, double latitude)
      54             : {
      55           0 :     if (MakeValidLatitude(latitude) != latitude)
      56             :     {
      57           0 :         if (poValidGeometry == nullptr)
      58           0 :             CPLError(CE_Warning, CPLE_NotSupported,
      59             :                      "Latitude values must be between -90 and 90 degrees");
      60           0 :         return false;
      61             :     }
      62           0 :     if (MakeValidLongitude(longitude) != longitude)
      63             :     {
      64           0 :         if (poValidGeometry == nullptr)
      65           0 :             CPLError(
      66             :                 CE_Warning, CPLE_NotSupported,
      67             :                 "Longitude values must be between -15069 and 15069 degrees");
      68           0 :         return false;
      69             :     }
      70           0 :     return true;
      71             : }
      72             : 
      73             : /************************************************************************/
      74             : /*                         IsValidCircularZ()                           */
      75             : /************************************************************************/
      76             : 
      77           0 : bool OGRMSSQLGeometryValidator::IsValidCircularZ(double z1, double z2)
      78             : {
      79           0 :     if (z1 != z2)
      80             :     {
      81           0 :         if (poValidGeometry == nullptr)
      82           0 :             CPLError(CE_Warning, CPLE_NotSupported,
      83             :                      "Circular arc segments with Z values must have equal Z "
      84             :                      "value for all 3 points");
      85           0 :         return false;
      86             :     }
      87           0 :     return true;
      88             : }
      89             : 
      90             : /************************************************************************/
      91             : /*                         IsValidPolygonRingCount()                    */
      92             : /************************************************************************/
      93             : 
      94           0 : bool OGRMSSQLGeometryValidator::IsValidPolygonRingCount(const OGRCurve *poGeom)
      95             : {
      96           0 :     if (poGeom->getNumPoints() < 4)
      97             :     {
      98           0 :         if (poValidGeometry == nullptr)
      99           0 :             CPLError(
     100             :                 CE_Warning, CPLE_NotSupported,
     101             :                 "Each ring of a polygon must contain at least four points");
     102           0 :         return false;
     103             :     }
     104           0 :     return true;
     105             : }
     106             : 
     107             : /************************************************************************/
     108             : /*                         IsValidPolygonRingClosed()                   */
     109             : /************************************************************************/
     110             : 
     111           0 : bool OGRMSSQLGeometryValidator::IsValidPolygonRingClosed(const OGRCurve *poGeom)
     112             : {
     113           0 :     if (poGeom->get_IsClosed() == FALSE)
     114             :     {
     115           0 :         if (poValidGeometry == nullptr)
     116           0 :             CPLError(CE_Warning, CPLE_NotSupported,
     117             :                      "Each ring of a polygon must have the same start and end "
     118             :                      "points.");
     119           0 :         return false;
     120             :     }
     121           0 :     return true;
     122             : }
     123             : 
     124             : /************************************************************************/
     125             : /*                         ValidatePoint()                              */
     126             : /************************************************************************/
     127             : 
     128           0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRPoint *poGeom)
     129             : {
     130           0 :     if (poGeom->IsEmpty())
     131           0 :         return true;
     132             : 
     133           0 :     if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     134           0 :         return IsValidLatLon(poGeom->getX(), poGeom->getY());
     135             : 
     136           0 :     return true;
     137             : }
     138             : 
     139           0 : void OGRMSSQLGeometryValidator::MakeValid(OGRPoint *poGeom)
     140             : {
     141           0 :     if (poGeom->IsEmpty())
     142           0 :         return;
     143             : 
     144           0 :     if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     145             :     {
     146           0 :         poGeom->setX(MakeValidLongitude(poGeom->getX()));
     147           0 :         poGeom->setY(MakeValidLatitude(poGeom->getY()));
     148             :     }
     149             : }
     150             : 
     151             : /************************************************************************/
     152             : /*                     ValidateMultiPoint()                             */
     153             : /************************************************************************/
     154             : 
     155           0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRMultiPoint *poGeom)
     156             : {
     157           0 :     if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     158             :     {
     159           0 :         for (const auto point : *poGeom)
     160             :         {
     161           0 :             if (!IsValid(point))
     162           0 :                 return false;
     163             :         }
     164             :     }
     165           0 :     return true;
     166             : }
     167             : 
     168           0 : void OGRMSSQLGeometryValidator::MakeValid(OGRMultiPoint *poGeom)
     169             : {
     170           0 :     if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     171             :     {
     172           0 :         for (auto point : *poGeom)
     173             :         {
     174           0 :             MakeValid(point);
     175             :         }
     176             :     }
     177           0 : }
     178             : 
     179             : /************************************************************************/
     180             : /*                         ValidateSimpleCurve()                        */
     181             : /************************************************************************/
     182             : 
     183           0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRSimpleCurve *poGeom)
     184             : {
     185           0 :     if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     186             :     {
     187           0 :         const int numPoints = poGeom->getNumPoints();
     188           0 :         for (int i = 0; i < numPoints; i++)
     189             :         {
     190           0 :             if (!IsValidLatLon(poGeom->getX(i), poGeom->getY(i)))
     191           0 :                 return false;
     192             :         }
     193             :     }
     194           0 :     return true;
     195             : }
     196             : 
     197           0 : void OGRMSSQLGeometryValidator::MakeValid(OGRSimpleCurve *poGeom)
     198             : {
     199           0 :     if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     200             :     {
     201           0 :         const int numPoints = poGeom->getNumPoints();
     202           0 :         for (int i = 0; i < numPoints; i++)
     203             :         {
     204           0 :             poGeom->setPoint(i, MakeValidLongitude(poGeom->getX(i)),
     205             :                              MakeValidLatitude(poGeom->getY(i)));
     206             :         }
     207             :     }
     208           0 : }
     209             : 
     210             : /************************************************************************/
     211             : /*                         ValidateCircularString()                     */
     212             : /************************************************************************/
     213             : 
     214           0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRCircularString *poGeom)
     215             : {
     216           0 :     if (!IsValid(poGeom->toSimpleCurve()))
     217           0 :         return false;
     218             : 
     219           0 :     if (poGeom->Is3D())
     220             :     {
     221           0 :         const int numPoints = poGeom->getNumPoints();
     222           0 :         for (int i = 1; i < numPoints; i++)
     223             :         {
     224           0 :             if (!IsValidCircularZ(poGeom->getZ(i), poGeom->getZ(0)))
     225             :             {
     226           0 :                 return false;
     227             :             }
     228             :         }
     229             :     }
     230           0 :     return true;
     231             : }
     232             : 
     233           0 : void OGRMSSQLGeometryValidator::MakeValid(OGRCircularString *poGeom)
     234             : {
     235           0 :     MakeValid(poGeom->toSimpleCurve());
     236             : 
     237           0 :     if (poGeom->Is3D())
     238             :     {
     239           0 :         const int numPoints = poGeom->getNumPoints();
     240           0 :         for (int i = 1; i < numPoints; i++)
     241             :         {
     242           0 :             poGeom->setZ(i, poGeom->getZ(0));
     243             :         }
     244             :     }
     245           0 : }
     246             : 
     247             : /************************************************************************/
     248             : /*                         ValidateCompoundCurve()                      */
     249             : /************************************************************************/
     250             : 
     251           0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRCompoundCurve *poGeom)
     252             : {
     253           0 :     for (const auto poCurve : *poGeom)
     254             :     {
     255           0 :         switch (wkbFlatten(poCurve->getGeometryType()))
     256             :         {
     257           0 :             case wkbLineString:
     258           0 :                 if (!IsValid(poCurve->toLineString()))
     259           0 :                     return false;
     260           0 :                 break;
     261             : 
     262           0 :             case wkbCircularString:
     263           0 :                 if (!IsValid(poCurve->toCircularString()))
     264           0 :                     return false;
     265           0 :                 break;
     266             : 
     267           0 :             default:
     268           0 :                 break;
     269             :         }
     270             :     }
     271           0 :     return true;
     272             : }
     273             : 
     274           0 : void OGRMSSQLGeometryValidator::MakeValid(OGRCompoundCurve *poGeom)
     275             : {
     276           0 :     for (auto poCurve : *poGeom)
     277             :     {
     278           0 :         switch (wkbFlatten(poCurve->getGeometryType()))
     279             :         {
     280           0 :             case wkbLineString:
     281           0 :                 MakeValid(poCurve->toLineString());
     282           0 :                 break;
     283             : 
     284           0 :             case wkbCircularString:
     285           0 :                 MakeValid(poCurve->toCircularString());
     286           0 :                 break;
     287             : 
     288           0 :             default:
     289           0 :                 break;
     290             :         }
     291             :     }
     292           0 : }
     293             : 
     294             : /************************************************************************/
     295             : /*                     ValidateMultiLineString()                        */
     296             : /************************************************************************/
     297             : 
     298           0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRMultiLineString *poGeom)
     299             : {
     300           0 :     if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     301             :     {
     302           0 :         for (const auto part : *poGeom)
     303             :         {
     304           0 :             if (!IsValid(part))
     305           0 :                 return false;
     306             :         }
     307             :     }
     308           0 :     return true;
     309             : }
     310             : 
     311           0 : void OGRMSSQLGeometryValidator::MakeValid(OGRMultiLineString *poGeom)
     312             : {
     313           0 :     if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
     314             :     {
     315           0 :         for (auto part : *poGeom)
     316             :         {
     317           0 :             MakeValid(part);
     318             :         }
     319             :     }
     320           0 : }
     321             : 
     322             : /************************************************************************/
     323             : /*                         ValidatePolygon()                            */
     324             : /************************************************************************/
     325             : 
     326           0 : void OGRMSSQLGeometryValidator::MakeValid(OGRPolygon *poGeom)
     327             : {
     328           0 :     OGRMSSQLGeometryValidator::MakeValid(poGeom->toCurvePolygon());
     329             : 
     330           0 :     poGeom->closeRings();
     331           0 : }
     332             : 
     333             : /************************************************************************/
     334             : /*                         ValidateCurvePolygon()                       */
     335             : /************************************************************************/
     336             : 
     337           0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRCurvePolygon *poGeom)
     338             : {
     339           0 :     if (poGeom->IsEmpty())
     340           0 :         return true;
     341             : 
     342           0 :     for (const auto part : *poGeom)
     343             :     {
     344           0 :         if (!IsValid(part))
     345           0 :             return false;
     346             : 
     347           0 :         if (!IsValidPolygonRingCount(part))
     348           0 :             return false;
     349             : 
     350           0 :         if (!IsValidPolygonRingClosed(part))
     351           0 :             return false;
     352             :     }
     353             : 
     354           0 :     return true;
     355             : }
     356             : 
     357           0 : void OGRMSSQLGeometryValidator::MakeValid(OGRCurvePolygon *poGeom)
     358             : {
     359           0 :     if (poGeom->IsEmpty())
     360           0 :         return;
     361             : 
     362           0 :     for (auto part : *poGeom)
     363             :     {
     364           0 :         MakeValid(part);
     365             :     }
     366             : }
     367             : 
     368             : /************************************************************************/
     369             : /*                         ValidateMultiPolygon()                       */
     370             : /************************************************************************/
     371             : 
     372           0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRMultiPolygon *poGeom)
     373             : {
     374           0 :     for (const auto part : *poGeom)
     375             :     {
     376           0 :         if (!IsValid(part))
     377           0 :             return false;
     378             :     }
     379           0 :     return true;
     380             : }
     381             : 
     382           0 : void OGRMSSQLGeometryValidator::MakeValid(OGRMultiPolygon *poGeom)
     383             : {
     384           0 :     for (auto part : *poGeom)
     385             :     {
     386           0 :         MakeValid(part);
     387             :     }
     388           0 : }
     389             : 
     390             : /************************************************************************/
     391             : /*                     ValidateGeometryCollection()                     */
     392             : /************************************************************************/
     393             : 
     394           0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRGeometryCollection *poGeom)
     395             : {
     396           0 :     for (const auto part : *poGeom)
     397             :     {
     398           0 :         if (!IsValid(part))
     399           0 :             return false;
     400             :     }
     401           0 :     return true;
     402             : }
     403             : 
     404           0 : void OGRMSSQLGeometryValidator::MakeValid(OGRGeometryCollection *poGeom)
     405             : {
     406           0 :     for (auto part : *poGeom)
     407             :     {
     408           0 :         MakeValid(part);
     409             :     }
     410           0 : }
     411             : 
     412             : /************************************************************************/
     413             : /*                         ValidateGeometry()                           */
     414             : /************************************************************************/
     415             : 
     416           0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRGeometry *poGeom)
     417             : {
     418           0 :     if (!poGeom)
     419           0 :         return false;
     420             : 
     421           0 :     switch (wkbFlatten(poGeom->getGeometryType()))
     422             :     {
     423           0 :         case wkbPoint:
     424           0 :             return IsValid(poGeom->toPoint());
     425           0 :         case wkbLineString:
     426           0 :             return IsValid(poGeom->toSimpleCurve());
     427           0 :         case wkbPolygon:
     428           0 :             return IsValid(poGeom->toPolygon());
     429           0 :         case wkbCurvePolygon:
     430           0 :             return IsValid(poGeom->toCurvePolygon());
     431           0 :         case wkbMultiPoint:
     432           0 :             return IsValid(poGeom->toMultiPoint());
     433           0 :         case wkbMultiLineString:
     434           0 :             return IsValid(poGeom->toMultiLineString());
     435           0 :         case wkbCircularString:
     436           0 :             return IsValid(poGeom->toCircularString());
     437           0 :         case wkbCompoundCurve:
     438           0 :             return IsValid(poGeom->toCompoundCurve());
     439           0 :         case wkbMultiPolygon:
     440           0 :             return IsValid(poGeom->toMultiPolygon());
     441           0 :         case wkbGeometryCollection:
     442           0 :             return IsValid(poGeom->toGeometryCollection());
     443           0 :         default:
     444           0 :             break;
     445             :     }
     446           0 :     return false;
     447             : }
     448             : 
     449           0 : void OGRMSSQLGeometryValidator::MakeValid(OGRGeometry *poGeom)
     450             : {
     451           0 :     if (!poGeom)
     452           0 :         return;
     453             : 
     454           0 :     switch (wkbFlatten(poGeom->getGeometryType()))
     455             :     {
     456           0 :         case wkbPoint:
     457           0 :             MakeValid(poGeom->toPoint());
     458           0 :             break;
     459           0 :         case wkbLineString:
     460           0 :             MakeValid(poGeom->toSimpleCurve());
     461           0 :             break;
     462           0 :         case wkbPolygon:
     463           0 :             MakeValid(poGeom->toPolygon());
     464           0 :             break;
     465           0 :         case wkbCurvePolygon:
     466           0 :             MakeValid(poGeom->toCurvePolygon());
     467           0 :             break;
     468           0 :         case wkbMultiPoint:
     469           0 :             MakeValid(poGeom->toMultiPoint());
     470           0 :             break;
     471           0 :         case wkbMultiLineString:
     472           0 :             MakeValid(poGeom->toMultiLineString());
     473           0 :             break;
     474           0 :         case wkbCircularString:
     475           0 :             MakeValid(poGeom->toCircularString());
     476           0 :             break;
     477           0 :         case wkbCompoundCurve:
     478           0 :             MakeValid(poGeom->toCompoundCurve());
     479           0 :             break;
     480           0 :         case wkbMultiPolygon:
     481           0 :             MakeValid(poGeom->toMultiPolygon());
     482           0 :             break;
     483           0 :         case wkbGeometryCollection:
     484           0 :             MakeValid(poGeom->toGeometryCollection());
     485           0 :             break;
     486           0 :         default:
     487           0 :             break;
     488             :     }
     489             : }
     490             : 
     491           0 : bool OGRMSSQLGeometryValidator::ValidateGeometry(OGRGeometry *poGeom)
     492             : {
     493           0 :     poValidGeometry.reset();
     494           0 :     if (!IsValid(poGeom))
     495             :     {
     496           0 :         poValidGeometry.reset(poGeom->clone());
     497           0 :         MakeValid(poValidGeometry.get());
     498           0 :         return false;
     499             :     }
     500           0 :     return true;
     501             : }
     502             : 
     503             : /************************************************************************/
     504             : /*                      GetValidGeometryRef()                           */
     505             : /************************************************************************/
     506           0 : const OGRGeometry *OGRMSSQLGeometryValidator::GetValidGeometryRef() const
     507             : {
     508           0 :     if (bIsValid || poOriginalGeometry == nullptr)
     509           0 :         return poOriginalGeometry;
     510             : 
     511           0 :     if (poValidGeometry)
     512             :     {
     513           0 :         CPLError(CE_Warning, CPLE_NotSupported,
     514             :                  "Invalid geometry has been converted from %s to %s.",
     515           0 :                  poOriginalGeometry->getGeometryName(),
     516           0 :                  poValidGeometry->getGeometryName());
     517             :     }
     518             :     else
     519             :     {
     520           0 :         CPLError(CE_Warning, CPLE_NotSupported,
     521             :                  "Invalid geometry has been converted from %s to null.",
     522           0 :                  poOriginalGeometry->getGeometryName());
     523             :     }
     524             : 
     525           0 :     return poValidGeometry.get();
     526             : }

Generated by: LCOV version 1.14