Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/miramon/miramon_rel.h
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  MiraMonRaster driver
4
 * Purpose:  Implements MMRRel: provides access to the REL file, which
5
 *           holds all the necessary metadata to correctly interpret and
6
 *           access the associated raw data.
7
 * Author:   Abel Pau
8
 *
9
 ******************************************************************************
10
 * Copyright (c) 2025, Xavier Pons
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14
15
#ifndef MMR_REL_H_INCLUDED
16
#define MMR_REL_H_INCLUDED
17
18
#include "cpl_string.h"
19
#include "gdal_priv.h"
20
#include "miramon_band.h"  // For MMRBand
21
22
#include "../miramon_common/mm_gdal_driver_structs.h"  // For SECTION_VERSIO
23
24
constexpr auto pszExtRaster = ".img";
25
constexpr auto pszExtRasterREL = "I.rel";
26
constexpr auto pszExtREL = ".rel";
27
constexpr auto LineReturn = "\r\n";
28
29
class MMRBand;
30
31
/************************************************************************/
32
/*                                MMRRel                                */
33
/************************************************************************/
34
35
enum class MMRNomFitxerState
36
{
37
    NOMFITXER_NOT_FOUND,        // There is no NomFitxer key
38
    NOMFITXER_VALUE_EXPECTED,   // The NomFitxer value is the expected
39
    NOMFITXER_VALUE_EMPTY,      // The NomFitxer value is empty
40
    NOMFITXER_VALUE_UNEXPECTED  // The NomFitxer value is unexpected
41
};
42
43
using ExcludedEntry = std::pair<CPLString, CPLString>;
44
45
class MMRRel
46
{
47
  public:
48
    MMRRel(const CPLString &, bool);  // Used in reading
49
    MMRRel(const CPLString &, bool bNeedOfNomFitxer, const CPLString &osEPSG,
50
           int nWidth, int nHeight, double dfMinX, double dfMaxX, double dfMinY,
51
           double dfMaxY,
52
           std::vector<MMRBand> &&oBands);  // Used in writing
53
    explicit MMRRel(const CPLString &);     // Used in writing. Simple version
54
    MMRRel(const MMRRel &) =
55
        delete;  // I don't want to construct a MMRDataset from another MMRDataset (effc++)
56
    MMRRel &operator=(const MMRRel &) =
57
        delete;  // I don't want to assign a MMRDataset to another MMRDataset (effc++)
58
    ~MMRRel();
59
60
    static CPLString
61
    GetValueFromSectionKeyPriorToREL(const CPLString &osPriorRelName,
62
                                     const CPLString &osSection,
63
                                     const CPLString &osKey);
64
    CPLString GetValueFromSectionKeyFromREL(const CPLString &osSection,
65
                                            const CPLString &osKey);
66
    static CPLString GetValueFromSectionKey(VSILFILE *pf,
67
                                            const CPLString &osSection,
68
                                            const CPLString &osKey);
69
    bool GetMetadataValue(const CPLString &osMainSection,
70
                          const CPLString &osSubSection,
71
                          const CPLString &osSubSubSection,
72
                          const CPLString &osKey, CPLString &osValue);
73
    bool GetMetadataValue(const CPLString &osMainSection,
74
                          const CPLString &osSubSection, const CPLString &osKey,
75
                          CPLString &osValue);
76
    bool GetMetadataValue(const CPLString &osSection, const CPLString &osKey,
77
                          CPLString &osValue);
78
    bool GetAndExcludeMetadataValueDirectly(const CPLString &osRELFile,
79
                                            const CPLString &osSection,
80
                                            const CPLString &osKey,
81
                                            CPLString &osValue);
82
    static bool GetMetadataValueDirectly(const CPLString &osRELFile,
83
                                         const CPLString &osSection,
84
                                         const CPLString &osKey,
85
                                         CPLString &osValue);
86
    void RELToGDALMetadata(GDALDataset *poDS);
87
88
    static CPLString MMRGetFileNameWithOutI(const CPLString &osRELFile);
89
    static CPLString MMRGetFileNameFromRelName(const CPLString &osRELFile,
90
                                               const CPLString &osExtension);
91
    int GetColumnsNumberFromREL();
92
    int GetRowsNumberFromREL();
93
    static int IdentifySubdataSetFile(const CPLString &osFileName);
94
    static int IdentifyFile(const GDALOpenInfo *poOpenInfo);
95
    CPLString GetColor_TractamentVariable(int nIBand) const;
96
    CPLString GetColor_Paleta(int nIBand) const;
97
    CPLErr UpdateGDALColorEntryFromBand(const CPLString &m_osBandSection,
98
                                        GDALColorEntry &m_sConstantColorRGB);
99
100
    void UpdateLineage(CSLConstList papszOptions, GDALDataset &oSrcDS);
101
    bool Write(GDALDataset &oSrcDS);
102
    void WriteMETADADES();
103
    void WriteIDENTIFICATION();
104
    void WriteOVERVIEW_ASPECTES_TECNICS(GDALDataset &oSrcDS);
105
    void WriteMetadataInComments(GDALDataset &oSrcDS);
106
    void WriteSPATIAL_REFERENCE_SYSTEM_HORIZONTAL();
107
    void WriteEXTENT();
108
    void WriteOVERVIEW();
109
    bool WriteATTRIBUTE_DATA(GDALDataset &oSrcDS);
110
    void WriteBandSection(const MMRBand &osBand, const CPLString &osDSDataType);
111
    void WriteCOLOR_TEXTSection();
112
    void WriteVISU_LLEGENDASection();
113
    void WriteLINEAGE(GDALDataset &oSrcDS);
114
    void WriteCurrentProcess();
115
    void WriteINOUTSection(const CPLString &osSection, int nInOut,
116
                           const CPLString &osIdentifierValue,
117
                           const CPLString &osSentitValue,
118
                           const CPLString &osTypeValuesValue,
119
                           const CPLString &osResultValueValue);
120
    void ImportAndWriteLineageSection(GDALDataset &oSrcDS);
121
    bool ProcessProcessSection(const CPLStringList &aosMiraMonSortedMetaData,
122
                               const CPLString &osProcessSection);
123
    void EndProcessesSection();
124
125
    bool IsValid() const
126
1.67k
    {
127
1.67k
        return m_bIsValid;
128
1.67k
    }
129
130
    void SetIsValid(bool bIsValidIn)
131
0
    {
132
0
        m_bIsValid = bIsValidIn;
133
0
    }
134
135
    VSILFILE *GetRELFile() const
136
229k
    {
137
229k
        return m_pRELFile;
138
229k
    }
139
140
    bool OpenRELFile(const char *pszAccess)
141
1.58k
    {
142
1.58k
        if (m_osRelFileName.empty())
143
0
            return false;
144
145
1.58k
        m_pRELFile = VSIFOpenL(m_osRelFileName, pszAccess);
146
1.58k
        if (m_pRELFile)
147
1.58k
            return true;
148
0
        return false;
149
1.58k
    }
150
151
    bool CreateRELFile()
152
0
    {
153
0
        if (m_osRelFileName.empty())
154
0
            return false;
155
156
0
        m_pRELFile = VSIFOpenL(m_osRelFileName, "wb");
157
0
        if (m_pRELFile)
158
0
            return true;
159
160
0
        CPLError(CE_Failure, CPLE_FileIO, "Failed to create output file: %s",
161
0
                 m_osRelFileName.c_str());
162
163
0
        return false;
164
0
    }
165
166
    void AddRELVersion()
167
0
    {
168
0
        if (!m_pRELFile)
169
0
            return;
170
171
        // Writing MiraMon version section
172
0
        AddSectionStart(SECTION_VERSIO);
173
0
        AddKeyValue(KEY_VersMetaDades,
174
0
                    static_cast<unsigned>(MM_VERS_METADADES));
175
0
        AddKeyValue(KEY_SubVersMetaDades,
176
0
                    static_cast<unsigned>(MM_SUBVERS_METADADES));
177
0
        AddKeyValue(KEY_Vers, static_cast<unsigned>(MM_VERS));
178
0
        AddKeyValue(KEY_SubVers, static_cast<unsigned>(MM_SUBVERS));
179
0
        AddSectionEnd();
180
0
    }
181
182
    void AddCOLOR_TEXTVersion()
183
0
    {
184
0
        if (!m_pRELFile)
185
0
            return;
186
187
        // Writing COLOR_TEXT version keys
188
0
        AddKeyValue("Simb_Vers", 5);
189
0
        AddKeyValue("Simb_SubVers", 1);
190
0
    }
191
192
    void AddVISU_LLEGENDAVersion()
193
0
    {
194
0
        if (!m_pRELFile)
195
0
            return;
196
197
        // Writing VISU_LLEGENDA version keys
198
0
        AddKeyValue("LlegSimb_Vers", 4);
199
0
        AddKeyValue("LlegSimb_SubVers", 5);
200
0
    }
201
202
    void AddSectionStart(const CPLString &osSection)
203
0
    {
204
0
        if (!m_pRELFile)
205
0
            return;
206
207
0
        VSIFPrintfL(m_pRELFile, "[%s]%s", osSection.c_str(), LineReturn);
208
0
    }
209
210
    void AddSectionStart(const CPLString &osSectionP1,
211
                         const CPLString &osSectionP2)
212
0
    {
213
0
        if (!m_pRELFile)
214
0
            return;
215
216
0
        VSIFPrintfL(m_pRELFile, "[%s:%s]%s", osSectionP1.c_str(),
217
0
                    osSectionP2.c_str(), LineReturn);
218
0
    }
219
220
    void AddSectionEnd()
221
0
    {
222
0
        if (!m_pRELFile)
223
0
            return;
224
225
0
        VSIFPrintfL(m_pRELFile, LineReturn);
226
0
    }
227
228
    void AddKey(const CPLString &osKey)
229
0
    {
230
0
        if (!m_pRELFile)
231
0
            return;
232
233
0
        char *pzsKey = CPLRecode(osKey, CPL_ENC_UTF8, "CP1252");
234
0
        VSIFPrintfL(m_pRELFile, "%s=%s", pzsKey ? pzsKey : osKey.c_str(),
235
0
                    LineReturn);
236
0
        CPLFree(pzsKey);
237
0
    }
238
239
    void AddKeyValue(const CPLString &osKey, const CPLString &osValue)
240
0
    {
241
0
        if (!m_pRELFile)
242
0
            return;
243
244
0
        char *pzsKey = CPLRecode(osKey, CPL_ENC_UTF8, "CP1252");
245
246
0
        CPLString osValidValue = osValue;
247
0
        size_t nPos = osValue.find(MMEmptyValue);
248
0
        if (nPos != std::string::npos)
249
0
            osValidValue.replaceAll(MMEmptyValue, "");
250
251
0
        if (osValidValue.empty())
252
0
        {
253
0
            VSIFPrintfL(m_pRELFile, "%s=%s", pzsKey ? pzsKey : osKey.c_str(),
254
0
                        LineReturn);
255
0
        }
256
0
        else
257
0
        {
258
0
            char *pzsValue = CPLRecode(osValidValue, CPL_ENC_UTF8, "CP1252");
259
0
            VSIFPrintfL(m_pRELFile, "%s=%s%s", pzsKey ? pzsKey : osKey.c_str(),
260
0
                        pzsValue ? pzsValue : osValidValue.c_str(), LineReturn);
261
0
            CPLFree(pzsValue);
262
0
        }
263
0
        CPLFree(pzsKey);
264
0
    }
265
266
    void AddKeyValue(const CPLString &osKey, int nValue)
267
0
    {
268
0
        if (!m_pRELFile)
269
0
            return;
270
271
0
        char *pzsKey = CPLRecode(osKey, CPL_ENC_UTF8, "CP1252");
272
0
        VSIFPrintfL(m_pRELFile, "%s=%d%s", pzsKey ? pzsKey : osKey.c_str(),
273
0
                    nValue, LineReturn);
274
275
0
        CPLFree(pzsKey);
276
0
    }
277
278
    void AddKeyValue(const CPLString &osKey, unsigned nValue)
279
0
    {
280
0
        if (!m_pRELFile)
281
0
            return;
282
283
0
        char *pzsKey = CPLRecode(osKey, CPL_ENC_UTF8, "CP1252");
284
0
        VSIFPrintfL(m_pRELFile, "%s=%u%s", pzsKey ? pzsKey : osKey.c_str(),
285
0
                    nValue, LineReturn);
286
287
0
        CPLFree(pzsKey);
288
0
    }
289
290
    void AddKeyValue(const CPLString &osKey, double dfValue)
291
0
    {
292
0
        if (!m_pRELFile)
293
0
            return;
294
295
0
        char *pzsKey = CPLRecode(osKey, CPL_ENC_UTF8, "CP1252");
296
0
        CPLString osValue = CPLSPrintf("%.15g", dfValue);
297
0
        VSIFPrintfL(m_pRELFile, "%s=%s%s", pzsKey ? pzsKey : osKey.c_str(),
298
0
                    osValue.c_str(), LineReturn);
299
300
0
        CPLFree(pzsKey);
301
0
    }
302
303
    void CloseRELFile()
304
1.67k
    {
305
1.67k
        if (!m_pRELFile)
306
90
            return;
307
308
1.58k
        VSIFCloseL(m_pRELFile);
309
1.58k
        m_pRELFile = nullptr;
310
1.58k
    }
311
312
    const char *GetRELNameChar() const
313
1.55k
    {
314
1.55k
        return m_osRelFileName.c_str();
315
1.55k
    }
316
317
    const CPLString &GetRELName() const
318
947
    {
319
947
        return m_osRelFileName;
320
947
    }
321
322
    int GetNBands() const
323
46.0k
    {
324
46.0k
        return m_nBands;
325
46.0k
    }
326
327
    MMRBand *GetBand(int nIBand)
328
43.6k
    {
329
43.6k
        if (nIBand < 0 || nIBand >= m_nBands)
330
0
            return nullptr;
331
332
43.6k
        return &m_oBands[nIBand];
333
43.6k
    }
334
335
    const CPLString &GetPattern() const
336
0
    {
337
0
        return osPattern;
338
0
    }
339
340
    int isAMiraMonFile() const
341
580
    {
342
580
        return m_bIsAMiraMonFile;
343
580
    }
344
345
    void addExcludedSectionKey(const CPLString &section, const CPLString &key)
346
114k
    {
347
114k
        m_ExcludedSectionKey.emplace(section, key);
348
114k
    }
349
350
    std::set<ExcludedEntry> GetExcludedMetadata() const
351
192k
    {
352
192k
        return m_ExcludedSectionKey;
353
192k
    }
354
355
  private:
356
    static CPLErr CheckBandInRel(const CPLString &osRELFileName,
357
                                 const CPLString &osIMGFile);
358
    static CPLString MMRGetSimpleMetadataName(const CPLString &osLayerName);
359
    static bool SameFile(const CPLString &osFile1, const CPLString &osFile2);
360
    MMRNomFitxerState MMRStateOfNomFitxerInSection(const CPLString &osLayerName,
361
                                                   const CPLString &osSection,
362
                                                   const CPLString &osRELFile,
363
                                                   bool bNomFitxerMustExist);
364
    CPLString MMRGetAReferenceToIMGFile(const CPLString &osLayerName,
365
                                        const CPLString &osRELFile);
366
367
    CPLString GetAssociatedMetadataFileName(const CPLString &osFileName);
368
369
    void UpdateRELNameChar(const CPLString &osRelFileNameIn);
370
    CPLErr ParseBandInfo();
371
372
    CPLString m_osRelFileName = "";
373
    CPLString m_osTitle = "";
374
    VSILFILE *m_pRELFile = nullptr;
375
    static CPLString m_szImprobableRELChain;
376
377
    char m_szFileIdentifier[MM_MAX_LEN_LAYER_IDENTIFIER];
378
379
    bool m_bIsValid =
380
        false;  // Determines if the created object is valid or not.
381
    bool m_bIsAMiraMonFile = false;
382
383
    // List of rawBandNames in a subdataset
384
    std::vector<CPLString> m_papoSDSBands{};
385
386
    int m_nBands = 0;
387
    std::vector<MMRBand> m_oBands{};
388
389
    CPLString osPattern = "";  // A pattern used to create all band names
390
391
    // If there is only one band and the name of the rel is the same than the
392
    // name of the band (ex: band.img and bandI.rel) then NomFitxer
393
    // is not necessary to be written in the REL
394
    bool m_bNeedOfNomFitxer = true;
395
396
    // If a key-value pair is the same for all bands, it must be written
397
    // in a single general section. Otherwise, the most common key-value
398
    // (or any chosen one) can be written in the general section, and
399
    // all bands with a different value must write it in their
400
    // corresponding specific section.
401
    bool m_bDimAlreadyWritten = false;
402
    CPLString m_osDefTractVariable = "";
403
    CPLString m_osDefUnits = "";
404
405
    // Preserving metadata
406
407
    // List of excluded pairs {Section, Key} to be added to metadata
408
    // Empty Key means all section
409
    std::set<ExcludedEntry> m_ExcludedSectionKey = {};
410
411
    // For writing part
412
    // EPSG number
413
    CPLString m_osEPSG = "";
414
415
    // Global raster dimensions
416
    int m_nWidth = 0;
417
    int m_nHeight = 0;
418
419
    double m_dfMinX = MM_UNDEFINED_STATISTICAL_VALUE;
420
    double m_dfMaxX = -MM_UNDEFINED_STATISTICAL_VALUE;
421
    double m_dfMinY = MM_UNDEFINED_STATISTICAL_VALUE;
422
    double m_dfMaxY = -MM_UNDEFINED_STATISTICAL_VALUE;
423
424
    // Lineage
425
    CPLString m_osInFile = "";
426
    CPLString m_osOutFile = "";
427
    CPLStringList m_aosOptions{};
428
429
    // Number of processes in the lineage.
430
    // It is incremented each time a process is added
431
    // to the lineage, and it is used to set the "processes"
432
    // key in the QUALITY:LINEAGE section.
433
    int m_nNProcesses = 0;
434
    // It's possible to have a list of process like that:
435
    // processes=1,5,8 (nILastProcess=8)
436
    // So, current process would be 9 after the nILastProcess.
437
    int nILastProcess = 0;
438
439
    // List of processes
440
    CPLString m_osListOfProcesses = "";
441
};
442
443
#endif /* ndef MMR_REL_H_INCLUDED */