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(double *padfTransform)
773 : {
774 47 : memcpy(padfTransform, adfGeoTransform.data(), sizeof(adfGeoTransform));
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(double *padfTransform)
787 : {
788 41 : memcpy(adfGeoTransform.data(), padfTransform, sizeof(adfGeoTransform));
789 41 : sHeader.dfPixelSize = adfGeoTransform[1];
790 41 : if (sHeader.dfPixelSize != 0.0)
791 41 : sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
792 41 : sHeader.dfLLX = adfGeoTransform[0];
793 41 : sHeader.dfLLY = adfGeoTransform[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 : std::array<double, 6> adfReverseGeoTransform = {0};
908 2 : if (GDALInvGeoTransform(adfGeoTransform.data(),
909 2 : adfReverseGeoTransform.data()) == TRUE)
910 : {
911 2 : OGRPolygon *poFramePoly = poFrameGeom->toPolygon();
912 2 : if (!poFramePoly->IsEmpty())
913 : {
914 : OGRLinearRing *poFrameRing =
915 2 : poFramePoly->getExteriorRing();
916 12 : for (int i = 0; i < poFrameRing->getNumPoints(); i++)
917 : {
918 10 : int nX = int(adfReverseGeoTransform[0] +
919 10 : poFrameRing->getX(i) *
920 10 : adfReverseGeoTransform[1] -
921 10 : 0.5);
922 10 : int nY = int(adfReverseGeoTransform[3] +
923 10 : poFrameRing->getY(i) *
924 10 : adfReverseGeoTransform[5] -
925 10 : 0.5);
926 :
927 10 : CPLDebug("RMF", "X: %d, Y: %d", nX, nY);
928 :
929 10 : astFrameCoords.push_back({nX, nY});
930 : }
931 : }
932 :
933 4 : if (astFrameCoords.empty() ||
934 2 : astFrameCoords.size() > nMaxFramePointCount)
935 : {
936 : // CPLError(CE_Warning, CPLE_AppDefined, "Invalid frame WKT: %s", pszFrameWKT);
937 0 : CPLDebug("RMF", "Write to header frame failed: no "
938 : "points or too many");
939 0 : astFrameCoords.clear();
940 : }
941 : else
942 : {
943 2 : sHeader.nROISize = static_cast<GUInt32>(
944 2 : sizeof(RSWFrame) +
945 : sizeof(RSWFrameCoord) *
946 : astFrameCoords
947 2 : .size()); // Set real size and real point count
948 2 : sHeader.iFrameFlag = 0;
949 : }
950 : }
951 : else
952 : {
953 0 : CPLDebug("RMF", "Write to header frame failed: "
954 : "GDALInvGeoTransform == FALSE");
955 : }
956 : }
957 2 : OGRGeometryFactory::destroyGeometry(poFrameGeom);
958 : }
959 : else
960 : {
961 0 : CPLDebug("RMF", "Write to header frame failed: "
962 : "OGRGeometryFactory::createFromWkt error");
963 : }
964 : }
965 :
966 200 : vsi_l_offset iCurrentFileSize(GetLastOffset());
967 200 : sHeader.nFileSize0 = GetRMFOffset(iCurrentFileSize, &iCurrentFileSize);
968 200 : sHeader.nSize = sHeader.nFileSize0 - GetRMFOffset(nHeaderOffset, nullptr);
969 : /* -------------------------------------------------------------------- */
970 : /* Write out the main header. */
971 : /* -------------------------------------------------------------------- */
972 : {
973 200 : GByte abyHeader[RMF_HEADER_SIZE] = {};
974 :
975 200 : memcpy(abyHeader, sHeader.bySignature, RMF_SIGNATURE_SIZE);
976 200 : RMF_WRITE_ULONG(abyHeader, sHeader.iVersion, 4);
977 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nSize, 8);
978 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nOvrOffset, 12);
979 200 : RMF_WRITE_ULONG(abyHeader, sHeader.iUserID, 16);
980 200 : memcpy(abyHeader + 20, sHeader.byName, RMF_NAME_SIZE);
981 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nBitDepth, 52);
982 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nHeight, 56);
983 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nWidth, 60);
984 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nXTiles, 64);
985 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nYTiles, 68);
986 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileHeight, 72);
987 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileWidth, 76);
988 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nLastTileHeight, 80);
989 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nLastTileWidth, 84);
990 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nROIOffset, 88);
991 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nROISize, 92);
992 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nClrTblOffset, 96);
993 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nClrTblSize, 100);
994 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileTblOffset, 104);
995 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileTblSize, 108);
996 200 : RMF_WRITE_LONG(abyHeader, sHeader.iMapType, 124);
997 200 : RMF_WRITE_LONG(abyHeader, sHeader.iProjection, 128);
998 200 : RMF_WRITE_LONG(abyHeader, sHeader.iEPSGCode, 132);
999 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfScale, 136);
1000 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfResolution, 144);
1001 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfPixelSize, 152);
1002 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfLLY, 160);
1003 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfLLX, 168);
1004 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfStdP1, 176);
1005 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfStdP2, 184);
1006 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfCenterLong, 192);
1007 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfCenterLat, 200);
1008 200 : *(abyHeader + 208) = sHeader.iCompression;
1009 200 : *(abyHeader + 209) = sHeader.iMaskType;
1010 200 : *(abyHeader + 210) = sHeader.iMaskStep;
1011 200 : *(abyHeader + 211) = sHeader.iFrameFlag;
1012 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFlagsTblOffset, 212);
1013 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFlagsTblSize, 216);
1014 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFileSize0, 220);
1015 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFileSize1, 224);
1016 200 : *(abyHeader + 228) = sHeader.iUnknown;
1017 200 : *(abyHeader + 244) = sHeader.iGeorefFlag;
1018 200 : *(abyHeader + 245) = sHeader.iInverse;
1019 200 : *(abyHeader + 246) = sHeader.iJpegQuality;
1020 200 : memcpy(abyHeader + 248, sHeader.abyInvisibleColors,
1021 : sizeof(sHeader.abyInvisibleColors));
1022 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.adfElevMinMax[0], 280);
1023 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.adfElevMinMax[1], 288);
1024 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfNoData, 296);
1025 200 : RMF_WRITE_ULONG(abyHeader, sHeader.iElevationUnit, 304);
1026 200 : *(abyHeader + 308) = sHeader.iElevationType;
1027 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nExtHdrOffset, 312);
1028 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nExtHdrSize, 316);
1029 :
1030 200 : VSIFSeekL(fp, nHeaderOffset, SEEK_SET);
1031 200 : VSIFWriteL(abyHeader, 1, sizeof(abyHeader), fp);
1032 : }
1033 :
1034 : /* -------------------------------------------------------------------- */
1035 : /* Write out the extended header. */
1036 : /* -------------------------------------------------------------------- */
1037 :
1038 200 : if (sHeader.nExtHdrOffset && sHeader.nExtHdrSize >= RMF_MIN_EXT_HEADER_SIZE)
1039 : {
1040 200 : if (sHeader.nExtHdrSize > RMF_MAX_EXT_HEADER_SIZE)
1041 : {
1042 0 : CPLError(CE_Failure, CPLE_FileIO, "RMF File malformed");
1043 0 : return CE_Failure;
1044 : }
1045 : GByte *pabyExtHeader =
1046 200 : reinterpret_cast<GByte *>(CPLCalloc(sHeader.nExtHdrSize, 1));
1047 :
1048 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nEllipsoid, 24);
1049 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nVertDatum, 28);
1050 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nDatum, 32);
1051 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nZone, 36);
1052 :
1053 200 : VSIFSeekL(fp, GetFileOffset(sHeader.nExtHdrOffset), SEEK_SET);
1054 200 : VSIFWriteL(pabyExtHeader, 1, sHeader.nExtHdrSize, fp);
1055 :
1056 200 : CPLFree(pabyExtHeader);
1057 : }
1058 :
1059 : /* -------------------------------------------------------------------- */
1060 : /* Write out the color table. */
1061 : /* -------------------------------------------------------------------- */
1062 :
1063 200 : if (sHeader.nClrTblOffset && sHeader.nClrTblSize)
1064 : {
1065 59 : VSIFSeekL(fp, GetFileOffset(sHeader.nClrTblOffset), SEEK_SET);
1066 59 : VSIFWriteL(pabyColorTable, 1, sHeader.nClrTblSize, fp);
1067 : }
1068 :
1069 200 : if (sHeader.nROIOffset && sHeader.nROISize)
1070 : {
1071 : GByte *pabyROI =
1072 2 : reinterpret_cast<GByte *>(CPLCalloc(sHeader.nROISize, 1));
1073 2 : memset(pabyROI, 0, sHeader.nROISize);
1074 :
1075 2 : auto nPointCount = astFrameCoords.size();
1076 2 : size_t offset = 0;
1077 2 : RMF_WRITE_LONG(pabyROI, nPolygonType, offset);
1078 2 : offset += 4;
1079 2 : RMF_WRITE_LONG(pabyROI, static_cast<GInt32>((4 + nPointCount * 2) * 4),
1080 : offset);
1081 2 : offset += 4;
1082 2 : RMF_WRITE_LONG(pabyROI, 0, offset);
1083 2 : offset += 4;
1084 2 : RMF_WRITE_LONG(pabyROI, static_cast<GInt32>(32768 * nPointCount * 2),
1085 : offset);
1086 2 : offset += 4;
1087 :
1088 : // Write points
1089 12 : for (size_t i = 0; i < nPointCount; i++)
1090 : {
1091 10 : RMF_WRITE_LONG(pabyROI, astFrameCoords[i].nX, offset);
1092 10 : offset += 4;
1093 10 : RMF_WRITE_LONG(pabyROI, astFrameCoords[i].nY, offset);
1094 10 : offset += 4;
1095 : }
1096 :
1097 2 : VSIFSeekL(fp, GetFileOffset(sHeader.nROIOffset), SEEK_SET);
1098 2 : VSIFWriteL(pabyROI, 1, sHeader.nROISize, fp);
1099 :
1100 2 : CPLFree(pabyROI);
1101 : }
1102 :
1103 200 : if (sHeader.nFlagsTblOffset && sHeader.nFlagsTblSize)
1104 : {
1105 : GByte *pabyFlagsTbl =
1106 200 : reinterpret_cast<GByte *>(CPLCalloc(sHeader.nFlagsTblSize, 1));
1107 :
1108 200 : if (sHeader.iFrameFlag == 0)
1109 : {
1110 : // TODO: Add more strictly check for flag value
1111 2 : memset(
1112 : pabyFlagsTbl, 2,
1113 : sHeader
1114 2 : .nFlagsTblSize); // Mark all blocks as intersected with ROI. 0 - complete outside, 1 - complete inside.
1115 : }
1116 : else
1117 : {
1118 198 : memset(pabyFlagsTbl, 0, sHeader.nFlagsTblSize);
1119 : }
1120 :
1121 200 : VSIFSeekL(fp, GetFileOffset(sHeader.nFlagsTblOffset), SEEK_SET);
1122 200 : VSIFWriteL(pabyFlagsTbl, 1, sHeader.nFlagsTblSize, fp);
1123 :
1124 200 : CPLFree(pabyFlagsTbl);
1125 : }
1126 :
1127 : #undef RMF_WRITE_DOUBLE
1128 : #undef RMF_WRITE_ULONG
1129 : #undef RMF_WRITE_LONG
1130 :
1131 : /* -------------------------------------------------------------------- */
1132 : /* Write out the block table, swap if needed. */
1133 : /* -------------------------------------------------------------------- */
1134 :
1135 200 : VSIFSeekL(fp, GetFileOffset(sHeader.nTileTblOffset), SEEK_SET);
1136 :
1137 : #ifdef CPL_MSB
1138 : GUInt32 *paiTilesSwapped =
1139 : reinterpret_cast<GUInt32 *>(CPLMalloc(sHeader.nTileTblSize));
1140 : if (!paiTilesSwapped)
1141 : return CE_Failure;
1142 :
1143 : memcpy(paiTilesSwapped, paiTiles, sHeader.nTileTblSize);
1144 : for (GUInt32 i = 0; i < sHeader.nTileTblSize / sizeof(GUInt32); i++)
1145 : CPL_SWAP32PTR(paiTilesSwapped + i);
1146 : VSIFWriteL(paiTilesSwapped, 1, sHeader.nTileTblSize, fp);
1147 :
1148 : CPLFree(paiTilesSwapped);
1149 : #else
1150 200 : VSIFWriteL(paiTiles, 1, sHeader.nTileTblSize, fp);
1151 : #endif
1152 :
1153 200 : bHeaderDirty = false;
1154 :
1155 200 : return CE_None;
1156 : }
1157 :
1158 : /************************************************************************/
1159 : /* FlushCache() */
1160 : /************************************************************************/
1161 :
1162 411 : CPLErr RMFDataset::FlushCache(bool bAtClosing)
1163 :
1164 : {
1165 411 : CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
1166 :
1167 549 : if (poCompressData != nullptr &&
1168 138 : poCompressData->oThreadPool.GetThreadCount() > 0)
1169 : {
1170 9 : poCompressData->oThreadPool.WaitCompletion();
1171 : }
1172 :
1173 411 : if (bAtClosing && eRMFType == RMFT_MTW && eAccess == GA_Update)
1174 : {
1175 77 : GDALRasterBand *poBand = GetRasterBand(1);
1176 :
1177 77 : if (poBand)
1178 : {
1179 : // ComputeRasterMinMax can setup error in case of dataset full
1180 : // from NoData values, but it makes no sense here.
1181 77 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1182 77 : poBand->ComputeRasterMinMax(FALSE, sHeader.adfElevMinMax);
1183 77 : bHeaderDirty = true;
1184 : }
1185 : }
1186 411 : if (bHeaderDirty && WriteHeader() != CE_None)
1187 0 : eErr = CE_Failure;
1188 411 : return eErr;
1189 : }
1190 :
1191 : /************************************************************************/
1192 : /* Identify() */
1193 : /************************************************************************/
1194 :
1195 58806 : int RMFDataset::Identify(GDALOpenInfo *poOpenInfo)
1196 :
1197 : {
1198 58806 : if (poOpenInfo->pabyHeader == nullptr)
1199 53675 : return FALSE;
1200 :
1201 5131 : if (memcmp(poOpenInfo->pabyHeader, RMF_SigRSW, sizeof(RMF_SigRSW)) != 0 &&
1202 4922 : memcmp(poOpenInfo->pabyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) !=
1203 4910 : 0 &&
1204 4910 : memcmp(poOpenInfo->pabyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) != 0)
1205 4792 : return FALSE;
1206 :
1207 339 : return TRUE;
1208 : }
1209 :
1210 : /************************************************************************/
1211 : /* Open() */
1212 : /************************************************************************/
1213 :
1214 128 : GDALDataset *RMFDataset::Open(GDALOpenInfo *poOpenInfo)
1215 : {
1216 128 : auto poDS = Open(poOpenInfo, nullptr, 0);
1217 128 : if (poDS == nullptr)
1218 : {
1219 9 : return nullptr;
1220 : }
1221 :
1222 119 : RMFDataset *poCurrentLayer = poDS;
1223 119 : RMFDataset *poParent = poCurrentLayer;
1224 119 : const int nMaxPossibleOvCount = 64;
1225 :
1226 200 : for (int iOv = 0; iOv < nMaxPossibleOvCount && poCurrentLayer != nullptr;
1227 : ++iOv)
1228 : {
1229 200 : poCurrentLayer = poCurrentLayer->OpenOverview(poParent, poOpenInfo);
1230 200 : if (poCurrentLayer == nullptr)
1231 119 : break;
1232 81 : poParent->poOvrDatasets.push_back(poCurrentLayer);
1233 : }
1234 :
1235 119 : return poDS;
1236 : }
1237 :
1238 213 : RMFDataset *RMFDataset::Open(GDALOpenInfo *poOpenInfo, RMFDataset *poParentDS,
1239 : vsi_l_offset nNextHeaderOffset)
1240 : {
1241 341 : if (!Identify(poOpenInfo) ||
1242 128 : (poParentDS == nullptr && poOpenInfo->fpL == nullptr))
1243 2 : return nullptr;
1244 :
1245 : /* -------------------------------------------------------------------- */
1246 : /* Create a corresponding GDALDataset. */
1247 : /* -------------------------------------------------------------------- */
1248 211 : RMFDataset *poDS = new RMFDataset();
1249 :
1250 211 : if (poParentDS == nullptr)
1251 : {
1252 128 : poDS->fp = poOpenInfo->fpL;
1253 128 : poOpenInfo->fpL = nullptr;
1254 128 : poDS->nHeaderOffset = 0;
1255 128 : poDS->poParentDS = nullptr;
1256 : }
1257 : else
1258 : {
1259 83 : poDS->fp = poParentDS->fp;
1260 83 : poDS->poParentDS = poParentDS;
1261 83 : poDS->nHeaderOffset = nNextHeaderOffset;
1262 : }
1263 211 : poDS->eAccess = poOpenInfo->eAccess;
1264 :
1265 : #define RMF_READ_SHORT(ptr, value, offset) \
1266 : do \
1267 : { \
1268 : memcpy(&(value), reinterpret_cast<GInt16 *>((ptr) + (offset)), \
1269 : sizeof(GInt16)); \
1270 : if (poDS->bBigEndian) \
1271 : { \
1272 : CPL_MSBPTR16(&(value)); \
1273 : } \
1274 : else \
1275 : { \
1276 : CPL_LSBPTR16(&(value)); \
1277 : } \
1278 : } while (false);
1279 :
1280 : #define RMF_READ_ULONG(ptr, value, offset) \
1281 : do \
1282 : { \
1283 : memcpy(&(value), reinterpret_cast<GUInt32 *>((ptr) + (offset)), \
1284 : sizeof(GUInt32)); \
1285 : if (poDS->bBigEndian) \
1286 : { \
1287 : CPL_MSBPTR32(&(value)); \
1288 : } \
1289 : else \
1290 : { \
1291 : CPL_LSBPTR32(&(value)); \
1292 : } \
1293 : } while (false);
1294 :
1295 : #define RMF_READ_LONG(ptr, value, offset) RMF_READ_ULONG(ptr, value, offset)
1296 :
1297 : #define RMF_READ_DOUBLE(ptr, value, offset) \
1298 : do \
1299 : { \
1300 : memcpy(&(value), reinterpret_cast<double *>((ptr) + (offset)), \
1301 : sizeof(double)); \
1302 : if (poDS->bBigEndian) \
1303 : { \
1304 : CPL_MSBPTR64(&(value)); \
1305 : } \
1306 : else \
1307 : { \
1308 : CPL_LSBPTR64(&(value)); \
1309 : } \
1310 : } while (false);
1311 :
1312 : /* -------------------------------------------------------------------- */
1313 : /* Read the main header. */
1314 : /* -------------------------------------------------------------------- */
1315 :
1316 : {
1317 211 : GByte abyHeader[RMF_HEADER_SIZE] = {};
1318 :
1319 211 : VSIFSeekL(poDS->fp, nNextHeaderOffset, SEEK_SET);
1320 211 : if (VSIFReadL(abyHeader, 1, sizeof(abyHeader), poDS->fp) !=
1321 : sizeof(abyHeader))
1322 : {
1323 0 : delete poDS;
1324 0 : return nullptr;
1325 : }
1326 :
1327 211 : if (memcmp(abyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) == 0)
1328 : {
1329 76 : poDS->eRMFType = RMFT_MTW;
1330 : }
1331 135 : else if (memcmp(abyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) == 0)
1332 : {
1333 6 : poDS->eRMFType = RMFT_RSW;
1334 6 : poDS->bBigEndian = true;
1335 : }
1336 : else
1337 : {
1338 129 : poDS->eRMFType = RMFT_RSW;
1339 : }
1340 :
1341 211 : memcpy(poDS->sHeader.bySignature, abyHeader, RMF_SIGNATURE_SIZE);
1342 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.iVersion, 4);
1343 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nSize, 8);
1344 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nOvrOffset, 12);
1345 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.iUserID, 16);
1346 211 : memcpy(poDS->sHeader.byName, abyHeader + 20,
1347 : sizeof(poDS->sHeader.byName));
1348 211 : poDS->sHeader.byName[sizeof(poDS->sHeader.byName) - 1] = '\0';
1349 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nBitDepth, 52);
1350 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nHeight, 56);
1351 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nWidth, 60);
1352 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nXTiles, 64);
1353 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nYTiles, 68);
1354 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileHeight, 72);
1355 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileWidth, 76);
1356 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nLastTileHeight, 80);
1357 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nLastTileWidth, 84);
1358 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nROIOffset, 88);
1359 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nROISize, 92);
1360 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nClrTblOffset, 96);
1361 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nClrTblSize, 100);
1362 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileTblOffset, 104);
1363 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileTblSize, 108);
1364 211 : RMF_READ_LONG(abyHeader, poDS->sHeader.iMapType, 124);
1365 211 : RMF_READ_LONG(abyHeader, poDS->sHeader.iProjection, 128);
1366 211 : RMF_READ_LONG(abyHeader, poDS->sHeader.iEPSGCode, 132);
1367 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfScale, 136);
1368 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfResolution, 144);
1369 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfPixelSize, 152);
1370 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfLLY, 160);
1371 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfLLX, 168);
1372 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfStdP1, 176);
1373 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfStdP2, 184);
1374 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfCenterLong, 192);
1375 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfCenterLat, 200);
1376 211 : poDS->sHeader.iCompression = *(abyHeader + 208);
1377 211 : poDS->sHeader.iMaskType = *(abyHeader + 209);
1378 211 : poDS->sHeader.iMaskStep = *(abyHeader + 210);
1379 211 : poDS->sHeader.iFrameFlag = *(abyHeader + 211);
1380 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFlagsTblOffset, 212);
1381 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFlagsTblSize, 216);
1382 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFileSize0, 220);
1383 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFileSize1, 224);
1384 211 : poDS->sHeader.iUnknown = *(abyHeader + 228);
1385 211 : poDS->sHeader.iGeorefFlag = *(abyHeader + 244);
1386 211 : poDS->sHeader.iInverse = *(abyHeader + 245);
1387 211 : poDS->sHeader.iJpegQuality = *(abyHeader + 246);
1388 211 : memcpy(poDS->sHeader.abyInvisibleColors, abyHeader + 248,
1389 : sizeof(poDS->sHeader.abyInvisibleColors));
1390 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.adfElevMinMax[0], 280);
1391 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.adfElevMinMax[1], 288);
1392 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfNoData, 296);
1393 :
1394 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.iElevationUnit, 304);
1395 211 : poDS->sHeader.iElevationType = *(abyHeader + 308);
1396 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nExtHdrOffset, 312);
1397 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nExtHdrSize, 316);
1398 211 : poDS->SetMetadataItem(MD_SCALE_KEY,
1399 211 : CPLSPrintf("1 : %u", int(poDS->sHeader.dfScale)));
1400 211 : poDS->SetMetadataItem(MD_NAME_KEY,
1401 211 : CPLSPrintf("%s", poDS->sHeader.byName));
1402 211 : poDS->SetMetadataItem(MD_VERSION_KEY,
1403 : CPLSPrintf("%d", poDS->sHeader.iVersion));
1404 211 : poDS->SetMetadataItem(MD_MATH_BASE_MAP_TYPE_KEY,
1405 : CPLSPrintf("%d", poDS->sHeader.iMapType));
1406 211 : poDS->SetMetadataItem(MD_MATH_BASE_PROJECTION_KEY,
1407 : CPLSPrintf("%d", poDS->sHeader.iProjection));
1408 : }
1409 :
1410 211 : if (poDS->sHeader.nTileTblSize % (sizeof(GUInt32) * 2))
1411 : {
1412 0 : CPLError(CE_Warning, CPLE_IllegalArg, "Invalid tile table size.");
1413 0 : delete poDS;
1414 0 : return nullptr;
1415 : }
1416 :
1417 : bool bInvalidTileSize;
1418 : try
1419 : {
1420 : uint64_t nMaxTileBits =
1421 211 : (CPLSM(static_cast<uint64_t>(2)) *
1422 422 : CPLSM(static_cast<uint64_t>(poDS->sHeader.nTileWidth)) *
1423 422 : CPLSM(static_cast<uint64_t>(poDS->sHeader.nTileHeight)) *
1424 422 : CPLSM(static_cast<uint64_t>(poDS->sHeader.nBitDepth)))
1425 211 : .v();
1426 211 : bInvalidTileSize =
1427 : (nMaxTileBits >
1428 211 : static_cast<uint64_t>(std::numeric_limits<GUInt32>::max()));
1429 : }
1430 0 : catch (...)
1431 : {
1432 0 : bInvalidTileSize = true;
1433 : }
1434 211 : if (bInvalidTileSize)
1435 : {
1436 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1437 : "Invalid tile size. Width %lu, height %lu, bit depth %lu.",
1438 0 : static_cast<unsigned long>(poDS->sHeader.nTileWidth),
1439 0 : static_cast<unsigned long>(poDS->sHeader.nTileHeight),
1440 0 : static_cast<unsigned long>(poDS->sHeader.nBitDepth));
1441 0 : delete poDS;
1442 0 : return nullptr;
1443 : }
1444 :
1445 211 : if (poDS->sHeader.nLastTileWidth > poDS->sHeader.nTileWidth ||
1446 211 : poDS->sHeader.nLastTileHeight > poDS->sHeader.nTileHeight)
1447 : {
1448 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1449 : "Invalid last tile size %lu x %lu. "
1450 : "It can't be greater than %lu x %lu.",
1451 0 : static_cast<unsigned long>(poDS->sHeader.nLastTileWidth),
1452 0 : static_cast<unsigned long>(poDS->sHeader.nLastTileHeight),
1453 0 : static_cast<unsigned long>(poDS->sHeader.nTileWidth),
1454 0 : static_cast<unsigned long>(poDS->sHeader.nTileHeight));
1455 0 : delete poDS;
1456 0 : return nullptr;
1457 : }
1458 :
1459 211 : if (poParentDS != nullptr)
1460 : {
1461 83 : if (0 != memcmp(poDS->sHeader.bySignature,
1462 83 : poParentDS->sHeader.bySignature, RMF_SIGNATURE_SIZE))
1463 : {
1464 2 : CPLError(CE_Warning, CPLE_IllegalArg,
1465 : "Invalid subheader signature.");
1466 2 : delete poDS;
1467 2 : return nullptr;
1468 : }
1469 : }
1470 :
1471 : /* -------------------------------------------------------------------- */
1472 : /* Read the extended header. */
1473 : /* -------------------------------------------------------------------- */
1474 :
1475 209 : if (poDS->sHeader.nExtHdrOffset &&
1476 198 : poDS->sHeader.nExtHdrSize >= RMF_MIN_EXT_HEADER_SIZE)
1477 : {
1478 198 : if (poDS->sHeader.nExtHdrSize > RMF_MAX_EXT_HEADER_SIZE)
1479 : {
1480 0 : CPLError(CE_Failure, CPLE_FileIO, "RMF File malformed");
1481 0 : delete poDS;
1482 0 : return nullptr;
1483 : }
1484 : GByte *pabyExtHeader =
1485 198 : reinterpret_cast<GByte *>(CPLCalloc(poDS->sHeader.nExtHdrSize, 1));
1486 198 : if (pabyExtHeader == nullptr)
1487 : {
1488 0 : delete poDS;
1489 0 : return nullptr;
1490 : }
1491 :
1492 198 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nExtHdrOffset),
1493 : SEEK_SET);
1494 198 : VSIFReadL(pabyExtHeader, 1, poDS->sHeader.nExtHdrSize, poDS->fp);
1495 :
1496 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nEllipsoid, 24);
1497 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nVertDatum, 28);
1498 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nDatum, 32);
1499 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nZone, 36);
1500 :
1501 198 : CPLFree(pabyExtHeader);
1502 : }
1503 :
1504 209 : CPLDebug("RMF", "Version %d", poDS->sHeader.iVersion);
1505 :
1506 209 : constexpr GUInt32 ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE =
1507 : 10 * 1024 * 1024;
1508 : #ifdef DEBUG
1509 :
1510 418 : CPLDebug("RMF",
1511 : "%s image has width %d, height %d, bit depth %d, "
1512 : "compression scheme %d, %s, nodata %f",
1513 209 : (poDS->eRMFType == RMFT_MTW) ? "MTW" : "RSW", poDS->sHeader.nWidth,
1514 : poDS->sHeader.nHeight, poDS->sHeader.nBitDepth,
1515 209 : poDS->sHeader.iCompression,
1516 209 : poDS->bBigEndian ? "big endian" : "little endian",
1517 : poDS->sHeader.dfNoData);
1518 209 : CPLDebug("RMF",
1519 : "Size %d, offset to overview %#lx, user ID %d, "
1520 : "ROI offset %#lx, ROI size %d",
1521 : poDS->sHeader.nSize,
1522 209 : static_cast<unsigned long>(poDS->sHeader.nOvrOffset),
1523 : poDS->sHeader.iUserID,
1524 209 : static_cast<unsigned long>(poDS->sHeader.nROIOffset),
1525 : poDS->sHeader.nROISize);
1526 209 : CPLDebug("RMF", "Map type %d, projection %d, scale %f, resolution %f, ",
1527 : poDS->sHeader.iMapType, poDS->sHeader.iProjection,
1528 : poDS->sHeader.dfScale, poDS->sHeader.dfResolution);
1529 209 : CPLDebug("RMF", "EPSG %d ", poDS->sHeader.iEPSGCode);
1530 209 : CPLDebug("RMF", "Georeferencing: pixel size %f, LLX %f, LLY %f",
1531 : poDS->sHeader.dfPixelSize, poDS->sHeader.dfLLX,
1532 : poDS->sHeader.dfLLY);
1533 :
1534 209 : if (poDS->sHeader.nROIOffset &&
1535 116 : poDS->sHeader.nROISize >= sizeof(RSWFrame) &&
1536 12 : poDS->sHeader.nROISize <= ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE)
1537 : {
1538 : GByte *pabyROI = reinterpret_cast<GByte *>(
1539 12 : VSI_MALLOC_VERBOSE(poDS->sHeader.nROISize));
1540 12 : if (pabyROI == nullptr)
1541 : {
1542 0 : delete poDS;
1543 0 : return nullptr;
1544 : }
1545 :
1546 12 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nROIOffset),
1547 : SEEK_SET);
1548 12 : if (VSIFReadL(pabyROI, poDS->sHeader.nROISize, 1, poDS->fp) != 1)
1549 : {
1550 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot read ROI");
1551 0 : CPLFree(pabyROI);
1552 0 : delete poDS;
1553 0 : return nullptr;
1554 : }
1555 :
1556 : GInt32 nValue;
1557 :
1558 12 : CPLDebug("RMF", "ROI coordinates:");
1559 : /* coverity[tainted_data] */
1560 3504 : for (GUInt32 i = 0; i + sizeof(nValue) <= poDS->sHeader.nROISize;
1561 3492 : i += sizeof(nValue))
1562 : {
1563 3492 : RMF_READ_LONG(pabyROI, nValue, i);
1564 3492 : CPLDebug("RMF", "%d", nValue);
1565 : }
1566 :
1567 12 : CPLFree(pabyROI);
1568 : }
1569 : #endif
1570 418 : if (poDS->sHeader.nWidth >= INT_MAX || poDS->sHeader.nHeight >= INT_MAX ||
1571 209 : !GDALCheckDatasetDimensions(poDS->sHeader.nWidth,
1572 209 : poDS->sHeader.nHeight))
1573 : {
1574 0 : delete poDS;
1575 0 : return nullptr;
1576 : }
1577 :
1578 : /* -------------------------------------------------------------------- */
1579 : /* Read array of blocks offsets/sizes. */
1580 : /* -------------------------------------------------------------------- */
1581 :
1582 : // To avoid useless excessive memory allocation
1583 209 : if (poDS->sHeader.nTileTblSize > 1000000)
1584 : {
1585 0 : VSIFSeekL(poDS->fp, 0, SEEK_END);
1586 0 : vsi_l_offset nFileSize = VSIFTellL(poDS->fp);
1587 0 : if (nFileSize < poDS->sHeader.nTileTblSize)
1588 : {
1589 0 : delete poDS;
1590 0 : return nullptr;
1591 : }
1592 : }
1593 :
1594 209 : if (VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nTileTblOffset),
1595 209 : SEEK_SET) < 0)
1596 : {
1597 0 : delete poDS;
1598 0 : return nullptr;
1599 : }
1600 :
1601 209 : poDS->paiTiles =
1602 209 : reinterpret_cast<GUInt32 *>(VSIMalloc(poDS->sHeader.nTileTblSize));
1603 209 : if (!poDS->paiTiles)
1604 : {
1605 0 : delete poDS;
1606 0 : return nullptr;
1607 : }
1608 :
1609 209 : if (VSIFReadL(poDS->paiTiles, 1, poDS->sHeader.nTileTblSize, poDS->fp) <
1610 209 : poDS->sHeader.nTileTblSize)
1611 : {
1612 9 : CPLDebug("RMF", "Can't read tiles offsets/sizes table.");
1613 9 : delete poDS;
1614 9 : return nullptr;
1615 : }
1616 :
1617 : #ifdef CPL_MSB
1618 : if (!poDS->bBigEndian)
1619 : {
1620 : for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
1621 : i++)
1622 : CPL_SWAP32PTR(poDS->paiTiles + i);
1623 : }
1624 : #else
1625 200 : if (poDS->bBigEndian)
1626 : {
1627 30 : for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
1628 : i++)
1629 24 : CPL_SWAP32PTR(poDS->paiTiles + i);
1630 : }
1631 : #endif
1632 :
1633 : #ifdef DEBUG
1634 200 : CPLDebug("RMF", "List of block offsets/sizes:");
1635 :
1636 646 : for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
1637 446 : i += 2)
1638 : {
1639 446 : CPLDebug("RMF", " %u / %u", poDS->paiTiles[i],
1640 446 : poDS->paiTiles[i + 1]);
1641 : }
1642 : #endif
1643 :
1644 : /* -------------------------------------------------------------------- */
1645 : /* Set up essential image parameters. */
1646 : /* -------------------------------------------------------------------- */
1647 200 : GDALDataType eType = GDT_Byte;
1648 :
1649 200 : poDS->nRasterXSize = poDS->sHeader.nWidth;
1650 200 : poDS->nRasterYSize = poDS->sHeader.nHeight;
1651 :
1652 200 : if (poDS->eRMFType == RMFT_RSW)
1653 : {
1654 126 : switch (poDS->sHeader.nBitDepth)
1655 : {
1656 58 : case 32:
1657 : case 24:
1658 : case 16:
1659 58 : poDS->nBands = 3;
1660 58 : break;
1661 68 : case 1:
1662 : case 4:
1663 : case 8:
1664 68 : if (poParentDS != nullptr &&
1665 26 : poParentDS->poColorTable != nullptr)
1666 : {
1667 26 : poDS->poColorTable = poParentDS->poColorTable->Clone();
1668 : }
1669 : else
1670 : {
1671 : // Allocate memory for colour table and read it
1672 42 : poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
1673 42 : GUInt32 nExpectedColorTableBytes =
1674 42 : poDS->nColorTableSize * 4;
1675 42 : if (nExpectedColorTableBytes > poDS->sHeader.nClrTblSize)
1676 : {
1677 : // We could probably test for strict equality in
1678 : // the above test ???
1679 0 : CPLDebug("RMF",
1680 : "Wrong color table size. "
1681 : "Expected %u, got %u.",
1682 : nExpectedColorTableBytes,
1683 : poDS->sHeader.nClrTblSize);
1684 0 : delete poDS;
1685 0 : return nullptr;
1686 : }
1687 42 : poDS->pabyColorTable = reinterpret_cast<GByte *>(
1688 42 : VSIMalloc(nExpectedColorTableBytes));
1689 42 : if (poDS->pabyColorTable == nullptr)
1690 : {
1691 0 : CPLDebug("RMF", "Can't allocate color table.");
1692 0 : delete poDS;
1693 0 : return nullptr;
1694 : }
1695 42 : if (VSIFSeekL(
1696 : poDS->fp,
1697 : poDS->GetFileOffset(poDS->sHeader.nClrTblOffset),
1698 42 : SEEK_SET) < 0)
1699 : {
1700 0 : CPLDebug("RMF", "Can't seek to color table location.");
1701 0 : delete poDS;
1702 0 : return nullptr;
1703 : }
1704 42 : if (VSIFReadL(poDS->pabyColorTable, 1,
1705 : nExpectedColorTableBytes,
1706 42 : poDS->fp) < nExpectedColorTableBytes)
1707 : {
1708 0 : CPLDebug("RMF", "Can't read color table.");
1709 0 : delete poDS;
1710 0 : return nullptr;
1711 : }
1712 :
1713 42 : poDS->poColorTable = new GDALColorTable();
1714 9326 : for (GUInt32 i = 0; i < poDS->nColorTableSize; i++)
1715 : {
1716 9284 : const GDALColorEntry oEntry = {
1717 9284 : poDS->pabyColorTable[i * 4], // Red
1718 9284 : poDS->pabyColorTable[i * 4 + 1], // Green
1719 9284 : poDS->pabyColorTable[i * 4 + 2], // Blue
1720 : 255 // Alpha
1721 9284 : };
1722 :
1723 9284 : poDS->poColorTable->SetColorEntry(i, &oEntry);
1724 : }
1725 : }
1726 68 : poDS->nBands = 1;
1727 68 : break;
1728 0 : default:
1729 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1730 : "Invalid RSW bit depth %lu.",
1731 0 : static_cast<unsigned long>(poDS->sHeader.nBitDepth));
1732 0 : delete poDS;
1733 0 : return nullptr;
1734 : }
1735 126 : eType = GDT_Byte;
1736 : }
1737 : else
1738 : {
1739 74 : poDS->nBands = 1;
1740 74 : if (poDS->sHeader.nBitDepth == 8)
1741 : {
1742 0 : eType = GDT_Byte;
1743 : }
1744 74 : else if (poDS->sHeader.nBitDepth == 16)
1745 : {
1746 0 : eType = GDT_Int16;
1747 : }
1748 74 : else if (poDS->sHeader.nBitDepth == 32)
1749 : {
1750 9 : eType = GDT_Int32;
1751 : }
1752 65 : else if (poDS->sHeader.nBitDepth == 64)
1753 : {
1754 65 : eType = GDT_Float64;
1755 : }
1756 : else
1757 : {
1758 0 : CPLError(CE_Warning, CPLE_IllegalArg, "Invalid MTW bit depth %lu.",
1759 0 : static_cast<unsigned long>(poDS->sHeader.nBitDepth));
1760 0 : delete poDS;
1761 0 : return nullptr;
1762 : }
1763 : }
1764 :
1765 200 : if (poDS->sHeader.nTileWidth == 0 || poDS->sHeader.nTileWidth > INT_MAX ||
1766 200 : poDS->sHeader.nTileHeight == 0 || poDS->sHeader.nTileHeight > INT_MAX)
1767 : {
1768 0 : CPLDebug("RMF", "Invalid tile dimension : %u x %u",
1769 : poDS->sHeader.nTileWidth, poDS->sHeader.nTileHeight);
1770 0 : delete poDS;
1771 0 : return nullptr;
1772 : }
1773 :
1774 200 : const int nDataSize = GDALGetDataTypeSizeBytes(eType);
1775 200 : const int nBlockXSize = static_cast<int>(poDS->sHeader.nTileWidth);
1776 200 : const int nBlockYSize = static_cast<int>(poDS->sHeader.nTileHeight);
1777 200 : if (nDataSize == 0 || nBlockXSize > INT_MAX / nBlockYSize ||
1778 200 : nBlockYSize > INT_MAX / nDataSize ||
1779 200 : nBlockXSize > INT_MAX / (nBlockYSize * nDataSize))
1780 : {
1781 0 : CPLDebug("RMF", "Too big raster / tile dimension");
1782 0 : delete poDS;
1783 0 : return nullptr;
1784 : }
1785 :
1786 200 : poDS->nXTiles = DIV_ROUND_UP(poDS->nRasterXSize, nBlockXSize);
1787 200 : poDS->nYTiles = DIV_ROUND_UP(poDS->nRasterYSize, nBlockYSize);
1788 :
1789 : #ifdef DEBUG
1790 200 : CPLDebug("RMF", "Image is %d tiles wide, %d tiles long", poDS->nXTiles,
1791 : poDS->nYTiles);
1792 : #endif
1793 :
1794 : /* -------------------------------------------------------------------- */
1795 : /* Choose compression scheme. */
1796 : /* -------------------------------------------------------------------- */
1797 200 : if (CE_None != poDS->SetupCompression(eType, poOpenInfo->pszFilename))
1798 : {
1799 0 : delete poDS;
1800 0 : return nullptr;
1801 : }
1802 :
1803 200 : if (poOpenInfo->eAccess == GA_Update)
1804 : {
1805 20 : if (poParentDS == nullptr)
1806 : {
1807 12 : if (CE_None !=
1808 12 : poDS->InitCompressorData(poOpenInfo->papszOpenOptions))
1809 : {
1810 0 : delete poDS;
1811 0 : return nullptr;
1812 : }
1813 : }
1814 : else
1815 : {
1816 8 : poDS->poCompressData = poParentDS->poCompressData;
1817 : }
1818 : }
1819 : /* -------------------------------------------------------------------- */
1820 : /* Create band information objects. */
1821 : /* -------------------------------------------------------------------- */
1822 516 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
1823 316 : poDS->SetBand(iBand, new RMFRasterBand(poDS, iBand, eType));
1824 :
1825 200 : poDS->SetupNBits();
1826 :
1827 200 : if (poDS->nBands > 1)
1828 : {
1829 58 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
1830 : }
1831 : /* -------------------------------------------------------------------- */
1832 : /* Set up projection. */
1833 : /* */
1834 : /* XXX: If projection value is not specified, but image still have */
1835 : /* georeferencing information, assume Gauss-Kruger projection. */
1836 : /* -------------------------------------------------------------------- */
1837 200 : if (poDS->sHeader.iEPSGCode > RMF_EPSG_MIN_CODE ||
1838 186 : poDS->sHeader.iProjection > 0 ||
1839 117 : (poDS->sHeader.dfPixelSize != 0.0 && poDS->sHeader.dfLLX != 0.0 &&
1840 15 : poDS->sHeader.dfLLY != 0.0))
1841 : {
1842 98 : GInt32 nProj =
1843 98 : (poDS->sHeader.iProjection) ? poDS->sHeader.iProjection : 1;
1844 98 : double padfPrjParams[8] = {poDS->sHeader.dfStdP1,
1845 98 : poDS->sHeader.dfStdP2,
1846 98 : poDS->sHeader.dfCenterLat,
1847 98 : poDS->sHeader.dfCenterLong,
1848 : 1.0,
1849 : 0.0,
1850 : 0.0,
1851 98 : 0.0};
1852 :
1853 : // XXX: Compute zone number for Gauss-Kruger (Transverse Mercator)
1854 : // projection if it is not specified.
1855 98 : if (nProj == 1L && poDS->sHeader.dfCenterLong == 0.0)
1856 : {
1857 6 : if (poDS->sExtHeader.nZone == 0)
1858 : {
1859 0 : double centerXCoord =
1860 0 : poDS->sHeader.dfLLX +
1861 0 : (poDS->nRasterXSize * poDS->sHeader.dfPixelSize / 2.0);
1862 0 : padfPrjParams[7] = floor((centerXCoord - 500000.0) / 1000000.0);
1863 : }
1864 : else
1865 : {
1866 6 : padfPrjParams[7] = poDS->sExtHeader.nZone;
1867 : }
1868 : }
1869 :
1870 98 : OGRErr res = OGRERR_FAILURE;
1871 98 : if (nProj >= 0 &&
1872 88 : (poDS->sExtHeader.nDatum >= 0 || poDS->sExtHeader.nEllipsoid >= 0))
1873 : {
1874 88 : res = poDS->m_oSRS.importFromPanorama(
1875 88 : nProj, poDS->sExtHeader.nDatum, poDS->sExtHeader.nEllipsoid,
1876 : padfPrjParams);
1877 : }
1878 :
1879 111 : if (poDS->sHeader.iEPSGCode > RMF_EPSG_MIN_CODE &&
1880 13 : (OGRERR_NONE != res || poDS->m_oSRS.IsLocal()))
1881 : {
1882 1 : res = poDS->m_oSRS.importFromEPSG(poDS->sHeader.iEPSGCode);
1883 : }
1884 :
1885 : const char *pszSetVertCS =
1886 98 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "RMF_SET_VERTCS",
1887 : CPLGetConfigOption("RMF_SET_VERTCS", "NO"));
1888 98 : if (CPLTestBool(pszSetVertCS) && res == OGRERR_NONE &&
1889 0 : poDS->sExtHeader.nVertDatum > 0)
1890 : {
1891 0 : poDS->m_oSRS.importVertCSFromPanorama(poDS->sExtHeader.nVertDatum);
1892 : }
1893 : }
1894 :
1895 : /* -------------------------------------------------------------------- */
1896 : /* Set up georeferencing. */
1897 : /* -------------------------------------------------------------------- */
1898 200 : if ((poDS->eRMFType == RMFT_RSW && poDS->sHeader.iGeorefFlag) ||
1899 128 : (poDS->eRMFType == RMFT_MTW && poDS->sHeader.dfPixelSize != 0.0))
1900 : {
1901 146 : poDS->adfGeoTransform[0] = poDS->sHeader.dfLLX;
1902 292 : poDS->adfGeoTransform[3] =
1903 146 : poDS->sHeader.dfLLY +
1904 146 : poDS->nRasterYSize * poDS->sHeader.dfPixelSize;
1905 146 : poDS->adfGeoTransform[1] = poDS->sHeader.dfPixelSize;
1906 146 : poDS->adfGeoTransform[5] = -poDS->sHeader.dfPixelSize;
1907 146 : poDS->adfGeoTransform[2] = 0.0;
1908 146 : poDS->adfGeoTransform[4] = 0.0;
1909 : }
1910 :
1911 : /* -------------------------------------------------------------------- */
1912 : /* Set units. */
1913 : /* -------------------------------------------------------------------- */
1914 :
1915 200 : if (poDS->eRMFType == RMFT_MTW)
1916 : {
1917 74 : CPLFree(poDS->pszUnitType);
1918 74 : poDS->pszUnitType = RMFUnitTypeToStr(poDS->sHeader.iElevationUnit);
1919 : }
1920 :
1921 : /* -------------------------------------------------------------------- */
1922 : /* Report some other dataset related information. */
1923 : /* -------------------------------------------------------------------- */
1924 :
1925 200 : if (poDS->eRMFType == RMFT_MTW)
1926 : {
1927 74 : char szTemp[256] = {};
1928 :
1929 74 : snprintf(szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[0]);
1930 74 : poDS->SetMetadataItem("ELEVATION_MINIMUM", szTemp);
1931 :
1932 74 : snprintf(szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[1]);
1933 74 : poDS->SetMetadataItem("ELEVATION_MAXIMUM", szTemp);
1934 :
1935 74 : poDS->SetMetadataItem("ELEVATION_UNITS", poDS->pszUnitType);
1936 :
1937 74 : snprintf(szTemp, sizeof(szTemp), "%d", poDS->sHeader.iElevationType);
1938 74 : poDS->SetMetadataItem("ELEVATION_TYPE", szTemp);
1939 : }
1940 :
1941 : /* -------------------------------------------------------------------- */
1942 : /* Check for overviews. */
1943 : /* -------------------------------------------------------------------- */
1944 200 : if (nNextHeaderOffset == 0 && poParentDS == nullptr)
1945 : {
1946 119 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
1947 : }
1948 :
1949 : /* Set frame */
1950 200 : if (poDS->sHeader.nROIOffset &&
1951 107 : poDS->sHeader.nROISize >= sizeof(RSWFrame) &&
1952 12 : poDS->sHeader.nROISize <= ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE)
1953 : {
1954 : GByte *pabyROI = reinterpret_cast<GByte *>(
1955 12 : VSI_MALLOC_VERBOSE(poDS->sHeader.nROISize));
1956 12 : if (pabyROI == nullptr)
1957 : {
1958 0 : delete poDS;
1959 0 : return nullptr;
1960 : }
1961 :
1962 12 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nROIOffset),
1963 : SEEK_SET);
1964 12 : if (VSIFReadL(pabyROI, poDS->sHeader.nROISize, 1, poDS->fp) != 1)
1965 : {
1966 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot read ROI");
1967 0 : CPLFree(pabyROI);
1968 0 : delete poDS;
1969 0 : return nullptr;
1970 : }
1971 :
1972 : GInt32 nFrameType;
1973 12 : RMF_READ_LONG(pabyROI, nFrameType, 0);
1974 12 : if (nFrameType == nPolygonType)
1975 : {
1976 24 : CPLString osWKT = "POLYGON((";
1977 12 : bool bFirst = true;
1978 :
1979 12 : CPLDebug("RMF", "ROI coordinates:");
1980 : /* coverity[tainted_data] */
1981 12 : for (GUInt32 i = sizeof(RSWFrame);
1982 1734 : i + sizeof(RSWFrameCoord) <= poDS->sHeader.nROISize;
1983 1722 : i += sizeof(RSWFrameCoord))
1984 : {
1985 : GInt32 nX, nY;
1986 1722 : RMF_READ_LONG(pabyROI, nX, i);
1987 1722 : RMF_READ_LONG(pabyROI, nY, i + 4);
1988 :
1989 1722 : CPLDebug("RMF", "X: %d, Y: %d", nX, nY);
1990 :
1991 1722 : double dfX = poDS->adfGeoTransform[0] +
1992 1722 : nX * poDS->adfGeoTransform[1] +
1993 1722 : nY * poDS->adfGeoTransform[2];
1994 1722 : double dfY = poDS->adfGeoTransform[3] +
1995 1722 : nX * poDS->adfGeoTransform[4] +
1996 1722 : nY * poDS->adfGeoTransform[5];
1997 :
1998 1722 : if (bFirst)
1999 : {
2000 12 : osWKT += CPLSPrintf("%f %f", dfX, dfY);
2001 12 : bFirst = false;
2002 : }
2003 : else
2004 : {
2005 1710 : osWKT += CPLSPrintf(", %f %f", dfX, dfY);
2006 : }
2007 : }
2008 12 : osWKT += "))";
2009 12 : CPLDebug("RMF", "Frame WKT: %s", osWKT.c_str());
2010 12 : poDS->SetMetadataItem(MD_FRAME_KEY, osWKT);
2011 : }
2012 12 : CPLFree(pabyROI);
2013 : }
2014 :
2015 : #undef RMF_READ_DOUBLE
2016 : #undef RMF_READ_LONG
2017 : #undef RMF_READ_ULONG
2018 :
2019 200 : if (poDS->sHeader.nFlagsTblOffset && poDS->sHeader.nFlagsTblSize)
2020 : {
2021 107 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nFlagsTblOffset),
2022 : SEEK_SET);
2023 107 : CPLDebug("RMF", "Blocks flags:");
2024 : /* coverity[tainted_data] */
2025 416 : for (GUInt32 i = 0; i < poDS->sHeader.nFlagsTblSize; i += sizeof(GByte))
2026 : {
2027 : GByte nValue;
2028 309 : if (VSIFReadL(&nValue, 1, sizeof(nValue), poDS->fp) !=
2029 : sizeof(nValue))
2030 : {
2031 0 : CPLDebug("RMF", "Cannot read Block flag at index %u", i);
2032 0 : break;
2033 : }
2034 309 : CPLDebug("RMF", "Block %u -- flag %d", i, nValue);
2035 : }
2036 : }
2037 200 : return poDS;
2038 : }
2039 :
2040 : /************************************************************************/
2041 : /* Create() */
2042 : /************************************************************************/
2043 89 : GDALDataset *RMFDataset::Create(const char *pszFilename, int nXSize, int nYSize,
2044 : int nBandsIn, GDALDataType eType,
2045 : char **papszParamList)
2046 : {
2047 89 : return Create(pszFilename, nXSize, nYSize, nBandsIn, eType, papszParamList,
2048 89 : nullptr, 1.0);
2049 : }
2050 :
2051 123 : GDALDataset *RMFDataset::Create(const char *pszFilename, int nXSize, int nYSize,
2052 : int nBandsIn, GDALDataType eType,
2053 : char **papszParamList, RMFDataset *poParentDS,
2054 : double dfOvFactor)
2055 :
2056 : {
2057 123 : if (nBandsIn != 1 && nBandsIn != 3)
2058 : {
2059 7 : CPLError(CE_Failure, CPLE_NotSupported,
2060 : "RMF driver doesn't support %d bands. Must be 1 or 3.",
2061 : nBandsIn);
2062 :
2063 7 : return nullptr;
2064 : }
2065 :
2066 116 : if (nBandsIn == 1 && eType != GDT_Byte && eType != GDT_Int16 &&
2067 52 : eType != GDT_Int32 && eType != GDT_Float64)
2068 : {
2069 17 : CPLError(
2070 : CE_Failure, CPLE_AppDefined,
2071 : "Attempt to create RMF dataset with an illegal data type (%s), "
2072 : "only Byte, Int16, Int32 and Float64 types supported "
2073 : "by the format for single-band images.",
2074 : GDALGetDataTypeName(eType));
2075 :
2076 17 : return nullptr;
2077 : }
2078 :
2079 99 : if (nBandsIn == 3 && eType != GDT_Byte)
2080 : {
2081 13 : CPLError(
2082 : CE_Failure, CPLE_AppDefined,
2083 : "Attempt to create RMF dataset with an illegal data type (%s), "
2084 : "only Byte type supported by the format for three-band images.",
2085 : GDALGetDataTypeName(eType));
2086 :
2087 13 : return nullptr;
2088 : }
2089 :
2090 : /* -------------------------------------------------------------------- */
2091 : /* Create the dataset. */
2092 : /* -------------------------------------------------------------------- */
2093 86 : RMFDataset *poDS = new RMFDataset();
2094 :
2095 86 : GUInt32 nBlockXSize =
2096 86 : (nXSize < RMF_DEFAULT_BLOCKXSIZE) ? nXSize : RMF_DEFAULT_BLOCKXSIZE;
2097 86 : GUInt32 nBlockYSize =
2098 86 : (nYSize < RMF_DEFAULT_BLOCKYSIZE) ? nYSize : RMF_DEFAULT_BLOCKYSIZE;
2099 : double dfScale;
2100 : double dfResolution;
2101 : double dfPixelSize;
2102 86 : if (poParentDS == nullptr)
2103 : {
2104 52 : poDS->fp = VSIFOpenL(pszFilename, "w+b");
2105 52 : if (poDS->fp == nullptr)
2106 : {
2107 3 : CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create file %s.",
2108 : pszFilename);
2109 3 : delete poDS;
2110 3 : return nullptr;
2111 : }
2112 :
2113 : const char *pszScaleValue =
2114 49 : CSLFetchNameValue(papszParamList, MD_SCALE_KEY);
2115 49 : if (pszScaleValue != nullptr && CPLStrnlen(pszScaleValue, 10) > 4)
2116 : {
2117 0 : dfScale = atof(pszScaleValue + 4);
2118 : }
2119 : else
2120 : {
2121 49 : dfScale = RMF_DEFAULT_SCALE;
2122 : }
2123 49 : dfResolution = RMF_DEFAULT_RESOLUTION;
2124 49 : dfPixelSize = 1;
2125 :
2126 49 : if (CPLFetchBool(papszParamList, "MTW", false))
2127 12 : poDS->eRMFType = RMFT_MTW;
2128 : else
2129 37 : poDS->eRMFType = RMFT_RSW;
2130 :
2131 49 : GUInt32 iVersion = RMF_VERSION;
2132 49 : const char *pszRMFHUGE = CSLFetchNameValue(papszParamList, "RMFHUGE");
2133 :
2134 49 : if (pszRMFHUGE == nullptr)
2135 35 : pszRMFHUGE = "NO"; // Keep old behavior by default
2136 :
2137 49 : if (EQUAL(pszRMFHUGE, "NO"))
2138 : {
2139 42 : iVersion = RMF_VERSION;
2140 : }
2141 7 : else if (EQUAL(pszRMFHUGE, "YES"))
2142 : {
2143 7 : iVersion = RMF_VERSION_HUGE;
2144 : }
2145 0 : else if (EQUAL(pszRMFHUGE, "IF_SAFER"))
2146 : {
2147 : const double dfImageSize =
2148 0 : static_cast<double>(nXSize) * static_cast<double>(nYSize) *
2149 0 : static_cast<double>(nBandsIn) *
2150 0 : static_cast<double>(GDALGetDataTypeSizeBytes(eType));
2151 0 : if (dfImageSize > 3.0 * 1024.0 * 1024.0 * 1024.0)
2152 : {
2153 0 : iVersion = RMF_VERSION_HUGE;
2154 : }
2155 : else
2156 : {
2157 0 : iVersion = RMF_VERSION;
2158 : }
2159 : }
2160 :
2161 49 : const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKXSIZE");
2162 49 : if (pszValue != nullptr)
2163 1 : nBlockXSize = atoi(pszValue);
2164 49 : if (static_cast<int>(nBlockXSize) <= 0)
2165 0 : nBlockXSize = RMF_DEFAULT_BLOCKXSIZE;
2166 :
2167 49 : pszValue = CSLFetchNameValue(papszParamList, "BLOCKYSIZE");
2168 49 : if (pszValue != nullptr)
2169 1 : nBlockYSize = atoi(pszValue);
2170 49 : if (static_cast<int>(nBlockYSize) <= 0)
2171 0 : nBlockYSize = RMF_DEFAULT_BLOCKXSIZE;
2172 :
2173 49 : if (poDS->eRMFType == RMFT_MTW)
2174 12 : memcpy(poDS->sHeader.bySignature, RMF_SigMTW, RMF_SIGNATURE_SIZE);
2175 : else
2176 37 : memcpy(poDS->sHeader.bySignature, RMF_SigRSW, RMF_SIGNATURE_SIZE);
2177 49 : poDS->sHeader.iVersion = iVersion;
2178 49 : poDS->sHeader.nOvrOffset = 0x00;
2179 : }
2180 : else
2181 : {
2182 34 : poDS->fp = poParentDS->fp;
2183 34 : memcpy(poDS->sHeader.bySignature, poParentDS->sHeader.bySignature,
2184 : RMF_SIGNATURE_SIZE);
2185 34 : poDS->sHeader.iVersion = poParentDS->sHeader.iVersion;
2186 34 : poDS->eRMFType = poParentDS->eRMFType;
2187 34 : nBlockXSize = poParentDS->sHeader.nTileWidth;
2188 34 : nBlockYSize = poParentDS->sHeader.nTileHeight;
2189 34 : dfScale = poParentDS->sHeader.dfScale;
2190 34 : dfResolution = poParentDS->sHeader.dfResolution / dfOvFactor;
2191 34 : dfPixelSize = poParentDS->sHeader.dfPixelSize * dfOvFactor;
2192 :
2193 34 : poDS->nHeaderOffset = poParentDS->GetLastOffset();
2194 34 : poParentDS->sHeader.nOvrOffset =
2195 34 : poDS->GetRMFOffset(poDS->nHeaderOffset, &poDS->nHeaderOffset);
2196 34 : poParentDS->bHeaderDirty = true;
2197 34 : VSIFSeekL(poDS->fp, poDS->nHeaderOffset, SEEK_SET);
2198 34 : poDS->poParentDS = poParentDS;
2199 34 : CPLDebug("RMF",
2200 : "Create overview subfile at " CPL_FRMT_GUIB
2201 : " with size %dx%d, parent overview offset %d",
2202 : poDS->nHeaderOffset, nXSize, nYSize,
2203 : poParentDS->sHeader.nOvrOffset);
2204 : }
2205 : /* -------------------------------------------------------------------- */
2206 : /* Fill the RMFHeader */
2207 : /* -------------------------------------------------------------------- */
2208 83 : CPLDebug("RMF", "Version %d", poDS->sHeader.iVersion);
2209 :
2210 83 : poDS->sHeader.iUserID = 0x00;
2211 83 : memset(poDS->sHeader.byName, 0, sizeof(poDS->sHeader.byName));
2212 83 : poDS->sHeader.nBitDepth = GDALGetDataTypeSizeBits(eType) * nBandsIn;
2213 83 : poDS->sHeader.nHeight = nYSize;
2214 83 : poDS->sHeader.nWidth = nXSize;
2215 83 : poDS->sHeader.nTileWidth = nBlockXSize;
2216 83 : poDS->sHeader.nTileHeight = nBlockYSize;
2217 :
2218 83 : poDS->nXTiles = poDS->sHeader.nXTiles =
2219 83 : (nXSize + poDS->sHeader.nTileWidth - 1) / poDS->sHeader.nTileWidth;
2220 83 : poDS->nYTiles = poDS->sHeader.nYTiles =
2221 83 : (nYSize + poDS->sHeader.nTileHeight - 1) / poDS->sHeader.nTileHeight;
2222 83 : poDS->sHeader.nLastTileHeight = nYSize % poDS->sHeader.nTileHeight;
2223 83 : if (!poDS->sHeader.nLastTileHeight)
2224 44 : poDS->sHeader.nLastTileHeight = poDS->sHeader.nTileHeight;
2225 83 : poDS->sHeader.nLastTileWidth = nXSize % poDS->sHeader.nTileWidth;
2226 83 : if (!poDS->sHeader.nLastTileWidth)
2227 40 : poDS->sHeader.nLastTileWidth = poDS->sHeader.nTileWidth;
2228 :
2229 : // poDS->sHeader.nROIOffset = 0x00;
2230 : // poDS->sHeader.nROISize = 0x00;
2231 :
2232 83 : vsi_l_offset nCurPtr = poDS->nHeaderOffset + RMF_HEADER_SIZE;
2233 :
2234 : // Extended header
2235 83 : poDS->sHeader.nExtHdrOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2236 83 : poDS->sHeader.nExtHdrSize = RMF_EXT_HEADER_SIZE;
2237 83 : nCurPtr += poDS->sHeader.nExtHdrSize;
2238 :
2239 : // Color table
2240 83 : if (poDS->eRMFType == RMFT_RSW && nBandsIn == 1)
2241 : {
2242 34 : if (poDS->sHeader.nBitDepth > 8)
2243 : {
2244 6 : CPLError(CE_Failure, CPLE_AppDefined,
2245 : "Cannot create color table of RSW with nBitDepth = %d. "
2246 : "Retry with MTW ?",
2247 : poDS->sHeader.nBitDepth);
2248 6 : delete poDS;
2249 6 : return nullptr;
2250 : }
2251 :
2252 28 : poDS->sHeader.nClrTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2253 28 : poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
2254 28 : poDS->sHeader.nClrTblSize = poDS->nColorTableSize * 4;
2255 28 : poDS->pabyColorTable =
2256 28 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(poDS->sHeader.nClrTblSize));
2257 28 : if (poDS->pabyColorTable == nullptr)
2258 : {
2259 0 : delete poDS;
2260 0 : return nullptr;
2261 : }
2262 7196 : for (GUInt32 i = 0; i < poDS->nColorTableSize; i++)
2263 : {
2264 7168 : poDS->pabyColorTable[i * 4 + 0] = static_cast<GByte>(i);
2265 7168 : poDS->pabyColorTable[i * 4 + 1] = static_cast<GByte>(i);
2266 7168 : poDS->pabyColorTable[i * 4 + 2] = static_cast<GByte>(i);
2267 7168 : poDS->pabyColorTable[i * 4 + 3] = 0;
2268 : }
2269 28 : nCurPtr += poDS->sHeader.nClrTblSize;
2270 : }
2271 : else
2272 : {
2273 49 : poDS->sHeader.nClrTblOffset = 0x00;
2274 49 : poDS->sHeader.nClrTblSize = 0x00;
2275 : }
2276 :
2277 : // Add room for ROI (frame)
2278 77 : poDS->sHeader.nROIOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2279 77 : poDS->sHeader.nROISize = 0x00;
2280 77 : nCurPtr +=
2281 : sizeof(RSWFrame) +
2282 : sizeof(RSWFrameCoord) *
2283 : nMaxFramePointCount; // Allocate nMaxFramePointCount coordinates for frame
2284 :
2285 : // Add blocks flags
2286 77 : poDS->sHeader.nFlagsTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2287 77 : poDS->sHeader.nFlagsTblSize =
2288 77 : sizeof(GByte) * poDS->sHeader.nXTiles * poDS->sHeader.nYTiles;
2289 77 : nCurPtr += poDS->sHeader.nFlagsTblSize;
2290 :
2291 : // Blocks table
2292 77 : poDS->sHeader.nTileTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2293 77 : poDS->sHeader.nTileTblSize =
2294 77 : 2 * sizeof(GUInt32) * poDS->sHeader.nXTiles * poDS->sHeader.nYTiles;
2295 77 : poDS->paiTiles =
2296 77 : reinterpret_cast<GUInt32 *>(CPLCalloc(poDS->sHeader.nTileTblSize, 1));
2297 : // nCurPtr += poDS->sHeader.nTileTblSize;
2298 77 : const GUInt32 nTileSize = poDS->sHeader.nTileWidth *
2299 77 : poDS->sHeader.nTileHeight *
2300 77 : GDALGetDataTypeSizeBytes(eType);
2301 77 : poDS->sHeader.nSize =
2302 77 : poDS->paiTiles[poDS->sHeader.nTileTblSize / 4 - 2] + nTileSize;
2303 :
2304 : // Elevation units
2305 77 : poDS->sHeader.iElevationUnit = RMFStrToUnitType(poDS->pszUnitType);
2306 :
2307 77 : poDS->sHeader.iMapType = -1;
2308 77 : poDS->sHeader.iProjection = -1;
2309 77 : poDS->sHeader.iEPSGCode = -1;
2310 77 : poDS->sHeader.dfScale = dfScale;
2311 77 : poDS->sHeader.dfResolution = dfResolution;
2312 77 : poDS->sHeader.dfPixelSize = dfPixelSize;
2313 77 : poDS->sHeader.iMaskType = 0;
2314 77 : poDS->sHeader.iMaskStep = 0;
2315 77 : poDS->sHeader.iFrameFlag = 1; // 1 - Frame not using
2316 : // poDS->sHeader.nFlagsTblOffset = 0x00;
2317 : // poDS->sHeader.nFlagsTblSize = 0x00;
2318 77 : poDS->sHeader.nFileSize0 = 0x00;
2319 77 : poDS->sHeader.nFileSize1 = 0x00;
2320 77 : poDS->sHeader.iUnknown = 0;
2321 77 : poDS->sHeader.iGeorefFlag = 0;
2322 77 : poDS->sHeader.iInverse = 0;
2323 77 : poDS->sHeader.iJpegQuality = 0;
2324 77 : memset(poDS->sHeader.abyInvisibleColors, 0,
2325 : sizeof(poDS->sHeader.abyInvisibleColors));
2326 77 : poDS->sHeader.iElevationType = 0;
2327 :
2328 77 : poDS->nRasterXSize = nXSize;
2329 77 : poDS->nRasterYSize = nYSize;
2330 77 : poDS->eAccess = GA_Update;
2331 77 : poDS->nBands = nBandsIn;
2332 :
2333 77 : if (poParentDS == nullptr)
2334 : {
2335 43 : poDS->sHeader.adfElevMinMax[0] = 0.0;
2336 43 : poDS->sHeader.adfElevMinMax[1] = 0.0;
2337 43 : poDS->sHeader.dfNoData = 0.0;
2338 43 : poDS->sHeader.iCompression =
2339 43 : GetCompressionType(CSLFetchNameValue(papszParamList, "COMPRESS"));
2340 43 : if (CE_None != poDS->InitCompressorData(papszParamList))
2341 : {
2342 0 : delete poDS;
2343 0 : return nullptr;
2344 : }
2345 :
2346 43 : if (poDS->sHeader.iCompression == RMF_COMPRESSION_JPEG)
2347 : {
2348 : const char *pszJpegQuality =
2349 1 : CSLFetchNameValue(papszParamList, "JPEG_QUALITY");
2350 1 : if (pszJpegQuality == nullptr)
2351 : {
2352 1 : poDS->sHeader.iJpegQuality = 75;
2353 : }
2354 : else
2355 : {
2356 0 : int iJpegQuality = atoi(pszJpegQuality);
2357 0 : if (iJpegQuality < 10 || iJpegQuality > 100)
2358 : {
2359 0 : CPLError(CE_Failure, CPLE_IllegalArg,
2360 : "JPEG_QUALITY=%s is not a legal value in the "
2361 : "range 10-100.\n"
2362 : "Defaulting to 75",
2363 : pszJpegQuality);
2364 0 : iJpegQuality = 75;
2365 : }
2366 0 : poDS->sHeader.iJpegQuality = static_cast<GByte>(iJpegQuality);
2367 : }
2368 : }
2369 :
2370 43 : if (CE_None != poDS->SetupCompression(eType, pszFilename))
2371 : {
2372 0 : delete poDS;
2373 0 : return nullptr;
2374 : }
2375 : }
2376 : else
2377 : {
2378 34 : poDS->sHeader.adfElevMinMax[0] = poParentDS->sHeader.adfElevMinMax[0];
2379 34 : poDS->sHeader.adfElevMinMax[1] = poParentDS->sHeader.adfElevMinMax[1];
2380 34 : poDS->sHeader.dfNoData = poParentDS->sHeader.dfNoData;
2381 34 : poDS->sHeader.iCompression = poParentDS->sHeader.iCompression;
2382 34 : poDS->sHeader.iJpegQuality = poParentDS->sHeader.iJpegQuality;
2383 34 : poDS->Decompress = poParentDS->Decompress;
2384 34 : poDS->Compress = poParentDS->Compress;
2385 34 : poDS->poCompressData = poParentDS->poCompressData;
2386 : }
2387 :
2388 77 : if (nBandsIn > 1)
2389 : {
2390 13 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
2391 : }
2392 :
2393 77 : poDS->WriteHeader();
2394 :
2395 : /* -------------------------------------------------------------------- */
2396 : /* Create band information objects. */
2397 : /* -------------------------------------------------------------------- */
2398 180 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
2399 103 : poDS->SetBand(iBand, new RMFRasterBand(poDS, iBand, eType));
2400 :
2401 77 : poDS->SetupNBits();
2402 :
2403 77 : return GDALDataset::FromHandle(poDS);
2404 : }
2405 :
2406 : // GIS Panorama 11 was introduced new format for huge files (greater than 3 Gb)
2407 3859 : vsi_l_offset RMFDataset::GetFileOffset(GUInt32 iRMFOffset) const
2408 : {
2409 3859 : if (sHeader.iVersion >= RMF_VERSION_HUGE)
2410 : {
2411 1142 : return (static_cast<vsi_l_offset>(iRMFOffset)) * RMF_HUGE_OFFSET_FACTOR;
2412 : }
2413 :
2414 2717 : return static_cast<vsi_l_offset>(iRMFOffset);
2415 : }
2416 :
2417 966 : GUInt32 RMFDataset::GetRMFOffset(vsi_l_offset nFileOffset,
2418 : vsi_l_offset *pnNewFileOffset) const
2419 : {
2420 966 : if (sHeader.iVersion >= RMF_VERSION_HUGE)
2421 : {
2422 : // Round offset to next RMF_HUGE_OFFSET_FACTOR
2423 272 : const GUInt32 iRMFOffset =
2424 272 : static_cast<GUInt32>((nFileOffset + (RMF_HUGE_OFFSET_FACTOR - 1)) /
2425 : RMF_HUGE_OFFSET_FACTOR);
2426 272 : if (pnNewFileOffset != nullptr)
2427 : {
2428 205 : *pnNewFileOffset = GetFileOffset(iRMFOffset);
2429 : }
2430 272 : return iRMFOffset;
2431 : }
2432 :
2433 694 : if (pnNewFileOffset != nullptr)
2434 : {
2435 561 : *pnNewFileOffset = nFileOffset;
2436 : }
2437 694 : return static_cast<GUInt32>(nFileOffset);
2438 : }
2439 :
2440 200 : RMFDataset *RMFDataset::OpenOverview(RMFDataset *poParent,
2441 : GDALOpenInfo *poOpenInfo)
2442 : {
2443 200 : if (sHeader.nOvrOffset == 0)
2444 : {
2445 111 : return nullptr;
2446 : }
2447 :
2448 89 : if (poParent == nullptr)
2449 : {
2450 0 : return nullptr;
2451 : }
2452 :
2453 89 : vsi_l_offset nSubOffset = GetFileOffset(sHeader.nOvrOffset);
2454 :
2455 89 : CPLDebug("RMF",
2456 : "Try to open overview subfile at " CPL_FRMT_GUIB " for '%s'",
2457 : nSubOffset, poOpenInfo->pszFilename);
2458 :
2459 89 : if (!poParent->poOvrDatasets.empty())
2460 : {
2461 49 : if (poParent->GetFileOffset(poParent->sHeader.nOvrOffset) == nSubOffset)
2462 : {
2463 2 : CPLError(CE_Warning, CPLE_IllegalArg,
2464 : "Recursive subdataset list is detected. "
2465 : "Overview open failed.");
2466 2 : return nullptr;
2467 : }
2468 :
2469 58 : for (size_t n = 0; n != poParent->poOvrDatasets.size() - 1; ++n)
2470 : {
2471 13 : RMFDataset *poOvr(poParent->poOvrDatasets[n]);
2472 :
2473 13 : if (poOvr == nullptr)
2474 0 : continue;
2475 13 : if (poOvr->GetFileOffset(poOvr->sHeader.nOvrOffset) == nSubOffset)
2476 : {
2477 2 : CPLError(CE_Warning, CPLE_IllegalArg,
2478 : "Recursive subdataset list is detected. "
2479 : "Overview open failed.");
2480 2 : return nullptr;
2481 : }
2482 : }
2483 : }
2484 :
2485 85 : size_t nHeaderSize(RMF_HEADER_SIZE);
2486 : GByte *pabyNewHeader;
2487 : pabyNewHeader = static_cast<GByte *>(
2488 85 : CPLRealloc(poOpenInfo->pabyHeader, nHeaderSize + 1));
2489 85 : if (pabyNewHeader == nullptr)
2490 : {
2491 0 : CPLError(CE_Warning, CPLE_OutOfMemory,
2492 : "Can't allocate buffer for overview header");
2493 0 : return nullptr;
2494 : }
2495 :
2496 85 : poOpenInfo->pabyHeader = pabyNewHeader;
2497 85 : memset(poOpenInfo->pabyHeader, 0, nHeaderSize + 1);
2498 85 : VSIFSeekL(fp, nSubOffset, SEEK_SET);
2499 85 : poOpenInfo->nHeaderBytes =
2500 85 : static_cast<int>(VSIFReadL(poOpenInfo->pabyHeader, 1, nHeaderSize, fp));
2501 :
2502 85 : return Open(poOpenInfo, poParent, nSubOffset);
2503 : }
2504 :
2505 17 : CPLErr RMFDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
2506 : const int *panOverviewList, int nBandsIn,
2507 : const int *panBandList,
2508 : GDALProgressFunc pfnProgress,
2509 : void *pProgressData,
2510 : CSLConstList papszOptions)
2511 : {
2512 17 : bool bUseGenericHandling = false;
2513 :
2514 17 : if (GetAccess() != GA_Update)
2515 : {
2516 0 : CPLDebug("RMF", "File open for read-only accessing, "
2517 : "creating overviews externally.");
2518 :
2519 0 : bUseGenericHandling = true;
2520 : }
2521 :
2522 17 : if (bUseGenericHandling)
2523 : {
2524 0 : if (!poOvrDatasets.empty())
2525 : {
2526 0 : CPLError(CE_Failure, CPLE_NotSupported,
2527 : "Cannot add external overviews when there are already "
2528 : "internal overviews");
2529 0 : return CE_Failure;
2530 : }
2531 :
2532 0 : return GDALDataset::IBuildOverviews(
2533 : pszResampling, nOverviews, panOverviewList, nBandsIn, panBandList,
2534 0 : pfnProgress, pProgressData, papszOptions);
2535 : }
2536 :
2537 17 : if (nBandsIn != GetRasterCount())
2538 : {
2539 0 : CPLError(CE_Failure, CPLE_NotSupported,
2540 : "Generation of overviews in RMF is only "
2541 : "supported when operating on all bands. "
2542 : "Operation failed.");
2543 0 : return CE_Failure;
2544 : }
2545 :
2546 17 : if (nOverviews == 0)
2547 : {
2548 0 : if (poOvrDatasets.empty())
2549 : {
2550 0 : return GDALDataset::IBuildOverviews(
2551 : pszResampling, nOverviews, panOverviewList, nBandsIn,
2552 0 : panBandList, pfnProgress, pProgressData, papszOptions);
2553 : }
2554 0 : return CleanOverviews();
2555 : }
2556 :
2557 : // First destroy old overviews
2558 17 : if (CE_None != CleanOverviews())
2559 : {
2560 0 : return CE_Failure;
2561 : }
2562 :
2563 17 : CPLDebug("RMF", "Build overviews on dataset %d x %d size", GetRasterXSize(),
2564 : GetRasterYSize());
2565 :
2566 17 : GDALDataType eMainType = GetRasterBand(1)->GetRasterDataType();
2567 17 : RMFDataset *poParent = this;
2568 17 : double prevOvLevel = 1.0;
2569 51 : for (int n = 0; n != nOverviews; ++n)
2570 : {
2571 34 : int nOvLevel = panOverviewList[n];
2572 34 : const int nOXSize = (GetRasterXSize() + nOvLevel - 1) / nOvLevel;
2573 34 : const int nOYSize = (GetRasterYSize() + nOvLevel - 1) / nOvLevel;
2574 34 : CPLDebug("RMF", "\tCreate overview #%d size %d x %d", nOvLevel, nOXSize,
2575 : nOYSize);
2576 :
2577 : RMFDataset *poOvrDataset;
2578 34 : poOvrDataset = static_cast<RMFDataset *>(RMFDataset::Create(
2579 : nullptr, nOXSize, nOYSize, GetRasterCount(), eMainType, nullptr,
2580 : poParent, nOvLevel / prevOvLevel));
2581 :
2582 34 : if (poOvrDataset == nullptr)
2583 : {
2584 0 : CPLError(CE_Failure, CPLE_AppDefined,
2585 : "Can't create overview dataset #%d size %d x %d", nOvLevel,
2586 : nOXSize, nOYSize);
2587 0 : return CE_Failure;
2588 : }
2589 :
2590 34 : prevOvLevel = nOvLevel;
2591 34 : poParent = poOvrDataset;
2592 34 : poOvrDatasets.push_back(poOvrDataset);
2593 : }
2594 :
2595 : GDALRasterBand ***papapoOverviewBands =
2596 17 : static_cast<GDALRasterBand ***>(CPLCalloc(sizeof(void *), nBandsIn));
2597 : GDALRasterBand **papoBandList =
2598 17 : static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nBandsIn));
2599 :
2600 36 : for (int iBand = 0; iBand < nBandsIn; ++iBand)
2601 : {
2602 19 : GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
2603 :
2604 19 : papoBandList[iBand] = poBand;
2605 38 : papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
2606 19 : CPLCalloc(sizeof(void *), poBand->GetOverviewCount()));
2607 :
2608 57 : for (int i = 0; i < nOverviews; ++i)
2609 : {
2610 38 : papapoOverviewBands[iBand][i] = poBand->GetOverview(i);
2611 : }
2612 : }
2613 : #ifdef DEBUG
2614 36 : for (int iBand = 0; iBand < nBandsIn; ++iBand)
2615 : {
2616 19 : CPLDebug("RMF", "Try to create overview for #%d size %d x %d",
2617 19 : iBand + 1, papoBandList[iBand]->GetXSize(),
2618 19 : papoBandList[iBand]->GetYSize());
2619 57 : for (int i = 0; i < nOverviews; ++i)
2620 : {
2621 38 : CPLDebug("RMF", "\t%d x %d",
2622 38 : papapoOverviewBands[iBand][i]->GetXSize(),
2623 38 : papapoOverviewBands[iBand][i]->GetYSize());
2624 : }
2625 : }
2626 : #endif // DEBUG
2627 : CPLErr res;
2628 17 : res = GDALRegenerateOverviewsMultiBand(
2629 : nBandsIn, papoBandList, nOverviews, papapoOverviewBands, pszResampling,
2630 : pfnProgress, pProgressData, papszOptions);
2631 :
2632 36 : for (int iBand = 0; iBand < nBandsIn; ++iBand)
2633 : {
2634 19 : CPLFree(papapoOverviewBands[iBand]);
2635 : }
2636 :
2637 17 : CPLFree(papapoOverviewBands);
2638 17 : CPLFree(papoBandList);
2639 :
2640 17 : return res;
2641 : }
2642 :
2643 68 : CPLErr RMFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2644 : int nXSize, int nYSize, void *pData, int nBufXSize,
2645 : int nBufYSize, GDALDataType eBufType,
2646 : int nBandCount, BANDMAP_TYPE panBandMap,
2647 : GSpacing nPixelSpace, GSpacing nLineSpace,
2648 : GSpacing nBandSpace,
2649 : GDALRasterIOExtraArg *psExtraArg)
2650 : {
2651 : #ifdef DEBUG
2652 68 : CPLDebug("RMF", "Dataset %p, %s %d %d %d %d, %d %d", this,
2653 : (eRWFlag == GF_Read ? "Read" : "Write"), nXOff, nYOff, nXSize,
2654 : nYSize, nBufXSize, nBufYSize);
2655 : #endif // DEBUG
2656 68 : if (eRWFlag == GF_Read && poCompressData != nullptr &&
2657 0 : poCompressData->oThreadPool.GetThreadCount() > 0)
2658 : {
2659 0 : poCompressData->oThreadPool.WaitCompletion();
2660 : }
2661 :
2662 68 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
2663 : nBufXSize, nBufYSize, eBufType, nBandCount,
2664 : panBandMap, nPixelSpace, nLineSpace,
2665 68 : nBandSpace, psExtraArg);
2666 : }
2667 :
2668 238 : vsi_l_offset RMFDataset::GetLastOffset() const
2669 : {
2670 238 : vsi_l_offset nLastTileOff = 0;
2671 238 : GUInt32 nTiles(sHeader.nTileTblSize / sizeof(GUInt32));
2672 :
2673 768 : for (GUInt32 n = 0; n < nTiles; n += 2)
2674 : {
2675 530 : vsi_l_offset nTileOffset = GetFileOffset(paiTiles[n]);
2676 530 : GUInt32 nTileBytes = paiTiles[n + 1];
2677 530 : nLastTileOff = std::max(nLastTileOff, nTileOffset + nTileBytes);
2678 : }
2679 :
2680 238 : nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nROIOffset) +
2681 238 : sHeader.nROISize);
2682 238 : nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nClrTblOffset) +
2683 238 : sHeader.nClrTblSize);
2684 238 : nLastTileOff =
2685 238 : std::max(nLastTileOff,
2686 238 : GetFileOffset(sHeader.nTileTblOffset) + sHeader.nTileTblSize);
2687 238 : nLastTileOff =
2688 238 : std::max(nLastTileOff, GetFileOffset(sHeader.nFlagsTblOffset) +
2689 238 : sHeader.nFlagsTblSize);
2690 238 : nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nExtHdrOffset) +
2691 238 : sHeader.nExtHdrSize);
2692 238 : return nLastTileOff;
2693 : }
2694 :
2695 17 : CPLErr RMFDataset::CleanOverviews()
2696 : {
2697 17 : if (sHeader.nOvrOffset == 0)
2698 : {
2699 13 : return CE_None;
2700 : }
2701 :
2702 4 : if (GetAccess() != GA_Update)
2703 : {
2704 0 : CPLError(CE_Failure, CPLE_NotSupported,
2705 : "File open for read-only accessing, "
2706 : "overviews cleanup failed.");
2707 0 : return CE_Failure;
2708 : }
2709 :
2710 4 : if (poParentDS != nullptr)
2711 : {
2712 0 : CPLError(CE_Failure, CPLE_NotSupported,
2713 : "Overviews cleanup for non-root dataset is not possible.");
2714 0 : return CE_Failure;
2715 : }
2716 :
2717 12 : for (size_t n = 0; n != poOvrDatasets.size(); ++n)
2718 : {
2719 8 : GDALClose(poOvrDatasets[n]);
2720 : }
2721 4 : poOvrDatasets.clear();
2722 :
2723 4 : vsi_l_offset nLastTileOff = GetLastOffset();
2724 :
2725 4 : if (0 != VSIFSeekL(fp, 0, SEEK_END))
2726 : {
2727 0 : CPLError(CE_Failure, CPLE_FileIO,
2728 : "Failed to seek to end of file, "
2729 : "overviews cleanup failed.");
2730 : }
2731 :
2732 4 : vsi_l_offset nFileSize = VSIFTellL(fp);
2733 4 : if (nFileSize < nLastTileOff)
2734 : {
2735 0 : CPLError(CE_Failure, CPLE_FileIO,
2736 : "Invalid file offset, "
2737 : "overviews cleanup failed.");
2738 0 : return CE_Failure;
2739 : }
2740 :
2741 4 : CPLDebug("RMF", "Truncate to " CPL_FRMT_GUIB, nLastTileOff);
2742 4 : CPLDebug("RMF", "File size: " CPL_FRMT_GUIB, nFileSize);
2743 :
2744 4 : if (0 != VSIFTruncateL(fp, nLastTileOff))
2745 : {
2746 0 : CPLError(CE_Failure, CPLE_FileIO,
2747 : "Failed to truncate file, "
2748 : "overviews cleanup failed.");
2749 0 : return CE_Failure;
2750 : }
2751 :
2752 4 : sHeader.nOvrOffset = 0;
2753 4 : bHeaderDirty = true;
2754 :
2755 4 : return CE_None;
2756 : }
2757 :
2758 : /************************************************************************/
2759 : /* GetCompressionType() */
2760 : /************************************************************************/
2761 :
2762 43 : GByte RMFDataset::GetCompressionType(const char *pszCompressName)
2763 : {
2764 43 : if (pszCompressName == nullptr || EQUAL(pszCompressName, "NONE"))
2765 : {
2766 35 : return RMF_COMPRESSION_NONE;
2767 : }
2768 8 : else if (EQUAL(pszCompressName, "LZW"))
2769 : {
2770 5 : return RMF_COMPRESSION_LZW;
2771 : }
2772 3 : else if (EQUAL(pszCompressName, "JPEG"))
2773 : {
2774 1 : return RMF_COMPRESSION_JPEG;
2775 : }
2776 2 : else if (EQUAL(pszCompressName, "RMF_DEM"))
2777 : {
2778 2 : return RMF_COMPRESSION_DEM;
2779 : }
2780 :
2781 0 : CPLError(CE_Failure, CPLE_AppDefined,
2782 : "RMF: Unknown compression scheme <%s>.\n"
2783 : "Defaults to NONE compression.",
2784 : pszCompressName);
2785 0 : return RMF_COMPRESSION_NONE;
2786 : }
2787 :
2788 : /************************************************************************/
2789 : /* SetupCompression() */
2790 : /************************************************************************/
2791 :
2792 243 : int RMFDataset::SetupCompression(GDALDataType eType, const char *pszFilename)
2793 : {
2794 : /* -------------------------------------------------------------------- */
2795 : /* XXX: The DEM compression method seems to be only applicable */
2796 : /* to Int32 data. */
2797 : /* -------------------------------------------------------------------- */
2798 243 : if (sHeader.iCompression == RMF_COMPRESSION_NONE)
2799 : {
2800 173 : Decompress = nullptr;
2801 173 : Compress = nullptr;
2802 : }
2803 70 : else if (sHeader.iCompression == RMF_COMPRESSION_LZW)
2804 : {
2805 57 : Decompress = &LZWDecompress;
2806 57 : Compress = &LZWCompress;
2807 57 : SetMetadataItem("COMPRESSION", "LZW", "IMAGE_STRUCTURE");
2808 : }
2809 13 : else if (sHeader.iCompression == RMF_COMPRESSION_JPEG)
2810 : {
2811 3 : if (eType != GDT_Byte || nBands != RMF_JPEG_BAND_COUNT ||
2812 3 : sHeader.nBitDepth != 24)
2813 : {
2814 0 : CPLError(CE_Failure, CPLE_AppDefined,
2815 : "RMF support only 24 bpp JPEG compressed files.");
2816 0 : return CE_Failure;
2817 : }
2818 : #ifdef HAVE_LIBJPEG
2819 6 : CPLString oBuf;
2820 3 : oBuf.Printf("%d", sHeader.iJpegQuality);
2821 3 : Decompress = &JPEGDecompress;
2822 3 : Compress = &JPEGCompress;
2823 3 : SetMetadataItem("JPEG_QUALITY", oBuf.c_str(), "IMAGE_STRUCTURE");
2824 3 : SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
2825 : #else // HAVE_LIBJPEG
2826 : CPLError(CE_Failure, CPLE_AppDefined,
2827 : "JPEG codec is needed to open <%s>.\n"
2828 : "Please rebuild GDAL with libjpeg support.",
2829 : pszFilename);
2830 : return CE_Failure;
2831 : #endif // HAVE_LIBJPEG
2832 : }
2833 10 : else if (sHeader.iCompression == RMF_COMPRESSION_DEM &&
2834 10 : eType == GDT_Int32 && nBands == RMF_DEM_BAND_COUNT)
2835 : {
2836 10 : Decompress = &DEMDecompress;
2837 10 : Compress = &DEMCompress;
2838 10 : SetMetadataItem("COMPRESSION", "RMF_DEM", "IMAGE_STRUCTURE");
2839 : }
2840 : else
2841 : {
2842 0 : CPLError(CE_Failure, CPLE_AppDefined,
2843 0 : "Unknown compression #%d at file <%s>.", sHeader.iCompression,
2844 : pszFilename);
2845 0 : return CE_Failure;
2846 : }
2847 :
2848 243 : return CE_None;
2849 : }
2850 :
2851 190 : void RMFDataset::WriteTileJobFunc(void *pData)
2852 : {
2853 190 : RMFCompressionJob *psJob = static_cast<RMFCompressionJob *>(pData);
2854 190 : RMFDataset *poDS = psJob->poDS;
2855 :
2856 : GByte *pabyTileData;
2857 : size_t nTileSize;
2858 :
2859 190 : if (poDS->Compress)
2860 : {
2861 : // RMF doesn't store compressed tiles with size greater than 80% of
2862 : // uncompressed size
2863 131 : GUInt32 nMaxCompressedTileSize =
2864 131 : static_cast<GUInt32>((psJob->nUncompressedBytes * 8) / 10);
2865 : size_t nCompressedBytes =
2866 262 : poDS->Compress(psJob->pabyUncompressedData,
2867 131 : static_cast<GUInt32>(psJob->nUncompressedBytes),
2868 : psJob->pabyCompressedData, nMaxCompressedTileSize,
2869 : psJob->nXSize, psJob->nYSize, poDS);
2870 131 : if (nCompressedBytes == 0)
2871 : {
2872 28 : pabyTileData = psJob->pabyUncompressedData;
2873 28 : nTileSize = psJob->nUncompressedBytes;
2874 : }
2875 : else
2876 : {
2877 103 : pabyTileData = psJob->pabyCompressedData;
2878 103 : nTileSize = nCompressedBytes;
2879 : }
2880 : }
2881 : else
2882 : {
2883 59 : pabyTileData = psJob->pabyUncompressedData;
2884 59 : nTileSize = psJob->nUncompressedBytes;
2885 : }
2886 :
2887 : {
2888 190 : CPLMutexHolder oHolder(poDS->poCompressData->hWriteTileMutex);
2889 190 : psJob->eResult = poDS->WriteRawTile(
2890 : psJob->nBlockXOff, psJob->nBlockYOff, pabyTileData, nTileSize);
2891 : }
2892 190 : if (poDS->poCompressData->oThreadPool.GetThreadCount() > 0)
2893 : {
2894 188 : CPLMutexHolder oHolder(poDS->poCompressData->hReadyJobMutex);
2895 94 : poDS->poCompressData->asReadyJobs.push_back(psJob);
2896 : }
2897 190 : }
2898 :
2899 55 : CPLErr RMFDataset::InitCompressorData(char **papszParamList)
2900 : {
2901 : const char *pszNumThreads =
2902 55 : CSLFetchNameValue(papszParamList, "NUM_THREADS");
2903 55 : if (pszNumThreads == nullptr)
2904 51 : pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
2905 :
2906 55 : int nThreads = 0;
2907 55 : if (pszNumThreads != nullptr)
2908 : {
2909 8 : nThreads = EQUAL(pszNumThreads, "ALL_CPUS") ? CPLGetNumCPUs()
2910 4 : : atoi(pszNumThreads);
2911 : }
2912 :
2913 55 : if (nThreads < 0)
2914 : {
2915 0 : nThreads = 0;
2916 : }
2917 55 : if (nThreads > 1024)
2918 : {
2919 0 : nThreads = 1024;
2920 : }
2921 :
2922 55 : poCompressData = std::make_shared<RMFCompressData>();
2923 55 : if (nThreads > 0)
2924 : {
2925 3 : if (!poCompressData->oThreadPool.Setup(nThreads, nullptr, nullptr))
2926 : {
2927 0 : CPLError(CE_Failure, CPLE_AppDefined,
2928 : "Can't setup %d compressor threads", nThreads);
2929 0 : return CE_Failure;
2930 : }
2931 : }
2932 :
2933 55 : poCompressData->asJobs.resize(nThreads + 1);
2934 :
2935 55 : size_t nMaxTileBytes =
2936 55 : sHeader.nTileWidth * sHeader.nTileHeight * sHeader.nBitDepth / 8;
2937 : size_t nCompressBufferSize =
2938 55 : 2 * nMaxTileBytes * poCompressData->asJobs.size();
2939 110 : poCompressData->pabyBuffers =
2940 55 : static_cast<GByte *>(VSIMalloc(nCompressBufferSize));
2941 :
2942 55 : CPLDebug("RMF", "Setup %d compressor threads and allocate %lu bytes buffer",
2943 : nThreads, static_cast<unsigned long>(nCompressBufferSize));
2944 55 : if (poCompressData->pabyBuffers == nullptr)
2945 : {
2946 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2947 : "Can't allocate compress buffer of size %lu.",
2948 : static_cast<unsigned long>(nCompressBufferSize));
2949 0 : return CE_Failure;
2950 : }
2951 :
2952 122 : for (size_t i = 0; i != poCompressData->asJobs.size(); ++i)
2953 : {
2954 67 : RMFCompressionJob &sJob(poCompressData->asJobs[i]);
2955 67 : sJob.pabyCompressedData =
2956 67 : poCompressData->pabyBuffers + 2 * i * nMaxTileBytes;
2957 67 : sJob.pabyUncompressedData = sJob.pabyCompressedData + nMaxTileBytes;
2958 67 : poCompressData->asReadyJobs.push_back(&sJob);
2959 : }
2960 :
2961 55 : if (nThreads > 0)
2962 : {
2963 3 : poCompressData->hReadyJobMutex = CPLCreateMutex();
2964 3 : CPLReleaseMutex(poCompressData->hReadyJobMutex);
2965 3 : poCompressData->hWriteTileMutex = CPLCreateMutex();
2966 3 : CPLReleaseMutex(poCompressData->hWriteTileMutex);
2967 : }
2968 :
2969 55 : return CE_None;
2970 : }
2971 :
2972 190 : CPLErr RMFDataset::WriteTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
2973 : size_t nBytes, GUInt32 nRawXSize,
2974 : GUInt32 nRawYSize)
2975 : {
2976 190 : RMFCompressionJob *poJob = nullptr;
2977 190 : if (poCompressData == nullptr)
2978 : {
2979 0 : CPLError(CE_Failure, CPLE_AppDefined, "RMF: Compress data is null");
2980 0 : return CE_Failure;
2981 : }
2982 :
2983 190 : if (poCompressData->oThreadPool.GetThreadCount() > 0)
2984 : {
2985 94 : size_t nJobs(poCompressData->asJobs.size());
2986 :
2987 94 : poCompressData->oThreadPool.WaitCompletion(static_cast<int>(nJobs - 1));
2988 :
2989 188 : CPLMutexHolder oHolder(poCompressData->hReadyJobMutex);
2990 94 : CPLAssert(!poCompressData->asReadyJobs.empty());
2991 94 : poJob = poCompressData->asReadyJobs.front();
2992 94 : poCompressData->asReadyJobs.pop_front();
2993 : }
2994 : else
2995 : {
2996 96 : poJob = poCompressData->asReadyJobs.front();
2997 : }
2998 :
2999 190 : if (poJob->eResult != CE_None)
3000 : {
3001 : // One of the previous jobs is not done.
3002 : // Detailed debug message is already emitted from WriteRawTile
3003 0 : return poJob->eResult;
3004 : }
3005 190 : poJob->poDS = this;
3006 190 : poJob->eResult = CE_Failure;
3007 190 : poJob->nBlockXOff = nBlockXOff;
3008 190 : poJob->nBlockYOff = nBlockYOff;
3009 190 : poJob->nUncompressedBytes = nBytes;
3010 190 : poJob->nXSize = nRawXSize;
3011 190 : poJob->nYSize = nRawYSize;
3012 :
3013 190 : memcpy(poJob->pabyUncompressedData, pabyData, nBytes);
3014 :
3015 190 : if (poCompressData->oThreadPool.GetThreadCount() > 0)
3016 : {
3017 94 : if (!poCompressData->oThreadPool.SubmitJob(WriteTileJobFunc, poJob))
3018 : {
3019 0 : CPLError(CE_Failure, CPLE_NotSupported,
3020 : "Can't submit job to thread pool.");
3021 0 : return CE_Failure;
3022 : }
3023 : }
3024 : else
3025 : {
3026 96 : WriteTileJobFunc(poJob);
3027 96 : if (poJob->eResult != CE_None)
3028 : {
3029 0 : return poJob->eResult;
3030 : }
3031 : }
3032 :
3033 190 : return CE_None;
3034 : }
3035 :
3036 190 : CPLErr RMFDataset::WriteRawTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
3037 : size_t nTileBytes)
3038 : {
3039 190 : CPLAssert(nBlockXOff >= 0 && nBlockYOff >= 0 && pabyData != nullptr &&
3040 : nTileBytes > 0);
3041 :
3042 190 : const GUInt32 nTile = nBlockYOff * nXTiles + nBlockXOff;
3043 :
3044 190 : vsi_l_offset nTileOffset = GetFileOffset(paiTiles[2 * nTile]);
3045 190 : size_t nTileSize = static_cast<size_t>(paiTiles[2 * nTile + 1]);
3046 :
3047 190 : if (nTileOffset && nTileSize <= nTileBytes)
3048 : {
3049 0 : if (VSIFSeekL(fp, nTileOffset, SEEK_SET) < 0)
3050 : {
3051 0 : CPLError(
3052 : CE_Failure, CPLE_FileIO,
3053 : "Can't seek to offset %ld in output file to write data.\n%s",
3054 0 : static_cast<long>(nTileOffset), VSIStrerror(errno));
3055 0 : return CE_Failure;
3056 : }
3057 : }
3058 : else
3059 : {
3060 190 : if (VSIFSeekL(fp, 0, SEEK_END) < 0)
3061 : {
3062 0 : CPLError(
3063 : CE_Failure, CPLE_FileIO,
3064 : "Can't seek to offset %ld in output file to write data.\n%s",
3065 0 : static_cast<long>(nTileOffset), VSIStrerror(errno));
3066 0 : return CE_Failure;
3067 : }
3068 190 : nTileOffset = VSIFTellL(fp);
3069 190 : vsi_l_offset nNewTileOffset = 0;
3070 190 : paiTiles[2 * nTile] = GetRMFOffset(nTileOffset, &nNewTileOffset);
3071 :
3072 190 : if (nTileOffset != nNewTileOffset)
3073 : {
3074 23 : if (VSIFSeekL(fp, nNewTileOffset, SEEK_SET) < 0)
3075 : {
3076 0 : CPLError(CE_Failure, CPLE_FileIO,
3077 : "Can't seek to offset %ld in output file to "
3078 : "write data.\n%s",
3079 0 : static_cast<long>(nNewTileOffset), VSIStrerror(errno));
3080 0 : return CE_Failure;
3081 : }
3082 : }
3083 190 : bHeaderDirty = true;
3084 : }
3085 :
3086 : #ifdef CPL_MSB
3087 : // Compressed tiles are already with proper byte order
3088 : if (eRMFType == RMFT_MTW && sHeader.iCompression == RMF_COMPRESSION_NONE)
3089 : {
3090 : // Byte swap can be done in place
3091 : if (sHeader.nBitDepth == 16)
3092 : {
3093 : for (size_t i = 0; i < nTileBytes; i += 2)
3094 : CPL_SWAP16PTR(pabyData + i);
3095 : }
3096 : else if (sHeader.nBitDepth == 32)
3097 : {
3098 : for (size_t i = 0; i < nTileBytes; i += 4)
3099 : CPL_SWAP32PTR(pabyData + i);
3100 : }
3101 : else if (sHeader.nBitDepth == 64)
3102 : {
3103 : for (size_t i = 0; i < nTileBytes; i += 8)
3104 : CPL_SWAPDOUBLE(pabyData + i);
3105 : }
3106 : }
3107 : #endif
3108 :
3109 190 : bool bOk = (VSIFWriteL(pabyData, 1, nTileBytes, fp) == nTileBytes);
3110 :
3111 190 : if (!bOk)
3112 : {
3113 0 : CPLError(CE_Failure, CPLE_FileIO,
3114 : "Can't write tile with X offset %d and Y offset %d.\n%s",
3115 0 : nBlockXOff, nBlockYOff, VSIStrerror(errno));
3116 0 : return CE_Failure;
3117 : }
3118 :
3119 190 : paiTiles[2 * nTile + 1] = static_cast<GUInt32>(nTileBytes);
3120 190 : bHeaderDirty = true;
3121 :
3122 190 : return CE_None;
3123 : }
3124 :
3125 352 : CPLErr RMFDataset::ReadTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
3126 : size_t nRawBytes, GUInt32 nRawXSize,
3127 : GUInt32 nRawYSize, bool &bNullTile)
3128 : {
3129 352 : bNullTile = false;
3130 :
3131 352 : const GUInt32 nTile = nBlockYOff * nXTiles + nBlockXOff;
3132 352 : if (2 * nTile + 1 >= sHeader.nTileTblSize / sizeof(GUInt32))
3133 : {
3134 0 : return CE_Failure;
3135 : }
3136 352 : vsi_l_offset nTileOffset = GetFileOffset(paiTiles[2 * nTile]);
3137 352 : GUInt32 nTileBytes = paiTiles[2 * nTile + 1];
3138 : // RMF doesn't store compressed tiles with size greater than 80% of
3139 : // uncompressed size. But just in case, select twice as many.
3140 352 : GUInt32 nMaxTileBytes =
3141 352 : 2 * sHeader.nTileWidth * sHeader.nTileHeight * sHeader.nBitDepth / 8;
3142 :
3143 352 : if (nTileBytes >= nMaxTileBytes)
3144 : {
3145 0 : CPLError(CE_Failure, CPLE_AppDefined,
3146 : "Invalid tile size %lu at offset %ld. Must be less than %lu",
3147 : static_cast<unsigned long>(nTileBytes),
3148 : static_cast<long>(nTileOffset),
3149 : static_cast<unsigned long>(nMaxTileBytes));
3150 0 : return CE_Failure;
3151 : }
3152 :
3153 352 : if (nTileOffset == 0)
3154 : {
3155 1 : bNullTile = true;
3156 1 : return CE_None;
3157 : }
3158 :
3159 : #ifdef DEBUG
3160 351 : CPLDebug("RMF", "Read RawSize [%d, %d], nTileBytes %d, nRawBytes %d",
3161 : nRawXSize, nRawYSize, static_cast<int>(nTileBytes),
3162 : static_cast<int>(nRawBytes));
3163 : #endif // DEBUG
3164 :
3165 351 : if (VSIFSeekL(fp, nTileOffset, SEEK_SET) < 0)
3166 : {
3167 : // XXX: We will not report error here, because file just may be
3168 : // in update state and data for this block will be available later
3169 0 : if (eAccess == GA_Update)
3170 0 : return CE_None;
3171 :
3172 0 : CPLError(CE_Failure, CPLE_FileIO,
3173 : "Can't seek to offset %ld in input file to read data.\n%s",
3174 0 : static_cast<long>(nTileOffset), VSIStrerror(errno));
3175 0 : return CE_Failure;
3176 : }
3177 :
3178 351 : if (Decompress == nullptr || nTileBytes == nRawBytes)
3179 : {
3180 166 : if (nTileBytes != nRawBytes)
3181 : {
3182 0 : CPLError(CE_Failure, CPLE_AppDefined,
3183 : "RMF: Invalid tile size %lu, expected %lu",
3184 : static_cast<unsigned long>(nTileBytes),
3185 : static_cast<unsigned long>(nRawBytes));
3186 0 : return CE_Failure;
3187 : }
3188 :
3189 166 : if (VSIFReadL(pabyData, 1, nRawBytes, fp) < nRawBytes)
3190 : {
3191 0 : CPLError(CE_Failure, CPLE_FileIO,
3192 : "RMF: Can't read at offset %lu from input file.\n%s",
3193 : static_cast<unsigned long>(nTileOffset),
3194 0 : VSIStrerror(errno));
3195 0 : return CE_Failure;
3196 : }
3197 :
3198 : #ifdef CPL_MSB
3199 : if (eRMFType == RMFT_MTW)
3200 : {
3201 : if (sHeader.nBitDepth == 16)
3202 : {
3203 : for (GUInt32 i = 0; i < nRawBytes; i += 2)
3204 : CPL_SWAP16PTR(pabyData + i);
3205 : }
3206 : else if (sHeader.nBitDepth == 32)
3207 : {
3208 : for (GUInt32 i = 0; i < nRawBytes; i += 4)
3209 : CPL_SWAP32PTR(pabyData + i);
3210 : }
3211 : else if (sHeader.nBitDepth == 64)
3212 : {
3213 : for (GUInt32 i = 0; i < nRawBytes; i += 8)
3214 : CPL_SWAPDOUBLE(pabyData + i);
3215 : }
3216 : }
3217 : #endif
3218 166 : return CE_None;
3219 : }
3220 :
3221 185 : if (pabyDecompressBuffer == nullptr)
3222 : {
3223 21 : pabyDecompressBuffer =
3224 21 : static_cast<GByte *>(VSIMalloc(std::max(1U, nMaxTileBytes)));
3225 21 : if (!pabyDecompressBuffer)
3226 : {
3227 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
3228 : "Can't allocate decompress buffer of size %lu.\n%s",
3229 : static_cast<unsigned long>(nMaxTileBytes),
3230 0 : VSIStrerror(errno));
3231 0 : return CE_Failure;
3232 : }
3233 : }
3234 :
3235 185 : if (VSIFReadL(pabyDecompressBuffer, 1, nTileBytes, fp) < nTileBytes)
3236 : {
3237 0 : CPLError(CE_Failure, CPLE_FileIO,
3238 : "RMF: Can't read at offset %lu from input file.\n%s",
3239 0 : static_cast<unsigned long>(nTileOffset), VSIStrerror(errno));
3240 0 : return CE_Failure;
3241 : }
3242 :
3243 : size_t nDecompressedSize =
3244 185 : Decompress(pabyDecompressBuffer, nTileBytes, pabyData,
3245 : static_cast<GUInt32>(nRawBytes), nRawXSize, nRawYSize);
3246 :
3247 185 : if (nDecompressedSize != static_cast<size_t>(nRawBytes))
3248 : {
3249 0 : CPLError(CE_Failure, CPLE_FileIO,
3250 : "Can't decompress tile xOff %d yOff %d. "
3251 : "Raw tile size is %lu but decompressed is %lu. "
3252 : "Compressed tile size is %lu",
3253 : nBlockXOff, nBlockYOff, static_cast<unsigned long>(nRawBytes),
3254 : static_cast<unsigned long>(nDecompressedSize),
3255 : static_cast<unsigned long>(nTileBytes));
3256 0 : return CE_Failure;
3257 : }
3258 : // We don't need to swap bytes here,
3259 : // because decompressed data is in proper byte order
3260 185 : return CE_None;
3261 : }
3262 :
3263 277 : void RMFDataset::SetupNBits()
3264 : {
3265 277 : int nBitDepth = 0;
3266 277 : if (sHeader.nBitDepth < 8 && nBands == 1)
3267 : {
3268 6 : nBitDepth = static_cast<int>(sHeader.nBitDepth);
3269 : }
3270 271 : else if (sHeader.nBitDepth == 16 && nBands == 3 && eRMFType == RMFT_RSW)
3271 : {
3272 0 : nBitDepth = 5;
3273 : }
3274 :
3275 277 : if (nBitDepth > 0)
3276 : {
3277 6 : char szNBits[32] = {};
3278 6 : snprintf(szNBits, sizeof(szNBits), "%d", nBitDepth);
3279 12 : for (int iBand = 1; iBand <= nBands; iBand++)
3280 : {
3281 6 : GetRasterBand(iBand)->SetMetadataItem("NBITS", szNBits,
3282 6 : "IMAGE_STRUCTURE");
3283 : }
3284 : }
3285 277 : }
3286 :
3287 : /************************************************************************/
3288 : /* GDALRegister_RMF() */
3289 : /************************************************************************/
3290 :
3291 1889 : void GDALRegister_RMF()
3292 :
3293 : {
3294 1889 : if (GDALGetDriverByName("RMF") != nullptr)
3295 282 : return;
3296 :
3297 1607 : GDALDriver *poDriver = new GDALDriver();
3298 :
3299 1607 : poDriver->SetDescription("RMF");
3300 1607 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
3301 1607 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Raster Matrix Format");
3302 1607 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/rmf.html");
3303 1607 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rsw");
3304 1607 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
3305 1607 : "Byte Int16 Int32 Float64");
3306 1607 : poDriver->SetMetadataItem(
3307 : GDAL_DMD_CREATIONOPTIONLIST,
3308 : "<CreationOptionList>"
3309 : " <Option name='MTW' type='boolean' description='Create MTW DEM "
3310 : "matrix'/>"
3311 : " <Option name='BLOCKXSIZE' type='int' description='Tile Width'/>"
3312 : " <Option name='BLOCKYSIZE' type='int' description='Tile Height'/>"
3313 : " <Option name='RMFHUGE' type='string-select' description='Creation "
3314 : "of huge RMF file (Supported by GIS Panorama since v11)'>"
3315 : " <Value>NO</Value>"
3316 : " <Value>YES</Value>"
3317 : " <Value>IF_SAFER</Value>"
3318 : " </Option>"
3319 : " <Option name='COMPRESS' type='string-select' default='NONE'>"
3320 : " <Value>NONE</Value>"
3321 : " <Value>LZW</Value>"
3322 : " <Value>JPEG</Value>"
3323 : " <Value>RMF_DEM</Value>"
3324 : " </Option>"
3325 : " <Option name='JPEG_QUALITY' type='int' description='JPEG quality "
3326 : "1-100' default='75'/>"
3327 : " <Option name='NUM_THREADS' type='string' description='Number of "
3328 : "worker threads for compression. Can be set to ALL_CPUS' default='1'/>"
3329 1607 : "</CreationOptionList>");
3330 1607 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
3331 :
3332 1607 : poDriver->pfnIdentify = RMFDataset::Identify;
3333 1607 : poDriver->pfnOpen = RMFDataset::Open;
3334 1607 : poDriver->pfnCreate = RMFDataset::Create;
3335 1607 : poDriver->SetMetadataItem(
3336 : GDAL_DMD_OPENOPTIONLIST,
3337 : "<OpenOptionList>"
3338 : " <Option name='RMF_SET_VERTCS' type='string' description='Layers "
3339 : "spatial reference will include vertical coordinate system description "
3340 : "if exist' default='NO'/>"
3341 1607 : "</OpenOptionList>");
3342 :
3343 1607 : GetGDALDriverManager()->RegisterDriver(poDriver);
3344 : }
3345 :
3346 : /************************************************************************/
3347 : /* RMFCompressData */
3348 : /************************************************************************/
3349 :
3350 55 : RMFCompressData::RMFCompressData() : pabyBuffers(nullptr)
3351 : {
3352 55 : }
3353 :
3354 55 : RMFCompressData::~RMFCompressData()
3355 : {
3356 55 : if (pabyBuffers != nullptr)
3357 : {
3358 55 : VSIFree(pabyBuffers);
3359 : }
3360 :
3361 55 : if (hWriteTileMutex != nullptr)
3362 : {
3363 3 : CPLDestroyMutex(hWriteTileMutex);
3364 : }
3365 :
3366 55 : if (hReadyJobMutex != nullptr)
3367 : {
3368 3 : CPLDestroyMutex(hReadyJobMutex);
3369 : }
3370 55 : }
3371 :
3372 : GDALSuggestedBlockAccessPattern
3373 21 : RMFRasterBand::GetSuggestedBlockAccessPattern() const
3374 : {
3375 21 : return GSBAP_RANDOM;
3376 : }
3377 :
3378 1507 : CPLErr RMFDataset::SetMetadataItem(const char *pszName, const char *pszValue,
3379 : const char *pszDomain)
3380 : {
3381 1507 : if (GetAccess() == GA_Update)
3382 : {
3383 190 : CPLDebug("RMF", "SetMetadataItem: %s=%s", pszName, pszValue);
3384 190 : if (EQUAL(pszName, MD_NAME_KEY))
3385 : {
3386 20 : memcpy(sHeader.byName, pszValue,
3387 : CPLStrnlen(pszValue, RMF_NAME_SIZE));
3388 20 : bHeaderDirty = true;
3389 : }
3390 170 : else if (EQUAL(pszName, MD_SCALE_KEY) && CPLStrnlen(pszValue, 10) > 4)
3391 : {
3392 20 : sHeader.dfScale = atof(pszValue + 4);
3393 20 : sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
3394 20 : bHeaderDirty = true;
3395 : }
3396 150 : else if (EQUAL(pszName, MD_FRAME_KEY))
3397 : {
3398 0 : bHeaderDirty = true;
3399 : }
3400 : }
3401 1507 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
3402 : }
3403 :
3404 39 : CPLErr RMFDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
3405 : {
3406 39 : if (GetAccess() == GA_Update)
3407 : {
3408 39 : auto pszName = CSLFetchNameValue(papszMetadata, MD_NAME_KEY);
3409 39 : if (pszName != nullptr)
3410 : {
3411 21 : memcpy(sHeader.byName, pszName, CPLStrnlen(pszName, RMF_NAME_SIZE));
3412 21 : bHeaderDirty = true;
3413 :
3414 21 : CPLDebug("RMF", "SetMetadata: %s", pszName);
3415 : }
3416 39 : auto pszScale = CSLFetchNameValue(papszMetadata, MD_SCALE_KEY);
3417 39 : if (pszScale != nullptr && CPLStrnlen(pszScale, 10) > 4)
3418 : {
3419 21 : sHeader.dfScale = atof(pszScale + 4);
3420 21 : sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
3421 21 : bHeaderDirty = true;
3422 :
3423 21 : CPLDebug("RMF", "SetMetadata: %s", pszScale);
3424 : }
3425 39 : auto pszFrame = CSLFetchNameValue(papszMetadata, MD_FRAME_KEY);
3426 39 : if (pszFrame != nullptr)
3427 : {
3428 2 : bHeaderDirty = true;
3429 :
3430 2 : CPLDebug("RMF", "SetMetadata: %s", pszFrame);
3431 : }
3432 : }
3433 39 : return GDALDataset::SetMetadata(papszMetadata, pszDomain);
3434 : }
|