Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/mitab/mitab_mapheaderblock.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 *
3
 * Name:     mitab_mapheaderblock.cpp
4
 * Project:  MapInfo TAB Read/Write library
5
 * Language: C++
6
 * Purpose:  Implementation of the TABHeaderBlock class used to handle
7
 *           reading/writing of the .MAP files' header block
8
 * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
9
 *
10
 **********************************************************************
11
 * Copyright (c) 1999-2002, Daniel Morissette
12
 * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
13
 *
14
 * SPDX-License-Identifier: MIT
15
 **********************************************************************/
16
17
#include "cpl_port.h"
18
#include "mitab.h"
19
20
#include <cmath>
21
#include <cstddef>
22
23
#include "cpl_conv.h"
24
#include "cpl_error.h"
25
#include "cpl_vsi.h"
26
#include "mitab_priv.h"
27
28
/*---------------------------------------------------------------------
29
 * Set various constants used in generating the header block.
30
 *--------------------------------------------------------------------*/
31
constexpr GInt32 HDR_MAGIC_COOKIE = 42424242;
32
constexpr GInt16 HDR_VERSION_NUMBER = 500;
33
34
constexpr GByte HDR_DEF_ORG_QUADRANT = 1;  // N-E Quadrant
35
constexpr GByte HDR_DEF_REFLECTXAXIS = 0;
36
37
/*---------------------------------------------------------------------
38
 * The header block starts with an array of map object length constants.
39
 *--------------------------------------------------------------------*/
40
constexpr GByte HDR_OBJ_LEN_ARRAY_SIZE = 73;
41
constexpr GByte gabyObjLenArray[HDR_OBJ_LEN_ARRAY_SIZE] = {
42
    0x00, 0x0a, 0x0e, 0x15, 0x0e, 0x16, 0x1b, 0xa2, 0xa6, 0xab, 0x1a,
43
    0x2a, 0x2f, 0xa5, 0xa9, 0xb5, 0xa7, 0xb5, 0xd9, 0x0f, 0x17, 0x23,
44
    0x13, 0x1f, 0x2b, 0x0f, 0x17, 0x23, 0x4f, 0x57, 0x63, 0x9c, 0xa4,
45
    0xa9, 0xa0, 0xa8, 0xad, 0xa4, 0xa8, 0xad, 0x16, 0x1a, 0x39, 0x0d,
46
    0x11, 0x37, 0xa5, 0xa9, 0xb5, 0xa4, 0xa8, 0xad, 0xb2, 0xb6, 0xdc,
47
    0xbd, 0xbd, 0xf4, 0x2b, 0x2f, 0x55, 0xc8, 0xcc, 0xd8, 0xc7, 0xcb,
48
    0xd0, 0xd3, 0xd7, 0xfd, 0xc2, 0xc2, 0xf9};
49
50
/*=====================================================================
51
 *                      class TABMAPHeaderBlock
52
 *====================================================================*/
53
54
/**********************************************************************
55
 *                   TABMAPHeaderBlock::TABMAPHeaderBlock()
56
 *
57
 * Constructor.
58
 **********************************************************************/
59
TABMAPHeaderBlock::TABMAPHeaderBlock(TABAccess eAccessMode /*= TABRead*/)
60
897
    : TABRawBinBlock(eAccessMode, TRUE)
61
897
{
62
    // TODO(schwehr): Consider using initializer list for most values.
63
897
    InitMembersWithDefaultValues();
64
65
    // We don't want to reset it once it is set.
66
897
    m_bIntBoundsOverflow = FALSE;
67
897
}
68
69
/**********************************************************************
70
 *                   TABMAPHeaderBlock::~TABMAPHeaderBlock()
71
 *
72
 * Destructor.
73
 **********************************************************************/
74
TABMAPHeaderBlock::~TABMAPHeaderBlock()
75
897
{
76
897
}
77
78
/**********************************************************************
79
 *            TABMAPHeaderBlock::InitMembersWithDefaultValues()
80
 **********************************************************************/
