1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the CPCIDSKChannel Abstract class.
4 : *
5 : ******************************************************************************
6 : * Copyright (c) 2009
7 : * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada
8 : *
9 : * Permission is hereby granted, free of charge, to any person obtaining a
10 : * copy of this software and associated documentation files (the "Software"),
11 : * to deal in the Software without restriction, including without limitation
12 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 : * and/or sell copies of the Software, and to permit persons to whom the
14 : * Software is furnished to do so, subject to the following conditions:
15 : *
16 : * The above copyright notice and this permission notice shall be included
17 : * in all copies or substantial portions of the Software.
18 : *
19 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 : * DEALINGS IN THE SOFTWARE.
26 : ****************************************************************************/
27 :
28 : #include "pcidsk_config.h"
29 : #include "pcidsk_types.h"
30 : #include "core/pcidsk_utils.h"
31 : #include "pcidsk_exception.h"
32 : #include "pcidsk_channel.h"
33 : #include "core/cpcidskfile.h"
34 : #include "channel/cpcidskchannel.h"
35 : #include "channel/ctiledchannel.h"
36 : #include <cstring>
37 : #include <cassert>
38 : #include <cstdlib>
39 : #include <cstring>
40 : #include <cstdio>
41 :
42 : using namespace PCIDSK;
43 :
44 : /************************************************************************/
45 : /* CPCIDSKChannel() */
46 : /************************************************************************/
47 :
48 : CPCIDSKChannel::CPCIDSKChannel( PCIDSKBuffer &image_header,
49 : uint64 ih_offset,
50 : CPCIDSKFile *file,
51 : eChanType pixel_type,
52 242 : int channel_number )
53 :
54 : {
55 242 : this->pixel_type = pixel_type;
56 242 : this->file = file;
57 242 : this->channel_number = channel_number;
58 242 : this->ih_offset = ih_offset;
59 :
60 484 : width = file->GetWidth();
61 242 : height = file->GetHeight();
62 :
63 242 : block_width = width;
64 242 : block_height = 1;
65 :
66 : /* -------------------------------------------------------------------- */
67 : /* Establish if we need to byte swap the data on load/store. */
68 : /* -------------------------------------------------------------------- */
69 242 : if( channel_number != -1 )
70 : {
71 239 : unsigned short test_value = 1;
72 :
73 239 : byte_order = image_header.buffer[201];
74 239 : if( ((uint8 *) &test_value)[0] == 1 )
75 239 : needs_swap = (byte_order != 'S');
76 : else
77 0 : needs_swap = (byte_order == 'S');
78 :
79 239 : if( pixel_type == CHN_8U )
80 157 : needs_swap = 0;
81 :
82 239 : LoadHistory( image_header );
83 :
84 : /* -------------------------------------------------------------------- */
85 : /* Initialize the metadata object, but do not try to load till */
86 : /* needed. We avoid doing this for unassociated channels such */
87 : /* as overviews. */
88 : /* -------------------------------------------------------------------- */
89 239 : metadata.Initialize( file, "IMG", channel_number );
90 : }
91 :
92 : /* -------------------------------------------------------------------- */
93 : /* No overviews for unassociated files, so just mark them as */
94 : /* initialized. */
95 : /* -------------------------------------------------------------------- */
96 242 : overviews_initialized = (channel_number == -1);
97 242 : }
98 :
99 : /************************************************************************/
100 : /* ~CPCIDSKChannel() */
101 : /************************************************************************/
102 :
103 242 : CPCIDSKChannel::~CPCIDSKChannel()
104 :
105 : {
106 242 : InvalidateOverviewInfo();
107 242 : }
108 :
109 : /************************************************************************/
110 : /* InvalidateOverviewInfo() */
111 : /* */
112 : /* This is called when CreateOverviews() creates overviews - we */
113 : /* invalidate our loaded info and re-establish on a next request. */
114 : /************************************************************************/
115 :
116 242 : void CPCIDSKChannel::InvalidateOverviewInfo()
117 :
118 : {
119 245 : for( size_t io=0; io < overview_bands.size(); io++ )
120 : {
121 3 : if( overview_bands[io] != NULL )
122 : {
123 3 : delete overview_bands[io];
124 3 : overview_bands[io] = NULL;
125 : }
126 : }
127 :
128 242 : overview_infos.clear();
129 242 : overview_bands.clear();
130 242 : overview_decimations.clear();
131 :
132 242 : overviews_initialized = false;
133 242 : }
134 :
135 : /************************************************************************/
136 : /* EstablishOverviewInfo() */
137 : /************************************************************************/
138 794 : void CPCIDSKChannel::EstablishOverviewInfo() const
139 :
140 : {
141 794 : if( overviews_initialized )
142 640 : return;
143 :
144 154 : overviews_initialized = true;
145 :
146 154 : std::vector<std::string> keys = GetMetadataKeys();
147 : size_t i;
148 :
149 167 : for( i = 0; i < keys.size(); i++ )
150 : {
151 10 : if( strncmp(keys[i].c_str(),"_Overview_",10) != 0 )
152 7 : continue;
153 :
154 3 : std::string value = GetMetadataValue( keys[i] );
155 :
156 3 : overview_infos.push_back( value );
157 3 : overview_bands.push_back( NULL );
158 3 : overview_decimations.push_back( atoi(keys[i].c_str()+10) );
159 154 : }
160 : }
161 :
162 : /************************************************************************/
163 : /* GetBlockCount() */
164 : /************************************************************************/
165 :
166 0 : int CPCIDSKChannel::GetBlockCount() const
167 :
168 : {
169 : // We deliberately call GetBlockWidth() and GetWidth() to trigger
170 : // computation of the values for tiled layers. At some point it would
171 : // be good to cache the block count as this computation is a bit expensive
172 :
173 0 : int x_block_count = (GetWidth() + GetBlockWidth() - 1) / GetBlockWidth();
174 0 : int y_block_count = (GetHeight() + GetBlockHeight() - 1) / GetBlockHeight();
175 :
176 0 : return x_block_count * y_block_count;
177 : }
178 :
179 : /************************************************************************/
180 : /* GetOverviewCount() */
181 : /************************************************************************/
182 :
183 474 : int CPCIDSKChannel::GetOverviewCount()
184 :
185 : {
186 474 : EstablishOverviewInfo();
187 :
188 474 : return overview_infos.size();
189 : }
190 :
191 : /************************************************************************/
192 : /* GetOverview() */
193 : /************************************************************************/
194 :
195 3 : PCIDSKChannel *CPCIDSKChannel::GetOverview( int overview_index )
196 :
197 : {
198 3 : EstablishOverviewInfo();
199 :
200 3 : if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
201 : ThrowPCIDSKException( "Non existant overview (%d) requested.",
202 0 : overview_index );
203 :
204 3 : if( overview_bands[overview_index] == NULL )
205 : {
206 3 : PCIDSKBuffer image_header(1024), file_header(1024);
207 : char pseudo_filename[65];
208 :
209 : sprintf( pseudo_filename, "/SIS=%d",
210 3 : atoi(overview_infos[overview_index].c_str()) );
211 :
212 3 : image_header.Put( pseudo_filename, 64, 64 );
213 :
214 : overview_bands[overview_index] =
215 : new CTiledChannel( image_header, 0, file_header, -1, file,
216 3 : CHN_UNKNOWN );
217 : }
218 :
219 3 : return overview_bands[overview_index];
220 : }
221 :
222 : /************************************************************************/
223 : /* IsOverviewValid() */
224 : /************************************************************************/
225 :
226 0 : bool CPCIDSKChannel::IsOverviewValid( int overview_index )
227 :
228 : {
229 0 : EstablishOverviewInfo();
230 :
231 0 : if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
232 : ThrowPCIDSKException( "Non existant overview (%d) requested.",
233 0 : overview_index );
234 :
235 0 : int sis_id, validity=0;
236 :
237 : sscanf( overview_infos[overview_index].c_str(), "%d %d",
238 0 : &sis_id, &validity );
239 :
240 0 : return validity != 0;
241 : }
242 :
243 : /************************************************************************/
244 : /* GetOverviewResampling() */
245 : /************************************************************************/
246 :
247 0 : std::string CPCIDSKChannel::GetOverviewResampling( int overview_index )
248 :
249 : {
250 0 : EstablishOverviewInfo();
251 :
252 0 : if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
253 : ThrowPCIDSKException( "Non existant overview (%d) requested.",
254 0 : overview_index );
255 :
256 0 : int sis_id, validity=0;
257 : char resampling[17];
258 :
259 : sscanf( overview_infos[overview_index].c_str(), "%d %d %16s",
260 0 : &sis_id, &validity, &(resampling[0]) );
261 :
262 0 : return resampling;
263 : }
264 :
265 : /************************************************************************/
266 : /* SetOverviewValidity() */
267 : /************************************************************************/
268 :
269 : void CPCIDSKChannel::SetOverviewValidity( int overview_index,
270 0 : bool new_validity )
271 :
272 : {
273 0 : EstablishOverviewInfo();
274 :
275 0 : if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
276 : ThrowPCIDSKException( "Non existant overview (%d) requested.",
277 0 : overview_index );
278 :
279 0 : int sis_id, validity=0;
280 : char resampling[17];
281 :
282 : sscanf( overview_infos[overview_index].c_str(), "%d %d %16s",
283 0 : &sis_id, &validity, &(resampling[0]) );
284 :
285 : // are we already set to this value?
286 0 : if( new_validity == (validity != 0) )
287 0 : return;
288 :
289 : char new_info[48];
290 :
291 : sprintf( new_info, "%d %d %s",
292 0 : sis_id, (new_validity ? 1 : 0 ), resampling );
293 :
294 0 : overview_infos[overview_index] = new_info;
295 :
296 : // write back to metadata.
297 : char key[20];
298 0 : sprintf( key, "_Overview_%d", overview_decimations[overview_index] );
299 :
300 0 : SetMetadataValue( key, new_info );
301 : }
302 :
303 : /************************************************************************/
304 : /* InvalidateOverviews() */
305 : /* */
306 : /* Whenever a write is done on this band, we will invalidate */
307 : /* any previously valid overviews. */
308 : /************************************************************************/
309 :
310 317 : void CPCIDSKChannel::InvalidateOverviews()
311 :
312 : {
313 317 : EstablishOverviewInfo();
314 :
315 317 : for( int i = 0; i < GetOverviewCount(); i++ )
316 0 : SetOverviewValidity( i, false );
317 317 : }
318 :
319 : /************************************************************************/
320 : /* GetOverviewLevelMapping() */
321 : /************************************************************************/
322 :
323 0 : std::vector<int> CPCIDSKChannel::GetOverviewLevelMapping() const
324 : {
325 0 : EstablishOverviewInfo();
326 :
327 0 : return overview_decimations;
328 : }
329 :
330 : /************************************************************************/
331 : /* GetDescription() */
332 : /************************************************************************/
333 :
334 154 : std::string CPCIDSKChannel::GetDescription()
335 :
336 : {
337 154 : if( ih_offset == 0 )
338 0 : return "";
339 :
340 154 : PCIDSKBuffer ih_1(64);
341 154 : std::string ret;
342 :
343 154 : file->ReadFromFile( ih_1.buffer, ih_offset, 64 );
344 154 : ih_1.Get(0,64,ret);
345 :
346 154 : return ret;
347 :
348 : }
349 :
350 : /************************************************************************/
351 : /* SetDescription() */
352 : /************************************************************************/
353 :
354 0 : void CPCIDSKChannel::SetDescription( const std::string &description )
355 :
356 : {
357 0 : if( ih_offset == 0 )
358 0 : ThrowPCIDSKException( "Description cannot be set on overviews." );
359 :
360 0 : PCIDSKBuffer ih_1(64);
361 0 : ih_1.Put( description.c_str(), 0, 64 );
362 0 : file->WriteToFile( ih_1.buffer, ih_offset, 64 );
363 0 : }
364 :
365 : /************************************************************************/
366 : /* LoadHistory() */
367 : /************************************************************************/
368 :
369 239 : void CPCIDSKChannel::LoadHistory( const PCIDSKBuffer &image_header )
370 :
371 : {
372 : // Read the history from the image header. PCIDSK supports
373 : // 8 history entries per channel.
374 :
375 239 : std::string hist_msg;
376 239 : history_.clear();
377 2151 : for (unsigned int i = 0; i < 8; i++)
378 : {
379 1912 : image_header.Get(384 + i * 80, 80, hist_msg);
380 :
381 : // Some programs seem to push history records with a trailing '\0'
382 : // so do some extra processing to cleanup. FUN records on segment
383 : // 3 of eltoro.pix are an example of this.
384 1912 : size_t size = hist_msg.size();
385 3824 : while( size > 0
386 : && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') )
387 0 : size--;
388 :
389 1912 : hist_msg.resize(size);
390 :
391 1912 : history_.push_back(hist_msg);
392 239 : }
393 239 : }
394 :
395 : /************************************************************************/
396 : /* GetHistoryEntries() */
397 : /************************************************************************/
398 :
399 0 : std::vector<std::string> CPCIDSKChannel::GetHistoryEntries() const
400 : {
401 0 : return history_;
402 : }
403 :
404 : /************************************************************************/
405 : /* SetHistoryEntries() */
406 : /************************************************************************/
407 :
408 0 : void CPCIDSKChannel::SetHistoryEntries(const std::vector<std::string> &entries)
409 :
410 : {
411 0 : if( ih_offset == 0 )
412 0 : ThrowPCIDSKException( "Attempt to update history on a raster that is not\na conventional band with an image header." );
413 :
414 0 : PCIDSKBuffer image_header(1024);
415 :
416 0 : file->ReadFromFile( image_header.buffer, ih_offset, 1024 );
417 :
418 0 : for( unsigned int i = 0; i < 8; i++ )
419 : {
420 0 : const char *msg = "";
421 0 : if( entries.size() > i )
422 0 : msg = entries[i].c_str();
423 :
424 0 : image_header.Put( msg, 384 + i * 80, 80 );
425 : }
426 :
427 0 : file->WriteToFile( image_header.buffer, ih_offset, 1024 );
428 :
429 : // Force reloading of history_
430 0 : LoadHistory( image_header );
431 0 : }
432 :
433 : /************************************************************************/
434 : /* PushHistory() */
435 : /************************************************************************/
436 :
437 : void CPCIDSKChannel::PushHistory( const std::string &app,
438 0 : const std::string &message )
439 :
440 : {
441 : #define MY_MIN(a,b) ((a<b) ? a : b)
442 :
443 : char current_time[17];
444 : char history[81];
445 :
446 0 : GetCurrentDateTime( current_time );
447 :
448 0 : memset( history, ' ', 80 );
449 0 : history[80] = '\0';
450 :
451 0 : memcpy( history + 0, app.c_str(), MY_MIN(app.size(),7) );
452 0 : history[7] = ':';
453 :
454 0 : memcpy( history + 8, message.c_str(), MY_MIN(message.size(),56) );
455 0 : memcpy( history + 64, current_time, 16 );
456 :
457 0 : std::vector<std::string> history_entries = GetHistoryEntries();
458 :
459 0 : history_entries.insert( history_entries.begin(), history );
460 0 : history_entries.resize(8);
461 :
462 0 : SetHistoryEntries( history_entries );
463 0 : }
|