]> granicus.if.org Git - postgis/commitdiff
Initial add of the data loader code.
authorPaul Ramsey <pramsey@cleverelephant.ca>
Wed, 18 Jul 2001 21:42:12 +0000 (21:42 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Wed, 18 Jul 2001 21:42:12 +0000 (21:42 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@16 b70326c6-7e19-0410-871a-916f4a2858ee

loader/Makefile [new file with mode: 0644]
loader/README [new file with mode: 0644]
loader/dbfopen.c [new file with mode: 0644]
loader/pop.c [new file with mode: 0644]
loader/shapefil.h [new file with mode: 0644]
loader/shpopen.c [new file with mode: 0644]

diff --git a/loader/Makefile b/loader/Makefile
new file mode 100644 (file)
index 0000000..e727a19
--- /dev/null
@@ -0,0 +1,12 @@
+OBJS = pop.o shpopen.o dbfopen.o
+
+defailt: all
+
+all: pop
+
+pop: $(OBJS)
+
+clean:
+       @rm -f $(OBJS) pop
+
+
diff --git a/loader/README b/loader/README
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/loader/dbfopen.c b/loader/dbfopen.c
new file mode 100644 (file)
index 0000000..7903506
--- /dev/null
@@ -0,0 +1,1074 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  Shapelib
+ * Purpose:  Implementation of .dbf access API documented in dbf_api.html.
+ * Author:   Frank Warmerdam, warmerda@home.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$
+ * Revision 1.1  2001/07/18 21:42:12  pramsey
+ * Initial add of the data loader code.
+ *
+ * Revision 1.22  1999/12/15 13:47:24  warmerda
+ * Added stdlib.h to ensure that atof() is prototyped.
+ *
+ * Revision 1.21  1999/12/13 17:25:46  warmerda
+ * Added support for upper case .DBF extention.
+ *
+ * Revision 1.20  1999/11/30 16:32:11  warmerda
+ * Use atof() instead of sscanf().
+ *
+ * Revision 1.19  1999/11/05 14:12:04  warmerda
+ * updated license terms
+ *
+ * Revision 1.18  1999/07/27 00:53:28  warmerda
+ * ensure that whole old field value clear on write of string
+ *
+ * Revision 1.1  1999/07/05 18:58:07  warmerda
+ * New
+ *
+ * Revision 1.17  1999/06/11 19:14:12  warmerda
+ * Fixed some memory leaks.
+ *
+ * Revision 1.16  1999/06/11 19:04:11  warmerda
+ * Remoted some unused variables.
+ *
+ * Revision 1.15  1999/05/11 03:19:28  warmerda
+ * added new Tuple api, and improved extension handling - add from candrsn
+ *
+ * Revision 1.14  1999/05/04 15:01:48  warmerda
+ * Added 'F' support.
+ *
+ * Revision 1.13  1999/03/23 17:38:59  warmerda
+ * DBFAddField() now actually does return the new field number, or -1 if
+ * it fails.
+ *
+ * Revision 1.12  1999/03/06 02:54:46  warmerda
+ * Added logic to convert shapefile name to dbf filename in DBFOpen()
+ * for convenience.
+ *
+ * Revision 1.11  1998/12/31 15:30:34  warmerda
+ * Improved the interchangability of numeric and string attributes.  Add
+ * white space trimming option for attributes.
+ *
+ * Revision 1.10  1998/12/03 16:36:44  warmerda
+ * Use r+b instead of rb+ for binary access.
+ *
+ * Revision 1.9  1998/12/03 15:34:23  warmerda
+ * Updated copyright message.
+ *
+ * Revision 1.8  1997/12/04 15:40:15  warmerda
+ * Added newline character after field definitions.
+ *
+ * Revision 1.7  1997/03/06 14:02:10  warmerda
+ * Ensure bUpdated is initialized.
+ *
+ * Revision 1.6  1996/02/12 04:54:41  warmerda
+ * Ensure that DBFWriteAttribute() returns TRUE if it succeeds.
+ *
+ * Revision 1.5  1995/10/21  03:15:12  warmerda
+ * Changed to use binary file access, and ensure that the
+ * field name field is zero filled, and limited to 10 chars.
+ *
+ * Revision 1.4  1995/08/24  18:10:42  warmerda
+ * Added use of SfRealloc() to avoid pre-ANSI realloc() functions such
+ * as on the Sun.
+ *
+ * Revision 1.3  1995/08/04  03:15:16  warmerda
+ * Fixed up header.
+ *
+ * Revision 1.2  1995/08/04  03:14:43  warmerda
+ * Added header.
+ */
+
+static char rcsid[] = 
+  "$Id$";
+
+#include "shapefil.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+typedef unsigned char uchar;
+
+#ifndef FALSE
+#  define FALSE                0
+#  define TRUE         1
+#endif
+
+static int     nStringFieldLen = 0;
+static char * pszStringField = NULL;
+
+/************************************************************************/
+/*                             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)
+
+{
+    uchar      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   */
+
+    /* date updated on close, record count preset at zero */
+
+    abyHeader[8] = psDBF->nHeaderLength % 256;
+    abyHeader[9] = psDBF->nHeaderLength / 256;
+    
+    abyHeader[10] = psDBF->nRecordLength % 256;
+    abyHeader[11] = psDBF->nRecordLength / 256;
+
+/* -------------------------------------------------------------------- */
+/*      Write the initial 32 byte file header, and all the field        */
+/*      descriptions.                                                  */
+/* -------------------------------------------------------------------- */
+    fseek( psDBF->fp, 0, 0 );
+    fwrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp );
+    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;
+        fwrite( &cNewline, 1, 1, psDBF->fp );
+    }
+}
+
+/************************************************************************/
+/*                           DBFFlushRecord()                           */
+/*                                                                      */
+/*      Write out the current record if there is one.                   */
+/************************************************************************/
+
+static void DBFFlushRecord( DBFHandle psDBF )
+
+{
+    int                nRecordOffset;
+
+    if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 )
+    {
+       psDBF->bCurrentRecordModified = FALSE;
+
+       nRecordOffset = psDBF->nRecordLength * psDBF->nCurrentRecord 
+                                                    + psDBF->nHeaderLength;
+
+       fseek( psDBF->fp, nRecordOffset, 0 );
+       fwrite( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
+    }
+}
+
+/************************************************************************/
+/*                              DBFOpen()                               */
+/*                                                                      */
+/*      Open a .dbf file.                                               */
+/************************************************************************/
+   
+DBFHandle DBFOpen( const char * pszFilename, const char * pszAccess )
+
+{
+    DBFHandle          psDBF;
+    uchar              *pabyBuf;
+    int                        nFields, nRecords, nHeadLen, nRecLen, iField, i;
+    char               *pszBasename, *pszFullname;
+
+/* -------------------------------------------------------------------- */
+/*      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 );
+
+/* -------------------------------------------------------------------- */
+/*     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 = fopen( pszFullname, pszAccess );
+
+    if( psDBF->fp == NULL )
+    {
+        sprintf( pszFullname, "%s.DBF", pszBasename );
+        psDBF->fp = fopen(pszFullname, pszAccess );
+    }
+    
+    free( pszBasename );
+    free( pszFullname );
+    
+    if( psDBF->fp == NULL )
+    {
+        free( psDBF );
+        return( NULL );
+    }
+
+    psDBF->bNoHeader = FALSE;
+    psDBF->nCurrentRecord = -1;
+    psDBF->bCurrentRecordModified = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*  Read Table Header info                                              */
+/* -------------------------------------------------------------------- */
+    pabyBuf = (uchar *) malloc(500);
+    fread( pabyBuf, 32, 1, psDBF->fp );
+
+    psDBF->nRecords = 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 = nRecLen = pabyBuf[10] + pabyBuf[11]*256;
+    
+    psDBF->nFields = nFields = (nHeadLen - 32) / 32;
+
+    psDBF->pszCurrentRecord = (char *) malloc(nRecLen);
+
+/* -------------------------------------------------------------------- */
+/*  Read in Field Definitions                                           */
+/* -------------------------------------------------------------------- */
+    
+    pabyBuf = (uchar *) SfRealloc(pabyBuf,nHeadLen);
+    psDBF->pszHeader = (char *) pabyBuf;
+
+    fseek( psDBF->fp, 32, 0 );
+    fread( pabyBuf, nHeadLen, 1, psDBF->fp );
+
+    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++ )
+    {
+       uchar           *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] + 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   DBFClose(DBFHandle psDBF)
+{
+/* -------------------------------------------------------------------- */
+/*      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 )
+    {
+       uchar           abyFileHeader[32];
+
+       fseek( psDBF->fp, 0, 0 );
+       fread( abyFileHeader, 32, 1, psDBF->fp );
+
+       abyFileHeader[1] = 95;                  /* YY */
+       abyFileHeader[2] = 7;                   /* MM */
+       abyFileHeader[3] = 26;                  /* DD */
+
+       abyFileHeader[4] = psDBF->nRecords % 256;
+       abyFileHeader[5] = (psDBF->nRecords/256) % 256;
+       abyFileHeader[6] = (psDBF->nRecords/(256*256)) % 256;
+       abyFileHeader[7] = (psDBF->nRecords/(256*256*256)) % 256;
+
+       fseek( psDBF->fp, 0, 0 );
+       fwrite( abyFileHeader, 32, 1, psDBF->fp );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Close, and free resources.                                      */
+/* -------------------------------------------------------------------- */
+    fclose( psDBF->fp );
+
+    if( psDBF->panFieldOffset != NULL )
+    {
+        free( psDBF->panFieldOffset );
+        free( psDBF->panFieldSize );
+        free( psDBF->panFieldDecimals );
+        free( psDBF->pachFieldType );
+    }
+
+    free( psDBF->pszHeader );
+    free( psDBF->pszCurrentRecord );
+
+    free( psDBF );
+
+    if( pszStringField != NULL )
+    {
+        free( pszStringField );
+        pszStringField = NULL;
+        nStringFieldLen = 0;
+    }
+}
+
+/************************************************************************/
+/*                             DBFCreate()                              */
+/*                                                                      */
+/*      Create a new .dbf file.                                         */
+/************************************************************************/
+
+DBFHandle DBFCreate( const char * pszFilename )
+
+{
+    DBFHandle  psDBF;
+    FILE       *fp;
+    char       *pszFullname, *pszBasename;
+    int                i;
+
+/* -------------------------------------------------------------------- */
+/*     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 );
+    free( pszBasename );
+
+/* -------------------------------------------------------------------- */
+/*      Create the file.                                                */
+/* -------------------------------------------------------------------- */
+    fp = fopen( pszFullname, "wb" );
+    if( fp == NULL )
+        return( NULL );
+
+    fputc( 0, fp );
+    fclose( fp );
+
+    fp = fopen( pszFullname, "rb+" );
+    if( fp == NULL )
+        return( NULL );
+
+    free( pszFullname );
+
+/* -------------------------------------------------------------------- */
+/*     Create the info structure.                                      */
+/* -------------------------------------------------------------------- */
+    psDBF = (DBFHandle) malloc(sizeof(DBFInfo));
+
+    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;
+
+    return( psDBF );
+}
+
+/************************************************************************/
+/*                            DBFAddField()                             */
+/*                                                                      */
+/*      Add a field to a newly created .dbf file before any records     */
+/*      are written.                                                    */
+/************************************************************************/
+
+int    DBFAddField(DBFHandle psDBF, const char * pszFieldName, 
+                   DBFFieldType eType, int nWidth, int nDecimals )
+
+{
+    char       *pszFInfo;
+    int                i;
+
+/* -------------------------------------------------------------------- */
+/*      Do some checking to ensure we can add records to this file.     */
+/* -------------------------------------------------------------------- */
+    if( psDBF->nRecords > 0 )
+        return( -1 );
+
+    if( !psDBF->bNoHeader )
+        return( -1 );
+
+    if( eType != FTDouble && nDecimals != 0 )
+        return( -1 );
+
+/* -------------------------------------------------------------------- */
+/*      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;
+
+    if( eType == FTString )
+        psDBF->pachFieldType[psDBF->nFields-1] = 'C';
+    else
+        psDBF->pachFieldType[psDBF->nFields-1] = 'N';
+
+/* -------------------------------------------------------------------- */
+/*      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( strlen(pszFieldName) < 10 )
+        strncpy( pszFInfo, pszFieldName, strlen(pszFieldName));
+    else
+        strncpy( pszFInfo, pszFieldName, 10);
+
+    pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1];
+
+    if( eType == FTString )
+    {
+        pszFInfo[16] = nWidth % 256;
+        pszFInfo[17] = nWidth / 256;
+    }
+    else
+    {
+        pszFInfo[16] = nWidth;
+        pszFInfo[17] = nDecimals;
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Make the current record buffer appropriately larger.            */
+/* -------------------------------------------------------------------- */
+    psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
+                                              psDBF->nRecordLength);
+
+    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 )
+
+{
+    int                nRecordOffset;
+    uchar      *pabyRec;
+    void       *pReturnField = NULL;
+
+    static double dDoubleField;
+
+/* -------------------------------------------------------------------- */
+/*     Have we read the record?                                        */
+/* -------------------------------------------------------------------- */
+    if( hEntity < 0 || hEntity >= psDBF->nRecords )
+        return( NULL );
+
+    if( psDBF->nCurrentRecord != hEntity )
+    {
+       DBFFlushRecord( psDBF );
+
+       nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
+
+       fseek( psDBF->fp, nRecordOffset, 0 );
+       fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
+
+       psDBF->nCurrentRecord = hEntity;
+    }
+
+    pabyRec = (uchar *) psDBF->pszCurrentRecord;
+
+/* -------------------------------------------------------------------- */
+/*     Ensure our field buffer is large enough to hold this buffer.    */
+/* -------------------------------------------------------------------- */
+    if( psDBF->panFieldSize[iField]+1 > nStringFieldLen )
+    {
+       nStringFieldLen = psDBF->panFieldSize[iField]*2 + 10;
+       pszStringField = (char *) SfRealloc(pszStringField,nStringFieldLen);
+    }
+
+/* -------------------------------------------------------------------- */
+/*     Extract the requested field.                                    */
+/* -------------------------------------------------------------------- */
+    strncpy( pszStringField, pabyRec+psDBF->panFieldOffset[iField],
+            psDBF->panFieldSize[iField] );
+    pszStringField[psDBF->panFieldSize[iField]] = '\0';
+
+    pReturnField = pszStringField;
+
+/* -------------------------------------------------------------------- */
+/*      Decode the field.                                               */
+/* -------------------------------------------------------------------- */
+    if( chReqType == 'N' )
+    {
+        dDoubleField = atof(pszStringField);
+
+       pReturnField = &dDoubleField;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Should we trim white space off the string attribute value?      */
+/* -------------------------------------------------------------------- */
+#ifdef TRIM_DBF_WHITESPACE
+    else
+    {
+        char   *pchSrc, *pchDst;
+
+        pchDst = pchSrc = pszStringField;
+        while( *pchSrc == ' ' )
+            pchSrc++;
+
+        while( *pchSrc != '\0' )
+            *(pchDst++) = *(pchSrc++);
+        *pchDst = '\0';
+
+        while( *(--pchDst) == ' ' && pchDst != pszStringField )
+            *pchDst = '\0';
+
+    }
+#endif
+    
+    return( pReturnField );
+}
+
+/************************************************************************/
+/*                        DBFReadIntAttribute()                         */
+/*                                                                      */
+/*      Read an integer attribute.                                      */
+/************************************************************************/
+
+int    DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField )
+
+{
+    double     *pdValue;
+
+    pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
+
+    return( (int) *pdValue );
+}
+
+/************************************************************************/
+/*                        DBFReadDoubleAttribute()                      */
+/*                                                                      */
+/*      Read a double attribute.                                        */
+/************************************************************************/
+
+double DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField )
+
+{
+    double     *pdValue;
+
+    pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' );
+
+    return( *pdValue );
+}
+
+/************************************************************************/
+/*                        DBFReadStringAttribute()                      */
+/*                                                                      */
+/*      Read a string attribute.                                        */
+/************************************************************************/
+
+const char *DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField )
+
+{
+    return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) );
+}
+
+/************************************************************************/
+/*                          DBFGetFieldCount()                          */
+/*                                                                      */
+/*      Return the number of fields in this table.                      */
+/************************************************************************/
+
+int    DBFGetFieldCount( DBFHandle psDBF )
+
+{
+    return( psDBF->nFields );
+}
+
+/************************************************************************/
+/*                         DBFGetRecordCount()                          */
+/*                                                                      */
+/*      Return the number of records in this table.                     */
+/************************************************************************/
+
+int    DBFGetRecordCount( DBFHandle psDBF )
+
+{
+    return( psDBF->nRecords );
+}
+
+/************************************************************************/
+/*                          DBFGetFieldInfo()                           */
+/*                                                                      */
+/*      Return any requested information about the field.               */
+/************************************************************************/
+
+DBFFieldType 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] == 'N' 
+        || psDBF->pachFieldType[iField] == 'F'
+        || psDBF->pachFieldType[iField] == 'D' )
+    {
+       if( psDBF->panFieldDecimals[iField] > 0 )
+           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                nRecordOffset, i, j;
+    uchar      *pabyRec;
+    char       szSField[40], szFormat[12];
+
+/* -------------------------------------------------------------------- */
+/*     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 )
+    {
+       DBFFlushRecord( psDBF );
+
+       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( psDBF->nCurrentRecord != hEntity )
+    {
+       DBFFlushRecord( psDBF );
+
+       nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
+
+       fseek( psDBF->fp, nRecordOffset, 0 );
+       fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
+
+       psDBF->nCurrentRecord = hEntity;
+    }
+
+    pabyRec = (uchar *) psDBF->pszCurrentRecord;
+
+/* -------------------------------------------------------------------- */
+/*      Assign all the record fields.                                   */
+/* -------------------------------------------------------------------- */
+       switch( psDBF->pachFieldType[iField] )
+    {
+      case 'D':
+      case 'N':
+      case 'F':
+  if( psDBF->panFieldDecimals[iField] == 0 )
+       {
+               sprintf( szFormat, "%%%dd", psDBF->panFieldSize[iField] );
+           sprintf(szSField, szFormat, (int) *((double *) pValue) );
+           if( strlen(szSField) > psDBF->panFieldSize[iField] )
+               szSField[psDBF->panFieldSize[iField]] = '\0';
+           strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
+                   szSField, strlen(szSField) );
+       }
+       else
+       {
+           sprintf( szFormat, "%%%d.%df", 
+                    psDBF->panFieldSize[iField],
+                    psDBF->panFieldDecimals[iField] );
+           sprintf(szSField, szFormat, *((double *) pValue) );
+           if( strlen(szSField) > psDBF->panFieldSize[iField] )
+               szSField[psDBF->panFieldSize[iField]] = '\0';
+           strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]),
+                   szSField, strlen(szSField) );
+       }
+
+       break;
+
+      default:
+       if( 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 );
+       break;
+    }
+
+    psDBF->bCurrentRecordModified = TRUE;
+    psDBF->bUpdated = TRUE;
+
+    return( TRUE );
+}
+
+/************************************************************************/
+/*                      DBFWriteDoubleAttribute()                       */
+/*                                                                      */
+/*      Write a double attribute.                                       */
+/************************************************************************/
+
+int DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField,
+                            double dValue )
+
+{
+    return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) );
+}
+
+/************************************************************************/
+/*                      DBFWriteIntegerAttribute()                      */
+/*                                                                      */
+/*      Write a integer attribute.                                      */
+/************************************************************************/
+
+int 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 DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField,
+                            const char * pszValue )
+
+{
+    return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) );
+}
+
+/************************************************************************/
+/*                         DBFWriteTuple()                              */
+/*                                                                     */
+/*     Write an attribute record to the file.                          */
+/************************************************************************/
+
+int DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple )
+
+{
+    int                nRecordOffset, i;
+    uchar      *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 )
+    {
+       DBFFlushRecord( psDBF );
+
+       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( psDBF->nCurrentRecord != hEntity )
+    {
+       DBFFlushRecord( psDBF );
+
+       nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
+
+       fseek( psDBF->fp, nRecordOffset, 0 );
+       fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
+
+       psDBF->nCurrentRecord = hEntity;
+    }
+
+    pabyRec = (uchar *) psDBF->pszCurrentRecord;
+
+    memcpy ( pabyRec, pRawTuple,  psDBF->nRecordLength );
+
+    psDBF->bCurrentRecordModified = TRUE;
+    psDBF->bUpdated = TRUE;
+
+    return( TRUE );
+}
+
+/************************************************************************/
+/*                          DBFReadTuple()                              */
+/*                                                                      */
+/*      Read one of the attribute fields of a record.                   */
+/************************************************************************/
+
+const char *DBFReadTuple(DBFHandle psDBF, int hEntity )
+
+{
+    int                nRecordOffset;
+    uchar      *pabyRec;
+    static char        *pReturnTuple = NULL;
+
+    static int nTupleLen = 0;
+
+/* -------------------------------------------------------------------- */
+/*     Have we read the record?                                        */
+/* -------------------------------------------------------------------- */
+    if( hEntity < 0 || hEntity >= psDBF->nRecords )
+        return( NULL );
+
+    if( psDBF->nCurrentRecord != hEntity )
+    {
+       DBFFlushRecord( psDBF );
+
+       nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
+
+       fseek( psDBF->fp, nRecordOffset, 0 );
+       fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
+
+       psDBF->nCurrentRecord = hEntity;
+    }
+
+    pabyRec = (uchar *) psDBF->pszCurrentRecord;
+
+    if ( nTupleLen < psDBF->nRecordLength) {
+      nTupleLen = psDBF->nRecordLength;
+      pReturnTuple = (char *) SfRealloc(pReturnTuple, psDBF->nRecordLength);
+    }
+    
+    memcpy ( pReturnTuple, pabyRec, psDBF->nRecordLength );
+        
+    return( pReturnTuple );
+}
+
+/************************************************************************/
+/*                          DBFCloneEmpty()                              */
+/*                                                                      */
+/*      Read one of the attribute fields of a record.                   */
+/************************************************************************/
+
+DBFHandle DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) 
+{
+    DBFHandle  newDBF;
+
+   newDBF = DBFCreate ( pszFilename );
+   if ( newDBF == NULL ) return ( NULL ); 
+   
+   newDBF->pszHeader = (void *) malloc ( 32 * psDBF->nFields );
+   memcpy ( newDBF->pszHeader, psDBF->pszHeader, 32 * psDBF->nFields );
+   
+   newDBF->nFields = psDBF->nFields;
+   newDBF->nRecordLength = psDBF->nRecordLength;
+   newDBF->nHeaderLength = psDBF->nHeaderLength;
+    
+   newDBF->panFieldOffset = (void *) malloc ( sizeof(int) * psDBF->nFields ); 
+   memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
+   newDBF->panFieldSize = (void *) malloc ( sizeof(int) * psDBF->nFields );
+   memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
+   newDBF->panFieldDecimals = (void *) malloc ( sizeof(int) * psDBF->nFields );
+   memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
+   newDBF->pachFieldType = (void *) malloc ( sizeof(int) * psDBF->nFields );
+   memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(int) * psDBF->nFields );
+
+   newDBF->bNoHeader = TRUE;
+   newDBF->bUpdated = TRUE;
+   
+   DBFWriteHeader ( newDBF );
+   DBFClose ( newDBF );
+   
+   newDBF = DBFOpen ( pszFilename, "rb+" );
+
+   return ( newDBF );
+}
diff --git a/loader/pop.c b/loader/pop.c
new file mode 100644 (file)
index 0000000..76b9414
--- /dev/null
@@ -0,0 +1,623 @@
+//compile line for Solaris
+//gcc -g pop.c ../shapelib-1.2.8/shpopen.o ../shapelib-1.2.8/dbfopen.o -o pop
+
+//  usage:   pop  <shapefile to process>  <table name> [-d || -a || -c] | psql -h <host> -d <database> -p <port> ...
+// -d: drops the table , then recreates it and populates it with current shape file data
+// -a: appends shape file into current table, must be excatly the same table schema
+// -c: creates a new table and populates it, this is the default if you don't specify any options
+
+//     Using shapelib 1.2.8, this program reads in shape files and processes it's contents
+//     into a Insert statements which can be easily piped into a database frontend.
+//     Specifically designed to insert type 'geometry' (a custom written PostgreSQL type)
+//     for the shape files and PostgreSQL standard types for all attributes of the entity. 
+//
+//     Basically the program determines which type of shape (currently supports: 2d points,2d lines,2d
+//  polygons,3d points, and 3d lines) is in the file and takes appropriate action to read out the attributes.
+
+
+
+#include "../shapelib-1.2.8/shapefil.h"
+#include <stdio.h>
+
+typedef struct {double x, y;} Point;
+
+typedef struct Ring{
+       Point *list;    //list of points
+       struct Ring  *next;
+       int             n;              //number of points in list
+} Ring;
+
+int Insert_attributes(DBFHandle hDBFHandle, char **ARGV, int row);
+
+// PIP(): crossing number test for a point in a polygon
+//      input:   P = a point,
+//               V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
+//      returns: 0 = outside, 1 = inside
+int PIP( Point P, Point* V, int n )
+{
+    int cn = 0;    // the crossing number counter
+       int i;
+    // loop through all edges of the polygon
+    for (i=0; i<n; i++) {    // edge from V[i] to V[i+1]
+       if (((V[i].y <= P.y) && (V[i+1].y > P.y))    // an upward crossing
+        || ((V[i].y > P.y) && (V[i+1].y <= P.y))) { // a downward crossing
+            double vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
+            if (P.x < V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
+                ++cn;   // a valid crossing of y=P.y right of P.x
+        }
+    }
+    return (cn&1);    // 0 if even (out), and 1 if odd (in)
+
+}
+
+
+//This function basically deals with the polygon case.
+ //it sorts the polys in order of outer,inner,inner, so that inners always come after outers they are within 
+ //return value is the number of rings seen so far, used to keep id's unique.
+int ring_check(SHPObject* obj, char **ARGV, int rings, DBFHandle hDBFHandle){
+       Point pt,pt2;
+       Ring *Poly;
+       Ring *temp;
+       Ring **Outer; //pointer to a list of outer polygons
+       Ring **Inner; //pointer to a list of inner polygons
+       int out_index,in_index,indet_index; //indexes to the lists **Outer and **Inner
+       int     in,temp2;
+       int u,i,N,n,new_outer;
+       int next_ring;  //the index of the panPartStart list 
+       double area;    
+       
+       //initialize all counters/indexes
+       out_index=0;
+       in_index=0;
+       indet_index=0;
+       area=0;
+       n=0;
+       i=0;
+       N = obj->nVertices;
+
+       if(obj->nParts >1){
+               next_ring = 1;//if there is more than one part, set the next_ring index to one
+       }else{
+               next_ring = -99;
+       }
+       
+       
+       //allocate initial pointer memory
+       Outer = (Ring**)malloc(sizeof(Ring*)*obj->nParts);                              
+       Inner = (Ring**)malloc(sizeof(Ring*)*obj->nParts);                              
+       Poly = (Ring*)malloc(sizeof(Ring));                             
+       Poly->list = (Point*)malloc(sizeof(Point)*N);
+       Poly->next = NULL;
+
+       
+       for (u=0;u<N;u++){
+       
+               //check if the next point is the start of a new ring
+               if(((next_ring != -99) && (u+1 == obj->panPartStart[next_ring] )) || u==N-1){
+                       //check if a ring is clockwise(outer) or not(inner) by getting positive(inner) or negative(outer) area.
+                       //'area' is actually twice actual polygon area so divide by 2, not that it matters but in case we use it latter...
+                       area = area/2.0;
+                       if(area < 0.0 || obj->nParts ==1){
+                               
+                               //An outer ring so fill in the last point then put it in the 'Outer' list
+                               Poly->list[n].x = obj->padfX[u]; //the polygon is ended with it's first co-ordinates reused
+                               Poly->list[n].y = obj->padfY[u];
+                               Poly->n = n+1;
+                               Outer[out_index] = Poly;
+                               out_index++;
+                               
+                               //allocate memory to start building the next ring
+                               Poly = (Ring*)malloc(sizeof(Ring));     
+                               
+                               //temp2 is the number of points in the list of the next ring
+                               //determined so that we can allocate the right amount of mem 6 lines down
+                               if((next_ring + 1) == obj->nParts){
+                                       temp2 = N;
+                               }else{
+                                       temp2 = obj->panPartStart[next_ring+1] - obj->panPartStart[next_ring];
+                               }
+                               Poly->list = (Point*)malloc(sizeof(Point)*temp2);
+                               Poly->next = NULL;//make sure to make to initiale next to null or you never know when the list ends
+                                                                 //this never used to be here and was a pain in the ass bug to find...
+                               
+                               n=0;//set your count of what point you are at in the current ring back to 0
+               
+                       }else{
+                               
+                               Poly->list[n].x = obj->padfX[u]; //the polygon ends with it's first co-ordinates reused
+                               Poly->list[n].y = obj->padfY[u];
+                               Poly->n = n+1;
+
+                               Inner[in_index] = Poly;
+                               in_index++;
+       
+                               Poly = (Ring*)malloc(sizeof(Ring));                             
+                               temp2 = N;
+                               if((next_ring + 1) == obj->nParts){
+                               }else{
+                                       temp2 = obj->panPartStart[next_ring+1] - obj->panPartStart[next_ring];
+                               }
+
+
+                               //printf("temp2 -> %d for the list in the loop parts = %d  N= %d\n",temp2,obj->nParts,N);                       
+                               Poly->list = (Point*)malloc(sizeof(Point)*temp2);
+                               Poly->next = NULL;
+
+                               n=0;
+                               //printf("pt is ( %g   %g)\n Poly starts with  (%e  %e)\n",obj->padfX[u],obj->padfY[u],Outer[0].x,Outer[0].y);
+
+                       }
+                       area=0.0;
+                       if((next_ring + 1) == obj->nParts){
+                               //printf("go to end of N\n");
+                       }else{
+                               next_ring++;
+                       }
+               }else{
+       
+                       //printf(" x---   %g  , y---   %g     u=%d\n",obj->padfX[u],obj->padfY[u],u);
+                       Poly->list[n].x = obj->padfX[u];
+                       Poly->list[n].y = obj->padfY[u];
+                       n++;
+                       area += (obj->padfX[u] * obj->padfY[u+1]) - (obj->padfY[u] * obj->padfX[u+1]); //calculate the area 
+
+               }
+       }
+       
+
+
+//Put the inner rings into the list of the outer rings of which they are within
+       for(u=0; u < in_index; u++){
+               pt.x = Inner[u]->list[0].x;
+               pt.y = Inner[u]->list[0].y;
+
+               pt2.x = Inner[u]->list[1].x;
+               pt2.y = Inner[u]->list[1].y;
+               for(i=0;i< out_index; i++){
+                       in = PIP(pt,Outer[i]->list,Outer[i]->n);                                
+                       if(in==1 && PIP(pt2,Outer[i]->list,Outer[i]->n)){
+                               Poly = Outer[i];
+                               while(Poly->next != NULL){
+                                       Poly = Poly->next;
+                               }
+                               Poly->next = Inner[u];
+                               break;
+                       }
+               }
+               //if the ring wasn't within any outer rings, assume it is a new outer ring
+               
+               if(i == out_index){
+                       Outer[out_index] = Inner[u];
+                       out_index++;
+               }
+       }
+       
+       //start spitting out the sql for ordered entities now.
+       
+       printf("\nInsert into %s values('%i','MULTIPOLYGON(",ARGV[2],rings);
+       rings++;
+       for(u=0; u < out_index; u++){
+               Poly = Outer[u];
+               if(u==0){
+                       printf("(");
+               }else{
+                       printf(",(");
+               }
+               while(Poly != NULL){
+                       for(i=0;i<Poly->n;i++){
+                               if(i==0){
+                                       if(Poly != Outer[u]){
+                                               printf(",");    
+                                       }
+                                       printf("(%.15g %.15g ",Poly->list[i].x,Poly->list[i].y);
+                               }else{
+                                       printf(",%.15g %.15g ",Poly->list[i].x,Poly->list[i].y);
+                               }
+                       }
+                       printf(")");
+                       temp = Poly;
+                       Poly = Poly->next;
+                       free(temp->list);
+                       free(temp);
+               }
+               printf(")");
+       }
+       printf(")'");
+       Insert_attributes(hDBFHandle, ARGV,rings-1);
+
+
+       free(Outer);
+       free(Inner);
+       free(Poly);
+       
+
+       return rings;
+}
+
+
+
+//Insert the attributes from the correct row of dbf file
+
+int Insert_attributes(DBFHandle hDBFHandle, char **ARGV, int row){
+
+       int i,num_fields;
+
+
+       num_fields = DBFGetFieldCount( hDBFHandle );
+               
+       
+       for( i = 0; i < num_fields; i++ ){
+                       printf(",'%s'",DBFReadStringAttribute( hDBFHandle, row, i ) );
+       }
+       printf (");\n");
+       
+       return 1;
+}
+
+
+
+
+// main()     
+//  usage:   pop  <shapefile to process>  <table name> [-d || -a || -c] | psql -h <host> -d <database> -p <port> ...
+// -d: drops the table , then recreates it and populates it with current shape file data
+// -a: appends shape file into current table, must be excatly the same table schema
+// -c: creates a new table and populates it, this is the default if you don't specify any options
+
+//     Using shapelib 1.2.8, this program reads in shape files and processes it's contents
+//     into a Insert statements which can be easily piped into a database frontend.
+//     Specifically designed to insert type 'geometry' (a custom written PostgreSQL type)
+//     for the shape files and PostgreSQL standard types for all attributes of the entity. 
+//
+//     Basically the program determines which type of shape (currently supports: 2d points,2d lines,2d
+//  polygons,3d points, and 3d lines) is in the file and takes appropriate action to read out the attributes.
+
+
+main (int ARGC, char **ARGV)
+{
+    SHPHandle   hSHPHandle;
+       DBFHandle       hDBFHandle;
+       int num_fields,num_records,begin,trans;
+       int num_entities,  phnshapetype,next_ring;
+       double padminbound[8], padmaxbound[8];
+       int u,j,tot_rings;
+       SHPObject       *obj;
+       char            name[12];
+       char            opt;
+       DBFFieldType type;
+
+
+       //display proper usage if incorrect number of arguments given   
+       if (ARGC != 3 && ARGC != 4)
+       {
+               printf ("usage: pop <shapefile> <table name to create> [-d || -a || -c]\n");
+               exit (-1);
+       }
+
+       if(ARGC ==4){
+               if(strcmp(ARGV[3], "-d")==0){
+                       opt = 'd';
+               }else if(strcmp(ARGV[3], "-c")==0){
+                       opt = 'c';
+               }else if(strcmp(ARGV[3], "-a")==0){
+                       opt = 'a';
+               }else{
+                       printf("option %s is not a valid option, use -a, -d, or -c\n",ARGV[3]);
+               exit(-1);
+               }
+       }else{
+               opt = 'c';
+       }
+
+       
+       //Open the shp and dbf files
+       hSHPHandle = SHPOpen( ARGV[1], "rb" );
+       hDBFHandle = DBFOpen( ARGV[1], "rb" );
+       if (hSHPHandle == NULL || hDBFHandle == NULL){
+               printf ("shape is null\n");     
+               exit(-1);
+       }
+       if(opt == 'd'){
+               //-------------------------Drop the table--------------------------------
+               //drop the table given
+               printf("drop table %s;",ARGV[2]);
+
+       }
+
+       
+
+       if(opt == 'c' || opt == 'd'){ //if opt is 'a' do nothing, go straight to making inserts
+
+               //-------------------------Create the table--------------------------------
+               //create a table for inserting the shapes into with appropriate columns and types
+
+               printf("create table %s (geoid int4, geo_value geometry ",ARGV[2]);
+
+               num_fields = DBFGetFieldCount( hDBFHandle );
+               num_records = DBFGetRecordCount(hDBFHandle);
+
+               for(j=0;j<num_fields;j++){
+                       type = DBFGetFieldInfo(hDBFHandle, j, name, NULL, NULL);
+                       
+                       if(strcmp(name,"GEOID")==0 || strcmp(name,"GEO_VALUE")==0){
+                               printf(", %s__2 ",name);
+                       }else{
+                               //printf("NAME = S\n\n",name);
+                               printf(", %s ",name);
+                       }
+       
+                       if(hDBFHandle->pachFieldType[j] == 'D' ){
+                               printf ("varchar(8)");//date type is not supported in API so check for it explicity before the api call.
+                       }else{
+                               if(type == FTString){
+                                       printf ("varchar");
+                               }else if(type == FTInteger){
+                                       printf ("int4");                        
+                               }else if(type == FTDouble){
+                                       printf ("float8");
+                               }else{
+                                       printf ("Invalid type in DBF file");
+                               }
+                       }       
+               }
+               printf (");\n");
+               //finished creating the table
+       }
+
+
+       SHPGetInfo( hSHPHandle, &num_entities, &phnshapetype, &padminbound[0], &padmaxbound[0]);
+       obj =   SHPReadObject(hSHPHandle,0);
+       trans=0;        
+       //Determine what type of shape is in the file and do appropriate processing
+       if( obj->nSHPType == 5 ){  
+       //---------------------------------------------------------------------------------
+       //---------POLYGONS----------------------------------------------------------------
+
+               // sorts of all the rings so that they are outer,inner,iner,outer,inner...
+               // with the inner ones coming after the outer ones they are within spatially
+                               
+               tot_rings = 0;
+               
+
+               //go through each entity and call ring_check() to sort the rings and print out the sql statement
+               // keep track of total number of inserts in tot_rings so 
+               // you can pass it to the function for the next entity
+               for (j=0;j<num_entities; j++){  
+                       
+                       //wrap a transaction block around each 250 inserts...
+                       if(trans == 250 || j==0){
+                               if(j==0){
+                                       printf("begin;");
+                               }else{
+                                       printf("end;\n");
+                                       printf("begin;");
+                               }
+                               trans=0;
+                       }
+                       trans++;
+                       // transaction stuff done
+                       
+                       obj = SHPReadObject(hSHPHandle,j);      //open the next object
+                       tot_rings = ring_check(obj,ARGV,tot_rings,hDBFHandle);
+                       SHPDestroyObject(obj); //close the object
+               }
+
+               printf("end;"); //End the last transaction block
+
+       
+       }else if( obj->nSHPType == 1){  
+               //---------------------------------------------------------------------
+               //----------POINTS-----------------------------------------------------
+
+               for (j=0;j<num_entities; j++){
+                       
+                       //wrap a transaction block around each 250 inserts...
+                       if(trans == 250 || j==0){
+                               if(j==0){
+                                       printf("begin;");
+                               }else{
+                                       printf("end;\n");
+                                       printf("begin;");
+                               }
+                               trans=0;
+                       }
+                       trans++;
+                       // transaction stuff done
+
+                       printf("insert into %s values ('%i','POINT (",ARGV[2],j); //print out sql statements
+                       obj = SHPReadObject(hSHPHandle,j);
+                       
+                       for (u=0;u<obj->nVertices; u++){
+                               if (u>0){
+                                       printf(",%.15g %.15g",obj->padfX[u],obj->padfY[u]);
+                               }else{
+                                       printf("%.15g %.15g",obj->padfX[u],obj->padfY[u]);
+                               }
+                       }
+
+                       printf(")'");
+                       Insert_attributes(hDBFHandle, ARGV,j); //add the attributes for each entity to the insert statement
+
+                       SHPDestroyObject(obj);
+               }
+               printf("end;"); //End the last transaction
+               
+       }else if( obj->nSHPType == 3){
+               //------------------------------------------------------------------------
+               //--------ARCs / LINES----------------------------------------------------
+
+               begin=0;//initialize the begin flag
+
+               for (j=0;j<num_entities; j++){
+               
+                       //wrap a transaction around each 250 inserts...
+                       if(trans == 250 || j==0){
+                               if(j==0){
+                                       printf("begin;");
+                               }else{
+                                       printf("end;\n");
+                                       printf("begin;");
+                               }
+                               trans=0;
+                       }
+                       trans++;
+                       //end of transaction stuff
+
+                       printf("insert into %s values ('%i','MULTILINESTRING (",ARGV[2],j);
+                       obj = SHPReadObject(hSHPHandle,j);
+
+                       if(obj->nParts >1){
+                               next_ring = 1;//if there is more than one part, set the next_ring index to one
+                       }else{
+                               next_ring = -99;
+                       }
+                       
+                       //for each vertice write out the coordinates in the insert statement, when there is a new line 
+                       //you must end the brackets and start new ones etc.
+                       for (u=0;u<obj->nVertices; u++){
+                               
+                               //check if the next vertice is the start of a new line
+               //              printf("\n\nu+1 = %d, next_ring = %d  index = %d\n",u+1,next_ring,obj->panPartStart[next_ring]);
+                               if(((next_ring != -99) && (u+1 == obj->panPartStart[next_ring] )) || u==(obj->nVertices-1) ){
+                                       printf(",%.15g %.15g ",obj->padfX[u],obj->padfY[u]);
+                                       printf(")");
+                                       
+                                       next_ring++;
+                                       begin=1;//flag the fact that you area at a new line next time through the loop
+                               }else{
+                                       if (u==0 || begin==1){ //if you are at the begging of a new line add comma and brackets 
+                                               if(u!=0) printf(",");
+                                               printf("(%.15g %.15g ",obj->padfX[u],obj->padfY[u]);
+                                               begin=0;
+                                       }else{
+                                               printf(",%.15g %.15g ",obj->padfX[u],obj->padfY[u]);
+                                       }
+                               }
+                       }       
+                       
+
+                       printf(")'");
+                       Insert_attributes(hDBFHandle, ARGV,j); //add the attributes of each shape to the insert statement
+
+                       
+                       SHPDestroyObject(obj);
+               }
+
+               printf("end;");//end the last transaction
+               
+
+       
+       }else if( obj->nSHPType == 13){  
+               //---------------------------------------------------------------------
+               //------PolyLineZ(3D lines)--------------------------------------------
+               
+               begin=0;//initialize the begin flag
+                               
+               for (j=0;j<num_entities; j++){
+
+                       //wrap a transaction around each 250 inserts...
+                       if(trans == 250 || j==0){
+                               if(j==0){
+                                       printf("begin;");
+                               }else{
+                                       printf("end;\n");
+                                       printf("begin;");
+                               }
+                               trans=0;
+                       }
+                       trans++;
+                       //end transaction stuff
+
+
+               
+                       printf("insert into %s values ('%i','MULTILINESTRING (",ARGV[2],j);
+                       obj = SHPReadObject(hSHPHandle,j);
+
+                       if(obj->nParts >1){
+                               next_ring = 1;//if there is more than one part, set the next_ring index to one
+                       }else{
+                               next_ring = -99;
+                       }
+
+                       //for each vertice write out the coordinates in the insert statement, when there is a new line 
+                       //you must end the brackets and start new ones etc.
+                       for (u=0;u<obj->nVertices; u++){
+                               
+                               //check if the next vertice is the start of a new line                          
+                               if(((next_ring != -99) && (u+1 == obj->panPartStart[next_ring] )) || u==(obj->nVertices-1) ){
+                                       printf(",%.15g %.15g ",obj->padfX[u],obj->padfY[u]);
+                                       printf(")");
+                                       next_ring++;
+                                       begin =1;//flag the fact that you area at a new line next time through the loop
+                               }else{
+                                       if (u==0 || begin==1){
+                                               if(u!=0) printf(",");
+                                               printf("(%.15g %.15g %.15g ",obj->padfX[u],obj->padfY[u],obj->padfZ[u]);
+                                               begin=0;
+                                       }else{
+                                               printf(",%.15g %.15g %.15g ",obj->padfX[u],obj->padfY[u],obj->padfZ[u]);
+                                       }
+                               }
+                       }       
+                       
+
+                       printf(")'");
+                       Insert_attributes(hDBFHandle, ARGV,j);//add the attributes of each shape to the insert statement
+
+                       
+                       SHPDestroyObject(obj);
+               }
+
+               printf("end;");//close last transaction
+       
+       
+       }else if( obj->nSHPType == 11){  
+               //---------------------------------------------------------------------------
+               //------POINTZ (3D POINTS)---------------------------------------------------
+               
+               for (j=0;j<num_entities; j++){
+
+                       //wrap a transaction around each 250 inserts...
+                       if(trans == 250 || j==0){
+                               if(j==0){
+                                       printf("begin;");
+                               }else{
+                                       printf("end;\n");
+                                       printf("begin;");
+                               }
+                               trans=0;
+                       }
+                       trans++;
+                       //end of transaction stuff
+                       
+                       
+                       printf("insert into %s values ('%i','POINT (",ARGV[2],j);
+
+                       obj = SHPReadObject(hSHPHandle,j);
+                       
+                       for (u=0;u<obj->nVertices; u++){
+                               if (u>0){
+                                       printf(",%.15g %.15g %.15g",obj->padfX[u],obj->padfY[u],obj->padfZ[u]);
+                               }else{
+                                       printf("%.15g %.15g %.15g",obj->padfX[u],obj->padfY[u],obj->padfZ[u]);
+                               }
+                       }
+
+                       printf(")'");
+                       Insert_attributes(hDBFHandle, ARGV,j);//add the attributes of each shape to the insert statement
+
+                       
+                       SHPDestroyObject(obj);
+               }
+               
+
+       
+               printf("end;");//end the last transaction
+       }else{  
+               printf ("");
+               printf ("\n\n**** Type is NOT SUPPORTED, type id = %d ****\n\n",obj->nSHPType);
+               //print out what type the file is and that it is not supported
+       
+       }//end the if statement for shape types
+
+}//end main()
diff --git a/loader/shapefil.h b/loader/shapefil.h
new file mode 100644 (file)
index 0000000..5b2d781
--- /dev/null
@@ -0,0 +1,351 @@
+#ifndef _SHAPEFILE_H_INCLUDED
+#define _SHAPEFILE_H_INCLUDED
+
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  Shapelib
+ * Purpose:  Primary include file for Shapelib.
+ * Author:   Frank Warmerdam, warmerda@home.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$
+ * Revision 1.1  2001/07/18 21:42:12  pramsey
+ * Initial add of the data loader code.
+ *
+ * Revision 1.15  2000/02/16 16:03:51  warmerda
+ * added null shape support
+ *
+ * Revision 1.14  1999/11/05 14:12:05  warmerda
+ * updated license terms
+ *
+ * Revision 1.13  1999/06/02 18:24:21  warmerda
+ * added trimming code
+ *
+ * Revision 1.12  1999/06/02 17:56:12  warmerda
+ * added quad'' subnode support for trees
+ *
+ * Revision 1.11  1999/05/18 19:11:11  warmerda
+ * Added example searching capability
+ *
+ * Revision 1.10  1999/05/18 17:49:38  warmerda
+ * added initial quadtree support
+ *
+ * Revision 1.9  1999/05/11 03:19:28  warmerda
+ * added new Tuple api, and improved extension handling - add from candrsn
+ *
+ * Revision 1.8  1999/03/23 17:22:27  warmerda
+ * Added extern "C" protection for C++ users of shapefil.h.
+ *
+ * Revision 1.7  1998/12/31 15:31:07  warmerda
+ * Added the TRIM_DBF_WHITESPACE and DISABLE_MULTIPATCH_MEASURE options.
+ *
+ * Revision 1.6  1998/12/03 15:48:15  warmerda
+ * Added SHPCalculateExtents().
+ *
+ * Revision 1.5  1998/11/09 20:57:16  warmerda
+ * Altered SHPGetInfo() call.
+ *
+ * Revision 1.4  1998/11/09 20:19:33  warmerda
+ * Added 3D support, and use of SHPObject.
+ *
+ * Revision 1.3  1995/08/23 02:24:05  warmerda
+ * Added support for reading bounds.
+ *
+ * Revision 1.2  1995/08/04  03:17:39  warmerda
+ * Added header.
+ *
+ */
+
+#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
+
+/************************************************************************/
+/*                             SHP Support.                             */
+/************************************************************************/
+typedef        struct
+{
+    FILE        *fpSHP;
+    FILE       *fpSHX;
+
+    int                nShapeType;                             /* SHPT_* */
+    
+    int                nFileSize;                              /* SHP file */
+
+    int         nRecords;
+    int                nMaxRecords;
+    int                *panRecOffset;
+    int                *panRecSize;
+
+    double     adBoundsMin[4];
+    double     adBoundsMax[4];
+
+    int                bUpdated;
+} 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;
+} SHPObject;
+
+/* -------------------------------------------------------------------- */
+/*      SHP API Prototypes                                              */
+/* -------------------------------------------------------------------- */
+SHPHandle SHPOpen( const char * pszShapeFile, const char * pszAccess );
+SHPHandle SHPCreate( const char * pszShapeFile, int nShapeType );
+void   SHPGetInfo( SHPHandle hSHP, int * pnEntities, int * pnShapeType,
+                    double * padfMinBound, double * padfMaxBound );
+
+SHPObject *SHPReadObject( SHPHandle hSHP, int iShape );
+int    SHPWriteObject( SHPHandle hSHP, int iShape, SHPObject * psObject );
+
+void   SHPDestroyObject( SHPObject * psObject );
+void   SHPComputeExtents( SHPObject * psObject );
+SHPObject *SHPCreateObject( int nSHPType, int nShapeId,
+                            int nParts, int * panPartStart, int * panPartType,
+                            int nVertices, double * padfX, double * padfY,
+                            double * padfZ, double * padfM );
+SHPObject *SHPCreateSimpleObject( int nSHPType, int nVertices,
+                              double * padfX, double * padfY, double * padfZ );
+
+void   SHPClose( SHPHandle hSHP );
+
+const char *SHPTypeName( int nSHPType );
+const char *SHPPartTypeName( int nPartType );
+
+/* -------------------------------------------------------------------- */
+/*      Shape quadtree indexing API.                                    */
+/* -------------------------------------------------------------------- */
+
+/* this can be two or four for binary or quad tree */
+#define MAX_SUBNODE    4
+
+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;
+    
+    SHPTreeNode        *psRoot;
+} SHPTree;
+
+SHPTree *SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth,
+                        double *padfBoundsMin, double *padfBoundsMax );
+void     SHPDestroyTree( SHPTree * hTree );
+
+int    SHPWriteTree( SHPTree *hTree, const char * pszFilename );
+SHPTree SHPReadTree( const char * pszFilename );
+
+int    SHPTreeAddObject( SHPTree * hTree, SHPObject * psObject );
+int    SHPTreeAddShapeId( SHPTree * hTree, SHPObject * psObject );
+int    SHPTreeRemoveShapeId( SHPTree * hTree, int nShapeId );
+
+void   SHPTreeTrimExtraNodes( SHPTree * hTree );
+
+int    *SHPTreeFindLikelyShapes( SHPTree * hTree,
+                                 double * padfBoundsMin,
+                                 double * padfBoundsMax,
+                                 int * );
+int     SHPCheckBoundsOverlap( double *, double *, double *, double *, int );
+
+/************************************************************************/
+/*                             DBF Support.                             */
+/************************************************************************/
+typedef        struct
+{
+    FILE       *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                bNoHeader;
+    int                bUpdated;
+} DBFInfo;
+
+typedef DBFInfo * DBFHandle;
+
+typedef enum {
+  FTString,
+  FTInteger,
+  FTDouble,
+  FTInvalid,
+} DBFFieldType;
+
+#define XBASE_FLDHDR_SZ       32
+
+DBFHandle DBFOpen( const char * pszDBFFile, const char * pszAccess );
+DBFHandle DBFCreate( const char * pszDBFFile );
+
+int    DBFGetFieldCount( DBFHandle psDBF );
+int    DBFGetRecordCount( DBFHandle psDBF );
+int    DBFAddField( DBFHandle hDBF, const char * pszFieldName,
+                    DBFFieldType eType, int nWidth, int nDecimals );
+
+DBFFieldType DBFGetFieldInfo( DBFHandle psDBF, int iField, 
+                             char * pszFieldName, 
+                             int * pnWidth, int * pnDecimals );
+
+int    DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField );
+double         DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField );
+const char *DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField );
+
+int DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, 
+                             int nFieldValue );
+int DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField,
+                            double dFieldValue );
+int DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField,
+                            const char * pszFieldValue );
+
+const char *DBFReadTuple(DBFHandle psDBF, int hEntity );
+int DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple );
+
+DBFHandle DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename );
+void   DBFClose( DBFHandle hDBF );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ndef _SHAPEFILE_H_INCLUDED */
diff --git a/loader/shpopen.c b/loader/shpopen.c
new file mode 100644 (file)
index 0000000..d7c475f
--- /dev/null
@@ -0,0 +1,1623 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  Shapelib
+ * Purpose:  Implementation of core Shapefile read/write functions.
+ * Author:   Frank Warmerdam, warmerda@home.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$
+ * Revision 1.1  2001/07/18 21:42:12  pramsey
+ * Initial add of the data loader code.
+ *
+ * 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.
+ *
+ */
+
+static char rcsid[] = 
+  "$Id$";
+
+#include "shapefil.h"
+
+#include <math.h>
+#include <limits.h>
+#include <assert.h>
+#include <stdlib.h>
+
+typedef unsigned char uchar;
+
+#if UINT_MAX == 65535
+typedef long         int32;
+#else
+typedef int          int32;
+#endif
+
+#ifndef FALSE
+#  define FALSE                0
+#  define TRUE         1
+#endif
+
+#define ByteCopy( a, b, c )    memcpy( b, a, c )
+#ifndef MAX
+#  define MIN(a,b)      ((a<b) ? a : b)
+#  define MAX(a,b)      ((a>b) ? a : b)
+#endif
+
+static int     bBigEndian;
+static uchar   *pabyRec = NULL;
+static int     nBufSize = 0;
+
+
+/************************************************************************/
+/*                              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.                              */
+/************************************************************************/
+
+static void SHPWriteHeader( SHPHandle psSHP )
+
+{
+    uchar      abyHeader[100];
+    int                i;
+    int32      i32;
+    double     dValue;
+    int32      *panSHX;
+
+/* -------------------------------------------------------------------- */
+/*      Prepare header block for .shp file.                             */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < 100; i++ )
+      abyHeader[i] = 0;
+
+    abyHeader[2] = 0x27;                               /* magic cookie */
+    abyHeader[3] = 0x0a;
+
+    i32 = psSHP->nFileSize/2;                          /* file size */
+    ByteCopy( &i32, abyHeader+24, 4 );
+    if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
+    
+    i32 = 1000;                                                /* version */
+    ByteCopy( &i32, abyHeader+28, 4 );
+    if( bBigEndian ) SwapWord( 4, abyHeader+28 );
+    
+    i32 = psSHP->nShapeType;                           /* shape type */
+    ByteCopy( &i32, abyHeader+32, 4 );
+    if( bBigEndian ) SwapWord( 4, abyHeader+32 );
+
+    dValue = psSHP->adBoundsMin[0];                    /* set bounds */
+    ByteCopy( &dValue, abyHeader+36, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+36 );
+
+    dValue = psSHP->adBoundsMin[1];
+    ByteCopy( &dValue, abyHeader+44, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+44 );
+
+    dValue = psSHP->adBoundsMax[0];
+    ByteCopy( &dValue, abyHeader+52, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+52 );
+
+    dValue = psSHP->adBoundsMax[1];
+    ByteCopy( &dValue, abyHeader+60, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+60 );
+
+    dValue = psSHP->adBoundsMin[2];                    /* z */
+    ByteCopy( &dValue, abyHeader+68, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+68 );
+
+    dValue = psSHP->adBoundsMax[2];
+    ByteCopy( &dValue, abyHeader+76, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+76 );
+
+    dValue = psSHP->adBoundsMin[3];                    /* m */
+    ByteCopy( &dValue, abyHeader+84, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+84 );
+
+    dValue = psSHP->adBoundsMax[3];
+    ByteCopy( &dValue, abyHeader+92, 8 );
+    if( bBigEndian ) SwapWord( 8, abyHeader+92 );
+
+/* -------------------------------------------------------------------- */
+/*      Write .shp file header.                                         */
+/* -------------------------------------------------------------------- */
+    fseek( psSHP->fpSHP, 0, 0 );
+    fwrite( abyHeader, 100, 1, psSHP->fpSHP );
+
+/* -------------------------------------------------------------------- */
+/*      Prepare, and write .shx file header.                            */
+/* -------------------------------------------------------------------- */
+    i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2;   /* file size */
+    ByteCopy( &i32, abyHeader+24, 4 );
+    if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
+    
+    fseek( psSHP->fpSHX, 0, 0 );
+    fwrite( abyHeader, 100, 1, psSHP->fpSHX );
+
+/* -------------------------------------------------------------------- */
+/*      Write out the .shx contents.                                    */
+/* -------------------------------------------------------------------- */
+    panSHX = (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 );
+    }
+
+    fwrite( panSHX, sizeof(int32) * 2, psSHP->nRecords, psSHP->fpSHX );
+
+    free( panSHX );
+}
+
+/************************************************************************/
+/*                              SHPOpen()                               */
+/*                                                                      */
+/*      Open the .shp and .shx files based on the basename of the       */
+/*      files or either file name.                                      */
+/************************************************************************/
+   
+SHPHandle SHPOpen( const char * pszLayer, const char * pszAccess )
+
+{
+    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) malloc(sizeof(SHPInfo));
+
+    psSHP->bUpdated = FALSE;
+
+/* -------------------------------------------------------------------- */
+/*     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 = fopen(pszFullname, pszAccess );
+    if( psSHP->fpSHP == NULL )
+    {
+        sprintf( pszFullname, "%s.SHP", pszBasename );
+        psSHP->fpSHP = fopen(pszFullname, pszAccess );
+    }
+    
+    if( psSHP->fpSHP == NULL )
+        return( NULL );
+
+    sprintf( pszFullname, "%s.shx", pszBasename );
+    psSHP->fpSHX = fopen(pszFullname, pszAccess );
+    if( psSHP->fpSHX == NULL )
+    {
+        sprintf( pszFullname, "%s.SHX", pszBasename );
+        psSHP->fpSHX = fopen(pszFullname, pszAccess );
+    }
+    
+    if( psSHP->fpSHX == NULL )
+        return( NULL );
+
+    free( pszFullname );
+    free( pszBasename );
+
+/* -------------------------------------------------------------------- */
+/*  Read the file size from the SHP file.                              */
+/* -------------------------------------------------------------------- */
+    pabyBuf = (uchar *) malloc(100);
+    fread( pabyBuf, 100, 1, psSHP->fpSHP );
+
+    psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256
+                       + pabyBuf[25] * 256 * 256
+                       + pabyBuf[26] * 256
+                       + pabyBuf[27]) * 2;
+
+/* -------------------------------------------------------------------- */
+/*  Read SHX file Header info                                           */
+/* -------------------------------------------------------------------- */
+    fread( pabyBuf, 100, 1, psSHP->fpSHX );
+
+    if( pabyBuf[0] != 0 
+        || pabyBuf[1] != 0 
+        || pabyBuf[2] != 0x27 
+        || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) )
+    {
+       fclose( psSHP->fpSHP );
+       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];
+
+/* -------------------------------------------------------------------- */
+/*      Read the bounds.                                                */
+/* -------------------------------------------------------------------- */
+    if( bBigEndian ) SwapWord( 8, pabyBuf+36 );
+    memcpy( &dValue, pabyBuf+36, 8 );
+    psSHP->adBoundsMin[0] = dValue;
+
+    if( bBigEndian ) SwapWord( 8, pabyBuf+44 );
+    memcpy( &dValue, pabyBuf+44, 8 );
+    psSHP->adBoundsMin[1] = dValue;
+
+    if( bBigEndian ) SwapWord( 8, pabyBuf+52 );
+    memcpy( &dValue, pabyBuf+52, 8 );
+    psSHP->adBoundsMax[0] = dValue;
+
+    if( bBigEndian ) SwapWord( 8, pabyBuf+60 );
+    memcpy( &dValue, pabyBuf+60, 8 );
+    psSHP->adBoundsMax[1] = dValue;
+
+    if( bBigEndian ) SwapWord( 8, pabyBuf+68 );                /* z */
+    memcpy( &dValue, pabyBuf+68, 8 );
+    psSHP->adBoundsMin[2] = dValue;
+    
+    if( bBigEndian ) SwapWord( 8, pabyBuf+76 );
+    memcpy( &dValue, pabyBuf+76, 8 );
+    psSHP->adBoundsMax[2] = dValue;
+    
+    if( bBigEndian ) SwapWord( 8, pabyBuf+84 );                /* z */
+    memcpy( &dValue, pabyBuf+84, 8 );
+    psSHP->adBoundsMin[3] = dValue;
+
+    if( bBigEndian ) SwapWord( 8, pabyBuf+92 );
+    memcpy( &dValue, pabyBuf+92, 8 );
+    psSHP->adBoundsMax[3] = dValue;
+
+    free( pabyBuf );
+
+/* -------------------------------------------------------------------- */
+/*     Read the .shx file to get the offsets to each record in         */
+/*     the .shp file.                                                  */
+/* -------------------------------------------------------------------- */
+    psSHP->nMaxRecords = psSHP->nRecords;
+
+    psSHP->panRecOffset =
+        (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );
+    psSHP->panRecSize =
+        (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );
+
+    pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) );
+    fread( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX );
+
+    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   SHPClose(SHPHandle psSHP )
+
+{
+/* -------------------------------------------------------------------- */
+/*     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 );
+
+    fclose( psSHP->fpSHX );
+    fclose( psSHP->fpSHP );
+
+    free( psSHP );
+
+    if( pabyRec != NULL )
+    {
+        free( pabyRec );
+        pabyRec = NULL;
+        nBufSize = 0;
+    }
+}
+
+/************************************************************************/
+/*                             SHPGetInfo()                             */
+/*                                                                      */
+/*      Fetch general information about the shape file.                 */
+/************************************************************************/
+
+void SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType,
+                double * padfMinBound, double * padfMaxBound )
+
+{
+    int                i;
+    
+    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 SHPCreate( const char * pszLayer, int nShapeType )
+
+{
+    char       *pszBasename, *pszFullname;
+    int                i;
+    FILE       *fpSHP, *fpSHX;
+    uchar      abyHeader[100];
+    int32      i32;
+    double     dValue;
+    
+/* -------------------------------------------------------------------- */
+/*      Establish the byte order on this system.                        */
+/* -------------------------------------------------------------------- */
+    i = 1;
+    if( *((uchar *) &i) == 1 )
+        bBigEndian = FALSE;
+    else
+        bBigEndian = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*     Compute the base (layer) name.  If there is any extension       */
+/*     on the passed in filename we will strip it off.                 */
+/* -------------------------------------------------------------------- */
+    pszBasename = (char *) malloc(strlen(pszLayer)+5);
+    strcpy( pszBasename, pszLayer );
+    for( i = strlen(pszBasename)-1; 
+        i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
+              && pszBasename[i] != '\\';
+        i-- ) {}
+
+    if( pszBasename[i] == '.' )
+        pszBasename[i] = '\0';
+
+/* -------------------------------------------------------------------- */
+/*      Open the two files so we can write their headers.               */
+/* -------------------------------------------------------------------- */
+    pszFullname = (char *) malloc(strlen(pszBasename) + 5);
+    sprintf( pszFullname, "%s.shp", pszBasename );
+    fpSHP = fopen(pszFullname, "wb" );
+    if( fpSHP == NULL )
+        return( NULL );
+
+    sprintf( pszFullname, "%s.shx", pszBasename );
+    fpSHX = fopen(pszFullname, "wb" );
+    if( fpSHX == NULL )
+        return( NULL );
+
+    free( pszFullname );
+    free( pszBasename );
+
+/* -------------------------------------------------------------------- */
+/*      Prepare header block for .shp file.                             */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < 100; i++ )
+      abyHeader[i] = 0;
+
+    abyHeader[2] = 0x27;                               /* magic cookie */
+    abyHeader[3] = 0x0a;
+
+    i32 = 50;                                          /* file size */
+    ByteCopy( &i32, abyHeader+24, 4 );
+    if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
+    
+    i32 = 1000;                                                /* version */
+    ByteCopy( &i32, abyHeader+28, 4 );
+    if( bBigEndian ) SwapWord( 4, abyHeader+28 );
+    
+    i32 = nShapeType;                                  /* shape type */
+    ByteCopy( &i32, abyHeader+32, 4 );
+    if( bBigEndian ) SwapWord( 4, abyHeader+32 );
+
+    dValue = 0.0;                                      /* set bounds */
+    ByteCopy( &dValue, abyHeader+36, 8 );
+    ByteCopy( &dValue, abyHeader+44, 8 );
+    ByteCopy( &dValue, abyHeader+52, 8 );
+    ByteCopy( &dValue, abyHeader+60, 8 );
+
+/* -------------------------------------------------------------------- */
+/*      Write .shp file header.                                         */
+/* -------------------------------------------------------------------- */
+    fwrite( abyHeader, 100, 1, fpSHP );
+
+/* -------------------------------------------------------------------- */
+/*      Prepare, and write .shx file header.                            */
+/* -------------------------------------------------------------------- */
+    i32 = 50;                                          /* file size */
+    ByteCopy( &i32, abyHeader+24, 4 );
+    if( !bBigEndian ) SwapWord( 4, abyHeader+24 );
+    
+    fwrite( abyHeader, 100, 1, fpSHX );
+
+/* -------------------------------------------------------------------- */
+/*      Close the files, and then open them as regular existing files.  */
+/* -------------------------------------------------------------------- */
+    fclose( fpSHP );
+    fclose( fpSHX );
+
+    return( SHPOpen( pszLayer, "r+b" ) );
+}
+
+/************************************************************************/
+/*                           _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 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 *SHPCreateObject( int nSHPType, int nShapeId, int nParts,
+                            int * panPartStart, int * panPartType,
+                            int nVertices, double * padfX, double * padfY,
+                            double * padfZ, double * padfM )
+
+{
+    SHPObject  *psObject;
+    int                i, bHasM, bHasZ;
+
+    psObject = (SHPObject *) calloc(1,sizeof(SHPObject));
+    psObject->nSHPType = nSHPType;
+    psObject->nShapeId = nShapeId;
+
+/* -------------------------------------------------------------------- */
+/*     Establish whether this shape type has M, and Z values.          */
+/* -------------------------------------------------------------------- */
+    if( nSHPType == SHPT_ARCM
+        || nSHPType == SHPT_POINTM
+        || nSHPType == SHPT_POLYGONM
+        || nSHPType == SHPT_MULTIPOINTM )
+    {
+        bHasM = TRUE;
+        bHasZ = FALSE;
+    }
+    else if( nSHPType == SHPT_ARCZ
+             || nSHPType == SHPT_POINTZ
+             || nSHPType == SHPT_POLYGONZ
+             || nSHPType == SHPT_MULTIPOINTZ
+             || nSHPType == SHPT_MULTIPATCH )
+    {
+        bHasM = TRUE;
+        bHasZ = TRUE;
+    }
+    else
+    {
+        bHasM = FALSE;
+        bHasZ = FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Capture parts.  Note that part type is optional, and            */
+/*      defaults to ring.                                               */
+/* -------------------------------------------------------------------- */
+    if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON
+        || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM
+        || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ
+        || nSHPType == SHPT_MULTIPATCH )
+    {
+        psObject->nParts = MAX(1,nParts);
+
+        psObject->panPartStart = (int *)
+            malloc(sizeof(int) * psObject->nParts);
+        psObject->panPartType = (int *)
+            malloc(sizeof(int) * psObject->nParts);
+
+        psObject->panPartStart[0] = 0;
+        psObject->panPartType[0] = SHPP_RING;
+        
+        for( i = 0; i < nParts; i++ )
+        {
+            psObject->panPartStart[i] = panPartStart[i];
+            if( panPartType != NULL )
+                psObject->panPartType[i] = panPartType[i];
+            else
+                psObject->panPartType[i] = SHPP_RING;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Capture vertices.  Note that Z and M are optional, but X and    */
+/*      Y are not.                                                      */
+/* -------------------------------------------------------------------- */
+    psObject->padfX = (double *) calloc(sizeof(double),nVertices);
+    psObject->padfY = (double *) calloc(sizeof(double),nVertices);
+    psObject->padfZ = (double *) calloc(sizeof(double),nVertices);
+    psObject->padfM = (double *) calloc(sizeof(double),nVertices);
+
+    assert( padfX != NULL );
+    assert( padfY != NULL );
+    
+    for( i = 0; i < nVertices; i++ )
+    {
+        psObject->padfX[i] = padfX[i];
+        psObject->padfY[i] = padfY[i];
+        if( padfZ != NULL && bHasZ )
+            psObject->padfZ[i] = padfZ[i];
+        if( padfM != NULL && bHasM )
+            psObject->padfM[i] = padfM[i];
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Compute the extents.                                            */
+/* -------------------------------------------------------------------- */
+    psObject->nVertices = nVertices;
+    
+    SHPComputeExtents( psObject );
+
+    return( psObject );
+}
+
+/************************************************************************/
+/*                       SHPCreateSimpleObject()                        */
+/*                                                                      */
+/*      Create a simple (common) shape object.  Destroy with            */
+/*      SHPDestroyObject().                                             */
+/************************************************************************/
+
+SHPObject *SHPCreateSimpleObject( int nSHPType, int nVertices,
+                                  double * padfX, double * padfY,
+                                  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 SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject )
+                     
+{
+    int                nRecordOffset, i, nRecordSize;
+    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 );
+
+/* -------------------------------------------------------------------- */
+/*      Add the new entity to the in memory index.                      */
+/* -------------------------------------------------------------------- */
+    if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords )
+    {
+       psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100);
+
+       psSHP->panRecOffset = (int *) 
+         SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords );
+       psSHP->panRecSize = (int *) 
+         SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Initialize record.                                              */
+/* -------------------------------------------------------------------- */
+    pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) 
+                              + psObject->nParts * 8 + 128);
+    
+/* -------------------------------------------------------------------- */
+/*  Extract vertices for a Polygon or Arc.                             */
+/* -------------------------------------------------------------------- */
+    if( psSHP->nShapeType == SHPT_POLYGON
+        || psSHP->nShapeType == SHPT_POLYGONZ
+        || psSHP->nShapeType == SHPT_POLYGONM
+        || psSHP->nShapeType == SHPT_ARC 
+        || psSHP->nShapeType == SHPT_ARCZ
+        || psSHP->nShapeType == SHPT_ARCM
+        || psSHP->nShapeType == 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( psSHP->nShapeType == 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( psSHP->nShapeType == SHPT_POLYGONZ
+            || psSHP->nShapeType == SHPT_ARCZ
+            || psSHP->nShapeType == 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( psSHP->nShapeType == SHPT_POLYGONM
+            || psSHP->nShapeType == SHPT_ARCM
+#ifndef DISABLE_MULTIPATCH_MEASURE            
+            || psSHP->nShapeType == SHPT_MULTIPATCH
+#endif            
+            || psSHP->nShapeType == SHPT_POLYGONZ
+            || psSHP->nShapeType == 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( psSHP->nShapeType == SHPT_MULTIPOINT
+             || psSHP->nShapeType == SHPT_MULTIPOINTZ
+             || psSHP->nShapeType == 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( psSHP->nShapeType == 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( psSHP->nShapeType == SHPT_MULTIPOINTZ
+            || psSHP->nShapeType == 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;
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Extract vertices for a point.                                   */
+/* -------------------------------------------------------------------- */
+    else if( psSHP->nShapeType == SHPT_POINT
+             || psSHP->nShapeType == SHPT_POINTZ
+             || psSHP->nShapeType == 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( psSHP->nShapeType == SHPT_POINTZ )
+        {
+            ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+        }
+        
+        if( psSHP->nShapeType == SHPT_POINTZ
+            || psSHP->nShapeType == SHPT_POINTM )
+        {
+            ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 );
+            if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
+            nRecordSize += 8;
+        }
+    }
+
+    else
+    {
+        /* unknown type */
+        assert( FALSE );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Establish where we are going to put this record. If we are      */
+/*      rewriting and existing record, and it will fit, then put it     */
+/*      back where the original came from.  Otherwise write at the end. */
+/* -------------------------------------------------------------------- */
+    if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 )
+    {
+        if( nShapeId == -1 )
+            nShapeId = psSHP->nRecords++;
+
+        psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize;
+        psSHP->panRecSize[nShapeId] = nRecordSize-8;
+        psSHP->nFileSize += nRecordSize;
+    }
+    else
+    {
+        nRecordOffset = psSHP->panRecOffset[nShapeId];
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      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 = psSHP->nShapeType;                           /* shape type */
+    if( bBigEndian ) SwapWord( 4, &i32 );
+    ByteCopy( &i32, pabyRec + 8, 4 );
+
+/* -------------------------------------------------------------------- */
+/*      Write out record.                                               */
+/* -------------------------------------------------------------------- */
+    if( fseek( psSHP->fpSHP, nRecordOffset, 0 ) != 0
+        || fwrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 )
+    {
+        printf( "Error in fseek() or fwrite().\n" );
+        free( pabyRec );
+        return -1;
+    }
+    
+    free( pabyRec );
+
+/* -------------------------------------------------------------------- */
+/*     Expand file wide bounds based on this shape.                    */
+/* -------------------------------------------------------------------- */
+    if( psSHP->nRecords == 1 )
+    {
+       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 *SHPReadObject( SHPHandle psSHP, int hEntity )
+
+{
+    SHPObject          *psShape;
+
+/* -------------------------------------------------------------------- */
+/*      Validate the record/entity number.                              */
+/* -------------------------------------------------------------------- */
+    if( hEntity < 0 || hEntity >= psSHP->nRecords )
+        return( NULL );
+
+/* -------------------------------------------------------------------- */
+/*      Ensure our record buffer is large enough.                       */
+/* -------------------------------------------------------------------- */
+    if( psSHP->panRecSize[hEntity]+8 > nBufSize )
+    {
+       nBufSize = psSHP->panRecSize[hEntity]+8;
+       pabyRec = (uchar *) SfRealloc(pabyRec,nBufSize);
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Read the record.                                                */
+/* -------------------------------------------------------------------- */
+    fseek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 );
+    fread( pabyRec, psSHP->panRecSize[hEntity]+8, 1, psSHP->fpSHP );
+
+/* -------------------------------------------------------------------- */
+/*     Allocate and minimally initialize the object.                   */
+/* -------------------------------------------------------------------- */
+    psShape = (SHPObject *) calloc(1,sizeof(SHPObject));
+    psShape->nShapeId = hEntity;
+
+    memcpy( &psShape->nSHPType, 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;
+
+/* -------------------------------------------------------------------- */
+/*     Get the X/Y bounds.                                             */
+/* -------------------------------------------------------------------- */
+        memcpy( &(psShape->dfXMin), pabyRec + 8 +  4, 8 );
+        memcpy( &(psShape->dfYMin), pabyRec + 8 + 12, 8 );
+        memcpy( &(psShape->dfXMax), pabyRec + 8 + 20, 8 );
+        memcpy( &(psShape->dfYMax), 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, pabyRec + 40 + 8, 4 );
+       memcpy( &nParts, pabyRec + 36 + 8, 4 );
+
+       if( bBigEndian ) SwapWord( 4, &nPoints );
+       if( bBigEndian ) SwapWord( 4, &nParts );
+
+       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));
+
+        for( i = 0; i < nParts; i++ )
+            psShape->panPartType[i] = SHPP_RING;
+
+/* -------------------------------------------------------------------- */
+/*      Copy out the part array from the record.                        */
+/* -------------------------------------------------------------------- */
+       memcpy( psShape->panPartStart, pabyRec + 44 + 8, 4 * nParts );
+       for( i = 0; i < nParts; i++ )
+       {
+           if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i );
+       }
+
+       nOffset = 44 + 8 + 4*nParts;
+
+/* -------------------------------------------------------------------- */
+/*      If this is a multipatch, we will also have parts types.         */
+/* -------------------------------------------------------------------- */
+        if( psShape->nSHPType == SHPT_MULTIPATCH )
+        {
+            memcpy( psShape->panPartType, 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,
+                  pabyRec + nOffset + i * 16,
+                  8 );
+
+           memcpy(psShape->padfY + i,
+                  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), pabyRec + nOffset, 8 );
+            memcpy( &(psShape->dfZMax), 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,
+                        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( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints )
+        {
+            memcpy( &(psShape->dfMMin), pabyRec + nOffset, 8 );
+            memcpy( &(psShape->dfMMax), 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,
+                        pabyRec + nOffset + 16 + i*8, 8 );
+                if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
+            }
+        }
+        
+    }
+
+/* ==================================================================== */
+/*  Extract vertices for a MultiPoint.                                 */
+/* ==================================================================== */
+    else if( psShape->nSHPType == SHPT_MULTIPOINT
+             || psShape->nSHPType == SHPT_MULTIPOINTM
+             || psShape->nSHPType == SHPT_MULTIPOINTZ )
+    {
+       int32           nPoints;
+       int             i, nOffset;
+
+       memcpy( &nPoints, pabyRec + 44, 4 );
+       if( bBigEndian ) SwapWord( 4, &nPoints );
+
+       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));
+
+       for( i = 0; i < nPoints; i++ )
+       {
+           memcpy(psShape->padfX+i, pabyRec + 48 + 16 * i, 8 );
+           memcpy(psShape->padfY+i, 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), pabyRec + 8 +  4, 8 );
+        memcpy( &(psShape->dfYMin), pabyRec + 8 + 12, 8 );
+        memcpy( &(psShape->dfXMax), pabyRec + 8 + 20, 8 );
+        memcpy( &(psShape->dfYMax), 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), pabyRec + nOffset, 8 );
+            memcpy( &(psShape->dfZMax), 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,
+                        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( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints )
+        {
+            memcpy( &(psShape->dfMMin), pabyRec + nOffset, 8 );
+            memcpy( &(psShape->dfMMax), 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,
+                        pabyRec + nOffset + 16 + i*8, 8 );
+                if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
+            }
+        }
+    }
+
+/* ==================================================================== */
+/*      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));
+
+       memcpy( psShape->padfX, pabyRec + 12, 8 );
+       memcpy( psShape->padfY, 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, 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( psSHP->panRecSize[hEntity]+8 >= nOffset + 8 )
+        {
+            memcpy( psShape->padfM, pabyRec + nOffset, 8 );
+        
+            if( bBigEndian ) SwapWord( 8, psShape->padfM );
+        }
+
+/* -------------------------------------------------------------------- */
+/*      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 *SHPTypeName( int nSHPType )
+
+{
+    switch( nSHPType )
+    {
+      case 0:
+        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 *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 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 );
+}