/src/gdal/ogr/ogrsf_frmts/shape/shpopen.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: Shapelib |
4 | | * Purpose: Implementation of core Shapefile read/write functions. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 1999, 2001, Frank Warmerdam |
9 | | * Copyright (c) 2011-2024, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later |
12 | | ******************************************************************************/ |
13 | | |
14 | | #include "shapefil_private.h" |
15 | | |
16 | | #include <assert.h> |
17 | | #include <errno.h> |
18 | | #include <limits.h> |
19 | | #include <math.h> |
20 | | #include <stdbool.h> |
21 | | #include <stdint.h> |
22 | | #include <stdio.h> |
23 | | #include <stdlib.h> |
24 | | #include <string.h> |
25 | | |
26 | | #ifndef FALSE |
27 | | #define FALSE 0 |
28 | | #define TRUE 1 |
29 | | #endif |
30 | | |
31 | 0 | #define ByteCopy(a, b, c) memcpy(b, a, c) |
32 | | #ifndef MAX |
33 | | #define MIN(a, b) ((a < b) ? a : b) |
34 | | #define MAX(a, b) ((a > b) ? a : b) |
35 | | #endif |
36 | | |
37 | | #ifndef USE_CPL |
38 | | #if defined(_MSC_VER) |
39 | | #if _MSC_VER < 1900 |
40 | | #define snprintf _snprintf |
41 | | #endif |
42 | | #elif defined(_WIN32) |
43 | | #ifndef snprintf |
44 | | #define snprintf _snprintf |
45 | | #endif |
46 | | #endif |
47 | | #endif |
48 | | |
49 | | /* Allows customization of the message in vendored builds (such as GDAL) */ |
50 | | #ifndef SHP_RESTORE_SHX_HINT_MESSAGE |
51 | | #define SHP_RESTORE_SHX_HINT_MESSAGE \ |
52 | | " Use SHPRestoreSHX() to restore or create it." |
53 | | #endif |
54 | | |
55 | | /************************************************************************/ |
56 | | /* SHPWriteHeader() */ |
57 | | /* */ |
58 | | /* Write out a header for the .shp and .shx files as well as the */ |
59 | | /* contents of the index (.shx) file. */ |
60 | | /************************************************************************/ |
61 | | |
62 | | void SHPAPI_CALL SHPWriteHeader(SHPHandle psSHP) |
63 | 0 | { |
64 | 0 | if (psSHP->fpSHX == SHPLIB_NULLPTR) |
65 | 0 | { |
66 | 0 | psSHP->sHooks.Error("SHPWriteHeader failed : SHX file is closed"); |
67 | 0 | return; |
68 | 0 | } |
69 | | |
70 | | /* -------------------------------------------------------------------- */ |
71 | | /* Prepare header block for .shp file. */ |
72 | | /* -------------------------------------------------------------------- */ |
73 | | |
74 | 0 | unsigned char abyHeader[100] = {0}; |
75 | 0 | abyHeader[2] = 0x27; /* magic cookie */ |
76 | 0 | abyHeader[3] = 0x0a; |
77 | |
|
78 | 0 | uint32_t i32 = psSHP->nFileSize / 2; /* file size */ |
79 | 0 | ByteCopy(&i32, abyHeader + 24, 4); |
80 | 0 | #if !defined(SHP_BIG_ENDIAN) |
81 | 0 | SHP_SWAP32(abyHeader + 24); |
82 | 0 | #endif |
83 | |
|
84 | 0 | i32 = 1000; /* version */ |
85 | 0 | ByteCopy(&i32, abyHeader + 28, 4); |
86 | | #if defined(SHP_BIG_ENDIAN) |
87 | | SHP_SWAP32(abyHeader + 28); |
88 | | #endif |
89 | |
|
90 | 0 | i32 = psSHP->nShapeType; /* shape type */ |
91 | 0 | ByteCopy(&i32, abyHeader + 32, 4); |
92 | | #if defined(SHP_BIG_ENDIAN) |
93 | | SHP_SWAP32(abyHeader + 32); |
94 | | #endif |
95 | |
|
96 | 0 | double dValue = psSHP->adBoundsMin[0]; /* set bounds */ |
97 | 0 | ByteCopy(&dValue, abyHeader + 36, 8); |
98 | | #if defined(SHP_BIG_ENDIAN) |
99 | | SHP_SWAP64(abyHeader + 36); |
100 | | #endif |
101 | 0 | dValue = psSHP->adBoundsMin[1]; |
102 | 0 | ByteCopy(&dValue, abyHeader + 44, 8); |
103 | | #if defined(SHP_BIG_ENDIAN) |
104 | | SHP_SWAP64(abyHeader + 44); |
105 | | #endif |
106 | 0 | dValue = psSHP->adBoundsMax[0]; |
107 | 0 | ByteCopy(&dValue, abyHeader + 52, 8); |
108 | | #if defined(SHP_BIG_ENDIAN) |
109 | | SHP_SWAP64(abyHeader + 52); |
110 | | #endif |
111 | |
|
112 | 0 | dValue = psSHP->adBoundsMax[1]; |
113 | 0 | ByteCopy(&dValue, abyHeader + 60, 8); |
114 | | #if defined(SHP_BIG_ENDIAN) |
115 | | SHP_SWAP64(abyHeader + 60); |
116 | | #endif |
117 | |
|
118 | 0 | dValue = psSHP->adBoundsMin[2]; /* z */ |
119 | 0 | ByteCopy(&dValue, abyHeader + 68, 8); |
120 | | #if defined(SHP_BIG_ENDIAN) |
121 | | SHP_SWAP64(abyHeader + 68); |
122 | | #endif |
123 | |
|
124 | 0 | dValue = psSHP->adBoundsMax[2]; |
125 | 0 | ByteCopy(&dValue, abyHeader + 76, 8); |
126 | | #if defined(SHP_BIG_ENDIAN) |
127 | | SHP_SWAP64(abyHeader + 76); |
128 | | #endif |
129 | |
|
130 | 0 | dValue = psSHP->adBoundsMin[3]; /* m */ |
131 | 0 | ByteCopy(&dValue, abyHeader + 84, 8); |
132 | | #if defined(SHP_BIG_ENDIAN) |
133 | | SHP_SWAP64(abyHeader + 84); |
134 | | #endif |
135 | |
|
136 | 0 | dValue = psSHP->adBoundsMax[3]; |
137 | 0 | ByteCopy(&dValue, abyHeader + 92, 8); |
138 | | #if defined(SHP_BIG_ENDIAN) |
139 | | SHP_SWAP64(abyHeader + 92); |
140 | | #endif |
141 | | |
142 | | /* -------------------------------------------------------------------- */ |
143 | | /* Write .shp file header. */ |
144 | | /* -------------------------------------------------------------------- */ |
145 | 0 | if (psSHP->sHooks.FSeek(psSHP->fpSHP, 0, 0) != 0 || |
146 | 0 | psSHP->sHooks.FWrite(abyHeader, 100, 1, psSHP->fpSHP) != 1) |
147 | 0 | { |
148 | 0 | char szErrorMsg[200]; |
149 | |
|
150 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
151 | 0 | "Failure writing .shp header: %s", strerror(errno)); |
152 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
153 | 0 | psSHP->sHooks.Error(szErrorMsg); |
154 | 0 | return; |
155 | 0 | } |
156 | | |
157 | | /* -------------------------------------------------------------------- */ |
158 | | /* Prepare, and write .shx file header. */ |
159 | | /* -------------------------------------------------------------------- */ |
160 | 0 | i32 = (psSHP->nRecords * 2 * sizeof(uint32_t) + 100) / 2; /* file size */ |
161 | 0 | ByteCopy(&i32, abyHeader + 24, 4); |
162 | 0 | #if !defined(SHP_BIG_ENDIAN) |
163 | 0 | SHP_SWAP32(abyHeader + 24); |
164 | 0 | #endif |
165 | |
|
166 | 0 | if (psSHP->sHooks.FSeek(psSHP->fpSHX, 0, 0) != 0 || |
167 | 0 | psSHP->sHooks.FWrite(abyHeader, 100, 1, psSHP->fpSHX) != 1) |
168 | 0 | { |
169 | 0 | char szErrorMsg[200]; |
170 | |
|
171 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
172 | 0 | "Failure writing .shx header: %s", strerror(errno)); |
173 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
174 | 0 | psSHP->sHooks.Error(szErrorMsg); |
175 | |
|
176 | 0 | return; |
177 | 0 | } |
178 | | |
179 | | /* -------------------------------------------------------------------- */ |
180 | | /* Write out the .shx contents. */ |
181 | | /* -------------------------------------------------------------------- */ |
182 | 0 | uint32_t *panSHX = |
183 | 0 | STATIC_CAST(uint32_t *, malloc(sizeof(uint32_t) * 2 * psSHP->nRecords)); |
184 | 0 | if (panSHX == SHPLIB_NULLPTR) |
185 | 0 | { |
186 | 0 | psSHP->sHooks.Error("Failure allocatin panSHX"); |
187 | 0 | return; |
188 | 0 | } |
189 | | |
190 | 0 | for (int i = 0; i < psSHP->nRecords; i++) |
191 | 0 | { |
192 | 0 | panSHX[i * 2] = psSHP->panRecOffset[i] / 2; |
193 | 0 | panSHX[i * 2 + 1] = psSHP->panRecSize[i] / 2; |
194 | 0 | #if !defined(SHP_BIG_ENDIAN) |
195 | 0 | SHP_SWAP32(panSHX + i * 2); |
196 | 0 | SHP_SWAP32(panSHX + i * 2 + 1); |
197 | 0 | #endif |
198 | 0 | } |
199 | |
|
200 | 0 | if (STATIC_CAST(int, psSHP->sHooks.FWrite(panSHX, sizeof(uint32_t) * 2, |
201 | 0 | psSHP->nRecords, psSHP->fpSHX)) != |
202 | 0 | psSHP->nRecords) |
203 | 0 | { |
204 | 0 | char szErrorMsg[200]; |
205 | |
|
206 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
207 | 0 | "Failure writing .shx contents: %s", strerror(errno)); |
208 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
209 | 0 | psSHP->sHooks.Error(szErrorMsg); |
210 | 0 | } |
211 | |
|
212 | 0 | free(panSHX); |
213 | | |
214 | | /* -------------------------------------------------------------------- */ |
215 | | /* Flush to disk. */ |
216 | | /* -------------------------------------------------------------------- */ |
217 | 0 | psSHP->sHooks.FFlush(psSHP->fpSHP); |
218 | 0 | psSHP->sHooks.FFlush(psSHP->fpSHX); |
219 | 0 | } |
220 | | |
221 | | /************************************************************************/ |
222 | | /* SHPOpen() */ |
223 | | /************************************************************************/ |
224 | | |
225 | | SHPHandle SHPAPI_CALL SHPOpen(const char *pszLayer, const char *pszAccess) |
226 | 0 | { |
227 | 0 | SAHooks sHooks; |
228 | |
|
229 | 0 | SASetupDefaultHooks(&sHooks); |
230 | |
|
231 | 0 | return SHPOpenLL(pszLayer, pszAccess, &sHooks); |
232 | 0 | } |
233 | | |
234 | | /************************************************************************/ |
235 | | /* SHPGetLenWithoutExtension() */ |
236 | | /************************************************************************/ |
237 | | |
238 | | static int SHPGetLenWithoutExtension(const char *pszBasename) |
239 | 0 | { |
240 | 0 | const int nLen = STATIC_CAST(int, strlen(pszBasename)); |
241 | 0 | for (int i = nLen - 1; |
242 | 0 | i > 0 && pszBasename[i] != '/' && pszBasename[i] != '\\'; i--) |
243 | 0 | { |
244 | 0 | if (pszBasename[i] == '.') |
245 | 0 | { |
246 | 0 | return i; |
247 | 0 | } |
248 | 0 | } |
249 | 0 | return nLen; |
250 | 0 | } |
251 | | |
252 | | /************************************************************************/ |
253 | | /* SHPOpen() */ |
254 | | /* */ |
255 | | /* Open the .shp and .shx files based on the basename of the */ |
256 | | /* files or either file name. */ |
257 | | /************************************************************************/ |
258 | | |
259 | | SHPHandle SHPAPI_CALL SHPOpenLL(const char *pszLayer, const char *pszAccess, |
260 | | const SAHooks *psHooks) |
261 | 0 | { |
262 | | /* -------------------------------------------------------------------- */ |
263 | | /* Ensure the access string is one of the legal ones. We */ |
264 | | /* ensure the result string indicates binary to avoid common */ |
265 | | /* problems on Windows. */ |
266 | | /* -------------------------------------------------------------------- */ |
267 | 0 | bool bLazySHXLoading = false; |
268 | 0 | if (strcmp(pszAccess, "rb+") == 0 || strcmp(pszAccess, "r+b") == 0 || |
269 | 0 | strcmp(pszAccess, "r+") == 0) |
270 | 0 | { |
271 | 0 | pszAccess = "r+b"; |
272 | 0 | } |
273 | 0 | else |
274 | 0 | { |
275 | 0 | bLazySHXLoading = strchr(pszAccess, 'l') != SHPLIB_NULLPTR; |
276 | 0 | pszAccess = "rb"; |
277 | 0 | } |
278 | | |
279 | | /* -------------------------------------------------------------------- */ |
280 | | /* Initialize the info structure. */ |
281 | | /* -------------------------------------------------------------------- */ |
282 | 0 | SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(1, sizeof(SHPInfo))); |
283 | 0 | if (!psSHP) |
284 | 0 | return SHPLIB_NULLPTR; |
285 | | |
286 | 0 | psSHP->bUpdated = FALSE; |
287 | 0 | memcpy(&(psSHP->sHooks), psHooks, sizeof(SAHooks)); |
288 | | |
289 | | /* -------------------------------------------------------------------- */ |
290 | | /* Open the .shp and .shx files. Note that files pulled from */ |
291 | | /* a PC to Unix with upper case filenames won't work! */ |
292 | | /* -------------------------------------------------------------------- */ |
293 | 0 | const int nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer); |
294 | 0 | char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5)); |
295 | 0 | if (!pszFullname) |
296 | 0 | { |
297 | 0 | free(psSHP); |
298 | 0 | return SHPLIB_NULLPTR; |
299 | 0 | } |
300 | 0 | memcpy(pszFullname, pszLayer, nLenWithoutExtension); |
301 | 0 | memcpy(pszFullname + nLenWithoutExtension, ".shp", 5); |
302 | 0 | psSHP->fpSHP = |
303 | 0 | psSHP->sHooks.FOpen(pszFullname, pszAccess, psSHP->sHooks.pvUserData); |
304 | 0 | if (psSHP->fpSHP == SHPLIB_NULLPTR) |
305 | 0 | { |
306 | 0 | memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5); |
307 | 0 | psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess, |
308 | 0 | psSHP->sHooks.pvUserData); |
309 | 0 | } |
310 | |
|
311 | 0 | if (psSHP->fpSHP == SHPLIB_NULLPTR) |
312 | 0 | { |
313 | 0 | const size_t nMessageLen = strlen(pszFullname) * 2 + 256; |
314 | 0 | char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen)); |
315 | 0 | if (pszMessage) |
316 | 0 | { |
317 | 0 | pszFullname[nLenWithoutExtension] = 0; |
318 | 0 | snprintf(pszMessage, nMessageLen, |
319 | 0 | "Unable to open %s.shp or %s.SHP in %s mode.", pszFullname, |
320 | 0 | pszFullname, pszAccess); |
321 | 0 | psHooks->Error(pszMessage); |
322 | 0 | free(pszMessage); |
323 | 0 | } |
324 | |
|
325 | 0 | free(psSHP); |
326 | 0 | free(pszFullname); |
327 | |
|
328 | 0 | return SHPLIB_NULLPTR; |
329 | 0 | } |
330 | | |
331 | 0 | memcpy(pszFullname + nLenWithoutExtension, ".shx", 5); |
332 | 0 | psSHP->fpSHX = |
333 | 0 | psSHP->sHooks.FOpen(pszFullname, pszAccess, psSHP->sHooks.pvUserData); |
334 | 0 | if (psSHP->fpSHX == SHPLIB_NULLPTR) |
335 | 0 | { |
336 | 0 | memcpy(pszFullname + nLenWithoutExtension, ".SHX", 5); |
337 | 0 | psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess, |
338 | 0 | psSHP->sHooks.pvUserData); |
339 | 0 | } |
340 | |
|
341 | 0 | if (psSHP->fpSHX == SHPLIB_NULLPTR) |
342 | 0 | { |
343 | 0 | const size_t nMessageLen = |
344 | 0 | 64 + strlen(pszFullname) * 2 + strlen(SHP_RESTORE_SHX_HINT_MESSAGE); |
345 | 0 | char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen)); |
346 | 0 | if (pszMessage) |
347 | 0 | { |
348 | 0 | pszFullname[nLenWithoutExtension] = 0; |
349 | 0 | snprintf( |
350 | 0 | pszMessage, nMessageLen, |
351 | 0 | "Unable to open %s.shx or %s.SHX." SHP_RESTORE_SHX_HINT_MESSAGE, |
352 | 0 | pszFullname, pszFullname); |
353 | 0 | psHooks->Error(pszMessage); |
354 | 0 | free(pszMessage); |
355 | 0 | } |
356 | |
|
357 | 0 | psSHP->sHooks.FClose(psSHP->fpSHP); |
358 | 0 | free(psSHP); |
359 | 0 | free(pszFullname); |
360 | 0 | return SHPLIB_NULLPTR; |
361 | 0 | } |
362 | | |
363 | 0 | free(pszFullname); |
364 | | |
365 | | /* -------------------------------------------------------------------- */ |
366 | | /* Read the file size from the SHP file. */ |
367 | | /* -------------------------------------------------------------------- */ |
368 | 0 | unsigned char *pabyBuf = STATIC_CAST(unsigned char *, malloc(100)); |
369 | 0 | if (!pabyBuf || psSHP->sHooks.FRead(pabyBuf, 100, 1, psSHP->fpSHP) != 1) |
370 | 0 | { |
371 | 0 | psSHP->sHooks.Error(".shp file is unreadable, or corrupt."); |
372 | 0 | psSHP->sHooks.FClose(psSHP->fpSHP); |
373 | 0 | psSHP->sHooks.FClose(psSHP->fpSHX); |
374 | 0 | free(pabyBuf); |
375 | 0 | free(psSHP); |
376 | |
|
377 | 0 | return SHPLIB_NULLPTR; |
378 | 0 | } |
379 | | |
380 | 0 | psSHP->nFileSize = (STATIC_CAST(unsigned int, pabyBuf[24]) << 24) | |
381 | 0 | (pabyBuf[25] << 16) | (pabyBuf[26] << 8) | pabyBuf[27]; |
382 | 0 | if (psSHP->nFileSize < UINT_MAX / 2) |
383 | 0 | psSHP->nFileSize *= 2; |
384 | 0 | else |
385 | 0 | psSHP->nFileSize = (UINT_MAX / 2) * 2; |
386 | | |
387 | | /* -------------------------------------------------------------------- */ |
388 | | /* Read SHX file Header info */ |
389 | | /* -------------------------------------------------------------------- */ |
390 | 0 | if (psSHP->sHooks.FRead(pabyBuf, 100, 1, psSHP->fpSHX) != 1 || |
391 | 0 | pabyBuf[0] != 0 || pabyBuf[1] != 0 || pabyBuf[2] != 0x27 || |
392 | 0 | (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d)) |
393 | 0 | { |
394 | 0 | psSHP->sHooks.Error(".shx file is unreadable, or corrupt."); |
395 | 0 | psSHP->sHooks.FClose(psSHP->fpSHP); |
396 | 0 | psSHP->sHooks.FClose(psSHP->fpSHX); |
397 | 0 | free(pabyBuf); |
398 | 0 | free(psSHP); |
399 | |
|
400 | 0 | return SHPLIB_NULLPTR; |
401 | 0 | } |
402 | | |
403 | 0 | psSHP->nRecords = pabyBuf[27] | (pabyBuf[26] << 8) | (pabyBuf[25] << 16) | |
404 | 0 | ((pabyBuf[24] & 0x7F) << 24); |
405 | 0 | psSHP->nRecords = (psSHP->nRecords - 50) / 4; |
406 | |
|
407 | 0 | psSHP->nShapeType = pabyBuf[32]; |
408 | |
|
409 | 0 | if (psSHP->nRecords < 0 || psSHP->nRecords > 256000000) |
410 | 0 | { |
411 | 0 | char szErrorMsg[200]; |
412 | |
|
413 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
414 | 0 | "Record count in .shx header is %d, which seems\n" |
415 | 0 | "unreasonable. Assuming header is corrupt.", |
416 | 0 | psSHP->nRecords); |
417 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
418 | 0 | psSHP->sHooks.Error(szErrorMsg); |
419 | 0 | psSHP->sHooks.FClose(psSHP->fpSHP); |
420 | 0 | psSHP->sHooks.FClose(psSHP->fpSHX); |
421 | 0 | free(psSHP); |
422 | 0 | free(pabyBuf); |
423 | |
|
424 | 0 | return SHPLIB_NULLPTR; |
425 | 0 | } |
426 | | |
427 | | /* If a lot of records are advertized, check that the file is big enough */ |
428 | | /* to hold them */ |
429 | 0 | if (psSHP->nRecords >= 1024 * 1024) |
430 | 0 | { |
431 | 0 | psSHP->sHooks.FSeek(psSHP->fpSHX, 0, 2); |
432 | 0 | const SAOffset nFileSize = psSHP->sHooks.FTell(psSHP->fpSHX); |
433 | 0 | if (nFileSize > 100 && |
434 | 0 | nFileSize / 2 < STATIC_CAST(SAOffset, psSHP->nRecords * 4 + 50)) |
435 | 0 | { |
436 | 0 | psSHP->nRecords = STATIC_CAST(int, (nFileSize - 100) / 8); |
437 | 0 | } |
438 | 0 | psSHP->sHooks.FSeek(psSHP->fpSHX, 100, 0); |
439 | 0 | } |
440 | | |
441 | | /* -------------------------------------------------------------------- */ |
442 | | /* Read the bounds. */ |
443 | | /* -------------------------------------------------------------------- */ |
444 | 0 | double dValue; |
445 | |
|
446 | | #if defined(SHP_BIG_ENDIAN) |
447 | | SHP_SWAP64(pabyBuf + 36); |
448 | | #endif |
449 | 0 | memcpy(&dValue, pabyBuf + 36, 8); |
450 | 0 | psSHP->adBoundsMin[0] = dValue; |
451 | |
|
452 | | #if defined(SHP_BIG_ENDIAN) |
453 | | SHP_SWAP64(pabyBuf + 44); |
454 | | #endif |
455 | 0 | memcpy(&dValue, pabyBuf + 44, 8); |
456 | 0 | psSHP->adBoundsMin[1] = dValue; |
457 | |
|
458 | | #if defined(SHP_BIG_ENDIAN) |
459 | | SHP_SWAP64(pabyBuf + 52); |
460 | | #endif |
461 | 0 | memcpy(&dValue, pabyBuf + 52, 8); |
462 | 0 | psSHP->adBoundsMax[0] = dValue; |
463 | |
|
464 | | #if defined(SHP_BIG_ENDIAN) |
465 | | SHP_SWAP64(pabyBuf + 60); |
466 | | #endif |
467 | 0 | memcpy(&dValue, pabyBuf + 60, 8); |
468 | 0 | psSHP->adBoundsMax[1] = dValue; |
469 | |
|
470 | | #if defined(SHP_BIG_ENDIAN) |
471 | | SHP_SWAP64(pabyBuf + 68); /* z */ |
472 | | #endif |
473 | 0 | memcpy(&dValue, pabyBuf + 68, 8); |
474 | 0 | psSHP->adBoundsMin[2] = dValue; |
475 | |
|
476 | | #if defined(SHP_BIG_ENDIAN) |
477 | | SHP_SWAP64(pabyBuf + 76); |
478 | | #endif |
479 | 0 | memcpy(&dValue, pabyBuf + 76, 8); |
480 | 0 | psSHP->adBoundsMax[2] = dValue; |
481 | |
|
482 | | #if defined(SHP_BIG_ENDIAN) |
483 | | SHP_SWAP64(pabyBuf + 84); /* z */ |
484 | | #endif |
485 | 0 | memcpy(&dValue, pabyBuf + 84, 8); |
486 | 0 | psSHP->adBoundsMin[3] = dValue; |
487 | |
|
488 | | #if defined(SHP_BIG_ENDIAN) |
489 | | SHP_SWAP64(pabyBuf + 92); |
490 | | #endif |
491 | 0 | memcpy(&dValue, pabyBuf + 92, 8); |
492 | 0 | psSHP->adBoundsMax[3] = dValue; |
493 | |
|
494 | 0 | free(pabyBuf); |
495 | | |
496 | | /* -------------------------------------------------------------------- */ |
497 | | /* Read the .shx file to get the offsets to each record in */ |
498 | | /* the .shp file. */ |
499 | | /* -------------------------------------------------------------------- */ |
500 | 0 | psSHP->nMaxRecords = psSHP->nRecords; |
501 | |
|
502 | 0 | psSHP->panRecOffset = |
503 | 0 | STATIC_CAST(unsigned int *, |
504 | 0 | malloc(sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords))); |
505 | 0 | psSHP->panRecSize = |
506 | 0 | STATIC_CAST(unsigned int *, |
507 | 0 | malloc(sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords))); |
508 | 0 | if (bLazySHXLoading) |
509 | 0 | pabyBuf = SHPLIB_NULLPTR; |
510 | 0 | else |
511 | 0 | pabyBuf = |
512 | 0 | STATIC_CAST(unsigned char *, malloc(8 * MAX(1, psSHP->nRecords))); |
513 | |
|
514 | 0 | if (psSHP->panRecOffset == SHPLIB_NULLPTR || |
515 | 0 | psSHP->panRecSize == SHPLIB_NULLPTR || |
516 | 0 | (!bLazySHXLoading && pabyBuf == SHPLIB_NULLPTR)) |
517 | 0 | { |
518 | 0 | char szErrorMsg[200]; |
519 | |
|
520 | 0 | snprintf( |
521 | 0 | szErrorMsg, sizeof(szErrorMsg), |
522 | 0 | "Not enough memory to allocate requested memory (nRecords=%d).\n" |
523 | 0 | "Probably broken SHP file", |
524 | 0 | psSHP->nRecords); |
525 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
526 | 0 | psSHP->sHooks.Error(szErrorMsg); |
527 | 0 | psSHP->sHooks.FClose(psSHP->fpSHP); |
528 | 0 | psSHP->sHooks.FClose(psSHP->fpSHX); |
529 | 0 | if (psSHP->panRecOffset) |
530 | 0 | free(psSHP->panRecOffset); |
531 | 0 | if (psSHP->panRecSize) |
532 | 0 | free(psSHP->panRecSize); |
533 | 0 | if (pabyBuf) |
534 | 0 | free(pabyBuf); |
535 | 0 | free(psSHP); |
536 | 0 | return SHPLIB_NULLPTR; |
537 | 0 | } |
538 | | |
539 | 0 | if (bLazySHXLoading) |
540 | 0 | { |
541 | 0 | memset(psSHP->panRecOffset, 0, |
542 | 0 | sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords)); |
543 | 0 | memset(psSHP->panRecSize, 0, |
544 | 0 | sizeof(unsigned int) * MAX(1, psSHP->nMaxRecords)); |
545 | 0 | free(pabyBuf); // sometimes make cppcheck happy, but |
546 | 0 | return (psSHP); |
547 | 0 | } |
548 | | |
549 | 0 | if (STATIC_CAST(int, psSHP->sHooks.FRead(pabyBuf, 8, psSHP->nRecords, |
550 | 0 | psSHP->fpSHX)) != psSHP->nRecords) |
551 | 0 | { |
552 | 0 | char szErrorMsg[200]; |
553 | |
|
554 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
555 | 0 | "Failed to read all values for %d records in .shx file: %s.", |
556 | 0 | psSHP->nRecords, strerror(errno)); |
557 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
558 | 0 | psSHP->sHooks.Error(szErrorMsg); |
559 | | |
560 | | /* SHX is short or unreadable for some reason. */ |
561 | 0 | psSHP->sHooks.FClose(psSHP->fpSHP); |
562 | 0 | psSHP->sHooks.FClose(psSHP->fpSHX); |
563 | 0 | free(psSHP->panRecOffset); |
564 | 0 | free(psSHP->panRecSize); |
565 | 0 | free(pabyBuf); |
566 | 0 | free(psSHP); |
567 | |
|
568 | 0 | return SHPLIB_NULLPTR; |
569 | 0 | } |
570 | | |
571 | | /* In read-only mode, we can close the SHX now */ |
572 | 0 | if (strcmp(pszAccess, "rb") == 0) |
573 | 0 | { |
574 | 0 | psSHP->sHooks.FClose(psSHP->fpSHX); |
575 | 0 | psSHP->fpSHX = SHPLIB_NULLPTR; |
576 | 0 | } |
577 | |
|
578 | 0 | for (int i = 0; i < psSHP->nRecords; i++) |
579 | 0 | { |
580 | 0 | unsigned int nOffset; |
581 | 0 | memcpy(&nOffset, pabyBuf + i * 8, 4); |
582 | 0 | #if !defined(SHP_BIG_ENDIAN) |
583 | 0 | SHP_SWAP32(&nOffset); |
584 | 0 | #endif |
585 | |
|
586 | 0 | unsigned int nLength; |
587 | 0 | memcpy(&nLength, pabyBuf + i * 8 + 4, 4); |
588 | 0 | #if !defined(SHP_BIG_ENDIAN) |
589 | 0 | SHP_SWAP32(&nLength); |
590 | 0 | #endif |
591 | |
|
592 | 0 | if (nOffset > STATIC_CAST(unsigned int, INT_MAX)) |
593 | 0 | { |
594 | 0 | char str[128]; |
595 | 0 | snprintf(str, sizeof(str), "Invalid offset for entity %d", i); |
596 | 0 | str[sizeof(str) - 1] = '\0'; |
597 | |
|
598 | 0 | psSHP->sHooks.Error(str); |
599 | 0 | SHPClose(psSHP); |
600 | 0 | free(pabyBuf); |
601 | 0 | return SHPLIB_NULLPTR; |
602 | 0 | } |
603 | 0 | if (nLength > STATIC_CAST(unsigned int, INT_MAX / 2 - 4)) |
604 | 0 | { |
605 | 0 | char str[128]; |
606 | 0 | snprintf(str, sizeof(str), "Invalid length for entity %d", i); |
607 | 0 | str[sizeof(str) - 1] = '\0'; |
608 | |
|
609 | 0 | psSHP->sHooks.Error(str); |
610 | 0 | SHPClose(psSHP); |
611 | 0 | free(pabyBuf); |
612 | 0 | return SHPLIB_NULLPTR; |
613 | 0 | } |
614 | 0 | psSHP->panRecOffset[i] = nOffset * 2; |
615 | 0 | psSHP->panRecSize[i] = nLength * 2; |
616 | 0 | } |
617 | 0 | free(pabyBuf); |
618 | |
|
619 | 0 | return (psSHP); |
620 | 0 | } |
621 | | |
622 | | /************************************************************************/ |
623 | | /* SHPOpenLLEx() */ |
624 | | /* */ |
625 | | /* Open the .shp and .shx files based on the basename of the */ |
626 | | /* files or either file name. It generally invokes SHPRestoreSHX() */ |
627 | | /* in case when bRestoreSHX equals true. */ |
628 | | /************************************************************************/ |
629 | | |
630 | | SHPHandle SHPAPI_CALL SHPOpenLLEx(const char *pszLayer, const char *pszAccess, |
631 | | const SAHooks *psHooks, int bRestoreSHX) |
632 | 0 | { |
633 | 0 | if (!bRestoreSHX) |
634 | 0 | return SHPOpenLL(pszLayer, pszAccess, psHooks); |
635 | 0 | else |
636 | 0 | { |
637 | 0 | if (SHPRestoreSHX(pszLayer, pszAccess, psHooks)) |
638 | 0 | { |
639 | 0 | return SHPOpenLL(pszLayer, pszAccess, psHooks); |
640 | 0 | } |
641 | 0 | } |
642 | | |
643 | 0 | return SHPLIB_NULLPTR; |
644 | 0 | } |
645 | | |
646 | | /************************************************************************/ |
647 | | /* SHPRestoreSHX() */ |
648 | | /* */ |
649 | | /* Restore .SHX file using associated .SHP file. */ |
650 | | /* */ |
651 | | /************************************************************************/ |
652 | | |
653 | | int SHPAPI_CALL SHPRestoreSHX(const char *pszLayer, const char *pszAccess, |
654 | | const SAHooks *psHooks) |
655 | 0 | { |
656 | | /* -------------------------------------------------------------------- */ |
657 | | /* Ensure the access string is one of the legal ones. We */ |
658 | | /* ensure the result string indicates binary to avoid common */ |
659 | | /* problems on Windows. */ |
660 | | /* -------------------------------------------------------------------- */ |
661 | 0 | if (strcmp(pszAccess, "rb+") == 0 || strcmp(pszAccess, "r+b") == 0 || |
662 | 0 | strcmp(pszAccess, "r+") == 0) |
663 | 0 | { |
664 | 0 | pszAccess = "r+b"; |
665 | 0 | } |
666 | 0 | else |
667 | 0 | { |
668 | 0 | pszAccess = "rb"; |
669 | 0 | } |
670 | | |
671 | | /* -------------------------------------------------------------------- */ |
672 | | /* Open the .shp file. Note that files pulled from */ |
673 | | /* a PC to Unix with upper case filenames won't work! */ |
674 | | /* -------------------------------------------------------------------- */ |
675 | 0 | const int nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer); |
676 | 0 | char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5)); |
677 | 0 | if (!pszFullname) |
678 | 0 | return 0; |
679 | 0 | memcpy(pszFullname, pszLayer, nLenWithoutExtension); |
680 | 0 | memcpy(pszFullname + nLenWithoutExtension, ".shp", 5); |
681 | 0 | SAFile fpSHP = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData); |
682 | 0 | if (fpSHP == SHPLIB_NULLPTR) |
683 | 0 | { |
684 | 0 | memcpy(pszFullname + nLenWithoutExtension, ".SHP", 5); |
685 | 0 | fpSHP = psHooks->FOpen(pszFullname, pszAccess, psHooks->pvUserData); |
686 | 0 | } |
687 | |
|
688 | 0 | if (fpSHP == SHPLIB_NULLPTR) |
689 | 0 | { |
690 | 0 | const size_t nMessageLen = strlen(pszFullname) * 2 + 256; |
691 | 0 | char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen)); |
692 | 0 | if (pszMessage) |
693 | 0 | { |
694 | 0 | pszFullname[nLenWithoutExtension] = 0; |
695 | 0 | snprintf(pszMessage, nMessageLen, |
696 | 0 | "Unable to open %s.shp or %s.SHP.", pszFullname, |
697 | 0 | pszFullname); |
698 | 0 | psHooks->Error(pszMessage); |
699 | 0 | free(pszMessage); |
700 | 0 | } |
701 | |
|
702 | 0 | free(pszFullname); |
703 | |
|
704 | 0 | return (0); |
705 | 0 | } |
706 | | |
707 | | /* -------------------------------------------------------------------- */ |
708 | | /* Read the file size from the SHP file. */ |
709 | | /* -------------------------------------------------------------------- */ |
710 | 0 | unsigned char *pabyBuf = STATIC_CAST(unsigned char *, malloc(100)); |
711 | 0 | if (psHooks->FRead(pabyBuf, 100, 1, fpSHP) != 1) |
712 | 0 | { |
713 | 0 | psHooks->Error(".shp file is unreadable, or corrupt."); |
714 | 0 | psHooks->FClose(fpSHP); |
715 | |
|
716 | 0 | free(pabyBuf); |
717 | 0 | free(pszFullname); |
718 | |
|
719 | 0 | return (0); |
720 | 0 | } |
721 | | |
722 | 0 | unsigned int nSHPFilesize = (STATIC_CAST(unsigned int, pabyBuf[24]) << 24) | |
723 | 0 | (pabyBuf[25] << 16) | (pabyBuf[26] << 8) | |
724 | 0 | pabyBuf[27]; |
725 | 0 | if (nSHPFilesize < UINT_MAX / 2) |
726 | 0 | nSHPFilesize *= 2; |
727 | 0 | else |
728 | 0 | nSHPFilesize = (UINT_MAX / 2) * 2; |
729 | |
|
730 | 0 | memcpy(pszFullname + nLenWithoutExtension, ".shx", 5); |
731 | 0 | const char pszSHXAccess[] = "w+b"; |
732 | 0 | SAFile fpSHX = |
733 | 0 | psHooks->FOpen(pszFullname, pszSHXAccess, psHooks->pvUserData); |
734 | 0 | if (fpSHX == SHPLIB_NULLPTR) |
735 | 0 | { |
736 | 0 | size_t nMessageLen = strlen(pszFullname) * 2 + 256; |
737 | 0 | char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen)); |
738 | 0 | if (pszMessage) |
739 | 0 | { |
740 | 0 | pszFullname[nLenWithoutExtension] = 0; |
741 | 0 | snprintf(pszMessage, nMessageLen, |
742 | 0 | "Error opening file %s.shx for writing", pszFullname); |
743 | 0 | psHooks->Error(pszMessage); |
744 | 0 | free(pszMessage); |
745 | 0 | } |
746 | |
|
747 | 0 | psHooks->FClose(fpSHP); |
748 | |
|
749 | 0 | free(pabyBuf); |
750 | 0 | free(pszFullname); |
751 | |
|
752 | 0 | return (0); |
753 | 0 | } |
754 | | |
755 | | /* -------------------------------------------------------------------- */ |
756 | | /* Open SHX and create it using SHP file content. */ |
757 | | /* -------------------------------------------------------------------- */ |
758 | 0 | psHooks->FSeek(fpSHP, 100, 0); |
759 | 0 | char *pabySHXHeader = STATIC_CAST(char *, malloc(100)); |
760 | 0 | if (!pabySHXHeader) |
761 | 0 | { |
762 | 0 | psHooks->FClose(fpSHP); |
763 | |
|
764 | 0 | free(pabyBuf); |
765 | 0 | free(pszFullname); |
766 | |
|
767 | 0 | return (0); |
768 | 0 | } |
769 | 0 | memcpy(pabySHXHeader, pabyBuf, 100); |
770 | 0 | psHooks->FWrite(pabySHXHeader, 100, 1, fpSHX); |
771 | 0 | free(pabyBuf); |
772 | | |
773 | | // unsigned int nCurrentRecordOffset = 0; |
774 | 0 | unsigned int nCurrentSHPOffset = 100; |
775 | 0 | unsigned int nRealSHXContentSize = 100; |
776 | 0 | int nRetCode = TRUE; |
777 | 0 | unsigned int nRecordOffset = 50; |
778 | |
|
779 | 0 | while (nCurrentSHPOffset < nSHPFilesize) |
780 | 0 | { |
781 | 0 | unsigned int niRecord = 0; |
782 | 0 | unsigned int nRecordLength = 0; |
783 | 0 | int nSHPType; |
784 | |
|
785 | 0 | if (psHooks->FRead(&niRecord, 4, 1, fpSHP) == 1 && |
786 | 0 | psHooks->FRead(&nRecordLength, 4, 1, fpSHP) == 1 && |
787 | 0 | psHooks->FRead(&nSHPType, 4, 1, fpSHP) == 1) |
788 | 0 | { |
789 | 0 | char abyReadRecord[8]; |
790 | 0 | unsigned int nRecordOffsetBE = nRecordOffset; |
791 | |
|
792 | 0 | #if !defined(SHP_BIG_ENDIAN) |
793 | 0 | SHP_SWAP32(&nRecordOffsetBE); |
794 | 0 | #endif |
795 | 0 | memcpy(abyReadRecord, &nRecordOffsetBE, 4); |
796 | 0 | memcpy(abyReadRecord + 4, &nRecordLength, 4); |
797 | |
|
798 | 0 | #if !defined(SHP_BIG_ENDIAN) |
799 | 0 | SHP_SWAP32(&nRecordLength); |
800 | 0 | #endif |
801 | | #if defined(SHP_BIG_ENDIAN) |
802 | | SHP_SWAP32(&nSHPType); |
803 | | #endif |
804 | | |
805 | | // Sanity check on record length |
806 | 0 | if (nRecordLength < 1 || |
807 | 0 | nRecordLength > (nSHPFilesize - (nCurrentSHPOffset + 8)) / 2) |
808 | 0 | { |
809 | 0 | char szErrorMsg[200]; |
810 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
811 | 0 | "Error parsing .shp to restore .shx. " |
812 | 0 | "Invalid record length = %u at record starting at " |
813 | 0 | "offset %u", |
814 | 0 | nRecordLength, nCurrentSHPOffset); |
815 | 0 | psHooks->Error(szErrorMsg); |
816 | |
|
817 | 0 | nRetCode = FALSE; |
818 | 0 | break; |
819 | 0 | } |
820 | | |
821 | | // Sanity check on record type |
822 | 0 | if (nSHPType != SHPT_NULL && nSHPType != SHPT_POINT && |
823 | 0 | nSHPType != SHPT_ARC && nSHPType != SHPT_POLYGON && |
824 | 0 | nSHPType != SHPT_MULTIPOINT && nSHPType != SHPT_POINTZ && |
825 | 0 | nSHPType != SHPT_ARCZ && nSHPType != SHPT_POLYGONZ && |
826 | 0 | nSHPType != SHPT_MULTIPOINTZ && nSHPType != SHPT_POINTM && |
827 | 0 | nSHPType != SHPT_ARCM && nSHPType != SHPT_POLYGONM && |
828 | 0 | nSHPType != SHPT_MULTIPOINTM && nSHPType != SHPT_MULTIPATCH) |
829 | 0 | { |
830 | 0 | char szErrorMsg[200]; |
831 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
832 | 0 | "Error parsing .shp to restore .shx. " |
833 | 0 | "Invalid shape type = %d at record starting at " |
834 | 0 | "offset %u", |
835 | 0 | nSHPType, nCurrentSHPOffset); |
836 | 0 | psHooks->Error(szErrorMsg); |
837 | |
|
838 | 0 | nRetCode = FALSE; |
839 | 0 | break; |
840 | 0 | } |
841 | | |
842 | 0 | psHooks->FWrite(abyReadRecord, 8, 1, fpSHX); |
843 | |
|
844 | 0 | nRecordOffset += nRecordLength + 4; |
845 | | // nCurrentRecordOffset += 8; |
846 | 0 | nCurrentSHPOffset += 8 + nRecordLength * 2; |
847 | |
|
848 | 0 | psHooks->FSeek(fpSHP, nCurrentSHPOffset, 0); |
849 | 0 | nRealSHXContentSize += 8; |
850 | 0 | } |
851 | 0 | else |
852 | 0 | { |
853 | 0 | char szErrorMsg[200]; |
854 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
855 | 0 | "Error parsing .shp to restore .shx. " |
856 | 0 | "Cannot read first bytes of record starting at " |
857 | 0 | "offset %u", |
858 | 0 | nCurrentSHPOffset); |
859 | 0 | psHooks->Error(szErrorMsg); |
860 | |
|
861 | 0 | nRetCode = FALSE; |
862 | 0 | break; |
863 | 0 | } |
864 | 0 | } |
865 | 0 | if (nRetCode && nCurrentSHPOffset != nSHPFilesize) |
866 | 0 | { |
867 | 0 | psHooks->Error("Error parsing .shp to restore .shx. " |
868 | 0 | "Not expected number of bytes"); |
869 | |
|
870 | 0 | nRetCode = FALSE; |
871 | 0 | } |
872 | |
|
873 | 0 | nRealSHXContentSize /= 2; // Bytes counted -> WORDs |
874 | 0 | #if !defined(SHP_BIG_ENDIAN) |
875 | 0 | SHP_SWAP32(&nRealSHXContentSize); |
876 | 0 | #endif |
877 | |
|
878 | 0 | psHooks->FSeek(fpSHX, 24, 0); |
879 | 0 | psHooks->FWrite(&nRealSHXContentSize, 4, 1, fpSHX); |
880 | |
|
881 | 0 | psHooks->FClose(fpSHP); |
882 | 0 | psHooks->FClose(fpSHX); |
883 | |
|
884 | 0 | free(pszFullname); |
885 | 0 | free(pabySHXHeader); |
886 | |
|
887 | 0 | return nRetCode; |
888 | 0 | } |
889 | | |
890 | | /************************************************************************/ |
891 | | /* SHPClose() */ |
892 | | /* */ |
893 | | /* Close the .shp and .shx files. */ |
894 | | /************************************************************************/ |
895 | | |
896 | | void SHPAPI_CALL SHPClose(SHPHandle psSHP) |
897 | 0 | { |
898 | 0 | if (psSHP == SHPLIB_NULLPTR) |
899 | 0 | return; |
900 | | |
901 | | /* -------------------------------------------------------------------- */ |
902 | | /* Update the header if we have modified anything. */ |
903 | | /* -------------------------------------------------------------------- */ |
904 | 0 | if (psSHP->bUpdated) |
905 | 0 | SHPWriteHeader(psSHP); |
906 | | |
907 | | /* -------------------------------------------------------------------- */ |
908 | | /* Free all resources, and close files. */ |
909 | | /* -------------------------------------------------------------------- */ |
910 | 0 | free(psSHP->panRecOffset); |
911 | 0 | free(psSHP->panRecSize); |
912 | |
|
913 | 0 | if (psSHP->fpSHX != SHPLIB_NULLPTR) |
914 | 0 | psSHP->sHooks.FClose(psSHP->fpSHX); |
915 | 0 | psSHP->sHooks.FClose(psSHP->fpSHP); |
916 | |
|
917 | 0 | if (psSHP->pabyRec != SHPLIB_NULLPTR) |
918 | 0 | { |
919 | 0 | free(psSHP->pabyRec); |
920 | 0 | } |
921 | |
|
922 | 0 | if (psSHP->pabyObjectBuf != SHPLIB_NULLPTR) |
923 | 0 | { |
924 | 0 | free(psSHP->pabyObjectBuf); |
925 | 0 | } |
926 | 0 | if (psSHP->psCachedObject != SHPLIB_NULLPTR) |
927 | 0 | { |
928 | 0 | free(psSHP->psCachedObject); |
929 | 0 | } |
930 | |
|
931 | 0 | free(psSHP); |
932 | 0 | } |
933 | | |
934 | | /************************************************************************/ |
935 | | /* SHPSetFastModeReadObject() */ |
936 | | /************************************************************************/ |
937 | | |
938 | | /* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the SHPHandle. */ |
939 | | /* So you cannot have 2 valid instances of SHPReadObject() simultaneously. */ |
940 | | /* The SHPObject padfZ and padfM members may be NULL depending on the geometry */ |
941 | | /* type. It is illegal to free at hand any of the pointer members of the SHPObject structure */ |
942 | | void SHPAPI_CALL SHPSetFastModeReadObject(SHPHandle hSHP, int bFastMode) |
943 | 0 | { |
944 | 0 | if (bFastMode) |
945 | 0 | { |
946 | 0 | if (hSHP->psCachedObject == SHPLIB_NULLPTR) |
947 | 0 | { |
948 | 0 | hSHP->psCachedObject = |
949 | 0 | STATIC_CAST(SHPObject *, calloc(1, sizeof(SHPObject))); |
950 | 0 | assert(hSHP->psCachedObject != SHPLIB_NULLPTR); |
951 | 0 | } |
952 | 0 | } |
953 | | |
954 | 0 | hSHP->bFastModeReadObject = bFastMode; |
955 | 0 | } |
956 | | |
957 | | /************************************************************************/ |
958 | | /* SHPGetInfo() */ |
959 | | /* */ |
960 | | /* Fetch general information about the shape file. */ |
961 | | /************************************************************************/ |
962 | | |
963 | | void SHPAPI_CALL SHPGetInfo(const SHPHandle psSHP, int *pnEntities, |
964 | | int *pnShapeType, double *padfMinBound, |
965 | | double *padfMaxBound) |
966 | 0 | { |
967 | 0 | if (psSHP == SHPLIB_NULLPTR) |
968 | 0 | return; |
969 | | |
970 | 0 | if (pnEntities != SHPLIB_NULLPTR) |
971 | 0 | *pnEntities = psSHP->nRecords; |
972 | |
|
973 | 0 | if (pnShapeType != SHPLIB_NULLPTR) |
974 | 0 | *pnShapeType = psSHP->nShapeType; |
975 | |
|
976 | 0 | for (int i = 0; i < 4; i++) |
977 | 0 | { |
978 | 0 | if (padfMinBound != SHPLIB_NULLPTR) |
979 | 0 | padfMinBound[i] = psSHP->adBoundsMin[i]; |
980 | 0 | if (padfMaxBound != SHPLIB_NULLPTR) |
981 | 0 | padfMaxBound[i] = psSHP->adBoundsMax[i]; |
982 | 0 | } |
983 | 0 | } |
984 | | |
985 | | /************************************************************************/ |
986 | | /* SHPCreate() */ |
987 | | /* */ |
988 | | /* Create a new shape file and return a handle to the open */ |
989 | | /* shape file with read/write access. */ |
990 | | /************************************************************************/ |
991 | | |
992 | | SHPHandle SHPAPI_CALL SHPCreate(const char *pszLayer, int nShapeType) |
993 | 0 | { |
994 | 0 | SAHooks sHooks; |
995 | |
|
996 | 0 | SASetupDefaultHooks(&sHooks); |
997 | |
|
998 | 0 | return SHPCreateLL(pszLayer, nShapeType, &sHooks); |
999 | 0 | } |
1000 | | |
1001 | | /************************************************************************/ |
1002 | | /* SHPCreate() */ |
1003 | | /* */ |
1004 | | /* Create a new shape file and return a handle to the open */ |
1005 | | /* shape file with read/write access. */ |
1006 | | /************************************************************************/ |
1007 | | |
1008 | | SHPHandle SHPAPI_CALL SHPCreateLL(const char *pszLayer, int nShapeType, |
1009 | | const SAHooks *psHooks) |
1010 | 0 | { |
1011 | |
|
1012 | 0 | SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(1, sizeof(SHPInfo))); |
1013 | 0 | if (!psSHP) |
1014 | 0 | return SHPLIB_NULLPTR; |
1015 | | |
1016 | | /* -------------------------------------------------------------------- */ |
1017 | | /* Open the two files so we can write their headers. */ |
1018 | | /* -------------------------------------------------------------------- */ |
1019 | 0 | const int nLenWithoutExtension = SHPGetLenWithoutExtension(pszLayer); |
1020 | 0 | char *pszFullname = STATIC_CAST(char *, malloc(nLenWithoutExtension + 5)); |
1021 | 0 | if (!pszFullname) |
1022 | 0 | { |
1023 | 0 | free(psSHP); |
1024 | 0 | return SHPLIB_NULLPTR; |
1025 | 0 | } |
1026 | 0 | memcpy(pszFullname, pszLayer, nLenWithoutExtension); |
1027 | 0 | memcpy(pszFullname + nLenWithoutExtension, ".shp", 5); |
1028 | 0 | SAFile fpSHP = psHooks->FOpen(pszFullname, "w+b", psHooks->pvUserData); |
1029 | 0 | if (fpSHP == SHPLIB_NULLPTR) |
1030 | 0 | { |
1031 | 0 | const size_t nMessageLen = strlen(pszFullname) + 256; |
1032 | 0 | char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen)); |
1033 | 0 | if (pszMessage) |
1034 | 0 | { |
1035 | 0 | snprintf(pszMessage, nMessageLen, "Failed to create file %s: %s", |
1036 | 0 | pszFullname, strerror(errno)); |
1037 | 0 | psHooks->Error(pszMessage); |
1038 | 0 | free(pszMessage); |
1039 | 0 | } |
1040 | 0 | free(pszFullname); |
1041 | 0 | free(psSHP); |
1042 | 0 | return SHPLIB_NULLPTR; |
1043 | 0 | } |
1044 | | |
1045 | 0 | memcpy(pszFullname + nLenWithoutExtension, ".shx", 5); |
1046 | 0 | SAFile fpSHX = psHooks->FOpen(pszFullname, "w+b", psHooks->pvUserData); |
1047 | 0 | if (fpSHX == SHPLIB_NULLPTR) |
1048 | 0 | { |
1049 | 0 | const size_t nMessageLen = strlen(pszFullname) + 256; |
1050 | 0 | char *pszMessage = STATIC_CAST(char *, malloc(nMessageLen)); |
1051 | 0 | if (pszMessage) |
1052 | 0 | { |
1053 | 0 | snprintf(pszMessage, nMessageLen, "Failed to create file %s: %s", |
1054 | 0 | pszFullname, strerror(errno)); |
1055 | 0 | psHooks->Error(pszMessage); |
1056 | 0 | free(pszMessage); |
1057 | 0 | } |
1058 | |
|
1059 | 0 | free(pszFullname); |
1060 | 0 | psHooks->FClose(fpSHP); |
1061 | 0 | free(psSHP); |
1062 | 0 | return SHPLIB_NULLPTR; |
1063 | 0 | } |
1064 | | |
1065 | 0 | free(pszFullname); |
1066 | 0 | pszFullname = SHPLIB_NULLPTR; |
1067 | | |
1068 | | /* -------------------------------------------------------------------- */ |
1069 | | /* Prepare header block for .shp file. */ |
1070 | | /* -------------------------------------------------------------------- */ |
1071 | 0 | unsigned char abyHeader[100]; |
1072 | 0 | memset(abyHeader, 0, sizeof(abyHeader)); |
1073 | |
|
1074 | 0 | abyHeader[2] = 0x27; /* magic cookie */ |
1075 | 0 | abyHeader[3] = 0x0a; |
1076 | |
|
1077 | 0 | uint32_t i32 = 50; /* file size */ |
1078 | 0 | ByteCopy(&i32, abyHeader + 24, 4); |
1079 | 0 | #if !defined(SHP_BIG_ENDIAN) |
1080 | 0 | SHP_SWAP32(abyHeader + 24); |
1081 | 0 | #endif |
1082 | |
|
1083 | 0 | i32 = 1000; /* version */ |
1084 | 0 | ByteCopy(&i32, abyHeader + 28, 4); |
1085 | | #if defined(SHP_BIG_ENDIAN) |
1086 | | SHP_SWAP32(abyHeader + 28); |
1087 | | #endif |
1088 | |
|
1089 | 0 | i32 = nShapeType; /* shape type */ |
1090 | 0 | ByteCopy(&i32, abyHeader + 32, 4); |
1091 | | #if defined(SHP_BIG_ENDIAN) |
1092 | | SHP_SWAP32(abyHeader + 32); |
1093 | | #endif |
1094 | |
|
1095 | 0 | double dValue = 0.0; /* set bounds */ |
1096 | 0 | ByteCopy(&dValue, abyHeader + 36, 8); |
1097 | 0 | ByteCopy(&dValue, abyHeader + 44, 8); |
1098 | 0 | ByteCopy(&dValue, abyHeader + 52, 8); |
1099 | 0 | ByteCopy(&dValue, abyHeader + 60, 8); |
1100 | | |
1101 | | /* -------------------------------------------------------------------- */ |
1102 | | /* Write .shp file header. */ |
1103 | | /* -------------------------------------------------------------------- */ |
1104 | 0 | if (psHooks->FWrite(abyHeader, 100, 1, fpSHP) != 1) |
1105 | 0 | { |
1106 | 0 | char szErrorMsg[200]; |
1107 | |
|
1108 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
1109 | 0 | "Failed to write .shp header: %s", strerror(errno)); |
1110 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
1111 | 0 | psHooks->Error(szErrorMsg); |
1112 | |
|
1113 | 0 | free(pszFullname); |
1114 | 0 | psHooks->FClose(fpSHP); |
1115 | 0 | psHooks->FClose(fpSHX); |
1116 | 0 | free(psSHP); |
1117 | 0 | return SHPLIB_NULLPTR; |
1118 | 0 | } |
1119 | | |
1120 | | /* -------------------------------------------------------------------- */ |
1121 | | /* Prepare, and write .shx file header. */ |
1122 | | /* -------------------------------------------------------------------- */ |
1123 | 0 | i32 = 50; /* file size */ |
1124 | 0 | ByteCopy(&i32, abyHeader + 24, 4); |
1125 | 0 | #if !defined(SHP_BIG_ENDIAN) |
1126 | 0 | SHP_SWAP32(abyHeader + 24); |
1127 | 0 | #endif |
1128 | |
|
1129 | 0 | if (psHooks->FWrite(abyHeader, 100, 1, fpSHX) != 1) |
1130 | 0 | { |
1131 | 0 | char szErrorMsg[200]; |
1132 | |
|
1133 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
1134 | 0 | "Failure writing .shx header: %s", strerror(errno)); |
1135 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
1136 | 0 | psHooks->Error(szErrorMsg); |
1137 | |
|
1138 | 0 | free(pszFullname); |
1139 | 0 | psHooks->FClose(fpSHP); |
1140 | 0 | psHooks->FClose(fpSHX); |
1141 | 0 | free(psSHP); |
1142 | 0 | return SHPLIB_NULLPTR; |
1143 | 0 | } |
1144 | | |
1145 | 0 | psSHP->bUpdated = FALSE; |
1146 | 0 | memcpy(&(psSHP->sHooks), psHooks, sizeof(SAHooks)); |
1147 | |
|
1148 | 0 | psSHP->fpSHP = fpSHP; |
1149 | 0 | psSHP->fpSHX = fpSHX; |
1150 | 0 | psSHP->nShapeType = nShapeType; |
1151 | 0 | psSHP->nFileSize = 100; |
1152 | 0 | psSHP->panRecOffset = |
1153 | 0 | STATIC_CAST(unsigned int *, malloc(sizeof(unsigned int))); |
1154 | 0 | psSHP->panRecSize = |
1155 | 0 | STATIC_CAST(unsigned int *, malloc(sizeof(unsigned int))); |
1156 | |
|
1157 | 0 | if (psSHP->panRecOffset == SHPLIB_NULLPTR || |
1158 | 0 | psSHP->panRecSize == SHPLIB_NULLPTR) |
1159 | 0 | { |
1160 | 0 | psSHP->sHooks.Error("Not enough memory to allocate requested memory"); |
1161 | 0 | psSHP->sHooks.FClose(psSHP->fpSHP); |
1162 | 0 | psSHP->sHooks.FClose(psSHP->fpSHX); |
1163 | 0 | if (psSHP->panRecOffset) |
1164 | 0 | free(psSHP->panRecOffset); |
1165 | 0 | if (psSHP->panRecSize) |
1166 | 0 | free(psSHP->panRecSize); |
1167 | 0 | free(psSHP); |
1168 | 0 | return SHPLIB_NULLPTR; |
1169 | 0 | } |
1170 | | |
1171 | 0 | return psSHP; |
1172 | 0 | } |
1173 | | |
1174 | | /************************************************************************/ |
1175 | | /* _SHPSetBounds() */ |
1176 | | /* */ |
1177 | | /* Compute a bounds rectangle for a shape, and set it into the */ |
1178 | | /* indicated location in the record. */ |
1179 | | /************************************************************************/ |
1180 | | |
1181 | | static void _SHPSetBounds(unsigned char *pabyRec, const SHPObject *psShape) |
1182 | 0 | { |
1183 | 0 | ByteCopy(&(psShape->dfXMin), pabyRec + 0, 8); |
1184 | 0 | ByteCopy(&(psShape->dfYMin), pabyRec + 8, 8); |
1185 | 0 | ByteCopy(&(psShape->dfXMax), pabyRec + 16, 8); |
1186 | 0 | ByteCopy(&(psShape->dfYMax), pabyRec + 24, 8); |
1187 | |
|
1188 | | #if defined(SHP_BIG_ENDIAN) |
1189 | | SHP_SWAP64(pabyRec + 0); |
1190 | | SHP_SWAP64(pabyRec + 8); |
1191 | | SHP_SWAP64(pabyRec + 16); |
1192 | | SHP_SWAP64(pabyRec + 24); |
1193 | | #endif |
1194 | 0 | } |
1195 | | |
1196 | | /************************************************************************/ |
1197 | | /* SHPComputeExtents() */ |
1198 | | /* */ |
1199 | | /* Recompute the extents of a shape. Automatically done by */ |
1200 | | /* SHPCreateObject(). */ |
1201 | | /************************************************************************/ |
1202 | | |
1203 | | void SHPAPI_CALL SHPComputeExtents(SHPObject *psObject) |
1204 | 0 | { |
1205 | | /* -------------------------------------------------------------------- */ |
1206 | | /* Build extents for this object. */ |
1207 | | /* -------------------------------------------------------------------- */ |
1208 | 0 | if (psObject->nVertices > 0) |
1209 | 0 | { |
1210 | 0 | psObject->dfXMin = psObject->dfXMax = psObject->padfX[0]; |
1211 | 0 | psObject->dfYMin = psObject->dfYMax = psObject->padfY[0]; |
1212 | 0 | psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0]; |
1213 | 0 | psObject->dfMMin = psObject->dfMMax = psObject->padfM[0]; |
1214 | 0 | } |
1215 | |
|
1216 | 0 | for (int i = 0; i < psObject->nVertices; i++) |
1217 | 0 | { |
1218 | 0 | psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]); |
1219 | 0 | psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]); |
1220 | 0 | psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]); |
1221 | 0 | psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]); |
1222 | |
|
1223 | 0 | psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]); |
1224 | 0 | psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]); |
1225 | 0 | psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]); |
1226 | 0 | psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]); |
1227 | 0 | } |
1228 | 0 | } |
1229 | | |
1230 | | /************************************************************************/ |
1231 | | /* SHPCreateObject() */ |
1232 | | /* */ |
1233 | | /* Create a shape object. It should be freed with */ |
1234 | | /* SHPDestroyObject(). */ |
1235 | | /************************************************************************/ |
1236 | | |
1237 | | SHPObject SHPAPI_CALL1(*) |
1238 | | SHPCreateObject(int nSHPType, int nShapeId, int nParts, |
1239 | | const int *panPartStart, const int *panPartType, |
1240 | | int nVertices, const double *padfX, const double *padfY, |
1241 | | const double *padfZ, const double *padfM) |
1242 | 0 | { |
1243 | 0 | SHPObject *psObject = |
1244 | 0 | STATIC_CAST(SHPObject *, calloc(1, sizeof(SHPObject))); |
1245 | 0 | if (!psObject) |
1246 | 0 | return SHPLIB_NULLPTR; |
1247 | 0 | psObject->nSHPType = nSHPType; |
1248 | 0 | psObject->nShapeId = nShapeId; |
1249 | 0 | psObject->bMeasureIsUsed = FALSE; |
1250 | | |
1251 | | /* -------------------------------------------------------------------- */ |
1252 | | /* Establish whether this shape type has M, and Z values. */ |
1253 | | /* -------------------------------------------------------------------- */ |
1254 | 0 | bool bHasM; |
1255 | 0 | bool bHasZ; |
1256 | |
|
1257 | 0 | if (nSHPType == SHPT_ARCM || nSHPType == SHPT_POINTM || |
1258 | 0 | nSHPType == SHPT_POLYGONM || nSHPType == SHPT_MULTIPOINTM) |
1259 | 0 | { |
1260 | 0 | bHasM = true; |
1261 | 0 | bHasZ = false; |
1262 | 0 | } |
1263 | 0 | else if (nSHPType == SHPT_ARCZ || nSHPType == SHPT_POINTZ || |
1264 | 0 | nSHPType == SHPT_POLYGONZ || nSHPType == SHPT_MULTIPOINTZ || |
1265 | 0 | nSHPType == SHPT_MULTIPATCH) |
1266 | 0 | { |
1267 | 0 | bHasM = true; |
1268 | 0 | bHasZ = true; |
1269 | 0 | } |
1270 | 0 | else |
1271 | 0 | { |
1272 | 0 | bHasM = false; |
1273 | 0 | bHasZ = false; |
1274 | 0 | } |
1275 | | |
1276 | | /* -------------------------------------------------------------------- */ |
1277 | | /* Capture parts. Note that part type is optional, and */ |
1278 | | /* defaults to ring. */ |
1279 | | /* -------------------------------------------------------------------- */ |
1280 | 0 | if (nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON || |
1281 | 0 | nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM || |
1282 | 0 | nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ || |
1283 | 0 | nSHPType == SHPT_MULTIPATCH) |
1284 | 0 | { |
1285 | 0 | psObject->nParts = MAX(1, nParts); |
1286 | |
|
1287 | 0 | psObject->panPartStart = |
1288 | 0 | STATIC_CAST(int *, calloc(psObject->nParts, sizeof(int))); |
1289 | 0 | psObject->panPartType = |
1290 | 0 | STATIC_CAST(int *, malloc(sizeof(int) * psObject->nParts)); |
1291 | 0 | if (!psObject->panPartStart || !psObject->panPartType) |
1292 | 0 | { |
1293 | 0 | free(psObject->panPartStart); |
1294 | 0 | free(psObject->panPartType); |
1295 | 0 | free(psObject); |
1296 | 0 | return SHPLIB_NULLPTR; |
1297 | 0 | } |
1298 | | |
1299 | 0 | psObject->panPartStart[0] = 0; |
1300 | 0 | psObject->panPartType[0] = SHPP_RING; |
1301 | |
|
1302 | 0 | for (int i = 0; i < nParts; i++) |
1303 | 0 | { |
1304 | 0 | if (panPartStart != SHPLIB_NULLPTR) |
1305 | 0 | psObject->panPartStart[i] = panPartStart[i]; |
1306 | |
|
1307 | 0 | if (panPartType != SHPLIB_NULLPTR) |
1308 | 0 | psObject->panPartType[i] = panPartType[i]; |
1309 | 0 | else |
1310 | 0 | psObject->panPartType[i] = SHPP_RING; |
1311 | 0 | } |
1312 | |
|
1313 | 0 | psObject->panPartStart[0] = 0; |
1314 | 0 | } |
1315 | | |
1316 | | /* -------------------------------------------------------------------- */ |
1317 | | /* Capture vertices. Note that X, Y, Z and M are optional. */ |
1318 | | /* -------------------------------------------------------------------- */ |
1319 | 0 | if (nVertices > 0) |
1320 | 0 | { |
1321 | 0 | const size_t nSize = sizeof(double) * nVertices; |
1322 | 0 | psObject->padfX = |
1323 | 0 | STATIC_CAST(double *, padfX ? malloc(nSize) |
1324 | 0 | : calloc(nVertices, sizeof(double))); |
1325 | 0 | psObject->padfY = |
1326 | 0 | STATIC_CAST(double *, padfY ? malloc(nSize) |
1327 | 0 | : calloc(nVertices, sizeof(double))); |
1328 | 0 | psObject->padfZ = STATIC_CAST( |
1329 | 0 | double *, |
1330 | 0 | padfZ &&bHasZ ? malloc(nSize) : calloc(nVertices, sizeof(double))); |
1331 | 0 | psObject->padfM = STATIC_CAST( |
1332 | 0 | double *, |
1333 | 0 | padfM &&bHasM ? malloc(nSize) : calloc(nVertices, sizeof(double))); |
1334 | 0 | if (!psObject->padfX || !psObject->padfY || !psObject->padfZ || |
1335 | 0 | !psObject->padfM) |
1336 | 0 | { |
1337 | 0 | free(psObject->panPartStart); |
1338 | 0 | free(psObject->panPartType); |
1339 | 0 | free(psObject->padfX); |
1340 | 0 | free(psObject->padfY); |
1341 | 0 | free(psObject->padfZ); |
1342 | 0 | free(psObject->padfM); |
1343 | 0 | free(psObject); |
1344 | 0 | return SHPLIB_NULLPTR; |
1345 | 0 | } |
1346 | 0 | if (padfX != SHPLIB_NULLPTR) |
1347 | 0 | memcpy(psObject->padfX, padfX, nSize); |
1348 | 0 | if (padfY != SHPLIB_NULLPTR) |
1349 | 0 | memcpy(psObject->padfY, padfY, nSize); |
1350 | 0 | if (padfZ != SHPLIB_NULLPTR && bHasZ) |
1351 | 0 | memcpy(psObject->padfZ, padfZ, nSize); |
1352 | 0 | if (padfM != SHPLIB_NULLPTR && bHasM) |
1353 | 0 | { |
1354 | 0 | memcpy(psObject->padfM, padfM, nSize); |
1355 | 0 | psObject->bMeasureIsUsed = TRUE; |
1356 | 0 | } |
1357 | 0 | } |
1358 | | |
1359 | | /* -------------------------------------------------------------------- */ |
1360 | | /* Compute the extents. */ |
1361 | | /* -------------------------------------------------------------------- */ |
1362 | 0 | psObject->nVertices = nVertices; |
1363 | 0 | SHPComputeExtents(psObject); |
1364 | |
|
1365 | 0 | return (psObject); |
1366 | 0 | } |
1367 | | |
1368 | | /************************************************************************/ |
1369 | | /* SHPCreateSimpleObject() */ |
1370 | | /* */ |
1371 | | /* Create a simple (common) shape object. Destroy with */ |
1372 | | /* SHPDestroyObject(). */ |
1373 | | /************************************************************************/ |
1374 | | |
1375 | | SHPObject SHPAPI_CALL1(*) |
1376 | | SHPCreateSimpleObject(int nSHPType, int nVertices, const double *padfX, |
1377 | | const double *padfY, const double *padfZ) |
1378 | 0 | { |
1379 | 0 | return (SHPCreateObject(nSHPType, -1, 0, SHPLIB_NULLPTR, SHPLIB_NULLPTR, |
1380 | 0 | nVertices, padfX, padfY, padfZ, SHPLIB_NULLPTR)); |
1381 | 0 | } |
1382 | | |
1383 | | /************************************************************************/ |
1384 | | /* SHPWriteObject() */ |
1385 | | /* */ |
1386 | | /* Write out the vertices of a new structure. Note that it is */ |
1387 | | /* only possible to write vertices at the end of the file. */ |
1388 | | /************************************************************************/ |
1389 | | |
1390 | | int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, |
1391 | | const SHPObject *psObject) |
1392 | 0 | { |
1393 | 0 | psSHP->bUpdated = TRUE; |
1394 | | |
1395 | | /* -------------------------------------------------------------------- */ |
1396 | | /* Ensure that shape object matches the type of the file it is */ |
1397 | | /* being written to. */ |
1398 | | /* -------------------------------------------------------------------- */ |
1399 | 0 | assert(psObject->nSHPType == psSHP->nShapeType || |
1400 | 0 | psObject->nSHPType == SHPT_NULL); |
1401 | | |
1402 | | /* -------------------------------------------------------------------- */ |
1403 | | /* Ensure that -1 is used for appends. Either blow an */ |
1404 | | /* assertion, or if they are disabled, set the shapeid to -1 */ |
1405 | | /* for appends. */ |
1406 | | /* -------------------------------------------------------------------- */ |
1407 | 0 | assert(nShapeId == -1 || (nShapeId >= 0 && nShapeId < psSHP->nRecords)); |
1408 | | |
1409 | 0 | if (nShapeId != -1 && nShapeId >= psSHP->nRecords) |
1410 | 0 | nShapeId = -1; |
1411 | | |
1412 | | /* -------------------------------------------------------------------- */ |
1413 | | /* Add the new entity to the in memory index. */ |
1414 | | /* -------------------------------------------------------------------- */ |
1415 | 0 | if (nShapeId == -1 && psSHP->nRecords + 1 > psSHP->nMaxRecords) |
1416 | 0 | { |
1417 | | /* This cannot overflow given that we check that the file size does |
1418 | | * not grow over 4 GB, and the minimum size of a record is 12 bytes, |
1419 | | * hence the maximm value for nMaxRecords is 357,913,941 |
1420 | | */ |
1421 | 0 | int nNewMaxRecords = psSHP->nMaxRecords + psSHP->nMaxRecords / 3 + 100; |
1422 | 0 | unsigned int *panRecOffsetNew; |
1423 | 0 | unsigned int *panRecSizeNew; |
1424 | |
|
1425 | 0 | panRecOffsetNew = STATIC_CAST( |
1426 | 0 | unsigned int *, realloc(psSHP->panRecOffset, |
1427 | 0 | sizeof(unsigned int) * nNewMaxRecords)); |
1428 | 0 | if (panRecOffsetNew == SHPLIB_NULLPTR) |
1429 | 0 | { |
1430 | 0 | psSHP->sHooks.Error("Failed to write shape object. " |
1431 | 0 | "Memory allocation error."); |
1432 | 0 | return -1; |
1433 | 0 | } |
1434 | 0 | psSHP->panRecOffset = panRecOffsetNew; |
1435 | |
|
1436 | 0 | panRecSizeNew = STATIC_CAST( |
1437 | 0 | unsigned int *, |
1438 | 0 | realloc(psSHP->panRecSize, sizeof(unsigned int) * nNewMaxRecords)); |
1439 | 0 | if (panRecSizeNew == SHPLIB_NULLPTR) |
1440 | 0 | { |
1441 | 0 | psSHP->sHooks.Error("Failed to write shape object. " |
1442 | 0 | "Memory allocation error."); |
1443 | 0 | return -1; |
1444 | 0 | } |
1445 | 0 | psSHP->panRecSize = panRecSizeNew; |
1446 | |
|
1447 | 0 | psSHP->nMaxRecords = nNewMaxRecords; |
1448 | 0 | } |
1449 | | |
1450 | | /* -------------------------------------------------------------------- */ |
1451 | | /* Initialize record. */ |
1452 | | /* -------------------------------------------------------------------- */ |
1453 | | |
1454 | | /* The following computation cannot overflow on 32-bit platforms given that |
1455 | | * the user had to allocate arrays of at least that size. */ |
1456 | 0 | size_t nRecMaxSize = |
1457 | 0 | psObject->nVertices * 4 * sizeof(double) + psObject->nParts * 8; |
1458 | | /* But the following test could trigger on 64-bit platforms on huge |
1459 | | * geometries. */ |
1460 | 0 | const unsigned nExtraSpaceForGeomHeader = 128; |
1461 | 0 | if (nRecMaxSize > UINT_MAX - nExtraSpaceForGeomHeader) |
1462 | 0 | { |
1463 | 0 | psSHP->sHooks.Error("Failed to write shape object. Too big geometry."); |
1464 | 0 | return -1; |
1465 | 0 | } |
1466 | 0 | nRecMaxSize += nExtraSpaceForGeomHeader; |
1467 | 0 | unsigned char *pabyRec = STATIC_CAST(unsigned char *, malloc(nRecMaxSize)); |
1468 | 0 | if (pabyRec == SHPLIB_NULLPTR) |
1469 | 0 | { |
1470 | 0 | psSHP->sHooks.Error("Failed to write shape object. " |
1471 | 0 | "Memory allocation error."); |
1472 | 0 | return -1; |
1473 | 0 | } |
1474 | | |
1475 | | /* -------------------------------------------------------------------- */ |
1476 | | /* Extract vertices for a Polygon or Arc. */ |
1477 | | /* -------------------------------------------------------------------- */ |
1478 | 0 | unsigned int nRecordSize = 0; |
1479 | 0 | const bool bFirstFeature = psSHP->nRecords == 0; |
1480 | |
|
1481 | 0 | if (psObject->nSHPType == SHPT_POLYGON || |
1482 | 0 | psObject->nSHPType == SHPT_POLYGONZ || |
1483 | 0 | psObject->nSHPType == SHPT_POLYGONM || psObject->nSHPType == SHPT_ARC || |
1484 | 0 | psObject->nSHPType == SHPT_ARCZ || psObject->nSHPType == SHPT_ARCM || |
1485 | 0 | psObject->nSHPType == SHPT_MULTIPATCH) |
1486 | 0 | { |
1487 | 0 | uint32_t nPoints = psObject->nVertices; |
1488 | 0 | uint32_t nParts = psObject->nParts; |
1489 | |
|
1490 | 0 | _SHPSetBounds(pabyRec + 12, psObject); |
1491 | |
|
1492 | | #if defined(SHP_BIG_ENDIAN) |
1493 | | SHP_SWAP32(&nPoints); |
1494 | | SHP_SWAP32(&nParts); |
1495 | | #endif |
1496 | |
|
1497 | 0 | ByteCopy(&nPoints, pabyRec + 40 + 8, 4); |
1498 | 0 | ByteCopy(&nParts, pabyRec + 36 + 8, 4); |
1499 | |
|
1500 | 0 | nRecordSize = 52; |
1501 | | |
1502 | | /* |
1503 | | * Write part start positions. |
1504 | | */ |
1505 | 0 | ByteCopy(psObject->panPartStart, pabyRec + 44 + 8, |
1506 | 0 | 4 * psObject->nParts); |
1507 | 0 | for (int i = 0; i < psObject->nParts; i++) |
1508 | 0 | { |
1509 | | #if defined(SHP_BIG_ENDIAN) |
1510 | | SHP_SWAP32(pabyRec + 44 + 8 + 4 * i); |
1511 | | #endif |
1512 | 0 | nRecordSize += 4; |
1513 | 0 | } |
1514 | | |
1515 | | /* |
1516 | | * Write multipatch part types if needed. |
1517 | | */ |
1518 | 0 | if (psObject->nSHPType == SHPT_MULTIPATCH) |
1519 | 0 | { |
1520 | 0 | memcpy(pabyRec + nRecordSize, psObject->panPartType, |
1521 | 0 | 4 * psObject->nParts); |
1522 | 0 | for (int i = 0; i < psObject->nParts; i++) |
1523 | 0 | { |
1524 | | #if defined(SHP_BIG_ENDIAN) |
1525 | | SHP_SWAP32(pabyRec + nRecordSize); |
1526 | | #endif |
1527 | 0 | nRecordSize += 4; |
1528 | 0 | } |
1529 | 0 | } |
1530 | | |
1531 | | /* |
1532 | | * Write the (x,y) vertex values. |
1533 | | */ |
1534 | 0 | for (int i = 0; i < psObject->nVertices; i++) |
1535 | 0 | { |
1536 | 0 | ByteCopy(psObject->padfX + i, pabyRec + nRecordSize, 8); |
1537 | 0 | ByteCopy(psObject->padfY + i, pabyRec + nRecordSize + 8, 8); |
1538 | |
|
1539 | | #if defined(SHP_BIG_ENDIAN) |
1540 | | SHP_SWAP64(pabyRec + nRecordSize); |
1541 | | SHP_SWAP64(pabyRec + nRecordSize + 8); |
1542 | | #endif |
1543 | |
|
1544 | 0 | nRecordSize += 2 * 8; |
1545 | 0 | } |
1546 | | |
1547 | | /* |
1548 | | * Write the Z coordinates (if any). |
1549 | | */ |
1550 | 0 | if (psObject->nSHPType == SHPT_POLYGONZ || |
1551 | 0 | psObject->nSHPType == SHPT_ARCZ || |
1552 | 0 | psObject->nSHPType == SHPT_MULTIPATCH) |
1553 | 0 | { |
1554 | 0 | ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8); |
1555 | | #if defined(SHP_BIG_ENDIAN) |
1556 | | SHP_SWAP64(pabyRec + nRecordSize); |
1557 | | #endif |
1558 | 0 | nRecordSize += 8; |
1559 | |
|
1560 | 0 | ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8); |
1561 | | #if defined(SHP_BIG_ENDIAN) |
1562 | | SHP_SWAP64(pabyRec + nRecordSize); |
1563 | | #endif |
1564 | 0 | nRecordSize += 8; |
1565 | |
|
1566 | 0 | for (int i = 0; i < psObject->nVertices; i++) |
1567 | 0 | { |
1568 | 0 | ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8); |
1569 | | #if defined(SHP_BIG_ENDIAN) |
1570 | | SHP_SWAP64(pabyRec + nRecordSize); |
1571 | | #endif |
1572 | 0 | nRecordSize += 8; |
1573 | 0 | } |
1574 | 0 | } |
1575 | | |
1576 | | /* |
1577 | | * Write the M values, if any. |
1578 | | */ |
1579 | 0 | if (psObject->bMeasureIsUsed && |
1580 | 0 | (psObject->nSHPType == SHPT_POLYGONM || |
1581 | 0 | psObject->nSHPType == SHPT_ARCM |
1582 | | #ifndef DISABLE_MULTIPATCH_MEASURE |
1583 | | || psObject->nSHPType == SHPT_MULTIPATCH |
1584 | | #endif |
1585 | 0 | || psObject->nSHPType == SHPT_POLYGONZ || |
1586 | 0 | psObject->nSHPType == SHPT_ARCZ)) |
1587 | 0 | { |
1588 | 0 | ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8); |
1589 | | #if defined(SHP_BIG_ENDIAN) |
1590 | | SHP_SWAP64(pabyRec + nRecordSize); |
1591 | | #endif |
1592 | 0 | nRecordSize += 8; |
1593 | |
|
1594 | 0 | ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8); |
1595 | | #if defined(SHP_BIG_ENDIAN) |
1596 | | SHP_SWAP64(pabyRec + nRecordSize); |
1597 | | #endif |
1598 | 0 | nRecordSize += 8; |
1599 | |
|
1600 | 0 | for (int i = 0; i < psObject->nVertices; i++) |
1601 | 0 | { |
1602 | 0 | ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8); |
1603 | | #if defined(SHP_BIG_ENDIAN) |
1604 | | SHP_SWAP64(pabyRec + nRecordSize); |
1605 | | #endif |
1606 | 0 | nRecordSize += 8; |
1607 | 0 | } |
1608 | 0 | } |
1609 | 0 | } |
1610 | | |
1611 | | /* -------------------------------------------------------------------- */ |
1612 | | /* Extract vertices for a MultiPoint. */ |
1613 | | /* -------------------------------------------------------------------- */ |
1614 | 0 | else if (psObject->nSHPType == SHPT_MULTIPOINT || |
1615 | 0 | psObject->nSHPType == SHPT_MULTIPOINTZ || |
1616 | 0 | psObject->nSHPType == SHPT_MULTIPOINTM) |
1617 | 0 | { |
1618 | 0 | uint32_t nPoints = psObject->nVertices; |
1619 | |
|
1620 | 0 | _SHPSetBounds(pabyRec + 12, psObject); |
1621 | |
|
1622 | | #if defined(SHP_BIG_ENDIAN) |
1623 | | SHP_SWAP32(&nPoints); |
1624 | | #endif |
1625 | 0 | ByteCopy(&nPoints, pabyRec + 44, 4); |
1626 | |
|
1627 | 0 | for (int i = 0; i < psObject->nVertices; i++) |
1628 | 0 | { |
1629 | 0 | ByteCopy(psObject->padfX + i, pabyRec + 48 + i * 16, 8); |
1630 | 0 | ByteCopy(psObject->padfY + i, pabyRec + 48 + i * 16 + 8, 8); |
1631 | |
|
1632 | | #if defined(SHP_BIG_ENDIAN) |
1633 | | SHP_SWAP64(pabyRec + 48 + i * 16); |
1634 | | SHP_SWAP64(pabyRec + 48 + i * 16 + 8); |
1635 | | #endif |
1636 | 0 | } |
1637 | |
|
1638 | 0 | nRecordSize = 48 + 16 * psObject->nVertices; |
1639 | |
|
1640 | 0 | if (psObject->nSHPType == SHPT_MULTIPOINTZ) |
1641 | 0 | { |
1642 | 0 | ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8); |
1643 | | #if defined(SHP_BIG_ENDIAN) |
1644 | | SHP_SWAP64(pabyRec + nRecordSize); |
1645 | | #endif |
1646 | 0 | nRecordSize += 8; |
1647 | |
|
1648 | 0 | ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8); |
1649 | | #if defined(SHP_BIG_ENDIAN) |
1650 | | SHP_SWAP64(pabyRec + nRecordSize); |
1651 | | #endif |
1652 | 0 | nRecordSize += 8; |
1653 | |
|
1654 | 0 | for (int i = 0; i < psObject->nVertices; i++) |
1655 | 0 | { |
1656 | 0 | ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8); |
1657 | | #if defined(SHP_BIG_ENDIAN) |
1658 | | SHP_SWAP64(pabyRec + nRecordSize); |
1659 | | #endif |
1660 | 0 | nRecordSize += 8; |
1661 | 0 | } |
1662 | 0 | } |
1663 | |
|
1664 | 0 | if (psObject->bMeasureIsUsed && |
1665 | 0 | (psObject->nSHPType == SHPT_MULTIPOINTZ || |
1666 | 0 | psObject->nSHPType == SHPT_MULTIPOINTM)) |
1667 | 0 | { |
1668 | 0 | ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8); |
1669 | | #if defined(SHP_BIG_ENDIAN) |
1670 | | SHP_SWAP64(pabyRec + nRecordSize); |
1671 | | #endif |
1672 | 0 | nRecordSize += 8; |
1673 | |
|
1674 | 0 | ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8); |
1675 | | #if defined(SHP_BIG_ENDIAN) |
1676 | | SHP_SWAP64(pabyRec + nRecordSize); |
1677 | | #endif |
1678 | 0 | nRecordSize += 8; |
1679 | |
|
1680 | 0 | for (int i = 0; i < psObject->nVertices; i++) |
1681 | 0 | { |
1682 | 0 | ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8); |
1683 | | #if defined(SHP_BIG_ENDIAN) |
1684 | | SHP_SWAP64(pabyRec + nRecordSize); |
1685 | | #endif |
1686 | 0 | nRecordSize += 8; |
1687 | 0 | } |
1688 | 0 | } |
1689 | 0 | } |
1690 | | |
1691 | | /* -------------------------------------------------------------------- */ |
1692 | | /* Write point. */ |
1693 | | /* -------------------------------------------------------------------- */ |
1694 | 0 | else if (psObject->nSHPType == SHPT_POINT || |
1695 | 0 | psObject->nSHPType == SHPT_POINTZ || |
1696 | 0 | psObject->nSHPType == SHPT_POINTM) |
1697 | 0 | { |
1698 | 0 | ByteCopy(psObject->padfX, pabyRec + 12, 8); |
1699 | 0 | ByteCopy(psObject->padfY, pabyRec + 20, 8); |
1700 | |
|
1701 | | #if defined(SHP_BIG_ENDIAN) |
1702 | | SHP_SWAP64(pabyRec + 12); |
1703 | | SHP_SWAP64(pabyRec + 20); |
1704 | | #endif |
1705 | |
|
1706 | 0 | nRecordSize = 28; |
1707 | |
|
1708 | 0 | if (psObject->nSHPType == SHPT_POINTZ) |
1709 | 0 | { |
1710 | 0 | ByteCopy(psObject->padfZ, pabyRec + nRecordSize, 8); |
1711 | | #if defined(SHP_BIG_ENDIAN) |
1712 | | SHP_SWAP64(pabyRec + nRecordSize); |
1713 | | #endif |
1714 | 0 | nRecordSize += 8; |
1715 | 0 | } |
1716 | |
|
1717 | 0 | if (psObject->bMeasureIsUsed && (psObject->nSHPType == SHPT_POINTZ || |
1718 | 0 | psObject->nSHPType == SHPT_POINTM)) |
1719 | 0 | { |
1720 | 0 | ByteCopy(psObject->padfM, pabyRec + nRecordSize, 8); |
1721 | | #if defined(SHP_BIG_ENDIAN) |
1722 | | SHP_SWAP64(pabyRec + nRecordSize); |
1723 | | #endif |
1724 | 0 | nRecordSize += 8; |
1725 | 0 | } |
1726 | 0 | } |
1727 | | |
1728 | | /* -------------------------------------------------------------------- */ |
1729 | | /* Not much to do for null geometries. */ |
1730 | | /* -------------------------------------------------------------------- */ |
1731 | 0 | else if (psObject->nSHPType == SHPT_NULL) |
1732 | 0 | { |
1733 | 0 | nRecordSize = 12; |
1734 | 0 | } |
1735 | 0 | else |
1736 | 0 | { |
1737 | | /* unknown type */ |
1738 | 0 | assert(false); |
1739 | 0 | } |
1740 | | |
1741 | | /* -------------------------------------------------------------------- */ |
1742 | | /* Establish where we are going to put this record. If we are */ |
1743 | | /* rewriting the last record of the file, then we can update it in */ |
1744 | | /* place. Otherwise if rewriting an existing record, and it will */ |
1745 | | /* fit, then put it back where the original came from. Otherwise */ |
1746 | | /* write at the end. */ |
1747 | | /* -------------------------------------------------------------------- */ |
1748 | 0 | SAOffset nRecordOffset; |
1749 | 0 | bool bAppendToLastRecord = false; |
1750 | 0 | bool bAppendToFile = false; |
1751 | 0 | if (nShapeId != -1 && |
1752 | 0 | psSHP->panRecOffset[nShapeId] + psSHP->panRecSize[nShapeId] + 8 == |
1753 | 0 | psSHP->nFileSize) |
1754 | 0 | { |
1755 | 0 | nRecordOffset = psSHP->panRecOffset[nShapeId]; |
1756 | 0 | bAppendToLastRecord = true; |
1757 | 0 | } |
1758 | 0 | else if (nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize - 8) |
1759 | 0 | { |
1760 | 0 | if (psSHP->nFileSize > UINT_MAX - nRecordSize) |
1761 | 0 | { |
1762 | 0 | char str[255]; |
1763 | 0 | snprintf(str, sizeof(str), |
1764 | 0 | "Failed to write shape object. " |
1765 | 0 | "The maximum file size of %u has been reached. " |
1766 | 0 | "The current record of size %u cannot be added.", |
1767 | 0 | psSHP->nFileSize, nRecordSize); |
1768 | 0 | str[sizeof(str) - 1] = '\0'; |
1769 | 0 | psSHP->sHooks.Error(str); |
1770 | 0 | free(pabyRec); |
1771 | 0 | return -1; |
1772 | 0 | } |
1773 | | |
1774 | 0 | bAppendToFile = true; |
1775 | 0 | nRecordOffset = psSHP->nFileSize; |
1776 | 0 | } |
1777 | 0 | else |
1778 | 0 | { |
1779 | 0 | nRecordOffset = psSHP->panRecOffset[nShapeId]; |
1780 | 0 | } |
1781 | | |
1782 | | /* -------------------------------------------------------------------- */ |
1783 | | /* Set the shape type, record number, and record size. */ |
1784 | | /* -------------------------------------------------------------------- */ |
1785 | 0 | uint32_t i32 = |
1786 | 0 | (nShapeId < 0) ? psSHP->nRecords + 1 : nShapeId + 1; /* record # */ |
1787 | 0 | #if !defined(SHP_BIG_ENDIAN) |
1788 | 0 | SHP_SWAP32(&i32); |
1789 | 0 | #endif |
1790 | 0 | ByteCopy(&i32, pabyRec, 4); |
1791 | |
|
1792 | 0 | i32 = (nRecordSize - 8) / 2; /* record size */ |
1793 | 0 | #if !defined(SHP_BIG_ENDIAN) |
1794 | 0 | SHP_SWAP32(&i32); |
1795 | 0 | #endif |
1796 | 0 | ByteCopy(&i32, pabyRec + 4, 4); |
1797 | |
|
1798 | 0 | i32 = psObject->nSHPType; /* shape type */ |
1799 | | #if defined(SHP_BIG_ENDIAN) |
1800 | | SHP_SWAP32(&i32); |
1801 | | #endif |
1802 | 0 | ByteCopy(&i32, pabyRec + 8, 4); |
1803 | | |
1804 | | /* -------------------------------------------------------------------- */ |
1805 | | /* Write out record. */ |
1806 | | /* -------------------------------------------------------------------- */ |
1807 | | |
1808 | | /* -------------------------------------------------------------------- */ |
1809 | | /* Guard FSeek with check for whether we're already at position; */ |
1810 | | /* no-op FSeeks defeat network filesystems' write buffering. */ |
1811 | | /* -------------------------------------------------------------------- */ |
1812 | 0 | if (psSHP->sHooks.FTell(psSHP->fpSHP) != nRecordOffset) |
1813 | 0 | { |
1814 | 0 | if (psSHP->sHooks.FSeek(psSHP->fpSHP, nRecordOffset, 0) != 0) |
1815 | 0 | { |
1816 | 0 | char szErrorMsg[200]; |
1817 | |
|
1818 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
1819 | 0 | "Error in psSHP->sHooks.FSeek() while writing object to " |
1820 | 0 | ".shp file: %s", |
1821 | 0 | strerror(errno)); |
1822 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
1823 | 0 | psSHP->sHooks.Error(szErrorMsg); |
1824 | |
|
1825 | 0 | free(pabyRec); |
1826 | 0 | return -1; |
1827 | 0 | } |
1828 | 0 | } |
1829 | 0 | if (psSHP->sHooks.FWrite(pabyRec, nRecordSize, 1, psSHP->fpSHP) < 1) |
1830 | 0 | { |
1831 | 0 | char szErrorMsg[200]; |
1832 | |
|
1833 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
1834 | 0 | "Error in psSHP->sHooks.FWrite() while writing object of %u " |
1835 | 0 | "bytes to .shp file: %s", |
1836 | 0 | nRecordSize, strerror(errno)); |
1837 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
1838 | 0 | psSHP->sHooks.Error(szErrorMsg); |
1839 | |
|
1840 | 0 | free(pabyRec); |
1841 | 0 | return -1; |
1842 | 0 | } |
1843 | | |
1844 | 0 | free(pabyRec); |
1845 | |
|
1846 | 0 | if (bAppendToLastRecord) |
1847 | 0 | { |
1848 | 0 | psSHP->nFileSize = psSHP->panRecOffset[nShapeId] + nRecordSize; |
1849 | 0 | } |
1850 | 0 | else if (bAppendToFile) |
1851 | 0 | { |
1852 | 0 | if (nShapeId == -1) |
1853 | 0 | nShapeId = psSHP->nRecords++; |
1854 | |
|
1855 | 0 | psSHP->panRecOffset[nShapeId] = psSHP->nFileSize; |
1856 | 0 | psSHP->nFileSize += nRecordSize; |
1857 | 0 | } |
1858 | 0 | psSHP->panRecSize[nShapeId] = nRecordSize - 8; |
1859 | | |
1860 | | /* -------------------------------------------------------------------- */ |
1861 | | /* Expand file wide bounds based on this shape. */ |
1862 | | /* -------------------------------------------------------------------- */ |
1863 | 0 | if (bFirstFeature) |
1864 | 0 | { |
1865 | 0 | if (psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0) |
1866 | 0 | { |
1867 | 0 | psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0; |
1868 | 0 | psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0; |
1869 | 0 | psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0; |
1870 | 0 | psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0; |
1871 | 0 | } |
1872 | 0 | else |
1873 | 0 | { |
1874 | 0 | psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0]; |
1875 | 0 | psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0]; |
1876 | 0 | psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = |
1877 | 0 | psObject->padfZ ? psObject->padfZ[0] : 0.0; |
1878 | 0 | psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = |
1879 | 0 | psObject->padfM ? psObject->padfM[0] : 0.0; |
1880 | 0 | } |
1881 | 0 | } |
1882 | |
|
1883 | 0 | for (int i = 0; i < psObject->nVertices; i++) |
1884 | 0 | { |
1885 | 0 | psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0], psObject->padfX[i]); |
1886 | 0 | psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1], psObject->padfY[i]); |
1887 | 0 | psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0], psObject->padfX[i]); |
1888 | 0 | psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1], psObject->padfY[i]); |
1889 | 0 | if (psObject->padfZ) |
1890 | 0 | { |
1891 | 0 | psSHP->adBoundsMin[2] = |
1892 | 0 | MIN(psSHP->adBoundsMin[2], psObject->padfZ[i]); |
1893 | 0 | psSHP->adBoundsMax[2] = |
1894 | 0 | MAX(psSHP->adBoundsMax[2], psObject->padfZ[i]); |
1895 | 0 | } |
1896 | 0 | if (psObject->padfM) |
1897 | 0 | { |
1898 | 0 | psSHP->adBoundsMin[3] = |
1899 | 0 | MIN(psSHP->adBoundsMin[3], psObject->padfM[i]); |
1900 | 0 | psSHP->adBoundsMax[3] = |
1901 | 0 | MAX(psSHP->adBoundsMax[3], psObject->padfM[i]); |
1902 | 0 | } |
1903 | 0 | } |
1904 | |
|
1905 | 0 | return (nShapeId); |
1906 | 0 | } |
1907 | | |
1908 | | /************************************************************************/ |
1909 | | /* SHPAllocBuffer() */ |
1910 | | /************************************************************************/ |
1911 | | |
1912 | | static void *SHPAllocBuffer(unsigned char **pBuffer, int nSize) |
1913 | 0 | { |
1914 | 0 | if (pBuffer == SHPLIB_NULLPTR) |
1915 | 0 | return calloc(1, nSize); |
1916 | | |
1917 | 0 | unsigned char *pRet = *pBuffer; |
1918 | 0 | if (pRet == SHPLIB_NULLPTR) |
1919 | 0 | return SHPLIB_NULLPTR; |
1920 | | |
1921 | 0 | (*pBuffer) += nSize; |
1922 | 0 | return pRet; |
1923 | 0 | } |
1924 | | |
1925 | | /************************************************************************/ |
1926 | | /* SHPReallocObjectBufIfNecessary() */ |
1927 | | /************************************************************************/ |
1928 | | |
1929 | | static unsigned char *SHPReallocObjectBufIfNecessary(SHPHandle psSHP, |
1930 | | int nObjectBufSize) |
1931 | 0 | { |
1932 | 0 | if (nObjectBufSize == 0) |
1933 | 0 | { |
1934 | 0 | nObjectBufSize = 4 * sizeof(double); |
1935 | 0 | } |
1936 | |
|
1937 | 0 | unsigned char *pBuffer; |
1938 | 0 | if (nObjectBufSize > psSHP->nObjectBufSize) |
1939 | 0 | { |
1940 | 0 | pBuffer = STATIC_CAST(unsigned char *, |
1941 | 0 | realloc(psSHP->pabyObjectBuf, nObjectBufSize)); |
1942 | 0 | if (pBuffer != SHPLIB_NULLPTR) |
1943 | 0 | { |
1944 | 0 | psSHP->pabyObjectBuf = pBuffer; |
1945 | 0 | psSHP->nObjectBufSize = nObjectBufSize; |
1946 | 0 | } |
1947 | 0 | } |
1948 | 0 | else |
1949 | 0 | { |
1950 | 0 | pBuffer = psSHP->pabyObjectBuf; |
1951 | 0 | } |
1952 | |
|
1953 | 0 | return pBuffer; |
1954 | 0 | } |
1955 | | |
1956 | | /************************************************************************/ |
1957 | | /* SHPReadObject() */ |
1958 | | /* */ |
1959 | | /* Read the vertices, parts, and other non-attribute information */ |
1960 | | /* for one shape. */ |
1961 | | /************************************************************************/ |
1962 | | |
1963 | | SHPObject SHPAPI_CALL1(*) SHPReadObject(const SHPHandle psSHP, int hEntity) |
1964 | 0 | { |
1965 | | /* -------------------------------------------------------------------- */ |
1966 | | /* Validate the record/entity number. */ |
1967 | | /* -------------------------------------------------------------------- */ |
1968 | 0 | if (hEntity < 0 || hEntity >= psSHP->nRecords) |
1969 | 0 | return SHPLIB_NULLPTR; |
1970 | | |
1971 | | /* -------------------------------------------------------------------- */ |
1972 | | /* Read offset/length from SHX loading if necessary. */ |
1973 | | /* -------------------------------------------------------------------- */ |
1974 | 0 | if (psSHP->panRecOffset[hEntity] == 0 && psSHP->fpSHX != SHPLIB_NULLPTR) |
1975 | 0 | { |
1976 | 0 | unsigned int nOffset; |
1977 | 0 | unsigned int nLength; |
1978 | |
|
1979 | 0 | if (psSHP->sHooks.FSeek(psSHP->fpSHX, 100 + 8 * hEntity, 0) != 0 || |
1980 | 0 | psSHP->sHooks.FRead(&nOffset, 1, 4, psSHP->fpSHX) != 4 || |
1981 | 0 | psSHP->sHooks.FRead(&nLength, 1, 4, psSHP->fpSHX) != 4) |
1982 | 0 | { |
1983 | 0 | char str[128]; |
1984 | 0 | snprintf(str, sizeof(str), |
1985 | 0 | "Error in fseek()/fread() reading object from .shx file " |
1986 | 0 | "at offset %d", |
1987 | 0 | 100 + 8 * hEntity); |
1988 | 0 | str[sizeof(str) - 1] = '\0'; |
1989 | |
|
1990 | 0 | psSHP->sHooks.Error(str); |
1991 | 0 | return SHPLIB_NULLPTR; |
1992 | 0 | } |
1993 | 0 | #if !defined(SHP_BIG_ENDIAN) |
1994 | 0 | SHP_SWAP32(&nOffset); |
1995 | 0 | SHP_SWAP32(&nLength); |
1996 | 0 | #endif |
1997 | |
|
1998 | 0 | if (nOffset > STATIC_CAST(unsigned int, INT_MAX)) |
1999 | 0 | { |
2000 | 0 | char str[128]; |
2001 | 0 | snprintf(str, sizeof(str), "Invalid offset for entity %d", hEntity); |
2002 | 0 | str[sizeof(str) - 1] = '\0'; |
2003 | |
|
2004 | 0 | psSHP->sHooks.Error(str); |
2005 | 0 | return SHPLIB_NULLPTR; |
2006 | 0 | } |
2007 | 0 | if (nLength > STATIC_CAST(unsigned int, INT_MAX / 2 - 4)) |
2008 | 0 | { |
2009 | 0 | char str[128]; |
2010 | 0 | snprintf(str, sizeof(str), "Invalid length for entity %d", hEntity); |
2011 | 0 | str[sizeof(str) - 1] = '\0'; |
2012 | |
|
2013 | 0 | psSHP->sHooks.Error(str); |
2014 | 0 | return SHPLIB_NULLPTR; |
2015 | 0 | } |
2016 | | |
2017 | 0 | psSHP->panRecOffset[hEntity] = nOffset * 2; |
2018 | 0 | psSHP->panRecSize[hEntity] = nLength * 2; |
2019 | 0 | } |
2020 | | |
2021 | | /* -------------------------------------------------------------------- */ |
2022 | | /* Ensure our record buffer is large enough. */ |
2023 | | /* -------------------------------------------------------------------- */ |
2024 | 0 | const int nEntitySize = psSHP->panRecSize[hEntity] + 8; |
2025 | 0 | if (nEntitySize > psSHP->nBufSize) |
2026 | 0 | { |
2027 | 0 | int nNewBufSize = nEntitySize; |
2028 | 0 | if (nNewBufSize < INT_MAX - nNewBufSize / 3) |
2029 | 0 | nNewBufSize += nNewBufSize / 3; |
2030 | 0 | else |
2031 | 0 | nNewBufSize = INT_MAX; |
2032 | | |
2033 | | /* Before allocating too much memory, check that the file is big enough */ |
2034 | | /* and do not trust the file size in the header the first time we */ |
2035 | | /* need to allocate more than 10 MB */ |
2036 | 0 | if (nNewBufSize >= 10 * 1024 * 1024) |
2037 | 0 | { |
2038 | 0 | if (psSHP->nBufSize < 10 * 1024 * 1024) |
2039 | 0 | { |
2040 | 0 | SAOffset nFileSize; |
2041 | 0 | psSHP->sHooks.FSeek(psSHP->fpSHP, 0, 2); |
2042 | 0 | nFileSize = psSHP->sHooks.FTell(psSHP->fpSHP); |
2043 | 0 | if (nFileSize >= UINT_MAX) |
2044 | 0 | psSHP->nFileSize = UINT_MAX; |
2045 | 0 | else |
2046 | 0 | psSHP->nFileSize = STATIC_CAST(unsigned int, nFileSize); |
2047 | 0 | } |
2048 | |
|
2049 | 0 | if (psSHP->panRecOffset[hEntity] >= psSHP->nFileSize || |
2050 | | /* We should normally use nEntitySize instead of*/ |
2051 | | /* psSHP->panRecSize[hEntity] in the below test, but because of */ |
2052 | | /* the case of non conformant .shx files detailed a bit below, */ |
2053 | | /* let be more tolerant */ |
2054 | 0 | psSHP->panRecSize[hEntity] > |
2055 | 0 | psSHP->nFileSize - psSHP->panRecOffset[hEntity]) |
2056 | 0 | { |
2057 | 0 | char str[128]; |
2058 | 0 | snprintf(str, sizeof(str), |
2059 | 0 | "Error in fread() reading object of size %d at offset " |
2060 | 0 | "%u from .shp file", |
2061 | 0 | nEntitySize, psSHP->panRecOffset[hEntity]); |
2062 | 0 | str[sizeof(str) - 1] = '\0'; |
2063 | |
|
2064 | 0 | psSHP->sHooks.Error(str); |
2065 | 0 | return SHPLIB_NULLPTR; |
2066 | 0 | } |
2067 | 0 | } |
2068 | | |
2069 | 0 | unsigned char *pabyRecNew = |
2070 | 0 | STATIC_CAST(unsigned char *, realloc(psSHP->pabyRec, nNewBufSize)); |
2071 | 0 | if (pabyRecNew == SHPLIB_NULLPTR) |
2072 | 0 | { |
2073 | 0 | char szErrorMsg[160]; |
2074 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2075 | 0 | "Not enough memory to allocate requested memory " |
2076 | 0 | "(nNewBufSize=%d). " |
2077 | 0 | "Probably broken SHP file", |
2078 | 0 | nNewBufSize); |
2079 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2080 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2081 | 0 | return SHPLIB_NULLPTR; |
2082 | 0 | } |
2083 | | |
2084 | | /* Only set new buffer size after successful alloc */ |
2085 | 0 | psSHP->pabyRec = pabyRecNew; |
2086 | 0 | psSHP->nBufSize = nNewBufSize; |
2087 | 0 | } |
2088 | | |
2089 | | /* In case we were not able to reallocate the buffer on a previous step */ |
2090 | 0 | if (psSHP->pabyRec == SHPLIB_NULLPTR) |
2091 | 0 | { |
2092 | 0 | return SHPLIB_NULLPTR; |
2093 | 0 | } |
2094 | | |
2095 | | /* -------------------------------------------------------------------- */ |
2096 | | /* Read the record. */ |
2097 | | /* -------------------------------------------------------------------- */ |
2098 | 0 | if (psSHP->sHooks.FSeek(psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0) != 0) |
2099 | 0 | { |
2100 | | /* |
2101 | | * TODO - mloskot: Consider detailed diagnostics of shape file, |
2102 | | * for example to detect if file is truncated. |
2103 | | */ |
2104 | 0 | char str[128]; |
2105 | 0 | snprintf(str, sizeof(str), |
2106 | 0 | "Error in fseek() reading object from .shp file at offset %u", |
2107 | 0 | psSHP->panRecOffset[hEntity]); |
2108 | 0 | str[sizeof(str) - 1] = '\0'; |
2109 | |
|
2110 | 0 | psSHP->sHooks.Error(str); |
2111 | 0 | return SHPLIB_NULLPTR; |
2112 | 0 | } |
2113 | | |
2114 | 0 | const int nBytesRead = STATIC_CAST( |
2115 | 0 | int, psSHP->sHooks.FRead(psSHP->pabyRec, 1, nEntitySize, psSHP->fpSHP)); |
2116 | | |
2117 | | /* Special case for a shapefile whose .shx content length field is not equal */ |
2118 | | /* to the content length field of the .shp, which is a violation of "The */ |
2119 | | /* content length stored in the index record is the same as the value stored in the main */ |
2120 | | /* file record header." (http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf, page 24) */ |
2121 | | /* Actually in that case the .shx content length is equal to the .shp content length + */ |
2122 | | /* 4 (16 bit words), representing the 8 bytes of the record header... */ |
2123 | 0 | if (nBytesRead >= 8 && nBytesRead == nEntitySize - 8) |
2124 | 0 | { |
2125 | | /* Do a sanity check */ |
2126 | 0 | int nSHPContentLength; |
2127 | 0 | memcpy(&nSHPContentLength, psSHP->pabyRec + 4, 4); |
2128 | 0 | #if !defined(SHP_BIG_ENDIAN) |
2129 | 0 | SHP_SWAP32(&(nSHPContentLength)); |
2130 | 0 | #endif |
2131 | 0 | if (nSHPContentLength < 0 || nSHPContentLength > INT_MAX / 2 - 4 || |
2132 | 0 | 2 * nSHPContentLength + 8 != nBytesRead) |
2133 | 0 | { |
2134 | 0 | char str[128]; |
2135 | 0 | snprintf(str, sizeof(str), |
2136 | 0 | "Sanity check failed when trying to recover from " |
2137 | 0 | "inconsistent .shx/.shp with shape %d", |
2138 | 0 | hEntity); |
2139 | 0 | str[sizeof(str) - 1] = '\0'; |
2140 | |
|
2141 | 0 | psSHP->sHooks.Error(str); |
2142 | 0 | return SHPLIB_NULLPTR; |
2143 | 0 | } |
2144 | 0 | } |
2145 | 0 | else if (nBytesRead != nEntitySize) |
2146 | 0 | { |
2147 | | /* |
2148 | | * TODO - mloskot: Consider detailed diagnostics of shape file, |
2149 | | * for example to detect if file is truncated. |
2150 | | */ |
2151 | 0 | char str[128]; |
2152 | 0 | snprintf(str, sizeof(str), |
2153 | 0 | "Error in fread() reading object of size %d at offset %u from " |
2154 | 0 | ".shp file", |
2155 | 0 | nEntitySize, psSHP->panRecOffset[hEntity]); |
2156 | 0 | str[sizeof(str) - 1] = '\0'; |
2157 | |
|
2158 | 0 | psSHP->sHooks.Error(str); |
2159 | 0 | return SHPLIB_NULLPTR; |
2160 | 0 | } |
2161 | | |
2162 | 0 | if (8 + 4 > nEntitySize) |
2163 | 0 | { |
2164 | 0 | char szErrorMsg[160]; |
2165 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2166 | 0 | "Corrupted .shp file : shape %d : nEntitySize = %d", hEntity, |
2167 | 0 | nEntitySize); |
2168 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2169 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2170 | 0 | return SHPLIB_NULLPTR; |
2171 | 0 | } |
2172 | 0 | int nSHPType; |
2173 | 0 | memcpy(&nSHPType, psSHP->pabyRec + 8, 4); |
2174 | |
|
2175 | | #if defined(SHP_BIG_ENDIAN) |
2176 | | SHP_SWAP32(&(nSHPType)); |
2177 | | #endif |
2178 | | |
2179 | | /* -------------------------------------------------------------------- */ |
2180 | | /* Allocate and minimally initialize the object. */ |
2181 | | /* -------------------------------------------------------------------- */ |
2182 | 0 | SHPObject *psShape; |
2183 | 0 | if (psSHP->bFastModeReadObject) |
2184 | 0 | { |
2185 | 0 | if (psSHP->psCachedObject->bFastModeReadObject) |
2186 | 0 | { |
2187 | 0 | psSHP->sHooks.Error("Invalid read pattern in fast read mode. " |
2188 | 0 | "SHPDestroyObject() should be called."); |
2189 | 0 | return SHPLIB_NULLPTR; |
2190 | 0 | } |
2191 | | |
2192 | 0 | psShape = psSHP->psCachedObject; |
2193 | 0 | memset(psShape, 0, sizeof(SHPObject)); |
2194 | 0 | } |
2195 | 0 | else |
2196 | 0 | { |
2197 | 0 | psShape = STATIC_CAST(SHPObject *, calloc(1, sizeof(SHPObject))); |
2198 | 0 | if (!psShape) |
2199 | 0 | { |
2200 | 0 | psSHP->sHooks.Error("Out of memory."); |
2201 | 0 | return SHPLIB_NULLPTR; |
2202 | 0 | } |
2203 | 0 | } |
2204 | 0 | psShape->nShapeId = hEntity; |
2205 | 0 | psShape->nSHPType = nSHPType; |
2206 | 0 | psShape->bMeasureIsUsed = FALSE; |
2207 | 0 | psShape->bFastModeReadObject = psSHP->bFastModeReadObject; |
2208 | | |
2209 | | /* ==================================================================== */ |
2210 | | /* Extract vertices for a Polygon or Arc. */ |
2211 | | /* ==================================================================== */ |
2212 | 0 | if (psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC || |
2213 | 0 | psShape->nSHPType == SHPT_POLYGONZ || |
2214 | 0 | psShape->nSHPType == SHPT_POLYGONM || psShape->nSHPType == SHPT_ARCZ || |
2215 | 0 | psShape->nSHPType == SHPT_ARCM || psShape->nSHPType == SHPT_MULTIPATCH) |
2216 | 0 | { |
2217 | 0 | if (40 + 8 + 4 > nEntitySize) |
2218 | 0 | { |
2219 | 0 | char szErrorMsg[160]; |
2220 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2221 | 0 | "Corrupted .shp file : shape %d : nEntitySize = %d", |
2222 | 0 | hEntity, nEntitySize); |
2223 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2224 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2225 | 0 | SHPDestroyObject(psShape); |
2226 | 0 | return SHPLIB_NULLPTR; |
2227 | 0 | } |
2228 | | /* -------------------------------------------------------------------- */ |
2229 | | /* Get the X/Y bounds. */ |
2230 | | /* -------------------------------------------------------------------- */ |
2231 | | #if defined(SHP_BIG_ENDIAN) |
2232 | | SHP_SWAPDOUBLE_CPY(&psShape->dfXMin, psSHP->pabyRec + 8 + 4); |
2233 | | SHP_SWAPDOUBLE_CPY(&psShape->dfYMin, psSHP->pabyRec + 8 + 12); |
2234 | | SHP_SWAPDOUBLE_CPY(&psShape->dfXMax, psSHP->pabyRec + 8 + 20); |
2235 | | SHP_SWAPDOUBLE_CPY(&psShape->dfYMax, psSHP->pabyRec + 8 + 28); |
2236 | | #else |
2237 | 0 | memcpy(&psShape->dfXMin, psSHP->pabyRec + 8 + 4, 8); |
2238 | 0 | memcpy(&psShape->dfYMin, psSHP->pabyRec + 8 + 12, 8); |
2239 | 0 | memcpy(&psShape->dfXMax, psSHP->pabyRec + 8 + 20, 8); |
2240 | 0 | memcpy(&psShape->dfYMax, psSHP->pabyRec + 8 + 28, 8); |
2241 | 0 | #endif |
2242 | | |
2243 | | /* -------------------------------------------------------------------- */ |
2244 | | /* Extract part/point count, and build vertex and part arrays */ |
2245 | | /* to proper size. */ |
2246 | | /* -------------------------------------------------------------------- */ |
2247 | 0 | uint32_t nPoints; |
2248 | 0 | memcpy(&nPoints, psSHP->pabyRec + 40 + 8, 4); |
2249 | 0 | uint32_t nParts; |
2250 | 0 | memcpy(&nParts, psSHP->pabyRec + 36 + 8, 4); |
2251 | |
|
2252 | | #if defined(SHP_BIG_ENDIAN) |
2253 | | SHP_SWAP32(&nPoints); |
2254 | | SHP_SWAP32(&nParts); |
2255 | | #endif |
2256 | | |
2257 | | /* nPoints and nParts are unsigned */ |
2258 | 0 | if (/* nPoints < 0 || nParts < 0 || */ |
2259 | 0 | nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000) |
2260 | 0 | { |
2261 | 0 | char szErrorMsg[160]; |
2262 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2263 | 0 | "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u.", |
2264 | 0 | hEntity, nPoints, nParts); |
2265 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2266 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2267 | 0 | SHPDestroyObject(psShape); |
2268 | 0 | return SHPLIB_NULLPTR; |
2269 | 0 | } |
2270 | | |
2271 | | /* With the previous checks on nPoints and nParts, */ |
2272 | | /* we should not overflow here and after */ |
2273 | | /* since 50 M * (16 + 8 + 8) = 1 600 MB */ |
2274 | 0 | int nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints; |
2275 | 0 | if (psShape->nSHPType == SHPT_POLYGONZ || |
2276 | 0 | psShape->nSHPType == SHPT_ARCZ || |
2277 | 0 | psShape->nSHPType == SHPT_MULTIPATCH) |
2278 | 0 | { |
2279 | 0 | nRequiredSize += 16 + 8 * nPoints; |
2280 | 0 | } |
2281 | 0 | if (psShape->nSHPType == SHPT_MULTIPATCH) |
2282 | 0 | { |
2283 | 0 | nRequiredSize += 4 * nParts; |
2284 | 0 | } |
2285 | 0 | if (nRequiredSize > nEntitySize) |
2286 | 0 | { |
2287 | 0 | char szErrorMsg[160]; |
2288 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2289 | 0 | "Corrupted .shp file : shape %d, nPoints=%u, nParts=%u, " |
2290 | 0 | "nEntitySize=%d.", |
2291 | 0 | hEntity, nPoints, nParts, nEntitySize); |
2292 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2293 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2294 | 0 | SHPDestroyObject(psShape); |
2295 | 0 | return SHPLIB_NULLPTR; |
2296 | 0 | } |
2297 | | |
2298 | 0 | unsigned char *pBuffer = SHPLIB_NULLPTR; |
2299 | 0 | unsigned char **ppBuffer = SHPLIB_NULLPTR; |
2300 | |
|
2301 | 0 | if (psShape->bFastModeReadObject) |
2302 | 0 | { |
2303 | 0 | const int nObjectBufSize = |
2304 | 0 | 4 * sizeof(double) * nPoints + 2 * sizeof(int) * nParts; |
2305 | 0 | pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize); |
2306 | 0 | ppBuffer = &pBuffer; |
2307 | 0 | } |
2308 | |
|
2309 | 0 | psShape->nVertices = nPoints; |
2310 | 0 | psShape->padfX = STATIC_CAST( |
2311 | 0 | double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints)); |
2312 | 0 | psShape->padfY = STATIC_CAST( |
2313 | 0 | double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints)); |
2314 | 0 | psShape->padfZ = STATIC_CAST( |
2315 | 0 | double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints)); |
2316 | 0 | psShape->padfM = STATIC_CAST( |
2317 | 0 | double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints)); |
2318 | |
|
2319 | 0 | psShape->nParts = nParts; |
2320 | 0 | psShape->panPartStart = |
2321 | 0 | STATIC_CAST(int *, SHPAllocBuffer(ppBuffer, nParts * sizeof(int))); |
2322 | 0 | psShape->panPartType = |
2323 | 0 | STATIC_CAST(int *, SHPAllocBuffer(ppBuffer, nParts * sizeof(int))); |
2324 | |
|
2325 | 0 | if (psShape->padfX == SHPLIB_NULLPTR || |
2326 | 0 | psShape->padfY == SHPLIB_NULLPTR || |
2327 | 0 | psShape->padfZ == SHPLIB_NULLPTR || |
2328 | 0 | psShape->padfM == SHPLIB_NULLPTR || |
2329 | 0 | psShape->panPartStart == SHPLIB_NULLPTR || |
2330 | 0 | psShape->panPartType == SHPLIB_NULLPTR) |
2331 | 0 | { |
2332 | 0 | char szErrorMsg[160]; |
2333 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2334 | 0 | "Not enough memory to allocate requested memory " |
2335 | 0 | "(nPoints=%u, nParts=%u) for shape %d. " |
2336 | 0 | "Probably broken SHP file", |
2337 | 0 | nPoints, nParts, hEntity); |
2338 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2339 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2340 | 0 | SHPDestroyObject(psShape); |
2341 | 0 | return SHPLIB_NULLPTR; |
2342 | 0 | } |
2343 | | |
2344 | 0 | for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++) |
2345 | 0 | psShape->panPartType[i] = SHPP_RING; |
2346 | | |
2347 | | /* -------------------------------------------------------------------- */ |
2348 | | /* Copy out the part array from the record. */ |
2349 | | /* -------------------------------------------------------------------- */ |
2350 | 0 | memcpy(psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts); |
2351 | 0 | for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++) |
2352 | 0 | { |
2353 | | #if defined(SHP_BIG_ENDIAN) |
2354 | | SHP_SWAP32(psShape->panPartStart + i); |
2355 | | #endif |
2356 | | |
2357 | | /* We check that the offset is inside the vertex array */ |
2358 | 0 | if (psShape->panPartStart[i] < 0 || |
2359 | 0 | (psShape->panPartStart[i] >= psShape->nVertices && |
2360 | 0 | psShape->nVertices > 0) || |
2361 | 0 | (psShape->panPartStart[i] > 0 && psShape->nVertices == 0)) |
2362 | 0 | { |
2363 | 0 | char szErrorMsg[160]; |
2364 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2365 | 0 | "Corrupted .shp file : shape %d : panPartStart[%d] = " |
2366 | 0 | "%d, nVertices = %d", |
2367 | 0 | hEntity, i, psShape->panPartStart[i], |
2368 | 0 | psShape->nVertices); |
2369 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2370 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2371 | 0 | SHPDestroyObject(psShape); |
2372 | 0 | return SHPLIB_NULLPTR; |
2373 | 0 | } |
2374 | 0 | if (i > 0 && |
2375 | 0 | psShape->panPartStart[i] <= psShape->panPartStart[i - 1]) |
2376 | 0 | { |
2377 | 0 | char szErrorMsg[160]; |
2378 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2379 | 0 | "Corrupted .shp file : shape %d : panPartStart[%d] = " |
2380 | 0 | "%d, panPartStart[%d] = %d", |
2381 | 0 | hEntity, i, psShape->panPartStart[i], i - 1, |
2382 | 0 | psShape->panPartStart[i - 1]); |
2383 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2384 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2385 | 0 | SHPDestroyObject(psShape); |
2386 | 0 | return SHPLIB_NULLPTR; |
2387 | 0 | } |
2388 | 0 | } |
2389 | | |
2390 | 0 | int nOffset = 44 + 8 + 4 * nParts; |
2391 | | |
2392 | | /* -------------------------------------------------------------------- */ |
2393 | | /* If this is a multipatch, we will also have parts types. */ |
2394 | | /* -------------------------------------------------------------------- */ |
2395 | 0 | if (psShape->nSHPType == SHPT_MULTIPATCH) |
2396 | 0 | { |
2397 | 0 | memcpy(psShape->panPartType, psSHP->pabyRec + nOffset, 4 * nParts); |
2398 | 0 | for (int i = 0; STATIC_CAST(uint32_t, i) < nParts; i++) |
2399 | 0 | { |
2400 | | #if defined(SHP_BIG_ENDIAN) |
2401 | | SHP_SWAP32(psShape->panPartType + i); |
2402 | | #endif |
2403 | 0 | } |
2404 | |
|
2405 | 0 | nOffset += 4 * nParts; |
2406 | 0 | } |
2407 | | |
2408 | | /* -------------------------------------------------------------------- */ |
2409 | | /* Copy out the vertices from the record. */ |
2410 | | /* -------------------------------------------------------------------- */ |
2411 | 0 | for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) |
2412 | 0 | { |
2413 | | #if defined(SHP_BIG_ENDIAN) |
2414 | | SHP_SWAPDOUBLE_CPY(psShape->padfX + i, |
2415 | | psSHP->pabyRec + nOffset + i * 16); |
2416 | | SHP_SWAPDOUBLE_CPY(psShape->padfY + i, |
2417 | | psSHP->pabyRec + nOffset + i * 16 + 8); |
2418 | | #else |
2419 | 0 | memcpy(psShape->padfX + i, psSHP->pabyRec + nOffset + i * 16, 8); |
2420 | 0 | memcpy(psShape->padfY + i, psSHP->pabyRec + nOffset + i * 16 + 8, |
2421 | 0 | 8); |
2422 | 0 | #endif |
2423 | 0 | } |
2424 | |
|
2425 | 0 | nOffset += 16 * nPoints; |
2426 | | |
2427 | | /* -------------------------------------------------------------------- */ |
2428 | | /* If we have a Z coordinate, collect that now. */ |
2429 | | /* -------------------------------------------------------------------- */ |
2430 | 0 | if (psShape->nSHPType == SHPT_POLYGONZ || |
2431 | 0 | psShape->nSHPType == SHPT_ARCZ || |
2432 | 0 | psShape->nSHPType == SHPT_MULTIPATCH) |
2433 | 0 | { |
2434 | | #if defined(SHP_BIG_ENDIAN) |
2435 | | SHP_SWAPDOUBLE_CPY(&psShape->dfZMin, psSHP->pabyRec + nOffset); |
2436 | | SHP_SWAPDOUBLE_CPY(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8); |
2437 | | #else |
2438 | 0 | memcpy(&psShape->dfZMin, psSHP->pabyRec + nOffset, 8); |
2439 | 0 | memcpy(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8, 8); |
2440 | |
|
2441 | 0 | #endif |
2442 | |
|
2443 | 0 | for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) |
2444 | 0 | { |
2445 | | #if defined(SHP_BIG_ENDIAN) |
2446 | | SHP_SWAPDOUBLE_CPY(psShape->padfZ + i, |
2447 | | psSHP->pabyRec + nOffset + 16 + i * 8); |
2448 | | #else |
2449 | 0 | memcpy(psShape->padfZ + i, |
2450 | 0 | psSHP->pabyRec + nOffset + 16 + i * 8, 8); |
2451 | 0 | #endif |
2452 | 0 | } |
2453 | |
|
2454 | 0 | nOffset += 16 + 8 * nPoints; |
2455 | 0 | } |
2456 | 0 | else if (psShape->bFastModeReadObject) |
2457 | 0 | { |
2458 | 0 | psShape->padfZ = SHPLIB_NULLPTR; |
2459 | 0 | } |
2460 | | |
2461 | | /* -------------------------------------------------------------------- */ |
2462 | | /* If we have a M measure value, then read it now. We assume */ |
2463 | | /* that the measure can be present for any shape if the size is */ |
2464 | | /* big enough, but really it will only occur for the Z shapes */ |
2465 | | /* (options), and the M shapes. */ |
2466 | | /* -------------------------------------------------------------------- */ |
2467 | 0 | if (nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8 * nPoints)) |
2468 | 0 | { |
2469 | | #if defined(SHP_BIG_ENDIAN) |
2470 | | SHP_SWAPDOUBLE_CPY(&psShape->dfMMin, psSHP->pabyRec + nOffset); |
2471 | | SHP_SWAPDOUBLE_CPY(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8); |
2472 | | #else |
2473 | 0 | memcpy(&psShape->dfMMin, psSHP->pabyRec + nOffset, 8); |
2474 | 0 | memcpy(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8, 8); |
2475 | 0 | #endif |
2476 | |
|
2477 | 0 | for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) |
2478 | 0 | { |
2479 | | #if defined(SHP_BIG_ENDIAN) |
2480 | | SHP_SWAPDOUBLE_CPY(psShape->padfM + i, |
2481 | | psSHP->pabyRec + nOffset + 16 + i * 8); |
2482 | | #else |
2483 | 0 | memcpy(psShape->padfM + i, |
2484 | 0 | psSHP->pabyRec + nOffset + 16 + i * 8, 8); |
2485 | 0 | #endif |
2486 | 0 | } |
2487 | 0 | psShape->bMeasureIsUsed = TRUE; |
2488 | 0 | } |
2489 | 0 | else if (psShape->bFastModeReadObject) |
2490 | 0 | { |
2491 | 0 | psShape->padfM = SHPLIB_NULLPTR; |
2492 | 0 | } |
2493 | 0 | } |
2494 | | |
2495 | | /* ==================================================================== */ |
2496 | | /* Extract vertices for a MultiPoint. */ |
2497 | | /* ==================================================================== */ |
2498 | 0 | else if (psShape->nSHPType == SHPT_MULTIPOINT || |
2499 | 0 | psShape->nSHPType == SHPT_MULTIPOINTM || |
2500 | 0 | psShape->nSHPType == SHPT_MULTIPOINTZ) |
2501 | 0 | { |
2502 | 0 | if (44 + 4 > nEntitySize) |
2503 | 0 | { |
2504 | 0 | char szErrorMsg[160]; |
2505 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2506 | 0 | "Corrupted .shp file : shape %d : nEntitySize = %d", |
2507 | 0 | hEntity, nEntitySize); |
2508 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2509 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2510 | 0 | SHPDestroyObject(psShape); |
2511 | 0 | return SHPLIB_NULLPTR; |
2512 | 0 | } |
2513 | 0 | uint32_t nPoints; |
2514 | 0 | memcpy(&nPoints, psSHP->pabyRec + 44, 4); |
2515 | |
|
2516 | | #if defined(SHP_BIG_ENDIAN) |
2517 | | SHP_SWAP32(&nPoints); |
2518 | | #endif |
2519 | | |
2520 | | /* nPoints is unsigned */ |
2521 | 0 | if (/* nPoints < 0 || */ nPoints > 50 * 1000 * 1000) |
2522 | 0 | { |
2523 | 0 | char szErrorMsg[160]; |
2524 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2525 | 0 | "Corrupted .shp file : shape %d : nPoints = %u", hEntity, |
2526 | 0 | nPoints); |
2527 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2528 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2529 | 0 | SHPDestroyObject(psShape); |
2530 | 0 | return SHPLIB_NULLPTR; |
2531 | 0 | } |
2532 | | |
2533 | 0 | int nRequiredSize = 48 + nPoints * 16; |
2534 | 0 | if (psShape->nSHPType == SHPT_MULTIPOINTZ) |
2535 | 0 | { |
2536 | 0 | nRequiredSize += 16 + nPoints * 8; |
2537 | 0 | } |
2538 | 0 | if (nRequiredSize > nEntitySize) |
2539 | 0 | { |
2540 | 0 | char szErrorMsg[160]; |
2541 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2542 | 0 | "Corrupted .shp file : shape %d : nPoints = %u, " |
2543 | 0 | "nEntitySize = %d", |
2544 | 0 | hEntity, nPoints, nEntitySize); |
2545 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2546 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2547 | 0 | SHPDestroyObject(psShape); |
2548 | 0 | return SHPLIB_NULLPTR; |
2549 | 0 | } |
2550 | | |
2551 | 0 | unsigned char *pBuffer = SHPLIB_NULLPTR; |
2552 | 0 | unsigned char **ppBuffer = SHPLIB_NULLPTR; |
2553 | |
|
2554 | 0 | if (psShape->bFastModeReadObject) |
2555 | 0 | { |
2556 | 0 | const int nObjectBufSize = 4 * sizeof(double) * nPoints; |
2557 | 0 | pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize); |
2558 | 0 | ppBuffer = &pBuffer; |
2559 | 0 | } |
2560 | |
|
2561 | 0 | psShape->nVertices = nPoints; |
2562 | |
|
2563 | 0 | psShape->padfX = STATIC_CAST( |
2564 | 0 | double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints)); |
2565 | 0 | psShape->padfY = STATIC_CAST( |
2566 | 0 | double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints)); |
2567 | 0 | psShape->padfZ = STATIC_CAST( |
2568 | 0 | double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints)); |
2569 | 0 | psShape->padfM = STATIC_CAST( |
2570 | 0 | double *, SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints)); |
2571 | |
|
2572 | 0 | if (psShape->padfX == SHPLIB_NULLPTR || |
2573 | 0 | psShape->padfY == SHPLIB_NULLPTR || |
2574 | 0 | psShape->padfZ == SHPLIB_NULLPTR || |
2575 | 0 | psShape->padfM == SHPLIB_NULLPTR) |
2576 | 0 | { |
2577 | 0 | char szErrorMsg[160]; |
2578 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2579 | 0 | "Not enough memory to allocate requested memory " |
2580 | 0 | "(nPoints=%u) for shape %d. " |
2581 | 0 | "Probably broken SHP file", |
2582 | 0 | nPoints, hEntity); |
2583 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2584 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2585 | 0 | SHPDestroyObject(psShape); |
2586 | 0 | return SHPLIB_NULLPTR; |
2587 | 0 | } |
2588 | | |
2589 | 0 | for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) |
2590 | 0 | { |
2591 | | #if defined(SHP_BIG_ENDIAN) |
2592 | | SHP_SWAPDOUBLE_CPY(psShape->padfX + i, |
2593 | | psSHP->pabyRec + 48 + 16 * i); |
2594 | | SHP_SWAPDOUBLE_CPY(psShape->padfY + i, |
2595 | | psSHP->pabyRec + 48 + 16 * i + 8); |
2596 | | #else |
2597 | 0 | memcpy(psShape->padfX + i, psSHP->pabyRec + 48 + 16 * i, 8); |
2598 | 0 | memcpy(psShape->padfY + i, psSHP->pabyRec + 48 + 16 * i + 8, 8); |
2599 | 0 | #endif |
2600 | 0 | } |
2601 | |
|
2602 | 0 | int nOffset = 48 + 16 * nPoints; |
2603 | | |
2604 | | /* -------------------------------------------------------------------- */ |
2605 | | /* Get the X/Y bounds. */ |
2606 | | /* -------------------------------------------------------------------- */ |
2607 | | #if defined(SHP_BIG_ENDIAN) |
2608 | | SHP_SWAPDOUBLE_CPY(&psShape->dfXMin, psSHP->pabyRec + 8 + 4); |
2609 | | SHP_SWAPDOUBLE_CPY(&psShape->dfYMin, psSHP->pabyRec + 8 + 12); |
2610 | | SHP_SWAPDOUBLE_CPY(&psShape->dfXMax, psSHP->pabyRec + 8 + 20); |
2611 | | SHP_SWAPDOUBLE_CPY(&psShape->dfYMax, psSHP->pabyRec + 8 + 28); |
2612 | | #else |
2613 | 0 | memcpy(&psShape->dfXMin, psSHP->pabyRec + 8 + 4, 8); |
2614 | 0 | memcpy(&psShape->dfYMin, psSHP->pabyRec + 8 + 12, 8); |
2615 | 0 | memcpy(&psShape->dfXMax, psSHP->pabyRec + 8 + 20, 8); |
2616 | 0 | memcpy(&psShape->dfYMax, psSHP->pabyRec + 8 + 28, 8); |
2617 | 0 | #endif |
2618 | | |
2619 | | /* -------------------------------------------------------------------- */ |
2620 | | /* If we have a Z coordinate, collect that now. */ |
2621 | | /* -------------------------------------------------------------------- */ |
2622 | 0 | if (psShape->nSHPType == SHPT_MULTIPOINTZ) |
2623 | 0 | { |
2624 | | #if defined(SHP_BIG_ENDIAN) |
2625 | | SHP_SWAPDOUBLE_CPY(&psShape->dfZMin, psSHP->pabyRec + nOffset); |
2626 | | SHP_SWAPDOUBLE_CPY(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8); |
2627 | | #else |
2628 | 0 | memcpy(&psShape->dfZMin, psSHP->pabyRec + nOffset, 8); |
2629 | 0 | memcpy(&psShape->dfZMax, psSHP->pabyRec + nOffset + 8, 8); |
2630 | 0 | #endif |
2631 | |
|
2632 | 0 | for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) |
2633 | 0 | { |
2634 | | #if defined(SHP_BIG_ENDIAN) |
2635 | | SHP_SWAPDOUBLE_CPY(psShape->padfZ + i, |
2636 | | psSHP->pabyRec + nOffset + 16 + i * 8); |
2637 | | #else |
2638 | 0 | memcpy(psShape->padfZ + i, |
2639 | 0 | psSHP->pabyRec + nOffset + 16 + i * 8, 8); |
2640 | 0 | #endif |
2641 | 0 | } |
2642 | |
|
2643 | 0 | nOffset += 16 + 8 * nPoints; |
2644 | 0 | } |
2645 | 0 | else if (psShape->bFastModeReadObject) |
2646 | 0 | psShape->padfZ = SHPLIB_NULLPTR; |
2647 | | |
2648 | | /* -------------------------------------------------------------------- */ |
2649 | | /* If we have a M measure value, then read it now. We assume */ |
2650 | | /* that the measure can be present for any shape if the size is */ |
2651 | | /* big enough, but really it will only occur for the Z shapes */ |
2652 | | /* (options), and the M shapes. */ |
2653 | | /* -------------------------------------------------------------------- */ |
2654 | 0 | if (nEntitySize >= STATIC_CAST(int, nOffset + 16 + 8 * nPoints)) |
2655 | 0 | { |
2656 | | #if defined(SHP_BIG_ENDIAN) |
2657 | | SHP_SWAPDOUBLE_CPY(&psShape->dfMMin, psSHP->pabyRec + nOffset); |
2658 | | SHP_SWAPDOUBLE_CPY(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8); |
2659 | | #else |
2660 | 0 | memcpy(&psShape->dfMMin, psSHP->pabyRec + nOffset, 8); |
2661 | 0 | memcpy(&psShape->dfMMax, psSHP->pabyRec + nOffset + 8, 8); |
2662 | 0 | #endif |
2663 | |
|
2664 | 0 | for (int i = 0; STATIC_CAST(uint32_t, i) < nPoints; i++) |
2665 | 0 | { |
2666 | | #if defined(SHP_BIG_ENDIAN) |
2667 | | SHP_SWAPDOUBLE_CPY(psShape->padfM + i, |
2668 | | psSHP->pabyRec + nOffset + 16 + i * 8); |
2669 | | #else |
2670 | 0 | memcpy(psShape->padfM + i, |
2671 | 0 | psSHP->pabyRec + nOffset + 16 + i * 8, 8); |
2672 | 0 | #endif |
2673 | 0 | } |
2674 | 0 | psShape->bMeasureIsUsed = TRUE; |
2675 | 0 | } |
2676 | 0 | else if (psShape->bFastModeReadObject) |
2677 | 0 | psShape->padfM = SHPLIB_NULLPTR; |
2678 | 0 | } |
2679 | | |
2680 | | /* ==================================================================== */ |
2681 | | /* Extract vertices for a point. */ |
2682 | | /* ==================================================================== */ |
2683 | 0 | else if (psShape->nSHPType == SHPT_POINT || |
2684 | 0 | psShape->nSHPType == SHPT_POINTM || |
2685 | 0 | psShape->nSHPType == SHPT_POINTZ) |
2686 | 0 | { |
2687 | 0 | psShape->nVertices = 1; |
2688 | 0 | if (psShape->bFastModeReadObject) |
2689 | 0 | { |
2690 | 0 | psShape->padfX = &(psShape->dfXMin); |
2691 | 0 | psShape->padfY = &(psShape->dfYMin); |
2692 | 0 | psShape->padfZ = &(psShape->dfZMin); |
2693 | 0 | psShape->padfM = &(psShape->dfMMin); |
2694 | 0 | psShape->padfZ[0] = 0.0; |
2695 | 0 | psShape->padfM[0] = 0.0; |
2696 | 0 | } |
2697 | 0 | else |
2698 | 0 | { |
2699 | 0 | psShape->padfX = STATIC_CAST(double *, calloc(1, sizeof(double))); |
2700 | 0 | psShape->padfY = STATIC_CAST(double *, calloc(1, sizeof(double))); |
2701 | 0 | psShape->padfZ = STATIC_CAST(double *, calloc(1, sizeof(double))); |
2702 | 0 | psShape->padfM = STATIC_CAST(double *, calloc(1, sizeof(double))); |
2703 | 0 | } |
2704 | |
|
2705 | 0 | if (20 + 8 + ((psShape->nSHPType == SHPT_POINTZ) ? 8 : 0) > nEntitySize) |
2706 | 0 | { |
2707 | 0 | char szErrorMsg[160]; |
2708 | 0 | snprintf(szErrorMsg, sizeof(szErrorMsg), |
2709 | 0 | "Corrupted .shp file : shape %d : nEntitySize = %d", |
2710 | 0 | hEntity, nEntitySize); |
2711 | 0 | szErrorMsg[sizeof(szErrorMsg) - 1] = '\0'; |
2712 | 0 | psSHP->sHooks.Error(szErrorMsg); |
2713 | 0 | SHPDestroyObject(psShape); |
2714 | 0 | return SHPLIB_NULLPTR; |
2715 | 0 | } |
2716 | | #if defined(SHP_BIG_ENDIAN) |
2717 | | SHP_SWAPDOUBLE_CPY(psShape->padfX, psSHP->pabyRec + 12); |
2718 | | SHP_SWAPDOUBLE_CPY(psShape->padfY, psSHP->pabyRec + 20); |
2719 | | #else |
2720 | 0 | memcpy(psShape->padfX, psSHP->pabyRec + 12, 8); |
2721 | 0 | memcpy(psShape->padfY, psSHP->pabyRec + 20, 8); |
2722 | 0 | #endif |
2723 | |
|
2724 | 0 | int nOffset = 20 + 8; |
2725 | | |
2726 | | /* -------------------------------------------------------------------- */ |
2727 | | /* If we have a Z coordinate, collect that now. */ |
2728 | | /* -------------------------------------------------------------------- */ |
2729 | 0 | if (psShape->nSHPType == SHPT_POINTZ) |
2730 | 0 | { |
2731 | | #if defined(SHP_BIG_ENDIAN) |
2732 | | SHP_SWAPDOUBLE_CPY(psShape->padfZ, psSHP->pabyRec + nOffset); |
2733 | | #else |
2734 | 0 | memcpy(psShape->padfZ, psSHP->pabyRec + nOffset, 8); |
2735 | 0 | #endif |
2736 | |
|
2737 | 0 | nOffset += 8; |
2738 | 0 | } |
2739 | | |
2740 | | /* -------------------------------------------------------------------- */ |
2741 | | /* If we have a M measure value, then read it now. We assume */ |
2742 | | /* that the measure can be present for any shape if the size is */ |
2743 | | /* big enough, but really it will only occur for the Z shapes */ |
2744 | | /* (options), and the M shapes. */ |
2745 | | /* -------------------------------------------------------------------- */ |
2746 | 0 | if (nEntitySize >= nOffset + 8) |
2747 | 0 | { |
2748 | | #if defined(SHP_BIG_ENDIAN) |
2749 | | SHP_SWAPDOUBLE_CPY(psShape->padfM, psSHP->pabyRec + nOffset); |
2750 | | #else |
2751 | 0 | memcpy(psShape->padfM, psSHP->pabyRec + nOffset, 8); |
2752 | 0 | #endif |
2753 | 0 | psShape->bMeasureIsUsed = TRUE; |
2754 | 0 | } |
2755 | | |
2756 | | /* -------------------------------------------------------------------- */ |
2757 | | /* Since no extents are supplied in the record, we will apply */ |
2758 | | /* them from the single vertex. */ |
2759 | | /* -------------------------------------------------------------------- */ |
2760 | 0 | psShape->dfXMin = psShape->dfXMax = psShape->padfX[0]; |
2761 | 0 | psShape->dfYMin = psShape->dfYMax = psShape->padfY[0]; |
2762 | 0 | psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0]; |
2763 | 0 | psShape->dfMMin = psShape->dfMMax = psShape->padfM[0]; |
2764 | 0 | } |
2765 | | |
2766 | 0 | return (psShape); |
2767 | 0 | } |
2768 | | |
2769 | | /************************************************************************/ |
2770 | | /* SHPTypeName() */ |
2771 | | /************************************************************************/ |
2772 | | |
2773 | | const char SHPAPI_CALL1(*) SHPTypeName(int nSHPType) |
2774 | 0 | { |
2775 | 0 | switch (nSHPType) |
2776 | 0 | { |
2777 | 0 | case SHPT_NULL: |
2778 | 0 | return "NullShape"; |
2779 | | |
2780 | 0 | case SHPT_POINT: |
2781 | 0 | return "Point"; |
2782 | | |
2783 | 0 | case SHPT_ARC: |
2784 | 0 | return "Arc"; |
2785 | | |
2786 | 0 | case SHPT_POLYGON: |
2787 | 0 | return "Polygon"; |
2788 | | |
2789 | 0 | case SHPT_MULTIPOINT: |
2790 | 0 | return "MultiPoint"; |
2791 | | |
2792 | 0 | case SHPT_POINTZ: |
2793 | 0 | return "PointZ"; |
2794 | | |
2795 | 0 | case SHPT_ARCZ: |
2796 | 0 | return "ArcZ"; |
2797 | | |
2798 | 0 | case SHPT_POLYGONZ: |
2799 | 0 | return "PolygonZ"; |
2800 | | |
2801 | 0 | case SHPT_MULTIPOINTZ: |
2802 | 0 | return "MultiPointZ"; |
2803 | | |
2804 | 0 | case SHPT_POINTM: |
2805 | 0 | return "PointM"; |
2806 | | |
2807 | 0 | case SHPT_ARCM: |
2808 | 0 | return "ArcM"; |
2809 | | |
2810 | 0 | case SHPT_POLYGONM: |
2811 | 0 | return "PolygonM"; |
2812 | | |
2813 | 0 | case SHPT_MULTIPOINTM: |
2814 | 0 | return "MultiPointM"; |
2815 | | |
2816 | 0 | case SHPT_MULTIPATCH: |
2817 | 0 | return "MultiPatch"; |
2818 | | |
2819 | 0 | default: |
2820 | 0 | return "UnknownShapeType"; |
2821 | 0 | } |
2822 | 0 | } |
2823 | | |
2824 | | /************************************************************************/ |
2825 | | /* SHPPartTypeName() */ |
2826 | | /************************************************************************/ |
2827 | | |
2828 | | const char SHPAPI_CALL1(*) SHPPartTypeName(int nPartType) |
2829 | 0 | { |
2830 | 0 | switch (nPartType) |
2831 | 0 | { |
2832 | 0 | case SHPP_TRISTRIP: |
2833 | 0 | return "TriangleStrip"; |
2834 | | |
2835 | 0 | case SHPP_TRIFAN: |
2836 | 0 | return "TriangleFan"; |
2837 | | |
2838 | 0 | case SHPP_OUTERRING: |
2839 | 0 | return "OuterRing"; |
2840 | | |
2841 | 0 | case SHPP_INNERRING: |
2842 | 0 | return "InnerRing"; |
2843 | | |
2844 | 0 | case SHPP_FIRSTRING: |
2845 | 0 | return "FirstRing"; |
2846 | | |
2847 | 0 | case SHPP_RING: |
2848 | 0 | return "Ring"; |
2849 | | |
2850 | 0 | default: |
2851 | 0 | return "UnknownPartType"; |
2852 | 0 | } |
2853 | 0 | } |
2854 | | |
2855 | | /************************************************************************/ |
2856 | | /* SHPDestroyObject() */ |
2857 | | /************************************************************************/ |
2858 | | |
2859 | | void SHPAPI_CALL SHPDestroyObject(SHPObject *psShape) |
2860 | 0 | { |
2861 | 0 | if (psShape == SHPLIB_NULLPTR) |
2862 | 0 | return; |
2863 | | |
2864 | 0 | if (psShape->bFastModeReadObject) |
2865 | 0 | { |
2866 | 0 | psShape->bFastModeReadObject = FALSE; |
2867 | 0 | return; |
2868 | 0 | } |
2869 | | |
2870 | 0 | if (psShape->padfX != SHPLIB_NULLPTR) |
2871 | 0 | free(psShape->padfX); |
2872 | 0 | if (psShape->padfY != SHPLIB_NULLPTR) |
2873 | 0 | free(psShape->padfY); |
2874 | 0 | if (psShape->padfZ != SHPLIB_NULLPTR) |
2875 | 0 | free(psShape->padfZ); |
2876 | 0 | if (psShape->padfM != SHPLIB_NULLPTR) |
2877 | 0 | free(psShape->padfM); |
2878 | |
|
2879 | 0 | if (psShape->panPartStart != SHPLIB_NULLPTR) |
2880 | 0 | free(psShape->panPartStart); |
2881 | 0 | if (psShape->panPartType != SHPLIB_NULLPTR) |
2882 | 0 | free(psShape->panPartType); |
2883 | |
|
2884 | 0 | free(psShape); |
2885 | 0 | } |
2886 | | |
2887 | | /************************************************************************/ |
2888 | | /* SHPGetPartVertexCount() */ |
2889 | | /************************************************************************/ |
2890 | | |
2891 | | static int SHPGetPartVertexCount(const SHPObject *psObject, int iPart) |
2892 | 0 | { |
2893 | 0 | if (iPart == psObject->nParts - 1) |
2894 | 0 | return psObject->nVertices - psObject->panPartStart[iPart]; |
2895 | 0 | else |
2896 | 0 | return psObject->panPartStart[iPart + 1] - |
2897 | 0 | psObject->panPartStart[iPart]; |
2898 | 0 | } |
2899 | | |
2900 | | /************************************************************************/ |
2901 | | /* SHPRewindIsInnerRing() */ |
2902 | | /************************************************************************/ |
2903 | | |
2904 | | /* Return -1 in case of ambiguity */ |
2905 | | static int SHPRewindIsInnerRing(const SHPObject *psObject, int iOpRing, |
2906 | | double dfTestX, double dfTestY, |
2907 | | double dfRelativeTolerance, int bSameZ, |
2908 | | double dfTestZ) |
2909 | 0 | { |
2910 | | /* -------------------------------------------------------------------- */ |
2911 | | /* Determine if this ring is an inner ring or an outer ring */ |
2912 | | /* relative to all the other rings. For now we assume the */ |
2913 | | /* first ring is outer and all others are inner, but eventually */ |
2914 | | /* we need to fix this to handle multiple island polygons and */ |
2915 | | /* unordered sets of rings. */ |
2916 | | /* */ |
2917 | | /* -------------------------------------------------------------------- */ |
2918 | |
|
2919 | 0 | bool bInner = false; |
2920 | 0 | for (int iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++) |
2921 | 0 | { |
2922 | 0 | if (iCheckRing == iOpRing) |
2923 | 0 | continue; |
2924 | | |
2925 | 0 | const int nVertStartCheck = psObject->panPartStart[iCheckRing]; |
2926 | 0 | const int nVertCountCheck = SHPGetPartVertexCount(psObject, iCheckRing); |
2927 | | |
2928 | | /* Ignore rings that don't have the same (constant) Z value as the |
2929 | | * point. */ |
2930 | | /* As noted in SHPRewindObject(), this is a simplification */ |
2931 | | /* of what we should ideally do. */ |
2932 | 0 | if (!bSameZ) |
2933 | 0 | { |
2934 | 0 | int bZTestOK = TRUE; |
2935 | 0 | for (int iVert = nVertStartCheck + 1; |
2936 | 0 | iVert < nVertStartCheck + nVertCountCheck; ++iVert) |
2937 | 0 | { |
2938 | 0 | if (psObject->padfZ[iVert] != dfTestZ) |
2939 | 0 | { |
2940 | 0 | bZTestOK = FALSE; |
2941 | 0 | break; |
2942 | 0 | } |
2943 | 0 | } |
2944 | 0 | if (!bZTestOK) |
2945 | 0 | continue; |
2946 | 0 | } |
2947 | | |
2948 | 0 | for (int iEdge = 0; iEdge < nVertCountCheck; iEdge++) |
2949 | 0 | { |
2950 | 0 | int iNext; |
2951 | 0 | if (iEdge < nVertCountCheck - 1) |
2952 | 0 | iNext = iEdge + 1; |
2953 | 0 | else |
2954 | 0 | iNext = 0; |
2955 | |
|
2956 | 0 | const double y0 = psObject->padfY[iEdge + nVertStartCheck]; |
2957 | 0 | const double y1 = psObject->padfY[iNext + nVertStartCheck]; |
2958 | | /* Rule #1: |
2959 | | * Test whether the edge 'straddles' the horizontal ray from |
2960 | | * the test point (dfTestY,dfTestY) |
2961 | | * The rule #1 also excludes edges colinear with the ray. |
2962 | | */ |
2963 | 0 | if ((y0 < dfTestY && dfTestY <= y1) || |
2964 | 0 | (y1 < dfTestY && dfTestY <= y0)) |
2965 | 0 | { |
2966 | | /* Rule #2: |
2967 | | * Test if edge-ray intersection is on the right from the |
2968 | | * test point (dfTestY,dfTestY) |
2969 | | */ |
2970 | 0 | const double x0 = psObject->padfX[iEdge + nVertStartCheck]; |
2971 | 0 | const double x1 = psObject->padfX[iNext + nVertStartCheck]; |
2972 | 0 | const double intersect_minus_testX = |
2973 | 0 | (x0 - dfTestX) + (dfTestY - y0) / (y1 - y0) * (x1 - x0); |
2974 | |
|
2975 | 0 | if (fabs(intersect_minus_testX) <= |
2976 | 0 | dfRelativeTolerance * fabs(dfTestX)) |
2977 | 0 | { |
2978 | | /* Potential shared edge, or slightly overlapping polygons |
2979 | | */ |
2980 | 0 | return -1; |
2981 | 0 | } |
2982 | 0 | else if (intersect_minus_testX < 0) |
2983 | 0 | { |
2984 | 0 | bInner = !bInner; |
2985 | 0 | } |
2986 | 0 | } |
2987 | 0 | } |
2988 | 0 | } /* for iCheckRing */ |
2989 | 0 | return bInner; |
2990 | 0 | } |
2991 | | |
2992 | | /************************************************************************/ |
2993 | | /* SHPRewindObject() */ |
2994 | | /* */ |
2995 | | /* Reset the winding of polygon objects to adhere to the */ |
2996 | | /* specification. */ |
2997 | | /************************************************************************/ |
2998 | | |
2999 | | int SHPAPI_CALL SHPRewindObject(const SHPHandle hSHP, SHPObject *psObject) |
3000 | 0 | { |
3001 | 0 | (void)hSHP; |
3002 | | /* -------------------------------------------------------------------- */ |
3003 | | /* Do nothing if this is not a polygon object. */ |
3004 | | /* -------------------------------------------------------------------- */ |
3005 | 0 | if (psObject->nSHPType != SHPT_POLYGON && |
3006 | 0 | psObject->nSHPType != SHPT_POLYGONZ && |
3007 | 0 | psObject->nSHPType != SHPT_POLYGONM) |
3008 | 0 | return 0; |
3009 | | |
3010 | 0 | if (psObject->nVertices == 0 || psObject->nParts == 0) |
3011 | 0 | return 0; |
3012 | | |
3013 | | /* -------------------------------------------------------------------- */ |
3014 | | /* Test if all points have the same Z value. */ |
3015 | | /* -------------------------------------------------------------------- */ |
3016 | 0 | int bSameZ = TRUE; |
3017 | 0 | if (psObject->nSHPType == SHPT_POLYGONZ || |
3018 | 0 | psObject->nSHPType == SHPT_POLYGONM) |
3019 | 0 | { |
3020 | 0 | for (int iVert = 1; iVert < psObject->nVertices; ++iVert) |
3021 | 0 | { |
3022 | 0 | if (psObject->padfZ[iVert] != psObject->padfZ[0]) |
3023 | 0 | { |
3024 | 0 | bSameZ = FALSE; |
3025 | 0 | break; |
3026 | 0 | } |
3027 | 0 | } |
3028 | 0 | } |
3029 | | |
3030 | | /* -------------------------------------------------------------------- */ |
3031 | | /* Process each of the rings. */ |
3032 | | /* -------------------------------------------------------------------- */ |
3033 | 0 | int bAltered = 0; |
3034 | 0 | for (int iOpRing = 0; iOpRing < psObject->nParts; iOpRing++) |
3035 | 0 | { |
3036 | 0 | const int nVertStart = psObject->panPartStart[iOpRing]; |
3037 | 0 | const int nVertCount = SHPGetPartVertexCount(psObject, iOpRing); |
3038 | |
|
3039 | 0 | if (nVertCount < 2) |
3040 | 0 | continue; |
3041 | | |
3042 | | /* If a ring has a non-constant Z value, then consider it as an outer */ |
3043 | | /* ring. */ |
3044 | | /* NOTE: this is a rough approximation. If we were smarter, */ |
3045 | | /* we would check that all points of the ring are coplanar, and compare |
3046 | | */ |
3047 | | /* that to other rings in the same (oblique) plane. */ |
3048 | 0 | int bDoIsInnerRingTest = TRUE; |
3049 | 0 | if (!bSameZ) |
3050 | 0 | { |
3051 | 0 | int bPartSameZ = TRUE; |
3052 | 0 | for (int iVert = nVertStart + 1; iVert < nVertStart + nVertCount; |
3053 | 0 | ++iVert) |
3054 | 0 | { |
3055 | 0 | if (psObject->padfZ[iVert] != psObject->padfZ[nVertStart]) |
3056 | 0 | { |
3057 | 0 | bPartSameZ = FALSE; |
3058 | 0 | break; |
3059 | 0 | } |
3060 | 0 | } |
3061 | 0 | if (!bPartSameZ) |
3062 | 0 | bDoIsInnerRingTest = FALSE; |
3063 | 0 | } |
3064 | |
|
3065 | 0 | int bInner = FALSE; |
3066 | 0 | if (bDoIsInnerRingTest) |
3067 | 0 | { |
3068 | 0 | for (int iTolerance = 0; iTolerance < 2; iTolerance++) |
3069 | 0 | { |
3070 | | /* In a first attempt, use a relaxed criterion to decide if a |
3071 | | * point */ |
3072 | | /* is inside another ring. If all points of the current ring are |
3073 | | * in the */ |
3074 | | /* "grey" zone w.r.t that criterion, which seems really |
3075 | | * unlikely, */ |
3076 | | /* then use the strict criterion for another pass. */ |
3077 | 0 | const double dfRelativeTolerance = (iTolerance == 0) ? 1e-9 : 0; |
3078 | 0 | for (int iVert = nVertStart; |
3079 | 0 | iVert + 1 < nVertStart + nVertCount; ++iVert) |
3080 | 0 | { |
3081 | | /* Use point in the middle of segment to avoid testing |
3082 | | * common points of rings. |
3083 | | */ |
3084 | 0 | const double dfTestX = |
3085 | 0 | (psObject->padfX[iVert] + psObject->padfX[iVert + 1]) / |
3086 | 0 | 2; |
3087 | 0 | const double dfTestY = |
3088 | 0 | (psObject->padfY[iVert] + psObject->padfY[iVert + 1]) / |
3089 | 0 | 2; |
3090 | 0 | const double dfTestZ = |
3091 | 0 | !bSameZ ? psObject->padfZ[nVertStart] : 0; |
3092 | |
|
3093 | 0 | bInner = SHPRewindIsInnerRing(psObject, iOpRing, dfTestX, |
3094 | 0 | dfTestY, dfRelativeTolerance, |
3095 | 0 | bSameZ, dfTestZ); |
3096 | 0 | if (bInner >= 0) |
3097 | 0 | break; |
3098 | 0 | } |
3099 | 0 | if (bInner >= 0) |
3100 | 0 | break; |
3101 | 0 | } |
3102 | 0 | if (bInner < 0) |
3103 | 0 | { |
3104 | | /* Completely degenerate case. Do not bother touching order. */ |
3105 | 0 | continue; |
3106 | 0 | } |
3107 | 0 | } |
3108 | | |
3109 | | /* -------------------------------------------------------------------- */ |
3110 | | /* Determine the current order of this ring so we will know if */ |
3111 | | /* it has to be reversed. */ |
3112 | | /* -------------------------------------------------------------------- */ |
3113 | | |
3114 | 0 | double dfSum = psObject->padfX[nVertStart] * |
3115 | 0 | (psObject->padfY[nVertStart + 1] - |
3116 | 0 | psObject->padfY[nVertStart + nVertCount - 1]); |
3117 | 0 | int iVert = nVertStart + 1; |
3118 | 0 | for (; iVert < nVertStart + nVertCount - 1; iVert++) |
3119 | 0 | { |
3120 | 0 | dfSum += psObject->padfX[iVert] * |
3121 | 0 | (psObject->padfY[iVert + 1] - psObject->padfY[iVert - 1]); |
3122 | 0 | } |
3123 | |
|
3124 | 0 | dfSum += psObject->padfX[iVert] * |
3125 | 0 | (psObject->padfY[nVertStart] - psObject->padfY[iVert - 1]); |
3126 | | |
3127 | | /* -------------------------------------------------------------------- */ |
3128 | | /* Reverse if necessary. */ |
3129 | | /* -------------------------------------------------------------------- */ |
3130 | 0 | if ((dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner)) |
3131 | 0 | { |
3132 | 0 | bAltered++; |
3133 | 0 | for (int i = 0; i < nVertCount / 2; i++) |
3134 | 0 | { |
3135 | | /* Swap X */ |
3136 | 0 | double dfSaved = psObject->padfX[nVertStart + i]; |
3137 | 0 | psObject->padfX[nVertStart + i] = |
3138 | 0 | psObject->padfX[nVertStart + nVertCount - i - 1]; |
3139 | 0 | psObject->padfX[nVertStart + nVertCount - i - 1] = dfSaved; |
3140 | | |
3141 | | /* Swap Y */ |
3142 | 0 | dfSaved = psObject->padfY[nVertStart + i]; |
3143 | 0 | psObject->padfY[nVertStart + i] = |
3144 | 0 | psObject->padfY[nVertStart + nVertCount - i - 1]; |
3145 | 0 | psObject->padfY[nVertStart + nVertCount - i - 1] = dfSaved; |
3146 | | |
3147 | | /* Swap Z */ |
3148 | 0 | if (psObject->padfZ) |
3149 | 0 | { |
3150 | 0 | dfSaved = psObject->padfZ[nVertStart + i]; |
3151 | 0 | psObject->padfZ[nVertStart + i] = |
3152 | 0 | psObject->padfZ[nVertStart + nVertCount - i - 1]; |
3153 | 0 | psObject->padfZ[nVertStart + nVertCount - i - 1] = dfSaved; |
3154 | 0 | } |
3155 | | |
3156 | | /* Swap M */ |
3157 | 0 | if (psObject->padfM) |
3158 | 0 | { |
3159 | 0 | dfSaved = psObject->padfM[nVertStart + i]; |
3160 | 0 | psObject->padfM[nVertStart + i] = |
3161 | 0 | psObject->padfM[nVertStart + nVertCount - i - 1]; |
3162 | 0 | psObject->padfM[nVertStart + nVertCount - i - 1] = dfSaved; |
3163 | 0 | } |
3164 | 0 | } |
3165 | 0 | } |
3166 | 0 | } |
3167 | |
|
3168 | 0 | return bAltered; |
3169 | 0 | } |