Line data Source code
1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the CPCIDSK_TEX class.
4 : *
5 : ******************************************************************************
6 : * Copyright (c) 2010
7 : * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
8 : *
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #include "pcidsk_exception.h"
13 : #include "segment/cpcidsk_array.h"
14 : #include "core/cpcidskfile.h"
15 : #include <cstring>
16 : #include <sstream>
17 : #include <cassert>
18 : #include "core/pcidsk_utils.h"
19 :
20 : using namespace PCIDSK;
21 :
22 : PCIDSK_ARRAY::~PCIDSK_ARRAY() = default;
23 :
24 : /************************************************************************/
25 : /* CPCIDSK_ARRAY() */
26 : /************************************************************************/
27 :
28 0 : CPCIDSK_ARRAY::CPCIDSK_ARRAY( PCIDSKFile *fileIn, int segmentIn,
29 0 : const char *segment_pointer )
30 : : CPCIDSKSegment( fileIn, segmentIn, segment_pointer ),
31 0 : loaded_(false),mbModified(false)
32 : {
33 0 : MAX_DIMENSIONS = 8;
34 0 : Load();
35 0 : }
36 :
37 : /************************************************************************/
38 : /* ~CPCIDSK_ARRAY */
39 : /************************************************************************/
40 :
41 0 : CPCIDSK_ARRAY::~CPCIDSK_ARRAY()
42 :
43 : {
44 0 : }
45 :
46 : /**
47 : * Load the contents of the segment
48 : */
49 0 : void CPCIDSK_ARRAY::Load()
50 : {
51 : // Check if we've already loaded the segment into memory
52 0 : if (loaded_) {
53 0 : return;
54 : }
55 :
56 0 : PCIDSKBuffer& seg_header = this->GetHeader();
57 0 : seg_data.SetSize(!IsContentSizeValid() ? -1 : // will throw exception
58 0 : static_cast<int>(GetContentSize()));
59 0 : ReadFromFile(seg_data.buffer, 0, seg_data.buffer_size);
60 :
61 0 : if(!STARTS_WITH(seg_header.buffer+160, "64R "))
62 : {
63 0 : seg_header.Put("64R ",160,8);
64 0 : loaded_ = true;
65 0 : return ;
66 : }
67 :
68 0 : int nDimension = seg_header.GetInt(160+8,8);
69 0 : if(nDimension < 1 || nDimension > MAX_DIMENSIONS)
70 : {
71 0 : std::stringstream oStream;
72 0 : oStream << "Invalid array dimension " << nDimension;
73 0 : oStream << " stored in the segment.";
74 0 : std::string oMsg = oStream.str();
75 0 : return ThrowPCIDSKException("%s", oMsg.c_str());
76 : }
77 0 : mnDimension = static_cast<unsigned char>(nDimension);
78 :
79 0 : moSizes.clear();
80 0 : for( int i = 0; i < mnDimension; i++ )
81 : {
82 0 : int nSize = seg_header.GetInt(160+24 + i*8,8);
83 0 : if(nSize < 1)
84 : {
85 0 : std::stringstream oStream;
86 0 : oStream << "Invalid size " << nSize << " for dimension " << i+1;
87 0 : std::string oMsg = oStream.str();
88 0 : return ThrowPCIDSKException("%s", oMsg.c_str());
89 : }
90 0 : moSizes.push_back( nSize );
91 : }
92 :
93 : //calculate the total number of elements in the array.
94 0 : unsigned int nElements = 1;
95 0 : for(unsigned int i=0 ; i < moSizes.size() ; i++)
96 : {
97 0 : nElements *= moSizes[i];
98 : }
99 :
100 0 : moArray.resize(nElements);
101 0 : for( unsigned int i = 0; i < nElements; i++ )
102 : {
103 0 : const double* pdValue = (const double*)seg_data.Get(i*8,8);
104 : char uValue[8];
105 0 : std::memcpy(uValue,pdValue,8);
106 0 : SwapData(uValue,8,1);
107 0 : memcpy(&moArray[i], uValue, 8);
108 : }
109 :
110 : //PCIDSK doesn't have support for headers.
111 :
112 : // We've now loaded the structure up with data. Mark it as being loaded
113 : // properly.
114 0 : loaded_ = true;
115 :
116 : }
117 :
118 : /**
119 : * Write the segment on disk
120 : */
121 0 : void CPCIDSK_ARRAY::Write(void)
122 : {
123 : //We are not writing if nothing was loaded.
124 0 : if (!loaded_) {
125 0 : return;
126 : }
127 :
128 0 : PCIDSKBuffer& seg_header = this->GetHeader();
129 0 : int nBlocks = (static_cast<int>(moArray.size())*8 + 511)/512 ;
130 0 : unsigned int nSizeBuffer = (nBlocks)*512 ;
131 : //64 values can be put into 512 bytes.
132 0 : unsigned int nRest = nBlocks*64 - static_cast<unsigned int>(moArray.size());
133 :
134 0 : seg_data.SetSize(nSizeBuffer);
135 :
136 0 : seg_header.Put("64R ",160,8);
137 0 : seg_header.Put((int)mnDimension,160+8,8);
138 :
139 0 : for( int i = 0; i < mnDimension; i++ )
140 : {
141 0 : int nSize = static_cast<int>(moSizes[i]);
142 0 : seg_header.Put(nSize,160+24 + i*8,8);
143 : }
144 :
145 0 : for( unsigned int i = 0; i < moArray.size(); i++ )
146 : {
147 0 : double dValue = moArray[i];
148 0 : SwapData(&dValue,8,1);
149 0 : seg_data.PutBin(dValue,i*8);
150 : }
151 :
152 : //set the end of the buffer to 0.
153 0 : for( unsigned int i=0 ; i < nRest ; i++)
154 : {
155 0 : seg_data.Put(0.0,(static_cast<int>(moArray.size())+i)*8,8,"%22.14f");
156 : }
157 :
158 0 : WriteToFile(seg_data.buffer,0,seg_data.buffer_size);
159 :
160 0 : mbModified = false;
161 : }
162 :
163 : /**
164 : * Synchronize the segment, if it was modified then
165 : * write it into disk.
166 : */
167 0 : void CPCIDSK_ARRAY::Synchronize()
168 : {
169 0 : if(mbModified)
170 : {
171 0 : this->Write();
172 : //write the modified header
173 0 : file->WriteToFile( header.buffer, data_offset, 1024 );
174 : }
175 0 : }
176 :
177 : /**
178 : * This function returns the number of dimension in the array.
179 : * an array segment can have minimum 1 dimension and maximum
180 : * 8 dimension.
181 : *
182 : * @return the dimension of the array in [1,8]
183 : */
184 0 : unsigned char CPCIDSK_ARRAY::GetDimensionCount() const
185 : {
186 0 : return mnDimension;
187 : }
188 :
189 : /**
190 : * This function set the dimension of the array. the dimension
191 : * must be in [1,8] or a pci::Exception is thrown.
192 : *
193 : * @param nDim number of dimension, should be in [1,8]
194 : */
195 0 : void CPCIDSK_ARRAY::SetDimensionCount(unsigned char nDim)
196 : {
197 0 : if( !file->GetUpdatable() )
198 0 : return ThrowPCIDSKException("File not open for update.");
199 0 : if(nDim < 1 || nDim > 8)
200 : {
201 0 : return ThrowPCIDSKException("An array cannot have a "
202 0 : "dimension bigger than 8 or smaller than 1.");
203 : }
204 0 : mnDimension = nDim;
205 0 : mbModified = true;
206 : }
207 :
208 : /**
209 : * Get the number of element that can be put in each of the dimension
210 : * of the array. the size of the return vector is GetDimensionCount().
211 : *
212 : * @return the size of each dimension.
213 : */
214 0 : const std::vector<unsigned int>& CPCIDSK_ARRAY::GetSizes() const
215 : {
216 0 : return moSizes;
217 : }
218 :
219 : /**
220 : * Set the size of each dimension. If the size of the array is bigger
221 : * or smaller than GetDimensionCount(), then a pci::Exception is thrown
222 : * if one of the sizes is 0, then a pci::Exception is thrown.
223 : *
224 : * @param oSizes the size of each dimension
225 : */
226 0 : void CPCIDSK_ARRAY::SetSizes(const std::vector<unsigned int>& oSizes)
227 : {
228 0 : if(oSizes.size() != GetDimensionCount())
229 : {
230 0 : return ThrowPCIDSKException("You need to specify the sizes"
231 0 : " for each dimension of the array");
232 : }
233 :
234 0 : for( unsigned int i=0 ; i < oSizes.size() ; i++)
235 : {
236 0 : if(oSizes[i] == 0)
237 : {
238 0 : return ThrowPCIDSKException("You cannot define the size of a dimension to 0.");
239 : }
240 : }
241 0 : moSizes = oSizes;
242 0 : mbModified = true;
243 : }
244 :
245 : /**
246 : * Get the array in a vector. the size of this vector is
247 : * GetSize()[0]*GetSize()[2]*...*GetSize()[GetDimensionCount()-1].
248 : * value are stored in the following order inside this vector:
249 : * ViDj = Value i of Dimension j
250 : * n = size of dimension 1
251 : * p = size of dimension 2
252 : * h = size of dimension k
253 : *
254 : * V1D1 ... VnD1 V1D2 ... VpD2 ... V1Dk ... VhDk
255 : *
256 : * @return the array.
257 : */
258 0 : const std::vector<double>& CPCIDSK_ARRAY::GetArray() const
259 : {
260 0 : return moArray;
261 : }
262 :
263 : /**
264 : * Set the array in the segment. the size of this vector is
265 : * GetSize()[0]*GetSize()[2]*...*GetSize()[GetDimensionCount()-1].
266 : * value are stored in the following order inside this vector:
267 : * ViDj = Value i of Dimension j
268 : * n = size of dimension 1
269 : * p = size of dimension 2
270 : * h = size of dimension k
271 : *
272 : * V1D1 ... VnD1 V1D2 ... VpD2 ... V1Dk ... VhDk
273 : *
274 : * If the size of oArray doesn't match the sizes and dimensions
275 : * then a pci::Exception is thrown.
276 : *
277 : * @param oArray the array.
278 : */
279 0 : void CPCIDSK_ARRAY::SetArray(const std::vector<double>& oArray)
280 : {
281 0 : if( !file->GetUpdatable() )
282 0 : return ThrowPCIDSKException("File not open for update.");
283 0 : unsigned int nLength = 1;
284 0 : for( unsigned int i=0 ; i < moSizes.size() ; i++)
285 : {
286 0 : nLength *= moSizes[i];
287 : }
288 :
289 0 : if(nLength != oArray.size())
290 : {
291 0 : return ThrowPCIDSKException("the size of this array doesn't match "
292 : "the size specified in GetSizes(). See documentation for"
293 0 : " more information.");
294 : }
295 0 : moArray = oArray;
296 0 : mbModified = true;
297 : }
298 :
299 : /**
300 : * Get the headers of this array. If no headers has be specified, then
301 : * this function return an empty vector.
302 : * the size of this vector should be equal to the size of the first dimension
303 : * returned by GetSize()[0]
304 : *
305 : * @return the headers.
306 : */
307 0 : const std::vector<std::string>& CPCIDSK_ARRAY::GetHeaders() const
308 : {
309 0 : return moHeaders;
310 : }
311 :
312 : /**
313 : * Set the headers of this array. An empty vector can be specified to clear
314 : * the headers in the segment.
315 : * the size of this vector should be equal to the size of the first dimension
316 : * returned by GetSize()[0]. If it is not the case, a pci::Exception is thrown.
317 : *
318 : * @param oHeaders the headers.
319 : */
320 0 : void CPCIDSK_ARRAY::SetHeaders(const std::vector<std::string>& oHeaders)
321 : {
322 0 : moHeaders = oHeaders;
323 0 : mbModified = true;
324 0 : }
|