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