1 : /**********************************************************************
2 : * $Id: mitab_mapcoordblock.cpp,v 1.18 2010-07-07 19:00:15 aboudreault Exp $
3 : *
4 : * Name: mitab_mapcoordblock.cpp
5 : * Project: MapInfo TAB Read/Write library
6 : * Language: C++
7 : * Purpose: Implementation of the TABMAPCoordBlock class used to handle
8 : * reading/writing of the .MAP files' coordinate blocks
9 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
10 : *
11 : **********************************************************************
12 : * Copyright (c) 1999-2001, Daniel Morissette
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 : * $Log: mitab_mapcoordblock.cpp,v $
34 : * Revision 1.18 2010-07-07 19:00:15 aboudreault
35 : * Cleanup Win32 Compile Warnings (GDAL bug #2930)
36 : *
37 : * Revision 1.17 2008-02-01 19:36:31 dmorissette
38 : * Initial support for V800 REGION and MULTIPLINE (bug 1496)
39 : *
40 : * Revision 1.16 2007/02/23 18:56:44 dmorissette
41 : * Fixed another problem writing collections when the header of objects
42 : * part of a collection were split on multiple blocks. Fix WriteBytes()
43 : * to reload next coord block in TABReadWrite mode if there is one (bug 1663)
44 : *
45 : * Revision 1.15 2006/11/28 18:49:08 dmorissette
46 : * Completed changes to split TABMAPObjectBlocks properly and produce an
47 : * optimal spatial index (bug 1585)
48 : *
49 : * Revision 1.14 2005/10/06 19:15:31 dmorissette
50 : * Collections: added support for reading/writing pen/brush/symbol ids and
51 : * for writing collection objects to .TAB/.MAP (bug 1126)
52 : *
53 : * Revision 1.13 2004/06/30 20:29:04 dmorissette
54 : * Fixed refs to old address danmo@videotron.ca
55 : *
56 : * Revision 1.12 2002/08/27 17:18:23 warmerda
57 : * improved CPL error testing
58 : *
59 : * Revision 1.11 2001/11/17 21:54:06 daniel
60 : * Made several changes in order to support writing objects in 16 bits coordinate format.
61 : * New TABMAPObjHdr-derived classes are used to hold object info in mem until block is full.
62 : *
63 : * Revision 1.10 2001/05/09 17:45:12 daniel
64 : * Support reading and writing data blocks > 512 bytes (for text objects).
65 : *
66 : * Revision 1.9 2000/10/10 19:05:12 daniel
67 : * Fixed ReadBytes() to allow strings overlapping on 2 blocks
68 : *
69 : * Revision 1.8 2000/02/28 16:58:55 daniel
70 : * Added V450 object types with num_points > 32767 and pen width in points
71 : *
72 : * Revision 1.7 2000/01/15 22:30:44 daniel
73 : * Switch to MIT/X-Consortium OpenSource license
74 : *
75 : * Revision 1.6 1999/11/08 04:29:31 daniel
76 : * Fixed problem with compressed coord. offset for regions and multiplines
77 : *
78 : * Revision 1.5 1999/10/06 15:19:11 daniel
79 : * Do not automatically init. curr. feature MBR when block is initialized
80 : *
81 : * Revision 1.4 1999/10/06 13:18:55 daniel
82 : * Fixed uninitialized class members
83 : *
84 : * Revision 1.3 1999/09/29 04:25:42 daniel
85 : * Fixed typo in GetFeatureMBR()
86 : *
87 : * Revision 1.2 1999/09/26 14:59:36 daniel
88 : * Implemented write support
89 : *
90 : * Revision 1.1 1999/07/12 04:18:24 daniel
91 : * Initial checkin
92 : *
93 : **********************************************************************/
94 :
95 : #include "mitab.h"
96 :
97 : /*=====================================================================
98 : * class TABMAPCoordBlock
99 : *====================================================================*/
100 :
101 : #define MAP_COORD_HEADER_SIZE 8
102 :
103 : /**********************************************************************
104 : * TABMAPCoordBlock::TABMAPCoordBlock()
105 : *
106 : * Constructor.
107 : **********************************************************************/
108 4 : TABMAPCoordBlock::TABMAPCoordBlock(TABAccess eAccessMode /*= TABRead*/):
109 4 : TABRawBinBlock(eAccessMode, TRUE)
110 : {
111 4 : m_nComprOrgX = m_nComprOrgY = m_nNextCoordBlock = m_numDataBytes = 0;
112 :
113 4 : m_numBlocksInChain = 1; // Current block counts as 1
114 :
115 4 : m_poBlockManagerRef = NULL;
116 :
117 4 : m_nTotalDataSize = 0;
118 4 : m_nFeatureDataSize = 0;
119 :
120 4 : m_nFeatureXMin = m_nMinX = 1000000000;
121 4 : m_nFeatureYMin = m_nMinY = 1000000000;
122 4 : m_nFeatureXMax = m_nMaxX = -1000000000;
123 4 : m_nFeatureYMax = m_nMaxY = -1000000000;
124 :
125 4 : }
126 :
127 : /**********************************************************************
128 : * TABMAPCoordBlock::~TABMAPCoordBlock()
129 : *
130 : * Destructor.
131 : **********************************************************************/
132 4 : TABMAPCoordBlock::~TABMAPCoordBlock()
133 : {
134 :
135 4 : }
136 :
137 :
138 : /**********************************************************************
139 : * TABMAPCoordBlock::InitBlockFromData()
140 : *
141 : * Perform some initialization on the block after its binary data has
142 : * been set or changed (or loaded from a file).
143 : *
144 : * Returns 0 if succesful or -1 if an error happened, in which case
145 : * CPLError() will have been called.
146 : **********************************************************************/
147 16 : int TABMAPCoordBlock::InitBlockFromData(GByte *pabyBuf,
148 : int nBlockSize, int nSizeUsed,
149 : GBool bMakeCopy /* = TRUE */,
150 : FILE *fpSrc /* = NULL */,
151 : int nOffset /* = 0 */)
152 : {
153 : int nStatus;
154 :
155 : /*-----------------------------------------------------------------
156 : * First of all, we must call the base class' InitBlockFromData()
157 : *----------------------------------------------------------------*/
158 : nStatus = TABRawBinBlock::InitBlockFromData(pabyBuf, nBlockSize, nSizeUsed,
159 16 : bMakeCopy, fpSrc, nOffset);
160 16 : if (nStatus != 0)
161 0 : return nStatus;
162 :
163 : /*-----------------------------------------------------------------
164 : * Validate block type
165 : *----------------------------------------------------------------*/
166 16 : if (m_nBlockType != TABMAP_COORD_BLOCK)
167 : {
168 : CPLError(CE_Failure, CPLE_FileIO,
169 : "InitBlockFromData(): Invalid Block Type: got %d expected %d",
170 0 : m_nBlockType, TABMAP_COORD_BLOCK);
171 0 : CPLFree(m_pabyBuf);
172 0 : m_pabyBuf = NULL;
173 0 : return -1;
174 : }
175 :
176 : /*-----------------------------------------------------------------
177 : * Init member variables
178 : *----------------------------------------------------------------*/
179 16 : GotoByteInBlock(0x002);
180 16 : m_numDataBytes = ReadInt16(); /* Excluding 8 bytes header */
181 :
182 16 : m_nNextCoordBlock = ReadInt32();
183 :
184 : // Set the real SizeUsed based on numDataBytes
185 16 : m_nSizeUsed = m_numDataBytes + MAP_COORD_HEADER_SIZE;
186 :
187 : /*-----------------------------------------------------------------
188 : * The read ptr is now located at the beginning of the data part.
189 : *----------------------------------------------------------------*/
190 16 : GotoByteInBlock(MAP_COORD_HEADER_SIZE);
191 :
192 16 : return 0;
193 : }
194 :
195 : /**********************************************************************
196 : * TABMAPCoordBlock::CommitToFile()
197 : *
198 : * Commit the current state of the binary block to the file to which
199 : * it has been previously attached.
200 : *
201 : * This method makes sure all values are properly set in the map object
202 : * block header and then calls TABRawBinBlock::CommitToFile() to do
203 : * the actual writing to disk.
204 : *
205 : * Returns 0 if succesful or -1 if an error happened, in which case
206 : * CPLError() will have been called.
207 : **********************************************************************/
208 4 : int TABMAPCoordBlock::CommitToFile()
209 : {
210 4 : int nStatus = 0;
211 :
212 4 : CPLErrorReset();
213 :
214 4 : if ( m_pabyBuf == NULL )
215 : {
216 : CPLError(CE_Failure, CPLE_AssertionFailed,
217 0 : "CommitToFile(): Block has not been initialized yet!");
218 0 : return -1;
219 : }
220 :
221 : /*-----------------------------------------------------------------
222 : * Make sure 8 bytes block header is up to date.
223 : *----------------------------------------------------------------*/
224 4 : GotoByteInBlock(0x000);
225 :
226 4 : WriteInt16(TABMAP_COORD_BLOCK); // Block type code
227 4 : WriteInt16((GInt16)(m_nSizeUsed - MAP_COORD_HEADER_SIZE)); // num. bytes used
228 4 : WriteInt32(m_nNextCoordBlock);
229 :
230 4 : if( CPLGetLastErrorType() == CE_Failure )
231 0 : nStatus = CPLGetLastErrorNo();
232 :
233 : /*-----------------------------------------------------------------
234 : * OK, call the base class to write the block to disk.
235 : *----------------------------------------------------------------*/
236 4 : if (nStatus == 0)
237 4 : nStatus = TABRawBinBlock::CommitToFile();
238 :
239 4 : return nStatus;
240 : }
241 :
242 : /**********************************************************************
243 : * TABMAPCoordBlock::InitNewBlock()
244 : *
245 : * Initialize a newly created block so that it knows to which file it
246 : * is attached, its block size, etc . and then perform any specific
247 : * initialization for this block type, including writing a default
248 : * block header, etc. and leave the block ready to receive data.
249 : *
250 : * This is an alternative to calling ReadFromFile() or InitBlockFromData()
251 : * that puts the block in a stable state without loading any initial
252 : * data in it.
253 : *
254 : * Returns 0 if succesful or -1 if an error happened, in which case
255 : * CPLError() will have been called.
256 : **********************************************************************/
257 6 : int TABMAPCoordBlock::InitNewBlock(FILE *fpSrc, int nBlockSize,
258 : int nFileOffset /* = 0*/)
259 : {
260 6 : CPLErrorReset();
261 :
262 : /*-----------------------------------------------------------------
263 : * Start with the default initialisation
264 : *----------------------------------------------------------------*/
265 6 : if ( TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
266 0 : return -1;
267 :
268 : /*-----------------------------------------------------------------
269 : * And then set default values for the block header.
270 : *
271 : * IMPORTANT: Do not reset m_nComprOrg here because its value needs to be
272 : * maintained between blocks in the same chain.
273 : *----------------------------------------------------------------*/
274 6 : m_nNextCoordBlock = 0;
275 :
276 6 : m_numDataBytes = 0;
277 :
278 : // m_nMin/Max are used to keep track of current block MBR
279 : // FeatureMin/Max should not be reset here since feature coords can
280 : // be split on several blocks
281 6 : m_nMinX = 1000000000;
282 6 : m_nMinY = 1000000000;
283 6 : m_nMaxX = -1000000000;
284 6 : m_nMaxY = -1000000000;
285 :
286 6 : if (m_eAccess != TABRead)
287 : {
288 4 : GotoByteInBlock(0x000);
289 :
290 4 : WriteInt16(TABMAP_COORD_BLOCK); // Block type code
291 4 : WriteInt16(0); // num. bytes used, excluding header
292 4 : WriteInt32(0); // Pointer to next coord block
293 : }
294 :
295 6 : if (CPLGetLastErrorType() == CE_Failure )
296 0 : return -1;
297 :
298 6 : return 0;
299 : }
300 :
301 : /**********************************************************************
302 : * TABMAPObjectBlock::SetNextCoordBlock()
303 : *
304 : * Set the address (offset from beginning of file) of the coord. block
305 : * that follows the current one.
306 : **********************************************************************/
307 2 : void TABMAPCoordBlock::SetNextCoordBlock(GInt32 nNextCoordBlockAddress)
308 : {
309 2 : m_nNextCoordBlock = nNextCoordBlockAddress;
310 2 : }
311 :
312 :
313 : /**********************************************************************
314 : * TABMAPObjectBlock::SetComprCoordOrigin()
315 : *
316 : * Set the Compressed integer coordinates space origin to be used when
317 : * reading compressed coordinates using ReadIntCoord().
318 : **********************************************************************/
319 62 : void TABMAPCoordBlock::SetComprCoordOrigin(GInt32 nX, GInt32 nY)
320 : {
321 62 : m_nComprOrgX = nX;
322 62 : m_nComprOrgY = nY;
323 62 : }
324 :
325 : /**********************************************************************
326 : * TABMAPObjectBlock::ReadIntCoord()
327 : *
328 : * Read the next pair of integer coordinates value from the block, and
329 : * apply the translation relative to the origin of the coord. space
330 : * previously set using SetComprCoordOrigin() if bCompressed=TRUE.
331 : *
332 : * This means that the returned coordinates are always absolute integer
333 : * coordinates, even when the source coords are in compressed form.
334 : *
335 : * Returns 0 if succesful or -1 if an error happened, in which case
336 : * CPLError() will have been called.
337 : **********************************************************************/
338 102 : int TABMAPCoordBlock::ReadIntCoord(GBool bCompressed,
339 : GInt32 &nX, GInt32 &nY)
340 : {
341 102 : if (bCompressed)
342 : {
343 92 : nX = m_nComprOrgX + ReadInt16();
344 92 : nY = m_nComprOrgY + ReadInt16();
345 : }
346 : else
347 : {
348 10 : nX = ReadInt32();
349 10 : nY = ReadInt32();
350 : }
351 :
352 102 : if (CPLGetLastErrorType() == CE_Failure)
353 0 : return -1;
354 :
355 102 : return 0;
356 : }
357 :
358 : /**********************************************************************
359 : * TABMAPObjectBlock::ReadIntCoords()
360 : *
361 : * Read the specified number of pairs of X,Y integer coordinates values
362 : * from the block, and apply the translation relative to the origin of
363 : * the coord. space previously set using SetComprCoordOrigin() if
364 : * bCompressed=TRUE.
365 : *
366 : * This means that the returned coordinates are always absolute integer
367 : * coordinates, even when the source coords are in compressed form.
368 : *
369 : * panXY should point to an array big enough to receive the specified
370 : * number of coordinates.
371 : *
372 : * Returns 0 if succesful or -1 if an error happened, in which case
373 : * CPLError() will have been called.
374 : **********************************************************************/
375 51 : int TABMAPCoordBlock::ReadIntCoords(GBool bCompressed, int numCoordPairs,
376 : GInt32 *panXY)
377 : {
378 51 : int i, numValues = numCoordPairs*2;
379 :
380 51 : if (bCompressed)
381 : {
382 971 : for(i=0; i<numValues; i+=2)
383 : {
384 925 : panXY[i] = m_nComprOrgX + ReadInt16();
385 925 : panXY[i+1] = m_nComprOrgY + ReadInt16();
386 925 : if (CPLGetLastErrorType() != 0)
387 0 : return -1;
388 : }
389 : }
390 : else
391 : {
392 310 : for(i=0; i<numValues; i+=2)
393 : {
394 305 : panXY[i] = ReadInt32();
395 305 : panXY[i+1] = ReadInt32();
396 305 : if (CPLGetLastErrorType() != 0)
397 0 : return -1;
398 : }
399 : }
400 :
401 51 : return 0;
402 : }
403 :
404 : /**********************************************************************
405 : * TABMAPObjectBlock::ReadCoordSecHdrs()
406 : *
407 : * Read a set of coordinate section headers for PLINE MULTIPLE or REGIONs
408 : * and store the result in the array of structures pasHdrs[]. It is assumed
409 : * that pasHdrs points to an allocated array of at least numSections
410 : * TABMAPCoordSecHdr structures.
411 : *
412 : * The function will also set the values of numVerticesTotal to the
413 : * total number of coordinates in the object (the sum of all sections
414 : * headers read).
415 : *
416 : * At the end of the call, this TABMAPCoordBlock object will be located
417 : * at the beginning of the coordinate data.
418 : *
419 : * In V450 the numVertices is stored on an int32 instead of an int16
420 : *
421 : * In V800 the numHoles is stored on an int32 instead of an int16
422 : *
423 : * IMPORTANT: This function makes the assumption that coordinates for all
424 : * the sections are grouped together immediately after the
425 : * last section header block (i.e. that the coord. data is not
426 : * located all over the place). If it is not the case then
427 : * an error will be produced and the code to read region and
428 : * multipline objects will have to be updated.
429 : *
430 : * Returns 0 if succesful or -1 if an error happened, in which case
431 : * CPLError() will have been called.
432 : **********************************************************************/
433 51 : int TABMAPCoordBlock::ReadCoordSecHdrs(GBool bCompressed,
434 : int nVersion,
435 : int numSections,
436 : TABMAPCoordSecHdr *pasHdrs,
437 : GInt32 &numVerticesTotal)
438 : {
439 : int i, nTotalHdrSizeUncompressed;
440 :
441 51 : CPLErrorReset();
442 :
443 : /*-------------------------------------------------------------
444 : * Note about header+vertices size vs compressed coordinates:
445 : * The uncompressed header sections are actually 16 bytes, but the
446 : * offset calculations are based on prior decompression of the
447 : * coordinates. Our coordinate offset calculations have
448 : * to take this fact into account.
449 : * Also, V450 header section uses int32 instead of int16 for numVertices
450 : * and we add another 2 bytes to align with a 4 bytes boundary.
451 : * V800 header section uses int32 for numHoles but there is no need
452 : * for the 2 alignment bytes so the size is the same as V450
453 : *------------------------------------------------------------*/
454 51 : if (nVersion >= 450)
455 0 : nTotalHdrSizeUncompressed = 28 * numSections;
456 : else
457 51 : nTotalHdrSizeUncompressed = 24 * numSections;
458 :
459 51 : numVerticesTotal = 0;
460 :
461 102 : for(i=0; i<numSections; i++)
462 : {
463 : /*-------------------------------------------------------------
464 : * Read the coord. section header blocks
465 : *------------------------------------------------------------*/
466 : #ifdef TABDUMP
467 : int nHdrAddress = GetCurAddress();
468 : #endif
469 51 : if (nVersion >= 450)
470 0 : pasHdrs[i].numVertices = ReadInt32();
471 : else
472 51 : pasHdrs[i].numVertices = ReadInt16();
473 51 : if (nVersion >= 800)
474 0 : pasHdrs[i].numHoles = ReadInt32();
475 : else
476 51 : pasHdrs[i].numHoles = ReadInt16();
477 51 : ReadIntCoord(bCompressed, pasHdrs[i].nXMin, pasHdrs[i].nYMin);
478 51 : ReadIntCoord(bCompressed, pasHdrs[i].nXMax, pasHdrs[i].nYMax);
479 51 : pasHdrs[i].nDataOffset = ReadInt32();
480 :
481 51 : if (CPLGetLastErrorType() != 0)
482 0 : return -1;
483 :
484 51 : numVerticesTotal += pasHdrs[i].numVertices;
485 :
486 :
487 51 : pasHdrs[i].nVertexOffset = (pasHdrs[i].nDataOffset -
488 51 : nTotalHdrSizeUncompressed ) / 8;
489 : #ifdef TABDUMP
490 : printf("READING pasHdrs[%d] @ %d = \n"
491 : " { numVertices = %d, numHoles = %d, \n"
492 : " nXMin=%d, nYMin=%d, nXMax=%d, nYMax=%d,\n"
493 : " nDataOffset=%d, nVertexOffset=%d }\n",
494 : i, nHdrAddress, pasHdrs[i].numVertices, pasHdrs[i].numHoles,
495 : pasHdrs[i].nXMin, pasHdrs[i].nYMin, pasHdrs[i].nXMax,
496 : pasHdrs[i].nYMax, pasHdrs[i].nDataOffset,
497 : pasHdrs[i].nVertexOffset);
498 : printf(" dX = %d, dY = %d (center = %d , %d)\n",
499 : pasHdrs[i].nXMax - pasHdrs[i].nXMin,
500 : pasHdrs[i].nYMax - pasHdrs[i].nYMin,
501 : m_nComprOrgX, m_nComprOrgY);
502 : #endif
503 : }
504 :
505 102 : for(i=0; i<numSections; i++)
506 : {
507 : /*-------------------------------------------------------------
508 : * Make sure all coordinates are grouped together
509 : * (Well... at least check that all the vertex indices are enclosed
510 : * inside the [0..numVerticesTotal] range.)
511 : *------------------------------------------------------------*/
512 153 : if ( pasHdrs[i].nVertexOffset < 0 ||
513 51 : (pasHdrs[i].nVertexOffset +
514 51 : pasHdrs[i].numVertices ) > numVerticesTotal)
515 : {
516 : CPLError(CE_Failure, CPLE_AssertionFailed,
517 : "Unsupported case or corrupt file: MULTIPLINE/REGION "
518 0 : "object vertices do not appear to be grouped together.");
519 0 : return -1;
520 : }
521 : }
522 :
523 51 : return 0;
524 : }
525 :
526 : /**********************************************************************
527 : * TABMAPObjectBlock::WriteCoordSecHdrs()
528 : *
529 : * Write a set of coordinate section headers for PLINE MULTIPLE or REGIONs.
530 : * pasHdrs should point to an array of numSections TABMAPCoordSecHdr
531 : * structures that have been properly initialized.
532 : *
533 : * In V450 the numVertices is stored on an int32 instead of an int16
534 : *
535 : * In V800 the numHoles is stored on an int32 instead of an int16
536 : *
537 : * At the end of the call, this TABMAPCoordBlock object will be ready to
538 : * receive the coordinate data.
539 : *
540 : * Returns 0 if succesful or -1 if an error happened, in which case
541 : * CPLError() will have been called.
542 : **********************************************************************/
543 11 : int TABMAPCoordBlock::WriteCoordSecHdrs(int nVersion,
544 : int numSections,
545 : TABMAPCoordSecHdr *pasHdrs,
546 : GBool bCompressed /*=FALSE*/)
547 : {
548 : int i;
549 :
550 11 : CPLErrorReset();
551 :
552 22 : for(i=0; i<numSections; i++)
553 : {
554 : /*-------------------------------------------------------------
555 : * Write the coord. section header blocks
556 : *------------------------------------------------------------*/
557 : #ifdef TABDUMP
558 : printf("WRITING pasHdrs[%d] @ %d = \n"
559 : " { numVertices = %d, numHoles = %d, \n"
560 : " nXMin=%d, nYMin=%d, nXMax=%d, nYMax=%d,\n"
561 : " nDataOffset=%d, nVertexOffset=%d }\n",
562 : i, GetCurAddress(), pasHdrs[i].numVertices, pasHdrs[i].numHoles,
563 : pasHdrs[i].nXMin, pasHdrs[i].nYMin, pasHdrs[i].nXMax,
564 : pasHdrs[i].nYMax, pasHdrs[i].nDataOffset,
565 : pasHdrs[i].nVertexOffset);
566 : printf(" dX = %d, dY = %d (center = %d , %d)\n",
567 : pasHdrs[i].nXMax - pasHdrs[i].nXMin,
568 : pasHdrs[i].nYMax - pasHdrs[i].nYMin,
569 : m_nComprOrgX, m_nComprOrgY);
570 : #endif
571 :
572 11 : if (nVersion >= 450)
573 0 : WriteInt32(pasHdrs[i].numVertices);
574 : else
575 11 : WriteInt16((GInt16)pasHdrs[i].numVertices);
576 11 : if (nVersion >= 800)
577 0 : WriteInt32(pasHdrs[i].numHoles);
578 : else
579 11 : WriteInt16((GInt16)pasHdrs[i].numHoles);
580 11 : WriteIntCoord(pasHdrs[i].nXMin, pasHdrs[i].nYMin, bCompressed);
581 11 : WriteIntCoord(pasHdrs[i].nXMax, pasHdrs[i].nYMax, bCompressed);
582 11 : WriteInt32(pasHdrs[i].nDataOffset);
583 :
584 11 : if (CPLGetLastErrorType() == CE_Failure )
585 0 : return -1;
586 : }
587 :
588 11 : return 0;
589 : }
590 :
591 :
592 : /**********************************************************************
593 : * TABMAPCoordBlock::WriteIntCoord()
594 : *
595 : * Write a pair of integer coordinates values to the current position in the
596 : * the block.
597 : *
598 : * Returns 0 if succesful or -1 if an error happened, in which case
599 : * CPLError() will have been called.
600 : **********************************************************************/
601 :
602 272 : int TABMAPCoordBlock::WriteIntCoord(GInt32 nX, GInt32 nY,
603 : GBool bCompressed /*=FALSE*/)
604 : {
605 :
606 272 : if ((!bCompressed && (WriteInt32(nX) != 0 || WriteInt32(nY) != 0 ) ) ||
607 : (bCompressed && (WriteInt16((GInt16)(nX - m_nComprOrgX)) != 0 ||
608 : WriteInt16((GInt16)(nY - m_nComprOrgY)) != 0) ) )
609 : {
610 0 : return -1;
611 : }
612 :
613 : /*-----------------------------------------------------------------
614 : * Update block MBR
615 : *----------------------------------------------------------------*/
616 : //__TODO__ Do we still need to track the block MBR???
617 272 : if (nX < m_nMinX)
618 22 : m_nMinX = nX;
619 272 : if (nX > m_nMaxX)
620 13 : m_nMaxX = nX;
621 :
622 272 : if (nY < m_nMinY)
623 21 : m_nMinY = nY;
624 272 : if (nY > m_nMaxY)
625 24 : m_nMaxY = nY;
626 :
627 : /*-------------------------------------------------------------
628 : * Also keep track of current feature MBR.
629 : *------------------------------------------------------------*/
630 272 : if (nX < m_nFeatureXMin)
631 11 : m_nFeatureXMin = nX;
632 272 : if (nX > m_nFeatureXMax)
633 22 : m_nFeatureXMax = nX;
634 :
635 272 : if (nY < m_nFeatureYMin)
636 11 : m_nFeatureYMin = nY;
637 272 : if (nY > m_nFeatureYMax)
638 22 : m_nFeatureYMax = nY;
639 :
640 272 : return 0;
641 : }
642 :
643 : /**********************************************************************
644 : * TABMAPCoordBlock::SetMAPBlockManagerRef()
645 : *
646 : * Pass a reference to the block manager object for the file this
647 : * block belongs to. The block manager will be used by this object
648 : * when it needs to automatically allocate a new block.
649 : **********************************************************************/
650 2 : void TABMAPCoordBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr)
651 : {
652 2 : m_poBlockManagerRef = poBlockMgr;
653 2 : };
654 :
655 :
656 : /**********************************************************************
657 : * TABMAPCoordBlock::ReadBytes()
658 : *
659 : * Cover function for TABRawBinBlock::ReadBytes() that will automagically
660 : * load the next coordinate block in the chain before reading the
661 : * requested bytes if we are at the end of the current block and if
662 : * m_nNextCoordBlock is a valid block.
663 : *
664 : * Then the control is passed to TABRawBinBlock::ReadBytes() to finish the
665 : * work:
666 : * Copy the number of bytes from the data block's internal buffer to
667 : * the user's buffer pointed by pabyDstBuf.
668 : *
669 : * Passing pabyDstBuf = NULL will only move the read pointer by the
670 : * specified number of bytes as if the copy had happened... but it
671 : * won't crash.
672 : *
673 : * Returns 0 if succesful or -1 if an error happened, in which case
674 : * CPLError() will have been called.
675 : **********************************************************************/
676 2849 : int TABMAPCoordBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
677 : {
678 : int nStatus;
679 :
680 2849 : if (m_pabyBuf &&
681 : m_nCurPos >= (m_numDataBytes+MAP_COORD_HEADER_SIZE) &&
682 : m_nNextCoordBlock > 0)
683 : {
684 : // We're at end of current block... advance to next block.
685 :
686 10 : if ( (nStatus=GotoByteInFile(m_nNextCoordBlock, TRUE)) != 0)
687 : {
688 : // Failed.... an error has already been reported.
689 0 : return nStatus;
690 : }
691 :
692 10 : GotoByteInBlock(MAP_COORD_HEADER_SIZE); // Move pointer past header
693 10 : m_numBlocksInChain++;
694 : }
695 :
696 2849 : if (m_pabyBuf &&
697 : m_nCurPos < (m_numDataBytes+MAP_COORD_HEADER_SIZE) &&
698 : m_nCurPos+numBytes > (m_numDataBytes+MAP_COORD_HEADER_SIZE) &&
699 : m_nNextCoordBlock > 0)
700 : {
701 : // Data overlaps on more than one block
702 : // Read until end of this block and then recursively call ReadBytes()
703 : // for the rest.
704 : int numBytesInThisBlock =
705 0 : (m_numDataBytes+MAP_COORD_HEADER_SIZE)-m_nCurPos;
706 0 : nStatus = TABRawBinBlock::ReadBytes(numBytesInThisBlock, pabyDstBuf);
707 0 : if (nStatus == 0)
708 : nStatus = TABMAPCoordBlock::ReadBytes(numBytes-numBytesInThisBlock,
709 0 : pabyDstBuf+numBytesInThisBlock);
710 0 : return nStatus;
711 : }
712 :
713 :
714 2849 : return TABRawBinBlock::ReadBytes(numBytes, pabyDstBuf);
715 : }
716 :
717 :
718 : /**********************************************************************
719 : * TABMAPCoordBlock::WriteBytes()
720 : *
721 : * Cover function for TABRawBinBlock::WriteBytes() that will automagically
722 : * CommitToFile() the current block and create a new one if we are at
723 : * the end of the current block.
724 : *
725 : * Then the control is passed to TABRawBinBlock::WriteBytes() to finish the
726 : * work.
727 : *
728 : * Passing pabySrcBuf = NULL will only move the write pointer by the
729 : * specified number of bytes as if the copy had happened... but it
730 : * won't crash.
731 : *
732 : * Returns 0 if succesful or -1 if an error happened, in which case
733 : * CPLError() will have been called.
734 : **********************************************************************/
735 601 : int TABMAPCoordBlock::WriteBytes(int nBytesToWrite, GByte *pabySrcBuf)
736 : {
737 601 : if (m_eAccess != TABWrite && m_eAccess != TABReadWrite )
738 : {
739 : CPLError(CE_Failure, CPLE_AppDefined,
740 0 : "WriteBytes(): Block does not support write operations.");
741 0 : return -1;
742 : }
743 :
744 601 : if (m_poBlockManagerRef && (m_nBlockSize - m_nCurPos) < nBytesToWrite)
745 : {
746 2 : if (nBytesToWrite <= (m_nBlockSize-MAP_COORD_HEADER_SIZE))
747 : {
748 : // Data won't fit in this block but can fit inside a single
749 : // block, so we'll allocate a new block for it. This will
750 : // prevent us from overlapping coordinate values on 2 blocks, but
751 : // still allows strings longer than one block (see 'else' below).
752 : //
753 :
754 2 : if ( m_nNextCoordBlock != 0 )
755 : {
756 : // We're in read/write mode and there is already an allocated
757 : // block following this one in the chain ... just reload it
758 : // and continue writing to it
759 :
760 0 : CPLAssert( m_eAccess == TABReadWrite );
761 :
762 0 : if (CommitToFile() != 0 ||
763 0 : ReadFromFile(m_fp, m_nNextCoordBlock, m_nBlockSize) != 0)
764 : {
765 : // An error message should have already been reported.
766 0 : return -1;
767 : }
768 : }
769 : else
770 : {
771 : // Need to alloc a new block.
772 :
773 2 : int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock();
774 2 : SetNextCoordBlock(nNewBlockOffset);
775 :
776 4 : if (CommitToFile() != 0 ||
777 2 : InitNewBlock(m_fp, m_nBlockSize, nNewBlockOffset) != 0)
778 : {
779 : // An error message should have already been reported.
780 0 : return -1;
781 : }
782 :
783 2 : m_numBlocksInChain++;
784 : }
785 : }
786 : else
787 : {
788 : // Data to write is longer than one block... so we'll have to
789 : // split it over multiple block through multiple calls.
790 : //
791 0 : int nStatus = 0;
792 0 : while(nStatus == 0 && nBytesToWrite > 0)
793 : {
794 0 : int nBytes = m_nBlockSize-MAP_COORD_HEADER_SIZE;
795 0 : if ( (m_nBlockSize - m_nCurPos) > 0 )
796 : {
797 : // Use free room in current block
798 0 : nBytes = (m_nBlockSize - m_nCurPos);
799 : }
800 :
801 0 : nBytes = MIN(nBytes, nBytesToWrite);
802 :
803 : // The following call will result in a new block being
804 : // allocated in the if() block above.
805 : nStatus = TABMAPCoordBlock::WriteBytes(nBytes,
806 0 : pabySrcBuf);
807 :
808 0 : nBytesToWrite -= nBytes;
809 0 : pabySrcBuf += nBytes;
810 : }
811 0 : return nStatus;
812 : }
813 : }
814 :
815 601 : if (m_nCurPos >= MAP_COORD_HEADER_SIZE)
816 : {
817 : // Keep track of Coordinate data... this means ignore header bytes
818 : // that could be written.
819 577 : m_nTotalDataSize += nBytesToWrite;
820 577 : m_nFeatureDataSize += nBytesToWrite;
821 : }
822 :
823 601 : return TABRawBinBlock::WriteBytes(nBytesToWrite, pabySrcBuf);
824 : }
825 :
826 : /**********************************************************************
827 : * TABMAPObjectBlock::SeekEnd()
828 : *
829 : * Move read/write pointer to end of used part of the block
830 : **********************************************************************/
831 11 : void TABMAPCoordBlock::SeekEnd()
832 : {
833 11 : m_nCurPos = m_nSizeUsed;
834 11 : }
835 :
836 : /**********************************************************************
837 : * TABMAPCoordBlock::StartNewFeature()
838 : *
839 : * Reset all member vars that are used to keep track of data size
840 : * and MBR for the current feature. This is info is not needed by
841 : * the coord blocks themselves, but it helps a lot the callers to
842 : * have this class take care of that for them.
843 : *
844 : * See Also: GetFeatureDataSize() and GetFeatureMBR()
845 : **********************************************************************/
846 11 : void TABMAPCoordBlock::StartNewFeature()
847 : {
848 11 : m_nFeatureDataSize = 0;
849 :
850 11 : m_nFeatureXMin = 1000000000;
851 11 : m_nFeatureYMin = 1000000000;
852 11 : m_nFeatureXMax = -1000000000;
853 11 : m_nFeatureYMax = -1000000000;
854 11 : }
855 :
856 : /**********************************************************************
857 : * TABMAPCoordBlock::GetFeatureMBR()
858 : *
859 : * Return the MBR of all the coords written using WriteIntCoord() since
860 : * the last call to StartNewFeature().
861 : **********************************************************************/
862 0 : void TABMAPCoordBlock::GetFeatureMBR(GInt32 &nXMin, GInt32 &nYMin,
863 : GInt32 &nXMax, GInt32 &nYMax)
864 : {
865 0 : nXMin = m_nFeatureXMin;
866 0 : nYMin = m_nFeatureYMin;
867 0 : nXMax = m_nFeatureXMax;
868 0 : nYMax = m_nFeatureYMax;
869 0 : }
870 :
871 :
872 : /**********************************************************************
873 : * TABMAPCoordBlock::Dump()
874 : *
875 : * Dump block contents... available only in DEBUG mode.
876 : **********************************************************************/
877 : #ifdef DEBUG
878 :
879 0 : void TABMAPCoordBlock::Dump(FILE *fpOut /*=NULL*/)
880 : {
881 0 : if (fpOut == NULL)
882 0 : fpOut = stdout;
883 :
884 0 : fprintf(fpOut, "----- TABMAPCoordBlock::Dump() -----\n");
885 0 : if (m_pabyBuf == NULL)
886 : {
887 0 : fprintf(fpOut, "Block has not been initialized yet.");
888 : }
889 : else
890 : {
891 : fprintf(fpOut,"Coordinate Block (type %d) at offset %d.\n",
892 0 : m_nBlockType, m_nFileOffset);
893 0 : fprintf(fpOut," m_numDataBytes = %d\n", m_numDataBytes);
894 0 : fprintf(fpOut," m_nNextCoordBlock = %d\n", m_nNextCoordBlock);
895 : }
896 :
897 0 : fflush(fpOut);
898 0 : }
899 :
900 : #endif // DEBUG
901 :
902 :
903 :
|