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 186 : CPCIDSKChannel::CPCIDSKChannel( PCIDSKBuffer &image_header,
49 : uint64 ih_offset,
50 : CPCIDSKFile *file,
51 : eChanType pixel_type,
52 186 : int channel_number )
53 :
54 : {
55 186 : this->pixel_type = pixel_type;
56 186 : this->file = file;
57 186 : this->channel_number = channel_number;
58 186 : this->ih_offset = ih_offset;
59 :
60 186 : width = file->GetWidth();
61 186 : height = file->GetHeight();
62 :
63 186 : block_width = width;
64 186 : block_height = 1;
65 :
66 : /* -------------------------------------------------------------------- */
67 : /* Establish if we need to byte swap the data on load/store. */
68 : /* -------------------------------------------------------------------- */
69 186 : if( channel_number != -1 )
70 : {
71 181 : unsigned short test_value = 1;
72 :
73 181 : byte_order = image_header.buffer[201];
74 181 : if( ((uint8 *) &test_value)[0] == 1 )
75 181 : needs_swap = (byte_order != 'S');
76 : else
77 0 : needs_swap = (byte_order == 'S');
78 :
79 181 : if( pixel_type == CHN_8U )
80 119 : needs_swap = 0;
81 :
82 181 : 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 181 : metadata.Initialize( file, "IMG", channel_number );
90 : }
91 :
92 : /* -------------------------------------------------------------------- */
93 : /* No overviews for unassociated files, so just mark them as */
94 : /* initialized. */
95 : /* -------------------------------------------------------------------- */
96 186 : overviews_initialized = (channel_number == -1);
97 186 : }
98 :
99 : /************************************************************************/
100 : /* ~CPCIDSKChannel() */
101 : /************************************************************************/
102 :
103 186 : CPCIDSKChannel::~CPCIDSKChannel()
104 :
105 : {
106 186 : InvalidateOverviewInfo();
107 186 : }
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 187 : void CPCIDSKChannel::InvalidateOverviewInfo()
117 :
118 : {
119 192 : for( size_t io=0; io < overview_bands.size(); io++ )
120 : {
121 5 : if( overview_bands[io] != NULL )
122 : {
123 5 : delete overview_bands[io];
124 5 : overview_bands[io] = NULL;
125 : }
126 : }
127 :
128 187 : overview_infos.clear();
129 187 : overview_bands.clear();
130 187 : overview_decimations.clear();
131 :
132 187 : overviews_initialized = false;
133 187 : }
134 :
135 : /************************************************************************/
136 : /* EstablishOverviewInfo() */
137 : /************************************************************************/
138 874 : void CPCIDSKChannel::EstablishOverviewInfo() const
139 :
140 : {
141 874 : if( overviews_initialized )
142 694 : return;
143 :
144 180 : overviews_initialized = true;
145 :
146 180 : std::vector<std::string> keys = GetMetadataKeys();
147 : size_t i;
148 :
149 185 : for( i = 0; i < keys.size(); i++ )
150 : {
151 155 : if( strncmp(keys[i].c_str(),"_Overview_",10) != 0 )
152 150 : continue;
153 :
154 5 : std::string value = GetMetadataValue( keys[i] );
155 :
156 5 : overview_infos.push_back( value );
157 5 : overview_bands.push_back( NULL );
158 5 : overview_decimations.push_back( atoi(keys[i].c_str()+10) );
159 180 : }
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 527 : int CPCIDSKChannel::GetOverviewCount()
184 :
185 : {
186 527 : EstablishOverviewInfo();
187 :
188 527 : return overview_infos.size();
189 : }
190 :
191 : /************************************************************************/
192 : /* GetOverview() */
193 : /************************************************************************/
194 :
195 5 : PCIDSKChannel *CPCIDSKChannel::GetOverview( int overview_index )
196 :
197 : {
198 5 : EstablishOverviewInfo();
199 :
200 5 : if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
201 : ThrowPCIDSKException( "Non existant overview (%d) requested.",
202 0 : overview_index );
203 :
204 5 : if( overview_bands[overview_index] == NULL )
205 : {
206 5 : PCIDSKBuffer image_header(1024), file_header(1024);
207 : char pseudo_filename[65];
208 :
209 : sprintf( pseudo_filename, "/SIS=%d",
210 5 : atoi(overview_infos[overview_index].c_str()) );
211 :
212 5 : image_header.Put( pseudo_filename, 64, 64 );
213 :
214 : overview_bands[overview_index] =
215 : new CTiledChannel( image_header, 0, file_header, -1, file,
216 5 : CHN_UNKNOWN );
217 : }
218 :
219 5 : 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 21 : void CPCIDSKChannel::SetOverviewValidity( int overview_index,
270 : bool new_validity )
271 :
272 : {
273 21 : EstablishOverviewInfo();
274 :
275 21 : if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
276 : ThrowPCIDSKException( "Non existant overview (%d) requested.",
277 0 : overview_index );
278 :
279 21 : int sis_id, validity=0;
280 : char resampling[17];
281 :
282 : sscanf( overview_infos[overview_index].c_str(), "%d %d %16s",
283 21 : &sis_id, &validity, &(resampling[0]) );
284 :
285 : // are we already set to this value?
286 21 : if( new_validity == (validity != 0) )
287 19 : return;
288 :
289 : char new_info[48];
290 :
291 : sprintf( new_info, "%d %d %s",
292 2 : sis_id, (new_validity ? 1 : 0 ), resampling );
293 :
294 2 : overview_infos[overview_index] = new_info;
295 :
296 : // write back to metadata.
297 : char key[20];
298 2 : sprintf( key, "_Overview_%d", overview_decimations[overview_index] );
299 :
300 2 : 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 321 : void CPCIDSKChannel::InvalidateOverviews()
311 :
312 : {
313 321 : EstablishOverviewInfo();
314 :
315 341 : for( int i = 0; i < GetOverviewCount(); i++ )
316 20 : SetOverviewValidity( i, false );
317 321 : }
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 197 : std::string CPCIDSKChannel::GetDescription()
335 :
336 : {
337 197 : if( ih_offset == 0 )
338 0 : return "";
339 :
340 197 : PCIDSKBuffer ih_1(64);
341 197 : std::string ret;
342 :
343 197 : file->ReadFromFile( ih_1.buffer, ih_offset, 64 );
344 197 : ih_1.Get(0,64,ret);
345 :
346 197 : return ret;
347 : }
348 :
349 : /************************************************************************/
350 : /* SetDescription() */
351 : /************************************************************************/
352 :
353 1 : void CPCIDSKChannel::SetDescription( const std::string &description )
354 :
355 : {
356 1 : if( ih_offset == 0 )
357 0 : ThrowPCIDSKException( "Description cannot be set on overviews." );
358 :
359 1 : PCIDSKBuffer ih_1(64);
360 1 : ih_1.Put( description.c_str(), 0, 64 );
361 1 : file->WriteToFile( ih_1.buffer, ih_offset, 64 );
362 1 : }
363 :
364 : /************************************************************************/
365 : /* LoadHistory() */
366 : /************************************************************************/
367 :
368 181 : void CPCIDSKChannel::LoadHistory( const PCIDSKBuffer &image_header )
369 :
370 : {
371 : // Read the history from the image header. PCIDSK supports
372 : // 8 history entries per channel.
373 :
374 181 : std::string hist_msg;
375 181 : history_.clear();
376 1629 : for (unsigned int i = 0; i < 8; i++)
377 : {
378 1448 : image_header.Get(384 + i * 80, 80, hist_msg);
379 :
380 : // Some programs seem to push history records with a trailing '\0'
381 : // so do some extra processing to cleanup. FUN records on segment
382 : // 3 of eltoro.pix are an example of this.
383 1448 : size_t size = hist_msg.size();
384 2896 : while( size > 0
385 : && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') )
386 0 : size--;
387 :
388 1448 : hist_msg.resize(size);
389 :
390 1448 : history_.push_back(hist_msg);
391 181 : }
392 181 : }
393 :
394 : /************************************************************************/
395 : /* GetHistoryEntries() */
396 : /************************************************************************/
397 :
398 0 : std::vector<std::string> CPCIDSKChannel::GetHistoryEntries() const
399 : {
400 0 : return history_;
401 : }
402 :
403 : /************************************************************************/
404 : /* SetHistoryEntries() */
405 : /************************************************************************/
406 :
407 0 : void CPCIDSKChannel::SetHistoryEntries(const std::vector<std::string> &entries)
408 :
409 : {
410 0 : if( ih_offset == 0 )
411 0 : ThrowPCIDSKException( "Attempt to update history on a raster that is not\na conventional band with an image header." );
412 :
413 0 : PCIDSKBuffer image_header(1024);
414 :
415 0 : file->ReadFromFile( image_header.buffer, ih_offset, 1024 );
416 :
417 0 : for( unsigned int i = 0; i < 8; i++ )
418 : {
419 0 : const char *msg = "";
420 0 : if( entries.size() > i )
421 0 : msg = entries[i].c_str();
422 :
423 0 : image_header.Put( msg, 384 + i * 80, 80 );
424 : }
425 :
426 0 : file->WriteToFile( image_header.buffer, ih_offset, 1024 );
427 :
428 : // Force reloading of history_
429 0 : LoadHistory( image_header );
430 0 : }
431 :
432 : /************************************************************************/
433 : /* PushHistory() */
434 : /************************************************************************/
435 :
436 0 : void CPCIDSKChannel::PushHistory( const std::string &app,
437 : const std::string &message )
438 :
439 : {
440 : #define MY_MIN(a,b) ((a<b) ? a : b)
441 :
442 : char current_time[17];
443 : char history[81];
444 :
445 0 : GetCurrentDateTime( current_time );
446 :
447 0 : memset( history, ' ', 80 );
448 0 : history[80] = '\0';
449 :
450 0 : memcpy( history + 0, app.c_str(), MY_MIN(app.size(),7) );
451 0 : history[7] = ':';
452 :
453 0 : memcpy( history + 8, message.c_str(), MY_MIN(message.size(),56) );
454 0 : memcpy( history + 64, current_time, 16 );
455 :
456 0 : std::vector<std::string> history_entries = GetHistoryEntries();
457 :
458 0 : history_entries.insert( history_entries.begin(), history );
459 0 : history_entries.resize(8);
460 :
461 0 : SetHistoryEntries( history_entries );
462 0 : }
463 :
464 : /************************************************************************/
465 : /* GetChanInfo() */
466 : /************************************************************************/
467 3 : void CPCIDSKChannel::GetChanInfo( std::string &filename, uint64 &image_offset,
468 : uint64 &pixel_offset, uint64 &line_offset,
469 : bool &little_endian ) const
470 :
471 : {
472 3 : image_offset = 0;
473 3 : pixel_offset = 0;
474 3 : line_offset = 0;
475 3 : little_endian = true;
476 3 : filename = "";
477 3 : }
478 :
479 : /************************************************************************/
480 : /* SetChanInfo() */
481 : /************************************************************************/
482 :
483 0 : void CPCIDSKChannel::SetChanInfo( std::string filename, uint64 image_offset,
484 : uint64 pixel_offset, uint64 line_offset,
485 : bool little_endian )
486 :
487 : {
488 0 : ThrowPCIDSKException( "Attempt to SetChanInfo() on a channel that is not FILE interleaved." );
489 0 : }
490 :
491 : /************************************************************************/
492 : /* GetEChanInfo() */
493 : /************************************************************************/
494 0 : void CPCIDSKChannel::GetEChanInfo( std::string &filename, int &echannel,
495 : int &exoff, int &eyoff,
496 : int &exsize, int &eysize ) const
497 :
498 : {
499 0 : echannel = 0;
500 0 : exoff = 0;
501 0 : eyoff = 0;
502 0 : exsize = 0;
503 0 : eysize = 0;
504 0 : filename = "";
505 0 : }
506 :
507 : /************************************************************************/
508 : /* SetEChanInfo() */
509 : /************************************************************************/
510 :
511 0 : void CPCIDSKChannel::SetEChanInfo( std::string filename, int echannel,
512 : int exoff, int eyoff,
513 : int exsize, int eysize )
514 :
515 : {
516 0 : ThrowPCIDSKException( "Attempt to SetEChanInfo() on a channel that is not FILE interleaved." );
517 0 : }
|