1 : /******************************************************************************
2 : * $Id: ecwcreatecopy.cpp 17906 2009-10-26 19:47:21Z rouault $
3 : *
4 : * Project: GDAL ECW Driver
5 : * Purpose: ECW CreateCopy method implementation.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2001, 2004, Frank Warmerdam <warmerdam@pobox.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "gdal_pam.h"
31 : #include "ogr_spatialref.h"
32 : #include "cpl_string.h"
33 : #include "cpl_conv.h"
34 : #include "vsiiostream.h"
35 : #include "jp2userbox.h"
36 : #include "gdaljp2metadata.h"
37 :
38 : CPL_CVSID("$Id: ecwcreatecopy.cpp 17906 2009-10-26 19:47:21Z rouault $");
39 :
40 : CPL_C_START
41 : CPLErr CPL_DLL GTIFMemBufFromWkt( const char *pszWKT,
42 : const double *padfGeoTransform,
43 : int nGCPCount, const GDAL_GCP *pasGCPList,
44 : int *pnSize, unsigned char **ppabyBuffer );
45 : CPL_C_END
46 :
47 : void ECWInitialize( void );
48 :
49 : GDALDataset* ECWDatasetOpenJPEG2000(GDALOpenInfo* poOpenInfo);
50 :
51 : #if defined(FRMT_ecw) && defined(HAVE_COMPRESS)
52 :
53 : class GDALECWCompressor : public CNCSFile {
54 :
55 : public:
56 : GDALECWCompressor();
57 : virtual ~GDALECWCompressor();
58 :
59 : virtual CNCSError WriteReadLine(UINT32 nNextLine, void **ppInputArray);
60 : virtual void WriteStatus(UINT32 nCurrentLine);
61 : virtual bool WriteCancel();
62 :
63 : CPLErr Initialize( const char *pszFilename, char **papszOptions,
64 : int nXSize, int nYSize, int nBands,
65 : GDALDataType eType,
66 : const char *pszWKT, double *padfGeoTransform,
67 : int nGCPCount, const GDAL_GCP *pasGCPList,
68 : int bIsJPEG2000 );
69 : CPLErr CloseDown();
70 :
71 : CPLErr PrepareCoverageBox( const char *pszWKT, double *padfGeoTransform );
72 : CPLErr WriteJP2Box( GDALJP2Box * );
73 :
74 : #ifdef ECW_FW
75 : CNCSJP2File::CNCSJPXAssocBox m_oGMLAssoc;
76 : #endif
77 :
78 : // Data
79 :
80 : GDALDataset *m_poSrcDS;
81 :
82 : VSIIOStream m_OStream;
83 : int m_nPercentComplete;
84 :
85 : int m_bCancelled;
86 :
87 : GDALProgressFunc pfnProgress;
88 : void *pProgressData;
89 :
90 : NCSFileViewFileInfoEx sFileInfo;
91 : GDALDataType eWorkDT;
92 :
93 : JP2UserBox** papoJP2UserBox;
94 : int nJP2UserBox;
95 : };
96 :
97 : /************************************************************************/
98 : /* GDALECWCompressor() */
99 : /************************************************************************/
100 :
101 33 : GDALECWCompressor::GDALECWCompressor()
102 :
103 : {
104 33 : m_poSrcDS = NULL;
105 33 : m_nPercentComplete = -1;
106 33 : m_bCancelled = FALSE;
107 33 : pfnProgress = GDALDummyProgress;
108 33 : pProgressData = NULL;
109 33 : papoJP2UserBox = NULL;
110 33 : nJP2UserBox = 0;
111 33 : }
112 :
113 : /************************************************************************/
114 : /* ~GDALECWCompressor() */
115 : /************************************************************************/
116 :
117 33 : GDALECWCompressor::~GDALECWCompressor()
118 :
119 : {
120 : int i;
121 65 : for(i=0;i<nJP2UserBox;i++)
122 32 : delete papoJP2UserBox[i];
123 33 : CPLFree(papoJP2UserBox);
124 33 : }
125 :
126 : /************************************************************************/
127 : /* CloseDown() */
128 : /************************************************************************/
129 :
130 17 : CPLErr GDALECWCompressor::CloseDown()
131 :
132 : {
133 52 : for( int i = 0; i < sFileInfo.nBands; i++ )
134 : {
135 35 : CPLFree( sFileInfo.pBands[i].szDesc );
136 : }
137 17 : CPLFree( sFileInfo.pBands );
138 :
139 17 : Close( true );
140 :
141 17 : return CE_None;
142 : }
143 :
144 : /************************************************************************/
145 : /* WriteReadLine() */
146 : /************************************************************************/
147 :
148 640 : CNCSError GDALECWCompressor::WriteReadLine( UINT32 nNextLine,
149 : void **ppInputArray )
150 :
151 : {
152 : int iBand, *panBandMap;
153 : CPLErr eErr;
154 : GByte *pabyLineBuf;
155 640 : int nWordSize = GDALGetDataTypeSize( eWorkDT ) / 8;
156 :
157 640 : panBandMap = (int *) CPLMalloc(sizeof(int) * sFileInfo.nBands);
158 2350 : for( iBand = 0; iBand < sFileInfo.nBands; iBand++ )
159 1710 : panBandMap[iBand] = iBand+1;
160 :
161 : pabyLineBuf = (GByte *) CPLMalloc( sFileInfo.nSizeX * sFileInfo.nBands
162 640 : * nWordSize );
163 :
164 : eErr = m_poSrcDS->RasterIO( GF_Read, 0, nNextLine, sFileInfo.nSizeX, 1,
165 : pabyLineBuf, sFileInfo.nSizeX, 1,
166 : eWorkDT,
167 : sFileInfo.nBands, panBandMap,
168 640 : nWordSize, 0, nWordSize * sFileInfo.nSizeX );
169 :
170 2350 : for( iBand = 0; iBand < (int) sFileInfo.nBands; iBand++ )
171 : {
172 : memcpy( ppInputArray[iBand],
173 : pabyLineBuf + nWordSize * sFileInfo.nSizeX * iBand,
174 1710 : nWordSize * sFileInfo.nSizeX );
175 : }
176 :
177 640 : CPLFree( pabyLineBuf );
178 640 : CPLFree( panBandMap );
179 :
180 640 : if( eErr == CE_None )
181 640 : return NCS_SUCCESS;
182 : else
183 0 : return NCS_FILEIO_ERROR;
184 : }
185 :
186 : /************************************************************************/
187 : /* WriteStatus() */
188 : /************************************************************************/
189 :
190 640 : void GDALECWCompressor::WriteStatus( UINT32 nCurrentLine )
191 :
192 : {
193 : m_bCancelled =
194 : !pfnProgress( nCurrentLine / (float) sFileInfo.nSizeY,
195 640 : NULL, pProgressData );
196 640 : }
197 :
198 : /************************************************************************/
199 : /* WriteCancel() */
200 : /************************************************************************/
201 :
202 640 : bool GDALECWCompressor::WriteCancel()
203 :
204 : {
205 640 : return (bool) m_bCancelled;
206 : }
207 :
208 : /************************************************************************/
209 : /* PrepareCoverageBox() */
210 : /************************************************************************/
211 :
212 0 : CPLErr GDALECWCompressor::PrepareCoverageBox( const char *pszWKT,
213 : double *padfGeoTransform )
214 :
215 : {
216 : #ifndef ECW_FW
217 0 : return CE_Failure;
218 : #else
219 : /* -------------------------------------------------------------------- */
220 : /* Try do determine a PCS or GCS code we can use. */
221 : /* -------------------------------------------------------------------- */
222 : OGRSpatialReference oSRS;
223 : char *pszWKTCopy = (char *) pszWKT;
224 : int nEPSGCode = 0;
225 : char szSRSName[100];
226 :
227 : if( oSRS.importFromWkt( &pszWKTCopy ) != OGRERR_NONE )
228 : return CE_Failure;
229 :
230 : if( oSRS.IsProjected() )
231 : {
232 : const char *pszAuthName = oSRS.GetAuthorityName( "PROJCS" );
233 :
234 : if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") )
235 : {
236 : nEPSGCode = atoi(oSRS.GetAuthorityCode( "PROJCS" ));
237 : }
238 : }
239 : else if( oSRS.IsGeographic() )
240 : {
241 : const char *pszAuthName = oSRS.GetAuthorityName( "GEOGCS" );
242 :
243 : if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") )
244 : {
245 : nEPSGCode = atoi(oSRS.GetAuthorityCode( "GEOGCS" ));
246 : }
247 : }
248 :
249 : if( nEPSGCode != 0 )
250 : sprintf( szSRSName, "urn:ogc:def:crs:EPSG::%d", nEPSGCode );
251 : else
252 : strcpy( szSRSName,
253 : "gmljp2://xml/CRSDictionary.gml#ogrcrs1" );
254 :
255 : /* -------------------------------------------------------------------- */
256 : /* For now we hardcode for a minimal instance format. */
257 : /* -------------------------------------------------------------------- */
258 : char szDoc[4000];
259 :
260 : sprintf( szDoc,
261 : "<gml:FeatureCollection\n"
262 : " xmlns:gml=\"http://www.opengis.net/gml\"\n"
263 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
264 : " xsi:schemaLocation=\"http://www.opengis.net/gml http://www.math.ubc.ca/~burggraf/gml/gml4jp2.xsd\">\n"
265 : " <gml:boundedBy>\n"
266 : " <gml:Null>withheld</gml:Null>\n"
267 : " </gml:boundedBy>\n"
268 : " <gml:featureMember>\n"
269 : " <gml:FeatureCollection>\n"
270 : " <gml:featureMember>\n"
271 : " <gml:RectifiedGridCoverage dimension=\"2\" gml:id=\"RGC0001\">\n"
272 : " <gml:rectifiedGridDomain>\n"
273 : " <gml:RectifiedGrid dimension=\"2\">\n"
274 : " <gml:limits>\n"
275 : " <gml:GridEnvelope>\n"
276 : " <gml:low>0 0</gml:low>\n"
277 : " <gml:high>%d %d</gml:high>\n"
278 : " </gml:GridEnvelope>\n"
279 : " </gml:limits>\n"
280 : " <gml:axisName>x</gml:axisName>\n"
281 : " <gml:axisName>y</gml:axisName>\n"
282 : " <gml:origin>\n"
283 : " <gml:Point gml:id=\"P0001\" srsName=\"%s\">\n"
284 : " <gml:pos>%.15g %.15g</gml:pos>\n"
285 : " </gml:Point>\n"
286 : " </gml:origin>\n"
287 : " <gml:offsetVector srsName=\"%s\">%.15g %.15g</gml:offsetVector>\n"
288 : " <gml:offsetVector srsName=\"%s\">%.15g %.15g</gml:offsetVector>\n"
289 : " </gml:RectifiedGrid>\n"
290 : " </gml:rectifiedGridDomain>\n"
291 : " <gml:rangeSet>\n"
292 : " <gml:File>\n"
293 : " <gml:fileName>urn:ogc:tc:gmljp2:codestream:0</gml:fileName>\n"
294 : " <gml:fileStructure>Record Interleaved</gml:fileStructure>\n"
295 : " </gml:File>\n"
296 : " </gml:rangeSet>\n"
297 : " </gml:RectifiedGridCoverage>\n"
298 : " </gml:featureMember>\n"
299 : " </gml:FeatureCollection>\n"
300 : " </gml:featureMember>\n"
301 : "</gml:FeatureCollection>\n",
302 : sFileInfo.nSizeX-1, sFileInfo.nSizeY-1,
303 : szSRSName,
304 : padfGeoTransform[0] + padfGeoTransform[1] * 0.5
305 : + padfGeoTransform[4] * 0.5,
306 : padfGeoTransform[3] + padfGeoTransform[2] * 0.5
307 : + padfGeoTransform[5] * 0.5,
308 : szSRSName,
309 : padfGeoTransform[1], padfGeoTransform[2],
310 : szSRSName,
311 : padfGeoTransform[4], padfGeoTransform[5] );
312 :
313 : /* -------------------------------------------------------------------- */
314 : /* If we need a user defined CRSDictionary entry, prepare it */
315 : /* here. */
316 : /* -------------------------------------------------------------------- */
317 : char *pszDictBox = NULL;
318 :
319 : if( nEPSGCode == 0 )
320 : {
321 : char *pszGMLDef = NULL;
322 :
323 : if( oSRS.exportToXML( &pszGMLDef, NULL ) == OGRERR_NONE )
324 : {
325 : pszDictBox = (char *) CPLMalloc(strlen(pszGMLDef) + 4000);
326 :
327 : sprintf( pszDictBox,
328 : "<gml:Dictionary gml:id=\"CRSU1\" \n"
329 : " xmlns:gml=\"http://www.opengis.net/gml\"\n"
330 : " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
331 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"
332 : " <gml:dictionaryEntry>\n"
333 : "%s\n"
334 : " </gml:dictionaryEntry>\n"
335 : "</gml:Dictionary>\n",
336 : pszGMLDef );
337 : }
338 : CPLFree( pszGMLDef );
339 : }
340 :
341 : /* -------------------------------------------------------------------- */
342 : /* Setup the various required boxes. */
343 : /* -------------------------------------------------------------------- */
344 : JP2UserBox *poGMLData;
345 : CNCSJP2File::CNCSJPXAssocBox *poAssoc;
346 : CNCSJP2File::CNCSJPXLabelBox *poLabel;
347 :
348 : poLabel = new CNCSJP2File::CNCSJPXLabelBox();
349 : poLabel->SetLabel( "gml.data" );
350 : poLabel->m_bValid = true;
351 : m_oGMLAssoc.m_OtherBoxes.push_back( poLabel );
352 : m_oGMLAssoc.m_OwnedBoxes.push_back( poLabel );
353 :
354 : poAssoc = new CNCSJP2File::CNCSJPXAssocBox();
355 : m_oGMLAssoc.m_OtherBoxes.push_back( poAssoc );
356 : m_oGMLAssoc.m_OwnedBoxes.push_back( poAssoc );
357 : poAssoc->m_bValid = true;
358 :
359 : poLabel = new CNCSJP2File::CNCSJPXLabelBox();
360 : poLabel->SetLabel( "gml.root-instance" );
361 : poLabel->m_bValid = true;
362 : poAssoc->m_OtherBoxes.push_back( poLabel );
363 : poAssoc->m_OwnedBoxes.push_back( poLabel );
364 :
365 : poGMLData = new JP2UserBox();
366 : poGMLData->m_nTBox = 'xml ';
367 : poGMLData->SetData( strlen( szDoc ), (unsigned char *) szDoc );
368 : poAssoc->m_OtherBoxes.push_back( poGMLData );
369 : poAssoc->m_OwnedBoxes.push_back( poGMLData );
370 :
371 : if( pszDictBox != NULL )
372 : {
373 : poAssoc = new CNCSJP2File::CNCSJPXAssocBox();
374 : m_oGMLAssoc.m_OtherBoxes.push_back( poAssoc );
375 : m_oGMLAssoc.m_OwnedBoxes.push_back( poAssoc );
376 : poAssoc->m_bValid = true;
377 :
378 : poLabel = new CNCSJP2File::CNCSJPXLabelBox();
379 : poLabel->SetLabel( "CRSDictionary.gml" );
380 : poLabel->m_bValid = true;
381 : poAssoc->m_OtherBoxes.push_back( poLabel );
382 : poAssoc->m_OwnedBoxes.push_back( poLabel );
383 :
384 : poGMLData = new JP2UserBox();
385 : poGMLData->m_nTBox = 'xml ';
386 : poGMLData->SetData( strlen(pszDictBox),
387 : (unsigned char *) pszDictBox );
388 : poAssoc->m_OtherBoxes.push_back( poGMLData );
389 : poAssoc->m_OwnedBoxes.push_back( poGMLData );
390 :
391 : CPLFree( pszDictBox );
392 : }
393 :
394 : m_oGMLAssoc.m_bValid = true;
395 : AddBox( &m_oGMLAssoc );
396 :
397 : return CE_None;
398 : #endif /* def ECW_FW */
399 : }
400 :
401 : /************************************************************************/
402 : /* WriteJP2Box() */
403 : /************************************************************************/
404 :
405 34 : CPLErr GDALECWCompressor::WriteJP2Box( GDALJP2Box * poBox )
406 :
407 : {
408 : JP2UserBox *poECWBox;
409 :
410 34 : if( poBox == NULL )
411 2 : return CE_None;
412 :
413 32 : poECWBox = new JP2UserBox();
414 32 : memcpy( &(poECWBox->m_nTBox), poBox->GetType(), 4 );
415 :
416 : poECWBox->SetData( poBox->GetDataLength(),
417 32 : poBox->GetWritableData() );
418 :
419 32 : AddBox( poECWBox );
420 :
421 64 : delete poBox;
422 :
423 : papoJP2UserBox =(JP2UserBox**) CPLRealloc(papoJP2UserBox,
424 32 : (nJP2UserBox + 1) * sizeof(JP2UserBox*));
425 32 : papoJP2UserBox[nJP2UserBox] = poECWBox;
426 32 : nJP2UserBox ++;
427 :
428 32 : return CE_None;
429 : }
430 :
431 : /************************************************************************/
432 : /* ECWTranslateFromWKT() */
433 : /************************************************************************/
434 :
435 16 : static int ECWTranslateFromWKT( const char *pszWKT,
436 : char *pszProjection,
437 : int nProjectionLen,
438 : char *pszDatum,
439 : int nDatumLen)
440 :
441 : {
442 16 : OGRSpatialReference oSRS;
443 16 : char *pszWKTIn = (char *) pszWKT;
444 :
445 16 : strcpy( pszProjection, "RAW" );
446 16 : strcpy( pszDatum, "RAW" );
447 :
448 16 : if( pszWKT == NULL || strlen(pszWKT) == 0 )
449 1 : return FALSE;
450 :
451 15 : oSRS.importFromWkt( &pszWKTIn );
452 :
453 15 : if( oSRS.IsLocal() )
454 0 : return TRUE;
455 :
456 : /* -------------------------------------------------------------------- */
457 : /* Do we have an overall EPSG number for this coordinate system? */
458 : /* -------------------------------------------------------------------- */
459 15 : const char *pszAuthorityCode = NULL;
460 15 : const char *pszAuthorityName = NULL;
461 15 : UINT32 nEPSGCode = 0;
462 :
463 15 : if( oSRS.IsProjected() )
464 : {
465 3 : pszAuthorityCode = oSRS.GetAuthorityCode( "PROJCS" );
466 3 : pszAuthorityName = oSRS.GetAuthorityName( "PROJCS" );
467 : }
468 12 : else if( oSRS.IsGeographic() )
469 : {
470 12 : pszAuthorityCode = oSRS.GetAuthorityCode( "GEOGCS" );
471 12 : pszAuthorityName = oSRS.GetAuthorityName( "GEOGCS" );
472 : }
473 :
474 15 : if( pszAuthorityName != NULL && EQUAL(pszAuthorityName,"EPSG")
475 : && pszAuthorityCode != NULL && atoi(pszAuthorityCode) > 0 )
476 15 : nEPSGCode = (UINT32) atoi(pszAuthorityCode);
477 :
478 15 : if( nEPSGCode != 0 )
479 : {
480 15 : char *pszEPSGProj = NULL, *pszEPSGDatum = NULL;
481 15 : CNCSError oErr;
482 :
483 : oErr =
484 : CNCSJP2FileView::GetProjectionAndDatum( atoi(pszAuthorityCode),
485 15 : &pszEPSGProj, &pszEPSGDatum );
486 :
487 : CPLDebug( "ECW", "GetGDTProjDat(%d) = %s/%s",
488 15 : atoi(pszAuthorityCode), pszEPSGProj, pszEPSGDatum );
489 :
490 15 : if( oErr.GetErrorNumber() == NCS_SUCCESS
491 : && pszEPSGProj != NULL && pszEPSGDatum != NULL )
492 : {
493 15 : strncpy( pszProjection, pszEPSGProj, nProjectionLen );
494 15 : strncpy( pszDatum, pszEPSGDatum, nDatumLen );
495 15 : pszProjection[nProjectionLen - 1] = 0;
496 15 : pszDatum[nDatumLen - 1] = 0;
497 15 : NCSFree( pszEPSGProj );
498 15 : NCSFree( pszEPSGDatum );
499 15 : return TRUE;
500 : }
501 :
502 0 : NCSFree( pszEPSGProj );
503 0 : NCSFree( pszEPSGDatum );
504 :
505 : }
506 :
507 : /* -------------------------------------------------------------------- */
508 : /* Fallback to translating based on the ecw_cs.wkt file, and */
509 : /* various jiffy rules. */
510 : /* -------------------------------------------------------------------- */
511 : char szUnits[32];
512 :
513 0 : return oSRS.exportToERM( pszProjection, pszDatum, szUnits ) == OGRERR_NONE;
514 : }
515 :
516 : /************************************************************************/
517 : /* Initialize() */
518 : /* */
519 : /* Initialize compressor output. */
520 : /************************************************************************/
521 :
522 17 : CPLErr GDALECWCompressor::Initialize(
523 : const char *pszFilename, char **papszOptions,
524 : int nXSize, int nYSize, int nBands,
525 : GDALDataType eType,
526 : const char *pszWKT, double *padfGeoTransform,
527 : int nGCPCount, const GDAL_GCP *pasGCPList,
528 : int bIsJPEG2000 )
529 :
530 : {
531 : /* -------------------------------------------------------------------- */
532 : /* Do some rudimentary checking in input. */
533 : /* -------------------------------------------------------------------- */
534 17 : if( nBands == 0 )
535 : {
536 : CPLError( CE_Failure, CPLE_NotSupported,
537 0 : "ECW driver requires at least one band." );
538 0 : return CE_Failure;
539 : }
540 :
541 : /* -------------------------------------------------------------------- */
542 : /* Parse out some known options. */
543 : /* -------------------------------------------------------------------- */
544 17 : float fTargetCompression = 75.0;
545 :
546 17 : if( CSLFetchNameValue(papszOptions, "TARGET") != NULL )
547 : {
548 : fTargetCompression = (float)
549 1 : atof(CSLFetchNameValue(papszOptions, "TARGET"));
550 :
551 1 : if( fTargetCompression < 0.0 || fTargetCompression > 99.0 )
552 : {
553 : CPLError( CE_Failure, CPLE_NotSupported,
554 : "TARGET compression of %.3f invalid, should be a\n"
555 : "value between 0 and 99 percent.\n",
556 0 : (double) fTargetCompression );
557 0 : return CE_Failure;
558 : }
559 : }
560 :
561 : /* -------------------------------------------------------------------- */
562 : /* Create and initialize compressor. */
563 : /* -------------------------------------------------------------------- */
564 17 : NCSFileViewFileInfoEx *psClient = &(sFileInfo);
565 :
566 17 : psClient->nBands = nBands;
567 17 : psClient->nSizeX = nXSize;
568 17 : psClient->nSizeY = nYSize;
569 17 : psClient->nCompressionRate = (int) MAX(1,100 / (100-fTargetCompression));
570 17 : psClient->eCellSizeUnits = ECW_CELL_UNITS_METERS;
571 :
572 17 : if( nBands == 1 )
573 8 : psClient->eColorSpace = NCSCS_GREYSCALE;
574 9 : else if( nBands == 3 )
575 4 : psClient->eColorSpace = NCSCS_sRGB;
576 : else
577 5 : psClient->eColorSpace = NCSCS_MULTIBAND;
578 :
579 : /* -------------------------------------------------------------------- */
580 : /* Figure out the data type. */
581 : /* -------------------------------------------------------------------- */
582 17 : int bSigned = FALSE;
583 17 : int nBits = 8;
584 17 : eWorkDT = eType;
585 :
586 17 : switch( eWorkDT )
587 : {
588 : case GDT_Byte:
589 11 : psClient->eCellType = NCSCT_UINT8;
590 11 : nBits = 8;
591 11 : bSigned = FALSE;
592 11 : break;
593 :
594 : case GDT_UInt16:
595 1 : psClient->eCellType = NCSCT_UINT16;
596 1 : nBits = 16;
597 1 : bSigned = FALSE;
598 1 : break;
599 :
600 : case GDT_UInt32:
601 1 : psClient->eCellType = NCSCT_UINT32;
602 1 : nBits = 32;
603 1 : bSigned = FALSE;
604 1 : break;
605 :
606 : case GDT_Int16:
607 2 : psClient->eCellType = NCSCT_INT16;
608 2 : nBits = 16;
609 2 : bSigned = TRUE;
610 2 : break;
611 :
612 : case GDT_Int32:
613 1 : psClient->eCellType = NCSCT_INT32;
614 1 : nBits = 32;
615 1 : bSigned = TRUE;
616 1 : break;
617 :
618 : case GDT_Float32:
619 1 : psClient->eCellType = NCSCT_IEEE4;
620 1 : nBits = 32;
621 1 : bSigned = TRUE;
622 1 : break;
623 :
624 : case GDT_Float64:
625 0 : psClient->eCellType = NCSCT_IEEE8;
626 0 : nBits = 64;
627 0 : bSigned = TRUE;
628 0 : break;
629 :
630 : default:
631 : // We treat complex types as float.
632 0 : psClient->eCellType = NCSCT_IEEE4;
633 0 : nBits = 32;
634 0 : bSigned = TRUE;
635 0 : eWorkDT = GDT_Float32;
636 : break;
637 : }
638 :
639 : /* -------------------------------------------------------------------- */
640 : /* Create band information structures. */
641 : /* -------------------------------------------------------------------- */
642 : int iBand;
643 :
644 : psClient->pBands = (NCSFileBandInfo *)
645 17 : CPLMalloc( sizeof(NCSFileBandInfo) * nBands );
646 52 : for( iBand = 0; iBand < nBands; iBand++ )
647 : {
648 35 : psClient->pBands[iBand].nBits = nBits;
649 35 : psClient->pBands[iBand].bSigned = bSigned;
650 35 : psClient->pBands[iBand].szDesc = CPLStrdup(
651 35 : CPLSPrintf("Band%d",iBand+1) );
652 : }
653 :
654 : /* -------------------------------------------------------------------- */
655 : /* Allow CNCSFile::SetParameter() requests. */
656 : /* -------------------------------------------------------------------- */
657 : const char *pszOption;
658 :
659 17 : if( bIsJPEG2000 )
660 : {
661 16 : pszOption = CSLFetchNameValue(papszOptions, "PROFILE");
662 16 : if( pszOption != NULL && EQUAL(pszOption,"BASELINE_0") )
663 : SetParameter(
664 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_0 );
665 16 : else if( pszOption != NULL && EQUAL(pszOption,"BASELINE_1") )
666 : SetParameter(
667 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_1 );
668 16 : else if( pszOption != NULL && EQUAL(pszOption,"BASELINE_2") )
669 : SetParameter(
670 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_2 );
671 19 : else if( pszOption != NULL && EQUAL(pszOption,"NPJE") )
672 : SetParameter(
673 3 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_NPJE );
674 13 : else if( pszOption != NULL && EQUAL(pszOption,"EPJE") )
675 : SetParameter(
676 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_EPJE );
677 :
678 16 : pszOption = CSLFetchNameValue(papszOptions, "CODESTREAM_ONLY" );
679 16 : if( pszOption != NULL )
680 : SetParameter(
681 : CNCSJP2FileView::JP2_COMPRESS_CODESTREAM_ONLY,
682 3 : (bool) CSLTestBoolean( pszOption ) );
683 :
684 16 : pszOption = CSLFetchNameValue(papszOptions, "LEVELS");
685 16 : if( pszOption != NULL )
686 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_LEVELS,
687 0 : (UINT32) atoi(pszOption) );
688 :
689 16 : pszOption = CSLFetchNameValue(papszOptions, "LAYERS");
690 16 : if( pszOption != NULL )
691 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_LAYERS,
692 0 : (UINT32) atoi(pszOption) );
693 :
694 16 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_WIDTH");
695 16 : if( pszOption != NULL )
696 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_PRECINCT_WIDTH,
697 0 : (UINT32) atoi(pszOption) );
698 :
699 16 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_HEIGHT");
700 16 : if( pszOption != NULL )
701 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_HEIGHT,
702 0 : (UINT32) atoi(pszOption) );
703 :
704 16 : pszOption = CSLFetchNameValue(papszOptions, "TILE_WIDTH");
705 16 : if( pszOption != NULL )
706 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_TILE_WIDTH,
707 0 : (UINT32) atoi(pszOption) );
708 :
709 16 : pszOption = CSLFetchNameValue(papszOptions, "TILE_HEIGHT");
710 16 : if( pszOption != NULL )
711 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_TILE_HEIGHT,
712 0 : (UINT32) atoi(pszOption) );
713 :
714 16 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_SOP");
715 16 : if( pszOption != NULL )
716 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_INCLUDE_SOP,
717 0 : (bool) CSLTestBoolean( pszOption ) );
718 :
719 16 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_EPH");
720 16 : if( pszOption != NULL )
721 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_INCLUDE_EPH,
722 0 : (bool) CSLTestBoolean( pszOption ) );
723 :
724 16 : pszOption = CSLFetchNameValue(papszOptions, "PROGRESSION");
725 16 : if( pszOption != NULL && EQUAL(pszOption,"LRCP") )
726 : SetParameter(
727 0 : CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_LRCP );
728 :
729 16 : else if( pszOption != NULL && EQUAL(pszOption,"RLCP") )
730 : SetParameter(
731 0 : CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RLCP );
732 :
733 16 : else if( pszOption != NULL && EQUAL(pszOption,"RPCL") )
734 : SetParameter(
735 0 : CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RPCL );
736 :
737 16 : pszOption = CSLFetchNameValue(papszOptions, "GEODATA_USAGE");
738 16 : if( pszOption == NULL )
739 : // Default to supressing ECW SDK geodata, just use our own stuff.
740 16 : SetGeodataUsage( JP2_GEODATA_USE_NONE );
741 0 : else if( EQUAL(pszOption,"NONE") )
742 0 : SetGeodataUsage( JP2_GEODATA_USE_NONE );
743 0 : else if( EQUAL(pszOption,"PCS_ONLY") )
744 0 : SetGeodataUsage( JP2_GEODATA_USE_PCS_ONLY );
745 0 : else if( EQUAL(pszOption,"GML_ONLY") )
746 0 : SetGeodataUsage( JP2_GEODATA_USE_GML_ONLY );
747 0 : else if( EQUAL(pszOption,"PCS_GML") )
748 0 : SetGeodataUsage( JP2_GEODATA_USE_PCS_GML );
749 0 : else if( EQUAL(pszOption,"GML_PCS") )
750 0 : SetGeodataUsage( JP2_GEODATA_USE_GML_PCS );
751 0 : else if( EQUAL(pszOption,"ALL") )
752 0 : SetGeodataUsage( JP2_GEODATA_USE_GML_PCS_WLD );
753 :
754 16 : pszOption = CSLFetchNameValue(papszOptions, "DECOMPRESS_LAYERS");
755 16 : if( pszOption != NULL )
756 : SetParameter(
757 : CNCSJP2FileView::JP2_DECOMPRESS_LAYERS,
758 0 : (UINT32) atoi(pszOption) );
759 :
760 : pszOption = CSLFetchNameValue(papszOptions,
761 16 : "DECOMPRESS_RECONSTRUCTION_PARAMETER");
762 16 : if( pszOption != NULL )
763 : SetParameter(
764 : CNCSJP2FileView::JPC_DECOMPRESS_RECONSTRUCTION_PARAMETER,
765 0 : (IEEE4) atof(pszOption) );
766 : }
767 :
768 : /* -------------------------------------------------------------------- */
769 : /* Georeferencing. */
770 : /* -------------------------------------------------------------------- */
771 :
772 17 : psClient->fOriginX = 0.0;
773 17 : psClient->fOriginY = psClient->nSizeY;
774 17 : psClient->fCellIncrementX = 1.0;
775 17 : psClient->fCellIncrementY = -1.0;
776 17 : psClient->fCWRotationDegrees = 0.0;
777 :
778 17 : if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0 )
779 : CPLError( CE_Warning, CPLE_NotSupported,
780 : "Rotational coefficients ignored, georeferencing of\n"
781 0 : "output ECW file will be incorrect.\n" );
782 : else
783 : {
784 17 : psClient->fOriginX = padfGeoTransform[0];
785 17 : psClient->fOriginY = padfGeoTransform[3];
786 17 : psClient->fCellIncrementX = padfGeoTransform[1];
787 17 : psClient->fCellIncrementY = padfGeoTransform[5];
788 : }
789 :
790 : /* -------------------------------------------------------------------- */
791 : /* Projection. */
792 : /* -------------------------------------------------------------------- */
793 : char szProjection[128];
794 : char szDatum[128];
795 :
796 17 : strcpy( szProjection, "RAW" );
797 17 : strcpy( szDatum, "RAW" );
798 :
799 17 : if( CSLFetchNameValue(papszOptions, "PROJ") != NULL )
800 : {
801 : strncpy( szProjection,
802 0 : CSLFetchNameValue(papszOptions, "PROJ"), sizeof(szProjection) );
803 0 : szProjection[sizeof(szProjection)-1] = 0;
804 : }
805 :
806 17 : if( CSLFetchNameValue(papszOptions, "DATUM") != NULL )
807 : {
808 0 : strncpy( szDatum, CSLFetchNameValue(papszOptions, "DATUM"), sizeof(szDatum) );
809 0 : szDatum[sizeof(szDatum)-1] = 0;
810 0 : if( EQUAL(szProjection,"RAW") )
811 0 : strcpy( szProjection, "GEODETIC" );
812 : }
813 :
814 17 : if( EQUAL(szProjection,"RAW") && pszWKT != NULL )
815 : {
816 16 : ECWTranslateFromWKT( pszWKT, szProjection, sizeof(szProjection), szDatum, sizeof(szDatum) );
817 : }
818 :
819 17 : psClient->szDatum = szDatum;
820 17 : psClient->szProjection = szProjection;
821 :
822 : CPLDebug( "ECW", "Writing with PROJ=%s, DATUM=%s",
823 17 : szProjection, szDatum );
824 :
825 : /* -------------------------------------------------------------------- */
826 : /* Setup GML and GeoTIFF information. */
827 : /* -------------------------------------------------------------------- */
828 17 : GDALJP2Metadata oJP2MD;
829 :
830 17 : oJP2MD.SetProjection( pszWKT );
831 17 : oJP2MD.SetGeoTransform( padfGeoTransform );
832 17 : oJP2MD.SetGCPs( nGCPCount, pasGCPList );
833 :
834 17 : if( CSLFetchBoolean( papszOptions, "GMLJP2", TRUE ) )
835 17 : WriteJP2Box( oJP2MD.CreateGMLJP2(nXSize,nYSize) );
836 17 : if( CSLFetchBoolean( papszOptions, "GeoJP2", TRUE ) )
837 17 : WriteJP2Box( oJP2MD.CreateJP2GeoTIFF() );
838 :
839 : /* -------------------------------------------------------------------- */
840 : /* Handle special case of a JPEG2000 data stream in another file. */
841 : /* -------------------------------------------------------------------- */
842 17 : FILE *fpVSIL = NULL;
843 :
844 17 : if( EQUALN(pszFilename,"J2K_SUBFILE:",12) )
845 : {
846 3 : int subfile_offset=-1, subfile_size=-1;
847 3 : const char *real_filename = NULL;
848 :
849 3 : if( sscanf( pszFilename, "J2K_SUBFILE:%d,%d",
850 : &subfile_offset, &subfile_size ) != 2 )
851 : {
852 : CPLError( CE_Failure, CPLE_OpenFailed,
853 0 : "Failed to parse J2K_SUBFILE specification." );
854 0 : return CE_Failure;
855 : }
856 :
857 3 : real_filename = strstr(pszFilename,",");
858 3 : if( real_filename != NULL )
859 3 : real_filename = strstr(real_filename+1,",");
860 3 : if( real_filename != NULL )
861 3 : real_filename++;
862 : else
863 : {
864 : CPLError( CE_Failure, CPLE_OpenFailed,
865 0 : "Failed to parse J2K_SUBFILE specification." );
866 0 : return CE_Failure;
867 : }
868 :
869 6 : fpVSIL = VSIFOpenL( real_filename, "rb+" );
870 3 : if( fpVSIL == NULL )
871 : {
872 : CPLError( CE_Failure, CPLE_OpenFailed,
873 0 : "Failed to open %s.", real_filename );
874 0 : return CE_Failure;
875 : }
876 :
877 : m_OStream.Access( fpVSIL, TRUE, real_filename,
878 3 : subfile_offset, subfile_size );
879 : }
880 :
881 :
882 : /* -------------------------------------------------------------------- */
883 : /* Check if we can enable large files. This option should only */
884 : /* be set when the application is adhering to one of the */
885 : /* ERMapper options for licensing larger than 500MB input */
886 : /* files. See Bug 767. */
887 : /* -------------------------------------------------------------------- */
888 17 : const char *pszLargeOK = CSLFetchNameValue(papszOptions, "LARGE_OK");
889 17 : if( pszLargeOK == NULL )
890 17 : pszLargeOK = "NO";
891 :
892 17 : pszLargeOK = CPLGetConfigOption( "ECW_LARGE_OK", pszLargeOK );
893 :
894 17 : if( CSLTestBoolean(pszLargeOK) )
895 : {
896 0 : CNCSFile::SetKeySize();
897 0 : CPLDebug( "ECW", "Large file generation enabled." );
898 : }
899 :
900 : /* -------------------------------------------------------------------- */
901 : /* Set the file info. */
902 : /* -------------------------------------------------------------------- */
903 17 : CNCSError oError;
904 :
905 17 : oError = SetFileInfo( sFileInfo );
906 :
907 17 : if( oError.GetErrorNumber() == NCS_SUCCESS )
908 : {
909 17 : if( fpVSIL == NULL )
910 14 : oError = Open( (char *) pszFilename, false, true );
911 : else
912 3 : oError = CNCSJP2FileView::Open( &(m_OStream) );
913 : }
914 :
915 17 : if( oError.GetErrorNumber() == NCS_SUCCESS )
916 17 : return CE_None;
917 0 : else if( oError.GetErrorNumber() == NCS_INPUT_SIZE_EXCEEDED )
918 : {
919 : CPLError( CE_Failure, CPLE_AppDefined,
920 0 : "ECW SDK 500MB compress limit exceeded." );
921 0 : return CE_Failure;
922 : }
923 : else
924 : {
925 0 : char* pszErrorMessage = oError.GetErrorMessage();
926 : CPLError( CE_Failure, CPLE_AppDefined,
927 0 : "%s", pszErrorMessage );
928 0 : NCSFree(pszErrorMessage);
929 :
930 0 : return CE_Failure;
931 0 : }
932 : }
933 :
934 : /************************************************************************/
935 : /* ECWCreateCopy() */
936 : /************************************************************************/
937 :
938 : static GDALDataset *
939 15 : ECWCreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
940 : int bStrict, char ** papszOptions,
941 : GDALProgressFunc pfnProgress, void * pProgressData,
942 : int bIsJPEG2000 )
943 :
944 : {
945 15 : ECWInitialize();
946 :
947 : /* -------------------------------------------------------------------- */
948 : /* Get various values from the source dataset. */
949 : /* -------------------------------------------------------------------- */
950 15 : int nBands = poSrcDS->GetRasterCount();
951 15 : int nXSize = poSrcDS->GetRasterXSize();
952 15 : int nYSize = poSrcDS->GetRasterYSize();
953 :
954 15 : if (nBands == 0)
955 : {
956 : CPLError( CE_Failure, CPLE_NotSupported,
957 0 : "ECW driver does not support source dataset with zero band.\n");
958 0 : return NULL;
959 : }
960 :
961 15 : GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
962 :
963 15 : const char *pszWKT = poSrcDS->GetProjectionRef();
964 15 : double adfGeoTransform[6] = { 0, 1, 0, 0, 0, 1 };;
965 :
966 15 : poSrcDS->GetGeoTransform( adfGeoTransform );
967 :
968 15 : if( poSrcDS->GetGCPCount() > 0 )
969 1 : pszWKT = poSrcDS->GetGCPProjection();
970 :
971 : /* -------------------------------------------------------------------- */
972 : /* Confirm the datatype is 8bit. It appears no other datatype */
973 : /* is supported in ECW format. */
974 : /* -------------------------------------------------------------------- */
975 15 : if( eType != GDT_Byte )
976 : {
977 5 : if( bStrict )
978 : {
979 : CPLError( CE_Failure, CPLE_AppDefined,
980 : "Attempt to create ECW file with pixel data type %s failed.\n"
981 : "Only Byte data type supported.",
982 5 : GDALGetDataTypeName( eType ) );
983 : }
984 : else
985 : {
986 : CPLError( CE_Warning, CPLE_AppDefined,
987 : "ECW only supports Byte pixel data type, ignoring request for %s.",
988 0 : GDALGetDataTypeName( eType ) );
989 0 : eType = GDT_Byte;
990 : }
991 : }
992 :
993 : /* -------------------------------------------------------------------- */
994 : /* Setup the compressor. */
995 : /* -------------------------------------------------------------------- */
996 15 : GDALECWCompressor oCompressor;
997 :
998 15 : oCompressor.pfnProgress = pfnProgress;
999 15 : oCompressor.pProgressData = pProgressData;
1000 15 : oCompressor.m_poSrcDS = poSrcDS;
1001 :
1002 15 : if( !pfnProgress( 0.0, NULL, pProgressData ) )
1003 0 : return NULL;
1004 :
1005 30 : if( oCompressor.Initialize( pszFilename, papszOptions,
1006 : nXSize, nYSize, nBands,
1007 : eType, pszWKT, adfGeoTransform,
1008 15 : poSrcDS->GetGCPCount(),
1009 15 : poSrcDS->GetGCPs(),
1010 : bIsJPEG2000 )
1011 : != CE_None )
1012 0 : return NULL;
1013 :
1014 : /* -------------------------------------------------------------------- */
1015 : /* Start the compression. */
1016 : /* -------------------------------------------------------------------- */
1017 15 : oCompressor.Write();
1018 :
1019 : /* -------------------------------------------------------------------- */
1020 : /* Cleanup, and return read-only handle. */
1021 : /* -------------------------------------------------------------------- */
1022 15 : oCompressor.CloseDown();
1023 15 : pfnProgress( 1.001, NULL, pProgressData );
1024 :
1025 : /* -------------------------------------------------------------------- */
1026 : /* Re-open dataset, and copy any auxilary pam information. */
1027 : /* -------------------------------------------------------------------- */
1028 15 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
1029 : GDALPamDataset *poDS;
1030 :
1031 15 : if (bIsJPEG2000)
1032 14 : poDS = (GDALPamDataset*) ECWDatasetOpenJPEG2000(&oOpenInfo);
1033 : else
1034 1 : poDS = (GDALPamDataset*) GDALOpen(pszFilename, GA_ReadOnly);
1035 :
1036 15 : if( poDS )
1037 15 : poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
1038 :
1039 15 : return poDS;
1040 : }
1041 :
1042 : /************************************************************************/
1043 : /* ECWCreateCopyECW() */
1044 : /************************************************************************/
1045 :
1046 : GDALDataset *
1047 17 : ECWCreateCopyECW( const char * pszFilename, GDALDataset *poSrcDS,
1048 : int bStrict, char ** papszOptions,
1049 : GDALProgressFunc pfnProgress, void * pProgressData )
1050 :
1051 : {
1052 17 : int nBands = poSrcDS->GetRasterCount();
1053 17 : if (nBands == 0)
1054 : {
1055 : CPLError( CE_Failure, CPLE_NotSupported,
1056 1 : "ECW driver does not support source dataset with zero band.\n");
1057 1 : return NULL;
1058 : }
1059 :
1060 16 : if( !EQUAL(CPLGetExtension(pszFilename),"ecw") )
1061 : {
1062 : CPLError( CE_Failure, CPLE_AppDefined,
1063 : "ECW driver does not support creating ECW files\n"
1064 15 : "with an extension other than .ecw" );
1065 15 : return NULL;
1066 : }
1067 :
1068 1 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
1069 : && bStrict )
1070 : {
1071 : CPLError( CE_Failure, CPLE_NotSupported,
1072 : "ECW driver doesn't support data type %s. "
1073 : "Only eight bit bands supported.\n",
1074 : GDALGetDataTypeName(
1075 0 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
1076 :
1077 0 : return NULL;
1078 : }
1079 :
1080 1 : if( poSrcDS->GetRasterXSize() < 128
1081 : || poSrcDS->GetRasterYSize() < 128 )
1082 : {
1083 : CPLError( CE_Failure, CPLE_NotSupported,
1084 : "ECW driver requires image to be at least 128x128,\n"
1085 : "the source image is %dx%d.\n",
1086 : poSrcDS->GetRasterXSize(),
1087 0 : poSrcDS->GetRasterYSize() );
1088 :
1089 0 : return NULL;
1090 : }
1091 :
1092 1 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != NULL)
1093 : {
1094 : CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1095 : "ECW driver ignores color table. "
1096 : "The source raster band will be considered as grey level.\n"
1097 0 : "Consider using color table expansion (-expand option in gdal_translate)\n");
1098 0 : if (bStrict)
1099 0 : return NULL;
1100 : }
1101 :
1102 : return ECWCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions,
1103 1 : pfnProgress, pProgressData, FALSE );
1104 : }
1105 :
1106 : /************************************************************************/
1107 : /* ECWCreateCopyJPEG2000() */
1108 : /************************************************************************/
1109 :
1110 : GDALDataset *
1111 19 : ECWCreateCopyJPEG2000( const char * pszFilename, GDALDataset *poSrcDS,
1112 : int bStrict, char ** papszOptions,
1113 : GDALProgressFunc pfnProgress, void * pProgressData )
1114 :
1115 : {
1116 19 : int nBands = poSrcDS->GetRasterCount();
1117 19 : if (nBands == 0)
1118 : {
1119 : CPLError( CE_Failure, CPLE_NotSupported,
1120 1 : "JP2ECW driver does not support source dataset with zero band.\n");
1121 1 : return NULL;
1122 : }
1123 :
1124 18 : if( EQUAL(CPLGetExtension(pszFilename),"ecw") )
1125 : {
1126 : CPLError( CE_Failure, CPLE_AppDefined,
1127 : "JP2ECW driver does not support creating JPEG2000 files\n"
1128 0 : "with a .ecw extension. Please use anything else." );
1129 0 : return NULL;
1130 : }
1131 :
1132 18 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
1133 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
1134 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int16
1135 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16
1136 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int32
1137 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt32
1138 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Float32
1139 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Float64
1140 : && bStrict )
1141 : {
1142 : CPLError( CE_Failure, CPLE_NotSupported,
1143 : "JP2ECW driver doesn't support data type %s. ",
1144 : GDALGetDataTypeName(
1145 4 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
1146 :
1147 4 : return NULL;
1148 : }
1149 :
1150 14 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != NULL)
1151 : {
1152 : CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1153 : "JP2ECW driver ignores color table. "
1154 : "The source raster band will be considered as grey level.\n"
1155 0 : "Consider using color table expansion (-expand option in gdal_translate)\n");
1156 0 : if (bStrict)
1157 0 : return NULL;
1158 : }
1159 :
1160 : return ECWCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions,
1161 14 : pfnProgress, pProgressData, TRUE );
1162 : }
1163 :
1164 : /************************************************************************/
1165 : /************************************************************************
1166 :
1167 : ECW/JPEG200 Create() Support
1168 : ----------------------------
1169 :
1170 : The remainder of the file is code to implement the Create() method.
1171 : New dataset and raster band classes are defined specifically for the
1172 : purpose of being write-only. In particular, you cannot read back data
1173 : from these datasets, and writing must occur in a pretty specific order.
1174 :
1175 : That is, you need to write all metadata (projection, georef, etc) first
1176 : and then write the image data. All bands data for the first scanline
1177 : should be written followed by all bands for the second scanline and so on.
1178 :
1179 : Creation supports the same virtual subfile names as CreateCopy() supports.
1180 :
1181 : ************************************************************************/
1182 : /************************************************************************/
1183 :
1184 : /************************************************************************/
1185 : /* ==================================================================== */
1186 : /* ECWWriteDataset */
1187 : /* ==================================================================== */
1188 : /************************************************************************/
1189 :
1190 : class ECWWriteRasterBand;
1191 :
1192 : class CPL_DLL ECWWriteDataset : public GDALDataset
1193 : {
1194 : friend class ECWWriteRasterBand;
1195 :
1196 : char *pszFilename;
1197 :
1198 : int bIsJPEG2000;
1199 : GDALDataType eDataType;
1200 : char **papszOptions;
1201 :
1202 : char *pszProjection;
1203 : double adfGeoTransform[6];
1204 :
1205 : GDALECWCompressor oCompressor;
1206 : int bCrystalized;
1207 :
1208 : int nLoadedLine;
1209 : GByte *pabyBILBuffer;
1210 :
1211 : CPLErr Crystalize();
1212 : CPLErr FlushLine();
1213 :
1214 : public:
1215 : ECWWriteDataset( const char *, int, int, int,
1216 : GDALDataType, char **papszOptions,
1217 : int );
1218 : ~ECWWriteDataset();
1219 :
1220 : virtual void FlushCache( void );
1221 :
1222 : virtual CPLErr SetGeoTransform( double * );
1223 : virtual CPLErr SetProjection( const char *pszWKT );
1224 : };
1225 :
1226 : /************************************************************************/
1227 : /* ==================================================================== */
1228 : /* ECWWriteRasterBand */
1229 : /* ==================================================================== */
1230 : /************************************************************************/
1231 :
1232 : class ECWWriteRasterBand : public GDALRasterBand
1233 58 : {
1234 : friend class ECWWriteDataset;
1235 :
1236 : // NOTE: poDS may be altered for NITF/JPEG2000 files!
1237 : ECWWriteDataset *poGDS;
1238 :
1239 : GDALColorInterp eInterp;
1240 :
1241 : public:
1242 :
1243 : ECWWriteRasterBand( ECWWriteDataset *, int );
1244 :
1245 0 : virtual CPLErr SetColorInterpretation( GDALColorInterp eInterpIn )
1246 0 : { eInterp = eInterpIn; return CE_None; }
1247 3 : virtual GDALColorInterp GetColorInterpretation()
1248 3 : { return eInterp; }
1249 :
1250 : virtual CPLErr IReadBlock( int, int, void * );
1251 : virtual CPLErr IWriteBlock( int, int, void * );
1252 : };
1253 :
1254 : /************************************************************************/
1255 : /* ECWWriteDataset() */
1256 : /************************************************************************/
1257 :
1258 18 : ECWWriteDataset::ECWWriteDataset( const char *pszFilename,
1259 : int nXSize, int nYSize, int nBandCount,
1260 : GDALDataType eType,
1261 18 : char **papszOptions, int bIsJPEG2000 )
1262 :
1263 : {
1264 18 : bCrystalized = FALSE;
1265 18 : pabyBILBuffer = NULL;
1266 18 : nLoadedLine = -1;
1267 :
1268 18 : this->bIsJPEG2000 = bIsJPEG2000;
1269 18 : this->eDataType = eType;
1270 18 : this->papszOptions = CSLDuplicate( papszOptions );
1271 18 : this->pszFilename = CPLStrdup( pszFilename );
1272 :
1273 18 : nRasterXSize = nXSize;
1274 18 : nRasterYSize = nYSize;
1275 18 : pszProjection = NULL;
1276 :
1277 18 : adfGeoTransform[0] = 0.0;
1278 18 : adfGeoTransform[1] = 1.0;
1279 18 : adfGeoTransform[2] = 0.0;
1280 18 : adfGeoTransform[3] = 0.0;
1281 18 : adfGeoTransform[4] = 0.0;
1282 18 : adfGeoTransform[5] = 1.0;
1283 :
1284 : // create band objects.
1285 47 : for( int iBand = 1; iBand <= nBandCount; iBand++ )
1286 : {
1287 29 : SetBand( iBand, new ECWWriteRasterBand( this, iBand ) );
1288 : }
1289 18 : }
1290 :
1291 : /************************************************************************/
1292 : /* ~ECWWriteDataset() */
1293 : /************************************************************************/
1294 :
1295 36 : ECWWriteDataset::~ECWWriteDataset()
1296 :
1297 : {
1298 18 : FlushCache();
1299 :
1300 18 : if( bCrystalized )
1301 : {
1302 2 : if( nLoadedLine == nRasterYSize - 1 )
1303 2 : FlushLine();
1304 2 : oCompressor.CloseDown();
1305 : }
1306 :
1307 18 : CPLFree( pabyBILBuffer );
1308 18 : CPLFree( pszProjection );
1309 18 : CSLDestroy( papszOptions );
1310 18 : CPLFree( pszFilename );
1311 36 : }
1312 :
1313 : /************************************************************************/
1314 : /* FlushCache() */
1315 : /************************************************************************/
1316 :
1317 19 : void ECWWriteDataset::FlushCache()
1318 :
1319 : {
1320 19 : BlockBasedFlushCache();
1321 19 : }
1322 :
1323 : /************************************************************************/
1324 : /* SetGeoTransform() */
1325 : /************************************************************************/
1326 :
1327 1 : CPLErr ECWWriteDataset::SetGeoTransform( double *padfGeoTransform )
1328 :
1329 : {
1330 1 : memcpy( adfGeoTransform, padfGeoTransform, sizeof(double) * 6 );
1331 1 : return CE_None;
1332 : }
1333 :
1334 : /************************************************************************/
1335 : /* SetProjection() */
1336 : /************************************************************************/
1337 :
1338 1 : CPLErr ECWWriteDataset::SetProjection( const char *pszWKT )
1339 :
1340 : {
1341 1 : CPLFree( pszProjection );
1342 1 : pszProjection = CPLStrdup( pszWKT );
1343 :
1344 1 : return CE_None;
1345 : }
1346 :
1347 :
1348 : /************************************************************************/
1349 : /* Crystalize() */
1350 : /************************************************************************/
1351 :
1352 2 : CPLErr ECWWriteDataset::Crystalize()
1353 :
1354 : {
1355 2 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1356 :
1357 : CPLErr eErr;
1358 2 : CNCSError oError;
1359 :
1360 2 : if( bCrystalized )
1361 0 : return CE_None;
1362 :
1363 : eErr = oCompressor.Initialize( pszFilename, papszOptions,
1364 : nRasterXSize, nRasterYSize, nBands,
1365 : eDataType,
1366 : pszProjection, adfGeoTransform,
1367 : 0, NULL,
1368 2 : bIsJPEG2000 );
1369 :
1370 2 : if( eErr == CE_None )
1371 2 : bCrystalized = TRUE;
1372 :
1373 2 : nLoadedLine = -1;
1374 2 : pabyBILBuffer = (GByte *) CPLMalloc( nWordSize * nBands * nRasterXSize );
1375 :
1376 2 : return eErr;
1377 : }
1378 :
1379 : /************************************************************************/
1380 : /* FlushLine() */
1381 : /************************************************************************/
1382 :
1383 202 : CPLErr ECWWriteDataset::FlushLine()
1384 :
1385 : {
1386 202 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1387 : CPLErr eErr;
1388 :
1389 : /* -------------------------------------------------------------------- */
1390 : /* Crystalize if not already done. */
1391 : /* -------------------------------------------------------------------- */
1392 202 : if( !bCrystalized )
1393 : {
1394 2 : eErr = Crystalize();
1395 :
1396 2 : if( eErr != CE_None )
1397 0 : return eErr;
1398 : }
1399 :
1400 : /* -------------------------------------------------------------------- */
1401 : /* Write out the currently loaded line. */
1402 : /* -------------------------------------------------------------------- */
1403 202 : if( nLoadedLine != -1 )
1404 : {
1405 200 : CNCSError oError;
1406 : void **papOutputLine;
1407 :
1408 200 : papOutputLine = (void **) CPLMalloc(sizeof(void*) * nBands);
1409 600 : for( int i = 0; i < nBands; i++ )
1410 400 : papOutputLine[i] =
1411 400 : (void *) (pabyBILBuffer + i * nWordSize * nRasterXSize);
1412 :
1413 :
1414 : oError = oCompressor.WriteLineBIL( oCompressor.sFileInfo.eCellType,
1415 200 : (UINT16) nBands, papOutputLine );
1416 200 : CPLFree( papOutputLine );
1417 200 : if( oError.GetErrorNumber() != NCS_SUCCESS )
1418 : {
1419 0 : char* pszErrorMessage = oError.GetErrorMessage();
1420 : CPLError( CE_Failure, CPLE_AppDefined,
1421 : "Scanline write write failed.\n%s",
1422 0 : pszErrorMessage );
1423 0 : NCSFree(pszErrorMessage);
1424 :
1425 0 : return CE_Failure;
1426 0 : }
1427 : }
1428 :
1429 : /* -------------------------------------------------------------------- */
1430 : /* Clear the buffer and increment the "current line" indicator. */
1431 : /* -------------------------------------------------------------------- */
1432 202 : memset( pabyBILBuffer, 0, nWordSize * nRasterXSize * nBands );
1433 202 : nLoadedLine++;
1434 :
1435 202 : return CE_None;
1436 : }
1437 :
1438 :
1439 : /************************************************************************/
1440 : /* ==================================================================== */
1441 : /* ECWWriteRasterBand */
1442 : /* ==================================================================== */
1443 : /************************************************************************/
1444 :
1445 : /************************************************************************/
1446 : /* ECWWriteRasterBand() */
1447 : /************************************************************************/
1448 :
1449 29 : ECWWriteRasterBand::ECWWriteRasterBand( ECWWriteDataset *poDSIn,
1450 29 : int nBandIn )
1451 :
1452 : {
1453 29 : nBand = nBandIn;
1454 29 : poDS = poDSIn;
1455 29 : poGDS = poDSIn;
1456 29 : nBlockXSize = poDSIn->GetRasterXSize();
1457 29 : nBlockYSize = 1;
1458 29 : eDataType = poDSIn->eDataType;
1459 29 : eInterp = GCI_Undefined;
1460 29 : }
1461 :
1462 : /************************************************************************/
1463 : /* IReadBlock() */
1464 : /************************************************************************/
1465 :
1466 0 : CPLErr ECWWriteRasterBand::IReadBlock( int nBlockX, int nBlockY,
1467 : void *pBuffer )
1468 :
1469 : {
1470 0 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1471 :
1472 : // We zero stuff out here, but we can't really read stuff from
1473 : // a write only stream.
1474 :
1475 0 : memset( pBuffer, 0, nBlockXSize * nWordSize );
1476 :
1477 0 : return CE_None;
1478 : }
1479 :
1480 : /************************************************************************/
1481 : /* IWriteBlock() */
1482 : /************************************************************************/
1483 :
1484 400 : CPLErr ECWWriteRasterBand::IWriteBlock( int nBlockX, int nBlockY,
1485 : void *pBuffer )
1486 :
1487 : {
1488 400 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1489 : CPLErr eErr;
1490 :
1491 : /* -------------------------------------------------------------------- */
1492 : /* Flush previous line if needed. */
1493 : /* -------------------------------------------------------------------- */
1494 400 : if( nBlockY == poGDS->nLoadedLine + 1 )
1495 : {
1496 200 : eErr = poGDS->FlushLine();
1497 200 : if( eErr != CE_None )
1498 0 : return eErr;
1499 : }
1500 :
1501 : /* -------------------------------------------------------------------- */
1502 : /* Blow a gasket if we have been asked to write something out */
1503 : /* of order. */
1504 : /* -------------------------------------------------------------------- */
1505 400 : if( nBlockY != poGDS->nLoadedLine )
1506 : {
1507 : CPLError( CE_Failure, CPLE_AppDefined,
1508 : "Apparent attempt to write to ECW non-sequentially.\n"
1509 : "Loaded line is %d, but %d of band %d was written to.",
1510 0 : poGDS->nLoadedLine, nBlockY, nBand );
1511 0 : return CE_Failure;
1512 : }
1513 :
1514 : /* -------------------------------------------------------------------- */
1515 : /* Copy passed data into current line buffer. */
1516 : /* -------------------------------------------------------------------- */
1517 : memcpy( poGDS->pabyBILBuffer + (nBand-1) * nWordSize * nRasterXSize,
1518 : pBuffer,
1519 400 : nWordSize * nRasterXSize );
1520 :
1521 400 : return CE_None;
1522 : }
1523 :
1524 : /************************************************************************/
1525 : /* ECWCreateJPEG2000() */
1526 : /************************************************************************/
1527 :
1528 : GDALDataset *
1529 18 : ECWCreateJPEG2000(const char *pszFilename, int nXSize, int nYSize, int nBands,
1530 : GDALDataType eType, char **papszOptions )
1531 :
1532 : {
1533 18 : ECWInitialize();
1534 :
1535 : return new ECWWriteDataset( pszFilename, nXSize, nYSize, nBands,
1536 18 : eType, papszOptions, TRUE );
1537 : }
1538 :
1539 : /************************************************************************/
1540 : /* ECWCreateECW() */
1541 : /************************************************************************/
1542 :
1543 : GDALDataset *
1544 0 : ECWCreateECW( const char *pszFilename, int nXSize, int nYSize, int nBands,
1545 : GDALDataType eType, char **papszOptions )
1546 :
1547 : {
1548 0 : ECWInitialize();
1549 :
1550 : return new ECWWriteDataset( pszFilename, nXSize, nYSize, nBands,
1551 0 : eType, papszOptions, FALSE );
1552 1140 : }
1553 :
1554 : #endif /* def FRMT_ecw && def HAVE_COMPRESS */
|