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