1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the Create() function to create new PCIDSK files.
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 : #include "pcidsk.h"
28 : #include "pcidsk_config.h"
29 : #include "pcidsk_types.h"
30 : #include "pcidsk_exception.h"
31 : #include "pcidsk_file.h"
32 : #include "pcidsk_georef.h"
33 : #include "core/pcidsk_utils.h"
34 : #include "segment/sysblockmap.h"
35 : #include <cassert>
36 : #include <cstdlib>
37 : #include <cstring>
38 : #include <cstdio>
39 :
40 : using namespace PCIDSK;
41 :
42 : /************************************************************************/
43 : /* Create() */
44 : /************************************************************************/
45 :
46 : /**
47 : * Create a PCIDSK (.pix) file.
48 : *
49 : * @param filename the name of the PCIDSK file to create.
50 : * @param pixels the width of the new file in pixels.
51 : * @param lines the height of the new file in scanlines.
52 : * @param channel_count the number of channels to create.
53 : * @param channel_types an array of types for all the channels, or NULL for
54 : * all CHN_8U channels.
55 : * @param options creation options (interleaving, etc)
56 : * @param interfaces Either NULL to use default interfaces, or a pointer
57 : * to a populated interfaces object.
58 : *
59 : * @return a pointer to a file object for accessing the PCIDSK file.
60 : */
61 :
62 : PCIDSKFile PCIDSK_DLL *
63 54 : PCIDSK::Create( std::string filename, int pixels, int lines,
64 : int channel_count, eChanType *channel_types,
65 : std::string options, const PCIDSKInterfaces *interfaces )
66 :
67 : {
68 : /* -------------------------------------------------------------------- */
69 : /* Use default interfaces if none are passed in. */
70 : /* -------------------------------------------------------------------- */
71 54 : PCIDSKInterfaces default_interfaces;
72 54 : if( interfaces == NULL )
73 0 : interfaces = &default_interfaces;
74 :
75 : /* -------------------------------------------------------------------- */
76 : /* Default the channel types to all 8U if not provided. */
77 : /* -------------------------------------------------------------------- */
78 54 : std::vector<eChanType> default_channel_types;
79 :
80 54 : if( channel_types == NULL )
81 : {
82 2 : default_channel_types.resize( channel_count+1, CHN_8U );
83 2 : channel_types = &(default_channel_types[0]);
84 : }
85 :
86 : /* -------------------------------------------------------------------- */
87 : /* Validate parameters. */
88 : /* -------------------------------------------------------------------- */
89 54 : const char *interleaving = NULL;
90 54 : std::string compression = "NONE";
91 54 : bool nozero = false;
92 54 : bool nocreate = false;
93 54 : bool externallink = false;
94 54 : int blocksize = 127;
95 :
96 54 : UCaseStr( options );
97 :
98 54 : if(strncmp(options.c_str(),"PIXEL",5) == 0 )
99 0 : interleaving = "PIXEL";
100 54 : else if( strncmp(options.c_str(),"BAND",4) == 0 )
101 50 : interleaving = "BAND";
102 4 : else if( strncmp(options.c_str(),"TILED",5) == 0 )
103 : {
104 3 : interleaving = "FILE";
105 3 : ParseTileFormat( options, blocksize, compression );
106 : }
107 1 : else if( strncmp(options.c_str(),"FILE",4) == 0 )
108 : {
109 1 : if( strncmp(options.c_str(),"FILENOCREATE",12) == 0 )
110 0 : nocreate = true;
111 1 : else if( strncmp(options.c_str(),"FILELINK",8) == 0 )
112 : {
113 0 : nocreate = true;
114 0 : externallink = true;
115 : }
116 1 : interleaving = "FILE";
117 : }
118 : else
119 : ThrowPCIDSKException( "PCIDSK::Create() options '%s' not recognised.",
120 0 : options.c_str() );
121 :
122 54 : if( strstr(options.c_str(),"NOZERO") != NULL )
123 0 : nozero = true;
124 :
125 : /* -------------------------------------------------------------------- */
126 : /* Validate the channel types. */
127 : /* -------------------------------------------------------------------- */
128 54 : int channels[7] = {0,0,0,0,0,0,0};
129 : int chan_index;
130 54 : bool regular = true;
131 :
132 145 : for( chan_index=0; chan_index < channel_count; chan_index++ )
133 : {
134 171 : if( chan_index > 0
135 40 : && ((int) channel_types[chan_index])
136 40 : < ((int) channel_types[chan_index-1]) )
137 0 : regular = false;
138 :
139 91 : channels[((int) channel_types[chan_index])]++;
140 : }
141 :
142 54 : if( !regular && strcmp(interleaving,"FILE") != 0 )
143 : {
144 : ThrowPCIDSKException(
145 : "Requested mixture of band types not supported for interleaving=%s.",
146 0 : interleaving );
147 : }
148 :
149 : /* -------------------------------------------------------------------- */
150 : /* Create the file. */
151 : /* -------------------------------------------------------------------- */
152 56 : void *io_handle = interfaces->io->Open( filename, "w+" );
153 :
154 52 : assert( io_handle != NULL );
155 :
156 : /* ==================================================================== */
157 : /* Establish some key file layout information. */
158 : /* ==================================================================== */
159 52 : int image_header_start = 1; // in blocks
160 52 : uint64 image_data_start, image_data_size=0; // in blocks
161 52 : uint64 segment_ptr_start, segment_ptr_size=64; // in blocks
162 : int pixel_group_size, line_size; // in bytes
163 52 : int image_header_count = channel_count;
164 :
165 : /* -------------------------------------------------------------------- */
166 : /* Pixel interleaved. */
167 : /* -------------------------------------------------------------------- */
168 52 : if( strcmp(interleaving,"PIXEL") == 0 )
169 : {
170 : pixel_group_size =
171 0 : channels[0] + // CHN_8U
172 0 : channels[1] * DataTypeSize(CHN_16U) +
173 0 : channels[2] * DataTypeSize(CHN_16S) +
174 0 : channels[3] * DataTypeSize(CHN_32R) +
175 0 : channels[4] * DataTypeSize(CHN_C16U) +
176 0 : channels[5] * DataTypeSize(CHN_C16S) +
177 0 : channels[6] * DataTypeSize(CHN_C32R);
178 : //channels[0] + channels[1]*2 + channels[2]*2 + channels[3]*4;
179 0 : line_size = ((pixel_group_size * pixels + 511) / 512) * 512;
180 0 : image_data_size = (((uint64)line_size) * lines) / 512;
181 :
182 : // TODO: Old code enforces a 1TB limit for some reason.
183 : }
184 :
185 : /* -------------------------------------------------------------------- */
186 : /* Band interleaved. */
187 : /* -------------------------------------------------------------------- */
188 52 : else if( strcmp(interleaving,"BAND") == 0 )
189 : {
190 : pixel_group_size =
191 48 : channels[0] + // CHN_8U
192 48 : channels[1] * DataTypeSize(CHN_16U) +
193 48 : channels[2] * DataTypeSize(CHN_16S) +
194 48 : channels[3] * DataTypeSize(CHN_32R) +
195 48 : channels[4] * DataTypeSize(CHN_C16U) +
196 48 : channels[5] * DataTypeSize(CHN_C16S) +
197 240 : channels[6] * DataTypeSize(CHN_C32R);
198 : // BAND interleaved bands are tightly packed.
199 : image_data_size =
200 48 : (((uint64)pixel_group_size) * pixels * lines + 511) / 512;
201 :
202 : // TODO: Old code enforces a 1TB limit for some reason.
203 : }
204 :
205 : /* -------------------------------------------------------------------- */
206 : /* FILE/Tiled. */
207 : /* -------------------------------------------------------------------- */
208 4 : else if( strcmp(interleaving,"FILE") == 0 )
209 : {
210 : // For some reason we reserve extra space, but only for FILE.
211 4 : if( channel_count < 64 )
212 4 : image_header_count = 64;
213 :
214 4 : image_data_size = 0;
215 :
216 : // TODO: Old code enforces a 1TB limit on the fattest band.
217 : }
218 :
219 : /* -------------------------------------------------------------------- */
220 : /* Place components. */
221 : /* -------------------------------------------------------------------- */
222 52 : segment_ptr_start = image_header_start + image_header_count*2;
223 52 : image_data_start = segment_ptr_start + segment_ptr_size;
224 :
225 : /* ==================================================================== */
226 : /* Prepare the file header. */
227 : /* ==================================================================== */
228 52 : PCIDSKBuffer fh(512);
229 :
230 : char current_time[17];
231 52 : GetCurrentDateTime( current_time );
232 :
233 : // Initialize everything to spaces.
234 52 : fh.Put( "", 0, 512 );
235 :
236 : /* -------------------------------------------------------------------- */
237 : /* File Type, Version, and Size */
238 : /* Notice: we get the first 4 characters from PCIVERSIONAME. */
239 : /* -------------------------------------------------------------------- */
240 : // FH1 - magic format string.
241 52 : fh.Put( "PCIDSK", 0, 8 );
242 :
243 : // FH2 - TODO: Allow caller to pass this in.
244 52 : fh.Put( "SDK V1.0", 8, 8 );
245 :
246 : // FH3 - file size later.
247 52 : fh.Put( (image_data_start + image_data_size), 16, 16 );
248 :
249 : // FH4 - 16 characters reserved - spaces.
250 :
251 : // FH5 - Description
252 52 : fh.Put( filename.c_str(), 48, 64 );
253 :
254 : // FH6 - Facility
255 52 : fh.Put( "PCI Inc., Richmond Hill, Canada", 112, 32 );
256 :
257 : // FH7.1 / FH7.2 - left blank (64+64 bytes @ 144)
258 :
259 : // FH8 Creation date/time
260 52 : fh.Put( current_time, 272, 16 );
261 :
262 : // FH9 Update date/time
263 52 : fh.Put( current_time, 288, 16 );
264 :
265 : /* -------------------------------------------------------------------- */
266 : /* Image Data */
267 : /* -------------------------------------------------------------------- */
268 : // FH10 - start block of image data
269 52 : fh.Put( image_data_start+1, 304, 16 );
270 :
271 : // FH11 - number of blocks of image data.
272 52 : fh.Put( image_data_size, 320, 16 );
273 :
274 : // FH12 - start block of image headers.
275 52 : fh.Put( image_header_start+1, 336, 16 );
276 :
277 : // FH13 - number of blocks of image headers.
278 52 : fh.Put( image_header_count*2, 352, 8);
279 :
280 : // FH14 - interleaving.
281 52 : fh.Put( interleaving, 360, 8);
282 :
283 : // FH15 - reserved - MIXED is for some ancient backwards compatability.
284 52 : fh.Put( "MIXED", 368, 8);
285 :
286 : // FH16 - number of image bands.
287 52 : fh.Put( channel_count, 376, 8 );
288 :
289 : // FH17 - width of image in pixels.
290 52 : fh.Put( pixels, 384, 8 );
291 :
292 : // FH18 - height of image in pixels.
293 52 : fh.Put( lines, 392, 8 );
294 :
295 : // FH19 - pixel ground size interpretation.
296 52 : fh.Put( "METRE", 400, 8 );
297 :
298 : // TODO:
299 : //PrintDouble( fh->XPixelSize, "%16.9f", 1.0 );
300 : //PrintDouble( fh->YPixelSize, "%16.9f", 1.0 );
301 52 : fh.Put( "1.0", 408, 16 );
302 52 : fh.Put( "1.0", 424, 16 );
303 :
304 : /* -------------------------------------------------------------------- */
305 : /* Segment Pointers */
306 : /* -------------------------------------------------------------------- */
307 : // FH22 - start block of segment pointers.
308 52 : fh.Put( segment_ptr_start+1, 440, 16 );
309 :
310 : // fH23 - number of blocks of segment pointers.
311 52 : fh.Put( segment_ptr_size, 456, 8 );
312 :
313 : /* -------------------------------------------------------------------- */
314 : /* Number of different types of Channels */
315 : /* -------------------------------------------------------------------- */
316 : // FH24.1 - 8U bands.
317 52 : fh.Put( channels[0], 464, 4 );
318 :
319 : // FH24.2 - 16S bands.
320 52 : fh.Put( channels[1], 468, 4 );
321 :
322 : // FH24.3 - 16U bands.
323 52 : fh.Put( channels[2], 472, 4 );
324 :
325 : // FH24.4 - 32R bands.
326 52 : fh.Put( channels[3], 476, 4 );
327 :
328 : // FH24.5 - C16U bands
329 52 : fh.Put( channels[4], 480, 4 );
330 :
331 : // FH24.6 - C16S bands
332 52 : fh.Put( channels[5], 484, 4 );
333 :
334 : // FH24.7 - C32R bands
335 52 : fh.Put( channels[6], 488, 4 );
336 :
337 : /* -------------------------------------------------------------------- */
338 : /* Write out the file header. */
339 : /* -------------------------------------------------------------------- */
340 52 : interfaces->io->Write( fh.buffer, 512, 1, io_handle );
341 :
342 : /* ==================================================================== */
343 : /* Write out the image headers. */
344 : /* ==================================================================== */
345 52 : PCIDSKBuffer ih( 1024 );
346 :
347 52 : ih.Put( " ", 0, 1024 );
348 :
349 : // IHi.1 - Text describing Channel Contents
350 52 : ih.Put( "Contents Not Specified", 0, 64 );
351 :
352 : // IHi.2 - Filename storing image.
353 52 : if( strncmp(interleaving,"FILE",4) == 0 )
354 4 : ih.Put( "<unintialized>", 64, 64 );
355 :
356 52 : if( externallink )
357 : {
358 : // IHi.6.7 - IHi.6.10
359 0 : ih.Put( 0, 250, 8 );
360 0 : ih.Put( 0, 258, 8 );
361 0 : ih.Put( pixels, 266, 8 );
362 0 : ih.Put( lines, 274, 8 );
363 : }
364 :
365 : // IHi.3 - Creation time and date.
366 52 : ih.Put( current_time, 128, 16 );
367 :
368 : // IHi.4 - Creation time and date.
369 52 : ih.Put( current_time, 144, 16 );
370 :
371 52 : interfaces->io->Seek( io_handle, image_header_start*512, SEEK_SET );
372 :
373 141 : for( chan_index = 0; chan_index < channel_count; chan_index++ )
374 : {
375 89 : ih.Put(DataTypeName(channel_types[chan_index]).c_str(), 160, 8);
376 :
377 89 : if( strncmp("TILED",options.c_str(),5) == 0 )
378 : {
379 : char sis_filename[65];
380 3 : sprintf( sis_filename, "/SIS=%d", chan_index );
381 3 : ih.Put( sis_filename, 64, 64 );
382 :
383 : // IHi.6.7 - IHi.6.10
384 3 : ih.Put( 0, 250, 8 );
385 3 : ih.Put( 0, 258, 8 );
386 3 : ih.Put( pixels, 266, 8 );
387 3 : ih.Put( lines, 274, 8 );
388 :
389 : // IHi.6.11
390 3 : ih.Put( 1, 282, 8 );
391 : }
392 :
393 89 : interfaces->io->Write( ih.buffer, 1024, 1, io_handle );
394 : }
395 :
396 304 : for( chan_index = channel_count;
397 : chan_index < image_header_count;
398 : chan_index++ )
399 : {
400 252 : ih.Put( "", 160, 8 );
401 252 : ih.Put( "<unintialized>", 64, 64 );
402 252 : ih.Put( "", 250, 40 );
403 :
404 252 : interfaces->io->Write( ih.buffer, 1024, 1, io_handle );
405 : }
406 :
407 : /* ==================================================================== */
408 : /* Write out the segment pointers, all spaces. */
409 : /* ==================================================================== */
410 52 : PCIDSKBuffer segment_pointers( (int) (segment_ptr_size*512) );
411 52 : segment_pointers.Put( " ", 0, (int) (segment_ptr_size*512) );
412 :
413 52 : interfaces->io->Seek( io_handle, segment_ptr_start*512, SEEK_SET );
414 : interfaces->io->Write( segment_pointers.buffer, segment_ptr_size, 512,
415 52 : io_handle );
416 :
417 : /* -------------------------------------------------------------------- */
418 : /* Ensure we write out something at the end of the image data */
419 : /* to force the file size. */
420 : /* -------------------------------------------------------------------- */
421 52 : if( image_data_size > 0 )
422 : {
423 : interfaces->io->Seek( io_handle, (image_data_start + image_data_size)*512-1,
424 45 : SEEK_SET );
425 45 : interfaces->io->Write( "\0", 1, 1, io_handle );
426 : }
427 :
428 : /* -------------------------------------------------------------------- */
429 : /* Close the raw file, and reopen as a pcidsk file. */
430 : /* -------------------------------------------------------------------- */
431 52 : interfaces->io->Close( io_handle );
432 :
433 52 : PCIDSKFile *file = Open( filename, "r+", interfaces );
434 :
435 : /* -------------------------------------------------------------------- */
436 : /* Create a default georeferencing segment. */
437 : /* -------------------------------------------------------------------- */
438 : file->CreateSegment( "GEOref",
439 : "Master Georeferencing Segment for File",
440 104 : SEG_GEO, 6 );
441 :
442 : /* -------------------------------------------------------------------- */
443 : /* If the dataset is tiled, create the file band data. */
444 : /* -------------------------------------------------------------------- */
445 104 : if( strncmp(options.c_str(),"TILED",5) == 0 )
446 : {
447 3 : file->SetMetadataValue( "_DBLayout", options );
448 :
449 : // For sizing the SysBMDir we want an approximate size of the
450 : // the imagery.
451 : uint64 rough_image_size =
452 3 : (channels[0] + // CHN_8U
453 3 : channels[1] * DataTypeSize(CHN_16U) +
454 3 : channels[2] * DataTypeSize(CHN_16S) +
455 3 : channels[3] * DataTypeSize(CHN_32R) +
456 3 : channels[4] * DataTypeSize(CHN_C16U) +
457 3 : channels[5] * DataTypeSize(CHN_C16S) +
458 3 : channels[6] * DataTypeSize(CHN_C32R))
459 21 : * (pixels * (uint64) lines);
460 3 : uint64 sysbmdir_size = ((rough_image_size / 8192) * 28) / 512;
461 :
462 3 : sysbmdir_size = (int) (sysbmdir_size * 1.1 + 100);
463 : int segment = file->CreateSegment( "SysBMDir",
464 : "System Block Map Directory - Do not modify.",
465 3 : SEG_SYS, sysbmdir_size );
466 :
467 : SysBlockMap *bm =
468 6 : dynamic_cast<SysBlockMap *>(file->GetSegment( segment ));
469 :
470 6 : for( chan_index = 0; chan_index < channel_count; chan_index++ )
471 : {
472 : bm->CreateVirtualImageFile( pixels, lines, blocksize, blocksize,
473 : channel_types[chan_index],
474 3 : compression );
475 : }
476 : }
477 :
478 : /* -------------------------------------------------------------------- */
479 : /* If we have a non-tiled FILE interleaved file, should we */
480 : /* create external band files now? */
481 : /* -------------------------------------------------------------------- */
482 52 : if( strncmp(interleaving,"FILE",4) == 0
483 : && strncmp(options.c_str(),"TILED",5) != 0
484 : && !nocreate )
485 : {
486 2 : for( chan_index = 0; chan_index < channel_count; chan_index++ )
487 : {
488 1 : PCIDSKChannel *channel = file->GetChannel( chan_index + 1 );
489 1 : int pixel_size = DataTypeSize(channel->GetType());
490 :
491 : // build a band filename that uses the basename of the PCIDSK
492 : // file, and adds ".nnn" based on the band.
493 1 : std::string band_filename = filename;
494 : char ext[5];
495 1 : sprintf( ext, ".%03d", chan_index+1 );
496 :
497 1 : size_t last_dot = band_filename.find_last_of(".");
498 1 : if( last_dot != std::string::npos
499 : && (band_filename.find_last_of("/\\:") == std::string::npos
500 : || band_filename.find_last_of("/\\:") < last_dot) )
501 : {
502 1 : band_filename.resize( last_dot );
503 : }
504 :
505 1 : band_filename += ext;
506 :
507 : // Now build a version without a path.
508 1 : std::string relative_band_filename;
509 1 : size_t path_div = band_filename.find_last_of( "/\\:" );
510 1 : if( path_div == std::string::npos )
511 0 : relative_band_filename = band_filename;
512 : else
513 1 : relative_band_filename = band_filename.c_str() + path_div + 1;
514 :
515 : // create the file - ought we write the whole file?
516 1 : void *band_io_handle = interfaces->io->Open( band_filename, "w" );
517 1 : interfaces->io->Write( "\0", 1, 1, band_io_handle );
518 1 : interfaces->io->Close( band_io_handle );
519 :
520 : // Set the channel header information.
521 : channel->SetChanInfo( relative_band_filename, 0, pixel_size,
522 1 : pixel_size * pixels, true );
523 : }
524 : }
525 :
526 52 : return file;
527 : }
|