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