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