81
void TABMAPHeaderBlock::InitMembersWithDefaultValues()
82
1.36k
{
83
    /*-----------------------------------------------------------------
84
     * Set acceptable default values for member vars.
85
     *----------------------------------------------------------------*/
86
1.36k
    m_nMAPVersionNumber = HDR_VERSION_NUMBER;
87
1.36k
    m_nRegularBlockSize = TAB_MIN_BLOCK_SIZE;
88
89
1.36k
    m_dCoordsys2DistUnits = 1.0;
90
1.36k
    m_nXMin = -1000000000;
91
1.36k
    m_nYMin = -1000000000;
92
1.36k
    m_nXMax = 1000000000;
93
1.36k
    m_nYMax = 1000000000;
94
1.36k
    m_bIntBoundsOverflow = FALSE;
95
96
1.36k
    m_nFirstIndexBlock = 0;
97
1.36k
    m_nFirstGarbageBlock = 0;
98
1.36k
    m_nFirstToolBlock = 0;
99
100
1.36k
    m_numPointObjects = 0;
101
1.36k
    m_numLineObjects = 0;
102
1.36k
    m_numRegionObjects = 0;
103
1.36k
    m_numTextObjects = 0;
104
1.36k
    m_nMaxCoordBufSize = 0;
105
106
1.36k
    m_nDistUnitsCode = 7;  // Meters
107
1.36k
    m_nMaxSpIndexDepth = 0;
108
1.36k
    m_nCoordPrecision = 3;                          // ??? 3 Digits of precision
109
1.36k
    m_nCoordOriginQuadrant = HDR_DEF_ORG_QUADRANT;  // ??? N-E quadrant
110
1.36k
    m_nReflectXAxisCoord = HDR_DEF_REFLECTXAXIS;
111
1.36k
    m_nMaxObjLenArrayId = HDR_OBJ_LEN_ARRAY_SIZE - 1;  // See gabyObjLenArray[]
112
1.36k
    m_numPenDefs = 0;
113
1.36k
    m_numBrushDefs = 0;
114
1.36k
    m_numSymbolDefs = 0;
115
1.36k
    m_numFontDefs = 0;
116
1.36k
    m_numMapToolBlocks = 0;
117
118
1.36k
    m_sProj.nProjId = 0;
119
1.36k
    m_sProj.nEllipsoidId = 0;
120
1.36k
    m_sProj.nUnitsId = 7;
121
1.36k
    m_sProj.nDatumId = 0;
122
1.36k
    m_XScale = 1000.0;  // Default coord range (before SetCoordSysBounds())
123
1.36k
    m_YScale = 1000.0;  // will be [-1000000.000 .. 1000000.000]
124
1.36k
    m_XDispl = 0.0;
125
1.36k
    m_YDispl = 0.0;
126
1.36k
    m_XPrecision = 0.0;  // not specified
127
1.36k
    m_YPrecision = 0.0;  // not specified
128
129
9.54k
    for (int i = 0; i < 6; i++)
130
8.18k
        m_sProj.adProjParams[i] = 0.0;
131
132
1.36k
    m_sProj.dDatumShiftX = 0.0;
133
1.36k
    m_sProj.dDatumShiftY = 0.0;
134
1.36k
    m_sProj.dDatumShiftZ = 0.0;
135
8.18k
    for (int i = 0; i < 5; i++)
136
6.82k
        m_sProj.adDatumParams[i] = 0.0;
137
138
1.36k
    m_sProj.nAffineFlag = 0;  // Only in version 500 and up
139
1.36k
    m_sProj.nAffineUnits = 7;
140
1.36k
    m_sProj.dAffineParamA = 0.0;
141
1.36k
    m_sProj.dAffineParamB = 0.0;
142
1.36k
    m_sProj.dAffineParamC = 0.0;
143
1.36k
    m_sProj.dAffineParamD = 0.0;
144
1.36k
    m_sProj.dAffineParamE = 0.0;
145
1.36k
    m_sProj.dAffineParamF = 0.0;
146
1.36k
}
147
148
/**********************************************************************
149
 *                   TABMAPHeaderBlock::InitBlockFromData()
150
 *
151
 * Perform some initialization on the block after its binary data has
152
 * been set or changed (or loaded from a file).
153
 *
154
 * Returns 0 if successful or -1 if an error happened, in which case
155
 * CPLError() will have been called.
156
 **********************************************************************/
157
int TABMAPHeaderBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
158
                                         int nSizeUsed,
159
                                         GBool bMakeCopy /* = TRUE */,
160
                                         VSILFILE *fpSrc /* = NULL */,
161
                                         int nOffset /* = 0 */)
