Coverage Report

Created: 2025-12-31 08:30

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