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