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