GRASS Programmer's Manual
6.4.2(2012)
|
00001 /****************************************************************************** 00002 * $Id: shpopen.c 32581 2008-08-06 19:30:45Z neteler $ 00003 * 00004 * Project: Shapelib 00005 * Purpose: Implementation of core Shapefile read/write functions. 00006 * Author: Frank Warmerdam, warmerdam@pobox.com 00007 * 00008 ****************************************************************************** 00009 * Copyright (c) 1999, 2001, Frank Warmerdam 00010 * 00011 * This software is available under the following "MIT Style" license, 00012 * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This 00013 * option is discussed in more detail in shapelib.html. 00014 * 00015 * -- 00016 * 00017 * Permission is hereby granted, free of charge, to any person obtaining a 00018 * copy of this software and associated documentation files (the "Software"), 00019 * to deal in the Software without restriction, including without limitation 00020 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00021 * and/or sell copies of the Software, and to permit persons to whom the 00022 * Software is furnished to do so, subject to the following conditions: 00023 * 00024 * The above copyright notice and this permission notice shall be included 00025 * in all copies or substantial portions of the Software. 00026 * 00027 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00028 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00029 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 00030 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00031 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00032 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00033 * DEALINGS IN THE SOFTWARE. 00034 ****************************************************************************** 00035 * 00036 * $Log: shpopen.c,v $ 00037 * Revision 1.59 2008/03/14 05:25:31 fwarmerdam 00038 * Correct crash on buggy geometries (gdal #2218) 00039 * 00040 * Revision 1.58 2008/01/08 23:28:26 bram 00041 * on line 2095, use a float instead of a double to avoid a compiler warning 00042 * 00043 * Revision 1.57 2007/12/06 07:00:25 fwarmerdam 00044 * dbfopen now using SAHooks for fileio 00045 * 00046 * Revision 1.56 2007/12/04 20:37:56 fwarmerdam 00047 * preliminary implementation of hooks api for io and errors 00048 * 00049 * Revision 1.55 2007/11/21 22:39:56 fwarmerdam 00050 * close shx file in readonly mode (GDAL #1956) 00051 * 00052 * Revision 1.54 2007/11/15 00:12:47 mloskot 00053 * Backported recent changes from GDAL (Ticket #1415) to Shapelib. 00054 * 00055 * Revision 1.53 2007/11/14 22:31:08 fwarmerdam 00056 * checks after mallocs to detect for corrupted/voluntary broken shapefiles. 00057 * http://trac.osgeo.org/gdal/ticket/1991 00058 * 00059 * Revision 1.52 2007/06/21 15:58:33 fwarmerdam 00060 * fix for SHPRewindObject when rings touch at one vertex (gdal #976) 00061 * 00062 * Revision 1.51 2006/09/04 15:24:01 fwarmerdam 00063 * Fixed up log message for 1.49. 00064 * 00065 * Revision 1.50 2006/09/04 15:21:39 fwarmerdam 00066 * fix of last fix 00067 * 00068 * Revision 1.49 2006/09/04 15:21:00 fwarmerdam 00069 * MLoskot: Added stronger test of Shapefile reading failures, e.g. truncated 00070 * files. The problem was discovered by Tim Sutton and reported here 00071 * https://svn.qgis.org/trac/ticket/200 00072 * 00073 * Revision 1.48 2006/01/26 15:07:32 fwarmerdam 00074 * add bMeasureIsUsed flag from Craig Bruce: Bug 1249 00075 * 00076 * Revision 1.47 2006/01/04 20:07:23 fwarmerdam 00077 * In SHPWriteObject() make sure that the record length is updated 00078 * when rewriting an existing record. 00079 * 00080 * Revision 1.46 2005/02/11 17:17:46 fwarmerdam 00081 * added panPartStart[0] validation 00082 * 00083 * Revision 1.45 2004/09/26 20:09:48 fwarmerdam 00084 * const correctness changes 00085 * 00086 * Revision 1.44 2003/12/29 00:18:39 fwarmerdam 00087 * added error checking for failed IO and optional CPL error reporting 00088 * 00089 * Revision 1.43 2003/12/01 16:20:08 warmerda 00090 * be careful of zero vertex shapes 00091 * 00092 * Revision 1.42 2003/12/01 14:58:27 warmerda 00093 * added degenerate object check in SHPRewindObject() 00094 * 00095 * Revision 1.41 2003/07/08 15:22:43 warmerda 00096 * avoid warning 00097 * 00098 * Revision 1.40 2003/04/21 18:30:37 warmerda 00099 * added header write/update public methods 00100 * 00101 * Revision 1.39 2002/08/26 06:46:56 warmerda 00102 * avoid c++ comments 00103 * 00104 * Revision 1.38 2002/05/07 16:43:39 warmerda 00105 * Removed debugging printf. 00106 * 00107 * Revision 1.37 2002/04/10 17:35:22 warmerda 00108 * fixed bug in ring reversal code 00109 * 00110 * Revision 1.36 2002/04/10 16:59:54 warmerda 00111 * added SHPRewindObject 00112 * 00113 * Revision 1.35 2001/12/07 15:10:44 warmerda 00114 * fix if .shx fails to open 00115 * 00116 * Revision 1.34 2001/11/01 16:29:55 warmerda 00117 * move pabyRec into SHPInfo for thread safety 00118 * 00119 * Revision 1.33 2001/07/03 12:18:15 warmerda 00120 * Improved cleanup if SHX not found, provied by Riccardo Cohen. 00121 * 00122 * Revision 1.32 2001/06/22 01:58:07 warmerda 00123 * be more careful about establishing initial bounds in face of NULL shapes 00124 * 00125 * Revision 1.31 2001/05/31 19:35:29 warmerda 00126 * added support for writing null shapes 00127 * 00128 * Revision 1.30 2001/05/28 12:46:29 warmerda 00129 * Add some checking on reasonableness of record count when opening. 00130 * 00131 * Revision 1.29 2001/05/23 13:36:52 warmerda 00132 * added use of SHPAPI_CALL 00133 * 00134 * Revision 1.28 2001/02/06 22:25:06 warmerda 00135 * fixed memory leaks when SHPOpen() fails 00136 * 00137 * Revision 1.27 2000/07/18 15:21:33 warmerda 00138 * added better enforcement of -1 for append in SHPWriteObject 00139 * 00140 * Revision 1.26 2000/02/16 16:03:51 warmerda 00141 * added null shape support 00142 * 00143 * Revision 1.25 1999/12/15 13:47:07 warmerda 00144 * Fixed record size settings in .shp file (was 4 words too long) 00145 * Added stdlib.h. 00146 * 00147 * Revision 1.24 1999/11/05 14:12:04 warmerda 00148 * updated license terms 00149 * 00150 * Revision 1.23 1999/07/27 00:53:46 warmerda 00151 * added support for rewriting shapes 00152 * 00153 * Revision 1.22 1999/06/11 19:19:11 warmerda 00154 * Cleanup pabyRec static buffer on SHPClose(). 00155 * 00156 * Revision 1.21 1999/06/02 14:57:56 kshih 00157 * Remove unused variables 00158 * 00159 * Revision 1.20 1999/04/19 21:04:17 warmerda 00160 * Fixed syntax error. 00161 * 00162 * Revision 1.19 1999/04/19 21:01:57 warmerda 00163 * Force access string to binary in SHPOpen(). 00164 * 00165 * Revision 1.18 1999/04/01 18:48:07 warmerda 00166 * Try upper case extensions if lower case doesn't work. 00167 * 00168 * Revision 1.17 1998/12/31 15:29:39 warmerda 00169 * Disable writing measure values to multipatch objects if 00170 * DISABLE_MULTIPATCH_MEASURE is defined. 00171 * 00172 * Revision 1.16 1998/12/16 05:14:33 warmerda 00173 * Added support to write MULTIPATCH. Fixed reading Z coordinate of 00174 * MULTIPATCH. Fixed record size written for all feature types. 00175 * 00176 * Revision 1.15 1998/12/03 16:35:29 warmerda 00177 * r+b is proper binary access string, not rb+. 00178 * 00179 * Revision 1.14 1998/12/03 15:47:56 warmerda 00180 * Fixed setting of nVertices in SHPCreateObject(). 00181 * 00182 * Revision 1.13 1998/12/03 15:33:54 warmerda 00183 * Made SHPCalculateExtents() separately callable. 00184 * 00185 * Revision 1.12 1998/11/11 20:01:50 warmerda 00186 * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines. 00187 * 00188 * Revision 1.11 1998/11/09 20:56:44 warmerda 00189 * Fixed up handling of file wide bounds. 00190 * 00191 * Revision 1.10 1998/11/09 20:18:51 warmerda 00192 * Converted to support 3D shapefiles, and use of SHPObject. 00193 * 00194 * Revision 1.9 1998/02/24 15:09:05 warmerda 00195 * Fixed memory leak. 00196 * 00197 * Revision 1.8 1997/12/04 15:40:29 warmerda 00198 * Fixed byte swapping of record number, and record length fields in the 00199 * .shp file. 00200 * 00201 * Revision 1.7 1995/10/21 03:15:58 warmerda 00202 * Added support for binary file access, the magic cookie 9997 00203 * and tried to improve the int32 selection logic for 16bit systems. 00204 * 00205 * Revision 1.6 1995/09/04 04:19:41 warmerda 00206 * Added fix for file bounds. 00207 * 00208 * Revision 1.5 1995/08/25 15:16:44 warmerda 00209 * Fixed a couple of problems with big endian systems ... one with bounds 00210 * and the other with multipart polygons. 00211 * 00212 * Revision 1.4 1995/08/24 18:10:17 warmerda 00213 * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc() 00214 * functions (such as on the Sun). 00215 * 00216 * Revision 1.3 1995/08/23 02:23:15 warmerda 00217 * Added support for reading bounds, and fixed up problems in setting the 00218 * file wide bounds. 00219 * 00220 * Revision 1.2 1995/08/04 03:16:57 warmerda 00221 * Added header. 00222 * 00223 */ 00224 00225 #include <grass/shapefil.h> 00226 00227 #include <math.h> 00228 #include <limits.h> 00229 #include <assert.h> 00230 #include <stdlib.h> 00231 #include <string.h> 00232 #include <stdio.h> 00233 00234 SHP_CVSID("$Id: shpopen.c 32581 2008-08-06 19:30:45Z neteler $") 00235 00236 typedef unsigned char uchar; 00237 00238 #if UINT_MAX == 65535 00239 typedef long int32; 00240 #else 00241 typedef int int32; 00242 #endif 00243 00244 #ifndef FALSE 00245 # define FALSE 0 00246 # define TRUE 1 00247 #endif 00248 00249 #define ByteCopy( a, b, c ) memcpy( b, a, c ) 00250 #ifndef MAX 00251 # define MIN(a,b) ((a<b) ? a : b) 00252 # define MAX(a,b) ((a>b) ? a : b) 00253 #endif 00254 00255 static int bBigEndian; 00256 00257 00258 /************************************************************************/ 00259 /* SwapWord() */ 00260 /* */ 00261 /* Swap a 2, 4 or 8 byte word. */ 00262 /************************************************************************/ 00263 00264 static void SwapWord( int length, void * wordP ) 00265 00266 { 00267 int i; 00268 uchar temp; 00269 00270 for( i=0; i < length/2; i++ ) 00271 { 00272 temp = ((uchar *) wordP)[i]; 00273 ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1]; 00274 ((uchar *) wordP)[length-i-1] = temp; 00275 } 00276 } 00277 00278 /************************************************************************/ 00279 /* SfRealloc() */ 00280 /* */ 00281 /* A realloc cover function that will access a NULL pointer as */ 00282 /* a valid input. */ 00283 /************************************************************************/ 00284 00285 static void * SfRealloc( void * pMem, int nNewSize ) 00286 00287 { 00288 if( pMem == NULL ) 00289 return( (void *) malloc(nNewSize) ); 00290 else 00291 return( (void *) realloc(pMem,nNewSize) ); 00292 } 00293 00294 /************************************************************************/ 00295 /* SHPWriteHeader() */ 00296 /* */ 00297 /* Write out a header for the .shp and .shx files as well as the */ 00298 /* contents of the index (.shx) file. */ 00299 /************************************************************************/ 00300 00301 void SHPWriteHeader( SHPHandle psSHP ) 00302 00303 { 00304 uchar abyHeader[100]; 00305 int i; 00306 int32 i32; 00307 double dValue; 00308 int32 *panSHX; 00309 00310 if (psSHP->fpSHX == NULL) 00311 { 00312 psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed"); 00313 return; 00314 } 00315 00316 /* -------------------------------------------------------------------- */ 00317 /* Prepare header block for .shp file. */ 00318 /* -------------------------------------------------------------------- */ 00319 for( i = 0; i < 100; i++ ) 00320 abyHeader[i] = 0; 00321 00322 abyHeader[2] = 0x27; /* magic cookie */ 00323 abyHeader[3] = 0x0a; 00324 00325 i32 = psSHP->nFileSize/2; /* file size */ 00326 ByteCopy( &i32, abyHeader+24, 4 ); 00327 if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); 00328 00329 i32 = 1000; /* version */ 00330 ByteCopy( &i32, abyHeader+28, 4 ); 00331 if( bBigEndian ) SwapWord( 4, abyHeader+28 ); 00332 00333 i32 = psSHP->nShapeType; /* shape type */ 00334 ByteCopy( &i32, abyHeader+32, 4 ); 00335 if( bBigEndian ) SwapWord( 4, abyHeader+32 ); 00336 00337 dValue = psSHP->adBoundsMin[0]; /* set bounds */ 00338 ByteCopy( &dValue, abyHeader+36, 8 ); 00339 if( bBigEndian ) SwapWord( 8, abyHeader+36 ); 00340 00341 dValue = psSHP->adBoundsMin[1]; 00342 ByteCopy( &dValue, abyHeader+44, 8 ); 00343 if( bBigEndian ) SwapWord( 8, abyHeader+44 ); 00344 00345 dValue = psSHP->adBoundsMax[0]; 00346 ByteCopy( &dValue, abyHeader+52, 8 ); 00347 if( bBigEndian ) SwapWord( 8, abyHeader+52 ); 00348 00349 dValue = psSHP->adBoundsMax[1]; 00350 ByteCopy( &dValue, abyHeader+60, 8 ); 00351 if( bBigEndian ) SwapWord( 8, abyHeader+60 ); 00352 00353 dValue = psSHP->adBoundsMin[2]; /* z */ 00354 ByteCopy( &dValue, abyHeader+68, 8 ); 00355 if( bBigEndian ) SwapWord( 8, abyHeader+68 ); 00356 00357 dValue = psSHP->adBoundsMax[2]; 00358 ByteCopy( &dValue, abyHeader+76, 8 ); 00359 if( bBigEndian ) SwapWord( 8, abyHeader+76 ); 00360 00361 dValue = psSHP->adBoundsMin[3]; /* m */ 00362 ByteCopy( &dValue, abyHeader+84, 8 ); 00363 if( bBigEndian ) SwapWord( 8, abyHeader+84 ); 00364 00365 dValue = psSHP->adBoundsMax[3]; 00366 ByteCopy( &dValue, abyHeader+92, 8 ); 00367 if( bBigEndian ) SwapWord( 8, abyHeader+92 ); 00368 00369 /* -------------------------------------------------------------------- */ 00370 /* Write .shp file header. */ 00371 /* -------------------------------------------------------------------- */ 00372 if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0 00373 || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHP ) != 1 ) 00374 { 00375 psSHP->sHooks.Error( "Failure writing .shp header" ); 00376 return; 00377 } 00378 00379 /* -------------------------------------------------------------------- */ 00380 /* Prepare, and write .shx file header. */ 00381 /* -------------------------------------------------------------------- */ 00382 i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */ 00383 ByteCopy( &i32, abyHeader+24, 4 ); 00384 if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); 00385 00386 if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0 00387 || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 ) 00388 { 00389 psSHP->sHooks.Error( "Failure writing .shx header" ); 00390 return; 00391 } 00392 00393 /* -------------------------------------------------------------------- */ 00394 /* Write out the .shx contents. */ 00395 /* -------------------------------------------------------------------- */ 00396 panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords); 00397 00398 for( i = 0; i < psSHP->nRecords; i++ ) 00399 { 00400 panSHX[i*2 ] = psSHP->panRecOffset[i]/2; 00401 panSHX[i*2+1] = psSHP->panRecSize[i]/2; 00402 if( !bBigEndian ) SwapWord( 4, panSHX+i*2 ); 00403 if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 ); 00404 } 00405 00406 if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX ) 00407 != psSHP->nRecords ) 00408 { 00409 psSHP->sHooks.Error( "Failure writing .shx contents" ); 00410 } 00411 00412 free( panSHX ); 00413 00414 /* -------------------------------------------------------------------- */ 00415 /* Flush to disk. */ 00416 /* -------------------------------------------------------------------- */ 00417 psSHP->sHooks.FFlush( psSHP->fpSHP ); 00418 psSHP->sHooks.FFlush( psSHP->fpSHX ); 00419 } 00420 00421 /************************************************************************/ 00422 /* SHPOpen() */ 00423 /************************************************************************/ 00424 00425 SHPHandle SHPAPI_CALL 00426 SHPOpen( const char * pszLayer, const char * pszAccess ) 00427 00428 { 00429 SAHooks sHooks; 00430 00431 SASetupDefaultHooks( &sHooks ); 00432 00433 return SHPOpenLL( pszLayer, pszAccess, &sHooks ); 00434 } 00435 00436 /************************************************************************/ 00437 /* SHPOpen() */ 00438 /* */ 00439 /* Open the .shp and .shx files based on the basename of the */ 00440 /* files or either file name. */ 00441 /************************************************************************/ 00442 00443 SHPHandle SHPAPI_CALL 00444 SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) 00445 00446 { 00447 char *pszFullname, *pszBasename; 00448 SHPHandle psSHP; 00449 00450 uchar *pabyBuf; 00451 int i; 00452 double dValue; 00453 00454 /* -------------------------------------------------------------------- */ 00455 /* Ensure the access string is one of the legal ones. We */ 00456 /* ensure the result string indicates binary to avoid common */ 00457 /* problems on Windows. */ 00458 /* -------------------------------------------------------------------- */ 00459 if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 00460 || strcmp(pszAccess,"r+") == 0 ) 00461 pszAccess = "r+b"; 00462 else 00463 pszAccess = "rb"; 00464 00465 /* -------------------------------------------------------------------- */ 00466 /* Establish the byte order on this machine. */ 00467 /* -------------------------------------------------------------------- */ 00468 i = 1; 00469 if( *((uchar *) &i) == 1 ) 00470 bBigEndian = FALSE; 00471 else 00472 bBigEndian = TRUE; 00473 00474 /* -------------------------------------------------------------------- */ 00475 /* Initialize the info structure. */ 00476 /* -------------------------------------------------------------------- */ 00477 psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1); 00478 00479 psSHP->bUpdated = FALSE; 00480 memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) ); 00481 00482 /* -------------------------------------------------------------------- */ 00483 /* Compute the base (layer) name. If there is any extension */ 00484 /* on the passed in filename we will strip it off. */ 00485 /* -------------------------------------------------------------------- */ 00486 pszBasename = (char *) malloc(strlen(pszLayer)+5); 00487 strcpy( pszBasename, pszLayer ); 00488 for( i = strlen(pszBasename)-1; 00489 i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' 00490 && pszBasename[i] != '\\'; 00491 i-- ) {} 00492 00493 if( pszBasename[i] == '.' ) 00494 pszBasename[i] = '\0'; 00495 00496 /* -------------------------------------------------------------------- */ 00497 /* Open the .shp and .shx files. Note that files pulled from */ 00498 /* a PC to Unix with upper case filenames won't work! */ 00499 /* -------------------------------------------------------------------- */ 00500 pszFullname = (char *) malloc(strlen(pszBasename) + 5); 00501 sprintf( pszFullname, "%s.shp", pszBasename ) ; 00502 psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); 00503 if( psSHP->fpSHP == NULL ) 00504 { 00505 sprintf( pszFullname, "%s.SHP", pszBasename ); 00506 psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); 00507 } 00508 00509 if( psSHP->fpSHP == NULL ) 00510 { 00511 #ifdef USE_CPL 00512 CPLError( CE_Failure, CPLE_OpenFailed, 00513 "Unable to open %s.shp or %s.SHP.", 00514 pszBasename, pszBasename ); 00515 #endif 00516 free( psSHP ); 00517 free( pszBasename ); 00518 free( pszFullname ); 00519 return( NULL ); 00520 } 00521 00522 sprintf( pszFullname, "%s.shx", pszBasename ); 00523 psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); 00524 if( psSHP->fpSHX == NULL ) 00525 { 00526 sprintf( pszFullname, "%s.SHX", pszBasename ); 00527 psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); 00528 } 00529 00530 if( psSHP->fpSHX == NULL ) 00531 { 00532 #ifdef USE_CPL 00533 CPLError( CE_Failure, CPLE_OpenFailed, 00534 "Unable to open %s.shx or %s.SHX.", 00535 pszBasename, pszBasename ); 00536 #endif 00537 psSHP->sHooks.FClose( psSHP->fpSHP ); 00538 free( psSHP ); 00539 free( pszBasename ); 00540 free( pszFullname ); 00541 return( NULL ); 00542 } 00543 00544 free( pszFullname ); 00545 free( pszBasename ); 00546 00547 /* -------------------------------------------------------------------- */ 00548 /* Read the file size from the SHP file. */ 00549 /* -------------------------------------------------------------------- */ 00550 pabyBuf = (uchar *) malloc(100); 00551 psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHP ); 00552 00553 psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256 00554 + pabyBuf[25] * 256 * 256 00555 + pabyBuf[26] * 256 00556 + pabyBuf[27]) * 2; 00557 00558 /* -------------------------------------------------------------------- */ 00559 /* Read SHX file Header info */ 00560 /* -------------------------------------------------------------------- */ 00561 if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1 00562 || pabyBuf[0] != 0 00563 || pabyBuf[1] != 0 00564 || pabyBuf[2] != 0x27 00565 || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) ) 00566 { 00567 psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." ); 00568 psSHP->sHooks.FClose( psSHP->fpSHP ); 00569 psSHP->sHooks.FClose( psSHP->fpSHX ); 00570 free( psSHP ); 00571 00572 return( NULL ); 00573 } 00574 00575 psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256 00576 + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256; 00577 psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8; 00578 00579 psSHP->nShapeType = pabyBuf[32]; 00580 00581 if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 ) 00582 { 00583 char szError[200]; 00584 00585 sprintf( szError, 00586 "Record count in .shp header is %d, which seems\n" 00587 "unreasonable. Assuming header is corrupt.", 00588 psSHP->nRecords ); 00589 psSHP->sHooks.Error( szError ); 00590 psSHP->sHooks.FClose( psSHP->fpSHP ); 00591 psSHP->sHooks.FClose( psSHP->fpSHX ); 00592 free( psSHP ); 00593 free(pabyBuf); 00594 00595 return( NULL ); 00596 } 00597 00598 /* -------------------------------------------------------------------- */ 00599 /* Read the bounds. */ 00600 /* -------------------------------------------------------------------- */ 00601 if( bBigEndian ) SwapWord( 8, pabyBuf+36 ); 00602 memcpy( &dValue, pabyBuf+36, 8 ); 00603 psSHP->adBoundsMin[0] = dValue; 00604 00605 if( bBigEndian ) SwapWord( 8, pabyBuf+44 ); 00606 memcpy( &dValue, pabyBuf+44, 8 ); 00607 psSHP->adBoundsMin[1] = dValue; 00608 00609 if( bBigEndian ) SwapWord( 8, pabyBuf+52 ); 00610 memcpy( &dValue, pabyBuf+52, 8 ); 00611 psSHP->adBoundsMax[0] = dValue; 00612 00613 if( bBigEndian ) SwapWord( 8, pabyBuf+60 ); 00614 memcpy( &dValue, pabyBuf+60, 8 ); 00615 psSHP->adBoundsMax[1] = dValue; 00616 00617 if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */ 00618 memcpy( &dValue, pabyBuf+68, 8 ); 00619 psSHP->adBoundsMin[2] = dValue; 00620 00621 if( bBigEndian ) SwapWord( 8, pabyBuf+76 ); 00622 memcpy( &dValue, pabyBuf+76, 8 ); 00623 psSHP->adBoundsMax[2] = dValue; 00624 00625 if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */ 00626 memcpy( &dValue, pabyBuf+84, 8 ); 00627 psSHP->adBoundsMin[3] = dValue; 00628 00629 if( bBigEndian ) SwapWord( 8, pabyBuf+92 ); 00630 memcpy( &dValue, pabyBuf+92, 8 ); 00631 psSHP->adBoundsMax[3] = dValue; 00632 00633 free( pabyBuf ); 00634 00635 /* -------------------------------------------------------------------- */ 00636 /* Read the .shx file to get the offsets to each record in */ 00637 /* the .shp file. */ 00638 /* -------------------------------------------------------------------- */ 00639 psSHP->nMaxRecords = psSHP->nRecords; 00640 00641 psSHP->panRecOffset = 00642 (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); 00643 psSHP->panRecSize = 00644 (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); 00645 pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) ); 00646 00647 if (psSHP->panRecOffset == NULL || 00648 psSHP->panRecSize == NULL || 00649 pabyBuf == NULL) 00650 { 00651 char szError[200]; 00652 00653 sprintf(szError, 00654 "Not enough memory to allocate requested memory (nRecords=%d).\n" 00655 "Probably broken SHP file", 00656 psSHP->nRecords ); 00657 psSHP->sHooks.Error( szError ); 00658 psSHP->sHooks.FClose( psSHP->fpSHP ); 00659 psSHP->sHooks.FClose( psSHP->fpSHX ); 00660 if (psSHP->panRecOffset) free( psSHP->panRecOffset ); 00661 if (psSHP->panRecSize) free( psSHP->panRecSize ); 00662 if (pabyBuf) free( pabyBuf ); 00663 free( psSHP ); 00664 return( NULL ); 00665 } 00666 00667 if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ) 00668 != psSHP->nRecords ) 00669 { 00670 char szError[200]; 00671 00672 sprintf( szError, 00673 "Failed to read all values for %d records in .shx file.", 00674 psSHP->nRecords ); 00675 psSHP->sHooks.Error( szError ); 00676 00677 /* SHX is short or unreadable for some reason. */ 00678 psSHP->sHooks.FClose( psSHP->fpSHP ); 00679 psSHP->sHooks.FClose( psSHP->fpSHX ); 00680 free( psSHP->panRecOffset ); 00681 free( psSHP->panRecSize ); 00682 free( pabyBuf ); 00683 free( psSHP ); 00684 00685 return( NULL ); 00686 } 00687 00688 /* In read-only mode, we can close the SHX now */ 00689 if (strcmp(pszAccess, "rb") == 0) 00690 { 00691 psSHP->sHooks.FClose( psSHP->fpSHX ); 00692 psSHP->fpSHX = NULL; 00693 } 00694 00695 for( i = 0; i < psSHP->nRecords; i++ ) 00696 { 00697 int32 nOffset, nLength; 00698 00699 memcpy( &nOffset, pabyBuf + i * 8, 4 ); 00700 if( !bBigEndian ) SwapWord( 4, &nOffset ); 00701 00702 memcpy( &nLength, pabyBuf + i * 8 + 4, 4 ); 00703 if( !bBigEndian ) SwapWord( 4, &nLength ); 00704 00705 psSHP->panRecOffset[i] = nOffset*2; 00706 psSHP->panRecSize[i] = nLength*2; 00707 } 00708 free( pabyBuf ); 00709 00710 return( psSHP ); 00711 } 00712 00713 /************************************************************************/ 00714 /* SHPClose() */ 00715 /* */ 00716 /* Close the .shp and .shx files. */ 00717 /************************************************************************/ 00718 00719 void SHPAPI_CALL 00720 SHPClose(SHPHandle psSHP ) 00721 00722 { 00723 if( psSHP == NULL ) 00724 return; 00725 00726 /* -------------------------------------------------------------------- */ 00727 /* Update the header if we have modified anything. */ 00728 /* -------------------------------------------------------------------- */ 00729 if( psSHP->bUpdated ) 00730 SHPWriteHeader( psSHP ); 00731 00732 /* -------------------------------------------------------------------- */ 00733 /* Free all resources, and close files. */ 00734 /* -------------------------------------------------------------------- */ 00735 free( psSHP->panRecOffset ); 00736 free( psSHP->panRecSize ); 00737 00738 if ( psSHP->fpSHX != NULL) 00739 psSHP->sHooks.FClose( psSHP->fpSHX ); 00740 psSHP->sHooks.FClose( psSHP->fpSHP ); 00741 00742 if( psSHP->pabyRec != NULL ) 00743 { 00744 free( psSHP->pabyRec ); 00745 } 00746 00747 free( psSHP ); 00748 } 00749 00750 /************************************************************************/ 00751 /* SHPGetInfo() */ 00752 /* */ 00753 /* Fetch general information about the shape file. */ 00754 /************************************************************************/ 00755 00756 void SHPAPI_CALL 00757 SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, 00758 double * padfMinBound, double * padfMaxBound ) 00759 00760 { 00761 int i; 00762 00763 if( psSHP == NULL ) 00764 return; 00765 00766 if( pnEntities != NULL ) 00767 *pnEntities = psSHP->nRecords; 00768 00769 if( pnShapeType != NULL ) 00770 *pnShapeType = psSHP->nShapeType; 00771 00772 for( i = 0; i < 4; i++ ) 00773 { 00774 if( padfMinBound != NULL ) 00775 padfMinBound[i] = psSHP->adBoundsMin[i]; 00776 if( padfMaxBound != NULL ) 00777 padfMaxBound[i] = psSHP->adBoundsMax[i]; 00778 } 00779 } 00780 00781 /************************************************************************/ 00782 /* SHPCreate() */ 00783 /* */ 00784 /* Create a new shape file and return a handle to the open */ 00785 /* shape file with read/write access. */ 00786 /************************************************************************/ 00787 00788 SHPHandle SHPAPI_CALL 00789 SHPCreate( const char * pszLayer, int nShapeType ) 00790 00791 { 00792 SAHooks sHooks; 00793 00794 SASetupDefaultHooks( &sHooks ); 00795 00796 return SHPCreateLL( pszLayer, nShapeType, &sHooks ); 00797 } 00798 00799 /************************************************************************/ 00800 /* SHPCreate() */ 00801 /* */ 00802 /* Create a new shape file and return a handle to the open */ 00803 /* shape file with read/write access. */ 00804 /************************************************************************/ 00805 00806 SHPHandle SHPAPI_CALL 00807 SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) 00808 00809 { 00810 char *pszBasename, *pszFullname; 00811 int i; 00812 SAFile fpSHP, fpSHX; 00813 uchar abyHeader[100]; 00814 int32 i32; 00815 double dValue; 00816 00817 /* -------------------------------------------------------------------- */ 00818 /* Establish the byte order on this system. */ 00819 /* -------------------------------------------------------------------- */ 00820 i = 1; 00821 if( *((uchar *) &i) == 1 ) 00822 bBigEndian = FALSE; 00823 else 00824 bBigEndian = TRUE; 00825 00826 /* -------------------------------------------------------------------- */ 00827 /* Compute the base (layer) name. If there is any extension */ 00828 /* on the passed in filename we will strip it off. */ 00829 /* -------------------------------------------------------------------- */ 00830 pszBasename = (char *) malloc(strlen(pszLayer)+5); 00831 strcpy( pszBasename, pszLayer ); 00832 for( i = strlen(pszBasename)-1; 00833 i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' 00834 && pszBasename[i] != '\\'; 00835 i-- ) {} 00836 00837 if( pszBasename[i] == '.' ) 00838 pszBasename[i] = '\0'; 00839 00840 /* -------------------------------------------------------------------- */ 00841 /* Open the two files so we can write their headers. */ 00842 /* -------------------------------------------------------------------- */ 00843 pszFullname = (char *) malloc(strlen(pszBasename) + 5); 00844 sprintf( pszFullname, "%s.shp", pszBasename ); 00845 fpSHP = psHooks->FOpen(pszFullname, "wb" ); 00846 if( fpSHP == NULL ) 00847 { 00848 psHooks->Error( "Failed to create file .shp file." ); 00849 return( NULL ); 00850 } 00851 00852 sprintf( pszFullname, "%s.shx", pszBasename ); 00853 fpSHX = psHooks->FOpen(pszFullname, "wb" ); 00854 if( fpSHX == NULL ) 00855 { 00856 psHooks->Error( "Failed to create file .shx file." ); 00857 return( NULL ); 00858 } 00859 00860 free( pszFullname ); 00861 free( pszBasename ); 00862 00863 /* -------------------------------------------------------------------- */ 00864 /* Prepare header block for .shp file. */ 00865 /* -------------------------------------------------------------------- */ 00866 for( i = 0; i < 100; i++ ) 00867 abyHeader[i] = 0; 00868 00869 abyHeader[2] = 0x27; /* magic cookie */ 00870 abyHeader[3] = 0x0a; 00871 00872 i32 = 50; /* file size */ 00873 ByteCopy( &i32, abyHeader+24, 4 ); 00874 if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); 00875 00876 i32 = 1000; /* version */ 00877 ByteCopy( &i32, abyHeader+28, 4 ); 00878 if( bBigEndian ) SwapWord( 4, abyHeader+28 ); 00879 00880 i32 = nShapeType; /* shape type */ 00881 ByteCopy( &i32, abyHeader+32, 4 ); 00882 if( bBigEndian ) SwapWord( 4, abyHeader+32 ); 00883 00884 dValue = 0.0; /* set bounds */ 00885 ByteCopy( &dValue, abyHeader+36, 8 ); 00886 ByteCopy( &dValue, abyHeader+44, 8 ); 00887 ByteCopy( &dValue, abyHeader+52, 8 ); 00888 ByteCopy( &dValue, abyHeader+60, 8 ); 00889 00890 /* -------------------------------------------------------------------- */ 00891 /* Write .shp file header. */ 00892 /* -------------------------------------------------------------------- */ 00893 if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 ) 00894 { 00895 psHooks->Error( "Failed to write .shp header." ); 00896 return NULL; 00897 } 00898 00899 /* -------------------------------------------------------------------- */ 00900 /* Prepare, and write .shx file header. */ 00901 /* -------------------------------------------------------------------- */ 00902 i32 = 50; /* file size */ 00903 ByteCopy( &i32, abyHeader+24, 4 ); 00904 if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); 00905 00906 if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 ) 00907 { 00908 psHooks->Error( "Failed to write .shx header." ); 00909 return NULL; 00910 } 00911 00912 /* -------------------------------------------------------------------- */ 00913 /* Close the files, and then open them as regular existing files. */ 00914 /* -------------------------------------------------------------------- */ 00915 psHooks->FClose( fpSHP ); 00916 psHooks->FClose( fpSHX ); 00917 00918 return( SHPOpenLL( pszLayer, "r+b", psHooks ) ); 00919 } 00920 00921 /************************************************************************/ 00922 /* _SHPSetBounds() */ 00923 /* */ 00924 /* Compute a bounds rectangle for a shape, and set it into the */ 00925 /* indicated location in the record. */ 00926 /************************************************************************/ 00927 00928 static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape ) 00929 00930 { 00931 ByteCopy( &(psShape->dfXMin), pabyRec + 0, 8 ); 00932 ByteCopy( &(psShape->dfYMin), pabyRec + 8, 8 ); 00933 ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 ); 00934 ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 ); 00935 00936 if( bBigEndian ) 00937 { 00938 SwapWord( 8, pabyRec + 0 ); 00939 SwapWord( 8, pabyRec + 8 ); 00940 SwapWord( 8, pabyRec + 16 ); 00941 SwapWord( 8, pabyRec + 24 ); 00942 } 00943 } 00944 00945 /************************************************************************/ 00946 /* SHPComputeExtents() */ 00947 /* */ 00948 /* Recompute the extents of a shape. Automatically done by */ 00949 /* SHPCreateObject(). */ 00950 /************************************************************************/ 00951 00952 void SHPAPI_CALL 00953 SHPComputeExtents( SHPObject * psObject ) 00954 00955 { 00956 int i; 00957 00958 /* -------------------------------------------------------------------- */ 00959 /* Build extents for this object. */ 00960 /* -------------------------------------------------------------------- */ 00961 if( psObject->nVertices > 0 ) 00962 { 00963 psObject->dfXMin = psObject->dfXMax = psObject->padfX[0]; 00964 psObject->dfYMin = psObject->dfYMax = psObject->padfY[0]; 00965 psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0]; 00966 psObject->dfMMin = psObject->dfMMax = psObject->padfM[0]; 00967 } 00968 00969 for( i = 0; i < psObject->nVertices; i++ ) 00970 { 00971 psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]); 00972 psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]); 00973 psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]); 00974 psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]); 00975 00976 psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]); 00977 psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]); 00978 psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]); 00979 psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]); 00980 } 00981 } 00982 00983 /************************************************************************/ 00984 /* SHPCreateObject() */ 00985 /* */ 00986 /* Create a shape object. It should be freed with */ 00987 /* SHPDestroyObject(). */ 00988 /************************************************************************/ 00989 00990 SHPObject SHPAPI_CALL1(*) 00991 SHPCreateObject( int nSHPType, int nShapeId, int nParts, 00992 const int * panPartStart, const int * panPartType, 00993 int nVertices, const double *padfX, const double *padfY, 00994 const double * padfZ, const double * padfM ) 00995 00996 { 00997 SHPObject *psObject; 00998 int i, bHasM, bHasZ; 00999 01000 psObject = (SHPObject *) calloc(1,sizeof(SHPObject)); 01001 psObject->nSHPType = nSHPType; 01002 psObject->nShapeId = nShapeId; 01003 psObject->bMeasureIsUsed = FALSE; 01004 01005 /* -------------------------------------------------------------------- */ 01006 /* Establish whether this shape type has M, and Z values. */ 01007 /* -------------------------------------------------------------------- */ 01008 if( nSHPType == SHPT_ARCM 01009 || nSHPType == SHPT_POINTM 01010 || nSHPType == SHPT_POLYGONM 01011 || nSHPType == SHPT_MULTIPOINTM ) 01012 { 01013 bHasM = TRUE; 01014 bHasZ = FALSE; 01015 } 01016 else if( nSHPType == SHPT_ARCZ 01017 || nSHPType == SHPT_POINTZ 01018 || nSHPType == SHPT_POLYGONZ 01019 || nSHPType == SHPT_MULTIPOINTZ 01020 || nSHPType == SHPT_MULTIPATCH ) 01021 { 01022 bHasM = TRUE; 01023 bHasZ = TRUE; 01024 } 01025 else 01026 { 01027 bHasM = FALSE; 01028 bHasZ = FALSE; 01029 } 01030 01031 /* -------------------------------------------------------------------- */ 01032 /* Capture parts. Note that part type is optional, and */ 01033 /* defaults to ring. */ 01034 /* -------------------------------------------------------------------- */ 01035 if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON 01036 || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM 01037 || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ 01038 || nSHPType == SHPT_MULTIPATCH ) 01039 { 01040 psObject->nParts = MAX(1,nParts); 01041 01042 psObject->panPartStart = (int *) 01043 malloc(sizeof(int) * psObject->nParts); 01044 psObject->panPartType = (int *) 01045 malloc(sizeof(int) * psObject->nParts); 01046 01047 psObject->panPartStart[0] = 0; 01048 psObject->panPartType[0] = SHPP_RING; 01049 01050 for( i = 0; i < nParts; i++ ) 01051 { 01052 psObject->panPartStart[i] = panPartStart[i]; 01053 01054 if( panPartType != NULL ) 01055 psObject->panPartType[i] = panPartType[i]; 01056 else 01057 psObject->panPartType[i] = SHPP_RING; 01058 } 01059 01060 if( psObject->panPartStart[0] != 0 ) 01061 psObject->panPartStart[0] = 0; 01062 } 01063 01064 /* -------------------------------------------------------------------- */ 01065 /* Capture vertices. Note that Z and M are optional, but X and */ 01066 /* Y are not. */ 01067 /* -------------------------------------------------------------------- */ 01068 if( nVertices > 0 ) 01069 { 01070 psObject->padfX = (double *) calloc(sizeof(double),nVertices); 01071 psObject->padfY = (double *) calloc(sizeof(double),nVertices); 01072 psObject->padfZ = (double *) calloc(sizeof(double),nVertices); 01073 psObject->padfM = (double *) calloc(sizeof(double),nVertices); 01074 01075 assert( padfX != NULL ); 01076 assert( padfY != NULL ); 01077 01078 for( i = 0; i < nVertices; i++ ) 01079 { 01080 psObject->padfX[i] = padfX[i]; 01081 psObject->padfY[i] = padfY[i]; 01082 if( padfZ != NULL && bHasZ ) 01083 psObject->padfZ[i] = padfZ[i]; 01084 if( padfM != NULL && bHasM ) 01085 psObject->padfM[i] = padfM[i]; 01086 } 01087 if( padfM != NULL && bHasM ) 01088 psObject->bMeasureIsUsed = TRUE; 01089 } 01090 01091 /* -------------------------------------------------------------------- */ 01092 /* Compute the extents. */ 01093 /* -------------------------------------------------------------------- */ 01094 psObject->nVertices = nVertices; 01095 SHPComputeExtents( psObject ); 01096 01097 return( psObject ); 01098 } 01099 01100 /************************************************************************/ 01101 /* SHPCreateSimpleObject() */ 01102 /* */ 01103 /* Create a simple (common) shape object. Destroy with */ 01104 /* SHPDestroyObject(). */ 01105 /************************************************************************/ 01106 01107 SHPObject SHPAPI_CALL1(*) 01108 SHPCreateSimpleObject( int nSHPType, int nVertices, 01109 const double * padfX, const double * padfY, 01110 const double * padfZ ) 01111 01112 { 01113 return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL, 01114 nVertices, padfX, padfY, padfZ, NULL ) ); 01115 } 01116 01117 /************************************************************************/ 01118 /* SHPWriteObject() */ 01119 /* */ 01120 /* Write out the vertices of a new structure. Note that it is */ 01121 /* only possible to write vertices at the end of the file. */ 01122 /************************************************************************/ 01123 01124 int SHPAPI_CALL 01125 SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) 01126 01127 { 01128 int nRecordOffset, i, nRecordSize=0; 01129 uchar *pabyRec; 01130 int32 i32; 01131 01132 psSHP->bUpdated = TRUE; 01133 01134 /* -------------------------------------------------------------------- */ 01135 /* Ensure that shape object matches the type of the file it is */ 01136 /* being written to. */ 01137 /* -------------------------------------------------------------------- */ 01138 assert( psObject->nSHPType == psSHP->nShapeType 01139 || psObject->nSHPType == SHPT_NULL ); 01140 01141 /* -------------------------------------------------------------------- */ 01142 /* Ensure that -1 is used for appends. Either blow an */ 01143 /* assertion, or if they are disabled, set the shapeid to -1 */ 01144 /* for appends. */ 01145 /* -------------------------------------------------------------------- */ 01146 assert( nShapeId == -1 01147 || (nShapeId >= 0 && nShapeId < psSHP->nRecords) ); 01148 01149 if( nShapeId != -1 && nShapeId >= psSHP->nRecords ) 01150 nShapeId = -1; 01151 01152 /* -------------------------------------------------------------------- */ 01153 /* Add the new entity to the in memory index. */ 01154 /* -------------------------------------------------------------------- */ 01155 if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords ) 01156 { 01157 psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100); 01158 01159 psSHP->panRecOffset = (int *) 01160 SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords ); 01161 psSHP->panRecSize = (int *) 01162 SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords ); 01163 } 01164 01165 /* -------------------------------------------------------------------- */ 01166 /* Initialize record. */ 01167 /* -------------------------------------------------------------------- */ 01168 pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) 01169 + psObject->nParts * 8 + 128); 01170 01171 /* -------------------------------------------------------------------- */ 01172 /* Extract vertices for a Polygon or Arc. */ 01173 /* -------------------------------------------------------------------- */ 01174 if( psObject->nSHPType == SHPT_POLYGON 01175 || psObject->nSHPType == SHPT_POLYGONZ 01176 || psObject->nSHPType == SHPT_POLYGONM 01177 || psObject->nSHPType == SHPT_ARC 01178 || psObject->nSHPType == SHPT_ARCZ 01179 || psObject->nSHPType == SHPT_ARCM 01180 || psObject->nSHPType == SHPT_MULTIPATCH ) 01181 { 01182 int32 nPoints, nParts; 01183 int i; 01184 01185 nPoints = psObject->nVertices; 01186 nParts = psObject->nParts; 01187 01188 _SHPSetBounds( pabyRec + 12, psObject ); 01189 01190 if( bBigEndian ) SwapWord( 4, &nPoints ); 01191 if( bBigEndian ) SwapWord( 4, &nParts ); 01192 01193 ByteCopy( &nPoints, pabyRec + 40 + 8, 4 ); 01194 ByteCopy( &nParts, pabyRec + 36 + 8, 4 ); 01195 01196 nRecordSize = 52; 01197 01198 /* 01199 * Write part start positions. 01200 */ 01201 ByteCopy( psObject->panPartStart, pabyRec + 44 + 8, 01202 4 * psObject->nParts ); 01203 for( i = 0; i < psObject->nParts; i++ ) 01204 { 01205 if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i ); 01206 nRecordSize += 4; 01207 } 01208 01209 /* 01210 * Write multipatch part types if needed. 01211 */ 01212 if( psObject->nSHPType == SHPT_MULTIPATCH ) 01213 { 01214 memcpy( pabyRec + nRecordSize, psObject->panPartType, 01215 4*psObject->nParts ); 01216 for( i = 0; i < psObject->nParts; i++ ) 01217 { 01218 if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize ); 01219 nRecordSize += 4; 01220 } 01221 } 01222 01223 /* 01224 * Write the (x,y) vertex values. 01225 */ 01226 for( i = 0; i < psObject->nVertices; i++ ) 01227 { 01228 ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 ); 01229 ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 ); 01230 01231 if( bBigEndian ) 01232 SwapWord( 8, pabyRec + nRecordSize ); 01233 01234 if( bBigEndian ) 01235 SwapWord( 8, pabyRec + nRecordSize + 8 ); 01236 01237 nRecordSize += 2 * 8; 01238 } 01239 01240 /* 01241 * Write the Z coordinates (if any). 01242 */ 01243 if( psObject->nSHPType == SHPT_POLYGONZ 01244 || psObject->nSHPType == SHPT_ARCZ 01245 || psObject->nSHPType == SHPT_MULTIPATCH ) 01246 { 01247 ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); 01248 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01249 nRecordSize += 8; 01250 01251 ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); 01252 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01253 nRecordSize += 8; 01254 01255 for( i = 0; i < psObject->nVertices; i++ ) 01256 { 01257 ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); 01258 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01259 nRecordSize += 8; 01260 } 01261 } 01262 01263 /* 01264 * Write the M values, if any. 01265 */ 01266 if( psObject->bMeasureIsUsed 01267 && (psObject->nSHPType == SHPT_POLYGONM 01268 || psObject->nSHPType == SHPT_ARCM 01269 #ifndef DISABLE_MULTIPATCH_MEASURE 01270 || psObject->nSHPType == SHPT_MULTIPATCH 01271 #endif 01272 || psObject->nSHPType == SHPT_POLYGONZ 01273 || psObject->nSHPType == SHPT_ARCZ) ) 01274 { 01275 ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); 01276 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01277 nRecordSize += 8; 01278 01279 ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); 01280 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01281 nRecordSize += 8; 01282 01283 for( i = 0; i < psObject->nVertices; i++ ) 01284 { 01285 ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); 01286 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01287 nRecordSize += 8; 01288 } 01289 } 01290 } 01291 01292 /* -------------------------------------------------------------------- */ 01293 /* Extract vertices for a MultiPoint. */ 01294 /* -------------------------------------------------------------------- */ 01295 else if( psObject->nSHPType == SHPT_MULTIPOINT 01296 || psObject->nSHPType == SHPT_MULTIPOINTZ 01297 || psObject->nSHPType == SHPT_MULTIPOINTM ) 01298 { 01299 int32 nPoints; 01300 int i; 01301 01302 nPoints = psObject->nVertices; 01303 01304 _SHPSetBounds( pabyRec + 12, psObject ); 01305 01306 if( bBigEndian ) SwapWord( 4, &nPoints ); 01307 ByteCopy( &nPoints, pabyRec + 44, 4 ); 01308 01309 for( i = 0; i < psObject->nVertices; i++ ) 01310 { 01311 ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 ); 01312 ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 ); 01313 01314 if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 ); 01315 if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 ); 01316 } 01317 01318 nRecordSize = 48 + 16 * psObject->nVertices; 01319 01320 if( psObject->nSHPType == SHPT_MULTIPOINTZ ) 01321 { 01322 ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); 01323 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01324 nRecordSize += 8; 01325 01326 ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); 01327 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01328 nRecordSize += 8; 01329 01330 for( i = 0; i < psObject->nVertices; i++ ) 01331 { 01332 ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); 01333 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01334 nRecordSize += 8; 01335 } 01336 } 01337 01338 if( psObject->bMeasureIsUsed 01339 && (psObject->nSHPType == SHPT_MULTIPOINTZ 01340 || psObject->nSHPType == SHPT_MULTIPOINTM) ) 01341 { 01342 ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); 01343 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01344 nRecordSize += 8; 01345 01346 ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); 01347 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01348 nRecordSize += 8; 01349 01350 for( i = 0; i < psObject->nVertices; i++ ) 01351 { 01352 ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); 01353 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01354 nRecordSize += 8; 01355 } 01356 } 01357 } 01358 01359 /* -------------------------------------------------------------------- */ 01360 /* Write point. */ 01361 /* -------------------------------------------------------------------- */ 01362 else if( psObject->nSHPType == SHPT_POINT 01363 || psObject->nSHPType == SHPT_POINTZ 01364 || psObject->nSHPType == SHPT_POINTM ) 01365 { 01366 ByteCopy( psObject->padfX, pabyRec + 12, 8 ); 01367 ByteCopy( psObject->padfY, pabyRec + 20, 8 ); 01368 01369 if( bBigEndian ) SwapWord( 8, pabyRec + 12 ); 01370 if( bBigEndian ) SwapWord( 8, pabyRec + 20 ); 01371 01372 nRecordSize = 28; 01373 01374 if( psObject->nSHPType == SHPT_POINTZ ) 01375 { 01376 ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 ); 01377 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01378 nRecordSize += 8; 01379 } 01380 01381 if( psObject->bMeasureIsUsed 01382 && (psObject->nSHPType == SHPT_POINTZ 01383 || psObject->nSHPType == SHPT_POINTM) ) 01384 { 01385 ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 ); 01386 if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); 01387 nRecordSize += 8; 01388 } 01389 } 01390 01391 /* -------------------------------------------------------------------- */ 01392 /* Not much to do for null geometries. */ 01393 /* -------------------------------------------------------------------- */ 01394 else if( psObject->nSHPType == SHPT_NULL ) 01395 { 01396 nRecordSize = 12; 01397 } 01398 01399 else 01400 { 01401 /* unknown type */ 01402 assert( FALSE ); 01403 } 01404 01405 /* -------------------------------------------------------------------- */ 01406 /* Establish where we are going to put this record. If we are */ 01407 /* rewriting and existing record, and it will fit, then put it */ 01408 /* back where the original came from. Otherwise write at the end. */ 01409 /* -------------------------------------------------------------------- */ 01410 if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 ) 01411 { 01412 if( nShapeId == -1 ) 01413 nShapeId = psSHP->nRecords++; 01414 01415 psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize; 01416 psSHP->panRecSize[nShapeId] = nRecordSize-8; 01417 psSHP->nFileSize += nRecordSize; 01418 } 01419 else 01420 { 01421 nRecordOffset = psSHP->panRecOffset[nShapeId]; 01422 psSHP->panRecSize[nShapeId] = nRecordSize-8; 01423 } 01424 01425 /* -------------------------------------------------------------------- */ 01426 /* Set the shape type, record number, and record size. */ 01427 /* -------------------------------------------------------------------- */ 01428 i32 = nShapeId+1; /* record # */ 01429 if( !bBigEndian ) SwapWord( 4, &i32 ); 01430 ByteCopy( &i32, pabyRec, 4 ); 01431 01432 i32 = (nRecordSize-8)/2; /* record size */ 01433 if( !bBigEndian ) SwapWord( 4, &i32 ); 01434 ByteCopy( &i32, pabyRec + 4, 4 ); 01435 01436 i32 = psObject->nSHPType; /* shape type */ 01437 if( bBigEndian ) SwapWord( 4, &i32 ); 01438 ByteCopy( &i32, pabyRec + 8, 4 ); 01439 01440 /* -------------------------------------------------------------------- */ 01441 /* Write out record. */ 01442 /* -------------------------------------------------------------------- */ 01443 if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 01444 || psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 ) 01445 { 01446 psSHP->sHooks.Error( "Error in psSHP->sHooks.FSeek() or fwrite() writing object to .shp file." ); 01447 free( pabyRec ); 01448 return -1; 01449 } 01450 01451 free( pabyRec ); 01452 01453 /* -------------------------------------------------------------------- */ 01454 /* Expand file wide bounds based on this shape. */ 01455 /* -------------------------------------------------------------------- */ 01456 if( psSHP->adBoundsMin[0] == 0.0 01457 && psSHP->adBoundsMax[0] == 0.0 01458 && psSHP->adBoundsMin[1] == 0.0 01459 && psSHP->adBoundsMax[1] == 0.0 ) 01460 { 01461 if( psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0 ) 01462 { 01463 psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0; 01464 psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0; 01465 psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0; 01466 psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0; 01467 } 01468 else 01469 { 01470 psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0]; 01471 psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0]; 01472 psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0]; 01473 psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0]; 01474 } 01475 } 01476 01477 for( i = 0; i < psObject->nVertices; i++ ) 01478 { 01479 psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]); 01480 psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]); 01481 psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]); 01482 psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]); 01483 psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]); 01484 psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]); 01485 psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]); 01486 psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); 01487 } 01488 01489 return( nShapeId ); 01490 } 01491 01492 /************************************************************************/ 01493 /* SHPReadObject() */ 01494 /* */ 01495 /* Read the vertices, parts, and other non-attribute information */ 01496 /* for one shape. */ 01497 /************************************************************************/ 01498 01499 SHPObject SHPAPI_CALL1(*) 01500 SHPReadObject( SHPHandle psSHP, int hEntity ) 01501 01502 { 01503 int nEntitySize, nRequiredSize; 01504 SHPObject *psShape; 01505 char pszErrorMsg[128]; 01506 01507 /* -------------------------------------------------------------------- */ 01508 /* Validate the record/entity number. */ 01509 /* -------------------------------------------------------------------- */ 01510 if( hEntity < 0 || hEntity >= psSHP->nRecords ) 01511 return( NULL ); 01512 01513 /* -------------------------------------------------------------------- */ 01514 /* Ensure our record buffer is large enough. */ 01515 /* -------------------------------------------------------------------- */ 01516 nEntitySize = psSHP->panRecSize[hEntity]+8; 01517 if( nEntitySize > psSHP->nBufSize ) 01518 { 01519 psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize); 01520 if (psSHP->pabyRec == NULL) 01521 { 01522 char szError[200]; 01523 01524 /* Reallocate previous successfull size for following features */ 01525 psSHP->pabyRec = malloc(psSHP->nBufSize); 01526 01527 sprintf( szError, 01528 "Not enough memory to allocate requested memory (nBufSize=%d). " 01529 "Probably broken SHP file", psSHP->nBufSize ); 01530 psSHP->sHooks.Error( szError ); 01531 return NULL; 01532 } 01533 01534 /* Only set new buffer size after successfull alloc */ 01535 psSHP->nBufSize = nEntitySize; 01536 } 01537 01538 /* In case we were not able to reallocate the buffer on a previous step */ 01539 if (psSHP->pabyRec == NULL) 01540 { 01541 return NULL; 01542 } 01543 01544 /* -------------------------------------------------------------------- */ 01545 /* Read the record. */ 01546 /* -------------------------------------------------------------------- */ 01547 if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0 01548 || psSHP->sHooks.FRead( psSHP->pabyRec, nEntitySize, 1, 01549 psSHP->fpSHP ) != 1 ) 01550 { 01551 /* 01552 * TODO - mloskot: Consider detailed diagnostics of shape file, 01553 * for example to detect if file is truncated. 01554 */ 01555 01556 psSHP->sHooks.Error( "Error in fseek() or fread() reading object from .shp file." ); 01557 return NULL; 01558 } 01559 01560 /* -------------------------------------------------------------------- */ 01561 /* Allocate and minimally initialize the object. */ 01562 /* -------------------------------------------------------------------- */ 01563 psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); 01564 psShape->nShapeId = hEntity; 01565 psShape->bMeasureIsUsed = FALSE; 01566 01567 if ( 8 + 4 > nEntitySize ) 01568 { 01569 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", 01570 hEntity, nEntitySize); 01571 psSHP->sHooks.Error( pszErrorMsg ); 01572 SHPDestroyObject(psShape); 01573 return NULL; 01574 } 01575 memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 ); 01576 01577 if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) ); 01578 01579 /* ==================================================================== */ 01580 /* Extract vertices for a Polygon or Arc. */ 01581 /* ==================================================================== */ 01582 if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC 01583 || psShape->nSHPType == SHPT_POLYGONZ 01584 || psShape->nSHPType == SHPT_POLYGONM 01585 || psShape->nSHPType == SHPT_ARCZ 01586 || psShape->nSHPType == SHPT_ARCM 01587 || psShape->nSHPType == SHPT_MULTIPATCH ) 01588 { 01589 int32 nPoints, nParts; 01590 int i, nOffset; 01591 01592 if ( 40 + 8 + 4 > nEntitySize ) 01593 { 01594 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", 01595 hEntity, nEntitySize); 01596 psSHP->sHooks.Error( pszErrorMsg ); 01597 SHPDestroyObject(psShape); 01598 return NULL; 01599 } 01600 /* -------------------------------------------------------------------- */ 01601 /* Get the X/Y bounds. */ 01602 /* -------------------------------------------------------------------- */ 01603 memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); 01604 memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); 01605 memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); 01606 memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); 01607 01608 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); 01609 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); 01610 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); 01611 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); 01612 01613 /* -------------------------------------------------------------------- */ 01614 /* Extract part/point count, and build vertex and part arrays */ 01615 /* to proper size. */ 01616 /* -------------------------------------------------------------------- */ 01617 memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 ); 01618 memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 ); 01619 01620 if( bBigEndian ) SwapWord( 4, &nPoints ); 01621 if( bBigEndian ) SwapWord( 4, &nParts ); 01622 01623 if (nPoints < 0 || nParts < 0 || 01624 nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000) 01625 { 01626 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.", 01627 hEntity, nPoints, nParts); 01628 psSHP->sHooks.Error( pszErrorMsg ); 01629 SHPDestroyObject(psShape); 01630 return NULL; 01631 } 01632 01633 /* With the previous checks on nPoints and nParts, */ 01634 /* we should not overflow here and after */ 01635 /* since 50 M * (16 + 8 + 8) = 1 600 MB */ 01636 nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints; 01637 if ( psShape->nSHPType == SHPT_POLYGONZ 01638 || psShape->nSHPType == SHPT_ARCZ 01639 || psShape->nSHPType == SHPT_MULTIPATCH ) 01640 { 01641 nRequiredSize += 16 + 8 * nPoints; 01642 } 01643 if( psShape->nSHPType == SHPT_MULTIPATCH ) 01644 { 01645 nRequiredSize += 4 * nParts; 01646 } 01647 if (nRequiredSize > nEntitySize) 01648 { 01649 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d, nEntitySize=%d.", 01650 hEntity, nPoints, nParts, nEntitySize); 01651 psSHP->sHooks.Error( pszErrorMsg ); 01652 SHPDestroyObject(psShape); 01653 return NULL; 01654 } 01655 01656 psShape->nVertices = nPoints; 01657 psShape->padfX = (double *) calloc(nPoints,sizeof(double)); 01658 psShape->padfY = (double *) calloc(nPoints,sizeof(double)); 01659 psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); 01660 psShape->padfM = (double *) calloc(nPoints,sizeof(double)); 01661 01662 psShape->nParts = nParts; 01663 psShape->panPartStart = (int *) calloc(nParts,sizeof(int)); 01664 psShape->panPartType = (int *) calloc(nParts,sizeof(int)); 01665 01666 if (psShape->padfX == NULL || 01667 psShape->padfY == NULL || 01668 psShape->padfZ == NULL || 01669 psShape->padfM == NULL || 01670 psShape->panPartStart == NULL || 01671 psShape->panPartType == NULL) 01672 { 01673 snprintf(pszErrorMsg, 128, 01674 "Not enough memory to allocate requested memory (nPoints=%d, nParts=%d) for shape %d. " 01675 "Probably broken SHP file", hEntity, nPoints, nParts ); 01676 psSHP->sHooks.Error( pszErrorMsg ); 01677 SHPDestroyObject(psShape); 01678 return NULL; 01679 } 01680 01681 for( i = 0; i < nParts; i++ ) 01682 psShape->panPartType[i] = SHPP_RING; 01683 01684 /* -------------------------------------------------------------------- */ 01685 /* Copy out the part array from the record. */ 01686 /* -------------------------------------------------------------------- */ 01687 memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts ); 01688 for( i = 0; i < nParts; i++ ) 01689 { 01690 if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i ); 01691 01692 /* We check that the offset is inside the vertex array */ 01693 if (psShape->panPartStart[i] < 0 || 01694 psShape->panPartStart[i] >= psShape->nVertices) 01695 { 01696 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d", 01697 hEntity, i, psShape->panPartStart[i], psShape->nVertices); 01698 psSHP->sHooks.Error( pszErrorMsg ); 01699 SHPDestroyObject(psShape); 01700 return NULL; 01701 } 01702 if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1]) 01703 { 01704 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d", 01705 hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]); 01706 psSHP->sHooks.Error( pszErrorMsg ); 01707 SHPDestroyObject(psShape); 01708 return NULL; 01709 } 01710 } 01711 01712 nOffset = 44 + 8 + 4*nParts; 01713 01714 /* -------------------------------------------------------------------- */ 01715 /* If this is a multipatch, we will also have parts types. */ 01716 /* -------------------------------------------------------------------- */ 01717 if( psShape->nSHPType == SHPT_MULTIPATCH ) 01718 { 01719 memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts ); 01720 for( i = 0; i < nParts; i++ ) 01721 { 01722 if( bBigEndian ) SwapWord( 4, psShape->panPartType+i ); 01723 } 01724 01725 nOffset += 4*nParts; 01726 } 01727 01728 /* -------------------------------------------------------------------- */ 01729 /* Copy out the vertices from the record. */ 01730 /* -------------------------------------------------------------------- */ 01731 for( i = 0; i < nPoints; i++ ) 01732 { 01733 memcpy(psShape->padfX + i, 01734 psSHP->pabyRec + nOffset + i * 16, 01735 8 ); 01736 01737 memcpy(psShape->padfY + i, 01738 psSHP->pabyRec + nOffset + i * 16 + 8, 01739 8 ); 01740 01741 if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); 01742 if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); 01743 } 01744 01745 nOffset += 16*nPoints; 01746 01747 /* -------------------------------------------------------------------- */ 01748 /* If we have a Z coordinate, collect that now. */ 01749 /* -------------------------------------------------------------------- */ 01750 if( psShape->nSHPType == SHPT_POLYGONZ 01751 || psShape->nSHPType == SHPT_ARCZ 01752 || psShape->nSHPType == SHPT_MULTIPATCH ) 01753 { 01754 memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); 01755 memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); 01756 01757 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); 01758 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); 01759 01760 for( i = 0; i < nPoints; i++ ) 01761 { 01762 memcpy( psShape->padfZ + i, 01763 psSHP->pabyRec + nOffset + 16 + i*8, 8 ); 01764 if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); 01765 } 01766 01767 nOffset += 16 + 8*nPoints; 01768 } 01769 01770 /* -------------------------------------------------------------------- */ 01771 /* If we have a M measure value, then read it now. We assume */ 01772 /* that the measure can be present for any shape if the size is */ 01773 /* big enough, but really it will only occur for the Z shapes */ 01774 /* (options), and the M shapes. */ 01775 /* -------------------------------------------------------------------- */ 01776 if( nEntitySize >= nOffset + 16 + 8*nPoints ) 01777 { 01778 memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); 01779 memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); 01780 01781 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); 01782 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); 01783 01784 for( i = 0; i < nPoints; i++ ) 01785 { 01786 memcpy( psShape->padfM + i, 01787 psSHP->pabyRec + nOffset + 16 + i*8, 8 ); 01788 if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); 01789 } 01790 psShape->bMeasureIsUsed = TRUE; 01791 } 01792 } 01793 01794 /* ==================================================================== */ 01795 /* Extract vertices for a MultiPoint. */ 01796 /* ==================================================================== */ 01797 else if( psShape->nSHPType == SHPT_MULTIPOINT 01798 || psShape->nSHPType == SHPT_MULTIPOINTM 01799 || psShape->nSHPType == SHPT_MULTIPOINTZ ) 01800 { 01801 int32 nPoints; 01802 int i, nOffset; 01803 01804 if ( 44 + 4 > nEntitySize ) 01805 { 01806 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", 01807 hEntity, nEntitySize); 01808 psSHP->sHooks.Error( pszErrorMsg ); 01809 SHPDestroyObject(psShape); 01810 return NULL; 01811 } 01812 memcpy( &nPoints, psSHP->pabyRec + 44, 4 ); 01813 01814 if( bBigEndian ) SwapWord( 4, &nPoints ); 01815 01816 if (nPoints < 0 || nPoints > 50 * 1000 * 1000) 01817 { 01818 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d", 01819 hEntity, nPoints); 01820 psSHP->sHooks.Error( pszErrorMsg ); 01821 SHPDestroyObject(psShape); 01822 return NULL; 01823 } 01824 01825 nRequiredSize = 48 + nPoints * 16; 01826 if( psShape->nSHPType == SHPT_MULTIPOINTZ ) 01827 { 01828 nRequiredSize += 16 + nPoints * 8; 01829 } 01830 if (nRequiredSize > nEntitySize) 01831 { 01832 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d", 01833 hEntity, nPoints, nEntitySize); 01834 psSHP->sHooks.Error( pszErrorMsg ); 01835 SHPDestroyObject(psShape); 01836 return NULL; 01837 } 01838 01839 psShape->nVertices = nPoints; 01840 psShape->padfX = (double *) calloc(nPoints,sizeof(double)); 01841 psShape->padfY = (double *) calloc(nPoints,sizeof(double)); 01842 psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); 01843 psShape->padfM = (double *) calloc(nPoints,sizeof(double)); 01844 01845 if (psShape->padfX == NULL || 01846 psShape->padfY == NULL || 01847 psShape->padfZ == NULL || 01848 psShape->padfM == NULL) 01849 { 01850 snprintf(pszErrorMsg, 128, 01851 "Not enough memory to allocate requested memory (nPoints=%d) for shape %d. " 01852 "Probably broken SHP file", hEntity, nPoints ); 01853 psSHP->sHooks.Error( pszErrorMsg ); 01854 SHPDestroyObject(psShape); 01855 return NULL; 01856 } 01857 01858 for( i = 0; i < nPoints; i++ ) 01859 { 01860 memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 ); 01861 memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 ); 01862 01863 if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); 01864 if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); 01865 } 01866 01867 nOffset = 48 + 16*nPoints; 01868 01869 /* -------------------------------------------------------------------- */ 01870 /* Get the X/Y bounds. */ 01871 /* -------------------------------------------------------------------- */ 01872 memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); 01873 memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); 01874 memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); 01875 memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); 01876 01877 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); 01878 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); 01879 if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); 01880 if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); 01881 01882 /* -------------------------------------------------------------------- */ 01883 /* If we have a Z coordinate, collect that now. */ 01884 /* -------------------------------------------------------------------- */ 01885 if( psShape->nSHPType == SHPT_MULTIPOINTZ ) 01886 { 01887 memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); 01888 memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); 01889 01890 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); 01891 if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); 01892 01893 for( i = 0; i < nPoints; i++ ) 01894 { 01895 memcpy( psShape->padfZ + i, 01896 psSHP->pabyRec + nOffset + 16 + i*8, 8 ); 01897 if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); 01898 } 01899 01900 nOffset += 16 + 8*nPoints; 01901 } 01902 01903 /* -------------------------------------------------------------------- */ 01904 /* If we have a M measure value, then read it now. We assume */ 01905 /* that the measure can be present for any shape if the size is */ 01906 /* big enough, but really it will only occur for the Z shapes */ 01907 /* (options), and the M shapes. */ 01908 /* -------------------------------------------------------------------- */ 01909 if( nEntitySize >= nOffset + 16 + 8*nPoints ) 01910 { 01911 memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); 01912 memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); 01913 01914 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); 01915 if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); 01916 01917 for( i = 0; i < nPoints; i++ ) 01918 { 01919 memcpy( psShape->padfM + i, 01920 psSHP->pabyRec + nOffset + 16 + i*8, 8 ); 01921 if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); 01922 } 01923 psShape->bMeasureIsUsed = TRUE; 01924 } 01925 } 01926 01927 /* ==================================================================== */ 01928 /* Extract vertices for a point. */ 01929 /* ==================================================================== */ 01930 else if( psShape->nSHPType == SHPT_POINT 01931 || psShape->nSHPType == SHPT_POINTM 01932 || psShape->nSHPType == SHPT_POINTZ ) 01933 { 01934 int nOffset; 01935 01936 psShape->nVertices = 1; 01937 psShape->padfX = (double *) calloc(1,sizeof(double)); 01938 psShape->padfY = (double *) calloc(1,sizeof(double)); 01939 psShape->padfZ = (double *) calloc(1,sizeof(double)); 01940 psShape->padfM = (double *) calloc(1,sizeof(double)); 01941 01942 if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize) 01943 { 01944 snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", 01945 hEntity, nEntitySize); 01946 psSHP->sHooks.Error( pszErrorMsg ); 01947 SHPDestroyObject(psShape); 01948 return NULL; 01949 } 01950 memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 ); 01951 memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 ); 01952 01953 if( bBigEndian ) SwapWord( 8, psShape->padfX ); 01954 if( bBigEndian ) SwapWord( 8, psShape->padfY ); 01955 01956 nOffset = 20 + 8; 01957 01958 /* -------------------------------------------------------------------- */ 01959 /* If we have a Z coordinate, collect that now. */ 01960 /* -------------------------------------------------------------------- */ 01961 if( psShape->nSHPType == SHPT_POINTZ ) 01962 { 01963 memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 ); 01964 01965 if( bBigEndian ) SwapWord( 8, psShape->padfZ ); 01966 01967 nOffset += 8; 01968 } 01969 01970 /* -------------------------------------------------------------------- */ 01971 /* If we have a M measure value, then read it now. We assume */ 01972 /* that the measure can be present for any shape if the size is */ 01973 /* big enough, but really it will only occur for the Z shapes */ 01974 /* (options), and the M shapes. */ 01975 /* -------------------------------------------------------------------- */ 01976 if( nEntitySize >= nOffset + 8 ) 01977 { 01978 memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 ); 01979 01980 if( bBigEndian ) SwapWord( 8, psShape->padfM ); 01981 psShape->bMeasureIsUsed = TRUE; 01982 } 01983 01984 /* -------------------------------------------------------------------- */ 01985 /* Since no extents are supplied in the record, we will apply */ 01986 /* them from the single vertex. */ 01987 /* -------------------------------------------------------------------- */ 01988 psShape->dfXMin = psShape->dfXMax = psShape->padfX[0]; 01989 psShape->dfYMin = psShape->dfYMax = psShape->padfY[0]; 01990 psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0]; 01991 psShape->dfMMin = psShape->dfMMax = psShape->padfM[0]; 01992 } 01993 01994 return( psShape ); 01995 } 01996 01997 /************************************************************************/ 01998 /* SHPTypeName() */ 01999 /************************************************************************/ 02000 02001 const char SHPAPI_CALL1(*) 02002 SHPTypeName( int nSHPType ) 02003 02004 { 02005 switch( nSHPType ) 02006 { 02007 case SHPT_NULL: 02008 return "NullShape"; 02009 02010 case SHPT_POINT: 02011 return "Point"; 02012 02013 case SHPT_ARC: 02014 return "Arc"; 02015 02016 case SHPT_POLYGON: 02017 return "Polygon"; 02018 02019 case SHPT_MULTIPOINT: 02020 return "MultiPoint"; 02021 02022 case SHPT_POINTZ: 02023 return "PointZ"; 02024 02025 case SHPT_ARCZ: 02026 return "ArcZ"; 02027 02028 case SHPT_POLYGONZ: 02029 return "PolygonZ"; 02030 02031 case SHPT_MULTIPOINTZ: 02032 return "MultiPointZ"; 02033 02034 case SHPT_POINTM: 02035 return "PointM"; 02036 02037 case SHPT_ARCM: 02038 return "ArcM"; 02039 02040 case SHPT_POLYGONM: 02041 return "PolygonM"; 02042 02043 case SHPT_MULTIPOINTM: 02044 return "MultiPointM"; 02045 02046 case SHPT_MULTIPATCH: 02047 return "MultiPatch"; 02048 02049 default: 02050 return "UnknownShapeType"; 02051 } 02052 } 02053 02054 /************************************************************************/ 02055 /* SHPPartTypeName() */ 02056 /************************************************************************/ 02057 02058 const char SHPAPI_CALL1(*) 02059 SHPPartTypeName( int nPartType ) 02060 02061 { 02062 switch( nPartType ) 02063 { 02064 case SHPP_TRISTRIP: 02065 return "TriangleStrip"; 02066 02067 case SHPP_TRIFAN: 02068 return "TriangleFan"; 02069 02070 case SHPP_OUTERRING: 02071 return "OuterRing"; 02072 02073 case SHPP_INNERRING: 02074 return "InnerRing"; 02075 02076 case SHPP_FIRSTRING: 02077 return "FirstRing"; 02078 02079 case SHPP_RING: 02080 return "Ring"; 02081 02082 default: 02083 return "UnknownPartType"; 02084 } 02085 } 02086 02087 /************************************************************************/ 02088 /* SHPDestroyObject() */ 02089 /************************************************************************/ 02090 02091 void SHPAPI_CALL 02092 SHPDestroyObject( SHPObject * psShape ) 02093 02094 { 02095 if( psShape == NULL ) 02096 return; 02097 02098 if( psShape->padfX != NULL ) 02099 free( psShape->padfX ); 02100 if( psShape->padfY != NULL ) 02101 free( psShape->padfY ); 02102 if( psShape->padfZ != NULL ) 02103 free( psShape->padfZ ); 02104 if( psShape->padfM != NULL ) 02105 free( psShape->padfM ); 02106 02107 if( psShape->panPartStart != NULL ) 02108 free( psShape->panPartStart ); 02109 if( psShape->panPartType != NULL ) 02110 free( psShape->panPartType ); 02111 02112 free( psShape ); 02113 } 02114 02115 /************************************************************************/ 02116 /* SHPRewindObject() */ 02117 /* */ 02118 /* Reset the winding of polygon objects to adhere to the */ 02119 /* specification. */ 02120 /************************************************************************/ 02121 02122 int SHPAPI_CALL 02123 SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) 02124 02125 { 02126 int iOpRing, bAltered = 0; 02127 02128 /* -------------------------------------------------------------------- */ 02129 /* Do nothing if this is not a polygon object. */ 02130 /* -------------------------------------------------------------------- */ 02131 if( psObject->nSHPType != SHPT_POLYGON 02132 && psObject->nSHPType != SHPT_POLYGONZ 02133 && psObject->nSHPType != SHPT_POLYGONM ) 02134 return 0; 02135 02136 if( psObject->nVertices == 0 || psObject->nParts == 0 ) 02137 return 0; 02138 02139 /* -------------------------------------------------------------------- */ 02140 /* Process each of the rings. */ 02141 /* -------------------------------------------------------------------- */ 02142 for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ ) 02143 { 02144 int bInner, iVert, nVertCount, nVertStart, iCheckRing; 02145 double dfSum, dfTestX, dfTestY; 02146 02147 /* -------------------------------------------------------------------- */ 02148 /* Determine if this ring is an inner ring or an outer ring */ 02149 /* relative to all the other rings. For now we assume the */ 02150 /* first ring is outer and all others are inner, but eventually */ 02151 /* we need to fix this to handle multiple island polygons and */ 02152 /* unordered sets of rings. */ 02153 /* */ 02154 /* -------------------------------------------------------------------- */ 02155 02156 /* Use point in the middle of segment to avoid testing 02157 * common points of rings. 02158 */ 02159 dfTestX = ( psObject->padfX[psObject->panPartStart[iOpRing]] 02160 + psObject->padfX[psObject->panPartStart[iOpRing] + 1] ) / 2; 02161 dfTestY = ( psObject->padfY[psObject->panPartStart[iOpRing]] 02162 + psObject->padfY[psObject->panPartStart[iOpRing] + 1] ) / 2; 02163 02164 bInner = FALSE; 02165 for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ ) 02166 { 02167 int iEdge; 02168 02169 if( iCheckRing == iOpRing ) 02170 continue; 02171 02172 nVertStart = psObject->panPartStart[iCheckRing]; 02173 02174 if( iCheckRing == psObject->nParts-1 ) 02175 nVertCount = psObject->nVertices 02176 - psObject->panPartStart[iCheckRing]; 02177 else 02178 nVertCount = psObject->panPartStart[iCheckRing+1] 02179 - psObject->panPartStart[iCheckRing]; 02180 02181 for( iEdge = 0; iEdge < nVertCount; iEdge++ ) 02182 { 02183 int iNext; 02184 02185 if( iEdge < nVertCount-1 ) 02186 iNext = iEdge+1; 02187 else 02188 iNext = 0; 02189 02190 /* Rule #1: 02191 * Test whether the edge 'straddles' the horizontal ray from the test point (dfTestY,dfTestY) 02192 * The rule #1 also excludes edges collinear with the ray. 02193 */ 02194 if ( ( psObject->padfY[iEdge+nVertStart] < dfTestY 02195 && dfTestY <= psObject->padfY[iNext+nVertStart] ) 02196 || ( psObject->padfY[iNext+nVertStart] < dfTestY 02197 && dfTestY <= psObject->padfY[iEdge+nVertStart] ) ) 02198 { 02199 /* Rule #2: 02200 * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY) 02201 */ 02202 double const intersect = 02203 ( psObject->padfX[iEdge+nVertStart] 02204 + ( dfTestY - psObject->padfY[iEdge+nVertStart] ) 02205 / ( psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart] ) 02206 * ( psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart] ) ); 02207 02208 if (intersect < dfTestX) 02209 { 02210 bInner = !bInner; 02211 } 02212 } 02213 } 02214 } /* for iCheckRing */ 02215 02216 /* -------------------------------------------------------------------- */ 02217 /* Determine the current order of this ring so we will know if */ 02218 /* it has to be reversed. */ 02219 /* -------------------------------------------------------------------- */ 02220 nVertStart = psObject->panPartStart[iOpRing]; 02221 02222 if( iOpRing == psObject->nParts-1 ) 02223 nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing]; 02224 else 02225 nVertCount = psObject->panPartStart[iOpRing+1] 02226 - psObject->panPartStart[iOpRing]; 02227 02228 dfSum = 0.0; 02229 for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ ) 02230 { 02231 dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1] 02232 - psObject->padfY[iVert] * psObject->padfX[iVert+1]; 02233 } 02234 02235 dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart] 02236 - psObject->padfY[iVert] * psObject->padfX[nVertStart]; 02237 02238 /* -------------------------------------------------------------------- */ 02239 /* Reverse if necessary. */ 02240 /* -------------------------------------------------------------------- */ 02241 if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) ) 02242 { 02243 int i; 02244 02245 bAltered++; 02246 for( i = 0; i < nVertCount/2; i++ ) 02247 { 02248 double dfSaved; 02249 02250 /* Swap X */ 02251 dfSaved = psObject->padfX[nVertStart+i]; 02252 psObject->padfX[nVertStart+i] = 02253 psObject->padfX[nVertStart+nVertCount-i-1]; 02254 psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved; 02255 02256 /* Swap Y */ 02257 dfSaved = psObject->padfY[nVertStart+i]; 02258 psObject->padfY[nVertStart+i] = 02259 psObject->padfY[nVertStart+nVertCount-i-1]; 02260 psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved; 02261 02262 /* Swap Z */ 02263 if( psObject->padfZ ) 02264 { 02265 dfSaved = psObject->padfZ[nVertStart+i]; 02266 psObject->padfZ[nVertStart+i] = 02267 psObject->padfZ[nVertStart+nVertCount-i-1]; 02268 psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved; 02269 } 02270 02271 /* Swap M */ 02272 if( psObject->padfM ) 02273 { 02274 dfSaved = psObject->padfM[nVertStart+i]; 02275 psObject->padfM[nVertStart+i] = 02276 psObject->padfM[nVertStart+nVertCount-i-1]; 02277 psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved; 02278 } 02279 } 02280 } 02281 } 02282 02283 return bAltered; 02284 }