1 /******************************************************************************
2 * $Id: shpopen.c,v 1.59 2008/03/14 05:25:31 fwarmerdam Exp $
5 * Purpose: Implementation of core Shapefile read/write functions.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
8 ******************************************************************************
9 * Copyright (c) 1999, 2001, Frank Warmerdam
11 * This software is available under the following "MIT Style" license,
12 * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This
13 * option is discussed in more detail in shapelib.html.
17 * Permission is hereby granted, free of charge, to any person obtaining a
18 * copy of this software and associated documentation files (the "Software"),
19 * to deal in the Software without restriction, including without limitation
20 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
21 * and/or sell copies of the Software, and to permit persons to whom the
22 * Software is furnished to do so, subject to the following conditions:
24 * The above copyright notice and this permission notice shall be included
25 * in all copies or substantial portions of the Software.
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
28 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
30 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33 * DEALINGS IN THE SOFTWARE.
34 ******************************************************************************
37 * Revision 1.59 2008/03/14 05:25:31 fwarmerdam
38 * Correct crash on buggy geometries (gdal #2218)
40 * Revision 1.58 2008/01/08 23:28:26 bram
41 * on line 2095, use a float instead of a double to avoid a compiler warning
43 * Revision 1.57 2007/12/06 07:00:25 fwarmerdam
44 * dbfopen now using SAHooks for fileio
46 * Revision 1.56 2007/12/04 20:37:56 fwarmerdam
47 * preliminary implementation of hooks api for io and errors
49 * Revision 1.55 2007/11/21 22:39:56 fwarmerdam
50 * close shx file in readonly mode (GDAL #1956)
52 * Revision 1.54 2007/11/15 00:12:47 mloskot
53 * Backported recent changes from GDAL (Ticket #1415) to Shapelib.
55 * Revision 1.53 2007/11/14 22:31:08 fwarmerdam
56 * checks after mallocs to detect for corrupted/voluntary broken shapefiles.
57 * http://trac.osgeo.org/gdal/ticket/1991
59 * Revision 1.52 2007/06/21 15:58:33 fwarmerdam
60 * fix for SHPRewindObject when rings touch at one vertex (gdal #976)
62 * Revision 1.51 2006/09/04 15:24:01 fwarmerdam
63 * Fixed up log message for 1.49.
65 * Revision 1.50 2006/09/04 15:21:39 fwarmerdam
68 * Revision 1.49 2006/09/04 15:21:00 fwarmerdam
69 * MLoskot: Added stronger test of Shapefile reading failures, e.g. truncated
70 * files. The problem was discovered by Tim Sutton and reported here
71 * https://svn.qgis.org/trac/ticket/200
73 * Revision 1.48 2006/01/26 15:07:32 fwarmerdam
74 * add bMeasureIsUsed flag from Craig Bruce: Bug 1249
76 * Revision 1.47 2006/01/04 20:07:23 fwarmerdam
77 * In SHPWriteObject() make sure that the record length is updated
78 * when rewriting an existing record.
80 * Revision 1.46 2005/02/11 17:17:46 fwarmerdam
81 * added panPartStart[0] validation
83 * Revision 1.45 2004/09/26 20:09:48 fwarmerdam
84 * const correctness changes
86 * Revision 1.44 2003/12/29 00:18:39 fwarmerdam
87 * added error checking for failed IO and optional CPL error reporting
89 * Revision 1.43 2003/12/01 16:20:08 warmerda
90 * be careful of zero vertex shapes
92 * Revision 1.42 2003/12/01 14:58:27 warmerda
93 * added degenerate object check in SHPRewindObject()
95 * Revision 1.41 2003/07/08 15:22:43 warmerda
98 * Revision 1.40 2003/04/21 18:30:37 warmerda
99 * added header write/update public methods
101 * Revision 1.39 2002/08/26 06:46:56 warmerda
104 * Revision 1.38 2002/05/07 16:43:39 warmerda
105 * Removed debugging printf.
107 * Revision 1.37 2002/04/10 17:35:22 warmerda
108 * fixed bug in ring reversal code
110 * Revision 1.36 2002/04/10 16:59:54 warmerda
111 * added SHPRewindObject
113 * Revision 1.35 2001/12/07 15:10:44 warmerda
114 * fix if .shx fails to open
116 * Revision 1.34 2001/11/01 16:29:55 warmerda
117 * move pabyRec into SHPInfo for thread safety
119 * Revision 1.33 2001/07/03 12:18:15 warmerda
120 * Improved cleanup if SHX not found, provied by Riccardo Cohen.
122 * Revision 1.32 2001/06/22 01:58:07 warmerda
123 * be more careful about establishing initial bounds in face of NULL shapes
125 * Revision 1.31 2001/05/31 19:35:29 warmerda
126 * added support for writing null shapes
128 * Revision 1.30 2001/05/28 12:46:29 warmerda
129 * Add some checking on reasonableness of record count when opening.
131 * Revision 1.29 2001/05/23 13:36:52 warmerda
132 * added use of SHPAPI_CALL
134 * Revision 1.28 2001/02/06 22:25:06 warmerda
135 * fixed memory leaks when SHPOpen() fails
137 * Revision 1.27 2000/07/18 15:21:33 warmerda
138 * added better enforcement of -1 for append in SHPWriteObject
140 * Revision 1.26 2000/02/16 16:03:51 warmerda
141 * added null shape support
143 * Revision 1.25 1999/12/15 13:47:07 warmerda
144 * Fixed record size settings in .shp file (was 4 words too long)
147 * Revision 1.24 1999/11/05 14:12:04 warmerda
148 * updated license terms
150 * Revision 1.23 1999/07/27 00:53:46 warmerda
151 * added support for rewriting shapes
153 * Revision 1.22 1999/06/11 19:19:11 warmerda
154 * Cleanup pabyRec static buffer on SHPClose().
156 * Revision 1.21 1999/06/02 14:57:56 kshih
157 * Remove unused variables
159 * Revision 1.20 1999/04/19 21:04:17 warmerda
160 * Fixed syntax error.
162 * Revision 1.19 1999/04/19 21:01:57 warmerda
163 * Force access string to binary in SHPOpen().
165 * Revision 1.18 1999/04/01 18:48:07 warmerda
166 * Try upper case extensions if lower case doesn't work.
168 * Revision 1.17 1998/12/31 15:29:39 warmerda
169 * Disable writing measure values to multipatch objects if
170 * DISABLE_MULTIPATCH_MEASURE is defined.
172 * Revision 1.16 1998/12/16 05:14:33 warmerda
173 * Added support to write MULTIPATCH. Fixed reading Z coordinate of
174 * MULTIPATCH. Fixed record size written for all feature types.
176 * Revision 1.15 1998/12/03 16:35:29 warmerda
177 * r+b is proper binary access string, not rb+.
179 * Revision 1.14 1998/12/03 15:47:56 warmerda
180 * Fixed setting of nVertices in SHPCreateObject().
182 * Revision 1.13 1998/12/03 15:33:54 warmerda
183 * Made SHPCalculateExtents() separately callable.
185 * Revision 1.12 1998/11/11 20:01:50 warmerda
186 * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines.
188 * Revision 1.11 1998/11/09 20:56:44 warmerda
189 * Fixed up handling of file wide bounds.
191 * Revision 1.10 1998/11/09 20:18:51 warmerda
192 * Converted to support 3D shapefiles, and use of SHPObject.
194 * Revision 1.9 1998/02/24 15:09:05 warmerda
197 * Revision 1.8 1997/12/04 15:40:29 warmerda
198 * Fixed byte swapping of record number, and record length fields in the
201 * Revision 1.7 1995/10/21 03:15:58 warmerda
202 * Added support for binary file access, the magic cookie 9997
203 * and tried to improve the int32 selection logic for 16bit systems.
205 * Revision 1.6 1995/09/04 04:19:41 warmerda
206 * Added fix for file bounds.
208 * Revision 1.5 1995/08/25 15:16:44 warmerda
209 * Fixed a couple of problems with big endian systems ... one with bounds
210 * and the other with multipart polygons.
212 * Revision 1.4 1995/08/24 18:10:17 warmerda
213 * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc()
214 * functions (such as on the Sun).
216 * Revision 1.3 1995/08/23 02:23:15 warmerda
217 * Added support for reading bounds, and fixed up problems in setting the
220 * Revision 1.2 1995/08/04 03:16:57 warmerda
225 #include "shapefil.h"
234 SHP_CVSID("$Id: shpopen.c,v 1.59 2008/03/14 05:25:31 fwarmerdam Exp $")
236 typedef unsigned char uchar;
238 #if UINT_MAX == 65535
249 #define ByteCopy( a, b, c ) memcpy( b, a, c )
251 # define MIN(a,b) ((a<b) ? a : b)
252 # define MAX(a,b) ((a>b) ? a : b)
255 static int bBigEndian;
258 /************************************************************************/
261 /* Swap a 2, 4 or 8 byte word. */
262 /************************************************************************/
264 static void SwapWord( int length, void * wordP )
270 for( i=0; i < length/2; i++ )
272 temp = ((uchar *) wordP)[i];
273 ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1];
274 ((uchar *) wordP)[length-i-1] = temp;
278 /************************************************************************/
281 /* A realloc cover function that will access a NULL pointer as */
283 /************************************************************************/
285 static void * SfRealloc( void * pMem, int nNewSize )
289 return( (void *) malloc(nNewSize) );
291 return( (void *) realloc(pMem,nNewSize) );
294 /************************************************************************/
295 /* SHPWriteHeader() */
297 /* Write out a header for the .shp and .shx files as well as the */
298 /* contents of the index (.shx) file. */
299 /************************************************************************/
301 void SHPWriteHeader( SHPHandle psSHP )
304 uchar abyHeader[100];
310 if (psSHP->fpSHX == NULL)
312 psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed");
316 /* -------------------------------------------------------------------- */
317 /* Prepare header block for .shp file. */
318 /* -------------------------------------------------------------------- */
319 for( i = 0; i < 100; i++ )
322 abyHeader[2] = 0x27; /* magic cookie */
325 i32 = psSHP->nFileSize/2; /* file size */
326 ByteCopy( &i32, abyHeader+24, 4 );
327 if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
329 i32 = 1000; /* version */
330 ByteCopy( &i32, abyHeader+28, 4 );
331 if( bBigEndian ) SwapWord( 4, abyHeader+28 );
333 i32 = psSHP->nShapeType; /* shape type */
334 ByteCopy( &i32, abyHeader+32, 4 );
335 if( bBigEndian ) SwapWord( 4, abyHeader+32 );
337 dValue = psSHP->adBoundsMin[0]; /* set bounds */
338 ByteCopy( &dValue, abyHeader+36, 8 );
339 if( bBigEndian ) SwapWord( 8, abyHeader+36 );
341 dValue = psSHP->adBoundsMin[1];
342 ByteCopy( &dValue, abyHeader+44, 8 );
343 if( bBigEndian ) SwapWord( 8, abyHeader+44 );
345 dValue = psSHP->adBoundsMax[0];
346 ByteCopy( &dValue, abyHeader+52, 8 );
347 if( bBigEndian ) SwapWord( 8, abyHeader+52 );
349 dValue = psSHP->adBoundsMax[1];
350 ByteCopy( &dValue, abyHeader+60, 8 );
351 if( bBigEndian ) SwapWord( 8, abyHeader+60 );
353 dValue = psSHP->adBoundsMin[2]; /* z */
354 ByteCopy( &dValue, abyHeader+68, 8 );
355 if( bBigEndian ) SwapWord( 8, abyHeader+68 );
357 dValue = psSHP->adBoundsMax[2];
358 ByteCopy( &dValue, abyHeader+76, 8 );
359 if( bBigEndian ) SwapWord( 8, abyHeader+76 );
361 dValue = psSHP->adBoundsMin[3]; /* m */
362 ByteCopy( &dValue, abyHeader+84, 8 );
363 if( bBigEndian ) SwapWord( 8, abyHeader+84 );
365 dValue = psSHP->adBoundsMax[3];
366 ByteCopy( &dValue, abyHeader+92, 8 );
367 if( bBigEndian ) SwapWord( 8, abyHeader+92 );
369 /* -------------------------------------------------------------------- */
370 /* Write .shp file header. */
371 /* -------------------------------------------------------------------- */
372 if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0
373 || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHP ) != 1 )
375 psSHP->sHooks.Error( "Failure writing .shp header" );
379 /* -------------------------------------------------------------------- */
380 /* Prepare, and write .shx file header. */
381 /* -------------------------------------------------------------------- */
382 i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */
383 ByteCopy( &i32, abyHeader+24, 4 );
384 if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
386 if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0
387 || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 )
389 psSHP->sHooks.Error( "Failure writing .shx header" );
393 /* -------------------------------------------------------------------- */
394 /* Write out the .shx contents. */
395 /* -------------------------------------------------------------------- */
396 panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords);
398 for( i = 0; i < psSHP->nRecords; i++ )
400 panSHX[i*2 ] = psSHP->panRecOffset[i]/2;
401 panSHX[i*2+1] = psSHP->panRecSize[i]/2;
402 if( !bBigEndian ) SwapWord( 4, panSHX+i*2 );
403 if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 );
406 if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX )
409 psSHP->sHooks.Error( "Failure writing .shx contents" );
414 /* -------------------------------------------------------------------- */
416 /* -------------------------------------------------------------------- */
417 psSHP->sHooks.FFlush( psSHP->fpSHP );
418 psSHP->sHooks.FFlush( psSHP->fpSHX );
421 /************************************************************************/
423 /************************************************************************/
425 SHPHandle SHPAPI_CALL
426 SHPOpen( const char * pszLayer, const char * pszAccess )
431 SASetupDefaultHooks( &sHooks );
433 return SHPOpenLL( pszLayer, pszAccess, &sHooks );
436 /************************************************************************/
439 /* Open the .shp and .shx files based on the basename of the */
440 /* files or either file name. */
441 /************************************************************************/
443 SHPHandle SHPAPI_CALL
444 SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks )
447 char *pszFullname, *pszBasename;
454 /* -------------------------------------------------------------------- */
455 /* Ensure the access string is one of the legal ones. We */
456 /* ensure the result string indicates binary to avoid common */
457 /* problems on Windows. */
458 /* -------------------------------------------------------------------- */
459 if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0
460 || strcmp(pszAccess,"r+") == 0 )
465 /* -------------------------------------------------------------------- */
466 /* Establish the byte order on this machine. */
467 /* -------------------------------------------------------------------- */
469 if( *((uchar *) &i) == 1 )
474 /* -------------------------------------------------------------------- */
475 /* Initialize the info structure. */
476 /* -------------------------------------------------------------------- */
477 psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1);
479 psSHP->bUpdated = FALSE;
480 memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) );
482 /* -------------------------------------------------------------------- */
483 /* Compute the base (layer) name. If there is any extension */
484 /* on the passed in filename we will strip it off. */
485 /* -------------------------------------------------------------------- */
486 pszBasename = (char *) malloc(strlen(pszLayer)+5);
487 strcpy( pszBasename, pszLayer );
488 for( i = strlen(pszBasename)-1;
489 i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
490 && pszBasename[i] != '\\';
493 if( pszBasename[i] == '.' )
494 pszBasename[i] = '\0';
496 /* -------------------------------------------------------------------- */
497 /* Open the .shp and .shx files. Note that files pulled from */
498 /* a PC to Unix with upper case filenames won't work! */
499 /* -------------------------------------------------------------------- */
500 pszFullname = (char *) malloc(strlen(pszBasename) + 5);
501 sprintf( pszFullname, "%s.shp", pszBasename ) ;
502 psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess );
503 if( psSHP->fpSHP == NULL )
505 sprintf( pszFullname, "%s.SHP", pszBasename );
506 psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess );
509 if( psSHP->fpSHP == NULL )
512 CPLError( CE_Failure, CPLE_OpenFailed,
513 "Unable to open %s.shp or %s.SHP.",
514 pszBasename, pszBasename );
522 sprintf( pszFullname, "%s.shx", pszBasename );
523 psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess );
524 if( psSHP->fpSHX == NULL )
526 sprintf( pszFullname, "%s.SHX", pszBasename );
527 psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess );
530 if( psSHP->fpSHX == NULL )
533 CPLError( CE_Failure, CPLE_OpenFailed,
534 "Unable to open %s.shx or %s.SHX.",
535 pszBasename, pszBasename );
537 psSHP->sHooks.FClose( psSHP->fpSHP );
547 /* -------------------------------------------------------------------- */
548 /* Read the file size from the SHP file. */
549 /* -------------------------------------------------------------------- */
550 pabyBuf = (uchar *) malloc(100);
551 psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHP );
553 psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256
554 + pabyBuf[25] * 256 * 256
558 /* -------------------------------------------------------------------- */
559 /* Read SHX file Header info */
560 /* -------------------------------------------------------------------- */
561 if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1
564 || pabyBuf[2] != 0x27
565 || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) )
567 psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." );
568 psSHP->sHooks.FClose( psSHP->fpSHP );
569 psSHP->sHooks.FClose( psSHP->fpSHX );
575 psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256
576 + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256;
577 psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8;
579 psSHP->nShapeType = pabyBuf[32];
581 if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 )
586 "Record count in .shp header is %d, which seems\n"
587 "unreasonable. Assuming header is corrupt.",
589 psSHP->sHooks.Error( szError );
590 psSHP->sHooks.FClose( psSHP->fpSHP );
591 psSHP->sHooks.FClose( psSHP->fpSHX );
598 /* -------------------------------------------------------------------- */
599 /* Read the bounds. */
600 /* -------------------------------------------------------------------- */
601 if( bBigEndian ) SwapWord( 8, pabyBuf+36 );
602 memcpy( &dValue, pabyBuf+36, 8 );
603 psSHP->adBoundsMin[0] = dValue;
605 if( bBigEndian ) SwapWord( 8, pabyBuf+44 );
606 memcpy( &dValue, pabyBuf+44, 8 );
607 psSHP->adBoundsMin[1] = dValue;
609 if( bBigEndian ) SwapWord( 8, pabyBuf+52 );
610 memcpy( &dValue, pabyBuf+52, 8 );
611 psSHP->adBoundsMax[0] = dValue;
613 if( bBigEndian ) SwapWord( 8, pabyBuf+60 );
614 memcpy( &dValue, pabyBuf+60, 8 );
615 psSHP->adBoundsMax[1] = dValue;
617 if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */
618 memcpy( &dValue, pabyBuf+68, 8 );
619 psSHP->adBoundsMin[2] = dValue;
621 if( bBigEndian ) SwapWord( 8, pabyBuf+76 );
622 memcpy( &dValue, pabyBuf+76, 8 );
623 psSHP->adBoundsMax[2] = dValue;
625 if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */
626 memcpy( &dValue, pabyBuf+84, 8 );
627 psSHP->adBoundsMin[3] = dValue;
629 if( bBigEndian ) SwapWord( 8, pabyBuf+92 );
630 memcpy( &dValue, pabyBuf+92, 8 );
631 psSHP->adBoundsMax[3] = dValue;
635 /* -------------------------------------------------------------------- */
636 /* Read the .shx file to get the offsets to each record in */
638 /* -------------------------------------------------------------------- */
639 psSHP->nMaxRecords = psSHP->nRecords;
641 psSHP->panRecOffset =
642 (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );
644 (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );
645 pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) );
647 if (psSHP->panRecOffset == NULL ||
648 psSHP->panRecSize == NULL ||
654 "Not enough memory to allocate requested memory (nRecords=%d).\n"
655 "Probably broken SHP file",
657 psSHP->sHooks.Error( szError );
658 psSHP->sHooks.FClose( psSHP->fpSHP );
659 psSHP->sHooks.FClose( psSHP->fpSHX );
660 if (psSHP->panRecOffset) free( psSHP->panRecOffset );
661 if (psSHP->panRecSize) free( psSHP->panRecSize );
662 if (pabyBuf) free( pabyBuf );
667 if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX )
673 "Failed to read all values for %d records in .shx file.",
675 psSHP->sHooks.Error( szError );
677 /* SHX is short or unreadable for some reason. */
678 psSHP->sHooks.FClose( psSHP->fpSHP );
679 psSHP->sHooks.FClose( psSHP->fpSHX );
680 free( psSHP->panRecOffset );
681 free( psSHP->panRecSize );
688 /* In read-only mode, we can close the SHX now */
689 if (strcmp(pszAccess, "rb") == 0)
691 psSHP->sHooks.FClose( psSHP->fpSHX );
695 for( i = 0; i < psSHP->nRecords; i++ )
697 int32 nOffset, nLength;
699 memcpy( &nOffset, pabyBuf + i * 8, 4 );
700 if( !bBigEndian ) SwapWord( 4, &nOffset );
702 memcpy( &nLength, pabyBuf + i * 8 + 4, 4 );
703 if( !bBigEndian ) SwapWord( 4, &nLength );
705 psSHP->panRecOffset[i] = nOffset*2;
706 psSHP->panRecSize[i] = nLength*2;
713 /************************************************************************/
716 /* Close the .shp and .shx files. */
717 /************************************************************************/
720 SHPClose(SHPHandle psSHP )
726 /* -------------------------------------------------------------------- */
727 /* Update the header if we have modified anything. */
728 /* -------------------------------------------------------------------- */
729 if( psSHP->bUpdated )
730 SHPWriteHeader( psSHP );
732 /* -------------------------------------------------------------------- */
733 /* Free all resources, and close files. */
734 /* -------------------------------------------------------------------- */
735 free( psSHP->panRecOffset );
736 free( psSHP->panRecSize );
738 if ( psSHP->fpSHX != NULL)
739 psSHP->sHooks.FClose( psSHP->fpSHX );
740 psSHP->sHooks.FClose( psSHP->fpSHP );
742 if( psSHP->pabyRec != NULL )
744 free( psSHP->pabyRec );
750 /************************************************************************/
753 /* Fetch general information about the shape file. */
754 /************************************************************************/
757 SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType,
758 double * padfMinBound, double * padfMaxBound )
766 if( pnEntities != NULL )
767 *pnEntities = psSHP->nRecords;
769 if( pnShapeType != NULL )
770 *pnShapeType = psSHP->nShapeType;
772 for( i = 0; i < 4; i++ )
774 if( padfMinBound != NULL )
775 padfMinBound[i] = psSHP->adBoundsMin[i];
776 if( padfMaxBound != NULL )
777 padfMaxBound[i] = psSHP->adBoundsMax[i];
781 /************************************************************************/
784 /* Create a new shape file and return a handle to the open */
785 /* shape file with read/write access. */
786 /************************************************************************/
788 SHPHandle SHPAPI_CALL
789 SHPCreate( const char * pszLayer, int nShapeType )
794 SASetupDefaultHooks( &sHooks );
796 return SHPCreateLL( pszLayer, nShapeType, &sHooks );
799 /************************************************************************/
802 /* Create a new shape file and return a handle to the open */
803 /* shape file with read/write access. */
804 /************************************************************************/
806 SHPHandle SHPAPI_CALL
807 SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks )
810 char *pszBasename, *pszFullname;
813 uchar abyHeader[100];
817 /* -------------------------------------------------------------------- */
818 /* Establish the byte order on this system. */
819 /* -------------------------------------------------------------------- */
821 if( *((uchar *) &i) == 1 )
826 /* -------------------------------------------------------------------- */
827 /* Compute the base (layer) name. If there is any extension */
828 /* on the passed in filename we will strip it off. */
829 /* -------------------------------------------------------------------- */
830 pszBasename = (char *) malloc(strlen(pszLayer)+5);
831 strcpy( pszBasename, pszLayer );
832 for( i = strlen(pszBasename)-1;
833 i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
834 && pszBasename[i] != '\\';
837 if( pszBasename[i] == '.' )
838 pszBasename[i] = '\0';
840 /* -------------------------------------------------------------------- */
841 /* Open the two files so we can write their headers. */
842 /* -------------------------------------------------------------------- */
843 pszFullname = (char *) malloc(strlen(pszBasename) + 5);
844 sprintf( pszFullname, "%s.shp", pszBasename );
845 fpSHP = psHooks->FOpen(pszFullname, "wb" );
848 psHooks->Error( "Failed to create file .shp file." );
852 sprintf( pszFullname, "%s.shx", pszBasename );
853 fpSHX = psHooks->FOpen(pszFullname, "wb" );
856 psHooks->Error( "Failed to create file .shx file." );
863 /* -------------------------------------------------------------------- */
864 /* Prepare header block for .shp file. */
865 /* -------------------------------------------------------------------- */
866 for( i = 0; i < 100; i++ )
869 abyHeader[2] = 0x27; /* magic cookie */
872 i32 = 50; /* file size */
873 ByteCopy( &i32, abyHeader+24, 4 );
874 if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
876 i32 = 1000; /* version */
877 ByteCopy( &i32, abyHeader+28, 4 );
878 if( bBigEndian ) SwapWord( 4, abyHeader+28 );
880 i32 = nShapeType; /* shape type */
881 ByteCopy( &i32, abyHeader+32, 4 );
882 if( bBigEndian ) SwapWord( 4, abyHeader+32 );
884 dValue = 0.0; /* set bounds */
885 ByteCopy( &dValue, abyHeader+36, 8 );
886 ByteCopy( &dValue, abyHeader+44, 8 );
887 ByteCopy( &dValue, abyHeader+52, 8 );
888 ByteCopy( &dValue, abyHeader+60, 8 );
890 /* -------------------------------------------------------------------- */
891 /* Write .shp file header. */
892 /* -------------------------------------------------------------------- */
893 if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 )
895 psHooks->Error( "Failed to write .shp header." );
899 /* -------------------------------------------------------------------- */
900 /* Prepare, and write .shx file header. */
901 /* -------------------------------------------------------------------- */
902 i32 = 50; /* file size */
903 ByteCopy( &i32, abyHeader+24, 4 );
904 if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
906 if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 )
908 psHooks->Error( "Failed to write .shx header." );
912 /* -------------------------------------------------------------------- */
913 /* Close the files, and then open them as regular existing files. */
914 /* -------------------------------------------------------------------- */
915 psHooks->FClose( fpSHP );
916 psHooks->FClose( fpSHX );
918 return( SHPOpenLL( pszLayer, "r+b", psHooks ) );
921 /************************************************************************/
922 /* _SHPSetBounds() */
924 /* Compute a bounds rectangle for a shape, and set it into the */
925 /* indicated location in the record. */
926 /************************************************************************/
928 static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape )
931 ByteCopy( &(psShape->dfXMin), pabyRec + 0, 8 );
932 ByteCopy( &(psShape->dfYMin), pabyRec + 8, 8 );
933 ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 );
934 ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 );
938 SwapWord( 8, pabyRec + 0 );
939 SwapWord( 8, pabyRec + 8 );
940 SwapWord( 8, pabyRec + 16 );
941 SwapWord( 8, pabyRec + 24 );
945 /************************************************************************/
946 /* SHPComputeExtents() */
948 /* Recompute the extents of a shape. Automatically done by */
949 /* SHPCreateObject(). */
950 /************************************************************************/
953 SHPComputeExtents( SHPObject * psObject )
958 /* -------------------------------------------------------------------- */
959 /* Build extents for this object. */
960 /* -------------------------------------------------------------------- */
961 if( psObject->nVertices > 0 )
963 psObject->dfXMin = psObject->dfXMax = psObject->padfX[0];
964 psObject->dfYMin = psObject->dfYMax = psObject->padfY[0];
965 psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0];
966 psObject->dfMMin = psObject->dfMMax = psObject->padfM[0];
969 for( i = 0; i < psObject->nVertices; i++ )
971 psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]);
972 psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]);
973 psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]);
974 psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]);
976 psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]);
977 psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]);
978 psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]);
979 psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]);
983 /************************************************************************/
984 /* SHPCreateObject() */
986 /* Create a shape object. It should be freed with */
987 /* SHPDestroyObject(). */
988 /************************************************************************/
990 SHPObject SHPAPI_CALL1(*)
991 SHPCreateObject( int nSHPType, int nShapeId, int nParts,
992 const int * panPartStart, const int * panPartType,
993 int nVertices, const double *padfX, const double *padfY,
994 const double * padfZ, const double * padfM )
1000 psObject = (SHPObject *) calloc(1,sizeof(SHPObject));
1001 psObject->nSHPType = nSHPType;
1002 psObject->nShapeId = nShapeId;
1003 psObject->bMeasureIsUsed = FALSE;
1005 /* -------------------------------------------------------------------- */
1006 /* Establish whether this shape type has M, and Z values. */
1007 /* -------------------------------------------------------------------- */
1008 if( nSHPType == SHPT_ARCM
1009 || nSHPType == SHPT_POINTM
1010 || nSHPType == SHPT_POLYGONM
1011 || nSHPType == SHPT_MULTIPOINTM )
1016 else if( nSHPType == SHPT_ARCZ
1017 || nSHPType == SHPT_POINTZ
1018 || nSHPType == SHPT_POLYGONZ
1019 || nSHPType == SHPT_MULTIPOINTZ
1020 || nSHPType == SHPT_MULTIPATCH )
1031 /* -------------------------------------------------------------------- */
1032 /* Capture parts. Note that part type is optional, and */
1033 /* defaults to ring. */
1034 /* -------------------------------------------------------------------- */
1035 if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON
1036 || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM
1037 || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ
1038 || nSHPType == SHPT_MULTIPATCH )
1040 psObject->nParts = MAX(1,nParts);
1042 psObject->panPartStart = (int *)
1043 malloc(sizeof(int) * psObject->nParts);
1044 psObject->panPartType = (int *)
1045 malloc(sizeof(int) * psObject->nParts);
1047 psObject->panPartStart[0] = 0;
1048 psObject->panPartType[0] = SHPP_RING;
1050 for( i = 0; i < nParts; i++ )
1052 psObject->panPartStart[i] = panPartStart[i];
1054 if( panPartType != NULL )
1055 psObject->panPartType[i] = panPartType[i];
1057 psObject->panPartType[i] = SHPP_RING;
1060 if( psObject->panPartStart[0] != 0 )
1061 psObject->panPartStart[0] = 0;
1064 /* -------------------------------------------------------------------- */
1065 /* Capture vertices. Note that Z and M are optional, but X and */
1067 /* -------------------------------------------------------------------- */
1070 psObject->padfX = (double *) calloc(sizeof(double),nVertices);
1071 psObject->padfY = (double *) calloc(sizeof(double),nVertices);
1072 psObject->padfZ = (double *) calloc(sizeof(double),nVertices);
1073 psObject->padfM = (double *) calloc(sizeof(double),nVertices);
1075 assert( padfX != NULL );
1076 assert( padfY != NULL );
1078 for( i = 0; i < nVertices; i++ )
1080 psObject->padfX[i] = padfX[i];
1081 psObject->padfY[i] = padfY[i];
1082 if( padfZ != NULL && bHasZ )
1083 psObject->padfZ[i] = padfZ[i];
1084 if( padfM != NULL && bHasM )
1085 psObject->padfM[i] = padfM[i];
1087 if( padfM != NULL && bHasM )
1088 psObject->bMeasureIsUsed = TRUE;
1091 /* -------------------------------------------------------------------- */
1092 /* Compute the extents. */
1093 /* -------------------------------------------------------------------- */
1094 psObject->nVertices = nVertices;
1095 SHPComputeExtents( psObject );
1100 /************************************************************************/
1101 /* SHPCreateSimpleObject() */
1103 /* Create a simple (common) shape object. Destroy with */
1104 /* SHPDestroyObject(). */
1105 /************************************************************************/
1107 SHPObject SHPAPI_CALL1(*)
1108 SHPCreateSimpleObject( int nSHPType, int nVertices,
1109 const double * padfX, const double * padfY,
1110 const double * padfZ )
1113 return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL,
1114 nVertices, padfX, padfY, padfZ, NULL ) );
1117 /************************************************************************/
1118 /* SHPWriteObject() */
1120 /* Write out the vertices of a new structure. Note that it is */
1121 /* only possible to write vertices at the end of the file. */
1122 /************************************************************************/
1125 SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject )
1128 int nRecordOffset, i, nRecordSize=0;
1132 psSHP->bUpdated = TRUE;
1134 /* -------------------------------------------------------------------- */
1135 /* Ensure that shape object matches the type of the file it is */
1136 /* being written to. */
1137 /* -------------------------------------------------------------------- */
1138 assert( psObject->nSHPType == psSHP->nShapeType
1139 || psObject->nSHPType == SHPT_NULL );
1141 /* -------------------------------------------------------------------- */
1142 /* Ensure that -1 is used for appends. Either blow an */
1143 /* assertion, or if they are disabled, set the shapeid to -1 */
1145 /* -------------------------------------------------------------------- */
1146 assert( nShapeId == -1
1147 || (nShapeId >= 0 && nShapeId < psSHP->nRecords) );
1149 if( nShapeId != -1 && nShapeId >= psSHP->nRecords )
1152 /* -------------------------------------------------------------------- */
1153 /* Add the new entity to the in memory index. */
1154 /* -------------------------------------------------------------------- */
1155 if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords )
1157 psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100);
1159 psSHP->panRecOffset = (int *)
1160 SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords );
1161 psSHP->panRecSize = (int *)
1162 SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords );
1165 /* -------------------------------------------------------------------- */
1166 /* Initialize record. */
1167 /* -------------------------------------------------------------------- */
1168 pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double)
1169 + psObject->nParts * 8 + 128);
1171 /* -------------------------------------------------------------------- */
1172 /* Extract vertices for a Polygon or Arc. */
1173 /* -------------------------------------------------------------------- */
1174 if( psObject->nSHPType == SHPT_POLYGON
1175 || psObject->nSHPType == SHPT_POLYGONZ
1176 || psObject->nSHPType == SHPT_POLYGONM
1177 || psObject->nSHPType == SHPT_ARC
1178 || psObject->nSHPType == SHPT_ARCZ
1179 || psObject->nSHPType == SHPT_ARCM
1180 || psObject->nSHPType == SHPT_MULTIPATCH )
1182 int32 nPoints, nParts;
1185 nPoints = psObject->nVertices;
1186 nParts = psObject->nParts;
1188 _SHPSetBounds( pabyRec + 12, psObject );
1190 if( bBigEndian ) SwapWord( 4, &nPoints );
1191 if( bBigEndian ) SwapWord( 4, &nParts );
1193 ByteCopy( &nPoints, pabyRec + 40 + 8, 4 );
1194 ByteCopy( &nParts, pabyRec + 36 + 8, 4 );
1199 * Write part start positions.
1201 ByteCopy( psObject->panPartStart, pabyRec + 44 + 8,
1202 4 * psObject->nParts );
1203 for( i = 0; i < psObject->nParts; i++ )
1205 if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i );
1210 * Write multipatch part types if needed.
1212 if( psObject->nSHPType == SHPT_MULTIPATCH )
1214 memcpy( pabyRec + nRecordSize, psObject->panPartType,
1215 4*psObject->nParts );
1216 for( i = 0; i < psObject->nParts; i++ )
1218 if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize );
1224 * Write the (x,y) vertex values.
1226 for( i = 0; i < psObject->nVertices; i++ )
1228 ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 );
1229 ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 );
1232 SwapWord( 8, pabyRec + nRecordSize );
1235 SwapWord( 8, pabyRec + nRecordSize + 8 );
1237 nRecordSize += 2 * 8;
1241 * Write the Z coordinates (if any).
1243 if( psObject->nSHPType == SHPT_POLYGONZ
1244 || psObject->nSHPType == SHPT_ARCZ
1245 || psObject->nSHPType == SHPT_MULTIPATCH )
1247 ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
1248 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1251 ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
1252 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1255 for( i = 0; i < psObject->nVertices; i++ )
1257 ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
1258 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1264 * Write the M values, if any.
1266 if( psObject->bMeasureIsUsed
1267 && (psObject->nSHPType == SHPT_POLYGONM
1268 || psObject->nSHPType == SHPT_ARCM
1269 #ifndef DISABLE_MULTIPATCH_MEASURE
1270 || psObject->nSHPType == SHPT_MULTIPATCH
1272 || psObject->nSHPType == SHPT_POLYGONZ
1273 || psObject->nSHPType == SHPT_ARCZ) )
1275 ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
1276 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1279 ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
1280 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1283 for( i = 0; i < psObject->nVertices; i++ )
1285 ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
1286 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1292 /* -------------------------------------------------------------------- */
1293 /* Extract vertices for a MultiPoint. */
1294 /* -------------------------------------------------------------------- */
1295 else if( psObject->nSHPType == SHPT_MULTIPOINT
1296 || psObject->nSHPType == SHPT_MULTIPOINTZ
1297 || psObject->nSHPType == SHPT_MULTIPOINTM )
1302 nPoints = psObject->nVertices;
1304 _SHPSetBounds( pabyRec + 12, psObject );
1306 if( bBigEndian ) SwapWord( 4, &nPoints );
1307 ByteCopy( &nPoints, pabyRec + 44, 4 );
1309 for( i = 0; i < psObject->nVertices; i++ )
1311 ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 );
1312 ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 );
1314 if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 );
1315 if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 );
1318 nRecordSize = 48 + 16 * psObject->nVertices;
1320 if( psObject->nSHPType == SHPT_MULTIPOINTZ )
1322 ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
1323 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1326 ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
1327 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1330 for( i = 0; i < psObject->nVertices; i++ )
1332 ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
1333 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1338 if( psObject->bMeasureIsUsed
1339 && (psObject->nSHPType == SHPT_MULTIPOINTZ
1340 || psObject->nSHPType == SHPT_MULTIPOINTM) )
1342 ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
1343 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1346 ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
1347 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1350 for( i = 0; i < psObject->nVertices; i++ )
1352 ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
1353 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1359 /* -------------------------------------------------------------------- */
1361 /* -------------------------------------------------------------------- */
1362 else if( psObject->nSHPType == SHPT_POINT
1363 || psObject->nSHPType == SHPT_POINTZ
1364 || psObject->nSHPType == SHPT_POINTM )
1366 ByteCopy( psObject->padfX, pabyRec + 12, 8 );
1367 ByteCopy( psObject->padfY, pabyRec + 20, 8 );
1369 if( bBigEndian ) SwapWord( 8, pabyRec + 12 );
1370 if( bBigEndian ) SwapWord( 8, pabyRec + 20 );
1374 if( psObject->nSHPType == SHPT_POINTZ )
1376 ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 );
1377 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1381 if( psObject->bMeasureIsUsed
1382 && (psObject->nSHPType == SHPT_POINTZ
1383 || psObject->nSHPType == SHPT_POINTM) )
1385 ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 );
1386 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1391 /* -------------------------------------------------------------------- */
1392 /* Not much to do for null geometries. */
1393 /* -------------------------------------------------------------------- */
1394 else if( psObject->nSHPType == SHPT_NULL )
1405 /* -------------------------------------------------------------------- */
1406 /* Establish where we are going to put this record. If we are */
1407 /* rewriting and existing record, and it will fit, then put it */
1408 /* back where the original came from. Otherwise write at the end. */
1409 /* -------------------------------------------------------------------- */
1410 if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 )
1412 if( nShapeId == -1 )
1413 nShapeId = psSHP->nRecords++;
1415 psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize;
1416 psSHP->panRecSize[nShapeId] = nRecordSize-8;
1417 psSHP->nFileSize += nRecordSize;
1421 nRecordOffset = psSHP->panRecOffset[nShapeId];
1422 psSHP->panRecSize[nShapeId] = nRecordSize-8;
1425 /* -------------------------------------------------------------------- */
1426 /* Set the shape type, record number, and record size. */
1427 /* -------------------------------------------------------------------- */
1428 i32 = nShapeId+1; /* record # */
1429 if( !bBigEndian ) SwapWord( 4, &i32 );
1430 ByteCopy( &i32, pabyRec, 4 );
1432 i32 = (nRecordSize-8)/2; /* record size */
1433 if( !bBigEndian ) SwapWord( 4, &i32 );
1434 ByteCopy( &i32, pabyRec + 4, 4 );
1436 i32 = psObject->nSHPType; /* shape type */
1437 if( bBigEndian ) SwapWord( 4, &i32 );
1438 ByteCopy( &i32, pabyRec + 8, 4 );
1440 /* -------------------------------------------------------------------- */
1441 /* Write out record. */
1442 /* -------------------------------------------------------------------- */
1443 if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0
1444 || psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 )
1446 psSHP->sHooks.Error( "Error in psSHP->sHooks.FSeek() or fwrite() writing object to .shp file." );
1453 /* -------------------------------------------------------------------- */
1454 /* Expand file wide bounds based on this shape. */
1455 /* -------------------------------------------------------------------- */
1456 if( psSHP->adBoundsMin[0] == 0.0
1457 && psSHP->adBoundsMax[0] == 0.0
1458 && psSHP->adBoundsMin[1] == 0.0
1459 && psSHP->adBoundsMax[1] == 0.0 )
1461 if( psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0 )
1463 psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0;
1464 psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0;
1465 psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0;
1466 psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0;
1470 psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0];
1471 psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0];
1472 psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0];
1473 psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0];
1477 for( i = 0; i < psObject->nVertices; i++ )
1479 psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]);
1480 psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]);
1481 psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]);
1482 psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]);
1483 psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]);
1484 psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]);
1485 psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]);
1486 psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]);
1492 /************************************************************************/
1493 /* SHPReadObject() */
1495 /* Read the vertices, parts, and other non-attribute information */
1496 /* for one shape. */
1497 /************************************************************************/
1499 SHPObject SHPAPI_CALL1(*)
1500 SHPReadObject( SHPHandle psSHP, int hEntity )
1503 int nEntitySize, nRequiredSize;
1505 char pszErrorMsg[128];
1507 /* -------------------------------------------------------------------- */
1508 /* Validate the record/entity number. */
1509 /* -------------------------------------------------------------------- */
1510 if( hEntity < 0 || hEntity >= psSHP->nRecords )
1513 /* -------------------------------------------------------------------- */
1514 /* Ensure our record buffer is large enough. */
1515 /* -------------------------------------------------------------------- */
1516 nEntitySize = psSHP->panRecSize[hEntity]+8;
1517 if( nEntitySize > psSHP->nBufSize )
1519 psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize);
1520 if (psSHP->pabyRec == NULL)
1524 /* Reallocate previous successfull size for following features */
1525 psSHP->pabyRec = malloc(psSHP->nBufSize);
1528 "Not enough memory to allocate requested memory (nBufSize=%d). "
1529 "Probably broken SHP file", psSHP->nBufSize );
1530 psSHP->sHooks.Error( szError );
1534 /* Only set new buffer size after successfull alloc */
1535 psSHP->nBufSize = nEntitySize;
1538 /* In case we were not able to reallocate the buffer on a previous step */
1539 if (psSHP->pabyRec == NULL)
1544 /* -------------------------------------------------------------------- */
1545 /* Read the record. */
1546 /* -------------------------------------------------------------------- */
1547 if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0
1548 || psSHP->sHooks.FRead( psSHP->pabyRec, nEntitySize, 1,
1549 psSHP->fpSHP ) != 1 )
1552 * TODO - mloskot: Consider detailed diagnostics of shape file,
1553 * for example to detect if file is truncated.
1556 psSHP->sHooks.Error( "Error in fseek() or fread() reading object from .shp file." );
1560 /* -------------------------------------------------------------------- */
1561 /* Allocate and minimally initialize the object. */
1562 /* -------------------------------------------------------------------- */
1563 psShape = (SHPObject *) calloc(1,sizeof(SHPObject));
1564 psShape->nShapeId = hEntity;
1565 psShape->bMeasureIsUsed = FALSE;
1567 if ( 8 + 4 > nEntitySize )
1569 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d",
1570 hEntity, nEntitySize);
1571 psSHP->sHooks.Error( pszErrorMsg );
1572 SHPDestroyObject(psShape);
1575 memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 );
1577 if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) );
1579 /* ==================================================================== */
1580 /* Extract vertices for a Polygon or Arc. */
1581 /* ==================================================================== */
1582 if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC
1583 || psShape->nSHPType == SHPT_POLYGONZ
1584 || psShape->nSHPType == SHPT_POLYGONM
1585 || psShape->nSHPType == SHPT_ARCZ
1586 || psShape->nSHPType == SHPT_ARCM
1587 || psShape->nSHPType == SHPT_MULTIPATCH )
1589 int32 nPoints, nParts;
1592 if ( 40 + 8 + 4 > nEntitySize )
1594 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d",
1595 hEntity, nEntitySize);
1596 psSHP->sHooks.Error( pszErrorMsg );
1597 SHPDestroyObject(psShape);
1600 /* -------------------------------------------------------------------- */
1601 /* Get the X/Y bounds. */
1602 /* -------------------------------------------------------------------- */
1603 memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 );
1604 memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
1605 memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
1606 memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
1608 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
1609 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
1610 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
1611 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
1613 /* -------------------------------------------------------------------- */
1614 /* Extract part/point count, and build vertex and part arrays */
1615 /* to proper size. */
1616 /* -------------------------------------------------------------------- */
1617 memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 );
1618 memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 );
1620 if( bBigEndian ) SwapWord( 4, &nPoints );
1621 if( bBigEndian ) SwapWord( 4, &nParts );
1623 if (nPoints < 0 || nParts < 0 ||
1624 nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000)
1626 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.",
1627 hEntity, nPoints, nParts);
1628 psSHP->sHooks.Error( pszErrorMsg );
1629 SHPDestroyObject(psShape);
1633 /* With the previous checks on nPoints and nParts, */
1634 /* we should not overflow here and after */
1635 /* since 50 M * (16 + 8 + 8) = 1 600 MB */
1636 nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints;
1637 if ( psShape->nSHPType == SHPT_POLYGONZ
1638 || psShape->nSHPType == SHPT_ARCZ
1639 || psShape->nSHPType == SHPT_MULTIPATCH )
1641 nRequiredSize += 16 + 8 * nPoints;
1643 if( psShape->nSHPType == SHPT_MULTIPATCH )
1645 nRequiredSize += 4 * nParts;
1647 if (nRequiredSize > nEntitySize)
1649 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d, nEntitySize=%d.",
1650 hEntity, nPoints, nParts, nEntitySize);
1651 psSHP->sHooks.Error( pszErrorMsg );
1652 SHPDestroyObject(psShape);
1656 psShape->nVertices = nPoints;
1657 psShape->padfX = (double *) calloc(nPoints,sizeof(double));
1658 psShape->padfY = (double *) calloc(nPoints,sizeof(double));
1659 psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
1660 psShape->padfM = (double *) calloc(nPoints,sizeof(double));
1662 psShape->nParts = nParts;
1663 psShape->panPartStart = (int *) calloc(nParts,sizeof(int));
1664 psShape->panPartType = (int *) calloc(nParts,sizeof(int));
1666 if (psShape->padfX == NULL ||
1667 psShape->padfY == NULL ||
1668 psShape->padfZ == NULL ||
1669 psShape->padfM == NULL ||
1670 psShape->panPartStart == NULL ||
1671 psShape->panPartType == NULL)
1673 snprintf(pszErrorMsg, 128,
1674 "Not enough memory to allocate requested memory (nPoints=%d, nParts=%d) for shape %d. "
1675 "Probably broken SHP file", hEntity, nPoints, nParts );
1676 psSHP->sHooks.Error( pszErrorMsg );
1677 SHPDestroyObject(psShape);
1681 for( i = 0; i < nParts; i++ )
1682 psShape->panPartType[i] = SHPP_RING;
1684 /* -------------------------------------------------------------------- */
1685 /* Copy out the part array from the record. */
1686 /* -------------------------------------------------------------------- */
1687 memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts );
1688 for( i = 0; i < nParts; i++ )
1690 if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i );
1692 /* We check that the offset is inside the vertex array */
1693 if (psShape->panPartStart[i] < 0 ||
1694 psShape->panPartStart[i] >= psShape->nVertices)
1696 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d",
1697 hEntity, i, psShape->panPartStart[i], psShape->nVertices);
1698 psSHP->sHooks.Error( pszErrorMsg );
1699 SHPDestroyObject(psShape);
1702 if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1])
1704 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d",
1705 hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]);
1706 psSHP->sHooks.Error( pszErrorMsg );
1707 SHPDestroyObject(psShape);
1712 nOffset = 44 + 8 + 4*nParts;
1714 /* -------------------------------------------------------------------- */
1715 /* If this is a multipatch, we will also have parts types. */
1716 /* -------------------------------------------------------------------- */
1717 if( psShape->nSHPType == SHPT_MULTIPATCH )
1719 memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts );
1720 for( i = 0; i < nParts; i++ )
1722 if( bBigEndian ) SwapWord( 4, psShape->panPartType+i );
1725 nOffset += 4*nParts;
1728 /* -------------------------------------------------------------------- */
1729 /* Copy out the vertices from the record. */
1730 /* -------------------------------------------------------------------- */
1731 for( i = 0; i < nPoints; i++ )
1733 memcpy(psShape->padfX + i,
1734 psSHP->pabyRec + nOffset + i * 16,
1737 memcpy(psShape->padfY + i,
1738 psSHP->pabyRec + nOffset + i * 16 + 8,
1741 if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
1742 if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
1745 nOffset += 16*nPoints;
1747 /* -------------------------------------------------------------------- */
1748 /* If we have a Z coordinate, collect that now. */
1749 /* -------------------------------------------------------------------- */
1750 if( psShape->nSHPType == SHPT_POLYGONZ
1751 || psShape->nSHPType == SHPT_ARCZ
1752 || psShape->nSHPType == SHPT_MULTIPATCH )
1754 memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
1755 memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
1757 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
1758 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
1760 for( i = 0; i < nPoints; i++ )
1762 memcpy( psShape->padfZ + i,
1763 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1764 if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
1767 nOffset += 16 + 8*nPoints;
1770 /* -------------------------------------------------------------------- */
1771 /* If we have a M measure value, then read it now. We assume */
1772 /* that the measure can be present for any shape if the size is */
1773 /* big enough, but really it will only occur for the Z shapes */
1774 /* (options), and the M shapes. */
1775 /* -------------------------------------------------------------------- */
1776 if( nEntitySize >= nOffset + 16 + 8*nPoints )
1778 memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
1779 memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
1781 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
1782 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
1784 for( i = 0; i < nPoints; i++ )
1786 memcpy( psShape->padfM + i,
1787 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1788 if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
1790 psShape->bMeasureIsUsed = TRUE;
1794 /* ==================================================================== */
1795 /* Extract vertices for a MultiPoint. */
1796 /* ==================================================================== */
1797 else if( psShape->nSHPType == SHPT_MULTIPOINT
1798 || psShape->nSHPType == SHPT_MULTIPOINTM
1799 || psShape->nSHPType == SHPT_MULTIPOINTZ )
1804 if ( 44 + 4 > nEntitySize )
1806 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d",
1807 hEntity, nEntitySize);
1808 psSHP->sHooks.Error( pszErrorMsg );
1809 SHPDestroyObject(psShape);
1812 memcpy( &nPoints, psSHP->pabyRec + 44, 4 );
1814 if( bBigEndian ) SwapWord( 4, &nPoints );
1816 if (nPoints < 0 || nPoints > 50 * 1000 * 1000)
1818 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d",
1820 psSHP->sHooks.Error( pszErrorMsg );
1821 SHPDestroyObject(psShape);
1825 nRequiredSize = 48 + nPoints * 16;
1826 if( psShape->nSHPType == SHPT_MULTIPOINTZ )
1828 nRequiredSize += 16 + nPoints * 8;
1830 if (nRequiredSize > nEntitySize)
1832 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d",
1833 hEntity, nPoints, nEntitySize);
1834 psSHP->sHooks.Error( pszErrorMsg );
1835 SHPDestroyObject(psShape);
1839 psShape->nVertices = nPoints;
1840 psShape->padfX = (double *) calloc(nPoints,sizeof(double));
1841 psShape->padfY = (double *) calloc(nPoints,sizeof(double));
1842 psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
1843 psShape->padfM = (double *) calloc(nPoints,sizeof(double));
1845 if (psShape->padfX == NULL ||
1846 psShape->padfY == NULL ||
1847 psShape->padfZ == NULL ||
1848 psShape->padfM == NULL)
1850 snprintf(pszErrorMsg, 128,
1851 "Not enough memory to allocate requested memory (nPoints=%d) for shape %d. "
1852 "Probably broken SHP file", hEntity, nPoints );
1853 psSHP->sHooks.Error( pszErrorMsg );
1854 SHPDestroyObject(psShape);
1858 for( i = 0; i < nPoints; i++ )
1860 memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 );
1861 memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 );
1863 if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
1864 if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
1867 nOffset = 48 + 16*nPoints;
1869 /* -------------------------------------------------------------------- */
1870 /* Get the X/Y bounds. */
1871 /* -------------------------------------------------------------------- */
1872 memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 );
1873 memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
1874 memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
1875 memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
1877 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
1878 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
1879 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
1880 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
1882 /* -------------------------------------------------------------------- */
1883 /* If we have a Z coordinate, collect that now. */
1884 /* -------------------------------------------------------------------- */
1885 if( psShape->nSHPType == SHPT_MULTIPOINTZ )
1887 memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
1888 memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
1890 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
1891 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
1893 for( i = 0; i < nPoints; i++ )
1895 memcpy( psShape->padfZ + i,
1896 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1897 if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
1900 nOffset += 16 + 8*nPoints;
1903 /* -------------------------------------------------------------------- */
1904 /* If we have a M measure value, then read it now. We assume */
1905 /* that the measure can be present for any shape if the size is */
1906 /* big enough, but really it will only occur for the Z shapes */
1907 /* (options), and the M shapes. */
1908 /* -------------------------------------------------------------------- */
1909 if( nEntitySize >= nOffset + 16 + 8*nPoints )
1911 memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
1912 memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
1914 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
1915 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
1917 for( i = 0; i < nPoints; i++ )
1919 memcpy( psShape->padfM + i,
1920 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1921 if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
1923 psShape->bMeasureIsUsed = TRUE;
1927 /* ==================================================================== */
1928 /* Extract vertices for a point. */
1929 /* ==================================================================== */
1930 else if( psShape->nSHPType == SHPT_POINT
1931 || psShape->nSHPType == SHPT_POINTM
1932 || psShape->nSHPType == SHPT_POINTZ )
1936 psShape->nVertices = 1;
1937 psShape->padfX = (double *) calloc(1,sizeof(double));
1938 psShape->padfY = (double *) calloc(1,sizeof(double));
1939 psShape->padfZ = (double *) calloc(1,sizeof(double));
1940 psShape->padfM = (double *) calloc(1,sizeof(double));
1942 if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize)
1944 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d",
1945 hEntity, nEntitySize);
1946 psSHP->sHooks.Error( pszErrorMsg );
1947 SHPDestroyObject(psShape);
1950 memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 );
1951 memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 );
1953 if( bBigEndian ) SwapWord( 8, psShape->padfX );
1954 if( bBigEndian ) SwapWord( 8, psShape->padfY );
1958 /* -------------------------------------------------------------------- */
1959 /* If we have a Z coordinate, collect that now. */
1960 /* -------------------------------------------------------------------- */
1961 if( psShape->nSHPType == SHPT_POINTZ )
1963 memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 );
1965 if( bBigEndian ) SwapWord( 8, psShape->padfZ );
1970 /* -------------------------------------------------------------------- */
1971 /* If we have a M measure value, then read it now. We assume */
1972 /* that the measure can be present for any shape if the size is */
1973 /* big enough, but really it will only occur for the Z shapes */
1974 /* (options), and the M shapes. */
1975 /* -------------------------------------------------------------------- */
1976 if( nEntitySize >= nOffset + 8 )
1978 memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 );
1980 if( bBigEndian ) SwapWord( 8, psShape->padfM );
1981 psShape->bMeasureIsUsed = TRUE;
1984 /* -------------------------------------------------------------------- */
1985 /* Since no extents are supplied in the record, we will apply */
1986 /* them from the single vertex. */
1987 /* -------------------------------------------------------------------- */
1988 psShape->dfXMin = psShape->dfXMax = psShape->padfX[0];
1989 psShape->dfYMin = psShape->dfYMax = psShape->padfY[0];
1990 psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0];
1991 psShape->dfMMin = psShape->dfMMax = psShape->padfM[0];
1997 /************************************************************************/
1999 /************************************************************************/
2001 const char SHPAPI_CALL1(*)
2002 SHPTypeName( int nSHPType )
2019 case SHPT_MULTIPOINT:
2020 return "MultiPoint";
2031 case SHPT_MULTIPOINTZ:
2032 return "MultiPointZ";
2043 case SHPT_MULTIPOINTM:
2044 return "MultiPointM";
2046 case SHPT_MULTIPATCH:
2047 return "MultiPatch";
2050 return "UnknownShapeType";
2054 /************************************************************************/
2055 /* SHPPartTypeName() */
2056 /************************************************************************/
2058 const char SHPAPI_CALL1(*)
2059 SHPPartTypeName( int nPartType )
2065 return "TriangleStrip";
2068 return "TriangleFan";
2070 case SHPP_OUTERRING:
2073 case SHPP_INNERRING:
2076 case SHPP_FIRSTRING:
2083 return "UnknownPartType";
2087 /************************************************************************/
2088 /* SHPDestroyObject() */
2089 /************************************************************************/
2092 SHPDestroyObject( SHPObject * psShape )
2095 if( psShape == NULL )
2098 if( psShape->padfX != NULL )
2099 free( psShape->padfX );
2100 if( psShape->padfY != NULL )
2101 free( psShape->padfY );
2102 if( psShape->padfZ != NULL )
2103 free( psShape->padfZ );
2104 if( psShape->padfM != NULL )
2105 free( psShape->padfM );
2107 if( psShape->panPartStart != NULL )
2108 free( psShape->panPartStart );
2109 if( psShape->panPartType != NULL )
2110 free( psShape->panPartType );
2115 /************************************************************************/
2116 /* SHPRewindObject() */
2118 /* Reset the winding of polygon objects to adhere to the */
2119 /* specification. */
2120 /************************************************************************/
2123 SHPRewindObject( SHPHandle hSHP, SHPObject * psObject )
2126 int iOpRing, bAltered = 0;
2128 /* -------------------------------------------------------------------- */
2129 /* Do nothing if this is not a polygon object. */
2130 /* -------------------------------------------------------------------- */
2131 if( psObject->nSHPType != SHPT_POLYGON
2132 && psObject->nSHPType != SHPT_POLYGONZ
2133 && psObject->nSHPType != SHPT_POLYGONM )
2136 if( psObject->nVertices == 0 || psObject->nParts == 0 )
2139 /* -------------------------------------------------------------------- */
2140 /* Process each of the rings. */
2141 /* -------------------------------------------------------------------- */
2142 for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ )
2144 int bInner, iVert, nVertCount, nVertStart, iCheckRing;
2145 double dfSum, dfTestX, dfTestY;
2147 /* -------------------------------------------------------------------- */
2148 /* Determine if this ring is an inner ring or an outer ring */
2149 /* relative to all the other rings. For now we assume the */
2150 /* first ring is outer and all others are inner, but eventually */
2151 /* we need to fix this to handle multiple island polygons and */
2152 /* unordered sets of rings. */
2154 /* -------------------------------------------------------------------- */
2156 /* Use point in the middle of segment to avoid testing
2157 * common points of rings.
2159 dfTestX = ( psObject->padfX[psObject->panPartStart[iOpRing]]
2160 + psObject->padfX[psObject->panPartStart[iOpRing] + 1] ) / 2;
2161 dfTestY = ( psObject->padfY[psObject->panPartStart[iOpRing]]
2162 + psObject->padfY[psObject->panPartStart[iOpRing] + 1] ) / 2;
2165 for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ )
2169 if( iCheckRing == iOpRing )
2172 nVertStart = psObject->panPartStart[iCheckRing];
2174 if( iCheckRing == psObject->nParts-1 )
2175 nVertCount = psObject->nVertices
2176 - psObject->panPartStart[iCheckRing];
2178 nVertCount = psObject->panPartStart[iCheckRing+1]
2179 - psObject->panPartStart[iCheckRing];
2181 for( iEdge = 0; iEdge < nVertCount; iEdge++ )
2185 if( iEdge < nVertCount-1 )
2191 * Test whether the edge 'straddles' the horizontal ray from the test point (dfTestY,dfTestY)
2192 * The rule #1 also excludes edges collinear with the ray.
2194 if ( ( psObject->padfY[iEdge+nVertStart] < dfTestY
2195 && dfTestY <= psObject->padfY[iNext+nVertStart] )
2196 || ( psObject->padfY[iNext+nVertStart] < dfTestY
2197 && dfTestY <= psObject->padfY[iEdge+nVertStart] ) )
2200 * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY)
2202 double const intersect =
2203 ( psObject->padfX[iEdge+nVertStart]
2204 + ( dfTestY - psObject->padfY[iEdge+nVertStart] )
2205 / ( psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart] )
2206 * ( psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart] ) );
2208 if (intersect < dfTestX)
2214 } /* for iCheckRing */
2216 /* -------------------------------------------------------------------- */
2217 /* Determine the current order of this ring so we will know if */
2218 /* it has to be reversed. */
2219 /* -------------------------------------------------------------------- */
2220 nVertStart = psObject->panPartStart[iOpRing];
2222 if( iOpRing == psObject->nParts-1 )
2223 nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing];
2225 nVertCount = psObject->panPartStart[iOpRing+1]
2226 - psObject->panPartStart[iOpRing];
2229 for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ )
2231 dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1]
2232 - psObject->padfY[iVert] * psObject->padfX[iVert+1];
2235 dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart]
2236 - psObject->padfY[iVert] * psObject->padfX[nVertStart];
2238 /* -------------------------------------------------------------------- */
2239 /* Reverse if necessary. */
2240 /* -------------------------------------------------------------------- */
2241 if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) )
2246 for( i = 0; i < nVertCount/2; i++ )
2251 dfSaved = psObject->padfX[nVertStart+i];
2252 psObject->padfX[nVertStart+i] =
2253 psObject->padfX[nVertStart+nVertCount-i-1];
2254 psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved;
2257 dfSaved = psObject->padfY[nVertStart+i];
2258 psObject->padfY[nVertStart+i] =
2259 psObject->padfY[nVertStart+nVertCount-i-1];
2260 psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved;
2263 if( psObject->padfZ )
2265 dfSaved = psObject->padfZ[nVertStart+i];
2266 psObject->padfZ[nVertStart+i] =
2267 psObject->padfZ[nVertStart+nVertCount-i-1];
2268 psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved;
2272 if( psObject->padfM )
2274 dfSaved = psObject->padfM[nVertStart+i];
2275 psObject->padfM[nVertStart+i] =
2276 psObject->padfM[nVertStart+nVertCount-i-1];
2277 psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved;