Add:Core:Initial work on shapefile driver
[navit-package] / navit / map / shapefile / shpopen.c
1 /******************************************************************************
2  * $Id: shpopen.c,v 1.59 2008/03/14 05:25:31 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.59  2008/03/14 05:25:31  fwarmerdam
38  * Correct crash on buggy geometries (gdal #2218)
39  *
40  * Revision 1.58  2008/01/08 23:28:26  bram
41  * on line 2095, use a float instead of a double to avoid a compiler warning
42  *
43  * Revision 1.57  2007/12/06 07:00:25  fwarmerdam
44  * dbfopen now using SAHooks for fileio
45  *
46  * Revision 1.56  2007/12/04 20:37:56  fwarmerdam
47  * preliminary implementation of hooks api for io and errors
48  *
49  * Revision 1.55  2007/11/21 22:39:56  fwarmerdam
50  * close shx file in readonly mode (GDAL #1956)
51  *
52  * Revision 1.54  2007/11/15 00:12:47  mloskot
53  * Backported recent changes from GDAL (Ticket #1415) to Shapelib.
54  *
55  * Revision 1.53  2007/11/14 22:31:08  fwarmerdam
56  * checks after mallocs to detect for corrupted/voluntary broken shapefiles.
57  * http://trac.osgeo.org/gdal/ticket/1991
58  *
59  * Revision 1.52  2007/06/21 15:58:33  fwarmerdam
60  * fix for SHPRewindObject when rings touch at one vertex (gdal #976)
61  *
62  * Revision 1.51  2006/09/04 15:24:01  fwarmerdam
63  * Fixed up log message for 1.49.
64  *
65  * Revision 1.50  2006/09/04 15:21:39  fwarmerdam
66  * fix of last fix
67  *
68  * Revision 1.49  2006/09/04 15:21:00  fwarmerdam
69  * MLoskot: Added stronger test of Shapefile reading failures, e.g. truncated
70  * files.  The problem was discovered by Tim Sutton and reported here
71  *   https://svn.qgis.org/trac/ticket/200
72  *
73  * Revision 1.48  2006/01/26 15:07:32  fwarmerdam
74  * add bMeasureIsUsed flag from Craig Bruce: Bug 1249
75  *
76  * Revision 1.47  2006/01/04 20:07:23  fwarmerdam
77  * In SHPWriteObject() make sure that the record length is updated
78  * when rewriting an existing record.
79  *
80  * Revision 1.46  2005/02/11 17:17:46  fwarmerdam
81  * added panPartStart[0] validation
82  *
83  * Revision 1.45  2004/09/26 20:09:48  fwarmerdam
84  * const correctness changes
85  *
86  * Revision 1.44  2003/12/29 00:18:39  fwarmerdam
87  * added error checking for failed IO and optional CPL error reporting
88  *
89  * Revision 1.43  2003/12/01 16:20:08  warmerda
90  * be careful of zero vertex shapes
91  *
92  * Revision 1.42  2003/12/01 14:58:27  warmerda
93  * added degenerate object check in SHPRewindObject()
94  *
95  * Revision 1.41  2003/07/08 15:22:43  warmerda
96  * avoid warning
97  *
98  * Revision 1.40  2003/04/21 18:30:37  warmerda
99  * added header write/update public methods
100  *
101  * Revision 1.39  2002/08/26 06:46:56  warmerda
102  * avoid c++ comments
103  *
104  * Revision 1.38  2002/05/07 16:43:39  warmerda
105  * Removed debugging printf.
106  *
107  * Revision 1.37  2002/04/10 17:35:22  warmerda
108  * fixed bug in ring reversal code
109  *
110  * Revision 1.36  2002/04/10 16:59:54  warmerda
111  * added SHPRewindObject
112  *
113  * Revision 1.35  2001/12/07 15:10:44  warmerda
114  * fix if .shx fails to open
115  *
116  * Revision 1.34  2001/11/01 16:29:55  warmerda
117  * move pabyRec into SHPInfo for thread safety
118  *
119  * Revision 1.33  2001/07/03 12:18:15  warmerda
120  * Improved cleanup if SHX not found, provied by Riccardo Cohen.
121  *
122  * Revision 1.32  2001/06/22 01:58:07  warmerda
123  * be more careful about establishing initial bounds in face of NULL shapes
124  *
125  * Revision 1.31  2001/05/31 19:35:29  warmerda
126  * added support for writing null shapes
127  *
128  * Revision 1.30  2001/05/28 12:46:29  warmerda
129  * Add some checking on reasonableness of record count when opening.
130  *
131  * Revision 1.29  2001/05/23 13:36:52  warmerda
132  * added use of SHPAPI_CALL
133  *
134  * Revision 1.28  2001/02/06 22:25:06  warmerda
135  * fixed memory leaks when SHPOpen() fails
136  *
137  * Revision 1.27  2000/07/18 15:21:33  warmerda
138  * added better enforcement of -1 for append in SHPWriteObject
139  *
140  * Revision 1.26  2000/02/16 16:03:51  warmerda
141  * added null shape support
142  *
143  * Revision 1.25  1999/12/15 13:47:07  warmerda
144  * Fixed record size settings in .shp file (was 4 words too long)
145  * Added stdlib.h.
146  *
147  * Revision 1.24  1999/11/05 14:12:04  warmerda
148  * updated license terms
149  *
150  * Revision 1.23  1999/07/27 00:53:46  warmerda
151  * added support for rewriting shapes
152  *
153  * Revision 1.22  1999/06/11 19:19:11  warmerda
154  * Cleanup pabyRec static buffer on SHPClose().
155  *
156  * Revision 1.21  1999/06/02 14:57:56  kshih
157  * Remove unused variables
158  *
159  * Revision 1.20  1999/04/19 21:04:17  warmerda
160  * Fixed syntax error.
161  *
162  * Revision 1.19  1999/04/19 21:01:57  warmerda
163  * Force access string to binary in SHPOpen().
164  *
165  * Revision 1.18  1999/04/01 18:48:07  warmerda
166  * Try upper case extensions if lower case doesn't work.
167  *
168  * Revision 1.17  1998/12/31 15:29:39  warmerda
169  * Disable writing measure values to multipatch objects if
170  * DISABLE_MULTIPATCH_MEASURE is defined.
171  *
172  * Revision 1.16  1998/12/16 05:14:33  warmerda
173  * Added support to write MULTIPATCH.  Fixed reading Z coordinate of
174  * MULTIPATCH. Fixed record size written for all feature types.
175  *
176  * Revision 1.15  1998/12/03 16:35:29  warmerda
177  * r+b is proper binary access string, not rb+.
178  *
179  * Revision 1.14  1998/12/03 15:47:56  warmerda
180  * Fixed setting of nVertices in SHPCreateObject().
181  *
182  * Revision 1.13  1998/12/03 15:33:54  warmerda
183  * Made SHPCalculateExtents() separately callable.
184  *
185  * Revision 1.12  1998/11/11 20:01:50  warmerda
186  * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines.
187  *
188  * Revision 1.11  1998/11/09 20:56:44  warmerda
189  * Fixed up handling of file wide bounds.
190  *
191  * Revision 1.10  1998/11/09 20:18:51  warmerda
192  * Converted to support 3D shapefiles, and use of SHPObject.
193  *
194  * Revision 1.9  1998/02/24 15:09:05  warmerda
195  * Fixed memory leak.
196  *
197  * Revision 1.8  1997/12/04 15:40:29  warmerda
198  * Fixed byte swapping of record number, and record length fields in the
199  * .shp file.
200  *
201  * Revision 1.7  1995/10/21 03:15:58  warmerda
202  * Added support for binary file access, the magic cookie 9997
203  * and tried to improve the int32 selection logic for 16bit systems.
204  *
205  * Revision 1.6  1995/09/04  04:19:41  warmerda
206  * Added fix for file bounds.
207  *
208  * Revision 1.5  1995/08/25  15:16:44  warmerda
209  * Fixed a couple of problems with big endian systems ... one with bounds
210  * and the other with multipart polygons.
211  *
212  * Revision 1.4  1995/08/24  18:10:17  warmerda
213  * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc()
214  * functions (such as on the Sun).
215  *
216  * Revision 1.3  1995/08/23  02:23:15  warmerda
217  * Added support for reading bounds, and fixed up problems in setting the
218  * file wide bounds.
219  *
220  * Revision 1.2  1995/08/04  03:16:57  warmerda
221  * Added header.
222  *
223  */
224
225 #include "shapefil.h"
226
227 #include <math.h>
228 #include <limits.h>
229 #include <assert.h>
230 #include <stdlib.h>
231 #include <string.h>
232 #include <stdio.h>
233
234 SHP_CVSID("$Id: shpopen.c,v 1.59 2008/03/14 05:25:31 fwarmerdam Exp $")
235
236 typedef unsigned char uchar;
237
238 #if UINT_MAX == 65535
239 typedef long          int32;
240 #else
241 typedef int           int32;
242 #endif
243
244 #ifndef FALSE
245 #  define FALSE         0
246 #  define TRUE          1
247 #endif
248
249 #define ByteCopy( a, b, c )     memcpy( b, a, c )
250 #ifndef MAX
251 #  define MIN(a,b)      ((a<b) ? a : b)
252 #  define MAX(a,b)      ((a>b) ? a : b)
253 #endif
254
255 static int      bBigEndian;
256
257
258 /************************************************************************/
259 /*                              SwapWord()                              */
260 /*                                                                      */
261 /*      Swap a 2, 4 or 8 byte word.                                     */
262 /************************************************************************/
263
264 static void     SwapWord( int length, void * wordP )
265
266 {
267     int         i;
268     uchar       temp;
269
270     for( i=0; i < length/2; i++ )
271     {
272         temp = ((uchar *) wordP)[i];
273         ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1];
274         ((uchar *) wordP)[length-i-1] = temp;
275     }
276 }
277
278 /************************************************************************/
279 /*                             SfRealloc()                              */
280 /*                                                                      */
281 /*      A realloc cover function that will access a NULL pointer as     */
282 /*      a valid input.                                                  */
283 /************************************************************************/
284
285 static void * SfRealloc( void * pMem, int nNewSize )
286
287 {
288     if( pMem == NULL )
289         return( (void *) malloc(nNewSize) );
290     else
291         return( (void *) realloc(pMem,nNewSize) );
292 }
293
294 /************************************************************************/
295 /*                          SHPWriteHeader()                            */
296 /*                                                                      */
297 /*      Write out a header for the .shp and .shx files as well as the   */
298 /*      contents of the index (.shx) file.                              */
299 /************************************************************************/
300
301 void SHPWriteHeader( SHPHandle psSHP )
302
303 {
304     uchar       abyHeader[100];
305     int         i;
306     int32       i32;
307     double      dValue;
308     int32       *panSHX;
309     
310     if (psSHP->fpSHX == NULL)
311     {
312         psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed");
313         return;
314     }
315
316 /* -------------------------------------------------------------------- */
317 /*      Prepare header block for .shp file.                             */
318 /* -------------------------------------------------------------------- */
319     for( i = 0; i < 100; i++ )
320       abyHeader[i] = 0;
321
322     abyHeader[2] = 0x27;                                /* magic cookie */
323     abyHeader[3] = 0x0a;
324
325     i32 = psSHP->nFileSize/2;                           /* file size */
326     ByteCopy( &i32, abyHeader+24, 4 );
327     if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
328     
329     i32 = 1000;                                         /* version */
330     ByteCopy( &i32, abyHeader+28, 4 );
331     if( bBigEndian ) SwapWord( 4, abyHeader+28 );
332     
333     i32 = psSHP->nShapeType;                            /* shape type */
334     ByteCopy( &i32, abyHeader+32, 4 );
335     if( bBigEndian ) SwapWord( 4, abyHeader+32 );
336
337     dValue = psSHP->adBoundsMin[0];                     /* set bounds */
338     ByteCopy( &dValue, abyHeader+36, 8 );
339     if( bBigEndian ) SwapWord( 8, abyHeader+36 );
340
341     dValue = psSHP->adBoundsMin[1];
342     ByteCopy( &dValue, abyHeader+44, 8 );
343     if( bBigEndian ) SwapWord( 8, abyHeader+44 );
344
345     dValue = psSHP->adBoundsMax[0];
346     ByteCopy( &dValue, abyHeader+52, 8 );
347     if( bBigEndian ) SwapWord( 8, abyHeader+52 );
348
349     dValue = psSHP->adBoundsMax[1];
350     ByteCopy( &dValue, abyHeader+60, 8 );
351     if( bBigEndian ) SwapWord( 8, abyHeader+60 );
352
353     dValue = psSHP->adBoundsMin[2];                     /* z */
354     ByteCopy( &dValue, abyHeader+68, 8 );
355     if( bBigEndian ) SwapWord( 8, abyHeader+68 );
356
357     dValue = psSHP->adBoundsMax[2];
358     ByteCopy( &dValue, abyHeader+76, 8 );
359     if( bBigEndian ) SwapWord( 8, abyHeader+76 );
360
361     dValue = psSHP->adBoundsMin[3];                     /* m */
362     ByteCopy( &dValue, abyHeader+84, 8 );
363     if( bBigEndian ) SwapWord( 8, abyHeader+84 );
364
365     dValue = psSHP->adBoundsMax[3];
366     ByteCopy( &dValue, abyHeader+92, 8 );
367     if( bBigEndian ) SwapWord( 8, abyHeader+92 );
368
369 /* -------------------------------------------------------------------- */
370 /*      Write .shp file header.                                         */
371 /* -------------------------------------------------------------------- */
372     if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0 
373         || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHP ) != 1 )
374     {
375         psSHP->sHooks.Error( "Failure writing .shp header" );
376         return;
377     }
378
379 /* -------------------------------------------------------------------- */
380 /*      Prepare, and write .shx file header.                            */
381 /* -------------------------------------------------------------------- */
382     i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2;   /* file size */
383     ByteCopy( &i32, abyHeader+24, 4 );
384     if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
385     
386     if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0 
387         || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 )
388     {
389         psSHP->sHooks.Error( "Failure writing .shx header" );
390         return;
391     }
392
393 /* -------------------------------------------------------------------- */
394 /*      Write out the .shx contents.                                    */
395 /* -------------------------------------------------------------------- */
396     panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords);
397
398     for( i = 0; i < psSHP->nRecords; i++ )
399     {
400         panSHX[i*2  ] = psSHP->panRecOffset[i]/2;
401         panSHX[i*2+1] = psSHP->panRecSize[i]/2;
402         if( !bBigEndian ) SwapWord( 4, panSHX+i*2 );
403         if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 );
404     }
405
406     if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX ) 
407         != psSHP->nRecords )
408     {
409         psSHP->sHooks.Error( "Failure writing .shx contents" );
410     }
411
412     free( panSHX );
413
414 /* -------------------------------------------------------------------- */
415 /*      Flush to disk.                                                  */
416 /* -------------------------------------------------------------------- */
417     psSHP->sHooks.FFlush( psSHP->fpSHP );
418     psSHP->sHooks.FFlush( psSHP->fpSHX );
419 }
420
421 /************************************************************************/
422 /*                              SHPOpen()                               */
423 /************************************************************************/
424
425 SHPHandle SHPAPI_CALL
426 SHPOpen( const char * pszLayer, const char * pszAccess )
427
428 {
429     SAHooks sHooks;
430
431     SASetupDefaultHooks( &sHooks );
432
433     return SHPOpenLL( pszLayer, pszAccess, &sHooks );
434 }
435
436 /************************************************************************/
437 /*                              SHPOpen()                               */
438 /*                                                                      */
439 /*      Open the .shp and .shx files based on the basename of the       */
440 /*      files or either file name.                                      */
441 /************************************************************************/
442    
443 SHPHandle SHPAPI_CALL
444 SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks )
445
446 {
447     char                *pszFullname, *pszBasename;
448     SHPHandle           psSHP;
449     
450     uchar               *pabyBuf;
451     int                 i;
452     double              dValue;
453     
454 /* -------------------------------------------------------------------- */
455 /*      Ensure the access string is one of the legal ones.  We          */
456 /*      ensure the result string indicates binary to avoid common       */
457 /*      problems on Windows.                                            */
458 /* -------------------------------------------------------------------- */
459     if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0
460         || strcmp(pszAccess,"r+") == 0 )
461         pszAccess = "r+b";
462     else
463         pszAccess = "rb";
464     
465 /* -------------------------------------------------------------------- */
466 /*      Establish the byte order on this machine.                       */
467 /* -------------------------------------------------------------------- */
468     i = 1;
469     if( *((uchar *) &i) == 1 )
470         bBigEndian = FALSE;
471     else
472         bBigEndian = TRUE;
473
474 /* -------------------------------------------------------------------- */
475 /*      Initialize the info structure.                                  */
476 /* -------------------------------------------------------------------- */
477     psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1);
478
479     psSHP->bUpdated = FALSE;
480     memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) );
481
482 /* -------------------------------------------------------------------- */
483 /*      Compute the base (layer) name.  If there is any extension       */
484 /*      on the passed in filename we will strip it off.                 */
485 /* -------------------------------------------------------------------- */
486     pszBasename = (char *) malloc(strlen(pszLayer)+5);
487     strcpy( pszBasename, pszLayer );
488     for( i = strlen(pszBasename)-1; 
489          i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
490                && pszBasename[i] != '\\';
491          i-- ) {}
492
493     if( pszBasename[i] == '.' )
494         pszBasename[i] = '\0';
495
496 /* -------------------------------------------------------------------- */
497 /*      Open the .shp and .shx files.  Note that files pulled from      */
498 /*      a PC to Unix with upper case filenames won't work!              */
499 /* -------------------------------------------------------------------- */
500     pszFullname = (char *) malloc(strlen(pszBasename) + 5);
501     sprintf( pszFullname, "%s.shp", pszBasename ) ;
502     psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess );
503     if( psSHP->fpSHP == NULL )
504     {
505         sprintf( pszFullname, "%s.SHP", pszBasename );
506         psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess );
507     }
508     
509     if( psSHP->fpSHP == NULL )
510     {
511 #ifdef USE_CPL
512         CPLError( CE_Failure, CPLE_OpenFailed, 
513                   "Unable to open %s.shp or %s.SHP.", 
514                   pszBasename, pszBasename );
515 #endif
516         free( psSHP );
517         free( pszBasename );
518         free( pszFullname );
519         return( NULL );
520     }
521
522     sprintf( pszFullname, "%s.shx", pszBasename );
523     psSHP->fpSHX =  psSHP->sHooks.FOpen(pszFullname, pszAccess );
524     if( psSHP->fpSHX == NULL )
525     {
526         sprintf( pszFullname, "%s.SHX", pszBasename );
527         psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess );
528     }
529     
530     if( psSHP->fpSHX == NULL )
531     {
532 #ifdef USE_CPL
533         CPLError( CE_Failure, CPLE_OpenFailed, 
534                   "Unable to open %s.shx or %s.SHX.", 
535                   pszBasename, pszBasename );
536 #endif
537         psSHP->sHooks.FClose( psSHP->fpSHP );
538         free( psSHP );
539         free( pszBasename );
540         free( pszFullname );
541         return( NULL );
542     }
543
544     free( pszFullname );
545     free( pszBasename );
546
547 /* -------------------------------------------------------------------- */
548 /*  Read the file size from the SHP file.                               */
549 /* -------------------------------------------------------------------- */
550     pabyBuf = (uchar *) malloc(100);
551     psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHP );
552
553     psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256
554                         + pabyBuf[25] * 256 * 256
555                         + pabyBuf[26] * 256
556                         + pabyBuf[27]) * 2;
557
558 /* -------------------------------------------------------------------- */
559 /*  Read SHX file Header info                                           */
560 /* -------------------------------------------------------------------- */
561     if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1 
562         || pabyBuf[0] != 0 
563         || pabyBuf[1] != 0 
564         || pabyBuf[2] != 0x27 
565         || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) )
566     {
567         psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." );
568         psSHP->sHooks.FClose( psSHP->fpSHP );
569         psSHP->sHooks.FClose( psSHP->fpSHX );
570         free( psSHP );
571
572         return( NULL );
573     }
574
575     psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256
576       + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256;
577     psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8;
578
579     psSHP->nShapeType = pabyBuf[32];
580
581     if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 )
582     {
583         char szError[200];
584         
585         sprintf( szError, 
586                  "Record count in .shp header is %d, which seems\n"
587                  "unreasonable.  Assuming header is corrupt.",
588                   psSHP->nRecords );
589         psSHP->sHooks.Error( szError );                                
590         psSHP->sHooks.FClose( psSHP->fpSHP );
591         psSHP->sHooks.FClose( psSHP->fpSHX );
592         free( psSHP );
593         free(pabyBuf);
594
595         return( NULL );
596     }
597
598 /* -------------------------------------------------------------------- */
599 /*      Read the bounds.                                                */
600 /* -------------------------------------------------------------------- */
601     if( bBigEndian ) SwapWord( 8, pabyBuf+36 );
602     memcpy( &dValue, pabyBuf+36, 8 );
603     psSHP->adBoundsMin[0] = dValue;
604
605     if( bBigEndian ) SwapWord( 8, pabyBuf+44 );
606     memcpy( &dValue, pabyBuf+44, 8 );
607     psSHP->adBoundsMin[1] = dValue;
608
609     if( bBigEndian ) SwapWord( 8, pabyBuf+52 );
610     memcpy( &dValue, pabyBuf+52, 8 );
611     psSHP->adBoundsMax[0] = dValue;
612
613     if( bBigEndian ) SwapWord( 8, pabyBuf+60 );
614     memcpy( &dValue, pabyBuf+60, 8 );
615     psSHP->adBoundsMax[1] = dValue;
616
617     if( bBigEndian ) SwapWord( 8, pabyBuf+68 );         /* z */
618     memcpy( &dValue, pabyBuf+68, 8 );
619     psSHP->adBoundsMin[2] = dValue;
620     
621     if( bBigEndian ) SwapWord( 8, pabyBuf+76 );
622     memcpy( &dValue, pabyBuf+76, 8 );
623     psSHP->adBoundsMax[2] = dValue;
624     
625     if( bBigEndian ) SwapWord( 8, pabyBuf+84 );         /* z */
626     memcpy( &dValue, pabyBuf+84, 8 );
627     psSHP->adBoundsMin[3] = dValue;
628
629     if( bBigEndian ) SwapWord( 8, pabyBuf+92 );
630     memcpy( &dValue, pabyBuf+92, 8 );
631     psSHP->adBoundsMax[3] = dValue;
632
633     free( pabyBuf );
634
635 /* -------------------------------------------------------------------- */
636 /*      Read the .shx file to get the offsets to each record in         */
637 /*      the .shp file.                                                  */
638 /* -------------------------------------------------------------------- */
639     psSHP->nMaxRecords = psSHP->nRecords;
640
641     psSHP->panRecOffset =
642         (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );
643     psSHP->panRecSize =
644         (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );
645     pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) );
646
647     if (psSHP->panRecOffset == NULL ||
648         psSHP->panRecSize == NULL ||
649         pabyBuf == NULL)
650     {
651         char szError[200];
652
653         sprintf(szError, 
654                 "Not enough memory to allocate requested memory (nRecords=%d).\n"
655                 "Probably broken SHP file", 
656                 psSHP->nRecords );
657         psSHP->sHooks.Error( szError );
658         psSHP->sHooks.FClose( psSHP->fpSHP );
659         psSHP->sHooks.FClose( psSHP->fpSHX );
660         if (psSHP->panRecOffset) free( psSHP->panRecOffset );
661         if (psSHP->panRecSize) free( psSHP->panRecSize );
662         if (pabyBuf) free( pabyBuf );
663         free( psSHP );
664         return( NULL );
665     }
666
667     if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ) 
668                         != psSHP->nRecords )
669     {
670         char szError[200];
671
672         sprintf( szError, 
673                  "Failed to read all values for %d records in .shx file.",
674                  psSHP->nRecords );
675         psSHP->sHooks.Error( szError );
676
677         /* SHX is short or unreadable for some reason. */
678         psSHP->sHooks.FClose( psSHP->fpSHP );
679         psSHP->sHooks.FClose( psSHP->fpSHX );
680         free( psSHP->panRecOffset );
681         free( psSHP->panRecSize );
682         free( pabyBuf );
683         free( psSHP );
684
685         return( NULL );
686     }
687     
688     /* In read-only mode, we can close the SHX now */
689     if (strcmp(pszAccess, "rb") == 0)
690     {
691         psSHP->sHooks.FClose( psSHP->fpSHX );
692         psSHP->fpSHX = NULL;
693     }
694
695     for( i = 0; i < psSHP->nRecords; i++ )
696     {
697         int32           nOffset, nLength;
698
699         memcpy( &nOffset, pabyBuf + i * 8, 4 );
700         if( !bBigEndian ) SwapWord( 4, &nOffset );
701
702         memcpy( &nLength, pabyBuf + i * 8 + 4, 4 );
703         if( !bBigEndian ) SwapWord( 4, &nLength );
704
705         psSHP->panRecOffset[i] = nOffset*2;
706         psSHP->panRecSize[i] = nLength*2;
707     }
708     free( pabyBuf );
709
710     return( psSHP );
711 }
712
713 /************************************************************************/
714 /*                              SHPClose()                              */
715 /*                                                                      */
716 /*      Close the .shp and .shx files.                                  */
717 /************************************************************************/
718
719 void SHPAPI_CALL
720 SHPClose(SHPHandle psSHP )
721
722 {
723     if( psSHP == NULL )
724         return;
725
726 /* -------------------------------------------------------------------- */
727 /*      Update the header if we have modified anything.                 */
728 /* -------------------------------------------------------------------- */
729     if( psSHP->bUpdated )
730         SHPWriteHeader( psSHP );
731
732 /* -------------------------------------------------------------------- */
733 /*      Free all resources, and close files.                            */
734 /* -------------------------------------------------------------------- */
735     free( psSHP->panRecOffset );
736     free( psSHP->panRecSize );
737
738     if ( psSHP->fpSHX != NULL)
739         psSHP->sHooks.FClose( psSHP->fpSHX );
740     psSHP->sHooks.FClose( psSHP->fpSHP );
741
742     if( psSHP->pabyRec != NULL )
743     {
744         free( psSHP->pabyRec );
745     }
746     
747     free( psSHP );
748 }
749
750 /************************************************************************/
751 /*                             SHPGetInfo()                             */
752 /*                                                                      */
753 /*      Fetch general information about the shape file.                 */
754 /************************************************************************/
755
756 void SHPAPI_CALL
757 SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType,
758            double * padfMinBound, double * padfMaxBound )
759
760 {
761     int         i;
762
763     if( psSHP == NULL )
764         return;
765     
766     if( pnEntities != NULL )
767         *pnEntities = psSHP->nRecords;
768
769     if( pnShapeType != NULL )
770         *pnShapeType = psSHP->nShapeType;
771
772     for( i = 0; i < 4; i++ )
773     {
774         if( padfMinBound != NULL )
775             padfMinBound[i] = psSHP->adBoundsMin[i];
776         if( padfMaxBound != NULL )
777             padfMaxBound[i] = psSHP->adBoundsMax[i];
778     }
779 }
780
781 /************************************************************************/
782 /*                             SHPCreate()                              */
783 /*                                                                      */
784 /*      Create a new shape file and return a handle to the open         */
785 /*      shape file with read/write access.                              */
786 /************************************************************************/
787
788 SHPHandle SHPAPI_CALL
789 SHPCreate( const char * pszLayer, int nShapeType )
790
791 {
792     SAHooks sHooks;
793
794     SASetupDefaultHooks( &sHooks );
795
796     return SHPCreateLL( pszLayer, nShapeType, &sHooks );
797 }
798
799 /************************************************************************/
800 /*                             SHPCreate()                              */
801 /*                                                                      */
802 /*      Create a new shape file and return a handle to the open         */
803 /*      shape file with read/write access.                              */
804 /************************************************************************/
805
806 SHPHandle SHPAPI_CALL
807 SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks )
808
809 {
810     char        *pszBasename, *pszFullname;
811     int         i;
812     SAFile      fpSHP, fpSHX;
813     uchar       abyHeader[100];
814     int32       i32;
815     double      dValue;
816     
817 /* -------------------------------------------------------------------- */
818 /*      Establish the byte order on this system.                        */
819 /* -------------------------------------------------------------------- */
820     i = 1;
821     if( *((uchar *) &i) == 1 )
822         bBigEndian = FALSE;
823     else
824         bBigEndian = TRUE;
825
826 /* -------------------------------------------------------------------- */
827 /*      Compute the base (layer) name.  If there is any extension       */
828 /*      on the passed in filename we will strip it off.                 */
829 /* -------------------------------------------------------------------- */
830     pszBasename = (char *) malloc(strlen(pszLayer)+5);
831     strcpy( pszBasename, pszLayer );
832     for( i = strlen(pszBasename)-1; 
833          i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
834                && pszBasename[i] != '\\';
835          i-- ) {}
836
837     if( pszBasename[i] == '.' )
838         pszBasename[i] = '\0';
839
840 /* -------------------------------------------------------------------- */
841 /*      Open the two files so we can write their headers.               */
842 /* -------------------------------------------------------------------- */
843     pszFullname = (char *) malloc(strlen(pszBasename) + 5);
844     sprintf( pszFullname, "%s.shp", pszBasename );
845     fpSHP = psHooks->FOpen(pszFullname, "wb" );
846     if( fpSHP == NULL )
847     {
848         psHooks->Error( "Failed to create file .shp file." );
849         return( NULL );
850     }
851
852     sprintf( pszFullname, "%s.shx", pszBasename );
853     fpSHX = psHooks->FOpen(pszFullname, "wb" );
854     if( fpSHX == NULL )
855     {
856         psHooks->Error( "Failed to create file .shx file." );
857         return( NULL );
858     }
859
860     free( pszFullname );
861     free( pszBasename );
862
863 /* -------------------------------------------------------------------- */
864 /*      Prepare header block for .shp file.                             */
865 /* -------------------------------------------------------------------- */
866     for( i = 0; i < 100; i++ )
867       abyHeader[i] = 0;
868
869     abyHeader[2] = 0x27;                                /* magic cookie */
870     abyHeader[3] = 0x0a;
871
872     i32 = 50;                                           /* file size */
873     ByteCopy( &i32, abyHeader+24, 4 );
874     if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
875     
876     i32 = 1000;                                         /* version */
877     ByteCopy( &i32, abyHeader+28, 4 );
878     if( bBigEndian ) SwapWord( 4, abyHeader+28 );
879     
880     i32 = nShapeType;                                   /* shape type */
881     ByteCopy( &i32, abyHeader+32, 4 );
882     if( bBigEndian ) SwapWord( 4, abyHeader+32 );
883
884     dValue = 0.0;                                       /* set bounds */
885     ByteCopy( &dValue, abyHeader+36, 8 );
886     ByteCopy( &dValue, abyHeader+44, 8 );
887     ByteCopy( &dValue, abyHeader+52, 8 );
888     ByteCopy( &dValue, abyHeader+60, 8 );
889
890 /* -------------------------------------------------------------------- */
891 /*      Write .shp file header.                                         */
892 /* -------------------------------------------------------------------- */
893     if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 )
894     {
895         psHooks->Error( "Failed to write .shp header." );
896         return NULL;
897     }
898
899 /* -------------------------------------------------------------------- */
900 /*      Prepare, and write .shx file header.                            */
901 /* -------------------------------------------------------------------- */
902     i32 = 50;                                           /* file size */
903     ByteCopy( &i32, abyHeader+24, 4 );
904     if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
905     
906     if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 )
907     {
908         psHooks->Error( "Failed to write .shx header." );
909         return NULL;
910     }
911
912 /* -------------------------------------------------------------------- */
913 /*      Close the files, and then open them as regular existing files.  */
914 /* -------------------------------------------------------------------- */
915     psHooks->FClose( fpSHP );
916     psHooks->FClose( fpSHX );
917
918     return( SHPOpenLL( pszLayer, "r+b", psHooks ) );
919 }
920
921 /************************************************************************/
922 /*                           _SHPSetBounds()                            */
923 /*                                                                      */
924 /*      Compute a bounds rectangle for a shape, and set it into the     */
925 /*      indicated location in the record.                               */
926 /************************************************************************/
927
928 static void     _SHPSetBounds( uchar * pabyRec, SHPObject * psShape )
929
930 {
931     ByteCopy( &(psShape->dfXMin), pabyRec +  0, 8 );
932     ByteCopy( &(psShape->dfYMin), pabyRec +  8, 8 );
933     ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 );
934     ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 );
935
936     if( bBigEndian )
937     {
938         SwapWord( 8, pabyRec + 0 );
939         SwapWord( 8, pabyRec + 8 );
940         SwapWord( 8, pabyRec + 16 );
941         SwapWord( 8, pabyRec + 24 );
942     }
943 }
944
945 /************************************************************************/
946 /*                         SHPComputeExtents()                          */
947 /*                                                                      */
948 /*      Recompute the extents of a shape.  Automatically done by        */
949 /*      SHPCreateObject().                                              */
950 /************************************************************************/
951
952 void SHPAPI_CALL
953 SHPComputeExtents( SHPObject * psObject )
954
955 {
956     int         i;
957     
958 /* -------------------------------------------------------------------- */
959 /*      Build extents for this object.                                  */
960 /* -------------------------------------------------------------------- */
961     if( psObject->nVertices > 0 )
962     {
963         psObject->dfXMin = psObject->dfXMax = psObject->padfX[0];
964         psObject->dfYMin = psObject->dfYMax = psObject->padfY[0];
965         psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0];
966         psObject->dfMMin = psObject->dfMMax = psObject->padfM[0];
967     }
968     
969     for( i = 0; i < psObject->nVertices; i++ )
970     {
971         psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]);
972         psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]);
973         psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]);
974         psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]);
975
976         psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]);
977         psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]);
978         psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]);
979         psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]);
980     }
981 }
982
983 /************************************************************************/
984 /*                          SHPCreateObject()                           */
985 /*                                                                      */
986 /*      Create a shape object.  It should be freed with                 */
987 /*      SHPDestroyObject().                                             */
988 /************************************************************************/
989
990 SHPObject SHPAPI_CALL1(*)
991 SHPCreateObject( int nSHPType, int nShapeId, int nParts,
992                  const int * panPartStart, const int * panPartType,
993                  int nVertices, const double *padfX, const double *padfY,
994                  const double * padfZ, const double * padfM )
995
996 {
997     SHPObject   *psObject;
998     int         i, bHasM, bHasZ;
999
1000     psObject = (SHPObject *) calloc(1,sizeof(SHPObject));
1001     psObject->nSHPType = nSHPType;
1002     psObject->nShapeId = nShapeId;
1003     psObject->bMeasureIsUsed = FALSE;
1004
1005 /* -------------------------------------------------------------------- */
1006 /*      Establish whether this shape type has M, and Z values.          */
1007 /* -------------------------------------------------------------------- */
1008     if( nSHPType == SHPT_ARCM
1009         || nSHPType == SHPT_POINTM
1010         || nSHPType == SHPT_POLYGONM
1011         || nSHPType == SHPT_MULTIPOINTM )
1012     {
1013         bHasM = TRUE;
1014         bHasZ = FALSE;
1015     }
1016     else if( nSHPType == SHPT_ARCZ
1017              || nSHPType == SHPT_POINTZ
1018              || nSHPType == SHPT_POLYGONZ
1019              || nSHPType == SHPT_MULTIPOINTZ
1020              || nSHPType == SHPT_MULTIPATCH )
1021     {
1022         bHasM = TRUE;
1023         bHasZ = TRUE;
1024     }
1025     else
1026     {
1027         bHasM = FALSE;
1028         bHasZ = FALSE;
1029     }
1030
1031 /* -------------------------------------------------------------------- */
1032 /*      Capture parts.  Note that part type is optional, and            */
1033 /*      defaults to ring.                                               */
1034 /* -------------------------------------------------------------------- */
1035     if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON
1036         || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM
1037         || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ
1038         || nSHPType == SHPT_MULTIPATCH )
1039     {
1040         psObject->nParts = MAX(1,nParts);
1041
1042         psObject->panPartStart = (int *)
1043             malloc(sizeof(int) * psObject->nParts);
1044         psObject->panPartType = (int *)
1045             malloc(sizeof(int) * psObject->nParts);
1046
1047         psObject->panPartStart[0] = 0;
1048         psObject->panPartType[0] = SHPP_RING;
1049         
1050         for( i = 0; i < nParts; i++ )
1051         {
1052             psObject->panPartStart[i] = panPartStart[i];
1053
1054             if( panPartType != NULL )
1055                 psObject->panPartType[i] = panPartType[i];
1056             else
1057                 psObject->panPartType[i] = SHPP_RING;
1058         }
1059
1060         if( psObject->panPartStart[0] != 0 )
1061             psObject->panPartStart[0] = 0;
1062     }
1063
1064 /* -------------------------------------------------------------------- */
1065 /*      Capture vertices.  Note that Z and M are optional, but X and    */
1066 /*      Y are not.                                                      */
1067 /* -------------------------------------------------------------------- */
1068     if( nVertices > 0 )
1069     {
1070         psObject->padfX = (double *) calloc(sizeof(double),nVertices);
1071         psObject->padfY = (double *) calloc(sizeof(double),nVertices);
1072         psObject->padfZ = (double *) calloc(sizeof(double),nVertices);
1073         psObject->padfM = (double *) calloc(sizeof(double),nVertices);
1074
1075         assert( padfX != NULL );
1076         assert( padfY != NULL );
1077     
1078         for( i = 0; i < nVertices; i++ )
1079         {
1080             psObject->padfX[i] = padfX[i];
1081             psObject->padfY[i] = padfY[i];
1082             if( padfZ != NULL && bHasZ )
1083                 psObject->padfZ[i] = padfZ[i];
1084             if( padfM != NULL && bHasM )
1085                 psObject->padfM[i] = padfM[i];
1086         }
1087         if( padfM != NULL && bHasM )
1088             psObject->bMeasureIsUsed = TRUE;
1089     }
1090
1091 /* -------------------------------------------------------------------- */
1092 /*      Compute the extents.                                            */
1093 /* -------------------------------------------------------------------- */
1094     psObject->nVertices = nVertices;
1095     SHPComputeExtents( psObject );
1096
1097     return( psObject );
1098 }
1099
1100 /************************************************************************/
1101 /*                       SHPCreateSimpleObject()                        */
1102 /*                                                                      */
1103 /*      Create a simple (common) shape object.  Destroy with            */
1104 /*      SHPDestroyObject().                                             */
1105 /************************************************************************/
1106
1107 SHPObject SHPAPI_CALL1(*)
1108 SHPCreateSimpleObject( int nSHPType, int nVertices,
1109                        const double * padfX, const double * padfY,
1110                        const double * padfZ )
1111
1112 {
1113     return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL,
1114                              nVertices, padfX, padfY, padfZ, NULL ) );
1115 }
1116                                   
1117 /************************************************************************/
1118 /*                           SHPWriteObject()                           */
1119 /*                                                                      */
1120 /*      Write out the vertices of a new structure.  Note that it is     */
1121 /*      only possible to write vertices at the end of the file.         */
1122 /************************************************************************/
1123
1124 int SHPAPI_CALL
1125 SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject )
1126                       
1127 {
1128     int         nRecordOffset, i, nRecordSize=0;
1129     uchar       *pabyRec;
1130     int32       i32;
1131
1132     psSHP->bUpdated = TRUE;
1133
1134 /* -------------------------------------------------------------------- */
1135 /*      Ensure that shape object matches the type of the file it is     */
1136 /*      being written to.                                               */
1137 /* -------------------------------------------------------------------- */
1138     assert( psObject->nSHPType == psSHP->nShapeType 
1139             || psObject->nSHPType == SHPT_NULL );
1140
1141 /* -------------------------------------------------------------------- */
1142 /*      Ensure that -1 is used for appends.  Either blow an             */
1143 /*      assertion, or if they are disabled, set the shapeid to -1       */
1144 /*      for appends.                                                    */
1145 /* -------------------------------------------------------------------- */
1146     assert( nShapeId == -1 
1147             || (nShapeId >= 0 && nShapeId < psSHP->nRecords) );
1148
1149     if( nShapeId != -1 && nShapeId >= psSHP->nRecords )
1150         nShapeId = -1;
1151
1152 /* -------------------------------------------------------------------- */
1153 /*      Add the new entity to the in memory index.                      */
1154 /* -------------------------------------------------------------------- */
1155     if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords )
1156     {
1157         psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100);
1158
1159         psSHP->panRecOffset = (int *) 
1160             SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords );
1161         psSHP->panRecSize = (int *) 
1162             SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords );
1163     }
1164
1165 /* -------------------------------------------------------------------- */
1166 /*      Initialize record.                                              */
1167 /* -------------------------------------------------------------------- */
1168     pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) 
1169                                + psObject->nParts * 8 + 128);
1170     
1171 /* -------------------------------------------------------------------- */
1172 /*  Extract vertices for a Polygon or Arc.                              */
1173 /* -------------------------------------------------------------------- */
1174     if( psObject->nSHPType == SHPT_POLYGON
1175         || psObject->nSHPType == SHPT_POLYGONZ
1176         || psObject->nSHPType == SHPT_POLYGONM
1177         || psObject->nSHPType == SHPT_ARC 
1178         || psObject->nSHPType == SHPT_ARCZ
1179         || psObject->nSHPType == SHPT_ARCM
1180         || psObject->nSHPType == SHPT_MULTIPATCH )
1181     {
1182         int32           nPoints, nParts;
1183         int             i;
1184
1185         nPoints = psObject->nVertices;
1186         nParts = psObject->nParts;
1187
1188         _SHPSetBounds( pabyRec + 12, psObject );
1189
1190         if( bBigEndian ) SwapWord( 4, &nPoints );
1191         if( bBigEndian ) SwapWord( 4, &nParts );
1192
1193         ByteCopy( &nPoints, pabyRec + 40 + 8, 4 );
1194         ByteCopy( &nParts, pabyRec + 36 + 8, 4 );
1195
1196         nRecordSize = 52;
1197
1198         /*
1199          * Write part start positions.
1200          */
1201         ByteCopy( psObject->panPartStart, pabyRec + 44 + 8,
1202                   4 * psObject->nParts );
1203         for( i = 0; i < psObject->nParts; i++ )
1204         {
1205             if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i );
1206             nRecordSize += 4;
1207         }
1208
1209         /*
1210          * Write multipatch part types if needed.
1211          */
1212         if( psObject->nSHPType == SHPT_MULTIPATCH )
1213         {
1214             memcpy( pabyRec + nRecordSize, psObject->panPartType,
1215                     4*psObject->nParts );
1216             for( i = 0; i < psObject->nParts; i++ )
1217             {
1218                 if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize );
1219                 nRecordSize += 4;
1220             }
1221         }
1222
1223         /*
1224          * Write the (x,y) vertex values.
1225          */
1226         for( i = 0; i < psObject->nVertices; i++ )
1227         {
1228             ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 );
1229             ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 );
1230
1231             if( bBigEndian )
1232                 SwapWord( 8, pabyRec + nRecordSize );
1233             
1234             if( bBigEndian )
1235                 SwapWord( 8, pabyRec + nRecordSize + 8 );
1236
1237             nRecordSize += 2 * 8;
1238         }
1239
1240         /*
1241          * Write the Z coordinates (if any).
1242          */
1243         if( psObject->nSHPType == SHPT_POLYGONZ
1244             || psObject->nSHPType == SHPT_ARCZ
1245             || psObject->nSHPType == SHPT_MULTIPATCH )
1246         {
1247             ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
1248             if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1249             nRecordSize += 8;
1250             
1251             ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
1252             if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1253             nRecordSize += 8;
1254
1255             for( i = 0; i < psObject->nVertices; i++ )
1256             {
1257                 ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
1258                 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1259                 nRecordSize += 8;
1260             }
1261         }
1262
1263         /*
1264          * Write the M values, if any.
1265          */
1266         if( psObject->bMeasureIsUsed
1267             && (psObject->nSHPType == SHPT_POLYGONM
1268             || psObject->nSHPType == SHPT_ARCM
1269 #ifndef DISABLE_MULTIPATCH_MEASURE            
1270             || psObject->nSHPType == SHPT_MULTIPATCH
1271 #endif            
1272             || psObject->nSHPType == SHPT_POLYGONZ
1273             || psObject->nSHPType == SHPT_ARCZ) )
1274         {
1275             ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
1276             if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1277             nRecordSize += 8;
1278             
1279             ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
1280             if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1281             nRecordSize += 8;
1282
1283             for( i = 0; i < psObject->nVertices; i++ )
1284             {
1285                 ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
1286                 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1287                 nRecordSize += 8;
1288             }
1289         }
1290     }
1291
1292 /* -------------------------------------------------------------------- */
1293 /*  Extract vertices for a MultiPoint.                                  */
1294 /* -------------------------------------------------------------------- */
1295     else if( psObject->nSHPType == SHPT_MULTIPOINT
1296              || psObject->nSHPType == SHPT_MULTIPOINTZ
1297              || psObject->nSHPType == SHPT_MULTIPOINTM )
1298     {
1299         int32           nPoints;
1300         int             i;
1301
1302         nPoints = psObject->nVertices;
1303
1304         _SHPSetBounds( pabyRec + 12, psObject );
1305
1306         if( bBigEndian ) SwapWord( 4, &nPoints );
1307         ByteCopy( &nPoints, pabyRec + 44, 4 );
1308         
1309         for( i = 0; i < psObject->nVertices; i++ )
1310         {
1311             ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 );
1312             ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 );
1313
1314             if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 );
1315             if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 );
1316         }
1317
1318         nRecordSize = 48 + 16 * psObject->nVertices;
1319
1320         if( psObject->nSHPType == SHPT_MULTIPOINTZ )
1321         {
1322             ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
1323             if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1324             nRecordSize += 8;
1325
1326             ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
1327             if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1328             nRecordSize += 8;
1329             
1330             for( i = 0; i < psObject->nVertices; i++ )
1331             {
1332                 ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
1333                 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1334                 nRecordSize += 8;
1335             }
1336         }
1337
1338         if( psObject->bMeasureIsUsed
1339             && (psObject->nSHPType == SHPT_MULTIPOINTZ
1340             || psObject->nSHPType == SHPT_MULTIPOINTM) )
1341         {
1342             ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
1343             if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1344             nRecordSize += 8;
1345
1346             ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
1347             if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1348             nRecordSize += 8;
1349             
1350             for( i = 0; i < psObject->nVertices; i++ )
1351             {
1352                 ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
1353                 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1354                 nRecordSize += 8;
1355             }
1356         }
1357     }
1358
1359 /* -------------------------------------------------------------------- */
1360 /*      Write point.                                                    */
1361 /* -------------------------------------------------------------------- */
1362     else if( psObject->nSHPType == SHPT_POINT
1363              || psObject->nSHPType == SHPT_POINTZ
1364              || psObject->nSHPType == SHPT_POINTM )
1365     {
1366         ByteCopy( psObject->padfX, pabyRec + 12, 8 );
1367         ByteCopy( psObject->padfY, pabyRec + 20, 8 );
1368
1369         if( bBigEndian ) SwapWord( 8, pabyRec + 12 );
1370         if( bBigEndian ) SwapWord( 8, pabyRec + 20 );
1371
1372         nRecordSize = 28;
1373         
1374         if( psObject->nSHPType == SHPT_POINTZ )
1375         {
1376             ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 );
1377             if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1378             nRecordSize += 8;
1379         }
1380         
1381         if( psObject->bMeasureIsUsed
1382             && (psObject->nSHPType == SHPT_POINTZ
1383             || psObject->nSHPType == SHPT_POINTM) )
1384         {
1385             ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 );
1386             if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1387             nRecordSize += 8;
1388         }
1389     }
1390
1391 /* -------------------------------------------------------------------- */
1392 /*      Not much to do for null geometries.                             */
1393 /* -------------------------------------------------------------------- */
1394     else if( psObject->nSHPType == SHPT_NULL )
1395     {
1396         nRecordSize = 12;
1397     }
1398
1399     else
1400     {
1401         /* unknown type */
1402         assert( FALSE );
1403     }
1404
1405 /* -------------------------------------------------------------------- */
1406 /*      Establish where we are going to put this record. If we are      */
1407 /*      rewriting and existing record, and it will fit, then put it     */
1408 /*      back where the original came from.  Otherwise write at the end. */
1409 /* -------------------------------------------------------------------- */
1410     if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 )
1411     {
1412         if( nShapeId == -1 )
1413             nShapeId = psSHP->nRecords++;
1414
1415         psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize;
1416         psSHP->panRecSize[nShapeId] = nRecordSize-8;
1417         psSHP->nFileSize += nRecordSize;
1418     }
1419     else
1420     {
1421         nRecordOffset = psSHP->panRecOffset[nShapeId];
1422         psSHP->panRecSize[nShapeId] = nRecordSize-8;
1423     }
1424     
1425 /* -------------------------------------------------------------------- */
1426 /*      Set the shape type, record number, and record size.             */
1427 /* -------------------------------------------------------------------- */
1428     i32 = nShapeId+1;                                   /* record # */
1429     if( !bBigEndian ) SwapWord( 4, &i32 );
1430     ByteCopy( &i32, pabyRec, 4 );
1431
1432     i32 = (nRecordSize-8)/2;                            /* record size */
1433     if( !bBigEndian ) SwapWord( 4, &i32 );
1434     ByteCopy( &i32, pabyRec + 4, 4 );
1435
1436     i32 = psObject->nSHPType;                           /* shape type */
1437     if( bBigEndian ) SwapWord( 4, &i32 );
1438     ByteCopy( &i32, pabyRec + 8, 4 );
1439
1440 /* -------------------------------------------------------------------- */
1441 /*      Write out record.                                               */
1442 /* -------------------------------------------------------------------- */
1443     if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0
1444         || psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 )
1445     {
1446         psSHP->sHooks.Error( "Error in psSHP->sHooks.FSeek() or fwrite() writing object to .shp file." );
1447         free( pabyRec );
1448         return -1;
1449     }
1450     
1451     free( pabyRec );
1452
1453 /* -------------------------------------------------------------------- */
1454 /*      Expand file wide bounds based on this shape.                    */
1455 /* -------------------------------------------------------------------- */
1456     if( psSHP->adBoundsMin[0] == 0.0
1457         && psSHP->adBoundsMax[0] == 0.0
1458         && psSHP->adBoundsMin[1] == 0.0
1459         && psSHP->adBoundsMax[1] == 0.0 )
1460     {
1461         if( psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0 )
1462         {
1463             psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0;
1464             psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0;
1465             psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0;
1466             psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0;
1467         }
1468         else
1469         {
1470             psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0];
1471             psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0];
1472             psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0];
1473             psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0];
1474         }
1475     }
1476
1477     for( i = 0; i < psObject->nVertices; i++ )
1478     {
1479         psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]);
1480         psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]);
1481         psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]);
1482         psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]);
1483         psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]);
1484         psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]);
1485         psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]);
1486         psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]);
1487     }
1488
1489     return( nShapeId  );
1490 }
1491
1492 /************************************************************************/
1493 /*                          SHPReadObject()                             */
1494 /*                                                                      */
1495 /*      Read the vertices, parts, and other non-attribute information   */
1496 /*      for one shape.                                                  */
1497 /************************************************************************/
1498
1499 SHPObject SHPAPI_CALL1(*)
1500 SHPReadObject( SHPHandle psSHP, int hEntity )
1501
1502 {
1503     int                  nEntitySize, nRequiredSize;
1504     SHPObject           *psShape;
1505     char                 pszErrorMsg[128];
1506
1507 /* -------------------------------------------------------------------- */
1508 /*      Validate the record/entity number.                              */
1509 /* -------------------------------------------------------------------- */
1510     if( hEntity < 0 || hEntity >= psSHP->nRecords )
1511         return( NULL );
1512
1513 /* -------------------------------------------------------------------- */
1514 /*      Ensure our record buffer is large enough.                       */
1515 /* -------------------------------------------------------------------- */
1516     nEntitySize = psSHP->panRecSize[hEntity]+8;
1517     if( nEntitySize > psSHP->nBufSize )
1518     {
1519         psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize);
1520         if (psSHP->pabyRec == NULL)
1521         {
1522             char szError[200];
1523
1524             /* Reallocate previous successfull size for following features */
1525             psSHP->pabyRec = malloc(psSHP->nBufSize);
1526
1527             sprintf( szError, 
1528                      "Not enough memory to allocate requested memory (nBufSize=%d). "
1529                      "Probably broken SHP file", psSHP->nBufSize );
1530             psSHP->sHooks.Error( szError );
1531             return NULL;
1532         }
1533
1534         /* Only set new buffer size after successfull alloc */
1535         psSHP->nBufSize = nEntitySize;
1536     }
1537
1538     /* In case we were not able to reallocate the buffer on a previous step */
1539     if (psSHP->pabyRec == NULL)
1540     {
1541         return NULL;
1542     }
1543
1544 /* -------------------------------------------------------------------- */
1545 /*      Read the record.                                                */
1546 /* -------------------------------------------------------------------- */
1547     if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0 
1548         || psSHP->sHooks.FRead( psSHP->pabyRec, nEntitySize, 1, 
1549                   psSHP->fpSHP ) != 1 )
1550     {
1551         /*
1552          * TODO - mloskot: Consider detailed diagnostics of shape file,
1553          * for example to detect if file is truncated.
1554          */
1555
1556         psSHP->sHooks.Error( "Error in fseek() or fread() reading object from .shp file." );
1557         return NULL;
1558     }
1559
1560 /* -------------------------------------------------------------------- */
1561 /*      Allocate and minimally initialize the object.                   */
1562 /* -------------------------------------------------------------------- */
1563     psShape = (SHPObject *) calloc(1,sizeof(SHPObject));
1564     psShape->nShapeId = hEntity;
1565     psShape->bMeasureIsUsed = FALSE;
1566
1567     if ( 8 + 4 > nEntitySize )
1568     {
1569         snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d",
1570                     hEntity, nEntitySize); 
1571         psSHP->sHooks.Error( pszErrorMsg );
1572         SHPDestroyObject(psShape);
1573         return NULL;
1574     }
1575     memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 );
1576
1577     if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) );
1578
1579 /* ==================================================================== */
1580 /*  Extract vertices for a Polygon or Arc.                              */
1581 /* ==================================================================== */
1582     if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC
1583         || psShape->nSHPType == SHPT_POLYGONZ
1584         || psShape->nSHPType == SHPT_POLYGONM
1585         || psShape->nSHPType == SHPT_ARCZ
1586         || psShape->nSHPType == SHPT_ARCM
1587         || psShape->nSHPType == SHPT_MULTIPATCH )
1588     {
1589         int32           nPoints, nParts;
1590         int             i, nOffset;
1591
1592         if ( 40 + 8 + 4 > nEntitySize )
1593         {
1594             snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d",
1595                      hEntity, nEntitySize); 
1596             psSHP->sHooks.Error( pszErrorMsg );
1597             SHPDestroyObject(psShape);
1598             return NULL;
1599         }
1600 /* -------------------------------------------------------------------- */
1601 /*      Get the X/Y bounds.                                             */
1602 /* -------------------------------------------------------------------- */
1603         memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 +  4, 8 );
1604         memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
1605         memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
1606         memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
1607
1608         if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
1609         if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
1610         if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
1611         if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
1612
1613 /* -------------------------------------------------------------------- */
1614 /*      Extract part/point count, and build vertex and part arrays      */
1615 /*      to proper size.                                                 */
1616 /* -------------------------------------------------------------------- */
1617         memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 );
1618         memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 );
1619
1620         if( bBigEndian ) SwapWord( 4, &nPoints );
1621         if( bBigEndian ) SwapWord( 4, &nParts );
1622
1623         if (nPoints < 0 || nParts < 0 ||
1624             nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000)
1625         {
1626             snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.",
1627                         hEntity, nPoints, nParts);
1628             psSHP->sHooks.Error( pszErrorMsg );
1629             SHPDestroyObject(psShape);
1630             return NULL;
1631         }
1632         
1633         /* With the previous checks on nPoints and nParts, */
1634         /* we should not overflow here and after */
1635         /* since 50 M * (16 + 8 + 8) = 1 600 MB */
1636         nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints;
1637         if ( psShape->nSHPType == SHPT_POLYGONZ
1638             || psShape->nSHPType == SHPT_ARCZ
1639             || psShape->nSHPType == SHPT_MULTIPATCH )
1640         {
1641             nRequiredSize += 16 + 8 * nPoints;
1642         }
1643         if( psShape->nSHPType == SHPT_MULTIPATCH )
1644         {
1645             nRequiredSize += 4 * nParts;
1646         }
1647         if (nRequiredSize > nEntitySize)
1648         {
1649             snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d, nEntitySize=%d.",
1650                         hEntity, nPoints, nParts, nEntitySize);
1651             psSHP->sHooks.Error( pszErrorMsg );
1652             SHPDestroyObject(psShape);
1653             return NULL;
1654         }
1655
1656         psShape->nVertices = nPoints;
1657         psShape->padfX = (double *) calloc(nPoints,sizeof(double));
1658         psShape->padfY = (double *) calloc(nPoints,sizeof(double));
1659         psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
1660         psShape->padfM = (double *) calloc(nPoints,sizeof(double));
1661
1662         psShape->nParts = nParts;
1663         psShape->panPartStart = (int *) calloc(nParts,sizeof(int));
1664         psShape->panPartType = (int *) calloc(nParts,sizeof(int));
1665         
1666         if (psShape->padfX == NULL ||
1667             psShape->padfY == NULL ||
1668             psShape->padfZ == NULL ||
1669             psShape->padfM == NULL ||
1670             psShape->panPartStart == NULL ||
1671             psShape->panPartType == NULL)
1672         {
1673             snprintf(pszErrorMsg, 128,
1674                      "Not enough memory to allocate requested memory (nPoints=%d, nParts=%d) for shape %d. "
1675                      "Probably broken SHP file", hEntity, nPoints, nParts );
1676             psSHP->sHooks.Error( pszErrorMsg );
1677             SHPDestroyObject(psShape);
1678             return NULL;
1679         }
1680
1681         for( i = 0; i < nParts; i++ )
1682             psShape->panPartType[i] = SHPP_RING;
1683
1684 /* -------------------------------------------------------------------- */
1685 /*      Copy out the part array from the record.                        */
1686 /* -------------------------------------------------------------------- */
1687         memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts );
1688         for( i = 0; i < nParts; i++ )
1689         {
1690             if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i );
1691
1692             /* We check that the offset is inside the vertex array */
1693             if (psShape->panPartStart[i] < 0 ||
1694                 psShape->panPartStart[i] >= psShape->nVertices)
1695             {
1696                 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d",
1697                          hEntity, i, psShape->panPartStart[i], psShape->nVertices); 
1698                 psSHP->sHooks.Error( pszErrorMsg );
1699                 SHPDestroyObject(psShape);
1700                 return NULL;
1701             }
1702             if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1])
1703             {
1704                 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d",
1705                          hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]); 
1706                 psSHP->sHooks.Error( pszErrorMsg );
1707                 SHPDestroyObject(psShape);
1708                 return NULL;
1709             }
1710         }
1711
1712         nOffset = 44 + 8 + 4*nParts;
1713
1714 /* -------------------------------------------------------------------- */
1715 /*      If this is a multipatch, we will also have parts types.         */
1716 /* -------------------------------------------------------------------- */
1717         if( psShape->nSHPType == SHPT_MULTIPATCH )
1718         {
1719             memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts );
1720             for( i = 0; i < nParts; i++ )
1721             {
1722                 if( bBigEndian ) SwapWord( 4, psShape->panPartType+i );
1723             }
1724
1725             nOffset += 4*nParts;
1726         }
1727         
1728 /* -------------------------------------------------------------------- */
1729 /*      Copy out the vertices from the record.                          */
1730 /* -------------------------------------------------------------------- */
1731         for( i = 0; i < nPoints; i++ )
1732         {
1733             memcpy(psShape->padfX + i,
1734                    psSHP->pabyRec + nOffset + i * 16,
1735                    8 );
1736
1737             memcpy(psShape->padfY + i,
1738                    psSHP->pabyRec + nOffset + i * 16 + 8,
1739                    8 );
1740
1741             if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
1742             if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
1743         }
1744
1745         nOffset += 16*nPoints;
1746         
1747 /* -------------------------------------------------------------------- */
1748 /*      If we have a Z coordinate, collect that now.                    */
1749 /* -------------------------------------------------------------------- */
1750         if( psShape->nSHPType == SHPT_POLYGONZ
1751             || psShape->nSHPType == SHPT_ARCZ
1752             || psShape->nSHPType == SHPT_MULTIPATCH )
1753         {
1754             memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
1755             memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
1756             
1757             if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
1758             if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
1759             
1760             for( i = 0; i < nPoints; i++ )
1761             {
1762                 memcpy( psShape->padfZ + i,
1763                         psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1764                 if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
1765             }
1766
1767             nOffset += 16 + 8*nPoints;
1768         }
1769
1770 /* -------------------------------------------------------------------- */
1771 /*      If we have a M measure value, then read it now.  We assume      */
1772 /*      that the measure can be present for any shape if the size is    */
1773 /*      big enough, but really it will only occur for the Z shapes      */
1774 /*      (options), and the M shapes.                                    */
1775 /* -------------------------------------------------------------------- */
1776         if( nEntitySize >= nOffset + 16 + 8*nPoints )
1777         {
1778             memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
1779             memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
1780             
1781             if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
1782             if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
1783             
1784             for( i = 0; i < nPoints; i++ )
1785             {
1786                 memcpy( psShape->padfM + i,
1787                         psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1788                 if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
1789             }
1790             psShape->bMeasureIsUsed = TRUE;
1791         }
1792     }
1793
1794 /* ==================================================================== */
1795 /*  Extract vertices for a MultiPoint.                                  */
1796 /* ==================================================================== */
1797     else if( psShape->nSHPType == SHPT_MULTIPOINT
1798              || psShape->nSHPType == SHPT_MULTIPOINTM
1799              || psShape->nSHPType == SHPT_MULTIPOINTZ )
1800     {
1801         int32           nPoints;
1802         int             i, nOffset;
1803
1804         if ( 44 + 4 > nEntitySize )
1805         {
1806             snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d",
1807                      hEntity, nEntitySize); 
1808             psSHP->sHooks.Error( pszErrorMsg );
1809             SHPDestroyObject(psShape);
1810             return NULL;
1811         }
1812         memcpy( &nPoints, psSHP->pabyRec + 44, 4 );
1813
1814         if( bBigEndian ) SwapWord( 4, &nPoints );
1815
1816         if (nPoints < 0 || nPoints > 50 * 1000 * 1000)
1817         {
1818             snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d",
1819                      hEntity, nPoints); 
1820             psSHP->sHooks.Error( pszErrorMsg );
1821             SHPDestroyObject(psShape);
1822             return NULL;
1823         }
1824
1825         nRequiredSize = 48 + nPoints * 16;
1826         if( psShape->nSHPType == SHPT_MULTIPOINTZ )
1827         {
1828             nRequiredSize += 16 + nPoints * 8;
1829         }
1830         if (nRequiredSize > nEntitySize)
1831         {
1832             snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d",
1833                      hEntity, nPoints, nEntitySize); 
1834             psSHP->sHooks.Error( pszErrorMsg );
1835             SHPDestroyObject(psShape);
1836             return NULL;
1837         }
1838         
1839         psShape->nVertices = nPoints;
1840         psShape->padfX = (double *) calloc(nPoints,sizeof(double));
1841         psShape->padfY = (double *) calloc(nPoints,sizeof(double));
1842         psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
1843         psShape->padfM = (double *) calloc(nPoints,sizeof(double));
1844
1845         if (psShape->padfX == NULL ||
1846             psShape->padfY == NULL ||
1847             psShape->padfZ == NULL ||
1848             psShape->padfM == NULL)
1849         {
1850             snprintf(pszErrorMsg, 128,
1851                      "Not enough memory to allocate requested memory (nPoints=%d) for shape %d. "
1852                      "Probably broken SHP file", hEntity, nPoints );
1853             psSHP->sHooks.Error( pszErrorMsg );
1854             SHPDestroyObject(psShape);
1855             return NULL;
1856         }
1857
1858         for( i = 0; i < nPoints; i++ )
1859         {
1860             memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 );
1861             memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 );
1862
1863             if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
1864             if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
1865         }
1866
1867         nOffset = 48 + 16*nPoints;
1868         
1869 /* -------------------------------------------------------------------- */
1870 /*      Get the X/Y bounds.                                             */
1871 /* -------------------------------------------------------------------- */
1872         memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 +  4, 8 );
1873         memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
1874         memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
1875         memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
1876
1877         if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
1878         if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
1879         if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
1880         if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
1881
1882 /* -------------------------------------------------------------------- */
1883 /*      If we have a Z coordinate, collect that now.                    */
1884 /* -------------------------------------------------------------------- */
1885         if( psShape->nSHPType == SHPT_MULTIPOINTZ )
1886         {
1887             memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
1888             memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
1889             
1890             if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
1891             if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
1892             
1893             for( i = 0; i < nPoints; i++ )
1894             {
1895                 memcpy( psShape->padfZ + i,
1896                         psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1897                 if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
1898             }
1899
1900             nOffset += 16 + 8*nPoints;
1901         }
1902
1903 /* -------------------------------------------------------------------- */
1904 /*      If we have a M measure value, then read it now.  We assume      */
1905 /*      that the measure can be present for any shape if the size is    */
1906 /*      big enough, but really it will only occur for the Z shapes      */
1907 /*      (options), and the M shapes.                                    */
1908 /* -------------------------------------------------------------------- */
1909         if( nEntitySize >= nOffset + 16 + 8*nPoints )
1910         {
1911             memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
1912             memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
1913             
1914             if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
1915             if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
1916             
1917             for( i = 0; i < nPoints; i++ )
1918             {
1919                 memcpy( psShape->padfM + i,
1920                         psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1921                 if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
1922             }
1923             psShape->bMeasureIsUsed = TRUE;
1924         }
1925     }
1926
1927 /* ==================================================================== */
1928 /*      Extract vertices for a point.                                   */
1929 /* ==================================================================== */
1930     else if( psShape->nSHPType == SHPT_POINT
1931              || psShape->nSHPType == SHPT_POINTM
1932              || psShape->nSHPType == SHPT_POINTZ )
1933     {
1934         int     nOffset;
1935         
1936         psShape->nVertices = 1;
1937         psShape->padfX = (double *) calloc(1,sizeof(double));
1938         psShape->padfY = (double *) calloc(1,sizeof(double));
1939         psShape->padfZ = (double *) calloc(1,sizeof(double));
1940         psShape->padfM = (double *) calloc(1,sizeof(double));
1941
1942         if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize)
1943         {
1944             snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d",
1945                      hEntity, nEntitySize); 
1946             psSHP->sHooks.Error( pszErrorMsg );
1947             SHPDestroyObject(psShape);
1948             return NULL;
1949         }
1950         memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 );
1951         memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 );
1952
1953         if( bBigEndian ) SwapWord( 8, psShape->padfX );
1954         if( bBigEndian ) SwapWord( 8, psShape->padfY );
1955
1956         nOffset = 20 + 8;
1957         
1958 /* -------------------------------------------------------------------- */
1959 /*      If we have a Z coordinate, collect that now.                    */
1960 /* -------------------------------------------------------------------- */
1961         if( psShape->nSHPType == SHPT_POINTZ )
1962         {
1963             memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 );
1964         
1965             if( bBigEndian ) SwapWord( 8, psShape->padfZ );
1966             
1967             nOffset += 8;
1968         }
1969
1970 /* -------------------------------------------------------------------- */
1971 /*      If we have a M measure value, then read it now.  We assume      */
1972 /*      that the measure can be present for any shape if the size is    */
1973 /*      big enough, but really it will only occur for the Z shapes      */
1974 /*      (options), and the M shapes.                                    */
1975 /* -------------------------------------------------------------------- */
1976         if( nEntitySize >= nOffset + 8 )
1977         {
1978             memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 );
1979         
1980             if( bBigEndian ) SwapWord( 8, psShape->padfM );
1981             psShape->bMeasureIsUsed = TRUE;
1982         }
1983
1984 /* -------------------------------------------------------------------- */
1985 /*      Since no extents are supplied in the record, we will apply      */
1986 /*      them from the single vertex.                                    */
1987 /* -------------------------------------------------------------------- */
1988         psShape->dfXMin = psShape->dfXMax = psShape->padfX[0];
1989         psShape->dfYMin = psShape->dfYMax = psShape->padfY[0];
1990         psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0];
1991         psShape->dfMMin = psShape->dfMMax = psShape->padfM[0];
1992     }
1993
1994     return( psShape );
1995 }
1996
1997 /************************************************************************/
1998 /*                            SHPTypeName()                             */
1999 /************************************************************************/
2000
2001 const char SHPAPI_CALL1(*)
2002 SHPTypeName( int nSHPType )
2003
2004 {
2005     switch( nSHPType )
2006     {
2007       case SHPT_NULL:
2008         return "NullShape";
2009
2010       case SHPT_POINT:
2011         return "Point";
2012
2013       case SHPT_ARC:
2014         return "Arc";
2015
2016       case SHPT_POLYGON:
2017         return "Polygon";
2018
2019       case SHPT_MULTIPOINT:
2020         return "MultiPoint";
2021         
2022       case SHPT_POINTZ:
2023         return "PointZ";
2024
2025       case SHPT_ARCZ:
2026         return "ArcZ";
2027
2028       case SHPT_POLYGONZ:
2029         return "PolygonZ";
2030
2031       case SHPT_MULTIPOINTZ:
2032         return "MultiPointZ";
2033         
2034       case SHPT_POINTM:
2035         return "PointM";
2036
2037       case SHPT_ARCM:
2038         return "ArcM";
2039
2040       case SHPT_POLYGONM:
2041         return "PolygonM";
2042
2043       case SHPT_MULTIPOINTM:
2044         return "MultiPointM";
2045
2046       case SHPT_MULTIPATCH:
2047         return "MultiPatch";
2048
2049       default:
2050         return "UnknownShapeType";
2051     }
2052 }
2053
2054 /************************************************************************/
2055 /*                          SHPPartTypeName()                           */
2056 /************************************************************************/
2057
2058 const char SHPAPI_CALL1(*)
2059 SHPPartTypeName( int nPartType )
2060
2061 {
2062     switch( nPartType )
2063     {
2064       case SHPP_TRISTRIP:
2065         return "TriangleStrip";
2066         
2067       case SHPP_TRIFAN:
2068         return "TriangleFan";
2069
2070       case SHPP_OUTERRING:
2071         return "OuterRing";
2072
2073       case SHPP_INNERRING:
2074         return "InnerRing";
2075
2076       case SHPP_FIRSTRING:
2077         return "FirstRing";
2078
2079       case SHPP_RING:
2080         return "Ring";
2081
2082       default:
2083         return "UnknownPartType";
2084     }
2085 }
2086
2087 /************************************************************************/
2088 /*                          SHPDestroyObject()                          */
2089 /************************************************************************/
2090
2091 void SHPAPI_CALL
2092 SHPDestroyObject( SHPObject * psShape )
2093
2094 {
2095     if( psShape == NULL )
2096         return;
2097     
2098     if( psShape->padfX != NULL )
2099         free( psShape->padfX );
2100     if( psShape->padfY != NULL )
2101         free( psShape->padfY );
2102     if( psShape->padfZ != NULL )
2103         free( psShape->padfZ );
2104     if( psShape->padfM != NULL )
2105         free( psShape->padfM );
2106
2107     if( psShape->panPartStart != NULL )
2108         free( psShape->panPartStart );
2109     if( psShape->panPartType != NULL )
2110         free( psShape->panPartType );
2111
2112     free( psShape );
2113 }
2114
2115 /************************************************************************/
2116 /*                          SHPRewindObject()                           */
2117 /*                                                                      */
2118 /*      Reset the winding of polygon objects to adhere to the           */
2119 /*      specification.                                                  */
2120 /************************************************************************/
2121
2122 int SHPAPI_CALL
2123 SHPRewindObject( SHPHandle hSHP, SHPObject * psObject )
2124
2125 {
2126     int  iOpRing, bAltered = 0;
2127
2128 /* -------------------------------------------------------------------- */
2129 /*      Do nothing if this is not a polygon object.                     */
2130 /* -------------------------------------------------------------------- */
2131     if( psObject->nSHPType != SHPT_POLYGON
2132         && psObject->nSHPType != SHPT_POLYGONZ
2133         && psObject->nSHPType != SHPT_POLYGONM )
2134         return 0;
2135
2136     if( psObject->nVertices == 0 || psObject->nParts == 0 )
2137         return 0;
2138
2139 /* -------------------------------------------------------------------- */
2140 /*      Process each of the rings.                                      */
2141 /* -------------------------------------------------------------------- */
2142     for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ )
2143     {
2144         int      bInner, iVert, nVertCount, nVertStart, iCheckRing;
2145         double   dfSum, dfTestX, dfTestY;
2146
2147 /* -------------------------------------------------------------------- */
2148 /*      Determine if this ring is an inner ring or an outer ring        */
2149 /*      relative to all the other rings.  For now we assume the         */
2150 /*      first ring is outer and all others are inner, but eventually    */
2151 /*      we need to fix this to handle multiple island polygons and      */
2152 /*      unordered sets of rings.                                        */
2153 /*                                                                      */
2154 /* -------------------------------------------------------------------- */
2155
2156         /* Use point in the middle of segment to avoid testing
2157          * common points of rings.
2158          */
2159         dfTestX = ( psObject->padfX[psObject->panPartStart[iOpRing]]
2160                     + psObject->padfX[psObject->panPartStart[iOpRing] + 1] ) / 2;
2161         dfTestY = ( psObject->padfY[psObject->panPartStart[iOpRing]]
2162                     + psObject->padfY[psObject->panPartStart[iOpRing] + 1] ) / 2;
2163
2164         bInner = FALSE;
2165         for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ )
2166         {
2167             int iEdge;
2168
2169             if( iCheckRing == iOpRing )
2170                 continue;
2171             
2172             nVertStart = psObject->panPartStart[iCheckRing];
2173
2174             if( iCheckRing == psObject->nParts-1 )
2175                 nVertCount = psObject->nVertices 
2176                     - psObject->panPartStart[iCheckRing];
2177             else
2178                 nVertCount = psObject->panPartStart[iCheckRing+1] 
2179                     - psObject->panPartStart[iCheckRing];
2180
2181             for( iEdge = 0; iEdge < nVertCount; iEdge++ )
2182             {
2183                 int iNext;
2184
2185                 if( iEdge < nVertCount-1 )
2186                     iNext = iEdge+1;
2187                 else
2188                     iNext = 0;
2189
2190                 /* Rule #1:
2191                  * Test whether the edge 'straddles' the horizontal ray from the test point (dfTestY,dfTestY)
2192                  * The rule #1 also excludes edges collinear with the ray.
2193                  */
2194                 if ( ( psObject->padfY[iEdge+nVertStart] < dfTestY
2195                        && dfTestY <= psObject->padfY[iNext+nVertStart] )
2196                     || ( psObject->padfY[iNext+nVertStart] < dfTestY
2197                          && dfTestY <= psObject->padfY[iEdge+nVertStart] ) )
2198                 {
2199                     /* Rule #2:
2200                     * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY)
2201                     */
2202                     double const intersect = 
2203                         ( psObject->padfX[iEdge+nVertStart]
2204                           + ( dfTestY - psObject->padfY[iEdge+nVertStart] ) 
2205                           / ( psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart] )
2206                           * ( psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart] ) );
2207
2208                     if (intersect  < dfTestX)
2209                     {
2210                         bInner = !bInner;
2211                     }
2212                 }    
2213             }
2214         } /* for iCheckRing */
2215
2216 /* -------------------------------------------------------------------- */
2217 /*      Determine the current order of this ring so we will know if     */
2218 /*      it has to be reversed.                                          */
2219 /* -------------------------------------------------------------------- */
2220         nVertStart = psObject->panPartStart[iOpRing];
2221
2222         if( iOpRing == psObject->nParts-1 )
2223             nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing];
2224         else
2225             nVertCount = psObject->panPartStart[iOpRing+1] 
2226                 - psObject->panPartStart[iOpRing];
2227
2228         dfSum = 0.0;
2229         for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ )
2230         {
2231             dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1]
2232                 - psObject->padfY[iVert] * psObject->padfX[iVert+1];
2233         }
2234
2235         dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart]
2236                - psObject->padfY[iVert] * psObject->padfX[nVertStart];
2237
2238 /* -------------------------------------------------------------------- */
2239 /*      Reverse if necessary.                                           */
2240 /* -------------------------------------------------------------------- */
2241         if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) )
2242         {
2243             int   i;
2244
2245             bAltered++;
2246             for( i = 0; i < nVertCount/2; i++ )
2247             {
2248                 double dfSaved;
2249
2250                 /* Swap X */
2251                 dfSaved = psObject->padfX[nVertStart+i];
2252                 psObject->padfX[nVertStart+i] = 
2253                     psObject->padfX[nVertStart+nVertCount-i-1];
2254                 psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved;
2255
2256                 /* Swap Y */
2257                 dfSaved = psObject->padfY[nVertStart+i];
2258                 psObject->padfY[nVertStart+i] = 
2259                     psObject->padfY[nVertStart+nVertCount-i-1];
2260                 psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved;
2261
2262                 /* Swap Z */
2263                 if( psObject->padfZ )
2264                 {
2265                     dfSaved = psObject->padfZ[nVertStart+i];
2266                     psObject->padfZ[nVertStart+i] = 
2267                         psObject->padfZ[nVertStart+nVertCount-i-1];
2268                     psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved;
2269                 }
2270
2271                 /* Swap M */
2272                 if( psObject->padfM )
2273                 {
2274                     dfSaved = psObject->padfM[nVertStart+i];
2275                     psObject->padfM[nVertStart+i] = 
2276                         psObject->padfM[nVertStart+nVertCount-i-1];
2277                     psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved;
2278                 }
2279             }
2280         }
2281     }
2282
2283     return bAltered;
2284 }