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