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 143 : OGRMultiSurface *OGRMultiSurface::clone() const 56 : 57 : { 58 143 : auto ret = new (std::nothrow) OGRMultiSurface(*this); 59 143 : if (ret) 60 : { 61 143 : if (ret->WkbSize() != WkbSize()) 62 : { 63 0 : delete ret; 64 0 : ret = nullptr; 65 : } 66 : } 67 143 : return ret; 68 : } 69 : 70 : /************************************************************************/ 71 : /* getGeometryType() */ 72 : /************************************************************************/ 73 : 74 3837 : OGRwkbGeometryType OGRMultiSurface::getGeometryType() const 75 : 76 : { 77 3837 : if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED)) 78 3041 : return wkbMultiSurfaceZM; 79 796 : else if (flags & OGR_G_MEASURED) 80 23 : return wkbMultiSurfaceM; 81 773 : else if (flags & OGR_G_3D) 82 107 : return wkbMultiSurfaceZ; 83 : else 84 666 : 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 421 : const char *OGRMultiSurface::getGeometryName() const 102 : 103 : { 104 421 : return "MULTISURFACE"; 105 : } 106 : 107 : /************************************************************************/ 108 : /* isCompatibleSubType() */ 109 : /************************************************************************/ 110 : 111 1412 : bool OGRMultiSurface::isCompatibleSubType(OGRwkbGeometryType eGeomType) const 112 : { 113 1412 : OGRwkbGeometryType eFlattenGeomType = wkbFlatten(eGeomType); 114 1412 : return eFlattenGeomType == wkbPolygon || 115 1412 : eFlattenGeomType == wkbCurvePolygon; 116 : } 117 : 118 : /************************************************************************/ 119 : /* importFromWkt() */ 120 : /* */ 121 : /* Instantiate from well known text format. */ 122 : /************************************************************************/ 123 : 124 1122 : OGRErr OGRMultiSurface::importFromWkt(const char **ppszInput) 125 : 126 : { 127 1122 : int bHasZ = FALSE; 128 1122 : int bHasM = FALSE; 129 1122 : bool bIsEmpty = false; 130 1122 : OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty); 131 1122 : flags = 0; 132 1122 : if (eErr != OGRERR_NONE) 133 5 : return eErr; 134 1117 : if (bHasZ) 135 177 : flags |= OGR_G_3D; 136 1117 : if (bHasM) 137 113 : flags |= OGR_G_MEASURED; 138 1117 : if (bIsEmpty) 139 75 : return OGRERR_NONE; 140 : 141 1042 : char szToken[OGR_WKT_TOKEN_MAX] = {}; 142 1042 : const char *pszInput = *ppszInput; 143 1042 : eErr = OGRERR_NONE; 144 : 145 : // Skip first '('. 146 1042 : pszInput = OGRWktReadToken(pszInput, szToken); 147 : 148 : /* ==================================================================== */ 149 : /* Read each surface in turn. Note that we try to reuse the same */ 150 : /* point list buffer from ring to ring to cut down on */ 151 : /* allocate/deallocate overhead. */ 152 : /* ==================================================================== */ 153 1042 : OGRRawPoint *paoPoints = nullptr; 154 1042 : int nMaxPoints = 0; 155 1042 : double *padfZ = nullptr; 156 : 157 345 : do 158 : { 159 : /* -------------------------------------------------------------------- 160 : */ 161 : /* Get the first token, which should be the geometry type. */ 162 : /* -------------------------------------------------------------------- 163 : */ 164 1387 : const char *pszInputBefore = pszInput; 165 1387 : pszInput = OGRWktReadToken(pszInput, szToken); 166 : 167 1387 : OGRSurface *poSurface = nullptr; 168 : 169 : /* -------------------------------------------------------------------- 170 : */ 171 : /* Do the import. */ 172 : /* -------------------------------------------------------------------- 173 : */ 174 1387 : if (EQUAL(szToken, "(")) 175 : { 176 1264 : OGRPolygon *poPolygon = new OGRPolygon(); 177 1264 : poSurface = poPolygon; 178 1264 : pszInput = pszInputBefore; 179 1264 : eErr = poPolygon->importFromWKTListOnly( 180 1264 : &pszInput, bHasZ, bHasM, paoPoints, nMaxPoints, padfZ); 181 : } 182 123 : else if (EQUAL(szToken, "EMPTY")) 183 : { 184 12 : poSurface = new OGRPolygon(); 185 : } 186 : // We accept POLYGON() but this is an extension to the BNF, also 187 : // accepted by PostGIS. 188 111 : else if (STARTS_WITH_CI(szToken, "POLYGON") || 189 110 : STARTS_WITH_CI(szToken, "CURVEPOLYGON")) 190 : { 191 96 : OGRGeometry *poGeom = nullptr; 192 96 : pszInput = pszInputBefore; 193 : eErr = 194 96 : OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom); 195 96 : if (poGeom == nullptr) 196 : { 197 1 : eErr = OGRERR_CORRUPT_DATA; 198 1 : break; 199 : } 200 95 : poSurface = poGeom->toSurface(); 201 : } 202 : else 203 : { 204 15 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s", 205 : szToken); 206 15 : eErr = OGRERR_CORRUPT_DATA; 207 15 : break; 208 : } 209 : 210 1371 : if (eErr == OGRERR_NONE) 211 1357 : eErr = addGeometryDirectly(poSurface); 212 1371 : if (eErr != OGRERR_NONE) 213 : { 214 14 : delete poSurface; 215 14 : break; 216 : } 217 : 218 : /* -------------------------------------------------------------------- 219 : */ 220 : /* Read the delimiter following the surface. */ 221 : /* -------------------------------------------------------------------- 222 : */ 223 1357 : pszInput = OGRWktReadToken(pszInput, szToken); 224 1357 : } while (szToken[0] == ',' && eErr == OGRERR_NONE); 225 : 226 1042 : CPLFree(paoPoints); 227 1042 : CPLFree(padfZ); 228 : 229 : /* -------------------------------------------------------------------- */ 230 : /* freak if we don't get a closing bracket. */ 231 : /* -------------------------------------------------------------------- */ 232 : 233 1042 : if (eErr != OGRERR_NONE) 234 30 : return eErr; 235 : 236 1012 : if (szToken[0] != ')') 237 3 : return OGRERR_CORRUPT_DATA; 238 : 239 1009 : *ppszInput = pszInput; 240 1009 : return OGRERR_NONE; 241 : } 242 : 243 : /************************************************************************/ 244 : /* exportToWkt() */ 245 : /************************************************************************/ 246 : 247 85 : std::string OGRMultiSurface::exportToWkt(const OGRWktOptions &opts, 248 : OGRErr *err) const 249 : { 250 85 : OGRWktOptions optsModified(opts); 251 85 : optsModified.variant = wkbVariantIso; 252 85 : return exportToWktInternal(optsModified, err, "POLYGON"); 253 : } 254 : 255 : /************************************************************************/ 256 : /* hasCurveGeometry() */ 257 : /************************************************************************/ 258 : 259 1221 : bool OGRMultiSurface::hasCurveGeometry(int bLookForNonLinear) const 260 : { 261 1221 : if (bLookForNonLinear) 262 23 : return OGRGeometryCollection::hasCurveGeometry(TRUE); 263 1198 : return TRUE; 264 : } 265 : 266 : /************************************************************************/ 267 : /* PointOnSurface() */ 268 : /************************************************************************/ 269 : 270 : /** \brief This method relates to the SFCOM 271 : * IMultiSurface::get_PointOnSurface() method. 272 : * 273 : * NOTE: Only implemented when GEOS included in build. 274 : * 275 : * @param poPoint point to be set with an internal point. 276 : * 277 : * @return OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise. 278 : */ 279 : 280 0 : OGRErr OGRMultiSurface::PointOnSurface(OGRPoint *poPoint) const 281 : { 282 0 : return PointOnSurfaceInternal(poPoint); 283 : } 284 : 285 : /************************************************************************/ 286 : /* CastToMultiPolygon() */ 287 : /************************************************************************/ 288 : 289 : /** 290 : * \brief Cast to multipolygon. 291 : * 292 : * This method should only be called if the multisurface actually only contains 293 : * instances of OGRPolygon. This can be verified if hasCurveGeometry(TRUE) 294 : * returns FALSE. It is not intended to approximate curve polygons. For that 295 : * use getLinearGeometry(). 296 : * 297 : * The passed in geometry is consumed and a new one returned (or NULL in case 298 : * of failure). 299 : * 300 : * @param poMS the input geometry - ownership is passed to the method. 301 : * @return new geometry. 302 : */ 303 : 304 62 : OGRMultiPolygon *OGRMultiSurface::CastToMultiPolygon(OGRMultiSurface *poMS) 305 : { 306 128 : for (auto &&poSubGeom : *poMS) 307 : { 308 66 : poSubGeom = OGRSurface::CastToPolygon(poSubGeom); 309 66 : if (poSubGeom == nullptr) 310 : { 311 0 : delete poMS; 312 0 : return nullptr; 313 : } 314 : } 315 : 316 62 : OGRMultiPolygon *poMP = new OGRMultiPolygon(); 317 62 : TransferMembersAndDestroy(poMS, poMP); 318 62 : return poMP; 319 : }