Line data Source code
1 : /******************************************************************************
2 : * $Id$
3 : *
4 : * Project: Hierarchical Data Format Release 5 (HDF5)
5 : * Purpose: Implementation of HDF5 HDFEOS parser
6 : * Author: Even Rouault
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_error.h"
15 : #include "nasakeywordhandler.h"
16 :
17 : #include "hdf5eosparser.h"
18 :
19 : #include <cstring>
20 : #include <utility>
21 :
22 : /************************************************************************/
23 : /* HasHDFEOS() */
24 : /************************************************************************/
25 :
26 277 : bool HDF5EOSParser::HasHDFEOS(hid_t hRoot)
27 : {
28 277 : hsize_t numObjs = 0;
29 277 : H5Gget_num_objs(hRoot, &numObjs);
30 277 : bool bFound = false;
31 1031 : for (hsize_t i = 0; i < numObjs; ++i)
32 : {
33 : char szName[128];
34 : ssize_t nLen =
35 780 : H5Gget_objname_by_idx(hRoot, i, szName, sizeof(szName) - 1);
36 780 : if (nLen > 0)
37 : {
38 780 : szName[nLen] = 0;
39 780 : if (strcmp(szName, "HDFEOS INFORMATION") == 0)
40 : {
41 26 : bFound = true;
42 26 : break;
43 : }
44 : }
45 : }
46 277 : if (!bFound)
47 251 : return false;
48 :
49 : H5G_stat_t oStatbuf;
50 26 : if (H5Gget_objinfo(hRoot, "HDFEOS INFORMATION", false, &oStatbuf) < 0)
51 0 : return false;
52 :
53 26 : auto hHDFEOSInformation = H5Gopen(hRoot, "HDFEOS INFORMATION");
54 26 : if (hHDFEOSInformation < 0)
55 : {
56 0 : return false;
57 : }
58 26 : H5Gclose(hHDFEOSInformation);
59 26 : return true;
60 : }
61 :
62 : /************************************************************************/
63 : /* Parse() */
64 : /************************************************************************/
65 :
66 26 : bool HDF5EOSParser::Parse(hid_t hRoot)
67 : {
68 26 : auto hHDFEOSInformation = H5Gopen(hRoot, "HDFEOS INFORMATION");
69 26 : if (hHDFEOSInformation < 0)
70 : {
71 0 : return false;
72 : }
73 :
74 26 : const hid_t hArrayId = H5Dopen(hHDFEOSInformation, "StructMetadata.0");
75 26 : if (hArrayId < 0)
76 : {
77 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find StructMetadata.0");
78 0 : H5Gclose(hHDFEOSInformation);
79 0 : return false;
80 : }
81 :
82 26 : const hid_t hAttrSpace = H5Dget_space(hArrayId);
83 26 : const hid_t hAttrTypeID = H5Dget_type(hArrayId);
84 : const hid_t hAttrNativeType =
85 26 : H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT);
86 :
87 : // Fetch StructMetadata.0 content in a std::string
88 52 : std::string osResult;
89 26 : if (H5Tget_class(hAttrNativeType) == H5T_STRING &&
90 52 : !H5Tis_variable_str(hAttrNativeType) &&
91 26 : H5Sget_simple_extent_ndims(hAttrSpace) == 0)
92 : {
93 26 : const auto nSize = H5Tget_size(hAttrNativeType);
94 26 : if (nSize > 10 * 1024 * 1024)
95 : {
96 0 : CPLError(CE_Failure, CPLE_AppDefined,
97 : "Too large HDFEOS INFORMATION.StructMetadata.0");
98 : }
99 : else
100 : {
101 26 : osResult.resize(nSize);
102 26 : H5Dread(hArrayId, hAttrNativeType, H5S_ALL, hAttrSpace, H5P_DEFAULT,
103 26 : &osResult[0]);
104 : }
105 : }
106 : else
107 : {
108 0 : CPLError(CE_Failure, CPLE_AppDefined,
109 : "HDFEOS INFORMATION.StructMetadata.0 not of type string");
110 : }
111 26 : H5Sclose(hAttrSpace);
112 26 : H5Tclose(hAttrNativeType);
113 26 : H5Tclose(hAttrTypeID);
114 :
115 26 : H5Dclose(hArrayId);
116 26 : H5Gclose(hHDFEOSInformation);
117 :
118 26 : if (osResult.empty())
119 0 : return false;
120 :
121 : // Parse StructMetadata.0 with NASAKeywordHandler
122 52 : NASAKeywordHandler oKWHandler;
123 : #ifdef DEBUG
124 26 : CPLDebug("HDF5EOS", "%s", osResult.c_str());
125 : #endif
126 26 : if (!oKWHandler.Parse(osResult.c_str()))
127 : {
128 0 : CPLError(CE_Failure, CPLE_AppDefined,
129 : "Cannot parse HDFEOS INFORMATION.StructMetadata.0 with "
130 : "NASAKeywordHandler");
131 0 : return false;
132 : }
133 :
134 52 : auto oJsonRoot = oKWHandler.GetJsonObject();
135 78 : auto oGridStructure = oJsonRoot.GetObj("GridStructure");
136 52 : auto oSwathStructure = oJsonRoot.GetObj("SwathStructure");
137 26 : bool bOK = false;
138 : // An empty
139 : // GROUP=GridStructure
140 : // END_GROUP=GridStructure
141 : // will generate 2 keys (_type and END_GROUP)
142 26 : if (oGridStructure.IsValid() && oGridStructure.GetChildren().size() > 2)
143 : {
144 12 : bOK = true;
145 12 : m_eDataModel = DataModel::GRID;
146 12 : ParseGridStructure(oGridStructure);
147 : }
148 28 : else if (oSwathStructure.IsValid() &&
149 28 : oSwathStructure.GetChildren().size() > 2)
150 : {
151 14 : bOK = true;
152 14 : m_eDataModel = DataModel::SWATH;
153 14 : ParseSwathStructure(oSwathStructure);
154 : }
155 :
156 26 : return bOK;
157 : }
158 :
159 : /************************************************************************/
160 : /* GetGTCPProjectionCode() */
161 : /************************************************************************/
162 :
163 12 : static int GetGTCPProjectionCode(const std::string &osProjection)
164 : {
165 12 : const char *const apszGCTPProjections[] = {
166 : "HE5_GCTP_GEO", "HE5_GCTP_UTM", "HE5_GCTP_SPCS",
167 : "HE5_GCTP_ALBERS", "HE5_GCTP_LAMCC", "HE5_GCTP_MERCAT",
168 : "HE5_GCTP_PS", "HE5_GCTP_POLYC", "HE5_GCTP_EQUIDC",
169 : "HE5_GCTP_TM", "HE5_GCTP_STEREO", "HE5_GCTP_LAMAZ",
170 : "HE5_GCTP_AZMEQD", "HE5_GCTP_GNOMON", "HE5_GCTP_ORTHO",
171 : "HE5_GCTP_GVNSP", "HE5_GCTP_SNSOID", "HE5_GCTP_EQRECT",
172 : "HE5_GCTP_MILLER", "HE5_GCTP_VGRINT", "HE5_GCTP_HOM",
173 : "HE5_GCTP_ROBIN", "HE5_GCTP_SOM", "HE5_GCTP_ALASKA",
174 : "HE5_GCTP_GOOD", "HE5_GCTP_MOLL", "HE5_GCTP_IMOLL",
175 : "HE5_GCTP_HAMMER", "HE5_GCTP_WAGIV", "HE5_GCTP_WAGVII",
176 : "HE5_GCTP_OBLEQA"};
177 : // HE5_GCTP_CEA, HE5_GCTP_BCEA, HE5_GCTP_ISINUS not taken
178 : // into account.
179 81 : for (int i = 0; i < static_cast<int>(CPL_ARRAYSIZE(apszGCTPProjections));
180 : ++i)
181 : {
182 81 : if (osProjection == apszGCTPProjections[i])
183 : {
184 12 : return i;
185 : }
186 : }
187 0 : return -1;
188 : }
189 :
190 : /************************************************************************/
191 : /* ParseGridStructure() */
192 : /************************************************************************/
193 :
194 12 : void HDF5EOSParser::ParseGridStructure(const CPLJSONObject &oGridStructure)
195 : {
196 48 : for (const auto &oGrid : oGridStructure.GetChildren())
197 : {
198 36 : if (oGrid.GetType() == CPLJSONObject::Type::Object)
199 : {
200 36 : const auto osGridName = oGrid.GetString("GridName");
201 36 : const auto oDataFields = oGrid.GetObj("DataField");
202 36 : const auto oDimensions = oGrid.GetObj("Dimension");
203 24 : std::map<std::string, int> oMapDimensionNameToSize;
204 24 : auto poGridMetadata = std::make_unique<GridMetadata>();
205 12 : poGridMetadata->osGridName = osGridName;
206 60 : for (const auto &oDimension : oDimensions.GetChildren())
207 : {
208 48 : if (oDimension.GetType() == CPLJSONObject::Type::Object)
209 : {
210 : const auto osDimensionName =
211 72 : oDimension.GetString("DimensionName");
212 24 : int nSize = oDimension.GetInteger("Size");
213 24 : oMapDimensionNameToSize[osDimensionName] = nSize;
214 48 : Dimension oDim;
215 24 : oDim.osName = osDimensionName;
216 24 : oDim.nSize = nSize;
217 24 : poGridMetadata->aoDimensions.push_back(oDim);
218 : }
219 : }
220 :
221 : // Happens for example for products following
222 : // AMSR-E/AMSR2 Unified L3 Daily 12.5 km Brightness Temperatures,
223 : // Sea Ice Concentration, Motion & Snow Depth Polar Grids
224 : // (https://nsidc.org/sites/default/files/au_si12-v001-userguide_1.pdf)
225 : // such as
226 : // https://n5eil01u.ecs.nsidc.org/AMSA/AU_SI12.001/2012.07.02/AMSR_U2_L3_SeaIce12km_B04_20120702.he5
227 12 : const int nXDim = oGrid.GetInteger("XDim", 0);
228 12 : const int nYDim = oGrid.GetInteger("YDim", 0);
229 12 : if (poGridMetadata->aoDimensions.empty() && nXDim > 0 && nYDim > 0)
230 : {
231 : // Check that all data fields have a DimList=(YDim,XDim)
232 : // property. This may be unneeded, but at least if we meet
233 : // this condition, that should be a strong hint that the first
234 : // dimension is Y, and the second X.
235 2 : bool bDimListIsYDimXDim = true;
236 8 : for (const auto &oDataField : oDataFields.GetChildren())
237 : {
238 6 : if (oDataField.GetType() == CPLJSONObject::Type::Object)
239 : {
240 4 : const auto oDimList = oDataField.GetArray("DimList");
241 4 : if (!(oDimList.Size() == 2 &&
242 4 : oDimList[0].ToString() == "YDim" &&
243 4 : oDimList[1].ToString() == "XDim"))
244 : {
245 0 : bDimListIsYDimXDim = false;
246 0 : break;
247 : }
248 : }
249 : }
250 2 : if (bDimListIsYDimXDim)
251 : {
252 : {
253 4 : const std::string osDimensionName("YDim");
254 2 : oMapDimensionNameToSize[osDimensionName] = nYDim;
255 4 : Dimension oDim;
256 2 : oDim.osName = osDimensionName;
257 2 : oDim.nSize = nYDim;
258 2 : poGridMetadata->aoDimensions.push_back(oDim);
259 : }
260 : {
261 4 : const std::string osDimensionName("XDim");
262 2 : oMapDimensionNameToSize[osDimensionName] = nXDim;
263 4 : Dimension oDim;
264 2 : oDim.osName = osDimensionName;
265 2 : oDim.nSize = nXDim;
266 2 : poGridMetadata->aoDimensions.push_back(oDim);
267 : }
268 : }
269 : }
270 :
271 12 : poGridMetadata->osProjection = oGrid.GetString("Projection");
272 24 : poGridMetadata->nProjCode =
273 12 : GetGTCPProjectionCode(poGridMetadata->osProjection);
274 12 : poGridMetadata->osGridOrigin = oGrid.GetString("GridOrigin");
275 12 : poGridMetadata->nZone = oGrid.GetInteger("ZoneCode", -1);
276 12 : poGridMetadata->nSphereCode = oGrid.GetInteger("SphereCode", -1);
277 :
278 36 : const auto oProjParams = oGrid.GetArray("ProjParams");
279 64 : for (int j = 0; j < oProjParams.Size(); ++j)
280 52 : poGridMetadata->adfProjParams.push_back(
281 52 : oProjParams[j].ToDouble());
282 :
283 : const auto oUpperLeftPointMtrs =
284 36 : oGrid.GetArray("UpperLeftPointMtrs");
285 36 : for (int j = 0; j < oUpperLeftPointMtrs.Size(); ++j)
286 24 : poGridMetadata->adfUpperLeftPointMeters.push_back(
287 24 : oUpperLeftPointMtrs[j].ToDouble());
288 :
289 36 : const auto oLowerRightMtrs = oGrid.GetArray("LowerRightMtrs");
290 36 : for (int j = 0; j < oLowerRightMtrs.Size(); ++j)
291 24 : poGridMetadata->adfLowerRightPointMeters.push_back(
292 24 : oLowerRightMtrs[j].ToDouble());
293 :
294 12 : m_oMapGridNameToGridMetadata[osGridName] =
295 24 : std::move(poGridMetadata);
296 : const auto poGridMetadataRef =
297 12 : m_oMapGridNameToGridMetadata[osGridName].get();
298 :
299 48 : for (const auto &oDataField : oDataFields.GetChildren())
300 : {
301 36 : if (oDataField.GetType() == CPLJSONObject::Type::Object)
302 : {
303 : const auto osDataFieldName =
304 36 : oDataField.GetString("DataFieldName");
305 36 : const auto oDimList = oDataField.GetArray("DimList");
306 24 : GridDataFieldMetadata oDataFieldMetadata;
307 12 : bool bValid = oDimList.Size() > 0;
308 40 : for (int j = 0; j < oDimList.Size(); ++j)
309 : {
310 56 : const auto osDimensionName = oDimList[j].ToString();
311 : const auto oIter = oMapDimensionNameToSize.find(
312 28 : osDimensionName.c_str());
313 28 : if (oIter == oMapDimensionNameToSize.end())
314 : {
315 0 : bValid = false;
316 0 : break;
317 : }
318 56 : Dimension oDim;
319 28 : oDim.osName = osDimensionName;
320 28 : oDim.nSize = oIter->second;
321 28 : oDataFieldMetadata.aoDimensions.push_back(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(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 : const auto 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 = osDimensionName;
518 78 : oDim.nSize = oIter->second;
519 78 : oMetadata.aoDimensions.push_back(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 : }
|