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 <(int) 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 <(int) 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 <(int) 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 <(int) 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 <(int) 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 >= (int)(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 <(int) 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 <(int) 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 <(int) 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 >=(int)( 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 <(int) 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;