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