/src/gdal/frmts/netcdf/netcdf_sentinel3_sral_mwr.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: netCDF read/write Driver |
4 | | * Purpose: Sentinel 3 SRAL/MWR Level 2 products |
5 | | * Author: Even Rouault <even.rouault at spatialys.com> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | // Example product: |
14 | | // https://scihub.copernicus.eu/dhus/odata/v1/Products('65b615b0-0db9-4ced-8020-eb17818f0c26')/$value |
15 | | // Specification: |
16 | | // https://sentinel.esa.int/documents/247904/2753172/Sentinel-3-Product-Data-Format-Specification-Level-2-Land |
17 | | |
18 | | #include "netcdfdataset.h" |
19 | | |
20 | | #include <limits> |
21 | | |
22 | | /************************************************************************/ |
23 | | /* Sentinel3_SRAL_MWR_Layer */ |
24 | | /************************************************************************/ |
25 | | |
26 | | class Sentinel3_SRAL_MWR_Layer final : public OGRLayer |
27 | | { |
28 | | OGRFeatureDefn *m_poFDefn = nullptr; |
29 | | int m_cdfid; |
30 | | size_t m_nCurIdx = 0; |
31 | | size_t m_nFeatureCount = 0; |
32 | | CPLStringList m_aosMetadata{}; |
33 | | |
34 | | struct VariableInfo |
35 | | { |
36 | | int varid; |
37 | | nc_type nctype; |
38 | | double scale; |
39 | | double offset; |
40 | | double nodata; |
41 | | }; |
42 | | |
43 | | std::vector<VariableInfo> m_asVarInfo{}; |
44 | | int m_iLongitude = -1; |
45 | | int m_iLatitude = -1; |
46 | | double m_dfLongScale = 1.0; |
47 | | double m_dfLongOffset = 0.0; |
48 | | double m_dfLatScale = 1.0; |
49 | | double m_dfLatOffset = 0.0; |
50 | | |
51 | | OGRFeature *TranslateFeature(size_t nIndex); |
52 | | OGRFeature *GetNextRawFeature(); |
53 | | |
54 | | public: |
55 | | Sentinel3_SRAL_MWR_Layer(const std::string &name, int cdfid, int dimid); |
56 | | ~Sentinel3_SRAL_MWR_Layer() override; |
57 | | |
58 | | const OGRFeatureDefn *GetLayerDefn() const override |
59 | 0 | { |
60 | 0 | return m_poFDefn; |
61 | 0 | } |
62 | | |
63 | | void ResetReading() override; |
64 | | OGRFeature *GetNextFeature() override; |
65 | | OGRFeature *GetFeature(GIntBig nFID) override; |
66 | | GIntBig GetFeatureCount(int bForce) override; |
67 | | int TestCapability(const char *pszCap) const override; |
68 | | CSLConstList GetMetadata(const char *pszDomain) override; |
69 | | const char *GetMetadataItem(const char *pszKey, |
70 | | const char *pszDomain) override; |
71 | | }; |
72 | | |
73 | | /************************************************************************/ |
74 | | /* Sentinel3_SRAL_MWR_Layer() */ |
75 | | /************************************************************************/ |
76 | | |
77 | | Sentinel3_SRAL_MWR_Layer::Sentinel3_SRAL_MWR_Layer(const std::string &name, |
78 | | int cdfid, int dimid) |
79 | 0 | : m_cdfid(cdfid) |
80 | 0 | { |
81 | 0 | m_poFDefn = new OGRFeatureDefn(name.c_str()); |
82 | 0 | m_poFDefn->SetGeomType(wkbPoint); |
83 | 0 | m_poFDefn->Reference(); |
84 | 0 | SetDescription(name.c_str()); |
85 | |
|
86 | 0 | nc_inq_dimlen(cdfid, dimid, &m_nFeatureCount); |
87 | |
|
88 | 0 | int nVars = 0; |
89 | 0 | NCDF_ERR(nc_inq(cdfid, nullptr, &nVars, nullptr, nullptr)); |
90 | 0 | for (int iVar = 0; iVar < nVars; iVar++) |
91 | 0 | { |
92 | 0 | int nVarDims = 0; |
93 | 0 | NCDF_ERR(nc_inq_varndims(cdfid, iVar, &nVarDims)); |
94 | 0 | if (nVarDims != 1) |
95 | 0 | continue; |
96 | | |
97 | 0 | int vardimid = -1; |
98 | 0 | NCDF_ERR(nc_inq_vardimid(cdfid, iVar, &vardimid)); |
99 | 0 | if (vardimid != dimid) |
100 | 0 | continue; |
101 | | |
102 | 0 | char szVarName[NC_MAX_NAME + 1] = {}; |
103 | 0 | NCDF_ERR(nc_inq_varname(cdfid, iVar, szVarName)); |
104 | |
|
105 | 0 | nc_type vartype = NC_NAT; |
106 | 0 | nc_inq_vartype(cdfid, iVar, &vartype); |
107 | |
|
108 | 0 | int nbAttr = 0; |
109 | 0 | NCDF_ERR(nc_inq_varnatts(cdfid, iVar, &nbAttr)); |
110 | 0 | std::string scaleFactor; |
111 | 0 | std::string offset; |
112 | 0 | std::string fillValue; |
113 | 0 | CPLStringList aosMetadata; |
114 | 0 | for (int iAttr = 0; iAttr < nbAttr; iAttr++) |
115 | 0 | { |
116 | 0 | char szAttrName[NC_MAX_NAME + 1]; |
117 | 0 | szAttrName[0] = 0; |
118 | 0 | NCDF_ERR(nc_inq_attname(cdfid, iVar, iAttr, szAttrName)); |
119 | 0 | char *pszMetaTemp = nullptr; |
120 | 0 | if (NCDFGetAttr(cdfid, iVar, szAttrName, &pszMetaTemp) == CE_None && |
121 | 0 | pszMetaTemp) |
122 | 0 | { |
123 | 0 | if (EQUAL(szAttrName, "scale_factor")) |
124 | 0 | { |
125 | 0 | scaleFactor = pszMetaTemp; |
126 | 0 | } |
127 | 0 | else if (EQUAL(szAttrName, "add_offset")) |
128 | 0 | { |
129 | 0 | offset = pszMetaTemp; |
130 | 0 | } |
131 | 0 | else if (EQUAL(szAttrName, "_FillValue")) |
132 | 0 | { |
133 | 0 | fillValue = pszMetaTemp; |
134 | 0 | } |
135 | 0 | else if (!EQUAL(szAttrName, "coordinates")) |
136 | 0 | { |
137 | 0 | aosMetadata.SetNameValue(szAttrName, pszMetaTemp); |
138 | 0 | } |
139 | 0 | } |
140 | 0 | CPLFree(pszMetaTemp); |
141 | 0 | } |
142 | |
|
143 | 0 | const char *pszStandardName = |
144 | 0 | aosMetadata.FetchNameValue("standard_name"); |
145 | 0 | if (pszStandardName) |
146 | 0 | { |
147 | 0 | if (EQUAL(pszStandardName, "longitude") && vartype == NC_INT) |
148 | 0 | { |
149 | 0 | m_iLongitude = iVar; |
150 | 0 | if (!scaleFactor.empty()) |
151 | 0 | m_dfLongScale = CPLAtof(scaleFactor.c_str()); |
152 | 0 | if (!offset.empty()) |
153 | 0 | m_dfLongOffset = CPLAtof(offset.c_str()); |
154 | 0 | continue; |
155 | 0 | } |
156 | 0 | if (EQUAL(pszStandardName, "latitude") && vartype == NC_INT) |
157 | 0 | { |
158 | 0 | m_iLatitude = iVar; |
159 | 0 | if (!scaleFactor.empty()) |
160 | 0 | m_dfLatScale = CPLAtof(scaleFactor.c_str()); |
161 | 0 | if (!offset.empty()) |
162 | 0 | m_dfLatOffset = CPLAtof(offset.c_str()); |
163 | 0 | continue; |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | 0 | for (int i = 0; i < aosMetadata.size(); i++) |
168 | 0 | m_aosMetadata.AddString( |
169 | 0 | (std::string(szVarName) + '_' + aosMetadata[i]).c_str()); |
170 | |
|
171 | 0 | OGRFieldType eType = OFTReal; |
172 | 0 | if (!scaleFactor.empty()) |
173 | 0 | { |
174 | | // do nothing |
175 | 0 | } |
176 | 0 | else if (!offset.empty()) |
177 | 0 | { |
178 | | // do nothing |
179 | 0 | } |
180 | 0 | else if (vartype == NC_BYTE || vartype == NC_SHORT || |
181 | 0 | vartype == NC_INT || vartype == NC_USHORT || |
182 | 0 | vartype == NC_UINT) |
183 | 0 | { |
184 | 0 | eType = OFTInteger; |
185 | 0 | } |
186 | 0 | OGRFieldDefn oField(szVarName, eType); |
187 | 0 | m_poFDefn->AddFieldDefn(&oField); |
188 | 0 | VariableInfo varInfo; |
189 | 0 | varInfo.varid = iVar; |
190 | 0 | varInfo.nctype = vartype; |
191 | 0 | varInfo.scale = |
192 | 0 | scaleFactor.empty() ? 1.0 : CPLAtof(scaleFactor.c_str()); |
193 | 0 | varInfo.offset = offset.empty() ? 0.0 : CPLAtof(offset.c_str()); |
194 | 0 | varInfo.nodata = fillValue.empty() |
195 | 0 | ? std::numeric_limits<double>::quiet_NaN() |
196 | 0 | : CPLAtof(fillValue.c_str()); |
197 | 0 | m_asVarInfo.emplace_back(varInfo); |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | | /************************************************************************/ |
202 | | /* ~Sentinel3_SRAL_MWR_Layer() */ |
203 | | /************************************************************************/ |
204 | | |
205 | | Sentinel3_SRAL_MWR_Layer::~Sentinel3_SRAL_MWR_Layer() |
206 | 0 | { |
207 | 0 | m_poFDefn->Release(); |
208 | 0 | } |
209 | | |
210 | | /************************************************************************/ |
211 | | /* GetMetadata() */ |
212 | | /************************************************************************/ |
213 | | |
214 | | CSLConstList Sentinel3_SRAL_MWR_Layer::GetMetadata(const char *pszDomain) |
215 | 0 | { |
216 | 0 | if (pszDomain == nullptr || EQUAL(pszDomain, "")) |
217 | 0 | return m_aosMetadata.List(); |
218 | 0 | return OGRLayer::GetMetadata(pszDomain); |
219 | 0 | } |
220 | | |
221 | | /************************************************************************/ |
222 | | /* GetMetadataItem() */ |
223 | | /************************************************************************/ |
224 | | |
225 | | const char *Sentinel3_SRAL_MWR_Layer::GetMetadataItem(const char *pszKey, |
226 | | const char *pszDomain) |
227 | 0 | { |
228 | 0 | if (pszDomain == nullptr || EQUAL(pszDomain, "")) |
229 | 0 | return m_aosMetadata.FetchNameValue(pszKey); |
230 | 0 | return OGRLayer::GetMetadataItem(pszKey, pszDomain); |
231 | 0 | } |
232 | | |
233 | | /************************************************************************/ |
234 | | /* ResetReading() */ |
235 | | /************************************************************************/ |
236 | | |
237 | | void Sentinel3_SRAL_MWR_Layer::ResetReading() |
238 | 0 | { |
239 | 0 | m_nCurIdx = 0; |
240 | 0 | } |
241 | | |
242 | | /************************************************************************/ |
243 | | /* GetFeatureCount() */ |
244 | | /************************************************************************/ |
245 | | |
246 | | GIntBig Sentinel3_SRAL_MWR_Layer::GetFeatureCount(int bForce) |
247 | 0 | { |
248 | 0 | if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr) |
249 | 0 | return m_nFeatureCount; |
250 | 0 | return OGRLayer::GetFeatureCount(bForce); |
251 | 0 | } |
252 | | |
253 | | /************************************************************************/ |
254 | | /* TestCapability() */ |
255 | | /************************************************************************/ |
256 | | |
257 | | int Sentinel3_SRAL_MWR_Layer::TestCapability(const char *pszCap) const |
258 | 0 | { |
259 | 0 | if (EQUAL(pszCap, OLCFastFeatureCount)) |
260 | 0 | return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr; |
261 | 0 | if (EQUAL(pszCap, OLCRandomRead)) |
262 | 0 | return true; |
263 | 0 | return false; |
264 | 0 | } |
265 | | |
266 | | /************************************************************************/ |
267 | | /* TranslateFeature() */ |
268 | | /************************************************************************/ |
269 | | |
270 | | OGRFeature *Sentinel3_SRAL_MWR_Layer::TranslateFeature(size_t nIndex) |
271 | 0 | { |
272 | 0 | OGRFeature *poFeat = new OGRFeature(m_poFDefn); |
273 | 0 | poFeat->SetFID(nIndex + 1); |
274 | 0 | if (m_iLongitude >= 0 && m_iLatitude >= 0) |
275 | 0 | { |
276 | 0 | int nLong = 0; |
277 | 0 | int status = nc_get_var1_int(m_cdfid, m_iLongitude, &nIndex, &nLong); |
278 | 0 | if (status == NC_NOERR) |
279 | 0 | { |
280 | 0 | int nLat = 0; |
281 | 0 | status = nc_get_var1_int(m_cdfid, m_iLatitude, &nIndex, &nLat); |
282 | 0 | if (status == NC_NOERR) |
283 | 0 | { |
284 | 0 | const double dfLong = nLong * m_dfLongScale + m_dfLongOffset; |
285 | 0 | const double dfLat = nLat * m_dfLatScale + m_dfLatOffset; |
286 | 0 | auto poGeom = new OGRPoint(dfLong, dfLat); |
287 | 0 | auto poGeomField = m_poFDefn->GetGeomFieldDefn(0); |
288 | 0 | poGeom->assignSpatialReference(poGeomField->GetSpatialRef()); |
289 | 0 | poFeat->SetGeometryDirectly(poGeom); |
290 | 0 | } |
291 | 0 | } |
292 | 0 | } |
293 | |
|
294 | 0 | for (int i = 0; i < static_cast<int>(m_asVarInfo.size()); i++) |
295 | 0 | { |
296 | 0 | if (m_asVarInfo[i].nctype == NC_BYTE) |
297 | 0 | { |
298 | 0 | signed char nVal = 0; |
299 | 0 | int status = nc_get_var1_schar(m_cdfid, m_asVarInfo[i].varid, |
300 | 0 | &nIndex, &nVal); |
301 | 0 | if (status == NC_NOERR && nVal != m_asVarInfo[i].nodata) |
302 | 0 | { |
303 | 0 | poFeat->SetField(i, nVal * m_asVarInfo[i].scale + |
304 | 0 | m_asVarInfo[i].offset); |
305 | 0 | } |
306 | 0 | } |
307 | 0 | else if (m_asVarInfo[i].nctype == NC_SHORT) |
308 | 0 | { |
309 | 0 | short nVal = 0; |
310 | 0 | int status = nc_get_var1_short(m_cdfid, m_asVarInfo[i].varid, |
311 | 0 | &nIndex, &nVal); |
312 | 0 | if (status == NC_NOERR && nVal != m_asVarInfo[i].nodata) |
313 | 0 | { |
314 | 0 | poFeat->SetField(i, nVal * m_asVarInfo[i].scale + |
315 | 0 | m_asVarInfo[i].offset); |
316 | 0 | } |
317 | 0 | } |
318 | 0 | else if (m_asVarInfo[i].nctype == NC_USHORT) |
319 | 0 | { |
320 | 0 | unsigned short nVal = 0; |
321 | 0 | int status = nc_get_var1_ushort(m_cdfid, m_asVarInfo[i].varid, |
322 | 0 | &nIndex, &nVal); |
323 | 0 | if (status == NC_NOERR && nVal != m_asVarInfo[i].nodata) |
324 | 0 | { |
325 | 0 | poFeat->SetField(i, nVal * m_asVarInfo[i].scale + |
326 | 0 | m_asVarInfo[i].offset); |
327 | 0 | } |
328 | 0 | } |
329 | 0 | else if (m_asVarInfo[i].nctype == NC_INT) |
330 | 0 | { |
331 | 0 | int nVal = 0; |
332 | 0 | int status = |
333 | 0 | nc_get_var1_int(m_cdfid, m_asVarInfo[i].varid, &nIndex, &nVal); |
334 | 0 | if (status == NC_NOERR && nVal != m_asVarInfo[i].nodata) |
335 | 0 | { |
336 | 0 | poFeat->SetField(i, nVal * m_asVarInfo[i].scale + |
337 | 0 | m_asVarInfo[i].offset); |
338 | 0 | } |
339 | 0 | } |
340 | 0 | else if (m_asVarInfo[i].nctype == NC_UINT) |
341 | 0 | { |
342 | 0 | unsigned int nVal = 0; |
343 | 0 | int status = |
344 | 0 | nc_get_var1_uint(m_cdfid, m_asVarInfo[i].varid, &nIndex, &nVal); |
345 | 0 | if (status == NC_NOERR && nVal != m_asVarInfo[i].nodata) |
346 | 0 | { |
347 | 0 | poFeat->SetField(i, nVal * m_asVarInfo[i].scale + |
348 | 0 | m_asVarInfo[i].offset); |
349 | 0 | } |
350 | 0 | } |
351 | 0 | else if (m_asVarInfo[i].nctype == NC_DOUBLE) |
352 | 0 | { |
353 | 0 | double dfVal = 0.0; |
354 | 0 | int status = nc_get_var1_double(m_cdfid, m_asVarInfo[i].varid, |
355 | 0 | &nIndex, &dfVal); |
356 | 0 | if (status == NC_NOERR && dfVal != m_asVarInfo[i].nodata) |
357 | 0 | { |
358 | 0 | poFeat->SetField(i, dfVal * m_asVarInfo[i].scale + |
359 | 0 | m_asVarInfo[i].offset); |
360 | 0 | } |
361 | 0 | } |
362 | 0 | else |
363 | 0 | { |
364 | 0 | CPLDebug("netCDF", "Unhandled data type %d for %s", |
365 | 0 | m_asVarInfo[i].nctype, |
366 | 0 | m_poFDefn->GetFieldDefn(i)->GetNameRef()); |
367 | 0 | } |
368 | 0 | } |
369 | |
|
370 | 0 | return poFeat; |
371 | 0 | } |
372 | | |
373 | | /************************************************************************/ |
374 | | /* GetNextRawFeature() */ |
375 | | /************************************************************************/ |
376 | | |
377 | | OGRFeature *Sentinel3_SRAL_MWR_Layer::GetNextRawFeature() |
378 | 0 | { |
379 | 0 | if (m_nCurIdx == m_nFeatureCount) |
380 | 0 | return nullptr; |
381 | 0 | OGRFeature *poFeat = TranslateFeature(m_nCurIdx); |
382 | 0 | m_nCurIdx++; |
383 | 0 | return poFeat; |
384 | 0 | } |
385 | | |
386 | | /************************************************************************/ |
387 | | /* GetFeature() */ |
388 | | /************************************************************************/ |
389 | | |
390 | | OGRFeature *Sentinel3_SRAL_MWR_Layer::GetFeature(GIntBig nFID) |
391 | 0 | { |
392 | 0 | if (nFID <= 0 || static_cast<size_t>(nFID) > m_nFeatureCount) |
393 | 0 | return nullptr; |
394 | 0 | return TranslateFeature(static_cast<size_t>(nFID - 1)); |
395 | 0 | } |
396 | | |
397 | | /************************************************************************/ |
398 | | /* GetNextFeature() */ |
399 | | /************************************************************************/ |
400 | | |
401 | | OGRFeature *Sentinel3_SRAL_MWR_Layer::GetNextFeature() |
402 | 0 | { |
403 | 0 | while (true) |
404 | 0 | { |
405 | 0 | OGRFeature *poFeature = GetNextRawFeature(); |
406 | 0 | if (poFeature == nullptr) |
407 | 0 | return nullptr; |
408 | | |
409 | 0 | if ((m_poFilterGeom == nullptr || |
410 | 0 | FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) && |
411 | 0 | (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature))) |
412 | 0 | return poFeature; |
413 | | |
414 | 0 | delete poFeature; |
415 | 0 | } |
416 | 0 | } |
417 | | |
418 | | /************************************************************************/ |
419 | | /* ProcessSentinel3_SRAL_MWR() */ |
420 | | /************************************************************************/ |
421 | | |
422 | | void netCDFDataset::ProcessSentinel3_SRAL_MWR() |
423 | 0 | { |
424 | 0 | int nDimCount = -1; |
425 | 0 | int status = nc_inq_ndims(cdfid, &nDimCount); |
426 | 0 | NCDF_ERR(status); |
427 | 0 | if (status != NC_NOERR || nDimCount == 0 || nDimCount > 1000) |
428 | 0 | return; |
429 | 0 | std::vector<int> dimIds(nDimCount); |
430 | 0 | int nDimCount2 = -1; |
431 | 0 | status = nc_inq_dimids(cdfid, &nDimCount2, &dimIds[0], FALSE); |
432 | 0 | NCDF_ERR(status); |
433 | 0 | if (status != NC_NOERR) |
434 | 0 | return; |
435 | 0 | CPLAssert(nDimCount == nDimCount2); |
436 | |
|
437 | 0 | OGRSpatialReference *poSRS = nullptr; |
438 | 0 | const char *pszSemiMajor = |
439 | 0 | aosMetadata.FetchNameValue("NC_GLOBAL#semi_major_ellipsoid_axis"); |
440 | 0 | const char *pszFlattening = |
441 | 0 | aosMetadata.FetchNameValue("NC_GLOBAL#ellipsoid_flattening"); |
442 | 0 | if (pszSemiMajor && EQUAL(pszSemiMajor, "6378137") && pszFlattening && |
443 | 0 | fabs(CPLAtof(pszFlattening) - 0.00335281066474748) < 1e-16) |
444 | 0 | { |
445 | 0 | int iAttr = aosMetadata.FindName("NC_GLOBAL#semi_major_ellipsoid_axis"); |
446 | 0 | if (iAttr >= 0) |
447 | 0 | aosMetadata.RemoveStrings(iAttr, 1); |
448 | |
|
449 | 0 | iAttr = aosMetadata.FindName("NC_GLOBAL#ellipsoid_flattening"); |
450 | 0 | if (iAttr >= 0) |
451 | 0 | aosMetadata.RemoveStrings(iAttr, 1); |
452 | 0 | poSRS = new OGRSpatialReference(); |
453 | 0 | poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
454 | 0 | poSRS->importFromEPSG(4326); |
455 | 0 | } |
456 | |
|
457 | 0 | for (int i = 0; i < nDimCount; i++) |
458 | 0 | { |
459 | 0 | char szDimName[NC_MAX_NAME + 1] = {}; |
460 | 0 | status = nc_inq_dimname(cdfid, dimIds[i], szDimName); |
461 | 0 | NCDF_ERR(status); |
462 | 0 | if (status != NC_NOERR) |
463 | 0 | break; |
464 | 0 | std::string name(CPLGetBasenameSafe(GetDescription())); |
465 | 0 | name += '_'; |
466 | 0 | name += szDimName; |
467 | 0 | std::shared_ptr<OGRLayer> poLayer( |
468 | 0 | new Sentinel3_SRAL_MWR_Layer(name.c_str(), cdfid, dimIds[i])); |
469 | 0 | auto poGeomField = poLayer->GetLayerDefn()->GetGeomFieldDefn(0); |
470 | 0 | if (poGeomField) |
471 | 0 | poGeomField->SetSpatialRef(poSRS); |
472 | 0 | papoLayers.emplace_back(poLayer); |
473 | 0 | } |
474 | |
|
475 | 0 | if (poSRS) |
476 | 0 | poSRS->Release(); |
477 | 0 | } |