diff --git a/src/Lib/shapelib-1.2.10.tar.gz b/src/Lib/shapelib-1.2.10.tar.gz new file mode 100644 index 00000000..9114d332 Binary files /dev/null and b/src/Lib/shapelib-1.2.10.tar.gz differ diff --git a/src/Lib/shapelib-1.2.5.tar.gz b/src/Lib/shapelib-1.2.5.tar.gz deleted file mode 100644 index bef122f3..00000000 Binary files a/src/Lib/shapelib-1.2.5.tar.gz and /dev/null differ diff --git a/src/Lib/shapelib/ChangeLog b/src/Lib/shapelib/ChangeLog index 5b633a2d..0675b70c 100644 --- a/src/Lib/shapelib/ChangeLog +++ b/src/Lib/shapelib/ChangeLog @@ -1,3 +1,201 @@ +2003-04-07 Frank Warmerdam + + * Issue 1.2.10 Release. + +2003-03-10 Frank Warmerdam + + * dbfopen.c: modified DBFWriteAttribute call so that it returns FALSE + if it has to truncate the input value. + +2003-01-28 Frank Warmerdam + + * shptree.c: avoid build warnings. + +2002-05-07 Frank Warmerdam + + * dbfopen.c: Added DBFWriteAttributeDirectly() from the AVCE00 + distribution to simplify AVC integration in GDAL. + + * shptree.c: added use of qsort() in place of bubble sort as + submitted by Bernhard Herzog. + +2002-04-10 Frank Warmerdam + + * shpopen.c: Added SHPRewindObject() to correct ring winding. + + * shprewind.c: New utility program. + +2002-03-12 Frank Warmerdam + + * shapelib.def: added DBFWriteNULLAttribute. + +2002-01-17 Frank Warmerdam + + * contrib/ShapeFileII.pas: Contributed Delphi Pascal interface + to Shapelib. + +2002-01-15 Frank Warmerdam + + * shapelib.h: Added support for SHAPELIB_DLLEXPORT macro, and write + up material attempting to explain the use of SHPAPI_CALL macros. + + * dbfopen.c: Compute nHeaderLength in DBFCloneEmpty() instead of + copying it from the source file so we don't have quirks when copying + from files with extra bytes of spacers in the header that don't + themselves get copied properly. + +2001-12-07 Frank Warmerdam + + * shpopen.c: Fix fclose() of SHX file if SHX file fails to open. + Should be closing SHP file. Reported by Ben Discoe. + +2001-11-28 Frank Warmerdam + + * dbfopen.c: two fixes for compiler warnings as suggested by + Richard Hash. + +2001-11-01 Frank Warmerdam + + * shpopen.c/shapefil.h: Move record buffer into SHPInfo so that + different threads can safely access separate files. Other threading + issues may remain. + +2001-08-28 Frank Warmerdam + + * Issue Shapelib 1.2.9 + + * shputils.c: DBFAddField() call should check for -1 return value + for failure. + +2001-07-03 Frank Warmerdam + + * shpopen.c: cleanup better if SHX missing, provided by + Riccardo Cohen. + +2001-06-21 Frank Warmerdam + + * dbfopen.c: Fixed NULL support with patches from Jim Matthews. + + * shpopen.c: Be more careful of establishing initial file bounds in + face of possible NULL shapes. + +2001-06-01 Frank Warmerdam + + * dbfopen.c: ensure binary mode open. + +2001-05-31 Frank Warmerdam + + * shpopen.c: Add support for writing null shapes. + + * dbfopen.c: added DBFGetFieldIndex(), contributed by Jim Matthews. + + * dbfopen.c/shapefil.h/dbf_api.h: added support for NULL fields + in .dbf files. + +2001-05-28 Frank Warmerdam + + * shpopen.c: add some checking on the record count to ensure it + is reasonable. + +2001-05-23 Frank Warmerdam + + * shapefile.h, shpopen.c, dbfopen.c, shptree.c: added the SHPAPI_CALL + macro to allow compilation with _stdcall conventions. + +2001-02-06 Frank Warmerdam + + * Fixed a few memory leaks when SHPOpen() fails. + +2000-12-05 Frank Warmerdam + + * Fix from Craig Bruce (Cubewerx) for DBFReadAttribute() for + the white space trimming code to avoid reading outside allocated + memory. + +2000-11-02 Frank Warmerdam + + * Checked in upgraded shputils.c from Bill Miller. + +2000-10-05 Frank Warmerdam + + * Fixed DBFWriteAttribute() to ensure we can't overwrite the + end of the szSField buffer even if the width is set large. + Bug report by Kirk Benell . + +2000-09-25 Frank Warmerdam + + * Added DBFGetNativeFieldType() (contributed by Daniel) to dbfopen.c. + +2000-07-18 Frank Warmerdam + + * added better enforcement of -1 for append in SHPWriteObject(). + +2000-07-07 Frank Warmerdam + + * Added stdlib.h and string.h where needed, and removed lots of + unused variables, mainly from example mainlines at the suggestion + of Bill Hughes. + +2000-05-24 Frank Warmerdam + + * Added logic to shpadd to grow vertex lists at the suggestion of + Santiago Nullo . + +2000-05-23 Frank Warmerdam + + * Added checks in dbfopen.c on return result of fseek() and fread(). + + * Avoid crashing in DBReadIntegerAttribute() or DBFReadDoubleAttribte() + if the field or record are out of range. + +2000-03-28 Frank Warmerdam + + * Release as 1.2.8. + + * Incorporated a -version-info fix and added mkinstalldirs from Jan. + +2000-03-17 Frank Warmerdam + + * Added shared library hack to Makefile. + + * Fixed up test scripts to look in ./ for executables. + +Wed Feb 16 11:20:29 2000 Frank Warmerdam + + * Release 1.2.7. + + * Modified SHPReadObject() so that will return NULL (type 0) shapes + in a sort of sensible way. + +Wed Dec 15 08:49:53 1999 Frank Warmerdam + + * Fixed record size written at beginning of records in .shp + file. It was 4 bytes to long (thanks to Mikko Syrja of 3D-system Oy) + + * Use atof() instead of sscanf() in dbfopen.c, and add stdlib.h. + +Mon Dec 13 12:29:01 1999 Frank Warmerdam + + * Added support for uppercase .DBF extention c/o + Dennis Christopher + +Fri Nov 5 09:12:31 1999 Frank Warmerdam + + * Updated license headers to include the option of use of the code + under the LGPL. + +1999-09-15 + + * Added shapelib.dll target to makefile.vc. + +Mon May 10 23:19:42 1999 Frank Warmerdam + + * Added candrsn's improvements to extension handling in dbfopen.c + + * Added ``raw tuple'' api to dbfopen.c, still not in dbf_api.html. + From candrsn. + + Tue May 4 11:04:31 1999 Frank Warmerdam * Prepare 1.2.5 release. diff --git a/src/Lib/shapelib/dbf_api.html b/src/Lib/shapelib/dbf_api.html index 15f85ec8..b0fa3737 100644 --- a/src/Lib/shapelib/dbf_api.html +++ b/src/Lib/shapelib/dbf_api.html @@ -80,6 +80,22 @@ int DBFGetRecordCount( DBFHandle hDBF ); +

DBFGetFieldIndex()

+ +
+int DBFGetFieldIndex( DBFHandle hDBF, const char *pszFieldName );
+
+  hDBF:		The access handle for the file to be queried, as returned by
+		DBFOpen(), or DBFCreate().
+
+  pszFieldName: Name of the field to search for.
+
+ + Returns the index of the field matching this name, or -1 on failure. The + comparison is case insensitive. However, lengths must match exactly.

+ + +

DBFGetFieldInfo()

@@ -119,6 +135,7 @@ DBFFieldType DBFGetFieldInfo( DBFHandle hDBF, int iField, char * pszFieldName,
       FTString,			/* fixed length string field 		*/
       FTInteger,		/* numeric field with no decimals 	*/
       FTDouble,			/* numeric field with decimals 		*/
+      FTLogical,		/* logical field.                       */
       FTInvalid                 /* not a recognised field type 		*/
     } DBFFieldType;
 
@@ -145,7 +162,8 @@ int DBFAddField( DBFHandle hDBF, const char * pszFieldName, nWidth: The width of the field to be created. For FTString fields this establishes the maximum length of string that can be stored. - For FTInteger this establishes the largest number that can + For FTInteger this establishes the number of digits of the + largest number that can be represented. For FTDouble fields this in combination with the nDecimals value establish the size, and precision of the created field. @@ -232,6 +250,28 @@ const char *DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField ); +

DBFIsAttributeNULL()

+ +
+int DBFIsAttributeNULL( DBFHandle hDBF, int iShape, int iField );
+  
+  hDBF:		The access handle for the file to be queried, as returned by
+		DBFOpen(), or DBFCreate().
+
+  iShape:	The record number (shape number) from which the field value
+                should be read.
+
+  iField:	The field within the selected record that should be read.
+
+ + This function will return TRUE if the indicated field is NULL valued + otherwise FALSE. Note that NULL fields are represented in the .dbf file + as having all spaces in the field. Reading NULL fields will result in + a value of 0.0 or an empty string with the other DBFRead*Attribute() + functions.

+ + +

DBFWriteIntegerAttribute

@@ -251,8 +291,8 @@ int DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField,
 
 The DBFWriteIntegerAttribute() function is used to write a value to a numeric
 field (FTInteger, or FTDouble).  If the write succeeds the value TRUE will
-be returned, otherwise FALSE will be returned.  The value may be truncated 
-without warning if written to a field to narrow to represent the value.

+be returned, otherwise FALSE will be returned. If the value is too large to +fit in the field, it will be truncated and FALSE returned.

@@ -275,8 +315,8 @@ int DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField, The DBFWriteDoubleAttribute() function is used to write a value to a numeric field (FTInteger, or FTDouble). If the write succeeds the value TRUE will -be returned, otherwise FALSE will be returned. The value may be truncated -without warning if written to a field to narrow to represent the value.

+be returned, otherwise FALSE will be returned. If the value is too large to +fit in the field, it will be truncated and FALSE returned.

@@ -299,8 +339,29 @@ int DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField, The DBFWriteStringAttribute() function is used to write a value to a string field (FString). If the write succeeds the value TRUE willbe returned, -otherwise FALSE will be returned. The value may be truncated -without warning if written to a field to narrow to hold the string.

+otherwise FALSE will be returned. If the value is too large to +fit in the field, it will be truncated and FALSE returned.

+ + + +

DBFWriteNULLAttribute()

+ +
+int DBFWriteNULLAttribute( DBFHandle hDBF, int iShape, int iField );
+
+  hDBF:		The access handle for the file to be written, as returned by
+		DBFOpen(), or DBFCreate().
+
+  iShape:	The record number (shape number) to which the field value
+                should be written.
+
+  iField:	The field within the selected record that should be written.
+
+ +The DBFWriteNULLAttribute() function is used to clear the indicated field +to a NULL value. In the .dbf file this is represented by setting the entire +field to spaces. If the write succeeds the value TRUE willbe returned, +otherwise FALSE will be returned.

@@ -318,5 +379,30 @@ void DBFClose( DBFHandle hDBF ); The file handle (hDBF) should not be used again with the DBF API after calling DBFClose().

+ + +

DBFGetNativeFieldType()

+ +
+char DBFGetNativeFieldType( DBFHandle hDBF, int iField );
+
+  hDBF:		The access handle for the file.
+  iField:       The field index to query.
+  
+
+ + This function returns the DBF type code of the indicated field. It will + be one of:

+ +

    +
  • 'C' (String) +
  • 'D' (Date) +
  • 'F' (Float) +
  • 'N' (Numeric, with or without decimal) +
  • 'L' (Logical) +
  • 'M' (Memo: 10 digits .DBT block ptr) +
  • ' ' (field out of range) +
