/src/MapServer/src/mapshape.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: Implements support for shapefile access. |
6 | | * Authors: Steve Lime and Frank Warmerdam |
7 | | * |
8 | | * Note: |
9 | | * This code is entirely based on the previous work of Frank Warmerdam. It is |
10 | | * essentially shapelib 1.1.5. However, there were enough changes that it was |
11 | | * incorporated into the MapServer source to avoid confusion. Relicensed with |
12 | | * permission of Frank Warmerdam (shapelib author). See the README |
13 | | * for licence details. |
14 | | * |
15 | | ****************************************************************************** |
16 | | * Copyright (c) 1996-2005 Regents of the University of Minnesota. |
17 | | * |
18 | | * Permission is hereby granted, free of charge, to any person obtaining a |
19 | | * copy of this software and associated documentation files (the "Software"), |
20 | | * to deal in the Software without restriction, including without limitation |
21 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
22 | | * and/or sell copies of the Software, and to permit persons to whom the |
23 | | * Software is furnished to do so, subject to the following conditions: |
24 | | * |
25 | | * The above copyright notice and this permission notice shall be included in |
26 | | * all copies of this Software or works derived from this Software. |
27 | | * |
28 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
29 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
30 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
31 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
32 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
33 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
34 | | * DEALINGS IN THE SOFTWARE. |
35 | | ****************************************************************************/ |
36 | | |
37 | | #define NEED_IGNORE_RET_VAL |
38 | | |
39 | | #include <limits.h> |
40 | | #include <assert.h> |
41 | | #include <stdbool.h> |
42 | | #include "mapserver.h" |
43 | | #include "mapows.h" |
44 | | |
45 | | #include <cpl_conv.h> |
46 | | #include <ogr_srs_api.h> |
47 | | |
48 | | /* Only use this macro on 32-bit integers! */ |
49 | 0 | #define SWAP_FOUR_BYTES(data) CPL_SWAP32(data) |
50 | | |
51 | 0 | #define ByteCopy(a, b, c) memcpy(b, a, c) |
52 | | |
53 | | #ifdef __BYTE_ORDER__ |
54 | | /* GCC/clang predefined macro */ |
55 | 0 | #define bBigEndian (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) |
56 | | #elif defined(_MSC_VER) |
57 | | /* MSVC doesn't support the C99 trick below, but all Microsoft |
58 | | platforms are little-endian */ |
59 | | #define bBigEndian false |
60 | | #else |
61 | | /* generic check */ |
62 | | #define bBigEndian \ |
63 | | (((union { \ |
64 | | int in; \ |
65 | | char out; \ |
66 | | }){1}) \ |
67 | | .out) |
68 | | #endif |
69 | | |
70 | | /* SHX reading */ |
71 | | static int msSHXLoadAll(SHPHandle psSHP); |
72 | | static int msSHXReadOffset(SHPHandle psSHP, int hEntity); |
73 | | static int msSHXReadSize(SHPHandle psSHP, int hEntity); |
74 | | |
75 | | /************************************************************************/ |
76 | | /* SwapWord() */ |
77 | | /* */ |
78 | | /* Swap a 2, 4 or 8 byte word. */ |
79 | | /************************************************************************/ |
80 | 0 | static void SwapWord(int length, void *wordP) { |
81 | 0 | int i; |
82 | 0 | uchar temp; |
83 | |
|
84 | 0 | for (i = 0; i < length / 2; i++) { |
85 | 0 | temp = ((uchar *)wordP)[i]; |
86 | 0 | ((uchar *)wordP)[i] = ((uchar *)wordP)[length - i - 1]; |
87 | 0 | ((uchar *)wordP)[length - i - 1] = temp; |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | | /************************************************************************/ |
92 | | /* SfRealloc() */ |
93 | | /* */ |
94 | | /* A realloc cover function that will access a NULL pointer as */ |
95 | | /* a valid input. */ |
96 | | /************************************************************************/ |
97 | 0 | static void *SfRealloc(void *pMem, int nNewSize) { |
98 | 0 | return realloc(pMem, nNewSize); |
99 | 0 | } |
100 | | |
101 | | /************************************************************************/ |
102 | | /* writeHeader() */ |
103 | | /* */ |
104 | | /* Write out a header for the .shp and .shx files as well as the */ |
105 | | /* contents of the index (.shx) file. */ |
106 | | /************************************************************************/ |
107 | 0 | static void writeHeader(SHPHandle psSHP) { |
108 | 0 | uchar abyHeader[100]; |
109 | 0 | int i; |
110 | 0 | ms_int32 i32; |
111 | 0 | double dValue; |
112 | 0 | ms_int32 *panSHX; |
113 | | |
114 | | /* -------------------------------------------------------------------- */ |
115 | | /* Prepare header block for .shp file. */ |
116 | | /* -------------------------------------------------------------------- */ |
117 | 0 | for (i = 0; i < 100; i++) |
118 | 0 | abyHeader[i] = 0; |
119 | |
|
120 | 0 | abyHeader[2] = 0x27; /* magic cookie */ |
121 | 0 | abyHeader[3] = 0x0a; |
122 | |
|
123 | 0 | i32 = psSHP->nFileSize / 2; /* file size */ |
124 | 0 | ByteCopy(&i32, abyHeader + 24, 4); |
125 | 0 | if (!bBigEndian) |
126 | 0 | SwapWord(4, abyHeader + 24); |
127 | |
|
128 | 0 | i32 = 1000; /* version */ |
129 | 0 | ByteCopy(&i32, abyHeader + 28, 4); |
130 | 0 | if (bBigEndian) |
131 | 0 | SwapWord(4, abyHeader + 28); |
132 | |
|
133 | 0 | i32 = psSHP->nShapeType; /* shape type */ |
134 | 0 | ByteCopy(&i32, abyHeader + 32, 4); |
135 | 0 | if (bBigEndian) |
136 | 0 | SwapWord(4, abyHeader + 32); |
137 | |
|
138 | 0 | dValue = psSHP->adBoundsMin[0]; /* set bounds */ |
139 | 0 | ByteCopy(&dValue, abyHeader + 36, 8); |
140 | 0 | if (bBigEndian) |
141 | 0 | SwapWord(8, abyHeader + 36); |
142 | |
|
143 | 0 | dValue = psSHP->adBoundsMin[1]; |
144 | 0 | ByteCopy(&dValue, abyHeader + 44, 8); |
145 | 0 | if (bBigEndian) |
146 | 0 | SwapWord(8, abyHeader + 44); |
147 | |
|
148 | 0 | dValue = psSHP->adBoundsMax[0]; |
149 | 0 | ByteCopy(&dValue, abyHeader + 52, 8); |
150 | 0 | if (bBigEndian) |
151 | 0 | SwapWord(8, abyHeader + 52); |
152 | |
|
153 | 0 | dValue = psSHP->adBoundsMax[1]; |
154 | 0 | ByteCopy(&dValue, abyHeader + 60, 8); |
155 | 0 | if (bBigEndian) |
156 | 0 | SwapWord(8, abyHeader + 60); |
157 | |
|
158 | 0 | dValue = psSHP->adBoundsMin[2]; /* z */ |
159 | 0 | ByteCopy(&dValue, abyHeader + 68, 8); |
160 | 0 | if (bBigEndian) |
161 | 0 | SwapWord(8, abyHeader + 68); |
162 | |
|
163 | 0 | dValue = psSHP->adBoundsMax[2]; |
164 | 0 | ByteCopy(&dValue, abyHeader + 76, 8); |
165 | 0 | if (bBigEndian) |
166 | 0 | SwapWord(8, abyHeader + 76); |
167 | |
|
168 | 0 | dValue = psSHP->adBoundsMin[3]; /* m */ |
169 | 0 | ByteCopy(&dValue, abyHeader + 84, 8); |
170 | 0 | if (bBigEndian) |
171 | 0 | SwapWord(8, abyHeader + 84); |
172 | |
|
173 | 0 | dValue = psSHP->adBoundsMax[3]; |
174 | 0 | ByteCopy(&dValue, abyHeader + 92, 8); |
175 | 0 | if (bBigEndian) |
176 | 0 | SwapWord(8, abyHeader + 92); |
177 | | |
178 | | /* -------------------------------------------------------------------- */ |
179 | | /* Write .shp file header. */ |
180 | | /* -------------------------------------------------------------------- */ |
181 | 0 | VSIFSeekL(psSHP->fpSHP, 0, 0); |
182 | 0 | VSIFWriteL(abyHeader, 100, 1, psSHP->fpSHP); |
183 | | |
184 | | /* -------------------------------------------------------------------- */ |
185 | | /* Prepare, and write .shx file header. */ |
186 | | /* -------------------------------------------------------------------- */ |
187 | 0 | i32 = (psSHP->nRecords * 2 * sizeof(ms_int32) + 100) / 2; /* file size */ |
188 | 0 | ByteCopy(&i32, abyHeader + 24, 4); |
189 | 0 | if (!bBigEndian) |
190 | 0 | SwapWord(4, abyHeader + 24); |
191 | |
|
192 | 0 | VSIFSeekL(psSHP->fpSHX, 0, 0); |
193 | 0 | VSIFWriteL(abyHeader, 100, 1, psSHP->fpSHX); |
194 | | |
195 | | /* -------------------------------------------------------------------- */ |
196 | | /* Write out the .shx contents. */ |
197 | | /* -------------------------------------------------------------------- */ |
198 | 0 | panSHX = (ms_int32 *)msSmallMalloc(sizeof(ms_int32) * 2 * psSHP->nRecords); |
199 | |
|
200 | 0 | for (i = 0; i < psSHP->nRecords; i++) { |
201 | 0 | panSHX[i * 2] = psSHP->panRecOffset[i] / 2; |
202 | 0 | panSHX[i * 2 + 1] = psSHP->panRecSize[i] / 2; |
203 | 0 | if (!bBigEndian) { |
204 | 0 | *(panSHX + i * 2) = SWAP_FOUR_BYTES(*(panSHX + i * 2)); |
205 | 0 | *(panSHX + i * 2 + 1) = SWAP_FOUR_BYTES(*(panSHX + i * 2 + 1)); |
206 | 0 | } |
207 | 0 | } |
208 | |
|
209 | 0 | VSIFWriteL(panSHX, sizeof(ms_int32) * 2, psSHP->nRecords, psSHP->fpSHX); |
210 | |
|
211 | 0 | free(panSHX); |
212 | 0 | } |
213 | | |
214 | 0 | SHPHandle msSHPOpenVirtualFile(VSILFILE *fpSHP, VSILFILE *fpSHX) { |
215 | | /* -------------------------------------------------------------------- */ |
216 | | /* Initialize the info structure. */ |
217 | | /* -------------------------------------------------------------------- */ |
218 | 0 | SHPHandle psSHP = (SHPHandle)msSmallMalloc(sizeof(SHPInfo)); |
219 | |
|
220 | 0 | psSHP->bUpdated = MS_FALSE; |
221 | |
|
222 | 0 | psSHP->pabyRec = NULL; |
223 | 0 | psSHP->panParts = NULL; |
224 | 0 | psSHP->nBufSize = psSHP->nPartMax = 0; |
225 | |
|
226 | 0 | psSHP->fpSHP = fpSHP; |
227 | 0 | psSHP->fpSHX = fpSHX; |
228 | | |
229 | | /* -------------------------------------------------------------------- */ |
230 | | /* Read the file size from the SHP file. */ |
231 | | /* -------------------------------------------------------------------- */ |
232 | 0 | uchar *pabyBuf = (uchar *)msSmallMalloc(100); |
233 | 0 | if (1 != VSIFReadL(pabyBuf, 100, 1, psSHP->fpSHP)) { |
234 | 0 | VSIFCloseL(psSHP->fpSHP); |
235 | 0 | VSIFCloseL(psSHP->fpSHX); |
236 | 0 | free(psSHP); |
237 | 0 | free(pabyBuf); |
238 | 0 | return (NULL); |
239 | 0 | } |
240 | | |
241 | 0 | if (!bBigEndian) |
242 | 0 | SwapWord(4, pabyBuf + 24); |
243 | 0 | memcpy(&psSHP->nFileSize, pabyBuf + 24, 4); |
244 | 0 | if (psSHP->nFileSize < 0 || psSHP->nFileSize > INT_MAX / 2) { |
245 | 0 | msDebug("Invalid / unsupported nFileSize = %d value. Got it from actual " |
246 | 0 | "file length", |
247 | 0 | psSHP->nFileSize); |
248 | 0 | VSIFSeekL(psSHP->fpSHP, 0, SEEK_END); |
249 | 0 | vsi_l_offset nSize = VSIFTellL(psSHP->fpSHP); |
250 | 0 | if (nSize > (vsi_l_offset)INT_MAX) { |
251 | 0 | msDebug("Actual .shp size is larger than 2 GB. Not supported. " |
252 | 0 | "Invalidating nFileSize"); |
253 | 0 | psSHP->nFileSize = 0; |
254 | 0 | } else |
255 | 0 | psSHP->nFileSize = (int)nSize; |
256 | 0 | } else { |
257 | 0 | psSHP->nFileSize *= 2; |
258 | 0 | } |
259 | | |
260 | | /* -------------------------------------------------------------------- */ |
261 | | /* Read SHX file Header info */ |
262 | | /* -------------------------------------------------------------------- */ |
263 | 0 | if (1 != VSIFReadL(pabyBuf, 100, 1, psSHP->fpSHX)) { |
264 | 0 | msSetError(MS_SHPERR, "Corrupted .shx file", "msSHPOpen()"); |
265 | 0 | VSIFCloseL(psSHP->fpSHP); |
266 | 0 | VSIFCloseL(psSHP->fpSHX); |
267 | 0 | free(psSHP); |
268 | 0 | free(pabyBuf); |
269 | 0 | return (NULL); |
270 | 0 | } |
271 | | |
272 | 0 | if (pabyBuf[0] != 0 || pabyBuf[1] != 0 || pabyBuf[2] != 0x27 || |
273 | 0 | (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d)) { |
274 | 0 | msSetError(MS_SHPERR, "Corrupted .shx file", "msSHPOpen()"); |
275 | 0 | VSIFCloseL(psSHP->fpSHP); |
276 | 0 | VSIFCloseL(psSHP->fpSHX); |
277 | 0 | free(psSHP); |
278 | 0 | free(pabyBuf); |
279 | |
|
280 | 0 | return (NULL); |
281 | 0 | } |
282 | | |
283 | 0 | int nSHXHalfFileSize; |
284 | 0 | if (!bBigEndian) |
285 | 0 | SwapWord(4, pabyBuf + 24); |
286 | 0 | memcpy(&nSHXHalfFileSize, pabyBuf + 24, 4); |
287 | 0 | if (nSHXHalfFileSize >= 50) |
288 | 0 | psSHP->nRecords = (nSHXHalfFileSize - 50) / 4; // (nSHXFileSize - 100) / 8 |
289 | 0 | else |
290 | 0 | psSHP->nRecords = -1; |
291 | |
|
292 | 0 | if (psSHP->nRecords < 0 || psSHP->nRecords > 256000000) { |
293 | 0 | msSetError(MS_SHPERR, "Corrupted .shx file : nRecords = %d.", "msSHPOpen()", |
294 | 0 | psSHP->nRecords); |
295 | 0 | VSIFCloseL(psSHP->fpSHP); |
296 | 0 | VSIFCloseL(psSHP->fpSHX); |
297 | 0 | free(psSHP); |
298 | 0 | free(pabyBuf); |
299 | 0 | return (NULL); |
300 | 0 | } |
301 | | |
302 | 0 | psSHP->nShapeType = pabyBuf[32]; |
303 | |
|
304 | 0 | if (bBigEndian) |
305 | 0 | SwapWord(8, pabyBuf + 36); |
306 | 0 | double dValue; |
307 | 0 | memcpy(&dValue, pabyBuf + 36, 8); |
308 | 0 | psSHP->adBoundsMin[0] = dValue; |
309 | |
|
310 | 0 | if (bBigEndian) |
311 | 0 | SwapWord(8, pabyBuf + 44); |
312 | 0 | memcpy(&dValue, pabyBuf + 44, 8); |
313 | 0 | psSHP->adBoundsMin[1] = dValue; |
314 | |
|
315 | 0 | if (bBigEndian) |
316 | 0 | SwapWord(8, pabyBuf + 52); |
317 | 0 | memcpy(&dValue, pabyBuf + 52, 8); |
318 | 0 | psSHP->adBoundsMax[0] = dValue; |
319 | |
|
320 | 0 | if (bBigEndian) |
321 | 0 | SwapWord(8, pabyBuf + 60); |
322 | 0 | memcpy(&dValue, pabyBuf + 60, 8); |
323 | 0 | psSHP->adBoundsMax[1] = dValue; |
324 | |
|
325 | 0 | if (bBigEndian) |
326 | 0 | SwapWord(8, pabyBuf + 68); /* z */ |
327 | 0 | memcpy(&dValue, pabyBuf + 68, 8); |
328 | 0 | psSHP->adBoundsMin[2] = dValue; |
329 | |
|
330 | 0 | if (bBigEndian) |
331 | 0 | SwapWord(8, pabyBuf + 76); |
332 | 0 | memcpy(&dValue, pabyBuf + 76, 8); |
333 | 0 | psSHP->adBoundsMax[2] = dValue; |
334 | |
|
335 | 0 | if (bBigEndian) |
336 | 0 | SwapWord(8, pabyBuf + 84); /* m */ |
337 | 0 | memcpy(&dValue, pabyBuf + 84, 8); |
338 | 0 | psSHP->adBoundsMin[3] = dValue; |
339 | |
|
340 | 0 | if (bBigEndian) |
341 | 0 | SwapWord(8, pabyBuf + 92); |
342 | 0 | memcpy(&dValue, pabyBuf + 92, 8); |
343 | 0 | psSHP->adBoundsMax[3] = dValue; |
344 | 0 | free(pabyBuf); |
345 | | |
346 | | /* -------------------------------------------------------------------- */ |
347 | | /* Read the .shx file to get the offsets to each record in */ |
348 | | /* the .shp file. */ |
349 | | /* -------------------------------------------------------------------- */ |
350 | 0 | psSHP->nMaxRecords = psSHP->nRecords; |
351 | | |
352 | | /* Our in-memory cache of offset information */ |
353 | 0 | psSHP->panRecOffset = (int *)malloc(sizeof(int) * psSHP->nMaxRecords); |
354 | | /* Our in-memory cache of size information */ |
355 | 0 | psSHP->panRecSize = (int *)malloc(sizeof(int) * psSHP->nMaxRecords); |
356 | | /* The completeness information for our in-memory cache */ |
357 | 0 | psSHP->panRecLoaded = |
358 | 0 | msAllocBitArray(1 + (psSHP->nMaxRecords / SHX_BUFFER_PAGE)); |
359 | | /* Is our in-memory cache completely populated? */ |
360 | 0 | psSHP->panRecAllLoaded = 0; |
361 | | |
362 | | /* malloc failed? clean up and shut down */ |
363 | 0 | if (psSHP->panRecOffset == NULL || psSHP->panRecSize == NULL || |
364 | 0 | psSHP->panRecLoaded == NULL) { |
365 | 0 | free(psSHP->panRecOffset); |
366 | 0 | free(psSHP->panRecSize); |
367 | 0 | free(psSHP->panRecLoaded); |
368 | 0 | VSIFCloseL(psSHP->fpSHP); |
369 | 0 | VSIFCloseL(psSHP->fpSHX); |
370 | 0 | free(psSHP); |
371 | 0 | msSetError(MS_MEMERR, "Out of memory", "msSHPOpen()"); |
372 | 0 | return (NULL); |
373 | 0 | } |
374 | | |
375 | 0 | return (psSHP); |
376 | 0 | } |
377 | | |
378 | | /************************************************************************/ |
379 | | /* msSHPOpen() */ |
380 | | /* */ |
381 | | /* Open the .shp and .shx files based on the basename of the */ |
382 | | /* files or either file name. */ |
383 | | /************************************************************************/ |
384 | 0 | SHPHandle msSHPOpen(const char *pszLayer, const char *pszAccess) { |
385 | 0 | char *pszFullname, *pszBasename; |
386 | |
|
387 | 0 | int i; |
388 | | |
389 | | /* -------------------------------------------------------------------- */ |
390 | | /* Ensure the access string is one of the legal ones. We */ |
391 | | /* ensure the result string indicates binary to avoid common */ |
392 | | /* problems on Windows. */ |
393 | | /* -------------------------------------------------------------------- */ |
394 | 0 | if (strcmp(pszAccess, "rb+") == 0 || strcmp(pszAccess, "r+b") == 0 || |
395 | 0 | strcmp(pszAccess, "r+") == 0) |
396 | 0 | pszAccess = "r+b"; |
397 | 0 | else |
398 | 0 | pszAccess = "rb"; |
399 | | |
400 | | /* -------------------------------------------------------------------- */ |
401 | | /* Compute the base (layer) name. If there is any extension */ |
402 | | /* on the passed in filename we will strip it off. */ |
403 | | /* -------------------------------------------------------------------- */ |
404 | 0 | pszBasename = (char *)msSmallMalloc(strlen(pszLayer) + 5); |
405 | 0 | strcpy(pszBasename, pszLayer); |
406 | 0 | for (i = strlen(pszBasename) - 1; |
407 | 0 | i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && |
408 | 0 | pszBasename[i] != '\\'; |
409 | 0 | i--) { |
410 | 0 | } |
411 | |
|
412 | 0 | if (pszBasename[i] == '.') |
413 | 0 | pszBasename[i] = '\0'; |
414 | | |
415 | | /* -------------------------------------------------------------------- */ |
416 | | /* Open the .shp and .shx files. Note that files pulled from */ |
417 | | /* a PC to Unix with upper case filenames won't work! */ |
418 | | /* -------------------------------------------------------------------- */ |
419 | 0 | pszFullname = (char *)msSmallMalloc(strlen(pszBasename) + 5); |
420 | 0 | sprintf(pszFullname, "%s.shp", pszBasename); |
421 | 0 | VSILFILE *fpSHP = VSIFOpenL(pszFullname, pszAccess); |
422 | 0 | if (fpSHP == NULL) { |
423 | 0 | sprintf(pszFullname, "%s.SHP", pszBasename); |
424 | 0 | fpSHP = VSIFOpenL(pszFullname, pszAccess); |
425 | 0 | } |
426 | 0 | if (fpSHP == NULL) { |
427 | 0 | msFree(pszBasename); |
428 | 0 | msFree(pszFullname); |
429 | 0 | return (NULL); |
430 | 0 | } |
431 | | |
432 | 0 | sprintf(pszFullname, "%s.shx", pszBasename); |
433 | 0 | VSILFILE *fpSHX = VSIFOpenL(pszFullname, pszAccess); |
434 | 0 | if (fpSHX == NULL) { |
435 | 0 | sprintf(pszFullname, "%s.SHX", pszBasename); |
436 | 0 | fpSHX = VSIFOpenL(pszFullname, pszAccess); |
437 | 0 | } |
438 | 0 | if (fpSHX == NULL) { |
439 | 0 | VSIFCloseL(fpSHP); |
440 | 0 | msFree(pszBasename); |
441 | 0 | msFree(pszFullname); |
442 | 0 | return (NULL); |
443 | 0 | } |
444 | | |
445 | 0 | free(pszFullname); |
446 | 0 | free(pszBasename); |
447 | |
|
448 | 0 | return msSHPOpenVirtualFile(fpSHP, fpSHX); |
449 | 0 | } |
450 | | |
451 | | /************************************************************************/ |
452 | | /* msSHPClose() */ |
453 | | /* */ |
454 | | /* Close the .shp and .shx files. */ |
455 | | /************************************************************************/ |
456 | 0 | void msSHPClose(SHPHandle psSHP) { |
457 | | /* -------------------------------------------------------------------- */ |
458 | | /* Update the header if we have modified anything. */ |
459 | | /* -------------------------------------------------------------------- */ |
460 | 0 | if (psSHP->bUpdated) |
461 | 0 | writeHeader(psSHP); |
462 | | |
463 | | /* -------------------------------------------------------------------- */ |
464 | | /* Free all resources, and close files. */ |
465 | | /* -------------------------------------------------------------------- */ |
466 | 0 | free(psSHP->panRecOffset); |
467 | 0 | free(psSHP->panRecSize); |
468 | 0 | free(psSHP->panRecLoaded); |
469 | |
|
470 | 0 | free(psSHP->pabyRec); |
471 | 0 | free(psSHP->panParts); |
472 | |
|
473 | 0 | VSIFCloseL(psSHP->fpSHX); |
474 | 0 | VSIFCloseL(psSHP->fpSHP); |
475 | |
|
476 | 0 | free(psSHP); |
477 | 0 | } |
478 | | |
479 | | /************************************************************************/ |
480 | | /* msSHPGetInfo() */ |
481 | | /* */ |
482 | | /* Fetch general information about the shape file. */ |
483 | | /************************************************************************/ |
484 | 0 | void msSHPGetInfo(SHPHandle psSHP, int *pnEntities, int *pnShapeType) { |
485 | 0 | if (pnEntities) |
486 | 0 | *pnEntities = psSHP->nRecords; |
487 | |
|
488 | 0 | if (pnShapeType) |
489 | 0 | *pnShapeType = psSHP->nShapeType; |
490 | 0 | } |
491 | | |
492 | | /************************************************************************/ |
493 | | /* msSHPCreate() */ |
494 | | /* */ |
495 | | /* Create a new shape file and return a handle to the open */ |
496 | | /* shape file with read/write access. */ |
497 | | /************************************************************************/ |
498 | 0 | SHPHandle msSHPCreate(const char *pszLayer, int nShapeType) { |
499 | 0 | char *pszBasename, *pszFullname; |
500 | 0 | int i; |
501 | 0 | VSILFILE *fpSHP, *fpSHX; |
502 | 0 | uchar abyHeader[100]; |
503 | 0 | ms_int32 i32; |
504 | 0 | double dValue; |
505 | | |
506 | | /* -------------------------------------------------------------------- */ |
507 | | /* Compute the base (layer) name. If there is any extension */ |
508 | | /* on the passed in filename we will strip it off. */ |
509 | | /* -------------------------------------------------------------------- */ |
510 | 0 | pszBasename = (char *)msSmallMalloc(strlen(pszLayer) + 5); |
511 | 0 | strcpy(pszBasename, pszLayer); |
512 | 0 | for (i = strlen(pszBasename) - 1; |
513 | 0 | i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && |
514 | 0 | pszBasename[i] != '\\'; |
515 | 0 | i--) { |
516 | 0 | } |
517 | |
|
518 | 0 | if (pszBasename[i] == '.') |
519 | 0 | pszBasename[i] = '\0'; |
520 | | |
521 | | /* -------------------------------------------------------------------- */ |
522 | | /* Open the two files so we can write their headers. */ |
523 | | /* -------------------------------------------------------------------- */ |
524 | 0 | pszFullname = (char *)msSmallMalloc(strlen(pszBasename) + 5); |
525 | 0 | sprintf(pszFullname, "%s.shp", pszBasename); |
526 | 0 | fpSHP = VSIFOpenL(pszFullname, "wb"); |
527 | 0 | if (fpSHP == NULL) { |
528 | 0 | free(pszFullname); |
529 | 0 | free(pszBasename); |
530 | 0 | return (NULL); |
531 | 0 | } |
532 | | |
533 | 0 | sprintf(pszFullname, "%s.shx", pszBasename); |
534 | 0 | fpSHX = VSIFOpenL(pszFullname, "wb"); |
535 | 0 | if (fpSHX == NULL) { |
536 | 0 | VSIFCloseL(fpSHP); |
537 | 0 | free(pszFullname); |
538 | 0 | free(pszBasename); |
539 | 0 | return (NULL); |
540 | 0 | } |
541 | | |
542 | 0 | free(pszFullname); |
543 | 0 | free(pszBasename); |
544 | | |
545 | | /* -------------------------------------------------------------------- */ |
546 | | /* Prepare header block for .shp file. */ |
547 | | /* -------------------------------------------------------------------- */ |
548 | 0 | for (i = 0; i < 100; i++) |
549 | 0 | abyHeader[i] = 0; |
550 | |
|
551 | 0 | abyHeader[2] = 0x27; /* magic cookie */ |
552 | 0 | abyHeader[3] = 0x0a; |
553 | |
|
554 | 0 | i32 = 50; /* file size */ |
555 | 0 | ByteCopy(&i32, abyHeader + 24, 4); |
556 | 0 | if (!bBigEndian) |
557 | 0 | SwapWord(4, abyHeader + 24); |
558 | |
|
559 | 0 | i32 = 1000; /* version */ |
560 | 0 | ByteCopy(&i32, abyHeader + 28, 4); |
561 | 0 | if (bBigEndian) |
562 | 0 | SwapWord(4, abyHeader + 28); |
563 | |
|
564 | 0 | i32 = nShapeType; /* shape type */ |
565 | 0 | ByteCopy(&i32, abyHeader + 32, 4); |
566 | 0 | if (bBigEndian) |
567 | 0 | SwapWord(4, abyHeader + 32); |
568 | |
|
569 | 0 | dValue = 0.0; /* set bounds */ |
570 | 0 | ByteCopy(&dValue, abyHeader + 36, 8); |
571 | 0 | ByteCopy(&dValue, abyHeader + 44, 8); |
572 | 0 | ByteCopy(&dValue, abyHeader + 52, 8); |
573 | 0 | ByteCopy(&dValue, abyHeader + 60, 8); |
574 | | |
575 | | /* -------------------------------------------------------------------- */ |
576 | | /* Write .shp file header. */ |
577 | | /* -------------------------------------------------------------------- */ |
578 | 0 | VSIFWriteL(abyHeader, 100, 1, fpSHP); |
579 | | |
580 | | /* -------------------------------------------------------------------- */ |
581 | | /* Prepare, and write .shx file header. */ |
582 | | /* -------------------------------------------------------------------- */ |
583 | 0 | i32 = 50; /* file size */ |
584 | 0 | ByteCopy(&i32, abyHeader + 24, 4); |
585 | 0 | if (!bBigEndian) |
586 | 0 | SwapWord(4, abyHeader + 24); |
587 | |
|
588 | 0 | VSIFWriteL(abyHeader, 100, 1, fpSHX); |
589 | | |
590 | | /* -------------------------------------------------------------------- */ |
591 | | /* Close the files, and then open them as regular existing files. */ |
592 | | /* -------------------------------------------------------------------- */ |
593 | 0 | VSIFCloseL(fpSHP); |
594 | 0 | VSIFCloseL(fpSHX); |
595 | |
|
596 | 0 | return (msSHPOpen(pszLayer, "rb+")); |
597 | 0 | } |
598 | | |
599 | | /************************************************************************/ |
600 | | /* writeBounds() */ |
601 | | /* */ |
602 | | /* Compute a bounds rectangle for a shape, and set it into the */ |
603 | | /* indicated location in the record. */ |
604 | | /************************************************************************/ |
605 | 0 | static void writeBounds(uchar *pabyRec, shapeObj *shape, int nVCount) { |
606 | 0 | double dXMin, dXMax, dYMin, dYMax; |
607 | 0 | int i, j; |
608 | |
|
609 | 0 | if (nVCount == 0) { |
610 | 0 | dXMin = dYMin = dXMax = dYMax = 0.0; |
611 | 0 | } else { |
612 | 0 | dXMin = dXMax = shape->line[0].point[0].x; |
613 | 0 | dYMin = dYMax = shape->line[0].point[0].y; |
614 | |
|
615 | 0 | for (i = 0; i < shape->numlines; i++) { |
616 | 0 | for (j = 0; j < shape->line[i].numpoints; j++) { |
617 | 0 | dXMin = MS_MIN(dXMin, shape->line[i].point[j].x); |
618 | 0 | dXMax = MS_MAX(dXMax, shape->line[i].point[j].x); |
619 | 0 | dYMin = MS_MIN(dYMin, shape->line[i].point[j].y); |
620 | 0 | dYMax = MS_MAX(dYMax, shape->line[i].point[j].y); |
621 | 0 | } |
622 | 0 | } |
623 | 0 | } |
624 | |
|
625 | 0 | if (bBigEndian) { |
626 | 0 | SwapWord(8, &dXMin); |
627 | 0 | SwapWord(8, &dYMin); |
628 | 0 | SwapWord(8, &dXMax); |
629 | 0 | SwapWord(8, &dYMax); |
630 | 0 | } |
631 | |
|
632 | 0 | ByteCopy(&dXMin, pabyRec + 0, 8); |
633 | 0 | ByteCopy(&dYMin, pabyRec + 8, 8); |
634 | 0 | ByteCopy(&dXMax, pabyRec + 16, 8); |
635 | 0 | ByteCopy(&dYMax, pabyRec + 24, 8); |
636 | 0 | } |
637 | | |
638 | 0 | int msSHPWritePoint(SHPHandle psSHP, pointObj *point) { |
639 | 0 | int nRecordOffset, nRecordSize = 0; |
640 | 0 | uchar *pabyRec; |
641 | 0 | ms_int32 i32, nPoints, nParts; |
642 | |
|
643 | 0 | if (psSHP->nShapeType != SHP_POINT) |
644 | 0 | return (-1); |
645 | 0 | if (psSHP->nFileSize == 0) |
646 | 0 | return -1; |
647 | | |
648 | 0 | psSHP->bUpdated = MS_TRUE; |
649 | | |
650 | | /* Fill the SHX buffer if it is not already full. */ |
651 | 0 | if (!psSHP->panRecAllLoaded) |
652 | 0 | msSHXLoadAll(psSHP); |
653 | | |
654 | | /* -------------------------------------------------------------------- */ |
655 | | /* Add the new entity to the in memory index. */ |
656 | | /* -------------------------------------------------------------------- */ |
657 | 0 | psSHP->nRecords++; |
658 | 0 | if (psSHP->nRecords > psSHP->nMaxRecords) { |
659 | 0 | psSHP->nMaxRecords = (int)(psSHP->nMaxRecords * 1.3 + 100); |
660 | |
|
661 | 0 | psSHP->panRecOffset = |
662 | 0 | (int *)SfRealloc(psSHP->panRecOffset, sizeof(int) * psSHP->nMaxRecords); |
663 | 0 | psSHP->panRecSize = |
664 | 0 | (int *)SfRealloc(psSHP->panRecSize, sizeof(int) * psSHP->nMaxRecords); |
665 | 0 | } |
666 | | |
667 | | /* -------------------------------------------------------------------- */ |
668 | | /* Compute a few things. */ |
669 | | /* -------------------------------------------------------------------- */ |
670 | 0 | nPoints = 1; |
671 | 0 | nParts = 1; |
672 | | |
673 | | /* -------------------------------------------------------------------- */ |
674 | | /* Initialize record. */ |
675 | | /* -------------------------------------------------------------------- */ |
676 | 0 | psSHP->panRecOffset[psSHP->nRecords - 1] = nRecordOffset = psSHP->nFileSize; |
677 | |
|
678 | 0 | pabyRec = |
679 | 0 | (uchar *)msSmallMalloc(nPoints * 2 * sizeof(double) + nParts * 4 + 128); |
680 | | |
681 | | /* -------------------------------------------------------------------- */ |
682 | | /* Write vertices for a point. */ |
683 | | /* -------------------------------------------------------------------- */ |
684 | 0 | ByteCopy(&(point->x), pabyRec + 12, 8); |
685 | 0 | ByteCopy(&(point->y), pabyRec + 20, 8); |
686 | |
|
687 | 0 | if (bBigEndian) { |
688 | 0 | SwapWord(8, pabyRec + 12); |
689 | 0 | SwapWord(8, pabyRec + 20); |
690 | 0 | } |
691 | |
|
692 | 0 | nRecordSize = 20; |
693 | | |
694 | | /* -------------------------------------------------------------------- */ |
695 | | /* Set the shape type, record number, and record size. */ |
696 | | /* -------------------------------------------------------------------- */ |
697 | 0 | i32 = psSHP->nRecords - 1 + 1; /* record # */ |
698 | 0 | if (!bBigEndian) |
699 | 0 | i32 = SWAP_FOUR_BYTES(i32); |
700 | 0 | ByteCopy(&i32, pabyRec, 4); |
701 | |
|
702 | 0 | i32 = nRecordSize / 2; /* record size */ |
703 | 0 | if (!bBigEndian) |
704 | 0 | i32 = SWAP_FOUR_BYTES(i32); |
705 | 0 | ByteCopy(&i32, pabyRec + 4, 4); |
706 | |
|
707 | 0 | i32 = psSHP->nShapeType; /* shape type */ |
708 | 0 | if (bBigEndian) |
709 | 0 | i32 = SWAP_FOUR_BYTES(i32); |
710 | 0 | ByteCopy(&i32, pabyRec + 8, 4); |
711 | | |
712 | | /* -------------------------------------------------------------------- */ |
713 | | /* Write out record. */ |
714 | | /* -------------------------------------------------------------------- */ |
715 | 0 | if (VSIFSeekL(psSHP->fpSHP, nRecordOffset, 0) == 0) { |
716 | 0 | VSIFWriteL(pabyRec, nRecordSize + 8, 1, psSHP->fpSHP); |
717 | |
|
718 | 0 | psSHP->panRecSize[psSHP->nRecords - 1] = nRecordSize; |
719 | 0 | psSHP->nFileSize += nRecordSize + 8; |
720 | | |
721 | | /* -------------------------------------------------------------------- */ |
722 | | /* Expand file wide bounds based on this shape. */ |
723 | | /* -------------------------------------------------------------------- */ |
724 | 0 | if (psSHP->nRecords == 1) { |
725 | 0 | psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = point->x; |
726 | 0 | psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = point->y; |
727 | 0 | } else { |
728 | 0 | psSHP->adBoundsMin[0] = MS_MIN(psSHP->adBoundsMin[0], point->x); |
729 | 0 | psSHP->adBoundsMin[1] = MS_MIN(psSHP->adBoundsMin[1], point->y); |
730 | 0 | psSHP->adBoundsMax[0] = MS_MAX(psSHP->adBoundsMax[0], point->x); |
731 | 0 | psSHP->adBoundsMax[1] = MS_MAX(psSHP->adBoundsMax[1], point->y); |
732 | 0 | } |
733 | 0 | } else { |
734 | 0 | psSHP->nRecords--; |
735 | 0 | } |
736 | 0 | free(pabyRec); |
737 | 0 | return (psSHP->nRecords - 1); |
738 | 0 | } |
739 | | |
740 | 0 | int msSHPWriteShape(SHPHandle psSHP, shapeObj *shape) { |
741 | 0 | int nRecordOffset, i, j, k, nRecordSize = 0; |
742 | 0 | uchar *pabyRec; |
743 | 0 | int nShapeType; |
744 | |
|
745 | 0 | ms_int32 i32, nPoints, nParts; |
746 | |
|
747 | 0 | if (psSHP->nFileSize == 0) |
748 | 0 | return -1; |
749 | | |
750 | 0 | psSHP->bUpdated = MS_TRUE; |
751 | | |
752 | | /* Fill the SHX buffer if it is not already full. */ |
753 | 0 | if (!psSHP->panRecAllLoaded) |
754 | 0 | msSHXLoadAll(psSHP); |
755 | | |
756 | | /* -------------------------------------------------------------------- */ |
757 | | /* Add the new entity to the in memory index. */ |
758 | | /* -------------------------------------------------------------------- */ |
759 | 0 | psSHP->nRecords++; |
760 | 0 | if (psSHP->nRecords > psSHP->nMaxRecords) { |
761 | 0 | psSHP->nMaxRecords = (int)(psSHP->nMaxRecords * 1.3 + 100); |
762 | |
|
763 | 0 | psSHP->panRecOffset = |
764 | 0 | (int *)SfRealloc(psSHP->panRecOffset, sizeof(int) * psSHP->nMaxRecords); |
765 | 0 | psSHP->panRecSize = |
766 | 0 | (int *)SfRealloc(psSHP->panRecSize, sizeof(int) * psSHP->nMaxRecords); |
767 | 0 | } |
768 | | |
769 | | /* -------------------------------------------------------------------- */ |
770 | | /* Compute a few things. */ |
771 | | /* -------------------------------------------------------------------- */ |
772 | 0 | nPoints = 0; |
773 | 0 | for (i = 0; i < shape->numlines; i++) |
774 | 0 | nPoints += shape->line[i].numpoints; |
775 | |
|
776 | 0 | nParts = shape->numlines; |
777 | | |
778 | | /* -------------------------------------------------------------------- */ |
779 | | /* Initialize record. */ |
780 | | /* -------------------------------------------------------------------- */ |
781 | 0 | psSHP->panRecOffset[psSHP->nRecords - 1] = nRecordOffset = psSHP->nFileSize; |
782 | |
|
783 | 0 | pabyRec = |
784 | 0 | (uchar *)msSmallMalloc(nPoints * 4 * sizeof(double) + nParts * 8 + 128); |
785 | 0 | nShapeType = psSHP->nShapeType; |
786 | |
|
787 | 0 | if (shape->type == MS_SHAPE_NULL) { |
788 | 0 | nShapeType = 0; |
789 | 0 | nRecordSize = 12; |
790 | 0 | } |
791 | | /* -------------------------------------------------------------------- */ |
792 | | /* Write vertices for a Polygon or Arc. */ |
793 | | /* -------------------------------------------------------------------- */ |
794 | 0 | else if (psSHP->nShapeType == SHP_POLYGON || psSHP->nShapeType == SHP_ARC || |
795 | 0 | psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM || |
796 | 0 | psSHP->nShapeType == SHP_ARCZ || psSHP->nShapeType == SHP_POLYGONZ) { |
797 | 0 | ms_int32 t_nParts, t_nPoints, partSize; |
798 | |
|
799 | 0 | t_nParts = nParts; |
800 | 0 | t_nPoints = nPoints; |
801 | |
|
802 | 0 | writeBounds(pabyRec + 12, shape, t_nPoints); |
803 | |
|
804 | 0 | if (bBigEndian) { |
805 | 0 | nPoints = SWAP_FOUR_BYTES(nPoints); |
806 | 0 | nParts = SWAP_FOUR_BYTES(nParts); |
807 | 0 | } |
808 | |
|
809 | 0 | ByteCopy(&nPoints, pabyRec + 40 + 8, 4); |
810 | 0 | ByteCopy(&nParts, pabyRec + 36 + 8, 4); |
811 | |
|
812 | 0 | partSize = 0; /* first part always starts at 0 */ |
813 | 0 | ByteCopy(&partSize, pabyRec + 44 + 8 + 4 * 0, 4); |
814 | 0 | if (bBigEndian) |
815 | 0 | SwapWord(4, pabyRec + 44 + 8 + 4 * 0); |
816 | |
|
817 | 0 | for (i = 1; i < t_nParts; i++) { |
818 | 0 | partSize += shape->line[i - 1].numpoints; |
819 | 0 | ByteCopy(&partSize, pabyRec + 44 + 8 + 4 * i, 4); |
820 | 0 | if (bBigEndian) |
821 | 0 | SwapWord(4, pabyRec + 44 + 8 + 4 * i); |
822 | 0 | } |
823 | |
|
824 | 0 | k = 0; /* overall point counter */ |
825 | 0 | for (i = 0; i < shape->numlines; i++) { |
826 | 0 | for (j = 0; j < shape->line[i].numpoints; j++) { |
827 | 0 | ByteCopy(&(shape->line[i].point[j].x), |
828 | 0 | pabyRec + 44 + 4 * t_nParts + 8 + k * 16, 8); |
829 | 0 | ByteCopy(&(shape->line[i].point[j].y), |
830 | 0 | pabyRec + 44 + 4 * t_nParts + 8 + k * 16 + 8, 8); |
831 | |
|
832 | 0 | if (bBigEndian) { |
833 | 0 | SwapWord(8, pabyRec + 44 + 4 * t_nParts + 8 + k * 16); |
834 | 0 | SwapWord(8, pabyRec + 44 + 4 * t_nParts + 8 + k * 16 + 8); |
835 | 0 | } |
836 | |
|
837 | 0 | k++; |
838 | 0 | } |
839 | 0 | } |
840 | |
|
841 | 0 | nRecordSize = 44 + 4 * t_nParts + 16 * t_nPoints; |
842 | | |
843 | | /* -------------------------------------------------------------------- */ |
844 | | /* measured shape : polygon and arc. */ |
845 | | /* -------------------------------------------------------------------- */ |
846 | 0 | if (psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM) { |
847 | 0 | const double dfMMin = shape->line[0].point[0].m; |
848 | 0 | const double dfMMax = |
849 | 0 | shape->line[shape->numlines - 1] |
850 | 0 | .point[shape->line[shape->numlines - 1].numpoints - 1] |
851 | 0 | .m; |
852 | |
|
853 | 0 | nRecordSize = 44 + 4 * t_nParts + 8 + (t_nPoints * 16); |
854 | |
|
855 | 0 | ByteCopy(&(dfMMin), pabyRec + nRecordSize, 8); |
856 | 0 | if (bBigEndian) |
857 | 0 | SwapWord(8, pabyRec + nRecordSize); |
858 | 0 | nRecordSize += 8; |
859 | |
|
860 | 0 | ByteCopy(&(dfMMax), pabyRec + nRecordSize, 8); |
861 | 0 | if (bBigEndian) |
862 | 0 | SwapWord(8, pabyRec + nRecordSize); |
863 | 0 | nRecordSize += 8; |
864 | |
|
865 | 0 | for (i = 0; i < shape->numlines; i++) { |
866 | 0 | for (j = 0; j < shape->line[i].numpoints; j++) { |
867 | 0 | ByteCopy(&(shape->line[i].point[j].m), pabyRec + nRecordSize, 8); |
868 | 0 | if (bBigEndian) |
869 | 0 | SwapWord(8, pabyRec + nRecordSize); |
870 | 0 | nRecordSize += 8; |
871 | 0 | } |
872 | 0 | } |
873 | 0 | } |
874 | | |
875 | | /* -------------------------------------------------------------------- */ |
876 | | /* Polygon. Arc with Z */ |
877 | | /* -------------------------------------------------------------------- */ |
878 | 0 | if (psSHP->nShapeType == SHP_POLYGONZ || psSHP->nShapeType == SHP_ARCZ || |
879 | 0 | psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM) { |
880 | 0 | const double dfMMin = shape->line[0].point[0].z; |
881 | 0 | const double dfMMax = |
882 | 0 | shape->line[shape->numlines - 1] |
883 | 0 | .point[shape->line[shape->numlines - 1].numpoints - 1] |
884 | 0 | .z; |
885 | |
|
886 | 0 | nRecordSize = 44 + 4 * t_nParts + 8 + (t_nPoints * 16); |
887 | |
|
888 | 0 | ByteCopy(&(dfMMin), pabyRec + nRecordSize, 8); |
889 | 0 | if (bBigEndian) |
890 | 0 | SwapWord(8, pabyRec + nRecordSize); |
891 | 0 | nRecordSize += 8; |
892 | |
|
893 | 0 | ByteCopy(&(dfMMax), pabyRec + nRecordSize, 8); |
894 | 0 | if (bBigEndian) |
895 | 0 | SwapWord(8, pabyRec + nRecordSize); |
896 | 0 | nRecordSize += 8; |
897 | |
|
898 | 0 | for (i = 0; i < shape->numlines; i++) { |
899 | 0 | for (j = 0; j < shape->line[i].numpoints; j++) { |
900 | 0 | ByteCopy(&(shape->line[i].point[j].z), pabyRec + nRecordSize, 8); |
901 | 0 | if (bBigEndian) |
902 | 0 | SwapWord(8, pabyRec + nRecordSize); |
903 | 0 | nRecordSize += 8; |
904 | 0 | } |
905 | 0 | } |
906 | 0 | } |
907 | 0 | } |
908 | | |
909 | | /* -------------------------------------------------------------------- */ |
910 | | /* Write vertices for a MultiPoint. */ |
911 | | /* -------------------------------------------------------------------- */ |
912 | 0 | else if (psSHP->nShapeType == SHP_MULTIPOINT || |
913 | 0 | psSHP->nShapeType == SHP_MULTIPOINTM || |
914 | 0 | psSHP->nShapeType == SHP_MULTIPOINTZ) { |
915 | 0 | ms_int32 t_nPoints; |
916 | |
|
917 | 0 | t_nPoints = nPoints; |
918 | |
|
919 | 0 | writeBounds(pabyRec + 12, shape, nPoints); |
920 | |
|
921 | 0 | if (bBigEndian) |
922 | 0 | nPoints = SWAP_FOUR_BYTES(nPoints); |
923 | 0 | ByteCopy(&nPoints, pabyRec + 44, 4); |
924 | |
|
925 | 0 | for (i = 0; i < shape->line[0].numpoints; i++) { |
926 | 0 | ByteCopy(&(shape->line[0].point[i].x), pabyRec + 48 + i * 16, 8); |
927 | 0 | ByteCopy(&(shape->line[0].point[i].y), pabyRec + 48 + i * 16 + 8, 8); |
928 | |
|
929 | 0 | if (bBigEndian) { |
930 | 0 | SwapWord(8, pabyRec + 48 + i * 16); |
931 | 0 | SwapWord(8, pabyRec + 48 + i * 16 + 8); |
932 | 0 | } |
933 | 0 | } |
934 | |
|
935 | 0 | nRecordSize = 40 + 16 * t_nPoints; |
936 | |
|
937 | 0 | if (psSHP->nShapeType == SHP_MULTIPOINTM) { |
938 | 0 | const double dfMMin = shape->line[0].point[0].m; |
939 | 0 | const double dfMMax = |
940 | 0 | shape->line[0].point[shape->line[0].numpoints - 1].m; |
941 | |
|
942 | 0 | ByteCopy(&(dfMMin), pabyRec + nRecordSize, 8); |
943 | 0 | if (bBigEndian) |
944 | 0 | SwapWord(8, pabyRec + nRecordSize); |
945 | 0 | nRecordSize += 8; |
946 | |
|
947 | 0 | ByteCopy(&(dfMMax), pabyRec + nRecordSize, 8); |
948 | 0 | if (bBigEndian) |
949 | 0 | SwapWord(8, pabyRec + nRecordSize); |
950 | 0 | nRecordSize += 8; |
951 | |
|
952 | 0 | for (i = 0; i < shape->line[0].numpoints; i++) { |
953 | 0 | ByteCopy(&(shape->line[0].point[i].m), pabyRec + nRecordSize, 8); |
954 | 0 | if (bBigEndian) |
955 | 0 | SwapWord(8, pabyRec + nRecordSize); |
956 | 0 | nRecordSize += 8; |
957 | 0 | } |
958 | 0 | } |
959 | |
|
960 | 0 | if (psSHP->nShapeType == SHP_MULTIPOINTZ) { |
961 | 0 | const double dfMMin = shape->line[0].point[0].z; |
962 | 0 | const double dfMMax = |
963 | 0 | shape->line[0].point[shape->line[0].numpoints - 1].z; |
964 | |
|
965 | 0 | ByteCopy(&(dfMMin), pabyRec + nRecordSize, 8); |
966 | 0 | if (bBigEndian) |
967 | 0 | SwapWord(8, pabyRec + nRecordSize); |
968 | 0 | nRecordSize += 8; |
969 | |
|
970 | 0 | ByteCopy(&(dfMMax), pabyRec + nRecordSize, 8); |
971 | 0 | if (bBigEndian) |
972 | 0 | SwapWord(8, pabyRec + nRecordSize); |
973 | 0 | nRecordSize += 8; |
974 | |
|
975 | 0 | for (i = 0; i < shape->line[0].numpoints; i++) { |
976 | 0 | ByteCopy(&(shape->line[0].point[i].z), pabyRec + nRecordSize, 8); |
977 | 0 | if (bBigEndian) |
978 | 0 | SwapWord(8, pabyRec + nRecordSize); |
979 | 0 | nRecordSize += 8; |
980 | 0 | } |
981 | 0 | } |
982 | 0 | } |
983 | | |
984 | | /* -------------------------------------------------------------------- */ |
985 | | /* Write vertices for a point. */ |
986 | | /* -------------------------------------------------------------------- */ |
987 | 0 | else if (psSHP->nShapeType == SHP_POINT || psSHP->nShapeType == SHP_POINTM || |
988 | 0 | psSHP->nShapeType == SHP_POINTZ) { |
989 | 0 | ByteCopy(&(shape->line[0].point[0].x), pabyRec + 12, 8); |
990 | 0 | ByteCopy(&(shape->line[0].point[0].y), pabyRec + 20, 8); |
991 | |
|
992 | 0 | if (bBigEndian) { |
993 | 0 | SwapWord(8, pabyRec + 12); |
994 | 0 | SwapWord(8, pabyRec + 20); |
995 | 0 | } |
996 | |
|
997 | 0 | nRecordSize = 20; |
998 | |
|
999 | 0 | if (psSHP->nShapeType == SHP_POINTM) { |
1000 | 0 | ByteCopy(&(shape->line[0].point[0].m), pabyRec + nRecordSize, 8); |
1001 | 0 | if (bBigEndian) |
1002 | 0 | SwapWord(8, pabyRec + nRecordSize); |
1003 | 0 | nRecordSize += 8; |
1004 | 0 | } |
1005 | |
|
1006 | 0 | if (psSHP->nShapeType == SHP_POINTZ) { |
1007 | 0 | ByteCopy(&(shape->line[0].point[0].z), pabyRec + nRecordSize, 8); |
1008 | 0 | if (bBigEndian) |
1009 | 0 | SwapWord(8, pabyRec + nRecordSize); |
1010 | 0 | nRecordSize += 8; |
1011 | 0 | } |
1012 | 0 | } |
1013 | | |
1014 | | /* -------------------------------------------------------------------- */ |
1015 | | /* Set the shape type, record number, and record size. */ |
1016 | | /* -------------------------------------------------------------------- */ |
1017 | 0 | i32 = psSHP->nRecords - 1 + 1; /* record # */ |
1018 | 0 | if (!bBigEndian) |
1019 | 0 | i32 = SWAP_FOUR_BYTES(i32); |
1020 | 0 | ByteCopy(&i32, pabyRec, 4); |
1021 | |
|
1022 | 0 | i32 = nRecordSize / 2; /* record size */ |
1023 | 0 | if (!bBigEndian) |
1024 | 0 | i32 = SWAP_FOUR_BYTES(i32); |
1025 | 0 | ByteCopy(&i32, pabyRec + 4, 4); |
1026 | |
|
1027 | 0 | i32 = nShapeType; /* shape type */ |
1028 | 0 | if (bBigEndian) |
1029 | 0 | i32 = SWAP_FOUR_BYTES(i32); |
1030 | 0 | ByteCopy(&i32, pabyRec + 8, 4); |
1031 | | |
1032 | | /* -------------------------------------------------------------------- */ |
1033 | | /* Write out record. */ |
1034 | | /* -------------------------------------------------------------------- */ |
1035 | 0 | if (VSIFSeekL(psSHP->fpSHP, nRecordOffset, 0) == 0) { |
1036 | 0 | VSIFWriteL(pabyRec, nRecordSize + 8, 1, psSHP->fpSHP); |
1037 | |
|
1038 | 0 | psSHP->panRecSize[psSHP->nRecords - 1] = nRecordSize; |
1039 | 0 | psSHP->nFileSize += nRecordSize + 8; |
1040 | | |
1041 | | /* -------------------------------------------------------------------- */ |
1042 | | /* Expand file wide bounds based on this shape. */ |
1043 | | /* -------------------------------------------------------------------- */ |
1044 | 0 | if (psSHP->nRecords == 1) { |
1045 | 0 | psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = shape->line[0].point[0].x; |
1046 | 0 | psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = shape->line[0].point[0].y; |
1047 | 0 | psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = shape->line[0].point[0].z; |
1048 | 0 | psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = shape->line[0].point[0].m; |
1049 | 0 | } |
1050 | |
|
1051 | 0 | for (i = 0; i < shape->numlines; i++) { |
1052 | 0 | for (j = 0; j < shape->line[i].numpoints; j++) { |
1053 | 0 | psSHP->adBoundsMin[0] = |
1054 | 0 | MS_MIN(psSHP->adBoundsMin[0], shape->line[i].point[j].x); |
1055 | 0 | psSHP->adBoundsMin[1] = |
1056 | 0 | MS_MIN(psSHP->adBoundsMin[1], shape->line[i].point[j].y); |
1057 | 0 | psSHP->adBoundsMin[2] = |
1058 | 0 | MS_MIN(psSHP->adBoundsMin[2], shape->line[i].point[j].z); |
1059 | 0 | psSHP->adBoundsMin[3] = |
1060 | 0 | MS_MIN(psSHP->adBoundsMin[3], shape->line[i].point[j].m); |
1061 | 0 | psSHP->adBoundsMax[0] = |
1062 | 0 | MS_MAX(psSHP->adBoundsMax[0], shape->line[i].point[j].x); |
1063 | 0 | psSHP->adBoundsMax[1] = |
1064 | 0 | MS_MAX(psSHP->adBoundsMax[1], shape->line[i].point[j].y); |
1065 | 0 | psSHP->adBoundsMax[2] = |
1066 | 0 | MS_MAX(psSHP->adBoundsMax[2], shape->line[i].point[j].z); |
1067 | 0 | psSHP->adBoundsMax[3] = |
1068 | 0 | MS_MAX(psSHP->adBoundsMax[3], shape->line[i].point[j].m); |
1069 | 0 | } |
1070 | 0 | } |
1071 | |
|
1072 | 0 | } else { |
1073 | 0 | psSHP->nRecords--; |
1074 | | /* there was an error writing the record */ |
1075 | 0 | } |
1076 | 0 | free(pabyRec); |
1077 | 0 | return (psSHP->nRecords - 1); |
1078 | 0 | } |
1079 | | |
1080 | | /* |
1081 | | ** msSHPReadAllocateBuffer() - Ensure our record buffer is large enough. |
1082 | | */ |
1083 | | static uchar *msSHPReadAllocateBuffer(SHPHandle psSHP, int hEntity, |
1084 | 0 | const char *pszCallingFunction) { |
1085 | |
|
1086 | 0 | int nEntitySize = msSHXReadSize(psSHP, hEntity); |
1087 | 0 | if (nEntitySize <= 0 || nEntitySize > INT_MAX - 8) { |
1088 | 0 | msSetError(MS_MEMERR, |
1089 | 0 | "Out of memory. Cannot allocate %d bytes. Probably broken " |
1090 | 0 | "shapefile at feature %d", |
1091 | 0 | pszCallingFunction, nEntitySize, hEntity); |
1092 | 0 | return NULL; |
1093 | 0 | } |
1094 | 0 | nEntitySize += 8; |
1095 | | /* -------------------------------------------------------------------- */ |
1096 | | /* Ensure our record buffer is large enough. */ |
1097 | | /* -------------------------------------------------------------------- */ |
1098 | |
|
1099 | 0 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
1100 | | /* when running with libFuzzer, allocate a new buffer for every |
1101 | | call, to allow AddressSanitizer to detect memory errors */ |
1102 | 0 | free(psSHP->pabyRec); |
1103 | 0 | psSHP->pabyRec = NULL; |
1104 | 0 | psSHP->nBufSize = 0; |
1105 | 0 | #endif |
1106 | |
|
1107 | 0 | if (nEntitySize > psSHP->nBufSize) { |
1108 | 0 | uchar *pabyRec = (uchar *)SfRealloc(psSHP->pabyRec, nEntitySize); |
1109 | 0 | if (pabyRec == NULL) { |
1110 | 0 | msSetError(MS_MEMERR, |
1111 | 0 | "Out of memory. Cannot allocate %d bytes. Probably broken " |
1112 | 0 | "shapefile at feature %d", |
1113 | 0 | pszCallingFunction, nEntitySize, hEntity); |
1114 | 0 | return NULL; |
1115 | 0 | } |
1116 | 0 | psSHP->pabyRec = pabyRec; |
1117 | 0 | psSHP->nBufSize = nEntitySize; |
1118 | 0 | } |
1119 | 0 | return psSHP->pabyRec; |
1120 | 0 | } |
1121 | | |
1122 | | /* |
1123 | | ** msSHPReadPoint() - Reads a single point from a POINT shape file. |
1124 | | */ |
1125 | 0 | int msSHPReadPoint(SHPHandle psSHP, int hEntity, pointObj *point) { |
1126 | 0 | int nEntitySize; |
1127 | | |
1128 | | /* -------------------------------------------------------------------- */ |
1129 | | /* Only valid for point shapefiles */ |
1130 | | /* -------------------------------------------------------------------- */ |
1131 | 0 | if (psSHP->nShapeType != SHP_POINT) { |
1132 | 0 | msSetError(MS_SHPERR, "msSHPReadPoint only operates on point shapefiles.", |
1133 | 0 | "msSHPReadPoint()"); |
1134 | 0 | return (MS_FAILURE); |
1135 | 0 | } |
1136 | | |
1137 | | /* -------------------------------------------------------------------- */ |
1138 | | /* Validate the record/entity number. */ |
1139 | | /* -------------------------------------------------------------------- */ |
1140 | 0 | if (hEntity < 0 || hEntity >= psSHP->nRecords) { |
1141 | 0 | msSetError(MS_SHPERR, "Record index out of bounds.", "msSHPReadPoint()"); |
1142 | 0 | return (MS_FAILURE); |
1143 | 0 | } |
1144 | | |
1145 | 0 | nEntitySize = msSHXReadSize(psSHP, hEntity) + 8; |
1146 | |
|
1147 | 0 | if (nEntitySize == 12) { |
1148 | 0 | msSetError(MS_SHPERR, "NULL feature encountered.", "msSHPReadPoint()"); |
1149 | 0 | return (MS_FAILURE); |
1150 | 0 | } else if (nEntitySize < 28) { |
1151 | 0 | msSetError(MS_SHPERR, |
1152 | 0 | "Corrupted feature encountered. hEntity=%d, nEntitySize=%d", |
1153 | 0 | "msSHPReadPoint()", hEntity, nEntitySize); |
1154 | 0 | return (MS_FAILURE); |
1155 | 0 | } |
1156 | | |
1157 | 0 | uchar *pabyRec = msSHPReadAllocateBuffer(psSHP, hEntity, "msSHPReadPoint()"); |
1158 | 0 | if (pabyRec == NULL) { |
1159 | 0 | return MS_FAILURE; |
1160 | 0 | } |
1161 | | |
1162 | | /* -------------------------------------------------------------------- */ |
1163 | | /* Read the record. */ |
1164 | | /* -------------------------------------------------------------------- */ |
1165 | 0 | const int offset = msSHXReadOffset(psSHP, hEntity); |
1166 | 0 | if (offset <= 0 || 0 != VSIFSeekL(psSHP->fpSHP, offset, 0)) { |
1167 | 0 | msSetError(MS_IOERR, "failed to seek offset", "msSHPReadPoint()"); |
1168 | 0 | return (MS_FAILURE); |
1169 | 0 | } |
1170 | 0 | if (1 != VSIFReadL(pabyRec, nEntitySize, 1, psSHP->fpSHP)) { |
1171 | 0 | msSetError(MS_IOERR, "failed to fread record", "msSHPReadPoint()"); |
1172 | 0 | return (MS_FAILURE); |
1173 | 0 | } |
1174 | | |
1175 | 0 | memcpy(&(point->x), pabyRec + 12, 8); |
1176 | 0 | memcpy(&(point->y), pabyRec + 20, 8); |
1177 | |
|
1178 | 0 | if (bBigEndian) { |
1179 | 0 | SwapWord(8, &(point->x)); |
1180 | 0 | SwapWord(8, &(point->y)); |
1181 | 0 | } |
1182 | |
|
1183 | 0 | return (MS_SUCCESS); |
1184 | 0 | } |
1185 | | |
1186 | | /* |
1187 | | ** msSHXLoadPage() |
1188 | | ** |
1189 | | ** The SHX tells us what the byte offsets of the shapes in the SHP file are. |
1190 | | ** We read the SHX file in ~8K pages and store those pages in memory for |
1191 | | ** successive accesses during the reading cycle (first bounds are read, |
1192 | | ** then entire shapes). Each time we read a page, we mark it as read. |
1193 | | */ |
1194 | 0 | static bool msSHXLoadPage(SHPHandle psSHP, int shxBufferPage) { |
1195 | 0 | int i; |
1196 | | |
1197 | | /* Each SHX record is 8 bytes long (two ints), hence our buffer size. */ |
1198 | 0 | char buffer[SHX_BUFFER_PAGE * 8]; |
1199 | | |
1200 | | /* Validate the page number. */ |
1201 | 0 | if (shxBufferPage < 0) |
1202 | 0 | return (MS_FAILURE); |
1203 | | |
1204 | 0 | const int nShapesToCache = |
1205 | 0 | shxBufferPage < psSHP->nRecords / SHX_BUFFER_PAGE |
1206 | 0 | ? SHX_BUFFER_PAGE |
1207 | 0 | : psSHP->nRecords - shxBufferPage * SHX_BUFFER_PAGE; |
1208 | |
|
1209 | 0 | if (0 != |
1210 | 0 | VSIFSeekL(psSHP->fpSHX, 100 + shxBufferPage * SHX_BUFFER_PAGE * 8, 0)) { |
1211 | 0 | memset(psSHP->panRecOffset + shxBufferPage * SHX_BUFFER_PAGE, 0, |
1212 | 0 | nShapesToCache * sizeof(psSHP->panRecOffset[0])); |
1213 | 0 | memset(psSHP->panRecSize + shxBufferPage * SHX_BUFFER_PAGE, 0, |
1214 | 0 | nShapesToCache * sizeof(psSHP->panRecSize[0])); |
1215 | 0 | msSetBit(psSHP->panRecLoaded, shxBufferPage, 1); |
1216 | 0 | msSetError(MS_IOERR, "failed to seek offset", "msSHXLoadPage()"); |
1217 | 0 | return false; |
1218 | 0 | } |
1219 | | |
1220 | 0 | if ((size_t)nShapesToCache != |
1221 | 0 | VSIFReadL(buffer, 8, nShapesToCache, psSHP->fpSHX)) { |
1222 | 0 | memset(psSHP->panRecOffset + shxBufferPage * SHX_BUFFER_PAGE, 0, |
1223 | 0 | nShapesToCache * sizeof(psSHP->panRecOffset[0])); |
1224 | 0 | memset(psSHP->panRecSize + shxBufferPage * SHX_BUFFER_PAGE, 0, |
1225 | 0 | nShapesToCache * sizeof(psSHP->panRecSize[0])); |
1226 | 0 | msSetBit(psSHP->panRecLoaded, shxBufferPage, 1); |
1227 | 0 | msSetError(MS_IOERR, "failed to fread SHX record", "msSHXLoadPage()"); |
1228 | 0 | return false; |
1229 | 0 | } |
1230 | | |
1231 | | /* Copy the buffer contents out into the working arrays. */ |
1232 | 0 | for (i = 0; i < nShapesToCache; i++) { |
1233 | 0 | int tmpOffset, tmpSize; |
1234 | |
|
1235 | 0 | memcpy(&tmpOffset, (buffer + (8 * i)), 4); |
1236 | 0 | memcpy(&tmpSize, (buffer + (8 * i) + 4), 4); |
1237 | | |
1238 | | /* SHX uses big endian numbers for the offsets, so we have to flip them */ |
1239 | | /* if we are a little endian machine. */ |
1240 | 0 | if (!bBigEndian) { |
1241 | 0 | tmpOffset = SWAP_FOUR_BYTES(tmpOffset); |
1242 | 0 | tmpSize = SWAP_FOUR_BYTES(tmpSize); |
1243 | 0 | } |
1244 | | |
1245 | | /* SHX stores the offsets in 2 byte units, so we double them to get */ |
1246 | | /* an offset in bytes. */ |
1247 | 0 | if (tmpOffset > 0 && tmpOffset < INT_MAX / 2) |
1248 | 0 | tmpOffset = tmpOffset * 2; |
1249 | 0 | else |
1250 | 0 | tmpOffset = 0; |
1251 | |
|
1252 | 0 | if (tmpSize > 0 && tmpSize < INT_MAX / 2) |
1253 | 0 | tmpSize = tmpSize * 2; |
1254 | 0 | else |
1255 | 0 | tmpSize = 0; |
1256 | | |
1257 | | /* Write the answer into the working arrays on the SHPHandle */ |
1258 | 0 | psSHP->panRecOffset[shxBufferPage * SHX_BUFFER_PAGE + i] = tmpOffset; |
1259 | 0 | psSHP->panRecSize[shxBufferPage * SHX_BUFFER_PAGE + i] = tmpSize; |
1260 | 0 | } |
1261 | |
|
1262 | 0 | msSetBit(psSHP->panRecLoaded, shxBufferPage, 1); |
1263 | |
|
1264 | 0 | return (MS_SUCCESS); |
1265 | 0 | } |
1266 | | |
1267 | 0 | static int msSHXLoadAll(SHPHandle psSHP) { |
1268 | |
|
1269 | 0 | int i; |
1270 | 0 | uchar *pabyBuf; |
1271 | |
|
1272 | 0 | pabyBuf = (uchar *)malloc(8 * psSHP->nRecords); |
1273 | 0 | if (pabyBuf == NULL) { |
1274 | 0 | msSetError(MS_IOERR, "failed to allocate memory for SHX buffer", |
1275 | 0 | "msSHXLoadAll()"); |
1276 | 0 | return MS_FAILURE; |
1277 | 0 | } |
1278 | 0 | if ((size_t)psSHP->nRecords != |
1279 | 0 | VSIFReadL(pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX)) { |
1280 | 0 | msSetError(MS_IOERR, "failed to read shx records", "msSHXLoadAll()"); |
1281 | 0 | free(pabyBuf); |
1282 | 0 | return MS_FAILURE; |
1283 | 0 | } |
1284 | 0 | for (i = 0; i < psSHP->nRecords; i++) { |
1285 | 0 | ms_int32 nOffset, nLength; |
1286 | |
|
1287 | 0 | memcpy(&nOffset, pabyBuf + i * 8, 4); |
1288 | 0 | memcpy(&nLength, pabyBuf + i * 8 + 4, 4); |
1289 | |
|
1290 | 0 | if (!bBigEndian) { |
1291 | 0 | nOffset = SWAP_FOUR_BYTES(nOffset); |
1292 | 0 | nLength = SWAP_FOUR_BYTES(nLength); |
1293 | 0 | } |
1294 | | |
1295 | | /* SHX stores the offsets in 2 byte units, so we double them to get */ |
1296 | | /* an offset in bytes. */ |
1297 | 0 | if (nOffset > 0 && nOffset < INT_MAX / 2) |
1298 | 0 | nOffset = nOffset * 2; |
1299 | 0 | else |
1300 | 0 | nOffset = 0; |
1301 | |
|
1302 | 0 | if (nLength > 0 && nLength < INT_MAX / 2) |
1303 | 0 | nLength = nLength * 2; |
1304 | 0 | else |
1305 | 0 | nLength = 0; |
1306 | |
|
1307 | 0 | psSHP->panRecOffset[i] = nOffset; |
1308 | 0 | psSHP->panRecSize[i] = nLength; |
1309 | 0 | } |
1310 | 0 | free(pabyBuf); |
1311 | 0 | psSHP->panRecAllLoaded = 1; |
1312 | |
|
1313 | 0 | return (MS_SUCCESS); |
1314 | 0 | } |
1315 | | |
1316 | 0 | static int msSHXReadOffset(SHPHandle psSHP, int hEntity) { |
1317 | |
|
1318 | 0 | int shxBufferPage = hEntity / SHX_BUFFER_PAGE; |
1319 | | |
1320 | | /* Validate the record/entity number. */ |
1321 | 0 | if (hEntity < 0 || hEntity >= psSHP->nRecords) |
1322 | 0 | return 0; |
1323 | | |
1324 | 0 | if (!(psSHP->panRecAllLoaded || |
1325 | 0 | msGetBit(psSHP->panRecLoaded, shxBufferPage))) { |
1326 | 0 | msSHXLoadPage(psSHP, shxBufferPage); |
1327 | 0 | } |
1328 | |
|
1329 | 0 | return psSHP->panRecOffset[hEntity]; |
1330 | 0 | } |
1331 | | |
1332 | 0 | static int msSHXReadSize(SHPHandle psSHP, int hEntity) { |
1333 | |
|
1334 | 0 | int shxBufferPage = hEntity / SHX_BUFFER_PAGE; |
1335 | | |
1336 | | /* Validate the record/entity number. */ |
1337 | 0 | if (hEntity < 0 || hEntity >= psSHP->nRecords) |
1338 | 0 | return 0; |
1339 | | |
1340 | 0 | if (!(psSHP->panRecAllLoaded || |
1341 | 0 | msGetBit(psSHP->panRecLoaded, shxBufferPage))) { |
1342 | 0 | msSHXLoadPage(psSHP, shxBufferPage); |
1343 | 0 | } |
1344 | |
|
1345 | 0 | return psSHP->panRecSize[hEntity]; |
1346 | 0 | } |
1347 | | |
1348 | 0 | static void ReadRect(rectObj *r, const uchar *src) { |
1349 | 0 | memcpy(&r->minx, src, 8); |
1350 | 0 | memcpy(&r->miny, src + 8, 8); |
1351 | 0 | memcpy(&r->maxx, src + 16, 8); |
1352 | 0 | memcpy(&r->maxy, src + 24, 8); |
1353 | |
|
1354 | 0 | if (bBigEndian) { |
1355 | 0 | SwapWord(8, &r->minx); |
1356 | 0 | SwapWord(8, &r->miny); |
1357 | 0 | SwapWord(8, &r->maxx); |
1358 | 0 | SwapWord(8, &r->maxy); |
1359 | 0 | } |
1360 | 0 | } |
1361 | | |
1362 | | /* |
1363 | | ** msSHPReadShape() - Reads the vertices for one shape from a shape file. |
1364 | | */ |
1365 | 0 | void msSHPReadShape(SHPHandle psSHP, int hEntity, shapeObj *shape) { |
1366 | 0 | int i, j, k; |
1367 | 0 | int nEntitySize, nRequiredSize; |
1368 | |
|
1369 | 0 | msInitShape(shape); /* initialize the shape */ |
1370 | | |
1371 | | /* -------------------------------------------------------------------- */ |
1372 | | /* Validate the record/entity number. */ |
1373 | | /* -------------------------------------------------------------------- */ |
1374 | 0 | if (hEntity < 0 || hEntity >= psSHP->nRecords) |
1375 | 0 | return; |
1376 | | |
1377 | 0 | nEntitySize = msSHXReadSize(psSHP, hEntity); |
1378 | 0 | if (nEntitySize < 4 || nEntitySize > INT_MAX - 8) { |
1379 | 0 | shape->type = MS_SHAPE_NULL; |
1380 | 0 | msSetError(MS_SHPERR, |
1381 | 0 | "Corrupted feature encountered. hEntity = %d, nEntitySize=%d", |
1382 | 0 | "msSHPReadShape()", hEntity, nEntitySize); |
1383 | 0 | return; |
1384 | 0 | } |
1385 | | |
1386 | 0 | nEntitySize += 8; |
1387 | 0 | if (nEntitySize == 12) { |
1388 | 0 | shape->type = MS_SHAPE_NULL; |
1389 | 0 | return; |
1390 | 0 | } |
1391 | | |
1392 | 0 | uchar *pabyRec = msSHPReadAllocateBuffer(psSHP, hEntity, "msSHPReadShape()"); |
1393 | 0 | if (pabyRec == NULL) { |
1394 | 0 | shape->type = MS_SHAPE_NULL; |
1395 | 0 | return; |
1396 | 0 | } |
1397 | | |
1398 | | /* -------------------------------------------------------------------- */ |
1399 | | /* Read the record. */ |
1400 | | /* -------------------------------------------------------------------- */ |
1401 | 0 | const int offset = msSHXReadOffset(psSHP, hEntity); |
1402 | 0 | if (offset <= 0 || 0 != VSIFSeekL(psSHP->fpSHP, offset, 0)) { |
1403 | 0 | msSetError(MS_IOERR, "failed to seek offset", "msSHPReadShape()"); |
1404 | 0 | shape->type = MS_SHAPE_NULL; |
1405 | 0 | return; |
1406 | 0 | } |
1407 | 0 | if (1 != VSIFReadL(pabyRec, nEntitySize, 1, psSHP->fpSHP)) { |
1408 | 0 | msSetError(MS_IOERR, "failed to fread record", "msSHPReadPoint()"); |
1409 | 0 | shape->type = MS_SHAPE_NULL; |
1410 | 0 | return; |
1411 | 0 | } |
1412 | | |
1413 | | /* -------------------------------------------------------------------- */ |
1414 | | /* Extract vertices for a Polygon or Arc. */ |
1415 | | /* -------------------------------------------------------------------- */ |
1416 | 0 | if (psSHP->nShapeType == SHP_POLYGON || psSHP->nShapeType == SHP_ARC || |
1417 | 0 | psSHP->nShapeType == SHP_POLYGONM || psSHP->nShapeType == SHP_ARCM || |
1418 | 0 | psSHP->nShapeType == SHP_POLYGONZ || psSHP->nShapeType == SHP_ARCZ) { |
1419 | 0 | ms_int32 nPoints, nParts; |
1420 | |
|
1421 | 0 | if (nEntitySize < 40 + 8 + 4) { |
1422 | 0 | shape->type = MS_SHAPE_NULL; |
1423 | 0 | msSetError(MS_SHPERR, |
1424 | 0 | "Corrupted feature encountered. hEntity = %d, nEntitySize=%d", |
1425 | 0 | "msSHPReadShape()", hEntity, nEntitySize); |
1426 | 0 | return; |
1427 | 0 | } |
1428 | | |
1429 | | /* copy the bounding box */ |
1430 | 0 | ReadRect(&shape->bounds, pabyRec + 8 + 4); |
1431 | |
|
1432 | 0 | memcpy(&nPoints, pabyRec + 40 + 8, 4); |
1433 | 0 | memcpy(&nParts, pabyRec + 36 + 8, 4); |
1434 | |
|
1435 | 0 | if (bBigEndian) { |
1436 | 0 | nPoints = SWAP_FOUR_BYTES(nPoints); |
1437 | 0 | nParts = SWAP_FOUR_BYTES(nParts); |
1438 | 0 | } |
1439 | |
|
1440 | 0 | if (nPoints < 0 || nParts < 0 || nPoints > 50 * 1000 * 1000 || |
1441 | 0 | nParts > 10 * 1000 * 1000) { |
1442 | 0 | shape->type = MS_SHAPE_NULL; |
1443 | 0 | msSetError(MS_SHPERR, |
1444 | 0 | "Corrupted feature encountered. hEntity = %d, nPoints =%d, " |
1445 | 0 | "nParts = %d", |
1446 | 0 | "msSHPReadShape()", hEntity, nPoints, nParts); |
1447 | 0 | return; |
1448 | 0 | } |
1449 | | |
1450 | | /* -------------------------------------------------------------------- */ |
1451 | | /* Copy out the part array from the record. */ |
1452 | | /* -------------------------------------------------------------------- */ |
1453 | | |
1454 | 0 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
1455 | | /* when running with libFuzzer, allocate a new buffer for every |
1456 | | call, to allow AddressSanitizer to detect memory errors */ |
1457 | 0 | free(psSHP->panParts); |
1458 | 0 | psSHP->panParts = NULL; |
1459 | 0 | psSHP->nPartMax = 0; |
1460 | 0 | #endif |
1461 | |
|
1462 | 0 | if (psSHP->nPartMax < nParts) { |
1463 | 0 | psSHP->panParts = (int *)SfRealloc(psSHP->panParts, nParts * sizeof(int)); |
1464 | 0 | if (psSHP->panParts == NULL) { |
1465 | | /* Reallocate previous successful size for following features */ |
1466 | 0 | psSHP->panParts = (int *)msSmallMalloc(psSHP->nPartMax * sizeof(int)); |
1467 | |
|
1468 | 0 | shape->type = MS_SHAPE_NULL; |
1469 | 0 | msSetError(MS_MEMERR, |
1470 | 0 | "Out of memory. Cannot allocate %d bytes. Probably broken " |
1471 | 0 | "shapefile at feature %d", |
1472 | 0 | "msSHPReadShape()", (int)(nParts * sizeof(int)), hEntity); |
1473 | 0 | return; |
1474 | 0 | } |
1475 | 0 | psSHP->nPartMax = nParts; |
1476 | 0 | } |
1477 | 0 | if (psSHP->panParts == NULL) { |
1478 | 0 | shape->type = MS_SHAPE_NULL; |
1479 | 0 | msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()"); |
1480 | 0 | return; |
1481 | 0 | } |
1482 | | |
1483 | | /* With the previous checks on nPoints and nParts, */ |
1484 | | /* we should not overflow here and after */ |
1485 | | /* since 50 M * (16 + 8 + 8) = 1 600 MB */ |
1486 | 0 | if (44 + 8 + 4 * nParts + 16 * nPoints > nEntitySize) { |
1487 | 0 | shape->type = MS_SHAPE_NULL; |
1488 | 0 | msSetError(MS_SHPERR, |
1489 | 0 | "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.", |
1490 | 0 | "msSHPReadShape()", hEntity, nPoints, nParts); |
1491 | 0 | return; |
1492 | 0 | } |
1493 | | |
1494 | 0 | memcpy(psSHP->panParts, pabyRec + 44 + 8, 4 * nParts); |
1495 | 0 | if (bBigEndian) { |
1496 | 0 | for (i = 0; i < nParts; i++) { |
1497 | 0 | *(psSHP->panParts + i) = SWAP_FOUR_BYTES(*(psSHP->panParts + i)); |
1498 | 0 | } |
1499 | 0 | } |
1500 | | |
1501 | | /* -------------------------------------------------------------------- */ |
1502 | | /* Fill the shape structure. */ |
1503 | | /* -------------------------------------------------------------------- */ |
1504 | 0 | shape->line = (lineObj *)malloc(sizeof(lineObj) * nParts); |
1505 | 0 | MS_CHECK_ALLOC_NO_RET(shape->line, sizeof(lineObj) * nParts); |
1506 | |
|
1507 | 0 | shape->numlines = nParts; |
1508 | |
|
1509 | 0 | k = 0; /* overall point counter */ |
1510 | 0 | for (i = 0; i < nParts; i++) { |
1511 | 0 | const ms_int32 end = i == nParts - 1 ? nPoints : psSHP->panParts[i + 1]; |
1512 | 0 | if (psSHP->panParts[i] < 0 || end < 0 || end > nPoints || |
1513 | 0 | psSHP->panParts[i] >= end) { |
1514 | 0 | msSetError(MS_SHPERR, |
1515 | 0 | "Corrupted .shp file : shape %d, shape->line[%d].start=%d, " |
1516 | 0 | "shape->line[%d].end=%d", |
1517 | 0 | "msSHPReadShape()", hEntity, i, psSHP->panParts[i], i, end); |
1518 | 0 | while (--i >= 0) |
1519 | 0 | free(shape->line[i].point); |
1520 | 0 | free(shape->line); |
1521 | 0 | shape->line = NULL; |
1522 | 0 | shape->numlines = 0; |
1523 | 0 | shape->type = MS_SHAPE_NULL; |
1524 | 0 | return; |
1525 | 0 | } |
1526 | | |
1527 | 0 | shape->line[i].numpoints = end - psSHP->panParts[i]; |
1528 | 0 | if ((shape->line[i].point = (pointObj *)malloc( |
1529 | 0 | sizeof(pointObj) * shape->line[i].numpoints)) == NULL) { |
1530 | 0 | while (--i >= 0) |
1531 | 0 | free(shape->line[i].point); |
1532 | 0 | free(shape->line); |
1533 | 0 | shape->line = NULL; |
1534 | 0 | shape->numlines = 0; |
1535 | 0 | shape->type = MS_SHAPE_NULL; |
1536 | 0 | msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()"); |
1537 | 0 | return; |
1538 | 0 | } |
1539 | | |
1540 | | /* nOffset = 44 + 8 + 4*nParts; */ |
1541 | 0 | for (j = 0; j < shape->line[i].numpoints; j++) { |
1542 | 0 | memcpy(&(shape->line[i].point[j].x), |
1543 | 0 | pabyRec + 44 + 4 * nParts + 8 + k * 16, 8); |
1544 | 0 | memcpy(&(shape->line[i].point[j].y), |
1545 | 0 | pabyRec + 44 + 4 * nParts + 8 + k * 16 + 8, 8); |
1546 | |
|
1547 | 0 | if (bBigEndian) { |
1548 | 0 | SwapWord(8, &(shape->line[i].point[j].x)); |
1549 | 0 | SwapWord(8, &(shape->line[i].point[j].y)); |
1550 | 0 | } |
1551 | | |
1552 | | /* -------------------------------------------------------------------- |
1553 | | */ |
1554 | | /* Polygon, Arc with Z values. */ |
1555 | | /* -------------------------------------------------------------------- |
1556 | | */ |
1557 | 0 | shape->line[i].point[j].z = 0.0; /* initialize */ |
1558 | 0 | if (psSHP->nShapeType == SHP_POLYGONZ || |
1559 | 0 | psSHP->nShapeType == SHP_ARCZ) { |
1560 | 0 | const int nOffset = 44 + 8 + (4 * nParts) + (16 * nPoints); |
1561 | 0 | if (nEntitySize >= nOffset + 16 + 8 * nPoints) { |
1562 | 0 | memcpy(&(shape->line[i].point[j].z), pabyRec + nOffset + 16 + k * 8, |
1563 | 0 | 8); |
1564 | 0 | if (bBigEndian) |
1565 | 0 | SwapWord(8, &(shape->line[i].point[j].z)); |
1566 | 0 | } |
1567 | 0 | } |
1568 | | |
1569 | | /* -------------------------------------------------------------------- |
1570 | | */ |
1571 | | /* Measured arc and polygon support. */ |
1572 | | /* -------------------------------------------------------------------- |
1573 | | */ |
1574 | 0 | shape->line[i].point[j].m = 0; /* initialize */ |
1575 | 0 | if (psSHP->nShapeType == SHP_POLYGONM || |
1576 | 0 | psSHP->nShapeType == SHP_ARCM) { |
1577 | 0 | const int nOffset = 44 + 8 + (4 * nParts) + (16 * nPoints); |
1578 | 0 | if (nEntitySize >= nOffset + 16 + 8 * nPoints) { |
1579 | 0 | memcpy(&(shape->line[i].point[j].m), pabyRec + nOffset + 16 + k * 8, |
1580 | 0 | 8); |
1581 | 0 | if (bBigEndian) |
1582 | 0 | SwapWord(8, &(shape->line[i].point[j].m)); |
1583 | 0 | } |
1584 | 0 | } |
1585 | 0 | k++; |
1586 | 0 | } |
1587 | 0 | } |
1588 | | |
1589 | 0 | if (psSHP->nShapeType == SHP_POLYGON || psSHP->nShapeType == SHP_POLYGONZ || |
1590 | 0 | psSHP->nShapeType == SHP_POLYGONM) |
1591 | 0 | shape->type = MS_SHAPE_POLYGON; |
1592 | 0 | else |
1593 | 0 | shape->type = MS_SHAPE_LINE; |
1594 | |
|
1595 | 0 | } |
1596 | | |
1597 | | /* -------------------------------------------------------------------- */ |
1598 | | /* Extract a MultiPoint. */ |
1599 | | /* -------------------------------------------------------------------- */ |
1600 | 0 | else if (psSHP->nShapeType == SHP_MULTIPOINT || |
1601 | 0 | psSHP->nShapeType == SHP_MULTIPOINTM || |
1602 | 0 | psSHP->nShapeType == SHP_MULTIPOINTZ) { |
1603 | 0 | ms_int32 nPoints; |
1604 | |
|
1605 | 0 | if (nEntitySize < 44 + 4) { |
1606 | 0 | shape->type = MS_SHAPE_NULL; |
1607 | 0 | msSetError(MS_SHPERR, |
1608 | 0 | "Corrupted feature encountered. recSize of feature %d=%d", |
1609 | 0 | "msSHPReadShape()", hEntity, msSHXReadSize(psSHP, hEntity)); |
1610 | 0 | return; |
1611 | 0 | } |
1612 | | |
1613 | | /* copy the bounding box */ |
1614 | 0 | ReadRect(&shape->bounds, pabyRec + 8 + 4); |
1615 | |
|
1616 | 0 | memcpy(&nPoints, pabyRec + 44, 4); |
1617 | 0 | if (bBigEndian) |
1618 | 0 | nPoints = SWAP_FOUR_BYTES(nPoints); |
1619 | | |
1620 | | /* -------------------------------------------------------------------- */ |
1621 | | /* Fill the shape structure. */ |
1622 | | /* -------------------------------------------------------------------- */ |
1623 | 0 | if ((shape->line = (lineObj *)malloc(sizeof(lineObj))) == NULL) { |
1624 | 0 | shape->type = MS_SHAPE_NULL; |
1625 | 0 | msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()"); |
1626 | 0 | return; |
1627 | 0 | } |
1628 | | |
1629 | 0 | if (nPoints < 0 || nPoints > 50 * 1000 * 1000) { |
1630 | 0 | free(shape->line); |
1631 | 0 | shape->line = NULL; |
1632 | 0 | shape->numlines = 0; |
1633 | 0 | shape->type = MS_SHAPE_NULL; |
1634 | 0 | msSetError(MS_SHPERR, "Corrupted .shp file : shape %d, nPoints=%d.", |
1635 | 0 | "msSHPReadShape()", hEntity, nPoints); |
1636 | 0 | return; |
1637 | 0 | } |
1638 | | |
1639 | 0 | nRequiredSize = 48 + nPoints * 16; |
1640 | 0 | if (psSHP->nShapeType == SHP_MULTIPOINTZ || |
1641 | 0 | psSHP->nShapeType == SHP_MULTIPOINTM) |
1642 | 0 | nRequiredSize += 16 + nPoints * 8; |
1643 | 0 | if (nRequiredSize > nEntitySize) { |
1644 | 0 | free(shape->line); |
1645 | 0 | shape->line = NULL; |
1646 | 0 | shape->numlines = 0; |
1647 | 0 | shape->type = MS_SHAPE_NULL; |
1648 | 0 | msSetError( |
1649 | 0 | MS_SHPERR, |
1650 | 0 | "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d", |
1651 | 0 | "msSHPReadShape()", hEntity, nPoints, nEntitySize); |
1652 | 0 | return; |
1653 | 0 | } |
1654 | | |
1655 | 0 | shape->numlines = 1; |
1656 | 0 | shape->line[0].numpoints = nPoints; |
1657 | 0 | shape->line[0].point = (pointObj *)malloc(nPoints * sizeof(pointObj)); |
1658 | 0 | if (shape->line[0].point == NULL) { |
1659 | 0 | free(shape->line); |
1660 | 0 | shape->line = NULL; |
1661 | 0 | shape->numlines = 0; |
1662 | 0 | shape->type = MS_SHAPE_NULL; |
1663 | 0 | msSetError(MS_MEMERR, "Out of memory", "msSHPReadShape()"); |
1664 | 0 | return; |
1665 | 0 | } |
1666 | | |
1667 | 0 | for (i = 0; i < nPoints; i++) { |
1668 | 0 | memcpy(&(shape->line[0].point[i].x), pabyRec + 48 + 16 * i, 8); |
1669 | 0 | memcpy(&(shape->line[0].point[i].y), pabyRec + 48 + 16 * i + 8, 8); |
1670 | |
|
1671 | 0 | if (bBigEndian) { |
1672 | 0 | SwapWord(8, &(shape->line[0].point[i].x)); |
1673 | 0 | SwapWord(8, &(shape->line[0].point[i].y)); |
1674 | 0 | } |
1675 | | |
1676 | | /* -------------------------------------------------------------------- */ |
1677 | | /* MulipointZ */ |
1678 | | /* -------------------------------------------------------------------- */ |
1679 | 0 | shape->line[0].point[i].z = 0; /* initialize */ |
1680 | 0 | if (psSHP->nShapeType == SHP_MULTIPOINTZ) { |
1681 | 0 | const int nOffset = 48 + 16 * nPoints; |
1682 | 0 | memcpy(&(shape->line[0].point[i].z), pabyRec + nOffset + 16 + i * 8, 8); |
1683 | 0 | if (bBigEndian) |
1684 | 0 | SwapWord(8, &(shape->line[0].point[i].z)); |
1685 | 0 | } |
1686 | | |
1687 | | /* -------------------------------------------------------------------- */ |
1688 | | /* Measured shape : multipont. */ |
1689 | | /* -------------------------------------------------------------------- */ |
1690 | 0 | shape->line[0].point[i].m = 0; /* initialize */ |
1691 | 0 | if (psSHP->nShapeType == SHP_MULTIPOINTM) { |
1692 | 0 | const int nOffset = 48 + 16 * nPoints; |
1693 | 0 | memcpy(&(shape->line[0].point[i].m), pabyRec + nOffset + 16 + i * 8, 8); |
1694 | 0 | if (bBigEndian) |
1695 | 0 | SwapWord(8, &(shape->line[0].point[i].m)); |
1696 | 0 | } |
1697 | 0 | } |
1698 | |
|
1699 | 0 | shape->type = MS_SHAPE_POINT; |
1700 | 0 | } |
1701 | | |
1702 | | /* -------------------------------------------------------------------- */ |
1703 | | /* Extract a Point. */ |
1704 | | /* -------------------------------------------------------------------- */ |
1705 | 0 | else if (psSHP->nShapeType == SHP_POINT || psSHP->nShapeType == SHP_POINTM || |
1706 | 0 | psSHP->nShapeType == SHP_POINTZ) { |
1707 | |
|
1708 | 0 | if (nEntitySize < 20 + 8) { |
1709 | 0 | shape->type = MS_SHAPE_NULL; |
1710 | 0 | msSetError(MS_SHPERR, |
1711 | 0 | "Corrupted feature encountered. recSize of feature %d=%d", |
1712 | 0 | "msSHPReadShape()", hEntity, msSHXReadSize(psSHP, hEntity)); |
1713 | 0 | return; |
1714 | 0 | } |
1715 | | |
1716 | | /* -------------------------------------------------------------------- */ |
1717 | | /* Fill the shape structure. */ |
1718 | | /* -------------------------------------------------------------------- */ |
1719 | 0 | shape->line = (lineObj *)malloc(sizeof(lineObj)); |
1720 | 0 | MS_CHECK_ALLOC_NO_RET(shape->line, sizeof(lineObj)); |
1721 | |
|
1722 | 0 | shape->numlines = 1; |
1723 | 0 | shape->line[0].numpoints = 1; |
1724 | 0 | shape->line[0].point = (pointObj *)msSmallMalloc(sizeof(pointObj)); |
1725 | |
|
1726 | 0 | memcpy(&(shape->line[0].point[0].x), pabyRec + 12, 8); |
1727 | 0 | memcpy(&(shape->line[0].point[0].y), pabyRec + 20, 8); |
1728 | |
|
1729 | 0 | if (bBigEndian) { |
1730 | 0 | SwapWord(8, &(shape->line[0].point[0].x)); |
1731 | 0 | SwapWord(8, &(shape->line[0].point[0].y)); |
1732 | 0 | } |
1733 | | |
1734 | | /* -------------------------------------------------------------------- */ |
1735 | | /* PointZ */ |
1736 | | /* -------------------------------------------------------------------- */ |
1737 | 0 | shape->line[0].point[0].z = 0; /* initialize */ |
1738 | 0 | if (psSHP->nShapeType == SHP_POINTZ) { |
1739 | 0 | const int nOffset = 20 + 8; |
1740 | 0 | if (nEntitySize >= nOffset + 8) { |
1741 | 0 | memcpy(&(shape->line[0].point[0].z), pabyRec + nOffset, 8); |
1742 | 0 | if (bBigEndian) |
1743 | 0 | SwapWord(8, &(shape->line[0].point[0].z)); |
1744 | 0 | } |
1745 | 0 | } |
1746 | | |
1747 | | /* -------------------------------------------------------------------- */ |
1748 | | /* Measured support : point. */ |
1749 | | /* -------------------------------------------------------------------- */ |
1750 | 0 | shape->line[0].point[0].m = 0; /* initialize */ |
1751 | 0 | if (psSHP->nShapeType == SHP_POINTM) { |
1752 | 0 | const int nOffset = 20 + 8; |
1753 | 0 | if (nEntitySize >= nOffset + 8) { |
1754 | 0 | memcpy(&(shape->line[0].point[0].m), pabyRec + nOffset, 8); |
1755 | 0 | if (bBigEndian) |
1756 | 0 | SwapWord(8, &(shape->line[0].point[0].m)); |
1757 | 0 | } |
1758 | 0 | } |
1759 | | |
1760 | | /* set the bounding box to the point */ |
1761 | 0 | shape->bounds.minx = shape->bounds.maxx = shape->line[0].point[0].x; |
1762 | 0 | shape->bounds.miny = shape->bounds.maxy = shape->line[0].point[0].y; |
1763 | |
|
1764 | 0 | shape->type = MS_SHAPE_POINT; |
1765 | 0 | } |
1766 | | |
1767 | 0 | shape->index = hEntity; |
1768 | |
|
1769 | 0 | return; |
1770 | 0 | } |
1771 | | |
1772 | 0 | int msSHPReadBounds(SHPHandle psSHP, int hEntity, rectObj *padBounds) { |
1773 | | /* -------------------------------------------------------------------- */ |
1774 | | /* Validate the record/entity number. */ |
1775 | | /* -------------------------------------------------------------------- */ |
1776 | 0 | if (psSHP->nRecords <= 0 || hEntity < -1 || hEntity >= psSHP->nRecords) { |
1777 | 0 | padBounds->minx = padBounds->miny = padBounds->maxx = padBounds->maxy = 0.0; |
1778 | 0 | return MS_FAILURE; |
1779 | 0 | } |
1780 | | |
1781 | | /* -------------------------------------------------------------------- */ |
1782 | | /* If the entity is -1 we fetch the bounds for the whole file. */ |
1783 | | /* -------------------------------------------------------------------- */ |
1784 | 0 | if (hEntity == -1) { |
1785 | 0 | padBounds->minx = psSHP->adBoundsMin[0]; |
1786 | 0 | padBounds->miny = psSHP->adBoundsMin[1]; |
1787 | 0 | padBounds->maxx = psSHP->adBoundsMax[0]; |
1788 | 0 | padBounds->maxy = psSHP->adBoundsMax[1]; |
1789 | 0 | } else { |
1790 | |
|
1791 | 0 | if (msSHXReadSize(psSHP, hEntity) <= 4) { /* NULL shape */ |
1792 | 0 | padBounds->minx = padBounds->miny = padBounds->maxx = padBounds->maxy = |
1793 | 0 | 0.0; |
1794 | 0 | return MS_FAILURE; |
1795 | 0 | } |
1796 | | |
1797 | 0 | const int offset = msSHXReadOffset(psSHP, hEntity); |
1798 | 0 | if (offset <= 0 || offset >= INT_MAX - 12 || |
1799 | 0 | 0 != VSIFSeekL(psSHP->fpSHP, offset + 12, 0)) { |
1800 | 0 | msSetError(MS_IOERR, "failed to seek offset", "msSHPReadBounds()"); |
1801 | 0 | return (MS_FAILURE); |
1802 | 0 | } |
1803 | | |
1804 | 0 | if (psSHP->nShapeType != SHP_POINT && psSHP->nShapeType != SHP_POINTZ && |
1805 | 0 | psSHP->nShapeType != SHP_POINTM) { |
1806 | 0 | if (1 != VSIFReadL(padBounds, sizeof(double) * 4, 1, psSHP->fpSHP)) { |
1807 | 0 | msSetError(MS_IOERR, "failed to fread record", "msSHPReadBounds()"); |
1808 | 0 | return (MS_FAILURE); |
1809 | 0 | } |
1810 | | |
1811 | 0 | if (bBigEndian) { |
1812 | 0 | SwapWord(8, &(padBounds->minx)); |
1813 | 0 | SwapWord(8, &(padBounds->miny)); |
1814 | 0 | SwapWord(8, &(padBounds->maxx)); |
1815 | 0 | SwapWord(8, &(padBounds->maxy)); |
1816 | 0 | } |
1817 | |
|
1818 | 0 | if (msIsNan(padBounds->minx)) { /* empty shape */ |
1819 | 0 | padBounds->minx = padBounds->miny = padBounds->maxx = padBounds->maxy = |
1820 | 0 | 0.0; |
1821 | 0 | return MS_FAILURE; |
1822 | 0 | } |
1823 | 0 | } else { |
1824 | | /* -------------------------------------------------------------------- */ |
1825 | | /* For points we fetch the point, and duplicate it as the */ |
1826 | | /* minimum and maximum bound. */ |
1827 | | /* -------------------------------------------------------------------- */ |
1828 | 0 | if (1 != VSIFReadL(padBounds, sizeof(double) * 2, 1, psSHP->fpSHP)) { |
1829 | 0 | msSetError(MS_IOERR, "failed to fread record", "msSHPReadBounds()"); |
1830 | 0 | return (MS_FAILURE); |
1831 | 0 | } |
1832 | | |
1833 | 0 | if (bBigEndian) { |
1834 | 0 | SwapWord(8, &(padBounds->minx)); |
1835 | 0 | SwapWord(8, &(padBounds->miny)); |
1836 | 0 | } |
1837 | |
|
1838 | 0 | padBounds->maxx = padBounds->minx; |
1839 | 0 | padBounds->maxy = padBounds->miny; |
1840 | 0 | } |
1841 | 0 | } |
1842 | | |
1843 | 0 | return MS_SUCCESS; |
1844 | 0 | } |
1845 | | |
1846 | | int msShapefileOpenHandle(shapefileObj *shpfile, const char *filename, |
1847 | 0 | SHPHandle hSHP, DBFHandle hDBF) { |
1848 | 0 | assert(filename != NULL); |
1849 | 0 | assert(hSHP != NULL); |
1850 | 0 | assert(hDBF != NULL); |
1851 | | |
1852 | | /* initialize a few things */ |
1853 | 0 | shpfile->status = NULL; |
1854 | 0 | shpfile->lastshape = -1; |
1855 | 0 | shpfile->isopen = MS_FALSE; |
1856 | |
|
1857 | 0 | shpfile->hSHP = hSHP; |
1858 | |
|
1859 | 0 | strlcpy(shpfile->source, filename, sizeof(shpfile->source)); |
1860 | | |
1861 | | /* load some information about this shapefile */ |
1862 | 0 | msSHPGetInfo(shpfile->hSHP, &shpfile->numshapes, &shpfile->type); |
1863 | |
|
1864 | 0 | if (shpfile->numshapes < 0 || shpfile->numshapes > 256000000) { |
1865 | 0 | msSetError(MS_SHPERR, "Corrupted .shp file : numshapes = %d.", |
1866 | 0 | "msShapefileOpen()", shpfile->numshapes); |
1867 | 0 | msDBFClose(hDBF); |
1868 | 0 | msSHPClose(hSHP); |
1869 | 0 | return -1; |
1870 | 0 | } |
1871 | | |
1872 | 0 | msSHPReadBounds(shpfile->hSHP, -1, &(shpfile->bounds)); |
1873 | |
|
1874 | 0 | shpfile->hDBF = hDBF; |
1875 | |
|
1876 | 0 | shpfile->isopen = MS_TRUE; |
1877 | 0 | return (0); /* all o.k. */ |
1878 | 0 | } |
1879 | | |
1880 | | int msShapefileOpenVirtualFile(shapefileObj *shpfile, const char *filename, |
1881 | | VSILFILE *fpSHP, VSILFILE *fpSHX, |
1882 | 0 | VSILFILE *fpDBF, int log_failures) { |
1883 | 0 | assert(filename != NULL); |
1884 | 0 | assert(fpSHP != NULL); |
1885 | 0 | assert(fpSHX != NULL); |
1886 | 0 | assert(fpDBF != NULL); |
1887 | | |
1888 | | /* open the shapefile file (appending ok) and get basic info */ |
1889 | 0 | SHPHandle hSHP = msSHPOpenVirtualFile(fpSHP, fpSHX); |
1890 | 0 | if (!hSHP) { |
1891 | 0 | if (log_failures) |
1892 | 0 | msSetError(MS_IOERR, "(%s)", "msShapefileOpen()", filename); |
1893 | 0 | VSIFCloseL(fpDBF); |
1894 | 0 | return (-1); |
1895 | 0 | } |
1896 | | |
1897 | 0 | DBFHandle hDBF = msDBFOpenVirtualFile(fpDBF); |
1898 | |
|
1899 | 0 | if (!hDBF) { |
1900 | 0 | if (log_failures) |
1901 | 0 | msSetError(MS_IOERR, "(%s)", "msShapefileOpen()", filename); |
1902 | 0 | msSHPClose(hSHP); |
1903 | 0 | return (-1); |
1904 | 0 | } |
1905 | | |
1906 | 0 | return msShapefileOpenHandle(shpfile, filename, hSHP, hDBF); |
1907 | 0 | } |
1908 | | |
1909 | | int msShapefileOpen(shapefileObj *shpfile, const char *mode, |
1910 | 0 | const char *filename, int log_failures) { |
1911 | 0 | int i; |
1912 | 0 | char *dbfFilename; |
1913 | 0 | size_t bufferSize = 0; |
1914 | |
|
1915 | 0 | if (!filename) { |
1916 | 0 | if (log_failures) |
1917 | 0 | msSetError(MS_IOERR, "No (NULL) filename provided.", "msShapefileOpen()"); |
1918 | 0 | return (-1); |
1919 | 0 | } |
1920 | | |
1921 | | /* open the shapefile file (appending ok) and get basic info */ |
1922 | 0 | SHPHandle hSHP; |
1923 | 0 | if (!mode) |
1924 | 0 | hSHP = msSHPOpen(filename, "rb"); |
1925 | 0 | else |
1926 | 0 | hSHP = msSHPOpen(filename, mode); |
1927 | |
|
1928 | 0 | if (!hSHP) { |
1929 | 0 | if (log_failures) |
1930 | 0 | msSetError(MS_IOERR, "(%s)", "msShapefileOpen()", filename); |
1931 | 0 | return (-1); |
1932 | 0 | } |
1933 | | |
1934 | 0 | bufferSize = strlen(filename) + 5; |
1935 | 0 | dbfFilename = (char *)msSmallMalloc(bufferSize); |
1936 | 0 | strcpy(dbfFilename, filename); |
1937 | | |
1938 | | /* clean off any extension the filename might have */ |
1939 | 0 | for (i = strlen(dbfFilename) - 1; |
1940 | 0 | i > 0 && dbfFilename[i] != '.' && dbfFilename[i] != '/' && |
1941 | 0 | dbfFilename[i] != '\\'; |
1942 | 0 | i--) { |
1943 | 0 | } |
1944 | |
|
1945 | 0 | if (dbfFilename[i] == '.') |
1946 | 0 | dbfFilename[i] = '\0'; |
1947 | |
|
1948 | 0 | strlcat(dbfFilename, ".dbf", bufferSize); |
1949 | |
|
1950 | 0 | DBFHandle hDBF = msDBFOpen(dbfFilename, "rb"); |
1951 | |
|
1952 | 0 | if (!hDBF) { |
1953 | 0 | if (log_failures) |
1954 | 0 | msSetError(MS_IOERR, "(%s)", "msShapefileOpen()", dbfFilename); |
1955 | 0 | free(dbfFilename); |
1956 | 0 | msSHPClose(hSHP); |
1957 | 0 | return (-1); |
1958 | 0 | } |
1959 | 0 | free(dbfFilename); |
1960 | |
|
1961 | 0 | return msShapefileOpenHandle(shpfile, filename, hSHP, hDBF); |
1962 | 0 | } |
1963 | | |
1964 | | /* Creates a new shapefile */ |
1965 | 0 | int msShapefileCreate(shapefileObj *shpfile, char *filename, int type) { |
1966 | 0 | if (type != SHP_POINT && type != SHP_MULTIPOINT && type != SHP_ARC && |
1967 | 0 | type != SHP_POLYGON && type != SHP_POINTM && type != SHP_MULTIPOINTM && |
1968 | 0 | type != SHP_ARCM && type != SHP_POLYGONM && type != SHP_POINTZ && |
1969 | 0 | type != SHP_MULTIPOINTZ && type != SHP_ARCZ && type != SHP_POLYGONZ) { |
1970 | 0 | msSetError(MS_SHPERR, "Invalid shape type.", "msNewSHPFile()"); |
1971 | 0 | return (-1); |
1972 | 0 | } |
1973 | | |
1974 | | /* create the spatial portion */ |
1975 | 0 | shpfile->hSHP = msSHPCreate(filename, type); |
1976 | 0 | if (!shpfile->hSHP) { |
1977 | 0 | msSetError(MS_IOERR, "(%s)", "msNewSHPFile()", filename); |
1978 | 0 | return (-1); |
1979 | 0 | } |
1980 | | |
1981 | | /* retrieve a few things about this shapefile */ |
1982 | 0 | msSHPGetInfo(shpfile->hSHP, &shpfile->numshapes, &shpfile->type); |
1983 | 0 | msSHPReadBounds(shpfile->hSHP, -1, &(shpfile->bounds)); |
1984 | | |
1985 | | /* initialize a few other things */ |
1986 | 0 | shpfile->status = NULL; |
1987 | 0 | shpfile->lastshape = -1; |
1988 | 0 | shpfile->isopen = MS_TRUE; |
1989 | |
|
1990 | 0 | shpfile->hDBF = NULL; /* XBase file is NOT created here... */ |
1991 | 0 | return (0); |
1992 | 0 | } |
1993 | | |
1994 | 0 | void msShapefileClose(shapefileObj *shpfile) { |
1995 | 0 | if (shpfile && shpfile->isopen == MS_TRUE) { /* Silently return if called with |
1996 | | NULL shpfile by freeLayer() */ |
1997 | 0 | if (shpfile->hSHP) |
1998 | 0 | msSHPClose(shpfile->hSHP); |
1999 | 0 | if (shpfile->hDBF) |
2000 | 0 | msDBFClose(shpfile->hDBF); |
2001 | 0 | free(shpfile->status); |
2002 | 0 | shpfile->isopen = MS_FALSE; |
2003 | 0 | } |
2004 | 0 | } |
2005 | | |
2006 | | /* status array lives in the shpfile, can return MS_SUCCESS/MS_FAILURE/MS_DONE |
2007 | | */ |
2008 | 0 | int msShapefileWhichShapes(shapefileObj *shpfile, rectObj rect, int debug) { |
2009 | 0 | int i; |
2010 | 0 | rectObj shaperect; |
2011 | 0 | char *filename; |
2012 | |
|
2013 | 0 | free(shpfile->status); |
2014 | 0 | shpfile->status = NULL; |
2015 | | |
2016 | | /* rect and shapefile DON'T overlap... */ |
2017 | 0 | if (msRectOverlap(&shpfile->bounds, &rect) != MS_TRUE) |
2018 | 0 | return (MS_DONE); |
2019 | | |
2020 | 0 | if (msRectContained(&shpfile->bounds, &rect) == MS_TRUE) { |
2021 | 0 | shpfile->status = msAllocBitArray(shpfile->numshapes); |
2022 | 0 | if (!shpfile->status) { |
2023 | 0 | msSetError(MS_MEMERR, NULL, "msShapefileWhichShapes()"); |
2024 | 0 | return (MS_FAILURE); |
2025 | 0 | } |
2026 | 0 | msSetAllBits(shpfile->status, shpfile->numshapes, 1); |
2027 | 0 | } else { |
2028 | | |
2029 | | /* deal with case where sourcename is of the form 'file.shp' */ |
2030 | 0 | char *sourcename = |
2031 | 0 | msStrdup(shpfile->source); /* shape file source string from map file */ |
2032 | 0 | char *s = strstr(sourcename, ".shp"); |
2033 | 0 | if (s) |
2034 | 0 | *s = '\0'; |
2035 | 0 | else { |
2036 | 0 | s = strstr(sourcename, ".SHP"); |
2037 | 0 | if (s) |
2038 | 0 | *s = '\0'; |
2039 | 0 | } |
2040 | |
|
2041 | 0 | filename = |
2042 | 0 | (char *)malloc(strlen(sourcename) + strlen(MS_INDEX_EXTENSION) + 1); |
2043 | 0 | MS_CHECK_ALLOC(filename, |
2044 | 0 | strlen(sourcename) + strlen(MS_INDEX_EXTENSION) + 1, |
2045 | 0 | MS_FAILURE); |
2046 | |
|
2047 | 0 | sprintf(filename, "%s%s", sourcename, MS_INDEX_EXTENSION); |
2048 | |
|
2049 | 0 | shpfile->status = |
2050 | 0 | msSearchDiskTree(filename, rect, debug, shpfile->numshapes); |
2051 | 0 | free(filename); |
2052 | 0 | free(sourcename); |
2053 | |
|
2054 | 0 | if (shpfile->status) { /* index */ |
2055 | 0 | msFilterTreeSearch(shpfile, shpfile->status, rect); |
2056 | 0 | } else { /* no index */ |
2057 | 0 | shpfile->status = msAllocBitArray(shpfile->numshapes); |
2058 | 0 | if (!shpfile->status) { |
2059 | 0 | msSetError(MS_MEMERR, NULL, "msShapefileWhichShapes()"); |
2060 | 0 | return (MS_FAILURE); |
2061 | 0 | } |
2062 | | |
2063 | 0 | for (i = 0; i < shpfile->numshapes; i++) { |
2064 | 0 | if (msSHPReadBounds(shpfile->hSHP, i, &shaperect) != MS_SUCCESS) { |
2065 | 0 | if (msSHXReadSize(shpfile->hSHP, i) == 4) { /* handle NULL shape */ |
2066 | 0 | continue; |
2067 | 0 | } |
2068 | 0 | return (MS_FAILURE); |
2069 | 0 | } |
2070 | | |
2071 | 0 | if (msRectOverlap(&shaperect, &rect) == MS_TRUE) |
2072 | 0 | msSetBit(shpfile->status, i, 1); |
2073 | 0 | } |
2074 | 0 | } |
2075 | 0 | } |
2076 | | |
2077 | 0 | shpfile->lastshape = -1; |
2078 | |
|
2079 | 0 | return (MS_SUCCESS); /* success */ |
2080 | 0 | } |
2081 | | |
2082 | | /* Return the absolute path to the given layer's tileindex file's directory */ |
2083 | 0 | void msTileIndexAbsoluteDir(char *tiFileAbsDir, layerObj *layer) { |
2084 | 0 | char tiFileAbsPath[MS_MAXPATHLEN]; |
2085 | 0 | char *tiFileAbsDirTmp = NULL; |
2086 | |
|
2087 | 0 | msBuildPath(tiFileAbsPath, layer->map->mappath, |
2088 | 0 | layer->tileindex); /* absolute path to tileindex file */ |
2089 | 0 | tiFileAbsDirTmp = msGetPath(tiFileAbsPath); /* tileindex file's directory */ |
2090 | 0 | strlcpy(tiFileAbsDir, tiFileAbsDirTmp, MS_MAXPATHLEN); |
2091 | 0 | free(tiFileAbsDirTmp); |
2092 | 0 | } |
2093 | | |
2094 | | /* |
2095 | | ** Build possible paths we might find the tile file at: |
2096 | | ** map dir + shape path + filename? |
2097 | | ** tile dir + shape path + filename? |
2098 | | ** map dir + filename? |
2099 | | ** |
2100 | | ** Returns |
2101 | | ** MS_SUCCESS - found a file |
2102 | | ** MS_FAILURE - no file, and map is configured to fail on missing |
2103 | | ** MS_DONE - no file, and map is configured to continue on missing |
2104 | | */ |
2105 | | static int msTiledSHPTryOpen(shapefileObj *shpfile, layerObj *layer, |
2106 | 0 | char *tiFileAbsDir, const char *filename) { |
2107 | 0 | char szPath[MS_MAXPATHLEN]; |
2108 | 0 | int ignore_missing = msMapIgnoreMissingData(layer->map); |
2109 | 0 | int log_failures = MS_TRUE; |
2110 | |
|
2111 | 0 | if (ignore_missing == MS_MISSING_DATA_IGNORE) |
2112 | 0 | log_failures = MS_FALSE; |
2113 | |
|
2114 | 0 | if (msShapefileOpen(shpfile, "rb", |
2115 | 0 | msBuildPath3(szPath, layer->map->mappath, |
2116 | 0 | layer->map->shapepath, filename), |
2117 | 0 | log_failures) == -1) { |
2118 | 0 | if (msShapefileOpen( |
2119 | 0 | shpfile, "rb", |
2120 | 0 | msBuildPath3(szPath, tiFileAbsDir, layer->map->shapepath, filename), |
2121 | 0 | log_failures) == -1) { |
2122 | 0 | if (msShapefileOpen(shpfile, "rb", |
2123 | 0 | msBuildPath(szPath, layer->map->mappath, filename), |
2124 | 0 | log_failures) == -1) { |
2125 | 0 | if (ignore_missing == MS_MISSING_DATA_FAIL) { |
2126 | 0 | msSetError( |
2127 | 0 | MS_IOERR, |
2128 | 0 | "Unable to open shapefile '%s' for layer '%s' ... fatal error.", |
2129 | 0 | "msTiledSHPTryOpen()", filename, layer->name); |
2130 | 0 | return (MS_FAILURE); |
2131 | 0 | } else if (ignore_missing == MS_MISSING_DATA_LOG) { |
2132 | 0 | if (layer->debug || layer->map->debug) { |
2133 | 0 | msDebug("Unable to open shapefile '%s' for layer '%s' ... ignoring " |
2134 | 0 | "this missing data.\n", |
2135 | 0 | szPath, layer->name); |
2136 | 0 | } |
2137 | 0 | return (MS_DONE); |
2138 | 0 | } else if (ignore_missing == MS_MISSING_DATA_IGNORE) { |
2139 | 0 | return (MS_DONE); |
2140 | 0 | } else { |
2141 | | /* never get here */ |
2142 | 0 | msSetError(MS_IOERR, "msIgnoreMissingData returned unexpected value.", |
2143 | 0 | "msTiledSHPTryOpen()"); |
2144 | 0 | return (MS_FAILURE); |
2145 | 0 | } |
2146 | 0 | } |
2147 | 0 | } |
2148 | 0 | } |
2149 | 0 | return (MS_SUCCESS); |
2150 | 0 | } |
2151 | | |
2152 | | static const char *msTiledSHPLoadEntry(layerObj *layer, int i, char *tilename, |
2153 | 0 | size_t tilenamesize) { |
2154 | 0 | const char *filename; |
2155 | 0 | msTiledSHPLayerInfo *tSHP = layer->layerinfo; |
2156 | |
|
2157 | 0 | msProjectDestroyReprojector(tSHP->reprojectorFromTileProjToLayerProj); |
2158 | 0 | tSHP->reprojectorFromTileProjToLayerProj = NULL; |
2159 | |
|
2160 | 0 | msFreeProjection(&(tSHP->sTileProj)); |
2161 | 0 | if (layer->tilesrs != NULL) { |
2162 | 0 | int idx = msDBFGetItemIndex(tSHP->tileshpfile->hDBF, layer->tilesrs); |
2163 | 0 | const char *pszWKT = |
2164 | 0 | msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, idx); |
2165 | 0 | IGNORE_RET_VAL( |
2166 | 0 | msOGCWKT2ProjectionObj(pszWKT, &(tSHP->sTileProj), layer->debug)); |
2167 | 0 | } |
2168 | |
|
2169 | 0 | if (!layer->data) /* assume whole filename is in attribute field */ |
2170 | 0 | filename = msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, |
2171 | 0 | layer->tileitemindex); |
2172 | 0 | else { |
2173 | 0 | snprintf(tilename, tilenamesize, "%s/%s", |
2174 | 0 | msDBFReadStringAttribute(tSHP->tileshpfile->hDBF, i, |
2175 | 0 | layer->tileitemindex), |
2176 | 0 | layer->data); |
2177 | 0 | filename = tilename; |
2178 | 0 | } |
2179 | |
|
2180 | 0 | return filename; |
2181 | 0 | } |
2182 | | |
2183 | 0 | static int msTiledSHPOpenFile(layerObj *layer) { |
2184 | 0 | int i; |
2185 | 0 | const char *filename; |
2186 | 0 | char tilename[MS_MAXPATHLEN], szPath[MS_MAXPATHLEN]; |
2187 | 0 | char tiFileAbsDir[MS_MAXPATHLEN]; |
2188 | |
|
2189 | 0 | msTiledSHPLayerInfo *tSHP = NULL; |
2190 | |
|
2191 | 0 | if (layer->layerinfo != NULL) { |
2192 | 0 | return MS_SUCCESS; // Nothing to do... layer is already opened |
2193 | 0 | } |
2194 | | |
2195 | 0 | if (msCheckParentPointer(layer->map, "map") == MS_FAILURE) |
2196 | 0 | return MS_FAILURE; |
2197 | | |
2198 | | /* allocate space for a shapefileObj using layer->layerinfo */ |
2199 | 0 | tSHP = (msTiledSHPLayerInfo *)calloc(1, sizeof(msTiledSHPLayerInfo)); |
2200 | 0 | MS_CHECK_ALLOC(tSHP, sizeof(msTiledSHPLayerInfo), MS_FAILURE); |
2201 | 0 | msInitProjection(&(tSHP->sTileProj)); |
2202 | 0 | msProjectionInheritContextFrom(&(tSHP->sTileProj), &layer->projection); |
2203 | |
|
2204 | 0 | tSHP->shpfile = (shapefileObj *)malloc(sizeof(shapefileObj)); |
2205 | 0 | if (tSHP->shpfile == NULL) { |
2206 | 0 | msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n", |
2207 | 0 | "msTiledSHPOpenFile()", __FILE__, __LINE__, |
2208 | 0 | (unsigned int)sizeof(shapefileObj)); |
2209 | 0 | msFreeProjection(&(tSHP->sTileProj)); |
2210 | 0 | free(tSHP); |
2211 | 0 | return MS_FAILURE; |
2212 | 0 | } |
2213 | | |
2214 | 0 | tSHP->shpfile->isopen = |
2215 | 0 | MS_FALSE; /* in case of error: do not try to close the shpfile */ |
2216 | 0 | tSHP->tileshpfile = |
2217 | 0 | NULL; /* may need this if not using a tile layer, look for malloc later */ |
2218 | 0 | layer->layerinfo = tSHP; |
2219 | |
|
2220 | 0 | tSHP->tilelayerindex = msGetLayerIndex(layer->map, layer->tileindex); |
2221 | 0 | if (tSHP->tilelayerindex != |
2222 | 0 | -1) { /* does the tileindex reference another layer */ |
2223 | 0 | int status; |
2224 | 0 | layerObj *tlp; |
2225 | |
|
2226 | 0 | tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex)); |
2227 | |
|
2228 | 0 | if (tlp->connectiontype != MS_SHAPEFILE) { |
2229 | 0 | msSetError(MS_SHPERR, "Tileindex layer must be a shapefile.", |
2230 | 0 | "msTiledSHPOpenFile()"); |
2231 | 0 | return (MS_FAILURE); |
2232 | 0 | } |
2233 | | |
2234 | 0 | status = msLayerOpen(tlp); |
2235 | 0 | if (status != MS_SUCCESS) |
2236 | 0 | return (MS_FAILURE); |
2237 | | |
2238 | | /* build item list */ |
2239 | 0 | status = msLayerWhichItems(tlp, MS_FALSE, NULL); |
2240 | 0 | if (status != MS_SUCCESS) |
2241 | 0 | return (MS_FAILURE); |
2242 | | |
2243 | 0 | tSHP->tileshpfile = |
2244 | 0 | (shapefileObj *)tlp->layerinfo; /* shapefiles use layerinfo to point to |
2245 | | a shapefileObj */ |
2246 | |
|
2247 | 0 | } else { /* or reference a shapefile directly */ |
2248 | | |
2249 | | /* we need tSHP->tileshpfile if we're not working with a layer */ |
2250 | 0 | tSHP->tileshpfile = (shapefileObj *)malloc(sizeof(shapefileObj)); |
2251 | 0 | if (tSHP->tileshpfile == NULL) { |
2252 | 0 | msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n", |
2253 | 0 | "msTiledSHPOpenFile()", __FILE__, __LINE__, |
2254 | 0 | (unsigned int)sizeof(shapefileObj)); |
2255 | 0 | free(tSHP->shpfile); |
2256 | 0 | msFreeProjection(&(tSHP->sTileProj)); |
2257 | 0 | free(tSHP); |
2258 | 0 | layer->layerinfo = NULL; |
2259 | 0 | return MS_FAILURE; |
2260 | 0 | } |
2261 | | |
2262 | 0 | if (msShapefileOpen(tSHP->tileshpfile, "rb", |
2263 | 0 | msBuildPath3(szPath, layer->map->mappath, |
2264 | 0 | layer->map->shapepath, layer->tileindex), |
2265 | 0 | MS_TRUE) == -1) |
2266 | 0 | if (msShapefileOpen( |
2267 | 0 | tSHP->tileshpfile, "rb", |
2268 | 0 | msBuildPath(szPath, layer->map->mappath, layer->tileindex), |
2269 | 0 | MS_TRUE) == -1) |
2270 | 0 | return (MS_FAILURE); |
2271 | 0 | } |
2272 | | |
2273 | 0 | if ((layer->tileitemindex = |
2274 | 0 | msDBFGetItemIndex(tSHP->tileshpfile->hDBF, layer->tileitem)) == -1) |
2275 | 0 | return (MS_FAILURE); |
2276 | | |
2277 | 0 | if (layer->tilesrs != NULL && |
2278 | 0 | msDBFGetItemIndex(tSHP->tileshpfile->hDBF, layer->tilesrs) < 0) { |
2279 | 0 | msSetError(MS_SHPERR, "Cannot identify TILESRS field.", |
2280 | 0 | "msTiledSHPOpenFile()"); |
2281 | 0 | return MS_FAILURE; |
2282 | 0 | } |
2283 | 0 | if (layer->tilesrs != NULL && layer->projection.numargs == 0) { |
2284 | 0 | msSetError(MS_SHPERR, |
2285 | 0 | "A layer with TILESRS set in TILEINDEX `%s' must have a " |
2286 | 0 | "projection set on itself.", |
2287 | 0 | "msOGRLayerOpen()", layer->tileindex); |
2288 | 0 | return MS_FAILURE; |
2289 | 0 | } |
2290 | | |
2291 | 0 | msTileIndexAbsoluteDir(tiFileAbsDir, layer); |
2292 | | |
2293 | | /* position the source at the FIRST tile to use as a template, this is so the |
2294 | | * functions that fill the iteminfo array have something to work from */ |
2295 | 0 | for (i = 0; i < tSHP->tileshpfile->numshapes; i++) { |
2296 | 0 | int try_open; |
2297 | |
|
2298 | 0 | filename = msTiledSHPLoadEntry(layer, i, tilename, sizeof(tilename)); |
2299 | 0 | if (strlen(filename) == 0) |
2300 | 0 | continue; /* check again */ |
2301 | | |
2302 | 0 | try_open = msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename); |
2303 | 0 | if (try_open == MS_DONE) |
2304 | 0 | continue; |
2305 | 0 | else if (try_open == MS_FAILURE) |
2306 | 0 | return (MS_FAILURE); |
2307 | | |
2308 | 0 | return (MS_SUCCESS); /* found a template, ok to proceed */ |
2309 | 0 | } |
2310 | | |
2311 | 0 | msSetError(MS_SHPERR, |
2312 | 0 | "Unable to open a single tile to use as a template in layer %s.", |
2313 | 0 | "msTiledSHPOpenFile()", layer->name ? layer->name : "(null)"); |
2314 | 0 | return (MS_FAILURE); |
2315 | 0 | } |
2316 | | |
2317 | 0 | static int msTiledSHPWhichShapes(layerObj *layer, rectObj rect, int isQuery) { |
2318 | 0 | int i, status; |
2319 | 0 | const char *filename; |
2320 | 0 | char tilename[MS_MAXPATHLEN]; |
2321 | 0 | char tiFileAbsDir[MS_MAXPATHLEN]; |
2322 | |
|
2323 | 0 | msTiledSHPLayerInfo *tSHP = NULL; |
2324 | |
|
2325 | 0 | if (msCheckParentPointer(layer->map, "map") == MS_FAILURE) |
2326 | 0 | return MS_FAILURE; |
2327 | | |
2328 | 0 | tSHP = layer->layerinfo; |
2329 | 0 | if (!tSHP) { |
2330 | 0 | msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", |
2331 | 0 | "msTiledSHPWhichShapes()"); |
2332 | 0 | return (MS_FAILURE); |
2333 | 0 | } |
2334 | | |
2335 | 0 | msShapefileClose(tSHP->shpfile); /* close previously opened files */ |
2336 | |
|
2337 | 0 | tSHP->searchrect = rect; /* save the search extent */ |
2338 | |
|
2339 | 0 | if (tSHP->tilelayerindex != |
2340 | 0 | -1) { /* does the tileindex reference another layer */ |
2341 | 0 | layerObj *tlp; |
2342 | 0 | shapeObj tshape; |
2343 | |
|
2344 | 0 | tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex)); |
2345 | 0 | status = msLayerWhichShapes(tlp, rect, isQuery); |
2346 | 0 | if (status != MS_SUCCESS) |
2347 | 0 | return (status); /* could be MS_DONE or MS_FAILURE */ |
2348 | | |
2349 | 0 | msTileIndexAbsoluteDir(tiFileAbsDir, layer); |
2350 | |
|
2351 | 0 | msInitShape(&tshape); |
2352 | 0 | while ((status = msLayerNextShape(tlp, &tshape)) == MS_SUCCESS) { |
2353 | 0 | int try_open; |
2354 | 0 | rectObj rectTile = rect; |
2355 | |
|
2356 | 0 | filename = |
2357 | 0 | msTiledSHPLoadEntry(layer, tshape.index, tilename, sizeof(tilename)); |
2358 | 0 | if (strlen(filename) == 0) |
2359 | 0 | continue; /* check again */ |
2360 | | |
2361 | 0 | try_open = |
2362 | 0 | msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename); |
2363 | 0 | if (try_open == MS_DONE) |
2364 | 0 | continue; |
2365 | 0 | else if (try_open == MS_FAILURE) |
2366 | 0 | return (MS_FAILURE); |
2367 | | |
2368 | 0 | if (tSHP->sTileProj.numargs > 0) { |
2369 | 0 | msProjectRect(&(layer->projection), &(tSHP->sTileProj), &rectTile); |
2370 | 0 | } |
2371 | |
|
2372 | 0 | status = msShapefileWhichShapes(tSHP->shpfile, rectTile, layer->debug); |
2373 | 0 | if (status == MS_DONE) { |
2374 | | /* Close and continue to next tile */ |
2375 | 0 | msShapefileClose(tSHP->shpfile); |
2376 | 0 | continue; |
2377 | 0 | } else if (status != MS_SUCCESS) { |
2378 | 0 | msShapefileClose(tSHP->shpfile); |
2379 | 0 | return (MS_FAILURE); |
2380 | 0 | } |
2381 | | |
2382 | | /* the layer functions keeps track of this */ |
2383 | | /* tSHP->tileshpfile->lastshape = tshape.index; */ |
2384 | 0 | break; |
2385 | 0 | } |
2386 | 0 | return (status); /* if we reach here we either 1) ran out of tiles or 2) had |
2387 | | an error reading a tile */ |
2388 | |
|
2389 | 0 | } else { /* or reference a shapefile directly */ |
2390 | 0 | int try_open; |
2391 | |
|
2392 | 0 | status = msShapefileWhichShapes(tSHP->tileshpfile, rect, layer->debug); |
2393 | 0 | if (status != MS_SUCCESS) |
2394 | 0 | return (status); /* could be MS_DONE or MS_FAILURE */ |
2395 | | |
2396 | 0 | msTileIndexAbsoluteDir(tiFileAbsDir, layer); |
2397 | | |
2398 | | /* position the source at the FIRST shapefile */ |
2399 | 0 | for (i = 0; i < tSHP->tileshpfile->numshapes; i++) { |
2400 | 0 | rectObj rectTile = rect; |
2401 | 0 | if (msGetBit(tSHP->tileshpfile->status, i)) { |
2402 | |
|
2403 | 0 | filename = msTiledSHPLoadEntry(layer, i, tilename, sizeof(tilename)); |
2404 | 0 | if (strlen(filename) == 0) |
2405 | 0 | continue; /* check again */ |
2406 | | |
2407 | 0 | try_open = |
2408 | 0 | msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename); |
2409 | 0 | if (try_open == MS_DONE) |
2410 | 0 | continue; |
2411 | 0 | else if (try_open == MS_FAILURE) |
2412 | 0 | return (MS_FAILURE); |
2413 | | |
2414 | 0 | if (tSHP->sTileProj.numargs > 0) { |
2415 | 0 | msProjectRect(&(layer->projection), &(tSHP->sTileProj), &rectTile); |
2416 | 0 | } |
2417 | |
|
2418 | 0 | status = msShapefileWhichShapes(tSHP->shpfile, rectTile, layer->debug); |
2419 | 0 | if (status == MS_DONE) { |
2420 | | /* Close and continue to next tile */ |
2421 | 0 | msShapefileClose(tSHP->shpfile); |
2422 | 0 | continue; |
2423 | 0 | } else if (status != MS_SUCCESS) { |
2424 | 0 | msShapefileClose(tSHP->shpfile); |
2425 | 0 | return (MS_FAILURE); |
2426 | 0 | } |
2427 | | |
2428 | 0 | tSHP->tileshpfile->lastshape = i; |
2429 | 0 | break; |
2430 | 0 | } |
2431 | 0 | } |
2432 | | |
2433 | 0 | if (i == tSHP->tileshpfile->numshapes) |
2434 | 0 | return (MS_DONE); /* no more tiles */ |
2435 | 0 | else |
2436 | 0 | return (MS_SUCCESS); |
2437 | 0 | } |
2438 | | |
2439 | 0 | return (MS_FAILURE); /* should *never* get here */ |
2440 | 0 | } |
2441 | | |
2442 | 0 | static int msTiledSHPNextShape(layerObj *layer, shapeObj *shape) { |
2443 | 0 | int i, status, filter_passed = MS_FALSE; |
2444 | 0 | const char *filename; |
2445 | 0 | char tilename[MS_MAXPATHLEN]; |
2446 | 0 | char tiFileAbsDir[MS_MAXPATHLEN]; |
2447 | |
|
2448 | 0 | msTiledSHPLayerInfo *tSHP = NULL; |
2449 | |
|
2450 | 0 | if (msCheckParentPointer(layer->map, "map") == MS_FAILURE) |
2451 | 0 | return MS_FAILURE; |
2452 | | |
2453 | 0 | tSHP = layer->layerinfo; |
2454 | 0 | if (!tSHP) { |
2455 | 0 | msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", |
2456 | 0 | "msTiledSHPNextShape()"); |
2457 | 0 | return (MS_FAILURE); |
2458 | 0 | } |
2459 | | |
2460 | 0 | msTileIndexAbsoluteDir(tiFileAbsDir, layer); |
2461 | |
|
2462 | 0 | do { |
2463 | 0 | i = tSHP->shpfile->lastshape + 1; |
2464 | 0 | while (i < tSHP->shpfile->numshapes && !msGetBit(tSHP->shpfile->status, i)) |
2465 | 0 | i++; /* next "in" shape */ |
2466 | |
|
2467 | 0 | if (i == |
2468 | 0 | tSHP->shpfile->numshapes) { /* done with this tile, need a new one */ |
2469 | 0 | msShapefileClose(tSHP->shpfile); /* clean up */ |
2470 | | |
2471 | | /* position the source to the NEXT shapefile based on the tileindex */ |
2472 | 0 | if (tSHP->tilelayerindex != |
2473 | 0 | -1) { /* does the tileindex reference another layer */ |
2474 | 0 | layerObj *tlp; |
2475 | 0 | shapeObj tshape; |
2476 | 0 | int try_open; |
2477 | |
|
2478 | 0 | tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex)); |
2479 | |
|
2480 | 0 | msInitShape(&tshape); |
2481 | 0 | while ((status = msLayerNextShape(tlp, &tshape)) == MS_SUCCESS) { |
2482 | 0 | rectObj rectTile = tSHP->searchrect; |
2483 | |
|
2484 | 0 | filename = msTiledSHPLoadEntry(layer, tshape.index, tilename, |
2485 | 0 | sizeof(tilename)); |
2486 | 0 | if (strlen(filename) == 0) |
2487 | 0 | continue; /* check again */ |
2488 | | |
2489 | 0 | try_open = |
2490 | 0 | msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename); |
2491 | 0 | if (try_open == MS_DONE) |
2492 | 0 | continue; |
2493 | 0 | else if (try_open == MS_FAILURE) |
2494 | 0 | return (MS_FAILURE); |
2495 | | |
2496 | 0 | if (tSHP->sTileProj.numargs > 0) { |
2497 | 0 | msProjectRect(&(layer->projection), &(tSHP->sTileProj), &rectTile); |
2498 | 0 | } |
2499 | |
|
2500 | 0 | status = |
2501 | 0 | msShapefileWhichShapes(tSHP->shpfile, rectTile, layer->debug); |
2502 | 0 | if (status == MS_DONE) { |
2503 | | /* Close and continue to next tile */ |
2504 | 0 | msShapefileClose(tSHP->shpfile); |
2505 | 0 | continue; |
2506 | 0 | } else if (status != MS_SUCCESS) { |
2507 | 0 | msShapefileClose(tSHP->shpfile); |
2508 | 0 | tSHP->tileshpfile->lastshape = -1; |
2509 | 0 | return (MS_FAILURE); |
2510 | 0 | } |
2511 | | |
2512 | | /* the layer functions keeps track of this */ |
2513 | | /* tSHP->tileshpfile->lastshape = tshape.index; */ |
2514 | 0 | break; |
2515 | 0 | } |
2516 | | |
2517 | 0 | if (status == MS_DONE) { |
2518 | 0 | tSHP->tileshpfile->lastshape = -1; |
2519 | 0 | return (MS_DONE); /* no more tiles */ |
2520 | 0 | } else { |
2521 | 0 | msFreeShape(&tshape); |
2522 | 0 | continue; /* we've got shapes */ |
2523 | 0 | } |
2524 | |
|
2525 | 0 | } else { /* or reference a shapefile directly */ |
2526 | |
|
2527 | 0 | for (i = (tSHP->tileshpfile->lastshape + 1); |
2528 | 0 | i < tSHP->tileshpfile->numshapes; i++) { |
2529 | 0 | if (msGetBit(tSHP->tileshpfile->status, i)) { |
2530 | 0 | rectObj rectTile = tSHP->searchrect; |
2531 | 0 | int try_open; |
2532 | |
|
2533 | 0 | filename = |
2534 | 0 | msTiledSHPLoadEntry(layer, i, tilename, sizeof(tilename)); |
2535 | 0 | if (strlen(filename) == 0) |
2536 | 0 | continue; /* check again */ |
2537 | | |
2538 | 0 | try_open = |
2539 | 0 | msTiledSHPTryOpen(tSHP->shpfile, layer, tiFileAbsDir, filename); |
2540 | 0 | if (try_open == MS_DONE) |
2541 | 0 | continue; |
2542 | 0 | else if (try_open == MS_FAILURE) |
2543 | 0 | return (MS_FAILURE); |
2544 | | |
2545 | 0 | if (tSHP->sTileProj.numargs > 0) { |
2546 | 0 | msProjectRect(&(layer->projection), &(tSHP->sTileProj), |
2547 | 0 | &rectTile); |
2548 | 0 | } |
2549 | |
|
2550 | 0 | status = |
2551 | 0 | msShapefileWhichShapes(tSHP->shpfile, rectTile, layer->debug); |
2552 | 0 | if (status == MS_DONE) { |
2553 | | /* Close and continue to next tile */ |
2554 | 0 | msShapefileClose(tSHP->shpfile); |
2555 | 0 | continue; |
2556 | 0 | } else if (status != MS_SUCCESS) { |
2557 | 0 | msShapefileClose(tSHP->shpfile); |
2558 | 0 | tSHP->tileshpfile->lastshape = -1; |
2559 | 0 | return (MS_FAILURE); |
2560 | 0 | } |
2561 | | |
2562 | 0 | tSHP->tileshpfile->lastshape = i; |
2563 | 0 | break; |
2564 | 0 | } |
2565 | 0 | } /* end for loop */ |
2566 | | |
2567 | 0 | if (i == tSHP->tileshpfile->numshapes) { |
2568 | 0 | tSHP->tileshpfile->lastshape = -1; |
2569 | 0 | return (MS_DONE); /* no more tiles */ |
2570 | 0 | } else |
2571 | 0 | continue; /* we've got shapes */ |
2572 | 0 | } |
2573 | 0 | } |
2574 | | |
2575 | 0 | tSHP->shpfile->lastshape = i; |
2576 | |
|
2577 | 0 | msSHPReadShape(tSHP->shpfile->hSHP, i, shape); |
2578 | 0 | if (shape->type == MS_SHAPE_NULL) { |
2579 | 0 | msFreeShape(shape); |
2580 | 0 | continue; /* skip NULL shapes */ |
2581 | 0 | } |
2582 | | |
2583 | 0 | if (tSHP->sTileProj.numargs > 0) { |
2584 | 0 | if (tSHP->reprojectorFromTileProjToLayerProj == NULL) { |
2585 | 0 | tSHP->reprojectorFromTileProjToLayerProj = msProjectCreateReprojector( |
2586 | 0 | &(tSHP->sTileProj), &(layer->projection)); |
2587 | 0 | } |
2588 | 0 | if (tSHP->reprojectorFromTileProjToLayerProj) { |
2589 | 0 | msProjectShapeEx(tSHP->reprojectorFromTileProjToLayerProj, shape); |
2590 | 0 | } |
2591 | 0 | } |
2592 | |
|
2593 | 0 | shape->tileindex = tSHP->tileshpfile->lastshape; |
2594 | 0 | shape->numvalues = layer->numitems; |
2595 | 0 | shape->values = msDBFGetValueList(tSHP->shpfile->hDBF, i, layer->iteminfo, |
2596 | 0 | layer->numitems); |
2597 | 0 | if (!shape->values) |
2598 | 0 | shape->numvalues = 0; |
2599 | |
|
2600 | 0 | filter_passed = MS_TRUE; /* By default accept ANY shape */ |
2601 | 0 | if (layer->numitems > 0 && layer->iteminfo) { |
2602 | 0 | filter_passed = msEvalExpression(layer, shape, &(layer->filter), |
2603 | 0 | layer->filteritemindex); |
2604 | 0 | } |
2605 | |
|
2606 | 0 | if (!filter_passed) |
2607 | 0 | msFreeShape(shape); /* free's values as well */ |
2608 | |
|
2609 | 0 | } while ( |
2610 | 0 | !filter_passed); /* Loop until both spatial and attribute filters match */ |
2611 | | |
2612 | 0 | return (MS_SUCCESS); |
2613 | 0 | } |
2614 | | |
2615 | | static int msTiledSHPGetShape(layerObj *layer, shapeObj *shape, |
2616 | 0 | resultObj *record) { |
2617 | 0 | const char *filename; |
2618 | 0 | char tilename[MS_MAXPATHLEN], szPath[MS_MAXPATHLEN]; |
2619 | |
|
2620 | 0 | msTiledSHPLayerInfo *tSHP = NULL; |
2621 | 0 | char tiFileAbsDir[MS_MAXPATHLEN]; |
2622 | |
|
2623 | 0 | long shapeindex = record->shapeindex; |
2624 | 0 | int tileindex = record->tileindex; |
2625 | |
|
2626 | 0 | if (msCheckParentPointer(layer->map, "map") == MS_FAILURE) |
2627 | 0 | return MS_FAILURE; |
2628 | | |
2629 | 0 | tSHP = layer->layerinfo; |
2630 | 0 | if (!tSHP) { |
2631 | 0 | msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", |
2632 | 0 | "msTiledSHPGetShape()"); |
2633 | 0 | return (MS_FAILURE); |
2634 | 0 | } |
2635 | | |
2636 | 0 | msTileIndexAbsoluteDir(tiFileAbsDir, layer); |
2637 | |
|
2638 | 0 | if ((tileindex < 0) || (tileindex >= tSHP->tileshpfile->numshapes)) |
2639 | 0 | return (MS_FAILURE); /* invalid tile id */ |
2640 | | |
2641 | 0 | if (tileindex != |
2642 | 0 | tSHP->tileshpfile->lastshape) { /* correct tile is not currently open so |
2643 | | open the correct tile */ |
2644 | 0 | msShapefileClose(tSHP->shpfile); /* close current tile */ |
2645 | |
|
2646 | 0 | filename = |
2647 | 0 | msTiledSHPLoadEntry(layer, tileindex, tilename, sizeof(tilename)); |
2648 | | |
2649 | | /* open the shapefile, since a specific tile was request an error should be |
2650 | | * generated if that tile does not exist */ |
2651 | 0 | if (strlen(filename) == 0) |
2652 | 0 | return (MS_FAILURE); |
2653 | 0 | if (msShapefileOpen( |
2654 | 0 | tSHP->shpfile, "rb", |
2655 | 0 | msBuildPath3(szPath, tiFileAbsDir, layer->map->shapepath, filename), |
2656 | 0 | MS_TRUE) == -1) { |
2657 | 0 | if (msShapefileOpen(tSHP->shpfile, "rb", |
2658 | 0 | msBuildPath3(szPath, layer->map->mappath, |
2659 | 0 | layer->map->shapepath, filename), |
2660 | 0 | MS_TRUE) == -1) { |
2661 | 0 | if (msShapefileOpen(tSHP->shpfile, "rb", |
2662 | 0 | msBuildPath(szPath, layer->map->mappath, filename), |
2663 | 0 | MS_TRUE) == -1) { |
2664 | 0 | return (MS_FAILURE); |
2665 | 0 | } |
2666 | 0 | } |
2667 | 0 | } |
2668 | 0 | } |
2669 | | |
2670 | 0 | if ((shapeindex < 0) || (shapeindex >= tSHP->shpfile->numshapes)) |
2671 | 0 | return (MS_FAILURE); |
2672 | | |
2673 | 0 | msSHPReadShape(tSHP->shpfile->hSHP, shapeindex, shape); |
2674 | 0 | tSHP->shpfile->lastshape = shapeindex; |
2675 | 0 | tSHP->tileshpfile->lastshape = tileindex; |
2676 | |
|
2677 | 0 | if (tSHP->sTileProj.numargs > 0) { |
2678 | 0 | if (tSHP->reprojectorFromTileProjToLayerProj == NULL) { |
2679 | 0 | tSHP->reprojectorFromTileProjToLayerProj = |
2680 | 0 | msProjectCreateReprojector(&(tSHP->sTileProj), &(layer->projection)); |
2681 | 0 | } |
2682 | 0 | if (tSHP->reprojectorFromTileProjToLayerProj) { |
2683 | 0 | msProjectShapeEx(tSHP->reprojectorFromTileProjToLayerProj, shape); |
2684 | 0 | } |
2685 | 0 | } |
2686 | |
|
2687 | 0 | if (layer->numitems > 0 && layer->iteminfo) { |
2688 | 0 | shape->numvalues = layer->numitems; |
2689 | 0 | shape->values = msDBFGetValueList(tSHP->shpfile->hDBF, shapeindex, |
2690 | 0 | layer->iteminfo, layer->numitems); |
2691 | 0 | if (!shape->values) |
2692 | 0 | return (MS_FAILURE); |
2693 | 0 | } |
2694 | | |
2695 | 0 | shape->tileindex = tileindex; |
2696 | |
|
2697 | 0 | return (MS_SUCCESS); |
2698 | 0 | } |
2699 | | |
2700 | 0 | static void msTiledSHPClose(layerObj *layer) { |
2701 | 0 | msTiledSHPLayerInfo *tSHP = NULL; |
2702 | |
|
2703 | 0 | tSHP = layer->layerinfo; |
2704 | 0 | if (tSHP) { |
2705 | 0 | msShapefileClose(tSHP->shpfile); |
2706 | 0 | free(tSHP->shpfile); |
2707 | |
|
2708 | 0 | if (tSHP->tilelayerindex != -1) { |
2709 | 0 | layerObj *tlp; |
2710 | 0 | if (msCheckParentPointer(layer->map, "map") == MS_FAILURE) |
2711 | 0 | return; |
2712 | 0 | tlp = (GET_LAYER(layer->map, tSHP->tilelayerindex)); |
2713 | 0 | msLayerClose(tlp); |
2714 | 0 | } else { |
2715 | 0 | msShapefileClose(tSHP->tileshpfile); |
2716 | 0 | free(tSHP->tileshpfile); |
2717 | 0 | } |
2718 | | |
2719 | 0 | msProjectDestroyReprojector(tSHP->reprojectorFromTileProjToLayerProj); |
2720 | |
|
2721 | 0 | msFreeProjection(&(tSHP->sTileProj)); |
2722 | |
|
2723 | 0 | free(tSHP); |
2724 | 0 | } |
2725 | 0 | layer->layerinfo = NULL; |
2726 | 0 | } |
2727 | | /************************************************************************/ |
2728 | | /* msTiledSHPClose() */ |
2729 | | /* Overloaded version of msTiledSHPClose for virtual table architecture */ |
2730 | | /************************************************************************/ |
2731 | 0 | int msTiledSHPCloseVT(layerObj *layer) { |
2732 | 0 | msTiledSHPClose(layer); |
2733 | 0 | return MS_SUCCESS; |
2734 | 0 | } |
2735 | | |
2736 | 0 | void msTiledSHPLayerFreeItemInfo(layerObj *layer) { |
2737 | 0 | if (layer->iteminfo) { |
2738 | 0 | free(layer->iteminfo); |
2739 | 0 | layer->iteminfo = NULL; |
2740 | 0 | } |
2741 | 0 | } |
2742 | | |
2743 | 0 | int msTiledSHPLayerInitItemInfo(layerObj *layer) { |
2744 | 0 | msTiledSHPLayerInfo *tSHP = NULL; |
2745 | |
|
2746 | 0 | tSHP = layer->layerinfo; |
2747 | 0 | if (!tSHP) { |
2748 | 0 | msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", |
2749 | 0 | "msTiledSHPLayerInitItemInfo()"); |
2750 | 0 | return MS_FAILURE; |
2751 | 0 | } |
2752 | | |
2753 | 0 | msTiledSHPLayerFreeItemInfo(layer); |
2754 | 0 | layer->iteminfo = (int *)msDBFGetItemIndexes(tSHP->shpfile->hDBF, |
2755 | 0 | layer->items, layer->numitems); |
2756 | 0 | if (!layer->iteminfo) |
2757 | 0 | return (MS_FAILURE); |
2758 | | |
2759 | 0 | return MS_SUCCESS; |
2760 | 0 | } |
2761 | | |
2762 | 0 | static void msSHPPassThroughFieldDefinitions(layerObj *layer, DBFHandle hDBF) { |
2763 | 0 | int numitems, i; |
2764 | |
|
2765 | 0 | numitems = msDBFGetFieldCount(hDBF); |
2766 | |
|
2767 | 0 | for (i = 0; i < numitems; i++) { |
2768 | 0 | char item[16]; |
2769 | 0 | int nWidth = 0, nPrecision = 0; |
2770 | 0 | char gml_width[32], gml_precision[32]; |
2771 | 0 | DBFFieldType eType; |
2772 | 0 | const char *gml_type = NULL; |
2773 | |
|
2774 | 0 | eType = msDBFGetFieldInfo(hDBF, i, item, &nWidth, &nPrecision); |
2775 | |
|
2776 | 0 | gml_width[0] = '\0'; |
2777 | 0 | gml_precision[0] = '\0'; |
2778 | |
|
2779 | 0 | switch (eType) { |
2780 | 0 | case FTInteger: |
2781 | 0 | gml_type = "Integer"; |
2782 | 0 | sprintf(gml_width, "%d", nWidth); |
2783 | 0 | break; |
2784 | | |
2785 | 0 | case FTDouble: |
2786 | 0 | gml_type = "Real"; |
2787 | 0 | sprintf(gml_width, "%d", nWidth); |
2788 | 0 | sprintf(gml_precision, "%d", nPrecision); |
2789 | 0 | break; |
2790 | | |
2791 | 0 | case FTString: |
2792 | 0 | default: |
2793 | 0 | gml_type = "Character"; |
2794 | 0 | sprintf(gml_width, "%d", nWidth); |
2795 | 0 | break; |
2796 | 0 | } |
2797 | | |
2798 | 0 | msUpdateGMLFieldMetadata(layer, item, gml_type, gml_width, gml_precision, |
2799 | 0 | 0); |
2800 | 0 | } |
2801 | 0 | } |
2802 | | |
2803 | 0 | int msTiledSHPLayerGetItems(layerObj *layer) { |
2804 | 0 | msTiledSHPLayerInfo *tSHP = NULL; |
2805 | 0 | const char *value; |
2806 | |
|
2807 | 0 | tSHP = layer->layerinfo; |
2808 | 0 | if (!tSHP) { |
2809 | 0 | msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", |
2810 | 0 | "msTiledSHPLayerGetItems()"); |
2811 | 0 | return MS_FAILURE; |
2812 | 0 | } |
2813 | | |
2814 | 0 | layer->numitems = msDBFGetFieldCount(tSHP->shpfile->hDBF); |
2815 | 0 | layer->items = msDBFGetItems(tSHP->shpfile->hDBF); |
2816 | 0 | if (!layer->items) |
2817 | 0 | return MS_FAILURE; |
2818 | | |
2819 | | /* -------------------------------------------------------------------- */ |
2820 | | /* consider populating the field definitions in metadata. */ |
2821 | | /* -------------------------------------------------------------------- */ |
2822 | 0 | if ((value = msOWSLookupMetadata(&(layer->metadata), "G", "types")) != NULL && |
2823 | 0 | strcasecmp(value, "auto") == 0) |
2824 | 0 | msSHPPassThroughFieldDefinitions(layer, tSHP->shpfile->hDBF); |
2825 | |
|
2826 | 0 | return msTiledSHPLayerInitItemInfo(layer); |
2827 | 0 | } |
2828 | | |
2829 | 0 | int msTiledSHPLayerGetExtent(layerObj *layer, rectObj *extent) { |
2830 | 0 | msTiledSHPLayerInfo *tSHP = NULL; |
2831 | |
|
2832 | 0 | tSHP = layer->layerinfo; |
2833 | 0 | if (!tSHP) { |
2834 | 0 | msSetError(MS_SHPERR, "Tiled shapefile layer has not been opened.", |
2835 | 0 | "msTiledSHPLayerGetExtent()"); |
2836 | 0 | return MS_FAILURE; |
2837 | 0 | } |
2838 | | |
2839 | 0 | *extent = tSHP->tileshpfile->bounds; |
2840 | 0 | return MS_SUCCESS; |
2841 | 0 | } |
2842 | | |
2843 | 0 | int msTiledSHPLayerIsOpen(layerObj *layer) { |
2844 | 0 | if (layer->layerinfo) |
2845 | 0 | return MS_TRUE; |
2846 | 0 | else |
2847 | 0 | return MS_FALSE; |
2848 | 0 | } |
2849 | | |
2850 | 0 | int msTiledSHPLayerSupportsCommonFilters(layerObj *layer) { |
2851 | 0 | (void)layer; |
2852 | 0 | return MS_TRUE; |
2853 | 0 | } |
2854 | | |
2855 | 0 | int msTiledSHPLayerInitializeVirtualTable(layerObj *layer) { |
2856 | 0 | assert(layer != NULL); |
2857 | 0 | assert(layer->vtable != NULL); |
2858 | | |
2859 | 0 | layer->vtable->LayerSupportsCommonFilters = |
2860 | 0 | msTiledSHPLayerSupportsCommonFilters; |
2861 | 0 | layer->vtable->LayerInitItemInfo = msTiledSHPLayerInitItemInfo; |
2862 | 0 | layer->vtable->LayerFreeItemInfo = msTiledSHPLayerFreeItemInfo; |
2863 | 0 | layer->vtable->LayerOpen = msTiledSHPOpenFile; |
2864 | 0 | layer->vtable->LayerIsOpen = msTiledSHPLayerIsOpen; |
2865 | 0 | layer->vtable->LayerWhichShapes = msTiledSHPWhichShapes; |
2866 | 0 | layer->vtable->LayerNextShape = msTiledSHPNextShape; |
2867 | | /* no special version, use ...GetShape() */ |
2868 | | /* layer->vtable->LayerResultsGetShape = msTiledSHPGetShape; */ |
2869 | 0 | layer->vtable->LayerGetShape = msTiledSHPGetShape; |
2870 | 0 | layer->vtable->LayerClose = msTiledSHPCloseVT; |
2871 | 0 | layer->vtable->LayerGetItems = msTiledSHPLayerGetItems; |
2872 | 0 | layer->vtable->LayerGetExtent = msTiledSHPLayerGetExtent; |
2873 | | /* layer->vtable->LayerApplyFilterToLayer, use default */ |
2874 | | /* layer->vtable->LayerGetAutoStyle, use default */ |
2875 | 0 | /* layer->vtable->LayerCloseConnection, use default */; |
2876 | 0 | layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter; |
2877 | | /* layer->vtable->LayerCreateItems, use default */ |
2878 | | /* layer->vtable->LayerGetNumFeatures, use default */ |
2879 | | /* layer->vtable->LayerGetAutoProjection, use default*/ |
2880 | |
|
2881 | 0 | return MS_SUCCESS; |
2882 | 0 | } |
2883 | | |
2884 | | /* SHAPEFILE Layer virtual table functions */ |
2885 | | |
2886 | 0 | void msSHPLayerFreeItemInfo(layerObj *layer) { |
2887 | 0 | if (layer->iteminfo) { |
2888 | 0 | free(layer->iteminfo); |
2889 | 0 | layer->iteminfo = NULL; |
2890 | 0 | } |
2891 | 0 | } |
2892 | | |
2893 | 0 | int msSHPLayerInitItemInfo(layerObj *layer) { |
2894 | 0 | shapefileObj *shpfile = layer->layerinfo; |
2895 | 0 | if (!shpfile) { |
2896 | 0 | msSetError(MS_SHPERR, "Shapefile layer has not been opened.", |
2897 | 0 | "msSHPLayerInitItemInfo()"); |
2898 | 0 | return MS_FAILURE; |
2899 | 0 | } |
2900 | | |
2901 | | /* iteminfo needs to be a bit more complex, a list of indexes plus the length |
2902 | | * of the list */ |
2903 | 0 | msSHPLayerFreeItemInfo(layer); |
2904 | 0 | layer->iteminfo = |
2905 | 0 | (int *)msDBFGetItemIndexes(shpfile->hDBF, layer->items, layer->numitems); |
2906 | 0 | if (!layer->iteminfo) { |
2907 | 0 | return MS_FAILURE; |
2908 | 0 | } |
2909 | | |
2910 | 0 | return MS_SUCCESS; |
2911 | 0 | } |
2912 | | |
2913 | 0 | int msSHPLayerOpen(layerObj *layer) { |
2914 | 0 | char szPath[MS_MAXPATHLEN]; |
2915 | 0 | shapefileObj *shpfile; |
2916 | |
|
2917 | 0 | if (layer->layerinfo) |
2918 | 0 | return MS_SUCCESS; /* layer already open */ |
2919 | | |
2920 | 0 | if (msCheckParentPointer(layer->map, "map") == MS_FAILURE) |
2921 | 0 | return MS_FAILURE; |
2922 | | |
2923 | | /* allocate space for a shapefileObj using layer->layerinfo */ |
2924 | 0 | shpfile = (shapefileObj *)malloc(sizeof(shapefileObj)); |
2925 | 0 | MS_CHECK_ALLOC(shpfile, sizeof(shapefileObj), MS_FAILURE); |
2926 | |
|
2927 | 0 | layer->layerinfo = shpfile; |
2928 | |
|
2929 | 0 | if (msShapefileOpen(shpfile, "rb", |
2930 | 0 | msBuildPath3(szPath, layer->map->mappath, |
2931 | 0 | layer->map->shapepath, layer->data), |
2932 | 0 | MS_TRUE) == -1) { |
2933 | 0 | if (msShapefileOpen(shpfile, "rb", |
2934 | 0 | msBuildPath(szPath, layer->map->mappath, layer->data), |
2935 | 0 | MS_TRUE) == -1) { |
2936 | 0 | layer->layerinfo = NULL; |
2937 | 0 | free(shpfile); |
2938 | 0 | return MS_FAILURE; |
2939 | 0 | } |
2940 | 0 | } |
2941 | | |
2942 | | /* Update layer encoding if encoding is defined in CPG file */ |
2943 | 0 | if (!layer->encoding && shpfile->hDBF->pszEncoding) { |
2944 | 0 | layer->encoding = msStrdup(shpfile->hDBF->pszEncoding); |
2945 | 0 | } |
2946 | |
|
2947 | 0 | if (layer->projection.numargs > 0 && |
2948 | 0 | EQUAL(layer->projection.args[0], "auto")) { |
2949 | 0 | const char *pszPRJFilename = CPLResetExtension(szPath, "prj"); |
2950 | 0 | int bOK = MS_FALSE; |
2951 | 0 | VSILFILE *fp = VSIFOpenL(pszPRJFilename, "rb"); |
2952 | 0 | if (fp != NULL) { |
2953 | 0 | char szPRJ[2048]; |
2954 | 0 | OGRSpatialReferenceH hSRS; |
2955 | 0 | int nRead; |
2956 | |
|
2957 | 0 | nRead = (int)VSIFReadL(szPRJ, 1, sizeof(szPRJ) - 1, fp); |
2958 | 0 | szPRJ[nRead] = '\0'; |
2959 | 0 | hSRS = OSRNewSpatialReference(szPRJ); |
2960 | 0 | if (hSRS != NULL) { |
2961 | 0 | if (OSRMorphFromESRI(hSRS) == OGRERR_NONE) { |
2962 | 0 | char *pszWKT = NULL; |
2963 | 0 | if (OSRExportToWkt(hSRS, &pszWKT) == OGRERR_NONE) { |
2964 | 0 | if (msOGCWKT2ProjectionObj(pszWKT, &(layer->projection), |
2965 | 0 | layer->debug) == MS_SUCCESS) { |
2966 | 0 | bOK = MS_TRUE; |
2967 | 0 | } |
2968 | 0 | } |
2969 | 0 | CPLFree(pszWKT); |
2970 | 0 | } |
2971 | 0 | OSRDestroySpatialReference(hSRS); |
2972 | 0 | } |
2973 | 0 | VSIFCloseL(fp); |
2974 | 0 | } |
2975 | |
|
2976 | 0 | if (bOK != MS_TRUE) { |
2977 | 0 | if (layer->debug || layer->map->debug) { |
2978 | 0 | msDebug("Unable to get SRS from shapefile '%s' for layer '%s'.\n", |
2979 | 0 | szPath, layer->name); |
2980 | 0 | } |
2981 | 0 | } |
2982 | 0 | } |
2983 | |
|
2984 | 0 | return MS_SUCCESS; |
2985 | 0 | } |
2986 | | |
2987 | 0 | int msSHPLayerIsOpen(layerObj *layer) { |
2988 | 0 | if (layer->layerinfo) |
2989 | 0 | return MS_TRUE; |
2990 | 0 | else |
2991 | 0 | return MS_FALSE; |
2992 | 0 | } |
2993 | | |
2994 | 0 | int msSHPLayerWhichShapes(layerObj *layer, rectObj rect, int isQuery) { |
2995 | 0 | (void)isQuery; |
2996 | 0 | int status; |
2997 | 0 | shapefileObj *shpfile; |
2998 | |
|
2999 | 0 | shpfile = layer->layerinfo; |
3000 | |
|
3001 | 0 | if (!shpfile) { |
3002 | 0 | msSetError(MS_SHPERR, "Shapefile layer has not been opened.", |
3003 | 0 | "msSHPLayerWhichShapes()"); |
3004 | 0 | return MS_FAILURE; |
3005 | 0 | } |
3006 | | |
3007 | 0 | status = msShapefileWhichShapes(shpfile, rect, layer->debug); |
3008 | 0 | if (status != MS_SUCCESS) { |
3009 | 0 | return status; |
3010 | 0 | } |
3011 | | |
3012 | 0 | return MS_SUCCESS; |
3013 | 0 | } |
3014 | | |
3015 | 0 | int msSHPLayerNextShape(layerObj *layer, shapeObj *shape) { |
3016 | 0 | int i; |
3017 | 0 | shapefileObj *shpfile; |
3018 | |
|
3019 | 0 | shpfile = layer->layerinfo; |
3020 | |
|
3021 | 0 | if (!shpfile) { |
3022 | 0 | msSetError(MS_SHPERR, "Shapefile layer has not been opened.", |
3023 | 0 | "msSHPLayerNextShape()"); |
3024 | 0 | return MS_FAILURE; |
3025 | 0 | } |
3026 | | |
3027 | 0 | if (!shpfile->status) { |
3028 | | /* probably whichShapes didn't overlap */ |
3029 | 0 | return MS_DONE; |
3030 | 0 | } |
3031 | | |
3032 | 0 | i = msGetNextBit(shpfile->status, shpfile->lastshape + 1, shpfile->numshapes); |
3033 | 0 | shpfile->lastshape = i; |
3034 | 0 | if (i == -1) |
3035 | 0 | return (MS_DONE); /* nothing else to read */ |
3036 | | |
3037 | 0 | msSHPReadShape(shpfile->hSHP, i, shape); |
3038 | 0 | if (shape->type == MS_SHAPE_NULL) { |
3039 | 0 | msFreeShape(shape); |
3040 | 0 | return msSHPLayerNextShape(layer, shape); /* skip NULL shapes */ |
3041 | 0 | } |
3042 | 0 | shape->numvalues = layer->numitems; |
3043 | 0 | shape->values = |
3044 | 0 | msDBFGetValueList(shpfile->hDBF, i, layer->iteminfo, layer->numitems); |
3045 | 0 | if (!shape->values) |
3046 | 0 | shape->numvalues = 0; |
3047 | |
|
3048 | 0 | return MS_SUCCESS; |
3049 | 0 | } |
3050 | | |
3051 | 0 | int msSHPLayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record) { |
3052 | 0 | shapefileObj *shpfile; |
3053 | 0 | long shapeindex; |
3054 | |
|
3055 | 0 | shpfile = layer->layerinfo; |
3056 | |
|
3057 | 0 | shapeindex = record->shapeindex; |
3058 | |
|
3059 | 0 | if (!shpfile) { |
3060 | 0 | msSetError(MS_SHPERR, "Shapefile layer has not been opened.", |
3061 | 0 | "msSHPLayerGetShape()"); |
3062 | 0 | return MS_FAILURE; |
3063 | 0 | } |
3064 | | |
3065 | | /* msSHPReadShape *should* return success or failure so we don't have to test |
3066 | | * here */ |
3067 | 0 | if (shapeindex < 0 || shapeindex >= shpfile->numshapes) { |
3068 | 0 | msSetError(MS_MISCERR, "Invalid feature id.", "msSHPLayerGetShape()"); |
3069 | 0 | return MS_FAILURE; |
3070 | 0 | } |
3071 | | |
3072 | 0 | msSHPReadShape(shpfile->hSHP, shapeindex, shape); |
3073 | 0 | if (layer->numitems > 0 && layer->iteminfo) { |
3074 | 0 | shape->numvalues = layer->numitems; |
3075 | 0 | shape->values = msDBFGetValueList(shpfile->hDBF, shapeindex, |
3076 | 0 | layer->iteminfo, layer->numitems); |
3077 | 0 | if (!shape->values) |
3078 | 0 | return MS_FAILURE; |
3079 | 0 | } |
3080 | | |
3081 | 0 | shpfile->lastshape = shapeindex; |
3082 | |
|
3083 | 0 | return MS_SUCCESS; |
3084 | 0 | } |
3085 | | |
3086 | 0 | int msSHPLayerClose(layerObj *layer) { |
3087 | 0 | shapefileObj *shpfile; |
3088 | 0 | shpfile = layer->layerinfo; |
3089 | 0 | if (!shpfile) |
3090 | 0 | return MS_SUCCESS; /* nothing to do */ |
3091 | | |
3092 | 0 | msShapefileClose(shpfile); |
3093 | 0 | free(layer->layerinfo); |
3094 | 0 | layer->layerinfo = NULL; |
3095 | |
|
3096 | 0 | return MS_SUCCESS; |
3097 | 0 | } |
3098 | | |
3099 | 0 | int msSHPLayerGetItems(layerObj *layer) { |
3100 | 0 | shapefileObj *shpfile; |
3101 | 0 | const char *value; |
3102 | |
|
3103 | 0 | shpfile = layer->layerinfo; |
3104 | |
|
3105 | 0 | if (!shpfile) { |
3106 | 0 | msSetError(MS_SHPERR, "Shapefile layer has not been opened.", |
3107 | 0 | "msSHPLayerGetItems()"); |
3108 | 0 | return MS_FAILURE; |
3109 | 0 | } |
3110 | | |
3111 | 0 | layer->numitems = msDBFGetFieldCount(shpfile->hDBF); |
3112 | 0 | layer->items = msDBFGetItems(shpfile->hDBF); |
3113 | 0 | if (layer->numitems == 0) |
3114 | 0 | return MS_SUCCESS; /* No items is a valid case (#3147) */ |
3115 | 0 | if (!layer->items) |
3116 | 0 | return MS_FAILURE; |
3117 | | |
3118 | | /* -------------------------------------------------------------------- */ |
3119 | | /* consider populating the field definitions in metadata. */ |
3120 | | /* -------------------------------------------------------------------- */ |
3121 | 0 | if ((value = msOWSLookupMetadata(&(layer->metadata), "G", "types")) != NULL && |
3122 | 0 | strcasecmp(value, "auto") == 0) |
3123 | 0 | msSHPPassThroughFieldDefinitions(layer, shpfile->hDBF); |
3124 | |
|
3125 | 0 | return msLayerInitItemInfo(layer); |
3126 | 0 | } |
3127 | | |
3128 | 0 | int msSHPLayerGetExtent(layerObj *layer, rectObj *extent) { |
3129 | 0 | *extent = ((shapefileObj *)layer->layerinfo)->bounds; |
3130 | 0 | return MS_SUCCESS; |
3131 | 0 | } |
3132 | | |
3133 | 0 | int msSHPLayerSupportsCommonFilters(layerObj *layer) { |
3134 | 0 | (void)layer; |
3135 | 0 | return MS_TRUE; |
3136 | 0 | } |
3137 | | |
3138 | 0 | int msSHPLayerInitializeVirtualTable(layerObj *layer) { |
3139 | 0 | assert(layer != NULL); |
3140 | 0 | assert(layer->vtable != NULL); |
3141 | | |
3142 | 0 | layer->vtable->LayerSupportsCommonFilters = msSHPLayerSupportsCommonFilters; |
3143 | 0 | layer->vtable->LayerInitItemInfo = msSHPLayerInitItemInfo; |
3144 | 0 | layer->vtable->LayerFreeItemInfo = msSHPLayerFreeItemInfo; |
3145 | 0 | layer->vtable->LayerOpen = msSHPLayerOpen; |
3146 | 0 | layer->vtable->LayerIsOpen = msSHPLayerIsOpen; |
3147 | 0 | layer->vtable->LayerWhichShapes = msSHPLayerWhichShapes; |
3148 | 0 | layer->vtable->LayerNextShape = msSHPLayerNextShape; |
3149 | 0 | layer->vtable->LayerGetShape = msSHPLayerGetShape; |
3150 | | /* layer->vtable->LayerGetShapeCount, use default */ |
3151 | 0 | layer->vtable->LayerClose = msSHPLayerClose; |
3152 | 0 | layer->vtable->LayerGetItems = msSHPLayerGetItems; |
3153 | 0 | layer->vtable->LayerGetExtent = msSHPLayerGetExtent; |
3154 | | /* layer->vtable->LayerGetAutoStyle, use default */ |
3155 | | /* layer->vtable->LayerCloseConnection, use default */ |
3156 | 0 | layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter; |
3157 | | /* layer->vtable->LayerTranslateFilter, use default */ |
3158 | | /* layer->vtable->LayerApplyFilterToLayer, use default */ |
3159 | | /* layer->vtable->LayerCreateItems, use default */ |
3160 | | /* layer->vtable->LayerGetNumFeatures, use default */ |
3161 | |
|
3162 | 0 | return MS_SUCCESS; |
3163 | 0 | } |