1 : /******************************************************************************
2 : * $Id: ogrmultipolygon.cpp 25426 2013-01-01 17:51:35Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: The OGRMultiPolygon class.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "ogr_geometry.h"
31 : #include "ogr_p.h"
32 :
33 : CPL_CVSID("$Id: ogrmultipolygon.cpp 25426 2013-01-01 17:51:35Z rouault $");
34 :
35 : /************************************************************************/
36 : /* OGRMultiPolygon() */
37 : /************************************************************************/
38 :
39 1222 : OGRMultiPolygon::OGRMultiPolygon()
40 : {
41 1222 : }
42 :
43 : /************************************************************************/
44 : /* getGeometryType() */
45 : /************************************************************************/
46 :
47 1437 : OGRwkbGeometryType OGRMultiPolygon::getGeometryType() const
48 :
49 : {
50 1437 : if( getCoordinateDimension() == 3 )
51 137 : return wkbMultiPolygon25D;
52 : else
53 1300 : return wkbMultiPolygon;
54 : }
55 :
56 : /************************************************************************/
57 : /* getDimension() */
58 : /************************************************************************/
59 :
60 0 : int OGRMultiPolygon::getDimension() const
61 :
62 : {
63 0 : return 2;
64 : }
65 :
66 : /************************************************************************/
67 : /* getGeometryName() */
68 : /************************************************************************/
69 :
70 321 : const char * OGRMultiPolygon::getGeometryName() const
71 :
72 : {
73 321 : return "MULTIPOLYGON";
74 : }
75 :
76 : /************************************************************************/
77 : /* addGeometryDirectly() */
78 : /************************************************************************/
79 :
80 1463 : OGRErr OGRMultiPolygon::addGeometryDirectly( OGRGeometry * poNewGeom )
81 :
82 : {
83 1708 : if( poNewGeom->getGeometryType() != wkbPolygon
84 245 : && poNewGeom->getGeometryType() != wkbPolygon25D )
85 2 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
86 :
87 1461 : return OGRGeometryCollection::addGeometryDirectly( poNewGeom );
88 : }
89 :
90 : /************************************************************************/
91 : /* clone() */
92 : /************************************************************************/
93 :
94 203 : OGRGeometry *OGRMultiPolygon::clone() const
95 :
96 : {
97 : OGRMultiPolygon *poNewGC;
98 :
99 203 : poNewGC = new OGRMultiPolygon;
100 203 : poNewGC->assignSpatialReference( getSpatialReference() );
101 :
102 536 : for( int i = 0; i < getNumGeometries(); i++ )
103 : {
104 333 : poNewGC->addGeometry( getGeometryRef(i) );
105 : }
106 :
107 203 : return poNewGC;
108 : }
109 :
110 : /************************************************************************/
111 : /* importFromWkt() */
112 : /* */
113 : /* Instantiate from well known text format. Currently this is */
114 : /* `MULTIPOLYGON ((x y, x y, ...),(x y, ...),...)'. */
115 : /************************************************************************/
116 :
117 188 : OGRErr OGRMultiPolygon::importFromWkt( char ** ppszInput )
118 :
119 : {
120 : char szToken[OGR_WKT_TOKEN_MAX];
121 188 : const char *pszInput = *ppszInput;
122 188 : OGRErr eErr = OGRERR_NONE;
123 :
124 : /* -------------------------------------------------------------------- */
125 : /* Clear existing rings. */
126 : /* -------------------------------------------------------------------- */
127 188 : empty();
128 :
129 : /* -------------------------------------------------------------------- */
130 : /* Read and verify the MULTIPOLYGON keyword token. */
131 : /* -------------------------------------------------------------------- */
132 188 : pszInput = OGRWktReadToken( pszInput, szToken );
133 :
134 188 : if( !EQUAL(szToken,getGeometryName()) )
135 0 : return OGRERR_CORRUPT_DATA;
136 :
137 : /* -------------------------------------------------------------------- */
138 : /* Check for EMPTY ... */
139 : /* -------------------------------------------------------------------- */
140 : const char *pszPreScan;
141 188 : int bHasZ = FALSE, bHasM = FALSE;
142 :
143 188 : pszPreScan = OGRWktReadToken( pszInput, szToken );
144 188 : if( EQUAL(szToken,"EMPTY") )
145 : {
146 15 : *ppszInput = (char *) pszPreScan;
147 15 : empty();
148 15 : return OGRERR_NONE;
149 : }
150 :
151 : /* -------------------------------------------------------------------- */
152 : /* Check for Z, M or ZM. Will ignore the Measure */
153 : /* -------------------------------------------------------------------- */
154 173 : else if( EQUAL(szToken,"Z") )
155 : {
156 13 : bHasZ = TRUE;
157 : }
158 160 : else if( EQUAL(szToken,"M") )
159 : {
160 2 : bHasM = TRUE;
161 : }
162 158 : else if( EQUAL(szToken,"ZM") )
163 : {
164 2 : bHasZ = TRUE;
165 2 : bHasM = TRUE;
166 : }
167 :
168 173 : if (bHasZ || bHasM)
169 : {
170 17 : pszInput = pszPreScan;
171 17 : pszPreScan = OGRWktReadToken( pszInput, szToken );
172 17 : if( EQUAL(szToken,"EMPTY") )
173 : {
174 4 : *ppszInput = (char *) pszPreScan;
175 4 : empty();
176 : /* FIXME?: In theory we should store the dimension and M presence */
177 : /* if we want to allow round-trip with ExportToWKT v1.2 */
178 4 : return OGRERR_NONE;
179 : }
180 : }
181 :
182 169 : if( !EQUAL(szToken,"(") )
183 4 : return OGRERR_CORRUPT_DATA;
184 :
185 165 : if ( !bHasZ && !bHasM )
186 : {
187 : /* Test for old-style MULTIPOLYGON(EMPTY) */
188 154 : pszPreScan = OGRWktReadToken( pszPreScan, szToken );
189 154 : if( EQUAL(szToken,"EMPTY") )
190 : {
191 6 : pszPreScan = OGRWktReadToken( pszPreScan, szToken );
192 :
193 6 : if( EQUAL(szToken,",") )
194 : {
195 : /* This is OK according to SFSQL SPEC. */
196 : }
197 4 : else if( !EQUAL(szToken,")") )
198 1 : return OGRERR_CORRUPT_DATA;
199 : else
200 : {
201 3 : *ppszInput = (char *) pszPreScan;
202 3 : empty();
203 3 : return OGRERR_NONE;
204 : }
205 : }
206 : }
207 :
208 : /* Skip first '(' */
209 161 : pszInput = OGRWktReadToken( pszInput, szToken );
210 :
211 : /* ==================================================================== */
212 : /* Read each polygon in turn. Note that we try to reuse the same */
213 : /* point list buffer from ring to ring to cut down on */
214 : /* allocate/deallocate overhead. */
215 : /* ==================================================================== */
216 161 : OGRRawPoint *paoPoints = NULL;
217 161 : int nMaxPoints = 0;
218 161 : double *padfZ = NULL;
219 :
220 245 : do
221 : {
222 263 : OGRPolygon *poPolygon = new OGRPolygon();
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* The next character should be a ( indicating the start of the */
226 : /* list of polygons. */
227 : /* -------------------------------------------------------------------- */
228 263 : pszInput = OGRWktReadToken( pszInput, szToken );
229 263 : if( EQUAL(szToken, "EMPTY") )
230 : {
231 7 : eErr = addGeometryDirectly( poPolygon );
232 7 : if( eErr != OGRERR_NONE )
233 0 : return eErr;
234 :
235 7 : pszInput = OGRWktReadToken( pszInput, szToken );
236 7 : if ( !EQUAL(szToken, ",") )
237 4 : break;
238 :
239 3 : continue;
240 : }
241 256 : else if( szToken[0] != '(' )
242 : {
243 14 : eErr = OGRERR_CORRUPT_DATA;
244 14 : delete poPolygon;
245 14 : break;
246 : }
247 :
248 : /* -------------------------------------------------------------------- */
249 : /* Loop over each ring in this polygon. */
250 : /* -------------------------------------------------------------------- */
251 291 : do
252 : {
253 307 : int nPoints = 0;
254 :
255 307 : const char* pszNext = OGRWktReadToken( pszInput, szToken );
256 307 : if (EQUAL(szToken,"EMPTY"))
257 : {
258 7 : poPolygon->addRingDirectly( new OGRLinearRing() );
259 :
260 7 : pszInput = OGRWktReadToken( pszNext, szToken );
261 7 : if ( !EQUAL(szToken, ",") )
262 3 : break;
263 :
264 4 : continue;
265 : }
266 :
267 : /* -------------------------------------------------------------------- */
268 : /* Read points for one line from input. */
269 : /* -------------------------------------------------------------------- */
270 : pszInput = OGRWktReadPoints( pszInput, &paoPoints, &padfZ, &nMaxPoints,
271 300 : &nPoints );
272 :
273 300 : if( pszInput == NULL || nPoints == 0 )
274 : {
275 13 : eErr = OGRERR_CORRUPT_DATA;
276 13 : break;
277 : }
278 :
279 : /* -------------------------------------------------------------------- */
280 : /* Create the new line, and add to collection. */
281 : /* -------------------------------------------------------------------- */
282 : OGRLinearRing *poLine;
283 :
284 287 : poLine = new OGRLinearRing();
285 : /* Ignore Z array when we have a MULTIPOLYGON M */
286 290 : if (bHasM && !bHasZ)
287 1 : poLine->setPoints( nPoints, paoPoints, NULL );
288 : else
289 286 : poLine->setPoints( nPoints, paoPoints, padfZ );
290 :
291 287 : poPolygon->addRingDirectly( poLine );
292 :
293 : /* -------------------------------------------------------------------- */
294 : /* Read the delimeter following the ring. */
295 : /* -------------------------------------------------------------------- */
296 :
297 287 : pszInput = OGRWktReadToken( pszInput, szToken );
298 291 : } while( szToken[0] == ',' && eErr == OGRERR_NONE );
299 :
300 : /* -------------------------------------------------------------------- */
301 : /* Verify that we have a closing bracket. */
302 : /* -------------------------------------------------------------------- */
303 242 : if( eErr == OGRERR_NONE )
304 : {
305 229 : if( szToken[0] != ')' )
306 1 : eErr = OGRERR_CORRUPT_DATA;
307 : else
308 228 : pszInput = OGRWktReadToken( pszInput, szToken );
309 : }
310 :
311 : /* -------------------------------------------------------------------- */
312 : /* Add the polygon to the MULTIPOLYGON. */
313 : /* -------------------------------------------------------------------- */
314 242 : if( eErr == OGRERR_NONE )
315 228 : eErr = addGeometryDirectly( poPolygon );
316 : else
317 14 : delete poPolygon;
318 :
319 245 : } while( szToken[0] == ',' && eErr == OGRERR_NONE );
320 :
321 : /* -------------------------------------------------------------------- */
322 : /* freak if we don't get a closing bracket. */
323 : /* -------------------------------------------------------------------- */
324 161 : CPLFree( paoPoints );
325 161 : CPLFree( padfZ );
326 :
327 161 : if( eErr != OGRERR_NONE )
328 28 : return eErr;
329 :
330 133 : if( szToken[0] != ')' )
331 3 : return OGRERR_CORRUPT_DATA;
332 :
333 130 : *ppszInput = (char *) pszInput;
334 130 : return OGRERR_NONE;
335 : }
336 :
337 : /************************************************************************/
338 : /* exportToWkt() */
339 : /* */
340 : /* Translate this structure into it's well known text format */
341 : /* equivelent. This could be made alot more CPU efficient! */
342 : /************************************************************************/
343 :
344 80 : OGRErr OGRMultiPolygon::exportToWkt( char ** ppszDstText ) const
345 :
346 : {
347 : char **papszPolygons;
348 80 : int iPoly, nCumulativeLength = 0, nValidPolys=0;
349 : OGRErr eErr;
350 80 : int bMustWriteComma = FALSE;
351 :
352 : /* -------------------------------------------------------------------- */
353 : /* Build a list of strings containing the stuff for each ring. */
354 : /* -------------------------------------------------------------------- */
355 80 : papszPolygons = (char **) CPLCalloc(sizeof(char *),getNumGeometries());
356 :
357 186 : for( iPoly = 0; iPoly < getNumGeometries(); iPoly++ )
358 : {
359 106 : eErr = getGeometryRef(iPoly)->exportToWkt( &(papszPolygons[iPoly]) );
360 106 : if( eErr != OGRERR_NONE )
361 0 : goto error;
362 :
363 106 : if( !EQUALN(papszPolygons[iPoly],"POLYGON (", 9) )
364 : {
365 : CPLDebug( "OGR", "OGRMultiPolygon::exportToWkt() - skipping %s.",
366 10 : papszPolygons[iPoly] );
367 10 : CPLFree( papszPolygons[iPoly] );
368 10 : papszPolygons[iPoly] = NULL;
369 10 : continue;
370 : }
371 :
372 96 : nCumulativeLength += strlen(papszPolygons[iPoly] + 8);
373 96 : nValidPolys++;
374 : }
375 :
376 : /* -------------------------------------------------------------------- */
377 : /* Return MULTIPOLYGON EMPTY if we get no valid polygons. */
378 : /* -------------------------------------------------------------------- */
379 80 : if( nValidPolys == 0 )
380 : {
381 22 : CPLFree( papszPolygons );
382 22 : *ppszDstText = CPLStrdup("MULTIPOLYGON EMPTY");
383 22 : return OGRERR_NONE;
384 : }
385 :
386 : /* -------------------------------------------------------------------- */
387 : /* Allocate exactly the right amount of space for the */
388 : /* aggregated string. */
389 : /* -------------------------------------------------------------------- */
390 58 : *ppszDstText = (char *) VSIMalloc(nCumulativeLength+getNumGeometries()+20);
391 :
392 58 : if( *ppszDstText == NULL )
393 : {
394 0 : eErr = OGRERR_NOT_ENOUGH_MEMORY;
395 0 : goto error;
396 : }
397 :
398 : /* -------------------------------------------------------------------- */
399 : /* Build up the string, freeing temporary strings as we go. */
400 : /* -------------------------------------------------------------------- */
401 58 : strcpy( *ppszDstText, "MULTIPOLYGON (" );
402 58 : nCumulativeLength = strlen(*ppszDstText);
403 :
404 160 : for( iPoly = 0; iPoly < getNumGeometries(); iPoly++ )
405 : {
406 102 : if( papszPolygons[iPoly] == NULL )
407 6 : continue;
408 :
409 96 : if( bMustWriteComma )
410 38 : (*ppszDstText)[nCumulativeLength++] = ',';
411 96 : bMustWriteComma = TRUE;
412 :
413 96 : int nPolyLength = strlen(papszPolygons[iPoly] + 8);
414 96 : memcpy( *ppszDstText + nCumulativeLength, papszPolygons[iPoly] + 8, nPolyLength );
415 96 : nCumulativeLength += nPolyLength;
416 96 : VSIFree( papszPolygons[iPoly] );
417 : }
418 :
419 58 : (*ppszDstText)[nCumulativeLength++] = ')';
420 58 : (*ppszDstText)[nCumulativeLength] = '\0';
421 :
422 58 : CPLFree( papszPolygons );
423 :
424 58 : return OGRERR_NONE;
425 :
426 : error:
427 0 : for( iPoly = 0; iPoly < getNumGeometries(); iPoly++ )
428 0 : CPLFree( papszPolygons[iPoly] );
429 0 : CPLFree( papszPolygons );
430 0 : return eErr;
431 : }
432 :
433 : /************************************************************************/
434 : /* get_Area() */
435 : /************************************************************************/
436 :
437 : /**
438 : * Compute area of multipolygon.
439 : *
440 : * The area is computed as the sum of the areas of all polygon members
441 : * in this collection.
442 : *
443 : * @return computed area.
444 : */
445 :
446 3 : double OGRMultiPolygon::get_Area() const
447 :
448 : {
449 3 : double dfArea = 0.0;
450 : int iPoly;
451 :
452 8 : for( iPoly = 0; iPoly < getNumGeometries(); iPoly++ )
453 : {
454 5 : OGRPolygon *poPoly = (OGRPolygon *) getGeometryRef( iPoly );
455 :
456 5 : dfArea += poPoly->get_Area();
457 : }
458 :
459 3 : return dfArea;
460 : }
461 :
|