Compare commits
15 Commits
e191f99c8f
...
86c40b42f2
Author | SHA1 | Date |
---|---|---|
Michael Uleysky | 86c40b42f2 | 8 months ago |
Michael Uleysky | 400de53eef | 8 months ago |
Michael Uleysky | dc92f4697f | 8 months ago |
Michael Uleysky | 93cee61939 | 8 months ago |
Michael Uleysky | 82b233b443 | 8 months ago |
Michael Uleysky | 3e9430673d | 8 months ago |
Michael Uleysky | f0fefd5f90 | 8 months ago |
Michael Uleysky | 8bfa07b9c9 | 8 months ago |
Michael Uleysky | 5ca27193f7 | 8 months ago |
Michael Uleysky | 2a99a98192 | 8 months ago |
Michael Uleysky | 01af4109c1 | 8 months ago |
Michael Uleysky | 8a4fbea40a | 8 months ago |
Michael Uleysky | 8670511319 | 8 months ago |
Michael Uleysky | 775a038263 | 8 months ago |
Michael Uleysky | 4627f76a0d | 8 months ago |
31 changed files with 2676 additions and 210 deletions
@ -0,0 +1,54 @@ |
|||||||
|
#pragma once |
||||||
|
#include "cache.h" |
||||||
|
#include "curlfuncs.h" |
||||||
|
#include "merrors.h" |
||||||
|
#include <json/json.h> |
||||||
|
|
||||||
|
using michlib::Error; |
||||||
|
using michlib::RetVal; |
||||||
|
|
||||||
|
class CopernicusCatalog |
||||||
|
{ |
||||||
|
static const MString caturl; |
||||||
|
|
||||||
|
std::unique_ptr<GenericCache> cache; |
||||||
|
CURLRAII chandle; |
||||||
|
Json::Value catalog; |
||||||
|
|
||||||
|
// Download catalog
|
||||||
|
Error GetCatalog(); |
||||||
|
|
||||||
|
// Asset url from dataset
|
||||||
|
RetVal<MString> AssetURL(const MString& prod, const MString& dataset, const MString& asset) const; |
||||||
|
|
||||||
|
public: |
||||||
|
CopernicusCatalog(); |
||||||
|
|
||||||
|
// Download JSON from url
|
||||||
|
RetVal<Json::Value> GetJSON(const MString& url) const; |
||||||
|
|
||||||
|
// List of products
|
||||||
|
RetVal<std::vector<MString>> ProductList() const; |
||||||
|
|
||||||
|
// List of datasets in product
|
||||||
|
RetVal<std::vector<MString>> DatasetList(const MString& prod) const; |
||||||
|
|
||||||
|
// URL of product
|
||||||
|
RetVal<MString> ProductURL(const MString& prod) const; |
||||||
|
|
||||||
|
// URL of dataset
|
||||||
|
RetVal<MString> DatasetURL(const MString& prod, const MString& dataset) const; |
||||||
|
|
||||||
|
// URL of native data (files) in dataset
|
||||||
|
RetVal<MString> DatasetNativeURL(const MString& prod, const MString& dataset) const { return AssetURL(prod, dataset, "native"); } |
||||||
|
|
||||||
|
// URL of timechuncked data (files) in dataset
|
||||||
|
RetVal<MString> DatasetTimeURL(const MString& prod, const MString& dataset) const { return AssetURL(prod, dataset, "timeChunked"); } |
||||||
|
|
||||||
|
// URL of geochuncked data (files) in dataset
|
||||||
|
RetVal<MString> DatasetGeoURL(const MString& prod, const MString& dataset) const { return AssetURL(prod, dataset, "geoChunked"); } |
||||||
|
|
||||||
|
bool Valid() const { return catalog.isObject(); } |
||||||
|
|
||||||
|
explicit operator bool() const { return Valid(); } |
||||||
|
}; |
@ -0,0 +1,207 @@ |
|||||||
|
#pragma once |
||||||
|
#include "gsw.h" |
||||||
|
#include "ncfuncs.h" |
||||||
|
#include "simple2ddata.h" |
||||||
|
#include "zarr.h" |
||||||
|
#include <memory> |
||||||
|
|
||||||
|
using michlib::Ceil; |
||||||
|
using michlib::DetGeoDomain; |
||||||
|
using michlib::Floor; |
||||||
|
using michlib::GPL; |
||||||
|
using michlib::int2; |
||||||
|
|
||||||
|
class LayeredDataZ: public NCFuncs |
||||||
|
{ |
||||||
|
public: |
||||||
|
using Data = Simple2DData; |
||||||
|
|
||||||
|
private: |
||||||
|
class NC: public Zarr |
||||||
|
{ |
||||||
|
std::vector<MDateTime> times; |
||||||
|
|
||||||
|
public: |
||||||
|
Error ReadTimes(const MString& tname) |
||||||
|
{ |
||||||
|
static const MString pref = "LayeredDataZ::NC::ReadTimes"; |
||||||
|
if(!*this) return Error(pref, "Dataset not open"); |
||||||
|
std::vector<double> time; |
||||||
|
|
||||||
|
{ |
||||||
|
auto ret = Read(tname, time); |
||||||
|
if(!ret) return ret.Add(pref, "Can't read time"); |
||||||
|
} |
||||||
|
|
||||||
|
MDateTime refdate; |
||||||
|
time_t step = 0; |
||||||
|
{ |
||||||
|
auto units = AttString(tname, "units"); |
||||||
|
if(!units.Exist()) return Error(pref, "Can't read refdate"); |
||||||
|
auto [rd, st, suc] = Refdate(units); |
||||||
|
if(!suc) return Error(pref, "Can't parse " + units + " to refdate"); |
||||||
|
if(st == 0) return Error(pref, "Can't get timestep from string " + units); |
||||||
|
refdate = rd; |
||||||
|
step = st; |
||||||
|
} |
||||||
|
|
||||||
|
times.resize(time.size()); |
||||||
|
for(size_t i = 0; i < time.size(); i++) times[i] = refdate + static_cast<time_t>(time[i] * step); |
||||||
|
return Error(); |
||||||
|
} |
||||||
|
|
||||||
|
MDateTime Begin() const { return times.front(); } |
||||||
|
MDateTime End() const { return times.back(); } |
||||||
|
|
||||||
|
const std::vector<MDateTime>& Times() const { return times; } |
||||||
|
|
||||||
|
size_t Index(MDateTime tm) const |
||||||
|
{ |
||||||
|
if(tm < Begin() || tm > End()) return 0; |
||||||
|
size_t b = 0, e = times.size() - 1; |
||||||
|
if(tm == times[b]) return b + 1; |
||||||
|
if(tm == times[e]) return e + 1; |
||||||
|
while(e - b > 1) |
||||||
|
{ |
||||||
|
size_t c = (e + b) / 2; |
||||||
|
if(tm == times[c]) return c + 1; |
||||||
|
if(tm > times[c]) |
||||||
|
b = c; |
||||||
|
else |
||||||
|
e = c; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
}; |
||||||
|
std::vector<NC> nc; |
||||||
|
std::vector<real> depths; |
||||||
|
bool depthinv; |
||||||
|
std::vector<MDateTime> times; |
||||||
|
struct CoordNames dname; |
||||||
|
real lonb, latb, lone, late; |
||||||
|
real lonstep, latstep; |
||||||
|
MString title; |
||||||
|
|
||||||
|
class EnvVar |
||||||
|
{ |
||||||
|
MString name, oldvalue; |
||||||
|
bool activated, saved; |
||||||
|
|
||||||
|
public: |
||||||
|
EnvVar(): activated(false) {} |
||||||
|
~EnvVar() { Deactivate(); } |
||||||
|
|
||||||
|
void Activate(const MString& var, const MString& val) |
||||||
|
{ |
||||||
|
if(activated) Deactivate(); |
||||||
|
name = var; |
||||||
|
char* curval = getenv(name.Buf()); |
||||||
|
if(nullptr == curval) |
||||||
|
saved = false; |
||||||
|
else |
||||||
|
{ |
||||||
|
oldvalue = curval; |
||||||
|
saved = true; |
||||||
|
} |
||||||
|
setenv(name.Buf(), val.Buf(), 1); |
||||||
|
} |
||||||
|
void Deactivate() |
||||||
|
{ |
||||||
|
if(!activated) return; |
||||||
|
if(saved) |
||||||
|
setenv(name.Buf(), oldvalue.Buf(), 1); |
||||||
|
else |
||||||
|
unsetenv(name.Buf()); |
||||||
|
activated = false; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
EnvVar proxy; |
||||||
|
|
||||||
|
protected: |
||||||
|
struct Parameters: public BaseParameters |
||||||
|
{ |
||||||
|
size_t xb, yb, xe, ye, layer; |
||||||
|
virtual ~Parameters() override = default; |
||||||
|
}; |
||||||
|
|
||||||
|
// TODO: RetVal
|
||||||
|
MString Open(const MString& dataset); |
||||||
|
|
||||||
|
void SetTitle(const MString& newtitle) { title = newtitle; } |
||||||
|
|
||||||
|
public: |
||||||
|
MString Info() const; |
||||||
|
|
||||||
|
std::pair<const BaseParameters*, MString> Parameters(michlib_internal::ParameterListEx& pars, const CLArgs& args, const struct Region& reg) const; |
||||||
|
|
||||||
|
bool Read(const MString& vname, std::map<MString, Data>& cache, const BaseParameters* ip, size_t i) const; |
||||||
|
|
||||||
|
bool isOk() const { return nc.size() > 0; } |
||||||
|
|
||||||
|
explicit operator bool() const { return nc.size() > 0; } |
||||||
|
|
||||||
|
real Depth(size_t l) const { return isOk() ? depths[l] : -1000.0; } |
||||||
|
|
||||||
|
real Depth(const BaseParameters* ip) const { return Depth(dynamic_cast<const struct Parameters*>(ip)->layer); } |
||||||
|
|
||||||
|
real Lon(size_t ix) const { return isOk() ? (lonb + ix * lonstep) : -1000.0; } |
||||||
|
|
||||||
|
real Lat(size_t iy) const { return isOk() ? (latb + iy * latstep) : -1000.0; } |
||||||
|
|
||||||
|
size_t NDepths() const { return depths.size(); } |
||||||
|
|
||||||
|
size_t NTimes() const { return times.size(); } |
||||||
|
|
||||||
|
MDateTime Time(size_t i) const |
||||||
|
{ |
||||||
|
if(!isOk() || i >= times.size()) return MDateTime(); |
||||||
|
return times[i]; |
||||||
|
} |
||||||
|
|
||||||
|
time_t Timestep() const { return isOk() ? (times[1] - times[0]).Seconds() : 0; } |
||||||
|
|
||||||
|
MString Title() const { return title; } |
||||||
|
|
||||||
|
MString Dump(const struct Parameters* ppar) const |
||||||
|
{ |
||||||
|
// clang-format off
|
||||||
|
return |
||||||
|
"Current settings:\n" + MString() + |
||||||
|
" Longitudes: from " + Lon(ppar->xb) + " (" + ppar->xb + ") to "+ Lon(ppar->xe) + " (" + ppar->xe + ")\n" + |
||||||
|
" Latitudes: from " + Lat(ppar->yb) + " (" + ppar->yb + ") to "+ Lat(ppar->ye) + " (" + ppar->ye + ")\n" + |
||||||
|
" Depth: layer " + ppar->layer + ", depth " + Depth(ppar->layer) + " m\n"; |
||||||
|
// clang-format on
|
||||||
|
} |
||||||
|
|
||||||
|
VarPresence CheckVar(const MString& vname) const |
||||||
|
{ |
||||||
|
return NCFuncs::CheckVar(vname, [this](const MString& vn) { return HaveVar(vn); }); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
template<class DataType> Data ReadVarRaw(const NC& f, const MString& name, size_t i, bool nodepth, const struct Parameters* p) const; |
||||||
|
|
||||||
|
bool HaveVar(const MString& vname) const |
||||||
|
{ |
||||||
|
for(size_t i = 0; i < nc.size(); i++) |
||||||
|
if(NCFuncs::HaveVar(nc[i], vname)) return true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
std::tuple<MString, size_t, size_t> VarNameLoc(const MString vname, MDateTime tm) const |
||||||
|
{ |
||||||
|
for(size_t i = 0; i < nc.size(); i++) |
||||||
|
{ |
||||||
|
auto tind = nc[i].Index(tm); |
||||||
|
if(tind == 0) continue; |
||||||
|
for(const auto& v: nc[i].Vars()) |
||||||
|
{ |
||||||
|
auto stname = nc[i].AttString(v.Name(), "standard_name"); |
||||||
|
if(!stname.Exist()) continue; |
||||||
|
if(StName2Name(stname) == vname) return {v.Name(), i, tind - 1}; |
||||||
|
} |
||||||
|
} |
||||||
|
return {"", 0, 0}; |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,87 @@ |
|||||||
|
#pragma once |
||||||
|
#include "nczarrcommon.h" |
||||||
|
#include <netcdf.h> |
||||||
|
#include <variant> |
||||||
|
|
||||||
|
class NCSimpleTypes: public NcZarrTypes |
||||||
|
{ |
||||||
|
protected: |
||||||
|
template<class VType> class ReadedData |
||||||
|
{ |
||||||
|
using Vec = std::vector<size_t>; |
||||||
|
|
||||||
|
private: |
||||||
|
std::unique_ptr<VType[]> data; |
||||||
|
|
||||||
|
public: |
||||||
|
ReadedData() = default; |
||||||
|
|
||||||
|
ReadedData(std::unique_ptr<VType[]>&& d): data(std::move(d)) {} |
||||||
|
|
||||||
|
VType operator()(size_t lini) const { return data[lini]; } |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
class NCSimpleFunctions: public NCSimpleTypes |
||||||
|
{ |
||||||
|
int ncid; |
||||||
|
|
||||||
|
RetVal<std::vector<Attribute>> ReadAtts(int vid) const; |
||||||
|
|
||||||
|
protected: |
||||||
|
NCSimpleFunctions(): ncid(0) {} |
||||||
|
|
||||||
|
template<class VType> RetVal<ReadedData<VType>> Read(const MString& var, const size_t* start, const size_t* count) const |
||||||
|
{ |
||||||
|
static const MString pref = "NCSimpleFunctions::Read"; |
||||||
|
|
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
const size_t N = vars[ind].NDim(); |
||||||
|
|
||||||
|
std::unique_ptr<VType[]> cdata; |
||||||
|
|
||||||
|
size_t dsize = 1; |
||||||
|
for(size_t i = 0; i < N; i++) dsize *= count[i]; |
||||||
|
cdata.reset(new VType[dsize]); |
||||||
|
|
||||||
|
int vid; |
||||||
|
int res = nc_inq_varid(ncid, var.Buf(), &vid); |
||||||
|
if(res != NC_NOERR) return Error(pref, MString("nc_inq_varid error: ") + nc_strerror(res)); |
||||||
|
|
||||||
|
if constexpr(std::is_same_v<VType, float>) |
||||||
|
res = nc_get_vara_float(ncid, vid, start, count, cdata.get()); |
||||||
|
else if constexpr(std::is_same_v<VType, double>) |
||||||
|
res = nc_get_vara_double(ncid, vid, start, count, cdata.get()); |
||||||
|
else if constexpr(std::is_same_v<VType, int>) |
||||||
|
res = nc_get_vara_int(ncid, vid, start, count, cdata.get()); |
||||||
|
else if constexpr(std::is_same_v<VType, long>) |
||||||
|
res = nc_get_vara_long(ncid, vid, start, count, cdata.get()); |
||||||
|
else if constexpr(std::is_same_v<VType, long long>) |
||||||
|
res = nc_get_vara_longlong(ncid, vid, start, count, cdata.get()); |
||||||
|
else if constexpr(std::is_same_v<VType, short>) |
||||||
|
res = nc_get_vara_short(ncid, vid, start, count, cdata.get()); |
||||||
|
else if constexpr(std::is_same_v<VType, signed char>) |
||||||
|
res = nc_get_vara_schar(ncid, vid, start, count, cdata.get()); |
||||||
|
else if constexpr(std::is_same_v<VType, unsigned int>) |
||||||
|
res = nc_get_vara_uint(ncid, vid, start, count, cdata.get()); |
||||||
|
else if constexpr(std::is_same_v<VType, unsigned long long>) |
||||||
|
res = nc_get_vara_ulonglong(ncid, vid, start, count, cdata.get()); |
||||||
|
else if constexpr(std::is_same_v<VType, unsigned short>) |
||||||
|
res = nc_get_vara_ushort(ncid, vid, start, count, cdata.get()); |
||||||
|
else if constexpr(std::is_same_v<VType, unsigned char>) |
||||||
|
res = nc_get_vara_ubyte(ncid, vid, start, count, cdata.get()); |
||||||
|
else |
||||||
|
return Error(pref, "Unsupported variable type"); |
||||||
|
|
||||||
|
if(res != NC_NOERR) return Error(pref, MString("nc_get_vara error: ") + nc_strerror(res)); |
||||||
|
|
||||||
|
return ReadedData<VType>(std::move(cdata)); |
||||||
|
} |
||||||
|
|
||||||
|
public: |
||||||
|
~NCSimpleFunctions() { nc_close(ncid); } |
||||||
|
|
||||||
|
Error Open(const MString& filename); |
||||||
|
}; |
||||||
|
|
||||||
|
using NCSimple = NcZarrRead<NCSimpleFunctions>; |
@ -0,0 +1,836 @@ |
|||||||
|
#pragma once |
||||||
|
#include "merrors.h" |
||||||
|
#include <utility> |
||||||
|
#include <variant> |
||||||
|
|
||||||
|
using michlib::Error; |
||||||
|
using michlib::int1; |
||||||
|
using michlib::int2; |
||||||
|
using michlib::int4; |
||||||
|
using michlib::int8; |
||||||
|
using michlib::int_cast; |
||||||
|
using michlib::MString; |
||||||
|
using michlib::RetVal; |
||||||
|
using michlib::uint1; |
||||||
|
using michlib::uint8; |
||||||
|
|
||||||
|
class NcZarrTypes |
||||||
|
{ |
||||||
|
protected: |
||||||
|
using AttVT = std::variant<std::monostate, int8, uint8, double, MString, bool>; |
||||||
|
|
||||||
|
class ArrCounter |
||||||
|
{ |
||||||
|
using VT = std::vector<size_t>; |
||||||
|
const VT count; |
||||||
|
VT ind; |
||||||
|
bool end; |
||||||
|
|
||||||
|
public: |
||||||
|
static size_t Index(const VT& i, const VT& c) |
||||||
|
{ |
||||||
|
size_t out = 0; |
||||||
|
size_t mul = 1; |
||||||
|
for(size_t ii = i.size(); ii != 0; ii--) |
||||||
|
{ |
||||||
|
out += mul * i[ii - 1]; |
||||||
|
mul *= c[ii - 1]; |
||||||
|
} |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
static VT Index(size_t lind, const VT& c) |
||||||
|
{ |
||||||
|
VT out(c.size()); |
||||||
|
|
||||||
|
size_t j = lind; |
||||||
|
|
||||||
|
for(auto i = c.size(); i > 0; i--) |
||||||
|
{ |
||||||
|
out[i - 1] = j % c[i - 1]; |
||||||
|
j = j / c[i - 1]; |
||||||
|
} |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
ArrCounter() = delete; |
||||||
|
ArrCounter(const VT& cnt): count(cnt), ind(cnt.size(), 0), end(false) {} |
||||||
|
|
||||||
|
size_t operator[](size_t i) const { return ind[i]; } |
||||||
|
|
||||||
|
ArrCounter& operator++() |
||||||
|
{ |
||||||
|
size_t curind = count.size(); |
||||||
|
while(curind != 0) |
||||||
|
{ |
||||||
|
ind[curind - 1]++; |
||||||
|
if(ind[curind - 1] >= count[curind - 1]) |
||||||
|
{ |
||||||
|
ind[curind - 1] = 0; |
||||||
|
curind--; |
||||||
|
} |
||||||
|
else |
||||||
|
return *this; |
||||||
|
} |
||||||
|
ind = count; |
||||||
|
end = true; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
explicit operator bool() const { return !end; } |
||||||
|
|
||||||
|
size_t Index() const { return Index(ind, count); } |
||||||
|
size_t Index(const VT& i) const { return Index(i, count); } |
||||||
|
VT Index(size_t lind) const { return Index(lind, count); } |
||||||
|
|
||||||
|
size_t Count(size_t i) const { return count[i]; } |
||||||
|
|
||||||
|
const VT& VIndex() const { return ind; } |
||||||
|
VT VIndex(const VT& start) const |
||||||
|
{ |
||||||
|
VT out(ind.size()); |
||||||
|
for(size_t i = 0; i < ind.size(); i++) out[i] = ind[i] + start[i]; |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
const auto& Count() const { return count; } |
||||||
|
|
||||||
|
size_t N() const |
||||||
|
{ |
||||||
|
size_t out = 1; |
||||||
|
for(size_t i = 0; i < count.size(); i++) out *= count[i]; |
||||||
|
return out; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public: |
||||||
|
enum class AttType |
||||||
|
{ |
||||||
|
UNDEF, |
||||||
|
INT, |
||||||
|
UINT, |
||||||
|
REAL, |
||||||
|
STRING, |
||||||
|
BOOL |
||||||
|
}; |
||||||
|
enum class VarType |
||||||
|
{ |
||||||
|
UNDEF, |
||||||
|
FLOAT, |
||||||
|
DOUBLE, |
||||||
|
INT1, |
||||||
|
INT2, |
||||||
|
INT4, |
||||||
|
INT8, |
||||||
|
UINT1 |
||||||
|
}; |
||||||
|
|
||||||
|
protected: |
||||||
|
template<VarType VT, class Dummy = void> struct VarType2Type; |
||||||
|
|
||||||
|
template<class Dummy> struct VarType2Type<VarType::FLOAT, Dummy> |
||||||
|
{ |
||||||
|
using type = float; |
||||||
|
}; |
||||||
|
template<class Dummy> struct VarType2Type<VarType::DOUBLE, Dummy> |
||||||
|
{ |
||||||
|
using type = double; |
||||||
|
}; |
||||||
|
template<class Dummy> struct VarType2Type<VarType::INT1, Dummy> |
||||||
|
{ |
||||||
|
using type = int1; |
||||||
|
}; |
||||||
|
template<class Dummy> struct VarType2Type<VarType::INT2, Dummy> |
||||||
|
{ |
||||||
|
using type = int2; |
||||||
|
}; |
||||||
|
template<class Dummy> struct VarType2Type<VarType::INT4, Dummy> |
||||||
|
{ |
||||||
|
using type = int4; |
||||||
|
}; |
||||||
|
template<class Dummy> struct VarType2Type<VarType::INT8, Dummy> |
||||||
|
{ |
||||||
|
using type = int8; |
||||||
|
}; |
||||||
|
template<class Dummy> struct VarType2Type<VarType::UINT1, Dummy> |
||||||
|
{ |
||||||
|
using type = uint1; |
||||||
|
}; |
||||||
|
|
||||||
|
template<VarType VT> using Type = VarType2Type<VT>::type; |
||||||
|
|
||||||
|
static constexpr size_t SizeOf(VarType vt) |
||||||
|
{ |
||||||
|
switch(vt) |
||||||
|
{ |
||||||
|
case(VarType::UNDEF): return 0; |
||||||
|
case(VarType::FLOAT): return sizeof(Type<VarType::FLOAT>); |
||||||
|
case(VarType::DOUBLE): return sizeof(Type<VarType::DOUBLE>); |
||||||
|
case(VarType::INT1): return sizeof(Type<VarType::INT1>); |
||||||
|
case(VarType::INT2): return sizeof(Type<VarType::INT2>); |
||||||
|
case(VarType::INT4): return sizeof(Type<VarType::INT4>); |
||||||
|
case(VarType::INT8): return sizeof(Type<VarType::INT8>); |
||||||
|
case(VarType::UINT1): return sizeof(Type<VarType::UINT1>); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
template<class T> static size_t FindInd(const MString& name, const std::vector<T>& arr) |
||||||
|
{ |
||||||
|
for(size_t i = 0; i < arr.size(); i++) |
||||||
|
if(arr[i].Name() == name) return i; |
||||||
|
return arr.size(); |
||||||
|
} |
||||||
|
|
||||||
|
class Attribute: public AttVT |
||||||
|
{ |
||||||
|
MString name; |
||||||
|
|
||||||
|
public: |
||||||
|
Attribute(const MString& n, AttVT&& v): AttVT(std::move(v)), name(n) {} |
||||||
|
Attribute(const std::string& n, AttVT&& v): AttVT(std::move(v)), name(n.c_str(), n.size()) {} |
||||||
|
|
||||||
|
const MString& Name() const { return name; } |
||||||
|
|
||||||
|
AttType Type() const |
||||||
|
{ |
||||||
|
if(std::holds_alternative<int8>(*this)) |
||||||
|
return AttType::INT; |
||||||
|
else if(std::holds_alternative<uint8>(*this)) |
||||||
|
return AttType::UINT; |
||||||
|
else if(std::holds_alternative<double>(*this)) |
||||||
|
return AttType::REAL; |
||||||
|
else if(std::holds_alternative<MString>(*this)) |
||||||
|
return AttType::STRING; |
||||||
|
else if(std::holds_alternative<bool>(*this)) |
||||||
|
return AttType::BOOL; |
||||||
|
|
||||||
|
return AttType::UNDEF; |
||||||
|
} |
||||||
|
|
||||||
|
int8 I() const |
||||||
|
{ |
||||||
|
if(std::holds_alternative<int8>(*this)) |
||||||
|
return std::get<int8>(*this); |
||||||
|
else if(std::holds_alternative<uint8>(*this)) |
||||||
|
return int_cast<int8>(std::get<uint8>(*this)); |
||||||
|
else if(std::holds_alternative<double>(*this)) |
||||||
|
return static_cast<int8>(std::get<double>(*this)); |
||||||
|
else if(std::holds_alternative<MString>(*this)) |
||||||
|
return std::get<MString>(*this).ToInteger<int8>(); |
||||||
|
else if(std::holds_alternative<bool>(*this)) |
||||||
|
return std::get<bool>(*this) ? 1 : 0; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
uint8 U() const |
||||||
|
{ |
||||||
|
if(std::holds_alternative<int8>(*this)) |
||||||
|
return int_cast<uint8>(std::get<int8>(*this)); |
||||||
|
else if(std::holds_alternative<uint8>(*this)) |
||||||
|
return std::get<uint8>(*this); |
||||||
|
else if(std::holds_alternative<double>(*this)) |
||||||
|
return static_cast<uint8>(std::get<double>(*this)); |
||||||
|
else if(std::holds_alternative<MString>(*this)) |
||||||
|
return std::get<MString>(*this).ToInteger<uint8>(); |
||||||
|
else if(std::holds_alternative<bool>(*this)) |
||||||
|
return std::get<bool>(*this) ? 1 : 0; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
double D() const |
||||||
|
{ |
||||||
|
if(std::holds_alternative<int8>(*this)) |
||||||
|
return std::get<int8>(*this); |
||||||
|
else if(std::holds_alternative<uint8>(*this)) |
||||||
|
return std::get<uint8>(*this); |
||||||
|
else if(std::holds_alternative<double>(*this)) |
||||||
|
return std::get<double>(*this); |
||||||
|
else if(std::holds_alternative<MString>(*this)) |
||||||
|
return michlib_internal::RealType<sizeof(double)>::String2Real(std::get<MString>(*this).Buf()); |
||||||
|
else if(std::holds_alternative<bool>(*this)) |
||||||
|
return std::get<bool>(*this) ? 1 : 0; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
MString S() const |
||||||
|
{ |
||||||
|
if(std::holds_alternative<int8>(*this)) |
||||||
|
return MString().FromInt(std::get<int8>(*this)); |
||||||
|
else if(std::holds_alternative<uint8>(*this)) |
||||||
|
return MString().FromUInt(std::get<uint8>(*this)); |
||||||
|
else if(std::holds_alternative<double>(*this)) |
||||||
|
return MString().FromReal(std::get<double>(*this)); |
||||||
|
else if(std::holds_alternative<MString>(*this)) |
||||||
|
return std::get<MString>(*this); |
||||||
|
else if(std::holds_alternative<bool>(*this)) |
||||||
|
return MString().FromBool(std::get<bool>(*this)); |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
bool B() const |
||||||
|
{ |
||||||
|
if(std::holds_alternative<int8>(*this)) |
||||||
|
return std::get<int8>(*this) != 0; |
||||||
|
else if(std::holds_alternative<uint8>(*this)) |
||||||
|
return std::get<uint8>(*this) != 0; |
||||||
|
else if(std::holds_alternative<double>(*this)) |
||||||
|
return std::get<double>(*this) != 0.0; |
||||||
|
else if(std::holds_alternative<MString>(*this)) |
||||||
|
return std::get<MString>(*this).ToBool(); |
||||||
|
else if(std::holds_alternative<bool>(*this)) |
||||||
|
return std::get<bool>(*this); |
||||||
|
return false; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
class Dimension |
||||||
|
{ |
||||||
|
MString name; |
||||||
|
size_t size; |
||||||
|
|
||||||
|
public: |
||||||
|
Dimension(const MString& str, size_t num): name(str), size(num) {} |
||||||
|
const MString& Name() const { return name; } |
||||||
|
|
||||||
|
size_t Size() const { return size; } |
||||||
|
}; |
||||||
|
|
||||||
|
class Variable |
||||||
|
{ |
||||||
|
public: |
||||||
|
using FillType = std::variant<std::monostate, int8, uint8, double>; |
||||||
|
|
||||||
|
private: |
||||||
|
MString name; |
||||||
|
VarType type = VarType::UNDEF; |
||||||
|
std::vector<size_t> dims; |
||||||
|
std::vector<Attribute> atts; |
||||||
|
FillType fill; |
||||||
|
|
||||||
|
public: |
||||||
|
Variable(const MString& name_, VarType type_, std::vector<size_t>&& dims_, std::vector<Attribute>&& atts_, FillType fill_ = 0): |
||||||
|
name(name_), type(type_), dims(std::move(dims_)), atts(std::move(atts_)), fill(fill_) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
explicit operator bool() const { return type != VarType::UNDEF; } |
||||||
|
|
||||||
|
const auto& Dims() const { return dims; } |
||||||
|
|
||||||
|
size_t NDim() const { return dims.size(); } |
||||||
|
|
||||||
|
size_t NAtt() const { return atts.size(); } |
||||||
|
|
||||||
|
auto AttNames() const |
||||||
|
{ |
||||||
|
std::vector<MString> out; |
||||||
|
std::transform(atts.cbegin(), atts.cend(), std::back_inserter(out), [](const Attribute& a) { return a.Name(); }); |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
AttType AttT(const MString& name) const |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, atts); |
||||||
|
return ind < atts.size() ? atts[ind].Type() : AttType::UNDEF; |
||||||
|
} |
||||||
|
|
||||||
|
int8 AttInt(const MString& name) const |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, atts); |
||||||
|
return ind < atts.size() ? atts[ind].I() : 0; |
||||||
|
} |
||||||
|
|
||||||
|
uint8 AttUInt(const MString& name) const |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, atts); |
||||||
|
return ind < atts.size() ? atts[ind].U() : 0; |
||||||
|
} |
||||||
|
|
||||||
|
double AttReal(const MString& name) const |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, atts); |
||||||
|
return ind < atts.size() ? atts[ind].D() : 0.0; |
||||||
|
} |
||||||
|
|
||||||
|
MString AttString(const MString& name) const |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, atts); |
||||||
|
return ind < atts.size() ? atts[ind].S() : MString(); |
||||||
|
} |
||||||
|
|
||||||
|
bool AttBool(const MString& name) const |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, atts); |
||||||
|
return ind < atts.size() ? atts[ind].B() : false; |
||||||
|
} |
||||||
|
|
||||||
|
const MString& Name() const { return name; } |
||||||
|
|
||||||
|
auto Type() const { return type; } |
||||||
|
|
||||||
|
const auto& Fill() const { return fill; } |
||||||
|
}; |
||||||
|
|
||||||
|
protected: |
||||||
|
std::vector<Attribute> gats; |
||||||
|
std::vector<Dimension> dims; |
||||||
|
std::vector<Variable> vars; |
||||||
|
|
||||||
|
public: |
||||||
|
operator bool() const { return !vars.empty(); } |
||||||
|
|
||||||
|
size_t NDim() const { return dims.size(); } |
||||||
|
|
||||||
|
size_t NDim(const MString& var) const |
||||||
|
{ |
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
return ind < vars.size() ? vars[ind].NDim() : 0; |
||||||
|
} |
||||||
|
|
||||||
|
size_t NAtt() const { return gats.size(); } |
||||||
|
|
||||||
|
auto AttNames() const |
||||||
|
{ |
||||||
|
std::vector<MString> out; |
||||||
|
std::transform(gats.cbegin(), gats.cend(), std::back_inserter(out), [](const Attribute& a) { return a.Name(); }); |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
size_t NAtt(const MString& var) const |
||||||
|
{ |
||||||
|
if(!var.Exist()) return NAtt(); |
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
return ind < vars.size() ? vars[ind].NAtt() : 0; |
||||||
|
} |
||||||
|
|
||||||
|
auto AttNames(const MString& var) const |
||||||
|
{ |
||||||
|
if(!var.Exist()) return AttNames(); |
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
return ind < vars.size() ? vars[ind].AttNames() : decltype(AttNames())(); |
||||||
|
} |
||||||
|
|
||||||
|
auto VarNames() const |
||||||
|
{ |
||||||
|
std::vector<MString> out; |
||||||
|
std::transform(vars.cbegin(), vars.cend(), std::back_inserter(out), [](const Variable& v) { return v.Name(); }); |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
VarType VarT(const MString& var) const |
||||||
|
{ |
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
return ind < vars.size() ? vars[ind].Type() : VarType::UNDEF; |
||||||
|
} |
||||||
|
|
||||||
|
auto VarFill(const MString& var) const |
||||||
|
{ |
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
return ind < vars.size() ? vars[ind].Fill() : Variable::FillType(); |
||||||
|
} |
||||||
|
|
||||||
|
auto DimNames() const |
||||||
|
{ |
||||||
|
std::vector<MString> out; |
||||||
|
std::transform(dims.cbegin(), dims.cend(), std::back_inserter(out), [](const Dimension& d) { return d.Name(); }); |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
auto DimNames(const MString& var) const |
||||||
|
{ |
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
|
||||||
|
std::vector<MString> out; |
||||||
|
if(ind >= vars.size()) return out; |
||||||
|
|
||||||
|
auto vdims = vars[ind].Dims(); |
||||||
|
std::transform(vdims.cbegin(), vdims.cend(), std::back_inserter(out), [&dims = std::as_const(dims)](const size_t& i) { return dims[i].Name(); }); |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
size_t DimSize(const MString& dim) const |
||||||
|
{ |
||||||
|
size_t ind = FindInd(dim, dims); |
||||||
|
return ind < dims.size() ? dims[ind].Size() : 0; |
||||||
|
} |
||||||
|
|
||||||
|
AttType AttT(const MString& var, const MString& name) const |
||||||
|
{ |
||||||
|
if(!var.Exist()) |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, gats); |
||||||
|
return ind < gats.size() ? gats[ind].Type() : AttType::UNDEF; |
||||||
|
} |
||||||
|
|
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
return ind < vars.size() ? vars[ind].AttT(name) : AttType::UNDEF; |
||||||
|
} |
||||||
|
|
||||||
|
int8 AttInt(const MString& var, const MString& name) const |
||||||
|
{ |
||||||
|
if(!var.Exist()) |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, gats); |
||||||
|
return ind < gats.size() ? gats[ind].I() : 0; |
||||||
|
} |
||||||
|
|
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
return ind < vars.size() ? vars[ind].AttInt(name) : 0; |
||||||
|
} |
||||||
|
|
||||||
|
uint8 AttUInt(const MString& var, const MString& name) const |
||||||
|
{ |
||||||
|
if(!var.Exist()) |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, gats); |
||||||
|
return ind < gats.size() ? gats[ind].U() : 0; |
||||||
|
} |
||||||
|
|
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
return ind < vars.size() ? vars[ind].AttUInt(name) : 0; |
||||||
|
} |
||||||
|
|
||||||
|
double AttReal(const MString& var, const MString& name) const |
||||||
|
{ |
||||||
|
if(!var.Exist()) |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, gats); |
||||||
|
return ind < gats.size() ? gats[ind].D() : 0.0; |
||||||
|
} |
||||||
|
|
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
return ind < vars.size() ? vars[ind].AttReal(name) : 0.0; |
||||||
|
} |
||||||
|
|
||||||
|
MString AttString(const MString& var, const MString& name) const |
||||||
|
{ |
||||||
|
if(!var.Exist()) |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, gats); |
||||||
|
return ind < gats.size() ? gats[ind].S() : MString(); |
||||||
|
} |
||||||
|
|
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
return ind < vars.size() ? vars[ind].AttString(name) : MString(); |
||||||
|
} |
||||||
|
|
||||||
|
bool AttBool(const MString& var, const MString& name) const |
||||||
|
{ |
||||||
|
if(!var.Exist()) |
||||||
|
{ |
||||||
|
size_t ind = FindInd(name, gats); |
||||||
|
return ind < gats.size() ? gats[ind].B() : false; |
||||||
|
} |
||||||
|
|
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
return ind < vars.size() ? vars[ind].AttBool(name) : false; |
||||||
|
} |
||||||
|
|
||||||
|
auto AttT(const MString& name) const { return AttT("", name); } |
||||||
|
auto AttInt(const MString& name) const { return AttInt("", name); } |
||||||
|
auto AttUInt(const MString& name) const { return AttUInt("", name); } |
||||||
|
auto AttReal(const MString& name) const { return AttReal("", name); } |
||||||
|
auto AttString(const MString& name) const { return AttString("", name); } |
||||||
|
auto AttBool(const MString& name) const { return AttBool("", name); } |
||||||
|
|
||||||
|
bool HasDim(const MString& name) const { return FindInd(name, dims) < dims.size(); } |
||||||
|
bool HasVar(const MString& name) const { return FindInd(name, vars) < vars.size(); } |
||||||
|
bool HasAtt(const MString& vname, const MString& aname) const { return AttT(vname, aname) != AttType::UNDEF; } |
||||||
|
bool HasAtt(const MString& aname) const { return AttT(aname) != AttType::UNDEF; } |
||||||
|
|
||||||
|
const auto& Vars() const { return vars; } |
||||||
|
const auto& Dims() const { return dims; } |
||||||
|
}; |
||||||
|
|
||||||
|
class DimReqDef |
||||||
|
{ |
||||||
|
protected: |
||||||
|
struct DimReq |
||||||
|
{ |
||||||
|
static const auto fill = std::numeric_limits<size_t>::max(); |
||||||
|
MString name; |
||||||
|
size_t beg, count; |
||||||
|
|
||||||
|
DimReq(): name(MString()), beg(fill), count(fill) {} |
||||||
|
DimReq(const char* n): name(n), beg(fill), count(fill) {} |
||||||
|
DimReq(const MString& n): name(n), beg(fill), count(fill) {} |
||||||
|
DimReq(MString&& n): name(std::move(n)), beg(fill), count(fill) {} |
||||||
|
DimReq(const char* n, size_t s): name(n), beg(s), count(fill) {} |
||||||
|
DimReq(const MString& n, size_t s): name(n), beg(s), count(fill) {} |
||||||
|
DimReq(MString&& n, size_t s): name(std::move(n)), beg(s), count(fill) {} |
||||||
|
DimReq(const char* n, size_t s, size_t c): name(n), beg(s), count(c) {} |
||||||
|
DimReq(const MString& n, size_t s, size_t c): name(n), beg(s), count(c) {} |
||||||
|
DimReq(MString&& n, size_t s, size_t c): name(std::move(n)), beg(s), count(c) {} |
||||||
|
|
||||||
|
const MString& Name() const { return name; } |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
template<class C> class NcZarrRead: public C, public DimReqDef |
||||||
|
{ |
||||||
|
template<class Data> static constexpr size_t Dimensionity() |
||||||
|
{ |
||||||
|
if constexpr(requires(Data& d) { d(0, 0, 0, 0); }) return 4; |
||||||
|
if constexpr(requires(Data& d) { d(0, 0, 0); }) return 3; |
||||||
|
if constexpr(requires(Data& d) { d(0, 0); }) return 2; |
||||||
|
if constexpr(requires(Data& d) { d(0); }) return 1; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
template<class Data, size_t D, class Dummy = void> struct DataTypeExtractorS; |
||||||
|
|
||||||
|
template<class Data, class Dummy> struct DataTypeExtractorS<Data, 1, Dummy> |
||||||
|
{ |
||||||
|
using type = std::decay_t<decltype(std::declval<Data>()(0))>; |
||||||
|
}; |
||||||
|
template<class Data, class Dummy> struct DataTypeExtractorS<Data, 2, Dummy> |
||||||
|
{ |
||||||
|
using type = std::decay_t<decltype(std::declval<Data>()(0, 0))>; |
||||||
|
}; |
||||||
|
template<class Data, class Dummy> struct DataTypeExtractorS<Data, 3, Dummy> |
||||||
|
{ |
||||||
|
using type = std::decay_t<decltype(std::declval<Data>()(0, 0, 0))>; |
||||||
|
}; |
||||||
|
template<class Data, class Dummy> struct DataTypeExtractorS<Data, 4, Dummy> |
||||||
|
{ |
||||||
|
using type = std::decay_t<decltype(std::declval<Data>()(0, 0, 0, 0))>; |
||||||
|
}; |
||||||
|
|
||||||
|
template<class Data> using DataTypeExtractor = DataTypeExtractorS<Data, Dimensionity<Data>()>::type; |
||||||
|
|
||||||
|
template<class VType, class Data, class Transform> |
||||||
|
Error Read(const MString& vname, const std::vector<size_t>& transindex, Data& data, Transform transform, std::vector<DimReq> reqs) const |
||||||
|
{ |
||||||
|
size_t nval = 1; |
||||||
|
for(const auto& r: reqs) nval *= r.count; |
||||||
|
const size_t indim = reqs.size(); |
||||||
|
constexpr size_t outdim = Dimensionity<Data>(); |
||||||
|
|
||||||
|
std::vector<size_t> start; |
||||||
|
std::vector<size_t> count; |
||||||
|
|
||||||
|
start.resize(indim); |
||||||
|
count.resize(indim); |
||||||
|
for(size_t i = 0; i < indim; i++) |
||||||
|
{ |
||||||
|
start[i] = reqs[i].beg; |
||||||
|
count[i] = reqs[i].count; |
||||||
|
} |
||||||
|
|
||||||
|
using DataType = DataTypeExtractor<Data>; |
||||||
|
DataType fillout; |
||||||
|
bool havefill = C::VarFill(vname).index() > 0; |
||||||
|
VType fillin = std::visit( |
||||||
|
[](auto v) |
||||||
|
{ |
||||||
|
if constexpr(std::is_convertible_v<decltype(v), VType>) |
||||||
|
return static_cast<VType>(v); |
||||||
|
else |
||||||
|
return std::numeric_limits<VType>::max(); |
||||||
|
}, |
||||||
|
C::VarFill(vname)); |
||||||
|
|
||||||
|
if constexpr(requires(Data& d) { // Data have own fillvalue
|
||||||
|
{ |
||||||
|
d.Fillval() |
||||||
|
} -> std::convertible_to<DataType>; |
||||||
|
}) |
||||||
|
fillout = data.Fillval(); |
||||||
|
else // Data does'nt have own fillvalue, using variable fillvalue
|
||||||
|
fillout = static_cast<DataType>(fillin); |
||||||
|
|
||||||
|
auto ret = C::template Read<VType>(vname, start.data(), count.data()); |
||||||
|
if(!ret) return ret; |
||||||
|
const auto& rawdata = ret.Value(); |
||||||
|
|
||||||
|
std::vector<size_t> mul(indim, 1); |
||||||
|
for(size_t i = indim - 1; i > 0; i--) mul[i - 1] = mul[i] * count[i]; |
||||||
|
|
||||||
|
size_t inind = 0; |
||||||
|
for(typename C::ArrCounter i(count); i; ++i) |
||||||
|
{ |
||||||
|
// TODO: Remove this testing block
|
||||||
|
size_t cind = 0; |
||||||
|
for(size_t j = 0; j < indim; j++) cind += i[j] * mul[j]; |
||||||
|
if(cind != inind) return {"NcZarrRead::Read", "Internal error"}; |
||||||
|
if(i.Index() != inind) return {"NcZarrRead::Read", "Internal error"}; |
||||||
|
if(inind != i.Index(i.Index(inind, count), count)) return {"NcZarrRead::Read", "Internal error"}; |
||||||
|
|
||||||
|
DataType out; |
||||||
|
const VType& in = rawdata(inind); |
||||||
|
if(havefill && in == fillin) |
||||||
|
out = fillout; |
||||||
|
else |
||||||
|
out = transform(in); |
||||||
|
|
||||||
|
if constexpr(outdim == 1) |
||||||
|
data(i[transindex[0]]) = out; |
||||||
|
else if constexpr(outdim == 2) |
||||||
|
data(i[transindex[0]], i[transindex[1]]) = out; |
||||||
|
else if constexpr(outdim == 3) |
||||||
|
data(i[transindex[0]], i[transindex[1]], i[transindex[2]]) = out; |
||||||
|
else if constexpr(outdim == 4) |
||||||
|
data(i[transindex[0]], i[transindex[1]], i[transindex[2]], i[transindex[3]]) = out; |
||||||
|
|
||||||
|
inind++; |
||||||
|
} |
||||||
|
|
||||||
|
return Error(); |
||||||
|
} |
||||||
|
|
||||||
|
public: |
||||||
|
// Request is string
|
||||||
|
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, const char* request) const |
||||||
|
{ |
||||||
|
return Read(vname, data, transform, MString(request)); |
||||||
|
} |
||||||
|
|
||||||
|
// Request by one dimension
|
||||||
|
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, DimReq&& req1) const |
||||||
|
{ |
||||||
|
return Read(vname, data, transform, std::vector<DimReq>{std::move(req1)}); |
||||||
|
} |
||||||
|
// Request by two dimension
|
||||||
|
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, DimReq&& req1, DimReq&& req2) const |
||||||
|
{ |
||||||
|
return Read(vname, data, transform, std::vector<DimReq>{std::move(req1), std::move(req2)}); |
||||||
|
} |
||||||
|
// Request by three dimension
|
||||||
|
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, DimReq&& req1, DimReq&& req2, DimReq&& req3) const |
||||||
|
{ |
||||||
|
return Read(vname, data, transform, std::vector<DimReq>{std::move(req1), std::move(req2), std::move(req3)}); |
||||||
|
} |
||||||
|
// Request by four dimension
|
||||||
|
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, DimReq&& req1, DimReq&& req2, DimReq&& req3, DimReq&& req4) const |
||||||
|
{ |
||||||
|
return Read(vname, data, transform, std::vector<DimReq>{std::move(req1), std::move(req2), std::move(req3), std::move(req4)}); |
||||||
|
} |
||||||
|
|
||||||
|
// Request full variable
|
||||||
|
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform) const |
||||||
|
{ |
||||||
|
static const MString pref = "NcZarrRead::Read"; |
||||||
|
if(!C::HasVar(vname)) return {pref, "Variable " + vname + " not found"}; |
||||||
|
|
||||||
|
std::vector<struct DimReq> pdims; |
||||||
|
|
||||||
|
const auto vdims = C::DimNames(vname); |
||||||
|
std::transform( |
||||||
|
vdims.cbegin(), vdims.cend(), std::back_inserter(pdims), [this](const MString& n) -> struct DimReq { |
||||||
|
return {n, 0, C::DimSize(n)}; |
||||||
|
}); |
||||||
|
|
||||||
|
return Read(vname, data, transform, pdims); |
||||||
|
} |
||||||
|
|
||||||
|
// Base function for all Read's
|
||||||
|
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, std::vector<DimReq> reqs) const |
||||||
|
{ |
||||||
|
static const MString pref = "NcZarrRead::Read"; |
||||||
|
|
||||||
|
if(!C::HasVar(vname)) return {pref, "Variable " + vname + " not found"}; |
||||||
|
|
||||||
|
std::vector<struct DimReq> pdims; |
||||||
|
{ |
||||||
|
const auto vdims = C::DimNames(vname); |
||||||
|
std::transform( |
||||||
|
vdims.cbegin(), vdims.cend(), std::back_inserter(pdims), [](const MString& n) -> struct DimReq { |
||||||
|
return {n, 0, 1}; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<size_t> transindex; |
||||||
|
|
||||||
|
// Parse request
|
||||||
|
if(reqs.size() == 0) return {pref, "Empty request"}; |
||||||
|
for(const auto& req: reqs) |
||||||
|
{ |
||||||
|
size_t ind = C::FindInd(req.name, pdims); |
||||||
|
if(ind >= pdims.size()) return {pref, "Variable " + vname + " has no dimension " + req.name}; |
||||||
|
|
||||||
|
for(size_t i = 0; i < transindex.size(); i++) |
||||||
|
if(transindex[i] == ind) return {pref, "Parameters for dimension " + req.name + " already defined"}; |
||||||
|
transindex.push_back(ind); |
||||||
|
|
||||||
|
size_t dlen = C::DimSize(pdims[ind].name); |
||||||
|
if(req.beg == req.fill && req.count == req.fill) // Only name, so, we request full length
|
||||||
|
{ |
||||||
|
pdims[ind].beg = 0; |
||||||
|
pdims[ind].count = dlen; |
||||||
|
} |
||||||
|
else if(req.count == req.fill) // Name and first index
|
||||||
|
{ |
||||||
|
pdims[ind].beg = req.beg; |
||||||
|
pdims[ind].count = 1; |
||||||
|
} |
||||||
|
else // Name, first index, count
|
||||||
|
{ |
||||||
|
pdims[ind].beg = req.beg; |
||||||
|
pdims[ind].count = req.count; |
||||||
|
} |
||||||
|
// Sanity checks
|
||||||
|
if(pdims[ind].count <= 0) return {pref, "Error parsing request: count must be greter then zero"}; |
||||||
|
if(pdims[ind].beg >= dlen) return {pref, MString("Error parsing request: start index ") + pdims[ind].beg + " must be lesser then " + pdims[ind].name + " size " + dlen}; |
||||||
|
if(pdims[ind].beg + pdims[ind].count > dlen) |
||||||
|
return {pref, MString("Error parsing request: start index ") + pdims[ind].beg + " with count " + pdims[ind].count + " exceeds " + pdims[ind].name + " size " + dlen}; |
||||||
|
|
||||||
|
// Ignore hyperplanes in requests for calculation of data dimensionality
|
||||||
|
if(pdims[transindex.back()].count == 1) transindex.pop_back(); |
||||||
|
} |
||||||
|
|
||||||
|
if(transindex.size() != Dimensionity<Data>()) |
||||||
|
return {pref, MString("Output data dimensions (") + Dimensionity<Data>() + ") not corresponding request dimensions (" + transindex.size() + ")"}; |
||||||
|
switch(C::VarT(vname)) |
||||||
|
{ |
||||||
|
case(C::VarType::UNDEF): return {pref, "No variable with name " + vname + " (impossible)"}; |
||||||
|
case(C::VarType::FLOAT): return Read<typename C::template Type<C::VarType::FLOAT>>(vname, transindex, data, transform, pdims); |
||||||
|
case(C::VarType::DOUBLE): return Read<typename C::template Type<C::VarType::DOUBLE>>(vname, transindex, data, transform, pdims); |
||||||
|
case(C::VarType::INT1): return Read<typename C::template Type<C::VarType::INT1>>(vname, transindex, data, transform, pdims); |
||||||
|
case(C::VarType::INT2): return Read<typename C::template Type<C::VarType::INT2>>(vname, transindex, data, transform, pdims); |
||||||
|
case(C::VarType::INT4): return Read<typename C::template Type<C::VarType::INT4>>(vname, transindex, data, transform, pdims); |
||||||
|
case(C::VarType::INT8): return Read<typename C::template Type<C::VarType::INT8>>(vname, transindex, data, transform, pdims); |
||||||
|
case(C::VarType::UINT1): return Read<typename C::template Type<C::VarType::INT1>>(vname, transindex, data, transform, pdims); |
||||||
|
} |
||||||
|
|
||||||
|
return {pref, "Internal error (impossible)"}; |
||||||
|
} |
||||||
|
|
||||||
|
// Request by string argument
|
||||||
|
template<class Data, class Transform> Error Read(const MString& vname, Data& data, Transform transform, const MString& request) const |
||||||
|
{ |
||||||
|
static const MString pref = "NcZarrRead::Read"; |
||||||
|
|
||||||
|
std::vector<struct DimReq> pdims; |
||||||
|
|
||||||
|
// Parse request
|
||||||
|
const auto dimdesc = request.Split(";, \t"); |
||||||
|
if(dimdesc.size() == 0) return {pref, "Empty request"}; |
||||||
|
for(const auto& dd: dimdesc) |
||||||
|
{ |
||||||
|
const auto dimpar = dd.Split(":", true); |
||||||
|
|
||||||
|
if(dimpar.size() == 1) // Only name, so, we request full length
|
||||||
|
pdims.emplace_back(dimpar[0]); |
||||||
|
else if(dimpar.size() == 2) // Name and first index
|
||||||
|
pdims.emplace_back(dimpar[0], dimpar[1].ToInteger<size_t>()); |
||||||
|
else if(dimpar.size() == 3) // Name, first index, count
|
||||||
|
pdims.emplace_back(dimpar[0], dimpar[1].ToInteger<size_t>(), dimpar[2].ToInteger<size_t>()); |
||||||
|
else |
||||||
|
return {pref, "Can't parse expression " + dd}; |
||||||
|
} |
||||||
|
|
||||||
|
return Read(vname, data, transform, pdims); |
||||||
|
} |
||||||
|
|
||||||
|
// Request full one-dimensional variable
|
||||||
|
template<class Type> Error Read(const MString& vname, std::vector<Type>& out) const |
||||||
|
{ |
||||||
|
const auto& dnames = C::DimNames(vname); |
||||||
|
if(dnames.size() > 0) out.resize(C::DimSize(dnames[0])); |
||||||
|
auto data = [&vec = out](size_t i) -> Type& { return vec[i]; }; |
||||||
|
|
||||||
|
return Read(vname, data, std::identity()); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,175 @@ |
|||||||
|
#pragma once |
||||||
|
#include "GPL.h" |
||||||
|
#include "cache.h" |
||||||
|
#include "curlfuncs.h" |
||||||
|
#include "nczarrcommon.h" |
||||||
|
#include <json/json.h> |
||||||
|
#include <variant> |
||||||
|
|
||||||
|
class ZarrTypes: public NcZarrTypes |
||||||
|
{ |
||||||
|
protected: |
||||||
|
template<class VType> class ReadedData |
||||||
|
{ |
||||||
|
//public:
|
||||||
|
using Vec = std::vector<size_t>; |
||||||
|
|
||||||
|
private: |
||||||
|
Vec start, chunkstart; |
||||||
|
ArrCounter mainind, chunkind, inchunkind; |
||||||
|
std::vector<std::unique_ptr<VType[]>> data; |
||||||
|
|
||||||
|
public: |
||||||
|
ReadedData(): mainind(Vec()), chunkind(Vec()), inchunkind(Vec()) {} |
||||||
|
|
||||||
|
ReadedData(size_t N, const size_t* start, const size_t* count, const size_t* csize, std::vector<std::unique_ptr<VType[]>>&& d): |
||||||
|
start(start, start + N), |
||||||
|
chunkstart( |
||||||
|
[](size_t N, const size_t* st, const size_t* cs) |
||||||
|
{ |
||||||
|
Vec out(N); |
||||||
|
for(size_t i = 0; i < N; i++) out[i] = st[i] / cs[i]; |
||||||
|
return out; |
||||||
|
}(N, start, csize)), |
||||||
|
mainind(Vec(count, count + N)), |
||||||
|
chunkind( |
||||||
|
[](size_t N, const size_t* st, const size_t* cn, const size_t* cs) |
||||||
|
{ |
||||||
|
Vec out(N); |
||||||
|
for(size_t i = 0; i < N; i++) out[i] = (st[i] + cn[i]) / cs[i] - st[i] / cs[i] + 1; |
||||||
|
return out; |
||||||
|
}(N, start, count, csize)), |
||||||
|
inchunkind(Vec(csize, csize + N)), |
||||||
|
data(std::move(d)) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
VType operator()(size_t lini) const |
||||||
|
{ |
||||||
|
Vec ind = mainind.Index(lini, mainind.Count()); |
||||||
|
Vec cind(ind.size()), inind(ind.size()); |
||||||
|
|
||||||
|
for(size_t i = 0; i < ind.size(); i++) |
||||||
|
{ |
||||||
|
cind[i] = (ind[i] + start[i]) / inchunkind.Count(i) - chunkstart[i]; // indes of chunk
|
||||||
|
inind[i] = (ind[i] + start[i]) % inchunkind.Count(i); // index inside chunk
|
||||||
|
} |
||||||
|
size_t chunk = chunkind.Index(cind); |
||||||
|
size_t inside = inchunkind.Index(inind); |
||||||
|
return data[chunk][inside]; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
private: |
||||||
|
// Create attribute from json value
|
||||||
|
static AttVT CreateAtt(const Json::Value& val) |
||||||
|
{ |
||||||
|
if(val.type() == Json::intValue) return AttVT{std::in_place_type<int8>, val.asInt64()}; |
||||||
|
if(val.type() == Json::uintValue) return AttVT(std::in_place_type<uint8>, val.asUInt64()); |
||||||
|
if(val.type() == Json::realValue) return AttVT(std::in_place_type<double>, val.asDouble()); |
||||||
|
if(val.type() == Json::stringValue) |
||||||
|
{ |
||||||
|
auto str = val.asString(); |
||||||
|
return AttVT(std::in_place_type<MString>, MString(str.c_str(), str.size())); |
||||||
|
} |
||||||
|
if(val.type() == Json::booleanValue) return AttVT(std::in_place_type<bool>, val.asBool()); |
||||||
|
return AttVT(); |
||||||
|
} |
||||||
|
|
||||||
|
public: |
||||||
|
// Read attributes from .zattrs
|
||||||
|
static auto ReadAtts(const Json::Value& obj) |
||||||
|
{ |
||||||
|
std::vector<Attribute> out; |
||||||
|
if(obj.type() != Json::objectValue) return out; |
||||||
|
const auto keys = obj.getMemberNames(); |
||||||
|
for(const auto& key: keys) |
||||||
|
if(key != "_ARRAY_DIMENSIONS") out.emplace_back(key, CreateAtt(obj[key])); |
||||||
|
return out; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
class ZarrFunctions: public ZarrTypes |
||||||
|
{ |
||||||
|
std::unique_ptr<GenericCache> cache; |
||||||
|
CURLRAII chandle; |
||||||
|
MString url; |
||||||
|
|
||||||
|
std::vector<std::vector<size_t>> chunks; |
||||||
|
|
||||||
|
// Find variable names in metadata
|
||||||
|
static std::vector<MString> ReadVarNames(const Json::Value& meta); |
||||||
|
|
||||||
|
Error AddVar(const MString& name, const Json::Value& zattrs, const Json::Value& zarray); |
||||||
|
|
||||||
|
protected: |
||||||
|
ZarrFunctions() |
||||||
|
{ |
||||||
|
auto oldprefix = michlib::GPL.UsePrefix("ZARR"); |
||||||
|
cache.reset(CreateCache(michlib::GPL.ParameterSValue("Cache", ""))); |
||||||
|
michlib::GPL.UsePrefix(oldprefix); |
||||||
|
if(!cache) |
||||||
|
{ |
||||||
|
michlib::errmessage("Can't init data cache"); |
||||||
|
cache.reset(new FakeCache); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template<class VType> RetVal<ReadedData<VType>> Read(const MString& var, const size_t* start, const size_t* count) const |
||||||
|
{ |
||||||
|
using Vec = std::vector<size_t>; |
||||||
|
|
||||||
|
size_t ind = FindInd(var, vars); |
||||||
|
const size_t N = vars[ind].NDim(); |
||||||
|
const auto& csize = chunks[ind]; |
||||||
|
|
||||||
|
Vec chunkstart( |
||||||
|
[](size_t N, const size_t* st, const size_t* cs) |
||||||
|
{ |
||||||
|
Vec out(N); |
||||||
|
for(size_t i = 0; i < N; i++) out[i] = st[i] / cs[i]; |
||||||
|
return out; |
||||||
|
}(N, start, csize.data())); |
||||||
|
ArrCounter chunkind( |
||||||
|
[](size_t N, const size_t* st, const size_t* cn, const size_t* cs) |
||||||
|
{ |
||||||
|
Vec out(N); |
||||||
|
for(size_t i = 0; i < N; i++) out[i] = (st[i] + cn[i] - 1) / cs[i] - st[i] / cs[i] + 1; |
||||||
|
return out; |
||||||
|
}(N, start, count, csize.data())); |
||||||
|
|
||||||
|
bool havefill = vars[ind].Fill().index() > 0; |
||||||
|
VType fill = std::visit( |
||||||
|
[](auto v) |
||||||
|
{ |
||||||
|
if constexpr(std::is_convertible_v<decltype(v), VType>) |
||||||
|
return static_cast<VType>(v); |
||||||
|
else |
||||||
|
return std::numeric_limits<VType>::max(); |
||||||
|
}, |
||||||
|
vars[ind].Fill()); |
||||||
|
|
||||||
|
std::vector<std::unique_ptr<VType[]>> cdata; |
||||||
|
|
||||||
|
size_t chunksize = 1; |
||||||
|
for(const auto c: csize) chunksize *= c; |
||||||
|
|
||||||
|
cdata.resize(chunkind.N()); |
||||||
|
|
||||||
|
for(; chunkind; ++chunkind) |
||||||
|
{ |
||||||
|
cdata[chunkind.Index()].reset(new VType[chunksize]); |
||||||
|
auto res = GetChunk(var, chunkind.VIndex(chunkstart), chunksize, sizeof(VType), cdata[chunkind.Index()].get(), havefill ? &fill : nullptr); |
||||||
|
if(!res) return res; |
||||||
|
} |
||||||
|
|
||||||
|
return ReadedData<VType>(N, start, count, csize.data(), std::move(cdata)); |
||||||
|
} |
||||||
|
|
||||||
|
Error GetChunk(const MString& var, const std::vector<size_t>& chunkind, size_t chunksize, size_t elsize, void* data, const void* fill) const; |
||||||
|
|
||||||
|
public: |
||||||
|
Error Open(const MString& product, const MString& dataset, bool time = true); |
||||||
|
}; |
||||||
|
|
||||||
|
using Zarr = NcZarrRead<ZarrFunctions>; |
@ -1 +1 @@ |
|||||||
Subproject commit e2882902b88229bb4b0e6fbeb76c79ac6d46d53d |
Subproject commit f380988909fadd7f42c7ce09288c4ff3198ca318 |
@ -0,0 +1,168 @@ |
|||||||
|
#define MICHLIB_NOSOURCE |
||||||
|
#include "copcat.h" |
||||||
|
#include "GPL.h" |
||||||
|
#include "mirrorfuncs.h" |
||||||
|
|
||||||
|
const MString CopernicusCatalog::caturl = "https://stac.marine.copernicus.eu/metadata/catalog.stac.json"; |
||||||
|
|
||||||
|
CopernicusCatalog::CopernicusCatalog() |
||||||
|
{ |
||||||
|
// Cache
|
||||||
|
auto oldprefix = michlib::GPL.UsePrefix("COPERNICUS"); |
||||||
|
cache.reset(CreateCache(michlib::GPL.ParameterSValue("Cache", ""))); |
||||||
|
michlib::GPL.UsePrefix(oldprefix); |
||||||
|
if(!cache) |
||||||
|
{ |
||||||
|
michlib::errmessage("Can't init cache"); |
||||||
|
cache.reset(new FakeCache); |
||||||
|
} |
||||||
|
|
||||||
|
GetCatalog(); |
||||||
|
} |
||||||
|
|
||||||
|
Error CopernicusCatalog::GetCatalog() |
||||||
|
{ |
||||||
|
if(Valid()) return Error(); |
||||||
|
auto ret = GetJSON(caturl); |
||||||
|
if(ret) |
||||||
|
catalog = ret.Value(); |
||||||
|
else |
||||||
|
return ret.Add("CopernicusCatalog::GetCatalog", "can't download catalog"); |
||||||
|
return Error(); |
||||||
|
} |
||||||
|
|
||||||
|
RetVal<std::vector<MString>> CopernicusCatalog::ProductList() const |
||||||
|
{ |
||||||
|
static const MString pref = "CopernicusCatalog::ProductList"; |
||||||
|
|
||||||
|
if(!Valid()) return {pref, "no catalog"}; |
||||||
|
|
||||||
|
const auto& links = catalog["links"]; |
||||||
|
if(links.type() != Json::arrayValue) return {pref, "no \"links\" section in the catalog"}; |
||||||
|
|
||||||
|
std::vector<MString> out; |
||||||
|
for(Json::ArrayIndex i = 0; i < links.size(); i++) |
||||||
|
{ |
||||||
|
const auto& rel = links[i]["rel"]; |
||||||
|
const auto& titl = links[i]["title"]; |
||||||
|
|
||||||
|
if(rel.type() == Json::stringValue && titl.type() == Json::stringValue && rel.asString() == "child") out.emplace_back(titl.asString().c_str()); |
||||||
|
} |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
RetVal<MString> CopernicusCatalog::ProductURL(const MString& prod) const |
||||||
|
{ |
||||||
|
static const MString pref = "CopernicusCatalog::ProductURL"; |
||||||
|
|
||||||
|
if(!Valid()) return {pref, "no catalog"}; |
||||||
|
|
||||||
|
const auto& links = catalog["links"]; |
||||||
|
if(links.type() != Json::arrayValue) return {pref, "no \"links\" section in the catalog"}; |
||||||
|
|
||||||
|
for(Json::ArrayIndex i = 0; i < links.size(); i++) |
||||||
|
{ |
||||||
|
const auto& titl = links[i]["title"]; |
||||||
|
const auto& href = links[i]["href"]; |
||||||
|
if(titl.type() == Json::stringValue && href.type() == Json::stringValue && titl.asString().c_str() == prod) return DirName(caturl) + "/" + MString(href.asString().c_str()); |
||||||
|
} |
||||||
|
return {pref, "unknown product: " + prod}; |
||||||
|
} |
||||||
|
|
||||||
|
RetVal<std::vector<MString>> CopernicusCatalog::DatasetList(const MString& prod) const |
||||||
|
{ |
||||||
|
static const MString pref = "CopernicusCatalog::DatasetList"; |
||||||
|
|
||||||
|
MString url; |
||||||
|
{ |
||||||
|
auto ret = ProductURL(prod); |
||||||
|
if(!ret) return ret.Add(pref, "Can't get url for the product " + prod); |
||||||
|
url = ret.Value(); |
||||||
|
} |
||||||
|
|
||||||
|
auto ret = GetJSON(url); |
||||||
|
if(!ret) return ret.Add(pref, "Can't download product " + prod); |
||||||
|
|
||||||
|
const auto& links = ret.Value()["links"]; |
||||||
|
if(links.type() != Json::arrayValue) return {pref, "no \"links\" section in the product " + prod + " description"}; |
||||||
|
|
||||||
|
std::vector<MString> out; |
||||||
|
for(Json::ArrayIndex i = 0; i < links.size(); i++) |
||||||
|
{ |
||||||
|
const auto& rel = links[i]["rel"]; |
||||||
|
const auto& titl = links[i]["title"]; |
||||||
|
|
||||||
|
if(rel.type() == Json::stringValue && titl.type() == Json::stringValue && rel.asString() == "item") out.emplace_back(titl.asString().c_str()); |
||||||
|
} |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
RetVal<MString> CopernicusCatalog::DatasetURL(const MString& prod, const MString& dataset) const |
||||||
|
{ |
||||||
|
static const MString pref = "CopernicusCatalog::DatasetURL"; |
||||||
|
|
||||||
|
MString url; |
||||||
|
{ |
||||||
|
auto ret = ProductURL(prod); |
||||||
|
if(!ret) return ret.Add(pref, "Can't get url for the product " + prod); |
||||||
|
url = ret.Value(); |
||||||
|
} |
||||||
|
|
||||||
|
auto ret = GetJSON(url); |
||||||
|
if(!ret) return ret.Add(pref, "Can't download product " + prod); |
||||||
|
|
||||||
|
const auto& links = ret.Value()["links"]; |
||||||
|
if(links.type() != Json::arrayValue) return {pref, "no \"links\" section in the product " + prod + " description"}; |
||||||
|
|
||||||
|
for(Json::ArrayIndex i = 0; i < links.size(); i++) |
||||||
|
{ |
||||||
|
const auto& titl = links[i]["title"]; |
||||||
|
const auto& href = links[i]["href"]; |
||||||
|
if(titl.type() == Json::stringValue && href.type() == Json::stringValue && titl.asString().c_str() == dataset) return DirName(url) + "/" + MString(href.asString().c_str()); |
||||||
|
} |
||||||
|
return {pref, "unknown dataset: " + dataset}; |
||||||
|
} |
||||||
|
|
||||||
|
RetVal<MString> CopernicusCatalog::AssetURL(const MString& prod, const MString& dataset, const MString& asset) const |
||||||
|
{ |
||||||
|
static const MString pref = "CopernicusCatalog::AssetURL"; |
||||||
|
|
||||||
|
MString url; |
||||||
|
{ |
||||||
|
auto ret = DatasetURL(prod, dataset); |
||||||
|
if(!ret) return ret.Add(pref, "Can't get url for the dataset " + dataset); |
||||||
|
url = ret.Value(); |
||||||
|
} |
||||||
|
|
||||||
|
auto ret = GetJSON(url); |
||||||
|
if(!ret) return ret.Add(pref, "Can't download dataset " + dataset); |
||||||
|
|
||||||
|
const auto& href = ret.Value()["assets"][asset.Buf()]["href"]; |
||||||
|
if(!href || href.type() != Json::stringValue) return {pref, "href for the asset " + asset + " not found"}; |
||||||
|
return MString(href.asString().c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
RetVal<Json::Value> CopernicusCatalog::GetJSON(const MString& url) const |
||||||
|
{ |
||||||
|
const static MString pref = "CopernicusCatalog::GetJSON"; |
||||||
|
|
||||||
|
Json::Reader reader; |
||||||
|
Json::Value obj; |
||||||
|
MString content; |
||||||
|
|
||||||
|
auto [val, suc] = cache->Get(url); |
||||||
|
if(suc) |
||||||
|
content = std::move(val); |
||||||
|
else |
||||||
|
{ |
||||||
|
michlib::message(url + " not found in cache, downloading"); |
||||||
|
auto [out, res] = GetUrl(chandle, url); |
||||||
|
if(res != CURLE_OK) return Error(pref, MString("can't download JSON: ") + chandle.Err()); |
||||||
|
cache->Put(url, out, 3600); |
||||||
|
content = std::move(out); |
||||||
|
} |
||||||
|
|
||||||
|
reader.parse(content.Buf(), content.Buf() + content.Len(), obj, false); |
||||||
|
|
||||||
|
return obj; |
||||||
|
} |
@ -0,0 +1,298 @@ |
|||||||
|
#define MICHLIB_NOSOURCE |
||||||
|
#include "layereddataz.h" |
||||||
|
|
||||||
|
MString LayeredDataZ::Info() const |
||||||
|
{ |
||||||
|
if(!isOk()) return ""; |
||||||
|
MString d; |
||||||
|
for(size_t i = 0; i < NDepths(); i++) d += MString(" ") + "(" + i + " " + Depth(i) + ")"; |
||||||
|
|
||||||
|
std::set<MString> vars; |
||||||
|
for(const auto& f: nc) GetVars(f, vars); |
||||||
|
|
||||||
|
MString svars; |
||||||
|
{ |
||||||
|
bool first = true; |
||||||
|
for(const auto& v: vars) |
||||||
|
{ |
||||||
|
svars += (first ? "" : ", ") + v; |
||||||
|
first = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
return |
||||||
|
"Dataset: " + Title() + "\n" + |
||||||
|
" Begin date: " + Time(0).ToString() + "\n" + |
||||||
|
" End date: " + Time(NTimes()-1).ToString() + "\n" + |
||||||
|
" Time step: " + Timestep() + " seconds\n" + |
||||||
|
" Time moments: " + NTimes() + "\n" + |
||||||
|
" Region: (" + lonb + " : " + lone + ") x (" + latb + " : " + late + ")\n" + |
||||||
|
" Grid: " + dname.nx + "x" + dname.ny + " (" + lonstep + " x " + latstep + ")\n" + |
||||||
|
" Depths:" + d + "\n" + |
||||||
|
" Supported variables: " + svars; |
||||||
|
// clang-format on
|
||||||
|
} |
||||||
|
|
||||||
|
MString LayeredDataZ::Open(const MString& dataset) |
||||||
|
{ |
||||||
|
nc.clear(); |
||||||
|
MString proxyurl = GPL.ParameterSValue("USEPROXY", ""); |
||||||
|
if(proxyurl.Exist()) proxy.Activate("all_proxy", proxyurl); |
||||||
|
|
||||||
|
nc.clear(); |
||||||
|
size_t i = 1; |
||||||
|
while(true) |
||||||
|
{ |
||||||
|
MString url = GPL.ParameterSValue(dataset + "_URL" + i, ""); |
||||||
|
if(url.Exist()) |
||||||
|
{ |
||||||
|
// Split url on product and dataset
|
||||||
|
auto words = url.Split(":"); |
||||||
|
if(words.size() == 0 || words.size() > 2) |
||||||
|
{ |
||||||
|
nc.clear(); |
||||||
|
return "Invalid url " + url; |
||||||
|
} |
||||||
|
MString product = words[0]; |
||||||
|
MString dataset = words.size() == 2 ? words[1] : ""; |
||||||
|
|
||||||
|
nc.emplace_back(); |
||||||
|
{ |
||||||
|
auto ret = nc.back().Open(product, dataset); |
||||||
|
if(!ret) |
||||||
|
{ |
||||||
|
nc.clear(); |
||||||
|
return "Can't open " + dataset + " of " + product; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
break; |
||||||
|
i++; |
||||||
|
} |
||||||
|
if(nc.size() == 0) return "No urls for dataset " + dataset + " specified in config"; |
||||||
|
|
||||||
|
dname = GetDNames(nc[0]); |
||||||
|
if(!(dname.lonname.Exist() && dname.latname.Exist())) |
||||||
|
{ |
||||||
|
nc.clear(); |
||||||
|
return "Can't find longitude/latitude"; |
||||||
|
} |
||||||
|
if(!dname.timename.Exist()) |
||||||
|
{ |
||||||
|
nc.clear(); |
||||||
|
return "Can't find time"; |
||||||
|
} |
||||||
|
|
||||||
|
auto cn = GetCNames(nc[0]); |
||||||
|
|
||||||
|
// Read times
|
||||||
|
for(auto& f: nc) |
||||||
|
{ |
||||||
|
auto ret = f.ReadTimes(cn.timename); |
||||||
|
if(!ret) |
||||||
|
{ |
||||||
|
nc.clear(); |
||||||
|
return "Can't read times"; |
||||||
|
} |
||||||
|
times.insert(times.end(), f.Times().begin(), f.Times().end()); |
||||||
|
} |
||||||
|
std::sort(times.begin(), times.end()); |
||||||
|
auto last = std::unique(times.begin(), times.end()); |
||||||
|
times.erase(last, times.end()); |
||||||
|
|
||||||
|
depthinv = false; |
||||||
|
if(cn.depthname.Exist()) |
||||||
|
{ |
||||||
|
auto ret = nc[0].Read(cn.depthname, depths); |
||||||
|
if(!ret) |
||||||
|
{ |
||||||
|
nc.clear(); |
||||||
|
return "Can't read depths"; |
||||||
|
} |
||||||
|
if(depths.back() <= 0 && depths.front() <= 0) std::ranges::transform(depths, depths.begin(), std::negate{}); |
||||||
|
if(depths.back() < depths.front() && depths.size() > 1) |
||||||
|
{ |
||||||
|
depthinv = true; |
||||||
|
for(size_t i = 0; i < depths.size() - i - 1; i++) std::swap(depths[i], depths[depths.size() - i - 1]); |
||||||
|
} |
||||||
|
} |
||||||
|
else // Surface only data
|
||||||
|
{ |
||||||
|
depths.resize(1); |
||||||
|
depths[0] = 0; |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<double> lons, lats; |
||||||
|
{ |
||||||
|
auto ret = nc[0].Read(cn.lonname, lons); |
||||||
|
if(!ret) |
||||||
|
{ |
||||||
|
nc.clear(); |
||||||
|
return "Can't get longitudes"; |
||||||
|
} |
||||||
|
} |
||||||
|
{ |
||||||
|
auto ret = nc[0].Read(cn.latname, lats); |
||||||
|
if(!ret) |
||||||
|
{ |
||||||
|
nc.clear(); |
||||||
|
return "Can't get latitudes"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
lonb = lons[0]; |
||||||
|
latb = lats[0]; |
||||||
|
lone = lons.back(); |
||||||
|
late = lats.back(); |
||||||
|
lonstep = (lone - lonb) / (dname.nx - 1); |
||||||
|
latstep = (late - latb) / (dname.ny - 1); |
||||||
|
|
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
std::pair<const BaseParameters*, MString> LayeredDataZ::Parameters(michlib_internal::ParameterListEx& pars, const CLArgs& args, const struct Region& reg) const |
||||||
|
{ |
||||||
|
std::unique_ptr<struct Parameters> ppar{new struct Parameters}; |
||||||
|
|
||||||
|
ppar->layer = args.contains("layer") ? args.at("layer").ToInteger<size_t>() : 0; |
||||||
|
if(!args.contains("depth") && ppar->layer >= NDepths()) return {nullptr, MString("Layer ") + ppar->layer + " is too deep!"}; |
||||||
|
real depth = args.contains("depth") ? args.at("depth").ToReal() : Depth(ppar->layer); |
||||||
|
|
||||||
|
{ |
||||||
|
auto dom = DetGeoDomain(lonb, lone); |
||||||
|
real lon1 = ToGeoDomain(reg.lonb, dom); |
||||||
|
real lon2 = ToGeoDomain(reg.lone, dom); |
||||||
|
real lat1 = reg.latb; |
||||||
|
real lat2 = reg.late; |
||||||
|
bool global = lone - lonb + 1.5 * lonstep > 360.0; |
||||||
|
|
||||||
|
// Special case when the longitude lies in a small sector between the end and the start
|
||||||
|
if(global) |
||||||
|
{ |
||||||
|
if(lon1 < lonb) lon1 = lone; |
||||||
|
if(lon2 > lone) lon2 = lonb; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if(lon1 < lonb) lon1 = lonb; |
||||||
|
if(lon2 > lone) lon2 = lone; |
||||||
|
} |
||||||
|
|
||||||
|
ppar->xb = static_cast<size_t>(Floor((lon1 - lonb) / lonstep)); |
||||||
|
ppar->xe = static_cast<size_t>(Ceil((lon2 - lonb) / lonstep)); |
||||||
|
|
||||||
|
if(ppar->xb == ppar->xe) return {nullptr, "Lonb must be not equal late"}; |
||||||
|
if(!global && ppar->xb > ppar->xe) return {nullptr, "Lonb must be lesser then lone"}; |
||||||
|
|
||||||
|
ppar->yb = static_cast<size_t>(Floor((lat1 - latb) / latstep)); |
||||||
|
ppar->ye = static_cast<size_t>(Ceil((lat2 - latb) / latstep)); |
||||||
|
if(ppar->ye > dname.ny - 1) ppar->ye = dname.ny - 1; |
||||||
|
if(ppar->yb >= ppar->ye) return {nullptr, "Latb must be lesser then late"}; |
||||||
|
|
||||||
|
if(depth < 0.0 || depth > depths.back()) |
||||||
|
ppar->layer = (depth < 0.0) ? 0 : (depths.size() - 1); |
||||||
|
else |
||||||
|
for(size_t i = 0; i < depths.size() - 1; i++) |
||||||
|
{ |
||||||
|
if(depth >= depths[i] && depth <= depths[i + 1]) |
||||||
|
{ |
||||||
|
ppar->layer = (depth - depths[i] <= depths[i + 1] - depth) ? i : (i + 1); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if(depthinv) ppar->layer = depths.size() - ppar->layer - 1; |
||||||
|
} |
||||||
|
|
||||||
|
pars.SetParameter("depth", Depth(ppar->layer)); |
||||||
|
pars.SetParameter("layer", ppar->layer); |
||||||
|
pars.SetParameter("dataset", Title()); |
||||||
|
pars.SetParameter("lonb", Lon(ppar->xb)); |
||||||
|
pars.SetParameter("latb", Lat(ppar->yb)); |
||||||
|
pars.SetParameter("lone", Lon(ppar->xe)); |
||||||
|
pars.SetParameter("late", Lat(ppar->ye)); |
||||||
|
|
||||||
|
return {ppar.release(), ""}; |
||||||
|
} |
||||||
|
|
||||||
|
bool LayeredDataZ::Read(const MString& vname, std::map<MString, LayeredDataZ::Data>& cache, const BaseParameters* ip, size_t i) const |
||||||
|
{ |
||||||
|
if(cache.contains(vname)) return true; |
||||||
|
if(!isOk()) return false; |
||||||
|
|
||||||
|
auto p = dynamic_cast<const struct Parameters*>(ip); |
||||||
|
auto [name, id, tid] = VarNameLoc(vname, times[i]); |
||||||
|
if(!name.Exist()) // Conversion read
|
||||||
|
return TransformationRead(this, vname, cache, ip, i); |
||||||
|
|
||||||
|
// Direct read
|
||||||
|
bool nodepth = false; |
||||||
|
Data data; |
||||||
|
//auto head = nc[id]->Header();
|
||||||
|
for(const auto& v: nc[id].Vars()) |
||||||
|
if(v.Name() == name) |
||||||
|
{ |
||||||
|
if(v.NDim() == 3) nodepth = true; |
||||||
|
if(v.Type() == NcZarrTypes::VarType::INT2) data = ReadVarRaw<int2>(nc[id], name, tid, nodepth, p); |
||||||
|
if(v.Type() == NcZarrTypes::VarType::INT4) data = ReadVarRaw<int>(nc[id], name, tid, nodepth, p); |
||||||
|
if(v.Type() == NcZarrTypes::VarType::FLOAT) data = ReadVarRaw<float>(nc[id], name, tid, nodepth, p); |
||||||
|
if(v.Type() == NcZarrTypes::VarType::DOUBLE) data = ReadVarRaw<double>(nc[id], name, tid, nodepth, p); |
||||||
|
if(data) |
||||||
|
{ |
||||||
|
cache[vname] = std::move(data); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
template<class DataType> LayeredDataZ::Data LayeredDataZ::ReadVarRaw(const NC& f, const MString& name, size_t i, bool nodepth, const struct LayeredDataZ::Parameters* p) const |
||||||
|
{ |
||||||
|
real unitmul = 1.0; |
||||||
|
//DataType fill;
|
||||||
|
real offset = 0.0, scale = 1.0; |
||||||
|
|
||||||
|
if(f.HasAtt(name, "add_offset")) offset = f.AttReal(name, "add_offset"); |
||||||
|
if(f.HasAtt(name, "scale_factor")) scale = f.AttReal(name, "scale_factor"); |
||||||
|
|
||||||
|
MString unit; |
||||||
|
if(f.HasAtt(name, "units")) unit = f.AttString(name, "units"); |
||||||
|
if(unit == "m s-1" || unit == "m/s") |
||||||
|
{ |
||||||
|
unitmul = 100.0; |
||||||
|
unit = "cm/s"; |
||||||
|
} |
||||||
|
|
||||||
|
Data data((p->xb < p->xe) ? (p->xe - p->xb + 1) : (dname.nx + p->xe - p->xb + 1), p->ye - p->yb + 1, Lon(p->xb), Lat(p->yb), lonstep, latstep, std::move(unit)); |
||||||
|
|
||||||
|
auto trans = [scale, offset, unitmul](auto raw) -> DataType { return (raw * scale + offset) * unitmul; }; |
||||||
|
|
||||||
|
if(p->xb < p->xe) |
||||||
|
{ |
||||||
|
auto ret = nodepth ? f.Read(name, data, trans, {dname.lonname, p->xb, p->xe - p->xb + 1}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}) |
||||||
|
: f.Read(name, data, trans, {dname.lonname, p->xb, p->xe - p->xb + 1}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}, |
||||||
|
{dname.depthname, p->layer, 1}); |
||||||
|
if(!ret) return Data(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
{ |
||||||
|
auto ret = nodepth ? f.Read(name, data, trans, {dname.lonname, p->xb, dname.nx - p->xb + 1}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}) |
||||||
|
: f.Read(name, data, trans, {dname.lonname, p->xb, dname.nx - p->xb + 1}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}, |
||||||
|
{dname.depthname, p->layer, 1}); |
||||||
|
if(!ret) return Data(); |
||||||
|
} |
||||||
|
{ |
||||||
|
size_t shift = dname.nx - p->xb + 1; |
||||||
|
auto shifteddata = [&data, shift](size_t ix, size_t iy) -> real& { return data(ix + shift, iy); }; |
||||||
|
|
||||||
|
auto ret = |
||||||
|
nodepth ? f.Read(name, shifteddata, trans, {dname.lonname, 0, p->xe + 1}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}) |
||||||
|
: f.Read(name, shifteddata, trans, {dname.lonname, 0, p->xe + 1}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}, {dname.depthname, p->layer, 1}); |
||||||
|
if(!ret) return Data(); |
||||||
|
} |
||||||
|
} |
||||||
|
return data; |
||||||
|
} |
@ -0,0 +1,231 @@ |
|||||||
|
#define MICHLIB_NOSOURCE |
||||||
|
#include "ncsimple.h" |
||||||
|
|
||||||
|
RetVal<std::vector<NCSimpleFunctions::Attribute>> NCSimpleFunctions::ReadAtts(int vid) const |
||||||
|
{ |
||||||
|
static const MString pref = "NCSimple::ReadAtts"; |
||||||
|
|
||||||
|
int natt; |
||||||
|
int ret; |
||||||
|
if(vid == NC_GLOBAL) |
||||||
|
ret = nc_inq_natts(ncid, &natt); |
||||||
|
else |
||||||
|
ret = nc_inq_var(ncid, vid, nullptr, nullptr, nullptr, nullptr, &natt); |
||||||
|
if(ret != NC_NOERR) return Error(pref, MString("Can't inquire number of attributes: ") + nc_strerror(ret)); |
||||||
|
|
||||||
|
std::vector<Attribute> out; |
||||||
|
char name[NC_MAX_NAME + 1]; |
||||||
|
|
||||||
|
for(int aid = 0; aid < natt; aid++) |
||||||
|
{ |
||||||
|
nc_type type; |
||||||
|
size_t len; |
||||||
|
|
||||||
|
ret = nc_inq_attname(ncid, vid, aid, name); |
||||||
|
if(ret != NC_NOERR) return Error(pref, MString("Can't inquire attribute name: ") + nc_strerror(ret)); |
||||||
|
ret = nc_inq_atttype(ncid, vid, name, &type); |
||||||
|
if(ret != NC_NOERR) return Error(pref, MString("Can't inquire attribute type: ") + nc_strerror(ret)); |
||||||
|
ret = nc_inq_attlen(ncid, vid, name, &len); |
||||||
|
if(ret != NC_NOERR) return Error(pref, MString("Can't inquire attribute length: ") + nc_strerror(ret)); |
||||||
|
|
||||||
|
if(type == NC_DOUBLE || type == NC_FLOAT) |
||||||
|
{ |
||||||
|
if(len == 1) |
||||||
|
{ |
||||||
|
double d; |
||||||
|
ret = nc_get_att_double(ncid, vid, name, &d); |
||||||
|
if(ret != NC_NOERR) return Error(pref, MString("Can't read attribute ") + name + ": " + nc_strerror(ret)); |
||||||
|
out.emplace_back(MString(name), d); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
std::vector<double> dd(len); |
||||||
|
ret = nc_get_att_double(ncid, vid, name, dd.data()); |
||||||
|
if(ret != NC_NOERR) return Error(pref, MString("Can't read attribute ") + name + ": " + nc_strerror(ret)); |
||||||
|
for(size_t i = 0; i < dd.size(); i++) out.emplace_back(MString(name) + "[" + i + "]", dd[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
else if(type == NC_BYTE || type == NC_SHORT || type == NC_INT || type == NC_INT64) |
||||||
|
{ |
||||||
|
if(len == 1) |
||||||
|
{ |
||||||
|
long long i; |
||||||
|
ret = nc_get_att_longlong(ncid, vid, name, &i); |
||||||
|
if(ret != NC_NOERR) return Error(pref, MString("Can't read attribute ") + name + ": " + nc_strerror(ret)); |
||||||
|
out.emplace_back(MString(name), int_cast<int8>(i)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
std::vector<long long> ii(len); |
||||||
|
ret = nc_get_att_longlong(ncid, vid, name, ii.data()); |
||||||
|
if(ret != NC_NOERR) return Error(pref, MString("Can't read attribute ") + name + ": " + nc_strerror(ret)); |
||||||
|
for(size_t i = 0; i < ii.size(); i++) out.emplace_back(MString(name) + "[" + i + "]", int_cast<int8>(ii[i])); |
||||||
|
} |
||||||
|
} |
||||||
|
else if(type == NC_UBYTE || type == NC_USHORT || type == NC_UINT || type == NC_UINT64) |
||||||
|
{ |
||||||
|
if(len == 1) |
||||||
|
{ |
||||||
|
unsigned long long u; |
||||||
|
ret = nc_get_att_ulonglong(ncid, vid, name, &u); |
||||||
|
if(ret != NC_NOERR) return Error(pref, MString("Can't read attribute ") + name + ": " + nc_strerror(ret)); |
||||||
|
out.emplace_back(MString(name), int_cast<uint8>(u)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
std::vector<unsigned long long> uu(len); |
||||||
|
ret = nc_get_att_ulonglong(ncid, vid, name, uu.data()); |
||||||
|
if(ret != NC_NOERR) return Error(pref, MString("Can't read attribute ") + name + ": " + nc_strerror(ret)); |
||||||
|
for(size_t i = 0; i < uu.size(); i++) out.emplace_back(MString(name) + "[" + i + "]", int_cast<uint8>(uu[i])); |
||||||
|
} |
||||||
|
} |
||||||
|
else if(type == NC_CHAR) |
||||||
|
{ |
||||||
|
std::vector<char> ss(len + 1, 0); |
||||||
|
ret = nc_get_att_text(ncid, vid, name, ss.data()); |
||||||
|
if(ret != NC_NOERR) return Error(pref, MString("Can't read attribute ") + name + ": " + nc_strerror(ret)); |
||||||
|
out.emplace_back(MString(name), MString(ss.data())); |
||||||
|
} |
||||||
|
else |
||||||
|
return Error(pref, MString("Unsupported type of attribute ") + name); |
||||||
|
|
||||||
|
// Ignore all other types
|
||||||
|
} |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
Error NCSimpleFunctions::Open(const MString& filename) |
||||||
|
{ |
||||||
|
static const MString pref = "NCSimple::Open"; |
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
gats.clear(); |
||||||
|
dims.clear(); |
||||||
|
vars.clear(); |
||||||
|
|
||||||
|
nc_close(ncid); |
||||||
|
|
||||||
|
std::vector<Attribute> newgats; |
||||||
|
std::vector<Dimension> newdims; |
||||||
|
std::vector<Variable> newvars; |
||||||
|
|
||||||
|
char name[NC_MAX_NAME + 1]; |
||||||
|
|
||||||
|
// Open
|
||||||
|
{ |
||||||
|
auto ret = nc_open(filename.Buf(), 0, &ncid); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't open file " + filename + ": " + nc_strerror(ret)); |
||||||
|
} |
||||||
|
|
||||||
|
// Dimensions
|
||||||
|
{ |
||||||
|
int ndim; |
||||||
|
auto ret = nc_inq_dimids(ncid, &ndim, nullptr, 1); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't inquire number of dimensions in file " + filename + ": " + nc_strerror(ret)); |
||||||
|
std::vector<int> dimids(ndim); |
||||||
|
ret = nc_inq_dimids(ncid, nullptr, dimids.data(), 1); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't inquire dimension ids in file " + filename + ": " + nc_strerror(ret)); |
||||||
|
|
||||||
|
size_t len; |
||||||
|
for(const auto id: dimids) |
||||||
|
{ |
||||||
|
ret = nc_inq_dim(ncid, id, name, &len); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't inquire dimension name and size in file " + filename + ": " + nc_strerror(ret)); |
||||||
|
newdims.emplace_back(name, len); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Global attributes
|
||||||
|
{ |
||||||
|
auto ret = ReadAtts(NC_GLOBAL); |
||||||
|
if(!ret) return ret.Add(pref, "Can't read global attributes in file " + filename); |
||||||
|
newgats = std::move(ret.Value()); |
||||||
|
} |
||||||
|
|
||||||
|
// Variables
|
||||||
|
{ |
||||||
|
int nvar; |
||||||
|
auto ret = nc_inq_varids(ncid, &nvar, nullptr); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't inquire number of variables in file " + filename + ": " + nc_strerror(ret)); |
||||||
|
std::vector<int> varids(nvar); |
||||||
|
ret = nc_inq_varids(ncid, nullptr, varids.data()); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't inquire variables ids in file " + filename + ": " + nc_strerror(ret)); |
||||||
|
|
||||||
|
for(const auto vid: varids) |
||||||
|
{ |
||||||
|
nc_type nctype; |
||||||
|
int ndim; |
||||||
|
|
||||||
|
auto ret = nc_inq_var(ncid, vid, name, &nctype, &ndim, nullptr, nullptr); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't inquire variable info in file " + filename + ": " + nc_strerror(ret)); |
||||||
|
|
||||||
|
VarType vt = VarType::UNDEF; |
||||||
|
|
||||||
|
if(nctype == NC_FLOAT) vt = VarType::FLOAT; |
||||||
|
if(nctype == NC_DOUBLE) vt = VarType::DOUBLE; |
||||||
|
if(nctype == NC_BYTE) vt = VarType::INT1; |
||||||
|
if(nctype == NC_SHORT) vt = VarType::INT2; |
||||||
|
if(nctype == NC_INT) vt = VarType::INT4; |
||||||
|
if(nctype == NC_INT64) vt = VarType::INT8; |
||||||
|
if(nctype == NC_UBYTE) vt = VarType::UINT1; |
||||||
|
|
||||||
|
if(vt == VarType::UNDEF) return Error(pref, "Unsupported type of variable " + MString(name) + " in file " + filename); |
||||||
|
|
||||||
|
std::vector<int> dimids(ndim); |
||||||
|
ret = nc_inq_vardimid(ncid, vid, dimids.data()); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't inquire variable dimensions in file " + filename + ": " + nc_strerror(ret)); |
||||||
|
|
||||||
|
std::vector<size_t> dims; |
||||||
|
char dname[NC_MAX_NAME + 1]; |
||||||
|
for(const auto did: dimids) |
||||||
|
{ |
||||||
|
auto ret = nc_inq_dimname(ncid, did, dname); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't inquire dimension name in file " + filename + ": " + nc_strerror(ret)); |
||||||
|
size_t ind = newdims.size(); |
||||||
|
for(size_t i = 0; i < newdims.size() && ind == newdims.size(); i++) |
||||||
|
if(dname == newdims[i].Name()) ind = i; |
||||||
|
if(ind == newdims.size()) return Error(pref, "Can't find dimension " + MString(dname) + " of variable " + name + " in file " + filename); |
||||||
|
dims.push_back(ind); |
||||||
|
} |
||||||
|
|
||||||
|
auto atts = ReadAtts(vid); |
||||||
|
const char fname[] = "_FillValue"; |
||||||
|
if(!atts) return Error(pref, "Can't get attributes of variable " + MString(name) + " in file " + filename); |
||||||
|
std::vector<Attribute> vatts = std::move(atts.Value()); |
||||||
|
|
||||||
|
Variable::FillType fill; |
||||||
|
if(FindInd(fname, vatts) != vatts.size()) |
||||||
|
{ |
||||||
|
if(nctype == NC_FLOAT || nctype == NC_DOUBLE) |
||||||
|
{ |
||||||
|
double d; |
||||||
|
auto ret = nc_get_att_double(ncid, vid, fname, &d); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't get fill value for the variable " + MString(name) + " in file " + filename + ": " + nc_strerror(ret)); |
||||||
|
fill = d; |
||||||
|
} |
||||||
|
if(nctype == NC_BYTE || nctype == NC_SHORT || nctype == NC_INT || nctype == NC_INT64) |
||||||
|
{ |
||||||
|
long long l; |
||||||
|
auto ret = nc_get_att_longlong(ncid, vid, fname, &l); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't get fill value for the variable " + MString(name) + " in file " + filename + ": " + nc_strerror(ret)); |
||||||
|
fill = int_cast<int8>(l); |
||||||
|
} |
||||||
|
if(nctype == NC_UBYTE || nctype == NC_USHORT || nctype == NC_UINT || nctype == NC_UINT64) |
||||||
|
{ |
||||||
|
unsigned long long u; |
||||||
|
auto ret = nc_get_att_ulonglong(ncid, vid, fname, &u); |
||||||
|
if(ret != NC_NOERR) return Error(pref, "Can't get fill value for the variable " + MString(name) + " in file " + filename + ": " + nc_strerror(ret)); |
||||||
|
fill = int_cast<uint8>(u); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
newvars.emplace_back(name, vt, std::move(dims), std::move(vatts), fill); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
gats = std::move(newgats); |
||||||
|
dims = std::move(newdims); |
||||||
|
vars = std::move(newvars); |
||||||
|
return Error(); |
||||||
|
} |
@ -0,0 +1,249 @@ |
|||||||
|
#define MICHLIB_NOSOURCE |
||||||
|
#include "zarr.h" |
||||||
|
#include "copcat.h" |
||||||
|
#include <blosc.h> |
||||||
|
|
||||||
|
std::vector<MString> ZarrFunctions::ReadVarNames(const Json::Value& meta) |
||||||
|
{ |
||||||
|
std::vector<MString> out; |
||||||
|
if(meta.type() != Json::objectValue) return out; |
||||||
|
const auto keys = meta.getMemberNames(); |
||||||
|
for(const auto& key: keys) |
||||||
|
{ |
||||||
|
if(!key.ends_with("/.zarray")) continue; |
||||||
|
const auto vname = key.substr(0, key.size() - 8); |
||||||
|
const auto& zattr = meta[vname + "/.zattrs"]; |
||||||
|
if(!(zattr && zattr.type() == Json::objectValue)) continue; |
||||||
|
|
||||||
|
MString name(vname.c_str(), vname.size()); |
||||||
|
bool found = false; |
||||||
|
for(size_t id = 0; id < out.size(); id++) |
||||||
|
if(out[id] == name) |
||||||
|
{ |
||||||
|
found = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
if(!found) out.emplace_back(std::move(name)); |
||||||
|
} |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
Error ZarrFunctions::AddVar(const MString& name, const Json::Value& zattrs, const Json::Value& zarray) |
||||||
|
{ |
||||||
|
static const MString pref = "Zarr::AddVar"; |
||||||
|
|
||||||
|
VarType newtype; |
||||||
|
|
||||||
|
Variable::FillType fill; |
||||||
|
|
||||||
|
// Checks for parameters in zarray
|
||||||
|
{ |
||||||
|
const auto& cid = zarray["compressor"]["id"]; |
||||||
|
if(!cid || cid.type() != Json::stringValue || cid.asString() != "blosc") return {pref, "Unsupported compressor: " + MString(cid.asString().c_str())}; |
||||||
|
} |
||||||
|
{ |
||||||
|
const auto& zf = zarray["zarr_format"]; |
||||||
|
if(!zf || (zf.type() != Json::uintValue && zf.type() != Json::intValue) || zf.asUInt() != 2) return {pref, "Unsupported format version: " + MString(zf.asUInt())}; |
||||||
|
} |
||||||
|
{ |
||||||
|
const auto& ord = zarray["order"]; |
||||||
|
if(!ord || ord.type() != Json::stringValue || ord.asString() != "C") return {pref, "Order in not C"}; |
||||||
|
} |
||||||
|
{ |
||||||
|
const auto& f = zarray["filters"]; |
||||||
|
if(f.type() != Json::nullValue) return {pref, "Filters is not null"}; |
||||||
|
} |
||||||
|
|
||||||
|
// Read dtype
|
||||||
|
{ |
||||||
|
const auto& dtype = zarray["dtype"]; |
||||||
|
if(!dtype || dtype.type() != Json::stringValue) return {pref, "No datatype"}; |
||||||
|
const auto str = dtype.asString(); |
||||||
|
if(str == "<f4") |
||||||
|
newtype = VarType::FLOAT; |
||||||
|
else if(str == "<f8") |
||||||
|
newtype = VarType::DOUBLE; |
||||||
|
else if(str == "|i1") |
||||||
|
newtype = VarType::INT1; |
||||||
|
else if(str == "|u1") |
||||||
|
newtype = VarType::UINT1; |
||||||
|
else if(str == "<i2") |
||||||
|
newtype = VarType::INT2; |
||||||
|
else if(str == "<i4") |
||||||
|
newtype = VarType::INT4; |
||||||
|
else if(str == "<i8") |
||||||
|
newtype = VarType::INT8; |
||||||
|
else |
||||||
|
return {pref, "Unsupported datatype: " + MString(str.c_str())}; |
||||||
|
} |
||||||
|
|
||||||
|
// Read fill_value
|
||||||
|
{ |
||||||
|
const auto& fillval = zarray["fill_value"]; |
||||||
|
if(!fillval) // return {pref, "No fillval"};
|
||||||
|
fill = 0; |
||||||
|
else if(fillval.type() == Json::uintValue) |
||||||
|
fill = fillval.asUInt64(); |
||||||
|
else if(fillval.type() == Json::intValue) |
||||||
|
fill = fillval.asInt64(); |
||||||
|
else if(fillval.type() == Json::realValue) |
||||||
|
fill = fillval.asDouble(); |
||||||
|
else if(fillval.type() == Json::stringValue && fillval.asString() == "NaN") |
||||||
|
fill = NAN; |
||||||
|
} |
||||||
|
|
||||||
|
// Read attributes
|
||||||
|
auto atts = ReadAtts(zattrs); |
||||||
|
|
||||||
|
std::vector<MString> dnames; |
||||||
|
std::vector<size_t> dsizes; |
||||||
|
std::vector<size_t> csizes; |
||||||
|
std::vector<size_t> dids; |
||||||
|
|
||||||
|
// Read dimensions names
|
||||||
|
{ |
||||||
|
const auto& arrdim = zattrs["_ARRAY_DIMENSIONS"]; |
||||||
|
if(!(arrdim && arrdim.type() == Json::arrayValue)) return {pref, "_ARRAY_DIMENSIONS not found"}; |
||||||
|
for(Json::ArrayIndex i = 0; i < arrdim.size(); i++) |
||||||
|
if(const auto& dim = arrdim[i]; dim.type() == Json::stringValue) |
||||||
|
{ |
||||||
|
const auto val = dim.asString(); |
||||||
|
dnames.emplace_back(val.c_str(), val.size()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Read dimensions sizes
|
||||||
|
{ |
||||||
|
const auto& shape = zarray["shape"]; |
||||||
|
if(!(shape && shape.type() == Json::arrayValue)) return {pref, "shape not found"}; |
||||||
|
for(Json::ArrayIndex i = 0; i < shape.size(); i++) |
||||||
|
if(const auto& s = shape[i]; s.type() == Json::uintValue || s.type() == Json::intValue) dsizes.push_back(s.asUInt()); |
||||||
|
} |
||||||
|
|
||||||
|
// Read chunk sizes
|
||||||
|
{ |
||||||
|
const auto& chunk = zarray["chunks"]; |
||||||
|
if(!(chunk && chunk.type() == Json::arrayValue)) return {pref, "chunks not found"}; |
||||||
|
for(Json::ArrayIndex i = 0; i < chunk.size(); i++) |
||||||
|
if(const auto& c = chunk[i]; c.type() == Json::uintValue || c.type() == Json::intValue) csizes.push_back(c.asUInt()); |
||||||
|
} |
||||||
|
|
||||||
|
if(dnames.size() != dsizes.size() || dnames.size() != csizes.size()) return {pref, "shape and chunks are in contradiction"}; |
||||||
|
|
||||||
|
dids.resize(dnames.size()); |
||||||
|
|
||||||
|
// Check dimensions names and sizes
|
||||||
|
for(size_t i = 0; i < dnames.size(); i++) |
||||||
|
{ |
||||||
|
bool found = false; |
||||||
|
for(size_t id = 0; id < dims.size(); id++) |
||||||
|
if(dims[id].Name() == dnames[i]) |
||||||
|
{ |
||||||
|
found = true; |
||||||
|
if(dims[id].Size() != dsizes[i]) |
||||||
|
return {pref, "According to previous data, the dimension " + dnames[i] + " has a size of " + dims[id].Size() + ", but here it is defined as " + dsizes[i]}; |
||||||
|
dids[i] = id; |
||||||
|
break; |
||||||
|
} |
||||||
|
if(!found) |
||||||
|
{ |
||||||
|
dids[i] = dims.size(); |
||||||
|
dims.emplace_back(dnames[i], dsizes[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
vars.emplace_back(name, newtype, std::move(dids), std::move(atts), fill); |
||||||
|
chunks.push_back(std::move(csizes)); |
||||||
|
|
||||||
|
return Error(); |
||||||
|
} |
||||||
|
|
||||||
|
Error ZarrFunctions::GetChunk(const MString& var, const std::vector<size_t>& chunkind, size_t chunksize, size_t elsize, void* data, const void* fill) const |
||||||
|
{ |
||||||
|
static const MString pref = "Zarr::GetChunk"; |
||||||
|
|
||||||
|
MString str = url + "/" + var + "/"; |
||||||
|
for(size_t i = 0; i < chunkind.size(); i++) str += (i == 0 ? "" : ".") + MString(chunkind[i]); |
||||||
|
|
||||||
|
auto [content, suc] = cache->Get(str); |
||||||
|
|
||||||
|
if(!suc) |
||||||
|
{ |
||||||
|
michlib::message(str + " not found in cache, downloading"); |
||||||
|
CURLRAII myhandle; // TODO: remove this workaround of unknown bug
|
||||||
|
//auto [out, res] = GetUrl(chandle, str);
|
||||||
|
auto [out, res] = GetUrl(myhandle, str); |
||||||
|
if(res != CURLE_OK) return Error(pref, MString("can't download chunk: ") + chandle.Err()); |
||||||
|
long respcode; |
||||||
|
//curl_easy_getinfo(chandle, CURLINFO_RESPONSE_CODE, &respcode);
|
||||||
|
curl_easy_getinfo(myhandle, CURLINFO_RESPONSE_CODE, &respcode); |
||||||
|
if(respcode == 403) out = ""; // Failed chunk download mean that this chunk contains only fill
|
||||||
|
cache->Put(str, out, 3600); |
||||||
|
content = std::move(out); |
||||||
|
} |
||||||
|
|
||||||
|
if(content.Exist()) |
||||||
|
{ |
||||||
|
size_t nb, cb, bs; |
||||||
|
blosc_cbuffer_sizes(content.Buf(), &nb, &cb, &bs); |
||||||
|
if(cb != content.Len()) return Error(pref, MString("bytes download: ") + content.Len() + ", but compressed bytes " + cb); |
||||||
|
if(nb != chunksize * elsize) return Error(pref, MString("decompressed bytes: ") + nb + ", but buffer size " + chunksize * elsize); |
||||||
|
auto res = blosc_decompress_ctx(content.Buf(), data, chunksize * elsize, 1); |
||||||
|
if(int_cast<size_t>(res) != chunksize * elsize) return Error(pref, MString("decompress only ") + res + " bytes of " + chunksize * elsize); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if(fill == nullptr) return Error(pref, MString("can't download chunk: ") + chandle.Err()); |
||||||
|
for(size_t i = 0; i < chunksize; i++) memcpy(michlib::P1(data) + i * elsize, fill, elsize); |
||||||
|
} |
||||||
|
|
||||||
|
return Error(); |
||||||
|
} |
||||||
|
|
||||||
|
Error ZarrFunctions::Open(const MString& product, const MString& dataset, bool time) |
||||||
|
{ |
||||||
|
static const MString pref = "Zarr::Open"; |
||||||
|
|
||||||
|
gats.clear(); |
||||||
|
dims.clear(); |
||||||
|
vars.clear(); |
||||||
|
|
||||||
|
CopernicusCatalog cat; |
||||||
|
Json::Value json; |
||||||
|
|
||||||
|
MString realdataset; |
||||||
|
if(!dataset.Exist()) |
||||||
|
{ |
||||||
|
auto dsets = cat.DatasetList(product); |
||||||
|
if(!dsets) return dsets.Add(pref, "Can't get default dataset of product " + product); |
||||||
|
realdataset = dsets.Value()[0]; |
||||||
|
} |
||||||
|
else |
||||||
|
realdataset = dataset; |
||||||
|
|
||||||
|
{ |
||||||
|
auto urlret = time ? cat.DatasetTimeURL(product, realdataset) : cat.DatasetGeoURL(product, realdataset); |
||||||
|
if(!urlret) return urlret.Add(pref, "Can't get url for the dataset " + realdataset + " of product " + product); |
||||||
|
url = urlret.Value(); |
||||||
|
|
||||||
|
auto ret = cat.GetJSON(url + "/.zmetadata"); |
||||||
|
if(ret) |
||||||
|
json = ret.Value(); |
||||||
|
else |
||||||
|
return ret.Add(pref, "can't download .zmetadata"); |
||||||
|
} |
||||||
|
|
||||||
|
const auto& meta = json["metadata"]; |
||||||
|
if(!meta) return {pref, "No \"metadata\" key in JSON data"}; |
||||||
|
|
||||||
|
if(meta[".zattrs"]) gats = ReadAtts(meta[".zattrs"]); |
||||||
|
auto vnames = ReadVarNames(meta); |
||||||
|
|
||||||
|
for(size_t i = 0; i < vnames.size(); i++) |
||||||
|
{ |
||||||
|
auto err = AddVar(vnames[i], meta[(vnames[i] + "/.zattrs").Buf()], meta[(vnames[i] + "/.zarray").Buf()]); |
||||||
|
if(!err) return err.Add(pref, "Can't init variable " + vnames[i]); |
||||||
|
} |
||||||
|
|
||||||
|
return Error(); |
||||||
|
} |
Loading…
Reference in new issue