162
430
{
163
    /*-----------------------------------------------------------------
164
     * First of all, we must call the base class' InitBlockFromData()
165
     *----------------------------------------------------------------*/
166
430
    const int nStatus = TABRawBinBlock::InitBlockFromData(
167
430
        pabyBuf, nBlockSize, nSizeUsed, bMakeCopy, fpSrc, nOffset);
168
430
    if (nStatus != 0)
169
0
        return nStatus;
170
171
    /*-----------------------------------------------------------------
172
     * Validate block type
173
     * Header blocks have a magic cookie at byte 0x100
174
     *----------------------------------------------------------------*/
175
430
    GotoByteInBlock(0x100);
176
430
    const GInt32 nMagicCookie = ReadInt32();
177
430
    if (nMagicCookie != HDR_MAGIC_COOKIE)
178
6
    {
179
6
        CPLError(CE_Failure, CPLE_FileIO,
180
6
                 "ReadFromFile(): Invalid Magic Cookie: got %d expected %d",
181
6
                 nMagicCookie, HDR_MAGIC_COOKIE);
182
6
        CPLFree(m_pabyBuf);
183
6
        m_pabyBuf = nullptr;
184
6
        return -1;
185
6
    }
186
187
    /*-----------------------------------------------------------------
188
     * Init member variables
189
     * Instead of having over 30 get/set methods, we'll make all data
190
     * members public and we will initialize them here.
191
     * For this reason, this class should be used with care.
192
     *----------------------------------------------------------------*/
193
424
    GotoByteInBlock(0x104);
194
424
    m_nMAPVersionNumber = ReadInt16();
195
424
    m_nRegularBlockSize = ReadInt16();
196
424
    if (m_nRegularBlockSize < TAB_MIN_BLOCK_SIZE)
197
1
    {
198
1
        CPLError(CE_Failure, CPLE_FileIO,
199
1
                 "ReadFromFile(): Invalid block size %d", m_nRegularBlockSize);
200
1
        CPLFree(m_pabyBuf);
201
1
        m_pabyBuf = nullptr;
202
1
        return -1;
203
1
    }
204
205
423
    m_dCoordsys2DistUnits = ReadDouble();
206
423
    m_nXMin = ReadInt32();
207
423
    m_nYMin = ReadInt32();
208
423
    m_nXMax = ReadInt32();
209
423
    m_nYMax = ReadInt32();
210
423
    if (m_nXMin > m_nXMax || m_nYMin > m_nYMax)
211
63
    {
212
63
        CPLError(CE_Warning, CPLE_AppDefined,
213
63
                 "Reading corrupted MBR from .map header");
214
63
        CPLErrorReset();
215
63
    }
216
217
423
    GotoByteInBlock(0x130);  // Skip 16 unknown bytes
218
219
423
    m_nFirstIndexBlock = ReadInt32();
220
423
    m_nFirstGarbageBlock = ReadInt32();
221
423
    m_nFirstToolBlock = ReadInt32();
222
223
423
    m_numPointObjects = ReadInt32();
224
423
    m_numLineObjects = ReadInt32();
225
423
    m_numRegionObjects = ReadInt32();
226
423
    m_numTextObjects = ReadInt32();
227
423
    m_nMaxCoordBufSize = ReadInt32();
228
229
423
    GotoByteInBlock(0x15e);  // Skip 14 unknown bytes
230
231
423
    m_nDistUnitsCode = ReadByte();
232
423
    m_nMaxSpIndexDepth = ReadByte();
233
423
    m_nCoordPrecision = ReadByte();
234
423
    m_nCoordOriginQuadrant = ReadByte();
235
423
    m_nReflectXAxisCoord = ReadByte();
236
423
    m_nMaxObjLenArrayId = ReadByte();  // See gabyObjLenArray[]
237
423
    m_numPenDefs = ReadByte();
238
423
    m_numBrushDefs = ReadByte();
239
423
    m_numSymbolDefs = ReadByte();
240
423
    m_numFontDefs = ReadByte();
241
423
    m_numMapToolBlocks = ReadByte();
242
243
423
    ReadByte();  // skip unused byte
244
245
    /* DatumId was never set (always 0) until MapInfo 7.8. See bug 910
246
     * MAP Version Number is 500 in this case.
247
     */
248
423
    if (m_nMAPVersionNumber >= 500)
249
421
        m_sProj.nDatumId = ReadInt16();
250
2
    else
251
2
    {
252
2
        ReadInt16();  // Skip.
253
2
        m_sProj.nDatumId = 0;
254
2
    }
255
423
    ReadByte();  // Skip unknown byte
256
423
    m_sProj.nProjId = ReadByte();
257
423
    m_sProj.nEllipsoidId = ReadByte();
258
423
    m_sProj.nUnitsId = ReadByte();
259
423
    m_XScale = ReadDouble();
260
423
    m_YScale = ReadDouble();
261
423
    if (m_XScale == 0.0 || m_YScale == 0.0)
262
5
    {
263
5
        CPLError(CE_Failure, CPLE_FileIO,
264
5
                 "ReadFromFile(): Null xscale and/or yscale");
265
5
        CPLFree(m_pabyBuf);
266
5
        m_pabyBuf = nullptr;
267
5
        return -1;
268
5
    }
269
418
    m_XDispl = ReadDouble();
270
418
    m_YDispl = ReadDouble();
271
272
    /* In V.100 files, the scale and displacement do not appear to be set.
273
     * we'll use m_nCoordPrecision to define the scale factor instead.
274
     */
275
418
    if (m_nMAPVersionNumber <= 100)
276
1
    {
277
1
        m_XScale = pow(10.0, m_nCoordPrecision);
278
1
        m_YScale = m_XScale;
279
1
        m_XDispl = 0.0;
280
1
        m_YDispl = 0.0;
281
1
    }
282
283
2.92k
    for (int i = 0; i < 6; i++)
284
2.50k
        m_sProj.adProjParams[i] = ReadDouble();
285
286
418
    m_sProj.dDatumShiftX = ReadDouble();
287
418
    m_sProj.dDatumShiftY = ReadDouble();
288
418
    m_sProj.dDatumShiftZ = ReadDouble();
289
2.50k
    for (int i = 0; i < 5; i++)
290
2.09k
    {
291
        /* In V.200 files, the next 5 datum params are unused and they
292
         * sometimes contain junk bytes... in this case we set adDatumParams[]
293
         * to 0 for the rest of the lib to be happy.
294
         */
295
2.09k
        m_sProj.adDatumParams[i] = ReadDouble();
296
2.09k
        if (m_nMAPVersionNumber <= 200)
297
5
            m_sProj.adDatumParams[i] = 0.0;
298
2.09k
    }
299
300
418
    m_sProj.nAffineFlag = 0;
301
418
    if (m_nMAPVersionNumber >= 500 && m_nSizeUsed > TAB_MIN_BLOCK_SIZE)
302
207
    {
303
        // Read Affine parameters A,B,C,D,E,F
304
        // only if version 500+ and block is larger than TAB_MIN_BLOCK_SIZE
305
        // bytes
306
207
        int nInUse = ReadByte();
307
207
        if (nInUse)
308
48
        {
309
48
            m_sProj.nAffineFlag = 1;
310
48
            m_sProj.nAffineUnits = ReadByte();
311
48
            GotoByteInBlock(0x0208);  // Skip unused bytes
312
48
            m_sProj.dAffineParamA = ReadDouble();
313
48
            m_sProj.dAffineParamB = ReadDouble();
314
48
            m_sProj.dAffineParamC = ReadDouble();
315
48
            m_sProj.dAffineParamD = ReadDouble();
316
48
            m_sProj.dAffineParamE = ReadDouble();
317
48
            m_sProj.dAffineParamF = ReadDouble();
318
48
        }
319
207
    }
320
321
418
    if (m_sProj.nProjId == 35 && m_nMAPVersionNumber >= 500 &&
322
418
        m_nSizeUsed >= 0x0268 + 8)
323
0
    {
324
0
        GotoByteInBlock(0x0268);
325
0
        m_sProj.adProjParams[6] = ReadDouble();
326
0
    }
327
328
418
    UpdatePrecision();
329
330
418
    return 0;
331
423
}
332
333
/**********************************************************************
334
 *                   TABMAPHeaderBlock::Int2Coordsys()
335
 *
336
 * Convert from long integer (internal) to coordinates system units
337
 * as defined in the file's coordsys clause.
338
 *
339
 * Note that the false easting/northing and the conversion factor from
340
 * datum to coordsys units are not included in the calculation.
341
 *
342
 * Returns 0 on success, -1 on error.
343
 **********************************************************************/
344
int TABMAPHeaderBlock::Int2Coordsys(GInt32 nX, GInt32 nY, double &dX,
345
                                    double &dY)
