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