1 : /******************************************************************************
2 : * $Id: blxdataset.cpp 23619 2011-12-20 22:40:19Z rouault $
3 : *
4 : * Project: BLX Driver
5 : * Purpose: GDAL BLX support.
6 : * Author: Henrik Johansson, henrik@johome.net
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2006, Henrik Johansson <henrik@johome.net>
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 : */
31 :
32 : #include "gdal_pam.h"
33 : #include "cpl_string.h"
34 :
35 : CPL_CVSID("$Id: blxdataset.cpp 23619 2011-12-20 22:40:19Z rouault $");
36 :
37 : CPL_C_START
38 : #include <blx.h>
39 : CPL_C_END
40 :
41 : CPL_C_START
42 : void GDALRegister_BLX(void);
43 : CPL_C_END
44 :
45 : class BLXDataset : public GDALPamDataset
46 : {
47 : friend class BLXRasterBand;
48 :
49 : CPLErr GetGeoTransform( double * padfTransform );
50 : const char *GetProjectionRef();
51 :
52 : blxcontext_t *blxcontext;
53 :
54 : int nOverviewCount;
55 : int bIsOverview;
56 : BLXDataset *papoOverviewDS[BLX_OVERVIEWLEVELS];
57 :
58 : public:
59 : BLXDataset();
60 : ~BLXDataset();
61 :
62 : static GDALDataset *Open( GDALOpenInfo * );
63 : };
64 :
65 : class BLXRasterBand : public GDALPamRasterBand
66 55 : {
67 : int overviewLevel;
68 : public:
69 : BLXRasterBand( BLXDataset *, int, int overviewLevel=0 );
70 :
71 : virtual double GetNoDataValue( int *pbSuccess = NULL );
72 : virtual GDALColorInterp GetColorInterpretation(void);
73 : virtual int GetOverviewCount();
74 : virtual GDALRasterBand *GetOverview( int );
75 :
76 : virtual CPLErr IReadBlock( int, int, void * );
77 : };
78 :
79 11787 : GDALDataset *BLXDataset::Open( GDALOpenInfo * poOpenInfo )
80 :
81 : {
82 : // --------------------------------------------------------------------
83 : // First that the header looks like a BLX header
84 : // --------------------------------------------------------------------
85 11787 : if( poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 102 )
86 11350 : return NULL;
87 :
88 437 : if(!blx_checkheader((char *)poOpenInfo->pabyHeader))
89 426 : return NULL;
90 :
91 : // --------------------------------------------------------------------
92 : // Create a corresponding GDALDataset.
93 : // --------------------------------------------------------------------
94 : BLXDataset *poDS;
95 :
96 11 : poDS = new BLXDataset();
97 :
98 : // --------------------------------------------------------------------
99 : // Open BLX file
100 : // --------------------------------------------------------------------
101 11 : poDS->blxcontext = blx_create_context();
102 11 : if(poDS->blxcontext==NULL)
103 : {
104 0 : delete poDS;
105 0 : return NULL;
106 : }
107 11 : if (blxopen(poDS->blxcontext, poOpenInfo->pszFilename, "rb") != 0)
108 : {
109 0 : delete poDS;
110 0 : return NULL;
111 : }
112 :
113 11 : if ((poDS->blxcontext->cell_xsize % (1 << (1+BLX_OVERVIEWLEVELS))) != 0 ||
114 : (poDS->blxcontext->cell_ysize % (1 << (1+BLX_OVERVIEWLEVELS))) != 0)
115 : {
116 0 : delete poDS;
117 0 : return NULL;
118 : }
119 :
120 : // Update dataset header from BLX context
121 11 : poDS->nRasterXSize = poDS->blxcontext->xsize;
122 11 : poDS->nRasterYSize = poDS->blxcontext->ysize;
123 :
124 : // --------------------------------------------------------------------
125 : // Create band information objects.
126 : // --------------------------------------------------------------------
127 11 : poDS->nBands = 1;
128 11 : poDS->SetBand( 1, new BLXRasterBand( poDS, 1 ));
129 :
130 : // Create overview bands
131 11 : poDS->nOverviewCount = BLX_OVERVIEWLEVELS;
132 110 : for(int i=0; i < poDS->nOverviewCount; i++) {
133 44 : poDS->papoOverviewDS[i] = new BLXDataset();
134 44 : poDS->papoOverviewDS[i]->blxcontext = poDS->blxcontext;
135 44 : poDS->papoOverviewDS[i]->bIsOverview = TRUE;
136 44 : poDS->papoOverviewDS[i]->nRasterXSize = poDS->nRasterXSize >> (i+1);
137 44 : poDS->papoOverviewDS[i]->nRasterYSize = poDS->nRasterYSize >> (i+1);
138 44 : poDS->nBands = 1;
139 88 : poDS->papoOverviewDS[i]->SetBand(1, new BLXRasterBand( poDS->papoOverviewDS[i], 1, i+1));
140 : }
141 :
142 : /* -------------------------------------------------------------------- */
143 : /* Confirm the requested access is supported. */
144 : /* -------------------------------------------------------------------- */
145 11 : if( poOpenInfo->eAccess == GA_Update )
146 : {
147 0 : delete poDS;
148 : CPLError( CE_Failure, CPLE_NotSupported,
149 : "The BLX driver does not support update access to existing"
150 0 : " datasets.\n" );
151 0 : return NULL;
152 : }
153 :
154 : /* -------------------------------------------------------------------- */
155 : /* Initialize any PAM information. */
156 : /* -------------------------------------------------------------------- */
157 11 : poDS->SetDescription( poOpenInfo->pszFilename );
158 11 : poDS->TryLoadXML();
159 :
160 11 : return( poDS );
161 : }
162 :
163 55 : BLXDataset::BLXDataset() {
164 55 : blxcontext = NULL;
165 55 : bIsOverview = FALSE;
166 55 : nOverviewCount = 0;
167 :
168 55 : for(int i=0; i < nOverviewCount; i++)
169 0 : papoOverviewDS[i]=NULL;
170 55 : }
171 :
172 55 : BLXDataset::~BLXDataset() {
173 55 : if(!bIsOverview) {
174 11 : if(blxcontext) {
175 11 : blxclose(blxcontext);
176 11 : blx_free_context(blxcontext);
177 : }
178 55 : for(int i=0; i < nOverviewCount; i++)
179 44 : if(papoOverviewDS[i])
180 44 : delete papoOverviewDS[i];
181 : }
182 55 : }
183 :
184 :
185 8 : CPLErr BLXDataset::GetGeoTransform( double * padfTransform )
186 :
187 : {
188 8 : padfTransform[0] = blxcontext->lon;
189 8 : padfTransform[1] = blxcontext->pixelsize_lon;
190 8 : padfTransform[2] = 0.0;
191 8 : padfTransform[3] = blxcontext->lat;
192 8 : padfTransform[4] = 0.0;
193 8 : padfTransform[5] = blxcontext->pixelsize_lat;
194 :
195 8 : return CE_None;
196 : }
197 :
198 6 : const char *BLXDataset::GetProjectionRef()
199 : {
200 6 : return( "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]]" );
201 : }
202 :
203 55 : BLXRasterBand::BLXRasterBand( BLXDataset *poDS, int nBand, int overviewLevel )
204 :
205 : {
206 55 : BLXDataset *poGDS = (BLXDataset *) poDS;
207 :
208 55 : this->poDS = poDS;
209 55 : this->nBand = nBand;
210 55 : this->overviewLevel = overviewLevel;
211 :
212 55 : eDataType = GDT_Int16;
213 :
214 55 : nBlockXSize = poGDS->blxcontext->cell_xsize >> overviewLevel;
215 55 : nBlockYSize = poGDS->blxcontext->cell_ysize >> overviewLevel;
216 55 : }
217 :
218 1 : int BLXRasterBand::GetOverviewCount()
219 : {
220 1 : BLXDataset *poGDS = (BLXDataset *) poDS;
221 :
222 1 : return poGDS->nOverviewCount;
223 : }
224 :
225 4 : GDALRasterBand *BLXRasterBand::GetOverview( int i )
226 : {
227 4 : BLXDataset *poGDS = (BLXDataset *) poDS;
228 :
229 4 : if( i < 0 || i >= poGDS->nOverviewCount )
230 0 : return NULL;
231 : else
232 4 : return poGDS->papoOverviewDS[i]->GetRasterBand(nBand);
233 : }
234 :
235 192 : CPLErr BLXRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
236 : void * pImage )
237 :
238 : {
239 192 : BLXDataset *poGDS = (BLXDataset *) poDS;
240 :
241 192 : if(blx_readcell(poGDS->blxcontext, nBlockYOff, nBlockXOff, (short *)pImage, nBlockXSize*nBlockYSize*2, overviewLevel) == NULL) {
242 : CPLError(CE_Failure, CPLE_AppDefined,
243 0 : "Failed to read BLX cell");
244 0 : return CE_Failure;
245 : }
246 :
247 192 : return CE_None;
248 : }
249 :
250 6 : double BLXRasterBand::GetNoDataValue( int * pbSuccess )
251 : {
252 6 : if (pbSuccess)
253 6 : *pbSuccess = TRUE;
254 6 : return BLX_UNDEF;
255 : }
256 :
257 0 : GDALColorInterp BLXRasterBand::GetColorInterpretation(void) {
258 0 : return GCI_GrayIndex;
259 : }
260 :
261 : /* TODO: check if georeference is the same as for BLX files, WGS84
262 : */
263 : static GDALDataset *
264 20 : BLXCreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
265 : int bStrict, char ** papszOptions,
266 : GDALProgressFunc pfnProgress, void * pProgressData )
267 :
268 : {
269 20 : int nBands = poSrcDS->GetRasterCount();
270 20 : int nXSize = poSrcDS->GetRasterXSize();
271 20 : int nYSize = poSrcDS->GetRasterYSize();
272 :
273 20 : int zscale = 1;
274 20 : int fillundef = 1, fillundefval = 0;
275 20 : int endian = LITTLEENDIAN;
276 :
277 : // --------------------------------------------------------------------
278 : // Some rudimentary checks
279 : // --------------------------------------------------------------------
280 20 : if( nBands != 1 )
281 : {
282 : CPLError( CE_Failure, CPLE_NotSupported,
283 : "BLX driver doesn't support %d bands. Must be 1 (grey) ",
284 5 : nBands );
285 5 : return NULL;
286 : }
287 :
288 15 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int16 && bStrict )
289 : {
290 : CPLError( CE_Failure, CPLE_NotSupported,
291 : "BLX driver doesn't support data type %s. "
292 : "Only 16 bit byte bands supported.\n",
293 : GDALGetDataTypeName(
294 10 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
295 :
296 10 : return NULL;
297 : }
298 :
299 5 : if( (nXSize % 128 != 0) || (nYSize % 128 != 0) ) {
300 : CPLError( CE_Failure, CPLE_NotSupported,
301 1 : "BLX driver doesn't support dimensions that are not a multiple of 128.\n");
302 :
303 1 : return NULL;
304 : }
305 :
306 : // --------------------------------------------------------------------
307 : // What options has the user selected?
308 : // --------------------------------------------------------------------
309 4 : if( CSLFetchNameValue(papszOptions,"ZSCALE") != NULL ) {
310 0 : zscale = atoi(CSLFetchNameValue(papszOptions,"ZSCALE"));
311 0 : if( zscale < 1 ) {
312 : CPLError( CE_Failure, CPLE_IllegalArg,
313 : "ZSCALE=%s is not a legal value in the range >= 1.",
314 0 : CSLFetchNameValue(papszOptions,"ZSCALE") );
315 0 : return NULL;
316 : }
317 : }
318 :
319 4 : if( CSLFetchNameValue(papszOptions,"FILLUNDEF") != NULL
320 : && EQUAL(CSLFetchNameValue(papszOptions,"FILLUNDEF"),"NO") )
321 0 : fillundef = 0;
322 : else
323 4 : fillundef = 1;
324 :
325 4 : if( CSLFetchNameValue(papszOptions,"FILLUNDEFVAL") != NULL ) {
326 0 : fillundefval = atoi(CSLFetchNameValue(papszOptions,"FILLUNDEFVAL"));
327 0 : if( (fillundefval < -32768) || (fillundefval > 32767) ) {
328 : CPLError( CE_Failure, CPLE_IllegalArg,
329 : "FILLUNDEFVAL=%s is not a legal value in the range -32768, 32767.",
330 0 : CSLFetchNameValue(papszOptions,"FILLUNDEFVAL") );
331 0 : return NULL;
332 : }
333 : }
334 4 : if( CSLFetchNameValue(papszOptions,"BIGENDIAN") != NULL
335 : && !EQUAL(CSLFetchNameValue(papszOptions,"BIGENDIAN"),"NO") )
336 1 : endian = BIGENDIAN;
337 :
338 :
339 :
340 : // --------------------------------------------------------------------
341 : // Create the dataset.
342 : // --------------------------------------------------------------------
343 : blxcontext_t *ctx;
344 :
345 : // Create a BLX context
346 4 : ctx = blx_create_context();
347 :
348 : // Setup BLX parameters
349 4 : ctx->cell_rows = nYSize / ctx->cell_ysize;
350 4 : ctx->cell_cols = nXSize / ctx->cell_xsize;
351 4 : ctx->zscale = zscale;
352 4 : ctx->fillundef = fillundef;
353 4 : ctx->fillundefval = fillundefval;
354 4 : ctx->endian = endian;
355 :
356 4 : if(blxopen(ctx, pszFilename, "wb")) {
357 : CPLError( CE_Failure, CPLE_OpenFailed,
358 : "Unable to create blx file %s.\n",
359 2 : pszFilename );
360 2 : blx_free_context(ctx);
361 2 : return NULL;
362 : }
363 :
364 : // --------------------------------------------------------------------
365 : // Loop over image, copying image data.
366 : // --------------------------------------------------------------------
367 : GInt16 *pabyTile;
368 2 : CPLErr eErr=CE_None;
369 :
370 2 : pabyTile = (GInt16 *) VSIMalloc( sizeof(GInt16)*ctx->cell_xsize*ctx->cell_ysize );
371 2 : if (pabyTile == NULL)
372 : {
373 0 : CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
374 0 : blxclose(ctx);
375 0 : blx_free_context(ctx);
376 0 : return NULL;
377 : }
378 :
379 2 : if( !pfnProgress( 0.0, NULL, pProgressData ) )
380 0 : eErr = CE_Failure;
381 :
382 10 : for(int i=0; (i < ctx->cell_rows) && (eErr == CE_None); i++)
383 40 : for(int j=0; j < ctx->cell_cols; j++) {
384 : blxdata *celldata;
385 32 : GDALRasterBand * poBand = poSrcDS->GetRasterBand( 1 );
386 : eErr = poBand->RasterIO( GF_Read, j*ctx->cell_xsize, i*ctx->cell_ysize,
387 : ctx->cell_xsize, ctx->cell_ysize,
388 : pabyTile, ctx->cell_xsize, ctx->cell_ysize, GDT_Int16,
389 32 : 0, 0 );
390 32 : if(eErr >= CE_Failure)
391 0 : break;
392 32 : celldata = pabyTile;
393 32 : if (blx_writecell(ctx, celldata, i, j) != 0)
394 : {
395 0 : eErr = CE_Failure;
396 0 : break;
397 : }
398 :
399 32 : if ( ! pfnProgress( 1.0 * (i * ctx->cell_cols + j) / (ctx->cell_rows * ctx->cell_cols), NULL, pProgressData ))
400 : {
401 0 : eErr = CE_Failure;
402 0 : break;
403 : }
404 : }
405 :
406 2 : pfnProgress( 1.0, NULL, pProgressData );
407 :
408 2 : CPLFree( pabyTile );
409 :
410 : double adfGeoTransform[6];
411 2 : if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None )
412 : {
413 2 : ctx->lon = adfGeoTransform[0];
414 2 : ctx->lat = adfGeoTransform[3];
415 2 : ctx->pixelsize_lon = adfGeoTransform[1];
416 2 : ctx->pixelsize_lat = adfGeoTransform[5];
417 : }
418 :
419 2 : blxclose(ctx);
420 2 : blx_free_context(ctx);
421 :
422 2 : if (eErr == CE_None)
423 2 : return (GDALDataset *) GDALOpen( pszFilename, GA_ReadOnly );
424 : else
425 0 : return NULL;
426 : }
427 :
428 :
429 582 : void GDALRegister_BLX()
430 :
431 : {
432 : GDALDriver *poDriver;
433 :
434 582 : if( GDALGetDriverByName( "BLX" ) == NULL )
435 : {
436 561 : poDriver = new GDALDriver();
437 :
438 561 : poDriver->SetDescription( "BLX" );
439 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
440 561 : "Magellan topo (.blx)" );
441 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
442 561 : "frmt_various.html#BLX" );
443 561 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "blx" );
444 :
445 561 : poDriver->pfnOpen = BLXDataset::Open;
446 561 : poDriver->pfnCreateCopy = BLXCreateCopy;
447 :
448 561 : GetGDALDriverManager()->RegisterDriver( poDriver );
449 : }
450 582 : }
451 :
|