+ diff --git a/src/Lib/shapelib/dbfadd.c b/src/Lib/shapelib/dbfadd.c index 47658d0b..f9782087 100644 --- a/src/Lib/shapelib/dbfadd.c +++ b/src/Lib/shapelib/dbfadd.c @@ -1,14 +1,47 @@ -/* - * Copyright (c) 1995 Frank Warmerdam +/****************************************************************************** + * $Id: dbfadd.c,v 1.7 2002/01/15 14:36:07 warmerda Exp $ * - * This code is in the public domain. + * Project: Shapelib + * Purpose: Sample application for adding a record to an existing .dbf file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** * * $Log: dbfadd.c,v $ - * Revision 1.1 2000-02-09 19:51:46 curt - * Initial revision + * Revision 1.7 2002/01/15 14:36:07 warmerda + * updated email address * - * Revision 1.1 1999/08/24 21:13:00 curt - * Initial revision. + * Revision 1.6 2001/05/31 18:15:40 warmerda + * Added support for NULL fields in DBF files + * + * Revision 1.5 1999/11/05 14:12:04 warmerda + * updated license terms * * Revision 1.4 1998/12/03 16:36:06 warmerda * Added stdlib.h and math.h to get atof() prototype. @@ -22,7 +55,7 @@ */ static char rcsid[] = - "$Id: dbfadd.c,v 1.1 2000-02-09 19:51:46 curt Exp $"; + "$Id: dbfadd.c,v 1.7 2002/01/15 14:36:07 warmerda Exp $"; #include "shapefil.h" #include @@ -71,7 +104,9 @@ int main( int argc, char ** argv ) /* -------------------------------------------------------------------- */ for( i = 0; i < DBFGetFieldCount(hDBF); i++ ) { - if( DBFGetFieldInfo( hDBF, i, NULL, NULL, NULL ) == FTString ) + if( strcmp( argv[i+2], "" ) == 0 ) + DBFWriteNULLAttribute(hDBF, iRecord, i ); + else if( DBFGetFieldInfo( hDBF, i, NULL, NULL, NULL ) == FTString ) DBFWriteStringAttribute(hDBF, iRecord, i, argv[i+2] ); else DBFWriteDoubleAttribute(hDBF, iRecord, i, atof(argv[i+2]) ); diff --git a/src/Lib/shapelib/dbfcreate.c b/src/Lib/shapelib/dbfcreate.c index b1171ccb..f1e22abe 100644 --- a/src/Lib/shapelib/dbfcreate.c +++ b/src/Lib/shapelib/dbfcreate.c @@ -1,14 +1,47 @@ -/* - * Copyright (c) 1995 Frank Warmerdam +/****************************************************************************** + * $Id: dbfcreate.c,v 1.6 2002/01/15 14:36:07 warmerda Exp $ * - * This code is in the public domain. + * Project: Shapelib + * Purpose: Sample application for creating a new .dbf file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** * * $Log: dbfcreate.c,v $ - * Revision 1.1 2000-02-09 19:51:46 curt - * Initial revision + * Revision 1.6 2002/01/15 14:36:07 warmerda + * updated email address * - * Revision 1.1 1999/08/24 21:13:00 curt - * Initial revision. + * Revision 1.5 2000/07/07 13:39:45 warmerda + * removed unused variables, and added system include files + * + * Revision 1.4 1999/11/05 14:12:04 warmerda + * updated license terms * * Revision 1.3 1999/04/01 18:47:44 warmerda * Fixed DBFAddField() call convention. @@ -19,17 +52,17 @@ */ static char rcsid[] = - "$Id: dbfcreate.c,v 1.1 2000-02-09 19:51:46 curt Exp $"; + "$Id: dbfcreate.c,v 1.6 2002/01/15 14:36:07 warmerda Exp $"; +#include +#include #include "shapefil.h" int main( int argc, char ** argv ) { DBFHandle hDBF; - int *panWidth, i, iRecord; - char szFormat[32], szField[1024]; - int nWidth, nDecimals; + int i; /* -------------------------------------------------------------------- */ /* Display a usage message. */ diff --git a/src/Lib/shapelib/dbfdump.c b/src/Lib/shapelib/dbfdump.c index 8ee92fb8..9fe44fa6 100644 --- a/src/Lib/shapelib/dbfdump.c +++ b/src/Lib/shapelib/dbfdump.c @@ -1,14 +1,53 @@ -/* - * Copyright (c) 1995 Frank Warmerdam +/****************************************************************************** + * $Id: dbfdump.c,v 1.9 2002/01/15 14:36:07 warmerda Exp $ * - * This code is in the public domain. + * Project: Shapelib + * Purpose: Sample application for dumping .dbf files to the terminal. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** * * $Log: dbfdump.c,v $ - * Revision 1.1 2000-02-09 19:51:46 curt - * Initial revision + * Revision 1.9 2002/01/15 14:36:07 warmerda + * updated email address * - * Revision 1.1 1999/08/24 21:13:00 curt - * Initial revision. + * Revision 1.8 2001/05/31 18:15:40 warmerda + * Added support for NULL fields in DBF files + * + * Revision 1.7 2000/09/20 13:13:55 warmerda + * added break after default: + * + * Revision 1.6 2000/07/07 13:39:45 warmerda + * removed unused variables, and added system include files + * + * Revision 1.5 1999/11/05 14:12:04 warmerda + * updated license terms * * Revision 1.4 1998/12/31 15:30:13 warmerda * Added -m, -r, and -h commandline options. @@ -22,8 +61,10 @@ */ static char rcsid[] = - "$Id: dbfdump.c,v 1.1 2000-02-09 19:51:46 curt Exp $"; + "$Id: dbfdump.c,v 1.9 2002/01/15 14:36:07 warmerda Exp $"; +#include +#include #include "shapefil.h" int main( int argc, char ** argv ) @@ -31,7 +72,7 @@ int main( int argc, char ** argv ) { DBFHandle hDBF; int *panWidth, i, iRecord; - char szFormat[32], szField[1024], *pszFilename = NULL; + char szFormat[32], *pszFilename = NULL; int nWidth, nDecimals; int bHeader = 0; int bRaw = 0; @@ -159,25 +200,40 @@ int main( int argc, char ** argv ) /* -------------------------------------------------------------------- */ if( !bRaw ) { - switch( eType ) + if( DBFIsAttributeNULL( hDBF, iRecord, i ) ) { - case FTString: - sprintf( szFormat, "%%-%ds", nWidth ); - printf( szFormat, - DBFReadStringAttribute( hDBF, iRecord, i ) ); - break; - - case FTInteger: - sprintf( szFormat, "%%%dd", nWidth ); - printf( szFormat, - DBFReadIntegerAttribute( hDBF, iRecord, i ) ); - break; + if( eType == FTString ) + sprintf( szFormat, "%%-%ds", nWidth ); + else + sprintf( szFormat, "%%%ds", nWidth ); - case FTDouble: - sprintf( szFormat, "%%%d.%dlf", nWidth, nDecimals ); - printf( szFormat, - DBFReadDoubleAttribute( hDBF, iRecord, i ) ); - break; + printf( szFormat, "(NULL)" ); + } + else + { + switch( eType ) + { + case FTString: + sprintf( szFormat, "%%-%ds", nWidth ); + printf( szFormat, + DBFReadStringAttribute( hDBF, iRecord, i ) ); + break; + + case FTInteger: + sprintf( szFormat, "%%%dd", nWidth ); + printf( szFormat, + DBFReadIntegerAttribute( hDBF, iRecord, i ) ); + break; + + case FTDouble: + sprintf( szFormat, "%%%d.%dlf", nWidth, nDecimals ); + printf( szFormat, + DBFReadDoubleAttribute( hDBF, iRecord, i ) ); + break; + + default: + break; + } } } diff --git a/src/Lib/shapelib/dbfopen.c b/src/Lib/shapelib/dbfopen.c index bbecd24e..43b25ac2 100644 --- a/src/Lib/shapelib/dbfopen.c +++ b/src/Lib/shapelib/dbfopen.c @@ -1,6 +1,19 @@ /****************************************************************************** - * Copyright (c) 1998, Frank Warmerdam + * $Id: dbfopen.c,v 1.48 2003/03/10 14:51:27 warmerda Exp $ * + * Project: Shapelib + * Purpose: Implementation of .dbf access API documented in dbf_api.html. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation @@ -21,134 +34,108 @@ ****************************************************************************** * * $Log: dbfopen.c,v $ - * Revision 1.2 2000-11-25 19:39:49 curt - * Contributed by Bruce Finney: + * Revision 1.48 2003/03/10 14:51:27 warmerda + * DBFWrite* calls now return FALSE if they have to truncate * - * The following files have been changed to enable the latest Terragear CVS - * to compile with MSVC++ 5.0 + * Revision 1.47 2002/11/20 03:32:22 warmerda + * Ensure field name in DBFGetFieldIndex() is properly terminated. * - * .\construct\clipper\clipper.cxx - * for( int i - lots of places + * Revision 1.46 2002/10/09 13:10:21 warmerda + * Added check that width is positive. * - * .\construct\genoutput\genobj.cxx - * fix directory logic for windows, line 320 and following + * Revision 1.45 2002/09/29 00:00:08 warmerda + * added FTLogical and logical attribute read/write calls * - * .\construct\main\main.cxx - * windows does not have an opendir function - * added code for windows directory functions - * disabled the mem allocation limit code - windows does not - * have similar functions - * for ( int i - several places + * Revision 1.44 2002/05/07 13:46:11 warmerda + * Added DBFWriteAttributeDirectly(). * - * .\construct\match\match.cxx - * moved the definition of file and command outside of the ifdef line 420 + * Revision 1.43 2002/02/13 19:39:21 warmerda + * Fix casting issues in DBFCloneEmpty(). * - * .\lib\e00\e00.cxx - * for( int i - several places + * Revision 1.42 2002/01/15 14:36:07 warmerda + * updated email address * - * .\lib\e00\e00.cxx - * use simgear/compiler.h constructs + * Revision 1.41 2002/01/15 14:31:49 warmerda + * compute rather than copying nHeaderLength in DBFCloneEmpty() * - * .\lib\geometry\contour_tree.hxx - * removed a cout statement + * Revision 1.40 2002/01/09 04:32:35 warmerda + * fixed to read correct amount of header * - * .\lib\geometry\poly_support.cxx - * added float.h changed 1.0e+999 to DBL_MAX, windows doesn't go that big - * lots of for ( int i changes - * lines 193 and 208 no != operator defined - changed logic - * line 801 flag should be int, not bool, get_hole_flag returns int + * Revision 1.39 2001/12/11 22:41:03 warmerda + * improve io related error checking when reading header * - * .\lib\landcover\landcover.cxx - * .\lib\landcover\landcover.hxx - * add include simgear/compiler.h - * see comments + * Revision 1.38 2001/11/28 16:07:31 warmerda + * Cleanup to avoid compiler warnings as suggested by Richard Hash. * - * .\lib\optimize\genfans.cxx - * function canonify added return at end, windows complains - * added using std for cout and endl + * Revision 1.37 2001/07/04 05:18:09 warmerda + * do last fix properly * - * .\lib\optimize\genstrips.cxx - * function tgGenStrips no return value, moved by_node into outer scope - * fix for ( int i ... + * Revision 1.36 2001/07/04 05:16:09 warmerda + * fixed fieldname comparison in DBFGetFieldIndex * - * .\lib\poly2tri\construct.c - * added include for windows - * remove unused variables lines 435 & 437 + * Revision 1.35 2001/06/22 02:10:06 warmerda + * fixed NULL shape support with help from Jim Matthews * - * .\lib\poly2tri\misc.c - * added HAVE_SYS_TIME_H logic for sys/time.h include file - * added logic to uses windows functions for time and rand + * Revision 1.33 2001/05/31 19:20:13 warmerda + * added DBFGetFieldIndex() * - * .\lib\poly2tri\monotone.c - * added include for windows - * lines 286-288 remove unused variables + * Revision 1.32 2001/05/31 18:15:40 warmerda + * Added support for NULL fields in DBF files * - * .\lib\poly2tri\tri.c - * remove sys/time.h - no time functions called - * added include for windows + * Revision 1.31 2001/05/23 13:36:52 warmerda + * added use of SHPAPI_CALL * - * .\lib\polygon\polygon.cxx - * function polygon_to_tristrip will not compile I don't think the - * logic is complete, no returned data added if else endif around - * function and polygon_to_tristrip_old, renamed _old function. - * Search of code reveals that function is not called by anyone. + * Revision 1.30 2000/12/05 14:43:38 warmerda + * DBReadAttribute() white space trimming bug fix * - * .\lib\polygon\superpoly.cxx - * changed include to "superpoly.hxx" + * Revision 1.29 2000/10/05 14:36:44 warmerda + * fix bug with writing very wide numeric fields * - * .\lib\polygon\superpoly.hxx - * add include for windows before include - * needed for definitions used in Microsoft version of opengl + * Revision 1.28 2000/09/25 14:18:07 warmerda + * Added some casts of strlen() return result to fix warnings on some + * systems, as submitted by Daniel. * - * .\lib\shapelib\dbfopen.c - * added include files for windows - * lines 195-197 271-272 515-517 removed unused variables + * Revision 1.27 2000/09/25 14:15:51 warmerda + * added DBFGetNativeFieldType() * - * .\lib\shapelib\shpopen.c - * added #include for malloc() and friends - * added include files for windows - * line 279 527 813 1127 removed unused variables - * line 827 cast result to int + * Revision 1.26 2000/07/07 13:39:45 warmerda + * removed unused variables, and added system include files * - * .\lib\win32\mkdir.cpp - * documented function, remove debug lines + * Revision 1.25 2000/05/29 18:19:13 warmerda + * avoid use of uchar, and adding casting fix * - * .\prep\demraw2ascii\main.c - * lines 46-50 remove unused variables + * Revision 1.24 2000/05/23 13:38:27 warmerda + * Added error checks on return results of fread() and fseek(). * - * .\prep\demraw2ascii\rawdem.c - * line 47 changed logic to compile with MSVC - * line 244-256 set real constants to float, windows complains with - * double constants + * Revision 1.23 2000/05/23 13:25:49 warmerda + * Avoid crashing if field or record are out of range in dbfread*attribute(). * - * .\prep\genairports\build.cxx - * lots of for ( int i changes + * Revision 1.22 1999/12/15 13:47:24 warmerda + * Added stdlib.h to ensure that atof() is prototyped. * - * .\prep\genairports\main.cxx - * fix mkdir logic for windows + * Revision 1.21 1999/12/13 17:25:46 warmerda + * Added support for upper case .DBF extention. * - * .\prep\genairports\output.cxx - * added using std cout endl - * lots of for ( int i changes - * fix mkdir logic for windows + * Revision 1.20 1999/11/30 16:32:11 warmerda + * Use atof() instead of sscanf(). * - * .\prep\genairports\runway.cxx - * for ( int i changes - * lines 117-118 161-162 remove default values for function parameters + * Revision 1.19 1999/11/05 14:12:04 warmerda + * updated license terms * - * .\prep\gshhs\main.cxx - * added using std cout + * Revision 1.18 1999/07/27 00:53:28 warmerda + * ensure that whole old field value clear on write of string * - * .\prep\shapefile\noaa_decode.cxx - * .\prep\shapefile\shape_decode.cxx - * added using std for cout - * lines 45-49 moved unused variables inside #if 0 block + * Revision 1.1 1999/07/05 18:58:07 warmerda + * New * - * Revision 1.1.1.1 2000/02/09 19:51:46 curt - * Initial revision + * Revision 1.17 1999/06/11 19:14:12 warmerda + * Fixed some memory leaks. * - * Revision 1.1 1999/08/24 21:13:00 curt - * Initial revision. + * 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. @@ -196,24 +183,23 @@ */ static char rcsid[] = - "$Id: dbfopen.c,v 1.2 2000-11-25 19:39:49 curt Exp $"; + "$Id: dbfopen.c,v 1.48 2003/03/10 14:51:27 warmerda Exp $"; #include "shapefil.h" #include -#ifdef _MSC_VER -# include -# include -# include -#endif - -typedef unsigned char uchar; +#include +#include +#include #ifndef FALSE # define FALSE 0 # define TRUE 1 #endif +static int nStringFieldLen = 0; +static char * pszStringField = NULL; + /************************************************************************/ /* SfRealloc() */ /* */ @@ -242,7 +228,7 @@ static void * SfRealloc( void * pMem, int nNewSize ) static void DBFWriteHeader(DBFHandle psDBF) { - uchar abyHeader[32]; + unsigned char abyHeader[XBASE_FLDHDR_SZ]; int i; if( !psDBF->bNoHeader ) @@ -253,7 +239,7 @@ static void DBFWriteHeader(DBFHandle psDBF) /* -------------------------------------------------------------------- */ /* Initialize the file header information. */ /* -------------------------------------------------------------------- */ - for( i = 0; i < 32; i++ ) + for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) abyHeader[i] = 0; abyHeader[0] = 0x03; /* memo field? - just copying */ @@ -271,8 +257,8 @@ static void DBFWriteHeader(DBFHandle psDBF) /* descriptions. */ /* -------------------------------------------------------------------- */ fseek( psDBF->fp, 0, 0 ); - fwrite( abyHeader, 32, 1, psDBF->fp ); - fwrite( psDBF->pszHeader, 32, psDBF->nFields, psDBF->fp ); + 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. */ @@ -315,60 +301,81 @@ static void DBFFlushRecord( DBFHandle psDBF ) /* Open a .dbf file. */ /************************************************************************/ -DBFHandle DBFOpen( const char * pszFilename, const char * pszAccess ) +DBFHandle SHPAPI_CALL +DBFOpen( const char * pszFilename, const char * pszAccess ) { DBFHandle psDBF; - uchar *pabyBuf; - int nFields, nRecords, nHeadLen, nRecLen, iField; - char *pszDBFFilename; + unsigned char *pabyBuf; + int nFields, 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,"r+b") != 0 ) + && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0 + && strcmp(pszAccess,"r+b") != 0 ) return( NULL ); - -/* -------------------------------------------------------------------- */ -/* Ensure the extension is converted to dbf or DBF if it is */ -/* currently .shp or .shx. */ -/* -------------------------------------------------------------------- */ - pszDBFFilename = (char *) malloc(strlen(pszFilename)+1); - strcpy( pszDBFFilename, pszFilename ); - - if( strcmp(pszFilename+strlen(pszFilename)-4,".shp") - || strcmp(pszFilename+strlen(pszFilename)-4,".shx") ) - { - strcpy( pszDBFFilename+strlen(pszDBFFilename)-4, ".dbf"); - } - else if( strcmp(pszFilename+strlen(pszFilename)-4,".SHP") - || strcmp(pszFilename+strlen(pszFilename)-4,".SHX") ) - { - strcpy( pszDBFFilename+strlen(pszDBFFilename)-4, ".DBF"); - } + + if( strcmp(pszAccess,"r") == 0 ) + pszAccess = "rb"; + + if( strcmp(pszAccess,"r+") == 0 ) + pszAccess = "rb+"; /* -------------------------------------------------------------------- */ -/* Open the file. */ +/* 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( pszDBFFilename, pszAccess ); + 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; - free( pszDBFFilename ); - /* -------------------------------------------------------------------- */ /* Read Table Header info */ /* -------------------------------------------------------------------- */ - pabyBuf = (uchar *) malloc(500); - fread( pabyBuf, 32, 1, psDBF->fp ); + pabyBuf = (unsigned char *) malloc(500); + if( fread( pabyBuf, 32, 1, psDBF->fp ) != 1 ) + { + fclose( psDBF->fp ); + free( pabyBuf ); + free( psDBF ); + return NULL; + } - psDBF->nRecords = nRecords = + psDBF->nRecords = pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256; psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256; @@ -382,11 +389,17 @@ DBFHandle DBFOpen( const char * pszFilename, const char * pszAccess ) /* Read in Field Definitions */ /* -------------------------------------------------------------------- */ - pabyBuf = (uchar *) SfRealloc(pabyBuf,nHeadLen); + pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen); psDBF->pszHeader = (char *) pabyBuf; fseek( psDBF->fp, 32, 0 ); - fread( pabyBuf, nHeadLen, 1, psDBF->fp ); + if( fread( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 ) + { + fclose( psDBF->fp ); + free( pabyBuf ); + free( psDBF ); + return NULL; + } psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields); psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields); @@ -395,7 +408,7 @@ DBFHandle DBFOpen( const char * pszFilename, const char * pszAccess ) for( iField = 0; iField < nFields; iField++ ) { - uchar *pabyFInfo; + unsigned char *pabyFInfo; pabyFInfo = pabyBuf+iField*32; @@ -425,7 +438,8 @@ DBFHandle DBFOpen( const char * pszFilename, const char * pszAccess ) /* DBFClose() */ /************************************************************************/ -void DBFClose(DBFHandle psDBF) +void SHPAPI_CALL +DBFClose(DBFHandle psDBF) { /* -------------------------------------------------------------------- */ /* Write out header if not already written. */ @@ -441,7 +455,7 @@ void DBFClose(DBFHandle psDBF) /* -------------------------------------------------------------------- */ if( psDBF->bUpdated ) { - uchar abyFileHeader[32]; + unsigned char abyFileHeader[32]; fseek( psDBF->fp, 0, 0 ); fread( abyFileHeader, 32, 1, psDBF->fp ); @@ -476,6 +490,13 @@ void DBFClose(DBFHandle psDBF) free( psDBF->pszCurrentRecord ); free( psDBF ); + + if( pszStringField != NULL ) + { + free( pszStringField ); + pszStringField = NULL; + nStringFieldLen = 0; + } } /************************************************************************/ @@ -484,26 +505,49 @@ void DBFClose(DBFHandle psDBF) /* Create a new .dbf file. */ /************************************************************************/ -DBFHandle DBFCreate( const char * pszFilename ) +DBFHandle SHPAPI_CALL +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( pszFilename, "wb" ); + fp = fopen( pszFullname, "wb" ); if( fp == NULL ) return( NULL ); fputc( 0, fp ); fclose( fp ); - fp = fopen( pszFilename, "rb+" ); + fp = fopen( pszFullname, "rb+" ); if( fp == NULL ) return( NULL ); + free( pszFullname ); + /* -------------------------------------------------------------------- */ /* Create the info structure. */ /* -------------------------------------------------------------------- */ @@ -537,8 +581,9 @@ DBFHandle DBFCreate( const char * pszFilename ) /* are written. */ /************************************************************************/ -int DBFAddField(DBFHandle psDBF, const char * pszFieldName, - DBFFieldType eType, int nWidth, int nDecimals ) +int SHPAPI_CALL +DBFAddField(DBFHandle psDBF, const char * pszFieldName, + DBFFieldType eType, int nWidth, int nDecimals ) { char *pszFInfo; @@ -556,6 +601,9 @@ int DBFAddField(DBFHandle psDBF, const char * pszFieldName, if( eType != FTDouble && nDecimals != 0 ) return( -1 ); + if( nWidth < 1 ) + return -1; + /* -------------------------------------------------------------------- */ /* SfRealloc all the arrays larger to hold the additional field */ /* information. */ @@ -582,7 +630,9 @@ int DBFAddField(DBFHandle psDBF, const char * pszFieldName, psDBF->panFieldSize[psDBF->nFields-1] = nWidth; psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals; - if( eType == FTString ) + if( eType == FTLogical ) + psDBF->pachFieldType[psDBF->nFields-1] = 'L'; + else if( eType == FTString ) psDBF->pachFieldType[psDBF->nFields-1] = 'C'; else psDBF->pachFieldType[psDBF->nFields-1] = 'N'; @@ -600,7 +650,7 @@ int DBFAddField(DBFHandle psDBF, const char * pszFieldName, for( i = 0; i < 32; i++ ) pszFInfo[i] = '\0'; - if( strlen(pszFieldName) < 10 ) + if( (int) strlen(pszFieldName) < 10 ) strncpy( pszFInfo, pszFieldName, strlen(pszFieldName)); else strncpy( pszFInfo, pszFieldName, 10); @@ -638,32 +688,48 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, { int nRecordOffset; - uchar *pabyRec; + unsigned char *pabyRec; void *pReturnField = NULL; static double dDoubleField; - static char * pszStringField = NULL; - static int nStringFieldLen = 0; /* -------------------------------------------------------------------- */ -/* Have we read the record? */ +/* Verify selection. */ /* -------------------------------------------------------------------- */ if( hEntity < 0 || hEntity >= psDBF->nRecords ) return( NULL ); + if( iField < 0 || iField >= psDBF->nFields ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Have we read the record? */ +/* -------------------------------------------------------------------- */ 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 ); + if( fseek( psDBF->fp, nRecordOffset, 0 ) != 0 ) + { + fprintf( stderr, "fseek(%d) failed on DBF file.\n", + nRecordOffset ); + return NULL; + } + + if( fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, + 1, psDBF->fp ) != 1 ) + { + fprintf( stderr, "fread(%d) failed on DBF file.\n", + psDBF->nRecordLength ); + return NULL; + } psDBF->nCurrentRecord = hEntity; } - pabyRec = (uchar *) psDBF->pszCurrentRecord; + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; /* -------------------------------------------------------------------- */ /* Ensure our field buffer is large enough to hold this buffer. */ @@ -677,7 +743,8 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, /* -------------------------------------------------------------------- */ /* Extract the requested field. */ /* -------------------------------------------------------------------- */ - strncpy( pszStringField, pabyRec+psDBF->panFieldOffset[iField], + strncpy( pszStringField, + ((const char *) pabyRec) + psDBF->panFieldOffset[iField], psDBF->panFieldSize[iField] ); pszStringField[psDBF->panFieldSize[iField]] = '\0'; @@ -688,7 +755,7 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, /* -------------------------------------------------------------------- */ if( chReqType == 'N' ) { - sscanf( pszStringField, "%lf", &dDoubleField ); + dDoubleField = atof(pszStringField); pReturnField = &dDoubleField; } @@ -709,9 +776,8 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, *(pchDst++) = *(pchSrc++); *pchDst = '\0'; - while( *(--pchDst) == ' ' && pchDst != pszStringField ) + while( pchDst != pszStringField && *(--pchDst) == ' ' ) *pchDst = '\0'; - } #endif @@ -724,14 +790,18 @@ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, /* Read an integer attribute. */ /************************************************************************/ -int DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField ) +int SHPAPI_CALL +DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField ) { double *pdValue; pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); - return( (int) *pdValue ); + if( pdValue == NULL ) + return 0; + else + return( (int) *pdValue ); } /************************************************************************/ @@ -740,14 +810,18 @@ int DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField ) /* Read a double attribute. */ /************************************************************************/ -double DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField ) +double SHPAPI_CALL +DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField ) { double *pdValue; pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); - return( *pdValue ); + if( pdValue == NULL ) + return 0.0; + else + return( *pdValue ); } /************************************************************************/ @@ -756,19 +830,71 @@ double DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField ) /* Read a string attribute. */ /************************************************************************/ -const char *DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField ) +const char SHPAPI_CALL1(*) +DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField ) { return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) ); } +/************************************************************************/ +/* DBFReadLogicalAttribute() */ +/* */ +/* Read a logical attribute. */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'L' ) ); +} + +/************************************************************************/ +/* DBFIsAttributeNULL() */ +/* */ +/* Return TRUE if value for field is NULL. */ +/* */ +/* Contributed by Jim Matthews. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField ) + +{ + const char *pszValue; + + pszValue = DBFReadStringAttribute( psDBF, iRecord, iField ); + + switch(psDBF->pachFieldType[iField]) + { + case 'N': + case 'F': + /* NULL numeric fields have value "****************" */ + return pszValue[0] == '*'; + + case 'D': + /* NULL date fields have value "00000000" */ + return strncmp(pszValue,"00000000",8) == 0; + + case 'L': + /* NULL boolean fields have value "?" */ + return pszValue[0] == '?'; + + default: + /* empty string fields are considered NULL */ + return strlen(pszValue) == 0; + } +} + /************************************************************************/ /* DBFGetFieldCount() */ /* */ /* Return the number of fields in this table. */ /************************************************************************/ -int DBFGetFieldCount( DBFHandle psDBF ) +int SHPAPI_CALL +DBFGetFieldCount( DBFHandle psDBF ) { return( psDBF->nFields ); @@ -780,7 +906,8 @@ int DBFGetFieldCount( DBFHandle psDBF ) /* Return the number of records in this table. */ /************************************************************************/ -int DBFGetRecordCount( DBFHandle psDBF ) +int SHPAPI_CALL +DBFGetRecordCount( DBFHandle psDBF ) { return( psDBF->nRecords ); @@ -792,8 +919,9 @@ int DBFGetRecordCount( DBFHandle psDBF ) /* Return any requested information about the field. */ /************************************************************************/ -DBFFieldType DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, - int * pnWidth, int * pnDecimals ) +DBFFieldType SHPAPI_CALL +DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, + int * pnWidth, int * pnDecimals ) { if( iField < 0 || iField >= psDBF->nFields ) @@ -815,9 +943,12 @@ DBFFieldType DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, pszFieldName[i] = '\0'; } - if( psDBF->pachFieldType[iField] == 'N' - || psDBF->pachFieldType[iField] == 'F' - || psDBF->pachFieldType[iField] == 'D' ) + if ( psDBF->pachFieldType[iField] == 'L' ) + return( FTLogical); + + else if( psDBF->pachFieldType[iField] == 'N' + || psDBF->pachFieldType[iField] == 'F' + || psDBF->pachFieldType[iField] == 'D' ) { if( psDBF->panFieldDecimals[iField] > 0 ) return( FTDouble ); @@ -840,9 +971,9 @@ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, void * pValue ) { - int nRecordOffset, i, j; - uchar *pabyRec; - char szSField[40], szFormat[12]; + int nRecordOffset, i, j, nRetResult = TRUE; + unsigned char *pabyRec; + char szSField[400], szFormat[20]; /* -------------------------------------------------------------------- */ /* Is this a valid record? */ @@ -883,7 +1014,47 @@ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, psDBF->nCurrentRecord = hEntity; } - pabyRec = (uchar *) psDBF->pszCurrentRecord; + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + +/* -------------------------------------------------------------------- */ +/* Translate NULL value to valid DBF file representation. */ +/* */ +/* Contributed by Jim Matthews. */ +/* -------------------------------------------------------------------- */ + if( pValue == NULL ) + { + switch(psDBF->pachFieldType[iField]) + { + case 'N': + case 'F': + /* NULL numeric fields have value "****************" */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '*', + psDBF->panFieldSize[iField] ); + break; + + case 'D': + /* NULL date fields have value "00000000" */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '0', + psDBF->panFieldSize[iField] ); + break; + + case 'L': + /* NULL boolean fields have value "?" */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '?', + psDBF->panFieldSize[iField] ); + break; + + default: + /* empty string fields are considered NULL */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '\0', + psDBF->panFieldSize[iField] ); + break; + } + return TRUE; + } /* -------------------------------------------------------------------- */ /* Assign all the record fields. */ @@ -895,37 +1066,140 @@ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, case 'F': if( psDBF->panFieldDecimals[iField] == 0 ) { - sprintf( szFormat, "%%%dd", psDBF->panFieldSize[iField] ); + int nWidth = psDBF->panFieldSize[iField]; + + if( sizeof(szSField)-2 < nWidth ) + nWidth = sizeof(szSField)-2; + + sprintf( szFormat, "%%%dd", nWidth ); sprintf(szSField, szFormat, (int) *((double *) pValue) ); - if( strlen(szSField) > psDBF->panFieldSize[iField] ) + if( (int)strlen(szSField) > psDBF->panFieldSize[iField] ) + { szSField[psDBF->panFieldSize[iField]] = '\0'; + nRetResult = FALSE; + } + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), szSField, strlen(szSField) ); } else { + int nWidth = psDBF->panFieldSize[iField]; + + if( sizeof(szSField)-2 < nWidth ) + nWidth = sizeof(szSField)-2; + sprintf( szFormat, "%%%d.%df", - psDBF->panFieldSize[iField], - psDBF->panFieldDecimals[iField] ); + nWidth, psDBF->panFieldDecimals[iField] ); sprintf(szSField, szFormat, *((double *) pValue) ); - if( strlen(szSField) > psDBF->panFieldSize[iField] ) + if( (int) strlen(szSField) > psDBF->panFieldSize[iField] ) + { szSField[psDBF->panFieldSize[iField]] = '\0'; + nRetResult = FALSE; + } strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), szSField, strlen(szSField) ); } break; + case 'L': + if (psDBF->panFieldSize[iField] >= 1 && + (*(char*)pValue == 'F' || *(char*)pValue == 'T')) + *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue; + break; + default: - if( strlen((char *) pValue) > psDBF->panFieldSize[iField] ) + if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] ) + { j = psDBF->panFieldSize[iField]; + nRetResult = FALSE; + } else + { + memset( pabyRec+psDBF->panFieldOffset[iField], ' ', + psDBF->panFieldSize[iField] ); j = strlen((char *) pValue); + } strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), (char *) pValue, j ); break; } + return( nRetResult ); +} + +/************************************************************************/ +/* DBFWriteAttributeDirectly() */ +/* */ +/* Write an attribute record to the file, but without any */ +/* reformatting based on type. The provided buffer is written */ +/* as is to the field position in the record. */ +/************************************************************************/ + +int DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, + void * pValue ) + +{ + int nRecordOffset, i, j; + unsigned char *pabyRec; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + 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 = (unsigned char *) psDBF->pszCurrentRecord; + +/* -------------------------------------------------------------------- */ +/* Assign all the record fields. */ +/* -------------------------------------------------------------------- */ + if( (int)strlen((char *) pValue) > psDBF->panFieldSize[iField] ) + j = psDBF->panFieldSize[iField]; + else + { + memset( pabyRec+psDBF->panFieldOffset[iField], ' ', + psDBF->panFieldSize[iField] ); + j = strlen((char *) pValue); + } + + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + (char *) pValue, j ); + psDBF->bCurrentRecordModified = TRUE; psDBF->bUpdated = TRUE; @@ -938,8 +1212,9 @@ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, /* Write a double attribute. */ /************************************************************************/ -int DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField, - double dValue ) +int SHPAPI_CALL +DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField, + double dValue ) { return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); @@ -951,8 +1226,9 @@ int DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField, /* Write a integer attribute. */ /************************************************************************/ -int DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField, - int nValue ) +int SHPAPI_CALL +DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField, + int nValue ) { double dValue = nValue; @@ -966,10 +1242,254 @@ int DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField, /* Write a string attribute. */ /************************************************************************/ -int DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField, - const char * pszValue ) +int SHPAPI_CALL +DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField, + const char * pszValue ) { return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) ); } +/************************************************************************/ +/* DBFWriteNULLAttribute() */ +/* */ +/* Write a string attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, NULL ) ); +} + +/************************************************************************/ +/* DBFWriteLogicalAttribute() */ +/* */ +/* Write a logical attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteLogicalAttribute( DBFHandle psDBF, int iRecord, int iField, + const char lValue) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) (&lValue) ) ); +} + +/************************************************************************/ +/* DBFWriteTuple() */ +/* */ +/* Write an attribute record to the file. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ) + +{ + int nRecordOffset, i; + unsigned char *pabyRec; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + 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 = (unsigned char *) 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 SHPAPI_CALL1(*) +DBFReadTuple(DBFHandle psDBF, int hEntity ) + +{ + int nRecordOffset; + unsigned char *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 = (unsigned char *) 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 SHPAPI_CALL +DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) +{ + DBFHandle newDBF; + + newDBF = DBFCreate ( pszFilename ); + if ( newDBF == NULL ) return ( NULL ); + + newDBF->pszHeader = (char *) malloc ( 32 * psDBF->nFields ); + memcpy ( newDBF->pszHeader, psDBF->pszHeader, 32 * psDBF->nFields ); + + newDBF->nFields = psDBF->nFields; + newDBF->nRecordLength = psDBF->nRecordLength; + newDBF->nHeaderLength = 32 * (psDBF->nFields+1); + + newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); + newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); + newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); + newDBF->pachFieldType = (char *) malloc ( sizeof(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 ); +} + +/************************************************************************/ +/* DBFGetNativeFieldType() */ +/* */ +/* Return the DBase field type for the specified field. */ +/* */ +/* Value can be one of: 'C' (String), 'D' (Date), 'F' (Float), */ +/* 'N' (Numeric, with or without decimal), */ +/* 'L' (Logical), */ +/* 'M' (Memo: 10 digits .DBT block ptr) */ +/************************************************************************/ + +char SHPAPI_CALL +DBFGetNativeFieldType( DBFHandle psDBF, int iField ) + +{ + if( iField >=0 && iField < psDBF->nFields ) + return psDBF->pachFieldType[iField]; + + return ' '; +} + +/************************************************************************/ +/* str_to_upper() */ +/************************************************************************/ + +static void str_to_upper (char *string) +{ + int len; + short i = -1; + + len = strlen (string); + + while (++i < len) + if (isalpha(string[i]) && islower(string[i])) + string[i] = toupper ((int)string[i]); +} + +/************************************************************************/ +/* DBFGetFieldIndex() */ +/* */ +/* Get the index number for a field in a .dbf file. */ +/* */ +/* Contributed by Jim Matthews. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName) + +{ + char name[12], name1[12], name2[12]; + int i; + + strncpy(name1, pszFieldName,11); + name1[11] = '\0'; + str_to_upper(name1); + + for( i = 0; i < DBFGetFieldCount(psDBF); i++ ) + { + DBFGetFieldInfo( psDBF, i, name, NULL, NULL ); + strncpy(name2,name,11); + str_to_upper(name2); + + if(!strncmp(name1,name2,10)) + return(i); + } + return(-1); +} diff --git a/src/Lib/shapelib/makeshape.sh b/src/Lib/shapelib/makeshape.sh index dbc3ebee..6e5f271e 100755 --- a/src/Lib/shapelib/makeshape.sh +++ b/src/Lib/shapelib/makeshape.sh @@ -14,5 +14,8 @@ dbfadd test.dbf "Square with triangle missing" 1.5 2.5 shpadd test 150 150 160 150 180 170 150 150 dbfadd test.dbf "Smaller triangle" 100 1000.25 +shpadd test 150 150 160 150 180 170 150 150 +dbfadd test.dbf "" "" "" + shpdump test.shp dbfdump test.dbf diff --git a/src/Lib/shapelib/shapefil.h b/src/Lib/shapelib/shapefil.h index 3202aea8..19ba9211 100644 --- a/src/Lib/shapelib/shapefil.h +++ b/src/Lib/shapelib/shapefil.h @@ -1,17 +1,95 @@ #ifndef _SHAPEFILE_H_INCLUDED #define _SHAPEFILE_H_INCLUDED -/* - * Copyright (c) 1995 Frank Warmerdam +/****************************************************************************** + * $Id: shapefil.h,v 1.26 2002/09/29 00:00:08 warmerda Exp $ * - * This code is in the public domain. + * Project: Shapelib + * Purpose: Primary include file for Shapelib. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** * * $Log: shapefil.h,v $ - * Revision 1.1 2000-02-09 19:51:46 curt - * Initial revision + * Revision 1.26 2002/09/29 00:00:08 warmerda + * added FTLogical and logical attribute read/write calls * - * Revision 1.1 1999/08/24 21:13:01 curt - * Initial revision. + * Revision 1.25 2002/05/07 13:46:30 warmerda + * added DBFWriteAttributeDirectly(). + * + * Revision 1.24 2002/04/10 16:59:54 warmerda + * added SHPRewindObject + * + * Revision 1.23 2002/01/15 14:36:07 warmerda + * updated email address + * + * Revision 1.22 2002/01/15 14:32:00 warmerda + * try to improve SHPAPI_CALL docs + * + * Revision 1.21 2001/11/01 16:29:55 warmerda + * move pabyRec into SHPInfo for thread safety + * + * Revision 1.20 2001/07/20 13:06:02 warmerda + * fixed SHPAPI attribute for SHPTreeFindLikelyShapes + * + * Revision 1.19 2001/05/31 19:20:13 warmerda + * added DBFGetFieldIndex() + * + * Revision 1.18 2001/05/31 18:15:40 warmerda + * Added support for NULL fields in DBF files + * + * Revision 1.17 2001/05/23 13:36:52 warmerda + * added use of SHPAPI_CALL + * + * Revision 1.16 2000/09/25 14:15:59 warmerda + * added DBFGetNativeFieldType() + * + * 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. @@ -45,7 +123,7 @@ #ifdef __cplusplus extern "C" { #endif - + /************************************************************************/ /* Configuration options. */ /************************************************************************/ @@ -63,6 +141,48 @@ extern "C" { /* -------------------------------------------------------------------- */ #define DISABLE_MULTIPATCH_MEASURE +/* -------------------------------------------------------------------- */ +/* SHPAPI_CALL */ +/* */ +/* The following two macros are present to allow forcing */ +/* various calling conventions on the Shapelib API. */ +/* */ +/* To force __stdcall conventions (needed to call Shapelib */ +/* from Visual Basic and/or Dephi I believe) the makefile could */ +/* be modified to define: */ +/* */ +/* /DSHPAPI_CALL=__stdcall */ +/* */ +/* If it is desired to force export of the Shapelib API without */ +/* using the shapelib.def file, use the following definition. */ +/* */ +/* /DSHAPELIB_DLLEXPORT */ +/* */ +/* To get both at once it will be necessary to hack this */ +/* include file to define: */ +/* */ +/* #define SHPAPI_CALL __declspec(dllexport) __stdcall */ +/* #define SHPAPI_CALL1 __declspec(dllexport) * __stdcall */ +/* */ +/* The complexity of the situtation is partly caused by the */ +/* peculiar requirement of Visual C++ that __stdcall appear */ +/* after any "*"'s in the return value of a function while the */ +/* __declspec(dllexport) must appear before them. */ +/* -------------------------------------------------------------------- */ + +#ifdef SHAPELIB_DLLEXPORT +# define SHPAPI_CALL __declspec(dllexport) +# define SHPAPI_CALL1(x) __declspec(dllexport) x +#endif + +#ifndef SHPAPI_CALL +# define SHPAPI_CALL +#endif + +#ifndef SHPAPI_CALL1 +# define SHPAPI_CALL1(x) x SHPAPI_CALL +#endif + /************************************************************************/ /* SHP Support. */ /************************************************************************/ @@ -84,6 +204,9 @@ typedef struct double adBoundsMax[4]; int bUpdated; + + unsigned char *pabyRec; + int nBufSize; } SHPInfo; typedef SHPInfo * SHPHandle; @@ -91,6 +214,7 @@ typedef SHPInfo * SHPHandle; /* -------------------------------------------------------------------- */ /* Shape types (nSHPType) */ /* -------------------------------------------------------------------- */ +#define SHPT_NULL 0 #define SHPT_POINT 1 #define SHPT_ARC 3 #define SHPT_POLYGON 5 @@ -152,27 +276,105 @@ typedef struct /* -------------------------------------------------------------------- */ /* 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 ); +SHPHandle SHPAPI_CALL + SHPOpen( const char * pszShapeFile, const char * pszAccess ); +SHPHandle SHPAPI_CALL + SHPCreate( const char * pszShapeFile, int nShapeType ); +void SHPAPI_CALL + 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 ); +SHPObject SHPAPI_CALL1(*) + SHPReadObject( SHPHandle hSHP, int iShape ); +int SHPAPI_CALL + 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 SHPAPI_CALL + SHPDestroyObject( SHPObject * psObject ); +void SHPAPI_CALL + SHPComputeExtents( SHPObject * psObject ); +SHPObject SHPAPI_CALL1(*) + SHPCreateObject( int nSHPType, int nShapeId, + int nParts, int * panPartStart, int * panPartType, + int nVertices, double * padfX, double * padfY, + double * padfZ, double * padfM ); +SHPObject SHPAPI_CALL1(*) + SHPCreateSimpleObject( int nSHPType, int nVertices, + double * padfX, double * padfY, double * padfZ ); -void SHPClose( SHPHandle hSHP ); +int SHPAPI_CALL + SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ); -const char *SHPTypeName( int nSHPType ); -const char *SHPPartTypeName( int nPartType ); +void SHPAPI_CALL + SHPClose( SHPHandle hSHP ); + +const char SHPAPI_CALL1(*) + SHPTypeName( int nSHPType ); +const char SHPAPI_CALL1(*) + SHPPartTypeName( int nPartType ); + +/* -------------------------------------------------------------------- */ +/* Shape quadtree indexing API. */ +/* -------------------------------------------------------------------- */ + +/* this can be two or four for binary or quad tree */ +#define MAX_SUBNODE 4 + +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 SHPAPI_CALL1(*) + SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, + double *padfBoundsMin, double *padfBoundsMax ); +void SHPAPI_CALL + SHPDestroyTree( SHPTree * hTree ); + +int SHPAPI_CALL + SHPWriteTree( SHPTree *hTree, const char * pszFilename ); +SHPTree SHPAPI_CALL + SHPReadTree( const char * pszFilename ); + +int SHPAPI_CALL + SHPTreeAddObject( SHPTree * hTree, SHPObject * psObject ); +int SHPAPI_CALL + SHPTreeAddShapeId( SHPTree * hTree, SHPObject * psObject ); +int SHPAPI_CALL + SHPTreeRemoveShapeId( SHPTree * hTree, int nShapeId ); + +void SHPAPI_CALL + SHPTreeTrimExtraNodes( SHPTree * hTree ); + +int SHPAPI_CALL1(*) + SHPTreeFindLikelyShapes( SHPTree * hTree, + double * padfBoundsMin, + double * padfBoundsMax, + int * ); +int SHPAPI_CALL + SHPCheckBoundsOverlap( double *, double *, double *, double *, int ); /************************************************************************/ /* DBF Support. */ @@ -207,33 +409,73 @@ typedef enum { FTString, FTInteger, FTDouble, + FTLogical, FTInvalid } DBFFieldType; -DBFHandle DBFOpen( const char * pszDBFFile, const char * pszAccess ); -DBFHandle DBFCreate( const char * pszDBFFile ); +#define XBASE_FLDHDR_SZ 32 -int DBFGetFieldCount( DBFHandle psDBF ); -int DBFGetRecordCount( DBFHandle psDBF ); -int DBFAddField( DBFHandle hDBF, const char * pszFieldName, - DBFFieldType eType, int nWidth, int nDecimals ); +DBFHandle SHPAPI_CALL + DBFOpen( const char * pszDBFFile, const char * pszAccess ); +DBFHandle SHPAPI_CALL + DBFCreate( const char * pszDBFFile ); -DBFFieldType DBFGetFieldInfo( DBFHandle psDBF, int iField, - char * pszFieldName, - int * pnWidth, int * pnDecimals ); +int SHPAPI_CALL + DBFGetFieldCount( DBFHandle psDBF ); +int SHPAPI_CALL + DBFGetRecordCount( DBFHandle psDBF ); +int SHPAPI_CALL + DBFAddField( DBFHandle hDBF, const char * pszFieldName, + DBFFieldType eType, int nWidth, int nDecimals ); -int DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField ); -double DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField ); -const char *DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField ); +DBFFieldType SHPAPI_CALL + DBFGetFieldInfo( DBFHandle psDBF, int iField, + char * pszFieldName, int * pnWidth, int * pnDecimals ); -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 ); +int SHPAPI_CALL + DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName); -void DBFClose( DBFHandle hDBF ); +int SHPAPI_CALL + DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField ); +double SHPAPI_CALL + DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField ); +const char SHPAPI_CALL1(*) + DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField ); +const char SHPAPI_CALL1(*) + DBFReadLogicalAttribute( DBFHandle hDBF, int iShape, int iField ); +int SHPAPI_CALL + DBFIsAttributeNULL( DBFHandle hDBF, int iShape, int iField ); + +int SHPAPI_CALL + DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, + int nFieldValue ); +int SHPAPI_CALL + DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField, + double dFieldValue ); +int SHPAPI_CALL + DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField, + const char * pszFieldValue ); +int SHPAPI_CALL + DBFWriteNULLAttribute( DBFHandle hDBF, int iShape, int iField ); + +int SHPAPI_CALL + DBFWriteLogicalAttribute( DBFHandle hDBF, int iShape, int iField, + const char lFieldValue); +int SHPAPI_CALL + DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, + void * pValue ); +const char SHPAPI_CALL1(*) + DBFReadTuple(DBFHandle psDBF, int hEntity ); +int SHPAPI_CALL + DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ); + +DBFHandle SHPAPI_CALL + DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ); + +void SHPAPI_CALL + DBFClose( DBFHandle hDBF ); +char SHPAPI_CALL + DBFGetNativeFieldType( DBFHandle hDBF, int iField ); #ifdef __cplusplus } diff --git a/src/Lib/shapelib/shapelib.html b/src/Lib/shapelib/shapelib.html index f8bc67d2..4372d1d6 100644 --- a/src/Lib/shapelib/shapelib.html +++ b/src/Lib/shapelib/shapelib.html @@ -12,34 +12,6 @@ The Shapefile C Library provides the ability to write simple C programs for reading, writing and updating (to a limited extent) ESRI Shapefiles, and the associated attribute file (.dbf).