346
2.63k
{
347
2.63k
    if (m_pabyBuf == nullptr)
348
0
        return -1;
349
350
    // For some obscure reason, some guy decided that it would be
351
    // more fun to be able to define our own origin quadrant!
352
    //
353
    // In version 100 .tab files (version 400 .map), it is possible to have
354
    // a quadrant 0 and it should be treated the same way as quadrant 3
355
356
2.63k
    if (m_nCoordOriginQuadrant == 2 || m_nCoordOriginQuadrant == 3 ||
357
2.63k
        m_nCoordOriginQuadrant == 0)
358
580
        dX = -1.0 * (nX + m_XDispl) / m_XScale;
359
2.05k
    else
360
2.05k
        dX = (nX - m_XDispl) / m_XScale;
361
362
2.63k
    if (m_nCoordOriginQuadrant == 3 || m_nCoordOriginQuadrant == 4 ||
363
2.63k
        m_nCoordOriginQuadrant == 0)
364
446
        dY = -1.0 * (nY + m_YDispl) / m_YScale;
365
2.19k
    else
366
2.19k
        dY = (nY - m_YDispl) / m_YScale;
367
368
    // Round coordinates to the desired precision
369
2.63k
    if (m_XPrecision > 0 && m_YPrecision > 0)
370
2.55k
    {
371
2.55k
        dX = round(dX * m_XPrecision) / m_XPrecision;
372
2.55k
        dY = round(dY * m_YPrecision) / m_YPrecision;
373
2.55k
    }
374
    // printf("Int2Coordsys: (%d, %d) -> (%.10g, %.10g)\n", nX, nY, dX, dY);
375
376
2.63k
    return 0;
377
2.63k
}
378
379
/**********************************************************************
380
 *                   TABMAPHeaderBlock::Coordsys2Int()
381
 *
382
 * Convert from coordinates system units as defined in the file's
383
 * coordsys clause to long integer (internal) coordinates.
384
 *
385
 * Note that the false easting/northing and the conversion factor from
386
 * datum to coordsys units are not included in the calculation.
387
 *
388
 * Returns 0 on success, -1 on error.
389
 **********************************************************************/
390
int TABMAPHeaderBlock::Coordsys2Int(double dX, double dY, GInt32 &nX,
391
                                    GInt32 &nY,
392
                                    GBool bIgnoreOverflow /*=FALSE*/)
393
2.83k
{
394
2.83k
    if (m_pabyBuf == nullptr)
395
0
        return -1;
396
397
    // For some obscure reason, some guy decided that it would be
398
    // more fun to be able to define our own origin quadrant!
399
    //
400
    // In version 100 .tab files (version 400 .map), it is possible to have
401
    // a quadrant 0 and it should be treated the same way as quadrant 3
402
403
    /*-----------------------------------------------------------------
404
     * NOTE: double values must be used here, the limit of integer value
405
     * have been reached some times due to the very big numbers used here.
406
     *----------------------------------------------------------------*/
407
2.83k
    double dTempX = 0.0;
408
2.83k
    double dTempY = 0.0;
409
410
2.83k
    if (m_nCoordOriginQuadrant == 2 || m_nCoordOriginQuadrant == 3 ||
411
2.83k
        m_nCoordOriginQuadrant == 0)
412
0
        dTempX = -1.0 * dX * m_XScale - m_XDispl;
413
2.83k
    else
414
2.83k
        dTempX = dX * m_XScale + m_XDispl;
415
416
2.83k
    if (m_nCoordOriginQuadrant == 3 || m_nCoordOriginQuadrant == 4 ||
417
2.83k
        m_nCoordOriginQuadrant == 0)
418
0
        dTempY = -1.0 * dY * m_YScale - m_YDispl;
419
2.83k
    else
420
2.83k
        dTempY = dY * m_YScale + m_YDispl;
421
422
    /*-----------------------------------------------------------------
423
     * Make sure we'll never output coordinates outside of the valid
424
     * integer coordinates range: (-1e9, -1e9) - (1e9, 1e9)
425
     * Integer coordinates outside of that range will confuse MapInfo.
426
     *----------------------------------------------------------------*/
427
2.83k
    GBool bIntBoundsOverflow = FALSE;
428
2.83k
    if (dTempX < -1000000000)
429
67
    {
430
67
        dTempX = -1000000000;
431
67
        bIntBoundsOverflow = TRUE;
432
67
    }
433
2.83k
    if (dTempX > 1000000000)
434
80
    {
435
80
        dTempX = 1000000000;
436
80
        bIntBoundsOverflow = TRUE;
437
80
    }
438
2.83k
    if (dTempY < -1000000000)
439
116
    {
440
116
        dTempY = -1000000000;
441
116
        bIntBoundsOverflow = TRUE;
442
116
    }
443
2.83k
    if (dTempY > 1000000000)
444
87
    {
445
87
        dTempY = 1000000000;
446
87
        bIntBoundsOverflow = TRUE;
447
87
    }
448
449
2.83k
    nX = static_cast<GInt32>(ROUND_INT(dTempX));
450
2.83k
    nY = static_cast<GInt32>(ROUND_INT(dTempY));
451
452
2.83k
    if (bIntBoundsOverflow && !bIgnoreOverflow)
453
290
    {
454
290
        m_bIntBoundsOverflow = TRUE;
455
#ifdef DEBUG
456
        CPLError(
457
            CE_Warning, static_cast<CPLErrorNum>(TAB_WarningBoundsOverflow),
458
            "Integer bounds overflow: (%f, %f) -> (%d, %d)\n", dX, dY, nX, nY);
459
#endif
460
290
    }
461
462
2.83k
    return 0;
463
2.83k
}
464
465
/**********************************************************************
466
 *                   TABMAPHeaderBlock::ComprInt2Coordsys()
467
 *
468
 * Convert from compressed integer (internal) to coordinates system units
469
 * as defined in the file's coordsys clause.
470
 * The difference between long integer and compressed integer coords is
471
 * that compressed coordinates are scaled displacement relative to an
472
 * object centroid.
473
 *
474
 * Note that the false easting/northing and the conversion factor from
475
 * datum to coordsys units are not included in the calculation.
476
 *
477
 * Returns 0 on success, -1 on error.
478
 **********************************************************************/
479
int TABMAPHeaderBlock::ComprInt2Coordsys(GInt32 nCenterX, GInt32 nCenterY,
480
                                         int nDeltaX, int nDeltaY, double &dX,
481
                                         double &dY)
