Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Hierarchical Data Format Release 5 (HDF5)
4 : * Purpose: Implementation of HDF5 HDFEOS parser
5 : * Author: Even Rouault
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_error.h"
14 : #include "nasakeywordhandler.h"
15 :
16 : #include "hdf5eosparser.h"
17 :
18 : #include <cstring>
19 : #include <utility>
20 :
21 : /************************************************************************/
22 : /* HasHDFEOS() */
23 : /************************************************************************/
24 :
25 277 : bool HDF5EOSParser::HasHDFEOS(hid_t hRoot)
26 : {
27 277 : hsize_t numObjs = 0;
28 277 : H5Gget_num_objs(hRoot, &numObjs);
29 277 : bool bFound = false;
30 1031 : for (hsize_t i = 0; i < numObjs; ++i)
31 : {
32 : char szName[128];
33 : ssize_t nLen =
34 780 : H5Gget_objname_by_idx(hRoot, i, szName, sizeof(szName) - 1);
35 780 : if (nLen > 0)
36 : {
37 780 : szName[nLen] = 0;
38 780 : if (strcmp(szName, "HDFEOS INFORMATION") == 0)
39 : {
40 26 : bFound = true;
41 26 : break;
42 : }
43 : }
44 : }
45 277 : if (!bFound)
46 251 : return false;
47 :
48 : H5G_stat_t oStatbuf;
49 26 : if (H5Gget_objinfo(hRoot, "HDFEOS INFORMATION", false, &oStatbuf) < 0)
50 0 : return false;
51 :
52 26 : auto hHDFEOSInformation = H5Gopen(hRoot, "HDFEOS INFORMATION");
53 26 : if (hHDFEOSInformation < 0)
54 : {
55 0 : return false;
56 : }
57 26 : H5Gclose(hHDFEOSInformation);
58 26 : return true;
59 : }
60 :
61 : /************************************************************************/
62 : /* Parse() */
63 : /************************************************************************/
64 :
65 26 : bool HDF5EOSParser::Parse(hid_t hRoot)
66 : {
67 26 : auto hHDFEOSInformation = H5Gopen(hRoot, "HDFEOS INFORMATION");
68 26 : if (hHDFEOSInformation < 0)
69 : {
70 0 : return false;
71 : }
72 :
73 26 : const hid_t hArrayId = H5Dopen(hHDFEOSInformation, "StructMetadata.0");
74 26 : if (hArrayId < 0)
75 : {
76 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find StructMetadata.0");
77 0 : H5Gclose(hHDFEOSInformation);
78 0 : return false;
79 : }
80 :
81 26 : const hid_t hAttrSpace = H5Dget_space(hArrayId);
82 26 : const hid_t hAttrTypeID = H5Dget_type(hArrayId);
83 : const hid_t hAttrNativeType =
84 26 : H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT);
85 :
86 : // Fetch StructMetadata.0 content in a std::string
87 52 : std::string osResult;
88 26 : if (H5Tget_class(hAttrNativeType) == H5T_STRING &&
89 52 : !H5Tis_variable_str(hAttrNativeType) &&
90 26 : H5Sget_simple_extent_ndims(hAttrSpace) == 0)
91 : {
92 26 : const auto nSize = H5Tget_size(hAttrNativeType);
93 26 : if (nSize > 10 * 1024 * 1024)
94 : {
95 0 : CPLError(CE_Failure, CPLE_AppDefined,
96 : "Too large HDFEOS INFORMATION.StructMetadata.0");
97 : }
98 : else
99 : {
100 26 : osResult.resize(nSize);
101 26 : H5Dread(hArrayId, hAttrNativeType, H5S_ALL, hAttrSpace, H5P_DEFAULT,
102 26 : &osResult[0]);
103 : }
104 : }
105 : else
106 : {
107 0 : CPLError(CE_Failure, CPLE_AppDefined,
108 : "HDFEOS INFORMATION.StructMetadata.0 not of type string");
109 : }
110 26 : H5Sclose(hAttrSpace);
111 26 : H5Tclose(hAttrNativeType);
112 26 : H5Tclose(hAttrTypeID);
113 :
114 26 : H5Dclose(hArrayId);
115 26 : H5Gclose(hHDFEOSInformation);
116 :
117 26 : if (osResult.empty())
118 0 : return false;
119 :
120 : // Parse StructMetadata.0 with NASAKeywordHandler
121 52 : NASAKeywordHandler oKWHandler;
122 : #ifdef DEBUG
123 26 : CPLDebug("HDF5EOS", "%s", osResult.c_str());
124 : #endif
125 26 : if (!oKWHandler.Parse(osResult.c_str()))
126 : {
127 0 : CPLError(CE_Failure, CPLE_AppDefined,
128 : "Cannot parse HDFEOS INFORMATION.StructMetadata.0 with "
129 : "NASAKeywordHandler");
130 0 : return false;
131 : }
132 :
133 52 : auto oJsonRoot = oKWHandler.GetJsonObject();
134 78 : auto oGridStructure = oJsonRoot.GetObj("GridStructure");
135 52 : auto oSwathStructure = oJsonRoot.GetObj("SwathStructure");
136 26 : bool bOK = false;
137 : // An empty
138 : // GROUP=GridStructure
139 : // END_GROUP=GridStructure
140 : // will generate 2 keys (_type and END_GROUP)
141 26 : if (oGridStructure.IsValid() && oGridStructure.GetChildren().size() > 2)
142 : {
143 12 : bOK = true;
144 12 : m_eDataModel = DataModel::GRID;
145 12 : ParseGridStructure(oGridStructure);
146 : }
147 28 : else if (oSwathStructure.IsValid() &&
148 28 : oSwathStructure.GetChildren().size() > 2)
149 : {
150 14 : bOK = true;
151 14 : m_eDataModel = DataModel::SWATH;
152 14 : ParseSwathStructure(oSwathStructure);
153 : }
154 :
155 26 : return bOK;
156 : }
157 :
158 : /************************************************************************/
159 : /* GetGTCPProjectionCode() */
160 : /************************************************************************/
161 :
162 12 : static int GetGTCPProjectionCode(const std::string &osProjection)
163 : {
164 12 : const char *const apszGCTPProjections[] = {
165 : "HE5_GCTP_GEO", "HE5_GCTP_UTM", "HE5_GCTP_SPCS",
166 : "HE5_GCTP_ALBERS", "HE5_GCTP_LAMCC", "HE5_GCTP_MERCAT",
167 : "HE5_GCTP_PS", "HE5_GCTP_POLYC", "HE5_GCTP_EQUIDC",
168 : "HE5_GCTP_TM", "HE5_GCTP_STEREO", "HE5_GCTP_LAMAZ",
169 : "HE5_GCTP_AZMEQD", "HE5_GCTP_GNOMON", "HE5_GCTP_ORTHO",
170 : "HE5_GCTP_GVNSP", "HE5_GCTP_SNSOID", "HE5_GCTP_EQRECT",
171 : "HE5_GCTP_MILLER", "HE5_GCTP_VGRINT", "HE5_GCTP_HOM",
172 : "HE5_GCTP_ROBIN", "HE5_GCTP_SOM", "HE5_GCTP_ALASKA",
173 : "HE5_GCTP_GOOD", "HE5_GCTP_MOLL", "HE5_GCTP_IMOLL",
174 : "HE5_GCTP_HAMMER", "HE5_GCTP_WAGIV", "HE5_GCTP_WAGVII",
175 : "HE5_GCTP_OBLEQA"};
176 : // HE5_GCTP_CEA, HE5_GCTP_BCEA, HE5_GCTP_ISINUS not taken
177 : // into account.
178 81 : for (int i = 0; i < static_cast<int>(CPL_ARRAYSIZE(apszGCTPProjections));
179 : ++i)
180 : {
181 81 : if (osProjection == apszGCTPProjections[i])
182 : {
183 12 : return i;
184 : }
185 : }
186 0 : return -1;
187 : }
188 :
189 : /************************************************************************/
190 : /* ParseGridStructure() */
191 : /************************************************************************/
192 :
193 12 : void HDF5EOSParser::ParseGridStructure(const CPLJSONObject &oGridStructure)
194 : {
195 48 : for (const auto &oGrid : oGridStructure.GetChildren())
196 : {
197 36 : if (oGrid.GetType() == CPLJSONObject::Type::Object)
198 : {
199 36 : const auto osGridName = oGrid.GetString("GridName");
200 36 : const auto oDataFields = oGrid.GetObj("DataField");
201 36 : const auto oDimensions = oGrid.GetObj("Dimension");
202 24 : std::map<std::string, int> oMapDimensionNameToSize;
203 24 : auto poGridMetadata = std::make_unique<GridMetadata>();
204 12 : poGridMetadata->osGridName = osGridName;
205 60 : for (const auto &oDimension : oDimensions.GetChildren())
206 : {
207 48 : if (oDimension.GetType() == CPLJSONObject::Type::Object)
208 : {
209 : const auto osDimensionName =
210 72 : oDimension.GetString("DimensionName");
211 24 : int nSize = oDimension.GetInteger("Size");
212 24 : oMapDimensionNameToSize[osDimensionName] = nSize;
213 48 : Dimension oDim;
214 24 : oDim.osName = osDimensionName;
215 24 : oDim.nSize = nSize;
216 24 : poGridMetadata->aoDimensions.push_back(oDim);
217 : }
218 : }
219 :
220 : // Happens for example for products following
221 : // AMSR-E/AMSR2 Unified L3 Daily 12.5 km Brightness Temperatures,
222 : // Sea Ice Concentration, Motion & Snow Depth Polar Grids
223 : // (https://nsidc.org/sites/default/files/au_si12-v001-userguide_1.pdf)
224 : // such as
225 : // https://n5eil01u.ecs.nsidc.org/AMSA/AU_SI12.001/2012.07.02/AMSR_U2_L3_SeaIce12km_B04_20120702.he5
226 12 : const int nXDim = oGrid.GetInteger("XDim", 0);
227 12 : const int nYDim = oGrid.GetInteger("YDim", 0);
228 12 : if (poGridMetadata->aoDimensions.empty() && nXDim > 0 && nYDim > 0)
229 : {
230 : // Check that all data fields have a DimList=(YDim,XDim)
231 : // property. This may be unneeded, but at least if we meet
232 : // this condition, that should be a strong hint that the first
233 : // dimension is Y, and the second X.
234 2 : bool bDimListIsYDimXDim = true;
235 8 : for (const auto &oDataField : oDataFields.GetChildren())
236 : {
237 6 : if (oDataField.GetType() == CPLJSONObject::Type::Object)
238 : {
239 4 : const auto oDimList = oDataField.GetArray("DimList");
240 4 : if (!(oDimList.Size() == 2 &&
241 4 : oDimList[0].ToString() == "YDim" &&
242 4 : oDimList[1].ToString() == "XDim"))
243 : {
244 0 : bDimListIsYDimXDim = false;
245 0 : break;
246 : }
247 : }
248 : }
249 2 : if (bDimListIsYDimXDim)
250 : {
251 : {
252 4 : const std::string osDimensionName("YDim");
253 2 : oMapDimensionNameToSize[osDimensionName] = nYDim;
254 4 : Dimension oDim;
255 2 : oDim.osName = osDimensionName;
256 2 : oDim.nSize = nYDim;
257 2 : poGridMetadata->aoDimensions.push_back(oDim);
258 : }
259 : {
260 4 : const std::string osDimensionName("XDim");
261 2 : oMapDimensionNameToSize[osDimensionName] = nXDim;
262 4 : Dimension oDim;
263 2 : oDim.osName = osDimensionName;
264 2 : oDim.nSize = nXDim;
265 2 : poGridMetadata->aoDimensions.push_back(oDim);
266 : }
267 : }
268 : }
269 :
270 12 : poGridMetadata->osProjection = oGrid.GetString("Projection");
271 24 : poGridMetadata->nProjCode =
272 12 : GetGTCPProjectionCode(poGridMetadata->osProjection);
273 12 : poGridMetadata->osGridOrigin = oGrid.GetString("GridOrigin");
274 12 : poGridMetadata->nZone = oGrid.GetInteger("ZoneCode", -1);
275 12 : poGridMetadata->nSphereCode = oGrid.GetInteger("SphereCode", -1);
276 :
277 36 : const auto oProjParams = oGrid.GetArray("ProjParams");
278 64 : for (int j = 0; j < oProjParams.Size(); ++j)
279 52 : poGridMetadata->adfProjParams.push_back(
280 52 : oProjParams[j].ToDouble());
281 :
282 : const auto oUpperLeftPointMtrs =
283 36 : oGrid.GetArray("UpperLeftPointMtrs");
284 36 : for (int j = 0; j < oUpperLeftPointMtrs.Size(); ++j)
285 24 : poGridMetadata->adfUpperLeftPointMeters.push_back(
286 24 : oUpperLeftPointMtrs[j].ToDouble());
287 :
288 36 : const auto oLowerRightMtrs = oGrid.GetArray("LowerRightMtrs");
289 36 : for (int j = 0; j < oLowerRightMtrs.Size(); ++j)
290 24 : poGridMetadata->adfLowerRightPointMeters.push_back(
291 24 : oLowerRightMtrs[j].ToDouble());
292 :
293 12 : m_oMapGridNameToGridMetadata[osGridName] =
294 24 : std::move(poGridMetadata);
295 : const auto poGridMetadataRef =
296 12 : m_oMapGridNameToGridMetadata[osGridName].get();
297 :
298 48 : for (const auto &oDataField : oDataFields.GetChildren())
299 : {
300 36 : if (oDataField.GetType() == CPLJSONObject::Type::Object)
301 : {
302 : const auto osDataFieldName =
303 36 : oDataField.GetString("DataFieldName");
304 36 : const auto oDimList = oDataField.GetArray("DimList");
305 24 : GridDataFieldMetadata oDataFieldMetadata;
306 12 : bool bValid = oDimList.Size() > 0;
307 40 : for (int j = 0; j < oDimList.Size(); ++j)
308 : {
309 56 : const auto osDimensionName = oDimList[j].ToString();
310 : const auto oIter = oMapDimensionNameToSize.find(
311 28 : osDimensionName.c_str());
312 28 : if (oIter == oMapDimensionNameToSize.end())
313 : {
314 0 : bValid = false;
315 0 : break;
316 : }
317 56 : Dimension oDim;
318 28 : oDim.osName = osDimensionName;
319 28 : oDim.nSize = oIter->second;
320 28 : oDataFieldMetadata.aoDimensions.push_back(oDim);
321 : }
322 12 : if (bValid)
323 : {
324 12 : oDataFieldMetadata.poGridMetadata = poGridMetadataRef;
325 : m_oMapSubdatasetNameToGridDataFieldMetadata
326 24 : ["//HDFEOS/GRIDS/" + osGridName + "/Data_Fields/" +
327 24 : osDataFieldName] = std::move(oDataFieldMetadata);
328 : }
329 : }
330 : }
331 : }
332 : }
333 12 : }
334 :
335 : /************************************************************************/
336 : /* GetGridMetadata() */
337 : /************************************************************************/
338 :
339 7 : bool HDF5EOSParser::GetGridMetadata(const std::string &osGridName,
340 : GridMetadata &gridMetadataOut) const
341 : {
342 7 : const auto oIter = m_oMapGridNameToGridMetadata.find(osGridName);
343 7 : if (oIter == m_oMapGridNameToGridMetadata.end())
344 5 : return false;
345 2 : gridMetadataOut = *(oIter->second);
346 2 : return true;
347 : }
348 :
349 : /************************************************************************/
350 : /* GetGridDataFieldMetadata() */
351 : /************************************************************************/
352 :
353 10 : bool HDF5EOSParser::GetGridDataFieldMetadata(
354 : const char *pszSubdatasetName,
355 : GridDataFieldMetadata &gridDataFieldMetadataOut) const
356 : {
357 : const auto oIter =
358 10 : m_oMapSubdatasetNameToGridDataFieldMetadata.find(pszSubdatasetName);
359 10 : if (oIter == m_oMapSubdatasetNameToGridDataFieldMetadata.end())
360 3 : return false;
361 7 : gridDataFieldMetadataOut = oIter->second;
362 7 : return true;
363 : }
364 :
365 : /************************************************************************/
366 : /* ParseSwathStructure() */
367 : /************************************************************************/
368 :
369 14 : void HDF5EOSParser::ParseSwathStructure(const CPLJSONObject &oSwathStructure)
370 : {
371 56 : for (const auto &oSwath : oSwathStructure.GetChildren())
372 : {
373 42 : if (oSwath.GetType() == CPLJSONObject::Type::Object)
374 : {
375 42 : const auto osSwathName = oSwath.GetString("SwathName");
376 :
377 42 : const auto oDimensions = oSwath.GetObj("Dimension");
378 28 : std::map<std::string, int> oMapDimensionNameToSize;
379 28 : auto poSwathMetadata = std::make_unique<SwathMetadata>();
380 14 : poSwathMetadata->osSwathName = osSwathName;
381 120 : for (const auto &oDimension : oDimensions.GetChildren())
382 : {
383 106 : if (oDimension.GetType() == CPLJSONObject::Type::Object)
384 : {
385 : auto osDimensionName =
386 234 : oDimension.GetString("DimensionName");
387 78 : int nSize = oDimension.GetInteger("Size");
388 78 : oMapDimensionNameToSize[osDimensionName] = nSize;
389 156 : Dimension oDim;
390 78 : oDim.osName = std::move(osDimensionName);
391 78 : oDim.nSize = nSize;
392 78 : poSwathMetadata->aoDimensions.emplace_back(std::move(oDim));
393 : }
394 : }
395 :
396 14 : m_oMapSwathNameToSwathMetadata[osSwathName] =
397 28 : std::move(poSwathMetadata);
398 : const auto poSwathMetadataRef =
399 14 : m_oMapSwathNameToSwathMetadata[osSwathName].get();
400 :
401 : struct DimensionMap
402 : {
403 : std::string osGeoDimName;
404 : std::string osDataDimName;
405 : int nOffset = 0;
406 : int nIncrement = 1;
407 : };
408 :
409 28 : std::vector<DimensionMap> aoDimensionMaps;
410 28 : std::map<std::string, std::string> oMapDataDimensionToGeoDimension;
411 :
412 42 : const auto jsonDimensionMaps = oSwath.GetObj("DimensionMap");
413 54 : for (const auto &jsonDimensionMap : jsonDimensionMaps.GetChildren())
414 : {
415 40 : if (jsonDimensionMap.GetType() == CPLJSONObject::Type::Object)
416 : {
417 24 : DimensionMap oDimensionMap;
418 : oDimensionMap.osGeoDimName =
419 12 : jsonDimensionMap.GetString("GeoDimension");
420 : oDimensionMap.osDataDimName =
421 12 : jsonDimensionMap.GetString("DataDimension");
422 12 : oDimensionMap.nOffset =
423 12 : jsonDimensionMap.GetInteger("Offset", 0);
424 12 : oDimensionMap.nIncrement =
425 12 : jsonDimensionMap.GetInteger("Increment", 1);
426 : oMapDataDimensionToGeoDimension[oDimensionMap
427 12 : .osDataDimName] =
428 12 : oDimensionMap.osGeoDimName;
429 12 : aoDimensionMaps.emplace_back(oDimensionMap);
430 : }
431 : }
432 :
433 42 : const auto oGeoFields = oSwath.GetObj("GeoField");
434 28 : std::vector<Dimension> aoLongitudeDimensions;
435 28 : std::vector<Dimension> aoLatitudeDimensions;
436 84 : for (const auto &oGeoField : oGeoFields.GetChildren())
437 : {
438 70 : if (oGeoField.GetType() == CPLJSONObject::Type::Object)
439 : {
440 126 : auto osGeoFieldName = oGeoField.GetString("GeoFieldName");
441 126 : auto oDimList = oGeoField.GetArray("DimList");
442 42 : bool bValid = true;
443 84 : std::vector<Dimension> aoDimensions;
444 112 : for (int j = 0; j < oDimList.Size(); ++j)
445 : {
446 140 : const auto osDimensionName = oDimList[j].ToString();
447 : const auto oIter = oMapDimensionNameToSize.find(
448 70 : osDimensionName.c_str());
449 70 : if (oIter == oMapDimensionNameToSize.end())
450 : {
451 0 : bValid = false;
452 0 : break;
453 : }
454 140 : Dimension oDim;
455 70 : oDim.osName = osDimensionName;
456 70 : oDim.nSize = oIter->second;
457 70 : aoDimensions.push_back(oDim);
458 70 : if (oMapDataDimensionToGeoDimension.find(
459 70 : osDimensionName) ==
460 140 : oMapDataDimensionToGeoDimension.end())
461 : {
462 : // Create a fake dimension map for this dim
463 56 : DimensionMap oDimensionMap;
464 28 : oDimensionMap.osGeoDimName = osDimensionName;
465 28 : oDimensionMap.osDataDimName = osDimensionName;
466 28 : oDimensionMap.nOffset = 0;
467 28 : oDimensionMap.nIncrement = 1;
468 28 : oMapDataDimensionToGeoDimension[osDimensionName] =
469 28 : osDimensionName;
470 28 : aoDimensionMaps.emplace_back(oDimensionMap);
471 : }
472 : }
473 42 : if (bValid)
474 : {
475 84 : SwathGeolocationFieldMetadata oMetadata;
476 42 : oMetadata.poSwathMetadata = poSwathMetadataRef;
477 :
478 42 : if (osGeoFieldName == "Longitude")
479 14 : aoLongitudeDimensions = aoDimensions;
480 28 : else if (osGeoFieldName == "Latitude")
481 14 : aoLatitudeDimensions = aoDimensions;
482 :
483 42 : oMetadata.aoDimensions = std::move(aoDimensions);
484 :
485 : const std::string osSubdatasetName =
486 84 : "//HDFEOS/SWATHS/" + osSwathName +
487 84 : "/Geolocation_Fields/" + osGeoFieldName;
488 : m_oMapSubdatasetNameToSwathGeolocationFieldMetadata
489 42 : [osSubdatasetName] = std::move(oMetadata);
490 : }
491 : }
492 : }
493 :
494 42 : const auto oDataFields = oSwath.GetObj("DataField");
495 86 : for (const auto &oDataField : oDataFields.GetChildren())
496 : {
497 72 : if (oDataField.GetType() == CPLJSONObject::Type::Object)
498 : {
499 : const auto osDataFieldName =
500 132 : oDataField.GetString("DataFieldName");
501 132 : const auto oDimList = oDataField.GetArray("DimList");
502 88 : SwathDataFieldMetadata oMetadata;
503 44 : oMetadata.poSwathMetadata = poSwathMetadataRef;
504 44 : bool bValid = oDimList.Size() > 0;
505 122 : for (int j = 0; j < oDimList.Size(); ++j)
506 : {
507 156 : const auto osDimensionName = oDimList[j].ToString();
508 : const auto oIter = oMapDimensionNameToSize.find(
509 78 : osDimensionName.c_str());
510 78 : if (oIter == oMapDimensionNameToSize.end())
511 : {
512 0 : bValid = false;
513 0 : break;
514 : }
515 156 : Dimension oDim;
516 78 : oDim.osName = osDimensionName;
517 78 : oDim.nSize = oIter->second;
518 78 : oMetadata.aoDimensions.push_back(oDim);
519 : }
520 44 : if (bValid)
521 : {
522 64 : if (oMetadata.aoDimensions.size() >= 2 &&
523 64 : aoLongitudeDimensions.size() == 2 &&
524 20 : aoLongitudeDimensions == aoLatitudeDimensions)
525 : {
526 20 : int i = 0;
527 40 : std::string osDataXDimName;
528 40 : std::string osDataYDimName;
529 74 : for (const auto &oDimSwath : oMetadata.aoDimensions)
530 : {
531 : auto oIter =
532 : oMapDataDimensionToGeoDimension.find(
533 54 : oDimSwath.osName);
534 54 : if (oIter !=
535 108 : oMapDataDimensionToGeoDimension.end())
536 : {
537 40 : const auto &osGeoDimName = oIter->second;
538 40 : if (osGeoDimName ==
539 40 : aoLongitudeDimensions[0].osName)
540 : {
541 20 : osDataYDimName = oDimSwath.osName;
542 20 : oMetadata.iYDim = i;
543 : }
544 20 : else if (osGeoDimName ==
545 20 : aoLongitudeDimensions[1].osName)
546 : {
547 20 : osDataXDimName = oDimSwath.osName;
548 20 : oMetadata.iXDim = i;
549 : }
550 : }
551 : else
552 : {
553 14 : oMetadata.iOtherDim = i;
554 : }
555 54 : ++i;
556 : }
557 20 : if (oMetadata.iXDim >= 0 && oMetadata.iYDim >= 0)
558 : {
559 : oMetadata.osLongitudeSubdataset =
560 40 : "//HDFEOS/SWATHS/" + osSwathName +
561 20 : "/Geolocation_Fields/Longitude";
562 : oMetadata.osLatitudeSubdataset =
563 40 : "//HDFEOS/SWATHS/" + osSwathName +
564 20 : "/Geolocation_Fields/Latitude";
565 :
566 84 : for (const auto &oDimMap : aoDimensionMaps)
567 : {
568 64 : if (oDimMap.osDataDimName == osDataYDimName)
569 : {
570 20 : oMetadata.nLineOffset = oDimMap.nOffset;
571 20 : oMetadata.nLineStep =
572 20 : oDimMap.nIncrement;
573 : }
574 44 : else if (oDimMap.osDataDimName ==
575 : osDataXDimName)
576 : {
577 20 : oMetadata.nPixelOffset =
578 20 : oDimMap.nOffset;
579 20 : oMetadata.nPixelStep =
580 20 : oDimMap.nIncrement;
581 : }
582 : }
583 : }
584 : }
585 :
586 : m_oMapSubdatasetNameToSwathDataFieldMetadata
587 88 : ["//HDFEOS/SWATHS/" + osSwathName +
588 132 : "/Data_Fields/" + osDataFieldName] =
589 88 : std::move(oMetadata);
590 : }
591 : }
592 : }
593 : }
594 : }
595 14 : }
596 :
597 : /************************************************************************/
598 : /* GetSwathMetadata() */
599 : /************************************************************************/
600 :
601 3 : bool HDF5EOSParser::GetSwathMetadata(const std::string &osSwathName,
602 : SwathMetadata &swathMetadataOut) const
603 : {
604 3 : const auto oIter = m_oMapSwathNameToSwathMetadata.find(osSwathName);
605 3 : if (oIter == m_oMapSwathNameToSwathMetadata.end())
606 0 : return false;
607 3 : swathMetadataOut = *(oIter->second.get());
608 3 : return true;
609 : }
610 :
611 : /************************************************************************/
612 : /* GetSwathDataFieldMetadata() */
613 : /************************************************************************/
614 :
615 21 : bool HDF5EOSParser::GetSwathDataFieldMetadata(
616 : const char *pszSubdatasetName,
617 : SwathDataFieldMetadata &swathDataFieldMetadataOut) const
618 : {
619 : const auto oIter =
620 21 : m_oMapSubdatasetNameToSwathDataFieldMetadata.find(pszSubdatasetName);
621 21 : if (oIter == m_oMapSubdatasetNameToSwathDataFieldMetadata.end())
622 12 : return false;
623 9 : swathDataFieldMetadataOut = oIter->second;
624 9 : return true;
625 : }
626 :
627 : /************************************************************************/
628 : /* GetSwathGeolocationFieldMetadata() */
629 : /************************************************************************/
630 :
631 2 : bool HDF5EOSParser::GetSwathGeolocationFieldMetadata(
632 : const char *pszSubdatasetName,
633 : SwathGeolocationFieldMetadata &swathGeolocationFieldMetadataOut) const
634 : {
635 : const auto oIter = m_oMapSubdatasetNameToSwathGeolocationFieldMetadata.find(
636 2 : pszSubdatasetName);
637 2 : if (oIter == m_oMapSubdatasetNameToSwathGeolocationFieldMetadata.end())
638 0 : return false;
639 2 : swathGeolocationFieldMetadataOut = oIter->second;
640 2 : return true;
641 : }
642 :
643 : /************************************************************************/
644 : /* GetGeoTransform() */
645 : /************************************************************************/
646 :
647 7 : bool HDF5EOSParser::GridMetadata::GetGeoTransform(
648 : double adfGeoTransform[6]) const
649 : {
650 14 : if (nProjCode >= 0 && osGridOrigin == "HE5_HDFE_GD_UL" &&
651 21 : adfUpperLeftPointMeters.size() == 2 &&
652 7 : adfLowerRightPointMeters.size() == 2)
653 : {
654 7 : int nRasterXSize = 0;
655 7 : int nRasterYSize = 0;
656 :
657 23 : for (const auto &oDim : aoDimensions)
658 : {
659 16 : if (oDim.osName == "XDim")
660 7 : nRasterXSize = oDim.nSize;
661 9 : else if (oDim.osName == "YDim")
662 7 : nRasterYSize = oDim.nSize;
663 : }
664 7 : if (nRasterXSize <= 0 || nRasterYSize <= 0)
665 0 : return false;
666 7 : if (nProjCode == 0) // GEO
667 : {
668 2 : adfGeoTransform[0] = CPLPackedDMSToDec(adfUpperLeftPointMeters[0]);
669 2 : adfGeoTransform[1] =
670 2 : (CPLPackedDMSToDec(adfLowerRightPointMeters[0]) -
671 2 : CPLPackedDMSToDec(adfUpperLeftPointMeters[0])) /
672 : nRasterXSize;
673 2 : adfGeoTransform[2] = 0;
674 2 : adfGeoTransform[3] = CPLPackedDMSToDec(adfUpperLeftPointMeters[1]);
675 2 : adfGeoTransform[4] = 0;
676 2 : adfGeoTransform[5] =
677 2 : (CPLPackedDMSToDec(adfLowerRightPointMeters[1]) -
678 2 : CPLPackedDMSToDec(adfUpperLeftPointMeters[1])) /
679 : nRasterYSize;
680 : }
681 : else
682 : {
683 5 : adfGeoTransform[0] = adfUpperLeftPointMeters[0];
684 5 : adfGeoTransform[1] =
685 5 : (adfLowerRightPointMeters[0] - adfUpperLeftPointMeters[0]) /
686 : nRasterXSize;
687 5 : adfGeoTransform[2] = 0;
688 5 : adfGeoTransform[3] = adfUpperLeftPointMeters[1];
689 5 : adfGeoTransform[4] = 0;
690 5 : adfGeoTransform[5] =
691 5 : (adfLowerRightPointMeters[1] - adfUpperLeftPointMeters[1]) /
692 : nRasterYSize;
693 : }
694 7 : return true;
695 : }
696 0 : return false;
697 : }
698 :
699 : /************************************************************************/
700 : /* GetSRS() */
701 : /************************************************************************/
702 :
703 7 : std::unique_ptr<OGRSpatialReference> HDF5EOSParser::GridMetadata::GetSRS() const
704 : {
705 14 : std::vector<double> l_adfProjParams = adfProjParams;
706 7 : l_adfProjParams.resize(15);
707 14 : auto poSRS = std::make_unique<OGRSpatialReference>();
708 7 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
709 14 : if (poSRS->importFromUSGS(nProjCode, nZone, l_adfProjParams.data(),
710 14 : nSphereCode) == OGRERR_NONE)
711 : {
712 7 : return poSRS;
713 : }
714 0 : return nullptr;
715 : }
|