--- /dev/null
+#include(../../CMake/Common.cmake)
+
+set(PROJECT_HEADERS
+ shapefil.h
+)
+
+set(PROJECT_SOURCES
+ dbfopen.c
+ safileio.c
+ shpopen.c
+ shptree.c
+ shputils.c
+)
+
+add_definitions(
+ -DSHAPELIB_DLLEXPORT
+)
+
+include_directories(
+)
+
+add_library(shapelib SHARED ${PROJECT_SOURCES} ${PROJECT_HEADERS})
+INSTALL(TARGETS shapelib EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_LIBS})
+
+set(PROJECT_LIBRARIES shapelib)
+
+#include(../../CMake/CommonInstall.cmake)
--- /dev/null
+/******************************************************************************
+ * $Id: dbfopen.c,v 1.89 2011-07-24 05:59:25 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.89 2011-07-24 05:59:25 fwarmerdam
+ * minimize use of CPLError in favor of SAHooks.Error()
+ *
+ * Revision 1.88 2011-05-13 17:35:17 fwarmerdam
+ * added DBFReorderFields() and DBFAlterFields() functions (from Even)
+ *
+ * Revision 1.87 2011-05-07 22:41:02 fwarmerdam
+ * ensure pending record is flushed when adding a native field (GDAL #4073)
+ *
+ * Revision 1.86 2011-04-17 15:15:29 fwarmerdam
+ * Removed unused variable.
+ *
+ * Revision 1.85 2010-12-06 16:09:34 fwarmerdam
+ * fix buffer read overrun fetching code page (bug 2276)
+ *
+ * Revision 1.84 2009-10-29 19:59:48 fwarmerdam
+ * avoid crash on truncated header (gdal #3093)
+ *
+ * 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/<number>") 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 <math.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+SHP_CVSID("$Id: dbfopen.c,v 1.89 2011-07-24 05:59:25 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 )
+ {
+ char szMessage[128];
+ sprintf( szMessage, "Failure writing DBF record %d.",
+ psDBF->nCurrentRecord );
+ psDBF->sHooks.Error( szMessage );
+ 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 )
+ {
+ char szMessage[128];
+ sprintf( szMessage, "fseek(%ld) failed on DBF file.\n",
+ (long) nRecordOffset );
+ psDBF->sHooks.Error( szMessage );
+ return FALSE;
+ }
+
+ if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord,
+ psDBF->nRecordLength, 1, psDBF->fp ) != 1 )
+ {
+ char szMessage[128];
+ sprintf( szMessage, "fread(%d) failed on DBF file.\n",
+ psDBF->nRecordLength );
+ psDBF->sHooks.Error( szMessage );
+ 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];
+
+ if (nHeadLen < 32)
+ {
+ psDBF->sHooks.FClose( psDBF->fp );
+ if( pfCPG ) psDBF->sHooks.FClose( pfCPG );
+ free( pabyBuf );
+ free( psDBF );
+ return NULL;
+ }
+
+ 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;
+ memset( pabyBuf, 0, nBufSize);
+ 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 );
+}
+
+/************************************************************************/
+/* DBFGetNullCharacter() */
+/************************************************************************/
+
+static char DBFGetNullCharacter(char chType)
+{
+ switch (chType)
+ {
+ case 'N':
+ case 'F':
+ return '*';
+ case 'D':
+ return '0';
+ case 'L':
+ return '?';
+ default:
+ return ' ';
+ }
+}
+
+/************************************************************************/
+/* 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;
+
+ /* make sure that everything is written in .dbf */
+ if( !DBFFlushRecord( psDBF ) )
+ return -1;
+
+/* -------------------------------------------------------------------- */
+/* 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);
+
+ chFieldFill = DBFGetNullCharacter(chType);
+
+ 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 );
+
+ psDBF->nCurrentRecord = -1;
+ psDBF->bCurrentRecordModified = FALSE;
+
+ 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' ) );
+}
+
+
+/************************************************************************/
+/* DBFIsValueNULL() */
+/* */
+/* Return TRUE if the passed string is NULL. */
+/************************************************************************/
+
+static int DBFIsValueNULL( char chType, const char* pszValue )
+{
+ int i;
+
+ if( pszValue == NULL )
+ return TRUE;
+
+ switch(chType)
+ {
+ 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;
+ }
+}
+
+/************************************************************************/
+/* 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;
+
+ pszValue = DBFReadStringAttribute( psDBF, iRecord, iField );
+
+ if( pszValue == NULL )
+ return TRUE;
+
+ return DBFIsValueNULL( psDBF->pachFieldType[iField], pszValue );
+}
+
+/************************************************************************/
+/* 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 )
+ {
+ memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]),
+ DBFGetNullCharacter(psDBF->pachFieldType[iField]),
+ psDBF->panFieldSize[iField] );
+ 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 */
+ memmove(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);
+
+ psDBF->nCurrentRecord = -1;
+ psDBF->bCurrentRecordModified = FALSE;
+
+ return TRUE;
+}
+
+/************************************************************************/
+/* DBFReorderFields() */
+/* */
+/* Reorder the fields of a .dbf file */
+/* */
+/* panMap must be exactly psDBF->nFields long and be a permutation */
+/* of [0, psDBF->nFields-1]. This assumption will not be asserted in the*/
+/* code of DBFReorderFields. */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFReorderFields( DBFHandle psDBF, int* panMap )
+{
+ SAOffset nRecordOffset;
+ int i, iRecord;
+ int *panFieldOffsetNew;
+ int *panFieldSizeNew;
+ int *panFieldDecimalsNew;
+ char *pachFieldTypeNew;
+ char *pszHeaderNew;
+ char *pszRecord;
+ char *pszRecordNew;
+
+ if ( psDBF->nFields == 0 )
+ return TRUE;
+
+ /* make sure that everything is written in .dbf */
+ if( !DBFFlushRecord( psDBF ) )
+ return FALSE;
+
+ panFieldOffsetNew = (int *) malloc(sizeof(int) * psDBF->nFields);
+ panFieldSizeNew = (int *) malloc(sizeof(int) * psDBF->nFields);
+ panFieldDecimalsNew = (int *) malloc(sizeof(int) * psDBF->nFields);
+ pachFieldTypeNew = (char *) malloc(sizeof(char) * psDBF->nFields);
+ pszHeaderNew = (char*) malloc(sizeof(char) * 32 * psDBF->nFields);
+
+ /* shuffle fields definitions */
+ for(i=0; i < psDBF->nFields; i++)
+ {
+ panFieldSizeNew[i] = psDBF->panFieldSize[panMap[i]];
+ panFieldDecimalsNew[i] = psDBF->panFieldDecimals[panMap[i]];
+ pachFieldTypeNew[i] = psDBF->pachFieldType[panMap[i]];
+ memcpy(pszHeaderNew + i * 32,
+ psDBF->pszHeader + panMap[i] * 32, 32);
+ }
+ panFieldOffsetNew[0] = 1;
+ for(i=1; i < psDBF->nFields; i++)
+ {
+ panFieldOffsetNew[i] = panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1];
+ }
+
+ free(psDBF->pszHeader);
+ psDBF->pszHeader = pszHeaderNew;
+
+ /* we're done if we're dealing with not yet created .dbf */
+ if ( !(psDBF->bNoHeader && psDBF->nRecords == 0) )
+ {
+ /* force update of header with new header and record length */
+ psDBF->bNoHeader = TRUE;
+ DBFUpdateHeader( psDBF );
+
+ /* alloc record */
+ pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
+ pszRecordNew = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
+
+ /* shuffle fields in records */
+ for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
+ {
+ nRecordOffset =
+ psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ /* load record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FRead( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
+
+ pszRecordNew[0] = pszRecord[0];
+
+ for(i=0; i < psDBF->nFields; i++)
+ {
+ memcpy(pszRecordNew + panFieldOffsetNew[i],
+ pszRecord + psDBF->panFieldOffset[panMap[i]],
+ psDBF->panFieldSize[panMap[i]]);
+ }
+
+ /* write record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FWrite( pszRecordNew, psDBF->nRecordLength, 1, psDBF->fp );
+ }
+
+ /* free record */
+ free(pszRecord);
+ free(pszRecordNew);
+ }
+
+ free(psDBF->panFieldOffset);
+ free(psDBF->panFieldSize);
+ free(psDBF->panFieldDecimals);
+ free(psDBF->pachFieldType);
+
+ psDBF->panFieldOffset = panFieldOffsetNew;
+ psDBF->panFieldSize = panFieldSizeNew;
+ psDBF->panFieldDecimals =panFieldDecimalsNew;
+ psDBF->pachFieldType = pachFieldTypeNew;
+
+ psDBF->nCurrentRecord = -1;
+ psDBF->bCurrentRecordModified = FALSE;
+
+ return TRUE;
+}
+
+
+/************************************************************************/
+/* DBFAlterFieldDefn() */
+/* */
+/* Alter a field definition in a .dbf file */
+/************************************************************************/
+
+int SHPAPI_CALL
+DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName,
+ char chType, int nWidth, int nDecimals )
+{
+ int i;
+ int iRecord;
+ int nOffset;
+ int nOldWidth;
+ int nOldRecordLength;
+ int nRecordOffset;
+ char* pszFInfo;
+ char chOldType;
+ int bIsNULL;
+ char chFieldFill;
+
+ if (iField < 0 || iField >= psDBF->nFields)
+ return FALSE;
+
+ /* make sure that everything is written in .dbf */
+ if( !DBFFlushRecord( psDBF ) )
+ return FALSE;
+
+ chFieldFill = DBFGetNullCharacter(chType);
+
+ chOldType = psDBF->pachFieldType[iField];
+ nOffset = psDBF->panFieldOffset[iField];
+ nOldWidth = psDBF->panFieldSize[iField];
+ nOldRecordLength = psDBF->nRecordLength;
+
+/* -------------------------------------------------------------------- */
+/* Do some checking to ensure we can add records to this file. */
+/* -------------------------------------------------------------------- */
+ if( nWidth < 1 )
+ return -1;
+
+ if( nWidth > 255 )
+ nWidth = 255;
+
+/* -------------------------------------------------------------------- */
+/* Assign the new field information fields. */
+/* -------------------------------------------------------------------- */
+ psDBF->panFieldSize[iField] = nWidth;
+ psDBF->panFieldDecimals[iField] = nDecimals;
+ psDBF->pachFieldType[iField] = chType;
+
+/* -------------------------------------------------------------------- */
+/* Update the header information. */
+/* -------------------------------------------------------------------- */
+ pszFInfo = psDBF->pszHeader + 32 * iField;
+
+ 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[iField];
+
+ 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;
+ }
+
+/* -------------------------------------------------------------------- */
+/* Update offsets */
+/* -------------------------------------------------------------------- */
+ if (nWidth != nOldWidth)
+ {
+ for (i = iField + 1; i < psDBF->nFields; i++)
+ psDBF->panFieldOffset[i] += nWidth - nOldWidth;
+ psDBF->nRecordLength += nWidth - nOldWidth;
+
+ 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 );
+
+ if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType))
+ {
+ char* pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength);
+ char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
+
+ pszOldField[nOldWidth] = 0;
+
+ /* move records to their new positions */
+ for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++)
+ {
+ nRecordOffset =
+ nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ /* load record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
+
+ memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
+ bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
+
+ if (nWidth != nOldWidth)
+ {
+ if ((chOldType == 'N' || chOldType == 'F') && pszOldField[0] == ' ')
+ {
+ /* Strip leading spaces when truncating a numeric field */
+ memmove( pszRecord + nOffset,
+ pszRecord + nOffset + nOldWidth - nWidth,
+ nWidth );
+ }
+ if (nOffset + nOldWidth < nOldRecordLength)
+ {
+ memmove( pszRecord + nOffset + nWidth,
+ pszRecord + nOffset + nOldWidth,
+ nOldRecordLength - (nOffset + nOldWidth));
+ }
+ }
+
+ /* Convert null value to the appropriate value of the new type */
+ if (bIsNULL)
+ {
+ memset( pszRecord + nOffset, chFieldFill, nWidth);
+ }
+
+ nRecordOffset =
+ psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ /* write record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
+ }
+
+ free(pszRecord);
+ free(pszOldField);
+ }
+ else if (nWidth > nOldWidth)
+ {
+ char* pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength);
+ char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1));
+
+ pszOldField[nOldWidth] = 0;
+
+ /* move records to their new positions */
+ for (iRecord = psDBF->nRecords - 1; iRecord >= 0; iRecord--)
+ {
+ nRecordOffset =
+ nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ /* load record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp );
+
+ memcpy(pszOldField, pszRecord + nOffset, nOldWidth);
+ bIsNULL = DBFIsValueNULL( chOldType, pszOldField );
+
+ if (nOffset + nOldWidth < nOldRecordLength)
+ {
+ memmove( pszRecord + nOffset + nWidth,
+ pszRecord + nOffset + nOldWidth,
+ nOldRecordLength - (nOffset + nOldWidth));
+ }
+
+ /* Convert null value to the appropriate value of the new type */
+ if (bIsNULL)
+ {
+ memset( pszRecord + nOffset, chFieldFill, nWidth);
+ }
+ else
+ {
+ if ((chOldType == 'N' || chOldType == 'F'))
+ {
+ /* Add leading spaces when expanding a numeric field */
+ memmove( pszRecord + nOffset + nWidth - nOldWidth,
+ pszRecord + nOffset, nOldWidth );
+ memset( pszRecord + nOffset, ' ', nWidth - nOldWidth );
+ }
+ else
+ {
+ /* Add trailing spaces */
+ memset(pszRecord + nOffset + nOldWidth, ' ', nWidth - nOldWidth);
+ }
+ }
+
+ nRecordOffset =
+ psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength;
+
+ /* write record */
+ psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 );
+ psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp );
+ }
+
+ free(pszRecord);
+ free(pszOldField);
+ }
+
+ psDBF->nCurrentRecord = -1;
+ psDBF->bCurrentRecordModified = FALSE;
+
+ return TRUE;
+}
--- /dev/null
+/******************************************************************************
+ * $Id: safileio.c,v 1.4 2008-01-16 20:05:14 bram Exp $
+ *
+ * Project: Shapelib
+ * Purpose: Default implementation of file io based on stdio.
+ * Author: Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2007, 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: safileio.c,v $
+ * Revision 1.4 2008-01-16 20:05:14 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.3 2007/12/18 18:28:11 bram
+ * - create hook for client specific atof (bugzilla ticket 1615)
+ * - check for NULL handle before closing cpCPG file, and close after reading.
+ *
+ * Revision 1.2 2007/12/15 20:25:30 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/<number>") or as the
+ * content of an accompanying .CPG file. When creating a DBF file, the code can
+ * be set using DBFCreateEx.
+ *
+ * Revision 1.1 2007/12/06 06:56:41 fwarmerdam
+ * new
+ *
+ */
+
+#include "shapefil.h"
+
+#include <math.h>
+#include <limits.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+SHP_CVSID("$Id: safileio.c,v 1.4 2008-01-16 20:05:14 bram Exp $");
+
+#ifdef SHPAPI_UTF8_HOOKS
+# ifdef SHPAPI_WINDOWS
+# define WIN32_LEAN_AND_MEAN
+# define NOMINMAX
+# include <windows.h>
+# pragma comment(lib, "kernel32.lib")
+# endif
+#endif
+
+/************************************************************************/
+/* SADFOpen() */
+/************************************************************************/
+
+SAFile SADFOpen( const char *pszFilename, const char *pszAccess )
+
+{
+ return (SAFile) fopen( pszFilename, pszAccess );
+}
+
+/************************************************************************/
+/* SADFRead() */
+/************************************************************************/
+
+SAOffset SADFRead( void *p, SAOffset size, SAOffset nmemb, SAFile file )
+
+{
+ return (SAOffset) fread( p, (size_t) size, (size_t) nmemb,
+ (FILE *) file );
+}
+
+/************************************************************************/
+/* SADFWrite() */
+/************************************************************************/
+
+SAOffset SADFWrite( void *p, SAOffset size, SAOffset nmemb, SAFile file )
+
+{
+ return (SAOffset) fwrite( p, (size_t) size, (size_t) nmemb,
+ (FILE *) file );
+}
+
+/************************************************************************/
+/* SADFSeek() */
+/************************************************************************/
+
+SAOffset SADFSeek( SAFile file, SAOffset offset, int whence )
+
+{
+ return (SAOffset) fseek( (FILE *) file, (long) offset, whence );
+}
+
+/************************************************************************/
+/* SADFTell() */
+/************************************************************************/
+
+SAOffset SADFTell( SAFile file )
+
+{
+ return (SAOffset) ftell( (FILE *) file );
+}
+
+/************************************************************************/
+/* SADFFlush() */
+/************************************************************************/
+
+int SADFFlush( SAFile file )
+
+{
+ return fflush( (FILE *) file );
+}
+
+/************************************************************************/
+/* SADFClose() */
+/************************************************************************/
+
+int SADFClose( SAFile file )
+
+{
+ return fclose( (FILE *) file );
+}
+
+/************************************************************************/
+/* SADFClose() */
+/************************************************************************/
+
+int SADRemove( const char *filename )
+
+{
+ return remove( filename );
+}
+
+/************************************************************************/
+/* SADError() */
+/************************************************************************/
+
+void SADError( const char *message )
+
+{
+ fprintf( stderr, "%s\n", message );
+}
+
+/************************************************************************/
+/* SASetupDefaultHooks() */
+/************************************************************************/
+
+void SASetupDefaultHooks( SAHooks *psHooks )
+
+{
+ psHooks->FOpen = SADFOpen;
+ psHooks->FRead = SADFRead;
+ psHooks->FWrite = SADFWrite;
+ psHooks->FSeek = SADFSeek;
+ psHooks->FTell = SADFTell;
+ psHooks->FFlush = SADFFlush;
+ psHooks->FClose = SADFClose;
+ psHooks->Remove = SADRemove;
+
+ psHooks->Error = SADError;
+ psHooks->Atof = atof;
+}
+
+
+
+
+#ifdef SHPAPI_WINDOWS
+
+/************************************************************************/
+/* Utf8ToWideChar */
+/************************************************************************/
+
+const wchar_t* Utf8ToWideChar( const char *pszFilename )
+{
+ int nMulti, nWide;
+ wchar_t *pwszFileName;
+
+ nMulti = strlen(pszFilename) + 1;
+ nWide = MultiByteToWideChar( CP_UTF8, 0, pszFilename, nMulti, 0, 0);
+ if( nWide == 0 )
+ {
+ return NULL;
+ }
+ pwszFileName = (wchar_t*) malloc(nWide * sizeof(wchar_t));
+ if ( pwszFileName == NULL )
+ {
+ return NULL;
+ }
+ if( MultiByteToWideChar( CP_UTF8, 0, pszFilename, nMulti, pwszFileName, nWide ) == 0 )
+ {
+ free( pwszFileName );
+ return NULL;
+ }
+ return pwszFileName;
+}
+
+/************************************************************************/
+/* SAUtf8WFOpen */
+/************************************************************************/
+
+SAFile SAUtf8WFOpen( const char *pszFilename, const char *pszAccess )
+{
+ SAFile file = NULL;
+ const wchar_t *pwszFileName, *pwszAccess;
+ pwszFileName = Utf8ToWideChar( pszFilename );
+ pwszAccess = Utf8ToWideChar( pszAccess );
+ if( pwszFileName != NULL && pwszFileName != NULL)
+ {
+ file = (SAFile) _wfopen( pwszFileName, pwszAccess );
+ }
+ free ((wchar_t*) pwszFileName);
+ free ((wchar_t*) pwszAccess);
+ return file;
+}
+
+/************************************************************************/
+/* SAUtf8WRemove() */
+/************************************************************************/
+
+int SAUtf8WRemove( const char *pszFilename )
+{
+ const wchar_t *pwszFileName = Utf8ToWideChar( pszFilename );
+ int rc = -1;
+ if( pwszFileName != NULL )
+ {
+ rc = _wremove( pwszFileName );
+ }
+ free ((wchar_t*) pwszFileName);
+ return rc;
+}
+
+#endif
+
+#ifdef SHPAPI_UTF8_HOOKS
+
+/************************************************************************/
+/* SASetupUtf8Hooks() */
+/************************************************************************/
+
+void SASetupUtf8Hooks( SAHooks *psHooks )
+{
+#ifdef SHPAPI_WINDOWS
+ psHooks->FOpen = SAUtf8WFOpen;
+ psHooks->Remove = SAUtf8WRemove;
+#else
+# error "no implementations of UTF-8 hooks available for this platform"
+#endif
+ psHooks->FRead = SADFRead;
+ psHooks->FWrite = SADFWrite;
+ psHooks->FSeek = SADFSeek;
+ psHooks->FTell = SADFTell;
+ psHooks->FFlush = SADFFlush;
+ psHooks->FClose = SADFClose;
+
+ psHooks->Error = SADError;
+ psHooks->Atof = atof;
+}
+
+#endif
--- /dev/null
+#ifndef SHAPEFILE_H_INCLUDED
+#define SHAPEFILE_H_INCLUDED
+
+/******************************************************************************
+ * $Id: shapefil.h,v 1.52 2011-12-11 22:26:46 fwarmerdam Exp $
+ *
+ * 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.52 2011-12-11 22:26:46 fwarmerdam
+ * upgrade .qix access code to use SAHooks (gdal #3365)
+ *
+ * Revision 1.51 2011-07-24 05:59:25 fwarmerdam
+ * minimize use of CPLError in favor of SAHooks.Error()
+ *
+ * Revision 1.50 2011-05-13 17:35:17 fwarmerdam
+ * added DBFReorderFields() and DBFAlterFields() functions (from Even)
+ *
+ * Revision 1.49 2011-04-16 14:38:21 fwarmerdam
+ * avoid warnings with gcc on SHP_CVSID
+ *
+ * Revision 1.48 2010-08-27 23:42:52 fwarmerdam
+ * add SHPAPI_CALL attribute in code
+ *
+ * Revision 1.47 2010-01-28 11:34:34 fwarmerdam
+ * handle the shape file length limits more gracefully (#3236)
+ *
+ * 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/<number>") 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 <stdio.h>
+
+#ifdef USE_DBMALLOC
+#include <dbmalloc.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 WIN32
+ #define SHAPELIB_DLLEXPORT 1
+#endif
+
+#ifdef SHAPELIB_DLLEXPORT
+# define SHPAPI_CALL __declspec(dllexport)
+# define SHPAPI_CALL1(x) __declspec(dllexport) x
+#else
+# define SHPAPI_CALL __declspec(dllimport)
+# define SHPAPI_CALL1(x) __declspec(dllimport) 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
+# if defined(__GNUC__) && __GNUC__ >= 4
+# define SHP_CVSID(string) static char cpl_cvsid[] __attribute__((used)) = string;
+# else
+# define SHP_CVSID(string) static char cpl_cvsid[] = string; \
+static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : cpl_cvsid ); }
+# endif
+#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_* */
+
+ unsigned int nFileSize; /* SHP file */
+
+ int nRecords;
+ int nMaxRecords;
+ unsigned int *panRecOffset;
+ unsigned 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 );
+
+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 );
+
+
+typedef struct SHPDiskTreeInfo* SHPTreeDiskHandle;
+
+SHPTreeDiskHandle SHPAPI_CALL
+ SHPOpenDiskTree( const char* pszQIXFilename,
+ SAHooks *psHooks );
+
+void SHPAPI_CALL
+ SHPCloseDiskTree( SHPTreeDiskHandle hDiskTree );
+
+int SHPAPI_CALL1(*)
+SHPSearchDiskTreeEx( SHPTreeDiskHandle hDiskTree,
+ double *padfBoundsMin, double *padfBoundsMax,
+ int *pnShapeCount );
+
+int SHPAPI_CALL
+ SHPWriteTreeLL(SHPTree *hTree, const char *pszFilename, SAHooks *psHooks );
+
+/************************************************************************/
+/* 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 );
+
+int SHPAPI_CALL
+ DBFReorderFields( DBFHandle psDBF, int* panMap );
+
+int SHPAPI_CALL
+ DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName,
+ char chType, int nWidth, int nDecimals );
+
+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 */
--- /dev/null
+/******************************************************************************
+ * $Id: shpopen.c,v 1.73 2012-01-24 22:33:01 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.73 2012-01-24 22:33:01 fwarmerdam
+ * fix memory leak on failure to open .shp (gdal #4410)
+ *
+ * Revision 1.72 2011-12-11 22:45:28 fwarmerdam
+ * fix failure return from SHPOpenLL.
+ *
+ * Revision 1.71 2011-09-15 03:33:58 fwarmerdam
+ * fix missing cast (#2344)
+ *
+ * Revision 1.70 2011-07-24 05:59:25 fwarmerdam
+ * minimize use of CPLError in favor of SAHooks.Error()
+ *
+ * Revision 1.69 2011-07-24 03:24:22 fwarmerdam
+ * fix memory leaks in error cases creating shapefiles (#2061)
+ *
+ * Revision 1.68 2010-08-27 23:42:52 fwarmerdam
+ * add SHPAPI_CALL attribute in code
+ *
+ * Revision 1.67 2010-07-01 08:15:48 fwarmerdam
+ * do not error out on an object with zero vertices
+ *
+ * Revision 1.66 2010-07-01 07:58:57 fwarmerdam
+ * minor cleanup of error handling
+ *
+ * Revision 1.65 2010-07-01 07:27:13 fwarmerdam
+ * white space formatting adjustments
+ *
+ * Revision 1.64 2010-01-28 11:34:34 fwarmerdam
+ * handle the shape file length limits more gracefully (#3236)
+ *
+ * Revision 1.63 2010-01-28 04:04:40 fwarmerdam
+ * improve numerical accuracy of SHPRewind() algs (gdal #3363)
+ *
+ * Revision 1.62 2010-01-17 05:34:13 fwarmerdam
+ * Remove asserts on x/y being null (#2148).
+ *
+ * Revision 1.61 2010-01-16 05:07:42 fwarmerdam
+ * allow 0/nulls in shpcreateobject (#2148)
+ *
+ * Revision 1.60 2009-09-17 20:50:02 bram
+ * on Win32, define snprintf as alias to _snprintf
+ *
+ * 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 <math.h>
+#include <limits.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+SHP_CVSID("$Id: shpopen.c,v 1.73 2012-01-24 22:33:01 fwarmerdam Exp $")
+
+typedef unsigned char uchar;
+
+#if UINT_MAX == 65535
+typedef unsigned long int32;
+#else
+typedef unsigned 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) ((a<b) ? a : b)
+# define MAX(a,b) ((a>b) ? a : b)
+#endif
+
+#if defined(WIN32) || defined(_WIN32)
+# ifndef snprintf
+# define snprintf _snprintf
+# endif
+#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 SHPAPI_CALL 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 )
+ {
+ char *pszMessage = (char *) malloc(strlen(pszBasename)*2+256);
+ sprintf( pszMessage, "Unable to open %s.shp or %s.SHP.",
+ pszBasename, pszBasename );
+ psHooks->Error( pszMessage );
+ free( pszMessage );
+
+ 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 )
+ {
+ char *pszMessage = (char *) malloc(strlen(pszBasename)*2+256);
+ sprintf( pszMessage, "Unable to open %s.shx or %s.SHX.",
+ pszBasename, pszBasename );
+ psHooks->Error( pszMessage );
+ free( pszMessage );
+
+ 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 = ((unsigned int)pabyBuf[24] * 256 * 256 * 256
+ + (unsigned int)pabyBuf[25] * 256 * 256
+ + (unsigned int)pabyBuf[26] * 256
+ + (unsigned int)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 = (unsigned int *)
+ malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) );
+ psSHP->panRecSize = (unsigned int *)
+ malloc(sizeof(unsigned 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 = NULL, *pszFullname = NULL;
+ int i;
+ SAFile fpSHP = NULL, fpSHX = NULL;
+ 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." );
+ goto error;
+ }
+
+ sprintf( pszFullname, "%s.shx", pszBasename );
+ fpSHX = psHooks->FOpen(pszFullname, "wb" );
+ if( fpSHX == NULL )
+ {
+ psHooks->Error( "Failed to create file .shx file." );
+ goto error;
+ }
+
+ free( pszFullname ); pszFullname = NULL;
+ free( pszBasename ); pszBasename = NULL;
+
+/* -------------------------------------------------------------------- */
+/* Prepare header block for .shp file. */
+/* -------------------------------------------------------------------- */
+ for( i = 0; i < 100; i++ )
+ abyHeader[i] = 0;
+
+ abyHeader[2] = 0x27; /* magic cookie */
+ abyHeader[3] = 0x0a;
+
+ i32 = 50; /* file size */
+ ByteCopy( &i32, abyHeader+24, 4 );
+ if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
+
+ i32 = 1000; /* version */
+ ByteCopy( &i32, abyHeader+28, 4 );
+ if( bBigEndian ) SwapWord( 4, abyHeader+28 );
+
+ i32 = nShapeType; /* shape type */
+ ByteCopy( &i32, abyHeader+32, 4 );
+ if( bBigEndian ) SwapWord( 4, abyHeader+32 );
+
+ dValue = 0.0; /* set bounds */
+ ByteCopy( &dValue, abyHeader+36, 8 );
+ ByteCopy( &dValue, abyHeader+44, 8 );
+ ByteCopy( &dValue, abyHeader+52, 8 );
+ ByteCopy( &dValue, abyHeader+60, 8 );
+
+/* -------------------------------------------------------------------- */
+/* Write .shp file header. */
+/* -------------------------------------------------------------------- */
+ if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 )
+ {
+ psHooks->Error( "Failed to write .shp header." );
+ goto error;
+ }
+
+/* -------------------------------------------------------------------- */
+/* 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." );
+ goto error;
+ }
+
+/* -------------------------------------------------------------------- */
+/* Close the files, and then open them as regular existing files. */
+/* -------------------------------------------------------------------- */
+ psHooks->FClose( fpSHP );
+ psHooks->FClose( fpSHX );
+
+ return( SHPOpenLL( pszLayer, "r+b", psHooks ) );
+
+error:
+ if (pszFullname) free(pszFullname);
+ if (pszBasename) free(pszBasename);
+ if (fpSHP) psHooks->FClose( fpSHP );
+ if (fpSHX) psHooks->FClose( fpSHX );
+ return NULL;
+}
+
+/************************************************************************/
+/* _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 *)
+ calloc(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++ )
+ {
+ if( psObject->panPartStart != NULL )
+ 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 X, Y, Z and M are optional. */
+/* -------------------------------------------------------------------- */
+ 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);
+
+ for( i = 0; i < nVertices; i++ )
+ {
+ if( padfX != NULL )
+ psObject->padfX[i] = padfX[i];
+ if( padfY != NULL )
+ 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 )
+
+{
+ unsigned int nRecordOffset, nRecordSize=0;
+ int i;
+ 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 = (unsigned int *)
+ SfRealloc(psSHP->panRecOffset,sizeof(unsigned int) * psSHP->nMaxRecords );
+ psSHP->panRecSize = (unsigned int *)
+ SfRealloc(psSHP->panRecSize,sizeof(unsigned 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 )
+ {
+ unsigned int nExpectedSize = psSHP->nFileSize + nRecordSize;
+ if( nExpectedSize < psSHP->nFileSize ) // due to unsigned int overflow
+ {
+ char str[128];
+ sprintf( str, "Failed to write shape object. "
+ "File size cannot reach %u + %u.",
+ psSHP->nFileSize, nRecordSize );
+ psSHP->sHooks.Error( str );
+ free( pabyRec );
+ return -1;
+ }
+
+ 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.Error( "Error in psSHP->sHooks.FSeek() while writing object to .shp file." );
+ free( pabyRec );
+ return -1;
+ }
+ if( psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 )
+ {
+ psSHP->sHooks.Error( "Error in psSHP->sHooks.Fwrite() while 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 szErrorMsg[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 = (uchar *) 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 )
+ {
+ /*
+ * TODO - mloskot: Consider detailed diagnostics of shape file,
+ * for example to detect if file is truncated.
+ */
+ char str[128];
+ sprintf( str,
+ "Error in fseek() reading object from .shp file at offset %u",
+ psSHP->panRecOffset[hEntity]);
+
+ psSHP->sHooks.Error( str );
+ return NULL;
+ }
+
+ if( 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.
+ */
+ char str[128];
+ sprintf( str,
+ "Error in fread() reading object of size %u at offset %u from .shp file",
+ nEntitySize, psSHP->panRecOffset[hEntity] );
+
+ psSHP->sHooks.Error( str );
+ 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(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nEntitySize = %d",
+ hEntity, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ 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(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nEntitySize = %d",
+ hEntity, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ 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(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.",
+ hEntity, nPoints, nParts);
+ psSHP->sHooks.Error( szErrorMsg );
+ 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(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d, nEntitySize=%d.",
+ hEntity, nPoints, nParts, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ 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(szErrorMsg, sizeof(szErrorMsg),
+ "Not enough memory to allocate requested memory (nPoints=%d, nParts=%d) for shape %d. "
+ "Probably broken SHP file", hEntity, nPoints, nParts );
+ psSHP->sHooks.Error( szErrorMsg );
+ 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
+ && psShape->nVertices > 0) )
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d",
+ hEntity, i, psShape->panPartStart[i], psShape->nVertices);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
+ if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1])
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "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( szErrorMsg );
+ 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(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nEntitySize = %d",
+ hEntity, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
+ memcpy( &nPoints, psSHP->pabyRec + 44, 4 );
+
+ if( bBigEndian ) SwapWord( 4, &nPoints );
+
+ if (nPoints < 0 || nPoints > 50 * 1000 * 1000)
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nPoints = %d",
+ hEntity, nPoints);
+ psSHP->sHooks.Error( szErrorMsg );
+ SHPDestroyObject(psShape);
+ return NULL;
+ }
+
+ nRequiredSize = 48 + nPoints * 16;
+ if( psShape->nSHPType == SHPT_MULTIPOINTZ )
+ {
+ nRequiredSize += 16 + nPoints * 8;
+ }
+ if (nRequiredSize > nEntitySize)
+ {
+ snprintf(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d",
+ hEntity, nPoints, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ 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(szErrorMsg, sizeof(szErrorMsg),
+ "Not enough memory to allocate requested memory (nPoints=%d) for shape %d. "
+ "Probably broken SHP file", hEntity, nPoints );
+ psSHP->sHooks.Error( szErrorMsg );
+ 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(szErrorMsg, sizeof(szErrorMsg),
+ "Corrupted .shp file : shape %d : nEntitySize = %d",
+ hEntity, nEntitySize);
+ psSHP->sHooks.Error( szErrorMsg );
+ 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];
+
+ if (nVertCount < 2)
+ continue;
+
+ dfSum = psObject->padfX[nVertStart] * (psObject->padfY[nVertStart+1] - psObject->padfY[nVertStart+nVertCount-1]);
+ for( iVert = nVertStart + 1; iVert < nVertStart+nVertCount-1; iVert++ )
+ {
+ dfSum += psObject->padfX[iVert] * (psObject->padfY[iVert+1] - psObject->padfY[iVert-1]);
+ }
+
+ dfSum += psObject->padfX[iVert] * (psObject->padfY[nVertStart] - psObject->padfY[iVert-1]);
+
+/* -------------------------------------------------------------------- */
+/* 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;
+}
--- /dev/null
+/******************************************************************************
+ * $Id: shptree.c,v 1.17 2012-01-27 21:09:26 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.17 2012-01-27 21:09:26 fwarmerdam
+ * optimize .qix output (gdal #4472)
+ *
+ * Revision 1.16 2011-12-11 22:26:46 fwarmerdam
+ * upgrade .qix access code to use SAHooks (gdal #3365)
+ *
+ * Revision 1.15 2011-07-24 05:59:25 fwarmerdam
+ * minimize use of CPLError in favor of SAHooks.Error()
+ *
+ * Revision 1.14 2010-08-27 23:43:27 fwarmerdam
+ * add SHPAPI_CALL attribute in code
+ *
+ * Revision 1.13 2010-06-29 05:50:15 fwarmerdam
+ * fix sign of Z/M comparisons in SHPCheckObjectContained (#2223)
+ *
+ * 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 <math.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_CPL
+#include "cpl_error.h"
+#endif
+
+SHP_CVSID("$Id: shptree.c,v 1.17 2012-01-27 21:09:26 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 )
+ 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 )
+ {
+ 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 */
+ }
+ }
+
+/* -------------------------------------------------------------------- */
+/* If the current node has 1 subnode and no shapes, promote that */
+/* subnode to the current node position. */
+/* -------------------------------------------------------------------- */
+ if( psTreeNode->nSubNodes == 1 && psTreeNode->nShapeCount == 0)
+ {
+ SHPTreeNode* psSubNode = psTreeNode->apsSubNode[0];
+
+ memcpy(psTreeNode->adfBoundsMin, psSubNode->adfBoundsMin,
+ sizeof(psSubNode->adfBoundsMin));
+ memcpy(psTreeNode->adfBoundsMax, psSubNode->adfBoundsMax,
+ sizeof(psSubNode->adfBoundsMax));
+ psTreeNode->nShapeCount = psSubNode->nShapeCount;
+ assert(psTreeNode->panShapeIds == NULL);
+ psTreeNode->panShapeIds = psSubNode->panShapeIds;
+ assert(psTreeNode->papsShapeObj == NULL);
+ psTreeNode->papsShapeObj = psSubNode->papsShapeObj;
+ psTreeNode->nSubNodes = psSubNode->nSubNodes;
+ for( i = 0; i < psSubNode->nSubNodes; i++ )
+ psTreeNode->apsSubNode[i] = psSubNode->apsSubNode[i];
+ free(psSubNode);
+ }
+
+/* -------------------------------------------------------------------- */
+/* 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;
+ }
+}
+
+
+struct SHPDiskTreeInfo
+{
+ SAHooks sHooks;
+ SAFile fpQIX;
+};
+
+/************************************************************************/
+/* SHPOpenDiskTree() */
+/************************************************************************/
+
+SHPTreeDiskHandle SHPOpenDiskTree( const char* pszQIXFilename,
+ SAHooks *psHooks )
+{
+ SHPTreeDiskHandle hDiskTree;
+
+ hDiskTree = (SHPTreeDiskHandle) calloc(sizeof(struct SHPDiskTreeInfo),1);
+
+ if (psHooks == NULL)
+ SASetupDefaultHooks( &(hDiskTree->sHooks) );
+ else
+ memcpy( &(hDiskTree->sHooks), psHooks, sizeof(SAHooks) );
+
+ hDiskTree->fpQIX = hDiskTree->sHooks.FOpen(pszQIXFilename, "rb");
+ if (hDiskTree->fpQIX == NULL)
+ {
+ free(hDiskTree);
+ return NULL;
+ }
+
+ return hDiskTree;
+}
+
+/***********************************************************************/
+/* SHPCloseDiskTree() */
+/************************************************************************/
+
+void SHPCloseDiskTree( SHPTreeDiskHandle hDiskTree )
+{
+ if (hDiskTree == NULL)
+ return;
+
+ hDiskTree->sHooks.FClose(hDiskTree->fpQIX);
+ free(hDiskTree);
+}
+
+/************************************************************************/
+/* SHPSearchDiskTreeNode() */
+/************************************************************************/
+
+static int
+SHPSearchDiskTreeNode( SHPTreeDiskHandle hDiskTree, 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. */
+/* -------------------------------------------------------------------- */
+ hDiskTree->sHooks.FRead( &offset, 4, 1, hDiskTree->fpQIX );
+ if ( bNeedSwap ) SwapWord ( 4, &offset );
+
+ hDiskTree->sHooks.FRead( adfNodeBoundsMin, sizeof(double), 2, hDiskTree->fpQIX );
+ hDiskTree->sHooks.FRead( adfNodeBoundsMax, sizeof(double), 2, hDiskTree->fpQIX );
+ if ( bNeedSwap )
+ {
+ SwapWord( 8, adfNodeBoundsMin + 0 );
+ SwapWord( 8, adfNodeBoundsMin + 1 );
+ SwapWord( 8, adfNodeBoundsMax + 0 );
+ SwapWord( 8, adfNodeBoundsMax + 1 );
+ }
+
+ hDiskTree->sHooks.FRead( &numshapes, 4, 1, hDiskTree->fpQIX );
+ 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);
+ hDiskTree->sHooks.FSeek(hDiskTree->fpQIX, 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) );
+ }
+
+ hDiskTree->sHooks.FRead( *ppanResultBuffer + *pnResultCount,
+ sizeof(int), numshapes, hDiskTree->fpQIX );
+
+ if (bNeedSwap )
+ {
+ for( i=0; i<numshapes; i++ )
+ SwapWord( 4, *ppanResultBuffer + *pnResultCount + i );
+ }
+
+ *pnResultCount += numshapes;
+ }
+
+/* -------------------------------------------------------------------- */
+/* Process the subnodes. */
+/* -------------------------------------------------------------------- */
+ hDiskTree->sHooks.FRead( &numsubnodes, 4, 1, hDiskTree->fpQIX );
+ if ( bNeedSwap ) SwapWord ( 4, &numsubnodes );
+
+ for(i=0; i<numsubnodes; i++)
+ {
+ if( !SHPSearchDiskTreeNode( hDiskTree, padfBoundsMin, padfBoundsMax,
+ ppanResultBuffer, pnBufferMax,
+ pnResultCount, bNeedSwap ) )
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/************************************************************************/
+/* SHPTreeReadLibc() */
+/************************************************************************/
+
+static
+SAOffset SHPTreeReadLibc( void *p, SAOffset size, SAOffset nmemb, SAFile file )
+
+{
+ return (SAOffset) fread( p, (size_t) size, (size_t) nmemb,
+ (FILE *) file );
+}
+
+/************************************************************************/
+/* SHPTreeSeekLibc() */
+/************************************************************************/
+
+static
+SAOffset SHPTreeSeekLibc( SAFile file, SAOffset offset, int whence )
+
+{
+ return (SAOffset) fseek( (FILE *) file, (long) offset, whence );
+}
+
+/************************************************************************/
+/* SHPSearchDiskTree() */
+/************************************************************************/
+
+int SHPAPI_CALL1(*)
+SHPSearchDiskTree( FILE *fp,
+ double *padfBoundsMin, double *padfBoundsMax,
+ int *pnShapeCount )
+{
+ struct SHPDiskTreeInfo sDiskTree;
+ memset(&sDiskTree.sHooks, 0, sizeof(sDiskTree.sHooks));
+
+ /* We do not use SASetupDefaultHooks() because the FILE* */
+ /* is a libc FILE* */
+ sDiskTree.sHooks.FSeek = SHPTreeSeekLibc;
+ sDiskTree.sHooks.FRead = SHPTreeReadLibc;
+
+ sDiskTree.fpQIX = (SAFile)fp;
+
+ return SHPSearchDiskTreeEx( &sDiskTree, padfBoundsMin, padfBoundsMax,
+ pnShapeCount );
+}
+
+/***********************************************************************/
+/* SHPSearchDiskTreeEx() */
+/************************************************************************/
+
+int* SHPSearchDiskTreeEx( SHPTreeDiskHandle hDiskTree,
+ double *padfBoundsMin, double *padfBoundsMax,
+ int *pnShapeCount )
+
+{
+ int i, bNeedSwap, nBufferMax = 0;
+ unsigned char abyBuf[16];
+ int *panResultBuffer = NULL;
+
+ *pnShapeCount = 0;
+
+/* -------------------------------------------------------------------- */
+/* Establish the byte order on this machine. */
+/* -------------------------------------------------------------------- */
+ i = 1;
+ if( *((unsigned char *) &i) == 1 )
+ bBigEndian = FALSE;
+ else
+ bBigEndian = TRUE;
+
+/* -------------------------------------------------------------------- */
+/* Read the header. */
+/* -------------------------------------------------------------------- */
+ hDiskTree->sHooks.FSeek( hDiskTree->fpQIX, 0, SEEK_SET );
+ hDiskTree->sHooks.FRead( abyBuf, 16, 1, hDiskTree->fpQIX );
+
+ if( memcmp( abyBuf, "SQT", 3 ) != 0 )
+ return NULL;
+
+ if( (abyBuf[3] == 2 && bBigEndian)
+ || (abyBuf[3] == 1 && !bBigEndian) )
+ bNeedSwap = FALSE;
+ else
+ bNeedSwap = TRUE;
+
+/* -------------------------------------------------------------------- */
+/* Search through root node and it's decendents. */
+/* -------------------------------------------------------------------- */
+ if( !SHPSearchDiskTreeNode( hDiskTree, padfBoundsMin, padfBoundsMax,
+ &panResultBuffer, &nBufferMax,
+ pnShapeCount, bNeedSwap ) )
+ {
+ if( panResultBuffer != NULL )
+ free( panResultBuffer );
+ *pnShapeCount = 0;
+ return NULL;
+ }
+/* -------------------------------------------------------------------- */
+/* Sort the id array */
+/* -------------------------------------------------------------------- */
+ qsort(panResultBuffer, *pnShapeCount, sizeof(int), compare_ints);
+
+ return panResultBuffer;
+}
+
+/************************************************************************/
+/* SHPGetSubNodeOffset() */
+/* */
+/* Determine how big all the subnodes of this node (and their */
+/* children) will be. This will allow disk based searchers to */
+/* seek past them all efficiently. */
+/************************************************************************/
+
+static int SHPGetSubNodeOffset( SHPTreeNode *node)
+{
+ int i;
+ long offset=0;
+
+ for(i=0; i<node->nSubNodes; 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( SAFile fp, SHPTreeNode *node, SAHooks* psHooks)
+{
+ 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 );
+ return;
+ }
+
+ 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);
+
+ psHooks->FWrite( pabyRec, 44+j, 1, fp );
+ free (pabyRec);
+
+ for(i=0; i<node->nSubNodes; i++ )
+ {
+ if(node->apsSubNode[i])
+ SHPWriteTreeNode( fp, node->apsSubNode[i], psHooks);
+ }
+}
+
+/************************************************************************/
+/* SHPWriteTree() */
+/************************************************************************/
+
+int SHPAPI_CALL SHPWriteTree(SHPTree *tree, const char *filename )
+{
+ SAHooks sHooks;
+
+ SASetupDefaultHooks( &sHooks );
+
+ return SHPWriteTreeLL(tree, filename, &sHooks);
+}
+
+/************************************************************************/
+/* SHPWriteTreeLL() */
+/************************************************************************/
+
+int SHPWriteTreeLL(SHPTree *tree, const char *filename, SAHooks* psHooks )
+{
+ char signature[4] = "SQT";
+ int i;
+ char abyBuf[32];
+ SAFile fp;
+
+ SAHooks sHooks;
+ if (psHooks == NULL)
+ {
+ SASetupDefaultHooks( &sHooks );
+ psHooks = &sHooks;
+ }
+
+/* -------------------------------------------------------------------- */
+/* Open the output file. */
+/* -------------------------------------------------------------------- */
+ fp = psHooks->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;
+
+ psHooks->FWrite( abyBuf, 8, 1, fp );
+
+ psHooks->FWrite( &(tree->nTotalCount), 4, 1, fp );
+
+ /* write maxdepth */
+
+ psHooks->FWrite( &(tree->nMaxDepth), 4, 1, fp );
+
+/* -------------------------------------------------------------------- */
+/* Write all the nodes "in order". */
+/* -------------------------------------------------------------------- */
+
+ SHPWriteTreeNode( fp, tree->psRoot, psHooks );
+
+ psHooks->FClose( fp );
+
+ return TRUE;
+}
--- /dev/null
+/******************************************************************************
+ * $Id: shputils.c,v 1.10 2007-12-13 19:59:23 fwarmerdam Exp $
+ *
+ * Project: Shapelib
+ * Purpose:
+ * Altered "shpdump" and "dbfdump" to allow two files to be appended.
+ * Other Functions:
+ * Selecting from the DBF before the write occurs.
+ * Change the UNITS between Feet and Meters and Shift X,Y.
+ * Clip and Erase boundary. The program only passes thru the
+ * data once.
+ *
+ * Bill Miller North Carolina - Department of Transporation
+ * Feb. 1997 -- bmiller@dot.state.nc.us
+ * There was not a lot of time to debug hidden problems;
+ * And the code is not very well organized or documented.
+ * The clip/erase function was not well tested.
+ * Oct. 2000 -- bmiller@dot.state.nc.us
+ * Fixed the problem when select is using numbers
+ * larger than short integer. It now reads long integer.
+ * NOTE: DBF files created using windows NT will read as a string with
+ * a length of 381 characters. This is a bug in "dbfopen".
+ *
+ *
+ * Author: Bill Miller (bmiller@dot.state.nc.us)
+ *
+ ******************************************************************************
+ * 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: shputils.c,v $
+ * Revision 1.10 2007-12-13 19:59:23 fwarmerdam
+ * reindent code, avoid some warnings.
+ *
+ * Revision 1.9 2004/01/14 14:56:00 fwarmerdam
+ * Some cleanlyness improvements.
+ *
+ * Revision 1.8 2004/01/14 14:40:22 fwarmerdam
+ * Fixed exit() call to include code.
+ *
+ * Revision 1.7 2003/02/25 17:20:22 warmerda
+ * Set psCShape to NULL after SHPDestroyObject() to avoid multi-frees of
+ * the same memory ... as submitted by Fred Fox.
+ *
+ * Revision 1.6 2001/08/28 13:57:14 warmerda
+ * fixed DBFAddField return value check
+ *
+ * Revision 1.5 2000/11/02 13:52:48 warmerda
+ * major upgrade from Bill Miller
+ *
+ * Revision 1.4 1999/11/05 14:12:05 warmerda
+ * updated license terms
+ *
+ * Revision 1.3 1998/12/03 15:47:39 warmerda
+ * Did a bunch of rewriting to make it work with the V1.2 API.
+ *
+ * Revision 1.2 1998/06/18 01:19:49 warmerda
+ * Made C++ compilable.
+ *
+ * Revision 1.1 1997/05/27 20:40:27 warmerda
+ * Initial revision
+ */
+
+#include "shapefil.h"
+#include "string.h"
+#include <stdlib.h>
+
+SHP_CVSID("$Id: shputils.c,v 1.10 2007-12-13 19:59:23 fwarmerdam Exp $")
+
+#ifndef FALSE
+# define FALSE 0
+# define TRUE 1
+#endif
+
+char infile[80], outfile[80], temp[400];
+
+/* Variables for shape files */
+SHPHandle hSHP;
+SHPHandle hSHPappend;
+int nShapeType, nEntities, iPart;
+int nShapeTypeAppend, nEntitiesAppend;
+SHPObject *psCShape;
+double adfBoundsMin[4], adfBoundsMax[4];
+
+
+/* Variables for DBF files */
+DBFHandle hDBF;
+DBFHandle hDBFappend;
+
+DBFFieldType iType;
+DBFFieldType jType;
+
+char iszTitle[12];
+char jszTitle[12];
+
+int *pt;
+char iszFormat[32], iszField[1024];
+char jszFormat[32], jszField[1024];
+int i, ti, iWidth, iDecimals, iRecord;
+int j, tj, jWidth, jDecimals, jRecord;
+
+
+int clip_boundary();
+double findunit(char *unit);
+void openfiles(void);
+void setext(char *pt, char *ext);
+int strncasecmp2(char *s1, char *s2, int n);
+void mergefields(void);
+void findselect(void);
+void showitems(void);
+int selectrec();
+void check_theme_bnd();
+int clip_boundary();
+void error();
+
+
+/* -------------------------------------------------------------------- */
+/* Variables for the DESCRIBE function */
+/* -------------------------------------------------------------------- */
+ int ilist = FALSE, iall = FALSE;
+/* -------------------------------------------------------------------- */
+/* Variables for the SELECT function */
+/* -------------------------------------------------------------------- */
+ int found = FALSE, newdbf = FALSE;
+ char selectitem[40], *cpt;
+ long int selectvalues[150], selcount=0;
+ int iselect = FALSE, iselectitem = -1;
+ int iunselect = FALSE;
+
+/* -------------------------------------------------------------------- */
+/* Variables for the CLIP and ERASE functions */
+/* -------------------------------------------------------------------- */
+ double cxmin, cymin, cxmax, cymax;
+ int iclip = FALSE, ierase = FALSE;
+ int itouch = FALSE, iinside = FALSE, icut = FALSE;
+ int ibound = FALSE, ipoly = FALSE;
+ char clipfile[80];
+
+/* -------------------------------------------------------------------- */
+/* Variables for the FACTOR function */
+/* -------------------------------------------------------------------- */
+ double infactor,outfactor,factor = 0; /* NO FACTOR */
+ int iunit = FALSE;
+ int ifactor = FALSE;
+
+
+/* -------------------------------------------------------------------- */
+/* Variables for the SHIFT function */
+/* -------------------------------------------------------------------- */
+ double xshift = 0, yshift = 0; /* NO SHIFT */
+
+int main( int argc, char ** argv )
+{
+
+/* -------------------------------------------------------------------- */
+/* Check command line usage. */
+/* -------------------------------------------------------------------- */
+ if( argc < 2 ) error();
+ strcpy(infile, argv[1]);
+ if (argc > 2) {
+ strcpy(outfile,argv[2]);
+ if (strncasecmp2(outfile, "LIST",0) == 0) { ilist = TRUE; }
+ if (strncasecmp2(outfile, "ALL",0) == 0) { iall = TRUE; }
+ }
+ if (ilist || iall || argc == 2 ) {
+ setext(infile, "shp");
+ printf("DESCRIBE: %s\n",infile);
+ strcpy(outfile,"");
+ }
+/* -------------------------------------------------------------------- */
+/* Look for other functions on the command line. (SELECT, UNIT) */
+/* -------------------------------------------------------------------- */
+ for (i = 3; i < argc; i++)
+ {
+ if ((strncasecmp2(argv[i], "SEL",3) == 0) ||
+ (strncasecmp2(argv[i], "UNSEL",5) == 0))
+ {
+ if (strncasecmp2(argv[i], "UNSEL",5) == 0) iunselect=TRUE;
+ i++;
+ if (i >= argc) error();
+ strcpy(selectitem,argv[i]);
+ i++;
+ if (i >= argc) error();
+ selcount=0;
+ strcpy(temp,argv[i]);
+ cpt=temp;
+ tj = atoi(cpt);
+ ti = 0;
+ while (tj>0) {
+ selectvalues[selcount] = tj;
+ while( *cpt >= '0' && *cpt <= '9')
+ cpt++;
+ while( *cpt > '\0' && (*cpt < '0' || *cpt > '9') )
+ cpt++;
+ tj=atoi(cpt);
+ selcount++;
+ }
+ iselect=TRUE;
+ } /*** End SEL & UNSEL ***/
+ else
+ if ((strncasecmp2(argv[i], "CLIP",4) == 0) ||
+ (strncasecmp2(argv[i], "ERASE",5) == 0))
+ {
+ if (strncasecmp2(argv[i], "ERASE",5) == 0) ierase=TRUE;
+ i++;
+ if (i >= argc) error();
+ strcpy(clipfile,argv[i]);
+ sscanf(argv[i],"%lf",&cxmin);
+ i++;
+ if (i >= argc) error();
+ if (strncasecmp2(argv[i], "BOUND",5) == 0) {
+ setext(clipfile, "shp");
+ hSHP = SHPOpen( clipfile, "rb" );
+ if( hSHP == NULL )
+ {
+ printf( "ERROR: Unable to open the clip shape file:%s\n", clipfile );
+ exit( 1 );
+ }
+ SHPGetInfo( hSHPappend, NULL, NULL,
+ adfBoundsMin, adfBoundsMax );
+ cxmin = adfBoundsMin[0];
+ cymin = adfBoundsMin[1];
+ cxmax = adfBoundsMax[0];
+ cymax = adfBoundsMax[1];
+ printf("Theme Clip Boundary: (%lf,%lf) - (%lf,%lf)\n",
+ cxmin, cymin, cxmax, cymax);
+ ibound=TRUE;
+ } else { /*** xmin,ymin,xmax,ymax ***/
+ sscanf(argv[i],"%lf",&cymin);
+ i++;
+ if (i >= argc) error();
+ sscanf(argv[i],"%lf",&cxmax);
+ i++;
+ if (i >= argc) error();
+ sscanf(argv[i],"%lf",&cymax);
+ printf("Clip Box: (%lf,%lf) - (%lf,%lf)\n",cxmin, cymin, cxmax, cymax);
+ }
+ i++;
+ if (i >= argc) error();
+ if (strncasecmp2(argv[i], "CUT",3) == 0) icut=TRUE;
+ else if (strncasecmp2(argv[i], "TOUCH",5) == 0) itouch=TRUE;
+ else if (strncasecmp2(argv[i], "INSIDE",6) == 0) iinside=TRUE;
+ else error();
+ iclip=TRUE;
+ } /*** End CLIP & ERASE ***/
+ else if (strncasecmp2(argv[i], "FACTOR",0) == 0)
+ {
+ i++;
+ if (i >= argc) error();
+ infactor=findunit(argv[i]);
+ if (infactor == 0) error();
+ iunit=TRUE;
+ i++;
+ if (i >= argc) error();
+ outfactor=findunit(argv[i]);
+ if (outfactor == 0)
+ {
+ sscanf(argv[i],"%lf",&factor);
+ if (factor == 0) error();
+ }
+ if (factor == 0)
+ {
+ if (infactor ==0)
+ { puts("ERROR: Input unit must be defined before output unit"); exit(1); }
+ factor=infactor/outfactor;
+ }
+ printf("Output file coordinate values will be factored by %lg\n",factor);
+ ifactor=(factor != 1); /* True if a valid factor */
+ } /*** End FACTOR ***/
+ else if (strncasecmp2(argv[i],"SHIFT",5) == 0)
+ {
+ i++;
+ if (i >= argc) error();
+ sscanf(argv[i],"%lf",&xshift);
+ i++;
+ if (i >= argc) error();
+ sscanf(argv[i],"%lf",&yshift);
+ iunit=TRUE;
+ printf("X Shift: %lg Y Shift: %lg\n",xshift,yshift);
+ } /*** End SHIFT ***/
+ else {
+ printf("ERROR: Unknown function %s\n",argv[i]); error();
+ }
+ }
+/* -------------------------------------------------------------------- */
+/* If there is no data in this file let the user know. */
+/* -------------------------------------------------------------------- */
+ openfiles(); /* Open the infile and the outfile for shape and dbf. */
+ if( DBFGetFieldCount(hDBF) == 0 )
+ {
+ puts( "There are no fields in this table!" );
+ exit( 1 );
+ }
+/* -------------------------------------------------------------------- */
+/* Print out the file bounds. */
+/* -------------------------------------------------------------------- */
+ iRecord = DBFGetRecordCount( hDBF );
+ SHPGetInfo( hSHP, NULL, NULL, adfBoundsMin, adfBoundsMax );
+
+ printf( "Input Bounds: (%lg,%lg) - (%lg,%lg) Entities: %d DBF: %d\n",
+ adfBoundsMin[0], adfBoundsMin[1],
+ adfBoundsMax[0], adfBoundsMax[1],
+ nEntities, iRecord );
+
+ if (strcmp(outfile,"") == 0) /* Describe the shapefile; No other functions */
+ {
+ ti = DBFGetFieldCount( hDBF );
+ showitems();
+ exit(0);
+ }
+
+ if (iclip) check_theme_bnd();
+
+ jRecord = DBFGetRecordCount( hDBFappend );
+ SHPGetInfo( hSHPappend, NULL, NULL, adfBoundsMin, adfBoundsMax );
+ if (nEntitiesAppend == 0)
+ puts("New Output File\n");
+ else
+ printf( "Append Bounds: (%lg,%lg)-(%lg,%lg) Entities: %d DBF: %d\n",
+ adfBoundsMin[0], adfBoundsMin[1],
+ adfBoundsMax[0], adfBoundsMax[1],
+ nEntitiesAppend, jRecord );
+
+/* -------------------------------------------------------------------- */
+/* Find matching fields in the append file or add new items. */
+/* -------------------------------------------------------------------- */
+ mergefields();
+/* -------------------------------------------------------------------- */
+/* Find selection field if needed. */
+/* -------------------------------------------------------------------- */
+ if (iselect) findselect();
+
+/* -------------------------------------------------------------------- */
+/* Read all the records */
+/* -------------------------------------------------------------------- */
+ jRecord = DBFGetRecordCount( hDBFappend );
+ for( iRecord = 0; iRecord < nEntities; iRecord++) /** DBFGetRecordCount(hDBF) **/
+ {
+/* -------------------------------------------------------------------- */
+/* SELECT for values if needed. (Can the record be skipped.) */
+/* -------------------------------------------------------------------- */
+ if (iselect)
+ if (selectrec() == 0) goto SKIP_RECORD; /** SKIP RECORD **/
+
+/* -------------------------------------------------------------------- */
+/* Read a Shape record */
+/* -------------------------------------------------------------------- */
+ psCShape = SHPReadObject( hSHP, iRecord );
+
+/* -------------------------------------------------------------------- */
+/* Clip coordinates of shapes if needed. */
+/* -------------------------------------------------------------------- */
+ if (iclip)
+ if (clip_boundary() == 0) goto SKIP_RECORD; /** SKIP RECORD **/
+
+/* -------------------------------------------------------------------- */
+/* Read a DBF record and copy each field. */
+/* -------------------------------------------------------------------- */
+ for( i = 0; i < DBFGetFieldCount(hDBF); i++ )
+ {
+/* -------------------------------------------------------------------- */
+/* Store the record according to the type and formatting */
+/* information implicit in the DBF field description. */
+/* -------------------------------------------------------------------- */
+ if (pt[i] > -1) /* if the current field exists in output file */
+ {
+ switch( DBFGetFieldInfo( hDBF, i, NULL, &iWidth, &iDecimals ) )
+ {
+ case FTString:
+ case FTLogical:
+ DBFWriteStringAttribute(hDBFappend, jRecord, pt[i],
+ (DBFReadStringAttribute( hDBF, iRecord, i )) );
+ break;
+
+ case FTInteger:
+ DBFWriteIntegerAttribute(hDBFappend, jRecord, pt[i],
+ (DBFReadIntegerAttribute( hDBF, iRecord, i )) );
+ break;
+
+ case FTDouble:
+ DBFWriteDoubleAttribute(hDBFappend, jRecord, pt[i],
+ (DBFReadDoubleAttribute( hDBF, iRecord, i )) );
+ break;
+
+ case FTInvalid:
+ break;
+ }
+ }
+ }
+ jRecord++;
+/* -------------------------------------------------------------------- */
+/* Change FACTOR and SHIFT coordinates of shapes if needed. */
+/* -------------------------------------------------------------------- */
+ if (iunit)
+ {
+ for( j = 0; j < psCShape->nVertices; j++ )
+ {
+ psCShape->padfX[j] = psCShape->padfX[j] * factor + xshift;
+ psCShape->padfY[j] = psCShape->padfY[j] * factor + yshift;
+ }
+ }
+
+/* -------------------------------------------------------------------- */
+/* Write the Shape record after recomputing current extents. */
+/* -------------------------------------------------------------------- */
+ SHPComputeExtents( psCShape );
+ SHPWriteObject( hSHPappend, -1, psCShape );
+
+ SKIP_RECORD:
+ SHPDestroyObject( psCShape );
+ psCShape = NULL;
+ j=0;
+ }
+
+/* -------------------------------------------------------------------- */
+/* Print out the # of Entities and the file bounds. */
+/* -------------------------------------------------------------------- */
+ jRecord = DBFGetRecordCount( hDBFappend );
+ SHPGetInfo( hSHPappend, &nEntitiesAppend, &nShapeTypeAppend,
+ adfBoundsMin, adfBoundsMax );
+
+ printf( "Output Bounds: (%lg,%lg) - (%lg,%lg) Entities: %d DBF: %d\n\n",
+ adfBoundsMin[0], adfBoundsMin[1],
+ adfBoundsMax[0], adfBoundsMax[1],
+ nEntitiesAppend, jRecord );
+
+/* -------------------------------------------------------------------- */
+/* Close the both shapefiles. */
+/* -------------------------------------------------------------------- */
+ SHPClose( hSHP );
+ SHPClose( hSHPappend );
+ DBFClose( hDBF );
+ DBFClose( hDBFappend );
+ if (nEntitiesAppend == 0) {
+ puts("Remove the output files.");
+ setext(outfile, "dbf");
+ remove(outfile);
+ setext(outfile, "shp");
+ remove(outfile);
+ setext(outfile, "shx");
+ remove(outfile);
+ }
+ return( 0 );
+}
+
+
+/************************************************************************/
+/* openfiles() */
+/************************************************************************/
+
+void openfiles() {
+/* -------------------------------------------------------------------- */
+/* Open the DBF file. */
+/* -------------------------------------------------------------------- */
+ setext(infile, "dbf");
+ hDBF = DBFOpen( infile, "rb" );
+ if( hDBF == NULL )
+ {
+ printf( "ERROR: Unable to open the input DBF:%s\n", infile );
+ exit( 1 );
+ }
+/* -------------------------------------------------------------------- */
+/* Open the append DBF file. */
+/* -------------------------------------------------------------------- */
+ if (strcmp(outfile,"")) {
+ setext(outfile, "dbf");
+ hDBFappend = DBFOpen( outfile, "rb+" );
+ newdbf=0;
+ if( hDBFappend == NULL )
+ {
+ newdbf=1;
+ hDBFappend = DBFCreate( outfile );
+ if( hDBFappend == NULL )
+ {
+ printf( "ERROR: Unable to open the append DBF:%s\n", outfile );
+ exit( 1 );
+ }
+ }
+ }
+/* -------------------------------------------------------------------- */
+/* Open the passed shapefile. */
+/* -------------------------------------------------------------------- */
+ setext(infile, "shp");
+ hSHP = SHPOpen( infile, "rb" );
+
+ if( hSHP == NULL )
+ {
+ printf( "ERROR: Unable to open the input shape file:%s\n", infile );
+ exit( 1 );
+ }
+
+ SHPGetInfo( hSHP, &nEntities, &nShapeType, NULL, NULL );
+
+/* -------------------------------------------------------------------- */
+/* Open the passed append shapefile. */
+/* -------------------------------------------------------------------- */
+ if (strcmp(outfile,"")) {
+ setext(outfile, "shp");
+ hSHPappend = SHPOpen( outfile, "rb+" );
+
+ if( hSHPappend == NULL )
+ {
+ hSHPappend = SHPCreate( outfile, nShapeType );
+ if( hSHPappend == NULL )
+ {
+ printf( "ERROR: Unable to open the append shape file:%s\n",
+ outfile );
+ exit( 1 );
+ }
+ }
+ SHPGetInfo( hSHPappend, &nEntitiesAppend, &nShapeTypeAppend,
+ NULL, NULL );
+
+ if (nShapeType != nShapeTypeAppend)
+ {
+ puts( "ERROR: Input and Append shape files are of different types.");
+ exit( 1 );
+ }
+ }
+}
+
+/* -------------------------------------------------------------------- */
+/* Change the extension. If there is any extension on the */
+/* filename, strip it off and add the new extension */
+/* -------------------------------------------------------------------- */
+void setext(char *pt, char *ext)
+{
+int i;
+ for( i = strlen(pt)-1;
+ i > 0 && pt[i] != '.' && pt[i] != '/' && pt[i] != '\\';
+ i-- ) {}
+
+ if( pt[i] == '.' )
+ pt[i] = '\0';
+
+ strcat(pt,".");
+ strcat(pt,ext);
+}
+
+
+
+/* -------------------------------------------------------------------- */
+/* Find matching fields in the append file. */
+/* Output file must have zero records to add any new fields. */
+/* -------------------------------------------------------------------- */
+void mergefields()
+{
+ int i,j;
+ ti = DBFGetFieldCount( hDBF );
+ tj = DBFGetFieldCount( hDBFappend );
+ /* Create a pointer array for the max # of fields in the output file */
+ pt = (int *) malloc( (ti+tj+1) * sizeof(int) );
+
+ for( i = 0; i < ti; i++ )
+ {
+ pt[i]= -1; /* Initial pt values to -1 */
+ }
+ /* DBF must be empty before adding items */
+ jRecord = DBFGetRecordCount( hDBFappend );
+ for( i = 0; i < ti; i++ )
+ {
+ iType = DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals );
+ found=FALSE;
+ {
+ for( j = 0; j < tj; j++ ) /* Search all field names for a match */
+ {
+ jType = DBFGetFieldInfo( hDBFappend, j, jszTitle, &jWidth, &jDecimals );
+ if (iType == jType && (strcmp(iszTitle, jszTitle) == 0) )
+ {
+ if (found || newdbf)
+ {
+ if (i == j) pt[i]=j;
+ printf("Warning: Duplicate field name found (%s)\n",iszTitle);
+ /* Duplicate field name
+ (Try to guess the correct field by position) */
+ }
+ else
+ {
+ pt[i]=j; found=TRUE;
+ }
+ }
+ }
+ }
+
+ if (pt[i] == -1 && (! found) ) /* Try to force into an existing field */
+ { /* Ignore the field name, width, and decimal places */
+ jType = DBFGetFieldInfo( hDBFappend, j, jszTitle, &jWidth, &jDecimals );
+ if (iType == jType)
+ {
+ pt[i]=i; found=1;
+ }
+ }
+ if ( (! found) && jRecord == 0) /* Add missing field to the append table */
+ { /* The output DBF must be is empty */
+ pt[i]=tj;
+ tj++;
+ if( DBFAddField( hDBFappend, iszTitle, iType, iWidth, iDecimals )
+ == -1 )
+ {
+ printf( "Warning: DBFAddField(%s, TYPE:%d, WIDTH:%d DEC:%d, ITEM#:%d of %d) failed.\n",
+ iszTitle, iType, iWidth, iDecimals, (i+1), (ti+1) );
+ pt[i]=-1;
+ }
+ }
+ }
+}
+
+
+void findselect()
+{
+ /* Find the select field name */
+ iselectitem = -1;
+ for( i = 0; i < ti && iselectitem < 0; i++ )
+ {
+ iType = DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals );
+ if (strncasecmp2(iszTitle, selectitem, 0) == 0) iselectitem = i;
+ }
+ if (iselectitem == -1)
+ {
+ printf("Warning: Item not found for selection (%s)\n",selectitem);
+ iselect = FALSE;
+ iall = FALSE;
+ showitems();
+ printf("Continued... (Selecting entire file)\n");
+ }
+ /* Extract all of the select values (by field type) */
+
+}
+
+void showitems()
+{
+ char stmp[40],slow[40],shigh[40];
+ double dtmp,dlow,dhigh,dsum,mean;
+ long int itmp,ilow,ihigh,isum;
+ long int maxrec;
+ char *pt;
+
+ printf("Available Items: (%d)",ti);
+ maxrec = DBFGetRecordCount(hDBF);
+ if (maxrec > 5000 && ! iall)
+ { maxrec=5000; printf(" ** ESTIMATED RANGES (MEAN) For more records use \"All\""); }
+ else { printf(" RANGES (MEAN)"); }
+
+ for( i = 0; i < ti; i++ )
+ {
+ switch( DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals ) )
+ {
+ case FTString:
+ case FTLogical:
+ strcpy(slow, "~");
+ strcpy(shigh,"\0");
+ printf("\n String %3d %-16s",iWidth,iszTitle);
+ for( iRecord = 0; iRecord < maxrec; iRecord++ ) {
+ strncpy(stmp,DBFReadStringAttribute( hDBF, iRecord, i ),39);
+ if (strcmp(stmp,"!!") > 0) {
+ if (strncasecmp2(stmp,slow,0) < 0) strncpy(slow, stmp,39);
+ if (strncasecmp2(stmp,shigh,0) > 0) strncpy(shigh,stmp,39);
+ }
+ }
+ pt=slow+strlen(slow)-1;
+ while(*pt == ' ') { *pt='\0'; pt--; }
+ pt=shigh+strlen(shigh)-1;
+ while(*pt == ' ') { *pt='\0'; pt--; }
+ if (strncasecmp2(slow,shigh,0) < 0) printf("%s to %s",slow,shigh);
+ else if (strncasecmp2(slow,shigh,0) == 0) printf("= %s",slow);
+ else printf("No Values");
+ break;
+ case FTInteger:
+ printf("\n Integer %3d %-16s",iWidth,iszTitle);
+ ilow = 1999999999;
+ ihigh= -1999999999;
+ isum = 0;
+ for( iRecord = 0; iRecord < maxrec; iRecord++ ) {
+ itmp = DBFReadIntegerAttribute( hDBF, iRecord, i );
+ if (ilow > itmp) ilow = itmp;
+ if (ihigh < itmp) ihigh = itmp;
+ isum = isum + itmp;
+ }
+ mean=isum/maxrec;
+ if (ilow < ihigh) printf("%ld to %ld \t(%.1f)",ilow,ihigh,mean);
+ else if (ilow == ihigh) printf("= %ld",ilow);
+ else printf("No Values");
+ break;
+
+ case FTDouble:
+ printf("\n Real %3d,%d %-16s",iWidth,iDecimals,iszTitle);
+ dlow = 999999999999999.0;
+ dhigh= -999999999999999.0;
+ dsum = 0;
+ for( iRecord = 0; iRecord < maxrec; iRecord++ ) {
+ dtmp = DBFReadDoubleAttribute( hDBF, iRecord, i );
+ if (dlow > dtmp) dlow = dtmp;
+ if (dhigh < dtmp) dhigh = dtmp;
+ dsum = dsum + dtmp;
+ }
+ mean=dsum/maxrec;
+ sprintf(stmp,"%%.%df to %%.%df \t(%%.%df)",iDecimals,iDecimals,iDecimals);
+ if (dlow < dhigh) printf(stmp,dlow,dhigh,mean);
+ else if (dlow == dhigh) {
+ sprintf(stmp,"= %%.%df",iDecimals);
+ printf(stmp,dlow);
+ }
+ else printf("No Values");
+ break;
+
+ case FTInvalid:
+ break;
+
+ }
+
+ }
+ printf("\n");
+}
+
+int selectrec()
+{
+ long int value, ty;
+
+ ty = DBFGetFieldInfo( hDBF, iselectitem, NULL, &iWidth, &iDecimals);
+ switch(ty)
+ {
+ case FTString:
+ puts("Invalid Item");
+ iselect=FALSE;
+ break;
+ case FTInteger:
+ value = DBFReadIntegerAttribute( hDBF, iRecord, iselectitem );
+ for (j = 0; j<selcount; j++)
+ {
+ if (selectvalues[j] == value)
+ {
+ if (iunselect) return(0); /* Keep this record */
+ else return(1); /* Skip this record */
+ }
+ }
+ break;
+ case FTDouble:
+ puts("Invalid Item");
+ iselect=FALSE;
+ break;
+ }
+ if (iunselect) return(1); /* Skip this record */
+ else return(0); /* Keep this record */
+}
+
+
+void check_theme_bnd()
+{
+ if ( (adfBoundsMin[0] >= cxmin) && (adfBoundsMax[0] <= cxmax) &&
+ (adfBoundsMin[1] >= cymin) && (adfBoundsMax[1] <= cymax) )
+ { /** Theme is totally inside clip area **/
+ if (ierase) nEntities=0; /** SKIP THEME **/
+ else iclip=FALSE; /** WRITE THEME (Clip not needed) **/
+ }
+
+ if ( ( (adfBoundsMin[0] < cxmin) && (adfBoundsMax[0] < cxmin) ) ||
+ ( (adfBoundsMin[1] < cymin) && (adfBoundsMax[1] < cymin) ) ||
+ ( (adfBoundsMin[0] > cxmax) && (adfBoundsMax[0] > cxmax) ) ||
+ ( (adfBoundsMin[1] > cymax) && (adfBoundsMax[1] > cymax) ) )
+ { /** Theme is totally outside clip area **/
+ if (ierase) iclip=FALSE; /** WRITE THEME (Clip not needed) **/
+ else nEntities=0; /** SKIP THEME **/
+ }
+
+ if (nEntities == 0)
+ puts("WARNING: Theme is outside the clip area."); /** SKIP THEME **/
+}
+
+int clip_boundary()
+{
+ int inside;
+ int prev_outside;
+ int i2;
+ int j2;
+
+ /*** FIRST check the boundary of the feature ***/
+ if ( ( (psCShape->dfXMin < cxmin) && (psCShape->dfXMax < cxmin) ) ||
+ ( (psCShape->dfYMin < cymin) && (psCShape->dfYMax < cymin) ) ||
+ ( (psCShape->dfXMin > cxmax) && (psCShape->dfXMax > cxmax) ) ||
+ ( (psCShape->dfYMin > cymax) && (psCShape->dfYMax > cymax) ) )
+ { /** Feature is totally outside clip area **/
+ if (ierase) return(1); /** WRITE RECORD **/
+ else return(0); /** SKIP RECORD **/
+ }
+
+ if ( (psCShape->dfXMin >= cxmin) && (psCShape->dfXMax <= cxmax) &&
+ (psCShape->dfYMin >= cymin) && (psCShape->dfYMax <= cymax) )
+ { /** Feature is totally inside clip area **/
+ if (ierase) return(0); /** SKIP RECORD **/
+ else return(1); /** WRITE RECORD **/
+ }
+
+ if (iinside)
+ { /** INSIDE * Feature might touch the boundary or could be outside **/
+ if (ierase) return(1); /** WRITE RECORD **/
+ else return(0); /** SKIP RECORD **/
+ }
+
+ if (itouch)
+ { /** TOUCH **/
+ if ( ( (psCShape->dfXMin <= cxmin) || (psCShape->dfXMax >= cxmax) ) &&
+ (psCShape->dfYMin >= cymin) && (psCShape->dfYMax <= cymax) )
+ { /** Feature intersects the clip boundary only on the X axis **/
+ if (ierase) return(0); /** SKIP RECORD **/
+ else return(1); /** WRITE RECORD **/
+ }
+
+ if ( (psCShape->dfXMin >= cxmin) && (psCShape->dfXMax <= cxmax) &&
+ ( (psCShape->dfYMin <= cymin) || (psCShape->dfYMax >= cymax) ) )
+ { /** Feature intersects the clip boundary only on the Y axis **/
+ if (ierase) return(0); /** SKIP RECORD **/
+ else return(1); /** WRITE RECORD **/
+ }
+
+ for( j2 = 0; j2 < psCShape->nVertices; j2++ )
+ { /** At least one vertex must be inside the clip boundary **/
+ if ( (psCShape->padfX[j2] >= cxmin && psCShape->padfX[j2] <= cxmax) ||
+ (psCShape->padfY[j2] >= cymin && psCShape->padfY[j2] <= cymax) )
+ {
+ if (ierase) return(0); /** SKIP RECORD **/
+ else return(1); /** WRITE RECORD **/
+ }
+ }
+
+ /** All vertices are outside the clip boundary **/
+ if (ierase) return(1); /** WRITE RECORD **/
+ else return(0); /** SKIP RECORD **/
+ } /** End TOUCH **/
+
+ if (icut)
+ { /** CUT **/
+ /*** Check each vertex in the feature with the Boundary and "CUT" ***/
+ /*** THIS CODE WAS NOT COMPLETED! READ NOTE AT THE BOTTOM ***/
+ i2=0;
+ prev_outside=FALSE;
+ for( j2 = 0; j2 < psCShape->nVertices; j2++ )
+ {
+ inside = psCShape->padfX[j2] >= cxmin && psCShape->padfX[j2] <= cxmax &&
+ psCShape->padfY[j2] >= cymin && psCShape->padfY[j2] <= cymax ;
+
+ if (ierase) inside=(! inside);
+ if (inside)
+ {
+ if (i2 != j2)
+ {
+ if (prev_outside)
+ {
+ /*** AddIntersection(i2); ***/ /*** Add intersection ***/
+ prev_outside=FALSE;
+ }
+ psCShape->padfX[i2]=psCShape->padfX[j2]; /** move vertex **/
+ psCShape->padfY[i2]=psCShape->padfY[j2];
+ }
+ i2++;
+ } else {
+ if ( (! prev_outside) && (j2 > 0) )
+ {
+ /*** AddIntersection(i2); ***//*** Add intersection (Watch out for j2==i2-1) ***/
+ /*** Also a polygon may overlap twice and will split into a several parts ***/
+ prev_outside=TRUE;
+ }
+ }
+ }
+
+ printf("Vertices:%d OUT:%d Number of Parts:%d\n",
+ psCShape->nVertices,i2, psCShape->nParts );
+
+ psCShape->nVertices = i2;
+
+ if (i2 < 2) return(0); /** SKIP RECORD **/
+ /*** (WE ARE NOT CREATING INTERESECTIONS and some lines could be reduced to one point) **/
+
+ if (i2 == 0) return(0); /** SKIP RECORD **/
+ else return(1); /** WRITE RECORD **/
+ } /** End CUT **/
+}
+
+
+/************************************************************************/
+/* strncasecmp2() */
+/* */
+/* Compare two strings up to n characters */
+/* If n=0 then s1 and s2 must be an exact match */
+/************************************************************************/
+
+int strncasecmp2(char *s1, char *s2, int n)
+
+{
+int j,i;
+ if (n<1) n=strlen(s1)+1;
+ for (i=0; i<n; i++)
+ {
+ if (*s1 != *s2)
+ {
+ if (*s1 >= 'a' && *s1 <= 'z') {
+ j=*s1-32;
+ if (j != *s2) return(*s1-*s2);
+ } else {
+ if (*s1 >= 'A' && *s1 <= 'Z') { j=*s1+32; }
+ else { j=*s1; }
+ if (j != *s2) return(*s1-*s2);
+ }
+ }
+ s1++;
+ s2++;
+ }
+ return(0);
+}
+
+
+#define NKEYS (sizeof(unitkeytab) / sizeof(struct unitkey))
+double findunit(char *unit)
+ {
+ struct unitkey {
+ char *name;
+ double value;
+ } unitkeytab[] = {
+ "CM", 39.37,
+ "CENTIMETER", 39.37,
+ "CENTIMETERS", 39.37, /** # of inches * 100 in unit **/
+ "METER", 3937,
+ "METERS", 3937,
+ "KM", 3937000,
+ "KILOMETER", 3937000,
+ "KILOMETERS", 3937000,
+ "INCH", 100,
+ "INCHES", 100,
+ "FEET", 1200,
+ "FOOT", 1200,
+ "YARD", 3600,
+ "YARDS", 3600,
+ "MILE", 6336000,
+ "MILES", 6336000
+ };
+
+ double unitfactor=0;
+ for (j = 0; j < NKEYS; j++) {
+ if (strncasecmp2(unit, unitkeytab[j].name, 0) == 0) unitfactor=unitkeytab[j].value;
+ }
+ return(unitfactor);
+}
+
+/* -------------------------------------------------------------------- */
+/* Display a usage message. */
+/* -------------------------------------------------------------------- */
+void error()
+{
+ puts( "The program will append to an existing shape file or it will" );
+ puts( "create a new file if needed." );
+ puts( "Only the items in the first output file will be preserved." );
+ puts( "When an item does not match with the append theme then the item");
+ puts( "might be placed to an existing item at the same position and type." );
+ puts( " OTHER FUNCTIONS:" );
+ puts( " - Describe all items in the dbase file (Use ALL for more than 5000 recs.)");
+ puts( " - Select a group of shapes from a comma separated selection list.");
+ puts( " - UnSelect a group of shapes from a comma separated selection list.");
+ puts( " - Clip boundary extent or by theme boundary." );
+ puts( " Touch writes all the shapes that touch the boundary.");
+ puts( " Inside writes all the shapes that are completely within the boundary.");
+ puts( " Boundary clips are only the min and max of a theme boundary." );
+ puts( " - Erase boundary extent or by theme boundary." );
+ puts( " Erase is the direct opposite of the Clip function." );
+ puts( " - Change coordinate value units between meters and feet.");
+ puts( " There is no way to determine the input unit of a shape file.");
+ puts( " Skip this function if the shape file is already in the correct unit.");
+ puts( " Clip and Erase will be done before the unit is changed.");
+ puts( " A shift will be done after the unit is changed.");
+ puts( " - Shift X and Y coordinates.\n" );
+ puts( "Finally, There can only be one select or unselect in the command line.");
+ puts( " There can only be one clip or erase in the command line.");
+ puts( " There can only be one unit and only one shift in the command line.\n");
+ puts( "Ex: shputils in.shp out.shp SELECT countycode 3,5,9,13,17,27");
+ puts( " shputils in.shp out.shp CLIP 10 10 90 90 Touch FACTOR Meter Feet");
+ puts( " shputils in.shp out.shp FACTOR Meter 3.0");
+ puts( " shputils in.shp out.shp CLIP clip.shp Boundary Touch SHIFT 40 40");
+ puts( " shputils in.shp out.shp SELECT co 112 CLIP clip.shp Boundary Touch\n");
+ puts( "USAGE: shputils <DescribeShape> {ALL}");
+ puts( "USAGE: shputils <InputShape> <OutShape|AppendShape>" );
+ puts( " { <FACTOR> <FEET|MILES|METERS|KM> <FEET|MILES|METERS|KM|factor> }" );
+ puts( " { <SHIFT> <xshift> <yshift> }" );
+ puts( " { <SELECT|UNSEL> <Item> <valuelist> }" );
+ puts( " { <CLIP|ERASE> <xmin> <ymin> <xmax> <ymax> <TOUCH|INSIDE|CUT> }" );
+ puts( " { <CLIP|ERASE> <theme> <BOUNDARY> <TOUCH|INSIDE|CUT> }" );
+ puts( " Note: CUT is not complete and does not create intersections.");
+ puts( " For more information read programmer comment.");
+
+ /**** Clip functions for Polygon and Cut is not supported
+ There are several web pages that describe methods of doing this function.
+ It seem easy to impliment until you start writting code. I don't have the
+ time to add these functions but a did leave a simple cut routine in the
+ program that can be called by using CUT instead of TOUCH in the
+ CLIP or ERASE functions. It does not add the intersection of the line and
+ the clip box, so polygons could look incomplete and lines will come up short.
+
+ Information about clipping lines with a box:
+ http://www.csclub.uwaterloo.ca/u/mpslager/articles/sutherland/wr.html
+ Information about finding the intersection of two lines:
+ http://www.whisqu.se/per/docs/math28.htm
+
+ THE CODE LOOKS LIKE THIS:
+ ********************************************************
+ void Intersect_Lines(float x0,float y0,float x1,float y1,
+ float x2,float y2,float x3,float y3,
+ float *xi,float *yi)
+ {
+// this function computes the intersection of the sent lines
+// and returns the intersection point, note that the function assumes
+// the lines intersect. the function can handle vertical as well
+// as horizontal lines. note the function isn't very clever, it simply
+// applies the math, but we don't need speed since this is a
+// pre-processing step
+// The Intersect_lines program came from (http://www.whisqu.se/per/docs/math28.htm)
+
+float a1,b1,c1, // constants of linear equations
+a2,b2,c2,
+det_inv, // the inverse of the determinant of the coefficientmatrix
+m1,m2; // the slopes of each line
+
+// compute slopes, note the cludge for infinity, however, this will
+// be close enough
+if ((x1-x0)!=0)
+m1 = (y1-y0)/(x1-x0);
+else
+m1 = (float)1e+10; // close enough to infinity
+
+
+if ((x3-x2)!=0)
+m2 = (y3-y2)/(x3-x2);
+else
+m2 = (float)1e+10; // close enough to infinity
+
+// compute constants
+a1 = m1;
+a2 = m2;
+b1 = -1;
+b2 = -1;
+c1 = (y0-m1*x0);
+c2 = (y2-m2*x2);
+// compute the inverse of the determinate
+det_inv = 1/(a1*b2 - a2*b1);
+// use Kramers rule to compute xi and yi
+*xi=((b1*c2 - b2*c1)*det_inv);
+*yi=((a2*c1 - a1*c2)*det_inv);
+} // end Intersect_Lines
+ **********************************************************/
+
+ exit( 1 );
+}