1 : /******************************************************************************
2 : * $Id: hf2dataset.cpp 21680 2011-02-11 21:12:07Z warmerdam $
3 : *
4 : * Project: HF2 driver
5 : * Purpose: GDALDataset driver for HF2/HFZ dataset.
6 : * Author: Even Rouault, <even dot rouault at mines dash paris dot org>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2010, Even Rouault
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 "cpl_string.h"
31 : #include "gdal_pam.h"
32 : #include "ogr_spatialref.h"
33 :
34 : CPL_CVSID("$Id: hf2dataset.cpp 21680 2011-02-11 21:12:07Z warmerdam $");
35 :
36 : CPL_C_START
37 : void GDALRegister_HF2(void);
38 : CPL_C_END
39 :
40 : /************************************************************************/
41 : /* ==================================================================== */
42 : /* HF2Dataset */
43 : /* ==================================================================== */
44 : /************************************************************************/
45 :
46 : class HF2RasterBand;
47 :
48 : class HF2Dataset : public GDALPamDataset
49 : {
50 : friend class HF2RasterBand;
51 :
52 : VSILFILE *fp;
53 : double adfGeoTransform[6];
54 : char *pszWKT;
55 : vsi_l_offset *panBlockOffset;
56 :
57 : int nTileSize;
58 : int bHasLoaderBlockMap;
59 : int LoadBlockMap();
60 :
61 : public:
62 : HF2Dataset();
63 : virtual ~HF2Dataset();
64 :
65 : virtual CPLErr GetGeoTransform( double * );
66 : virtual const char* GetProjectionRef();
67 :
68 : static GDALDataset *Open( GDALOpenInfo * );
69 : static int Identify( GDALOpenInfo * );
70 : static GDALDataset *CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
71 : int bStrict, char ** papszOptions,
72 : GDALProgressFunc pfnProgress, void * pProgressData );
73 : };
74 :
75 : /************************************************************************/
76 : /* ==================================================================== */
77 : /* HF2RasterBand */
78 : /* ==================================================================== */
79 : /************************************************************************/
80 :
81 : class HF2RasterBand : public GDALPamRasterBand
82 : {
83 : friend class HF2Dataset;
84 :
85 : float* pafBlockData;
86 : int nLastBlockYOff;
87 :
88 : public:
89 :
90 : HF2RasterBand( HF2Dataset *, int, GDALDataType );
91 : ~HF2RasterBand();
92 :
93 : virtual CPLErr IReadBlock( int, int, void * );
94 : };
95 :
96 :
97 : /************************************************************************/
98 : /* HF2RasterBand() */
99 : /************************************************************************/
100 :
101 23 : HF2RasterBand::HF2RasterBand( HF2Dataset *poDS, int nBand, GDALDataType eDT )
102 :
103 : {
104 23 : this->poDS = poDS;
105 23 : this->nBand = nBand;
106 :
107 23 : eDataType = eDT;
108 :
109 23 : nBlockXSize = poDS->nTileSize;
110 23 : nBlockYSize = 1;
111 :
112 23 : pafBlockData = NULL;
113 23 : nLastBlockYOff = -1;
114 23 : }
115 :
116 : /************************************************************************/
117 : /* ~HF2RasterBand() */
118 : /************************************************************************/
119 :
120 23 : HF2RasterBand::~HF2RasterBand()
121 : {
122 23 : CPLFree(pafBlockData);
123 23 : }
124 :
125 : /************************************************************************/
126 : /* IReadBlock() */
127 : /************************************************************************/
128 :
129 764 : CPLErr HF2RasterBand::IReadBlock( int nBlockXOff, int nLineYOff,
130 : void * pImage )
131 :
132 : {
133 764 : HF2Dataset *poGDS = (HF2Dataset *) poDS;
134 :
135 764 : int nXBlocks = (nRasterXSize + nBlockXSize - 1) / nBlockXSize;
136 764 : int nYBlocks = (nRasterYSize + nBlockXSize - 1) / nBlockXSize;
137 :
138 764 : if (!poGDS->LoadBlockMap())
139 0 : return CE_Failure;
140 :
141 764 : if (pafBlockData == NULL)
142 : {
143 8 : pafBlockData = (float*)VSIMalloc3(nXBlocks * sizeof(float), poGDS->nTileSize, poGDS->nTileSize);
144 8 : if (pafBlockData == NULL)
145 0 : return CE_Failure;
146 : }
147 :
148 764 : nLineYOff = nRasterYSize - 1 - nLineYOff;
149 :
150 764 : int nBlockYOff = nLineYOff / nBlockXSize;
151 764 : int nYOffInTile = nLineYOff % nBlockXSize;
152 :
153 764 : if (nBlockYOff != nLastBlockYOff)
154 : {
155 10 : nLastBlockYOff = nBlockYOff;
156 :
157 10 : memset(pafBlockData, 0, nXBlocks * sizeof(float) * nBlockXSize * nBlockXSize);
158 :
159 : /* 4 * nBlockXSize is the upper bound */
160 10 : void* pabyData = CPLMalloc( 4 * nBlockXSize );
161 :
162 : int nxoff;
163 24 : for(nxoff = 0; nxoff < nXBlocks; nxoff++)
164 : {
165 14 : VSIFSeekL(poGDS->fp, poGDS->panBlockOffset[(nYBlocks - 1 - nBlockYOff) * nXBlocks + nxoff], SEEK_SET);
166 : float fScale, fOff;
167 14 : VSIFReadL(&fScale, 4, 1, poGDS->fp);
168 14 : VSIFReadL(&fOff, 4, 1, poGDS->fp);
169 : CPL_LSBPTR32(&fScale);
170 : CPL_LSBPTR32(&fOff);
171 :
172 14 : int nTileWidth = MIN(nBlockXSize, nRasterXSize - nxoff * nBlockXSize);
173 14 : int nTileHeight = MIN(nBlockXSize, nRasterYSize - nBlockYOff * nBlockXSize);
174 :
175 : int j;
176 778 : for(j=0;j<nTileHeight;j++)
177 : {
178 : GByte nWordSize;
179 764 : VSIFReadL(&nWordSize, 1, 1, poGDS->fp);
180 764 : if (nWordSize != 1 && nWordSize != 2 && nWordSize != 4)
181 : {
182 : CPLError(CE_Failure, CPLE_AppDefined,
183 0 : "Unexpected word size : %d", (int)nWordSize);
184 0 : break;
185 : }
186 :
187 : GInt32 nVal;
188 764 : VSIFReadL(&nVal, 4, 1, poGDS->fp);
189 : CPL_LSBPTR32(&nVal);
190 764 : VSIFReadL(pabyData, nWordSize * (nTileWidth - 1), 1, poGDS->fp);
191 : #if defined(CPL_MSB)
192 : if (nWordSize > 1)
193 : GDALSwapWords(pabyData, nWordSize, nTileWidth - 1, nWordSize);
194 : #endif
195 :
196 764 : pafBlockData[nxoff * nBlockXSize * nBlockXSize + j * nBlockXSize + 0] = nVal * fScale + fOff;
197 : int i;
198 111684 : for(i=1;i<nTileWidth;i++)
199 : {
200 110920 : if (nWordSize == 1)
201 38920 : nVal += ((char*)pabyData)[i-1];
202 72000 : else if (nWordSize == 2)
203 72000 : nVal += ((GInt16*)pabyData)[i-1];
204 : else
205 0 : nVal += ((GInt32*)pabyData)[i-1];
206 110920 : pafBlockData[nxoff * nBlockXSize * nBlockXSize + j * nBlockXSize + i] = nVal * fScale + fOff;
207 : }
208 : }
209 : }
210 :
211 10 : CPLFree(pabyData);
212 : }
213 :
214 764 : int nTileWidth = MIN(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
215 : memcpy(pImage, pafBlockData + nBlockXOff * nBlockXSize * nBlockXSize +
216 : nYOffInTile * nBlockXSize,
217 764 : nTileWidth * sizeof(float));
218 :
219 764 : return CE_None;
220 : }
221 :
222 : /************************************************************************/
223 : /* ~HF2Dataset() */
224 : /************************************************************************/
225 :
226 23 : HF2Dataset::HF2Dataset()
227 : {
228 23 : fp = NULL;
229 23 : pszWKT = NULL;
230 23 : panBlockOffset = NULL;
231 23 : adfGeoTransform[0] = 0;
232 23 : adfGeoTransform[1] = 1;
233 23 : adfGeoTransform[2] = 0;
234 23 : adfGeoTransform[3] = 0;
235 23 : adfGeoTransform[4] = 0;
236 23 : adfGeoTransform[5] = 1;
237 23 : bHasLoaderBlockMap = FALSE;
238 23 : nTileSize = 0;
239 23 : }
240 :
241 : /************************************************************************/
242 : /* ~HF2Dataset() */
243 : /************************************************************************/
244 :
245 23 : HF2Dataset::~HF2Dataset()
246 :
247 : {
248 23 : FlushCache();
249 23 : CPLFree(pszWKT);
250 23 : CPLFree(panBlockOffset);
251 23 : if (fp)
252 23 : VSIFCloseL(fp);
253 23 : }
254 :
255 : /************************************************************************/
256 : /* LoadBlockMap() */
257 : /************************************************************************/
258 :
259 764 : int HF2Dataset::LoadBlockMap()
260 : {
261 764 : if (bHasLoaderBlockMap)
262 756 : return panBlockOffset != NULL;
263 :
264 8 : bHasLoaderBlockMap = TRUE;
265 :
266 8 : int nXBlocks = (nRasterXSize + nTileSize - 1) / nTileSize;
267 8 : int nYBlocks = (nRasterYSize + nTileSize - 1) / nTileSize;
268 8 : panBlockOffset = (vsi_l_offset*) VSIMalloc3(sizeof(vsi_l_offset), nXBlocks, nYBlocks);
269 8 : if (panBlockOffset == NULL)
270 : {
271 0 : return FALSE;
272 : }
273 : int i, j;
274 18 : for(j = 0; j < nYBlocks; j++)
275 : {
276 24 : for(i = 0; i < nXBlocks; i++)
277 : {
278 14 : vsi_l_offset nOff = VSIFTellL(fp);
279 14 : panBlockOffset[(nYBlocks - 1 - j) * nXBlocks + i] = nOff;
280 : //VSIFSeekL(fp, 4 + 4, SEEK_CUR);
281 : float fScale, fOff;
282 14 : VSIFReadL(&fScale, 4, 1, fp);
283 14 : VSIFReadL(&fOff, 4, 1, fp);
284 : CPL_LSBPTR32(&fScale);
285 : CPL_LSBPTR32(&fOff);
286 : //printf("fScale = %f, fOff = %f\n", fScale, fOff);
287 : int k;
288 14 : int nCols = MIN(nTileSize, nRasterXSize - nTileSize *i);
289 14 : int nLines = MIN(nTileSize, nRasterYSize - nTileSize *j);
290 1556 : for(k = 0; k < nLines; k++)
291 : {
292 : GByte nWordSize;
293 764 : VSIFReadL(&nWordSize, 1, 1, fp);
294 : //printf("nWordSize=%d\n", nWordSize);
295 764 : if (nWordSize == 1 || nWordSize == 2 || nWordSize == 4)
296 764 : VSIFSeekL(fp, 4 + nWordSize * (nCols - 1), SEEK_CUR);
297 : else
298 : {
299 : CPLError(CE_Failure, CPLE_AppDefined,
300 : "Got unexpected byte depth (%d) for block (%d, %d) line %d",
301 0 : (int)nWordSize, i, j, k);
302 0 : VSIFree(panBlockOffset);
303 0 : panBlockOffset = NULL;
304 0 : return FALSE;
305 : }
306 : }
307 : }
308 : }
309 :
310 8 : return TRUE;
311 : }
312 :
313 : /************************************************************************/
314 : /* GetProjectionRef() */
315 : /************************************************************************/
316 :
317 0 : const char* HF2Dataset::GetProjectionRef()
318 : {
319 0 : if (pszWKT)
320 0 : return pszWKT;
321 0 : return GDALPamDataset::GetProjectionRef();
322 : }
323 :
324 : /************************************************************************/
325 : /* Identify() */
326 : /************************************************************************/
327 :
328 10528 : int HF2Dataset::Identify( GDALOpenInfo * poOpenInfo)
329 : {
330 :
331 10528 : GDALOpenInfo* poOpenInfoToDelete = NULL;
332 : /* GZipped .hf2 files are common, so automagically open them */
333 : /* if the /vsigzip/ has not been explicitely passed */
334 10528 : CPLString osFilename(poOpenInfo->pszFilename);
335 10528 : if ((EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "hfz") ||
336 : (strlen(poOpenInfo->pszFilename) > 6 &&
337 : EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6, "hf2.gz"))) &&
338 : !EQUALN(poOpenInfo->pszFilename, "/vsigzip/", 9))
339 : {
340 3 : osFilename = "/vsigzip/";
341 3 : osFilename += poOpenInfo->pszFilename;
342 : poOpenInfo = poOpenInfoToDelete =
343 : new GDALOpenInfo(osFilename.c_str(), GA_ReadOnly,
344 3 : poOpenInfo->papszSiblingFiles);
345 : }
346 :
347 10528 : if (poOpenInfo->nHeaderBytes < 28)
348 : {
349 10329 : delete poOpenInfoToDelete;
350 10329 : return FALSE;
351 : }
352 :
353 199 : if (memcmp(poOpenInfo->pabyHeader, "HF2\0\0\0\0", 6) != 0)
354 : {
355 176 : delete poOpenInfoToDelete;
356 176 : return FALSE;
357 : }
358 :
359 23 : delete poOpenInfoToDelete;
360 23 : return TRUE;
361 : }
362 :
363 : /************************************************************************/
364 : /* Open() */
365 : /************************************************************************/
366 :
367 1290 : GDALDataset *HF2Dataset::Open( GDALOpenInfo * poOpenInfo )
368 :
369 : {
370 1290 : CPLString osOriginalFilename(poOpenInfo->pszFilename);
371 :
372 1290 : if (!Identify(poOpenInfo))
373 1267 : return NULL;
374 :
375 23 : GDALOpenInfo* poOpenInfoToDelete = NULL;
376 : /* GZipped .hf2 files are common, so automagically open them */
377 : /* if the /vsigzip/ has not been explicitely passed */
378 23 : CPLString osFilename(poOpenInfo->pszFilename);
379 23 : if ((EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "hfz") ||
380 : (strlen(poOpenInfo->pszFilename) > 6 &&
381 : EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6, "hf2.gz"))) &&
382 : !EQUALN(poOpenInfo->pszFilename, "/vsigzip/", 9))
383 : {
384 2 : osFilename = "/vsigzip/";
385 2 : osFilename += poOpenInfo->pszFilename;
386 : poOpenInfo = poOpenInfoToDelete =
387 : new GDALOpenInfo(osFilename.c_str(), GA_ReadOnly,
388 2 : poOpenInfo->papszSiblingFiles);
389 : }
390 :
391 : /* -------------------------------------------------------------------- */
392 : /* Parse header */
393 : /* -------------------------------------------------------------------- */
394 :
395 : int nXSize, nYSize;
396 23 : memcpy(&nXSize, poOpenInfo->pabyHeader + 6, 4);
397 : CPL_LSBPTR32(&nXSize);
398 23 : memcpy(&nYSize, poOpenInfo->pabyHeader + 10, 4);
399 : CPL_LSBPTR32(&nYSize);
400 :
401 : GUInt16 nTileSize;
402 23 : memcpy(&nTileSize, poOpenInfo->pabyHeader + 14, 2);
403 : CPL_LSBPTR16(&nTileSize);
404 :
405 : float fVertPres, fHorizScale;
406 23 : memcpy(&fVertPres, poOpenInfo->pabyHeader + 16, 4);
407 : CPL_LSBPTR32(&fVertPres);
408 23 : memcpy(&fHorizScale, poOpenInfo->pabyHeader + 20, 4);
409 : CPL_LSBPTR32(&fHorizScale);
410 :
411 : GUInt32 nExtendedHeaderLen;
412 23 : memcpy(&nExtendedHeaderLen, poOpenInfo->pabyHeader + 24, 4);
413 : CPL_LSBPTR32(&nExtendedHeaderLen);
414 :
415 23 : delete poOpenInfoToDelete;
416 23 : poOpenInfoToDelete = NULL;
417 :
418 23 : if (nTileSize < 8)
419 0 : return NULL;
420 23 : if (nXSize <= 0 || nXSize > INT_MAX - nTileSize ||
421 : nYSize <= 0 || nYSize > INT_MAX - nTileSize)
422 0 : return NULL;
423 : /* To avoid later potential int overflows */
424 23 : if (nExtendedHeaderLen > 1024 * 65536)
425 0 : return NULL;
426 :
427 23 : if (!GDALCheckDatasetDimensions(nXSize, nYSize))
428 : {
429 0 : return NULL;
430 : }
431 :
432 : /* -------------------------------------------------------------------- */
433 : /* Parse extended blocks */
434 : /* -------------------------------------------------------------------- */
435 :
436 23 : VSILFILE* fp = VSIFOpenL(osFilename.c_str(), "rb");
437 23 : if (fp == NULL)
438 0 : return NULL;
439 :
440 23 : VSIFSeekL(fp, 28, SEEK_SET);
441 :
442 23 : int bHasExtent = FALSE;
443 23 : double dfMinX = 0, dfMaxX = 0, dfMinY = 0, dfMaxY = 0;
444 23 : int bHasUTMZone = FALSE;
445 23 : GInt16 nUTMZone = 0;
446 23 : int bHasEPSGDatumCode = FALSE;
447 23 : GInt16 nEPSGDatumCode = 0;
448 23 : int bHasEPSGCode = FALSE;
449 23 : GInt16 nEPSGCode = 0;
450 23 : int bHasRelativePrecision = FALSE;
451 23 : float fRelativePrecision = 0;
452 : char szApplicationName[256];
453 23 : szApplicationName[0] = 0;
454 :
455 23 : GUInt32 nExtendedHeaderOff = 0;
456 104 : while(nExtendedHeaderOff < nExtendedHeaderLen)
457 : {
458 : char pabyBlockHeader[24];
459 58 : VSIFReadL(pabyBlockHeader, 24, 1, fp);
460 :
461 : char szBlockName[16 + 1];
462 58 : memcpy(szBlockName, pabyBlockHeader + 4, 16);
463 58 : szBlockName[16] = 0;
464 : GUInt32 nBlockSize;
465 58 : memcpy(&nBlockSize, pabyBlockHeader + 20, 4);
466 : CPL_LSBPTR32(&nBlockSize);
467 58 : if (nBlockSize > 65536)
468 0 : break;
469 :
470 58 : nExtendedHeaderOff += 24 + nBlockSize;
471 :
472 81 : if (strcmp(szBlockName, "georef-extents") == 0 &&
473 : nBlockSize == 34)
474 : {
475 : char pabyBlockData[34];
476 23 : VSIFReadL(pabyBlockData, 34, 1, fp);
477 :
478 23 : memcpy(&dfMinX, pabyBlockData + 2, 8);
479 : CPL_LSBPTR64(&dfMinX);
480 23 : memcpy(&dfMaxX, pabyBlockData + 2 + 8, 8);
481 : CPL_LSBPTR64(&dfMaxX);
482 23 : memcpy(&dfMinY, pabyBlockData + 2 + 8 + 8, 8);
483 : CPL_LSBPTR64(&dfMinY);
484 23 : memcpy(&dfMaxY, pabyBlockData + 2 + 8 + 8 + 8, 8);
485 : CPL_LSBPTR64(&dfMaxY);
486 :
487 23 : bHasExtent = TRUE;
488 : }
489 44 : else if (strcmp(szBlockName, "georef-utm") == 0 &&
490 : nBlockSize == 2)
491 : {
492 9 : VSIFReadL(&nUTMZone, 2, 1, fp);
493 : CPL_LSBPTR16(&nUTMZone);
494 9 : CPLDebug("HF2", "UTM Zone = %d", nUTMZone);
495 :
496 9 : bHasUTMZone = TRUE;
497 : }
498 46 : else if (strcmp(szBlockName, "georef-datum") == 0 &&
499 : nBlockSize == 2)
500 : {
501 20 : VSIFReadL(&nEPSGDatumCode, 2, 1, fp);
502 : CPL_LSBPTR16(&nEPSGDatumCode);
503 20 : CPLDebug("HF2", "EPSG Datum Code = %d", nEPSGDatumCode);
504 :
505 20 : bHasEPSGDatumCode = TRUE;
506 : }
507 12 : else if (strcmp(szBlockName, "georef-epsg-prj") == 0 &&
508 : nBlockSize == 2)
509 : {
510 6 : VSIFReadL(&nEPSGCode, 2, 1, fp);
511 : CPL_LSBPTR16(&nEPSGCode);
512 6 : CPLDebug("HF2", "EPSG Code = %d", nEPSGCode);
513 :
514 6 : bHasEPSGCode = TRUE;
515 : }
516 0 : else if (strcmp(szBlockName, "precis-rel") == 0 &&
517 : nBlockSize == 4)
518 : {
519 0 : VSIFReadL(&fRelativePrecision, 4, 1, fp);
520 : CPL_LSBPTR32(&fRelativePrecision);
521 :
522 0 : bHasRelativePrecision = TRUE;
523 : }
524 0 : else if (strcmp(szBlockName, "app-name") == 0 &&
525 : nBlockSize < 256)
526 : {
527 0 : VSIFReadL(szApplicationName, nBlockSize, 1, fp);
528 0 : szApplicationName[nBlockSize] = 0;
529 : }
530 : else
531 : {
532 0 : CPLDebug("HF2", "Skipping block %s", szBlockName);
533 0 : VSIFSeekL(fp, nBlockSize, SEEK_CUR);
534 : }
535 : }
536 :
537 : /* -------------------------------------------------------------------- */
538 : /* Create a corresponding GDALDataset. */
539 : /* -------------------------------------------------------------------- */
540 : HF2Dataset *poDS;
541 :
542 23 : poDS = new HF2Dataset();
543 23 : poDS->fp = fp;
544 23 : poDS->nRasterXSize = nXSize;
545 23 : poDS->nRasterYSize = nYSize;
546 23 : poDS->nTileSize = nTileSize;
547 23 : CPLDebug("HF2", "nXSize = %d, nYSize = %d, nTileSize = %d", nXSize, nYSize, nTileSize);
548 23 : if (bHasExtent)
549 : {
550 23 : poDS->adfGeoTransform[0] = dfMinX;
551 23 : poDS->adfGeoTransform[3] = dfMaxY;
552 23 : poDS->adfGeoTransform[1] = (dfMaxX - dfMinX) / nXSize;
553 23 : poDS->adfGeoTransform[5] = -(dfMaxY - dfMinY) / nYSize;
554 : }
555 : else
556 : {
557 0 : poDS->adfGeoTransform[1] = fHorizScale;
558 0 : poDS->adfGeoTransform[5] = fHorizScale;
559 : }
560 :
561 23 : if (bHasEPSGCode)
562 : {
563 6 : OGRSpatialReference oSRS;
564 6 : if (oSRS.importFromEPSG(nEPSGCode) == OGRERR_NONE)
565 6 : oSRS.exportToWkt(&poDS->pszWKT);
566 : }
567 : else
568 : {
569 17 : int bHasSRS = FALSE;
570 17 : OGRSpatialReference oSRS;
571 17 : oSRS.SetGeogCS("unknown", "unknown", "unknown", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING);
572 17 : if (bHasEPSGDatumCode)
573 : {
574 28 : if (nEPSGDatumCode == 23 || nEPSGDatumCode == 6326)
575 : {
576 14 : bHasSRS = TRUE;
577 14 : oSRS.SetWellKnownGeogCS("WGS84");
578 : }
579 0 : else if (nEPSGDatumCode >= 6000)
580 : {
581 : char szName[32];
582 0 : sprintf( szName, "EPSG:%d", nEPSGDatumCode-2000 );
583 0 : oSRS.SetWellKnownGeogCS( szName );
584 0 : bHasSRS = TRUE;
585 : }
586 : }
587 :
588 17 : if (bHasUTMZone && ABS(nUTMZone) >= 1 && ABS(nUTMZone) <= 60)
589 : {
590 3 : bHasSRS = TRUE;
591 3 : oSRS.SetUTM(ABS(nUTMZone), nUTMZone > 0);
592 : }
593 17 : if (bHasSRS)
594 17 : oSRS.exportToWkt(&poDS->pszWKT);
595 : }
596 :
597 : /* -------------------------------------------------------------------- */
598 : /* Create band information objects. */
599 : /* -------------------------------------------------------------------- */
600 23 : poDS->nBands = 1;
601 : int i;
602 46 : for( i = 0; i < poDS->nBands; i++ )
603 : {
604 23 : poDS->SetBand( i+1, new HF2RasterBand( poDS, i+1, GDT_Float32 ) );
605 23 : poDS->GetRasterBand(i+1)->SetUnitType("m");
606 : }
607 :
608 23 : if (szApplicationName[0] != '\0')
609 0 : poDS->SetMetadataItem("APPLICATION_NAME", szApplicationName);
610 23 : poDS->SetMetadataItem("VERTICAL_PRECISION", CPLString().Printf("%f", fVertPres));
611 23 : if (bHasRelativePrecision)
612 0 : poDS->SetMetadataItem("RELATIVE_VERTICAL_PRECISION", CPLString().Printf("%f", fRelativePrecision));
613 :
614 : /* -------------------------------------------------------------------- */
615 : /* Initialize any PAM information. */
616 : /* -------------------------------------------------------------------- */
617 23 : poDS->SetDescription( osOriginalFilename.c_str() );
618 23 : poDS->TryLoadXML();
619 :
620 : /* -------------------------------------------------------------------- */
621 : /* Support overviews. */
622 : /* -------------------------------------------------------------------- */
623 23 : poDS->oOvManager.Initialize( poDS, osOriginalFilename.c_str() );
624 23 : return( poDS );
625 : }
626 :
627 : /************************************************************************/
628 : /* GetGeoTransform() */
629 : /************************************************************************/
630 :
631 1 : CPLErr HF2Dataset::GetGeoTransform( double * padfTransform )
632 :
633 : {
634 1 : memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
635 :
636 1 : return( CE_None );
637 : }
638 :
639 :
640 36064 : static void WriteShort(VSILFILE* fp, GInt16 val)
641 : {
642 : CPL_LSBPTR16(&val);
643 36064 : VSIFWriteL(&val, 2, 1, fp);
644 36064 : }
645 :
646 571 : static void WriteInt(VSILFILE* fp, GInt32 val)
647 : {
648 : CPL_LSBPTR32(&val);
649 571 : VSIFWriteL(&val, 4, 1, fp);
650 571 : }
651 :
652 66 : static void WriteFloat(VSILFILE* fp, float val)
653 : {
654 : CPL_LSBPTR32(&val);
655 66 : VSIFWriteL(&val, 4, 1, fp);
656 66 : }
657 :
658 60 : static void WriteDouble(VSILFILE* fp, double val)
659 : {
660 : CPL_LSBPTR64(&val);
661 60 : VSIFWriteL(&val, 8, 1, fp);
662 60 : }
663 :
664 : /************************************************************************/
665 : /* CreateCopy() */
666 : /************************************************************************/
667 :
668 22 : GDALDataset* HF2Dataset::CreateCopy( const char * pszFilename,
669 : GDALDataset *poSrcDS,
670 : int bStrict, char ** papszOptions,
671 : GDALProgressFunc pfnProgress,
672 : void * pProgressData )
673 : {
674 : /* -------------------------------------------------------------------- */
675 : /* Some some rudimentary checks */
676 : /* -------------------------------------------------------------------- */
677 22 : int nBands = poSrcDS->GetRasterCount();
678 22 : if (nBands == 0)
679 : {
680 : CPLError( CE_Failure, CPLE_NotSupported,
681 1 : "HF2 driver does not support source dataset with zero band.\n");
682 1 : return NULL;
683 : }
684 :
685 21 : if (nBands != 1)
686 : {
687 : CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
688 4 : "HF2 driver only uses the first band of the dataset.\n");
689 4 : if (bStrict)
690 4 : return NULL;
691 : }
692 :
693 17 : if( pfnProgress && !pfnProgress( 0.0, NULL, pProgressData ) )
694 0 : return NULL;
695 :
696 : /* -------------------------------------------------------------------- */
697 : /* Get source dataset info */
698 : /* -------------------------------------------------------------------- */
699 :
700 17 : int nXSize = poSrcDS->GetRasterXSize();
701 17 : int nYSize = poSrcDS->GetRasterYSize();
702 : double adfGeoTransform[6];
703 17 : poSrcDS->GetGeoTransform(adfGeoTransform);
704 17 : int bHasGeoTransform = !(adfGeoTransform[0] == 0 &&
705 0 : adfGeoTransform[1] == 1 &&
706 0 : adfGeoTransform[2] == 0 &&
707 0 : adfGeoTransform[3] == 0 &&
708 0 : adfGeoTransform[4] == 0 &&
709 17 : adfGeoTransform[5] == 1);
710 17 : if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0)
711 : {
712 : CPLError( CE_Failure, CPLE_NotSupported,
713 0 : "HF2 driver does not support CreateCopy() from skewed or rotated dataset.\n");
714 0 : return NULL;
715 : }
716 :
717 17 : GDALDataType eSrcDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
718 : GDALDataType eReqDT;
719 17 : float fVertPres = (float) 0.01;
720 24 : if (eSrcDT == GDT_Byte || eSrcDT == GDT_Int16)
721 : {
722 7 : fVertPres = 1;
723 7 : eReqDT = GDT_Int16;
724 : }
725 : else
726 10 : eReqDT = GDT_Float32;
727 :
728 : /* -------------------------------------------------------------------- */
729 : /* Read creation options */
730 : /* -------------------------------------------------------------------- */
731 17 : const char* pszCompressed = CSLFetchNameValue(papszOptions, "COMPRESS");
732 17 : int bCompress = FALSE;
733 17 : if (pszCompressed)
734 1 : bCompress = CSLTestBoolean(pszCompressed);
735 :
736 17 : const char* pszVerticalPrecision = CSLFetchNameValue(papszOptions, "VERTICAL_PRECISION");
737 17 : if (pszVerticalPrecision)
738 : {
739 0 : fVertPres = (float) CPLAtofM(pszVerticalPrecision);
740 0 : if (fVertPres <= 0)
741 : {
742 : CPLError(CE_Warning, CPLE_AppDefined,
743 0 : "Unsupported value for VERTICAL_PRECISION. Defaulting to 0.01");
744 0 : fVertPres = (float) 0.01;
745 : }
746 0 : if (eReqDT == GDT_Int16 && fVertPres > 1)
747 0 : eReqDT = GDT_Float32;
748 : }
749 :
750 17 : const char* pszBlockSize = CSLFetchNameValue(papszOptions, "BLOCKSIZE");
751 17 : int nTileSize = 256;
752 17 : if (pszBlockSize)
753 : {
754 1 : nTileSize = atoi(pszBlockSize);
755 1 : if (nTileSize < 8 || nTileSize > 4096)
756 : {
757 : CPLError(CE_Warning, CPLE_AppDefined,
758 0 : "Unsupported value for BLOCKSIZE. Defaulting to 256");
759 0 : nTileSize = 256;
760 : }
761 : }
762 :
763 : /* -------------------------------------------------------------------- */
764 : /* Parse source dataset georeferencing info */
765 : /* -------------------------------------------------------------------- */
766 :
767 17 : int nExtendedHeaderLen = 0;
768 17 : if (bHasGeoTransform)
769 17 : nExtendedHeaderLen += 58;
770 17 : const char* pszProjectionRef = poSrcDS->GetProjectionRef();
771 17 : int nDatumCode = -2;
772 17 : int nUTMZone = 0;
773 17 : int bNorth = FALSE;
774 17 : int nEPSGCode = 0;
775 17 : int nExtentUnits = 1;
776 17 : if (pszProjectionRef != NULL && pszProjectionRef[0] != '\0')
777 : {
778 17 : OGRSpatialReference oSRS;
779 17 : char* pszTemp = (char*) pszProjectionRef;
780 17 : if (oSRS.importFromWkt(&pszTemp) == OGRERR_NONE)
781 : {
782 17 : const char* pszValue = NULL;
783 17 : if( oSRS.GetAuthorityName( "GEOGCS|DATUM" ) != NULL
784 : && EQUAL(oSRS.GetAuthorityName( "GEOGCS|DATUM" ),"EPSG") )
785 13 : nDatumCode = atoi(oSRS.GetAuthorityCode( "GEOGCS|DATUM" ));
786 4 : else if ((pszValue = oSRS.GetAttrValue("GEOGCS|DATUM")) != NULL)
787 : {
788 4 : if (strstr(pszValue, "WGS") && strstr(pszValue, "84"))
789 3 : nDatumCode = 6326;
790 : }
791 :
792 17 : nUTMZone = oSRS.GetUTMZone(&bNorth);
793 : }
794 17 : if( oSRS.GetAuthorityName( "PROJCS" ) != NULL
795 : && EQUAL(oSRS.GetAuthorityName( "PROJCS" ),"EPSG") )
796 2 : nEPSGCode = atoi(oSRS.GetAuthorityCode( "PROJCS" ));
797 :
798 17 : if( oSRS.IsGeographic() )
799 : {
800 14 : nExtentUnits = 0;
801 : }
802 : else
803 : {
804 3 : double dfLinear = oSRS.GetLinearUnits();
805 :
806 3 : if( ABS(dfLinear - 0.3048) < 0.0000001 )
807 0 : nExtentUnits = 2;
808 3 : else if( ABS(dfLinear - atof(SRS_UL_US_FOOT_CONV)) < 0.00000001 )
809 0 : nExtentUnits = 3;
810 : else
811 3 : nExtentUnits = 1;
812 17 : }
813 : }
814 17 : if (nDatumCode != -2)
815 16 : nExtendedHeaderLen += 26;
816 17 : if (nUTMZone != 0)
817 3 : nExtendedHeaderLen += 26;
818 17 : if (nEPSGCode)
819 2 : nExtendedHeaderLen += 26;
820 :
821 : /* -------------------------------------------------------------------- */
822 : /* Create target file */
823 : /* -------------------------------------------------------------------- */
824 :
825 17 : CPLString osFilename;
826 17 : if (bCompress)
827 : {
828 1 : osFilename = "/vsigzip/";
829 1 : osFilename += pszFilename;
830 : }
831 : else
832 16 : osFilename = pszFilename;
833 17 : VSILFILE* fp = VSIFOpenL(osFilename.c_str(), "wb");
834 17 : if (fp == NULL)
835 : {
836 : CPLError( CE_Failure, CPLE_AppDefined,
837 2 : "Cannot create %s", pszFilename );
838 2 : return NULL;
839 : }
840 :
841 : /* -------------------------------------------------------------------- */
842 : /* Write header */
843 : /* -------------------------------------------------------------------- */
844 :
845 15 : VSIFWriteL("HF2\0", 4, 1, fp);
846 15 : WriteShort(fp, 0);
847 15 : WriteInt(fp, nXSize);
848 15 : WriteInt(fp, nYSize);
849 15 : WriteShort(fp, (GInt16) nTileSize);
850 15 : WriteFloat(fp, fVertPres);
851 15 : float fHorizScale = (float) ((fabs(adfGeoTransform[1]) + fabs(adfGeoTransform[5])) / 2);
852 15 : WriteFloat(fp, fHorizScale);
853 15 : WriteInt(fp, nExtendedHeaderLen);
854 :
855 : /* -------------------------------------------------------------------- */
856 : /* Write extended header */
857 : /* -------------------------------------------------------------------- */
858 :
859 : char szBlockName[16 + 1];
860 15 : if (bHasGeoTransform)
861 : {
862 15 : VSIFWriteL("bin\0", 4, 1, fp);
863 15 : memset(szBlockName, 0, 16 + 1);
864 15 : strcpy(szBlockName, "georef-extents");
865 15 : VSIFWriteL(szBlockName, 16, 1, fp);
866 15 : WriteInt(fp, 34);
867 15 : WriteShort(fp, (GInt16) nExtentUnits);
868 15 : WriteDouble(fp, adfGeoTransform[0]);
869 15 : WriteDouble(fp, adfGeoTransform[0] + nXSize * adfGeoTransform[1]);
870 15 : WriteDouble(fp, adfGeoTransform[3] + nYSize * adfGeoTransform[5]);
871 15 : WriteDouble(fp, adfGeoTransform[3]);
872 : }
873 15 : if (nUTMZone != 0)
874 : {
875 3 : VSIFWriteL("bin\0", 4, 1, fp);
876 3 : memset(szBlockName, 0, 16 + 1);
877 3 : strcpy(szBlockName, "georef-utm");
878 3 : VSIFWriteL(szBlockName, 16, 1, fp);
879 3 : WriteInt(fp, 2);
880 3 : WriteShort(fp, (GInt16) ((bNorth) ? nUTMZone : -nUTMZone));
881 : }
882 15 : if (nDatumCode != -2)
883 : {
884 14 : VSIFWriteL("bin\0", 4, 1, fp);
885 14 : memset(szBlockName, 0, 16 + 1);
886 14 : strcpy(szBlockName, "georef-datum");
887 14 : VSIFWriteL(szBlockName, 16, 1, fp);
888 14 : WriteInt(fp, 2);
889 14 : WriteShort(fp, (GInt16) nDatumCode);
890 : }
891 15 : if (nEPSGCode != 0)
892 : {
893 2 : VSIFWriteL("bin\0", 4, 1, fp);
894 2 : memset(szBlockName, 0, 16 + 1);
895 2 : strcpy(szBlockName, "georef-epsg-prj");
896 2 : VSIFWriteL(szBlockName, 16, 1, fp);
897 2 : WriteInt(fp, 2);
898 2 : WriteShort(fp, (GInt16) nEPSGCode);
899 : }
900 :
901 : /* -------------------------------------------------------------------- */
902 : /* Copy imagery */
903 : /* -------------------------------------------------------------------- */
904 15 : int nXBlocks = (nXSize + nTileSize - 1) / nTileSize;
905 15 : int nYBlocks = (nYSize + nTileSize - 1) / nTileSize;
906 :
907 15 : void* pTileBuffer = (void*) VSIMalloc(nTileSize * nTileSize * (GDALGetDataTypeSize(eReqDT) / 8));
908 15 : if (pTileBuffer == NULL)
909 : {
910 0 : CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
911 0 : VSIFCloseL(fp);
912 0 : return NULL;
913 : }
914 :
915 : int i, j, k, l;
916 15 : CPLErr eErr = CE_None;
917 31 : for(j=0;j<nYBlocks && eErr == CE_None;j++)
918 : {
919 34 : for(i=0;i<nXBlocks && eErr == CE_None;i++)
920 : {
921 18 : int nReqXSize = MIN(nTileSize, nXSize - i * nTileSize);
922 18 : int nReqYSize = MIN(nTileSize, nYSize - j * nTileSize);
923 : eErr = poSrcDS->GetRasterBand(1)->RasterIO(GF_Read,
924 : i * nTileSize, MAX(0, nYSize - (j + 1) * nTileSize),
925 : nReqXSize, nReqYSize,
926 : pTileBuffer, nReqXSize, nReqYSize,
927 18 : eReqDT, 0, 0);
928 18 : if (eErr != CE_None)
929 0 : break;
930 :
931 18 : if (eReqDT == GDT_Int16)
932 : {
933 8 : WriteFloat(fp, 1); /* scale */
934 8 : WriteFloat(fp, 0); /* offset */
935 209 : for(k=0;k<nReqYSize;k++)
936 : {
937 201 : int nLastVal = ((short*)pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
938 201 : GByte nWordSize = 1;
939 15641 : for(l=1;l<nReqXSize;l++)
940 : {
941 15440 : int nVal = ((short*)pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + l];
942 15440 : int nDiff = nVal - nLastVal;
943 15440 : if (nDiff < -32768 || nDiff > 32767)
944 : {
945 0 : nWordSize = 4;
946 0 : break;
947 : }
948 15440 : if (nDiff < -128 || nDiff > 127)
949 0 : nWordSize = 2;
950 15440 : nLastVal = nVal;
951 : }
952 :
953 201 : VSIFWriteL(&nWordSize, 1, 1, fp);
954 201 : nLastVal = ((short*)pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
955 201 : WriteInt(fp, nLastVal);
956 15641 : for(l=1;l<nReqXSize;l++)
957 : {
958 15440 : int nVal = ((short*)pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + l];
959 15440 : int nDiff = nVal - nLastVal;
960 15440 : if (nWordSize == 1)
961 : {
962 15440 : CPLAssert(nDiff >= -128 && nDiff <= 127);
963 15440 : char chDiff = (char)nDiff;
964 15440 : VSIFWriteL(&chDiff, 1, 1, fp);
965 : }
966 0 : else if (nWordSize == 2)
967 : {
968 0 : CPLAssert(nDiff >= -32768 && nDiff <= 32767);
969 0 : WriteShort(fp, (short)nDiff);
970 : }
971 : else
972 : {
973 0 : WriteInt(fp, nDiff);
974 : }
975 15440 : nLastVal = nVal;
976 : }
977 : }
978 : }
979 : else
980 : {
981 10 : float fMinVal = ((float*)pTileBuffer)[0];
982 10 : float fMaxVal = fMinVal;
983 41301 : for(k=1;k<nReqYSize*nReqXSize;k++)
984 : {
985 41291 : float fVal = ((float*)pTileBuffer)[k];
986 41291 : if (fVal < fMinVal) fMinVal = fVal;
987 41291 : if (fVal > fMaxVal) fMaxVal = fVal;
988 : }
989 :
990 10 : float fIntRange = (fMaxVal - fMinVal) / fVertPres;
991 10 : float fScale = (fMinVal == fMaxVal) ? 1 : (fMaxVal - fMinVal) / fIntRange;
992 10 : float fOffset = fMinVal;
993 10 : WriteFloat(fp, fScale); /* scale */
994 10 : WriteFloat(fp, fOffset); /* offset */
995 301 : for(k=0;k<nReqYSize;k++)
996 : {
997 291 : float fLastVal = ((float*)pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
998 291 : float fIntLastVal = (fLastVal - fOffset) / fScale;
999 291 : CPLAssert(fIntLastVal >= -2147483648.0f && fIntLastVal <= 2147483647.0f);
1000 291 : int nLastVal = (int)fIntLastVal;
1001 291 : GByte nWordSize = 1;
1002 41301 : for(l=1;l<nReqXSize;l++)
1003 : {
1004 41010 : float fVal = ((float*)pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + l];
1005 41010 : float fIntVal = (fVal - fOffset) / fScale;
1006 41010 : CPLAssert(fIntVal >= -2147483648.0f && fIntVal <= 2147483647.0f);
1007 41010 : int nVal = (int)fIntVal;
1008 41010 : int nDiff = nVal - nLastVal;
1009 41010 : CPLAssert((int)((GIntBig)nVal - nLastVal) == nDiff);
1010 41010 : if (nDiff < -32768 || nDiff > 32767)
1011 : {
1012 0 : nWordSize = 4;
1013 0 : break;
1014 : }
1015 41010 : if (nDiff < -128 || nDiff > 127)
1016 201 : nWordSize = 2;
1017 41010 : nLastVal = nVal;
1018 : }
1019 :
1020 291 : VSIFWriteL(&nWordSize, 1, 1, fp);
1021 291 : fLastVal = ((float*)pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
1022 291 : fIntLastVal = (fLastVal - fOffset) / fScale;
1023 291 : nLastVal = (int)fIntLastVal;
1024 291 : WriteInt(fp, nLastVal);
1025 41301 : for(l=1;l<nReqXSize;l++)
1026 : {
1027 41010 : float fVal = ((float*)pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + l];
1028 41010 : float fIntVal = (fVal - fOffset) / fScale;
1029 41010 : int nVal = (int)fIntVal;
1030 41010 : int nDiff = nVal - nLastVal;
1031 41010 : CPLAssert((int)((GIntBig)nVal - nLastVal) == nDiff);
1032 41010 : if (nWordSize == 1)
1033 : {
1034 5010 : CPLAssert(nDiff >= -128 && nDiff <= 127);
1035 5010 : char chDiff = (char)nDiff;
1036 5010 : VSIFWriteL(&chDiff, 1, 1, fp);
1037 : }
1038 36000 : else if (nWordSize == 2)
1039 : {
1040 36000 : CPLAssert(nDiff >= -32768 && nDiff <= 32767);
1041 36000 : WriteShort(fp, (short)nDiff);
1042 : }
1043 : else
1044 : {
1045 0 : WriteInt(fp, nDiff);
1046 : }
1047 41010 : nLastVal = nVal;
1048 : }
1049 : }
1050 : }
1051 :
1052 18 : if( pfnProgress && !pfnProgress( (j * nXBlocks + i + 1) * 1.0 / (nXBlocks * nYBlocks), NULL, pProgressData ) )
1053 : {
1054 0 : eErr = CE_Failure;
1055 0 : break;
1056 : }
1057 : }
1058 : }
1059 :
1060 15 : CPLFree(pTileBuffer);
1061 :
1062 15 : VSIFCloseL(fp);
1063 :
1064 15 : return (GDALDataset*) GDALOpen(osFilename.c_str(), GA_ReadOnly);
1065 : }
1066 :
1067 : /************************************************************************/
1068 : /* GDALRegister_HF2() */
1069 : /************************************************************************/
1070 :
1071 558 : void GDALRegister_HF2()
1072 :
1073 : {
1074 : GDALDriver *poDriver;
1075 :
1076 558 : if( GDALGetDriverByName( "HF2" ) == NULL )
1077 : {
1078 537 : poDriver = new GDALDriver();
1079 :
1080 537 : poDriver->SetDescription( "HF2" );
1081 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1082 537 : "HF2/HFZ heightfield raster" );
1083 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
1084 537 : "frmt_hf2.html" );
1085 537 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "hf2" );
1086 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
1087 : "<CreationOptionList>"
1088 : " <Option name='VERTICAL_PRECISION' type='float' default='0.01' description='Vertical precision.'/>"
1089 : " <Option name='COMPRESS' type='boolean' default='false' description='Set to true to produce a GZip compressed file.'/>"
1090 : " <Option name='BLOCKSIZE' type='int' default='256' description='Tile size.'/>"
1091 537 : "</CreationOptionList>");
1092 :
1093 537 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
1094 :
1095 537 : poDriver->pfnOpen = HF2Dataset::Open;
1096 537 : poDriver->pfnIdentify = HF2Dataset::Identify;
1097 537 : poDriver->pfnCreateCopy = HF2Dataset::CreateCopy;
1098 :
1099 537 : GetGDALDriverManager()->RegisterDriver( poDriver );
1100 : }
1101 558 : }
1102 :
|