1 : /**********************************************************************
2 : * $Id: mitab_mapfile.cpp,v 1.46 2010-07-07 19:00:15 aboudreault Exp $
3 : *
4 : * Name: mitab_mapfile.cpp
5 : * Project: MapInfo TAB Read/Write library
6 : * Language: C++
7 : * Purpose: Implementation of the TABMAPFile class used to handle
8 : * reading/writing of the .MAP files at the MapInfo object level
9 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
10 : *
11 : **********************************************************************
12 : * Copyright (c) 1999-2002, 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_mapfile.cpp,v $
34 : * Revision 1.46 2010-07-07 19:00:15 aboudreault
35 : * Cleanup Win32 Compile Warnings (GDAL bug #2930)
36 : *
37 : * Revision 1.45 2010-01-08 22:02:51 aboudreault
38 : * Fixed error issued when reading empty TAB with spatial index active (bug 2136)
39 : *
40 : * Revision 1.44 2009-03-03 20:44:23 dmorissette
41 : * Use transparent brush in DumpSpatialIndexToMIF()
42 : *
43 : * Revision 1.43 2008/02/20 21:35:30 dmorissette
44 : * Added support for V800 COLLECTION of large objects (bug 1496)
45 : *
46 : * Revision 1.42 2008/02/01 19:36:31 dmorissette
47 : * Initial support for V800 REGION and MULTIPLINE (bug 1496)
48 : *
49 : * Revision 1.41 2007/11/08 18:57:56 dmorissette
50 : * Upgrade of OGR and CPL libs to the version from GDAL/OGR 1.4.3
51 : *
52 : * Revision 1.40 2007/09/14 18:30:19 dmorissette
53 : * Fixed the splitting of object blocks with the optimized spatial
54 : * index mode that was producing files with misaligned bytes that
55 : * confused MapInfo (bug 1732)
56 : *
57 : * Revision 1.39 2007/07/11 15:51:52 dmorissette
58 : * Fixed duplicate 'int i' definition build errors in SplitObjBlock()
59 : *
60 : * Revision 1.38 2007/06/12 12:50:39 dmorissette
61 : * Use Quick Spatial Index by default until bug 1732 is fixed (broken files
62 : * produced by current coord block splitting technique).
63 : *
64 : * Revision 1.37 2007/06/05 13:23:57 dmorissette
65 : * Fixed memory leak when writing .TAB with new (optimized) spatial index
66 : * introduced in v1.6.0 (bug 1725)
67 : *
68 : * Revision 1.36 2007/03/21 21:15:56 dmorissette
69 : * Added SetQuickSpatialIndexMode() which generates a non-optimal spatial
70 : * index but results in faster write time (bug 1669)
71 : *
72 : * Revision 1.35 2006/11/28 18:49:08 dmorissette
73 : * Completed changes to split TABMAPObjectBlocks properly and produce an
74 : * optimal spatial index (bug 1585)
75 : *
76 : * Revision 1.34 2006/11/20 20:05:58 dmorissette
77 : * First pass at improving generation of spatial index in .map file (bug 1585)
78 : * New methods for insertion and splittung in the spatial index are done.
79 : * Also implemented a method to dump the spatial index to .mif/.mid
80 : * Still need to implement splitting of TABMapObjectBlock to get optimal
81 : * results.
82 : *
83 : * Revision 1.33 2006/09/05 23:05:08 dmorissette
84 : * Added TABMAPFile::DumpSpatialIndex() (bug 1585)
85 : *
86 : * Revision 1.32 2005/10/06 19:15:31 dmorissette
87 : * Collections: added support for reading/writing pen/brush/symbol ids and
88 : * for writing collection objects to .TAB/.MAP (bug 1126)
89 : *
90 : * Revision 1.31 2004/09/22 13:07:58 fwarmerdam
91 : * fixed return value in LoadNextMatchingObjectBlock() per rso bug 615
92 : *
93 : * Revision 1.30 2004/06/30 20:29:04 dmorissette
94 : * Fixed refs to old address danmo@videotron.ca
95 : *
96 : * Revision 1.29 2003/08/12 23:17:21 dmorissette
97 : * Added reading of v500+ coordsys affine params (Anthony D. - Encom)
98 : *
99 : * Revision 1.28 2002/08/27 17:18:43 warmerda
100 : * improved CPL error testing
101 : *
102 : * Revision 1.27 2002/07/30 13:54:12 julien
103 : * TABMAPFile::GetFeatureId() now return -1 when there's no geometry. (bug 169)
104 : *
105 : * Revision 1.26 2002/04/25 16:05:24 julien
106 : * Disabled the overflow warning in SetCoordFilter() by adding bIgnoreOverflow
107 : * variable in Coordsys2Int of the TABMAPFile class and TABMAPHeaderBlock class
108 : *
109 : * Revision 1.25 2002/03/26 19:27:43 daniel
110 : * Got rid of tabs in source
111 : *
112 : * Revision 1.24 2002/03/26 01:48:40 daniel
113 : * Added Multipoint object type (V650)
114 : *
115 : * Revision 1.23 2002/02/20 13:53:40 daniel
116 : * Prevent an infinite loop of calls to LoadNextMatchingObjectBlock() in
117 : * GetNextFeatureId() if no objects found in spatial index.
118 : *
119 : * Revision 1.22 2001/11/19 15:04:41 daniel
120 : * Prevent writing of coordinates outside of the +/-1e9 integer bounds.
121 : *
122 : * Revision 1.21 2001/11/17 21:54:06 daniel
123 : * Made several changes in order to support writing objects in 16 bits
124 : * coordinate format. New TABMAPObjHdr-derived classes are used to hold
125 : * object info in mem until block is full.
126 : *
127 : * Revision 1.20 2001/09/18 20:33:52 warmerda
128 : * fixed case of spatial search on file with just one object block
129 : *
130 : * Revision 1.19 2001/09/14 03:23:55 warmerda
131 : * Substantial upgrade to support spatial queries using spatial indexes
132 : *
133 : * Revision 1.18 2001/03/15 03:57:51 daniel
134 : * Added implementation for new OGRLayer::GetExtent(), returning data MBR.
135 : *
136 : * Revision 1.17 2000/11/23 21:11:07 daniel
137 : * OOpps... VC++ didn't like the way TABPenDef, etc. were initialized
138 : *
139 : * Revision 1.16 2000/11/23 20:47:46 daniel
140 : * Use MI defaults for Pen, Brush, Font, Symbol instead of all zeros
141 : *
142 : * Revision 1.15 2000/11/22 04:03:10 daniel
143 : * Added warning when objects written outside of the +/-1e9 int. coord. range
144 : *
145 : * Revision 1.14 2000/11/15 04:13:49 daniel
146 : * Fixed writing of TABMAPToolBlock to allocate a new block when full
147 : *
148 : * Revision 1.13 2000/05/19 06:44:55 daniel
149 : * Modified generation of spatial index to split index nodes and produce a
150 : * more balanced tree.
151 : *
152 : * Revision 1.12 2000/03/13 05:58:01 daniel
153 : * Create 1024 bytes V500 .MAP header + limit m_nMaxCoordBufSize for V450 obj.
154 : *
155 : * Revision 1.11 2000/02/28 17:00:00 daniel
156 : * Added V450 object types
157 : *
158 : * Revision 1.10 2000/01/15 22:30:44 daniel
159 : * Switch to MIT/X-Consortium OpenSource license
160 : *
161 : * Revision 1.9 1999/12/19 17:37:52 daniel
162 : * Fixed memory leaks
163 : *
164 : * Revision 1.8 1999/11/14 04:43:31 daniel
165 : * Support dataset with no .MAP/.ID files
166 : *
167 : * Revision 1.7 1999/10/19 22:57:17 daniel
168 : * Create m_poCurObjBlock only when needed to avoid empty blocks in files
169 : * and problems with MBR in header block of files with only "NONE" geometries
170 : *
171 : * Revision 1.6 1999/10/06 13:17:46 daniel
172 : * Update m_nMaxCoordBufSize in header block
173 : *
174 : * Revision 1.5 1999/10/01 03:52:22 daniel
175 : * Avoid producing an unused block in the file when closing it.
176 : *
177 : * Revision 1.4 1999/09/26 14:59:36 daniel
178 : * Implemented write support
179 : *
180 : * Revision 1.3 1999/09/20 18:42:42 daniel
181 : * Use binary access to open file.
182 : *
183 : * Revision 1.2 1999/09/16 02:39:16 daniel
184 : * Completed read support for most feature types
185 : *
186 : * Revision 1.1 1999/07/12 04:18:24 daniel
187 : * Initial checkin
188 : *
189 : **********************************************************************/
190 :
191 : #include "mitab.h"
192 :
193 : /*=====================================================================
194 : * class TABMAPFile
195 : *====================================================================*/
196 :
197 :
198 : /**********************************************************************
199 : * TABMAPFile::TABMAPFile()
200 : *
201 : * Constructor.
202 : **********************************************************************/
203 6 : TABMAPFile::TABMAPFile()
204 : {
205 6 : m_nMinTABVersion = 300;
206 6 : m_fp = NULL;
207 6 : m_pszFname = NULL;
208 6 : m_poHeader = NULL;
209 6 : m_poSpIndex = NULL;
210 6 : m_poSpIndexLeaf = NULL;
211 : /* See bug 1732: Optimized spatial index produces broken files because
212 : * of the way CoordBlocks are split. For now we have to force using the
213 : * Quick (old) spatial index mode by default until bug 1732 is fixed.
214 : */
215 6 : m_bQuickSpatialIndexMode = TRUE;
216 : // m_bQuickSpatialIndexMode = FALSE;
217 :
218 6 : m_poCurObjBlock = NULL;
219 6 : m_nCurObjPtr = -1;
220 6 : m_nCurObjType = -1;
221 6 : m_nCurObjId = -1;
222 6 : m_poCurCoordBlock = NULL;
223 6 : m_poToolDefTable = NULL;
224 6 : }
225 :
226 : /**********************************************************************
227 : * TABMAPFile::~TABMAPFile()
228 : *
229 : * Destructor.
230 : **********************************************************************/
231 6 : TABMAPFile::~TABMAPFile()
232 : {
233 6 : Close();
234 6 : }
235 :
236 : /**********************************************************************
237 : * TABMAPFile::Open()
238 : *
239 : * Open a .MAP file, and initialize the structures to be ready to read
240 : * objects from it.
241 : *
242 : * Since .MAP and .ID files are optional, you can set bNoErrorMsg=TRUE to
243 : * disable the error message and receive an return value of 1 if file
244 : * cannot be opened.
245 : * In this case, only the methods MoveToObjId() and GetCurObjType() can
246 : * be used. They will behave as if the .ID file contained only null
247 : * references, so all object will look like they have NONE geometries.
248 : *
249 : * Returns 0 on success, -1 on error.
250 : **********************************************************************/
251 6 : int TABMAPFile::Open(const char *pszFname, const char *pszAccess,
252 : GBool bNoErrorMsg /* = FALSE */)
253 : {
254 6 : FILE *fp=NULL;
255 6 : TABRawBinBlock *poBlock=NULL;
256 :
257 6 : if (m_fp)
258 : {
259 : CPLError(CE_Failure, CPLE_FileIO,
260 0 : "Open() failed: object already contains an open file");
261 0 : return -1;
262 : }
263 :
264 6 : m_nMinTABVersion = 300;
265 6 : m_fp = NULL;
266 6 : m_poHeader = NULL;
267 6 : m_poIdIndex = NULL;
268 6 : m_poSpIndex = NULL;
269 6 : m_poToolDefTable = NULL;
270 :
271 : /*-----------------------------------------------------------------
272 : * Validate access mode and make sure we use binary access.
273 : *----------------------------------------------------------------*/
274 6 : if (EQUALN(pszAccess, "r", 1))
275 : {
276 3 : m_eAccessMode = TABRead;
277 3 : pszAccess = "rb";
278 : }
279 3 : else if (EQUALN(pszAccess, "w", 1))
280 : {
281 3 : m_eAccessMode = TABWrite;
282 3 : pszAccess = "wb+";
283 : }
284 : else
285 : {
286 : CPLError(CE_Failure, CPLE_FileIO,
287 0 : "Open() failed: access mode \"%s\" not supported", pszAccess);
288 0 : return -1;
289 : }
290 :
291 : /*-----------------------------------------------------------------
292 : * Open file
293 : *----------------------------------------------------------------*/
294 6 : fp = VSIFOpen(pszFname, pszAccess);
295 :
296 : // TODO: In Read/Write mode we should also preload the chain of deleted
297 : // blocks in the blockManager. Not needed for read-only or write-only.
298 6 : m_oBlockManager.Reset();
299 :
300 9 : if (fp != NULL && m_eAccessMode == TABRead)
301 : {
302 : /*-----------------------------------------------------------------
303 : * Read access: try to read header block
304 : * First try with a 512 bytes block to check the .map version.
305 : * If it's version 500 or more then read again a 1024 bytes block
306 : *----------------------------------------------------------------*/
307 3 : poBlock = TABCreateMAPBlockFromFile(fp, 0, 512);
308 :
309 3 : if (poBlock && poBlock->GetBlockClass() == TABMAP_HEADER_BLOCK &&
310 : ((TABMAPHeaderBlock*)poBlock)->m_nMAPVersionNumber >= 500)
311 : {
312 : // Version 500 or higher. Read 1024 bytes block instead of 512
313 3 : delete poBlock;
314 3 : poBlock = TABCreateMAPBlockFromFile(fp, 0, 1024);
315 : }
316 :
317 3 : if (poBlock==NULL || poBlock->GetBlockClass() != TABMAP_HEADER_BLOCK)
318 : {
319 0 : if (poBlock)
320 0 : delete poBlock;
321 0 : poBlock = NULL;
322 0 : VSIFClose(fp);
323 : CPLError(CE_Failure, CPLE_FileIO,
324 : "Open() failed: %s does not appear to be a valid .MAP file",
325 0 : pszFname);
326 0 : return -1;
327 : }
328 : }
329 6 : else if (fp != NULL && m_eAccessMode == TABWrite)
330 : {
331 : /*-----------------------------------------------------------------
332 : * Write access: create a new header block
333 : * .MAP files of Version 500 and up appear to have a 1024 bytes
334 : * header. The last 512 bytes are usually all zeros.
335 : *----------------------------------------------------------------*/
336 3 : poBlock = new TABMAPHeaderBlock(m_eAccessMode);
337 3 : poBlock->InitNewBlock(fp, 1024, m_oBlockManager.AllocNewBlock() );
338 :
339 : // Alloc a second 512 bytes of space since oBlockManager deals
340 : // with 512 bytes blocks.
341 3 : m_oBlockManager.AllocNewBlock();
342 : }
343 0 : else if (bNoErrorMsg)
344 : {
345 : /*-----------------------------------------------------------------
346 : * .MAP does not exist... produce no error message, but set
347 : * the class members so that MoveToObjId() and GetCurObjType()
348 : * can be used to return only NONE geometries.
349 : *----------------------------------------------------------------*/
350 0 : m_fp = NULL;
351 0 : m_nCurObjType = TAB_GEOM_NONE;
352 :
353 : /* Create a false header block that will return default
354 : * values for projection and coordsys conversion stuff...
355 : */
356 0 : m_poHeader = new TABMAPHeaderBlock(m_eAccessMode);
357 0 : m_poHeader->InitNewBlock(NULL, 512, 0 );
358 :
359 0 : return 1;
360 : }
361 : else
362 : {
363 : CPLError(CE_Failure, CPLE_FileIO,
364 0 : "Open() failed for %s", pszFname);
365 0 : return -1;
366 : }
367 :
368 : /*-----------------------------------------------------------------
369 : * File appears to be valid... set the various class members
370 : *----------------------------------------------------------------*/
371 6 : m_fp = fp;
372 6 : m_poHeader = (TABMAPHeaderBlock*)poBlock;
373 6 : m_pszFname = CPLStrdup(pszFname);
374 :
375 : /*-----------------------------------------------------------------
376 : * Create a TABMAPObjectBlock, in READ mode only.
377 : *
378 : * In WRITE mode, the object block will be created only when needed.
379 : * We do not create the object block in the open() call because
380 : * files that contained only "NONE" geometries ended up with empty
381 : * object and spatial index blocks.
382 : *----------------------------------------------------------------*/
383 :
384 6 : if (m_eAccessMode == TABRead)
385 : {
386 3 : m_poCurObjBlock = new TABMAPObjectBlock(m_eAccessMode);
387 3 : m_poCurObjBlock->InitNewBlock(m_fp, 512);
388 : }
389 : else
390 : {
391 3 : m_poCurObjBlock = NULL;
392 : }
393 :
394 : /*-----------------------------------------------------------------
395 : * Open associated .ID (object id index) file
396 : *----------------------------------------------------------------*/
397 6 : m_poIdIndex = new TABIDFile;
398 6 : if (m_poIdIndex->Open(pszFname, pszAccess) != 0)
399 : {
400 : // Failed... an error has already been reported
401 0 : Close();
402 0 : return -1;
403 : }
404 :
405 : /*-----------------------------------------------------------------
406 : * Default Coord filter is the MBR of the whole file
407 : * This is currently unused but could eventually be used to handle
408 : * spatial filters more efficiently.
409 : *----------------------------------------------------------------*/
410 6 : if (m_eAccessMode == TABRead)
411 : {
412 3 : ResetCoordFilter();
413 : }
414 :
415 : /*-----------------------------------------------------------------
416 : * We could scan a file through its quad tree index... but we don't!
417 : *
418 : * In read mode, we just ignore the spatial index.
419 : *
420 : * In write mode the index is created and maintained as new object
421 : * blocks are added inside CommitObjBlock().
422 : *----------------------------------------------------------------*/
423 6 : m_poSpIndex = NULL;
424 :
425 : /*-----------------------------------------------------------------
426 : * Initialization of the Drawing Tools table will be done automatically
427 : * as Read/Write calls are done later.
428 : *----------------------------------------------------------------*/
429 6 : m_poToolDefTable = NULL;
430 :
431 : /*-----------------------------------------------------------------
432 : * Make sure all previous calls succeded.
433 : *----------------------------------------------------------------*/
434 6 : if (CPLGetLastErrorNo() != 0)
435 : {
436 : // Open Failed... an error has already been reported
437 0 : Close();
438 0 : return -1;
439 : }
440 :
441 6 : return 0;
442 : }
443 :
444 : /**********************************************************************
445 : * TABMAPFile::Close()
446 : *
447 : * Close current file, and release all memory used.
448 : *
449 : * Returns 0 on success, -1 on error.
450 : **********************************************************************/
451 12 : int TABMAPFile::Close()
452 : {
453 : // Check if file is opened... it is possible to have a fake header
454 : // without an actual file attached to it.
455 12 : if (m_fp == NULL && m_poHeader == NULL)
456 6 : return 0;
457 :
458 : /*----------------------------------------------------------------
459 : * Write access: commit latest changes to the file.
460 : *---------------------------------------------------------------*/
461 6 : if (m_eAccessMode == TABWrite)
462 : {
463 : // Start by committing current object and coord blocks
464 : // Nothing happens if none has been created yet.
465 3 : CommitObjAndCoordBlocks(FALSE);
466 :
467 : // Write the drawing tools definitions now.
468 3 : CommitDrawingTools();
469 :
470 : // Commit spatial index blocks
471 3 : CommitSpatialIndex();
472 :
473 : // Update header fields and commit
474 3 : if (m_poHeader)
475 : {
476 : // OK, with V450 files, objects are not limited to 32k nodes
477 : // any more, and this means that m_nMaxCoordBufSize can become
478 : // huge, and actually more huge than can be held in memory.
479 : // MapInfo counts m_nMaxCoordBufSize=0 for V450 objects, but
480 : // until this is cleanly implented, we will just prevent
481 : // m_nMaxCoordBufSizefrom going beyond 512k in V450 files.
482 3 : if (m_nMinTABVersion >= 450)
483 : {
484 : m_poHeader->m_nMaxCoordBufSize =
485 0 : MIN(m_poHeader->m_nMaxCoordBufSize, 512*1024);
486 : }
487 :
488 : // Write Ref to beginning of the chain of garbage blocks
489 : m_poHeader->m_nFirstGarbageBlock =
490 3 : m_oBlockManager.GetFirstGarbageBlock();
491 :
492 3 : m_poHeader->CommitToFile();
493 : }
494 : }
495 :
496 : // Check for overflow of internal coordinates and produce a warning
497 : // if that happened...
498 6 : if (m_poHeader && m_poHeader->m_bIntBoundsOverflow)
499 : {
500 : double dBoundsMinX, dBoundsMinY, dBoundsMaxX, dBoundsMaxY;
501 0 : Int2Coordsys(-1000000000, -1000000000, dBoundsMinX, dBoundsMinY);
502 0 : Int2Coordsys(1000000000, 1000000000, dBoundsMaxX, dBoundsMaxY);
503 :
504 : CPLError(CE_Warning, TAB_WarningBoundsOverflow,
505 : "Some objects were written outside of the file's "
506 : "predefined bounds.\n"
507 : "These objects may have invalid coordinates when the file "
508 : "is reopened.\n"
509 : "Predefined bounds: (%.15g,%.15g)-(%.15g,%.15g)\n",
510 0 : dBoundsMinX, dBoundsMinY, dBoundsMaxX, dBoundsMaxY );
511 : }
512 :
513 : // Delete all structures
514 6 : if (m_poHeader)
515 6 : delete m_poHeader;
516 6 : m_poHeader = NULL;
517 :
518 6 : if (m_poIdIndex)
519 : {
520 6 : m_poIdIndex->Close();
521 6 : delete m_poIdIndex;
522 6 : m_poIdIndex = NULL;
523 : }
524 :
525 6 : if (m_poCurObjBlock)
526 : {
527 5 : delete m_poCurObjBlock;
528 5 : m_poCurObjBlock = NULL;
529 5 : m_nCurObjPtr = -1;
530 5 : m_nCurObjType = -1;
531 5 : m_nCurObjId = -1;
532 : }
533 :
534 6 : if (m_poCurCoordBlock)
535 : {
536 4 : delete m_poCurCoordBlock;
537 4 : m_poCurCoordBlock = NULL;
538 : }
539 :
540 6 : if (m_poSpIndex)
541 : {
542 2 : delete m_poSpIndex;
543 2 : m_poSpIndex = NULL;
544 2 : m_poSpIndexLeaf = NULL;
545 : }
546 :
547 6 : if (m_poToolDefTable)
548 : {
549 4 : delete m_poToolDefTable;
550 4 : m_poToolDefTable = NULL;
551 : }
552 :
553 : // Close file
554 6 : if (m_fp)
555 6 : VSIFClose(m_fp);
556 6 : m_fp = NULL;
557 :
558 6 : CPLFree(m_pszFname);
559 6 : m_pszFname = NULL;
560 :
561 6 : return 0;
562 : }
563 :
564 :
565 : /**********************************************************************
566 : * TABMAPFile::SetQuickSpatialIndexMode()
567 : *
568 : * Select "quick spatial index mode".
569 : *
570 : * The default behavior of MITAB is to generate an optimized spatial index,
571 : * but this results in slower write speed.
572 : *
573 : * Applications that want faster write speed and do not care
574 : * about the performance of spatial queries on the resulting file can
575 : * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
576 : * spatial index (actually emulating the type of spatial index produced
577 : * by MITAB before version 1.6.0). In this mode writing files can be
578 : * about 5 times faster, but spatial queries can be up to 30 times slower.
579 : *
580 : * Returns 0 on success, -1 on error.
581 : **********************************************************************/
582 0 : int TABMAPFile::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode/*=TRUE*/)
583 : {
584 0 : if (m_eAccessMode != TABWrite)
585 : {
586 : CPLError(CE_Failure, CPLE_AssertionFailed,
587 0 : "SetQuickSpatialIndexMode() failed: file not opened for write access.");
588 0 : return -1;
589 : }
590 :
591 0 : if (m_poCurObjBlock != NULL || m_poSpIndex != NULL)
592 : {
593 : CPLError(CE_Failure, CPLE_AssertionFailed,
594 0 : "SetQuickSpatialIndexMode() must be called before writing the first object.");
595 0 : return -1;
596 : }
597 :
598 0 : m_bQuickSpatialIndexMode = bQuickSpatialIndexMode;
599 :
600 0 : return 0;
601 : }
602 :
603 : /************************************************************************/
604 : /* PushBlock() */
605 : /* */
606 : /* Install a new block (object or spatial) as being current - */
607 : /* whatever that means. This method is only intended to ever */
608 : /* be called from LoadNextMatchingObjectBlock(). */
609 : /************************************************************************/
610 :
611 2 : TABRawBinBlock *TABMAPFile::PushBlock( int nFileOffset )
612 :
613 : {
614 : TABRawBinBlock *poBlock;
615 :
616 2 : poBlock = GetIndexObjectBlock( nFileOffset );
617 2 : if( poBlock == NULL )
618 0 : return NULL;
619 :
620 2 : if( poBlock->GetBlockType() == TABMAP_INDEX_BLOCK )
621 : {
622 1 : TABMAPIndexBlock *poIndex = (TABMAPIndexBlock *) poBlock;
623 :
624 1 : if( m_poSpIndexLeaf == NULL )
625 : {
626 1 : m_poSpIndexLeaf = m_poSpIndex = poIndex;
627 : }
628 : else
629 : {
630 : CPLAssert(
631 : m_poSpIndexLeaf->GetEntry(
632 : m_poSpIndexLeaf->GetCurChildIndex())->nBlockPtr
633 0 : == nFileOffset );
634 :
635 : m_poSpIndexLeaf->SetCurChildRef( poIndex,
636 0 : m_poSpIndexLeaf->GetCurChildIndex() );
637 0 : poIndex->SetParentRef( m_poSpIndexLeaf );
638 0 : m_poSpIndexLeaf = poIndex;
639 : }
640 : }
641 : else
642 : {
643 1 : CPLAssert( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK );
644 :
645 1 : if( m_poCurObjBlock != NULL )
646 1 : delete m_poCurObjBlock;
647 :
648 1 : m_poCurObjBlock = (TABMAPObjectBlock *) poBlock;
649 :
650 1 : m_nCurObjPtr = nFileOffset;
651 1 : m_nCurObjType = 0;
652 1 : m_nCurObjId = -1;
653 : }
654 :
655 2 : return poBlock;
656 : }
657 :
658 : /************************************************************************/
659 : /* LoadNextMatchingObjectBlock() */
660 : /* */
661 : /* Advance through the spatial indices till the next object */
662 : /* block is loaded that matching the spatial query extents. */
663 : /************************************************************************/
664 :
665 2 : int TABMAPFile::LoadNextMatchingObjectBlock( int bFirstObject )
666 :
667 : {
668 : // If we are just starting, verify the stack is empty.
669 2 : if( bFirstObject )
670 : {
671 1 : CPLAssert( m_poSpIndex == NULL && m_poSpIndexLeaf == NULL );
672 :
673 : /* m_nFirstIndexBlock set to 0 means that there is no feature */
674 1 : if ( m_poHeader->m_nFirstIndexBlock == 0 )
675 0 : return FALSE;
676 :
677 1 : if( PushBlock( m_poHeader->m_nFirstIndexBlock ) == NULL )
678 0 : return FALSE;
679 :
680 1 : if( m_poSpIndex == NULL )
681 : {
682 0 : CPLAssert( m_poCurObjBlock != NULL );
683 0 : return TRUE;
684 : }
685 : }
686 :
687 5 : while( m_poSpIndexLeaf != NULL )
688 : {
689 2 : int iEntry = m_poSpIndexLeaf->GetCurChildIndex();
690 :
691 2 : if( iEntry >= m_poSpIndexLeaf->GetNumEntries()-1 )
692 : {
693 1 : TABMAPIndexBlock *poParent = m_poSpIndexLeaf->GetParentRef();
694 1 : delete m_poSpIndexLeaf;
695 1 : m_poSpIndexLeaf = poParent;
696 :
697 1 : if( poParent != NULL )
698 : {
699 0 : poParent->SetCurChildRef( NULL, poParent->GetCurChildIndex() );
700 : }
701 : else
702 : {
703 1 : m_poSpIndex = NULL;
704 : }
705 1 : continue;
706 : }
707 :
708 1 : m_poSpIndexLeaf->SetCurChildRef( NULL, ++iEntry );
709 :
710 1 : TABMAPIndexEntry *psEntry = m_poSpIndexLeaf->GetEntry( iEntry );
711 : TABRawBinBlock *poBlock;
712 :
713 1 : if( psEntry->XMax < m_XMinFilter
714 : || psEntry->YMax < m_YMinFilter
715 : || psEntry->XMin > m_XMaxFilter
716 : || psEntry->YMin > m_YMaxFilter )
717 0 : continue;
718 :
719 1 : poBlock = PushBlock( psEntry->nBlockPtr );
720 1 : if( poBlock == NULL )
721 0 : return FALSE;
722 1 : else if( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK )
723 1 : return TRUE;
724 : else
725 : /* continue processing new index block */;
726 : }
727 :
728 1 : return m_poSpIndexLeaf != NULL;
729 : }
730 :
731 : /************************************************************************/
732 : /* ResetReading() */
733 : /* */
734 : /* Ensure that any resources related to a spatial traversal of */
735 : /* the file are recovered, and the state reinitialized to the */
736 : /* initial conditions. */
737 : /************************************************************************/
738 :
739 10 : void TABMAPFile::ResetReading()
740 :
741 : {
742 10 : if (m_poSpIndex && m_eAccessMode == TABRead )
743 : {
744 0 : delete m_poSpIndex;
745 0 : m_poSpIndex = NULL;
746 0 : m_poSpIndexLeaf = NULL;
747 : }
748 10 : }
749 :
750 : /************************************************************************/
751 : /* GetNextFeatureId() */
752 : /* */
753 : /* Fetch the next feature id based on a traversal of the */
754 : /* spatial index. */
755 : /************************************************************************/
756 :
757 11 : int TABMAPFile::GetNextFeatureId( int nPrevId )
758 :
759 : {
760 : /* -------------------------------------------------------------------- */
761 : /* m_fp is NULL when all geometry are NONE and/or there's */
762 : /* no .map file and/or there's no spatial indexes */
763 : /* -------------------------------------------------------------------- */
764 11 : if( m_fp == NULL )
765 0 : return -1;
766 :
767 11 : if( nPrevId == 0 )
768 1 : nPrevId = -1;
769 :
770 : /* -------------------------------------------------------------------- */
771 : /* This should always be true if we are being called properly. */
772 : /* -------------------------------------------------------------------- */
773 11 : if( nPrevId != -1 && m_nCurObjId != nPrevId )
774 : {
775 : CPLError( CE_Failure, CPLE_AppDefined,
776 : "TABMAPFile::GetNextFeatureId(%d) called out of sequence.",
777 0 : nPrevId );
778 0 : return -1;
779 : }
780 :
781 11 : CPLAssert( nPrevId == -1 || m_poCurObjBlock != NULL );
782 :
783 : /* -------------------------------------------------------------------- */
784 : /* Ensure things are initialized properly if this is a request */
785 : /* for the first feature. */
786 : /* -------------------------------------------------------------------- */
787 11 : if( nPrevId == -1 )
788 : {
789 1 : m_nCurObjId = -1;
790 : }
791 :
792 : /* -------------------------------------------------------------------- */
793 : /* Try to advance to the next object in the current object */
794 : /* block. */
795 : /* -------------------------------------------------------------------- */
796 11 : if( nPrevId == -1
797 : || m_poCurObjBlock->AdvanceToNextObject(m_poHeader) == -1 )
798 : {
799 : // If not, try to advance to the next object block, and get
800 : // first object from it. Note that some object blocks actually
801 : // have no objects, so we may have to advance to additional
802 : // object blocks till we find a non-empty one.
803 2 : GBool bFirstCall = (nPrevId == -1);
804 1 : do
805 : {
806 2 : if( !LoadNextMatchingObjectBlock( bFirstCall ) )
807 1 : return -1;
808 :
809 1 : bFirstCall = FALSE;
810 : } while( m_poCurObjBlock->AdvanceToNextObject(m_poHeader) == -1 );
811 : }
812 :
813 10 : m_nCurObjType = m_poCurObjBlock->GetCurObjectType();
814 10 : m_nCurObjId = m_poCurObjBlock->GetCurObjectId();
815 : m_nCurObjPtr = m_poCurObjBlock->GetStartAddress()
816 10 : + m_poCurObjBlock->GetCurObjectOffset();
817 :
818 10 : CPLAssert( m_nCurObjId != -1 );
819 :
820 10 : return m_nCurObjId;
821 : }
822 :
823 : /**********************************************************************
824 : * TABMAPFile::Int2Coordsys()
825 : *
826 : * Convert from long integer (internal) to coordinates system units
827 : * as defined in the file's coordsys clause.
828 : *
829 : * Note that the false easting/northing and the conversion factor from
830 : * datum to coordsys units are not included in the calculation.
831 : *
832 : * Returns 0 on success, -1 on error.
833 : **********************************************************************/
834 1417 : int TABMAPFile::Int2Coordsys(GInt32 nX, GInt32 nY, double &dX, double &dY)
835 : {
836 1417 : if (m_poHeader == NULL)
837 0 : return -1;
838 :
839 1417 : return m_poHeader->Int2Coordsys(nX, nY, dX, dY);
840 : }
841 :
842 : /**********************************************************************
843 : * TABMAPFile::Coordsys2Int()
844 : *
845 : * Convert from coordinates system units as defined in the file's
846 : * coordsys clause to long integer (internal) coordinates.
847 : *
848 : * Note that the false easting/northing and the conversion factor from
849 : * datum to coordsys units are not included in the calculation.
850 : *
851 : * Returns 0 on success, -1 on error.
852 : **********************************************************************/
853 311 : int TABMAPFile::Coordsys2Int(double dX, double dY, GInt32 &nX, GInt32 &nY,
854 : GBool bIgnoreOverflow/*=FALSE*/)
855 : {
856 311 : if (m_poHeader == NULL)
857 0 : return -1;
858 :
859 311 : return m_poHeader->Coordsys2Int(dX, dY, nX, nY, bIgnoreOverflow);
860 : }
861 :
862 : /**********************************************************************
863 : * TABMAPFile::Int2CoordsysDist()
864 : *
865 : * Convert a pair of X,Y size (or distance) values from long integer
866 : * (internal) to coordinates system units as defined in the file's coordsys
867 : * clause.
868 : *
869 : * The difference with Int2Coordsys() is that this function only applies
870 : * the scaling factor: it does not apply the displacement.
871 : *
872 : * Since the calculations on the X and Y values are independent, either
873 : * one can be omitted (i.e. passed as 0)
874 : *
875 : * Returns 0 on success, -1 on error.
876 : **********************************************************************/
877 0 : int TABMAPFile::Int2CoordsysDist(GInt32 nX, GInt32 nY, double &dX, double &dY)
878 : {
879 0 : if (m_poHeader == NULL)
880 0 : return -1;
881 :
882 0 : return m_poHeader->Int2CoordsysDist(nX, nY, dX, dY);
883 : }
884 :
885 : /**********************************************************************
886 : * TABMAPFile::Coordsys2IntDist()
887 : *
888 : * Convert a pair of X,Y size (or distance) values from coordinates
889 : * system units as defined in the file's coordsys clause to long
890 : * integer (internal) coordinate units.
891 : *
892 : * The difference with Int2Coordsys() is that this function only applies
893 : * the scaling factor: it does not apply the displacement.
894 : *
895 : * Since the calculations on the X and Y values are independent, either
896 : * one can be omitted (i.e. passed as 0)
897 : *
898 : * Returns 0 on success, -1 on error.
899 : **********************************************************************/
900 0 : int TABMAPFile::Coordsys2IntDist(double dX, double dY, GInt32 &nX, GInt32 &nY)
901 : {
902 0 : if (m_poHeader == NULL)
903 0 : return -1;
904 :
905 0 : return m_poHeader->Coordsys2IntDist(dX, dY, nX, nY);
906 : }
907 :
908 : /**********************************************************************
909 : * TABMAPFile::SetCoordsysBounds()
910 : *
911 : * Set projection coordinates bounds of the newly created dataset.
912 : *
913 : * This function must be called after creating a new dataset and before any
914 : * feature can be written to it.
915 : *
916 : * Returns 0 on success, -1 on error.
917 : **********************************************************************/
918 3 : int TABMAPFile::SetCoordsysBounds(double dXMin, double dYMin,
919 : double dXMax, double dYMax)
920 : {
921 3 : int nStatus = 0;
922 :
923 3 : if (m_poHeader == NULL)
924 0 : return -1;
925 :
926 3 : nStatus = m_poHeader->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax);
927 :
928 3 : if (nStatus == 0)
929 3 : ResetCoordFilter();
930 :
931 3 : return nStatus;
932 : }
933 :
934 : /**********************************************************************
935 : * TABMAPFile::GetMaxObjId()
936 : *
937 : * Return the value of the biggest valid object id.
938 : *
939 : * Note that object ids are positive and start at 1.
940 : *
941 : * Returns a value >= 0 on success, -1 on error.
942 : **********************************************************************/
943 0 : GInt32 TABMAPFile::GetMaxObjId()
944 : {
945 0 : if (m_poIdIndex)
946 0 : return m_poIdIndex->GetMaxObjId();
947 :
948 0 : return -1;
949 : }
950 :
951 : /**********************************************************************
952 : * TABMAPFile::MoveToObjId()
953 : *
954 : * Get ready to work with the object with the specified id. The object
955 : * data pointer (inside m_poCurObjBlock) will be moved to the first byte
956 : * of data for this map object.
957 : *
958 : * The object type and id (i.e. table row number) will be accessible
959 : * using GetCurObjType() and GetCurObjId().
960 : *
961 : * Note that object ids are positive and start at 1.
962 : *
963 : * Returns 0 on success, -1 on error.
964 : **********************************************************************/
965 96 : int TABMAPFile::MoveToObjId(int nObjId)
966 : {
967 : int nFileOffset;
968 :
969 : /*-----------------------------------------------------------------
970 : * In read access mode, since the .MAP/.ID are optional, if the
971 : * file is not opened then we can still act as if one existed and
972 : * make any object id look like a TAB_GEOM_NONE
973 : *----------------------------------------------------------------*/
974 96 : if (m_fp == NULL && m_eAccessMode == TABRead)
975 : {
976 0 : CPLAssert(m_poIdIndex == NULL && m_poCurObjBlock == NULL);
977 0 : m_nCurObjPtr = 0;
978 0 : m_nCurObjId = nObjId;
979 0 : m_nCurObjType = TAB_GEOM_NONE;
980 :
981 0 : return 0;
982 : }
983 :
984 96 : if (m_poIdIndex == NULL || m_poCurObjBlock == NULL)
985 : {
986 : CPLError(CE_Failure, CPLE_AssertionFailed,
987 0 : "MoveToObjId(): file not opened!");
988 0 : m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
989 0 : return -1;
990 : }
991 :
992 : /*-----------------------------------------------------------------
993 : * Move map object pointer to the right location. Fetch location
994 : * from the index file, unless we are already pointing at it.
995 : *----------------------------------------------------------------*/
996 96 : if( m_nCurObjId == nObjId )
997 53 : nFileOffset = m_nCurObjPtr;
998 : else
999 43 : nFileOffset = m_poIdIndex->GetObjPtr(nObjId);
1000 :
1001 96 : if (nFileOffset == 0)
1002 : {
1003 : /*---------------------------------------------------------
1004 : * Object with no geometry... this is a valid case.
1005 : *--------------------------------------------------------*/
1006 2 : m_nCurObjPtr = 0;
1007 2 : m_nCurObjId = nObjId;
1008 2 : m_nCurObjType = TAB_GEOM_NONE;
1009 : }
1010 94 : else if ( m_poCurObjBlock->GotoByteInFile(nFileOffset, TRUE) == 0)
1011 : {
1012 : /*-------------------------------------------------------------
1013 : * OK, it worked, read the object type and row id.
1014 : *------------------------------------------------------------*/
1015 94 : m_nCurObjPtr = nFileOffset;
1016 94 : m_nCurObjType = m_poCurObjBlock->ReadByte();
1017 94 : m_nCurObjId = m_poCurObjBlock->ReadInt32();
1018 :
1019 : // Do a consistency check...
1020 94 : if (m_nCurObjId != nObjId)
1021 : {
1022 : CPLError(CE_Failure, CPLE_FileIO,
1023 : "Object ID from the .ID file (%d) differs from the value "
1024 : "in the .MAP file (%d). File may be corrupt.",
1025 0 : nObjId, m_nCurObjId);
1026 0 : m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
1027 0 : return -1;
1028 : }
1029 : }
1030 : else
1031 : {
1032 : /*---------------------------------------------------------
1033 : * Failed positioning input file... CPLError has been called.
1034 : *--------------------------------------------------------*/
1035 0 : m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
1036 0 : return -1;
1037 : }
1038 :
1039 96 : return 0;
1040 : }
1041 :
1042 : /**********************************************************************
1043 : * TABMAPFile::UpdateMapHeaderInfo()
1044 : *
1045 : * Update .map header information (counter of objects by type and minimum
1046 : * required version) in light of a new object to be written to the file.
1047 : *
1048 : * Called only by PrepareNewObj() and by the TABCollection class.
1049 : **********************************************************************/
1050 12 : void TABMAPFile::UpdateMapHeaderInfo(GByte nObjType)
1051 : {
1052 : /*-----------------------------------------------------------------
1053 : * Update count of objects by type in the header block
1054 : *----------------------------------------------------------------*/
1055 12 : if (nObjType == TAB_GEOM_SYMBOL ||
1056 : nObjType == TAB_GEOM_FONTSYMBOL ||
1057 : nObjType == TAB_GEOM_CUSTOMSYMBOL ||
1058 : nObjType == TAB_GEOM_MULTIPOINT ||
1059 : nObjType == TAB_GEOM_V800_MULTIPOINT ||
1060 : nObjType == TAB_GEOM_SYMBOL_C ||
1061 : nObjType == TAB_GEOM_FONTSYMBOL_C ||
1062 : nObjType == TAB_GEOM_CUSTOMSYMBOL_C ||
1063 : nObjType == TAB_GEOM_MULTIPOINT_C ||
1064 : nObjType == TAB_GEOM_V800_MULTIPOINT_C )
1065 : {
1066 0 : m_poHeader->m_numPointObjects++;
1067 : }
1068 13 : else if (nObjType == TAB_GEOM_LINE ||
1069 : nObjType == TAB_GEOM_PLINE ||
1070 : nObjType == TAB_GEOM_MULTIPLINE ||
1071 : nObjType == TAB_GEOM_V450_MULTIPLINE ||
1072 : nObjType == TAB_GEOM_V800_MULTIPLINE ||
1073 : nObjType == TAB_GEOM_ARC ||
1074 : nObjType == TAB_GEOM_LINE_C ||
1075 : nObjType == TAB_GEOM_PLINE_C ||
1076 : nObjType == TAB_GEOM_MULTIPLINE_C ||
1077 : nObjType == TAB_GEOM_V450_MULTIPLINE_C ||
1078 : nObjType == TAB_GEOM_V800_MULTIPLINE_C ||
1079 : nObjType == TAB_GEOM_ARC_C)
1080 : {
1081 1 : m_poHeader->m_numLineObjects++;
1082 : }
1083 22 : else if (nObjType == TAB_GEOM_REGION ||
1084 : nObjType == TAB_GEOM_V450_REGION ||
1085 : nObjType == TAB_GEOM_V800_REGION ||
1086 : nObjType == TAB_GEOM_RECT ||
1087 : nObjType == TAB_GEOM_ROUNDRECT ||
1088 : nObjType == TAB_GEOM_ELLIPSE ||
1089 : nObjType == TAB_GEOM_REGION_C ||
1090 : nObjType == TAB_GEOM_V450_REGION_C ||
1091 : nObjType == TAB_GEOM_V800_REGION_C ||
1092 : nObjType == TAB_GEOM_RECT_C ||
1093 : nObjType == TAB_GEOM_ROUNDRECT_C ||
1094 : nObjType == TAB_GEOM_ELLIPSE_C)
1095 : {
1096 11 : m_poHeader->m_numRegionObjects++;
1097 : }
1098 0 : else if (nObjType == TAB_GEOM_TEXT ||
1099 : nObjType == TAB_GEOM_TEXT_C)
1100 : {
1101 0 : m_poHeader->m_numTextObjects++;
1102 : }
1103 :
1104 : /*-----------------------------------------------------------------
1105 : * Check forminimum TAB file version number
1106 : *----------------------------------------------------------------*/
1107 12 : int nVersion = TAB_GEOM_GET_VERSION(nObjType);
1108 :
1109 12 : if (nVersion > m_nMinTABVersion )
1110 : {
1111 0 : m_nMinTABVersion = nVersion;
1112 : }
1113 :
1114 12 : }
1115 :
1116 : /**********************************************************************
1117 : * TABMAPFile::PrepareNewObj()
1118 : *
1119 : * Get ready to write a new object described by poObjHdr (using the
1120 : * poObjHdr's m_nId (featureId), m_nType and IntMBR members which must
1121 : * have been set by the caller).
1122 : *
1123 : * Depending on whether "quick spatial index mode" is selected, we either:
1124 : *
1125 : * 1- Walk through the spatial index to find the best place to insert the
1126 : * new object, update the spatial index references, and prepare the object
1127 : * data block to be ready to write the object to it.
1128 : * ... or ...
1129 : * 2- prepare the current object data block to be ready to write the
1130 : * object to it. If the object block is full then it is inserted in the
1131 : * spatial index and committed to disk, and a new obj block is created.
1132 : *
1133 : * m_poCurObjBlock will be set to be ready to receive the new object, and
1134 : * a new block will be created if necessary (in which case the current
1135 : * block contents will be committed to disk, etc.) The actual ObjHdr
1136 : * data won't be written to m_poCurObjBlock until CommitNewObj() is called.
1137 : *
1138 : * If this object type uses coordinate blocks, then the coordinate block
1139 : * will be prepared to receive coordinates.
1140 : *
1141 : * This function will also take care of updating the .ID index entry for
1142 : * the new object.
1143 : *
1144 : * Note that object ids are positive and start at 1.
1145 : *
1146 : * Returns 0 on success, -1 on error.
1147 : **********************************************************************/
1148 14 : int TABMAPFile::PrepareNewObj(TABMAPObjHdr *poObjHdr)
1149 : {
1150 :
1151 14 : m_nCurObjPtr = m_nCurObjId = m_nCurObjType = -1;
1152 :
1153 14 : if (m_eAccessMode != TABWrite ||
1154 : m_poIdIndex == NULL || m_poHeader == NULL)
1155 : {
1156 : CPLError(CE_Failure, CPLE_AssertionFailed,
1157 0 : "PrepareNewObj() failed: file not opened for write access.");
1158 0 : return -1;
1159 : }
1160 :
1161 : /*-----------------------------------------------------------------
1162 : * For objects with no geometry, we just update the .ID file and return
1163 : *----------------------------------------------------------------*/
1164 14 : if (poObjHdr->m_nType == TAB_GEOM_NONE)
1165 : {
1166 2 : m_nCurObjType = poObjHdr->m_nType;
1167 2 : m_nCurObjId = poObjHdr->m_nId;
1168 2 : m_nCurObjPtr = 0;
1169 2 : m_poIdIndex->SetObjPtr(m_nCurObjId, 0);
1170 :
1171 2 : return 0;
1172 : }
1173 :
1174 : /*-----------------------------------------------------------------
1175 : * Update count of objects by type in the header block and minimum
1176 : * required version.
1177 : *----------------------------------------------------------------*/
1178 12 : UpdateMapHeaderInfo(poObjHdr->m_nType);
1179 :
1180 :
1181 : /*-----------------------------------------------------------------
1182 : * Depending on the selected spatial index mode, we will either insert
1183 : * new objects via the spatial index (slower write but results in optimal
1184 : * spatial index) or directly in the current ObjBlock (faster write
1185 : * but non-optimal spatial index)
1186 : *----------------------------------------------------------------*/
1187 12 : if ( !m_bQuickSpatialIndexMode )
1188 : {
1189 0 : if (PrepareNewObjViaSpatialIndex(poObjHdr) != 0)
1190 0 : return -1; /* Error already reported */
1191 : }
1192 : else
1193 : {
1194 12 : if (PrepareNewObjViaObjBlock(poObjHdr) != 0)
1195 0 : return -1; /* Error already reported */
1196 : }
1197 :
1198 : /*-----------------------------------------------------------------
1199 : * Prepare ObjBlock for this new object.
1200 : * Real data won't be written to the object block until CommitNewObj()
1201 : * is called.
1202 : *----------------------------------------------------------------*/
1203 12 : m_nCurObjPtr = m_poCurObjBlock->PrepareNewObject(poObjHdr);
1204 12 : if (m_nCurObjPtr < 0 )
1205 : {
1206 : CPLError(CE_Failure, CPLE_FileIO,
1207 : "Failed writing object header for feature id %d",
1208 0 : poObjHdr->m_nId);
1209 0 : return -1;
1210 : }
1211 :
1212 12 : m_nCurObjType = poObjHdr->m_nType;
1213 12 : m_nCurObjId = poObjHdr->m_nId;
1214 :
1215 : /*-----------------------------------------------------------------
1216 : * Update .ID Index
1217 : *----------------------------------------------------------------*/
1218 12 : m_poIdIndex->SetObjPtr(m_nCurObjId, m_nCurObjPtr);
1219 :
1220 : /*-----------------------------------------------------------------
1221 : * Prepare Coords block...
1222 : * create a new TABMAPCoordBlock if it was not done yet.
1223 : *----------------------------------------------------------------*/
1224 12 : PrepareCoordBlock(m_nCurObjType, m_poCurObjBlock, &m_poCurCoordBlock);
1225 :
1226 12 : if (CPLGetLastErrorNo() != 0 && CPLGetLastErrorType() == CE_Failure)
1227 0 : return -1;
1228 :
1229 12 : return 0;
1230 : }
1231 :
1232 : /**********************************************************************
1233 : * TABMAPFile::PrepareNewObjViaSpatialIndex()
1234 : *
1235 : * Used by TABMAPFile::PrepareNewObj() to walk through the spatial index
1236 : * to find the best place to insert the new object, update the spatial
1237 : * index references, and prepare the object data block to be ready to
1238 : * write the object to it.
1239 : *
1240 : * This method is used when "quick spatial index mode" is NOT selected,
1241 : * i.e. when we want to produce a file with an optimal spatial index
1242 : *
1243 : * Returns 0 on success, -1 on error.
1244 : **********************************************************************/
1245 0 : int TABMAPFile::PrepareNewObjViaSpatialIndex(TABMAPObjHdr *poObjHdr)
1246 : {
1247 : int nObjSize;
1248 0 : GInt32 nObjBlockForInsert = -1;
1249 :
1250 : /*-----------------------------------------------------------------
1251 : * Create spatial index if we don't have one yet.
1252 : * We do not create the index and object data blocks in the open()
1253 : * call because files that contained only "NONE" geometries ended up
1254 : * with empty object and spatial index blocks.
1255 : *----------------------------------------------------------------*/
1256 0 : if (m_poSpIndex == NULL)
1257 : {
1258 : // Spatial Index not created yet...
1259 0 : m_poSpIndex = new TABMAPIndexBlock(m_eAccessMode);
1260 :
1261 : m_poSpIndex->InitNewBlock(m_fp, 512,
1262 0 : m_oBlockManager.AllocNewBlock());
1263 0 : m_poSpIndex->SetMAPBlockManagerRef(&m_oBlockManager);
1264 :
1265 0 : m_poHeader->m_nFirstIndexBlock = m_poSpIndex->GetNodeBlockPtr();
1266 :
1267 : /* We'll also need to create an object data block (later) */
1268 0 : nObjBlockForInsert = -1;
1269 :
1270 0 : CPLAssert(m_poCurObjBlock == NULL);
1271 : }
1272 : else
1273 : /*-----------------------------------------------------------------
1274 : * Search the spatial index to find the best place to insert this
1275 : * new object.
1276 : *----------------------------------------------------------------*/
1277 : {
1278 : nObjBlockForInsert=m_poSpIndex->ChooseLeafForInsert(poObjHdr->m_nMinX,
1279 : poObjHdr->m_nMinY,
1280 : poObjHdr->m_nMaxX,
1281 0 : poObjHdr->m_nMaxY);
1282 0 : if (nObjBlockForInsert == -1)
1283 : {
1284 : /* ChooseLeafForInsert() should not fail unless file is corrupt*/
1285 : CPLError(CE_Failure, CPLE_AssertionFailed,
1286 0 : "ChooseLeafForInsert() Failed?!?!");
1287 0 : return -1;
1288 : }
1289 : }
1290 :
1291 :
1292 0 : if (nObjBlockForInsert == -1)
1293 : {
1294 : /*-------------------------------------------------------------
1295 : * Create a new object data block from scratch
1296 : *------------------------------------------------------------*/
1297 0 : m_poCurObjBlock = new TABMAPObjectBlock(TABReadWrite);
1298 :
1299 0 : int nBlockOffset = m_oBlockManager.AllocNewBlock();
1300 :
1301 0 : m_poCurObjBlock->InitNewBlock(m_fp, 512, nBlockOffset);
1302 :
1303 : /*-------------------------------------------------------------
1304 : * Insert new object block in index, based on MBR of poObjHdr
1305 : *------------------------------------------------------------*/
1306 0 : if (m_poSpIndex->AddEntry(poObjHdr->m_nMinX,
1307 : poObjHdr->m_nMinY,
1308 : poObjHdr->m_nMaxX,
1309 : poObjHdr->m_nMaxY,
1310 : m_poCurObjBlock->GetStartAddress()) != 0)
1311 0 : return -1;
1312 :
1313 : m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth,
1314 0 : (GByte)m_poSpIndex->GetCurMaxDepth()+1);
1315 : }
1316 : else
1317 : {
1318 : /*-------------------------------------------------------------
1319 : * Load existing object and Coord blocks, unless we've already
1320 : * got the right object block in memory
1321 : *------------------------------------------------------------*/
1322 0 : if (m_poCurObjBlock &&
1323 : m_poCurObjBlock->GetStartAddress() != nObjBlockForInsert)
1324 : {
1325 : /* Got a block in memory but it's not the right one, flush it */
1326 0 : if (CommitObjAndCoordBlocks(TRUE) != 0 )
1327 0 : return -1;
1328 : }
1329 :
1330 0 : if (m_poCurObjBlock == NULL)
1331 : {
1332 0 : if (LoadObjAndCoordBlocks(nObjBlockForInsert) != 0)
1333 0 : return -1;
1334 :
1335 : // The ObjBlock doesn't know its MBR. Get the value from the
1336 : // index and set it
1337 : GInt32 nMinX, nMinY, nMaxX, nMaxY;
1338 : m_poSpIndex->GetCurLeafEntryMBR(m_poCurObjBlock->GetStartAddress(),
1339 0 : nMinX, nMinY, nMaxX, nMaxY);
1340 0 : m_poCurObjBlock->SetMBR(nMinX, nMinY, nMaxX, nMaxY);
1341 : }
1342 : }
1343 :
1344 : /*-----------------------------------------------------------------
1345 : * Fetch new object size, make sure there is enough room in obj.
1346 : * block for new object, update spatial index and split if necessary.
1347 : *----------------------------------------------------------------*/
1348 0 : nObjSize = m_poHeader->GetMapObjectSize(poObjHdr->m_nType);
1349 0 : if (m_poCurObjBlock->GetNumUnusedBytes() >= nObjSize )
1350 : {
1351 : /*-------------------------------------------------------------
1352 : * New object fits in current block, just update the spatial index
1353 : *------------------------------------------------------------*/
1354 : GInt32 nMinX, nMinY, nMaxX, nMaxY;
1355 0 : m_poCurObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
1356 :
1357 : /* Need to calculate the enlarged MBR that includes new object */
1358 0 : nMinX = MIN(nMinX, poObjHdr->m_nMinX);
1359 0 : nMinY = MIN(nMinY, poObjHdr->m_nMinY);
1360 0 : nMaxX = MAX(nMaxX, poObjHdr->m_nMaxX);
1361 0 : nMaxY = MAX(nMaxY, poObjHdr->m_nMaxY);
1362 :
1363 0 : if (m_poSpIndex->UpdateLeafEntry(m_poCurObjBlock->GetStartAddress(),
1364 : nMinX, nMinY, nMaxX, nMaxY) != 0)
1365 0 : return -1;
1366 : }
1367 : else
1368 : {
1369 : /*-------------------------------------------------------------
1370 : * OK, the new object won't fit in the current block, need to split
1371 : * and update index.
1372 : * Split() does its job so that the current obj block will remain
1373 : * the best candidate to receive the new object. It also flushes
1374 : * everything to disk and will update m_poCurCoordBlock to point to
1375 : * the last coord block in the chain, ready to accept new data
1376 : *------------------------------------------------------------*/
1377 : TABMAPObjectBlock *poNewObjBlock;
1378 0 : poNewObjBlock= SplitObjBlock(poObjHdr, nObjSize);
1379 :
1380 0 : if (poNewObjBlock == NULL)
1381 0 : return -1; /* Split failed, error already reported. */
1382 :
1383 : /*-------------------------------------------------------------
1384 : * Update index with info about m_poCurObjectBlock *first*
1385 : * This is important since UpdateLeafEntry() needs the chain of
1386 : * index nodes preloaded by ChooseLeafEntry() in order to do its job
1387 : *------------------------------------------------------------*/
1388 : GInt32 nMinX, nMinY, nMaxX, nMaxY;
1389 0 : m_poCurObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
1390 :
1391 : /* Need to calculate the enlarged MBR that includes new object */
1392 0 : nMinX = MIN(nMinX, poObjHdr->m_nMinX);
1393 0 : nMinY = MIN(nMinY, poObjHdr->m_nMinY);
1394 0 : nMaxX = MAX(nMaxX, poObjHdr->m_nMaxX);
1395 0 : nMaxY = MAX(nMaxY, poObjHdr->m_nMaxY);
1396 :
1397 0 : if (m_poSpIndex->UpdateLeafEntry(m_poCurObjBlock->GetStartAddress(),
1398 : nMinX, nMinY, nMaxX, nMaxY) != 0)
1399 0 : return -1;
1400 :
1401 : /*-------------------------------------------------------------
1402 : * Add new obj block to index
1403 : *------------------------------------------------------------*/
1404 0 : poNewObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
1405 :
1406 0 : if (m_poSpIndex->AddEntry(nMinX, nMinY, nMaxX, nMaxY,
1407 : poNewObjBlock->GetStartAddress()) != 0)
1408 0 : return -1;
1409 : m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth,
1410 0 : (GByte)m_poSpIndex->GetCurMaxDepth()+1);
1411 :
1412 : /*-------------------------------------------------------------
1413 : * Delete second object block, no need to commit to file first since
1414 : * it's already been committed to disk by Split()
1415 : *------------------------------------------------------------*/
1416 0 : delete poNewObjBlock;
1417 : }
1418 :
1419 0 : return 0;
1420 : }
1421 :
1422 : /**********************************************************************
1423 : * TABMAPFile::PrepareNewObjViaObjBlock()
1424 : *
1425 : * Used by TABMAPFile::PrepareNewObj() to prepare the current object
1426 : * data block to be ready to write the object to it. If the object block
1427 : * is full then it is inserted in the spatial index and committed to disk,
1428 : * and a new obj block is created.
1429 : *
1430 : * This method is used when "quick spatial index mode" is selected,
1431 : * i.e. faster write, but non-optimal spatial index.
1432 : *
1433 : * Returns 0 on success, -1 on error.
1434 : **********************************************************************/
1435 12 : int TABMAPFile::PrepareNewObjViaObjBlock(TABMAPObjHdr *poObjHdr)
1436 : {
1437 : int nObjSize;
1438 :
1439 : /*-------------------------------------------------------------
1440 : * We will need an object block... check if it exists and
1441 : * create it if it has not been created yet (first time for this file).
1442 : * We do not create the object block in the open() call because
1443 : * files that contained only "NONE" geometries ended up with empty
1444 : * object and spatial index blocks.
1445 : * Note: A coord block will be created only if needed later.
1446 : *------------------------------------------------------------*/
1447 12 : if (m_poCurObjBlock == NULL)
1448 : {
1449 2 : m_poCurObjBlock = new TABMAPObjectBlock(m_eAccessMode);
1450 :
1451 2 : int nBlockOffset = m_oBlockManager.AllocNewBlock();
1452 :
1453 2 : m_poCurObjBlock->InitNewBlock(m_fp, 512, nBlockOffset);
1454 :
1455 : // The reference to the first object block should
1456 : // actually go through the index blocks... this will be
1457 : // updated when file is closed.
1458 2 : m_poHeader->m_nFirstIndexBlock = nBlockOffset;
1459 : }
1460 :
1461 : /*-----------------------------------------------------------------
1462 : * Fetch new object size, make sure there is enough room in obj.
1463 : * block for new object, and save/create a new one if necessary.
1464 : *----------------------------------------------------------------*/
1465 12 : nObjSize = m_poHeader->GetMapObjectSize(poObjHdr->m_nType);
1466 12 : if (m_poCurObjBlock->GetNumUnusedBytes() < nObjSize )
1467 : {
1468 : /*-------------------------------------------------------------
1469 : * OK, the new object won't fit in the current block. Add the
1470 : * current block to the spatial index, commit it to disk and init
1471 : * a new block
1472 : *------------------------------------------------------------*/
1473 0 : CommitObjAndCoordBlocks(FALSE);
1474 :
1475 0 : if (m_poCurObjBlock->InitNewBlock(m_fp,512,
1476 0 : m_oBlockManager.AllocNewBlock())!=0)
1477 0 : return -1; /* Error already reported */
1478 :
1479 : /*-------------------------------------------------------------
1480 : * Coord block has been committed to disk but not deleted.
1481 : * Delete it to require the creation of a new coord block chain
1482 : * as needed.
1483 : *-------------------------------------------------------------*/
1484 0 : if (m_poCurCoordBlock)
1485 : {
1486 0 : delete m_poCurCoordBlock;
1487 0 : m_poCurCoordBlock = NULL;
1488 : }
1489 :
1490 : }
1491 :
1492 12 : return 0;
1493 : }
1494 :
1495 : /**********************************************************************
1496 : * TABMAPFile::CommitNewObj()
1497 : *
1498 : * Commit object header data to the ObjBlock. Should be called after
1499 : * PrepareNewObj, once all members of the ObjHdr have been set.
1500 : *
1501 : * Returns 0 on success, -1 on error.
1502 : **********************************************************************/
1503 14 : int TABMAPFile::CommitNewObj(TABMAPObjHdr *poObjHdr)
1504 : {
1505 14 : return m_poCurObjBlock->CommitNewObject(poObjHdr);
1506 : }
1507 :
1508 :
1509 : /**********************************************************************
1510 : * TABMAPFile::CommitObjAndCoordBlocks()
1511 : *
1512 : * Commit the TABMAPObjBlock and TABMAPCoordBlock to disk.
1513 : *
1514 : * The objects are deleted from memory if bDeleteObjects==TRUE.
1515 : *
1516 : * Returns 0 on success, -1 on error.
1517 : **********************************************************************/
1518 3 : int TABMAPFile::CommitObjAndCoordBlocks(GBool bDeleteObjects /*=FALSE*/)
1519 : {
1520 3 : int nStatus = 0;
1521 :
1522 : /*-----------------------------------------------------------------
1523 : * First check that a objBlock has been created. It is possible to have
1524 : * no object block in files that contain only "NONE" geometries.
1525 : *----------------------------------------------------------------*/
1526 3 : if (m_poCurObjBlock == NULL)
1527 1 : return 0;
1528 :
1529 2 : if (m_eAccessMode != TABWrite)
1530 : {
1531 : CPLError(CE_Failure, CPLE_AssertionFailed,
1532 0 : "CommitObjAndCoordBlocks() failed: file not opened for write access.");
1533 0 : return -1;
1534 : }
1535 :
1536 : /*-----------------------------------------------------------------
1537 : * We need to flush the coord block if there was one
1538 : * since a list of coord blocks can belong to only one obj. block
1539 : *----------------------------------------------------------------*/
1540 2 : if (m_poCurCoordBlock)
1541 : {
1542 : // Update the m_nMaxCoordBufSize member in the header block
1543 : //
1544 2 : int nTotalCoordSize = m_poCurCoordBlock->GetNumBlocksInChain()*512;
1545 2 : if (nTotalCoordSize > m_poHeader->m_nMaxCoordBufSize)
1546 2 : m_poHeader->m_nMaxCoordBufSize = nTotalCoordSize;
1547 :
1548 : // Update the references to this coord block in the MAPObjBlock
1549 : //
1550 : m_poCurObjBlock->AddCoordBlockRef(m_poCurCoordBlock->
1551 2 : GetStartAddress());
1552 2 : nStatus = m_poCurCoordBlock->CommitToFile();
1553 :
1554 2 : if (bDeleteObjects)
1555 : {
1556 0 : delete m_poCurCoordBlock;
1557 0 : m_poCurCoordBlock = NULL;
1558 : }
1559 : }
1560 :
1561 : /*-----------------------------------------------------------------
1562 : * Commit the obj block
1563 : *----------------------------------------------------------------*/
1564 2 : if (nStatus == 0)
1565 : {
1566 2 : nStatus = m_poCurObjBlock->CommitToFile();
1567 : }
1568 :
1569 :
1570 : /*-----------------------------------------------------------------
1571 : * Update the spatial index ** only in "quick spatial index" mode **
1572 : * In the (default) optimized spatial index mode, the spatial index
1573 : * is already maintained up to date as part of inserting the objects in
1574 : * PrepareNewObj().
1575 : *
1576 : * Spatial index will be created here if it was not done yet.
1577 : *----------------------------------------------------------------*/
1578 2 : if (nStatus == 0 && m_bQuickSpatialIndexMode)
1579 : {
1580 : GInt32 nXMin, nYMin, nXMax, nYMax;
1581 :
1582 2 : if (m_poSpIndex == NULL)
1583 : {
1584 : // Spatial Index not created yet...
1585 2 : m_poSpIndex = new TABMAPIndexBlock(m_eAccessMode);
1586 :
1587 : m_poSpIndex->InitNewBlock(m_fp, 512,
1588 2 : m_oBlockManager.AllocNewBlock());
1589 2 : m_poSpIndex->SetMAPBlockManagerRef(&m_oBlockManager);
1590 :
1591 2 : m_poHeader->m_nFirstIndexBlock = m_poSpIndex->GetNodeBlockPtr();
1592 : }
1593 :
1594 2 : m_poCurObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax);
1595 : nStatus = m_poSpIndex->AddEntry(nXMin, nYMin, nXMax, nYMax,
1596 2 : m_poCurObjBlock->GetStartAddress());
1597 :
1598 : m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth,
1599 2 : (GByte)m_poSpIndex->GetCurMaxDepth()+1);
1600 : }
1601 :
1602 : /*-----------------------------------------------------------------
1603 : * Delete obj block only if requested
1604 : *----------------------------------------------------------------*/
1605 2 : if (bDeleteObjects)
1606 : {
1607 0 : delete m_poCurObjBlock;
1608 0 : m_poCurObjBlock = NULL;
1609 : }
1610 :
1611 2 : return nStatus;
1612 : }
1613 :
1614 : /**********************************************************************
1615 : * TABMAPFile::LoadObjAndCoordBlocks()
1616 : *
1617 : * Load the TABMAPObjBlock at specified address and corresponding
1618 : * TABMAPCoordBlock, ready to write new objects to them.
1619 : *
1620 : * It is assumed that pre-existing m_poCurObjBlock and m_poCurCoordBlock
1621 : * have been flushed to disk already using CommitObjAndCoordBlocks()
1622 : *
1623 : * Returns 0 on success, -1 on error.
1624 : **********************************************************************/
1625 0 : int TABMAPFile::LoadObjAndCoordBlocks(GInt32 nBlockPtr)
1626 : {
1627 0 : TABRawBinBlock *poBlock = NULL;
1628 :
1629 : /*-----------------------------------------------------------------
1630 : * In Write mode, if an object block is already in memory then flush it
1631 : *----------------------------------------------------------------*/
1632 0 : if (m_eAccessMode == TABWrite && m_poCurObjBlock != NULL)
1633 : {
1634 0 : int nStatus = CommitObjAndCoordBlocks(TRUE);
1635 0 : if (nStatus != 0)
1636 0 : return nStatus;
1637 : }
1638 :
1639 : /*-----------------------------------------------------------------
1640 : * Load Obj Block
1641 : *----------------------------------------------------------------*/
1642 0 : if ((poBlock = TABCreateMAPBlockFromFile(m_fp,
1643 : nBlockPtr,
1644 : 512, TRUE, TABReadWrite)) &&
1645 0 : poBlock->GetBlockClass() == TABMAP_OBJECT_BLOCK)
1646 : {
1647 0 : m_poCurObjBlock = (TABMAPObjectBlock*)poBlock;
1648 0 : poBlock = NULL;
1649 : }
1650 : else
1651 : {
1652 : CPLError(CE_Failure, CPLE_FileIO,
1653 : "LoadObjAndCoordBlocks() failed for object block at %d.",
1654 0 : nBlockPtr);
1655 0 : return -1;
1656 : }
1657 :
1658 : /*-----------------------------------------------------------------
1659 : * Load the last coord block in the chain
1660 : *----------------------------------------------------------------*/
1661 0 : if (m_poCurObjBlock->GetLastCoordBlockAddress() == 0)
1662 : {
1663 0 : m_poCurCoordBlock = NULL;
1664 : }
1665 0 : else if ((poBlock = TABCreateMAPBlockFromFile(m_fp,
1666 : m_poCurObjBlock->GetLastCoordBlockAddress(),
1667 : 512, TRUE, TABReadWrite)) &&
1668 0 : poBlock->GetBlockClass() == TABMAP_COORD_BLOCK)
1669 : {
1670 0 : m_poCurCoordBlock = (TABMAPCoordBlock*)poBlock;
1671 0 : m_poCurCoordBlock->SetMAPBlockManagerRef(&m_oBlockManager);
1672 0 : poBlock = NULL;
1673 : }
1674 : else
1675 : {
1676 : CPLError(CE_Failure, CPLE_FileIO,
1677 : "LoadObjAndCoordBlocks() failed for coord block at %d.",
1678 0 : m_poCurObjBlock->GetLastCoordBlockAddress());
1679 0 : return -1;
1680 : }
1681 :
1682 0 : return 0;
1683 : }
1684 :
1685 : /**********************************************************************
1686 : * TABMAPFile::SplitObjBlock()
1687 : *
1688 : * Split m_poCurObjBlock using Guttman algorithm.
1689 : *
1690 : * SplitObjBlock() doe its job so that the current obj block will remain
1691 : * the best candidate to receive the new object to add. It also flushes
1692 : * everything to disk and will update m_poCurCoordBlock to point to the
1693 : * last coord block in the chain, ready to accept new data
1694 : *
1695 : * Updates to the spatial index are left to the caller.
1696 : *
1697 : * Returns the TABMAPObjBlock of the second block for use by the caller
1698 : * in updating the spatial index, or NULL in case of error.
1699 : **********************************************************************/
1700 0 : TABMAPObjectBlock *TABMAPFile::SplitObjBlock(TABMAPObjHdr *poObjHdrToAdd,
1701 : int nSizeOfObjToAdd)
1702 : {
1703 0 : TABMAPObjHdr **papoSrcObjHdrs = NULL, *poObjHdr=NULL;
1704 0 : int i, numSrcObj = 0;
1705 :
1706 : /*-----------------------------------------------------------------
1707 : * Read all object headers
1708 : *----------------------------------------------------------------*/
1709 0 : m_poCurObjBlock->Rewind();
1710 0 : while ((poObjHdr = TABMAPObjHdr::ReadNextObj(m_poCurObjBlock,
1711 : m_poHeader)) != NULL)
1712 : {
1713 0 : if (papoSrcObjHdrs == NULL || numSrcObj%10 == 0)
1714 : {
1715 : // Realloc the array... by steps of 10
1716 : papoSrcObjHdrs = (TABMAPObjHdr**)CPLRealloc(papoSrcObjHdrs,
1717 : (numSrcObj+10)*
1718 0 : sizeof(TABMAPObjHdr*));
1719 : }
1720 0 : papoSrcObjHdrs[numSrcObj++] = poObjHdr;
1721 : }
1722 0 : CPLAssert(numSrcObj > 0);
1723 :
1724 : /*-----------------------------------------------------------------
1725 : * Reset current obj and coord block
1726 : *----------------------------------------------------------------*/
1727 0 : GInt32 nFirstSrcCoordBlock = m_poCurObjBlock->GetFirstCoordBlockAddress();
1728 :
1729 : m_poCurObjBlock->InitNewBlock(m_fp, 512,
1730 0 : m_poCurObjBlock->GetStartAddress());
1731 :
1732 0 : TABMAPCoordBlock *poSrcCoordBlock = m_poCurCoordBlock;
1733 0 : m_poCurCoordBlock = NULL;
1734 :
1735 : /*-----------------------------------------------------------------
1736 : * Create new obj and coord block
1737 : *----------------------------------------------------------------*/
1738 0 : TABMAPObjectBlock *poNewObjBlock = new TABMAPObjectBlock(m_eAccessMode);
1739 0 : poNewObjBlock->InitNewBlock(m_fp, 512, m_oBlockManager.AllocNewBlock());
1740 :
1741 : /* Coord block will be alloc'd automatically*/
1742 0 : TABMAPCoordBlock *poNewCoordBlock = NULL;
1743 :
1744 : /*-----------------------------------------------------------------
1745 : * Pick Seeds for each block
1746 : *----------------------------------------------------------------*/
1747 : TABMAPIndexEntry *pasSrcEntries =
1748 0 : (TABMAPIndexEntry*)CPLMalloc(numSrcObj*sizeof(TABMAPIndexEntry));
1749 0 : for (i=0; i<numSrcObj; i++)
1750 : {
1751 0 : pasSrcEntries[i].XMin = papoSrcObjHdrs[i]->m_nMinX;
1752 0 : pasSrcEntries[i].YMin = papoSrcObjHdrs[i]->m_nMinY;
1753 0 : pasSrcEntries[i].XMax = papoSrcObjHdrs[i]->m_nMaxX;
1754 0 : pasSrcEntries[i].YMax = papoSrcObjHdrs[i]->m_nMaxY;
1755 : }
1756 :
1757 : int nSeed1, nSeed2;
1758 : TABMAPIndexBlock::PickSeedsForSplit(pasSrcEntries, numSrcObj, -1,
1759 : poObjHdrToAdd->m_nMinX,
1760 : poObjHdrToAdd->m_nMinY,
1761 : poObjHdrToAdd->m_nMaxX,
1762 : poObjHdrToAdd->m_nMaxY,
1763 0 : nSeed1, nSeed2);
1764 0 : CPLFree(pasSrcEntries);
1765 0 : pasSrcEntries = NULL;
1766 :
1767 : /*-----------------------------------------------------------------
1768 : * Assign the seeds to their respective block
1769 : *----------------------------------------------------------------*/
1770 : // Insert nSeed1 in this block
1771 0 : poObjHdr = papoSrcObjHdrs[nSeed1];
1772 0 : if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
1773 : m_poCurObjBlock, &m_poCurCoordBlock) <= 0)
1774 0 : return NULL;
1775 :
1776 : // Move nSeed2 to 2nd block
1777 0 : poObjHdr = papoSrcObjHdrs[nSeed2];
1778 0 : if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
1779 : poNewObjBlock, &poNewCoordBlock) <= 0)
1780 0 : return NULL;
1781 :
1782 : /*-----------------------------------------------------------------
1783 : * Go through the rest of the entries and assign them to one
1784 : * of the 2 blocks
1785 : *
1786 : * Criteria is minimal area difference.
1787 : * Resolve ties by adding the entry to the block with smaller total
1788 : * area, then to the one with fewer entries, then to either.
1789 : *----------------------------------------------------------------*/
1790 0 : for(int iEntry=0; iEntry<numSrcObj; iEntry++)
1791 : {
1792 0 : if (iEntry == nSeed1 || iEntry == nSeed2)
1793 0 : continue;
1794 :
1795 0 : poObjHdr = papoSrcObjHdrs[iEntry];
1796 :
1797 0 : int nObjSize = m_poHeader->GetMapObjectSize(poObjHdr->m_nType);
1798 :
1799 : // If one of the two blocks is almost full then all remaining
1800 : // entries should go to the other block
1801 0 : if (m_poCurObjBlock->GetNumUnusedBytes() < nObjSize+nSizeOfObjToAdd )
1802 : {
1803 0 : if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
1804 : poNewObjBlock, &poNewCoordBlock) <= 0)
1805 0 : return NULL;
1806 0 : continue;
1807 : }
1808 0 : else if (poNewObjBlock->GetNumUnusedBytes() < nObjSize+nSizeOfObjToAdd)
1809 : {
1810 0 : if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
1811 : m_poCurObjBlock, &m_poCurCoordBlock) <= 0)
1812 0 : return NULL;
1813 0 : continue;
1814 : }
1815 :
1816 :
1817 : // Decide which of the two blocks to put this entry in
1818 : GInt32 nXMin, nYMin, nXMax, nYMax;
1819 0 : m_poCurObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax);
1820 : double dAreaDiff1 =
1821 : TABMAPIndexBlock::ComputeAreaDiff(nXMin, nYMin,
1822 : nXMax, nYMax,
1823 : poObjHdr->m_nMinX,
1824 : poObjHdr->m_nMinY,
1825 : poObjHdr->m_nMaxX,
1826 0 : poObjHdr->m_nMaxY);
1827 :
1828 0 : poNewObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax);
1829 : double dAreaDiff2 =
1830 : TABMAPIndexBlock::ComputeAreaDiff(nXMin, nYMin, nXMax, nYMax,
1831 : poObjHdr->m_nMinX,
1832 : poObjHdr->m_nMinY,
1833 : poObjHdr->m_nMaxX,
1834 0 : poObjHdr->m_nMaxY);
1835 :
1836 0 : if (dAreaDiff1 < dAreaDiff2)
1837 : {
1838 : // This entry stays in this block
1839 0 : if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
1840 : m_poCurObjBlock, &m_poCurCoordBlock) <= 0)
1841 0 : return NULL;
1842 : }
1843 : else
1844 : {
1845 : // This entry goes to new block
1846 0 : if (MoveObjToBlock(poObjHdr, poSrcCoordBlock,
1847 : poNewObjBlock, &poNewCoordBlock) <= 0)
1848 0 : return NULL;
1849 : }
1850 : }
1851 :
1852 : /* Cleanup papoSrcObjHdrs[] */
1853 0 : for(i=0; i<numSrcObj; i++)
1854 : {
1855 0 : delete papoSrcObjHdrs[i];
1856 : }
1857 0 : CPLFree(papoSrcObjHdrs);
1858 0 : papoSrcObjHdrs = NULL;
1859 :
1860 : /*-----------------------------------------------------------------
1861 : * Delete second coord block if one was created
1862 : * Refs to coord block were kept up to date by MoveObjToBlock()
1863 : * We just need to commit to file and delete the object now.
1864 : *----------------------------------------------------------------*/
1865 0 : if (poNewCoordBlock)
1866 : {
1867 0 : if (poNewCoordBlock->CommitToFile() != 0)
1868 0 : return NULL;
1869 0 : delete poNewCoordBlock;
1870 : }
1871 :
1872 : /*-----------------------------------------------------------------
1873 : * Release unused coord. data blocks
1874 : *----------------------------------------------------------------*/
1875 0 : if (poSrcCoordBlock)
1876 : {
1877 0 : if (poSrcCoordBlock->GetStartAddress() != nFirstSrcCoordBlock)
1878 : {
1879 0 : if (poSrcCoordBlock->GotoByteInFile(nFirstSrcCoordBlock, TRUE) != 0)
1880 0 : return NULL;
1881 : }
1882 :
1883 0 : int nNextCoordBlock = poSrcCoordBlock->GetNextCoordBlock();
1884 0 : while(poSrcCoordBlock != NULL)
1885 : {
1886 : // Mark this block as deleted
1887 0 : if (poSrcCoordBlock->CommitAsDeleted(m_oBlockManager.
1888 : GetFirstGarbageBlock()) != 0)
1889 0 : return NULL;
1890 0 : m_oBlockManager.PushGarbageBlock(poSrcCoordBlock->GetStartAddress());
1891 :
1892 : // Advance to next
1893 0 : if (nNextCoordBlock > 0)
1894 : {
1895 0 : if (poSrcCoordBlock->GotoByteInFile(nNextCoordBlock, TRUE) != 0)
1896 0 : return NULL;
1897 0 : nNextCoordBlock = poSrcCoordBlock->GetNextCoordBlock();
1898 : }
1899 : else
1900 : {
1901 : // end of chain
1902 0 : delete poSrcCoordBlock;
1903 0 : poSrcCoordBlock = NULL;
1904 : }
1905 : }
1906 : }
1907 :
1908 :
1909 0 : if (poNewObjBlock->CommitToFile() != 0)
1910 0 : return NULL;
1911 :
1912 0 : return poNewObjBlock;
1913 : }
1914 :
1915 : /**********************************************************************
1916 : * TABMAPFile::MoveObjToBlock()
1917 : *
1918 : * Moves an object and its coord data to a new ObjBlock. Used when
1919 : * splitting Obj Blocks.
1920 : *
1921 : * May update the value of ppoCoordBlock if a new coord block had to
1922 : * be created.
1923 : *
1924 : * Returns the address where new object is stored on success, -1 on error.
1925 : **********************************************************************/
1926 0 : int TABMAPFile::MoveObjToBlock(TABMAPObjHdr *poObjHdr,
1927 : TABMAPCoordBlock *poSrcCoordBlock,
1928 : TABMAPObjectBlock *poDstObjBlock,
1929 : TABMAPCoordBlock **ppoDstCoordBlock)
1930 : {
1931 : /*-----------------------------------------------------------------
1932 : * Copy Coord data if applicable
1933 : * We use a temporary TABFeature object to handle the reading/writing
1934 : * of coord block data.
1935 : *----------------------------------------------------------------*/
1936 0 : if (m_poHeader->MapObjectUsesCoordBlock(poObjHdr->m_nType))
1937 : {
1938 0 : TABMAPObjHdrWithCoord *poObjHdrCoord =(TABMAPObjHdrWithCoord*)poObjHdr;
1939 0 : OGRFeatureDefn * poDummyDefn = new OGRFeatureDefn;
1940 : // Ref count defaults to 0... set it to 1
1941 0 : poDummyDefn->Reference();
1942 :
1943 : TABFeature *poFeature =
1944 0 : TABFeature::CreateFromMapInfoType(poObjHdr->m_nType, poDummyDefn);
1945 :
1946 :
1947 0 : if (PrepareCoordBlock(poObjHdrCoord->m_nType,
1948 : poDstObjBlock, ppoDstCoordBlock) != 0)
1949 0 : return -1;
1950 :
1951 0 : GInt32 nSrcCoordPtr = poObjHdrCoord->m_nCoordBlockPtr;
1952 :
1953 : /* Copy Coord data
1954 : * poObjHdrCoord->m_nCoordBlockPtr will be set by WriteGeometry...
1955 : * We pass second arg to GotoByteInFile() to force reading from file
1956 : * if nSrcCoordPtr is not in current block
1957 : */
1958 0 : if (poSrcCoordBlock->GotoByteInFile(nSrcCoordPtr, TRUE) != 0 ||
1959 : poFeature->ReadGeometryFromMAPFile(this, poObjHdr,
1960 : TRUE /* bCoordDataOnly */,
1961 0 : &poSrcCoordBlock) != 0 ||
1962 : poFeature->WriteGeometryToMAPFile(this, poObjHdr,
1963 : TRUE /* bCoordDataOnly */,
1964 0 : ppoDstCoordBlock) != 0)
1965 : {
1966 0 : delete poFeature;
1967 0 : delete poDummyDefn;
1968 0 : return -1;
1969 : }
1970 :
1971 :
1972 : // Update the references to dest coord block in the MAPObjBlock
1973 : // in case new block has been alloc'd since PrepareCoordBlock()
1974 : //
1975 0 : poDstObjBlock->AddCoordBlockRef((*ppoDstCoordBlock)->GetStartAddress());
1976 : /* Cleanup */
1977 0 : delete poFeature;
1978 0 : poDummyDefn->Release();
1979 : }
1980 :
1981 : /*-----------------------------------------------------------------
1982 : * Prepare and Write ObjHdr to this ObjBlock
1983 : *----------------------------------------------------------------*/
1984 0 : int nObjPtr = poDstObjBlock->PrepareNewObject(poObjHdr);
1985 0 : if (nObjPtr < 0 ||
1986 : poDstObjBlock->CommitNewObject(poObjHdr) != 0)
1987 : {
1988 : CPLError(CE_Failure, CPLE_FileIO,
1989 : "Failed writing object header for feature id %d",
1990 0 : poObjHdr->m_nId);
1991 0 : return -1;
1992 : }
1993 :
1994 : /*-----------------------------------------------------------------
1995 : * Update .ID Index
1996 : *----------------------------------------------------------------*/
1997 0 : m_poIdIndex->SetObjPtr(poObjHdr->m_nId, nObjPtr);
1998 :
1999 0 : return nObjPtr;
2000 : }
2001 :
2002 : /**********************************************************************
2003 : * TABMAPFile::PrepareCoordBlock()
2004 : *
2005 : * Prepare the coord block to receive an object of specified type if one
2006 : * is needed, and update corresponding members in ObjBlock.
2007 : *
2008 : * May update the value of ppoCoordBlock and Returns 0 on success, -1 on error.
2009 : **********************************************************************/
2010 12 : int TABMAPFile::PrepareCoordBlock(int nObjType,
2011 : TABMAPObjectBlock *poObjBlock,
2012 : TABMAPCoordBlock **ppoCoordBlock)
2013 : {
2014 :
2015 : /*-----------------------------------------------------------------
2016 : * Prepare Coords block...
2017 : * create a new TABMAPCoordBlock if it was not done yet.
2018 : * Note that in write mode, TABCollections require read/write access
2019 : * to the coord block.
2020 : *----------------------------------------------------------------*/
2021 12 : if (m_poHeader->MapObjectUsesCoordBlock(nObjType))
2022 : {
2023 11 : if (*ppoCoordBlock == NULL)
2024 : {
2025 : *ppoCoordBlock = new TABMAPCoordBlock(m_eAccessMode==TABWrite?
2026 : TABReadWrite:
2027 2 : m_eAccessMode);
2028 : (*ppoCoordBlock)->InitNewBlock(m_fp, 512,
2029 2 : m_oBlockManager.AllocNewBlock());
2030 2 : (*ppoCoordBlock)->SetMAPBlockManagerRef(&m_oBlockManager);
2031 :
2032 : // Set the references to this coord block in the MAPObjBlock
2033 2 : poObjBlock->AddCoordBlockRef((*ppoCoordBlock)->GetStartAddress());
2034 :
2035 : }
2036 :
2037 11 : if ((*ppoCoordBlock)->GetNumUnusedBytes() < 4)
2038 : {
2039 0 : int nNewBlockOffset = m_oBlockManager.AllocNewBlock();
2040 0 : (*ppoCoordBlock)->SetNextCoordBlock(nNewBlockOffset);
2041 0 : (*ppoCoordBlock)->CommitToFile();
2042 0 : (*ppoCoordBlock)->InitNewBlock(m_fp, 512, nNewBlockOffset);
2043 : }
2044 :
2045 : // Make sure read/write pointer is at the end of the block
2046 11 : (*ppoCoordBlock)->SeekEnd();
2047 :
2048 11 : if (CPLGetLastErrorNo() != 0 && CPLGetLastErrorType() == CE_Failure)
2049 0 : return -1;
2050 : }
2051 :
2052 12 : return 0;
2053 : }
2054 :
2055 : /**********************************************************************
2056 : * TABMAPFile::GetCurObjType()
2057 : *
2058 : * Return the MapInfo object type of the object that the m_poCurObjBlock
2059 : * is pointing to. This value is set after a call to MoveToObjId().
2060 : *
2061 : * Returns a value >= 0 on success, -1 on error.
2062 : **********************************************************************/
2063 149 : int TABMAPFile::GetCurObjType()
2064 : {
2065 149 : return m_nCurObjType;
2066 : }
2067 :
2068 : /**********************************************************************
2069 : * TABMAPFile::GetCurObjId()
2070 : *
2071 : * Return the MapInfo object id of the object that the m_poCurObjBlock
2072 : * is pointing to. This value is set after a call to MoveToObjId().
2073 : *
2074 : * Returns a value >= 0 on success, -1 on error.
2075 : **********************************************************************/
2076 53 : int TABMAPFile::GetCurObjId()
2077 : {
2078 53 : return m_nCurObjId;
2079 : }
2080 :
2081 : /**********************************************************************
2082 : * TABMAPFile::GetCurObjBlock()
2083 : *
2084 : * Return the m_poCurObjBlock. If MoveToObjId() has previously been
2085 : * called then m_poCurObjBlock points to the beginning of the current
2086 : * object data.
2087 : *
2088 : * Returns a reference to an object owned by this TABMAPFile object, or
2089 : * NULL on error.
2090 : **********************************************************************/
2091 53 : TABMAPObjectBlock *TABMAPFile::GetCurObjBlock()
2092 : {
2093 53 : return m_poCurObjBlock;
2094 : }
2095 :
2096 : /**********************************************************************
2097 : * TABMAPFile::GetCurCoordBlock()
2098 : *
2099 : * Return the m_poCurCoordBlock. This function should be used after
2100 : * PrepareNewObj() to get the reference to the coord block that has
2101 : * just been initialized.
2102 : *
2103 : * Returns a reference to an object owned by this TABMAPFile object, or
2104 : * NULL on error.
2105 : **********************************************************************/
2106 11 : TABMAPCoordBlock *TABMAPFile::GetCurCoordBlock()
2107 : {
2108 11 : return m_poCurCoordBlock;
2109 : }
2110 :
2111 : /**********************************************************************
2112 : * TABMAPFile::GetCoordBlock()
2113 : *
2114 : * Return a TABMAPCoordBlock object ready to read coordinates from it.
2115 : * The block that contains nFileOffset will automatically be
2116 : * loaded, and if nFileOffset is the beginning of a new block then the
2117 : * pointer will be moved to the beginning of the data.
2118 : *
2119 : * The contents of the returned object is only valid until the next call
2120 : * to GetCoordBlock().
2121 : *
2122 : * Returns a reference to an object owned by this TABMAPFile object, or
2123 : * NULL on error.
2124 : **********************************************************************/
2125 51 : TABMAPCoordBlock *TABMAPFile::GetCoordBlock(int nFileOffset)
2126 : {
2127 51 : if (m_eAccessMode != TABRead)
2128 0 : return NULL;
2129 :
2130 51 : if (m_poCurCoordBlock == NULL)
2131 : {
2132 2 : m_poCurCoordBlock = new TABMAPCoordBlock(m_eAccessMode);
2133 2 : m_poCurCoordBlock->InitNewBlock(m_fp, 512);
2134 : }
2135 :
2136 : /*-----------------------------------------------------------------
2137 : * Use GotoByteInFile() to go to the requested location. This will
2138 : * force loading the block if necessary and reading its header.
2139 : * If nFileOffset is at the beginning of the requested block, then
2140 : * we make sure to move the read pointer past the 8 bytes header
2141 : * to be ready to read coordinates data
2142 : *----------------------------------------------------------------*/
2143 51 : if ( m_poCurCoordBlock->GotoByteInFile(nFileOffset, TRUE) != 0)
2144 : {
2145 : // Failed... an error has already been reported.
2146 0 : return NULL;
2147 : }
2148 :
2149 51 : if (nFileOffset % 512 == 0)
2150 0 : m_poCurCoordBlock->GotoByteInBlock(8); // Skip Header
2151 :
2152 51 : return m_poCurCoordBlock;
2153 : }
2154 :
2155 : /**********************************************************************
2156 : * TABMAPFile::GetHeaderBlock()
2157 : *
2158 : * Return a reference to the MAP file's header block.
2159 : *
2160 : * The returned pointer is a reference to an object owned by this TABMAPFile
2161 : * object and should not be deleted by the caller.
2162 : *
2163 : * Return NULL if file has not been opened yet.
2164 : **********************************************************************/
2165 8 : TABMAPHeaderBlock *TABMAPFile::GetHeaderBlock()
2166 : {
2167 8 : return m_poHeader;
2168 : }
2169 :
2170 : /**********************************************************************
2171 : * TABMAPFile::GetIDFileRef()
2172 : *
2173 : * Return a reference to the .ID file attached to this .MAP file
2174 : *
2175 : * The returned pointer is a reference to an object owned by this TABMAPFile
2176 : * object and should not be deleted by the caller.
2177 : *
2178 : * Return NULL if file has not been opened yet.
2179 : **********************************************************************/
2180 0 : TABIDFile *TABMAPFile::GetIDFileRef()
2181 : {
2182 0 : return m_poIdIndex;
2183 : }
2184 :
2185 : /**********************************************************************
2186 : * TABMAPFile::GetIndexBlock()
2187 : *
2188 : * Return a reference to the requested index or object block..
2189 : *
2190 : * Ownership of the returned block is turned over to the caller, who should
2191 : * delete it when no longer needed. The type of the block can be determined
2192 : * with the GetBlockType() method.
2193 : *
2194 : * @param nFileOffset the offset in the map file of the spatial index
2195 : * block or object block to load.
2196 : *
2197 : * @return The requested TABMAPIndexBlock, TABMAPObjectBlock or NULL if the
2198 : * read fails for some reason.
2199 : **********************************************************************/
2200 2 : TABRawBinBlock *TABMAPFile::GetIndexObjectBlock( int nFileOffset )
2201 : {
2202 : /*----------------------------------------------------------------
2203 : * Read from the file
2204 : *---------------------------------------------------------------*/
2205 : GByte abyData[512];
2206 :
2207 2 : if (VSIFSeek(m_fp, nFileOffset, SEEK_SET) != 0
2208 : || VSIFRead(abyData, sizeof(GByte), 512, m_fp) != 512 )
2209 : {
2210 : CPLError(CE_Failure, CPLE_FileIO,
2211 : "GetIndexBlock() failed reading %d bytes at offset %d.",
2212 0 : 512, nFileOffset);
2213 0 : return NULL;
2214 : }
2215 :
2216 : /* -------------------------------------------------------------------- */
2217 : /* Create and initialize depending on the block type. */
2218 : /* -------------------------------------------------------------------- */
2219 2 : int nBlockType = abyData[0];
2220 : TABRawBinBlock *poBlock;
2221 :
2222 2 : if( nBlockType == TABMAP_INDEX_BLOCK )
2223 1 : poBlock = new TABMAPIndexBlock();
2224 : else
2225 1 : poBlock = new TABMAPObjectBlock();
2226 :
2227 2 : if( poBlock->InitBlockFromData(abyData, 512, 512,
2228 2 : TRUE, m_fp, nFileOffset) == -1 )
2229 : {
2230 0 : delete poBlock;
2231 0 : poBlock = NULL;
2232 : }
2233 :
2234 2 : return poBlock;
2235 : }
2236 :
2237 : /**********************************************************************
2238 : * TABMAPFile::InitDrawingTools()
2239 : *
2240 : * Init the drawing tools for this file.
2241 : *
2242 : * In Read mode, this will load the drawing tools from the file.
2243 : *
2244 : * In Write mode, this function will init an empty the tool def table.
2245 : *
2246 : * Reutrns 0 on success, -1 on error.
2247 : **********************************************************************/
2248 4 : int TABMAPFile::InitDrawingTools()
2249 : {
2250 4 : int nStatus = 0;
2251 :
2252 4 : if (m_poHeader == NULL)
2253 0 : return -1; // File not opened yet!
2254 :
2255 : /*-------------------------------------------------------------
2256 : * We want to perform this initialisation only ONCE
2257 : *------------------------------------------------------------*/
2258 4 : if (m_poToolDefTable != NULL)
2259 0 : return 0;
2260 :
2261 : /*-------------------------------------------------------------
2262 : * Create a new ToolDefTable... no more initialization is required
2263 : * unless we want to read tool blocks from file.
2264 : *------------------------------------------------------------*/
2265 4 : m_poToolDefTable = new TABToolDefTable;
2266 :
2267 6 : if (m_eAccessMode == TABRead && m_poHeader->m_nFirstToolBlock != 0)
2268 : {
2269 : TABMAPToolBlock *poBlock;
2270 :
2271 2 : poBlock = new TABMAPToolBlock(m_eAccessMode);
2272 2 : poBlock->InitNewBlock(m_fp, 512);
2273 :
2274 : /*-------------------------------------------------------------
2275 : * Use GotoByteInFile() to go to the first block's location. This will
2276 : * force loading the block if necessary and reading its header.
2277 : * Also make sure to move the read pointer past the 8 bytes header
2278 : * to be ready to read drawing tools data
2279 : *------------------------------------------------------------*/
2280 2 : if ( poBlock->GotoByteInFile(m_poHeader->m_nFirstToolBlock)!= 0)
2281 : {
2282 : // Failed... an error has already been reported.
2283 0 : delete poBlock;
2284 0 : return -1;
2285 : }
2286 :
2287 2 : poBlock->GotoByteInBlock(8);
2288 :
2289 2 : nStatus = m_poToolDefTable->ReadAllToolDefs(poBlock);
2290 2 : delete poBlock;
2291 : }
2292 :
2293 4 : return nStatus;
2294 : }
2295 :
2296 :
2297 : /**********************************************************************
2298 : * TABMAPFile::CommitDrawingTools()
2299 : *
2300 : * Write the drawing tools for this file.
2301 : *
2302 : * This function applies only to write access mode.
2303 : *
2304 : * Returns 0 on success, -1 on error.
2305 : **********************************************************************/
2306 3 : int TABMAPFile::CommitDrawingTools()
2307 : {
2308 3 : int nStatus = 0;
2309 :
2310 3 : if (m_eAccessMode != TABWrite || m_poHeader == NULL)
2311 : {
2312 : CPLError(CE_Failure, CPLE_AssertionFailed,
2313 0 : "CommitDrawingTools() failed: file not opened for write access.");
2314 0 : return -1;
2315 : }
2316 :
2317 3 : if (m_poToolDefTable == NULL ||
2318 : (m_poToolDefTable->GetNumPen() +
2319 : m_poToolDefTable->GetNumBrushes() +
2320 : m_poToolDefTable->GetNumFonts() +
2321 : m_poToolDefTable->GetNumSymbols()) == 0)
2322 : {
2323 1 : return 0; // Nothing to do!
2324 : }
2325 :
2326 : /*-------------------------------------------------------------
2327 : * Create a new TABMAPToolBlock and update header fields
2328 : *------------------------------------------------------------*/
2329 : TABMAPToolBlock *poBlock;
2330 :
2331 2 : poBlock = new TABMAPToolBlock(m_eAccessMode);
2332 2 : poBlock->InitNewBlock(m_fp, 512, m_oBlockManager.AllocNewBlock());
2333 2 : poBlock->SetMAPBlockManagerRef(&m_oBlockManager);
2334 :
2335 2 : m_poHeader->m_nFirstToolBlock = poBlock->GetStartAddress();
2336 :
2337 2 : m_poHeader->m_numPenDefs = (GByte)m_poToolDefTable->GetNumPen();
2338 2 : m_poHeader->m_numBrushDefs = (GByte)m_poToolDefTable->GetNumBrushes();
2339 2 : m_poHeader->m_numFontDefs = (GByte)m_poToolDefTable->GetNumFonts();
2340 2 : m_poHeader->m_numSymbolDefs = (GByte)m_poToolDefTable->GetNumSymbols();
2341 :
2342 : /*-------------------------------------------------------------
2343 : * Do the actual work and delete poBlock
2344 : * (Note that poBlock will have already been committed to the file
2345 : * by WriteAllToolDefs() )
2346 : *------------------------------------------------------------*/
2347 2 : nStatus = m_poToolDefTable->WriteAllToolDefs(poBlock);
2348 :
2349 2 : m_poHeader->m_numMapToolBlocks = (GInt16)poBlock->GetNumBlocksInChain();
2350 :
2351 4 : delete poBlock;
2352 :
2353 2 : return nStatus;
2354 : }
2355 :
2356 :
2357 : /**********************************************************************
2358 : * TABMAPFile::ReadPenDef()
2359 : *
2360 : * Fill the TABPenDef structure with the definition of the specified pen
2361 : * index... (1-based pen index)
2362 : *
2363 : * If nPenIndex==0 or is invalid, then the structure is cleared.
2364 : *
2365 : * Returns 0 on success, -1 on error (i.e. Pen not found).
2366 : **********************************************************************/
2367 52 : int TABMAPFile::ReadPenDef(int nPenIndex, TABPenDef *psDef)
2368 : {
2369 : TABPenDef *psTmp;
2370 :
2371 52 : if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
2372 0 : return -1;
2373 :
2374 52 : if (psDef && m_poToolDefTable &&
2375 : (psTmp = m_poToolDefTable->GetPenDefRef(nPenIndex)) != NULL)
2376 : {
2377 52 : *psDef = *psTmp;
2378 : }
2379 0 : else if (psDef)
2380 : {
2381 : /* Init to MapInfo default */
2382 : static const TABPenDef csDefaultPen = MITAB_PEN_DEFAULT;
2383 0 : *psDef = csDefaultPen;
2384 0 : return -1;
2385 : }
2386 52 : return 0;
2387 : }
2388 :
2389 : /**********************************************************************
2390 : * TABMAPFile::WritePenDef()
2391 : *
2392 : * Write a Pen Tool to the map file and return the pen index that has
2393 : * been attributed to this Pen tool definition, or -1 if something went
2394 : * wrong
2395 : *
2396 : * Note that the returned index is a 1-based index. A value of 0
2397 : * indicates "none" in MapInfo.
2398 :
2399 : * Returns a value >= 0 on success, -1 on error
2400 : **********************************************************************/
2401 12 : int TABMAPFile::WritePenDef(TABPenDef *psDef)
2402 : {
2403 12 : if (psDef == NULL ||
2404 : (m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
2405 : m_poToolDefTable==NULL )
2406 : {
2407 0 : return -1;
2408 : }
2409 :
2410 12 : return m_poToolDefTable->AddPenDefRef(psDef);
2411 : }
2412 :
2413 :
2414 : /**********************************************************************
2415 : * TABMAPFile::ReadBrushDef()
2416 : *
2417 : * Fill the TABBrushDef structure with the definition of the specified Brush
2418 : * index... (1-based Brush index)
2419 : *
2420 : * If nBrushIndex==0 or is invalid, then the structure is cleared.
2421 : *
2422 : * Returns 0 on success, -1 on error (i.e. Brush not found).
2423 : **********************************************************************/
2424 51 : int TABMAPFile::ReadBrushDef(int nBrushIndex, TABBrushDef *psDef)
2425 : {
2426 : TABBrushDef *psTmp;
2427 :
2428 51 : if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
2429 0 : return -1;
2430 :
2431 51 : if (psDef && m_poToolDefTable &&
2432 : (psTmp = m_poToolDefTable->GetBrushDefRef(nBrushIndex)) != NULL)
2433 : {
2434 51 : *psDef = *psTmp;
2435 : }
2436 0 : else if (psDef)
2437 : {
2438 : /* Init to MapInfo default */
2439 : static const TABBrushDef csDefaultBrush = MITAB_BRUSH_DEFAULT;
2440 0 : *psDef = csDefaultBrush;
2441 0 : return -1;
2442 : }
2443 51 : return 0;
2444 : }
2445 :
2446 : /**********************************************************************
2447 : * TABMAPFile::WriteBrushDef()
2448 : *
2449 : * Write a Brush Tool to the map file and return the Brush index that has
2450 : * been attributed to this Brush tool definition, or -1 if something went
2451 : * wrong
2452 : *
2453 : * Note that the returned index is a 1-based index. A value of 0
2454 : * indicates "none" in MapInfo.
2455 :
2456 : * Returns a value >= 0 on success, -1 on error
2457 : **********************************************************************/
2458 11 : int TABMAPFile::WriteBrushDef(TABBrushDef *psDef)
2459 : {
2460 11 : if (psDef == NULL ||
2461 : (m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
2462 : m_poToolDefTable==NULL )
2463 : {
2464 0 : return -1;
2465 : }
2466 :
2467 11 : return m_poToolDefTable->AddBrushDefRef(psDef);
2468 : }
2469 :
2470 :
2471 : /**********************************************************************
2472 : * TABMAPFile::ReadFontDef()
2473 : *
2474 : * Fill the TABFontDef structure with the definition of the specified Font
2475 : * index... (1-based Font index)
2476 : *
2477 : * If nFontIndex==0 or is invalid, then the structure is cleared.
2478 : *
2479 : * Returns 0 on success, -1 on error (i.e. Font not found).
2480 : **********************************************************************/
2481 0 : int TABMAPFile::ReadFontDef(int nFontIndex, TABFontDef *psDef)
2482 : {
2483 : TABFontDef *psTmp;
2484 :
2485 0 : if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
2486 0 : return -1;
2487 :
2488 0 : if (psDef && m_poToolDefTable &&
2489 : (psTmp = m_poToolDefTable->GetFontDefRef(nFontIndex)) != NULL)
2490 : {
2491 0 : *psDef = *psTmp;
2492 : }
2493 0 : else if (psDef)
2494 : {
2495 : /* Init to MapInfo default */
2496 : static const TABFontDef csDefaultFont = MITAB_FONT_DEFAULT;
2497 0 : *psDef = csDefaultFont;
2498 0 : return -1;
2499 : }
2500 0 : return 0;
2501 : }
2502 :
2503 : /**********************************************************************
2504 : * TABMAPFile::WriteFontDef()
2505 : *
2506 : * Write a Font Tool to the map file and return the Font index that has
2507 : * been attributed to this Font tool definition, or -1 if something went
2508 : * wrong
2509 : *
2510 : * Note that the returned index is a 1-based index. A value of 0
2511 : * indicates "none" in MapInfo.
2512 :
2513 : * Returns a value >= 0 on success, -1 on error
2514 : **********************************************************************/
2515 0 : int TABMAPFile::WriteFontDef(TABFontDef *psDef)
2516 : {
2517 0 : if (psDef == NULL ||
2518 : (m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
2519 : m_poToolDefTable==NULL )
2520 : {
2521 0 : return -1;
2522 : }
2523 :
2524 0 : return m_poToolDefTable->AddFontDefRef(psDef);
2525 : }
2526 :
2527 : /**********************************************************************
2528 : * TABMAPFile::ReadSymbolDef()
2529 : *
2530 : * Fill the TABSymbolDef structure with the definition of the specified Symbol
2531 : * index... (1-based Symbol index)
2532 : *
2533 : * If nSymbolIndex==0 or is invalid, then the structure is cleared.
2534 : *
2535 : * Returns 0 on success, -1 on error (i.e. Symbol not found).
2536 : **********************************************************************/
2537 0 : int TABMAPFile::ReadSymbolDef(int nSymbolIndex, TABSymbolDef *psDef)
2538 : {
2539 : TABSymbolDef *psTmp;
2540 :
2541 0 : if (m_poToolDefTable == NULL && InitDrawingTools() != 0)
2542 0 : return -1;
2543 :
2544 0 : if (psDef && m_poToolDefTable &&
2545 : (psTmp = m_poToolDefTable->GetSymbolDefRef(nSymbolIndex)) != NULL)
2546 : {
2547 0 : *psDef = *psTmp;
2548 : }
2549 0 : else if (psDef)
2550 : {
2551 : /* Init to MapInfo default */
2552 : static const TABSymbolDef csDefaultSymbol = MITAB_SYMBOL_DEFAULT;
2553 0 : *psDef = csDefaultSymbol;
2554 0 : return -1;
2555 : }
2556 0 : return 0;
2557 : }
2558 :
2559 : /**********************************************************************
2560 : * TABMAPFile::WriteSymbolDef()
2561 : *
2562 : * Write a Symbol Tool to the map file and return the Symbol index that has
2563 : * been attributed to this Symbol tool definition, or -1 if something went
2564 : * wrong
2565 : *
2566 : * Note that the returned index is a 1-based index. A value of 0
2567 : * indicates "none" in MapInfo.
2568 :
2569 : * Returns a value >= 0 on success, -1 on error
2570 : **********************************************************************/
2571 0 : int TABMAPFile::WriteSymbolDef(TABSymbolDef *psDef)
2572 : {
2573 0 : if (psDef == NULL ||
2574 : (m_poToolDefTable == NULL && InitDrawingTools() != 0) ||
2575 : m_poToolDefTable==NULL )
2576 : {
2577 0 : return -1;
2578 : }
2579 :
2580 0 : return m_poToolDefTable->AddSymbolDefRef(psDef);
2581 : }
2582 :
2583 : #define ORDER_MIN_MAX(type,min,max) \
2584 : { if( (max) < (min) ) \
2585 : { type temp = (max); (max) = (min); (min) = temp; } }
2586 :
2587 : /**********************************************************************
2588 : * TABMAPFile::SetCoordFilter()
2589 : *
2590 : * Set the MBR of the area of interest... only objects that at least
2591 : * overlap with that area will be returned.
2592 : *
2593 : * @param sMin minimum x/y the file's projection coord.
2594 : * @param sMax maximum x/y the file's projection coord.
2595 : **********************************************************************/
2596 1 : void TABMAPFile::SetCoordFilter(TABVertex sMin, TABVertex sMax)
2597 : {
2598 1 : m_sMinFilter = sMin;
2599 1 : m_sMaxFilter = sMax;
2600 :
2601 1 : Coordsys2Int(sMin.x, sMin.y, m_XMinFilter, m_YMinFilter, TRUE);
2602 1 : Coordsys2Int(sMax.x, sMax.y, m_XMaxFilter, m_YMaxFilter, TRUE);
2603 :
2604 1 : ORDER_MIN_MAX(int,m_XMinFilter,m_XMaxFilter);
2605 1 : ORDER_MIN_MAX(int,m_YMinFilter,m_YMaxFilter);
2606 1 : ORDER_MIN_MAX(double,m_sMinFilter.x,m_sMaxFilter.x);
2607 1 : ORDER_MIN_MAX(double,m_sMinFilter.y,m_sMaxFilter.y);
2608 1 : }
2609 :
2610 : /**********************************************************************
2611 : * TABMAPFile::ResetCoordFilter()
2612 : *
2613 : * Reset the MBR of the area of interest to be the extents as defined
2614 : * in the header.
2615 : **********************************************************************/
2616 :
2617 16 : void TABMAPFile::ResetCoordFilter()
2618 :
2619 : {
2620 16 : m_XMinFilter = m_poHeader->m_nXMin;
2621 16 : m_YMinFilter = m_poHeader->m_nYMin;
2622 16 : m_XMaxFilter = m_poHeader->m_nXMax;
2623 16 : m_YMaxFilter = m_poHeader->m_nYMax;
2624 : Int2Coordsys(m_XMinFilter, m_YMinFilter,
2625 16 : m_sMinFilter.x, m_sMinFilter.y);
2626 : Int2Coordsys(m_XMaxFilter, m_YMaxFilter,
2627 16 : m_sMaxFilter.x, m_sMaxFilter.y);
2628 :
2629 16 : ORDER_MIN_MAX(int,m_XMinFilter,m_XMaxFilter);
2630 16 : ORDER_MIN_MAX(int,m_YMinFilter,m_YMaxFilter);
2631 16 : ORDER_MIN_MAX(double,m_sMinFilter.x,m_sMaxFilter.x);
2632 16 : ORDER_MIN_MAX(double,m_sMinFilter.y,m_sMaxFilter.y);
2633 16 : }
2634 :
2635 : /**********************************************************************
2636 : * TABMAPFile::GetCoordFilter()
2637 : *
2638 : * Get the MBR of the area of interest, as previously set by
2639 : * SetCoordFilter().
2640 : *
2641 : * @param sMin vertex into which the minimum x/y values put in coordsys space.
2642 : * @param sMax vertex into which the maximum x/y values put in coordsys space.
2643 : **********************************************************************/
2644 1 : void TABMAPFile::GetCoordFilter(TABVertex &sMin, TABVertex &sMax)
2645 : {
2646 1 : sMin = m_sMinFilter;
2647 1 : sMax = m_sMaxFilter;
2648 1 : }
2649 :
2650 : /**********************************************************************
2651 : * TABMAPFile::CommitSpatialIndex()
2652 : *
2653 : * Write the spatial index blocks tree for this file.
2654 : *
2655 : * This function applies only to write access mode.
2656 : *
2657 : * Returns 0 on success, -1 on error.
2658 : **********************************************************************/
2659 3 : int TABMAPFile::CommitSpatialIndex()
2660 : {
2661 3 : if (m_eAccessMode != TABWrite || m_poHeader == NULL)
2662 : {
2663 : CPLError(CE_Failure, CPLE_AssertionFailed,
2664 0 : "CommitSpatialIndex() failed: file not opened for write access.");
2665 0 : return -1;
2666 : }
2667 :
2668 3 : if (m_poSpIndex == NULL)
2669 : {
2670 1 : return 0; // Nothing to do!
2671 : }
2672 :
2673 : /*-------------------------------------------------------------
2674 : * Update header fields and commit index block
2675 : * (it's children will be recursively committed as well)
2676 : *------------------------------------------------------------*/
2677 : // Add 1 to Spatial Index Depth to account to the MapObjectBlocks
2678 : m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth,
2679 2 : (GByte)m_poSpIndex->GetCurMaxDepth()+1);
2680 :
2681 : m_poSpIndex->GetMBR(m_poHeader->m_nXMin, m_poHeader->m_nYMin,
2682 2 : m_poHeader->m_nXMax, m_poHeader->m_nYMax);
2683 :
2684 2 : return m_poSpIndex->CommitToFile();
2685 : }
2686 :
2687 :
2688 : /**********************************************************************
2689 : * TABMAPFile::GetMinTABFileVersion()
2690 : *
2691 : * Returns the minimum TAB file version number that can contain all the
2692 : * objects stored in this file.
2693 : **********************************************************************/
2694 3 : int TABMAPFile::GetMinTABFileVersion()
2695 : {
2696 3 : int nToolVersion = 0;
2697 :
2698 3 : if (m_poToolDefTable)
2699 2 : nToolVersion = m_poToolDefTable->GetMinVersionNumber();
2700 :
2701 3 : return MAX(nToolVersion, m_nMinTABVersion);
2702 : }
2703 :
2704 :
2705 : /**********************************************************************
2706 : * TABMAPFile::Dump()
2707 : *
2708 : * Dump block contents... available only in DEBUG mode.
2709 : **********************************************************************/
2710 : #ifdef DEBUG
2711 :
2712 0 : void TABMAPFile::Dump(FILE *fpOut /*=NULL*/)
2713 : {
2714 0 : if (fpOut == NULL)
2715 0 : fpOut = stdout;
2716 :
2717 0 : fprintf(fpOut, "----- TABMAPFile::Dump() -----\n");
2718 :
2719 0 : if (m_fp == NULL)
2720 : {
2721 0 : fprintf(fpOut, "File is not opened.\n");
2722 : }
2723 : else
2724 : {
2725 0 : fprintf(fpOut, "File is opened: %s\n", m_pszFname);
2726 : fprintf(fpOut, "Coordsys filter = (%g,%g)-(%g,%g)\n",
2727 0 : m_sMinFilter.x, m_sMinFilter.y, m_sMaxFilter.x,m_sMaxFilter.y);
2728 : fprintf(fpOut, "Int coord filter = (%d,%d)-(%d,%d)\n",
2729 0 : m_XMinFilter, m_YMinFilter, m_XMaxFilter,m_YMaxFilter);
2730 :
2731 0 : fprintf(fpOut, "\nFile Header follows ...\n\n");
2732 0 : m_poHeader->Dump(fpOut);
2733 0 : fprintf(fpOut, "... end of file header.\n\n");
2734 :
2735 0 : fprintf(fpOut, "Associated .ID file ...\n\n");
2736 0 : m_poIdIndex->Dump(fpOut);
2737 0 : fprintf(fpOut, "... end of ID file dump.\n\n");
2738 : }
2739 :
2740 0 : fflush(fpOut);
2741 0 : }
2742 :
2743 : #endif // DEBUG
2744 :
2745 :
2746 : /**********************************************************************
2747 : * TABMAPFile::DumpSpatialIndexToMIF()
2748 : *
2749 : * Dump the spatial index tree... available only in DEBUG mode.
2750 : **********************************************************************/
2751 : #ifdef DEBUG
2752 :
2753 0 : void TABMAPFile::DumpSpatialIndexToMIF(TABMAPIndexBlock *poNode,
2754 : FILE *fpMIF, FILE *fpMID,
2755 : int nParentId /*=-1*/,
2756 : int nIndexInNode /*=-1*/,
2757 : int nCurDepth /*=0*/,
2758 : int nMaxDepth /*=-1*/)
2759 : {
2760 0 : if (poNode == NULL)
2761 : {
2762 0 : if (m_poHeader && m_poHeader->m_nFirstIndexBlock != 0)
2763 : {
2764 : TABRawBinBlock *poBlock;
2765 :
2766 0 : poBlock = GetIndexObjectBlock(m_poHeader->m_nFirstIndexBlock);
2767 0 : if (poBlock && poBlock->GetBlockType() == TABMAP_INDEX_BLOCK)
2768 0 : poNode = (TABMAPIndexBlock *)poBlock;
2769 : }
2770 :
2771 0 : if (poNode == NULL)
2772 0 : return;
2773 : }
2774 :
2775 :
2776 : /*-------------------------------------------------------------
2777 : * Report info on current tree node
2778 : *------------------------------------------------------------*/
2779 0 : int numEntries = poNode->GetNumEntries();
2780 : GInt32 nXMin, nYMin, nXMax, nYMax;
2781 : double dXMin, dYMin, dXMax, dYMax;
2782 :
2783 0 : poNode->RecomputeMBR();
2784 0 : poNode->GetMBR(nXMin, nYMin, nXMax, nYMax);
2785 :
2786 0 : Int2Coordsys(nXMin, nYMin, dXMin, dYMin);
2787 0 : Int2Coordsys(nXMax, nYMax, dXMax, dYMax);
2788 :
2789 0 : VSIFPrintf(fpMIF, "RECT %g %g %g %g\n", dXMin, dYMin, dXMax, dYMax);
2790 0 : VSIFPrintf(fpMIF, " Brush(1, 0)\n"); /* No fill */
2791 :
2792 : VSIFPrintf(fpMID, "%d,%d,%d,%d,%g,%d,%d,%d,%d\n",
2793 : poNode->GetStartAddress(),
2794 : nParentId,
2795 : nIndexInNode,
2796 : nCurDepth,
2797 : MITAB_AREA(nXMin, nYMin, nXMax, nYMax),
2798 0 : nXMin, nYMin, nXMax, nYMax);
2799 :
2800 0 : if (nMaxDepth != 0)
2801 : {
2802 : /*-------------------------------------------------------------
2803 : * Loop through all entries, dumping each of them
2804 : *------------------------------------------------------------*/
2805 0 : for(int i=0; i<numEntries; i++)
2806 : {
2807 0 : TABMAPIndexEntry *psEntry = poNode->GetEntry(i);
2808 :
2809 : TABRawBinBlock *poBlock;
2810 0 : poBlock = GetIndexObjectBlock( psEntry->nBlockPtr );
2811 0 : if( poBlock == NULL )
2812 0 : continue;
2813 :
2814 0 : if( poBlock->GetBlockType() == TABMAP_INDEX_BLOCK )
2815 : {
2816 : /* Index block, dump recursively */
2817 : DumpSpatialIndexToMIF((TABMAPIndexBlock *)poBlock,
2818 : fpMIF, fpMID,
2819 : poNode->GetStartAddress(),
2820 0 : i, nCurDepth+1, nMaxDepth-1);
2821 : }
2822 : else
2823 : {
2824 : /* Object block, dump directly */
2825 0 : CPLAssert( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK );
2826 :
2827 0 : Int2Coordsys(psEntry->XMin, psEntry->YMin, dXMin, dYMin);
2828 0 : Int2Coordsys(psEntry->XMax, psEntry->YMax, dXMax, dYMax);
2829 :
2830 0 : VSIFPrintf(fpMIF, "RECT %g %g %g %g\n", dXMin, dYMin, dXMax, dYMax);
2831 0 : VSIFPrintf(fpMIF, " Brush(1, 0)\n"); /* No fill */
2832 :
2833 : VSIFPrintf(fpMID, "%d,%d,%d,%d,%g,%d,%d,%d,%d\n",
2834 : psEntry->nBlockPtr,
2835 : poNode->GetStartAddress(),
2836 : i,
2837 : nCurDepth+1,
2838 : MITAB_AREA(psEntry->XMin, psEntry->YMin,
2839 : psEntry->XMax, psEntry->YMax),
2840 : psEntry->XMin, psEntry->YMin,
2841 0 : psEntry->XMax, psEntry->YMax);
2842 : }
2843 :
2844 0 : delete poBlock;
2845 : }
2846 :
2847 : }
2848 :
2849 : }
2850 :
2851 : #endif // DEBUG
|