source: trunk/mapserver/mapshape.c

Last change on this file was 12629, checked in by pramsey, 5 years ago

Remove C++ comment line warnings and struct initialization warnings. GCC 4.2.1.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 95.7 KB
Line 
1/******************************************************************************
2 * $Id: mapshape.c 12629 2011-10-06 18:06:34Z capooti $
3 *
4 * Project:  MapServer
5 * Purpose:  Implements support for shapefile access.
6 * Authors:  Steve Lime and Frank Warmerdam
7 *
8 * Note:
9 * This code is entirely based on the previous work of Frank Warmerdam. It is
10 * essentially shapelib 1.1.5. However, there were enough changes that it was
11 * incorporated into the MapServer source to avoid confusion. Relicensed with
12 * permission of Frank Warmerdam (shapelib author). See the README
13 * for licence details.
14 *
15 ******************************************************************************
16 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
17 *
18 * Permission is hereby granted, free of charge, to any person obtaining a
19 * copy of this software and associated documentation files (the "Software"),
20 * to deal in the Software without restriction, including without limitation
21 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
22 * and/or sell copies of the Software, and to permit persons to whom the
23 * Software is furnished to do so, subject to the following conditions:
24 *
25 * The above copyright notice and this permission notice shall be included in
26 * all copies of this Software or works derived from this Software.
27 *
28 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
29 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
31 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
33 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
34 * DEALINGS IN THE SOFTWARE.
35 ****************************************************************************/
36
37#include <limits.h>
38#include <assert.h>
39#include "mapserver.h"
40
41MS_CVSID("$Id: mapshape.c 12629 2011-10-06 18:06:34Z capooti $")
42
43/* Only use this macro on 32-bit integers! */
44#define SWAP_FOUR_BYTES(data) \
45  ( ((data >> 24) & 0x000000FF) | ((data >>  8) & 0x0000FF00) | \
46    ((data <<  8) & 0x00FF0000) | ((data << 24) & 0xFF000000) )
47
48
49#define ByteCopy( a, b, c )     memcpy( b, a, c )
50
51static int      bBigEndian;
52
53/************************************************************************/
54/*                              SwapWord()                              */
55/*                                                                      */
56/*      Swap a 2, 4 or 8 byte word.                                     */
57/************************************************************************/
58static void SwapWord( int length, void * wordP )
59{
60  int i;
61  uchar temp;
62 
63  for( i=0; i < length/2; i++ ) {
64    temp = ((uchar *) wordP)[i];
65    ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1];
66    ((uchar *) wordP)[length-i-1] = temp;
67  }
68}
69
70/************************************************************************/
71/*                             SfRealloc()                              */
72/*                                                                      */
73/*      A realloc cover function that will access a NULL pointer as     */
74/*      a valid input.                                                  */
75/************************************************************************/
76static void * SfRealloc( void * pMem, int nNewSize )     
77{
78  if( pMem == NULL )
79    return( (void *) malloc(nNewSize) );
80  else
81    return( (void *) realloc(pMem,nNewSize) );
82}
83
84/************************************************************************/
85/*                          writeHeader()                               */
86/*                                                                      */
87/*      Write out a header for the .shp and .shx files as well as the   */
88/*      contents of the index (.shx) file.                              */
89/************************************************************************/
90static void writeHeader( SHPHandle psSHP )
91{
92  uchar abyHeader[100];
93  int   i;
94  ms_int32 i32;
95  double dValue;
96  ms_int32 *panSHX;
97 
98  /* -------------------------------------------------------------------- */
99  /*      Prepare header block for .shp file.                             */
100  /* -------------------------------------------------------------------- */
101  for( i = 0; i < 100; i++ )
102    abyHeader[i] = 0;
103 
104  abyHeader[2] = 0x27;                          /* magic cookie */
105  abyHeader[3] = 0x0a;
106 
107  i32 = psSHP->nFileSize/2;                             /* file size */
108  ByteCopy( &i32, abyHeader+24, 4 );
109  if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
110   
111  i32 = 1000;                                           /* version */
112  ByteCopy( &i32, abyHeader+28, 4 );
113  if( bBigEndian ) SwapWord( 4, abyHeader+28 );
114   
115  i32 = psSHP->nShapeType;                              /* shape type */
116  ByteCopy( &i32, abyHeader+32, 4 );
117  if( bBigEndian ) SwapWord( 4, abyHeader+32 );
118   
119  dValue = psSHP->adBoundsMin[0];                       /* set bounds */
120  ByteCopy( &dValue, abyHeader+36, 8 );
121  if( bBigEndian ) SwapWord( 8, abyHeader+36 );
122 
123  dValue = psSHP->adBoundsMin[1];
124  ByteCopy( &dValue, abyHeader+44, 8 );
125  if( bBigEndian ) SwapWord( 8, abyHeader+44 );
126 
127  dValue = psSHP->adBoundsMax[0];
128  ByteCopy( &dValue, abyHeader+52, 8 );
129  if( bBigEndian ) SwapWord( 8, abyHeader+52 );
130 
131  dValue = psSHP->adBoundsMax[1];
132  ByteCopy( &dValue, abyHeader+60, 8 );
133  if( bBigEndian ) SwapWord( 8, abyHeader+60 );
134
135  dValue = psSHP->adBoundsMin[2];                       /* z */
136  ByteCopy( &dValue, abyHeader+68, 8 );
137  if( bBigEndian ) SwapWord( 8, abyHeader+68 );
138
139  dValue = psSHP->adBoundsMax[2];
140  ByteCopy( &dValue, abyHeader+76, 8 );
141  if( bBigEndian ) SwapWord( 8, abyHeader+76 );
142
143 
144  dValue = psSHP->adBoundsMin[3];                       /* m */
145  ByteCopy( &dValue, abyHeader+84, 8 );
146  if( bBigEndian ) SwapWord( 8, abyHeader+84 );
147
148  dValue = psSHP->adBoundsMax[3];
149  ByteCopy( &dValue, abyHeader+92, 8 );
150  if( bBigEndian ) SwapWord( 8, abyHeader+92 );
151
152  /* -------------------------------------------------------------------- */
153  /*      Write .shp file header.                                         */
154  /* -------------------------------------------------------------------- */
155  fseek( psSHP->fpSHP, 0, 0 );
156  fwrite( abyHeader, 100, 1, psSHP->fpSHP );
157 
158  /* -------------------------------------------------------------------- */
159  /*      Prepare, and write .shx file header.                            */
160  /* -------------------------------------------------------------------- */
161  i32 = (psSHP->nRecords * 2 * sizeof(ms_int32) + 100)/2;   /* file size */
162  ByteCopy( &i32, abyHeader+24, 4 );
163  if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
164 
165  fseek( psSHP->fpSHX, 0, 0 );
166  fwrite( abyHeader, 100, 1, psSHP->fpSHX );
167 
168  /* -------------------------------------------------------------------- */
169  /*      Write out the .shx contents.                                    */
170  /* -------------------------------------------------------------------- */
171  panSHX = (ms_int32 *) msSmallMalloc(sizeof(ms_int32) * 2 * psSHP->nRecords);
172 
173  for( i = 0; i < psSHP->nRecords; i++ ) {
174    panSHX[i*2  ] = psSHP->panRecOffset[i]/2;
175    panSHX[i*2+1] = psSHP->panRecSize[i]/2;
176    if( !bBigEndian ) {
177      *(panSHX+i*2) = SWAP_FOUR_BYTES(*(panSHX+i*2));
178      *(panSHX+i*2+1) = SWAP_FOUR_BYTES(*(panSHX+i*2+1));
179    }
180  }
181 
182  fwrite( panSHX, sizeof(ms_int32) * 2, psSHP->nRecords, psSHP->fpSHX );
183 
184  free( panSHX );
185}
186
187/************************************************************************/
188/*                              msSHPOpen()                             */
189/*                                                                      */
190/*      Open the .shp and .shx files based on the basename of the       */
191/*      files or either file name.                                      */
192/************************************************************************/   
193SHPHandle msSHPOpen( const char * pszLayer, const char * pszAccess )
194{
195  char *pszFullname, *pszBasename;
196  SHPHandle     psSHP;
197 
198  uchar *pabyBuf;
199  int   i;
200  double dValue;
201
202  /* -------------------------------------------------------------------- */
203  /*      Ensure the access string is one of the legal ones.  We          */
204  /*      ensure the result string indicates binary to avoid common       */
205  /*      problems on Windows.                                            */
206  /* -------------------------------------------------------------------- */
207  if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 || strcmp(pszAccess,"r+") == 0 )
208    pszAccess = "r+b";
209  else
210    pszAccess = "rb";
211 
212  /* -------------------------------------------------------------------- */
213  /*    Establish the byte order on this machine.                           */
214  /* -------------------------------------------------------------------- */
215  i = 1;
216  if( *((uchar *) &i) == 1 )
217    bBigEndian = MS_FALSE;
218  else
219    bBigEndian = MS_TRUE;
220 
221  /* -------------------------------------------------------------------- */
222  /*    Initialize the info structure.                                      */
223  /* -------------------------------------------------------------------- */
224  psSHP = (SHPHandle) msSmallMalloc(sizeof(SHPInfo));
225 
226  psSHP->bUpdated = MS_FALSE;
227
228  psSHP->pabyRec = NULL;
229  psSHP->panParts = NULL;
230  psSHP->nBufSize = psSHP->nPartMax = 0;
231
232  /* -------------------------------------------------------------------- */
233  /*    Compute the base (layer) name.  If there is any extension           */
234  /*    on the passed in filename we will strip it off.                     */
235  /* -------------------------------------------------------------------- */
236  pszBasename = (char *) msSmallMalloc(strlen(pszLayer)+5);
237  strcpy( pszBasename, pszLayer );
238  for( i = strlen(pszBasename)-1; 
239       i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\';
240       i-- ) {}
241 
242  if( pszBasename[i] == '.' )
243    pszBasename[i] = '\0';
244 
245  /* -------------------------------------------------------------------- */
246  /*    Open the .shp and .shx files.  Note that files pulled from          */
247  /*    a PC to Unix with upper case filenames won't work!                  */
248  /* -------------------------------------------------------------------- */
249  pszFullname = (char *) msSmallMalloc(strlen(pszBasename) + 5);
250  sprintf( pszFullname, "%s.shp", pszBasename );
251  psSHP->fpSHP = fopen(pszFullname, pszAccess );
252  if( psSHP->fpSHP == NULL ) {
253    msFree(pszBasename);
254    msFree(pszFullname);
255    msFree(psSHP);
256    return( NULL );
257  }
258
259  sprintf( pszFullname, "%s.shx", pszBasename );
260  psSHP->fpSHX = fopen(pszFullname, pszAccess );
261  if( psSHP->fpSHX == NULL ) {
262    msFree(pszBasename);
263    msFree(pszFullname);
264    msFree(psSHP);
265    return( NULL );
266  }
267 
268  free( pszFullname );
269  free( pszBasename ); 
270
271  /* -------------------------------------------------------------------- */
272  /*   Read the file size from the SHP file.                                */
273  /* -------------------------------------------------------------------- */
274  pabyBuf = (uchar *) msSmallMalloc(100);
275  fread( pabyBuf, 100, 1, psSHP->fpSHP );
276 
277  psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256
278                      + pabyBuf[25] * 256 * 256
279                      + pabyBuf[26] * 256
280                      + pabyBuf[27]) * 2;
281 
282  /* -------------------------------------------------------------------- */
283  /*  Read SHX file Header info                                           */
284  /* -------------------------------------------------------------------- */
285  fread( pabyBuf, 100, 1, psSHP->fpSHX );
286 
287  if( pabyBuf[0] != 0 || pabyBuf[1] != 0 || pabyBuf[2] != 0x27  || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) ) {
288    fclose( psSHP->fpSHP );
289    fclose( psSHP->fpSHX );
290    free( psSHP );
291     
292    return( NULL );
293  }
294
295  psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256 + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256;
296  if (psSHP->nRecords != 0)
297      psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8;
298
299  if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 )
300  {
301    msSetError(MS_SHPERR, "Corrupted .shp file : nRecords = %d.", "msSHPOpen()",
302               psSHP->nRecords);
303    fclose( psSHP->fpSHP );
304    fclose( psSHP->fpSHX );
305    free( psSHP );
306    return( NULL );
307  }
308
309  psSHP->nShapeType = pabyBuf[32];
310 
311  if( bBigEndian ) SwapWord( 8, pabyBuf+36 );
312  memcpy( &dValue, pabyBuf+36, 8 );
313  psSHP->adBoundsMin[0] = dValue;
314 
315  if( bBigEndian ) SwapWord( 8, pabyBuf+44 );
316  memcpy( &dValue, pabyBuf+44, 8 );
317  psSHP->adBoundsMin[1] = dValue;
318 
319  if( bBigEndian ) SwapWord( 8, pabyBuf+52 );
320  memcpy( &dValue, pabyBuf+52, 8 );
321  psSHP->adBoundsMax[0] = dValue;
322 
323  if( bBigEndian ) SwapWord( 8, pabyBuf+60 );
324  memcpy( &dValue, pabyBuf+60, 8 );
325  psSHP->adBoundsMax[1] = dValue;
326 
327  if( bBigEndian ) SwapWord( 8, pabyBuf+68 );           /* z */
328  memcpy( &dValue, pabyBuf+68, 8 );
329  psSHP->adBoundsMin[2] = dValue;
330
331  if( bBigEndian ) SwapWord( 8, pabyBuf+76 );
332  memcpy( &dValue, pabyBuf+76, 8 );
333  psSHP->adBoundsMax[2] = dValue;
334
335  if( bBigEndian ) SwapWord( 8, pabyBuf+84 );           /* m */
336  memcpy( &dValue, pabyBuf+84, 8 );
337  psSHP->adBoundsMin[3] = dValue;
338
339  if( bBigEndian ) SwapWord( 8, pabyBuf+92 );
340  memcpy( &dValue, pabyBuf+92, 8 );
341  psSHP->adBoundsMax[3] = dValue;
342  free( pabyBuf );
343 
344  /* -------------------------------------------------------------------- */
345  /*    Read the .shx file to get the offsets to each record in             */
346  /*    the .shp file.                                                      */
347  /* -------------------------------------------------------------------- */
348  psSHP->nMaxRecords = psSHP->nRecords;
349 
350  /* Our in-memory cache of offset information */
351  psSHP->panRecOffset = (int *) malloc(sizeof(int) * psSHP->nMaxRecords );
352  /* Our in-memory cache of size information */
353  psSHP->panRecSize = (int *) malloc(sizeof(int) * psSHP->nMaxRecords );
354  /* The completeness information for our in-memory cache */
355  psSHP->panRecLoaded = msAllocBitArray( 1 + (psSHP->nMaxRecords / SHX_BUFFER_PAGE) ) ;
356  /* Is our in-memory cache completely populated? */
357  psSHP->panRecAllLoaded = 0; 
358 
359  /* malloc failed? clean up and shut down */ 
360  if (psSHP->panRecOffset == NULL ||
361      psSHP->panRecSize == NULL ||
362      psSHP->panRecLoaded == NULL)
363  {
364    free(psSHP->panRecOffset);
365    free(psSHP->panRecSize);
366    free(psSHP->panRecLoaded);
367    fclose( psSHP->fpSHP );
368    fclose( psSHP->fpSHX );
369    free( psSHP );
370    msSetError(MS_MEMERR, "Out of memory", "msSHPOpen()");
371    return( NULL );
372  }
373
374 
375  return( psSHP );
376}
377
378/************************************************************************/
379/*                              msSHPClose()                            */
380/*                                                                      */
381/*      Close the .shp and .shx files.                                  */
382/************************************************************************/
383void msSHPClose(SHPHandle psSHP )
384{
385  /* -------------------------------------------------------------------- */
386  /*    Update the header if we have modified anything.                   */
387  /* -------------------------------------------------------------------- */
388  if( psSHP->bUpdated )
389    writeHeader( psSHP );
390 
391  /* -------------------------------------------------------------------- */
392  /*      Free all resources, and close files.                            */
393  /* -------------------------------------------------------------------- */
394  free( psSHP->panRecOffset );
395  free( psSHP->panRecSize );
396  free( psSHP->panRecLoaded );
397 
398 
399  if(psSHP->pabyRec) free(psSHP->pabyRec);
400  if(psSHP->panParts) free(psSHP->panParts);
401
402  fclose( psSHP->fpSHX );
403  fclose( psSHP->fpSHP );
404 
405  free( psSHP );
406}
407
408/************************************************************************/
409/*                             msSHPGetInfo()                           */
410/*                                                                      */
411/*      Fetch general information about the shape file.                 */
412/************************************************************************/
413void msSHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType )
414{
415  if( pnEntities )
416    *pnEntities = psSHP->nRecords;
417 
418  if( pnShapeType )
419    *pnShapeType = psSHP->nShapeType;
420}
421
422/************************************************************************/
423/*                             msSHPCreate()                            */
424/*                                                                      */
425/*      Create a new shape file and return a handle to the open         */
426/*      shape file with read/write access.                              */
427/************************************************************************/
428SHPHandle msSHPCreate( const char * pszLayer, int nShapeType )
429{
430  char *pszBasename, *pszFullname;
431  int   i;
432  FILE *fpSHP, *fpSHX;
433  uchar abyHeader[100];
434  ms_int32 i32;
435  double dValue;
436
437#ifndef USE_POINT_Z_M
438  if( nShapeType == SHP_POLYGONZ
439      || nShapeType == SHP_POLYGONM
440      || nShapeType == SHP_ARCZ
441      || nShapeType == SHP_ARCM
442      || nShapeType == SHP_POINTZ
443      || nShapeType == SHP_POINTM
444      || nShapeType == SHP_MULTIPOINTZ
445      || nShapeType == SHP_MULTIPOINTM )
446  {
447      msSetError( MS_SHPERR, 
448                  "Attempt to create M/Z shapefile but without having enabled Z/M support.", 
449                  "msSHPCreate()" );
450      return NULL;
451  }
452#endif
453     
454  /* -------------------------------------------------------------------- */
455  /*      Establish the byte order on this system.                        */
456  /* -------------------------------------------------------------------- */
457  i = 1;
458  if( *((uchar *) &i) == 1 )
459    bBigEndian = MS_FALSE;
460  else
461    bBigEndian = MS_TRUE;
462 
463  /* -------------------------------------------------------------------- */
464  /*    Compute the base (layer) name.  If there is any extension           */
465  /*    on the passed in filename we will strip it off.                     */
466  /* -------------------------------------------------------------------- */
467  pszBasename = (char *) msSmallMalloc(strlen(pszLayer)+5);
468  strcpy( pszBasename, pszLayer );
469  for( i = strlen(pszBasename)-1; 
470       i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\';
471       i-- ) {}
472 
473  if( pszBasename[i] == '.' )
474    pszBasename[i] = '\0';
475 
476  /* -------------------------------------------------------------------- */
477  /*      Open the two files so we can write their headers.               */
478  /* -------------------------------------------------------------------- */
479  pszFullname = (char *) msSmallMalloc(strlen(pszBasename) + 5);
480  sprintf( pszFullname, "%s.shp", pszBasename );
481  fpSHP = fopen(pszFullname, "wb" );
482  if( fpSHP == NULL )
483    return( NULL );
484 
485  sprintf( pszFullname, "%s.shx", pszBasename );
486  fpSHX = fopen(pszFullname, "wb" );
487  if( fpSHX == NULL )
488    return( NULL );
489
490  free( pszFullname );
491 
492  /* -------------------------------------------------------------------- */
493  /*      Prepare header block for .shp file.                             */
494  /* -------------------------------------------------------------------- */
495  for( i = 0; i < 100; i++ )
496    abyHeader[i] = 0;
497 
498  abyHeader[2] = 0x27;                          /* magic cookie */
499  abyHeader[3] = 0x0a;
500 
501  i32 = 50;                                             /* file size */
502  ByteCopy( &i32, abyHeader+24, 4 );
503  if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
504
505  i32 = 1000;                                           /* version */
506  ByteCopy( &i32, abyHeader+28, 4 );
507  if( bBigEndian ) SwapWord( 4, abyHeader+28 );
508 
509  i32 = nShapeType;                                     /* shape type */
510  ByteCopy( &i32, abyHeader+32, 4 );
511  if( bBigEndian ) SwapWord( 4, abyHeader+32 );
512
513  dValue = 0.0;                                 /* set bounds */
514  ByteCopy( &dValue, abyHeader+36, 8 );
515  ByteCopy( &dValue, abyHeader+44, 8 );
516  ByteCopy( &dValue, abyHeader+52, 8 );
517  ByteCopy( &dValue, abyHeader+60, 8 );
518 
519  /* -------------------------------------------------------------------- */
520  /*      Write .shp file header.                                         */
521  /* -------------------------------------------------------------------- */
522  fwrite( abyHeader, 100, 1, fpSHP );
523 
524  /* -------------------------------------------------------------------- */
525  /*      Prepare, and write .shx file header.                            */
526  /* -------------------------------------------------------------------- */
527  i32 = 50;                                             /* file size */
528  ByteCopy( &i32, abyHeader+24, 4 );
529  if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
530 
531  fwrite( abyHeader, 100, 1, fpSHX );
532 
533  /* -------------------------------------------------------------------- */
534  /*      Close the files, and then open them as regular existing files.  */
535  /* -------------------------------------------------------------------- */
536  fclose( fpSHP );
537  fclose( fpSHX );
538
539  return( msSHPOpen( pszLayer, "rb+" ) );
540}
541
542/************************************************************************/
543/*                           writeBounds()                              */
544/*                                                                      */
545/*      Compute a bounds rectangle for a shape, and set it into the     */
546/*      indicated location in the record.                               */
547/************************************************************************/
548static void writeBounds( uchar * pabyRec, shapeObj *shape, int nVCount )
549{
550  double dXMin, dXMax, dYMin, dYMax;
551  int   i, j;
552 
553  if( nVCount == 0 ) {
554    dXMin = dYMin = dXMax = dYMax = 0.0;
555  } else {
556    dXMin = dXMax = shape->line[0].point[0].x;
557    dYMin = dYMax = shape->line[0].point[0].y;
558   
559    for( i=0; i<shape->numlines; i++ ) {
560      for( j=0; j<shape->line[i].numpoints; j++ ) {
561        dXMin = MS_MIN(dXMin, shape->line[i].point[j].x);
562        dXMax = MS_MAX(dXMax, shape->line[i].point[j].x);
563        dYMin = MS_MIN(dYMin, shape->line[i].point[j].y);
564        dYMax = MS_MAX(dYMax, shape->line[i].point[j].y);
565      }
566    }
567  }
568 
569  if( bBigEndian ) { 
570    SwapWord( 8, &dXMin );
571    SwapWord( 8, &dYMin );
572    SwapWord( 8, &dXMax );
573    SwapWord( 8, &dYMax );
574  }
575 
576  ByteCopy( &dXMin, pabyRec +  0, 8 );
577  ByteCopy( &dYMin, pabyRec +  8, 8 );
578  ByteCopy( &dXMax, pabyRec + 16, 8 );
579  ByteCopy( &dYMax, pabyRec + 24, 8 );
580}
581
582int msSHPWritePoint(SHPHandle psSHP, pointObj *point )
583{
584  int nRecordOffset, nRecordSize=0;
585  uchar *pabyRec;
586  ms_int32      i32, nPoints, nParts;
587 
588  if( psSHP->nShapeType != SHP_POINT) return(-1);
589
590  psSHP->bUpdated = MS_TRUE;
591
592  /* Fill the SHX buffer if it is not already full. */
593  if( ! psSHP->panRecAllLoaded ) msSHXLoadAll( psSHP );
594
595  /* -------------------------------------------------------------------- */
596  /*      Add the new entity to the in memory index.                      */
597  /* -------------------------------------------------------------------- */
598  psSHP->nRecords++;
599  if( psSHP->nRecords > psSHP->nMaxRecords ) {
600    psSHP->nMaxRecords = (int) (psSHP->nMaxRecords * 1.3 + 100);
601   
602    psSHP->panRecOffset = (int *) SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords );
603    psSHP->panRecSize = (int *) SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords );
604  }
605
606  /* -------------------------------------------------------------------- */
607  /*      Compute a few things.                                           */
608  /* -------------------------------------------------------------------- */
609  nPoints = 1;
610  nParts = 1;
611 
612  /* -------------------------------------------------------------------- */
613  /*      Initialize record.                                              */
614  /* -------------------------------------------------------------------- */
615  psSHP->panRecOffset[psSHP->nRecords-1] = nRecordOffset = psSHP->nFileSize;
616 
617  pabyRec = (uchar *) msSmallMalloc(nPoints * 2 * sizeof(double) + nParts * 4 + 128);
618 
619  /* -------------------------------------------------------------------- */
620  /*      Write vertices for a point.                                     */
621  /* -------------------------------------------------------------------- */
622  ByteCopy( &(point->x), pabyRec + 12, 8 );
623  ByteCopy( &(point->y), pabyRec + 20, 8 );
624 
625   
626  if( bBigEndian ) {
627    SwapWord( 8, pabyRec + 12 );
628    SwapWord( 8, pabyRec + 20 );
629  }
630   
631  nRecordSize = 20;
632
633  /* -------------------------------------------------------------------- */
634  /*      Set the shape type, record number, and record size.             */
635  /* -------------------------------------------------------------------- */
636  i32 = psSHP->nRecords-1+1;                                    /* record # */
637  if( !bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
638  ByteCopy( &i32, pabyRec, 4 );
639 
640  i32 = nRecordSize/2;                          /* record size */
641  if( !bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
642  ByteCopy( &i32, pabyRec + 4, 4 );
643 
644  i32 = psSHP->nShapeType;                              /* shape type */
645  if( bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
646  ByteCopy( &i32, pabyRec + 8, 4 );
647 
648  /* -------------------------------------------------------------------- */
649  /*      Write out record.                                               */
650  /* -------------------------------------------------------------------- */
651  fseek( psSHP->fpSHP, nRecordOffset, 0 );
652  fwrite( pabyRec, nRecordSize+8, 1, psSHP->fpSHP );
653  free( pabyRec );
654 
655  psSHP->panRecSize[psSHP->nRecords-1] = nRecordSize;
656  psSHP->nFileSize += nRecordSize + 8;
657 
658  /* -------------------------------------------------------------------- */
659  /*    Expand file wide bounds based on this shape.                      */
660  /* -------------------------------------------------------------------- */
661  if( psSHP->nRecords == 1 ) {
662    psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = point->x;
663    psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = point->y;
664  } else {
665    psSHP->adBoundsMin[0] = MS_MIN(psSHP->adBoundsMin[0], point->x);
666    psSHP->adBoundsMin[1] = MS_MIN(psSHP->adBoundsMin[1], point->y);
667    psSHP->adBoundsMax[0] = MS_MAX(psSHP->adBoundsMax[0], point->x);
668    psSHP->adBoundsMax[1] = MS_MAX(psSHP->adBoundsMax[1], point->y);
669  }
670 
671  return( psSHP->nRecords - 1 );
672}
673
674int msSHPWriteShape(SHPHandle psSHP, shapeObj *shape )
675{
676  int nRecordOffset, i, j, k, nRecordSize=0;
677  uchar *pabyRec;
678  int nShapeType;
679
680  ms_int32      i32, nPoints, nParts;
681#ifdef USE_POINT_Z_M
682  double dfMMin, dfMMax = 0;
683#endif
684  psSHP->bUpdated = MS_TRUE;
685 
686  /* Fill the SHX buffer if it is not already full. */
687  if( ! psSHP->panRecAllLoaded ) msSHXLoadAll( psSHP );
688 
689  /* -------------------------------------------------------------------- */
690  /*      Add the new entity to the in memory index.                      */
691  /* -------------------------------------------------------------------- */
692  psSHP->nRecords++;
693  if( psSHP->nRecords > psSHP->nMaxRecords ) {
694    psSHP->nMaxRecords = (int) (psSHP->nMaxRecords * 1.3 + 100);
695   
696    psSHP->panRecOffset = (int *) SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords );
697    psSHP->panRecSize = (int *) SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords );
698  }
699 
700  /* -------------------------------------------------------------------- */
701  /*      Compute a few things.                                           */
702  /* -------------------------------------------------------------------- */
703  nPoints = 0;
704  for(i=0; i<shape->numlines; i++)
705    nPoints += shape->line[i].numpoints;
706 
707  nParts = shape->numlines;
708 
709  /* -------------------------------------------------------------------- */
710  /*      Initialize record.                                              */
711  /* -------------------------------------------------------------------- */
712  psSHP->panRecOffset[psSHP->nRecords-1] = nRecordOffset = psSHP->nFileSize;
713 
714  pabyRec = (uchar *) msSmallMalloc(nPoints * 4 * sizeof(double) + nParts * 8 + 128);
715  nShapeType = psSHP->nShapeType;
716 
717  if (shape->type == MS_SHAPE_NULL) {
718      nShapeType = 0;
719      nRecordSize = 12;
720  }
721  /* -------------------------------------------------------------------- */
722  /*  Write vertices for a Polygon or Arc.                                  */
723  /* -------------------------------------------------------------------- */
724  else if(psSHP->nShapeType == SHP_POLYGON || psSHP->nShapeType == SHP_ARC ||
725     psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM ||
726     psSHP->nShapeType == SHP_ARCZ ||  psSHP->nShapeType == SHP_POLYGONZ) {
727    ms_int32 t_nParts, t_nPoints, partSize;
728   
729    t_nParts = nParts;
730    t_nPoints = nPoints;
731   
732    writeBounds( pabyRec + 12, shape, t_nPoints );
733   
734    if( bBigEndian ) { 
735      nPoints = SWAP_FOUR_BYTES(nPoints);
736      nParts = SWAP_FOUR_BYTES(nParts);
737    }
738   
739    ByteCopy( &nPoints, pabyRec + 40 + 8, 4 );
740    ByteCopy( &nParts, pabyRec + 36 + 8, 4 );
741
742    partSize = 0; /* first part always starts at 0 */
743    ByteCopy( &partSize, pabyRec + 44 + 8 + 4*0, 4 );
744    if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*0);
745
746    for( i = 1; i < t_nParts; i++ ) {
747      partSize += shape->line[i-1].numpoints;
748      ByteCopy( &partSize, pabyRec + 44 + 8 + 4*i, 4 );
749      if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i);
750
751    }
752   
753    k = 0; /* overall point counter */
754    for( i = 0; i < shape->numlines; i++ ) {
755      for( j = 0; j < shape->line[i].numpoints; j++ ) {
756        ByteCopy( &(shape->line[i].point[j].x), pabyRec + 44 + 4*t_nParts + 8 + k * 16, 8 );
757        ByteCopy( &(shape->line[i].point[j].y), pabyRec + 44 + 4*t_nParts + 8 + k * 16 + 8, 8 );
758       
759        if( bBigEndian ) {
760          SwapWord( 8, pabyRec + 44+4*t_nParts+8+k*16 );
761          SwapWord( 8, pabyRec + 44+4*t_nParts+8+k*16+8 );
762        }
763
764        k++;
765      }
766    }
767
768    nRecordSize = 44 + 4*t_nParts + 16 * t_nPoints;
769#ifdef USE_POINT_Z_M
770    /* -------------------------------------------------------------------- */
771    /*      measured shape : polygon and arc.                               */
772    /* -------------------------------------------------------------------- */
773    if(psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM) {
774      dfMMin = shape->line[0].point[0].m;
775      dfMMax = shape->line[shape->numlines-1].point[shape->line[shape->numlines-1].numpoints-1].m;
776
777      nRecordSize = 44 + 4*t_nParts + 8 + (t_nPoints* 16);
778
779      ByteCopy( &(dfMMin), pabyRec + nRecordSize, 8 );
780      if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
781      nRecordSize += 8;
782
783      ByteCopy( &(dfMMax), pabyRec + nRecordSize, 8 );
784      if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
785      nRecordSize += 8;
786           
787      for( i = 0; i < shape->numlines; i++ ) {
788        for( j = 0; j < shape->line[i].numpoints; j++ ) {
789          ByteCopy( &(shape->line[i].point[j].m), pabyRec + nRecordSize, 8 );
790          if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
791          nRecordSize += 8;
792        }
793      }
794    }
795
796    /* -------------------------------------------------------------------- */
797    /*      Polygon. Arc with Z                                             */
798    /* -------------------------------------------------------------------- */
799    if (psSHP->nShapeType == SHP_POLYGONZ || psSHP->nShapeType == SHP_ARCZ
800        || psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM) {
801      dfMMin = shape->line[0].point[0].z;
802      dfMMax = shape->line[shape->numlines-1].point[shape->line[shape->numlines-1].numpoints-1].z;
803           
804      nRecordSize = 44 + 4*t_nParts + 8 + (t_nPoints* 16);
805
806      ByteCopy( &(dfMMin), pabyRec + nRecordSize, 8 );
807      if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
808      nRecordSize += 8;
809
810      ByteCopy( &(dfMMax), pabyRec + nRecordSize, 8 );
811      if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
812      nRecordSize += 8;
813           
814      for( i = 0; i < shape->numlines; i++ ) {
815        for( j = 0; j < shape->line[i].numpoints; j++ ) {
816          ByteCopy( &(shape->line[i].point[j].z), pabyRec + nRecordSize, 8 );
817          if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
818          nRecordSize += 8;
819        }
820      }
821    }
822#endif /* def USE_POINT_Z_M */
823  }
824 
825  /* -------------------------------------------------------------------- */
826  /*  Write vertices for a MultiPoint.                                                      */
827  /* -------------------------------------------------------------------- */
828  else if( psSHP->nShapeType == SHP_MULTIPOINT ||
829           psSHP->nShapeType == SHP_MULTIPOINTM ||
830           psSHP->nShapeType == SHP_MULTIPOINTZ) {
831    ms_int32 t_nPoints;
832   
833    t_nPoints = nPoints;
834   
835    writeBounds( pabyRec + 12, shape, nPoints );
836   
837    if( bBigEndian ) nPoints = SWAP_FOUR_BYTES(nPoints);
838    ByteCopy( &nPoints, pabyRec + 44, 4 );
839   
840    for( i = 0; i < shape->line[0].numpoints; i++ ) {
841      ByteCopy( &(shape->line[0].point[i].x), pabyRec + 48 + i*16, 8 );
842      ByteCopy( &(shape->line[0].point[i].y), pabyRec + 48 + i*16 + 8, 8 );
843     
844      if( bBigEndian ) { 
845        SwapWord( 8, pabyRec + 48 + i*16 );
846        SwapWord( 8, pabyRec + 48 + i*16 + 8 );
847      }
848    }
849
850    nRecordSize = 40 + 16 * t_nPoints;
851
852#ifdef USE_POINT_Z_M
853    if (psSHP->nShapeType == SHP_MULTIPOINTM) {
854      dfMMin = shape->line[0].point[0].m;
855      dfMMax = shape->line[0].point[shape->line[0].numpoints-1].m;
856
857      ByteCopy( &(dfMMin), pabyRec + nRecordSize, 8 );
858      if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
859      nRecordSize += 8;
860
861      ByteCopy( &(dfMMax), pabyRec + nRecordSize, 8 );
862      if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
863      nRecordSize += 8;
864       
865      for( i = 0; i < shape->line[0].numpoints; i++ ) {
866        ByteCopy( &(shape->line[0].point[i].m), pabyRec + nRecordSize, 8 );
867        if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
868        nRecordSize += 8;
869      }
870    }
871
872    if (psSHP->nShapeType == SHP_MULTIPOINTZ) {
873      dfMMin = shape->line[0].point[0].z;
874      dfMMax = shape->line[0].point[shape->line[0].numpoints-1].z;
875
876      ByteCopy( &(dfMMin), pabyRec + nRecordSize, 8 );
877      if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
878      nRecordSize += 8;
879
880      ByteCopy( &(dfMMax), pabyRec + nRecordSize, 8 );
881      if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
882      nRecordSize += 8;
883       
884      for( i = 0; i < shape->line[0].numpoints; i++ ) {
885        ByteCopy( &(shape->line[0].point[i].z), pabyRec + nRecordSize, 8 );
886        if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
887        nRecordSize += 8;
888      }
889    }
890#endif /* USE_POINT_Z_M */
891  }
892 
893  /* -------------------------------------------------------------------- */
894  /*      Write vertices for a point.                                     */
895  /* -------------------------------------------------------------------- */
896  else if( psSHP->nShapeType == SHP_POINT ||  psSHP->nShapeType == SHP_POINTM ||
897           psSHP->nShapeType == SHP_POINTZ) {
898    ByteCopy( &(shape->line[0].point[0].x), pabyRec + 12, 8 );
899    ByteCopy( &(shape->line[0].point[0].y), pabyRec + 20, 8 );
900   
901    if( bBigEndian ) {
902      SwapWord( 8, pabyRec + 12 );
903      SwapWord( 8, pabyRec + 20 );
904    }
905   
906    nRecordSize = 20;
907
908#ifdef USE_POINT_Z_M
909    if (psSHP->nShapeType == SHP_POINTM) {
910      ByteCopy( &(shape->line[0].point[0].m), pabyRec + nRecordSize, 8 );
911      if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
912      nRecordSize += 8;
913    }
914
915    if (psSHP->nShapeType == SHP_POINTZ) {
916      ByteCopy( &(shape->line[0].point[0].z), pabyRec + nRecordSize, 8 );
917      if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
918      nRecordSize += 8;
919    }
920#endif /* USE_POINT_Z_M */
921  }
922 
923  /* -------------------------------------------------------------------- */
924  /*      Set the shape type, record number, and record size.             */
925  /* -------------------------------------------------------------------- */
926  i32 = psSHP->nRecords-1+1;                                    /* record # */
927  if( !bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
928  ByteCopy( &i32, pabyRec, 4 );
929 
930  i32 = nRecordSize/2;                          /* record size */
931  if( !bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
932  ByteCopy( &i32, pabyRec + 4, 4 );
933 
934  i32 = nShapeType;     /* shape type */
935  if( bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
936  ByteCopy( &i32, pabyRec + 8, 4 );
937 
938  /* -------------------------------------------------------------------- */
939  /*      Write out record.                                               */
940  /* -------------------------------------------------------------------- */
941  fseek( psSHP->fpSHP, nRecordOffset, 0 );
942  fwrite( pabyRec, nRecordSize+8, 1, psSHP->fpSHP );
943  free( pabyRec );
944 
945  psSHP->panRecSize[psSHP->nRecords-1] = nRecordSize;
946  psSHP->nFileSize += nRecordSize + 8;
947 
948  /* -------------------------------------------------------------------- */
949  /*    Expand file wide bounds based on this shape.                      */
950  /* -------------------------------------------------------------------- */
951  if( psSHP->nRecords == 1 ) {
952    psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = shape->line[0].point[0].x;
953    psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = shape->line[0].point[0].y;
954#ifdef USE_POINT_Z_M
955    psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = shape->line[0].point[0].z;
956    psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = shape->line[0].point[0].m;
957#endif
958  }
959 
960  for( i=0; i<shape->numlines; i++ ) {
961    for( j=0; j<shape->line[i].numpoints; j++ ) {
962      psSHP->adBoundsMin[0] = MS_MIN(psSHP->adBoundsMin[0], shape->line[i].point[j].x);
963      psSHP->adBoundsMin[1] = MS_MIN(psSHP->adBoundsMin[1], shape->line[i].point[j].y);
964#ifdef USE_POINT_Z_M
965      psSHP->adBoundsMin[2] = MS_MIN(psSHP->adBoundsMin[2], shape->line[i].point[j].z);
966      psSHP->adBoundsMin[3] = MS_MIN(psSHP->adBoundsMin[3], shape->line[i].point[j].m);
967#endif
968      psSHP->adBoundsMax[0] = MS_MAX(psSHP->adBoundsMax[0], shape->line[i].point[j].x);
969      psSHP->adBoundsMax[1] = MS_MAX(psSHP->adBoundsMax[1], shape->line[i].point[j].y);
970#ifdef USE_POINT_Z_M
971      psSHP->adBoundsMax[2] = MS_MAX(psSHP->adBoundsMax[2], shape->line[i].point[j].z);
972      psSHP->adBoundsMax[3] = MS_MAX(psSHP->adBoundsMax[3], shape->line[i].point[j].m);
973#endif
974    }
975  }
976 
977  return( psSHP->nRecords - 1 );
978}
979
980/*
981 ** msSHPReadAllocateBuffer() - Ensure our record buffer is large enough.
982 */
983static int msSHPReadAllocateBuffer( SHPHandle psSHP, int hEntity, const char* pszCallingFunction)
984{
985
986  int nEntitySize = msSHXReadSize(psSHP, hEntity) + 8;
987  /* -------------------------------------------------------------------- */
988  /*      Ensure our record buffer is large enough.                       */
989  /* -------------------------------------------------------------------- */
990  if( nEntitySize > psSHP->nBufSize ) {
991    psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize);
992    if (psSHP->pabyRec == NULL)
993    {
994        /* Reallocate previous successfull size for following features */
995        psSHP->pabyRec = msSmallMalloc(psSHP->nBufSize);
996
997        msSetError(MS_MEMERR, "Out of memory. Cannot allocate %d bytes. Probably broken shapefile at feature %d",
998                   pszCallingFunction, nEntitySize, hEntity);
999        return(MS_FAILURE);
1000    }
1001    psSHP->nBufSize = nEntitySize;
1002  }
1003  if (psSHP->pabyRec == NULL)
1004  {
1005    msSetError(MS_MEMERR, "Out of memory", pszCallingFunction);
1006    return(MS_FAILURE);
1007  }
1008  return MS_SUCCESS;
1009}
1010
1011/*
1012** msSHPReadPoint() - Reads a single point from a POINT shape file.
1013*/
1014int msSHPReadPoint( SHPHandle psSHP, int hEntity, pointObj *point )
1015{
1016  int nEntitySize;
1017
1018  /* -------------------------------------------------------------------- */
1019  /*      Only valid for point shapefiles                                 */
1020  /* -------------------------------------------------------------------- */
1021  if( psSHP->nShapeType != SHP_POINT ) {
1022    msSetError(MS_SHPERR, "msSHPReadPoint only operates on point shapefiles.", "msSHPReadPoint()");
1023    return(MS_FAILURE);
1024  }
1025
1026  /* -------------------------------------------------------------------- */
1027  /*      Validate the record/entity number.                              */
1028  /* -------------------------------------------------------------------- */
1029  if( hEntity < 0 || hEntity >= psSHP->nRecords ) {
1030    msSetError(MS_SHPERR, "Record index out of bounds.", "msSHPReadPoint()");
1031    return(MS_FAILURE);
1032  }
1033
1034  nEntitySize = msSHXReadSize( psSHP, hEntity) + 8;
1035
1036  if( msSHXReadSize( psSHP, hEntity) == 4 ) {
1037    msSetError(MS_SHPERR, "NULL feature encountered.", "msSHPReadPoint()");
1038    return(MS_FAILURE);
1039  }
1040  else if ( nEntitySize < 28 ) {
1041    msSetError(MS_SHPERR, "Corrupted feature encountered.  hEntity=%d, nEntitySize=%d", "msSHPReadPoint()",
1042               hEntity, nEntitySize);
1043    return(MS_FAILURE);
1044  }
1045
1046  if (msSHPReadAllocateBuffer(psSHP, hEntity, "msSHPReadPoint()") == MS_FAILURE)
1047  {
1048    return MS_FAILURE;
1049  }
1050
1051  /* -------------------------------------------------------------------- */
1052  /*      Read the record.                                                */
1053  /* -------------------------------------------------------------------- */
1054  fseek( psSHP->fpSHP, msSHXReadOffset( psSHP, hEntity), 0 );
1055  fread( psSHP->pabyRec, nEntitySize, 1, psSHP->fpSHP );
1056     
1057  memcpy( &(point->x), psSHP->pabyRec + 12, 8 );
1058  memcpy( &(point->y), psSHP->pabyRec + 20, 8 );
1059     
1060  if( bBigEndian ) {
1061    SwapWord( 8, &(point->x));
1062    SwapWord( 8, &(point->y));
1063  }
1064
1065  return(MS_SUCCESS);
1066}
1067
1068/*
1069** msSHXLoadPage()
1070**
1071** The SHX tells us what the byte offsets of the shapes in the SHP file are.
1072** We read the SHX file in ~8K pages and store those pages in memory for
1073** successive accesses during the reading cycle (first bounds are read,
1074** then entire shapes). Each time we read a page, we mark it as read.
1075*/
1076int msSHXLoadPage( SHPHandle psSHP, int shxBufferPage )
1077{
1078  int i;
1079
1080  /* Each SHX record is 8 bytes long (two ints), hence our buffer size. */
1081  char buffer[SHX_BUFFER_PAGE * 8];
1082
1083  /*  Validate the page number. */
1084  if( shxBufferPage < 0  )
1085    return(MS_FAILURE);
1086
1087  /* The SHX file starts with 100 bytes of header, skip that. */
1088  fseek( psSHP->fpSHX, 100 + shxBufferPage * SHX_BUFFER_PAGE * 8, 0 );
1089  fread( buffer, 8, SHX_BUFFER_PAGE, psSHP->fpSHX );
1090
1091  /* Copy the buffer contents out into the working arrays. */
1092  for( i = 0; i < SHX_BUFFER_PAGE; i++ ) {
1093    int tmpOffset, tmpSize;
1094   
1095    /* Don't write information past the end of the arrays, please. */
1096    if(psSHP->nRecords <= (shxBufferPage * SHX_BUFFER_PAGE + i) )
1097      break;
1098   
1099    memcpy( &tmpOffset, (buffer + (8*i)), 4);
1100    memcpy( &tmpSize, (buffer + (8*i) + 4), 4);
1101 
1102    /* SHX uses big endian numbers for the offsets, so we have to flip them */
1103    /* if we are a little endian machine. */
1104    if( !bBigEndian ) {
1105      tmpOffset = SWAP_FOUR_BYTES(tmpOffset);
1106      tmpSize = SWAP_FOUR_BYTES(tmpSize);
1107    }
1108   
1109    /* SHX stores the offsets in 2 byte units, so we double them to get */
1110    /* an offset in bytes. */
1111    tmpOffset = tmpOffset * 2;
1112    tmpSize = tmpSize * 2;
1113
1114    /* Write the answer into the working arrays on the SHPHandle */
1115    psSHP->panRecOffset[shxBufferPage * SHX_BUFFER_PAGE + i] = tmpOffset;
1116    psSHP->panRecSize[shxBufferPage * SHX_BUFFER_PAGE + i] = tmpSize;
1117  }
1118   
1119  msSetBit(psSHP->panRecLoaded, shxBufferPage, 1);
1120 
1121  return(MS_SUCCESS);
1122}
1123
1124int msSHXLoadAll( SHPHandle psSHP ) {
1125
1126  int i;
1127  uchar *pabyBuf;
1128
1129  pabyBuf = (uchar *) msSmallMalloc(8 * psSHP->nRecords );
1130  fread( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX );
1131  for( i = 0; i < psSHP->nRecords; i++ ) {
1132    ms_int32 nOffset, nLength;
1133   
1134    memcpy( &nOffset, pabyBuf + i * 8, 4 );
1135    memcpy( &nLength, pabyBuf + i * 8 + 4, 4 );
1136
1137    if( !bBigEndian ) {
1138      nOffset = SWAP_FOUR_BYTES( nOffset );
1139      nLength = SWAP_FOUR_BYTES( nLength );
1140    }
1141   
1142    psSHP->panRecOffset[i] = nOffset*2; 
1143    psSHP->panRecSize[i] = nLength*2; 
1144  }
1145  free(pabyBuf);
1146  psSHP->panRecAllLoaded = 1;
1147 
1148  return(MS_SUCCESS);
1149
1150}
1151
1152int msSHXReadOffset( SHPHandle psSHP, int hEntity ) {
1153
1154  int shxBufferPage = hEntity / SHX_BUFFER_PAGE;
1155
1156  /*  Validate the record/entity number. */
1157  if( hEntity < 0 || hEntity >= psSHP->nRecords )
1158    return(MS_FAILURE);
1159
1160  if( ! (psSHP->panRecAllLoaded || msGetBit(psSHP->panRecLoaded, shxBufferPage)) ) {
1161    msSHXLoadPage( psSHP, shxBufferPage );
1162  }
1163
1164  return psSHP->panRecOffset[hEntity];
1165
1166}
1167
1168int msSHXReadSize( SHPHandle psSHP, int hEntity ) {
1169
1170  int shxBufferPage = hEntity / SHX_BUFFER_PAGE;
1171
1172  /*  Validate the record/entity number. */
1173  if( hEntity < 0 || hEntity >= psSHP->nRecords )
1174    return(MS_FAILURE);
1175
1176  if( ! (psSHP->panRecAllLoaded || msGetBit(psSHP->panRecLoaded, shxBufferPage)) ) {
1177    msSHXLoadPage( psSHP, shxBufferPage );
1178  }
1179
1180  return psSHP->panRecSize[hEntity];
1181
1182}
1183
1184/*
1185** msSHPReadShape() - Reads the vertices for one shape from a shape file.
1186*/
1187void msSHPReadShape( SHPHandle psSHP, int hEntity, shapeObj *shape )
1188{
1189  int i, j, k;
1190#ifdef USE_POINT_Z_M
1191  int nOffset = 0;
1192#endif
1193  int nEntitySize, nRequiredSize;
1194
1195  msInitShape(shape); /* initialize the shape */
1196
1197  /* -------------------------------------------------------------------- */
1198  /*      Validate the record/entity number.                              */
1199  /* -------------------------------------------------------------------- */
1200  if( hEntity < 0 || hEntity >= psSHP->nRecords )
1201    return;
1202
1203  if( msSHXReadSize(psSHP, hEntity) == 4 ) {     
1204    shape->type = MS_SHAPE_NULL;
1205    return;
1206  }
1207
1208  nEntitySize = msSHXReadSize(psSHP, hEntity) + 8;
1209  if (msSHPReadAllocateBuffer(psSHP, hEntity, "msSHPReadShape()") == MS_FAILURE)
1210  {
1211    shape->type = MS_SHAPE_NULL;
1212    return;
1213  }
1214
1215  /* -------------------------------------------------------------------- */
1216  /*      Read the record.                                                */
1217  /* -------------------------------------------------------------------- */
1218  fseek( psSHP->fpSHP, msSHXReadOffset(psSHP, hEntity), 0 );
1219  fread( psSHP->pabyRec, nEntitySize, 1, psSHP->fpSHP );
1220
1221  /* -------------------------------------------------------------------- */
1222  /*  Extract vertices for a Polygon or Arc.                                */
1223  /* -------------------------------------------------------------------- */
1224  if( psSHP->nShapeType == SHP_POLYGON || psSHP->nShapeType == SHP_ARC || 
1225      psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM ||
1226      psSHP->nShapeType == SHP_POLYGONZ || psSHP->nShapeType == SHP_ARCZ)
1227  {
1228    ms_int32  nPoints, nParts;     
1229   
1230    if (nEntitySize < 40 + 8 + 4)
1231    {
1232      shape->type = MS_SHAPE_NULL;
1233      msSetError(MS_SHPERR, "Corrupted feature encountered.  hEntity = %d, nEntitySize=%d", "msSHPReadShape()",
1234                 hEntity, nEntitySize);
1235      return;
1236    }
1237
1238    /* copy the bounding box */
1239    memcpy( &shape->bounds.minx, psSHP->pabyRec + 8 + 4, 8 );
1240    memcpy( &shape->bounds.miny, psSHP->pabyRec + 8 + 12, 8 );
1241    memcpy( &shape->bounds.maxx, psSHP->pabyRec + 8 + 20, 8 );
1242    memcpy( &shape->bounds.maxy, psSHP->pabyRec + 8 + 28, 8 );
1243
1244    if( bBigEndian ) {
1245      SwapWord( 8, &shape->bounds.minx);
1246      SwapWord( 8, &shape->bounds.miny);
1247      SwapWord( 8, &shape->bounds.maxx);
1248      SwapWord( 8, &shape->bounds.maxy);
1249    }
1250
1251    memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 );
1252    memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 );
1253     
1254    if( bBigEndian ) {
1255      nPoints = SWAP_FOUR_BYTES(nPoints);
1256      nParts = SWAP_FOUR_BYTES(nParts);   
1257    }
1258
1259    if (nPoints < 0 || nParts < 0 || 
1260        nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000) 
1261    {
1262      shape->type = MS_SHAPE_NULL;
1263      msSetError(MS_SHPERR, "Corrupted feature encountered.  hEntity = %d, nPoints =%d, nParts = %d", "msSHPReadShape()",
1264                 hEntity, nPoints, nParts);
1265      return;
1266    }
1267   
1268    /* -------------------------------------------------------------------- */
1269    /*      Copy out the part array from the record.                        */
1270    /* -------------------------------------------------------------------- */
1271    if( psSHP->nPartMax < nParts ) {
1272      psSHP->panParts = (int *) SfRealloc(psSHP->panParts, nParts * sizeof(int) );
1273      if (psSHP->panParts == NULL)
1274      {
1275        /* Reallocate previous successfull size for following features */ 
1276        psSHP->panParts = (int *) msSmallMalloc(psSHP->nPartMax * sizeof(int) );
1277
1278        shape->type = MS_SHAPE_NULL;
1279        msSetError(MS_MEMERR, "Out of memory. Cannot allocate %d bytes. Probably broken shapefile at feature %d",
1280                   "msSHPReadShape()", nParts * sizeof(int), hEntity);
1281        return;
1282      }
1283      psSHP->nPartMax = nParts;
1284    }
1285    if (psSHP->panParts == NULL)
1286    {
1287       shape->type = MS_SHAPE_NULL;
1288       msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()");
1289       return;
1290    }
1291   
1292    /* With the previous checks on nPoints and nParts, */
1293    /* we should not overflow here and after */
1294    /* since 50 M * (16 + 8 + 8) = 1 600 MB */
1295    if (44 + 8 + 4 * nParts + 16 * nPoints > nEntitySize)
1296    {
1297      shape->type = MS_SHAPE_NULL;
1298      msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.",
1299                 "msSHPReadShape()", hEntity, nPoints, nParts);
1300      return;
1301    }
1302     
1303    memcpy( psSHP->panParts, psSHP->pabyRec + 44 + 8, 4 * nParts );
1304    if( bBigEndian ) {
1305      for( i = 0; i < nParts; i++ ) {
1306        *(psSHP->panParts+i) = SWAP_FOUR_BYTES(*(psSHP->panParts+i));
1307      }
1308    }
1309     
1310    /* -------------------------------------------------------------------- */
1311    /*      Fill the shape structure.                                       */
1312    /* -------------------------------------------------------------------- */
1313    shape->line = (lineObj *)malloc(sizeof(lineObj)*nParts);
1314    MS_CHECK_ALLOC_NO_RET(shape->line, sizeof(lineObj)*nParts);
1315
1316    shape->numlines = nParts;
1317     
1318    k = 0; /* overall point counter */
1319    for( i = 0; i < nParts; i++) {       
1320      if( i == nParts-1)
1321        shape->line[i].numpoints = nPoints - psSHP->panParts[i];
1322      else
1323        shape->line[i].numpoints = psSHP->panParts[i+1] - psSHP->panParts[i];
1324      if (shape->line[i].numpoints <= 0)
1325      {
1326        msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, shape->line[%d].numpoints=%d", "msSHPReadShape()",
1327                   hEntity, i, shape->line[i].numpoints);
1328        while(--i >= 0)
1329          free(shape->line[i].point);
1330        free(shape->line);
1331        shape->line = NULL;
1332        shape->numlines = 0;
1333        shape->type = MS_SHAPE_NULL;
1334        return;
1335      }
1336       
1337      if( (shape->line[i].point = (pointObj *)malloc(sizeof(pointObj)*shape->line[i].numpoints)) == NULL ) {
1338        while(--i >= 0)
1339          free(shape->line[i].point);
1340        free(shape->line);
1341        shape->numlines = 0;
1342        shape->type = MS_SHAPE_NULL;
1343        msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()");
1344        return;
1345      }
1346
1347      /* nOffset = 44 + 8 + 4*nParts; */
1348      for( j = 0; j < shape->line[i].numpoints; j++ ) {
1349        memcpy(&(shape->line[i].point[j].x), psSHP->pabyRec + 44 + 4*nParts + 8 + k * 16, 8 );
1350        memcpy(&(shape->line[i].point[j].y), psSHP->pabyRec + 44 + 4*nParts + 8 + k * 16 + 8, 8 );
1351         
1352        if( bBigEndian ) {
1353          SwapWord( 8, &(shape->line[i].point[j].x) );
1354          SwapWord( 8, &(shape->line[i].point[j].y) );
1355        }
1356
1357#ifdef USE_POINT_Z_M
1358        /* -------------------------------------------------------------------- */
1359        /*      Polygon, Arc with Z values.                                     */
1360        /* -------------------------------------------------------------------- */
1361        shape->line[i].point[j].z = 0.0; /* initialize */
1362        if (psSHP->nShapeType == SHP_POLYGONZ || psSHP->nShapeType == SHP_ARCZ) {
1363          nOffset = 44 + 8 + (4*nParts) + (16*nPoints) ;
1364          if( nEntitySize >= nOffset + 16 + 8*nPoints ) {
1365            memcpy(&(shape->line[i].point[j].z), psSHP->pabyRec + nOffset + 16 + k*8, 8 );
1366            if( bBigEndian ) SwapWord( 8, &(shape->line[i].point[j].z) );
1367          }   
1368        }
1369
1370        /* -------------------------------------------------------------------- */
1371        /*      Measured arc and polygon support.                               */
1372        /* -------------------------------------------------------------------- */
1373        shape->line[i].point[j].m = 0; /* initialize */
1374        if (psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM) {
1375          nOffset = 44 + 8 + (4*nParts) + (16*nPoints) ;
1376          if( nEntitySize >= nOffset + 16 + 8*nPoints ) {
1377            memcpy(&(shape->line[i].point[j].m), psSHP->pabyRec + nOffset + 16 + k*8, 8 );
1378            if( bBigEndian ) SwapWord( 8, &(shape->line[i].point[j].m) );
1379          }   
1380        }
1381#endif /* USE_POINT_Z_M */
1382              k++;
1383            }
1384    }
1385
1386    if(psSHP->nShapeType == SHP_POLYGON
1387       || psSHP->nShapeType == SHP_POLYGONZ
1388       || psSHP->nShapeType == SHP_POLYGONM)
1389      shape->type = MS_SHAPE_POLYGON;
1390    else
1391      shape->type = MS_SHAPE_LINE;
1392
1393  }
1394
1395  /* -------------------------------------------------------------------- */
1396  /*  Extract a MultiPoint.                                                                 */
1397  /* -------------------------------------------------------------------- */
1398  else if( psSHP->nShapeType == SHP_MULTIPOINT || psSHP->nShapeType == SHP_MULTIPOINTM ||
1399           psSHP->nShapeType == SHP_MULTIPOINTZ) {
1400    ms_int32 nPoints;
1401
1402    if (nEntitySize < 44 + 4)
1403    {
1404      shape->type = MS_SHAPE_NULL;
1405      msSetError(MS_SHPERR, "Corrupted feature encountered.  recSize of feature %d=%d", "msSHPReadShape()",
1406                 hEntity, msSHXReadSize(psSHP, hEntity));
1407      return;
1408    }
1409
1410    /* copy the bounding box */
1411    memcpy( &shape->bounds.minx, psSHP->pabyRec + 8 + 4, 8 );
1412    memcpy( &shape->bounds.miny, psSHP->pabyRec + 8 + 12, 8 );
1413    memcpy( &shape->bounds.maxx, psSHP->pabyRec + 8 + 20, 8 );
1414    memcpy( &shape->bounds.maxy, psSHP->pabyRec + 8 + 28, 8 );
1415
1416    if( bBigEndian ) {
1417      SwapWord( 8, &shape->bounds.minx);
1418      SwapWord( 8, &shape->bounds.miny);
1419      SwapWord( 8, &shape->bounds.maxx);
1420      SwapWord( 8, &shape->bounds.maxy);
1421    }
1422
1423    memcpy( &nPoints, psSHP->pabyRec + 44, 4 );
1424    if( bBigEndian ) nPoints = SWAP_FOUR_BYTES(nPoints);
1425   
1426    /* -------------------------------------------------------------------- */
1427    /*      Fill the shape structure.                                       */
1428    /* -------------------------------------------------------------------- */
1429    if( (shape->line = (lineObj *)malloc(sizeof(lineObj))) == NULL ) {
1430      shape->type = MS_SHAPE_NULL;
1431      msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()");
1432      return;
1433    }
1434
1435    if (nPoints < 0 || nPoints > 50 * 1000 * 1000)
1436    {
1437      free(shape->line);
1438      shape->type = MS_SHAPE_NULL;
1439      msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, nPoints=%d.",
1440                 "msSHPReadShape()", hEntity, nPoints);
1441      return;
1442    }
1443
1444    nRequiredSize = 48 + nPoints * 16;
1445    if (psSHP->nShapeType == SHP_MULTIPOINTZ || psSHP->nShapeType == SHP_MULTIPOINTM)
1446        nRequiredSize += 16 + nPoints * 8;
1447    if (nRequiredSize > nEntitySize)
1448    {
1449      free(shape->line);
1450      shape->type = MS_SHAPE_NULL;
1451      msSetError(MS_SHPERR, "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d",
1452                 "msSHPReadShape()", hEntity, nPoints, nEntitySize); 
1453      return;
1454    }
1455
1456    shape->numlines = 1;
1457    shape->line[0].numpoints = nPoints;
1458    shape->line[0].point = (pointObj *) malloc( nPoints * sizeof(pointObj) );
1459    if (shape->line[0].point == NULL)
1460    {
1461      free(shape->line);
1462      shape->numlines = 0;
1463      shape->type = MS_SHAPE_NULL;
1464      msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()");
1465      return;
1466    }
1467     
1468    for( i = 0; i < nPoints; i++ ) {
1469      memcpy(&(shape->line[0].point[i].x), psSHP->pabyRec + 48 + 16 * i, 8 );
1470      memcpy(&(shape->line[0].point[i].y), psSHP->pabyRec + 48 + 16 * i + 8, 8 );
1471       
1472      if( bBigEndian ) {
1473              SwapWord( 8, &(shape->line[0].point[i].x) );
1474        SwapWord( 8, &(shape->line[0].point[i].y) );
1475      }
1476
1477#ifdef USE_POINT_Z_M
1478      /* -------------------------------------------------------------------- */
1479      /*      MulipointZ                                                      */
1480      /* -------------------------------------------------------------------- */
1481      shape->line[0].point[i].z = 0; /* initialize */
1482      if (psSHP->nShapeType == SHP_MULTIPOINTZ) {
1483        nOffset = 48 + 16*nPoints;
1484        memcpy(&(shape->line[0].point[i].z), psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1485        if( bBigEndian ) SwapWord( 8, &(shape->line[0].point[i].z));
1486      }
1487
1488      /* -------------------------------------------------------------------- */
1489      /*      Measured shape : multipont.                                     */
1490      /* -------------------------------------------------------------------- */
1491      shape->line[0].point[i].m = 0; /* initialize */
1492      if (psSHP->nShapeType == SHP_MULTIPOINTM) {
1493        nOffset = 48 + 16*nPoints;
1494        memcpy(&(shape->line[0].point[i].m), psSHP->pabyRec + nOffset + 16 + i*8, 8 );
1495        if( bBigEndian ) SwapWord( 8, &(shape->line[0].point[i].m));
1496      }
1497#endif /* USE_POINT_Z_M */
1498    }
1499
1500    shape->type = MS_SHAPE_POINT;
1501  }
1502
1503  /* -------------------------------------------------------------------- */
1504  /*  Extract a Point.                                              */
1505  /* -------------------------------------------------------------------- */
1506  else if(psSHP->nShapeType == SHP_POINT ||  psSHP->nShapeType == SHP_POINTM ||
1507          psSHP->nShapeType == SHP_POINTZ) {   
1508
1509    if (nEntitySize < 20 + 8)
1510    {
1511      shape->type = MS_SHAPE_NULL;
1512      msSetError(MS_SHPERR, "Corrupted feature encountered.  recSize of feature %d=%d", "msSHPReadShape()",
1513                 hEntity, msSHXReadSize(psSHP, hEntity));
1514      return;
1515    }
1516
1517    /* -------------------------------------------------------------------- */
1518    /*      Fill the shape structure.                                       */
1519    /* -------------------------------------------------------------------- */
1520    shape->line = (lineObj *)malloc(sizeof(lineObj));
1521    MS_CHECK_ALLOC_NO_RET(shape->line, sizeof(lineObj));
1522
1523    shape->numlines = 1;
1524    shape->line[0].numpoints = 1;
1525    shape->line[0].point = (pointObj *) msSmallMalloc(sizeof(pointObj));
1526     
1527    memcpy( &(shape->line[0].point[0].x), psSHP->pabyRec + 12, 8 );
1528    memcpy( &(shape->line[0].point[0].y), psSHP->pabyRec + 20, 8 );
1529     
1530    if( bBigEndian ) {
1531      SwapWord( 8, &(shape->line[0].point[0].x));
1532      SwapWord( 8, &(shape->line[0].point[0].y));
1533    }
1534
1535#ifdef USE_POINT_Z_M
1536    /* -------------------------------------------------------------------- */
1537    /*      PointZ                                                          */
1538    /* -------------------------------------------------------------------- */
1539    shape->line[0].point[0].z = 0; /* initialize */
1540    if (psSHP->nShapeType == SHP_POINTZ) {
1541      nOffset = 20 + 8;
1542      if( nEntitySize >= nOffset + 8 ) {
1543        memcpy(&(shape->line[0].point[0].z), psSHP->pabyRec + nOffset, 8 );       
1544        if( bBigEndian ) SwapWord( 8, &(shape->line[0].point[0].z));
1545      }
1546    }
1547
1548    /* -------------------------------------------------------------------- */
1549    /*      Measured support : point.                                       */
1550    /* -------------------------------------------------------------------- */
1551    shape->line[0].point[0].m = 0; /* initialize */
1552    if (psSHP->nShapeType == SHP_POINTM) {
1553      nOffset = 20 + 8;
1554      if( nEntitySize >= nOffset + 8 ) {
1555        memcpy(&(shape->line[0].point[0].m), psSHP->pabyRec + nOffset, 8 );
1556        if( bBigEndian ) SwapWord( 8, &(shape->line[0].point[0].m));
1557      }
1558    }
1559#endif /* USE_POINT_Z_M */
1560
1561    /* set the bounding box to the point */
1562    shape->bounds.minx = shape->bounds.maxx = shape->line[0].point[0].x;
1563    shape->bounds.miny = shape->bounds.maxy = shape->line[0].point[0].y;
1564
1565    shape->type = MS_SHAPE_POINT;
1566  }
1567
1568  shape->index = hEntity;
1569
1570  return;
1571}
1572
1573int msSHPReadBounds( SHPHandle psSHP, int hEntity, rectObj *padBounds)
1574{
1575  /* -------------------------------------------------------------------- */
1576  /*      Validate the record/entity number.                              */
1577  /* -------------------------------------------------------------------- */
1578  if( psSHP->nRecords <= 0 || hEntity < -1 || hEntity >= psSHP->nRecords ) {
1579    padBounds->minx = padBounds->miny = padBounds->maxx = padBounds->maxy = 0.0;
1580    return MS_FAILURE;
1581  }
1582
1583  /* -------------------------------------------------------------------- */
1584  /*    If the entity is -1 we fetch the bounds for the whole file.       */
1585  /* -------------------------------------------------------------------- */
1586  if( hEntity == -1 ) {
1587    padBounds->minx = psSHP->adBoundsMin[0];
1588    padBounds->miny = psSHP->adBoundsMin[1];
1589    padBounds->maxx = psSHP->adBoundsMax[0];
1590    padBounds->maxy = psSHP->adBoundsMax[1];
1591  } else {   
1592   
1593    if( msSHXReadSize(psSHP, hEntity) == 4 ) { /* NULL shape */
1594      padBounds->minx = padBounds->miny = padBounds->maxx = padBounds->maxy = 0.0;
1595      return MS_FAILURE;
1596    } 
1597   
1598    if( psSHP->nShapeType != SHP_POINT && psSHP->nShapeType != SHP_POINTZ && psSHP->nShapeType != SHP_POINTM) {
1599      fseek( psSHP->fpSHP, msSHXReadOffset(psSHP, hEntity) + 12, 0 );
1600      fread( padBounds, sizeof(double)*4, 1, psSHP->fpSHP );
1601
1602      if( bBigEndian ) {
1603        SwapWord( 8, &(padBounds->minx) );
1604        SwapWord( 8, &(padBounds->miny) );
1605        SwapWord( 8, &(padBounds->maxx) );
1606        SwapWord( 8, &(padBounds->maxy) );
1607      }
1608
1609      if(msIsNan(padBounds->minx)) { /* empty shape */
1610        padBounds->minx = padBounds->miny = padBounds->maxx = padBounds->maxy = 0.0;
1611        return MS_FAILURE;
1612      }
1613    } else {
1614      /* -------------------------------------------------------------------- */
1615      /*      For points we fetch the point, and duplicate it as the          */
1616      /*      minimum and maximum bound.                                      */
1617      /* -------------------------------------------------------------------- */
1618     
1619      fseek( psSHP->fpSHP, msSHXReadOffset(psSHP, hEntity) + 12, 0 );
1620      fread( padBounds, sizeof(double)*2, 1, psSHP->fpSHP );
1621     
1622      if( bBigEndian ) {
1623        SwapWord( 8, &(padBounds->minx) );
1624        SwapWord( 8, &(padBounds->miny) );
1625      }
1626     
1627      padBounds->maxx = padBounds->minx;
1628      padBounds->maxy = padBounds->miny;
1629    }
1630  }
1631
1632  return MS_SUCCESS;
1633}
1634
1635int msShapefileOpen(shapefileObj *shpfile, char *mode, char *filename, int log_failures)
1636{
1637  int i;
1638  char *dbfFilename;
1639  size_t bufferSize = 0;
1640
1641  if(!filename) {
1642    if( log_failures )
1643      msSetError(MS_IOERR, "No (NULL) filename provided.", "msShapefileOpen()");
1644    return(-1);
1645  }
1646
1647  /* initialize a few things */
1648  shpfile->status = NULL;
1649  shpfile->lastshape = -1;
1650  shpfile->isopen = MS_FALSE;
1651
1652  /* open the shapefile file (appending ok) and get basic info */
1653  if(!mode)     
1654    shpfile->hSHP = msSHPOpen( filename, "rb");
1655  else
1656    shpfile->hSHP = msSHPOpen( filename, mode);
1657
1658  if(!shpfile->hSHP) {
1659    if( log_failures )
1660      msSetError(MS_IOERR, "(%s)", "msShapefileOpen()", filename);
1661    return(-1);
1662  }
1663
1664  strlcpy(shpfile->source, filename, sizeof(shpfile->source));
1665 
1666  /* load some information about this shapefile */
1667  msSHPGetInfo( shpfile->hSHP, &shpfile->numshapes, &shpfile->type);
1668  msSHPReadBounds( shpfile->hSHP, -1, &(shpfile->bounds));
1669 
1670  bufferSize = strlen(filename)+5;
1671  dbfFilename = (char *)msSmallMalloc(bufferSize);
1672  dbfFilename[0] = '\0';
1673  strcpy(dbfFilename, filename);
1674 
1675  /* clean off any extention the filename might have */
1676  for (i = strlen(dbfFilename) - 1; 
1677       i > 0 && dbfFilename[i] != '.' && dbfFilename[i] != '/' && dbfFilename[i] != '\\';
1678       i-- ) {}
1679
1680  if( dbfFilename[i] == '.' )
1681    dbfFilename[i] = '\0';
1682 
1683  strlcat(dbfFilename, ".dbf", bufferSize);
1684
1685  shpfile->hDBF = msDBFOpen(dbfFilename, "rb");
1686
1687  if(!shpfile->hDBF) {
1688    if( log_failures )
1689      msSetError(MS_IOERR, "(%s)", "msShapefileOpen()", dbfFilename);   
1690    free(dbfFilename);
1691    return(-1);
1692  }
1693  free(dbfFilename);
1694
1695  shpfile->isopen = MS_TRUE;
1696  return(0); /* all o.k. */
1697}
1698
1699/* Creates a new shapefile */
1700int msShapefileCreate(shapefileObj *shpfile, char *filename, int type)
1701{
1702  if(type != SHP_POINT && type != SHP_MULTIPOINT && type != SHP_ARC &&
1703     type != SHP_POLYGON && 
1704     type != SHP_POINTM && type != SHP_MULTIPOINTM &&
1705     type != SHP_ARCM && type != SHP_POLYGONM && 
1706     type != SHP_POINTZ && type != SHP_MULTIPOINTZ &&
1707     type != SHP_ARCZ && type != SHP_POLYGONZ) {
1708    msSetError(MS_SHPERR, "Invalid shape type.", "msNewSHPFile()");
1709    return(-1);
1710  }
1711
1712  /* create the spatial portion */
1713  shpfile->hSHP = msSHPCreate(filename, type);
1714  if(!shpfile->hSHP) {
1715    msSetError(MS_IOERR, "(%s)", "msNewSHPFile()",filename);   
1716    return(-1);
1717  }
1718
1719  /* retrieve a few things about this shapefile */
1720  msSHPGetInfo( shpfile->hSHP, &shpfile->numshapes, &shpfile->type);
1721  msSHPReadBounds( shpfile->hSHP, -1, &(shpfile->bounds));
1722
1723  /* initialize a few other things */
1724  shpfile->status = NULL;
1725  shpfile->lastshape = -1;
1726  shpfile->isopen = MS_TRUE;
1727
1728  shpfile->hDBF = NULL; /* XBase file is NOT created here... */
1729  return(0);
1730}
1731
1732void msShapefileClose(shapefileObj *shpfile)
1733{
1734  if (shpfile && shpfile->isopen == MS_TRUE) { /* Silently return if called with NULL shpfile by freeLayer() */
1735    if(shpfile->hSHP) msSHPClose(shpfile->hSHP);
1736    if(shpfile->hDBF) msDBFClose(shpfile->hDBF);
1737    if(shpfile->status) free(shpfile->status);
1738    shpfile->isopen = MS_FALSE;
1739  }
1740}
1741
1742/* status array lives in the shpfile, can return MS_SUCCESS/MS_FAILURE/MS_DONE */
1743int msShapefileWhichShapes(shapefileObj *shpfile, rectObj rect, int debug)
1744{
1745  int i;
1746  rectObj shaperect;
1747  char *filename;
1748  char *sourcename = 0; /* shape file source string from map file */
1749  char *s = 0; /* pointer to start of '.shp' in source string */
1750 
1751  if(shpfile->status) {
1752    free(shpfile->status);
1753    shpfile->status = NULL;
1754  }
1755
1756  shpfile->statusbounds = rect; /* save the search extent */
1757
1758  /* rect and shapefile DON'T overlap... */
1759  if(msRectOverlap(&shpfile->bounds, &rect) != MS_TRUE) 
1760    return(MS_DONE);
1761
1762  if(msRectContained(&shpfile->bounds, &rect) == MS_TRUE) {
1763    shpfile->status = msAllocBitArray(shpfile->numshapes);
1764    if(!shpfile->status) {
1765      msSetError(MS_MEMERR, NULL, "msShapefileWhichShapes()");
1766      return(MS_FAILURE);
1767    }
1768    msSetAllBits(shpfile->status, shpfile->numshapes, 1);
1769  } 
1770  else {
1771
1772    /* deal with case where sourcename is of the form 'file.shp' */
1773    sourcename = msStrdup(shpfile->source);
1774    /* TODO: need to add case-insensitive handling! */
1775    s = strstr(sourcename, ".shp");
1776    if( s ) *s = '\0';
1777
1778    filename = (char *)malloc(strlen(sourcename)+strlen(MS_INDEX_EXTENSION)+1);
1779    MS_CHECK_ALLOC(filename, strlen(sourcename)+strlen(MS_INDEX_EXTENSION)+1, MS_FAILURE);
1780 
1781    sprintf(filename, "%s%s", sourcename, MS_INDEX_EXTENSION);
1782   
1783    shpfile->status = msSearchDiskTree(filename, rect, debug);
1784    free(filename);
1785    free(sourcename);
1786
1787    if(shpfile->status) { /* index  */
1788      msFilterTreeSearch(shpfile, shpfile->status, rect);
1789    }
1790    else { /* no index  */
1791      shpfile->status = msAllocBitArray(shpfile->numshapes);
1792      if(!shpfile->status) {
1793        msSetError(MS_MEMERR, NULL, "msShapefileWhichShapes()");       
1794        return(MS_FAILURE);
1795      }
1796     
1797      for(i=0;i<shpfile->numshapes;i++) {
1798        if(msSHPReadBounds(shpfile->hSHP, i, &shaperect) == MS_SUCCESS)
1799          if(msRectOverlap(&shaperect, &rect) == MS_TRUE) msSetBit(shpfile->status, i, 1);
1800      }
1801    }
1802  }
1803 
1804  shpfile->lastshape = -1;
1805
1806  return(MS_SUCCESS); /* success */
1807}
1808
1809/* Return the absolute path to the given layer's tileindex file's directory */
1810void msTileIndexAbsoluteDir(char *tiFileAbsDir, layerObj *layer)
1811{
1812  char tiFileAbsPath[MS_MAXPATHLEN];
1813  char *tiFileAbsDirTmp=NULL;
1814 
1815  msBuildPath(tiFileAbsPath, layer->map->mappath, layer->tileindex); /* absolute path to tileindex file */
1816  tiFileAbsDirTmp = msGetPath(tiFileAbsPath); /* tileindex file's directory */
1817  strlcpy(tiFileAbsDir, tiFileAbsDirTmp, MS_MAXPATHLEN);
1818  free(tiFileAbsDirTmp);
1819}
1820
1821/*
1822** Build possible paths we might find the tile file at:
1823**   map dir + shape path + filename?
1824**   tile dir + shape path + filename?
1825**   map dir + filename?
1826**
1827** Returns
1828** MS_SUCCESS - found a file
1829** MS_FAILURE - no file, and map is configured to fail on missing
1830** MS_DONE - no file, and map is configured to continue on missing
1831*/
1832int msTiledSHPTryOpen(shapefileObj *shpfile, layerObj *layer, char *tiFileAbsDir, char *filename) {
1833    char szPath[MS_MAXPATHLEN];
1834    int ignore_missing = msMapIgnoreMissingData(layer->map);
1835    int log_failures = MS_TRUE;
1836   
1837    if( ignore_missing == MS_MISSING_DATA_IGNORE ) 
1838      log_failures = MS_FALSE;
1839   
1840    if(msShapefileOpen(shpfile, "rb", msBuildPath3(szPath, layer->map->mappath, layer->map->shapepath, filename), log_failures) == -1) { 
1841      if(msShapefileOpen(shpfile, "rb", msBuildPath3(szPath, tiFileAbsDir, layer->map->shapepath, filename), log_failures) == -1) { 
1842        if(msShapefileOpen(shpfile, "rb", msBuildPath(szPath, layer->map->mappath, filename), log_failures) == -1) { 
1843          if(ignore_missing == MS_MISSING_DATA_FAIL) {
1844            msSetError(MS_IOERR, "Unable to open shapefile '%s' for layer '%s' ... fatal error.", "msTiledSHPTryOpen()", filename, layer->name);
1845            return(MS_FAILURE); 
1846          }
1847          else if( ignore_missing == MS_MISSING_DATA_LOG ) {
1848            if( layer->debug || layer->map->debug ) {
1849              msDebug( "Unable to open shapefile '%s' for layer '%s' ... ignoring this missing data.\n", szPath, layer->name );
1850            }
1851            return(MS_DONE);
1852          }
1853          else if( ignore_missing == MS_MISSING_DATA_IGNORE ) {
1854            return(MS_DONE);
1855          }
1856          else {
1857            /* never get here */
1858            msSetError(MS_IOERR, "msIgnoreMissingData returned unexpected value.", "msTiledSHPTryOpen()");
1859            return(MS_FAILURE);
1860          }
1861        }
1862      } 
1863    }     
1864    return(MS_SUCCESS);
1865}
1866
1867int msTiledSHPOpenFile(layerObj *layer)
1868{
1869  int i;
1870  char *filename, tilename[MS_MAXPATHLEN], szPath[MS_MAXPATHLEN];
1871  char tiFileAbsDir[MS_MAXPATHLEN];
1872
1873  msTiledSHPLayerInfo *tSHP=NULL;
1874 
1875  if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
1876        return MS_FAILURE; 
1877
1878  /* allocate space for a shapefileObj using layer->layerinfo    */
1879  tSHP = (msTiledSHPLayerInfo *) malloc(sizeof(msTiledSHPLayerInfo));
1880  MS_CHECK_ALLOC(tSHP, sizeof(msTiledSHPLayerInfo), MS_FAILURE);
1881
1882  tSHP->shpfile = (shapefileObj *) malloc(sizeof(shapefileObj));
1883  if (tSHP->shpfile == NULL)
1884  {
1885      msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n", "msTiledSHPOpenFile()",
1886                 __FILE__, __LINE__, sizeof(shapefileObj));
1887      free(tSHP);
1888      return MS_FAILURE;
1889  }
1890
1891  tSHP->tileshpfile = NULL; /* may need this if not using a tile layer, look for malloc later */
1892  layer->layerinfo = tSHP;
1893
1894  tSHP->tilelayerindex = msGetLayerIndex(layer->map, layer->tileindex);
1895  if(tSHP->tilelayerindex != -1) { /* does the tileindex reference another layer */
1896    int status;
1897    layerObj *tlp;
1898
1899    tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex));
1900
1901    if(tlp->connectiontype != MS_SHAPEFILE) {
1902          msSetError(MS_SDEERR, "Tileindex layer must be a shapefile.", "msTiledSHPOpenFile()");
1903      return(MS_FAILURE);
1904    }
1905
1906    status = msLayerOpen(tlp);
1907    if(status != MS_SUCCESS) return(MS_FAILURE);
1908
1909     /* build item list */
1910     status = msLayerWhichItems(tlp, MS_FALSE, NULL);
1911     if(status != MS_SUCCESS) return(MS_FAILURE);
1912
1913     tSHP->tileshpfile = (shapefileObj *) tlp->layerinfo; /* shapefiles use layerinfo to point to a shapefileObj */
1914
1915  } else { /* or reference a shapefile directly */
1916
1917    /* we need tSHP->tileshpfile if we're not working with a layer */
1918    tSHP->tileshpfile = (shapefileObj *) malloc(sizeof(shapefileObj));
1919    if (tSHP->tileshpfile == NULL)
1920    {
1921        msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n", "msTiledSHPOpenFile()",
1922                   __FILE__, __LINE__, sizeof(shapefileObj));
1923        free(tSHP->shpfile);
1924        free(tSHP);
1925        return MS_FAILURE;
1926    }
1927
1928
1929    if(msShapefileOpen(tSHP->tileshpfile, "rb", msBuildPath3(szPath, layer->map->mappath, layer->map->shapepath, layer->tileindex), MS_TRUE) == -1) 
1930      if(msShapefileOpen(tSHP->tileshpfile, "rb", msBuildPath(szPath, layer->map->mappath, layer->tileindex), MS_TRUE) == -1)
1931        return(MS_FAILURE);
1932  }
1933
1934  if((layer->tileitemindex = msDBFGetItemIndex(tSHP->tileshpfile->hDBF, layer->tileitem)) == -1) return(MS_FAILURE);
1935 
1936  msTileIndexAbsoluteDir(tiFileAbsDir, layer);
1937
1938  /* position the source at the FIRST tile to use as a template, this is so the functions that fill the iteminfo array have something to work from */
1939  for(i=0; i<tSHP->tileshpfile->numshapes; i++) {
1940    int try_open;
1941   
1942    if(!layer->data) /* assume whole filename is in attribute field */
1943      filename = (char*) msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex);
1944    else { 
1945      snprintf(tilename, sizeof(tilename), "%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex) , layer->data);
1946      filename = tilename;
1947    }
1948     
1949    if(strlen(filename) == 0) continue; /* check again */
1950   
1951    try_open = msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename);
1952    if( try_open == MS_DONE )
1953      continue;
1954    else if (try_open == MS_FAILURE ) 
1955      return(MS_FAILURE);
1956
1957    return(MS_SUCCESS); /* found a template, ok to proceed */
1958  }
1959
1960  msSetError(MS_SHPERR, "Unable to open a single tile to use as a template in layer %s.", "msTiledSHPOpenFile()", layer->name?layer->name:"(null)");
1961  return(MS_FAILURE);
1962}
1963
1964
1965int msTiledSHPWhichShapes(layerObj *layer, rectObj rect, int isQuery)
1966{
1967  int i, status;
1968  char *filename, tilename[MS_MAXPATHLEN];
1969  char tiFileAbsDir[MS_MAXPATHLEN];
1970
1971  msTiledSHPLayerInfo *tSHP=NULL;
1972 
1973  if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
1974        return MS_FAILURE;
1975
1976  tSHP = layer->layerinfo;
1977  if(!tSHP) {
1978    msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPWhichShapes()");
1979    return(MS_FAILURE);
1980  }
1981
1982  msShapefileClose(tSHP->shpfile); /* close previously opened files */
1983
1984  if(tSHP->tilelayerindex != -1) {  /* does the tileindex reference another layer */
1985    layerObj *tlp;
1986    shapeObj tshape;
1987
1988    tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex));
1989    status= msLayerWhichShapes(tlp, rect, isQuery);
1990    if(status != MS_SUCCESS) return(status); /* could be MS_DONE or MS_FAILURE */
1991
1992    msTileIndexAbsoluteDir(tiFileAbsDir, layer);
1993
1994    msInitShape(&tshape);
1995    while((status = msLayerNextShape(tlp, &tshape)) == MS_SUCCESS) {
1996      int try_open;
1997     
1998      /* TODO: seems stupid to read the tileitem seperately from the shape, need to fix msTiledSHPOpenFile */
1999      if(!layer->data) /* assume whole filename is in attribute field */
2000            filename = (char *) msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tshape.index, layer->tileitemindex);
2001      else {
2002            snprintf(tilename, sizeof(tilename), "%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tshape.index, layer->tileitemindex) , layer->data);
2003            filename = tilename;
2004      }
2005
2006      if(strlen(filename) == 0) continue; /* check again */
2007
2008      try_open = msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename);
2009      if( try_open == MS_DONE ) 
2010        continue;
2011      else if (try_open == MS_FAILURE ) 
2012        return(MS_FAILURE);
2013
2014      status = msShapefileWhichShapes(tSHP->shpfile, rect, layer->debug);
2015      if(status == MS_DONE) {
2016          /* Close and continue to next tile */
2017          msShapefileClose(tSHP->shpfile);
2018          continue;
2019      }
2020      else if(status != MS_SUCCESS) {
2021          msShapefileClose(tSHP->shpfile);
2022          return(MS_FAILURE);
2023      }
2024       
2025      /* the layer functions keeps track of this */
2026      /* tSHP->tileshpfile->lastshape = tshape.index; */
2027      break;
2028    }
2029    return(status); /* if we reach here we either 1) ran out of tiles or 2) had an error reading a tile */
2030
2031  } else { /* or reference a shapefile directly */
2032    int try_open;
2033   
2034    status = msShapefileWhichShapes(tSHP->tileshpfile, rect, layer->debug);
2035    if(status != MS_SUCCESS) return(status); /* could be MS_DONE or MS_FAILURE */
2036
2037    msTileIndexAbsoluteDir(tiFileAbsDir, layer);
2038
2039    /* position the source at the FIRST shapefile */
2040    for(i=0; i<tSHP->tileshpfile->numshapes; i++) {
2041      if(msGetBit(tSHP->tileshpfile->status,i)) {
2042        if(!layer->data) /* assume whole filename is in attribute field */
2043              filename = (char *) msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex);
2044        else { 
2045              snprintf(tilename, sizeof(tilename), "%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex) , layer->data);
2046              filename = tilename;
2047        }
2048
2049        if(strlen(filename) == 0) continue; /* check again */
2050
2051        try_open = msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename);
2052        if( try_open == MS_DONE )
2053          continue;
2054        else if (try_open == MS_FAILURE ) 
2055          return(MS_FAILURE);
2056
2057        status = msShapefileWhichShapes(tSHP->shpfile, rect, layer->debug);
2058        if(status == MS_DONE) {
2059            /* Close and continue to next tile */
2060            msShapefileClose(tSHP->shpfile);
2061            continue;
2062        }
2063        else if(status != MS_SUCCESS) {
2064            msShapefileClose(tSHP->shpfile);
2065            return(MS_FAILURE);
2066        }
2067
2068        tSHP->tileshpfile->lastshape = i;
2069        break;
2070      }
2071    }
2072
2073    if(i == tSHP->tileshpfile->numshapes)
2074      return(MS_DONE); /* no more tiles */
2075    else
2076      return(MS_SUCCESS);
2077  }
2078
2079  return(MS_FAILURE); /* should *never* get here */
2080}
2081
2082int msTiledSHPNextShape(layerObj *layer, shapeObj *shape) 
2083{
2084  int i, status, filter_passed = MS_FALSE;
2085  char *filename, tilename[MS_MAXPATHLEN];
2086  char tiFileAbsDir[MS_MAXPATHLEN];
2087
2088  msTiledSHPLayerInfo *tSHP=NULL;
2089 
2090  if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
2091    return MS_FAILURE;
2092 
2093  tSHP = layer->layerinfo;
2094  if(!tSHP) {
2095    msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPNextShape()");
2096    return(MS_FAILURE);
2097  }
2098
2099  msTileIndexAbsoluteDir(tiFileAbsDir, layer);
2100
2101  do {
2102    i = tSHP->shpfile->lastshape + 1;
2103    while(i<tSHP->shpfile->numshapes && !msGetBit(tSHP->shpfile->status,i)) i++; /* next "in" shape */
2104   
2105    if(i == tSHP->shpfile->numshapes) { /* done with this tile, need a new one */
2106      msShapefileClose(tSHP->shpfile); /* clean up */
2107       
2108      /* position the source to the NEXT shapefile based on the tileindex */
2109      if(tSHP->tilelayerindex != -1) { /* does the tileindex reference another layer */
2110        layerObj *tlp;
2111        shapeObj tshape;
2112        int try_open;
2113       
2114        tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex));
2115
2116        msInitShape(&tshape);
2117        while((status = msLayerNextShape(tlp, &tshape)) == MS_SUCCESS) {
2118 
2119          /* TODO: seems stupid to read the tileitem seperately from the shape, need to fix msTiledSHPOpenFile */
2120          if(!layer->data) /* assume whole filename is in attribute field */
2121            filename = (char *) msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tshape.index, layer->tileitemindex);
2122          else {
2123            snprintf(tilename, sizeof(tilename),"%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tshape.index, layer->tileitemindex) , layer->data);
2124            filename = tilename;
2125          }
2126
2127          if(strlen(filename) == 0) continue; /* check again */
2128
2129          try_open = msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename);
2130          if( try_open == MS_DONE )
2131            continue;
2132          else if (try_open == MS_FAILURE ) 
2133            return(MS_FAILURE);
2134
2135          status = msShapefileWhichShapes(tSHP->shpfile, tSHP->tileshpfile->statusbounds, layer->debug);
2136          if(status == MS_DONE) {
2137            /* Close and continue to next tile */
2138            msShapefileClose(tSHP->shpfile);
2139            continue;
2140          } else if(status != MS_SUCCESS) {
2141            msShapefileClose(tSHP->shpfile);
2142            return(MS_FAILURE);
2143          }
2144
2145          /* the layer functions keeps track of this */
2146          /* tSHP->tileshpfile->lastshape = tshape.index; */
2147          break;
2148        }
2149       
2150        if(status == MS_DONE) return(MS_DONE); /* no more tiles */
2151        else {
2152          msFreeShape(&tshape); 
2153          continue; /* we've got shapes */
2154        }
2155
2156      } else { /* or reference a shapefile directly   */
2157
2158        for(i=(tSHP->tileshpfile->lastshape + 1); i<tSHP->tileshpfile->numshapes; i++) {
2159          if(msGetBit(tSHP->tileshpfile->status,i)) {
2160            int try_open;
2161           
2162            if(!layer->data) /* assume whole filename is in attribute field */
2163              filename = (char*)msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex);
2164            else { 
2165              snprintf(tilename, sizeof(tilename),"%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex) , layer->data);
2166              filename = tilename;
2167                  }
2168
2169            if(strlen(filename) == 0) continue; /* check again */
2170
2171            try_open = msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename);
2172            if( try_open == MS_DONE )
2173              continue;
2174            else if (try_open == MS_FAILURE ) 
2175              return(MS_FAILURE);
2176
2177            status = msShapefileWhichShapes(tSHP->shpfile, tSHP->tileshpfile->statusbounds, layer->debug);
2178            if(status == MS_DONE) {
2179              /* Close and continue to next tile */
2180              msShapefileClose(tSHP->shpfile);
2181              continue;
2182            } else if(status != MS_SUCCESS) {
2183              msShapefileClose(tSHP->shpfile);
2184              return(MS_FAILURE);
2185            }
2186         
2187            tSHP->tileshpfile->lastshape = i;
2188            break;
2189          }
2190        } /* end for loop */
2191     
2192        if(i == tSHP->tileshpfile->numshapes) return(MS_DONE); /* no more tiles */
2193        else continue; /* we've got shapes */
2194      }
2195    }
2196   
2197    tSHP->shpfile->lastshape = i;
2198 
2199    msSHPReadShape(tSHP->shpfile->hSHP, i, shape);
2200    if(shape->type == MS_SHAPE_NULL) {
2201      msFreeShape(shape);
2202      continue; /* skip NULL shapes */
2203    }
2204    shape->tileindex = tSHP->tileshpfile->lastshape;
2205    shape->values = msDBFGetValueList(tSHP->shpfile->hDBF, i, layer->iteminfo, layer->numitems);
2206    shape->numvalues = layer->numitems;
2207
2208    filter_passed = MS_TRUE;  /* By default accept ANY shape */
2209    if(layer->numitems > 0 && layer->iteminfo) {
2210      filter_passed = msEvalExpression(layer, shape, &(layer->filter), layer->filteritemindex);
2211    }
2212   
2213    if(!filter_passed) msFreeShape(shape); /* free's values as well */
2214
2215  } while(!filter_passed);  /* Loop until both spatial and attribute filters match  */
2216
2217  return(MS_SUCCESS);
2218}
2219
2220int msTiledSHPGetShape(layerObj *layer, shapeObj *shape, resultObj *record) 
2221{
2222  char *filename, tilename[MS_MAXPATHLEN], szPath[MS_MAXPATHLEN];
2223
2224  msTiledSHPLayerInfo *tSHP=NULL;
2225  char tiFileAbsDir[MS_MAXPATHLEN];
2226
2227  long shapeindex = record->shapeindex;
2228  int tileindex = record->tileindex;
2229 
2230  if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
2231    return MS_FAILURE;
2232
2233  tSHP = layer->layerinfo;
2234  if(!tSHP) {
2235    msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPGetShape()");
2236    return(MS_FAILURE);
2237  }
2238
2239  if((tileindex < 0) || (tileindex >= tSHP->tileshpfile->numshapes)) return(MS_FAILURE); /* invalid tile id */
2240
2241  if(tileindex != tSHP->tileshpfile->lastshape) { /* correct tile is not currenly open so open the correct tile */
2242    msShapefileClose(tSHP->shpfile); /* close current tile */
2243
2244    if(!layer->data) /* assume whole filename is in attribute field */
2245      filename = (char*) msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tileindex, layer->tileitemindex);
2246    else { 
2247      snprintf(tilename, sizeof(tilename), "%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tileindex, layer->tileitemindex) , layer->data);
2248      filename = tilename;
2249    }
2250     
2251    /* open the shapefile, since a specific tile was request an error should be generated if that tile does not exist */
2252    if(strlen(filename) == 0) return(MS_FAILURE);
2253    if(msShapefileOpen(tSHP->shpfile, "rb", msBuildPath3(szPath, tiFileAbsDir, layer->map->shapepath, filename), MS_TRUE) == -1) { 
2254      if(msShapefileOpen(tSHP->shpfile, "rb", msBuildPath3(szPath, layer->map->mappath, layer->map->shapepath, filename), MS_TRUE) == -1) { 
2255        if(msShapefileOpen(tSHP->shpfile, "rb", msBuildPath(szPath, layer->map->mappath, filename), MS_TRUE) == -1) { 
2256          return(MS_FAILURE); 
2257        }
2258      } 
2259    }   
2260
2261  }
2262
2263  if((shapeindex < 0) || (shapeindex >= tSHP->shpfile->numshapes)) return(MS_FAILURE);
2264
2265  msSHPReadShape(tSHP->shpfile->hSHP, shapeindex, shape);
2266  tSHP->shpfile->lastshape = shapeindex;
2267
2268  if(layer->numitems > 0 && layer->iteminfo) {
2269    shape->numvalues = layer->numitems;
2270    shape->values = msDBFGetValueList(tSHP->shpfile->hDBF, shapeindex, layer->iteminfo, layer->numitems);
2271    if(!shape->values) return(MS_FAILURE);
2272  }
2273
2274  shape->tileindex = tileindex;
2275
2276  return(MS_SUCCESS);
2277}
2278
2279void msTiledSHPClose(layerObj *layer) 
2280{ 
2281  msTiledSHPLayerInfo *tSHP=NULL;
2282
2283  tSHP = layer->layerinfo;
2284  if(tSHP) {
2285    msShapefileClose(tSHP->shpfile);
2286    free(tSHP->shpfile);
2287 
2288    if(tSHP->tilelayerindex != -1) {
2289      layerObj *tlp;
2290          if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
2291            return;
2292      tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex));
2293      msLayerClose(tlp);
2294    } else { 
2295      msShapefileClose(tSHP->tileshpfile);
2296      free(tSHP->tileshpfile);
2297    }
2298                       
2299    free(tSHP);
2300  }
2301  layer->layerinfo = NULL;
2302}
2303/************************************************************************/
2304/*                              msTiledSHPClose()                       */
2305/* Overloaded version of msTiledSHPClose for virtual table architecture */
2306/************************************************************************/
2307int msTiledSHPCloseVT(layerObj *layer) 
2308{
2309  msTiledSHPClose(layer);
2310  return MS_SUCCESS;
2311}
2312
2313void msTiledSHPLayerFreeItemInfo(layerObj *layer)
2314{
2315  if(layer->iteminfo) {
2316    free(layer->iteminfo);
2317    layer->iteminfo = NULL;
2318  }
2319}
2320
2321int msTiledSHPLayerInitItemInfo(layerObj *layer)
2322{
2323  msTiledSHPLayerInfo *tSHP=NULL;
2324
2325  tSHP = layer->layerinfo;
2326  if(!tSHP) {
2327    msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPLayerInitItemInfo()");
2328    return MS_FAILURE;
2329  }
2330
2331  msTiledSHPLayerFreeItemInfo(layer);
2332  layer->iteminfo = (int *) msDBFGetItemIndexes(tSHP->shpfile->hDBF, layer->items, layer->numitems);
2333  if(!layer->iteminfo) return(MS_FAILURE);
2334
2335  return MS_SUCCESS;
2336}
2337
2338static void msSHPPassThroughFieldDefinitions( layerObj *layer, DBFHandle hDBF )
2339{
2340    int numitems, i;
2341 
2342    numitems = msDBFGetFieldCount( hDBF );
2343
2344    for(i=0;i<numitems;i++)
2345    {
2346        char item[16];
2347        int  nWidth=0, nPrecision=0;
2348        char md_item_name[64];
2349        char gml_width[32], gml_precision[32];
2350        DBFFieldType eType;
2351        const char *gml_type = NULL;
2352
2353        eType = msDBFGetFieldInfo( hDBF, i, item, &nWidth, &nPrecision );
2354       
2355        gml_width[0] = '\0';
2356        gml_precision[0] = '\0';
2357
2358        switch( eType )
2359        {
2360          case FTInteger:
2361            gml_type = "Integer";
2362            sprintf( gml_width, "%d", nWidth ); 
2363            break;
2364
2365          case FTDouble:
2366            gml_type = "Real";
2367            sprintf( gml_width, "%d", nWidth ); 
2368            sprintf( gml_precision, "%d", nPrecision ); 
2369            break;
2370
2371          case FTString:
2372          default:
2373            gml_type = "Character";
2374            sprintf( gml_width, "%d", nWidth ); 
2375            break;
2376        }
2377
2378        snprintf( md_item_name, sizeof(md_item_name), "gml_%s_type", item );
2379        if( msOWSLookupMetadata(&(layer->metadata), "G", "type") == NULL )
2380            msInsertHashTable(&(layer->metadata), md_item_name, gml_type );
2381       
2382        snprintf( md_item_name, sizeof(md_item_name), "gml_%s_width", item );
2383        if( strlen(gml_width) > 0 
2384            && msOWSLookupMetadata(&(layer->metadata), "G", "width") == NULL )
2385            msInsertHashTable(&(layer->metadata), md_item_name, gml_width );
2386
2387        snprintf( md_item_name, sizeof(md_item_name), "gml_%s_precision",item );
2388        if( strlen(gml_precision) > 0 
2389            && msOWSLookupMetadata(&(layer->metadata), "G", "precision")==NULL )
2390            msInsertHashTable(&(layer->metadata), md_item_name, gml_precision );
2391    }
2392}
2393
2394int msTiledSHPLayerGetItems(layerObj *layer) 
2395{
2396  msTiledSHPLayerInfo *tSHP=NULL;
2397  const char *value;
2398
2399  tSHP = layer->layerinfo;
2400  if(!tSHP) {
2401    msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPLayerGetItems()");
2402    return MS_FAILURE;
2403  }
2404
2405  layer->numitems = msDBFGetFieldCount(tSHP->shpfile->hDBF);
2406  layer->items = msDBFGetItems(tSHP->shpfile->hDBF);   
2407  if(!layer->items) return MS_FAILURE;
2408
2409/* -------------------------------------------------------------------- */
2410/*      consider populating the field definitions in metadata.          */
2411/* -------------------------------------------------------------------- */
2412  if((value = msOWSLookupMetadata(&(layer->metadata), "G", "types")) != NULL
2413     && strcasecmp(value,"auto") == 0 )
2414      msSHPPassThroughFieldDefinitions( layer, tSHP->shpfile->hDBF );
2415
2416  return msTiledSHPLayerInitItemInfo(layer);
2417}
2418
2419int msTiledSHPLayerGetExtent(layerObj *layer, rectObj *extent) 
2420{
2421  msTiledSHPLayerInfo *tSHP=NULL;
2422
2423  tSHP = layer->layerinfo;
2424  if(!tSHP) {
2425    msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPLayerGetExtent()");
2426    return MS_FAILURE;
2427  }
2428
2429  *extent = tSHP->tileshpfile->bounds;
2430  return MS_SUCCESS;
2431}
2432
2433int msTiledSHPLayerIsOpen(layerObj *layer)
2434{
2435  if(layer->layerinfo)
2436    return MS_TRUE;
2437  else
2438    return MS_FALSE;
2439}
2440
2441int msTiledSHPLayerSupportsCommonFilters(layerObj *layer)
2442{
2443  return MS_TRUE;
2444}
2445
2446int msTiledSHPLayerInitializeVirtualTable(layerObj *layer)
2447{
2448  assert(layer != NULL);
2449  assert(layer->vtable != NULL);
2450
2451  layer->vtable->LayerSupportsCommonFilters = msTiledSHPLayerSupportsCommonFilters;
2452  layer->vtable->LayerInitItemInfo = msTiledSHPLayerInitItemInfo;
2453  layer->vtable->LayerFreeItemInfo = msTiledSHPLayerFreeItemInfo;
2454  layer->vtable->LayerOpen = msTiledSHPOpenFile;
2455  layer->vtable->LayerIsOpen = msTiledSHPLayerIsOpen;
2456  layer->vtable->LayerWhichShapes = msTiledSHPWhichShapes;
2457  layer->vtable->LayerNextShape = msTiledSHPNextShape;
2458  /* no special version, use ...GetShape() */
2459  /* layer->vtable->LayerResultsGetShape = msTiledSHPGetShape; */ 
2460  layer->vtable->LayerGetShape = msTiledSHPGetShape;
2461  layer->vtable->LayerClose = msTiledSHPCloseVT;
2462  layer->vtable->LayerGetItems = msTiledSHPLayerGetItems;
2463  layer->vtable->LayerGetExtent = msTiledSHPLayerGetExtent;
2464  /* layer->vtable->LayerApplyFilterToLayer, use default */
2465  /* layer->vtable->LayerGetAutoStyle, use default */
2466  /* layer->vtable->LayerCloseConnection, use default */;
2467  layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter;
2468  /* layer->vtable->LayerCreateItems, use default */
2469  /* layer->vtable->LayerGetNumFeatures, use default */
2470  /* layer->vtable->LayerGetAutoProjection, use defaut*/
2471
2472  return MS_SUCCESS;
2473}
2474
2475/* SHAPEFILE Layer virtual table functions */
2476
2477void msSHPLayerFreeItemInfo(layerObj *layer) 
2478{ 
2479  if(layer->iteminfo) {
2480    free(layer->iteminfo);
2481    layer->iteminfo = NULL;
2482  }
2483}
2484
2485int msSHPLayerInitItemInfo(layerObj *layer) 
2486{
2487  shapefileObj *shpfile = shpfile = layer->layerinfo;
2488  if( ! shpfile) {
2489    msSetError(MS_SHPERR, "Shapefile layer has not been opened.", "msSHPLayerInitItemInfo()");
2490    return MS_FAILURE;
2491  }
2492
2493  /* iteminfo needs to be a bit more complex, a list of indexes plus the length of the list */
2494  msSHPLayerFreeItemInfo(layer);
2495  layer->iteminfo = (int *) msDBFGetItemIndexes(shpfile->hDBF, layer->items, layer->numitems);
2496  if( ! layer->iteminfo) {
2497    return MS_FAILURE;
2498  }
2499
2500  return MS_SUCCESS;
2501}
2502
2503int msSHPLayerOpen(layerObj *layer)
2504{
2505  char szPath[MS_MAXPATHLEN];
2506  shapefileObj *shpfile;
2507
2508  if(layer->layerinfo) return MS_SUCCESS; /* layer already open */
2509   
2510  /* allocate space for a shapefileObj using layer->layerinfo  */
2511  shpfile = (shapefileObj *) malloc(sizeof(shapefileObj));
2512  MS_CHECK_ALLOC(shpfile, sizeof(shapefileObj), MS_FAILURE);
2513
2514    if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
2515                return MS_FAILURE;
2516   
2517
2518  layer->layerinfo = shpfile;
2519
2520  if(msShapefileOpen(shpfile, "rb", msBuildPath3(szPath, layer->map->mappath, layer->map->shapepath, layer->data), MS_TRUE) == -1) {
2521    if(msShapefileOpen(shpfile, "rb", msBuildPath(szPath, layer->map->mappath, layer->data), MS_TRUE) == -1) {
2522      layer->layerinfo = NULL;
2523      free(shpfile);
2524      return MS_FAILURE;
2525    }
2526  }
2527   
2528  return MS_SUCCESS;
2529}
2530
2531int msSHPLayerIsOpen(layerObj *layer)
2532{
2533  if(layer->layerinfo)
2534    return MS_TRUE;
2535  else
2536    return MS_FALSE;
2537}
2538
2539int msSHPLayerWhichShapes(layerObj *layer, rectObj rect, int isQuery)
2540{
2541  int i, n1=0, n2=0;
2542  int status;
2543  shapefileObj *shpfile;
2544
2545  shpfile = layer->layerinfo;
2546
2547  if(!shpfile) {
2548    msSetError(MS_SHPERR, "Shapefile layer has not been opened.", "msSHPLayerWhichShapes()");
2549    return MS_FAILURE;
2550  }
2551
2552  status = msShapefileWhichShapes(shpfile, rect, layer->debug);
2553  if(status != MS_SUCCESS) {
2554    return status;
2555  }
2556
2557  /* now apply the maxshapes criteria (NOTE: this ignores the filter so you could get less than maxfeatures) */
2558  if(layer->maxfeatures > 0) {
2559
2560    for( i = (shpfile->numshapes - 1); i >= 0; i-- ) {
2561      n2 = msGetBit(shpfile->status, i);
2562      n1 += n2;
2563      if( n2 && n1 > layer->maxfeatures ) {
2564        msSetBit(shpfile->status, i, 0);
2565      }
2566    }
2567
2568  }
2569   
2570  return MS_SUCCESS;
2571}
2572
2573int msSHPLayerNextShape(layerObj *layer, shapeObj *shape) 
2574{
2575  int i, filter_passed=MS_FALSE;
2576  shapefileObj *shpfile;
2577
2578  shpfile = layer->layerinfo;
2579
2580  if(!shpfile) {
2581    msSetError(MS_SHPERR, "Shapefile layer has not been opened.", "msSHPLayerNextShape()");
2582    return MS_FAILURE;
2583  }   
2584 
2585  do {
2586    i = msGetNextBit(shpfile->status, shpfile->lastshape + 1, shpfile->numshapes);
2587    shpfile->lastshape = i;
2588    if(i == -1) return(MS_DONE); /* nothing else to read */
2589
2590    msSHPReadShape(shpfile->hSHP, i, shape);
2591    if(shape->type == MS_SHAPE_NULL) {
2592      msFreeShape(shape);
2593      continue; /* skip NULL shapes */
2594    }
2595    shape->values = msDBFGetValueList(shpfile->hDBF, i, layer->iteminfo, layer->numitems);
2596    shape->numvalues = layer->numitems;
2597
2598    filter_passed = MS_TRUE;  /* By default accept ANY shape */
2599    if(layer->numitems > 0 && layer->iteminfo) {
2600      filter_passed = msEvalExpression(layer, shape, &(layer->filter), layer->filteritemindex);
2601    }
2602
2603    if(!filter_passed) msFreeShape(shape);
2604  } while(!filter_passed);  /* Loop until both spatial and attribute filters match */
2605
2606  return MS_SUCCESS;
2607}
2608
2609int msSHPLayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record)
2610{
2611  shapefileObj *shpfile;
2612  long shapeindex;
2613
2614  shpfile = layer->layerinfo;
2615
2616  shapeindex = record->shapeindex;
2617
2618  if(!shpfile) {
2619    msSetError(MS_SHPERR, "Shapefile layer has not been opened.", "msSHPLayerGetShape()");
2620    return MS_FAILURE;
2621  }
2622
2623  /* msSHPReadShape *should* return success or failure so we don't have to test here */
2624  if(shapeindex < 0 || shapeindex >= shpfile->numshapes) {
2625    msSetError(MS_MISCERR, "Invalid feature id.", "msSHPLayerGetShape()");
2626    return MS_FAILURE;
2627  }
2628
2629  msSHPReadShape(shpfile->hSHP, shapeindex, shape);
2630  if(layer->numitems > 0 && layer->iteminfo) {
2631    shape->numvalues = layer->numitems;
2632    shape->values = msDBFGetValueList(shpfile->hDBF, shapeindex, layer->iteminfo, layer->numitems);
2633    if(!shape->values) return MS_FAILURE;
2634  }
2635
2636  return MS_SUCCESS;
2637}
2638
2639int msSHPLayerClose(layerObj *layer) 
2640{
2641  shapefileObj *shpfile;
2642  shpfile = layer->layerinfo;
2643  if(!shpfile) return MS_SUCCESS; /* nothing to do */ 
2644
2645  msShapefileClose(shpfile);
2646  free(layer->layerinfo);
2647  layer->layerinfo = NULL;
2648
2649  return MS_SUCCESS; 
2650}
2651
2652int msSHPLayerGetItems(layerObj *layer) 
2653{
2654  shapefileObj *shpfile;
2655  const char *value;
2656
2657  shpfile = layer->layerinfo;
2658
2659  if(!shpfile) {
2660    msSetError(MS_SHPERR, "Shapefile layer has not been opened.", "msSHPLayerGetItems()");
2661    return MS_FAILURE;
2662  }
2663
2664  layer->numitems = msDBFGetFieldCount(shpfile->hDBF);
2665  layer->items = msDBFGetItems(shpfile->hDBF);
2666  if(layer->numitems == 0) return MS_SUCCESS; /* No items is a valid case (#3147) */
2667  if(!layer->items) return MS_FAILURE;
2668
2669/* -------------------------------------------------------------------- */
2670/*      consider populating the field definitions in metadata.          */
2671/* -------------------------------------------------------------------- */
2672  if((value = msOWSLookupMetadata(&(layer->metadata), "G", "types")) != NULL
2673     && strcasecmp(value,"auto") == 0 )
2674      msSHPPassThroughFieldDefinitions( layer, shpfile->hDBF );
2675
2676  return msLayerInitItemInfo(layer);
2677}
2678
2679int msSHPLayerGetExtent(layerObj *layer, rectObj *extent) 
2680{
2681  *extent = ((shapefileObj*)layer->layerinfo)->bounds;
2682  return MS_SUCCESS;
2683}
2684
2685int msSHPLayerSupportsCommonFilters(layerObj *layer)
2686{
2687  return MS_TRUE;
2688}
2689
2690int msSHPLayerInitializeVirtualTable(layerObj *layer)
2691{
2692  assert(layer != NULL);
2693  assert(layer->vtable != NULL);
2694
2695  layer->vtable->LayerSupportsCommonFilters = msSHPLayerSupportsCommonFilters;
2696  layer->vtable->LayerInitItemInfo = msSHPLayerInitItemInfo;
2697  layer->vtable->LayerFreeItemInfo = msSHPLayerFreeItemInfo;
2698  layer->vtable->LayerOpen = msSHPLayerOpen;
2699  layer->vtable->LayerIsOpen = msSHPLayerIsOpen;
2700  layer->vtable->LayerWhichShapes = msSHPLayerWhichShapes;
2701  layer->vtable->LayerNextShape = msSHPLayerNextShape;
2702  layer->vtable->LayerGetShape = msSHPLayerGetShape;
2703  layer->vtable->LayerClose = msSHPLayerClose;
2704  layer->vtable->LayerGetItems = msSHPLayerGetItems;
2705  layer->vtable->LayerGetExtent = msSHPLayerGetExtent;
2706  /* layer->vtable->LayerGetAutoStyle, use default */
2707  /* layer->vtable->LayerCloseConnection, use default */
2708  layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter;
2709  /* layer->vtable->LayerApplyFilterToLayer, use default */
2710  /* layer->vtable->LayerCreateItems, use default */
2711  /* layer->vtable->LayerGetNumFeatures, use default */
2712
2713  return MS_SUCCESS;
2714}
Note: See TracBrowser for help on using the repository browser.