Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: OpenGIS Simple Features Reference Implementation 4 : * Purpose: The OGRMultiSurface class. 5 : * Author: Even Rouault <even dot rouault at spatialys dot com> 6 : * 7 : ****************************************************************************** 8 : * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com> 9 : * 10 : * SPDX-License-Identifier: MIT 11 : ****************************************************************************/ 12 : 13 : #include "cpl_port.h" 14 : #include "ogr_geometry.h" 15 : 16 : #include <cstddef> 17 : 18 : #include "cpl_conv.h" 19 : #include "cpl_error.h" 20 : #include "ogr_api.h" 21 : #include "ogr_core.h" 22 : #include "ogr_p.h" 23 : 24 : /************************************************************************/ 25 : /* OGRMultiSurface( const OGRMultiSurface& ) */ 26 : /************************************************************************/ 27 : 28 : /** 29 : * \brief Copy constructor. 30 : */ 31 : 32 : OGRMultiSurface::OGRMultiSurface(const OGRMultiSurface &) = default; 33 : 34 : /************************************************************************/ 35 : /* operator=( const OGRMultiCurve&) */ 36 : /************************************************************************/ 37 : 38 : /** 39 : * \brief Assignment operator. 40 : */ 41 : 42 13 : OGRMultiSurface &OGRMultiSurface::operator=(const OGRMultiSurface &other) 43 : { 44 13 : if (this != &other) 45 : { 46 12 : OGRGeometryCollection::operator=(other); 47 : } 48 13 : return *this; 49 : } 50 : 51 : /************************************************************************/ 52 : /* clone() */ 53 : /************************************************************************/ 54 : 55 144 : OGRMultiSurface *OGRMultiSurface::clone() const 56 : 57 : { 58 144 : auto ret = new (std::nothrow) OGRMultiSurface(*this); 59 144 : if (ret) 60 : { 61 144 : if (ret->WkbSize() != WkbSize()) 62 : { 63 0 : delete ret; 64 0 : ret = nullptr; 65 : } 66 : } 67 144 : return ret; 68 : } 69 : 70 : /************************************************************************/ 71 : /* getGeometryType() */ 72 : /************************************************************************/ 73 : 74 3832 : OGRwkbGeometryType OGRMultiSurface::getGeometryType() const 75 : 76 : { 77 3832 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED)) 78 3041 : return wkbMultiSurfaceZM; 79 791 : else if (flags & OGR_G_MEASURED) 80 23 : return wkbMultiSurfaceM; 81 768 : else if (flags & OGR_G_3D) 82 107 : return wkbMultiSurfaceZ; 83 : else 84 661 : return wkbMultiSurface; 85 : } 86 : 87 : /************************************************************************/ 88 : /* getDimension() */ 89 : /************************************************************************/ 90 : 91 11 : int OGRMultiSurface::getDimension() const 92 : 93 : { 94 11 : return 2; 95 : } 96 : 97 : /************************************************************************/ 98 : /* getGeometryName() */ 99 : /************************************************************************/ 100 : 101 419 : const char *OGRMultiSurface::getGeometryName() const 102 : 103 : { 104 419 : return "MULTISURFACE"; 105 : } 106 : 107 : /************************************************************************/ 108 : /* isCompatibleSubType() */ 109 : /************************************************************************/ 110 : 111 : OGRBoolean 112 1415 : OGRMultiSurface::isCompatibleSubType(OGRwkbGeometryType eGeomType) const 113 : { 114 1415 : OGRwkbGeometryType eFlattenGeomType = wkbFlatten(eGeomType); 115 1415 : return eFlattenGeomType == wkbPolygon || 116 1415 : eFlattenGeomType == wkbCurvePolygon; 117 : } 118 : 119 : /************************************************************************/ 120 : /* importFromWkt() */ 121 : /* */ 122 : /* Instantiate from well known text format. */ 123 : /************************************************************************/ 124 : 125 1087 : OGRErr OGRMultiSurface::importFromWkt(const char **ppszInput) 126 : 127 : { 128 1087 : int bHasZ = FALSE; 129 1087 : int bHasM = FALSE; 130 1087 : bool bIsEmpty = false; 131 1087 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty); 132 1087 : flags = 0; 133 1087 : if (eErr != OGRERR_NONE) 134 5 : return eErr; 135 1082 : if (bHasZ) 136 172 : flags |= OGR_G_3D; 137 1082 : if (bHasM) 138 113 : flags |= OGR_G_MEASURED; 139 1082 : if (bIsEmpty) 140 73 : return OGRERR_NONE; 141 : 142 1009 : char szToken[OGR_WKT_TOKEN_MAX] = {}; 143 1009 : const char *pszInput = *ppszInput; 144 1009 : eErr = OGRERR_NONE; 145 : 146 : // Skip first '('. 147 1009 : pszInput = OGRWktReadToken(pszInput, szToken); 148 : 149 : /* ==================================================================== */ 150 : /* Read each surface in turn. Note that we try to reuse the same */ 151 : /* point list buffer from ring to ring to cut down on */ 152 : /* allocate/deallocate overhead. */ 153 : /* ==================================================================== */ 154 1009 : OGRRawPoint *paoPoints = nullptr; 155 1009 : int nMaxPoints = 0; 156 1009 : double *padfZ = nullptr; 157 : 158 322 : do 159 : { 160 : /* -------------------------------------------------------------------- 161 : */ 162 : /* Get the first token, which should be the geometry type. */ 163 : /* -------------------------------------------------------------------- 164 : */ 165 1331 : const char *pszInputBefore = pszInput; 166 1331 : pszInput = OGRWktReadToken(pszInput, szToken); 167 : 168 1331 : OGRSurface *poSurface = nullptr; 169 : 170 : /* -------------------------------------------------------------------- 171 : */ 172 : /* Do the import. */ 173 : /* -------------------------------------------------------------------- 174 : */ 175 1331 : if (EQUAL(szToken, "(")) 176 : { 177 1209 : OGRPolygon *poPolygon = new OGRPolygon(); 178 1209 : poSurface = poPolygon; 179 1209 : pszInput = pszInputBefore; 180 1209 : eErr = poPolygon->importFromWKTListOnly( 181 1209 : &pszInput, bHasZ, bHasM, paoPoints, nMaxPoints, padfZ); 182 : } 183 122 : else if (EQUAL(szToken, "EMPTY")) 184 : { 185 12 : poSurface = new OGRPolygon(); 186 : } 187 : // We accept POLYGON() but this is an extension to the BNF, also 188 : // accepted by PostGIS. 189 110 : else if (STARTS_WITH_CI(szToken, "POLYGON") || 190 106 : STARTS_WITH_CI(szToken, "CURVEPOLYGON")) 191 : { 192 95 : OGRGeometry *poGeom = nullptr; 193 95 : pszInput = pszInputBefore; 194 : eErr = 195 95 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom); 196 95 : if (poGeom == nullptr) 197 : { 198 1 : eErr = OGRERR_CORRUPT_DATA; 199 1 : break; 200 : } 201 94 : poSurface = poGeom->toSurface(); 202 : } 203 : else 204 : { 205 15 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s", 206 : szToken); 207 15 : eErr = OGRERR_CORRUPT_DATA; 208 15 : break; 209 : } 210 : 211 1315 : if (eErr == OGRERR_NONE) 212 1301 : eErr = addGeometryDirectly(poSurface); 213 1315 : if (eErr != OGRERR_NONE) 214 : { 215 14 : delete poSurface; 216 14 : break; 217 : } 218 : 219 : /* -------------------------------------------------------------------- 220 : */ 221 : /* Read the delimiter following the surface. */ 222 : /* -------------------------------------------------------------------- 223 : */ 224 1301 : pszInput = OGRWktReadToken(pszInput, szToken); 225 1301 : } while (szToken[0] == ',' && eErr == OGRERR_NONE); 226 : 227 1009 : CPLFree(paoPoints); 228 1009 : CPLFree(padfZ); 229 : 230 : /* -------------------------------------------------------------------- */ 231 : /* freak if we don't get a closing bracket. */ 232 : /* -------------------------------------------------------------------- */ 233 : 234 1009 : if (eErr != OGRERR_NONE) 235 30 : return eErr; 236 : 237 979 : if (szToken[0] != ')') 238 3 : return OGRERR_CORRUPT_DATA; 239 : 240 976 : *ppszInput = pszInput; 241 976 : return OGRERR_NONE; 242 : } 243 : 244 : /************************************************************************/ 245 : /* exportToWkt() */ 246 : /************************************************************************/ 247 : 248 85 : std::string OGRMultiSurface::exportToWkt(const OGRWktOptions &opts, 249 : OGRErr *err) const 250 : { 251 85 : OGRWktOptions optsModified(opts); 252 85 : optsModified.variant = wkbVariantIso; 253 85 : return exportToWktInternal(optsModified, err, "POLYGON"); 254 : } 255 : 256 : /************************************************************************/ 257 : /* hasCurveGeometry() */ 258 : /************************************************************************/ 259 : 260 1218 : OGRBoolean OGRMultiSurface::hasCurveGeometry(int bLookForNonLinear) const 261 : { 262 1218 : if (bLookForNonLinear) 263 23 : return OGRGeometryCollection::hasCurveGeometry(TRUE); 264 1195 : return TRUE; 265 : } 266 : 267 : /************************************************************************/ 268 : /* PointOnSurface() */ 269 : /************************************************************************/ 270 : 271 : /** \brief This method relates to the SFCOM 272 : * IMultiSurface::get_PointOnSurface() method. 273 : * 274 : * NOTE: Only implemented when GEOS included in build. 275 : * 276 : * @param poPoint point to be set with an internal point. 277 : * 278 : * @return OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise. 279 : */ 280 : 281 0 : OGRErr OGRMultiSurface::PointOnSurface(OGRPoint *poPoint) const 282 : { 283 0 : return PointOnSurfaceInternal(poPoint); 284 : } 285 : 286 : /************************************************************************/ 287 : /* CastToMultiPolygon() */ 288 : /************************************************************************/ 289 : 290 : /** 291 : * \brief Cast to multipolygon. 292 : * 293 : * This method should only be called if the multisurface actually only contains 294 : * instances of OGRPolygon. This can be verified if hasCurveGeometry(TRUE) 295 : * returns FALSE. It is not intended to approximate curve polygons. For that 296 : * use getLinearGeometry(). 297 : * 298 : * The passed in geometry is consumed and a new one returned (or NULL in case 299 : * of failure). 300 : * 301 : * @param poMS the input geometry - ownership is passed to the method. 302 : * @return new geometry. 303 : */ 304 : 305 62 : OGRMultiPolygon *OGRMultiSurface::CastToMultiPolygon(OGRMultiSurface *poMS) 306 : { 307 128 : for (auto &&poSubGeom : *poMS) 308 : { 309 66 : poSubGeom = OGRSurface::CastToPolygon(poSubGeom); 310 66 : if (poSubGeom == nullptr) 311 : { 312 0 : delete poMS; 313 0 : return nullptr; 314 : } 315 : } 316 : 317 62 : OGRMultiPolygon *poMP = new OGRMultiPolygon(); 318 62 : TransferMembersAndDestroy(poMS, poMP); 319 62 : return poMP; 320 : }