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 258 : bool HDF5EOSParser::HasHDFEOS(hid_t hRoot)
26 : {
27 258 : hsize_t numObjs = 0;
28 258 : H5Gget_num_objs(hRoot, &numObjs);
29 258 : bool bFound = false;
30 866 : for (hsize_t i = 0; i < numObjs; ++i)
31 : {
32 : char szName[128];
33 : ssize_t nLen =
34 634 : H5Gget_objname_by_idx(hRoot, i, szName, sizeof(szName) - 1);
35 634 : if (nLen > 0)
36 : {
37 634 : szName[nLen] = 0;
38 634 : if (strcmp(szName, "HDFEOS INFORMATION") == 0)
39 : {
40 26 : bFound = true;
41 26 : break;
42 : }
43 : }
44 : }
45 258 : if (!bFound)
46 232 : 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 : std::string 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 = std::move(osDimensionName);
215 24 : oDim.nSize = nSize;
216 24 : poGridMetadata->aoDimensions.push_back(std::move(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 : std::string osDimensionName("YDim");
253 2 : oMapDimensionNameToSize[osDimensionName] = nYDim;
254 4 : Dimension oDim;
255 2 : oDim.osName = std::move(osDimensionName);
256 2 : oDim.nSize = nYDim;
257 2 : poGridMetadata->aoDimensions.push_back(std::move(oDim));
258 : }
259 : {
260 4 : std::string osDimensionName("XDim");
261 2 : oMapDimensionNameToSize[osDimensionName] = nXDim;
262 4 : Dimension oDim;
263 2 : oDim.osName = std::move(osDimensionName);
264 2 : oDim.nSize = nXDim;
265 2 : poGridMetadata->aoDimensions.push_back(std::move(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 : std::string 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 = std::move(osDimensionName);
319 28 : oDim.nSize = oIter->second;
320 28 : oDataFieldMetadata.aoDimensions.push_back(
321 28 : std::move(oDim));
322 : }
323 12 : if (bValid)
324 : {
325 12 : oDataFieldMetadata.poGridMetadata = poGridMetadataRef;
326 : m_oMapSubdatasetNameToGridDataFieldMetadata
327 24 : ["//HDFEOS/GRIDS/" + osGridName + "/Data_Fields/" +
328 24 : osDataFieldName] = std::move(oDataFieldMetadata);
329 : }
330 : }
331 : }
332 : }
333 : }
334 12 : }
335 :
336 : /************************************************************************/
337 : /* GetGridMetadata() */
338 : /************************************************************************/
339 :
340 7 : bool HDF5EOSParser::GetGridMetadata(const std::string &osGridName,
341 : GridMetadata &gridMetadataOut) const
342 : {
343 7 : const auto oIter = m_oMapGridNameToGridMetadata.find(osGridName);
344 7 : if (oIter == m_oMapGridNameToGridMetadata.end())
345 5 : return false;
346 2 : gridMetadataOut = *(oIter->second);
347 2 : return true;
348 : }
349 :
350 : /************************************************************************/
351 : /* GetGridDataFieldMetadata() */
352 : /************************************************************************/
353 :
354 10 : bool HDF5EOSParser::GetGridDataFieldMetadata(
355 : const char *pszSubdatasetName,
356 : GridDataFieldMetadata &gridDataFieldMetadataOut) const
357 : {
358 : const auto oIter =
359 10 : m_oMapSubdatasetNameToGridDataFieldMetadata.find(pszSubdatasetName);
360 10 : if (oIter == m_oMapSubdatasetNameToGridDataFieldMetadata.end())
361 3 : return false;
362 7 : gridDataFieldMetadataOut = oIter->second;
363 7 : return true;
364 : }
365 :
366 : /************************************************************************/
367 : /* ParseSwathStructure() */
368 : /************************************************************************/
369 :
370 14 : void HDF5EOSParser::ParseSwathStructure(const CPLJSONObject &oSwathStructure)
371 : {
372 56 : for (const auto &oSwath : oSwathStructure.GetChildren())
373 : {
374 42 : if (oSwath.GetType() == CPLJSONObject::Type::Object)
375 : {
376 42 : const auto osSwathName = oSwath.GetString("SwathName");
377 :
378 42 : const auto oDimensions = oSwath.GetObj("Dimension");
379 28 : std::map<std::string, int> oMapDimensionNameToSize;
380 28 : auto poSwathMetadata = std::make_unique<SwathMetadata>();
381 14 : poSwathMetadata->osSwathName = osSwathName;
382 120 : for (const auto &oDimension : oDimensions.GetChildren())
383 : {
384 106 : if (oDimension.GetType() == CPLJSONObject::Type::Object)
385 : {
386 : auto osDimensionName =
387 234 : oDimension.GetString("DimensionName");
388 78 : int nSize = oDimension.GetInteger("Size");
389 78 : oMapDimensionNameToSize[osDimensionName] = nSize;
390 156 : Dimension oDim;
391 78 : oDim.osName = std::move(osDimensionName);
392 78 : oDim.nSize = nSize;
393 78 : poSwathMetadata->aoDimensions.emplace_back(std::move(oDim));
394 : }
395 : }
396 :
397 14 : m_oMapSwathNameToSwathMetadata[osSwathName] =
398 28 : std::move(poSwathMetadata);
399 : const auto poSwathMetadataRef =
400 14 : m_oMapSwathNameToSwathMetadata[osSwathName].get();
401 :
402 : struct DimensionMap
403 : {
404 : std::string osGeoDimName;
405 : std::string osDataDimName;
406 : int nOffset = 0;
407 : int nIncrement = 1;
408 : };
409 :
410 28 : std::vector<DimensionMap> aoDimensionMaps;
411 28 : std::map<std::string, std::string> oMapDataDimensionToGeoDimension;
412 :
413 42 : const auto jsonDimensionMaps = oSwath.GetObj("DimensionMap");
414 54 : for (const auto &jsonDimensionMap : jsonDimensionMaps.GetChildren())
415 : {
416 40 : if (jsonDimensionMap.GetType() == CPLJSONObject::Type::Object)
417 : {
418 24 : DimensionMap oDimensionMap;
419 : oDimensionMap.osGeoDimName =
420 12 : jsonDimensionMap.GetString("GeoDimension");
421 : oDimensionMap.osDataDimName =
422 12 : jsonDimensionMap.GetString("DataDimension");
423 12 : oDimensionMap.nOffset =
424 12 : jsonDimensionMap.GetInteger("Offset", 0);
425 12 : oDimensionMap.nIncrement =
426 12 : jsonDimensionMap.GetInteger("Increment", 1);
427 : oMapDataDimensionToGeoDimension[oDimensionMap
428 12 : .osDataDimName] =
429 12 : oDimensionMap.osGeoDimName;
430 12 : aoDimensionMaps.emplace_back(oDimensionMap);
431 : }
432 : }
433 :
434 42 : const auto oGeoFields = oSwath.GetObj("GeoField");
435 28 : std::vector<Dimension> aoLongitudeDimensions;
436 28 : std::vector<Dimension> aoLatitudeDimensions;
437 84 : for (const auto &oGeoField : oGeoFields.GetChildren())
438 : {
439 70 : if (oGeoField.GetType() == CPLJSONObject::Type::Object)
440 : {
441 126 : auto osGeoFieldName = oGeoField.GetString("GeoFieldName");
442 126 : auto oDimList = oGeoField.GetArray("DimList");
443 42 : bool bValid = true;
444 84 : std::vector<Dimension> aoDimensions;
445 112 : for (int j = 0; j < oDimList.Size(); ++j)
446 : {
447 140 : const auto osDimensionName = oDimList[j].ToString();
448 : const auto oIter = oMapDimensionNameToSize.find(
449 70 : osDimensionName.c_str());
450 70 : if (oIter == oMapDimensionNameToSize.end())
451 : {
452 0 : bValid = false;
453 0 : break;
454 : }
455 140 : Dimension oDim;
456 70 : oDim.osName = osDimensionName;
457 70 : oDim.nSize = oIter->second;
458 70 : aoDimensions.push_back(std::move(oDim));
459 70 : if (oMapDataDimensionToGeoDimension.find(
460 70 : osDimensionName) ==
461 140 : oMapDataDimensionToGeoDimension.end())
462 : {
463 : // Create a fake dimension map for this dim
464 56 : DimensionMap oDimensionMap;
465 28 : oDimensionMap.osGeoDimName = osDimensionName;
466 28 : oDimensionMap.osDataDimName = osDimensionName;
467 28 : oDimensionMap.nOffset = 0;
468 28 : oDimensionMap.nIncrement = 1;
469 28 : oMapDataDimensionToGeoDimension[osDimensionName] =
470 28 : osDimensionName;
471 28 : aoDimensionMaps.emplace_back(oDimensionMap);
472 : }
473 : }
474 42 : if (bValid)
475 : {
476 84 : SwathGeolocationFieldMetadata oMetadata;
477 42 : oMetadata.poSwathMetadata = poSwathMetadataRef;
478 :
479 42 : if (osGeoFieldName == "Longitude")
480 14 : aoLongitudeDimensions = aoDimensions;
481 28 : else if (osGeoFieldName == "Latitude")
482 14 : aoLatitudeDimensions = aoDimensions;
483 :
484 42 : oMetadata.aoDimensions = std::move(aoDimensions);
485 :
486 : const std::string osSubdatasetName =
487 84 : "//HDFEOS/SWATHS/" + osSwathName +
488 84 : "/Geolocation_Fields/" + osGeoFieldName;
489 : m_oMapSubdatasetNameToSwathGeolocationFieldMetadata
490 42 : [osSubdatasetName] = std::move(oMetadata);
491 : }
492 : }
493 : }
494 :
495 42 : const auto oDataFields = oSwath.GetObj("DataField");
496 86 : for (const auto &oDataField : oDataFields.GetChildren())
497 : {
498 72 : if (oDataField.GetType() == CPLJSONObject::Type::Object)
499 : {
500 : const auto osDataFieldName =
501 132 : oDataField.GetString("DataFieldName");
502 132 : const auto oDimList = oDataField.GetArray("DimList");
503 88 : SwathDataFieldMetadata oMetadata;
504 44 : oMetadata.poSwathMetadata = poSwathMetadataRef;
505 44 : bool bValid = oDimList.Size() > 0;
506 122 : for (int j = 0; j < oDimList.Size(); ++j)
507 : {
508 156 : std::string osDimensionName = oDimList[j].ToString();
509 : const auto oIter = oMapDimensionNameToSize.find(
510 78 : osDimensionName.c_str());
511 78 : if (oIter == oMapDimensionNameToSize.end())
512 : {
513 0 : bValid = false;
514 0 : break;
515 : }
516 156 : Dimension oDim;
517 78 : oDim.osName = std::move(osDimensionName);
518 78 : oDim.nSize = oIter->second;
519 78 : oMetadata.aoDimensions.push_back(std::move(oDim));
520 : }
521 44 : if (bValid)
522 : {
523 64 : if (oMetadata.aoDimensions.size() >= 2 &&
524 64 : aoLongitudeDimensions.size() == 2 &&
525 20 : aoLongitudeDimensions == aoLatitudeDimensions)
526 : {
527 20 : int i = 0;
528 40 : std::string osDataXDimName;
529 40 : std::string osDataYDimName;
530 74 : for (const auto &oDimSwath : oMetadata.aoDimensions)
531 : {
532 : auto oIter =
533 : oMapDataDimensionToGeoDimension.find(
534 54 : oDimSwath.osName);
535 54 : if (oIter !=
536 108 : oMapDataDimensionToGeoDimension.end())
537 : {
538 40 : const auto &osGeoDimName = oIter->second;
539 40 : if (osGeoDimName ==
540 40 : aoLongitudeDimensions[0].osName)
541 : {
542 20 : osDataYDimName = oDimSwath.osName;
543 20 : oMetadata.iYDim = i;
544 : }
545 20 : else if (osGeoDimName ==
546 20 : aoLongitudeDimensions[1].osName)
547 : {
548 20 : osDataXDimName = oDimSwath.osName;
549 20 : oMetadata.iXDim = i;
550 : }
551 : }
552 : else
553 : {
554 14 : oMetadata.iOtherDim = i;
555 : }
556 54 : ++i;
557 : }
558 20 : if (oMetadata.iXDim >= 0 && oMetadata.iYDim >= 0)
559 : {
560 : oMetadata.osLongitudeSubdataset =
561 40 : "//HDFEOS/SWATHS/" + osSwathName +
562 20 : "/Geolocation_Fields/Longitude";
563 : oMetadata.osLatitudeSubdataset =
564 40 : "//HDFEOS/SWATHS/" + osSwathName +
565 20 : "/Geolocation_Fields/Latitude";
566 :
567 84 : for (const auto &oDimMap : aoDimensionMaps)
568 : {
569 64 : if (oDimMap.osDataDimName == osDataYDimName)
570 : {
571 20 : oMetadata.nLineOffset = oDimMap.nOffset;
572 20 : oMetadata.nLineStep =
573 20 : oDimMap.nIncrement;
574 : }
575 44 : else if (oDimMap.osDataDimName ==
576 : osDataXDimName)
577 : {
578 20 : oMetadata.nPixelOffset =
579 20 : oDimMap.nOffset;
580 20 : oMetadata.nPixelStep =
581 20 : oDimMap.nIncrement;
582 : }
583 : }
584 : }
585 : }
586 :
587 : m_oMapSubdatasetNameToSwathDataFieldMetadata
588 88 : ["//HDFEOS/SWATHS/" + osSwathName +
589 132 : "/Data_Fields/" + osDataFieldName] =
590 88 : std::move(oMetadata);
591 : }
592 : }
593 : }
594 : }
595 : }
596 14 : }
597 :
598 : /************************************************************************/
599 : /* GetSwathMetadata() */
600 : /************************************************************************/
601 :
602 3 : bool HDF5EOSParser::GetSwathMetadata(const std::string &osSwathName,
603 : SwathMetadata &swathMetadataOut) const
604 : {
605 3 : const auto oIter = m_oMapSwathNameToSwathMetadata.find(osSwathName);
606 3 : if (oIter == m_oMapSwathNameToSwathMetadata.end())
607 0 : return false;
608 3 : swathMetadataOut = *(oIter->second.get());
609 3 : return true;
610 : }
611 :
612 : /************************************************************************/
613 : /* GetSwathDataFieldMetadata() */
614 : /************************************************************************/
615 :
616 21 : bool HDF5EOSParser::GetSwathDataFieldMetadata(
617 : const char *pszSubdatasetName,
618 : SwathDataFieldMetadata &swathDataFieldMetadataOut) const
619 : {
620 : const auto oIter =
621 21 : m_oMapSubdatasetNameToSwathDataFieldMetadata.find(pszSubdatasetName);
622 21 : if (oIter == m_oMapSubdatasetNameToSwathDataFieldMetadata.end())
623 12 : return false;
624 9 : swathDataFieldMetadataOut = oIter->second;
625 9 : return true;
626 : }
627 :
628 : /************************************************************************/
629 : /* GetSwathGeolocationFieldMetadata() */
630 : /************************************************************************/
631 :
632 2 : bool HDF5EOSParser::GetSwathGeolocationFieldMetadata(
633 : const char *pszSubdatasetName,
634 : SwathGeolocationFieldMetadata &swathGeolocationFieldMetadataOut) const
635 : {
636 : const auto oIter = m_oMapSubdatasetNameToSwathGeolocationFieldMetadata.find(
637 2 : pszSubdatasetName);
638 2 : if (oIter == m_oMapSubdatasetNameToSwathGeolocationFieldMetadata.end())
639 0 : return false;
640 2 : swathGeolocationFieldMetadataOut = oIter->second;
641 2 : return true;
642 : }
643 :
644 : /************************************************************************/
645 : /* GetGeoTransform() */
646 : /************************************************************************/
647 :
648 7 : bool HDF5EOSParser::GridMetadata::GetGeoTransform(
649 : double adfGeoTransform[6]) const
650 : {
651 14 : if (nProjCode >= 0 && osGridOrigin == "HE5_HDFE_GD_UL" &&
652 21 : adfUpperLeftPointMeters.size() == 2 &&
653 7 : adfLowerRightPointMeters.size() == 2)
654 : {
655 7 : int nRasterXSize = 0;
656 7 : int nRasterYSize = 0;
657 :
658 23 : for (const auto &oDim : aoDimensions)
659 : {
660 16 : if (oDim.osName == "XDim")
661 7 : nRasterXSize = oDim.nSize;
662 9 : else if (oDim.osName == "YDim")
663 7 : nRasterYSize = oDim.nSize;
664 : }
665 7 : if (nRasterXSize <= 0 || nRasterYSize <= 0)
666 0 : return false;
667 7 : if (nProjCode == 0) // GEO
668 : {
669 2 : adfGeoTransform[0] = CPLPackedDMSToDec(adfUpperLeftPointMeters[0]);
670 2 : adfGeoTransform[1] =
671 2 : (CPLPackedDMSToDec(adfLowerRightPointMeters[0]) -
672 2 : CPLPackedDMSToDec(adfUpperLeftPointMeters[0])) /
673 : nRasterXSize;
674 2 : adfGeoTransform[2] = 0;
675 2 : adfGeoTransform[3] = CPLPackedDMSToDec(adfUpperLeftPointMeters[1]);
676 2 : adfGeoTransform[4] = 0;
677 2 : adfGeoTransform[5] =
678 2 : (CPLPackedDMSToDec(adfLowerRightPointMeters[1]) -
679 2 : CPLPackedDMSToDec(adfUpperLeftPointMeters[1])) /
680 : nRasterYSize;
681 : }
682 : else
683 : {
684 5 : adfGeoTransform[0] = adfUpperLeftPointMeters[0];
685 5 : adfGeoTransform[1] =
686 5 : (adfLowerRightPointMeters[0] - adfUpperLeftPointMeters[0]) /
687 : nRasterXSize;
688 5 : adfGeoTransform[2] = 0;
689 5 : adfGeoTransform[3] = adfUpperLeftPointMeters[1];
690 5 : adfGeoTransform[4] = 0;
691 5 : adfGeoTransform[5] =
692 5 : (adfLowerRightPointMeters[1] - adfUpperLeftPointMeters[1]) /
693 : nRasterYSize;
694 : }
695 7 : return true;
696 : }
697 0 : return false;
698 : }
699 :
700 : /************************************************************************/
701 : /* GetSRS() */
702 : /************************************************************************/
703 :
704 7 : std::unique_ptr<OGRSpatialReference> HDF5EOSParser::GridMetadata::GetSRS() const
705 : {
706 14 : std::vector<double> l_adfProjParams = adfProjParams;
707 7 : l_adfProjParams.resize(15);
708 14 : auto poSRS = std::make_unique<OGRSpatialReference>();
709 7 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
710 14 : if (poSRS->importFromUSGS(nProjCode, nZone, l_adfProjParams.data(),
711 14 : nSphereCode) == OGRERR_NONE)
712 : {
713 7 : return poSRS;
714 : }
715 0 : return nullptr;
716 : }
|