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 29844 : GDALDataset *BLXDataset::Open(GDALOpenInfo *poOpenInfo)
60 :
61 : {
62 : // --------------------------------------------------------------------
63 : // First that the header looks like a BLX header
64 : // --------------------------------------------------------------------
65 29844 : if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes < 102)
66 27434 : return nullptr;
67 :
68 2410 : if (!blx_checkheader((const char *)poOpenInfo->pabyHeader))
69 2397 : 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 : CPLError(CE_Failure, CPLE_NotSupported,
128 : "The BLX driver does not support update access to existing"
129 : " datasets.\n");
130 0 : return nullptr;
131 : }
132 :
133 : /* -------------------------------------------------------------------- */
134 : /* Initialize any PAM information. */
135 : /* -------------------------------------------------------------------- */
136 13 : poDS->SetDescription(poOpenInfo->pszFilename);
137 13 : poDS->TryLoadXML();
138 :
139 13 : return poDS;
140 : }
141 :
142 65 : BLXDataset::BLXDataset()
143 : {
144 65 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
145 65 : m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
146 65 : }
147 :
148 130 : BLXDataset::~BLXDataset()
149 : {
150 65 : if (!bIsOverview)
151 : {
152 13 : if (blxcontext)
153 : {
154 13 : blxclose(blxcontext);
155 13 : blx_free_context(blxcontext);
156 : }
157 : }
158 130 : }
159 :
160 8 : CPLErr BLXDataset::GetGeoTransform(double *padfTransform)
161 :
162 : {
163 8 : padfTransform[0] = blxcontext->lon;
164 8 : padfTransform[1] = blxcontext->pixelsize_lon;
165 8 : padfTransform[2] = 0.0;
166 8 : padfTransform[3] = blxcontext->lat;
167 8 : padfTransform[4] = 0.0;
168 8 : padfTransform[5] = blxcontext->pixelsize_lat;
169 :
170 8 : return CE_None;
171 : }
172 :
173 6 : const OGRSpatialReference *BLXDataset::GetSpatialRef() const
174 : {
175 6 : return &m_oSRS;
176 : }
177 :
178 65 : BLXRasterBand::BLXRasterBand(BLXDataset *poDSIn, int nBandIn,
179 65 : int overviewLevelIn)
180 65 : : overviewLevel(overviewLevelIn)
181 : {
182 65 : BLXDataset *poGDS = poDSIn;
183 :
184 65 : poDS = poDSIn;
185 65 : nBand = nBandIn;
186 :
187 65 : eDataType = GDT_Int16;
188 :
189 65 : nBlockXSize = poGDS->blxcontext->cell_xsize >> overviewLevel;
190 65 : nBlockYSize = poGDS->blxcontext->cell_ysize >> overviewLevel;
191 65 : }
192 :
193 1 : int BLXRasterBand::GetOverviewCount()
194 : {
195 1 : BLXDataset *poGDS = cpl::down_cast<BLXDataset *>(poDS);
196 1 : return static_cast<int>(poGDS->apoOverviewDS.size());
197 : }
198 :
199 4 : GDALRasterBand *BLXRasterBand::GetOverview(int i)
200 : {
201 4 : BLXDataset *poGDS = cpl::down_cast<BLXDataset *>(poDS);
202 :
203 4 : if (i < 0 || static_cast<size_t>(i) >= poGDS->apoOverviewDS.size())
204 0 : return nullptr;
205 :
206 4 : return poGDS->apoOverviewDS[i]->GetRasterBand(nBand);
207 : }
208 :
209 192 : CPLErr BLXRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
210 :
211 : {
212 192 : BLXDataset *poGDS = reinterpret_cast<BLXDataset *>(poDS);
213 :
214 384 : if (blx_readcell(poGDS->blxcontext, nBlockYOff, nBlockXOff, (short *)pImage,
215 192 : nBlockXSize * nBlockYSize * 2, overviewLevel) == nullptr)
216 : {
217 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to read BLX cell");
218 0 : return CE_Failure;
219 : }
220 :
221 192 : return CE_None;
222 : }
223 :
224 6 : double BLXRasterBand::GetNoDataValue(int *pbSuccess)
225 : {
226 6 : if (pbSuccess)
227 6 : *pbSuccess = TRUE;
228 6 : return BLX_UNDEF;
229 : }
230 :
231 0 : GDALColorInterp BLXRasterBand::GetColorInterpretation(void)
232 : {
233 0 : return GCI_GrayIndex;
234 : }
235 :
236 : /* TODO: check if georeference is the same as for BLX files, WGS84
237 : */
238 20 : static GDALDataset *BLXCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
239 : int bStrict, char **papszOptions,
240 : GDALProgressFunc pfnProgress,
241 : void *pProgressData)
242 :
243 : {
244 : // --------------------------------------------------------------------
245 : // Some rudimentary checks
246 : // --------------------------------------------------------------------
247 20 : const int nBands = poSrcDS->GetRasterCount();
248 20 : if (nBands != 1)
249 : {
250 5 : CPLError(CE_Failure, CPLE_NotSupported,
251 : "BLX driver doesn't support %d bands. Must be 1 (grey) ",
252 : nBands);
253 5 : return nullptr;
254 : }
255 :
256 15 : if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int16 && bStrict)
257 : {
258 10 : CPLError(CE_Failure, CPLE_NotSupported,
259 : "BLX driver doesn't support data type %s. "
260 : "Only 16 bit byte bands supported.\n",
261 : GDALGetDataTypeName(
262 : poSrcDS->GetRasterBand(1)->GetRasterDataType()));
263 :
264 10 : return nullptr;
265 : }
266 :
267 5 : const int nXSize = poSrcDS->GetRasterXSize();
268 5 : const int nYSize = poSrcDS->GetRasterYSize();
269 5 : if ((nXSize % 128 != 0) || (nYSize % 128 != 0))
270 : {
271 1 : CPLError(CE_Failure, CPLE_NotSupported,
272 : "BLX driver doesn't support dimensions that are not a "
273 : "multiple of 128.\n");
274 :
275 1 : return nullptr;
276 : }
277 :
278 : // --------------------------------------------------------------------
279 : // What options has the user selected?
280 : // --------------------------------------------------------------------
281 4 : int zscale = 1;
282 4 : if (CSLFetchNameValue(papszOptions, "ZSCALE") != nullptr)
283 : {
284 0 : zscale = atoi(CSLFetchNameValue(papszOptions, "ZSCALE"));
285 0 : if (zscale < 1)
286 : {
287 0 : CPLError(CE_Failure, CPLE_IllegalArg,
288 : "ZSCALE=%s is not a legal value in the range >= 1.",
289 : CSLFetchNameValue(papszOptions, "ZSCALE"));
290 0 : return nullptr;
291 : }
292 : }
293 :
294 4 : int fillundef = 1;
295 4 : if (CSLFetchNameValue(papszOptions, "FILLUNDEF") != nullptr &&
296 0 : EQUAL(CSLFetchNameValue(papszOptions, "FILLUNDEF"), "NO"))
297 0 : fillundef = 0;
298 :
299 4 : int fillundefval = 0;
300 4 : if (CSLFetchNameValue(papszOptions, "FILLUNDEFVAL") != nullptr)
301 : {
302 0 : fillundefval = atoi(CSLFetchNameValue(papszOptions, "FILLUNDEFVAL"));
303 0 : if ((fillundefval < -32768) || (fillundefval > 32767))
304 : {
305 0 : CPLError(CE_Failure, CPLE_IllegalArg,
306 : "FILLUNDEFVAL=%s is not a legal value in the range "
307 : "-32768, 32767.",
308 : CSLFetchNameValue(papszOptions, "FILLUNDEFVAL"));
309 0 : return nullptr;
310 : }
311 : }
312 :
313 4 : int endian = LITTLEENDIAN;
314 5 : if (CSLFetchNameValue(papszOptions, "BIGENDIAN") != nullptr &&
315 1 : !EQUAL(CSLFetchNameValue(papszOptions, "BIGENDIAN"), "NO"))
316 1 : endian = BIGENDIAN;
317 :
318 : // --------------------------------------------------------------------
319 : // Create the dataset.
320 : // --------------------------------------------------------------------
321 :
322 : // Create a BLX context
323 4 : blxcontext_t *ctx = blx_create_context();
324 :
325 : // Setup BLX parameters
326 4 : ctx->cell_rows = nYSize / ctx->cell_ysize;
327 4 : ctx->cell_cols = nXSize / ctx->cell_xsize;
328 4 : ctx->zscale = zscale;
329 4 : ctx->fillundef = fillundef;
330 4 : ctx->fillundefval = fillundefval;
331 4 : ctx->endian = endian;
332 :
333 4 : if (blxopen(ctx, pszFilename, "wb"))
334 : {
335 2 : CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create blx file %s.\n",
336 : pszFilename);
337 2 : blx_free_context(ctx);
338 2 : return nullptr;
339 : }
340 :
341 : // --------------------------------------------------------------------
342 : // Loop over image, copying image data.
343 : // --------------------------------------------------------------------
344 :
345 2 : GInt16 *pabyTile = (GInt16 *)VSI_MALLOC_VERBOSE(
346 : sizeof(GInt16) * ctx->cell_xsize * ctx->cell_ysize);
347 2 : if (pabyTile == nullptr)
348 : {
349 0 : blxclose(ctx);
350 0 : blx_free_context(ctx);
351 0 : return nullptr;
352 : }
353 :
354 2 : CPLErr eErr = CE_None;
355 2 : if (!pfnProgress(0.0, nullptr, pProgressData))
356 0 : eErr = CE_Failure;
357 :
358 10 : for (int i = 0; (i < ctx->cell_rows) && (eErr == CE_None); i++)
359 40 : for (int j = 0; j < ctx->cell_cols; j++)
360 : {
361 32 : GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
362 64 : eErr = poBand->RasterIO(GF_Read, j * ctx->cell_xsize,
363 32 : i * ctx->cell_ysize, ctx->cell_xsize,
364 : ctx->cell_ysize, pabyTile, ctx->cell_xsize,
365 : ctx->cell_ysize, GDT_Int16, 0, 0, nullptr);
366 32 : if (eErr >= CE_Failure)
367 0 : break;
368 32 : blxdata *celldata = pabyTile;
369 32 : if (blx_writecell(ctx, celldata, i, j) != 0)
370 : {
371 0 : eErr = CE_Failure;
372 0 : break;
373 : }
374 :
375 32 : if (!pfnProgress(1.0 * (i * ctx->cell_cols + j) /
376 32 : (ctx->cell_rows * ctx->cell_cols),
377 : nullptr, pProgressData))
378 : {
379 0 : eErr = CE_Failure;
380 0 : break;
381 : }
382 : }
383 :
384 2 : pfnProgress(1.0, nullptr, pProgressData);
385 :
386 2 : CPLFree(pabyTile);
387 :
388 : double adfGeoTransform[6];
389 2 : if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None)
390 : {
391 2 : ctx->lon = adfGeoTransform[0];
392 2 : ctx->lat = adfGeoTransform[3];
393 2 : ctx->pixelsize_lon = adfGeoTransform[1];
394 2 : ctx->pixelsize_lat = adfGeoTransform[5];
395 : }
396 :
397 2 : blxclose(ctx);
398 2 : blx_free_context(ctx);
399 :
400 2 : if (eErr == CE_None)
401 2 : return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_ReadOnly));
402 :
403 0 : return nullptr;
404 : }
405 :
406 1595 : void GDALRegister_BLX()
407 :
408 : {
409 1595 : if (GDALGetDriverByName("BLX") != nullptr)
410 302 : return;
411 :
412 1293 : GDALDriver *poDriver = new GDALDriver();
413 :
414 1293 : poDriver->SetDescription("BLX");
415 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
416 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Magellan topo (.blx)");
417 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/blx.html");
418 1293 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "blx");
419 :
420 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
421 :
422 1293 : poDriver->pfnOpen = BLXDataset::Open;
423 1293 : poDriver->pfnCreateCopy = BLXCreateCopy;
424 :
425 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
426 : }
|