LCOV - code coverage report
Current view: top level - frmts/leveller - levellerdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 174 510 34.1 %
Date: 2025-07-01 22:47:05 Functions: 20 56 35.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * levellerdataset.cpp,v 1.22
       3             :  *
       4             :  * Project:  Leveller TER Driver
       5             :  * Purpose:  Reader for Leveller TER documents
       6             :  * Author:   Ray Gardener, Daylon Graphics Ltd.
       7             :  *
       8             :  * Portions of this module derived from GDAL drivers by
       9             :  * Frank Warmerdam, see http://www.gdal.org
      10             :  *
      11             :  ******************************************************************************
      12             :  * Copyright (c) 2005-2007 Daylon Graphics Ltd.
      13             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
      14             :  *
      15             :  * SPDX-License-Identifier: MIT
      16             :  ****************************************************************************/
      17             : 
      18             : #include "gdal_frmts.h"
      19             : #include "gdal_pam.h"
      20             : #include "ogr_spatialref.h"
      21             : 
      22        1540 : static bool str_equal(const char *_s1, const char *_s2)
      23             : {
      24        1540 :     return 0 == strcmp(_s1, _s2);
      25             : }
      26             : 
      27             : /*GDALDataset *LevellerCreateCopy( const char *, GDALDataset *, int, char **,
      28             :                                 GDALProgressFunc pfnProgress,
      29             :                                 void * pProgressData );
      30             : */
      31             : 
      32             : /************************************************************************/
      33             : /* ==================================================================== */
      34             : /*                              LevellerDataset                         */
      35             : /* ==================================================================== */
      36             : /************************************************************************/
      37             : 
      38             : constexpr size_t kMaxTagNameLen = 63;
      39             : 
      40             : enum
      41             : {
      42             :     // Leveller coordsys types.
      43             :     LEV_COORDSYS_RASTER = 0,
      44             :     LEV_COORDSYS_LOCAL,
      45             :     LEV_COORDSYS_GEO
      46             : };
      47             : 
      48             : enum
      49             : {
      50             :     // Leveller digital axis extent styles.
      51             :     LEV_DA_POSITIONED = 0,
      52             :     LEV_DA_SIZED,
      53             :     LEV_DA_PIXEL_SIZED
      54             : };
      55             : 
      56             : typedef enum
      57             : {
      58             :     // Measurement unit IDs, OEM version.
      59             :     UNITLABEL_UNKNOWN = 0x00000000,
      60             :     UNITLABEL_PIXEL = 0x70780000,
      61             :     UNITLABEL_PERCENT = 0x25000000,
      62             : 
      63             :     UNITLABEL_RADIAN = 0x72616400,
      64             :     UNITLABEL_DEGREE = 0x64656700,
      65             :     UNITLABEL_ARCMINUTE = 0x6172636D,
      66             :     UNITLABEL_ARCSECOND = 0x61726373,
      67             : 
      68             :     UNITLABEL_YM = 0x796D0000,
      69             :     UNITLABEL_ZM = 0x7A6D0000,
      70             :     UNITLABEL_AM = 0x616D0000,
      71             :     UNITLABEL_FM = 0x666D0000,
      72             :     UNITLABEL_PM = 0x706D0000,
      73             :     UNITLABEL_A = 0x41000000,
      74             :     UNITLABEL_NM = 0x6E6D0000,
      75             :     UNITLABEL_U = 0x75000000,
      76             :     UNITLABEL_UM = 0x756D0000,
      77             :     UNITLABEL_PPT = 0x70707400,
      78             :     UNITLABEL_PT = 0x70740000,
      79             :     UNITLABEL_MM = 0x6D6D0000,
      80             :     UNITLABEL_P = 0x70000000,
      81             :     UNITLABEL_CM = 0x636D0000,
      82             :     UNITLABEL_IN = 0x696E0000,
      83             :     UNITLABEL_DFT = 0x64667400,
      84             :     UNITLABEL_DM = 0x646D0000,
      85             :     UNITLABEL_LI = 0x6C690000,
      86             :     UNITLABEL_SLI = 0x736C6900,
      87             :     UNITLABEL_SP = 0x73700000,
      88             :     UNITLABEL_FT = 0x66740000,
      89             :     UNITLABEL_SFT = 0x73667400,
      90             :     UNITLABEL_YD = 0x79640000,
      91             :     UNITLABEL_SYD = 0x73796400,
      92             :     UNITLABEL_M = 0x6D000000,
      93             :     UNITLABEL_FATH = 0x66617468,
      94             :     UNITLABEL_R = 0x72000000,
      95             :     UNITLABEL_RD = UNITLABEL_R,
      96             :     UNITLABEL_DAM = 0x64416D00,
      97             :     UNITLABEL_DKM = UNITLABEL_DAM,
      98             :     UNITLABEL_CH = 0x63680000,
      99             :     UNITLABEL_SCH = 0x73636800,
     100             :     UNITLABEL_HM = 0x686D0000,
     101             :     UNITLABEL_F = 0x66000000,
     102             :     UNITLABEL_KM = 0x6B6D0000,
     103             :     UNITLABEL_MI = 0x6D690000,
     104             :     UNITLABEL_SMI = 0x736D6900,
     105             :     UNITLABEL_NMI = 0x6E6D6900,
     106             :     UNITLABEL_MEGAM = 0x4D6D0000,
     107             :     UNITLABEL_LS = 0x6C730000,
     108             :     UNITLABEL_GM = 0x476D0000,
     109             :     UNITLABEL_LM = 0x6C6D0000,
     110             :     UNITLABEL_AU = 0x41550000,
     111             :     UNITLABEL_TM = 0x546D0000,
     112             :     UNITLABEL_LHR = 0x6C687200,
     113             :     UNITLABEL_LD = 0x6C640000,
     114             :     UNITLABEL_PETAM = 0x506D0000,
     115             :     UNITLABEL_LY = 0x6C790000,
     116             :     UNITLABEL_PC = 0x70630000,
     117             :     UNITLABEL_EXAM = 0x456D0000,
     118             :     UNITLABEL_KLY = 0x6B6C7900,
     119             :     UNITLABEL_KPC = 0x6B706300,
     120             :     UNITLABEL_ZETTAM = 0x5A6D0000,
     121             :     UNITLABEL_MLY = 0x4D6C7900,
     122             :     UNITLABEL_MPC = 0x4D706300,
     123             :     UNITLABEL_YOTTAM = 0x596D0000
     124             : } UNITLABEL;
     125             : 
     126             : typedef struct
     127             : {
     128             :     const char *pszID;
     129             :     double dScale;
     130             :     UNITLABEL oemCode;
     131             : } measurement_unit;
     132             : 
     133             : constexpr double kdays_per_year = 365.25;
     134             : constexpr double kdLStoM = 299792458.0;
     135             : constexpr double kdLYtoM = kdLStoM * kdays_per_year * 24 * 60 * 60;
     136             : constexpr double kdInch = 0.3048 / 12;
     137             : constexpr double kPI = M_PI;
     138             : 
     139             : constexpr int kFirstLinearMeasureIdx = 9;
     140             : 
     141             : static const measurement_unit kUnits[] = {
     142             :     {"", 1.0, UNITLABEL_UNKNOWN},
     143             :     {"px", 1.0, UNITLABEL_PIXEL},
     144             :     {"%", 1.0, UNITLABEL_PERCENT},  // not actually used
     145             : 
     146             :     {"rad", 1.0, UNITLABEL_RADIAN},
     147             :     {"\xB0", kPI / 180.0, UNITLABEL_DEGREE},  // \xB0 is Unicode degree symbol
     148             :     {"d", kPI / 180.0, UNITLABEL_DEGREE},
     149             :     {"deg", kPI / 180.0, UNITLABEL_DEGREE},
     150             :     {"'", kPI / (60.0 * 180.0), UNITLABEL_ARCMINUTE},
     151             :     {"\"", kPI / (3600.0 * 180.0), UNITLABEL_ARCSECOND},
     152             : 
     153             :     {"ym", 1.0e-24, UNITLABEL_YM},
     154             :     {"zm", 1.0e-21, UNITLABEL_ZM},
     155             :     {"am", 1.0e-18, UNITLABEL_AM},
     156             :     {"fm", 1.0e-15, UNITLABEL_FM},
     157             :     {"pm", 1.0e-12, UNITLABEL_PM},
     158             :     {"A", 1.0e-10, UNITLABEL_A},
     159             :     {"nm", 1.0e-9, UNITLABEL_NM},
     160             :     {"u", 1.0e-6, UNITLABEL_U},
     161             :     {"um", 1.0e-6, UNITLABEL_UM},
     162             :     {"ppt", kdInch / 72.27, UNITLABEL_PPT},
     163             :     {"pt", kdInch / 72.0, UNITLABEL_PT},
     164             :     {"mm", 1.0e-3, UNITLABEL_MM},
     165             :     {"p", kdInch / 6.0, UNITLABEL_P},
     166             :     {"cm", 1.0e-2, UNITLABEL_CM},
     167             :     {"in", kdInch, UNITLABEL_IN},
     168             :     {"dft", 0.03048, UNITLABEL_DFT},
     169             :     {"dm", 0.1, UNITLABEL_DM},
     170             :     {"li", 0.2011684 /* GDAL 0.20116684023368047 ? */, UNITLABEL_LI},
     171             :     {"sli", 0.201168402336805, UNITLABEL_SLI},
     172             :     {"sp", 0.2286, UNITLABEL_SP},
     173             :     {"ft", 0.3048, UNITLABEL_FT},
     174             :     {"sft", 1200.0 / 3937.0, UNITLABEL_SFT},
     175             :     {"yd", 0.9144, UNITLABEL_YD},
     176             :     {"syd", 0.914401828803658, UNITLABEL_SYD},
     177             :     {"m", 1.0, UNITLABEL_M},
     178             :     {"fath", 1.8288, UNITLABEL_FATH},
     179             :     {"rd", 5.02921, UNITLABEL_RD},
     180             :     {"dam", 10.0, UNITLABEL_DAM},
     181             :     {"dkm", 10.0, UNITLABEL_DKM},
     182             :     {"ch", 20.1168 /* GDAL: 2.0116684023368047 ? */, UNITLABEL_CH},
     183             :     {"sch", 20.1168402336805, UNITLABEL_SCH},
     184             :     {"hm", 100.0, UNITLABEL_HM},
     185             :     {"f", 201.168, UNITLABEL_F},
     186             :     {"km", 1000.0, UNITLABEL_KM},
     187             :     {"mi", 1609.344, UNITLABEL_MI},
     188             :     {"smi", 1609.34721869444, UNITLABEL_SMI},
     189             :     {"nmi", 1853.0, UNITLABEL_NMI},
     190             :     {"Mm", 1.0e+6, UNITLABEL_MEGAM},
     191             :     {"ls", kdLStoM, UNITLABEL_LS},
     192             :     {"Gm", 1.0e+9, UNITLABEL_GM},
     193             :     {"lm", kdLStoM * 60, UNITLABEL_LM},
     194             :     {"AU", 8.317 * kdLStoM * 60, UNITLABEL_AU},
     195             :     {"Tm", 1.0e+12, UNITLABEL_TM},
     196             :     {"lhr", 60.0 * 60.0 * kdLStoM, UNITLABEL_LHR},
     197             :     {"ld", 24 * 60.0 * 60.0 * kdLStoM, UNITLABEL_LD},
     198             :     {"Pm", 1.0e+15, UNITLABEL_PETAM},
     199             :     {"ly", kdLYtoM, UNITLABEL_LY},
     200             :     {"pc", 3.2616 * kdLYtoM, UNITLABEL_PC},
     201             :     {"Em", 1.0e+18, UNITLABEL_EXAM},
     202             :     {"kly", 1.0e+3 * kdLYtoM, UNITLABEL_KLY},
     203             :     {"kpc", 3.2616 * 1.0e+3 * kdLYtoM, UNITLABEL_KPC},
     204             :     {"Zm", 1.0e+21, UNITLABEL_ZETTAM},
     205             :     {"Mly", 1.0e+6 * kdLYtoM, UNITLABEL_MLY},
     206             :     {"Mpc", 3.2616 * 1.0e+6 * kdLYtoM, UNITLABEL_MPC},
     207             :     {"Ym", 1.0e+24, UNITLABEL_YOTTAM}};
     208             : 
     209             : // ----------------------------------------------------------------
     210             : 
     211           0 : static bool approx_equal(double a, double b)
     212             : {
     213           0 :     const double epsilon = 1e-5;
     214           0 :     return fabs(a - b) <= epsilon;
     215             : }
     216             : 
     217             : // ----------------------------------------------------------------
     218             : 
     219             : class LevellerRasterBand;
     220             : 
     221             : class LevellerDataset final : public GDALPamDataset
     222             : {
     223             :     friend class LevellerRasterBand;
     224             :     friend class digital_axis;
     225             : 
     226             :     int m_version;
     227             : 
     228             :     char *m_pszFilename;
     229             :     OGRSpatialReference m_oSRS{};
     230             : 
     231             :     // char              m_szUnits[8];
     232             :     char m_szElevUnits[8];
     233             :     double m_dElevScale;  // physical-to-logical scaling.
     234             :     double m_dElevBase;   // logical offset.
     235             :     GDALGeoTransform m_gt{};
     236             :     // double            m_dMeasurePerPixel;
     237             :     double m_dLogSpan[2];
     238             : 
     239             :     VSILFILE *m_fp;
     240             :     vsi_l_offset m_nDataOffset;
     241             : 
     242             :     bool load_from_file(VSILFILE *, const char *);
     243             : 
     244             :     static bool locate_data(vsi_l_offset &, size_t &, VSILFILE *, const char *);
     245             :     static bool get(int &, VSILFILE *, const char *);
     246             : 
     247             :     static bool get(size_t &n, VSILFILE *fp, const char *psz)
     248             :     {
     249             :         return get((int &)n, fp, psz);
     250             :     }
     251             : 
     252             :     static bool get(double &, VSILFILE *, const char *);
     253             :     static bool get(char *, size_t, VSILFILE *, const char *);
     254             : 
     255             :     bool write_header();
     256             :     bool write_tag(const char *, int);
     257             :     bool write_tag(const char *, size_t);
     258             :     bool write_tag(const char *, double);
     259             :     bool write_tag(const char *, const char *);
     260             :     bool write_tag_start(const char *, size_t);
     261             :     bool write(int);
     262             :     bool write(size_t);
     263             :     bool write(double);
     264             :     bool write_byte(size_t);
     265             : 
     266             :     static const measurement_unit *get_uom(const char *);
     267             :     static const measurement_unit *get_uom(UNITLABEL);
     268             :     static const measurement_unit *get_uom(double);
     269             : 
     270             :     static bool convert_measure(double, double &, const char *pszUnitsFrom);
     271             :     bool make_local_coordsys(const char *pszName, const char *pszUnits);
     272             :     bool make_local_coordsys(const char *pszName, UNITLABEL);
     273             :     const char *code_to_id(UNITLABEL) const;
     274             :     UNITLABEL id_to_code(const char *) const;
     275             :     UNITLABEL meter_measure_to_code(double) const;
     276             :     bool compute_elev_scaling(const OGRSpatialReference &);
     277             :     void raw_to_proj(double, double, double &, double &) const;
     278             : 
     279             :   public:
     280             :     LevellerDataset();
     281             :     virtual ~LevellerDataset();
     282             : 
     283             :     static GDALDataset *Open(GDALOpenInfo *);
     284             :     static int Identify(GDALOpenInfo *);
     285             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     286             :                                int nBandsIn, GDALDataType eType,
     287             :                                char **papszOptions);
     288             : 
     289             :     virtual CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
     290             : 
     291             :     virtual CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
     292             : 
     293             :     const OGRSpatialReference *GetSpatialRef() const override;
     294             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
     295             : };
     296             : 
     297             : class digital_axis
     298             : {
     299             :   public:
     300           0 :     digital_axis() : m_eStyle(LEV_DA_PIXEL_SIZED), m_fixedEnd(0)
     301             :     {
     302           0 :         m_d[0] = 0.0;
     303           0 :         m_d[1] = 0.0;
     304           0 :     }
     305             : 
     306           0 :     bool get(LevellerDataset &ds, VSILFILE *fp, int n)
     307             :     {
     308             :         char szTag[32];
     309           0 :         snprintf(szTag, sizeof(szTag), "coordsys_da%d_style", n);
     310           0 :         if (!ds.get(m_eStyle, fp, szTag))
     311           0 :             return false;
     312           0 :         snprintf(szTag, sizeof(szTag), "coordsys_da%d_fixedend", n);
     313           0 :         if (!ds.get(m_fixedEnd, fp, szTag))
     314           0 :             return false;
     315           0 :         snprintf(szTag, sizeof(szTag), "coordsys_da%d_v0", n);
     316           0 :         if (!ds.get(m_d[0], fp, szTag))
     317           0 :             return false;
     318           0 :         snprintf(szTag, sizeof(szTag), "coordsys_da%d_v1", n);
     319           0 :         if (!ds.get(m_d[1], fp, szTag))
     320           0 :             return false;
     321           0 :         return true;
     322             :     }
     323             : 
     324           0 :     double origin(size_t pixels) const
     325             :     {
     326           0 :         if (m_fixedEnd == 1)
     327             :         {
     328           0 :             switch (m_eStyle)
     329             :             {
     330           0 :                 case LEV_DA_SIZED:
     331           0 :                     return m_d[1] + m_d[0];
     332             : 
     333           0 :                 case LEV_DA_PIXEL_SIZED:
     334           0 :                     return m_d[1] + (m_d[0] * (pixels - 1));
     335             :             }
     336             :         }
     337           0 :         return m_d[0];
     338             :     }
     339             : 
     340           0 :     double scaling(size_t pixels) const
     341             :     {
     342           0 :         CPLAssert(pixels > 1);
     343           0 :         if (m_eStyle == LEV_DA_PIXEL_SIZED)
     344           0 :             return m_d[1 - m_fixedEnd];
     345             : 
     346           0 :         return this->length(static_cast<int>(pixels)) / (pixels - 1);
     347             :     }
     348             : 
     349           0 :     double length(int pixels) const
     350             :     {
     351             :         // Return the signed length of the axis.
     352             : 
     353           0 :         switch (m_eStyle)
     354             :         {
     355           0 :             case LEV_DA_POSITIONED:
     356           0 :                 return m_d[1] - m_d[0];
     357             : 
     358           0 :             case LEV_DA_SIZED:
     359           0 :                 return m_d[1 - m_fixedEnd];
     360             : 
     361           0 :             case LEV_DA_PIXEL_SIZED:
     362           0 :                 return m_d[1 - m_fixedEnd] * (pixels - 1);
     363             :         }
     364           0 :         CPLAssert(false);
     365             :         return 0.0;
     366             :     }
     367             : 
     368             :   protected:
     369             :     int m_eStyle;
     370             :     int m_fixedEnd;
     371             :     double m_d[2];
     372             : };
     373             : 
     374             : /************************************************************************/
     375             : /* ==================================================================== */
     376             : /*                            LevellerRasterBand                        */
     377             : /* ==================================================================== */
     378             : /************************************************************************/
     379             : 
     380             : class LevellerRasterBand final : public GDALPamRasterBand
     381             : {
     382             :     friend class LevellerDataset;
     383             : 
     384             :     float *m_pLine;
     385             :     bool m_bFirstTime;
     386             : 
     387             :   public:
     388             :     explicit LevellerRasterBand(LevellerDataset *);
     389             :     virtual ~LevellerRasterBand();
     390             : 
     391             :     bool Init();
     392             : 
     393             :     // Geomeasure support.
     394             :     virtual const char *GetUnitType() override;
     395             :     virtual double GetScale(int *pbSuccess = nullptr) override;
     396             :     virtual double GetOffset(int *pbSuccess = nullptr) override;
     397             : 
     398             :     virtual CPLErr IReadBlock(int, int, void *) override;
     399             :     virtual CPLErr IWriteBlock(int, int, void *) override;
     400             :     virtual CPLErr SetUnitType(const char *) override;
     401             : };
     402             : 
     403             : /************************************************************************/
     404             : /*                         LevellerRasterBand()                         */
     405             : /************************************************************************/
     406             : 
     407           2 : LevellerRasterBand::LevellerRasterBand(LevellerDataset *poDSIn)
     408           2 :     : m_pLine(nullptr), m_bFirstTime(true)
     409             : {
     410           2 :     poDS = poDSIn;
     411           2 :     nBand = 1;
     412             : 
     413           2 :     eDataType = GDT_Float32;
     414             : 
     415           2 :     nBlockXSize = poDS->GetRasterXSize();
     416           2 :     nBlockYSize = 1;  // poDS->GetRasterYSize();
     417           2 : }
     418             : 
     419             : /************************************************************************/
     420             : /*                           Init()                                     */
     421             : /************************************************************************/
     422             : 
     423           2 : bool LevellerRasterBand::Init()
     424             : {
     425           2 :     m_pLine = reinterpret_cast<float *>(
     426           2 :         VSI_MALLOC2_VERBOSE(sizeof(float), nBlockXSize));
     427           2 :     return m_pLine != nullptr;
     428             : }
     429             : 
     430           4 : LevellerRasterBand::~LevellerRasterBand()
     431             : {
     432           2 :     CPLFree(m_pLine);
     433           4 : }
     434             : 
     435             : /************************************************************************/
     436             : /*                             IWriteBlock()                            */
     437             : /************************************************************************/
     438             : 
     439           0 : CPLErr LevellerRasterBand::IWriteBlock(CPL_UNUSED int nBlockXOff,
     440             :                                        int nBlockYOff, void *pImage)
     441             : {
     442           0 :     CPLAssert(nBlockXOff == 0);
     443           0 :     CPLAssert(pImage != nullptr);
     444           0 :     CPLAssert(m_pLine != nullptr);
     445             : 
     446             :     /*  #define sgn(_n) ((_n) < 0 ? -1 : ((_n) > 0 ? 1 : 0) )
     447             :         #define sround(_f)                          \
     448             :         (int)((_f) + (0.5 * sgn(_f)))
     449             :     */
     450           0 :     const size_t pixelsize = sizeof(float);
     451             : 
     452           0 :     LevellerDataset &ds = *reinterpret_cast<LevellerDataset *>(poDS);
     453           0 :     if (m_bFirstTime)
     454             :     {
     455           0 :         m_bFirstTime = false;
     456           0 :         if (!ds.write_header())
     457           0 :             return CE_Failure;
     458           0 :         ds.m_nDataOffset = VSIFTellL(ds.m_fp);
     459             :     }
     460           0 :     const size_t rowbytes = nBlockXSize * pixelsize;
     461           0 :     const float *pfImage = reinterpret_cast<float *>(pImage);
     462             : 
     463           0 :     if (0 ==
     464           0 :         VSIFSeekL(ds.m_fp, ds.m_nDataOffset + nBlockYOff * rowbytes, SEEK_SET))
     465             :     {
     466           0 :         for (size_t x = 0; x < (size_t)nBlockXSize; x++)
     467             :         {
     468             :             // Convert logical elevations to physical.
     469           0 :             m_pLine[x] = static_cast<float>((pfImage[x] - ds.m_dElevBase) /
     470           0 :                                             ds.m_dElevScale);
     471             :         }
     472             : 
     473             : #ifdef CPL_MSB
     474             :         GDALSwapWords(m_pLine, pixelsize, nBlockXSize, pixelsize);
     475             : #endif
     476           0 :         if (1 == VSIFWriteL(m_pLine, rowbytes, 1, ds.m_fp))
     477           0 :             return CE_None;
     478             :     }
     479             : 
     480           0 :     return CE_Failure;
     481             : }
     482             : 
     483           0 : CPLErr LevellerRasterBand::SetUnitType(const char *psz)
     484             : {
     485           0 :     LevellerDataset &ds = *reinterpret_cast<LevellerDataset *>(poDS);
     486             : 
     487           0 :     if (strlen(psz) >= sizeof(ds.m_szElevUnits))
     488           0 :         return CE_Failure;
     489             : 
     490           0 :     strcpy(ds.m_szElevUnits, psz);
     491             : 
     492           0 :     return CE_None;
     493             : }
     494             : 
     495             : /************************************************************************/
     496             : /*                             IReadBlock()                             */
     497             : /************************************************************************/
     498             : 
     499          96 : CPLErr LevellerRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
     500             :                                       void *pImage)
     501             : 
     502             : {
     503             :     CPLAssert(sizeof(float) == sizeof(GInt32));
     504          96 :     CPLAssert(nBlockXOff == 0);
     505          96 :     CPLAssert(pImage != nullptr);
     506             : 
     507          96 :     LevellerDataset *poGDS = reinterpret_cast<LevellerDataset *>(poDS);
     508             : 
     509             :     /* -------------------------------------------------------------------- */
     510             :     /*      Seek to scanline.                                               */
     511             :     /* -------------------------------------------------------------------- */
     512          96 :     const size_t rowbytes = nBlockXSize * sizeof(float);
     513             : 
     514          96 :     if (0 != VSIFSeekL(poGDS->m_fp,
     515          96 :                        poGDS->m_nDataOffset + nBlockYOff * rowbytes, SEEK_SET))
     516             :     {
     517           0 :         CPLError(CE_Failure, CPLE_FileIO, "Leveller seek failed: %s",
     518           0 :                  VSIStrerror(errno));
     519           0 :         return CE_Failure;
     520             :     }
     521             : 
     522             :     /* -------------------------------------------------------------------- */
     523             :     /*      Read the scanline into the image buffer.                        */
     524             :     /* -------------------------------------------------------------------- */
     525             : 
     526          96 :     if (VSIFReadL(pImage, rowbytes, 1, poGDS->m_fp) != 1)
     527             :     {
     528           0 :         CPLError(CE_Failure, CPLE_FileIO, "Leveller read failed: %s",
     529           0 :                  VSIStrerror(errno));
     530           0 :         return CE_Failure;
     531             :     }
     532             : 
     533             : /* -------------------------------------------------------------------- */
     534             : /*      Swap on MSB platforms.                                          */
     535             : /* -------------------------------------------------------------------- */
     536             : #ifdef CPL_MSB
     537             :     GDALSwapWords(pImage, 4, nRasterXSize, 4);
     538             : #endif
     539             : 
     540             :     /* -------------------------------------------------------------------- */
     541             :     /*      Convert from legacy-format fixed-point if necessary.            */
     542             :     /* -------------------------------------------------------------------- */
     543          96 :     float *pf = reinterpret_cast<float *>(pImage);
     544             : 
     545          96 :     if (poGDS->m_version < 6)
     546             :     {
     547           0 :         GInt32 *pi = reinterpret_cast<int *>(pImage);
     548           0 :         for (size_t i = 0; i < (size_t)nBlockXSize; i++)
     549           0 :             pf[i] = static_cast<float>(pi[i]) / 65536;
     550             :     }
     551             : 
     552             : /* -------------------------------------------------------------------- */
     553             : /*      Convert raw elevations to realworld elevs.                      */
     554             : /* -------------------------------------------------------------------- */
     555             : #if 0
     556             :     for(size_t i = 0; i < nBlockXSize; i++)
     557             :         pf[i] *= poGDS->m_dWorldscale; //this->GetScale();
     558             : #endif
     559             : 
     560          96 :     return CE_None;
     561             : }
     562             : 
     563             : /************************************************************************/
     564             : /*                            GetUnitType()                             */
     565             : /************************************************************************/
     566           0 : const char *LevellerRasterBand::GetUnitType()
     567             : {
     568             :     // Return elevation units.
     569             : 
     570           0 :     LevellerDataset *poGDS = reinterpret_cast<LevellerDataset *>(poDS);
     571             : 
     572           0 :     return poGDS->m_szElevUnits;
     573             : }
     574             : 
     575             : /************************************************************************/
     576             : /*                              GetScale()                              */
     577             : /************************************************************************/
     578             : 
     579           0 : double LevellerRasterBand::GetScale(int *pbSuccess)
     580             : {
     581           0 :     LevellerDataset *poGDS = reinterpret_cast<LevellerDataset *>(poDS);
     582           0 :     if (pbSuccess != nullptr)
     583           0 :         *pbSuccess = TRUE;
     584           0 :     return poGDS->m_dElevScale;
     585             : }
     586             : 
     587             : /************************************************************************/
     588             : /*                             GetOffset()                              */
     589             : /************************************************************************/
     590             : 
     591           0 : double LevellerRasterBand::GetOffset(int *pbSuccess)
     592             : {
     593           0 :     LevellerDataset *poGDS = reinterpret_cast<LevellerDataset *>(poDS);
     594           0 :     if (pbSuccess != nullptr)
     595           0 :         *pbSuccess = TRUE;
     596           0 :     return poGDS->m_dElevBase;
     597             : }
     598             : 
     599             : /************************************************************************/
     600             : /* ==================================================================== */
     601             : /*                              LevellerDataset                         */
     602             : /* ==================================================================== */
     603             : /************************************************************************/
     604             : 
     605             : /************************************************************************/
     606             : /*                          LevellerDataset()                           */
     607             : /************************************************************************/
     608             : 
     609           6 : LevellerDataset::LevellerDataset()
     610             :     : m_version(0), m_pszFilename(nullptr), m_dElevScale(), m_dElevBase(),
     611           6 :       m_fp(nullptr), m_nDataOffset()
     612             : {
     613           6 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     614           6 :     memset(m_szElevUnits, 0, sizeof(m_szElevUnits));
     615           6 :     memset(m_dLogSpan, 0, sizeof(m_dLogSpan));
     616           6 : }
     617             : 
     618             : /************************************************************************/
     619             : /*                          ~LevellerDataset()                          */
     620             : /************************************************************************/
     621             : 
     622          12 : LevellerDataset::~LevellerDataset()
     623             : {
     624           6 :     FlushCache(true);
     625             : 
     626           6 :     CPLFree(m_pszFilename);
     627             : 
     628           6 :     if (m_fp != nullptr)
     629           4 :         VSIFCloseL(m_fp);
     630          12 : }
     631             : 
     632           0 : static double degrees_to_radians(double d)
     633             : {
     634           0 :     return d * (M_PI / 180);
     635             : }
     636             : 
     637           0 : static double average(double a, double b)
     638             : {
     639           0 :     return 0.5 * (a + b);
     640             : }
     641             : 
     642           0 : void LevellerDataset::raw_to_proj(double x, double y, double &xp,
     643             :                                   double &yp) const
     644             : {
     645           0 :     xp = x * m_gt[1] + m_gt[0];
     646           0 :     yp = y * m_gt[5] + m_gt[3];
     647           0 : }
     648             : 
     649           0 : bool LevellerDataset::compute_elev_scaling(const OGRSpatialReference &sr)
     650             : {
     651           0 :     const char *pszGroundUnits = nullptr;
     652             : 
     653           0 :     if (!sr.IsGeographic())
     654             :     {
     655             :         // For projected or local CS, the elev scale is
     656             :         // the average ground scale.
     657           0 :         m_dElevScale = average(m_gt[1], m_gt[5]);
     658             : 
     659           0 :         const double dfLinear = sr.GetLinearUnits();
     660           0 :         const measurement_unit *pu = this->get_uom(dfLinear);
     661           0 :         if (pu == nullptr)
     662           0 :             return false;
     663             : 
     664           0 :         pszGroundUnits = pu->pszID;
     665             :     }
     666             :     else
     667             :     {
     668           0 :         pszGroundUnits = "m";
     669             : 
     670           0 :         const double kdEarthCircumPolar = 40007849;
     671           0 :         const double kdEarthCircumEquat = 40075004;
     672             : 
     673           0 :         const double xr = 0.5 * nRasterXSize;
     674           0 :         const double yr = 0.5 * nRasterYSize;
     675             : 
     676             :         double xg[2], yg[2];
     677           0 :         raw_to_proj(xr, yr, xg[0], yg[0]);
     678           0 :         raw_to_proj(xr + 1, yr + 1, xg[1], yg[1]);
     679             : 
     680             :         // The earths' circumference shrinks using a sin()
     681             :         // curve as we go up in latitude.
     682             :         const double dLatCircum =
     683           0 :             kdEarthCircumEquat * sin(degrees_to_radians(90.0 - yg[0]));
     684             : 
     685             :         // Derive meter distance between geolongitudes
     686             :         // in xg[0] and xg[1].
     687           0 :         const double dx = fabs(xg[1] - xg[0]) / 360.0 * dLatCircum;
     688           0 :         const double dy = fabs(yg[1] - yg[0]) / 360.0 * kdEarthCircumPolar;
     689             : 
     690           0 :         m_dElevScale = average(dx, dy);
     691             :     }
     692             : 
     693           0 :     m_dElevBase = m_dLogSpan[0];
     694             : 
     695             :     // Convert from ground units to elev units.
     696           0 :     const measurement_unit *puG = this->get_uom(pszGroundUnits);
     697           0 :     const measurement_unit *puE = this->get_uom(m_szElevUnits);
     698             : 
     699           0 :     if (puG == nullptr || puE == nullptr)
     700           0 :         return false;
     701             : 
     702           0 :     const double g_to_e = puG->dScale / puE->dScale;
     703             : 
     704           0 :     m_dElevScale *= g_to_e;
     705           0 :     return true;
     706             : }
     707             : 
     708           0 : bool LevellerDataset::write_header()
     709             : {
     710             :     char szHeader[5];
     711           0 :     strcpy(szHeader, "trrn");
     712           0 :     szHeader[4] = 7;  // TER v7 introduced w/ Lev 2.6.
     713             : 
     714           0 :     if (1 != VSIFWriteL(szHeader, 5, 1, m_fp) ||
     715           0 :         !this->write_tag("hf_w", (size_t)nRasterXSize) ||
     716           0 :         !this->write_tag("hf_b", (size_t)nRasterYSize))
     717             :     {
     718           0 :         CPLError(CE_Failure, CPLE_FileIO, "Could not write header");
     719           0 :         return false;
     720             :     }
     721             : 
     722           0 :     m_dElevBase = 0.0;
     723           0 :     m_dElevScale = 1.0;
     724             : 
     725           0 :     if (m_oSRS.IsEmpty())
     726             :     {
     727           0 :         write_tag("csclass", LEV_COORDSYS_RASTER);
     728             :     }
     729             :     else
     730             :     {
     731           0 :         char *pszWkt = nullptr;
     732           0 :         m_oSRS.exportToWkt(&pszWkt);
     733           0 :         if (pszWkt)
     734           0 :             write_tag("coordsys_wkt", pszWkt);
     735           0 :         CPLFree(pszWkt);
     736           0 :         const UNITLABEL units_elev = this->id_to_code(m_szElevUnits);
     737             : 
     738           0 :         const int bHasECS =
     739           0 :             (units_elev != UNITLABEL_PIXEL && units_elev != UNITLABEL_UNKNOWN);
     740             : 
     741           0 :         write_tag("coordsys_haselevm", bHasECS);
     742             : 
     743           0 :         if (bHasECS)
     744             :         {
     745           0 :             if (!this->compute_elev_scaling(m_oSRS))
     746           0 :                 return false;
     747             : 
     748             :             // Raw-to-real units scaling.
     749           0 :             write_tag("coordsys_em_scale", m_dElevScale);
     750             : 
     751             :             // Elev offset, in real units.
     752           0 :             write_tag("coordsys_em_base", m_dElevBase);
     753           0 :             write_tag("coordsys_em_units", units_elev);
     754             :         }
     755             : 
     756           0 :         if (m_oSRS.IsLocal())
     757             :         {
     758           0 :             write_tag("csclass", LEV_COORDSYS_LOCAL);
     759             : 
     760           0 :             const double dfLinear = m_oSRS.GetLinearUnits();
     761           0 :             const int n = this->meter_measure_to_code(dfLinear);
     762           0 :             write_tag("coordsys_units", n);
     763             :         }
     764             :         else
     765             :         {
     766           0 :             write_tag("csclass", LEV_COORDSYS_GEO);
     767             :         }
     768             : 
     769           0 :         if (m_gt[2] != 0.0 || m_gt[4] != 0.0)
     770             :         {
     771           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     772             :                      "Cannot handle rotated geotransform");
     773           0 :             return false;
     774             :         }
     775             : 
     776             :         // todo: GDAL gridpost spacing is based on extent / rastersize
     777             :         // instead of extent / (rastersize-1) like Leveller.
     778             :         // We need to look into this and adjust accordingly.
     779             : 
     780             :         // Write north-south digital axis.
     781           0 :         write_tag("coordsys_da0_style", LEV_DA_PIXEL_SIZED);
     782           0 :         write_tag("coordsys_da0_fixedend", 0);
     783           0 :         write_tag("coordsys_da0_v0", m_gt[3]);
     784           0 :         write_tag("coordsys_da0_v1", m_gt[5]);
     785             : 
     786             :         // Write east-west digital axis.
     787           0 :         write_tag("coordsys_da1_style", LEV_DA_PIXEL_SIZED);
     788           0 :         write_tag("coordsys_da1_fixedend", 0);
     789           0 :         write_tag("coordsys_da1_v0", m_gt[0]);
     790           0 :         write_tag("coordsys_da1_v1", m_gt[1]);
     791             :     }
     792             : 
     793           0 :     this->write_tag_start("hf_data",
     794           0 :                           sizeof(float) * nRasterXSize * nRasterYSize);
     795             : 
     796           0 :     return true;
     797             : }
     798             : 
     799             : /************************************************************************/
     800             : /*                          SetGeoTransform()                           */
     801             : /************************************************************************/
     802             : 
     803           0 : CPLErr LevellerDataset::SetGeoTransform(const GDALGeoTransform &gt)
     804             : {
     805           0 :     m_gt = gt;
     806             : 
     807           0 :     return CE_None;
     808             : }
     809             : 
     810             : /************************************************************************/
     811             : /*                           SetSpatialRef()                            */
     812             : /************************************************************************/
     813             : 
     814           0 : CPLErr LevellerDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
     815             : {
     816           0 :     m_oSRS.Clear();
     817           0 :     if (poSRS)
     818           0 :         m_oSRS = *poSRS;
     819             : 
     820           0 :     return CE_None;
     821             : }
     822             : 
     823             : /************************************************************************/
     824             : /*                           Create()                                   */
     825             : /************************************************************************/
     826          49 : GDALDataset *LevellerDataset::Create(const char *pszFilename, int nXSize,
     827             :                                      int nYSize, int nBandsIn,
     828             :                                      GDALDataType eType, char **papszOptions)
     829             : {
     830          49 :     if (nBandsIn != 1)
     831             :     {
     832          22 :         CPLError(CE_Failure, CPLE_IllegalArg, "Band count must be 1");
     833          22 :         return nullptr;
     834             :     }
     835             : 
     836          27 :     if (eType != GDT_Float32)
     837             :     {
     838          23 :         CPLError(CE_Failure, CPLE_IllegalArg, "Pixel type must be Float32");
     839          23 :         return nullptr;
     840             :     }
     841             : 
     842           4 :     if (nXSize < 2 || nYSize < 2)
     843             :     {
     844           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     845             :                  "One or more raster dimensions too small");
     846           0 :         return nullptr;
     847             :     }
     848             : 
     849           4 :     LevellerDataset *poDS = new LevellerDataset;
     850             : 
     851           4 :     poDS->eAccess = GA_Update;
     852             : 
     853           4 :     poDS->m_pszFilename = CPLStrdup(pszFilename);
     854             : 
     855           4 :     poDS->m_fp = VSIFOpenL(pszFilename, "wb+");
     856             : 
     857           4 :     if (poDS->m_fp == nullptr)
     858             :     {
     859           2 :         CPLError(CE_Failure, CPLE_OpenFailed,
     860             :                  "Attempt to create file `%s' failed.", pszFilename);
     861           2 :         delete poDS;
     862           2 :         return nullptr;
     863             :     }
     864             : 
     865             :     // Header will be written the first time IWriteBlock
     866             :     // is called.
     867             : 
     868           2 :     poDS->nRasterXSize = nXSize;
     869           2 :     poDS->nRasterYSize = nYSize;
     870             : 
     871           2 :     const char *pszValue = CSLFetchNameValue(papszOptions, "MINUSERPIXELVALUE");
     872           2 :     if (pszValue != nullptr)
     873           0 :         poDS->m_dLogSpan[0] = CPLAtof(pszValue);
     874             :     else
     875             :     {
     876           2 :         delete poDS;
     877           2 :         CPLError(CE_Failure, CPLE_IllegalArg,
     878             :                  "MINUSERPIXELVALUE must be specified.");
     879           2 :         return nullptr;
     880             :     }
     881             : 
     882           0 :     pszValue = CSLFetchNameValue(papszOptions, "MAXUSERPIXELVALUE");
     883           0 :     if (pszValue != nullptr)
     884           0 :         poDS->m_dLogSpan[1] = CPLAtof(pszValue);
     885             : 
     886           0 :     if (poDS->m_dLogSpan[1] < poDS->m_dLogSpan[0])
     887             :     {
     888           0 :         double t = poDS->m_dLogSpan[0];
     889           0 :         poDS->m_dLogSpan[0] = poDS->m_dLogSpan[1];
     890           0 :         poDS->m_dLogSpan[1] = t;
     891             :     }
     892             : 
     893             :     // --------------------------------------------------------------------
     894             :     //      Instance a band.
     895             :     // --------------------------------------------------------------------
     896           0 :     LevellerRasterBand *poBand = new LevellerRasterBand(poDS);
     897           0 :     poDS->SetBand(1, poBand);
     898             : 
     899           0 :     if (!poBand->Init())
     900             :     {
     901           0 :         delete poDS;
     902           0 :         return nullptr;
     903             :     }
     904             : 
     905           0 :     return poDS;
     906             : }
     907             : 
     908           0 : bool LevellerDataset::write_byte(size_t n)
     909             : {
     910           0 :     unsigned char uch = static_cast<unsigned char>(n);
     911           0 :     return 1 == VSIFWriteL(&uch, 1, 1, m_fp);
     912             : }
     913             : 
     914           0 : bool LevellerDataset::write(int n)
     915             : {
     916           0 :     CPL_LSBPTR32(&n);
     917           0 :     return 1 == VSIFWriteL(&n, sizeof(n), 1, m_fp);
     918             : }
     919             : 
     920           0 : bool LevellerDataset::write(size_t n)
     921             : {
     922           0 :     GUInt32 n32 = (GUInt32)n;
     923           0 :     CPL_LSBPTR32(&n32);
     924           0 :     return 1 == VSIFWriteL(&n32, sizeof(n32), 1, m_fp);
     925             : }
     926             : 
     927           0 : bool LevellerDataset::write(double d)
     928             : {
     929           0 :     CPL_LSBPTR64(&d);
     930           0 :     return 1 == VSIFWriteL(&d, sizeof(d), 1, m_fp);
     931             : }
     932             : 
     933           0 : bool LevellerDataset::write_tag_start(const char *pszTag, size_t n)
     934             : {
     935           0 :     if (this->write_byte(strlen(pszTag)))
     936             :     {
     937           0 :         return (1 == VSIFWriteL(pszTag, strlen(pszTag), 1, m_fp) &&
     938           0 :                 this->write(n));
     939             :     }
     940             : 
     941           0 :     return false;
     942             : }
     943             : 
     944           0 : bool LevellerDataset::write_tag(const char *pszTag, int n)
     945             : {
     946           0 :     return (this->write_tag_start(pszTag, sizeof(n)) && this->write(n));
     947             : }
     948             : 
     949           0 : bool LevellerDataset::write_tag(const char *pszTag, size_t n)
     950             : {
     951           0 :     return (this->write_tag_start(pszTag, sizeof(n)) && this->write(n));
     952             : }
     953             : 
     954           0 : bool LevellerDataset::write_tag(const char *pszTag, double d)
     955             : {
     956           0 :     return (this->write_tag_start(pszTag, sizeof(d)) && this->write(d));
     957             : }
     958             : 
     959           0 : bool LevellerDataset::write_tag(const char *pszTag, const char *psz)
     960             : {
     961           0 :     CPLAssert(strlen(pszTag) <= kMaxTagNameLen);
     962             : 
     963             :     char sz[kMaxTagNameLen + 1];
     964           0 :     snprintf(sz, sizeof(sz), "%sl", pszTag);
     965           0 :     const size_t len = strlen(psz);
     966             : 
     967           0 :     if (len > 0 && this->write_tag(sz, len))
     968             :     {
     969           0 :         snprintf(sz, sizeof(sz), "%sd", pszTag);
     970           0 :         this->write_tag_start(sz, len);
     971           0 :         return 1 == VSIFWriteL(psz, len, 1, m_fp);
     972             :     }
     973           0 :     return false;
     974             : }
     975             : 
     976          10 : bool LevellerDataset::locate_data(vsi_l_offset &offset, size_t &len,
     977             :                                   VSILFILE *fp, const char *pszTag)
     978             : {
     979             :     // Locate the file offset of the desired tag's data.
     980             :     // If it is not available, return false.
     981             :     // If the tag is found, leave the filemark at the
     982             :     // start of its data.
     983             : 
     984          10 :     if (0 != VSIFSeekL(fp, 5, SEEK_SET))
     985           0 :         return false;
     986             : 
     987          10 :     const int kMaxDescLen = 64;
     988             :     for (;;)
     989             :     {
     990             :         unsigned char c;
     991        1498 :         if (1 != VSIFReadL(&c, sizeof(c), 1, fp))
     992          10 :             return false;
     993             : 
     994        1498 :         const size_t descriptorLen = c;
     995        1498 :         if (descriptorLen == 0 || descriptorLen > (size_t)kMaxDescLen)
     996           0 :             return false;
     997             : 
     998             :         char descriptor[kMaxDescLen + 1];
     999        1498 :         if (1 != VSIFReadL(descriptor, descriptorLen, 1, fp))
    1000           0 :             return false;
    1001             : 
    1002             :         GUInt32 datalen;
    1003        1498 :         if (1 != VSIFReadL(&datalen, sizeof(datalen), 1, fp))
    1004           0 :             return false;
    1005             : 
    1006        1498 :         CPL_LSBPTR32(&datalen);
    1007        1498 :         descriptor[descriptorLen] = 0;
    1008        1498 :         if (str_equal(descriptor, pszTag))
    1009             :         {
    1010          10 :             len = (size_t)datalen;
    1011          10 :             offset = VSIFTellL(fp);
    1012          10 :             return true;
    1013             :         }
    1014             :         else
    1015             :         {
    1016             :             // Seek to next tag.
    1017        1488 :             if (0 != VSIFSeekL(fp, (vsi_l_offset)datalen, SEEK_CUR))
    1018           0 :                 return false;
    1019             :         }
    1020        1488 :     }
    1021             : }
    1022             : 
    1023             : /************************************************************************/
    1024             : /*                                get()                                 */
    1025             : /************************************************************************/
    1026             : 
    1027           4 : bool LevellerDataset::get(int &n, VSILFILE *fp, const char *psz)
    1028             : {
    1029             :     vsi_l_offset offset;
    1030             :     size_t len;
    1031             : 
    1032           4 :     if (locate_data(offset, len, fp, psz))
    1033             :     {
    1034             :         GInt32 value;
    1035           4 :         if (1 == VSIFReadL(&value, sizeof(value), 1, fp))
    1036             :         {
    1037           4 :             CPL_LSBPTR32(&value);
    1038           4 :             n = static_cast<int>(value);
    1039           4 :             return true;
    1040             :         }
    1041             :     }
    1042           0 :     return false;
    1043             : }
    1044             : 
    1045             : /************************************************************************/
    1046             : /*                                get()                                 */
    1047             : /************************************************************************/
    1048             : 
    1049           2 : bool LevellerDataset::get(double &d, VSILFILE *fp, const char *pszTag)
    1050             : {
    1051             :     vsi_l_offset offset;
    1052             :     size_t len;
    1053             : 
    1054           2 :     if (locate_data(offset, len, fp, pszTag))
    1055             :     {
    1056           2 :         if (1 == VSIFReadL(&d, sizeof(d), 1, fp))
    1057             :         {
    1058           2 :             CPL_LSBPTR64(&d);
    1059           2 :             return true;
    1060             :         }
    1061             :     }
    1062           0 :     return false;
    1063             : }
    1064             : 
    1065             : /************************************************************************/
    1066             : /*                                get()                                 */
    1067             : /************************************************************************/
    1068           2 : bool LevellerDataset::get(char *pszValue, size_t maxchars, VSILFILE *fp,
    1069             :                           const char *pszTag)
    1070             : {
    1071             :     char szTag[65];
    1072             : 
    1073             :     // We can assume 8-bit encoding, so just go straight
    1074             :     // to the *_d tag.
    1075           2 :     snprintf(szTag, sizeof(szTag), "%sd", pszTag);
    1076             : 
    1077             :     vsi_l_offset offset;
    1078             :     size_t len;
    1079             : 
    1080           2 :     if (locate_data(offset, len, fp, szTag))
    1081             :     {
    1082           2 :         if (len > maxchars)
    1083           0 :             return false;
    1084             : 
    1085           2 :         if (1 == VSIFReadL(pszValue, len, 1, fp))
    1086             :         {
    1087           2 :             pszValue[len] = 0;  // terminate C-string
    1088           2 :             return true;
    1089             :         }
    1090             :     }
    1091             : 
    1092           0 :     return false;
    1093             : }
    1094             : 
    1095           0 : UNITLABEL LevellerDataset::meter_measure_to_code(double dM) const
    1096             : {
    1097             :     // Convert a meter conversion factor to its UOM OEM code.
    1098             :     // If the factor is close to the approximation margin, then
    1099             :     // require exact equality, otherwise be loose.
    1100             : 
    1101           0 :     const measurement_unit *pu = this->get_uom(dM);
    1102           0 :     return pu != nullptr ? pu->oemCode : UNITLABEL_UNKNOWN;
    1103             : }
    1104             : 
    1105           0 : UNITLABEL LevellerDataset::id_to_code(const char *pszUnits) const
    1106             : {
    1107             :     // Convert a readable UOM to its OEM code.
    1108             : 
    1109           0 :     const measurement_unit *pu = this->get_uom(pszUnits);
    1110           0 :     return pu != nullptr ? pu->oemCode : UNITLABEL_UNKNOWN;
    1111             : }
    1112             : 
    1113           0 : const char *LevellerDataset::code_to_id(UNITLABEL code) const
    1114             : {
    1115             :     // Convert a measurement unit's OEM ID to its readable ID.
    1116             : 
    1117           0 :     const measurement_unit *pu = this->get_uom(code);
    1118           0 :     return pu != nullptr ? pu->pszID : nullptr;
    1119             : }
    1120             : 
    1121           0 : const measurement_unit *LevellerDataset::get_uom(const char *pszUnits)
    1122             : {
    1123           0 :     for (size_t i = 0; i < CPL_ARRAYSIZE(kUnits); i++)
    1124             :     {
    1125           0 :         if (strcmp(pszUnits, kUnits[i].pszID) == 0)
    1126           0 :             return &kUnits[i];
    1127             :     }
    1128           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Unknown measurement units: %s",
    1129             :              pszUnits);
    1130           0 :     return nullptr;
    1131             : }
    1132             : 
    1133           0 : const measurement_unit *LevellerDataset::get_uom(UNITLABEL code)
    1134             : {
    1135           0 :     for (size_t i = 0; i < CPL_ARRAYSIZE(kUnits); i++)
    1136             :     {
    1137           0 :         if (kUnits[i].oemCode == code)
    1138           0 :             return &kUnits[i];
    1139             :     }
    1140           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Unknown measurement unit code: %08x",
    1141             :              code);
    1142           0 :     return nullptr;
    1143             : }
    1144             : 
    1145           0 : const measurement_unit *LevellerDataset::get_uom(double dM)
    1146             : {
    1147           0 :     for (size_t i = kFirstLinearMeasureIdx; i < CPL_ARRAYSIZE(kUnits); i++)
    1148             :     {
    1149           0 :         if (dM >= 1.0e-4)
    1150             :         {
    1151           0 :             if (approx_equal(dM, kUnits[i].dScale))
    1152           0 :                 return &kUnits[i];
    1153             :         }
    1154           0 :         else if (dM == kUnits[i].dScale)
    1155           0 :             return &kUnits[i];
    1156             :     }
    1157           0 :     CPLError(CE_Failure, CPLE_AppDefined,
    1158             :              "Unknown measurement conversion factor: %f", dM);
    1159           0 :     return nullptr;
    1160             : }
    1161             : 
    1162             : /************************************************************************/
    1163             : /*                          convert_measure()                           */
    1164             : /************************************************************************/
    1165             : 
    1166           2 : bool LevellerDataset::convert_measure(double d, double &dResult,
    1167             :                                       const char *pszSpace)
    1168             : {
    1169             :     // Convert a measure to meters.
    1170             : 
    1171          42 :     for (size_t i = kFirstLinearMeasureIdx; i < CPL_ARRAYSIZE(kUnits); i++)
    1172             :     {
    1173          42 :         if (str_equal(pszSpace, kUnits[i].pszID))
    1174             :         {
    1175           2 :             dResult = d * kUnits[i].dScale;
    1176           2 :             return true;
    1177             :         }
    1178             :     }
    1179           0 :     CPLError(CE_Failure, CPLE_FileIO, "Unknown linear measurement unit: '%s'",
    1180             :              pszSpace);
    1181           0 :     return false;
    1182             : }
    1183             : 
    1184           2 : bool LevellerDataset::make_local_coordsys(const char *pszName,
    1185             :                                           const char *pszUnits)
    1186             : {
    1187           2 :     m_oSRS.SetLocalCS(pszName);
    1188             :     double d;
    1189           4 :     return (convert_measure(1.0, d, pszUnits) &&
    1190           4 :             OGRERR_NONE == m_oSRS.SetLinearUnits(pszUnits, d));
    1191             : }
    1192             : 
    1193           0 : bool LevellerDataset::make_local_coordsys(const char *pszName, UNITLABEL code)
    1194             : {
    1195           0 :     const char *pszUnitID = code_to_id(code);
    1196           0 :     return pszUnitID != nullptr && make_local_coordsys(pszName, pszUnitID);
    1197             : }
    1198             : 
    1199             : /************************************************************************/
    1200             : /*                            load_from_file()                            */
    1201             : /************************************************************************/
    1202             : 
    1203           2 : bool LevellerDataset::load_from_file(VSILFILE *file, const char *pszFilename)
    1204             : {
    1205             :     // get hf dimensions
    1206           2 :     if (!get(nRasterXSize, file, "hf_w"))
    1207             :     {
    1208           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1209             :                  "Cannot determine heightfield width.");
    1210           0 :         return false;
    1211             :     }
    1212             : 
    1213           2 :     if (!get(nRasterYSize, file, "hf_b"))
    1214             :     {
    1215           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1216             :                  "Cannot determine heightfield breadth.");
    1217           0 :         return false;
    1218             :     }
    1219             : 
    1220           2 :     if (nRasterXSize < 2 || nRasterYSize < 2)
    1221             :     {
    1222           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1223             :                  "Heightfield raster dimensions too small.");
    1224           0 :         return false;
    1225             :     }
    1226             : 
    1227             :     // Record start of pixel data
    1228             :     size_t datalen;
    1229           2 :     if (!locate_data(m_nDataOffset, datalen, file, "hf_data"))
    1230             :     {
    1231           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Cannot locate elevation data.");
    1232           0 :         return false;
    1233             :     }
    1234             : 
    1235             :     // Sanity check: do we have enough pixels?
    1236           2 :     if (static_cast<GUIntBig>(datalen) !=
    1237           2 :         static_cast<GUIntBig>(nRasterXSize) *
    1238           2 :             static_cast<GUIntBig>(nRasterYSize) * sizeof(float))
    1239             :     {
    1240           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1241             :                  "File does not have enough data.");
    1242           0 :         return false;
    1243             :     }
    1244             : 
    1245             :     // Defaults for raster coordsys.
    1246           2 :     m_gt = GDALGeoTransform();
    1247             : 
    1248           2 :     m_dElevScale = 1.0;
    1249           2 :     m_dElevBase = 0.0;
    1250           2 :     strcpy(m_szElevUnits, "");
    1251             : 
    1252           2 :     if (m_version >= 7)
    1253             :     {
    1254             :         // Read coordsys info.
    1255           0 :         int csclass = LEV_COORDSYS_RASTER;
    1256           0 :         /* (void) */ get(csclass, file, "csclass");
    1257             : 
    1258           0 :         if (csclass != LEV_COORDSYS_RASTER)
    1259             :         {
    1260             :             // Get projection details and units.
    1261           0 :             if (csclass == LEV_COORDSYS_LOCAL)
    1262             :             {
    1263             :                 UNITLABEL unitcode;
    1264             :                 // char szLocalUnits[8];
    1265             :                 int unitcode_int;
    1266           0 :                 if (!get(unitcode_int, file, "coordsys_units"))
    1267           0 :                     unitcode_int = UNITLABEL_M;
    1268           0 :                 unitcode = static_cast<UNITLABEL>(unitcode_int);
    1269             : 
    1270           0 :                 if (!make_local_coordsys("Leveller", unitcode))
    1271             :                 {
    1272           0 :                     CPLError(CE_Failure, CPLE_OpenFailed,
    1273             :                              "Cannot define local coordinate system.");
    1274           0 :                     return false;
    1275             :                 }
    1276             :             }
    1277           0 :             else if (csclass == LEV_COORDSYS_GEO)
    1278             :             {
    1279             :                 char szWKT[1024];
    1280           0 :                 if (!get(szWKT, 1023, file, "coordsys_wkt"))
    1281           0 :                     return false;
    1282             : 
    1283           0 :                 m_oSRS.importFromWkt(szWKT);
    1284             :             }
    1285             :             else
    1286             :             {
    1287           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
    1288             :                          "Unknown coordinate system type in %s.", pszFilename);
    1289           0 :                 return false;
    1290             :             }
    1291             : 
    1292             :             // Get ground extents.
    1293           0 :             digital_axis axis_ns, axis_ew;
    1294             : 
    1295           0 :             if (axis_ns.get(*this, file, 0) && axis_ew.get(*this, file, 1))
    1296             :             {
    1297           0 :                 m_gt[0] = axis_ew.origin(nRasterXSize);
    1298           0 :                 m_gt[1] = axis_ew.scaling(nRasterXSize);
    1299           0 :                 m_gt[2] = 0.0;
    1300             : 
    1301           0 :                 m_gt[3] = axis_ns.origin(nRasterYSize);
    1302           0 :                 m_gt[4] = 0.0;
    1303           0 :                 m_gt[5] = axis_ns.scaling(nRasterYSize);
    1304             :             }
    1305             :         }
    1306             : 
    1307             :         // Get vertical (elev) coordsys.
    1308           0 :         int bHasVertCS = FALSE;
    1309           0 :         if (get(bHasVertCS, file, "coordsys_haselevm") && bHasVertCS)
    1310             :         {
    1311           0 :             get(m_dElevScale, file, "coordsys_em_scale");
    1312           0 :             get(m_dElevBase, file, "coordsys_em_base");
    1313             :             UNITLABEL unitcode;
    1314             :             int unitcode_int;
    1315           0 :             if (get(unitcode_int, file, "coordsys_em_units"))
    1316             :             {
    1317           0 :                 unitcode = static_cast<UNITLABEL>(unitcode_int);
    1318           0 :                 const char *pszUnitID = code_to_id(unitcode);
    1319           0 :                 if (pszUnitID != nullptr)
    1320             :                 {
    1321           0 :                     strncpy(m_szElevUnits, pszUnitID, sizeof(m_szElevUnits));
    1322           0 :                     m_szElevUnits[sizeof(m_szElevUnits) - 1] = '\0';
    1323             :                 }
    1324             :                 else
    1325             :                 {
    1326           0 :                     CPLError(CE_Failure, CPLE_OpenFailed,
    1327             :                              "Unknown OEM elevation unit of measure (%d)",
    1328             :                              unitcode);
    1329           0 :                     return false;
    1330             :                 }
    1331             :             }
    1332             :             // datum and localcs are currently unused.
    1333             :         }
    1334             :     }
    1335             :     else
    1336             :     {
    1337             :         // Legacy files use world units.
    1338             :         char szWorldUnits[32];
    1339           2 :         strcpy(szWorldUnits, "m");
    1340             : 
    1341           2 :         double dWorldscale = 1.0;
    1342             : 
    1343           2 :         if (get(dWorldscale, file, "hf_worldspacing"))
    1344             :         {
    1345             :             // m_bHasWorldscale = true;
    1346           2 :             if (get(szWorldUnits, sizeof(szWorldUnits) - 1, file,
    1347             :                     "hf_worldspacinglabel"))
    1348             :             {
    1349             :                 // Drop long name, if present.
    1350           2 :                 char *p = strchr(szWorldUnits, ' ');
    1351           2 :                 if (p != nullptr)
    1352           2 :                     *p = 0;
    1353             :             }
    1354             : 
    1355             : #if 0
    1356             :           // If the units are something besides m/ft/sft,
    1357             :           // then convert them to meters.
    1358             : 
    1359             :           if(!str_equal("m", szWorldUnits)
    1360             :              && !str_equal("ft", szWorldUnits)
    1361             :              && !str_equal("sft", szWorldUnits))
    1362             :           {
    1363             :               dWorldscale = this->convert_measure(dWorldscale, szWorldUnits);
    1364             :               strcpy(szWorldUnits, "m");
    1365             :           }
    1366             : #endif
    1367             : 
    1368             :             // Our extents are such that the origin is at the
    1369             :             // center of the heightfield.
    1370           2 :             m_gt[0] = -0.5 * dWorldscale * (nRasterXSize - 1);
    1371           2 :             m_gt[3] = -0.5 * dWorldscale * (nRasterYSize - 1);
    1372           2 :             m_gt[1] = dWorldscale;
    1373           2 :             m_gt[5] = dWorldscale;
    1374             :         }
    1375           2 :         m_dElevScale = dWorldscale;  // this was 1.0 before because
    1376             :         // we were converting to real elevs ourselves, but
    1377             :         // some callers may want both the raw pixels and the
    1378             :         // transform to get real elevs.
    1379             : 
    1380           2 :         if (!make_local_coordsys("Leveller world space", szWorldUnits))
    1381             :         {
    1382           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1383             :                      "Cannot define local coordinate system.");
    1384           0 :             return false;
    1385             :         }
    1386             :     }
    1387             : 
    1388           2 :     return true;
    1389             : }
    1390             : 
    1391             : /************************************************************************/
    1392             : /*                          GetSpatialRef()                             */
    1393             : /************************************************************************/
    1394             : 
    1395           0 : const OGRSpatialReference *LevellerDataset::GetSpatialRef() const
    1396             : {
    1397           0 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
    1398             : }
    1399             : 
    1400             : /************************************************************************/
    1401             : /*                          GetGeoTransform()                           */
    1402             : /************************************************************************/
    1403             : 
    1404           0 : CPLErr LevellerDataset::GetGeoTransform(GDALGeoTransform &gt) const
    1405             : 
    1406             : {
    1407           0 :     gt = m_gt;
    1408           0 :     return CE_None;
    1409             : }
    1410             : 
    1411             : /************************************************************************/
    1412             : /*                              Identify()                              */
    1413             : /************************************************************************/
    1414             : 
    1415       60860 : int LevellerDataset::Identify(GDALOpenInfo *poOpenInfo)
    1416             : {
    1417       60860 :     if (poOpenInfo->nHeaderBytes < 4)
    1418       54675 :         return FALSE;
    1419             : 
    1420        6185 :     return STARTS_WITH_CI(
    1421             :         reinterpret_cast<const char *>(poOpenInfo->pabyHeader), "trrn");
    1422             : }
    1423             : 
    1424             : /************************************************************************/
    1425             : /*                                Open()                                */
    1426             : /************************************************************************/
    1427             : 
    1428           2 : GDALDataset *LevellerDataset::Open(GDALOpenInfo *poOpenInfo)
    1429             : {
    1430             :     // The file should have at least 5 header bytes
    1431             :     // and hf_w, hf_b, and hf_data tags.
    1432             : 
    1433           2 :     if (poOpenInfo->nHeaderBytes < 5 + 13 + 13 + 16 ||
    1434           2 :         poOpenInfo->fpL == nullptr)
    1435           0 :         return nullptr;
    1436             : 
    1437           2 :     if (!LevellerDataset::Identify(poOpenInfo))
    1438           0 :         return nullptr;
    1439             : 
    1440           2 :     const int version = poOpenInfo->pabyHeader[4];
    1441           2 :     if (version < 4 || version > 12)
    1442           0 :         return nullptr;
    1443             : 
    1444             :     /* -------------------------------------------------------------------- */
    1445             :     /*      Create a corresponding GDALDataset.                             */
    1446             :     /* -------------------------------------------------------------------- */
    1447             : 
    1448           2 :     LevellerDataset *poDS = new LevellerDataset();
    1449             : 
    1450           2 :     poDS->m_version = version;
    1451             : 
    1452           2 :     poDS->m_fp = poOpenInfo->fpL;
    1453           2 :     poOpenInfo->fpL = nullptr;
    1454           2 :     poDS->eAccess = poOpenInfo->eAccess;
    1455             : 
    1456             :     /* -------------------------------------------------------------------- */
    1457             :     /*      Read the file.                                                  */
    1458             :     /* -------------------------------------------------------------------- */
    1459           2 :     if (!poDS->load_from_file(poDS->m_fp, poOpenInfo->pszFilename))
    1460             :     {
    1461           0 :         delete poDS;
    1462           0 :         return nullptr;
    1463             :     }
    1464             : 
    1465             :     /* -------------------------------------------------------------------- */
    1466             :     /*      Create band information objects.                                */
    1467             :     /* -------------------------------------------------------------------- */
    1468           2 :     LevellerRasterBand *poBand = new LevellerRasterBand(poDS);
    1469           2 :     poDS->SetBand(1, poBand);
    1470           2 :     if (!poBand->Init())
    1471             :     {
    1472           0 :         delete poDS;
    1473           0 :         return nullptr;
    1474             :     }
    1475             : 
    1476           2 :     poDS->SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
    1477             : 
    1478             :     /* -------------------------------------------------------------------- */
    1479             :     /*      Initialize any PAM information.                                 */
    1480             :     /* -------------------------------------------------------------------- */
    1481           2 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1482           2 :     poDS->TryLoadXML();
    1483             : 
    1484             :     /* -------------------------------------------------------------------- */
    1485             :     /*      Check for external overviews.                                   */
    1486             :     /* -------------------------------------------------------------------- */
    1487           4 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
    1488           2 :                                 poOpenInfo->GetSiblingFiles());
    1489             : 
    1490           2 :     return poDS;
    1491             : }
    1492             : 
    1493             : /************************************************************************/
    1494             : /*                        GDALRegister_Leveller()                       */
    1495             : /************************************************************************/
    1496             : 
    1497        1911 : void GDALRegister_Leveller()
    1498             : 
    1499             : {
    1500        1911 :     if (GDALGetDriverByName("Leveller") != nullptr)
    1501         282 :         return;
    1502             : 
    1503        1629 :     GDALDriver *poDriver = new GDALDriver();
    1504             : 
    1505        1629 :     poDriver->SetDescription("Leveller");
    1506        1629 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1507        1629 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "ter");
    1508        1629 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Leveller heightfield");
    1509        1629 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
    1510        1629 :                               "drivers/raster/leveller.html");
    1511        1629 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1512             : 
    1513        1629 :     poDriver->pfnIdentify = LevellerDataset::Identify;
    1514        1629 :     poDriver->pfnOpen = LevellerDataset::Open;
    1515        1629 :     poDriver->pfnCreate = LevellerDataset::Create;
    1516             : 
    1517        1629 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1518             : }

Generated by: LCOV version 1.14