482
0
{
483
0
    if (m_pabyBuf == nullptr)
484
0
        return -1;
485
486
0
    return Int2Coordsys(nCenterX + nDeltaX, nCenterY + nDeltaY, dX, dY);
487
0
}
488
489
/**********************************************************************
490
 *                   TABMAPHeaderBlock::Int2CoordsysDist()
491
 *
492
 * Convert a pair of X and Y size (or distance) value from long integer
493
 * (internal) to coordinates system units as defined in the file's
494
 * coordsys clause.
495
 *
496
 * The difference with Int2Coordsys() is that this function only applies
497
 * the scaling factor: it does not apply the displacement.
498
 *
499
 * Since the calculations on the X and Y values are independent, either
500
 * one can be omitted (i.e. passed as 0)
501
 *
502
 * Returns 0 on success, -1 on error.
503
 **********************************************************************/
504
int TABMAPHeaderBlock::Int2CoordsysDist(GInt32 nX, GInt32 nY, double &dX,
505
                                        double &dY)
506
62
{
507
62
    if (m_pabyBuf == nullptr)
508
0
        return -1;
509
510
62
    dX = nX / m_XScale;
511
62
    dY = nY / m_YScale;
512
513
62
    return 0;
514
62
}
515
516
/**********************************************************************
517
 *                   TABMAPHeaderBlock::Coordsys2IntDist()
518
 *
519
 * Convert a pair of X and Y size (or distance) values from coordinates
520
 * system units as defined in the file's coordsys clause to long integer
521
 * (internal) coordinates.
522
 *
523
 * The difference with Coordsys2Int() is that this function only applies
524
 * the scaling factor: it does not apply the displacement.
525
 *
526
 * Since the calculations on the X and Y values are independent, either
527
 * one can be omitted (i.e. passed as 0)
528
 *
529
 * Returns 0 on success, -1 on error.
530
 **********************************************************************/
531
int TABMAPHeaderBlock::Coordsys2IntDist(double dX, double dY, GInt32 &nX,
532
                                        GInt32 &nY)
533
0
{
534
0
    if (m_pabyBuf == nullptr)
535
0
        return -1;
536
537
0
    nX = static_cast<GInt32>(dX * m_XScale);
538
0
    nY = static_cast<GInt32>(dY * m_YScale);
539
540
0
    return 0;
541
0
}
542
543
/**********************************************************************
544
 *                   TABMAPHeaderBlock::SetCoordsysBounds()
545
 *
546
 * Take projection coordinates bounds of the newly created dataset and
547
 * compute new values for the X/Y Scales and X/Y displacement.
548
 *
549
 * This function must be called after creating a new dataset and before any
550
 * of the coordinates conversion functions can be used.
551
 *
552
 * Returns 0 on success, -1 on error.
553
 **********************************************************************/
554
int TABMAPHeaderBlock::SetCoordsysBounds(double dXMin, double dYMin,
555
                                         double dXMax, double dYMax)
556
28
{
557
    // printf("SetCoordsysBounds(%10g, %10g, %10g, %10g)\n", dXMin, dYMin,
558
    // dXMax, dYMax);
559
    /*-----------------------------------------------------------------
560
     * Check for 0-width or 0-height bounds
561
     *----------------------------------------------------------------*/
562
28
    if (dXMax == dXMin)
563
0
    {
564
0
        dXMin -= 1.0;
565
0
        dXMax += 1.0;
566
0
    }
567
568
28
    if (dYMax == dYMin)
569
0
    {
570
0
        dYMin -= 1.0;
571
0
        dYMax += 1.0;
572
0
    }
573
574
    /*-----------------------------------------------------------------
575
     * X and Y scales are used to map coordsys coordinates to integer
576
     * internal coordinates.  We want to find the scale and displacement
577
     * values that will result in an integer coordinate range of
578
     * (-1e9, -1e9) - (1e9, 1e9)
579
     *
580
     * Note that we ALWAYS generate datasets with the OriginQuadrant = 1
581
     * so that we avoid reverted X/Y axis complications, etc.
582
     *----------------------------------------------------------------*/
583
28
    m_XScale = 2e9 / (dXMax - dXMin);
584
28
    m_YScale = 2e9 / (dYMax - dYMin);
585
586
28
    m_XDispl = -1.0 * m_XScale * (dXMax + dXMin) / 2;
587
28
    m_YDispl = -1.0 * m_YScale * (dYMax + dYMin) / 2;
588
589
28
    m_nXMin = -1000000000;
590
28
    m_nYMin = -1000000000;
591
28
    m_nXMax = 1000000000;
592
28
    m_nYMax = 1000000000;
593
594
28
    UpdatePrecision();
595
596
28
    return 0;
597
28
}
598
599
/**********************************************************************
600
 *                   TABMAPHeaderBlock::GetMapObjectSize()
601
 *
602
 * Return the size of the object body for the specified object type.
603
 * The value is looked up in the first 256 bytes of the header.
604
 **********************************************************************/
605
int TABMAPHeaderBlock::GetMapObjectSize(int nObjType)
606
784
{
607
784
    if (m_pabyBuf == nullptr)
608
0
    {
609
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
610
0
                 "Block has not been initialized yet!");
611
0
        return -1;
612
0
    }
613
614
784
    if (nObjType < 0 || nObjType > 255)
615
0
    {
616
0
        CPLError(CE_Failure, CPLE_IllegalArg, "Invalid object type %d",
617
0
                 nObjType);
618
0
        return -1;
619
0
    }
620
621
    // Byte 0x80 is set for objects that have coordinates inside type 3 blocks
622
784
    return m_pabyBuf[nObjType] & 0x7f;
623
784
}
624
625
/**********************************************************************
626
 *                   TABMAPHeaderBlock::MapObjectUsesCoordBlock()
627
 *
628
 * Return TRUE if the specified map object type has coordinates stored
629
 * inside type 3 coordinate blocks.
630
 * The info is looked up in the first 256 bytes of the header.
631
 **********************************************************************/
