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 option 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 : PCIDSK::Create( std::string filename, int pixels, int lines,
64 : int channel_count, eChanType *channel_types,
65 44 : std::string options, const PCIDSKInterfaces *interfaces )
66 :
67 : {
68 : /* -------------------------------------------------------------------- */
69 : /* Use default interfaces if none are passed in. */
70 : /* -------------------------------------------------------------------- */
71 44 : PCIDSKInterfaces default_interfaces;
72 44 : if( interfaces == NULL )
73 0 : interfaces = &default_interfaces;
74 :
75 : /* -------------------------------------------------------------------- */
76 : /* Default the channel types to all 8U if not provided. */
77 : /* -------------------------------------------------------------------- */
78 44 : std::vector<eChanType> default_channel_types;
79 :
80 44 : if( channel_types == NULL )
81 : {
82 0 : default_channel_types.resize( channel_count+1, CHN_8U );
83 0 : channel_types = &(default_channel_types[0]);
84 : }
85 :
86 : /* -------------------------------------------------------------------- */
87 : /* Validate parameters. */
88 : /* -------------------------------------------------------------------- */
89 : const char *interleaving;
90 44 : std::string compression = "NONE";
91 44 : bool nozero = false;
92 44 : int blocksize = 127;
93 :
94 44 : UCaseStr( options );
95 :
96 44 : if(strncmp(options.c_str(),"PIXEL",5) == 0 )
97 0 : interleaving = "PIXEL";
98 44 : else if( strncmp(options.c_str(),"BAND",4) == 0 )
99 44 : interleaving = "BAND";
100 0 : else if( strncmp(options.c_str(),"TILED",5) == 0 )
101 : {
102 0 : interleaving = "FILE";
103 0 : ParseTileFormat( options, blocksize, compression );
104 : }
105 0 : else if( strncmp(options.c_str(),"FILE",4) == 0 )
106 0 : interleaving = "FILE";
107 : else
108 : ThrowPCIDSKException( "PCIDSK::Create() options '%s' not recognised.",
109 0 : options.c_str() );
110 :
111 44 : if( strstr(options.c_str(),"NOZERO") != NULL )
112 0 : nozero = true;
113 :
114 : /* -------------------------------------------------------------------- */
115 : /* Validate the channel types. */
116 : /* -------------------------------------------------------------------- */
117 44 : int channels[7] = {0,0,0,0,0,0,0};
118 : int chan_index;
119 44 : bool regular = true;
120 :
121 129 : for( chan_index=0; chan_index < channel_count; chan_index++ )
122 : {
123 85 : if( chan_index > 0
124 : && ((int) channel_types[chan_index])
125 : < ((int) channel_types[chan_index-1]) )
126 0 : regular = false;
127 :
128 85 : channels[((int) channel_types[chan_index])]++;
129 : }
130 :
131 44 : if( !regular && strcmp(interleaving,"FILE") != 0 )
132 : {
133 : ThrowPCIDSKException(
134 : "Requested mixture of band types not supported for interleaving=%s.",
135 0 : interleaving );
136 : }
137 :
138 : /* -------------------------------------------------------------------- */
139 : /* Create the file. */
140 : /* -------------------------------------------------------------------- */
141 44 : void *io_handle = interfaces->io->Open( filename, "w+" );
142 :
143 44 : assert( io_handle != NULL );
144 :
145 : /* ==================================================================== */
146 : /* Establish some key file layout information. */
147 : /* ==================================================================== */
148 44 : int image_header_start = 1; // in blocks
149 : uint64 image_data_start, image_data_size; // in blocks
150 44 : uint64 segment_ptr_start, segment_ptr_size=64; // in blocks
151 : int pixel_group_size, line_size; // in bytes
152 44 : int image_header_count = channel_count;
153 :
154 : /* -------------------------------------------------------------------- */
155 : /* Pixel interleaved. */
156 : /* -------------------------------------------------------------------- */
157 44 : if( strcmp(interleaving,"PIXEL") == 0 )
158 : {
159 : pixel_group_size =
160 : channels[0] + // CHN_8U
161 : channels[1] * DataTypeSize(CHN_16U) +
162 : channels[2] * DataTypeSize(CHN_16S) +
163 : channels[3] * DataTypeSize(CHN_32R) +
164 : channels[4] * DataTypeSize(CHN_C16U) +
165 : channels[5] * DataTypeSize(CHN_C16S) +
166 0 : channels[6] * DataTypeSize(CHN_C32R);
167 : //channels[0] + channels[1]*2 + channels[2]*2 + channels[3]*4;
168 0 : line_size = ((pixel_group_size * pixels + 511) / 512) * 512;
169 0 : image_data_size = (((uint64)line_size) * lines) / 512;
170 :
171 : // TODO: Old code enforces a 1TB limit for some reason.
172 : }
173 :
174 : /* -------------------------------------------------------------------- */
175 : /* Band interleaved. */
176 : /* -------------------------------------------------------------------- */
177 44 : else if( strcmp(interleaving,"BAND") == 0 )
178 : {
179 : pixel_group_size =
180 : channels[0] + // CHN_8U
181 : channels[1] * DataTypeSize(CHN_16U) +
182 : channels[2] * DataTypeSize(CHN_16S) +
183 : channels[3] * DataTypeSize(CHN_32R) +
184 : channels[4] * DataTypeSize(CHN_C16U) +
185 : channels[5] * DataTypeSize(CHN_C16S) +
186 44 : channels[6] * DataTypeSize(CHN_C32R);
187 : // BAND interleaved bands are tightly packed.
188 : image_data_size =
189 44 : (((uint64)pixel_group_size) * pixels * lines + 511) / 512;
190 :
191 : // TODO: Old code enforces a 1TB limit for some reason.
192 : }
193 :
194 : /* -------------------------------------------------------------------- */
195 : /* FILE/Tiled. */
196 : /* -------------------------------------------------------------------- */
197 0 : else if( strcmp(interleaving,"FILE") == 0 )
198 : {
199 : // For some reason we reserve extra space, but only for FILE.
200 0 : if( channel_count < 64 )
201 0 : image_header_count = 64;
202 :
203 0 : image_data_size = 0;
204 :
205 : // TODO: Old code enforces a 1TB limit on the fattest band.
206 : }
207 :
208 : /* -------------------------------------------------------------------- */
209 : /* Place components. */
210 : /* -------------------------------------------------------------------- */
211 44 : segment_ptr_start = image_header_start + image_header_count*2;
212 44 : image_data_start = segment_ptr_start + segment_ptr_size;
213 :
214 : /* ==================================================================== */
215 : /* Prepare the file header. */
216 : /* ==================================================================== */
217 44 : PCIDSKBuffer fh(512);
218 :
219 : char current_time[17];
220 44 : GetCurrentDateTime( current_time );
221 :
222 : // Initialize everything to spaces.
223 44 : fh.Put( "", 0, 512 );
224 :
225 : /* -------------------------------------------------------------------- */
226 : /* File Type, Version, and Size */
227 : /* Notice: we get the first 4 characters from PCIVERSIONAME. */
228 : /* -------------------------------------------------------------------- */
229 : // FH1 - magic format string.
230 44 : fh.Put( "PCIDSK", 0, 8 );
231 :
232 : // FH2 - TODO: Allow caller to pass this in.
233 44 : fh.Put( "SDK V1.0", 8, 8 );
234 :
235 : // FH3 - file size later.
236 44 : fh.Put( (image_data_start + image_data_size), 16, 16 );
237 :
238 : // FH4 - 16 characters reserved - spaces.
239 :
240 : // FH5 - Description
241 44 : fh.Put( filename.c_str(), 48, 64 );
242 :
243 : // FH6 - Facility
244 44 : fh.Put( "PCI Inc., Richmond Hill, Canada", 112, 32 );
245 :
246 : // FH7.1 / FH7.2 - left blank (64+64 bytes @ 144)
247 :
248 : // FH8 Creation date/time
249 44 : fh.Put( current_time, 272, 16 );
250 :
251 : // FH9 Update date/time
252 44 : fh.Put( current_time, 288, 16 );
253 :
254 : /* -------------------------------------------------------------------- */
255 : /* Image Data */
256 : /* -------------------------------------------------------------------- */
257 : // FH10 - start block of image data
258 44 : fh.Put( image_data_start+1, 304, 16 );
259 :
260 : // FH11 - number of blocks of image data.
261 44 : fh.Put( image_data_size, 320, 16 );
262 :
263 : // FH12 - start block of image headers.
264 44 : fh.Put( image_header_start+1, 336, 16 );
265 :
266 : // FH13 - number of blocks of image headers.
267 44 : fh.Put( image_header_count*2, 352, 8);
268 :
269 : // FH14 - interleaving.
270 44 : fh.Put( interleaving, 360, 8);
271 :
272 : // FH15 - reserved - MIXED is for some ancient backwards compatability.
273 44 : fh.Put( "MIXED", 368, 8);
274 :
275 : // FH16 - number of image bands.
276 44 : fh.Put( channel_count, 376, 8 );
277 :
278 : // FH17 - width of image in pixels.
279 44 : fh.Put( pixels, 384, 8 );
280 :
281 : // FH18 - height of image in pixels.
282 44 : fh.Put( lines, 392, 8 );
283 :
284 : // FH19 - pixel ground size interpretation.
285 44 : fh.Put( "METRE", 400, 8 );
286 :
287 : // TODO:
288 : //PrintDouble( fh->XPixelSize, "%16.9f", 1.0 );
289 : //PrintDouble( fh->YPixelSize, "%16.9f", 1.0 );
290 44 : fh.Put( "1.0", 408, 16 );
291 44 : fh.Put( "1.0", 424, 16 );
292 :
293 : /* -------------------------------------------------------------------- */
294 : /* Segment Pointers */
295 : /* -------------------------------------------------------------------- */
296 : // FH22 - start block of segment pointers.
297 44 : fh.Put( segment_ptr_start+1, 440, 16 );
298 :
299 : // fH23 - number of blocks of segment pointers.
300 44 : fh.Put( segment_ptr_size, 456, 8 );
301 :
302 : /* -------------------------------------------------------------------- */
303 : /* Number of different types of Channels */
304 : /* -------------------------------------------------------------------- */
305 : // FH24.1 - 8U bands.
306 44 : fh.Put( channels[0], 464, 4 );
307 :
308 : // FH24.2 - 16S bands.
309 44 : fh.Put( channels[1], 468, 4 );
310 :
311 : // FH24.3 - 16U bands.
312 44 : fh.Put( channels[2], 472, 4 );
313 :
314 : // FH24.4 - 32R bands.
315 44 : fh.Put( channels[3], 476, 4 );
316 :
317 : // FH24.5 - C16U bands
318 44 : fh.Put( channels[4], 480, 4 );
319 :
320 : // FH24.6 - C16S bands
321 44 : fh.Put( channels[5], 484, 4 );
322 :
323 : // FH24.7 - C32R bands
324 44 : fh.Put( channels[6], 488, 4 );
325 :
326 : /* -------------------------------------------------------------------- */
327 : /* Write out the file header. */
328 : /* -------------------------------------------------------------------- */
329 44 : interfaces->io->Write( fh.buffer, 512, 1, io_handle );
330 :
331 : /* ==================================================================== */
332 : /* Write out the image headers. */
333 : /* ==================================================================== */
334 44 : PCIDSKBuffer ih( 1024 );
335 :
336 44 : ih.Put( " ", 0, 1024 );
337 :
338 : // IHi.1 - Text describing Channel Contents
339 44 : ih.Put( "Contents Not Specified", 0, 64 );
340 :
341 : // IHi.2 - Filename storing image.
342 44 : if( strncmp(interleaving,"FILE",4) == 0 )
343 0 : ih.Put( "<unintialized>", 64, 64 );
344 :
345 : // IHi.3 - Creation time and date.
346 44 : ih.Put( current_time, 128, 16 );
347 :
348 : // IHi.4 - Creation time and date.
349 44 : ih.Put( current_time, 144, 16 );
350 :
351 44 : interfaces->io->Seek( io_handle, image_header_start*512, SEEK_SET );
352 :
353 129 : for( chan_index = 0; chan_index < channel_count; chan_index++ )
354 : {
355 85 : ih.Put(DataTypeName(channel_types[chan_index]).c_str(), 160, 8);
356 :
357 85 : if( strncmp("TILED",options.c_str(),5) == 0 )
358 : {
359 : char sis_filename[65];
360 0 : sprintf( sis_filename, "/SIS=%d", chan_index );
361 0 : ih.Put( sis_filename, 64, 64 );
362 :
363 : // IHi.6.7 - IHi.6.10
364 0 : ih.Put( 0, 250, 8 );
365 0 : ih.Put( 0, 258, 8 );
366 0 : ih.Put( pixels, 266, 8 );
367 0 : ih.Put( lines, 274, 8 );
368 :
369 : // IHi.6.11
370 0 : ih.Put( 1, 282, 8 );
371 : }
372 :
373 85 : interfaces->io->Write( ih.buffer, 1024, 1, io_handle );
374 : }
375 :
376 44 : for( chan_index = channel_count;
377 : chan_index < image_header_count;
378 : chan_index++ )
379 : {
380 0 : ih.Put( "", 160, 8 );
381 0 : ih.Put( "<unintialized>", 64, 64 );
382 0 : ih.Put( "", 250, 40 );
383 :
384 0 : interfaces->io->Write( ih.buffer, 1024, 1, io_handle );
385 : }
386 :
387 : /* ==================================================================== */
388 : /* Write out the segment pointers, all spaces. */
389 : /* ==================================================================== */
390 44 : PCIDSKBuffer segment_pointers( segment_ptr_size*512 );
391 44 : segment_pointers.Put( " ", 0, segment_ptr_size*512 );
392 :
393 44 : interfaces->io->Seek( io_handle, segment_ptr_start*512, SEEK_SET );
394 : interfaces->io->Write( segment_pointers.buffer, segment_ptr_size, 512,
395 44 : io_handle );
396 :
397 : /* -------------------------------------------------------------------- */
398 : /* Ensure we write out something at the end of the image data */
399 : /* to force the file size. */
400 : /* -------------------------------------------------------------------- */
401 44 : if( image_data_size > 0 )
402 : {
403 : interfaces->io->Seek( io_handle, (image_data_start + image_data_size)*512-1,
404 43 : SEEK_SET );
405 43 : interfaces->io->Write( "\0", 1, 1, io_handle );
406 : }
407 :
408 : /* -------------------------------------------------------------------- */
409 : /* Close the raw file, and reopen as a pcidsk file. */
410 : /* -------------------------------------------------------------------- */
411 44 : interfaces->io->Close( io_handle );
412 :
413 44 : PCIDSKFile *file = Open( filename, "r+", interfaces );
414 :
415 : /* -------------------------------------------------------------------- */
416 : /* Create a default georeferencing segment. */
417 : /* -------------------------------------------------------------------- */
418 : file->CreateSegment( "GEOref",
419 : "Master Georeferencing Segment for File",
420 88 : SEG_GEO, 6 );
421 :
422 : /* -------------------------------------------------------------------- */
423 : /* If the dataset is tiled, create the file band data. */
424 : /* -------------------------------------------------------------------- */
425 88 : if( strncmp(options.c_str(),"TILED",5) == 0 )
426 : {
427 0 : file->SetMetadataValue( "_DBLayout", options );
428 :
429 : int segment = file->CreateSegment( "SysBMDir",
430 : "System Block Map Directory - Do not modify.",
431 0 : SEG_SYS, 0 );
432 :
433 : SysBlockMap *bm =
434 0 : dynamic_cast<SysBlockMap *>(file->GetSegment( segment ));
435 :
436 0 : for( chan_index = 0; chan_index < channel_count; chan_index++ )
437 : {
438 : bm->CreateVirtualImageFile( pixels, lines, blocksize, blocksize,
439 : channel_types[chan_index],
440 0 : compression );
441 : }
442 : }
443 :
444 44 : return file;
445 : }
|