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;
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 : const int nLongitudeCount =
459 4 : (pabyRecordHeader[4] << 8) | pabyRecordHeader[5];
460 4 : if (nLongitudeCount >= psDInfo->nXSize)
461 : {
462 0 : CPLDebug("DTED",
463 : "Invalid longitude count (%d) at physical column %d",
464 : nLongitudeCount, i);
465 0 : return;
466 : }
467 :
468 4 : psDInfo->panMapLogicalColsToOffsets[nLongitudeCount] =
469 4 : psDInfo->nDataOffset + i * nColByteSize;
470 : }
471 : }
472 : }
473 :
474 : /************************************************************************/
475 : /* DTEDReadPoint() */
476 : /* */
477 : /* Read one single sample. The coordinates are given from the */
478 : /* top-left corner of the file (contrary to the internal */
479 : /* organization or a DTED file) */
480 : /************************************************************************/
481 :
482 0 : int DTEDReadPoint(DTEDInfo *psDInfo, int nXOff, int nYOff, GInt16 *panVal)
483 : {
484 : int nOffset;
485 : GByte pabyData[2];
486 :
487 0 : if (nYOff < 0 || nXOff < 0 || nYOff >= psDInfo->nYSize ||
488 0 : nXOff >= psDInfo->nXSize)
489 : {
490 : #ifndef AVOID_CPL
491 0 : CPLError(CE_Failure, CPLE_AppDefined,
492 : #else
493 : fprintf(stderr,
494 : #endif
495 : "Invalid raster coordinates (%d,%d) in DTED file.\n", nXOff,
496 : nYOff);
497 0 : return FALSE;
498 : }
499 :
500 0 : if (psDInfo->panMapLogicalColsToOffsets != NULL)
501 : {
502 0 : nOffset = psDInfo->panMapLogicalColsToOffsets[nXOff];
503 0 : if (nOffset < 0)
504 : {
505 0 : *panVal = DTED_NODATA_VALUE;
506 0 : return TRUE;
507 : }
508 : }
509 : else
510 0 : nOffset = psDInfo->nDataOffset + nXOff * (12 + psDInfo->nYSize * 2);
511 0 : nOffset += 8 + 2 * (psDInfo->nYSize - 1 - nYOff);
512 :
513 0 : if (VSIFSeekL(psDInfo->fp, nOffset, SEEK_SET) != 0 ||
514 0 : VSIFReadL(pabyData, 2, 1, psDInfo->fp) != 1)
515 : {
516 : #ifndef AVOID_CPL
517 0 : CPLError(CE_Failure, CPLE_FileIO,
518 : #else
519 : fprintf(stderr,
520 : #endif
521 : "Failed to seek to, or read (%d,%d) at offset %d\n"
522 : "in DTED file.\n",
523 : nXOff, nYOff, nOffset);
524 0 : return FALSE;
525 : }
526 :
527 0 : *panVal = ((pabyData[0] & 0x7f) << 8) | pabyData[1];
528 :
529 0 : if (pabyData[0] & 0x80)
530 : {
531 0 : *panVal *= -1;
532 :
533 : /*
534 : ** It seems that some files are improperly generated in twos
535 : ** complement form for negatives. For these, redo the job
536 : ** in twos complement. eg. w_069_s50.dt0
537 : */
538 0 : if (!psDInfo->bAssumeConformant && (*panVal < -16000) &&
539 0 : (*panVal != DTED_NODATA_VALUE))
540 : {
541 0 : *panVal = (pabyData[0] << 8) | pabyData[1];
542 :
543 0 : if (!bWarnedTwoComplement)
544 : {
545 0 : bWarnedTwoComplement = TRUE;
546 : #ifndef AVOID_CPL
547 0 : CPLError(
548 : CE_Warning, CPLE_AppDefined,
549 : #else
550 : fprintf(
551 : stderr,
552 : #endif
553 : "The DTED driver found values less than -16000, and has "
554 : "adjusted\n"
555 : "them assuming they are improperly two-complemented. If "
556 : "you wish to\n"
557 : "disable this behavior, set the DTED_ASSUME_CONFORMANT "
558 : "configuration\n"
559 : "option to YES. No more warnings will be issued in this "
560 : "session\n"
561 : "about this operation.");
562 : }
563 : }
564 : }
565 :
566 0 : return TRUE;
567 : }
568 :
569 : /************************************************************************/
570 : /* DTEDReadProfile() */
571 : /* */
572 : /* Read one profile line. These are organized in bottom to top */
573 : /* order starting from the leftmost column (0). */
574 : /************************************************************************/
575 :
576 0 : int DTEDReadProfile(DTEDInfo *psDInfo, int nColumnOffset, GInt16 *panData)
577 : {
578 0 : return DTEDReadProfileEx(psDInfo, nColumnOffset, panData, FALSE);
579 : }
580 :
581 3758 : int DTEDReadProfileEx(DTEDInfo *psDInfo, int nColumnOffset, GInt16 *panData,
582 : int bVerifyChecksum)
583 : {
584 : int nOffset;
585 : int i;
586 : GByte *pabyRecord;
587 : int nLongitudeCount;
588 :
589 : /* -------------------------------------------------------------------- */
590 : /* Read data record from disk. */
591 : /* -------------------------------------------------------------------- */
592 3758 : if (psDInfo->panMapLogicalColsToOffsets != NULL)
593 : {
594 242 : nOffset = psDInfo->panMapLogicalColsToOffsets[nColumnOffset];
595 242 : if (nOffset < 0)
596 : {
597 29036 : for (i = 0; i < psDInfo->nYSize; i++)
598 : {
599 28798 : panData[i] = DTED_NODATA_VALUE;
600 : }
601 238 : return TRUE;
602 : }
603 : }
604 : else
605 3516 : nOffset =
606 3516 : psDInfo->nDataOffset + nColumnOffset * (12 + psDInfo->nYSize * 2);
607 :
608 3520 : pabyRecord = (GByte *)CPLMalloc(12 + psDInfo->nYSize * 2);
609 :
610 7040 : if (VSIFSeekL(psDInfo->fp, nOffset, SEEK_SET) != 0 ||
611 3520 : VSIFReadL(pabyRecord, (12 + psDInfo->nYSize * 2), 1, psDInfo->fp) != 1)
612 : {
613 : #ifndef AVOID_CPL
614 0 : CPLError(CE_Failure, CPLE_FileIO,
615 : #else
616 : fprintf(stderr,
617 : #endif
618 : "Failed to seek to, or read profile %d at offset %d\n"
619 : "in DTED file.\n",
620 : nColumnOffset, nOffset);
621 0 : CPLFree(pabyRecord);
622 0 : return FALSE;
623 : }
624 :
625 3520 : nLongitudeCount = (pabyRecord[4] << 8) | pabyRecord[5];
626 3520 : if (nLongitudeCount != nColumnOffset)
627 : {
628 : #ifndef AVOID_CPL
629 0 : CPLError(
630 : CE_Warning, CPLE_AppDefined,
631 : #else
632 : fprintf(
633 : stderr,
634 : #endif
635 : "Longitude count (%d) of column %d doesn't match expected value.\n",
636 : nLongitudeCount, nColumnOffset);
637 : }
638 :
639 : /* -------------------------------------------------------------------- */
640 : /* Translate data values from "signed magnitude" to standard */
641 : /* binary. */
642 : /* -------------------------------------------------------------------- */
643 1078520 : for (i = 0; i < psDInfo->nYSize; i++)
644 : {
645 1075000 : panData[i] =
646 1075000 : ((pabyRecord[8 + i * 2] & 0x7f) << 8) | pabyRecord[8 + i * 2 + 1];
647 :
648 1075000 : if (pabyRecord[8 + i * 2] & 0x80)
649 : {
650 0 : panData[i] *= -1;
651 :
652 : /*
653 : ** It seems that some files are improperly generated in twos
654 : ** complement form for negatives. For these, redo the job
655 : ** in twos complement. eg. w_069_s50.dt0
656 : */
657 0 : if ((panData[i] < -16000) && (panData[i] != DTED_NODATA_VALUE))
658 : {
659 0 : panData[i] =
660 0 : (pabyRecord[8 + i * 2] << 8) | pabyRecord[8 + i * 2 + 1];
661 :
662 0 : if (!bWarnedTwoComplement)
663 : {
664 0 : bWarnedTwoComplement = TRUE;
665 : #ifndef AVOID_CPL
666 0 : CPLError(
667 : CE_Warning, CPLE_AppDefined,
668 : #else
669 : fprintf(
670 : stderr,
671 : #endif
672 : "The DTED driver found values less than -16000, and "
673 : "has adjusted\n"
674 : "them assuming they are improperly two-complemented. "
675 : "No more warnings\n"
676 : "will be issued in this session about this operation.");
677 : }
678 : }
679 : }
680 : }
681 :
682 3520 : if (bVerifyChecksum)
683 : {
684 1 : unsigned int nCheckSum = 0;
685 : unsigned int fileCheckSum;
686 :
687 : /* --------------------------------------------------------------------
688 : */
689 : /* Verify the checksum. */
690 : /* --------------------------------------------------------------------
691 : */
692 :
693 251 : for (i = 0; i < psDInfo->nYSize * 2 + 8; i++)
694 250 : nCheckSum += pabyRecord[i];
695 :
696 1 : fileCheckSum = (pabyRecord[8 + psDInfo->nYSize * 2 + 0] << 24) |
697 1 : (pabyRecord[8 + psDInfo->nYSize * 2 + 1] << 16) |
698 1 : (pabyRecord[8 + psDInfo->nYSize * 2 + 2] << 8) |
699 1 : pabyRecord[8 + psDInfo->nYSize * 2 + 3];
700 :
701 1 : if (fileCheckSum > 0xff * (8 + (unsigned int)psDInfo->nYSize * 2))
702 : {
703 : static int bWarned = FALSE;
704 0 : if (!bWarned)
705 : {
706 0 : bWarned = TRUE;
707 : #ifndef AVOID_CPL
708 0 : CPLError(CE_Warning, CPLE_AppDefined,
709 : #else
710 : fprintf(stderr,
711 : #endif
712 : "The DTED driver has read from the file a checksum "
713 : "with an impossible value (0x%X) at column %d.\n"
714 : "Check with your file producer.\n"
715 : "No more warnings will be issued in this session "
716 : "about this operation.",
717 : fileCheckSum, nColumnOffset);
718 : }
719 : }
720 1 : else if (fileCheckSum != nCheckSum)
721 : {
722 : #ifndef AVOID_CPL
723 1 : CPLError(
724 : CE_Failure, CPLE_AppDefined,
725 : #else
726 : fprintf(
727 : stderr,
728 : #endif
729 : "The DTED driver has found a computed and read checksum "
730 : "that do not match at column %d. Computed 0x%X, read 0x%X\n",
731 : nColumnOffset, nCheckSum, fileCheckSum);
732 1 : CPLFree(pabyRecord);
733 1 : return FALSE;
734 : }
735 : }
736 :
737 3519 : CPLFree(pabyRecord);
738 :
739 3519 : return TRUE;
740 : }
741 :
742 : /************************************************************************/
743 : /* DTEDWriteProfile() */
744 : /************************************************************************/
745 :
746 964 : int DTEDWriteProfile(DTEDInfo *psDInfo, int nColumnOffset, GInt16 *panData)
747 :
748 : {
749 : int nOffset;
750 964 : int i, nCheckSum = 0;
751 : GByte *pabyRecord;
752 :
753 964 : if (psDInfo->panMapLogicalColsToOffsets != NULL)
754 : {
755 : #ifndef AVOID_CPL
756 0 : CPLError(CE_Failure, CPLE_NotSupported,
757 : #else
758 : fprintf(stderr,
759 : #endif
760 : "Write to partial file not supported.\n");
761 0 : return FALSE;
762 : }
763 :
764 : /* -------------------------------------------------------------------- */
765 : /* Format the data record. */
766 : /* -------------------------------------------------------------------- */
767 964 : pabyRecord = (GByte *)CPLMalloc(12 + psDInfo->nYSize * 2);
768 :
769 766688 : for (i = 0; i < psDInfo->nYSize; i++)
770 : {
771 765724 : int nABSVal = CPL_ABS(panData[psDInfo->nYSize - i - 1]);
772 765724 : pabyRecord[8 + i * 2] = (GByte)((nABSVal >> 8) & 0x7f);
773 765724 : pabyRecord[8 + i * 2 + 1] = (GByte)(nABSVal & 0xff);
774 :
775 765724 : if (panData[psDInfo->nYSize - i - 1] < 0)
776 0 : pabyRecord[8 + i * 2] |= 0x80;
777 : }
778 :
779 964 : pabyRecord[0] = 0xaa;
780 964 : pabyRecord[1] = 0;
781 964 : pabyRecord[2] = (GByte)(nColumnOffset / 256);
782 964 : pabyRecord[3] = (GByte)(nColumnOffset % 256);
783 964 : pabyRecord[4] = (GByte)(nColumnOffset / 256);
784 964 : pabyRecord[5] = (GByte)(nColumnOffset % 256);
785 964 : pabyRecord[6] = 0;
786 964 : pabyRecord[7] = 0;
787 :
788 : /* -------------------------------------------------------------------- */
789 : /* Compute the checksum. */
790 : /* -------------------------------------------------------------------- */
791 1540120 : for (i = 0; i < psDInfo->nYSize * 2 + 8; i++)
792 1539160 : nCheckSum += pabyRecord[i];
793 :
794 964 : pabyRecord[8 + psDInfo->nYSize * 2 + 0] = (GByte)((nCheckSum >> 24) & 0xff);
795 964 : pabyRecord[8 + psDInfo->nYSize * 2 + 1] = (GByte)((nCheckSum >> 16) & 0xff);
796 964 : pabyRecord[8 + psDInfo->nYSize * 2 + 2] = (GByte)((nCheckSum >> 8) & 0xff);
797 964 : pabyRecord[8 + psDInfo->nYSize * 2 + 3] = (GByte)(nCheckSum & 0xff);
798 :
799 : /* -------------------------------------------------------------------- */
800 : /* Write the record. */
801 : /* -------------------------------------------------------------------- */
802 964 : nOffset = psDInfo->nDataOffset + nColumnOffset * (12 + psDInfo->nYSize * 2);
803 :
804 1928 : if (VSIFSeekL(psDInfo->fp, nOffset, SEEK_SET) != 0 ||
805 964 : VSIFWriteL(pabyRecord, (12 + psDInfo->nYSize * 2), 1, psDInfo->fp) != 1)
806 : {
807 : #ifndef AVOID_CPL
808 0 : CPLError(CE_Failure, CPLE_FileIO,
809 : #else
810 : fprintf(stderr,
811 : #endif
812 : "Failed to seek to, or write profile %d at offset %d\n"
813 : "in DTED file.\n",
814 : nColumnOffset, nOffset);
815 0 : CPLFree(pabyRecord);
816 0 : return FALSE;
817 : }
818 :
819 964 : CPLFree(pabyRecord);
820 :
821 964 : return TRUE;
822 : }
823 :
824 : /************************************************************************/
825 : /* DTEDGetMetadataLocation() */
826 : /************************************************************************/
827 :
828 1261 : static void DTEDGetMetadataLocation(DTEDInfo *psDInfo, DTEDMetaDataCode eCode,
829 : char **ppszLocation, int *pnLength)
830 : {
831 1261 : int bIsWeirdDTED = psDInfo->pachUHLRecord[4] == ' ';
832 :
833 1261 : switch (eCode)
834 : {
835 48 : case DTEDMD_ORIGINLONG:
836 48 : if (bIsWeirdDTED)
837 0 : *ppszLocation = psDInfo->pachUHLRecord + 8;
838 : else
839 48 : *ppszLocation = psDInfo->pachUHLRecord + 4;
840 48 : *pnLength = 8;
841 48 : break;
842 :
843 48 : case DTEDMD_ORIGINLAT:
844 48 : if (bIsWeirdDTED)
845 0 : *ppszLocation = psDInfo->pachUHLRecord + 24;
846 : else
847 48 : *ppszLocation = psDInfo->pachUHLRecord + 12;
848 48 : *pnLength = 8;
849 48 : break;
850 :
851 51 : case DTEDMD_VERTACCURACY_UHL:
852 51 : if (bIsWeirdDTED)
853 0 : *ppszLocation = psDInfo->pachUHLRecord + 56;
854 : else
855 51 : *ppszLocation = psDInfo->pachUHLRecord + 28;
856 51 : *pnLength = 4;
857 51 : break;
858 :
859 51 : case DTEDMD_SECURITYCODE_UHL:
860 51 : if (bIsWeirdDTED)
861 0 : *ppszLocation = psDInfo->pachUHLRecord + 60;
862 : else
863 51 : *ppszLocation = psDInfo->pachUHLRecord + 32;
864 51 : *pnLength = 3;
865 51 : break;
866 :
867 51 : case DTEDMD_UNIQUEREF_UHL:
868 51 : if (bIsWeirdDTED)
869 0 : *ppszLocation = NULL;
870 : else
871 51 : *ppszLocation = psDInfo->pachUHLRecord + 35;
872 51 : *pnLength = 12;
873 51 : break;
874 :
875 51 : case DTEDMD_DATA_EDITION:
876 51 : if (bIsWeirdDTED)
877 0 : *ppszLocation = psDInfo->pachDSIRecord + 174;
878 : else
879 51 : *ppszLocation = psDInfo->pachDSIRecord + 87;
880 51 : *pnLength = 2;
881 51 : break;
882 :
883 51 : case DTEDMD_MATCHMERGE_VERSION:
884 51 : if (bIsWeirdDTED)
885 0 : *ppszLocation = psDInfo->pachDSIRecord + 176;
886 : else
887 51 : *ppszLocation = psDInfo->pachDSIRecord + 89;
888 51 : *pnLength = 1;
889 51 : break;
890 :
891 51 : case DTEDMD_MAINT_DATE:
892 51 : if (bIsWeirdDTED)
893 0 : *ppszLocation = psDInfo->pachDSIRecord + 177;
894 : else
895 51 : *ppszLocation = psDInfo->pachDSIRecord + 90;
896 51 : *pnLength = 4;
897 51 : break;
898 :
899 51 : case DTEDMD_MATCHMERGE_DATE:
900 51 : if (bIsWeirdDTED)
901 0 : *ppszLocation = psDInfo->pachDSIRecord + 181;
902 : else
903 51 : *ppszLocation = psDInfo->pachDSIRecord + 94;
904 51 : *pnLength = 4;
905 51 : break;
906 :
907 51 : case DTEDMD_MAINT_DESCRIPTION:
908 51 : if (bIsWeirdDTED)
909 0 : *ppszLocation = psDInfo->pachDSIRecord + 185;
910 : else
911 51 : *ppszLocation = psDInfo->pachDSIRecord + 98;
912 51 : *pnLength = 4;
913 51 : break;
914 :
915 51 : case DTEDMD_PRODUCER:
916 51 : if (bIsWeirdDTED)
917 0 : *ppszLocation = psDInfo->pachDSIRecord + 189;
918 : else
919 51 : *ppszLocation = psDInfo->pachDSIRecord + 102;
920 51 : *pnLength = 8;
921 51 : break;
922 :
923 51 : case DTEDMD_VERTDATUM:
924 51 : if (bIsWeirdDTED)
925 0 : *ppszLocation = psDInfo->pachDSIRecord + 267;
926 : else
927 51 : *ppszLocation = psDInfo->pachDSIRecord + 141;
928 51 : *pnLength = 3;
929 51 : break;
930 :
931 51 : case DTEDMD_HORIZDATUM:
932 51 : if (bIsWeirdDTED)
933 0 : *ppszLocation = psDInfo->pachDSIRecord + 270;
934 : else
935 51 : *ppszLocation = psDInfo->pachDSIRecord + 144;
936 51 : *pnLength = 5;
937 51 : break;
938 :
939 51 : case DTEDMD_DIGITIZING_SYS:
940 51 : if (bIsWeirdDTED)
941 0 : *ppszLocation = NULL;
942 : else
943 51 : *ppszLocation = psDInfo->pachDSIRecord + 149;
944 51 : *pnLength = 10;
945 51 : break;
946 :
947 51 : case DTEDMD_COMPILATION_DATE:
948 51 : if (bIsWeirdDTED)
949 0 : *ppszLocation = NULL;
950 : else
951 51 : *ppszLocation = psDInfo->pachDSIRecord + 159;
952 51 : *pnLength = 4;
953 51 : break;
954 :
955 51 : case DTEDMD_HORIZACCURACY:
956 51 : *ppszLocation = psDInfo->pachACCRecord + 3;
957 51 : *pnLength = 4;
958 51 : break;
959 :
960 51 : case DTEDMD_REL_HORIZACCURACY:
961 51 : *ppszLocation = psDInfo->pachACCRecord + 11;
962 51 : *pnLength = 4;
963 51 : break;
964 :
965 51 : case DTEDMD_REL_VERTACCURACY:
966 51 : *ppszLocation = psDInfo->pachACCRecord + 15;
967 51 : *pnLength = 4;
968 51 : break;
969 :
970 51 : case DTEDMD_VERTACCURACY_ACC:
971 51 : *ppszLocation = psDInfo->pachACCRecord + 7;
972 51 : *pnLength = 4;
973 51 : break;
974 :
975 51 : case DTEDMD_SECURITYCODE_DSI:
976 51 : *ppszLocation = psDInfo->pachDSIRecord + 3;
977 51 : *pnLength = 1;
978 51 : break;
979 :
980 51 : case DTEDMD_UNIQUEREF_DSI:
981 51 : if (bIsWeirdDTED)
982 0 : *ppszLocation = NULL;
983 : else
984 51 : *ppszLocation = psDInfo->pachDSIRecord + 64;
985 51 : *pnLength = 15;
986 51 : break;
987 :
988 48 : case DTEDMD_NIMA_DESIGNATOR:
989 48 : if (bIsWeirdDTED)
990 0 : *ppszLocation = psDInfo->pachDSIRecord + 118;
991 : else
992 48 : *ppszLocation = psDInfo->pachDSIRecord + 59;
993 48 : *pnLength = 5;
994 48 : break;
995 :
996 52 : case DTEDMD_PARTIALCELL_DSI:
997 52 : if (bIsWeirdDTED)
998 0 : *ppszLocation = NULL;
999 : else
1000 52 : *ppszLocation = psDInfo->pachDSIRecord + 289;
1001 52 : *pnLength = 2;
1002 52 : break;
1003 :
1004 48 : case DTEDMD_SECURITYCONTROL:
1005 48 : *ppszLocation = psDInfo->pachDSIRecord + 4;
1006 48 : *pnLength = 2;
1007 48 : break;
1008 :
1009 48 : case DTEDMD_SECURITYHANDLING:
1010 48 : *ppszLocation = psDInfo->pachDSIRecord + 6;
1011 48 : *pnLength = 27;
1012 48 : break;
1013 :
1014 0 : default:
1015 0 : *ppszLocation = NULL;
1016 0 : *pnLength = 0;
1017 : }
1018 1261 : }
1019 :
1020 : /************************************************************************/
1021 : /* DTEDGetMetadata() */
1022 : /************************************************************************/
1023 :
1024 1200 : char *DTEDGetMetadata(DTEDInfo *psDInfo, DTEDMetaDataCode eCode)
1025 :
1026 : {
1027 : int nFieldLen;
1028 : char *pszFieldSrc;
1029 : char *pszResult;
1030 :
1031 1200 : DTEDGetMetadataLocation(psDInfo, eCode, &pszFieldSrc, &nFieldLen);
1032 1200 : if (pszFieldSrc == NULL)
1033 0 : return CPLStrdup("");
1034 :
1035 1200 : pszResult = (char *)CPLMalloc(nFieldLen + 1);
1036 1200 : strncpy(pszResult, pszFieldSrc, nFieldLen);
1037 1200 : pszResult[nFieldLen] = '\0';
1038 :
1039 1200 : return pszResult;
1040 : }
1041 :
1042 : /************************************************************************/
1043 : /* DTEDSetMetadata() */
1044 : /************************************************************************/
1045 :
1046 61 : int DTEDSetMetadata(DTEDInfo *psDInfo, DTEDMetaDataCode eCode,
1047 : const char *pszNewValue)
1048 :
1049 : {
1050 : int nFieldLen;
1051 : char *pszFieldSrc;
1052 : size_t nLenToCopy;
1053 :
1054 61 : if (!psDInfo->bUpdate)
1055 0 : return FALSE;
1056 :
1057 : /* -------------------------------------------------------------------- */
1058 : /* Get the location in the headers to update. */
1059 : /* -------------------------------------------------------------------- */
1060 61 : DTEDGetMetadataLocation(psDInfo, eCode, &pszFieldSrc, &nFieldLen);
1061 61 : if (pszFieldSrc == NULL)
1062 0 : return FALSE;
1063 :
1064 : /* -------------------------------------------------------------------- */
1065 : /* Update it, padding with spaces. */
1066 : /* -------------------------------------------------------------------- */
1067 61 : nLenToCopy = CPL_MIN((size_t)nFieldLen, strlen(pszNewValue));
1068 61 : memcpy(pszFieldSrc, pszNewValue, nLenToCopy);
1069 61 : if (nLenToCopy < (size_t)nFieldLen)
1070 0 : memset(pszFieldSrc + nLenToCopy, ' ', nFieldLen - nLenToCopy);
1071 :
1072 : /* Turn the flag on, so that the headers are rewritten at file */
1073 : /* closing */
1074 61 : psDInfo->bRewriteHeaders = TRUE;
1075 :
1076 61 : return TRUE;
1077 : }
1078 :
1079 : /************************************************************************/
1080 : /* DTEDClose() */
1081 : /************************************************************************/
1082 :
1083 63 : void DTEDClose(DTEDInfo *psDInfo)
1084 :
1085 : {
1086 63 : if (psDInfo->bRewriteHeaders)
1087 : {
1088 : /* --------------------------------------------------------------------
1089 : */
1090 : /* Write all headers back to disk. */
1091 : /* --------------------------------------------------------------------
1092 : */
1093 4 : CPL_IGNORE_RET_VAL_INT(
1094 4 : VSIFSeekL(psDInfo->fp, psDInfo->nUHLOffset, SEEK_SET));
1095 4 : CPL_IGNORE_RET_VAL_SIZET(
1096 4 : VSIFWriteL(psDInfo->pachUHLRecord, 1, DTED_UHL_SIZE, psDInfo->fp));
1097 :
1098 4 : CPL_IGNORE_RET_VAL_INT(
1099 4 : VSIFSeekL(psDInfo->fp, psDInfo->nDSIOffset, SEEK_SET));
1100 4 : CPL_IGNORE_RET_VAL_SIZET(
1101 4 : VSIFWriteL(psDInfo->pachDSIRecord, 1, DTED_DSI_SIZE, psDInfo->fp));
1102 :
1103 4 : CPL_IGNORE_RET_VAL_INT(
1104 4 : VSIFSeekL(psDInfo->fp, psDInfo->nACCOffset, SEEK_SET));
1105 4 : CPL_IGNORE_RET_VAL_SIZET(
1106 4 : VSIFWriteL(psDInfo->pachACCRecord, 1, DTED_ACC_SIZE, psDInfo->fp));
1107 : }
1108 :
1109 63 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(psDInfo->fp));
1110 :
1111 63 : CPLFree(psDInfo->pachUHLRecord);
1112 63 : CPLFree(psDInfo->pachDSIRecord);
1113 63 : CPLFree(psDInfo->pachACCRecord);
1114 :
1115 63 : CPLFree(psDInfo->panMapLogicalColsToOffsets);
1116 :
1117 63 : CPLFree(psDInfo);
1118 63 : }
|