GRASS Programmer's Manual  6.4.3(2013)-r
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
shpopen.c
Go to the documentation of this file.
1 /******************************************************************************
2  * $Id: shpopen.c 32581 2008-08-06 19:30:45Z neteler $
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 <grass/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 32581 2008-08-06 19:30:45Z neteler $")
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 
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 
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 
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
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 
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 
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
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 
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 
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
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 
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
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
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 }