Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Raster Matrix Format
4 : * Purpose: Read/write raster files used in GIS "Integratsia"
5 : * (also known as "Panorama" GIS).
6 : * Author: Andrey Kiselev, dron@ak4719.spb.edu
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2005, Andrey Kiselev <dron@ak4719.spb.edu>
10 : * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com>
11 : * Copyright (c) 2023, NextGIS <info@nextgis.com>
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 : #include <algorithm>
16 : #include <array>
17 : #include <limits>
18 :
19 : #include "cpl_string.h"
20 : #include "gdal_frmts.h"
21 : #include "ogr_spatialref.h"
22 :
23 : #include "rmfdataset.h"
24 :
25 : #include "cpl_safemaths.hpp"
26 :
27 : constexpr int RMF_DEFAULT_BLOCKXSIZE = 256;
28 : constexpr int RMF_DEFAULT_BLOCKYSIZE = 256;
29 :
30 : static const char RMF_SigRSW[] = {'R', 'S', 'W', '\0'};
31 : static const char RMF_SigRSW_BE[] = {'\0', 'W', 'S', 'R'};
32 : static const char RMF_SigMTW[] = {'M', 'T', 'W', '\0'};
33 :
34 : static const char RMF_UnitsEmpty[] = "";
35 : static const char RMF_UnitsM[] = "m";
36 : static const char RMF_UnitsCM[] = "cm";
37 : static const char RMF_UnitsDM[] = "dm";
38 : static const char RMF_UnitsMM[] = "mm";
39 :
40 : constexpr double RMF_DEFAULT_SCALE = 10000.0;
41 : constexpr double RMF_DEFAULT_RESOLUTION = 100.0;
42 :
43 : constexpr const char *MD_VERSION_KEY = "VERSION";
44 : constexpr const char *MD_NAME_KEY = "NAME";
45 : constexpr const char *MD_SCALE_KEY = "SCALE";
46 : constexpr const char *MD_FRAME_KEY = "FRAME";
47 :
48 : constexpr const char *MD_MATH_BASE_MAP_TYPE_KEY = "MATH_BASE.Map type";
49 : constexpr const char *MD_MATH_BASE_PROJECTION_KEY = "MATH_BASE.Projection";
50 :
51 : constexpr int nMaxFramePointCount = 2048;
52 : constexpr GInt32 nPolygonType =
53 : 2147385342; // 2147385342 magic number for polygon
54 :
55 : /* -------------------------------------------------------------------- */
56 : /* Note: Due to the fact that in the early versions of RMF */
57 : /* format the field of the iEPSGCode was marked as a 'reserved', */
58 : /* in the header on its place in many cases garbage values were written.*/
59 : /* Most of them can be weeded out by the minimum EPSG code value. */
60 : /* */
61 : /* see: Surveying and Positioning Guidance Note Number 7, part 1 */
62 : /* Using the EPSG Geodetic Parameter Dataset p. 22 */
63 : /* http://www.epsg.org/Portals/0/373-07-1.pdf */
64 : /* -------------------------------------------------------------------- */
65 : constexpr GInt32 RMF_EPSG_MIN_CODE = 1024;
66 :
67 74 : static char *RMFUnitTypeToStr(GUInt32 iElevationUnit)
68 : {
69 74 : switch (iElevationUnit)
70 : {
71 68 : case 0:
72 68 : return CPLStrdup(RMF_UnitsM);
73 0 : case 1:
74 0 : return CPLStrdup(RMF_UnitsDM);
75 1 : case 2:
76 1 : return CPLStrdup(RMF_UnitsCM);
77 5 : case 3:
78 5 : return CPLStrdup(RMF_UnitsMM);
79 0 : default:
80 0 : return CPLStrdup(RMF_UnitsEmpty);
81 : }
82 : }
83 :
84 80 : static GUInt32 RMFStrToUnitType(const char *pszUnit, int *pbSuccess = nullptr)
85 : {
86 80 : if (pbSuccess != nullptr)
87 : {
88 3 : *pbSuccess = TRUE;
89 : }
90 80 : if (EQUAL(pszUnit, RMF_UnitsM))
91 0 : return 0;
92 80 : else if (EQUAL(pszUnit, RMF_UnitsDM))
93 0 : return 1;
94 80 : else if (EQUAL(pszUnit, RMF_UnitsCM))
95 1 : return 2;
96 79 : else if (EQUAL(pszUnit, RMF_UnitsMM))
97 1 : return 3;
98 : else
99 : {
100 : // There is no 'invalid unit' in RMF format. So meter is default...
101 78 : if (pbSuccess != nullptr)
102 : {
103 1 : *pbSuccess = FALSE;
104 : }
105 78 : return 0;
106 : }
107 : }
108 :
109 : /************************************************************************/
110 : /* ==================================================================== */
111 : /* RMFRasterBand */
112 : /* ==================================================================== */
113 : /************************************************************************/
114 :
115 : /************************************************************************/
116 : /* RMFRasterBand() */
117 : /************************************************************************/
118 :
119 419 : RMFRasterBand::RMFRasterBand(RMFDataset *poDSIn, int nBandIn,
120 419 : GDALDataType eType)
121 838 : : nLastTileWidth(poDSIn->GetRasterXSize() % poDSIn->sHeader.nTileWidth),
122 838 : nLastTileHeight(poDSIn->GetRasterYSize() % poDSIn->sHeader.nTileHeight),
123 419 : nDataSize(GDALGetDataTypeSizeBytes(eType))
124 : {
125 419 : poDS = poDSIn;
126 419 : nBand = nBandIn;
127 :
128 419 : eDataType = eType;
129 419 : nBlockXSize = poDSIn->sHeader.nTileWidth;
130 419 : nBlockYSize = poDSIn->sHeader.nTileHeight;
131 419 : nBlockSize = nBlockXSize * nBlockYSize;
132 419 : nBlockBytes = nBlockSize * nDataSize;
133 :
134 : #ifdef DEBUG
135 419 : CPLDebug("RMF",
136 : "Band %d: tile width is %d, tile height is %d, "
137 : " last tile width %u, last tile height %u, "
138 : "bytes per pixel is %d, data type size is %d",
139 : nBand, nBlockXSize, nBlockYSize, nLastTileWidth, nLastTileHeight,
140 419 : poDSIn->sHeader.nBitDepth / 8, nDataSize);
141 : #endif
142 419 : }
143 :
144 : /************************************************************************/
145 : /* ~RMFRasterBand() */
146 : /************************************************************************/
147 :
148 838 : RMFRasterBand::~RMFRasterBand()
149 : {
150 838 : }
151 :
152 : /************************************************************************/
153 : /* IReadBlock() */
154 : /************************************************************************/
155 :
156 454 : CPLErr RMFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
157 : {
158 454 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
159 :
160 454 : CPLAssert(poGDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
161 : pImage != nullptr);
162 :
163 454 : memset(pImage, 0, nBlockBytes);
164 :
165 454 : GUInt32 nRawXSize = nBlockXSize;
166 454 : GUInt32 nRawYSize = nBlockYSize;
167 :
168 454 : if (nLastTileWidth &&
169 381 : static_cast<GUInt32>(nBlockXOff) == poGDS->nXTiles - 1)
170 176 : nRawXSize = nLastTileWidth;
171 :
172 454 : if (nLastTileHeight &&
173 365 : static_cast<GUInt32>(nBlockYOff) == poGDS->nYTiles - 1)
174 198 : nRawYSize = nLastTileHeight;
175 :
176 454 : GUInt32 nRawBytes = nRawXSize * nRawYSize * poGDS->sHeader.nBitDepth / 8;
177 :
178 : // Direct read optimization
179 454 : if (poGDS->nBands == 1 && poGDS->sHeader.nBitDepth >= 8 &&
180 183 : nRawXSize == static_cast<GUInt32>(nBlockXSize) &&
181 71 : nRawYSize == static_cast<GUInt32>(nBlockYSize))
182 : {
183 67 : bool bNullTile = false;
184 67 : if (CE_None != poGDS->ReadTile(nBlockXOff, nBlockYOff,
185 : reinterpret_cast<GByte *>(pImage),
186 : nRawBytes, nRawXSize, nRawYSize,
187 : bNullTile))
188 : {
189 0 : CPLError(CE_Failure, CPLE_FileIO,
190 : "Failed to read tile xOff %d yOff %d", nBlockXOff,
191 : nBlockYOff);
192 0 : return CE_Failure;
193 : }
194 67 : if (bNullTile)
195 : {
196 : const int nChunkSize =
197 1 : std::max(1, GDALGetDataTypeSizeBytes(eDataType));
198 1 : const GPtrDiff_t nWords =
199 1 : static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
200 1 : GDALCopyWords64(&poGDS->sHeader.dfNoData, GDT_Float64, 0, pImage,
201 : eDataType, nChunkSize, nWords);
202 : }
203 67 : return CE_None;
204 : }
205 : #ifdef DEBUG
206 387 : CPLDebug("RMF", "IReadBlock nBand %d, RawSize [%d, %d], Bits %u", nBand,
207 : nRawXSize, nRawYSize, poGDS->sHeader.nBitDepth);
208 : #endif // DEBUG
209 387 : if (poGDS->pabyCurrentTile == nullptr ||
210 290 : poGDS->nCurrentTileXOff != nBlockXOff ||
211 102 : poGDS->nCurrentTileYOff != nBlockYOff ||
212 102 : poGDS->nCurrentTileBytes != nRawBytes)
213 : {
214 285 : if (poGDS->pabyCurrentTile == nullptr)
215 : {
216 97 : GUInt32 nMaxTileBytes = poGDS->sHeader.nTileWidth *
217 97 : poGDS->sHeader.nTileHeight *
218 97 : poGDS->sHeader.nBitDepth / 8;
219 97 : poGDS->pabyCurrentTile = reinterpret_cast<GByte *>(
220 97 : VSIMalloc(std::max(1U, nMaxTileBytes)));
221 97 : if (!poGDS->pabyCurrentTile)
222 : {
223 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
224 : "Can't allocate tile block of size %lu.\n%s",
225 : static_cast<unsigned long>(nMaxTileBytes),
226 0 : VSIStrerror(errno));
227 0 : poGDS->nCurrentTileBytes = 0;
228 0 : return CE_Failure;
229 : }
230 : }
231 :
232 285 : poGDS->nCurrentTileXOff = nBlockXOff;
233 285 : poGDS->nCurrentTileYOff = nBlockYOff;
234 285 : poGDS->nCurrentTileBytes = nRawBytes;
235 :
236 285 : if (CE_None != poGDS->ReadTile(nBlockXOff, nBlockYOff,
237 : poGDS->pabyCurrentTile, nRawBytes,
238 : nRawXSize, nRawYSize,
239 285 : poGDS->bCurrentTileIsNull))
240 : {
241 0 : CPLError(CE_Failure, CPLE_FileIO,
242 : "Failed to read tile xOff %d yOff %d", nBlockXOff,
243 : nBlockYOff);
244 0 : poGDS->nCurrentTileBytes = 0;
245 0 : return CE_Failure;
246 : }
247 : }
248 :
249 : /* -------------------------------------------------------------------- */
250 : /* Deinterleave pixels from input buffer. */
251 : /* -------------------------------------------------------------------- */
252 :
253 387 : if (poGDS->bCurrentTileIsNull)
254 : {
255 0 : const int nChunkSize = std::max(1, GDALGetDataTypeSizeBytes(eDataType));
256 0 : const GPtrDiff_t nWords =
257 0 : static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
258 0 : GDALCopyWords64(&poGDS->sHeader.dfNoData, GDT_Float64, 0, pImage,
259 : eDataType, nChunkSize, nWords);
260 0 : return CE_None;
261 : }
262 387 : else if ((poGDS->eRMFType == RMFT_RSW &&
263 283 : (poGDS->sHeader.nBitDepth == 8 ||
264 271 : poGDS->sHeader.nBitDepth == 24 ||
265 9 : poGDS->sHeader.nBitDepth == 32)) ||
266 107 : (poGDS->eRMFType == RMFT_MTW))
267 : {
268 384 : const size_t nTilePixelSize = poGDS->sHeader.nBitDepth / 8;
269 384 : const size_t nTileLineSize = nTilePixelSize * nRawXSize;
270 384 : const size_t nBlockLineSize =
271 384 : static_cast<size_t>(nDataSize) * nBlockXSize;
272 384 : int iDstBand = (poGDS->nBands - nBand);
273 50423 : for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
274 : {
275 : GByte *pabySrc;
276 : GByte *pabyDst;
277 50039 : pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize +
278 50039 : iDstBand * nDataSize;
279 50039 : pabyDst =
280 50039 : reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
281 50039 : GDALCopyWords(pabySrc, eDataType, static_cast<int>(nTilePixelSize),
282 50039 : pabyDst, eDataType, static_cast<int>(nDataSize),
283 : nRawXSize);
284 : }
285 384 : return CE_None;
286 : }
287 3 : else if (poGDS->eRMFType == RMFT_RSW && poGDS->sHeader.nBitDepth == 16 &&
288 0 : poGDS->nBands == 3)
289 : {
290 0 : const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
291 0 : const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
292 0 : const size_t nBlockLineSize =
293 0 : static_cast<size_t>(nDataSize) * nBlockXSize;
294 :
295 0 : for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
296 : {
297 : GUInt16 *pabySrc;
298 : GByte *pabyDst;
299 0 : pabySrc = reinterpret_cast<GUInt16 *>(poGDS->pabyCurrentTile +
300 0 : iLine * nTileLineSize);
301 0 : pabyDst =
302 0 : reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
303 :
304 0 : for (GUInt32 i = 0; i < nRawXSize; i++)
305 : {
306 0 : switch (nBand)
307 : {
308 0 : case 1:
309 0 : pabyDst[i] =
310 0 : static_cast<GByte>((pabySrc[i] & 0x7c00) >> 7);
311 0 : break;
312 0 : case 2:
313 0 : pabyDst[i] =
314 0 : static_cast<GByte>((pabySrc[i] & 0x03e0) >> 2);
315 0 : break;
316 0 : case 3:
317 0 : pabyDst[i] =
318 0 : static_cast<GByte>((pabySrc[i] & 0x1F) << 3);
319 0 : break;
320 0 : default:
321 0 : break;
322 : }
323 : }
324 : }
325 0 : return CE_None;
326 : }
327 3 : else if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1 &&
328 3 : poGDS->sHeader.nBitDepth == 4)
329 : {
330 2 : if (poGDS->nCurrentTileBytes != (nBlockSize + 1) / 2)
331 : {
332 0 : CPLError(CE_Failure, CPLE_AppDefined,
333 : "Tile has %d bytes, %d were expected",
334 0 : poGDS->nCurrentTileBytes, (nBlockSize + 1) / 2);
335 0 : return CE_Failure;
336 : }
337 :
338 2 : const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
339 2 : const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
340 2 : const size_t nBlockLineSize =
341 2 : static_cast<size_t>(nDataSize) * nBlockXSize;
342 :
343 464 : for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
344 : {
345 : GByte *pabySrc;
346 : GByte *pabyDst;
347 462 : pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize;
348 462 : pabyDst =
349 462 : reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
350 112266 : for (GUInt32 i = 0; i < nRawXSize; ++i)
351 : {
352 111804 : if (i & 0x01)
353 55902 : pabyDst[i] = (*pabySrc++ & 0xF0) >> 4;
354 : else
355 55902 : pabyDst[i] = *pabySrc & 0x0F;
356 : }
357 : }
358 2 : return CE_None;
359 : }
360 1 : else if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1 &&
361 1 : poGDS->sHeader.nBitDepth == 1)
362 : {
363 1 : if (poGDS->nCurrentTileBytes != (nBlockSize + 7) / 8)
364 : {
365 0 : CPLError(CE_Failure, CPLE_AppDefined,
366 : "Tile has %d bytes, %d were expected",
367 0 : poGDS->nCurrentTileBytes, (nBlockSize + 7) / 8);
368 0 : return CE_Failure;
369 : }
370 :
371 1 : const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
372 1 : const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
373 1 : const size_t nBlockLineSize =
374 1 : static_cast<size_t>(nDataSize) * nBlockXSize;
375 :
376 232 : for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
377 : {
378 : GByte *pabySrc;
379 : GByte *pabyDst;
380 231 : pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize;
381 231 : pabyDst =
382 231 : reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
383 :
384 57519 : for (GUInt32 i = 0; i < nRawXSize; ++i)
385 : {
386 57288 : switch (i & 0x7)
387 : {
388 7161 : case 0:
389 7161 : pabyDst[i] = (*pabySrc & 0x80) >> 7;
390 7161 : break;
391 7161 : case 1:
392 7161 : pabyDst[i] = (*pabySrc & 0x40) >> 6;
393 7161 : break;
394 7161 : case 2:
395 7161 : pabyDst[i] = (*pabySrc & 0x20) >> 5;
396 7161 : break;
397 7161 : case 3:
398 7161 : pabyDst[i] = (*pabySrc & 0x10) >> 4;
399 7161 : break;
400 7161 : case 4:
401 7161 : pabyDst[i] = (*pabySrc & 0x08) >> 3;
402 7161 : break;
403 7161 : case 5:
404 7161 : pabyDst[i] = (*pabySrc & 0x04) >> 2;
405 7161 : break;
406 7161 : case 6:
407 7161 : pabyDst[i] = (*pabySrc & 0x02) >> 1;
408 7161 : break;
409 7161 : case 7:
410 7161 : pabyDst[i] = *pabySrc++ & 0x01;
411 7161 : break;
412 0 : default:
413 0 : break;
414 : }
415 : }
416 : }
417 1 : return CE_None;
418 : }
419 :
420 0 : CPLError(CE_Failure, CPLE_AppDefined,
421 : "Invalid block data type. BitDepth %d, nBands %d",
422 0 : static_cast<int>(poGDS->sHeader.nBitDepth), poGDS->nBands);
423 :
424 0 : return CE_Failure;
425 : }
426 :
427 : /************************************************************************/
428 : /* IWriteBlock() */
429 : /************************************************************************/
430 :
431 454 : CPLErr RMFRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
432 : {
433 454 : CPLAssert(poDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
434 : pImage != nullptr);
435 :
436 454 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
437 :
438 : // First drop current tile read by IReadBlock
439 454 : poGDS->nCurrentTileBytes = 0;
440 :
441 454 : GUInt32 nRawXSize = nBlockXSize;
442 454 : GUInt32 nRawYSize = nBlockYSize;
443 :
444 454 : if (nLastTileWidth &&
445 426 : static_cast<GUInt32>(nBlockXOff) == poGDS->nXTiles - 1)
446 103 : nRawXSize = nLastTileWidth;
447 :
448 454 : if (nLastTileHeight &&
449 402 : static_cast<GUInt32>(nBlockYOff) == poGDS->nYTiles - 1)
450 136 : nRawYSize = nLastTileHeight;
451 :
452 454 : const size_t nTilePixelSize =
453 454 : static_cast<size_t>(nDataSize) * poGDS->nBands;
454 454 : const size_t nTileLineSize = nTilePixelSize * nRawXSize;
455 454 : const size_t nTileSize = nTileLineSize * nRawYSize;
456 454 : const size_t nBlockLineSize = static_cast<size_t>(nDataSize) * nBlockXSize;
457 :
458 : #ifdef DEBUG
459 454 : CPLDebug(
460 : "RMF",
461 : "IWriteBlock BlockSize [%d, %d], RawSize [%d, %d], size %d, nBand %d",
462 : nBlockXSize, nBlockYSize, nRawXSize, nRawYSize,
463 : static_cast<int>(nTileSize), nBand);
464 : #endif // DEBUG
465 :
466 454 : if (poGDS->nBands == 1 && nRawXSize == static_cast<GUInt32>(nBlockXSize) &&
467 24 : nRawYSize == static_cast<GUInt32>(nBlockYSize))
468 : { // Immediate write
469 46 : return poGDS->WriteTile(
470 : nBlockXOff, nBlockYOff, reinterpret_cast<GByte *>(pImage),
471 23 : static_cast<size_t>(nRawXSize) * nRawYSize * nDataSize, nRawXSize,
472 23 : nRawYSize);
473 : }
474 : else
475 : { // Try to construct full tile in memory and write later
476 431 : const GUInt32 nTile = nBlockYOff * poGDS->nXTiles + nBlockXOff;
477 :
478 : // Find tile
479 431 : auto poTile(poGDS->oUnfinishedTiles.find(nTile));
480 431 : if (poTile == poGDS->oUnfinishedTiles.end())
481 : {
482 167 : RMFTileData oTile;
483 167 : oTile.oData.resize(nTileSize);
484 : // If not found, but exist on disk than read it
485 167 : if (poGDS->paiTiles[2 * nTile + 1])
486 : {
487 : CPLErr eRes;
488 0 : bool bNullTile = false;
489 : eRes =
490 0 : poGDS->ReadTile(nBlockXOff, nBlockYOff, oTile.oData.data(),
491 : nTileSize, nRawXSize, nRawYSize, bNullTile);
492 0 : if (eRes != CE_None)
493 : {
494 0 : CPLError(CE_Failure, CPLE_FileIO,
495 : "Can't read block with offset [%d, %d]",
496 : nBlockXOff, nBlockYOff);
497 0 : return eRes;
498 : }
499 : }
500 : poTile = poGDS->oUnfinishedTiles.insert(
501 167 : poGDS->oUnfinishedTiles.end(), std::make_pair(nTile, oTile));
502 : }
503 :
504 431 : GByte *pabyTileData = poTile->second.oData.data();
505 :
506 : // Copy new data to a tile
507 431 : int iDstBand = (poGDS->nBands - nBand);
508 79381 : for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
509 : {
510 : const GByte *pabySrc;
511 : GByte *pabyDst;
512 78950 : pabySrc = reinterpret_cast<const GByte *>(pImage) +
513 78950 : iLine * nBlockLineSize;
514 78950 : pabyDst =
515 78950 : pabyTileData + iLine * nTileLineSize + iDstBand * nDataSize;
516 78950 : GDALCopyWords(pabySrc, eDataType, static_cast<int>(nDataSize),
517 : pabyDst, eDataType, static_cast<int>(nTilePixelSize),
518 : nRawXSize);
519 : }
520 431 : ++poTile->second.nBandsWritten;
521 :
522 : // Write to disk if tile is finished
523 431 : if (poTile->second.nBandsWritten == poGDS->nBands)
524 : {
525 167 : poGDS->WriteTile(nBlockXOff, nBlockYOff, pabyTileData, nTileSize,
526 : nRawXSize, nRawYSize);
527 167 : poGDS->oUnfinishedTiles.erase(poTile);
528 : }
529 : #ifdef DEBUG
530 431 : CPLDebug("RMF", "poGDS->oUnfinishedTiles.size() %d",
531 431 : static_cast<int>(poGDS->oUnfinishedTiles.size()));
532 : #endif // DEBUG
533 : }
534 :
535 431 : return CE_None;
536 : }
537 :
538 : /************************************************************************/
539 : /* GetNoDataValue() */
540 : /************************************************************************/
541 :
542 191 : double RMFRasterBand::GetNoDataValue(int *pbSuccess)
543 :
544 : {
545 191 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
546 :
547 191 : if (pbSuccess)
548 164 : *pbSuccess = TRUE;
549 :
550 191 : return poGDS->sHeader.dfNoData;
551 : }
552 :
553 27 : CPLErr RMFRasterBand::SetNoDataValue(double dfNoData)
554 : {
555 27 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
556 :
557 27 : poGDS->sHeader.dfNoData = dfNoData;
558 27 : poGDS->bHeaderDirty = true;
559 :
560 27 : return CE_None;
561 : }
562 :
563 : /************************************************************************/
564 : /* GetUnitType() */
565 : /************************************************************************/
566 :
567 10 : const char *RMFRasterBand::GetUnitType()
568 :
569 : {
570 10 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
571 :
572 10 : return poGDS->pszUnitType;
573 : }
574 :
575 : /************************************************************************/
576 : /* SetUnitType() */
577 : /************************************************************************/
578 :
579 3 : CPLErr RMFRasterBand::SetUnitType(const char *pszNewValue)
580 :
581 : {
582 3 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
583 3 : int bSuccess = FALSE;
584 3 : int iNewUnit = RMFStrToUnitType(pszNewValue, &bSuccess);
585 :
586 3 : if (bSuccess)
587 : {
588 2 : CPLFree(poGDS->pszUnitType);
589 2 : poGDS->pszUnitType = CPLStrdup(pszNewValue);
590 2 : poGDS->sHeader.iElevationUnit = iNewUnit;
591 2 : poGDS->bHeaderDirty = true;
592 2 : return CE_None;
593 : }
594 : else
595 : {
596 1 : CPLError(CE_Warning, CPLE_NotSupported,
597 : "RMF driver does not support '%s' elevation units. "
598 : "Possible values are: m, dm, cm, mm.",
599 : pszNewValue);
600 1 : return CE_Failure;
601 : }
602 : }
603 :
604 : /************************************************************************/
605 : /* GetColorTable() */
606 : /************************************************************************/
607 :
608 28 : GDALColorTable *RMFRasterBand::GetColorTable()
609 : {
610 28 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
611 :
612 28 : return poGDS->poColorTable;
613 : }
614 :
615 : /************************************************************************/
616 : /* SetColorTable() */
617 : /************************************************************************/
618 :
619 8 : CPLErr RMFRasterBand::SetColorTable(GDALColorTable *poColorTable)
620 : {
621 8 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
622 :
623 8 : if (poColorTable)
624 : {
625 8 : if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1)
626 : {
627 8 : if (!poGDS->pabyColorTable)
628 0 : return CE_Failure;
629 :
630 : GDALColorEntry oEntry;
631 2056 : for (GUInt32 i = 0; i < poGDS->nColorTableSize; i++)
632 : {
633 2048 : poColorTable->GetColorEntryAsRGB(i, &oEntry);
634 : // Red
635 2048 : poGDS->pabyColorTable[i * 4 + 0] =
636 2048 : static_cast<GByte>(oEntry.c1);
637 : // Green
638 2048 : poGDS->pabyColorTable[i * 4 + 1] =
639 2048 : static_cast<GByte>(oEntry.c2);
640 : // Blue
641 2048 : poGDS->pabyColorTable[i * 4 + 2] =
642 2048 : static_cast<GByte>(oEntry.c3);
643 2048 : poGDS->pabyColorTable[i * 4 + 3] = 0;
644 : }
645 :
646 8 : poGDS->bHeaderDirty = true;
647 : }
648 8 : return CE_None;
649 : }
650 :
651 0 : return CE_Failure;
652 : }
653 :
654 59 : int RMFRasterBand::GetOverviewCount()
655 : {
656 59 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
657 59 : if (poGDS->poOvrDatasets.empty())
658 0 : return GDALRasterBand::GetOverviewCount();
659 : else
660 59 : return static_cast<int>(poGDS->poOvrDatasets.size());
661 : }
662 :
663 79 : GDALRasterBand *RMFRasterBand::GetOverview(int i)
664 : {
665 79 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
666 79 : size_t n = static_cast<size_t>(i);
667 79 : if (poGDS->poOvrDatasets.empty())
668 0 : return GDALRasterBand::GetOverview(i);
669 : else
670 79 : return poGDS->poOvrDatasets[n]->GetRasterBand(nBand);
671 : }
672 :
673 694 : CPLErr RMFRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
674 : int nXSize, int nYSize, void *pData,
675 : int nBufXSize, int nBufYSize,
676 : GDALDataType eType, GSpacing nPixelSpace,
677 : GSpacing nLineSpace,
678 : GDALRasterIOExtraArg *psExtraArg)
679 : {
680 694 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
681 :
682 743 : if (eRWFlag == GF_Read && poGDS->poCompressData != nullptr &&
683 49 : poGDS->poCompressData->oThreadPool.GetThreadCount() > 0)
684 : {
685 9 : poGDS->poCompressData->oThreadPool.WaitCompletion();
686 : }
687 :
688 694 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
689 : pData, nBufXSize, nBufYSize, eType,
690 694 : nPixelSpace, nLineSpace, psExtraArg);
691 : }
692 :
693 : /************************************************************************/
694 : /* GetColorInterpretation() */
695 : /************************************************************************/
696 :
697 104 : GDALColorInterp RMFRasterBand::GetColorInterpretation()
698 : {
699 104 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
700 :
701 104 : if (poGDS->nBands == 3)
702 : {
703 53 : if (nBand == 1)
704 18 : return GCI_RedBand;
705 35 : else if (nBand == 2)
706 17 : return GCI_GreenBand;
707 18 : else if (nBand == 3)
708 18 : return GCI_BlueBand;
709 :
710 0 : return GCI_Undefined;
711 : }
712 :
713 51 : if (poGDS->eRMFType == RMFT_RSW)
714 28 : return GCI_PaletteIndex;
715 :
716 23 : return GCI_Undefined;
717 : }
718 :
719 : /************************************************************************/
720 : /* ==================================================================== */
721 : /* RMFDataset */
722 : /* ==================================================================== */
723 : /************************************************************************/
724 :
725 : /************************************************************************/
726 : /* RMFDataset() */
727 : /************************************************************************/
728 :
729 297 : RMFDataset::RMFDataset() : pszUnitType(CPLStrdup(RMF_UnitsEmpty))
730 : {
731 297 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
732 297 : nBands = 0;
733 297 : memset(&sHeader, 0, sizeof(sHeader));
734 297 : memset(&sExtHeader, 0, sizeof(sExtHeader));
735 297 : }
736 :
737 : /************************************************************************/
738 : /* ~RMFDataset() */
739 : /************************************************************************/
740 :
741 594 : RMFDataset::~RMFDataset()
742 : {
743 297 : RMFDataset::FlushCache(true);
744 404 : for (size_t n = 0; n != poOvrDatasets.size(); ++n)
745 : {
746 107 : poOvrDatasets[n]->RMFDataset::FlushCache(true);
747 : }
748 :
749 297 : VSIFree(paiTiles);
750 297 : VSIFree(pabyDecompressBuffer);
751 297 : VSIFree(pabyCurrentTile);
752 297 : CPLFree(pszUnitType);
753 297 : CPLFree(pabyColorTable);
754 297 : if (poColorTable != nullptr)
755 68 : delete poColorTable;
756 :
757 404 : for (size_t n = 0; n != poOvrDatasets.size(); ++n)
758 : {
759 107 : GDALClose(poOvrDatasets[n]);
760 : }
761 :
762 297 : if (fp != nullptr && poParentDS == nullptr)
763 : {
764 177 : VSIFCloseL(fp);
765 : }
766 594 : }
767 :
768 : /************************************************************************/
769 : /* GetGeoTransform() */
770 : /************************************************************************/
771 :
772 47 : CPLErr RMFDataset::GetGeoTransform(GDALGeoTransform >) const
773 : {
774 47 : gt = m_gt;
775 :
776 47 : if (sHeader.iGeorefFlag)
777 41 : return CE_None;
778 :
779 6 : return CE_Failure;
780 : }
781 :
782 : /************************************************************************/
783 : /* SetGeoTransform() */
784 : /************************************************************************/
785 :
786 41 : CPLErr RMFDataset::SetGeoTransform(const GDALGeoTransform >)
787 : {
788 41 : m_gt = gt;
789 41 : sHeader.dfPixelSize = m_gt[1];
790 41 : if (sHeader.dfPixelSize != 0.0)
791 41 : sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
792 41 : sHeader.dfLLX = m_gt[0];
793 41 : sHeader.dfLLY = m_gt[3] - nRasterYSize * sHeader.dfPixelSize;
794 41 : sHeader.iGeorefFlag = 1;
795 :
796 41 : bHeaderDirty = true;
797 :
798 41 : return CE_None;
799 : }
800 :
801 : /************************************************************************/
802 : /* GetSpatialRef() */
803 : /************************************************************************/
804 :
805 34 : const OGRSpatialReference *RMFDataset::GetSpatialRef() const
806 :
807 : {
808 34 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
809 : }
810 :
811 : /************************************************************************/
812 : /* SetSpatialRef() */
813 : /************************************************************************/
814 :
815 41 : CPLErr RMFDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
816 :
817 : {
818 41 : m_oSRS.Clear();
819 41 : if (poSRS)
820 41 : m_oSRS = *poSRS;
821 :
822 41 : bHeaderDirty = true;
823 :
824 41 : return CE_None;
825 : }
826 :
827 : /************************************************************************/
828 : /* WriteHeader() */
829 : /************************************************************************/
830 :
831 200 : CPLErr RMFDataset::WriteHeader()
832 : {
833 : /* -------------------------------------------------------------------- */
834 : /* Setup projection. */
835 : /* -------------------------------------------------------------------- */
836 200 : if (!m_oSRS.IsEmpty())
837 : {
838 52 : long iProjection = 0;
839 52 : long iDatum = 0;
840 52 : long iEllips = 0;
841 52 : long iZone = 0;
842 52 : int iVertCS = 0;
843 52 : double adfPrjParams[7] = {};
844 :
845 52 : m_oSRS.exportToPanorama(&iProjection, &iDatum, &iEllips, &iZone,
846 : adfPrjParams);
847 52 : m_oSRS.exportVertCSToPanorama(&iVertCS);
848 52 : sHeader.iProjection = static_cast<GInt32>(iProjection);
849 52 : sHeader.dfStdP1 = adfPrjParams[0];
850 52 : sHeader.dfStdP2 = adfPrjParams[1];
851 52 : sHeader.dfCenterLat = adfPrjParams[2];
852 52 : sHeader.dfCenterLong = adfPrjParams[3];
853 52 : if (m_oSRS.GetAuthorityName(nullptr) != nullptr &&
854 62 : m_oSRS.GetAuthorityCode(nullptr) != nullptr &&
855 10 : EQUAL(m_oSRS.GetAuthorityName(nullptr), "EPSG"))
856 : {
857 10 : sHeader.iEPSGCode = atoi(m_oSRS.GetAuthorityCode(nullptr));
858 : }
859 :
860 52 : sExtHeader.nEllipsoid = static_cast<GInt32>(iEllips);
861 52 : sExtHeader.nDatum = static_cast<GInt32>(iDatum);
862 52 : sExtHeader.nZone = static_cast<GInt32>(iZone);
863 52 : sExtHeader.nVertDatum = static_cast<GInt32>(iVertCS);
864 :
865 : // Set map type
866 52 : auto pszMapType = GetMetadataItem(MD_MATH_BASE_MAP_TYPE_KEY);
867 52 : if (pszMapType != nullptr)
868 : {
869 31 : sHeader.iMapType = static_cast<GInt32>(atoi(pszMapType));
870 : }
871 : }
872 :
873 : #define RMF_WRITE_LONG(ptr, value, offset) \
874 : do \
875 : { \
876 : GInt32 iLong = CPL_LSBWORD32(value); \
877 : memcpy((ptr) + (offset), &iLong, 4); \
878 : } while (false);
879 :
880 : #define RMF_WRITE_ULONG(ptr, value, offset) \
881 : do \
882 : { \
883 : GUInt32 iULong = CPL_LSBWORD32(value); \
884 : memcpy((ptr) + (offset), &iULong, 4); \
885 : } while (false);
886 :
887 : #define RMF_WRITE_DOUBLE(ptr, value, offset) \
888 : do \
889 : { \
890 : double dfDouble = (value); \
891 : CPL_LSBPTR64(&dfDouble); \
892 : memcpy((ptr) + (offset), &dfDouble, 8); \
893 : } while (false);
894 :
895 : // Frame if present
896 400 : std::vector<RSWFrameCoord> astFrameCoords;
897 200 : auto pszFrameWKT = GetMetadataItem(MD_FRAME_KEY);
898 200 : if (pszFrameWKT != nullptr)
899 : {
900 2 : CPLDebug("RMF", "Write to header frame: %s", pszFrameWKT);
901 2 : OGRGeometry *poFrameGeom = nullptr;
902 2 : if (OGRGeometryFactory::createFromWkt(pszFrameWKT, nullptr,
903 2 : &poFrameGeom) == OGRERR_NONE)
904 : {
905 2 : if (poFrameGeom->getGeometryType() == wkbPolygon)
906 : {
907 2 : GDALGeoTransform reverseGT;
908 2 : if (m_gt.GetInverse(reverseGT))
909 : {
910 2 : OGRPolygon *poFramePoly = poFrameGeom->toPolygon();
911 2 : if (!poFramePoly->IsEmpty())
912 : {
913 : OGRLinearRing *poFrameRing =
914 2 : poFramePoly->getExteriorRing();
915 12 : for (int i = 0; i < poFrameRing->getNumPoints(); i++)
916 : {
917 : int nX =
918 10 : int(reverseGT[0] +
919 10 : poFrameRing->getX(i) * reverseGT[1] - 0.5);
920 : int nY =
921 10 : int(reverseGT[3] +
922 10 : poFrameRing->getY(i) * reverseGT[5] - 0.5);
923 :
924 10 : CPLDebug("RMF", "X: %d, Y: %d", nX, nY);
925 :
926 10 : astFrameCoords.push_back({nX, nY});
927 : }
928 : }
929 :
930 4 : if (astFrameCoords.empty() ||
931 2 : astFrameCoords.size() > nMaxFramePointCount)
932 : {
933 : // CPLError(CE_Warning, CPLE_AppDefined, "Invalid frame WKT: %s", pszFrameWKT);
934 0 : CPLDebug("RMF", "Write to header frame failed: no "
935 : "points or too many");
936 0 : astFrameCoords.clear();
937 : }
938 : else
939 : {
940 2 : sHeader.nROISize = static_cast<GUInt32>(
941 2 : sizeof(RSWFrame) +
942 : sizeof(RSWFrameCoord) *
943 : astFrameCoords
944 2 : .size()); // Set real size and real point count
945 2 : sHeader.iFrameFlag = 0;
946 : }
947 : }
948 : else
949 : {
950 0 : CPLDebug("RMF", "Write to header frame failed: "
951 : "GDALInvGeoTransform == FALSE");
952 : }
953 : }
954 2 : OGRGeometryFactory::destroyGeometry(poFrameGeom);
955 : }
956 : else
957 : {
958 0 : CPLDebug("RMF", "Write to header frame failed: "
959 : "OGRGeometryFactory::createFromWkt error");
960 : }
961 : }
962 :
963 200 : vsi_l_offset iCurrentFileSize(GetLastOffset());
964 200 : sHeader.nFileSize0 = GetRMFOffset(iCurrentFileSize, &iCurrentFileSize);
965 200 : sHeader.nSize = sHeader.nFileSize0 - GetRMFOffset(nHeaderOffset, nullptr);
966 : /* -------------------------------------------------------------------- */
967 : /* Write out the main header. */
968 : /* -------------------------------------------------------------------- */
969 : {
970 200 : GByte abyHeader[RMF_HEADER_SIZE] = {};
971 :
972 200 : memcpy(abyHeader, sHeader.bySignature, RMF_SIGNATURE_SIZE);
973 200 : RMF_WRITE_ULONG(abyHeader, sHeader.iVersion, 4);
974 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nSize, 8);
975 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nOvrOffset, 12);
976 200 : RMF_WRITE_ULONG(abyHeader, sHeader.iUserID, 16);
977 200 : memcpy(abyHeader + 20, sHeader.byName, RMF_NAME_SIZE);
978 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nBitDepth, 52);
979 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nHeight, 56);
980 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nWidth, 60);
981 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nXTiles, 64);
982 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nYTiles, 68);
983 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileHeight, 72);
984 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileWidth, 76);
985 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nLastTileHeight, 80);
986 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nLastTileWidth, 84);
987 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nROIOffset, 88);
988 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nROISize, 92);
989 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nClrTblOffset, 96);
990 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nClrTblSize, 100);
991 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileTblOffset, 104);
992 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileTblSize, 108);
993 200 : RMF_WRITE_LONG(abyHeader, sHeader.iMapType, 124);
994 200 : RMF_WRITE_LONG(abyHeader, sHeader.iProjection, 128);
995 200 : RMF_WRITE_LONG(abyHeader, sHeader.iEPSGCode, 132);
996 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfScale, 136);
997 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfResolution, 144);
998 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfPixelSize, 152);
999 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfLLY, 160);
1000 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfLLX, 168);
1001 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfStdP1, 176);
1002 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfStdP2, 184);
1003 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfCenterLong, 192);
1004 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfCenterLat, 200);
1005 200 : *(abyHeader + 208) = sHeader.iCompression;
1006 200 : *(abyHeader + 209) = sHeader.iMaskType;
1007 200 : *(abyHeader + 210) = sHeader.iMaskStep;
1008 200 : *(abyHeader + 211) = sHeader.iFrameFlag;
1009 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFlagsTblOffset, 212);
1010 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFlagsTblSize, 216);
1011 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFileSize0, 220);
1012 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFileSize1, 224);
1013 200 : *(abyHeader + 228) = sHeader.iUnknown;
1014 200 : *(abyHeader + 244) = sHeader.iGeorefFlag;
1015 200 : *(abyHeader + 245) = sHeader.iInverse;
1016 200 : *(abyHeader + 246) = sHeader.iJpegQuality;
1017 200 : memcpy(abyHeader + 248, sHeader.abyInvisibleColors,
1018 : sizeof(sHeader.abyInvisibleColors));
1019 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.adfElevMinMax[0], 280);
1020 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.adfElevMinMax[1], 288);
1021 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfNoData, 296);
1022 200 : RMF_WRITE_ULONG(abyHeader, sHeader.iElevationUnit, 304);
1023 200 : *(abyHeader + 308) = sHeader.iElevationType;
1024 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nExtHdrOffset, 312);
1025 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nExtHdrSize, 316);
1026 :
1027 200 : VSIFSeekL(fp, nHeaderOffset, SEEK_SET);
1028 200 : VSIFWriteL(abyHeader, 1, sizeof(abyHeader), fp);
1029 : }
1030 :
1031 : /* -------------------------------------------------------------------- */
1032 : /* Write out the extended header. */
1033 : /* -------------------------------------------------------------------- */
1034 :
1035 200 : if (sHeader.nExtHdrOffset && sHeader.nExtHdrSize >= RMF_MIN_EXT_HEADER_SIZE)
1036 : {
1037 200 : if (sHeader.nExtHdrSize > RMF_MAX_EXT_HEADER_SIZE)
1038 : {
1039 0 : CPLError(CE_Failure, CPLE_FileIO, "RMF File malformed");
1040 0 : return CE_Failure;
1041 : }
1042 : GByte *pabyExtHeader =
1043 200 : reinterpret_cast<GByte *>(CPLCalloc(sHeader.nExtHdrSize, 1));
1044 :
1045 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nEllipsoid, 24);
1046 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nVertDatum, 28);
1047 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nDatum, 32);
1048 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nZone, 36);
1049 :
1050 200 : VSIFSeekL(fp, GetFileOffset(sHeader.nExtHdrOffset), SEEK_SET);
1051 200 : VSIFWriteL(pabyExtHeader, 1, sHeader.nExtHdrSize, fp);
1052 :
1053 200 : CPLFree(pabyExtHeader);
1054 : }
1055 :
1056 : /* -------------------------------------------------------------------- */
1057 : /* Write out the color table. */
1058 : /* -------------------------------------------------------------------- */
1059 :
1060 200 : if (sHeader.nClrTblOffset && sHeader.nClrTblSize)
1061 : {
1062 59 : VSIFSeekL(fp, GetFileOffset(sHeader.nClrTblOffset), SEEK_SET);
1063 59 : VSIFWriteL(pabyColorTable, 1, sHeader.nClrTblSize, fp);
1064 : }
1065 :
1066 200 : if (sHeader.nROIOffset && sHeader.nROISize)
1067 : {
1068 : GByte *pabyROI =
1069 2 : reinterpret_cast<GByte *>(CPLCalloc(sHeader.nROISize, 1));
1070 2 : memset(pabyROI, 0, sHeader.nROISize);
1071 :
1072 2 : auto nPointCount = astFrameCoords.size();
1073 2 : size_t offset = 0;
1074 2 : RMF_WRITE_LONG(pabyROI, nPolygonType, offset);
1075 2 : offset += 4;
1076 2 : RMF_WRITE_LONG(pabyROI, static_cast<GInt32>((4 + nPointCount * 2) * 4),
1077 : offset);
1078 2 : offset += 4;
1079 2 : RMF_WRITE_LONG(pabyROI, 0, offset);
1080 2 : offset += 4;
1081 2 : RMF_WRITE_LONG(pabyROI, static_cast<GInt32>(32768 * nPointCount * 2),
1082 : offset);
1083 2 : offset += 4;
1084 :
1085 : // Write points
1086 12 : for (size_t i = 0; i < nPointCount; i++)
1087 : {
1088 10 : RMF_WRITE_LONG(pabyROI, astFrameCoords[i].nX, offset);
1089 10 : offset += 4;
1090 10 : RMF_WRITE_LONG(pabyROI, astFrameCoords[i].nY, offset);
1091 10 : offset += 4;
1092 : }
1093 :
1094 2 : VSIFSeekL(fp, GetFileOffset(sHeader.nROIOffset), SEEK_SET);
1095 2 : VSIFWriteL(pabyROI, 1, sHeader.nROISize, fp);
1096 :
1097 2 : CPLFree(pabyROI);
1098 : }
1099 :
1100 200 : if (sHeader.nFlagsTblOffset && sHeader.nFlagsTblSize)
1101 : {
1102 : GByte *pabyFlagsTbl =
1103 200 : reinterpret_cast<GByte *>(CPLCalloc(sHeader.nFlagsTblSize, 1));
1104 :
1105 200 : if (sHeader.iFrameFlag == 0)
1106 : {
1107 : // TODO: Add more strictly check for flag value
1108 2 : memset(
1109 : pabyFlagsTbl, 2,
1110 : sHeader
1111 2 : .nFlagsTblSize); // Mark all blocks as intersected with ROI. 0 - complete outside, 1 - complete inside.
1112 : }
1113 : else
1114 : {
1115 198 : memset(pabyFlagsTbl, 0, sHeader.nFlagsTblSize);
1116 : }
1117 :
1118 200 : VSIFSeekL(fp, GetFileOffset(sHeader.nFlagsTblOffset), SEEK_SET);
1119 200 : VSIFWriteL(pabyFlagsTbl, 1, sHeader.nFlagsTblSize, fp);
1120 :
1121 200 : CPLFree(pabyFlagsTbl);
1122 : }
1123 :
1124 : #undef RMF_WRITE_DOUBLE
1125 : #undef RMF_WRITE_ULONG
1126 : #undef RMF_WRITE_LONG
1127 :
1128 : /* -------------------------------------------------------------------- */
1129 : /* Write out the block table, swap if needed. */
1130 : /* -------------------------------------------------------------------- */
1131 :
1132 200 : VSIFSeekL(fp, GetFileOffset(sHeader.nTileTblOffset), SEEK_SET);
1133 :
1134 : #ifdef CPL_MSB
1135 : GUInt32 *paiTilesSwapped =
1136 : reinterpret_cast<GUInt32 *>(CPLMalloc(sHeader.nTileTblSize));
1137 : if (!paiTilesSwapped)
1138 : return CE_Failure;
1139 :
1140 : memcpy(paiTilesSwapped, paiTiles, sHeader.nTileTblSize);
1141 : for (GUInt32 i = 0; i < sHeader.nTileTblSize / sizeof(GUInt32); i++)
1142 : CPL_SWAP32PTR(paiTilesSwapped + i);
1143 : VSIFWriteL(paiTilesSwapped, 1, sHeader.nTileTblSize, fp);
1144 :
1145 : CPLFree(paiTilesSwapped);
1146 : #else
1147 200 : VSIFWriteL(paiTiles, 1, sHeader.nTileTblSize, fp);
1148 : #endif
1149 :
1150 200 : bHeaderDirty = false;
1151 :
1152 200 : return CE_None;
1153 : }
1154 :
1155 : /************************************************************************/
1156 : /* FlushCache() */
1157 : /************************************************************************/
1158 :
1159 411 : CPLErr RMFDataset::FlushCache(bool bAtClosing)
1160 :
1161 : {
1162 411 : CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
1163 :
1164 549 : if (poCompressData != nullptr &&
1165 138 : poCompressData->oThreadPool.GetThreadCount() > 0)
1166 : {
1167 9 : poCompressData->oThreadPool.WaitCompletion();
1168 : }
1169 :
1170 411 : if (bAtClosing && eRMFType == RMFT_MTW && eAccess == GA_Update)
1171 : {
1172 77 : GDALRasterBand *poBand = GetRasterBand(1);
1173 :
1174 77 : if (poBand)
1175 : {
1176 : // ComputeRasterMinMax can setup error in case of dataset full
1177 : // from NoData values, but it makes no sense here.
1178 77 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1179 77 : poBand->ComputeRasterMinMax(FALSE, sHeader.adfElevMinMax);
1180 77 : bHeaderDirty = true;
1181 : }
1182 : }
1183 411 : if (bHeaderDirty && WriteHeader() != CE_None)
1184 0 : eErr = CE_Failure;
1185 411 : return eErr;
1186 : }
1187 :
1188 : /************************************************************************/
1189 : /* Identify() */
1190 : /************************************************************************/
1191 :
1192 59712 : int RMFDataset::Identify(GDALOpenInfo *poOpenInfo)
1193 :
1194 : {
1195 59712 : if (poOpenInfo->pabyHeader == nullptr)
1196 54560 : return FALSE;
1197 :
1198 5152 : if (memcmp(poOpenInfo->pabyHeader, RMF_SigRSW, sizeof(RMF_SigRSW)) != 0 &&
1199 4953 : memcmp(poOpenInfo->pabyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) !=
1200 4941 : 0 &&
1201 4941 : memcmp(poOpenInfo->pabyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) != 0)
1202 4823 : return FALSE;
1203 :
1204 329 : return TRUE;
1205 : }
1206 :
1207 : /************************************************************************/
1208 : /* Open() */
1209 : /************************************************************************/
1210 :
1211 128 : GDALDataset *RMFDataset::Open(GDALOpenInfo *poOpenInfo)
1212 : {
1213 128 : auto poDS = Open(poOpenInfo, nullptr, 0);
1214 128 : if (poDS == nullptr)
1215 : {
1216 9 : return nullptr;
1217 : }
1218 :
1219 119 : RMFDataset *poCurrentLayer = poDS;
1220 119 : RMFDataset *poParent = poCurrentLayer;
1221 119 : const int nMaxPossibleOvCount = 64;
1222 :
1223 200 : for (int iOv = 0; iOv < nMaxPossibleOvCount && poCurrentLayer != nullptr;
1224 : ++iOv)
1225 : {
1226 200 : poCurrentLayer = poCurrentLayer->OpenOverview(poParent, poOpenInfo);
1227 200 : if (poCurrentLayer == nullptr)
1228 119 : break;
1229 81 : poParent->poOvrDatasets.push_back(poCurrentLayer);
1230 : }
1231 :
1232 119 : return poDS;
1233 : }
1234 :
1235 213 : RMFDataset *RMFDataset::Open(GDALOpenInfo *poOpenInfo, RMFDataset *poParentDS,
1236 : vsi_l_offset nNextHeaderOffset)
1237 : {
1238 341 : if (!Identify(poOpenInfo) ||
1239 128 : (poParentDS == nullptr && poOpenInfo->fpL == nullptr))
1240 2 : return nullptr;
1241 :
1242 : /* -------------------------------------------------------------------- */
1243 : /* Create a corresponding GDALDataset. */
1244 : /* -------------------------------------------------------------------- */
1245 211 : RMFDataset *poDS = new RMFDataset();
1246 :
1247 211 : if (poParentDS == nullptr)
1248 : {
1249 128 : poDS->fp = poOpenInfo->fpL;
1250 128 : poOpenInfo->fpL = nullptr;
1251 128 : poDS->nHeaderOffset = 0;
1252 128 : poDS->poParentDS = nullptr;
1253 : }
1254 : else
1255 : {
1256 83 : poDS->fp = poParentDS->fp;
1257 83 : poDS->poParentDS = poParentDS;
1258 83 : poDS->nHeaderOffset = nNextHeaderOffset;
1259 : }
1260 211 : poDS->eAccess = poOpenInfo->eAccess;
1261 :
1262 : #define RMF_READ_SHORT(ptr, value, offset) \
1263 : do \
1264 : { \
1265 : memcpy(&(value), reinterpret_cast<GInt16 *>((ptr) + (offset)), \
1266 : sizeof(GInt16)); \
1267 : if (poDS->bBigEndian) \
1268 : { \
1269 : CPL_MSBPTR16(&(value)); \
1270 : } \
1271 : else \
1272 : { \
1273 : CPL_LSBPTR16(&(value)); \
1274 : } \
1275 : } while (false);
1276 :
1277 : #define RMF_READ_ULONG(ptr, value, offset) \
1278 : do \
1279 : { \
1280 : memcpy(&(value), reinterpret_cast<GUInt32 *>((ptr) + (offset)), \
1281 : sizeof(GUInt32)); \
1282 : if (poDS->bBigEndian) \
1283 : { \
1284 : CPL_MSBPTR32(&(value)); \
1285 : } \
1286 : else \
1287 : { \
1288 : CPL_LSBPTR32(&(value)); \
1289 : } \
1290 : } while (false);
1291 :
1292 : #define RMF_READ_LONG(ptr, value, offset) RMF_READ_ULONG(ptr, value, offset)
1293 :
1294 : #define RMF_READ_DOUBLE(ptr, value, offset) \
1295 : do \
1296 : { \
1297 : memcpy(&(value), reinterpret_cast<double *>((ptr) + (offset)), \
1298 : sizeof(double)); \
1299 : if (poDS->bBigEndian) \
1300 : { \
1301 : CPL_MSBPTR64(&(value)); \
1302 : } \
1303 : else \
1304 : { \
1305 : CPL_LSBPTR64(&(value)); \
1306 : } \
1307 : } while (false);
1308 :
1309 : /* -------------------------------------------------------------------- */
1310 : /* Read the main header. */
1311 : /* -------------------------------------------------------------------- */
1312 :
1313 : {
1314 211 : GByte abyHeader[RMF_HEADER_SIZE] = {};
1315 :
1316 211 : VSIFSeekL(poDS->fp, nNextHeaderOffset, SEEK_SET);
1317 211 : if (VSIFReadL(abyHeader, 1, sizeof(abyHeader), poDS->fp) !=
1318 : sizeof(abyHeader))
1319 : {
1320 0 : delete poDS;
1321 0 : return nullptr;
1322 : }
1323 :
1324 211 : if (memcmp(abyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) == 0)
1325 : {
1326 76 : poDS->eRMFType = RMFT_MTW;
1327 : }
1328 135 : else if (memcmp(abyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) == 0)
1329 : {
1330 6 : poDS->eRMFType = RMFT_RSW;
1331 6 : poDS->bBigEndian = true;
1332 : }
1333 : else
1334 : {
1335 129 : poDS->eRMFType = RMFT_RSW;
1336 : }
1337 :
1338 211 : memcpy(poDS->sHeader.bySignature, abyHeader, RMF_SIGNATURE_SIZE);
1339 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.iVersion, 4);
1340 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nSize, 8);
1341 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nOvrOffset, 12);
1342 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.iUserID, 16);
1343 211 : memcpy(poDS->sHeader.byName, abyHeader + 20,
1344 : sizeof(poDS->sHeader.byName));
1345 211 : poDS->sHeader.byName[sizeof(poDS->sHeader.byName) - 1] = '\0';
1346 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nBitDepth, 52);
1347 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nHeight, 56);
1348 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nWidth, 60);
1349 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nXTiles, 64);
1350 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nYTiles, 68);
1351 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileHeight, 72);
1352 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileWidth, 76);
1353 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nLastTileHeight, 80);
1354 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nLastTileWidth, 84);
1355 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nROIOffset, 88);
1356 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nROISize, 92);
1357 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nClrTblOffset, 96);
1358 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nClrTblSize, 100);
1359 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileTblOffset, 104);
1360 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileTblSize, 108);
1361 211 : RMF_READ_LONG(abyHeader, poDS->sHeader.iMapType, 124);
1362 211 : RMF_READ_LONG(abyHeader, poDS->sHeader.iProjection, 128);
1363 211 : RMF_READ_LONG(abyHeader, poDS->sHeader.iEPSGCode, 132);
1364 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfScale, 136);
1365 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfResolution, 144);
1366 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfPixelSize, 152);
1367 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfLLY, 160);
1368 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfLLX, 168);
1369 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfStdP1, 176);
1370 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfStdP2, 184);
1371 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfCenterLong, 192);
1372 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfCenterLat, 200);
1373 211 : poDS->sHeader.iCompression = *(abyHeader + 208);
1374 211 : poDS->sHeader.iMaskType = *(abyHeader + 209);
1375 211 : poDS->sHeader.iMaskStep = *(abyHeader + 210);
1376 211 : poDS->sHeader.iFrameFlag = *(abyHeader + 211);
1377 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFlagsTblOffset, 212);
1378 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFlagsTblSize, 216);
1379 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFileSize0, 220);
1380 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFileSize1, 224);
1381 211 : poDS->sHeader.iUnknown = *(abyHeader + 228);
1382 211 : poDS->sHeader.iGeorefFlag = *(abyHeader + 244);
1383 211 : poDS->sHeader.iInverse = *(abyHeader + 245);
1384 211 : poDS->sHeader.iJpegQuality = *(abyHeader + 246);
1385 211 : memcpy(poDS->sHeader.abyInvisibleColors, abyHeader + 248,
1386 : sizeof(poDS->sHeader.abyInvisibleColors));
1387 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.adfElevMinMax[0], 280);
1388 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.adfElevMinMax[1], 288);
1389 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfNoData, 296);
1390 :
1391 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.iElevationUnit, 304);
1392 211 : poDS->sHeader.iElevationType = *(abyHeader + 308);
1393 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nExtHdrOffset, 312);
1394 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nExtHdrSize, 316);
1395 211 : poDS->SetMetadataItem(MD_SCALE_KEY,
1396 211 : CPLSPrintf("1 : %u", int(poDS->sHeader.dfScale)));
1397 211 : poDS->SetMetadataItem(MD_NAME_KEY,
1398 211 : CPLSPrintf("%s", poDS->sHeader.byName));
1399 211 : poDS->SetMetadataItem(MD_VERSION_KEY,
1400 : CPLSPrintf("%d", poDS->sHeader.iVersion));
1401 211 : poDS->SetMetadataItem(MD_MATH_BASE_MAP_TYPE_KEY,
1402 : CPLSPrintf("%d", poDS->sHeader.iMapType));
1403 211 : poDS->SetMetadataItem(MD_MATH_BASE_PROJECTION_KEY,
1404 : CPLSPrintf("%d", poDS->sHeader.iProjection));
1405 : }
1406 :
1407 211 : if (poDS->sHeader.nTileTblSize % (sizeof(GUInt32) * 2))
1408 : {
1409 0 : CPLError(CE_Warning, CPLE_IllegalArg, "Invalid tile table size.");
1410 0 : delete poDS;
1411 0 : return nullptr;
1412 : }
1413 :
1414 : bool bInvalidTileSize;
1415 : try
1416 : {
1417 : uint64_t nMaxTileBits =
1418 211 : (CPLSM(static_cast<uint64_t>(2)) *
1419 422 : CPLSM(static_cast<uint64_t>(poDS->sHeader.nTileWidth)) *
1420 422 : CPLSM(static_cast<uint64_t>(poDS->sHeader.nTileHeight)) *
1421 422 : CPLSM(static_cast<uint64_t>(poDS->sHeader.nBitDepth)))
1422 211 : .v();
1423 211 : bInvalidTileSize =
1424 : (nMaxTileBits >
1425 211 : static_cast<uint64_t>(std::numeric_limits<GUInt32>::max()));
1426 : }
1427 0 : catch (...)
1428 : {
1429 0 : bInvalidTileSize = true;
1430 : }
1431 211 : if (bInvalidTileSize)
1432 : {
1433 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1434 : "Invalid tile size. Width %lu, height %lu, bit depth %lu.",
1435 0 : static_cast<unsigned long>(poDS->sHeader.nTileWidth),
1436 0 : static_cast<unsigned long>(poDS->sHeader.nTileHeight),
1437 0 : static_cast<unsigned long>(poDS->sHeader.nBitDepth));
1438 0 : delete poDS;
1439 0 : return nullptr;
1440 : }
1441 :
1442 211 : if (poDS->sHeader.nLastTileWidth > poDS->sHeader.nTileWidth ||
1443 211 : poDS->sHeader.nLastTileHeight > poDS->sHeader.nTileHeight)
1444 : {
1445 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1446 : "Invalid last tile size %lu x %lu. "
1447 : "It can't be greater than %lu x %lu.",
1448 0 : static_cast<unsigned long>(poDS->sHeader.nLastTileWidth),
1449 0 : static_cast<unsigned long>(poDS->sHeader.nLastTileHeight),
1450 0 : static_cast<unsigned long>(poDS->sHeader.nTileWidth),
1451 0 : static_cast<unsigned long>(poDS->sHeader.nTileHeight));
1452 0 : delete poDS;
1453 0 : return nullptr;
1454 : }
1455 :
1456 211 : if (poParentDS != nullptr)
1457 : {
1458 83 : if (0 != memcmp(poDS->sHeader.bySignature,
1459 83 : poParentDS->sHeader.bySignature, RMF_SIGNATURE_SIZE))
1460 : {
1461 2 : CPLError(CE_Warning, CPLE_IllegalArg,
1462 : "Invalid subheader signature.");
1463 2 : delete poDS;
1464 2 : return nullptr;
1465 : }
1466 : }
1467 :
1468 : /* -------------------------------------------------------------------- */
1469 : /* Read the extended header. */
1470 : /* -------------------------------------------------------------------- */
1471 :
1472 209 : if (poDS->sHeader.nExtHdrOffset &&
1473 198 : poDS->sHeader.nExtHdrSize >= RMF_MIN_EXT_HEADER_SIZE)
1474 : {
1475 198 : if (poDS->sHeader.nExtHdrSize > RMF_MAX_EXT_HEADER_SIZE)
1476 : {
1477 0 : CPLError(CE_Failure, CPLE_FileIO, "RMF File malformed");
1478 0 : delete poDS;
1479 0 : return nullptr;
1480 : }
1481 : GByte *pabyExtHeader =
1482 198 : reinterpret_cast<GByte *>(CPLCalloc(poDS->sHeader.nExtHdrSize, 1));
1483 198 : if (pabyExtHeader == nullptr)
1484 : {
1485 0 : delete poDS;
1486 0 : return nullptr;
1487 : }
1488 :
1489 198 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nExtHdrOffset),
1490 : SEEK_SET);
1491 198 : VSIFReadL(pabyExtHeader, 1, poDS->sHeader.nExtHdrSize, poDS->fp);
1492 :
1493 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nEllipsoid, 24);
1494 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nVertDatum, 28);
1495 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nDatum, 32);
1496 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nZone, 36);
1497 :
1498 198 : CPLFree(pabyExtHeader);
1499 : }
1500 :
1501 209 : CPLDebug("RMF", "Version %d", poDS->sHeader.iVersion);
1502 :
1503 209 : constexpr GUInt32 ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE =
1504 : 10 * 1024 * 1024;
1505 : #ifdef DEBUG
1506 :
1507 418 : CPLDebug("RMF",
1508 : "%s image has width %d, height %d, bit depth %d, "
1509 : "compression scheme %d, %s, nodata %f",
1510 209 : (poDS->eRMFType == RMFT_MTW) ? "MTW" : "RSW", poDS->sHeader.nWidth,
1511 : poDS->sHeader.nHeight, poDS->sHeader.nBitDepth,
1512 209 : poDS->sHeader.iCompression,
1513 209 : poDS->bBigEndian ? "big endian" : "little endian",
1514 : poDS->sHeader.dfNoData);
1515 209 : CPLDebug("RMF",
1516 : "Size %d, offset to overview %#lx, user ID %d, "
1517 : "ROI offset %#lx, ROI size %d",
1518 : poDS->sHeader.nSize,
1519 209 : static_cast<unsigned long>(poDS->sHeader.nOvrOffset),
1520 : poDS->sHeader.iUserID,
1521 209 : static_cast<unsigned long>(poDS->sHeader.nROIOffset),
1522 : poDS->sHeader.nROISize);
1523 209 : CPLDebug("RMF", "Map type %d, projection %d, scale %f, resolution %f, ",
1524 : poDS->sHeader.iMapType, poDS->sHeader.iProjection,
1525 : poDS->sHeader.dfScale, poDS->sHeader.dfResolution);
1526 209 : CPLDebug("RMF", "EPSG %d ", poDS->sHeader.iEPSGCode);
1527 209 : CPLDebug("RMF", "Georeferencing: pixel size %f, LLX %f, LLY %f",
1528 : poDS->sHeader.dfPixelSize, poDS->sHeader.dfLLX,
1529 : poDS->sHeader.dfLLY);
1530 :
1531 209 : if (poDS->sHeader.nROIOffset &&
1532 116 : poDS->sHeader.nROISize >= sizeof(RSWFrame) &&
1533 12 : poDS->sHeader.nROISize <= ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE)
1534 : {
1535 : GByte *pabyROI = reinterpret_cast<GByte *>(
1536 12 : VSI_MALLOC_VERBOSE(poDS->sHeader.nROISize));
1537 12 : if (pabyROI == nullptr)
1538 : {
1539 0 : delete poDS;
1540 0 : return nullptr;
1541 : }
1542 :
1543 12 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nROIOffset),
1544 : SEEK_SET);
1545 12 : if (VSIFReadL(pabyROI, poDS->sHeader.nROISize, 1, poDS->fp) != 1)
1546 : {
1547 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot read ROI");
1548 0 : CPLFree(pabyROI);
1549 0 : delete poDS;
1550 0 : return nullptr;
1551 : }
1552 :
1553 : GInt32 nValue;
1554 :
1555 12 : CPLDebug("RMF", "ROI coordinates:");
1556 : /* coverity[tainted_data] */
1557 3504 : for (GUInt32 i = 0; i + sizeof(nValue) <= poDS->sHeader.nROISize;
1558 3492 : i += sizeof(nValue))
1559 : {
1560 3492 : RMF_READ_LONG(pabyROI, nValue, i);
1561 3492 : CPLDebug("RMF", "%d", nValue);
1562 : }
1563 :
1564 12 : CPLFree(pabyROI);
1565 : }
1566 : #endif
1567 418 : if (poDS->sHeader.nWidth >= INT_MAX || poDS->sHeader.nHeight >= INT_MAX ||
1568 209 : !GDALCheckDatasetDimensions(poDS->sHeader.nWidth,
1569 209 : poDS->sHeader.nHeight))
1570 : {
1571 0 : delete poDS;
1572 0 : return nullptr;
1573 : }
1574 :
1575 : /* -------------------------------------------------------------------- */
1576 : /* Read array of blocks offsets/sizes. */
1577 : /* -------------------------------------------------------------------- */
1578 :
1579 : // To avoid useless excessive memory allocation
1580 209 : if (poDS->sHeader.nTileTblSize > 1000000)
1581 : {
1582 0 : VSIFSeekL(poDS->fp, 0, SEEK_END);
1583 0 : vsi_l_offset nFileSize = VSIFTellL(poDS->fp);
1584 0 : if (nFileSize < poDS->sHeader.nTileTblSize)
1585 : {
1586 0 : delete poDS;
1587 0 : return nullptr;
1588 : }
1589 : }
1590 :
1591 209 : if (VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nTileTblOffset),
1592 209 : SEEK_SET) < 0)
1593 : {
1594 0 : delete poDS;
1595 0 : return nullptr;
1596 : }
1597 :
1598 209 : poDS->paiTiles =
1599 209 : reinterpret_cast<GUInt32 *>(VSIMalloc(poDS->sHeader.nTileTblSize));
1600 209 : if (!poDS->paiTiles)
1601 : {
1602 0 : delete poDS;
1603 0 : return nullptr;
1604 : }
1605 :
1606 209 : if (VSIFReadL(poDS->paiTiles, 1, poDS->sHeader.nTileTblSize, poDS->fp) <
1607 209 : poDS->sHeader.nTileTblSize)
1608 : {
1609 9 : CPLDebug("RMF", "Can't read tiles offsets/sizes table.");
1610 9 : delete poDS;
1611 9 : return nullptr;
1612 : }
1613 :
1614 : #ifdef CPL_MSB
1615 : if (!poDS->bBigEndian)
1616 : {
1617 : for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
1618 : i++)
1619 : CPL_SWAP32PTR(poDS->paiTiles + i);
1620 : }
1621 : #else
1622 200 : if (poDS->bBigEndian)
1623 : {
1624 30 : for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
1625 : i++)
1626 24 : CPL_SWAP32PTR(poDS->paiTiles + i);
1627 : }
1628 : #endif
1629 :
1630 : #ifdef DEBUG
1631 200 : CPLDebug("RMF", "List of block offsets/sizes:");
1632 :
1633 646 : for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
1634 446 : i += 2)
1635 : {
1636 446 : CPLDebug("RMF", " %u / %u", poDS->paiTiles[i],
1637 446 : poDS->paiTiles[i + 1]);
1638 : }
1639 : #endif
1640 :
1641 : /* -------------------------------------------------------------------- */
1642 : /* Set up essential image parameters. */
1643 : /* -------------------------------------------------------------------- */
1644 200 : GDALDataType eType = GDT_Byte;
1645 :
1646 200 : poDS->nRasterXSize = poDS->sHeader.nWidth;
1647 200 : poDS->nRasterYSize = poDS->sHeader.nHeight;
1648 :
1649 200 : if (poDS->eRMFType == RMFT_RSW)
1650 : {
1651 126 : switch (poDS->sHeader.nBitDepth)
1652 : {
1653 58 : case 32:
1654 : case 24:
1655 : case 16:
1656 58 : poDS->nBands = 3;
1657 58 : break;
1658 68 : case 1:
1659 : case 4:
1660 : case 8:
1661 68 : if (poParentDS != nullptr &&
1662 26 : poParentDS->poColorTable != nullptr)
1663 : {
1664 26 : poDS->poColorTable = poParentDS->poColorTable->Clone();
1665 : }
1666 : else
1667 : {
1668 : // Allocate memory for colour table and read it
1669 42 : poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
1670 42 : GUInt32 nExpectedColorTableBytes =
1671 42 : poDS->nColorTableSize * 4;
1672 42 : if (nExpectedColorTableBytes > poDS->sHeader.nClrTblSize)
1673 : {
1674 : // We could probably test for strict equality in
1675 : // the above test ???
1676 0 : CPLDebug("RMF",
1677 : "Wrong color table size. "
1678 : "Expected %u, got %u.",
1679 : nExpectedColorTableBytes,
1680 : poDS->sHeader.nClrTblSize);
1681 0 : delete poDS;
1682 0 : return nullptr;
1683 : }
1684 42 : poDS->pabyColorTable = reinterpret_cast<GByte *>(
1685 42 : VSIMalloc(nExpectedColorTableBytes));
1686 42 : if (poDS->pabyColorTable == nullptr)
1687 : {
1688 0 : CPLDebug("RMF", "Can't allocate color table.");
1689 0 : delete poDS;
1690 0 : return nullptr;
1691 : }
1692 42 : if (VSIFSeekL(
1693 : poDS->fp,
1694 : poDS->GetFileOffset(poDS->sHeader.nClrTblOffset),
1695 42 : SEEK_SET) < 0)
1696 : {
1697 0 : CPLDebug("RMF", "Can't seek to color table location.");
1698 0 : delete poDS;
1699 0 : return nullptr;
1700 : }
1701 42 : if (VSIFReadL(poDS->pabyColorTable, 1,
1702 : nExpectedColorTableBytes,
1703 42 : poDS->fp) < nExpectedColorTableBytes)
1704 : {
1705 0 : CPLDebug("RMF", "Can't read color table.");
1706 0 : delete poDS;
1707 0 : return nullptr;
1708 : }
1709 :
1710 42 : poDS->poColorTable = new GDALColorTable();
1711 9326 : for (GUInt32 i = 0; i < poDS->nColorTableSize; i++)
1712 : {
1713 9284 : const GDALColorEntry oEntry = {
1714 9284 : poDS->pabyColorTable[i * 4], // Red
1715 9284 : poDS->pabyColorTable[i * 4 + 1], // Green
1716 9284 : poDS->pabyColorTable[i * 4 + 2], // Blue
1717 : 255 // Alpha
1718 9284 : };
1719 :
1720 9284 : poDS->poColorTable->SetColorEntry(i, &oEntry);
1721 : }
1722 : }
1723 68 : poDS->nBands = 1;
1724 68 : break;
1725 0 : default:
1726 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1727 : "Invalid RSW bit depth %lu.",
1728 0 : static_cast<unsigned long>(poDS->sHeader.nBitDepth));
1729 0 : delete poDS;
1730 0 : return nullptr;
1731 : }
1732 126 : eType = GDT_Byte;
1733 : }
1734 : else
1735 : {
1736 74 : poDS->nBands = 1;
1737 74 : if (poDS->sHeader.nBitDepth == 8)
1738 : {
1739 0 : eType = GDT_Byte;
1740 : }
1741 74 : else if (poDS->sHeader.nBitDepth == 16)
1742 : {
1743 0 : eType = GDT_Int16;
1744 : }
1745 74 : else if (poDS->sHeader.nBitDepth == 32)
1746 : {
1747 9 : eType = GDT_Int32;
1748 : }
1749 65 : else if (poDS->sHeader.nBitDepth == 64)
1750 : {
1751 65 : eType = GDT_Float64;
1752 : }
1753 : else
1754 : {
1755 0 : CPLError(CE_Warning, CPLE_IllegalArg, "Invalid MTW bit depth %lu.",
1756 0 : static_cast<unsigned long>(poDS->sHeader.nBitDepth));
1757 0 : delete poDS;
1758 0 : return nullptr;
1759 : }
1760 : }
1761 :
1762 200 : if (poDS->sHeader.nTileWidth == 0 || poDS->sHeader.nTileWidth > INT_MAX ||
1763 200 : poDS->sHeader.nTileHeight == 0 || poDS->sHeader.nTileHeight > INT_MAX)
1764 : {
1765 0 : CPLDebug("RMF", "Invalid tile dimension : %u x %u",
1766 : poDS->sHeader.nTileWidth, poDS->sHeader.nTileHeight);
1767 0 : delete poDS;
1768 0 : return nullptr;
1769 : }
1770 :
1771 200 : const int nDataSize = GDALGetDataTypeSizeBytes(eType);
1772 200 : const int nBlockXSize = static_cast<int>(poDS->sHeader.nTileWidth);
1773 200 : const int nBlockYSize = static_cast<int>(poDS->sHeader.nTileHeight);
1774 200 : if (nDataSize == 0 || nBlockXSize > INT_MAX / nBlockYSize ||
1775 200 : nBlockYSize > INT_MAX / nDataSize ||
1776 200 : nBlockXSize > INT_MAX / (nBlockYSize * nDataSize))
1777 : {
1778 0 : CPLDebug("RMF", "Too big raster / tile dimension");
1779 0 : delete poDS;
1780 0 : return nullptr;
1781 : }
1782 :
1783 200 : poDS->nXTiles = DIV_ROUND_UP(poDS->nRasterXSize, nBlockXSize);
1784 200 : poDS->nYTiles = DIV_ROUND_UP(poDS->nRasterYSize, nBlockYSize);
1785 :
1786 : #ifdef DEBUG
1787 200 : CPLDebug("RMF", "Image is %d tiles wide, %d tiles long", poDS->nXTiles,
1788 : poDS->nYTiles);
1789 : #endif
1790 :
1791 : /* -------------------------------------------------------------------- */
1792 : /* Choose compression scheme. */
1793 : /* -------------------------------------------------------------------- */
1794 200 : if (CE_None != poDS->SetupCompression(eType, poOpenInfo->pszFilename))
1795 : {
1796 0 : delete poDS;
1797 0 : return nullptr;
1798 : }
1799 :
1800 200 : if (poOpenInfo->eAccess == GA_Update)
1801 : {
1802 20 : if (poParentDS == nullptr)
1803 : {
1804 12 : if (CE_None !=
1805 12 : poDS->InitCompressorData(poOpenInfo->papszOpenOptions))
1806 : {
1807 0 : delete poDS;
1808 0 : return nullptr;
1809 : }
1810 : }
1811 : else
1812 : {
1813 8 : poDS->poCompressData = poParentDS->poCompressData;
1814 : }
1815 : }
1816 : /* -------------------------------------------------------------------- */
1817 : /* Create band information objects. */
1818 : /* -------------------------------------------------------------------- */
1819 516 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
1820 316 : poDS->SetBand(iBand, new RMFRasterBand(poDS, iBand, eType));
1821 :
1822 200 : poDS->SetupNBits();
1823 :
1824 200 : if (poDS->nBands > 1)
1825 : {
1826 58 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
1827 : }
1828 : /* -------------------------------------------------------------------- */
1829 : /* Set up projection. */
1830 : /* */
1831 : /* XXX: If projection value is not specified, but image still have */
1832 : /* georeferencing information, assume Gauss-Kruger projection. */
1833 : /* -------------------------------------------------------------------- */
1834 200 : if (poDS->sHeader.iEPSGCode > RMF_EPSG_MIN_CODE ||
1835 186 : poDS->sHeader.iProjection > 0 ||
1836 117 : (poDS->sHeader.dfPixelSize != 0.0 && poDS->sHeader.dfLLX != 0.0 &&
1837 15 : poDS->sHeader.dfLLY != 0.0))
1838 : {
1839 98 : GInt32 nProj =
1840 98 : (poDS->sHeader.iProjection) ? poDS->sHeader.iProjection : 1;
1841 98 : double padfPrjParams[8] = {poDS->sHeader.dfStdP1,
1842 98 : poDS->sHeader.dfStdP2,
1843 98 : poDS->sHeader.dfCenterLat,
1844 98 : poDS->sHeader.dfCenterLong,
1845 : 1.0,
1846 : 0.0,
1847 : 0.0,
1848 98 : 0.0};
1849 :
1850 : // XXX: Compute zone number for Gauss-Kruger (Transverse Mercator)
1851 : // projection if it is not specified.
1852 98 : if (nProj == 1L && poDS->sHeader.dfCenterLong == 0.0)
1853 : {
1854 6 : if (poDS->sExtHeader.nZone == 0)
1855 : {
1856 0 : double centerXCoord =
1857 0 : poDS->sHeader.dfLLX +
1858 0 : (poDS->nRasterXSize * poDS->sHeader.dfPixelSize / 2.0);
1859 0 : padfPrjParams[7] = floor((centerXCoord - 500000.0) / 1000000.0);
1860 : }
1861 : else
1862 : {
1863 6 : padfPrjParams[7] = poDS->sExtHeader.nZone;
1864 : }
1865 : }
1866 :
1867 98 : OGRErr res = OGRERR_FAILURE;
1868 98 : if (nProj >= 0 &&
1869 88 : (poDS->sExtHeader.nDatum >= 0 || poDS->sExtHeader.nEllipsoid >= 0))
1870 : {
1871 88 : res = poDS->m_oSRS.importFromPanorama(
1872 88 : nProj, poDS->sExtHeader.nDatum, poDS->sExtHeader.nEllipsoid,
1873 : padfPrjParams);
1874 : }
1875 :
1876 111 : if (poDS->sHeader.iEPSGCode > RMF_EPSG_MIN_CODE &&
1877 13 : (OGRERR_NONE != res || poDS->m_oSRS.IsLocal()))
1878 : {
1879 1 : res = poDS->m_oSRS.importFromEPSG(poDS->sHeader.iEPSGCode);
1880 : }
1881 :
1882 : const char *pszSetVertCS =
1883 98 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "RMF_SET_VERTCS",
1884 : CPLGetConfigOption("RMF_SET_VERTCS", "NO"));
1885 98 : if (CPLTestBool(pszSetVertCS) && res == OGRERR_NONE &&
1886 0 : poDS->sExtHeader.nVertDatum > 0)
1887 : {
1888 0 : poDS->m_oSRS.importVertCSFromPanorama(poDS->sExtHeader.nVertDatum);
1889 : }
1890 : }
1891 :
1892 : /* -------------------------------------------------------------------- */
1893 : /* Set up georeferencing. */
1894 : /* -------------------------------------------------------------------- */
1895 200 : if ((poDS->eRMFType == RMFT_RSW && poDS->sHeader.iGeorefFlag) ||
1896 128 : (poDS->eRMFType == RMFT_MTW && poDS->sHeader.dfPixelSize != 0.0))
1897 : {
1898 146 : poDS->m_gt[0] = poDS->sHeader.dfLLX;
1899 292 : poDS->m_gt[3] = poDS->sHeader.dfLLY +
1900 146 : poDS->nRasterYSize * poDS->sHeader.dfPixelSize;
1901 146 : poDS->m_gt[1] = poDS->sHeader.dfPixelSize;
1902 146 : poDS->m_gt[5] = -poDS->sHeader.dfPixelSize;
1903 146 : poDS->m_gt[2] = 0.0;
1904 146 : poDS->m_gt[4] = 0.0;
1905 : }
1906 :
1907 : /* -------------------------------------------------------------------- */
1908 : /* Set units. */
1909 : /* -------------------------------------------------------------------- */
1910 :
1911 200 : if (poDS->eRMFType == RMFT_MTW)
1912 : {
1913 74 : CPLFree(poDS->pszUnitType);
1914 74 : poDS->pszUnitType = RMFUnitTypeToStr(poDS->sHeader.iElevationUnit);
1915 : }
1916 :
1917 : /* -------------------------------------------------------------------- */
1918 : /* Report some other dataset related information. */
1919 : /* -------------------------------------------------------------------- */
1920 :
1921 200 : if (poDS->eRMFType == RMFT_MTW)
1922 : {
1923 74 : char szTemp[256] = {};
1924 :
1925 74 : snprintf(szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[0]);
1926 74 : poDS->SetMetadataItem("ELEVATION_MINIMUM", szTemp);
1927 :
1928 74 : snprintf(szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[1]);
1929 74 : poDS->SetMetadataItem("ELEVATION_MAXIMUM", szTemp);
1930 :
1931 74 : poDS->SetMetadataItem("ELEVATION_UNITS", poDS->pszUnitType);
1932 :
1933 74 : snprintf(szTemp, sizeof(szTemp), "%d", poDS->sHeader.iElevationType);
1934 74 : poDS->SetMetadataItem("ELEVATION_TYPE", szTemp);
1935 : }
1936 :
1937 : /* -------------------------------------------------------------------- */
1938 : /* Check for overviews. */
1939 : /* -------------------------------------------------------------------- */
1940 200 : if (nNextHeaderOffset == 0 && poParentDS == nullptr)
1941 : {
1942 119 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
1943 : }
1944 :
1945 : /* Set frame */
1946 200 : if (poDS->sHeader.nROIOffset &&
1947 107 : poDS->sHeader.nROISize >= sizeof(RSWFrame) &&
1948 12 : poDS->sHeader.nROISize <= ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE)
1949 : {
1950 : GByte *pabyROI = reinterpret_cast<GByte *>(
1951 12 : VSI_MALLOC_VERBOSE(poDS->sHeader.nROISize));
1952 12 : if (pabyROI == nullptr)
1953 : {
1954 0 : delete poDS;
1955 0 : return nullptr;
1956 : }
1957 :
1958 12 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nROIOffset),
1959 : SEEK_SET);
1960 12 : if (VSIFReadL(pabyROI, poDS->sHeader.nROISize, 1, poDS->fp) != 1)
1961 : {
1962 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot read ROI");
1963 0 : CPLFree(pabyROI);
1964 0 : delete poDS;
1965 0 : return nullptr;
1966 : }
1967 :
1968 : GInt32 nFrameType;
1969 12 : RMF_READ_LONG(pabyROI, nFrameType, 0);
1970 12 : if (nFrameType == nPolygonType)
1971 : {
1972 24 : CPLString osWKT = "POLYGON((";
1973 12 : bool bFirst = true;
1974 :
1975 12 : CPLDebug("RMF", "ROI coordinates:");
1976 : /* coverity[tainted_data] */
1977 12 : for (GUInt32 i = sizeof(RSWFrame);
1978 1734 : i + sizeof(RSWFrameCoord) <= poDS->sHeader.nROISize;
1979 1722 : i += sizeof(RSWFrameCoord))
1980 : {
1981 : GInt32 nX, nY;
1982 1722 : RMF_READ_LONG(pabyROI, nX, i);
1983 1722 : RMF_READ_LONG(pabyROI, nY, i + 4);
1984 :
1985 1722 : CPLDebug("RMF", "X: %d, Y: %d", nX, nY);
1986 :
1987 : double dfX =
1988 1722 : poDS->m_gt[0] + nX * poDS->m_gt[1] + nY * poDS->m_gt[2];
1989 : double dfY =
1990 1722 : poDS->m_gt[3] + nX * poDS->m_gt[4] + nY * poDS->m_gt[5];
1991 :
1992 1722 : if (bFirst)
1993 : {
1994 12 : osWKT += CPLSPrintf("%f %f", dfX, dfY);
1995 12 : bFirst = false;
1996 : }
1997 : else
1998 : {
1999 1710 : osWKT += CPLSPrintf(", %f %f", dfX, dfY);
2000 : }
2001 : }
2002 12 : osWKT += "))";
2003 12 : CPLDebug("RMF", "Frame WKT: %s", osWKT.c_str());
2004 12 : poDS->SetMetadataItem(MD_FRAME_KEY, osWKT);
2005 : }
2006 12 : CPLFree(pabyROI);
2007 : }
2008 :
2009 : #undef RMF_READ_DOUBLE
2010 : #undef RMF_READ_LONG
2011 : #undef RMF_READ_ULONG
2012 :
2013 200 : if (poDS->sHeader.nFlagsTblOffset && poDS->sHeader.nFlagsTblSize)
2014 : {
2015 107 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nFlagsTblOffset),
2016 : SEEK_SET);
2017 107 : CPLDebug("RMF", "Blocks flags:");
2018 : /* coverity[tainted_data] */
2019 416 : for (GUInt32 i = 0; i < poDS->sHeader.nFlagsTblSize; i += sizeof(GByte))
2020 : {
2021 : GByte nValue;
2022 309 : if (VSIFReadL(&nValue, 1, sizeof(nValue), poDS->fp) !=
2023 : sizeof(nValue))
2024 : {
2025 0 : CPLDebug("RMF", "Cannot read Block flag at index %u", i);
2026 0 : break;
2027 : }
2028 309 : CPLDebug("RMF", "Block %u -- flag %d", i, nValue);
2029 : }
2030 : }
2031 200 : return poDS;
2032 : }
2033 :
2034 : /************************************************************************/
2035 : /* Create() */
2036 : /************************************************************************/
2037 89 : GDALDataset *RMFDataset::Create(const char *pszFilename, int nXSize, int nYSize,
2038 : int nBandsIn, GDALDataType eType,
2039 : char **papszParamList)
2040 : {
2041 89 : return Create(pszFilename, nXSize, nYSize, nBandsIn, eType, papszParamList,
2042 89 : nullptr, 1.0);
2043 : }
2044 :
2045 123 : GDALDataset *RMFDataset::Create(const char *pszFilename, int nXSize, int nYSize,
2046 : int nBandsIn, GDALDataType eType,
2047 : char **papszParamList, RMFDataset *poParentDS,
2048 : double dfOvFactor)
2049 :
2050 : {
2051 123 : if (nBandsIn != 1 && nBandsIn != 3)
2052 : {
2053 7 : CPLError(CE_Failure, CPLE_NotSupported,
2054 : "RMF driver doesn't support %d bands. Must be 1 or 3.",
2055 : nBandsIn);
2056 :
2057 7 : return nullptr;
2058 : }
2059 :
2060 116 : if (nBandsIn == 1 && eType != GDT_Byte && eType != GDT_Int16 &&
2061 52 : eType != GDT_Int32 && eType != GDT_Float64)
2062 : {
2063 17 : CPLError(
2064 : CE_Failure, CPLE_AppDefined,
2065 : "Attempt to create RMF dataset with an illegal data type (%s), "
2066 : "only Byte, Int16, Int32 and Float64 types supported "
2067 : "by the format for single-band images.",
2068 : GDALGetDataTypeName(eType));
2069 :
2070 17 : return nullptr;
2071 : }
2072 :
2073 99 : if (nBandsIn == 3 && eType != GDT_Byte)
2074 : {
2075 13 : CPLError(
2076 : CE_Failure, CPLE_AppDefined,
2077 : "Attempt to create RMF dataset with an illegal data type (%s), "
2078 : "only Byte type supported by the format for three-band images.",
2079 : GDALGetDataTypeName(eType));
2080 :
2081 13 : return nullptr;
2082 : }
2083 :
2084 : /* -------------------------------------------------------------------- */
2085 : /* Create the dataset. */
2086 : /* -------------------------------------------------------------------- */
2087 86 : RMFDataset *poDS = new RMFDataset();
2088 :
2089 86 : GUInt32 nBlockXSize =
2090 86 : (nXSize < RMF_DEFAULT_BLOCKXSIZE) ? nXSize : RMF_DEFAULT_BLOCKXSIZE;
2091 86 : GUInt32 nBlockYSize =
2092 86 : (nYSize < RMF_DEFAULT_BLOCKYSIZE) ? nYSize : RMF_DEFAULT_BLOCKYSIZE;
2093 : double dfScale;
2094 : double dfResolution;
2095 : double dfPixelSize;
2096 86 : if (poParentDS == nullptr)
2097 : {
2098 52 : poDS->fp = VSIFOpenL(pszFilename, "w+b");
2099 52 : if (poDS->fp == nullptr)
2100 : {
2101 3 : CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create file %s.",
2102 : pszFilename);
2103 3 : delete poDS;
2104 3 : return nullptr;
2105 : }
2106 :
2107 : const char *pszScaleValue =
2108 49 : CSLFetchNameValue(papszParamList, MD_SCALE_KEY);
2109 49 : if (pszScaleValue != nullptr && CPLStrnlen(pszScaleValue, 10) > 4)
2110 : {
2111 0 : dfScale = atof(pszScaleValue + 4);
2112 : }
2113 : else
2114 : {
2115 49 : dfScale = RMF_DEFAULT_SCALE;
2116 : }
2117 49 : dfResolution = RMF_DEFAULT_RESOLUTION;
2118 49 : dfPixelSize = 1;
2119 :
2120 49 : if (CPLFetchBool(papszParamList, "MTW", false))
2121 12 : poDS->eRMFType = RMFT_MTW;
2122 : else
2123 37 : poDS->eRMFType = RMFT_RSW;
2124 :
2125 49 : GUInt32 iVersion = RMF_VERSION;
2126 49 : const char *pszRMFHUGE = CSLFetchNameValue(papszParamList, "RMFHUGE");
2127 :
2128 49 : if (pszRMFHUGE == nullptr)
2129 35 : pszRMFHUGE = "NO"; // Keep old behavior by default
2130 :
2131 49 : if (EQUAL(pszRMFHUGE, "NO"))
2132 : {
2133 42 : iVersion = RMF_VERSION;
2134 : }
2135 7 : else if (EQUAL(pszRMFHUGE, "YES"))
2136 : {
2137 7 : iVersion = RMF_VERSION_HUGE;
2138 : }
2139 0 : else if (EQUAL(pszRMFHUGE, "IF_SAFER"))
2140 : {
2141 : const double dfImageSize =
2142 0 : static_cast<double>(nXSize) * static_cast<double>(nYSize) *
2143 0 : static_cast<double>(nBandsIn) *
2144 0 : static_cast<double>(GDALGetDataTypeSizeBytes(eType));
2145 0 : if (dfImageSize > 3.0 * 1024.0 * 1024.0 * 1024.0)
2146 : {
2147 0 : iVersion = RMF_VERSION_HUGE;
2148 : }
2149 : else
2150 : {
2151 0 : iVersion = RMF_VERSION;
2152 : }
2153 : }
2154 :
2155 49 : const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKXSIZE");
2156 49 : if (pszValue != nullptr)
2157 1 : nBlockXSize = atoi(pszValue);
2158 49 : if (static_cast<int>(nBlockXSize) <= 0)
2159 0 : nBlockXSize = RMF_DEFAULT_BLOCKXSIZE;
2160 :
2161 49 : pszValue = CSLFetchNameValue(papszParamList, "BLOCKYSIZE");
2162 49 : if (pszValue != nullptr)
2163 1 : nBlockYSize = atoi(pszValue);
2164 49 : if (static_cast<int>(nBlockYSize) <= 0)
2165 0 : nBlockYSize = RMF_DEFAULT_BLOCKXSIZE;
2166 :
2167 49 : if (poDS->eRMFType == RMFT_MTW)
2168 12 : memcpy(poDS->sHeader.bySignature, RMF_SigMTW, RMF_SIGNATURE_SIZE);
2169 : else
2170 37 : memcpy(poDS->sHeader.bySignature, RMF_SigRSW, RMF_SIGNATURE_SIZE);
2171 49 : poDS->sHeader.iVersion = iVersion;
2172 49 : poDS->sHeader.nOvrOffset = 0x00;
2173 : }
2174 : else
2175 : {
2176 34 : poDS->fp = poParentDS->fp;
2177 34 : memcpy(poDS->sHeader.bySignature, poParentDS->sHeader.bySignature,
2178 : RMF_SIGNATURE_SIZE);
2179 34 : poDS->sHeader.iVersion = poParentDS->sHeader.iVersion;
2180 34 : poDS->eRMFType = poParentDS->eRMFType;
2181 34 : nBlockXSize = poParentDS->sHeader.nTileWidth;
2182 34 : nBlockYSize = poParentDS->sHeader.nTileHeight;
2183 34 : dfScale = poParentDS->sHeader.dfScale;
2184 34 : dfResolution = poParentDS->sHeader.dfResolution / dfOvFactor;
2185 34 : dfPixelSize = poParentDS->sHeader.dfPixelSize * dfOvFactor;
2186 :
2187 34 : poDS->nHeaderOffset = poParentDS->GetLastOffset();
2188 34 : poParentDS->sHeader.nOvrOffset =
2189 34 : poDS->GetRMFOffset(poDS->nHeaderOffset, &poDS->nHeaderOffset);
2190 34 : poParentDS->bHeaderDirty = true;
2191 34 : VSIFSeekL(poDS->fp, poDS->nHeaderOffset, SEEK_SET);
2192 34 : poDS->poParentDS = poParentDS;
2193 34 : CPLDebug("RMF",
2194 : "Create overview subfile at " CPL_FRMT_GUIB
2195 : " with size %dx%d, parent overview offset %d",
2196 : poDS->nHeaderOffset, nXSize, nYSize,
2197 : poParentDS->sHeader.nOvrOffset);
2198 : }
2199 : /* -------------------------------------------------------------------- */
2200 : /* Fill the RMFHeader */
2201 : /* -------------------------------------------------------------------- */
2202 83 : CPLDebug("RMF", "Version %d", poDS->sHeader.iVersion);
2203 :
2204 83 : poDS->sHeader.iUserID = 0x00;
2205 83 : memset(poDS->sHeader.byName, 0, sizeof(poDS->sHeader.byName));
2206 83 : poDS->sHeader.nBitDepth = GDALGetDataTypeSizeBits(eType) * nBandsIn;
2207 83 : poDS->sHeader.nHeight = nYSize;
2208 83 : poDS->sHeader.nWidth = nXSize;
2209 83 : poDS->sHeader.nTileWidth = nBlockXSize;
2210 83 : poDS->sHeader.nTileHeight = nBlockYSize;
2211 :
2212 83 : poDS->nXTiles = poDS->sHeader.nXTiles =
2213 83 : DIV_ROUND_UP(nXSize, poDS->sHeader.nTileWidth);
2214 83 : poDS->nYTiles = poDS->sHeader.nYTiles =
2215 83 : DIV_ROUND_UP(nYSize, poDS->sHeader.nTileHeight);
2216 83 : poDS->sHeader.nLastTileHeight = nYSize % poDS->sHeader.nTileHeight;
2217 83 : if (!poDS->sHeader.nLastTileHeight)
2218 44 : poDS->sHeader.nLastTileHeight = poDS->sHeader.nTileHeight;
2219 83 : poDS->sHeader.nLastTileWidth = nXSize % poDS->sHeader.nTileWidth;
2220 83 : if (!poDS->sHeader.nLastTileWidth)
2221 40 : poDS->sHeader.nLastTileWidth = poDS->sHeader.nTileWidth;
2222 :
2223 : // poDS->sHeader.nROIOffset = 0x00;
2224 : // poDS->sHeader.nROISize = 0x00;
2225 :
2226 83 : vsi_l_offset nCurPtr = poDS->nHeaderOffset + RMF_HEADER_SIZE;
2227 :
2228 : // Extended header
2229 83 : poDS->sHeader.nExtHdrOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2230 83 : poDS->sHeader.nExtHdrSize = RMF_EXT_HEADER_SIZE;
2231 83 : nCurPtr += poDS->sHeader.nExtHdrSize;
2232 :
2233 : // Color table
2234 83 : if (poDS->eRMFType == RMFT_RSW && nBandsIn == 1)
2235 : {
2236 34 : if (poDS->sHeader.nBitDepth > 8)
2237 : {
2238 6 : CPLError(CE_Failure, CPLE_AppDefined,
2239 : "Cannot create color table of RSW with nBitDepth = %d. "
2240 : "Retry with MTW ?",
2241 : poDS->sHeader.nBitDepth);
2242 6 : delete poDS;
2243 6 : return nullptr;
2244 : }
2245 :
2246 28 : poDS->sHeader.nClrTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2247 28 : poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
2248 28 : poDS->sHeader.nClrTblSize = poDS->nColorTableSize * 4;
2249 28 : poDS->pabyColorTable =
2250 28 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(poDS->sHeader.nClrTblSize));
2251 28 : if (poDS->pabyColorTable == nullptr)
2252 : {
2253 0 : delete poDS;
2254 0 : return nullptr;
2255 : }
2256 7196 : for (GUInt32 i = 0; i < poDS->nColorTableSize; i++)
2257 : {
2258 7168 : poDS->pabyColorTable[i * 4 + 0] = static_cast<GByte>(i);
2259 7168 : poDS->pabyColorTable[i * 4 + 1] = static_cast<GByte>(i);
2260 7168 : poDS->pabyColorTable[i * 4 + 2] = static_cast<GByte>(i);
2261 7168 : poDS->pabyColorTable[i * 4 + 3] = 0;
2262 : }
2263 28 : nCurPtr += poDS->sHeader.nClrTblSize;
2264 : }
2265 : else
2266 : {
2267 49 : poDS->sHeader.nClrTblOffset = 0x00;
2268 49 : poDS->sHeader.nClrTblSize = 0x00;
2269 : }
2270 :
2271 : // Add room for ROI (frame)
2272 77 : poDS->sHeader.nROIOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2273 77 : poDS->sHeader.nROISize = 0x00;
2274 77 : nCurPtr +=
2275 : sizeof(RSWFrame) +
2276 : sizeof(RSWFrameCoord) *
2277 : nMaxFramePointCount; // Allocate nMaxFramePointCount coordinates for frame
2278 :
2279 : // Add blocks flags
2280 77 : poDS->sHeader.nFlagsTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2281 77 : poDS->sHeader.nFlagsTblSize =
2282 77 : sizeof(GByte) * poDS->sHeader.nXTiles * poDS->sHeader.nYTiles;
2283 77 : nCurPtr += poDS->sHeader.nFlagsTblSize;
2284 :
2285 : // Blocks table
2286 77 : poDS->sHeader.nTileTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2287 77 : poDS->sHeader.nTileTblSize =
2288 77 : 2 * sizeof(GUInt32) * poDS->sHeader.nXTiles * poDS->sHeader.nYTiles;
2289 77 : poDS->paiTiles =
2290 77 : reinterpret_cast<GUInt32 *>(CPLCalloc(poDS->sHeader.nTileTblSize, 1));
2291 : // nCurPtr += poDS->sHeader.nTileTblSize;
2292 77 : const GUInt32 nTileSize = poDS->sHeader.nTileWidth *
2293 77 : poDS->sHeader.nTileHeight *
2294 77 : GDALGetDataTypeSizeBytes(eType);
2295 77 : poDS->sHeader.nSize =
2296 77 : poDS->paiTiles[poDS->sHeader.nTileTblSize / 4 - 2] + nTileSize;
2297 :
2298 : // Elevation units
2299 77 : poDS->sHeader.iElevationUnit = RMFStrToUnitType(poDS->pszUnitType);
2300 :
2301 77 : poDS->sHeader.iMapType = -1;
2302 77 : poDS->sHeader.iProjection = -1;
2303 77 : poDS->sHeader.iEPSGCode = -1;
2304 77 : poDS->sHeader.dfScale = dfScale;
2305 77 : poDS->sHeader.dfResolution = dfResolution;
2306 77 : poDS->sHeader.dfPixelSize = dfPixelSize;
2307 77 : poDS->sHeader.iMaskType = 0;
2308 77 : poDS->sHeader.iMaskStep = 0;
2309 77 : poDS->sHeader.iFrameFlag = 1; // 1 - Frame not using
2310 : // poDS->sHeader.nFlagsTblOffset = 0x00;
2311 : // poDS->sHeader.nFlagsTblSize = 0x00;
2312 77 : poDS->sHeader.nFileSize0 = 0x00;
2313 77 : poDS->sHeader.nFileSize1 = 0x00;
2314 77 : poDS->sHeader.iUnknown = 0;
2315 77 : poDS->sHeader.iGeorefFlag = 0;
2316 77 : poDS->sHeader.iInverse = 0;
2317 77 : poDS->sHeader.iJpegQuality = 0;
2318 77 : memset(poDS->sHeader.abyInvisibleColors, 0,
2319 : sizeof(poDS->sHeader.abyInvisibleColors));
2320 77 : poDS->sHeader.iElevationType = 0;
2321 :
2322 77 : poDS->nRasterXSize = nXSize;
2323 77 : poDS->nRasterYSize = nYSize;
2324 77 : poDS->eAccess = GA_Update;
2325 77 : poDS->nBands = nBandsIn;
2326 :
2327 77 : if (poParentDS == nullptr)
2328 : {
2329 43 : poDS->sHeader.adfElevMinMax[0] = 0.0;
2330 43 : poDS->sHeader.adfElevMinMax[1] = 0.0;
2331 43 : poDS->sHeader.dfNoData = 0.0;
2332 43 : poDS->sHeader.iCompression =
2333 43 : GetCompressionType(CSLFetchNameValue(papszParamList, "COMPRESS"));
2334 43 : if (CE_None != poDS->InitCompressorData(papszParamList))
2335 : {
2336 0 : delete poDS;
2337 0 : return nullptr;
2338 : }
2339 :
2340 43 : if (poDS->sHeader.iCompression == RMF_COMPRESSION_JPEG)
2341 : {
2342 : const char *pszJpegQuality =
2343 1 : CSLFetchNameValue(papszParamList, "JPEG_QUALITY");
2344 1 : if (pszJpegQuality == nullptr)
2345 : {
2346 1 : poDS->sHeader.iJpegQuality = 75;
2347 : }
2348 : else
2349 : {
2350 0 : int iJpegQuality = atoi(pszJpegQuality);
2351 0 : if (iJpegQuality < 10 || iJpegQuality > 100)
2352 : {
2353 0 : CPLError(CE_Failure, CPLE_IllegalArg,
2354 : "JPEG_QUALITY=%s is not a legal value in the "
2355 : "range 10-100.\n"
2356 : "Defaulting to 75",
2357 : pszJpegQuality);
2358 0 : iJpegQuality = 75;
2359 : }
2360 0 : poDS->sHeader.iJpegQuality = static_cast<GByte>(iJpegQuality);
2361 : }
2362 : }
2363 :
2364 43 : if (CE_None != poDS->SetupCompression(eType, pszFilename))
2365 : {
2366 0 : delete poDS;
2367 0 : return nullptr;
2368 : }
2369 : }
2370 : else
2371 : {
2372 34 : poDS->sHeader.adfElevMinMax[0] = poParentDS->sHeader.adfElevMinMax[0];
2373 34 : poDS->sHeader.adfElevMinMax[1] = poParentDS->sHeader.adfElevMinMax[1];
2374 34 : poDS->sHeader.dfNoData = poParentDS->sHeader.dfNoData;
2375 34 : poDS->sHeader.iCompression = poParentDS->sHeader.iCompression;
2376 34 : poDS->sHeader.iJpegQuality = poParentDS->sHeader.iJpegQuality;
2377 34 : poDS->Decompress = poParentDS->Decompress;
2378 34 : poDS->Compress = poParentDS->Compress;
2379 34 : poDS->poCompressData = poParentDS->poCompressData;
2380 : }
2381 :
2382 77 : if (nBandsIn > 1)
2383 : {
2384 13 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
2385 : }
2386 :
2387 77 : poDS->WriteHeader();
2388 :
2389 : /* -------------------------------------------------------------------- */
2390 : /* Create band information objects. */
2391 : /* -------------------------------------------------------------------- */
2392 180 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
2393 103 : poDS->SetBand(iBand, new RMFRasterBand(poDS, iBand, eType));
2394 :
2395 77 : poDS->SetupNBits();
2396 :
2397 77 : return GDALDataset::FromHandle(poDS);
2398 : }
2399 :
2400 : // GIS Panorama 11 was introduced new format for huge files (greater than 3 Gb)
2401 3859 : vsi_l_offset RMFDataset::GetFileOffset(GUInt32 iRMFOffset) const
2402 : {
2403 3859 : if (sHeader.iVersion >= RMF_VERSION_HUGE)
2404 : {
2405 1142 : return (static_cast<vsi_l_offset>(iRMFOffset)) * RMF_HUGE_OFFSET_FACTOR;
2406 : }
2407 :
2408 2717 : return static_cast<vsi_l_offset>(iRMFOffset);
2409 : }
2410 :
2411 966 : GUInt32 RMFDataset::GetRMFOffset(vsi_l_offset nFileOffset,
2412 : vsi_l_offset *pnNewFileOffset) const
2413 : {
2414 966 : if (sHeader.iVersion >= RMF_VERSION_HUGE)
2415 : {
2416 : // Round offset to next RMF_HUGE_OFFSET_FACTOR
2417 272 : const GUInt32 iRMFOffset =
2418 272 : static_cast<GUInt32>((nFileOffset + (RMF_HUGE_OFFSET_FACTOR - 1)) /
2419 : RMF_HUGE_OFFSET_FACTOR);
2420 272 : if (pnNewFileOffset != nullptr)
2421 : {
2422 205 : *pnNewFileOffset = GetFileOffset(iRMFOffset);
2423 : }
2424 272 : return iRMFOffset;
2425 : }
2426 :
2427 694 : if (pnNewFileOffset != nullptr)
2428 : {
2429 561 : *pnNewFileOffset = nFileOffset;
2430 : }
2431 694 : return static_cast<GUInt32>(nFileOffset);
2432 : }
2433 :
2434 200 : RMFDataset *RMFDataset::OpenOverview(RMFDataset *poParent,
2435 : GDALOpenInfo *poOpenInfo)
2436 : {
2437 200 : if (sHeader.nOvrOffset == 0)
2438 : {
2439 111 : return nullptr;
2440 : }
2441 :
2442 89 : if (poParent == nullptr)
2443 : {
2444 0 : return nullptr;
2445 : }
2446 :
2447 89 : vsi_l_offset nSubOffset = GetFileOffset(sHeader.nOvrOffset);
2448 :
2449 89 : CPLDebug("RMF",
2450 : "Try to open overview subfile at " CPL_FRMT_GUIB " for '%s'",
2451 : nSubOffset, poOpenInfo->pszFilename);
2452 :
2453 89 : if (!poParent->poOvrDatasets.empty())
2454 : {
2455 49 : if (poParent->GetFileOffset(poParent->sHeader.nOvrOffset) == nSubOffset)
2456 : {
2457 2 : CPLError(CE_Warning, CPLE_IllegalArg,
2458 : "Recursive subdataset list is detected. "
2459 : "Overview open failed.");
2460 2 : return nullptr;
2461 : }
2462 :
2463 58 : for (size_t n = 0; n != poParent->poOvrDatasets.size() - 1; ++n)
2464 : {
2465 13 : RMFDataset *poOvr(poParent->poOvrDatasets[n]);
2466 :
2467 13 : if (poOvr == nullptr)
2468 0 : continue;
2469 13 : if (poOvr->GetFileOffset(poOvr->sHeader.nOvrOffset) == nSubOffset)
2470 : {
2471 2 : CPLError(CE_Warning, CPLE_IllegalArg,
2472 : "Recursive subdataset list is detected. "
2473 : "Overview open failed.");
2474 2 : return nullptr;
2475 : }
2476 : }
2477 : }
2478 :
2479 85 : size_t nHeaderSize(RMF_HEADER_SIZE);
2480 : GByte *pabyNewHeader;
2481 : pabyNewHeader = static_cast<GByte *>(
2482 85 : CPLRealloc(poOpenInfo->pabyHeader, nHeaderSize + 1));
2483 85 : if (pabyNewHeader == nullptr)
2484 : {
2485 0 : CPLError(CE_Warning, CPLE_OutOfMemory,
2486 : "Can't allocate buffer for overview header");
2487 0 : return nullptr;
2488 : }
2489 :
2490 85 : poOpenInfo->pabyHeader = pabyNewHeader;
2491 85 : memset(poOpenInfo->pabyHeader, 0, nHeaderSize + 1);
2492 85 : VSIFSeekL(fp, nSubOffset, SEEK_SET);
2493 85 : poOpenInfo->nHeaderBytes =
2494 85 : static_cast<int>(VSIFReadL(poOpenInfo->pabyHeader, 1, nHeaderSize, fp));
2495 :
2496 85 : return Open(poOpenInfo, poParent, nSubOffset);
2497 : }
2498 :
2499 17 : CPLErr RMFDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
2500 : const int *panOverviewList, int nBandsIn,
2501 : const int *panBandList,
2502 : GDALProgressFunc pfnProgress,
2503 : void *pProgressData,
2504 : CSLConstList papszOptions)
2505 : {
2506 17 : bool bUseGenericHandling = false;
2507 :
2508 17 : if (GetAccess() != GA_Update)
2509 : {
2510 0 : CPLDebug("RMF", "File open for read-only accessing, "
2511 : "creating overviews externally.");
2512 :
2513 0 : bUseGenericHandling = true;
2514 : }
2515 :
2516 17 : if (bUseGenericHandling)
2517 : {
2518 0 : if (!poOvrDatasets.empty())
2519 : {
2520 0 : CPLError(CE_Failure, CPLE_NotSupported,
2521 : "Cannot add external overviews when there are already "
2522 : "internal overviews");
2523 0 : return CE_Failure;
2524 : }
2525 :
2526 0 : return GDALDataset::IBuildOverviews(
2527 : pszResampling, nOverviews, panOverviewList, nBandsIn, panBandList,
2528 0 : pfnProgress, pProgressData, papszOptions);
2529 : }
2530 :
2531 17 : if (nBandsIn != GetRasterCount())
2532 : {
2533 0 : CPLError(CE_Failure, CPLE_NotSupported,
2534 : "Generation of overviews in RMF is only "
2535 : "supported when operating on all bands. "
2536 : "Operation failed.");
2537 0 : return CE_Failure;
2538 : }
2539 :
2540 17 : if (nOverviews == 0)
2541 : {
2542 0 : if (poOvrDatasets.empty())
2543 : {
2544 0 : return GDALDataset::IBuildOverviews(
2545 : pszResampling, nOverviews, panOverviewList, nBandsIn,
2546 0 : panBandList, pfnProgress, pProgressData, papszOptions);
2547 : }
2548 0 : return CleanOverviews();
2549 : }
2550 :
2551 : // First destroy old overviews
2552 17 : if (CE_None != CleanOverviews())
2553 : {
2554 0 : return CE_Failure;
2555 : }
2556 :
2557 17 : CPLDebug("RMF", "Build overviews on dataset %d x %d size", GetRasterXSize(),
2558 : GetRasterYSize());
2559 :
2560 17 : GDALDataType eMainType = GetRasterBand(1)->GetRasterDataType();
2561 17 : RMFDataset *poParent = this;
2562 17 : double prevOvLevel = 1.0;
2563 51 : for (int n = 0; n != nOverviews; ++n)
2564 : {
2565 34 : int nOvLevel = panOverviewList[n];
2566 34 : const int nOXSize = DIV_ROUND_UP(GetRasterXSize(), nOvLevel);
2567 34 : const int nOYSize = DIV_ROUND_UP(GetRasterYSize(), nOvLevel);
2568 34 : CPLDebug("RMF", "\tCreate overview #%d size %d x %d", nOvLevel, nOXSize,
2569 : nOYSize);
2570 :
2571 : RMFDataset *poOvrDataset;
2572 34 : poOvrDataset = static_cast<RMFDataset *>(RMFDataset::Create(
2573 : nullptr, nOXSize, nOYSize, GetRasterCount(), eMainType, nullptr,
2574 : poParent, nOvLevel / prevOvLevel));
2575 :
2576 34 : if (poOvrDataset == nullptr)
2577 : {
2578 0 : CPLError(CE_Failure, CPLE_AppDefined,
2579 : "Can't create overview dataset #%d size %d x %d", nOvLevel,
2580 : nOXSize, nOYSize);
2581 0 : return CE_Failure;
2582 : }
2583 :
2584 34 : prevOvLevel = nOvLevel;
2585 34 : poParent = poOvrDataset;
2586 34 : poOvrDatasets.push_back(poOvrDataset);
2587 : }
2588 :
2589 : GDALRasterBand ***papapoOverviewBands =
2590 17 : static_cast<GDALRasterBand ***>(CPLCalloc(sizeof(void *), nBandsIn));
2591 : GDALRasterBand **papoBandList =
2592 17 : static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nBandsIn));
2593 :
2594 36 : for (int iBand = 0; iBand < nBandsIn; ++iBand)
2595 : {
2596 19 : GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
2597 :
2598 19 : papoBandList[iBand] = poBand;
2599 38 : papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
2600 19 : CPLCalloc(sizeof(void *), poBand->GetOverviewCount()));
2601 :
2602 57 : for (int i = 0; i < nOverviews; ++i)
2603 : {
2604 38 : papapoOverviewBands[iBand][i] = poBand->GetOverview(i);
2605 : }
2606 : }
2607 : #ifdef DEBUG
2608 36 : for (int iBand = 0; iBand < nBandsIn; ++iBand)
2609 : {
2610 19 : CPLDebug("RMF", "Try to create overview for #%d size %d x %d",
2611 19 : iBand + 1, papoBandList[iBand]->GetXSize(),
2612 19 : papoBandList[iBand]->GetYSize());
2613 57 : for (int i = 0; i < nOverviews; ++i)
2614 : {
2615 38 : CPLDebug("RMF", "\t%d x %d",
2616 38 : papapoOverviewBands[iBand][i]->GetXSize(),
2617 38 : papapoOverviewBands[iBand][i]->GetYSize());
2618 : }
2619 : }
2620 : #endif // DEBUG
2621 : CPLErr res;
2622 17 : res = GDALRegenerateOverviewsMultiBand(
2623 : nBandsIn, papoBandList, nOverviews, papapoOverviewBands, pszResampling,
2624 : pfnProgress, pProgressData, papszOptions);
2625 :
2626 36 : for (int iBand = 0; iBand < nBandsIn; ++iBand)
2627 : {
2628 19 : CPLFree(papapoOverviewBands[iBand]);
2629 : }
2630 :
2631 17 : CPLFree(papapoOverviewBands);
2632 17 : CPLFree(papoBandList);
2633 :
2634 17 : return res;
2635 : }
2636 :
2637 68 : CPLErr RMFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2638 : int nXSize, int nYSize, void *pData, int nBufXSize,
2639 : int nBufYSize, GDALDataType eBufType,
2640 : int nBandCount, BANDMAP_TYPE panBandMap,
2641 : GSpacing nPixelSpace, GSpacing nLineSpace,
2642 : GSpacing nBandSpace,
2643 : GDALRasterIOExtraArg *psExtraArg)
2644 : {
2645 : #ifdef DEBUG
2646 68 : CPLDebug("RMF", "Dataset %p, %s %d %d %d %d, %d %d", this,
2647 : (eRWFlag == GF_Read ? "Read" : "Write"), nXOff, nYOff, nXSize,
2648 : nYSize, nBufXSize, nBufYSize);
2649 : #endif // DEBUG
2650 68 : if (eRWFlag == GF_Read && poCompressData != nullptr &&
2651 0 : poCompressData->oThreadPool.GetThreadCount() > 0)
2652 : {
2653 0 : poCompressData->oThreadPool.WaitCompletion();
2654 : }
2655 :
2656 68 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
2657 : nBufXSize, nBufYSize, eBufType, nBandCount,
2658 : panBandMap, nPixelSpace, nLineSpace,
2659 68 : nBandSpace, psExtraArg);
2660 : }
2661 :
2662 238 : vsi_l_offset RMFDataset::GetLastOffset() const
2663 : {
2664 238 : vsi_l_offset nLastTileOff = 0;
2665 238 : GUInt32 nTiles(sHeader.nTileTblSize / sizeof(GUInt32));
2666 :
2667 768 : for (GUInt32 n = 0; n < nTiles; n += 2)
2668 : {
2669 530 : vsi_l_offset nTileOffset = GetFileOffset(paiTiles[n]);
2670 530 : GUInt32 nTileBytes = paiTiles[n + 1];
2671 530 : nLastTileOff = std::max(nLastTileOff, nTileOffset + nTileBytes);
2672 : }
2673 :
2674 238 : nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nROIOffset) +
2675 238 : sHeader.nROISize);
2676 238 : nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nClrTblOffset) +
2677 238 : sHeader.nClrTblSize);
2678 238 : nLastTileOff =
2679 238 : std::max(nLastTileOff,
2680 238 : GetFileOffset(sHeader.nTileTblOffset) + sHeader.nTileTblSize);
2681 238 : nLastTileOff =
2682 238 : std::max(nLastTileOff, GetFileOffset(sHeader.nFlagsTblOffset) +
2683 238 : sHeader.nFlagsTblSize);
2684 238 : nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nExtHdrOffset) +
2685 238 : sHeader.nExtHdrSize);
2686 238 : return nLastTileOff;
2687 : }
2688 :
2689 17 : CPLErr RMFDataset::CleanOverviews()
2690 : {
2691 17 : if (sHeader.nOvrOffset == 0)
2692 : {
2693 13 : return CE_None;
2694 : }
2695 :
2696 4 : if (GetAccess() != GA_Update)
2697 : {
2698 0 : CPLError(CE_Failure, CPLE_NotSupported,
2699 : "File open for read-only accessing, "
2700 : "overviews cleanup failed.");
2701 0 : return CE_Failure;
2702 : }
2703 :
2704 4 : if (poParentDS != nullptr)
2705 : {
2706 0 : CPLError(CE_Failure, CPLE_NotSupported,
2707 : "Overviews cleanup for non-root dataset is not possible.");
2708 0 : return CE_Failure;
2709 : }
2710 :
2711 12 : for (size_t n = 0; n != poOvrDatasets.size(); ++n)
2712 : {
2713 8 : GDALClose(poOvrDatasets[n]);
2714 : }
2715 4 : poOvrDatasets.clear();
2716 :
2717 4 : vsi_l_offset nLastTileOff = GetLastOffset();
2718 :
2719 4 : if (0 != VSIFSeekL(fp, 0, SEEK_END))
2720 : {
2721 0 : CPLError(CE_Failure, CPLE_FileIO,
2722 : "Failed to seek to end of file, "
2723 : "overviews cleanup failed.");
2724 : }
2725 :
2726 4 : vsi_l_offset nFileSize = VSIFTellL(fp);
2727 4 : if (nFileSize < nLastTileOff)
2728 : {
2729 0 : CPLError(CE_Failure, CPLE_FileIO,
2730 : "Invalid file offset, "
2731 : "overviews cleanup failed.");
2732 0 : return CE_Failure;
2733 : }
2734 :
2735 4 : CPLDebug("RMF", "Truncate to " CPL_FRMT_GUIB, nLastTileOff);
2736 4 : CPLDebug("RMF", "File size: " CPL_FRMT_GUIB, nFileSize);
2737 :
2738 4 : if (0 != VSIFTruncateL(fp, nLastTileOff))
2739 : {
2740 0 : CPLError(CE_Failure, CPLE_FileIO,
2741 : "Failed to truncate file, "
2742 : "overviews cleanup failed.");
2743 0 : return CE_Failure;
2744 : }
2745 :
2746 4 : sHeader.nOvrOffset = 0;
2747 4 : bHeaderDirty = true;
2748 :
2749 4 : return CE_None;
2750 : }
2751 :
2752 : /************************************************************************/
2753 : /* GetCompressionType() */
2754 : /************************************************************************/
2755 :
2756 43 : GByte RMFDataset::GetCompressionType(const char *pszCompressName)
2757 : {
2758 43 : if (pszCompressName == nullptr || EQUAL(pszCompressName, "NONE"))
2759 : {
2760 35 : return RMF_COMPRESSION_NONE;
2761 : }
2762 8 : else if (EQUAL(pszCompressName, "LZW"))
2763 : {
2764 5 : return RMF_COMPRESSION_LZW;
2765 : }
2766 3 : else if (EQUAL(pszCompressName, "JPEG"))
2767 : {
2768 1 : return RMF_COMPRESSION_JPEG;
2769 : }
2770 2 : else if (EQUAL(pszCompressName, "RMF_DEM"))
2771 : {
2772 2 : return RMF_COMPRESSION_DEM;
2773 : }
2774 :
2775 0 : CPLError(CE_Failure, CPLE_AppDefined,
2776 : "RMF: Unknown compression scheme <%s>.\n"
2777 : "Defaults to NONE compression.",
2778 : pszCompressName);
2779 0 : return RMF_COMPRESSION_NONE;
2780 : }
2781 :
2782 : /************************************************************************/
2783 : /* SetupCompression() */
2784 : /************************************************************************/
2785 :
2786 243 : int RMFDataset::SetupCompression(GDALDataType eType, const char *pszFilename)
2787 : {
2788 : /* -------------------------------------------------------------------- */
2789 : /* XXX: The DEM compression method seems to be only applicable */
2790 : /* to Int32 data. */
2791 : /* -------------------------------------------------------------------- */
2792 243 : if (sHeader.iCompression == RMF_COMPRESSION_NONE)
2793 : {
2794 173 : Decompress = nullptr;
2795 173 : Compress = nullptr;
2796 : }
2797 70 : else if (sHeader.iCompression == RMF_COMPRESSION_LZW)
2798 : {
2799 57 : Decompress = &LZWDecompress;
2800 57 : Compress = &LZWCompress;
2801 57 : SetMetadataItem("COMPRESSION", "LZW", "IMAGE_STRUCTURE");
2802 : }
2803 13 : else if (sHeader.iCompression == RMF_COMPRESSION_JPEG)
2804 : {
2805 3 : if (eType != GDT_Byte || nBands != RMF_JPEG_BAND_COUNT ||
2806 3 : sHeader.nBitDepth != 24)
2807 : {
2808 0 : CPLError(CE_Failure, CPLE_AppDefined,
2809 : "RMF support only 24 bpp JPEG compressed files.");
2810 0 : return CE_Failure;
2811 : }
2812 : #ifdef HAVE_LIBJPEG
2813 6 : CPLString oBuf;
2814 3 : oBuf.Printf("%d", sHeader.iJpegQuality);
2815 3 : Decompress = &JPEGDecompress;
2816 3 : Compress = &JPEGCompress;
2817 3 : SetMetadataItem("JPEG_QUALITY", oBuf.c_str(), "IMAGE_STRUCTURE");
2818 3 : SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
2819 : #else // HAVE_LIBJPEG
2820 : CPLError(CE_Failure, CPLE_AppDefined,
2821 : "JPEG codec is needed to open <%s>.\n"
2822 : "Please rebuild GDAL with libjpeg support.",
2823 : pszFilename);
2824 : return CE_Failure;
2825 : #endif // HAVE_LIBJPEG
2826 : }
2827 10 : else if (sHeader.iCompression == RMF_COMPRESSION_DEM &&
2828 10 : eType == GDT_Int32 && nBands == RMF_DEM_BAND_COUNT)
2829 : {
2830 10 : Decompress = &DEMDecompress;
2831 10 : Compress = &DEMCompress;
2832 10 : SetMetadataItem("COMPRESSION", "RMF_DEM", "IMAGE_STRUCTURE");
2833 : }
2834 : else
2835 : {
2836 0 : CPLError(CE_Failure, CPLE_AppDefined,
2837 0 : "Unknown compression #%d at file <%s>.", sHeader.iCompression,
2838 : pszFilename);
2839 0 : return CE_Failure;
2840 : }
2841 :
2842 243 : return CE_None;
2843 : }
2844 :
2845 190 : void RMFDataset::WriteTileJobFunc(void *pData)
2846 : {
2847 190 : RMFCompressionJob *psJob = static_cast<RMFCompressionJob *>(pData);
2848 190 : RMFDataset *poDS = psJob->poDS;
2849 :
2850 : GByte *pabyTileData;
2851 : size_t nTileSize;
2852 :
2853 190 : if (poDS->Compress)
2854 : {
2855 : // RMF doesn't store compressed tiles with size greater than 80% of
2856 : // uncompressed size
2857 131 : GUInt32 nMaxCompressedTileSize =
2858 131 : static_cast<GUInt32>((psJob->nUncompressedBytes * 8) / 10);
2859 : size_t nCompressedBytes =
2860 262 : poDS->Compress(psJob->pabyUncompressedData,
2861 131 : static_cast<GUInt32>(psJob->nUncompressedBytes),
2862 : psJob->pabyCompressedData, nMaxCompressedTileSize,
2863 : psJob->nXSize, psJob->nYSize, poDS);
2864 131 : if (nCompressedBytes == 0)
2865 : {
2866 28 : pabyTileData = psJob->pabyUncompressedData;
2867 28 : nTileSize = psJob->nUncompressedBytes;
2868 : }
2869 : else
2870 : {
2871 103 : pabyTileData = psJob->pabyCompressedData;
2872 103 : nTileSize = nCompressedBytes;
2873 : }
2874 : }
2875 : else
2876 : {
2877 59 : pabyTileData = psJob->pabyUncompressedData;
2878 59 : nTileSize = psJob->nUncompressedBytes;
2879 : }
2880 :
2881 : {
2882 190 : CPLMutexHolder oHolder(poDS->poCompressData->hWriteTileMutex);
2883 190 : psJob->eResult = poDS->WriteRawTile(
2884 : psJob->nBlockXOff, psJob->nBlockYOff, pabyTileData, nTileSize);
2885 : }
2886 190 : if (poDS->poCompressData->oThreadPool.GetThreadCount() > 0)
2887 : {
2888 188 : CPLMutexHolder oHolder(poDS->poCompressData->hReadyJobMutex);
2889 94 : poDS->poCompressData->asReadyJobs.push_back(psJob);
2890 : }
2891 190 : }
2892 :
2893 55 : CPLErr RMFDataset::InitCompressorData(char **papszParamList)
2894 : {
2895 : const char *pszNumThreads =
2896 55 : CSLFetchNameValue(papszParamList, "NUM_THREADS");
2897 55 : if (pszNumThreads == nullptr)
2898 51 : pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
2899 :
2900 55 : int nThreads = 0;
2901 55 : if (pszNumThreads != nullptr)
2902 : {
2903 8 : nThreads = EQUAL(pszNumThreads, "ALL_CPUS") ? CPLGetNumCPUs()
2904 4 : : atoi(pszNumThreads);
2905 : }
2906 :
2907 55 : if (nThreads < 0)
2908 : {
2909 0 : nThreads = 0;
2910 : }
2911 55 : if (nThreads > 1024)
2912 : {
2913 0 : nThreads = 1024;
2914 : }
2915 :
2916 55 : poCompressData = std::make_shared<RMFCompressData>();
2917 55 : if (nThreads > 0)
2918 : {
2919 3 : if (!poCompressData->oThreadPool.Setup(nThreads, nullptr, nullptr))
2920 : {
2921 0 : CPLError(CE_Failure, CPLE_AppDefined,
2922 : "Can't setup %d compressor threads", nThreads);
2923 0 : return CE_Failure;
2924 : }
2925 : }
2926 :
2927 55 : poCompressData->asJobs.resize(nThreads + 1);
2928 :
2929 55 : size_t nMaxTileBytes =
2930 55 : sHeader.nTileWidth * sHeader.nTileHeight * sHeader.nBitDepth / 8;
2931 : size_t nCompressBufferSize =
2932 55 : 2 * nMaxTileBytes * poCompressData->asJobs.size();
2933 110 : poCompressData->pabyBuffers =
2934 55 : static_cast<GByte *>(VSIMalloc(nCompressBufferSize));
2935 :
2936 55 : CPLDebug("RMF", "Setup %d compressor threads and allocate %lu bytes buffer",
2937 : nThreads, static_cast<unsigned long>(nCompressBufferSize));
2938 55 : if (poCompressData->pabyBuffers == nullptr)
2939 : {
2940 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2941 : "Can't allocate compress buffer of size %lu.",
2942 : static_cast<unsigned long>(nCompressBufferSize));
2943 0 : return CE_Failure;
2944 : }
2945 :
2946 122 : for (size_t i = 0; i != poCompressData->asJobs.size(); ++i)
2947 : {
2948 67 : RMFCompressionJob &sJob(poCompressData->asJobs[i]);
2949 67 : sJob.pabyCompressedData =
2950 67 : poCompressData->pabyBuffers + 2 * i * nMaxTileBytes;
2951 67 : sJob.pabyUncompressedData = sJob.pabyCompressedData + nMaxTileBytes;
2952 67 : poCompressData->asReadyJobs.push_back(&sJob);
2953 : }
2954 :
2955 55 : if (nThreads > 0)
2956 : {
2957 3 : poCompressData->hReadyJobMutex = CPLCreateMutex();
2958 3 : CPLReleaseMutex(poCompressData->hReadyJobMutex);
2959 3 : poCompressData->hWriteTileMutex = CPLCreateMutex();
2960 3 : CPLReleaseMutex(poCompressData->hWriteTileMutex);
2961 : }
2962 :
2963 55 : return CE_None;
2964 : }
2965 :
2966 190 : CPLErr RMFDataset::WriteTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
2967 : size_t nBytes, GUInt32 nRawXSize,
2968 : GUInt32 nRawYSize)
2969 : {
2970 190 : RMFCompressionJob *poJob = nullptr;
2971 190 : if (poCompressData == nullptr)
2972 : {
2973 0 : CPLError(CE_Failure, CPLE_AppDefined, "RMF: Compress data is null");
2974 0 : return CE_Failure;
2975 : }
2976 :
2977 190 : if (poCompressData->oThreadPool.GetThreadCount() > 0)
2978 : {
2979 94 : size_t nJobs(poCompressData->asJobs.size());
2980 :
2981 94 : poCompressData->oThreadPool.WaitCompletion(static_cast<int>(nJobs - 1));
2982 :
2983 188 : CPLMutexHolder oHolder(poCompressData->hReadyJobMutex);
2984 94 : CPLAssert(!poCompressData->asReadyJobs.empty());
2985 94 : poJob = poCompressData->asReadyJobs.front();
2986 94 : poCompressData->asReadyJobs.pop_front();
2987 : }
2988 : else
2989 : {
2990 96 : poJob = poCompressData->asReadyJobs.front();
2991 : }
2992 :
2993 190 : if (poJob->eResult != CE_None)
2994 : {
2995 : // One of the previous jobs is not done.
2996 : // Detailed debug message is already emitted from WriteRawTile
2997 0 : return poJob->eResult;
2998 : }
2999 190 : poJob->poDS = this;
3000 190 : poJob->eResult = CE_Failure;
3001 190 : poJob->nBlockXOff = nBlockXOff;
3002 190 : poJob->nBlockYOff = nBlockYOff;
3003 190 : poJob->nUncompressedBytes = nBytes;
3004 190 : poJob->nXSize = nRawXSize;
3005 190 : poJob->nYSize = nRawYSize;
3006 :
3007 190 : memcpy(poJob->pabyUncompressedData, pabyData, nBytes);
3008 :
3009 190 : if (poCompressData->oThreadPool.GetThreadCount() > 0)
3010 : {
3011 94 : if (!poCompressData->oThreadPool.SubmitJob(WriteTileJobFunc, poJob))
3012 : {
3013 0 : CPLError(CE_Failure, CPLE_NotSupported,
3014 : "Can't submit job to thread pool.");
3015 0 : return CE_Failure;
3016 : }
3017 : }
3018 : else
3019 : {
3020 96 : WriteTileJobFunc(poJob);
3021 96 : if (poJob->eResult != CE_None)
3022 : {
3023 0 : return poJob->eResult;
3024 : }
3025 : }
3026 :
3027 190 : return CE_None;
3028 : }
3029 :
3030 190 : CPLErr RMFDataset::WriteRawTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
3031 : size_t nTileBytes)
3032 : {
3033 190 : CPLAssert(nBlockXOff >= 0 && nBlockYOff >= 0 && pabyData != nullptr &&
3034 : nTileBytes > 0);
3035 :
3036 190 : const GUInt32 nTile = nBlockYOff * nXTiles + nBlockXOff;
3037 :
3038 190 : vsi_l_offset nTileOffset = GetFileOffset(paiTiles[2 * nTile]);
3039 190 : size_t nTileSize = static_cast<size_t>(paiTiles[2 * nTile + 1]);
3040 :
3041 190 : if (nTileOffset && nTileSize <= nTileBytes)
3042 : {
3043 0 : if (VSIFSeekL(fp, nTileOffset, SEEK_SET) < 0)
3044 : {
3045 0 : CPLError(
3046 : CE_Failure, CPLE_FileIO,
3047 : "Can't seek to offset %ld in output file to write data.\n%s",
3048 0 : static_cast<long>(nTileOffset), VSIStrerror(errno));
3049 0 : return CE_Failure;
3050 : }
3051 : }
3052 : else
3053 : {
3054 190 : if (VSIFSeekL(fp, 0, SEEK_END) < 0)
3055 : {
3056 0 : CPLError(
3057 : CE_Failure, CPLE_FileIO,
3058 : "Can't seek to offset %ld in output file to write data.\n%s",
3059 0 : static_cast<long>(nTileOffset), VSIStrerror(errno));
3060 0 : return CE_Failure;
3061 : }
3062 190 : nTileOffset = VSIFTellL(fp);
3063 190 : vsi_l_offset nNewTileOffset = 0;
3064 190 : paiTiles[2 * nTile] = GetRMFOffset(nTileOffset, &nNewTileOffset);
3065 :
3066 190 : if (nTileOffset != nNewTileOffset)
3067 : {
3068 23 : if (VSIFSeekL(fp, nNewTileOffset, SEEK_SET) < 0)
3069 : {
3070 0 : CPLError(CE_Failure, CPLE_FileIO,
3071 : "Can't seek to offset %ld in output file to "
3072 : "write data.\n%s",
3073 0 : static_cast<long>(nNewTileOffset), VSIStrerror(errno));
3074 0 : return CE_Failure;
3075 : }
3076 : }
3077 190 : bHeaderDirty = true;
3078 : }
3079 :
3080 : #ifdef CPL_MSB
3081 : // Compressed tiles are already with proper byte order
3082 : if (eRMFType == RMFT_MTW && sHeader.iCompression == RMF_COMPRESSION_NONE)
3083 : {
3084 : // Byte swap can be done in place
3085 : if (sHeader.nBitDepth == 16)
3086 : {
3087 : for (size_t i = 0; i < nTileBytes; i += 2)
3088 : CPL_SWAP16PTR(pabyData + i);
3089 : }
3090 : else if (sHeader.nBitDepth == 32)
3091 : {
3092 : for (size_t i = 0; i < nTileBytes; i += 4)
3093 : CPL_SWAP32PTR(pabyData + i);
3094 : }
3095 : else if (sHeader.nBitDepth == 64)
3096 : {
3097 : for (size_t i = 0; i < nTileBytes; i += 8)
3098 : CPL_SWAPDOUBLE(pabyData + i);
3099 : }
3100 : }
3101 : #endif
3102 :
3103 190 : bool bOk = (VSIFWriteL(pabyData, 1, nTileBytes, fp) == nTileBytes);
3104 :
3105 190 : if (!bOk)
3106 : {
3107 0 : CPLError(CE_Failure, CPLE_FileIO,
3108 : "Can't write tile with X offset %d and Y offset %d.\n%s",
3109 0 : nBlockXOff, nBlockYOff, VSIStrerror(errno));
3110 0 : return CE_Failure;
3111 : }
3112 :
3113 190 : paiTiles[2 * nTile + 1] = static_cast<GUInt32>(nTileBytes);
3114 190 : bHeaderDirty = true;
3115 :
3116 190 : return CE_None;
3117 : }
3118 :
3119 352 : CPLErr RMFDataset::ReadTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
3120 : size_t nRawBytes, GUInt32 nRawXSize,
3121 : GUInt32 nRawYSize, bool &bNullTile)
3122 : {
3123 352 : bNullTile = false;
3124 :
3125 352 : const GUInt32 nTile = nBlockYOff * nXTiles + nBlockXOff;
3126 352 : if (2 * nTile + 1 >= sHeader.nTileTblSize / sizeof(GUInt32))
3127 : {
3128 0 : return CE_Failure;
3129 : }
3130 352 : vsi_l_offset nTileOffset = GetFileOffset(paiTiles[2 * nTile]);
3131 352 : GUInt32 nTileBytes = paiTiles[2 * nTile + 1];
3132 : // RMF doesn't store compressed tiles with size greater than 80% of
3133 : // uncompressed size. But just in case, select twice as many.
3134 352 : GUInt32 nMaxTileBytes =
3135 352 : 2 * sHeader.nTileWidth * sHeader.nTileHeight * sHeader.nBitDepth / 8;
3136 :
3137 352 : if (nTileBytes >= nMaxTileBytes)
3138 : {
3139 0 : CPLError(CE_Failure, CPLE_AppDefined,
3140 : "Invalid tile size %lu at offset %ld. Must be less than %lu",
3141 : static_cast<unsigned long>(nTileBytes),
3142 : static_cast<long>(nTileOffset),
3143 : static_cast<unsigned long>(nMaxTileBytes));
3144 0 : return CE_Failure;
3145 : }
3146 :
3147 352 : if (nTileOffset == 0)
3148 : {
3149 1 : bNullTile = true;
3150 1 : return CE_None;
3151 : }
3152 :
3153 : #ifdef DEBUG
3154 351 : CPLDebug("RMF", "Read RawSize [%d, %d], nTileBytes %d, nRawBytes %d",
3155 : nRawXSize, nRawYSize, static_cast<int>(nTileBytes),
3156 : static_cast<int>(nRawBytes));
3157 : #endif // DEBUG
3158 :
3159 351 : if (VSIFSeekL(fp, nTileOffset, SEEK_SET) < 0)
3160 : {
3161 : // XXX: We will not report error here, because file just may be
3162 : // in update state and data for this block will be available later
3163 0 : if (eAccess == GA_Update)
3164 0 : return CE_None;
3165 :
3166 0 : CPLError(CE_Failure, CPLE_FileIO,
3167 : "Can't seek to offset %ld in input file to read data.\n%s",
3168 0 : static_cast<long>(nTileOffset), VSIStrerror(errno));
3169 0 : return CE_Failure;
3170 : }
3171 :
3172 351 : if (Decompress == nullptr || nTileBytes == nRawBytes)
3173 : {
3174 166 : if (nTileBytes != nRawBytes)
3175 : {
3176 0 : CPLError(CE_Failure, CPLE_AppDefined,
3177 : "RMF: Invalid tile size %lu, expected %lu",
3178 : static_cast<unsigned long>(nTileBytes),
3179 : static_cast<unsigned long>(nRawBytes));
3180 0 : return CE_Failure;
3181 : }
3182 :
3183 166 : if (VSIFReadL(pabyData, 1, nRawBytes, fp) < nRawBytes)
3184 : {
3185 0 : CPLError(CE_Failure, CPLE_FileIO,
3186 : "RMF: Can't read at offset %lu from input file.\n%s",
3187 : static_cast<unsigned long>(nTileOffset),
3188 0 : VSIStrerror(errno));
3189 0 : return CE_Failure;
3190 : }
3191 :
3192 : #ifdef CPL_MSB
3193 : if (eRMFType == RMFT_MTW)
3194 : {
3195 : if (sHeader.nBitDepth == 16)
3196 : {
3197 : for (GUInt32 i = 0; i < nRawBytes; i += 2)
3198 : CPL_SWAP16PTR(pabyData + i);
3199 : }
3200 : else if (sHeader.nBitDepth == 32)
3201 : {
3202 : for (GUInt32 i = 0; i < nRawBytes; i += 4)
3203 : CPL_SWAP32PTR(pabyData + i);
3204 : }
3205 : else if (sHeader.nBitDepth == 64)
3206 : {
3207 : for (GUInt32 i = 0; i < nRawBytes; i += 8)
3208 : CPL_SWAPDOUBLE(pabyData + i);
3209 : }
3210 : }
3211 : #endif
3212 166 : return CE_None;
3213 : }
3214 :
3215 185 : if (pabyDecompressBuffer == nullptr)
3216 : {
3217 21 : pabyDecompressBuffer =
3218 21 : static_cast<GByte *>(VSIMalloc(std::max(1U, nMaxTileBytes)));
3219 21 : if (!pabyDecompressBuffer)
3220 : {
3221 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
3222 : "Can't allocate decompress buffer of size %lu.\n%s",
3223 : static_cast<unsigned long>(nMaxTileBytes),
3224 0 : VSIStrerror(errno));
3225 0 : return CE_Failure;
3226 : }
3227 : }
3228 :
3229 185 : if (VSIFReadL(pabyDecompressBuffer, 1, nTileBytes, fp) < nTileBytes)
3230 : {
3231 0 : CPLError(CE_Failure, CPLE_FileIO,
3232 : "RMF: Can't read at offset %lu from input file.\n%s",
3233 0 : static_cast<unsigned long>(nTileOffset), VSIStrerror(errno));
3234 0 : return CE_Failure;
3235 : }
3236 :
3237 : size_t nDecompressedSize =
3238 185 : Decompress(pabyDecompressBuffer, nTileBytes, pabyData,
3239 : static_cast<GUInt32>(nRawBytes), nRawXSize, nRawYSize);
3240 :
3241 185 : if (nDecompressedSize != static_cast<size_t>(nRawBytes))
3242 : {
3243 0 : CPLError(CE_Failure, CPLE_FileIO,
3244 : "Can't decompress tile xOff %d yOff %d. "
3245 : "Raw tile size is %lu but decompressed is %lu. "
3246 : "Compressed tile size is %lu",
3247 : nBlockXOff, nBlockYOff, static_cast<unsigned long>(nRawBytes),
3248 : static_cast<unsigned long>(nDecompressedSize),
3249 : static_cast<unsigned long>(nTileBytes));
3250 0 : return CE_Failure;
3251 : }
3252 : // We don't need to swap bytes here,
3253 : // because decompressed data is in proper byte order
3254 185 : return CE_None;
3255 : }
3256 :
3257 277 : void RMFDataset::SetupNBits()
3258 : {
3259 277 : int nBitDepth = 0;
3260 277 : if (sHeader.nBitDepth < 8 && nBands == 1)
3261 : {
3262 6 : nBitDepth = static_cast<int>(sHeader.nBitDepth);
3263 : }
3264 271 : else if (sHeader.nBitDepth == 16 && nBands == 3 && eRMFType == RMFT_RSW)
3265 : {
3266 0 : nBitDepth = 5;
3267 : }
3268 :
3269 277 : if (nBitDepth > 0)
3270 : {
3271 6 : char szNBits[32] = {};
3272 6 : snprintf(szNBits, sizeof(szNBits), "%d", nBitDepth);
3273 12 : for (int iBand = 1; iBand <= nBands; iBand++)
3274 : {
3275 6 : GetRasterBand(iBand)->SetMetadataItem("NBITS", szNBits,
3276 6 : "IMAGE_STRUCTURE");
3277 : }
3278 : }
3279 277 : }
3280 :
3281 : /************************************************************************/
3282 : /* GDALRegister_RMF() */
3283 : /************************************************************************/
3284 :
3285 1935 : void GDALRegister_RMF()
3286 :
3287 : {
3288 1935 : if (GDALGetDriverByName("RMF") != nullptr)
3289 282 : return;
3290 :
3291 1653 : GDALDriver *poDriver = new GDALDriver();
3292 :
3293 1653 : poDriver->SetDescription("RMF");
3294 1653 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
3295 1653 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Raster Matrix Format");
3296 1653 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/rmf.html");
3297 1653 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rsw");
3298 1653 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
3299 1653 : "Byte Int16 Int32 Float64");
3300 1653 : poDriver->SetMetadataItem(
3301 : GDAL_DMD_CREATIONOPTIONLIST,
3302 : "<CreationOptionList>"
3303 : " <Option name='MTW' type='boolean' description='Create MTW DEM "
3304 : "matrix'/>"
3305 : " <Option name='BLOCKXSIZE' type='int' description='Tile Width'/>"
3306 : " <Option name='BLOCKYSIZE' type='int' description='Tile Height'/>"
3307 : " <Option name='RMFHUGE' type='string-select' description='Creation "
3308 : "of huge RMF file (Supported by GIS Panorama since v11)'>"
3309 : " <Value>NO</Value>"
3310 : " <Value>YES</Value>"
3311 : " <Value>IF_SAFER</Value>"
3312 : " </Option>"
3313 : " <Option name='COMPRESS' type='string-select' default='NONE'>"
3314 : " <Value>NONE</Value>"
3315 : " <Value>LZW</Value>"
3316 : " <Value>JPEG</Value>"
3317 : " <Value>RMF_DEM</Value>"
3318 : " </Option>"
3319 : " <Option name='JPEG_QUALITY' type='int' description='JPEG quality "
3320 : "1-100' default='75'/>"
3321 : " <Option name='NUM_THREADS' type='string' description='Number of "
3322 : "worker threads for compression. Can be set to ALL_CPUS' default='1'/>"
3323 1653 : "</CreationOptionList>");
3324 1653 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
3325 :
3326 1653 : poDriver->pfnIdentify = RMFDataset::Identify;
3327 1653 : poDriver->pfnOpen = RMFDataset::Open;
3328 1653 : poDriver->pfnCreate = RMFDataset::Create;
3329 1653 : poDriver->SetMetadataItem(
3330 : GDAL_DMD_OPENOPTIONLIST,
3331 : "<OpenOptionList>"
3332 : " <Option name='RMF_SET_VERTCS' type='string' description='Layers "
3333 : "spatial reference will include vertical coordinate system description "
3334 : "if exist' default='NO'/>"
3335 1653 : "</OpenOptionList>");
3336 :
3337 1653 : GetGDALDriverManager()->RegisterDriver(poDriver);
3338 : }
3339 :
3340 : /************************************************************************/
3341 : /* RMFCompressData */
3342 : /************************************************************************/
3343 :
3344 55 : RMFCompressData::RMFCompressData() : pabyBuffers(nullptr)
3345 : {
3346 55 : }
3347 :
3348 55 : RMFCompressData::~RMFCompressData()
3349 : {
3350 55 : if (pabyBuffers != nullptr)
3351 : {
3352 55 : VSIFree(pabyBuffers);
3353 : }
3354 :
3355 55 : if (hWriteTileMutex != nullptr)
3356 : {
3357 3 : CPLDestroyMutex(hWriteTileMutex);
3358 : }
3359 :
3360 55 : if (hReadyJobMutex != nullptr)
3361 : {
3362 3 : CPLDestroyMutex(hReadyJobMutex);
3363 : }
3364 55 : }
3365 :
3366 : GDALSuggestedBlockAccessPattern
3367 21 : RMFRasterBand::GetSuggestedBlockAccessPattern() const
3368 : {
3369 21 : return GSBAP_RANDOM;
3370 : }
3371 :
3372 1507 : CPLErr RMFDataset::SetMetadataItem(const char *pszName, const char *pszValue,
3373 : const char *pszDomain)
3374 : {
3375 1507 : if (GetAccess() == GA_Update)
3376 : {
3377 190 : CPLDebug("RMF", "SetMetadataItem: %s=%s", pszName, pszValue);
3378 190 : if (EQUAL(pszName, MD_NAME_KEY))
3379 : {
3380 20 : memcpy(sHeader.byName, pszValue,
3381 : CPLStrnlen(pszValue, RMF_NAME_SIZE));
3382 20 : bHeaderDirty = true;
3383 : }
3384 170 : else if (EQUAL(pszName, MD_SCALE_KEY) && CPLStrnlen(pszValue, 10) > 4)
3385 : {
3386 20 : sHeader.dfScale = atof(pszValue + 4);
3387 20 : sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
3388 20 : bHeaderDirty = true;
3389 : }
3390 150 : else if (EQUAL(pszName, MD_FRAME_KEY))
3391 : {
3392 0 : bHeaderDirty = true;
3393 : }
3394 : }
3395 1507 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
3396 : }
3397 :
3398 39 : CPLErr RMFDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
3399 : {
3400 39 : if (GetAccess() == GA_Update)
3401 : {
3402 39 : auto pszName = CSLFetchNameValue(papszMetadata, MD_NAME_KEY);
3403 39 : if (pszName != nullptr)
3404 : {
3405 21 : memcpy(sHeader.byName, pszName, CPLStrnlen(pszName, RMF_NAME_SIZE));
3406 21 : bHeaderDirty = true;
3407 :
3408 21 : CPLDebug("RMF", "SetMetadata: %s", pszName);
3409 : }
3410 39 : auto pszScale = CSLFetchNameValue(papszMetadata, MD_SCALE_KEY);
3411 39 : if (pszScale != nullptr && CPLStrnlen(pszScale, 10) > 4)
3412 : {
3413 21 : sHeader.dfScale = atof(pszScale + 4);
3414 21 : sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
3415 21 : bHeaderDirty = true;
3416 :
3417 21 : CPLDebug("RMF", "SetMetadata: %s", pszScale);
3418 : }
3419 39 : auto pszFrame = CSLFetchNameValue(papszMetadata, MD_FRAME_KEY);
3420 39 : if (pszFrame != nullptr)
3421 : {
3422 2 : bHeaderDirty = true;
3423 :
3424 2 : CPLDebug("RMF", "SetMetadata: %s", pszFrame);
3425 : }
3426 : }
3427 39 : return GDALDataset::SetMetadata(papszMetadata, pszDomain);
3428 : }
|