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