Skip to content

Instantly share code, notes, and snippets.

@rbranson
Created December 4, 2009 17:14
Show Gist options
  • Select an option

  • Save rbranson/249170 to your computer and use it in GitHub Desktop.

Select an option

Save rbranson/249170 to your computer and use it in GitHub Desktop.
/******************************************************************************
* $Id: mapshape.c 8755 2009-03-10 13:35:19Z jlacroix $
*
* Project: MapServer
* Purpose: Implements support for shapefile access.
* Authors: Steve Lime and Frank Warmerdam
*
* Note:
* This code is entirely based on the previous work of Frank Warmerdam. It is
* essentially shapelib 1.1.5. However, there were enough changes that it was
* incorporated into the MapServer source to avoid confusion. Relicensed with
* permission of Frank Warmerdam (shapelib author). See the README
* for licence details.
*
******************************************************************************
* Copyright (c) 1996-2005 Regents of the University of Minnesota.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of this Software or works derived from this Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#include <limits.h>
#include <assert.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "mapserver.h"
MS_CVSID("$Id: mapshape.c 8755 2009-03-10 13:35:19Z jlacroix $")
/* Only use this macro on 32-bit integers! */
#define SWAP_FOUR_BYTES(data) \
( ((data >> 24) & 0x000000FF) | ((data >> 8) & 0x0000FF00) | \
((data << 8) & 0x00FF0000) | ((data << 24) & 0xFF000000) )
#define _SWAP_EIGHT_BYTES(data) \
( ((data >> 56) & 0x00000000000000FF) | ((data >> 40) & 0x000000000000FF00) | \
((data >> 24) & 0x0000000000FF0000) | ((data >> 8) & 0x00000000FF000000) | \
((data << 8) & 0x000000FF00000000) | ((data << 24) & 0x0000FF0000000000) | \
((data << 40) & 0x00FF000000000000) | ((data << 56) & 0xFF00000000000000) )
#define USE_FAST_SWAP 1
#ifdef USE_FAST_SWAP
#define SWAP_EIGHT_BYTES(data) _SWAP_EIGHT_BYTES((unsigned long long)data)
#else
#define SWAP_EIGHT_BYTES(data) SwapDouble(data)
#endif
const int __endian_i = 1;
#define IS_BIG_ENDIAN ((*((uchar *) &__endian_i) == 1) ? MS_FALSE : MS_TRUE)
#define INT_AT(ptr, byte) (*((int *)(ptr + byte)))
#define DBL_AT(ptr, byte) (*((double *)(ptr + byte)))
#define INTPTR_AT(ptr, byte) ((int *)(ptr + byte))
#define DBLPTR_AT(ptr, byte) ((double *)(ptr + byte))
#define LE_INT(data) (IS_BIG_ENDIAN ? SWAP_FOUR_BYTES(data) : data)
#define BE_INT(data) (IS_BIG_ENDIAN ? data : SWAP_FOUR_BYTES(data))
#define LE_DBL(data) (IS_BIG_ENDIAN ? SWAP_EIGHT_BYTES(data) : data)
#define BE_DBL(data) (IS_BIG_ENDIAN ? data : SWAP_EIGHT_BYTES(data))
#define LE_INT_AT(ptr, byte) LE_INT(INT_AT(ptr, byte))
#define BE_INT_AT(ptr, byte) BE_INT(INT_AT(ptr, byte))
#define LE_DBL_AT(ptr, byte) LE_DBL(DBL_AT(ptr, byte))
#define BE_DBL_AT(ptr, byte) BE_DBL(DBL_AT(ptr, byte))
#define ByteCopy( a, b, c ) memcpy( b, a, c )
static int bBigEndian;
/************************************************************************/
/* SwapWord() */
/* */
/* Swap a 2, 4 or 8 byte word. */
/************************************************************************/
static void SwapWord( int length, void * wordP )
{
int i;
uchar temp;
for( i=0; i < length/2; i++ ) {
temp = ((uchar *) wordP)[i];
((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1];
((uchar *) wordP)[length-i-1] = temp;
}
}
double SwapDouble(double in)
{
double tmp;
unsigned char *d = (unsigned char *)&tmp;
unsigned char *s = (unsigned char *)&in;
d[0] = s[7];
d[1] = s[6];
d[2] = s[5];
d[3] = s[4];
d[4] = s[3];
d[5] = s[2];
d[6] = s[1];
d[7] = s[0];
return tmp;
}
/************************************************************************/
/* SfRealloc() */
/* */
/* A realloc cover function that will access a NULL pointer as */
/* a valid input. */
/************************************************************************/
static void * SfRealloc( void * pMem, int nNewSize )
{
if( pMem == NULL )
return( (void *) malloc(nNewSize) );
else
return( (void *) realloc(pMem,nNewSize) );
}
/************************************************************************/
/* writeHeader() */
/* */
/* Write out a header for the .shp and .shx files as well as the */
/* contents of the index (.shx) file. */
/************************************************************************/
static void writeHeader( SHPHandle psSHP )
{
uchar abyHeader[100];
int i;
ms_int32 i32;
double dValue;
ms_int32 *panSHX;
/* -------------------------------------------------------------------- */
/* Prepare header block for .shp file. */
/* -------------------------------------------------------------------- */
for( i = 0; i < 100; i++ )
abyHeader[i] = 0;
abyHeader[2] = 0x27; /* magic cookie */
abyHeader[3] = 0x0a;
i32 = psSHP->nFileSize/2; /* file size */
ByteCopy( &i32, abyHeader+24, 4 );
if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
i32 = 1000; /* version */
ByteCopy( &i32, abyHeader+28, 4 );
if( bBigEndian ) SwapWord( 4, abyHeader+28 );
i32 = psSHP->nShapeType; /* shape type */
ByteCopy( &i32, abyHeader+32, 4 );
if( bBigEndian ) SwapWord( 4, abyHeader+32 );
dValue = psSHP->adBoundsMin[0]; /* set bounds */
ByteCopy( &dValue, abyHeader+36, 8 );
if( bBigEndian ) SwapWord( 8, abyHeader+36 );
dValue = psSHP->adBoundsMin[1];
ByteCopy( &dValue, abyHeader+44, 8 );
if( bBigEndian ) SwapWord( 8, abyHeader+44 );
dValue = psSHP->adBoundsMax[0];
ByteCopy( &dValue, abyHeader+52, 8 );
if( bBigEndian ) SwapWord( 8, abyHeader+52 );
dValue = psSHP->adBoundsMax[1];
ByteCopy( &dValue, abyHeader+60, 8 );
if( bBigEndian ) SwapWord( 8, abyHeader+60 );
dValue = psSHP->adBoundsMin[2]; /* z */
ByteCopy( &dValue, abyHeader+68, 8 );
if( bBigEndian ) SwapWord( 8, abyHeader+68 );
dValue = psSHP->adBoundsMax[2];
ByteCopy( &dValue, abyHeader+76, 8 );
if( bBigEndian ) SwapWord( 8, abyHeader+76 );
dValue = psSHP->adBoundsMin[3]; /* m */
ByteCopy( &dValue, abyHeader+84, 8 );
if( bBigEndian ) SwapWord( 8, abyHeader+84 );
dValue = psSHP->adBoundsMax[3];
ByteCopy( &dValue, abyHeader+92, 8 );
if( bBigEndian ) SwapWord( 8, abyHeader+92 );
/* -------------------------------------------------------------------- */
/* Write .shp file header. */
/* -------------------------------------------------------------------- */
fseek( psSHP->fpSHP, 0, 0 );
fwrite( abyHeader, 100, 1, psSHP->fpSHP );
/* -------------------------------------------------------------------- */
/* Prepare, and write .shx file header. */
/* -------------------------------------------------------------------- */
i32 = (psSHP->nRecords * 2 * sizeof(ms_int32) + 100)/2; /* file size */
ByteCopy( &i32, abyHeader+24, 4 );
if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
fseek( psSHP->fpSHX, 0, 0 );
fwrite( abyHeader, 100, 1, psSHP->fpSHX );
/* -------------------------------------------------------------------- */
/* Write out the .shx contents. */
/* -------------------------------------------------------------------- */
panSHX = (ms_int32 *) malloc(sizeof(ms_int32) * 2 * psSHP->nRecords);
for( i = 0; i < psSHP->nRecords; i++ ) {
panSHX[i*2 ] = psSHP->panRecOffset[i]/2;
panSHX[i*2+1] = psSHP->panRecSize[i]/2;
if( !bBigEndian ) {
*(panSHX+i*2) = SWAP_FOUR_BYTES(*(panSHX+i*2));
*(panSHX+i*2+1) = SWAP_FOUR_BYTES(*(panSHX+i*2+1));
}
}
fwrite( panSHX, sizeof(ms_int32) * 2, psSHP->nRecords, psSHP->fpSHX );
free( panSHX );
}
/************************************************************************/
/* msSHPOpen() */
/* */
/* Open the .shp and .shx files based on the basename of the */
/* files or either file name. */
/************************************************************************/
SHPHandle msSHPOpen( const char * pszLayer, const char * pszAccess )
{
char *pszFullname, *pszBasename;
SHPHandle psSHP;
int i;
double adBounds[8];
int statret;
struct stat statBuf;
/* -------------------------------------------------------------------- */
/* Ensure the access string is one of the legal ones. We */
/* ensure the result string indicates binary to avoid common */
/* problems on Windows. */
/* -------------------------------------------------------------------- */
if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 || strcmp(pszAccess,"r+") == 0 )
pszAccess = "r+b";
else
pszAccess = "rb";
/* -------------------------------------------------------------------- */
/* Establish the byte order on this machine. */
/* -------------------------------------------------------------------- */
i = 1;
if( *((uchar *) &i) == 1 )
bBigEndian = MS_FALSE;
else
bBigEndian = MS_TRUE;
/* -------------------------------------------------------------------- */
/* Initialize the info structure. */
/* -------------------------------------------------------------------- */
psSHP = (SHPHandle) malloc(sizeof(SHPInfo));
psSHP->bUpdated = MS_FALSE;
psSHP->pabyRec = NULL;
psSHP->panParts = NULL;
psSHP->pabySHX = NULL;
psSHP->nBufSize = psSHP->nPartMax = 0;
/* -------------------------------------------------------------------- */
/* Compute the base (layer) name. If there is any extension */
/* on the passed in filename we will strip it off. */
/* -------------------------------------------------------------------- */
pszBasename = (char *) malloc(strlen(pszLayer)+5);
strcpy( pszBasename, pszLayer );
for( i = strlen(pszBasename)-1;
i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\';
i-- ) {}
if( pszBasename[i] == '.' )
pszBasename[i] = '\0';
/* -------------------------------------------------------------------- */
/* Open the .shp and .shx files. Note that files pulled from */
/* a PC to Unix with upper case filenames won't work! */
/* -------------------------------------------------------------------- */
pszFullname = (char *) malloc(strlen(pszBasename) + 5);
/*** Load SHP file ***/
sprintf( pszFullname, "%s.shp", pszBasename );
psSHP->fpSHP = fopen(pszFullname, pszAccess );
statret = stat(pszFullname, &statBuf);
if( psSHP->fpSHP == NULL || statret == -1) {
msFree(pszBasename);
msFree(pszFullname);
msFree(psSHP);
return( NULL );
}
psSHP->nSHPMapSize = statBuf.st_size - 1;
/*** Load SHX file ***/
sprintf( pszFullname, "%s.shx", pszBasename );
psSHP->fpSHX = fopen(pszFullname, pszAccess );
statret = stat(pszFullname, &statBuf);
if( psSHP->fpSHX == NULL || statret == -1) {
fclose( psSHP->fpSHP );
msFree(pszBasename);
msFree(pszFullname);
msFree(psSHP);
return( NULL );
}
psSHP->nSHXMapSize = statBuf.st_size - 1;
free( pszFullname );
free( pszBasename );
psSHP->pSHP = (uchar *)mmap(0, psSHP->nSHPMapSize, PROT_READ, MAP_SHARED, fileno(psSHP->fpSHP), 0);
if (psSHP->pSHP == (void *)-1) {
fclose( psSHP->fpSHP );
fclose( psSHP->fpSHX );
msFree(psSHP);
return(NULL);
}
psSHP->pSHX = (uchar *)mmap(0, psSHP->nSHXMapSize, PROT_READ, MAP_SHARED, fileno(psSHP->fpSHX), 0);
if (psSHP->pSHX == (void *)-1) {
munmap(psSHP->pSHP, psSHP->nSHPMapSize);
fclose( psSHP->fpSHP );
fclose( psSHP->fpSHX );
msFree(psSHP);
return(NULL);
}
madvise(psSHP->pSHP + 100, psSHP->nSHPMapSize - 100, MADV_SEQUENTIAL);
madvise(psSHP->pSHX, psSHP->nSHXMapSize, MADV_WILLNEED);
/* -------------------------------------------------------------------- */
/* Read the file size from the SHP file. */
/* -------------------------------------------------------------------- */
psSHP->nFileSize = BE_INT_AT(psSHP->pSHP, 24) * 2;
/* -------------------------------------------------------------------- */
/* Read SHX file Header info */
/* -------------------------------------------------------------------- */
if( psSHP->pSHX[0] != 0 || psSHP->pSHX[1] != 0 || psSHP->pSHX[2] != 0x27 || (psSHP->pSHX[3] != 0x0a && psSHP->pSHX[3] != 0x0d) ) {
munmap(psSHP->pSHP, psSHP->nSHPMapSize);
munmap(psSHP->pSHX, psSHP->nSHXMapSize);
free( psSHP );
return( NULL );
}
psSHP->nRecords = ((BE_INT_AT(psSHP->pSHX, 24) * 2) - 100) / 8;
if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 )
{
msSetError(MS_SHPERR, "Corrupted .shp file : nRecords = %d.", "msSHPOpen()",
psSHP->nRecords);
munmap(psSHP->pSHP, psSHP->nSHPMapSize);
munmap(psSHP->pSHX, psSHP->nSHXMapSize);
free( psSHP );
return( NULL );
}
psSHP->nShapeType = LE_INT_AT(psSHP->pSHP, 32);
memcpy(adBounds, psSHP->pSHP + 36, sizeof(double) * 8);
if ( IS_BIG_ENDIAN ) {
for (i = 0; i < 8; i++)
SwapWord(8, &adBounds[i]);
}
psSHP->adBoundsMin[0] = adBounds[0];
psSHP->adBoundsMin[1] = adBounds[1];
psSHP->adBoundsMax[0] = adBounds[2];
psSHP->adBoundsMax[1] = adBounds[3];
psSHP->adBoundsMin[2] = adBounds[4];
psSHP->adBoundsMin[2] = adBounds[5];
psSHP->adBoundsMax[3] = adBounds[6];
psSHP->adBoundsMax[3] = adBounds[7];
/* -------------------------------------------------------------------- */
/* Read the .shx file to get the offsets to each record in */
/* the .shp file. */
/* -------------------------------------------------------------------- */
psSHP->nMaxRecords = psSHP->nRecords;
return( psSHP );
}
/************************************************************************/
/* msSHPClose() */
/* */
/* Close the .shp and .shx files. */
/************************************************************************/
void msSHPClose(SHPHandle psSHP )
{
/* -------------------------------------------------------------------- */
/* Update the header if we have modified anything. */
/* -------------------------------------------------------------------- */
if( psSHP->bUpdated )
writeHeader( psSHP );
/* -------------------------------------------------------------------- */
/* Free all resources, and close files. */
/* -------------------------------------------------------------------- */
// free( psSHP->panRecOffset );
// free( psSHP->panRecSize );
// free( psSHP->panRecLoaded );
//
//
// if(psSHP->pabyRec) free(psSHP->pabyRec);
// if(psSHP->panParts) free(psSHP->panParts);
munmap(psSHP->pSHP, psSHP->nSHPMapSize);
munmap(psSHP->pSHX, psSHP->nSHXMapSize);
fclose( psSHP->fpSHX );
fclose( psSHP->fpSHP );
free( psSHP );
}
/************************************************************************/
/* msSHPGetInfo() */
/* */
/* Fetch general information about the shape file. */
/************************************************************************/
void msSHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType )
{
if( pnEntities )
*pnEntities = psSHP->nRecords;
if( pnShapeType )
*pnShapeType = psSHP->nShapeType;
}
/************************************************************************/
/* msSHPCreate() */
/* */
/* Create a new shape file and return a handle to the open */
/* shape file with read/write access. */
/************************************************************************/
SHPHandle msSHPCreate( const char * pszLayer, int nShapeType )
{
char *pszBasename, *pszFullname;
int i;
FILE *fpSHP, *fpSHX;
uchar *pSHP, *pSHX;
uchar abyHeader[100];
ms_int32 i32;
double dValue;
/* -------------------------------------------------------------------- */
/* Establish the byte order on this system. */
/* -------------------------------------------------------------------- */
i = 1;
if( *((uchar *) &i) == 1 )
bBigEndian = MS_FALSE;
else
bBigEndian = MS_TRUE;
/* -------------------------------------------------------------------- */
/* Compute the base (layer) name. If there is any extension */
/* on the passed in filename we will strip it off. */
/* -------------------------------------------------------------------- */
pszBasename = (char *) malloc(strlen(pszLayer)+5);
strcpy( pszBasename, pszLayer );
for( i = strlen(pszBasename)-1;
i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\';
i-- ) {}
if( pszBasename[i] == '.' )
pszBasename[i] = '\0';
/* -------------------------------------------------------------------- */
/* Open the two files so we can write their headers. */
/* -------------------------------------------------------------------- */
pszFullname = (char *) malloc(strlen(pszBasename) + 5);
sprintf( pszFullname, "%s.shp", pszBasename );
fpSHP = fopen(pszFullname, "wb" );
if( fpSHP == NULL )
return( NULL );
sprintf( pszFullname, "%s.shx", pszBasename );
fpSHX = fopen(pszFullname, "wb" );
if( fpSHX == NULL )
return( NULL );
free( pszFullname );
pSHP = (uchar *)mmap(0, 100, PROT_READ | PROT_WRITE, NULL, fileno(fpSHP), 0);
if (pSHP == (void *)-1)
return(NULL);
pSHX = (uchar *)mmap(0, 100, PROT_READ | PROT_WRITE, NULL, fileno(fpSHX), 0);
if (pSHX == (void *)-1) {
munmap(pSHP, 100);
return(NULL);
}
/* -------------------------------------------------------------------- */
/* Prepare header block for .shp file. */
/* -------------------------------------------------------------------- */
for( i = 0; i < 100; i++ )
abyHeader[i] = 0;
abyHeader[2] = 0x27; /* magic cookie */
abyHeader[3] = 0x0a;
i32 = 50; /* file size */
ByteCopy( &i32, abyHeader+24, 4 );
if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
i32 = 1000; /* version */
ByteCopy( &i32, abyHeader+28, 4 );
if( bBigEndian ) SwapWord( 4, abyHeader+28 );
i32 = nShapeType; /* shape type */
ByteCopy( &i32, abyHeader+32, 4 );
if( bBigEndian ) SwapWord( 4, abyHeader+32 );
dValue = 0.0; /* set bounds */
ByteCopy( &dValue, abyHeader+36, 8 );
ByteCopy( &dValue, abyHeader+44, 8 );
ByteCopy( &dValue, abyHeader+52, 8 );
ByteCopy( &dValue, abyHeader+60, 8 );
/* -------------------------------------------------------------------- */
/* Write .shp file header. */
/* -------------------------------------------------------------------- */
memcpy(pSHP, abyHeader, 100);
/* -------------------------------------------------------------------- */
/* Prepare, and write .shx file header. */
/* -------------------------------------------------------------------- */
i32 = 50; /* file size */
ByteCopy( &i32, abyHeader+24, 4 );
if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
memcpy(pSHX, abyHeader, 100);
/* -------------------------------------------------------------------- */
/* Close the files, and then open them as regular existing files. */
/* -------------------------------------------------------------------- */
munmap(pSHP, 100);
munmap(pSHX, 100);
fclose( fpSHP );
fclose( fpSHX );
return( msSHPOpen( pszLayer, "rb+" ) );
}
/************************************************************************/
/* writeBounds() */
/* */
/* Compute a bounds rectangle for a shape, and set it into the */
/* indicated location in the record. */
/************************************************************************/
static void writeBounds( uchar * pabyRec, shapeObj *shape, int nVCount )
{
double dXMin, dXMax, dYMin, dYMax;
int i, j;
if( nVCount == 0 ) {
dXMin = dYMin = dXMax = dYMax = 0.0;
} else {
dXMin = dXMax = shape->line[0].point[0].x;
dYMin = dYMax = shape->line[0].point[0].y;
for( i=0; i<shape->numlines; i++ ) {
for( j=0; j<shape->line[i].numpoints; j++ ) {
dXMin = MS_MIN(dXMin, shape->line[i].point[j].x);
dXMax = MS_MAX(dXMax, shape->line[i].point[j].x);
dYMin = MS_MIN(dYMin, shape->line[i].point[j].y);
dYMax = MS_MAX(dYMax, shape->line[i].point[j].y);
}
}
}
if( bBigEndian ) {
SwapWord( 8, &dXMin );
SwapWord( 8, &dYMin );
SwapWord( 8, &dXMax );
SwapWord( 8, &dYMax );
}
ByteCopy( &dXMin, pabyRec + 0, 8 );
ByteCopy( &dYMin, pabyRec + 8, 8 );
ByteCopy( &dXMax, pabyRec + 16, 8 );
ByteCopy( &dYMax, pabyRec + 24, 8 );
}
int msSHPWritePoint(SHPHandle psSHP, pointObj *point )
{
int nRecordOffset, nRecordSize=0;
uchar *pabyRec;
ms_int32 i32, nPoints, nParts;
if( psSHP->nShapeType != SHP_POINT) return(-1);
psSHP->bUpdated = MS_TRUE;
/* -------------------------------------------------------------------- */
/* Add the new entity to the in memory index. */
/* -------------------------------------------------------------------- */
psSHP->nRecords++;
if( psSHP->nRecords > psSHP->nMaxRecords ) {
psSHP->nMaxRecords = (int) (psSHP->nMaxRecords * 1.3 + 100);
psSHP->panRecOffset = (int *) SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords );
psSHP->panRecSize = (int *) SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords );
}
/* -------------------------------------------------------------------- */
/* Compute a few things. */
/* -------------------------------------------------------------------- */
nPoints = 1;
nParts = 1;
/* -------------------------------------------------------------------- */
/* Initialize record. */
/* -------------------------------------------------------------------- */
psSHP->panRecOffset[psSHP->nRecords-1] = nRecordOffset = psSHP->nFileSize;
pabyRec = (uchar *) malloc(nPoints * 2 * sizeof(double) + nParts * 4 + 128);
/* -------------------------------------------------------------------- */
/* Write vertices for a point. */
/* -------------------------------------------------------------------- */
ByteCopy( &(point->x), pabyRec + 12, 8 );
ByteCopy( &(point->y), pabyRec + 20, 8 );
if( bBigEndian ) {
SwapWord( 8, pabyRec + 12 );
SwapWord( 8, pabyRec + 20 );
}
nRecordSize = 20;
/* -------------------------------------------------------------------- */
/* Set the shape type, record number, and record size. */
/* -------------------------------------------------------------------- */
i32 = psSHP->nRecords-1+1; /* record # */
if( !bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
ByteCopy( &i32, pabyRec, 4 );
i32 = nRecordSize/2; /* record size */
if( !bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
ByteCopy( &i32, pabyRec + 4, 4 );
i32 = psSHP->nShapeType; /* shape type */
if( bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
ByteCopy( &i32, pabyRec + 8, 4 );
/* -------------------------------------------------------------------- */
/* Write out record. */
/* -------------------------------------------------------------------- */
fseek( psSHP->fpSHP, nRecordOffset, 0 );
fwrite( pabyRec, nRecordSize+8, 1, psSHP->fpSHP );
free( pabyRec );
psSHP->panRecSize[psSHP->nRecords-1] = nRecordSize;
psSHP->nFileSize += nRecordSize + 8;
/* -------------------------------------------------------------------- */
/* Expand file wide bounds based on this shape. */
/* -------------------------------------------------------------------- */
if( psSHP->nRecords == 1 ) {
psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = point->x;
psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = point->y;
} else {
psSHP->adBoundsMin[0] = MS_MIN(psSHP->adBoundsMin[0], point->x);
psSHP->adBoundsMin[1] = MS_MIN(psSHP->adBoundsMin[1], point->y);
psSHP->adBoundsMax[0] = MS_MAX(psSHP->adBoundsMax[0], point->x);
psSHP->adBoundsMax[1] = MS_MAX(psSHP->adBoundsMax[1], point->y);
}
return( psSHP->nRecords - 1 );
}
int msSHPWriteShape(SHPHandle psSHP, shapeObj *shape )
{
int nRecordOffset, i, j, k, nRecordSize=0;
uchar *pabyRec;
ms_int32 i32, nPoints, nParts;
#ifdef USE_POINT_Z_M
double dfMMin, dfMMax = 0;
#endif
psSHP->bUpdated = MS_TRUE;
/* -------------------------------------------------------------------- */
/* Add the new entity to the in memory index. */
/* -------------------------------------------------------------------- */
psSHP->nRecords++;
if( psSHP->nRecords > psSHP->nMaxRecords ) {
psSHP->nMaxRecords = (int) (psSHP->nMaxRecords * 1.3 + 100);
psSHP->panRecOffset = (int *) SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords );
psSHP->panRecSize = (int *) SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords );
}
/* -------------------------------------------------------------------- */
/* Compute a few things. */
/* -------------------------------------------------------------------- */
nPoints = 0;
for(i=0; i<shape->numlines; i++)
nPoints += shape->line[i].numpoints;
nParts = shape->numlines;
/* -------------------------------------------------------------------- */
/* Initialize record. */
/* -------------------------------------------------------------------- */
psSHP->panRecOffset[psSHP->nRecords-1] = nRecordOffset = psSHP->nFileSize;
pabyRec = (uchar *) malloc(nPoints * 4 * sizeof(double) + nParts * 8 + 128);
/* -------------------------------------------------------------------- */
/* Write vertices for a Polygon or Arc. */
/* -------------------------------------------------------------------- */
if(psSHP->nShapeType == SHP_POLYGON || psSHP->nShapeType == SHP_ARC ||
psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM ||
psSHP->nShapeType == SHP_ARCZ || psSHP->nShapeType == SHP_POLYGONZ) {
ms_int32 t_nParts, t_nPoints, partSize;
t_nParts = nParts;
t_nPoints = nPoints;
writeBounds( pabyRec + 12, shape, t_nPoints );
if( bBigEndian ) {
nPoints = SWAP_FOUR_BYTES(nPoints);
nParts = SWAP_FOUR_BYTES(nParts);
}
ByteCopy( &nPoints, pabyRec + 40 + 8, 4 );
ByteCopy( &nParts, pabyRec + 36 + 8, 4 );
partSize = 0; /* first part always starts at 0 */
ByteCopy( &partSize, pabyRec + 44 + 8 + 4*0, 4 );
if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*0);
for( i = 1; i < t_nParts; i++ ) {
partSize += shape->line[i-1].numpoints;
ByteCopy( &partSize, pabyRec + 44 + 8 + 4*i, 4 );
if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i);
}
k = 0; /* overall point counter */
for( i = 0; i < shape->numlines; i++ ) {
for( j = 0; j < shape->line[i].numpoints; j++ ) {
ByteCopy( &(shape->line[i].point[j].x), pabyRec + 44 + 4*t_nParts + 8 + k * 16, 8 );
ByteCopy( &(shape->line[i].point[j].y), pabyRec + 44 + 4*t_nParts + 8 + k * 16 + 8, 8 );
if( bBigEndian ) {
SwapWord( 8, pabyRec + 44+4*t_nParts+8+k*16 );
SwapWord( 8, pabyRec + 44+4*t_nParts+8+k*16+8 );
}
k++;
}
}
#ifdef USE_POINT_Z_M
/* -------------------------------------------------------------------- */
/* Polygon. Arc with Z */
/* -------------------------------------------------------------------- */
if (psSHP->nShapeType == SHP_POLYGONZ || psSHP->nShapeType == SHP_ARCZ) {
dfMMin = shape->line[0].point[0].z;
dfMMax = shape->line[shape->numlines-1].point[shape->line[shape->numlines-1].numpoints-1].z;
nRecordSize = 44 + 4*t_nParts + 8 + (t_nPoints* 16);
ByteCopy( &(dfMMin), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
ByteCopy( &(dfMMax), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
for( i = 0; i < shape->numlines; i++ ) {
for( j = 0; j < shape->line[i].numpoints; j++ ) {
ByteCopy( &(shape->line[i].point[j].z), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
}
}
} else
nRecordSize = 44 + 4*t_nParts + 16 * t_nPoints;
/* -------------------------------------------------------------------- */
/* measured shape : polygon and arc. */
/* -------------------------------------------------------------------- */
if(psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM) {
dfMMin = shape->line[0].point[0].m;
dfMMax = shape->line[shape->numlines-1].point[shape->line[shape->numlines-1].numpoints-1].m;
nRecordSize = 44 + 4*t_nParts + 8 + (t_nPoints* 16);
ByteCopy( &(dfMMin), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
ByteCopy( &(dfMMax), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
for( i = 0; i < shape->numlines; i++ ) {
for( j = 0; j < shape->line[i].numpoints; j++ ) {
ByteCopy( &(shape->line[i].point[j].m), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
}
}
}
else
#endif /* USE_POINT_Z_M */
nRecordSize = 44 + 4*t_nParts + 16 * t_nPoints;
}
/* -------------------------------------------------------------------- */
/* Write vertices for a MultiPoint. */
/* -------------------------------------------------------------------- */
else if( psSHP->nShapeType == SHP_MULTIPOINT ||
psSHP->nShapeType == SHP_MULTIPOINTM ||
psSHP->nShapeType == SHP_MULTIPOINTZ) {
ms_int32 t_nPoints;
t_nPoints = nPoints;
writeBounds( pabyRec + 12, shape, nPoints );
if( bBigEndian ) nPoints = SWAP_FOUR_BYTES(nPoints);
ByteCopy( &nPoints, pabyRec + 44, 4 );
for( i = 0; i < shape->line[0].numpoints; i++ ) {
ByteCopy( &(shape->line[0].point[i].x), pabyRec + 48 + i*16, 8 );
ByteCopy( &(shape->line[0].point[i].y), pabyRec + 48 + i*16 + 8, 8 );
if( bBigEndian ) {
SwapWord( 8, pabyRec + 48 + i*16 );
SwapWord( 8, pabyRec + 48 + i*16 + 8 );
}
}
#ifdef USE_POINT_Z_M
if (psSHP->nShapeType == SHP_MULTIPOINTZ) {
nRecordSize = 48 + 16 * t_nPoints;
dfMMin = shape->line[0].point[0].z;
dfMMax = shape->line[0].point[shape->line[0].numpoints-1].z;
ByteCopy( &(dfMMin), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
ByteCopy( &(dfMMax), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
for( i = 0; i < shape->line[0].numpoints; i++ ) {
ByteCopy( &(shape->line[0].point[i].z), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
}
} else
nRecordSize = 40 + 16 * t_nPoints;
if (psSHP->nShapeType == SHP_MULTIPOINTM) {
nRecordSize = 48 + 16 * t_nPoints;
dfMMin = shape->line[0].point[0].m;
dfMMax = shape->line[0].point[shape->line[0].numpoints-1].m;
ByteCopy( &(dfMMin), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
ByteCopy( &(dfMMax), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
for( i = 0; i < shape->line[0].numpoints; i++ ) {
ByteCopy( &(shape->line[0].point[i].m), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
}
} else
#endif /* USE_POINT_Z_M */
nRecordSize = 40 + 16 * t_nPoints;
}
/* -------------------------------------------------------------------- */
/* Write vertices for a point. */
/* -------------------------------------------------------------------- */
else if( psSHP->nShapeType == SHP_POINT || psSHP->nShapeType == SHP_POINTM ||
psSHP->nShapeType == SHP_POINTZ) {
ByteCopy( &(shape->line[0].point[0].x), pabyRec + 12, 8 );
ByteCopy( &(shape->line[0].point[0].y), pabyRec + 20, 8 );
if( bBigEndian ) {
SwapWord( 8, pabyRec + 12 );
SwapWord( 8, pabyRec + 20 );
}
#ifdef USE_POINT_Z_M
if (psSHP->nShapeType == SHP_POINTZ) {
nRecordSize = 28;
ByteCopy( &(shape->line[0].point[0].z), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
} else
nRecordSize = 20;
if (psSHP->nShapeType == SHP_POINTM) {
nRecordSize = 28;
ByteCopy( &(shape->line[0].point[0].m), pabyRec + nRecordSize, 8 );
if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
nRecordSize += 8;
} else
nRecordSize = 20;
#else
nRecordSize = 20;
#endif /* USE_POINT_Z_M */
}
/* -------------------------------------------------------------------- */
/* Set the shape type, record number, and record size. */
/* -------------------------------------------------------------------- */
i32 = psSHP->nRecords-1+1; /* record # */
if( !bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
ByteCopy( &i32, pabyRec, 4 );
i32 = nRecordSize/2; /* record size */
if( !bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
ByteCopy( &i32, pabyRec + 4, 4 );
i32 = psSHP->nShapeType; /* shape type */
if( bBigEndian ) i32 = SWAP_FOUR_BYTES(i32);
ByteCopy( &i32, pabyRec + 8, 4 );
/* -------------------------------------------------------------------- */
/* Write out record. */
/* -------------------------------------------------------------------- */
fseek( psSHP->fpSHP, nRecordOffset, 0 );
fwrite( pabyRec, nRecordSize+8, 1, psSHP->fpSHP );
free( pabyRec );
psSHP->panRecSize[psSHP->nRecords-1] = nRecordSize;
psSHP->nFileSize += nRecordSize + 8;
/* -------------------------------------------------------------------- */
/* Expand file wide bounds based on this shape. */
/* -------------------------------------------------------------------- */
if( psSHP->nRecords == 1 ) {
psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = shape->line[0].point[0].x;
psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = shape->line[0].point[0].y;
#ifdef USE_POINT_Z_M
psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = shape->line[0].point[0].z;
psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = shape->line[0].point[0].m;
#endif
}
for( i=0; i<shape->numlines; i++ ) {
for( j=0; j<shape->line[i].numpoints; j++ ) {
psSHP->adBoundsMin[0] = MS_MIN(psSHP->adBoundsMin[0], shape->line[i].point[j].x);
psSHP->adBoundsMin[1] = MS_MIN(psSHP->adBoundsMin[1], shape->line[i].point[j].y);
#ifdef USE_POINT_Z_M
psSHP->adBoundsMin[2] = MS_MIN(psSHP->adBoundsMin[2], shape->line[i].point[j].z);
psSHP->adBoundsMin[3] = MS_MIN(psSHP->adBoundsMin[3], shape->line[i].point[j].m);
#endif
psSHP->adBoundsMax[0] = MS_MAX(psSHP->adBoundsMax[0], shape->line[i].point[j].x);
psSHP->adBoundsMax[1] = MS_MAX(psSHP->adBoundsMax[1], shape->line[i].point[j].y);
#ifdef USE_POINT_Z_M
psSHP->adBoundsMax[2] = MS_MAX(psSHP->adBoundsMax[2], shape->line[i].point[j].z);
psSHP->adBoundsMax[3] = MS_MAX(psSHP->adBoundsMax[3], shape->line[i].point[j].m);
#endif
}
}
return( psSHP->nRecords - 1 );
}
/*
** msSHPReadPoint() - Reads a single point from a POINT shape file.
*/
int msSHPReadPoint( SHPHandle psSHP, int hEntity, pointObj *point )
{
int nEntitySize;
uchar *pRec;
/* -------------------------------------------------------------------- */
/* Only valid for point shapefiles */
/* -------------------------------------------------------------------- */
if( psSHP->nShapeType != SHP_POINT ) {
msSetError(MS_SHPERR, "msSHPReadPoint only operates on point shapefiles.", "msSHPReadPoint()");
return(MS_FAILURE);
}
/* -------------------------------------------------------------------- */
/* Validate the record/entity number. */
/* -------------------------------------------------------------------- */
if( hEntity < 0 || hEntity >= psSHP->nRecords ) {
msSetError(MS_SHPERR, "Record index out of bounds.", "msSHPReadPoint()");
return(MS_FAILURE);
}
nEntitySize = msSHXReadSize(psSHP, hEntity) + 8;
if( msSHXReadSize( psSHP, hEntity) == 4 ) {
msSetError(MS_SHPERR, "NULL feature encountered.", "msSHPReadPoint()");
return(MS_FAILURE);
}
else if ( nEntitySize < 28 ) {
msSetError(MS_SHPERR, "Corrupted feature encountered. hEntity=%d, nEntitySize=%d", "msSHPReadPoint()",
hEntity, nEntitySize);
return(MS_FAILURE);
}
/* -------------------------------------------------------------------- */
/* Read the record. */
/* -------------------------------------------------------------------- */
pRec = psSHP->pSHP + msSHXReadOffset(psSHP, hEntity);
point->x = LE_DBL_AT(pRec, 12);
point->y = LE_DBL_AT(pRec, 20);
return(MS_SUCCESS);
}
int msSHXReadRecord( SHPHandle psSHP, int hEntity, int offset) {
/* Validate the record/entity number. */
if( hEntity < 0 || hEntity >= psSHP->nRecords )
return(MS_FAILURE);
return BE_INT_AT(psSHP->pSHX, 100 + (hEntity * 8) + (offset * sizeof(int))) * 2;
}
int msSHXReadOffset( SHPHandle psSHP, int hEntity ) {
return msSHXReadRecord(psSHP, hEntity, 0);
}
int msSHXReadSize( SHPHandle psSHP, int hEntity ) {
return msSHXReadRecord(psSHP, hEntity, 1);
}
/*
** msSHPReadShape() - Reads the vertices for one shape from a shape file.
*/
void msSHPReadShape( SHPHandle psSHP, int hEntity, shapeObj *shape )
{
int i, j, k;
int nOffset = 0;
// #ifdef USE_POINT_Z_M
// int nOffset = 0;
// #endif
int nEntitySize, nRequiredSize;
pointObj *paryAlloc;
uchar pBuf[1024];
uchar *pRec;
msInitShape(shape); /* initialize the shape */
/* -------------------------------------------------------------------- */
/* Validate the record/entity number. */
/* -------------------------------------------------------------------- */
if( hEntity < 0 || hEntity >= psSHP->nRecords )
return;
if( msSHXReadSize(psSHP, hEntity) == 4 ) {
shape->type = MS_SHAPE_NULL;
return;
}
nEntitySize = msSHXReadSize(psSHP, hEntity) + 8;
/* -------------------------------------------------------------------- */
/* Read the record. */
/* -------------------------------------------------------------------- */
pRec = psSHP->pSHP + msSHXReadOffset(psSHP, hEntity);
/* -------------------------------------------------------------------- */
/* Extract vertices for a Polygon or Arc. */
/* -------------------------------------------------------------------- */
if( psSHP->nShapeType == SHP_POLYGON || psSHP->nShapeType == SHP_ARC ||
psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM ||
psSHP->nShapeType == SHP_POLYGONZ || psSHP->nShapeType == SHP_ARCZ)
{
ms_int32 nPoints, nParts;
if (nEntitySize < 40 + 8 + 4)
{
shape->type = MS_SHAPE_NULL;
msSetError(MS_SHPERR, "Corrupted feature encountered. hEntity = %d, nEntitySize=%d", "msSHPReadShape()",
hEntity, nEntitySize);
return;
}
memcpy(&pBuf, pRec + 8, 44);
/* copy the bounding box */
shape->bounds.minx = LE_DBL_AT(pBuf, 4);
shape->bounds.miny = LE_DBL_AT(pBuf, 12);
shape->bounds.maxx = LE_DBL_AT(pBuf, 20);
shape->bounds.maxy = LE_DBL_AT(pBuf, 28);
nParts = LE_INT_AT(pBuf, 36);
nPoints = LE_INT_AT(pBuf, 40);
if (nPoints < 0 || nParts < 0 ||
nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000)
{
shape->type = MS_SHAPE_NULL;
msSetError(MS_SHPERR, "Corrupted feature encountered. hEntity = %d, nPoints =%d, nParts = %d", "msSHPReadShape()",
hEntity, nPoints, nParts);
return;
}
/* With the previous checks on nPoints and nParts, */
/* we should not overflow here and after */
/* since 50 M * (16 + 8 + 8) = 1 600 MB */
if (44 + 8 + 4 * nParts + 16 * nPoints > nEntitySize)
{
shape->type = MS_SHAPE_NULL;
msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.",
"msSHPReadShape()", hEntity, nPoints, nParts);
return;
}
psSHP->nPartMax = nParts;
psSHP->panParts = (int *)(pRec + 44 + 8);
/* -------------------------------------------------------------------- */
/* Fill the shape structure. */
/* -------------------------------------------------------------------- */
if( (shape->line = (lineObj *)malloc(sizeof(lineObj)*nParts)) == NULL ) {
shape->type = MS_SHAPE_NULL;
msSetError(MS_MEMERR, NULL, "msSHPReadShape()");
return;
}
shape->numlines = nParts;
k = 0; /* overall point counter */
for( i = 0; i < nParts; i++) {
if( i == nParts-1)
shape->line[i].numpoints = nPoints - LE_INT(psSHP->panParts[i]);
else
shape->line[i].numpoints = LE_INT(psSHP->panParts[i+1]) - LE_INT(psSHP->panParts[i]);
if (shape->line[i].numpoints <= 0)
{
msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, shape->line[%d].numpoints=%d", "msSHPReadShape()",
hEntity, i, shape->line[i].numpoints);
while(--i >= 0)
free(shape->line[i].point);
free(shape->line);
shape->numlines = 0;
shape->type = MS_SHAPE_NULL;
return;
}
if( (shape->line[i].point = (pointObj *)malloc(sizeof(pointObj)*shape->line[i].numpoints)) == NULL ) {
while(--i >= 0)
free(shape->line[i].point);
free(shape->line);
shape->numlines = 0;
shape->type = MS_SHAPE_NULL;
msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()");
return;
}
memcpy(shape->line[i].point,
pRec + (44 + 8 + (4 * nParts)) + (k * 16),
shape->line[i].numpoints * 16
);
k += shape->line[i].numpoints;
}
if(psSHP->nShapeType == SHP_POLYGON
|| psSHP->nShapeType == SHP_POLYGONZ
|| psSHP->nShapeType == SHP_POLYGONM)
shape->type = MS_SHAPE_POLYGON;
else
shape->type = MS_SHAPE_LINE;
}
/* -------------------------------------------------------------------- */
/* Extract a MultiPoint. */
/* -------------------------------------------------------------------- */
else if( psSHP->nShapeType == SHP_MULTIPOINT || psSHP->nShapeType == SHP_MULTIPOINTM ||
psSHP->nShapeType == SHP_MULTIPOINTZ) {
ms_int32 nPoints;
if (nEntitySize < 44 + 4)
{
shape->type = MS_SHAPE_NULL;
msSetError(MS_SHPERR, "Corrupted feature encountered. recSize of feature %d=%d", "msSHPReadShape()",
hEntity, msSHXReadSize(psSHP, hEntity));
return;
}
shape->bounds.minx = LE_DBL_AT(pRec, 8 + 4);
shape->bounds.miny = LE_DBL_AT(pRec, 8 + 12);
shape->bounds.maxx = LE_DBL_AT(pRec, 8 + 20);
shape->bounds.maxy = LE_DBL_AT(pRec, 8 + 28);
nPoints = LE_INT_AT(pRec, 8 + 36);
/* -------------------------------------------------------------------- */
/* Fill the shape structure. */
/* -------------------------------------------------------------------- */
if( (shape->line = (lineObj *)malloc(sizeof(lineObj))) == NULL ) {
shape->type = MS_SHAPE_NULL;
msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()");
return;
}
if (nPoints < 0 || nPoints > 50 * 1000 * 1000)
{
free(shape->line);
shape->type = MS_SHAPE_NULL;
msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, nPoints=%d.",
"msSHPReadShape()", hEntity, nPoints);
return;
}
nRequiredSize = 48 + nPoints * 16;
if (psSHP->nShapeType == SHP_MULTIPOINTZ || psSHP->nShapeType == SHP_MULTIPOINTM)
nRequiredSize += 16 + nPoints * 8;
if (nRequiredSize > nEntitySize)
{
free(shape->line);
shape->type = MS_SHAPE_NULL;
msSetError(MS_SHPERR, "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d",
"msSHPReadShape()", hEntity, nPoints, nEntitySize);
return;
}
shape->numlines = 1;
shape->line[0].numpoints = nPoints;
shape->line[0].point = (pointObj *) malloc( nPoints * sizeof(pointObj) );
if (shape->line[0].point == NULL)
{
free(shape->line);
shape->numlines = 0;
shape->type = MS_SHAPE_NULL;
msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()");
return;
}
for( i = 0; i < nPoints; i++ ) {
shape->line[0].point[i].x = LE_DBL_AT(pRec, 48 + (16 * i));
shape->line[0].point[i].y = LE_DBL_AT(pRec, 48 + (16 * i) + 8);
#ifdef USE_POINT_Z_M
/* -------------------------------------------------------------------- */
/* MulipointZ */
/* -------------------------------------------------------------------- */
shape->line[0].point[i].z = 0; /* initialize */
if (psSHP->nShapeType == SHP_MULTIPOINTZ)
shape->line[0].point[i].z = LE_DBL_AT(pRec + 48 + (16 * nPoints) + 16 + (i * 8));
/* -------------------------------------------------------------------- */
/* Measured shape : multipont. */
/* -------------------------------------------------------------------- */
shape->line[0].point[i].m = 0; /* initialize */
if (psSHP->nShapeType == SHP_MULTIPOINTM) {
shape->line[0].point[i].m = LE_DBL_AT(pRec + 48 + (16 * nPoints) + 16 + (i * 8));
}
#endif /* USE_POINT_Z_M */
}
shape->type = MS_SHAPE_POINT;
}
/* -------------------------------------------------------------------- */
/* Extract a Point. */
/* -------------------------------------------------------------------- */
else if(psSHP->nShapeType == SHP_POINT || psSHP->nShapeType == SHP_POINTM ||
psSHP->nShapeType == SHP_POINTZ) {
if (nEntitySize < 20 + 8)
{
shape->type = MS_SHAPE_NULL;
msSetError(MS_SHPERR, "Corrupted feature encountered. recSize of feature %d=%d", "msSHPReadShape()",
hEntity, msSHXReadSize(psSHP, hEntity));
return;
}
/* -------------------------------------------------------------------- */
/* Fill the shape structure. */
/* -------------------------------------------------------------------- */
if( (shape->line = (lineObj *)malloc(sizeof(lineObj))) == NULL ) {
msSetError(MS_MEMERR, NULL, "msSHPReadShape()");
return;
}
shape->numlines = 1;
shape->line[0].numpoints = 1;
shape->line[0].point = (pointObj *) malloc(sizeof(pointObj));
shape->line[0].point[0].x = LE_DBL_AT(pRec, 12);
shape->line[0].point[0].y = LE_DBL_AT(pRec, 20);
#ifdef USE_POINT_Z_M
/* -------------------------------------------------------------------- */
/* PointZ */
/* -------------------------------------------------------------------- */
shape->line[0].point[0].z = 0; /* initialize */
if (psSHP->nShapeType == SHP_POINTZ) {
nOffset = 20 + 8;
if( nEntitySize >= nOffset + 8 ) {
shape->line[0].point[0].z = LE_DBL_AT(pRec, nOffset);
}
}
/* -------------------------------------------------------------------- */
/* Measured support : point. */
/* -------------------------------------------------------------------- */
shape->line[0].point[0].m = 0; /* initialize */
if (psSHP->nShapeType == SHP_POINTM) {
nOffset = 20 + 8;
if( nEntitySize >= nOffset + 8 ) {
shape->line[0].point[0].m = LE_DBL_AT(pRec, nOffset);
}
}
#endif /* USE_POINT_Z_M */
/* set the bounding box to the point */
shape->bounds.minx = shape->bounds.maxx = shape->line[0].point[0].x;
shape->bounds.miny = shape->bounds.maxy = shape->line[0].point[0].y;
shape->type = MS_SHAPE_POINT;
}
shape->index = hEntity;
return;
}
int msSHPReadBounds( SHPHandle psSHP, int hEntity, rectObj *padBounds)
{
double *pRec;
int nSize;
char tmp;
int i;
/* -------------------------------------------------------------------- */
/* Validate the record/entity number. */
/* -------------------------------------------------------------------- */
if( psSHP->nRecords <= 0 || hEntity < -1 || hEntity >= psSHP->nRecords ) {
padBounds->minx = padBounds->miny = padBounds->maxx = padBounds->maxy = 0.0;
return MS_FAILURE;
}
/* -------------------------------------------------------------------- */
/* If the entity is -1 we fetch the bounds for the whole file. */
/* -------------------------------------------------------------------- */
if( hEntity == -1 ) {
padBounds->minx = psSHP->adBoundsMin[0];
padBounds->miny = psSHP->adBoundsMin[1];
padBounds->maxx = psSHP->adBoundsMax[0];
padBounds->maxy = psSHP->adBoundsMax[1];
} else {
nSize = msSHXReadSize(psSHP, hEntity);
if( nSize == 4 ) { /* NULL shape */
padBounds->minx = padBounds->miny = padBounds->maxx = padBounds->maxy = 0.0;
return MS_FAILURE;
}
pRec = (double *)(psSHP->pSHP + msSHXReadOffset(psSHP, hEntity) + 12);
if( psSHP->nShapeType != SHP_POINT && psSHP->nShapeType != SHP_POINTZ && psSHP->nShapeType != SHP_POINTM) {
padBounds->minx = LE_DBL(pRec[0]);
padBounds->miny = LE_DBL(pRec[1]);
padBounds->maxx = LE_DBL(pRec[2]);
padBounds->maxy = LE_DBL(pRec[3]);
if(msIsNan(padBounds->minx)) { /* empty shape */
padBounds->minx = padBounds->miny = padBounds->maxx = padBounds->maxy = 0.0;
return MS_FAILURE;
}
} else {
/* -------------------------------------------------------------------- */
/* For points we fetch the point, and duplicate it as the */
/* minimum and maximum bound. */
/* -------------------------------------------------------------------- */
padBounds->minx = LE_DBL(pRec[0]);
padBounds->miny = LE_DBL(pRec[1]);
padBounds->maxx = padBounds->minx;
padBounds->maxy = padBounds->miny;
}
}
return MS_SUCCESS;
}
int msShapefileOpen(shapefileObj *shpfile, char *mode, char *filename, int log_failures)
{
int i;
char *dbfFilename;
if(!filename) {
if( log_failures )
msSetError(MS_IOERR, "No (NULL) filename provided.", "msShapefileOpen()");
return(-1);
}
/* initialize a few things */
shpfile->status = NULL;
shpfile->lastshape = -1;
shpfile->isopen = MS_FALSE;
/* open the shapefile file (appending ok) and get basic info */
if(!mode)
shpfile->hSHP = msSHPOpen( filename, "rb");
else
shpfile->hSHP = msSHPOpen( filename, mode);
if(!shpfile->hSHP) {
if( log_failures )
msSetError(MS_IOERR, "(%s)", "msShapefileOpen()", filename);
return(-1);
}
strcpy(shpfile->source, filename);
/* load some information about this shapefile */
msSHPGetInfo( shpfile->hSHP, &shpfile->numshapes, &shpfile->type);
msSHPReadBounds( shpfile->hSHP, -1, &(shpfile->bounds));
dbfFilename = (char *)malloc(strlen(filename)+5);
strcpy(dbfFilename, filename);
/* clean off any extention the filename might have */
for (i = strlen(dbfFilename) - 1;
i > 0 && dbfFilename[i] != '.' && dbfFilename[i] != '/' && dbfFilename[i] != '\\';
i-- ) {}
if( dbfFilename[i] == '.' )
dbfFilename[i] = '\0';
strcat(dbfFilename, ".dbf");
shpfile->hDBF = msDBFOpen(dbfFilename, "rb");
if(!shpfile->hDBF) {
if( log_failures )
msSetError(MS_IOERR, "(%s)", "msShapefileOpen()", dbfFilename);
free(dbfFilename);
return(-1);
}
free(dbfFilename);
shpfile->isopen = MS_TRUE;
return(0); /* all o.k. */
}
/* Creates a new shapefile */
int msShapefileCreate(shapefileObj *shpfile, char *filename, int type)
{
if(type != SHP_POINT && type != SHP_MULTIPOINT && type != SHP_ARC &&
type != SHP_POLYGON &&
type != SHP_POINTM && type != SHP_MULTIPOINTM &&
type != SHP_ARCM && type != SHP_POLYGONM &&
type != SHP_POINTZ && type != SHP_MULTIPOINTZ &&
type != SHP_ARCZ && type != SHP_POLYGONZ) {
msSetError(MS_SHPERR, "Invalid shape type.", "msNewSHPFile()");
return(-1);
}
/* create the spatial portion */
shpfile->hSHP = msSHPCreate(filename, type);
if(!shpfile->hSHP) {
msSetError(MS_IOERR, "(%s)", "msNewSHPFile()",filename);
return(-1);
}
/* retrieve a few things about this shapefile */
msSHPGetInfo( shpfile->hSHP, &shpfile->numshapes, &shpfile->type);
msSHPReadBounds( shpfile->hSHP, -1, &(shpfile->bounds));
/* initialize a few other things */
shpfile->status = NULL;
shpfile->lastshape = -1;
shpfile->isopen = MS_TRUE;
shpfile->hDBF = NULL; /* XBase file is NOT created here... */
return(0);
}
void msShapefileClose(shapefileObj *shpfile)
{
if (shpfile && shpfile->isopen == MS_TRUE) { /* Silently return if called with NULL shpfile by freeLayer() */
if(shpfile->hSHP) msSHPClose(shpfile->hSHP);
if(shpfile->hDBF) msDBFClose(shpfile->hDBF);
if(shpfile->status) free(shpfile->status);
shpfile->isopen = MS_FALSE;
}
}
void msSHPReadAhead(shapefileObj *shpfile)
{
}
int msBitArrayCountBits(ms_bitarray array, int size)
{
int count = 0;
int i;
unsigned int n;
for (i = 0; i < size; i++)
{
n = array[i];
while (n != 0)
{
if (n & 1)
count++;
n = n >> 1;
}
}
return count;
}
/* status array lives in the shpfile, can return MS_SUCCESS/MS_FAILURE/MS_DONE */
int msShapefileWhichShapes(shapefileObj *shpfile, rectObj rect, int debug)
{
int i;
rectObj shaperect;
char *filename;
char *sourcename = 0; /* shape file source string from map file */
char *s = 0; /* pointer to start of '.shp' in source string */
if(shpfile->status) {
free(shpfile->status);
shpfile->status = NULL;
}
shpfile->statusbounds = rect; /* save the search extent */
/* rect and shapefile DON'T overlap... */
if(msRectOverlap(&shpfile->bounds, &rect) != MS_TRUE)
return(MS_DONE);
if(msRectContained(&shpfile->bounds, &rect) == MS_TRUE) {
shpfile->status = msAllocBitArray(shpfile->numshapes);
if(!shpfile->status) {
msSetError(MS_MEMERR, NULL, "msShapefileWhichShapes()");
return(MS_FAILURE);
}
msSetAllBits(shpfile->status, shpfile->numshapes, 1);
}
else {
/* deal with case where sourcename is of the form 'file.shp' */
sourcename = strdup(shpfile->source);
/* TODO: need to add case-insensitive handling! */
s = strstr(sourcename, ".shp");
if( s ) *s = '\0';
if((filename = (char *)malloc(strlen(sourcename)+strlen(MS_INDEX_EXTENSION)+1)) == NULL) {
msSetError(MS_MEMERR, NULL, "msShapefileWhichShapes()");
return(MS_FAILURE);
}
sprintf(filename, "%s%s", sourcename, MS_INDEX_EXTENSION);
shpfile->status = msSearchDiskTree(filename, rect, debug);
free(filename);
free(sourcename);
if(shpfile->status) { /* index */
msFilterTreeSearch(shpfile, shpfile->status, rect);
}
else { /* no index */
shpfile->status = msAllocBitArray(shpfile->numshapes);
if(!shpfile->status) {
msSetError(MS_MEMERR, NULL, "msShapefileWhichShapes()");
return(MS_FAILURE);
}
for(i=0;i<shpfile->numshapes;i++) {
if(msSHPReadBounds(shpfile->hSHP, i, &shaperect) == MS_SUCCESS)
if(msRectOverlap(&shaperect, &rect) == MS_TRUE) msSetBit(shpfile->status, i, 1);
}
}
}
shpfile->lastshape = -1;
return(MS_SUCCESS); /* success */
}
/* Return the absolute path to the given layer's tileindex file's directory */
void msTileIndexAbsoluteDir(char *tiFileAbsDir, layerObj *layer)
{
char tiFileAbsPath[MS_MAXPATHLEN];
char *tiFileAbsDirTmp=NULL;
msBuildPath(tiFileAbsPath, layer->map->mappath, layer->tileindex); /* absolute path to tileindex file */
tiFileAbsDirTmp = msGetPath(tiFileAbsPath); /* tileindex file's directory */
strncpy(tiFileAbsDir, tiFileAbsDirTmp, MS_MAXPATHLEN);
free(tiFileAbsDirTmp);
}
/*
** Build possible paths we might find the tile file at:
** map dir + shape path + filename?
** tile dir + shape path + filename?
** map dir + filename?
**
** Returns
** MS_SUCCESS - found a file
** MS_FAILURE - no file, and map is configured to fail on missing
** MS_DONE - no file, and map is configured to continue on missing
*/
int msTiledSHPTryOpen(shapefileObj *shpfile, layerObj *layer, char *tiFileAbsDir, char *filename) {
char szPath[MS_MAXPATHLEN];
int ignore_missing = msMapIgnoreMissingData(layer->map);
int log_failures = MS_TRUE;
if( ignore_missing == MS_MISSING_DATA_IGNORE )
log_failures = MS_FALSE;
if(msShapefileOpen(shpfile, "rb", msBuildPath3(szPath, layer->map->mappath, layer->map->shapepath, filename), log_failures) == -1) {
if(msShapefileOpen(shpfile, "rb", msBuildPath3(szPath, tiFileAbsDir, layer->map->shapepath, filename), log_failures) == -1) {
if(msShapefileOpen(shpfile, "rb", msBuildPath(szPath, layer->map->mappath, filename), log_failures) == -1) {
if(ignore_missing == MS_MISSING_DATA_FAIL) {
msSetError(MS_IOERR, "Unable to open shapefile '%s' for layer '%s' ... fatal error.", "msTiledSHPTryOpen()", filename, layer->name);
return(MS_FAILURE);
}
else if( ignore_missing == MS_MISSING_DATA_LOG ) {
if( layer->debug || layer->map->debug ) {
msDebug( "Unable to open shapefile '%s' for layer '%s' ... ignoring this missing data.\n", szPath, layer->name );
}
return(MS_DONE);
}
else if( ignore_missing == MS_MISSING_DATA_IGNORE ) {
return(MS_DONE);
}
else {
/* never get here */
msSetError(MS_IOERR, "msIgnoreMissingData returned unexpected value.", "msTiledSHPTryOpen()");
return(MS_FAILURE);
}
}
}
}
return(MS_SUCCESS);
}
int msTiledSHPOpenFile(layerObj *layer)
{
int i;
char *filename, tilename[MS_MAXPATHLEN], szPath[MS_MAXPATHLEN];
char tiFileAbsDir[MS_MAXPATHLEN];
msTiledSHPLayerInfo *tSHP=NULL;
if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
return MS_FAILURE;
/* allocate space for a shapefileObj using layer->layerinfo */
tSHP = (msTiledSHPLayerInfo *) malloc(sizeof(msTiledSHPLayerInfo));
if(!tSHP) {
msSetError(MS_MEMERR, "Error allocating tiled shapefile structures.", "msTiledSHPOpenFile()");
return(MS_FAILURE);
}
tSHP->shpfile = (shapefileObj *) malloc(sizeof(shapefileObj));
tSHP->tileshpfile = NULL; /* may need this if not using a tile layer, look for malloc later */
layer->layerinfo = tSHP;
tSHP->tilelayerindex = msGetLayerIndex(layer->map, layer->tileindex);
if(tSHP->tilelayerindex != -1) { /* does the tileindex reference another layer */
int status;
layerObj *tlp;
tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex));
if(tlp->connectiontype != MS_SHAPEFILE) {
msSetError(MS_SDEERR, "Tileindex layer must be a shapefile.", "msTiledSHPOpenFile()");
return(MS_FAILURE);
}
status = msLayerOpen(tlp);
if(status != MS_SUCCESS) return(MS_FAILURE);
/* build item list (no annotation) since we do have to classify the shape */
status = msLayerWhichItems(tlp, MS_TRUE, MS_FALSE, NULL);
if(status != MS_SUCCESS) return(MS_FAILURE);
tSHP->tileshpfile = (shapefileObj *) tlp->layerinfo; /* shapefiles use layerinfo to point to a shapefileObj */
} else { /* or reference a shapefile directly */
/* we need tSHP->tileshpfile if we're not working with a layer */
tSHP->tileshpfile = (shapefileObj *) malloc(sizeof(shapefileObj));
if(msShapefileOpen(tSHP->tileshpfile, "rb", msBuildPath3(szPath, layer->map->mappath, layer->map->shapepath, layer->tileindex), MS_TRUE) == -1)
if(msShapefileOpen(tSHP->tileshpfile, "rb", msBuildPath(szPath, layer->map->mappath, layer->tileindex), MS_TRUE) == -1)
return(MS_FAILURE);
}
if((layer->tileitemindex = msDBFGetItemIndex(tSHP->tileshpfile->hDBF, layer->tileitem)) == -1) return(MS_FAILURE);
msTileIndexAbsoluteDir(tiFileAbsDir, layer);
/* 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 */
for(i=0; i<tSHP->tileshpfile->numshapes; i++) {
int try_open;
if(!layer->data) /* assume whole filename is in attribute field */
filename = (char*) msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex);
else {
sprintf(tilename,"%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex) , layer->data);
filename = tilename;
}
if(strlen(filename) == 0) continue; /* check again */
try_open = msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename);
if( try_open == MS_DONE )
continue;
else if (try_open == MS_FAILURE )
return(MS_FAILURE);
return(MS_SUCCESS); /* found a template, ok to proceed */
}
msSetError(MS_SHPERR, "Unable to open a single tile to use as a template in layer %s.", "msTiledSHPOpenFile()", layer->name?layer->name:"(null)");
return(MS_FAILURE);
}
int msTiledSHPWhichShapes(layerObj *layer, rectObj rect)
{
int i, status;
char *filename, tilename[MS_MAXPATHLEN];
char tiFileAbsDir[MS_MAXPATHLEN];
msTiledSHPLayerInfo *tSHP=NULL;
if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
return MS_FAILURE;
tSHP = layer->layerinfo;
if(!tSHP) {
msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPWhichShapes()");
return(MS_FAILURE);
}
msShapefileClose(tSHP->shpfile); /* close previously opened files */
if(tSHP->tilelayerindex != -1) { /* does the tileindex reference another layer */
layerObj *tlp;
shapeObj tshape;
tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex));
status= msLayerWhichShapes(tlp, rect);
if(status != MS_SUCCESS) return(status); /* could be MS_DONE or MS_FAILURE */
msTileIndexAbsoluteDir(tiFileAbsDir, layer);
msInitShape(&tshape);
while((status = msLayerNextShape(tlp, &tshape)) == MS_SUCCESS) {
int try_open;
/* TODO: seems stupid to read the tileitem seperately from the shape, need to fix msTiledSHPOpenFile */
if(!layer->data) /* assume whole filename is in attribute field */
filename = (char *) msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tshape.index, layer->tileitemindex);
else {
sprintf(tilename,"%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tshape.index, layer->tileitemindex) , layer->data);
filename = tilename;
}
if(strlen(filename) == 0) continue; /* check again */
try_open = msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename);
if( try_open == MS_DONE )
continue;
else if (try_open == MS_FAILURE )
return(MS_FAILURE);
status = msShapefileWhichShapes(tSHP->shpfile, rect, layer->debug);
if(status == MS_DONE) {
/* Close and continue to next tile */
msShapefileClose(tSHP->shpfile);
continue;
}
else if(status != MS_SUCCESS) {
msShapefileClose(tSHP->shpfile);
return(MS_FAILURE);
}
/* the layer functions keeps track of this */
/* tSHP->tileshpfile->lastshape = tshape.index; */
break;
}
return(status); /* if we reach here we either 1) ran out of tiles or 2) had an error reading a tile */
} else { /* or reference a shapefile directly */
int try_open;
status = msShapefileWhichShapes(tSHP->tileshpfile, rect, layer->debug);
if(status != MS_SUCCESS) return(status); /* could be MS_DONE or MS_FAILURE */
msTileIndexAbsoluteDir(tiFileAbsDir, layer);
/* position the source at the FIRST shapefile */
for(i=0; i<tSHP->tileshpfile->numshapes; i++) {
if(msGetBit(tSHP->tileshpfile->status,i)) {
if(!layer->data) /* assume whole filename is in attribute field */
filename = (char *) msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex);
else {
sprintf(tilename,"%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex) , layer->data);
filename = tilename;
}
if(strlen(filename) == 0) continue; /* check again */
try_open = msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename);
if( try_open == MS_DONE )
continue;
else if (try_open == MS_FAILURE )
return(MS_FAILURE);
status = msShapefileWhichShapes(tSHP->shpfile, rect, layer->debug);
if(status == MS_DONE) {
/* Close and continue to next tile */
msShapefileClose(tSHP->shpfile);
continue;
}
else if(status != MS_SUCCESS) {
msShapefileClose(tSHP->shpfile);
return(MS_FAILURE);
}
tSHP->tileshpfile->lastshape = i;
break;
}
}
if(i == tSHP->tileshpfile->numshapes)
return(MS_DONE); /* no more tiles */
else
return(MS_SUCCESS);
}
return(MS_FAILURE); /* should *never* get here */
}
int msTiledSHPNextShape(layerObj *layer, shapeObj *shape)
{
int i, status, filter_passed = MS_FALSE;
char *filename, tilename[MS_MAXPATHLEN];
char **values=NULL;
char tiFileAbsDir[MS_MAXPATHLEN];
msTiledSHPLayerInfo *tSHP=NULL;
if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
return MS_FAILURE;
tSHP = layer->layerinfo;
if(!tSHP) {
msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPNextShape()");
return(MS_FAILURE);
}
msTileIndexAbsoluteDir(tiFileAbsDir, layer);
do {
i = tSHP->shpfile->lastshape + 1;
while(i<tSHP->shpfile->numshapes && !msGetBit(tSHP->shpfile->status,i)) i++; /* next "in" shape */
if(i == tSHP->shpfile->numshapes) { /* done with this tile, need a new one */
msShapefileClose(tSHP->shpfile); /* clean up */
/* position the source to the NEXT shapefile based on the tileindex */
if(tSHP->tilelayerindex != -1) { /* does the tileindex reference another layer */
layerObj *tlp;
shapeObj tshape;
int try_open;
tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex));
msInitShape(&tshape);
while((status = msLayerNextShape(tlp, &tshape)) == MS_SUCCESS) {
/* TODO: seems stupid to read the tileitem seperately from the shape, need to fix msTiledSHPOpenFile */
if(!layer->data) /* assume whole filename is in attribute field */
filename = (char *) msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tshape.index, layer->tileitemindex);
else {
sprintf(tilename,"%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tshape.index, layer->tileitemindex) , layer->data);
filename = tilename;
}
if(strlen(filename) == 0) continue; /* check again */
try_open = msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename);
if( try_open == MS_DONE )
continue;
else if (try_open == MS_FAILURE )
return(MS_FAILURE);
status = msShapefileWhichShapes(tSHP->shpfile, tSHP->tileshpfile->statusbounds, layer->debug);
if(status == MS_DONE) {
/* Close and continue to next tile */
msShapefileClose(tSHP->shpfile);
continue;
}
else if(status != MS_SUCCESS) {
msShapefileClose(tSHP->shpfile);
return(MS_FAILURE);
}
/* the layer functions keeps track of this */
/* tSHP->tileshpfile->lastshape = tshape.index; */
break;
}
if(status == MS_DONE) return(MS_DONE); /* no more tiles */
else {
msFreeShape(&tshape);
continue; /* we've got shapes */
}
} else { /* or reference a shapefile directly */
for(i=(tSHP->tileshpfile->lastshape + 1); i<tSHP->tileshpfile->numshapes; i++) {
if(msGetBit(tSHP->tileshpfile->status,i)) {
int try_open;
if(!layer->data) /* assume whole filename is in attribute field */
filename = (char*)msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex);
else {
sprintf(tilename,"%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, layer->tileitemindex) , layer->data);
filename = tilename;
}
if(strlen(filename) == 0) continue; /* check again */
try_open = msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename);
if( try_open == MS_DONE )
continue;
else if (try_open == MS_FAILURE )
return(MS_FAILURE);
status = msShapefileWhichShapes(tSHP->shpfile, tSHP->tileshpfile->statusbounds, layer->debug);
if(status == MS_DONE) {
/* Close and continue to next tile */
msShapefileClose(tSHP->shpfile);
continue;
} else if(status != MS_SUCCESS) {
msShapefileClose(tSHP->shpfile);
return(MS_FAILURE);
}
tSHP->tileshpfile->lastshape = i;
break;
}
} /* end for loop */
if(i == tSHP->tileshpfile->numshapes) return(MS_DONE); /* no more tiles */
else continue; /* we've got shapes */
}
}
tSHP->shpfile->lastshape = i;
filter_passed = MS_TRUE; /* By default accept ANY shape */
if(layer->numitems > 0 && layer->iteminfo) {
values = msDBFGetValueList(tSHP->shpfile->hDBF, i, layer->iteminfo, layer->numitems);
if(!values) return(MS_FAILURE);
if((filter_passed = msEvalExpression(&(layer->filter), layer->filteritemindex, values, layer->numitems)) != MS_TRUE) {
msFreeCharArray(values, layer->numitems);
values = NULL;
}
}
msSHPReadShape(tSHP->shpfile->hSHP, i, shape); /* ok to read the data now */
if(shape->type == MS_SHAPE_NULL)
{
msFreeShape(shape);
continue; /* skip NULL shapes */
}
shape->tileindex = tSHP->tileshpfile->lastshape;
shape->values = values;
shape->numvalues = layer->numitems;
if (!filter_passed)
msFreeShape(shape);
} while(!filter_passed); /* Loop until both spatial and attribute filters match */
return(MS_SUCCESS);
}
int msTiledSHPGetShape(layerObj *layer, shapeObj *shape, int tile, long record)
{
char *filename, tilename[MS_MAXPATHLEN], szPath[MS_MAXPATHLEN];
msTiledSHPLayerInfo *tSHP=NULL;
char tiFileAbsDir[MS_MAXPATHLEN];
if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
return MS_FAILURE;
tSHP = layer->layerinfo;
if(!tSHP) {
msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPGetShape()");
return(MS_FAILURE);
}
if((tile < 0) || (tile >= tSHP->tileshpfile->numshapes)) return(MS_FAILURE); /* invalid tile id */
if(tile != tSHP->tileshpfile->lastshape) { /* correct tile is not currenly open so open the correct tile */
msShapefileClose(tSHP->shpfile); /* close current tile */
if(!layer->data) /* assume whole filename is in attribute field */
filename = (char*) msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tile, layer->tileitemindex);
else {
sprintf(tilename,"%s/%s", msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, tile, layer->tileitemindex) , layer->data);
filename = tilename;
}
/* open the shapefile, since a specific tile was request an error should be generated if that tile does not exist */
if(strlen(filename) == 0) return(MS_FAILURE);
if(msShapefileOpen(tSHP->shpfile, "rb", msBuildPath3(szPath, tiFileAbsDir, layer->map->shapepath, filename), MS_TRUE) == -1) {
if(msShapefileOpen(tSHP->shpfile, "rb", msBuildPath3(szPath, layer->map->mappath, layer->map->shapepath, filename), MS_TRUE) == -1) {
if(msShapefileOpen(tSHP->shpfile, "rb", msBuildPath(szPath, layer->map->mappath, filename), MS_TRUE) == -1) {
return(MS_FAILURE);
}
}
}
}
if((record < 0) || (record >= tSHP->shpfile->numshapes)) return(MS_FAILURE);
msSHPReadShape(tSHP->shpfile->hSHP, record, shape);
tSHP->shpfile->lastshape = record;
if(layer->numitems > 0 && layer->iteminfo) {
shape->numvalues = layer->numitems;
shape->values = msDBFGetValueList(tSHP->shpfile->hDBF, record, layer->iteminfo, layer->numitems);
if(!shape->values) return(MS_FAILURE);
}
shape->tileindex = tile;
return(MS_SUCCESS);
}
void msTiledSHPClose(layerObj *layer)
{
msTiledSHPLayerInfo *tSHP=NULL;
tSHP = layer->layerinfo;
if(tSHP) {
msShapefileClose(tSHP->shpfile);
free(tSHP->shpfile);
if(tSHP->tilelayerindex != -1) {
layerObj *tlp;
if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
return;
tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex));
msLayerClose(tlp);
} else {
msShapefileClose(tSHP->tileshpfile);
free(tSHP->tileshpfile);
}
free(tSHP);
}
layer->layerinfo = NULL;
}
/************************************************************************/
/* msTiledSHPClose() */
/* Overloaded version of msTiledSHPClose for virtual table architecture */
/************************************************************************/
int msTiledSHPCloseVT(layerObj *layer)
{
msTiledSHPClose(layer);
return MS_SUCCESS;
}
int msTiledSHPLayerInitItemInfo(layerObj *layer)
{
msTiledSHPLayerInfo *tSHP=NULL;
tSHP = layer->layerinfo;
if(!tSHP) {
msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPLayerInitItemInfo()");
return MS_FAILURE;
}
layer->iteminfo = (int *) msDBFGetItemIndexes(tSHP->shpfile->hDBF, layer->items, layer->numitems);
if(!layer->iteminfo) return(MS_FAILURE);
return MS_SUCCESS;
}
int msTiledSHPLayerGetItems(layerObj *layer)
{
msTiledSHPLayerInfo *tSHP=NULL;
tSHP = layer->layerinfo;
if(!tSHP) {
msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPLayerGetItems()");
return MS_FAILURE;
}
layer->numitems = msDBFGetFieldCount(tSHP->shpfile->hDBF);
layer->items = msDBFGetItems(tSHP->shpfile->hDBF);
if(!layer->items) return MS_FAILURE;
return msTiledSHPLayerInitItemInfo(layer);
}
int msTiledSHPLayerGetExtent(layerObj *layer, rectObj *extent)
{
msTiledSHPLayerInfo *tSHP=NULL;
tSHP = layer->layerinfo;
if(!tSHP) {
msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", "msTiledSHPLayerGetExtent()");
return MS_FAILURE;
}
*extent = tSHP->tileshpfile->bounds;
return MS_SUCCESS;
}
void msTiledSHPLayerFreeItemInfo(layerObj *layer)
{
if(layer->iteminfo) {
free(layer->iteminfo);
layer->iteminfo = NULL;
}
}
int msTiledSHPLayerIsOpen(layerObj *layer)
{
if(layer->layerinfo)
return MS_TRUE;
else
return MS_FALSE;
}
int msTiledSHPLayerInitializeVirtualTable(layerObj *layer)
{
assert(layer != NULL);
assert(layer->vtable != NULL);
layer->vtable->LayerInitItemInfo = msTiledSHPLayerInitItemInfo;
layer->vtable->LayerFreeItemInfo = msTiledSHPLayerFreeItemInfo;
layer->vtable->LayerOpen = msTiledSHPOpenFile;
layer->vtable->LayerIsOpen = msTiledSHPLayerIsOpen;
layer->vtable->LayerWhichShapes = msTiledSHPWhichShapes;
layer->vtable->LayerNextShape = msTiledSHPNextShape;
layer->vtable->LayerGetShape = msTiledSHPGetShape;
layer->vtable->LayerClose = msTiledSHPCloseVT;
layer->vtable->LayerGetItems = msTiledSHPLayerGetItems;
layer->vtable->LayerGetExtent = msTiledSHPLayerGetExtent;
/* layer->vtable->LayerApplyFilterToLayer, use default */
/* layer->vtable->LayerGetAutoStyle, use default */
/* layer->vtable->LayerCloseConnection, use default */;
layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter;
/* layer->vtable->LayerCreateItems, use default */
/* layer->vtable->LayerGetNumFeatures, use default */
return MS_SUCCESS;
}
/* SHAPEFILE Layer virtual table functions */
int msShapeFileLayerInitItemInfo(layerObj *layer)
{
shapefileObj *shpfile = shpfile = layer->layerinfo;
if( ! shpfile) {
msSetError(MS_SHPERR, "Shapefile layer has not been opened.", "msShapeFileLayerInitItemInfo()");
return MS_FAILURE;
}
/* iteminfo needs to be a bit more complex, a list of indexes plus the length of the list */
layer->iteminfo = (int *) msDBFGetItemIndexes(shpfile->hDBF, layer->items, layer->numitems);
if( ! layer->iteminfo) {
return MS_FAILURE;
}
return MS_SUCCESS;
}
void msShapeFileLayerFreeItemInfo(layerObj *layer)
{
if(layer->iteminfo) {
free(layer->iteminfo);
layer->iteminfo = NULL;
}
}
int msShapeFileLayerOpen(layerObj *layer)
{
char szPath[MS_MAXPATHLEN];
shapefileObj *shpfile;
if(layer->layerinfo) return MS_SUCCESS; /* layer already open */
/* allocate space for a shapefileObj using layer->layerinfo */
shpfile = (shapefileObj *) malloc(sizeof(shapefileObj));
if( ! shpfile) {
msSetError(MS_MEMERR, "Error allocating shapefileObj structure.", "msLayerOpen()");
return MS_FAILURE;
}
if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
return MS_FAILURE;
layer->layerinfo = shpfile;
if(msShapefileOpen(shpfile, "rb", msBuildPath3(szPath, layer->map->mappath, layer->map->shapepath, layer->data), MS_TRUE) == -1) {
if(msShapefileOpen(shpfile, "rb", msBuildPath(szPath, layer->map->mappath, layer->data), MS_TRUE) == -1) {
layer->layerinfo = NULL;
free(shpfile);
return MS_FAILURE;
}
}
return MS_SUCCESS;
}
int msShapeFileLayerIsOpen(layerObj *layer)
{
if(layer->layerinfo)
return MS_TRUE;
else
return MS_FALSE;
}
int msShapeFileLayerWhichShapes(layerObj *layer, rectObj rect)
{
int i, n1=0, n2=0;
int status;
shapefileObj *shpfile;
shpfile = layer->layerinfo;
if(!shpfile) {
msSetError(MS_SHPERR, "Shapefile layer has not been opened.", "msLayerWhichShapes()");
return MS_FAILURE;
}
status = msShapefileWhichShapes(shpfile, rect, layer->debug);
if(status != MS_SUCCESS) {
return status;
}
/* now apply the maxshapes criteria (NOTE: this ignores the filter so you could get less than maxfeatures) */
if(layer->maxfeatures > 0) {
for( i = (shpfile->numshapes - 1); i >= 0; i-- ) {
n2 = msGetBit(shpfile->status, i);
n1 += n2;
if( n2 && n1 > layer->maxfeatures ) {
msSetBit(shpfile->status, i, 0);
}
}
}
return MS_SUCCESS;
}
int msShapeFileLayerNextShape(layerObj *layer, shapeObj *shape)
{
int i, filter_passed;
char **values=NULL;
shapefileObj *shpfile;
shpfile = layer->layerinfo;
if(!shpfile) {
msSetError(MS_SHPERR, "Shapefile layer has not been opened.", "msLayerNextShape()");
return MS_FAILURE;
}
do {
i = msGetNextBit(shpfile->status, shpfile->lastshape + 1, shpfile->numshapes);
shpfile->lastshape = i;
if(i == -1) return(MS_DONE); /* nothing else to read */
filter_passed = MS_TRUE; /* By default accept ANY shape */
if(layer->numitems > 0 && layer->iteminfo) {
values = msDBFGetValueList(shpfile->hDBF, i, layer->iteminfo, layer->numitems);
if(!values) return(MS_FAILURE);
if((filter_passed = msEvalExpression(&(layer->filter), layer->filteritemindex, values, layer->numitems)) != MS_TRUE) {
msFreeCharArray(values, layer->numitems);
values = NULL;
}
}
} while(!filter_passed); /* Loop until both spatial and attribute filters match */
msSHPReadShape(shpfile->hSHP, i, shape); /* ok to read the data now */
/* skip NULL shapes (apparently valid for shapefiles, at least ArcView doesn't care) */
if(shape->type == MS_SHAPE_NULL) return(msLayerNextShape(layer, shape));
shape->values = values;
shape->numvalues = layer->numitems;
return MS_SUCCESS;
}
int msShapeFileLayerGetShape(layerObj *layer, shapeObj *shape, int tile, long record)
{
shapefileObj *shpfile;
shpfile = layer->layerinfo;
if(!shpfile) {
msSetError(MS_SHPERR, "Shapefile layer has not been opened.", "msLayerGetShape()");
return MS_FAILURE;
}
/* msSHPReadShape *should* return success or failure so we don't have to test here */
if(record < 0 || record >= shpfile->numshapes) {
msSetError(MS_MISCERR, "Invalid feature id.", "msLayerGetShape()");
return MS_FAILURE;
}
msSHPReadShape(shpfile->hSHP, record, shape);
if(layer->numitems > 0 && layer->iteminfo) {
shape->numvalues = layer->numitems;
shape->values = msDBFGetValueList(shpfile->hDBF, record, layer->iteminfo, layer->numitems);
if(!shape->values) return MS_FAILURE;
}
return MS_SUCCESS;
}
int msShapeFileLayerClose(layerObj *layer)
{
shapefileObj *shpfile;
shpfile = layer->layerinfo;
if(!shpfile) return MS_SUCCESS; /* nothing to do */
msShapefileClose(shpfile);
free(layer->layerinfo);
layer->layerinfo = NULL;
return MS_SUCCESS;
}
int msShapeFileLayerGetItems(layerObj *layer)
{
shapefileObj *shpfile;
shpfile = layer->layerinfo;
if(!shpfile) {
msSetError(MS_SHPERR, "Shapefile layer has not been opened.", "msLayerGetItems()");
return MS_FAILURE;
}
layer->numitems = msDBFGetFieldCount(shpfile->hDBF);
layer->items = msDBFGetItems(shpfile->hDBF);
if(!layer->items) return MS_FAILURE;
return msLayerInitItemInfo(layer);
}
int msShapeFileLayerGetExtent(layerObj *layer, rectObj *extent)
{
*extent = ((shapefileObj*)layer->layerinfo)->bounds;
return MS_SUCCESS;
}
int msShapeFileLayerInitializeVirtualTable(layerObj *layer)
{
assert(layer != NULL);
assert(layer->vtable != NULL);
layer->vtable->LayerInitItemInfo = msShapeFileLayerInitItemInfo;
layer->vtable->LayerFreeItemInfo = msShapeFileLayerFreeItemInfo;
layer->vtable->LayerOpen = msShapeFileLayerOpen;
layer->vtable->LayerIsOpen = msShapeFileLayerIsOpen;
layer->vtable->LayerWhichShapes = msShapeFileLayerWhichShapes;
layer->vtable->LayerNextShape = msShapeFileLayerNextShape;
layer->vtable->LayerGetShape = msShapeFileLayerGetShape;
layer->vtable->LayerClose = msShapeFileLayerClose;
layer->vtable->LayerGetItems = msShapeFileLayerGetItems;
layer->vtable->LayerGetExtent = msShapeFileLayerGetExtent;
/* layer->vtable->LayerGetAutoStyle, use default */
/* layer->vtable->LayerCloseConnection, use default */
layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter;
/* layer->vtable->LayerApplyFilterToLayer, use default */
/* layer->vtable->LayerCreateItems, use default */
/* layer->vtable->LayerGetNumFeatures, use default */
return MS_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment