From 6056d8a8f64fdef92b761c2c8690f41c2c26feb1 Mon Sep 17 00:00:00 2001 From: martin-s Date: Sun, 8 Mar 2009 20:03:25 +0000 Subject: [PATCH] Add:Core:Initial work on shapefile driver git-svn-id: https://navit.svn.sourceforge.net/svnroot/navit/trunk/navit@2086 ffa7fe5e-494d-0410-b361-a75ebd5db220 --- configure.in | 1 + navit/map/Makefile.am | 2 +- navit/map/shapefile/Makefile.am | 9 + navit/map/shapefile/dbfopen.c | 1871 ++++++++++++++++++++++++++++++++ navit/map/shapefile/shapefil.h | 609 +++++++++++ navit/map/shapefile/shapefile.c | 378 +++++++ navit/map/shapefile/shpopen.c | 2284 +++++++++++++++++++++++++++++++++++++++ navit/map/shapefile/shptree.c | 1045 ++++++++++++++++++ 8 files changed, 6198 insertions(+), 1 deletion(-) create mode 100644 navit/map/shapefile/Makefile.am create mode 100644 navit/map/shapefile/dbfopen.c create mode 100644 navit/map/shapefile/shapefil.h create mode 100644 navit/map/shapefile/shapefile.c create mode 100644 navit/map/shapefile/shpopen.c create mode 100644 navit/map/shapefile/shptree.c diff --git a/configure.in b/configure.in index 81b9c95..2367286 100644 --- a/configure.in +++ b/configure.in @@ -828,6 +828,7 @@ navit/binding/dbus/Makefile navit/map/Makefile navit/map/mg/Makefile navit/map/textfile/Makefile +navit/map/shapefile/Makefile navit/map/binfile/Makefile navit/map/garmin/Makefile navit/map/poi_geodownload/Makefile diff --git a/navit/map/Makefile.am b/navit/map/Makefile.am index 6a35548..03eda72 100644 --- a/navit/map/Makefile.am +++ b/navit/map/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS=mg textfile binfile +SUBDIRS=mg textfile binfile shapefile # garmin_img if HAVELIBGARMIN SUBDIRS+=garmin diff --git a/navit/map/shapefile/Makefile.am b/navit/map/shapefile/Makefile.am new file mode 100644 index 0000000..acf0ab1 --- /dev/null +++ b/navit/map/shapefile/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=map_shapefile +if PLUGINS + modulemap_LTLIBRARIES = libmap_shapefile.la +else + noinst_LTLIBRARIES = libmap_shapefile.la +endif +libmap_shapefile_la_SOURCES = shapefile.c dbfopen.c shpopen.c shptree.c shapefil.h +libmap_shapefile_la_LDFLAGS = -module diff --git a/navit/map/shapefile/dbfopen.c b/navit/map/shapefile/dbfopen.c new file mode 100644 index 0000000..4f682ea --- /dev/null +++ b/navit/map/shapefile/dbfopen.c @@ -0,0 +1,1871 @@ +/****************************************************************************** + * $Id: dbfopen.c,v 1.83 2008/11/12 14:28:15 fwarmerdam Exp $ + * + * Project: Shapelib + * Purpose: Implementation of .dbf access API documented in dbf_api.html. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * 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 or substantial portions of the 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. + ****************************************************************************** + * + * $Log: dbfopen.c,v $ + * Revision 1.83 2008/11/12 14:28:15 fwarmerdam + * DBFCreateField() now works on files with records + * + * Revision 1.82 2008/11/11 17:47:09 fwarmerdam + * added DBFDeleteField() function + * + * Revision 1.81 2008/01/03 17:48:13 bram + * in DBFCreate, use default code page LDID/87 (= 0x57, ANSI) + * instead of LDID/3. This seems to be the same as what ESRI + * would be doing by default. + * + * Revision 1.80 2007/12/30 14:36:39 fwarmerdam + * avoid syntax issue with last comment. + * + * Revision 1.79 2007/12/30 14:35:48 fwarmerdam + * Avoid char* / unsigned char* warnings. + * + * Revision 1.78 2007/12/18 18:28:07 bram + * - create hook for client specific atof (bugzilla ticket 1615) + * - check for NULL handle before closing cpCPG file, and close after reading. + * + * Revision 1.77 2007/12/15 20:25:21 bram + * dbfopen.c now reads the Code Page information from the DBF file, and exports + * this information as a string through the DBFGetCodePage function. This is + * either the number from the LDID header field ("LDID/") or as the + * content of an accompanying .CPG file. When creating a DBF file, the code can + * be set using DBFCreateEx. + * + * Revision 1.76 2007/12/12 22:21:32 bram + * DBFClose: check for NULL psDBF handle before trying to close it. + * + * Revision 1.75 2007/12/06 13:58:19 fwarmerdam + * make sure file offset calculations are done in as SAOffset + * + * Revision 1.74 2007/12/06 07:00:25 fwarmerdam + * dbfopen now using SAHooks for fileio + * + * Revision 1.73 2007/09/03 19:48:11 fwarmerdam + * move DBFReadAttribute() static dDoubleField into dbfinfo + * + * Revision 1.72 2007/09/03 19:34:06 fwarmerdam + * Avoid use of static tuple buffer in DBFReadTuple() + * + * Revision 1.71 2006/06/22 14:37:18 fwarmerdam + * avoid memory leak if dbfopen fread fails + * + * Revision 1.70 2006/06/17 17:47:05 fwarmerdam + * use calloc() for dbfinfo in DBFCreate + * + * Revision 1.69 2006/06/17 15:34:32 fwarmerdam + * disallow creating fields wider than 255 + * + * Revision 1.68 2006/06/17 15:12:40 fwarmerdam + * Fixed C++ style comments. + * + * Revision 1.67 2006/06/17 00:24:53 fwarmerdam + * Don't treat non-zero decimals values as high order byte for length + * for strings. It causes serious corruption for some files. + * http://bugzilla.remotesensing.org/show_bug.cgi?id=1202 + * + * Revision 1.66 2006/03/29 18:26:20 fwarmerdam + * fixed bug with size of pachfieldtype in dbfcloneempty + * + * Revision 1.65 2006/02/15 01:14:30 fwarmerdam + * added DBFAddNativeFieldType + * + * Revision 1.64 2006/02/09 00:29:04 fwarmerdam + * Changed to put spaces into string fields that are NULL as + * per http://bugzilla.maptools.org/show_bug.cgi?id=316. + * + * Revision 1.63 2006/01/25 15:35:43 fwarmerdam + * check success on DBFFlushRecord + * + * Revision 1.62 2006/01/10 16:28:03 fwarmerdam + * Fixed typo in CPLError. + * + * Revision 1.61 2006/01/10 16:26:29 fwarmerdam + * Push loading record buffer into DBFLoadRecord. + * Implement CPL error reporting if USE_CPL defined. + * + * Revision 1.60 2006/01/05 01:27:27 fwarmerdam + * added dbf deletion mark/fetch + * + * Revision 1.59 2005/03/14 15:20:28 fwarmerdam + * Fixed last change. + * + * Revision 1.58 2005/03/14 15:18:54 fwarmerdam + * Treat very wide fields with no decimals as double. This is + * more than 32bit integer fields. + * + * Revision 1.57 2005/02/10 20:16:54 fwarmerdam + * Make the pszStringField buffer for DBFReadAttribute() static char [256] + * as per bug 306. + * + * Revision 1.56 2005/02/10 20:07:56 fwarmerdam + * Fixed bug 305 in DBFCloneEmpty() - header length problem. + * + * Revision 1.55 2004/09/26 20:23:46 fwarmerdam + * avoid warnings with rcsid and signed/unsigned stuff + * + * Revision 1.54 2004/09/15 16:26:10 fwarmerdam + * Treat all blank numeric fields as null too. + */ + +#include "shapefil.h" + +#include +#include +#include +#include + +SHP_CVSID("$Id: dbfopen.c,v 1.83 2008/11/12 14:28:15 fwarmerdam Exp $") + +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif + +/************************************************************************/ +/* 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) ); +} + +/************************************************************************/ +/* DBFWriteHeader() */ +/* */ +/* This is called to write out the file header, and field */ +/* descriptions before writing any actual data records. This */ +/* also computes all the DBFDataSet field offset/size/decimals */ +/* and so forth values. */ +/************************************************************************/ + +static void DBFWriteHeader(DBFHandle psDBF) + +{ + unsigned char abyHeader[XBASE_FLDHDR_SZ]; + int i; + + if( !psDBF->bNoHeader ) + return; + + psDBF->bNoHeader = FALSE; + +/* -------------------------------------------------------------------- */ +/* Initialize the file header information. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) + abyHeader[i] = 0; + + abyHeader[0] = 0x03; /* memo field? - just copying */ + + /* write out a dummy date */ + abyHeader[1] = 95; /* YY */ + abyHeader[2] = 7; /* MM */ + abyHeader[3] = 26; /* DD */ + + /* record count preset at zero */ + + abyHeader[8] = (unsigned char) (psDBF->nHeaderLength % 256); + abyHeader[9] = (unsigned char) (psDBF->nHeaderLength / 256); + + abyHeader[10] = (unsigned char) (psDBF->nRecordLength % 256); + abyHeader[11] = (unsigned char) (psDBF->nRecordLength / 256); + + abyHeader[29] = (unsigned char) (psDBF->iLanguageDriver); + +/* -------------------------------------------------------------------- */ +/* Write the initial 32 byte file header, and all the field */ +/* descriptions. */ +/* -------------------------------------------------------------------- */ + psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); + psDBF->sHooks.FWrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp ); + psDBF->sHooks.FWrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields, + psDBF->fp ); + +/* -------------------------------------------------------------------- */ +/* Write out the newline character if there is room for it. */ +/* -------------------------------------------------------------------- */ + if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 ) + { + char cNewline; + + cNewline = 0x0d; + psDBF->sHooks.FWrite( &cNewline, 1, 1, psDBF->fp ); + } +} + +/************************************************************************/ +/* DBFFlushRecord() */ +/* */ +/* Write out the current record if there is one. */ +/************************************************************************/ + +static int DBFFlushRecord( DBFHandle psDBF ) + +{ + SAOffset nRecordOffset; + + if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 ) + { + psDBF->bCurrentRecordModified = FALSE; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord + + psDBF->nHeaderLength; + + if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0 + || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord, + psDBF->nRecordLength, + 1, psDBF->fp ) != 1 ) + { +#ifdef USE_CPL + CPLError( CE_Failure, CPLE_FileIO, + "Failure writing DBF record %d.", + psDBF->nCurrentRecord ); +#else + fprintf( stderr, "Failure writing DBF record %d.", + psDBF->nCurrentRecord ); +#endif + return FALSE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* DBFLoadRecord() */ +/************************************************************************/ + +static int DBFLoadRecord( DBFHandle psDBF, int iRecord ) + +{ + if( psDBF->nCurrentRecord != iRecord ) + { + SAOffset nRecordOffset; + + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, SEEK_SET ) != 0 ) + { +#ifdef USE_CPL + CPLError( CE_Failure, CPLE_FileIO, + "fseek(%ld) failed on DBF file.\n", + (long) nRecordOffset ); +#else + fprintf( stderr, "fseek(%ld) failed on DBF file.\n", + (long) nRecordOffset ); +#endif + return FALSE; + } + + if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord, + psDBF->nRecordLength, 1, psDBF->fp ) != 1 ) + { +#ifdef USE_CPL + CPLError( CE_Failure, CPLE_FileIO, + "fread(%d) failed on DBF file.\n", + psDBF->nRecordLength ); +#else + fprintf( stderr, "fread(%d) failed on DBF file.\n", + psDBF->nRecordLength ); +#endif + return FALSE; + } + + psDBF->nCurrentRecord = iRecord; + } + + return TRUE; +} + +/************************************************************************/ +/* DBFUpdateHeader() */ +/************************************************************************/ + +void SHPAPI_CALL +DBFUpdateHeader( DBFHandle psDBF ) + +{ + unsigned char abyFileHeader[32]; + + if( psDBF->bNoHeader ) + DBFWriteHeader( psDBF ); + + DBFFlushRecord( psDBF ); + + psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); + psDBF->sHooks.FRead( abyFileHeader, 32, 1, psDBF->fp ); + + abyFileHeader[4] = (unsigned char) (psDBF->nRecords % 256); + abyFileHeader[5] = (unsigned char) ((psDBF->nRecords/256) % 256); + abyFileHeader[6] = (unsigned char) ((psDBF->nRecords/(256*256)) % 256); + abyFileHeader[7] = (unsigned char) ((psDBF->nRecords/(256*256*256)) % 256); + + psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); + psDBF->sHooks.FWrite( abyFileHeader, 32, 1, psDBF->fp ); + + psDBF->sHooks.FFlush( psDBF->fp ); +} + +/************************************************************************/ +/* DBFOpen() */ +/* */ +/* Open a .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFOpen( const char * pszFilename, const char * pszAccess ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return DBFOpenLL( pszFilename, pszAccess, &sHooks ); +} + +/************************************************************************/ +/* DBFOpen() */ +/* */ +/* Open a .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) + +{ + DBFHandle psDBF; + SAFile pfCPG; + unsigned char *pabyBuf; + int nFields, nHeadLen, iField, i; + char *pszBasename, *pszFullname; + int nBufSize = 500; + +/* -------------------------------------------------------------------- */ +/* We only allow the access strings "rb" and "r+". */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 + && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0 + && strcmp(pszAccess,"r+b") != 0 ) + return( NULL ); + + if( strcmp(pszAccess,"r") == 0 ) + pszAccess = "rb"; + + if( strcmp(pszAccess,"r+") == 0 ) + pszAccess = "rb+"; + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszFilename)+5); + strcpy( pszBasename, pszFilename ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.dbf", pszBasename ); + + psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) ); + psDBF->fp = psHooks->FOpen( pszFullname, pszAccess ); + memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) ); + + if( psDBF->fp == NULL ) + { + sprintf( pszFullname, "%s.DBF", pszBasename ); + psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess ); + } + + sprintf( pszFullname, "%s.cpg", pszBasename ); + pfCPG = psHooks->FOpen( pszFullname, "r" ); + if( pfCPG == NULL ) + { + sprintf( pszFullname, "%s.CPG", pszBasename ); + pfCPG = psHooks->FOpen( pszFullname, "r" ); + } + + free( pszBasename ); + free( pszFullname ); + + if( psDBF->fp == NULL ) + { + free( psDBF ); + if( pfCPG ) psHooks->FClose( pfCPG ); + return( NULL ); + } + + psDBF->bNoHeader = FALSE; + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + +/* -------------------------------------------------------------------- */ +/* Read Table Header info */ +/* -------------------------------------------------------------------- */ + pabyBuf = (unsigned char *) malloc(nBufSize); + if( psDBF->sHooks.FRead( pabyBuf, 32, 1, psDBF->fp ) != 1 ) + { + psDBF->sHooks.FClose( psDBF->fp ); + if( pfCPG ) psDBF->sHooks.FClose( pfCPG ); + free( pabyBuf ); + free( psDBF ); + return NULL; + } + + psDBF->nRecords = + pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256; + + psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256; + psDBF->nRecordLength = pabyBuf[10] + pabyBuf[11]*256; + psDBF->iLanguageDriver = pabyBuf[29]; + + psDBF->nFields = nFields = (nHeadLen - 32) / 32; + + psDBF->pszCurrentRecord = (char *) malloc(psDBF->nRecordLength); + +/* -------------------------------------------------------------------- */ +/* Figure out the code page from the LDID and CPG */ +/* -------------------------------------------------------------------- */ + + psDBF->pszCodePage = NULL; + if( pfCPG ) + { + size_t n; + char *buffer = (char *) pabyBuf; + buffer[0] = '\0'; + psDBF->sHooks.FRead( pabyBuf, nBufSize - 1, 1, pfCPG ); + n = strcspn( (char *) pabyBuf, "\n\r" ); + if( n > 0 ) + { + pabyBuf[n] = '\0'; + psDBF->pszCodePage = (char *) malloc(n + 1); + memcpy( psDBF->pszCodePage, pabyBuf, n + 1 ); + } + psDBF->sHooks.FClose( pfCPG ); + } + if( psDBF->pszCodePage == NULL && pabyBuf[29] != 0 ) + { + sprintf( (char *) pabyBuf, "LDID/%d", psDBF->iLanguageDriver ); + psDBF->pszCodePage = (char *) malloc(strlen((char*)pabyBuf) + 1); + strcpy( psDBF->pszCodePage, (char *) pabyBuf ); + } + +/* -------------------------------------------------------------------- */ +/* Read in Field Definitions */ +/* -------------------------------------------------------------------- */ + + pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen); + psDBF->pszHeader = (char *) pabyBuf; + + psDBF->sHooks.FSeek( psDBF->fp, 32, 0 ); + if( psDBF->sHooks.FRead( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 ) + { + psDBF->sHooks.FClose( psDBF->fp ); + free( pabyBuf ); + free( psDBF->pszCurrentRecord ); + free( psDBF ); + return NULL; + } + + psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields); + psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields); + psDBF->panFieldDecimals = (int *) malloc(sizeof(int) * nFields); + psDBF->pachFieldType = (char *) malloc(sizeof(char) * nFields); + + for( iField = 0; iField < nFields; iField++ ) + { + unsigned char *pabyFInfo; + + pabyFInfo = pabyBuf+iField*32; + + if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' ) + { + psDBF->panFieldSize[iField] = pabyFInfo[16]; + psDBF->panFieldDecimals[iField] = pabyFInfo[17]; + } + else + { + psDBF->panFieldSize[iField] = pabyFInfo[16]; + psDBF->panFieldDecimals[iField] = 0; + +/* +** The following seemed to be used sometimes to handle files with long +** string fields, but in other cases (such as bug 1202) the decimals field +** just seems to indicate some sort of preferred formatting, not very +** wide fields. So I have disabled this code. FrankW. + psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17]*256; + psDBF->panFieldDecimals[iField] = 0; +*/ + } + + psDBF->pachFieldType[iField] = (char) pabyFInfo[11]; + if( iField == 0 ) + psDBF->panFieldOffset[iField] = 1; + else + psDBF->panFieldOffset[iField] = + psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1]; + } + + return( psDBF ); +} + +/************************************************************************/ +/* DBFClose() */ +/************************************************************************/ + +void SHPAPI_CALL +DBFClose(DBFHandle psDBF) +{ + if( psDBF == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Write out header if not already written. */ +/* -------------------------------------------------------------------- */ + if( psDBF->bNoHeader ) + DBFWriteHeader( psDBF ); + + DBFFlushRecord( psDBF ); + +/* -------------------------------------------------------------------- */ +/* Update last access date, and number of records if we have */ +/* write access. */ +/* -------------------------------------------------------------------- */ + if( psDBF->bUpdated ) + DBFUpdateHeader( psDBF ); + +/* -------------------------------------------------------------------- */ +/* Close, and free resources. */ +/* -------------------------------------------------------------------- */ + psDBF->sHooks.FClose( psDBF->fp ); + + if( psDBF->panFieldOffset != NULL ) + { + free( psDBF->panFieldOffset ); + free( psDBF->panFieldSize ); + free( psDBF->panFieldDecimals ); + free( psDBF->pachFieldType ); + } + + if( psDBF->pszWorkField != NULL ) + free( psDBF->pszWorkField ); + + free( psDBF->pszHeader ); + free( psDBF->pszCurrentRecord ); + free( psDBF->pszCodePage ); + + free( psDBF ); +} + +/************************************************************************/ +/* DBFCreate() */ +/* */ +/* Create a new .dbf file with default code page LDID/87 (0x57) */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCreate( const char * pszFilename ) + +{ + return DBFCreateEx( pszFilename, "LDID/87" ); // 0x57 +} + +/************************************************************************/ +/* DBFCreateEx() */ +/* */ +/* Create a new .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCreateEx( const char * pszFilename, const char* pszCodePage ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return DBFCreateLL( pszFilename, pszCodePage , &sHooks ); +} + +/************************************************************************/ +/* DBFCreate() */ +/* */ +/* Create a new .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHooks ) + +{ + DBFHandle psDBF; + SAFile fp; + char *pszFullname, *pszBasename; + int i, ldid = -1; + char chZero = '\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(pszFilename)+5); + strcpy( pszBasename, pszFilename ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.dbf", pszBasename ); + +/* -------------------------------------------------------------------- */ +/* Create the file. */ +/* -------------------------------------------------------------------- */ + fp = psHooks->FOpen( pszFullname, "wb" ); + if( fp == NULL ) + return( NULL ); + + psHooks->FWrite( &chZero, 1, 1, fp ); + psHooks->FClose( fp ); + + fp = psHooks->FOpen( pszFullname, "rb+" ); + if( fp == NULL ) + return( NULL ); + + + sprintf( pszFullname, "%s.cpg", pszBasename ); + if( pszCodePage != NULL ) + { + if( strncmp( pszCodePage, "LDID/", 5 ) == 0 ) + { + ldid = atoi( pszCodePage + 5 ); + if( ldid > 255 ) + ldid = -1; // don't use 0 to indicate out of range as LDID/0 is a valid one + } + if( ldid < 0 ) + { + SAFile fpCPG = psHooks->FOpen( pszFullname, "w" ); + psHooks->FWrite( (char*) pszCodePage, strlen(pszCodePage), 1, fpCPG ); + psHooks->FClose( fpCPG ); + } + } + if( pszCodePage == NULL || ldid >= 0 ) + { + psHooks->Remove( pszFullname ); + } + + free( pszBasename ); + free( pszFullname ); + +/* -------------------------------------------------------------------- */ +/* Create the info structure. */ +/* -------------------------------------------------------------------- */ + psDBF = (DBFHandle) calloc(1,sizeof(DBFInfo)); + + memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) ); + psDBF->fp = fp; + psDBF->nRecords = 0; + psDBF->nFields = 0; + psDBF->nRecordLength = 1; + psDBF->nHeaderLength = 33; + + psDBF->panFieldOffset = NULL; + psDBF->panFieldSize = NULL; + psDBF->panFieldDecimals = NULL; + psDBF->pachFieldType = NULL; + psDBF->pszHeader = NULL; + + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->pszCurrentRecord = NULL; + + psDBF->bNoHeader = TRUE; + + psDBF->iLanguageDriver = ldid > 0 ? ldid : 0; + psDBF->pszCodePage = NULL; + if( pszCodePage ) + { + psDBF->pszCodePage = (char * ) malloc( strlen(pszCodePage) + 1 ); + strcpy( psDBF->pszCodePage, pszCodePage ); + } + + return( psDBF ); +} + +/************************************************************************/ +/* DBFAddField() */ +/* */ +/* Add a field to a newly created .dbf or to an existing one */ +/************************************************************************/ + +int SHPAPI_CALL +DBFAddField(DBFHandle psDBF, const char * pszFieldName, + DBFFieldType eType, int nWidth, int nDecimals ) + +{ + char chNativeType = 'C'; + + if( eType == FTLogical ) + chNativeType = 'L'; + else if( eType == FTString ) + chNativeType = 'C'; + else + chNativeType = 'N'; + + return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType, + nWidth, nDecimals ); +} + +/************************************************************************/ +/* DBFAddField() */ +/* */ +/* Add a field to a newly created .dbf file before any records */ +/* are written. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, + char chType, int nWidth, int nDecimals ) + +{ + char *pszFInfo; + int i; + int nOldRecordLength, nOldHeaderLength; + char *pszRecord; + char chFieldFill; + SAOffset nRecordOffset; + +/* -------------------------------------------------------------------- */ +/* Do some checking to ensure we can add records to this file. */ +/* -------------------------------------------------------------------- */ + if( nWidth < 1 ) + return -1; + + if( nWidth > 255 ) + nWidth = 255; + + nOldRecordLength = psDBF->nRecordLength; + nOldHeaderLength = psDBF->nHeaderLength; + +/* -------------------------------------------------------------------- */ +/* SfRealloc all the arrays larger to hold the additional field */ +/* information. */ +/* -------------------------------------------------------------------- */ + psDBF->nFields++; + + psDBF->panFieldOffset = (int *) + SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldSize = (int *) + SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldDecimals = (int *) + SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); + + psDBF->pachFieldType = (char *) + SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); + +/* -------------------------------------------------------------------- */ +/* Assign the new field information fields. */ +/* -------------------------------------------------------------------- */ + psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength; + psDBF->nRecordLength += nWidth; + psDBF->panFieldSize[psDBF->nFields-1] = nWidth; + psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals; + psDBF->pachFieldType[psDBF->nFields-1] = chType; + +/* -------------------------------------------------------------------- */ +/* Extend the required header information. */ +/* -------------------------------------------------------------------- */ + psDBF->nHeaderLength += 32; + psDBF->bUpdated = FALSE; + + psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); + + pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1); + + for( i = 0; i < 32; i++ ) + pszFInfo[i] = '\0'; + + if( (int) strlen(pszFieldName) < 10 ) + strncpy( pszFInfo, pszFieldName, strlen(pszFieldName)); + else + strncpy( pszFInfo, pszFieldName, 10); + + pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1]; + + if( chType == 'C' ) + { + pszFInfo[16] = (unsigned char) (nWidth % 256); + pszFInfo[17] = (unsigned char) (nWidth / 256); + } + else + { + pszFInfo[16] = (unsigned char) nWidth; + pszFInfo[17] = (unsigned char) nDecimals; + } + +/* -------------------------------------------------------------------- */ +/* Make the current record buffer appropriately larger. */ +/* -------------------------------------------------------------------- */ + psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, + psDBF->nRecordLength); + + /* we're done if dealing with new .dbf */ + if( psDBF->bNoHeader ) + return( psDBF->nFields - 1 ); + +/* -------------------------------------------------------------------- */ +/* For existing .dbf file, shift records */ +/* -------------------------------------------------------------------- */ + + /* alloc record */ + pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength); + + switch (chType) + { + case 'N': + case 'F': + chFieldFill = '*'; + break; + case 'D': + chFieldFill = '0'; + break; + case 'L': + chFieldFill = '?'; + break; + default: + chFieldFill = ' '; + break; + } + + for (i = psDBF->nRecords-1; i >= 0; --i) + { + nRecordOffset = nOldRecordLength * (SAOffset) i + nOldHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); + + /* set new field's value to NULL */ + memset(pszRecord + nOldRecordLength, chFieldFill, nWidth); + + nRecordOffset = psDBF->nRecordLength * (SAOffset) i + psDBF->nHeaderLength; + + /* move record to the new place*/ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); + } + + /* free record */ + free(pszRecord); + + /* force update of header with new header, record length and new field */ + psDBF->bNoHeader = TRUE; + DBFUpdateHeader( psDBF ); + + return( psDBF->nFields-1 ); +} + +/************************************************************************/ +/* DBFReadAttribute() */ +/* */ +/* Read one of the attribute fields of a record. */ +/************************************************************************/ + +static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, + char chReqType ) + +{ + unsigned char *pabyRec; + void *pReturnField = NULL; + +/* -------------------------------------------------------------------- */ +/* Verify selection. */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity >= psDBF->nRecords ) + return( NULL ); + + if( iField < 0 || iField >= psDBF->nFields ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Have we read the record? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return NULL; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + +/* -------------------------------------------------------------------- */ +/* Ensure we have room to extract the target field. */ +/* -------------------------------------------------------------------- */ + if( psDBF->panFieldSize[iField] >= psDBF->nWorkFieldLength ) + { + psDBF->nWorkFieldLength = psDBF->panFieldSize[iField] + 100; + if( psDBF->pszWorkField == NULL ) + psDBF->pszWorkField = (char *) malloc(psDBF->nWorkFieldLength); + else + psDBF->pszWorkField = (char *) realloc(psDBF->pszWorkField, + psDBF->nWorkFieldLength); + } + +/* -------------------------------------------------------------------- */ +/* Extract the requested field. */ +/* -------------------------------------------------------------------- */ + strncpy( psDBF->pszWorkField, + ((const char *) pabyRec) + psDBF->panFieldOffset[iField], + psDBF->panFieldSize[iField] ); + psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0'; + + pReturnField = psDBF->pszWorkField; + +/* -------------------------------------------------------------------- */ +/* Decode the field. */ +/* -------------------------------------------------------------------- */ + if( chReqType == 'N' ) + { + psDBF->dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField); + + pReturnField = &(psDBF->dfDoubleField); + } + +/* -------------------------------------------------------------------- */ +/* Should we trim white space off the string attribute value? */ +/* -------------------------------------------------------------------- */ +#ifdef TRIM_DBF_WHITESPACE + else + { + char *pchSrc, *pchDst; + + pchDst = pchSrc = psDBF->pszWorkField; + while( *pchSrc == ' ' ) + pchSrc++; + + while( *pchSrc != '\0' ) + *(pchDst++) = *(pchSrc++); + *pchDst = '\0'; + + while( pchDst != psDBF->pszWorkField && *(--pchDst) == ' ' ) + *pchDst = '\0'; + } +#endif + + return( pReturnField ); +} + +/************************************************************************/ +/* DBFReadIntAttribute() */ +/* */ +/* Read an integer attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + double *pdValue; + + pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); + + if( pdValue == NULL ) + return 0; + else + return( (int) *pdValue ); +} + +/************************************************************************/ +/* DBFReadDoubleAttribute() */ +/* */ +/* Read a double attribute. */ +/************************************************************************/ + +double SHPAPI_CALL +DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + double *pdValue; + + pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); + + if( pdValue == NULL ) + return 0.0; + else + return( *pdValue ); +} + +/************************************************************************/ +/* DBFReadStringAttribute() */ +/* */ +/* Read a string attribute. */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) ); +} + +/************************************************************************/ +/* DBFReadLogicalAttribute() */ +/* */ +/* Read a logical attribute. */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'L' ) ); +} + +/************************************************************************/ +/* DBFIsAttributeNULL() */ +/* */ +/* Return TRUE if value for field is NULL. */ +/* */ +/* Contributed by Jim Matthews. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField ) + +{ + const char *pszValue; + int i; + + pszValue = DBFReadStringAttribute( psDBF, iRecord, iField ); + + if( pszValue == NULL ) + return TRUE; + + switch(psDBF->pachFieldType[iField]) + { + case 'N': + case 'F': + /* + ** We accept all asterisks or all blanks as NULL + ** though according to the spec I think it should be all + ** asterisks. + */ + if( pszValue[0] == '*' ) + return TRUE; + + for( i = 0; pszValue[i] != '\0'; i++ ) + { + if( pszValue[i] != ' ' ) + return FALSE; + } + return TRUE; + + case 'D': + /* NULL date fields have value "00000000" */ + return strncmp(pszValue,"00000000",8) == 0; + + case 'L': + /* NULL boolean fields have value "?" */ + return pszValue[0] == '?'; + + default: + /* empty string fields are considered NULL */ + return strlen(pszValue) == 0; + } +} + +/************************************************************************/ +/* DBFGetFieldCount() */ +/* */ +/* Return the number of fields in this table. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFGetFieldCount( DBFHandle psDBF ) + +{ + return( psDBF->nFields ); +} + +/************************************************************************/ +/* DBFGetRecordCount() */ +/* */ +/* Return the number of records in this table. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFGetRecordCount( DBFHandle psDBF ) + +{ + return( psDBF->nRecords ); +} + +/************************************************************************/ +/* DBFGetFieldInfo() */ +/* */ +/* Return any requested information about the field. */ +/************************************************************************/ + +DBFFieldType SHPAPI_CALL +DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, + int * pnWidth, int * pnDecimals ) + +{ + if( iField < 0 || iField >= psDBF->nFields ) + return( FTInvalid ); + + if( pnWidth != NULL ) + *pnWidth = psDBF->panFieldSize[iField]; + + if( pnDecimals != NULL ) + *pnDecimals = psDBF->panFieldDecimals[iField]; + + if( pszFieldName != NULL ) + { + int i; + + strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 ); + pszFieldName[11] = '\0'; + for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- ) + pszFieldName[i] = '\0'; + } + + if ( psDBF->pachFieldType[iField] == 'L' ) + return( FTLogical); + + else if( psDBF->pachFieldType[iField] == 'N' + || psDBF->pachFieldType[iField] == 'F' ) + { + if( psDBF->panFieldDecimals[iField] > 0 + || psDBF->panFieldSize[iField] > 10 ) + return( FTDouble ); + else + return( FTInteger ); + } + else + { + return( FTString ); + } +} + +/************************************************************************/ +/* DBFWriteAttribute() */ +/* */ +/* Write an attribute record to the file. */ +/************************************************************************/ + +static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, + void * pValue ) + +{ + int i, j, nRetResult = TRUE; + unsigned char *pabyRec; + char szSField[400], szFormat[20]; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + psDBF->nRecords++; + for( i = 0; i < psDBF->nRecordLength; i++ ) + psDBF->pszCurrentRecord[i] = ' '; + + psDBF->nCurrentRecord = hEntity; + } + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return FALSE; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + +/* -------------------------------------------------------------------- */ +/* Translate NULL value to valid DBF file representation. */ +/* */ +/* Contributed by Jim Matthews. */ +/* -------------------------------------------------------------------- */ + if( pValue == NULL ) + { + switch(psDBF->pachFieldType[iField]) + { + case 'N': + case 'F': + /* NULL numeric fields have value "****************" */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '*', + psDBF->panFieldSize[iField] ); + break; + + case 'D': + /* NULL date fields have value "00000000" */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '0', + psDBF->panFieldSize[iField] ); + break; + + case 'L': + /* NULL boolean fields have value "?" */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '?', + psDBF->panFieldSize[iField] ); + break; + + default: + /* empty string fields are considered NULL */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), ' ', + psDBF->panFieldSize[iField] ); + break; + } + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Assign all the record fields. */ +/* -------------------------------------------------------------------- */ + switch( psDBF->pachFieldType[iField] ) + { + case 'D': + case 'N': + case 'F': + if( psDBF->panFieldDecimals[iField] == 0 ) + { + int nWidth = psDBF->panFieldSize[iField]; + + if( (int) sizeof(szSField)-2 < nWidth ) + nWidth = sizeof(szSField)-2; + + sprintf( szFormat, "%%%dd", nWidth ); + sprintf(szSField, szFormat, (int) *((double *) pValue) ); + if( (int)strlen(szSField) > psDBF->panFieldSize[iField] ) + { + szSField[psDBF->panFieldSize[iField]] = '\0'; + nRetResult = FALSE; + } + + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + szSField, strlen(szSField) ); + } + else + { + int nWidth = psDBF->panFieldSize[iField]; + + if( (int) sizeof(szSField)-2 < nWidth ) + nWidth = sizeof(szSField)-2; + + sprintf( szFormat, "%%%d.%df", + nWidth, psDBF->panFieldDecimals[iField] ); + sprintf(szSField, szFormat, *((double *) pValue) ); + if( (int) strlen(szSField) > psDBF->panFieldSize[iField] ) + { + szSField[psDBF->panFieldSize[iField]] = '\0'; + nRetResult = FALSE; + } + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + szSField, strlen(szSField) ); + } + break; + + case 'L': + if (psDBF->panFieldSize[iField] >= 1 && + (*(char*)pValue == 'F' || *(char*)pValue == 'T')) + *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue; + break; + + default: + if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] ) + { + j = psDBF->panFieldSize[iField]; + nRetResult = FALSE; + } + else + { + memset( pabyRec+psDBF->panFieldOffset[iField], ' ', + psDBF->panFieldSize[iField] ); + j = strlen((char *) pValue); + } + + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + (char *) pValue, j ); + break; + } + + return( nRetResult ); +} + +/************************************************************************/ +/* DBFWriteAttributeDirectly() */ +/* */ +/* Write an attribute record to the file, but without any */ +/* reformatting based on type. The provided buffer is written */ +/* as is to the field position in the record. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, + void * pValue ) + +{ + int i, j; + unsigned char *pabyRec; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + psDBF->nRecords++; + for( i = 0; i < psDBF->nRecordLength; i++ ) + psDBF->pszCurrentRecord[i] = ' '; + + psDBF->nCurrentRecord = hEntity; + } + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return FALSE; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + +/* -------------------------------------------------------------------- */ +/* Assign all the record fields. */ +/* -------------------------------------------------------------------- */ + if( (int)strlen((char *) pValue) > psDBF->panFieldSize[iField] ) + j = psDBF->panFieldSize[iField]; + else + { + memset( pabyRec+psDBF->panFieldOffset[iField], ' ', + psDBF->panFieldSize[iField] ); + j = strlen((char *) pValue); + } + + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + (char *) pValue, j ); + + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + + return( TRUE ); +} + +/************************************************************************/ +/* DBFWriteDoubleAttribute() */ +/* */ +/* Write a double attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField, + double dValue ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); +} + +/************************************************************************/ +/* DBFWriteIntegerAttribute() */ +/* */ +/* Write a integer attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField, + int nValue ) + +{ + double dValue = nValue; + + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); +} + +/************************************************************************/ +/* DBFWriteStringAttribute() */ +/* */ +/* Write a string attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField, + const char * pszValue ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) ); +} + +/************************************************************************/ +/* DBFWriteNULLAttribute() */ +/* */ +/* Write a string attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, NULL ) ); +} + +/************************************************************************/ +/* DBFWriteLogicalAttribute() */ +/* */ +/* Write a logical attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteLogicalAttribute( DBFHandle psDBF, int iRecord, int iField, + const char lValue) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) (&lValue) ) ); +} + +/************************************************************************/ +/* DBFWriteTuple() */ +/* */ +/* Write an attribute record to the file. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ) + +{ + int i; + unsigned char *pabyRec; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + psDBF->nRecords++; + for( i = 0; i < psDBF->nRecordLength; i++ ) + psDBF->pszCurrentRecord[i] = ' '; + + psDBF->nCurrentRecord = hEntity; + } + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return FALSE; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + + memcpy ( pabyRec, pRawTuple, psDBF->nRecordLength ); + + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + + return( TRUE ); +} + +/************************************************************************/ +/* DBFReadTuple() */ +/* */ +/* Read a complete record. Note that the result is only valid */ +/* till the next record read for any reason. */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFReadTuple(DBFHandle psDBF, int hEntity ) + +{ + if( hEntity < 0 || hEntity >= psDBF->nRecords ) + return( NULL ); + + if( !DBFLoadRecord( psDBF, hEntity ) ) + return NULL; + + return (const char *) psDBF->pszCurrentRecord; +} + +/************************************************************************/ +/* DBFCloneEmpty() */ +/* */ +/* Read one of the attribute fields of a record. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) +{ + DBFHandle newDBF; + + newDBF = DBFCreateEx ( pszFilename, psDBF->pszCodePage ); + if ( newDBF == NULL ) return ( NULL ); + + newDBF->nFields = psDBF->nFields; + newDBF->nRecordLength = psDBF->nRecordLength; + newDBF->nHeaderLength = psDBF->nHeaderLength; + + newDBF->pszHeader = (char *) malloc ( newDBF->nHeaderLength ); + memcpy ( newDBF->pszHeader, psDBF->pszHeader, newDBF->nHeaderLength ); + + newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); + newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); + newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); + newDBF->pachFieldType = (char *) malloc ( sizeof(char) * psDBF->nFields ); + memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(char)*psDBF->nFields ); + + newDBF->bNoHeader = TRUE; + newDBF->bUpdated = TRUE; + + DBFWriteHeader ( newDBF ); + DBFClose ( newDBF ); + + newDBF = DBFOpen ( pszFilename, "rb+" ); + + return ( newDBF ); +} + +/************************************************************************/ +/* DBFGetNativeFieldType() */ +/* */ +/* Return the DBase field type for the specified field. */ +/* */ +/* Value can be one of: 'C' (String), 'D' (Date), 'F' (Float), */ +/* 'N' (Numeric, with or without decimal), */ +/* 'L' (Logical), */ +/* 'M' (Memo: 10 digits .DBT block ptr) */ +/************************************************************************/ + +char SHPAPI_CALL +DBFGetNativeFieldType( DBFHandle psDBF, int iField ) + +{ + if( iField >=0 && iField < psDBF->nFields ) + return psDBF->pachFieldType[iField]; + + return ' '; +} + +/************************************************************************/ +/* str_to_upper() */ +/************************************************************************/ + +static void str_to_upper (char *string) +{ + int len; + short i = -1; + + len = strlen (string); + + while (++i < len) + if (isalpha(string[i]) && islower(string[i])) + string[i] = (char) toupper ((int)string[i]); +} + +/************************************************************************/ +/* DBFGetFieldIndex() */ +/* */ +/* Get the index number for a field in a .dbf file. */ +/* */ +/* Contributed by Jim Matthews. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName) + +{ + char name[12], name1[12], name2[12]; + int i; + + strncpy(name1, pszFieldName,11); + name1[11] = '\0'; + str_to_upper(name1); + + for( i = 0; i < DBFGetFieldCount(psDBF); i++ ) + { + DBFGetFieldInfo( psDBF, i, name, NULL, NULL ); + strncpy(name2,name,11); + str_to_upper(name2); + + if(!strncmp(name1,name2,10)) + return(i); + } + return(-1); +} + +/************************************************************************/ +/* DBFIsRecordDeleted() */ +/* */ +/* Returns TRUE if the indicated record is deleted, otherwise */ +/* it returns FALSE. */ +/************************************************************************/ + +int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify selection. */ +/* -------------------------------------------------------------------- */ + if( iShape < 0 || iShape >= psDBF->nRecords ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Have we read the record? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, iShape ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* '*' means deleted. */ +/* -------------------------------------------------------------------- */ + return psDBF->pszCurrentRecord[0] == '*'; +} + +/************************************************************************/ +/* DBFMarkRecordDeleted() */ +/************************************************************************/ + +int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, + int bIsDeleted ) + +{ + char chNewFlag; + +/* -------------------------------------------------------------------- */ +/* Verify selection. */ +/* -------------------------------------------------------------------- */ + if( iShape < 0 || iShape >= psDBF->nRecords ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, iShape ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Assign value, marking record as dirty if it changes. */ +/* -------------------------------------------------------------------- */ + if( bIsDeleted ) + chNewFlag = '*'; + else + chNewFlag = ' '; + + if( psDBF->pszCurrentRecord[0] != chNewFlag ) + { + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + psDBF->pszCurrentRecord[0] = chNewFlag; + } + + return TRUE; +} + +/************************************************************************/ +/* DBFGetCodePage */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFGetCodePage(DBFHandle psDBF ) +{ + if( psDBF == NULL ) + return NULL; + return psDBF->pszCodePage; +} + +/************************************************************************/ +/* DBFDeleteField() */ +/* */ +/* Remove a field from a .dbf file */ +/************************************************************************/ + +int SHPAPI_CALL +DBFDeleteField(DBFHandle psDBF, int iField) +{ + int nOldRecordLength, nOldHeaderLength; + int nDeletedFieldOffset, nDeletedFieldSize; + SAOffset nRecordOffset; + char* pszRecord; + int i, iRecord; + + if (iField < 0 || iField >= psDBF->nFields) + return FALSE; + + /* make sure that everything is written in .dbf */ + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + /* get information about field to be deleted */ + nOldRecordLength = psDBF->nRecordLength; + nOldHeaderLength = psDBF->nHeaderLength; + nDeletedFieldOffset = psDBF->panFieldOffset[iField]; + nDeletedFieldSize = psDBF->panFieldSize[iField]; + + /* update fields info */ + for (i = iField + 1; i < psDBF->nFields; i++) + { + psDBF->panFieldOffset[i-1] = psDBF->panFieldOffset[i] - nDeletedFieldSize; + psDBF->panFieldSize[i-1] = psDBF->panFieldSize[i]; + psDBF->panFieldDecimals[i-1] = psDBF->panFieldDecimals[i]; + psDBF->pachFieldType[i-1] = psDBF->pachFieldType[i]; + } + + /* resize fields arrays */ + psDBF->nFields--; + + psDBF->panFieldOffset = (int *) + SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldSize = (int *) + SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldDecimals = (int *) + SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); + + psDBF->pachFieldType = (char *) + SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); + + /* update header information */ + psDBF->nHeaderLength -= 32; + psDBF->nRecordLength -= nDeletedFieldSize; + + /* overwrite field information in header */ + memcpy(psDBF->pszHeader + iField*32, + psDBF->pszHeader + (iField+1)*32, + sizeof(char) * (psDBF->nFields - iField)*32); + + psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); + + /* update size of current record appropriately */ + psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, + psDBF->nRecordLength); + + /* we're done if we're dealing with not yet created .dbf */ + if ( psDBF->bNoHeader && psDBF->nRecords == 0 ) + return TRUE; + + /* force update of header with new header and record length */ + psDBF->bNoHeader = TRUE; + DBFUpdateHeader( psDBF ); + + /* alloc record */ + pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength); + + /* shift records to their new positions */ + for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++) + { + nRecordOffset = + nOldRecordLength * (SAOffset) iRecord + nOldHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* move record in two steps */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecord, nDeletedFieldOffset, 1, psDBF->fp ); + psDBF->sHooks.FWrite( pszRecord + nDeletedFieldOffset + nDeletedFieldSize, + nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize, + 1, psDBF->fp ); + + } + + /* TODO: truncate file */ + + /* free record */ + free(pszRecord); + + return TRUE; +} diff --git a/navit/map/shapefile/shapefil.h b/navit/map/shapefile/shapefil.h new file mode 100644 index 0000000..059244b --- /dev/null +++ b/navit/map/shapefile/shapefil.h @@ -0,0 +1,609 @@ +#ifndef SHAPEFILE_H_INCLUDED +#define SHAPEFILE_H_INCLUDED + +/****************************************************************************** + * $Id: shapefil.h 15715 2008-11-12 15:15:21Z warmerdam $ + * + * Project: Shapelib + * Purpose: Primary include file for Shapelib. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * 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 or substantial portions of the 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. + ****************************************************************************** + * + * $Log: shapefil.h,v $ + * Revision 1.46 2008/11/12 14:28:15 fwarmerdam + * DBFCreateField() now works on files with records + * + * Revision 1.45 2008/11/11 17:47:10 fwarmerdam + * added DBFDeleteField() function + * + * Revision 1.44 2008/01/16 20:05:19 bram + * Add file hooks that accept UTF-8 encoded filenames on some platforms. Use SASetupUtf8Hooks + * tosetup the hooks and check SHPAPI_UTF8_HOOKS for its availability. Currently, this + * is only available on the Windows platform that decodes the UTF-8 filenames to wide + * character strings and feeds them to _wfopen and _wremove. + * + * Revision 1.43 2008/01/10 16:35:30 fwarmerdam + * avoid _ prefix on #defined symbols (bug 1840) + * + * Revision 1.42 2007/12/18 18:28:14 bram + * - create hook for client specific atof (bugzilla ticket 1615) + * - check for NULL handle before closing cpCPG file, and close after reading. + * + * Revision 1.41 2007/12/15 20:25:32 bram + * dbfopen.c now reads the Code Page information from the DBF file, and exports + * this information as a string through the DBFGetCodePage function. This is + * either the number from the LDID header field ("LDID/") or as the + * content of an accompanying .CPG file. When creating a DBF file, the code can + * be set using DBFCreateEx. + * + * Revision 1.40 2007/12/06 07:00:25 fwarmerdam + * dbfopen now using SAHooks for fileio + * + * Revision 1.39 2007/12/04 20:37:56 fwarmerdam + * preliminary implementation of hooks api for io and errors + * + * Revision 1.38 2007/11/21 22:39:56 fwarmerdam + * close shx file in readonly mode (GDAL #1956) + * + * Revision 1.37 2007/10/27 03:31:14 fwarmerdam + * limit default depth of tree to 12 levels (gdal ticket #1594) + * + * Revision 1.36 2007/09/10 23:33:15 fwarmerdam + * Upstreamed support for visibility flag in SHPAPI_CALL for the needs + * of GDAL (gdal ticket #1810). + * + * Revision 1.35 2007/09/03 19:48:10 fwarmerdam + * move DBFReadAttribute() static dDoubleField into dbfinfo + * + * Revision 1.34 2006/06/17 15:33:32 fwarmerdam + * added pszWorkField - bug 1202 (rso) + * + * Revision 1.33 2006/02/15 01:14:30 fwarmerdam + * added DBFAddNativeFieldType + * + * Revision 1.32 2006/01/26 15:07:32 fwarmerdam + * add bMeasureIsUsed flag from Craig Bruce: Bug 1249 + * + * Revision 1.31 2006/01/05 01:27:27 fwarmerdam + * added dbf deletion mark/fetch + * + * Revision 1.30 2005/01/03 22:30:13 fwarmerdam + * added support for saved quadtrees + * + * Revision 1.29 2004/09/26 20:09:35 fwarmerdam + * avoid rcsid warnings + * + * Revision 1.28 2003/12/29 06:02:18 fwarmerdam + * added cpl_error.h option + * + * Revision 1.27 2003/04/21 18:30:37 warmerda + * added header write/update public methods + * + * Revision 1.26 2002/09/29 00:00:08 warmerda + * added FTLogical and logical attribute read/write calls + * + * Revision 1.25 2002/05/07 13:46:30 warmerda + * added DBFWriteAttributeDirectly(). + * + * Revision 1.24 2002/04/10 16:59:54 warmerda + * added SHPRewindObject + * + * Revision 1.23 2002/01/15 14:36:07 warmerda + * updated email address + * + * Revision 1.22 2002/01/15 14:32:00 warmerda + * try to improve SHPAPI_CALL docs + */ + +#include + +#ifdef USE_DBMALLOC +#include +#endif + +#ifdef USE_CPL +#include "cpl_error.h" +#include "cpl_vsi.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************/ +/* Configuration options. */ +/************************************************************************/ + +/* -------------------------------------------------------------------- */ +/* Should the DBFReadStringAttribute() strip leading and */ +/* trailing white space? */ +/* -------------------------------------------------------------------- */ +#define TRIM_DBF_WHITESPACE + +/* -------------------------------------------------------------------- */ +/* Should we write measure values to the Multipatch object? */ +/* Reportedly ArcView crashes if we do write it, so for now it */ +/* is disabled. */ +/* -------------------------------------------------------------------- */ +#define DISABLE_MULTIPATCH_MEASURE + +/* -------------------------------------------------------------------- */ +/* SHPAPI_CALL */ +/* */ +/* The following two macros are present to allow forcing */ +/* various calling conventions on the Shapelib API. */ +/* */ +/* To force __stdcall conventions (needed to call Shapelib */ +/* from Visual Basic and/or Dephi I believe) the makefile could */ +/* be modified to define: */ +/* */ +/* /DSHPAPI_CALL=__stdcall */ +/* */ +/* If it is desired to force export of the Shapelib API without */ +/* using the shapelib.def file, use the following definition. */ +/* */ +/* /DSHAPELIB_DLLEXPORT */ +/* */ +/* To get both at once it will be necessary to hack this */ +/* include file to define: */ +/* */ +/* #define SHPAPI_CALL __declspec(dllexport) __stdcall */ +/* #define SHPAPI_CALL1 __declspec(dllexport) * __stdcall */ +/* */ +/* The complexity of the situtation is partly caused by the */ +/* peculiar requirement of Visual C++ that __stdcall appear */ +/* after any "*"'s in the return value of a function while the */ +/* __declspec(dllexport) must appear before them. */ +/* -------------------------------------------------------------------- */ + +#ifdef SHAPELIB_DLLEXPORT +# define SHPAPI_CALL __declspec(dllexport) +# define SHPAPI_CALL1(x) __declspec(dllexport) x +#endif + +#ifndef SHPAPI_CALL +# if defined(USE_GCC_VISIBILITY_FLAG) +# define SHPAPI_CALL __attribute__ ((visibility("default"))) +# define SHPAPI_CALL1(x) __attribute__ ((visibility("default"))) x +# else +# define SHPAPI_CALL +# endif +#endif + +#ifndef SHPAPI_CALL1 +# define SHPAPI_CALL1(x) x SHPAPI_CALL +#endif + +/* -------------------------------------------------------------------- */ +/* Macros for controlling CVSID and ensuring they don't appear */ +/* as unreferenced variables resulting in lots of warnings. */ +/* -------------------------------------------------------------------- */ +#ifndef DISABLE_CVSID +# define SHP_CVSID(string) static char cpl_cvsid[] = string; \ +static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : cpl_cvsid ); } +#else +# define SHP_CVSID(string) +#endif + +/* -------------------------------------------------------------------- */ +/* On some platforms, additional file IO hooks are defined that */ +/* UTF-8 encoded filenames Unicode filenames */ +/* -------------------------------------------------------------------- */ +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +# define SHPAPI_WINDOWS +# define SHPAPI_UTF8_HOOKS +#endif + +/* -------------------------------------------------------------------- */ +/* IO/Error hook functions. */ +/* -------------------------------------------------------------------- */ +typedef int *SAFile; + +#ifndef SAOffset +typedef unsigned long SAOffset; +#endif + +typedef struct { + SAFile (*FOpen) ( const char *filename, const char *access); + SAOffset (*FRead) ( void *p, SAOffset size, SAOffset nmemb, SAFile file); + SAOffset (*FWrite)( void *p, SAOffset size, SAOffset nmemb, SAFile file); + SAOffset (*FSeek) ( SAFile file, SAOffset offset, int whence ); + SAOffset (*FTell) ( SAFile file ); + int (*FFlush)( SAFile file ); + int (*FClose)( SAFile file ); + int (*Remove) ( const char *filename ); + + void (*Error) ( const char *message ); + double (*Atof) ( const char *str ); +} SAHooks; + +void SHPAPI_CALL SASetupDefaultHooks( SAHooks *psHooks ); +#ifdef SHPAPI_UTF8_HOOKS +void SHPAPI_CALL SASetupUtf8Hooks( SAHooks *psHooks ); +#endif + +/************************************************************************/ +/* SHP Support. */ +/************************************************************************/ +typedef struct +{ + SAHooks sHooks; + + SAFile fpSHP; + SAFile fpSHX; + + int nShapeType; /* SHPT_* */ + + int nFileSize; /* SHP file */ + + int nRecords; + int nMaxRecords; + int *panRecOffset; + int *panRecSize; + + double adBoundsMin[4]; + double adBoundsMax[4]; + + int bUpdated; + + unsigned char *pabyRec; + int nBufSize; +} SHPInfo; + +typedef SHPInfo * SHPHandle; + +/* -------------------------------------------------------------------- */ +/* Shape types (nSHPType) */ +/* -------------------------------------------------------------------- */ +#define SHPT_NULL 0 +#define SHPT_POINT 1 +#define SHPT_ARC 3 +#define SHPT_POLYGON 5 +#define SHPT_MULTIPOINT 8 +#define SHPT_POINTZ 11 +#define SHPT_ARCZ 13 +#define SHPT_POLYGONZ 15 +#define SHPT_MULTIPOINTZ 18 +#define SHPT_POINTM 21 +#define SHPT_ARCM 23 +#define SHPT_POLYGONM 25 +#define SHPT_MULTIPOINTM 28 +#define SHPT_MULTIPATCH 31 + + +/* -------------------------------------------------------------------- */ +/* Part types - everything but SHPT_MULTIPATCH just uses */ +/* SHPP_RING. */ +/* -------------------------------------------------------------------- */ + +#define SHPP_TRISTRIP 0 +#define SHPP_TRIFAN 1 +#define SHPP_OUTERRING 2 +#define SHPP_INNERRING 3 +#define SHPP_FIRSTRING 4 +#define SHPP_RING 5 + +/* -------------------------------------------------------------------- */ +/* SHPObject - represents on shape (without attributes) read */ +/* from the .shp file. */ +/* -------------------------------------------------------------------- */ +typedef struct +{ + int nSHPType; + + int nShapeId; /* -1 is unknown/unassigned */ + + int nParts; + int *panPartStart; + int *panPartType; + + int nVertices; + double *padfX; + double *padfY; + double *padfZ; + double *padfM; + + double dfXMin; + double dfYMin; + double dfZMin; + double dfMMin; + + double dfXMax; + double dfYMax; + double dfZMax; + double dfMMax; + + int bMeasureIsUsed; +} SHPObject; + +/* -------------------------------------------------------------------- */ +/* SHP API Prototypes */ +/* -------------------------------------------------------------------- */ + +/* If pszAccess is read-only, the fpSHX field of the returned structure */ +/* will be NULL as it is not necessary to keep the SHX file open */ +SHPHandle SHPAPI_CALL + SHPOpen( const char * pszShapeFile, const char * pszAccess ); +SHPHandle SHPAPI_CALL + SHPOpenLL( const char *pszShapeFile, const char *pszAccess, + SAHooks *psHooks ); +SHPHandle SHPAPI_CALL + SHPCreate( const char * pszShapeFile, int nShapeType ); +SHPHandle SHPAPI_CALL + SHPCreateLL( const char * pszShapeFile, int nShapeType, + SAHooks *psHooks ); +void SHPAPI_CALL + SHPGetInfo( SHPHandle hSHP, int * pnEntities, int * pnShapeType, + double * padfMinBound, double * padfMaxBound ); + +SHPObject SHPAPI_CALL1(*) + SHPReadObject( SHPHandle hSHP, int iShape ); +int SHPAPI_CALL + SHPWriteObject( SHPHandle hSHP, int iShape, SHPObject * psObject ); + +void SHPAPI_CALL + SHPDestroyObject( SHPObject * psObject ); +void SHPAPI_CALL + SHPComputeExtents( SHPObject * psObject ); +SHPObject SHPAPI_CALL1(*) + SHPCreateObject( int nSHPType, int nShapeId, int nParts, + const int * panPartStart, const int * panPartType, + int nVertices, + const double * padfX, const double * padfY, + const double * padfZ, const double * padfM ); +SHPObject SHPAPI_CALL1(*) + SHPCreateSimpleObject( int nSHPType, int nVertices, + const double * padfX, + const double * padfY, + const double * padfZ ); + +int SHPAPI_CALL + SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ); + +void SHPAPI_CALL SHPClose( SHPHandle hSHP ); +void SHPAPI_CALL SHPWriteHeader( SHPHandle hSHP ); + +const char SHPAPI_CALL1(*) + SHPTypeName( int nSHPType ); +const char SHPAPI_CALL1(*) + SHPPartTypeName( int nPartType ); + +/* -------------------------------------------------------------------- */ +/* Shape quadtree indexing API. */ +/* -------------------------------------------------------------------- */ + +/* this can be two or four for binary or quad tree */ +#define MAX_SUBNODE 4 + +/* upper limit of tree levels for automatic estimation */ +#define MAX_DEFAULT_TREE_DEPTH 12 + +typedef struct shape_tree_node +{ + /* region covered by this node */ + double adfBoundsMin[4]; + double adfBoundsMax[4]; + + /* list of shapes stored at this node. The papsShapeObj pointers + or the whole list can be NULL */ + int nShapeCount; + int *panShapeIds; + SHPObject **papsShapeObj; + + int nSubNodes; + struct shape_tree_node *apsSubNode[MAX_SUBNODE]; + +} SHPTreeNode; + +typedef struct +{ + SHPHandle hSHP; + + int nMaxDepth; + int nDimension; + int nTotalCount; + + SHPTreeNode *psRoot; +} SHPTree; + +SHPTree SHPAPI_CALL1(*) + SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, + double *padfBoundsMin, double *padfBoundsMax ); +void SHPAPI_CALL + SHPDestroyTree( SHPTree * hTree ); + +int SHPAPI_CALL + SHPWriteTree( SHPTree *hTree, const char * pszFilename ); +SHPTree SHPAPI_CALL + SHPReadTree( const char * pszFilename ); + +int SHPAPI_CALL + SHPTreeAddObject( SHPTree * hTree, SHPObject * psObject ); +int SHPAPI_CALL + SHPTreeAddShapeId( SHPTree * hTree, SHPObject * psObject ); +int SHPAPI_CALL + SHPTreeRemoveShapeId( SHPTree * hTree, int nShapeId ); + +void SHPAPI_CALL + SHPTreeTrimExtraNodes( SHPTree * hTree ); + +int SHPAPI_CALL1(*) + SHPTreeFindLikelyShapes( SHPTree * hTree, + double * padfBoundsMin, + double * padfBoundsMax, + int * ); +int SHPAPI_CALL + SHPCheckBoundsOverlap( double *, double *, double *, double *, int ); + +int SHPAPI_CALL1(*) +SHPSearchDiskTree( FILE *fp, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ); + +/************************************************************************/ +/* DBF Support. */ +/************************************************************************/ +typedef struct +{ + SAHooks sHooks; + + SAFile fp; + + int nRecords; + + int nRecordLength; + int nHeaderLength; + int nFields; + int *panFieldOffset; + int *panFieldSize; + int *panFieldDecimals; + char *pachFieldType; + + char *pszHeader; + + int nCurrentRecord; + int bCurrentRecordModified; + char *pszCurrentRecord; + + int nWorkFieldLength; + char *pszWorkField; + + int bNoHeader; + int bUpdated; + + double dfDoubleField; + + int iLanguageDriver; + char *pszCodePage; +} DBFInfo; + +typedef DBFInfo * DBFHandle; + +typedef enum { + FTString, + FTInteger, + FTDouble, + FTLogical, + FTInvalid +} DBFFieldType; + +#define XBASE_FLDHDR_SZ 32 + + +DBFHandle SHPAPI_CALL + DBFOpen( const char * pszDBFFile, const char * pszAccess ); +DBFHandle SHPAPI_CALL + DBFOpenLL( const char * pszDBFFile, const char * pszAccess, + SAHooks *psHooks ); +DBFHandle SHPAPI_CALL + DBFCreate( const char * pszDBFFile ); +DBFHandle SHPAPI_CALL + DBFCreateEx( const char * pszDBFFile, const char * pszCodePage ); +DBFHandle SHPAPI_CALL + DBFCreateLL( const char * pszDBFFile, const char * pszCodePage, SAHooks *psHooks ); + +int SHPAPI_CALL + DBFGetFieldCount( DBFHandle psDBF ); +int SHPAPI_CALL + DBFGetRecordCount( DBFHandle psDBF ); +int SHPAPI_CALL + DBFAddField( DBFHandle hDBF, const char * pszFieldName, + DBFFieldType eType, int nWidth, int nDecimals ); + +int SHPAPI_CALL + DBFAddNativeFieldType( DBFHandle hDBF, const char * pszFieldName, + char chType, int nWidth, int nDecimals ); + +int SHPAPI_CALL + DBFDeleteField( DBFHandle hDBF, int iField ); + +DBFFieldType SHPAPI_CALL + DBFGetFieldInfo( DBFHandle psDBF, int iField, + char * pszFieldName, int * pnWidth, int * pnDecimals ); + +int SHPAPI_CALL + DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName); + +int SHPAPI_CALL + DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField ); +double SHPAPI_CALL + DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField ); +const char SHPAPI_CALL1(*) + DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField ); +const char SHPAPI_CALL1(*) + DBFReadLogicalAttribute( DBFHandle hDBF, int iShape, int iField ); +int SHPAPI_CALL + DBFIsAttributeNULL( DBFHandle hDBF, int iShape, int iField ); + +int SHPAPI_CALL + DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, + int nFieldValue ); +int SHPAPI_CALL + DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField, + double dFieldValue ); +int SHPAPI_CALL + DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField, + const char * pszFieldValue ); +int SHPAPI_CALL + DBFWriteNULLAttribute( DBFHandle hDBF, int iShape, int iField ); + +int SHPAPI_CALL + DBFWriteLogicalAttribute( DBFHandle hDBF, int iShape, int iField, + const char lFieldValue); +int SHPAPI_CALL + DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, + void * pValue ); +const char SHPAPI_CALL1(*) + DBFReadTuple(DBFHandle psDBF, int hEntity ); +int SHPAPI_CALL + DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ); + +int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape ); +int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, + int bIsDeleted ); + +DBFHandle SHPAPI_CALL + DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ); + +void SHPAPI_CALL + DBFClose( DBFHandle hDBF ); +void SHPAPI_CALL + DBFUpdateHeader( DBFHandle hDBF ); +char SHPAPI_CALL + DBFGetNativeFieldType( DBFHandle hDBF, int iField ); + +const char SHPAPI_CALL1(*) + DBFGetCodePage(DBFHandle psDBF ); + +#ifdef __cplusplus +} +#endif + +#endif /* ndef SHAPEFILE_H_INCLUDED */ diff --git a/navit/map/shapefile/shapefile.c b/navit/map/shapefile/shapefile.c new file mode 100644 index 0000000..f2180ef --- /dev/null +++ b/navit/map/shapefile/shapefile.c @@ -0,0 +1,378 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 Navit Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include "config.h" +#include "debug.h" +#include "plugin.h" +#include "projection.h" +#include "item.h" +#include "map.h" +#include "maptype.h" +#include "attr.h" +#include "transform.h" +#include "file.h" +#include "shapefil.h" + + +struct map_priv { + int id; + char *filename; + char *charset; + SHPHandle hSHP; + DBFHandle hDBF; + int nShapeType, nEntities, nFields; + double adfMinBound[4], adfMaxBound[4]; +}; + + +struct map_rect_priv { + struct map_selection *sel; + struct map_priv *m; + struct item item; + int idx; + int cidx; + int aidx; + enum attr_type anext; + SHPObject *psShape; + char *str; +}; + +static void +map_destroy_shapefile(struct map_priv *m) +{ + dbg(1,"map_destroy_shapefile\n"); + g_free(m); +} + +static void +shapefile_coord_rewind(void *priv_data) +{ + struct map_rect_priv *mr=priv_data; + mr->cidx=0; +} + +static int +shapefile_coord_get(void *priv_data, struct coord *c, int count) +{ + struct map_rect_priv *mr=priv_data; + int ret=0; + int idx; + struct coord_geo g; + SHPObject *psShape=mr->psShape; + while (count) { + idx=mr->cidx; + if (idx >= psShape->nVertices) + break; + g.lng=psShape->padfX[idx]; + g.lat=psShape->padfY[idx]; + transform_from_geo(projection_mg, &g, c); + mr->cidx++; + ret++; + c++; + count--; + } + return ret; +} + +static void +shapefile_attr_rewind(void *priv_data) +{ + struct map_rect_priv *mr=priv_data; + mr->aidx=0; + mr->anext=attr_debug; +} + +static int +shapefile_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct map_rect_priv *mr=priv_data; + struct map_priv *m=mr->m; + char szTitle[12],*pszTypeName,*str; + int ret, nWidth, nDecimals; + + attr->type=attr_type; + switch (attr_type) { + case attr_any: + while (mr->anext != attr_none) { + ret=shapefile_attr_get(priv_data, mr->anext, attr); + if (ret) + return ret; + } + return 0; + case attr_debug: + if (mr->aidx >= m->nFields) { + mr->anext=attr_none; + return 0; + } + switch (DBFGetFieldInfo(m->hDBF, mr->aidx, szTitle, &nWidth, &nDecimals )) { + case FTString: + pszTypeName = "String"; + str=g_strdup(DBFReadStringAttribute( m->hDBF, mr->item.id_lo, mr->aidx )); + break; + case FTInteger: + pszTypeName = "Integer"; + str=g_strdup_printf("%d",DBFReadIntegerAttribute( m->hDBF, mr->item.id_lo, mr->aidx )); + break; + case FTDouble: + pszTypeName = "Double"; + str=g_strdup_printf("%lf",DBFReadDoubleAttribute( m->hDBF, mr->item.id_lo, mr->aidx )); + break; + case FTInvalid: + pszTypeName = "Invalid"; + str=NULL; + break; + default: + pszTypeName = "Unknown"; + str=NULL; + } + g_free(mr->str); + mr->str=attr->u.str=g_strdup_printf("%s=%s(%s)",szTitle,str,pszTypeName); + g_free(str); + mr->aidx++; + return 1; + default: + return 0; + } +} + +static struct item_methods methods_shapefile = { + shapefile_coord_rewind, + shapefile_coord_get, + shapefile_attr_rewind, + shapefile_attr_get, +}; + +static struct map_rect_priv * +map_rect_new_shapefile(struct map_priv *map, struct map_selection *sel) +{ + struct map_rect_priv *mr; + + dbg(1,"map_rect_new_shapefile\n"); + mr=g_new0(struct map_rect_priv, 1); + mr->m=map; + mr->idx=0; + mr->item.id_lo=0; + mr->item.id_hi=0; + mr->item.meth=&methods_shapefile; + mr->item.priv_data=mr; + return mr; +} + + +static void +map_rect_destroy_shapefile(struct map_rect_priv *mr) +{ + if (mr->psShape) + SHPDestroyObject(mr->psShape); + g_free(mr->str); + g_free(mr); +} + +static struct item * +map_rect_get_item_shapefile(struct map_rect_priv *mr) +{ + struct map_priv *m=mr->m; + if (mr->idx >= m->nEntities) + return NULL; + mr->item.type=type_street_unkn; + mr->item.id_lo=mr->idx; + if (mr->psShape) + SHPDestroyObject(mr->psShape); + mr->psShape=SHPReadObject(m->hSHP, mr->idx); + mr->idx++; + shapefile_coord_rewind(mr); + shapefile_attr_rewind(mr); + return &mr->item; +} + +static struct item * +map_rect_get_item_byid_shapefile(struct map_rect_priv *mr, int id_hi, int id_lo) +{ + mr->idx=id_lo; + return map_rect_get_item_shapefile(mr); +} + +static struct map_methods map_methods_shapefile = { + projection_mg, + "iso8859-1", + map_destroy_shapefile, + map_rect_new_shapefile, + map_rect_destroy_shapefile, + map_rect_get_item_shapefile, + map_rect_get_item_byid_shapefile, +}; + +static struct map_priv * +map_new_shapefile(struct map_methods *meth, struct attr **attrs) +{ + struct map_priv *m; + struct attr *data=attr_search(attrs, NULL, attr_data); + struct attr *charset=attr_search(attrs, NULL, attr_charset); + struct file_wordexp *wexp; + char *wdata; + char **wexp_data; + char *shapefile,*dbffile; + if (! data) + return NULL; + dbg(1,"map_new_shapefile %s\n", data->u.str); + wdata=g_strdup(data->u.str); + wexp=file_wordexp_new(wdata); + wexp_data=file_wordexp_get_array(wexp); + *meth=map_methods_shapefile; + + m=g_new0(struct map_priv, 1); + m->filename=g_strdup(wexp_data[0]); + shapefile=g_strdup_printf("%s.shp", m->filename); + m->hSHP=SHPOpen(shapefile, "rb" ); + SHPGetInfo( m->hSHP, &m->nEntities, &m->nShapeType, m->adfMinBound, m->adfMaxBound ); + g_free(shapefile); + dbffile=g_strdup_printf("%s.dbf", m->filename); + m->hDBF=DBFOpen(dbffile, "rb"); + m->nFields=DBFGetFieldCount(m->hDBF); + g_free(dbffile); + dbg(1,"map_new_shapefile %s %s\n", m->filename, wdata); + if (charset) { + m->charset=g_strdup(charset->u.str); + meth->charset=m->charset; + } + file_wordexp_destroy(wexp); + return m; +} + +void +plugin_init(void) +{ + dbg(1,"shapefile: plugin_init\n"); + plugin_register_map_type("shapefile", map_new_shapefile); +} + +/************************************************************************/ +/* VSI_SHP_Open() */ +/************************************************************************/ + +static SAFile VSI_SHP_Open( const char *pszFilename, const char *pszAccess ) + +{ + return (SAFile)fopen(pszFilename, pszAccess); +} + +/************************************************************************/ +/* VSI_SHP_Read() */ +/************************************************************************/ + +static SAOffset VSI_SHP_Read( void *p, SAOffset size, SAOffset nmemb, SAFile file ) + +{ + return fread(p, size, nmemb, (FILE *)file); +} + +/************************************************************************/ +/* VSI_SHP_Write() */ +/************************************************************************/ + +static SAOffset VSI_SHP_Write( void *p, SAOffset size, SAOffset nmemb, SAFile file ) + +{ + return fwrite(p, size, nmemb, (FILE *)file); +} + +/************************************************************************/ +/* VSI_SHP_Seek() */ +/************************************************************************/ + +static SAOffset VSI_SHP_Seek( SAFile file, SAOffset offset, int whence ) + +{ + return fseek((FILE *)file, offset, whence); +} + +/************************************************************************/ +/* VSI_SHP_Tell() */ +/************************************************************************/ + +static SAOffset VSI_SHP_Tell( SAFile file ) + +{ + return ftell((FILE *)file); +} + + +static int VSI_SHP_Flush( SAFile file ) + +{ + return fflush((FILE *)file); +} + +/************************************************************************/ +/* VSI_SHP_Close() */ +/************************************************************************/ + +static int VSI_SHP_Close( SAFile file ) + +{ + return fclose((FILE *)file); +} + +/************************************************************************/ +/* SADError() */ +/************************************************************************/ + +static void VSI_SHP_Error( const char *message ) + +{ + dbg(0,"error:%s\n", message); +} + +/************************************************************************/ +/* VSI_SHP_Remove() */ +/************************************************************************/ + +static int VSI_SHP_Remove( const char *pszFilename ) + +{ + return unlink(pszFilename); +} + +/************************************************************************/ +/* SASetupDefaultHooks() */ +/************************************************************************/ + +void SASetupDefaultHooks( SAHooks *psHooks ) + +{ + psHooks->FOpen = VSI_SHP_Open; + psHooks->FRead = VSI_SHP_Read; + psHooks->FWrite = VSI_SHP_Write; + psHooks->FSeek = VSI_SHP_Seek; + psHooks->FTell = VSI_SHP_Tell; + psHooks->FFlush = VSI_SHP_Flush; + psHooks->FClose = VSI_SHP_Close; + + psHooks->Remove = VSI_SHP_Remove; + psHooks->Atof = atof; + + psHooks->Error = VSI_SHP_Error; +} + + diff --git a/navit/map/shapefile/shpopen.c b/navit/map/shapefile/shpopen.c new file mode 100644 index 0000000..ba0ae84 --- /dev/null +++ b/navit/map/shapefile/shpopen.c @@ -0,0 +1,2284 @@ +/****************************************************************************** + * $Id: shpopen.c,v 1.59 2008/03/14 05:25:31 fwarmerdam Exp $ + * + * Project: Shapelib + * Purpose: Implementation of core Shapefile read/write functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * 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 or substantial portions of the 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. + ****************************************************************************** + * + * $Log: shpopen.c,v $ + * Revision 1.59 2008/03/14 05:25:31 fwarmerdam + * Correct crash on buggy geometries (gdal #2218) + * + * Revision 1.58 2008/01/08 23:28:26 bram + * on line 2095, use a float instead of a double to avoid a compiler warning + * + * Revision 1.57 2007/12/06 07:00:25 fwarmerdam + * dbfopen now using SAHooks for fileio + * + * Revision 1.56 2007/12/04 20:37:56 fwarmerdam + * preliminary implementation of hooks api for io and errors + * + * Revision 1.55 2007/11/21 22:39:56 fwarmerdam + * close shx file in readonly mode (GDAL #1956) + * + * Revision 1.54 2007/11/15 00:12:47 mloskot + * Backported recent changes from GDAL (Ticket #1415) to Shapelib. + * + * Revision 1.53 2007/11/14 22:31:08 fwarmerdam + * checks after mallocs to detect for corrupted/voluntary broken shapefiles. + * http://trac.osgeo.org/gdal/ticket/1991 + * + * Revision 1.52 2007/06/21 15:58:33 fwarmerdam + * fix for SHPRewindObject when rings touch at one vertex (gdal #976) + * + * Revision 1.51 2006/09/04 15:24:01 fwarmerdam + * Fixed up log message for 1.49. + * + * Revision 1.50 2006/09/04 15:21:39 fwarmerdam + * fix of last fix + * + * Revision 1.49 2006/09/04 15:21:00 fwarmerdam + * MLoskot: Added stronger test of Shapefile reading failures, e.g. truncated + * files. The problem was discovered by Tim Sutton and reported here + * https://svn.qgis.org/trac/ticket/200 + * + * Revision 1.48 2006/01/26 15:07:32 fwarmerdam + * add bMeasureIsUsed flag from Craig Bruce: Bug 1249 + * + * Revision 1.47 2006/01/04 20:07:23 fwarmerdam + * In SHPWriteObject() make sure that the record length is updated + * when rewriting an existing record. + * + * Revision 1.46 2005/02/11 17:17:46 fwarmerdam + * added panPartStart[0] validation + * + * Revision 1.45 2004/09/26 20:09:48 fwarmerdam + * const correctness changes + * + * Revision 1.44 2003/12/29 00:18:39 fwarmerdam + * added error checking for failed IO and optional CPL error reporting + * + * Revision 1.43 2003/12/01 16:20:08 warmerda + * be careful of zero vertex shapes + * + * Revision 1.42 2003/12/01 14:58:27 warmerda + * added degenerate object check in SHPRewindObject() + * + * Revision 1.41 2003/07/08 15:22:43 warmerda + * avoid warning + * + * Revision 1.40 2003/04/21 18:30:37 warmerda + * added header write/update public methods + * + * Revision 1.39 2002/08/26 06:46:56 warmerda + * avoid c++ comments + * + * Revision 1.38 2002/05/07 16:43:39 warmerda + * Removed debugging printf. + * + * Revision 1.37 2002/04/10 17:35:22 warmerda + * fixed bug in ring reversal code + * + * Revision 1.36 2002/04/10 16:59:54 warmerda + * added SHPRewindObject + * + * Revision 1.35 2001/12/07 15:10:44 warmerda + * fix if .shx fails to open + * + * Revision 1.34 2001/11/01 16:29:55 warmerda + * move pabyRec into SHPInfo for thread safety + * + * Revision 1.33 2001/07/03 12:18:15 warmerda + * Improved cleanup if SHX not found, provied by Riccardo Cohen. + * + * Revision 1.32 2001/06/22 01:58:07 warmerda + * be more careful about establishing initial bounds in face of NULL shapes + * + * Revision 1.31 2001/05/31 19:35:29 warmerda + * added support for writing null shapes + * + * Revision 1.30 2001/05/28 12:46:29 warmerda + * Add some checking on reasonableness of record count when opening. + * + * Revision 1.29 2001/05/23 13:36:52 warmerda + * added use of SHPAPI_CALL + * + * Revision 1.28 2001/02/06 22:25:06 warmerda + * fixed memory leaks when SHPOpen() fails + * + * Revision 1.27 2000/07/18 15:21:33 warmerda + * added better enforcement of -1 for append in SHPWriteObject + * + * Revision 1.26 2000/02/16 16:03:51 warmerda + * added null shape support + * + * Revision 1.25 1999/12/15 13:47:07 warmerda + * Fixed record size settings in .shp file (was 4 words too long) + * Added stdlib.h. + * + * Revision 1.24 1999/11/05 14:12:04 warmerda + * updated license terms + * + * Revision 1.23 1999/07/27 00:53:46 warmerda + * added support for rewriting shapes + * + * Revision 1.22 1999/06/11 19:19:11 warmerda + * Cleanup pabyRec static buffer on SHPClose(). + * + * Revision 1.21 1999/06/02 14:57:56 kshih + * Remove unused variables + * + * Revision 1.20 1999/04/19 21:04:17 warmerda + * Fixed syntax error. + * + * Revision 1.19 1999/04/19 21:01:57 warmerda + * Force access string to binary in SHPOpen(). + * + * Revision 1.18 1999/04/01 18:48:07 warmerda + * Try upper case extensions if lower case doesn't work. + * + * Revision 1.17 1998/12/31 15:29:39 warmerda + * Disable writing measure values to multipatch objects if + * DISABLE_MULTIPATCH_MEASURE is defined. + * + * Revision 1.16 1998/12/16 05:14:33 warmerda + * Added support to write MULTIPATCH. Fixed reading Z coordinate of + * MULTIPATCH. Fixed record size written for all feature types. + * + * Revision 1.15 1998/12/03 16:35:29 warmerda + * r+b is proper binary access string, not rb+. + * + * Revision 1.14 1998/12/03 15:47:56 warmerda + * Fixed setting of nVertices in SHPCreateObject(). + * + * Revision 1.13 1998/12/03 15:33:54 warmerda + * Made SHPCalculateExtents() separately callable. + * + * Revision 1.12 1998/11/11 20:01:50 warmerda + * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines. + * + * Revision 1.11 1998/11/09 20:56:44 warmerda + * Fixed up handling of file wide bounds. + * + * Revision 1.10 1998/11/09 20:18:51 warmerda + * Converted to support 3D shapefiles, and use of SHPObject. + * + * Revision 1.9 1998/02/24 15:09:05 warmerda + * Fixed memory leak. + * + * Revision 1.8 1997/12/04 15:40:29 warmerda + * Fixed byte swapping of record number, and record length fields in the + * .shp file. + * + * Revision 1.7 1995/10/21 03:15:58 warmerda + * Added support for binary file access, the magic cookie 9997 + * and tried to improve the int32 selection logic for 16bit systems. + * + * Revision 1.6 1995/09/04 04:19:41 warmerda + * Added fix for file bounds. + * + * Revision 1.5 1995/08/25 15:16:44 warmerda + * Fixed a couple of problems with big endian systems ... one with bounds + * and the other with multipart polygons. + * + * Revision 1.4 1995/08/24 18:10:17 warmerda + * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc() + * functions (such as on the Sun). + * + * Revision 1.3 1995/08/23 02:23:15 warmerda + * Added support for reading bounds, and fixed up problems in setting the + * file wide bounds. + * + * Revision 1.2 1995/08/04 03:16:57 warmerda + * Added header. + * + */ + +#include "shapefil.h" + +#include +#include +#include +#include +#include +#include + +SHP_CVSID("$Id: shpopen.c,v 1.59 2008/03/14 05:25:31 fwarmerdam Exp $") + +typedef unsigned char uchar; + +#if UINT_MAX == 65535 +typedef long int32; +#else +typedef int int32; +#endif + +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif + +#define ByteCopy( a, b, c ) memcpy( b, a, c ) +#ifndef MAX +# define MIN(a,b) ((ab) ? a : b) +#endif + +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; + } +} + +/************************************************************************/ +/* 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) ); +} + +/************************************************************************/ +/* SHPWriteHeader() */ +/* */ +/* Write out a header for the .shp and .shx files as well as the */ +/* contents of the index (.shx) file. */ +/************************************************************************/ + +void SHPWriteHeader( SHPHandle psSHP ) + +{ + uchar abyHeader[100]; + int i; + int32 i32; + double dValue; + int32 *panSHX; + + if (psSHP->fpSHX == NULL) + { + psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed"); + return; + } + +/* -------------------------------------------------------------------- */ +/* 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. */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0 + || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHP ) != 1 ) + { + psSHP->sHooks.Error( "Failure writing .shp header" ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Prepare, and write .shx file header. */ +/* -------------------------------------------------------------------- */ + i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0 + || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 ) + { + psSHP->sHooks.Error( "Failure writing .shx header" ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Write out the .shx contents. */ +/* -------------------------------------------------------------------- */ + panSHX = (int32 *) malloc(sizeof(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 ) SwapWord( 4, panSHX+i*2 ); + if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 ); + } + + if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX ) + != psSHP->nRecords ) + { + psSHP->sHooks.Error( "Failure writing .shx contents" ); + } + + free( panSHX ); + +/* -------------------------------------------------------------------- */ +/* Flush to disk. */ +/* -------------------------------------------------------------------- */ + psSHP->sHooks.FFlush( psSHP->fpSHP ); + psSHP->sHooks.FFlush( psSHP->fpSHX ); +} + +/************************************************************************/ +/* SHPOpen() */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPOpen( const char * pszLayer, const char * pszAccess ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return SHPOpenLL( pszLayer, pszAccess, &sHooks ); +} + +/************************************************************************/ +/* SHPOpen() */ +/* */ +/* Open the .shp and .shx files based on the basename of the */ +/* files or either file name. */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) + +{ + char *pszFullname, *pszBasename; + SHPHandle psSHP; + + uchar *pabyBuf; + int i; + double dValue; + +/* -------------------------------------------------------------------- */ +/* 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 = FALSE; + else + bBigEndian = TRUE; + +/* -------------------------------------------------------------------- */ +/* Initialize the info structure. */ +/* -------------------------------------------------------------------- */ + psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1); + + psSHP->bUpdated = FALSE; + memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) ); + +/* -------------------------------------------------------------------- */ +/* 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); + sprintf( pszFullname, "%s.shp", pszBasename ) ; + psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + if( psSHP->fpSHP == NULL ) + { + sprintf( pszFullname, "%s.SHP", pszBasename ); + psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + } + + if( psSHP->fpSHP == NULL ) + { +#ifdef USE_CPL + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open %s.shp or %s.SHP.", + pszBasename, pszBasename ); +#endif + free( psSHP ); + free( pszBasename ); + free( pszFullname ); + return( NULL ); + } + + sprintf( pszFullname, "%s.shx", pszBasename ); + psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + if( psSHP->fpSHX == NULL ) + { + sprintf( pszFullname, "%s.SHX", pszBasename ); + psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + } + + if( psSHP->fpSHX == NULL ) + { +#ifdef USE_CPL + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open %s.shx or %s.SHX.", + pszBasename, pszBasename ); +#endif + psSHP->sHooks.FClose( psSHP->fpSHP ); + free( psSHP ); + free( pszBasename ); + free( pszFullname ); + return( NULL ); + } + + free( pszFullname ); + free( pszBasename ); + +/* -------------------------------------------------------------------- */ +/* Read the file size from the SHP file. */ +/* -------------------------------------------------------------------- */ + pabyBuf = (uchar *) malloc(100); + psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHP ); + + psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256 + + pabyBuf[25] * 256 * 256 + + pabyBuf[26] * 256 + + pabyBuf[27]) * 2; + +/* -------------------------------------------------------------------- */ +/* Read SHX file Header info */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1 + || pabyBuf[0] != 0 + || pabyBuf[1] != 0 + || pabyBuf[2] != 0x27 + || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) ) + { + psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + free( psSHP ); + + return( NULL ); + } + + psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256 + + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256; + psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8; + + psSHP->nShapeType = pabyBuf[32]; + + if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 ) + { + char szError[200]; + + sprintf( szError, + "Record count in .shp header is %d, which seems\n" + "unreasonable. Assuming header is corrupt.", + psSHP->nRecords ); + psSHP->sHooks.Error( szError ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + free( psSHP ); + free(pabyBuf); + + return( NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Read the bounds. */ +/* -------------------------------------------------------------------- */ + if( bBigEndian ) SwapWord( 8, pabyBuf+36 ); + memcpy( &dValue, pabyBuf+36, 8 ); + psSHP->adBoundsMin[0] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+44 ); + memcpy( &dValue, pabyBuf+44, 8 ); + psSHP->adBoundsMin[1] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+52 ); + memcpy( &dValue, pabyBuf+52, 8 ); + psSHP->adBoundsMax[0] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+60 ); + memcpy( &dValue, pabyBuf+60, 8 ); + psSHP->adBoundsMax[1] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */ + memcpy( &dValue, pabyBuf+68, 8 ); + psSHP->adBoundsMin[2] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+76 ); + memcpy( &dValue, pabyBuf+76, 8 ); + psSHP->adBoundsMax[2] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */ + memcpy( &dValue, pabyBuf+84, 8 ); + psSHP->adBoundsMin[3] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+92 ); + memcpy( &dValue, pabyBuf+92, 8 ); + psSHP->adBoundsMax[3] = dValue; + + free( pabyBuf ); + +/* -------------------------------------------------------------------- */ +/* Read the .shx file to get the offsets to each record in */ +/* the .shp file. */ +/* -------------------------------------------------------------------- */ + psSHP->nMaxRecords = psSHP->nRecords; + + psSHP->panRecOffset = + (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); + psSHP->panRecSize = + (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); + pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) ); + + if (psSHP->panRecOffset == NULL || + psSHP->panRecSize == NULL || + pabyBuf == NULL) + { + char szError[200]; + + sprintf(szError, + "Not enough memory to allocate requested memory (nRecords=%d).\n" + "Probably broken SHP file", + psSHP->nRecords ); + psSHP->sHooks.Error( szError ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + if (psSHP->panRecOffset) free( psSHP->panRecOffset ); + if (psSHP->panRecSize) free( psSHP->panRecSize ); + if (pabyBuf) free( pabyBuf ); + free( psSHP ); + return( NULL ); + } + + if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ) + != psSHP->nRecords ) + { + char szError[200]; + + sprintf( szError, + "Failed to read all values for %d records in .shx file.", + psSHP->nRecords ); + psSHP->sHooks.Error( szError ); + + /* SHX is short or unreadable for some reason. */ + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + free( psSHP->panRecOffset ); + free( psSHP->panRecSize ); + free( pabyBuf ); + free( psSHP ); + + return( NULL ); + } + + /* In read-only mode, we can close the SHX now */ + if (strcmp(pszAccess, "rb") == 0) + { + psSHP->sHooks.FClose( psSHP->fpSHX ); + psSHP->fpSHX = NULL; + } + + for( i = 0; i < psSHP->nRecords; i++ ) + { + int32 nOffset, nLength; + + memcpy( &nOffset, pabyBuf + i * 8, 4 ); + if( !bBigEndian ) SwapWord( 4, &nOffset ); + + memcpy( &nLength, pabyBuf + i * 8 + 4, 4 ); + if( !bBigEndian ) SwapWord( 4, &nLength ); + + psSHP->panRecOffset[i] = nOffset*2; + psSHP->panRecSize[i] = nLength*2; + } + free( pabyBuf ); + + return( psSHP ); +} + +/************************************************************************/ +/* SHPClose() */ +/* */ +/* Close the .shp and .shx files. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPClose(SHPHandle psSHP ) + +{ + if( psSHP == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Update the header if we have modified anything. */ +/* -------------------------------------------------------------------- */ + if( psSHP->bUpdated ) + SHPWriteHeader( psSHP ); + +/* -------------------------------------------------------------------- */ +/* Free all resources, and close files. */ +/* -------------------------------------------------------------------- */ + free( psSHP->panRecOffset ); + free( psSHP->panRecSize ); + + if ( psSHP->fpSHX != NULL) + psSHP->sHooks.FClose( psSHP->fpSHX ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + + if( psSHP->pabyRec != NULL ) + { + free( psSHP->pabyRec ); + } + + free( psSHP ); +} + +/************************************************************************/ +/* SHPGetInfo() */ +/* */ +/* Fetch general information about the shape file. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, + double * padfMinBound, double * padfMaxBound ) + +{ + int i; + + if( psSHP == NULL ) + return; + + if( pnEntities != NULL ) + *pnEntities = psSHP->nRecords; + + if( pnShapeType != NULL ) + *pnShapeType = psSHP->nShapeType; + + for( i = 0; i < 4; i++ ) + { + if( padfMinBound != NULL ) + padfMinBound[i] = psSHP->adBoundsMin[i]; + if( padfMaxBound != NULL ) + padfMaxBound[i] = psSHP->adBoundsMax[i]; + } +} + +/************************************************************************/ +/* SHPCreate() */ +/* */ +/* Create a new shape file and return a handle to the open */ +/* shape file with read/write access. */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPCreate( const char * pszLayer, int nShapeType ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return SHPCreateLL( pszLayer, nShapeType, &sHooks ); +} + +/************************************************************************/ +/* SHPCreate() */ +/* */ +/* Create a new shape file and return a handle to the open */ +/* shape file with read/write access. */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) + +{ + char *pszBasename, *pszFullname; + int i; + SAFile fpSHP, fpSHX; + uchar abyHeader[100]; + int32 i32; + double dValue; + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this system. */ +/* -------------------------------------------------------------------- */ + i = 1; + if( *((uchar *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = 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 = psHooks->FOpen(pszFullname, "wb" ); + if( fpSHP == NULL ) + { + psHooks->Error( "Failed to create file .shp file." ); + return( NULL ); + } + + sprintf( pszFullname, "%s.shx", pszBasename ); + fpSHX = psHooks->FOpen(pszFullname, "wb" ); + if( fpSHX == NULL ) + { + psHooks->Error( "Failed to create file .shx file." ); + return( NULL ); + } + + free( pszFullname ); + free( pszBasename ); + +/* -------------------------------------------------------------------- */ +/* 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. */ +/* -------------------------------------------------------------------- */ + if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 ) + { + psHooks->Error( "Failed to write .shp header." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Prepare, and write .shx file header. */ +/* -------------------------------------------------------------------- */ + i32 = 50; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 ) + { + psHooks->Error( "Failed to write .shx header." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Close the files, and then open them as regular existing files. */ +/* -------------------------------------------------------------------- */ + psHooks->FClose( fpSHP ); + psHooks->FClose( fpSHX ); + + return( SHPOpenLL( pszLayer, "r+b", psHooks ) ); +} + +/************************************************************************/ +/* _SHPSetBounds() */ +/* */ +/* Compute a bounds rectangle for a shape, and set it into the */ +/* indicated location in the record. */ +/************************************************************************/ + +static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape ) + +{ + ByteCopy( &(psShape->dfXMin), pabyRec + 0, 8 ); + ByteCopy( &(psShape->dfYMin), pabyRec + 8, 8 ); + ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 ); + ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 ); + + if( bBigEndian ) + { + SwapWord( 8, pabyRec + 0 ); + SwapWord( 8, pabyRec + 8 ); + SwapWord( 8, pabyRec + 16 ); + SwapWord( 8, pabyRec + 24 ); + } +} + +/************************************************************************/ +/* SHPComputeExtents() */ +/* */ +/* Recompute the extents of a shape. Automatically done by */ +/* SHPCreateObject(). */ +/************************************************************************/ + +void SHPAPI_CALL +SHPComputeExtents( SHPObject * psObject ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Build extents for this object. */ +/* -------------------------------------------------------------------- */ + if( psObject->nVertices > 0 ) + { + psObject->dfXMin = psObject->dfXMax = psObject->padfX[0]; + psObject->dfYMin = psObject->dfYMax = psObject->padfY[0]; + psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0]; + psObject->dfMMin = psObject->dfMMax = psObject->padfM[0]; + } + + for( i = 0; i < psObject->nVertices; i++ ) + { + psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]); + psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]); + psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]); + psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]); + + psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]); + psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]); + psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]); + psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]); + } +} + +/************************************************************************/ +/* SHPCreateObject() */ +/* */ +/* Create a shape object. It should be freed with */ +/* SHPDestroyObject(). */ +/************************************************************************/ + +SHPObject SHPAPI_CALL1(*) +SHPCreateObject( int nSHPType, int nShapeId, int nParts, + const int * panPartStart, const int * panPartType, + int nVertices, const double *padfX, const double *padfY, + const double * padfZ, const double * padfM ) + +{ + SHPObject *psObject; + int i, bHasM, bHasZ; + + psObject = (SHPObject *) calloc(1,sizeof(SHPObject)); + psObject->nSHPType = nSHPType; + psObject->nShapeId = nShapeId; + psObject->bMeasureIsUsed = FALSE; + +/* -------------------------------------------------------------------- */ +/* Establish whether this shape type has M, and Z values. */ +/* -------------------------------------------------------------------- */ + if( nSHPType == SHPT_ARCM + || nSHPType == SHPT_POINTM + || nSHPType == SHPT_POLYGONM + || nSHPType == SHPT_MULTIPOINTM ) + { + bHasM = TRUE; + bHasZ = FALSE; + } + else if( nSHPType == SHPT_ARCZ + || nSHPType == SHPT_POINTZ + || nSHPType == SHPT_POLYGONZ + || nSHPType == SHPT_MULTIPOINTZ + || nSHPType == SHPT_MULTIPATCH ) + { + bHasM = TRUE; + bHasZ = TRUE; + } + else + { + bHasM = FALSE; + bHasZ = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Capture parts. Note that part type is optional, and */ +/* defaults to ring. */ +/* -------------------------------------------------------------------- */ + if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON + || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM + || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ + || nSHPType == SHPT_MULTIPATCH ) + { + psObject->nParts = MAX(1,nParts); + + psObject->panPartStart = (int *) + malloc(sizeof(int) * psObject->nParts); + psObject->panPartType = (int *) + malloc(sizeof(int) * psObject->nParts); + + psObject->panPartStart[0] = 0; + psObject->panPartType[0] = SHPP_RING; + + for( i = 0; i < nParts; i++ ) + { + psObject->panPartStart[i] = panPartStart[i]; + + if( panPartType != NULL ) + psObject->panPartType[i] = panPartType[i]; + else + psObject->panPartType[i] = SHPP_RING; + } + + if( psObject->panPartStart[0] != 0 ) + psObject->panPartStart[0] = 0; + } + +/* -------------------------------------------------------------------- */ +/* Capture vertices. Note that Z and M are optional, but X and */ +/* Y are not. */ +/* -------------------------------------------------------------------- */ + if( nVertices > 0 ) + { + psObject->padfX = (double *) calloc(sizeof(double),nVertices); + psObject->padfY = (double *) calloc(sizeof(double),nVertices); + psObject->padfZ = (double *) calloc(sizeof(double),nVertices); + psObject->padfM = (double *) calloc(sizeof(double),nVertices); + + assert( padfX != NULL ); + assert( padfY != NULL ); + + for( i = 0; i < nVertices; i++ ) + { + psObject->padfX[i] = padfX[i]; + psObject->padfY[i] = padfY[i]; + if( padfZ != NULL && bHasZ ) + psObject->padfZ[i] = padfZ[i]; + if( padfM != NULL && bHasM ) + psObject->padfM[i] = padfM[i]; + } + if( padfM != NULL && bHasM ) + psObject->bMeasureIsUsed = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Compute the extents. */ +/* -------------------------------------------------------------------- */ + psObject->nVertices = nVertices; + SHPComputeExtents( psObject ); + + return( psObject ); +} + +/************************************************************************/ +/* SHPCreateSimpleObject() */ +/* */ +/* Create a simple (common) shape object. Destroy with */ +/* SHPDestroyObject(). */ +/************************************************************************/ + +SHPObject SHPAPI_CALL1(*) +SHPCreateSimpleObject( int nSHPType, int nVertices, + const double * padfX, const double * padfY, + const double * padfZ ) + +{ + return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL, + nVertices, padfX, padfY, padfZ, NULL ) ); +} + +/************************************************************************/ +/* SHPWriteObject() */ +/* */ +/* Write out the vertices of a new structure. Note that it is */ +/* only possible to write vertices at the end of the file. */ +/************************************************************************/ + +int SHPAPI_CALL +SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) + +{ + int nRecordOffset, i, nRecordSize=0; + uchar *pabyRec; + int32 i32; + + psSHP->bUpdated = TRUE; + +/* -------------------------------------------------------------------- */ +/* Ensure that shape object matches the type of the file it is */ +/* being written to. */ +/* -------------------------------------------------------------------- */ + assert( psObject->nSHPType == psSHP->nShapeType + || psObject->nSHPType == SHPT_NULL ); + +/* -------------------------------------------------------------------- */ +/* Ensure that -1 is used for appends. Either blow an */ +/* assertion, or if they are disabled, set the shapeid to -1 */ +/* for appends. */ +/* -------------------------------------------------------------------- */ + assert( nShapeId == -1 + || (nShapeId >= 0 && nShapeId < psSHP->nRecords) ); + + if( nShapeId != -1 && nShapeId >= psSHP->nRecords ) + nShapeId = -1; + +/* -------------------------------------------------------------------- */ +/* Add the new entity to the in memory index. */ +/* -------------------------------------------------------------------- */ + if( nShapeId == -1 && psSHP->nRecords+1 > 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 ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize record. */ +/* -------------------------------------------------------------------- */ + pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) + + psObject->nParts * 8 + 128); + +/* -------------------------------------------------------------------- */ +/* Extract vertices for a Polygon or Arc. */ +/* -------------------------------------------------------------------- */ + if( psObject->nSHPType == SHPT_POLYGON + || psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_POLYGONM + || psObject->nSHPType == SHPT_ARC + || psObject->nSHPType == SHPT_ARCZ + || psObject->nSHPType == SHPT_ARCM + || psObject->nSHPType == SHPT_MULTIPATCH ) + { + int32 nPoints, nParts; + int i; + + nPoints = psObject->nVertices; + nParts = psObject->nParts; + + _SHPSetBounds( pabyRec + 12, psObject ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + if( bBigEndian ) SwapWord( 4, &nParts ); + + ByteCopy( &nPoints, pabyRec + 40 + 8, 4 ); + ByteCopy( &nParts, pabyRec + 36 + 8, 4 ); + + nRecordSize = 52; + + /* + * Write part start positions. + */ + ByteCopy( psObject->panPartStart, pabyRec + 44 + 8, + 4 * psObject->nParts ); + for( i = 0; i < psObject->nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i ); + nRecordSize += 4; + } + + /* + * Write multipatch part types if needed. + */ + if( psObject->nSHPType == SHPT_MULTIPATCH ) + { + memcpy( pabyRec + nRecordSize, psObject->panPartType, + 4*psObject->nParts ); + for( i = 0; i < psObject->nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize ); + nRecordSize += 4; + } + } + + /* + * Write the (x,y) vertex values. + */ + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 ); + ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 ); + + if( bBigEndian ) + SwapWord( 8, pabyRec + nRecordSize ); + + if( bBigEndian ) + SwapWord( 8, pabyRec + nRecordSize + 8 ); + + nRecordSize += 2 * 8; + } + + /* + * Write the Z coordinates (if any). + */ + if( psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_ARCZ + || psObject->nSHPType == SHPT_MULTIPATCH ) + { + ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + + /* + * Write the M values, if any. + */ + if( psObject->bMeasureIsUsed + && (psObject->nSHPType == SHPT_POLYGONM + || psObject->nSHPType == SHPT_ARCM +#ifndef DISABLE_MULTIPATCH_MEASURE + || psObject->nSHPType == SHPT_MULTIPATCH +#endif + || psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_ARCZ) ) + { + ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Extract vertices for a MultiPoint. */ +/* -------------------------------------------------------------------- */ + else if( psObject->nSHPType == SHPT_MULTIPOINT + || psObject->nSHPType == SHPT_MULTIPOINTZ + || psObject->nSHPType == SHPT_MULTIPOINTM ) + { + int32 nPoints; + int i; + + nPoints = psObject->nVertices; + + _SHPSetBounds( pabyRec + 12, psObject ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + ByteCopy( &nPoints, pabyRec + 44, 4 ); + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 ); + ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 ); + if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 ); + } + + nRecordSize = 48 + 16 * psObject->nVertices; + + if( psObject->nSHPType == SHPT_MULTIPOINTZ ) + { + ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + + if( psObject->bMeasureIsUsed + && (psObject->nSHPType == SHPT_MULTIPOINTZ + || psObject->nSHPType == SHPT_MULTIPOINTM) ) + { + ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Write point. */ +/* -------------------------------------------------------------------- */ + else if( psObject->nSHPType == SHPT_POINT + || psObject->nSHPType == SHPT_POINTZ + || psObject->nSHPType == SHPT_POINTM ) + { + ByteCopy( psObject->padfX, pabyRec + 12, 8 ); + ByteCopy( psObject->padfY, pabyRec + 20, 8 ); + + if( bBigEndian ) SwapWord( 8, pabyRec + 12 ); + if( bBigEndian ) SwapWord( 8, pabyRec + 20 ); + + nRecordSize = 28; + + if( psObject->nSHPType == SHPT_POINTZ ) + { + ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + + if( psObject->bMeasureIsUsed + && (psObject->nSHPType == SHPT_POINTZ + || psObject->nSHPType == SHPT_POINTM) ) + { + ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + +/* -------------------------------------------------------------------- */ +/* Not much to do for null geometries. */ +/* -------------------------------------------------------------------- */ + else if( psObject->nSHPType == SHPT_NULL ) + { + nRecordSize = 12; + } + + else + { + /* unknown type */ + assert( FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Establish where we are going to put this record. If we are */ +/* rewriting and existing record, and it will fit, then put it */ +/* back where the original came from. Otherwise write at the end. */ +/* -------------------------------------------------------------------- */ + if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 ) + { + if( nShapeId == -1 ) + nShapeId = psSHP->nRecords++; + + psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize; + psSHP->panRecSize[nShapeId] = nRecordSize-8; + psSHP->nFileSize += nRecordSize; + } + else + { + nRecordOffset = psSHP->panRecOffset[nShapeId]; + psSHP->panRecSize[nShapeId] = nRecordSize-8; + } + +/* -------------------------------------------------------------------- */ +/* Set the shape type, record number, and record size. */ +/* -------------------------------------------------------------------- */ + i32 = nShapeId+1; /* record # */ + if( !bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec, 4 ); + + i32 = (nRecordSize-8)/2; /* record size */ + if( !bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec + 4, 4 ); + + i32 = psObject->nSHPType; /* shape type */ + if( bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec + 8, 4 ); + +/* -------------------------------------------------------------------- */ +/* Write out record. */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 + || psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 ) + { + psSHP->sHooks.Error( "Error in psSHP->sHooks.FSeek() or fwrite() writing object to .shp file." ); + free( pabyRec ); + return -1; + } + + free( pabyRec ); + +/* -------------------------------------------------------------------- */ +/* Expand file wide bounds based on this shape. */ +/* -------------------------------------------------------------------- */ + if( psSHP->adBoundsMin[0] == 0.0 + && psSHP->adBoundsMax[0] == 0.0 + && psSHP->adBoundsMin[1] == 0.0 + && psSHP->adBoundsMax[1] == 0.0 ) + { + if( psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0 ) + { + psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0; + psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0; + psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0; + psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0; + } + else + { + psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0]; + psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0]; + psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0]; + psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0]; + } + } + + for( i = 0; i < psObject->nVertices; i++ ) + { + psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]); + psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]); + psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]); + psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]); + psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]); + psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]); + psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]); + psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); + } + + return( nShapeId ); +} + +/************************************************************************/ +/* SHPReadObject() */ +/* */ +/* Read the vertices, parts, and other non-attribute information */ +/* for one shape. */ +/************************************************************************/ + +SHPObject SHPAPI_CALL1(*) +SHPReadObject( SHPHandle psSHP, int hEntity ) + +{ + int nEntitySize, nRequiredSize; + SHPObject *psShape; + char pszErrorMsg[128]; + +/* -------------------------------------------------------------------- */ +/* Validate the record/entity number. */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity >= psSHP->nRecords ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Ensure our record buffer is large enough. */ +/* -------------------------------------------------------------------- */ + nEntitySize = psSHP->panRecSize[hEntity]+8; + if( nEntitySize > psSHP->nBufSize ) + { + psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize); + if (psSHP->pabyRec == NULL) + { + char szError[200]; + + /* Reallocate previous successfull size for following features */ + psSHP->pabyRec = malloc(psSHP->nBufSize); + + sprintf( szError, + "Not enough memory to allocate requested memory (nBufSize=%d). " + "Probably broken SHP file", psSHP->nBufSize ); + psSHP->sHooks.Error( szError ); + return NULL; + } + + /* Only set new buffer size after successfull alloc */ + psSHP->nBufSize = nEntitySize; + } + + /* In case we were not able to reallocate the buffer on a previous step */ + if (psSHP->pabyRec == NULL) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the record. */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0 + || psSHP->sHooks.FRead( psSHP->pabyRec, nEntitySize, 1, + psSHP->fpSHP ) != 1 ) + { + /* + * TODO - mloskot: Consider detailed diagnostics of shape file, + * for example to detect if file is truncated. + */ + + psSHP->sHooks.Error( "Error in fseek() or fread() reading object from .shp file." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Allocate and minimally initialize the object. */ +/* -------------------------------------------------------------------- */ + psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); + psShape->nShapeId = hEntity; + psShape->bMeasureIsUsed = FALSE; + + if ( 8 + 4 > nEntitySize ) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 ); + + if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) ); + +/* ==================================================================== */ +/* Extract vertices for a Polygon or Arc. */ +/* ==================================================================== */ + if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC + || psShape->nSHPType == SHPT_POLYGONZ + || psShape->nSHPType == SHPT_POLYGONM + || psShape->nSHPType == SHPT_ARCZ + || psShape->nSHPType == SHPT_ARCM + || psShape->nSHPType == SHPT_MULTIPATCH ) + { + int32 nPoints, nParts; + int i, nOffset; + + if ( 40 + 8 + 4 > nEntitySize ) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Get the X/Y bounds. */ +/* -------------------------------------------------------------------- */ + memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); + memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); + memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); + memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); + +/* -------------------------------------------------------------------- */ +/* Extract part/point count, and build vertex and part arrays */ +/* to proper size. */ +/* -------------------------------------------------------------------- */ + memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 ); + memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + if( bBigEndian ) SwapWord( 4, &nParts ); + + if (nPoints < 0 || nParts < 0 || + nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.", + hEntity, nPoints, nParts); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + /* With the previous checks on nPoints and nParts, */ + /* we should not overflow here and after */ + /* since 50 M * (16 + 8 + 8) = 1 600 MB */ + nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints; + if ( psShape->nSHPType == SHPT_POLYGONZ + || psShape->nSHPType == SHPT_ARCZ + || psShape->nSHPType == SHPT_MULTIPATCH ) + { + nRequiredSize += 16 + 8 * nPoints; + } + if( psShape->nSHPType == SHPT_MULTIPATCH ) + { + nRequiredSize += 4 * nParts; + } + if (nRequiredSize > nEntitySize) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d, nEntitySize=%d.", + hEntity, nPoints, nParts, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + psShape->nVertices = nPoints; + psShape->padfX = (double *) calloc(nPoints,sizeof(double)); + psShape->padfY = (double *) calloc(nPoints,sizeof(double)); + psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); + psShape->padfM = (double *) calloc(nPoints,sizeof(double)); + + psShape->nParts = nParts; + psShape->panPartStart = (int *) calloc(nParts,sizeof(int)); + psShape->panPartType = (int *) calloc(nParts,sizeof(int)); + + if (psShape->padfX == NULL || + psShape->padfY == NULL || + psShape->padfZ == NULL || + psShape->padfM == NULL || + psShape->panPartStart == NULL || + psShape->panPartType == NULL) + { + snprintf(pszErrorMsg, 128, + "Not enough memory to allocate requested memory (nPoints=%d, nParts=%d) for shape %d. " + "Probably broken SHP file", hEntity, nPoints, nParts ); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + for( i = 0; i < nParts; i++ ) + psShape->panPartType[i] = SHPP_RING; + +/* -------------------------------------------------------------------- */ +/* Copy out the part array from the record. */ +/* -------------------------------------------------------------------- */ + memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts ); + for( i = 0; i < nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i ); + + /* We check that the offset is inside the vertex array */ + if (psShape->panPartStart[i] < 0 || + psShape->panPartStart[i] >= psShape->nVertices) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d", + hEntity, i, psShape->panPartStart[i], psShape->nVertices); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1]) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d", + hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + } + + nOffset = 44 + 8 + 4*nParts; + +/* -------------------------------------------------------------------- */ +/* If this is a multipatch, we will also have parts types. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_MULTIPATCH ) + { + memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts ); + for( i = 0; i < nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, psShape->panPartType+i ); + } + + nOffset += 4*nParts; + } + +/* -------------------------------------------------------------------- */ +/* Copy out the vertices from the record. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nPoints; i++ ) + { + memcpy(psShape->padfX + i, + psSHP->pabyRec + nOffset + i * 16, + 8 ); + + memcpy(psShape->padfY + i, + psSHP->pabyRec + nOffset + i * 16 + 8, + 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); + if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); + } + + nOffset += 16*nPoints; + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_POLYGONZ + || psShape->nSHPType == SHPT_ARCZ + || psShape->nSHPType == SHPT_MULTIPATCH ) + { + memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfZ + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); + } + + nOffset += 16 + 8*nPoints; + } + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( nEntitySize >= nOffset + 16 + 8*nPoints ) + { + memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfM + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); + } + psShape->bMeasureIsUsed = TRUE; + } + } + +/* ==================================================================== */ +/* Extract vertices for a MultiPoint. */ +/* ==================================================================== */ + else if( psShape->nSHPType == SHPT_MULTIPOINT + || psShape->nSHPType == SHPT_MULTIPOINTM + || psShape->nSHPType == SHPT_MULTIPOINTZ ) + { + int32 nPoints; + int i, nOffset; + + if ( 44 + 4 > nEntitySize ) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + memcpy( &nPoints, psSHP->pabyRec + 44, 4 ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + + if (nPoints < 0 || nPoints > 50 * 1000 * 1000) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d", + hEntity, nPoints); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + nRequiredSize = 48 + nPoints * 16; + if( psShape->nSHPType == SHPT_MULTIPOINTZ ) + { + nRequiredSize += 16 + nPoints * 8; + } + if (nRequiredSize > nEntitySize) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d", + hEntity, nPoints, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + psShape->nVertices = nPoints; + psShape->padfX = (double *) calloc(nPoints,sizeof(double)); + psShape->padfY = (double *) calloc(nPoints,sizeof(double)); + psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); + psShape->padfM = (double *) calloc(nPoints,sizeof(double)); + + if (psShape->padfX == NULL || + psShape->padfY == NULL || + psShape->padfZ == NULL || + psShape->padfM == NULL) + { + snprintf(pszErrorMsg, 128, + "Not enough memory to allocate requested memory (nPoints=%d) for shape %d. " + "Probably broken SHP file", hEntity, nPoints ); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + for( i = 0; i < nPoints; i++ ) + { + memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 ); + memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); + if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); + } + + nOffset = 48 + 16*nPoints; + +/* -------------------------------------------------------------------- */ +/* Get the X/Y bounds. */ +/* -------------------------------------------------------------------- */ + memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); + memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); + memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); + memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_MULTIPOINTZ ) + { + memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfZ + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); + } + + nOffset += 16 + 8*nPoints; + } + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( nEntitySize >= nOffset + 16 + 8*nPoints ) + { + memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfM + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); + } + psShape->bMeasureIsUsed = TRUE; + } + } + +/* ==================================================================== */ +/* Extract vertices for a point. */ +/* ==================================================================== */ + else if( psShape->nSHPType == SHPT_POINT + || psShape->nSHPType == SHPT_POINTM + || psShape->nSHPType == SHPT_POINTZ ) + { + int nOffset; + + psShape->nVertices = 1; + psShape->padfX = (double *) calloc(1,sizeof(double)); + psShape->padfY = (double *) calloc(1,sizeof(double)); + psShape->padfZ = (double *) calloc(1,sizeof(double)); + psShape->padfM = (double *) calloc(1,sizeof(double)); + + if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 ); + memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX ); + if( bBigEndian ) SwapWord( 8, psShape->padfY ); + + nOffset = 20 + 8; + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_POINTZ ) + { + memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfZ ); + + nOffset += 8; + } + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( nEntitySize >= nOffset + 8 ) + { + memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfM ); + psShape->bMeasureIsUsed = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Since no extents are supplied in the record, we will apply */ +/* them from the single vertex. */ +/* -------------------------------------------------------------------- */ + psShape->dfXMin = psShape->dfXMax = psShape->padfX[0]; + psShape->dfYMin = psShape->dfYMax = psShape->padfY[0]; + psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0]; + psShape->dfMMin = psShape->dfMMax = psShape->padfM[0]; + } + + return( psShape ); +} + +/************************************************************************/ +/* SHPTypeName() */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +SHPTypeName( int nSHPType ) + +{ + switch( nSHPType ) + { + case SHPT_NULL: + return "NullShape"; + + case SHPT_POINT: + return "Point"; + + case SHPT_ARC: + return "Arc"; + + case SHPT_POLYGON: + return "Polygon"; + + case SHPT_MULTIPOINT: + return "MultiPoint"; + + case SHPT_POINTZ: + return "PointZ"; + + case SHPT_ARCZ: + return "ArcZ"; + + case SHPT_POLYGONZ: + return "PolygonZ"; + + case SHPT_MULTIPOINTZ: + return "MultiPointZ"; + + case SHPT_POINTM: + return "PointM"; + + case SHPT_ARCM: + return "ArcM"; + + case SHPT_POLYGONM: + return "PolygonM"; + + case SHPT_MULTIPOINTM: + return "MultiPointM"; + + case SHPT_MULTIPATCH: + return "MultiPatch"; + + default: + return "UnknownShapeType"; + } +} + +/************************************************************************/ +/* SHPPartTypeName() */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +SHPPartTypeName( int nPartType ) + +{ + switch( nPartType ) + { + case SHPP_TRISTRIP: + return "TriangleStrip"; + + case SHPP_TRIFAN: + return "TriangleFan"; + + case SHPP_OUTERRING: + return "OuterRing"; + + case SHPP_INNERRING: + return "InnerRing"; + + case SHPP_FIRSTRING: + return "FirstRing"; + + case SHPP_RING: + return "Ring"; + + default: + return "UnknownPartType"; + } +} + +/************************************************************************/ +/* SHPDestroyObject() */ +/************************************************************************/ + +void SHPAPI_CALL +SHPDestroyObject( SHPObject * psShape ) + +{ + if( psShape == NULL ) + return; + + if( psShape->padfX != NULL ) + free( psShape->padfX ); + if( psShape->padfY != NULL ) + free( psShape->padfY ); + if( psShape->padfZ != NULL ) + free( psShape->padfZ ); + if( psShape->padfM != NULL ) + free( psShape->padfM ); + + if( psShape->panPartStart != NULL ) + free( psShape->panPartStart ); + if( psShape->panPartType != NULL ) + free( psShape->panPartType ); + + free( psShape ); +} + +/************************************************************************/ +/* SHPRewindObject() */ +/* */ +/* Reset the winding of polygon objects to adhere to the */ +/* specification. */ +/************************************************************************/ + +int SHPAPI_CALL +SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) + +{ + int iOpRing, bAltered = 0; + +/* -------------------------------------------------------------------- */ +/* Do nothing if this is not a polygon object. */ +/* -------------------------------------------------------------------- */ + if( psObject->nSHPType != SHPT_POLYGON + && psObject->nSHPType != SHPT_POLYGONZ + && psObject->nSHPType != SHPT_POLYGONM ) + return 0; + + if( psObject->nVertices == 0 || psObject->nParts == 0 ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Process each of the rings. */ +/* -------------------------------------------------------------------- */ + for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ ) + { + int bInner, iVert, nVertCount, nVertStart, iCheckRing; + double dfSum, dfTestX, dfTestY; + +/* -------------------------------------------------------------------- */ +/* Determine if this ring is an inner ring or an outer ring */ +/* relative to all the other rings. For now we assume the */ +/* first ring is outer and all others are inner, but eventually */ +/* we need to fix this to handle multiple island polygons and */ +/* unordered sets of rings. */ +/* */ +/* -------------------------------------------------------------------- */ + + /* Use point in the middle of segment to avoid testing + * common points of rings. + */ + dfTestX = ( psObject->padfX[psObject->panPartStart[iOpRing]] + + psObject->padfX[psObject->panPartStart[iOpRing] + 1] ) / 2; + dfTestY = ( psObject->padfY[psObject->panPartStart[iOpRing]] + + psObject->padfY[psObject->panPartStart[iOpRing] + 1] ) / 2; + + bInner = FALSE; + for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ ) + { + int iEdge; + + if( iCheckRing == iOpRing ) + continue; + + nVertStart = psObject->panPartStart[iCheckRing]; + + if( iCheckRing == psObject->nParts-1 ) + nVertCount = psObject->nVertices + - psObject->panPartStart[iCheckRing]; + else + nVertCount = psObject->panPartStart[iCheckRing+1] + - psObject->panPartStart[iCheckRing]; + + for( iEdge = 0; iEdge < nVertCount; iEdge++ ) + { + int iNext; + + if( iEdge < nVertCount-1 ) + iNext = iEdge+1; + else + iNext = 0; + + /* Rule #1: + * Test whether the edge 'straddles' the horizontal ray from the test point (dfTestY,dfTestY) + * The rule #1 also excludes edges collinear with the ray. + */ + if ( ( psObject->padfY[iEdge+nVertStart] < dfTestY + && dfTestY <= psObject->padfY[iNext+nVertStart] ) + || ( psObject->padfY[iNext+nVertStart] < dfTestY + && dfTestY <= psObject->padfY[iEdge+nVertStart] ) ) + { + /* Rule #2: + * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY) + */ + double const intersect = + ( psObject->padfX[iEdge+nVertStart] + + ( dfTestY - psObject->padfY[iEdge+nVertStart] ) + / ( psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart] ) + * ( psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart] ) ); + + if (intersect < dfTestX) + { + bInner = !bInner; + } + } + } + } /* for iCheckRing */ + +/* -------------------------------------------------------------------- */ +/* Determine the current order of this ring so we will know if */ +/* it has to be reversed. */ +/* -------------------------------------------------------------------- */ + nVertStart = psObject->panPartStart[iOpRing]; + + if( iOpRing == psObject->nParts-1 ) + nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing]; + else + nVertCount = psObject->panPartStart[iOpRing+1] + - psObject->panPartStart[iOpRing]; + + dfSum = 0.0; + for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ ) + { + dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1] + - psObject->padfY[iVert] * psObject->padfX[iVert+1]; + } + + dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart] + - psObject->padfY[iVert] * psObject->padfX[nVertStart]; + +/* -------------------------------------------------------------------- */ +/* Reverse if necessary. */ +/* -------------------------------------------------------------------- */ + if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) ) + { + int i; + + bAltered++; + for( i = 0; i < nVertCount/2; i++ ) + { + double dfSaved; + + /* Swap X */ + dfSaved = psObject->padfX[nVertStart+i]; + psObject->padfX[nVertStart+i] = + psObject->padfX[nVertStart+nVertCount-i-1]; + psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved; + + /* Swap Y */ + dfSaved = psObject->padfY[nVertStart+i]; + psObject->padfY[nVertStart+i] = + psObject->padfY[nVertStart+nVertCount-i-1]; + psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved; + + /* Swap Z */ + if( psObject->padfZ ) + { + dfSaved = psObject->padfZ[nVertStart+i]; + psObject->padfZ[nVertStart+i] = + psObject->padfZ[nVertStart+nVertCount-i-1]; + psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved; + } + + /* Swap M */ + if( psObject->padfM ) + { + dfSaved = psObject->padfM[nVertStart+i]; + psObject->padfM[nVertStart+i] = + psObject->padfM[nVertStart+nVertCount-i-1]; + psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved; + } + } + } + } + + return bAltered; +} diff --git a/navit/map/shapefile/shptree.c b/navit/map/shapefile/shptree.c new file mode 100644 index 0000000..d89a6cf --- /dev/null +++ b/navit/map/shapefile/shptree.c @@ -0,0 +1,1045 @@ +/****************************************************************************** + * $Id: shptree.c,v 1.12 2008/11/12 15:39:50 fwarmerdam Exp $ + * + * Project: Shapelib + * Purpose: Implementation of quadtree building and searching functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * 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 or substantial portions of the 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. + ****************************************************************************** + * + * $Log: shptree.c,v $ + * Revision 1.12 2008/11/12 15:39:50 fwarmerdam + * improve safety in face of buggy .shp file. + * + * Revision 1.11 2007/10/27 03:31:14 fwarmerdam + * limit default depth of tree to 12 levels (gdal ticket #1594) + * + * Revision 1.10 2005/01/03 22:30:13 fwarmerdam + * added support for saved quadtrees + * + * Revision 1.9 2003/01/28 15:53:41 warmerda + * Avoid build warnings. + * + * Revision 1.8 2002/05/07 13:07:45 warmerda + * use qsort() - patch from Bernhard Herzog + * + * Revision 1.7 2002/01/15 14:36:07 warmerda + * updated email address + * + * Revision 1.6 2001/05/23 13:36:52 warmerda + * added use of SHPAPI_CALL + * + * Revision 1.5 1999/11/05 14:12:05 warmerda + * updated license terms + * + * Revision 1.4 1999/06/02 18:24:21 warmerda + * added trimming code + * + * Revision 1.3 1999/06/02 17:56:12 warmerda + * added quad'' subnode support for trees + * + * Revision 1.2 1999/05/18 19:11:11 warmerda + * Added example searching capability + * + * Revision 1.1 1999/05/18 17:49:20 warmerda + * New + * + */ + +#include "shapefil.h" + +#include +#include +#include +#include +#ifdef USE_CPL +#include +#endif + +SHP_CVSID("$Id: shptree.c,v 1.12 2008/11/12 15:39:50 fwarmerdam Exp $") + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +static int bBigEndian = 0; + + +/* -------------------------------------------------------------------- */ +/* If the following is 0.5, nodes will be split in half. If it */ +/* is 0.6 then each subnode will contain 60% of the parent */ +/* node, with 20% representing overlap. This can be help to */ +/* prevent small objects on a boundary from shifting too high */ +/* up the tree. */ +/* -------------------------------------------------------------------- */ + +#define SHP_SPLIT_RATIO 0.55 + +/************************************************************************/ +/* 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) ); +} + +/************************************************************************/ +/* SHPTreeNodeInit() */ +/* */ +/* Initialize a tree node. */ +/************************************************************************/ + +static SHPTreeNode *SHPTreeNodeCreate( double * padfBoundsMin, + double * padfBoundsMax ) + +{ + SHPTreeNode *psTreeNode; + + psTreeNode = (SHPTreeNode *) malloc(sizeof(SHPTreeNode)); + if( NULL == psTreeNode ) + { +#ifdef USE_CPL + CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure"); +#endif + return NULL; + } + + psTreeNode->nShapeCount = 0; + psTreeNode->panShapeIds = NULL; + psTreeNode->papsShapeObj = NULL; + + psTreeNode->nSubNodes = 0; + + if( padfBoundsMin != NULL ) + memcpy( psTreeNode->adfBoundsMin, padfBoundsMin, sizeof(double) * 4 ); + + if( padfBoundsMax != NULL ) + memcpy( psTreeNode->adfBoundsMax, padfBoundsMax, sizeof(double) * 4 ); + + return psTreeNode; +} + + +/************************************************************************/ +/* SHPCreateTree() */ +/************************************************************************/ + +SHPTree SHPAPI_CALL1(*) +SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, + double *padfBoundsMin, double *padfBoundsMax ) + +{ + SHPTree *psTree; + + if( padfBoundsMin == NULL && hSHP == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Allocate the tree object */ +/* -------------------------------------------------------------------- */ + psTree = (SHPTree *) malloc(sizeof(SHPTree)); + if( NULL == psTree ) + { +#ifdef USE_CPL + CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure"); +#endif + return NULL; + } + + psTree->hSHP = hSHP; + psTree->nMaxDepth = nMaxDepth; + psTree->nDimension = nDimension; + psTree->nTotalCount = 0; + +/* -------------------------------------------------------------------- */ +/* If no max depth was defined, try to select a reasonable one */ +/* that implies approximately 8 shapes per node. */ +/* -------------------------------------------------------------------- */ + if( psTree->nMaxDepth == 0 && hSHP != NULL ) + { + int nMaxNodeCount = 1; + int nShapeCount; + + SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL ); + while( nMaxNodeCount*4 < nShapeCount ) + { + psTree->nMaxDepth += 1; + nMaxNodeCount = nMaxNodeCount * 2; + } + +#ifdef USE_CPL + CPLDebug( "Shape", + "Estimated spatial index tree depth: %d", + psTree->nMaxDepth ); +#endif + + /* NOTE: Due to problems with memory allocation for deep trees, + * automatically estimated depth is limited up to 12 levels. + * See Ticket #1594 for detailed discussion. + */ + if( psTree->nMaxDepth > MAX_DEFAULT_TREE_DEPTH ) + { + psTree->nMaxDepth = MAX_DEFAULT_TREE_DEPTH; + +#ifdef USE_CPL + CPLDebug( "Shape", + "Falling back to max number of allowed index tree levels (%d).", + MAX_DEFAULT_TREE_DEPTH ); +#endif + } + } + +/* -------------------------------------------------------------------- */ +/* Allocate the root node. */ +/* -------------------------------------------------------------------- */ + psTree->psRoot = SHPTreeNodeCreate( padfBoundsMin, padfBoundsMax ); + if( NULL == psTree->psRoot ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Assign the bounds to the root node. If none are passed in, */ +/* use the bounds of the provided file otherwise the create */ +/* function will have already set the bounds. */ +/* -------------------------------------------------------------------- */ + assert( NULL != psTree ); + assert( NULL != psTree->psRoot ); + + if( padfBoundsMin == NULL ) + { + SHPGetInfo( hSHP, NULL, NULL, + psTree->psRoot->adfBoundsMin, + psTree->psRoot->adfBoundsMax ); + } + +/* -------------------------------------------------------------------- */ +/* If we have a file, insert all it's shapes into the tree. */ +/* -------------------------------------------------------------------- */ + if( hSHP != NULL ) + { + int iShape, nShapeCount; + + SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL ); + + for( iShape = 0; iShape < nShapeCount; iShape++ ) + { + SHPObject *psShape; + + psShape = SHPReadObject( hSHP, iShape ); + if( psShape != NULL ) + { + SHPTreeAddShapeId( psTree, psShape ); + SHPDestroyObject( psShape ); + } + } + } + + return psTree; +} + +/************************************************************************/ +/* SHPDestroyTreeNode() */ +/************************************************************************/ + +static void SHPDestroyTreeNode( SHPTreeNode * psTreeNode ) + +{ + int i; + + assert( NULL != psTreeNode ); + + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( psTreeNode->apsSubNode[i] != NULL ) + SHPDestroyTreeNode( psTreeNode->apsSubNode[i] ); + } + + if( psTreeNode->panShapeIds != NULL ) + free( psTreeNode->panShapeIds ); + + if( psTreeNode->papsShapeObj != NULL ) + { + for( i = 0; i < psTreeNode->nShapeCount; i++ ) + { + if( psTreeNode->papsShapeObj[i] != NULL ) + SHPDestroyObject( psTreeNode->papsShapeObj[i] ); + } + + free( psTreeNode->papsShapeObj ); + } + + free( psTreeNode ); +} + +/************************************************************************/ +/* SHPDestroyTree() */ +/************************************************************************/ + +void SHPAPI_CALL +SHPDestroyTree( SHPTree * psTree ) + +{ + SHPDestroyTreeNode( psTree->psRoot ); + free( psTree ); +} + +/************************************************************************/ +/* SHPCheckBoundsOverlap() */ +/* */ +/* Do the given boxes overlap at all? */ +/************************************************************************/ + +int SHPAPI_CALL +SHPCheckBoundsOverlap( double * padfBox1Min, double * padfBox1Max, + double * padfBox2Min, double * padfBox2Max, + int nDimension ) + +{ + int iDim; + + for( iDim = 0; iDim < nDimension; iDim++ ) + { + if( padfBox2Max[iDim] < padfBox1Min[iDim] ) + return FALSE; + + if( padfBox1Max[iDim] < padfBox2Min[iDim] ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* SHPCheckObjectContained() */ +/* */ +/* Does the given shape fit within the indicated extents? */ +/************************************************************************/ + +static int SHPCheckObjectContained( SHPObject * psObject, int nDimension, + double * padfBoundsMin, double * padfBoundsMax ) + +{ + if( psObject->dfXMin < padfBoundsMin[0] + || psObject->dfXMax > padfBoundsMax[0] ) + return FALSE; + + if( psObject->dfYMin < padfBoundsMin[1] + || psObject->dfYMax > padfBoundsMax[1] ) + return FALSE; + + if( nDimension == 2 ) + return TRUE; + + if( psObject->dfZMin < padfBoundsMin[2] + || psObject->dfZMax < padfBoundsMax[2] ) + return FALSE; + + if( nDimension == 3 ) + return TRUE; + + if( psObject->dfMMin < padfBoundsMin[3] + || psObject->dfMMax < padfBoundsMax[3] ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* SHPTreeSplitBounds() */ +/* */ +/* Split a region into two subregion evenly, cutting along the */ +/* longest dimension. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeSplitBounds( double *padfBoundsMinIn, double *padfBoundsMaxIn, + double *padfBoundsMin1, double * padfBoundsMax1, + double *padfBoundsMin2, double * padfBoundsMax2 ) + +{ +/* -------------------------------------------------------------------- */ +/* The output bounds will be very similar to the input bounds, */ +/* so just copy over to start. */ +/* -------------------------------------------------------------------- */ + memcpy( padfBoundsMin1, padfBoundsMinIn, sizeof(double) * 4 ); + memcpy( padfBoundsMax1, padfBoundsMaxIn, sizeof(double) * 4 ); + memcpy( padfBoundsMin2, padfBoundsMinIn, sizeof(double) * 4 ); + memcpy( padfBoundsMax2, padfBoundsMaxIn, sizeof(double) * 4 ); + +/* -------------------------------------------------------------------- */ +/* Split in X direction. */ +/* -------------------------------------------------------------------- */ + if( (padfBoundsMaxIn[0] - padfBoundsMinIn[0]) + > (padfBoundsMaxIn[1] - padfBoundsMinIn[1]) ) + { + double dfRange = padfBoundsMaxIn[0] - padfBoundsMinIn[0]; + + padfBoundsMax1[0] = padfBoundsMinIn[0] + dfRange * SHP_SPLIT_RATIO; + padfBoundsMin2[0] = padfBoundsMaxIn[0] - dfRange * SHP_SPLIT_RATIO; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise split in Y direction. */ +/* -------------------------------------------------------------------- */ + else + { + double dfRange = padfBoundsMaxIn[1] - padfBoundsMinIn[1]; + + padfBoundsMax1[1] = padfBoundsMinIn[1] + dfRange * SHP_SPLIT_RATIO; + padfBoundsMin2[1] = padfBoundsMaxIn[1] - dfRange * SHP_SPLIT_RATIO; + } +} + +/************************************************************************/ +/* SHPTreeNodeAddShapeId() */ +/************************************************************************/ + +static int +SHPTreeNodeAddShapeId( SHPTreeNode * psTreeNode, SHPObject * psObject, + int nMaxDepth, int nDimension ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* If there are subnodes, then consider wiether this object */ +/* will fit in them. */ +/* -------------------------------------------------------------------- */ + if( nMaxDepth > 1 && psTreeNode->nSubNodes > 0 ) + { + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( SHPCheckObjectContained(psObject, nDimension, + psTreeNode->apsSubNode[i]->adfBoundsMin, + psTreeNode->apsSubNode[i]->adfBoundsMax)) + { + return SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[i], + psObject, nMaxDepth-1, + nDimension ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise, consider creating four subnodes if could fit into */ +/* them, and adding to the appropriate subnode. */ +/* -------------------------------------------------------------------- */ +#if MAX_SUBNODE == 4 + else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 ) + { + double adfBoundsMinH1[4], adfBoundsMaxH1[4]; + double adfBoundsMinH2[4], adfBoundsMaxH2[4]; + double adfBoundsMin1[4], adfBoundsMax1[4]; + double adfBoundsMin2[4], adfBoundsMax2[4]; + double adfBoundsMin3[4], adfBoundsMax3[4]; + double adfBoundsMin4[4], adfBoundsMax4[4]; + + SHPTreeSplitBounds( psTreeNode->adfBoundsMin, + psTreeNode->adfBoundsMax, + adfBoundsMinH1, adfBoundsMaxH1, + adfBoundsMinH2, adfBoundsMaxH2 ); + + SHPTreeSplitBounds( adfBoundsMinH1, adfBoundsMaxH1, + adfBoundsMin1, adfBoundsMax1, + adfBoundsMin2, adfBoundsMax2 ); + + SHPTreeSplitBounds( adfBoundsMinH2, adfBoundsMaxH2, + adfBoundsMin3, adfBoundsMax3, + adfBoundsMin4, adfBoundsMax4 ); + + if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin1, adfBoundsMax1) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin2, adfBoundsMax2) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin3, adfBoundsMax3) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin4, adfBoundsMax4) ) + { + psTreeNode->nSubNodes = 4; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + psTreeNode->apsSubNode[2] = SHPTreeNodeCreate( adfBoundsMin3, + adfBoundsMax3 ); + psTreeNode->apsSubNode[3] = SHPTreeNodeCreate( adfBoundsMin4, + adfBoundsMax4 ); + + /* recurse back on this node now that it has subnodes */ + return( SHPTreeNodeAddShapeId( psTreeNode, psObject, + nMaxDepth, nDimension ) ); + } + } +#endif /* MAX_SUBNODE == 4 */ + +/* -------------------------------------------------------------------- */ +/* Otherwise, consider creating two subnodes if could fit into */ +/* them, and adding to the appropriate subnode. */ +/* -------------------------------------------------------------------- */ +#if MAX_SUBNODE == 2 + else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 ) + { + double adfBoundsMin1[4], adfBoundsMax1[4]; + double adfBoundsMin2[4], adfBoundsMax2[4]; + + SHPTreeSplitBounds( psTreeNode->adfBoundsMin, psTreeNode->adfBoundsMax, + adfBoundsMin1, adfBoundsMax1, + adfBoundsMin2, adfBoundsMax2 ); + + if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin1, adfBoundsMax1)) + { + psTreeNode->nSubNodes = 2; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + + return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[0], psObject, + nMaxDepth - 1, nDimension ) ); + } + else if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin2, adfBoundsMax2) ) + { + psTreeNode->nSubNodes = 2; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + + return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[1], psObject, + nMaxDepth - 1, nDimension ) ); + } + } +#endif /* MAX_SUBNODE == 2 */ + +/* -------------------------------------------------------------------- */ +/* If none of that worked, just add it to this nodes list. */ +/* -------------------------------------------------------------------- */ + psTreeNode->nShapeCount++; + + psTreeNode->panShapeIds = (int *) + SfRealloc( psTreeNode->panShapeIds, + sizeof(int) * psTreeNode->nShapeCount ); + psTreeNode->panShapeIds[psTreeNode->nShapeCount-1] = psObject->nShapeId; + + if( psTreeNode->papsShapeObj != NULL ) + { + psTreeNode->papsShapeObj = (SHPObject **) + SfRealloc( psTreeNode->papsShapeObj, + sizeof(void *) * psTreeNode->nShapeCount ); + psTreeNode->papsShapeObj[psTreeNode->nShapeCount-1] = NULL; + } + + return TRUE; +} + +/************************************************************************/ +/* SHPTreeAddShapeId() */ +/* */ +/* Add a shape to the tree, but don't keep a pointer to the */ +/* object data, just keep the shapeid. */ +/************************************************************************/ + +int SHPAPI_CALL +SHPTreeAddShapeId( SHPTree * psTree, SHPObject * psObject ) + +{ + psTree->nTotalCount++; + + return( SHPTreeNodeAddShapeId( psTree->psRoot, psObject, + psTree->nMaxDepth, psTree->nDimension ) ); +} + +/************************************************************************/ +/* SHPTreeCollectShapesIds() */ +/* */ +/* Work function implementing SHPTreeFindLikelyShapes() on a */ +/* tree node by tree node basis. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeCollectShapeIds( SHPTree *hTree, SHPTreeNode * psTreeNode, + double * padfBoundsMin, double * padfBoundsMax, + int * pnShapeCount, int * pnMaxShapes, + int ** ppanShapeList ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Does this node overlap the area of interest at all? If not, */ +/* return without adding to the list at all. */ +/* -------------------------------------------------------------------- */ + if( !SHPCheckBoundsOverlap( psTreeNode->adfBoundsMin, + psTreeNode->adfBoundsMax, + padfBoundsMin, + padfBoundsMax, + hTree->nDimension ) ) + return; + +/* -------------------------------------------------------------------- */ +/* Grow the list to hold the shapes on this node. */ +/* -------------------------------------------------------------------- */ + if( *pnShapeCount + psTreeNode->nShapeCount > *pnMaxShapes ) + { + *pnMaxShapes = (*pnShapeCount + psTreeNode->nShapeCount) * 2 + 20; + *ppanShapeList = (int *) + SfRealloc(*ppanShapeList,sizeof(int) * *pnMaxShapes); + } + +/* -------------------------------------------------------------------- */ +/* Add the local nodes shapeids to the list. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nShapeCount; i++ ) + { + (*ppanShapeList)[(*pnShapeCount)++] = psTreeNode->panShapeIds[i]; + } + +/* -------------------------------------------------------------------- */ +/* Recurse to subnodes if they exist. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( psTreeNode->apsSubNode[i] != NULL ) + SHPTreeCollectShapeIds( hTree, psTreeNode->apsSubNode[i], + padfBoundsMin, padfBoundsMax, + pnShapeCount, pnMaxShapes, + ppanShapeList ); + } +} + +/************************************************************************/ +/* SHPTreeFindLikelyShapes() */ +/* */ +/* Find all shapes within tree nodes for which the tree node */ +/* bounding box overlaps the search box. The return value is */ +/* an array of shapeids terminated by a -1. The shapeids will */ +/* be in order, as hopefully this will result in faster (more */ +/* sequential) reading from the file. */ +/************************************************************************/ + +/* helper for qsort */ +static int +compare_ints( const void * a, const void * b) +{ + return (*(int*)a) - (*(int*)b); +} + +int SHPAPI_CALL1(*) +SHPTreeFindLikelyShapes( SHPTree * hTree, + double * padfBoundsMin, double * padfBoundsMax, + int * pnShapeCount ) + +{ + int *panShapeList=NULL, nMaxShapes = 0; + +/* -------------------------------------------------------------------- */ +/* Perform the search by recursive descent. */ +/* -------------------------------------------------------------------- */ + *pnShapeCount = 0; + + SHPTreeCollectShapeIds( hTree, hTree->psRoot, + padfBoundsMin, padfBoundsMax, + pnShapeCount, &nMaxShapes, + &panShapeList ); + +/* -------------------------------------------------------------------- */ +/* Sort the id array */ +/* -------------------------------------------------------------------- */ + + qsort(panShapeList, *pnShapeCount, sizeof(int), compare_ints); + + return panShapeList; +} + +/************************************************************************/ +/* SHPTreeNodeTrim() */ +/* */ +/* This is the recurve version of SHPTreeTrimExtraNodes() that */ +/* walks the tree cleaning it up. */ +/************************************************************************/ + +static int SHPTreeNodeTrim( SHPTreeNode * psTreeNode ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Trim subtrees, and free subnodes that come back empty. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( SHPTreeNodeTrim( psTreeNode->apsSubNode[i] ) ) + { + SHPDestroyTreeNode( psTreeNode->apsSubNode[i] ); + + psTreeNode->apsSubNode[i] = + psTreeNode->apsSubNode[psTreeNode->nSubNodes-1]; + + psTreeNode->nSubNodes--; + + i--; /* process the new occupant of this subnode entry */ + } + } + +/* -------------------------------------------------------------------- */ +/* We should be trimmed if we have no subnodes, and no shapes. */ +/* -------------------------------------------------------------------- */ + return( psTreeNode->nSubNodes == 0 && psTreeNode->nShapeCount == 0 ); +} + +/************************************************************************/ +/* SHPTreeTrimExtraNodes() */ +/* */ +/* Trim empty nodes from the tree. Note that we never trim an */ +/* empty root node. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeTrimExtraNodes( SHPTree * hTree ) + +{ + SHPTreeNodeTrim( hTree->psRoot ); +} + +/************************************************************************/ +/* SwapWord() */ +/* */ +/* Swap a 2, 4 or 8 byte word. */ +/************************************************************************/ + +static void SwapWord( int length, void * wordP ) + +{ + int i; + unsigned char temp; + + for( i=0; i < length/2; i++ ) + { + temp = ((unsigned char *) wordP)[i]; + ((unsigned char *)wordP)[i] = ((unsigned char *) wordP)[length-i-1]; + ((unsigned char *) wordP)[length-i-1] = temp; + } +} + +/************************************************************************/ +/* SHPSearchDiskTreeNode() */ +/************************************************************************/ + +static int +SHPSearchDiskTreeNode( FILE *fp, double *padfBoundsMin, double *padfBoundsMax, + int **ppanResultBuffer, int *pnBufferMax, + int *pnResultCount, int bNeedSwap ) + +{ + int i; + int offset; + int numshapes, numsubnodes; + double adfNodeBoundsMin[2], adfNodeBoundsMax[2]; + +/* -------------------------------------------------------------------- */ +/* Read and unswap first part of node info. */ +/* -------------------------------------------------------------------- */ + fread( &offset, 4, 1, fp ); + if ( bNeedSwap ) SwapWord ( 4, &offset ); + + fread( adfNodeBoundsMin, sizeof(double), 2, fp ); + fread( adfNodeBoundsMax, sizeof(double), 2, fp ); + if ( bNeedSwap ) + { + SwapWord( 8, adfNodeBoundsMin + 0 ); + SwapWord( 8, adfNodeBoundsMin + 1 ); + SwapWord( 8, adfNodeBoundsMax + 0 ); + SwapWord( 8, adfNodeBoundsMax + 1 ); + } + + fread( &numshapes, 4, 1, fp ); + if ( bNeedSwap ) SwapWord ( 4, &numshapes ); + +/* -------------------------------------------------------------------- */ +/* If we don't overlap this node at all, we can just fseek() */ +/* pass this node info and all subnodes. */ +/* -------------------------------------------------------------------- */ + if( !SHPCheckBoundsOverlap( adfNodeBoundsMin, adfNodeBoundsMax, + padfBoundsMin, padfBoundsMax, 2 ) ) + { + offset += numshapes*sizeof(int) + sizeof(int); + fseek(fp, offset, SEEK_CUR); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Add all the shapeids at this node to our list. */ +/* -------------------------------------------------------------------- */ + if(numshapes > 0) + { + if( *pnResultCount + numshapes > *pnBufferMax ) + { + *pnBufferMax = (int) ((*pnResultCount + numshapes + 100) * 1.25); + *ppanResultBuffer = (int *) + SfRealloc( *ppanResultBuffer, *pnBufferMax * sizeof(int) ); + } + + fread( *ppanResultBuffer + *pnResultCount, + sizeof(int), numshapes, fp ); + + if (bNeedSwap ) + { + for( i=0; inSubNodes; i++ ) + { + if(node->apsSubNode[i]) + { + offset += 4*sizeof(double) + + (node->apsSubNode[i]->nShapeCount+3)*sizeof(int); + offset += SHPGetSubNodeOffset(node->apsSubNode[i]); + } + } + + return(offset); +} + +/************************************************************************/ +/* SHPWriteTreeNode() */ +/************************************************************************/ + +static void SHPWriteTreeNode( FILE *fp, SHPTreeNode *node) +{ + int i,j; + int offset; + unsigned char *pabyRec = NULL; + assert( NULL != node ); + + offset = SHPGetSubNodeOffset(node); + + pabyRec = (unsigned char *) + malloc(sizeof(double) * 4 + + (3 * sizeof(int)) + (node->nShapeCount * sizeof(int)) ); + if( NULL == pabyRec ) + { +#ifdef USE_CPL + CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure"); +#endif + assert( 0 ); + } + assert( NULL != pabyRec ); + + memcpy( pabyRec, &offset, 4); + + /* minx, miny, maxx, maxy */ + memcpy( pabyRec+ 4, node->adfBoundsMin+0, sizeof(double) ); + memcpy( pabyRec+12, node->adfBoundsMin+1, sizeof(double) ); + memcpy( pabyRec+20, node->adfBoundsMax+0, sizeof(double) ); + memcpy( pabyRec+28, node->adfBoundsMax+1, sizeof(double) ); + + memcpy( pabyRec+36, &node->nShapeCount, 4); + j = node->nShapeCount * sizeof(int); + memcpy( pabyRec+40, node->panShapeIds, j); + memcpy( pabyRec+j+40, &node->nSubNodes, 4); + + fwrite( pabyRec, 44+j, 1, fp ); + free (pabyRec); + + for(i=0; inSubNodes; i++ ) + { + if(node->apsSubNode[i]) + SHPWriteTreeNode( fp, node->apsSubNode[i]); + } +} + +/************************************************************************/ +/* SHPWriteTree() */ +/************************************************************************/ + +int SHPWriteTree(SHPTree *tree, const char *filename ) +{ + char signature[4] = "SQT"; + int i; + char abyBuf[32]; + FILE *fp; + +/* -------------------------------------------------------------------- */ +/* Open the output file. */ +/* -------------------------------------------------------------------- */ + fp = fopen(filename, "wb"); + if( fp == NULL ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this machine. */ +/* -------------------------------------------------------------------- */ + i = 1; + if( *((unsigned char *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; + +/* -------------------------------------------------------------------- */ +/* Write the header. */ +/* -------------------------------------------------------------------- */ + memcpy( abyBuf+0, signature, 3 ); + + if( bBigEndian ) + abyBuf[3] = 2; /* New MSB */ + else + abyBuf[3] = 1; /* New LSB */ + + abyBuf[4] = 1; /* version */ + abyBuf[5] = 0; /* next 3 reserved */ + abyBuf[6] = 0; + abyBuf[7] = 0; + + fwrite( abyBuf, 8, 1, fp ); + + fwrite( &(tree->nTotalCount), 4, 1, fp ); + + /* write maxdepth */ + + fwrite( &(tree->nMaxDepth), 4, 1, fp ); + +/* -------------------------------------------------------------------- */ +/* Write all the nodes "in order". */ +/* -------------------------------------------------------------------- */ + + SHPWriteTreeNode( fp, tree->psRoot ); + + fclose( fp ); + + return TRUE; +} -- 1.7.9.5