Line data Source code
1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the CPCIDSKVectorSegment class.
4 : *
5 : ******************************************************************************
6 : * Copyright (c) 2009
7 : * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
8 : *
9 : * Permission is hereby granted, free of charge, to any person obtaining a
10 : * copy of this software and associated documentation files (the "Software"),
11 : * to deal in the Software without restriction, including without limitation
12 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 : * and/or sell copies of the Software, and to permit persons to whom the
14 : * Software is furnished to do so, subject to the following conditions:
15 : *
16 : * The above copyright notice and this permission notice shall be included
17 : * in all copies or substantial portions of the Software.
18 : *
19 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 : * DEALINGS IN THE SOFTWARE.
26 : ****************************************************************************/
27 :
28 : #include "pcidsk_file.h"
29 : #include "pcidsk_exception.h"
30 : #include "core/pcidsk_utils.h"
31 : #include "segment/cpcidskvectorsegment.h"
32 : #include <cassert>
33 : #include <cstring>
34 : #include <cstdio>
35 : #include <algorithm>
36 : #include <limits>
37 :
38 : using namespace PCIDSK;
39 :
40 : /* -------------------------------------------------------------------- */
41 : /* Size of one page of loaded shapeids. This is not related to */
42 : /* the file format, and may be changed to alter the number of */
43 : /* shapeid pointers kept in RAM at one time from the shape */
44 : /* index. */
45 : /* -------------------------------------------------------------------- */
46 : static const int shapeid_page_size = 1024;
47 :
48 : /************************************************************************/
49 : /* CPCIDSKVectorSegment() */
50 : /************************************************************************/
51 :
52 1185 : CPCIDSKVectorSegment::CPCIDSKVectorSegment( PCIDSKFile *fileIn, int segmentIn,
53 1185 : const char *segment_pointer )
54 3555 : : CPCIDSKSegment( fileIn, segmentIn, segment_pointer )
55 :
56 : {
57 1185 : base_initialized = false;
58 1185 : needs_swap = false;
59 :
60 1185 : total_shape_count = 0;
61 1185 : valid_shape_count = 0;
62 :
63 1185 : last_shapes_id = NullShapeId;
64 1185 : last_shapes_index = -1;
65 :
66 1185 : raw_loaded_data_offset = 0;
67 1185 : vert_loaded_data_offset = 0;
68 1185 : record_loaded_data_offset = 0;
69 1185 : raw_loaded_data_dirty = false;
70 1185 : vert_loaded_data_dirty = false;
71 1185 : record_loaded_data_dirty = false;
72 :
73 1185 : shape_index_start = 0;
74 1185 : shape_index_page_dirty = false;
75 :
76 1185 : shapeid_map_active = false;
77 1185 : shapeid_pages_certainly_mapped = -1;
78 :
79 1185 : vh.vs = this;
80 :
81 1185 : highest_shapeid_used = NullShapeId;
82 1185 : }
83 :
84 : /************************************************************************/
85 : /* ~CPCIDSKVectorSegment() */
86 : /************************************************************************/
87 :
88 4740 : CPCIDSKVectorSegment::~CPCIDSKVectorSegment()
89 :
90 : {
91 : try
92 : {
93 1185 : Synchronize();
94 : }
95 0 : catch( const PCIDSKException& e )
96 : {
97 0 : fprintf(stderr, "Exception in ~CPCIDSKVectorSegment(): %s", e.what()); // ok
98 : }
99 2370 : }
100 :
101 : /************************************************************************/
102 : /* Synchronize() */
103 : /************************************************************************/
104 :
105 3373 : void CPCIDSKVectorSegment::Synchronize()
106 : {
107 3373 : if( base_initialized )
108 : {
109 3371 : FlushSegHeaderIfNeeded();
110 :
111 3371 : FlushDataBuffer( sec_vert );
112 3371 : FlushDataBuffer( sec_record );
113 :
114 3371 : di[sec_vert].Flush();
115 3371 : di[sec_record].Flush();
116 :
117 3371 : FlushLoadedShapeIndex();
118 :
119 3371 : if( GetHeader().GetInt( 192, 16 ) != total_shape_count
120 3371 : && file->GetUpdatable() )
121 : {
122 51 : GetHeader().Put( total_shape_count, 192, 16 );
123 51 : FlushHeader();
124 : }
125 : }
126 3373 : }
127 :
128 : /************************************************************************/
129 : /* Initialize() */
130 : /* */
131 : /* Initialize the header of a new vector segment in a */
132 : /* consistent state for an empty segment. */
133 : /************************************************************************/
134 :
135 1088 : void CPCIDSKVectorSegment::Initialize()
136 :
137 : {
138 1088 : needs_swap = !BigEndianSystem();
139 :
140 : /* -------------------------------------------------------------------- */
141 : /* Initialize the header that occurs within the regular segment */
142 : /* data. */
143 : /* -------------------------------------------------------------------- */
144 1088 : vh.InitializeNew();
145 :
146 : /* -------------------------------------------------------------------- */
147 : /* Initialize the values in the generic segment header. */
148 : /* -------------------------------------------------------------------- */
149 1088 : PCIDSKBuffer &head = GetHeader();
150 :
151 1088 : head.Put( "METRE", 160, 16 );
152 1088 : head.Put( 1.0, 176, 16 );
153 1088 : head.Put( 0, 192, 16 );
154 1088 : head.Put( 0, 208, 16 );
155 1088 : head.Put( 0, 224, 16 );
156 1088 : head.Put( "", 240, 16 );
157 1088 : head.Put( 0, 256, 16 );
158 1088 : head.Put( 0, 272, 16 );
159 :
160 : #ifdef PCIMAJORVERSION
161 : PCIDSK::ShapeField oFieldsDefault;
162 : oFieldsDefault.SetValue(std::vector<int32>{});
163 :
164 : // Add the RingStart field, because it can't be added after
165 : // shapes have been added. This is a bug that should be properly fixed
166 : AddField(ATT_RINGSTART,
167 : PCIDSK::FieldTypeCountedInt,
168 : "", "",
169 : &oFieldsDefault);
170 : #endif
171 :
172 1088 : FlushHeader();
173 1088 : }
174 :
175 : /************************************************************************/
176 : /* LoadHeader() */
177 : /* */
178 : /* Initialize minimum information from the vector segment */
179 : /* header. We defer this till an actual vector related action */
180 : /* is taken. */
181 : /************************************************************************/
182 :
183 7704 : void CPCIDSKVectorSegment::LoadHeader()
184 :
185 : {
186 7704 : if( base_initialized )
187 6521 : return;
188 :
189 1183 : base_initialized = true;
190 :
191 1183 : needs_swap = !BigEndianSystem();
192 :
193 1183 : vh.InitializeExisting();
194 :
195 : // When the IDB code deletes a shape, it simply writes a -1
196 : // into the index. We need to know how many actual valid shapes
197 : // there are in the segment, so count them
198 1183 : valid_shape_count = 0;
199 1183 : ShapeId iShape = FindFirst();
200 1316 : while (iShape != NullShapeId)
201 : {
202 133 : ++valid_shape_count;
203 133 : iShape = FindNext(iShape);
204 : }
205 : }
206 :
207 : /************************************************************************/
208 : /* ReadField() */
209 : /* */
210 : /* Read a value from the indicated offset in a section of the */
211 : /* vector segment, and place the value into a ShapeField */
212 : /* structure based on the passed in field type. */
213 : /************************************************************************/
214 :
215 4442 : uint32 CPCIDSKVectorSegment::ReadField( uint32 offset, ShapeField& field,
216 : ShapeFieldType field_type,
217 : int section )
218 :
219 : {
220 4442 : switch( field_type )
221 : {
222 1397 : case FieldTypeInteger:
223 : {
224 : int32 value;
225 1397 : memcpy( &value, GetData( section, offset, nullptr, 4), 4 );
226 1397 : if( needs_swap )
227 1397 : SwapData( &value, 4, 1 );
228 1397 : field.SetValue( value );
229 1397 : return offset + 4;
230 : }
231 :
232 0 : case FieldTypeFloat:
233 : {
234 : float value;
235 0 : memcpy( &value, GetData( section, offset, nullptr, 4), 4 );
236 0 : if( needs_swap )
237 0 : SwapData( &value, 4, 1 );
238 0 : field.SetValue( value );
239 0 : return offset + 4;
240 : }
241 :
242 64 : case FieldTypeDouble:
243 : {
244 : double value;
245 64 : memcpy( &value, GetData( section, offset, nullptr, 8), 8 );
246 64 : if( needs_swap )
247 64 : SwapData( &value, 8, 1 );
248 64 : field.SetValue( value );
249 64 : return offset + 8;
250 : }
251 :
252 2981 : case FieldTypeString:
253 : {
254 : int available;
255 2981 : char *srcdata = GetData( section, offset, &available, 1 );
256 :
257 : // Simple case -- all initially available.
258 2981 : int string_len = 0;
259 :
260 4623 : while( srcdata[string_len] != '\0' && available - string_len > 0 )
261 1642 : string_len++;
262 :
263 2981 : if( string_len < available && srcdata[string_len] == '\0' )
264 : {
265 2981 : std::string value( srcdata, string_len );
266 2981 : field.SetValue( value );
267 2981 : return offset + string_len + 1;
268 : }
269 :
270 0 : std::string value;
271 :
272 0 : while( *srcdata != '\0' )
273 : {
274 0 : value += *(srcdata++);
275 0 : offset++;
276 0 : available--;
277 0 : if( available == 0 )
278 0 : srcdata = GetData( section, offset, &available, 1 );
279 : }
280 :
281 0 : field.SetValue( value );
282 0 : return offset+1;
283 : }
284 :
285 0 : case FieldTypeCountedInt:
286 : {
287 0 : std::vector<int32> value;
288 : int32 count;
289 0 : char *srcdata = GetData( section, offset, nullptr, 4 );
290 0 : memcpy( &count, srcdata, 4 );
291 0 : if( needs_swap )
292 0 : SwapData( &count, 4, 1 );
293 :
294 0 : value.resize( count );
295 0 : if( count > 0 )
296 : {
297 0 : if( offset > std::numeric_limits<uint32>::max() - 8 )
298 0 : return ThrowPCIDSKException(0, "Invalid offset = %u", offset);
299 0 : memcpy( &(value[0]), GetData(section,offset+4,nullptr,4*count), 4*count );
300 0 : if( needs_swap )
301 0 : SwapData( &(value[0]), 4, count );
302 : }
303 :
304 0 : field.SetValue( value );
305 0 : return offset + 4 + 4*count;
306 : }
307 :
308 0 : default:
309 0 : return ThrowPCIDSKException(0, "Unhandled field type %d", field_type);
310 : }
311 : }
312 :
313 : /************************************************************************/
314 : /* WriteField() */
315 : /* */
316 : /* Write a field value into a buffer, growing the buffer if */
317 : /* needed to hold the value. */
318 : /************************************************************************/
319 :
320 912 : uint32 CPCIDSKVectorSegment::WriteField( uint32 offset,
321 : const ShapeField& field,
322 : PCIDSKBuffer& buffer )
323 :
324 : {
325 : /* -------------------------------------------------------------------- */
326 : /* How much space do we need for this value? */
327 : /* -------------------------------------------------------------------- */
328 912 : uint32 item_size = 0;
329 :
330 912 : switch( field.GetType() )
331 : {
332 226 : case FieldTypeInteger:
333 226 : item_size = 4;
334 226 : break;
335 :
336 0 : case FieldTypeFloat:
337 0 : item_size = 4;
338 0 : break;
339 :
340 88 : case FieldTypeDouble:
341 88 : item_size = 8;
342 88 : break;
343 :
344 598 : case FieldTypeString:
345 598 : item_size = static_cast<uint32>(field.GetValueString().size()) + 1;
346 598 : break;
347 :
348 0 : case FieldTypeCountedInt:
349 0 : item_size = static_cast<uint32>(field.GetValueCountedInt().size()) * 4 + 4;
350 0 : break;
351 :
352 0 : default:
353 0 : assert( 0 );
354 : item_size = 0;
355 : break;
356 : }
357 :
358 : /* -------------------------------------------------------------------- */
359 : /* Do we need to grow the buffer to hold this? Try to make it */
360 : /* plenty larger. */
361 : /* -------------------------------------------------------------------- */
362 912 : if( item_size + offset > (uint32) buffer.buffer_size )
363 212 : buffer.SetSize( buffer.buffer_size*2 + item_size );
364 :
365 : /* -------------------------------------------------------------------- */
366 : /* Write to the buffer, and byte swap if needed. */
367 : /* -------------------------------------------------------------------- */
368 912 : switch( field.GetType() )
369 : {
370 226 : case FieldTypeInteger:
371 : {
372 226 : int32 value = field.GetValueInteger();
373 226 : if( needs_swap )
374 226 : SwapData( &value, 4, 1 );
375 226 : memcpy( buffer.buffer+offset, &value, 4 );
376 226 : break;
377 : }
378 :
379 0 : case FieldTypeFloat:
380 : {
381 0 : float value = field.GetValueFloat();
382 0 : if( needs_swap )
383 0 : SwapData( &value, 4, 1 );
384 0 : memcpy( buffer.buffer+offset, &value, 4 );
385 0 : break;
386 : }
387 :
388 88 : case FieldTypeDouble:
389 : {
390 88 : double value = field.GetValueDouble();
391 88 : if( needs_swap )
392 88 : SwapData( &value, 8, 1 );
393 88 : memcpy( buffer.buffer+offset, &value, 8 );
394 88 : break;
395 : }
396 :
397 598 : case FieldTypeString:
398 : {
399 1196 : std::string value = field.GetValueString();
400 598 : memcpy( buffer.buffer+offset, value.c_str(), item_size );
401 598 : break;
402 : }
403 :
404 0 : case FieldTypeCountedInt:
405 : {
406 0 : std::vector<int32> value = field.GetValueCountedInt();
407 0 : uint32 count = static_cast<uint32>(value.size());
408 0 : memcpy( buffer.buffer+offset, &count, 4 );
409 0 : if( count > 0 )
410 : {
411 0 : memcpy( buffer.buffer+offset+4, &(value[0]), count * 4 );
412 0 : if( needs_swap )
413 0 : SwapData( buffer.buffer+offset, 4, count+1 );
414 : }
415 0 : break;
416 : }
417 :
418 0 : default:
419 0 : assert( 0 );
420 : break;
421 : }
422 :
423 912 : return offset + item_size;
424 : }
425 :
426 : /************************************************************************/
427 : /* GetData() */
428 : /************************************************************************/
429 :
430 14364 : char *CPCIDSKVectorSegment::GetData( int section, uint32 offset,
431 : int *bytes_available, int min_bytes,
432 : bool update )
433 :
434 : {
435 14364 : if( min_bytes == 0 )
436 0 : min_bytes = 1;
437 :
438 : /* -------------------------------------------------------------------- */
439 : /* Select the section to act on. */
440 : /* -------------------------------------------------------------------- */
441 14364 : PCIDSKBuffer *pbuf = nullptr;
442 14364 : uint32 *pbuf_offset = nullptr;
443 14364 : bool *pbuf_dirty = nullptr;
444 :
445 14364 : if( section == sec_raw )
446 : {
447 13763 : pbuf = &raw_loaded_data;
448 13763 : pbuf_offset = &raw_loaded_data_offset;
449 13763 : pbuf_dirty = &raw_loaded_data_dirty;
450 : }
451 601 : else if( section == sec_vert )
452 : {
453 368 : pbuf = &vert_loaded_data;
454 368 : pbuf_offset = &vert_loaded_data_offset;
455 368 : pbuf_dirty = &vert_loaded_data_dirty;
456 : }
457 233 : else if( section == sec_record )
458 : {
459 233 : pbuf = &record_loaded_data;
460 233 : pbuf_offset = &record_loaded_data_offset;
461 233 : pbuf_dirty = &record_loaded_data_dirty;
462 : }
463 : else
464 : {
465 0 : return (char*)ThrowPCIDSKExceptionPtr("Unexpected case");
466 : }
467 :
468 14364 : if( offset > std::numeric_limits<uint32>::max() - static_cast<uint32>(min_bytes) )
469 0 : return (char*)ThrowPCIDSKExceptionPtr("Invalid offset : %u", offset);
470 :
471 : /* -------------------------------------------------------------------- */
472 : /* If the desired data is not within our loaded section, reload */
473 : /* one or more blocks around the request. */
474 : /* -------------------------------------------------------------------- */
475 14364 : if( offset < *pbuf_offset
476 14364 : || offset+static_cast<uint32>(min_bytes) > *pbuf_offset + static_cast<uint32>(pbuf->buffer_size) )
477 : {
478 1277 : if( *pbuf_dirty )
479 0 : FlushDataBuffer( section );
480 :
481 : // we want whole 8K blocks around the target region.
482 1277 : uint32 load_offset = offset - (offset % block_page_size);
483 1277 : int size = (offset + static_cast<uint32>(min_bytes) - load_offset + block_page_size - 1);
484 :
485 1277 : size -= (size % block_page_size);
486 :
487 : // If the request goes beyond the end of the file, and we are
488 : // in update mode, grow the segment by writing at the end of
489 : // the requested section. This will throw an exception if we
490 : // are unable to grow the file.
491 1277 : if( section != sec_raw
492 94 : && load_offset + size > di[section].GetIndex()->size() * block_page_size
493 1371 : && update )
494 : {
495 160 : PCIDSKBuffer zerobuf(block_page_size);
496 :
497 80 : memset( zerobuf.buffer, 0, block_page_size );
498 80 : WriteSecToFile( section, zerobuf.buffer,
499 80 : (load_offset + size) / block_page_size - 1, 1 );
500 : }
501 :
502 1277 : *pbuf_offset = load_offset;
503 1277 : pbuf->SetSize( size );
504 :
505 1277 : ReadSecFromFile( section, pbuf->buffer,
506 1277 : load_offset / block_page_size, size / block_page_size );
507 : }
508 :
509 : /* -------------------------------------------------------------------- */
510 : /* If an update request goes beyond the end of the last data */
511 : /* byte in a data section, then update the bytes used. Now */
512 : /* read into our buffer. */
513 : /* -------------------------------------------------------------------- */
514 14364 : if( section != sec_raw
515 14364 : && offset + min_bytes > di[section].GetSectionEnd() )
516 182 : di[section].SetSectionEnd( offset + min_bytes );
517 :
518 : /* -------------------------------------------------------------------- */
519 : /* Return desired info. */
520 : /* -------------------------------------------------------------------- */
521 14364 : if( bytes_available != nullptr )
522 2981 : *bytes_available = *pbuf_offset + pbuf->buffer_size - offset;
523 :
524 14364 : if( update )
525 186 : *pbuf_dirty = true;
526 :
527 14364 : return pbuf->buffer + offset - *pbuf_offset;
528 : }
529 :
530 : /************************************************************************/
531 : /* ReadSecFromFile() */
532 : /* */
533 : /* Read one or more blocks from the desired "section" of the */
534 : /* segment data, going through the block pointer map for */
535 : /* vect/record sections. */
536 : /************************************************************************/
537 :
538 1277 : void CPCIDSKVectorSegment::ReadSecFromFile( int section, char *buffer,
539 : int block_offset,
540 : int block_count )
541 :
542 : {
543 : /* -------------------------------------------------------------------- */
544 : /* Raw is a simple case, directly gulp. */
545 : /* -------------------------------------------------------------------- */
546 1277 : if( section == sec_raw )
547 : {
548 1183 : ReadFromFile( buffer, static_cast<uint64>(block_offset)*static_cast<uint32>(block_page_size),
549 1183 : block_count*block_page_size );
550 1183 : return;
551 : }
552 :
553 : /* -------------------------------------------------------------------- */
554 : /* Process one 8K block at a time in case they are discontiguous */
555 : /* which they often are. */
556 : /* -------------------------------------------------------------------- */
557 : int i;
558 94 : const std::vector<uint32> *block_map = di[section].GetIndex();
559 :
560 94 : if( block_count + block_offset > (int) block_map->size() )
561 : {
562 0 : return ThrowPCIDSKException("Assertion failed: block_count(=%d) + block_offset(=%d) <= block_map->size()(=%d)",
563 0 : block_count, block_offset, (int) block_map->size() );
564 : }
565 :
566 188 : for( i = 0; i < block_count; i++ )
567 : {
568 188 : ReadFromFile( buffer + i * block_page_size,
569 94 : block_page_size * static_cast<uint64>((*block_map)[block_offset+i]),
570 94 : block_page_size );
571 : }
572 : }
573 :
574 : /************************************************************************/
575 : /* FlushDataBuffer() */
576 : /* */
577 : /* Flush the indicated data buffer to disk if it is marked */
578 : /* dirty. */
579 : /************************************************************************/
580 :
581 6742 : void CPCIDSKVectorSegment::FlushDataBuffer( int section )
582 :
583 : {
584 : /* -------------------------------------------------------------------- */
585 : /* Select the section to act on. */
586 : /* -------------------------------------------------------------------- */
587 6742 : PCIDSKBuffer *pbuf = nullptr;
588 6742 : uint32 *pbuf_offset = nullptr;
589 6742 : bool *pbuf_dirty = nullptr;
590 :
591 6742 : if( section == sec_raw )
592 : {
593 0 : pbuf = &raw_loaded_data;
594 0 : pbuf_offset = &raw_loaded_data_offset;
595 0 : pbuf_dirty = &raw_loaded_data_dirty;
596 : }
597 6742 : else if( section == sec_vert )
598 : {
599 3371 : pbuf = &vert_loaded_data;
600 3371 : pbuf_offset = &vert_loaded_data_offset;
601 3371 : pbuf_dirty = &vert_loaded_data_dirty;
602 : }
603 3371 : else if( section == sec_record )
604 : {
605 3371 : pbuf = &record_loaded_data;
606 3371 : pbuf_offset = &record_loaded_data_offset;
607 3371 : pbuf_dirty = &record_loaded_data_dirty;
608 : }
609 : else
610 : {
611 0 : return ThrowPCIDSKException("Unexpected case");
612 : }
613 :
614 6742 : if( ! *pbuf_dirty || pbuf->buffer_size == 0 )
615 6662 : return;
616 :
617 : /* -------------------------------------------------------------------- */
618 : /* We need to write something. */
619 : /* -------------------------------------------------------------------- */
620 80 : assert( (pbuf->buffer_size % block_page_size) == 0 );
621 80 : assert( (*pbuf_offset % block_page_size) == 0 );
622 :
623 80 : WriteSecToFile( section, pbuf->buffer,
624 80 : *pbuf_offset / block_page_size,
625 80 : pbuf->buffer_size / block_page_size );
626 :
627 80 : *pbuf_dirty = false;
628 : }
629 :
630 : /************************************************************************/
631 : /* WriteSecToFile() */
632 : /* */
633 : /* Read one or more blocks from the desired "section" of the */
634 : /* segment data, going through the block pointer map for */
635 : /* vect/record sections. */
636 : /************************************************************************/
637 :
638 160 : void CPCIDSKVectorSegment::WriteSecToFile( int section, char *buffer,
639 : int block_offset,
640 : int block_count )
641 :
642 : {
643 : /* -------------------------------------------------------------------- */
644 : /* Raw is a simple case, directly gulp. */
645 : /* -------------------------------------------------------------------- */
646 160 : if( section == sec_raw )
647 : {
648 0 : WriteToFile( buffer, block_offset*block_page_size,
649 0 : block_count*block_page_size );
650 0 : return;
651 : }
652 :
653 : /* -------------------------------------------------------------------- */
654 : /* Do we need to grow this data section to be able to do the */
655 : /* write? */
656 : /* -------------------------------------------------------------------- */
657 160 : const std::vector<uint32> *block_map = di[section].GetIndex();
658 :
659 160 : if( block_count + block_offset > (int) block_map->size() )
660 : {
661 80 : vh.GrowBlockIndex( section,
662 80 : block_count + block_offset - static_cast<int>(block_map->size()) );
663 : }
664 :
665 : /* -------------------------------------------------------------------- */
666 : /* Process one 8K block at a time in case they are discontiguous */
667 : /* which they often are. */
668 : /* -------------------------------------------------------------------- */
669 : int i;
670 320 : for( i = 0; i < block_count; i++ )
671 : {
672 320 : WriteToFile( buffer + i * block_page_size,
673 160 : block_page_size * (*block_map)[block_offset+i],
674 160 : block_page_size );
675 : }
676 : }
677 :
678 : /************************************************************************/
679 : /* GetProjection() */
680 : /************************************************************************/
681 :
682 1183 : std::vector<double> CPCIDSKVectorSegment::GetProjection( std::string &geosys )
683 :
684 : {
685 1183 : LoadHeader();
686 :
687 : /* -------------------------------------------------------------------- */
688 : /* Fetch the projparms string from the proj section of the */
689 : /* vector segment header. */
690 : /* -------------------------------------------------------------------- */
691 1183 : ShapeField projparms;
692 :
693 1183 : ReadField( vh.section_offsets[hsec_proj]+32, projparms,
694 : FieldTypeString, sec_raw );
695 :
696 : /* -------------------------------------------------------------------- */
697 : /* Read the geosys (units) string from SDH5.VEC1 in the segment */
698 : /* header. */
699 : /* -------------------------------------------------------------------- */
700 1183 : GetHeader().Get( 160, 16, geosys, 0 ); // do not unpad!
701 :
702 2366 : return ProjParamsFromText( geosys, projparms.GetValueString() );
703 : }
704 :
705 : /************************************************************************/
706 : /* SetProjection() */
707 : /************************************************************************/
708 :
709 6 : void CPCIDSKVectorSegment::SetProjection( const std::string& geosys,
710 : const std::vector<double>& params )
711 :
712 : {
713 6 : LoadHeader();
714 :
715 : /* -------------------------------------------------------------------- */
716 : /* Apply parameters in the vector segment "proj" header section. */
717 : /* -------------------------------------------------------------------- */
718 12 : PCIDSKBuffer proj(32);
719 : uint32 proj_size;
720 12 : ShapeField value;
721 :
722 6 : value.SetValue( ProjParamsToText( params ) );
723 :
724 6 : ReadFromFile( proj.buffer, vh.section_offsets[hsec_proj], 32 );
725 6 : proj_size = WriteField( 32, value, proj );
726 :
727 6 : vh.GrowSection( hsec_proj, proj_size );
728 6 : WriteToFile( proj.buffer, vh.section_offsets[hsec_proj], proj_size );
729 :
730 : /* -------------------------------------------------------------------- */
731 : /* Write the geosys string to the generic segment header. */
732 : /* -------------------------------------------------------------------- */
733 6 : GetHeader().Put( geosys.c_str(), 160, 16 );
734 6 : FlushHeader();
735 6 : }
736 :
737 : /************************************************************************/
738 : /* IndexFromShapeId() */
739 : /* */
740 : /* Translate a shapeid into a shape index. Several mechanisms */
741 : /* are used to accelerate this when possible. */
742 : /************************************************************************/
743 :
744 747 : int CPCIDSKVectorSegment::IndexFromShapeId( ShapeId id )
745 :
746 : {
747 747 : if( id == NullShapeId )
748 0 : return -1;
749 :
750 747 : LoadHeader();
751 :
752 : /* -------------------------------------------------------------------- */
753 : /* Does this match our last lookup? */
754 : /* -------------------------------------------------------------------- */
755 747 : if( id == last_shapes_id )
756 621 : return last_shapes_index;
757 :
758 : /* -------------------------------------------------------------------- */
759 : /* Is this the next shapeid in sequence, and is it in our */
760 : /* loaded index cache? */
761 : /* -------------------------------------------------------------------- */
762 252 : if( id == last_shapes_id + 1
763 96 : && last_shapes_index + 1 >= shape_index_start
764 222 : && last_shapes_index + 1 < shape_index_start + (int) shape_index_ids.size() )
765 : {
766 96 : last_shapes_index++;
767 96 : last_shapes_id++;
768 96 : return last_shapes_index;
769 : }
770 :
771 : /* -------------------------------------------------------------------- */
772 : /* Activate the shapeid map, if it is not already active. */
773 : /* -------------------------------------------------------------------- */
774 30 : if( !shapeid_map_active )
775 : {
776 0 : PopulateShapeIdMap();
777 : }
778 :
779 : /* -------------------------------------------------------------------- */
780 : /* Is this already in our shapeid map? */
781 : /* -------------------------------------------------------------------- */
782 30 : if( shapeid_map.count( id ) == 1 )
783 0 : return shapeid_map[id];
784 :
785 30 : return -1;
786 : }
787 :
788 : /************************************************************************/
789 : /* LoadShapeIdPage() */
790 : /************************************************************************/
791 :
792 64 : void CPCIDSKVectorSegment::LoadShapeIdPage( int page )
793 :
794 : {
795 : /* -------------------------------------------------------------------- */
796 : /* Load a chunk of shape index information into a */
797 : /* PCIDSKBuffer. */
798 : /* -------------------------------------------------------------------- */
799 64 : uint32 shape_index_byte_offset =
800 64 : vh.section_offsets[hsec_shape]
801 64 : + di[sec_record].offset_on_disk_within_section
802 64 : + di[sec_record].size_on_disk + 4;
803 :
804 64 : int entries_to_load = shapeid_page_size;
805 :
806 64 : shape_index_start = page * shapeid_page_size;
807 64 : if( shape_index_start + entries_to_load > total_shape_count )
808 64 : entries_to_load = total_shape_count - shape_index_start;
809 :
810 64 : PCIDSKBuffer wrk_index;
811 64 : if( entries_to_load < 0 || entries_to_load > std::numeric_limits<int>::max() / 12 )
812 0 : return ThrowPCIDSKException("Invalid entries_to_load = %d", entries_to_load);
813 64 : wrk_index.SetSize( entries_to_load * 12 );
814 :
815 64 : ReadFromFile( wrk_index.buffer,
816 64 : shape_index_byte_offset + static_cast<uint64>(shape_index_start)*12,
817 64 : wrk_index.buffer_size );
818 :
819 : /* -------------------------------------------------------------------- */
820 : /* Parse into the vectors for easier use. */
821 : /* -------------------------------------------------------------------- */
822 : int i;
823 :
824 64 : shape_index_ids.resize( entries_to_load );
825 64 : shape_index_vertex_off.resize( entries_to_load );
826 64 : shape_index_record_off.resize( entries_to_load );
827 :
828 197 : for( i = 0; i < entries_to_load; i++ )
829 : {
830 133 : memcpy( &(shape_index_ids[i]), wrk_index.buffer + i*12, 4 );
831 133 : memcpy( &(shape_index_vertex_off[i]), wrk_index.buffer + i*12+4, 4 );
832 133 : memcpy( &(shape_index_record_off[i]), wrk_index.buffer + i*12+8, 4 );
833 : }
834 :
835 64 : if( needs_swap && entries_to_load > 0 )
836 : {
837 64 : SwapData( &(shape_index_ids[0]), 4, entries_to_load );
838 64 : SwapData( &(shape_index_vertex_off[0]), 4, entries_to_load );
839 64 : SwapData( &(shape_index_record_off[0]), 4, entries_to_load );
840 : }
841 :
842 64 : PushLoadedIndexIntoMap();
843 : }
844 :
845 : /************************************************************************/
846 : /* AccessShapeByIndex() */
847 : /* */
848 : /* This method is responsible for loading the set of */
849 : /* information for shape "shape_index" into the shape_index data */
850 : /* structures if it is not already there. */
851 : /************************************************************************/
852 :
853 890 : void CPCIDSKVectorSegment::AccessShapeByIndex( int shape_index )
854 :
855 : {
856 890 : LoadHeader();
857 :
858 : /* -------------------------------------------------------------------- */
859 : /* Is the requested index already loaded? */
860 : /* -------------------------------------------------------------------- */
861 1780 : if( shape_index >= shape_index_start
862 890 : && shape_index < shape_index_start + (int) shape_index_ids.size() )
863 730 : return;
864 :
865 : // this is for requesting the next shapeindex after shapecount on
866 : // a partial page.
867 320 : if( shape_index == total_shape_count
868 96 : && (int) shape_index_ids.size() < shapeid_page_size
869 256 : && total_shape_count == (int) shape_index_ids.size() + shape_index_start )
870 96 : return;
871 :
872 : /* -------------------------------------------------------------------- */
873 : /* If the currently loaded shapeindex is dirty, we should write */
874 : /* it now. */
875 : /* -------------------------------------------------------------------- */
876 64 : FlushLoadedShapeIndex();
877 :
878 : /* -------------------------------------------------------------------- */
879 : /* Load the page of shapeid information for this shape index. */
880 : /* -------------------------------------------------------------------- */
881 64 : LoadShapeIdPage( shape_index / shapeid_page_size );
882 : }
883 :
884 : /************************************************************************/
885 : /* PushLoadedIndexIntoMap() */
886 : /************************************************************************/
887 :
888 70 : void CPCIDSKVectorSegment::PushLoadedIndexIntoMap()
889 :
890 : {
891 : /* -------------------------------------------------------------------- */
892 : /* If the shapeid map is active, apply the current pages */
893 : /* shapeids if it does not already appear to have been */
894 : /* applied. */
895 : /* -------------------------------------------------------------------- */
896 70 : int loaded_page = shape_index_start / shapeid_page_size;
897 :
898 70 : if( shapeid_map_active && !shape_index_ids.empty() )
899 : {
900 : unsigned int i;
901 :
902 12 : for( i = 0; i < shape_index_ids.size(); i++ )
903 : {
904 6 : if( shape_index_ids[i] != NullShapeId )
905 6 : shapeid_map[shape_index_ids[i]] = i+shape_index_start;
906 : }
907 :
908 6 : if( loaded_page == shapeid_pages_certainly_mapped+1 )
909 6 : shapeid_pages_certainly_mapped++;
910 : }
911 70 : }
912 :
913 : /************************************************************************/
914 : /* PopulateShapeIdMap() */
915 : /* */
916 : /* Completely populate the shapeid->index map. */
917 : /************************************************************************/
918 :
919 6 : void CPCIDSKVectorSegment::PopulateShapeIdMap()
920 :
921 : {
922 : /* -------------------------------------------------------------------- */
923 : /* Enable shapeid_map mode, and load the current page. */
924 : /* -------------------------------------------------------------------- */
925 6 : if( !shapeid_map_active )
926 : {
927 6 : shapeid_map_active = true;
928 6 : PushLoadedIndexIntoMap();
929 : }
930 :
931 : /* -------------------------------------------------------------------- */
932 : /* Load all outstanding pages. */
933 : /* -------------------------------------------------------------------- */
934 6 : int shapeid_pages = (total_shape_count+shapeid_page_size-1) / shapeid_page_size;
935 :
936 6 : while( shapeid_pages_certainly_mapped+1 < shapeid_pages )
937 : {
938 0 : LoadShapeIdPage( shapeid_pages_certainly_mapped+1 );
939 : }
940 6 : }
941 :
942 : /************************************************************************/
943 : /* FindNextValidByIndex() */
944 : /************************************************************************/
945 : /**
946 : * Find the next shape and the given shape index in the segment
947 : * (including deleted shapes), if the shape at nIndex is NullShapeId then
948 : * return the nexrt valid shape ID
949 : *
950 : * @param nIndex the index into
951 : */
952 1593 : ShapeId CPCIDSKVectorSegment::FindNextValidByIndex(int nIndex)
953 : {
954 1593 : LoadHeader();
955 :
956 1593 : if (total_shape_count == 0 || nIndex >= total_shape_count)
957 1317 : return NullShapeId;
958 :
959 :
960 276 : for (int nShapeIndex = nIndex; nShapeIndex < total_shape_count; ++nShapeIndex)
961 : {
962 : // set up shape_index_ids array
963 276 : AccessShapeByIndex(nShapeIndex);
964 :
965 276 : int32 nNextShapeId = shape_index_ids[nShapeIndex - shape_index_start];
966 276 : if (nNextShapeId != NullShapeId)
967 : {
968 276 : last_shapes_id = nNextShapeId;
969 276 : last_shapes_index = nShapeIndex;
970 276 : return last_shapes_id;
971 : }
972 : }
973 :
974 0 : return NullShapeId;
975 : }
976 :
977 : /************************************************************************/
978 : /* FindFirst() */
979 : /************************************************************************/
980 :
981 1380 : ShapeId CPCIDSKVectorSegment::FindFirst()
982 : {
983 1380 : return FindNextValidByIndex(0);
984 : }
985 :
986 : /************************************************************************/
987 : /* FindNext() */
988 : /************************************************************************/
989 :
990 213 : ShapeId CPCIDSKVectorSegment::FindNext( ShapeId previous_id )
991 : {
992 213 : if( previous_id == NullShapeId )
993 0 : return FindFirst();
994 :
995 213 : int previous_index = IndexFromShapeId( previous_id );
996 :
997 213 : return FindNextValidByIndex(previous_index+1);
998 : }
999 :
1000 : /************************************************************************/
1001 : /* GetShapeCount() */
1002 : /************************************************************************/
1003 :
1004 84 : int CPCIDSKVectorSegment::GetShapeCount()
1005 :
1006 : {
1007 84 : LoadHeader();
1008 :
1009 84 : return valid_shape_count;
1010 : }
1011 :
1012 : /************************************************************************/
1013 : /* GetVertices() */
1014 : /************************************************************************/
1015 :
1016 160 : void CPCIDSKVectorSegment::GetVertices( ShapeId shape_id,
1017 : std::vector<ShapeVertex> &vertices )
1018 :
1019 : {
1020 160 : int shape_index = IndexFromShapeId( shape_id );
1021 :
1022 160 : if( shape_index == -1 )
1023 0 : return ThrowPCIDSKException( "Attempt to call GetVertices() on non-existing shape id '%d'.",
1024 0 : (int) shape_id );
1025 :
1026 160 : AccessShapeByIndex( shape_index );
1027 :
1028 160 : uint32 vert_off = shape_index_vertex_off[shape_index - shape_index_start];
1029 : uint32 vertex_count;
1030 :
1031 160 : if( vert_off == 0xffffffff )
1032 : {
1033 0 : vertices.resize(0);
1034 0 : return;
1035 : }
1036 :
1037 160 : if( vert_off > std::numeric_limits<uint32>::max() - 4 )
1038 0 : return ThrowPCIDSKException( "Invalid vert_off = %u", vert_off);
1039 160 : memcpy( &vertex_count, GetData( sec_vert, vert_off+4, nullptr, 4 ), 4 );
1040 160 : if( needs_swap )
1041 160 : SwapData( &vertex_count, 4, 1 );
1042 :
1043 : try
1044 : {
1045 160 : vertices.resize( vertex_count );
1046 : }
1047 0 : catch( const std::exception& ex )
1048 : {
1049 0 : return ThrowPCIDSKException("Out of memory allocating vertices(%u): %s",
1050 0 : vertex_count, ex.what());
1051 : }
1052 :
1053 : // We ought to change this to process the available data and
1054 : // then request more.
1055 160 : if( vertex_count > 0 )
1056 : {
1057 108 : if( vert_off > std::numeric_limits<uint32>::max() - 8 )
1058 0 : return ThrowPCIDSKException( "Invalid vert_off = %u", vert_off);
1059 324 : memcpy( &(vertices[0]),
1060 108 : GetData( sec_vert, vert_off+8, nullptr, vertex_count*24),
1061 108 : vertex_count * 24 );
1062 108 : if( needs_swap )
1063 108 : SwapData( &(vertices[0]), 8, vertex_count*3 );
1064 : }
1065 : }
1066 :
1067 : /************************************************************************/
1068 : /* GetFieldCount() */
1069 : /************************************************************************/
1070 :
1071 1983 : int CPCIDSKVectorSegment::GetFieldCount()
1072 :
1073 : {
1074 1983 : LoadHeader();
1075 :
1076 1983 : return static_cast<int>(vh.field_names.size());
1077 : }
1078 :
1079 : /************************************************************************/
1080 : /* GetFieldName() */
1081 : /************************************************************************/
1082 :
1083 506 : std::string CPCIDSKVectorSegment::GetFieldName( int field_index )
1084 :
1085 : {
1086 506 : LoadHeader();
1087 :
1088 506 : return vh.field_names[field_index];
1089 : }
1090 :
1091 : /************************************************************************/
1092 : /* GetFieldDescription() */
1093 : /************************************************************************/
1094 :
1095 0 : std::string CPCIDSKVectorSegment::GetFieldDescription( int field_index )
1096 :
1097 : {
1098 0 : LoadHeader();
1099 :
1100 0 : return vh.field_descriptions[field_index];
1101 : }
1102 :
1103 : /************************************************************************/
1104 : /* GetFieldType() */
1105 : /************************************************************************/
1106 :
1107 506 : ShapeFieldType CPCIDSKVectorSegment::GetFieldType( int field_index )
1108 :
1109 : {
1110 506 : LoadHeader();
1111 :
1112 506 : return vh.field_types[field_index];
1113 : }
1114 :
1115 : /************************************************************************/
1116 : /* GetFieldFormat() */
1117 : /************************************************************************/
1118 :
1119 0 : std::string CPCIDSKVectorSegment::GetFieldFormat( int field_index )
1120 :
1121 : {
1122 0 : LoadHeader();
1123 :
1124 0 : return vh.field_formats[field_index];
1125 : }
1126 :
1127 : /************************************************************************/
1128 : /* GetFieldDefault() */
1129 : /************************************************************************/
1130 :
1131 0 : ShapeField CPCIDSKVectorSegment::GetFieldDefault( int field_index )
1132 :
1133 : {
1134 0 : LoadHeader();
1135 :
1136 0 : return vh.field_defaults[field_index];
1137 : }
1138 :
1139 : /************************************************************************/
1140 : /* GetFields() */
1141 : /************************************************************************/
1142 :
1143 168 : void CPCIDSKVectorSegment::GetFields( ShapeId id,
1144 : std::vector<ShapeField>& list )
1145 :
1146 : {
1147 : unsigned int i;
1148 168 : int shape_index = IndexFromShapeId( id );
1149 :
1150 168 : if( shape_index == -1 )
1151 18 : return ThrowPCIDSKException( "Attempt to call GetFields() on non-existing shape id '%d'.",
1152 0 : (int) id );
1153 :
1154 150 : AccessShapeByIndex( shape_index );
1155 :
1156 150 : uint32 offset = shape_index_record_off[shape_index - shape_index_start];
1157 :
1158 150 : list.resize(vh.field_names.size());
1159 :
1160 150 : if( offset == 0xffffffff )
1161 : {
1162 113 : for( i = 0; i < vh.field_names.size(); i++ )
1163 0 : list[i] = vh.field_defaults[i];
1164 : }
1165 : else
1166 : {
1167 37 : offset += 4; // skip size
1168 :
1169 180 : for( i = 0; i < vh.field_names.size(); i++ )
1170 143 : offset = ReadField( offset, list[i], vh.field_types[i], sec_record );
1171 : }
1172 : }
1173 :
1174 : /************************************************************************/
1175 : /* AddField() */
1176 : /************************************************************************/
1177 :
1178 104 : void CPCIDSKVectorSegment::AddField( const std::string& name, ShapeFieldType type,
1179 : const std::string& description,
1180 : const std::string& format,
1181 : ShapeField *default_value )
1182 :
1183 : {
1184 105 : ShapeField fallback_default;
1185 :
1186 104 : LoadHeader();
1187 :
1188 : /* -------------------------------------------------------------------- */
1189 : /* If we have existing features, we should go through adding */
1190 : /* this new field. */
1191 : /* -------------------------------------------------------------------- */
1192 104 : if( total_shape_count > 0 )
1193 : {
1194 1 : return ThrowPCIDSKException( "Support for adding fields in populated layers "
1195 0 : "has not yet been implemented." );
1196 : }
1197 :
1198 : /* -------------------------------------------------------------------- */
1199 : /* If no default is provided, use the obvious value. */
1200 : /* -------------------------------------------------------------------- */
1201 103 : if( default_value == nullptr )
1202 : {
1203 103 : switch( type )
1204 : {
1205 0 : case FieldTypeFloat:
1206 0 : fallback_default.SetValue( (float) 0.0 );
1207 0 : break;
1208 18 : case FieldTypeDouble:
1209 18 : fallback_default.SetValue( (double) 0.0 );
1210 18 : break;
1211 18 : case FieldTypeInteger:
1212 18 : fallback_default.SetValue( (int32) 0 );
1213 18 : break;
1214 0 : case FieldTypeCountedInt:
1215 : {
1216 0 : std::vector<int32> empty_list;
1217 0 : fallback_default.SetValue( empty_list );
1218 0 : break;
1219 : }
1220 67 : case FieldTypeString:
1221 67 : fallback_default.SetValue( "" );
1222 67 : break;
1223 :
1224 0 : case FieldTypeNone:
1225 0 : break;
1226 : }
1227 :
1228 103 : default_value = &fallback_default;
1229 : }
1230 :
1231 : /* -------------------------------------------------------------------- */
1232 : /* Make sure the default field is of the correct type. */
1233 : /* -------------------------------------------------------------------- */
1234 103 : if( default_value->GetType() != type )
1235 : {
1236 0 : return ThrowPCIDSKException( "Attempt to add field with a default value of "
1237 0 : "a different type than the field." );
1238 : }
1239 :
1240 103 : if( type == FieldTypeNone )
1241 : {
1242 0 : return ThrowPCIDSKException( "Creating fields of type None not supported." );
1243 : }
1244 :
1245 : /* -------------------------------------------------------------------- */
1246 : /* Add the field to the definition list. */
1247 : /* -------------------------------------------------------------------- */
1248 103 : vh.field_names.push_back( name );
1249 103 : vh.field_types.push_back( type );
1250 103 : vh.field_descriptions.push_back( description );
1251 103 : vh.field_formats.push_back( format );
1252 103 : vh.field_defaults.push_back( *default_value );
1253 :
1254 103 : vh_dirty = true;
1255 : }
1256 :
1257 : /************************************************************************/
1258 : /* FlushSegHeaderIfNeeded() */
1259 : /************************************************************************/
1260 :
1261 3679 : void CPCIDSKVectorSegment::FlushSegHeaderIfNeeded()
1262 : {
1263 3679 : if( vh_dirty )
1264 : {
1265 35 : vh.WriteFieldDefinitions();
1266 35 : vh_dirty = false;
1267 : }
1268 3679 : }
1269 :
1270 : /************************************************************************/
1271 : /* CreateShape() */
1272 : /************************************************************************/
1273 :
1274 102 : ShapeId CPCIDSKVectorSegment::CreateShape( ShapeId id )
1275 :
1276 : {
1277 102 : LoadHeader();
1278 102 : FlushSegHeaderIfNeeded();
1279 :
1280 : /* -------------------------------------------------------------------- */
1281 : /* Make sure we have the last shapeid index page loaded. */
1282 : /* -------------------------------------------------------------------- */
1283 102 : AccessShapeByIndex( total_shape_count );
1284 :
1285 : // if highest_shapeid_used is unset, then look at all Ids
1286 102 : if (highest_shapeid_used == NullShapeId &&!shape_index_ids.empty())
1287 : {
1288 6 : auto it = std::max_element(shape_index_ids.begin(), shape_index_ids.end());
1289 6 : highest_shapeid_used = *it;
1290 : }
1291 :
1292 : /* -------------------------------------------------------------------- */
1293 : /* Do we need to assign a shapeid? */
1294 : /* -------------------------------------------------------------------- */
1295 102 : if( id == NullShapeId )
1296 : {
1297 96 : if( highest_shapeid_used == NullShapeId )
1298 45 : id = 0;
1299 : else
1300 51 : id = highest_shapeid_used + 1;
1301 : }
1302 102 : if( id > highest_shapeid_used )
1303 96 : highest_shapeid_used = id;
1304 : else
1305 : {
1306 6 : PopulateShapeIdMap();
1307 6 : if( shapeid_map.count(id) > 0 )
1308 : {
1309 6 : return ThrowPCIDSKException( 0, "Attempt to create a shape with id '%d', but that already exists.", id );
1310 : }
1311 : }
1312 :
1313 : /* -------------------------------------------------------------------- */
1314 : /* Push this new shape on to our list of shapeids in the */
1315 : /* current page, and mark the page as dirty. */
1316 : /* -------------------------------------------------------------------- */
1317 96 : shape_index_ids.push_back( id );
1318 96 : shape_index_record_off.push_back( 0xffffffff );
1319 96 : shape_index_vertex_off.push_back( 0xffffffff );
1320 96 : shape_index_page_dirty = true;
1321 :
1322 96 : if( shapeid_map_active )
1323 0 : shapeid_map[id] = total_shape_count;
1324 :
1325 96 : total_shape_count++;
1326 96 : valid_shape_count++;
1327 :
1328 96 : return id;
1329 : }
1330 :
1331 : /************************************************************************/
1332 : /* DeleteShape() */
1333 : /* */
1334 : /* Delete a shape by shapeid. */
1335 : /************************************************************************/
1336 :
1337 20 : void CPCIDSKVectorSegment::DeleteShape( ShapeId id )
1338 :
1339 : {
1340 20 : FlushSegHeaderIfNeeded();
1341 20 : int shape_index = IndexFromShapeId( id );
1342 :
1343 20 : if( shape_index == -1 )
1344 12 : return ThrowPCIDSKException( "Attempt to call DeleteShape() on non-existing shape '%d'.",
1345 0 : (int) id );
1346 :
1347 : /* ==================================================================== */
1348 : /* Our strategy is to move the last shape in our index down to */
1349 : /* replace the shape that we are deleting. Unfortunately this */
1350 : /* will result in an out of sequence shapeid, but it is hard to */
1351 : /* avoid that without potentially rewriting much of the shape */
1352 : /* index. */
1353 : /* */
1354 : /* Note that the following sequence *does* work for special */
1355 : /* cases like deleting the last shape in the list, or deleting */
1356 : /* a shape on the same page as the last shape. At worst a wee */
1357 : /* bit of extra work is done. */
1358 : /* ==================================================================== */
1359 :
1360 : /* -------------------------------------------------------------------- */
1361 : /* Load the page of shapeids containing the last shape in our */
1362 : /* index, capture the last shape's details, and remove it. */
1363 : /* -------------------------------------------------------------------- */
1364 :
1365 : uint32 vert_off, rec_off;
1366 : ShapeId last_id;
1367 :
1368 8 : AccessShapeByIndex( total_shape_count-1 );
1369 :
1370 8 : last_id = shape_index_ids[total_shape_count-1-shape_index_start];
1371 8 : vert_off = shape_index_vertex_off[total_shape_count-1-shape_index_start];
1372 8 : rec_off = shape_index_record_off[total_shape_count-1-shape_index_start];
1373 :
1374 : // We don't actually have to modify this area of the index on disk.
1375 : // Some of the stuff at the end just becomes unreferenced when we
1376 : // decrement total_shape_count.
1377 :
1378 : /* -------------------------------------------------------------------- */
1379 : /* Load the page with the shape we are deleting, and put last */
1380 : /* the shapes information over it. */
1381 : /* -------------------------------------------------------------------- */
1382 8 : AccessShapeByIndex( shape_index );
1383 :
1384 8 : shape_index_ids[shape_index-shape_index_start] = last_id;
1385 8 : shape_index_vertex_off[shape_index-shape_index_start] = vert_off;
1386 8 : shape_index_record_off[shape_index-shape_index_start] = rec_off;
1387 :
1388 8 : shape_index_page_dirty = true;
1389 :
1390 8 : if( shapeid_map_active )
1391 0 : shapeid_map.erase( id );
1392 :
1393 : // if the highest shape_id is the one that was deleted,
1394 : // then reset highest_shapeid_used
1395 8 : if (id == highest_shapeid_used)
1396 2 : highest_shapeid_used = NullShapeId;
1397 8 : total_shape_count--;
1398 8 : valid_shape_count--;
1399 : }
1400 :
1401 : /************************************************************************/
1402 : /* SetVertices() */
1403 : /************************************************************************/
1404 :
1405 98 : void CPCIDSKVectorSegment::SetVertices( ShapeId id,
1406 : const std::vector<ShapeVertex>& list )
1407 :
1408 : {
1409 98 : FlushSegHeaderIfNeeded();
1410 98 : int shape_index = IndexFromShapeId( id );
1411 :
1412 98 : if( shape_index == -1 )
1413 0 : return ThrowPCIDSKException( "Attempt to call SetVertices() on non-existing shape '%d'.",
1414 0 : (int) id );
1415 :
1416 196 : PCIDSKBuffer vbuf( static_cast<int>(list.size()) * 24 + 8 );
1417 :
1418 98 : AccessShapeByIndex( shape_index );
1419 :
1420 : /* -------------------------------------------------------------------- */
1421 : /* Is the current space big enough to hold the new vertex set? */
1422 : /* -------------------------------------------------------------------- */
1423 98 : uint32 vert_off = shape_index_vertex_off[shape_index - shape_index_start];
1424 98 : uint32 chunk_size = 0;
1425 :
1426 98 : if( vert_off != 0xffffffff )
1427 : {
1428 2 : memcpy( &chunk_size, GetData( sec_vert, vert_off, nullptr, 4 ), 4 );
1429 2 : if( needs_swap )
1430 2 : SwapData( &chunk_size, 4, 1 );
1431 :
1432 2 : if( chunk_size < (uint32) vbuf.buffer_size )
1433 : {
1434 0 : vert_off = 0xffffffff;
1435 : }
1436 : }
1437 :
1438 : /* -------------------------------------------------------------------- */
1439 : /* Do we need to put this at the end of the section? */
1440 : /* -------------------------------------------------------------------- */
1441 98 : if( vert_off == 0xffffffff )
1442 : {
1443 96 : vert_off = di[sec_vert].GetSectionEnd();
1444 96 : chunk_size = vbuf.buffer_size;
1445 : }
1446 :
1447 : /* -------------------------------------------------------------------- */
1448 : /* Format the vertices in a buffer. */
1449 : /* -------------------------------------------------------------------- */
1450 98 : uint32 vert_count = static_cast<uint32>(list.size());
1451 : unsigned int i;
1452 :
1453 98 : memcpy( vbuf.buffer, &chunk_size, 4 );
1454 98 : memcpy( vbuf.buffer+4, &vert_count, 4 );
1455 98 : if( needs_swap )
1456 98 : SwapData( vbuf.buffer, 4, 2 );
1457 :
1458 132 : for( i = 0; i < vert_count; i++ )
1459 : {
1460 34 : memcpy( vbuf.buffer + 8 + i*24 + 0, &(list[i].x), 8 );
1461 34 : memcpy( vbuf.buffer + 8 + i*24 + 8, &(list[i].y), 8 );
1462 34 : memcpy( vbuf.buffer + 8 + i*24 + 16, &(list[i].z), 8 );
1463 : }
1464 :
1465 98 : if( needs_swap )
1466 98 : SwapData( vbuf.buffer + 8, 8, 3*vert_count );
1467 :
1468 : /* -------------------------------------------------------------------- */
1469 : /* Write the data into the working buffer. */
1470 : /* -------------------------------------------------------------------- */
1471 196 : memcpy( GetData( sec_vert, vert_off, nullptr, vbuf.buffer_size, true ),
1472 98 : vbuf.buffer, vbuf.buffer_size );
1473 :
1474 : /* -------------------------------------------------------------------- */
1475 : /* Record the offset */
1476 : /* -------------------------------------------------------------------- */
1477 98 : if( shape_index_vertex_off[shape_index - shape_index_start] != vert_off )
1478 : {
1479 96 : shape_index_vertex_off[shape_index - shape_index_start] = vert_off;
1480 96 : shape_index_page_dirty = true;
1481 : }
1482 : }
1483 :
1484 : /************************************************************************/
1485 : /* SetFields() */
1486 : /************************************************************************/
1487 :
1488 88 : void CPCIDSKVectorSegment::SetFields( ShapeId id,
1489 : const std::vector<ShapeField>& list_in )
1490 :
1491 : {
1492 88 : FlushSegHeaderIfNeeded();
1493 : uint32 i;
1494 88 : int shape_index = IndexFromShapeId( id );
1495 88 : std::vector<ShapeField> full_list;
1496 88 : const std::vector<ShapeField> *listp = nullptr;
1497 :
1498 88 : if( shape_index == -1 )
1499 0 : return ThrowPCIDSKException( "Attempt to call SetFields() on non-existing shape id '%d'.",
1500 0 : (int) id );
1501 :
1502 88 : if( list_in.size() > vh.field_names.size() )
1503 : {
1504 0 : return ThrowPCIDSKException(
1505 : "Attempt to write %d fields to a layer with only %d fields.",
1506 0 : static_cast<int>(list_in.size()), static_cast<int>(vh.field_names.size()) );
1507 : }
1508 :
1509 88 : if( list_in.size() < vh.field_names.size() )
1510 : {
1511 0 : full_list = list_in;
1512 :
1513 : // fill out missing fields in list with defaults.
1514 0 : for( i = static_cast<uint32>(list_in.size()); i < static_cast<uint32>(vh.field_names.size()); i++ )
1515 0 : full_list[i] = vh.field_defaults[i];
1516 :
1517 0 : listp = &full_list;
1518 : }
1519 : else
1520 88 : listp = &list_in;
1521 :
1522 88 : AccessShapeByIndex( shape_index );
1523 :
1524 : /* -------------------------------------------------------------------- */
1525 : /* Format the fields in the buffer. */
1526 : /* -------------------------------------------------------------------- */
1527 176 : PCIDSKBuffer fbuf(4);
1528 88 : uint32 offset = 4;
1529 :
1530 444 : for( i = 0; i < listp->size(); i++ )
1531 356 : offset = WriteField( offset, (*listp)[i], fbuf );
1532 :
1533 88 : fbuf.SetSize( offset );
1534 :
1535 : /* -------------------------------------------------------------------- */
1536 : /* Is the current space big enough to hold the new field set? */
1537 : /* -------------------------------------------------------------------- */
1538 88 : uint32 rec_off = shape_index_record_off[shape_index - shape_index_start];
1539 88 : uint32 chunk_size = offset;
1540 :
1541 88 : if( rec_off != 0xffffffff )
1542 : {
1543 2 : memcpy( &chunk_size, GetData( sec_record, rec_off, nullptr, 4 ), 4 );
1544 2 : if( needs_swap )
1545 2 : SwapData( &chunk_size, 4, 1 );
1546 :
1547 2 : if( chunk_size < (uint32) fbuf.buffer_size )
1548 : {
1549 0 : rec_off = 0xffffffff;
1550 : }
1551 : }
1552 :
1553 : /* -------------------------------------------------------------------- */
1554 : /* Do we need to put this at the end of the section? */
1555 : /* -------------------------------------------------------------------- */
1556 88 : if( rec_off == 0xffffffff )
1557 : {
1558 86 : rec_off = di[sec_record].GetSectionEnd();
1559 86 : chunk_size = fbuf.buffer_size;
1560 : }
1561 :
1562 : /* -------------------------------------------------------------------- */
1563 : /* Set the chunk size, and number of fields. */
1564 : /* -------------------------------------------------------------------- */
1565 88 : memcpy( fbuf.buffer + 0, &chunk_size, 4 );
1566 :
1567 88 : if( needs_swap )
1568 88 : SwapData( fbuf.buffer, 4, 1 );
1569 :
1570 : /* -------------------------------------------------------------------- */
1571 : /* Write the data into the working buffer. */
1572 : /* -------------------------------------------------------------------- */
1573 176 : memcpy( GetData( sec_record, rec_off, nullptr, fbuf.buffer_size, true ),
1574 88 : fbuf.buffer, fbuf.buffer_size );
1575 :
1576 : /* -------------------------------------------------------------------- */
1577 : /* Record the offset */
1578 : /* -------------------------------------------------------------------- */
1579 88 : if( shape_index_record_off[shape_index - shape_index_start] != rec_off )
1580 : {
1581 86 : shape_index_record_off[shape_index - shape_index_start] = rec_off;
1582 86 : shape_index_page_dirty = true;
1583 : }
1584 : }
1585 :
1586 : /************************************************************************/
1587 : /* FlushLoadedShapeIndex() */
1588 : /************************************************************************/
1589 :
1590 3435 : void CPCIDSKVectorSegment::FlushLoadedShapeIndex()
1591 :
1592 : {
1593 3435 : if( !shape_index_page_dirty )
1594 3384 : return;
1595 :
1596 51 : uint32 offset = vh.ShapeIndexPrepare( total_shape_count * 12 + 4 );
1597 :
1598 51 : PCIDSKBuffer write_buffer( shapeid_page_size * 12 );
1599 :
1600 : // Update the count field.
1601 51 : memcpy( write_buffer.buffer, &total_shape_count, 4 );
1602 51 : if( needs_swap )
1603 51 : SwapData( write_buffer.buffer, 4, 1 );
1604 51 : WriteToFile( write_buffer.buffer, offset, 4 );
1605 :
1606 : // Write out the page of shapeid information.
1607 : unsigned int i;
1608 153 : for( i = 0; i < shape_index_ids.size(); i++ )
1609 : {
1610 102 : memcpy( write_buffer.buffer + 12*i,
1611 102 : &(shape_index_ids[i]), 4 );
1612 102 : memcpy( write_buffer.buffer + 12*i + 4,
1613 102 : &(shape_index_vertex_off[i]), 4 );
1614 102 : memcpy( write_buffer.buffer + 12*i + 8,
1615 102 : &(shape_index_record_off[i]), 4 );
1616 : }
1617 :
1618 51 : if( needs_swap )
1619 51 : SwapData( write_buffer.buffer, 4, static_cast<int>(shape_index_ids.size()) * 3 );
1620 :
1621 102 : WriteToFile( write_buffer.buffer,
1622 51 : offset + 4 + shape_index_start * 12,
1623 51 : 12 * shape_index_ids.size() );
1624 :
1625 : // invalidate the raw buffer.
1626 51 : raw_loaded_data.buffer_size = 0;
1627 :
1628 :
1629 51 : shape_index_page_dirty = false;
1630 : }
1631 :
|