Line data Source code
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 2759 : TABMAPHeaderBlock::TABMAPHeaderBlock(TABAccess eAccessMode /*= TABRead*/)
60 2759 : : TABRawBinBlock(eAccessMode, TRUE)
61 : {
62 : // TODO(schwehr): Consider using initializer list for most values.
63 2759 : InitMembersWithDefaultValues();
64 :
65 : // We don't want to reset it once it is set.
66 2759 : m_bIntBoundsOverflow = FALSE;
67 2759 : }
68 :
69 : /**********************************************************************
70 : * TABMAPHeaderBlock::~TABMAPHeaderBlock()
71 : *
72 : * Destructor.
73 : **********************************************************************/
74 5518 : TABMAPHeaderBlock::~TABMAPHeaderBlock()
75 : {
76 5518 : }
77 :
78 : /**********************************************************************
79 : * TABMAPHeaderBlock::InitMembersWithDefaultValues()
80 : **********************************************************************/
81 2884 : void TABMAPHeaderBlock::InitMembersWithDefaultValues()
82 : {
83 : /*-----------------------------------------------------------------
84 : * Set acceptable default values for member vars.
85 : *----------------------------------------------------------------*/
86 2884 : m_nMAPVersionNumber = HDR_VERSION_NUMBER;
87 2884 : m_nRegularBlockSize = TAB_MIN_BLOCK_SIZE;
88 :
89 2884 : m_dCoordsys2DistUnits = 1.0;
90 2884 : m_nXMin = -1000000000;
91 2884 : m_nYMin = -1000000000;
92 2884 : m_nXMax = 1000000000;
93 2884 : m_nYMax = 1000000000;
94 2884 : m_bIntBoundsOverflow = FALSE;
95 :
96 2884 : m_nFirstIndexBlock = 0;
97 2884 : m_nFirstGarbageBlock = 0;
98 2884 : m_nFirstToolBlock = 0;
99 :
100 2884 : m_numPointObjects = 0;
101 2884 : m_numLineObjects = 0;
102 2884 : m_numRegionObjects = 0;
103 2884 : m_numTextObjects = 0;
104 2884 : m_nMaxCoordBufSize = 0;
105 :
106 2884 : m_nDistUnitsCode = 7; // Meters
107 2884 : m_nMaxSpIndexDepth = 0;
108 2884 : m_nCoordPrecision = 3; // ??? 3 Digits of precision
109 2884 : m_nCoordOriginQuadrant = HDR_DEF_ORG_QUADRANT; // ??? N-E quadrant
110 2884 : m_nReflectXAxisCoord = HDR_DEF_REFLECTXAXIS;
111 2884 : m_nMaxObjLenArrayId = HDR_OBJ_LEN_ARRAY_SIZE - 1; // See gabyObjLenArray[]
112 2884 : m_numPenDefs = 0;
113 2884 : m_numBrushDefs = 0;
114 2884 : m_numSymbolDefs = 0;
115 2884 : m_numFontDefs = 0;
116 2884 : m_numMapToolBlocks = 0;
117 :
118 2884 : m_sProj.nProjId = 0;
119 2884 : m_sProj.nEllipsoidId = 0;
120 2884 : m_sProj.nUnitsId = 7;
121 2884 : m_sProj.nDatumId = 0;
122 2884 : m_XScale = 1000.0; // Default coord range (before SetCoordSysBounds())
123 2884 : m_YScale = 1000.0; // will be [-1000000.000 .. 1000000.000]
124 2884 : m_XDispl = 0.0;
125 2884 : m_YDispl = 0.0;
126 2884 : m_XPrecision = 0.0; // not specified
127 2884 : m_YPrecision = 0.0; // not specified
128 :
129 20188 : for (int i = 0; i < 6; i++)
130 17304 : m_sProj.adProjParams[i] = 0.0;
131 :
132 2884 : m_sProj.dDatumShiftX = 0.0;
133 2884 : m_sProj.dDatumShiftY = 0.0;
134 2884 : m_sProj.dDatumShiftZ = 0.0;
135 17304 : for (int i = 0; i < 5; i++)
136 14420 : m_sProj.adDatumParams[i] = 0.0;
137 :
138 2884 : m_sProj.nAffineFlag = 0; // Only in version 500 and up
139 2884 : m_sProj.nAffineUnits = 7;
140 2884 : m_sProj.dAffineParamA = 0.0;
141 2884 : m_sProj.dAffineParamB = 0.0;
142 2884 : m_sProj.dAffineParamC = 0.0;
143 2884 : m_sProj.dAffineParamD = 0.0;
144 2884 : m_sProj.dAffineParamE = 0.0;
145 2884 : m_sProj.dAffineParamF = 0.0;
146 2884 : }
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 2634 : int TABMAPHeaderBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
158 : int nSizeUsed,
159 : GBool bMakeCopy /* = TRUE */,
160 : VSILFILE *fpSrc /* = NULL */,
161 : int nOffset /* = 0 */)
162 : {
163 : /*-----------------------------------------------------------------
164 : * First of all, we must call the base class' InitBlockFromData()
165 : *----------------------------------------------------------------*/
166 2634 : const int nStatus = TABRawBinBlock::InitBlockFromData(
167 : pabyBuf, nBlockSize, nSizeUsed, bMakeCopy, fpSrc, nOffset);
168 2634 : 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 2634 : GotoByteInBlock(0x100);
176 2634 : const GInt32 nMagicCookie = ReadInt32();
177 2634 : if (nMagicCookie != HDR_MAGIC_COOKIE)
178 : {
179 0 : CPLError(CE_Failure, CPLE_FileIO,
180 : "ReadFromFile(): Invalid Magic Cookie: got %d expected %d",
181 : nMagicCookie, HDR_MAGIC_COOKIE);
182 0 : CPLFree(m_pabyBuf);
183 0 : m_pabyBuf = nullptr;
184 0 : return -1;
185 : }
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 2634 : GotoByteInBlock(0x104);
194 2634 : m_nMAPVersionNumber = ReadInt16();
195 2634 : m_nRegularBlockSize = ReadInt16();
196 2634 : if (m_nRegularBlockSize < TAB_MIN_BLOCK_SIZE)
197 : {
198 0 : CPLError(CE_Failure, CPLE_FileIO,
199 0 : "ReadFromFile(): Invalid block size %d", m_nRegularBlockSize);
200 0 : CPLFree(m_pabyBuf);
201 0 : m_pabyBuf = nullptr;
202 0 : return -1;
203 : }
204 :
205 2634 : m_dCoordsys2DistUnits = ReadDouble();
206 2634 : m_nXMin = ReadInt32();
207 2634 : m_nYMin = ReadInt32();
208 2634 : m_nXMax = ReadInt32();
209 2634 : m_nYMax = ReadInt32();
210 2634 : if (m_nXMin > m_nXMax || m_nYMin > m_nYMax)
211 : {
212 0 : CPLError(CE_Warning, CPLE_AppDefined,
213 : "Reading corrupted MBR from .map header");
214 0 : CPLErrorReset();
215 : }
216 :
217 2634 : GotoByteInBlock(0x130); // Skip 16 unknown bytes
218 :
219 2634 : m_nFirstIndexBlock = ReadInt32();
220 2634 : m_nFirstGarbageBlock = ReadInt32();
221 2634 : m_nFirstToolBlock = ReadInt32();
222 :
223 2634 : m_numPointObjects = ReadInt32();
224 2634 : m_numLineObjects = ReadInt32();
225 2634 : m_numRegionObjects = ReadInt32();
226 2634 : m_numTextObjects = ReadInt32();
227 2634 : m_nMaxCoordBufSize = ReadInt32();
228 :
229 2634 : GotoByteInBlock(0x15e); // Skip 14 unknown bytes
230 :
231 2634 : m_nDistUnitsCode = ReadByte();
232 2634 : m_nMaxSpIndexDepth = ReadByte();
233 2634 : m_nCoordPrecision = ReadByte();
234 2634 : m_nCoordOriginQuadrant = ReadByte();
235 2634 : m_nReflectXAxisCoord = ReadByte();
236 2634 : m_nMaxObjLenArrayId = ReadByte(); // See gabyObjLenArray[]
237 2634 : m_numPenDefs = ReadByte();
238 2634 : m_numBrushDefs = ReadByte();
239 2634 : m_numSymbolDefs = ReadByte();
240 2634 : m_numFontDefs = ReadByte();
241 2634 : m_numMapToolBlocks = ReadByte();
242 :
243 2634 : 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 2634 : if (m_nMAPVersionNumber >= 500)
249 2634 : m_sProj.nDatumId = ReadInt16();
250 : else
251 : {
252 0 : ReadInt16(); // Skip.
253 0 : m_sProj.nDatumId = 0;
254 : }
255 2634 : ReadByte(); // Skip unknown byte
256 2634 : m_sProj.nProjId = ReadByte();
257 2634 : m_sProj.nEllipsoidId = ReadByte();
258 2634 : m_sProj.nUnitsId = ReadByte();
259 2634 : m_XScale = ReadDouble();
260 2634 : m_YScale = ReadDouble();
261 2634 : if (m_XScale == 0.0 || m_YScale == 0.0)
262 : {
263 0 : CPLError(CE_Failure, CPLE_FileIO,
264 : "ReadFromFile(): Null xscale and/or yscale");
265 0 : CPLFree(m_pabyBuf);
266 0 : m_pabyBuf = nullptr;
267 0 : return -1;
268 : }
269 2634 : m_XDispl = ReadDouble();
270 2634 : 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 2634 : if (m_nMAPVersionNumber <= 100)
276 : {
277 0 : m_XScale = pow(10.0, m_nCoordPrecision);
278 0 : m_YScale = m_XScale;
279 0 : m_XDispl = 0.0;
280 0 : m_YDispl = 0.0;
281 : }
282 :
283 18438 : for (int i = 0; i < 6; i++)
284 15804 : m_sProj.adProjParams[i] = ReadDouble();
285 :
286 2634 : m_sProj.dDatumShiftX = ReadDouble();
287 2634 : m_sProj.dDatumShiftY = ReadDouble();
288 2634 : m_sProj.dDatumShiftZ = ReadDouble();
289 15804 : for (int i = 0; i < 5; i++)
290 : {
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 13170 : m_sProj.adDatumParams[i] = ReadDouble();
296 13170 : if (m_nMAPVersionNumber <= 200)
297 0 : m_sProj.adDatumParams[i] = 0.0;
298 : }
299 :
300 2634 : m_sProj.nAffineFlag = 0;
301 2634 : if (m_nMAPVersionNumber >= 500 && m_nSizeUsed > TAB_MIN_BLOCK_SIZE)
302 : {
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 1317 : int nInUse = ReadByte();
307 1317 : if (nInUse)
308 : {
309 0 : m_sProj.nAffineFlag = 1;
310 0 : m_sProj.nAffineUnits = ReadByte();
311 0 : GotoByteInBlock(0x0208); // Skip unused bytes
312 0 : m_sProj.dAffineParamA = ReadDouble();
313 0 : m_sProj.dAffineParamB = ReadDouble();
314 0 : m_sProj.dAffineParamC = ReadDouble();
315 0 : m_sProj.dAffineParamD = ReadDouble();
316 0 : m_sProj.dAffineParamE = ReadDouble();
317 0 : m_sProj.dAffineParamF = ReadDouble();
318 : }
319 : }
320 :
321 2634 : if (m_sProj.nProjId == 35 && m_nMAPVersionNumber >= 500 &&
322 2 : m_nSizeUsed >= 0x0268 + 8)
323 : {
324 1 : GotoByteInBlock(0x0268);
325 1 : m_sProj.adProjParams[6] = ReadDouble();
326 : }
327 :
328 2634 : UpdatePrecision();
329 :
330 2634 : return 0;
331 : }
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 628123 : int TABMAPHeaderBlock::Int2Coordsys(GInt32 nX, GInt32 nY, double &dX,
345 : double &dY)
346 : {
347 628123 : 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 628123 : if (m_nCoordOriginQuadrant == 2 || m_nCoordOriginQuadrant == 3 ||
357 628118 : m_nCoordOriginQuadrant == 0)
358 5 : dX = -1.0 * (nX + m_XDispl) / m_XScale;
359 : else
360 628118 : dX = (nX - m_XDispl) / m_XScale;
361 :
362 628123 : if (m_nCoordOriginQuadrant == 3 || m_nCoordOriginQuadrant == 4 ||
363 628123 : m_nCoordOriginQuadrant == 0)
364 0 : dY = -1.0 * (nY + m_YDispl) / m_YScale;
365 : else
366 628123 : dY = (nY - m_YDispl) / m_YScale;
367 :
368 : // Round coordinates to the desired precision
369 628123 : if (m_XPrecision > 0 && m_YPrecision > 0)
370 : {
371 628113 : dX = round(dX * m_XPrecision) / m_XPrecision;
372 628113 : dY = round(dY * m_YPrecision) / m_YPrecision;
373 : }
374 : // printf("Int2Coordsys: (%d, %d) -> (%.10g, %.10g)\n", nX, nY, dX, dY);
375 :
376 628123 : return 0;
377 : }
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 115469 : int TABMAPHeaderBlock::Coordsys2Int(double dX, double dY, GInt32 &nX,
391 : GInt32 &nY,
392 : GBool bIgnoreOverflow /*=FALSE*/)
393 : {
394 115469 : 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 115469 : double dTempX = 0.0;
408 115469 : double dTempY = 0.0;
409 :
410 115469 : if (m_nCoordOriginQuadrant == 2 || m_nCoordOriginQuadrant == 3 ||
411 115469 : m_nCoordOriginQuadrant == 0)
412 0 : dTempX = -1.0 * dX * m_XScale - m_XDispl;
413 : else
414 115469 : dTempX = dX * m_XScale + m_XDispl;
415 :
416 115469 : if (m_nCoordOriginQuadrant == 3 || m_nCoordOriginQuadrant == 4 ||
417 115469 : m_nCoordOriginQuadrant == 0)
418 0 : dTempY = -1.0 * dY * m_YScale - m_YDispl;
419 : else
420 115469 : 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 115469 : GBool bIntBoundsOverflow = FALSE;
428 115469 : if (dTempX < -1000000000)
429 : {
430 0 : dTempX = -1000000000;
431 0 : bIntBoundsOverflow = TRUE;
432 : }
433 115469 : if (dTempX > 1000000000)
434 : {
435 0 : dTempX = 1000000000;
436 0 : bIntBoundsOverflow = TRUE;
437 : }
438 115469 : if (dTempY < -1000000000)
439 : {
440 0 : dTempY = -1000000000;
441 0 : bIntBoundsOverflow = TRUE;
442 : }
443 115469 : if (dTempY > 1000000000)
444 : {
445 0 : dTempY = 1000000000;
446 0 : bIntBoundsOverflow = TRUE;
447 : }
448 :
449 115469 : nX = static_cast<GInt32>(ROUND_INT(dTempX));
450 115469 : nY = static_cast<GInt32>(ROUND_INT(dTempY));
451 :
452 115469 : if (bIntBoundsOverflow && !bIgnoreOverflow)
453 : {
454 0 : m_bIntBoundsOverflow = TRUE;
455 : #ifdef DEBUG
456 0 : 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 : }
461 :
462 115469 : return 0;
463 : }
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 0 : int TABMAPHeaderBlock::ComprInt2Coordsys(GInt32 nCenterX, GInt32 nCenterY,
480 : int nDeltaX, int nDeltaY, double &dX,
481 : double &dY)
482 : {
483 0 : if (m_pabyBuf == nullptr)
484 0 : return -1;
485 :
486 0 : return Int2Coordsys(nCenterX + nDeltaX, nCenterY + nDeltaY, dX, dY);
487 : }
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 12 : int TABMAPHeaderBlock::Int2CoordsysDist(GInt32 nX, GInt32 nY, double &dX,
505 : double &dY)
506 : {
507 12 : if (m_pabyBuf == nullptr)
508 0 : return -1;
509 :
510 12 : dX = nX / m_XScale;
511 12 : dY = nY / m_YScale;
512 :
513 12 : return 0;
514 : }
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 4 : int TABMAPHeaderBlock::Coordsys2IntDist(double dX, double dY, GInt32 &nX,
532 : GInt32 &nY)
533 : {
534 4 : if (m_pabyBuf == nullptr)
535 0 : return -1;
536 :
537 4 : nX = static_cast<GInt32>(dX * m_XScale);
538 4 : nY = static_cast<GInt32>(dY * m_YScale);
539 :
540 4 : return 0;
541 : }
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 122 : int TABMAPHeaderBlock::SetCoordsysBounds(double dXMin, double dYMin,
555 : double dXMax, double dYMax)
556 : {
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 122 : if (dXMax == dXMin)
563 : {
564 0 : dXMin -= 1.0;
565 0 : dXMax += 1.0;
566 : }
567 :
568 122 : if (dYMax == dYMin)
569 : {
570 0 : dYMin -= 1.0;
571 0 : dYMax += 1.0;
572 : }
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 122 : m_XScale = 2e9 / (dXMax - dXMin);
584 122 : m_YScale = 2e9 / (dYMax - dYMin);
585 :
586 122 : m_XDispl = -1.0 * m_XScale * (dXMax + dXMin) / 2;
587 122 : m_YDispl = -1.0 * m_YScale * (dYMax + dYMin) / 2;
588 :
589 122 : m_nXMin = -1000000000;
590 122 : m_nYMin = -1000000000;
591 122 : m_nXMax = 1000000000;
592 122 : m_nYMax = 1000000000;
593 :
594 122 : UpdatePrecision();
595 :
596 122 : return 0;
597 : }
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 446426 : int TABMAPHeaderBlock::GetMapObjectSize(int nObjType)
606 : {
607 446426 : if (m_pabyBuf == nullptr)
608 : {
609 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
610 : "Block has not been initialized yet!");
611 0 : return -1;
612 : }
613 :
614 446426 : if (nObjType < 0 || nObjType > 255)
615 : {
616 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Invalid object type %d",
617 : nObjType);
618 0 : return -1;
619 : }
620 :
621 : // Byte 0x80 is set for objects that have coordinates inside type 3 blocks
622 446426 : return m_pabyBuf[nObjType] & 0x7f;
623 : }
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 23856 : GBool TABMAPHeaderBlock::MapObjectUsesCoordBlock(int nObjType)
633 : {
634 23856 : if (m_pabyBuf == nullptr)
635 : {
636 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
637 : "Block has not been initialized yet!");
638 0 : return FALSE;
639 : }
640 :
641 23856 : if (nObjType < 0 || nObjType > 255)
642 : {
643 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Invalid object type %d",
644 : nObjType);
645 0 : return FALSE;
646 : }
647 :
648 : // Byte 0x80 is set for objects that have coordinates inside type 3 blocks
649 :
650 23856 : return ((m_pabyBuf[nObjType] & 0x80) != 0) ? TRUE : FALSE;
651 : }
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 1279 : int TABMAPHeaderBlock::GetProjInfo(TABProjInfo *psProjInfo)
662 : {
663 1279 : if (m_pabyBuf == nullptr)
664 : {
665 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
666 : "Block has not been initialized yet!");
667 0 : return -1;
668 : }
669 :
670 1279 : if (psProjInfo)
671 1279 : *psProjInfo = m_sProj;
672 :
673 1279 : return 0;
674 : }
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 29 : int TABMAPHeaderBlock::SetProjInfo(TABProjInfo *psProjInfo)
684 : {
685 29 : if (m_pabyBuf == nullptr)
686 : {
687 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
688 : "Block has not been initialized yet!");
689 0 : return -1;
690 : }
691 :
692 29 : if (psProjInfo)
693 29 : m_sProj = *psProjInfo;
694 :
695 29 : return 0;
696 : }
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 1201 : int TABMAPHeaderBlock::CommitToFile()
712 : {
713 : int i;
714 :
715 1201 : if (m_pabyBuf == nullptr || m_nRegularBlockSize == 0)
716 : {
717 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
718 : "TABRawBinBlock::CommitToFile(): Block has not been "
719 : "initialized yet!");
720 0 : return -1;
721 : }
722 :
723 : /*-----------------------------------------------------------------
724 : * Reconstruct header to make sure it is in sync with members variables.
725 : *----------------------------------------------------------------*/
726 1201 : GotoByteInBlock(0x000);
727 1201 : WriteBytes(HDR_OBJ_LEN_ARRAY_SIZE, gabyObjLenArray);
728 1201 : m_nMaxObjLenArrayId = HDR_OBJ_LEN_ARRAY_SIZE - 1;
729 :
730 1201 : GotoByteInBlock(0x100);
731 1201 : WriteInt32(HDR_MAGIC_COOKIE);
732 :
733 1201 : if (m_sProj.nAffineFlag && m_nMAPVersionNumber < 500)
734 : {
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 : "TABRawBinBlock::CommitToFile(): .MAP version 500 or more is "
741 : "required for affine projection parameter support.");
742 0 : return -1;
743 : }
744 :
745 1201 : WriteInt16(m_nMAPVersionNumber);
746 :
747 1201 : WriteInt16(m_nRegularBlockSize);
748 :
749 1201 : WriteDouble(m_dCoordsys2DistUnits);
750 1201 : WriteInt32(m_nXMin);
751 1201 : WriteInt32(m_nYMin);
752 1201 : WriteInt32(m_nXMax);
753 1201 : WriteInt32(m_nYMax);
754 1201 : if (m_nXMin > m_nXMax || m_nYMin > m_nYMax)
755 : {
756 0 : CPLError(CE_Warning, CPLE_AppDefined,
757 : "Writing corrupted MBR into .map header");
758 : }
759 :
760 1201 : WriteZeros(16); // ???
761 :
762 1201 : WriteInt32(m_nFirstIndexBlock);
763 1201 : WriteInt32(m_nFirstGarbageBlock);
764 1201 : WriteInt32(m_nFirstToolBlock);
765 :
766 1201 : WriteInt32(m_numPointObjects);
767 1201 : WriteInt32(m_numLineObjects);
768 1201 : WriteInt32(m_numRegionObjects);
769 1201 : WriteInt32(m_numTextObjects);
770 1201 : WriteInt32(m_nMaxCoordBufSize);
771 :
772 1201 : WriteZeros(14); // ???
773 :
774 1201 : WriteByte(m_nDistUnitsCode);
775 1201 : WriteByte(m_nMaxSpIndexDepth);
776 1201 : WriteByte(m_nCoordPrecision);
777 1201 : WriteByte(m_nCoordOriginQuadrant);
778 1201 : WriteByte(m_nReflectXAxisCoord);
779 1201 : WriteByte(m_nMaxObjLenArrayId); // See gabyObjLenArray[]
780 1201 : WriteByte(m_numPenDefs);
781 1201 : WriteByte(m_numBrushDefs);
782 1201 : WriteByte(m_numSymbolDefs);
783 1201 : WriteByte(m_numFontDefs);
784 1201 : CPLAssert(m_numMapToolBlocks >= 0 && m_numMapToolBlocks <= 255);
785 1201 : WriteByte(static_cast<GByte>(m_numMapToolBlocks));
786 :
787 1201 : WriteZeros(1); // unused byte
788 1201 : WriteInt16(m_sProj.nDatumId);
789 1201 : WriteZeros(1); // unused byte
790 :
791 1201 : WriteByte(m_sProj.nProjId);
792 1201 : WriteByte(m_sProj.nEllipsoidId);
793 1201 : WriteByte(m_sProj.nUnitsId);
794 1201 : WriteDouble(m_XScale);
795 1201 : WriteDouble(m_YScale);
796 1201 : WriteDouble(m_XDispl);
797 1201 : WriteDouble(m_YDispl);
798 :
799 8407 : for (i = 0; i < 6; i++)
800 7206 : WriteDouble(m_sProj.adProjParams[i]);
801 :
802 1201 : WriteDouble(m_sProj.dDatumShiftX);
803 1201 : WriteDouble(m_sProj.dDatumShiftY);
804 1201 : WriteDouble(m_sProj.dDatumShiftZ);
805 7206 : for (i = 0; i < 5; i++)
806 6005 : WriteDouble(m_sProj.adDatumParams[i]);
807 :
808 1201 : if (m_sProj.nAffineFlag)
809 : {
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 : }
822 :
823 1201 : if (m_nMAPVersionNumber >= 500 && m_nBlockSize == 1024 &&
824 1201 : m_sProj.nProjId == 35)
825 : {
826 1 : const auto nCurPosBak = m_nCurPos;
827 1 : if (m_nCurPos == 512)
828 1 : WriteZeros(512);
829 1 : m_nCurPos = 0x0268;
830 1 : WriteDouble(m_sProj.adProjParams[6]);
831 1 : m_nCurPos = nCurPosBak;
832 : }
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 1201 : return TABRawBinBlock::CommitToFile();
841 : }
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 125 : int TABMAPHeaderBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
859 : int nFileOffset /* = 0*/)
860 : {
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 125 : if (TABRawBinBlock::InitNewBlock(fpSrc, 1024, nFileOffset) != 0)
868 0 : return -1;
869 :
870 : /*-----------------------------------------------------------------
871 : * Set acceptable default values for member vars.
872 : *----------------------------------------------------------------*/
873 125 : InitMembersWithDefaultValues();
874 :
875 125 : CPLAssert(nBlockSize >= 0 && nBlockSize <= 32767);
876 125 : m_nRegularBlockSize = static_cast<GInt16>(nBlockSize);
877 :
878 : /*-----------------------------------------------------------------
879 : * And Set the map object length array in the buffer...
880 : *----------------------------------------------------------------*/
881 125 : if (m_eAccess != TABRead)
882 : {
883 123 : GotoByteInBlock(0x000);
884 123 : WriteBytes(HDR_OBJ_LEN_ARRAY_SIZE, gabyObjLenArray);
885 : }
886 :
887 125 : if (CPLGetLastErrorType() == CE_Failure)
888 0 : return -1;
889 :
890 125 : return 0;
891 : }
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 2756 : void TABMAPHeaderBlock::UpdatePrecision()
900 : {
901 2756 : m_XPrecision = pow(10.0, round(log10(m_XScale)));
902 2756 : m_YPrecision = pow(10.0, round(log10(m_YScale)));
903 2756 : }
904 :
905 : /**********************************************************************
906 : * TABMAPHeaderBlock::Dump()
907 : *
908 : * Dump block contents... available only in DEBUG mode.
909 : **********************************************************************/
910 : #ifdef DEBUG
911 :
912 0 : void TABMAPHeaderBlock::Dump(FILE *fpOut /*=NULL*/)
913 : {
914 0 : if (fpOut == nullptr)
915 0 : fpOut = stdout;
916 :
917 0 : fprintf(fpOut, "----- TABMAPHeaderBlock::Dump() -----\n");
918 :
919 0 : if (m_pabyBuf == nullptr)
920 : {
921 0 : fprintf(fpOut, "Block has not been initialized yet.");
922 : }
923 : else
924 : {
925 0 : fprintf(fpOut, "Version %d header block.\n", m_nMAPVersionNumber);
926 0 : fprintf(fpOut, " m_nRegularBlockSize = %d\n",
927 0 : m_nRegularBlockSize);
928 0 : fprintf(fpOut, " m_nFirstIndexBlock = %d\n", m_nFirstIndexBlock);
929 0 : fprintf(fpOut, " m_nFirstGarbageBlock = %d\n", m_nFirstGarbageBlock);
930 0 : fprintf(fpOut, " m_nFirstToolBlock = %d\n", m_nFirstToolBlock);
931 0 : fprintf(fpOut, " m_numPointObjects = %d\n", m_numPointObjects);
932 0 : fprintf(fpOut, " m_numLineObjects = %d\n", m_numLineObjects);
933 0 : fprintf(fpOut, " m_numRegionObjects = %d\n", m_numRegionObjects);
934 0 : fprintf(fpOut, " m_numTextObjects = %d\n", m_numTextObjects);
935 0 : fprintf(fpOut, " m_nMaxCoordBufSize = %d\n", m_nMaxCoordBufSize);
936 :
937 0 : fprintf(fpOut, "\n");
938 0 : fprintf(fpOut, " m_dCoordsys2DistUnits = %g\n", m_dCoordsys2DistUnits);
939 0 : fprintf(fpOut, " m_nXMin = %d\n", m_nXMin);
940 0 : fprintf(fpOut, " m_nYMin = %d\n", m_nYMin);
941 0 : fprintf(fpOut, " m_nXMax = %d\n", m_nXMax);
942 0 : fprintf(fpOut, " m_nYMax = %d\n", m_nYMax);
943 0 : fprintf(fpOut, " m_XScale = %g\n", m_XScale);
944 0 : fprintf(fpOut, " m_YScale = %g\n", m_YScale);
945 0 : fprintf(fpOut, " m_XDispl = %g\n", m_XDispl);
946 0 : fprintf(fpOut, " m_YDispl = %g\n", m_YDispl);
947 :
948 0 : fprintf(fpOut, "\n");
949 0 : fprintf(fpOut, " m_nDistUnistCode = %d\n", m_nDistUnitsCode);
950 0 : fprintf(fpOut, " m_nMaxSpIndexDepth = %d\n", m_nMaxSpIndexDepth);
951 0 : fprintf(fpOut, " m_nCoordPrecision = %d\n", m_nCoordPrecision);
952 0 : fprintf(fpOut, " m_nCoordOriginQuadrant= %d\n",
953 0 : m_nCoordOriginQuadrant);
954 0 : fprintf(fpOut, " m_nReflecXAxisCoord = %d\n", m_nReflectXAxisCoord);
955 0 : fprintf(fpOut, " m_nMaxObjLenArrayId = %d\n", m_nMaxObjLenArrayId);
956 0 : fprintf(fpOut, " m_numPenDefs = %d\n", m_numPenDefs);
957 0 : fprintf(fpOut, " m_numBrushDefs = %d\n", m_numBrushDefs);
958 0 : fprintf(fpOut, " m_numSymbolDefs = %d\n", m_numSymbolDefs);
959 0 : fprintf(fpOut, " m_numFontDefs = %d\n", m_numFontDefs);
960 0 : fprintf(fpOut, " m_numMapToolBlocks = %d\n", m_numMapToolBlocks);
961 :
962 0 : fprintf(fpOut, "\n");
963 0 : fprintf(fpOut, " m_sProj.nDatumId = %d\n", m_sProj.nDatumId);
964 0 : fprintf(fpOut, " m_sProj.nProjId = %d\n",
965 0 : static_cast<int>(m_sProj.nProjId));
966 0 : fprintf(fpOut, " m_sProj.nEllipsoidId = %d\n",
967 0 : static_cast<int>(m_sProj.nEllipsoidId));
968 0 : fprintf(fpOut, " m_sProj.nUnitsId = %d\n",
969 0 : static_cast<int>(m_sProj.nUnitsId));
970 0 : fprintf(fpOut, " m_sProj.adProjParams =");
971 0 : for (int i = 0; i < 6; i++)
972 0 : fprintf(fpOut, " %g", m_sProj.adProjParams[i]);
973 0 : fprintf(fpOut, "\n");
974 :
975 0 : fprintf(fpOut, " m_sProj.dDatumShiftX = %.15g\n",
976 : m_sProj.dDatumShiftX);
977 0 : fprintf(fpOut, " m_sProj.dDatumShiftY = %.15g\n",
978 : m_sProj.dDatumShiftY);
979 0 : fprintf(fpOut, " m_sProj.dDatumShiftZ = %.15g\n",
980 : m_sProj.dDatumShiftZ);
981 0 : fprintf(fpOut, " m_sProj.adDatumParams =");
982 0 : for (int i = 0; i < 5; i++)
983 0 : fprintf(fpOut, " %.15g", m_sProj.adDatumParams[i]);
984 0 : 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 0 : fflush(fpOut);
1003 0 : }
1004 :
1005 : #endif // DEBUG
|