ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
shpopen.c
Go to the documentation of this file.
1 /******************************************************************************
2  * $Id$
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  * Copyright (c) 2011-2013, Even Rouault <even dot rouault at mines-paris dot org>
11  *
12  * This software is available under the following "MIT Style" license,
13  * or at the option of the licensee under the LGPL (see COPYING). This
14  * option is discussed in more detail in shapelib.html.
15  *
16  * --
17  *
18  * Permission is hereby granted, free of charge, to any person obtaining a
19  * copy of this software and associated documentation files (the "Software"),
20  * to deal in the Software without restriction, including without limitation
21  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
22  * and/or sell copies of the Software, and to permit persons to whom the
23  * Software is furnished to do so, subject to the following conditions:
24  *
25  * The above copyright notice and this permission notice shall be included
26  * in all copies or substantial portions of the Software.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
29  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
31  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
33  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
34  * DEALINGS IN THE SOFTWARE.
35  ******************************************************************************
36  *
37  * $Log$
38  * Revision 1.78 2019-02-28 15:55:23 erouault
39  * * shpopen.c: resync with GDAL internal shapelib to avoid being dependent
40  * on correctness of file size field in .shp. Fixes
41  * https://lists.osgeo.org/pipermail/gdal-dev/2018-October/049218.html
42  *
43  * Revision 1.77 2018-08-16 15:39:07 erouault
44  * * shpopen.c, dbfopen.c, shptree.c, sbnsearch.c: resyc with GDAL
45  * internal shapelib. Mostly to allow building those files as C++
46  * without warning. Also add FTDate entry in DBFFieldType
47  * (see https://github.com/OSGeo/gdal/pull/308). And some other
48  * code cleanups
49  *
50  * Revision 1.76 2017-09-10 10:11:36 erouault
51  * * shpopen.c: resync with GDAL copy. Make sure to zero terminate all
52  * error messages. And fix regression regarding re-writing the last shape
53  * of a file (https://trac.osgeo.org/gdal/ticket/7031)
54  *
55  * Revision 1.75 2016-12-05 12:44:05 erouault
56  * * Major overhaul of Makefile build system to use autoconf/automake.
57  *
58  * * Warning fixes in contrib/
59  *
60  * Revision 1.74 2016-12-04 15:30:15 erouault
61  * * shpopen.c, dbfopen.c, shptree.c, shapefil.h: resync with
62  * GDAL Shapefile driver. Mostly cleanups. SHPObject and DBFInfo
63  * structures extended with new members. New functions:
64  * DBFSetLastModifiedDate, SHPOpenLLEx, SHPRestoreSHX,
65  * SHPSetFastModeReadObject
66  *
67  * * sbnsearch.c: new file to implement original ESRI .sbn spatial
68  * index reading. (no write support). New functions:
69  * SBNOpenDiskTree, SBNCloseDiskTree, SBNSearchDiskTree,
70  * SBNSearchDiskTreeInteger, SBNSearchFreeIds
71  *
72  * * Makefile, makefile.vc, CMakeLists.txt, shapelib.def: updates
73  * with new file and symbols.
74  *
75  * * commit: helper script to cvs commit
76  *
77  * Revision 1.73 2012-01-24 22:33:01 fwarmerdam
78  * fix memory leak on failure to open .shp (gdal #4410)
79  *
80  * Revision 1.72 2011-12-11 22:45:28 fwarmerdam
81  * fix failure return from SHPOpenLL.
82  *
83  * Revision 1.71 2011-09-15 03:33:58 fwarmerdam
84  * fix missing cast (#2344)
85  *
86  * Revision 1.70 2011-07-24 05:59:25 fwarmerdam
87  * minimize use of CPLError in favor of SAHooks.Error()
88  *
89  * Revision 1.69 2011-07-24 03:24:22 fwarmerdam
90  * fix memory leaks in error cases creating shapefiles (#2061)
91  *
92  * Revision 1.68 2010-08-27 23:42:52 fwarmerdam
93  * add SHPAPI_CALL attribute in code
94  *
95  * Revision 1.67 2010-07-01 08:15:48 fwarmerdam
96  * do not error out on an object with zero vertices
97  *
98  * Revision 1.66 2010-07-01 07:58:57 fwarmerdam
99  * minor cleanup of error handling
100  *
101  * Revision 1.65 2010-07-01 07:27:13 fwarmerdam
102  * white space formatting adjustments
103  *
104  * Revision 1.64 2010-01-28 11:34:34 fwarmerdam
105  * handle the shape file length limits more gracefully (#3236)
106  *
107  * Revision 1.63 2010-01-28 04:04:40 fwarmerdam
108  * improve numerical accuracy of SHPRewind() algs (gdal #3363)
109  *
110  * Revision 1.62 2010-01-17 05:34:13 fwarmerdam
111  * Remove asserts on x/y being null (#2148).
112  *
113  * Revision 1.61 2010-01-16 05:07:42 fwarmerdam
114  * allow 0/nulls in shpcreateobject (#2148)
115  *
116  * Revision 1.60 2009-09-17 20:50:02 bram
117  * on Win32, define snprintf as alias to _snprintf
118  *
119  * Revision 1.59 2008-03-14 05:25:31 fwarmerdam
120  * Correct crash on buggy geometries (gdal #2218)
121  *
122  * Revision 1.58 2008/01/08 23:28:26 bram
123  * on line 2095, use a float instead of a double to avoid a compiler warning
124  *
125  * Revision 1.57 2007/12/06 07:00:25 fwarmerdam
126  * dbfopen now using SAHooks for fileio
127  *
128  * Revision 1.56 2007/12/04 20:37:56 fwarmerdam
129  * preliminary implementation of hooks api for io and errors
130  *
131  * Revision 1.55 2007/11/21 22:39:56 fwarmerdam
132  * close shx file in readonly mode (GDAL #1956)
133  *
134  * Revision 1.54 2007/11/15 00:12:47 mloskot
135  * Backported recent changes from GDAL (Ticket #1415) to Shapelib.
136  *
137  * Revision 1.53 2007/11/14 22:31:08 fwarmerdam
138  * checks after mallocs to detect for corrupted/voluntary broken shapefiles.
139  * http://trac.osgeo.org/gdal/ticket/1991
140  *
141  * Revision 1.52 2007/06/21 15:58:33 fwarmerdam
142  * fix for SHPRewindObject when rings touch at one vertex (gdal #976)
143  *
144  * Revision 1.51 2006/09/04 15:24:01 fwarmerdam
145  * Fixed up log message for 1.49.
146  *
147  * Revision 1.50 2006/09/04 15:21:39 fwarmerdam
148  * fix of last fix
149  *
150  * Revision 1.49 2006/09/04 15:21:00 fwarmerdam
151  * MLoskot: Added stronger test of Shapefile reading failures, e.g. truncated
152  * files. The problem was discovered by Tim Sutton and reported here
153  * https://svn.qgis.org/trac/ticket/200
154  *
155  * Revision 1.48 2006/01/26 15:07:32 fwarmerdam
156  * add bMeasureIsUsed flag from Craig Bruce: Bug 1249
157  *
158  * Revision 1.47 2006/01/04 20:07:23 fwarmerdam
159  * In SHPWriteObject() make sure that the record length is updated
160  * when rewriting an existing record.
161  *
162  * Revision 1.46 2005/02/11 17:17:46 fwarmerdam
163  * added panPartStart[0] validation
164  *
165  * Revision 1.45 2004/09/26 20:09:48 fwarmerdam
166  * const correctness changes
167  *
168  * Revision 1.44 2003/12/29 00:18:39 fwarmerdam
169  * added error checking for failed IO and optional CPL error reporting
170  *
171  * Revision 1.43 2003/12/01 16:20:08 warmerda
172  * be careful of zero vertex shapes
173  *
174  * Revision 1.42 2003/12/01 14:58:27 warmerda
175  * added degenerate object check in SHPRewindObject()
176  *
177  * Revision 1.41 2003/07/08 15:22:43 warmerda
178  * avoid warning
179  *
180  * Revision 1.40 2003/04/21 18:30:37 warmerda
181  * added header write/update public methods
182  *
183  * Revision 1.39 2002/08/26 06:46:56 warmerda
184  * avoid c++ comments
185  *
186  * Revision 1.38 2002/05/07 16:43:39 warmerda
187  * Removed debugging printf()
188  *
189  * Revision 1.37 2002/04/10 17:35:22 warmerda
190  * fixed bug in ring reversal code
191  *
192  * Revision 1.36 2002/04/10 16:59:54 warmerda
193  * added SHPRewindObject
194  *
195  * Revision 1.35 2001/12/07 15:10:44 warmerda
196  * fix if .shx fails to open
197  *
198  * Revision 1.34 2001/11/01 16:29:55 warmerda
199  * move pabyRec into SHPInfo for thread safety
200  *
201  * Revision 1.33 2001/07/03 12:18:15 warmerda
202  * Improved cleanup if SHX not found, provided by Riccardo Cohen.
203  *
204  * Revision 1.32 2001/06/22 01:58:07 warmerda
205  * be more careful about establishing initial bounds in face of NULL shapes
206  *
207  * Revision 1.31 2001/05/31 19:35:29 warmerda
208  * added support for writing null shapes
209  *
210  * Revision 1.30 2001/05/28 12:46:29 warmerda
211  * Add some checking on reasonableness of record count when opening.
212  *
213  * Revision 1.29 2001/05/23 13:36:52 warmerda
214  * added use of SHPAPI_CALL
215  *
216  * Revision 1.28 2001/02/06 22:25:06 warmerda
217  * fixed memory leaks when SHPOpen() fails
218  *
219  * Revision 1.27 2000/07/18 15:21:33 warmerda
220  * added better enforcement of -1 for append in SHPWriteObject
221  *
222  * Revision 1.26 2000/02/16 16:03:51 warmerda
223  * added null shape support
224  *
225  * Revision 1.25 1999/12/15 13:47:07 warmerda
226  * Fixed record size settings in .shp file (was 4 words too long)
227  * Added stdlib.h.
228  *
229  * Revision 1.24 1999/11/05 14:12:04 warmerda
230  * updated license terms
231  *
232  * Revision 1.23 1999/07/27 00:53:46 warmerda
233  * added support for rewriting shapes
234  *
235  * Revision 1.22 1999/06/11 19:19:11 warmerda
236  * Cleanup pabyRec static buffer on SHPClose().
237  *
238  * Revision 1.21 1999/06/02 14:57:56 kshih
239  * Remove unused variables
240  *
241  * Revision 1.20 1999/04/19 21:04:17 warmerda
242  * Fixed syntax error.
243  *
244  * Revision 1.19 1999/04/19 21:01:57 warmerda
245  * Force access string to binary in SHPOpen().
246  *
247  * Revision 1.18 1999/04/01 18:48:07 warmerda
248  * Try upper case extensions if lower case doesn't work.
249  *
250  * Revision 1.17 1998/12/31 15:29:39 warmerda
251  * Disable writing measure values to multipatch objects if
252  * DISABLE_MULTIPATCH_MEASURE is defined.
253  *
254  * Revision 1.16 1998/12/16 05:14:33 warmerda
255  * Added support to write MULTIPATCH. Fixed reading Z coordinate of
256  * MULTIPATCH. Fixed record size written for all feature types.
257  *
258  * Revision 1.15 1998/12/03 16:35:29 warmerda
259  * r+b is proper binary access string, not rb+.
260  *
261  * Revision 1.14 1998/12/03 15:47:56 warmerda
262  * Fixed setting of nVertices in SHPCreateObject().
263  *
264  * Revision 1.13 1998/12/03 15:33:54 warmerda
265  * Made SHPCalculateExtents() separately callable.
266  *
267  * Revision 1.12 1998/11/11 20:01:50 warmerda
268  * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines.
269  *
270  * Revision 1.11 1998/11/09 20:56:44 warmerda
271  * Fixed up handling of file wide bounds.
272  *
273  * Revision 1.10 1998/11/09 20:18:51 warmerda
274  * Converted to support 3D shapefiles, and use of SHPObject.
275  *
276  * Revision 1.9 1998/02/24 15:09:05 warmerda
277  * Fixed memory leak.
278  *
279  * Revision 1.8 1997/12/04 15:40:29 warmerda
280  * Fixed byte swapping of record number, and record length fields in the
281  * .shp file.
282  *
283  * Revision 1.7 1995/10/21 03:15:58 warmerda
284  * Added support for binary file access, the magic cookie 9997
285  * and tried to improve the int32 selection logic for 16bit systems.
286  *
287  * Revision 1.6 1995/09/04 04:19:41 warmerda
288  * Added fix for file bounds.
289  *
290  * Revision 1.5 1995/08/25 15:16:44 warmerda
291  * Fixed a couple of problems with big endian systems ... one with bounds
292  * and the other with multipart polygons.
293  *
294  * Revision 1.4 1995/08/24 18:10:17 warmerda
295  * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc()
296  * functions (such as on the Sun).
297  *
298  * Revision 1.3 1995/08/23 02:23:15 warmerda
299  * Added support for reading bounds, and fixed up problems in setting the
300  * file wide bounds.
301  *
302  * Revision 1.2 1995/08/04 03:16:57 warmerda
303  * Added header.
304  *
305  */
306 
307 #include "shapefil.h"
308 
309 #include <math.h>
310 #include <limits.h>
311 #include <assert.h>
312 #include <stdlib.h>
313 #include <string.h>
314 #include <stdio.h>
315 #include <errno.h>
316 
317 SHP_CVSID("$Id$")
318 
319 typedef unsigned char uchar;
320 
321 #if UINT_MAX == 65535
322 typedef unsigned long int32;
323 #else
324 typedef unsigned int int32;
325 #endif
326 
327 #ifndef FALSE
328 # define FALSE 0
329 # define TRUE 1
330 #endif
331 
332 #define ByteCopy( a, b, c ) memcpy( b, a, c )
333 #ifndef MAX
334 # define MIN(a,b) ((a<b) ? a : b)
335 # define MAX(a,b) ((a>b) ? a : b)
336 #endif
337 
338 #ifndef USE_CPL
339 #if defined(_MSC_VER)
340 # if _MSC_VER < 1900
341 # define snprintf _snprintf
342 # endif
343 #elif defined(WIN32) || defined(_WIN32)
344 # ifndef snprintf
345 # define snprintf _snprintf
346 # endif
347 #endif
348 #endif
349 
350 #ifndef CPL_UNUSED
351 #if defined(__GNUC__) && __GNUC__ >= 4
352 # define CPL_UNUSED __attribute((__unused__))
353 #else
354 # define CPL_UNUSED
355 #endif
356 #endif
357 
358 #if defined(CPL_LSB)
359 #define bBigEndian FALSE
360 #elif defined(CPL_MSB)
361 #define bBigEndian TRUE
362 #else
363 static int bBigEndian;
364 #endif
365 
366 #ifdef __cplusplus
367 #define STATIC_CAST(type,x) static_cast<type>(x)
368 #define SHPLIB_NULLPTR nullptr
369 #else
370 #define STATIC_CAST(type,x) ((type)(x))
371 #define SHPLIB_NULLPTR NULL
372 #endif
373 
374 /************************************************************************/
375 /* SwapWord() */
376 /* */
377 /* Swap a 2, 4 or 8 byte word. */
378 /************************************************************************/
379 
380 static void SwapWord( int length, void * wordP )
381 
382 {
383  int i;
384  uchar temp;
385 
386  for( i=0; i < length/2; i++ )
387  {
388  temp = STATIC_CAST(uchar*, wordP)[i];
389  STATIC_CAST(uchar*, wordP)[i] = STATIC_CAST(uchar*, wordP)[length-i-1];
390  STATIC_CAST(uchar*, wordP)[length-i-1] = temp;
391  }
392 }
393 
394 /************************************************************************/
395 /* SfRealloc() */
396 /* */
397 /* A realloc cover function that will access a NULL pointer as */
398 /* a valid input. */
399 /************************************************************************/
400 
401 static void * SfRealloc( void * pMem, int nNewSize )
402 
403 {
404  if( pMem == SHPLIB_NULLPTR )
405  return malloc(nNewSize);
406  else
407  return realloc(pMem,nNewSize);
408 }
409 
410 /************************************************************************/
411 /* SHPWriteHeader() */
412 /* */
413 /* Write out a header for the .shp and .shx files as well as the */
414 /* contents of the index (.shx) file. */
415 /************************************************************************/
416 
418 
419 {
420  uchar abyHeader[100] = { 0 };
421  int i;
422  int32 i32;
423  double dValue;
424  int32 *panSHX;
425 
426  if (psSHP->fpSHX == SHPLIB_NULLPTR)
427  {
428  psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed");
429  return;
430  }
431 
432 /* -------------------------------------------------------------------- */
433 /* Prepare header block for .shp file. */
434 /* -------------------------------------------------------------------- */
435 
436  abyHeader[2] = 0x27; /* magic cookie */
437  abyHeader[3] = 0x0a;
438 
439  i32 = psSHP->nFileSize/2; /* file size */
440  ByteCopy( &i32, abyHeader+24, 4 );
441  if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
442 
443  i32 = 1000; /* version */
444  ByteCopy( &i32, abyHeader+28, 4 );
445  if( bBigEndian ) SwapWord( 4, abyHeader+28 );
446 
447  i32 = psSHP->nShapeType; /* shape type */
448  ByteCopy( &i32, abyHeader+32, 4 );
449  if( bBigEndian ) SwapWord( 4, abyHeader+32 );
450 
451  dValue = psSHP->adBoundsMin[0]; /* set bounds */
452  ByteCopy( &dValue, abyHeader+36, 8 );
453  if( bBigEndian ) SwapWord( 8, abyHeader+36 );
454 
455  dValue = psSHP->adBoundsMin[1];
456  ByteCopy( &dValue, abyHeader+44, 8 );
457  if( bBigEndian ) SwapWord( 8, abyHeader+44 );
458 
459  dValue = psSHP->adBoundsMax[0];
460  ByteCopy( &dValue, abyHeader+52, 8 );
461  if( bBigEndian ) SwapWord( 8, abyHeader+52 );
462 
463  dValue = psSHP->adBoundsMax[1];
464  ByteCopy( &dValue, abyHeader+60, 8 );
465  if( bBigEndian ) SwapWord( 8, abyHeader+60 );
466 
467  dValue = psSHP->adBoundsMin[2]; /* z */
468  ByteCopy( &dValue, abyHeader+68, 8 );
469  if( bBigEndian ) SwapWord( 8, abyHeader+68 );
470 
471  dValue = psSHP->adBoundsMax[2];
472  ByteCopy( &dValue, abyHeader+76, 8 );
473  if( bBigEndian ) SwapWord( 8, abyHeader+76 );
474 
475  dValue = psSHP->adBoundsMin[3]; /* m */
476  ByteCopy( &dValue, abyHeader+84, 8 );
477  if( bBigEndian ) SwapWord( 8, abyHeader+84 );
478 
479  dValue = psSHP->adBoundsMax[3];
480  ByteCopy( &dValue, abyHeader+92, 8 );
481  if( bBigEndian ) SwapWord( 8, abyHeader+92 );
482 
483 /* -------------------------------------------------------------------- */
484 /* Write .shp file header. */
485 /* -------------------------------------------------------------------- */
486  if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0
487  || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHP ) != 1 )
488  {
489  char szErrorMsg[200];
490 
491  snprintf( szErrorMsg, sizeof(szErrorMsg),
492  "Failure writing .shp header: %s", strerror(errno) );
493  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
494  psSHP->sHooks.Error( szErrorMsg );
495  return;
496  }
497 
498 /* -------------------------------------------------------------------- */
499 /* Prepare, and write .shx file header. */
500 /* -------------------------------------------------------------------- */
501  i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */
502  ByteCopy( &i32, abyHeader+24, 4 );
503  if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
504 
505  if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0
506  || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 )
507  {
508  char szErrorMsg[200];
509 
510  snprintf( szErrorMsg, sizeof(szErrorMsg),
511  "Failure writing .shx header: %s", strerror(errno) );
512  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
513  psSHP->sHooks.Error( szErrorMsg );
514 
515  return;
516  }
517 
518 /* -------------------------------------------------------------------- */
519 /* Write out the .shx contents. */
520 /* -------------------------------------------------------------------- */
521  panSHX = STATIC_CAST(int32 *, malloc(sizeof(int32) * 2 * psSHP->nRecords));
522  if( panSHX == SHPLIB_NULLPTR )
523  {
524  psSHP->sHooks.Error( "Failure allocatin panSHX" );
525  return;
526  }
527 
528  for( i = 0; i < psSHP->nRecords; i++ )
529  {
530  panSHX[i*2 ] = psSHP->panRecOffset[i]/2;
531  panSHX[i*2+1] = psSHP->panRecSize[i]/2;
532  if( !bBigEndian ) SwapWord( 4, panSHX+i*2 );
533  if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 );
534  }
535 
536  if( STATIC_CAST(int, psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX ))
537  != psSHP->nRecords )
538  {
539  char szErrorMsg[200];
540 
541  snprintf( szErrorMsg, sizeof(szErrorMsg),
542  "Failure writing .shx contents: %s", strerror(errno) );
543  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
544  psSHP->sHooks.Error( szErrorMsg );
545  }
546 
547  free( panSHX );
548 
549 /* -------------------------------------------------------------------- */
550 /* Flush to disk. */
551 /* -------------------------------------------------------------------- */
552  psSHP->sHooks.FFlush( psSHP->fpSHP );
553  psSHP->sHooks.FFlush( psSHP->fpSHX );
554 }
555 
556 /************************************************************************/
557 /* SHPOpen() */
558 /************************************************************************/
559 
561 SHPOpen( const char * pszLayer, const char * pszAccess )
562 
563 {
564  SAHooks sHooks;
565 
566  SASetupDefaultHooks( &sHooks );
567 
568  return SHPOpenLL( pszLayer, pszAccess, &sHooks );
569 }
570 
571 /************************************************************************/
572 /* SHPGetLenWithoutExtension() */
573 /************************************************************************/
574 
575 static int SHPGetLenWithoutExtension(const char* pszBasename)
576 {
577  int i;
578  int nLen = STATIC_CAST(int, strlen(pszBasename));
579  for( i = nLen-1;
580  i > 0 && pszBasename[i] != '/' && pszBasename[i] != '\\';
581  i-- )
582  {
583  if( pszBasename[i] == '.' )
584  {
585  return i;
586  }
587  }
588  return nLen;
589 }
590 
591 /************************************************************************/
592 /* SHPOpen() */
593 /* */
594 /* Open the .shp and .shx files based on the basename of the */
595 /* files or either file name. */
596 /************************************************************************/
597 
599 SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks )
600 
601 {
602  char *pszFullname;
603  SHPHandle psSHP;
604 
605  uchar *pabyBuf;
606  int i;
607  double dValue;
608  int bLazySHXLoading = FALSE;
609  int nLenWithoutExtension;
610 
611 /* -------------------------------------------------------------------- */
612 /* Ensure the access string is one of the legal ones. We */
613 /* ensure the result string indicates binary to avoid common */
614 /* problems on Windows. */
615 /* -------------------------------------------------------------------- */
616  if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0
617  || strcmp(pszAccess,"r+") == 0 )
618  pszAccess = "r+b";
619  else
620  {
621  bLazySHXLoading = strchr(pszAccess, 'l') != SHPLIB_NULLPTR;
622  pszAccess = "rb";
623  }
624 
625 /* -------------------------------------------------------------------- */
626 /* Establish the byte order on this machine. */
627 /* -------------------------------------------------------------------- */
628 #if !defined(bBigEndian)
629  i = 1;
630  if( *((uchar *) &i) == 1 )
631  bBigEndian = FALSE;
632  else
633  bBigEndian = TRUE;
634 #endif
635 
636 /* -------------------------------------------------------------------- */
637 /* Initialize the info structure. */
638 /* -------------------------------------------------------------------- */
639  psSHP = STATIC_CAST(SHPHandle, calloc(sizeof(SHPInfo),1));
640 
641  psSHP->bUpdated = FALSE;
642  memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) );
643 
644 /* -------------------------------------------------------------------- */
645 /* Open the .shp and .shx files. Note that files pulled from */
646 /* a PC to Unix with upper case filenames won't work! */
647 /* -------------------------------------------------------------------- */
648  nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer);
649  pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
650  memcpy(pszFullname, pszLayer, nLenWithoutExtension);
651  memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
652  psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess );
653  if( psSHP->fpSHP == SHPLIB_NULLPTR )
654  {
655  memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5);
656  psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess );
657  }
658 
659  if( psSHP->fpSHP == SHPLIB_NULLPTR )
660  {
661  size_t nMessageLen = strlen(pszFullname)*2+256;
662  char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
663  pszFullname[nLenWithoutExtension] = 0;
664  snprintf( pszMessage, nMessageLen, "Unable to open %s.shp or %s.SHP.",
665  pszFullname, pszFullname );
666  psHooks->Error( pszMessage );
667  free( pszMessage );
668 
669  free( psSHP );
670  free( pszFullname );
671 
672  return SHPLIB_NULLPTR;
673  }
674 
675  memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
676  psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess );
677  if( psSHP->fpSHX == SHPLIB_NULLPTR )
678  {
679  memcpy(pszFullname + nLenWithoutExtension, ".SHX", 5);
680  psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess );
681  }
682 
683  if( psSHP->fpSHX == SHPLIB_NULLPTR )
684  {
685  size_t nMessageLen = strlen(pszFullname)*2+256;
686  char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen));
687  pszFullname[nLenWithoutExtension] = 0;
688  snprintf( pszMessage, nMessageLen, "Unable to open %s.shx or %s.SHX. "
689  "Set SHAPE_RESTORE_SHX config option to YES to restore or "
690  "create it.", pszFullname, pszFullname );
691  psHooks->Error( pszMessage );
692  free( pszMessage );
693 
694  psSHP->sHooks.FClose( psSHP->fpSHP );
695  free( psSHP );
696  free( pszFullname );
697  return SHPLIB_NULLPTR ;
698  }
699 
700  free( pszFullname );
701 
702 /* -------------------------------------------------------------------- */
703 /* Read the file size from the SHP file. */
704 /* -------------------------------------------------------------------- */
705  pabyBuf = STATIC_CAST(uchar *, malloc(100));
706  if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHP ) != 1 )
707  {
708  psSHP->sHooks.Error( ".shp file is unreadable, or corrupt." );
709  psSHP->sHooks.FClose( psSHP->fpSHP );
710  psSHP->sHooks.FClose( psSHP->fpSHX );
711  free( pabyBuf );
712  free( psSHP );
713 
714  return SHPLIB_NULLPTR ;
715  }
716 
717  psSHP->nFileSize = (STATIC_CAST(unsigned int, pabyBuf[24])<<24)|(pabyBuf[25]<<16)|
718  (pabyBuf[26]<<8)|pabyBuf[27];
719  if( psSHP->nFileSize < UINT_MAX / 2 )
720  psSHP->nFileSize *= 2;
721  else
722  psSHP->nFileSize = (UINT_MAX / 2) * 2;
723 
724 /* -------------------------------------------------------------------- */
725 /* Read SHX file Header info */
726 /* -------------------------------------------------------------------- */
727  if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1
728  || pabyBuf[0] != 0
729  || pabyBuf[1] != 0
730  || pabyBuf[2] != 0x27
731  || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) )
732  {
733  psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." );
734  psSHP->sHooks.FClose( psSHP->fpSHP );
735  psSHP->sHooks.FClose( psSHP->fpSHX );
736  free( pabyBuf );
737  free( psSHP );
738 
739  return SHPLIB_NULLPTR;
740  }
741 
742  psSHP->nRecords = pabyBuf[27]|(pabyBuf[26]<<8)|(pabyBuf[25]<<16)|
743  ((pabyBuf[24] & 0x7F)<<24);
744  psSHP->nRecords = (psSHP->nRecords - 50) / 4;
745 
746  psSHP->nShapeType = pabyBuf[32];
747 
748  if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 )
749  {
750  char szErrorMsg[200];
751 
752  snprintf( szErrorMsg, sizeof(szErrorMsg),
753  "Record count in .shp header is %d, which seems\n"
754  "unreasonable. Assuming header is corrupt.",
755  psSHP->nRecords );
756  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
757  psSHP->sHooks.Error( szErrorMsg );
758  psSHP->sHooks.FClose( psSHP->fpSHP );
759  psSHP->sHooks.FClose( psSHP->fpSHX );
760  free( psSHP );
761  free(pabyBuf);
762 
763  return SHPLIB_NULLPTR;
764  }
765 
766  /* If a lot of records are advertized, check that the file is big enough */
767  /* to hold them */
768  if( psSHP->nRecords >= 1024 * 1024 )
769  {
770  SAOffset nFileSize;
771  psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 2 );
772  nFileSize = psSHP->sHooks.FTell( psSHP->fpSHX );
773  if( nFileSize > 100 &&
774  nFileSize/2 < STATIC_CAST(SAOffset, psSHP->nRecords * 4 + 50) )
775  {
776  psSHP->nRecords = STATIC_CAST(int, (nFileSize - 100) / 8);
777  }
778  psSHP->sHooks.FSeek( psSHP->fpSHX, 100, 0 );
779  }
780 
781 /* -------------------------------------------------------------------- */
782 /* Read the bounds. */
783 /* -------------------------------------------------------------------- */
784  if( bBigEndian ) SwapWord( 8, pabyBuf+36 );
785  memcpy( &dValue, pabyBuf+36, 8 );
786  psSHP->adBoundsMin[0] = dValue;
787 
788  if( bBigEndian ) SwapWord( 8, pabyBuf+44 );
789  memcpy( &dValue, pabyBuf+44, 8 );
790  psSHP->adBoundsMin[1] = dValue;
791 
792  if( bBigEndian ) SwapWord( 8, pabyBuf+52 );
793  memcpy( &dValue, pabyBuf+52, 8 );
794  psSHP->adBoundsMax[0] = dValue;
795 
796  if( bBigEndian ) SwapWord( 8, pabyBuf+60 );
797  memcpy( &dValue, pabyBuf+60, 8 );
798  psSHP->adBoundsMax[1] = dValue;
799 
800  if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */
801  memcpy( &dValue, pabyBuf+68, 8 );
802  psSHP->adBoundsMin[2] = dValue;
803 
804  if( bBigEndian ) SwapWord( 8, pabyBuf+76 );
805  memcpy( &dValue, pabyBuf+76, 8 );
806  psSHP->adBoundsMax[2] = dValue;
807 
808  if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */
809  memcpy( &dValue, pabyBuf+84, 8 );
810  psSHP->adBoundsMin[3] = dValue;
811 
812  if( bBigEndian ) SwapWord( 8, pabyBuf+92 );
813  memcpy( &dValue, pabyBuf+92, 8 );
814  psSHP->adBoundsMax[3] = dValue;
815 
816  free( pabyBuf );
817 
818 /* -------------------------------------------------------------------- */
819 /* Read the .shx file to get the offsets to each record in */
820 /* the .shp file. */
821 /* -------------------------------------------------------------------- */
822  psSHP->nMaxRecords = psSHP->nRecords;
823 
824  psSHP->panRecOffset = STATIC_CAST(unsigned int *,
825  malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ));
826  psSHP->panRecSize = STATIC_CAST(unsigned int *,
827  malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ));
828  if( bLazySHXLoading )
829  pabyBuf = SHPLIB_NULLPTR;
830  else
831  pabyBuf = STATIC_CAST(uchar *, malloc(8 * MAX(1,psSHP->nRecords) ));
832 
833  if (psSHP->panRecOffset == SHPLIB_NULLPTR ||
834  psSHP->panRecSize == SHPLIB_NULLPTR ||
835  (!bLazySHXLoading && pabyBuf == SHPLIB_NULLPTR))
836  {
837  char szErrorMsg[200];
838 
839  snprintf( szErrorMsg, sizeof(szErrorMsg),
840  "Not enough memory to allocate requested memory (nRecords=%d).\n"
841  "Probably broken SHP file",
842  psSHP->nRecords );
843  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
844  psSHP->sHooks.Error( szErrorMsg );
845  psSHP->sHooks.FClose( psSHP->fpSHP );
846  psSHP->sHooks.FClose( psSHP->fpSHX );
847  if (psSHP->panRecOffset) free( psSHP->panRecOffset );
848  if (psSHP->panRecSize) free( psSHP->panRecSize );
849  if (pabyBuf) free( pabyBuf );
850  free( psSHP );
851  return SHPLIB_NULLPTR;
852  }
853 
854  if( bLazySHXLoading )
855  {
856  memset(psSHP->panRecOffset, 0, sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) );
857  memset(psSHP->panRecSize, 0, sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) );
858  free( pabyBuf ); // sometimes make cppcheck happy, but
859  return( psSHP );
860  }
861 
862  if( STATIC_CAST(int, psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ))
863  != psSHP->nRecords )
864  {
865  char szErrorMsg[200];
866 
867  snprintf( szErrorMsg, sizeof(szErrorMsg),
868  "Failed to read all values for %d records in .shx file: %s.",
869  psSHP->nRecords, strerror(errno) );
870  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
871  psSHP->sHooks.Error( szErrorMsg );
872 
873  /* SHX is short or unreadable for some reason. */
874  psSHP->sHooks.FClose( psSHP->fpSHP );
875  psSHP->sHooks.FClose( psSHP->fpSHX );
876  free( psSHP->panRecOffset );
877  free( psSHP->panRecSize );
878  free( pabyBuf );
879  free( psSHP );
880 
881  return SHPLIB_NULLPTR;
882  }
883 
884  /* In read-only mode, we can close the SHX now */
885  if (strcmp(pszAccess, "rb") == 0)
886  {
887  psSHP->sHooks.FClose( psSHP->fpSHX );
888  psSHP->fpSHX = SHPLIB_NULLPTR;
889  }
890 
891  for( i = 0; i < psSHP->nRecords; i++ )
892  {
893  unsigned int nOffset, nLength;
894 
895  memcpy( &nOffset, pabyBuf + i * 8, 4 );
896  if( !bBigEndian ) SwapWord( 4, &nOffset );
897 
898  memcpy( &nLength, pabyBuf + i * 8 + 4, 4 );
899  if( !bBigEndian ) SwapWord( 4, &nLength );
900 
901  if( nOffset > STATIC_CAST(unsigned int, INT_MAX) )
902  {
903  char str[128];
904  snprintf( str, sizeof(str),
905  "Invalid offset for entity %d", i);
906  str[sizeof(str)-1] = '\0';
907 
908  psSHP->sHooks.Error( str );
909  SHPClose(psSHP);
910  free( pabyBuf );
911  return SHPLIB_NULLPTR;
912  }
913  if( nLength > STATIC_CAST(unsigned int, INT_MAX / 2 - 4) )
914  {
915  char str[128];
916  snprintf( str, sizeof(str),
917  "Invalid length for entity %d", i);
918  str[sizeof(str)-1] = '\0';
919 
920  psSHP->sHooks.Error( str );
921  SHPClose(psSHP);
922  free( pabyBuf );
923  return SHPLIB_NULLPTR;
924  }
925  psSHP->panRecOffset[i] = nOffset*2;
926  psSHP->panRecSize[i] = nLength*2;
927  }
928  free( pabyBuf );
929 
930  return( psSHP );
931 }
932 
933 /************************************************************************/
934 /* SHPOpenLLEx() */
935 /* */
936 /* Open the .shp and .shx files based on the basename of the */
937 /* files or either file name. It generally invokes SHPRestoreSHX() */
938 /* in case when bRestoreSHX equals true. */
939 /************************************************************************/
940 
942 SHPOpenLLEx( const char * pszLayer, const char * pszAccess, SAHooks *psHooks,
943  int bRestoreSHX )
944 
945 {
946  if ( !bRestoreSHX ) return SHPOpenLL ( pszLayer, pszAccess, psHooks );
947  else
948  {
949  if ( SHPRestoreSHX ( pszLayer, pszAccess, psHooks ) )
950  {
951  return SHPOpenLL ( pszLayer, pszAccess, psHooks );
952  }
953  }
954 
955  return SHPLIB_NULLPTR;
956 }
957 
958 /************************************************************************/
959 /* SHPRestoreSHX() */
960 /* */
961 /* Restore .SHX file using associated .SHP file. */
962 /* */
963 /************************************************************************/
964 
965 int SHPAPI_CALL
966 SHPRestoreSHX ( const char * pszLayer, const char * pszAccess, SAHooks *psHooks )
967 
968 {
969  char *pszFullname;
970  SAFile fpSHP, fpSHX;
971 
972 
973  uchar *pabyBuf;
974  int nLenWithoutExtension;
975  unsigned int nSHPFilesize;
976 
977  unsigned int nCurrentRecordOffset = 0;
978  unsigned int nCurrentSHPOffset = 100;
979  size_t nRealSHXContentSize = 100;
980 
981  const char pszSHXAccess[] = "w+b";
982  char *pabySHXHeader;
983  char abyReadedRecord[8];
984  unsigned int niRecord = 0;
985  unsigned int nRecordLength = 0;
986  unsigned int nRecordOffset = 50;
987 
988 /* -------------------------------------------------------------------- */
989 /* Ensure the access string is one of the legal ones. We */
990 /* ensure the result string indicates binary to avoid common */
991 /* problems on Windows. */
992 /* -------------------------------------------------------------------- */
993  if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0
994  || strcmp(pszAccess,"r+") == 0 )
995  pszAccess = "r+b";
996  else
997  {
998  pszAccess = "rb";
999  }
1000 
1001 /* -------------------------------------------------------------------- */
1002 /* Establish the byte order on this machine. */
1003 /* -------------------------------------------------------------------- */
1004 #if !defined(bBigEndian)
1005  {
1006  int i = 1;
1007  if( *((uchar *) &i) == 1 )
1008  bBigEndian = FALSE;
1009  else
1010  bBigEndian = TRUE;
1011  }
1012 #endif
1013 
1014 /* -------------------------------------------------------------------- */
1015 /* Open the .shp file. Note that files pulled from */
1016 /* a PC to Unix with upper case filenames won't work! */
1017 /* -------------------------------------------------------------------- */
1018  nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer);
1019  pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
1020  memcpy(pszFullname, pszLayer, nLenWithoutExtension);
1021  memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
1022  fpSHP = psHooks->FOpen(pszFullname, pszAccess );
1023  if( fpSHP == SHPLIB_NULLPTR )
1024  {
1025  memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5);
1026  fpSHP = psHooks->FOpen(pszFullname, pszAccess );
1027  }
1028 
1029  if( fpSHP == SHPLIB_NULLPTR )
1030  {
1031  size_t nMessageLen = strlen( pszFullname ) * 2 + 256;
1032  char* pszMessage = STATIC_CAST(char *, malloc( nMessageLen ));
1033 
1034  pszFullname[nLenWithoutExtension] = 0;
1035  snprintf( pszMessage, nMessageLen, "Unable to open %s.shp or %s.SHP.",
1036  pszFullname, pszFullname );
1037  psHooks->Error( pszMessage );
1038  free( pszMessage );
1039 
1040  free( pszFullname );
1041 
1042  return( 0 );
1043  }
1044 
1045 /* -------------------------------------------------------------------- */
1046 /* Read the file size from the SHP file. */
1047 /* -------------------------------------------------------------------- */
1048  pabyBuf = STATIC_CAST(uchar *, malloc(100));
1049  psHooks->FRead( pabyBuf, 100, 1, fpSHP );
1050 
1051  nSHPFilesize = (STATIC_CAST(unsigned int, pabyBuf[24])<<24)|(pabyBuf[25]<<16)|
1052  (pabyBuf[26]<<8)|pabyBuf[27];
1053  if( nSHPFilesize < UINT_MAX / 2 )
1054  nSHPFilesize *= 2;
1055  else
1056  nSHPFilesize = (UINT_MAX / 2) * 2;
1057 
1058  memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
1059  fpSHX = psHooks->FOpen( pszFullname, pszSHXAccess );
1060  if( fpSHX == SHPLIB_NULLPTR )
1061  {
1062  memcpy(pszFullname + nLenWithoutExtension, ".SHX", 5);
1063  fpSHP = psHooks->FOpen(pszFullname, pszAccess );
1064  }
1065 
1066  if( fpSHX == SHPLIB_NULLPTR )
1067  {
1068  size_t nMessageLen = strlen( pszFullname ) * 2 + 256;
1069  char* pszMessage = STATIC_CAST(char *, malloc( nMessageLen ));
1070  pszFullname[nLenWithoutExtension] = 0;
1071  snprintf( pszMessage, nMessageLen,
1072  "Error opening file %s.shx or %s.SHX for writing",
1073  pszFullname, pszFullname );
1074  psHooks->Error( pszMessage );
1075  free( pszMessage );
1076 
1077  psHooks->FClose( fpSHX );
1078 
1079  free( pabyBuf );
1080  free( pszFullname );
1081 
1082  return( 0 );
1083  }
1084 
1085 /* -------------------------------------------------------------------- */
1086 /* Open SHX and create it using SHP file content. */
1087 /* -------------------------------------------------------------------- */
1088  psHooks->FSeek( fpSHP, 100, 0 );
1089  pabySHXHeader = STATIC_CAST(char *, malloc ( 100 ));
1090  memcpy( pabySHXHeader, pabyBuf, 100 );
1091  psHooks->FWrite( pabySHXHeader, 100, 1, fpSHX );
1092 
1093  while( nCurrentSHPOffset < nSHPFilesize )
1094  {
1095  if( psHooks->FRead( &niRecord, 4, 1, fpSHP ) == 1 &&
1096  psHooks->FRead( &nRecordLength, 4, 1, fpSHP ) == 1)
1097  {
1098  if( !bBigEndian ) SwapWord( 4, &nRecordOffset );
1099  memcpy( abyReadedRecord, &nRecordOffset, 4 );
1100  memcpy( abyReadedRecord + 4, &nRecordLength, 4 );
1101 
1102  psHooks->FWrite( abyReadedRecord, 8, 1, fpSHX );
1103 
1104  if ( !bBigEndian ) SwapWord( 4, &nRecordOffset );
1105  if ( !bBigEndian ) SwapWord( 4, &nRecordLength );
1106  nRecordOffset += nRecordLength + 4;
1107  nCurrentRecordOffset += 8;
1108  nCurrentSHPOffset += 8 + nRecordLength * 2;
1109 
1110  psHooks->FSeek( fpSHP, nCurrentSHPOffset, 0 );
1111  nRealSHXContentSize += 8;
1112  }
1113  else
1114  {
1115  psHooks->Error( "Error parsing .shp to restore .shx" );
1116 
1117  psHooks->FClose( fpSHX );
1118  psHooks->FClose( fpSHP );
1119 
1120  free( pabySHXHeader );
1121  free( pszFullname );
1122 
1123  return( 0 );
1124  }
1125  }
1126 
1127  nRealSHXContentSize /= 2; // Bytes counted -> WORDs
1128  if( !bBigEndian ) SwapWord( 4, &nRealSHXContentSize );
1129  psHooks->FSeek( fpSHX, 24, 0 );
1130  psHooks->FWrite( &nRealSHXContentSize, 4, 1, fpSHX );
1131 
1132  psHooks->FClose( fpSHP );
1133  psHooks->FClose( fpSHX );
1134 
1135  free ( pabyBuf );
1136  free ( pszFullname );
1137  free ( pabySHXHeader );
1138 
1139  return( 1 );
1140 }
1141 
1142 /************************************************************************/
1143 /* SHPClose() */
1144 /* */
1145 /* Close the .shp and .shx files. */
1146 /************************************************************************/
1147 
1148 void SHPAPI_CALL
1150 
1151 {
1152  if( psSHP == SHPLIB_NULLPTR )
1153  return;
1154 
1155 /* -------------------------------------------------------------------- */
1156 /* Update the header if we have modified anything. */
1157 /* -------------------------------------------------------------------- */
1158  if( psSHP->bUpdated )
1159  SHPWriteHeader( psSHP );
1160 
1161 /* -------------------------------------------------------------------- */
1162 /* Free all resources, and close files. */
1163 /* -------------------------------------------------------------------- */
1164  free( psSHP->panRecOffset );
1165  free( psSHP->panRecSize );
1166 
1167  if ( psSHP->fpSHX != SHPLIB_NULLPTR)
1168  psSHP->sHooks.FClose( psSHP->fpSHX );
1169  psSHP->sHooks.FClose( psSHP->fpSHP );
1170 
1171  if( psSHP->pabyRec != SHPLIB_NULLPTR )
1172  {
1173  free( psSHP->pabyRec );
1174  }
1175 
1176  if( psSHP->pabyObjectBuf != SHPLIB_NULLPTR )
1177  {
1178  free( psSHP->pabyObjectBuf );
1179  }
1180  if( psSHP->psCachedObject != SHPLIB_NULLPTR )
1181  {
1182  free( psSHP->psCachedObject );
1183  }
1184 
1185  free( psSHP );
1186 }
1187 
1188 /************************************************************************/
1189 /* SHPSetFastModeReadObject() */
1190 /************************************************************************/
1191 
1192 /* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the SHPHandle. */
1193 /* So you cannot have 2 valid instances of SHPReadObject() simultaneously. */
1194 /* The SHPObject padfZ and padfM members may be NULL depending on the geometry */
1195 /* type. It is illegal to free at hand any of the pointer members of the SHPObject structure */
1197 {
1198  if( bFastMode )
1199  {
1200  if( hSHP->psCachedObject == SHPLIB_NULLPTR )
1201  {
1202  hSHP->psCachedObject = STATIC_CAST(SHPObject*, calloc(1, sizeof(SHPObject)));
1203  assert( hSHP->psCachedObject != SHPLIB_NULLPTR );
1204  }
1205  }
1206 
1207  hSHP->bFastModeReadObject = bFastMode;
1208 }
1209 
1210 /************************************************************************/
1211 /* SHPGetInfo() */
1212 /* */
1213 /* Fetch general information about the shape file. */
1214 /************************************************************************/
1215 
1216 void SHPAPI_CALL
1217 SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType,
1218  double * padfMinBound, double * padfMaxBound )
1219 
1220 {
1221  int i;
1222 
1223  if( psSHP == SHPLIB_NULLPTR )
1224  return;
1225 
1226  if( pnEntities != SHPLIB_NULLPTR )
1227  *pnEntities = psSHP->nRecords;
1228 
1229  if( pnShapeType != SHPLIB_NULLPTR )
1230  *pnShapeType = psSHP->nShapeType;
1231 
1232  for( i = 0; i < 4; i++ )
1233  {
1234  if( padfMinBound != SHPLIB_NULLPTR )
1235  padfMinBound[i] = psSHP->adBoundsMin[i];
1236  if( padfMaxBound != SHPLIB_NULLPTR )
1237  padfMaxBound[i] = psSHP->adBoundsMax[i];
1238  }
1239 }
1240 
1241 /************************************************************************/
1242 /* SHPCreate() */
1243 /* */
1244 /* Create a new shape file and return a handle to the open */
1245 /* shape file with read/write access. */
1246 /************************************************************************/
1247 
1249 SHPCreate( const char * pszLayer, int nShapeType )
1250 
1251 {
1252  SAHooks sHooks;
1253 
1254  SASetupDefaultHooks( &sHooks );
1255 
1256  return SHPCreateLL( pszLayer, nShapeType, &sHooks );
1257 }
1258 
1259 /************************************************************************/
1260 /* SHPCreate() */
1261 /* */
1262 /* Create a new shape file and return a handle to the open */
1263 /* shape file with read/write access. */
1264 /************************************************************************/
1265 
1267 SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks )
1268 
1269 {
1270  char *pszFullname;
1271  SAFile fpSHP;
1272  SAFile fpSHX = SHPLIB_NULLPTR;
1273  uchar abyHeader[100];
1274  int32 i32;
1275  double dValue;
1276  int nLenWithoutExtension;
1277 
1278 /* -------------------------------------------------------------------- */
1279 /* Establish the byte order on this system. */
1280 /* -------------------------------------------------------------------- */
1281 #if !defined(bBigEndian)
1282  {
1283  int i = 1;
1284  if( *((uchar *) &i) == 1 )
1285  bBigEndian = FALSE;
1286  else
1287  bBigEndian = TRUE;
1288  }
1289 #endif
1290 
1291 /* -------------------------------------------------------------------- */
1292 /* Open the two files so we can write their headers. */
1293 /* -------------------------------------------------------------------- */
1294  nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer);
1295  pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5));
1296  memcpy(pszFullname, pszLayer, nLenWithoutExtension);
1297  memcpy(pszFullname + nLenWithoutExtension, ".shp", 5);
1298  fpSHP = psHooks->FOpen(pszFullname, "wb" );
1299  if( fpSHP == SHPLIB_NULLPTR )
1300  {
1301  char szErrorMsg[200];
1302  snprintf( szErrorMsg, sizeof(szErrorMsg),
1303  "Failed to create file %s: %s",
1304  pszFullname, strerror(errno) );
1305  psHooks->Error( szErrorMsg );
1306 
1307  goto error;
1308  }
1309 
1310  memcpy(pszFullname + nLenWithoutExtension, ".shx", 5);
1311  fpSHX = psHooks->FOpen(pszFullname, "wb" );
1312  if( fpSHX == SHPLIB_NULLPTR )
1313  {
1314  char szErrorMsg[200];
1315  snprintf( szErrorMsg, sizeof(szErrorMsg),
1316  "Failed to create file %s: %s",
1317  pszFullname, strerror(errno) );
1318  psHooks->Error( szErrorMsg );
1319  goto error;
1320  }
1321 
1322  free( pszFullname ); pszFullname = SHPLIB_NULLPTR;
1323 
1324 /* -------------------------------------------------------------------- */
1325 /* Prepare header block for .shp file. */
1326 /* -------------------------------------------------------------------- */
1327  memset( abyHeader, 0, sizeof(abyHeader) );
1328 
1329  abyHeader[2] = 0x27; /* magic cookie */
1330  abyHeader[3] = 0x0a;
1331 
1332  i32 = 50; /* file size */
1333  ByteCopy( &i32, abyHeader+24, 4 );
1334  if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
1335 
1336  i32 = 1000; /* version */
1337  ByteCopy( &i32, abyHeader+28, 4 );
1338  if( bBigEndian ) SwapWord( 4, abyHeader+28 );
1339 
1340  i32 = nShapeType; /* shape type */
1341  ByteCopy( &i32, abyHeader+32, 4 );
1342  if( bBigEndian ) SwapWord( 4, abyHeader+32 );
1343 
1344  dValue = 0.0; /* set bounds */
1345  ByteCopy( &dValue, abyHeader+36, 8 );
1346  ByteCopy( &dValue, abyHeader+44, 8 );
1347  ByteCopy( &dValue, abyHeader+52, 8 );
1348  ByteCopy( &dValue, abyHeader+60, 8 );
1349 
1350 /* -------------------------------------------------------------------- */
1351 /* Write .shp file header. */
1352 /* -------------------------------------------------------------------- */
1353  if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 )
1354  {
1355  char szErrorMsg[200];
1356 
1357  snprintf( szErrorMsg, sizeof(szErrorMsg),
1358  "Failed to write .shp header: %s", strerror(errno) );
1359  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
1360  psHooks->Error( szErrorMsg );
1361 
1362  goto error;
1363  }
1364 
1365 /* -------------------------------------------------------------------- */
1366 /* Prepare, and write .shx file header. */
1367 /* -------------------------------------------------------------------- */
1368  i32 = 50; /* file size */
1369  ByteCopy( &i32, abyHeader+24, 4 );
1370  if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
1371 
1372  if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 )
1373  {
1374  char szErrorMsg[200];
1375 
1376  snprintf( szErrorMsg, sizeof(szErrorMsg),
1377  "Failure writing .shx header: %s", strerror(errno) );
1378  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
1379  psHooks->Error( szErrorMsg );
1380 
1381  goto error;
1382  }
1383 
1384 /* -------------------------------------------------------------------- */
1385 /* Close the files, and then open them as regular existing files. */
1386 /* -------------------------------------------------------------------- */
1387  psHooks->FClose( fpSHP );
1388  psHooks->FClose( fpSHX );
1389 
1390  return( SHPOpenLL( pszLayer, "r+b", psHooks ) );
1391 
1392 error:
1393  if (pszFullname) free(pszFullname);
1394  if (fpSHP) psHooks->FClose( fpSHP );
1395  if (fpSHX) psHooks->FClose( fpSHX );
1396  return SHPLIB_NULLPTR;
1397 }
1398 
1399 /************************************************************************/
1400 /* _SHPSetBounds() */
1401 /* */
1402 /* Compute a bounds rectangle for a shape, and set it into the */
1403 /* indicated location in the record. */
1404 /************************************************************************/
1405 
1406 static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape )
1407 
1408 {
1409  ByteCopy( &(psShape->dfXMin), pabyRec + 0, 8 );
1410  ByteCopy( &(psShape->dfYMin), pabyRec + 8, 8 );
1411  ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 );
1412  ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 );
1413 
1414  if( bBigEndian )
1415  {
1416  SwapWord( 8, pabyRec + 0 );
1417  SwapWord( 8, pabyRec + 8 );
1418  SwapWord( 8, pabyRec + 16 );
1419  SwapWord( 8, pabyRec + 24 );
1420  }
1421 }
1422 
1423 /************************************************************************/
1424 /* SHPComputeExtents() */
1425 /* */
1426 /* Recompute the extents of a shape. Automatically done by */
1427 /* SHPCreateObject(). */
1428 /************************************************************************/
1429 
1430 void SHPAPI_CALL
1432 
1433 {
1434  int i;
1435 
1436 /* -------------------------------------------------------------------- */
1437 /* Build extents for this object. */
1438 /* -------------------------------------------------------------------- */
1439  if( psObject->nVertices > 0 )
1440  {
1441  psObject->dfXMin = psObject->dfXMax = psObject->padfX[0];
1442  psObject->dfYMin = psObject->dfYMax = psObject->padfY[0];
1443  psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0];
1444  psObject->dfMMin = psObject->dfMMax = psObject->padfM[0];
1445  }
1446 
1447  for( i = 0; i < psObject->nVertices; i++ )
1448  {
1449  psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]);
1450  psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]);
1451  psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]);
1452  psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]);
1453 
1454  psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]);
1455  psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]);
1456  psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]);
1457  psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]);
1458  }
1459 }
1460 
1461 /************************************************************************/
1462 /* SHPCreateObject() */
1463 /* */
1464 /* Create a shape object. It should be freed with */
1465 /* SHPDestroyObject(). */
1466 /************************************************************************/
1467 
1469 SHPCreateObject( int nSHPType, int nShapeId, int nParts,
1470  const int * panPartStart, const int * panPartType,
1471  int nVertices, const double *padfX, const double *padfY,
1472  const double * padfZ, const double * padfM )
1473 
1474 {
1475  SHPObject *psObject;
1476  int i, bHasM, bHasZ;
1477 
1478  psObject = STATIC_CAST(SHPObject *, calloc(1,sizeof(SHPObject)));
1479  psObject->nSHPType = nSHPType;
1480  psObject->nShapeId = nShapeId;
1481  psObject->bMeasureIsUsed = FALSE;
1482 
1483 /* -------------------------------------------------------------------- */
1484 /* Establish whether this shape type has M, and Z values. */
1485 /* -------------------------------------------------------------------- */
1486  if( nSHPType == SHPT_ARCM
1487  || nSHPType == SHPT_POINTM
1488  || nSHPType == SHPT_POLYGONM
1489  || nSHPType == SHPT_MULTIPOINTM )
1490  {
1491  bHasM = TRUE;
1492  bHasZ = FALSE;
1493  }
1494  else if( nSHPType == SHPT_ARCZ
1495  || nSHPType == SHPT_POINTZ
1496  || nSHPType == SHPT_POLYGONZ
1497  || nSHPType == SHPT_MULTIPOINTZ
1498  || nSHPType == SHPT_MULTIPATCH )
1499  {
1500  bHasM = TRUE;
1501  bHasZ = TRUE;
1502  }
1503  else
1504  {
1505  bHasM = FALSE;
1506  bHasZ = FALSE;
1507  }
1508 
1509 /* -------------------------------------------------------------------- */
1510 /* Capture parts. Note that part type is optional, and */
1511 /* defaults to ring. */
1512 /* -------------------------------------------------------------------- */
1513  if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON
1514  || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM
1515  || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ
1516  || nSHPType == SHPT_MULTIPATCH )
1517  {
1518  psObject->nParts = MAX(1,nParts);
1519 
1520  psObject->panPartStart = STATIC_CAST(int *,
1521  calloc(sizeof(int), psObject->nParts));
1522  psObject->panPartType = STATIC_CAST(int *,
1523  malloc(sizeof(int) * psObject->nParts));
1524 
1525  psObject->panPartStart[0] = 0;
1526  psObject->panPartType[0] = SHPP_RING;
1527 
1528  for( i = 0; i < nParts; i++ )
1529  {
1530  if( panPartStart != SHPLIB_NULLPTR )
1531  psObject->panPartStart[i] = panPartStart[i];
1532 
1533  if( panPartType != SHPLIB_NULLPTR )
1534  psObject->panPartType[i] = panPartType[i];
1535  else
1536  psObject->panPartType[i] = SHPP_RING;
1537  }
1538 
1539  if( psObject->panPartStart[0] != 0 )
1540  psObject->panPartStart[0] = 0;
1541  }
1542 
1543 /* -------------------------------------------------------------------- */
1544 /* Capture vertices. Note that X, Y, Z and M are optional. */
1545 /* -------------------------------------------------------------------- */
1546  if( nVertices > 0 )
1547  {
1548  size_t nSize = sizeof(double) * nVertices;
1549  psObject->padfX = STATIC_CAST(double *, padfX ? malloc(nSize) :
1550  calloc(sizeof(double),nVertices));
1551  psObject->padfY = STATIC_CAST(double *, padfY ? malloc(nSize) :
1552  calloc(sizeof(double),nVertices));
1553  psObject->padfZ = STATIC_CAST(double *, padfZ && bHasZ ? malloc(nSize) :
1554  calloc(sizeof(double),nVertices));
1555  psObject->padfM = STATIC_CAST(double *, padfM && bHasM ? malloc(nSize) :
1556  calloc(sizeof(double),nVertices));
1557  if( padfX != SHPLIB_NULLPTR )
1558  memcpy(psObject->padfX, padfX, nSize);
1559  if( padfY != SHPLIB_NULLPTR )
1560  memcpy(psObject->padfY, padfY, nSize);
1561  if( padfZ != SHPLIB_NULLPTR && bHasZ )
1562  memcpy(psObject->padfZ, padfZ, nSize);
1563  if( padfM != SHPLIB_NULLPTR && bHasM )
1564  {
1565  memcpy(psObject->padfM, padfM, nSize);
1566  psObject->bMeasureIsUsed = TRUE;
1567  }
1568  }
1569 
1570 /* -------------------------------------------------------------------- */
1571 /* Compute the extents. */
1572 /* -------------------------------------------------------------------- */
1573  psObject->nVertices = nVertices;
1574  SHPComputeExtents( psObject );
1575 
1576  return( psObject );
1577 }
1578 
1579 /************************************************************************/
1580 /* SHPCreateSimpleObject() */
1581 /* */
1582 /* Create a simple (common) shape object. Destroy with */
1583 /* SHPDestroyObject(). */
1584 /************************************************************************/
1585 
1587 SHPCreateSimpleObject( int nSHPType, int nVertices,
1588  const double * padfX, const double * padfY,
1589  const double * padfZ )
1590 
1591 {
1592  return( SHPCreateObject( nSHPType, -1, 0, SHPLIB_NULLPTR, SHPLIB_NULLPTR,
1593  nVertices, padfX, padfY, padfZ, SHPLIB_NULLPTR ) );
1594 }
1595 
1596 /************************************************************************/
1597 /* SHPWriteObject() */
1598 /* */
1599 /* Write out the vertices of a new structure. Note that it is */
1600 /* only possible to write vertices at the end of the file. */
1601 /************************************************************************/
1602 
1603 int SHPAPI_CALL
1604 SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject )
1605 
1606 {
1607  unsigned int nRecordOffset, nRecordSize=0;
1608  int i;
1609  uchar *pabyRec;
1610  int32 i32;
1611  int bAppendToLastRecord = FALSE;
1612  int bAppendToFile = FALSE;
1613 
1614  psSHP->bUpdated = TRUE;
1615 
1616 /* -------------------------------------------------------------------- */
1617 /* Ensure that shape object matches the type of the file it is */
1618 /* being written to. */
1619 /* -------------------------------------------------------------------- */
1620  assert( psObject->nSHPType == psSHP->nShapeType
1621  || psObject->nSHPType == SHPT_NULL );
1622 
1623 /* -------------------------------------------------------------------- */
1624 /* Ensure that -1 is used for appends. Either blow an */
1625 /* assertion, or if they are disabled, set the shapeid to -1 */
1626 /* for appends. */
1627 /* -------------------------------------------------------------------- */
1628  assert( nShapeId == -1
1629  || (nShapeId >= 0 && nShapeId < psSHP->nRecords) );
1630 
1631  if( nShapeId != -1 && nShapeId >= psSHP->nRecords )
1632  nShapeId = -1;
1633 
1634 /* -------------------------------------------------------------------- */
1635 /* Add the new entity to the in memory index. */
1636 /* -------------------------------------------------------------------- */
1637  if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords )
1638  {
1639  int nNewMaxRecords = psSHP->nMaxRecords + psSHP->nMaxRecords / 3 + 100;
1640  unsigned int* panRecOffsetNew;
1641  unsigned int* panRecSizeNew;
1642 
1643  panRecOffsetNew = STATIC_CAST(unsigned int *,
1644  SfRealloc(psSHP->panRecOffset,sizeof(unsigned int) * nNewMaxRecords ));
1645  if( panRecOffsetNew == SHPLIB_NULLPTR )
1646  return -1;
1647  psSHP->panRecOffset = panRecOffsetNew;
1648 
1649  panRecSizeNew = STATIC_CAST(unsigned int *,
1650  SfRealloc(psSHP->panRecSize,sizeof(unsigned int) * nNewMaxRecords ));
1651  if( panRecSizeNew == SHPLIB_NULLPTR )
1652  return -1;
1653  psSHP->panRecSize = panRecSizeNew;
1654 
1655  psSHP->nMaxRecords = nNewMaxRecords;
1656  }
1657 
1658 /* -------------------------------------------------------------------- */
1659 /* Initialize record. */
1660 /* -------------------------------------------------------------------- */
1661  pabyRec = STATIC_CAST(uchar *, malloc(psObject->nVertices * 4 * sizeof(double)
1662  + psObject->nParts * 8 + 128));
1663  if( pabyRec == SHPLIB_NULLPTR )
1664  return -1;
1665 
1666 /* -------------------------------------------------------------------- */
1667 /* Extract vertices for a Polygon or Arc. */
1668 /* -------------------------------------------------------------------- */
1669  if( psObject->nSHPType == SHPT_POLYGON
1670  || psObject->nSHPType == SHPT_POLYGONZ
1671  || psObject->nSHPType == SHPT_POLYGONM
1672  || psObject->nSHPType == SHPT_ARC
1673  || psObject->nSHPType == SHPT_ARCZ
1674  || psObject->nSHPType == SHPT_ARCM
1675  || psObject->nSHPType == SHPT_MULTIPATCH )
1676  {
1677  int32 nPoints, nParts;
1678 
1679  nPoints = psObject->nVertices;
1680  nParts = psObject->nParts;
1681 
1682  _SHPSetBounds( pabyRec + 12, psObject );
1683 
1684  if( bBigEndian ) SwapWord( 4, &nPoints );
1685  if( bBigEndian ) SwapWord( 4, &nParts );
1686 
1687  ByteCopy( &nPoints, pabyRec + 40 + 8, 4 );
1688  ByteCopy( &nParts, pabyRec + 36 + 8, 4 );
1689 
1690  nRecordSize = 52;
1691 
1692  /*
1693  * Write part start positions.
1694  */
1695  ByteCopy( psObject->panPartStart, pabyRec + 44 + 8,
1696  4 * psObject->nParts );
1697  for( i = 0; i < psObject->nParts; i++ )
1698  {
1699  if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i );
1700  nRecordSize += 4;
1701  }
1702 
1703  /*
1704  * Write multipatch part types if needed.
1705  */
1706  if( psObject->nSHPType == SHPT_MULTIPATCH )
1707  {
1708  memcpy( pabyRec + nRecordSize, psObject->panPartType,
1709  4*psObject->nParts );
1710  for( i = 0; i < psObject->nParts; i++ )
1711  {
1712  if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize );
1713  nRecordSize += 4;
1714  }
1715  }
1716 
1717  /*
1718  * Write the (x,y) vertex values.
1719  */
1720  for( i = 0; i < psObject->nVertices; i++ )
1721  {
1722  ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 );
1723  ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 );
1724 
1725  if( bBigEndian )
1726  SwapWord( 8, pabyRec + nRecordSize );
1727 
1728  if( bBigEndian )
1729  SwapWord( 8, pabyRec + nRecordSize + 8 );
1730 
1731  nRecordSize += 2 * 8;
1732  }
1733 
1734  /*
1735  * Write the Z coordinates (if any).
1736  */
1737  if( psObject->nSHPType == SHPT_POLYGONZ
1738  || psObject->nSHPType == SHPT_ARCZ
1739  || psObject->nSHPType == SHPT_MULTIPATCH )
1740  {
1741  ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
1742  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1743  nRecordSize += 8;
1744 
1745  ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
1746  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1747  nRecordSize += 8;
1748 
1749  for( i = 0; i < psObject->nVertices; i++ )
1750  {
1751  ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
1752  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1753  nRecordSize += 8;
1754  }
1755  }
1756 
1757  /*
1758  * Write the M values, if any.
1759  */
1760  if( psObject->bMeasureIsUsed
1761  && (psObject->nSHPType == SHPT_POLYGONM
1762  || psObject->nSHPType == SHPT_ARCM
1764  || psObject->nSHPType == SHPT_MULTIPATCH
1765 #endif
1766  || psObject->nSHPType == SHPT_POLYGONZ
1767  || psObject->nSHPType == SHPT_ARCZ) )
1768  {
1769  ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
1770  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1771  nRecordSize += 8;
1772 
1773  ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
1774  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1775  nRecordSize += 8;
1776 
1777  for( i = 0; i < psObject->nVertices; i++ )
1778  {
1779  ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
1780  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1781  nRecordSize += 8;
1782  }
1783  }
1784  }
1785 
1786 /* -------------------------------------------------------------------- */
1787 /* Extract vertices for a MultiPoint. */
1788 /* -------------------------------------------------------------------- */
1789  else if( psObject->nSHPType == SHPT_MULTIPOINT
1790  || psObject->nSHPType == SHPT_MULTIPOINTZ
1791  || psObject->nSHPType == SHPT_MULTIPOINTM )
1792  {
1793  int32 nPoints;
1794 
1795  nPoints = psObject->nVertices;
1796 
1797  _SHPSetBounds( pabyRec + 12, psObject );
1798 
1799  if( bBigEndian ) SwapWord( 4, &nPoints );
1800  ByteCopy( &nPoints, pabyRec + 44, 4 );
1801 
1802  for( i = 0; i < psObject->nVertices; i++ )
1803  {
1804  ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 );
1805  ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 );
1806 
1807  if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 );
1808  if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 );
1809  }
1810 
1811  nRecordSize = 48 + 16 * psObject->nVertices;
1812 
1813  if( psObject->nSHPType == SHPT_MULTIPOINTZ )
1814  {
1815  ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
1816  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1817  nRecordSize += 8;
1818 
1819  ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
1820  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1821  nRecordSize += 8;
1822 
1823  for( i = 0; i < psObject->nVertices; i++ )
1824  {
1825  ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
1826  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1827  nRecordSize += 8;
1828  }
1829  }
1830 
1831  if( psObject->bMeasureIsUsed
1832  && (psObject->nSHPType == SHPT_MULTIPOINTZ
1833  || psObject->nSHPType == SHPT_MULTIPOINTM) )
1834  {
1835  ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
1836  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1837  nRecordSize += 8;
1838 
1839  ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
1840  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1841  nRecordSize += 8;
1842 
1843  for( i = 0; i < psObject->nVertices; i++ )
1844  {
1845  ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
1846  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1847  nRecordSize += 8;
1848  }
1849  }
1850  }
1851 
1852 /* -------------------------------------------------------------------- */
1853 /* Write point. */
1854 /* -------------------------------------------------------------------- */
1855  else if( psObject->nSHPType == SHPT_POINT
1856  || psObject->nSHPType == SHPT_POINTZ
1857  || psObject->nSHPType == SHPT_POINTM )
1858  {
1859  ByteCopy( psObject->padfX, pabyRec + 12, 8 );
1860  ByteCopy( psObject->padfY, pabyRec + 20, 8 );
1861 
1862  if( bBigEndian ) SwapWord( 8, pabyRec + 12 );
1863  if( bBigEndian ) SwapWord( 8, pabyRec + 20 );
1864 
1865  nRecordSize = 28;
1866 
1867  if( psObject->nSHPType == SHPT_POINTZ )
1868  {
1869  ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 );
1870  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1871  nRecordSize += 8;
1872  }
1873 
1874  if( psObject->bMeasureIsUsed
1875  && (psObject->nSHPType == SHPT_POINTZ
1876  || psObject->nSHPType == SHPT_POINTM) )
1877  {
1878  ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 );
1879  if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
1880  nRecordSize += 8;
1881  }
1882  }
1883 
1884 /* -------------------------------------------------------------------- */
1885 /* Not much to do for null geometries. */
1886 /* -------------------------------------------------------------------- */
1887  else if( psObject->nSHPType == SHPT_NULL )
1888  {
1889  nRecordSize = 12;
1890  }
1891 
1892  else
1893  {
1894  /* unknown type */
1895  assert( FALSE );
1896  }
1897 
1898 /* -------------------------------------------------------------------- */
1899 /* Establish where we are going to put this record. If we are */
1900 /* rewriting the last record of the file, then we can update it in */
1901 /* place. Otherwise if rewriting an existing record, and it will */
1902 /* fit, then put it back where the original came from. Otherwise */
1903 /* write at the end. */
1904 /* -------------------------------------------------------------------- */
1905  if( nShapeId != -1 && psSHP->panRecOffset[nShapeId] +
1906  psSHP->panRecSize[nShapeId] + 8 == psSHP->nFileSize )
1907  {
1908  nRecordOffset = psSHP->panRecOffset[nShapeId];
1909  bAppendToLastRecord = TRUE;
1910  }
1911  else if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 )
1912  {
1913  if( psSHP->nFileSize > UINT_MAX - nRecordSize)
1914  {
1915  char str[128];
1916  snprintf( str, sizeof(str), "Failed to write shape object. "
1917  "File size cannot reach %u + %u.",
1918  psSHP->nFileSize, nRecordSize );
1919  str[sizeof(str)-1] = '\0';
1920  psSHP->sHooks.Error( str );
1921  free( pabyRec );
1922  return -1;
1923  }
1924 
1925  bAppendToFile = TRUE;
1926  nRecordOffset = psSHP->nFileSize;
1927  }
1928  else
1929  {
1930  nRecordOffset = psSHP->panRecOffset[nShapeId];
1931  }
1932 
1933 /* -------------------------------------------------------------------- */
1934 /* Set the shape type, record number, and record size. */
1935 /* -------------------------------------------------------------------- */
1936  i32 = (nShapeId < 0) ? psSHP->nRecords+1 : nShapeId+1; /* record # */
1937  if( !bBigEndian ) SwapWord( 4, &i32 );
1938  ByteCopy( &i32, pabyRec, 4 );
1939 
1940  i32 = (nRecordSize-8)/2; /* record size */
1941  if( !bBigEndian ) SwapWord( 4, &i32 );
1942  ByteCopy( &i32, pabyRec + 4, 4 );
1943 
1944  i32 = psObject->nSHPType; /* shape type */
1945  if( bBigEndian ) SwapWord( 4, &i32 );
1946  ByteCopy( &i32, pabyRec + 8, 4 );
1947 
1948 /* -------------------------------------------------------------------- */
1949 /* Write out record. */
1950 /* -------------------------------------------------------------------- */
1951  if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 )
1952  {
1953  char szErrorMsg[200];
1954 
1955  snprintf( szErrorMsg, sizeof(szErrorMsg),
1956  "Error in psSHP->sHooks.FSeek() while writing object to .shp file: %s",
1957  strerror(errno) );
1958  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
1959  psSHP->sHooks.Error( szErrorMsg );
1960 
1961  free( pabyRec );
1962  return -1;
1963  }
1964  if( psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 )
1965  {
1966  char szErrorMsg[200];
1967 
1968  snprintf( szErrorMsg, sizeof(szErrorMsg),
1969  "Error in psSHP->sHooks.FWrite() while writing object of %u bytes to .shp file: %s",
1970  nRecordSize, strerror(errno) );
1971  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
1972  psSHP->sHooks.Error( szErrorMsg );
1973 
1974  free( pabyRec );
1975  return -1;
1976  }
1977 
1978  free( pabyRec );
1979 
1980  if( bAppendToLastRecord )
1981  {
1982  psSHP->nFileSize = psSHP->panRecOffset[nShapeId] + nRecordSize;
1983  }
1984  else if( bAppendToFile )
1985  {
1986  if( nShapeId == -1 )
1987  nShapeId = psSHP->nRecords++;
1988 
1989  psSHP->panRecOffset[nShapeId] = psSHP->nFileSize;
1990  psSHP->nFileSize += nRecordSize;
1991  }
1992  psSHP->panRecSize[nShapeId] = nRecordSize-8;
1993 
1994 /* -------------------------------------------------------------------- */
1995 /* Expand file wide bounds based on this shape. */
1996 /* -------------------------------------------------------------------- */
1997  if( psSHP->adBoundsMin[0] == 0.0
1998  && psSHP->adBoundsMax[0] == 0.0
1999  && psSHP->adBoundsMin[1] == 0.0
2000  && psSHP->adBoundsMax[1] == 0.0 )
2001  {
2002  if( psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0 )
2003  {
2004  psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0;
2005  psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0;
2006  psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0;
2007  psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0;
2008  }
2009  else
2010  {
2011  psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0];
2012  psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0];
2013  psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ ? psObject->padfZ[0] : 0.0;
2014  psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM ? psObject->padfM[0] : 0.0;
2015  }
2016  }
2017 
2018  for( i = 0; i < psObject->nVertices; i++ )
2019  {
2020  psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]);
2021  psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]);
2022  psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]);
2023  psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]);
2024  if( psObject->padfZ )
2025  {
2026  psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]);
2027  psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]);
2028  }
2029  if( psObject->padfM )
2030  {
2031  psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]);
2032  psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]);
2033  }
2034  }
2035 
2036  return( nShapeId );
2037 }
2038 
2039 /************************************************************************/
2040 /* SHPAllocBuffer() */
2041 /************************************************************************/
2042 
2043 static void* SHPAllocBuffer(unsigned char** pBuffer, int nSize)
2044 {
2045  unsigned char* pRet;
2046 
2047  if( pBuffer == SHPLIB_NULLPTR )
2048  return calloc(1, nSize);
2049 
2050  pRet = *pBuffer;
2051  if( pRet == SHPLIB_NULLPTR )
2052  return SHPLIB_NULLPTR;
2053 
2054  (*pBuffer) += nSize;
2055  return pRet;
2056 }
2057 
2058 /************************************************************************/
2059 /* SHPReallocObjectBufIfNecessary() */
2060 /************************************************************************/
2061 
2062 static unsigned char* SHPReallocObjectBufIfNecessary ( SHPHandle psSHP,
2063  int nObjectBufSize )
2064 {
2065  unsigned char* pBuffer;
2066  if( nObjectBufSize == 0 )
2067  {
2068  nObjectBufSize = 4 * sizeof(double);
2069  }
2070  if( nObjectBufSize > psSHP->nObjectBufSize )
2071  {
2072  pBuffer = STATIC_CAST(unsigned char*, realloc( psSHP->pabyObjectBuf, nObjectBufSize ));
2073  if( pBuffer != SHPLIB_NULLPTR )
2074  {
2075  psSHP->pabyObjectBuf = pBuffer;
2076  psSHP->nObjectBufSize = nObjectBufSize;
2077  }
2078  }
2079  else
2080  pBuffer = psSHP->pabyObjectBuf;
2081  return pBuffer;
2082 }
2083 
2084 /************************************************************************/
2085 /* SHPReadObject() */
2086 /* */
2087 /* Read the vertices, parts, and other non-attribute information */
2088 /* for one shape. */
2089 /************************************************************************/
2090 
2092 SHPReadObject( SHPHandle psSHP, int hEntity )
2093 
2094 {
2095  int nEntitySize, nRequiredSize;
2096  SHPObject *psShape;
2097  char szErrorMsg[128];
2098  int nSHPType;
2099  int nBytesRead;
2100 
2101 /* -------------------------------------------------------------------- */
2102 /* Validate the record/entity number. */
2103 /* -------------------------------------------------------------------- */
2104  if( hEntity < 0 || hEntity >= psSHP->nRecords )
2105  return SHPLIB_NULLPTR;
2106 
2107 /* -------------------------------------------------------------------- */
2108 /* Read offset/length from SHX loading if necessary. */
2109 /* -------------------------------------------------------------------- */
2110  if( psSHP->panRecOffset[hEntity] == 0 && psSHP->fpSHX != SHPLIB_NULLPTR )
2111  {
2112  unsigned int nOffset, nLength;
2113 
2114  if( psSHP->sHooks.FSeek( psSHP->fpSHX, 100 + 8 * hEntity, 0 ) != 0 ||
2115  psSHP->sHooks.FRead( &nOffset, 1, 4, psSHP->fpSHX ) != 4 ||
2116  psSHP->sHooks.FRead( &nLength, 1, 4, psSHP->fpSHX ) != 4 )
2117  {
2118  char str[128];
2119  snprintf( str, sizeof(str),
2120  "Error in fseek()/fread() reading object from .shx file at offset %d",
2121  100 + 8 * hEntity);
2122  str[sizeof(str)-1] = '\0';
2123 
2124  psSHP->sHooks.Error( str );
2125  return SHPLIB_NULLPTR;
2126  }
2127  if( !bBigEndian ) SwapWord( 4, &nOffset );
2128  if( !bBigEndian ) SwapWord( 4, &nLength );
2129 
2130  if( nOffset > STATIC_CAST(unsigned int, INT_MAX) )
2131  {
2132  char str[128];
2133  snprintf( str, sizeof(str),
2134  "Invalid offset for entity %d", hEntity);
2135  str[sizeof(str)-1] = '\0';
2136 
2137  psSHP->sHooks.Error( str );
2138  return SHPLIB_NULLPTR;
2139  }
2140  if( nLength > STATIC_CAST(unsigned int, INT_MAX / 2 - 4) )
2141  {
2142  char str[128];
2143  snprintf( str, sizeof(str),
2144  "Invalid length for entity %d", hEntity);
2145  str[sizeof(str)-1] = '\0';
2146 
2147  psSHP->sHooks.Error( str );
2148  return SHPLIB_NULLPTR;
2149  }
2150 
2151  psSHP->panRecOffset[hEntity] = nOffset*2;
2152  psSHP->panRecSize[hEntity] = nLength*2;
2153  }
2154 
2155 /* -------------------------------------------------------------------- */
2156 /* Ensure our record buffer is large enough. */
2157 /* -------------------------------------------------------------------- */
2158  nEntitySize = psSHP->panRecSize[hEntity]+8;
2159  if( nEntitySize > psSHP->nBufSize )
2160  {
2161  uchar* pabyRecNew;
2162  int nNewBufSize = nEntitySize;
2163  if( nNewBufSize < INT_MAX - nNewBufSize / 3 )
2164  nNewBufSize += nNewBufSize / 3;
2165  else
2166  nNewBufSize = INT_MAX;
2167 
2168  /* Before allocating too much memory, check that the file is big enough */
2169  /* and do not trust the file size in the header the first time we */
2170  /* need to allocate more than 10 MB */
2171  if( nNewBufSize >= 10 * 1024 * 1024 )
2172  {
2173  if( psSHP->nBufSize < 10 * 1024 * 1024 )
2174  {
2175  SAOffset nFileSize;
2176  psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 2 );
2177  nFileSize = psSHP->sHooks.FTell(psSHP->fpSHP);
2178  if( nFileSize >= UINT_MAX )
2179  psSHP->nFileSize = UINT_MAX;
2180  else
2181  psSHP->nFileSize = STATIC_CAST(unsigned int, nFileSize);
2182  }
2183 
2184  if( psSHP->panRecOffset[hEntity] >= psSHP->nFileSize ||
2185  /* We should normally use nEntitySize instead of*/
2186  /* psSHP->panRecSize[hEntity] in the below test, but because of */
2187  /* the case of non conformant .shx files detailed a bit below, */
2188  /* let be more tolerant */
2189  psSHP->panRecSize[hEntity] > psSHP->nFileSize - psSHP->panRecOffset[hEntity] )
2190  {
2191  char str[128];
2192  snprintf( str, sizeof(str),
2193  "Error in fread() reading object of size %d at offset %u from .shp file",
2194  nEntitySize, psSHP->panRecOffset[hEntity] );
2195  str[sizeof(str)-1] = '\0';
2196 
2197  psSHP->sHooks.Error( str );
2198  return SHPLIB_NULLPTR;
2199  }
2200  }
2201 
2202  pabyRecNew = STATIC_CAST(uchar *, SfRealloc(psSHP->pabyRec,nNewBufSize));
2203  if (pabyRecNew == SHPLIB_NULLPTR)
2204  {
2205  snprintf( szErrorMsg, sizeof(szErrorMsg),
2206  "Not enough memory to allocate requested memory (nNewBufSize=%d). "
2207  "Probably broken SHP file", nNewBufSize);
2208  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2209  psSHP->sHooks.Error( szErrorMsg );
2210  return SHPLIB_NULLPTR;
2211  }
2212 
2213  /* Only set new buffer size after successful alloc */
2214  psSHP->pabyRec = pabyRecNew;
2215  psSHP->nBufSize = nNewBufSize;
2216  }
2217 
2218  /* In case we were not able to reallocate the buffer on a previous step */
2219  if (psSHP->pabyRec == SHPLIB_NULLPTR)
2220  {
2221  return SHPLIB_NULLPTR;
2222  }
2223 
2224 /* -------------------------------------------------------------------- */
2225 /* Read the record. */
2226 /* -------------------------------------------------------------------- */
2227  if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0 )
2228  {
2229  /*
2230  * TODO - mloskot: Consider detailed diagnostics of shape file,
2231  * for example to detect if file is truncated.
2232  */
2233  char str[128];
2234  snprintf( str, sizeof(str),
2235  "Error in fseek() reading object from .shp file at offset %u",
2236  psSHP->panRecOffset[hEntity]);
2237  str[sizeof(str)-1] = '\0';
2238 
2239  psSHP->sHooks.Error( str );
2240  return SHPLIB_NULLPTR;
2241  }
2242 
2243  nBytesRead = STATIC_CAST(int, psSHP->sHooks.FRead( psSHP->pabyRec, 1, nEntitySize, psSHP->fpSHP ));
2244 
2245  /* Special case for a shapefile whose .shx content length field is not equal */
2246  /* to the content length field of the .shp, which is a violation of "The */
2247  /* content length stored in the index record is the same as the value stored in the main */
2248  /* file record header." (http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf, page 24) */
2249  /* Actually in that case the .shx content length is equal to the .shp content length + */
2250  /* 4 (16 bit words), representing the 8 bytes of the record header... */
2251  if( nBytesRead >= 8 && nBytesRead == nEntitySize - 8 )
2252  {
2253  /* Do a sanity check */
2254  int nSHPContentLength;
2255  memcpy( &nSHPContentLength, psSHP->pabyRec + 4, 4 );
2256  if( !bBigEndian ) SwapWord( 4, &(nSHPContentLength) );
2257  if( nSHPContentLength < 0 ||
2258  nSHPContentLength > INT_MAX / 2 - 4 ||
2259  2 * nSHPContentLength + 8 != nBytesRead )
2260  {
2261  char str[128];
2262  snprintf( str, sizeof(str),
2263  "Sanity check failed when trying to recover from inconsistent .shx/.shp with shape %d",
2264  hEntity );
2265  str[sizeof(str)-1] = '\0';
2266 
2267  psSHP->sHooks.Error( str );
2268  return SHPLIB_NULLPTR;
2269  }
2270  }
2271  else if( nBytesRead != nEntitySize )
2272  {
2273  /*
2274  * TODO - mloskot: Consider detailed diagnostics of shape file,
2275  * for example to detect if file is truncated.
2276  */
2277  char str[128];
2278  snprintf( str, sizeof(str),
2279  "Error in fread() reading object of size %d at offset %u from .shp file",
2280  nEntitySize, psSHP->panRecOffset[hEntity] );
2281  str[sizeof(str)-1] = '\0';
2282 
2283  psSHP->sHooks.Error( str );
2284  return SHPLIB_NULLPTR;
2285  }
2286 
2287  if ( 8 + 4 > nEntitySize )
2288  {
2289  snprintf(szErrorMsg, sizeof(szErrorMsg),
2290  "Corrupted .shp file : shape %d : nEntitySize = %d",
2291  hEntity, nEntitySize);
2292  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2293  psSHP->sHooks.Error( szErrorMsg );
2294  return SHPLIB_NULLPTR;
2295  }
2296  memcpy( &nSHPType, psSHP->pabyRec + 8, 4 );
2297 
2298  if( bBigEndian ) SwapWord( 4, &(nSHPType) );
2299 
2300 /* -------------------------------------------------------------------- */
2301 /* Allocate and minimally initialize the object. */
2302 /* -------------------------------------------------------------------- */
2303  if( psSHP->bFastModeReadObject )
2304  {
2305  if( psSHP->psCachedObject->bFastModeReadObject )
2306  {
2307  psSHP->sHooks.Error( "Invalid read pattern in fast read mode. "
2308  "SHPDestroyObject() should be called." );
2309  return SHPLIB_NULLPTR;
2310  }
2311 
2312  psShape = psSHP->psCachedObject;
2313  memset(psShape, 0, sizeof(SHPObject));
2314  }
2315  else
2316  psShape = STATIC_CAST(SHPObject *, calloc(1,sizeof(SHPObject)));
2317  psShape->nShapeId = hEntity;
2318  psShape->nSHPType = nSHPType;
2319  psShape->bMeasureIsUsed = FALSE;
2320  psShape->bFastModeReadObject = psSHP->bFastModeReadObject;
2321 
2322 /* ==================================================================== */
2323 /* Extract vertices for a Polygon or Arc. */
2324 /* ==================================================================== */
2325  if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC
2326  || psShape->nSHPType == SHPT_POLYGONZ
2327  || psShape->nSHPType == SHPT_POLYGONM
2328  || psShape->nSHPType == SHPT_ARCZ
2329  || psShape->nSHPType == SHPT_ARCM
2330  || psShape->nSHPType == SHPT_MULTIPATCH )
2331  {
2332  int32 nPoints, nParts;
2333  int i, nOffset;
2334  unsigned char* pBuffer = SHPLIB_NULLPTR;
2335  unsigned char** ppBuffer = SHPLIB_NULLPTR;
2336 
2337  if ( 40 + 8 + 4 > nEntitySize )
2338  {
2339  snprintf(szErrorMsg, sizeof(szErrorMsg),
2340  "Corrupted .shp file : shape %d : nEntitySize = %d",
2341  hEntity, nEntitySize);
2342  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2343  psSHP->sHooks.Error( szErrorMsg );
2344  SHPDestroyObject(psShape);
2345  return SHPLIB_NULLPTR;
2346  }
2347 /* -------------------------------------------------------------------- */
2348 /* Get the X/Y bounds. */
2349 /* -------------------------------------------------------------------- */
2350  memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 );
2351  memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
2352  memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
2353  memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
2354 
2355  if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
2356  if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
2357  if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
2358  if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
2359 
2360 /* -------------------------------------------------------------------- */
2361 /* Extract part/point count, and build vertex and part arrays */
2362 /* to proper size. */
2363 /* -------------------------------------------------------------------- */
2364  memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 );
2365  memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 );
2366 
2367  if( bBigEndian ) SwapWord( 4, &nPoints );
2368  if( bBigEndian ) SwapWord( 4, &nParts );
2369 
2370  /* nPoints and nParts are unsigned */
2371  if (/* nPoints < 0 || nParts < 0 || */
2372  nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000)
2373  {
2374  snprintf(szErrorMsg, sizeof(szErrorMsg),
2375  "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u.",
2376  hEntity, nPoints, nParts);
2377  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2378  psSHP->sHooks.Error( szErrorMsg );
2379  SHPDestroyObject(psShape);
2380  return SHPLIB_NULLPTR;
2381  }
2382 
2383  /* With the previous checks on nPoints and nParts, */
2384  /* we should not overflow here and after */
2385  /* since 50 M * (16 + 8 + 8) = 1 600 MB */
2386  nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints;
2387  if ( psShape->nSHPType == SHPT_POLYGONZ
2388  || psShape->nSHPType == SHPT_ARCZ
2389  || psShape->nSHPType == SHPT_MULTIPATCH )
2390  {
2391  nRequiredSize += 16 + 8 * nPoints;
2392  }
2393  if( psShape->nSHPType == SHPT_MULTIPATCH )
2394  {
2395  nRequiredSize += 4 * nParts;
2396  }
2397  if (nRequiredSize > nEntitySize)
2398  {
2399  snprintf(szErrorMsg, sizeof(szErrorMsg),
2400  "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u, nEntitySize=%d.",
2401  hEntity, nPoints, nParts, nEntitySize);
2402  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2403  psSHP->sHooks.Error( szErrorMsg );
2404  SHPDestroyObject(psShape);
2405  return SHPLIB_NULLPTR;
2406  }
2407 
2408  if( psShape->bFastModeReadObject )
2409  {
2410  int nObjectBufSize = 4 * sizeof(double) * nPoints + 2 * sizeof(int) * nParts;
2411  pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize);
2412  ppBuffer = &pBuffer;
2413  }
2414 
2415  psShape->nVertices = nPoints;
2416  psShape->padfX = STATIC_CAST(double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2417  psShape->padfY = STATIC_CAST(double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2418  psShape->padfZ = STATIC_CAST(double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2419  psShape->padfM = STATIC_CAST(double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2420 
2421  psShape->nParts = nParts;
2422  psShape->panPartStart = STATIC_CAST(int *, SHPAllocBuffer(ppBuffer, nParts * sizeof(int)));
2423  psShape->panPartType = STATIC_CAST(int *, SHPAllocBuffer(ppBuffer, nParts * sizeof(int)));
2424 
2425  if (psShape->padfX == SHPLIB_NULLPTR ||
2426  psShape->padfY == SHPLIB_NULLPTR ||
2427  psShape->padfZ == SHPLIB_NULLPTR ||
2428  psShape->padfM == SHPLIB_NULLPTR ||
2429  psShape->panPartStart == SHPLIB_NULLPTR ||
2430  psShape->panPartType == SHPLIB_NULLPTR)
2431  {
2432  snprintf(szErrorMsg, sizeof(szErrorMsg),
2433  "Not enough memory to allocate requested memory (nPoints=%u, nParts=%u) for shape %d. "
2434  "Probably broken SHP file", nPoints, nParts, hEntity );
2435  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2436  psSHP->sHooks.Error( szErrorMsg );
2437  SHPDestroyObject(psShape);
2438  return SHPLIB_NULLPTR;
2439  }
2440 
2441  for( i = 0; STATIC_CAST(int32, i) < nParts; i++ )
2442  psShape->panPartType[i] = SHPP_RING;
2443 
2444 /* -------------------------------------------------------------------- */
2445 /* Copy out the part array from the record. */
2446 /* -------------------------------------------------------------------- */
2447  memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts );
2448  for( i = 0; STATIC_CAST(int32, i) < nParts; i++ )
2449  {
2450  if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i );
2451 
2452  /* We check that the offset is inside the vertex array */
2453  if (psShape->panPartStart[i] < 0
2454  || (psShape->panPartStart[i] >= psShape->nVertices
2455  && psShape->nVertices > 0)
2456  || (psShape->panPartStart[i] > 0 && psShape->nVertices == 0) )
2457  {
2458  snprintf(szErrorMsg, sizeof(szErrorMsg),
2459  "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d",
2460  hEntity, i, psShape->panPartStart[i], psShape->nVertices);
2461  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2462  psSHP->sHooks.Error( szErrorMsg );
2463  SHPDestroyObject(psShape);
2464  return SHPLIB_NULLPTR;
2465  }
2466  if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1])
2467  {
2468  snprintf(szErrorMsg, sizeof(szErrorMsg),
2469  "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d",
2470  hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]);
2471  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2472  psSHP->sHooks.Error( szErrorMsg );
2473  SHPDestroyObject(psShape);
2474  return SHPLIB_NULLPTR;
2475  }
2476  }
2477 
2478  nOffset = 44 + 8 + 4*nParts;
2479 
2480 /* -------------------------------------------------------------------- */
2481 /* If this is a multipatch, we will also have parts types. */
2482 /* -------------------------------------------------------------------- */
2483  if( psShape->nSHPType == SHPT_MULTIPATCH )
2484  {
2485  memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts );
2486  for( i = 0; STATIC_CAST(int32, i) < nParts; i++ )
2487  {
2488  if( bBigEndian ) SwapWord( 4, psShape->panPartType+i );
2489  }
2490 
2491  nOffset += 4*nParts;
2492  }
2493 
2494 /* -------------------------------------------------------------------- */
2495 /* Copy out the vertices from the record. */
2496 /* -------------------------------------------------------------------- */
2497  for( i = 0; STATIC_CAST(int32, i) < nPoints; i++ )
2498  {
2499  memcpy(psShape->padfX + i,
2500  psSHP->pabyRec + nOffset + i * 16,
2501  8 );
2502 
2503  memcpy(psShape->padfY + i,
2504  psSHP->pabyRec + nOffset + i * 16 + 8,
2505  8 );
2506 
2507  if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
2508  if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
2509  }
2510 
2511  nOffset += 16*nPoints;
2512 
2513 /* -------------------------------------------------------------------- */
2514 /* If we have a Z coordinate, collect that now. */
2515 /* -------------------------------------------------------------------- */
2516  if( psShape->nSHPType == SHPT_POLYGONZ
2517  || psShape->nSHPType == SHPT_ARCZ
2518  || psShape->nSHPType == SHPT_MULTIPATCH )
2519  {
2520  memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
2521  memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
2522 
2523  if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
2524  if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
2525 
2526  for( i = 0; STATIC_CAST(int32, i) < nPoints; i++ )
2527  {
2528  memcpy( psShape->padfZ + i,
2529  psSHP->pabyRec + nOffset + 16 + i*8, 8 );
2530  if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
2531  }
2532 
2533  nOffset += 16 + 8*nPoints;
2534  }
2535  else if( psShape->bFastModeReadObject )
2536  {
2537  psShape->padfZ = SHPLIB_NULLPTR;
2538  }
2539 
2540 /* -------------------------------------------------------------------- */
2541 /* If we have a M measure value, then read it now. We assume */
2542 /* that the measure can be present for any shape if the size is */
2543 /* big enough, but really it will only occur for the Z shapes */
2544 /* (options), and the M shapes. */
2545 /* -------------------------------------------------------------------- */
2546  if( nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8*nPoints) )
2547  {
2548  memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
2549  memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
2550 
2551  if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
2552  if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
2553 
2554  for( i = 0; STATIC_CAST(int32, i) < nPoints; i++ )
2555  {
2556  memcpy( psShape->padfM + i,
2557  psSHP->pabyRec + nOffset + 16 + i*8, 8 );
2558  if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
2559  }
2560  psShape->bMeasureIsUsed = TRUE;
2561  }
2562  else if( psShape->bFastModeReadObject )
2563  {
2564  psShape->padfM = SHPLIB_NULLPTR;
2565  }
2566  }
2567 
2568 /* ==================================================================== */
2569 /* Extract vertices for a MultiPoint. */
2570 /* ==================================================================== */
2571  else if( psShape->nSHPType == SHPT_MULTIPOINT
2572  || psShape->nSHPType == SHPT_MULTIPOINTM
2573  || psShape->nSHPType == SHPT_MULTIPOINTZ )
2574  {
2575  int32 nPoints;
2576  int i, nOffset;
2577  unsigned char* pBuffer = SHPLIB_NULLPTR;
2578  unsigned char** ppBuffer = SHPLIB_NULLPTR;
2579 
2580  if ( 44 + 4 > nEntitySize )
2581  {
2582  snprintf(szErrorMsg, sizeof(szErrorMsg),
2583  "Corrupted .shp file : shape %d : nEntitySize = %d",
2584  hEntity, nEntitySize);
2585  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2586  psSHP->sHooks.Error( szErrorMsg );
2587  SHPDestroyObject(psShape);
2588  return SHPLIB_NULLPTR;
2589  }
2590  memcpy( &nPoints, psSHP->pabyRec + 44, 4 );
2591 
2592  if( bBigEndian ) SwapWord( 4, &nPoints );
2593 
2594  /* nPoints is unsigned */
2595  if (/* nPoints < 0 || */ nPoints > 50 * 1000 * 1000)
2596  {
2597  snprintf(szErrorMsg, sizeof(szErrorMsg),
2598  "Corrupted .shp file : shape %d : nPoints = %u",
2599  hEntity, nPoints);
2600  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2601  psSHP->sHooks.Error( szErrorMsg );
2602  SHPDestroyObject(psShape);
2603  return SHPLIB_NULLPTR;
2604  }
2605 
2606  nRequiredSize = 48 + nPoints * 16;
2607  if( psShape->nSHPType == SHPT_MULTIPOINTZ )
2608  {
2609  nRequiredSize += 16 + nPoints * 8;
2610  }
2611  if (nRequiredSize > nEntitySize)
2612  {
2613  snprintf(szErrorMsg, sizeof(szErrorMsg),
2614  "Corrupted .shp file : shape %d : nPoints = %u, nEntitySize = %d",
2615  hEntity, nPoints, nEntitySize);
2616  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2617  psSHP->sHooks.Error( szErrorMsg );
2618  SHPDestroyObject(psShape);
2619  return SHPLIB_NULLPTR;
2620  }
2621 
2622  if( psShape->bFastModeReadObject )
2623  {
2624  int nObjectBufSize = 4 * sizeof(double) * nPoints;
2625  pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize);
2626  ppBuffer = &pBuffer;
2627  }
2628 
2629  psShape->nVertices = nPoints;
2630 
2631  psShape->padfX = STATIC_CAST(double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2632  psShape->padfY = STATIC_CAST(double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2633  psShape->padfZ = STATIC_CAST(double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2634  psShape->padfM = STATIC_CAST(double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints));
2635 
2636  if (psShape->padfX == SHPLIB_NULLPTR ||
2637  psShape->padfY == SHPLIB_NULLPTR ||
2638  psShape->padfZ == SHPLIB_NULLPTR ||
2639  psShape->padfM == SHPLIB_NULLPTR)
2640  {
2641  snprintf(szErrorMsg, sizeof(szErrorMsg),
2642  "Not enough memory to allocate requested memory (nPoints=%u) for shape %d. "
2643  "Probably broken SHP file", nPoints, hEntity );
2644  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2645  psSHP->sHooks.Error( szErrorMsg );
2646  SHPDestroyObject(psShape);
2647  return SHPLIB_NULLPTR;
2648  }
2649 
2650  for( i = 0; STATIC_CAST(int32, i) < nPoints; i++ )
2651  {
2652  memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 );
2653  memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 );
2654 
2655  if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
2656  if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
2657  }
2658 
2659  nOffset = 48 + 16*nPoints;
2660 
2661 /* -------------------------------------------------------------------- */
2662 /* Get the X/Y bounds. */
2663 /* -------------------------------------------------------------------- */
2664  memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 );
2665  memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
2666  memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
2667  memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );
2668 
2669  if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
2670  if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
2671  if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
2672  if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );
2673 
2674 /* -------------------------------------------------------------------- */
2675 /* If we have a Z coordinate, collect that now. */
2676 /* -------------------------------------------------------------------- */
2677  if( psShape->nSHPType == SHPT_MULTIPOINTZ )
2678  {
2679  memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
2680  memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );
2681 
2682  if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
2683  if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );
2684 
2685  for( i = 0; STATIC_CAST(int32, i) < nPoints; i++ )
2686  {
2687  memcpy( psShape->padfZ + i,
2688  psSHP->pabyRec + nOffset + 16 + i*8, 8 );
2689  if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
2690  }
2691 
2692  nOffset += 16 + 8*nPoints;
2693  }
2694  else if( psShape->bFastModeReadObject )
2695  psShape->padfZ = SHPLIB_NULLPTR;
2696 
2697 /* -------------------------------------------------------------------- */
2698 /* If we have a M measure value, then read it now. We assume */
2699 /* that the measure can be present for any shape if the size is */
2700 /* big enough, but really it will only occur for the Z shapes */
2701 /* (options), and the M shapes. */
2702 /* -------------------------------------------------------------------- */
2703  if( nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8*nPoints) )
2704  {
2705  memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
2706  memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );
2707 
2708  if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
2709  if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );
2710 
2711  for( i = 0; STATIC_CAST(int32, i) < nPoints; i++ )
2712  {
2713  memcpy( psShape->padfM + i,
2714  psSHP->pabyRec + nOffset + 16 + i*8, 8 );
2715  if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
2716  }
2717  psShape->bMeasureIsUsed = TRUE;
2718  }
2719  else if( psShape->bFastModeReadObject )
2720  psShape->padfM = SHPLIB_NULLPTR;
2721  }
2722 
2723 /* ==================================================================== */
2724 /* Extract vertices for a point. */
2725 /* ==================================================================== */
2726  else if( psShape->nSHPType == SHPT_POINT
2727  || psShape->nSHPType == SHPT_POINTM
2728  || psShape->nSHPType == SHPT_POINTZ )
2729  {
2730  int nOffset;
2731 
2732  psShape->nVertices = 1;
2733  if( psShape->bFastModeReadObject )
2734  {
2735  psShape->padfX = &(psShape->dfXMin);
2736  psShape->padfY = &(psShape->dfYMin);
2737  psShape->padfZ = &(psShape->dfZMin);
2738  psShape->padfM = &(psShape->dfMMin);
2739  psShape->padfZ[0] = 0.0;
2740  psShape->padfM[0] = 0.0;
2741  }
2742  else
2743  {
2744  psShape->padfX = STATIC_CAST(double *, calloc(1,sizeof(double)));
2745  psShape->padfY = STATIC_CAST(double *, calloc(1,sizeof(double)));
2746  psShape->padfZ = STATIC_CAST(double *, calloc(1,sizeof(double)));
2747  psShape->padfM = STATIC_CAST(double *, calloc(1,sizeof(double)));
2748  }
2749 
2750  if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize)
2751  {
2752  snprintf(szErrorMsg, sizeof(szErrorMsg),
2753  "Corrupted .shp file : shape %d : nEntitySize = %d",
2754  hEntity, nEntitySize);
2755  szErrorMsg[sizeof(szErrorMsg)-1] = '\0';
2756  psSHP->sHooks.Error( szErrorMsg );
2757  SHPDestroyObject(psShape);
2758  return SHPLIB_NULLPTR;
2759  }
2760  memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 );
2761  memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 );
2762 
2763  if( bBigEndian ) SwapWord( 8, psShape->padfX );
2764  if( bBigEndian ) SwapWord( 8, psShape->padfY );
2765 
2766  nOffset = 20 + 8;
2767 
2768 /* -------------------------------------------------------------------- */
2769 /* If we have a Z coordinate, collect that now. */
2770 /* -------------------------------------------------------------------- */
2771  if( psShape->nSHPType == SHPT_POINTZ )
2772  {
2773  memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 );
2774 
2775  if( bBigEndian ) SwapWord( 8, psShape->padfZ );
2776 
2777  nOffset += 8;
2778  }
2779 
2780 /* -------------------------------------------------------------------- */
2781 /* If we have a M measure value, then read it now. We assume */
2782 /* that the measure can be present for any shape if the size is */
2783 /* big enough, but really it will only occur for the Z shapes */
2784 /* (options), and the M shapes. */
2785 /* -------------------------------------------------------------------- */
2786  if( nEntitySize >= nOffset + 8 )
2787  {
2788  memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 );
2789 
2790  if( bBigEndian ) SwapWord( 8, psShape->padfM );
2791  psShape->bMeasureIsUsed = TRUE;
2792  }
2793 
2794 /* -------------------------------------------------------------------- */
2795 /* Since no extents are supplied in the record, we will apply */
2796 /* them from the single vertex. */
2797 /* -------------------------------------------------------------------- */
2798  psShape->dfXMin = psShape->dfXMax = psShape->padfX[0];
2799  psShape->dfYMin = psShape->dfYMax = psShape->padfY[0];
2800  psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0];
2801  psShape->dfMMin = psShape->dfMMax = psShape->padfM[0];
2802  }
2803 
2804  return( psShape );
2805 }
2806 
2807 /************************************************************************/
2808 /* SHPTypeName() */
2809 /************************************************************************/
2810 
2811 const char SHPAPI_CALL1(*)
2812 SHPTypeName( int nSHPType )
2813 
2814 {
2815  switch( nSHPType )
2816  {
2817  case SHPT_NULL:
2818  return "NullShape";
2819 
2820  case SHPT_POINT:
2821  return "Point";
2822 
2823  case SHPT_ARC:
2824  return "Arc";
2825 
2826  case SHPT_POLYGON:
2827  return "Polygon";
2828 
2829  case SHPT_MULTIPOINT:
2830  return "MultiPoint";
2831 
2832  case SHPT_POINTZ:
2833  return "PointZ";
2834 
2835  case SHPT_ARCZ:
2836  return "ArcZ";
2837 
2838  case SHPT_POLYGONZ:
2839  return "PolygonZ";
2840 
2841  case SHPT_MULTIPOINTZ:
2842  return "MultiPointZ";
2843 
2844  case SHPT_POINTM:
2845  return "PointM";
2846 
2847  case SHPT_ARCM:
2848  return "ArcM";
2849 
2850  case SHPT_POLYGONM:
2851  return "PolygonM";
2852 
2853  case SHPT_MULTIPOINTM:
2854  return "MultiPointM";
2855 
2856  case SHPT_MULTIPATCH:
2857  return "MultiPatch";
2858 
2859  default:
2860  return "UnknownShapeType";
2861  }
2862 }
2863 
2864 /************************************************************************/
2865 /* SHPPartTypeName() */
2866 /************************************************************************/
2867 
2868 const char SHPAPI_CALL1(*)
2869 SHPPartTypeName( int nPartType )
2870 
2871 {
2872  switch( nPartType )
2873  {
2874  case SHPP_TRISTRIP:
2875  return "TriangleStrip";
2876 
2877  case SHPP_TRIFAN:
2878  return "TriangleFan";
2879 
2880  case SHPP_OUTERRING:
2881  return "OuterRing";
2882 
2883  case SHPP_INNERRING:
2884  return "InnerRing";
2885 
2886  case SHPP_FIRSTRING:
2887  return "FirstRing";
2888 
2889  case SHPP_RING:
2890  return "Ring";
2891 
2892  default:
2893  return "UnknownPartType";
2894  }
2895 }
2896 
2897 /************************************************************************/
2898 /* SHPDestroyObject() */
2899 /************************************************************************/
2900 
2901 void SHPAPI_CALL
2903 
2904 {
2905  if( psShape == SHPLIB_NULLPTR )
2906  return;
2907 
2908  if( psShape->bFastModeReadObject )
2909  {
2910  psShape->bFastModeReadObject = FALSE;
2911  return;
2912  }
2913 
2914  if( psShape->padfX != SHPLIB_NULLPTR )
2915  free( psShape->padfX );
2916  if( psShape->padfY != SHPLIB_NULLPTR )
2917  free( psShape->padfY );
2918  if( psShape->padfZ != SHPLIB_NULLPTR )
2919  free( psShape->padfZ );
2920  if( psShape->padfM != SHPLIB_NULLPTR )
2921  free( psShape->padfM );
2922 
2923  if( psShape->panPartStart != SHPLIB_NULLPTR )
2924  free( psShape->panPartStart );
2925  if( psShape->panPartType != SHPLIB_NULLPTR )
2926  free( psShape->panPartType );
2927 
2928  free( psShape );
2929 }
2930 
2931 /************************************************************************/
2932 /* SHPGetPartVertexCount() */
2933 /************************************************************************/
2934 
2935 static int SHPGetPartVertexCount( const SHPObject * psObject, int iPart )
2936 {
2937  if( iPart == psObject->nParts-1 )
2938  return psObject->nVertices - psObject->panPartStart[iPart];
2939  else
2940  return psObject->panPartStart[iPart+1] - psObject->panPartStart[iPart];
2941 }
2942 
2943 /************************************************************************/
2944 /* SHPRewindIsInnerRing() */
2945 /************************************************************************/
2946 
2947 static int SHPRewindIsInnerRing( const SHPObject * psObject,
2948  int iOpRing )
2949 {
2950 /* -------------------------------------------------------------------- */
2951 /* Determine if this ring is an inner ring or an outer ring */
2952 /* relative to all the other rings. For now we assume the */
2953 /* first ring is outer and all others are inner, but eventually */
2954 /* we need to fix this to handle multiple island polygons and */
2955 /* unordered sets of rings. */
2956 /* */
2957 /* -------------------------------------------------------------------- */
2958 
2959  /* Use point in the middle of segment to avoid testing
2960  * common points of rings.
2961  */
2962  const int iOpRingStart = psObject->panPartStart[iOpRing];
2963  double dfTestX = ( psObject->padfX[iOpRingStart] +
2964  psObject->padfX[iOpRingStart + 1] ) / 2;
2965  double dfTestY = ( psObject->padfY[iOpRingStart] +
2966  psObject->padfY[iOpRingStart + 1] ) / 2;
2967 
2968  int bInner = FALSE;
2969  int iCheckRing;
2970  for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ )
2971  {
2972  int nVertStartCheck, nVertCountCheck;
2973  int iEdge;
2974 
2975  if( iCheckRing == iOpRing )
2976  continue;
2977 
2978  nVertStartCheck = psObject->panPartStart[iCheckRing];
2979  nVertCountCheck = SHPGetPartVertexCount(psObject, iCheckRing);
2980 
2981  for( iEdge = 0; iEdge < nVertCountCheck; iEdge++ )
2982  {
2983  int iNext;
2984 
2985  if( iEdge < nVertCountCheck-1 )
2986  iNext = iEdge+1;
2987  else
2988  iNext = 0;
2989 
2990  /* Rule #1:
2991  * Test whether the edge 'straddles' the horizontal ray from
2992  * the test point (dfTestY,dfTestY)
2993  * The rule #1 also excludes edges colinear with the ray.
2994  */
2995  if ( ( psObject->padfY[iEdge+nVertStartCheck] < dfTestY
2996  && dfTestY <= psObject->padfY[iNext+nVertStartCheck] )
2997  || ( psObject->padfY[iNext+nVertStartCheck] < dfTestY
2998  && dfTestY <= psObject->padfY[iEdge+nVertStartCheck] ) )
2999  {
3000  /* Rule #2:
3001  * Test if edge-ray intersection is on the right from the
3002  * test point (dfTestY,dfTestY)
3003  */
3004  double const intersect =
3005  ( psObject->padfX[iEdge+nVertStartCheck]
3006  + ( dfTestY - psObject->padfY[iEdge+nVertStartCheck] )
3007  / ( psObject->padfY[iNext+nVertStartCheck] -
3008  psObject->padfY[iEdge+nVertStartCheck] )
3009  * ( psObject->padfX[iNext+nVertStartCheck] -
3010  psObject->padfX[iEdge+nVertStartCheck] ) );
3011 
3012  if (intersect < dfTestX)
3013  {
3014  bInner = !bInner;
3015  }
3016  }
3017  }
3018  } /* for iCheckRing */
3019  return bInner;
3020 }
3021 
3022 /************************************************************************/
3023 /* SHPRewindObject() */
3024 /* */
3025 /* reset the winding of polygon objects to adhere to the */
3026 /* specification. */
3027 /************************************************************************/
3028 
3029 int SHPAPI_CALL
3031  SHPObject * psObject )
3032 {
3033  int iOpRing, bAltered = 0;
3034 
3035 /* -------------------------------------------------------------------- */
3036 /* Do nothing if this is not a polygon object. */
3037 /* -------------------------------------------------------------------- */
3038  if( psObject->nSHPType != SHPT_POLYGON
3039  && psObject->nSHPType != SHPT_POLYGONZ
3040  && psObject->nSHPType != SHPT_POLYGONM )
3041  return 0;
3042 
3043  if( psObject->nVertices == 0 || psObject->nParts == 0 )
3044  return 0;
3045 
3046 /* -------------------------------------------------------------------- */
3047 /* Process each of the rings. */
3048 /* -------------------------------------------------------------------- */
3049  for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ )
3050  {
3051  int bInner, iVert, nVertCount, nVertStart;
3052  double dfSum;
3053 
3054  nVertStart = psObject->panPartStart[iOpRing];
3055  nVertCount = SHPGetPartVertexCount(psObject, iOpRing);
3056 
3057  if (nVertCount < 2)
3058  continue;
3059 
3060  bInner = SHPRewindIsInnerRing(psObject, iOpRing);
3061 
3062 /* -------------------------------------------------------------------- */
3063 /* Determine the current order of this ring so we will know if */
3064 /* it has to be reversed. */
3065 /* -------------------------------------------------------------------- */
3066 
3067  dfSum = psObject->padfX[nVertStart] *
3068  (psObject->padfY[nVertStart+1] -
3069  psObject->padfY[nVertStart+nVertCount-1]);
3070  for( iVert = nVertStart + 1; iVert < nVertStart+nVertCount-1; iVert++ )
3071  {
3072  dfSum += psObject->padfX[iVert] * (psObject->padfY[iVert+1] -
3073  psObject->padfY[iVert-1]);
3074  }
3075 
3076  dfSum += psObject->padfX[iVert] * (psObject->padfY[nVertStart] -
3077  psObject->padfY[iVert-1]);
3078 
3079 /* -------------------------------------------------------------------- */
3080 /* Reverse if necessary. */
3081 /* -------------------------------------------------------------------- */
3082  if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) )
3083  {
3084  int i;
3085 
3086  bAltered++;
3087  for( i = 0; i < nVertCount/2; i++ )
3088  {
3089  double dfSaved;
3090 
3091  /* Swap X */
3092  dfSaved = psObject->padfX[nVertStart+i];
3093  psObject->padfX[nVertStart+i] =
3094  psObject->padfX[nVertStart+nVertCount-i-1];
3095  psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved;
3096 
3097  /* Swap Y */
3098  dfSaved = psObject->padfY[nVertStart+i];
3099  psObject->padfY[nVertStart+i] =
3100  psObject->padfY[nVertStart+nVertCount-i-1];
3101  psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved;
3102 
3103  /* Swap Z */
3104  if( psObject->padfZ )
3105  {
3106  dfSaved = psObject->padfZ[nVertStart+i];
3107  psObject->padfZ[nVertStart+i] =
3108  psObject->padfZ[nVertStart+nVertCount-i-1];
3109  psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved;
3110  }
3111 
3112  /* Swap M */
3113  if( psObject->padfM )
3114  {
3115  dfSaved = psObject->padfM[nVertStart+i];
3116  psObject->padfM[nVertStart+i] =
3117  psObject->padfM[nVertStart+nVertCount-i-1];
3118  psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved;
3119  }
3120  }
3121  }
3122  }
3123 
3124  return bAltered;
3125 }
void SASetupDefaultHooks(SAHooks *psHooks)
Definition: safileio.c:184
#define SHPT_ARCZ
Definition: shapefil.h:357
#define SHPT_MULTIPATCH
Definition: shapefil.h:364
#define SHPP_OUTERRING
Definition: shapefil.h:373
#define SHPT_NULL
Definition: shapefil.h:351
#define SHPP_FIRSTRING
Definition: shapefil.h:375
#define SHPT_ARCM
Definition: shapefil.h:361
#define SHPT_POLYGONM
Definition: shapefil.h:362
#define SHP_CVSID(string)
Definition: shapefil.h:267
#define SHPT_ARC
Definition: shapefil.h:353
#define SHPT_POLYGON
Definition: shapefil.h:354
#define SHPP_RING
Definition: shapefil.h:376
#define DISABLE_MULTIPATCH_MEASURE
Definition: shapefil.h:209
#define SHPP_TRIFAN
Definition: shapefil.h:372
#define SHPT_MULTIPOINT
Definition: shapefil.h:355
int * SAFile
Definition: shapefil.h:287
#define SHPT_POINTZ
Definition: shapefil.h:356
#define SHPT_MULTIPOINTZ
Definition: shapefil.h:359
#define SHPAPI_CALL
Definition: shapefil.h:250
#define SHPAPI_CALL1(x)
Definition: shapefil.h:255
#define SHPP_TRISTRIP
Definition: shapefil.h:371
#define SHPT_MULTIPOINTM
Definition: shapefil.h:363
#define SHPT_POINTM
Definition: shapefil.h:360
#define SHPT_POINT
Definition: shapefil.h:352
#define SHPT_POLYGONZ
Definition: shapefil.h:358
#define SHPP_INNERRING
Definition: shapefil.h:374
unsigned long SAOffset
Definition: shapefil.h:290
static unsigned char * SHPReallocObjectBufIfNecessary(SHPHandle psSHP, int nObjectBufSize)
Definition: shpopen.c:2062
SHPHandle SHPOpenLLEx(const char *pszLayer, const char *pszAccess, SAHooks *psHooks, int bRestoreSHX)
Definition: shpopen.c:942
SHPObject * SHPReadObject(SHPHandle psSHP, int hEntity)
Definition: shpopen.c:2092
static int bBigEndian
Definition: shpopen.c:363
SHPObject * SHPCreateSimpleObject(int nSHPType, int nVertices, const double *padfX, const double *padfY, const double *padfZ)
Definition: shpopen.c:1587
unsigned int int32
Definition: shpopen.c:324
SHPObject * SHPCreateObject(int nSHPType, int nShapeId, int nParts, const int *panPartStart, const int *panPartType, int nVertices, const double *padfX, const double *padfY, const double *padfZ, const double *padfM)
Definition: shpopen.c:1469
void SHPClose(SHPHandle psSHP)
Definition: shpopen.c:1149
#define MIN(a, b)
Definition: shpopen.c:334
SHPHandle SHPCreateLL(const char *pszLayer, int nShapeType, SAHooks *psHooks)
Definition: shpopen.c:1267
int SHPRestoreSHX(const char *pszLayer, const char *pszAccess, SAHooks *psHooks)
Definition: shpopen.c:966
SHPHandle SHPOpenLL(const char *pszLayer, const char *pszAccess, SAHooks *psHooks)
Definition: shpopen.c:599
#define CPL_UNUSED
Definition: shpopen.c:354
void SHPWriteHeader(SHPHandle psSHP)
Definition: shpopen.c:417
unsigned char uchar
Definition: shpopen.c:319
void SHPDestroyObject(SHPObject *psShape)
Definition: shpopen.c:2902
static void SwapWord(int length, void *wordP)
Definition: shpopen.c:380
void SHPGetInfo(SHPHandle psSHP, int *pnEntities, int *pnShapeType, double *padfMinBound, double *padfMaxBound)
Definition: shpopen.c:1217
static int SHPGetLenWithoutExtension(const char *pszBasename)
Definition: shpopen.c:575
#define STATIC_CAST(type, x)
Definition: shpopen.c:370
const char * SHPPartTypeName(int nPartType)
Definition: shpopen.c:2869
static int SHPGetPartVertexCount(const SHPObject *psObject, int iPart)
Definition: shpopen.c:2935
#define TRUE
Definition: shpopen.c:329
#define FALSE
Definition: shpopen.c:328
SHPHandle SHPOpen(const char *pszLayer, const char *pszAccess)
Definition: shpopen.c:561
int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject *psObject)
Definition: shpopen.c:1604
static void * SHPAllocBuffer(unsigned char **pBuffer, int nSize)
Definition: shpopen.c:2043
static void * SfRealloc(void *pMem, int nNewSize)
Definition: shpopen.c:401
static void _SHPSetBounds(uchar *pabyRec, SHPObject *psShape)
Definition: shpopen.c:1406
void SHPSetFastModeReadObject(SHPHandle hSHP, int bFastMode)
Definition: shpopen.c:1196
void SHPComputeExtents(SHPObject *psObject)
Definition: shpopen.c:1431
#define SHPLIB_NULLPTR
Definition: shpopen.c:371
static int SHPRewindIsInnerRing(const SHPObject *psObject, int iOpRing)
Definition: shpopen.c:2947
#define ByteCopy(a, b, c)
Definition: shpopen.c:332
SHPHandle SHPCreate(const char *pszLayer, int nShapeType)
Definition: shpopen.c:1249
const char * SHPTypeName(int nSHPType)
Definition: shpopen.c:2812
int SHPRewindObject(SHPHandle hSHP, SHPObject *psObject)
Definition: shpopen.c:3030
#define MAX(a, b)
Definition: shpopen.c:335
void(* Error)(const char *message)
Definition: shapefil.h:303
SAFile(* FOpen)(const char *filename, const char *access)
Definition: shapefil.h:294
SAOffset(* FTell)(SAFile file)
Definition: shapefil.h:298
int(* FFlush)(SAFile file)
Definition: shapefil.h:299
SAOffset(* FWrite)(void *p, SAOffset size, SAOffset nmemb, SAFile file)
Definition: shapefil.h:296
int(* FClose)(SAFile file)
Definition: shapefil.h:300
SAOffset(* FRead)(void *p, SAOffset size, SAOffset nmemb, SAFile file)
Definition: shapefil.h:295
SAOffset(* FSeek)(SAFile file, SAOffset offset, int whence)
Definition: shapefil.h:297
SAFile fpSHX
Definition: shapefil.h:321
int nShapeType
Definition: shapefil.h:323
SAFile fpSHP
Definition: shapefil.h:320
int nMaxRecords
Definition: shapefil.h:328
SHPObject * psCachedObject
Definition: shapefil.h:343
unsigned int * panRecSize
Definition: shapefil.h:330
SAHooks sHooks
Definition: shapefil.h:318
double adBoundsMin[4]
Definition: shapefil.h:332
int nRecords
Definition: shapefil.h:327
unsigned char * pabyObjectBuf
Definition: shapefil.h:341
int bUpdated
Definition: shapefil.h:335
int nObjectBufSize
Definition: shapefil.h:342
unsigned int nFileSize
Definition: shapefil.h:325
unsigned int * panRecOffset
Definition: shapefil.h:329
int bFastModeReadObject
Definition: shapefil.h:340
unsigned char * pabyRec
Definition: shapefil.h:337
double adBoundsMax[4]
Definition: shapefil.h:333
int bFastModeReadObject
Definition: shapefil.h:408
double dfYMax
Definition: shapefil.h:403
double * padfX
Definition: shapefil.h:392
double dfXMin
Definition: shapefil.h:397
int * panPartType
Definition: shapefil.h:389
int nVertices
Definition: shapefil.h:391
int nShapeId
Definition: shapefil.h:385
double dfYMin
Definition: shapefil.h:398
double * padfY
Definition: shapefil.h:393
int nSHPType
Definition: shapefil.h:383
double dfMMax
Definition: shapefil.h:405
double * padfZ
Definition: shapefil.h:394
double dfZMax
Definition: shapefil.h:404
double dfXMax
Definition: shapefil.h:402
int * panPartStart
Definition: shapefil.h:388
double * padfM
Definition: shapefil.h:395
double dfMMin
Definition: shapefil.h:400
double dfZMin
Definition: shapefil.h:399
int bMeasureIsUsed
Definition: shapefil.h:407