- -

Copyright

- -The source for the Shapefile C Library is (c) 1998 Frank Warmerdam, -and released under the following conditions. The intent is that anyone -can do anything with the code, but that I do not assume any liability, nor -express any warranty for this code.

- - -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.

- -

Manifest

    @@ -59,6 +31,9 @@ for the API for accessing the .dbf attribute files.

  • shapefil.h: Include file defining all the services of dbfopen.c and shpopen.c.

    +

  • contrib/: A directory of "in progress" contributed programs +from Carl Anderson.

    +

  • dbfcreate.c: Simple example program for creating a new .dbf file.

    @@ -91,6 +66,13 @@ programs.

  • shptest.c: A simple test harnass to generate each of the supported types of shapefiles.

    + +

  • shptree.c: Implements a simple quadtree algorithm for fast +spatial searches of shapefiles.

    + +

  • shptreedump.c: A simple mainly showing information on quad +trees build using the quad tree api.

    +

  • stream1.sh - A test script, which should produce stream1.out. Note this will only work if you have the example data downloaded.

    @@ -99,6 +81,9 @@ Note this will only work if you have the example data downloaded.

  • stream2.sh: A test script, which should produce stream2.out.

  • stream2.out: Expected output of stream2.sh test script.

    + +

  • pyshapelib-0.1: Prototype contributed Python bindings.

    +

What is a Shapefile?

@@ -122,8 +107,38 @@ XXX.dbf - holds the attributes in xBase (dBase) format.

Release Notes

+To get notification of new releases of Shapelib subscribe to +the project at www.freshmeat.net. This is currently the only reliable +way of finding out about new releases since there is no shapelib specific +mailing list.

+ +Release 1.2.10: Added SHPRewindObject() function, and shprewind utility +program. Added FTLogical, DBFReadLogicalAttribute() and +DBFWriteLogicalAttribute() (thanks to Olek Neyman).

+ +Release 1.2.9: Good support for reading and writing NULL fields +in .dbf files, good support for NULL shapes and addition of the +DBFGetFieldIndex() functions (all contributed by Jim Matthews).

+ +An upgraded shputils.c has been contributed by Bill Miller. Daniel +Morissette contributed DBFGetNativeFieldType(). Better error checking +for disk errors in dbfopen.c. Various other bug fixes and safety improvements. +

+ +Release 1.2.8: Added hacked libtool support (supplied by Jan) +and "rpm ready" install logic.

+ +Release 1.2.7: Fix record size (was 4 bytes too long). Modify +SHPReadObject() to handle null shapes properly. Use atof() instead of +sscanf(). Support .DBF as well as .dbf.

+ +Release 1.2.6: Now available under old MIT style license, or at the +users option, LGPL. Added the contrib directory of stuff from Carl Anderson +and the shptree.c API for quadtree based spatial searches.

+ Release 1.2.5: SHPOpen() now forcably uses "rb" or "r+b" access string -to avoid common mistakes on Windows.

+to avoid common mistakes on Windows. Also fixed a serious bug with .dbf +files with a 'F' field type.

Release 1.2.4: DBFOpen() will now automatically translate a .shp extension to .dbf for convenience. SHPOpen() will try datasets with lower @@ -153,16 +168,51 @@ documentation.

This library is maintained by me (Frank Warmerdam) on my own time. Please send me bug patches and suggestions for the library. Email can be sent to -warmerda@home.com.

+warmerdam@pobox.com.

-The current status of the Shapelib code can be found somewhere off my -home page at http://members.home.com/warmerda.

+The current status of the Shapelib code can be found at + +http://pobox.com/~warmerdam/root/projects/shapelib/. To find out about +new releases of Shapelib, select the "Subscribe to new releases" option +from the link at +Freshmeat.

The shputils.c module was contributed by Bill Miller (NC-DOT) who can be reached at bmiller@doh.dot.state.nc.us. I had to modify it substantially to work with the 1.2 API, and I am not sure that it works as well as it did when it was originally provided by Bill.

+

Credits

+ +I didn't start this section anywhere near soon enough, so alot of earlier +contributors to Shapelib are lost in pre-history. + +
    +
  • Bill Miller (NY-DOT) for shputils.c +
  • Carl Anderson for the contents of the contrib directory, and +the "tuple" additions to dbfopen.c. +
  • Andrea Giacomelli for patches for dbfopen.c. +
  • Doug Matthews for portability improvements. +
  • Jan-Oliver Wagner for convincing me to make it available under LGPL, +shared library support, and various other patches. +
  • Dennis Christopher (of Avenza) for testing and bug fixes. +
  • Miko Syrjä (of 3D-system Oy) for a record size bug fix. +
  • Steven Lime and Curtis Hill for help with NULL shapes. +
  • Jim Matthews for support of NULL attributes in dbf files. +
  • PCI Geomatics who let me +release a modified version of their shapefile code in the beginning and +who hosted shapelib for years. +
