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