Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: MiraMonRaster driver
4 : * Purpose: Implements MMRBand class: This class manages the metadata of each
5 : * band to be processed. It is useful for maintaining a list of bands
6 : * and for determining the number of subdatasets that need to be
7 : * generated.
8 : * Author: Abel Pau
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2025, Xavier Pons
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 : #include <algorithm>
16 : #include <limits>
17 : #include "gdal_rat.h"
18 :
19 : #include "miramon_rel.h"
20 : #include "miramon_band.h"
21 :
22 : #include "../miramon_common/mm_gdal_functions.h" // For MM_CreateDBFHeader
23 :
24 : /************************************************************************/
25 : /* MMRBand() */
26 : /************************************************************************/
27 257 : MMRBand::MMRBand(MMRRel &fRel, const CPLString &osBandSectionIn)
28 : : m_pfRel(&fRel), m_nWidth(0), m_nHeight(0),
29 257 : m_osBandSection(osBandSectionIn)
30 : {
31 : // Getting band and band file name from metadata.
32 257 : CPLString osNomFitxer;
33 257 : osNomFitxer = SECTION_ATTRIBUTE_DATA;
34 257 : osNomFitxer.append(":");
35 257 : osNomFitxer.append(osBandSectionIn);
36 257 : if (!m_pfRel->GetMetadataValue(osNomFitxer, KEY_NomFitxer,
37 397 : m_osRawBandFileName) ||
38 140 : m_osRawBandFileName.empty())
39 : {
40 : // A band name may be empty only if it is the only band present
41 : // in the REL file. Otherwise, inferring the band name from the
42 : // REL filename is considered an error.
43 : // Consequently, for a REL file containing exactly one band, if
44 : // the band name is empty, it shall be inferred from the REL
45 : // filename.
46 : // Example: REL: testI.rel --> IMG: test.img
47 120 : if (m_pfRel->GetNBands() >= 1)
48 0 : m_osBandFileName = "";
49 : else
50 : {
51 360 : m_osBandFileName = m_pfRel->MMRGetFileNameFromRelName(
52 240 : m_pfRel->GetRELName(), pszExtRaster);
53 : }
54 :
55 120 : if (m_osBandFileName.empty())
56 : {
57 0 : m_nWidth = 0;
58 0 : m_nHeight = 0;
59 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
60 : "The REL file '%s' contains a documented \
61 : band with no explicit or wrong name. Section [%s] or [%s:%s].",
62 0 : m_pfRel->GetRELNameChar(), SECTION_ATTRIBUTE_DATA,
63 : SECTION_ATTRIBUTE_DATA, m_osBandSection.c_str());
64 0 : return;
65 : }
66 120 : m_osBandName = CPLGetBasenameSafe(m_osBandFileName);
67 120 : m_osRawBandFileName = m_osBandName;
68 : }
69 : else
70 : {
71 137 : m_osBandName = CPLGetBasenameSafe(m_osRawBandFileName);
72 137 : CPLString osAux = CPLGetPathSafe(m_pfRel->GetRELNameChar());
73 : m_osBandFileName =
74 137 : CPLFormFilenameSafe(osAux.c_str(), m_osRawBandFileName.c_str(), "");
75 :
76 : CPLString osExtension =
77 137 : CPLString(CPLGetExtensionSafe(m_osBandFileName).c_str());
78 137 : if (!EQUAL(osExtension, pszExtRaster + 1))
79 0 : return;
80 : }
81 :
82 : // There is a band file documented?
83 257 : if (m_osBandName.empty())
84 : {
85 0 : m_nWidth = 0;
86 0 : m_nHeight = 0;
87 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
88 : "The REL file '%s' contains a documented \
89 : band with no explicit name. Section [%s] or [%s:%s].",
90 0 : m_pfRel->GetRELNameChar(), SECTION_ATTRIBUTE_DATA,
91 : SECTION_ATTRIBUTE_DATA, m_osBandSection.c_str());
92 0 : return;
93 : }
94 :
95 : // Getting essential metadata documented at
96 : // https://www.miramon.cat/new_note/eng/notes/MiraMon_raster_file_format.pdf
97 :
98 : // Getting number of columns and rows
99 257 : if (!UpdateColumnsNumberFromREL(m_osBandSection))
100 : {
101 1 : m_nWidth = 0;
102 1 : m_nHeight = 0;
103 1 : return;
104 : }
105 :
106 256 : if (!UpdateRowsNumberFromREL(m_osBandSection))
107 : {
108 1 : m_nWidth = 0;
109 1 : m_nHeight = 0;
110 1 : return;
111 : }
112 :
113 255 : if (m_nWidth <= 0 || m_nHeight <= 0)
114 : {
115 1 : m_nWidth = 0;
116 1 : m_nHeight = 0;
117 1 : CPLError(CE_Failure, CPLE_AppDefined,
118 : "MMRBand::MMRBand : (nWidth <= 0 || nHeight <= 0)");
119 1 : return;
120 : }
121 :
122 : // Getting data type and compression.
123 : // If error, message given inside.
124 254 : if (!UpdateDataTypeFromREL(m_osBandSection))
125 2 : return;
126 :
127 : // Let's see if there is RLE compression
128 252 : m_bIsCompressed =
129 377 : (((m_eMMDataType >= MMDataType::DATATYPE_AND_COMPR_BYTE_RLE) &&
130 379 : (m_eMMDataType <= MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE)) ||
131 127 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT);
132 :
133 : // Getting min and max values
134 252 : UpdateMinMaxValuesFromREL(m_osBandSection);
135 :
136 : // Getting unit type
137 252 : UpdateUnitTypeValueFromREL(m_osBandSection);
138 :
139 : // Getting min and max values for simbolization
140 252 : UpdateMinMaxVisuValuesFromREL(m_osBandSection);
141 252 : if (!m_bMinVisuSet)
142 : {
143 240 : if (m_bMinSet)
144 : {
145 237 : m_dfVisuMin = m_dfMin;
146 237 : m_bMinVisuSet = true;
147 : }
148 : }
149 252 : if (!m_bMaxVisuSet)
150 : {
151 240 : if (m_bMaxSet)
152 : {
153 237 : m_dfVisuMax = m_dfMax;
154 237 : m_bMaxVisuSet = true;
155 : }
156 : }
157 :
158 : // Getting the friendly description of the band
159 252 : UpdateFriendlyDescriptionFromREL(m_osBandSection);
160 :
161 : // Getting NoData value and definition
162 252 : UpdateNoDataValue(m_osBandSection);
163 :
164 : // Getting reference system and coordinates of the geographic bounding box
165 252 : UpdateReferenceSystemFromREL();
166 :
167 : // Getting the bounding box: coordinates in the terrain
168 252 : UpdateBoundingBoxFromREL(m_osBandSection);
169 :
170 : // Getting all information about simbolization
171 252 : UpdateSimbolizationInfo(m_osBandSection);
172 :
173 : // Getting all information about RAT
174 252 : UpdateRATInfo(m_osBandSection);
175 :
176 : // MiraMon IMG files are efficient in going to an specified row.
177 : // So le'ts configurate the blocks as line blocks.
178 252 : m_nBlockXSize = m_nWidth;
179 252 : m_nBlockYSize = 1;
180 252 : m_nNRowsPerBlock = 1;
181 :
182 : // Can the binary file that contains all data for this band be opened?
183 252 : m_pfIMG = VSIFOpenL(m_osBandFileName, "rb");
184 252 : if (!m_pfIMG)
185 : {
186 1 : m_nWidth = 0;
187 1 : m_nHeight = 0;
188 1 : CPLError(CE_Failure, CPLE_OpenFailed,
189 : "Failed to open MiraMon band file `%s' with access 'rb'.",
190 : m_osBandFileName.c_str());
191 1 : return;
192 : }
193 :
194 : // We have a valid MMRBand.
195 251 : m_bIsValid = true;
196 : }
197 :
198 167 : MMRBand::MMRBand(GDALProgressFunc pfnProgress, void *pProgressData,
199 : GDALDataset &oSrcDS, int nIBand, const CPLString &osDestPath,
200 : GDALRasterBand &papoBand, bool bCompress, bool bCategorical,
201 : const CPLString &osPattern, const CPLString &osBandSection,
202 167 : bool bNeedOfNomFitxer)
203 : : m_pfnProgress(pfnProgress), m_pProgressData(pProgressData),
204 : m_nIBand(nIBand), m_osBandSection(osBandSection),
205 167 : m_osFriendlyDescription(papoBand.GetDescription()),
206 167 : m_bIsCompressed(bCompress), m_bIsCategorical(bCategorical)
207 : {
208 : // Getting the binary filename
209 167 : if (bNeedOfNomFitxer)
210 101 : m_osBandName = osPattern + "_" + osBandSection;
211 : else
212 66 : m_osBandName = osPattern;
213 :
214 167 : m_osRawBandFileName = m_osBandName + pszExtRaster;
215 : m_osBandFileName =
216 167 : CPLFormFilenameSafe(osDestPath, m_osBandName, pszExtRaster);
217 :
218 : // Getting essential metadata documented at
219 : // https://www.miramon.cat/new_note/eng/notes/MiraMon_raster_file_format.pdf
220 :
221 : // Getting number of columns and rows
222 167 : m_nWidth = papoBand.GetXSize();
223 167 : m_nHeight = papoBand.GetYSize();
224 :
225 167 : if (m_nWidth <= 0 || m_nHeight <= 0)
226 : {
227 0 : m_nWidth = 0;
228 0 : m_nHeight = 0;
229 0 : CPLError(CE_Failure, CPLE_AppDefined,
230 : "MMRBand::MMRBand : (nWidth <= 0 || nHeight <= 0)");
231 0 : return;
232 : }
233 :
234 : // Getting units
235 167 : m_osBandUnitType = papoBand.GetUnitType();
236 :
237 : // Getting data type and compression from papoBand.
238 : // If error, message given inside.
239 167 : if (!UpdateDataTypeAndBytesPerPixelFromRasterBand(papoBand))
240 : {
241 5 : CPLError(CE_Failure, CPLE_AppDefined,
242 : "MMRBand::MMRBand : DataType not supported");
243 5 : return;
244 : }
245 162 : m_nDataTypeSizeBytes = std::max(1, static_cast<int>(m_eMMBytesPerPixel));
246 162 : m_bIsCompressed = bCompress;
247 :
248 : // Getting NoData value and definition
249 162 : UpdateNoDataValueFromRasterBand(papoBand);
250 :
251 162 : if (WriteColorTable(oSrcDS))
252 : {
253 0 : m_osCTName.clear();
254 0 : CPLError(CE_Warning, CPLE_AppDefined,
255 : "MMRBand::MMRBand : Existent color table but not imported"
256 : "due to some existent errors");
257 : }
258 :
259 162 : if (WriteAttributeTable(oSrcDS))
260 : {
261 0 : m_osRATDBFName.clear();
262 0 : m_osRATRELName.clear();
263 0 : CPLError(CE_Warning, CPLE_AppDefined,
264 : "MMRBand::MMRBand : Existent attribute table but not imported "
265 : "due to some existent errors");
266 : }
267 :
268 : // We have a valid MMRBand.
269 162 : m_bIsValid = true;
270 : }
271 :
272 : /************************************************************************/
273 : /* ~MMRBand() */
274 : /************************************************************************/
275 690 : MMRBand::~MMRBand()
276 : {
277 424 : if (m_pfIMG == nullptr)
278 14 : return;
279 :
280 410 : CPL_IGNORE_RET_VAL(VSIFCloseL(m_pfIMG));
281 410 : m_pfIMG = nullptr;
282 424 : }
283 :
284 40 : const CPLString &MMRBand::GetRELFileName() const
285 : {
286 40 : static const CPLString osEmpty;
287 40 : if (!m_pfRel)
288 0 : return osEmpty;
289 40 : return m_pfRel->GetRELName();
290 : }
291 :
292 : /************************************************************************/
293 : /* GetRasterBlock() */
294 : /************************************************************************/
295 385 : CPLErr MMRBand::GetRasterBlock(int /*nXBlock*/, int nYBlock, void *pData,
296 : int nDataSize)
297 :
298 : {
299 385 : if (nYBlock > INT_MAX / (std::max(1, m_nNRowsPerBlock)))
300 : {
301 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
302 0 : return CE_Failure;
303 : }
304 385 : const int iBlock = nYBlock * m_nNRowsPerBlock;
305 :
306 385 : if (m_nBlockXSize > INT_MAX / (std::max(1, m_nDataTypeSizeBytes)))
307 : {
308 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
309 0 : return CE_Failure;
310 : }
311 :
312 770 : if (m_nBlockYSize >
313 385 : INT_MAX / (std::max(1, m_nDataTypeSizeBytes * m_nBlockXSize)))
314 : {
315 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
316 0 : return CE_Failure;
317 : }
318 :
319 385 : const int nGDALBlockSize =
320 385 : m_nDataTypeSizeBytes * m_nBlockXSize * m_nBlockYSize;
321 :
322 : // Calculate block offset in case we have spill file. Use predefined
323 : // block map otherwise.
324 :
325 385 : if (nDataSize != -1 && nGDALBlockSize > nDataSize)
326 : {
327 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid block size: %d",
328 : nGDALBlockSize);
329 0 : return CE_Failure;
330 : }
331 :
332 : // Getting the row offsets to optimize access.
333 385 : if (FillRowOffsets() == false || m_aFileOffsets.empty())
334 : {
335 0 : CPLError(CE_Failure, CPLE_AppDefined,
336 : "Some error in offsets calculation");
337 0 : return CE_Failure;
338 : }
339 :
340 : // Read the block in the documented or deduced offset
341 385 : if (VSIFSeekL(m_pfIMG, m_aFileOffsets[iBlock], SEEK_SET))
342 : {
343 0 : CPLError(CE_Failure, CPLE_AppDefined,
344 : "Read from invalid offset for grid block.");
345 0 : return CE_Failure;
346 : }
347 :
348 : size_t nCompressedRawSize;
349 385 : if (iBlock == m_nHeight - 1)
350 171 : nCompressedRawSize = SIZE_MAX; // We don't know it
351 : else
352 428 : nCompressedRawSize = static_cast<size_t>(m_aFileOffsets[iBlock + 1] -
353 214 : m_aFileOffsets[iBlock]);
354 :
355 385 : return GetBlockData(pData, nCompressedRawSize);
356 : }
357 :
358 210 : void MMRBand::UpdateGeoTransform()
359 : {
360 210 : m_gt.xorig = GetBoundingBoxMinX();
361 210 : m_gt.xscale = (GetBoundingBoxMaxX() - m_gt.xorig) / GetWidth();
362 210 : m_gt.xrot = 0.0; // No rotation in MiraMon rasters
363 210 : m_gt.yorig = GetBoundingBoxMaxY();
364 210 : m_gt.yrot = 0.0;
365 210 : m_gt.yscale = (GetBoundingBoxMinY() - m_gt.yorig) / GetHeight();
366 210 : }
367 :
368 : /************************************************************************/
369 : /* Other functions */
370 : /************************************************************************/
371 :
372 : // [ATTRIBUTE_DATA:xxxx] or [OVERVIEW:ASPECTES_TECNICS]
373 513 : bool MMRBand::Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
374 : const CPLString &osSection, const char *pszKey, int *nValue,
375 : const char *pszErrorMessage)
376 : {
377 513 : if (osSection.empty() || !pszKey || !nValue)
378 0 : return false;
379 :
380 1026 : CPLString osValue;
381 513 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, pszKey,
382 515 : osValue) ||
383 2 : osValue.empty())
384 : {
385 511 : if (m_pfRel->GetMetadataValue(SECTION_OVERVIEW,
386 : SECTION_ASPECTES_TECNICS, pszKey,
387 1020 : osValue) == false ||
388 509 : osValue.empty())
389 : {
390 2 : if (pszErrorMessage)
391 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage);
392 2 : return false;
393 : }
394 : }
395 :
396 511 : if (1 != sscanf(osValue, "%d", nValue))
397 : {
398 0 : if (pszErrorMessage)
399 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage);
400 0 : return false;
401 : }
402 511 : return true;
403 : }
404 :
405 253 : bool MMRBand::GetDataTypeAndBytesPerPixel(const char *pszCompType,
406 : MMDataType *nCompressionType,
407 : MMBytesPerPixel *nBytesPerPixel)
408 : {
409 253 : if (!nCompressionType || !nBytesPerPixel || !pszCompType)
410 0 : return false;
411 :
412 253 : if (EQUAL(pszCompType, "bit"))
413 : {
414 2 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BIT;
415 2 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
416 2 : return true;
417 : }
418 251 : if (EQUAL(pszCompType, "byte"))
419 : {
420 49 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BYTE;
421 49 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
422 49 : return true;
423 : }
424 202 : if (EQUAL(pszCompType, "byte-RLE"))
425 : {
426 41 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BYTE_RLE;
427 41 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
428 41 : return true;
429 : }
430 161 : if (EQUAL(pszCompType, "integer"))
431 : {
432 15 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_INTEGER;
433 15 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
434 15 : return true;
435 : }
436 146 : if (EQUAL(pszCompType, "integer-RLE"))
437 : {
438 29 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE;
439 29 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
440 29 : return true;
441 : }
442 117 : if (EQUAL(pszCompType, "uinteger"))
443 : {
444 18 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_UINTEGER;
445 18 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
446 18 : return true;
447 : }
448 99 : if (EQUAL(pszCompType, "uinteger-RLE"))
449 : {
450 14 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE;
451 14 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
452 14 : return true;
453 : }
454 85 : if (EQUAL(pszCompType, "long"))
455 : {
456 14 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_LONG;
457 14 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
458 14 : return true;
459 : }
460 71 : if (EQUAL(pszCompType, "long-RLE"))
461 : {
462 13 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_LONG_RLE;
463 13 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
464 13 : return true;
465 : }
466 58 : if (EQUAL(pszCompType, "real"))
467 : {
468 15 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_REAL;
469 15 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
470 15 : return true;
471 : }
472 43 : if (EQUAL(pszCompType, "real-RLE"))
473 : {
474 13 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_REAL_RLE;
475 13 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
476 13 : return true;
477 : }
478 30 : if (EQUAL(pszCompType, "double"))
479 : {
480 14 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_DOUBLE;
481 14 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
482 14 : return true;
483 : }
484 16 : if (EQUAL(pszCompType, "double-RLE"))
485 : {
486 15 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE;
487 15 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
488 15 : return true;
489 : }
490 :
491 1 : return false;
492 : }
493 :
494 : // Getting data type from metadata
495 254 : bool MMRBand::UpdateDataTypeFromREL(const CPLString &osSection)
496 : {
497 254 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_UNDEFINED;
498 254 : m_eMMBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_UNDEFINED;
499 :
500 508 : CPLString osValue;
501 254 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
502 508 : "TipusCompressio", osValue) ||
503 254 : osValue.empty())
504 : {
505 1 : m_nWidth = 0;
506 1 : m_nHeight = 0;
507 1 : CPLError(CE_Failure, CPLE_AppDefined,
508 : "MiraMonRaster: no nDataType documented");
509 1 : return false;
510 : }
511 :
512 253 : if (!GetDataTypeAndBytesPerPixel(osValue.c_str(), &m_eMMDataType,
513 : &m_eMMBytesPerPixel))
514 : {
515 1 : m_nWidth = 0;
516 1 : m_nHeight = 0;
517 1 : CPLError(CE_Failure, CPLE_AppDefined,
518 : "MiraMonRaster: data type unhandled");
519 1 : return false;
520 : }
521 :
522 252 : m_nDataTypeSizeBytes = std::max(1, static_cast<int>(m_eMMBytesPerPixel));
523 252 : return true;
524 : }
525 :
526 : // Getting number of columns from metadata
527 257 : bool MMRBand::UpdateColumnsNumberFromREL(const CPLString &osSection)
528 : {
529 257 : return Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
530 : osSection, "columns", &m_nWidth,
531 257 : "MMRBand::MMRBand : No number of columns documented");
532 : }
533 :
534 256 : bool MMRBand::UpdateRowsNumberFromREL(const CPLString &osSection)
535 : {
536 256 : return Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
537 : osSection, "rows", &m_nHeight,
538 256 : "MMRBand::MMRBand : No number of rows documented");
539 : }
540 :
541 : // Getting nodata value from metadata
542 252 : void MMRBand::UpdateNoDataValue(const CPLString &osSection)
543 : {
544 504 : CPLString osValue;
545 252 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, "NODATA",
546 433 : osValue) ||
547 181 : osValue.empty())
548 : {
549 85 : m_dfNoData = 0; // No a valid value.
550 85 : m_bNoDataSet = false;
551 : }
552 : else
553 : {
554 167 : m_dfNoData = CPLAtof(osValue);
555 167 : m_bNoDataSet = true;
556 : }
557 252 : }
558 :
559 252 : void MMRBand::UpdateMinMaxValuesFromREL(const CPLString &osSection)
560 : {
561 252 : m_bMinSet = false;
562 :
563 504 : CPLString osValue;
564 :
565 252 : if (m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, "min",
566 504 : osValue) &&
567 252 : !osValue.empty())
568 : {
569 249 : if (1 == CPLsscanf(osValue, "%lf", &m_dfMin))
570 249 : m_bMinSet = true;
571 : }
572 :
573 252 : m_bMaxSet = false;
574 252 : if (m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, "max",
575 504 : osValue) &&
576 252 : !osValue.empty())
577 : {
578 249 : if (1 == CPLsscanf(osValue, "%lf", &m_dfMax))
579 249 : m_bMaxSet = true;
580 : }
581 :
582 : // Special case: dfMin > dfMax
583 252 : if (m_bMinSet && m_bMaxSet && m_dfMin > m_dfMax)
584 : {
585 0 : m_bMinSet = false;
586 0 : m_bMaxSet = false;
587 : }
588 252 : }
589 :
590 252 : void MMRBand::UpdateUnitTypeValueFromREL(const CPLString &osSection)
591 : {
592 504 : CPLString osValue;
593 :
594 252 : if (m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, "unitats",
595 298 : osValue) &&
596 46 : !osValue.empty())
597 : {
598 41 : m_osBandUnitType = std::move(osValue);
599 : }
600 252 : }
601 :
602 252 : void MMRBand::UpdateMinMaxVisuValuesFromREL(const CPLString &osSection)
603 : {
604 252 : m_bMinVisuSet = false;
605 252 : m_dfVisuMin = 1;
606 :
607 504 : CPLString osValue;
608 252 : if (m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
609 264 : "Color_ValorColor_0", osValue) &&
610 12 : !osValue.empty())
611 : {
612 12 : if (1 == CPLsscanf(osValue, "%lf", &m_dfVisuMin))
613 12 : m_bMinVisuSet = true;
614 : }
615 :
616 252 : m_bMaxVisuSet = false;
617 252 : m_dfVisuMax = 1;
618 :
619 252 : if (m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
620 264 : "Color_ValorColor_n_1", osValue) &&
621 12 : !osValue.empty())
622 : {
623 12 : if (1 == CPLsscanf(osValue, "%lf", &m_dfVisuMax))
624 12 : m_bMaxVisuSet = true;
625 : }
626 252 : }
627 :
628 252 : void MMRBand::UpdateFriendlyDescriptionFromREL(const CPLString &osSection)
629 : {
630 : // This "if" is due to CID 1620830 in Coverity Scan
631 252 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
632 252 : KEY_descriptor, m_osFriendlyDescription))
633 147 : m_osFriendlyDescription = "";
634 252 : }
635 :
636 252 : void MMRBand::UpdateReferenceSystemFromREL()
637 : {
638 : // This "if" is due to CID 1620842 in Coverity Scan
639 252 : if (!m_pfRel->GetMetadataValue("SPATIAL_REFERENCE_SYSTEM:HORIZONTAL",
640 252 : "HorizontalSystemIdentifier", m_osRefSystem))
641 0 : m_osRefSystem = "";
642 252 : }
643 :
644 252 : void MMRBand::UpdateBoundingBoxFromREL(const CPLString &osSection)
645 : {
646 : // Bounding box of the band
647 : // [ATTRIBUTE_DATA:xxxx:EXTENT] or [EXTENT]
648 504 : CPLString osValue;
649 252 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
650 480 : SECTION_EXTENT, "MinX", osValue) ||
651 228 : osValue.empty())
652 : {
653 24 : m_dfBBMinX = 0;
654 : }
655 : else
656 : {
657 228 : if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMinX))
658 0 : m_dfBBMinX = 0;
659 : }
660 :
661 252 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
662 480 : SECTION_EXTENT, "MaxX", osValue) ||
663 228 : osValue.empty())
664 : {
665 24 : m_dfBBMaxX = m_nWidth;
666 : }
667 : else
668 : {
669 228 : if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMaxX))
670 : {
671 : // If the value is something that cannot be scanned,
672 : // we silently continue as it was undefined.
673 0 : m_dfBBMaxX = m_nWidth;
674 : }
675 : }
676 :
677 252 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
678 480 : SECTION_EXTENT, "MinY", osValue) ||
679 228 : osValue.empty())
680 : {
681 24 : m_dfBBMinY = 0;
682 : }
683 : else
684 : {
685 228 : if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMinY))
686 0 : m_dfBBMinY = 0;
687 : }
688 :
689 252 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
690 480 : SECTION_EXTENT, "MaxY", osValue) ||
691 228 : osValue.empty())
692 : {
693 24 : m_dfBBMaxY = m_nHeight;
694 : }
695 : else
696 : {
697 228 : if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMaxY))
698 : {
699 : // If the value is something that cannot be scanned,
700 : // we silently continue as it was undefined.
701 0 : m_dfBBMaxY = m_nHeight;
702 : }
703 : }
704 252 : }
705 :
706 252 : void MMRBand::UpdateSimbolizationInfo(const CPLString &osSection)
707 : {
708 252 : m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection, "Color_Const",
709 252 : m_osColor_Const);
710 :
711 252 : if (EQUAL(m_osColor_Const, "1"))
712 : {
713 3 : if (CE_None == m_pfRel->UpdateGDALColorEntryFromBand(
714 3 : osSection, m_sConstantColorRGB))
715 3 : m_osValidColorConst = true;
716 : }
717 :
718 252 : m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection, "Color_Paleta",
719 252 : m_osColor_Paleta);
720 :
721 : // Treatment of the color variable
722 252 : m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
723 : "Color_TractamentVariable",
724 252 : m_osColor_TractamentVariable);
725 :
726 252 : m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
727 252 : KEY_TractamentVariable, m_osTractamentVariable);
728 :
729 : // Is categorical?
730 252 : if (m_osTractamentVariable.empty())
731 : {
732 16 : m_bIsCategorical = false;
733 : }
734 : else
735 : {
736 236 : if (EQUAL(m_osTractamentVariable, "Categoric"))
737 119 : m_bIsCategorical = true;
738 : else
739 117 : m_bIsCategorical = false;
740 : }
741 :
742 252 : m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
743 252 : "Color_EscalatColor", m_osColor_EscalatColor);
744 :
745 252 : m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
746 : "Color_N_SimbolsALaTaula",
747 252 : m_osColor_N_SimbolsALaTaula);
748 252 : }
749 :
750 252 : void MMRBand::UpdateRATInfo(const CPLString &osSection)
751 : {
752 252 : CPLString os_IndexJoin;
753 :
754 252 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
755 359 : "IndexsJoinTaula", os_IndexJoin) ||
756 107 : os_IndexJoin.empty())
757 : {
758 145 : return;
759 : }
760 :
761 : // Let's see if there is any table that can ve converted to RAT
762 107 : const CPLStringList aosTokens(CSLTokenizeString2(os_IndexJoin, ",", 0));
763 107 : const int nTokens = CSLCount(aosTokens);
764 107 : if (nTokens < 1)
765 0 : return;
766 :
767 107 : CPLString os_Join = "JoinTaula";
768 107 : os_Join.append("_");
769 107 : os_Join.append(aosTokens[0]);
770 :
771 107 : CPLString osTableNameSection_value;
772 107 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, os_Join,
773 214 : osTableNameSection_value) ||
774 107 : osTableNameSection_value.empty())
775 0 : return;
776 :
777 107 : CPLString osTableNameSection = "TAULA_";
778 107 : osTableNameSection.append(osTableNameSection_value);
779 :
780 107 : if (!m_pfRel->GetMetadataValue(osTableNameSection, KEY_NomFitxer,
781 214 : m_osShortRATName) ||
782 107 : m_osShortRATName.empty())
783 : {
784 0 : m_osAssociateREL = "";
785 0 : return;
786 : }
787 :
788 107 : CPL_IGNORE_RET_VAL(m_pfRel->GetMetadataValue(
789 107 : osTableNameSection, "AssociatRel", m_osAssociateREL));
790 : }
791 :
792 : /************************************************************************/
793 : /* Functions that read bytes from IMG file band */
794 : /************************************************************************/
795 : template <typename TYPE>
796 192 : CPLErr MMRBand::UncompressRow(void *rowBuffer, size_t nCompressedRawSize)
797 : {
798 192 : int nAccumulated = 0L, nIAccumulated = 0L;
799 : unsigned char cCounter;
800 192 : size_t nCompressedIndex = 0;
801 :
802 : TYPE RLEValue;
803 : TYPE *pDst;
804 192 : size_t sizeof_TYPE = sizeof(TYPE);
805 :
806 384 : std::vector<unsigned char> aCompressedRow;
807 :
808 192 : if (nCompressedRawSize != SIZE_MAX)
809 : {
810 100 : if (nCompressedRawSize > 1000 * 1000 &&
811 0 : GetFileSize() < nCompressedRawSize)
812 : {
813 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too small file");
814 0 : return CE_Failure;
815 : }
816 : try
817 : {
818 100 : aCompressedRow.resize(nCompressedRawSize);
819 : }
820 0 : catch (const std::exception &)
821 : {
822 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
823 : "Out of memory allocating working buffer");
824 0 : return CE_Failure;
825 : }
826 100 : if (VSIFReadL(aCompressedRow.data(), nCompressedRawSize, 1, m_pfIMG) !=
827 : 1)
828 0 : return CE_Failure;
829 : }
830 :
831 685 : while (nAccumulated < m_nWidth)
832 : {
833 493 : if (nCompressedRawSize == SIZE_MAX)
834 : {
835 239 : if (VSIFReadL(&cCounter, 1, 1, m_pfIMG) != 1)
836 0 : return CE_Failure;
837 : }
838 : else
839 : {
840 254 : if (nCompressedIndex >= aCompressedRow.size())
841 : {
842 0 : CPLError(CE_Failure, CPLE_AppDefined,
843 : "Invalid nCompressedIndex");
844 0 : return CE_Failure;
845 : }
846 254 : cCounter = aCompressedRow[nCompressedIndex];
847 254 : nCompressedIndex++;
848 : }
849 :
850 493 : if (cCounter == 0) /* Not compressed part */
851 : {
852 : /* The following counter read does not indicate
853 : "how many repeated values follow" but rather
854 : "how many are decompressed in standard raster format" */
855 6 : if (nCompressedRawSize == SIZE_MAX)
856 : {
857 0 : if (VSIFReadL(&cCounter, 1, 1, m_pfIMG) != 1)
858 0 : return CE_Failure;
859 : }
860 : else
861 : {
862 6 : if (nCompressedIndex >= aCompressedRow.size())
863 : {
864 0 : CPLError(CE_Failure, CPLE_AppDefined,
865 : "Invalid nCompressedIndex");
866 0 : return CE_Failure;
867 : }
868 6 : cCounter = aCompressedRow[nCompressedIndex];
869 6 : nCompressedIndex++;
870 : }
871 :
872 6 : nAccumulated += cCounter;
873 :
874 6 : if (nAccumulated > m_nWidth) /* This should not happen if the file
875 : is RLE and does not share counters across rows */
876 0 : return CE_Failure;
877 :
878 36 : for (; nIAccumulated < nAccumulated; nIAccumulated++)
879 : {
880 30 : if (nCompressedRawSize == SIZE_MAX)
881 : {
882 0 : VSIFReadL(&RLEValue, sizeof_TYPE, 1, m_pfIMG);
883 0 : memcpy((static_cast<TYPE *>(rowBuffer)) + nIAccumulated,
884 : &RLEValue, sizeof_TYPE);
885 : }
886 : else
887 : {
888 30 : if (nCompressedIndex + sizeof_TYPE > aCompressedRow.size())
889 : {
890 0 : CPLError(CE_Failure, CPLE_AppDefined,
891 : "Invalid nCompressedIndex");
892 0 : return CE_Failure;
893 : }
894 30 : memcpy((static_cast<TYPE *>(rowBuffer)) + nIAccumulated,
895 30 : &aCompressedRow[nCompressedIndex], sizeof_TYPE);
896 30 : nCompressedIndex += sizeof_TYPE;
897 : }
898 : }
899 : }
900 : else
901 : {
902 487 : nAccumulated += cCounter;
903 487 : if (nAccumulated > m_nWidth) /* This should not happen if the file
904 : is RLE and does not share counters across rows */
905 0 : return CE_Failure;
906 :
907 487 : if (nCompressedRawSize == SIZE_MAX)
908 : {
909 239 : if (VSIFReadL(&RLEValue, sizeof_TYPE, 1, m_pfIMG) != 1)
910 0 : return CE_Failure;
911 : }
912 : else
913 : {
914 248 : if (nCompressedIndex + sizeof(TYPE) > aCompressedRow.size())
915 : {
916 0 : CPLError(CE_Failure, CPLE_AppDefined,
917 : "Invalid nCompressedIndex");
918 0 : return CE_Failure;
919 : }
920 248 : memcpy(&RLEValue, &aCompressedRow[nCompressedIndex],
921 : sizeof(TYPE));
922 248 : nCompressedIndex += sizeof(TYPE);
923 : }
924 :
925 487 : const int nCount = nAccumulated - nIAccumulated;
926 487 : pDst = static_cast<TYPE *>(rowBuffer) + nIAccumulated;
927 :
928 487 : std::fill(pDst, pDst + nCount, RLEValue);
929 :
930 487 : nIAccumulated = nAccumulated;
931 : }
932 : }
933 :
934 192 : return CE_None;
935 : }
936 :
937 391 : CPLErr MMRBand::GetBlockData(void *rowBuffer, size_t nCompressedRawSize)
938 : {
939 391 : if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT)
940 : {
941 16 : const int nGDALBlockSize = DIV_ROUND_UP(m_nBlockXSize, 8);
942 :
943 16 : if (VSIFReadL(rowBuffer, nGDALBlockSize, 1, m_pfIMG) != 1)
944 : {
945 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error while reading band");
946 0 : return CE_Failure;
947 : }
948 16 : return CE_None;
949 : }
950 :
951 375 : if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BYTE ||
952 332 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_INTEGER ||
953 304 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_UINTEGER ||
954 276 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_LONG ||
955 248 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_REAL ||
956 220 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_DOUBLE)
957 : {
958 183 : if (VSIFReadL(rowBuffer, m_nDataTypeSizeBytes, m_nWidth, m_pfIMG) !=
959 183 : static_cast<size_t>(m_nWidth))
960 : {
961 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error while reading band");
962 0 : return CE_Failure;
963 : }
964 183 : return CE_None;
965 : }
966 :
967 : CPLErr eErr;
968 192 : switch (m_eMMDataType)
969 : {
970 49 : case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
971 49 : eErr = UncompressRow<GByte>(rowBuffer, nCompressedRawSize);
972 49 : break;
973 31 : case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
974 31 : eErr = UncompressRow<GInt16>(rowBuffer, nCompressedRawSize);
975 31 : break;
976 28 : case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
977 28 : eErr = UncompressRow<GUInt16>(rowBuffer, nCompressedRawSize);
978 28 : break;
979 28 : case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
980 28 : eErr = UncompressRow<GInt32>(rowBuffer, nCompressedRawSize);
981 28 : break;
982 28 : case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
983 28 : eErr = UncompressRow<float>(rowBuffer, nCompressedRawSize);
984 28 : break;
985 28 : case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
986 28 : eErr = UncompressRow<double>(rowBuffer, nCompressedRawSize);
987 28 : break;
988 :
989 0 : default:
990 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error in datatype");
991 0 : eErr = CE_Failure;
992 : }
993 :
994 192 : return eErr;
995 : } // End of GetBlockData()
996 :
997 86 : int MMRBand::PositionAtStartOfRowOffsetsInFile()
998 : {
999 : vsi_l_offset nFileSize, nHeaderOffset;
1000 : char szChain[16];
1001 : GInt16 nVersion, nSubVersion;
1002 : int nOffsetSize, nOffsetsSectionType;
1003 :
1004 86 : if (VSIFSeekL(m_pfIMG, 0, SEEK_END))
1005 0 : return 0;
1006 :
1007 86 : nFileSize = VSIFTellL(m_pfIMG);
1008 :
1009 86 : if (nFileSize < 32) // Minimum required size
1010 2 : return 0;
1011 :
1012 84 : if (m_nHeight)
1013 : {
1014 84 : if (nFileSize < static_cast<vsi_l_offset>(32) + m_nHeight + 32)
1015 0 : return 0;
1016 : }
1017 :
1018 84 : vsi_l_offset nHeadOffset = nFileSize - 32;
1019 :
1020 84 : if (VSIFSeekL(m_pfIMG, nHeadOffset, SEEK_SET)) // Reading final header.
1021 0 : return 0;
1022 84 : if (VSIFReadL(szChain, 16, 1, m_pfIMG) != 1)
1023 0 : return 0;
1024 1428 : for (int nIndex = 0; nIndex < 16; nIndex++)
1025 : {
1026 1344 : if (szChain[nIndex] != '\0')
1027 0 : return 0; // Supposed 0's are not 0.
1028 : }
1029 :
1030 84 : if (VSIFReadL(szChain, 8, 1, m_pfIMG) != 1)
1031 0 : return 0;
1032 :
1033 84 : if (strncmp(szChain, "IMG ", 4) || szChain[5] != '.')
1034 0 : return 0;
1035 :
1036 : // Some version checks
1037 84 : szChain[7] = 0;
1038 84 : if (sscanf(szChain + 6, "%hd", &nSubVersion) != 1 || nSubVersion < 0)
1039 0 : return 0;
1040 :
1041 84 : szChain[5] = 0;
1042 84 : if (sscanf(szChain + 4, "%hd", &nVersion) != 1 || nVersion != 1)
1043 0 : return 0;
1044 :
1045 : // Next header to be read
1046 84 : if (VSIFReadL(&nHeaderOffset, sizeof(vsi_l_offset), 1, m_pfIMG) != 1)
1047 0 : return 0;
1048 :
1049 168 : std::set<vsi_l_offset> alreadyVisitedOffsets;
1050 : bool bRepeat;
1051 84 : do
1052 : {
1053 84 : bRepeat = FALSE;
1054 :
1055 84 : if (VSIFSeekL(m_pfIMG, nHeaderOffset, SEEK_SET))
1056 0 : return 0;
1057 :
1058 84 : if (VSIFReadL(szChain, 8, 1, m_pfIMG) != 1)
1059 0 : return 0;
1060 :
1061 84 : if (strncmp(szChain, "IMG ", 4) || szChain[5] != '.')
1062 0 : return 0;
1063 :
1064 84 : if (VSIFReadL(&nOffsetsSectionType, 4, 1, m_pfIMG) != 1)
1065 0 : return 0;
1066 :
1067 84 : if (nOffsetsSectionType != 2) // 2 = row offsets section
1068 : {
1069 : // This is not the section I am looking for
1070 0 : if (VSIFSeekL(m_pfIMG, 8 + 4, SEEK_CUR))
1071 0 : return 0;
1072 :
1073 0 : if (VSIFReadL(&nHeaderOffset, sizeof(vsi_l_offset), 1, m_pfIMG) !=
1074 : 1)
1075 0 : return 0;
1076 :
1077 0 : if (nHeaderOffset == 0)
1078 0 : return 0;
1079 :
1080 0 : if (cpl::contains(alreadyVisitedOffsets, nHeaderOffset))
1081 : {
1082 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1083 : "Error reading offsets. They will be ignored.");
1084 0 : return 0;
1085 : }
1086 :
1087 0 : alreadyVisitedOffsets.insert(nHeaderOffset);
1088 :
1089 0 : bRepeat = TRUE;
1090 : }
1091 :
1092 : } while (bRepeat);
1093 :
1094 84 : szChain[7] = 0;
1095 84 : if (sscanf(szChain + 6, "%hd", &nSubVersion) != 1 || nSubVersion < 0)
1096 0 : return 0;
1097 84 : szChain[5] = 0;
1098 84 : if (sscanf(szChain + 4, "%hd", &nVersion) != 1 || nVersion != 1)
1099 0 : return 0;
1100 :
1101 : /*
1102 : Now I'm in the correct section
1103 : -------------------------------
1104 : Info about this section:
1105 : RasterRLE: minimum size: nHeight*2
1106 : Offsets: minimum size: 32+nHeight*4
1107 : Final: size: 32
1108 : */
1109 :
1110 84 : if (m_nHeight)
1111 : {
1112 84 : if (nHeaderOffset < static_cast<vsi_l_offset>(m_nHeight) *
1113 84 : 2 || // Minimum size of an RLE
1114 84 : nFileSize - nHeaderOffset <
1115 84 : static_cast<vsi_l_offset>(32) + m_nHeight +
1116 : 32) // Minimum size of the section in version 1.0
1117 0 : return 0;
1118 : }
1119 :
1120 168 : if (VSIFReadL(&nOffsetSize, 4, 1, m_pfIMG) != 1 ||
1121 84 : (nOffsetSize != 8 && nOffsetSize != 4 && nOffsetSize != 2 &&
1122 84 : nOffsetSize != 1))
1123 0 : return 0;
1124 :
1125 84 : if (m_nHeight)
1126 : {
1127 84 : if (nFileSize - nHeaderOffset <
1128 84 : 32 + static_cast<vsi_l_offset>(nOffsetSize) * m_nHeight +
1129 : 32) // No space for this section in this file
1130 0 : return 0;
1131 :
1132 : // I leave the file prepared to read offsets
1133 84 : if (VSIFSeekL(m_pfIMG, 16, SEEK_CUR))
1134 0 : return 0;
1135 : }
1136 : else
1137 : {
1138 0 : if (VSIFSeekL(m_pfIMG, 4, SEEK_CUR))
1139 0 : return 0;
1140 :
1141 0 : if (VSIFSeekL(m_pfIMG, 4, SEEK_CUR))
1142 0 : return 0;
1143 :
1144 : // I leave the file prepared to read offsets
1145 0 : if (VSIFSeekL(m_pfIMG, 8, SEEK_CUR))
1146 0 : return 0;
1147 : }
1148 :
1149 : // There are offsets!
1150 84 : return nOffsetSize;
1151 : } // Fi de PositionAtStartOfRowOffsetsInFile()
1152 :
1153 : /************************************************************************/
1154 : /* GetFileSize() */
1155 : /************************************************************************/
1156 :
1157 0 : vsi_l_offset MMRBand::GetFileSize()
1158 : {
1159 0 : if (m_nFileSize == 0)
1160 : {
1161 0 : const auto nCurPos = VSIFTellL(m_pfIMG);
1162 0 : VSIFSeekL(m_pfIMG, 0, SEEK_END);
1163 0 : m_nFileSize = VSIFTellL(m_pfIMG);
1164 0 : VSIFSeekL(m_pfIMG, nCurPos, SEEK_SET);
1165 : }
1166 0 : return m_nFileSize;
1167 : }
1168 :
1169 : /************************************************************************/
1170 : /* FillRowOffsets() */
1171 : /************************************************************************/
1172 :
1173 385 : bool MMRBand::FillRowOffsets()
1174 : {
1175 : vsi_l_offset nStartOffset;
1176 : int nIRow;
1177 : vsi_l_offset nBytesPerPixelPerNCol;
1178 : int nSizeToRead; // nSizeToRead is not an offset, but the size of the offsets being read
1179 : // directly from the IMG file (can be 1, 2, 4, or 8).
1180 : vsi_l_offset nFileByte;
1181 : size_t nMaxBytesPerCompressedRow;
1182 385 : const int nGDALBlockSize = DIV_ROUND_UP(m_nBlockXSize, 8);
1183 :
1184 : // If it's filled, there is no need to fill it again
1185 385 : if (!m_aFileOffsets.empty())
1186 214 : return true;
1187 :
1188 : // Sanity check to avoid attempting huge memory allocation
1189 171 : if (m_nHeight > 1000 * 1000)
1190 : {
1191 0 : if (GetFileSize() < static_cast<vsi_l_offset>(m_nHeight))
1192 : {
1193 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too small file");
1194 0 : return false;
1195 : }
1196 : }
1197 :
1198 : try
1199 : {
1200 171 : m_aFileOffsets.resize(static_cast<size_t>(m_nHeight) + 1);
1201 : }
1202 0 : catch (const std::bad_alloc &e)
1203 : {
1204 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1205 0 : return false;
1206 : }
1207 :
1208 171 : switch (m_eMMDataType)
1209 : {
1210 2 : case MMDataType::DATATYPE_AND_COMPR_BIT:
1211 :
1212 : // "<=" it's ok. There is space and it's to make easier the programming
1213 20 : for (nIRow = 0; nIRow <= m_nHeight; nIRow++)
1214 18 : m_aFileOffsets[nIRow] =
1215 18 : static_cast<vsi_l_offset>(nIRow) * nGDALBlockSize;
1216 2 : break;
1217 :
1218 83 : case MMDataType::DATATYPE_AND_COMPR_BYTE:
1219 : case MMDataType::DATATYPE_AND_COMPR_INTEGER:
1220 : case MMDataType::DATATYPE_AND_COMPR_UINTEGER:
1221 : case MMDataType::DATATYPE_AND_COMPR_LONG:
1222 : case MMDataType::DATATYPE_AND_COMPR_REAL:
1223 : case MMDataType::DATATYPE_AND_COMPR_DOUBLE:
1224 83 : nBytesPerPixelPerNCol =
1225 83 : m_nDataTypeSizeBytes * static_cast<vsi_l_offset>(m_nWidth);
1226 : // "<=" it's ok. There is space and it's to make easier the programming
1227 349 : for (nIRow = 0; nIRow <= m_nHeight; nIRow++)
1228 266 : m_aFileOffsets[nIRow] =
1229 266 : static_cast<vsi_l_offset>(nIRow) * nBytesPerPixelPerNCol;
1230 83 : break;
1231 :
1232 86 : case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
1233 : case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
1234 : case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
1235 : case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
1236 : case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
1237 : case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
1238 :
1239 86 : nStartOffset = VSIFTellL(m_pfIMG);
1240 :
1241 : // Let's determine if are there offsets in the file
1242 86 : if (0 < (nSizeToRead = PositionAtStartOfRowOffsetsInFile()))
1243 : {
1244 : // I have offsets!!
1245 84 : nFileByte = 0L; // all bits to 0
1246 264 : for (nIRow = 0; nIRow < m_nHeight; nIRow++)
1247 : {
1248 180 : if (VSIFReadL(&nFileByte, nSizeToRead, 1, m_pfIMG) != 1)
1249 0 : return false;
1250 :
1251 180 : m_aFileOffsets[nIRow] = nFileByte;
1252 :
1253 : // Let's check that the difference between two offsets is in a int range
1254 180 : if (nIRow > 0)
1255 : {
1256 192 : if (m_aFileOffsets[nIRow] <=
1257 96 : m_aFileOffsets[static_cast<size_t>(nIRow) - 1])
1258 0 : return false;
1259 :
1260 96 : if (m_aFileOffsets[nIRow] -
1261 96 : m_aFileOffsets[static_cast<size_t>(nIRow) -
1262 96 : 1] >=
1263 : static_cast<vsi_l_offset>(SIZE_MAX))
1264 0 : return false;
1265 : }
1266 : }
1267 84 : m_aFileOffsets[nIRow] = 0; // Not reliable
1268 84 : VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
1269 84 : break;
1270 : }
1271 :
1272 : // Not indexed RLE. We create a dynamic indexation
1273 4 : if (m_nWidth >
1274 2 : INT_MAX /
1275 2 : (std::max(1, static_cast<int>(m_eMMBytesPerPixel)) + 1))
1276 : {
1277 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large row: %d",
1278 : m_nWidth);
1279 0 : VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
1280 0 : return false;
1281 : }
1282 :
1283 2 : nMaxBytesPerCompressedRow =
1284 2 : static_cast<int>(m_eMMBytesPerPixel)
1285 2 : ? (m_nWidth * (static_cast<int>(m_eMMBytesPerPixel) + 1))
1286 0 : : (m_nWidth * (1 + 1));
1287 : unsigned char *pBuffer;
1288 :
1289 2 : if (nullptr == (pBuffer = static_cast<unsigned char *>(
1290 2 : VSI_MALLOC_VERBOSE(nMaxBytesPerCompressedRow))))
1291 : {
1292 0 : VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
1293 0 : return false;
1294 : }
1295 :
1296 2 : VSIFSeekL(m_pfIMG, 0, SEEK_SET);
1297 2 : m_aFileOffsets[0] = 0;
1298 8 : for (nIRow = 0; nIRow < m_nHeight; nIRow++)
1299 : {
1300 6 : GetBlockData(pBuffer, SIZE_MAX);
1301 6 : m_aFileOffsets[static_cast<size_t>(nIRow) + 1] =
1302 6 : VSIFTellL(m_pfIMG);
1303 : }
1304 2 : VSIFree(pBuffer);
1305 2 : VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
1306 2 : break;
1307 :
1308 0 : default:
1309 0 : return false;
1310 : } // End of switch (eMMDataType)
1311 171 : return true;
1312 :
1313 : } // End of FillRowOffsets()
1314 :
1315 : /************************************************************************/
1316 : /* Writing part() */
1317 : /* Indexing a compressed file increments the efficiency when reading it */
1318 : /************************************************************************/
1319 93 : bool MMRBand::WriteRowOffsets()
1320 : {
1321 93 : if (m_aFileOffsets.empty() || m_nHeight == 0)
1322 0 : return true;
1323 :
1324 93 : VSIFSeekL(m_pfIMG, 0, SEEK_END);
1325 :
1326 93 : vsi_l_offset nStartOffset = VSIFTellL(m_pfIMG);
1327 93 : if (nStartOffset == 0)
1328 0 : return false;
1329 :
1330 93 : if (VSIFWriteL("IMG 1.0\0", sizeof("IMG 1.0"), 1, m_pfIMG) != 1)
1331 0 : return false;
1332 :
1333 : // Type of section
1334 93 : int nAux = 2;
1335 93 : if (VSIFWriteL(&nAux, 4, 1, m_pfIMG) != 1)
1336 0 : return false;
1337 :
1338 93 : size_t nIndexOffset = static_cast<size_t>(m_nHeight);
1339 : int nOffsetSize;
1340 :
1341 93 : if (m_aFileOffsets[nIndexOffset - 1] < static_cast<vsi_l_offset>(UCHAR_MAX))
1342 93 : nOffsetSize = 1;
1343 0 : else if (m_aFileOffsets[nIndexOffset - 1] <
1344 : static_cast<vsi_l_offset>(USHRT_MAX))
1345 0 : nOffsetSize = 2;
1346 0 : else if (m_aFileOffsets[nIndexOffset - 1] <
1347 : static_cast<vsi_l_offset>(UINT32_MAX))
1348 0 : nOffsetSize = 4;
1349 : else
1350 0 : nOffsetSize = 8;
1351 :
1352 93 : if (VSIFWriteL(&nOffsetSize, 4, 1, m_pfIMG) != 1)
1353 0 : return false;
1354 :
1355 93 : nAux = 0;
1356 93 : if (VSIFWriteL(&nAux, 4, 1, m_pfIMG) != 1)
1357 0 : return false;
1358 93 : if (VSIFWriteL(&nAux, 4, 1, m_pfIMG) != 1)
1359 0 : return false;
1360 :
1361 93 : vsi_l_offset nUselessOffset = 0;
1362 93 : if (VSIFWriteL(&nUselessOffset, sizeof(vsi_l_offset), 1, m_pfIMG) != 1)
1363 0 : return false;
1364 :
1365 : // The main part
1366 93 : size_t nSizeTOffsetSize = nOffsetSize;
1367 93 : if (nSizeTOffsetSize == sizeof(vsi_l_offset))
1368 : {
1369 0 : if (VSIFWriteL(&(m_aFileOffsets[0]), sizeof(vsi_l_offset), nIndexOffset,
1370 0 : m_pfIMG) != nIndexOffset)
1371 0 : return false;
1372 : }
1373 : else
1374 : {
1375 437 : for (nIndexOffset = 0; nIndexOffset < m_aFileOffsets.size() - 1;
1376 : nIndexOffset++)
1377 : {
1378 344 : if (VSIFWriteL(&(m_aFileOffsets[nIndexOffset]), nSizeTOffsetSize, 1,
1379 344 : m_pfIMG) != 1)
1380 0 : return false;
1381 : }
1382 : }
1383 :
1384 : // End part of file
1385 93 : nAux = 0;
1386 465 : for (int nIndex = 0; nIndex < 4; nIndex++)
1387 : {
1388 372 : if (VSIFWriteL(&nAux, 4, 1, m_pfIMG) != 1)
1389 0 : return false;
1390 : }
1391 93 : if (VSIFWriteL("IMG 1.0\0", sizeof("IMG 1.0"), 1, m_pfIMG) != 1)
1392 0 : return false;
1393 :
1394 93 : if (VSIFWriteL(&nStartOffset, sizeof(vsi_l_offset), 1, m_pfIMG) != 1)
1395 0 : return false;
1396 :
1397 93 : return true;
1398 :
1399 : } // End of WriteRowOffsets()
1400 :
1401 : // Getting data type from dataset
1402 167 : bool MMRBand::UpdateDataTypeAndBytesPerPixelFromRasterBand(
1403 : GDALRasterBand &papoBand)
1404 : {
1405 167 : switch (papoBand.GetRasterDataType())
1406 : {
1407 46 : case GDT_Byte:
1408 46 : if (m_bIsCompressed)
1409 35 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_BYTE_RLE;
1410 : else
1411 11 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_BYTE;
1412 :
1413 46 : m_eMMBytesPerPixel =
1414 : MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
1415 46 : break;
1416 :
1417 23 : case GDT_UInt16:
1418 23 : if (m_bIsCompressed)
1419 12 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE;
1420 : else
1421 11 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_UINTEGER;
1422 :
1423 23 : m_eMMBytesPerPixel =
1424 : MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
1425 23 : break;
1426 :
1427 24 : case GDT_Int16:
1428 24 : if (m_bIsCompressed)
1429 13 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE;
1430 : else
1431 11 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_INTEGER;
1432 :
1433 24 : m_eMMBytesPerPixel =
1434 : MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
1435 24 : break;
1436 :
1437 23 : case GDT_Int32:
1438 23 : if (m_bIsCompressed)
1439 12 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_LONG_RLE;
1440 : else
1441 11 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_LONG;
1442 :
1443 23 : m_eMMBytesPerPixel =
1444 : MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
1445 23 : break;
1446 :
1447 23 : case GDT_Float32:
1448 23 : if (m_bIsCompressed)
1449 12 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_REAL_RLE;
1450 : else
1451 11 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_REAL;
1452 :
1453 23 : m_eMMBytesPerPixel =
1454 : MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
1455 23 : break;
1456 :
1457 23 : case GDT_Float64:
1458 23 : if (m_bIsCompressed)
1459 12 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE;
1460 : else
1461 11 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_DOUBLE;
1462 :
1463 23 : m_eMMBytesPerPixel =
1464 : MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
1465 23 : break;
1466 :
1467 5 : default:
1468 5 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_UNDEFINED;
1469 5 : m_eMMBytesPerPixel =
1470 : MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_UNDEFINED;
1471 5 : m_nDataTypeSizeBytes = 0;
1472 5 : m_nWidth = 0;
1473 5 : m_nHeight = 0;
1474 5 : CPLError(CE_Failure, CPLE_AppDefined,
1475 : "MiraMonRaster: data type unhandled");
1476 5 : return false;
1477 : }
1478 162 : return true;
1479 : }
1480 :
1481 : // Getting nodata value from metadata
1482 162 : void MMRBand::UpdateNoDataValueFromRasterBand(GDALRasterBand &papoBand)
1483 : {
1484 : int pbSuccess;
1485 162 : m_dfNoData = papoBand.GetNoDataValue(&pbSuccess);
1486 162 : m_bNoDataSet = pbSuccess == 1 ? true : false;
1487 162 : }
1488 :
1489 282 : CPLString MMRBand::GetRELDataType() const
1490 : {
1491 282 : if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT)
1492 0 : return "bit";
1493 282 : if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BYTE ||
1494 262 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BYTE_RLE)
1495 : {
1496 70 : if (m_bIsCompressed)
1497 50 : return "byte-RLE";
1498 20 : return "byte";
1499 : }
1500 212 : if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_INTEGER ||
1501 192 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE)
1502 : {
1503 44 : if (m_bIsCompressed)
1504 24 : return "integer-RLE";
1505 20 : return "integer";
1506 : }
1507 :
1508 168 : if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_UINTEGER ||
1509 148 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE)
1510 : {
1511 42 : if (m_bIsCompressed)
1512 22 : return "uinteger-RLE";
1513 20 : return "uinteger";
1514 : }
1515 :
1516 126 : if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_LONG ||
1517 106 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_LONG_RLE)
1518 : {
1519 42 : if (m_bIsCompressed)
1520 22 : return "long-RLE";
1521 20 : return "long";
1522 : }
1523 :
1524 84 : if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_REAL ||
1525 64 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_REAL_RLE)
1526 : {
1527 42 : if (m_bIsCompressed)
1528 22 : return "real-RLE";
1529 20 : return "real";
1530 : }
1531 :
1532 42 : if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_DOUBLE ||
1533 22 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE)
1534 : {
1535 42 : if (m_bIsCompressed)
1536 22 : return "double-RLE";
1537 20 : return "double";
1538 : }
1539 :
1540 0 : return "";
1541 : }
1542 :
1543 476 : void MMRBand::UpdateRowMinMax(const void *pBuffer)
1544 : {
1545 476 : switch (m_eMMDataType)
1546 : {
1547 203 : case MMDataType::DATATYPE_AND_COMPR_BIT:
1548 : case MMDataType::DATATYPE_AND_COMPR_BYTE:
1549 : case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
1550 203 : UpdateRowMinMax<unsigned char>(pBuffer);
1551 203 : break;
1552 :
1553 54 : case MMDataType::DATATYPE_AND_COMPR_UINTEGER:
1554 : case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
1555 54 : UpdateRowMinMax<GUInt16>(pBuffer);
1556 54 : break;
1557 :
1558 57 : case MMDataType::DATATYPE_AND_COMPR_INTEGER:
1559 : case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
1560 : case MMDataType::DATATYPE_AND_COMPR_INTEGER_ASCII:
1561 57 : UpdateRowMinMax<GInt16>(pBuffer);
1562 57 : break;
1563 :
1564 54 : case MMDataType::DATATYPE_AND_COMPR_LONG:
1565 : case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
1566 54 : UpdateRowMinMax<int>(pBuffer);
1567 54 : break;
1568 :
1569 54 : case MMDataType::DATATYPE_AND_COMPR_REAL:
1570 : case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
1571 : case MMDataType::DATATYPE_AND_COMPR_REAL_ASCII:
1572 54 : UpdateRowMinMax<float>(pBuffer);
1573 54 : break;
1574 :
1575 54 : case MMDataType::DATATYPE_AND_COMPR_DOUBLE:
1576 : case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
1577 54 : UpdateRowMinMax<double>(pBuffer);
1578 54 : break;
1579 :
1580 0 : default:
1581 0 : break;
1582 : }
1583 476 : }
1584 :
1585 159 : bool MMRBand::WriteBandFile(GDALDataset &oSrcDS, int nNBands, int nIBand)
1586 : {
1587 :
1588 159 : GDALRasterBand *pRasterBand = oSrcDS.GetRasterBand(nIBand + 1);
1589 159 : if (!pRasterBand)
1590 0 : return false;
1591 :
1592 : // Updating variable to suitable values
1593 159 : m_nBlockXSize = m_nWidth;
1594 159 : m_nBlockYSize = 1;
1595 159 : m_nNRowsPerBlock = 1;
1596 :
1597 : // Opening the RAW file
1598 159 : m_pfIMG = VSIFOpenL(m_osBandFileName, "wb");
1599 159 : if (!m_pfIMG)
1600 : {
1601 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1602 : "Failed to create MiraMon band file `%s' with access 'wb'.",
1603 : m_osBandFileName.c_str());
1604 0 : return false;
1605 : }
1606 :
1607 : // Creating index information
1608 159 : if (m_bIsCompressed)
1609 : {
1610 : try
1611 : {
1612 93 : m_aFileOffsets.resize(static_cast<size_t>(m_nHeight) + 1);
1613 : }
1614 0 : catch (const std::bad_alloc &e)
1615 : {
1616 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1617 0 : return false;
1618 : }
1619 : }
1620 :
1621 159 : GDALDataType eDT = pRasterBand->GetRasterDataType();
1622 :
1623 : // Temporary buffer for one scanline
1624 : void *pBuffer =
1625 159 : VSI_MALLOC2_VERBOSE(m_nWidth, GDALGetDataTypeSizeBytes(eDT));
1626 159 : if (pBuffer == nullptr)
1627 0 : return false;
1628 :
1629 : void *pRow =
1630 159 : VSI_MALLOC2_VERBOSE(m_nWidth, (GDALGetDataTypeSizeBytes(eDT) + 1));
1631 159 : if (pRow == nullptr)
1632 : {
1633 0 : VSIFree(pBuffer);
1634 0 : return false;
1635 : }
1636 :
1637 : // Loop over each line
1638 159 : double dfComplete = nIBand * 1.0 / nNBands;
1639 159 : double dfIncr = 1.0 / (nNBands * m_nHeight);
1640 159 : if (!m_pfnProgress(dfComplete, nullptr, m_pProgressData))
1641 : {
1642 0 : VSIFree(pBuffer);
1643 0 : VSIFree(pRow);
1644 0 : return false;
1645 : }
1646 :
1647 159 : m_bMinSet = false;
1648 159 : m_dfMin = std::numeric_limits<double>::max();
1649 159 : m_bMaxSet = false;
1650 159 : m_dfMax = -std::numeric_limits<double>::max();
1651 :
1652 635 : for (int iLine = 0; iLine < m_nHeight; ++iLine)
1653 : {
1654 : // Read one line from the raster band
1655 : CPLErr err =
1656 476 : pRasterBand->RasterIO(GF_Read, 0, iLine, m_nWidth, 1, pBuffer,
1657 : m_nWidth, 1, eDT, 0, 0, nullptr);
1658 :
1659 476 : if (err != CE_None)
1660 : {
1661 0 : CPLError(CE_Failure, CPLE_AppDefined,
1662 : "Error reading line %d from raster band", iLine);
1663 0 : VSIFree(pBuffer);
1664 0 : VSIFree(pRow);
1665 0 : return false;
1666 : }
1667 :
1668 : // MinMax calculation
1669 476 : UpdateRowMinMax(pBuffer);
1670 :
1671 476 : if (m_bIsCompressed)
1672 344 : m_aFileOffsets[iLine] = VSIFTellL(m_pfIMG);
1673 :
1674 : // Write the line to the MiraMon band file
1675 : size_t nWritten, nCompressed;
1676 476 : if (m_bIsCompressed)
1677 : {
1678 : nCompressed =
1679 344 : CompressRowType(m_eMMDataType, pBuffer, m_nWidth, pRow);
1680 344 : nWritten = VSIFWriteL(pRow, 1, nCompressed, m_pfIMG);
1681 344 : if (nWritten != nCompressed)
1682 : {
1683 0 : CPLError(CE_Failure, CPLE_AppDefined,
1684 : "Failed to write line %d to MiraMon band file", iLine);
1685 0 : VSIFree(pBuffer);
1686 0 : VSIFree(pRow);
1687 0 : return false;
1688 : }
1689 : }
1690 : else
1691 : {
1692 132 : nWritten = VSIFWriteL(pBuffer, GDALGetDataTypeSizeBytes(eDT),
1693 132 : m_nWidth, m_pfIMG);
1694 132 : if (nWritten != static_cast<size_t>(m_nWidth))
1695 : {
1696 0 : CPLError(CE_Failure, CPLE_AppDefined,
1697 : "Failed to write line %d to MiraMon band file", iLine);
1698 0 : VSIFree(pBuffer);
1699 0 : VSIFree(pRow);
1700 0 : return false;
1701 : }
1702 : }
1703 :
1704 476 : dfComplete += dfIncr;
1705 476 : if (!m_pfnProgress(dfComplete, nullptr, m_pProgressData))
1706 : {
1707 0 : VSIFree(pBuffer);
1708 0 : VSIFree(pRow);
1709 0 : return false;
1710 : }
1711 : }
1712 159 : VSIFree(pBuffer);
1713 159 : VSIFree(pRow);
1714 :
1715 : // Updating min and max values for simbolization
1716 159 : m_dfVisuMin = m_dfMin;
1717 159 : m_bMinVisuSet = m_bMinSet;
1718 159 : m_dfVisuMax = m_dfMax;
1719 159 : m_bMaxVisuSet = m_bMaxSet;
1720 :
1721 : // There is a final part that contain the indexes to every row
1722 159 : if (m_bIsCompressed)
1723 : {
1724 93 : if (WriteRowOffsets() == false)
1725 0 : return false;
1726 : }
1727 :
1728 159 : dfComplete = (nIBand + 1.0) / nNBands;
1729 159 : if (!m_pfnProgress(dfComplete, nullptr, m_pProgressData))
1730 0 : return false;
1731 :
1732 159 : return true;
1733 : }
1734 :
1735 : constexpr uint8_t LIMIT = 255;
1736 :
1737 : template <typename T>
1738 344 : size_t MMRBand::CompressRowTypeTpl(const T *pRow, int nNCol, void *pBufferVoid)
1739 : {
1740 344 : uint8_t *pBuffer = static_cast<uint8_t *>(pBufferVoid);
1741 :
1742 344 : T tPreviousValue = pRow[0];
1743 344 : uint8_t nByteCounter = 0;
1744 344 : size_t nRowBytes = 0;
1745 :
1746 2773 : for (int i = 0; i < nNCol; i++)
1747 : {
1748 2435 : if (tPreviousValue == pRow[i] && nByteCounter < LIMIT)
1749 2170 : nByteCounter++;
1750 : else // I have found a different value or I have reached the limit of the counter
1751 : {
1752 265 : if (nByteCounter == 1)
1753 : {
1754 : //In cas of three consecutive different values, it's more efficient
1755 : // to write them as uncompressed values than as three RLE
1756 264 : if (i + 2 < nNCol && pRow[i] != pRow[i + 1] &&
1757 6 : pRow[i + 1] != pRow[i + 2])
1758 : {
1759 : // Indicates that the following values are
1760 : // uncompressed, and how many of them are there
1761 6 : *pBuffer++ = 0;
1762 6 : uint8_t *pHowManyUncompressed = pBuffer++;
1763 6 : nRowBytes += 2;
1764 :
1765 : // Writing first three
1766 6 : memcpy(pBuffer, &tPreviousValue, sizeof(T));
1767 6 : pBuffer += sizeof(T);
1768 :
1769 6 : tPreviousValue = pRow[i];
1770 6 : memcpy(pBuffer, &tPreviousValue, sizeof(T));
1771 6 : pBuffer += sizeof(T);
1772 6 : i++;
1773 :
1774 6 : tPreviousValue = pRow[i];
1775 6 : memcpy(pBuffer, &tPreviousValue, sizeof(T));
1776 6 : pBuffer += sizeof(T);
1777 :
1778 6 : nRowBytes += 3 * sizeof(T);
1779 :
1780 : // nByteCounter is now the number of bytes of the three uncompressed values
1781 6 : nByteCounter = 3;
1782 :
1783 12 : for (i++; i + 1 < nNCol && nByteCounter < LIMIT;
1784 : i++, nByteCounter++)
1785 : {
1786 6 : if (pRow[i] == pRow[i + 1])
1787 0 : break;
1788 :
1789 6 : memcpy(pBuffer, pRow + i, sizeof(T));
1790 6 : pBuffer += sizeof(T);
1791 6 : nRowBytes += sizeof(T);
1792 : }
1793 :
1794 6 : if (i + 1 == nNCol && nByteCounter < LIMIT)
1795 : {
1796 6 : *pHowManyUncompressed = ++nByteCounter;
1797 6 : memcpy(pBuffer, pRow + i, sizeof(T));
1798 6 : nRowBytes += sizeof(T);
1799 6 : return nRowBytes;
1800 : }
1801 :
1802 0 : *pHowManyUncompressed = nByteCounter;
1803 0 : tPreviousValue = pRow[i];
1804 0 : nByteCounter = 1;
1805 0 : continue;
1806 : }
1807 : }
1808 :
1809 : // Normal RLE
1810 259 : *pBuffer++ = nByteCounter;
1811 259 : memcpy(pBuffer, &tPreviousValue, sizeof(T));
1812 259 : pBuffer += sizeof(T);
1813 259 : nRowBytes += 1 + sizeof(T);
1814 :
1815 259 : tPreviousValue = pRow[i];
1816 259 : nByteCounter = 1;
1817 : }
1818 : }
1819 :
1820 : // Last element
1821 338 : *pBuffer++ = nByteCounter;
1822 :
1823 338 : memcpy(pBuffer, &tPreviousValue, sizeof(T));
1824 338 : nRowBytes += 1 + sizeof(T);
1825 :
1826 338 : return nRowBytes;
1827 : }
1828 :
1829 344 : size_t MMRBand::CompressRowType(MMDataType nDataType, const void *pRow,
1830 : int nNCol, void *pBuffer)
1831 : {
1832 344 : switch (nDataType)
1833 : {
1834 181 : case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
1835 : case MMDataType::DATATYPE_AND_COMPR_BYTE:
1836 181 : return CompressRowTypeTpl<uint8_t>(
1837 181 : reinterpret_cast<const uint8_t *>(pRow), nNCol, pBuffer);
1838 :
1839 35 : case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
1840 : case MMDataType::DATATYPE_AND_COMPR_INTEGER:
1841 35 : return CompressRowTypeTpl<GInt16>(
1842 35 : reinterpret_cast<const GInt16 *>(pRow), nNCol, pBuffer);
1843 :
1844 32 : case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
1845 : case MMDataType::DATATYPE_AND_COMPR_UINTEGER:
1846 32 : return CompressRowTypeTpl<GUInt16>(
1847 32 : reinterpret_cast<const GUInt16 *>(pRow), nNCol, pBuffer);
1848 :
1849 32 : case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
1850 : case MMDataType::DATATYPE_AND_COMPR_LONG:
1851 32 : return CompressRowTypeTpl<GInt32>(
1852 32 : reinterpret_cast<const GInt32 *>(pRow), nNCol, pBuffer);
1853 :
1854 32 : case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
1855 : case MMDataType::DATATYPE_AND_COMPR_REAL:
1856 32 : return CompressRowTypeTpl<float>(
1857 32 : reinterpret_cast<const float *>(pRow), nNCol, pBuffer);
1858 :
1859 32 : case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
1860 : case MMDataType::DATATYPE_AND_COMPR_DOUBLE:
1861 32 : return CompressRowTypeTpl<double>(
1862 32 : reinterpret_cast<const double *>(pRow), nNCol, pBuffer);
1863 :
1864 0 : default:
1865 : // same treatment than the original
1866 0 : return 0;
1867 : }
1868 : }
1869 :
1870 162 : int MMRBand::WriteColorTable(GDALDataset &oSrcDS)
1871 : {
1872 162 : GDALRasterBand *pRasterBand = oSrcDS.GetRasterBand(m_nIBand + 1);
1873 162 : if (!pRasterBand)
1874 0 : return 0;
1875 :
1876 162 : m_poCT = pRasterBand->GetColorTable();
1877 162 : if (!m_poCT)
1878 : {
1879 : // Perhaps the RAT contains colors and MiraMon can use them as a palette
1880 149 : return WriteColorTableFromRAT(oSrcDS);
1881 : }
1882 :
1883 13 : if (!m_poCT->GetColorEntryCount())
1884 0 : return 0;
1885 :
1886 : // Creating DBF table name
1887 13 : if (!cpl::ends_with(m_osBandFileName, pszExtRaster))
1888 0 : return 1;
1889 :
1890 : // Extract .img
1891 13 : m_osCTName = m_osBandFileName;
1892 13 : m_osCTName.resize(m_osCTName.size() - strlen(".img"));
1893 13 : m_osCTName.append("_CT.dbf");
1894 :
1895 : // Creating DBF
1896 : struct MM_DATA_BASE_XP *pBD_XP =
1897 13 : MM_CreateDBFHeader(4, MM_JOC_CARAC_UTF8_DBF);
1898 13 : if (!pBD_XP)
1899 0 : return 1;
1900 :
1901 : // Assigning DBF table name
1902 13 : CPLStrlcpy(pBD_XP->szFileName, m_osCTName, sizeof(pBD_XP->szFileName));
1903 :
1904 : // Initializing the table
1905 : MM_EXT_DBF_N_RECORDS nPaletteColors =
1906 13 : static_cast<MM_EXT_DBF_N_RECORDS>(m_poCT->GetColorEntryCount());
1907 13 : pBD_XP->nFields = 4;
1908 13 : pBD_XP->nRecords = nPaletteColors;
1909 13 : pBD_XP->FirstRecordOffset =
1910 13 : static_cast<MM_FIRST_RECORD_OFFSET_TYPE>(33 + (pBD_XP->nFields * 32));
1911 13 : pBD_XP->CharSet = MM_JOC_CARAC_ANSI_DBASE;
1912 13 : pBD_XP->dbf_version = MM_MARCA_DBASE4;
1913 13 : pBD_XP->BytesPerRecord = 1;
1914 :
1915 13 : MM_ACCUMULATED_BYTES_TYPE_DBF nClauSimbolNBytes = 0;
1916 2 : do
1917 : {
1918 15 : nClauSimbolNBytes++;
1919 15 : nPaletteColors /= 10;
1920 15 : } while (nPaletteColors > 0);
1921 13 : nClauSimbolNBytes++;
1922 :
1923 : // Fields of the DBF table
1924 : struct MM_FIELD MMField;
1925 13 : MM_InitializeField(&MMField);
1926 13 : MMField.FieldType = 'N';
1927 13 : CPLStrlcpy(MMField.FieldName, "CLAUSIMBOL", MM_MAX_LON_FIELD_NAME_DBF);
1928 13 : MMField.BytesPerField = nClauSimbolNBytes;
1929 13 : MM_DuplicateFieldDBXP(pBD_XP->pField, &MMField);
1930 :
1931 13 : CPLStrlcpy(MMField.FieldName, "R_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
1932 13 : MMField.BytesPerField = 3;
1933 13 : MM_DuplicateFieldDBXP(pBD_XP->pField + 1, &MMField);
1934 :
1935 13 : CPLStrlcpy(MMField.FieldName, "G_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
1936 13 : MMField.BytesPerField = 3;
1937 13 : MM_DuplicateFieldDBXP(pBD_XP->pField + 2, &MMField);
1938 :
1939 13 : CPLStrlcpy(MMField.FieldName, "B_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
1940 13 : MMField.BytesPerField = 3;
1941 13 : MM_DuplicateFieldDBXP(pBD_XP->pField + 3, &MMField);
1942 :
1943 : // Opening the table
1944 13 : if (!MM_CreateAndOpenDBFFile(pBD_XP, pBD_XP->szFileName))
1945 : {
1946 0 : MM_ReleaseDBFHeader(&pBD_XP);
1947 0 : return 1;
1948 : }
1949 :
1950 : // Writing records to the table
1951 13 : if (0 != VSIFSeekL(pBD_XP->pfDataBase,
1952 13 : static_cast<vsi_l_offset>(pBD_XP->FirstRecordOffset),
1953 : SEEK_SET))
1954 : {
1955 0 : MM_ReleaseDBFHeader(&pBD_XP);
1956 0 : return 1;
1957 : }
1958 :
1959 : GDALColorEntry colorEntry;
1960 : int nIColor;
1961 341 : for (nIColor = 0; nIColor < static_cast<int>(pBD_XP->nRecords); nIColor++)
1962 : {
1963 328 : m_poCT->GetColorEntryAsRGB(nIColor, &colorEntry);
1964 :
1965 : // Deletion flag
1966 328 : if (!VSIFPrintfL(pBD_XP->pfDataBase, " "))
1967 : {
1968 0 : MM_ReleaseDBFHeader(&pBD_XP);
1969 0 : return 1;
1970 : }
1971 :
1972 328 : if (!VSIFPrintfL(pBD_XP->pfDataBase, "%*d",
1973 : static_cast<int>(nClauSimbolNBytes), nIColor))
1974 : {
1975 0 : MM_ReleaseDBFHeader(&pBD_XP);
1976 0 : return 1;
1977 : }
1978 328 : if (colorEntry.c4 == 0)
1979 : {
1980 12 : if (!VSIFPrintfL(pBD_XP->pfDataBase, "%3d%3d%3d", -1, -1, -1))
1981 : {
1982 0 : MM_ReleaseDBFHeader(&pBD_XP);
1983 0 : return 1;
1984 : }
1985 : }
1986 : else
1987 : {
1988 316 : if (!VSIFPrintfL(pBD_XP->pfDataBase, "%3d%3d%3d",
1989 316 : static_cast<int>(colorEntry.c1),
1990 316 : static_cast<int>(colorEntry.c2),
1991 316 : static_cast<int>(colorEntry.c3)))
1992 : {
1993 0 : MM_ReleaseDBFHeader(&pBD_XP);
1994 0 : return 1;
1995 : }
1996 : }
1997 : }
1998 :
1999 13 : fclose_and_nullify(&pBD_XP->pfDataBase);
2000 13 : MM_ReleaseDBFHeader(&pBD_XP);
2001 13 : return 0;
2002 : }
2003 :
2004 : // Writes DBF and REL attribute table in MiraMon format
2005 149 : int MMRBand::WriteColorTableFromRAT(GDALDataset &oSrcDS)
2006 : {
2007 149 : GDALRasterBand *pRasterBand = oSrcDS.GetRasterBand(m_nIBand + 1);
2008 149 : if (!pRasterBand)
2009 0 : return 0;
2010 :
2011 149 : m_poRAT = pRasterBand->GetDefaultRAT();
2012 149 : if (!m_poRAT)
2013 135 : return 0;
2014 :
2015 : // At least a value and RGB columns are required
2016 14 : if (m_poRAT->GetColumnCount() < 4)
2017 12 : return 0;
2018 :
2019 2 : if (!m_poRAT->GetRowCount())
2020 0 : return 0;
2021 :
2022 : // Getting the columns that can be converted to a MiraMon palette
2023 2 : int nIValueMinMax = m_poRAT->GetColOfUsage(GFU_MinMax);
2024 2 : int nIValueMin = m_poRAT->GetColOfUsage(GFU_Min);
2025 2 : int nIValueMax = m_poRAT->GetColOfUsage(GFU_Max);
2026 2 : int nIRed = m_poRAT->GetColOfUsage(GFU_Red);
2027 2 : int nIGreen = m_poRAT->GetColOfUsage(GFU_Green);
2028 2 : int nIBlue = m_poRAT->GetColOfUsage(GFU_Blue);
2029 2 : int nIAlpha = m_poRAT->GetColOfUsage(GFU_Alpha);
2030 :
2031 : // If the RAT has no value type nor RGB colors, MiraMon can't handle that
2032 : // as a color table. MinMax or Min and Max are accepted as values.
2033 2 : if (!(nIValueMinMax != -1 || (nIValueMin != -1 && nIValueMax != -1) ||
2034 0 : nIRed == -1 || nIGreen == -1 || nIBlue == -1))
2035 0 : return 0;
2036 :
2037 : // Creating DBF table name
2038 2 : if (!cpl::ends_with(m_osBandFileName, pszExtRaster))
2039 0 : return 1;
2040 :
2041 : // Extract .img
2042 2 : m_osCTName = m_osBandFileName;
2043 2 : m_osCTName.resize(m_osCTName.size() - strlen(".img"));
2044 2 : m_osCTName.append("_CT.dbf");
2045 :
2046 : // Creating DBF
2047 : struct MM_DATA_BASE_XP *pBD_XP =
2048 2 : MM_CreateDBFHeader(4, MM_JOC_CARAC_UTF8_DBF);
2049 2 : if (!pBD_XP)
2050 0 : return 1;
2051 :
2052 : // Assigning DBF table name
2053 2 : CPLStrlcpy(pBD_XP->szFileName, m_osCTName, sizeof(pBD_XP->szFileName));
2054 :
2055 : // Initializing the table
2056 : MM_EXT_DBF_N_RECORDS nPaletteColors =
2057 2 : static_cast<MM_EXT_DBF_N_RECORDS>(m_poRAT->GetRowCount());
2058 2 : pBD_XP->nFields = 4;
2059 2 : pBD_XP->nRecords = nPaletteColors;
2060 2 : pBD_XP->FirstRecordOffset =
2061 2 : static_cast<MM_FIRST_RECORD_OFFSET_TYPE>(33 + (pBD_XP->nFields * 32));
2062 2 : pBD_XP->CharSet = MM_JOC_CARAC_ANSI_DBASE;
2063 2 : pBD_XP->dbf_version = MM_MARCA_DBASE4;
2064 2 : pBD_XP->BytesPerRecord = 1;
2065 :
2066 2 : MM_ACCUMULATED_BYTES_TYPE_DBF nClauSimbolNBytes = 0;
2067 0 : do
2068 : {
2069 2 : nClauSimbolNBytes++;
2070 2 : nPaletteColors /= 10;
2071 2 : } while (nPaletteColors > 0);
2072 2 : nClauSimbolNBytes++;
2073 :
2074 : // Fields of the DBF table
2075 : struct MM_FIELD MMField;
2076 2 : MM_InitializeField(&MMField);
2077 2 : MMField.FieldType = 'N';
2078 2 : CPLStrlcpy(MMField.FieldName, "CLAUSIMBOL", MM_MAX_LON_FIELD_NAME_DBF);
2079 2 : MMField.BytesPerField = nClauSimbolNBytes;
2080 2 : MM_DuplicateFieldDBXP(pBD_XP->pField, &MMField);
2081 :
2082 2 : CPLStrlcpy(MMField.FieldName, "R_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
2083 2 : MMField.BytesPerField = 3;
2084 2 : MM_DuplicateFieldDBXP(pBD_XP->pField + 1, &MMField);
2085 :
2086 2 : CPLStrlcpy(MMField.FieldName, "G_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
2087 2 : MMField.BytesPerField = 3;
2088 2 : MM_DuplicateFieldDBXP(pBD_XP->pField + 2, &MMField);
2089 :
2090 2 : CPLStrlcpy(MMField.FieldName, "B_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
2091 2 : MMField.BytesPerField = 3;
2092 2 : MM_DuplicateFieldDBXP(pBD_XP->pField + 3, &MMField);
2093 :
2094 : // Opening the table
2095 2 : if (!MM_CreateAndOpenDBFFile(pBD_XP, pBD_XP->szFileName))
2096 : {
2097 0 : MM_ReleaseDBFHeader(&pBD_XP);
2098 0 : return 1;
2099 : }
2100 :
2101 : // Writing records to the table
2102 2 : if (0 != VSIFSeekL(pBD_XP->pfDataBase,
2103 2 : static_cast<vsi_l_offset>(pBD_XP->FirstRecordOffset),
2104 : SEEK_SET))
2105 : {
2106 0 : MM_ReleaseDBFHeader(&pBD_XP);
2107 0 : return 1;
2108 : }
2109 :
2110 : int nIRow;
2111 14 : for (nIRow = 0; nIRow < static_cast<int>(pBD_XP->nRecords); nIRow++)
2112 : {
2113 12 : if (nIRow > 0 && nIValueMin != -1 && nIValueMax != -1)
2114 : {
2115 5 : if (m_poRAT->GetValueAsInt(nIRow, nIValueMin) ==
2116 5 : m_poRAT->GetValueAsInt(nIRow, nIValueMax))
2117 0 : break;
2118 : }
2119 : // Deletion flag
2120 12 : if (!VSIFPrintfL(pBD_XP->pfDataBase, " "))
2121 : {
2122 0 : MM_ReleaseDBFHeader(&pBD_XP);
2123 0 : return 1;
2124 : }
2125 :
2126 12 : if (!VSIFPrintfL(pBD_XP->pfDataBase, "%*d",
2127 : static_cast<int>(nClauSimbolNBytes), nIRow))
2128 : {
2129 0 : MM_ReleaseDBFHeader(&pBD_XP);
2130 0 : return 1;
2131 : }
2132 12 : if (nIAlpha != -1 && m_poRAT->GetValueAsInt(nIRow, nIAlpha) == 0)
2133 : {
2134 2 : if (!VSIFPrintfL(pBD_XP->pfDataBase, "%3d%3d%3d", -1, -1, -1))
2135 : {
2136 0 : MM_ReleaseDBFHeader(&pBD_XP);
2137 0 : return 1;
2138 : }
2139 : }
2140 : else
2141 : {
2142 10 : if (!VSIFPrintfL(pBD_XP->pfDataBase, "%3d%3d%3d",
2143 10 : m_poRAT->GetValueAsInt(nIRow, nIRed),
2144 10 : m_poRAT->GetValueAsInt(nIRow, nIGreen),
2145 10 : m_poRAT->GetValueAsInt(nIRow, nIBlue)))
2146 : {
2147 0 : MM_ReleaseDBFHeader(&pBD_XP);
2148 0 : return 1;
2149 : }
2150 : }
2151 : }
2152 :
2153 : // Nodata color
2154 2 : if (!VSIFPrintfL(pBD_XP->pfDataBase, " "))
2155 : {
2156 0 : MM_ReleaseDBFHeader(&pBD_XP);
2157 0 : return 1;
2158 : }
2159 2 : if (!VSIFPrintfL(pBD_XP->pfDataBase, "%*s",
2160 : static_cast<int>(nClauSimbolNBytes), ""))
2161 : {
2162 0 : MM_ReleaseDBFHeader(&pBD_XP);
2163 0 : return 1;
2164 : }
2165 2 : if (!VSIFPrintfL(pBD_XP->pfDataBase, "%3d%3d%3d", -1, -1, -1))
2166 : {
2167 0 : MM_ReleaseDBFHeader(&pBD_XP);
2168 0 : return 1;
2169 : }
2170 2 : nIRow++;
2171 :
2172 2 : if (nIRow != static_cast<int>(pBD_XP->nRecords))
2173 : {
2174 2 : pBD_XP->nRecords = nIRow;
2175 2 : MM_WriteNRecordsMMBD_XPFile(pBD_XP);
2176 : }
2177 :
2178 2 : fclose_and_nullify(&pBD_XP->pfDataBase);
2179 2 : MM_ReleaseDBFHeader(&pBD_XP);
2180 2 : return 0;
2181 : }
2182 :
2183 : // Writes DBF and REL attribute table in MiraMon format
2184 162 : int MMRBand::WriteAttributeTable(GDALDataset &oSrcDS)
2185 : {
2186 162 : GDALRasterBand *pRasterBand = oSrcDS.GetRasterBand(m_nIBand + 1);
2187 162 : if (!pRasterBand)
2188 0 : return 0;
2189 :
2190 162 : m_poRAT = pRasterBand->GetDefaultRAT();
2191 162 : if (!m_poRAT)
2192 136 : return 0;
2193 :
2194 26 : if (!m_poRAT->GetColumnCount())
2195 0 : return 0;
2196 :
2197 26 : if (!m_poRAT->GetRowCount())
2198 0 : return 0;
2199 :
2200 : // Getting the Value column
2201 : int nIField;
2202 26 : m_osValue = "";
2203 26 : int nIValue = m_poRAT->GetColOfUsage(GFU_MinMax);
2204 : // If the RAT has no value type, MiraMon can't handle that
2205 : // as a RAT
2206 26 : if (nIValue == -1)
2207 : {
2208 17 : nIValue = m_poRAT->GetColOfUsage(GFU_Min);
2209 17 : if (nIValue == -1)
2210 : {
2211 8 : GDALRATFieldType nType = m_poRAT->GetTypeOfCol(0);
2212 8 : if (nType == GFT_Integer)
2213 8 : nIValue = 0;
2214 : else
2215 0 : return 1;
2216 : }
2217 : }
2218 :
2219 26 : m_osValue = m_poRAT->GetNameOfCol(nIValue);
2220 :
2221 : // Creating DBF table name
2222 26 : if (!cpl::ends_with(m_osBandFileName, pszExtRaster))
2223 0 : return 1;
2224 :
2225 : // Extract .img and create DBF file name
2226 26 : m_osRATDBFName = m_osBandFileName;
2227 26 : m_osRATDBFName.resize(m_osRATDBFName.size() - strlen(".img"));
2228 26 : m_osRATDBFName.append("_RAT.dbf");
2229 :
2230 : // Extract .img and create REL file name
2231 26 : m_osRATRELName = m_osBandFileName;
2232 26 : m_osRATRELName.resize(m_osRATRELName.size() - strlen(".img"));
2233 26 : m_osRATRELName.append("_RAT.rel");
2234 :
2235 : // Creating DBF
2236 26 : int nFields = m_poRAT->GetColumnCount();
2237 : struct MM_DATA_BASE_XP *pBD_XP =
2238 26 : MM_CreateDBFHeader(nFields, MM_JOC_CARAC_UTF8_DBF);
2239 26 : if (!pBD_XP)
2240 0 : return 1;
2241 :
2242 : // Creating a simple REL that allows to MiraMon user to
2243 : // document this RAT in case of need.
2244 52 : auto pRATRel = std::make_unique<MMRRel>(m_osRATRELName);
2245 26 : if (!pRATRel->OpenRELFile("wb"))
2246 : {
2247 0 : MM_ReleaseDBFHeader(&pBD_XP);
2248 0 : return 1;
2249 : }
2250 :
2251 26 : pRATRel->AddSectionStart(SECTION_VERSIO);
2252 26 : pRATRel->AddKeyValue(KEY_Vers, "4");
2253 26 : pRATRel->AddKeyValue(KEY_SubVers, "3");
2254 26 : pRATRel->AddSectionEnd();
2255 :
2256 26 : pRATRel->AddSectionStart(SECTION_TAULA_PRINCIPAL);
2257 : // Get path relative to REL file
2258 26 : pRATRel->AddKeyValue(KEY_NomFitxer, CPLGetFilename(m_osRATDBFName));
2259 26 : pRATRel->AddKeyValue(KEY_TipusRelacio, "RELACIO_N_1");
2260 26 : pRATRel->AddKeyValue("AssociatRel", m_osValue);
2261 26 : pRATRel->AddSectionEnd();
2262 :
2263 : // Assigning DBF table name
2264 26 : CPLStrlcpy(pBD_XP->szFileName, m_osRATDBFName, sizeof(pBD_XP->szFileName));
2265 :
2266 : // Initializing the table
2267 : MM_EXT_DBF_N_RECORDS nRecords =
2268 26 : static_cast<MM_EXT_DBF_N_RECORDS>(m_poRAT->GetRowCount());
2269 26 : pBD_XP->nFields = nFields;
2270 26 : pBD_XP->nRecords = nRecords;
2271 26 : pBD_XP->FirstRecordOffset =
2272 26 : static_cast<MM_FIRST_RECORD_OFFSET_TYPE>(33 + (pBD_XP->nFields * 32));
2273 26 : pBD_XP->CharSet = MM_JOC_CARAC_ANSI_DBASE;
2274 26 : pBD_XP->dbf_version = MM_MARCA_DBASE4;
2275 26 : pBD_XP->BytesPerRecord = 1;
2276 :
2277 : // Fields of the DBF table
2278 : struct MM_FIELD MMField;
2279 109 : for (nIField = 0; nIField < m_poRAT->GetColumnCount(); nIField++)
2280 : {
2281 : // DBF part
2282 83 : MM_InitializeField(&MMField);
2283 83 : CPLStrlcpy(MMField.FieldName, m_poRAT->GetNameOfCol(nIField),
2284 : sizeof(MMField.FieldName));
2285 :
2286 83 : MMField.BytesPerField = 0;
2287 581 : for (int nIRow = 0; nIRow < m_poRAT->GetRowCount(); nIRow++)
2288 : {
2289 498 : if (m_poRAT->GetTypeOfCol(nIField) == GFT_DateTime)
2290 : {
2291 0 : MMField.FieldType = 'D';
2292 0 : MMField.DecimalsIfFloat = 0;
2293 0 : MMField.BytesPerField = MM_MAX_AMPLADA_CAMP_D_DBF;
2294 0 : break;
2295 : }
2296 498 : else if (m_poRAT->GetTypeOfCol(nIField) == GFT_Boolean)
2297 : {
2298 0 : MMField.FieldType = 'L';
2299 0 : MMField.DecimalsIfFloat = 0;
2300 0 : MMField.BytesPerField = 1;
2301 0 : break;
2302 : }
2303 : else
2304 : {
2305 852 : if (m_poRAT->GetTypeOfCol(nIField) == GFT_String ||
2306 354 : m_poRAT->GetTypeOfCol(nIField) == GFT_WKBGeometry)
2307 : {
2308 144 : MMField.FieldType = 'C';
2309 144 : MMField.DecimalsIfFloat = 0;
2310 : }
2311 : else
2312 : {
2313 354 : MMField.FieldType = 'N';
2314 354 : if (m_poRAT->GetTypeOfCol(nIField) == GFT_Real)
2315 : {
2316 144 : MMField.BytesPerField = 20;
2317 144 : MMField.DecimalsIfFloat = MAX_RELIABLE_SF_DOUBLE;
2318 : }
2319 : else
2320 210 : MMField.DecimalsIfFloat = 0;
2321 : }
2322 : char *pszString =
2323 498 : CPLRecode(m_poRAT->GetValueAsString(nIRow, nIField),
2324 : CPL_ENC_UTF8, "CP1252");
2325 :
2326 498 : if (strlen(pszString) > MMField.BytesPerField)
2327 67 : MMField.BytesPerField =
2328 : static_cast<MM_BYTES_PER_FIELD_TYPE_DBF>(
2329 67 : strlen(pszString));
2330 :
2331 498 : CPLFree(pszString);
2332 : }
2333 : }
2334 83 : MM_DuplicateFieldDBXP(pBD_XP->pField + nIField, &MMField);
2335 :
2336 : // REL part
2337 166 : CPLString osSection = SECTION_TAULA_PRINCIPAL;
2338 83 : osSection.append(":");
2339 83 : osSection.append(MMField.FieldName);
2340 83 : pRATRel->AddSectionStart(osSection);
2341 :
2342 83 : if (EQUAL(MMField.FieldName, m_osValue))
2343 : {
2344 26 : pRATRel->AddKeyValue("visible", "0");
2345 26 : pRATRel->AddKeyValue(KEY_TractamentVariable, "Categoric");
2346 : }
2347 83 : pRATRel->AddKeyValue(KEY_descriptor, "");
2348 83 : pRATRel->AddSectionEnd();
2349 : }
2350 :
2351 : // Closing the REL
2352 26 : pRATRel->CloseRELFile();
2353 :
2354 : // Opening the table
2355 26 : if (!MM_CreateAndOpenDBFFile(pBD_XP, pBD_XP->szFileName))
2356 : {
2357 0 : MM_ReleaseDBFHeader(&pBD_XP);
2358 0 : return 1;
2359 : }
2360 :
2361 : // Writing records to the table
2362 26 : if (0 != VSIFSeekL(pBD_XP->pfDataBase,
2363 26 : static_cast<vsi_l_offset>(pBD_XP->FirstRecordOffset),
2364 : SEEK_SET))
2365 : {
2366 0 : MM_ReleaseDBFHeader(&pBD_XP);
2367 0 : return 1;
2368 : }
2369 :
2370 182 : for (int nIRow = 0; nIRow < static_cast<int>(pBD_XP->nRecords); nIRow++)
2371 : {
2372 : // Deletion flag
2373 156 : if (!VSIFPrintfL(pBD_XP->pfDataBase, " "))
2374 : {
2375 0 : MM_ReleaseDBFHeader(&pBD_XP);
2376 0 : return 1;
2377 : }
2378 654 : for (nIField = 0; nIField < static_cast<int>(pBD_XP->nFields);
2379 : nIField++)
2380 : {
2381 498 : if (m_poRAT->GetTypeOfCol(nIRow) == GFT_DateTime)
2382 : {
2383 : char szDate[15];
2384 : const GDALRATDateTime osDT =
2385 0 : m_poRAT->GetValueAsDateTime(nIRow, nIField);
2386 0 : if (osDT.nYear >= 0)
2387 0 : snprintf(szDate, sizeof(szDate), "%04d%02d%02d", osDT.nYear,
2388 0 : osDT.nMonth, osDT.nDay);
2389 : else
2390 0 : snprintf(szDate, sizeof(szDate), "%04d%02d%02d", 0, 0, 0);
2391 :
2392 0 : if (pBD_XP->pField[nIField].BytesPerField !=
2393 0 : VSIFWriteL(szDate, 1, pBD_XP->pField[nIField].BytesPerField,
2394 : pBD_XP->pfDataBase))
2395 : {
2396 0 : MM_ReleaseDBFHeader(&pBD_XP);
2397 0 : return 1;
2398 : }
2399 : }
2400 498 : else if (m_poRAT->GetTypeOfCol(nIRow) == GFT_Boolean)
2401 : {
2402 0 : if (pBD_XP->pField[nIField].BytesPerField !=
2403 0 : VSIFWriteL(m_poRAT->GetValueAsBoolean(nIRow, nIField) ? "T"
2404 : : "F",
2405 0 : 1, pBD_XP->pField[nIField].BytesPerField,
2406 : pBD_XP->pfDataBase))
2407 : {
2408 0 : MM_ReleaseDBFHeader(&pBD_XP);
2409 0 : return 1;
2410 : }
2411 : }
2412 498 : else if (m_poRAT->GetTypeOfCol(nIRow) == GFT_WKBGeometry)
2413 : {
2414 0 : if (pBD_XP->pField[nIField].BytesPerField !=
2415 0 : VSIFWriteL(m_poRAT->GetValueAsString(nIRow, nIField), 1,
2416 0 : pBD_XP->pField[nIField].BytesPerField,
2417 : pBD_XP->pfDataBase))
2418 : {
2419 0 : MM_ReleaseDBFHeader(&pBD_XP);
2420 0 : return 1;
2421 : }
2422 : }
2423 : else
2424 : {
2425 : char *pszString =
2426 498 : CPLRecode(m_poRAT->GetValueAsString(nIRow, nIField),
2427 : CPL_ENC_UTF8, "CP1252");
2428 :
2429 996 : if (pBD_XP->pField[nIField].BytesPerField !=
2430 498 : VSIFWriteL(pszString, 1,
2431 498 : pBD_XP->pField[nIField].BytesPerField,
2432 : pBD_XP->pfDataBase))
2433 : {
2434 0 : MM_ReleaseDBFHeader(&pBD_XP);
2435 0 : return 1;
2436 : }
2437 498 : CPLFree(pszString);
2438 : }
2439 : }
2440 : }
2441 :
2442 26 : fclose_and_nullify(&pBD_XP->pfDataBase);
2443 26 : MM_ReleaseDBFHeader(&pBD_XP);
2444 26 : return 0;
2445 : }
|