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