Line data Source code
1 : /******************************************************************************
2 : * $Id$
3 : *
4 : * Project: Arc/Info Binary Grid Translator
5 : * Purpose: Grid file access cover API for non-GDAL use.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : * Copyright (c) 2009-2010, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "aigrid.h"
32 :
33 : #ifndef CPL_IGNORE_RET_VAL_INT_defined
34 : #define CPL_IGNORE_RET_VAL_INT_defined
35 :
36 3 : CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused)
37 : {
38 3 : }
39 : #endif
40 :
41 : /************************************************************************/
42 : /* AIGOpen() */
43 : /************************************************************************/
44 :
45 9 : AIGInfo_t *AIGOpen(const char *pszInputName, const char *pszAccess)
46 :
47 : {
48 : AIGInfo_t *psInfo;
49 : char *pszCoverName;
50 :
51 : (void)pszAccess;
52 :
53 : /* -------------------------------------------------------------------- */
54 : /* If the pass name ends in .adf assume a file within the */
55 : /* coverage has been selected, and strip that off the coverage */
56 : /* name. */
57 : /* -------------------------------------------------------------------- */
58 9 : pszCoverName = CPLStrdup(pszInputName);
59 9 : if (EQUAL(pszCoverName + strlen(pszCoverName) - 4, ".adf"))
60 : {
61 : int i;
62 :
63 0 : for (i = (int)strlen(pszCoverName) - 1; i > 0; i--)
64 : {
65 0 : if (pszCoverName[i] == '\\' || pszCoverName[i] == '/')
66 : {
67 0 : pszCoverName[i] = '\0';
68 0 : break;
69 : }
70 : }
71 :
72 0 : if (i == 0)
73 0 : strcpy(pszCoverName, ".");
74 : }
75 :
76 : /* -------------------------------------------------------------------- */
77 : /* Allocate info structure. */
78 : /* -------------------------------------------------------------------- */
79 9 : psInfo = (AIGInfo_t *)CPLCalloc(sizeof(AIGInfo_t), 1);
80 9 : psInfo->bHasWarned = FALSE;
81 9 : psInfo->nFailedOpenings = 0;
82 9 : psInfo->pszCoverName = pszCoverName;
83 :
84 : /* -------------------------------------------------------------------- */
85 : /* Read the header file. */
86 : /* -------------------------------------------------------------------- */
87 9 : if (AIGReadHeader(pszCoverName, psInfo) != CE_None)
88 : {
89 0 : CPLFree(pszCoverName);
90 0 : CPLFree(psInfo);
91 0 : return NULL;
92 : }
93 :
94 : /* -------------------------------------------------------------------- */
95 : /* Read the extents. */
96 : /* -------------------------------------------------------------------- */
97 9 : if (AIGReadBounds(pszCoverName, psInfo) != CE_None)
98 : {
99 0 : AIGClose(psInfo);
100 0 : return NULL;
101 : }
102 :
103 : /* -------------------------------------------------------------------- */
104 : /* Compute the number of pixels and lines, and the number of */
105 : /* tile files. */
106 : /* -------------------------------------------------------------------- */
107 9 : if (psInfo->dfCellSizeX <= 0 || psInfo->dfCellSizeY <= 0)
108 : {
109 0 : CPLError(CE_Failure, CPLE_AppDefined, "Illegal cell size : %f x %f",
110 : psInfo->dfCellSizeX, psInfo->dfCellSizeY);
111 0 : AIGClose(psInfo);
112 0 : return NULL;
113 : }
114 :
115 9 : psInfo->nPixels =
116 9 : (int)((psInfo->dfURX - psInfo->dfLLX + 0.5 * psInfo->dfCellSizeX) /
117 9 : psInfo->dfCellSizeX);
118 9 : psInfo->nLines =
119 9 : (int)((psInfo->dfURY - psInfo->dfLLY + 0.5 * psInfo->dfCellSizeY) /
120 9 : psInfo->dfCellSizeY);
121 :
122 9 : if (psInfo->nPixels <= 0 || psInfo->nLines <= 0)
123 : {
124 0 : CPLError(CE_Failure, CPLE_AppDefined,
125 : "Invalid raster dimensions : %d x %d", psInfo->nPixels,
126 : psInfo->nLines);
127 0 : AIGClose(psInfo);
128 0 : return NULL;
129 : }
130 :
131 9 : if (psInfo->nBlockXSize <= 0 || psInfo->nBlockYSize <= 0 ||
132 9 : psInfo->nBlocksPerRow <= 0 || psInfo->nBlocksPerColumn <= 0 ||
133 9 : psInfo->nBlockXSize > INT_MAX / psInfo->nBlocksPerRow ||
134 9 : psInfo->nBlockYSize > INT_MAX / psInfo->nBlocksPerColumn)
135 : {
136 0 : CPLError(CE_Failure, CPLE_AppDefined,
137 : "Invalid block characteristics: nBlockXSize=%d, "
138 : "nBlockYSize=%d, nBlocksPerRow=%d, nBlocksPerColumn=%d",
139 : psInfo->nBlockXSize, psInfo->nBlockYSize,
140 : psInfo->nBlocksPerRow, psInfo->nBlocksPerColumn);
141 0 : AIGClose(psInfo);
142 0 : return NULL;
143 : }
144 :
145 9 : if (psInfo->nBlocksPerRow > INT_MAX / psInfo->nBlocksPerColumn)
146 : {
147 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too many blocks");
148 0 : AIGClose(psInfo);
149 0 : return NULL;
150 : }
151 :
152 9 : psInfo->nTileXSize = psInfo->nBlockXSize * psInfo->nBlocksPerRow;
153 9 : psInfo->nTileYSize = psInfo->nBlockYSize * psInfo->nBlocksPerColumn;
154 :
155 9 : psInfo->nTilesPerRow = (psInfo->nPixels - 1) / psInfo->nTileXSize + 1;
156 9 : psInfo->nTilesPerColumn = (psInfo->nLines - 1) / psInfo->nTileYSize + 1;
157 :
158 : /* Each tile map to a file and there are only 3 characters in the */
159 : /* filename for the X and Y components. */
160 9 : if (psInfo->nTilesPerRow > 1000 * 1000 / psInfo->nTilesPerColumn)
161 : {
162 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many tiles");
163 0 : psInfo->nTilesPerRow = 0; /* to avoid int32 overflow in AIGClose() */
164 0 : psInfo->nTilesPerColumn = 0;
165 0 : AIGClose(psInfo);
166 0 : return NULL;
167 : }
168 :
169 : /* -------------------------------------------------------------------- */
170 : /* Setup tile infos, but defer reading of tile data. */
171 : /* -------------------------------------------------------------------- */
172 9 : psInfo->pasTileInfo = (AIGTileInfo *)VSI_CALLOC_VERBOSE(
173 : sizeof(AIGTileInfo),
174 : (size_t)psInfo->nTilesPerRow * psInfo->nTilesPerColumn);
175 9 : if (psInfo->pasTileInfo == NULL)
176 : {
177 0 : AIGClose(psInfo);
178 0 : return NULL;
179 : }
180 :
181 : /* -------------------------------------------------------------------- */
182 : /* Read the statistics. */
183 : /* -------------------------------------------------------------------- */
184 9 : if (AIGReadStatistics(pszCoverName, psInfo) != CE_None)
185 : {
186 0 : AIGClose(psInfo);
187 0 : return NULL;
188 : }
189 :
190 9 : return (psInfo);
191 : }
192 :
193 : /************************************************************************/
194 : /* AIGAccessTile() */
195 : /************************************************************************/
196 :
197 4 : CPLErr AIGAccessTile(AIGInfo_t *psInfo, int iTileX, int iTileY)
198 :
199 : {
200 : char szBasename[32];
201 : char *pszFilename;
202 : AIGTileInfo *psTInfo;
203 4 : const size_t nFilenameLen = strlen(psInfo->pszCoverName) + 40;
204 :
205 : /* -------------------------------------------------------------------- */
206 : /* Identify our tile. */
207 : /* -------------------------------------------------------------------- */
208 4 : if (iTileX < 0 || iTileX >= psInfo->nTilesPerRow || iTileY < 0 ||
209 4 : iTileY >= psInfo->nTilesPerColumn)
210 : {
211 0 : CPLAssert(FALSE);
212 : return CE_Failure;
213 : }
214 :
215 4 : psTInfo = psInfo->pasTileInfo + iTileX + iTileY * psInfo->nTilesPerRow;
216 :
217 4 : if (psTInfo->fpGrid != NULL)
218 1 : return psTInfo->panBlockOffset != NULL ? CE_None : CE_Failure;
219 :
220 3 : if (psTInfo->bTriedToLoad)
221 0 : return CE_None;
222 :
223 : #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
224 : /* After a significant number of failed openings, don't even try further */
225 : if (psInfo->nFailedOpenings == 1000)
226 : return CE_None;
227 : #endif
228 :
229 : /* -------------------------------------------------------------------- */
230 : /* Compute the basename. */
231 : /* -------------------------------------------------------------------- */
232 3 : if (iTileY == 0)
233 3 : snprintf(szBasename, sizeof(szBasename), "w%03d001", iTileX + 1);
234 0 : else if (iTileY == 1)
235 0 : snprintf(szBasename, sizeof(szBasename), "w%03d000", iTileX + 1);
236 : else
237 0 : snprintf(szBasename, sizeof(szBasename), "z%03d%03d", iTileX + 1,
238 : iTileY - 1);
239 :
240 : /* -------------------------------------------------------------------- */
241 : /* Open the file w001001.adf file itself. */
242 : /* -------------------------------------------------------------------- */
243 3 : pszFilename = (char *)CPLMalloc(nFilenameLen);
244 3 : snprintf(pszFilename, nFilenameLen, "%s/%s.adf", psInfo->pszCoverName,
245 : szBasename);
246 :
247 3 : psTInfo->fpGrid = AIGLLOpen(pszFilename, "rb");
248 3 : psTInfo->bTriedToLoad = TRUE;
249 :
250 3 : if (psTInfo->fpGrid == NULL)
251 : {
252 0 : psInfo->nFailedOpenings++;
253 0 : if (psInfo->nFailedOpenings < 100)
254 : {
255 0 : CPLError(
256 : CE_Warning, CPLE_OpenFailed,
257 : "Failed to open grid file, assuming region is nodata:\n%s\n",
258 : pszFilename);
259 : }
260 :
261 0 : CPLFree(pszFilename);
262 0 : return CE_Warning;
263 : }
264 :
265 3 : CPLFree(pszFilename);
266 3 : pszFilename = NULL;
267 :
268 : /* -------------------------------------------------------------------- */
269 : /* Read the block index file. */
270 : /* -------------------------------------------------------------------- */
271 3 : return AIGReadBlockIndex(psInfo, psTInfo, szBasename);
272 : }
273 :
274 : /************************************************************************/
275 : /* AIGReadTile() */
276 : /************************************************************************/
277 :
278 4 : CPLErr AIGReadTile(AIGInfo_t *psInfo, int nBlockXOff, int nBlockYOff,
279 : GInt32 *panData)
280 :
281 : {
282 : int nBlockID;
283 : CPLErr eErr;
284 : int iTileX, iTileY;
285 : AIGTileInfo *psTInfo;
286 :
287 : /* -------------------------------------------------------------------- */
288 : /* Compute our tile, and ensure it is accessible (open). Then */
289 : /* reduce block x/y values to be the block within that tile. */
290 : /* -------------------------------------------------------------------- */
291 4 : iTileX = nBlockXOff / psInfo->nBlocksPerRow;
292 4 : iTileY = nBlockYOff / psInfo->nBlocksPerColumn;
293 :
294 4 : eErr = AIGAccessTile(psInfo, iTileX, iTileY);
295 4 : if (eErr == CE_Failure)
296 2 : return eErr;
297 :
298 2 : psTInfo = psInfo->pasTileInfo + iTileX + iTileY * psInfo->nTilesPerRow;
299 :
300 2 : nBlockXOff -= iTileX * psInfo->nBlocksPerRow;
301 2 : nBlockYOff -= iTileY * psInfo->nBlocksPerColumn;
302 :
303 : /* -------------------------------------------------------------------- */
304 : /* Request for tile from a file which does not exist - treat as */
305 : /* all nodata. */
306 : /* -------------------------------------------------------------------- */
307 2 : if (psTInfo->fpGrid == NULL)
308 : {
309 : int i;
310 0 : for (i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i--)
311 0 : panData[i] = ESRI_GRID_NO_DATA;
312 0 : return CE_None;
313 : }
314 :
315 : /* -------------------------------------------------------------------- */
316 : /* validate block id. */
317 : /* -------------------------------------------------------------------- */
318 2 : nBlockID = nBlockXOff + nBlockYOff * psInfo->nBlocksPerRow;
319 2 : if (nBlockID < 0 ||
320 2 : nBlockID >= psInfo->nBlocksPerRow * psInfo->nBlocksPerColumn)
321 : {
322 0 : CPLError(CE_Failure, CPLE_AppDefined, "Illegal block requested.");
323 0 : return CE_Failure;
324 : }
325 :
326 2 : if (nBlockID >= psTInfo->nBlocks)
327 : {
328 : int i;
329 0 : CPLDebug("AIG",
330 : "Request legal block, but from beyond end of block map.\n"
331 : "Assuming all nodata.");
332 0 : for (i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i--)
333 0 : panData[i] = ESRI_GRID_NO_DATA;
334 0 : return CE_None;
335 : }
336 :
337 : /* -------------------------------------------------------------------- */
338 : /* Read block. */
339 : /* -------------------------------------------------------------------- */
340 2 : eErr = AIGReadBlock(psTInfo->fpGrid, psTInfo->panBlockOffset[nBlockID],
341 2 : psTInfo->panBlockSize[nBlockID], psInfo->nBlockXSize,
342 : psInfo->nBlockYSize, panData, psInfo->nCellType,
343 : psInfo->bCompressed);
344 :
345 : /* -------------------------------------------------------------------- */
346 : /* Apply floating point post-processing. */
347 : /* -------------------------------------------------------------------- */
348 2 : if (eErr == CE_None && psInfo->nCellType == AIG_CELLTYPE_FLOAT)
349 : {
350 0 : float *pafData = (float *)panData;
351 0 : int i, nPixels = psInfo->nBlockXSize * psInfo->nBlockYSize;
352 :
353 0 : for (i = 0; i < nPixels; i++)
354 : {
355 0 : panData[i] = (int)pafData[i];
356 : }
357 : }
358 :
359 2 : return (eErr);
360 : }
361 :
362 : /************************************************************************/
363 : /* AIGReadFloatTile() */
364 : /************************************************************************/
365 :
366 0 : CPLErr AIGReadFloatTile(AIGInfo_t *psInfo, int nBlockXOff, int nBlockYOff,
367 : float *pafData)
368 :
369 : {
370 : int nBlockID;
371 : CPLErr eErr;
372 : int iTileX, iTileY;
373 : AIGTileInfo *psTInfo;
374 :
375 : /* -------------------------------------------------------------------- */
376 : /* Compute our tile, and ensure it is accessible (open). Then */
377 : /* reduce block x/y values to be the block within that tile. */
378 : /* -------------------------------------------------------------------- */
379 0 : iTileX = nBlockXOff / psInfo->nBlocksPerRow;
380 0 : iTileY = nBlockYOff / psInfo->nBlocksPerColumn;
381 :
382 0 : eErr = AIGAccessTile(psInfo, iTileX, iTileY);
383 0 : if (eErr == CE_Failure)
384 0 : return eErr;
385 :
386 0 : psTInfo = psInfo->pasTileInfo + iTileX + iTileY * psInfo->nTilesPerRow;
387 :
388 0 : nBlockXOff -= iTileX * psInfo->nBlocksPerRow;
389 0 : nBlockYOff -= iTileY * psInfo->nBlocksPerColumn;
390 :
391 : /* -------------------------------------------------------------------- */
392 : /* Request for tile from a file which does not exist - treat as */
393 : /* all nodata. */
394 : /* -------------------------------------------------------------------- */
395 0 : if (psTInfo->fpGrid == NULL)
396 : {
397 : int i;
398 0 : for (i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i--)
399 0 : pafData[i] = ESRI_GRID_FLOAT_NO_DATA;
400 0 : return CE_None;
401 : }
402 :
403 : /* -------------------------------------------------------------------- */
404 : /* validate block id. */
405 : /* -------------------------------------------------------------------- */
406 0 : nBlockID = nBlockXOff + nBlockYOff * psInfo->nBlocksPerRow;
407 0 : if (nBlockID < 0 ||
408 0 : nBlockID >= psInfo->nBlocksPerRow * psInfo->nBlocksPerColumn)
409 : {
410 0 : CPLError(CE_Failure, CPLE_AppDefined, "Illegal block requested.");
411 0 : return CE_Failure;
412 : }
413 :
414 0 : if (nBlockID >= psTInfo->nBlocks)
415 : {
416 : int i;
417 0 : CPLDebug("AIG",
418 : "Request legal block, but from beyond end of block map.\n"
419 : "Assuming all nodata.");
420 0 : for (i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i--)
421 0 : pafData[i] = ESRI_GRID_FLOAT_NO_DATA;
422 0 : return CE_None;
423 : }
424 :
425 : /* -------------------------------------------------------------------- */
426 : /* Read block. */
427 : /* -------------------------------------------------------------------- */
428 0 : eErr = AIGReadBlock(psTInfo->fpGrid, psTInfo->panBlockOffset[nBlockID],
429 0 : psTInfo->panBlockSize[nBlockID], psInfo->nBlockXSize,
430 : psInfo->nBlockYSize, (GInt32 *)pafData,
431 : psInfo->nCellType, psInfo->bCompressed);
432 :
433 : /* -------------------------------------------------------------------- */
434 : /* Perform integer post processing. */
435 : /* -------------------------------------------------------------------- */
436 0 : if (eErr == CE_None && psInfo->nCellType == AIG_CELLTYPE_INT)
437 : {
438 0 : GUInt32 *panData = (GUInt32 *)pafData;
439 0 : int i, nPixels = psInfo->nBlockXSize * psInfo->nBlockYSize;
440 :
441 0 : for (i = 0; i < nPixels; i++)
442 : {
443 0 : pafData[i] = (float)panData[i];
444 : }
445 : }
446 :
447 0 : return (eErr);
448 : }
449 :
450 : /************************************************************************/
451 : /* AIGClose() */
452 : /************************************************************************/
453 :
454 9 : void AIGClose(AIGInfo_t *psInfo)
455 :
456 : {
457 9 : if (psInfo->pasTileInfo != NULL)
458 : {
459 9 : int nTileCount = psInfo->nTilesPerRow * psInfo->nTilesPerColumn;
460 : int iTile;
461 :
462 18 : for (iTile = 0; iTile < nTileCount; iTile++)
463 : {
464 9 : if (psInfo->pasTileInfo[iTile].fpGrid)
465 : {
466 3 : CPL_IGNORE_RET_VAL_INT(
467 3 : VSIFCloseL(psInfo->pasTileInfo[iTile].fpGrid));
468 :
469 3 : CPLFree(psInfo->pasTileInfo[iTile].panBlockOffset);
470 3 : CPLFree(psInfo->pasTileInfo[iTile].panBlockSize);
471 : }
472 : }
473 : }
474 :
475 9 : CPLFree(psInfo->pasTileInfo);
476 9 : CPLFree(psInfo->pszCoverName);
477 9 : CPLFree(psInfo);
478 9 : }
479 :
480 : /************************************************************************/
481 : /* AIGLLOpen() */
482 : /* */
483 : /* Low level fopen() replacement that will try provided, and */
484 : /* upper cased versions of file names. */
485 : /************************************************************************/
486 :
487 33 : VSILFILE *AIGLLOpen(const char *pszFilename, const char *pszAccess)
488 :
489 : {
490 : VSILFILE *fp;
491 :
492 33 : fp = VSIFOpenL(pszFilename, pszAccess);
493 33 : if (fp == NULL)
494 : {
495 11 : char *pszUCFilename = CPLStrdup(pszFilename);
496 : int i;
497 :
498 11 : for (i = (int)strlen(pszUCFilename) - 1;
499 106 : pszUCFilename[i] != '/' && pszUCFilename[i] != '\\'; i--)
500 : {
501 95 : pszUCFilename[i] = (char)toupper((unsigned char)(pszUCFilename[i]));
502 : }
503 :
504 11 : fp = VSIFOpenL(pszUCFilename, pszAccess);
505 :
506 11 : CPLFree(pszUCFilename);
507 : }
508 :
509 33 : return fp;
510 : }
|