Coverage Report

Created: 2026-09-26 08:22

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