1 : /******************************************************************************
2 : * $Id: shape2ogr.cpp 18473 2010-01-07 23:40:39Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements translation of Shapefile shapes into OGR
6 : * representation.
7 : * Author: Frank Warmerdam, warmerda@home.com
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 1999, Les Technologies SoftMap Inc.
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "ogrshape.h"
32 : #include "cpl_conv.h"
33 :
34 : CPL_CVSID("$Id: shape2ogr.cpp 18473 2010-01-07 23:40:39Z rouault $");
35 :
36 : static const double EPSILON = 1E-5;
37 :
38 : /************************************************************************/
39 : /* epsilonEqual() */
40 : /************************************************************************/
41 :
42 : static inline bool epsilonEqual(double a, double b, double eps)
43 : {
44 : return (::fabs(a - b) < eps);
45 : }
46 :
47 : /************************************************************************/
48 : /* RingStartEnd */
49 : /* set first and last vertex for given ring */
50 : /************************************************************************/
51 814 : void RingStartEnd ( SHPObject *psShape, int ring, int *start, int *end )
52 : {
53 814 : if( psShape->panPartStart == NULL )
54 : {
55 0 : *start = 0;
56 0 : *end = psShape->nVertices - 1;
57 : }
58 : else
59 : {
60 814 : if( ring == psShape->nParts - 1 )
61 775 : *end = psShape->nVertices - 1;
62 : else
63 39 : *end = psShape->panPartStart[ring+1] - 1;
64 :
65 814 : *start = psShape->panPartStart[ring];
66 : }
67 814 : }
68 :
69 : /************************************************************************/
70 : /* CreateLinearRing */
71 : /* */
72 : /************************************************************************/
73 814 : OGRLinearRing * CreateLinearRing ( SHPObject *psShape, int ring )
74 : {
75 : OGRLinearRing *poRing;
76 : int nRingStart, nRingEnd, nRingPoints;
77 :
78 814 : poRing = new OGRLinearRing();
79 :
80 814 : RingStartEnd ( psShape, ring, &nRingStart, &nRingEnd );
81 :
82 814 : nRingPoints = nRingEnd - nRingStart + 1;
83 :
84 : poRing->setPoints( nRingPoints, psShape->padfX + nRingStart,
85 814 : psShape->padfY + nRingStart, psShape->padfZ + nRingStart );
86 :
87 814 : return ( poRing );
88 : }
89 :
90 :
91 : /************************************************************************/
92 : /* SHPReadOGRObject() */
93 : /* */
94 : /* Read an item in a shapefile, and translate to OGR geometry */
95 : /* representation. */
96 : /************************************************************************/
97 :
98 15639 : OGRGeometry *SHPReadOGRObject( SHPHandle hSHP, int iShape, SHPObject *psShape )
99 : {
100 : // CPLDebug( "Shape", "SHPReadOGRObject( iShape=%d )\n", iShape );
101 :
102 15639 : OGRGeometry *poOGR = NULL;
103 :
104 15639 : if( psShape == NULL )
105 15593 : psShape = SHPReadObject( hSHP, iShape );
106 :
107 15639 : if( psShape == NULL )
108 : {
109 5 : return NULL;
110 : }
111 :
112 : /* -------------------------------------------------------------------- */
113 : /* Point. */
114 : /* -------------------------------------------------------------------- */
115 30308 : else if( psShape->nSHPType == SHPT_POINT
116 : || psShape->nSHPType == SHPT_POINTM
117 : || psShape->nSHPType == SHPT_POINTZ )
118 : {
119 : poOGR = new OGRPoint( psShape->padfX[0], psShape->padfY[0],
120 14674 : psShape->padfZ[0] );
121 :
122 14674 : if( psShape->nSHPType == SHPT_POINT )
123 : {
124 31 : poOGR->setCoordinateDimension( 2 );
125 : }
126 : }
127 :
128 : /* -------------------------------------------------------------------- */
129 : /* Multipoint. */
130 : /* -------------------------------------------------------------------- */
131 967 : else if( psShape->nSHPType == SHPT_MULTIPOINT
132 : || psShape->nSHPType == SHPT_MULTIPOINTM
133 : || psShape->nSHPType == SHPT_MULTIPOINTZ )
134 : {
135 7 : if (psShape->nVertices == 0)
136 : {
137 1 : poOGR = NULL;
138 : }
139 : else
140 : {
141 6 : OGRMultiPoint *poOGRMPoint = new OGRMultiPoint();
142 : int i;
143 :
144 17 : for( i = 0; i < psShape->nVertices; i++ )
145 : {
146 : OGRPoint *poPoint;
147 :
148 : poPoint = new OGRPoint( psShape->padfX[i], psShape->padfY[i],
149 11 : psShape->padfZ[i] );
150 :
151 11 : poOGRMPoint->addGeometry( poPoint );
152 :
153 22 : delete poPoint;
154 : }
155 :
156 6 : poOGR = poOGRMPoint;
157 :
158 6 : if( psShape->nSHPType == SHPT_MULTIPOINT )
159 4 : poOGR->setCoordinateDimension( 2 );
160 : }
161 : }
162 :
163 : /* -------------------------------------------------------------------- */
164 : /* Arc (LineString) */
165 : /* */
166 : /* I am ignoring parts though they can apply to arcs as well. */
167 : /* -------------------------------------------------------------------- */
168 1108 : else if( psShape->nSHPType == SHPT_ARC
169 : || psShape->nSHPType == SHPT_ARCM
170 : || psShape->nSHPType == SHPT_ARCZ )
171 : {
172 155 : if( psShape->nParts == 0 )
173 : {
174 1 : poOGR = NULL;
175 : }
176 154 : else if( psShape->nParts == 1 )
177 : {
178 148 : OGRLineString *poOGRLine = new OGRLineString();
179 :
180 : poOGRLine->setPoints( psShape->nVertices,
181 148 : psShape->padfX, psShape->padfY, psShape->padfZ );
182 :
183 148 : poOGR = poOGRLine;
184 : }
185 : else
186 : {
187 : int iRing;
188 : OGRMultiLineString *poOGRMulti;
189 :
190 6 : poOGR = poOGRMulti = new OGRMultiLineString();
191 :
192 18 : for( iRing = 0; iRing < psShape->nParts; iRing++ )
193 : {
194 : OGRLineString *poLine;
195 : int nRingPoints;
196 : int nRingStart;
197 :
198 12 : poLine = new OGRLineString();
199 :
200 12 : if( psShape->panPartStart == NULL )
201 : {
202 0 : nRingPoints = psShape->nVertices;
203 0 : nRingStart = 0;
204 : }
205 : else
206 : {
207 :
208 12 : if( iRing == psShape->nParts - 1 )
209 : nRingPoints =
210 6 : psShape->nVertices - psShape->panPartStart[iRing];
211 : else
212 6 : nRingPoints = psShape->panPartStart[iRing+1]
213 6 : - psShape->panPartStart[iRing];
214 12 : nRingStart = psShape->panPartStart[iRing];
215 : }
216 :
217 : poLine->setPoints( nRingPoints,
218 : psShape->padfX + nRingStart,
219 : psShape->padfY + nRingStart,
220 12 : psShape->padfZ + nRingStart );
221 :
222 12 : poOGRMulti->addGeometryDirectly( poLine );
223 : }
224 : }
225 :
226 155 : if( poOGR != NULL && psShape->nSHPType == SHPT_ARC )
227 144 : poOGR->setCoordinateDimension( 2 );
228 : }
229 :
230 : /* -------------------------------------------------------------------- */
231 : /* Polygon */
232 : /* */
233 : /* As for now Z coordinate is not handled correctly */
234 : /* -------------------------------------------------------------------- */
235 1574 : else if( psShape->nSHPType == SHPT_POLYGON
236 : || psShape->nSHPType == SHPT_POLYGONM
237 : || psShape->nSHPType == SHPT_POLYGONZ )
238 : {
239 : int iRing;
240 :
241 : //CPLDebug( "Shape", "Shape type: polygon with nParts=%d \n", psShape->nParts );
242 :
243 776 : if ( psShape->nParts == 0 )
244 : {
245 1 : poOGR = NULL;
246 : }
247 775 : else if ( psShape->nParts == 1 )
248 : {
249 : /* Surely outer ring */
250 758 : OGRPolygon *poOGRPoly = NULL;
251 758 : OGRLinearRing *poRing = NULL;
252 :
253 758 : poOGR = poOGRPoly = new OGRPolygon();
254 758 : poRing = CreateLinearRing ( psShape, 0 );
255 758 : poOGRPoly->addRingDirectly( poRing );
256 : }
257 :
258 : else
259 : {
260 17 : OGRPolygon** tabPolygons = new OGRPolygon*[psShape->nParts];
261 146 : for( iRing = 0; iRing < psShape->nParts; iRing++ )
262 : {
263 56 : tabPolygons[iRing] = new OGRPolygon();
264 56 : tabPolygons[iRing]->addRingDirectly(CreateLinearRing ( psShape, iRing ));
265 : }
266 :
267 : int isValidGeometry;
268 17 : const char* papszOptions[] = { "METHOD=ONLY_CCW", NULL };
269 : poOGR = OGRGeometryFactory::organizePolygons(
270 17 : (OGRGeometry**)tabPolygons, psShape->nParts, &isValidGeometry, papszOptions );
271 :
272 17 : if (!isValidGeometry)
273 : {
274 : CPLError(CE_Warning, CPLE_AppDefined,
275 : "Geometry of polygon of fid %d cannot be translated to Simple Geometry. "
276 : "All polygons will be contained in a multipolygon.\n",
277 0 : iShape);
278 : }
279 :
280 17 : delete[] tabPolygons;
281 : }
282 :
283 776 : if( poOGR != NULL && psShape->nSHPType == SHPT_POLYGON )
284 : {
285 771 : poOGR->setCoordinateDimension( 2 );
286 : }
287 : }
288 :
289 : /* -------------------------------------------------------------------- */
290 : /* Otherwise for now we just ignore the object. Eventually we */
291 : /* should implement multipatch. */
292 : /* -------------------------------------------------------------------- */
293 : else
294 : {
295 22 : if( psShape->nSHPType != SHPT_NULL )
296 : {
297 0 : CPLDebug( "OGR", "Unsupported shape type in SHPReadOGRObject()" );
298 : }
299 :
300 : /* nothing returned */
301 : }
302 :
303 : /* -------------------------------------------------------------------- */
304 : /* Cleanup shape, and set feature id. */
305 : /* -------------------------------------------------------------------- */
306 15634 : SHPDestroyObject( psShape );
307 :
308 15634 : return poOGR;
309 : }
310 :
311 : /************************************************************************/
312 : /* SHPWriteOGRObject() */
313 : /************************************************************************/
314 :
315 15069 : OGRErr SHPWriteOGRObject( SHPHandle hSHP, int iShape, OGRGeometry *poGeom )
316 :
317 : {
318 : int nReturnedShapeID;
319 : /* ==================================================================== */
320 : /* Write "shape" with no geometry or with empty geometry */
321 : /* ==================================================================== */
322 15069 : if( poGeom == NULL || poGeom->IsEmpty() )
323 : {
324 : SHPObject *psShape;
325 :
326 15 : psShape = SHPCreateSimpleObject( SHPT_NULL, 0, NULL, NULL, NULL );
327 15 : nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape );
328 15 : SHPDestroyObject( psShape );
329 15 : if( nReturnedShapeID == -1 )
330 : {
331 : //Assuming error is reported by SHPWriteObject()
332 0 : return OGRERR_FAILURE;
333 : }
334 : }
335 :
336 : /* ==================================================================== */
337 : /* Write point geometry. */
338 : /* ==================================================================== */
339 29724 : else if( hSHP->nShapeType == SHPT_POINT
340 : || hSHP->nShapeType == SHPT_POINTM
341 : || hSHP->nShapeType == SHPT_POINTZ )
342 : {
343 : SHPObject *psShape;
344 14676 : OGRPoint *poPoint = (OGRPoint *) poGeom;
345 14676 : double dfX, dfY, dfZ = 0;
346 :
347 29325 : if( poGeom->getGeometryType() != wkbPoint
348 14649 : && poGeom->getGeometryType() != wkbPoint25D )
349 : {
350 : CPLError( CE_Failure, CPLE_AppDefined,
351 : "Attempt to write non-point (%s) geometry to"
352 : " point shapefile.",
353 6 : poGeom->getGeometryName() );
354 :
355 6 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
356 : }
357 :
358 14670 : dfX = poPoint->getX();
359 14670 : dfY = poPoint->getY();
360 14670 : dfZ = poPoint->getZ();
361 :
362 : psShape = SHPCreateSimpleObject( hSHP->nShapeType, 1,
363 14670 : &dfX, &dfY, &dfZ );
364 14670 : nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape );
365 14670 : SHPDestroyObject( psShape );
366 14670 : if( nReturnedShapeID == -1 )
367 0 : return OGRERR_FAILURE;
368 : }
369 : /* ==================================================================== */
370 : /* MultiPoint. */
371 : /* ==================================================================== */
372 383 : else if( hSHP->nShapeType == SHPT_MULTIPOINT
373 : || hSHP->nShapeType == SHPT_MULTIPOINTM
374 : || hSHP->nShapeType == SHPT_MULTIPOINTZ )
375 : {
376 11 : OGRMultiPoint *poMP = (OGRMultiPoint *) poGeom;
377 : double *padfX, *padfY, *padfZ;
378 : int iPoint;
379 : SHPObject *psShape;
380 :
381 11 : if( wkbFlatten(poGeom->getGeometryType()) != wkbMultiPoint )
382 : {
383 : CPLError( CE_Failure, CPLE_AppDefined,
384 : "Attempt to write non-multipoint (%s) geometry to "
385 : "multipoint shapefile.",
386 6 : poGeom->getGeometryName() );
387 :
388 6 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
389 : }
390 :
391 5 : padfX = (double *) CPLMalloc(sizeof(double)*poMP->getNumGeometries());
392 5 : padfY = (double *) CPLMalloc(sizeof(double)*poMP->getNumGeometries());
393 5 : padfZ = (double *) CPLCalloc(sizeof(double),poMP->getNumGeometries());
394 :
395 5 : int iDstPoints = 0;
396 15 : for( iPoint = 0; iPoint < poMP->getNumGeometries(); iPoint++ )
397 : {
398 10 : OGRPoint *poPoint = (OGRPoint *) poMP->getGeometryRef(iPoint);
399 :
400 : /* Ignore POINT EMPTY */
401 10 : if (poPoint->IsEmpty() == FALSE)
402 : {
403 9 : padfX[iDstPoints] = poPoint->getX();
404 9 : padfY[iDstPoints] = poPoint->getY();
405 9 : padfZ[iDstPoints] = poPoint->getZ();
406 9 : iDstPoints ++;
407 : }
408 : else
409 : CPLDebug( "OGR",
410 1 : "Ignore POINT EMPTY inside MULTIPOINT in shapefile writer." );
411 : }
412 :
413 : psShape = SHPCreateSimpleObject( hSHP->nShapeType,
414 : iDstPoints,
415 5 : padfX, padfY, padfZ );
416 5 : nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape );
417 5 : SHPDestroyObject( psShape );
418 :
419 5 : CPLFree( padfX );
420 5 : CPLFree( padfY );
421 5 : CPLFree( padfZ );
422 5 : if( nReturnedShapeID == -1 )
423 0 : return OGRERR_FAILURE;
424 : }
425 :
426 : /* ==================================================================== */
427 : /* Arcs from simple line strings. */
428 : /* ==================================================================== */
429 524 : else if( (hSHP->nShapeType == SHPT_ARC
430 : || hSHP->nShapeType == SHPT_ARCM
431 : || hSHP->nShapeType == SHPT_ARCZ)
432 157 : && wkbFlatten(poGeom->getGeometryType()) == wkbLineString )
433 : {
434 140 : OGRLineString *poArc = (OGRLineString *) poGeom;
435 : double *padfX, *padfY, *padfZ;
436 : int iPoint;
437 : SHPObject *psShape;
438 :
439 140 : padfX = (double *) CPLMalloc(sizeof(double)*poArc->getNumPoints());
440 140 : padfY = (double *) CPLMalloc(sizeof(double)*poArc->getNumPoints());
441 140 : padfZ = (double *) CPLCalloc(sizeof(double),poArc->getNumPoints());
442 :
443 4506 : for( iPoint = 0; iPoint < poArc->getNumPoints(); iPoint++ )
444 : {
445 4366 : padfX[iPoint] = poArc->getX( iPoint );
446 4366 : padfY[iPoint] = poArc->getY( iPoint );
447 4366 : padfZ[iPoint] = poArc->getZ( iPoint );
448 : }
449 :
450 : psShape = SHPCreateSimpleObject( hSHP->nShapeType,
451 : poArc->getNumPoints(),
452 140 : padfX, padfY, padfZ );
453 140 : nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape );
454 140 : SHPDestroyObject( psShape );
455 :
456 140 : CPLFree( padfX );
457 140 : CPLFree( padfY );
458 140 : CPLFree( padfZ );
459 140 : if( nReturnedShapeID == -1 )
460 0 : return OGRERR_FAILURE;
461 : }
462 : /* ==================================================================== */
463 : /* Arcs - Try to treat as MultiLineString. */
464 : /* ==================================================================== */
465 232 : else if( hSHP->nShapeType == SHPT_ARC
466 : || hSHP->nShapeType == SHPT_ARCM
467 : || hSHP->nShapeType == SHPT_ARCZ )
468 : {
469 : OGRMultiLineString *poML;
470 17 : double *padfX=NULL, *padfY=NULL, *padfZ=NULL;
471 17 : int iGeom, iPoint, nPointCount = 0;
472 : SHPObject *psShape;
473 : int *panRingStart;
474 17 : int nParts = 0;
475 :
476 : poML = (OGRMultiLineString *)
477 17 : OGRGeometryFactory::forceToMultiLineString( poGeom->clone() );
478 :
479 17 : if( wkbFlatten(poML->getGeometryType()) != wkbMultiLineString )
480 : {
481 12 : delete poML;
482 : CPLError( CE_Failure, CPLE_AppDefined,
483 : "Attempt to write non-linestring (%s) geometry to "
484 : "ARC type shapefile.",
485 12 : poGeom->getGeometryName() );
486 :
487 12 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
488 : }
489 :
490 : panRingStart = (int *)
491 5 : CPLMalloc(sizeof(int) * poML->getNumGeometries());
492 :
493 16 : for( iGeom = 0; iGeom < poML->getNumGeometries(); iGeom++ )
494 : {
495 : OGRLineString *poArc = (OGRLineString *)
496 11 : poML->getGeometryRef(iGeom);
497 11 : int nNewPoints = poArc->getNumPoints();
498 :
499 : /* Ignore LINESTRING EMPTY */
500 11 : if (nNewPoints == 0)
501 : {
502 : CPLDebug( "OGR",
503 1 : "Ignore LINESTRING EMPTY inside MULTILINESTRING in shapefile writer." );
504 1 : continue;
505 : }
506 :
507 10 : panRingStart[nParts ++] = nPointCount;
508 :
509 : padfX = (double *)
510 10 : CPLRealloc( padfX, sizeof(double)*(nNewPoints+nPointCount) );
511 : padfY = (double *)
512 10 : CPLRealloc( padfY, sizeof(double)*(nNewPoints+nPointCount) );
513 : padfZ = (double *)
514 10 : CPLRealloc( padfZ, sizeof(double)*(nNewPoints+nPointCount) );
515 :
516 50 : for( iPoint = 0; iPoint < nNewPoints; iPoint++ )
517 : {
518 40 : padfX[nPointCount] = poArc->getX( iPoint );
519 40 : padfY[nPointCount] = poArc->getY( iPoint );
520 40 : padfZ[nPointCount] = poArc->getZ( iPoint );
521 40 : nPointCount++;
522 : }
523 : }
524 :
525 : CPLAssert(nParts != 0);
526 :
527 : psShape = SHPCreateObject( hSHP->nShapeType, iShape,
528 : nParts,
529 : panRingStart, NULL,
530 5 : nPointCount, padfX, padfY, padfZ, NULL);
531 5 : nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape );
532 5 : SHPDestroyObject( psShape );
533 :
534 5 : CPLFree( panRingStart );
535 5 : CPLFree( padfX );
536 5 : CPLFree( padfY );
537 5 : CPLFree( padfZ );
538 :
539 5 : delete poML;
540 5 : if( nReturnedShapeID == -1 )
541 0 : return OGRERR_FAILURE;
542 : }
543 :
544 : /* ==================================================================== */
545 : /* Polygons/MultiPolygons */
546 : /* ==================================================================== */
547 406 : else if( hSHP->nShapeType == SHPT_POLYGON
548 : || hSHP->nShapeType == SHPT_POLYGONM
549 : || hSHP->nShapeType == SHPT_POLYGONZ )
550 : {
551 : OGRPolygon *poPoly;
552 210 : OGRLinearRing *poRing, **papoRings=NULL;
553 210 : double *padfX=NULL, *padfY=NULL, *padfZ=NULL;
554 210 : int iPoint, iRing, nRings, nVertex=0, *panRingStart;
555 : SHPObject *psShape;
556 :
557 : /* Collect list of rings */
558 :
559 210 : if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon )
560 : {
561 186 : poPoly = (OGRPolygon *) poGeom;
562 :
563 372 : if( poPoly->getExteriorRing() == NULL ||
564 186 : poPoly->getExteriorRing()->IsEmpty() )
565 : {
566 : CPLDebug( "OGR",
567 1 : "Ignore POLYGON EMPTY in shapefile writer." );
568 1 : nRings = 0;
569 : }
570 : else
571 : {
572 185 : int nSrcRings = poPoly->getNumInteriorRings()+1;
573 185 : nRings = 0;
574 185 : papoRings = (OGRLinearRing **) CPLMalloc(sizeof(void*)*nSrcRings);
575 375 : for( iRing = 0; iRing < nSrcRings; iRing++ )
576 : {
577 190 : if( iRing == 0 )
578 185 : papoRings[nRings] = poPoly->getExteriorRing();
579 : else
580 5 : papoRings[nRings] = poPoly->getInteriorRing( iRing-1 );
581 :
582 : /* Ignore LINEARRING EMPTY */
583 190 : if (papoRings[nRings]->getNumPoints() != 0)
584 189 : nRings ++;
585 : else
586 : CPLDebug( "OGR",
587 1 : "Ignore LINEARRING EMPTY inside POLYGON in shapefile writer." );
588 : }
589 : }
590 : }
591 38 : else if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon
592 14 : || wkbFlatten(poGeom->getGeometryType())
593 : == wkbGeometryCollection )
594 : {
595 12 : OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom;
596 : int iGeom;
597 :
598 12 : nRings = 0;
599 40 : for( iGeom=0; iGeom < poGC->getNumGeometries(); iGeom++ )
600 : {
601 29 : poPoly = (OGRPolygon *) poGC->getGeometryRef( iGeom );
602 :
603 29 : if( wkbFlatten(poPoly->getGeometryType()) != wkbPolygon )
604 : {
605 1 : CPLFree( papoRings );
606 : CPLError( CE_Failure, CPLE_AppDefined,
607 : "Attempt to write non-polygon (%s) geometry to "
608 : "POLYGON type shapefile.",
609 1 : poGeom->getGeometryName());
610 :
611 1 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
612 : }
613 :
614 : /* Ignore POLYGON EMPTY */
615 54 : if( poPoly->getExteriorRing() == NULL ||
616 26 : poPoly->getExteriorRing()->IsEmpty() )
617 : {
618 : CPLDebug( "OGR",
619 2 : "Ignore POLYGON EMPTY inside MULTIPOLYGON in shapefile writer." );
620 2 : continue;
621 : }
622 :
623 : papoRings = (OGRLinearRing **) CPLRealloc(papoRings,
624 26 : sizeof(void*) * (nRings+poPoly->getNumInteriorRings()+1));
625 65 : for( iRing = 0;
626 : iRing < poPoly->getNumInteriorRings()+1;
627 : iRing++ )
628 : {
629 39 : if( iRing == 0 )
630 26 : papoRings[nRings] = poPoly->getExteriorRing();
631 : else
632 13 : papoRings[nRings] =
633 13 : poPoly->getInteriorRing( iRing-1 );
634 :
635 : /* Ignore LINEARRING EMPTY */
636 39 : if (papoRings[nRings]->getNumPoints() != 0)
637 37 : nRings ++;
638 : else
639 : CPLDebug( "OGR",
640 2 : "Ignore LINEARRING EMPTY inside POLYGON in shapefile writer." );
641 : }
642 : }
643 : }
644 : else
645 : {
646 : CPLError( CE_Failure, CPLE_AppDefined,
647 : "Attempt to write non-polygon (%s) geometry to "
648 : "POLYGON type shapefile.",
649 12 : poGeom->getGeometryName() );
650 :
651 12 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
652 : }
653 :
654 : /* -------------------------------------------------------------------- */
655 : /* If we only had emptypolygons or unacceptable geometries */
656 : /* write NULL geometry object. */
657 : /* -------------------------------------------------------------------- */
658 197 : if( nRings == 0 )
659 : {
660 : SHPObject *psShape;
661 :
662 1 : psShape = SHPCreateSimpleObject( SHPT_NULL, 0, NULL, NULL, NULL );
663 1 : nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape );
664 1 : SHPDestroyObject( psShape );
665 :
666 1 : if( nReturnedShapeID == -1 )
667 0 : return OGRERR_FAILURE;
668 :
669 1 : return OGRERR_NONE;
670 : }
671 :
672 : /* count vertices */
673 196 : nVertex = 0;
674 422 : for( iRing = 0; iRing < nRings; iRing++ )
675 226 : nVertex += papoRings[iRing]->getNumPoints();
676 :
677 196 : panRingStart = (int *) CPLMalloc(sizeof(int) * nRings);
678 196 : padfX = (double *) CPLMalloc(sizeof(double)*nVertex);
679 196 : padfY = (double *) CPLMalloc(sizeof(double)*nVertex);
680 196 : padfZ = (double *) CPLMalloc(sizeof(double)*nVertex);
681 :
682 : /* collect vertices */
683 196 : nVertex = 0;
684 422 : for( iRing = 0; iRing < nRings; iRing++ )
685 : {
686 226 : poRing = papoRings[iRing];
687 226 : panRingStart[iRing] = nVertex;
688 :
689 4535 : for( iPoint = 0; iPoint < poRing->getNumPoints(); iPoint++ )
690 : {
691 4309 : padfX[nVertex] = poRing->getX( iPoint );
692 4309 : padfY[nVertex] = poRing->getY( iPoint );
693 4309 : padfZ[nVertex] = poRing->getZ( iPoint );
694 4309 : nVertex++;
695 : }
696 : }
697 :
698 : psShape = SHPCreateObject( hSHP->nShapeType, iShape, nRings,
699 : panRingStart, NULL,
700 196 : nVertex, padfX, padfY, padfZ, NULL );
701 196 : SHPRewindObject( hSHP, psShape );
702 196 : nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape );
703 196 : SHPDestroyObject( psShape );
704 :
705 196 : CPLFree( papoRings );
706 196 : CPLFree( panRingStart );
707 196 : CPLFree( padfX );
708 196 : CPLFree( padfY );
709 196 : CPLFree( padfZ );
710 196 : if( nReturnedShapeID == -1 )
711 0 : return OGRERR_FAILURE;
712 : }
713 : else
714 : {
715 : /* do nothing for multipatch */
716 0 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
717 : }
718 :
719 15031 : return OGRERR_NONE;
720 : }
721 :
722 : /************************************************************************/
723 : /* SHPReadOGRFeatureDefn() */
724 : /************************************************************************/
725 :
726 914 : OGRFeatureDefn *SHPReadOGRFeatureDefn( const char * pszName,
727 : SHPHandle hSHP, DBFHandle hDBF )
728 :
729 : {
730 914 : OGRFeatureDefn *poDefn = new OGRFeatureDefn( pszName );
731 : int iField;
732 :
733 914 : poDefn->Reference();
734 :
735 2153 : for( iField = 0;
736 : hDBF != NULL && iField < DBFGetFieldCount( hDBF );
737 : iField++ )
738 : {
739 : char szFieldName[20];
740 : int nWidth, nPrecision;
741 : DBFFieldType eDBFType;
742 1239 : OGRFieldDefn oField("", OFTInteger);
743 : char chNativeType;
744 :
745 1239 : chNativeType = DBFGetNativeFieldType( hDBF, iField );
746 : eDBFType = DBFGetFieldInfo( hDBF, iField, szFieldName,
747 1239 : &nWidth, &nPrecision );
748 :
749 1239 : oField.SetName( szFieldName );
750 1239 : oField.SetWidth( nWidth );
751 1239 : oField.SetPrecision( nPrecision );
752 :
753 1239 : if( chNativeType == 'D' )
754 : {
755 : /* XXX - mloskot:
756 : * Shapefile date has following 8-chars long format: 20060101.
757 : * OGR splits it as YYYY/MM/DD, so 2 additional characters are required.
758 : * Is this correct assumtion? What about time part of date?
759 : * Shouldn't this format look as datetime: YYYY/MM/DD HH:MM:SS
760 : * with 4 additional characters?
761 : */
762 22 : oField.SetWidth( nWidth + 2 );
763 22 : oField.SetType( OFTDate );
764 : }
765 1217 : else if( eDBFType == FTDouble )
766 903 : oField.SetType( OFTReal );
767 314 : else if( eDBFType == FTInteger )
768 51 : oField.SetType( OFTInteger );
769 : else
770 263 : oField.SetType( OFTString );
771 :
772 1239 : poDefn->AddFieldDefn( &oField );
773 : }
774 :
775 914 : if( hSHP == NULL )
776 21 : poDefn->SetGeomType( wkbNone );
777 : else
778 : {
779 893 : switch( hSHP->nShapeType )
780 : {
781 : case SHPT_POINT:
782 : case SHPT_POINTM:
783 99 : poDefn->SetGeomType( wkbPoint );
784 99 : break;
785 :
786 : case SHPT_POINTZ:
787 47 : poDefn->SetGeomType( wkbPoint25D );
788 47 : break;
789 :
790 : case SHPT_ARC:
791 : case SHPT_ARCM:
792 205 : poDefn->SetGeomType( wkbLineString );
793 205 : break;
794 :
795 : case SHPT_ARCZ:
796 74 : poDefn->SetGeomType( wkbLineString25D );
797 74 : break;
798 :
799 : case SHPT_MULTIPOINT:
800 : case SHPT_MULTIPOINTM:
801 85 : poDefn->SetGeomType( wkbMultiPoint );
802 85 : break;
803 :
804 : case SHPT_MULTIPOINTZ:
805 42 : poDefn->SetGeomType( wkbMultiPoint25D );
806 42 : break;
807 :
808 : case SHPT_POLYGON:
809 : case SHPT_POLYGONM:
810 281 : poDefn->SetGeomType( wkbPolygon );
811 281 : break;
812 :
813 : case SHPT_POLYGONZ:
814 60 : poDefn->SetGeomType( wkbPolygon25D );
815 : break;
816 :
817 : }
818 : }
819 :
820 914 : return poDefn;
821 : }
822 :
823 : /************************************************************************/
824 : /* SHPReadOGRFeature() */
825 : /************************************************************************/
826 :
827 16051 : OGRFeature *SHPReadOGRFeature( SHPHandle hSHP, DBFHandle hDBF,
828 : OGRFeatureDefn * poDefn, int iShape,
829 : SHPObject *psShape )
830 :
831 : {
832 16051 : if( iShape < 0
833 : || (hSHP != NULL && iShape >= hSHP->nRecords)
834 : || (hDBF != NULL && iShape >= hDBF->nRecords) )
835 : {
836 : CPLError( CE_Failure, CPLE_AppDefined,
837 : "Attempt to read shape with feature id (%d) out of available"
838 0 : " range.", iShape );
839 0 : return NULL;
840 : }
841 :
842 16051 : if( hDBF && DBFIsRecordDeleted( hDBF, iShape ) )
843 : {
844 : CPLError( CE_Failure, CPLE_AppDefined,
845 : "Attempt to read shape with feature id (%d), but it is marked deleted.",
846 0 : iShape );
847 0 : return NULL;
848 : }
849 :
850 16051 : OGRFeature *poFeature = new OGRFeature( poDefn );
851 :
852 : /* -------------------------------------------------------------------- */
853 : /* Fetch geometry from Shapefile to OGRFeature. */
854 : /* -------------------------------------------------------------------- */
855 16051 : if( hSHP != NULL )
856 : {
857 15639 : OGRGeometry* poGeometry = NULL;
858 15639 : poGeometry = SHPReadOGRObject( hSHP, iShape, psShape );
859 :
860 : /*
861 : * NOTE - mloskot:
862 : * Two possibilities are expected here (bot hare tested by GDAL Autotests):
863 : * 1. Read valid geometry and assign it directly.
864 : * 2. Read and assign null geometry if it can not be read correctly from a shapefile
865 : *
866 : * It's NOT required here to test poGeometry == NULL.
867 : */
868 :
869 15639 : poFeature->SetGeometryDirectly( poGeometry );
870 : }
871 :
872 : /* -------------------------------------------------------------------- */
873 : /* Fetch feature attributes to OGRFeature fields. */
874 : /* -------------------------------------------------------------------- */
875 :
876 34615 : for( int iField = 0; iField < poDefn->GetFieldCount(); iField++ )
877 : {
878 : // Skip null fields.
879 18564 : if( DBFIsAttributeNULL( hDBF, iShape, iField ) )
880 370 : continue;
881 :
882 18194 : switch( poDefn->GetFieldDefn(iField)->GetType() )
883 : {
884 : case OFTString:
885 : poFeature->SetField( iField,
886 : DBFReadStringAttribute( hDBF, iShape,
887 1302 : iField ) );
888 1302 : break;
889 :
890 : case OFTInteger:
891 : poFeature->SetField( iField,
892 : DBFReadIntegerAttribute( hDBF, iShape,
893 432 : iField ) );
894 432 : break;
895 :
896 : case OFTReal:
897 : poFeature->SetField( iField,
898 : DBFReadDoubleAttribute( hDBF, iShape,
899 16458 : iField ) );
900 16458 : break;
901 :
902 : case OFTDate:
903 : {
904 : OGRField sFld;
905 : const char* pszDateValue =
906 2 : DBFReadStringAttribute(hDBF,iShape,iField);
907 :
908 2 : memset( &sFld, 0, sizeof(sFld) );
909 :
910 3 : if( pszDateValue[2] == '/' && pszDateValue[5] == '/'
911 : && strlen(pszDateValue) >= 10 )
912 : {
913 1 : sFld.Date.Month = (GByte)atoi(pszDateValue+0);
914 1 : sFld.Date.Day = (GByte)atoi(pszDateValue+3);
915 1 : sFld.Date.Year = (GInt16)atoi(pszDateValue+6);
916 : }
917 : else
918 : {
919 1 : int nFullDate = atoi(pszDateValue);
920 1 : sFld.Date.Year = (GInt16)(nFullDate / 10000);
921 1 : sFld.Date.Month = (GByte)((nFullDate / 100) % 100);
922 1 : sFld.Date.Day = (GByte)(nFullDate % 100);
923 : }
924 :
925 2 : poFeature->SetField( iField, &sFld );
926 : }
927 : break;
928 :
929 : default:
930 : CPLAssert( FALSE );
931 : }
932 : }
933 :
934 16051 : if( poFeature != NULL )
935 16051 : poFeature->SetFID( iShape );
936 :
937 16051 : return( poFeature );
938 : }
939 :
940 : /************************************************************************/
941 : /* SHPWriteOGRFeature() */
942 : /* */
943 : /* Write to an existing feature in a shapefile, or create a new */
944 : /* feature. */
945 : /************************************************************************/
946 :
947 15091 : OGRErr SHPWriteOGRFeature( SHPHandle hSHP, DBFHandle hDBF,
948 : OGRFeatureDefn * poDefn,
949 : OGRFeature * poFeature )
950 :
951 : {
952 : #ifdef notdef
953 : /* -------------------------------------------------------------------- */
954 : /* Don't write objects with missing geometry. */
955 : /* -------------------------------------------------------------------- */
956 : if( poFeature->GetGeometryRef() == NULL && hSHP != NULL )
957 : {
958 : CPLError( CE_Failure, CPLE_AppDefined,
959 : "Attempt to write feature without geometry not supported"
960 : " for shapefile driver." );
961 :
962 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
963 : }
964 : #endif
965 :
966 : /* -------------------------------------------------------------------- */
967 : /* Write the geometry. */
968 : /* -------------------------------------------------------------------- */
969 : OGRErr eErr;
970 :
971 15091 : if( hSHP != NULL )
972 : {
973 : eErr = SHPWriteOGRObject( hSHP, poFeature->GetFID(),
974 15069 : poFeature->GetGeometryRef() );
975 15069 : if( eErr != OGRERR_NONE )
976 37 : return eErr;
977 : }
978 :
979 : /* -------------------------------------------------------------------- */
980 : /* If there is no DBF, the job is done now. */
981 : /* -------------------------------------------------------------------- */
982 15054 : if( hDBF == NULL )
983 : {
984 : /* -------------------------------------------------------------------- */
985 : /* If this is a new feature, establish it's feature id. */
986 : /* -------------------------------------------------------------------- */
987 14 : if( hSHP != NULL && poFeature->GetFID() == OGRNullFID )
988 1 : poFeature->SetFID( hSHP->nRecords - 1 );
989 :
990 14 : return OGRERR_NONE;
991 : }
992 :
993 : /* -------------------------------------------------------------------- */
994 : /* If this is a new feature, establish it's feature id. */
995 : /* -------------------------------------------------------------------- */
996 15040 : if( poFeature->GetFID() == OGRNullFID )
997 15037 : poFeature->SetFID( DBFGetRecordCount( hDBF ) );
998 :
999 : /* -------------------------------------------------------------------- */
1000 : /* If this is the first feature to be written, verify that we */
1001 : /* have at least one attribute in the DBF file. If not, create */
1002 : /* a dummy FID attribute to satisfy the requirement that there */
1003 : /* be at least one attribute. */
1004 : /* -------------------------------------------------------------------- */
1005 15040 : if( DBFGetRecordCount( hDBF ) == 0 && DBFGetFieldCount( hDBF ) == 0 )
1006 : {
1007 : CPLDebug( "OGR",
1008 58 : "Created dummy FID field for shapefile since schema is empty.");
1009 58 : DBFAddField( hDBF, "FID", FTInteger, 11, 0 );
1010 : }
1011 :
1012 : /* -------------------------------------------------------------------- */
1013 : /* Write out dummy field value if it exists. */
1014 : /* -------------------------------------------------------------------- */
1015 15040 : if( DBFGetFieldCount( hDBF ) == 1 && poDefn->GetFieldCount() == 0 )
1016 : {
1017 : DBFWriteIntegerAttribute( hDBF, poFeature->GetFID(), 0,
1018 14701 : poFeature->GetFID() );
1019 : }
1020 :
1021 : /* -------------------------------------------------------------------- */
1022 : /* Write all the fields. */
1023 : /* -------------------------------------------------------------------- */
1024 15858 : for( int iField = 0; iField < poDefn->GetFieldCount(); iField++ )
1025 : {
1026 818 : if( !poFeature->IsFieldSet( iField ) )
1027 : {
1028 2 : DBFWriteNULLAttribute( hDBF, poFeature->GetFID(), iField );
1029 2 : continue;
1030 : }
1031 :
1032 816 : switch( poDefn->GetFieldDefn(iField)->GetType() )
1033 : {
1034 : case OFTString:
1035 : DBFWriteStringAttribute( hDBF, poFeature->GetFID(), iField,
1036 206 : poFeature->GetFieldAsString( iField ));
1037 206 : break;
1038 :
1039 : case OFTInteger:
1040 : DBFWriteIntegerAttribute( hDBF, poFeature->GetFID(), iField,
1041 167 : poFeature->GetFieldAsInteger(iField) );
1042 167 : break;
1043 :
1044 : case OFTReal:
1045 : DBFWriteDoubleAttribute( hDBF, poFeature->GetFID(), iField,
1046 442 : poFeature->GetFieldAsDouble(iField) );
1047 442 : break;
1048 :
1049 : case OFTDate:
1050 : {
1051 : int nYear, nMonth, nDay;
1052 :
1053 1 : if( poFeature->GetFieldAsDateTime( iField, &nYear, &nMonth, &nDay,
1054 : NULL, NULL, NULL, NULL ) )
1055 : {
1056 : DBFWriteIntegerAttribute( hDBF, poFeature->GetFID(), iField,
1057 1 : nYear*10000 + nMonth*100 + nDay );
1058 : }
1059 : }
1060 : break;
1061 :
1062 : default:
1063 : {
1064 : /* Ignore fields of other types */
1065 : break;
1066 : }
1067 : }
1068 : }
1069 :
1070 15040 : return OGRERR_NONE;
1071 : }
1072 :
|