632
GBool TABMAPHeaderBlock::MapObjectUsesCoordBlock(int nObjType)
633
784
{
634
784
    if (m_pabyBuf == nullptr)
635
0
    {
636
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
637
0
                 "Block has not been initialized yet!");
638
0
        return FALSE;
639
0
    }
640
641
784
    if (nObjType < 0 || nObjType > 255)
642
0
    {
643
0
        CPLError(CE_Failure, CPLE_IllegalArg, "Invalid object type %d",
644
0
                 nObjType);
645
0
        return FALSE;
646
0
    }
647
648
    // Byte 0x80 is set for objects that have coordinates inside type 3 blocks
649
650
784
    return ((m_pabyBuf[nObjType] & 0x80) != 0) ? TRUE : FALSE;
651
784
}
652
653
/**********************************************************************
654
 *                   TABMAPHeaderBlock::GetProjInfo()
655
 *
656
 * Fill the psProjInfo structure with the projection parameters previously
657
 * read from this header block.
658
 *
659
 * Returns 0 on success, -1 on error.
660
 **********************************************************************/
661
int TABMAPHeaderBlock::GetProjInfo(TABProjInfo *psProjInfo)
662
173
{
663
173
    if (m_pabyBuf == nullptr)
664
0
    {
665
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
666
0
                 "Block has not been initialized yet!");
667
0
        return -1;
668
0
    }
669
670
173
    if (psProjInfo)
671
173
        *psProjInfo = m_sProj;
672
673
173
    return 0;
674
173
}
675
676
/**********************************************************************
677
 *                   TABMAPHeaderBlock::SetProjInfo()
678
 *
679
 * Set the projection parameters for this dataset.
680
 *
681
 * Returns 0 on success, -1 on error.
682
 **********************************************************************/
683
int TABMAPHeaderBlock::SetProjInfo(TABProjInfo *psProjInfo)
684
2
{
685
2
    if (m_pabyBuf == nullptr)
686
0
    {
687
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
688
0
                 "Block has not been initialized yet!");
689
0
        return -1;
690
0
    }
691
692
2
    if (psProjInfo)
693
2
        m_sProj = *psProjInfo;
694
695
2
    return 0;
696
2
}
697
698
/**********************************************************************
699
 *                   TABMAPHeaderBlock::CommitToFile()
700
 *
701
 * Commit the current state of the binary block to the file to which
702
 * it has been previously attached.
703
 *
704
 * This method makes sure all values are properly set in the header
705
 * block buffer and then calls TABRawBinBlock::CommitToFile() to do
706
 * the actual writing to disk.
707
 *
708
 * Returns 0 if successful or -1 if an error happened, in which case
709
 * CPLError() will have been called.
710
 **********************************************************************/
711
int TABMAPHeaderBlock::CommitToFile()
712
28
{
713
28
    int i;
714
715
28
    if (m_pabyBuf == nullptr || m_nRegularBlockSize == 0)
716
0
    {
717
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
718
0
                 "TABRawBinBlock::CommitToFile(): Block has not been "
719
0
                 "initialized yet!");
720
0
        return -1;
721
0
    }
722
723
    /*-----------------------------------------------------------------
724
     * Reconstruct header to make sure it is in sync with members variables.
725
     *----------------------------------------------------------------*/
726
28
    GotoByteInBlock(0x000);
727
28
    WriteBytes(HDR_OBJ_LEN_ARRAY_SIZE, gabyObjLenArray);
728
28
    m_nMaxObjLenArrayId = HDR_OBJ_LEN_ARRAY_SIZE - 1;
729
730
28
    GotoByteInBlock(0x100);
731
28
    WriteInt32(HDR_MAGIC_COOKIE);
732
733
28
    if (m_sProj.nAffineFlag && m_nMAPVersionNumber < 500)
734
0
    {
735
        // Must be at least version 500 to support affine params
736
        // Default value for HDR_VERSION_NUMBER is 500 so this error should
737
        // never happen unless the caller changed the value, in which case they
738
        // deserve to get a failure
739
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
740
0
                 "TABRawBinBlock::CommitToFile(): .MAP version 500 or more is "
741
0
                 "required for affine projection parameter support.");
742
0
        return -1;
743
0
    }
744
745
28
    WriteInt16(m_nMAPVersionNumber);
746
747
28
    WriteInt16(m_nRegularBlockSize);
748
749
28
    WriteDouble(m_dCoordsys2DistUnits);
750
28
    WriteInt32(m_nXMin);
751
28
    WriteInt32(m_nYMin);
752
28
    WriteInt32(m_nXMax);
753
28
    WriteInt32(m_nYMax);
754
28
    if (m_nXMin > m_nXMax || m_nYMin > m_nYMax)
755
0
    {
756
0
        CPLError(CE_Warning, CPLE_AppDefined,
757
0
                 "Writing corrupted MBR into .map header");
758
0
    }
759
760
28
    WriteZeros(16);  // ???
761
762
28
    WriteInt32(m_nFirstIndexBlock);
763
28
    WriteInt32(m_nFirstGarbageBlock);
764
28
    WriteInt32(m_nFirstToolBlock);
765
766
28
    WriteInt32(m_numPointObjects);
767
28
    WriteInt32(m_numLineObjects);
768
28
    WriteInt32(m_numRegionObjects);
769
28
    WriteInt32(m_numTextObjects);
770
28
    WriteInt32(m_nMaxCoordBufSize);
771
772
28
    WriteZeros(14);  // ???
773
774
28
    WriteByte(m_nDistUnitsCode);
775
28
    WriteByte(m_nMaxSpIndexDepth);
776
28
    WriteByte(m_nCoordPrecision);
777
28
    WriteByte(m_nCoordOriginQuadrant);
778
28
    WriteByte(m_nReflectXAxisCoord);
779
28
    WriteByte(m_nMaxObjLenArrayId);  // See gabyObjLenArray[]
