/src/gdal/frmts/pds/pds4dataset.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: PDS 4 Driver; Planetary Data System Format |
4 | | * Purpose: Implementation of PDS4Dataset |
5 | | * Author: Even Rouault, even.rouault at spatialys.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2017, Hobu Inc |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "cpl_vsi_error.h" |
14 | | #include "gdal_proxy.h" |
15 | | #include "rawdataset.h" |
16 | | #include "vrtdataset.h" |
17 | | #include "ogrsf_frmts.h" |
18 | | #include "ogr_spatialref.h" |
19 | | #include "gdal_priv_templates.hpp" |
20 | | #include "ogreditablelayer.h" |
21 | | #include "pds4dataset.h" |
22 | | #include "pdsdrivercore.h" |
23 | | |
24 | | #ifdef EMBED_RESOURCE_FILES |
25 | | #include "embedded_resources.h" |
26 | | #endif |
27 | | |
28 | | #include <cstdlib> |
29 | | #include <vector> |
30 | | #include <algorithm> |
31 | | |
32 | 0 | #define TIFF_GEOTIFF_STRING "TIFF 6.0" |
33 | 0 | #define BIGTIFF_GEOTIFF_STRING "TIFF 6.0" |
34 | | #define PREEXISTING_BINARY_FILE \ |
35 | 0 | "Binary file pre-existing PDS4 label. This comment is used by GDAL to " \ |
36 | 0 | "avoid deleting the binary file when the label is deleted. Keep it to " \ |
37 | 0 | "preserve this behavior." |
38 | | |
39 | 0 | #define CURRENT_CART_VERSION "1G00_1950" |
40 | | |
41 | | extern "C" void GDALRegister_PDS4(); |
42 | | |
43 | | /************************************************************************/ |
44 | | /* PDS4WrapperRasterBand() */ |
45 | | /************************************************************************/ |
46 | | |
47 | | PDS4WrapperRasterBand::PDS4WrapperRasterBand(GDALRasterBand *poBaseBandIn) |
48 | 0 | : m_poBaseBand(poBaseBandIn), m_bHasOffset(false), m_bHasScale(false), |
49 | 0 | m_bHasNoData(false), m_dfOffset(0.0), m_dfScale(1.0), m_dfNoData(0.0) |
50 | 0 | { |
51 | 0 | eDataType = m_poBaseBand->GetRasterDataType(); |
52 | 0 | m_poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize); |
53 | 0 | } |
54 | | |
55 | | /************************************************************************/ |
56 | | /* SetMaskBand() */ |
57 | | /************************************************************************/ |
58 | | |
59 | | void PDS4WrapperRasterBand::SetMaskBand(GDALRasterBand *poMaskBand) |
60 | 0 | { |
61 | 0 | poMask.reset(poMaskBand, true); |
62 | 0 | nMaskFlags = 0; |
63 | 0 | } |
64 | | |
65 | | /************************************************************************/ |
66 | | /* GetOffset() */ |
67 | | /************************************************************************/ |
68 | | |
69 | | double PDS4WrapperRasterBand::GetOffset(int *pbSuccess) |
70 | 0 | { |
71 | 0 | if (pbSuccess) |
72 | 0 | *pbSuccess = m_bHasOffset; |
73 | 0 | return m_dfOffset; |
74 | 0 | } |
75 | | |
76 | | /************************************************************************/ |
77 | | /* GetScale() */ |
78 | | /************************************************************************/ |
79 | | |
80 | | double PDS4WrapperRasterBand::GetScale(int *pbSuccess) |
81 | 0 | { |
82 | 0 | if (pbSuccess) |
83 | 0 | *pbSuccess = m_bHasScale; |
84 | 0 | return m_dfScale; |
85 | 0 | } |
86 | | |
87 | | /************************************************************************/ |
88 | | /* SetOffset() */ |
89 | | /************************************************************************/ |
90 | | |
91 | | CPLErr PDS4WrapperRasterBand::SetOffset(double dfNewOffset) |
92 | 0 | { |
93 | 0 | m_dfOffset = dfNewOffset; |
94 | 0 | m_bHasOffset = true; |
95 | |
|
96 | 0 | PDS4Dataset *poGDS = reinterpret_cast<PDS4Dataset *>(poDS); |
97 | 0 | if (poGDS->m_poExternalDS && eAccess == GA_Update) |
98 | 0 | poGDS->m_poExternalDS->GetRasterBand(nBand)->SetOffset(dfNewOffset); |
99 | |
|
100 | 0 | return CE_None; |
101 | 0 | } |
102 | | |
103 | | /************************************************************************/ |
104 | | /* SetScale() */ |
105 | | /************************************************************************/ |
106 | | |
107 | | CPLErr PDS4WrapperRasterBand::SetScale(double dfNewScale) |
108 | 0 | { |
109 | 0 | m_dfScale = dfNewScale; |
110 | 0 | m_bHasScale = true; |
111 | |
|
112 | 0 | PDS4Dataset *poGDS = reinterpret_cast<PDS4Dataset *>(poDS); |
113 | 0 | if (poGDS->m_poExternalDS && eAccess == GA_Update) |
114 | 0 | poGDS->m_poExternalDS->GetRasterBand(nBand)->SetScale(dfNewScale); |
115 | |
|
116 | 0 | return CE_None; |
117 | 0 | } |
118 | | |
119 | | /************************************************************************/ |
120 | | /* GetNoDataValue() */ |
121 | | /************************************************************************/ |
122 | | |
123 | | double PDS4WrapperRasterBand::GetNoDataValue(int *pbSuccess) |
124 | 0 | { |
125 | 0 | if (pbSuccess) |
126 | 0 | *pbSuccess = m_bHasNoData; |
127 | 0 | return m_dfNoData; |
128 | 0 | } |
129 | | |
130 | | /************************************************************************/ |
131 | | /* SetNoDataValue() */ |
132 | | /************************************************************************/ |
133 | | |
134 | | CPLErr PDS4WrapperRasterBand::SetNoDataValue(double dfNewNoData) |
135 | 0 | { |
136 | 0 | m_dfNoData = dfNewNoData; |
137 | 0 | m_bHasNoData = true; |
138 | |
|
139 | 0 | PDS4Dataset *poGDS = reinterpret_cast<PDS4Dataset *>(poDS); |
140 | 0 | if (poGDS->m_poExternalDS && eAccess == GA_Update) |
141 | 0 | poGDS->m_poExternalDS->GetRasterBand(nBand)->SetNoDataValue( |
142 | 0 | dfNewNoData); |
143 | |
|
144 | 0 | return CE_None; |
145 | 0 | } |
146 | | |
147 | | /************************************************************************/ |
148 | | /* Fill() */ |
149 | | /************************************************************************/ |
150 | | |
151 | | CPLErr PDS4WrapperRasterBand::Fill(double dfRealValue, double dfImaginaryValue) |
152 | 0 | { |
153 | 0 | PDS4Dataset *poGDS = reinterpret_cast<PDS4Dataset *>(poDS); |
154 | 0 | if (poGDS->m_bMustInitImageFile) |
155 | 0 | { |
156 | 0 | if (!poGDS->InitImageFile()) |
157 | 0 | return CE_Failure; |
158 | 0 | } |
159 | 0 | return GDALProxyRasterBand::Fill(dfRealValue, dfImaginaryValue); |
160 | 0 | } |
161 | | |
162 | | /************************************************************************/ |
163 | | /* IWriteBlock() */ |
164 | | /************************************************************************/ |
165 | | |
166 | | CPLErr PDS4WrapperRasterBand::IWriteBlock(int nXBlock, int nYBlock, |
167 | | void *pImage) |
168 | | |
169 | 0 | { |
170 | 0 | PDS4Dataset *poGDS = reinterpret_cast<PDS4Dataset *>(poDS); |
171 | 0 | if (poGDS->m_bMustInitImageFile) |
172 | 0 | { |
173 | 0 | if (!poGDS->InitImageFile()) |
174 | 0 | return CE_Failure; |
175 | 0 | } |
176 | 0 | return GDALProxyRasterBand::IWriteBlock(nXBlock, nYBlock, pImage); |
177 | 0 | } |
178 | | |
179 | | /************************************************************************/ |
180 | | /* IRasterIO() */ |
181 | | /************************************************************************/ |
182 | | |
183 | | CPLErr PDS4WrapperRasterBand::IRasterIO( |
184 | | GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, |
185 | | void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, |
186 | | GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg) |
187 | | |
188 | 0 | { |
189 | 0 | PDS4Dataset *poGDS = reinterpret_cast<PDS4Dataset *>(poDS); |
190 | 0 | if (eRWFlag == GF_Write && poGDS->m_bMustInitImageFile) |
191 | 0 | { |
192 | 0 | if (!poGDS->InitImageFile()) |
193 | 0 | return CE_Failure; |
194 | 0 | } |
195 | 0 | return GDALProxyRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, |
196 | 0 | pData, nBufXSize, nBufYSize, eBufType, |
197 | 0 | nPixelSpace, nLineSpace, psExtraArg); |
198 | 0 | } |
199 | | |
200 | | /************************************************************************/ |
201 | | /* PDS4RawRasterBand() */ |
202 | | /************************************************************************/ |
203 | | |
204 | | PDS4RawRasterBand::PDS4RawRasterBand(GDALDataset *l_poDS, int l_nBand, |
205 | | VSILFILE *l_fpRaw, |
206 | | vsi_l_offset l_nImgOffset, |
207 | | int l_nPixelOffset, int l_nLineOffset, |
208 | | GDALDataType l_eDataType, |
209 | | RawRasterBand::ByteOrder eByteOrderIn) |
210 | 879 | : RawRasterBand(l_poDS, l_nBand, l_fpRaw, l_nImgOffset, l_nPixelOffset, |
211 | 879 | l_nLineOffset, l_eDataType, eByteOrderIn, |
212 | 879 | RawRasterBand::OwnFP::NO), |
213 | 879 | m_bHasOffset(false), m_bHasScale(false), m_bHasNoData(false), |
214 | 879 | m_dfOffset(0.0), m_dfScale(1.0), m_dfNoData(0.0) |
215 | 879 | { |
216 | 879 | } |
217 | | |
218 | | /************************************************************************/ |
219 | | /* SetMaskBand() */ |
220 | | /************************************************************************/ |
221 | | |
222 | | void PDS4RawRasterBand::SetMaskBand(GDALRasterBand *poMaskBand) |
223 | 0 | { |
224 | 0 | poMask.reset(poMaskBand, true); |
225 | 0 | nMaskFlags = 0; |
226 | 0 | } |
227 | | |
228 | | /************************************************************************/ |
229 | | /* GetOffset() */ |
230 | | /************************************************************************/ |
231 | | |
232 | | double PDS4RawRasterBand::GetOffset(int *pbSuccess) |
233 | 0 | { |
234 | 0 | if (pbSuccess) |
235 | 0 | *pbSuccess = m_bHasOffset; |
236 | 0 | return m_dfOffset; |
237 | 0 | } |
238 | | |
239 | | /************************************************************************/ |
240 | | /* GetScale() */ |
241 | | /************************************************************************/ |
242 | | |
243 | | double PDS4RawRasterBand::GetScale(int *pbSuccess) |
244 | 0 | { |
245 | 0 | if (pbSuccess) |
246 | 0 | *pbSuccess = m_bHasScale; |
247 | 0 | return m_dfScale; |
248 | 0 | } |
249 | | |
250 | | /************************************************************************/ |
251 | | /* SetOffset() */ |
252 | | /************************************************************************/ |
253 | | |
254 | | CPLErr PDS4RawRasterBand::SetOffset(double dfNewOffset) |
255 | 0 | { |
256 | 0 | m_dfOffset = dfNewOffset; |
257 | 0 | m_bHasOffset = true; |
258 | 0 | return CE_None; |
259 | 0 | } |
260 | | |
261 | | /************************************************************************/ |
262 | | /* SetScale() */ |
263 | | /************************************************************************/ |
264 | | |
265 | | CPLErr PDS4RawRasterBand::SetScale(double dfNewScale) |
266 | 0 | { |
267 | 0 | m_dfScale = dfNewScale; |
268 | 0 | m_bHasScale = true; |
269 | 0 | return CE_None; |
270 | 0 | } |
271 | | |
272 | | /************************************************************************/ |
273 | | /* GetNoDataValue() */ |
274 | | /************************************************************************/ |
275 | | |
276 | | double PDS4RawRasterBand::GetNoDataValue(int *pbSuccess) |
277 | 0 | { |
278 | 0 | if (pbSuccess) |
279 | 0 | *pbSuccess = m_bHasNoData; |
280 | 0 | return m_dfNoData; |
281 | 0 | } |
282 | | |
283 | | /************************************************************************/ |
284 | | /* SetNoDataValue() */ |
285 | | /************************************************************************/ |
286 | | |
287 | | CPLErr PDS4RawRasterBand::SetNoDataValue(double dfNewNoData) |
288 | 365 | { |
289 | 365 | m_dfNoData = dfNewNoData; |
290 | 365 | m_bHasNoData = true; |
291 | 365 | return CE_None; |
292 | 365 | } |
293 | | |
294 | | /************************************************************************/ |
295 | | /* IReadBlock() */ |
296 | | /************************************************************************/ |
297 | | |
298 | | CPLErr PDS4RawRasterBand::IWriteBlock(int nXBlock, int nYBlock, void *pImage) |
299 | | |
300 | 233 | { |
301 | 233 | PDS4Dataset *poGDS = reinterpret_cast<PDS4Dataset *>(poDS); |
302 | 233 | if (poGDS->m_bMustInitImageFile) |
303 | 0 | { |
304 | 0 | if (!poGDS->InitImageFile()) |
305 | 0 | return CE_Failure; |
306 | 0 | } |
307 | | |
308 | 233 | return RawRasterBand::IWriteBlock(nXBlock, nYBlock, pImage); |
309 | 233 | } |
310 | | |
311 | | /************************************************************************/ |
312 | | /* IRasterIO() */ |
313 | | /************************************************************************/ |
314 | | |
315 | | CPLErr PDS4RawRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, |
316 | | int nXSize, int nYSize, void *pData, |
317 | | int nBufXSize, int nBufYSize, |
318 | | GDALDataType eBufType, GSpacing nPixelSpace, |
319 | | GSpacing nLineSpace, |
320 | | GDALRasterIOExtraArg *psExtraArg) |
321 | | |
322 | 912 | { |
323 | 912 | PDS4Dataset *poGDS = reinterpret_cast<PDS4Dataset *>(poDS); |
324 | 912 | if (eRWFlag == GF_Write && poGDS->m_bMustInitImageFile) |
325 | 0 | { |
326 | 0 | if (!poGDS->InitImageFile()) |
327 | 0 | return CE_Failure; |
328 | 0 | } |
329 | | |
330 | 912 | return RawRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, |
331 | 912 | pData, nBufXSize, nBufYSize, eBufType, |
332 | 912 | nPixelSpace, nLineSpace, psExtraArg); |
333 | 912 | } |
334 | | |
335 | | /************************************************************************/ |
336 | | /* PDS4MaskBand() */ |
337 | | /************************************************************************/ |
338 | | |
339 | | PDS4MaskBand::PDS4MaskBand(GDALRasterBand *poBaseBand, |
340 | | const std::vector<double> &adfConstants) |
341 | 0 | : m_poBaseBand(poBaseBand), m_pBuffer(nullptr), m_adfConstants(adfConstants) |
342 | 0 | { |
343 | 0 | eDataType = GDT_Byte; |
344 | 0 | poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize); |
345 | 0 | nRasterXSize = poBaseBand->GetXSize(); |
346 | 0 | nRasterYSize = poBaseBand->GetYSize(); |
347 | 0 | } |
348 | | |
349 | | /************************************************************************/ |
350 | | /* ~PDS4MaskBand() */ |
351 | | /************************************************************************/ |
352 | | |
353 | | PDS4MaskBand::~PDS4MaskBand() |
354 | 0 | { |
355 | 0 | VSIFree(m_pBuffer); |
356 | 0 | } |
357 | | |
358 | | /************************************************************************/ |
359 | | /* FillMask() */ |
360 | | /************************************************************************/ |
361 | | |
362 | | template <class T> |
363 | | static void FillMask(void *pvBuffer, GByte *pabyDst, int nReqXSize, |
364 | | int nReqYSize, int nBlockXSize, |
365 | | const std::vector<double> &adfConstants) |
366 | 0 | { |
367 | 0 | const T *pSrc = static_cast<T *>(pvBuffer); |
368 | 0 | std::vector<T> aConstants; |
369 | 0 | for (size_t i = 0; i < adfConstants.size(); i++) |
370 | 0 | { |
371 | 0 | T cst; |
372 | 0 | GDALCopyWord(adfConstants[i], cst); |
373 | 0 | aConstants.push_back(cst); |
374 | 0 | } |
375 | |
|
376 | 0 | for (int y = 0; y < nReqYSize; y++) |
377 | 0 | { |
378 | 0 | for (int x = 0; x < nReqXSize; x++) |
379 | 0 | { |
380 | 0 | const T nSrc = pSrc[y * nBlockXSize + x]; |
381 | 0 | if (std::find(aConstants.begin(), aConstants.end(), nSrc) != |
382 | 0 | aConstants.end()) |
383 | 0 | { |
384 | 0 | pabyDst[y * nBlockXSize + x] = 0; |
385 | 0 | } |
386 | 0 | else |
387 | 0 | { |
388 | 0 | pabyDst[y * nBlockXSize + x] = 255; |
389 | 0 | } |
390 | 0 | } |
391 | 0 | } |
392 | 0 | } Unexecuted instantiation: pds4dataset.cpp:void FillMask<unsigned char>(void*, unsigned char*, int, int, int, std::__1::vector<double, std::__1::allocator<double> > const&) Unexecuted instantiation: pds4dataset.cpp:void FillMask<signed char>(void*, unsigned char*, int, int, int, std::__1::vector<double, std::__1::allocator<double> > const&) Unexecuted instantiation: pds4dataset.cpp:void FillMask<unsigned short>(void*, unsigned char*, int, int, int, std::__1::vector<double, std::__1::allocator<double> > const&) Unexecuted instantiation: pds4dataset.cpp:void FillMask<short>(void*, unsigned char*, int, int, int, std::__1::vector<double, std::__1::allocator<double> > const&) Unexecuted instantiation: pds4dataset.cpp:void FillMask<unsigned int>(void*, unsigned char*, int, int, int, std::__1::vector<double, std::__1::allocator<double> > const&) Unexecuted instantiation: pds4dataset.cpp:void FillMask<int>(void*, unsigned char*, int, int, int, std::__1::vector<double, std::__1::allocator<double> > const&) Unexecuted instantiation: pds4dataset.cpp:void FillMask<float>(void*, unsigned char*, int, int, int, std::__1::vector<double, std::__1::allocator<double> > const&) Unexecuted instantiation: pds4dataset.cpp:void FillMask<double>(void*, unsigned char*, int, int, int, std::__1::vector<double, std::__1::allocator<double> > const&) |
393 | | |
394 | | /************************************************************************/ |
395 | | /* IReadBlock() */ |
396 | | /************************************************************************/ |
397 | | |
398 | | CPLErr PDS4MaskBand::IReadBlock(int nXBlock, int nYBlock, void *pImage) |
399 | | |
400 | 0 | { |
401 | 0 | const GDALDataType eSrcDT = m_poBaseBand->GetRasterDataType(); |
402 | 0 | const int nSrcDTSize = GDALGetDataTypeSizeBytes(eSrcDT); |
403 | 0 | if (m_pBuffer == nullptr) |
404 | 0 | { |
405 | 0 | m_pBuffer = VSI_MALLOC3_VERBOSE(nBlockXSize, nBlockYSize, nSrcDTSize); |
406 | 0 | if (m_pBuffer == nullptr) |
407 | 0 | return CE_Failure; |
408 | 0 | } |
409 | | |
410 | 0 | int nXOff = nXBlock * nBlockXSize; |
411 | 0 | int nReqXSize = nBlockXSize; |
412 | 0 | if (nXOff + nReqXSize > nRasterXSize) |
413 | 0 | nReqXSize = nRasterXSize - nXOff; |
414 | 0 | int nYOff = nYBlock * nBlockYSize; |
415 | 0 | int nReqYSize = nBlockYSize; |
416 | 0 | if (nYOff + nReqYSize > nRasterYSize) |
417 | 0 | nReqYSize = nRasterYSize - nYOff; |
418 | |
|
419 | 0 | if (m_poBaseBand->RasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, |
420 | 0 | m_pBuffer, nReqXSize, nReqYSize, eSrcDT, |
421 | 0 | nSrcDTSize, |
422 | 0 | static_cast<GSpacing>(nSrcDTSize) * nBlockXSize, |
423 | 0 | nullptr) != CE_None) |
424 | 0 | { |
425 | 0 | return CE_Failure; |
426 | 0 | } |
427 | | |
428 | 0 | GByte *pabyDst = static_cast<GByte *>(pImage); |
429 | 0 | if (eSrcDT == GDT_Byte) |
430 | 0 | { |
431 | 0 | FillMask<GByte>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize, |
432 | 0 | m_adfConstants); |
433 | 0 | } |
434 | 0 | else if (eSrcDT == GDT_Int8) |
435 | 0 | { |
436 | 0 | FillMask<GInt8>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize, |
437 | 0 | m_adfConstants); |
438 | 0 | } |
439 | 0 | else if (eSrcDT == GDT_UInt16) |
440 | 0 | { |
441 | 0 | FillMask<GUInt16>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize, |
442 | 0 | m_adfConstants); |
443 | 0 | } |
444 | 0 | else if (eSrcDT == GDT_Int16) |
445 | 0 | { |
446 | 0 | FillMask<GInt16>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize, |
447 | 0 | m_adfConstants); |
448 | 0 | } |
449 | 0 | else if (eSrcDT == GDT_UInt32) |
450 | 0 | { |
451 | 0 | FillMask<GUInt32>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize, |
452 | 0 | m_adfConstants); |
453 | 0 | } |
454 | 0 | else if (eSrcDT == GDT_Int32) |
455 | 0 | { |
456 | 0 | FillMask<GInt32>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize, |
457 | 0 | m_adfConstants); |
458 | 0 | } |
459 | 0 | else if (eSrcDT == GDT_Float32) |
460 | 0 | { |
461 | 0 | FillMask<float>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize, |
462 | 0 | m_adfConstants); |
463 | 0 | } |
464 | 0 | else if (eSrcDT == GDT_Float64) |
465 | 0 | { |
466 | 0 | FillMask<double>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize, |
467 | 0 | m_adfConstants); |
468 | 0 | } |
469 | |
|
470 | 0 | return CE_None; |
471 | 0 | } |
472 | | |
473 | | /************************************************************************/ |
474 | | /* PDS4Dataset() */ |
475 | | /************************************************************************/ |
476 | | |
477 | | PDS4Dataset::PDS4Dataset() |
478 | 108 | { |
479 | 108 | m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
480 | 108 | m_adfGeoTransform[0] = 0.0; |
481 | 108 | m_adfGeoTransform[1] = 1.0; |
482 | 108 | m_adfGeoTransform[2] = 0.0; |
483 | 108 | m_adfGeoTransform[3] = 0.0; |
484 | 108 | m_adfGeoTransform[4] = 0.0; |
485 | 108 | m_adfGeoTransform[5] = 1.0; |
486 | 108 | } |
487 | | |
488 | | /************************************************************************/ |
489 | | /* ~PDS4Dataset() */ |
490 | | /************************************************************************/ |
491 | | |
492 | | PDS4Dataset::~PDS4Dataset() |
493 | 108 | { |
494 | 108 | PDS4Dataset::Close(); |
495 | 108 | } |
496 | | |
497 | | /************************************************************************/ |
498 | | /* Close() */ |
499 | | /************************************************************************/ |
500 | | |
501 | | CPLErr PDS4Dataset::Close() |
502 | 192 | { |
503 | 192 | CPLErr eErr = CE_None; |
504 | 192 | if (nOpenFlags != OPEN_FLAGS_CLOSED) |
505 | 108 | { |
506 | 108 | if (m_bMustInitImageFile) |
507 | 0 | { |
508 | 0 | if (!InitImageFile()) |
509 | 0 | eErr = CE_Failure; |
510 | 0 | } |
511 | | |
512 | 108 | if (PDS4Dataset::FlushCache(true) != CE_None) |
513 | 0 | eErr = CE_Failure; |
514 | | |
515 | 108 | if (m_bCreateHeader || m_bDirtyHeader) |
516 | 98 | WriteHeader(); |
517 | 108 | if (m_fpImage) |
518 | 37 | VSIFCloseL(m_fpImage); |
519 | 108 | CSLDestroy(m_papszCreationOptions); |
520 | 108 | PDS4Dataset::CloseDependentDatasets(); |
521 | | |
522 | 108 | if (GDALPamDataset::Close() != CE_None) |
523 | 0 | eErr = CE_Failure; |
524 | 108 | } |
525 | 192 | return eErr; |
526 | 192 | } |
527 | | |
528 | | /************************************************************************/ |
529 | | /* GetRawBinaryLayout() */ |
530 | | /************************************************************************/ |
531 | | |
532 | | bool PDS4Dataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout) |
533 | 0 | { |
534 | 0 | if (!RawDataset::GetRawBinaryLayout(sLayout)) |
535 | 0 | return false; |
536 | 0 | sLayout.osRawFilename = m_osImageFilename; |
537 | 0 | return true; |
538 | 0 | } |
539 | | |
540 | | /************************************************************************/ |
541 | | /* CloseDependentDatasets() */ |
542 | | /************************************************************************/ |
543 | | |
544 | | int PDS4Dataset::CloseDependentDatasets() |
545 | 108 | { |
546 | 108 | int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets(); |
547 | | |
548 | 108 | if (m_poExternalDS) |
549 | 0 | { |
550 | 0 | bHasDroppedRef = FALSE; |
551 | 0 | delete m_poExternalDS; |
552 | 0 | m_poExternalDS = nullptr; |
553 | |
|
554 | 0 | for (int iBand = 0; iBand < nBands; iBand++) |
555 | 0 | { |
556 | 0 | delete papoBands[iBand]; |
557 | 0 | papoBands[iBand] = nullptr; |
558 | 0 | } |
559 | 0 | nBands = 0; |
560 | 0 | } |
561 | | |
562 | 108 | return bHasDroppedRef; |
563 | 108 | } |
564 | | |
565 | | /************************************************************************/ |
566 | | /* GetSpatialRef() */ |
567 | | /************************************************************************/ |
568 | | |
569 | | const OGRSpatialReference *PDS4Dataset::GetSpatialRef() const |
570 | 1 | { |
571 | 1 | if (!m_oSRS.IsEmpty()) |
572 | 0 | return &m_oSRS; |
573 | 1 | return GDALPamDataset::GetSpatialRef(); |
574 | 1 | } |
575 | | |
576 | | /************************************************************************/ |
577 | | /* SetSpatialRef() */ |
578 | | /************************************************************************/ |
579 | | |
580 | | CPLErr PDS4Dataset::SetSpatialRef(const OGRSpatialReference *poSRS) |
581 | | |
582 | 7 | { |
583 | 7 | if (eAccess == GA_ReadOnly) |
584 | 0 | return CE_Failure; |
585 | 7 | m_oSRS.Clear(); |
586 | 7 | if (poSRS) |
587 | 7 | m_oSRS = *poSRS; |
588 | 7 | if (m_poExternalDS) |
589 | 0 | m_poExternalDS->SetSpatialRef(poSRS); |
590 | 7 | return CE_None; |
591 | 7 | } |
592 | | |
593 | | /************************************************************************/ |
594 | | /* GetGeoTransform() */ |
595 | | /************************************************************************/ |
596 | | |
597 | | CPLErr PDS4Dataset::GetGeoTransform(double *padfTransform) |
598 | | |
599 | 1 | { |
600 | 1 | if (m_bGotTransform) |
601 | 1 | { |
602 | 1 | memcpy(padfTransform, m_adfGeoTransform, sizeof(double) * 6); |
603 | 1 | return CE_None; |
604 | 1 | } |
605 | | |
606 | 0 | return GDALPamDataset::GetGeoTransform(padfTransform); |
607 | 1 | } |
608 | | |
609 | | /************************************************************************/ |
610 | | /* SetGeoTransform() */ |
611 | | /************************************************************************/ |
612 | | |
613 | | CPLErr PDS4Dataset::SetGeoTransform(double *padfTransform) |
614 | | |
615 | 29 | { |
616 | 29 | if (!((padfTransform[1] > 0.0 && padfTransform[2] == 0.0 && |
617 | 29 | padfTransform[4] == 0.0 && padfTransform[5] < 0.0) || |
618 | 29 | (padfTransform[1] == 0.0 && padfTransform[2] > 0.0 && |
619 | 25 | padfTransform[4] > 0.0 && padfTransform[5] == 0.0))) |
620 | 25 | { |
621 | 25 | CPLError(CE_Failure, CPLE_NotSupported, |
622 | 25 | "Only north-up geotransform or map_projection_rotation=90 " |
623 | 25 | "supported"); |
624 | 25 | return CE_Failure; |
625 | 25 | } |
626 | 4 | memcpy(m_adfGeoTransform, padfTransform, sizeof(double) * 6); |
627 | 4 | m_bGotTransform = true; |
628 | 4 | if (m_poExternalDS) |
629 | 0 | m_poExternalDS->SetGeoTransform(padfTransform); |
630 | 4 | return CE_None; |
631 | 29 | } |
632 | | |
633 | | /************************************************************************/ |
634 | | /* SetMetadata() */ |
635 | | /************************************************************************/ |
636 | | |
637 | | CPLErr PDS4Dataset::SetMetadata(char **papszMD, const char *pszDomain) |
638 | 0 | { |
639 | 0 | if (m_bUseSrcLabel && eAccess == GA_Update && pszDomain != nullptr && |
640 | 0 | EQUAL(pszDomain, "xml:PDS4")) |
641 | 0 | { |
642 | 0 | if (papszMD != nullptr && papszMD[0] != nullptr) |
643 | 0 | { |
644 | 0 | m_osXMLPDS4 = papszMD[0]; |
645 | 0 | } |
646 | 0 | return CE_None; |
647 | 0 | } |
648 | 0 | return GDALPamDataset::SetMetadata(papszMD, pszDomain); |
649 | 0 | } |
650 | | |
651 | | /************************************************************************/ |
652 | | /* GetFileList() */ |
653 | | /************************************************************************/ |
654 | | |
655 | | char **PDS4Dataset::GetFileList() |
656 | 2 | { |
657 | 2 | char **papszFileList = GDALPamDataset::GetFileList(); |
658 | 2 | if (!m_osXMLFilename.empty() && |
659 | 2 | CSLFindString(papszFileList, m_osXMLFilename) < 0) |
660 | 0 | { |
661 | 0 | papszFileList = CSLAddString(papszFileList, m_osXMLFilename); |
662 | 0 | } |
663 | 2 | if (!m_osImageFilename.empty()) |
664 | 0 | { |
665 | 0 | papszFileList = CSLAddString(papszFileList, m_osImageFilename); |
666 | 0 | } |
667 | 2 | for (const auto &poLayer : m_apoLayers) |
668 | 0 | { |
669 | 0 | auto papszTemp = poLayer->GetFileList(); |
670 | 0 | papszFileList = CSLInsertStrings(papszFileList, -1, papszTemp); |
671 | 0 | CSLDestroy(papszTemp); |
672 | 0 | } |
673 | 2 | return papszFileList; |
674 | 2 | } |
675 | | |
676 | | /************************************************************************/ |
677 | | /* GetLinearValue() */ |
678 | | /************************************************************************/ |
679 | | |
680 | | static const struct |
681 | | { |
682 | | const char *pszUnit; |
683 | | double dfToMeter; |
684 | | } apsLinearUnits[] = { |
685 | | {"AU", 149597870700.0}, {"Angstrom", 1e-10}, {"cm", 1e-2}, {"km", 1e3}, |
686 | | {"micrometer", 1e-6}, {"mm", 1e-3}, {"nm", 1e-9}}; |
687 | | |
688 | | static double GetLinearValue(const CPLXMLNode *psParent, |
689 | | const char *pszElementName) |
690 | 5 | { |
691 | 5 | const CPLXMLNode *psNode = CPLGetXMLNode(psParent, pszElementName); |
692 | 5 | if (psNode == nullptr) |
693 | 0 | return 0.0; |
694 | 5 | double dfVal = CPLAtof(CPLGetXMLValue(psNode, nullptr, "")); |
695 | 5 | const char *pszUnit = CPLGetXMLValue(psNode, "unit", nullptr); |
696 | 5 | if (pszUnit && !EQUAL(pszUnit, "m")) |
697 | 1 | { |
698 | 1 | bool bFound = false; |
699 | 4 | for (size_t i = 0; i < CPL_ARRAYSIZE(apsLinearUnits); i++) |
700 | 4 | { |
701 | 4 | if (EQUAL(pszUnit, apsLinearUnits[i].pszUnit)) |
702 | 1 | { |
703 | 1 | dfVal *= apsLinearUnits[i].dfToMeter; |
704 | 1 | bFound = true; |
705 | 1 | break; |
706 | 1 | } |
707 | 4 | } |
708 | 1 | if (!bFound) |
709 | 0 | { |
710 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unknown unit '%s' for '%s'", |
711 | 0 | pszUnit, pszElementName); |
712 | 0 | } |
713 | 1 | } |
714 | 5 | return dfVal; |
715 | 5 | } |
716 | | |
717 | | /************************************************************************/ |
718 | | /* GetResolutionValue() */ |
719 | | /************************************************************************/ |
720 | | |
721 | | static const struct |
722 | | { |
723 | | const char *pszUnit; |
724 | | double dfToMeter; |
725 | | } apsResolutionUnits[] = { |
726 | | {"km/pixel", 1e3}, |
727 | | {"mm/pixel", 1e-3}, |
728 | | }; |
729 | | |
730 | | static double GetResolutionValue(CPLXMLNode *psParent, |
731 | | const char *pszElementName) |
732 | 2 | { |
733 | 2 | CPLXMLNode *psNode = CPLGetXMLNode(psParent, pszElementName); |
734 | 2 | if (psNode == nullptr) |
735 | 0 | return 0.0; |
736 | 2 | double dfVal = CPLAtof(CPLGetXMLValue(psNode, nullptr, "")); |
737 | 2 | const char *pszUnit = CPLGetXMLValue(psNode, "unit", nullptr); |
738 | 2 | if (pszUnit && !EQUAL(pszUnit, "m/pixel")) |
739 | 1 | { |
740 | 1 | bool bFound = false; |
741 | 1 | for (size_t i = 0; i < CPL_ARRAYSIZE(apsResolutionUnits); i++) |
742 | 1 | { |
743 | 1 | if (EQUAL(pszUnit, apsResolutionUnits[i].pszUnit)) |
744 | 1 | { |
745 | 1 | dfVal *= apsResolutionUnits[i].dfToMeter; |
746 | 1 | bFound = true; |
747 | 1 | break; |
748 | 1 | } |
749 | 1 | } |
750 | 1 | if (!bFound) |
751 | 0 | { |
752 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unknown unit '%s' for '%s'", |
753 | 0 | pszUnit, pszElementName); |
754 | 0 | } |
755 | 1 | } |
756 | 2 | return dfVal; |
757 | 2 | } |
758 | | |
759 | | /************************************************************************/ |
760 | | /* GetAngularValue() */ |
761 | | /************************************************************************/ |
762 | | |
763 | | static const struct |
764 | | { |
765 | | const char *pszUnit; |
766 | | double dfToDeg; |
767 | | } apsAngularUnits[] = {{"arcmin", 1. / 60.}, |
768 | | {"arcsec", 1. / 3600}, |
769 | | {"hr", 15.0}, |
770 | | {"mrad", 180.0 / M_PI / 1000.}, |
771 | | {"rad", 180.0 / M_PI}}; |
772 | | |
773 | | static double GetAngularValue(CPLXMLNode *psParent, const char *pszElementName, |
774 | | bool *pbGotVal = nullptr) |
775 | 5 | { |
776 | 5 | CPLXMLNode *psNode = CPLGetXMLNode(psParent, pszElementName); |
777 | 5 | if (psNode == nullptr) |
778 | 3 | { |
779 | 3 | if (pbGotVal) |
780 | 2 | *pbGotVal = false; |
781 | 3 | return 0.0; |
782 | 3 | } |
783 | 2 | double dfVal = CPLAtof(CPLGetXMLValue(psNode, nullptr, "")); |
784 | 2 | const char *pszUnit = CPLGetXMLValue(psNode, "unit", nullptr); |
785 | 2 | if (pszUnit && !EQUAL(pszUnit, "deg")) |
786 | 0 | { |
787 | 0 | bool bFound = false; |
788 | 0 | for (size_t i = 0; i < CPL_ARRAYSIZE(apsAngularUnits); i++) |
789 | 0 | { |
790 | 0 | if (EQUAL(pszUnit, apsAngularUnits[i].pszUnit)) |
791 | 0 | { |
792 | 0 | dfVal *= apsAngularUnits[i].dfToDeg; |
793 | 0 | bFound = true; |
794 | 0 | break; |
795 | 0 | } |
796 | 0 | } |
797 | 0 | if (!bFound) |
798 | 0 | { |
799 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "Unknown unit '%s' for '%s'", |
800 | 0 | pszUnit, pszElementName); |
801 | 0 | } |
802 | 0 | } |
803 | 2 | if (pbGotVal) |
804 | 1 | *pbGotVal = true; |
805 | 2 | return dfVal; |
806 | 5 | } |
807 | | |
808 | | /************************************************************************/ |
809 | | /* ReadGeoreferencing() */ |
810 | | /************************************************************************/ |
811 | | |
812 | | // See https://pds.nasa.gov/pds4/cart/v1/PDS4_CART_1G00_1950.xsd, (GDAL 3.4) |
813 | | // https://pds.nasa.gov/pds4/cart/v1/PDS4_CART_1D00_1933.xsd, |
814 | | // https://raw.githubusercontent.com/nasa-pds-data-dictionaries/ldd-cart/master/build/1.B.0.0/PDS4_CART_1B00.xsd, |
815 | | // https://pds.nasa.gov/pds4/cart/v1/PDS4_CART_1700.xsd |
816 | | // and the corresponding .sch files |
817 | | void PDS4Dataset::ReadGeoreferencing(CPLXMLNode *psProduct) |
818 | 1 | { |
819 | 1 | CPLXMLNode *psCart = CPLGetXMLNode( |
820 | 1 | psProduct, "Observation_Area.Discipline_Area.Cartography"); |
821 | 1 | if (psCart == nullptr) |
822 | 0 | { |
823 | 0 | CPLDebug("PDS4", |
824 | 0 | "Did not find Observation_Area.Discipline_Area.Cartography"); |
825 | 0 | return; |
826 | 0 | } |
827 | | |
828 | | // Bounding box: informative only |
829 | 1 | CPLXMLNode *psBounding = |
830 | 1 | CPLGetXMLNode(psCart, "Spatial_Domain.Bounding_Coordinates"); |
831 | 1 | if (psBounding) |
832 | 1 | { |
833 | 1 | const char *pszWest = |
834 | 1 | CPLGetXMLValue(psBounding, "west_bounding_coordinate", nullptr); |
835 | 1 | const char *pszEast = |
836 | 1 | CPLGetXMLValue(psBounding, "east_bounding_coordinate", nullptr); |
837 | 1 | const char *pszNorth = |
838 | 1 | CPLGetXMLValue(psBounding, "north_bounding_coordinate", nullptr); |
839 | 1 | const char *pszSouth = |
840 | 1 | CPLGetXMLValue(psBounding, "south_bounding_coordinate", nullptr); |
841 | 1 | if (pszWest) |
842 | 1 | CPLDebug("PDS4", "West: %s", pszWest); |
843 | 1 | if (pszEast) |
844 | 1 | CPLDebug("PDS4", "East: %s", pszEast); |
845 | 1 | if (pszNorth) |
846 | 1 | CPLDebug("PDS4", "North: %s", pszNorth); |
847 | 1 | if (pszSouth) |
848 | 1 | CPLDebug("PDS4", "South: %s", pszSouth); |
849 | 1 | } |
850 | | |
851 | 1 | CPLXMLNode *psSR = |
852 | 1 | CPLGetXMLNode(psCart, "Spatial_Reference_Information.Horizontal_" |
853 | 1 | "Coordinate_System_Definition"); |
854 | 1 | if (psSR == nullptr) |
855 | 0 | { |
856 | 0 | CPLDebug("PDS4", "Did not find Spatial_Reference_Information." |
857 | 0 | "Horizontal_Coordinate_System_Definition"); |
858 | 0 | return; |
859 | 0 | } |
860 | | |
861 | 1 | double dfLongitudeMultiplier = 1; |
862 | 1 | const CPLXMLNode *psGeodeticModel = CPLGetXMLNode(psSR, "Geodetic_Model"); |
863 | 1 | if (psGeodeticModel != nullptr) |
864 | 1 | { |
865 | 1 | if (EQUAL(CPLGetXMLValue(psGeodeticModel, "longitude_direction", ""), |
866 | 1 | "Positive West")) |
867 | 0 | { |
868 | 0 | dfLongitudeMultiplier = -1; |
869 | 0 | } |
870 | 1 | } |
871 | | |
872 | 1 | OGRSpatialReference oSRS; |
873 | 1 | CPLXMLNode *psGridCoordinateSystem = |
874 | 1 | CPLGetXMLNode(psSR, "Planar.Grid_Coordinate_System"); |
875 | 1 | CPLXMLNode *psMapProjection = CPLGetXMLNode(psSR, "Planar.Map_Projection"); |
876 | 1 | CPLString osProjName; |
877 | 1 | double dfCenterLon = 0.0; |
878 | 1 | double dfCenterLat = 0.0; |
879 | 1 | double dfStdParallel1 = 0.0; |
880 | 1 | double dfStdParallel2 = 0.0; |
881 | 1 | double dfScale = 1.0; |
882 | 1 | double dfMapProjectionRotation = 0.0; |
883 | 1 | if (psGridCoordinateSystem != nullptr) |
884 | 0 | { |
885 | 0 | osProjName = CPLGetXMLValue(psGridCoordinateSystem, |
886 | 0 | "grid_coordinate_system_name", ""); |
887 | 0 | if (!osProjName.empty()) |
888 | 0 | { |
889 | 0 | if (osProjName == "Universal Transverse Mercator") |
890 | 0 | { |
891 | 0 | CPLXMLNode *psUTMZoneNumber = CPLGetXMLNode( |
892 | 0 | psGridCoordinateSystem, |
893 | 0 | "Universal_Transverse_Mercator.utm_zone_number"); |
894 | 0 | if (psUTMZoneNumber) |
895 | 0 | { |
896 | 0 | int nZone = |
897 | 0 | atoi(CPLGetXMLValue(psUTMZoneNumber, nullptr, "")); |
898 | 0 | oSRS.SetUTM(std::abs(nZone), nZone >= 0); |
899 | 0 | } |
900 | 0 | } |
901 | 0 | else if (osProjName == "Universal Polar Stereographic") |
902 | 0 | { |
903 | 0 | CPLXMLNode *psProjParamNode = CPLGetXMLNode( |
904 | 0 | psGridCoordinateSystem, |
905 | 0 | "Universal_Polar_Stereographic.Polar_Stereographic"); |
906 | 0 | if (psProjParamNode) |
907 | 0 | { |
908 | 0 | dfCenterLon = |
909 | 0 | GetAngularValue(psProjParamNode, |
910 | 0 | "longitude_of_central_meridian") * |
911 | 0 | dfLongitudeMultiplier; |
912 | 0 | dfCenterLat = GetAngularValue( |
913 | 0 | psProjParamNode, "latitude_of_projection_origin"); |
914 | 0 | dfScale = CPLAtof(CPLGetXMLValue( |
915 | 0 | psProjParamNode, "scale_factor_at_projection_origin", |
916 | 0 | "1")); |
917 | 0 | oSRS.SetPS(dfCenterLat, dfCenterLon, dfScale, 0, 0); |
918 | 0 | } |
919 | 0 | } |
920 | 0 | else |
921 | 0 | { |
922 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
923 | 0 | "grid_coordinate_system_name = %s not supported", |
924 | 0 | osProjName.c_str()); |
925 | 0 | } |
926 | 0 | } |
927 | 0 | } |
928 | 1 | else if (psMapProjection != nullptr) |
929 | 1 | { |
930 | 1 | osProjName = CPLGetXMLValue(psMapProjection, "map_projection_name", ""); |
931 | 1 | if (!osProjName.empty()) |
932 | 1 | { |
933 | 1 | CPLXMLNode *psProjParamNode = CPLGetXMLNode( |
934 | 1 | psMapProjection, |
935 | 1 | CPLString(osProjName).replaceAll(' ', '_').c_str()); |
936 | 1 | if (psProjParamNode == nullptr && |
937 | | // typo in https://pds.nasa.gov/pds4/cart/v1/PDS4_CART_1700.sch |
938 | 1 | EQUAL(osProjName, "Orothographic")) |
939 | 0 | { |
940 | 0 | psProjParamNode = |
941 | 0 | CPLGetXMLNode(psMapProjection, "Orthographic"); |
942 | 0 | } |
943 | 1 | bool bGotStdParallel1 = false; |
944 | 1 | bool bGotStdParallel2 = false; |
945 | 1 | bool bGotScale = false; |
946 | 1 | if (psProjParamNode) |
947 | 1 | { |
948 | 1 | bool bGotCenterLon = false; |
949 | 1 | dfCenterLon = GetAngularValue(psProjParamNode, |
950 | 1 | "longitude_of_central_meridian", |
951 | 1 | &bGotCenterLon) * |
952 | 1 | dfLongitudeMultiplier; |
953 | 1 | if (!bGotCenterLon) |
954 | 0 | { |
955 | 0 | dfCenterLon = |
956 | 0 | GetAngularValue(psProjParamNode, |
957 | 0 | "straight_vertical_longitude_from_pole", |
958 | 0 | &bGotCenterLon) * |
959 | 0 | dfLongitudeMultiplier; |
960 | 0 | } |
961 | 1 | dfCenterLat = GetAngularValue(psProjParamNode, |
962 | 1 | "latitude_of_projection_origin"); |
963 | 1 | dfStdParallel1 = GetAngularValue( |
964 | 1 | psProjParamNode, "standard_parallel_1", &bGotStdParallel1); |
965 | 1 | dfStdParallel2 = GetAngularValue( |
966 | 1 | psProjParamNode, "standard_parallel_2", &bGotStdParallel2); |
967 | 1 | const char *pszScaleParam = |
968 | 1 | (osProjName == "Transverse Mercator") |
969 | 1 | ? "scale_factor_at_central_meridian" |
970 | 1 | : "scale_factor_at_projection_origin"; |
971 | 1 | const char *pszScaleVal = |
972 | 1 | CPLGetXMLValue(psProjParamNode, pszScaleParam, nullptr); |
973 | 1 | bGotScale = pszScaleVal != nullptr; |
974 | 1 | dfScale = (pszScaleVal) ? CPLAtof(pszScaleVal) : 1.0; |
975 | | |
976 | 1 | dfMapProjectionRotation = |
977 | 1 | GetAngularValue(psProjParamNode, "map_projection_rotation"); |
978 | 1 | } |
979 | | |
980 | 1 | CPLXMLNode *psObliqueAzimuth = |
981 | 1 | CPLGetXMLNode(psProjParamNode, "Oblique_Line_Azimuth"); |
982 | 1 | CPLXMLNode *psObliquePoint = |
983 | 1 | CPLGetXMLNode(psProjParamNode, "Oblique_Line_Point"); |
984 | | |
985 | 1 | if (EQUAL(osProjName, "Equirectangular")) |
986 | 0 | { |
987 | 0 | oSRS.SetEquirectangular2(dfCenterLat, dfCenterLon, |
988 | 0 | dfStdParallel1, 0, 0); |
989 | 0 | } |
990 | 1 | else if (EQUAL(osProjName, "Lambert Conformal Conic")) |
991 | 0 | { |
992 | 0 | if (bGotScale) |
993 | 0 | { |
994 | 0 | if ((bGotStdParallel1 && dfStdParallel1 != dfCenterLat) || |
995 | 0 | (bGotStdParallel2 && dfStdParallel2 != dfCenterLat)) |
996 | 0 | { |
997 | 0 | CPLError( |
998 | 0 | CE_Warning, CPLE_AppDefined, |
999 | 0 | "Ignoring standard_parallel_1 and/or " |
1000 | 0 | "standard_parallel_2 with LCC_1SP formulation"); |
1001 | 0 | } |
1002 | 0 | oSRS.SetLCC1SP(dfCenterLat, dfCenterLon, dfScale, 0, 0); |
1003 | 0 | } |
1004 | 0 | else |
1005 | 0 | { |
1006 | 0 | oSRS.SetLCC(dfStdParallel1, dfStdParallel2, dfCenterLat, |
1007 | 0 | dfCenterLon, 0, 0); |
1008 | 0 | } |
1009 | 0 | } |
1010 | 1 | else if (EQUAL(osProjName, "Mercator")) |
1011 | 0 | { |
1012 | 0 | if (bGotScale) |
1013 | 0 | { |
1014 | 0 | oSRS.SetMercator(dfCenterLat, // should be 0 normally |
1015 | 0 | dfCenterLon, dfScale, 0.0, 0.0); |
1016 | 0 | } |
1017 | 0 | else |
1018 | 0 | { |
1019 | 0 | oSRS.SetMercator2SP(dfStdParallel1, |
1020 | 0 | dfCenterLat, // should be 0 normally |
1021 | 0 | dfCenterLon, 0.0, 0.0); |
1022 | 0 | } |
1023 | 0 | } |
1024 | 1 | else if (EQUAL(osProjName, "Orthographic")) |
1025 | 0 | { |
1026 | 0 | oSRS.SetOrthographic(dfCenterLat, dfCenterLon, 0.0, 0.0); |
1027 | 0 | } |
1028 | 1 | else if (EQUAL(osProjName, "Oblique Mercator") && |
1029 | 1 | (psObliqueAzimuth != nullptr || psObliquePoint != nullptr)) |
1030 | 0 | { |
1031 | 0 | if (psObliqueAzimuth) |
1032 | 0 | { |
1033 | | // Not sure of this |
1034 | 0 | dfCenterLon = CPLAtof( |
1035 | 0 | CPLGetXMLValue(psObliqueAzimuth, |
1036 | 0 | "azimuth_measure_point_longitude", "0")); |
1037 | |
|
1038 | 0 | double dfAzimuth = CPLAtof(CPLGetXMLValue( |
1039 | 0 | psObliqueAzimuth, "azimuthal_angle", "0")); |
1040 | 0 | oSRS.SetProjection( |
1041 | 0 | SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER); |
1042 | 0 | oSRS.SetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, |
1043 | 0 | dfCenterLat); |
1044 | 0 | oSRS.SetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, |
1045 | 0 | dfCenterLon); |
1046 | 0 | oSRS.SetNormProjParm(SRS_PP_AZIMUTH, dfAzimuth); |
1047 | | // SetNormProjParm( SRS_PP_RECTIFIED_GRID_ANGLE, |
1048 | | // dfRectToSkew ); |
1049 | 0 | oSRS.SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale); |
1050 | 0 | oSRS.SetNormProjParm(SRS_PP_FALSE_EASTING, 0.0); |
1051 | 0 | oSRS.SetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0); |
1052 | 0 | } |
1053 | 0 | else |
1054 | 0 | { |
1055 | 0 | double dfLat1 = 0.0; |
1056 | 0 | double dfLong1 = 0.0; |
1057 | 0 | double dfLat2 = 0.0; |
1058 | 0 | double dfLong2 = 0.0; |
1059 | 0 | CPLXMLNode *psPoint = CPLGetXMLNode( |
1060 | 0 | psObliquePoint, "Oblique_Line_Point_Group"); |
1061 | 0 | if (psPoint) |
1062 | 0 | { |
1063 | 0 | dfLat1 = CPLAtof(CPLGetXMLValue( |
1064 | 0 | psPoint, "oblique_line_latitude", "0.0")); |
1065 | 0 | dfLong1 = CPLAtof(CPLGetXMLValue( |
1066 | 0 | psPoint, "oblique_line_longitude", "0.0")); |
1067 | 0 | psPoint = psPoint->psNext; |
1068 | 0 | if (psPoint && psPoint->eType == CXT_Element && |
1069 | 0 | EQUAL(psPoint->pszValue, |
1070 | 0 | "Oblique_Line_Point_Group")) |
1071 | 0 | { |
1072 | 0 | dfLat2 = CPLAtof(CPLGetXMLValue( |
1073 | 0 | psPoint, "oblique_line_latitude", "0.0")); |
1074 | 0 | dfLong2 = CPLAtof(CPLGetXMLValue( |
1075 | 0 | psPoint, "oblique_line_longitude", "0.0")); |
1076 | 0 | } |
1077 | 0 | } |
1078 | 0 | oSRS.SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2, |
1079 | 0 | dfLong2, dfScale, 0.0, 0.0); |
1080 | 0 | } |
1081 | 0 | } |
1082 | 1 | else if (EQUAL(osProjName, "Polar Stereographic")) |
1083 | 0 | { |
1084 | 0 | oSRS.SetPS(dfCenterLat, dfCenterLon, dfScale, 0, 0); |
1085 | 0 | } |
1086 | 1 | else if (EQUAL(osProjName, "Polyconic")) |
1087 | 0 | { |
1088 | 0 | oSRS.SetPolyconic(dfCenterLat, dfCenterLon, 0, 0); |
1089 | 0 | } |
1090 | 1 | else if (EQUAL(osProjName, "Sinusoidal")) |
1091 | 0 | { |
1092 | 0 | oSRS.SetSinusoidal(dfCenterLon, 0, 0); |
1093 | 0 | } |
1094 | 1 | else if (EQUAL(osProjName, "Transverse Mercator")) |
1095 | 1 | { |
1096 | 1 | oSRS.SetTM(dfCenterLat, dfCenterLon, dfScale, 0, 0); |
1097 | 1 | } |
1098 | | |
1099 | | // Below values are valid map_projection_name according to |
1100 | | // the schematron but they don't have a dedicated element to |
1101 | | // hold the projection parameter. Assumed the schema is extended |
1102 | | // similarly to the existing for a few obvious ones |
1103 | 0 | else if (EQUAL(osProjName, "Albers Conical Equal Area")) |
1104 | 0 | { |
1105 | 0 | oSRS.SetACEA(dfStdParallel1, dfStdParallel2, dfCenterLat, |
1106 | 0 | dfCenterLon, 0.0, 0.0); |
1107 | 0 | } |
1108 | 0 | else if (EQUAL(osProjName, "Azimuthal Equidistant")) |
1109 | 0 | { |
1110 | 0 | oSRS.SetAE(dfCenterLat, dfCenterLon, 0, 0); |
1111 | 0 | } |
1112 | 0 | else if (EQUAL(osProjName, "Equidistant Conic")) |
1113 | 0 | { |
1114 | 0 | oSRS.SetEC(dfStdParallel1, dfStdParallel2, dfCenterLat, |
1115 | 0 | dfCenterLon, 0.0, 0.0); |
1116 | 0 | } |
1117 | | // Unhandled: General Vertical Near-sided Projection |
1118 | 0 | else if (EQUAL(osProjName, "Gnomonic")) |
1119 | 0 | { |
1120 | 0 | oSRS.SetGnomonic(dfCenterLat, dfCenterLon, 0, 0); |
1121 | 0 | } |
1122 | 0 | else if (EQUAL(osProjName, "Lambert Azimuthal Equal Area")) |
1123 | 0 | { |
1124 | 0 | oSRS.SetLAEA(dfCenterLat, dfCenterLon, 0, 0); |
1125 | 0 | } |
1126 | 0 | else if (EQUAL(osProjName, "Miller Cylindrical")) |
1127 | 0 | { |
1128 | 0 | oSRS.SetMC(dfCenterLat, dfCenterLon, 0, 0); |
1129 | 0 | } |
1130 | 0 | else if (EQUAL(osProjName, "Orothographic") // typo |
1131 | 0 | || EQUAL(osProjName, "Orthographic")) |
1132 | 0 | { |
1133 | 0 | osProjName = "Orthographic"; |
1134 | 0 | oSRS.SetOrthographic(dfCenterLat, dfCenterLon, 0, 0); |
1135 | 0 | } |
1136 | 0 | else if (EQUAL(osProjName, "Robinson")) |
1137 | 0 | { |
1138 | 0 | oSRS.SetRobinson(dfCenterLon, 0, 0); |
1139 | 0 | } |
1140 | | // Unhandled: Space Oblique Mercator |
1141 | 0 | else if (EQUAL(osProjName, "Stereographic")) |
1142 | 0 | { |
1143 | 0 | oSRS.SetStereographic(dfCenterLat, dfCenterLon, dfScale, 0, 0); |
1144 | 0 | } |
1145 | 0 | else if (EQUAL(osProjName, "van der Grinten")) |
1146 | 0 | { |
1147 | 0 | oSRS.SetVDG(dfCenterLon, 0, 0); |
1148 | 0 | } |
1149 | 0 | else if (EQUAL(osProjName, "Oblique Cylindrical")) |
1150 | 0 | { |
1151 | 0 | const double poleLatitude = GetAngularValue( |
1152 | 0 | psProjParamNode, "oblique_proj_pole_latitude"); |
1153 | 0 | const double poleLongitude = |
1154 | 0 | GetAngularValue(psProjParamNode, |
1155 | 0 | "oblique_proj_pole_longitude") * |
1156 | 0 | dfLongitudeMultiplier; |
1157 | 0 | const double poleRotation = GetAngularValue( |
1158 | 0 | psProjParamNode, "oblique_proj_pole_rotation"); |
1159 | |
|
1160 | 0 | CPLString oProj4String; |
1161 | | // Cf isis3dataset.cpp comments for ObliqueCylindrical |
1162 | 0 | oProj4String.Printf("+proj=ob_tran +o_proj=eqc +o_lon_p=%.17g " |
1163 | 0 | "+o_lat_p=%.17g +lon_0=%.17g", |
1164 | 0 | -poleRotation, 180 - poleLatitude, |
1165 | 0 | poleLongitude); |
1166 | 0 | oSRS.SetFromUserInput(oProj4String); |
1167 | 0 | } |
1168 | 0 | else |
1169 | 0 | { |
1170 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
1171 | 0 | "map_projection_name = %s not supported", |
1172 | 0 | osProjName.c_str()); |
1173 | 0 | } |
1174 | 1 | } |
1175 | 1 | } |
1176 | 0 | else |
1177 | 0 | { |
1178 | 0 | CPLXMLNode *psGeographic = CPLGetXMLNode(psSR, "Geographic"); |
1179 | 0 | if (GetLayerCount() && psGeographic) |
1180 | 0 | { |
1181 | | // do nothing |
1182 | 0 | } |
1183 | 0 | else |
1184 | 0 | { |
1185 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1186 | 0 | "Planar.Map_Projection not found"); |
1187 | 0 | } |
1188 | 0 | } |
1189 | | |
1190 | 1 | if (oSRS.IsProjected()) |
1191 | 1 | { |
1192 | 1 | oSRS.SetLinearUnits("Metre", 1.0); |
1193 | 1 | } |
1194 | | |
1195 | 1 | if (psGeodeticModel != nullptr) |
1196 | 1 | { |
1197 | 1 | const char *pszLatitudeType = |
1198 | 1 | CPLGetXMLValue(psGeodeticModel, "latitude_type", ""); |
1199 | 1 | bool bIsOgraphic = EQUAL(pszLatitudeType, "Planetographic"); |
1200 | | |
1201 | 1 | const bool bUseLDD1930RadiusNames = |
1202 | 1 | CPLGetXMLNode(psGeodeticModel, "a_axis_radius") != nullptr; |
1203 | | |
1204 | | // Before PDS CART schema pre-1.B.10.0 (pre LDD version 1.9.3.0), |
1205 | | // the confusing semi_major_radius, semi_minor_radius and polar_radius |
1206 | | // were used but did not follow the recommended |
1207 | | // FGDC names. Using both "semi" and "radius" in the same keyword, |
1208 | | // which both mean half, does not make sense. |
1209 | 1 | const char *pszAAxis = |
1210 | 1 | bUseLDD1930RadiusNames ? "a_axis_radius" : "semi_major_radius"; |
1211 | 1 | const char *pszBAxis = |
1212 | 1 | bUseLDD1930RadiusNames ? "b_axis_radius" : "semi_minor_radius"; |
1213 | 1 | const char *pszCAxis = |
1214 | 1 | bUseLDD1930RadiusNames ? "c_axis_radius" : "polar_radius"; |
1215 | | |
1216 | 1 | const double dfSemiMajor = GetLinearValue(psGeodeticModel, pszAAxis); |
1217 | | |
1218 | | // a_axis_radius and b_axis_radius should be the same in most cases |
1219 | | // unless a triaxial body is being defined. This should be extremely |
1220 | | // rare (and not used) since the IAU generally defines a best-fit sphere |
1221 | | // for triaxial bodies: https://astrogeology.usgs.gov/groups/IAU-WGCCRE |
1222 | 1 | const double dfBValue = GetLinearValue(psGeodeticModel, pszBAxis); |
1223 | 1 | if (dfSemiMajor != dfBValue) |
1224 | 0 | { |
1225 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1226 | 0 | "%s = %f m, different from " |
1227 | 0 | "%s = %f, will be ignored", |
1228 | 0 | pszBAxis, dfBValue, pszAAxis, dfSemiMajor); |
1229 | 0 | } |
1230 | | |
1231 | 1 | const double dfPolarRadius = GetLinearValue(psGeodeticModel, pszCAxis); |
1232 | | // Use the polar_radius as the actual semi minor |
1233 | 1 | const double dfSemiMinor = dfPolarRadius; |
1234 | | |
1235 | | // Compulsory |
1236 | 1 | const char *pszTargetName = CPLGetXMLValue( |
1237 | 1 | psProduct, "Observation_Area.Target_Identification.name", |
1238 | 1 | "unknown"); |
1239 | | |
1240 | 1 | if (oSRS.IsProjected()) |
1241 | 1 | { |
1242 | 1 | CPLString osProjTargetName = osProjName + " " + pszTargetName; |
1243 | 1 | oSRS.SetProjCS(osProjTargetName); |
1244 | 1 | } |
1245 | | |
1246 | 1 | CPLString osGeogName = CPLString("GCS_") + pszTargetName; |
1247 | | |
1248 | 1 | CPLString osSphereName = |
1249 | 1 | CPLGetXMLValue(psGeodeticModel, "spheroid_name", pszTargetName); |
1250 | 1 | CPLString osDatumName = "D_" + osSphereName; |
1251 | | |
1252 | | // calculate inverse flattening from major and minor axis: 1/f = a/(a-b) |
1253 | 1 | double dfInvFlattening = 0; |
1254 | 1 | if ((dfSemiMajor - dfSemiMinor) >= 0.00000001) |
1255 | 1 | { |
1256 | 1 | dfInvFlattening = dfSemiMajor / (dfSemiMajor - dfSemiMinor); |
1257 | 1 | } |
1258 | | |
1259 | | //(if stereographic with center lat ==90) or (polar stereographic ) |
1260 | 1 | if ((EQUAL(osProjName, "STEREOGRAPHIC") && fabs(dfCenterLat) == 90) || |
1261 | 1 | (EQUAL(osProjName, "POLAR STEREOGRAPHIC"))) |
1262 | 0 | { |
1263 | 0 | if (bIsOgraphic) |
1264 | 0 | { |
1265 | 0 | oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName, |
1266 | 0 | dfSemiMajor, dfInvFlattening, |
1267 | 0 | "Reference_Meridian", 0.0); |
1268 | 0 | } |
1269 | 0 | else |
1270 | 0 | { |
1271 | 0 | osSphereName += "_polarRadius"; |
1272 | 0 | oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName, |
1273 | 0 | dfPolarRadius, 0.0, "Reference_Meridian", 0.0); |
1274 | 0 | } |
1275 | 0 | } |
1276 | 1 | else if ((EQUAL(osProjName, "EQUIRECTANGULAR")) || |
1277 | 1 | (EQUAL(osProjName, "ORTHOGRAPHIC")) || |
1278 | 1 | (EQUAL(osProjName, "STEREOGRAPHIC")) || |
1279 | 1 | (EQUAL(osProjName, "SINUSOIDAL"))) |
1280 | 0 | { |
1281 | 0 | oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName, dfSemiMajor, |
1282 | 0 | 0.0, "Reference_Meridian", 0.0); |
1283 | 0 | } |
1284 | 1 | else |
1285 | 1 | { |
1286 | 1 | if (bIsOgraphic) |
1287 | 0 | { |
1288 | 0 | oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName, |
1289 | 0 | dfSemiMajor, dfInvFlattening, |
1290 | 0 | "Reference_Meridian", 0.0); |
1291 | 0 | } |
1292 | 1 | else |
1293 | 1 | { |
1294 | 1 | oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName, |
1295 | 1 | dfSemiMajor, 0.0, "Reference_Meridian", 0.0); |
1296 | 1 | } |
1297 | 1 | } |
1298 | 1 | } |
1299 | | |
1300 | 1 | CPLXMLNode *psPCI = |
1301 | 1 | CPLGetXMLNode(psSR, "Planar.Planar_Coordinate_Information"); |
1302 | 1 | CPLXMLNode *psGT = CPLGetXMLNode(psSR, "Planar.Geo_Transformation"); |
1303 | 1 | if (psPCI && psGT) |
1304 | 1 | { |
1305 | 1 | const char *pszPCIEncoding = |
1306 | 1 | CPLGetXMLValue(psPCI, "planar_coordinate_encoding_method", ""); |
1307 | 1 | CPLXMLNode *psCR = CPLGetXMLNode(psPCI, "Coordinate_Representation"); |
1308 | 1 | if (!EQUAL(pszPCIEncoding, "Coordinate Pair")) |
1309 | 0 | { |
1310 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
1311 | 0 | "planar_coordinate_encoding_method = %s not supported", |
1312 | 0 | pszPCIEncoding); |
1313 | 0 | } |
1314 | 1 | else if (psCR != nullptr) |
1315 | 1 | { |
1316 | 1 | double dfXRes = GetResolutionValue(psCR, "pixel_resolution_x"); |
1317 | 1 | double dfYRes = GetResolutionValue(psCR, "pixel_resolution_y"); |
1318 | 1 | double dfULX = GetLinearValue(psGT, "upperleft_corner_x"); |
1319 | 1 | double dfULY = GetLinearValue(psGT, "upperleft_corner_y"); |
1320 | | |
1321 | | // The PDS4 specification is not really clear about the |
1322 | | // origin convention, but it appears from |
1323 | | // https://github.com/OSGeo/gdal/issues/735 that it matches GDAL |
1324 | | // top-left corner of top-left pixel |
1325 | 1 | m_adfGeoTransform[0] = dfULX; |
1326 | 1 | m_adfGeoTransform[1] = dfXRes; |
1327 | 1 | m_adfGeoTransform[2] = 0.0; |
1328 | 1 | m_adfGeoTransform[3] = dfULY; |
1329 | 1 | m_adfGeoTransform[4] = 0.0; |
1330 | 1 | m_adfGeoTransform[5] = -dfYRes; |
1331 | 1 | m_bGotTransform = true; |
1332 | | |
1333 | 1 | if (dfMapProjectionRotation != 0) |
1334 | 0 | { |
1335 | 0 | const double sin_rot = |
1336 | 0 | dfMapProjectionRotation == 90 |
1337 | 0 | ? 1.0 |
1338 | 0 | : sin(dfMapProjectionRotation / 180 * M_PI); |
1339 | 0 | const double cos_rot = |
1340 | 0 | dfMapProjectionRotation == 90 |
1341 | 0 | ? 0.0 |
1342 | 0 | : cos(dfMapProjectionRotation / 180 * M_PI); |
1343 | 0 | const double gt_1 = cos_rot * m_adfGeoTransform[1] - |
1344 | 0 | sin_rot * m_adfGeoTransform[4]; |
1345 | 0 | const double gt_2 = cos_rot * m_adfGeoTransform[2] - |
1346 | 0 | sin_rot * m_adfGeoTransform[5]; |
1347 | 0 | const double gt_0 = cos_rot * m_adfGeoTransform[0] - |
1348 | 0 | sin_rot * m_adfGeoTransform[3]; |
1349 | 0 | const double gt_4 = sin_rot * m_adfGeoTransform[1] + |
1350 | 0 | cos_rot * m_adfGeoTransform[4]; |
1351 | 0 | const double gt_5 = sin_rot * m_adfGeoTransform[2] + |
1352 | 0 | cos_rot * m_adfGeoTransform[5]; |
1353 | 0 | const double gt_3 = sin_rot * m_adfGeoTransform[0] + |
1354 | 0 | cos_rot * m_adfGeoTransform[3]; |
1355 | 0 | m_adfGeoTransform[1] = gt_1; |
1356 | 0 | m_adfGeoTransform[2] = gt_2; |
1357 | 0 | m_adfGeoTransform[0] = gt_0; |
1358 | 0 | m_adfGeoTransform[4] = gt_4; |
1359 | 0 | m_adfGeoTransform[5] = gt_5; |
1360 | 0 | m_adfGeoTransform[3] = gt_3; |
1361 | 0 | } |
1362 | 1 | } |
1363 | 1 | } |
1364 | | |
1365 | 1 | if (!oSRS.IsEmpty()) |
1366 | 1 | { |
1367 | 1 | if (GetRasterCount()) |
1368 | 0 | { |
1369 | 0 | m_oSRS = std::move(oSRS); |
1370 | 0 | } |
1371 | 1 | else if (GetLayerCount()) |
1372 | 0 | { |
1373 | 0 | for (auto &poLayer : m_apoLayers) |
1374 | 0 | { |
1375 | 0 | if (poLayer->GetGeomType() != wkbNone) |
1376 | 0 | { |
1377 | 0 | auto poSRSClone = oSRS.Clone(); |
1378 | 0 | poLayer->SetSpatialRef(poSRSClone); |
1379 | 0 | poSRSClone->Release(); |
1380 | 0 | } |
1381 | 0 | } |
1382 | 0 | } |
1383 | 1 | } |
1384 | 1 | } |
1385 | | |
1386 | | /************************************************************************/ |
1387 | | /* GetLayer() */ |
1388 | | /************************************************************************/ |
1389 | | |
1390 | | OGRLayer *PDS4Dataset::GetLayer(int nIndex) |
1391 | 135k | { |
1392 | 135k | if (nIndex < 0 || nIndex >= GetLayerCount()) |
1393 | 0 | return nullptr; |
1394 | 135k | return m_apoLayers[nIndex].get(); |
1395 | 135k | } |
1396 | | |
1397 | | /************************************************************************/ |
1398 | | /* FixupTableFilename() */ |
1399 | | /************************************************************************/ |
1400 | | |
1401 | | static std::string FixupTableFilename(const std::string &osFilename) |
1402 | 0 | { |
1403 | 0 | VSIStatBufL sStat; |
1404 | 0 | if (VSIStatL(osFilename.c_str(), &sStat) == 0) |
1405 | 0 | { |
1406 | 0 | return osFilename; |
1407 | 0 | } |
1408 | 0 | const std::string osExt = CPLGetExtensionSafe(osFilename.c_str()); |
1409 | 0 | if (!osExt.empty()) |
1410 | 0 | { |
1411 | 0 | std::string osTry(osFilename); |
1412 | 0 | if (osExt[0] >= 'a' && osExt[0] <= 'z') |
1413 | 0 | { |
1414 | 0 | osTry = CPLResetExtensionSafe(osFilename.c_str(), |
1415 | 0 | CPLString(osExt).toupper()); |
1416 | 0 | } |
1417 | 0 | else |
1418 | 0 | { |
1419 | 0 | osTry = CPLResetExtensionSafe(osFilename.c_str(), |
1420 | 0 | CPLString(osExt).tolower()); |
1421 | 0 | } |
1422 | 0 | if (VSIStatL(osTry.c_str(), &sStat) == 0) |
1423 | 0 | { |
1424 | 0 | return osTry; |
1425 | 0 | } |
1426 | 0 | } |
1427 | 0 | return osFilename; |
1428 | 0 | } |
1429 | | |
1430 | | /************************************************************************/ |
1431 | | /* OpenTableCharacter() */ |
1432 | | /************************************************************************/ |
1433 | | |
1434 | | bool PDS4Dataset::OpenTableCharacter(const char *pszFilename, |
1435 | | const CPLXMLNode *psTable) |
1436 | 0 | { |
1437 | 0 | const std::string osLayerName(CPLGetBasenameSafe(pszFilename)); |
1438 | 0 | const std::string osFullFilename = FixupTableFilename(CPLFormFilenameSafe( |
1439 | 0 | CPLGetPathSafe(m_osXMLFilename.c_str()).c_str(), pszFilename, nullptr)); |
1440 | 0 | std::unique_ptr<PDS4TableCharacter> poLayer(new PDS4TableCharacter( |
1441 | 0 | this, osLayerName.c_str(), osFullFilename.c_str())); |
1442 | 0 | if (!poLayer->ReadTableDef(psTable)) |
1443 | 0 | { |
1444 | 0 | return false; |
1445 | 0 | } |
1446 | 0 | std::unique_ptr<PDS4EditableLayer> poEditableLayer( |
1447 | 0 | new PDS4EditableLayer(poLayer.release())); |
1448 | 0 | m_apoLayers.push_back(std::move(poEditableLayer)); |
1449 | 0 | return true; |
1450 | 0 | } |
1451 | | |
1452 | | /************************************************************************/ |
1453 | | /* OpenTableBinary() */ |
1454 | | /************************************************************************/ |
1455 | | |
1456 | | bool PDS4Dataset::OpenTableBinary(const char *pszFilename, |
1457 | | const CPLXMLNode *psTable) |
1458 | 0 | { |
1459 | 0 | const std::string osLayerName(CPLGetBasenameSafe(pszFilename)); |
1460 | 0 | const std::string osFullFilename = FixupTableFilename(CPLFormFilenameSafe( |
1461 | 0 | CPLGetPathSafe(m_osXMLFilename.c_str()).c_str(), pszFilename, nullptr)); |
1462 | 0 | std::unique_ptr<PDS4TableBinary> poLayer( |
1463 | 0 | new PDS4TableBinary(this, osLayerName.c_str(), osFullFilename.c_str())); |
1464 | 0 | if (!poLayer->ReadTableDef(psTable)) |
1465 | 0 | { |
1466 | 0 | return false; |
1467 | 0 | } |
1468 | 0 | std::unique_ptr<PDS4EditableLayer> poEditableLayer( |
1469 | 0 | new PDS4EditableLayer(poLayer.release())); |
1470 | 0 | m_apoLayers.push_back(std::move(poEditableLayer)); |
1471 | 0 | return true; |
1472 | 0 | } |
1473 | | |
1474 | | /************************************************************************/ |
1475 | | /* OpenTableDelimited() */ |
1476 | | /************************************************************************/ |
1477 | | |
1478 | | bool PDS4Dataset::OpenTableDelimited(const char *pszFilename, |
1479 | | const CPLXMLNode *psTable) |
1480 | 0 | { |
1481 | 0 | const std::string osLayerName(CPLGetBasenameSafe(pszFilename)); |
1482 | 0 | const std::string osFullFilename = FixupTableFilename(CPLFormFilenameSafe( |
1483 | 0 | CPLGetPathSafe(m_osXMLFilename.c_str()).c_str(), pszFilename, nullptr)); |
1484 | 0 | std::unique_ptr<PDS4DelimitedTable> poLayer(new PDS4DelimitedTable( |
1485 | 0 | this, osLayerName.c_str(), osFullFilename.c_str())); |
1486 | 0 | if (!poLayer->ReadTableDef(psTable)) |
1487 | 0 | { |
1488 | 0 | return false; |
1489 | 0 | } |
1490 | 0 | std::unique_ptr<PDS4EditableLayer> poEditableLayer( |
1491 | 0 | new PDS4EditableLayer(poLayer.release())); |
1492 | 0 | m_apoLayers.push_back(std::move(poEditableLayer)); |
1493 | 0 | return true; |
1494 | 0 | } |
1495 | | |
1496 | | /************************************************************************/ |
1497 | | /* Open() */ |
1498 | | /************************************************************************/ |
1499 | | |
1500 | | // See https://pds.nasa.gov/pds4/pds/v1/PDS4_PDS_1800.xsd |
1501 | | // and https://pds.nasa.gov/pds4/pds/v1/PDS4_PDS_1800.sch |
1502 | | PDS4Dataset *PDS4Dataset::OpenInternal(GDALOpenInfo *poOpenInfo) |
1503 | 516 | { |
1504 | 516 | if (!PDS4DriverIdentify(poOpenInfo)) |
1505 | 61 | return nullptr; |
1506 | | |
1507 | 455 | CPLString osXMLFilename(poOpenInfo->pszFilename); |
1508 | 455 | int nFAOIdxLookup = -1; |
1509 | 455 | int nArrayIdxLookup = -1; |
1510 | 455 | if (STARTS_WITH_CI(poOpenInfo->pszFilename, "PDS4:")) |
1511 | 0 | { |
1512 | 0 | char **papszTokens = |
1513 | 0 | CSLTokenizeString2(poOpenInfo->pszFilename, ":", 0); |
1514 | 0 | int nCount = CSLCount(papszTokens); |
1515 | 0 | if (nCount == 5 && strlen(papszTokens[1]) == 1 && |
1516 | 0 | (papszTokens[2][0] == '\\' || papszTokens[2][0] == '/')) |
1517 | 0 | { |
1518 | 0 | osXMLFilename = CPLString(papszTokens[1]) + ":" + papszTokens[2]; |
1519 | 0 | nFAOIdxLookup = atoi(papszTokens[3]); |
1520 | 0 | nArrayIdxLookup = atoi(papszTokens[4]); |
1521 | 0 | } |
1522 | 0 | else if (nCount == 5 && (EQUAL(papszTokens[1], "/vsicurl/http") || |
1523 | 0 | EQUAL(papszTokens[1], "/vsicurl/https"))) |
1524 | 0 | { |
1525 | 0 | osXMLFilename = CPLString(papszTokens[1]) + ":" + papszTokens[2]; |
1526 | 0 | nFAOIdxLookup = atoi(papszTokens[3]); |
1527 | 0 | nArrayIdxLookup = atoi(papszTokens[4]); |
1528 | 0 | } |
1529 | 0 | else if (nCount == 4) |
1530 | 0 | { |
1531 | 0 | osXMLFilename = papszTokens[1]; |
1532 | 0 | nFAOIdxLookup = atoi(papszTokens[2]); |
1533 | 0 | nArrayIdxLookup = atoi(papszTokens[3]); |
1534 | 0 | } |
1535 | 0 | else |
1536 | 0 | { |
1537 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1538 | 0 | "Invalid syntax for PDS4 subdataset name"); |
1539 | 0 | CSLDestroy(papszTokens); |
1540 | 0 | return nullptr; |
1541 | 0 | } |
1542 | 0 | CSLDestroy(papszTokens); |
1543 | 0 | } |
1544 | | |
1545 | 455 | CPLXMLTreeCloser oCloser(CPLParseXMLFile(osXMLFilename)); |
1546 | 455 | CPLXMLNode *psRoot = oCloser.get(); |
1547 | 455 | CPLStripXMLNamespace(psRoot, nullptr, TRUE); |
1548 | | |
1549 | 455 | GDALAccess eAccess = STARTS_WITH_CI(poOpenInfo->pszFilename, "PDS4:") |
1550 | 455 | ? GA_ReadOnly |
1551 | 455 | : poOpenInfo->eAccess; |
1552 | | |
1553 | 455 | CPLXMLNode *psProduct = CPLGetXMLNode(psRoot, "=Product_Observational"); |
1554 | 455 | if (psProduct == nullptr) |
1555 | 445 | { |
1556 | 445 | eAccess = GA_ReadOnly; |
1557 | 445 | psProduct = CPLGetXMLNode(psRoot, "=Product_Ancillary"); |
1558 | 445 | if (psProduct == nullptr) |
1559 | 445 | { |
1560 | 445 | psProduct = CPLGetXMLNode(psRoot, "=Product_Collection"); |
1561 | 445 | } |
1562 | 445 | } |
1563 | 455 | if (psProduct == nullptr) |
1564 | 445 | { |
1565 | 445 | return nullptr; |
1566 | 445 | } |
1567 | | |
1568 | | // Test case: |
1569 | | // https://starbase.jpl.nasa.gov/pds4/1700/dph_example_products/test_Images_DisplaySettings/TestPattern_Image/TestPattern.xml |
1570 | 10 | const char *pszVertDir = CPLGetXMLValue( |
1571 | 10 | psProduct, |
1572 | 10 | "Observation_Area.Discipline_Area.Display_Settings.Display_Direction." |
1573 | 10 | "vertical_display_direction", |
1574 | 10 | ""); |
1575 | 10 | const bool bBottomToTop = EQUAL(pszVertDir, "Bottom to Top"); |
1576 | | |
1577 | 10 | const char *pszHorizDir = CPLGetXMLValue( |
1578 | 10 | psProduct, |
1579 | 10 | "Observation_Area.Discipline_Area.Display_Settings.Display_Direction." |
1580 | 10 | "horizontal_display_direction", |
1581 | 10 | ""); |
1582 | 10 | const bool bRightToLeft = EQUAL(pszHorizDir, "Right to Left"); |
1583 | | |
1584 | 10 | auto poDS = std::make_unique<PDS4Dataset>(); |
1585 | 10 | poDS->m_osXMLFilename = osXMLFilename; |
1586 | 10 | poDS->eAccess = eAccess; |
1587 | 10 | poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions); |
1588 | | |
1589 | 10 | CPLStringList aosSubdatasets; |
1590 | 10 | int nFAOIdx = 0; |
1591 | 101 | for (CPLXMLNode *psIter = psProduct->psChild; psIter != nullptr; |
1592 | 91 | psIter = psIter->psNext) |
1593 | 91 | { |
1594 | 91 | if (psIter->eType != CXT_Element || |
1595 | 91 | (strcmp(psIter->pszValue, "File_Area_Observational") != 0 && |
1596 | 31 | strcmp(psIter->pszValue, "File_Area_Ancillary") != 0 && |
1597 | 31 | strcmp(psIter->pszValue, "File_Area_Inventory") != 0)) |
1598 | 80 | { |
1599 | 80 | continue; |
1600 | 80 | } |
1601 | | |
1602 | 11 | nFAOIdx++; |
1603 | 11 | CPLXMLNode *psFile = CPLGetXMLNode(psIter, "File"); |
1604 | 11 | if (psFile == nullptr) |
1605 | 0 | { |
1606 | 0 | continue; |
1607 | 0 | } |
1608 | 11 | const char *pszFilename = CPLGetXMLValue(psFile, "file_name", nullptr); |
1609 | 11 | if (pszFilename == nullptr) |
1610 | 0 | { |
1611 | 0 | continue; |
1612 | 0 | } |
1613 | | |
1614 | 27 | for (CPLXMLNode *psSubIter = psFile->psChild; psSubIter != nullptr; |
1615 | 16 | psSubIter = psSubIter->psNext) |
1616 | 16 | { |
1617 | 16 | if (psSubIter->eType == CXT_Comment && |
1618 | 16 | EQUAL(psSubIter->pszValue, PREEXISTING_BINARY_FILE)) |
1619 | 0 | { |
1620 | 0 | poDS->m_bCreatedFromExistingBinaryFile = true; |
1621 | 0 | } |
1622 | 16 | } |
1623 | | |
1624 | 11 | int nArrayIdx = 0; |
1625 | 11 | for (CPLXMLNode *psSubIter = psIter->psChild; |
1626 | 40 | (nFAOIdxLookup < 0 || nFAOIdxLookup == nFAOIdx) && |
1627 | 40 | psSubIter != nullptr; |
1628 | 29 | psSubIter = psSubIter->psNext) |
1629 | 29 | { |
1630 | 29 | if (psSubIter->eType != CXT_Element) |
1631 | 0 | { |
1632 | 0 | continue; |
1633 | 0 | } |
1634 | 29 | int nDIM = 0; |
1635 | 29 | if (STARTS_WITH(psSubIter->pszValue, "Array_1D")) |
1636 | 0 | { |
1637 | 0 | nDIM = 1; |
1638 | 0 | } |
1639 | 29 | else if (STARTS_WITH(psSubIter->pszValue, "Array_2D")) |
1640 | 1 | { |
1641 | 1 | nDIM = 2; |
1642 | 1 | } |
1643 | 28 | else if (STARTS_WITH(psSubIter->pszValue, "Array_3D")) |
1644 | 11 | { |
1645 | 11 | nDIM = 3; |
1646 | 11 | } |
1647 | 17 | else if (strcmp(psSubIter->pszValue, "Array") == 0) |
1648 | 0 | { |
1649 | 0 | nDIM = atoi(CPLGetXMLValue(psSubIter, "axes", "0")); |
1650 | 0 | } |
1651 | 17 | else if (strcmp(psSubIter->pszValue, "Table_Character") == 0) |
1652 | 0 | { |
1653 | 0 | poDS->OpenTableCharacter(pszFilename, psSubIter); |
1654 | 0 | continue; |
1655 | 0 | } |
1656 | 17 | else if (strcmp(psSubIter->pszValue, "Table_Binary") == 0) |
1657 | 0 | { |
1658 | 0 | poDS->OpenTableBinary(pszFilename, psSubIter); |
1659 | 0 | continue; |
1660 | 0 | } |
1661 | 17 | else if (strcmp(psSubIter->pszValue, "Table_Delimited") == 0 || |
1662 | 17 | strcmp(psSubIter->pszValue, "Inventory") == 0) |
1663 | 0 | { |
1664 | 0 | poDS->OpenTableDelimited(pszFilename, psSubIter); |
1665 | 0 | continue; |
1666 | 0 | } |
1667 | 29 | if (nDIM == 0) |
1668 | 17 | { |
1669 | 17 | continue; |
1670 | 17 | } |
1671 | | |
1672 | 12 | nArrayIdx++; |
1673 | | // Does it match a selected subdataset ? |
1674 | 12 | if (nArrayIdxLookup > 0 && nArrayIdx != nArrayIdxLookup) |
1675 | 0 | { |
1676 | 0 | continue; |
1677 | 0 | } |
1678 | | |
1679 | 12 | const char *pszArrayName = |
1680 | 12 | CPLGetXMLValue(psSubIter, "name", nullptr); |
1681 | 12 | const char *pszArrayId = |
1682 | 12 | CPLGetXMLValue(psSubIter, "local_identifier", nullptr); |
1683 | 12 | vsi_l_offset nOffset = static_cast<vsi_l_offset>( |
1684 | 12 | CPLAtoGIntBig(CPLGetXMLValue(psSubIter, "offset", "0"))); |
1685 | | |
1686 | 12 | const char *pszAxisIndexOrder = |
1687 | 12 | CPLGetXMLValue(psSubIter, "axis_index_order", ""); |
1688 | 12 | if (!EQUAL(pszAxisIndexOrder, "Last Index Fastest")) |
1689 | 0 | { |
1690 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
1691 | 0 | "axis_index_order = '%s' unhandled", |
1692 | 0 | pszAxisIndexOrder); |
1693 | 0 | continue; |
1694 | 0 | } |
1695 | | |
1696 | | // Figure out data type |
1697 | 12 | const char *pszDataType = |
1698 | 12 | CPLGetXMLValue(psSubIter, "Element_Array.data_type", ""); |
1699 | 12 | GDALDataType eDT = GDT_Byte; |
1700 | 12 | bool bLSBOrder = strstr(pszDataType, "LSB") != nullptr; |
1701 | | |
1702 | | // ComplexLSB16', 'ComplexLSB8', 'ComplexMSB16', 'ComplexMSB8', |
1703 | | // 'IEEE754LSBDouble', 'IEEE754LSBSingle', 'IEEE754MSBDouble', |
1704 | | // 'IEEE754MSBSingle', 'SignedBitString', 'SignedByte', |
1705 | | // 'SignedLSB2', 'SignedLSB4', 'SignedLSB8', 'SignedMSB2', |
1706 | | // 'SignedMSB4', 'SignedMSB8', 'UnsignedBitString', 'UnsignedByte', |
1707 | | // 'UnsignedLSB2', 'UnsignedLSB4', 'UnsignedLSB8', 'UnsignedMSB2', |
1708 | | // 'UnsignedMSB4', 'UnsignedMSB8' |
1709 | 12 | if (EQUAL(pszDataType, "ComplexLSB16") || |
1710 | 12 | EQUAL(pszDataType, "ComplexMSB16")) |
1711 | 0 | { |
1712 | 0 | eDT = GDT_CFloat64; |
1713 | 0 | } |
1714 | 12 | else if (EQUAL(pszDataType, "ComplexLSB8") || |
1715 | 12 | EQUAL(pszDataType, "ComplexMSB8")) |
1716 | 0 | { |
1717 | 0 | eDT = GDT_CFloat32; |
1718 | 0 | } |
1719 | 12 | else if (EQUAL(pszDataType, "IEEE754LSBDouble") || |
1720 | 12 | EQUAL(pszDataType, "IEEE754MSBDouble")) |
1721 | 0 | { |
1722 | 0 | eDT = GDT_Float64; |
1723 | 0 | } |
1724 | 12 | else if (EQUAL(pszDataType, "IEEE754LSBSingle") || |
1725 | 12 | EQUAL(pszDataType, "IEEE754MSBSingle")) |
1726 | 3 | { |
1727 | 3 | eDT = GDT_Float32; |
1728 | 3 | } |
1729 | | // SignedBitString unhandled |
1730 | 9 | else if (EQUAL(pszDataType, "SignedByte")) |
1731 | 0 | { |
1732 | 0 | eDT = GDT_Int8; |
1733 | 0 | } |
1734 | 9 | else if (EQUAL(pszDataType, "SignedLSB2") || |
1735 | 9 | EQUAL(pszDataType, "SignedMSB2")) |
1736 | 0 | { |
1737 | 0 | eDT = GDT_Int16; |
1738 | 0 | } |
1739 | 9 | else if (EQUAL(pszDataType, "SignedLSB4") || |
1740 | 9 | EQUAL(pszDataType, "SignedMSB4")) |
1741 | 0 | { |
1742 | 0 | eDT = GDT_Int32; |
1743 | 0 | } |
1744 | | // SignedLSB8 and SignedMSB8 unhandled |
1745 | 9 | else if (EQUAL(pszDataType, "UnsignedByte")) |
1746 | 8 | { |
1747 | 8 | eDT = GDT_Byte; |
1748 | 8 | } |
1749 | 1 | else if (EQUAL(pszDataType, "UnsignedLSB2") || |
1750 | 1 | EQUAL(pszDataType, "UnsignedMSB2")) |
1751 | 0 | { |
1752 | 0 | eDT = GDT_UInt16; |
1753 | 0 | } |
1754 | 1 | else if (EQUAL(pszDataType, "UnsignedLSB4") || |
1755 | 1 | EQUAL(pszDataType, "UnsignedMSB4")) |
1756 | 0 | { |
1757 | 0 | eDT = GDT_UInt32; |
1758 | 0 | } |
1759 | | // UnsignedLSB8 and UnsignedMSB8 unhandled |
1760 | 1 | else |
1761 | 1 | { |
1762 | 1 | CPLDebug("PDS4", "data_type = '%s' unhandled", pszDataType); |
1763 | 1 | continue; |
1764 | 1 | } |
1765 | | |
1766 | 11 | poDS->m_osUnits = |
1767 | 11 | CPLGetXMLValue(psSubIter, "Element_Array.unit", ""); |
1768 | | |
1769 | 11 | double dfValueOffset = CPLAtof( |
1770 | 11 | CPLGetXMLValue(psSubIter, "Element_Array.value_offset", "0")); |
1771 | 11 | double dfValueScale = CPLAtof( |
1772 | 11 | CPLGetXMLValue(psSubIter, "Element_Array.scaling_factor", "1")); |
1773 | | |
1774 | | // Parse Axis_Array elements |
1775 | 11 | char szOrder[4] = {0}; |
1776 | 11 | int l_nBands = 1; |
1777 | 11 | int nLines = 0; |
1778 | 11 | int nSamples = 0; |
1779 | 11 | int nAxisFound = 0; |
1780 | 11 | int anElements[3] = {0}; |
1781 | 11 | for (CPLXMLNode *psAxisIter = psSubIter->psChild; |
1782 | 103 | psAxisIter != nullptr; psAxisIter = psAxisIter->psNext) |
1783 | 92 | { |
1784 | 92 | if (psAxisIter->eType != CXT_Element || |
1785 | 92 | strcmp(psAxisIter->pszValue, "Axis_Array") != 0) |
1786 | 60 | { |
1787 | 60 | continue; |
1788 | 60 | } |
1789 | 32 | const char *pszAxisName = |
1790 | 32 | CPLGetXMLValue(psAxisIter, "axis_name", nullptr); |
1791 | 32 | const char *pszElements = |
1792 | 32 | CPLGetXMLValue(psAxisIter, "elements", nullptr); |
1793 | 32 | const char *pszSequenceNumber = |
1794 | 32 | CPLGetXMLValue(psAxisIter, "sequence_number", nullptr); |
1795 | 32 | if (pszAxisName == nullptr || pszElements == nullptr || |
1796 | 32 | pszSequenceNumber == nullptr) |
1797 | 0 | { |
1798 | 0 | continue; |
1799 | 0 | } |
1800 | 32 | int nSeqNumber = atoi(pszSequenceNumber); |
1801 | 32 | if (nSeqNumber < 1 || nSeqNumber > nDIM) |
1802 | 2 | { |
1803 | 2 | CPLError(CE_Warning, CPLE_AppDefined, |
1804 | 2 | "Invalid sequence_number = %s", pszSequenceNumber); |
1805 | 2 | continue; |
1806 | 2 | } |
1807 | 30 | int nElements = atoi(pszElements); |
1808 | 30 | if (nElements <= 0) |
1809 | 0 | { |
1810 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1811 | 0 | "Invalid elements = %s", pszElements); |
1812 | 0 | continue; |
1813 | 0 | } |
1814 | 30 | nSeqNumber--; |
1815 | 30 | if (szOrder[nSeqNumber] != '\0') |
1816 | 0 | { |
1817 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1818 | 0 | "Invalid sequence_number = %s", pszSequenceNumber); |
1819 | 0 | continue; |
1820 | 0 | } |
1821 | 30 | if (EQUAL(pszAxisName, "Band") && nDIM == 3) |
1822 | 10 | { |
1823 | 10 | szOrder[nSeqNumber] = 'B'; |
1824 | 10 | l_nBands = nElements; |
1825 | 10 | anElements[nSeqNumber] = nElements; |
1826 | 10 | nAxisFound++; |
1827 | 10 | } |
1828 | 20 | else if (EQUAL(pszAxisName, "Line")) |
1829 | 11 | { |
1830 | 11 | szOrder[nSeqNumber] = 'L'; |
1831 | 11 | nLines = nElements; |
1832 | 11 | anElements[nSeqNumber] = nElements; |
1833 | 11 | nAxisFound++; |
1834 | 11 | } |
1835 | 9 | else if (EQUAL(pszAxisName, "Sample")) |
1836 | 9 | { |
1837 | 9 | szOrder[nSeqNumber] = 'S'; |
1838 | 9 | nSamples = nElements; |
1839 | 9 | anElements[nSeqNumber] = nElements; |
1840 | 9 | nAxisFound++; |
1841 | 9 | } |
1842 | 0 | else |
1843 | 0 | { |
1844 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
1845 | 0 | "Unsupported axis_name = %s", pszAxisName); |
1846 | 0 | continue; |
1847 | 0 | } |
1848 | 30 | } |
1849 | 11 | if (nAxisFound != nDIM) |
1850 | 2 | { |
1851 | 2 | CPLError(CE_Warning, CPLE_AppDefined, |
1852 | 2 | "Found only %d Axis_Array elements. %d expected", |
1853 | 2 | nAxisFound, nDIM); |
1854 | 2 | continue; |
1855 | 2 | } |
1856 | | |
1857 | 9 | if (!GDALCheckDatasetDimensions(nSamples, nLines) || |
1858 | 9 | !GDALCheckBandCount(l_nBands, FALSE)) |
1859 | 0 | { |
1860 | 0 | continue; |
1861 | 0 | } |
1862 | | |
1863 | | // Compute pixel, line and band spacing |
1864 | 9 | vsi_l_offset nSpacing = GDALGetDataTypeSizeBytes(eDT); |
1865 | 9 | int nPixelOffset = 0; |
1866 | 9 | int nLineOffset = 0; |
1867 | 9 | vsi_l_offset nBandOffset = 0; |
1868 | 9 | int nCountPreviousDim = 1; |
1869 | 35 | for (int i = nDIM - 1; i >= 0; i--) |
1870 | 26 | { |
1871 | 26 | if (szOrder[i] == 'S') |
1872 | 9 | { |
1873 | 9 | if (nSpacing > |
1874 | 9 | static_cast<vsi_l_offset>(INT_MAX / nCountPreviousDim)) |
1875 | 0 | { |
1876 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1877 | 0 | "Integer overflow"); |
1878 | 0 | return nullptr; |
1879 | 0 | } |
1880 | 9 | nPixelOffset = |
1881 | 9 | static_cast<int>(nSpacing * nCountPreviousDim); |
1882 | 9 | nSpacing = nPixelOffset; |
1883 | 9 | } |
1884 | 17 | else if (szOrder[i] == 'L') |
1885 | 9 | { |
1886 | 9 | if (nSpacing > |
1887 | 9 | static_cast<vsi_l_offset>(INT_MAX / nCountPreviousDim)) |
1888 | 0 | { |
1889 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1890 | 0 | "Integer overflow"); |
1891 | 0 | return nullptr; |
1892 | 0 | } |
1893 | 9 | nLineOffset = |
1894 | 9 | static_cast<int>(nSpacing * nCountPreviousDim); |
1895 | 9 | nSpacing = nLineOffset; |
1896 | 9 | } |
1897 | 8 | else |
1898 | 8 | { |
1899 | 8 | nBandOffset = nSpacing * nCountPreviousDim; |
1900 | 8 | nSpacing = nBandOffset; |
1901 | 8 | } |
1902 | 26 | nCountPreviousDim = anElements[i]; |
1903 | 26 | } |
1904 | | |
1905 | | // Retrieve no data value |
1906 | 9 | bool bNoDataSet = false; |
1907 | 9 | double dfNoData = 0.0; |
1908 | 9 | std::vector<double> adfConstants; |
1909 | 9 | CPLXMLNode *psSC = CPLGetXMLNode(psSubIter, "Special_Constants"); |
1910 | 9 | if (psSC) |
1911 | 5 | { |
1912 | 5 | const char *pszMC = |
1913 | 5 | CPLGetXMLValue(psSC, "missing_constant", nullptr); |
1914 | 5 | if (pszMC) |
1915 | 5 | { |
1916 | 5 | bNoDataSet = true; |
1917 | 5 | dfNoData = CPLAtof(pszMC); |
1918 | 5 | } |
1919 | | |
1920 | 5 | const char *apszConstantNames[] = { |
1921 | 5 | "saturated_constant", |
1922 | 5 | "missing_constant", |
1923 | 5 | "error_constant", |
1924 | 5 | "invalid_constant", |
1925 | 5 | "unknown_constant", |
1926 | 5 | "not_applicable_constant", |
1927 | 5 | "high_instrument_saturation", |
1928 | 5 | "high_representation_saturation", |
1929 | 5 | "low_instrument_saturation", |
1930 | 5 | "low_representation_saturation"}; |
1931 | 55 | for (size_t i = 0; i < CPL_ARRAYSIZE(apszConstantNames); i++) |
1932 | 50 | { |
1933 | 50 | const char *pszConstant = |
1934 | 50 | CPLGetXMLValue(psSC, apszConstantNames[i], nullptr); |
1935 | 50 | if (pszConstant) |
1936 | 15 | adfConstants.push_back(CPLAtof(pszConstant)); |
1937 | 50 | } |
1938 | 5 | } |
1939 | | |
1940 | | // Add subdatasets |
1941 | 9 | const int nSDSIdx = 1 + aosSubdatasets.size() / 2; |
1942 | 9 | aosSubdatasets.SetNameValue( |
1943 | 9 | CPLSPrintf("SUBDATASET_%d_NAME", nSDSIdx), |
1944 | 9 | CPLSPrintf("PDS4:%s:%d:%d", osXMLFilename.c_str(), nFAOIdx, |
1945 | 9 | nArrayIdx)); |
1946 | 9 | aosSubdatasets.SetNameValue( |
1947 | 9 | CPLSPrintf("SUBDATASET_%d_DESC", nSDSIdx), |
1948 | 9 | CPLSPrintf("Image file %s, array %s", pszFilename, |
1949 | 9 | pszArrayName ? pszArrayName |
1950 | 9 | : pszArrayId ? pszArrayId |
1951 | 9 | : CPLSPrintf("%d", nArrayIdx))); |
1952 | | |
1953 | 9 | if (poDS->nBands != 0) |
1954 | 0 | continue; |
1955 | | |
1956 | 9 | const std::string osImageFullFilename = CPLFormFilenameSafe( |
1957 | 9 | CPLGetPathSafe(osXMLFilename.c_str()).c_str(), pszFilename, |
1958 | 9 | nullptr); |
1959 | 9 | VSILFILE *fp = VSIFOpenExL( |
1960 | 9 | osImageFullFilename.c_str(), |
1961 | 9 | (poOpenInfo->eAccess == GA_Update) ? "rb+" : "rb", true); |
1962 | 9 | if (fp == nullptr) |
1963 | 9 | { |
1964 | 9 | CPLError(CE_Warning, CPLE_FileIO, "Cannt open %s: %s", |
1965 | 9 | osImageFullFilename.c_str(), VSIGetLastErrorMsg()); |
1966 | 9 | continue; |
1967 | 9 | } |
1968 | 0 | poDS->nRasterXSize = nSamples; |
1969 | 0 | poDS->nRasterYSize = nLines; |
1970 | 0 | poDS->m_osImageFilename = osImageFullFilename; |
1971 | 0 | poDS->m_fpImage = fp; |
1972 | 0 | poDS->m_bIsLSB = bLSBOrder; |
1973 | |
|
1974 | 0 | if (memcmp(szOrder, "BLS", 3) == 0) |
1975 | 0 | { |
1976 | 0 | poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "BAND", |
1977 | 0 | "IMAGE_STRUCTURE"); |
1978 | 0 | } |
1979 | 0 | else if (memcmp(szOrder, "LSB", 3) == 0) |
1980 | 0 | { |
1981 | 0 | poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", |
1982 | 0 | "IMAGE_STRUCTURE"); |
1983 | 0 | } |
1984 | |
|
1985 | 0 | CPLXMLNode *psOS = CPLGetXMLNode(psSubIter, "Object_Statistics"); |
1986 | 0 | const char *pszMin = nullptr; |
1987 | 0 | const char *pszMax = nullptr; |
1988 | 0 | const char *pszMean = nullptr; |
1989 | 0 | const char *pszStdDev = nullptr; |
1990 | 0 | if (psOS) |
1991 | 0 | { |
1992 | 0 | pszMin = CPLGetXMLValue(psOS, "minimum", nullptr); |
1993 | 0 | pszMax = CPLGetXMLValue(psOS, "maximum", nullptr); |
1994 | 0 | pszMean = CPLGetXMLValue(psOS, "mean", nullptr); |
1995 | 0 | pszStdDev = CPLGetXMLValue(psOS, "standard_deviation", nullptr); |
1996 | 0 | } |
1997 | |
|
1998 | 0 | for (int i = 0; i < l_nBands; i++) |
1999 | 0 | { |
2000 | 0 | vsi_l_offset nThisBandOffset = nOffset + nBandOffset * i; |
2001 | 0 | if (bBottomToTop) |
2002 | 0 | { |
2003 | 0 | nThisBandOffset += |
2004 | 0 | static_cast<vsi_l_offset>(nLines - 1) * nLineOffset; |
2005 | 0 | } |
2006 | 0 | if (bRightToLeft) |
2007 | 0 | { |
2008 | 0 | nThisBandOffset += |
2009 | 0 | static_cast<vsi_l_offset>(nSamples - 1) * nPixelOffset; |
2010 | 0 | } |
2011 | 0 | auto poBand = std::make_unique<PDS4RawRasterBand>( |
2012 | 0 | poDS.get(), i + 1, poDS->m_fpImage, nThisBandOffset, |
2013 | 0 | bRightToLeft ? -nPixelOffset : nPixelOffset, |
2014 | 0 | bBottomToTop ? -nLineOffset : nLineOffset, eDT, |
2015 | 0 | bLSBOrder ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN |
2016 | 0 | : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN); |
2017 | 0 | if (!poBand->IsValid()) |
2018 | 0 | return nullptr; |
2019 | 0 | if (bNoDataSet) |
2020 | 0 | { |
2021 | 0 | poBand->SetNoDataValue(dfNoData); |
2022 | 0 | } |
2023 | 0 | poBand->SetOffset(dfValueOffset); |
2024 | 0 | poBand->SetScale(dfValueScale); |
2025 | |
|
2026 | 0 | if (l_nBands == 1) |
2027 | 0 | { |
2028 | 0 | if (pszMin) |
2029 | 0 | { |
2030 | 0 | poBand->GDALRasterBand::SetMetadataItem( |
2031 | 0 | "STATISTICS_MINIMUM", pszMin); |
2032 | 0 | } |
2033 | 0 | if (pszMax) |
2034 | 0 | { |
2035 | 0 | poBand->GDALRasterBand::SetMetadataItem( |
2036 | 0 | "STATISTICS_MAXIMUM", pszMax); |
2037 | 0 | } |
2038 | 0 | if (pszMean) |
2039 | 0 | { |
2040 | 0 | poBand->GDALRasterBand::SetMetadataItem( |
2041 | 0 | "STATISTICS_MEAN", pszMean); |
2042 | 0 | } |
2043 | 0 | if (pszStdDev) |
2044 | 0 | { |
2045 | 0 | poBand->GDALRasterBand::SetMetadataItem( |
2046 | 0 | "STATISTICS_STDDEV", pszStdDev); |
2047 | 0 | } |
2048 | 0 | } |
2049 | | |
2050 | | // Only instantiate explicit mask band if we have at least one |
2051 | | // special constant (that is not the missing_constant, |
2052 | | // already exposed as nodata value) |
2053 | 0 | if (!GDALDataTypeIsComplex(eDT) && |
2054 | 0 | (CPLTestBool(CPLGetConfigOption("PDS4_FORCE_MASK", "NO")) || |
2055 | 0 | adfConstants.size() >= 2 || |
2056 | 0 | (adfConstants.size() == 1 && !bNoDataSet))) |
2057 | 0 | { |
2058 | 0 | poBand->SetMaskBand( |
2059 | 0 | new PDS4MaskBand(poBand.get(), adfConstants)); |
2060 | 0 | } |
2061 | |
|
2062 | 0 | poDS->SetBand(i + 1, std::move(poBand)); |
2063 | 0 | } |
2064 | 0 | } |
2065 | 11 | } |
2066 | | |
2067 | 10 | if (nFAOIdxLookup < 0 && aosSubdatasets.size() > 2) |
2068 | 1 | { |
2069 | 1 | poDS->GDALDataset::SetMetadata(aosSubdatasets.List(), "SUBDATASETS"); |
2070 | 1 | } |
2071 | 9 | else if (poDS->nBands == 0 && |
2072 | 9 | (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 && |
2073 | 9 | (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0) |
2074 | 9 | { |
2075 | 9 | return nullptr; |
2076 | 9 | } |
2077 | 0 | else if (poDS->m_apoLayers.empty() && |
2078 | 0 | (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 && |
2079 | 0 | (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0) |
2080 | 0 | { |
2081 | 0 | return nullptr; |
2082 | 0 | } |
2083 | | |
2084 | | // Expose XML content in xml:PDS4 metadata domain |
2085 | 1 | GByte *pabyRet = nullptr; |
2086 | 1 | CPL_IGNORE_RET_VAL(VSIIngestFile(nullptr, osXMLFilename, &pabyRet, nullptr, |
2087 | 1 | 10 * 1024 * 1024)); |
2088 | 1 | if (pabyRet) |
2089 | 1 | { |
2090 | 1 | char *apszXML[2] = {reinterpret_cast<char *>(pabyRet), nullptr}; |
2091 | 1 | poDS->GDALDataset::SetMetadata(apszXML, "xml:PDS4"); |
2092 | 1 | } |
2093 | 1 | VSIFree(pabyRet); |
2094 | | |
2095 | | /*--------------------------------------------------------------------------*/ |
2096 | | /* Parse georeferencing info */ |
2097 | | /*--------------------------------------------------------------------------*/ |
2098 | 1 | poDS->ReadGeoreferencing(psProduct); |
2099 | | |
2100 | | /*--------------------------------------------------------------------------*/ |
2101 | | /* Check for overviews */ |
2102 | | /*--------------------------------------------------------------------------*/ |
2103 | 1 | poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename); |
2104 | | |
2105 | | /*--------------------------------------------------------------------------*/ |
2106 | | /* Initialize any PAM information */ |
2107 | | /*--------------------------------------------------------------------------*/ |
2108 | 1 | poDS->SetDescription(poOpenInfo->pszFilename); |
2109 | 1 | poDS->TryLoadXML(); |
2110 | | |
2111 | 1 | return poDS.release(); |
2112 | 10 | } |
2113 | | |
2114 | | /************************************************************************/ |
2115 | | /* IsCARTVersionGTE() */ |
2116 | | /************************************************************************/ |
2117 | | |
2118 | | // Returns true is pszCur >= pszRef |
2119 | | // Must be things like 1900, 1B00, 1D00_1933 ... |
2120 | | static bool IsCARTVersionGTE(const char *pszCur, const char *pszRef) |
2121 | 0 | { |
2122 | 0 | return strcmp(pszCur, pszRef) >= 0; |
2123 | 0 | } |
2124 | | |
2125 | | /************************************************************************/ |
2126 | | /* WriteGeoreferencing() */ |
2127 | | /************************************************************************/ |
2128 | | |
2129 | | void PDS4Dataset::WriteGeoreferencing(CPLXMLNode *psCart, |
2130 | | const char *pszCARTVersion) |
2131 | 0 | { |
2132 | 0 | bool bHasBoundingBox = false; |
2133 | 0 | double adfX[4] = {0}; |
2134 | 0 | double adfY[4] = {0}; |
2135 | 0 | CPLString osPrefix; |
2136 | 0 | const char *pszColon = strchr(psCart->pszValue, ':'); |
2137 | 0 | if (pszColon) |
2138 | 0 | osPrefix.assign(psCart->pszValue, pszColon - psCart->pszValue + 1); |
2139 | |
|
2140 | 0 | if (m_bGotTransform) |
2141 | 0 | { |
2142 | 0 | bHasBoundingBox = true; |
2143 | | |
2144 | | // upper left |
2145 | 0 | adfX[0] = m_adfGeoTransform[0]; |
2146 | 0 | adfY[0] = m_adfGeoTransform[3]; |
2147 | | |
2148 | | // upper right |
2149 | 0 | adfX[1] = m_adfGeoTransform[0] + m_adfGeoTransform[1] * nRasterXSize; |
2150 | 0 | adfY[1] = m_adfGeoTransform[3]; |
2151 | | |
2152 | | // lower left |
2153 | 0 | adfX[2] = m_adfGeoTransform[0]; |
2154 | 0 | adfY[2] = m_adfGeoTransform[3] + m_adfGeoTransform[5] * nRasterYSize; |
2155 | | |
2156 | | // lower right |
2157 | 0 | adfX[3] = m_adfGeoTransform[0] + m_adfGeoTransform[1] * nRasterXSize; |
2158 | 0 | adfY[3] = m_adfGeoTransform[3] + m_adfGeoTransform[5] * nRasterYSize; |
2159 | 0 | } |
2160 | 0 | else |
2161 | 0 | { |
2162 | 0 | OGRLayer *poLayer = GetLayer(0); |
2163 | 0 | OGREnvelope sEnvelope; |
2164 | 0 | if (poLayer->GetExtent(&sEnvelope) == OGRERR_NONE) |
2165 | 0 | { |
2166 | 0 | bHasBoundingBox = true; |
2167 | |
|
2168 | 0 | adfX[0] = sEnvelope.MinX; |
2169 | 0 | adfY[0] = sEnvelope.MaxY; |
2170 | |
|
2171 | 0 | adfX[1] = sEnvelope.MaxX; |
2172 | 0 | adfY[1] = sEnvelope.MaxY; |
2173 | |
|
2174 | 0 | adfX[2] = sEnvelope.MinX; |
2175 | 0 | adfY[2] = sEnvelope.MinY; |
2176 | |
|
2177 | 0 | adfX[3] = sEnvelope.MaxX; |
2178 | 0 | adfY[3] = sEnvelope.MinY; |
2179 | 0 | } |
2180 | 0 | } |
2181 | |
|
2182 | 0 | if (bHasBoundingBox && !m_oSRS.IsGeographic()) |
2183 | 0 | { |
2184 | 0 | bHasBoundingBox = false; |
2185 | 0 | OGRSpatialReference *poSRSLongLat = m_oSRS.CloneGeogCS(); |
2186 | 0 | if (poSRSLongLat) |
2187 | 0 | { |
2188 | 0 | poSRSLongLat->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
2189 | 0 | OGRCoordinateTransformation *poCT = |
2190 | 0 | OGRCreateCoordinateTransformation(&m_oSRS, poSRSLongLat); |
2191 | 0 | if (poCT) |
2192 | 0 | { |
2193 | 0 | if (poCT->Transform(4, adfX, adfY)) |
2194 | 0 | { |
2195 | 0 | bHasBoundingBox = true; |
2196 | 0 | } |
2197 | 0 | delete poCT; |
2198 | 0 | } |
2199 | 0 | delete poSRSLongLat; |
2200 | 0 | } |
2201 | 0 | } |
2202 | |
|
2203 | 0 | if (!bHasBoundingBox) |
2204 | 0 | { |
2205 | | // Write dummy values |
2206 | 0 | adfX[0] = -180.0; |
2207 | 0 | adfY[0] = 90.0; |
2208 | 0 | adfX[1] = 180.0; |
2209 | 0 | adfY[1] = 90.0; |
2210 | 0 | adfX[2] = -180.0; |
2211 | 0 | adfY[2] = -90.0; |
2212 | 0 | adfX[3] = 180.0; |
2213 | 0 | adfY[3] = -90.0; |
2214 | 0 | } |
2215 | |
|
2216 | 0 | const char *pszLongitudeDirection = CSLFetchNameValueDef( |
2217 | 0 | m_papszCreationOptions, "LONGITUDE_DIRECTION", "Positive East"); |
2218 | 0 | const double dfLongitudeMultiplier = |
2219 | 0 | EQUAL(pszLongitudeDirection, "Positive West") ? -1 : 1; |
2220 | 0 | const auto FixLong = [dfLongitudeMultiplier](double dfLon) |
2221 | 0 | { return dfLon * dfLongitudeMultiplier; }; |
2222 | | |
2223 | | // Note: starting with CART 1900, Spatial_Domain is actually optional |
2224 | 0 | CPLXMLNode *psSD = CPLCreateXMLNode(psCart, CXT_Element, |
2225 | 0 | (osPrefix + "Spatial_Domain").c_str()); |
2226 | 0 | CPLXMLNode *psBC = CPLCreateXMLNode( |
2227 | 0 | psSD, CXT_Element, (osPrefix + "Bounding_Coordinates").c_str()); |
2228 | |
|
2229 | 0 | const char *pszBoundingDegrees = |
2230 | 0 | CSLFetchNameValue(m_papszCreationOptions, "BOUNDING_DEGREES"); |
2231 | 0 | double dfWest = FixLong( |
2232 | 0 | std::min(std::min(adfX[0], adfX[1]), std::min(adfX[2], adfX[3]))); |
2233 | 0 | double dfEast = FixLong( |
2234 | 0 | std::max(std::max(adfX[0], adfX[1]), std::max(adfX[2], adfX[3]))); |
2235 | 0 | double dfNorth = |
2236 | 0 | std::max(std::max(adfY[0], adfY[1]), std::max(adfY[2], adfY[3])); |
2237 | 0 | double dfSouth = |
2238 | 0 | std::min(std::min(adfY[0], adfY[1]), std::min(adfY[2], adfY[3])); |
2239 | 0 | if (pszBoundingDegrees) |
2240 | 0 | { |
2241 | 0 | char **papszTokens = CSLTokenizeString2(pszBoundingDegrees, ",", 0); |
2242 | 0 | if (CSLCount(papszTokens) == 4) |
2243 | 0 | { |
2244 | 0 | dfWest = CPLAtof(papszTokens[0]); |
2245 | 0 | dfSouth = CPLAtof(papszTokens[1]); |
2246 | 0 | dfEast = CPLAtof(papszTokens[2]); |
2247 | 0 | dfNorth = CPLAtof(papszTokens[3]); |
2248 | 0 | } |
2249 | 0 | CSLDestroy(papszTokens); |
2250 | 0 | } |
2251 | |
|
2252 | 0 | CPLAddXMLAttributeAndValue( |
2253 | 0 | CPLCreateXMLElementAndValue( |
2254 | 0 | psBC, (osPrefix + "west_bounding_coordinate").c_str(), |
2255 | 0 | CPLSPrintf("%.17g", dfWest)), |
2256 | 0 | "unit", "deg"); |
2257 | 0 | CPLAddXMLAttributeAndValue( |
2258 | 0 | CPLCreateXMLElementAndValue( |
2259 | 0 | psBC, (osPrefix + "east_bounding_coordinate").c_str(), |
2260 | 0 | CPLSPrintf("%.17g", dfEast)), |
2261 | 0 | "unit", "deg"); |
2262 | 0 | CPLAddXMLAttributeAndValue( |
2263 | 0 | CPLCreateXMLElementAndValue( |
2264 | 0 | psBC, (osPrefix + "north_bounding_coordinate").c_str(), |
2265 | 0 | CPLSPrintf("%.17g", dfNorth)), |
2266 | 0 | "unit", "deg"); |
2267 | 0 | CPLAddXMLAttributeAndValue( |
2268 | 0 | CPLCreateXMLElementAndValue( |
2269 | 0 | psBC, (osPrefix + "south_bounding_coordinate").c_str(), |
2270 | 0 | CPLSPrintf("%.17g", dfSouth)), |
2271 | 0 | "unit", "deg"); |
2272 | |
|
2273 | 0 | CPLXMLNode *psSRI = |
2274 | 0 | CPLCreateXMLNode(psCart, CXT_Element, |
2275 | 0 | (osPrefix + "Spatial_Reference_Information").c_str()); |
2276 | 0 | CPLXMLNode *psHCSD = CPLCreateXMLNode( |
2277 | 0 | psSRI, CXT_Element, |
2278 | 0 | (osPrefix + "Horizontal_Coordinate_System_Definition").c_str()); |
2279 | |
|
2280 | 0 | double dfUnrotatedULX = m_adfGeoTransform[0]; |
2281 | 0 | double dfUnrotatedULY = m_adfGeoTransform[3]; |
2282 | 0 | double dfUnrotatedResX = m_adfGeoTransform[1]; |
2283 | 0 | double dfUnrotatedResY = m_adfGeoTransform[5]; |
2284 | 0 | double dfMapProjectionRotation = 0.0; |
2285 | 0 | if (m_adfGeoTransform[1] == 0.0 && m_adfGeoTransform[2] > 0.0 && |
2286 | 0 | m_adfGeoTransform[4] > 0.0 && m_adfGeoTransform[5] == 0.0) |
2287 | 0 | { |
2288 | 0 | dfUnrotatedULX = m_adfGeoTransform[3]; |
2289 | 0 | dfUnrotatedULY = -m_adfGeoTransform[0]; |
2290 | 0 | dfUnrotatedResX = m_adfGeoTransform[4]; |
2291 | 0 | dfUnrotatedResY = -m_adfGeoTransform[2]; |
2292 | 0 | dfMapProjectionRotation = 90.0; |
2293 | 0 | } |
2294 | |
|
2295 | 0 | if (GetRasterCount() || m_oSRS.IsProjected()) |
2296 | 0 | { |
2297 | 0 | CPLXMLNode *psPlanar = CPLCreateXMLNode(psHCSD, CXT_Element, |
2298 | 0 | (osPrefix + "Planar").c_str()); |
2299 | 0 | CPLXMLNode *psMP = CPLCreateXMLNode( |
2300 | 0 | psPlanar, CXT_Element, (osPrefix + "Map_Projection").c_str()); |
2301 | 0 | const char *pszProjection = m_oSRS.GetAttrValue("PROJECTION"); |
2302 | 0 | CPLString pszPDS4ProjectionName = ""; |
2303 | 0 | typedef std::pair<const char *, double> ProjParam; |
2304 | 0 | std::vector<ProjParam> aoProjParams; |
2305 | |
|
2306 | 0 | const bool bUse_CART_1933_Or_Later = |
2307 | 0 | IsCARTVersionGTE(pszCARTVersion, "1D00_1933"); |
2308 | |
|
2309 | 0 | const bool bUse_CART_1950_Or_Later = |
2310 | 0 | IsCARTVersionGTE(pszCARTVersion, "1G00_1950"); |
2311 | |
|
2312 | 0 | if (pszProjection == nullptr) |
2313 | 0 | { |
2314 | 0 | pszPDS4ProjectionName = "Equirectangular"; |
2315 | 0 | if (bUse_CART_1933_Or_Later) |
2316 | 0 | { |
2317 | 0 | aoProjParams.push_back( |
2318 | 0 | ProjParam("latitude_of_projection_origin", 0.0)); |
2319 | 0 | aoProjParams.push_back(ProjParam("standard_parallel_1", 0.0)); |
2320 | 0 | aoProjParams.push_back( |
2321 | 0 | ProjParam("longitude_of_central_meridian", 0.0)); |
2322 | 0 | } |
2323 | 0 | else |
2324 | 0 | { |
2325 | 0 | aoProjParams.push_back(ProjParam("standard_parallel_1", 0.0)); |
2326 | 0 | aoProjParams.push_back( |
2327 | 0 | ProjParam("longitude_of_central_meridian", 0.0)); |
2328 | 0 | aoProjParams.push_back( |
2329 | 0 | ProjParam("latitude_of_projection_origin", 0.0)); |
2330 | 0 | } |
2331 | 0 | } |
2332 | | |
2333 | 0 | else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR)) |
2334 | 0 | { |
2335 | 0 | pszPDS4ProjectionName = "Equirectangular"; |
2336 | 0 | if (bUse_CART_1933_Or_Later) |
2337 | 0 | { |
2338 | 0 | aoProjParams.push_back(ProjParam( |
2339 | 0 | "latitude_of_projection_origin", |
2340 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2341 | 0 | aoProjParams.push_back(ProjParam( |
2342 | 0 | "standard_parallel_1", |
2343 | 0 | m_oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 1.0))); |
2344 | 0 | aoProjParams.push_back( |
2345 | 0 | ProjParam("longitude_of_central_meridian", |
2346 | 0 | FixLong(m_oSRS.GetNormProjParm( |
2347 | 0 | SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2348 | 0 | } |
2349 | 0 | else |
2350 | 0 | { |
2351 | 0 | aoProjParams.push_back(ProjParam( |
2352 | 0 | "standard_parallel_1", |
2353 | 0 | m_oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 1.0))); |
2354 | 0 | aoProjParams.push_back( |
2355 | 0 | ProjParam("longitude_of_central_meridian", |
2356 | 0 | FixLong(m_oSRS.GetNormProjParm( |
2357 | 0 | SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2358 | 0 | aoProjParams.push_back(ProjParam( |
2359 | 0 | "latitude_of_projection_origin", |
2360 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2361 | 0 | } |
2362 | 0 | } |
2363 | | |
2364 | 0 | else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP)) |
2365 | 0 | { |
2366 | 0 | pszPDS4ProjectionName = "Lambert Conformal Conic"; |
2367 | 0 | if (bUse_CART_1933_Or_Later) |
2368 | 0 | { |
2369 | 0 | aoProjParams.push_back( |
2370 | 0 | ProjParam("longitude_of_central_meridian", |
2371 | 0 | FixLong(m_oSRS.GetNormProjParm( |
2372 | 0 | SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2373 | 0 | aoProjParams.push_back(ProjParam( |
2374 | 0 | "latitude_of_projection_origin", |
2375 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2376 | 0 | aoProjParams.push_back(ProjParam( |
2377 | 0 | "scale_factor_at_projection_origin", |
2378 | 0 | m_oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0))); |
2379 | 0 | } |
2380 | 0 | else |
2381 | 0 | { |
2382 | 0 | aoProjParams.push_back(ProjParam( |
2383 | 0 | "scale_factor_at_projection_origin", |
2384 | 0 | m_oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0))); |
2385 | 0 | aoProjParams.push_back( |
2386 | 0 | ProjParam("longitude_of_central_meridian", |
2387 | 0 | FixLong(m_oSRS.GetNormProjParm( |
2388 | 0 | SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2389 | 0 | aoProjParams.push_back(ProjParam( |
2390 | 0 | "latitude_of_projection_origin", |
2391 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2392 | 0 | } |
2393 | 0 | } |
2394 | | |
2395 | 0 | else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP)) |
2396 | 0 | { |
2397 | 0 | pszPDS4ProjectionName = "Lambert Conformal Conic"; |
2398 | 0 | aoProjParams.push_back(ProjParam( |
2399 | 0 | "standard_parallel_1", |
2400 | 0 | m_oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0))); |
2401 | 0 | aoProjParams.push_back(ProjParam( |
2402 | 0 | "standard_parallel_2", |
2403 | 0 | m_oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0))); |
2404 | 0 | aoProjParams.push_back(ProjParam( |
2405 | 0 | "longitude_of_central_meridian", |
2406 | 0 | FixLong(m_oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2407 | 0 | aoProjParams.push_back(ProjParam( |
2408 | 0 | "latitude_of_projection_origin", |
2409 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2410 | 0 | } |
2411 | | |
2412 | 0 | else if (EQUAL(pszProjection, |
2413 | 0 | SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER)) |
2414 | 0 | { |
2415 | 0 | pszPDS4ProjectionName = "Oblique Mercator"; |
2416 | | // Proj params defined later |
2417 | 0 | } |
2418 | | |
2419 | 0 | else if (EQUAL(pszProjection, |
2420 | 0 | SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN)) |
2421 | 0 | { |
2422 | 0 | pszPDS4ProjectionName = "Oblique Mercator"; |
2423 | | // Proj params defined later |
2424 | 0 | } |
2425 | | |
2426 | 0 | else if (EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC)) |
2427 | 0 | { |
2428 | 0 | pszPDS4ProjectionName = "Polar Stereographic"; |
2429 | 0 | if (bUse_CART_1950_Or_Later) |
2430 | 0 | { |
2431 | 0 | aoProjParams.push_back( |
2432 | 0 | ProjParam("longitude_of_central_meridian", |
2433 | 0 | FixLong(m_oSRS.GetNormProjParm( |
2434 | 0 | SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2435 | 0 | aoProjParams.push_back(ProjParam( |
2436 | 0 | "latitude_of_projection_origin", |
2437 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2438 | 0 | aoProjParams.push_back(ProjParam( |
2439 | 0 | "scale_factor_at_projection_origin", |
2440 | 0 | m_oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0))); |
2441 | 0 | } |
2442 | 0 | else |
2443 | 0 | { |
2444 | 0 | aoProjParams.push_back( |
2445 | 0 | ProjParam(bUse_CART_1933_Or_Later |
2446 | 0 | ? "longitude_of_central_meridian" |
2447 | 0 | : "straight_vertical_longitude_from_pole", |
2448 | 0 | FixLong(m_oSRS.GetNormProjParm( |
2449 | 0 | SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2450 | 0 | aoProjParams.push_back(ProjParam( |
2451 | 0 | "scale_factor_at_projection_origin", |
2452 | 0 | m_oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0))); |
2453 | 0 | aoProjParams.push_back(ProjParam( |
2454 | 0 | "latitude_of_projection_origin", |
2455 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2456 | 0 | } |
2457 | 0 | } |
2458 | | |
2459 | 0 | else if (EQUAL(pszProjection, SRS_PT_POLYCONIC)) |
2460 | 0 | { |
2461 | 0 | pszPDS4ProjectionName = "Polyconic"; |
2462 | 0 | aoProjParams.push_back(ProjParam( |
2463 | 0 | "longitude_of_central_meridian", |
2464 | 0 | m_oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0))); |
2465 | 0 | aoProjParams.push_back(ProjParam( |
2466 | 0 | "latitude_of_projection_origin", |
2467 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2468 | 0 | } |
2469 | 0 | else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL)) |
2470 | 0 | { |
2471 | 0 | pszPDS4ProjectionName = "Sinusoidal"; |
2472 | 0 | aoProjParams.push_back(ProjParam( |
2473 | 0 | "longitude_of_central_meridian", |
2474 | 0 | FixLong(m_oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2475 | 0 | aoProjParams.push_back(ProjParam( |
2476 | 0 | "latitude_of_projection_origin", |
2477 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2478 | 0 | } |
2479 | | |
2480 | 0 | else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR)) |
2481 | 0 | { |
2482 | 0 | pszPDS4ProjectionName = "Transverse Mercator"; |
2483 | 0 | aoProjParams.push_back( |
2484 | 0 | ProjParam("scale_factor_at_central_meridian", |
2485 | 0 | m_oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0))); |
2486 | 0 | aoProjParams.push_back(ProjParam( |
2487 | 0 | "longitude_of_central_meridian", |
2488 | 0 | m_oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0))); |
2489 | 0 | aoProjParams.push_back(ProjParam( |
2490 | 0 | "latitude_of_projection_origin", |
2491 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2492 | 0 | } |
2493 | 0 | else if (EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC)) |
2494 | 0 | { |
2495 | 0 | pszPDS4ProjectionName = "Orthographic"; |
2496 | 0 | aoProjParams.push_back(ProjParam( |
2497 | 0 | "longitude_of_central_meridian", |
2498 | 0 | FixLong(m_oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2499 | 0 | aoProjParams.push_back(ProjParam( |
2500 | 0 | "latitude_of_projection_origin", |
2501 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2502 | 0 | } |
2503 | | |
2504 | 0 | else if (EQUAL(pszProjection, SRS_PT_MERCATOR_1SP)) |
2505 | 0 | { |
2506 | 0 | pszPDS4ProjectionName = "Mercator"; |
2507 | 0 | aoProjParams.push_back(ProjParam( |
2508 | 0 | "longitude_of_central_meridian", |
2509 | 0 | FixLong(m_oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2510 | 0 | aoProjParams.push_back(ProjParam( |
2511 | 0 | "latitude_of_projection_origin", |
2512 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2513 | 0 | aoProjParams.push_back( |
2514 | 0 | ProjParam("scale_factor_at_projection_origin", |
2515 | 0 | m_oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0))); |
2516 | 0 | } |
2517 | | |
2518 | 0 | else if (EQUAL(pszProjection, SRS_PT_MERCATOR_2SP)) |
2519 | 0 | { |
2520 | 0 | pszPDS4ProjectionName = "Mercator"; |
2521 | 0 | aoProjParams.push_back(ProjParam( |
2522 | 0 | "standard_parallel_1", |
2523 | 0 | m_oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0))); |
2524 | 0 | aoProjParams.push_back(ProjParam( |
2525 | 0 | "longitude_of_central_meridian", |
2526 | 0 | FixLong(m_oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2527 | 0 | aoProjParams.push_back(ProjParam( |
2528 | 0 | "latitude_of_projection_origin", |
2529 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2530 | 0 | } |
2531 | | |
2532 | 0 | else if (EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA)) |
2533 | 0 | { |
2534 | 0 | pszPDS4ProjectionName = "Lambert Azimuthal Equal Area"; |
2535 | 0 | aoProjParams.push_back(ProjParam( |
2536 | 0 | "longitude_of_central_meridian", |
2537 | 0 | FixLong(m_oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)))); |
2538 | 0 | aoProjParams.push_back(ProjParam( |
2539 | 0 | "latitude_of_projection_origin", |
2540 | 0 | m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0))); |
2541 | 0 | } |
2542 | | |
2543 | 0 | else if (EQUAL(pszProjection, "custom_proj4")) |
2544 | 0 | { |
2545 | 0 | const char *pszProj4 = |
2546 | 0 | m_oSRS.GetExtension("PROJCS", "PROJ4", nullptr); |
2547 | 0 | if (pszProj4 && strstr(pszProj4, "+proj=ob_tran") && |
2548 | 0 | strstr(pszProj4, "+o_proj=eqc")) |
2549 | 0 | { |
2550 | 0 | pszPDS4ProjectionName = "Oblique Cylindrical"; |
2551 | 0 | const auto FetchParam = |
2552 | 0 | [](const char *pszProj4Str, const char *pszKey) |
2553 | 0 | { |
2554 | 0 | CPLString needle; |
2555 | 0 | needle.Printf("+%s=", pszKey); |
2556 | 0 | const char *pszVal = strstr(pszProj4Str, needle.c_str()); |
2557 | 0 | if (pszVal) |
2558 | 0 | return CPLAtof(pszVal + needle.size()); |
2559 | 0 | return 0.0; |
2560 | 0 | }; |
2561 | |
|
2562 | 0 | double dfLonP = FetchParam(pszProj4, "o_lon_p"); |
2563 | 0 | double dfLatP = FetchParam(pszProj4, "o_lat_p"); |
2564 | 0 | double dfLon0 = FetchParam(pszProj4, "lon_0"); |
2565 | 0 | double dfPoleRotation = -dfLonP; |
2566 | 0 | double dfPoleLatitude = 180 - dfLatP; |
2567 | 0 | double dfPoleLongitude = dfLon0; |
2568 | |
|
2569 | 0 | aoProjParams.push_back(ProjParam("map_projection_rotation", |
2570 | 0 | dfMapProjectionRotation)); |
2571 | 0 | aoProjParams.push_back( |
2572 | 0 | ProjParam("oblique_proj_pole_latitude", dfPoleLatitude)); |
2573 | 0 | aoProjParams.push_back(ProjParam("oblique_proj_pole_longitude", |
2574 | 0 | FixLong(dfPoleLongitude))); |
2575 | 0 | aoProjParams.push_back( |
2576 | 0 | ProjParam("oblique_proj_pole_rotation", dfPoleRotation)); |
2577 | 0 | } |
2578 | 0 | else |
2579 | 0 | { |
2580 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
2581 | 0 | "Projection %s not supported", pszProjection); |
2582 | 0 | } |
2583 | 0 | } |
2584 | 0 | else |
2585 | 0 | { |
2586 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
2587 | 0 | "Projection %s not supported", pszProjection); |
2588 | 0 | } |
2589 | 0 | CPLCreateXMLElementAndValue(psMP, |
2590 | 0 | (osPrefix + "map_projection_name").c_str(), |
2591 | 0 | pszPDS4ProjectionName); |
2592 | 0 | CPLXMLNode *psProj = CPLCreateXMLNode( |
2593 | 0 | psMP, CXT_Element, |
2594 | 0 | CPLString(osPrefix + pszPDS4ProjectionName).replaceAll(' ', '_')); |
2595 | 0 | for (size_t i = 0; i < aoProjParams.size(); i++) |
2596 | 0 | { |
2597 | 0 | CPLXMLNode *psParam = CPLCreateXMLElementAndValue( |
2598 | 0 | psProj, (osPrefix + aoProjParams[i].first).c_str(), |
2599 | 0 | CPLSPrintf("%.17g", aoProjParams[i].second)); |
2600 | 0 | if (!STARTS_WITH(aoProjParams[i].first, "scale_factor")) |
2601 | 0 | { |
2602 | 0 | CPLAddXMLAttributeAndValue(psParam, "unit", "deg"); |
2603 | 0 | } |
2604 | 0 | } |
2605 | |
|
2606 | 0 | if (pszProjection && |
2607 | 0 | EQUAL(pszProjection, SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER)) |
2608 | 0 | { |
2609 | 0 | CPLXMLNode *psOLA = |
2610 | 0 | CPLCreateXMLNode(nullptr, CXT_Element, |
2611 | 0 | (osPrefix + "Oblique_Line_Azimuth").c_str()); |
2612 | 0 | CPLAddXMLAttributeAndValue( |
2613 | 0 | CPLCreateXMLElementAndValue( |
2614 | 0 | psOLA, (osPrefix + "azimuthal_angle").c_str(), |
2615 | 0 | CPLSPrintf("%.17g", |
2616 | 0 | m_oSRS.GetNormProjParm(SRS_PP_AZIMUTH, 0.0))), |
2617 | 0 | "unit", "deg"); |
2618 | 0 | ; |
2619 | | // Not completely sure of this |
2620 | 0 | CPLAddXMLAttributeAndValue( |
2621 | 0 | CPLCreateXMLElementAndValue( |
2622 | 0 | psOLA, |
2623 | 0 | (osPrefix + "azimuth_measure_point_longitude").c_str(), |
2624 | 0 | CPLSPrintf("%.17g", FixLong(m_oSRS.GetNormProjParm( |
2625 | 0 | SRS_PP_CENTRAL_MERIDIAN, 0.0)))), |
2626 | 0 | "unit", "deg"); |
2627 | |
|
2628 | 0 | if (bUse_CART_1933_Or_Later) |
2629 | 0 | { |
2630 | 0 | CPLAddXMLChild(psProj, psOLA); |
2631 | |
|
2632 | 0 | CPLAddXMLAttributeAndValue( |
2633 | 0 | CPLCreateXMLElementAndValue( |
2634 | 0 | psProj, |
2635 | 0 | (osPrefix + "longitude_of_central_meridian").c_str(), |
2636 | 0 | "0"), |
2637 | 0 | "unit", "deg"); |
2638 | |
|
2639 | 0 | const double dfScaleFactor = |
2640 | 0 | m_oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0); |
2641 | 0 | if (dfScaleFactor != 1.0) |
2642 | 0 | { |
2643 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
2644 | 0 | "Scale factor on initial support = %.17g cannot " |
2645 | 0 | "be encoded in PDS4", |
2646 | 0 | dfScaleFactor); |
2647 | 0 | } |
2648 | 0 | } |
2649 | 0 | else |
2650 | 0 | { |
2651 | 0 | CPLCreateXMLElementAndValue( |
2652 | 0 | psProj, |
2653 | 0 | (osPrefix + "scale_factor_at_projection_origin").c_str(), |
2654 | 0 | CPLSPrintf("%.17g", m_oSRS.GetNormProjParm( |
2655 | 0 | SRS_PP_SCALE_FACTOR, 0.0))); |
2656 | |
|
2657 | 0 | CPLAddXMLChild(psProj, psOLA); |
2658 | 0 | } |
2659 | |
|
2660 | 0 | CPLAddXMLAttributeAndValue( |
2661 | 0 | CPLCreateXMLElementAndValue( |
2662 | 0 | psProj, |
2663 | 0 | (osPrefix + "latitude_of_projection_origin").c_str(), |
2664 | 0 | CPLSPrintf("%.17g", m_oSRS.GetNormProjParm( |
2665 | 0 | SRS_PP_LATITUDE_OF_ORIGIN, 0.0))), |
2666 | 0 | "unit", "deg"); |
2667 | 0 | } |
2668 | 0 | else if (pszProjection && |
2669 | 0 | EQUAL(pszProjection, |
2670 | 0 | SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN)) |
2671 | 0 | { |
2672 | 0 | if (bUse_CART_1933_Or_Later) |
2673 | 0 | { |
2674 | 0 | const double dfScaleFactor = |
2675 | 0 | m_oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0); |
2676 | 0 | if (dfScaleFactor != 1.0) |
2677 | 0 | { |
2678 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
2679 | 0 | "Scale factor on initial support = %.17g cannot " |
2680 | 0 | "be encoded in PDS4", |
2681 | 0 | dfScaleFactor); |
2682 | 0 | } |
2683 | 0 | } |
2684 | 0 | else |
2685 | 0 | { |
2686 | 0 | CPLCreateXMLElementAndValue( |
2687 | 0 | psProj, |
2688 | 0 | (osPrefix + "scale_factor_at_projection_origin").c_str(), |
2689 | 0 | CPLSPrintf("%.17g", m_oSRS.GetNormProjParm( |
2690 | 0 | SRS_PP_SCALE_FACTOR, 0.0))); |
2691 | 0 | } |
2692 | |
|
2693 | 0 | CPLXMLNode *psOLP = CPLCreateXMLNode( |
2694 | 0 | psProj, CXT_Element, (osPrefix + "Oblique_Line_Point").c_str()); |
2695 | 0 | CPLXMLNode *psOLPG1 = CPLCreateXMLNode( |
2696 | 0 | psOLP, CXT_Element, |
2697 | 0 | (osPrefix + "Oblique_Line_Point_Group").c_str()); |
2698 | 0 | CPLAddXMLAttributeAndValue( |
2699 | 0 | CPLCreateXMLElementAndValue( |
2700 | 0 | psOLPG1, (osPrefix + "oblique_line_latitude").c_str(), |
2701 | 0 | CPLSPrintf("%.17g", m_oSRS.GetNormProjParm( |
2702 | 0 | SRS_PP_LATITUDE_OF_POINT_1, 0.0))), |
2703 | 0 | "unit", "deg"); |
2704 | 0 | CPLAddXMLAttributeAndValue( |
2705 | 0 | CPLCreateXMLElementAndValue( |
2706 | 0 | psOLPG1, (osPrefix + "oblique_line_longitude").c_str(), |
2707 | 0 | CPLSPrintf("%.17g", |
2708 | 0 | FixLong(m_oSRS.GetNormProjParm( |
2709 | 0 | SRS_PP_LONGITUDE_OF_POINT_1, 0.0)))), |
2710 | 0 | "unit", "deg"); |
2711 | 0 | CPLXMLNode *psOLPG2 = CPLCreateXMLNode( |
2712 | 0 | psOLP, CXT_Element, |
2713 | 0 | (osPrefix + "Oblique_Line_Point_Group").c_str()); |
2714 | 0 | CPLAddXMLAttributeAndValue( |
2715 | 0 | CPLCreateXMLElementAndValue( |
2716 | 0 | psOLPG2, (osPrefix + "oblique_line_latitude").c_str(), |
2717 | 0 | CPLSPrintf("%.17g", m_oSRS.GetNormProjParm( |
2718 | 0 | SRS_PP_LATITUDE_OF_POINT_2, 0.0))), |
2719 | 0 | "unit", "deg"); |
2720 | 0 | CPLAddXMLAttributeAndValue( |
2721 | 0 | CPLCreateXMLElementAndValue( |
2722 | 0 | psOLPG2, (osPrefix + "oblique_line_longitude").c_str(), |
2723 | 0 | CPLSPrintf("%.17g", m_oSRS.GetNormProjParm( |
2724 | 0 | SRS_PP_LONGITUDE_OF_POINT_2, 0.0))), |
2725 | 0 | "unit", "deg"); |
2726 | |
|
2727 | 0 | if (bUse_CART_1933_Or_Later) |
2728 | 0 | { |
2729 | 0 | CPLAddXMLAttributeAndValue( |
2730 | 0 | CPLCreateXMLElementAndValue( |
2731 | 0 | psProj, |
2732 | 0 | (osPrefix + "longitude_of_central_meridian").c_str(), |
2733 | 0 | "0"), |
2734 | 0 | "unit", "deg"); |
2735 | 0 | } |
2736 | |
|
2737 | 0 | CPLAddXMLAttributeAndValue( |
2738 | 0 | CPLCreateXMLElementAndValue( |
2739 | 0 | psProj, |
2740 | 0 | (osPrefix + "latitude_of_projection_origin").c_str(), |
2741 | 0 | CPLSPrintf("%.17g", FixLong(m_oSRS.GetNormProjParm( |
2742 | 0 | SRS_PP_LATITUDE_OF_ORIGIN, 0.0)))), |
2743 | 0 | "unit", "deg"); |
2744 | 0 | } |
2745 | |
|
2746 | 0 | CPLXMLNode *psCR = nullptr; |
2747 | 0 | if (m_bGotTransform || !IsCARTVersionGTE(pszCARTVersion, "1B00")) |
2748 | 0 | { |
2749 | 0 | CPLXMLNode *psPCI = CPLCreateXMLNode( |
2750 | 0 | psPlanar, CXT_Element, |
2751 | 0 | (osPrefix + "Planar_Coordinate_Information").c_str()); |
2752 | 0 | CPLCreateXMLElementAndValue( |
2753 | 0 | psPCI, (osPrefix + "planar_coordinate_encoding_method").c_str(), |
2754 | 0 | "Coordinate Pair"); |
2755 | 0 | psCR = CPLCreateXMLNode( |
2756 | 0 | psPCI, CXT_Element, |
2757 | 0 | (osPrefix + "Coordinate_Representation").c_str()); |
2758 | 0 | } |
2759 | 0 | const double dfLinearUnits = m_oSRS.GetLinearUnits(); |
2760 | 0 | const double dfDegToMeter = m_oSRS.GetSemiMajor() * M_PI / 180.0; |
2761 | |
|
2762 | 0 | if (psCR == nullptr) |
2763 | 0 | { |
2764 | | // do nothing |
2765 | 0 | } |
2766 | 0 | else if (!m_bGotTransform) |
2767 | 0 | { |
2768 | 0 | CPLAddXMLAttributeAndValue( |
2769 | 0 | CPLCreateXMLElementAndValue( |
2770 | 0 | psCR, (osPrefix + "pixel_resolution_x").c_str(), "0"), |
2771 | 0 | "unit", "m/pixel"); |
2772 | 0 | CPLAddXMLAttributeAndValue( |
2773 | 0 | CPLCreateXMLElementAndValue( |
2774 | 0 | psCR, (osPrefix + "pixel_resolution_y").c_str(), "0"), |
2775 | 0 | "unit", "m/pixel"); |
2776 | 0 | CPLAddXMLAttributeAndValue( |
2777 | 0 | CPLCreateXMLElementAndValue( |
2778 | 0 | psCR, (osPrefix + "pixel_scale_x").c_str(), "0"), |
2779 | 0 | "unit", "pixel/deg"); |
2780 | 0 | CPLAddXMLAttributeAndValue( |
2781 | 0 | CPLCreateXMLElementAndValue( |
2782 | 0 | psCR, (osPrefix + "pixel_scale_y").c_str(), "0"), |
2783 | 0 | "unit", "pixel/deg"); |
2784 | 0 | } |
2785 | 0 | else if (m_oSRS.IsGeographic()) |
2786 | 0 | { |
2787 | 0 | CPLAddXMLAttributeAndValue( |
2788 | 0 | CPLCreateXMLElementAndValue( |
2789 | 0 | psCR, (osPrefix + "pixel_resolution_x").c_str(), |
2790 | 0 | CPLSPrintf("%.17g", dfUnrotatedResX * dfDegToMeter)), |
2791 | 0 | "unit", "m/pixel"); |
2792 | 0 | CPLAddXMLAttributeAndValue( |
2793 | 0 | CPLCreateXMLElementAndValue( |
2794 | 0 | psCR, (osPrefix + "pixel_resolution_y").c_str(), |
2795 | 0 | CPLSPrintf("%.17g", -dfUnrotatedResY * dfDegToMeter)), |
2796 | 0 | "unit", "m/pixel"); |
2797 | 0 | CPLAddXMLAttributeAndValue( |
2798 | 0 | CPLCreateXMLElementAndValue( |
2799 | 0 | psCR, (osPrefix + "pixel_scale_x").c_str(), |
2800 | 0 | CPLSPrintf("%.17g", 1.0 / (dfUnrotatedResX))), |
2801 | 0 | "unit", "pixel/deg"); |
2802 | 0 | CPLAddXMLAttributeAndValue( |
2803 | 0 | CPLCreateXMLElementAndValue( |
2804 | 0 | psCR, (osPrefix + "pixel_scale_y").c_str(), |
2805 | 0 | CPLSPrintf("%.17g", 1.0 / (-dfUnrotatedResY))), |
2806 | 0 | "unit", "pixel/deg"); |
2807 | 0 | } |
2808 | 0 | else if (m_oSRS.IsProjected()) |
2809 | 0 | { |
2810 | 0 | CPLAddXMLAttributeAndValue( |
2811 | 0 | CPLCreateXMLElementAndValue( |
2812 | 0 | psCR, (osPrefix + "pixel_resolution_x").c_str(), |
2813 | 0 | CPLSPrintf("%.17g", dfUnrotatedResX * dfLinearUnits)), |
2814 | 0 | "unit", "m/pixel"); |
2815 | 0 | CPLAddXMLAttributeAndValue( |
2816 | 0 | CPLCreateXMLElementAndValue( |
2817 | 0 | psCR, (osPrefix + "pixel_resolution_y").c_str(), |
2818 | 0 | CPLSPrintf("%.17g", -dfUnrotatedResY * dfLinearUnits)), |
2819 | 0 | "unit", "m/pixel"); |
2820 | 0 | CPLAddXMLAttributeAndValue( |
2821 | 0 | CPLCreateXMLElementAndValue( |
2822 | 0 | psCR, (osPrefix + "pixel_scale_x").c_str(), |
2823 | 0 | CPLSPrintf("%.17g", dfDegToMeter / |
2824 | 0 | (dfUnrotatedResX * dfLinearUnits))), |
2825 | 0 | "unit", "pixel/deg"); |
2826 | 0 | CPLAddXMLAttributeAndValue( |
2827 | 0 | CPLCreateXMLElementAndValue( |
2828 | 0 | psCR, (osPrefix + "pixel_scale_y").c_str(), |
2829 | 0 | CPLSPrintf("%.17g", dfDegToMeter / (-dfUnrotatedResY * |
2830 | 0 | dfLinearUnits))), |
2831 | 0 | "unit", "pixel/deg"); |
2832 | 0 | } |
2833 | |
|
2834 | 0 | if (m_bGotTransform) |
2835 | 0 | { |
2836 | 0 | CPLXMLNode *psGT = |
2837 | 0 | CPLCreateXMLNode(psPlanar, CXT_Element, |
2838 | 0 | (osPrefix + "Geo_Transformation").c_str()); |
2839 | 0 | const double dfFalseEasting = |
2840 | 0 | m_oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0); |
2841 | 0 | const double dfFalseNorthing = |
2842 | 0 | m_oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0); |
2843 | 0 | const double dfULX = -dfFalseEasting + dfUnrotatedULX; |
2844 | 0 | const double dfULY = -dfFalseNorthing + dfUnrotatedULY; |
2845 | 0 | if (m_oSRS.IsGeographic()) |
2846 | 0 | { |
2847 | 0 | CPLAddXMLAttributeAndValue( |
2848 | 0 | CPLCreateXMLElementAndValue( |
2849 | 0 | psGT, (osPrefix + "upperleft_corner_x").c_str(), |
2850 | 0 | CPLSPrintf("%.17g", dfULX * dfDegToMeter)), |
2851 | 0 | "unit", "m"); |
2852 | 0 | CPLAddXMLAttributeAndValue( |
2853 | 0 | CPLCreateXMLElementAndValue( |
2854 | 0 | psGT, (osPrefix + "upperleft_corner_y").c_str(), |
2855 | 0 | CPLSPrintf("%.17g", dfULY * dfDegToMeter)), |
2856 | 0 | "unit", "m"); |
2857 | 0 | } |
2858 | 0 | else if (m_oSRS.IsProjected()) |
2859 | 0 | { |
2860 | 0 | CPLAddXMLAttributeAndValue( |
2861 | 0 | CPLCreateXMLElementAndValue( |
2862 | 0 | psGT, (osPrefix + "upperleft_corner_x").c_str(), |
2863 | 0 | CPLSPrintf("%.17g", dfULX * dfLinearUnits)), |
2864 | 0 | "unit", "m"); |
2865 | 0 | CPLAddXMLAttributeAndValue( |
2866 | 0 | CPLCreateXMLElementAndValue( |
2867 | 0 | psGT, (osPrefix + "upperleft_corner_y").c_str(), |
2868 | 0 | CPLSPrintf("%.17g", dfULY * dfLinearUnits)), |
2869 | 0 | "unit", "m"); |
2870 | 0 | } |
2871 | 0 | } |
2872 | 0 | } |
2873 | 0 | else |
2874 | 0 | { |
2875 | 0 | CPLXMLNode *psGeographic = CPLCreateXMLNode( |
2876 | 0 | psHCSD, CXT_Element, (osPrefix + "Geographic").c_str()); |
2877 | 0 | if (!IsCARTVersionGTE(pszCARTVersion, "1B00")) |
2878 | 0 | { |
2879 | 0 | CPLAddXMLAttributeAndValue( |
2880 | 0 | CPLCreateXMLElementAndValue( |
2881 | 0 | psGeographic, (osPrefix + "latitude_resolution").c_str(), |
2882 | 0 | "0"), |
2883 | 0 | "unit", "deg"); |
2884 | 0 | CPLAddXMLAttributeAndValue( |
2885 | 0 | CPLCreateXMLElementAndValue( |
2886 | 0 | psGeographic, (osPrefix + "longitude_resolution").c_str(), |
2887 | 0 | "0"), |
2888 | 0 | "unit", "deg"); |
2889 | 0 | } |
2890 | 0 | } |
2891 | |
|
2892 | 0 | CPLXMLNode *psGM = CPLCreateXMLNode(psHCSD, CXT_Element, |
2893 | 0 | (osPrefix + "Geodetic_Model").c_str()); |
2894 | 0 | const char *pszLatitudeType = CSLFetchNameValueDef( |
2895 | 0 | m_papszCreationOptions, "LATITUDE_TYPE", "Planetocentric"); |
2896 | | // Fix case |
2897 | 0 | if (EQUAL(pszLatitudeType, "Planetocentric")) |
2898 | 0 | pszLatitudeType = "Planetocentric"; |
2899 | 0 | else if (EQUAL(pszLatitudeType, "Planetographic")) |
2900 | 0 | pszLatitudeType = "Planetographic"; |
2901 | 0 | CPLCreateXMLElementAndValue(psGM, (osPrefix + "latitude_type").c_str(), |
2902 | 0 | pszLatitudeType); |
2903 | |
|
2904 | 0 | const char *pszDatum = m_oSRS.GetAttrValue("DATUM"); |
2905 | 0 | if (pszDatum && STARTS_WITH(pszDatum, "D_")) |
2906 | 0 | { |
2907 | 0 | CPLCreateXMLElementAndValue(psGM, (osPrefix + "spheroid_name").c_str(), |
2908 | 0 | pszDatum + 2); |
2909 | 0 | } |
2910 | 0 | else if (pszDatum) |
2911 | 0 | { |
2912 | 0 | CPLCreateXMLElementAndValue(psGM, (osPrefix + "spheroid_name").c_str(), |
2913 | 0 | pszDatum); |
2914 | 0 | } |
2915 | |
|
2916 | 0 | double dfSemiMajor = m_oSRS.GetSemiMajor(); |
2917 | 0 | double dfSemiMinor = m_oSRS.GetSemiMinor(); |
2918 | 0 | const char *pszRadii = CSLFetchNameValue(m_papszCreationOptions, "RADII"); |
2919 | 0 | if (pszRadii) |
2920 | 0 | { |
2921 | 0 | char **papszTokens = CSLTokenizeString2(pszRadii, " ,", 0); |
2922 | 0 | if (CSLCount(papszTokens) == 2) |
2923 | 0 | { |
2924 | 0 | dfSemiMajor = CPLAtof(papszTokens[0]); |
2925 | 0 | dfSemiMinor = CPLAtof(papszTokens[1]); |
2926 | 0 | } |
2927 | 0 | CSLDestroy(papszTokens); |
2928 | 0 | } |
2929 | |
|
2930 | 0 | const bool bUseLDD1930RadiusNames = |
2931 | 0 | IsCARTVersionGTE(pszCARTVersion, "1B10_1930"); |
2932 | |
|
2933 | 0 | CPLAddXMLAttributeAndValue( |
2934 | 0 | CPLCreateXMLElementAndValue( |
2935 | 0 | psGM, |
2936 | 0 | (osPrefix + |
2937 | 0 | (bUseLDD1930RadiusNames ? "a_axis_radius" : "semi_major_radius")) |
2938 | 0 | .c_str(), |
2939 | 0 | CPLSPrintf("%.17g", dfSemiMajor)), |
2940 | 0 | "unit", "m"); |
2941 | | // No, this is not a bug. The PDS4 b_axis_radius/semi_minor_radius is the |
2942 | | // minor radius on the equatorial plane. Which in WKT doesn't really exist, |
2943 | | // so reuse the WKT semi major |
2944 | 0 | CPLAddXMLAttributeAndValue( |
2945 | 0 | CPLCreateXMLElementAndValue( |
2946 | 0 | psGM, |
2947 | 0 | (osPrefix + |
2948 | 0 | (bUseLDD1930RadiusNames ? "b_axis_radius" : "semi_minor_radius")) |
2949 | 0 | .c_str(), |
2950 | 0 | CPLSPrintf("%.17g", dfSemiMajor)), |
2951 | 0 | "unit", "m"); |
2952 | 0 | CPLAddXMLAttributeAndValue( |
2953 | 0 | CPLCreateXMLElementAndValue( |
2954 | 0 | psGM, |
2955 | 0 | (osPrefix + |
2956 | 0 | (bUseLDD1930RadiusNames ? "c_axis_radius" : "polar_radius")) |
2957 | 0 | .c_str(), |
2958 | 0 | CPLSPrintf("%.17g", dfSemiMinor)), |
2959 | 0 | "unit", "m"); |
2960 | | |
2961 | | // Fix case |
2962 | 0 | if (EQUAL(pszLongitudeDirection, "Positive East")) |
2963 | 0 | pszLongitudeDirection = "Positive East"; |
2964 | 0 | else if (EQUAL(pszLongitudeDirection, "Positive West")) |
2965 | 0 | pszLongitudeDirection = "Positive West"; |
2966 | 0 | CPLCreateXMLElementAndValue(psGM, |
2967 | 0 | (osPrefix + "longitude_direction").c_str(), |
2968 | 0 | pszLongitudeDirection); |
2969 | 0 | } |
2970 | | |
2971 | | /************************************************************************/ |
2972 | | /* SubstituteVariables() */ |
2973 | | /************************************************************************/ |
2974 | | |
2975 | | void PDS4Dataset::SubstituteVariables(CPLXMLNode *psNode, char **papszDict) |
2976 | 0 | { |
2977 | 0 | if (psNode->eType == CXT_Text && psNode->pszValue && |
2978 | 0 | strstr(psNode->pszValue, "${")) |
2979 | 0 | { |
2980 | 0 | CPLString osVal(psNode->pszValue); |
2981 | |
|
2982 | 0 | if (strstr(psNode->pszValue, "${TITLE}") != nullptr && |
2983 | 0 | CSLFetchNameValue(papszDict, "VAR_TITLE") == nullptr) |
2984 | 0 | { |
2985 | 0 | const CPLString osTitle(CPLGetFilename(GetDescription())); |
2986 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
2987 | 0 | "VAR_TITLE not defined. Using %s by default", |
2988 | 0 | osTitle.c_str()); |
2989 | 0 | osVal.replaceAll("${TITLE}", osTitle); |
2990 | 0 | } |
2991 | |
|
2992 | 0 | for (char **papszIter = papszDict; papszIter && *papszIter; papszIter++) |
2993 | 0 | { |
2994 | 0 | if (STARTS_WITH_CI(*papszIter, "VAR_")) |
2995 | 0 | { |
2996 | 0 | char *pszKey = nullptr; |
2997 | 0 | const char *pszValue = CPLParseNameValue(*papszIter, &pszKey); |
2998 | 0 | if (pszKey && pszValue) |
2999 | 0 | { |
3000 | 0 | const char *pszVarName = pszKey + strlen("VAR_"); |
3001 | 0 | osVal.replaceAll( |
3002 | 0 | (CPLString("${") + pszVarName + "}").c_str(), pszValue); |
3003 | 0 | osVal.replaceAll( |
3004 | 0 | CPLString(CPLString("${") + pszVarName + "}") |
3005 | 0 | .tolower() |
3006 | 0 | .c_str(), |
3007 | 0 | CPLString(pszValue).tolower()); |
3008 | 0 | CPLFree(pszKey); |
3009 | 0 | } |
3010 | 0 | } |
3011 | 0 | } |
3012 | 0 | if (osVal.find("${") != std::string::npos) |
3013 | 0 | { |
3014 | 0 | CPLError(CE_Warning, CPLE_AppDefined, "%s could not be substituted", |
3015 | 0 | osVal.c_str()); |
3016 | 0 | } |
3017 | 0 | CPLFree(psNode->pszValue); |
3018 | 0 | psNode->pszValue = CPLStrdup(osVal); |
3019 | 0 | } |
3020 | |
|
3021 | 0 | for (CPLXMLNode *psIter = psNode->psChild; psIter; psIter = psIter->psNext) |
3022 | 0 | { |
3023 | 0 | SubstituteVariables(psIter, papszDict); |
3024 | 0 | } |
3025 | 0 | } |
3026 | | |
3027 | | /************************************************************************/ |
3028 | | /* InitImageFile() */ |
3029 | | /************************************************************************/ |
3030 | | |
3031 | | bool PDS4Dataset::InitImageFile() |
3032 | 0 | { |
3033 | 0 | m_bMustInitImageFile = false; |
3034 | |
|
3035 | 0 | if (m_poExternalDS) |
3036 | 0 | { |
3037 | 0 | int nBlockXSize, nBlockYSize; |
3038 | 0 | GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize); |
3039 | 0 | const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType(); |
3040 | 0 | const int nDTSize = GDALGetDataTypeSizeBytes(eDT); |
3041 | 0 | const int nBlockSizeBytes = nBlockXSize * nBlockYSize * nDTSize; |
3042 | 0 | const int l_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize); |
3043 | |
|
3044 | 0 | int bHasNoData = FALSE; |
3045 | 0 | double dfNoData = GetRasterBand(1)->GetNoDataValue(&bHasNoData); |
3046 | 0 | if (!bHasNoData) |
3047 | 0 | dfNoData = 0; |
3048 | |
|
3049 | 0 | if (nBands == 1 || EQUAL(m_osInterleave, "BSQ")) |
3050 | 0 | { |
3051 | | // We need to make sure that blocks are written in the right order |
3052 | 0 | for (int i = 0; i < nBands; i++) |
3053 | 0 | { |
3054 | 0 | if (m_poExternalDS->GetRasterBand(i + 1)->Fill(dfNoData) != |
3055 | 0 | CE_None) |
3056 | 0 | { |
3057 | 0 | return false; |
3058 | 0 | } |
3059 | 0 | } |
3060 | 0 | m_poExternalDS->FlushCache(false); |
3061 | | |
3062 | | // Check that blocks are effectively written in expected order. |
3063 | 0 | GIntBig nLastOffset = 0; |
3064 | 0 | for (int i = 0; i < nBands; i++) |
3065 | 0 | { |
3066 | 0 | for (int y = 0; y < l_nBlocksPerColumn; y++) |
3067 | 0 | { |
3068 | 0 | const char *pszBlockOffset = |
3069 | 0 | m_poExternalDS->GetRasterBand(i + 1)->GetMetadataItem( |
3070 | 0 | CPLSPrintf("BLOCK_OFFSET_%d_%d", 0, y), "TIFF"); |
3071 | 0 | if (pszBlockOffset) |
3072 | 0 | { |
3073 | 0 | GIntBig nOffset = CPLAtoGIntBig(pszBlockOffset); |
3074 | 0 | if (i != 0 || y != 0) |
3075 | 0 | { |
3076 | 0 | if (nOffset != nLastOffset + nBlockSizeBytes) |
3077 | 0 | { |
3078 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3079 | 0 | "Block %d,%d band %d not at expected " |
3080 | 0 | "offset", |
3081 | 0 | 0, y, i + 1); |
3082 | 0 | return false; |
3083 | 0 | } |
3084 | 0 | } |
3085 | 0 | nLastOffset = nOffset; |
3086 | 0 | } |
3087 | 0 | else |
3088 | 0 | { |
3089 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3090 | 0 | "Block %d,%d band %d not at expected " |
3091 | 0 | "offset", |
3092 | 0 | 0, y, i + 1); |
3093 | 0 | return false; |
3094 | 0 | } |
3095 | 0 | } |
3096 | 0 | } |
3097 | 0 | } |
3098 | 0 | else |
3099 | 0 | { |
3100 | 0 | void *pBlockData = VSI_MALLOC_VERBOSE(nBlockSizeBytes); |
3101 | 0 | if (pBlockData == nullptr) |
3102 | 0 | return false; |
3103 | 0 | GDALCopyWords(&dfNoData, GDT_Float64, 0, pBlockData, eDT, nDTSize, |
3104 | 0 | nBlockXSize * nBlockYSize); |
3105 | 0 | for (int y = 0; y < l_nBlocksPerColumn; y++) |
3106 | 0 | { |
3107 | 0 | for (int i = 0; i < nBands; i++) |
3108 | 0 | { |
3109 | 0 | if (m_poExternalDS->GetRasterBand(i + 1)->WriteBlock( |
3110 | 0 | 0, y, pBlockData) != CE_None) |
3111 | 0 | { |
3112 | 0 | VSIFree(pBlockData); |
3113 | 0 | return false; |
3114 | 0 | } |
3115 | 0 | } |
3116 | 0 | } |
3117 | 0 | VSIFree(pBlockData); |
3118 | 0 | m_poExternalDS->FlushCache(false); |
3119 | | |
3120 | | // Check that blocks are effectively written in expected order. |
3121 | 0 | GIntBig nLastOffset = 0; |
3122 | 0 | for (int y = 0; y < l_nBlocksPerColumn; y++) |
3123 | 0 | { |
3124 | 0 | const char *pszBlockOffset = |
3125 | 0 | m_poExternalDS->GetRasterBand(1)->GetMetadataItem( |
3126 | 0 | CPLSPrintf("BLOCK_OFFSET_%d_%d", 0, y), "TIFF"); |
3127 | 0 | if (pszBlockOffset) |
3128 | 0 | { |
3129 | 0 | GIntBig nOffset = CPLAtoGIntBig(pszBlockOffset); |
3130 | 0 | if (y != 0) |
3131 | 0 | { |
3132 | 0 | if (nOffset != |
3133 | 0 | nLastOffset + |
3134 | 0 | static_cast<GIntBig>(nBlockSizeBytes) * nBands) |
3135 | 0 | { |
3136 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3137 | 0 | "Block %d,%d not at expected " |
3138 | 0 | "offset", |
3139 | 0 | 0, y); |
3140 | 0 | return false; |
3141 | 0 | } |
3142 | 0 | } |
3143 | 0 | nLastOffset = nOffset; |
3144 | 0 | } |
3145 | 0 | else |
3146 | 0 | { |
3147 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3148 | 0 | "Block %d,%d not at expected " |
3149 | 0 | "offset", |
3150 | 0 | 0, y); |
3151 | 0 | return false; |
3152 | 0 | } |
3153 | 0 | } |
3154 | 0 | } |
3155 | | |
3156 | 0 | return true; |
3157 | 0 | } |
3158 | | |
3159 | 0 | int bHasNoData = FALSE; |
3160 | 0 | const double dfNoData = GetRasterBand(1)->GetNoDataValue(&bHasNoData); |
3161 | 0 | const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType(); |
3162 | 0 | const int nDTSize = GDALGetDataTypeSizeBytes(eDT); |
3163 | 0 | const vsi_l_offset nFileSize = static_cast<vsi_l_offset>(nRasterXSize) * |
3164 | 0 | nRasterYSize * nBands * nDTSize; |
3165 | 0 | if (dfNoData == 0 || !bHasNoData) |
3166 | 0 | { |
3167 | 0 | if (VSIFTruncateL(m_fpImage, nFileSize) != 0) |
3168 | 0 | { |
3169 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
3170 | 0 | "Cannot create file of size " CPL_FRMT_GUIB " bytes", |
3171 | 0 | nFileSize); |
3172 | 0 | return false; |
3173 | 0 | } |
3174 | 0 | } |
3175 | 0 | else |
3176 | 0 | { |
3177 | 0 | size_t nLineSize = static_cast<size_t>(nRasterXSize) * nDTSize; |
3178 | 0 | void *pData = VSI_MALLOC_VERBOSE(nLineSize); |
3179 | 0 | if (pData == nullptr) |
3180 | 0 | return false; |
3181 | 0 | GDALCopyWords(&dfNoData, GDT_Float64, 0, pData, eDT, nDTSize, |
3182 | 0 | nRasterXSize); |
3183 | | #ifdef CPL_MSB |
3184 | | if (GDALDataTypeIsComplex(eDT)) |
3185 | | { |
3186 | | GDALSwapWords(pData, nDTSize / 2, nRasterXSize * 2, nDTSize / 2); |
3187 | | } |
3188 | | else |
3189 | | { |
3190 | | GDALSwapWords(pData, nDTSize, nRasterXSize, nDTSize); |
3191 | | } |
3192 | | #endif |
3193 | 0 | for (vsi_l_offset i = 0; |
3194 | 0 | i < static_cast<vsi_l_offset>(nRasterYSize) * nBands; i++) |
3195 | 0 | { |
3196 | 0 | size_t nBytesWritten = VSIFWriteL(pData, 1, nLineSize, m_fpImage); |
3197 | 0 | if (nBytesWritten != nLineSize) |
3198 | 0 | { |
3199 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
3200 | 0 | "Cannot create file of size " CPL_FRMT_GUIB " bytes", |
3201 | 0 | nFileSize); |
3202 | 0 | VSIFree(pData); |
3203 | 0 | return false; |
3204 | 0 | } |
3205 | 0 | } |
3206 | 0 | VSIFree(pData); |
3207 | 0 | } |
3208 | 0 | return true; |
3209 | 0 | } |
3210 | | |
3211 | | /************************************************************************/ |
3212 | | /* GetSpecialConstants() */ |
3213 | | /************************************************************************/ |
3214 | | |
3215 | | static CPLXMLNode *GetSpecialConstants(const CPLString &osPrefix, |
3216 | | CPLXMLNode *psFileAreaObservational) |
3217 | 0 | { |
3218 | 0 | for (CPLXMLNode *psIter = psFileAreaObservational->psChild; psIter; |
3219 | 0 | psIter = psIter->psNext) |
3220 | 0 | { |
3221 | 0 | if (psIter->eType == CXT_Element && |
3222 | 0 | STARTS_WITH(psIter->pszValue, (osPrefix + "Array").c_str())) |
3223 | 0 | { |
3224 | 0 | CPLXMLNode *psSC = |
3225 | 0 | CPLGetXMLNode(psIter, (osPrefix + "Special_Constants").c_str()); |
3226 | 0 | if (psSC) |
3227 | 0 | { |
3228 | 0 | CPLXMLNode *psNext = psSC->psNext; |
3229 | 0 | psSC->psNext = nullptr; |
3230 | 0 | CPLXMLNode *psRet = CPLCloneXMLTree(psSC); |
3231 | 0 | psSC->psNext = psNext; |
3232 | 0 | return psRet; |
3233 | 0 | } |
3234 | 0 | } |
3235 | 0 | } |
3236 | 0 | return nullptr; |
3237 | 0 | } |
3238 | | |
3239 | | /************************************************************************/ |
3240 | | /* WriteHeaderAppendCase() */ |
3241 | | /************************************************************************/ |
3242 | | |
3243 | | void PDS4Dataset::WriteHeaderAppendCase() |
3244 | 0 | { |
3245 | 0 | CPLXMLTreeCloser oCloser(CPLParseXMLFile(GetDescription())); |
3246 | 0 | CPLXMLNode *psRoot = oCloser.get(); |
3247 | 0 | if (psRoot == nullptr) |
3248 | 0 | return; |
3249 | 0 | CPLString osPrefix; |
3250 | 0 | CPLXMLNode *psProduct = CPLGetXMLNode(psRoot, "=Product_Observational"); |
3251 | 0 | if (psProduct == nullptr) |
3252 | 0 | { |
3253 | 0 | psProduct = CPLGetXMLNode(psRoot, "=pds:Product_Observational"); |
3254 | 0 | if (psProduct) |
3255 | 0 | osPrefix = "pds:"; |
3256 | 0 | } |
3257 | 0 | if (psProduct == nullptr) |
3258 | 0 | { |
3259 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3260 | 0 | "Cannot find Product_Observational element"); |
3261 | 0 | return; |
3262 | 0 | } |
3263 | 0 | CPLXMLNode *psFAO = CPLGetXMLNode( |
3264 | 0 | psProduct, (osPrefix + "File_Area_Observational").c_str()); |
3265 | 0 | if (psFAO == nullptr) |
3266 | 0 | { |
3267 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3268 | 0 | "Cannot find File_Area_Observational element"); |
3269 | 0 | return; |
3270 | 0 | } |
3271 | | |
3272 | 0 | WriteArray(osPrefix, psFAO, nullptr, nullptr); |
3273 | |
|
3274 | 0 | CPLSerializeXMLTreeToFile(psRoot, GetDescription()); |
3275 | 0 | } |
3276 | | |
3277 | | /************************************************************************/ |
3278 | | /* WriteArray() */ |
3279 | | /************************************************************************/ |
3280 | | |
3281 | | void PDS4Dataset::WriteArray(const CPLString &osPrefix, CPLXMLNode *psFAO, |
3282 | | const char *pszLocalIdentifierDefault, |
3283 | | CPLXMLNode *psTemplateSpecialConstants) |
3284 | 0 | { |
3285 | 0 | const char *pszArrayType = CSLFetchNameValueDef( |
3286 | 0 | m_papszCreationOptions, "ARRAY_TYPE", "Array_3D_Image"); |
3287 | 0 | const bool bIsArray2D = STARTS_WITH(pszArrayType, "Array_2D"); |
3288 | 0 | CPLXMLNode *psArray = |
3289 | 0 | CPLCreateXMLNode(psFAO, CXT_Element, (osPrefix + pszArrayType).c_str()); |
3290 | |
|
3291 | 0 | const char *pszLocalIdentifier = CSLFetchNameValueDef( |
3292 | 0 | m_papszCreationOptions, "ARRAY_IDENTIFIER", pszLocalIdentifierDefault); |
3293 | 0 | if (pszLocalIdentifier) |
3294 | 0 | { |
3295 | 0 | CPLCreateXMLElementAndValue(psArray, |
3296 | 0 | (osPrefix + "local_identifier").c_str(), |
3297 | 0 | pszLocalIdentifier); |
3298 | 0 | } |
3299 | |
|
3300 | 0 | GUIntBig nOffset = m_nBaseOffset; |
3301 | 0 | if (m_poExternalDS) |
3302 | 0 | { |
3303 | 0 | const char *pszOffset = |
3304 | 0 | m_poExternalDS->GetRasterBand(1)->GetMetadataItem( |
3305 | 0 | "BLOCK_OFFSET_0_0", "TIFF"); |
3306 | 0 | if (pszOffset) |
3307 | 0 | nOffset = CPLAtoGIntBig(pszOffset); |
3308 | 0 | } |
3309 | 0 | CPLAddXMLAttributeAndValue( |
3310 | 0 | CPLCreateXMLElementAndValue(psArray, (osPrefix + "offset").c_str(), |
3311 | 0 | CPLSPrintf(CPL_FRMT_GUIB, nOffset)), |
3312 | 0 | "unit", "byte"); |
3313 | 0 | CPLCreateXMLElementAndValue(psArray, (osPrefix + "axes").c_str(), |
3314 | 0 | (bIsArray2D) ? "2" : "3"); |
3315 | 0 | CPLCreateXMLElementAndValue( |
3316 | 0 | psArray, (osPrefix + "axis_index_order").c_str(), "Last Index Fastest"); |
3317 | 0 | CPLXMLNode *psElementArray = CPLCreateXMLNode( |
3318 | 0 | psArray, CXT_Element, (osPrefix + "Element_Array").c_str()); |
3319 | 0 | GDALDataType eDT = GetRasterBand(1)->GetRasterDataType(); |
3320 | 0 | const char *pszDataType = |
3321 | 0 | (eDT == GDT_Byte) ? "UnsignedByte" |
3322 | 0 | : (eDT == GDT_Int8) ? "SignedByte" |
3323 | 0 | : (eDT == GDT_UInt16) ? "UnsignedLSB2" |
3324 | 0 | : (eDT == GDT_Int16) ? (m_bIsLSB ? "SignedLSB2" : "SignedMSB2") |
3325 | 0 | : (eDT == GDT_UInt32) ? (m_bIsLSB ? "UnsignedLSB4" : "UnsignedMSB4") |
3326 | 0 | : (eDT == GDT_Int32) ? (m_bIsLSB ? "SignedLSB4" : "SignedMSB4") |
3327 | 0 | : (eDT == GDT_Float32) |
3328 | 0 | ? (m_bIsLSB ? "IEEE754LSBSingle" : "IEEE754MSBSingle") |
3329 | 0 | : (eDT == GDT_Float64) |
3330 | 0 | ? (m_bIsLSB ? "IEEE754LSBDouble" : "IEEE754MSBDouble") |
3331 | 0 | : (eDT == GDT_CFloat32) ? (m_bIsLSB ? "ComplexLSB8" : "ComplexMSB8") |
3332 | 0 | : (eDT == GDT_CFloat64) ? (m_bIsLSB ? "ComplexLSB16" : "ComplexMSB16") |
3333 | 0 | : "should not happen"; |
3334 | 0 | CPLCreateXMLElementAndValue(psElementArray, |
3335 | 0 | (osPrefix + "data_type").c_str(), pszDataType); |
3336 | |
|
3337 | 0 | const char *pszUnits = GetRasterBand(1)->GetUnitType(); |
3338 | 0 | const char *pszUnitsCO = CSLFetchNameValue(m_papszCreationOptions, "UNIT"); |
3339 | 0 | if (pszUnitsCO) |
3340 | 0 | { |
3341 | 0 | pszUnits = pszUnitsCO; |
3342 | 0 | } |
3343 | 0 | if (pszUnits && pszUnits[0] != 0) |
3344 | 0 | { |
3345 | 0 | CPLCreateXMLElementAndValue(psElementArray, (osPrefix + "unit").c_str(), |
3346 | 0 | pszUnits); |
3347 | 0 | } |
3348 | |
|
3349 | 0 | int bHasScale = FALSE; |
3350 | 0 | double dfScale = GetRasterBand(1)->GetScale(&bHasScale); |
3351 | 0 | if (bHasScale && dfScale != 1.0) |
3352 | 0 | { |
3353 | 0 | CPLCreateXMLElementAndValue(psElementArray, |
3354 | 0 | (osPrefix + "scaling_factor").c_str(), |
3355 | 0 | CPLSPrintf("%.17g", dfScale)); |
3356 | 0 | } |
3357 | |
|
3358 | 0 | int bHasOffset = FALSE; |
3359 | 0 | double dfOffset = GetRasterBand(1)->GetOffset(&bHasOffset); |
3360 | 0 | if (bHasOffset && dfOffset != 1.0) |
3361 | 0 | { |
3362 | 0 | CPLCreateXMLElementAndValue(psElementArray, |
3363 | 0 | (osPrefix + "value_offset").c_str(), |
3364 | 0 | CPLSPrintf("%.17g", dfOffset)); |
3365 | 0 | } |
3366 | | |
3367 | | // Axis definitions |
3368 | 0 | { |
3369 | 0 | CPLXMLNode *psAxis = CPLCreateXMLNode( |
3370 | 0 | psArray, CXT_Element, (osPrefix + "Axis_Array").c_str()); |
3371 | 0 | CPLCreateXMLElementAndValue( |
3372 | 0 | psAxis, (osPrefix + "axis_name").c_str(), |
3373 | 0 | EQUAL(m_osInterleave, "BSQ") |
3374 | 0 | ? "Band" |
3375 | 0 | : |
3376 | | /* EQUAL(m_osInterleave, "BIL") ? "Line" : */ |
3377 | 0 | "Line"); |
3378 | 0 | CPLCreateXMLElementAndValue( |
3379 | 0 | psAxis, (osPrefix + "elements").c_str(), |
3380 | 0 | CPLSPrintf("%d", |
3381 | 0 | EQUAL(m_osInterleave, "BSQ") |
3382 | 0 | ? nBands |
3383 | 0 | : |
3384 | | /* EQUAL(m_osInterleave, "BIL") ? nRasterYSize : */ |
3385 | 0 | nRasterYSize)); |
3386 | 0 | CPLCreateXMLElementAndValue( |
3387 | 0 | psAxis, (osPrefix + "sequence_number").c_str(), "1"); |
3388 | 0 | } |
3389 | 0 | { |
3390 | 0 | CPLXMLNode *psAxis = CPLCreateXMLNode( |
3391 | 0 | psArray, CXT_Element, (osPrefix + "Axis_Array").c_str()); |
3392 | 0 | CPLCreateXMLElementAndValue(psAxis, (osPrefix + "axis_name").c_str(), |
3393 | 0 | EQUAL(m_osInterleave, "BSQ") ? "Line" |
3394 | 0 | : EQUAL(m_osInterleave, "BIL") ? "Band" |
3395 | 0 | : "Sample"); |
3396 | 0 | CPLCreateXMLElementAndValue( |
3397 | 0 | psAxis, (osPrefix + "elements").c_str(), |
3398 | 0 | CPLSPrintf("%d", EQUAL(m_osInterleave, "BSQ") ? nRasterYSize |
3399 | 0 | : EQUAL(m_osInterleave, "BIL") ? nBands |
3400 | 0 | : nRasterXSize)); |
3401 | 0 | CPLCreateXMLElementAndValue( |
3402 | 0 | psAxis, (osPrefix + "sequence_number").c_str(), "2"); |
3403 | 0 | } |
3404 | 0 | if (!bIsArray2D) |
3405 | 0 | { |
3406 | 0 | CPLXMLNode *psAxis = CPLCreateXMLNode( |
3407 | 0 | psArray, CXT_Element, (osPrefix + "Axis_Array").c_str()); |
3408 | 0 | CPLCreateXMLElementAndValue(psAxis, (osPrefix + "axis_name").c_str(), |
3409 | 0 | EQUAL(m_osInterleave, "BSQ") ? "Sample" |
3410 | 0 | : EQUAL(m_osInterleave, "BIL") ? "Sample" |
3411 | 0 | : "Band"); |
3412 | 0 | CPLCreateXMLElementAndValue( |
3413 | 0 | psAxis, (osPrefix + "elements").c_str(), |
3414 | 0 | CPLSPrintf("%d", EQUAL(m_osInterleave, "BSQ") ? nRasterXSize |
3415 | 0 | : EQUAL(m_osInterleave, "BIL") ? nRasterXSize |
3416 | 0 | : nBands)); |
3417 | 0 | CPLCreateXMLElementAndValue( |
3418 | 0 | psAxis, (osPrefix + "sequence_number").c_str(), "3"); |
3419 | 0 | } |
3420 | |
|
3421 | 0 | int bHasNoData = FALSE; |
3422 | 0 | double dfNoData = GetRasterBand(1)->GetNoDataValue(&bHasNoData); |
3423 | 0 | if (psTemplateSpecialConstants) |
3424 | 0 | { |
3425 | 0 | CPLAddXMLChild(psArray, psTemplateSpecialConstants); |
3426 | 0 | if (bHasNoData) |
3427 | 0 | { |
3428 | 0 | CPLXMLNode *psMC = |
3429 | 0 | CPLGetXMLNode(psTemplateSpecialConstants, |
3430 | 0 | (osPrefix + "missing_constant").c_str()); |
3431 | 0 | if (psMC != nullptr) |
3432 | 0 | { |
3433 | 0 | if (psMC->psChild && psMC->psChild->eType == CXT_Text) |
3434 | 0 | { |
3435 | 0 | CPLFree(psMC->psChild->pszValue); |
3436 | 0 | psMC->psChild->pszValue = |
3437 | 0 | CPLStrdup(CPLSPrintf("%.17g", dfNoData)); |
3438 | 0 | } |
3439 | 0 | } |
3440 | 0 | else |
3441 | 0 | { |
3442 | 0 | CPLXMLNode *psSaturatedConstant = |
3443 | 0 | CPLGetXMLNode(psTemplateSpecialConstants, |
3444 | 0 | (osPrefix + "saturated_constant").c_str()); |
3445 | 0 | psMC = CPLCreateXMLElementAndValue( |
3446 | 0 | nullptr, (osPrefix + "missing_constant").c_str(), |
3447 | 0 | CPLSPrintf("%.17g", dfNoData)); |
3448 | 0 | CPLXMLNode *psNext; |
3449 | 0 | if (psSaturatedConstant) |
3450 | 0 | { |
3451 | 0 | psNext = psSaturatedConstant->psNext; |
3452 | 0 | psSaturatedConstant->psNext = psMC; |
3453 | 0 | } |
3454 | 0 | else |
3455 | 0 | { |
3456 | 0 | psNext = psTemplateSpecialConstants->psChild; |
3457 | 0 | psTemplateSpecialConstants->psChild = psMC; |
3458 | 0 | } |
3459 | 0 | psMC->psNext = psNext; |
3460 | 0 | } |
3461 | 0 | } |
3462 | 0 | } |
3463 | 0 | else if (bHasNoData) |
3464 | 0 | { |
3465 | 0 | CPLXMLNode *psSC = CPLCreateXMLNode( |
3466 | 0 | psArray, CXT_Element, (osPrefix + "Special_Constants").c_str()); |
3467 | 0 | CPLCreateXMLElementAndValue(psSC, |
3468 | 0 | (osPrefix + "missing_constant").c_str(), |
3469 | 0 | CPLSPrintf("%.17g", dfNoData)); |
3470 | 0 | } |
3471 | 0 | } |
3472 | | |
3473 | | /************************************************************************/ |
3474 | | /* WriteVectorLayers() */ |
3475 | | /************************************************************************/ |
3476 | | |
3477 | | void PDS4Dataset::WriteVectorLayers(CPLXMLNode *psProduct) |
3478 | 0 | { |
3479 | 0 | CPLString osPrefix; |
3480 | 0 | if (STARTS_WITH(psProduct->pszValue, "pds:")) |
3481 | 0 | osPrefix = "pds:"; |
3482 | |
|
3483 | 0 | for (auto &poLayer : m_apoLayers) |
3484 | 0 | { |
3485 | 0 | if (!poLayer->IsDirtyHeader()) |
3486 | 0 | continue; |
3487 | | |
3488 | 0 | if (poLayer->GetFeatureCount(false) == 0) |
3489 | 0 | { |
3490 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3491 | 0 | "Writing header for layer %s which has 0 features. " |
3492 | 0 | "This is not legal in PDS4", |
3493 | 0 | poLayer->GetName()); |
3494 | 0 | } |
3495 | |
|
3496 | 0 | if (poLayer->GetRawFieldCount() == 0) |
3497 | 0 | { |
3498 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3499 | 0 | "Writing header for layer %s which has 0 fields. " |
3500 | 0 | "This is not legal in PDS4", |
3501 | 0 | poLayer->GetName()); |
3502 | 0 | } |
3503 | |
|
3504 | 0 | const std::string osRelativePath( |
3505 | 0 | CPLExtractRelativePath(CPLGetPathSafe(m_osXMLFilename).c_str(), |
3506 | 0 | poLayer->GetFileName(), nullptr)); |
3507 | |
|
3508 | 0 | bool bFound = false; |
3509 | 0 | for (CPLXMLNode *psIter = psProduct->psChild; psIter != nullptr; |
3510 | 0 | psIter = psIter->psNext) |
3511 | 0 | { |
3512 | 0 | if (psIter->eType == CXT_Element && |
3513 | 0 | strcmp(psIter->pszValue, |
3514 | 0 | (osPrefix + "File_Area_Observational").c_str()) == 0) |
3515 | 0 | { |
3516 | 0 | const char *pszFilename = CPLGetXMLValue( |
3517 | 0 | psIter, |
3518 | 0 | (osPrefix + "File." + osPrefix + "file_name").c_str(), ""); |
3519 | 0 | if (strcmp(pszFilename, osRelativePath.c_str()) == 0) |
3520 | 0 | { |
3521 | 0 | poLayer->RefreshFileAreaObservational(psIter); |
3522 | 0 | bFound = true; |
3523 | 0 | break; |
3524 | 0 | } |
3525 | 0 | } |
3526 | 0 | } |
3527 | 0 | if (!bFound) |
3528 | 0 | { |
3529 | 0 | CPLXMLNode *psFAO = CPLCreateXMLNode( |
3530 | 0 | psProduct, CXT_Element, |
3531 | 0 | (osPrefix + "File_Area_Observational").c_str()); |
3532 | 0 | CPLXMLNode *psFile = CPLCreateXMLNode(psFAO, CXT_Element, |
3533 | 0 | (osPrefix + "File").c_str()); |
3534 | 0 | CPLCreateXMLElementAndValue(psFile, |
3535 | 0 | (osPrefix + "file_name").c_str(), |
3536 | 0 | osRelativePath.c_str()); |
3537 | 0 | poLayer->RefreshFileAreaObservational(psFAO); |
3538 | 0 | } |
3539 | 0 | } |
3540 | 0 | } |
3541 | | |
3542 | | /************************************************************************/ |
3543 | | /* CreateHeader() */ |
3544 | | /************************************************************************/ |
3545 | | |
3546 | | void PDS4Dataset::CreateHeader(CPLXMLNode *psProduct, |
3547 | | const char *pszCARTVersion) |
3548 | 0 | { |
3549 | 0 | CPLString osPrefix; |
3550 | 0 | if (STARTS_WITH(psProduct->pszValue, "pds:")) |
3551 | 0 | osPrefix = "pds:"; |
3552 | |
|
3553 | 0 | OGREnvelope sExtent; |
3554 | 0 | if (m_oSRS.IsEmpty() && GetLayerCount() >= 1 && |
3555 | 0 | GetLayer(0)->GetSpatialRef() != nullptr) |
3556 | 0 | { |
3557 | 0 | const auto poSRS = GetLayer(0)->GetSpatialRef(); |
3558 | 0 | if (poSRS) |
3559 | 0 | m_oSRS = *poSRS; |
3560 | 0 | } |
3561 | |
|
3562 | 0 | if (!m_oSRS.IsEmpty() && |
3563 | 0 | CSLFetchNameValue(m_papszCreationOptions, "VAR_TARGET") == nullptr) |
3564 | 0 | { |
3565 | 0 | const char *pszTarget = nullptr; |
3566 | 0 | if (fabs(m_oSRS.GetSemiMajor() - 6378137) < 0.001 * 6378137) |
3567 | 0 | { |
3568 | 0 | pszTarget = "Earth"; |
3569 | 0 | m_papszCreationOptions = CSLSetNameValue( |
3570 | 0 | m_papszCreationOptions, "VAR_TARGET_TYPE", "Planet"); |
3571 | 0 | } |
3572 | 0 | else |
3573 | 0 | { |
3574 | 0 | const char *pszDatum = m_oSRS.GetAttrValue("DATUM"); |
3575 | 0 | if (pszDatum && STARTS_WITH(pszDatum, "D_")) |
3576 | 0 | { |
3577 | 0 | pszTarget = pszDatum + 2; |
3578 | 0 | } |
3579 | 0 | else if (pszDatum) |
3580 | 0 | { |
3581 | 0 | pszTarget = pszDatum; |
3582 | 0 | } |
3583 | 0 | } |
3584 | 0 | if (pszTarget) |
3585 | 0 | { |
3586 | 0 | m_papszCreationOptions = CSLSetNameValue(m_papszCreationOptions, |
3587 | 0 | "VAR_TARGET", pszTarget); |
3588 | 0 | } |
3589 | 0 | } |
3590 | 0 | SubstituteVariables(psProduct, m_papszCreationOptions); |
3591 | | |
3592 | | // Remove <Discipline_Area>/<disp:Display_Settings> if there is no raster |
3593 | 0 | if (GetRasterCount() == 0) |
3594 | 0 | { |
3595 | 0 | CPLXMLNode *psDisciplineArea = |
3596 | 0 | CPLGetXMLNode(psProduct, (osPrefix + "Observation_Area." + |
3597 | 0 | osPrefix + "Discipline_Area") |
3598 | 0 | .c_str()); |
3599 | 0 | if (psDisciplineArea) |
3600 | 0 | { |
3601 | 0 | CPLXMLNode *psDisplaySettings = |
3602 | 0 | CPLGetXMLNode(psDisciplineArea, "disp:Display_Settings"); |
3603 | 0 | if (psDisplaySettings) |
3604 | 0 | { |
3605 | 0 | CPLRemoveXMLChild(psDisciplineArea, psDisplaySettings); |
3606 | 0 | CPLDestroyXMLNode(psDisplaySettings); |
3607 | 0 | } |
3608 | 0 | } |
3609 | 0 | } |
3610 | | |
3611 | | // Depending on the version of the DISP schema, Local_Internal_Reference |
3612 | | // may be in the disp: namespace or the default one. |
3613 | 0 | const auto GetLocalIdentifierReferenceFromDisciplineArea = |
3614 | 0 | [](const CPLXMLNode *psDisciplineArea, const char *pszDefault) |
3615 | 0 | { |
3616 | 0 | return CPLGetXMLValue( |
3617 | 0 | psDisciplineArea, |
3618 | 0 | "disp:Display_Settings.Local_Internal_Reference." |
3619 | 0 | "local_identifier_reference", |
3620 | 0 | CPLGetXMLValue( |
3621 | 0 | psDisciplineArea, |
3622 | 0 | "disp:Display_Settings.disp:Local_Internal_Reference." |
3623 | 0 | "local_identifier_reference", |
3624 | 0 | pszDefault)); |
3625 | 0 | }; |
3626 | |
|
3627 | 0 | if (GetRasterCount() || !m_oSRS.IsEmpty()) |
3628 | 0 | { |
3629 | 0 | CPLXMLNode *psDisciplineArea = |
3630 | 0 | CPLGetXMLNode(psProduct, (osPrefix + "Observation_Area." + |
3631 | 0 | osPrefix + "Discipline_Area") |
3632 | 0 | .c_str()); |
3633 | 0 | if (GetRasterCount() && !(m_bGotTransform && !m_oSRS.IsEmpty())) |
3634 | 0 | { |
3635 | | // if we have no georeferencing, strip any existing georeferencing |
3636 | | // from the template |
3637 | 0 | if (psDisciplineArea) |
3638 | 0 | { |
3639 | 0 | CPLXMLNode *psCart = |
3640 | 0 | CPLGetXMLNode(psDisciplineArea, "cart:Cartography"); |
3641 | 0 | if (psCart == nullptr) |
3642 | 0 | psCart = CPLGetXMLNode(psDisciplineArea, "Cartography"); |
3643 | 0 | if (psCart) |
3644 | 0 | { |
3645 | 0 | CPLRemoveXMLChild(psDisciplineArea, psCart); |
3646 | 0 | CPLDestroyXMLNode(psCart); |
3647 | 0 | } |
3648 | |
|
3649 | 0 | if (CPLGetXMLNode(psDisciplineArea, |
3650 | 0 | "sp:Spectral_Characteristics")) |
3651 | 0 | { |
3652 | 0 | const char *pszArrayType = |
3653 | 0 | CSLFetchNameValue(m_papszCreationOptions, "ARRAY_TYPE"); |
3654 | | // The schematron PDS4_SP_1100.sch requires that |
3655 | | // sp:local_identifier_reference is used by |
3656 | | // Array_[2D|3D]_Spectrum/pds:local_identifier |
3657 | 0 | if (pszArrayType == nullptr) |
3658 | 0 | { |
3659 | 0 | m_papszCreationOptions = |
3660 | 0 | CSLSetNameValue(m_papszCreationOptions, |
3661 | 0 | "ARRAY_TYPE", "Array_3D_Spectrum"); |
3662 | 0 | } |
3663 | 0 | else if (!EQUAL(pszArrayType, "Array_2D_Spectrum") && |
3664 | 0 | !EQUAL(pszArrayType, "Array_3D_Spectrum")) |
3665 | 0 | { |
3666 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3667 | 0 | "PDS4_SP_xxxx.sch schematron requires the " |
3668 | 0 | "use of ARRAY_TYPE=Array_2D_Spectrum or " |
3669 | 0 | "Array_3D_Spectrum"); |
3670 | 0 | } |
3671 | 0 | } |
3672 | 0 | } |
3673 | 0 | } |
3674 | 0 | else |
3675 | 0 | { |
3676 | 0 | if (psDisciplineArea == nullptr) |
3677 | 0 | { |
3678 | 0 | CPLXMLNode *psTI = CPLGetXMLNode( |
3679 | 0 | psProduct, (osPrefix + "Observation_Area." + osPrefix + |
3680 | 0 | "Target_Identification") |
3681 | 0 | .c_str()); |
3682 | 0 | if (psTI == nullptr) |
3683 | 0 | { |
3684 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3685 | 0 | "Cannot find Target_Identification element in " |
3686 | 0 | "template"); |
3687 | 0 | return; |
3688 | 0 | } |
3689 | 0 | psDisciplineArea = |
3690 | 0 | CPLCreateXMLNode(nullptr, CXT_Element, |
3691 | 0 | (osPrefix + "Discipline_Area").c_str()); |
3692 | 0 | if (psTI->psNext) |
3693 | 0 | psDisciplineArea->psNext = psTI->psNext; |
3694 | 0 | psTI->psNext = psDisciplineArea; |
3695 | 0 | } |
3696 | 0 | CPLXMLNode *psCart = |
3697 | 0 | CPLGetXMLNode(psDisciplineArea, "cart:Cartography"); |
3698 | 0 | if (psCart == nullptr) |
3699 | 0 | psCart = CPLGetXMLNode(psDisciplineArea, "Cartography"); |
3700 | 0 | if (psCart == nullptr) |
3701 | 0 | { |
3702 | 0 | psCart = CPLCreateXMLNode(psDisciplineArea, CXT_Element, |
3703 | 0 | "cart:Cartography"); |
3704 | 0 | if (CPLGetXMLNode(psProduct, "xmlns:cart") == nullptr) |
3705 | 0 | { |
3706 | 0 | CPLXMLNode *psNS = |
3707 | 0 | CPLCreateXMLNode(nullptr, CXT_Attribute, "xmlns:cart"); |
3708 | 0 | CPLCreateXMLNode(psNS, CXT_Text, |
3709 | 0 | "http://pds.nasa.gov/pds4/cart/v1"); |
3710 | 0 | CPLAddXMLChild(psProduct, psNS); |
3711 | 0 | CPLXMLNode *psSchemaLoc = |
3712 | 0 | CPLGetXMLNode(psProduct, "xsi:schemaLocation"); |
3713 | 0 | if (psSchemaLoc != nullptr && |
3714 | 0 | psSchemaLoc->psChild != nullptr && |
3715 | 0 | psSchemaLoc->psChild->pszValue != nullptr) |
3716 | 0 | { |
3717 | 0 | CPLString osCartSchema; |
3718 | 0 | if (strstr(psSchemaLoc->psChild->pszValue, |
3719 | 0 | "PDS4_PDS_1800.xsd")) |
3720 | 0 | { |
3721 | | // GDAL 2.4 |
3722 | 0 | osCartSchema = "https://pds.nasa.gov/pds4/cart/v1/" |
3723 | 0 | "PDS4_CART_1700.xsd"; |
3724 | 0 | pszCARTVersion = "1700"; |
3725 | 0 | } |
3726 | 0 | else if (strstr(psSchemaLoc->psChild->pszValue, |
3727 | 0 | "PDS4_PDS_1B00.xsd")) |
3728 | 0 | { |
3729 | | // GDAL 3.0 |
3730 | 0 | osCartSchema = |
3731 | 0 | "https://raw.githubusercontent.com/" |
3732 | 0 | "nasa-pds-data-dictionaries/ldd-cart/master/" |
3733 | 0 | "build/1.B.0.0/PDS4_CART_1B00.xsd"; |
3734 | 0 | pszCARTVersion = "1B00"; |
3735 | 0 | } |
3736 | 0 | else if (strstr(psSchemaLoc->psChild->pszValue, |
3737 | 0 | "PDS4_PDS_1D00.xsd")) |
3738 | 0 | { |
3739 | | // GDAL 3.1 |
3740 | 0 | osCartSchema = "https://pds.nasa.gov/pds4/cart/v1/" |
3741 | 0 | "PDS4_CART_1D00_1933.xsd"; |
3742 | 0 | pszCARTVersion = "1D00_1933"; |
3743 | 0 | } |
3744 | 0 | else |
3745 | 0 | { |
3746 | | // GDAL 3.4 |
3747 | 0 | osCartSchema = |
3748 | 0 | "https://pds.nasa.gov/pds4/cart/v1/" |
3749 | 0 | "PDS4_CART_" CURRENT_CART_VERSION ".xsd"; |
3750 | 0 | pszCARTVersion = CURRENT_CART_VERSION; |
3751 | 0 | } |
3752 | 0 | CPLString osNewVal(psSchemaLoc->psChild->pszValue); |
3753 | 0 | osNewVal += |
3754 | 0 | " http://pds.nasa.gov/pds4/cart/v1 " + osCartSchema; |
3755 | 0 | CPLFree(psSchemaLoc->psChild->pszValue); |
3756 | 0 | psSchemaLoc->psChild->pszValue = CPLStrdup(osNewVal); |
3757 | 0 | } |
3758 | 0 | } |
3759 | 0 | } |
3760 | 0 | else |
3761 | 0 | { |
3762 | 0 | if (psCart->psChild) |
3763 | 0 | { |
3764 | 0 | CPLDestroyXMLNode(psCart->psChild); |
3765 | 0 | psCart->psChild = nullptr; |
3766 | 0 | } |
3767 | 0 | } |
3768 | |
|
3769 | 0 | if (IsCARTVersionGTE(pszCARTVersion, "1900")) |
3770 | 0 | { |
3771 | 0 | const char *pszLocalIdentifier = |
3772 | 0 | GetLocalIdentifierReferenceFromDisciplineArea( |
3773 | 0 | psDisciplineArea, |
3774 | 0 | GetRasterCount() == 0 && GetLayerCount() > 0 |
3775 | 0 | ? GetLayer(0)->GetName() |
3776 | 0 | : "image"); |
3777 | 0 | CPLXMLNode *psLIR = CPLCreateXMLNode( |
3778 | 0 | psCart, CXT_Element, |
3779 | 0 | (osPrefix + "Local_Internal_Reference").c_str()); |
3780 | 0 | CPLCreateXMLElementAndValue( |
3781 | 0 | psLIR, (osPrefix + "local_identifier_reference").c_str(), |
3782 | 0 | pszLocalIdentifier); |
3783 | 0 | CPLCreateXMLElementAndValue( |
3784 | 0 | psLIR, (osPrefix + "local_reference_type").c_str(), |
3785 | 0 | "cartography_parameters_to_image_object"); |
3786 | 0 | } |
3787 | |
|
3788 | 0 | WriteGeoreferencing(psCart, pszCARTVersion); |
3789 | 0 | } |
3790 | | |
3791 | 0 | const char *pszVertDir = CSLFetchNameValue( |
3792 | 0 | m_papszCreationOptions, "VAR_VERTICAL_DISPLAY_DIRECTION"); |
3793 | 0 | if (pszVertDir) |
3794 | 0 | { |
3795 | 0 | CPLXMLNode *psVertDirNode = |
3796 | 0 | CPLGetXMLNode(psDisciplineArea, |
3797 | 0 | "disp:Display_Settings.disp:Display_Direction." |
3798 | 0 | "disp:vertical_display_direction"); |
3799 | 0 | if (psVertDirNode == nullptr) |
3800 | 0 | { |
3801 | 0 | CPLError( |
3802 | 0 | CE_Warning, CPLE_AppDefined, |
3803 | 0 | "PDS4 template lacks a disp:vertical_display_direction " |
3804 | 0 | "element where to write %s", |
3805 | 0 | pszVertDir); |
3806 | 0 | } |
3807 | 0 | else |
3808 | 0 | { |
3809 | 0 | CPLDestroyXMLNode(psVertDirNode->psChild); |
3810 | 0 | psVertDirNode->psChild = |
3811 | 0 | CPLCreateXMLNode(nullptr, CXT_Text, pszVertDir); |
3812 | 0 | } |
3813 | 0 | } |
3814 | 0 | } |
3815 | 0 | else |
3816 | 0 | { |
3817 | | // Remove Observation_Area.Discipline_Area if it contains only |
3818 | | // <disp:Display_Settings> or is empty |
3819 | 0 | CPLXMLNode *psObservationArea = |
3820 | 0 | CPLGetXMLNode(psProduct, (osPrefix + "Observation_Area").c_str()); |
3821 | 0 | if (psObservationArea) |
3822 | 0 | { |
3823 | 0 | CPLXMLNode *psDisciplineArea = CPLGetXMLNode( |
3824 | 0 | psObservationArea, (osPrefix + "Discipline_Area").c_str()); |
3825 | 0 | if (psDisciplineArea && |
3826 | 0 | (psDisciplineArea->psChild == nullptr || |
3827 | 0 | (psDisciplineArea->psChild->eType == CXT_Element && |
3828 | 0 | psDisciplineArea->psChild->psNext == nullptr && |
3829 | 0 | strcmp(psDisciplineArea->psChild->pszValue, |
3830 | 0 | "disp:Display_Settings") == 0))) |
3831 | 0 | { |
3832 | 0 | CPLRemoveXMLChild(psObservationArea, psDisciplineArea); |
3833 | 0 | CPLDestroyXMLNode(psDisciplineArea); |
3834 | 0 | } |
3835 | 0 | } |
3836 | 0 | } |
3837 | | |
3838 | 0 | if (m_bStripFileAreaObservationalFromTemplate) |
3839 | 0 | { |
3840 | 0 | m_bStripFileAreaObservationalFromTemplate = false; |
3841 | 0 | CPLXMLNode *psObservationArea = nullptr; |
3842 | 0 | CPLXMLNode *psPrev = nullptr; |
3843 | 0 | CPLXMLNode *psTemplateSpecialConstants = nullptr; |
3844 | 0 | for (CPLXMLNode *psIter = psProduct->psChild; psIter != nullptr;) |
3845 | 0 | { |
3846 | 0 | if (psIter->eType == CXT_Element && |
3847 | 0 | psIter->pszValue == osPrefix + "Observation_Area") |
3848 | 0 | { |
3849 | 0 | psObservationArea = psIter; |
3850 | 0 | psPrev = psIter; |
3851 | 0 | psIter = psIter->psNext; |
3852 | 0 | } |
3853 | 0 | else if (psIter->eType == CXT_Element && |
3854 | 0 | (psIter->pszValue == |
3855 | 0 | osPrefix + "File_Area_Observational" || |
3856 | 0 | psIter->pszValue == |
3857 | 0 | osPrefix + "File_Area_Observational_Supplemental")) |
3858 | 0 | { |
3859 | 0 | if (psIter->pszValue == osPrefix + "File_Area_Observational") |
3860 | 0 | { |
3861 | 0 | psTemplateSpecialConstants = |
3862 | 0 | GetSpecialConstants(osPrefix, psIter); |
3863 | 0 | } |
3864 | 0 | if (psPrev) |
3865 | 0 | psPrev->psNext = psIter->psNext; |
3866 | 0 | else |
3867 | 0 | { |
3868 | 0 | CPLAssert(psProduct->psChild == psIter); |
3869 | 0 | psProduct->psChild = psIter->psNext; |
3870 | 0 | } |
3871 | 0 | CPLXMLNode *psNext = psIter->psNext; |
3872 | 0 | psIter->psNext = nullptr; |
3873 | 0 | CPLDestroyXMLNode(psIter); |
3874 | 0 | psIter = psNext; |
3875 | 0 | } |
3876 | 0 | else |
3877 | 0 | { |
3878 | 0 | psPrev = psIter; |
3879 | 0 | psIter = psIter->psNext; |
3880 | 0 | } |
3881 | 0 | } |
3882 | 0 | if (psObservationArea == nullptr) |
3883 | 0 | { |
3884 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3885 | 0 | "Cannot find Observation_Area in template"); |
3886 | 0 | CPLDestroyXMLNode(psTemplateSpecialConstants); |
3887 | 0 | return; |
3888 | 0 | } |
3889 | | |
3890 | 0 | if (GetRasterCount()) |
3891 | 0 | { |
3892 | 0 | CPLXMLNode *psFAOPrev = psObservationArea; |
3893 | 0 | while (psFAOPrev->psNext != nullptr && |
3894 | 0 | psFAOPrev->psNext->eType == CXT_Comment) |
3895 | 0 | { |
3896 | 0 | psFAOPrev = psFAOPrev->psNext; |
3897 | 0 | } |
3898 | 0 | if (psFAOPrev->psNext != nullptr) |
3899 | 0 | { |
3900 | | // There may be an optional Reference_List element between |
3901 | | // Observation_Area and File_Area_Observational |
3902 | 0 | if (!(psFAOPrev->psNext->eType == CXT_Element && |
3903 | 0 | psFAOPrev->psNext->pszValue == |
3904 | 0 | osPrefix + "Reference_List")) |
3905 | 0 | { |
3906 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3907 | 0 | "Unexpected content found after Observation_Area " |
3908 | 0 | "in template"); |
3909 | 0 | CPLDestroyXMLNode(psTemplateSpecialConstants); |
3910 | 0 | return; |
3911 | 0 | } |
3912 | 0 | psFAOPrev = psFAOPrev->psNext; |
3913 | 0 | while (psFAOPrev->psNext != nullptr && |
3914 | 0 | psFAOPrev->psNext->eType == CXT_Comment) |
3915 | 0 | { |
3916 | 0 | psFAOPrev = psFAOPrev->psNext; |
3917 | 0 | } |
3918 | 0 | if (psFAOPrev->psNext != nullptr) |
3919 | 0 | { |
3920 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3921 | 0 | "Unexpected content found after Reference_List in " |
3922 | 0 | "template"); |
3923 | 0 | CPLDestroyXMLNode(psTemplateSpecialConstants); |
3924 | 0 | return; |
3925 | 0 | } |
3926 | 0 | } |
3927 | | |
3928 | 0 | CPLXMLNode *psFAO = CPLCreateXMLNode( |
3929 | 0 | nullptr, CXT_Element, |
3930 | 0 | (osPrefix + "File_Area_Observational").c_str()); |
3931 | 0 | psFAOPrev->psNext = psFAO; |
3932 | |
|
3933 | 0 | CPLXMLNode *psFile = CPLCreateXMLNode(psFAO, CXT_Element, |
3934 | 0 | (osPrefix + "File").c_str()); |
3935 | 0 | CPLCreateXMLElementAndValue(psFile, |
3936 | 0 | (osPrefix + "file_name").c_str(), |
3937 | 0 | CPLGetFilename(m_osImageFilename)); |
3938 | 0 | if (m_bCreatedFromExistingBinaryFile) |
3939 | 0 | { |
3940 | 0 | CPLCreateXMLNode(psFile, CXT_Comment, PREEXISTING_BINARY_FILE); |
3941 | 0 | } |
3942 | 0 | CPLXMLNode *psDisciplineArea = |
3943 | 0 | CPLGetXMLNode(psProduct, (osPrefix + "Observation_Area." + |
3944 | 0 | osPrefix + "Discipline_Area") |
3945 | 0 | .c_str()); |
3946 | 0 | const char *pszLocalIdentifier = |
3947 | 0 | GetLocalIdentifierReferenceFromDisciplineArea(psDisciplineArea, |
3948 | 0 | "image"); |
3949 | |
|
3950 | 0 | if (m_poExternalDS && m_poExternalDS->GetDriver() && |
3951 | 0 | EQUAL(m_poExternalDS->GetDriver()->GetDescription(), "GTiff")) |
3952 | 0 | { |
3953 | 0 | VSILFILE *fpTemp = |
3954 | 0 | VSIFOpenL(m_poExternalDS->GetDescription(), "rb"); |
3955 | 0 | if (fpTemp) |
3956 | 0 | { |
3957 | 0 | GByte abySignature[4] = {0}; |
3958 | 0 | VSIFReadL(abySignature, 1, 4, fpTemp); |
3959 | 0 | VSIFCloseL(fpTemp); |
3960 | 0 | const bool bBigTIFF = |
3961 | 0 | abySignature[2] == 43 || abySignature[3] == 43; |
3962 | 0 | m_osHeaderParsingStandard = |
3963 | 0 | bBigTIFF ? BIGTIFF_GEOTIFF_STRING : TIFF_GEOTIFF_STRING; |
3964 | 0 | const char *pszOffset = |
3965 | 0 | m_poExternalDS->GetRasterBand(1)->GetMetadataItem( |
3966 | 0 | "BLOCK_OFFSET_0_0", "TIFF"); |
3967 | 0 | if (pszOffset) |
3968 | 0 | m_nBaseOffset = CPLAtoGIntBig(pszOffset); |
3969 | 0 | } |
3970 | 0 | } |
3971 | |
|
3972 | 0 | if (!m_osHeaderParsingStandard.empty() && m_nBaseOffset > 0) |
3973 | 0 | { |
3974 | 0 | CPLXMLNode *psHeader = CPLCreateXMLNode( |
3975 | 0 | psFAO, CXT_Element, (osPrefix + "Header").c_str()); |
3976 | 0 | CPLAddXMLAttributeAndValue( |
3977 | 0 | CPLCreateXMLElementAndValue( |
3978 | 0 | psHeader, (osPrefix + "offset").c_str(), "0"), |
3979 | 0 | "unit", "byte"); |
3980 | 0 | CPLAddXMLAttributeAndValue( |
3981 | 0 | CPLCreateXMLElementAndValue( |
3982 | 0 | psHeader, (osPrefix + "object_length").c_str(), |
3983 | 0 | CPLSPrintf(CPL_FRMT_GUIB, |
3984 | 0 | static_cast<GUIntBig>(m_nBaseOffset))), |
3985 | 0 | "unit", "byte"); |
3986 | 0 | CPLCreateXMLElementAndValue( |
3987 | 0 | psHeader, (osPrefix + "parsing_standard_id").c_str(), |
3988 | 0 | m_osHeaderParsingStandard.c_str()); |
3989 | 0 | if (m_osHeaderParsingStandard == TIFF_GEOTIFF_STRING) |
3990 | 0 | { |
3991 | 0 | CPLCreateXMLElementAndValue( |
3992 | 0 | psHeader, (osPrefix + "description").c_str(), |
3993 | 0 | "TIFF/GeoTIFF header. The TIFF/GeoTIFF format is used " |
3994 | 0 | "throughout the geospatial and science communities " |
3995 | 0 | "to share geographic image data. "); |
3996 | 0 | } |
3997 | 0 | else if (m_osHeaderParsingStandard == BIGTIFF_GEOTIFF_STRING) |
3998 | 0 | { |
3999 | 0 | CPLCreateXMLElementAndValue( |
4000 | 0 | psHeader, (osPrefix + "description").c_str(), |
4001 | 0 | "BigTIFF/GeoTIFF header. The BigTIFF/GeoTIFF format is " |
4002 | 0 | "used " |
4003 | 0 | "throughout the geospatial and science communities " |
4004 | 0 | "to share geographic image data. "); |
4005 | 0 | } |
4006 | 0 | } |
4007 | |
|
4008 | 0 | WriteArray(osPrefix, psFAO, pszLocalIdentifier, |
4009 | 0 | psTemplateSpecialConstants); |
4010 | 0 | } |
4011 | 0 | } |
4012 | 0 | } |
4013 | | |
4014 | | /************************************************************************/ |
4015 | | /* WriteHeader() */ |
4016 | | /************************************************************************/ |
4017 | | |
4018 | | void PDS4Dataset::WriteHeader() |
4019 | 98 | { |
4020 | 98 | const bool bAppend = |
4021 | 98 | CPLFetchBool(m_papszCreationOptions, "APPEND_SUBDATASET", false); |
4022 | 98 | if (bAppend) |
4023 | 0 | { |
4024 | 0 | WriteHeaderAppendCase(); |
4025 | 0 | return; |
4026 | 0 | } |
4027 | | |
4028 | 98 | CPLXMLNode *psRoot; |
4029 | 98 | if (m_bCreateHeader) |
4030 | 98 | { |
4031 | 98 | CPLString osTemplateFilename = |
4032 | 98 | CSLFetchNameValueDef(m_papszCreationOptions, "TEMPLATE", ""); |
4033 | 98 | if (!osTemplateFilename.empty()) |
4034 | 0 | { |
4035 | 0 | if (STARTS_WITH(osTemplateFilename, "http://") || |
4036 | 0 | STARTS_WITH(osTemplateFilename, "https://")) |
4037 | 0 | { |
4038 | 0 | osTemplateFilename = "/vsicurl_streaming/" + osTemplateFilename; |
4039 | 0 | } |
4040 | 0 | psRoot = CPLParseXMLFile(osTemplateFilename); |
4041 | 0 | } |
4042 | 98 | else if (!m_osXMLPDS4.empty()) |
4043 | 0 | psRoot = CPLParseXMLString(m_osXMLPDS4); |
4044 | 98 | else |
4045 | 98 | { |
4046 | 98 | #ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES |
4047 | | #ifdef EMBED_RESOURCE_FILES |
4048 | | CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler); |
4049 | | #endif |
4050 | 98 | const char *pszDefaultTemplateFilename = |
4051 | 98 | CPLFindFile("gdal", "pds4_template.xml"); |
4052 | 98 | if (pszDefaultTemplateFilename) |
4053 | 0 | { |
4054 | 0 | psRoot = CPLParseXMLFile(pszDefaultTemplateFilename); |
4055 | 0 | } |
4056 | 98 | else |
4057 | 98 | #endif |
4058 | 98 | { |
4059 | | #ifdef EMBED_RESOURCE_FILES |
4060 | | static const bool bOnce [[maybe_unused]] = []() |
4061 | | { |
4062 | | CPLDebug("PDS4", "Using embedded pds4_template.xml"); |
4063 | | return true; |
4064 | | }(); |
4065 | | psRoot = CPLParseXMLString(PDS4GetEmbeddedTemplate()); |
4066 | | #else |
4067 | 98 | CPLError(CE_Failure, CPLE_AppDefined, |
4068 | 98 | "Cannot find pds4_template.xml and TEMPLATE " |
4069 | 98 | "creation option not specified"); |
4070 | 98 | return; |
4071 | 98 | #endif |
4072 | 98 | } |
4073 | 98 | } |
4074 | 98 | } |
4075 | 0 | else |
4076 | 0 | { |
4077 | 0 | psRoot = CPLParseXMLFile(m_osXMLFilename); |
4078 | 0 | } |
4079 | 0 | CPLXMLTreeCloser oCloser(psRoot); |
4080 | 0 | psRoot = oCloser.get(); |
4081 | 0 | if (psRoot == nullptr) |
4082 | 0 | return; |
4083 | 0 | CPLXMLNode *psProduct = CPLGetXMLNode(psRoot, "=Product_Observational"); |
4084 | 0 | if (psProduct == nullptr) |
4085 | 0 | { |
4086 | 0 | psProduct = CPLGetXMLNode(psRoot, "=pds:Product_Observational"); |
4087 | 0 | } |
4088 | 0 | if (psProduct == nullptr) |
4089 | 0 | { |
4090 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4091 | 0 | "Cannot find Product_Observational element in template"); |
4092 | 0 | return; |
4093 | 0 | } |
4094 | | |
4095 | 0 | if (m_bCreateHeader) |
4096 | 0 | { |
4097 | 0 | CPLString osCARTVersion(CURRENT_CART_VERSION); |
4098 | 0 | char *pszXML = CPLSerializeXMLTree(psRoot); |
4099 | 0 | if (pszXML) |
4100 | 0 | { |
4101 | 0 | const char *pszIter = pszXML; |
4102 | 0 | while (true) |
4103 | 0 | { |
4104 | 0 | const char *pszCartSchema = strstr(pszIter, "PDS4_CART_"); |
4105 | 0 | if (pszCartSchema) |
4106 | 0 | { |
4107 | 0 | const char *pszXSDExtension = strstr(pszCartSchema, ".xsd"); |
4108 | 0 | if (pszXSDExtension && |
4109 | 0 | pszXSDExtension - pszCartSchema <= 20) |
4110 | 0 | { |
4111 | 0 | osCARTVersion = pszCartSchema + strlen("PDS4_CART_"); |
4112 | 0 | osCARTVersion.resize(pszXSDExtension - pszCartSchema - |
4113 | 0 | strlen("PDS4_CART_")); |
4114 | 0 | break; |
4115 | 0 | } |
4116 | 0 | else |
4117 | 0 | { |
4118 | 0 | pszIter = pszCartSchema + 1; |
4119 | 0 | } |
4120 | 0 | } |
4121 | 0 | else |
4122 | 0 | { |
4123 | 0 | break; |
4124 | 0 | } |
4125 | 0 | } |
4126 | |
|
4127 | 0 | CPLFree(pszXML); |
4128 | 0 | } |
4129 | |
|
4130 | 0 | CreateHeader(psProduct, osCARTVersion.c_str()); |
4131 | 0 | } |
4132 | |
|
4133 | 0 | WriteVectorLayers(psProduct); |
4134 | |
|
4135 | 0 | CPLSerializeXMLTreeToFile(psRoot, GetDescription()); |
4136 | 0 | } |
4137 | | |
4138 | | /************************************************************************/ |
4139 | | /* ICreateLayer() */ |
4140 | | /************************************************************************/ |
4141 | | |
4142 | | OGRLayer *PDS4Dataset::ICreateLayer(const char *pszName, |
4143 | | const OGRGeomFieldDefn *poGeomFieldDefn, |
4144 | | CSLConstList papszOptions) |
4145 | 2.37k | { |
4146 | 2.37k | const char *pszTableType = |
4147 | 2.37k | CSLFetchNameValueDef(papszOptions, "TABLE_TYPE", "DELIMITED"); |
4148 | 2.37k | if (!EQUAL(pszTableType, "CHARACTER") && !EQUAL(pszTableType, "BINARY") && |
4149 | 2.37k | !EQUAL(pszTableType, "DELIMITED")) |
4150 | 0 | { |
4151 | 0 | return nullptr; |
4152 | 0 | } |
4153 | | |
4154 | 2.37k | const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone; |
4155 | 2.37k | const auto poSpatialRef = |
4156 | 2.37k | poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr; |
4157 | | |
4158 | 2.37k | const char *pszExt = EQUAL(pszTableType, "CHARACTER") ? "dat" |
4159 | 2.37k | : EQUAL(pszTableType, "BINARY") ? "bin" |
4160 | 2.37k | : "csv"; |
4161 | | |
4162 | 2.37k | bool bSameDirectory = |
4163 | 2.37k | CPLTestBool(CSLFetchNameValueDef(papszOptions, "SAME_DIRECTORY", "NO")); |
4164 | | |
4165 | 2.37k | std::string osBasename(pszName); |
4166 | 2.37k | for (char &ch : osBasename) |
4167 | 57.4k | { |
4168 | 57.4k | if (!isalnum(static_cast<unsigned char>(ch)) && |
4169 | 57.4k | static_cast<unsigned>(ch) <= 127) |
4170 | 9.66k | ch = '_'; |
4171 | 57.4k | } |
4172 | | |
4173 | 2.37k | CPLString osFullFilename; |
4174 | 2.37k | if (bSameDirectory) |
4175 | 0 | { |
4176 | 0 | osFullFilename = |
4177 | 0 | CPLFormFilenameSafe(CPLGetPathSafe(m_osXMLFilename.c_str()).c_str(), |
4178 | 0 | osBasename.c_str(), pszExt); |
4179 | 0 | VSIStatBufL sStat; |
4180 | 0 | if (VSIStatL(osFullFilename, &sStat) == 0) |
4181 | 0 | { |
4182 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4183 | 0 | "%s already exists. Please delete it before, or " |
4184 | 0 | "rename the layer", |
4185 | 0 | osFullFilename.c_str()); |
4186 | 0 | return nullptr; |
4187 | 0 | } |
4188 | 0 | } |
4189 | 2.37k | else |
4190 | 2.37k | { |
4191 | 2.37k | CPLString osDirectory = CPLFormFilenameSafe( |
4192 | 2.37k | CPLGetPathSafe(m_osXMLFilename).c_str(), |
4193 | 2.37k | CPLGetBasenameSafe(m_osXMLFilename).c_str(), nullptr); |
4194 | 2.37k | VSIStatBufL sStat; |
4195 | 2.37k | if (VSIStatL(osDirectory, &sStat) != 0 && |
4196 | 2.37k | VSIMkdir(osDirectory, 0755) != 0) |
4197 | 0 | { |
4198 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot create directory %s", |
4199 | 0 | osDirectory.c_str()); |
4200 | 0 | return nullptr; |
4201 | 0 | } |
4202 | 2.37k | osFullFilename = |
4203 | 2.37k | CPLFormFilenameSafe(osDirectory, osBasename.c_str(), pszExt); |
4204 | 2.37k | } |
4205 | | |
4206 | 2.37k | if (EQUAL(pszTableType, "DELIMITED")) |
4207 | 2.37k | { |
4208 | 2.37k | std::unique_ptr<PDS4DelimitedTable> poLayer( |
4209 | 2.37k | new PDS4DelimitedTable(this, pszName, osFullFilename)); |
4210 | 2.37k | if (!poLayer->InitializeNewLayer(poSpatialRef, false, eGType, |
4211 | 2.37k | papszOptions)) |
4212 | 0 | { |
4213 | 0 | return nullptr; |
4214 | 0 | } |
4215 | 2.37k | std::unique_ptr<PDS4EditableLayer> poEditableLayer( |
4216 | 2.37k | new PDS4EditableLayer(poLayer.release())); |
4217 | 2.37k | m_apoLayers.push_back(std::move(poEditableLayer)); |
4218 | 2.37k | } |
4219 | 0 | else |
4220 | 0 | { |
4221 | 0 | std::unique_ptr<PDS4FixedWidthTable> poLayer( |
4222 | 0 | EQUAL(pszTableType, "CHARACTER") |
4223 | 0 | ? static_cast<PDS4FixedWidthTable *>( |
4224 | 0 | new PDS4TableCharacter(this, pszName, osFullFilename)) |
4225 | 0 | : static_cast<PDS4FixedWidthTable *>( |
4226 | 0 | new PDS4TableBinary(this, pszName, osFullFilename))); |
4227 | 0 | if (!poLayer->InitializeNewLayer(poSpatialRef, false, eGType, |
4228 | 0 | papszOptions)) |
4229 | 0 | { |
4230 | 0 | return nullptr; |
4231 | 0 | } |
4232 | 0 | std::unique_ptr<PDS4EditableLayer> poEditableLayer( |
4233 | 0 | new PDS4EditableLayer(poLayer.release())); |
4234 | 0 | m_apoLayers.push_back(std::move(poEditableLayer)); |
4235 | 0 | } |
4236 | 2.37k | return m_apoLayers.back().get(); |
4237 | 2.37k | } |
4238 | | |
4239 | | /************************************************************************/ |
4240 | | /* TestCapability() */ |
4241 | | /************************************************************************/ |
4242 | | |
4243 | | int PDS4Dataset::TestCapability(const char *pszCap) |
4244 | 5.00k | { |
4245 | 5.00k | if (EQUAL(pszCap, ODsCCreateLayer)) |
4246 | 2.37k | return eAccess == GA_Update; |
4247 | 2.63k | else if (EQUAL(pszCap, ODsCZGeometries)) |
4248 | 0 | return TRUE; |
4249 | 2.63k | else |
4250 | 2.63k | return FALSE; |
4251 | 5.00k | } |
4252 | | |
4253 | | /************************************************************************/ |
4254 | | /* Create() */ |
4255 | | /************************************************************************/ |
4256 | | |
4257 | | GDALDataset *PDS4Dataset::Create(const char *pszFilename, int nXSize, |
4258 | | int nYSize, int nBandsIn, GDALDataType eType, |
4259 | | char **papszOptions) |
4260 | 61 | { |
4261 | 61 | return CreateInternal(pszFilename, nullptr, nXSize, nYSize, nBandsIn, eType, |
4262 | 61 | papszOptions); |
4263 | 61 | } |
4264 | | |
4265 | | /************************************************************************/ |
4266 | | /* CreateInternal() */ |
4267 | | /************************************************************************/ |
4268 | | |
4269 | | PDS4Dataset *PDS4Dataset::CreateInternal(const char *pszFilename, |
4270 | | GDALDataset *poSrcDS, int nXSize, |
4271 | | int nYSize, int nBandsIn, |
4272 | | GDALDataType eType, |
4273 | | const char *const *papszOptionsIn) |
4274 | 98 | { |
4275 | 98 | CPLStringList aosOptions(papszOptionsIn); |
4276 | | |
4277 | 98 | if (nXSize == 0 && nYSize == 0 && nBandsIn == 0 && eType == GDT_Unknown) |
4278 | 61 | { |
4279 | | // Vector file creation |
4280 | 61 | PDS4Dataset *poDS = new PDS4Dataset(); |
4281 | 61 | poDS->SetDescription(pszFilename); |
4282 | 61 | poDS->nRasterXSize = 0; |
4283 | 61 | poDS->nRasterYSize = 0; |
4284 | 61 | poDS->eAccess = GA_Update; |
4285 | 61 | poDS->m_osXMLFilename = pszFilename; |
4286 | 61 | poDS->m_bCreateHeader = true; |
4287 | 61 | poDS->m_bStripFileAreaObservationalFromTemplate = true; |
4288 | 61 | poDS->m_papszCreationOptions = CSLDuplicate(aosOptions.List()); |
4289 | 61 | poDS->m_bUseSrcLabel = aosOptions.FetchBool("USE_SRC_LABEL", true); |
4290 | 61 | return poDS; |
4291 | 61 | } |
4292 | | |
4293 | 37 | if (nXSize == 0) |
4294 | 0 | return nullptr; |
4295 | | |
4296 | 37 | if (!(eType == GDT_Byte || eType == GDT_Int8 || eType == GDT_Int16 || |
4297 | 37 | eType == GDT_UInt16 || eType == GDT_Int32 || eType == GDT_UInt32 || |
4298 | 37 | eType == GDT_Float32 || eType == GDT_Float64 || |
4299 | 37 | eType == GDT_CFloat32 || eType == GDT_CFloat64)) |
4300 | 0 | { |
4301 | 0 | CPLError( |
4302 | 0 | CE_Failure, CPLE_NotSupported, |
4303 | 0 | "The PDS4 driver does not supporting creating files of type %s.", |
4304 | 0 | GDALGetDataTypeName(eType)); |
4305 | 0 | return nullptr; |
4306 | 0 | } |
4307 | | |
4308 | 37 | if (nBandsIn == 0) |
4309 | 0 | { |
4310 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Invalid number of bands"); |
4311 | 0 | return nullptr; |
4312 | 0 | } |
4313 | | |
4314 | 37 | const char *pszArrayType = |
4315 | 37 | aosOptions.FetchNameValueDef("ARRAY_TYPE", "Array_3D_Image"); |
4316 | 37 | const bool bIsArray2D = STARTS_WITH(pszArrayType, "Array_2D"); |
4317 | 37 | if (nBandsIn > 1 && bIsArray2D) |
4318 | 0 | { |
4319 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
4320 | 0 | "ARRAY_TYPE=%s is not supported for a multi-band raster", |
4321 | 0 | pszArrayType); |
4322 | 0 | return nullptr; |
4323 | 0 | } |
4324 | | |
4325 | | /* -------------------------------------------------------------------- */ |
4326 | | /* Compute pixel, line and band offsets */ |
4327 | | /* -------------------------------------------------------------------- */ |
4328 | 37 | const int nItemSize = GDALGetDataTypeSizeBytes(eType); |
4329 | 37 | int nLineOffset, nPixelOffset; |
4330 | 37 | vsi_l_offset nBandOffset; |
4331 | | |
4332 | 37 | const char *pszInterleave = |
4333 | 37 | aosOptions.FetchNameValueDef("INTERLEAVE", "BSQ"); |
4334 | 37 | if (bIsArray2D) |
4335 | 0 | pszInterleave = "BIP"; |
4336 | | |
4337 | 37 | if (EQUAL(pszInterleave, "BIP")) |
4338 | 23 | { |
4339 | 23 | nPixelOffset = nItemSize * nBandsIn; |
4340 | 23 | if (nPixelOffset > INT_MAX / nBandsIn) |
4341 | 0 | { |
4342 | 0 | return nullptr; |
4343 | 0 | } |
4344 | 23 | nLineOffset = nPixelOffset * nXSize; |
4345 | 23 | nBandOffset = nItemSize; |
4346 | 23 | } |
4347 | 14 | else if (EQUAL(pszInterleave, "BSQ")) |
4348 | 14 | { |
4349 | 14 | nPixelOffset = nItemSize; |
4350 | 14 | if (nPixelOffset > INT_MAX / nXSize) |
4351 | 0 | { |
4352 | 0 | return nullptr; |
4353 | 0 | } |
4354 | 14 | nLineOffset = nPixelOffset * nXSize; |
4355 | 14 | nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nYSize; |
4356 | 14 | } |
4357 | 0 | else if (EQUAL(pszInterleave, "BIL")) |
4358 | 0 | { |
4359 | 0 | nPixelOffset = nItemSize; |
4360 | 0 | if (nPixelOffset > INT_MAX / nBandsIn || |
4361 | 0 | nPixelOffset * nBandsIn > INT_MAX / nXSize) |
4362 | 0 | { |
4363 | 0 | return nullptr; |
4364 | 0 | } |
4365 | 0 | nLineOffset = nItemSize * nBandsIn * nXSize; |
4366 | 0 | nBandOffset = static_cast<vsi_l_offset>(nItemSize) * nXSize; |
4367 | 0 | } |
4368 | 0 | else |
4369 | 0 | { |
4370 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Invalid value for INTERLEAVE"); |
4371 | 0 | return nullptr; |
4372 | 0 | } |
4373 | | |
4374 | 37 | const char *pszImageFormat = |
4375 | 37 | aosOptions.FetchNameValueDef("IMAGE_FORMAT", "RAW"); |
4376 | 37 | const char *pszImageExtension = aosOptions.FetchNameValueDef( |
4377 | 37 | "IMAGE_EXTENSION", EQUAL(pszImageFormat, "RAW") ? "img" : "tif"); |
4378 | 37 | CPLString osImageFilename(aosOptions.FetchNameValueDef( |
4379 | 37 | "IMAGE_FILENAME", |
4380 | 37 | CPLResetExtensionSafe(pszFilename, pszImageExtension).c_str())); |
4381 | | |
4382 | 37 | const bool bAppend = aosOptions.FetchBool("APPEND_SUBDATASET", false); |
4383 | 37 | if (bAppend) |
4384 | 0 | { |
4385 | 0 | GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); |
4386 | 0 | auto poExistingPDS4 = static_cast<PDS4Dataset *>(Open(&oOpenInfo)); |
4387 | 0 | if (!poExistingPDS4) |
4388 | 0 | { |
4389 | 0 | return nullptr; |
4390 | 0 | } |
4391 | 0 | osImageFilename = poExistingPDS4->m_osImageFilename; |
4392 | 0 | delete poExistingPDS4; |
4393 | |
|
4394 | 0 | auto poImageDS = GDALDataset::FromHandle(GDALOpenEx( |
4395 | 0 | osImageFilename, GDAL_OF_RASTER, nullptr, nullptr, nullptr)); |
4396 | 0 | if (poImageDS && poImageDS->GetDriver() && |
4397 | 0 | EQUAL(poImageDS->GetDriver()->GetDescription(), "GTiff")) |
4398 | 0 | { |
4399 | 0 | pszImageFormat = "GEOTIFF"; |
4400 | 0 | } |
4401 | 0 | delete poImageDS; |
4402 | 0 | } |
4403 | | |
4404 | 37 | GDALDataset *poExternalDS = nullptr; |
4405 | 37 | VSILFILE *fpImage = nullptr; |
4406 | 37 | vsi_l_offset nBaseOffset = 0; |
4407 | 37 | bool bIsLSB = true; |
4408 | 37 | CPLString osHeaderParsingStandard; |
4409 | 37 | const bool bCreateLabelOnly = |
4410 | 37 | aosOptions.FetchBool("CREATE_LABEL_ONLY", false); |
4411 | 37 | if (bCreateLabelOnly) |
4412 | 0 | { |
4413 | 0 | if (poSrcDS == nullptr) |
4414 | 0 | { |
4415 | 0 | CPLError( |
4416 | 0 | CE_Failure, CPLE_AppDefined, |
4417 | 0 | "CREATE_LABEL_ONLY is only compatible of CreateCopy() mode"); |
4418 | 0 | return nullptr; |
4419 | 0 | } |
4420 | 0 | RawBinaryLayout sLayout; |
4421 | 0 | if (!poSrcDS->GetRawBinaryLayout(sLayout)) |
4422 | 0 | { |
4423 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4424 | 0 | "Source dataset is not compatible of a raw binary format"); |
4425 | 0 | return nullptr; |
4426 | 0 | } |
4427 | 0 | if ((nBandsIn > 1 && |
4428 | 0 | sLayout.eInterleaving == RawBinaryLayout::Interleaving::UNKNOWN) || |
4429 | 0 | (nBandsIn == 1 && |
4430 | 0 | !(sLayout.nPixelOffset == nItemSize && |
4431 | 0 | sLayout.nLineOffset == sLayout.nPixelOffset * nXSize))) |
4432 | 0 | { |
4433 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4434 | 0 | "Source dataset has an interleaving not handled in PDS4"); |
4435 | 0 | return nullptr; |
4436 | 0 | } |
4437 | 0 | fpImage = VSIFOpenL(sLayout.osRawFilename.c_str(), "rb"); |
4438 | 0 | if (fpImage == nullptr) |
4439 | 0 | { |
4440 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot open raw image %s", |
4441 | 0 | sLayout.osRawFilename.c_str()); |
4442 | 0 | return nullptr; |
4443 | 0 | } |
4444 | 0 | osImageFilename = sLayout.osRawFilename; |
4445 | 0 | if (nBandsIn == 1 || |
4446 | 0 | sLayout.eInterleaving == RawBinaryLayout::Interleaving::BIP) |
4447 | 0 | pszInterleave = "BIP"; |
4448 | 0 | else if (sLayout.eInterleaving == RawBinaryLayout::Interleaving::BIL) |
4449 | 0 | pszInterleave = "BIL"; |
4450 | 0 | else |
4451 | 0 | pszInterleave = "BSQ"; |
4452 | 0 | nBaseOffset = sLayout.nImageOffset; |
4453 | 0 | nPixelOffset = static_cast<int>(sLayout.nPixelOffset); |
4454 | 0 | nLineOffset = static_cast<int>(sLayout.nLineOffset); |
4455 | 0 | nBandOffset = static_cast<vsi_l_offset>(sLayout.nBandOffset); |
4456 | 0 | bIsLSB = sLayout.bLittleEndianOrder; |
4457 | 0 | auto poSrcDriver = poSrcDS->GetDriver(); |
4458 | 0 | if (poSrcDriver) |
4459 | 0 | { |
4460 | 0 | auto pszDriverName = poSrcDriver->GetDescription(); |
4461 | 0 | if (EQUAL(pszDriverName, "GTiff")) |
4462 | 0 | { |
4463 | 0 | GByte abySignature[4] = {0}; |
4464 | 0 | VSIFReadL(abySignature, 1, 4, fpImage); |
4465 | 0 | const bool bBigTIFF = |
4466 | 0 | abySignature[2] == 43 || abySignature[3] == 43; |
4467 | 0 | osHeaderParsingStandard = |
4468 | 0 | bBigTIFF ? BIGTIFF_GEOTIFF_STRING : TIFF_GEOTIFF_STRING; |
4469 | 0 | } |
4470 | 0 | else if (EQUAL(pszDriverName, "ISIS3")) |
4471 | 0 | { |
4472 | 0 | osHeaderParsingStandard = "ISIS3"; |
4473 | 0 | } |
4474 | 0 | else if (EQUAL(pszDriverName, "VICAR")) |
4475 | 0 | { |
4476 | 0 | osHeaderParsingStandard = "VICAR2"; |
4477 | 0 | } |
4478 | 0 | else if (EQUAL(pszDriverName, "PDS")) |
4479 | 0 | { |
4480 | 0 | osHeaderParsingStandard = "PDS3"; |
4481 | 0 | } |
4482 | 0 | else if (EQUAL(pszDriverName, "FITS")) |
4483 | 0 | { |
4484 | 0 | osHeaderParsingStandard = "FITS 3.0"; |
4485 | 0 | aosOptions.SetNameValue("VAR_VERTICAL_DISPLAY_DIRECTION", |
4486 | 0 | "Bottom to Top"); |
4487 | 0 | } |
4488 | 0 | } |
4489 | 0 | } |
4490 | 37 | else if (EQUAL(pszImageFormat, "GEOTIFF")) |
4491 | 0 | { |
4492 | 0 | if (EQUAL(pszInterleave, "BIL")) |
4493 | 0 | { |
4494 | 0 | if (aosOptions.FetchBool("@INTERLEAVE_ADDED_AUTOMATICALLY", false)) |
4495 | 0 | { |
4496 | 0 | pszInterleave = "BSQ"; |
4497 | 0 | } |
4498 | 0 | else |
4499 | 0 | { |
4500 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4501 | 0 | "INTERLEAVE=BIL not supported for GeoTIFF in PDS4"); |
4502 | 0 | return nullptr; |
4503 | 0 | } |
4504 | 0 | } |
4505 | 0 | GDALDriver *poDrv = |
4506 | 0 | static_cast<GDALDriver *>(GDALGetDriverByName("GTiff")); |
4507 | 0 | if (poDrv == nullptr) |
4508 | 0 | { |
4509 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GTiff driver"); |
4510 | 0 | return nullptr; |
4511 | 0 | } |
4512 | 0 | char **papszGTiffOptions = nullptr; |
4513 | | #ifdef notdef |
4514 | | // In practice I can't see which option we can really use |
4515 | | const char *pszGTiffOptions = |
4516 | | CSLFetchNameValueDef(papszOptions, "GEOTIFF_OPTIONS", ""); |
4517 | | char **papszTokens = CSLTokenizeString2(pszGTiffOptions, ",", 0); |
4518 | | if (CPLFetchBool(papszTokens, "TILED", false)) |
4519 | | { |
4520 | | CSLDestroy(papszTokens); |
4521 | | CPLError(CE_Failure, CPLE_AppDefined, |
4522 | | "Tiled GeoTIFF is not supported for PDS4"); |
4523 | | return NULL; |
4524 | | } |
4525 | | if (!EQUAL(CSLFetchNameValueDef(papszTokens, "COMPRESS", "NONE"), |
4526 | | "NONE")) |
4527 | | { |
4528 | | CSLDestroy(papszTokens); |
4529 | | CPLError(CE_Failure, CPLE_AppDefined, |
4530 | | "Compressed GeoTIFF is not supported for PDS4"); |
4531 | | return NULL; |
4532 | | } |
4533 | | papszGTiffOptions = |
4534 | | CSLSetNameValue(papszGTiffOptions, "ENDIANNESS", "LITTLE"); |
4535 | | for (int i = 0; papszTokens[i] != NULL; i++) |
4536 | | { |
4537 | | papszGTiffOptions = CSLAddString(papszGTiffOptions, papszTokens[i]); |
4538 | | } |
4539 | | CSLDestroy(papszTokens); |
4540 | | #endif |
4541 | |
|
4542 | 0 | papszGTiffOptions = |
4543 | 0 | CSLSetNameValue(papszGTiffOptions, "INTERLEAVE", |
4544 | 0 | EQUAL(pszInterleave, "BSQ") ? "BAND" : "PIXEL"); |
4545 | | // Will make sure that our blocks at nodata are not optimized |
4546 | | // away but indeed well written |
4547 | 0 | papszGTiffOptions = CSLSetNameValue( |
4548 | 0 | papszGTiffOptions, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "YES"); |
4549 | 0 | if (nBandsIn > 1 && EQUAL(pszInterleave, "BSQ")) |
4550 | 0 | { |
4551 | 0 | papszGTiffOptions = |
4552 | 0 | CSLSetNameValue(papszGTiffOptions, "BLOCKYSIZE", "1"); |
4553 | 0 | } |
4554 | |
|
4555 | 0 | if (bAppend) |
4556 | 0 | { |
4557 | 0 | papszGTiffOptions = |
4558 | 0 | CSLAddString(papszGTiffOptions, "APPEND_SUBDATASET=YES"); |
4559 | 0 | } |
4560 | |
|
4561 | 0 | poExternalDS = poDrv->Create(osImageFilename, nXSize, nYSize, nBandsIn, |
4562 | 0 | eType, papszGTiffOptions); |
4563 | 0 | CSLDestroy(papszGTiffOptions); |
4564 | 0 | if (poExternalDS == nullptr) |
4565 | 0 | { |
4566 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", |
4567 | 0 | osImageFilename.c_str()); |
4568 | 0 | return nullptr; |
4569 | 0 | } |
4570 | 0 | } |
4571 | 37 | else |
4572 | 37 | { |
4573 | 37 | fpImage = VSIFOpenL( |
4574 | 37 | osImageFilename, |
4575 | 37 | bAppend ? "rb+" |
4576 | 37 | : VSISupportsRandomWrite(osImageFilename.c_str(), true) ? "wb+" |
4577 | 37 | : "wb"); |
4578 | 37 | if (fpImage == nullptr) |
4579 | 0 | { |
4580 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", |
4581 | 0 | osImageFilename.c_str()); |
4582 | 0 | return nullptr; |
4583 | 0 | } |
4584 | 37 | if (bAppend) |
4585 | 0 | { |
4586 | 0 | VSIFSeekL(fpImage, 0, SEEK_END); |
4587 | 0 | nBaseOffset = VSIFTellL(fpImage); |
4588 | 0 | } |
4589 | 37 | } |
4590 | | |
4591 | 37 | auto poDS = std::make_unique<PDS4Dataset>(); |
4592 | 37 | poDS->SetDescription(pszFilename); |
4593 | 37 | poDS->m_bMustInitImageFile = true; |
4594 | 37 | poDS->m_fpImage = fpImage; |
4595 | 37 | poDS->m_nBaseOffset = nBaseOffset; |
4596 | 37 | poDS->m_poExternalDS = poExternalDS; |
4597 | 37 | poDS->nRasterXSize = nXSize; |
4598 | 37 | poDS->nRasterYSize = nYSize; |
4599 | 37 | poDS->eAccess = GA_Update; |
4600 | 37 | poDS->m_osImageFilename = std::move(osImageFilename); |
4601 | 37 | poDS->m_bCreateHeader = true; |
4602 | 37 | poDS->m_bStripFileAreaObservationalFromTemplate = true; |
4603 | 37 | poDS->m_osInterleave = pszInterleave; |
4604 | 37 | poDS->m_papszCreationOptions = CSLDuplicate(aosOptions.List()); |
4605 | 37 | poDS->m_bUseSrcLabel = aosOptions.FetchBool("USE_SRC_LABEL", true); |
4606 | 37 | poDS->m_bIsLSB = bIsLSB; |
4607 | 37 | poDS->m_osHeaderParsingStandard = std::move(osHeaderParsingStandard); |
4608 | 37 | poDS->m_bCreatedFromExistingBinaryFile = bCreateLabelOnly; |
4609 | | |
4610 | 37 | if (EQUAL(pszInterleave, "BIP")) |
4611 | 23 | { |
4612 | 23 | poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", |
4613 | 23 | "IMAGE_STRUCTURE"); |
4614 | 23 | } |
4615 | 14 | else if (EQUAL(pszInterleave, "BSQ")) |
4616 | 14 | { |
4617 | 14 | poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "BAND", |
4618 | 14 | "IMAGE_STRUCTURE"); |
4619 | 14 | } |
4620 | | |
4621 | 916 | for (int i = 0; i < nBandsIn; i++) |
4622 | 879 | { |
4623 | 879 | if (poDS->m_poExternalDS != nullptr) |
4624 | 0 | { |
4625 | 0 | auto poBand = std::make_unique<PDS4WrapperRasterBand>( |
4626 | 0 | poDS->m_poExternalDS->GetRasterBand(i + 1)); |
4627 | 0 | poDS->SetBand(i + 1, std::move(poBand)); |
4628 | 0 | } |
4629 | 879 | else |
4630 | 879 | { |
4631 | 879 | auto poBand = std::make_unique<PDS4RawRasterBand>( |
4632 | 879 | poDS.get(), i + 1, poDS->m_fpImage, |
4633 | 879 | poDS->m_nBaseOffset + nBandOffset * i, nPixelOffset, |
4634 | 879 | nLineOffset, eType, |
4635 | 879 | bIsLSB ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN |
4636 | 879 | : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN); |
4637 | 879 | poDS->SetBand(i + 1, std::move(poBand)); |
4638 | 879 | } |
4639 | 879 | } |
4640 | | |
4641 | 37 | return poDS.release(); |
4642 | 37 | } |
4643 | | |
4644 | | /************************************************************************/ |
4645 | | /* PDS4GetUnderlyingDataset() */ |
4646 | | /************************************************************************/ |
4647 | | |
4648 | | static GDALDataset *PDS4GetUnderlyingDataset(GDALDataset *poSrcDS) |
4649 | 37 | { |
4650 | 37 | if (poSrcDS->GetDriver() != nullptr && |
4651 | 37 | poSrcDS->GetDriver() == GDALGetDriverByName("VRT")) |
4652 | 37 | { |
4653 | 37 | VRTDataset *poVRTDS = reinterpret_cast<VRTDataset *>(poSrcDS); |
4654 | 37 | poSrcDS = poVRTDS->GetSingleSimpleSource(); |
4655 | 37 | } |
4656 | | |
4657 | 37 | return poSrcDS; |
4658 | 37 | } |
4659 | | |
4660 | | /************************************************************************/ |
4661 | | /* CreateCopy() */ |
4662 | | /************************************************************************/ |
4663 | | |
4664 | | GDALDataset *PDS4Dataset::CreateCopy(const char *pszFilename, |
4665 | | GDALDataset *poSrcDS, int bStrict, |
4666 | | char **papszOptions, |
4667 | | GDALProgressFunc pfnProgress, |
4668 | | void *pProgressData) |
4669 | 37 | { |
4670 | 37 | const char *pszImageFormat = |
4671 | 37 | CSLFetchNameValueDef(papszOptions, "IMAGE_FORMAT", "RAW"); |
4672 | 37 | GDALDataset *poSrcUnderlyingDS = PDS4GetUnderlyingDataset(poSrcDS); |
4673 | 37 | if (poSrcUnderlyingDS == nullptr) |
4674 | 37 | poSrcUnderlyingDS = poSrcDS; |
4675 | 37 | if (EQUAL(pszImageFormat, "GEOTIFF") && |
4676 | 37 | strcmp(poSrcUnderlyingDS->GetDescription(), |
4677 | 0 | CSLFetchNameValueDef( |
4678 | 0 | papszOptions, "IMAGE_FILENAME", |
4679 | 0 | CPLResetExtensionSafe(pszFilename, "tif").c_str())) == 0) |
4680 | 0 | { |
4681 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
4682 | 0 | "Output file has same name as input file"); |
4683 | 0 | return nullptr; |
4684 | 0 | } |
4685 | 37 | if (poSrcDS->GetRasterCount() == 0) |
4686 | 0 | { |
4687 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count"); |
4688 | 0 | return nullptr; |
4689 | 0 | } |
4690 | | |
4691 | 37 | const bool bAppend = CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false); |
4692 | 37 | if (bAppend) |
4693 | 0 | { |
4694 | 0 | GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); |
4695 | 0 | GDALDataset *poExistingDS = Open(&oOpenInfo); |
4696 | 0 | if (poExistingDS) |
4697 | 0 | { |
4698 | 0 | double adfExistingGT[6] = {0.0}; |
4699 | 0 | const bool bExistingHasGT = |
4700 | 0 | poExistingDS->GetGeoTransform(adfExistingGT) == CE_None; |
4701 | 0 | double adfGeoTransform[6] = {0.0}; |
4702 | 0 | const bool bSrcHasGT = |
4703 | 0 | poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None; |
4704 | |
|
4705 | 0 | OGRSpatialReference oExistingSRS; |
4706 | 0 | OGRSpatialReference oSrcSRS; |
4707 | 0 | const char *pszExistingSRS = poExistingDS->GetProjectionRef(); |
4708 | 0 | const char *pszSrcSRS = poSrcDS->GetProjectionRef(); |
4709 | 0 | CPLString osExistingProj4; |
4710 | 0 | if (pszExistingSRS && pszExistingSRS[0]) |
4711 | 0 | { |
4712 | 0 | oExistingSRS.SetFromUserInput( |
4713 | 0 | pszExistingSRS, |
4714 | 0 | OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()); |
4715 | 0 | char *pszExistingProj4 = nullptr; |
4716 | 0 | oExistingSRS.exportToProj4(&pszExistingProj4); |
4717 | 0 | if (pszExistingProj4) |
4718 | 0 | osExistingProj4 = pszExistingProj4; |
4719 | 0 | CPLFree(pszExistingProj4); |
4720 | 0 | } |
4721 | 0 | CPLString osSrcProj4; |
4722 | 0 | if (pszSrcSRS && pszSrcSRS[0]) |
4723 | 0 | { |
4724 | 0 | oSrcSRS.SetFromUserInput( |
4725 | 0 | pszSrcSRS, |
4726 | 0 | OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()); |
4727 | 0 | char *pszSrcProj4 = nullptr; |
4728 | 0 | oSrcSRS.exportToProj4(&pszSrcProj4); |
4729 | 0 | if (pszSrcProj4) |
4730 | 0 | osSrcProj4 = pszSrcProj4; |
4731 | 0 | CPLFree(pszSrcProj4); |
4732 | 0 | } |
4733 | |
|
4734 | 0 | delete poExistingDS; |
4735 | |
|
4736 | 0 | const auto maxRelErrorGT = |
4737 | 0 | [](const double adfGT1[6], const double adfGT2[6]) |
4738 | 0 | { |
4739 | 0 | double maxRelError = 0.0; |
4740 | 0 | for (int i = 0; i < 6; i++) |
4741 | 0 | { |
4742 | 0 | if (adfGT1[i] == 0.0) |
4743 | 0 | { |
4744 | 0 | maxRelError = |
4745 | 0 | std::max(maxRelError, std::abs(adfGT2[i])); |
4746 | 0 | } |
4747 | 0 | else |
4748 | 0 | { |
4749 | 0 | maxRelError = std::max(maxRelError, |
4750 | 0 | std::abs(adfGT2[i] - adfGT1[i]) / |
4751 | 0 | std::abs(adfGT1[i])); |
4752 | 0 | } |
4753 | 0 | } |
4754 | 0 | return maxRelError; |
4755 | 0 | }; |
4756 | |
|
4757 | 0 | if ((bExistingHasGT && !bSrcHasGT) || |
4758 | 0 | (!bExistingHasGT && bSrcHasGT) || |
4759 | 0 | (bExistingHasGT && bSrcHasGT && |
4760 | 0 | maxRelErrorGT(adfExistingGT, adfGeoTransform) > 1e-10)) |
4761 | 0 | { |
4762 | 0 | CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported, |
4763 | 0 | "Appending to a dataset with a different " |
4764 | 0 | "geotransform is not supported"); |
4765 | 0 | if (bStrict) |
4766 | 0 | return nullptr; |
4767 | 0 | } |
4768 | | // Do proj string comparison, as it is unlikely that |
4769 | | // OGRSpatialReference::IsSame() will lead to identical reasons due |
4770 | | // to PDS changing CRS names, etc... |
4771 | 0 | if (osExistingProj4 != osSrcProj4) |
4772 | 0 | { |
4773 | 0 | CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported, |
4774 | 0 | "Appending to a dataset with a different " |
4775 | 0 | "coordinate reference system is not supported"); |
4776 | 0 | if (bStrict) |
4777 | 0 | return nullptr; |
4778 | 0 | } |
4779 | 0 | } |
4780 | 0 | } |
4781 | | |
4782 | 37 | const int nXSize = poSrcDS->GetRasterXSize(); |
4783 | 37 | const int nYSize = poSrcDS->GetRasterYSize(); |
4784 | 37 | const int nBands = poSrcDS->GetRasterCount(); |
4785 | 37 | GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); |
4786 | 37 | PDS4Dataset *poDS = CreateInternal(pszFilename, poSrcDS, nXSize, nYSize, |
4787 | 37 | nBands, eType, papszOptions); |
4788 | 37 | if (poDS == nullptr) |
4789 | 0 | return nullptr; |
4790 | | |
4791 | 37 | double adfGeoTransform[6] = {0.0}; |
4792 | 37 | if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None && |
4793 | 37 | (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 || |
4794 | 30 | adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 || |
4795 | 30 | adfGeoTransform[4] != 0.0 || adfGeoTransform[5] != 1.0)) |
4796 | 29 | { |
4797 | 29 | poDS->SetGeoTransform(adfGeoTransform); |
4798 | 29 | } |
4799 | | |
4800 | 37 | if (poSrcDS->GetProjectionRef() != nullptr && |
4801 | 37 | strlen(poSrcDS->GetProjectionRef()) > 0) |
4802 | 7 | { |
4803 | 7 | poDS->SetProjection(poSrcDS->GetProjectionRef()); |
4804 | 7 | } |
4805 | | |
4806 | 916 | for (int i = 1; i <= nBands; i++) |
4807 | 879 | { |
4808 | 879 | int bHasNoData = false; |
4809 | 879 | const double dfNoData = |
4810 | 879 | poSrcDS->GetRasterBand(i)->GetNoDataValue(&bHasNoData); |
4811 | 879 | if (bHasNoData) |
4812 | 365 | poDS->GetRasterBand(i)->SetNoDataValue(dfNoData); |
4813 | | |
4814 | 879 | const double dfOffset = poSrcDS->GetRasterBand(i)->GetOffset(); |
4815 | 879 | if (dfOffset != 0.0) |
4816 | 0 | poDS->GetRasterBand(i)->SetOffset(dfOffset); |
4817 | | |
4818 | 879 | const double dfScale = poSrcDS->GetRasterBand(i)->GetScale(); |
4819 | 879 | if (dfScale != 1.0) |
4820 | 0 | poDS->GetRasterBand(i)->SetScale(dfScale); |
4821 | | |
4822 | 879 | poDS->GetRasterBand(i)->SetUnitType( |
4823 | 879 | poSrcDS->GetRasterBand(i)->GetUnitType()); |
4824 | 879 | } |
4825 | | |
4826 | 37 | if (poDS->m_bUseSrcLabel) |
4827 | 37 | { |
4828 | 37 | char **papszMD_PDS4 = poSrcDS->GetMetadata("xml:PDS4"); |
4829 | 37 | if (papszMD_PDS4 != nullptr) |
4830 | 0 | { |
4831 | 0 | poDS->SetMetadata(papszMD_PDS4, "xml:PDS4"); |
4832 | 0 | } |
4833 | 37 | } |
4834 | | |
4835 | 37 | if (poDS->m_poExternalDS == nullptr) |
4836 | 37 | { |
4837 | | // We don't need to initialize the imagery as we are going to copy it |
4838 | | // completely |
4839 | 37 | poDS->m_bMustInitImageFile = false; |
4840 | 37 | } |
4841 | | |
4842 | 37 | if (!CPLFetchBool(papszOptions, "CREATE_LABEL_ONLY", false)) |
4843 | 37 | { |
4844 | 37 | CPLErr eErr = GDALDatasetCopyWholeRaster(poSrcDS, poDS, nullptr, |
4845 | 37 | pfnProgress, pProgressData); |
4846 | 37 | poDS->FlushCache(false); |
4847 | 37 | if (eErr != CE_None) |
4848 | 15 | { |
4849 | 15 | delete poDS; |
4850 | 15 | return nullptr; |
4851 | 15 | } |
4852 | | |
4853 | 22 | char **papszISIS3MD = poSrcDS->GetMetadata("json:ISIS3"); |
4854 | 22 | if (papszISIS3MD) |
4855 | 0 | { |
4856 | 0 | poDS->SetMetadata(papszISIS3MD, "json:ISIS3"); |
4857 | 0 | } |
4858 | 22 | } |
4859 | | |
4860 | 22 | return poDS; |
4861 | 37 | } |
4862 | | |
4863 | | /************************************************************************/ |
4864 | | /* Delete() */ |
4865 | | /************************************************************************/ |
4866 | | |
4867 | | CPLErr PDS4Dataset::Delete(const char *pszFilename) |
4868 | | |
4869 | 61 | { |
4870 | | /* -------------------------------------------------------------------- */ |
4871 | | /* Collect file list. */ |
4872 | | /* -------------------------------------------------------------------- */ |
4873 | 61 | GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); |
4874 | 61 | auto poDS = |
4875 | 61 | std::unique_ptr<PDS4Dataset>(PDS4Dataset::OpenInternal(&oOpenInfo)); |
4876 | 61 | if (poDS == nullptr) |
4877 | 61 | { |
4878 | 61 | if (CPLGetLastErrorNo() == 0) |
4879 | 61 | CPLError(CE_Failure, CPLE_OpenFailed, |
4880 | 61 | "Unable to open %s to obtain file list.", pszFilename); |
4881 | | |
4882 | 61 | return CE_Failure; |
4883 | 61 | } |
4884 | | |
4885 | 0 | char **papszFileList = poDS->GetFileList(); |
4886 | 0 | CPLString osImageFilename = poDS->m_osImageFilename; |
4887 | 0 | bool bCreatedFromExistingBinaryFile = |
4888 | 0 | poDS->m_bCreatedFromExistingBinaryFile; |
4889 | |
|
4890 | 0 | poDS.reset(); |
4891 | |
|
4892 | 0 | if (CSLCount(papszFileList) == 0) |
4893 | 0 | { |
4894 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
4895 | 0 | "Unable to determine files associated with %s, " |
4896 | 0 | "delete fails.", |
4897 | 0 | pszFilename); |
4898 | 0 | CSLDestroy(papszFileList); |
4899 | 0 | return CE_Failure; |
4900 | 0 | } |
4901 | | |
4902 | | /* -------------------------------------------------------------------- */ |
4903 | | /* Delete all files. */ |
4904 | | /* -------------------------------------------------------------------- */ |
4905 | 0 | CPLErr eErr = CE_None; |
4906 | 0 | for (int i = 0; papszFileList[i] != nullptr; ++i) |
4907 | 0 | { |
4908 | 0 | if (bCreatedFromExistingBinaryFile && |
4909 | 0 | EQUAL(papszFileList[i], osImageFilename)) |
4910 | 0 | { |
4911 | 0 | continue; |
4912 | 0 | } |
4913 | 0 | if (VSIUnlink(papszFileList[i]) != 0) |
4914 | 0 | { |
4915 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Deleting %s failed:\n%s", |
4916 | 0 | papszFileList[i], VSIStrerror(errno)); |
4917 | 0 | eErr = CE_Failure; |
4918 | 0 | } |
4919 | 0 | } |
4920 | |
|
4921 | 0 | CSLDestroy(papszFileList); |
4922 | |
|
4923 | 0 | return eErr; |
4924 | 0 | } |
4925 | | |
4926 | | /************************************************************************/ |
4927 | | /* GDALRegister_PDS4() */ |
4928 | | /************************************************************************/ |
4929 | | |
4930 | | void GDALRegister_PDS4() |
4931 | | |
4932 | 24 | { |
4933 | 24 | if (GDALGetDriverByName(PDS4_DRIVER_NAME) != nullptr) |
4934 | 0 | return; |
4935 | | |
4936 | 24 | GDALDriver *poDriver = new GDALDriver(); |
4937 | 24 | PDS4DriverSetCommonMetadata(poDriver); |
4938 | | |
4939 | 24 | poDriver->pfnOpen = PDS4Dataset::Open; |
4940 | 24 | poDriver->pfnCreate = PDS4Dataset::Create; |
4941 | 24 | poDriver->pfnCreateCopy = PDS4Dataset::CreateCopy; |
4942 | 24 | poDriver->pfnDelete = PDS4Dataset::Delete; |
4943 | | |
4944 | 24 | GetGDALDriverManager()->RegisterDriver(poDriver); |
4945 | 24 | } |