source: TI02-CSML/trunk/services/3rdParty/Quadtree-0.1.2/shapelib/shpopen.c @ 2194

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI02-CSML/trunk/services/3rdParty/Quadtree-0.1.2/shapelib/shpopen.c@2194
Revision 2194, 67.4 KB checked in by lawrence, 13 years ago (diff)

Adding various specs and 3rd party code of interest for the CSML
services development.

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