780
28
    WriteByte(m_numPenDefs);
781
28
    WriteByte(m_numBrushDefs);
782
28
    WriteByte(m_numSymbolDefs);
783
28
    WriteByte(m_numFontDefs);
784
28
    CPLAssert(m_numMapToolBlocks >= 0 && m_numMapToolBlocks <= 255);
785
28
    WriteByte(static_cast<GByte>(m_numMapToolBlocks));
786
787
28
    WriteZeros(1);  // unused byte
788
28
    WriteInt16(m_sProj.nDatumId);
789
28
    WriteZeros(1);  // unused byte
790
791
28
    WriteByte(m_sProj.nProjId);
792
28
    WriteByte(m_sProj.nEllipsoidId);
793
28
    WriteByte(m_sProj.nUnitsId);
794
28
    WriteDouble(m_XScale);
795
28
    WriteDouble(m_YScale);
796
28
    WriteDouble(m_XDispl);
797
28
    WriteDouble(m_YDispl);
798
799
196
    for (i = 0; i < 6; i++)
800
168
        WriteDouble(m_sProj.adProjParams[i]);
801
802
28
    WriteDouble(m_sProj.dDatumShiftX);
803
28
    WriteDouble(m_sProj.dDatumShiftY);
804
28
    WriteDouble(m_sProj.dDatumShiftZ);
805
168
    for (i = 0; i < 5; i++)
806
140
        WriteDouble(m_sProj.adDatumParams[i]);
807
808
28
    if (m_sProj.nAffineFlag)
809
0
    {
810
0
        WriteByte(1);  // In Use Flag
811
0
        WriteByte(m_sProj.nAffineUnits);
812
0
        WriteZeros(6);
813
0
        WriteDouble(m_sProj.dAffineParamA);
814
0
        WriteDouble(m_sProj.dAffineParamB);
815
0
        WriteDouble(m_sProj.dAffineParamC);
816
0
        WriteDouble(m_sProj.dAffineParamD);
817
0
        WriteDouble(m_sProj.dAffineParamE);
818
0
        WriteDouble(m_sProj.dAffineParamF);
819
820
0
        WriteZeros(456);  // Pad rest of block with zeros (Bounds info here ?)
821
0
    }
822
823
28
    if (m_nMAPVersionNumber >= 500 && m_nBlockSize == 1024 &&
824
28
        m_sProj.nProjId == 35)
825
0
    {
826
0
        const auto nCurPosBak = m_nCurPos;
827
0
        if (m_nCurPos == 512)
828
0
            WriteZeros(512);
829
0
        m_nCurPos = 0x0268;
830
0
        WriteDouble(m_sProj.adProjParams[6]);
831
0
        m_nCurPos = nCurPosBak;
832
0
    }
833
834
    /*-----------------------------------------------------------------
835
     * OK, call the base class to write the block to disk.
836
     *----------------------------------------------------------------*/
837
#ifdef DEBUG_VERBOSE
838
    CPLDebug("MITAB", "Committing HEADER block to offset %d", m_nFileOffset);
839
#endif
840
28
    return TABRawBinBlock::CommitToFile();
841
28
}
842
843
/**********************************************************************
844
 *                   TABMAPHeaderBlock::InitNewBlock()
845
 *
846
 * Initialize a newly created block so that it knows to which file it
847
 * is attached, its block size, etc . and then perform any specific
848
 * initialization for this block type, including writing a default
849
 * block header, etc. and leave the block ready to receive data.
850
 *
851
 * This is an alternative to calling ReadFromFile() or InitBlockFromData()
852
 * that puts the block in a stable state without loading any initial
853
 * data in it.
854
 *
855
 * Returns 0 if successful or -1 if an error happened, in which case
856
 * CPLError() will have been called.
857
 **********************************************************************/
858
int TABMAPHeaderBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
859
                                    int nFileOffset /* = 0*/)
860
467
{
861
    /*-----------------------------------------------------------------
862
     * Start with the default initialization
863
     *----------------------------------------------------------------*/
864
865
    /* .MAP files of Version 500 and up appear to have a 1024 bytes
866
     * header.  The last 512 bytes are usually all zeros. */
867
467
    if (TABRawBinBlock::InitNewBlock(fpSrc, 1024, nFileOffset) != 0)
868
0
        return -1;
869
870
    /*-----------------------------------------------------------------
871
     * Set acceptable default values for member vars.
872
     *----------------------------------------------------------------*/
873
467
    InitMembersWithDefaultValues();
874
875
467
    CPLAssert(nBlockSize >= 0 && nBlockSize <= 32767);
876
467
    m_nRegularBlockSize = static_cast<GInt16>(nBlockSize);
877
878
    /*-----------------------------------------------------------------
879
     * And Set the map object length array in the buffer...
880
     *----------------------------------------------------------------*/
881
467
    if (m_eAccess != TABRead)
882
28
    {
883
28
        GotoByteInBlock(0x000);
884
28
        WriteBytes(HDR_OBJ_LEN_ARRAY_SIZE, gabyObjLenArray);
885
28
    }
886
887
467
    if (CPLGetLastErrorType() == CE_Failure)
888
0
        return -1;
889
890
467
    return 0;
891
467
}
892
893
/**********************************************************************
894
 * TABMAPHeaderBlock::UpdatePrecision()
895
 *
896
 * Update x and y maximum achievable precision given current scales
897
 * (m_XScale and m_YScale)
898
 **********************************************************************/
899
void TABMAPHeaderBlock::UpdatePrecision()
900
446
{
901
446
    m_XPrecision = pow(10.0, round(log10(m_XScale)));
902
446
    m_YPrecision = pow(10.0, round(log10(m_YScale)));
903
446
}
904
905
/**********************************************************************
906
 *                   TABMAPHeaderBlock::Dump()
907
 *
908
 * Dump block contents... available only in DEBUG mode.
909
 **********************************************************************/
