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