Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: BSB Reader
4 : * Purpose: BSBDataset implementation for BSB format.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "bsb_read.h"
15 : #include "cpl_string.h"
16 : #include "gdal_frmts.h"
17 : #include "gdal_pam.h"
18 : #include "ogr_spatialref.h"
19 :
20 : #include <cstdlib>
21 : #include <algorithm>
22 :
23 : // Disabled as people may worry about the BSB patent
24 : // #define BSB_CREATE
25 :
26 : /************************************************************************/
27 : /* ==================================================================== */
28 : /* BSBDataset */
29 : /* ==================================================================== */
30 : /************************************************************************/
31 :
32 : class BSBRasterBand;
33 :
34 : class BSBDataset final : public GDALPamDataset
35 : {
36 : int nGCPCount;
37 : GDAL_GCP *pasGCPList;
38 : OGRSpatialReference m_oGCPSRS{};
39 :
40 : double adfGeoTransform[6];
41 : int bGeoTransformSet;
42 :
43 : void ScanForGCPs(bool isNos, const char *pszFilename);
44 : void ScanForGCPsNos(const char *pszFilename);
45 : void ScanForGCPsBSB();
46 :
47 : void ScanForCutline();
48 :
49 : static int IdentifyInternal(GDALOpenInfo *, bool &isNosOut);
50 :
51 : public:
52 : BSBDataset();
53 : ~BSBDataset() override;
54 :
55 : BSBInfo *psInfo;
56 :
57 : static GDALDataset *Open(GDALOpenInfo *);
58 : static int Identify(GDALOpenInfo *);
59 :
60 : int GetGCPCount() override;
61 : const OGRSpatialReference *GetSpatialRef() const override;
62 : const GDAL_GCP *GetGCPs() override;
63 :
64 : CPLErr GetGeoTransform(double *padfTransform) override;
65 : const OGRSpatialReference *GetGCPSpatialRef() const override;
66 : };
67 :
68 : /************************************************************************/
69 : /* ==================================================================== */
70 : /* BSBRasterBand */
71 : /* ==================================================================== */
72 : /************************************************************************/
73 :
74 : class BSBRasterBand final : public GDALPamRasterBand
75 : {
76 : GDALColorTable oCT;
77 :
78 : public:
79 : explicit BSBRasterBand(BSBDataset *);
80 :
81 : CPLErr IReadBlock(int, int, void *) override;
82 : GDALColorTable *GetColorTable() override;
83 : GDALColorInterp GetColorInterpretation() override;
84 : };
85 :
86 : /************************************************************************/
87 : /* BSBRasterBand() */
88 : /************************************************************************/
89 :
90 13 : BSBRasterBand::BSBRasterBand(BSBDataset *poDSIn)
91 :
92 : {
93 13 : poDS = poDSIn;
94 13 : nBand = 1;
95 :
96 13 : eDataType = GDT_Byte;
97 :
98 13 : nBlockXSize = poDS->GetRasterXSize();
99 13 : nBlockYSize = 1;
100 :
101 : // Note that the first color table entry is dropped, everything is
102 : // shifted down.
103 1430 : for (int i = 0; i < poDSIn->psInfo->nPCTSize - 1; i++)
104 : {
105 1417 : GDALColorEntry oColor = {poDSIn->psInfo->pabyPCT[i * 3 + 0 + 3],
106 1417 : poDSIn->psInfo->pabyPCT[i * 3 + 1 + 3],
107 1417 : poDSIn->psInfo->pabyPCT[i * 3 + 2 + 3], 255};
108 :
109 1417 : oCT.SetColorEntry(i, &oColor);
110 : }
111 13 : }
112 :
113 : /************************************************************************/
114 : /* IReadBlock() */
115 : /************************************************************************/
116 :
117 250 : CPLErr BSBRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
118 : void *pImage)
119 : {
120 250 : BSBDataset *poGDS = (BSBDataset *)poDS;
121 250 : GByte *pabyScanline = (GByte *)pImage;
122 :
123 250 : if (BSBReadScanline(poGDS->psInfo, nBlockYOff, pabyScanline))
124 : {
125 12648 : for (int i = 0; i < nBlockXSize; i++)
126 : {
127 : /* The indices start at 1, except in case of some charts */
128 : /* where there are missing values, which are filled to 0 */
129 : /* by BSBReadScanline */
130 12400 : if (pabyScanline[i] > 0)
131 12400 : pabyScanline[i] -= 1;
132 : }
133 :
134 248 : return CE_None;
135 : }
136 :
137 2 : return CE_Failure;
138 : }
139 :
140 : /************************************************************************/
141 : /* GetColorTable() */
142 : /************************************************************************/
143 :
144 0 : GDALColorTable *BSBRasterBand::GetColorTable()
145 :
146 : {
147 0 : return &oCT;
148 : }
149 :
150 : /************************************************************************/
151 : /* GetColorInterpretation() */
152 : /************************************************************************/
153 :
154 0 : GDALColorInterp BSBRasterBand::GetColorInterpretation()
155 :
156 : {
157 0 : return GCI_PaletteIndex;
158 : }
159 :
160 : /************************************************************************/
161 : /* ==================================================================== */
162 : /* BSBDataset */
163 : /* ==================================================================== */
164 : /************************************************************************/
165 :
166 : /************************************************************************/
167 : /* BSBDataset() */
168 : /************************************************************************/
169 :
170 13 : BSBDataset::BSBDataset()
171 : : nGCPCount(0), pasGCPList(nullptr), bGeoTransformSet(FALSE),
172 13 : psInfo(nullptr)
173 : {
174 13 : m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
175 13 : m_oGCPSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
176 :
177 13 : adfGeoTransform[0] = 0.0; /* X Origin (top left corner) */
178 13 : adfGeoTransform[1] = 1.0; /* X Pixel size */
179 13 : adfGeoTransform[2] = 0.0;
180 13 : adfGeoTransform[3] = 0.0; /* Y Origin (top left corner) */
181 13 : adfGeoTransform[4] = 0.0;
182 13 : adfGeoTransform[5] = 1.0; /* Y Pixel Size */
183 13 : }
184 :
185 : /************************************************************************/
186 : /* ~BSBDataset() */
187 : /************************************************************************/
188 :
189 26 : BSBDataset::~BSBDataset()
190 :
191 : {
192 13 : FlushCache(true);
193 :
194 13 : GDALDeinitGCPs(nGCPCount, pasGCPList);
195 13 : CPLFree(pasGCPList);
196 :
197 13 : if (psInfo != nullptr)
198 13 : BSBClose(psInfo);
199 26 : }
200 :
201 : /************************************************************************/
202 : /* GetGeoTransform() */
203 : /************************************************************************/
204 :
205 1 : CPLErr BSBDataset::GetGeoTransform(double *padfTransform)
206 :
207 : {
208 1 : memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
209 :
210 1 : if (bGeoTransformSet)
211 1 : return CE_None;
212 :
213 0 : return CE_Failure;
214 : }
215 :
216 : /************************************************************************/
217 : /* GetSpatialRef() */
218 : /************************************************************************/
219 :
220 1 : const OGRSpatialReference *BSBDataset::GetSpatialRef() const
221 :
222 : {
223 1 : if (bGeoTransformSet)
224 1 : return &m_oGCPSRS;
225 :
226 0 : return nullptr;
227 : }
228 :
229 : /************************************************************************/
230 : /* GDALHeuristicDatelineWrap() */
231 : /************************************************************************/
232 :
233 3 : static void GDALHeuristicDatelineWrap(int nPointCount, double *padfX)
234 :
235 : {
236 3 : if (nPointCount < 2)
237 3 : return;
238 :
239 : /* -------------------------------------------------------------------- */
240 : /* Work out what the longitude range will be centering on the */
241 : /* prime meridian (-180 to 180) and centering on the dateline */
242 : /* (0 to 360). */
243 : /* -------------------------------------------------------------------- */
244 : /* Following inits are useless but keep GCC happy */
245 3 : double dfX_PM_Min = 0.0;
246 3 : double dfX_PM_Max = 0.0;
247 3 : double dfX_Dateline_Min = 0.0;
248 3 : double dfX_Dateline_Max = 0.0;
249 :
250 110 : for (int i = 0; i < nPointCount; i++)
251 : {
252 107 : double dfX_PM = padfX[i];
253 107 : if (dfX_PM > 180)
254 0 : dfX_PM -= 360.0;
255 :
256 107 : double dfX_Dateline = padfX[i];
257 107 : if (dfX_Dateline < 0)
258 0 : dfX_Dateline += 360.0;
259 :
260 107 : if (i == 0)
261 : {
262 3 : dfX_PM_Min = dfX_PM;
263 3 : dfX_PM_Max = dfX_PM;
264 3 : dfX_Dateline_Min = dfX_Dateline;
265 3 : dfX_Dateline_Max = dfX_Dateline;
266 : }
267 : else
268 : {
269 104 : dfX_PM_Min = std::min(dfX_PM_Min, dfX_PM);
270 104 : dfX_PM_Max = std::max(dfX_PM_Max, dfX_PM);
271 104 : dfX_Dateline_Min = std::min(dfX_Dateline_Min, dfX_Dateline);
272 104 : dfX_Dateline_Max = std::max(dfX_Dateline_Max, dfX_Dateline);
273 : }
274 : }
275 :
276 : /* -------------------------------------------------------------------- */
277 : /* Do nothing if the range is always fairly small - no apparent */
278 : /* wrapping issues. */
279 : /* -------------------------------------------------------------------- */
280 3 : if ((dfX_PM_Max - dfX_PM_Min) < 270.0 &&
281 3 : (dfX_Dateline_Max - dfX_Dateline_Min) < 270.0)
282 3 : return;
283 :
284 : /* -------------------------------------------------------------------- */
285 : /* Do nothing if both approach have a wide range - best not to */
286 : /* fiddle if we aren't sure we are improving things. */
287 : /* -------------------------------------------------------------------- */
288 0 : if ((dfX_PM_Max - dfX_PM_Min) > 270.0 &&
289 0 : (dfX_Dateline_Max - dfX_Dateline_Min) > 270.0)
290 0 : return;
291 :
292 : /* -------------------------------------------------------------------- */
293 : /* Pick which way to transform things. */
294 : /* -------------------------------------------------------------------- */
295 : bool bUsePMWrap;
296 :
297 0 : if ((dfX_PM_Max - dfX_PM_Min) > 270.0 &&
298 0 : (dfX_Dateline_Max - dfX_Dateline_Min) < 270.0)
299 0 : bUsePMWrap = false;
300 : else
301 0 : bUsePMWrap = true;
302 :
303 : /* -------------------------------------------------------------------- */
304 : /* Apply rewrapping. */
305 : /* -------------------------------------------------------------------- */
306 0 : for (int i = 0; i < nPointCount; i++)
307 : {
308 0 : if (bUsePMWrap)
309 : {
310 0 : if (padfX[i] > 180)
311 0 : padfX[i] -= 360.0;
312 : }
313 : else
314 : {
315 0 : if (padfX[i] < 0)
316 0 : padfX[i] += 360.0;
317 : }
318 : }
319 : }
320 :
321 : /************************************************************************/
322 : /* GDALHeuristicDatelineWrapGCPs() */
323 : /************************************************************************/
324 :
325 3 : static void GDALHeuristicDatelineWrapGCPs(int nPointCount, GDAL_GCP *pasGCPList)
326 : {
327 6 : std::vector<double> oadfX;
328 :
329 3 : oadfX.resize(nPointCount);
330 110 : for (int i = 0; i < nPointCount; i++)
331 107 : oadfX[i] = pasGCPList[i].dfGCPX;
332 :
333 3 : GDALHeuristicDatelineWrap(nPointCount, &(oadfX[0]));
334 :
335 110 : for (int i = 0; i < nPointCount; i++)
336 107 : pasGCPList[i].dfGCPX = oadfX[i];
337 3 : }
338 :
339 : /************************************************************************/
340 : /* ScanForGCPs() */
341 : /************************************************************************/
342 :
343 13 : void BSBDataset::ScanForGCPs(bool isNos, const char *pszFilename)
344 :
345 : {
346 : /* -------------------------------------------------------------------- */
347 : /* Collect GCPs as appropriate to source. */
348 : /* -------------------------------------------------------------------- */
349 13 : nGCPCount = 0;
350 :
351 13 : if (isNos)
352 : {
353 0 : ScanForGCPsNos(pszFilename);
354 : }
355 : else
356 : {
357 13 : ScanForGCPsBSB();
358 : }
359 :
360 : /* -------------------------------------------------------------------- */
361 : /* Apply heuristics to re-wrap GCPs to maintain continuity */
362 : /* over the international dateline. */
363 : /* -------------------------------------------------------------------- */
364 13 : if (nGCPCount > 1)
365 3 : GDALHeuristicDatelineWrapGCPs(nGCPCount, pasGCPList);
366 :
367 : /* -------------------------------------------------------------------- */
368 : /* Collect coordinate system related parameters from header. */
369 : /* -------------------------------------------------------------------- */
370 13 : const char *pszKNP = nullptr;
371 13 : const char *pszKNQ = nullptr;
372 :
373 1765 : for (int i = 0; psInfo->papszHeader[i] != nullptr; i++)
374 : {
375 1752 : if (STARTS_WITH_CI(psInfo->papszHeader[i], "KNP/"))
376 : {
377 13 : pszKNP = psInfo->papszHeader[i];
378 13 : SetMetadataItem("BSB_KNP", pszKNP + 4);
379 : }
380 1752 : if (STARTS_WITH_CI(psInfo->papszHeader[i], "KNQ/"))
381 : {
382 3 : pszKNQ = psInfo->papszHeader[i];
383 3 : SetMetadataItem("BSB_KNQ", pszKNQ + 4);
384 : }
385 : }
386 :
387 : /* -------------------------------------------------------------------- */
388 : /* Can we derive a reasonable coordinate system definition for */
389 : /* this file? For now we keep it simple, just handling */
390 : /* mercator. In the future we should consider others. */
391 : /* -------------------------------------------------------------------- */
392 26 : CPLString osUnderlyingSRS;
393 13 : if (pszKNP != nullptr)
394 : {
395 13 : const char *pszPR = strstr(pszKNP, "PR=");
396 13 : const char *pszGD = strstr(pszKNP, "GD=");
397 13 : const char *pszGEOGCS = SRS_WKT_WGS84_LAT_LONG;
398 26 : CPLString osPP;
399 :
400 : // Capture the PP string.
401 13 : const char *pszValue = strstr(pszKNP, "PP=");
402 13 : const char *pszEnd = pszValue ? strstr(pszValue, ",") : nullptr;
403 13 : if (pszValue && pszEnd)
404 13 : osPP.assign(pszValue + 3, pszEnd - pszValue - 3);
405 :
406 : // Look at the datum
407 13 : if (pszGD == nullptr)
408 : {
409 : /* no match. We'll default to EPSG:4326 */
410 : }
411 13 : else if (STARTS_WITH_CI(pszGD, "GD=European 1950"))
412 : {
413 0 : pszGEOGCS =
414 : "GEOGCS[\"ED50\",DATUM[\"European_Datum_1950\",SPHEROID["
415 : "\"International "
416 : "1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],TOWGS84[-87,-"
417 : "98,-121,0,0,0,0],AUTHORITY[\"EPSG\",\"6230\"]],PRIMEM["
418 : "\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\","
419 : "0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY["
420 : "\"EPSG\",\"4230\"]]";
421 : }
422 :
423 : // Look at the projection
424 13 : if (pszPR == nullptr)
425 : {
426 : /* no match */
427 : }
428 13 : else if (STARTS_WITH_CI(pszPR, "PR=MERCATOR") && nGCPCount > 0)
429 : {
430 : // We somewhat arbitrarily select our first GCPX as our
431 : // central meridian. This is mostly helpful to ensure
432 : // that regions crossing the dateline will be contiguous
433 : // in mercator.
434 1 : osUnderlyingSRS.Printf(
435 : "PROJCS[\"Global "
436 : "Mercator\",%s,PROJECTION[\"Mercator_2SP\"],PARAMETER["
437 : "\"standard_parallel_1\",0],PARAMETER[\"latitude_of_origin\",0]"
438 : ",PARAMETER[\"central_meridian\",%d],PARAMETER[\"false_"
439 : "easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]"
440 : "]",
441 1 : pszGEOGCS, (int)pasGCPList[0].dfGCPX);
442 : }
443 :
444 13 : else if (STARTS_WITH_CI(pszPR, "PR=TRANSVERSE MERCATOR") &&
445 1 : !osPP.empty())
446 : {
447 :
448 : osUnderlyingSRS.Printf(
449 : "PROJCS[\"unnamed\",%s,PROJECTION[\"Transverse_Mercator\"],"
450 : "PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_"
451 : "meridian\",%s],PARAMETER[\"scale_factor\",1],PARAMETER["
452 : "\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT["
453 : "\"Meter\",1]]",
454 1 : pszGEOGCS, osPP.c_str());
455 : }
456 :
457 11 : else if (STARTS_WITH_CI(pszPR, "PR=UNIVERSAL TRANSVERSE MERCATOR") &&
458 0 : !osPP.empty())
459 : {
460 : // This is not *really* UTM unless the central meridian
461 : // matches a zone which it does not in some (most?) maps.
462 : osUnderlyingSRS.Printf(
463 : "PROJCS[\"unnamed\",%s,PROJECTION[\"Transverse_Mercator\"],"
464 : "PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_"
465 : "meridian\",%s],PARAMETER[\"scale_factor\",0.9996],PARAMETER["
466 : "\"false_easting\",500000],PARAMETER[\"false_northing\",0],"
467 : "UNIT[\"Meter\",1]]",
468 0 : pszGEOGCS, osPP.c_str());
469 : }
470 :
471 11 : else if (STARTS_WITH_CI(pszPR, "PR=POLYCONIC") && !osPP.empty())
472 : {
473 : osUnderlyingSRS.Printf(
474 : "PROJCS[\"unnamed\",%s,PROJECTION[\"Polyconic\"],PARAMETER["
475 : "\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",%s],"
476 : "PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]"
477 : ",UNIT[\"Meter\",1]]",
478 0 : pszGEOGCS, osPP.c_str());
479 : }
480 :
481 23 : else if (STARTS_WITH_CI(pszPR, "PR=LAMBERT CONFORMAL CONIC") &&
482 11 : !osPP.empty() && pszKNQ != nullptr)
483 : {
484 2 : CPLString osP2, osP3;
485 :
486 : // Capture the KNQ/P2 string.
487 1 : pszValue = strstr(pszKNQ, "P2=");
488 1 : if (pszValue)
489 1 : pszEnd = strstr(pszValue, ",");
490 1 : if (pszValue && pszEnd)
491 1 : osP2.assign(pszValue + 3, pszEnd - pszValue - 3);
492 :
493 : // Capture the KNQ/P3 string.
494 1 : pszValue = strstr(pszKNQ, "P3=");
495 1 : if (pszValue)
496 1 : pszEnd = strstr(pszValue, ",");
497 1 : if (pszValue)
498 : {
499 1 : if (pszEnd)
500 1 : osP3.assign(pszValue + 3, pszEnd - pszValue - 3);
501 : else
502 0 : osP3.assign(pszValue + 3);
503 : }
504 :
505 1 : if (!osP2.empty() && !osP3.empty())
506 : osUnderlyingSRS.Printf(
507 : "PROJCS[\"unnamed\",%s,PROJECTION[\"Lambert_Conformal_"
508 : "Conic_2SP\"],PARAMETER[\"standard_parallel_1\",%s],"
509 : "PARAMETER[\"standard_parallel_2\",%s],PARAMETER["
510 : "\"latitude_of_origin\",0.0],PARAMETER[\"central_"
511 : "meridian\",%s],PARAMETER[\"false_easting\",0.0],PARAMETER["
512 : "\"false_northing\",0.0],UNIT[\"Meter\",1]]",
513 1 : pszGEOGCS, osP2.c_str(), osP3.c_str(), osPP.c_str());
514 : }
515 : }
516 :
517 : /* -------------------------------------------------------------------- */
518 : /* If we got an alternate underlying coordinate system, try */
519 : /* converting the GCPs to that coordinate system. */
520 : /* -------------------------------------------------------------------- */
521 13 : if (osUnderlyingSRS.length() > 0)
522 : {
523 6 : OGRSpatialReference oGeog_SRS, oProjected_SRS;
524 :
525 3 : oProjected_SRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
526 3 : oProjected_SRS.SetFromUserInput(osUnderlyingSRS);
527 3 : oGeog_SRS.CopyGeogCSFrom(&oProjected_SRS);
528 3 : oGeog_SRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
529 :
530 : OGRCoordinateTransformation *poCT =
531 3 : OGRCreateCoordinateTransformation(&oGeog_SRS, &oProjected_SRS);
532 3 : if (poCT != nullptr)
533 : {
534 110 : for (int i = 0; i < nGCPCount; i++)
535 : {
536 107 : poCT->Transform(1, &(pasGCPList[i].dfGCPX),
537 107 : &(pasGCPList[i].dfGCPY),
538 107 : &(pasGCPList[i].dfGCPZ));
539 : }
540 :
541 3 : m_oGCPSRS.importFromWkt(osUnderlyingSRS.c_str());
542 :
543 3 : delete poCT;
544 : }
545 : else
546 0 : CPLErrorReset();
547 : }
548 :
549 : /* -------------------------------------------------------------------- */
550 : /* Attempt to prepare a geotransform from the GCPs. */
551 : /* -------------------------------------------------------------------- */
552 13 : if (GDALGCPsToGeoTransform(nGCPCount, pasGCPList, adfGeoTransform, FALSE))
553 : {
554 2 : bGeoTransformSet = TRUE;
555 : }
556 13 : }
557 :
558 : /************************************************************************/
559 : /* ScanForGCPsNos() */
560 : /* */
561 : /* Nos files have an accompanying .geo file, that contains some */
562 : /* of the information normally contained in the header section */
563 : /* with BSB files. we try and open a file with the same name, */
564 : /* but a .geo extension, and look for lines like... */
565 : /* PointX=long lat line pixel (using the same naming system */
566 : /* as BSB) Point1=-22.0000 64.250000 197 744 */
567 : /************************************************************************/
568 :
569 0 : void BSBDataset::ScanForGCPsNos(const char *pszFilename)
570 : {
571 0 : const char *extension = CPLGetExtension(pszFilename);
572 :
573 : // pseudointelligently try and guess whether we want a .geo or a .GEO
574 0 : const char *geofile = nullptr;
575 0 : if (extension[1] == 'O')
576 : {
577 0 : geofile = CPLResetExtension(pszFilename, "GEO");
578 : }
579 : else
580 : {
581 0 : geofile = CPLResetExtension(pszFilename, "geo");
582 : }
583 :
584 0 : FILE *gfp = VSIFOpen(geofile, "r"); // Text files
585 0 : if (gfp == nullptr)
586 : {
587 0 : CPLError(CE_Failure, CPLE_OpenFailed,
588 : "Couldn't find a matching .GEO file: %s", geofile);
589 0 : return;
590 : }
591 :
592 0 : char *thisLine = (char *)CPLMalloc(80); // FIXME
593 :
594 : // Count the GCPs (reference points) and seek the file pointer 'gfp' to the
595 : // starting point
596 0 : int fileGCPCount = 0;
597 0 : while (fgets(thisLine, 80, gfp))
598 : {
599 0 : if (STARTS_WITH_CI(thisLine, "Point"))
600 0 : fileGCPCount++;
601 : }
602 0 : VSIRewind(gfp);
603 :
604 : // Memory has not been allocated to fileGCPCount yet
605 0 : pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), fileGCPCount + 1);
606 :
607 0 : while (fgets(thisLine, 80, gfp))
608 : {
609 0 : if (STARTS_WITH_CI(thisLine, "Point"))
610 : {
611 : // got a point line, turn it into a gcp
612 : char **Tokens =
613 0 : CSLTokenizeStringComplex(thisLine, "= ", FALSE, FALSE);
614 0 : if (CSLCount(Tokens) >= 5)
615 : {
616 0 : GDALInitGCPs(1, pasGCPList + nGCPCount);
617 0 : pasGCPList[nGCPCount].dfGCPX = CPLAtof(Tokens[1]);
618 0 : pasGCPList[nGCPCount].dfGCPY = CPLAtof(Tokens[2]);
619 0 : pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(Tokens[4]);
620 0 : pasGCPList[nGCPCount].dfGCPLine = CPLAtof(Tokens[3]);
621 :
622 0 : CPLFree(pasGCPList[nGCPCount].pszId);
623 : char szName[50];
624 0 : snprintf(szName, sizeof(szName), "GCP_%d", nGCPCount + 1);
625 0 : pasGCPList[nGCPCount].pszId = CPLStrdup(szName);
626 :
627 0 : nGCPCount++;
628 : }
629 0 : CSLDestroy(Tokens);
630 : }
631 : }
632 :
633 0 : CPLFree(thisLine);
634 0 : VSIFClose(gfp);
635 : }
636 :
637 : /************************************************************************/
638 : /* ScanForGCPsBSB() */
639 : /************************************************************************/
640 :
641 13 : void BSBDataset::ScanForGCPsBSB()
642 : {
643 : /* -------------------------------------------------------------------- */
644 : /* Collect standalone GCPs. They look like: */
645 : /* */
646 : /* REF/1,115,2727,32.346666666667,-60.881666666667 */
647 : /* REF/n,pixel,line,lat,long */
648 : /* -------------------------------------------------------------------- */
649 13 : int fileGCPCount = 0;
650 :
651 : // Count the GCPs (reference points) in psInfo->papszHeader
652 1765 : for (int i = 0; psInfo->papszHeader[i] != nullptr; i++)
653 1752 : if (STARTS_WITH_CI(psInfo->papszHeader[i], "REF/"))
654 107 : fileGCPCount++;
655 :
656 : // Memory has not been allocated to fileGCPCount yet
657 13 : pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), fileGCPCount + 1);
658 :
659 1765 : for (int i = 0; psInfo->papszHeader[i] != nullptr; i++)
660 : {
661 :
662 1752 : if (!STARTS_WITH_CI(psInfo->papszHeader[i], "REF/"))
663 1645 : continue;
664 :
665 214 : char **papszTokens = CSLTokenizeStringComplex(
666 107 : psInfo->papszHeader[i] + 4, ",", FALSE, FALSE);
667 :
668 107 : if (CSLCount(papszTokens) > 4)
669 : {
670 107 : GDALInitGCPs(1, pasGCPList + nGCPCount);
671 :
672 107 : pasGCPList[nGCPCount].dfGCPX = CPLAtof(papszTokens[4]);
673 107 : pasGCPList[nGCPCount].dfGCPY = CPLAtof(papszTokens[3]);
674 107 : pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(papszTokens[1]);
675 107 : pasGCPList[nGCPCount].dfGCPLine = CPLAtof(papszTokens[2]);
676 :
677 107 : CPLFree(pasGCPList[nGCPCount].pszId);
678 107 : if (CSLCount(papszTokens) > 5)
679 : {
680 0 : pasGCPList[nGCPCount].pszId = CPLStrdup(papszTokens[5]);
681 : }
682 : else
683 : {
684 : char szName[50];
685 107 : snprintf(szName, sizeof(szName), "GCP_%d", nGCPCount + 1);
686 107 : pasGCPList[nGCPCount].pszId = CPLStrdup(szName);
687 : }
688 :
689 107 : nGCPCount++;
690 : }
691 107 : CSLDestroy(papszTokens);
692 : }
693 13 : }
694 :
695 : /************************************************************************/
696 : /* ScanForCutline() */
697 : /************************************************************************/
698 :
699 13 : void BSBDataset::ScanForCutline()
700 : {
701 : /* PLY: Border Polygon Record - coordinates of the panel within the
702 : * raster image, given in chart datum lat/long. Any shape polygon.
703 : * They look like:
704 : * PLY/1,32.346666666667,-60.881666666667
705 : * PLY/n,lat,long
706 : *
707 : * If found then we return it via a BSB_CUTLINE metadata item as a WKT
708 : * POLYGON.
709 : */
710 :
711 26 : std::string wkt;
712 1765 : for (int i = 0; psInfo->papszHeader[i] != nullptr; i++)
713 : {
714 1752 : if (!STARTS_WITH_CI(psInfo->papszHeader[i], "PLY/"))
715 1731 : continue;
716 :
717 : const CPLStringList aosTokens(
718 42 : CSLTokenizeString2(psInfo->papszHeader[i] + 4, ",", 0));
719 :
720 21 : if (aosTokens.size() >= 3)
721 : {
722 21 : if (wkt.empty())
723 2 : wkt = "POLYGON ((";
724 : else
725 19 : wkt += ',';
726 21 : wkt += aosTokens[2];
727 21 : wkt += ' ';
728 21 : wkt += aosTokens[1];
729 : }
730 : }
731 :
732 13 : if (!wkt.empty())
733 : {
734 2 : wkt += "))";
735 2 : SetMetadataItem("BSB_CUTLINE", wkt.c_str());
736 : }
737 13 : }
738 :
739 : /************************************************************************/
740 : /* IdentifyInternal() */
741 : /************************************************************************/
742 :
743 55666 : int BSBDataset::IdentifyInternal(GDALOpenInfo *poOpenInfo, bool &isNosOut)
744 :
745 : {
746 : /* -------------------------------------------------------------------- */
747 : /* Check for BSB/ keyword. */
748 : /* -------------------------------------------------------------------- */
749 55666 : isNosOut = false;
750 :
751 55666 : if (poOpenInfo->nHeaderBytes < 1000)
752 51408 : return FALSE;
753 :
754 4258 : int i = 0;
755 5601180 : for (; i < poOpenInfo->nHeaderBytes - 4; i++)
756 : {
757 5596940 : if (poOpenInfo->pabyHeader[i + 0] == 'B' &&
758 16782 : poOpenInfo->pabyHeader[i + 1] == 'S' &&
759 102 : poOpenInfo->pabyHeader[i + 2] == 'B' &&
760 28 : poOpenInfo->pabyHeader[i + 3] == '/')
761 26 : break;
762 5596920 : if (poOpenInfo->pabyHeader[i + 0] == 'N' &&
763 17913 : poOpenInfo->pabyHeader[i + 1] == 'O' &&
764 634 : poOpenInfo->pabyHeader[i + 2] == 'S' &&
765 19 : poOpenInfo->pabyHeader[i + 3] == '/')
766 : {
767 0 : isNosOut = true;
768 0 : break;
769 : }
770 5596920 : if (poOpenInfo->pabyHeader[i + 0] == 'W' &&
771 5227 : poOpenInfo->pabyHeader[i + 1] == 'X' &&
772 143 : poOpenInfo->pabyHeader[i + 2] == '\\' &&
773 0 : poOpenInfo->pabyHeader[i + 3] == '8')
774 0 : break;
775 : }
776 :
777 4258 : if (i == poOpenInfo->nHeaderBytes - 4)
778 4232 : return FALSE;
779 :
780 : /* Additional test to avoid false positive. See #2881 */
781 26 : const char *pszHeader =
782 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
783 26 : const char *pszShiftedHeader = pszHeader + i;
784 26 : const char *pszRA = strstr(pszShiftedHeader, "RA=");
785 26 : if (pszRA == nullptr) /* This may be a NO1 file */
786 0 : pszRA = strstr(pszShiftedHeader, "[JF");
787 26 : if (pszRA == nullptr)
788 0 : return FALSE;
789 26 : if (pszRA - pszShiftedHeader > 100 && !strstr(pszHeader, "VER/") &&
790 0 : !strstr(pszHeader, "KNP/") && !strstr(pszHeader, "KNQ/") &&
791 0 : !strstr(pszHeader, "RGB/"))
792 0 : return FALSE;
793 :
794 26 : return TRUE;
795 : }
796 :
797 : /************************************************************************/
798 : /* Identify() */
799 : /************************************************************************/
800 :
801 55654 : int BSBDataset::Identify(GDALOpenInfo *poOpenInfo)
802 :
803 : {
804 : bool isNos;
805 55654 : return IdentifyInternal(poOpenInfo, isNos);
806 : }
807 :
808 : /************************************************************************/
809 : /* Open() */
810 : /************************************************************************/
811 :
812 13 : GDALDataset *BSBDataset::Open(GDALOpenInfo *poOpenInfo)
813 :
814 : {
815 13 : bool isNos = false;
816 13 : if (!IdentifyInternal(poOpenInfo, isNos))
817 0 : return nullptr;
818 :
819 13 : if (poOpenInfo->eAccess == GA_Update)
820 : {
821 0 : CPLError(CE_Failure, CPLE_NotSupported,
822 : "The BSB driver does not support update access to existing"
823 : " datasets.\n");
824 0 : return nullptr;
825 : }
826 :
827 : /* -------------------------------------------------------------------- */
828 : /* Create a corresponding GDALDataset. */
829 : /* -------------------------------------------------------------------- */
830 13 : BSBDataset *poDS = new BSBDataset();
831 :
832 : /* -------------------------------------------------------------------- */
833 : /* Open the file. */
834 : /* -------------------------------------------------------------------- */
835 13 : poDS->psInfo = BSBOpen(poOpenInfo->pszFilename);
836 13 : if (poDS->psInfo == nullptr)
837 : {
838 0 : delete poDS;
839 0 : return nullptr;
840 : }
841 :
842 13 : poDS->nRasterXSize = poDS->psInfo->nXSize;
843 13 : poDS->nRasterYSize = poDS->psInfo->nYSize;
844 :
845 : /* -------------------------------------------------------------------- */
846 : /* Create band information objects. */
847 : /* -------------------------------------------------------------------- */
848 13 : poDS->SetBand(1, new BSBRasterBand(poDS));
849 :
850 13 : poDS->ScanForGCPs(isNos, poOpenInfo->pszFilename);
851 :
852 : /* -------------------------------------------------------------------- */
853 : /* Set CUTLINE metadata if a bounding polygon is available */
854 : /* -------------------------------------------------------------------- */
855 13 : poDS->ScanForCutline();
856 :
857 : /* -------------------------------------------------------------------- */
858 : /* Initialize any PAM information. */
859 : /* -------------------------------------------------------------------- */
860 13 : poDS->SetDescription(poOpenInfo->pszFilename);
861 13 : poDS->TryLoadXML();
862 :
863 13 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
864 :
865 13 : return poDS;
866 : }
867 :
868 : /************************************************************************/
869 : /* GetGCPCount() */
870 : /************************************************************************/
871 :
872 2 : int BSBDataset::GetGCPCount()
873 :
874 : {
875 2 : return nGCPCount;
876 : }
877 :
878 : /************************************************************************/
879 : /* GetGCPSpatialRef() */
880 : /************************************************************************/
881 :
882 1 : const OGRSpatialReference *BSBDataset::GetGCPSpatialRef() const
883 :
884 : {
885 1 : return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
886 : }
887 :
888 : /************************************************************************/
889 : /* GetGCP() */
890 : /************************************************************************/
891 :
892 1 : const GDAL_GCP *BSBDataset::GetGCPs()
893 :
894 : {
895 1 : return pasGCPList;
896 : }
897 :
898 : #ifdef BSB_CREATE
899 :
900 : /************************************************************************/
901 : /* BSBIsSRSOK() */
902 : /************************************************************************/
903 :
904 : static int BSBIsSRSOK(const char *pszWKT)
905 : {
906 : bool bOK = false;
907 : OGRSpatialReference oSRS, oSRS_WGS84, oSRS_NAD83;
908 :
909 : if (pszWKT != NULL && pszWKT[0] != '\0')
910 : {
911 : oSRS.importFromWkt(pszWKT);
912 :
913 : oSRS_WGS84.SetWellKnownGeogCS("WGS84");
914 : oSRS_NAD83.SetWellKnownGeogCS("NAD83");
915 : if ((oSRS.IsSameGeogCS(&oSRS_WGS84) ||
916 : oSRS.IsSameGeogCS(&oSRS_NAD83)) &&
917 : oSRS.IsGeographic() && oSRS.GetPrimeMeridian() == 0.0)
918 : {
919 : bOK = true;
920 : }
921 : }
922 :
923 : if (!bOK)
924 : {
925 : CPLError(CE_Warning, CPLE_NotSupported,
926 : "BSB only supports WGS84 or NAD83 geographic projections.\n");
927 : }
928 :
929 : return bOK;
930 : }
931 :
932 : /************************************************************************/
933 : /* BSBCreateCopy() */
934 : /************************************************************************/
935 :
936 : static GDALDataset *BSBCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
937 : int bStrict, char ** /*papszOptions*/,
938 : GDALProgressFunc /*pfnProgress*/,
939 : void * /*pProgressData*/)
940 :
941 : {
942 : /* -------------------------------------------------------------------- */
943 : /* Some some rudimentary checks */
944 : /* -------------------------------------------------------------------- */
945 : const int nBands = poSrcDS->GetRasterCount();
946 : if (nBands != 1)
947 : {
948 : CPLError(CE_Failure, CPLE_NotSupported,
949 : "BSB driver only supports one band images.\n");
950 :
951 : return NULL;
952 : }
953 :
954 : if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte && bStrict)
955 : {
956 : CPLError(CE_Failure, CPLE_NotSupported,
957 : "BSB driver doesn't support data type %s. "
958 : "Only eight bit bands supported.\n",
959 : GDALGetDataTypeName(
960 : poSrcDS->GetRasterBand(1)->GetRasterDataType()));
961 :
962 : return NULL;
963 : }
964 :
965 : const int nXSize = poSrcDS->GetRasterXSize();
966 : const int nYSize = poSrcDS->GetRasterYSize();
967 :
968 : /* -------------------------------------------------------------------- */
969 : /* Open the output file. */
970 : /* -------------------------------------------------------------------- */
971 : BSBInfo *psBSB = BSBCreate(pszFilename, 0, 200, nXSize, nYSize);
972 : if (psBSB == NULL)
973 : return NULL;
974 :
975 : /* -------------------------------------------------------------------- */
976 : /* Prepare initial color table.colortable. */
977 : /* -------------------------------------------------------------------- */
978 : GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
979 : unsigned char abyPCT[771];
980 : int nPCTSize;
981 : int anRemap[256];
982 :
983 : abyPCT[0] = 0;
984 : abyPCT[1] = 0;
985 : abyPCT[2] = 0;
986 :
987 : if (poBand->GetColorTable() == NULL)
988 : {
989 : /* map greyscale down to 63 grey levels. */
990 : for (int iColor = 0; iColor < 256; iColor++)
991 : {
992 : int nOutValue = (int)(iColor / 4.1) + 1;
993 :
994 : anRemap[iColor] = nOutValue;
995 : abyPCT[nOutValue * 3 + 0] = (unsigned char)iColor;
996 : abyPCT[nOutValue * 3 + 1] = (unsigned char)iColor;
997 : abyPCT[nOutValue * 3 + 2] = (unsigned char)iColor;
998 : }
999 : nPCTSize = 64;
1000 : }
1001 : else
1002 : {
1003 : GDALColorTable *poCT = poBand->GetColorTable();
1004 : int nColorTableSize = poCT->GetColorEntryCount();
1005 : if (nColorTableSize > 255)
1006 : nColorTableSize = 255;
1007 :
1008 : for (int iColor = 0; iColor < nColorTableSize; iColor++)
1009 : {
1010 : GDALColorEntry sEntry;
1011 :
1012 : poCT->GetColorEntryAsRGB(iColor, &sEntry);
1013 :
1014 : anRemap[iColor] = iColor + 1;
1015 : abyPCT[(iColor + 1) * 3 + 0] = (unsigned char)sEntry.c1;
1016 : abyPCT[(iColor + 1) * 3 + 1] = (unsigned char)sEntry.c2;
1017 : abyPCT[(iColor + 1) * 3 + 2] = (unsigned char)sEntry.c3;
1018 : }
1019 :
1020 : nPCTSize = nColorTableSize + 1;
1021 :
1022 : // Add entries for pixel values which apparently will not occur.
1023 : for (int iColor = nPCTSize; iColor < 256; iColor++)
1024 : anRemap[iColor] = 1;
1025 : }
1026 :
1027 : /* -------------------------------------------------------------------- */
1028 : /* Boil out all duplicate entries. */
1029 : /* -------------------------------------------------------------------- */
1030 : for (int i = 1; i < nPCTSize - 1; i++)
1031 : {
1032 : for (int j = i + 1; j < nPCTSize; j++)
1033 : {
1034 : if (abyPCT[i * 3 + 0] == abyPCT[j * 3 + 0] &&
1035 : abyPCT[i * 3 + 1] == abyPCT[j * 3 + 1] &&
1036 : abyPCT[i * 3 + 2] == abyPCT[j * 3 + 2])
1037 : {
1038 : nPCTSize--;
1039 : abyPCT[j * 3 + 0] = abyPCT[nPCTSize * 3 + 0];
1040 : abyPCT[j * 3 + 1] = abyPCT[nPCTSize * 3 + 1];
1041 : abyPCT[j * 3 + 2] = abyPCT[nPCTSize * 3 + 2];
1042 :
1043 : for (int k = 0; k < 256; k++)
1044 : {
1045 : // merge matching entries.
1046 : if (anRemap[k] == j)
1047 : anRemap[k] = i;
1048 :
1049 : // shift the last PCT entry into the new hole.
1050 : if (anRemap[k] == nPCTSize)
1051 : anRemap[k] = j;
1052 : }
1053 : }
1054 : }
1055 : }
1056 :
1057 : /* -------------------------------------------------------------------- */
1058 : /* Boil out all duplicate entries. */
1059 : /* -------------------------------------------------------------------- */
1060 : if (nPCTSize > 128)
1061 : {
1062 : CPLError(CE_Warning, CPLE_AppDefined,
1063 : "Having to merge color table entries to reduce %d real\n"
1064 : "color table entries down to 127 values.",
1065 : nPCTSize);
1066 : }
1067 :
1068 : while (nPCTSize > 128)
1069 : {
1070 : int nBestRange = 768;
1071 : int iBestMatch1 = -1;
1072 : int iBestMatch2 = -1;
1073 :
1074 : // Find the closest pair of color table entries.
1075 :
1076 : for (int i = 1; i < nPCTSize - 1; i++)
1077 : {
1078 : for (int j = i + 1; j < nPCTSize; j++)
1079 : {
1080 : int nRange = std::abs(abyPCT[i * 3 + 0] - abyPCT[j * 3 + 0]) +
1081 : std::abs(abyPCT[i * 3 + 1] - abyPCT[j * 3 + 1]) +
1082 : std::abs(abyPCT[i * 3 + 2] - abyPCT[j * 3 + 2]);
1083 :
1084 : if (nRange < nBestRange)
1085 : {
1086 : iBestMatch1 = i;
1087 : iBestMatch2 = j;
1088 : nBestRange = nRange;
1089 : }
1090 : }
1091 : }
1092 :
1093 : // Merge the second entry into the first.
1094 : nPCTSize--;
1095 : abyPCT[iBestMatch2 * 3 + 0] = abyPCT[nPCTSize * 3 + 0];
1096 : abyPCT[iBestMatch2 * 3 + 1] = abyPCT[nPCTSize * 3 + 1];
1097 : abyPCT[iBestMatch2 * 3 + 2] = abyPCT[nPCTSize * 3 + 2];
1098 :
1099 : for (int i = 0; i < 256; i++)
1100 : {
1101 : // merge matching entries.
1102 : if (anRemap[i] == iBestMatch2)
1103 : anRemap[i] = iBestMatch1;
1104 :
1105 : // shift the last PCT entry into the new hole.
1106 : if (anRemap[i] == nPCTSize)
1107 : anRemap[i] = iBestMatch2;
1108 : }
1109 : }
1110 :
1111 : /* -------------------------------------------------------------------- */
1112 : /* Write the PCT. */
1113 : /* -------------------------------------------------------------------- */
1114 : if (!BSBWritePCT(psBSB, nPCTSize, abyPCT))
1115 : {
1116 : BSBClose(psBSB);
1117 : return NULL;
1118 : }
1119 :
1120 : /* -------------------------------------------------------------------- */
1121 : /* Write the GCPs. */
1122 : /* -------------------------------------------------------------------- */
1123 : double adfGeoTransform[6];
1124 : int nGCPCount = poSrcDS->GetGCPCount();
1125 : if (nGCPCount)
1126 : {
1127 : const char *pszGCPProjection = poSrcDS->GetGCPProjection();
1128 : if (BSBIsSRSOK(pszGCPProjection))
1129 : {
1130 : const GDAL_GCP *pasGCPList = poSrcDS->GetGCPs();
1131 : for (int i = 0; i < nGCPCount; i++)
1132 : {
1133 : VSIFPrintfL(psBSB->fp, "REF/%d,%f,%f,%f,%f\n", i + 1,
1134 : pasGCPList[i].dfGCPPixel, pasGCPList[i].dfGCPLine,
1135 : pasGCPList[i].dfGCPY, pasGCPList[i].dfGCPX);
1136 : }
1137 : }
1138 : }
1139 : else if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None)
1140 : {
1141 : const char *pszProjection = poSrcDS->GetProjectionRef();
1142 : if (BSBIsSRSOK(pszProjection))
1143 : {
1144 : VSIFPrintfL(psBSB->fp, "REF/%d,%d,%d,%f,%f\n", 1, 0, 0,
1145 : adfGeoTransform[3] + 0 * adfGeoTransform[4] +
1146 : 0 * adfGeoTransform[5],
1147 : adfGeoTransform[0] + 0 * adfGeoTransform[1] +
1148 : 0 * adfGeoTransform[2]);
1149 : VSIFPrintfL(psBSB->fp, "REF/%d,%d,%d,%f,%f\n", 2, nXSize, 0,
1150 : adfGeoTransform[3] + nXSize * adfGeoTransform[4] +
1151 : 0 * adfGeoTransform[5],
1152 : adfGeoTransform[0] + nXSize * adfGeoTransform[1] +
1153 : 0 * adfGeoTransform[2]);
1154 : VSIFPrintfL(psBSB->fp, "REF/%d,%d,%d,%f,%f\n", 3, nXSize, nYSize,
1155 : adfGeoTransform[3] + nXSize * adfGeoTransform[4] +
1156 : nYSize * adfGeoTransform[5],
1157 : adfGeoTransform[0] + nXSize * adfGeoTransform[1] +
1158 : nYSize * adfGeoTransform[2]);
1159 : VSIFPrintfL(psBSB->fp, "REF/%d,%d,%d,%f,%f\n", 4, 0, nYSize,
1160 : adfGeoTransform[3] + 0 * adfGeoTransform[4] +
1161 : nYSize * adfGeoTransform[5],
1162 : adfGeoTransform[0] + 0 * adfGeoTransform[1] +
1163 : nYSize * adfGeoTransform[2]);
1164 : }
1165 : }
1166 :
1167 : /* -------------------------------------------------------------------- */
1168 : /* Loop over image, copying image data. */
1169 : /* -------------------------------------------------------------------- */
1170 : CPLErr eErr = CE_None;
1171 :
1172 : GByte *pabyScanline = (GByte *)CPLMalloc(nXSize);
1173 :
1174 : for (int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++)
1175 : {
1176 : eErr =
1177 : poBand->RasterIO(GF_Read, 0, iLine, nXSize, 1, pabyScanline, nXSize,
1178 : 1, GDT_Byte, nBands, nBands * nXSize, NULL);
1179 : if (eErr == CE_None)
1180 : {
1181 : for (int i = 0; i < nXSize; i++)
1182 : pabyScanline[i] = (GByte)anRemap[pabyScanline[i]];
1183 :
1184 : if (!BSBWriteScanline(psBSB, pabyScanline))
1185 : eErr = CE_Failure;
1186 : }
1187 : }
1188 :
1189 : CPLFree(pabyScanline);
1190 :
1191 : /* -------------------------------------------------------------------- */
1192 : /* cleanup */
1193 : /* -------------------------------------------------------------------- */
1194 : BSBClose(psBSB);
1195 :
1196 : if (eErr != CE_None)
1197 : {
1198 : VSIUnlink(pszFilename);
1199 : return NULL;
1200 : }
1201 :
1202 : return (GDALDataset *)GDALOpen(pszFilename, GA_ReadOnly);
1203 : }
1204 : #endif
1205 :
1206 : /************************************************************************/
1207 : /* GDALRegister_BSB() */
1208 : /************************************************************************/
1209 :
1210 1595 : void GDALRegister_BSB()
1211 :
1212 : {
1213 1595 : if (GDALGetDriverByName("BSB") != nullptr)
1214 302 : return;
1215 :
1216 1293 : GDALDriver *poDriver = new GDALDriver();
1217 :
1218 1293 : poDriver->SetDescription("BSB");
1219 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1220 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Maptech BSB Nautical Charts");
1221 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/bsb.html");
1222 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1223 1293 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "kap");
1224 : #ifdef BSB_CREATE
1225 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
1226 : #endif
1227 1293 : poDriver->pfnOpen = BSBDataset::Open;
1228 1293 : poDriver->pfnIdentify = BSBDataset::Identify;
1229 : #ifdef BSB_CREATE
1230 : poDriver->pfnCreateCopy = BSBCreateCopy;
1231 : #endif
1232 :
1233 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
1234 : }
|