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