+ +

In Memorium

+ +I would like to dedicate Shapelib to the memory of Sol Katz. While I never +met him in person, his generous contributions to the GIS community took +many forms, including free distribution of a variety of GIS translators +with source. The fact that he used this Shapelib in some of his utilities, +and thanked me was a great encouragement to me. I hope I can do his memory +honour by trying to contribute in a similar fashion.

+

Portability

The Shapefile C Library should port easily to 32bit systems with ANSI C @@ -175,6 +225,10 @@ such as MSDOS.

The shputils.c module is contributed, and may not take the same approach to portability as the rest of the package.

+On Linux, and most unix systems it should be possible to build and +install shapefile support as a shared library using the "lib" and "lib_install" +targets of the Makefile. Note that this Makefile doesn't use autoconf +mechanisms and will generally require some hand tailoring for your environment.

Limitations

@@ -203,8 +257,76 @@ not used by ESRI.

  • The application must keep the .dbf file in sync with the .shp/.shx files through appropriate use of the DBF and SHP APIs.

    +

  • No support for the undocumented .sbn/.sbx spatial index files.

    + +

    Copyright

    + +The source for the Shapefile C Library is (c) 1998 Frank Warmerdam, +and released under the following conditions. The intent is that anyone +can do anything with the code, but that I do not assume any liability, nor +express any warranty for this code.

    + +As of Shapelib 1.2.6 the core portions of the library are made available +under two possible licenses. The licensee can choose to use the code +under either the Library GNU Public License (LGPL) described in +LICENSE.LGPL or under the following MIT style license. Any files in +the Shapelib distribution without explicit copyright license terms +(such as this documentation, the Makefile and so forth) should be +considered to have the following licensing terms. Some auxilary portions +of Shapelib, notably some of the components in the contrib directory +come under slightly different license restrictions. Check the source +files that you are actually using for conditions.

    + +

    Default License Terms

    + + +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.

    + + +

    Shapelib Modifications

    + +I am pleased to receive bug fixes, and improvements for Shapelib. Unless +the submissions indicate otherwise I will assume that changes submitted to +me remain under the the above "dual license" terms. If changes are made +to the library with the intention that those changes should be protected by +the LGPL then I should be informed upon submission. Note that I will not +generally incorporate changes into the core of Shapelib that are protected +under the LGPL as this would effectively limit the whole file and +distribution to LGPL terms.

    + +

    Opting for LGPL

    + +For licensee's opting to use Shapelib under LGPL as opposed to the MIT +Style license above, and wishing to redistribute the software based on +Shapelib, I would ask that all "dual license" modules be updated to +indicate that only the LGPL (and not the MIT Style license) applies. This +action represents opting for the LGPL, and thereafter LGPL terms apply to +any redistribution and modification of the affected modules.

    + diff --git a/src/Lib/shapelib/shp_api.html b/src/Lib/shapelib/shp_api.html index f2002520..d773e3e5 100644 --- a/src/Lib/shapelib/shp_api.html +++ b/src/Lib/shapelib/shp_api.html @@ -17,9 +17,11 @@ accessed by the API functions.

    Shapes have types associated with them. The following is a list of the different shapetypes supported by Shapefiles. At this time all shapes in -a Shapefile must be of the same type.

    +a Shapefile must be of the same type (with the exception of NULL shapes).

    +  #define SHPT_NULL             0
    +
       2D Shape Types (pre ArcView 3.x):
     
       #define SHPT_POINT		1	Points
    @@ -161,6 +163,11 @@ SHPObject *SHPReadObject( SHPHandle hSHP, int iShape );
       file, and may not be correct.   For points the bounds are generated from
       the single point since bounds aren't normally provided for point types.

    + Generally the shapes returned will be of the type of the file as a whole. + However, any file may also contain type SHPT_NULL shapes which will have + no geometry. Generally speaking applications should skip rather than + preserve them, as they usually represented interactively deleted shapes.

    +

    SHPClose()

    @@ -347,5 +354,23 @@ void SHPDestroyObject( SHPObject *psObject ); SHPCreateSimpleObject(), SHPCreateObject() and returned from SHPReadObject().

    + + +

    SHPRewindObject()

    + +
    +int SHPRewindObject( SHPHandle hSHP, SHPObject *psObject );
    +
    +  hSHP:                 The shapefile (not used at this time).
    +  psObject:		The object to deallocate.
    +
    + + This function will reverse any rings necessary in order to enforce the + shapefile restrictions on the required order of inner and outer rings in + the Shapefile specification. It returns TRUE if a change is made and FALSE + if no change is made. Only polygon objects will be affected though any + object may be passed. +

    + diff --git a/src/Lib/shapelib/shpadd.c b/src/Lib/shapelib/shpadd.c index a6f6df3c..68b0c9d3 100644 --- a/src/Lib/shapelib/shpadd.c +++ b/src/Lib/shapelib/shpadd.c @@ -1,14 +1,53 @@ -/* - * Copyright (c) 1995 Frank Warmerdam +/****************************************************************************** + * $Id: shpadd.c,v 1.13 2002/01/15 14:36:07 warmerda Exp $ * - * This code is in the public domain. + * Project: Shapelib + * Purpose: Sample application for adding a shape to a shapefile. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** * * $Log: shpadd.c,v $ - * Revision 1.1 2000-02-09 19:51:46 curt - * Initial revision + * Revision 1.13 2002/01/15 14:36:07 warmerda + * updated email address * - * Revision 1.1 1999/08/24 21:13:01 curt - * Initial revision. + * Revision 1.12 2001/05/31 19:35:29 warmerda + * added support for writing null shapes + * + * Revision 1.11 2000/07/07 13:39:45 warmerda + * removed unused variables, and added system include files + * + * Revision 1.10 2000/05/24 15:09:22 warmerda + * Added logic to graw vertex lists of needed. + * + * Revision 1.9 1999/11/05 14:12:04 warmerda + * updated license terms * * Revision 1.8 1998/12/03 16:36:26 warmerda * Use r+b rather than rb+ for binary access. @@ -34,22 +73,24 @@ */ static char rcsid[] = - "$Id: shpadd.c,v 1.1 2000-02-09 19:51:46 curt Exp $"; + "$Id: shpadd.c,v 1.13 2002/01/15 14:36:07 warmerda Exp $"; +#include +#include #include "shapefil.h" int main( int argc, char ** argv ) { SHPHandle hSHP; - int nShapeType, nVertices, nParts, *panParts, i; + int nShapeType, nVertices, nParts, *panParts, i, nVMax; double *padfX, *padfY; SHPObject *psObject; /* -------------------------------------------------------------------- */ /* Display a usage message. */ /* -------------------------------------------------------------------- */ - if( argc < 4 ) + if( argc < 2 ) { printf( "shpadd shp_file [[x y] [+]]*\n" ); exit( 1 ); @@ -68,11 +109,15 @@ int main( int argc, char ** argv ) SHPGetInfo( hSHP, NULL, &nShapeType, NULL, NULL ); + if( argc == 2 ) + nShapeType = SHPT_NULL; + /* -------------------------------------------------------------------- */ /* Build a vertex/part list from the command line arguments. */ /* -------------------------------------------------------------------- */ - padfX = (double *) malloc(sizeof(double) * 1000); - padfY = (double *) malloc(sizeof(double) * 1000); + nVMax = 1000; + padfX = (double *) malloc(sizeof(double) * nVMax); + padfY = (double *) malloc(sizeof(double) * nVMax); nVertices = 0; @@ -94,6 +139,13 @@ int main( int argc, char ** argv ) } else if( i < argc-1 ) { + if( nVertices == nVMax ) + { + nVMax = nVMax * 2; + padfX = (double *) realloc(padfX,sizeof(double)*nVMax); + padfY = (double *) realloc(padfY,sizeof(double)*nVMax); + } + sscanf( argv[i], "%lg", padfX+nVertices ); sscanf( argv[i+1], "%lg", padfY+nVertices ); nVertices += 1; @@ -114,4 +166,6 @@ int main( int argc, char ** argv ) free( panParts ); free( padfX ); free( padfY ); + + return 0; } diff --git a/src/Lib/shapelib/shpcreate.c b/src/Lib/shapelib/shpcreate.c index 6669ebab..c33dd256 100644 --- a/src/Lib/shapelib/shpcreate.c +++ b/src/Lib/shapelib/shpcreate.c @@ -1,22 +1,55 @@ -/* - * Copyright (c) 1995 Frank Warmerdam +/****************************************************************************** + * $Id: shpcreate.c,v 1.5 2002/01/15 14:36:07 warmerda Exp $ * - * This code is in the public domain. + * Project: Shapelib + * Purpose: Sample application for creating a new shapefile. + * Author: Frank Warmerdam, warmerdm@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** * * $Log: shpcreate.c,v $ - * Revision 1.1 2000-02-09 19:51:46 curt - * Initial revision + * Revision 1.5 2002/01/15 14:36:07 warmerda + * updated email address * - * Revision 1.1 1999/08/24 21:13:01 curt - * Initial revision. + * Revision 1.4 2000/07/07 13:39:45 warmerda + * removed unused variables, and added system include files * - * Revision 1.2 1995/08/04 03:16:43 warmerda + * Revision 1.3 1999/11/05 14:12:04 warmerda + * updated license terms + * + * Revision 1.2 1995/08/04 03:16:43 warmerda * Added header. * */ static char rcsid[] = - "$Id: shpcreate.c,v 1.1 2000-02-09 19:51:46 curt Exp $"; + "$Id: shpcreate.c,v 1.5 2002/01/15 14:36:07 warmerda Exp $"; #include "shapefil.h" @@ -25,7 +58,6 @@ int main( int argc, char ** argv ) { SHPHandle hSHP; int nShapeType; - double *padVertices; /* -------------------------------------------------------------------- */ /* Display a usage message. */ @@ -65,4 +97,6 @@ int main( int argc, char ** argv ) } SHPClose( hSHP ); + + return 0; } diff --git a/src/Lib/shapelib/shpdump.c b/src/Lib/shapelib/shpdump.c index f10fc5a2..5dc1cfa8 100644 --- a/src/Lib/shapelib/shpdump.c +++ b/src/Lib/shapelib/shpdump.c @@ -1,14 +1,51 @@ -/* - * Copyright (c) 1995 Frank Warmerdam +/****************************************************************************** + * $Id: shpdump.c,v 1.10 2002/04/10 16:59:29 warmerda Exp $ * - * This code is in the public domain. + * Project: Shapelib + * Purpose: Sample application for dumping contents of a shapefile to + * the terminal in human readable form. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** * * $Log: shpdump.c,v $ - * Revision 1.1 2000-02-09 19:51:46 curt - * Initial revision + * Revision 1.10 2002/04/10 16:59:29 warmerda + * added -validate switch * - * Revision 1.1 1999/08/24 21:13:01 curt - * Initial revision. + * Revision 1.9 2002/01/15 14:36:07 warmerda + * updated email address + * + * Revision 1.8 2000/07/07 13:39:45 warmerda + * removed unused variables, and added system include files + * + * Revision 1.7 1999/11/05 14:12:04 warmerda + * updated license terms * * Revision 1.6 1998/12/03 15:48:48 warmerda * Added report of shapefile type, and total number of shapes. @@ -28,7 +65,7 @@ */ static char rcsid[] = - "$Id: shpdump.c,v 1.1 2000-02-09 19:51:46 curt Exp $"; + "$Id: shpdump.c,v 1.10 2002/04/10 16:59:29 warmerda Exp $"; #include "shapefil.h" @@ -36,16 +73,23 @@ int main( int argc, char ** argv ) { SHPHandle hSHP; - int nShapeType, nEntities, i, iPart; + int nShapeType, nEntities, i, iPart, bValidate = 0,nInvalidCount=0; const char *pszPlus; double adfMinBound[4], adfMaxBound[4]; + if( argc > 1 && strcmp(argv[1],"-validate") == 0 ) + { + bValidate = 1; + argv++; + argc--; + } + /* -------------------------------------------------------------------- */ /* Display a usage message. */ /* -------------------------------------------------------------------- */ if( argc != 2 ) { - printf( "shpdump shp_file\n" ); + printf( "shpdump [-validate] shp_file\n" ); exit( 1 ); } @@ -68,8 +112,8 @@ int main( int argc, char ** argv ) printf( "Shapefile Type: %s # of Shapes: %d\n\n", SHPTypeName( nShapeType ), nEntities ); - printf( "File Bounds: (%12.3f,%12.3f,%lg,%lg)\n" - " to (%12.3f,%12.3f,%lg,%lg)\n", + printf( "File Bounds: (%12.3f,%12.3f,%g,%g)\n" + " to (%12.3f,%12.3f,%g,%g)\n", adfMinBound[0], adfMinBound[1], adfMinBound[2], @@ -90,8 +134,8 @@ int main( int argc, char ** argv ) psShape = SHPReadObject( hSHP, i ); printf( "\nShape:%d (%s) nVertices=%d, nParts=%d\n" - " Bounds:(%12.3f,%12.3f, %lg, %lg)\n" - " to (%12.3f,%12.3f, %lg, %lg)\n", + " Bounds:(%12.3f,%12.3f, %g, %g)\n" + " to (%12.3f,%12.3f, %g, %g)\n", i, SHPTypeName(psShape->nSHPType), psShape->nVertices, psShape->nParts, psShape->dfXMin, psShape->dfYMin, @@ -116,7 +160,7 @@ int main( int argc, char ** argv ) else pszPlus = " "; - printf(" %s (%12.3f,%12.3f, %lg, %lg) %s \n", + printf(" %s (%12.3f,%12.3f, %g, %g) %s \n", pszPlus, psShape->padfX[j], psShape->padfY[j], @@ -124,12 +168,29 @@ int main( int argc, char ** argv ) psShape->padfM[j], pszPartType ); } + + if( bValidate ) + { + int nAltered = SHPRewindObject( hSHP, psShape ); + + if( nAltered > 0 ) + { + printf( " %d rings wound in the wrong direction.\n", + nAltered ); + nInvalidCount++; + } + } SHPDestroyObject( psShape ); } SHPClose( hSHP ); + if( bValidate ) + { + printf( "%d object has invalid ring orderings.\n", nInvalidCount ); + } + #ifdef USE_DBMALLOC malloc_dump(2); #endif diff --git a/src/Lib/shapelib/shpopen.c b/src/Lib/shapelib/shpopen.c index 482803a9..6e1d626c 100644 --- a/src/Lib/shapelib/shpopen.c +++ b/src/Lib/shapelib/shpopen.c @@ -1,6 +1,19 @@ /****************************************************************************** - * Copyright (c) 1998, Frank Warmerdam + * $Id: shpopen.c,v 1.39 2002/08/26 06:46:56 warmerda Exp $ * + * Project: Shapelib + * Purpose: Implementation of core Shapefile read/write functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation @@ -21,134 +34,63 @@ ****************************************************************************** * * $Log: shpopen.c,v $ - * Revision 1.2 2000-11-25 19:39:49 curt - * Contributed by Bruce Finney: + * Revision 1.39 2002/08/26 06:46:56 warmerda + * avoid c++ comments * - * The following files have been changed to enable the latest Terragear CVS - * to compile with MSVC++ 5.0 + * Revision 1.38 2002/05/07 16:43:39 warmerda + * Removed debugging printf. * - * .\construct\clipper\clipper.cxx - * for( int i - lots of places + * Revision 1.37 2002/04/10 17:35:22 warmerda + * fixed bug in ring reversal code * - * .\construct\genoutput\genobj.cxx - * fix directory logic for windows, line 320 and following + * Revision 1.36 2002/04/10 16:59:54 warmerda + * added SHPRewindObject * - * .\construct\main\main.cxx - * windows does not have an opendir function - * added code for windows directory functions - * disabled the mem allocation limit code - windows does not - * have similar functions - * for ( int i - several places + * Revision 1.35 2001/12/07 15:10:44 warmerda + * fix if .shx fails to open * - * .\construct\match\match.cxx - * moved the definition of file and command outside of the ifdef line 420 + * Revision 1.34 2001/11/01 16:29:55 warmerda + * move pabyRec into SHPInfo for thread safety * - * .\lib\e00\e00.cxx - * for( int i - several places + * Revision 1.33 2001/07/03 12:18:15 warmerda + * Improved cleanup if SHX not found, provied by Riccardo Cohen. * - * .\lib\e00\e00.cxx - * use simgear/compiler.h constructs + * Revision 1.32 2001/06/22 01:58:07 warmerda + * be more careful about establishing initial bounds in face of NULL shapes * - * .\lib\geometry\contour_tree.hxx - * removed a cout statement + * Revision 1.31 2001/05/31 19:35:29 warmerda + * added support for writing null shapes * - * .\lib\geometry\poly_support.cxx - * added float.h changed 1.0e+999 to DBL_MAX, windows doesn't go that big - * lots of for ( int i changes - * lines 193 and 208 no != operator defined - changed logic - * line 801 flag should be int, not bool, get_hole_flag returns int + * Revision 1.30 2001/05/28 12:46:29 warmerda + * Add some checking on reasonableness of record count when opening. * - * .\lib\landcover\landcover.cxx - * .\lib\landcover\landcover.hxx - * add include simgear/compiler.h - * see comments + * Revision 1.29 2001/05/23 13:36:52 warmerda + * added use of SHPAPI_CALL * - * .\lib\optimize\genfans.cxx - * function canonify added return at end, windows complains - * added using std for cout and endl + * Revision 1.28 2001/02/06 22:25:06 warmerda + * fixed memory leaks when SHPOpen() fails * - * .\lib\optimize\genstrips.cxx - * function tgGenStrips no return value, moved by_node into outer scope - * fix for ( int i ... + * Revision 1.27 2000/07/18 15:21:33 warmerda + * added better enforcement of -1 for append in SHPWriteObject * - * .\lib\poly2tri\construct.c - * added include for windows - * remove unused variables lines 435 & 437 + * Revision 1.26 2000/02/16 16:03:51 warmerda + * added null shape support * - * .\lib\poly2tri\misc.c - * added HAVE_SYS_TIME_H logic for sys/time.h include file - * added logic to uses windows functions for time and rand + * 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. * - * .\lib\poly2tri\monotone.c - * added include for windows - * lines 286-288 remove unused variables + * Revision 1.24 1999/11/05 14:12:04 warmerda + * updated license terms * - * .\lib\poly2tri\tri.c - * remove sys/time.h - no time functions called - * added include for windows + * Revision 1.23 1999/07/27 00:53:46 warmerda + * added support for rewriting shapes * - * .\lib\polygon\polygon.cxx - * function polygon_to_tristrip will not compile I don't think the - * logic is complete, no returned data added if else endif around - * function and polygon_to_tristrip_old, renamed _old function. - * Search of code reveals that function is not called by anyone. + * Revision 1.22 1999/06/11 19:19:11 warmerda + * Cleanup pabyRec static buffer on SHPClose(). * - * .\lib\polygon\superpoly.cxx - * changed include to "superpoly.hxx" - * - * .\lib\polygon\superpoly.hxx - * add include for windows before include - * needed for definitions used in Microsoft version of opengl - * - * .\lib\shapelib\dbfopen.c - * added include files for windows - * lines 195-197 271-272 515-517 removed unused variables - * - * .\lib\shapelib\shpopen.c - * added #include for malloc() and friends - * added include files for windows - * line 279 527 813 1127 removed unused variables - * line 827 cast result to int - * - * .\lib\win32\mkdir.cpp - * documented function, remove debug lines - * - * .\prep\demraw2ascii\main.c - * lines 46-50 remove unused variables - * - * .\prep\demraw2ascii\rawdem.c - * line 47 changed logic to compile with MSVC - * line 244-256 set real constants to float, windows complains with - * double constants - * - * .\prep\genairports\build.cxx - * lots of for ( int i changes - * - * .\prep\genairports\main.cxx - * fix mkdir logic for windows - * - * .\prep\genairports\output.cxx - * added using std cout endl - * lots of for ( int i changes - * fix mkdir logic for windows - * - * .\prep\genairports\runway.cxx - * for ( int i changes - * lines 117-118 161-162 remove default values for function parameters - * - * .\prep\gshhs\main.cxx - * added using std cout - * - * .\prep\shapefile\noaa_decode.cxx - * .\prep\shapefile\shape_decode.cxx - * added using std for cout - * lines 45-49 moved unused variables inside #if 0 block - * - * Revision 1.1.1.1 2000/02/09 19:51:46 curt - * Initial revision - * - * Revision 1.1 1999/08/24 21:13:01 curt - * Initial revision. + * 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. @@ -217,19 +159,15 @@ */ static char rcsid[] = - "$Id: shpopen.c,v 1.2 2000-11-25 19:39:49 curt Exp $"; + "$Id: shpopen.c,v 1.39 2002/08/26 06:46:56 warmerda Exp $"; #include "shapefil.h" #include #include #include -#include /* malloc() && friends */ -#ifdef _MSC_VER -# include -# include -# include -#endif +#include +#include typedef unsigned char uchar; @@ -252,6 +190,7 @@ typedef int int32; static int bBigEndian; + /************************************************************************/ /* SwapWord() */ /* */ @@ -398,7 +337,8 @@ static void SHPWriteHeader( SHPHandle psSHP ) /* files or either file name. */ /************************************************************************/ -SHPHandle SHPOpen( const char * pszLayer, const char * pszAccess ) +SHPHandle SHPAPI_CALL +SHPOpen( const char * pszLayer, const char * pszAccess ) { char *pszFullname, *pszBasename; @@ -413,7 +353,8 @@ SHPHandle SHPOpen( const char * pszLayer, const char * pszAccess ) /* ensure the result string indicates binary to avoid common */ /* problems on Windows. */ /* -------------------------------------------------------------------- */ - if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 ) + if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 + || strcmp(pszAccess,"r+") == 0 ) pszAccess = "r+b"; else pszAccess = "rb"; @@ -430,7 +371,7 @@ SHPHandle SHPOpen( const char * pszLayer, const char * pszAccess ) /* -------------------------------------------------------------------- */ /* Initialize the info structure. */ /* -------------------------------------------------------------------- */ - psSHP = (SHPHandle) malloc(sizeof(SHPInfo)); + psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1); psSHP->bUpdated = FALSE; @@ -462,7 +403,12 @@ SHPHandle SHPOpen( const char * pszLayer, const char * pszAccess ) } if( psSHP->fpSHP == NULL ) + { + free( psSHP ); + free( pszBasename ); + free( pszFullname ); return( NULL ); + } sprintf( pszFullname, "%s.shx", pszBasename ); psSHP->fpSHX = fopen(pszFullname, pszAccess ); @@ -473,7 +419,13 @@ SHPHandle SHPOpen( const char * pszLayer, const char * pszAccess ) } if( psSHP->fpSHX == NULL ) + { + fclose( psSHP->fpSHP ); + free( psSHP ); + free( pszBasename ); + free( pszFullname ); return( NULL ); + } free( pszFullname ); free( pszBasename ); @@ -512,6 +464,16 @@ SHPHandle SHPOpen( const char * pszLayer, const char * pszAccess ) psSHP->nShapeType = pabyBuf[32]; + if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 ) + { + /* this header appears to be corrupt. Give up. */ + fclose( psSHP->fpSHP ); + fclose( psSHP->fpSHX ); + free( psSHP ); + + return( NULL ); + } + /* -------------------------------------------------------------------- */ /* Read the bounds. */ /* -------------------------------------------------------------------- */ @@ -587,7 +549,8 @@ SHPHandle SHPOpen( const char * pszLayer, const char * pszAccess ) /* Close the .shp and .shx files. */ /************************************************************************/ -void SHPClose(SHPHandle psSHP ) +void SHPAPI_CALL +SHPClose(SHPHandle psSHP ) { /* -------------------------------------------------------------------- */ @@ -607,6 +570,11 @@ void SHPClose(SHPHandle psSHP ) fclose( psSHP->fpSHX ); fclose( psSHP->fpSHP ); + if( psSHP->pabyRec != NULL ) + { + free( psSHP->pabyRec ); + } + free( psSHP ); } @@ -616,8 +584,9 @@ void SHPClose(SHPHandle psSHP ) /* Fetch general information about the shape file. */ /************************************************************************/ -void SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, - double * padfMinBound, double * padfMaxBound ) +void SHPAPI_CALL +SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, + double * padfMinBound, double * padfMaxBound ) { int i; @@ -644,7 +613,8 @@ void SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, /* shape file with read/write access. */ /************************************************************************/ -SHPHandle SHPCreate( const char * pszLayer, int nShapeType ) +SHPHandle SHPAPI_CALL +SHPCreate( const char * pszLayer, int nShapeType ) { char *pszBasename, *pszFullname; @@ -653,7 +623,7 @@ SHPHandle SHPCreate( const char * pszLayer, int nShapeType ) uchar abyHeader[100]; int32 i32; double dValue; - + /* -------------------------------------------------------------------- */ /* Establish the byte order on this system. */ /* -------------------------------------------------------------------- */ @@ -775,7 +745,8 @@ static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape ) /* SHPCreateObject(). */ /************************************************************************/ -void SHPComputeExtents( SHPObject * psObject ) +void SHPAPI_CALL +SHPComputeExtents( SHPObject * psObject ) { int i; @@ -812,10 +783,11 @@ void SHPComputeExtents( SHPObject * psObject ) /* SHPDestroyObject(). */ /************************************************************************/ -SHPObject *SHPCreateObject( int nSHPType, int nShapeId, int nParts, - int * panPartStart, int * panPartType, - int nVertices, double * padfX, double * padfY, - double * padfZ, double * padfM ) +SHPObject SHPAPI_CALL1(*) +SHPCreateObject( int nSHPType, int nShapeId, int nParts, + int * panPartStart, int * panPartType, + int nVertices, double * padfX, double * padfY, + double * padfZ, double * padfM ) { SHPObject *psObject; @@ -884,29 +856,31 @@ SHPObject *SHPCreateObject( int nSHPType, int nShapeId, int nParts, /* 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++ ) + if( nVertices > 0 ) { - 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]; + 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 ); @@ -919,9 +893,10 @@ SHPObject *SHPCreateObject( int nSHPType, int nShapeId, int nParts, /* SHPDestroyObject(). */ /************************************************************************/ -SHPObject *SHPCreateSimpleObject( int nSHPType, int nVertices, - double * padfX, double * padfY, - double * padfZ ) +SHPObject SHPAPI_CALL1(*) +SHPCreateSimpleObject( int nSHPType, int nVertices, + double * padfX, double * padfY, + double * padfZ ) { return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL, @@ -935,49 +910,63 @@ SHPObject *SHPCreateSimpleObject( int nSHPType, int nVertices, /* only possible to write vertices at the end of the file. */ /************************************************************************/ -int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) +int SHPAPI_CALL +SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) { int nRecordOffset, i, nRecordSize; uchar *pabyRec; int32 i32; - assert( nShapeId == -1 ); - psSHP->bUpdated = TRUE; +/* -------------------------------------------------------------------- */ +/* Ensure that shape object matches the type of the file it is */ +/* being written to. */ +/* -------------------------------------------------------------------- */ + assert( psObject->nSHPType == psSHP->nShapeType + || psObject->nSHPType == SHPT_NULL ); + +/* -------------------------------------------------------------------- */ +/* Ensure that -1 is used for appends. Either blow an */ +/* assertion, or if they are disabled, set the shapeid to -1 */ +/* for appends. */ +/* -------------------------------------------------------------------- */ + assert( nShapeId == -1 + || (nShapeId >= 0 && nShapeId < psSHP->nRecords) ); + + if( nShapeId != -1 && nShapeId >= psSHP->nRecords ) + nShapeId = -1; + /* -------------------------------------------------------------------- */ /* Add the new entity to the in memory index. */ /* -------------------------------------------------------------------- */ - psSHP->nRecords++; - if( psSHP->nRecords > psSHP->nMaxRecords ) + if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords ) { - psSHP->nMaxRecords = (int)( psSHP->nMaxRecords * 1.3 ) + 100; + psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100); psSHP->panRecOffset = (int *) - SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords ); + SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords ); psSHP->panRecSize = (int *) - SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords ); + SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords ); } /* -------------------------------------------------------------------- */ /* Initialize record. */ /* -------------------------------------------------------------------- */ - psSHP->panRecOffset[psSHP->nRecords-1] = nRecordOffset = psSHP->nFileSize; - 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 ) + if( psObject->nSHPType == SHPT_POLYGON + || psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_POLYGONM + || psObject->nSHPType == SHPT_ARC + || psObject->nSHPType == SHPT_ARCZ + || psObject->nSHPType == SHPT_ARCM + || psObject->nSHPType == SHPT_MULTIPATCH ) { int32 nPoints, nParts; int i; @@ -1009,7 +998,7 @@ int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* * Write multipatch part types if needed. */ - if( psSHP->nShapeType == SHPT_MULTIPATCH ) + if( psObject->nSHPType == SHPT_MULTIPATCH ) { memcpy( pabyRec + nRecordSize, psObject->panPartType, 4*psObject->nParts ); @@ -1040,9 +1029,9 @@ int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* * Write the Z coordinates (if any). */ - if( psSHP->nShapeType == SHPT_POLYGONZ - || psSHP->nShapeType == SHPT_ARCZ - || psSHP->nShapeType == SHPT_MULTIPATCH ) + if( psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_ARCZ + || psObject->nSHPType == SHPT_MULTIPATCH ) { ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); @@ -1063,13 +1052,13 @@ int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* * Write the M values, if any. */ - if( psSHP->nShapeType == SHPT_POLYGONM - || psSHP->nShapeType == SHPT_ARCM + if( psObject->nSHPType == SHPT_POLYGONM + || psObject->nSHPType == SHPT_ARCM #ifndef DISABLE_MULTIPATCH_MEASURE - || psSHP->nShapeType == SHPT_MULTIPATCH + || psObject->nSHPType == SHPT_MULTIPATCH #endif - || psSHP->nShapeType == SHPT_POLYGONZ - || psSHP->nShapeType == SHPT_ARCZ ) + || psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_ARCZ ) { ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); @@ -1091,9 +1080,9 @@ int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* -------------------------------------------------------------------- */ /* Extract vertices for a MultiPoint. */ /* -------------------------------------------------------------------- */ - else if( psSHP->nShapeType == SHPT_MULTIPOINT - || psSHP->nShapeType == SHPT_MULTIPOINTZ - || psSHP->nShapeType == SHPT_MULTIPOINTM ) + else if( psObject->nSHPType == SHPT_MULTIPOINT + || psObject->nSHPType == SHPT_MULTIPOINTZ + || psObject->nSHPType == SHPT_MULTIPOINTM ) { int32 nPoints; int i; @@ -1116,7 +1105,7 @@ int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) nRecordSize = 48 + 16 * psObject->nVertices; - if( psSHP->nShapeType == SHPT_MULTIPOINTZ ) + if( psObject->nSHPType == SHPT_MULTIPOINTZ ) { ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); @@ -1134,8 +1123,8 @@ int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) } } - if( psSHP->nShapeType == SHPT_MULTIPOINTZ - || psSHP->nShapeType == SHPT_MULTIPOINTM ) + if( psObject->nSHPType == SHPT_MULTIPOINTZ + || psObject->nSHPType == SHPT_MULTIPOINTM ) { ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); @@ -1155,11 +1144,11 @@ int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) } /* -------------------------------------------------------------------- */ -/* Extract vertices for a point. */ +/* Write point. */ /* -------------------------------------------------------------------- */ - else if( psSHP->nShapeType == SHPT_POINT - || psSHP->nShapeType == SHPT_POINTZ - || psSHP->nShapeType == SHPT_POINTM ) + else if( psObject->nSHPType == SHPT_POINT + || psObject->nSHPType == SHPT_POINTZ + || psObject->nSHPType == SHPT_POINTM ) { ByteCopy( psObject->padfX, pabyRec + 12, 8 ); ByteCopy( psObject->padfY, pabyRec + 20, 8 ); @@ -1169,15 +1158,15 @@ int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) nRecordSize = 28; - if( psSHP->nShapeType == SHPT_POINTZ ) + if( psObject->nSHPType == 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 ) + if( psObject->nSHPType == SHPT_POINTZ + || psObject->nSHPType == SHPT_POINTM ) { ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); @@ -1185,46 +1174,80 @@ int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) } } +/* -------------------------------------------------------------------- */ +/* Not much to do for null geometries. */ +/* -------------------------------------------------------------------- */ + else if( psObject->nSHPType == SHPT_NULL ) + { + nRecordSize = 12; + } + else { /* unknown type */ assert( FALSE ); } +/* -------------------------------------------------------------------- */ +/* Establish where we are going to put this record. If we are */ +/* rewriting and existing record, and it will fit, then put it */ +/* back where the original came from. Otherwise write at the end. */ +/* -------------------------------------------------------------------- */ + if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 ) + { + if( nShapeId == -1 ) + nShapeId = psSHP->nRecords++; + + psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize; + psSHP->panRecSize[nShapeId] = nRecordSize-8; + psSHP->nFileSize += nRecordSize; + } + else + { + nRecordOffset = psSHP->panRecOffset[nShapeId]; + } + /* -------------------------------------------------------------------- */ /* Set the shape type, record number, and record size. */ /* -------------------------------------------------------------------- */ - i32 = psSHP->nRecords-1+1; /* record # */ + i32 = nShapeId+1; /* record # */ if( !bBigEndian ) SwapWord( 4, &i32 ); ByteCopy( &i32, pabyRec, 4 ); - i32 = nRecordSize/2; /* record size */ + i32 = (nRecordSize-8)/2; /* record size */ if( !bBigEndian ) SwapWord( 4, &i32 ); ByteCopy( &i32, pabyRec + 4, 4 ); - i32 = psSHP->nShapeType; /* shape type */ + i32 = psObject->nSHPType; /* shape type */ if( bBigEndian ) SwapWord( 4, &i32 ); ByteCopy( &i32, pabyRec + 8, 4 ); /* -------------------------------------------------------------------- */ /* Write out record. */ /* -------------------------------------------------------------------- */ - fseek( psSHP->fpSHP, nRecordOffset, 0 ); - fwrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ); + 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 ); - psSHP->panRecSize[psSHP->nRecords-1] = nRecordSize-8; - psSHP->nFileSize += nRecordSize; - /* -------------------------------------------------------------------- */ /* Expand file wide bounds based on this shape. */ /* -------------------------------------------------------------------- */ - if( psSHP->nRecords == 1 ) + if( psSHP->adBoundsMin[0] == 0.0 + && psSHP->adBoundsMax[0] == 0.0 + && psSHP->adBoundsMin[1] == 0.0 + && psSHP->adBoundsMax[1] == 0.0 + && psObject->nSHPType != SHPT_NULL ) { - 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]; + 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++ ) @@ -1239,7 +1262,7 @@ int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); } - return( psSHP->nRecords - 1 ); + return( nShapeId ); } /************************************************************************/ @@ -1249,14 +1272,12 @@ int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) /* for one shape. */ /************************************************************************/ -SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) +SHPObject SHPAPI_CALL1(*) +SHPReadObject( SHPHandle psSHP, int hEntity ) { SHPObject *psShape; - static uchar *pabyRec = NULL; - static int nBufSize = 0; - /* -------------------------------------------------------------------- */ /* Validate the record/entity number. */ /* -------------------------------------------------------------------- */ @@ -1266,34 +1287,36 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ /* Ensure our record buffer is large enough. */ /* -------------------------------------------------------------------- */ - if( psSHP->panRecSize[hEntity]+8 > nBufSize ) + if( psSHP->panRecSize[hEntity]+8 > psSHP->nBufSize ) { - nBufSize = psSHP->panRecSize[hEntity]+8; - pabyRec = (uchar *) SfRealloc(pabyRec,nBufSize); + psSHP->nBufSize = psSHP->panRecSize[hEntity]+8; + psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,psSHP->nBufSize); } /* -------------------------------------------------------------------- */ /* Read the record. */ /* -------------------------------------------------------------------- */ fseek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ); - fread( pabyRec, psSHP->panRecSize[hEntity]+8, 1, psSHP->fpSHP ); + fread( psSHP->pabyRec, psSHP->panRecSize[hEntity]+8, 1, psSHP->fpSHP ); /* -------------------------------------------------------------------- */ /* Allocate and minimally initialize the object. */ /* -------------------------------------------------------------------- */ psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); psShape->nShapeId = hEntity; - psShape->nSHPType = psSHP->nShapeType; /* should get from this shape */ + + memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 ); + if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) ); /* ==================================================================== */ /* Extract vertices for a Polygon or Arc. */ /* ==================================================================== */ - if( psSHP->nShapeType == SHPT_POLYGON || psSHP->nShapeType == SHPT_ARC - || psSHP->nShapeType == SHPT_POLYGONZ - || psSHP->nShapeType == SHPT_POLYGONM - || psSHP->nShapeType == SHPT_ARCZ - || psSHP->nShapeType == SHPT_ARCM - || psSHP->nShapeType == SHPT_MULTIPATCH ) + 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; @@ -1301,10 +1324,10 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ /* 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 ); + memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); + memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); + memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); + memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); @@ -1315,8 +1338,8 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* 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 ); + memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 ); + memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 ); if( bBigEndian ) SwapWord( 4, &nPoints ); if( bBigEndian ) SwapWord( 4, &nParts ); @@ -1337,7 +1360,7 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ /* Copy out the part array from the record. */ /* -------------------------------------------------------------------- */ - memcpy( psShape->panPartStart, pabyRec + 44 + 8, 4 * nParts ); + memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts ); for( i = 0; i < nParts; i++ ) { if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i ); @@ -1348,9 +1371,9 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ /* If this is a multipatch, we will also have parts types. */ /* -------------------------------------------------------------------- */ - if( psSHP->nShapeType == SHPT_MULTIPATCH ) + if( psShape->nSHPType == SHPT_MULTIPATCH ) { - memcpy( psShape->panPartType, pabyRec + nOffset, 4*nParts ); + memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts ); for( i = 0; i < nParts; i++ ) { if( bBigEndian ) SwapWord( 4, psShape->panPartType+i ); @@ -1365,11 +1388,11 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) for( i = 0; i < nPoints; i++ ) { memcpy(psShape->padfX + i, - pabyRec + nOffset + i * 16, + psSHP->pabyRec + nOffset + i * 16, 8 ); memcpy(psShape->padfY + i, - pabyRec + nOffset + i * 16 + 8, + psSHP->pabyRec + nOffset + i * 16 + 8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); @@ -1381,12 +1404,12 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ /* If we have a Z coordinate, collect that now. */ /* -------------------------------------------------------------------- */ - if( psSHP->nShapeType == SHPT_POLYGONZ - || psSHP->nShapeType == SHPT_ARCZ - || psSHP->nShapeType == SHPT_MULTIPATCH ) + 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 ); + memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); @@ -1394,7 +1417,7 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) for( i = 0; i < nPoints; i++ ) { memcpy( psShape->padfZ + i, - pabyRec + nOffset + 16 + i*8, 8 ); + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); } @@ -1409,8 +1432,8 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints ) { - memcpy( &(psShape->dfMMin), pabyRec + nOffset, 8 ); - memcpy( &(psShape->dfMMax), pabyRec + nOffset + 8, 8 ); + memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); @@ -1418,7 +1441,7 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) for( i = 0; i < nPoints; i++ ) { memcpy( psShape->padfM + i, - pabyRec + nOffset + 16 + i*8, 8 ); + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); } } @@ -1428,14 +1451,14 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* ==================================================================== */ /* Extract vertices for a MultiPoint. */ /* ==================================================================== */ - else if( psSHP->nShapeType == SHPT_MULTIPOINT - || psSHP->nShapeType == SHPT_MULTIPOINTM - || psSHP->nShapeType == SHPT_MULTIPOINTZ ) + else if( psShape->nSHPType == SHPT_MULTIPOINT + || psShape->nSHPType == SHPT_MULTIPOINTM + || psShape->nSHPType == SHPT_MULTIPOINTZ ) { int32 nPoints; int i, nOffset; - memcpy( &nPoints, pabyRec + 44, 4 ); + memcpy( &nPoints, psSHP->pabyRec + 44, 4 ); if( bBigEndian ) SwapWord( 4, &nPoints ); psShape->nVertices = nPoints; @@ -1446,8 +1469,8 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) 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 ); + memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 ); + memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); @@ -1458,10 +1481,10 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ /* 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 ); + memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); + memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); + memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); + memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); @@ -1471,10 +1494,10 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ /* If we have a Z coordinate, collect that now. */ /* -------------------------------------------------------------------- */ - if( psSHP->nShapeType == SHPT_MULTIPOINTZ ) + if( psShape->nSHPType == SHPT_MULTIPOINTZ ) { - memcpy( &(psShape->dfZMin), pabyRec + nOffset, 8 ); - memcpy( &(psShape->dfZMax), pabyRec + nOffset + 8, 8 ); + memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); @@ -1482,7 +1505,7 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) for( i = 0; i < nPoints; i++ ) { memcpy( psShape->padfZ + i, - pabyRec + nOffset + 16 + i*8, 8 ); + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); } @@ -1497,8 +1520,8 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints ) { - memcpy( &(psShape->dfMMin), pabyRec + nOffset, 8 ); - memcpy( &(psShape->dfMMax), pabyRec + nOffset + 8, 8 ); + memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); @@ -1506,7 +1529,7 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) for( i = 0; i < nPoints; i++ ) { memcpy( psShape->padfM + i, - pabyRec + nOffset + 16 + i*8, 8 ); + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); } } @@ -1515,9 +1538,9 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* ==================================================================== */ /* Extract vertices for a point. */ /* ==================================================================== */ - else if( psSHP->nShapeType == SHPT_POINT - || psSHP->nShapeType == SHPT_POINTM - || psSHP->nShapeType == SHPT_POINTZ ) + else if( psShape->nSHPType == SHPT_POINT + || psShape->nSHPType == SHPT_POINTM + || psShape->nSHPType == SHPT_POINTZ ) { int nOffset; @@ -1527,8 +1550,8 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) 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 ); + memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 ); + memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfX ); if( bBigEndian ) SwapWord( 8, psShape->padfY ); @@ -1538,9 +1561,9 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ /* If we have a Z coordinate, collect that now. */ /* -------------------------------------------------------------------- */ - if( psSHP->nShapeType == SHPT_POINTZ ) + if( psShape->nSHPType == SHPT_POINTZ ) { - memcpy( psShape->padfZ, pabyRec + nOffset, 8 ); + memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfZ ); @@ -1555,7 +1578,7 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* -------------------------------------------------------------------- */ if( psSHP->panRecSize[hEntity]+8 >= nOffset + 8 ) { - memcpy( psShape->padfM, pabyRec + nOffset, 8 ); + memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfM ); } @@ -1577,12 +1600,13 @@ SHPObject *SHPReadObject( SHPHandle psSHP, int hEntity ) /* SHPTypeName() */ /************************************************************************/ -const char *SHPTypeName( int nSHPType ) +const char SHPAPI_CALL1(*) +SHPTypeName( int nSHPType ) { switch( nSHPType ) { - case 0: + case SHPT_NULL: return "NullShape"; case SHPT_POINT: @@ -1633,7 +1657,8 @@ const char *SHPTypeName( int nSHPType ) /* SHPPartTypeName() */ /************************************************************************/ -const char *SHPPartTypeName( int nPartType ) +const char SHPAPI_CALL1(*) +SHPPartTypeName( int nPartType ) { switch( nPartType ) @@ -1665,7 +1690,8 @@ const char *SHPPartTypeName( int nPartType ) /* SHPDestroyObject() */ /************************************************************************/ -void SHPDestroyObject( SHPObject * psShape ) +void SHPAPI_CALL +SHPDestroyObject( SHPObject * psShape ) { if( psShape == NULL ) @@ -1687,3 +1713,154 @@ void SHPDestroyObject( SHPObject * psShape ) free( psShape ); } + +/************************************************************************/ +/* SHPRewindObject() */ +/* */ +/* Reset the winding of polygon objects to adhere to the */ +/* specification. */ +/************************************************************************/ + +int SHPAPI_CALL +SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) + +{ + int iOpRing, bAltered = 0; + +/* -------------------------------------------------------------------- */ +/* Do nothing if this is not a polygon object. */ +/* -------------------------------------------------------------------- */ + if( psObject->nSHPType != SHPT_POLYGON + && psObject->nSHPType != SHPT_POLYGONZ + && psObject->nSHPType != SHPT_POLYGONM ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Process each of the rings. */ +/* -------------------------------------------------------------------- */ + for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ ) + { + int bInner, iVert, nVertCount, nVertStart, iCheckRing; + double dfSum, dfTestX, dfTestY; + +/* -------------------------------------------------------------------- */ +/* Determine if this ring is an inner ring or an outer ring */ +/* relative to all the other rings. For now we assume the */ +/* first ring is outer and all others are inner, but eventually */ +/* we need to fix this to handle multiple island polygons and */ +/* unordered sets of rings. */ +/* -------------------------------------------------------------------- */ + dfTestX = psObject->padfX[psObject->panPartStart[iOpRing]]; + dfTestY = psObject->padfY[psObject->panPartStart[iOpRing]]; + + bInner = FALSE; + for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ ) + { + int iEdge; + + if( iCheckRing == iOpRing ) + continue; + + nVertStart = psObject->panPartStart[iCheckRing]; + + if( iCheckRing == psObject->nParts-1 ) + nVertCount = psObject->nVertices + - psObject->panPartStart[iCheckRing]; + else + nVertCount = psObject->panPartStart[iCheckRing+1] + - psObject->panPartStart[iCheckRing]; + + for( iEdge = 0; iEdge < nVertCount; iEdge++ ) + { + int iNext; + + if( iEdge < nVertCount-1 ) + iNext = iEdge+1; + else + iNext = 0; + + if( (psObject->padfY[iEdge+nVertStart] < dfTestY + && psObject->padfY[iNext+nVertStart] >= dfTestY) + || (psObject->padfY[iNext+nVertStart] < dfTestY + && psObject->padfY[iEdge+nVertStart] >= dfTestY) ) + { + if( psObject->padfX[iEdge+nVertStart] + + (dfTestY - psObject->padfY[iEdge+nVertStart]) + / (psObject->padfY[iNext+nVertStart] + - psObject->padfY[iEdge+nVertStart]) + * (psObject->padfX[iNext+nVertStart] + - psObject->padfX[iEdge+nVertStart]) < dfTestX ) + bInner = !bInner; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Determine the current order of this ring so we will know if */ +/* it has to be reversed. */ +/* -------------------------------------------------------------------- */ + nVertStart = psObject->panPartStart[iOpRing]; + + if( iOpRing == psObject->nParts-1 ) + nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing]; + else + nVertCount = psObject->panPartStart[iOpRing+1] + - psObject->panPartStart[iOpRing]; + + dfSum = 0.0; + for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ ) + { + dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1] + - psObject->padfY[iVert] * psObject->padfX[iVert+1]; + } + + dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart] + - psObject->padfY[iVert] * psObject->padfX[nVertStart]; + +/* -------------------------------------------------------------------- */ +/* Reverse if necessary. */ +/* -------------------------------------------------------------------- */ + if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) ) + { + int i; + + bAltered++; + for( i = 0; i < nVertCount/2; i++ ) + { + double dfSaved; + + /* Swap X */ + dfSaved = psObject->padfX[nVertStart+i]; + psObject->padfX[nVertStart+i] = + psObject->padfX[nVertStart+nVertCount-i-1]; + psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved; + + /* Swap Y */ + dfSaved = psObject->padfY[nVertStart+i]; + psObject->padfY[nVertStart+i] = + psObject->padfY[nVertStart+nVertCount-i-1]; + psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved; + + /* Swap Z */ + if( psObject->padfZ ) + { + dfSaved = psObject->padfZ[nVertStart+i]; + psObject->padfZ[nVertStart+i] = + psObject->padfZ[nVertStart+nVertCount-i-1]; + psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved; + } + + /* Swap M */ + if( psObject->padfM ) + { + dfSaved = psObject->padfM[nVertStart+i]; + psObject->padfM[nVertStart+i] = + psObject->padfM[nVertStart+nVertCount-i-1]; + psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved; + } + } + } + } + + return bAltered; +} diff --git a/src/Lib/shapelib/shprewind.c b/src/Lib/shapelib/shprewind.c new file mode 100644 index 00000000..503ee517 --- /dev/null +++ b/src/Lib/shapelib/shprewind.c @@ -0,0 +1,109 @@ +/****************************************************************************** + * $Id: shprewind.c,v 1.2 2002/04/10 17:23:11 warmerda Exp $ + * + * Project: Shapelib + * Purpose: Utility to validate and reset the winding order of rings in + * polygon geometries to match the ordering required by spec. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, 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: shprewind.c,v $ + * Revision 1.2 2002/04/10 17:23:11 warmerda + * copy from source to destination now + * + * Revision 1.1 2002/04/10 16:56:36 warmerda + * New + * + */ + +#include "shapefil.h" + +int main( int argc, char ** argv ) + +{ + SHPHandle hSHP, hSHPOut; + int nShapeType, nEntities, i, nInvalidCount=0; + double adfMinBound[4], adfMaxBound[4]; + +/* -------------------------------------------------------------------- */ +/* Display a usage message. */ +/* -------------------------------------------------------------------- */ + if( argc != 3 ) + { + printf( "shprewind in_shp_file out_shp_file\n" ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Open the passed shapefile. */ +/* -------------------------------------------------------------------- */ + hSHP = SHPOpen( argv[1], "rb" ); + + if( hSHP == NULL ) + { + printf( "Unable to open:%s\n", argv[1] ); + exit( 1 ); + } + + SHPGetInfo( hSHP, &nEntities, &nShapeType, adfMinBound, adfMaxBound ); + +/* -------------------------------------------------------------------- */ +/* Create output shapefile. */ +/* -------------------------------------------------------------------- */ + hSHPOut = SHPCreate( argv[2], nShapeType ); + + if( hSHPOut == NULL ) + { + printf( "Unable to create:%s\n", argv[2] ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Skim over the list of shapes, printing all the vertices. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nEntities; i++ ) + { + int j; + SHPObject *psShape; + + psShape = SHPReadObject( hSHP, i ); + if( SHPRewindObject( hSHP, psShape ) ) + nInvalidCount++; + SHPWriteObject( hSHPOut, -1, psShape ); + SHPDestroyObject( psShape ); + } + + SHPClose( hSHP ); + SHPClose( hSHPOut ); + + printf( "%d objects rewound.\n", nInvalidCount ); + + exit( 0 ); +} diff --git a/src/Lib/shapelib/shptest.c b/src/Lib/shapelib/shptest.c index c9f2035c..83b471ae 100644 --- a/src/Lib/shapelib/shptest.c +++ b/src/Lib/shapelib/shptest.c @@ -1,14 +1,51 @@ -/* - * Copyright (c) 1998 Frank Warmerdam +/****************************************************************************** + * $Id: shptest.c,v 1.6 2002/01/15 14:36:07 warmerda Exp $ * - * This code is in the public domain. + * Project: Shapelib + * Purpose: Application for generating sample Shapefiles of various types. + * Used by the stream2.sh test script. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** * * $Log: shptest.c,v $ - * Revision 1.1 2000-02-09 19:51:46 curt - * Initial revision + * Revision 1.6 2002/01/15 14:36:07 warmerda + * updated email address * - * Revision 1.1 1999/08/24 21:13:01 curt - * Initial revision. + * Revision 1.5 2001/06/22 02:18:20 warmerda + * Added null shape support + * + * Revision 1.4 2000/07/07 13:39:45 warmerda + * removed unused variables, and added system include files + * + * Revision 1.3 1999/11/05 14:12:05 warmerda + * updated license terms * * Revision 1.2 1998/12/16 05:15:20 warmerda * Added support for writing multipatch. @@ -19,8 +56,10 @@ */ static char rcsid[] = - "$Id: shptest.c,v 1.1 2000-02-09 19:51:46 curt Exp $"; + "$Id: shptest.c,v 1.6 2002/01/15 14:36:07 warmerda Exp $"; +#include +#include #include "shapefil.h" /************************************************************************/ @@ -208,10 +247,6 @@ static void Test_WriteArcPoly( int nSHPType, const char *pszFilename ) int main( int argc, char ** argv ) { - SHPHandle hSHP; - int nShapeType, nEntities, i, iPart; - const char *pszPlus; - /* -------------------------------------------------------------------- */ /* Display a usage message. */ /* -------------------------------------------------------------------- */ @@ -225,7 +260,10 @@ int main( int argc, char ** argv ) /* Figure out which test to run. */ /* -------------------------------------------------------------------- */ - if( atoi(argv[1]) == 1 ) + if( atoi(argv[1]) == 0 ) + Test_WritePoints( SHPT_NULL, "test0.shp" ); + + else if( atoi(argv[1]) == 1 ) Test_WritePoints( SHPT_POINT, "test1.shp" ); else if( atoi(argv[1]) == 2 ) Test_WritePoints( SHPT_POINTZ, "test2.shp" ); diff --git a/src/Lib/shapelib/shptree.c b/src/Lib/shapelib/shptree.c new file mode 100644 index 00000000..98d5ce01 --- /dev/null +++ b/src/Lib/shapelib/shptree.c @@ -0,0 +1,679 @@ +/****************************************************************************** + * $Id: shptree.c,v 1.9 2003/01/28 15:53:41 warmerda Exp $ + * + * Project: Shapelib + * Purpose: Implementation of quadtree building and searching functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log: shptree.c,v $ + * Revision 1.9 2003/01/28 15:53:41 warmerda + * Avoid build warnings. + * + * Revision 1.8 2002/05/07 13:07:45 warmerda + * use qsort() - patch from Bernhard Herzog + * + * Revision 1.7 2002/01/15 14:36:07 warmerda + * updated email address + * + * Revision 1.6 2001/05/23 13:36:52 warmerda + * added use of SHPAPI_CALL + * + * Revision 1.5 1999/11/05 14:12:05 warmerda + * updated license terms + * + * Revision 1.4 1999/06/02 18:24:21 warmerda + * added trimming code + * + * Revision 1.3 1999/06/02 17:56:12 warmerda + * added quad'' subnode support for trees + * + * Revision 1.2 1999/05/18 19:11:11 warmerda + * Added example searching capability + * + * Revision 1.1 1999/05/18 17:49:20 warmerda + * New + * + */ + +static char rcsid[] = + "$Id: shptree.c,v 1.9 2003/01/28 15:53:41 warmerda Exp $"; + +#include "shapefil.h" + +#include +#include +#include +#include + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + + +/* -------------------------------------------------------------------- */ +/* If the following is 0.5, nodes will be split in half. If it */ +/* is 0.6 then each subnode will contain 60% of the parent */ +/* node, with 20% representing overlap. This can be help to */ +/* prevent small objects on a boundary from shifting too high */ +/* up the tree. */ +/* -------------------------------------------------------------------- */ + +#define SHP_SPLIT_RATIO 0.55 + +/************************************************************************/ +/* SfRealloc() */ +/* */ +/* A realloc cover function that will access a NULL pointer as */ +/* a valid input. */ +/************************************************************************/ + +static void * SfRealloc( void * pMem, int nNewSize ) + +{ + if( pMem == NULL ) + return( (void *) malloc(nNewSize) ); + else + return( (void *) realloc(pMem,nNewSize) ); +} + +/************************************************************************/ +/* SHPTreeNodeInit() */ +/* */ +/* Initialize a tree node. */ +/************************************************************************/ + +static SHPTreeNode *SHPTreeNodeCreate( double * padfBoundsMin, + double * padfBoundsMax ) + +{ + SHPTreeNode *psTreeNode; + + psTreeNode = (SHPTreeNode *) malloc(sizeof(SHPTreeNode)); + + psTreeNode->nShapeCount = 0; + psTreeNode->panShapeIds = NULL; + psTreeNode->papsShapeObj = NULL; + + psTreeNode->nSubNodes = 0; + + if( padfBoundsMin != NULL ) + memcpy( psTreeNode->adfBoundsMin, padfBoundsMin, sizeof(double) * 4 ); + + if( padfBoundsMax != NULL ) + memcpy( psTreeNode->adfBoundsMax, padfBoundsMax, sizeof(double) * 4 ); + + return psTreeNode; +} + + +/************************************************************************/ +/* SHPCreateTree() */ +/************************************************************************/ + +SHPTree SHPAPI_CALL1(*) +SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, + double *padfBoundsMin, double *padfBoundsMax ) + +{ + SHPTree *psTree; + + if( padfBoundsMin == NULL && hSHP == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Allocate the tree object */ +/* -------------------------------------------------------------------- */ + psTree = (SHPTree *) malloc(sizeof(SHPTree)); + + psTree->hSHP = hSHP; + psTree->nMaxDepth = nMaxDepth; + psTree->nDimension = nDimension; + +/* -------------------------------------------------------------------- */ +/* If no max depth was defined, try to select a reasonable one */ +/* that implies approximately 8 shapes per node. */ +/* -------------------------------------------------------------------- */ + if( psTree->nMaxDepth == 0 && hSHP != NULL ) + { + int nMaxNodeCount = 1; + int nShapeCount; + + SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL ); + while( nMaxNodeCount*4 < nShapeCount ) + { + psTree->nMaxDepth += 1; + nMaxNodeCount = nMaxNodeCount * 2; + } + } + +/* -------------------------------------------------------------------- */ +/* Allocate the root node. */ +/* -------------------------------------------------------------------- */ + psTree->psRoot = SHPTreeNodeCreate( padfBoundsMin, padfBoundsMax ); + +/* -------------------------------------------------------------------- */ +/* Assign the bounds to the root node. If none are passed in, */ +/* use the bounds of the provided file otherwise the create */ +/* function will have already set the bounds. */ +/* -------------------------------------------------------------------- */ + if( padfBoundsMin == NULL ) + { + SHPGetInfo( hSHP, NULL, NULL, + psTree->psRoot->adfBoundsMin, + psTree->psRoot->adfBoundsMax ); + } + +/* -------------------------------------------------------------------- */ +/* If we have a file, insert all it's shapes into the tree. */ +/* -------------------------------------------------------------------- */ + if( hSHP != NULL ) + { + int iShape, nShapeCount; + + SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL ); + + for( iShape = 0; iShape < nShapeCount; iShape++ ) + { + SHPObject *psShape; + + psShape = SHPReadObject( hSHP, iShape ); + SHPTreeAddShapeId( psTree, psShape ); + SHPDestroyObject( psShape ); + } + } + + return psTree; +} + +/************************************************************************/ +/* SHPDestroyTreeNode() */ +/************************************************************************/ + +static void SHPDestroyTreeNode( SHPTreeNode * psTreeNode ) + +{ + int i; + + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( psTreeNode->apsSubNode[i] != NULL ) + SHPDestroyTreeNode( psTreeNode->apsSubNode[i] ); + } + + if( psTreeNode->panShapeIds != NULL ) + free( psTreeNode->panShapeIds ); + + if( psTreeNode->papsShapeObj != NULL ) + { + for( i = 0; i < psTreeNode->nShapeCount; i++ ) + { + if( psTreeNode->papsShapeObj[i] != NULL ) + SHPDestroyObject( psTreeNode->papsShapeObj[i] ); + } + + free( psTreeNode->papsShapeObj ); + } + + free( psTreeNode ); +} + +/************************************************************************/ +/* SHPDestroyTree() */ +/************************************************************************/ + +void SHPAPI_CALL +SHPDestroyTree( SHPTree * psTree ) + +{ + SHPDestroyTreeNode( psTree->psRoot ); + free( psTree ); +} + +/************************************************************************/ +/* SHPCheckBoundsOverlap() */ +/* */ +/* Do the given boxes overlap at all? */ +/************************************************************************/ + +int SHPAPI_CALL +SHPCheckBoundsOverlap( double * padfBox1Min, double * padfBox1Max, + double * padfBox2Min, double * padfBox2Max, + int nDimension ) + +{ + int iDim; + + for( iDim = 0; iDim < nDimension; iDim++ ) + { + if( padfBox2Max[iDim] < padfBox1Min[iDim] ) + return FALSE; + + if( padfBox1Max[iDim] < padfBox2Min[iDim] ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* SHPCheckObjectContained() */ +/* */ +/* Does the given shape fit within the indicated extents? */ +/************************************************************************/ + +static int SHPCheckObjectContained( SHPObject * psObject, int nDimension, + double * padfBoundsMin, double * padfBoundsMax ) + +{ + if( psObject->dfXMin < padfBoundsMin[0] + || psObject->dfXMax > padfBoundsMax[0] ) + return FALSE; + + if( psObject->dfYMin < padfBoundsMin[1] + || psObject->dfYMax > padfBoundsMax[1] ) + return FALSE; + + if( nDimension == 2 ) + return TRUE; + + if( psObject->dfZMin < padfBoundsMin[2] + || psObject->dfZMax < padfBoundsMax[2] ) + return FALSE; + + if( nDimension == 3 ) + return TRUE; + + if( psObject->dfMMin < padfBoundsMin[3] + || psObject->dfMMax < padfBoundsMax[3] ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* SHPTreeSplitBounds() */ +/* */ +/* Split a region into two subregion evenly, cutting along the */ +/* longest dimension. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeSplitBounds( double *padfBoundsMinIn, double *padfBoundsMaxIn, + double *padfBoundsMin1, double * padfBoundsMax1, + double *padfBoundsMin2, double * padfBoundsMax2 ) + +{ +/* -------------------------------------------------------------------- */ +/* The output bounds will be very similar to the input bounds, */ +/* so just copy over to start. */ +/* -------------------------------------------------------------------- */ + memcpy( padfBoundsMin1, padfBoundsMinIn, sizeof(double) * 4 ); + memcpy( padfBoundsMax1, padfBoundsMaxIn, sizeof(double) * 4 ); + memcpy( padfBoundsMin2, padfBoundsMinIn, sizeof(double) * 4 ); + memcpy( padfBoundsMax2, padfBoundsMaxIn, sizeof(double) * 4 ); + +/* -------------------------------------------------------------------- */ +/* Split in X direction. */ +/* -------------------------------------------------------------------- */ + if( (padfBoundsMaxIn[0] - padfBoundsMinIn[0]) + > (padfBoundsMaxIn[1] - padfBoundsMinIn[1]) ) + { + double dfRange = padfBoundsMaxIn[0] - padfBoundsMinIn[0]; + + padfBoundsMax1[0] = padfBoundsMinIn[0] + dfRange * SHP_SPLIT_RATIO; + padfBoundsMin2[0] = padfBoundsMaxIn[0] - dfRange * SHP_SPLIT_RATIO; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise split in Y direction. */ +/* -------------------------------------------------------------------- */ + else + { + double dfRange = padfBoundsMaxIn[1] - padfBoundsMinIn[1]; + + padfBoundsMax1[1] = padfBoundsMinIn[1] + dfRange * SHP_SPLIT_RATIO; + padfBoundsMin2[1] = padfBoundsMaxIn[1] - dfRange * SHP_SPLIT_RATIO; + } +} + +/************************************************************************/ +/* SHPTreeNodeAddShapeId() */ +/************************************************************************/ + +static int +SHPTreeNodeAddShapeId( SHPTreeNode * psTreeNode, SHPObject * psObject, + int nMaxDepth, int nDimension ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* If there are subnodes, then consider wiether this object */ +/* will fit in them. */ +/* -------------------------------------------------------------------- */ + if( nMaxDepth > 1 && psTreeNode->nSubNodes > 0 ) + { + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( SHPCheckObjectContained(psObject, nDimension, + psTreeNode->apsSubNode[i]->adfBoundsMin, + psTreeNode->apsSubNode[i]->adfBoundsMax)) + { + return SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[i], + psObject, nMaxDepth-1, + nDimension ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise, consider creating four subnodes if could fit into */ +/* them, and adding to the appropriate subnode. */ +/* -------------------------------------------------------------------- */ +#if MAX_SUBNODE == 4 + else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 ) + { + double adfBoundsMinH1[4], adfBoundsMaxH1[4]; + double adfBoundsMinH2[4], adfBoundsMaxH2[4]; + double adfBoundsMin1[4], adfBoundsMax1[4]; + double adfBoundsMin2[4], adfBoundsMax2[4]; + double adfBoundsMin3[4], adfBoundsMax3[4]; + double adfBoundsMin4[4], adfBoundsMax4[4]; + + SHPTreeSplitBounds( psTreeNode->adfBoundsMin, + psTreeNode->adfBoundsMax, + adfBoundsMinH1, adfBoundsMaxH1, + adfBoundsMinH2, adfBoundsMaxH2 ); + + SHPTreeSplitBounds( adfBoundsMinH1, adfBoundsMaxH1, + adfBoundsMin1, adfBoundsMax1, + adfBoundsMin2, adfBoundsMax2 ); + + SHPTreeSplitBounds( adfBoundsMinH2, adfBoundsMaxH2, + adfBoundsMin3, adfBoundsMax3, + adfBoundsMin4, adfBoundsMax4 ); + + if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin1, adfBoundsMax1) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin2, adfBoundsMax2) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin3, adfBoundsMax3) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin4, adfBoundsMax4) ) + { + psTreeNode->nSubNodes = 4; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + psTreeNode->apsSubNode[2] = SHPTreeNodeCreate( adfBoundsMin3, + adfBoundsMax3 ); + psTreeNode->apsSubNode[3] = SHPTreeNodeCreate( adfBoundsMin4, + adfBoundsMax4 ); + + /* recurse back on this node now that it has subnodes */ + return( SHPTreeNodeAddShapeId( psTreeNode, psObject, + nMaxDepth, nDimension ) ); + } + } +#endif /* MAX_SUBNODE == 4 */ + +/* -------------------------------------------------------------------- */ +/* Otherwise, consider creating two subnodes if could fit into */ +/* them, and adding to the appropriate subnode. */ +/* -------------------------------------------------------------------- */ +#if MAX_SUBNODE == 2 + else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 ) + { + double adfBoundsMin1[4], adfBoundsMax1[4]; + double adfBoundsMin2[4], adfBoundsMax2[4]; + + SHPTreeSplitBounds( psTreeNode->adfBoundsMin, psTreeNode->adfBoundsMax, + adfBoundsMin1, adfBoundsMax1, + adfBoundsMin2, adfBoundsMax2 ); + + if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin1, adfBoundsMax1)) + { + psTreeNode->nSubNodes = 2; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + + return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[0], psObject, + nMaxDepth - 1, nDimension ) ); + } + else if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin2, adfBoundsMax2) ) + { + psTreeNode->nSubNodes = 2; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + + return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[1], psObject, + nMaxDepth - 1, nDimension ) ); + } + } +#endif /* MAX_SUBNODE == 2 */ + +/* -------------------------------------------------------------------- */ +/* If none of that worked, just add it to this nodes list. */ +/* -------------------------------------------------------------------- */ + psTreeNode->nShapeCount++; + + psTreeNode->panShapeIds = + SfRealloc( psTreeNode->panShapeIds, + sizeof(int) * psTreeNode->nShapeCount ); + psTreeNode->panShapeIds[psTreeNode->nShapeCount-1] = psObject->nShapeId; + + if( psTreeNode->papsShapeObj != NULL ) + { + psTreeNode->papsShapeObj = + SfRealloc( psTreeNode->papsShapeObj, + sizeof(void *) * psTreeNode->nShapeCount ); + psTreeNode->papsShapeObj[psTreeNode->nShapeCount-1] = NULL; + } + + return TRUE; +} + +/************************************************************************/ +/* SHPTreeAddShapeId() */ +/* */ +/* Add a shape to the tree, but don't keep a pointer to the */ +/* object data, just keep the shapeid. */ +/************************************************************************/ + +int SHPAPI_CALL +SHPTreeAddShapeId( SHPTree * psTree, SHPObject * psObject ) + +{ + return( SHPTreeNodeAddShapeId( psTree->psRoot, psObject, + psTree->nMaxDepth, psTree->nDimension ) ); +} + +/************************************************************************/ +/* SHPTreeCollectShapesIds() */ +/* */ +/* Work function implementing SHPTreeFindLikelyShapes() on a */ +/* tree node by tree node basis. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeCollectShapeIds( SHPTree *hTree, SHPTreeNode * psTreeNode, + double * padfBoundsMin, double * padfBoundsMax, + int * pnShapeCount, int * pnMaxShapes, + int ** ppanShapeList ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Does this node overlap the area of interest at all? If not, */ +/* return without adding to the list at all. */ +/* -------------------------------------------------------------------- */ + if( !SHPCheckBoundsOverlap( psTreeNode->adfBoundsMin, + psTreeNode->adfBoundsMax, + padfBoundsMin, + padfBoundsMax, + hTree->nDimension ) ) + return; + +/* -------------------------------------------------------------------- */ +/* Grow the list to hold the shapes on this node. */ +/* -------------------------------------------------------------------- */ + if( *pnShapeCount + psTreeNode->nShapeCount > *pnMaxShapes ) + { + *pnMaxShapes = (*pnShapeCount + psTreeNode->nShapeCount) * 2 + 20; + *ppanShapeList = (int *) + SfRealloc(*ppanShapeList,sizeof(int) * *pnMaxShapes); + } + +/* -------------------------------------------------------------------- */ +/* Add the local nodes shapeids to the list. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nShapeCount; i++ ) + { + (*ppanShapeList)[(*pnShapeCount)++] = psTreeNode->panShapeIds[i]; + } + +/* -------------------------------------------------------------------- */ +/* Recurse to subnodes if they exist. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( psTreeNode->apsSubNode[i] != NULL ) + SHPTreeCollectShapeIds( hTree, psTreeNode->apsSubNode[i], + padfBoundsMin, padfBoundsMax, + pnShapeCount, pnMaxShapes, + ppanShapeList ); + } +} + +/************************************************************************/ +/* SHPTreeFindLikelyShapes() */ +/* */ +/* Find all shapes within tree nodes for which the tree node */ +/* bounding box overlaps the search box. The return value is */ +/* an array of shapeids terminated by a -1. The shapeids will */ +/* be in order, as hopefully this will result in faster (more */ +/* sequential) reading from the file. */ +/************************************************************************/ + +/* helper for qsort */ +static int +compare_ints( const void * a, const void * b) +{ + return (*(int*)a) - (*(int*)b); +} + +int SHPAPI_CALL1(*) +SHPTreeFindLikelyShapes( SHPTree * hTree, + double * padfBoundsMin, double * padfBoundsMax, + int * pnShapeCount ) + +{ + int *panShapeList=NULL, nMaxShapes = 0; + +/* -------------------------------------------------------------------- */ +/* Perform the search by recursive descent. */ +/* -------------------------------------------------------------------- */ + *pnShapeCount = 0; + + SHPTreeCollectShapeIds( hTree, hTree->psRoot, + padfBoundsMin, padfBoundsMax, + pnShapeCount, &nMaxShapes, + &panShapeList ); + +/* -------------------------------------------------------------------- */ +/* Sort the id array */ +/* -------------------------------------------------------------------- */ + + qsort(panShapeList, *pnShapeCount, sizeof(int), compare_ints); + + return panShapeList; +} + +/************************************************************************/ +/* SHPTreeNodeTrim() */ +/* */ +/* This is the recurve version of SHPTreeTrimExtraNodes() that */ +/* walks the tree cleaning it up. */ +/************************************************************************/ + +static int SHPTreeNodeTrim( SHPTreeNode * psTreeNode ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Trim subtrees, and free subnodes that come back empty. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( SHPTreeNodeTrim( psTreeNode->apsSubNode[i] ) ) + { + SHPDestroyTreeNode( psTreeNode->apsSubNode[i] ); + + psTreeNode->apsSubNode[i] = + psTreeNode->apsSubNode[psTreeNode->nSubNodes-1]; + + psTreeNode->nSubNodes--; + + i--; /* process the new occupant of this subnode entry */ + } + } + +/* -------------------------------------------------------------------- */ +/* We should be trimmed if we have no subnodes, and no shapes. */ +/* -------------------------------------------------------------------- */ + return( psTreeNode->nSubNodes == 0 && psTreeNode->nShapeCount == 0 ); +} + +/************************************************************************/ +/* SHPTreeTrimExtraNodes() */ +/* */ +/* Trim empty nodes from the tree. Note that we never trim an */ +/* empty root node. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeTrimExtraNodes( SHPTree * hTree ) + +{ + SHPTreeNodeTrim( hTree->psRoot ); +} + diff --git a/src/Lib/shapelib/shptreedump.c b/src/Lib/shapelib/shptreedump.c new file mode 100644 index 00000000..caa3c7d0 --- /dev/null +++ b/src/Lib/shapelib/shptreedump.c @@ -0,0 +1,398 @@ +/****************************************************************************** + * $Id: shptreedump.c,v 1.7 2002/04/10 16:59:12 warmerda Exp $ + * + * Project: Shapelib + * Purpose: Mainline for creating and dumping an ASCII representation of + * a quadtree. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log: shptreedump.c,v $ + * Revision 1.7 2002/04/10 16:59:12 warmerda + * fixed email + * + * Revision 1.6 1999/11/05 14:12:05 warmerda + * updated license terms + * + * Revision 1.5 1999/06/02 18:24:21 warmerda + * added trimming code + * + * Revision 1.4 1999/06/02 17:56:12 warmerda + * added quad'' subnode support for trees + * + * Revision 1.3 1999/05/18 19:13:13 warmerda + * Use fabs() instead of abs(). + * + * Revision 1.2 1999/05/18 19:11:11 warmerda + * Added example searching capability + * + * Revision 1.1 1999/05/18 17:49:20 warmerda + * New + * + */ + +static char rcsid[] = + "$Id: shptreedump.c,v 1.7 2002/04/10 16:59:12 warmerda Exp $"; + +#include "shapefil.h" + +#include +#include +#include + +static void SHPTreeNodeDump( SHPTree *, SHPTreeNode *, const char *, int ); +static void SHPTreeNodeSearchAndDump( SHPTree *, double *, double * ); + +/************************************************************************/ +/* Usage() */ +/************************************************************************/ + +static void Usage() + +{ + printf( "shptreedump [-maxdepth n] [-search xmin ymin xmax ymax]\n" + " [-v] shp_file\n" ); + exit( 1 ); +} + + + +/************************************************************************/ +/* main() */ +/************************************************************************/ +int main( int argc, char ** argv ) + +{ + SHPHandle hSHP; + SHPTree *psTree; + int nExpandShapes = 0; + int nMaxDepth = 0; + int nDoSearch = 0; + double adfSearchMin[4], adfSearchMax[4]; + + +/* -------------------------------------------------------------------- */ +/* Consume flags. */ +/* -------------------------------------------------------------------- */ + while( argc > 1 ) + { + if( strcmp(argv[1],"-v") == 0 ) + { + nExpandShapes = 1; + argv++; + argc--; + } + else if( strcmp(argv[1],"-maxdepth") == 0 && argc > 2 ) + { + nMaxDepth = atoi(argv[2]); + argv += 2; + argc -= 2; + } + else if( strcmp(argv[1],"-search") == 0 && argc > 5 ) + { + nDoSearch = 1; + + adfSearchMin[0] = atof(argv[2]); + adfSearchMin[1] = atof(argv[3]); + adfSearchMax[0] = atof(argv[4]); + adfSearchMax[1] = atof(argv[5]); + + adfSearchMin[2] = adfSearchMax[2] = 0.0; + adfSearchMin[3] = adfSearchMax[3] = 0.0; + + if( adfSearchMin[0] > adfSearchMax[0] + || adfSearchMin[1] > adfSearchMax[1] ) + { + printf( "Min greater than max in search criteria.\n" ); + Usage(); + } + + argv += 5; + argc -= 5; + } + else + break; + } + +/* -------------------------------------------------------------------- */ +/* Display a usage message. */ +/* -------------------------------------------------------------------- */ + if( argc < 2 ) + { + Usage(); + } + +/* -------------------------------------------------------------------- */ +/* Open the passed shapefile. */ +/* -------------------------------------------------------------------- */ + hSHP = SHPOpen( argv[1], "rb" ); + + if( hSHP == NULL ) + { + printf( "Unable to open:%s\n", argv[1] ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Build a quadtree structure for this file. */ +/* -------------------------------------------------------------------- */ + psTree = SHPCreateTree( hSHP, 2, nMaxDepth, NULL, NULL ); + +/* -------------------------------------------------------------------- */ +/* Trim unused nodes from the tree. */ +/* -------------------------------------------------------------------- */ + SHPTreeTrimExtraNodes( psTree ); + +/* -------------------------------------------------------------------- */ +/* Dump tree by recursive descent. */ +/* -------------------------------------------------------------------- */ + if( !nDoSearch ) + SHPTreeNodeDump( psTree, psTree->psRoot, "", nExpandShapes ); + +/* -------------------------------------------------------------------- */ +/* or do a search instead. */ +/* -------------------------------------------------------------------- */ + else + SHPTreeNodeSearchAndDump( psTree, adfSearchMin, adfSearchMax ); + +/* -------------------------------------------------------------------- */ +/* cleanup */ +/* -------------------------------------------------------------------- */ + SHPDestroyTree( psTree ); + + SHPClose( hSHP ); + +#ifdef USE_DBMALLOC + malloc_dump(2); +#endif + + exit( 0 ); +} + +/************************************************************************/ +/* EmitCoordinate() */ +/************************************************************************/ + +static void EmitCoordinate( double * padfCoord, int nDimension ) + +{ + const char *pszFormat; + + if( fabs(padfCoord[0]) < 180 && fabs(padfCoord[1]) < 180 ) + pszFormat = "%.9f"; + else + pszFormat = "%.2f"; + + printf( pszFormat, padfCoord[0] ); + printf( "," ); + printf( pszFormat, padfCoord[1] ); + + if( nDimension > 2 ) + { + printf( "," ); + printf( pszFormat, padfCoord[2] ); + } + if( nDimension > 3 ) + { + printf( "," ); + printf( pszFormat, padfCoord[3] ); + } +} + +/************************************************************************/ +/* EmitShape() */ +/************************************************************************/ + +static void EmitShape( SHPObject * psObject, const char * pszPrefix, + int nDimension ) + +{ + int i; + + printf( "%s( Shape\n", pszPrefix ); + printf( "%s ShapeId = %d\n", pszPrefix, psObject->nShapeId ); + + printf( "%s Min = (", pszPrefix ); + EmitCoordinate( &(psObject->dfXMin), nDimension ); + printf( ")\n" ); + + printf( "%s Max = (", pszPrefix ); + EmitCoordinate( &(psObject->dfXMax), nDimension ); + printf( ")\n" ); + + for( i = 0; i < psObject->nVertices; i++ ) + { + double adfVertex[4]; + + printf( "%s Vertex[%d] = (", pszPrefix, i ); + + adfVertex[0] = psObject->padfX[i]; + adfVertex[1] = psObject->padfY[i]; + adfVertex[2] = psObject->padfZ[i]; + adfVertex[3] = psObject->padfM[i]; + + EmitCoordinate( adfVertex, nDimension ); + printf( ")\n" ); + } + printf( "%s)\n", pszPrefix ); +} + +/************************************************************************/ +/* SHPTreeNodeDump() */ +/* */ +/* Dump a tree node in a readable form. */ +/************************************************************************/ + +static void SHPTreeNodeDump( SHPTree * psTree, + SHPTreeNode * psTreeNode, + const char * pszPrefix, + int nExpandShapes ) + +{ + char szNextPrefix[150]; + int i; + + strcpy( szNextPrefix, pszPrefix ); + if( strlen(pszPrefix) < sizeof(szNextPrefix) - 3 ) + strcat( szNextPrefix, " " ); + + printf( "%s( SHPTreeNode\n", pszPrefix ); + +/* -------------------------------------------------------------------- */ +/* Emit the bounds. */ +/* -------------------------------------------------------------------- */ + printf( "%s Min = (", pszPrefix ); + EmitCoordinate( psTreeNode->adfBoundsMin, psTree->nDimension ); + printf( ")\n" ); + + printf( "%s Max = (", pszPrefix ); + EmitCoordinate( psTreeNode->adfBoundsMax, psTree->nDimension ); + printf( ")\n" ); + +/* -------------------------------------------------------------------- */ +/* Emit the list of shapes on this node. */ +/* -------------------------------------------------------------------- */ + if( nExpandShapes ) + { + printf( "%s Shapes(%d):\n", pszPrefix, psTreeNode->nShapeCount ); + for( i = 0; i < psTreeNode->nShapeCount; i++ ) + { + SHPObject *psObject; + + psObject = SHPReadObject( psTree->hSHP, + psTreeNode->panShapeIds[i] ); + assert( psObject != NULL ); + if( psObject != NULL ) + { + EmitShape( psObject, szNextPrefix, psTree->nDimension ); + } + + SHPDestroyObject( psObject ); + } + } + else + { + printf( "%s Shapes(%d): ", pszPrefix, psTreeNode->nShapeCount ); + for( i = 0; i < psTreeNode->nShapeCount; i++ ) + { + printf( "%d ", psTreeNode->panShapeIds[i] ); + } + printf( "\n" ); + } + +/* -------------------------------------------------------------------- */ +/* Emit subnodes. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( psTreeNode->apsSubNode[i] != NULL ) + SHPTreeNodeDump( psTree, psTreeNode->apsSubNode[i], + szNextPrefix, nExpandShapes ); + } + + printf( "%s)\n", pszPrefix ); + + return; +} + +/************************************************************************/ +/* SHPTreeNodeSearchAndDump() */ +/************************************************************************/ + +static void SHPTreeNodeSearchAndDump( SHPTree * hTree, + double *padfBoundsMin, + double *padfBoundsMax ) + +{ + int *panHits, nShapeCount, i; + +/* -------------------------------------------------------------------- */ +/* Perform the search for likely candidates. These are shapes */ +/* that fall into a tree node whose bounding box intersects our */ +/* area of interest. */ +/* -------------------------------------------------------------------- */ + panHits = SHPTreeFindLikelyShapes( hTree, padfBoundsMin, padfBoundsMax, + &nShapeCount ); + +/* -------------------------------------------------------------------- */ +/* Read all of these shapes, and establish whether the shape's */ +/* bounding box actually intersects the area of interest. Note */ +/* that the bounding box could intersect the area of interest, */ +/* and the shape itself still not cross it but we don't try to */ +/* address that here. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nShapeCount; i++ ) + { + SHPObject *psObject; + + psObject = SHPReadObject( hTree->hSHP, panHits[i] ); + if( psObject == NULL ) + continue; + + if( !SHPCheckBoundsOverlap( padfBoundsMin, padfBoundsMax, + &(psObject->dfXMin), + &(psObject->dfXMax), + hTree->nDimension ) ) + { + printf( "Shape %d: not in area of interest, but fetched.\n", + panHits[i] ); + } + else + { + printf( "Shape %d: appears to be in area of interest.\n", + panHits[i] ); + } + + SHPDestroyObject( psObject ); + } + + if( nShapeCount == 0 ) + printf( "No shapes found in search.\n" ); +} diff --git a/src/Lib/shapelib/shputils.c b/src/Lib/shapelib/shputils.c index 271f4866..2a04f5e3 100644 --- a/src/Lib/shapelib/shputils.c +++ b/src/Lib/shapelib/shputils.c @@ -1,15 +1,70 @@ -/* - * ORGINAL CODE WAS FROM "dbfdump.c", "shpdump.c", and "shpopen.c". - * Frank Warmerdam 1995 +/****************************************************************************** + * $Id: shputils.c,v 1.7 2003/02/25 17:20:22 warmerda Exp $ * - * This code is in the public domain. + * Project: Shapelib + * Purpose: + * Altered "shpdump" and "dbfdump" to allow two files to be appended. + * Other Functions: + * Selecting from the DBF before the write occurs. + * Change the UNITS between Feet and Meters and Shift X,Y. + * Clip and Erase boundary. The program only passes thru the + * data once. + * + * Bill Miller North Carolina - Department of Transporation + * Feb. 1997 -- bmiller@dot.state.nc.us + * There was not a lot of time to debug hidden problems; + * And the code is not very well organized or documented. + * The clip/erase function was not well tested. + * Oct. 2000 -- bmiller@dot.state.nc.us + * Fixed the problem when select is using numbers + * larger than short integer. It now reads long integer. + * NOTE: DBF files created using windows NT will read as a string with + * a length of 381 characters. This is a bug in "dbfopen". + * + * + * Author: Bill Miller (bmiller@dot.state.nc.us) + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** * * $Log: shputils.c,v $ - * Revision 1.1 2000-02-09 19:51:46 curt - * Initial revision + * Revision 1.7 2003/02/25 17:20:22 warmerda + * Set psCShape to NULL after SHPDestroyObject() to avoid multi-frees of + * the same memory ... as submitted by Fred Fox. * - * Revision 1.1 1999/08/24 21:13:01 curt - * Initial revision. + * Revision 1.6 2001/08/28 13:57:14 warmerda + * fixed DBFAddField return value check + * + * Revision 1.5 2000/11/02 13:52:48 warmerda + * major upgrade from Bill Miller + * + * Revision 1.4 1999/11/05 14:12:05 warmerda + * updated license terms * * Revision 1.3 1998/12/03 15:47:39 warmerda * Did a bunch of rewriting to make it work with the V1.2 API. @@ -19,27 +74,10 @@ * * Revision 1.1 1997/05/27 20:40:27 warmerda * Initial revision - * - * - * Altered "shpdump" and "dbfdump" to allow two files to be appended. - * Other Functions: - * Selecting from the DBF before the write occurs. - * Change the UNITS between Feet and Meters and Shift X,Y. - * Clip and Erase boundary. - * - * Bill Miller NC-DOT -- Feb. 1997 -- bmiller@doh.dot.state.nc.us - * There was not a lot of time to debug hidden problems; - * And the code is not very well organized or documented. - * The clip/erase function was not well tested. - * - * PURPOSE: I needed a program to Append, Select, Change Unit, and - * Clip boundaries. The program only passes thru the - * data once. - * */ static char rcsid[] = - "$Id: shputils.c,v 1.1 2000-02-09 19:51:46 curt Exp $"; + "$Id: shputils.c,v 1.7 2003/02/25 17:20:22 warmerda Exp $"; #include "shapefil.h" #include "string.h" @@ -76,6 +114,7 @@ int i, ti, iWidth, iDecimals, iRecord; int j, tj, jWidth, jDecimals, jRecord; int found, newdbf; + void openfiles(void); void setext(char *pt, char *ext); int strncasecmp2(char *s1, char *s2, int n); @@ -84,33 +123,39 @@ void findselect(void); void showitems(void); int selectrec(); int check_theme_bnd(); -int clip(); +int clip_boundary(); void error(); +/* -------------------------------------------------------------------- */ +/* Variables for the DESCRIBE function */ +/* -------------------------------------------------------------------- */ + int ilist = FALSE, iall = FALSE; /* -------------------------------------------------------------------- */ /* Variables for the SELECT function */ /* -------------------------------------------------------------------- */ - char selectitem[40], *cpt; - int selectvalues[150]; - int iselect = FALSE, iselectitem = -1, selcount=0; - int iunselect = FALSE; + int found = FALSE, newdbf = FALSE; + char selectitem[40], *cpt; + long int selectvalues[150], selcount=0; + int iselect = FALSE, iselectitem = -1; + int iunselect = FALSE; /* -------------------------------------------------------------------- */ /* Variables for the CLIP and ERASE functions */ /* -------------------------------------------------------------------- */ double cxmin, cymin, cxmax, cymax; - int iclip = FALSE, ierase = FALSE; + int iclip = FALSE, ierase = FALSE; int itouch = FALSE, iinside = FALSE, icut = FALSE; int ibound = FALSE, ipoly = FALSE; char clipfile[80]; /* -------------------------------------------------------------------- */ -/* Variables for the UNIT function */ +/* Variables for the FACTOR function */ /* -------------------------------------------------------------------- */ - double factor = 1; /* NO FACTOR */ + double infactor,outfactor,factor = 0; /* NO FACTOR */ int iunit = FALSE; - + int ifactor = FALSE; + /* -------------------------------------------------------------------- */ /* Variables for the SHIFT function */ @@ -125,12 +170,15 @@ int main( int argc, char ** argv ) /* -------------------------------------------------------------------- */ if( argc < 2 ) error(); strcpy(infile, argv[1]); - if (argc == 2 ) { + if (argc > 2) { + strcpy(outfile,argv[2]); + if (strncasecmp2(outfile, "LIST",0) == 0) { ilist = TRUE; } + if (strncasecmp2(outfile, "ALL",0) == 0) { iall = TRUE; } + } + if (ilist || iall || argc == 2 ) { setext(infile, "shp"); printf("DESCRIBE: %s\n",infile); strcpy(outfile,""); - } else { - strcpy(outfile,argv[2]); } /* -------------------------------------------------------------------- */ /* Look for other functions on the command line. (SELECT, UNIT) */ @@ -161,7 +209,7 @@ int main( int argc, char ** argv ) selcount++; } iselect=TRUE; - } + } /*** End SEL & UNSEL ***/ else if ((strncasecmp2(argv[i], "CLIP",4) == 0) || (strncasecmp2(argv[i], "ERASE",5) == 0)) @@ -190,12 +238,7 @@ int main( int argc, char ** argv ) printf("Theme Clip Boundary: (%lf,%lf) - (%lf,%lf)\n", cxmin, cymin, cxmax, cymax); ibound=TRUE; - } - else if (strncasecmp2(argv[i], "POLY",4) == 0) - { - ipoly=TRUE; - } - else { /*** xmin,ymin,xmax,ymax ***/ + } else { /*** xmin,ymin,xmax,ymax ***/ sscanf(argv[i],"%lf",&cymin); i++; if (i >= argc) error(); @@ -207,46 +250,50 @@ int main( int argc, char ** argv ) } i++; if (i >= argc) error(); - if (strncasecmp2(argv[i], "CUT",3) == 0) icut=TRUE; - else if (strncasecmp2(argv[i], "TOUCH",5) == 0) itouch=TRUE; - else if (strncasecmp2(argv[i], "INSIDE",6) == 0) iinside=TRUE; + if (strncasecmp2(argv[i], "CUT",3) == 0) icut=TRUE; + else if (strncasecmp2(argv[i], "TOUCH",5) == 0) itouch=TRUE; + else if (strncasecmp2(argv[i], "INSIDE",6) == 0) iinside=TRUE; else error(); iclip=TRUE; - } - else - if (strncasecmp2(argv[i], "UNIT",4) == 0) + } /*** End CLIP & ERASE ***/ + else if (strncasecmp2(argv[i], "FACTOR",0) == 0) { - i++; - if (i >= argc) error(); - if (strncasecmp2(argv[i], "METER",5) == 0) - factor=0.304800609601; - else - { - if (strncasecmp2(argv[i], "FEET",4) == 0) - factor=3.280833; - else - sscanf(argv[i],"%lf",&factor); - } - if (factor == 0) error(); - iunit=TRUE; - printf("Output file coordinate values will be factored by %lg\n",factor); + i++; + if (i >= argc) error(); + infactor=findunit(argv[i]); + if (infactor == 0) error(); + iunit=TRUE; + i++; + if (i >= argc) error(); + outfactor=findunit(argv[i]); + if (outfactor == 0) + { + sscanf(argv[i],"%lf",&factor); + if (factor == 0) error(); + } + if (factor == 0) + { + if (infactor ==0) + { puts("ERROR: Input unit must be defined before output unit"); exit(); } + factor=infactor/outfactor; + } + printf("Output file coordinate values will be factored by %lg\n",factor); + ifactor=(factor != 1); /* True if a valid factor */ + } /*** End FACTOR ***/ + else if (strncasecmp2(argv[i],"SHIFT",5) == 0) + { + i++; + if (i >= argc) error(); + sscanf(argv[i],"%lf",&xshift); + i++; + if (i >= argc) error(); + sscanf(argv[i],"%lf",&yshift); + iunit=TRUE; + printf("X Shift: %lg Y Shift: %lg\n",xshift,yshift); + } /*** End SHIFT ***/ + else { + printf("ERROR: Unknown function %s\n",argv[i]); error(); } - else - if (strncasecmp2(argv[i],"SHIFT",5) == 0) - { - i++; - if (i >= argc) error(); - sscanf(argv[i],"%lf",&xshift); - i++; - if (i >= argc) error(); - sscanf(argv[i],"%lf",&yshift); - iunit=TRUE; - printf("X Shift: %lg Y Shift: %lg\n",xshift,yshift); - } - else - { - printf("ERROR: Unknown function %s\n",argv[i]); error(); - } } /* -------------------------------------------------------------------- */ /* If there is no data in this file let the user know. */ @@ -268,7 +315,7 @@ int main( int argc, char ** argv ) adfBoundsMax[0], adfBoundsMax[1], nEntities, iRecord ); - if (strcmp(outfile,"") == 0) + if (strcmp(outfile,"") == 0) /* Describe the shapefile; No other functions */ { ti = DBFGetFieldCount( hDBF ); showitems(); @@ -317,7 +364,7 @@ int main( int argc, char ** argv ) /* Clip coordinates of shapes if needed. */ /* -------------------------------------------------------------------- */ if (iclip) - if (clip() == 0) goto SKIP_RECORD; /** SKIP RECORD **/ + if (clip_boundary() == 0) goto SKIP_RECORD; /** SKIP RECORD **/ /* -------------------------------------------------------------------- */ /* Read a DBF record and copy each field. */ @@ -351,9 +398,9 @@ int main( int argc, char ** argv ) } jRecord++; /* -------------------------------------------------------------------- */ -/* Change UNIT and SHIFT coordinates of shapes if needed. */ +/* Change FACTOR and SHIFT coordinates of shapes if needed. */ /* -------------------------------------------------------------------- */ - if (iunit) + if (iunit) { for( j = 0; j < psCShape->nVertices; j++ ) { @@ -370,6 +417,7 @@ int main( int argc, char ** argv ) SKIP_RECORD: SHPDestroyObject( psCShape ); + psCShape = NULL; j=0; } @@ -392,6 +440,15 @@ int main( int argc, char ** argv ) SHPClose( hSHPappend ); DBFClose( hDBF ); DBFClose( hDBFappend ); + if (nEntitiesAppend == 0) { + puts("Remove the output files."); + setext(outfile, "dbf"); + remove(outfile); + setext(outfile, "shp"); + remove(outfile); + setext(outfile, "shx"); + remove(outfile); + } return( 0 ); } @@ -512,14 +569,14 @@ void mergefields() for( i = 0; i < ti; i++ ) { iType = DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals ); - found=0; + found=FALSE; { for( j = 0; j < tj; j++ ) /* Search all field names for a match */ { jType = DBFGetFieldInfo( hDBFappend, j, jszTitle, &jWidth, &jDecimals ); if (iType == jType && (strcmp(iszTitle, jszTitle) == 0) ) { - if (found == 1 || newdbf == 1) + if (found || newdbf) { if (i == j) pt[i]=j; printf("Warning: Duplicate field name found (%s)\n",iszTitle); @@ -528,13 +585,13 @@ void mergefields() } else { - pt[i]=j; found=1; + pt[i]=j; found=TRUE; } } } } - if (pt[i] == -1 && found == 0) /* Try to force into an existing field */ + if (pt[i] == -1 && (! found) ) /* Try to force into an existing field */ { /* Ignore the field name, width, and decimal places */ jType = DBFGetFieldInfo( hDBFappend, j, jszTitle, &jWidth, &jDecimals ); if (iType == jType) @@ -542,11 +599,12 @@ void mergefields() pt[i]=i; found=1; } } - if (found == 0 && jRecord == 0) /* Add missing field to the append table */ + if ( (! found) && jRecord == 0) /* Add missing field to the append table */ { /* The output DBF must be is empty */ pt[i]=tj; tj++; - if( !DBFAddField( hDBFappend, iszTitle, iType, iWidth, iDecimals )) + if( DBFAddField( hDBFappend, iszTitle, iType, iWidth, iDecimals ) + == -1 ) { printf( "Warning: DBFAddField(%s, TYPE:%d, WIDTH:%d DEC:%d, ITEM#:%d of %d) failed.\n", iszTitle, iType, iWidth, iDecimals, (i+1), (ti+1) ); @@ -569,7 +627,8 @@ void findselect() if (iselectitem == -1) { printf("Warning: Item not found for selection (%s)\n",selectitem); - iselect = 0; + iselect = FALSE; + iall = FALSE; showitems(); printf("Continued... (Selecting entire file)\n"); } @@ -579,23 +638,95 @@ void findselect() void showitems() { - printf("Available Items: "); +char stmp[40],slow[40],shigh[40]; +double dtmp,dlow,dhigh,dsum,mean; +long int itmp,ilow,ihigh,isum; +long int maxrec; +char *pt; + + printf("Available Items: (%d)",ti); + maxrec = DBFGetRecordCount(hDBF); + if (maxrec > 5000 && ! iall) + { maxrec=5000; printf(" ** ESTIMATED RANGES (MEAN) For more records use \"All\""); } + else { printf(" RANGES (MEAN)"); } + for( i = 0; i < ti; i++ ) { - iType = DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals ); - printf("%s, ",iszTitle); + switch( DBFGetFieldInfo( hDBF, i, iszTitle, &iWidth, &iDecimals ) ) + { + case FTString: + strcpy(slow, "~"); + strcpy(shigh,"\0"); + printf("\n String %3d %-16s",iWidth,iszTitle); + for( iRecord = 0; iRecord < maxrec; iRecord++ ) { + strncpy(stmp,DBFReadStringAttribute( hDBF, iRecord, i ),39); + if (strcmp(stmp,"!!") > 0) { + if (strncasecmp2(stmp,slow,0) < 0) strncpy(slow, stmp,39); + if (strncasecmp2(stmp,shigh,0) > 0) strncpy(shigh,stmp,39); + } + } + pt=slow+strlen(slow)-1; + while(*pt == ' ') { *pt='\0'; pt--; } + pt=shigh+strlen(shigh)-1; + while(*pt == ' ') { *pt='\0'; pt--; } + if (strncasecmp2(slow,shigh,0) < 0) printf("%s to %s",slow,shigh); + else if (strncasecmp2(slow,shigh,0) == 0) printf("= %s",slow); + else printf("No Values"); + break; + case FTInteger: + printf("\n Integer %3d %-16s",iWidth,iszTitle); + ilow = 1999999999; + ihigh= -1999999999; + isum = 0; + for( iRecord = 0; iRecord < maxrec; iRecord++ ) { + itmp = DBFReadIntegerAttribute( hDBF, iRecord, i ); + if (ilow > itmp) ilow = itmp; + if (ihigh < itmp) ihigh = itmp; + isum = isum + itmp; + } + mean=isum/maxrec; + if (ilow < ihigh) printf("%d to %d \t(%.1f)",ilow,ihigh,mean); + else if (ilow == ihigh) printf("= %d",ilow); + else printf("No Values"); + break; + + case FTDouble: + printf("\n Real %3d,%d %-16s",iWidth,iDecimals,iszTitle); + dlow = 999999999999999.0; + dhigh= -999999999999999.0; + dsum = 0; + for( iRecord = 0; iRecord < maxrec; iRecord++ ) { + dtmp = DBFReadDoubleAttribute( hDBF, iRecord, i ); + if (dlow > dtmp) dlow = dtmp; + if (dhigh < dtmp) dhigh = dtmp; + dsum = dsum + dtmp; + } + mean=dsum/maxrec; + sprintf(stmp,"%%.%df to %%.%df \t(%%.%df)",iDecimals,iDecimals,iDecimals); + if (dlow < dhigh) printf(stmp,dlow,dhigh,mean); + else if (dlow == dhigh) { + sprintf(stmp,"= %%.%df",iDecimals); + printf(stmp,dlow); + } + else printf("No Values"); + break; + + } + } - printf("(total=%d)\n",ti); + printf("\n"); } int selectrec() { -int value, ty; +long int value, ty; ty = DBFGetFieldInfo( hDBF, iselectitem, NULL, &iWidth, &iDecimals); switch(ty) { case FTString: + puts("Invalid Item"); + iselect=FALSE; break; case FTInteger: value = DBFReadIntegerAttribute( hDBF, iRecord, iselectitem ); @@ -607,6 +738,8 @@ int value, ty; } break; case FTDouble: + puts("Invalid Item"); + iselect=FALSE; break; } if (iunselect) return(1); /* Skip this record */ @@ -629,164 +762,120 @@ int check_theme_bnd() ( (adfBoundsMin[1] > cymax) && (adfBoundsMax[1] > cymax) ) ) { /** Theme is totally outside clip area **/ if (ierase) iclip=FALSE; /** WRITE THEME (Clip not needed) **/ - else nEntities=0; /** SKIP THEME **/ + else nEntities=0; /** SKIP THEME **/ } if (nEntities == 0) puts("WARNING: Theme is outside the clip area."); /** SKIP THEME **/ } -int clip() +clip_boundary() { - int outside=FALSE; - int j2=0, i2=0; - - if ( (psCShape->dfXMin >= cxmin) && (psCShape->dfXMax <= cxmax) && - (psCShape->dfYMin >= cymin) && (psCShape->dfYMax <= cymax) ) - { /** Feature is totally inside clip area **/ - if (ierase) return(0); /** SKIP RECORD **/ - else return(1); /** WRITE RECORD **/ - } - - if ( ( psCShape->dfXMax < cxmin ) || - ( psCShape->dfYMax < cymin ) || - ( psCShape->dfXMin > cxmax ) || - ( psCShape->dfYMin > cymax ) ) - { /** Feature is totally outside clip area **/ - if (ierase) return(1); /** WRITE RECORD **/ - else return(0); /** SKIP RECORD **/ - } - - if (itouch) - { - if (ierase) return(0); /** SKIP RECORD **/ - else return(1); /** WRITE RECORD **/ - } - - if (iinside) - { - if (ierase) return(1); /** WRITE RECORD **/ - else return(0); /** SKIP RECORD **/ - } - - /*** SECOND check each vertex in the feature ***/ - for( j2 = 0; j2 < psCShape->nVertices; j2++ ) - { - if (psCShape->padfX[j2] < cxmin || psCShape->padfX[j2] > cxmax) - { - outside=TRUE; - } - else - { - if (psCShape->padfY[j2] < cymin || psCShape->padfY[j2] > cymax) - outside=TRUE; - else - outside=FALSE; - } - - - if (icut) - { - if (outside) - { - } else { - if (i2 != j2) - { - /* write vertex */ - psCShape->padfX[i2] = psCShape->padfX[j2]; - psCShape->padfY[i2] = psCShape->padfY[j2]; - } - i2++; - } - } - else - if (outside) /* vertex is outside boundary */ - { - if (iinside) - { - if (ierase) return(1); /** WRITE RECORD **/ - else return(0); /** SKIP RECORD **/ - } - } - else /* vertex is inside boundary */ - { - if (itouch) - { - if (ierase) return(0); /** SKIP RECORD **/ - else return(1); /** WRITE RECORD **/ - } - } - } + int inside; + int prev_outside; + int i2; + int j2; - if (icut) - { - j2 = psCShape->nVertices; - if (i2 < 2) return(0); /** SKIP RECORD **/ - - psCShape->nVertices = i2; + /*** FIRST check the boundary of the feature ***/ + if ( ( (psCShape->dfXMin < cxmin) && (psCShape->dfXMax < cxmin) ) || + ( (psCShape->dfYMin < cymin) && (psCShape->dfYMax < cymin) ) || + ( (psCShape->dfXMin > cxmax) && (psCShape->dfXMax > cxmax) ) || + ( (psCShape->dfYMin > cymax) && (psCShape->dfYMax > cymax) ) ) + { /** Feature is totally outside clip area **/ + if (ierase) return(1); /** WRITE RECORD **/ + else return(0); /** SKIP RECORD **/ + } + + if ( (psCShape->dfXMin >= cxmin) && (psCShape->dfXMax <= cxmax) && + (psCShape->dfYMin >= cymin) && (psCShape->dfYMax <= cymax) ) + { /** Feature is totally inside clip area **/ + if (ierase) return(0); /** SKIP RECORD **/ + else return(1); /** WRITE RECORD **/ + } + + if (iinside) + { /** INSIDE * Feature might touch the boundary or could be outside **/ + if (ierase) return(1); /** WRITE RECORD **/ + else return(0); /** SKIP RECORD **/ + } + + if (itouch) + { /** TOUCH **/ + if ( ( (psCShape->dfXMin <= cxmin) || (psCShape->dfXMax >= cxmax) ) && + (psCShape->dfYMin >= cymin) && (psCShape->dfYMax <= cymax) ) + { /** Feature intersects the clip boundary only on the X axis **/ + if (ierase) return(0); /** SKIP RECORD **/ + else return(1); /** WRITE RECORD **/ + } + if ( (psCShape->dfXMin >= cxmin) && (psCShape->dfXMax <= cxmax) && + ( (psCShape->dfYMin <= cymin) || (psCShape->dfYMax >= cymax) ) ) + { /** Feature intersects the clip boundary only on the Y axis **/ + if (ierase) return(0); /** SKIP RECORD **/ + else return(1); /** WRITE RECORD **/ + } + + for( j2 = 0; j2 < psCShape->nVertices; j2++ ) + { /** At least one vertex must be inside the clip boundary **/ + if ( (psCShape->padfX[j2] >= cxmin && psCShape->padfX[j2] <= cxmax) || + (psCShape->padfY[j2] >= cymin && psCShape->padfY[j2] <= cymax) ) + if (ierase) return(0); /** SKIP RECORD **/ + else return(1); /** WRITE RECORD **/ + } + + /** All vertices are outside the clip boundary **/ + if (ierase) return(1); /** WRITE RECORD **/ + else return(0); /** SKIP RECORD **/ + } /** End TOUCH **/ + + if (icut) + { /** CUT **/ + /*** Check each vertex in the feature with the Boundary and "CUT" ***/ + /*** THIS CODE WAS NOT COMPLETED! READ NOTE AT THE BOTTOM ***/ + i2=0; + prev_outside=FALSE; + for( j2 = 0; j2 < psCShape->nVertices; j2++ ) + { + inside = psCShape->padfX[j2] >= cxmin && psCShape->padfX[j2] <= cxmax && + psCShape->padfY[j2] >= cymin && psCShape->padfY[j2] <= cymax ; + + if (ierase) inside=(! inside); + if (inside) + { + if (i2 != j2) + { + if (prev_outside) + { + /*** AddIntersection(i2); /*** Add intersection ***/ + prev_outside=FALSE; + } + psCShape->padfX[i2]=psCShape->padfX[j2]; /** move vertex **/ + psCShape->padfY[i2]=psCShape->padfY[j2]; + } + i2++; + } else { + if ( (! prev_outside) && (j2 > 0) ) + { + /*** AddIntersection(i2); /*** Add intersection (Watch out for j2==i2-1) ***/ + /*** Also a polygon may overlap twice and will split into a several parts ***/ + prev_outside=TRUE; + } + } + } + printf("Vertices:%d OUT:%d Number of Parts:%d\n", - j2, psCShape->nVertices, psCShape->nParts ); - } - if (itouch) - { - if (ierase) return(1); /** WRITE RECORD **/ - else return(0); /** SKIP RECORD **/ - } - if (iinside) - { - if (ierase) return(0); /** SKIP RECORD **/ - else return(1); /** WRITE RECORD **/ - } + psCShape->nVertices,i2, psCShape->nParts ); + + psCShape->nVertices = i2; + + if (i2 < 2) return(0); /** SKIP RECORD **/ + /*** (WE ARE NOT CREATING INTERESECTIONS and some lines could be reduced to one point) **/ + + if (i2 == 0) return(0); /** SKIP RECORD **/ + else return(1); /** WRITE RECORD **/ + } /** End CUT **/ } -/* -------------------------------------------------------------------- */ -/* Display a usage message. */ -/* -------------------------------------------------------------------- */ -void error() - { - puts( "USAGE: shputils "); - puts( "USAGE: shputils " ); - puts( " {