910
#ifdef DEBUG
911
912
void TABMAPHeaderBlock::Dump(FILE *fpOut /*=NULL*/)
913
{
914
    if (fpOut == nullptr)
915
        fpOut = stdout;
916
917
    fprintf(fpOut, "----- TABMAPHeaderBlock::Dump() -----\n");
918
919
    if (m_pabyBuf == nullptr)
920
    {
921
        fprintf(fpOut, "Block has not been initialized yet.");
922
    }
923
    else
924
    {
925
        fprintf(fpOut, "Version %d header block.\n", m_nMAPVersionNumber);
926
        fprintf(fpOut, "  m_nRegularBlockSize       = %d\n",
927
                m_nRegularBlockSize);
928
        fprintf(fpOut, "  m_nFirstIndexBlock    = %d\n", m_nFirstIndexBlock);
929
        fprintf(fpOut, "  m_nFirstGarbageBlock  = %d\n", m_nFirstGarbageBlock);
930
        fprintf(fpOut, "  m_nFirstToolBlock     = %d\n", m_nFirstToolBlock);
931
        fprintf(fpOut, "  m_numPointObjects     = %d\n", m_numPointObjects);
932
        fprintf(fpOut, "  m_numLineObjects      = %d\n", m_numLineObjects);
933
        fprintf(fpOut, "  m_numRegionObjects    = %d\n", m_numRegionObjects);
934
        fprintf(fpOut, "  m_numTextObjects      = %d\n", m_numTextObjects);
935
        fprintf(fpOut, "  m_nMaxCoordBufSize    = %d\n", m_nMaxCoordBufSize);
936
937
        fprintf(fpOut, "\n");
938
        fprintf(fpOut, "  m_dCoordsys2DistUnits = %g\n", m_dCoordsys2DistUnits);
939
        fprintf(fpOut, "  m_nXMin               = %d\n", m_nXMin);
940
        fprintf(fpOut, "  m_nYMin               = %d\n", m_nYMin);
941
        fprintf(fpOut, "  m_nXMax               = %d\n", m_nXMax);
942
        fprintf(fpOut, "  m_nYMax               = %d\n", m_nYMax);
943
        fprintf(fpOut, "  m_XScale              = %g\n", m_XScale);
944
        fprintf(fpOut, "  m_YScale              = %g\n", m_YScale);
945
        fprintf(fpOut, "  m_XDispl              = %g\n", m_XDispl);
946
        fprintf(fpOut, "  m_YDispl              = %g\n", m_YDispl);
947
948
        fprintf(fpOut, "\n");
949
        fprintf(fpOut, "  m_nDistUnistCode      = %d\n", m_nDistUnitsCode);
950
        fprintf(fpOut, "  m_nMaxSpIndexDepth    = %d\n", m_nMaxSpIndexDepth);
951
        fprintf(fpOut, "  m_nCoordPrecision     = %d\n", m_nCoordPrecision);
952
        fprintf(fpOut, "  m_nCoordOriginQuadrant= %d\n",
953
                m_nCoordOriginQuadrant);
954
        fprintf(fpOut, "  m_nReflecXAxisCoord   = %d\n", m_nReflectXAxisCoord);
955
        fprintf(fpOut, "  m_nMaxObjLenArrayId   = %d\n", m_nMaxObjLenArrayId);
956
        fprintf(fpOut, "  m_numPenDefs          = %d\n", m_numPenDefs);
957
        fprintf(fpOut, "  m_numBrushDefs        = %d\n", m_numBrushDefs);
958
        fprintf(fpOut, "  m_numSymbolDefs       = %d\n", m_numSymbolDefs);
959
        fprintf(fpOut, "  m_numFontDefs         = %d\n", m_numFontDefs);
960
        fprintf(fpOut, "  m_numMapToolBlocks    = %d\n", m_numMapToolBlocks);
961
962
        fprintf(fpOut, "\n");
963
        fprintf(fpOut, "  m_sProj.nDatumId      = %d\n", m_sProj.nDatumId);
964
        fprintf(fpOut, "  m_sProj.nProjId       = %d\n",
965
                static_cast<int>(m_sProj.nProjId));
966
        fprintf(fpOut, "  m_sProj.nEllipsoidId  = %d\n",
967
                static_cast<int>(m_sProj.nEllipsoidId));
968
        fprintf(fpOut, "  m_sProj.nUnitsId      = %d\n",
969
                static_cast<int>(m_sProj.nUnitsId));
970
        fprintf(fpOut, "  m_sProj.adProjParams  =");
971
        for (int i = 0; i < 6; i++)
972
            fprintf(fpOut, " %g", m_sProj.adProjParams[i]);
973
        fprintf(fpOut, "\n");
974
975
        fprintf(fpOut, "  m_sProj.dDatumShiftX  = %.15g\n",
976
                m_sProj.dDatumShiftX);
977
        fprintf(fpOut, "  m_sProj.dDatumShiftY  = %.15g\n",
978
                m_sProj.dDatumShiftY);
979
        fprintf(fpOut, "  m_sProj.dDatumShiftZ  = %.15g\n",
980
                m_sProj.dDatumShiftZ);
981
        fprintf(fpOut, "  m_sProj.adDatumParams =");
982
        for (int i = 0; i < 5; i++)
983
            fprintf(fpOut, " %.15g", m_sProj.adDatumParams[i]);
984
        fprintf(fpOut, "\n");
985
986
        // Dump array of map object lengths... optional
987
        if (FALSE)
988
        {
989
            fprintf(fpOut,
990
                    "-- Header bytes 00-FF: Array of map object lengths --\n");
991
            for (int i = 0; i < 256; i++)
992
            {
993
                fprintf(fpOut, "0x%2.2x", static_cast<int>(m_pabyBuf[i]));
994
                if (i != 255)
995
                    fprintf(fpOut, ",");
996
                if ((i + 1) % 16 == 0)
997
                    fprintf(fpOut, "\n");
998
            }
999
        }
1000
    }
1001
1002
    fflush(fpOut);
1003
}
1004
1005
#endif  // DEBUG