Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Virtual GDAL Datasets
4 : * Purpose: Implementation of VRTRawRasterBand
5 : * Author: Frank Warmerdam <warmerdam@pobox.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "rawdataset.h"
32 : #include "vrtdataset.h"
33 :
34 : #include <cerrno>
35 : #include <cstdio>
36 : #include <cstdlib>
37 : #include <cstring>
38 :
39 : #include "cpl_conv.h"
40 : #include "cpl_error.h"
41 : #include "cpl_hash_set.h"
42 : #include "cpl_minixml.h"
43 : #include "cpl_string.h"
44 : #include "cpl_vsi.h"
45 : #include "gdal.h"
46 : #include "gdal_priv.h"
47 :
48 : /*! @cond Doxygen_Suppress */
49 :
50 : /************************************************************************/
51 : /* ==================================================================== */
52 : /* VRTRawRasterBand */
53 : /* ==================================================================== */
54 : /************************************************************************/
55 :
56 : /************************************************************************/
57 : /* VRTRawRasterBand() */
58 : /************************************************************************/
59 :
60 28 : VRTRawRasterBand::VRTRawRasterBand(GDALDataset *poDSIn, int nBandIn,
61 28 : GDALDataType eType)
62 : : m_poRawRaster(nullptr), m_pszSourceFilename(nullptr),
63 28 : m_bRelativeToVRT(FALSE)
64 : {
65 28 : Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
66 :
67 : // Declared in GDALRasterBand.
68 28 : poDS = poDSIn;
69 28 : nBand = nBandIn;
70 :
71 28 : if (eType != GDT_Unknown)
72 6 : eDataType = eType;
73 28 : }
74 :
75 : /************************************************************************/
76 : /* ~VRTRawRasterBand() */
77 : /************************************************************************/
78 :
79 56 : VRTRawRasterBand::~VRTRawRasterBand()
80 :
81 : {
82 28 : FlushCache(true);
83 28 : ClearRawLink();
84 56 : }
85 :
86 : /************************************************************************/
87 : /* IRasterIO() */
88 : /************************************************************************/
89 :
90 256 : CPLErr VRTRawRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
91 : int nXSize, int nYSize, void *pData,
92 : int nBufXSize, int nBufYSize,
93 : GDALDataType eBufType, GSpacing nPixelSpace,
94 : GSpacing nLineSpace,
95 : GDALRasterIOExtraArg *psExtraArg)
96 : {
97 256 : if (m_poRawRaster == nullptr)
98 : {
99 0 : CPLError(CE_Failure, CPLE_AppDefined,
100 : "No raw raster band configured on VRTRawRasterBand.");
101 0 : return CE_Failure;
102 : }
103 :
104 256 : if (eRWFlag == GF_Write && eAccess == GA_ReadOnly)
105 : {
106 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
107 : "Attempt to write to read only dataset in"
108 : "VRTRawRasterBand::IRasterIO().");
109 :
110 0 : return CE_Failure;
111 : }
112 :
113 : /* -------------------------------------------------------------------- */
114 : /* Do we have overviews that would be appropriate to satisfy */
115 : /* this request? */
116 : /* -------------------------------------------------------------------- */
117 256 : if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
118 : {
119 0 : if (OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
120 : nBufXSize, nBufYSize, eBufType, nPixelSpace,
121 0 : nLineSpace, psExtraArg) == CE_None)
122 0 : return CE_None;
123 : }
124 :
125 256 : m_poRawRaster->SetAccess(eAccess);
126 :
127 256 : return m_poRawRaster->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
128 : nBufXSize, nBufYSize, eBufType, nPixelSpace,
129 256 : nLineSpace, psExtraArg);
130 : }
131 :
132 : /************************************************************************/
133 : /* IReadBlock() */
134 : /************************************************************************/
135 :
136 70 : CPLErr VRTRawRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
137 : void *pImage)
138 :
139 : {
140 70 : if (m_poRawRaster == nullptr)
141 : {
142 0 : CPLError(CE_Failure, CPLE_AppDefined,
143 : "No raw raster band configured on VRTRawRasterBand.");
144 0 : return CE_Failure;
145 : }
146 :
147 70 : return m_poRawRaster->ReadBlock(nBlockXOff, nBlockYOff, pImage);
148 : }
149 :
150 : /************************************************************************/
151 : /* IWriteBlock() */
152 : /************************************************************************/
153 :
154 0 : CPLErr VRTRawRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
155 : void *pImage)
156 :
157 : {
158 0 : if (m_poRawRaster == nullptr)
159 : {
160 0 : CPLError(CE_Failure, CPLE_AppDefined,
161 : "No raw raster band configured on VRTRawRasterBand.");
162 0 : return CE_Failure;
163 : }
164 :
165 0 : m_poRawRaster->SetAccess(eAccess);
166 :
167 0 : return m_poRawRaster->WriteBlock(nBlockXOff, nBlockYOff, pImage);
168 : }
169 :
170 : /************************************************************************/
171 : /* SetRawLink() */
172 : /************************************************************************/
173 :
174 28 : CPLErr VRTRawRasterBand::SetRawLink(const char *pszFilename,
175 : const char *pszVRTPath,
176 : int bRelativeToVRTIn,
177 : vsi_l_offset nImageOffset, int nPixelOffset,
178 : int nLineOffset, const char *pszByteOrder)
179 :
180 : {
181 28 : ClearRawLink();
182 :
183 28 : static_cast<VRTDataset *>(poDS)->SetNeedsFlush();
184 :
185 : /* -------------------------------------------------------------------- */
186 : /* Prepare filename. */
187 : /* -------------------------------------------------------------------- */
188 28 : if (pszFilename == nullptr)
189 : {
190 0 : CPLError(CE_Warning, CPLE_AppDefined,
191 : "Missing <SourceFilename> element in VRTRasterBand.");
192 0 : return CE_Failure;
193 : }
194 :
195 28 : char *pszExpandedFilename = nullptr;
196 28 : if (pszVRTPath != nullptr && bRelativeToVRTIn)
197 : {
198 16 : pszExpandedFilename =
199 16 : CPLStrdup(CPLProjectRelativeFilename(pszVRTPath, pszFilename));
200 : }
201 : else
202 : {
203 12 : pszExpandedFilename = CPLStrdup(pszFilename);
204 : }
205 :
206 : /* -------------------------------------------------------------------- */
207 : /* Try and open the file. We always use the large file API. */
208 : /* -------------------------------------------------------------------- */
209 28 : CPLPushErrorHandler(CPLQuietErrorHandler);
210 28 : FILE *fp = CPLOpenShared(pszExpandedFilename, "rb+", TRUE);
211 :
212 28 : if (fp == nullptr)
213 4 : fp = CPLOpenShared(pszExpandedFilename, "rb", TRUE);
214 :
215 32 : if (fp == nullptr &&
216 4 : static_cast<VRTDataset *>(poDS)->GetAccess() == GA_Update)
217 : {
218 3 : fp = CPLOpenShared(pszExpandedFilename, "wb+", TRUE);
219 : }
220 28 : CPLPopErrorHandler();
221 28 : CPLErrorReset();
222 :
223 28 : if (fp == nullptr)
224 : {
225 1 : CPLError(CE_Failure, CPLE_OpenFailed, "Unable to open %s.%s",
226 1 : pszExpandedFilename, VSIStrerror(errno));
227 :
228 1 : CPLFree(pszExpandedFilename);
229 1 : return CE_Failure;
230 : }
231 :
232 27 : CPLFree(pszExpandedFilename);
233 :
234 27 : if (!RAWDatasetCheckMemoryUsage(
235 : nRasterXSize, nRasterYSize, 1,
236 : GDALGetDataTypeSizeBytes(GetRasterDataType()), nPixelOffset,
237 : nLineOffset, nImageOffset, 0, reinterpret_cast<VSILFILE *>(fp)))
238 : {
239 2 : CPLCloseShared(fp);
240 2 : return CE_Failure;
241 : }
242 :
243 25 : m_pszSourceFilename = CPLStrdup(pszFilename);
244 25 : m_bRelativeToVRT = bRelativeToVRTIn;
245 :
246 : /* -------------------------------------------------------------------- */
247 : /* Work out if we are in native mode or not. */
248 : /* -------------------------------------------------------------------- */
249 25 : RawRasterBand::ByteOrder eByteOrder =
250 : #if CPL_IS_LSB
251 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
252 : #else
253 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
254 : #endif
255 :
256 25 : if (pszByteOrder != nullptr)
257 : {
258 23 : if (EQUAL(pszByteOrder, "LSB"))
259 14 : eByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
260 9 : else if (EQUAL(pszByteOrder, "MSB"))
261 7 : eByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
262 2 : else if (EQUAL(pszByteOrder, "VAX"))
263 1 : eByteOrder = RawRasterBand::ByteOrder::ORDER_VAX;
264 : else
265 : {
266 1 : CPLError(CE_Failure, CPLE_AppDefined,
267 : "Illegal ByteOrder value '%s', should be LSB, MSB or VAX.",
268 : pszByteOrder);
269 1 : CPLCloseShared(fp);
270 1 : return CE_Failure;
271 : }
272 : }
273 :
274 : /* -------------------------------------------------------------------- */
275 : /* Create a corresponding RawRasterBand. */
276 : /* -------------------------------------------------------------------- */
277 24 : m_poRawRaster =
278 48 : RawRasterBand::Create(reinterpret_cast<VSILFILE *>(fp), nImageOffset,
279 : nPixelOffset, nLineOffset, GetRasterDataType(),
280 : eByteOrder, GetXSize(), GetYSize(),
281 : RawRasterBand::OwnFP::NO)
282 24 : .release();
283 24 : if (!m_poRawRaster)
284 : {
285 0 : CPLCloseShared(fp);
286 0 : return CE_Failure;
287 : }
288 :
289 : /* -------------------------------------------------------------------- */
290 : /* Reset block size to match the raw raster. */
291 : /* -------------------------------------------------------------------- */
292 24 : m_poRawRaster->GetBlockSize(&nBlockXSize, &nBlockYSize);
293 :
294 24 : return CE_None;
295 : }
296 :
297 : /************************************************************************/
298 : /* ClearRawLink() */
299 : /************************************************************************/
300 :
301 56 : void VRTRawRasterBand::ClearRawLink()
302 :
303 : {
304 56 : if (m_poRawRaster != nullptr)
305 : {
306 24 : VSILFILE *fp = m_poRawRaster->GetFPL();
307 24 : delete m_poRawRaster;
308 24 : m_poRawRaster = nullptr;
309 : // We close the file after deleting the raster band
310 : // since data can be flushed in the destructor.
311 24 : if (fp != nullptr)
312 : {
313 24 : CPLCloseShared(reinterpret_cast<FILE *>(fp));
314 : }
315 : }
316 56 : CPLFree(m_pszSourceFilename);
317 56 : m_pszSourceFilename = nullptr;
318 56 : }
319 :
320 : /************************************************************************/
321 : /* GetVirtualMemAuto() */
322 : /************************************************************************/
323 :
324 0 : CPLVirtualMem *VRTRawRasterBand::GetVirtualMemAuto(GDALRWFlag eRWFlag,
325 : int *pnPixelSpace,
326 : GIntBig *pnLineSpace,
327 : char **papszOptions)
328 :
329 : {
330 : // check the pointer to RawRasterBand
331 0 : if (m_poRawRaster == nullptr)
332 : {
333 : // use the super class method
334 0 : return VRTRasterBand::GetVirtualMemAuto(eRWFlag, pnPixelSpace,
335 0 : pnLineSpace, papszOptions);
336 : }
337 : // if available, use the RawRasterBand method (use mmap if available)
338 0 : return m_poRawRaster->GetVirtualMemAuto(eRWFlag, pnPixelSpace, pnLineSpace,
339 0 : papszOptions);
340 : }
341 :
342 : /************************************************************************/
343 : /* XMLInit() */
344 : /************************************************************************/
345 :
346 : CPLErr
347 22 : VRTRawRasterBand::XMLInit(const CPLXMLNode *psTree, const char *pszVRTPath,
348 : std::map<CPLString, GDALDataset *> &oMapSharedSources)
349 :
350 : {
351 : const CPLErr eErr =
352 22 : VRTRasterBand::XMLInit(psTree, pszVRTPath, oMapSharedSources);
353 22 : if (eErr != CE_None)
354 0 : return eErr;
355 :
356 : /* -------------------------------------------------------------------- */
357 : /* Validate a bit. */
358 : /* -------------------------------------------------------------------- */
359 22 : if (psTree == nullptr || psTree->eType != CXT_Element ||
360 66 : !EQUAL(psTree->pszValue, "VRTRasterBand") ||
361 22 : !EQUAL(CPLGetXMLValue(psTree, "subClass", ""), "VRTRawRasterBand"))
362 : {
363 0 : CPLError(CE_Failure, CPLE_AppDefined,
364 : "Invalid node passed to VRTRawRasterBand::XMLInit().");
365 0 : return CE_Failure;
366 : }
367 :
368 : /* -------------------------------------------------------------------- */
369 : /* Prepare filename. */
370 : /* -------------------------------------------------------------------- */
371 22 : const char *pszFilename = CPLGetXMLValue(psTree, "SourceFilename", nullptr);
372 :
373 22 : if (pszFilename == nullptr)
374 : {
375 0 : CPLError(CE_Warning, CPLE_AppDefined,
376 : "Missing <SourceFilename> element in VRTRasterBand.");
377 0 : return CE_Failure;
378 : }
379 :
380 22 : const bool l_bRelativeToVRT = CPLTestBool(
381 : CPLGetXMLValue(psTree, "SourceFilename.relativeToVRT", "1"));
382 :
383 : /* -------------------------------------------------------------------- */
384 : /* Collect layout information. */
385 : /* -------------------------------------------------------------------- */
386 22 : int nWordDataSize = GDALGetDataTypeSizeBytes(GetRasterDataType());
387 :
388 22 : const char *pszImageOffset = CPLGetXMLValue(psTree, "ImageOffset", "0");
389 44 : const vsi_l_offset nImageOffset = CPLScanUIntBig(
390 22 : pszImageOffset, static_cast<int>(strlen(pszImageOffset)));
391 :
392 22 : int nPixelOffset = nWordDataSize;
393 22 : const char *pszPixelOffset = CPLGetXMLValue(psTree, "PixelOffset", nullptr);
394 22 : if (pszPixelOffset != nullptr)
395 : {
396 20 : nPixelOffset = atoi(pszPixelOffset);
397 : }
398 22 : if (nPixelOffset <= 0)
399 : {
400 0 : CPLError(CE_Failure, CPLE_AppDefined,
401 : "Invalid value for <PixelOffset> element : %d", nPixelOffset);
402 0 : return CE_Failure;
403 : }
404 :
405 22 : int nLineOffset = 0;
406 22 : const char *pszLineOffset = CPLGetXMLValue(psTree, "LineOffset", nullptr);
407 22 : if (pszLineOffset == nullptr)
408 : {
409 2 : if (nPixelOffset > INT_MAX / GetXSize())
410 : {
411 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow");
412 0 : return CE_Failure;
413 : }
414 2 : nLineOffset = nPixelOffset * GetXSize();
415 : }
416 : else
417 20 : nLineOffset = atoi(pszLineOffset);
418 :
419 22 : const char *pszByteOrder = CPLGetXMLValue(psTree, "ByteOrder", nullptr);
420 :
421 : /* -------------------------------------------------------------------- */
422 : /* Open the file, and setup the raw layer access to the data. */
423 : /* -------------------------------------------------------------------- */
424 22 : return SetRawLink(pszFilename, pszVRTPath, l_bRelativeToVRT, nImageOffset,
425 22 : nPixelOffset, nLineOffset, pszByteOrder);
426 : }
427 :
428 : /************************************************************************/
429 : /* SerializeToXML() */
430 : /************************************************************************/
431 :
432 7 : CPLXMLNode *VRTRawRasterBand::SerializeToXML(const char *pszVRTPath,
433 : bool &bHasWarnedAboutRAMUsage,
434 : size_t &nAccRAMUsage)
435 :
436 : {
437 :
438 : /* -------------------------------------------------------------------- */
439 : /* We can't set the layout if there is no open rawband. */
440 : /* -------------------------------------------------------------------- */
441 7 : if (m_poRawRaster == nullptr)
442 : {
443 0 : CPLError(CE_Failure, CPLE_AppDefined,
444 : "VRTRawRasterBand::SerializeToXML() fails because "
445 : "m_poRawRaster is NULL.");
446 0 : return nullptr;
447 : }
448 :
449 7 : CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
450 : pszVRTPath, bHasWarnedAboutRAMUsage, nAccRAMUsage);
451 :
452 : /* -------------------------------------------------------------------- */
453 : /* Set subclass. */
454 : /* -------------------------------------------------------------------- */
455 7 : CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
456 : CXT_Text, "VRTRawRasterBand");
457 :
458 : /* -------------------------------------------------------------------- */
459 : /* Setup the filename with relative flag. */
460 : /* -------------------------------------------------------------------- */
461 14 : CPLXMLNode *psNode = CPLCreateXMLElementAndValue(psTree, "SourceFilename",
462 7 : m_pszSourceFilename);
463 :
464 7 : CPLCreateXMLNode(CPLCreateXMLNode(psNode, CXT_Attribute, "relativeToVRT"),
465 7 : CXT_Text, m_bRelativeToVRT ? "1" : "0");
466 :
467 : /* -------------------------------------------------------------------- */
468 : /* Set other layout information. */
469 : /* -------------------------------------------------------------------- */
470 :
471 7 : CPLCreateXMLElementAndValue(
472 : psTree, "ImageOffset",
473 7 : CPLSPrintf(CPL_FRMT_GUIB, m_poRawRaster->GetImgOffset()));
474 :
475 7 : CPLCreateXMLElementAndValue(
476 : psTree, "PixelOffset",
477 7 : CPLSPrintf("%d", m_poRawRaster->GetPixelOffset()));
478 :
479 7 : CPLCreateXMLElementAndValue(
480 7 : psTree, "LineOffset", CPLSPrintf("%d", m_poRawRaster->GetLineOffset()));
481 :
482 7 : switch (m_poRawRaster->GetByteOrder())
483 : {
484 3 : case RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN:
485 3 : CPLCreateXMLElementAndValue(psTree, "ByteOrder", "LSB");
486 3 : break;
487 4 : case RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN:
488 4 : CPLCreateXMLElementAndValue(psTree, "ByteOrder", "MSB");
489 4 : break;
490 0 : case RawRasterBand::ByteOrder::ORDER_VAX:
491 0 : CPLCreateXMLElementAndValue(psTree, "ByteOrder", "VAX");
492 0 : break;
493 : }
494 :
495 7 : return psTree;
496 : }
497 :
498 : /************************************************************************/
499 : /* GetFileList() */
500 : /************************************************************************/
501 :
502 3 : void VRTRawRasterBand::GetFileList(char ***ppapszFileList, int *pnSize,
503 : int *pnMaxSize, CPLHashSet *hSetFiles)
504 : {
505 3 : if (m_pszSourceFilename == nullptr)
506 1 : return;
507 :
508 : /* -------------------------------------------------------------------- */
509 : /* Is it already in the list ? */
510 : /* -------------------------------------------------------------------- */
511 3 : CPLString osSourceFilename;
512 3 : if (m_bRelativeToVRT && strlen(poDS->GetDescription()) > 0)
513 : osSourceFilename =
514 2 : CPLFormFilename(CPLGetDirname(poDS->GetDescription()),
515 2 : m_pszSourceFilename, nullptr);
516 : else
517 1 : osSourceFilename = m_pszSourceFilename;
518 :
519 3 : if (CPLHashSetLookup(hSetFiles, osSourceFilename) != nullptr)
520 1 : return;
521 :
522 : /* -------------------------------------------------------------------- */
523 : /* Grow array if necessary */
524 : /* -------------------------------------------------------------------- */
525 2 : if (*pnSize + 1 >= *pnMaxSize)
526 : {
527 2 : *pnMaxSize = 2 + 2 * (*pnMaxSize);
528 2 : *ppapszFileList = static_cast<char **>(
529 2 : CPLRealloc(*ppapszFileList, sizeof(char *) * (*pnMaxSize)));
530 : }
531 :
532 : /* -------------------------------------------------------------------- */
533 : /* Add the string to the list */
534 : /* -------------------------------------------------------------------- */
535 2 : (*ppapszFileList)[*pnSize] = CPLStrdup(osSourceFilename);
536 2 : (*ppapszFileList)[(*pnSize + 1)] = nullptr;
537 2 : CPLHashSetInsert(hSetFiles, (*ppapszFileList)[*pnSize]);
538 :
539 2 : (*pnSize)++;
540 :
541 2 : VRTRasterBand::GetFileList(ppapszFileList, pnSize, pnMaxSize, hSetFiles);
542 : }
543 :
544 : /*! @endcond */
|