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