Line data Source code
1 : /******************************************************************************
2 : * $Id$
3 : *
4 : * Project: DTED Translator
5 : * Purpose: Implementation of DTED/CDED access functions.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : * Copyright (c) 2007-2012, 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 "dted_api.h"
32 :
33 : #ifndef AVOID_CPL
34 : #endif
35 :
36 : static int bWarnedTwoComplement = FALSE;
37 :
38 : static void DTEDDetectVariantWithMissingColumns(DTEDInfo *psDInfo);
39 :
40 78 : CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused)
41 : {
42 78 : }
43 :
44 208 : CPL_INLINE static void CPL_IGNORE_RET_VAL_SIZET(CPL_UNUSED size_t unused)
45 : {
46 208 : }
47 :
48 : #define DIGIT_ZERO '0'
49 :
50 : /************************************************************************/
51 : /* DTEDGetField() */
52 : /* */
53 : /* Extract a field as a zero terminated string. Address is */
54 : /* deliberately 1 based so the getfield arguments will be the */
55 : /* same as the numbers in the file format specification. */
56 : /************************************************************************/
57 :
58 660 : static char *DTEDGetField(char szResult[81], const char *pachRecord, int nStart,
59 : int nSize)
60 :
61 : {
62 660 : CPLAssert(nSize < 81);
63 660 : memcpy(szResult, pachRecord + nStart - 1, nSize);
64 660 : szResult[nSize] = '\0';
65 :
66 660 : return szResult;
67 : }
68 :
69 : /************************************************************************/
70 : /* StripLeadingZeros() */
71 : /* */
72 : /* Return a pointer to the first non-zero character in BUF. */
73 : /* BUF must be null terminated. */
74 : /* If buff is all zeros, then it will point to the last non-zero */
75 : /************************************************************************/
76 :
77 396 : static const char *stripLeadingZeros(const char *buf)
78 : {
79 396 : const char *ptr = buf;
80 :
81 : /* Go until we run out of characters or hit something non-zero */
82 :
83 802 : while (*ptr == DIGIT_ZERO && *(ptr + 1) != '\0')
84 : {
85 406 : ptr++;
86 : }
87 :
88 396 : return ptr;
89 : }
90 :
91 : /************************************************************************/
92 : /* DTEDOpen() */
93 : /************************************************************************/
94 :
95 15 : DTEDInfo *DTEDOpen(const char *pszFilename, const char *pszAccess,
96 : int bTestOpen)
97 :
98 : {
99 : VSILFILE *fp;
100 :
101 : /* -------------------------------------------------------------------- */
102 : /* Open the physical file. */
103 : /* -------------------------------------------------------------------- */
104 15 : if (EQUAL(pszAccess, "r") || EQUAL(pszAccess, "rb"))
105 0 : pszAccess = "rb";
106 : else
107 15 : pszAccess = "r+b";
108 :
109 15 : fp = VSIFOpenL(pszFilename, pszAccess);
110 :
111 15 : if (fp == NULL)
112 : {
113 0 : if (!bTestOpen)
114 : {
115 : #ifndef AVOID_CPL
116 0 : CPLError(CE_Failure, CPLE_OpenFailed,
117 : #else
118 : fprintf(stderr,
119 : #endif
120 : "Failed to open file %s.", pszFilename);
121 : }
122 :
123 0 : return NULL;
124 : }
125 :
126 15 : return DTEDOpenEx(fp, pszFilename, pszAccess, bTestOpen);
127 : }
128 :
129 : /************************************************************************/
130 : /* DTEDOpenEx() */
131 : /************************************************************************/
132 :
133 66 : DTEDInfo *DTEDOpenEx(VSILFILE *fp, const char *pszFilename,
134 : const char *pszAccess, int bTestOpen)
135 :
136 : {
137 : char achRecord[DTED_UHL_SIZE];
138 66 : DTEDInfo *psDInfo = NULL;
139 : double dfLLOriginX, dfLLOriginY;
140 66 : int deg = 0;
141 66 : int min = 0;
142 66 : int sec = 0;
143 66 : int bSwapLatLong = FALSE;
144 : char szResult[81];
145 : int bIsWeirdDTED;
146 : char chHemisphere;
147 :
148 : /* -------------------------------------------------------------------- */
149 : /* Read, trying to find the UHL record. Skip VOL or HDR */
150 : /* records if they are encountered. */
151 : /* -------------------------------------------------------------------- */
152 : do
153 : {
154 67 : if (VSIFReadL(achRecord, 1, DTED_UHL_SIZE, fp) != DTED_UHL_SIZE)
155 : {
156 0 : if (!bTestOpen)
157 : {
158 : #ifndef AVOID_CPL
159 0 : CPLError(CE_Failure, CPLE_OpenFailed,
160 : #else
161 : fprintf(stderr,
162 : #endif
163 : "Unable to read header, %s is not DTED.", pszFilename);
164 : }
165 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
166 0 : return NULL;
167 : }
168 :
169 134 : } while (STARTS_WITH_CI(achRecord, "VOL") ||
170 67 : STARTS_WITH_CI(achRecord, "HDR"));
171 :
172 66 : if (!STARTS_WITH_CI(achRecord, "UHL"))
173 : {
174 0 : if (!bTestOpen)
175 : {
176 : #ifndef AVOID_CPL
177 0 : CPLError(CE_Failure, CPLE_OpenFailed,
178 : #else
179 : fprintf(stderr,
180 : #endif
181 : "No UHL record. %s is not a DTED file.", pszFilename);
182 : }
183 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
184 0 : return NULL;
185 : }
186 :
187 : /* -------------------------------------------------------------------- */
188 : /* Create and initialize the DTEDInfo structure. */
189 : /* -------------------------------------------------------------------- */
190 66 : psDInfo = (DTEDInfo *)CPLCalloc(1, sizeof(DTEDInfo));
191 :
192 66 : psDInfo->fp = fp;
193 :
194 66 : psDInfo->bUpdate = EQUAL(pszAccess, "r+b");
195 66 : psDInfo->bRewriteHeaders = FALSE;
196 :
197 66 : psDInfo->nUHLOffset = (int)VSIFTellL(fp) - DTED_UHL_SIZE;
198 66 : psDInfo->pachUHLRecord = (char *)CPLMalloc(DTED_UHL_SIZE);
199 66 : memcpy(psDInfo->pachUHLRecord, achRecord, DTED_UHL_SIZE);
200 :
201 66 : psDInfo->nDSIOffset = (int)VSIFTellL(fp);
202 66 : psDInfo->pachDSIRecord = (char *)CPLMalloc(DTED_DSI_SIZE);
203 66 : CPL_IGNORE_RET_VAL_SIZET(
204 66 : VSIFReadL(psDInfo->pachDSIRecord, 1, DTED_DSI_SIZE, fp));
205 :
206 66 : psDInfo->nACCOffset = (int)VSIFTellL(fp);
207 66 : psDInfo->pachACCRecord = (char *)CPLMalloc(DTED_ACC_SIZE);
208 66 : CPL_IGNORE_RET_VAL_SIZET(
209 66 : VSIFReadL(psDInfo->pachACCRecord, 1, DTED_ACC_SIZE, fp));
210 :
211 66 : if (!STARTS_WITH_CI(psDInfo->pachDSIRecord, "DSI") ||
212 66 : !STARTS_WITH_CI(psDInfo->pachACCRecord, "ACC"))
213 : {
214 : #ifndef AVOID_CPL
215 0 : CPLError(CE_Failure, CPLE_OpenFailed,
216 : #else
217 : fprintf(stderr,
218 : #endif
219 : "DSI or ACC record missing. DTED access to\n%s failed.",
220 : pszFilename);
221 :
222 0 : DTEDClose(psDInfo);
223 0 : return NULL;
224 : }
225 :
226 66 : psDInfo->nDataOffset = (int)VSIFTellL(fp);
227 :
228 : /* DTED3 file from http://www.falconview.org/trac/FalconView/downloads/20 */
229 : /* (co_elevation.zip) has really weird offsets that don't comply with the
230 : * 89020B specification */
231 66 : bIsWeirdDTED = achRecord[4] == ' ';
232 :
233 : /* -------------------------------------------------------------------- */
234 : /* Parse out position information. Note that we are extracting */
235 : /* the top left corner of the top left pixel area, not the */
236 : /* center of the area. */
237 : /* -------------------------------------------------------------------- */
238 66 : if (!bIsWeirdDTED)
239 : {
240 66 : psDInfo->dfPixelSizeX =
241 66 : atoi(DTEDGetField(szResult, achRecord, 21, 4)) / 36000.0;
242 :
243 66 : psDInfo->dfPixelSizeY =
244 66 : atoi(DTEDGetField(szResult, achRecord, 25, 4)) / 36000.0;
245 :
246 66 : psDInfo->nXSize = atoi(DTEDGetField(szResult, achRecord, 48, 4));
247 66 : psDInfo->nYSize = atoi(DTEDGetField(szResult, achRecord, 52, 4));
248 : }
249 : else
250 : {
251 0 : psDInfo->dfPixelSizeX =
252 0 : atoi(DTEDGetField(szResult, achRecord, 41, 4)) / 36000.0;
253 :
254 0 : psDInfo->dfPixelSizeY =
255 0 : atoi(DTEDGetField(szResult, achRecord, 45, 4)) / 36000.0;
256 :
257 0 : psDInfo->nXSize =
258 0 : atoi(DTEDGetField(szResult, psDInfo->pachDSIRecord, 563, 4));
259 0 : psDInfo->nYSize =
260 0 : atoi(DTEDGetField(szResult, psDInfo->pachDSIRecord, 567, 4));
261 : }
262 :
263 66 : if (psDInfo->nXSize <= 0 || psDInfo->nYSize <= 0)
264 : {
265 : #ifndef AVOID_CPL
266 0 : CPLError(CE_Failure, CPLE_OpenFailed,
267 : #else
268 : fprintf(stderr,
269 : #endif
270 : "Invalid dimensions : %d x %d. DTED access to\n%s failed.",
271 : psDInfo->nXSize, psDInfo->nYSize, pszFilename);
272 :
273 0 : DTEDClose(psDInfo);
274 0 : return NULL;
275 : }
276 :
277 : /* create a scope so I don't need to declare these up top */
278 66 : if (!bIsWeirdDTED)
279 : {
280 66 : deg = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 5, 3)));
281 66 : min = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 8, 2)));
282 66 : sec = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 10, 2)));
283 66 : chHemisphere = achRecord[11];
284 : }
285 : else
286 : {
287 0 : deg = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 9, 3)));
288 0 : min = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 12, 2)));
289 0 : sec = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 14, 2)));
290 0 : chHemisphere = achRecord[15];
291 : }
292 :
293 : /* NOTE : The first version of MIL-D-89020 was buggy.
294 : The latitude and longitude of the LL corner of the UHF record was
295 : inverted. This was fixed in MIL-D-89020 Amendment 1, but some products
296 : may be affected. We detect this situation by looking at N/S in the
297 : longitude field and E/W in the latitude one.
298 : */
299 :
300 66 : dfLLOriginX = deg + min / 60.0 + sec / 3600.0;
301 66 : if (chHemisphere == 'W')
302 54 : dfLLOriginX *= -1;
303 12 : else if (chHemisphere == 'N')
304 1 : bSwapLatLong = TRUE;
305 11 : else if (chHemisphere == 'S')
306 : {
307 0 : dfLLOriginX *= -1;
308 0 : bSwapLatLong = TRUE;
309 : }
310 :
311 66 : if (!bIsWeirdDTED)
312 : {
313 66 : deg = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 13, 3)));
314 66 : min = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 16, 2)));
315 66 : sec = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 18, 2)));
316 66 : chHemisphere = achRecord[19];
317 : }
318 : else
319 : {
320 0 : deg = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 25, 3)));
321 0 : min = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 28, 2)));
322 0 : sec = atoi(stripLeadingZeros(DTEDGetField(szResult, achRecord, 30, 2)));
323 0 : chHemisphere = achRecord[31];
324 : }
325 :
326 66 : dfLLOriginY = deg + min / 60.0 + sec / 3600.0;
327 66 : if (chHemisphere == 'S' || (bSwapLatLong && chHemisphere == 'W'))
328 1 : dfLLOriginY *= -1;
329 :
330 66 : if (bSwapLatLong)
331 : {
332 1 : double dfTmp = dfLLOriginX;
333 1 : dfLLOriginX = dfLLOriginY;
334 1 : dfLLOriginY = dfTmp;
335 : }
336 :
337 66 : psDInfo->dfULCornerX = dfLLOriginX - 0.5 * psDInfo->dfPixelSizeX;
338 66 : psDInfo->dfULCornerY = dfLLOriginY - 0.5 * psDInfo->dfPixelSizeY +
339 66 : psDInfo->nYSize * psDInfo->dfPixelSizeY;
340 :
341 66 : DTEDDetectVariantWithMissingColumns(psDInfo);
342 :
343 66 : return psDInfo;
344 : }
345 :
346 : /************************************************************************/
347 : /* DTEDDetectVariantWithMissingColumns() */
348 : /************************************************************************/
349 :
350 66 : static void DTEDDetectVariantWithMissingColumns(DTEDInfo *psDInfo)
351 : {
352 : /* -------------------------------------------------------------------- */
353 : /* Some DTED files have only a subset of all possible columns. */
354 : /* They can declare for example 3601 columns, but in the file, */
355 : /* there are just columns 100->500. Detect that situation. */
356 : /* -------------------------------------------------------------------- */
357 :
358 : GByte pabyRecordHeader[8];
359 : int nFirstDataBlockCount, nFirstLongitudeCount;
360 : int nLastDataBlockCount, nLastLongitudeCount;
361 : int nSize;
362 66 : int nColByteSize = 12 + psDInfo->nYSize * 2;
363 :
364 132 : if (VSIFSeekL(psDInfo->fp, psDInfo->nDataOffset, SEEK_SET) < 0 ||
365 66 : VSIFReadL(pabyRecordHeader, 1, 8, psDInfo->fp) != 8 ||
366 64 : pabyRecordHeader[0] != 0252)
367 : {
368 2 : CPLDebug("DTED", "Cannot find signature of first column");
369 62 : return;
370 : }
371 :
372 64 : nFirstDataBlockCount = (pabyRecordHeader[2] << 8) | pabyRecordHeader[3];
373 64 : nFirstLongitudeCount = (pabyRecordHeader[4] << 8) | pabyRecordHeader[5];
374 :
375 64 : CPL_IGNORE_RET_VAL_SIZET(VSIFSeekL(psDInfo->fp, 0, SEEK_END));
376 64 : nSize = (int)VSIFTellL(psDInfo->fp);
377 64 : if (nSize < 12 + psDInfo->nYSize * 2)
378 : {
379 0 : CPLDebug("DTED", "File too short");
380 0 : return;
381 : }
382 :
383 128 : if (VSIFSeekL(psDInfo->fp, nSize - nColByteSize, SEEK_SET) < 0 ||
384 64 : VSIFReadL(pabyRecordHeader, 1, 8, psDInfo->fp) != 8 ||
385 64 : pabyRecordHeader[0] != 0252)
386 : {
387 0 : CPLDebug("DTED", "Cannot find signature of last column");
388 0 : return;
389 : }
390 :
391 64 : nLastDataBlockCount = (pabyRecordHeader[2] << 8) | pabyRecordHeader[3];
392 64 : nLastLongitudeCount = (pabyRecordHeader[4] << 8) | pabyRecordHeader[5];
393 :
394 64 : if (nFirstDataBlockCount == 0 && nFirstLongitudeCount == 0 &&
395 60 : nLastDataBlockCount == psDInfo->nXSize - 1 &&
396 60 : nLastLongitudeCount == psDInfo->nXSize - 1 &&
397 60 : nSize - psDInfo->nDataOffset == psDInfo->nXSize * nColByteSize)
398 : {
399 : /* This is the most standard form of DTED. Return happily now. */
400 60 : return;
401 : }
402 :
403 : /* Well, we have an odd DTED file at that point */
404 :
405 4 : psDInfo->panMapLogicalColsToOffsets =
406 4 : (int *)CPLMalloc(psDInfo->nXSize * sizeof(int));
407 :
408 4 : if (nFirstDataBlockCount == 0 &&
409 4 : nLastLongitudeCount - nFirstLongitudeCount ==
410 4 : nLastDataBlockCount - nFirstDataBlockCount &&
411 2 : nSize - psDInfo->nDataOffset ==
412 2 : (nLastLongitudeCount - nFirstLongitudeCount + 1) * nColByteSize)
413 2 : {
414 : int i;
415 :
416 : /* Case seen in a real-world file */
417 :
418 2 : CPLDebug("DTED",
419 : "The file only contains data from column %d to column %d.",
420 : nFirstLongitudeCount, nLastLongitudeCount);
421 :
422 244 : for (i = 0; i < psDInfo->nXSize; i++)
423 : {
424 242 : if (i < nFirstLongitudeCount)
425 4 : psDInfo->panMapLogicalColsToOffsets[i] = -1;
426 238 : else if (i <= nLastLongitudeCount)
427 4 : psDInfo->panMapLogicalColsToOffsets[i] =
428 4 : psDInfo->nDataOffset +
429 4 : (i - nFirstLongitudeCount) * nColByteSize;
430 : else
431 234 : psDInfo->panMapLogicalColsToOffsets[i] = -1;
432 : }
433 : }
434 : else
435 : {
436 2 : int nPhysicalCols = (nSize - psDInfo->nDataOffset) / nColByteSize;
437 : int i;
438 :
439 : /* Theoretical case for now... */
440 :
441 2 : CPLDebug("DTED", "There columns appear to be in non sequential order. "
442 : "Scanning the whole file.");
443 :
444 244 : for (i = 0; i < psDInfo->nXSize; i++)
445 : {
446 242 : psDInfo->panMapLogicalColsToOffsets[i] = -1;
447 : }
448 :
449 6 : for (i = 0; i < nPhysicalCols; i++)
450 : {
451 : int nDataBlockCount, nLongitudeCount;
452 :
453 4 : if (VSIFSeekL(psDInfo->fp, psDInfo->nDataOffset + i * nColByteSize,
454 4 : SEEK_SET) < 0 ||
455 4 : VSIFReadL(pabyRecordHeader, 1, 8, psDInfo->fp) != 8 ||
456 4 : pabyRecordHeader[0] != 0252)
457 : {
458 0 : CPLDebug("DTED", "Cannot find signature of physical column %d",
459 : i);
460 0 : return;
461 : }
462 :
463 4 : nDataBlockCount = (pabyRecordHeader[2] << 8) | pabyRecordHeader[3];
464 4 : if (nDataBlockCount != i)
465 : {
466 0 : CPLDebug("DTED",
467 : "Unexpected block count(%d) at physical column %d. "
468 : "Ignoring that and going on...",
469 : nDataBlockCount, i);
470 : }
471 :
472 4 : nLongitudeCount = (pabyRecordHeader[4] << 8) | pabyRecordHeader[5];
473 4 : if (nLongitudeCount < 0 || nLongitudeCount >= psDInfo->nXSize)
474 : {
475 0 : CPLDebug("DTED",
476 : "Invalid longitude count (%d) at physical column %d",
477 : nLongitudeCount, i);
478 0 : return;
479 : }
480 :
481 4 : psDInfo->panMapLogicalColsToOffsets[nLongitudeCount] =
482 4 : psDInfo->nDataOffset + i * nColByteSize;
483 : }
484 : }
485 : }
486 :
487 : /************************************************************************/
488 : /* DTEDReadPoint() */
489 : /* */
490 : /* Read one single sample. The coordinates are given from the */
491 : /* top-left corner of the file (contrary to the internal */
492 : /* organization or a DTED file) */
493 : /************************************************************************/
494 :
495 0 : int DTEDReadPoint(DTEDInfo *psDInfo, int nXOff, int nYOff, GInt16 *panVal)
496 : {
497 : int nOffset;
498 : GByte pabyData[2];
499 :
500 0 : if (nYOff < 0 || nXOff < 0 || nYOff >= psDInfo->nYSize ||
501 0 : nXOff >= psDInfo->nXSize)
502 : {
503 : #ifndef AVOID_CPL
504 0 : CPLError(CE_Failure, CPLE_AppDefined,
505 : #else
506 : fprintf(stderr,
507 : #endif
508 : "Invalid raster coordinates (%d,%d) in DTED file.\n", nXOff,
509 : nYOff);
510 0 : return FALSE;
511 : }
512 :
513 0 : if (psDInfo->panMapLogicalColsToOffsets != NULL)
514 : {
515 0 : nOffset = psDInfo->panMapLogicalColsToOffsets[nXOff];
516 0 : if (nOffset < 0)
517 : {
518 0 : *panVal = DTED_NODATA_VALUE;
519 0 : return TRUE;
520 : }
521 : }
522 : else
523 0 : nOffset = psDInfo->nDataOffset + nXOff * (12 + psDInfo->nYSize * 2);
524 0 : nOffset += 8 + 2 * (psDInfo->nYSize - 1 - nYOff);
525 :
526 0 : if (VSIFSeekL(psDInfo->fp, nOffset, SEEK_SET) != 0 ||
527 0 : VSIFReadL(pabyData, 2, 1, psDInfo->fp) != 1)
528 : {
529 : #ifndef AVOID_CPL
530 0 : CPLError(CE_Failure, CPLE_FileIO,
531 : #else
532 : fprintf(stderr,
533 : #endif
534 : "Failed to seek to, or read (%d,%d) at offset %d\n"
535 : "in DTED file.\n",
536 : nXOff, nYOff, nOffset);
537 0 : return FALSE;
538 : }
539 :
540 0 : *panVal = ((pabyData[0] & 0x7f) << 8) | pabyData[1];
541 :
542 0 : if (pabyData[0] & 0x80)
543 : {
544 0 : *panVal *= -1;
545 :
546 : /*
547 : ** It seems that some files are improperly generated in twos
548 : ** complement form for negatives. For these, redo the job
549 : ** in twos complement. eg. w_069_s50.dt0
550 : */
551 0 : if ((*panVal < -16000) && (*panVal != DTED_NODATA_VALUE))
552 : {
553 0 : *panVal = (pabyData[0] << 8) | pabyData[1];
554 :
555 0 : if (!bWarnedTwoComplement)
556 : {
557 0 : bWarnedTwoComplement = TRUE;
558 : #ifndef AVOID_CPL
559 0 : CPLError(
560 : CE_Warning, CPLE_AppDefined,
561 : #else
562 : fprintf(
563 : stderr,
564 : #endif
565 : "The DTED driver found values less than -16000, and has "
566 : "adjusted\n"
567 : "them assuming they are improperly two-complemented. No "
568 : "more warnings\n"
569 : "will be issued in this session about this operation.");
570 : }
571 : }
572 : }
573 :
574 0 : return TRUE;
575 : }
576 :
577 : /************************************************************************/
578 : /* DTEDReadProfile() */
579 : /* */
580 : /* Read one profile line. These are organized in bottom to top */
581 : /* order starting from the leftmost column (0). */
582 : /************************************************************************/
583 :
584 0 : int DTEDReadProfile(DTEDInfo *psDInfo, int nColumnOffset, GInt16 *panData)
585 : {
586 0 : return DTEDReadProfileEx(psDInfo, nColumnOffset, panData, FALSE);
587 : }
588 :
589 4031 : int DTEDReadProfileEx(DTEDInfo *psDInfo, int nColumnOffset, GInt16 *panData,
590 : int bVerifyChecksum)
591 : {
592 : int nOffset;
593 : int i;
594 : GByte *pabyRecord;
595 : int nLongitudeCount;
596 :
597 : /* -------------------------------------------------------------------- */
598 : /* Read data record from disk. */
599 : /* -------------------------------------------------------------------- */
600 4031 : if (psDInfo->panMapLogicalColsToOffsets != NULL)
601 : {
602 242 : nOffset = psDInfo->panMapLogicalColsToOffsets[nColumnOffset];
603 242 : if (nOffset < 0)
604 : {
605 29036 : for (i = 0; i < psDInfo->nYSize; i++)
606 : {
607 28798 : panData[i] = DTED_NODATA_VALUE;
608 : }
609 238 : return TRUE;
610 : }
611 : }
612 : else
613 3789 : nOffset =
614 3789 : psDInfo->nDataOffset + nColumnOffset * (12 + psDInfo->nYSize * 2);
615 :
616 3793 : pabyRecord = (GByte *)CPLMalloc(12 + psDInfo->nYSize * 2);
617 :
618 7586 : if (VSIFSeekL(psDInfo->fp, nOffset, SEEK_SET) != 0 ||
619 3793 : VSIFReadL(pabyRecord, (12 + psDInfo->nYSize * 2), 1, psDInfo->fp) != 1)
620 : {
621 : #ifndef AVOID_CPL
622 0 : CPLError(CE_Failure, CPLE_FileIO,
623 : #else
624 : fprintf(stderr,
625 : #endif
626 : "Failed to seek to, or read profile %d at offset %d\n"
627 : "in DTED file.\n",
628 : nColumnOffset, nOffset);
629 0 : CPLFree(pabyRecord);
630 0 : return FALSE;
631 : }
632 :
633 3793 : nLongitudeCount = (pabyRecord[4] << 8) | pabyRecord[5];
634 3793 : if (nLongitudeCount != nColumnOffset)
635 : {
636 : #ifndef AVOID_CPL
637 0 : CPLError(
638 : CE_Warning, CPLE_AppDefined,
639 : #else
640 : fprintf(
641 : stderr,
642 : #endif
643 : "Longitude count (%d) of column %d doesn't match expected value.\n",
644 : nLongitudeCount, nColumnOffset);
645 : }
646 :
647 : /* -------------------------------------------------------------------- */
648 : /* Translate data values from "signed magnitude" to standard */
649 : /* binary. */
650 : /* -------------------------------------------------------------------- */
651 1111830 : for (i = 0; i < psDInfo->nYSize; i++)
652 : {
653 1108030 : panData[i] =
654 1108030 : ((pabyRecord[8 + i * 2] & 0x7f) << 8) | pabyRecord[8 + i * 2 + 1];
655 :
656 1108030 : if (pabyRecord[8 + i * 2] & 0x80)
657 : {
658 0 : panData[i] *= -1;
659 :
660 : /*
661 : ** It seems that some files are improperly generated in twos
662 : ** complement form for negatives. For these, redo the job
663 : ** in twos complement. eg. w_069_s50.dt0
664 : */
665 0 : if ((panData[i] < -16000) && (panData[i] != DTED_NODATA_VALUE))
666 : {
667 0 : panData[i] =
668 0 : (pabyRecord[8 + i * 2] << 8) | pabyRecord[8 + i * 2 + 1];
669 :
670 0 : if (!bWarnedTwoComplement)
671 : {
672 0 : bWarnedTwoComplement = TRUE;
673 : #ifndef AVOID_CPL
674 0 : CPLError(
675 : CE_Warning, CPLE_AppDefined,
676 : #else
677 : fprintf(
678 : stderr,
679 : #endif
680 : "The DTED driver found values less than -16000, and "
681 : "has adjusted\n"
682 : "them assuming they are improperly two-complemented. "
683 : "No more warnings\n"
684 : "will be issued in this session about this operation.");
685 : }
686 : }
687 : }
688 : }
689 :
690 3793 : if (bVerifyChecksum)
691 : {
692 1 : unsigned int nCheckSum = 0;
693 : unsigned int fileCheckSum;
694 :
695 : /* --------------------------------------------------------------------
696 : */
697 : /* Verify the checksum. */
698 : /* --------------------------------------------------------------------
699 : */
700 :
701 251 : for (i = 0; i < psDInfo->nYSize * 2 + 8; i++)
702 250 : nCheckSum += pabyRecord[i];
703 :
704 1 : fileCheckSum = (pabyRecord[8 + psDInfo->nYSize * 2 + 0] << 24) |
705 1 : (pabyRecord[8 + psDInfo->nYSize * 2 + 1] << 16) |
706 1 : (pabyRecord[8 + psDInfo->nYSize * 2 + 2] << 8) |
707 1 : pabyRecord[8 + psDInfo->nYSize * 2 + 3];
708 :
709 1 : if (fileCheckSum > 0xff * (8 + (unsigned int)psDInfo->nYSize * 2))
710 : {
711 : static int bWarned = FALSE;
712 0 : if (!bWarned)
713 : {
714 0 : bWarned = TRUE;
715 : #ifndef AVOID_CPL
716 0 : CPLError(CE_Warning, CPLE_AppDefined,
717 : #else
718 : fprintf(stderr,
719 : #endif
720 : "The DTED driver has read from the file a checksum "
721 : "with an impossible value (0x%X) at column %d.\n"
722 : "Check with your file producer.\n"
723 : "No more warnings will be issued in this session "
724 : "about this operation.",
725 : fileCheckSum, nColumnOffset);
726 : }
727 : }
728 1 : else if (fileCheckSum != nCheckSum)
729 : {
730 : #ifndef AVOID_CPL
731 1 : CPLError(
732 : CE_Failure, CPLE_AppDefined,
733 : #else
734 : fprintf(
735 : stderr,
736 : #endif
737 : "The DTED driver has found a computed and read checksum "
738 : "that do not match at column %d. Computed 0x%X, read 0x%X\n",
739 : nColumnOffset, nCheckSum, fileCheckSum);
740 1 : CPLFree(pabyRecord);
741 1 : return FALSE;
742 : }
743 : }
744 :
745 3792 : CPLFree(pabyRecord);
746 :
747 3792 : return TRUE;
748 : }
749 :
750 : /************************************************************************/
751 : /* DTEDWriteProfile() */
752 : /************************************************************************/
753 :
754 964 : int DTEDWriteProfile(DTEDInfo *psDInfo, int nColumnOffset, GInt16 *panData)
755 :
756 : {
757 : int nOffset;
758 964 : int i, nCheckSum = 0;
759 : GByte *pabyRecord;
760 :
761 964 : if (psDInfo->panMapLogicalColsToOffsets != NULL)
762 : {
763 : #ifndef AVOID_CPL
764 0 : CPLError(CE_Failure, CPLE_NotSupported,
765 : #else
766 : fprintf(stderr,
767 : #endif
768 : "Write to partial file not supported.\n");
769 0 : return FALSE;
770 : }
771 :
772 : /* -------------------------------------------------------------------- */
773 : /* Format the data record. */
774 : /* -------------------------------------------------------------------- */
775 964 : pabyRecord = (GByte *)CPLMalloc(12 + psDInfo->nYSize * 2);
776 :
777 766688 : for (i = 0; i < psDInfo->nYSize; i++)
778 : {
779 765724 : int nABSVal = ABS(panData[psDInfo->nYSize - i - 1]);
780 765724 : pabyRecord[8 + i * 2] = (GByte)((nABSVal >> 8) & 0x7f);
781 765724 : pabyRecord[8 + i * 2 + 1] = (GByte)(nABSVal & 0xff);
782 :
783 765724 : if (panData[psDInfo->nYSize - i - 1] < 0)
784 0 : pabyRecord[8 + i * 2] |= 0x80;
785 : }
786 :
787 964 : pabyRecord[0] = 0xaa;
788 964 : pabyRecord[1] = 0;
789 964 : pabyRecord[2] = (GByte)(nColumnOffset / 256);
790 964 : pabyRecord[3] = (GByte)(nColumnOffset % 256);
791 964 : pabyRecord[4] = (GByte)(nColumnOffset / 256);
792 964 : pabyRecord[5] = (GByte)(nColumnOffset % 256);
793 964 : pabyRecord[6] = 0;
794 964 : pabyRecord[7] = 0;
795 :
796 : /* -------------------------------------------------------------------- */
797 : /* Compute the checksum. */
798 : /* -------------------------------------------------------------------- */
799 1540120 : for (i = 0; i < psDInfo->nYSize * 2 + 8; i++)
800 1539160 : nCheckSum += pabyRecord[i];
801 :
802 964 : pabyRecord[8 + psDInfo->nYSize * 2 + 0] = (GByte)((nCheckSum >> 24) & 0xff);
803 964 : pabyRecord[8 + psDInfo->nYSize * 2 + 1] = (GByte)((nCheckSum >> 16) & 0xff);
804 964 : pabyRecord[8 + psDInfo->nYSize * 2 + 2] = (GByte)((nCheckSum >> 8) & 0xff);
805 964 : pabyRecord[8 + psDInfo->nYSize * 2 + 3] = (GByte)(nCheckSum & 0xff);
806 :
807 : /* -------------------------------------------------------------------- */
808 : /* Write the record. */
809 : /* -------------------------------------------------------------------- */
810 964 : nOffset = psDInfo->nDataOffset + nColumnOffset * (12 + psDInfo->nYSize * 2);
811 :
812 1928 : if (VSIFSeekL(psDInfo->fp, nOffset, SEEK_SET) != 0 ||
813 964 : VSIFWriteL(pabyRecord, (12 + psDInfo->nYSize * 2), 1, psDInfo->fp) != 1)
814 : {
815 : #ifndef AVOID_CPL
816 0 : CPLError(CE_Failure, CPLE_FileIO,
817 : #else
818 : fprintf(stderr,
819 : #endif
820 : "Failed to seek to, or write profile %d at offset %d\n"
821 : "in DTED file.\n",
822 : nColumnOffset, nOffset);
823 0 : CPLFree(pabyRecord);
824 0 : return FALSE;
825 : }
826 :
827 964 : CPLFree(pabyRecord);
828 :
829 964 : return TRUE;
830 : }
831 :
832 : /************************************************************************/
833 : /* DTEDGetMetadataLocation() */
834 : /************************************************************************/
835 :
836 1234 : static void DTEDGetMetadataLocation(DTEDInfo *psDInfo, DTEDMetaDataCode eCode,
837 : char **ppszLocation, int *pnLength)
838 : {
839 1234 : int bIsWeirdDTED = psDInfo->pachUHLRecord[4] == ' ';
840 :
841 1234 : switch (eCode)
842 : {
843 51 : case DTEDMD_ORIGINLONG:
844 51 : if (bIsWeirdDTED)
845 0 : *ppszLocation = psDInfo->pachUHLRecord + 8;
846 : else
847 51 : *ppszLocation = psDInfo->pachUHLRecord + 4;
848 51 : *pnLength = 8;
849 51 : break;
850 :
851 51 : case DTEDMD_ORIGINLAT:
852 51 : if (bIsWeirdDTED)
853 0 : *ppszLocation = psDInfo->pachUHLRecord + 24;
854 : else
855 51 : *ppszLocation = psDInfo->pachUHLRecord + 12;
856 51 : *pnLength = 8;
857 51 : break;
858 :
859 54 : case DTEDMD_VERTACCURACY_UHL:
860 54 : if (bIsWeirdDTED)
861 0 : *ppszLocation = psDInfo->pachUHLRecord + 56;
862 : else
863 54 : *ppszLocation = psDInfo->pachUHLRecord + 28;
864 54 : *pnLength = 4;
865 54 : break;
866 :
867 54 : case DTEDMD_SECURITYCODE_UHL:
868 54 : if (bIsWeirdDTED)
869 0 : *ppszLocation = psDInfo->pachUHLRecord + 60;
870 : else
871 54 : *ppszLocation = psDInfo->pachUHLRecord + 32;
872 54 : *pnLength = 3;
873 54 : break;
874 :
875 54 : case DTEDMD_UNIQUEREF_UHL:
876 54 : if (bIsWeirdDTED)
877 0 : *ppszLocation = NULL;
878 : else
879 54 : *ppszLocation = psDInfo->pachUHLRecord + 35;
880 54 : *pnLength = 12;
881 54 : break;
882 :
883 54 : case DTEDMD_DATA_EDITION:
884 54 : if (bIsWeirdDTED)
885 0 : *ppszLocation = psDInfo->pachDSIRecord + 174;
886 : else
887 54 : *ppszLocation = psDInfo->pachDSIRecord + 87;
888 54 : *pnLength = 2;
889 54 : break;
890 :
891 54 : case DTEDMD_MATCHMERGE_VERSION:
892 54 : if (bIsWeirdDTED)
893 0 : *ppszLocation = psDInfo->pachDSIRecord + 176;
894 : else
895 54 : *ppszLocation = psDInfo->pachDSIRecord + 89;
896 54 : *pnLength = 1;
897 54 : break;
898 :
899 54 : case DTEDMD_MAINT_DATE:
900 54 : if (bIsWeirdDTED)
901 0 : *ppszLocation = psDInfo->pachDSIRecord + 177;
902 : else
903 54 : *ppszLocation = psDInfo->pachDSIRecord + 90;
904 54 : *pnLength = 4;
905 54 : break;
906 :
907 54 : case DTEDMD_MATCHMERGE_DATE:
908 54 : if (bIsWeirdDTED)
909 0 : *ppszLocation = psDInfo->pachDSIRecord + 181;
910 : else
911 54 : *ppszLocation = psDInfo->pachDSIRecord + 94;
912 54 : *pnLength = 4;
913 54 : break;
914 :
915 54 : case DTEDMD_MAINT_DESCRIPTION:
916 54 : if (bIsWeirdDTED)
917 0 : *ppszLocation = psDInfo->pachDSIRecord + 185;
918 : else
919 54 : *ppszLocation = psDInfo->pachDSIRecord + 98;
920 54 : *pnLength = 4;
921 54 : break;
922 :
923 54 : case DTEDMD_PRODUCER:
924 54 : if (bIsWeirdDTED)
925 0 : *ppszLocation = psDInfo->pachDSIRecord + 189;
926 : else
927 54 : *ppszLocation = psDInfo->pachDSIRecord + 102;
928 54 : *pnLength = 8;
929 54 : break;
930 :
931 54 : case DTEDMD_VERTDATUM:
932 54 : if (bIsWeirdDTED)
933 0 : *ppszLocation = psDInfo->pachDSIRecord + 267;
934 : else
935 54 : *ppszLocation = psDInfo->pachDSIRecord + 141;
936 54 : *pnLength = 3;
937 54 : break;
938 :
939 54 : case DTEDMD_HORIZDATUM:
940 54 : if (bIsWeirdDTED)
941 0 : *ppszLocation = psDInfo->pachDSIRecord + 270;
942 : else
943 54 : *ppszLocation = psDInfo->pachDSIRecord + 144;
944 54 : *pnLength = 5;
945 54 : break;
946 :
947 54 : case DTEDMD_DIGITIZING_SYS:
948 54 : if (bIsWeirdDTED)
949 0 : *ppszLocation = NULL;
950 : else
951 54 : *ppszLocation = psDInfo->pachDSIRecord + 149;
952 54 : *pnLength = 10;
953 54 : break;
954 :
955 54 : case DTEDMD_COMPILATION_DATE:
956 54 : if (bIsWeirdDTED)
957 0 : *ppszLocation = NULL;
958 : else
959 54 : *ppszLocation = psDInfo->pachDSIRecord + 159;
960 54 : *pnLength = 4;
961 54 : break;
962 :
963 54 : case DTEDMD_HORIZACCURACY:
964 54 : *ppszLocation = psDInfo->pachACCRecord + 3;
965 54 : *pnLength = 4;
966 54 : break;
967 :
968 54 : case DTEDMD_REL_HORIZACCURACY:
969 54 : *ppszLocation = psDInfo->pachACCRecord + 11;
970 54 : *pnLength = 4;
971 54 : break;
972 :
973 54 : case DTEDMD_REL_VERTACCURACY:
974 54 : *ppszLocation = psDInfo->pachACCRecord + 15;
975 54 : *pnLength = 4;
976 54 : break;
977 :
978 54 : case DTEDMD_VERTACCURACY_ACC:
979 54 : *ppszLocation = psDInfo->pachACCRecord + 7;
980 54 : *pnLength = 4;
981 54 : break;
982 :
983 54 : case DTEDMD_SECURITYCODE_DSI:
984 54 : *ppszLocation = psDInfo->pachDSIRecord + 3;
985 54 : *pnLength = 1;
986 54 : break;
987 :
988 54 : case DTEDMD_UNIQUEREF_DSI:
989 54 : if (bIsWeirdDTED)
990 0 : *ppszLocation = NULL;
991 : else
992 54 : *ppszLocation = psDInfo->pachDSIRecord + 64;
993 54 : *pnLength = 15;
994 54 : break;
995 :
996 51 : case DTEDMD_NIMA_DESIGNATOR:
997 51 : if (bIsWeirdDTED)
998 0 : *ppszLocation = psDInfo->pachDSIRecord + 118;
999 : else
1000 51 : *ppszLocation = psDInfo->pachDSIRecord + 59;
1001 51 : *pnLength = 5;
1002 51 : break;
1003 :
1004 55 : case DTEDMD_PARTIALCELL_DSI:
1005 55 : if (bIsWeirdDTED)
1006 0 : *ppszLocation = NULL;
1007 : else
1008 55 : *ppszLocation = psDInfo->pachDSIRecord + 289;
1009 55 : *pnLength = 2;
1010 55 : break;
1011 :
1012 0 : default:
1013 0 : *ppszLocation = NULL;
1014 0 : *pnLength = 0;
1015 : }
1016 1234 : }
1017 :
1018 : /************************************************************************/
1019 : /* DTEDGetMetadata() */
1020 : /************************************************************************/
1021 :
1022 1173 : char *DTEDGetMetadata(DTEDInfo *psDInfo, DTEDMetaDataCode eCode)
1023 :
1024 : {
1025 : int nFieldLen;
1026 : char *pszFieldSrc;
1027 : char *pszResult;
1028 :
1029 1173 : DTEDGetMetadataLocation(psDInfo, eCode, &pszFieldSrc, &nFieldLen);
1030 1173 : if (pszFieldSrc == NULL)
1031 0 : return CPLStrdup("");
1032 :
1033 1173 : pszResult = (char *)CPLMalloc(nFieldLen + 1);
1034 1173 : strncpy(pszResult, pszFieldSrc, nFieldLen);
1035 1173 : pszResult[nFieldLen] = '\0';
1036 :
1037 1173 : return pszResult;
1038 : }
1039 :
1040 : /************************************************************************/
1041 : /* DTEDSetMetadata() */
1042 : /************************************************************************/
1043 :
1044 61 : int DTEDSetMetadata(DTEDInfo *psDInfo, DTEDMetaDataCode eCode,
1045 : const char *pszNewValue)
1046 :
1047 : {
1048 : int nFieldLen;
1049 : char *pszFieldSrc;
1050 : size_t nLenToCopy;
1051 :
1052 61 : if (!psDInfo->bUpdate)
1053 0 : return FALSE;
1054 :
1055 : /* -------------------------------------------------------------------- */
1056 : /* Get the location in the headers to update. */
1057 : /* -------------------------------------------------------------------- */
1058 61 : DTEDGetMetadataLocation(psDInfo, eCode, &pszFieldSrc, &nFieldLen);
1059 61 : if (pszFieldSrc == NULL)
1060 0 : return FALSE;
1061 :
1062 : /* -------------------------------------------------------------------- */
1063 : /* Update it, padding with spaces. */
1064 : /* -------------------------------------------------------------------- */
1065 61 : nLenToCopy = MIN((size_t)nFieldLen, strlen(pszNewValue));
1066 61 : memcpy(pszFieldSrc, pszNewValue, nLenToCopy);
1067 61 : if (nLenToCopy < (size_t)nFieldLen)
1068 0 : memset(pszFieldSrc + nLenToCopy, ' ', nFieldLen - nLenToCopy);
1069 :
1070 : /* Turn the flag on, so that the headers are rewritten at file */
1071 : /* closing */
1072 61 : psDInfo->bRewriteHeaders = TRUE;
1073 :
1074 61 : return TRUE;
1075 : }
1076 :
1077 : /************************************************************************/
1078 : /* DTEDClose() */
1079 : /************************************************************************/
1080 :
1081 66 : void DTEDClose(DTEDInfo *psDInfo)
1082 :
1083 : {
1084 66 : if (psDInfo->bRewriteHeaders)
1085 : {
1086 : /* --------------------------------------------------------------------
1087 : */
1088 : /* Write all headers back to disk. */
1089 : /* --------------------------------------------------------------------
1090 : */
1091 4 : CPL_IGNORE_RET_VAL_INT(
1092 4 : VSIFSeekL(psDInfo->fp, psDInfo->nUHLOffset, SEEK_SET));
1093 4 : CPL_IGNORE_RET_VAL_SIZET(
1094 4 : VSIFWriteL(psDInfo->pachUHLRecord, 1, DTED_UHL_SIZE, psDInfo->fp));
1095 :
1096 4 : CPL_IGNORE_RET_VAL_INT(
1097 4 : VSIFSeekL(psDInfo->fp, psDInfo->nDSIOffset, SEEK_SET));
1098 4 : CPL_IGNORE_RET_VAL_SIZET(
1099 4 : VSIFWriteL(psDInfo->pachDSIRecord, 1, DTED_DSI_SIZE, psDInfo->fp));
1100 :
1101 4 : CPL_IGNORE_RET_VAL_INT(
1102 4 : VSIFSeekL(psDInfo->fp, psDInfo->nACCOffset, SEEK_SET));
1103 4 : CPL_IGNORE_RET_VAL_SIZET(
1104 4 : VSIFWriteL(psDInfo->pachACCRecord, 1, DTED_ACC_SIZE, psDInfo->fp));
1105 : }
1106 :
1107 66 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(psDInfo->fp));
1108 :
1109 66 : CPLFree(psDInfo->pachUHLRecord);
1110 66 : CPLFree(psDInfo->pachDSIRecord);
1111 66 : CPLFree(psDInfo->pachACCRecord);
1112 :
1113 66 : CPLFree(psDInfo->panMapLogicalColsToOffsets);
1114 :
1115 66 : CPLFree(psDInfo);
1116 66 : }
|