1 /******************************************************************************
2 * $Id: shpopen.c,v 1.73 2012-01-24 22:33:01 fwarmerdam Exp $
3 *
4 * Project: Shapelib
5 * Purpose: Implementation of core Shapefile read/write functions.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
7 *
8 ******************************************************************************
9 * Copyright (c) 1999, 2001, Frank Warmerdam
10 *
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.
14 *
15 * --
16 *
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:
23 *
24 * The above copyright notice and this permission notice shall be included
25 * in all copies or substantial portions of the Software.
26 *
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 ******************************************************************************
35 *
36 * $Log: shpopen.c,v $
37 * Revision 1.73 2012-01-24 22:33:01 fwarmerdam
38 * fix memory leak on failure to open .shp (gdal #4410)
39 *
40 * Revision 1.72 2011-12-11 22:45:28 fwarmerdam
41 * fix failure return from SHPOpenLL.
42 *
43 * Revision 1.71 2011-09-15 03:33:58 fwarmerdam
44 * fix missing cast (#2344)
45 *
46 * Revision 1.70 2011-07-24 05:59:25 fwarmerdam
47 * minimize use of CPLError in favor of SAHooks.Error()
48 *
49 * Revision 1.69 2011-07-24 03:24:22 fwarmerdam
50 * fix memory leaks in error cases creating shapefiles (#2061)
51 *
52 * Revision 1.68 2010-08-27 23:42:52 fwarmerdam
53 * add SHPAPI_CALL attribute in code
54 *
55 * Revision 1.67 2010-07-01 08:15:48 fwarmerdam
56 * do not error out on an object with zero vertices
57 *
58 * Revision 1.66 2010-07-01 07:58:57 fwarmerdam
59 * minor cleanup of error handling
60 *
61 * Revision 1.65 2010-07-01 07:27:13 fwarmerdam
62 * white space formatting adjustments
63 *
64 * Revision 1.64 2010-01-28 11:34:34 fwarmerdam
65 * handle the shape file length limits more gracefully (#3236)
66 *
67 * Revision 1.63 2010-01-28 04:04:40 fwarmerdam
68 * improve numerical accuracy of SHPRewind() algs (gdal #3363)
69 *
70 * Revision 1.62 2010-01-17 05:34:13 fwarmerdam
71 * Remove asserts on x/y being null (#2148).
72 *
73 * Revision 1.61 2010-01-16 05:07:42 fwarmerdam
74 * allow 0/nulls in shpcreateobject (#2148)
75 *
76 * Revision 1.60 2009-09-17 20:50:02 bram
77 * on Win32, define snprintf as alias to _snprintf
78 *
79 * Revision 1.59 2008-03-14 05:25:31 fwarmerdam
80 * Correct crash on buggy geometries (gdal #2218)
81 *
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
84 *
85 * Revision 1.57 2007/12/06 07:00:25 fwarmerdam
86 * dbfopen now using SAHooks for fileio
87 *
88 * Revision 1.56 2007/12/04 20:37:56 fwarmerdam
89 * preliminary implementation of hooks api for io and errors
90 *
91 * Revision 1.55 2007/11/21 22:39:56 fwarmerdam
92 * close shx file in readonly mode (GDAL #1956)
93 *
94 * Revision 1.54 2007/11/15 00:12:47 mloskot
95 * Backported recent changes from GDAL (Ticket #1415) to Shapelib.
96 *
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
100 *
101 * Revision 1.52 2007/06/21 15:58:33 fwarmerdam
102 * fix for SHPRewindObject when rings touch at one vertex (gdal #976)
103 *
104 * Revision 1.51 2006/09/04 15:24:01 fwarmerdam
105 * Fixed up log message for 1.49.
106 *
107 * Revision 1.50 2006/09/04 15:21:39 fwarmerdam
108 * fix of last fix
109 *
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
114 *
115 * Revision 1.48 2006/01/26 15:07:32 fwarmerdam
116 * add bMeasureIsUsed flag from Craig Bruce: Bug 1249
117 *
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.
121 *
122 * Revision 1.46 2005/02/11 17:17:46 fwarmerdam
123 * added panPartStart[0] validation
124 *
125 * Revision 1.45 2004/09/26 20:09:48 fwarmerdam
126 * const correctness changes
127 *
128 * Revision 1.44 2003/12/29 00:18:39 fwarmerdam
129 * added error checking for failed IO and optional CPL error reporting
130 *
131 * Revision 1.43 2003/12/01 16:20:08 warmerda
132 * be careful of zero vertex shapes
133 *
134 * Revision 1.42 2003/12/01 14:58:27 warmerda
135 * added degenerate object check in SHPRewindObject()
136 *
137 * Revision 1.41 2003/07/08 15:22:43 warmerda
138 * avoid warning
139 *
140 * Revision 1.40 2003/04/21 18:30:37 warmerda
141 * added header write/update public methods
142 *
143 * Revision 1.39 2002/08/26 06:46:56 warmerda
144 * avoid c++ comments
145 *
146 * Revision 1.38 2002/05/07 16:43:39 warmerda
147 * Removed debugging printf.
148 *
149 * Revision 1.37 2002/04/10 17:35:22 warmerda
150 * fixed bug in ring reversal code
151 *
152 * Revision 1.36 2002/04/10 16:59:54 warmerda
153 * added SHPRewindObject
154 *
155 * Revision 1.35 2001/12/07 15:10:44 warmerda
156 * fix if .shx fails to open
157 *
158 * Revision 1.34 2001/11/01 16:29:55 warmerda
159 * move pabyRec into SHPInfo for thread safety
160 *
161 * Revision 1.33 2001/07/03 12:18:15 warmerda
162 * Improved cleanup if SHX not found, provied by Riccardo Cohen.
163 *
164 * Revision 1.32 2001/06/22 01:58:07 warmerda
165 * be more careful about establishing initial bounds in face of NULL shapes
166 *
167 * Revision 1.31 2001/05/31 19:35:29 warmerda
168 * added support for writing null shapes
169 *
170 * Revision 1.30 2001/05/28 12:46:29 warmerda
171 * Add some checking on reasonableness of record count when opening.
172 *
173 * Revision 1.29 2001/05/23 13:36:52 warmerda
174 * added use of SHPAPI_CALL
175 *
176 * Revision 1.28 2001/02/06 22:25:06 warmerda
177 * fixed memory leaks when SHPOpen() fails
178 *
179 * Revision 1.27 2000/07/18 15:21:33 warmerda
180 * added better enforcement of -1 for append in SHPWriteObject
181 *
182 * Revision 1.26 2000/02/16 16:03:51 warmerda
183 * added null shape support
184 *
185 * Revision 1.25 1999/12/15 13:47:07 warmerda
186 * Fixed record size settings in .shp file (was 4 words too long)
187 * Added stdlib.h.
188 *
189 * Revision 1.24 1999/11/05 14:12:04 warmerda
190 * updated license terms
191 *
192 * Revision 1.23 1999/07/27 00:53:46 warmerda
193 * added support for rewriting shapes
194 *
195 * Revision 1.22 1999/06/11 19:19:11 warmerda
196 * Cleanup pabyRec static buffer on SHPClose().
197 *
198 * Revision 1.21 1999/06/02 14:57:56 kshih
199 * Remove unused variables
200 *
201 * Revision 1.20 1999/04/19 21:04:17 warmerda
202 * Fixed syntax error.
203 *
204 * Revision 1.19 1999/04/19 21:01:57 warmerda
205 * Force access string to binary in SHPOpen().
206 *
207 * Revision 1.18 1999/04/01 18:48:07 warmerda
208 * Try upper case extensions if lower case doesn't work.
209 *
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.
213 *
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.
217 *
218 * Revision 1.15 1998/12/03 16:35:29 warmerda
219 * r+b is proper binary access string, not rb+.
220 *
221 * Revision 1.14 1998/12/03 15:47:56 warmerda
222 * Fixed setting of nVertices in SHPCreateObject().
223 *
224 * Revision 1.13 1998/12/03 15:33:54 warmerda
225 * Made SHPCalculateExtents() separately callable.
226 *
227 * Revision 1.12 1998/11/11 20:01:50 warmerda
228 * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines.
229 *
230 * Revision 1.11 1998/11/09 20:56:44 warmerda
231 * Fixed up handling of file wide bounds.
232 *
233 * Revision 1.10 1998/11/09 20:18:51 warmerda
234 * Converted to support 3D shapefiles, and use of SHPObject.
235 *
236 * Revision 1.9 1998/02/24 15:09:05 warmerda
237 * Fixed memory leak.
238 *
239 * Revision 1.8 1997/12/04 15:40:29 warmerda
240 * Fixed byte swapping of record number, and record length fields in the
241 * .shp file.
242 *
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.
246 *
247 * Revision 1.6 1995/09/04 04:19:41 warmerda
248 * Added fix for file bounds.
249 *
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.
253 *
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).
257 *
258 * Revision 1.3 1995/08/23 02:23:15 warmerda
259 * Added support for reading bounds, and fixed up problems in setting the
260 * file wide bounds.
261 *
262 * Revision 1.2 1995/08/04 03:16:57 warmerda
263 * Added header.
264 *
265 */
266
267 #include "shapefil.h"
268
269 #include <math.h>
270 #include <limits.h>
271 #include <assert.h>
272 #include <stdlib.h>
273 #include <string.h>
274 #include <stdio.h>
275
276 SHP_CVSID("$Id: shpopen.c,v 1.73 2012-01-24 22:33:01 fwarmerdam Exp $")
277
278 typedef unsigned char uchar;
279
280 #if UINT_MAX == 65535
281 typedef unsigned long int32;
282 #else
283 typedef unsigned int int32;
284 #endif
285
286 #ifndef FALSE
287 # define FALSE 0
288 # define TRUE 1
289 #endif
290
291 #define ByteCopy( a, b, c ) memcpy( b, a, c )
292 #ifndef MAX
293 # define MIN(a,b) ((a<b) ? a : b)
294 # define MAX(a,b) ((a>b) ? a : b)
295 #endif
296
297 #if defined(WIN32) || defined(_WIN32)
298 # ifndef snprintf
299 # define snprintf _snprintf
300 # endif
301 #endif
302
303 static int bBigEndian;
304
305
306 /************************************************************************/
307 /* SwapWord() */
308 /* */
309 /* Swap a 2, 4 or 8 byte word. */
310 /************************************************************************/
311
SwapWord(int length,void * wordP)312 static void SwapWord( int length, void * wordP )
313
314 {
315 int i;
316 uchar temp;
317
318 for( i=0; i < length/2; i++ )
319 {
320 temp = ((uchar *) wordP)[i];
321 ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1];
322 ((uchar *) wordP)[length-i-1] = temp;
323 }
324 }
325
326 /************************************************************************/
327 /* SfRealloc() */
328 /* */
329 /* A realloc cover function that will access a NULL pointer as */
330 /* a valid input. */
331 /************************************************************************/
332
SfRealloc(void * pMem,int nNewSize)333 static void * SfRealloc( void * pMem, int nNewSize )
334
335 {
336 if( pMem == NULL )
337 return( (void *) malloc(nNewSize) );
338 else
339 return( (void *) realloc(pMem,nNewSize) );
340 }
341
342 /************************************************************************/
343 /* SHPWriteHeader() */
344 /* */
345 /* Write out a header for the .shp and .shx files as well as the */
346 /* contents of the index (.shx) file. */
347 /************************************************************************/
348
SHPWriteHeader(SHPHandle psSHP)349 void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP )
350
351 {
352 uchar abyHeader[100];
353 int i;
354 int32 i32;
355 double dValue;
356 int32 *panSHX;
357
358 if (psSHP->fpSHX == NULL)
359 {
360 psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed");
361 return;
362 }
363
364 /* -------------------------------------------------------------------- */
365 /* Prepare header block for .shp file. */
366 /* -------------------------------------------------------------------- */
367 for( i = 0; i < 100; i++ )
368 abyHeader[i] = 0;
369
370 abyHeader[2] = 0x27; /* magic cookie */
371 abyHeader[3] = 0x0a;
372
373 i32 = psSHP->nFileSize/2; /* file size */
374 ByteCopy( &i32, abyHeader+24, 4 );
375 if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
376
377 i32 = 1000; /* version */
378 ByteCopy( &i32, abyHeader+28, 4 );
379 if( bBigEndian ) SwapWord( 4, abyHeader+28 );
380
381 i32 = psSHP->nShapeType; /* shape type */
382 ByteCopy( &i32, abyHeader+32, 4 );
383 if( bBigEndian ) SwapWord( 4, abyHeader+32 );
384
385 dValue = psSHP->adBoundsMin[0]; /* set bounds */
386 ByteCopy( &dValue, abyHeader+36, 8 );
387 if( bBigEndian ) SwapWord( 8, abyHeader+36 );
388
389 dValue = psSHP->adBoundsMin[1];
390 ByteCopy( &dValue, abyHeader+44, 8 );
391 if( bBigEndian ) SwapWord( 8, abyHeader+44 );
392
393 dValue = psSHP->adBoundsMax[0];
394 ByteCopy( &dValue, abyHeader+52, 8 );
395 if( bBigEndian ) SwapWord( 8, abyHeader+52 );
396
397 dValue = psSHP->adBoundsMax[1];
398 ByteCopy( &dValue, abyHeader+60, 8 );
399 if( bBigEndian ) SwapWord( 8, abyHeader+60 );
400
401 dValue = psSHP->adBoundsMin[2]; /* z */
402 ByteCopy( &dValue, abyHeader+68, 8 );
403 if( bBigEndian ) SwapWord( 8, abyHeader+68 );
404
405 dValue = psSHP->adBoundsMax[2];
406 ByteCopy( &dValue, abyHeader+76, 8 );
407 if( bBigEndian ) SwapWord( 8, abyHeader+76 );
408
409 dValue = psSHP->adBoundsMin[3]; /* m */
410 ByteCopy( &dValue, abyHeader+84, 8 );
411 if( bBigEndian ) SwapWord( 8, abyHeader+84 );
412
413 dValue = psSHP->adBoundsMax[3];
414 ByteCopy( &dValue, abyHeader+92, 8 );
415 if( bBigEndian ) SwapWord( 8, abyHeader+92 );
416
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 )
422 {
423 psSHP->sHooks.Error( "Failure writing .shp header" );
424 return;
425 }
426
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 );
433
434 if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0
435 || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 )
436 {
437 psSHP->sHooks.Error( "Failure writing .shx header" );
438 return;
439 }
440
441 /* -------------------------------------------------------------------- */
442 /* Write out the .shx contents. */
443 /* -------------------------------------------------------------------- */
444 panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords);
445
446 for( i = 0; i < psSHP->nRecords; i++ )
447 {
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 );
452 }
453
454 if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX )
455 != psSHP->nRecords )
456 {
457 psSHP->sHooks.Error( "Failure writing .shx contents" );
458 }
459
460 free( panSHX );
461
462 /* -------------------------------------------------------------------- */
463 /* Flush to disk. */
464 /* -------------------------------------------------------------------- */
465 psSHP->sHooks.FFlush( psSHP->fpSHP );
466 psSHP->sHooks.FFlush( psSHP->fpSHX );
467 }
468
469 /************************************************************************/
470 /* SHPOpen() */
471 /************************************************************************/
472
473 SHPHandle SHPAPI_CALL
SHPOpen(const char * pszLayer,const char * pszAccess)474 SHPOpen( const char * pszLayer, const char * pszAccess )
475
476 {
477 SAHooks sHooks;
478
479 SASetupDefaultHooks( &sHooks );
480
481 return SHPOpenLL( pszLayer, pszAccess, &sHooks );
482 }
483
484 /************************************************************************/
485 /* SHPOpen() */
486 /* */
487 /* Open the .shp and .shx files based on the basename of the */
488 /* files or either file name. */
489 /************************************************************************/
490
491 SHPHandle SHPAPI_CALL
SHPOpenLL(const char * pszLayer,const char * pszAccess,SAHooks * psHooks)492 SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks )
493
494 {
495 char *pszFullname, *pszBasename;
496 SHPHandle psSHP;
497
498 uchar *pabyBuf;
499 int i;
500 double dValue;
501
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 )
509 pszAccess = "r+b";
510 else
511 pszAccess = "rb";
512
513 /* -------------------------------------------------------------------- */
514 /* Establish the byte order on this machine. */
515 /* -------------------------------------------------------------------- */
516 i = 1;
517 if( *((uchar *) &i) == 1 )
518 bBigEndian = FALSE;
519 else
520 bBigEndian = TRUE;
521
522 /* -------------------------------------------------------------------- */
523 /* Initialize the info structure. */
524 /* -------------------------------------------------------------------- */
525 psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1);
526
527 psSHP->bUpdated = FALSE;
528 memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) );
529
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] != '\\';
539 i-- ) {}
540
541 if( pszBasename[i] == '.' )
542 pszBasename[i] = '\0';
543
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 )
552 {
553 sprintf( pszFullname, "%s.SHP", pszBasename );
554 psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess );
555 }
556
557 if( psSHP->fpSHP == NULL )
558 {
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 );
563 free( pszMessage );
564
565 free( psSHP );
566 free( pszBasename );
567 free( pszFullname );
568
569 return NULL;
570 }
571
572 sprintf( pszFullname, "%s.shx", pszBasename );
573 psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess );
574 if( psSHP->fpSHX == NULL )
575 {
576 sprintf( pszFullname, "%s.SHX", pszBasename );
577 psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess );
578 }
579
580 if( psSHP->fpSHX == NULL )
581 {
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 );
586 free( pszMessage );
587
588 psSHP->sHooks.FClose( psSHP->fpSHP );
589 free( psSHP );
590 free( pszBasename );
591 free( pszFullname );
592 return( NULL );
593 }
594
595 free( pszFullname );
596 free( pszBasename );
597
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 );
603
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;
608
609 /* -------------------------------------------------------------------- */
610 /* Read SHX file Header info */
611 /* -------------------------------------------------------------------- */
612 if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1
613 || pabyBuf[0] != 0
614 || pabyBuf[1] != 0
615 || pabyBuf[2] != 0x27
616 || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) )
617 {
618 psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." );
619 psSHP->sHooks.FClose( psSHP->fpSHP );
620 psSHP->sHooks.FClose( psSHP->fpSHX );
621 free( psSHP );
622
623 return( NULL );
624 }
625
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;
629
630 psSHP->nShapeType = pabyBuf[32];
631
632 if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 )
633 {
634 char szError[200];
635
636 sprintf( szError,
637 "Record count in .shp header is %d, which seems\n"
638 "unreasonable. Assuming header is corrupt.",
639 psSHP->nRecords );
640 psSHP->sHooks.Error( szError );
641 psSHP->sHooks.FClose( psSHP->fpSHP );
642 psSHP->sHooks.FClose( psSHP->fpSHX );
643 free( psSHP );
644 free(pabyBuf);
645
646 return( NULL );
647 }
648
649 /* -------------------------------------------------------------------- */
650 /* Read the bounds. */
651 /* -------------------------------------------------------------------- */
652 if( bBigEndian ) SwapWord( 8, pabyBuf+36 );
653 memcpy( &dValue, pabyBuf+36, 8 );
654 psSHP->adBoundsMin[0] = dValue;
655
656 if( bBigEndian ) SwapWord( 8, pabyBuf+44 );
657 memcpy( &dValue, pabyBuf+44, 8 );
658 psSHP->adBoundsMin[1] = dValue;
659
660 if( bBigEndian ) SwapWord( 8, pabyBuf+52 );
661 memcpy( &dValue, pabyBuf+52, 8 );
662 psSHP->adBoundsMax[0] = dValue;
663
664 if( bBigEndian ) SwapWord( 8, pabyBuf+60 );
665 memcpy( &dValue, pabyBuf+60, 8 );
666 psSHP->adBoundsMax[1] = dValue;
667
668 if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */
669 memcpy( &dValue, pabyBuf+68, 8 );
670 psSHP->adBoundsMin[2] = dValue;
671
672 if( bBigEndian ) SwapWord( 8, pabyBuf+76 );
673 memcpy( &dValue, pabyBuf+76, 8 );
674 psSHP->adBoundsMax[2] = dValue;
675
676 if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */
677 memcpy( &dValue, pabyBuf+84, 8 );
678 psSHP->adBoundsMin[3] = dValue;
679
680 if( bBigEndian ) SwapWord( 8, pabyBuf+92 );
681 memcpy( &dValue, pabyBuf+92, 8 );
682 psSHP->adBoundsMax[3] = dValue;
683
684 free( pabyBuf );
685
686 /* -------------------------------------------------------------------- */
687 /* Read the .shx file to get the offsets to each record in */
688 /* the .shp file. */
689 /* -------------------------------------------------------------------- */
690 psSHP->nMaxRecords = psSHP->nRecords;
691
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) );
697
698 if (psSHP->panRecOffset == NULL ||
699 psSHP->panRecSize == NULL ||
700 pabyBuf == NULL)
701 {
702 char szError[200];
703
704 sprintf(szError,
705 "Not enough memory to allocate requested memory (nRecords=%d).\n"
706 "Probably broken SHP file",
707 psSHP->nRecords );
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 );
714 free( psSHP );
715 return( NULL );
716 }
717
718 if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX )
719 != psSHP->nRecords )
720 {
721 char szError[200];
722
723 sprintf( szError,
724 "Failed to read all values for %d records in .shx file.",
725 psSHP->nRecords );
726 psSHP->sHooks.Error( szError );
727
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 );
733 free( pabyBuf );
734 free( psSHP );
735
736 return( NULL );
737 }
738
739 /* In read-only mode, we can close the SHX now */
740 if (strcmp(pszAccess, "rb") == 0)
741 {
742 psSHP->sHooks.FClose( psSHP->fpSHX );
743 psSHP->fpSHX = NULL;
744 }
745
746 for( i = 0; i < psSHP->nRecords; i++ )
747 {
748 int32 nOffset, nLength;
749
750 memcpy( &nOffset, pabyBuf + i * 8, 4 );
751 if( !bBigEndian ) SwapWord( 4, &nOffset );
752
753 memcpy( &nLength, pabyBuf + i * 8 + 4, 4 );
754 if( !bBigEndian ) SwapWord( 4, &nLength );
755
756 psSHP->panRecOffset[i] = nOffset*2;
757 psSHP->panRecSize[i] = nLength*2;
758 }
759 free( pabyBuf );
760
761 return( psSHP );
762 }
763
764 /************************************************************************/
765 /* SHPClose() */
766 /* */
767 /* Close the .shp and .shx files. */
768 /************************************************************************/
769
770 void SHPAPI_CALL
SHPClose(SHPHandle psSHP)771 SHPClose(SHPHandle psSHP )
772
773 {
774 if( psSHP == NULL )
775 return;
776
777 /* -------------------------------------------------------------------- */
778 /* Update the header if we have modified anything. */
779 /* -------------------------------------------------------------------- */
780 if( psSHP->bUpdated )
781 SHPWriteHeader( psSHP );
782
783 /* -------------------------------------------------------------------- */
784 /* Free all resources, and close files. */
785 /* -------------------------------------------------------------------- */
786 free( psSHP->panRecOffset );
787 free( psSHP->panRecSize );
788
789 if ( psSHP->fpSHX != NULL)
790 psSHP->sHooks.FClose( psSHP->fpSHX );
791 psSHP->sHooks.FClose( psSHP->fpSHP );
792
793 if( psSHP->pabyRec != NULL )
794 {
795 free( psSHP->pabyRec );
796 }
797
798 free( psSHP );
799 }
800
801 /************************************************************************/
802 /* SHPGetInfo() */
803 /* */
804 /* Fetch general information about the shape file. */
805 /************************************************************************/
806
807 void SHPAPI_CALL
SHPGetInfo(SHPHandle psSHP,int * pnEntities,int * pnShapeType,double * padfMinBound,double * padfMaxBound)808 SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType,
809 double * padfMinBound, double * padfMaxBound )
810
811 {
812 int i;
813
814 if( psSHP == NULL )
815 return;
816
817 if( pnEntities != NULL )
818 *pnEntities = psSHP->nRecords;
819
820 if( pnShapeType != NULL )
821 *pnShapeType = psSHP->nShapeType;
822
823 for( i = 0; i < 4; i++ )
824 {
825 if( padfMinBound != NULL )
826 padfMinBound[i] = psSHP->adBoundsMin[i];
827 if( padfMaxBound != NULL )
828 padfMaxBound[i] = psSHP->adBoundsMax[i];
829 }
830 }
831
832 /************************************************************************/
833 /* SHPCreate() */
834 /* */
835 /* Create a new shape file and return a handle to the open */
836 /* shape file with read/write access. */
837 /************************************************************************/
838
839 SHPHandle SHPAPI_CALL
SHPCreate(const char * pszLayer,int nShapeType)840 SHPCreate( const char * pszLayer, int nShapeType )
841
842 {
843 SAHooks sHooks;
844
845 SASetupDefaultHooks( &sHooks );
846
847 return SHPCreateLL( pszLayer, nShapeType, &sHooks );
848 }
849
850 /************************************************************************/
851 /* SHPCreate() */
852 /* */
853 /* Create a new shape file and return a handle to the open */
854 /* shape file with read/write access. */
855 /************************************************************************/
856
857 SHPHandle SHPAPI_CALL
SHPCreateLL(const char * pszLayer,int nShapeType,SAHooks * psHooks)858 SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks )
859
860 {
861 char *pszBasename = NULL, *pszFullname = NULL;
862 int i;
863 SAFile fpSHP = NULL, fpSHX = NULL;
864 uchar abyHeader[100];
865 int32 i32;
866 double dValue;
867
868 /* -------------------------------------------------------------------- */
869 /* Establish the byte order on this system. */
870 /* -------------------------------------------------------------------- */
871 i = 1;
872 if( *((uchar *) &i) == 1 )
873 bBigEndian = FALSE;
874 else
875 bBigEndian = TRUE;
876
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] != '\\';
886 i-- ) {}
887
888 if( pszBasename[i] == '.' )
889 pszBasename[i] = '\0';
890
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" );
897 if( fpSHP == NULL )
898 {
899 psHooks->Error( "Failed to create file .shp file." );
900 goto error;
901 }
902
903 sprintf( pszFullname, "%s.shx", pszBasename );
904 fpSHX = psHooks->FOpen(pszFullname, "wb" );
905 if( fpSHX == NULL )
906 {
907 psHooks->Error( "Failed to create file .shx file." );
908 goto error;
909 }
910
911 free( pszFullname ); pszFullname = NULL;
912 free( pszBasename ); pszBasename = NULL;
913
914 /* -------------------------------------------------------------------- */
915 /* Prepare header block for .shp file. */
916 /* -------------------------------------------------------------------- */
917 for( i = 0; i < 100; i++ )
918 abyHeader[i] = 0;
919
920 abyHeader[2] = 0x27; /* magic cookie */
921 abyHeader[3] = 0x0a;
922
923 i32 = 50; /* file size */
924 ByteCopy( &i32, abyHeader+24, 4 );
925 if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
926
927 i32 = 1000; /* version */
928 ByteCopy( &i32, abyHeader+28, 4 );
929 if( bBigEndian ) SwapWord( 4, abyHeader+28 );
930
931 i32 = nShapeType; /* shape type */
932 ByteCopy( &i32, abyHeader+32, 4 );
933 if( bBigEndian ) SwapWord( 4, abyHeader+32 );
934
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 );
940
941 /* -------------------------------------------------------------------- */
942 /* Write .shp file header. */
943 /* -------------------------------------------------------------------- */
944 if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 )
945 {
946 psHooks->Error( "Failed to write .shp header." );
947 goto error;
948 }
949
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 );
956
957 if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 )
958 {
959 psHooks->Error( "Failed to write .shx header." );
960 goto error;
961 }
962
963 /* -------------------------------------------------------------------- */
964 /* Close the files, and then open them as regular existing files. */
965 /* -------------------------------------------------------------------- */
966 psHooks->FClose( fpSHP );
967 psHooks->FClose( fpSHX );
968
969 return( SHPOpenLL( pszLayer, "r+b", psHooks ) );
970
971 error:
972 if (pszFullname) free(pszFullname);
973 if (pszBasename) free(pszBasename);
974 if (fpSHP) psHooks->FClose( fpSHP );
975 if (fpSHX) psHooks->FClose( fpSHX );
976 return NULL;
977 }
978
979 /************************************************************************/
980 /* _SHPSetBounds() */
981 /* */
982 /* Compute a bounds rectangle for a shape, and set it into the */
983 /* indicated location in the record. */
984 /************************************************************************/
985
_SHPSetBounds(uchar * pabyRec,SHPObject * psShape)986 static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape )
987
988 {
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 );
993
994 if( bBigEndian )
995 {
996 SwapWord( 8, pabyRec + 0 );
997 SwapWord( 8, pabyRec + 8 );
998 SwapWord( 8, pabyRec + 16 );
999 SwapWord( 8, pabyRec + 24 );
1000 }
1001 }
1002
1003 /************************************************************************/
1004 /* SHPComputeExtents() */
1005 /* */
1006 /* Recompute the extents of a shape. Automatically done by */
1007 /* SHPCreateObject(). */
1008 /************************************************************************/
1009
1010 void SHPAPI_CALL
SHPComputeExtents(SHPObject * psObject)1011 SHPComputeExtents( SHPObject * psObject )
1012
1013 {
1014 int i;
1015
1016 /* -------------------------------------------------------------------- */
1017 /* Build extents for this object. */
1018 /* -------------------------------------------------------------------- */
1019 if( psObject->nVertices > 0 )
1020 {
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];
1025 }
1026
1027 for( i = 0; i < psObject->nVertices; i++ )
1028 {
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]);
1033
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]);
1038 }
1039 }
1040
1041 /************************************************************************/
1042 /* SHPCreateObject() */
1043 /* */
1044 /* Create a shape object. It should be freed with */
1045 /* SHPDestroyObject(). */
1046 /************************************************************************/
1047
1048 SHPObject SHPAPI_CALL1(*)
SHPCreateObject(int nSHPType,int nShapeId,int nParts,const int * panPartStart,const int * panPartType,int nVertices,const double * padfX,const double * padfY,const double * padfZ,const double * padfM)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 )
1053
1054 {
1055 SHPObject *psObject;
1056 int i, bHasM, bHasZ;
1057
1058 psObject = (SHPObject *) calloc(1,sizeof(SHPObject));
1059 psObject->nSHPType = nSHPType;
1060 psObject->nShapeId = nShapeId;
1061 psObject->bMeasureIsUsed = FALSE;
1062
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 )
1070 {
1071 bHasM = TRUE;
1072 bHasZ = FALSE;
1073 }
1074 else if( nSHPType == SHPT_ARCZ
1075 || nSHPType == SHPT_POINTZ
1076 || nSHPType == SHPT_POLYGONZ
1077 || nSHPType == SHPT_MULTIPOINTZ
1078 || nSHPType == SHPT_MULTIPATCH )
1079 {
1080 bHasM = TRUE;
1081 bHasZ = TRUE;
1082 }
1083 else
1084 {
1085 bHasM = FALSE;
1086 bHasZ = FALSE;
1087 }
1088
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 )
1097 {
1098 psObject->nParts = MAX(1,nParts);
1099
1100 psObject->panPartStart = (int *)
1101 calloc(sizeof(int), psObject->nParts);
1102 psObject->panPartType = (int *)
1103 malloc(sizeof(int) * psObject->nParts);
1104
1105 psObject->panPartStart[0] = 0;
1106 psObject->panPartType[0] = SHPP_RING;
1107
1108 for( i = 0; i < nParts; i++ )
1109 {
1110 if( psObject->panPartStart != NULL )
1111 psObject->panPartStart[i] = panPartStart[i];
1112
1113 if( panPartType != NULL )
1114 psObject->panPartType[i] = panPartType[i];
1115 else
1116 psObject->panPartType[i] = SHPP_RING;
1117 }
1118
1119 if( psObject->panPartStart[0] != 0 )
1120 psObject->panPartStart[0] = 0;
1121 }
1122
1123 /* -------------------------------------------------------------------- */
1124 /* Capture vertices. Note that X, Y, Z and M are optional. */
1125 /* -------------------------------------------------------------------- */
1126 if( nVertices > 0 )
1127 {
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);
1132
1133 for( i = 0; i < nVertices; i++ )
1134 {
1135 if( padfX != NULL )
1136 psObject->padfX[i] = padfX[i];
1137 if( padfY != NULL )
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];
1143 }
1144 if( padfM != NULL && bHasM )
1145 psObject->bMeasureIsUsed = TRUE;
1146 }
1147
1148 /* -------------------------------------------------------------------- */
1149 /* Compute the extents. */
1150 /* -------------------------------------------------------------------- */
1151 psObject->nVertices = nVertices;
1152 SHPComputeExtents( psObject );
1153
1154 return( psObject );
1155 }
1156
1157 /************************************************************************/
1158 /* SHPCreateSimpleObject() */
1159 /* */
1160 /* Create a simple (common) shape object. Destroy with */
1161 /* SHPDestroyObject(). */
1162 /************************************************************************/
1163
1164 SHPObject SHPAPI_CALL1(*)
SHPCreateSimpleObject(int nSHPType,int nVertices,const double * padfX,const double * padfY,const double * padfZ)1165 SHPCreateSimpleObject( int nSHPType, int nVertices,
1166 const double * padfX, const double * padfY,
1167 const double * padfZ )
1168
1169 {
1170 return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL,
1171 nVertices, padfX, padfY, padfZ, NULL ) );
1172 }
1173
1174 /************************************************************************/
1175 /* SHPWriteObject() */
1176 /* */
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 /************************************************************************/
1180
1181 int SHPAPI_CALL
SHPWriteObject(SHPHandle psSHP,int nShapeId,SHPObject * psObject)1182 SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject )
1183
1184 {
1185 unsigned int nRecordOffset, nRecordSize=0;
1186 int i;
1187 uchar *pabyRec;
1188 int32 i32;
1189
1190 psSHP->bUpdated = TRUE;
1191
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 );
1198
1199 /* -------------------------------------------------------------------- */
1200 /* Ensure that -1 is used for appends. Either blow an */
1201 /* assertion, or if they are disabled, set the shapeid to -1 */
1202 /* for appends. */
1203 /* -------------------------------------------------------------------- */
1204 assert( nShapeId == -1
1205 || (nShapeId >= 0 && nShapeId < psSHP->nRecords) );
1206
1207 if( nShapeId != -1 && nShapeId >= psSHP->nRecords )
1208 nShapeId = -1;
1209
1210 /* -------------------------------------------------------------------- */
1211 /* Add the new entity to the in memory index. */
1212 /* -------------------------------------------------------------------- */
1213 if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords )
1214 {
1215 psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100);
1216
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 );
1221 }
1222
1223 /* -------------------------------------------------------------------- */
1224 /* Initialize record. */
1225 /* -------------------------------------------------------------------- */
1226 pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double)
1227 + psObject->nParts * 8 + 128);
1228
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 )
1239 {
1240 int32 nPoints, nParts;
1241 int i;
1242
1243 nPoints = psObject->nVertices;
1244 nParts = psObject->nParts;
1245
1246 _SHPSetBounds( pabyRec + 12, psObject );
1247
1248 if( bBigEndian ) SwapWord( 4, &nPoints );
1249 if( bBigEndian ) SwapWord( 4, &nParts );
1250
1251 ByteCopy( &nPoints, pabyRec + 40 + 8, 4 );
1252 ByteCopy( &nParts, pabyRec + 36 + 8, 4 );
1253
1254 nRecordSize = 52;
1255
1256 /*
1257 * Write part start positions.
1258 */
1259 ByteCopy( psObject->panPartStart, pabyRec + 44 + 8,
1260 4 * psObject->nParts );
1261 for( i = 0; i < psObject->nParts; i++ )
1262 {
1263 if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i );
1264 nRecordSize += 4;
1265 }
1266
1267 /*
1268 * Write multipatch part types if needed.
1269 */
1270 if( psObject->nSHPType == SHPT_MULTIPATCH )
1271 {
1272 memcpy( pabyRec + nRecordSize, psObject->panPartType,
1273 4*psObject->nParts );
1274 for( i = 0; i < psObject->nParts; i++ )
1275 {
1276 if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize );
1277 nRecordSize += 4;
1278 }
1279 }
1280
1281 /*
1282 * Write the (x,y) vertex values.
1283 */
1284 for( i = 0; i < psObject->nVertices; i++ )
1285 {
1286 ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 );
1287 ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 );
1288
1289 if( bBigEndian )
1290 SwapWord( 8, pabyRec + nRecordSize );
1291
1292 if( bBigEndian )
1293 SwapWord( 8, pabyRec + nRecordSize + 8 );
1294
1295 nRecordSize += 2 * 8;
1296 }
1297
1298 /*
1299 * Write the Z coordinates (if any).
1300 */
1301 if( psObject->nSHPType == SHPT_POLYGONZ
1302 || psObject->nSHPType == SHPT_ARCZ
1303 || psObject->nSHPType == SHPT_MULTIPATCH )
1304 {
1305 ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
1306 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1307 nRecordSize += 8;
1308
1309 ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
1310 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1311 nRecordSize += 8;
1312
1313 for( i = 0; i < psObject->nVertices; i++ )
1314 {
1315 ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
1316 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1317 nRecordSize += 8;
1318 }
1319 }
1320
1321 /*
1322 * Write the M values, if any.
1323 */
1324 if( psObject->bMeasureIsUsed
1325 && (psObject->nSHPType == SHPT_POLYGONM
1326 || psObject->nSHPType == SHPT_ARCM
1327 #ifndef DISABLE_MULTIPATCH_MEASURE
1328 || psObject->nSHPType == SHPT_MULTIPATCH
1329 #endif
1330 || psObject->nSHPType == SHPT_POLYGONZ
1331 || psObject->nSHPType == SHPT_ARCZ) )
1332 {
1333 ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
1334 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1335 nRecordSize += 8;
1336
1337 ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
1338 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1339 nRecordSize += 8;
1340
1341 for( i = 0; i < psObject->nVertices; i++ )
1342 {
1343 ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
1344 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1345 nRecordSize += 8;
1346 }
1347 }
1348 }
1349
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 )
1356 {
1357 int32 nPoints;
1358 int i;
1359
1360 nPoints = psObject->nVertices;
1361
1362 _SHPSetBounds( pabyRec + 12, psObject );
1363
1364 if( bBigEndian ) SwapWord( 4, &nPoints );
1365 ByteCopy( &nPoints, pabyRec + 44, 4 );
1366
1367 for( i = 0; i < psObject->nVertices; i++ )
1368 {
1369 ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 );
1370 ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 );
1371
1372 if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 );
1373 if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 );
1374 }
1375
1376 nRecordSize = 48 + 16 * psObject->nVertices;
1377
1378 if( psObject->nSHPType == SHPT_MULTIPOINTZ )
1379 {
1380 ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
1381 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1382 nRecordSize += 8;
1383
1384 ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
1385 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1386 nRecordSize += 8;
1387
1388 for( i = 0; i < psObject->nVertices; i++ )
1389 {
1390 ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
1391 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1392 nRecordSize += 8;
1393 }
1394 }
1395
1396 if( psObject->bMeasureIsUsed
1397 && (psObject->nSHPType == SHPT_MULTIPOINTZ
1398 || psObject->nSHPType == SHPT_MULTIPOINTM) )
1399 {
1400 ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
1401 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1402 nRecordSize += 8;
1403
1404 ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
1405 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1406 nRecordSize += 8;
1407
1408 for( i = 0; i < psObject->nVertices; i++ )
1409 {
1410 ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
1411 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1412 nRecordSize += 8;
1413 }
1414 }
1415 }
1416
1417 /* -------------------------------------------------------------------- */
1418 /* Write point. */
1419 /* -------------------------------------------------------------------- */
1420 else if( psObject->nSHPType == SHPT_POINT
1421 || psObject->nSHPType == SHPT_POINTZ
1422 || psObject->nSHPType == SHPT_POINTM )
1423 {
1424 ByteCopy( psObject->padfX, pabyRec + 12, 8 );
1425 ByteCopy( psObject->padfY, pabyRec + 20, 8 );
1426
1427 if( bBigEndian ) SwapWord( 8, pabyRec + 12 );
1428 if( bBigEndian ) SwapWord( 8, pabyRec + 20 );
1429
1430 nRecordSize = 28;
1431
1432 if( psObject->nSHPType == SHPT_POINTZ )
1433 {
1434 ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 );
1435 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1436 nRecordSize += 8;
1437 }
1438
1439 if( psObject->bMeasureIsUsed
1440 && (psObject->nSHPType == SHPT_POINTZ
1441 || psObject->nSHPType == SHPT_POINTM) )
1442 {
1443 ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 );
1444 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1445 nRecordSize += 8;
1446 }
1447 }
1448
1449 /* -------------------------------------------------------------------- */
1450 /* Not much to do for null geometries. */
1451 /* -------------------------------------------------------------------- */
1452 else if( psObject->nSHPType == SHPT_NULL )
1453 {
1454 nRecordSize = 12;
1455 }
1456
1457 else
1458 {
1459 /* unknown type */
1460 assert( FALSE );
1461 }
1462
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 )
1469 {
1470 unsigned int nExpectedSize = psSHP->nFileSize + nRecordSize;
1471 if( nExpectedSize < psSHP->nFileSize ) // due to unsigned int overflow
1472 {
1473 char str[128];
1474 sprintf( str, "Failed to write shape object. "
1475 "File size cannot reach %u + %u.",
1476 psSHP->nFileSize, nRecordSize );
1477 psSHP->sHooks.Error( str );
1478 free( pabyRec );
1479 return -1;
1480 }
1481
1482 if( nShapeId == -1 )
1483 nShapeId = psSHP->nRecords++;
1484
1485 psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize;
1486 psSHP->panRecSize[nShapeId] = nRecordSize-8;
1487 psSHP->nFileSize += nRecordSize;
1488 }
1489 else
1490 {
1491 nRecordOffset = psSHP->panRecOffset[nShapeId];
1492 psSHP->panRecSize[nShapeId] = nRecordSize-8;
1493 }
1494
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 );
1501
1502 i32 = (nRecordSize-8)/2; /* record size */
1503 if( !bBigEndian ) SwapWord( 4, &i32 );
1504 ByteCopy( &i32, pabyRec + 4, 4 );
1505
1506 i32 = psObject->nSHPType; /* shape type */
1507 if( bBigEndian ) SwapWord( 4, &i32 );
1508 ByteCopy( &i32, pabyRec + 8, 4 );
1509
1510 /* -------------------------------------------------------------------- */
1511 /* Write out record. */
1512 /* -------------------------------------------------------------------- */
1513 if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 )
1514 {
1515 psSHP->sHooks.Error( "Error in psSHP->sHooks.FSeek() while writing object to .shp file." );
1516 free( pabyRec );
1517 return -1;
1518 }
1519 if( psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 )
1520 {
1521 psSHP->sHooks.Error( "Error in psSHP->sHooks.Fwrite() while writing object to .shp file." );
1522 free( pabyRec );
1523 return -1;
1524 }
1525
1526 free( pabyRec );
1527
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 )
1535 {
1536 if( psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0 )
1537 {
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;
1542 }
1543 else
1544 {
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];
1549 }
1550 }
1551
1552 for( i = 0; i < psObject->nVertices; i++ )
1553 {
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]);
1562 }
1563
1564 return( nShapeId );
1565 }
1566
1567 /************************************************************************/
1568 /* SHPReadObject() */
1569 /* */
1570 /* Read the vertices, parts, and other non-attribute information */
1571 /* for one shape. */
1572 /************************************************************************/
1573
1574 SHPObject SHPAPI_CALL1(*)
SHPReadObject(SHPHandle psSHP,int hEntity)1575 SHPReadObject( SHPHandle psSHP, int hEntity )
1576
1577 {
1578 int nEntitySize, nRequiredSize;
1579 SHPObject *psShape;
1580 char szErrorMsg[128];
1581
1582 /* -------------------------------------------------------------------- */
1583 /* Validate the record/entity number. */
1584 /* -------------------------------------------------------------------- */
1585 if( hEntity < 0 || hEntity >= psSHP->nRecords )
1586 return( NULL );
1587
1588 /* -------------------------------------------------------------------- */
1589 /* Ensure our record buffer is large enough. */
1590 /* -------------------------------------------------------------------- */
1591 nEntitySize = psSHP->panRecSize[hEntity]+8;
1592 if( nEntitySize > psSHP->nBufSize )
1593 {
1594 psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize);
1595 if (psSHP->pabyRec == NULL)
1596 {
1597 char szError[200];
1598
1599 /* Reallocate previous successful size for following features */
1600 psSHP->pabyRec = (uchar *) malloc(psSHP->nBufSize);
1601
1602 sprintf( szError,
1603 "Not enough memory to allocate requested memory (nBufSize=%d). "
1604 "Probably broken SHP file", psSHP->nBufSize );
1605 psSHP->sHooks.Error( szError );
1606 return NULL;
1607 }
1608
1609 /* Only set new buffer size after successful alloc */
1610 psSHP->nBufSize = nEntitySize;
1611 }
1612
1613 /* In case we were not able to reallocate the buffer on a previous step */
1614 if (psSHP->pabyRec == NULL)
1615 {
1616 return NULL;
1617 }
1618
1619 /* -------------------------------------------------------------------- */
1620 /* Read the record. */
1621 /* -------------------------------------------------------------------- */
1622 if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0 )
1623 {
1624 /*
1625 * TODO - mloskot: Consider detailed diagnostics of shape file,
1626 * for example to detect if file is truncated.
1627 */
1628 char str[128];
1629 sprintf( str,
1630 "Error in fseek() reading object from .shp file at offset %u",
1631 psSHP->panRecOffset[hEntity]);
1632
1633 psSHP->sHooks.Error( str );
1634 return NULL;
1635 }
1636
1637 if( psSHP->sHooks.FRead( psSHP->pabyRec, nEntitySize, 1, psSHP->fpSHP ) != 1 )
1638 {
1639 /*
1640 * TODO - mloskot: Consider detailed diagnostics of shape file,
1641 * for example to detect if file is truncated.
1642 */
1643 char str[128];
1644 sprintf( str,
1645 "Error in fread() reading object of size %u at offset %u from .shp file",
1646 nEntitySize, psSHP->panRecOffset[hEntity] );
1647
1648 psSHP->sHooks.Error( str );
1649 return NULL;
1650 }
1651
1652 /* -------------------------------------------------------------------- */
1653 /* Allocate and minimally initialize the object. */
1654 /* -------------------------------------------------------------------- */
1655 psShape = (SHPObject *) calloc(1,sizeof(SHPObject));
1656 psShape->nShapeId = hEntity;
1657 psShape->bMeasureIsUsed = FALSE;
1658
1659 if ( 8 + 4 > nEntitySize )
1660 {
1661 snprintf(szErrorMsg, sizeof(szErrorMsg),
1662 "Corrupted .shp file : shape %d : nEntitySize = %d",
1663 hEntity, nEntitySize);
1664 psSHP->sHooks.Error( szErrorMsg );
1665 SHPDestroyObject(psShape);
1666 return NULL;
1667 }
1668 memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 );
1669
1670 if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) );
1671
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 )
1681 {
1682 int32 nPoints, nParts;
1683 int i, nOffset;
1684
1685 if ( 40 + 8 + 4 > nEntitySize )
1686 {
1687 snprintf(szErrorMsg, sizeof(szErrorMsg),
1688 "Corrupted .shp file : shape %d : nEntitySize = %d",
1689 hEntity, nEntitySize);
1690 psSHP->sHooks.Error( szErrorMsg );
1691 SHPDestroyObject(psShape);
1692 return NULL;
1693 }
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 );
1701
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) );
1706
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 );
1713
1714 if( bBigEndian ) SwapWord( 4, &nPoints );
1715 if( bBigEndian ) SwapWord( 4, &nParts );
1716
1717 if (nPoints <= 0 || nParts <= 0 ||
1718 nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000)
1719 {
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);
1725 return NULL;
1726 }
1727
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 )
1735 {
1736 nRequiredSize += 16 + 8 * nPoints;
1737 }
1738 if( psShape->nSHPType == SHPT_MULTIPATCH )
1739 {
1740 nRequiredSize += 4 * nParts;
1741 }
1742 if (nRequiredSize > nEntitySize)
1743 {
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);
1749 return NULL;
1750 }
1751
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));
1757
1758 psShape->nParts = nParts;
1759 psShape->panPartStart = (int *) calloc(nParts,sizeof(int));
1760 psShape->panPartType = (int *) calloc(nParts,sizeof(int));
1761
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)
1768 {
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);
1774 return NULL;
1775 }
1776
1777 for( i = 0; i <(int) nParts; i++ )
1778 psShape->panPartType[i] = SHPP_RING;
1779
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++ )
1785 {
1786 if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i );
1787
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) )
1792 {
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);
1798 return NULL;
1799 }
1800 if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1])
1801 {
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);
1807 return NULL;
1808 }
1809 }
1810
1811 nOffset = 44 + 8 + 4*nParts;
1812
1813 /* -------------------------------------------------------------------- */
1814 /* If this is a multipatch, we will also have parts types. */
1815 /* -------------------------------------------------------------------- */
1816 if( psShape->nSHPType == SHPT_MULTIPATCH )
1817 {
1818 memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts );
1819 for( i = 0; i <(int) nParts; i++ )
1820 {
1821 if( bBigEndian ) SwapWord( 4, psShape->panPartType+i );
1822 }
1823
1824 nOffset += 4*nParts;
1825 }
1826
1827 /* -------------------------------------------------------------------- */
1828 /* Copy out the vertices from the record. */
1829 /* -------------------------------------------------------------------- */
1830 for( i = 0; i <(int) nPoints; i++ )
1831 {
1832 memcpy(psShape->padfX + i,
1833 psSHP->pabyRec + nOffset + i * 16,
1834 8 );
1835
1836 memcpy(psShape->padfY + i,
1837 psSHP->pabyRec + nOffset + i * 16 + 8,
1838 8 );
1839
1840 if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
1841 if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
1842 }
1843
1844 nOffset += 16*nPoints;
1845
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 )
1852 {
1853 memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
1854 memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
1855
1856 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
1857 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
1858
1859 for( i = 0; i <(int) nPoints; i++ )
1860 {
1861 memcpy( psShape->padfZ + i,
1862 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1863 if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
1864 }
1865
1866 nOffset += 16 + 8*nPoints;
1867 }
1868
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) )
1876 {
1877 memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
1878 memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
1879
1880 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
1881 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
1882
1883 for( i = 0; i <(int) nPoints; i++ )
1884 {
1885 memcpy( psShape->padfM + i,
1886 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1887 if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
1888 }
1889 psShape->bMeasureIsUsed = TRUE;
1890 }
1891 }
1892
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 )
1899 {
1900 int32 nPoints;
1901 int i, nOffset;
1902
1903 if ( 44 + 4 > nEntitySize )
1904 {
1905 snprintf(szErrorMsg, sizeof(szErrorMsg),
1906 "Corrupted .shp file : shape %d : nEntitySize = %d",
1907 hEntity, nEntitySize);
1908 psSHP->sHooks.Error( szErrorMsg );
1909 SHPDestroyObject(psShape);
1910 return NULL;
1911 }
1912 memcpy( &nPoints, psSHP->pabyRec + 44, 4 );
1913
1914 if( bBigEndian ) SwapWord( 4, &nPoints );
1915
1916 if (nPoints <= 0 || nPoints > 50 * 1000 * 1000)
1917 {
1918 snprintf(szErrorMsg, sizeof(szErrorMsg),
1919 "Corrupted .shp file : shape %d : nPoints = %d",
1920 hEntity, nPoints);
1921 psSHP->sHooks.Error( szErrorMsg );
1922 SHPDestroyObject(psShape);
1923 return NULL;
1924 }
1925
1926 nRequiredSize = 48 + nPoints * 16;
1927 if( psShape->nSHPType == SHPT_MULTIPOINTZ )
1928 {
1929 nRequiredSize += 16 + nPoints * 8;
1930 }
1931 if (nRequiredSize > nEntitySize)
1932 {
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);
1938 return NULL;
1939 }
1940
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));
1946
1947 if (psShape->padfX == NULL ||
1948 psShape->padfY == NULL ||
1949 psShape->padfZ == NULL ||
1950 psShape->padfM == NULL)
1951 {
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);
1957 return NULL;
1958 }
1959
1960 for( i = 0; i <(int) nPoints; i++ )
1961 {
1962 memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 );
1963 memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 );
1964
1965 if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
1966 if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
1967 }
1968
1969 nOffset = 48 + 16*nPoints;
1970
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 );
1978
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) );
1983
1984 /* -------------------------------------------------------------------- */
1985 /* If we have a Z coordinate, collect that now. */
1986 /* -------------------------------------------------------------------- */
1987 if( psShape->nSHPType == SHPT_MULTIPOINTZ )
1988 {
1989 memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
1990 memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
1991
1992 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
1993 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
1994
1995 for( i = 0; i <(int) nPoints; i++ )
1996 {
1997 memcpy( psShape->padfZ + i,
1998 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1999 if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
2000 }
2001
2002 nOffset += 16 + 8*nPoints;
2003 }
2004
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) )
2012 {
2013 memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
2014 memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
2015
2016 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
2017 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
2018
2019 for( i = 0; i <(int) nPoints; i++ )
2020 {
2021 memcpy( psShape->padfM + i,
2022 psSHP->pabyRec + nOffset + 16 + i*8, 8 );
2023 if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
2024 }
2025 psShape->bMeasureIsUsed = TRUE;
2026 }
2027 }
2028
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 )
2035 {
2036 int nOffset;
2037
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));
2043
2044 if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize)
2045 {
2046 snprintf(szErrorMsg, sizeof(szErrorMsg),
2047 "Corrupted .shp file : shape %d : nEntitySize = %d",
2048 hEntity, nEntitySize);
2049 psSHP->sHooks.Error( szErrorMsg );
2050 SHPDestroyObject(psShape);
2051 return NULL;
2052 }
2053 memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 );
2054 memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 );
2055
2056 if( bBigEndian ) SwapWord( 8, psShape->padfX );
2057 if( bBigEndian ) SwapWord( 8, psShape->padfY );
2058
2059 nOffset = 20 + 8;
2060
2061 /* -------------------------------------------------------------------- */
2062 /* If we have a Z coordinate, collect that now. */
2063 /* -------------------------------------------------------------------- */
2064 if( psShape->nSHPType == SHPT_POINTZ )
2065 {
2066 memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 );
2067
2068 if( bBigEndian ) SwapWord( 8, psShape->padfZ );
2069
2070 nOffset += 8;
2071 }
2072
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 )
2080 {
2081 memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 );
2082
2083 if( bBigEndian ) SwapWord( 8, psShape->padfM );
2084 psShape->bMeasureIsUsed = TRUE;
2085 }
2086
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];
2095 }
2096
2097 return( psShape );
2098 }
2099
2100 /************************************************************************/
2101 /* SHPTypeName() */
2102 /************************************************************************/
2103
2104 const char SHPAPI_CALL1(*)
SHPTypeName(int nSHPType)2105 SHPTypeName( int nSHPType )
2106
2107 {
2108 switch( nSHPType )
2109 {
2110 case SHPT_NULL:
2111 return "NullShape";
2112
2113 case SHPT_POINT:
2114 return "Point";
2115
2116 case SHPT_ARC:
2117 return "Arc";
2118
2119 case SHPT_POLYGON:
2120 return "Polygon";
2121
2122 case SHPT_MULTIPOINT:
2123 return "MultiPoint";
2124
2125 case SHPT_POINTZ:
2126 return "PointZ";
2127
2128 case SHPT_ARCZ:
2129 return "ArcZ";
2130
2131 case SHPT_POLYGONZ:
2132 return "PolygonZ";
2133
2134 case SHPT_MULTIPOINTZ:
2135 return "MultiPointZ";
2136
2137 case SHPT_POINTM:
2138 return "PointM";
2139
2140 case SHPT_ARCM:
2141 return "ArcM";
2142
2143 case SHPT_POLYGONM:
2144 return "PolygonM";
2145
2146 case SHPT_MULTIPOINTM:
2147 return "MultiPointM";
2148
2149 case SHPT_MULTIPATCH:
2150 return "MultiPatch";
2151
2152 default:
2153 return "UnknownShapeType";
2154 }
2155 }
2156
2157 /************************************************************************/
2158 /* SHPPartTypeName() */
2159 /************************************************************************/
2160
2161 const char SHPAPI_CALL1(*)
SHPPartTypeName(int nPartType)2162 SHPPartTypeName( int nPartType )
2163
2164 {
2165 switch( nPartType )
2166 {
2167 case SHPP_TRISTRIP:
2168 return "TriangleStrip";
2169
2170 case SHPP_TRIFAN:
2171 return "TriangleFan";
2172
2173 case SHPP_OUTERRING:
2174 return "OuterRing";
2175
2176 case SHPP_INNERRING:
2177 return "InnerRing";
2178
2179 case SHPP_FIRSTRING:
2180 return "FirstRing";
2181
2182 case SHPP_RING:
2183 return "Ring";
2184
2185 default:
2186 return "UnknownPartType";
2187 }
2188 }
2189
2190 /************************************************************************/
2191 /* SHPDestroyObject() */
2192 /************************************************************************/
2193
2194 void SHPAPI_CALL
SHPDestroyObject(SHPObject * psShape)2195 SHPDestroyObject( SHPObject * psShape )
2196
2197 {
2198 if( psShape == NULL )
2199 return;
2200
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 );
2209
2210 if( psShape->panPartStart != NULL )
2211 free( psShape->panPartStart );
2212 if( psShape->panPartType != NULL )
2213 free( psShape->panPartType );
2214
2215 free( psShape );
2216 }
2217
2218 /************************************************************************/
2219 /* SHPRewindObject() */
2220 /* */
2221 /* Reset the winding of polygon objects to adhere to the */
2222 /* specification. */
2223 /************************************************************************/
2224
2225 int SHPAPI_CALL
SHPRewindObject(SHPHandle hSHP,SHPObject * psObject)2226 SHPRewindObject( SHPHandle hSHP, SHPObject * psObject )
2227
2228 {
2229 (void) hSHP;
2230 int iOpRing, bAltered = 0;
2231
2232 /* -------------------------------------------------------------------- */
2233 /* Do nothing if this is not a polygon object. */
2234 /* -------------------------------------------------------------------- */
2235 if( psObject->nSHPType != SHPT_POLYGON
2236 && psObject->nSHPType != SHPT_POLYGONZ
2237 && psObject->nSHPType != SHPT_POLYGONM )
2238 return 0;
2239
2240 if( psObject->nVertices == 0 || psObject->nParts == 0 )
2241 return 0;
2242
2243 /* -------------------------------------------------------------------- */
2244 /* Process each of the rings. */
2245 /* -------------------------------------------------------------------- */
2246 for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ )
2247 {
2248 int bInner, iVert, nVertCount, nVertStart, iCheckRing;
2249 double dfSum, dfTestX, dfTestY;
2250
2251 /* -------------------------------------------------------------------- */
2252 /* Determine if this ring is an inner ring or an outer ring */
2253 /* relative to all the other rings. For now we assume the */
2254 /* first ring is outer and all others are inner, but eventually */
2255 /* we need to fix this to handle multiple island polygons and */
2256 /* unordered sets of rings. */
2257 /* */
2258 /* -------------------------------------------------------------------- */
2259
2260 /* Use point in the middle of segment to avoid testing
2261 * common points of rings.
2262 */
2263 dfTestX = ( psObject->padfX[psObject->panPartStart[iOpRing]]
2264 + psObject->padfX[psObject->panPartStart[iOpRing] + 1] ) / 2;
2265 dfTestY = ( psObject->padfY[psObject->panPartStart[iOpRing]]
2266 + psObject->padfY[psObject->panPartStart[iOpRing] + 1] ) / 2;
2267
2268 bInner = FALSE;
2269 for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ )
2270 {
2271 int iEdge;
2272
2273 if( iCheckRing == iOpRing )
2274 continue;
2275
2276 nVertStart = psObject->panPartStart[iCheckRing];
2277
2278 if( iCheckRing == psObject->nParts-1 )
2279 nVertCount = psObject->nVertices
2280 - psObject->panPartStart[iCheckRing];
2281 else
2282 nVertCount = psObject->panPartStart[iCheckRing+1]
2283 - psObject->panPartStart[iCheckRing];
2284
2285 for( iEdge = 0; iEdge < nVertCount; iEdge++ )
2286 {
2287 int iNext;
2288
2289 if( iEdge < nVertCount-1 )
2290 iNext = iEdge+1;
2291 else
2292 iNext = 0;
2293
2294 /* Rule #1:
2295 * Test whether the edge 'straddles' the horizontal ray from the test point (dfTestY,dfTestY)
2296 * The rule #1 also excludes edges collinear with the ray.
2297 */
2298 if ( ( psObject->padfY[iEdge+nVertStart] < dfTestY
2299 && dfTestY <= psObject->padfY[iNext+nVertStart] )
2300 || ( psObject->padfY[iNext+nVertStart] < dfTestY
2301 && dfTestY <= psObject->padfY[iEdge+nVertStart] ) )
2302 {
2303 /* Rule #2:
2304 * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY)
2305 */
2306 double const intersect =
2307 ( psObject->padfX[iEdge+nVertStart]
2308 + ( dfTestY - psObject->padfY[iEdge+nVertStart] )
2309 / ( psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart] )
2310 * ( psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart] ) );
2311
2312 if (intersect < dfTestX)
2313 {
2314 bInner = !bInner;
2315 }
2316 }
2317 }
2318 } /* for iCheckRing */
2319
2320 /* -------------------------------------------------------------------- */
2321 /* Determine the current order of this ring so we will know if */
2322 /* it has to be reversed. */
2323 /* -------------------------------------------------------------------- */
2324 nVertStart = psObject->panPartStart[iOpRing];
2325
2326 if( iOpRing == psObject->nParts-1 )
2327 nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing];
2328 else
2329 nVertCount = psObject->panPartStart[iOpRing+1]
2330 - psObject->panPartStart[iOpRing];
2331
2332 if (nVertCount < 2)
2333 continue;
2334
2335 dfSum = psObject->padfX[nVertStart] * (psObject->padfY[nVertStart+1] - psObject->padfY[nVertStart+nVertCount-1]);
2336 for( iVert = nVertStart + 1; iVert < nVertStart+nVertCount-1; iVert++ )
2337 {
2338 dfSum += psObject->padfX[iVert] * (psObject->padfY[iVert+1] - psObject->padfY[iVert-1]);
2339 }
2340
2341 dfSum += psObject->padfX[iVert] * (psObject->padfY[nVertStart] - psObject->padfY[iVert-1]);
2342
2343 /* -------------------------------------------------------------------- */
2344 /* Reverse if necessary. */
2345 /* -------------------------------------------------------------------- */
2346 if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) )
2347 {
2348 int i;
2349
2350 bAltered++;
2351 for( i = 0; i < nVertCount/2; i++ )
2352 {
2353 double dfSaved;
2354
2355 /* Swap X */
2356 dfSaved = psObject->padfX[nVertStart+i];
2357 psObject->padfX[nVertStart+i] =
2358 psObject->padfX[nVertStart+nVertCount-i-1];
2359 psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved;
2360
2361 /* Swap Y */
2362 dfSaved = psObject->padfY[nVertStart+i];
2363 psObject->padfY[nVertStart+i] =
2364 psObject->padfY[nVertStart+nVertCount-i-1];
2365 psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved;
2366
2367 /* Swap Z */
2368 if( psObject->padfZ )
2369 {
2370 dfSaved = psObject->padfZ[nVertStart+i];
2371 psObject->padfZ[nVertStart+i] =
2372 psObject->padfZ[nVertStart+nVertCount-i-1];
2373 psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved;
2374 }
2375
2376 /* Swap M */
2377 if( psObject->padfM )
2378 {
2379 dfSaved = psObject->padfM[nVertStart+i];
2380 psObject->padfM[nVertStart+i] =
2381 psObject->padfM[nVertStart+nVertCount-i-1];
2382 psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved;
2383 }
2384 }
2385 }
2386 }
2387
2388 return bAltered;
2389 }
2390