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