/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 §ion, 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 */ |