~librecad-dev/librecad/librecad

« back to all changes in this revision

Viewing changes to plugins/importshp/shapelib/shpopen.c

  • Committer: Scott Howard
  • Date: 2014-02-21 19:07:55 UTC
  • Revision ID: showard@debian.org-20140221190755-csjax9wb146hgdq4
first commit

Show diffs side-by-side

added added

removed removed

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