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