Line data Source code
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 492 : bool IsValid() const
126 : {
127 492 : return m_bIsValid;
128 : }
129 :
130 3 : void SetIsValid(bool bIsValidIn)
131 : {
132 3 : m_bIsValid = bIsValidIn;
133 3 : }
134 :
135 25858 : VSILFILE *GetRELFile() const
136 : {
137 25858 : return m_pRELFile;
138 : }
139 :
140 257 : bool OpenRELFile(const char *pszAccess)
141 : {
142 257 : if (m_osRelFileName.empty())
143 0 : return false;
144 :
145 257 : m_pRELFile = VSIFOpenL(m_osRelFileName, pszAccess);
146 257 : if (m_pRELFile)
147 257 : return true;
148 0 : return false;
149 : }
150 :
151 126 : bool CreateRELFile()
152 : {
153 126 : if (m_osRelFileName.empty())
154 0 : return false;
155 :
156 126 : m_pRELFile = VSIFOpenL(m_osRelFileName, "wb");
157 126 : if (m_pRELFile)
158 123 : return true;
159 :
160 3 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create output file: %s",
161 : m_osRelFileName.c_str());
162 :
163 3 : return false;
164 : }
165 :
166 123 : void AddRELVersion()
167 : {
168 123 : if (!m_pRELFile)
169 0 : return;
170 :
171 : // Writing MiraMon version section
172 123 : AddSectionStart(SECTION_VERSIO);
173 123 : AddKeyValue(KEY_VersMetaDades,
174 : static_cast<unsigned>(MM_VERS_METADADES));
175 123 : AddKeyValue(KEY_SubVersMetaDades,
176 : static_cast<unsigned>(MM_SUBVERS_METADADES));
177 123 : AddKeyValue(KEY_Vers, static_cast<unsigned>(MM_VERS));
178 123 : AddKeyValue(KEY_SubVers, static_cast<unsigned>(MM_SUBVERS));
179 123 : AddSectionEnd();
180 : }
181 :
182 123 : void AddCOLOR_TEXTVersion()
183 : {
184 123 : if (!m_pRELFile)
185 0 : return;
186 :
187 : // Writing COLOR_TEXT version keys
188 123 : AddKeyValue("Simb_Vers", 5);
189 123 : AddKeyValue("Simb_SubVers", 1);
190 : }
191 :
192 123 : void AddVISU_LLEGENDAVersion()
193 : {
194 123 : if (!m_pRELFile)
195 0 : return;
196 :
197 : // Writing VISU_LLEGENDA version keys
198 123 : AddKeyValue("LlegSimb_Vers", 4);
199 123 : AddKeyValue("LlegSimb_SubVers", 5);
200 : }
201 :
202 1869 : void AddSectionStart(const CPLString &osSection)
203 : {
204 1869 : if (!m_pRELFile)
205 0 : return;
206 :
207 1869 : VSIFPrintfL(m_pRELFile, "[%s]%s", osSection.c_str(), LineReturn);
208 : }
209 :
210 246 : void AddSectionStart(const CPLString &osSectionP1,
211 : const CPLString &osSectionP2)
212 : {
213 246 : if (!m_pRELFile)
214 0 : return;
215 :
216 246 : VSIFPrintfL(m_pRELFile, "[%s:%s]%s", osSectionP1.c_str(),
217 : osSectionP2.c_str(), LineReturn);
218 : }
219 :
220 2151 : void AddSectionEnd()
221 : {
222 2151 : if (!m_pRELFile)
223 0 : return;
224 :
225 2151 : VSIFPrintfL(m_pRELFile, LineReturn);
226 : }
227 :
228 123 : void AddKey(const CPLString &osKey)
229 : {
230 123 : if (!m_pRELFile)
231 0 : return;
232 :
233 123 : char *pzsKey = CPLRecode(osKey, CPL_ENC_UTF8, "CP1252");
234 123 : VSIFPrintfL(m_pRELFile, "%s=%s", pzsKey ? pzsKey : osKey.c_str(),
235 : LineReturn);
236 123 : CPLFree(pzsKey);
237 : }
238 :
239 4923 : void AddKeyValue(const CPLString &osKey, const CPLString &osValue)
240 : {
241 4923 : if (!m_pRELFile)
242 0 : return;
243 :
244 4923 : char *pzsKey = CPLRecode(osKey, CPL_ENC_UTF8, "CP1252");
245 :
246 9846 : CPLString osValidValue = osValue;
247 4923 : size_t nPos = osValue.find(MMEmptyValue);
248 4923 : if (nPos != std::string::npos)
249 34 : osValidValue.replaceAll(MMEmptyValue, "");
250 :
251 4923 : if (osValidValue.empty())
252 : {
253 523 : VSIFPrintfL(m_pRELFile, "%s=%s", pzsKey ? pzsKey : osKey.c_str(),
254 : LineReturn);
255 : }
256 : else
257 : {
258 4400 : char *pzsValue = CPLRecode(osValidValue, CPL_ENC_UTF8, "CP1252");
259 4400 : VSIFPrintfL(m_pRELFile, "%s=%s%s", pzsKey ? pzsKey : osKey.c_str(),
260 0 : pzsValue ? pzsValue : osValidValue.c_str(), LineReturn);
261 4400 : CPLFree(pzsValue);
262 : }
263 4923 : CPLFree(pzsKey);
264 : }
265 :
266 2706 : void AddKeyValue(const CPLString &osKey, int nValue)
267 : {
268 2706 : if (!m_pRELFile)
269 0 : return;
270 :
271 2706 : char *pzsKey = CPLRecode(osKey, CPL_ENC_UTF8, "CP1252");
272 2706 : VSIFPrintfL(m_pRELFile, "%s=%d%s", pzsKey ? pzsKey : osKey.c_str(),
273 : nValue, LineReturn);
274 :
275 2706 : CPLFree(pzsKey);
276 : }
277 :
278 492 : void AddKeyValue(const CPLString &osKey, unsigned nValue)
279 : {
280 492 : if (!m_pRELFile)
281 0 : return;
282 :
283 492 : char *pzsKey = CPLRecode(osKey, CPL_ENC_UTF8, "CP1252");
284 492 : VSIFPrintfL(m_pRELFile, "%s=%u%s", pzsKey ? pzsKey : osKey.c_str(),
285 : nValue, LineReturn);
286 :
287 492 : CPLFree(pzsKey);
288 : }
289 :
290 870 : void AddKeyValue(const CPLString &osKey, double dfValue)
291 : {
292 870 : if (!m_pRELFile)
293 0 : return;
294 :
295 870 : char *pzsKey = CPLRecode(osKey, CPL_ENC_UTF8, "CP1252");
296 1740 : CPLString osValue = CPLSPrintf("%.15g", dfValue);
297 870 : VSIFPrintfL(m_pRELFile, "%s=%s%s", pzsKey ? pzsKey : osKey.c_str(),
298 : osValue.c_str(), LineReturn);
299 :
300 870 : CPLFree(pzsKey);
301 : }
302 :
303 698 : void CloseRELFile()
304 : {
305 698 : if (!m_pRELFile)
306 318 : return;
307 :
308 380 : VSIFCloseL(m_pRELFile);
309 380 : m_pRELFile = nullptr;
310 : }
311 :
312 289 : const char *GetRELNameChar() const
313 : {
314 289 : return m_osRelFileName.c_str();
315 : }
316 :
317 161 : const CPLString &GetRELName() const
318 : {
319 161 : return m_osRelFileName;
320 : }
321 :
322 3255 : int GetNBands() const
323 : {
324 3255 : return m_nBands;
325 : }
326 :
327 2770 : MMRBand *GetBand(int nIBand)
328 : {
329 2770 : if (nIBand < 0 || nIBand >= m_nBands)
330 0 : return nullptr;
331 :
332 2770 : return &m_oBands[nIBand];
333 : }
334 :
335 : const CPLString &GetPattern() const
336 : {
337 : return osPattern;
338 : }
339 :
340 8155 : int isAMiraMonFile() const
341 : {
342 8155 : return m_bIsAMiraMonFile;
343 : }
344 :
345 12624 : void addExcludedSectionKey(const CPLString §ion, const CPLString &key)
346 : {
347 12624 : m_ExcludedSectionKey.emplace(section, key);
348 12624 : }
349 :
350 28916 : std::set<ExcludedEntry> GetExcludedMetadata() const
351 : {
352 28916 : return m_ExcludedSectionKey;
353 : }
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 */
|