Salome HOME
improve and document hydroGeoMeshUtils script
[modules/hydro.git] / src / shapelib / shpopen.c
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
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
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
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
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
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
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
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
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
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
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
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(*)
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(*)
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
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(*)
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 successfull 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 successfull 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 < 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 < 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 < 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 < 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 < 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 >= 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 < 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 < 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 < 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 >= 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 < 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(*)
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(*)
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
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
2226 SHPRewindObject( SHPHandle hSHP, SHPObject * psObject )
2227
2228 {
2229     int  iOpRing, bAltered = 0;
2230
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 )
2237         return 0;
2238
2239     if( psObject->nVertices == 0 || psObject->nParts == 0 )
2240         return 0;
2241
2242 /* -------------------------------------------------------------------- */
2243 /*      Process each of the rings.                                      */
2244 /* -------------------------------------------------------------------- */
2245     for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ )
2246     {
2247         int      bInner, iVert, nVertCount, nVertStart, iCheckRing;
2248         double   dfSum, dfTestX, dfTestY;
2249
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.                                        */
2256 /*                                                                      */
2257 /* -------------------------------------------------------------------- */
2258
2259         /* Use point in the middle of segment to avoid testing
2260          * common points of rings.
2261          */
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;
2266
2267         bInner = FALSE;
2268         for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ )
2269         {
2270             int iEdge;
2271
2272             if( iCheckRing == iOpRing )
2273                 continue;
2274             
2275             nVertStart = psObject->panPartStart[iCheckRing];
2276
2277             if( iCheckRing == psObject->nParts-1 )
2278                 nVertCount = psObject->nVertices 
2279                     - psObject->panPartStart[iCheckRing];
2280             else
2281                 nVertCount = psObject->panPartStart[iCheckRing+1] 
2282                     - psObject->panPartStart[iCheckRing];
2283
2284             for( iEdge = 0; iEdge < nVertCount; iEdge++ )
2285             {
2286                 int iNext;
2287
2288                 if( iEdge < nVertCount-1 )
2289                     iNext = iEdge+1;
2290                 else
2291                     iNext = 0;
2292
2293                 /* Rule #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.
2296                  */
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] ) )
2301                 {
2302                     /* Rule #2:
2303                      * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY)
2304                      */
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] ) );
2310
2311                     if (intersect  < dfTestX)
2312                     {
2313                         bInner = !bInner;
2314                     }
2315                 }    
2316             }
2317         } /* for iCheckRing */
2318
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];
2324
2325         if( iOpRing == psObject->nParts-1 )
2326             nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing];
2327         else
2328             nVertCount = psObject->panPartStart[iOpRing+1] 
2329                 - psObject->panPartStart[iOpRing];
2330
2331         if (nVertCount < 2)
2332             continue;
2333
2334         dfSum = psObject->padfX[nVertStart] * (psObject->padfY[nVertStart+1] - psObject->padfY[nVertStart+nVertCount-1]);
2335         for( iVert = nVertStart + 1; iVert < nVertStart+nVertCount-1; iVert++ )
2336         {
2337             dfSum += psObject->padfX[iVert] * (psObject->padfY[iVert+1] - psObject->padfY[iVert-1]);
2338         }
2339
2340         dfSum += psObject->padfX[iVert] * (psObject->padfY[nVertStart] - psObject->padfY[iVert-1]);
2341
2342 /* -------------------------------------------------------------------- */
2343 /*      Reverse if necessary.                                           */
2344 /* -------------------------------------------------------------------- */
2345         if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) )
2346         {
2347             int   i;
2348
2349             bAltered++;
2350             for( i = 0; i < nVertCount/2; i++ )
2351             {
2352                 double dfSaved;
2353
2354                 /* Swap X */
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;
2359
2360                 /* Swap Y */
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;
2365
2366                 /* Swap Z */
2367                 if( psObject->padfZ )
2368                 {
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;
2373                 }
2374
2375                 /* Swap M */
2376                 if( psObject->padfM )
2377                 {
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;
2382                 }
2383             }
2384         }
2385     }
2386
2387     return bAltered;
2388 }