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