1 /******************************************************************************
2 * $Id: shpopen.c,v 1.73 2012-01-24 22:33:01 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.73 2012-01-24 22:33:01 fwarmerdam
38 * fix memory leak on failure to open .shp (gdal #4410)
40 * Revision 1.72 2011-12-11 22:45:28 fwarmerdam
41 * fix failure return from SHPOpenLL.
43 * Revision 1.71 2011-09-15 03:33:58 fwarmerdam
44 * fix missing cast (#2344)
46 * Revision 1.70 2011-07-24 05:59:25 fwarmerdam
47 * minimize use of CPLError in favor of SAHooks.Error()
49 * Revision 1.69 2011-07-24 03:24:22 fwarmerdam
50 * fix memory leaks in error cases creating shapefiles (#2061)
52 * Revision 1.68 2010-08-27 23:42:52 fwarmerdam
53 * add SHPAPI_CALL attribute in code
55 * Revision 1.67 2010-07-01 08:15:48 fwarmerdam
56 * do not error out on an object with zero vertices
58 * Revision 1.66 2010-07-01 07:58:57 fwarmerdam
59 * minor cleanup of error handling
61 * Revision 1.65 2010-07-01 07:27:13 fwarmerdam
62 * white space formatting adjustments
64 * Revision 1.64 2010-01-28 11:34:34 fwarmerdam
65 * handle the shape file length limits more gracefully (#3236)
67 * Revision 1.63 2010-01-28 04:04:40 fwarmerdam
68 * improve numerical accuracy of SHPRewind() algs (gdal #3363)
70 * Revision 1.62 2010-01-17 05:34:13 fwarmerdam
71 * Remove asserts on x/y being null (#2148).
73 * Revision 1.61 2010-01-16 05:07:42 fwarmerdam
74 * allow 0/nulls in shpcreateobject (#2148)
76 * Revision 1.60 2009-09-17 20:50:02 bram
77 * on Win32, define snprintf as alias to _snprintf
79 * Revision 1.59 2008-03-14 05:25:31 fwarmerdam
80 * Correct crash on buggy geometries (gdal #2218)
82 * Revision 1.58 2008/01/08 23:28:26 bram
83 * on line 2095, use a float instead of a double to avoid a compiler warning
85 * Revision 1.57 2007/12/06 07:00:25 fwarmerdam
86 * dbfopen now using SAHooks for fileio
88 * Revision 1.56 2007/12/04 20:37:56 fwarmerdam
89 * preliminary implementation of hooks api for io and errors
91 * Revision 1.55 2007/11/21 22:39:56 fwarmerdam
92 * close shx file in readonly mode (GDAL #1956)
94 * Revision 1.54 2007/11/15 00:12:47 mloskot
95 * Backported recent changes from GDAL (Ticket #1415) to Shapelib.
97 * Revision 1.53 2007/11/14 22:31:08 fwarmerdam
98 * checks after mallocs to detect for corrupted/voluntary broken shapefiles.
99 * http://trac.osgeo.org/gdal/ticket/1991
101 * Revision 1.52 2007/06/21 15:58:33 fwarmerdam
102 * fix for SHPRewindObject when rings touch at one vertex (gdal #976)
104 * Revision 1.51 2006/09/04 15:24:01 fwarmerdam
105 * Fixed up log message for 1.49.
107 * Revision 1.50 2006/09/04 15:21:39 fwarmerdam
110 * Revision 1.49 2006/09/04 15:21:00 fwarmerdam
111 * MLoskot: Added stronger test of Shapefile reading failures, e.g. truncated
112 * files. The problem was discovered by Tim Sutton and reported here
113 * https://svn.qgis.org/trac/ticket/200
115 * Revision 1.48 2006/01/26 15:07:32 fwarmerdam
116 * add bMeasureIsUsed flag from Craig Bruce: Bug 1249
118 * Revision 1.47 2006/01/04 20:07:23 fwarmerdam
119 * In SHPWriteObject() make sure that the record length is updated
120 * when rewriting an existing record.
122 * Revision 1.46 2005/02/11 17:17:46 fwarmerdam
123 * added panPartStart[0] validation
125 * Revision 1.45 2004/09/26 20:09:48 fwarmerdam
126 * const correctness changes
128 * Revision 1.44 2003/12/29 00:18:39 fwarmerdam
129 * added error checking for failed IO and optional CPL error reporting
131 * Revision 1.43 2003/12/01 16:20:08 warmerda
132 * be careful of zero vertex shapes
134 * Revision 1.42 2003/12/01 14:58:27 warmerda
135 * added degenerate object check in SHPRewindObject()
137 * Revision 1.41 2003/07/08 15:22:43 warmerda
140 * Revision 1.40 2003/04/21 18:30:37 warmerda
141 * added header write/update public methods
143 * Revision 1.39 2002/08/26 06:46:56 warmerda
146 * Revision 1.38 2002/05/07 16:43:39 warmerda
147 * Removed debugging printf.
149 * Revision 1.37 2002/04/10 17:35:22 warmerda
150 * fixed bug in ring reversal code
152 * Revision 1.36 2002/04/10 16:59:54 warmerda
153 * added SHPRewindObject
155 * Revision 1.35 2001/12/07 15:10:44 warmerda
156 * fix if .shx fails to open
158 * Revision 1.34 2001/11/01 16:29:55 warmerda
159 * move pabyRec into SHPInfo for thread safety
161 * Revision 1.33 2001/07/03 12:18:15 warmerda
162 * Improved cleanup if SHX not found, provied by Riccardo Cohen.
164 * Revision 1.32 2001/06/22 01:58:07 warmerda
165 * be more careful about establishing initial bounds in face of NULL shapes
167 * Revision 1.31 2001/05/31 19:35:29 warmerda
168 * added support for writing null shapes
170 * Revision 1.30 2001/05/28 12:46:29 warmerda
171 * Add some checking on reasonableness of record count when opening.
173 * Revision 1.29 2001/05/23 13:36:52 warmerda
174 * added use of SHPAPI_CALL
176 * Revision 1.28 2001/02/06 22:25:06 warmerda
177 * fixed memory leaks when SHPOpen() fails
179 * Revision 1.27 2000/07/18 15:21:33 warmerda
180 * added better enforcement of -1 for append in SHPWriteObject
182 * Revision 1.26 2000/02/16 16:03:51 warmerda
183 * added null shape support
185 * Revision 1.25 1999/12/15 13:47:07 warmerda
186 * Fixed record size settings in .shp file (was 4 words too long)
189 * Revision 1.24 1999/11/05 14:12:04 warmerda
190 * updated license terms
192 * Revision 1.23 1999/07/27 00:53:46 warmerda
193 * added support for rewriting shapes
195 * Revision 1.22 1999/06/11 19:19:11 warmerda
196 * Cleanup pabyRec static buffer on SHPClose().
198 * Revision 1.21 1999/06/02 14:57:56 kshih
199 * Remove unused variables
201 * Revision 1.20 1999/04/19 21:04:17 warmerda
202 * Fixed syntax error.
204 * Revision 1.19 1999/04/19 21:01:57 warmerda
205 * Force access string to binary in SHPOpen().
207 * Revision 1.18 1999/04/01 18:48:07 warmerda
208 * Try upper case extensions if lower case doesn't work.
210 * Revision 1.17 1998/12/31 15:29:39 warmerda
211 * Disable writing measure values to multipatch objects if
212 * DISABLE_MULTIPATCH_MEASURE is defined.
214 * Revision 1.16 1998/12/16 05:14:33 warmerda
215 * Added support to write MULTIPATCH. Fixed reading Z coordinate of
216 * MULTIPATCH. Fixed record size written for all feature types.
218 * Revision 1.15 1998/12/03 16:35:29 warmerda
219 * r+b is proper binary access string, not rb+.
221 * Revision 1.14 1998/12/03 15:47:56 warmerda
222 * Fixed setting of nVertices in SHPCreateObject().
224 * Revision 1.13 1998/12/03 15:33:54 warmerda
225 * Made SHPCalculateExtents() separately callable.
227 * Revision 1.12 1998/11/11 20:01:50 warmerda
228 * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines.
230 * Revision 1.11 1998/11/09 20:56:44 warmerda
231 * Fixed up handling of file wide bounds.
233 * Revision 1.10 1998/11/09 20:18:51 warmerda
234 * Converted to support 3D shapefiles, and use of SHPObject.
236 * Revision 1.9 1998/02/24 15:09:05 warmerda
239 * Revision 1.8 1997/12/04 15:40:29 warmerda
240 * Fixed byte swapping of record number, and record length fields in the
243 * Revision 1.7 1995/10/21 03:15:58 warmerda
244 * Added support for binary file access, the magic cookie 9997
245 * and tried to improve the int32 selection logic for 16bit systems.
247 * Revision 1.6 1995/09/04 04:19:41 warmerda
248 * Added fix for file bounds.
250 * Revision 1.5 1995/08/25 15:16:44 warmerda
251 * Fixed a couple of problems with big endian systems ... one with bounds
252 * and the other with multipart polygons.
254 * Revision 1.4 1995/08/24 18:10:17 warmerda
255 * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc()
256 * functions (such as on the Sun).
258 * Revision 1.3 1995/08/23 02:23:15 warmerda
259 * Added support for reading bounds, and fixed up problems in setting the
262 * Revision 1.2 1995/08/04 03:16:57 warmerda
267 #include "shapefil.h"
276 SHP_CVSID("$Id: shpopen.c,v 1.73 2012-01-24 22:33:01 fwarmerdam Exp $")
278 typedef unsigned char uchar;
280 #if UINT_MAX == 65535
281 typedef unsigned long int32;
283 typedef unsigned int int32;
291 #define ByteCopy( a, b, c ) memcpy( b, a, c )
293 # define MIN(a,b) ((a<b) ? a : b)
294 # define MAX(a,b) ((a>b) ? a : b)
297 #if defined(WIN32) || defined(_WIN32)
299 # define snprintf _snprintf
303 static int bBigEndian;
306 /************************************************************************/
309 /* Swap a 2, 4 or 8 byte word. */
310 /************************************************************************/
312 static void SwapWord( int length, void * wordP )
318 for( i=0; i < length/2; i++ )
320 temp = ((uchar *) wordP)[i];
321 ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1];
322 ((uchar *) wordP)[length-i-1] = temp;
326 /************************************************************************/
329 /* A realloc cover function that will access a NULL pointer as */
331 /************************************************************************/
333 static void * SfRealloc( void * pMem, int nNewSize )
337 return( (void *) malloc(nNewSize) );
339 return( (void *) realloc(pMem,nNewSize) );
342 /************************************************************************/
343 /* SHPWriteHeader() */
345 /* Write out a header for the .shp and .shx files as well as the */
346 /* contents of the index (.shx) file. */
347 /************************************************************************/
349 void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP )
352 uchar abyHeader[100];
358 if (psSHP->fpSHX == NULL)
360 psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed");
364 /* -------------------------------------------------------------------- */
365 /* Prepare header block for .shp file. */
366 /* -------------------------------------------------------------------- */
367 for( i = 0; i < 100; i++ )
370 abyHeader[2] = 0x27; /* magic cookie */
373 i32 = psSHP->nFileSize/2; /* file size */
374 ByteCopy( &i32, abyHeader+24, 4 );
375 if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
377 i32 = 1000; /* version */
378 ByteCopy( &i32, abyHeader+28, 4 );
379 if( bBigEndian ) SwapWord( 4, abyHeader+28 );
381 i32 = psSHP->nShapeType; /* shape type */
382 ByteCopy( &i32, abyHeader+32, 4 );
383 if( bBigEndian ) SwapWord( 4, abyHeader+32 );
385 dValue = psSHP->adBoundsMin[0]; /* set bounds */
386 ByteCopy( &dValue, abyHeader+36, 8 );
387 if( bBigEndian ) SwapWord( 8, abyHeader+36 );
389 dValue = psSHP->adBoundsMin[1];
390 ByteCopy( &dValue, abyHeader+44, 8 );
391 if( bBigEndian ) SwapWord( 8, abyHeader+44 );
393 dValue = psSHP->adBoundsMax[0];
394 ByteCopy( &dValue, abyHeader+52, 8 );
395 if( bBigEndian ) SwapWord( 8, abyHeader+52 );
397 dValue = psSHP->adBoundsMax[1];
398 ByteCopy( &dValue, abyHeader+60, 8 );
399 if( bBigEndian ) SwapWord( 8, abyHeader+60 );
401 dValue = psSHP->adBoundsMin[2]; /* z */
402 ByteCopy( &dValue, abyHeader+68, 8 );
403 if( bBigEndian ) SwapWord( 8, abyHeader+68 );
405 dValue = psSHP->adBoundsMax[2];
406 ByteCopy( &dValue, abyHeader+76, 8 );
407 if( bBigEndian ) SwapWord( 8, abyHeader+76 );
409 dValue = psSHP->adBoundsMin[3]; /* m */
410 ByteCopy( &dValue, abyHeader+84, 8 );
411 if( bBigEndian ) SwapWord( 8, abyHeader+84 );
413 dValue = psSHP->adBoundsMax[3];
414 ByteCopy( &dValue, abyHeader+92, 8 );
415 if( bBigEndian ) SwapWord( 8, abyHeader+92 );
417 /* -------------------------------------------------------------------- */
418 /* Write .shp file header. */
419 /* -------------------------------------------------------------------- */
420 if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0
421 || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHP ) != 1 )
423 psSHP->sHooks.Error( "Failure writing .shp header" );
427 /* -------------------------------------------------------------------- */
428 /* Prepare, and write .shx file header. */
429 /* -------------------------------------------------------------------- */
430 i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */
431 ByteCopy( &i32, abyHeader+24, 4 );
432 if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
434 if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0
435 || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 )
437 psSHP->sHooks.Error( "Failure writing .shx header" );
441 /* -------------------------------------------------------------------- */
442 /* Write out the .shx contents. */
443 /* -------------------------------------------------------------------- */
444 panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords);
446 for( i = 0; i < psSHP->nRecords; i++ )
448 panSHX[i*2 ] = psSHP->panRecOffset[i]/2;
449 panSHX[i*2+1] = psSHP->panRecSize[i]/2;
450 if( !bBigEndian ) SwapWord( 4, panSHX+i*2 );
451 if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 );
454 if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX )
457 psSHP->sHooks.Error( "Failure writing .shx contents" );
462 /* -------------------------------------------------------------------- */
464 /* -------------------------------------------------------------------- */
465 psSHP->sHooks.FFlush( psSHP->fpSHP );
466 psSHP->sHooks.FFlush( psSHP->fpSHX );
469 /************************************************************************/
471 /************************************************************************/
473 SHPHandle SHPAPI_CALL
474 SHPOpen( const char * pszLayer, const char * pszAccess )
479 SASetupDefaultHooks( &sHooks );
481 return SHPOpenLL( pszLayer, pszAccess, &sHooks );
484 /************************************************************************/
487 /* Open the .shp and .shx files based on the basename of the */
488 /* files or either file name. */
489 /************************************************************************/
491 SHPHandle SHPAPI_CALL
492 SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks )
495 char *pszFullname, *pszBasename;
502 /* -------------------------------------------------------------------- */
503 /* Ensure the access string is one of the legal ones. We */
504 /* ensure the result string indicates binary to avoid common */
505 /* problems on Windows. */
506 /* -------------------------------------------------------------------- */
507 if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0
508 || strcmp(pszAccess,"r+") == 0 )
513 /* -------------------------------------------------------------------- */
514 /* Establish the byte order on this machine. */
515 /* -------------------------------------------------------------------- */
517 if( *((uchar *) &i) == 1 )
522 /* -------------------------------------------------------------------- */
523 /* Initialize the info structure. */
524 /* -------------------------------------------------------------------- */
525 psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1);
527 psSHP->bUpdated = FALSE;
528 memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) );
530 /* -------------------------------------------------------------------- */
531 /* Compute the base (layer) name. If there is any extension */
532 /* on the passed in filename we will strip it off. */
533 /* -------------------------------------------------------------------- */
534 pszBasename = (char *) malloc(strlen(pszLayer)+5);
535 strcpy( pszBasename, pszLayer );
536 for( i = strlen(pszBasename)-1;
537 i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
538 && pszBasename[i] != '\\';
541 if( pszBasename[i] == '.' )
542 pszBasename[i] = '\0';
544 /* -------------------------------------------------------------------- */
545 /* Open the .shp and .shx files. Note that files pulled from */
546 /* a PC to Unix with upper case filenames won't work! */
547 /* -------------------------------------------------------------------- */
548 pszFullname = (char *) malloc(strlen(pszBasename) + 5);
549 sprintf( pszFullname, "%s.shp", pszBasename ) ;
550 psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess );
551 if( psSHP->fpSHP == NULL )
553 sprintf( pszFullname, "%s.SHP", pszBasename );
554 psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess );
557 if( psSHP->fpSHP == NULL )
559 char *pszMessage = (char *) malloc(strlen(pszBasename)*2+256);
560 sprintf( pszMessage, "Unable to open %s.shp or %s.SHP.",
561 pszBasename, pszBasename );
562 psHooks->Error( pszMessage );
572 sprintf( pszFullname, "%s.shx", pszBasename );
573 psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess );
574 if( psSHP->fpSHX == NULL )
576 sprintf( pszFullname, "%s.SHX", pszBasename );
577 psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess );
580 if( psSHP->fpSHX == NULL )
582 char *pszMessage = (char *) malloc(strlen(pszBasename)*2+256);
583 sprintf( pszMessage, "Unable to open %s.shx or %s.SHX.",
584 pszBasename, pszBasename );
585 psHooks->Error( pszMessage );
588 psSHP->sHooks.FClose( psSHP->fpSHP );
598 /* -------------------------------------------------------------------- */
599 /* Read the file size from the SHP file. */
600 /* -------------------------------------------------------------------- */
601 pabyBuf = (uchar *) malloc(100);
602 psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHP );
604 psSHP->nFileSize = ((unsigned int)pabyBuf[24] * 256 * 256 * 256
605 + (unsigned int)pabyBuf[25] * 256 * 256
606 + (unsigned int)pabyBuf[26] * 256
607 + (unsigned int)pabyBuf[27]) * 2;
609 /* -------------------------------------------------------------------- */
610 /* Read SHX file Header info */
611 /* -------------------------------------------------------------------- */
612 if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1
615 || pabyBuf[2] != 0x27
616 || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) )
618 psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." );
619 psSHP->sHooks.FClose( psSHP->fpSHP );
620 psSHP->sHooks.FClose( psSHP->fpSHX );
626 psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256
627 + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256;
628 psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8;
630 psSHP->nShapeType = pabyBuf[32];
632 if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 )
637 "Record count in .shp header is %d, which seems\n"
638 "unreasonable. Assuming header is corrupt.",
640 psSHP->sHooks.Error( szError );
641 psSHP->sHooks.FClose( psSHP->fpSHP );
642 psSHP->sHooks.FClose( psSHP->fpSHX );
649 /* -------------------------------------------------------------------- */
650 /* Read the bounds. */
651 /* -------------------------------------------------------------------- */
652 if( bBigEndian ) SwapWord( 8, pabyBuf+36 );
653 memcpy( &dValue, pabyBuf+36, 8 );
654 psSHP->adBoundsMin[0] = dValue;
656 if( bBigEndian ) SwapWord( 8, pabyBuf+44 );
657 memcpy( &dValue, pabyBuf+44, 8 );
658 psSHP->adBoundsMin[1] = dValue;
660 if( bBigEndian ) SwapWord( 8, pabyBuf+52 );
661 memcpy( &dValue, pabyBuf+52, 8 );
662 psSHP->adBoundsMax[0] = dValue;
664 if( bBigEndian ) SwapWord( 8, pabyBuf+60 );
665 memcpy( &dValue, pabyBuf+60, 8 );
666 psSHP->adBoundsMax[1] = dValue;
668 if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */
669 memcpy( &dValue, pabyBuf+68, 8 );
670 psSHP->adBoundsMin[2] = dValue;
672 if( bBigEndian ) SwapWord( 8, pabyBuf+76 );
673 memcpy( &dValue, pabyBuf+76, 8 );
674 psSHP->adBoundsMax[2] = dValue;
676 if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */
677 memcpy( &dValue, pabyBuf+84, 8 );
678 psSHP->adBoundsMin[3] = dValue;
680 if( bBigEndian ) SwapWord( 8, pabyBuf+92 );
681 memcpy( &dValue, pabyBuf+92, 8 );
682 psSHP->adBoundsMax[3] = dValue;
686 /* -------------------------------------------------------------------- */
687 /* Read the .shx file to get the offsets to each record in */
689 /* -------------------------------------------------------------------- */
690 psSHP->nMaxRecords = psSHP->nRecords;
692 psSHP->panRecOffset = (unsigned int *)
693 malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) );
694 psSHP->panRecSize = (unsigned int *)
695 malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) );
696 pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) );
698 if (psSHP->panRecOffset == NULL ||
699 psSHP->panRecSize == NULL ||
705 "Not enough memory to allocate requested memory (nRecords=%d).\n"
706 "Probably broken SHP file",
708 psSHP->sHooks.Error( szError );
709 psSHP->sHooks.FClose( psSHP->fpSHP );
710 psSHP->sHooks.FClose( psSHP->fpSHX );
711 if (psSHP->panRecOffset) free( psSHP->panRecOffset );
712 if (psSHP->panRecSize) free( psSHP->panRecSize );
713 if (pabyBuf) free( pabyBuf );
718 if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX )
724 "Failed to read all values for %d records in .shx file.",
726 psSHP->sHooks.Error( szError );
728 /* SHX is short or unreadable for some reason. */
729 psSHP->sHooks.FClose( psSHP->fpSHP );
730 psSHP->sHooks.FClose( psSHP->fpSHX );
731 free( psSHP->panRecOffset );
732 free( psSHP->panRecSize );
739 /* In read-only mode, we can close the SHX now */
740 if (strcmp(pszAccess, "rb") == 0)
742 psSHP->sHooks.FClose( psSHP->fpSHX );
746 for( i = 0; i < psSHP->nRecords; i++ )
748 int32 nOffset, nLength;
750 memcpy( &nOffset, pabyBuf + i * 8, 4 );
751 if( !bBigEndian ) SwapWord( 4, &nOffset );
753 memcpy( &nLength, pabyBuf + i * 8 + 4, 4 );
754 if( !bBigEndian ) SwapWord( 4, &nLength );
756 psSHP->panRecOffset[i] = nOffset*2;
757 psSHP->panRecSize[i] = nLength*2;
764 /************************************************************************/
767 /* Close the .shp and .shx files. */
768 /************************************************************************/
771 SHPClose(SHPHandle psSHP )
777 /* -------------------------------------------------------------------- */
778 /* Update the header if we have modified anything. */
779 /* -------------------------------------------------------------------- */
780 if( psSHP->bUpdated )
781 SHPWriteHeader( psSHP );
783 /* -------------------------------------------------------------------- */
784 /* Free all resources, and close files. */
785 /* -------------------------------------------------------------------- */
786 free( psSHP->panRecOffset );
787 free( psSHP->panRecSize );
789 if ( psSHP->fpSHX != NULL)
790 psSHP->sHooks.FClose( psSHP->fpSHX );
791 psSHP->sHooks.FClose( psSHP->fpSHP );
793 if( psSHP->pabyRec != NULL )
795 free( psSHP->pabyRec );
801 /************************************************************************/
804 /* Fetch general information about the shape file. */
805 /************************************************************************/
808 SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType,
809 double * padfMinBound, double * padfMaxBound )
817 if( pnEntities != NULL )
818 *pnEntities = psSHP->nRecords;
820 if( pnShapeType != NULL )
821 *pnShapeType = psSHP->nShapeType;
823 for( i = 0; i < 4; i++ )
825 if( padfMinBound != NULL )
826 padfMinBound[i] = psSHP->adBoundsMin[i];
827 if( padfMaxBound != NULL )
828 padfMaxBound[i] = psSHP->adBoundsMax[i];
832 /************************************************************************/
835 /* Create a new shape file and return a handle to the open */
836 /* shape file with read/write access. */
837 /************************************************************************/
839 SHPHandle SHPAPI_CALL
840 SHPCreate( const char * pszLayer, int nShapeType )
845 SASetupDefaultHooks( &sHooks );
847 return SHPCreateLL( pszLayer, nShapeType, &sHooks );
850 /************************************************************************/
853 /* Create a new shape file and return a handle to the open */
854 /* shape file with read/write access. */
855 /************************************************************************/
857 SHPHandle SHPAPI_CALL
858 SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks )
861 char *pszBasename = NULL, *pszFullname = NULL;
863 SAFile fpSHP = NULL, fpSHX = NULL;
864 uchar abyHeader[100];
868 /* -------------------------------------------------------------------- */
869 /* Establish the byte order on this system. */
870 /* -------------------------------------------------------------------- */
872 if( *((uchar *) &i) == 1 )
877 /* -------------------------------------------------------------------- */
878 /* Compute the base (layer) name. If there is any extension */
879 /* on the passed in filename we will strip it off. */
880 /* -------------------------------------------------------------------- */
881 pszBasename = (char *) malloc(strlen(pszLayer)+5);
882 strcpy( pszBasename, pszLayer );
883 for( i = strlen(pszBasename)-1;
884 i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
885 && pszBasename[i] != '\\';
888 if( pszBasename[i] == '.' )
889 pszBasename[i] = '\0';
891 /* -------------------------------------------------------------------- */
892 /* Open the two files so we can write their headers. */
893 /* -------------------------------------------------------------------- */
894 pszFullname = (char *) malloc(strlen(pszBasename) + 5);
895 sprintf( pszFullname, "%s.shp", pszBasename );
896 fpSHP = psHooks->FOpen(pszFullname, "wb" );
899 psHooks->Error( "Failed to create file .shp file." );
903 sprintf( pszFullname, "%s.shx", pszBasename );
904 fpSHX = psHooks->FOpen(pszFullname, "wb" );
907 psHooks->Error( "Failed to create file .shx file." );
911 free( pszFullname ); pszFullname = NULL;
912 free( pszBasename ); pszBasename = NULL;
914 /* -------------------------------------------------------------------- */
915 /* Prepare header block for .shp file. */
916 /* -------------------------------------------------------------------- */
917 for( i = 0; i < 100; i++ )
920 abyHeader[2] = 0x27; /* magic cookie */
923 i32 = 50; /* file size */
924 ByteCopy( &i32, abyHeader+24, 4 );
925 if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
927 i32 = 1000; /* version */
928 ByteCopy( &i32, abyHeader+28, 4 );
929 if( bBigEndian ) SwapWord( 4, abyHeader+28 );
931 i32 = nShapeType; /* shape type */
932 ByteCopy( &i32, abyHeader+32, 4 );
933 if( bBigEndian ) SwapWord( 4, abyHeader+32 );
935 dValue = 0.0; /* set bounds */
936 ByteCopy( &dValue, abyHeader+36, 8 );
937 ByteCopy( &dValue, abyHeader+44, 8 );
938 ByteCopy( &dValue, abyHeader+52, 8 );
939 ByteCopy( &dValue, abyHeader+60, 8 );
941 /* -------------------------------------------------------------------- */
942 /* Write .shp file header. */
943 /* -------------------------------------------------------------------- */
944 if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 )
946 psHooks->Error( "Failed to write .shp header." );
950 /* -------------------------------------------------------------------- */
951 /* Prepare, and write .shx file header. */
952 /* -------------------------------------------------------------------- */
953 i32 = 50; /* file size */
954 ByteCopy( &i32, abyHeader+24, 4 );
955 if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
957 if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 )
959 psHooks->Error( "Failed to write .shx header." );
963 /* -------------------------------------------------------------------- */
964 /* Close the files, and then open them as regular existing files. */
965 /* -------------------------------------------------------------------- */
966 psHooks->FClose( fpSHP );
967 psHooks->FClose( fpSHX );
969 return( SHPOpenLL( pszLayer, "r+b", psHooks ) );
972 if (pszFullname) free(pszFullname);
973 if (pszBasename) free(pszBasename);
974 if (fpSHP) psHooks->FClose( fpSHP );
975 if (fpSHX) psHooks->FClose( fpSHX );
979 /************************************************************************/
980 /* _SHPSetBounds() */
982 /* Compute a bounds rectangle for a shape, and set it into the */
983 /* indicated location in the record. */
984 /************************************************************************/
986 static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape )
989 ByteCopy( &(psShape->dfXMin), pabyRec + 0, 8 );
990 ByteCopy( &(psShape->dfYMin), pabyRec + 8, 8 );
991 ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 );
992 ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 );
996 SwapWord( 8, pabyRec + 0 );
997 SwapWord( 8, pabyRec + 8 );
998 SwapWord( 8, pabyRec + 16 );
999 SwapWord( 8, pabyRec + 24 );
1003 /************************************************************************/
1004 /* SHPComputeExtents() */
1006 /* Recompute the extents of a shape. Automatically done by */
1007 /* SHPCreateObject(). */
1008 /************************************************************************/
1011 SHPComputeExtents( SHPObject * psObject )
1016 /* -------------------------------------------------------------------- */
1017 /* Build extents for this object. */
1018 /* -------------------------------------------------------------------- */
1019 if( psObject->nVertices > 0 )
1021 psObject->dfXMin = psObject->dfXMax = psObject->padfX[0];
1022 psObject->dfYMin = psObject->dfYMax = psObject->padfY[0];
1023 psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0];
1024 psObject->dfMMin = psObject->dfMMax = psObject->padfM[0];
1027 for( i = 0; i < psObject->nVertices; i++ )
1029 psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]);
1030 psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]);
1031 psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]);
1032 psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]);
1034 psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]);
1035 psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]);
1036 psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]);
1037 psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]);
1041 /************************************************************************/
1042 /* SHPCreateObject() */
1044 /* Create a shape object. It should be freed with */
1045 /* SHPDestroyObject(). */
1046 /************************************************************************/
1048 SHPObject SHPAPI_CALL1(*)
1049 SHPCreateObject( int nSHPType, int nShapeId, int nParts,
1050 const int * panPartStart, const int * panPartType,
1051 int nVertices, const double *padfX, const double *padfY,
1052 const double * padfZ, const double * padfM )
1055 SHPObject *psObject;
1056 int i, bHasM, bHasZ;
1058 psObject = (SHPObject *) calloc(1,sizeof(SHPObject));
1059 psObject->nSHPType = nSHPType;
1060 psObject->nShapeId = nShapeId;
1061 psObject->bMeasureIsUsed = FALSE;
1063 /* -------------------------------------------------------------------- */
1064 /* Establish whether this shape type has M, and Z values. */
1065 /* -------------------------------------------------------------------- */
1066 if( nSHPType == SHPT_ARCM
1067 || nSHPType == SHPT_POINTM
1068 || nSHPType == SHPT_POLYGONM
1069 || nSHPType == SHPT_MULTIPOINTM )
1074 else if( nSHPType == SHPT_ARCZ
1075 || nSHPType == SHPT_POINTZ
1076 || nSHPType == SHPT_POLYGONZ
1077 || nSHPType == SHPT_MULTIPOINTZ
1078 || nSHPType == SHPT_MULTIPATCH )
1089 /* -------------------------------------------------------------------- */
1090 /* Capture parts. Note that part type is optional, and */
1091 /* defaults to ring. */
1092 /* -------------------------------------------------------------------- */
1093 if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON
1094 || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM
1095 || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ
1096 || nSHPType == SHPT_MULTIPATCH )
1098 psObject->nParts = MAX(1,nParts);
1100 psObject->panPartStart = (int *)
1101 calloc(sizeof(int), psObject->nParts);
1102 psObject->panPartType = (int *)
1103 malloc(sizeof(int) * psObject->nParts);
1105 psObject->panPartStart[0] = 0;
1106 psObject->panPartType[0] = SHPP_RING;
1108 for( i = 0; i < nParts; i++ )
1110 if( psObject->panPartStart != NULL )
1111 psObject->panPartStart[i] = panPartStart[i];
1113 if( panPartType != NULL )
1114 psObject->panPartType[i] = panPartType[i];
1116 psObject->panPartType[i] = SHPP_RING;
1119 if( psObject->panPartStart[0] != 0 )
1120 psObject->panPartStart[0] = 0;
1123 /* -------------------------------------------------------------------- */
1124 /* Capture vertices. Note that X, Y, Z and M are optional. */
1125 /* -------------------------------------------------------------------- */
1128 psObject->padfX = (double *) calloc(sizeof(double),nVertices);
1129 psObject->padfY = (double *) calloc(sizeof(double),nVertices);
1130 psObject->padfZ = (double *) calloc(sizeof(double),nVertices);
1131 psObject->padfM = (double *) calloc(sizeof(double),nVertices);
1133 for( i = 0; i < nVertices; i++ )
1136 psObject->padfX[i] = padfX[i];
1138 psObject->padfY[i] = padfY[i];
1139 if( padfZ != NULL && bHasZ )
1140 psObject->padfZ[i] = padfZ[i];
1141 if( padfM != NULL && bHasM )
1142 psObject->padfM[i] = padfM[i];
1144 if( padfM != NULL && bHasM )
1145 psObject->bMeasureIsUsed = TRUE;
1148 /* -------------------------------------------------------------------- */
1149 /* Compute the extents. */
1150 /* -------------------------------------------------------------------- */
1151 psObject->nVertices = nVertices;
1152 SHPComputeExtents( psObject );
1157 /************************************************************************/
1158 /* SHPCreateSimpleObject() */
1160 /* Create a simple (common) shape object. Destroy with */
1161 /* SHPDestroyObject(). */
1162 /************************************************************************/
1164 SHPObject SHPAPI_CALL1(*)
1165 SHPCreateSimpleObject( int nSHPType, int nVertices,
1166 const double * padfX, const double * padfY,
1167 const double * padfZ )
1170 return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL,
1171 nVertices, padfX, padfY, padfZ, NULL ) );
1174 /************************************************************************/
1175 /* SHPWriteObject() */
1177 /* Write out the vertices of a new structure. Note that it is */
1178 /* only possible to write vertices at the end of the file. */
1179 /************************************************************************/
1182 SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject )
1185 unsigned int nRecordOffset, nRecordSize=0;
1190 psSHP->bUpdated = TRUE;
1192 /* -------------------------------------------------------------------- */
1193 /* Ensure that shape object matches the type of the file it is */
1194 /* being written to. */
1195 /* -------------------------------------------------------------------- */
1196 assert( psObject->nSHPType == psSHP->nShapeType
1197 || psObject->nSHPType == SHPT_NULL );
1199 /* -------------------------------------------------------------------- */
1200 /* Ensure that -1 is used for appends. Either blow an */
1201 /* assertion, or if they are disabled, set the shapeid to -1 */
1203 /* -------------------------------------------------------------------- */
1204 assert( nShapeId == -1
1205 || (nShapeId >= 0 && nShapeId < psSHP->nRecords) );
1207 if( nShapeId != -1 && nShapeId >= psSHP->nRecords )
1210 /* -------------------------------------------------------------------- */
1211 /* Add the new entity to the in memory index. */
1212 /* -------------------------------------------------------------------- */
1213 if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords )
1215 psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100);
1217 psSHP->panRecOffset = (unsigned int *)
1218 SfRealloc(psSHP->panRecOffset,sizeof(unsigned int) * psSHP->nMaxRecords );
1219 psSHP->panRecSize = (unsigned int *)
1220 SfRealloc(psSHP->panRecSize,sizeof(unsigned int) * psSHP->nMaxRecords );
1223 /* -------------------------------------------------------------------- */
1224 /* Initialize record. */
1225 /* -------------------------------------------------------------------- */
1226 pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double)
1227 + psObject->nParts * 8 + 128);
1229 /* -------------------------------------------------------------------- */
1230 /* Extract vertices for a Polygon or Arc. */
1231 /* -------------------------------------------------------------------- */
1232 if( psObject->nSHPType == SHPT_POLYGON
1233 || psObject->nSHPType == SHPT_POLYGONZ
1234 || psObject->nSHPType == SHPT_POLYGONM
1235 || psObject->nSHPType == SHPT_ARC
1236 || psObject->nSHPType == SHPT_ARCZ
1237 || psObject->nSHPType == SHPT_ARCM
1238 || psObject->nSHPType == SHPT_MULTIPATCH )
1240 int32 nPoints, nParts;
1243 nPoints = psObject->nVertices;
1244 nParts = psObject->nParts;
1246 _SHPSetBounds( pabyRec + 12, psObject );
1248 if( bBigEndian ) SwapWord( 4, &nPoints );
1249 if( bBigEndian ) SwapWord( 4, &nParts );
1251 ByteCopy( &nPoints, pabyRec + 40 + 8, 4 );
1252 ByteCopy( &nParts, pabyRec + 36 + 8, 4 );
1257 * Write part start positions.
1259 ByteCopy( psObject->panPartStart, pabyRec + 44 + 8,
1260 4 * psObject->nParts );
1261 for( i = 0; i < psObject->nParts; i++ )
1263 if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i );
1268 * Write multipatch part types if needed.
1270 if( psObject->nSHPType == SHPT_MULTIPATCH )
1272 memcpy( pabyRec + nRecordSize, psObject->panPartType,
1273 4*psObject->nParts );
1274 for( i = 0; i < psObject->nParts; i++ )
1276 if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize );
1282 * Write the (x,y) vertex values.
1284 for( i = 0; i < psObject->nVertices; i++ )
1286 ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 );
1287 ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 );
1290 SwapWord( 8, pabyRec + nRecordSize );
1293 SwapWord( 8, pabyRec + nRecordSize + 8 );
1295 nRecordSize += 2 * 8;
1299 * Write the Z coordinates (if any).
1301 if( psObject->nSHPType == SHPT_POLYGONZ
1302 || psObject->nSHPType == SHPT_ARCZ
1303 || psObject->nSHPType == SHPT_MULTIPATCH )
1305 ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
1306 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1309 ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
1310 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1313 for( i = 0; i < psObject->nVertices; i++ )
1315 ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
1316 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1322 * Write the M values, if any.
1324 if( psObject->bMeasureIsUsed
1325 && (psObject->nSHPType == SHPT_POLYGONM
1326 || psObject->nSHPType == SHPT_ARCM
1327 #ifndef DISABLE_MULTIPATCH_MEASURE
1328 || psObject->nSHPType == SHPT_MULTIPATCH
1330 || psObject->nSHPType == SHPT_POLYGONZ
1331 || psObject->nSHPType == SHPT_ARCZ) )
1333 ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
1334 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1337 ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
1338 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1341 for( i = 0; i < psObject->nVertices; i++ )
1343 ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
1344 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1350 /* -------------------------------------------------------------------- */
1351 /* Extract vertices for a MultiPoint. */
1352 /* -------------------------------------------------------------------- */
1353 else if( psObject->nSHPType == SHPT_MULTIPOINT
1354 || psObject->nSHPType == SHPT_MULTIPOINTZ
1355 || psObject->nSHPType == SHPT_MULTIPOINTM )
1360 nPoints = psObject->nVertices;
1362 _SHPSetBounds( pabyRec + 12, psObject );
1364 if( bBigEndian ) SwapWord( 4, &nPoints );
1365 ByteCopy( &nPoints, pabyRec + 44, 4 );
1367 for( i = 0; i < psObject->nVertices; i++ )
1369 ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 );
1370 ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 );
1372 if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 );
1373 if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 );
1376 nRecordSize = 48 + 16 * psObject->nVertices;
1378 if( psObject->nSHPType == SHPT_MULTIPOINTZ )
1380 ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
1381 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1384 ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
1385 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1388 for( i = 0; i < psObject->nVertices; i++ )
1390 ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
1391 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1396 if( psObject->bMeasureIsUsed
1397 && (psObject->nSHPType == SHPT_MULTIPOINTZ
1398 || psObject->nSHPType == SHPT_MULTIPOINTM) )
1400 ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
1401 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1404 ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
1405 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1408 for( i = 0; i < psObject->nVertices; i++ )
1410 ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
1411 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1417 /* -------------------------------------------------------------------- */
1419 /* -------------------------------------------------------------------- */
1420 else if( psObject->nSHPType == SHPT_POINT
1421 || psObject->nSHPType == SHPT_POINTZ
1422 || psObject->nSHPType == SHPT_POINTM )
1424 ByteCopy( psObject->padfX, pabyRec + 12, 8 );
1425 ByteCopy( psObject->padfY, pabyRec + 20, 8 );
1427 if( bBigEndian ) SwapWord( 8, pabyRec + 12 );
1428 if( bBigEndian ) SwapWord( 8, pabyRec + 20 );
1432 if( psObject->nSHPType == SHPT_POINTZ )
1434 ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 );
1435 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1439 if( psObject->bMeasureIsUsed
1440 && (psObject->nSHPType == SHPT_POINTZ
1441 || psObject->nSHPType == SHPT_POINTM) )
1443 ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 );
1444 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1449 /* -------------------------------------------------------------------- */
1450 /* Not much to do for null geometries. */
1451 /* -------------------------------------------------------------------- */
1452 else if( psObject->nSHPType == SHPT_NULL )
1463 /* -------------------------------------------------------------------- */
1464 /* Establish where we are going to put this record. If we are */
1465 /* rewriting and existing record, and it will fit, then put it */
1466 /* back where the original came from. Otherwise write at the end. */
1467 /* -------------------------------------------------------------------- */
1468 if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 )
1470 unsigned int nExpectedSize = psSHP->nFileSize + nRecordSize;
1471 if( nExpectedSize < psSHP->nFileSize ) // due to unsigned int overflow
1474 sprintf( str, "Failed to write shape object. "
1475 "File size cannot reach %u + %u.",
1476 psSHP->nFileSize, nRecordSize );
1477 psSHP->sHooks.Error( str );
1482 if( nShapeId == -1 )
1483 nShapeId = psSHP->nRecords++;
1485 psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize;
1486 psSHP->panRecSize[nShapeId] = nRecordSize-8;
1487 psSHP->nFileSize += nRecordSize;
1491 nRecordOffset = psSHP->panRecOffset[nShapeId];
1492 psSHP->panRecSize[nShapeId] = nRecordSize-8;
1495 /* -------------------------------------------------------------------- */
1496 /* Set the shape type, record number, and record size. */
1497 /* -------------------------------------------------------------------- */
1498 i32 = nShapeId+1; /* record # */
1499 if( !bBigEndian ) SwapWord( 4, &i32 );
1500 ByteCopy( &i32, pabyRec, 4 );
1502 i32 = (nRecordSize-8)/2; /* record size */
1503 if( !bBigEndian ) SwapWord( 4, &i32 );
1504 ByteCopy( &i32, pabyRec + 4, 4 );
1506 i32 = psObject->nSHPType; /* shape type */
1507 if( bBigEndian ) SwapWord( 4, &i32 );
1508 ByteCopy( &i32, pabyRec + 8, 4 );
1510 /* -------------------------------------------------------------------- */
1511 /* Write out record. */
1512 /* -------------------------------------------------------------------- */
1513 if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 )
1515 psSHP->sHooks.Error( "Error in psSHP->sHooks.FSeek() while writing object to .shp file." );
1519 if( psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 )
1521 psSHP->sHooks.Error( "Error in psSHP->sHooks.Fwrite() while writing object to .shp file." );
1528 /* -------------------------------------------------------------------- */
1529 /* Expand file wide bounds based on this shape. */
1530 /* -------------------------------------------------------------------- */
1531 if( psSHP->adBoundsMin[0] == 0.0
1532 && psSHP->adBoundsMax[0] == 0.0
1533 && psSHP->adBoundsMin[1] == 0.0
1534 && psSHP->adBoundsMax[1] == 0.0 )
1536 if( psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0 )
1538 psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0;
1539 psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0;
1540 psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0;
1541 psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0;
1545 psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0];
1546 psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0];
1547 psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0];
1548 psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0];
1552 for( i = 0; i < psObject->nVertices; i++ )
1554 psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]);
1555 psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]);
1556 psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]);
1557 psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]);
1558 psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]);
1559 psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]);
1560 psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]);
1561 psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]);
1567 /************************************************************************/
1568 /* SHPReadObject() */
1570 /* Read the vertices, parts, and other non-attribute information */
1571 /* for one shape. */
1572 /************************************************************************/
1574 SHPObject SHPAPI_CALL1(*)
1575 SHPReadObject( SHPHandle psSHP, int hEntity )
1578 int nEntitySize, nRequiredSize;
1580 char szErrorMsg[128];
1582 /* -------------------------------------------------------------------- */
1583 /* Validate the record/entity number. */
1584 /* -------------------------------------------------------------------- */
1585 if( hEntity < 0 || hEntity >= psSHP->nRecords )
1588 /* -------------------------------------------------------------------- */
1589 /* Ensure our record buffer is large enough. */
1590 /* -------------------------------------------------------------------- */
1591 nEntitySize = psSHP->panRecSize[hEntity]+8;
1592 if( nEntitySize > psSHP->nBufSize )
1594 psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize);
1595 if (psSHP->pabyRec == NULL)
1599 /* Reallocate previous successfull size for following features */
1600 psSHP->pabyRec = (uchar *) malloc(psSHP->nBufSize);
1603 "Not enough memory to allocate requested memory (nBufSize=%d). "
1604 "Probably broken SHP file", psSHP->nBufSize );
1605 psSHP->sHooks.Error( szError );
1609 /* Only set new buffer size after successfull alloc */
1610 psSHP->nBufSize = nEntitySize;
1613 /* In case we were not able to reallocate the buffer on a previous step */
1614 if (psSHP->pabyRec == NULL)
1619 /* -------------------------------------------------------------------- */
1620 /* Read the record. */
1621 /* -------------------------------------------------------------------- */
1622 if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0 )
1625 * TODO - mloskot: Consider detailed diagnostics of shape file,
1626 * for example to detect if file is truncated.
1630 "Error in fseek() reading object from .shp file at offset %u",
1631 psSHP->panRecOffset[hEntity]);
1633 psSHP->sHooks.Error( str );
1637 if( psSHP->sHooks.FRead( psSHP->pabyRec, nEntitySize, 1, psSHP->fpSHP ) != 1 )
1640 * TODO - mloskot: Consider detailed diagnostics of shape file,
1641 * for example to detect if file is truncated.
1645 "Error in fread() reading object of size %u at offset %u from .shp file",
1646 nEntitySize, psSHP->panRecOffset[hEntity] );
1648 psSHP->sHooks.Error( str );
1652 /* -------------------------------------------------------------------- */
1653 /* Allocate and minimally initialize the object. */
1654 /* -------------------------------------------------------------------- */
1655 psShape = (SHPObject *) calloc(1,sizeof(SHPObject));
1656 psShape->nShapeId = hEntity;
1657 psShape->bMeasureIsUsed = FALSE;
1659 if ( 8 + 4 > nEntitySize )
1661 snprintf(szErrorMsg, sizeof(szErrorMsg),
1662 "Corrupted .shp file : shape %d : nEntitySize = %d",
1663 hEntity, nEntitySize);
1664 psSHP->sHooks.Error( szErrorMsg );
1665 SHPDestroyObject(psShape);
1668 memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 );
1670 if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) );
1672 /* ==================================================================== */
1673 /* Extract vertices for a Polygon or Arc. */
1674 /* ==================================================================== */
1675 if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC
1676 || psShape->nSHPType == SHPT_POLYGONZ
1677 || psShape->nSHPType == SHPT_POLYGONM
1678 || psShape->nSHPType == SHPT_ARCZ
1679 || psShape->nSHPType == SHPT_ARCM
1680 || psShape->nSHPType == SHPT_MULTIPATCH )
1682 int32 nPoints, nParts;
1685 if ( 40 + 8 + 4 > nEntitySize )
1687 snprintf(szErrorMsg, sizeof(szErrorMsg),
1688 "Corrupted .shp file : shape %d : nEntitySize = %d",
1689 hEntity, nEntitySize);
1690 psSHP->sHooks.Error( szErrorMsg );
1691 SHPDestroyObject(psShape);
1694 /* -------------------------------------------------------------------- */
1695 /* Get the X/Y bounds. */
1696 /* -------------------------------------------------------------------- */
1697 memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 );
1698 memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
1699 memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
1700 memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
1702 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
1703 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
1704 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
1705 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
1707 /* -------------------------------------------------------------------- */
1708 /* Extract part/point count, and build vertex and part arrays */
1709 /* to proper size. */
1710 /* -------------------------------------------------------------------- */
1711 memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 );
1712 memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 );
1714 if( bBigEndian ) SwapWord( 4, &nPoints );
1715 if( bBigEndian ) SwapWord( 4, &nParts );
1717 if (nPoints < 0 || nParts < 0 ||
1718 nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000)
1720 snprintf(szErrorMsg, sizeof(szErrorMsg),
1721 "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.",
1722 hEntity, nPoints, nParts);
1723 psSHP->sHooks.Error( szErrorMsg );
1724 SHPDestroyObject(psShape);
1728 /* With the previous checks on nPoints and nParts, */
1729 /* we should not overflow here and after */
1730 /* since 50 M * (16 + 8 + 8) = 1 600 MB */
1731 nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints;
1732 if ( psShape->nSHPType == SHPT_POLYGONZ
1733 || psShape->nSHPType == SHPT_ARCZ
1734 || psShape->nSHPType == SHPT_MULTIPATCH )
1736 nRequiredSize += 16 + 8 * nPoints;
1738 if( psShape->nSHPType == SHPT_MULTIPATCH )
1740 nRequiredSize += 4 * nParts;
1742 if (nRequiredSize > nEntitySize)
1744 snprintf(szErrorMsg, sizeof(szErrorMsg),
1745 "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d, nEntitySize=%d.",
1746 hEntity, nPoints, nParts, nEntitySize);
1747 psSHP->sHooks.Error( szErrorMsg );
1748 SHPDestroyObject(psShape);
1752 psShape->nVertices = nPoints;
1753 psShape->padfX = (double *) calloc(nPoints,sizeof(double));
1754 psShape->padfY = (double *) calloc(nPoints,sizeof(double));
1755 psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
1756 psShape->padfM = (double *) calloc(nPoints,sizeof(double));
1758 psShape->nParts = nParts;
1759 psShape->panPartStart = (int *) calloc(nParts,sizeof(int));
1760 psShape->panPartType = (int *) calloc(nParts,sizeof(int));
1762 if (psShape->padfX == NULL ||
1763 psShape->padfY == NULL ||
1764 psShape->padfZ == NULL ||
1765 psShape->padfM == NULL ||
1766 psShape->panPartStart == NULL ||
1767 psShape->panPartType == NULL)
1769 snprintf(szErrorMsg, sizeof(szErrorMsg),
1770 "Not enough memory to allocate requested memory (nPoints=%d, nParts=%d) for shape %d. "
1771 "Probably broken SHP file", hEntity, nPoints, nParts );
1772 psSHP->sHooks.Error( szErrorMsg );
1773 SHPDestroyObject(psShape);
1777 for( i = 0; i < nParts; i++ )
1778 psShape->panPartType[i] = SHPP_RING;
1780 /* -------------------------------------------------------------------- */
1781 /* Copy out the part array from the record. */
1782 /* -------------------------------------------------------------------- */
1783 memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts );
1784 for( i = 0; i < nParts; i++ )
1786 if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i );
1788 /* We check that the offset is inside the vertex array */
1789 if (psShape->panPartStart[i] < 0
1790 || (psShape->panPartStart[i] >= psShape->nVertices
1791 && psShape->nVertices > 0) )
1793 snprintf(szErrorMsg, sizeof(szErrorMsg),
1794 "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d",
1795 hEntity, i, psShape->panPartStart[i], psShape->nVertices);
1796 psSHP->sHooks.Error( szErrorMsg );
1797 SHPDestroyObject(psShape);
1800 if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1])
1802 snprintf(szErrorMsg, sizeof(szErrorMsg),
1803 "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d",
1804 hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]);
1805 psSHP->sHooks.Error( szErrorMsg );
1806 SHPDestroyObject(psShape);
1811 nOffset = 44 + 8 + 4*nParts;
1813 /* -------------------------------------------------------------------- */
1814 /* If this is a multipatch, we will also have parts types. */
1815 /* -------------------------------------------------------------------- */
1816 if( psShape->nSHPType == SHPT_MULTIPATCH )
1818 memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts );
1819 for( i = 0; i < nParts; i++ )
1821 if( bBigEndian ) SwapWord( 4, psShape->panPartType+i );
1824 nOffset += 4*nParts;
1827 /* -------------------------------------------------------------------- */
1828 /* Copy out the vertices from the record. */
1829 /* -------------------------------------------------------------------- */
1830 for( i = 0; i < nPoints; i++ )
1832 memcpy(psShape->padfX + i,
1833 psSHP->pabyRec + nOffset + i * 16,
1836 memcpy(psShape->padfY + i,
1837 psSHP->pabyRec + nOffset + i * 16 + 8,
1840 if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
1841 if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
1844 nOffset += 16*nPoints;
1846 /* -------------------------------------------------------------------- */
1847 /* If we have a Z coordinate, collect that now. */
1848 /* -------------------------------------------------------------------- */
1849 if( psShape->nSHPType == SHPT_POLYGONZ
1850 || psShape->nSHPType == SHPT_ARCZ
1851 || psShape->nSHPType == SHPT_MULTIPATCH )
1853 memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
1854 memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
1856 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
1857 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
1859 for( i = 0; i < nPoints; i++ )
1861 memcpy( psShape->padfZ + i,
1862 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1863 if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
1866 nOffset += 16 + 8*nPoints;
1869 /* -------------------------------------------------------------------- */
1870 /* If we have a M measure value, then read it now. We assume */
1871 /* that the measure can be present for any shape if the size is */
1872 /* big enough, but really it will only occur for the Z shapes */
1873 /* (options), and the M shapes. */
1874 /* -------------------------------------------------------------------- */
1875 if( nEntitySize >= nOffset + 16 + 8*nPoints )
1877 memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
1878 memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
1880 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
1881 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
1883 for( i = 0; i < nPoints; i++ )
1885 memcpy( psShape->padfM + i,
1886 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1887 if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
1889 psShape->bMeasureIsUsed = TRUE;
1893 /* ==================================================================== */
1894 /* Extract vertices for a MultiPoint. */
1895 /* ==================================================================== */
1896 else if( psShape->nSHPType == SHPT_MULTIPOINT
1897 || psShape->nSHPType == SHPT_MULTIPOINTM
1898 || psShape->nSHPType == SHPT_MULTIPOINTZ )
1903 if ( 44 + 4 > nEntitySize )
1905 snprintf(szErrorMsg, sizeof(szErrorMsg),
1906 "Corrupted .shp file : shape %d : nEntitySize = %d",
1907 hEntity, nEntitySize);
1908 psSHP->sHooks.Error( szErrorMsg );
1909 SHPDestroyObject(psShape);
1912 memcpy( &nPoints, psSHP->pabyRec + 44, 4 );
1914 if( bBigEndian ) SwapWord( 4, &nPoints );
1916 if (nPoints < 0 || nPoints > 50 * 1000 * 1000)
1918 snprintf(szErrorMsg, sizeof(szErrorMsg),
1919 "Corrupted .shp file : shape %d : nPoints = %d",
1921 psSHP->sHooks.Error( szErrorMsg );
1922 SHPDestroyObject(psShape);
1926 nRequiredSize = 48 + nPoints * 16;
1927 if( psShape->nSHPType == SHPT_MULTIPOINTZ )
1929 nRequiredSize += 16 + nPoints * 8;
1931 if (nRequiredSize > nEntitySize)
1933 snprintf(szErrorMsg, sizeof(szErrorMsg),
1934 "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d",
1935 hEntity, nPoints, nEntitySize);
1936 psSHP->sHooks.Error( szErrorMsg );
1937 SHPDestroyObject(psShape);
1941 psShape->nVertices = nPoints;
1942 psShape->padfX = (double *) calloc(nPoints,sizeof(double));
1943 psShape->padfY = (double *) calloc(nPoints,sizeof(double));
1944 psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
1945 psShape->padfM = (double *) calloc(nPoints,sizeof(double));
1947 if (psShape->padfX == NULL ||
1948 psShape->padfY == NULL ||
1949 psShape->padfZ == NULL ||
1950 psShape->padfM == NULL)
1952 snprintf(szErrorMsg, sizeof(szErrorMsg),
1953 "Not enough memory to allocate requested memory (nPoints=%d) for shape %d. "
1954 "Probably broken SHP file", hEntity, nPoints );
1955 psSHP->sHooks.Error( szErrorMsg );
1956 SHPDestroyObject(psShape);
1960 for( i = 0; i < nPoints; i++ )
1962 memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 );
1963 memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 );
1965 if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
1966 if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
1969 nOffset = 48 + 16*nPoints;
1971 /* -------------------------------------------------------------------- */
1972 /* Get the X/Y bounds. */
1973 /* -------------------------------------------------------------------- */
1974 memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 );
1975 memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
1976 memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
1977 memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
1979 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
1980 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
1981 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
1982 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
1984 /* -------------------------------------------------------------------- */
1985 /* If we have a Z coordinate, collect that now. */
1986 /* -------------------------------------------------------------------- */
1987 if( psShape->nSHPType == SHPT_MULTIPOINTZ )
1989 memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
1990 memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
1992 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
1993 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
1995 for( i = 0; i < nPoints; i++ )
1997 memcpy( psShape->padfZ + i,
1998 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1999 if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
2002 nOffset += 16 + 8*nPoints;
2005 /* -------------------------------------------------------------------- */
2006 /* If we have a M measure value, then read it now. We assume */
2007 /* that the measure can be present for any shape if the size is */
2008 /* big enough, but really it will only occur for the Z shapes */
2009 /* (options), and the M shapes. */
2010 /* -------------------------------------------------------------------- */
2011 if( nEntitySize >= nOffset + 16 + 8*nPoints )
2013 memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
2014 memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
2016 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
2017 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
2019 for( i = 0; i < nPoints; i++ )
2021 memcpy( psShape->padfM + i,
2022 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
2023 if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
2025 psShape->bMeasureIsUsed = TRUE;
2029 /* ==================================================================== */
2030 /* Extract vertices for a point. */
2031 /* ==================================================================== */
2032 else if( psShape->nSHPType == SHPT_POINT
2033 || psShape->nSHPType == SHPT_POINTM
2034 || psShape->nSHPType == SHPT_POINTZ )
2038 psShape->nVertices = 1;
2039 psShape->padfX = (double *) calloc(1,sizeof(double));
2040 psShape->padfY = (double *) calloc(1,sizeof(double));
2041 psShape->padfZ = (double *) calloc(1,sizeof(double));
2042 psShape->padfM = (double *) calloc(1,sizeof(double));
2044 if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize)
2046 snprintf(szErrorMsg, sizeof(szErrorMsg),
2047 "Corrupted .shp file : shape %d : nEntitySize = %d",
2048 hEntity, nEntitySize);
2049 psSHP->sHooks.Error( szErrorMsg );
2050 SHPDestroyObject(psShape);
2053 memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 );
2054 memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 );
2056 if( bBigEndian ) SwapWord( 8, psShape->padfX );
2057 if( bBigEndian ) SwapWord( 8, psShape->padfY );
2061 /* -------------------------------------------------------------------- */
2062 /* If we have a Z coordinate, collect that now. */
2063 /* -------------------------------------------------------------------- */
2064 if( psShape->nSHPType == SHPT_POINTZ )
2066 memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 );
2068 if( bBigEndian ) SwapWord( 8, psShape->padfZ );
2073 /* -------------------------------------------------------------------- */
2074 /* If we have a M measure value, then read it now. We assume */
2075 /* that the measure can be present for any shape if the size is */
2076 /* big enough, but really it will only occur for the Z shapes */
2077 /* (options), and the M shapes. */
2078 /* -------------------------------------------------------------------- */
2079 if( nEntitySize >= nOffset + 8 )
2081 memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 );
2083 if( bBigEndian ) SwapWord( 8, psShape->padfM );
2084 psShape->bMeasureIsUsed = TRUE;
2087 /* -------------------------------------------------------------------- */
2088 /* Since no extents are supplied in the record, we will apply */
2089 /* them from the single vertex. */
2090 /* -------------------------------------------------------------------- */
2091 psShape->dfXMin = psShape->dfXMax = psShape->padfX[0];
2092 psShape->dfYMin = psShape->dfYMax = psShape->padfY[0];
2093 psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0];
2094 psShape->dfMMin = psShape->dfMMax = psShape->padfM[0];
2100 /************************************************************************/
2102 /************************************************************************/
2104 const char SHPAPI_CALL1(*)
2105 SHPTypeName( int nSHPType )
2122 case SHPT_MULTIPOINT:
2123 return "MultiPoint";
2134 case SHPT_MULTIPOINTZ:
2135 return "MultiPointZ";
2146 case SHPT_MULTIPOINTM:
2147 return "MultiPointM";
2149 case SHPT_MULTIPATCH:
2150 return "MultiPatch";
2153 return "UnknownShapeType";
2157 /************************************************************************/
2158 /* SHPPartTypeName() */
2159 /************************************************************************/
2161 const char SHPAPI_CALL1(*)
2162 SHPPartTypeName( int nPartType )
2168 return "TriangleStrip";
2171 return "TriangleFan";
2173 case SHPP_OUTERRING:
2176 case SHPP_INNERRING:
2179 case SHPP_FIRSTRING:
2186 return "UnknownPartType";
2190 /************************************************************************/
2191 /* SHPDestroyObject() */
2192 /************************************************************************/
2195 SHPDestroyObject( SHPObject * psShape )
2198 if( psShape == NULL )
2201 if( psShape->padfX != NULL )
2202 free( psShape->padfX );
2203 if( psShape->padfY != NULL )
2204 free( psShape->padfY );
2205 if( psShape->padfZ != NULL )
2206 free( psShape->padfZ );
2207 if( psShape->padfM != NULL )
2208 free( psShape->padfM );
2210 if( psShape->panPartStart != NULL )
2211 free( psShape->panPartStart );
2212 if( psShape->panPartType != NULL )
2213 free( psShape->panPartType );
2218 /************************************************************************/
2219 /* SHPRewindObject() */
2221 /* Reset the winding of polygon objects to adhere to the */
2222 /* specification. */
2223 /************************************************************************/
2226 SHPRewindObject( SHPHandle hSHP, SHPObject * psObject )
2229 int iOpRing, bAltered = 0;
2231 /* -------------------------------------------------------------------- */
2232 /* Do nothing if this is not a polygon object. */
2233 /* -------------------------------------------------------------------- */
2234 if( psObject->nSHPType != SHPT_POLYGON
2235 && psObject->nSHPType != SHPT_POLYGONZ
2236 && psObject->nSHPType != SHPT_POLYGONM )
2239 if( psObject->nVertices == 0 || psObject->nParts == 0 )
2242 /* -------------------------------------------------------------------- */
2243 /* Process each of the rings. */
2244 /* -------------------------------------------------------------------- */
2245 for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ )
2247 int bInner, iVert, nVertCount, nVertStart, iCheckRing;
2248 double dfSum, dfTestX, dfTestY;
2250 /* -------------------------------------------------------------------- */
2251 /* Determine if this ring is an inner ring or an outer ring */
2252 /* relative to all the other rings. For now we assume the */
2253 /* first ring is outer and all others are inner, but eventually */
2254 /* we need to fix this to handle multiple island polygons and */
2255 /* unordered sets of rings. */
2257 /* -------------------------------------------------------------------- */
2259 /* Use point in the middle of segment to avoid testing
2260 * common points of rings.
2262 dfTestX = ( psObject->padfX[psObject->panPartStart[iOpRing]]
2263 + psObject->padfX[psObject->panPartStart[iOpRing] + 1] ) / 2;
2264 dfTestY = ( psObject->padfY[psObject->panPartStart[iOpRing]]
2265 + psObject->padfY[psObject->panPartStart[iOpRing] + 1] ) / 2;
2268 for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ )
2272 if( iCheckRing == iOpRing )
2275 nVertStart = psObject->panPartStart[iCheckRing];
2277 if( iCheckRing == psObject->nParts-1 )
2278 nVertCount = psObject->nVertices
2279 - psObject->panPartStart[iCheckRing];
2281 nVertCount = psObject->panPartStart[iCheckRing+1]
2282 - psObject->panPartStart[iCheckRing];
2284 for( iEdge = 0; iEdge < nVertCount; iEdge++ )
2288 if( iEdge < nVertCount-1 )
2294 * Test whether the edge 'straddles' the horizontal ray from the test point (dfTestY,dfTestY)
2295 * The rule #1 also excludes edges collinear with the ray.
2297 if ( ( psObject->padfY[iEdge+nVertStart] < dfTestY
2298 && dfTestY <= psObject->padfY[iNext+nVertStart] )
2299 || ( psObject->padfY[iNext+nVertStart] < dfTestY
2300 && dfTestY <= psObject->padfY[iEdge+nVertStart] ) )
2303 * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY)
2305 double const intersect =
2306 ( psObject->padfX[iEdge+nVertStart]
2307 + ( dfTestY - psObject->padfY[iEdge+nVertStart] )
2308 / ( psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart] )
2309 * ( psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart] ) );
2311 if (intersect < dfTestX)
2317 } /* for iCheckRing */
2319 /* -------------------------------------------------------------------- */
2320 /* Determine the current order of this ring so we will know if */
2321 /* it has to be reversed. */
2322 /* -------------------------------------------------------------------- */
2323 nVertStart = psObject->panPartStart[iOpRing];
2325 if( iOpRing == psObject->nParts-1 )
2326 nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing];
2328 nVertCount = psObject->panPartStart[iOpRing+1]
2329 - psObject->panPartStart[iOpRing];
2334 dfSum = psObject->padfX[nVertStart] * (psObject->padfY[nVertStart+1] - psObject->padfY[nVertStart+nVertCount-1]);
2335 for( iVert = nVertStart + 1; iVert < nVertStart+nVertCount-1; iVert++ )
2337 dfSum += psObject->padfX[iVert] * (psObject->padfY[iVert+1] - psObject->padfY[iVert-1]);
2340 dfSum += psObject->padfX[iVert] * (psObject->padfY[nVertStart] - psObject->padfY[iVert-1]);
2342 /* -------------------------------------------------------------------- */
2343 /* Reverse if necessary. */
2344 /* -------------------------------------------------------------------- */
2345 if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) )
2350 for( i = 0; i < nVertCount/2; i++ )
2355 dfSaved = psObject->padfX[nVertStart+i];
2356 psObject->padfX[nVertStart+i] =
2357 psObject->padfX[nVertStart+nVertCount-i-1];
2358 psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved;
2361 dfSaved = psObject->padfY[nVertStart+i];
2362 psObject->padfY[nVertStart+i] =
2363 psObject->padfY[nVertStart+nVertCount-i-1];
2364 psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved;
2367 if( psObject->padfZ )
2369 dfSaved = psObject->padfZ[nVertStart+i];
2370 psObject->padfZ[nVertStart+i] =
2371 psObject->padfZ[nVertStart+nVertCount-i-1];
2372 psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved;
2376 if( psObject->padfM )
2378 dfSaved = psObject->padfM[nVertStart+i];
2379 psObject->padfM[nVertStart+i] =
2380 psObject->padfM[nVertStart+nVertCount-i-1];
2381 psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved;