You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
9.2 KiB
259 lines
9.2 KiB
#pragma once |
|
#include "ParseArgs.h" |
|
#include "basedata.h" |
|
#include "mdatetime.h" |
|
#include "mregex.h" |
|
#include "traits.h" |
|
#include "uvdata.h" |
|
#include <array> |
|
#include <utility> |
|
|
|
using michlib::MDateTime; |
|
|
|
#if defined GENACTIONLIST |
|
#define ADD_ACTION(actclass, actname, suptest, ...) ADD ACTION CLASS: actclass |
|
#else |
|
#define ADD_ACTION(actclass, actname, suptest, ...) \ |
|
class Action##actclass __VA_OPT__( : protected) __VA_ARGS__ \ |
|
{ \ |
|
public: \ |
|
static constexpr const char* name = #actname; \ |
|
template<class Source> static constexpr bool IsSupported = (suptest); \ |
|
template<class Source> static MString DoAction(const CLArgs& args, Source& data); \ |
|
}; |
|
#endif |
|
|
|
struct TimeData |
|
{ |
|
static constexpr auto stepunits = std::to_array<uint>({3600 * 24, 3600, 60, 1}); |
|
static constexpr std::array<const std::string, stepunits.size()> stepnames{"days", "hours", "minutes", "seconds"}; |
|
|
|
MDateTime refdate; |
|
size_t unitind; |
|
std::vector<michlib::int4> steps; |
|
|
|
template<class D> TimeData(const D& data, const TIndex& tindexes): refdate(), unitind(stepunits.size()), steps(tindexes.size()) |
|
{ |
|
if(steps.size() == 0) return; |
|
refdate = data.Time(tindexes[0]); |
|
unitind = 0; |
|
|
|
for(size_t i = 0; i < steps.size(); i++) |
|
{ |
|
auto delta = data.Time(tindexes[i]) - refdate; |
|
while(delta % stepunits[unitind] != 0) unitind++; |
|
} |
|
|
|
for(size_t i = 0; i < steps.size(); i++) |
|
{ |
|
auto delta = data.Time(tindexes[i]) - refdate; |
|
steps[i] = michlib::int_cast<decltype(steps)::value_type>(delta / stepunits[unitind]); |
|
} |
|
} |
|
|
|
MString UnitName() const |
|
{ |
|
MString out = stepnames[unitind].c_str(); |
|
return out + " since " + refdate.ToString(); |
|
} |
|
}; |
|
|
|
template<class D> size_t GetTIndex(const D& data, const MDateTime& t) |
|
{ |
|
size_t nt = data.NTimes(); |
|
|
|
if(t <= data.Time(0)) return 0; |
|
if(t >= data.Time(nt - 1)) return nt - 1; |
|
for(size_t i = 0; i < nt - 1; i++) |
|
if(t >= data.Time(i) && t <= data.Time(i + 1)) return (t - data.Time(i) <= data.Time(i + 1) - t) ? i : (i + 1); |
|
return 0; |
|
} |
|
|
|
template<class D> std::pair<TIndex, MString> GetTIndexes(const D& data, const CLArgs& args, michlib_internal::ParameterListEx& pars) |
|
{ |
|
TIndex tindexes; |
|
|
|
if(args.contains("time") && (args.contains("timeb") || args.contains("timee"))) |
|
return {tindexes, "Time must be set via time parameter or timeb and timee parameter but not via both"}; |
|
if(!(args.contains("time") || (args.contains("timeb") && args.contains("timee")))) return {tindexes, "Time must be set via time parameter or timeb and timee parameter"}; |
|
|
|
if(args.contains("time")) |
|
{ |
|
MString regex = args.at("time"); |
|
MDateTime time; |
|
|
|
if(time.FromString(regex)) // Time, not regex |
|
tindexes.push_back(GetTIndex(data, time)); |
|
else if(regex == "BEGIN" || regex == "BEG" || regex == "FIRST") // First time |
|
tindexes.push_back(0); |
|
else if(regex == "END" || regex == "LAST") // Last time |
|
tindexes.push_back(data.NTimes() - 1); |
|
else // Regular expression |
|
{ |
|
michlib::RegExpSimple reg(regex.Buf()); |
|
if(reg.Compile() != 0) return {tindexes, "Bad regular expression: " + regex}; |
|
|
|
for(size_t i = 0; i < data.NTimes(); i++) |
|
{ |
|
MString date = data.Time(i).ToString(); |
|
if(reg.Match(date.Buf())) tindexes.push_back(i); |
|
} |
|
} |
|
|
|
if(tindexes.size() == 0) return {tindexes, "There are no times matching the regular expression: " + regex}; |
|
if(tindexes.size() == 1) |
|
pars.SetParameter("time", data.Time(tindexes[0]).ToTString()); |
|
else |
|
pars.SetParameter("timeregex", args.at("time")); |
|
} |
|
else |
|
{ |
|
MString tb = args.at("timeb"); |
|
MString te = args.at("timee"); |
|
MDateTime b(tb), e(te); |
|
|
|
auto nt = data.NTimes(); |
|
if(tb == "BEGIN" || tb == "BEG") b = data.Time(0); |
|
if(te == "LAST" || te == "END") e = data.Time(nt - 1); |
|
|
|
const MDateTime& beg = (b < e) ? b : e; |
|
const MDateTime& end = (b > e) ? b : e; |
|
|
|
if(beg > data.Time(nt - 1)) return {tindexes, "Begin time " + b.ToTString() + " is greater then end time in the dataset " + data.Time(nt - 1).ToTString()}; |
|
if(end < data.Time(0)) return {tindexes, "End time " + e.ToTString() + " is lesser then begin time in the dataset " + data.Time(0).ToTString()}; |
|
|
|
for(size_t i = 0; i < nt; i++) |
|
if(data.Time(i) >= beg && data.Time(i) <= end) tindexes.push_back(i); |
|
|
|
if(tindexes.size() == 0) return {tindexes, "There are no times between " + b.ToString() + " and " + e.ToString()}; |
|
pars.SetParameter("timeb", b.ToTString()); |
|
pars.SetParameter("timee", e.ToTString()); |
|
} |
|
|
|
std::ranges::sort(tindexes, [&data = std::as_const(data)](size_t a, size_t b) { return data.Time(a) < data.Time(b); }); |
|
|
|
return {tindexes, ""}; |
|
} |
|
|
|
template<class D> std::vector<ReadType<D>> Read(const D& data, const std::vector<MString>& vnames, const BaseParameters* p, size_t ind) |
|
{ |
|
using RT = ReadType<D>; |
|
std::vector<RT> out; |
|
michlib::message("Time: " + data.Time(ind).ToTString()); |
|
std::map<MString, RT> cache; |
|
for(const auto& vname: vnames) |
|
{ |
|
bool res; |
|
if constexpr(ReadPSupported<D>) |
|
res = data.Read(vname, cache, p, ind); |
|
else if constexpr(ReadSupported<D>) |
|
res = data.Read(vname, cache, ind); |
|
if(!res) return out; |
|
} |
|
for(size_t i = 0; i < vnames.size(); i++) out.emplace_back(std::move(cache[vnames[i]])); |
|
|
|
return out; |
|
} |
|
|
|
template<class D> std::vector<ReadType<D>> Read(const D& data, const std::vector<MString>& vnames, const BaseParameters* p, const TIndex& tindex) |
|
{ |
|
using RT = ReadType<D>; |
|
size_t ind; |
|
std::vector<RT> out; |
|
if(tindex.size() == 1) |
|
return Read(data, vnames, p, tindex[0]); |
|
else |
|
{ |
|
std::vector<Averager<RT>> aver(vnames.size()); |
|
for(size_t i = 0; i < tindex.size(); i++) |
|
{ |
|
ind = tindex[i]; |
|
michlib::message("Time: " + data.Time(ind).ToTString()); |
|
std::map<MString, RT> cache; |
|
for(const auto& vname: vnames) |
|
{ |
|
bool res; |
|
if constexpr(ReadPSupported<D>) |
|
res = data.Read(vname, cache, p, ind); |
|
else if constexpr(ReadSupported<D>) |
|
res = data.Read(vname, cache, ind); |
|
if(!res) return out; |
|
} |
|
for(size_t i = 0; i < vnames.size(); i++) aver[i].Add(std::move(cache[vnames[i]])); |
|
} |
|
for(size_t i = 0; i < vnames.size(); i++) out.emplace_back(std::move(aver[i].Div())); |
|
return out; |
|
} |
|
return out; |
|
} |
|
|
|
template<class D> UVData<ReadType<D>> ReadUV(const D& data, const BaseParameters* p, size_t ind, bool usegeo) |
|
{ |
|
using RT = ReadType<D>; |
|
using UV = UVData<RT>; |
|
michlib::message("Time: " + data.Time(ind).ToTString()); |
|
std::map<MString, RT> cache; |
|
bool res = false; |
|
const MString uname = usegeo ? "ugeo" : "u"; |
|
const MString vname = usegeo ? "vgeo" : "v"; |
|
if constexpr(ReadPSupported<D>) |
|
res = data.Read(uname, cache, p, ind) && data.Read(vname, cache, p, ind); |
|
else if constexpr(ReadSupported<D>) |
|
res = data.Read(uname, cache, ind) && data.Read(vname, cache, ind); |
|
if(!res) return UV(); |
|
|
|
return UV(cache.at(uname), cache.at(vname)); |
|
} |
|
|
|
template<class D> UVData<ReadType<D>> ReadUV(const D& data, const BaseParameters* p, const TIndex& tindex, bool usegeo) |
|
{ |
|
using RT = ReadType<D>; |
|
using UV = UVData<RT>; |
|
const MString uname = usegeo ? "ugeo" : "u"; |
|
const MString vname = usegeo ? "vgeo" : "v"; |
|
|
|
if(tindex.size() == 1) |
|
return ReadUV(data, p, tindex[0], usegeo); |
|
else |
|
{ |
|
Averager<UV> out; |
|
bool ok = true; |
|
size_t ind; |
|
for(size_t i = 0; i < tindex.size(); i++) |
|
{ |
|
if(!ok) break; |
|
ind = tindex[i]; |
|
michlib::message("Time: " + data.Time(ind).ToTString()); |
|
std::map<MString, RT> cache; |
|
bool res = false; |
|
if constexpr(ReadPSupported<D>) |
|
res = data.Read(uname, cache, p, ind) && data.Read(vname, cache, p, ind); |
|
else if constexpr(ReadSupported<D>) |
|
res = data.Read(uname, cache, ind) && data.Read(vname, cache, ind); |
|
if(!res) return UV(); |
|
|
|
UV dat(cache.at(uname), cache.at(vname)); |
|
if(dat) |
|
out.Add(std::move(dat)); |
|
else |
|
ok = false; |
|
} |
|
if(ok) return out.Div(); |
|
} |
|
return UV(); |
|
} |
|
|
|
template<class D> std::pair<struct Region, MString> GetRegion(const CLArgs& args) |
|
{ |
|
struct Region reg; |
|
if constexpr(!ParametersRequiredRegion<D>) return {reg, ""}; |
|
if(!(args.contains("lonb") && args.contains("lone") && args.contains("latb") && args.contains("late"))) return {reg, "Region not specified (lonb, lone, latb, late)"}; |
|
reg.lonb = args.at("lonb").ToReal(); |
|
reg.lone = args.at("lone").ToReal(); |
|
real latb = args.at("latb").ToReal(); |
|
real late = args.at("late").ToReal(); |
|
reg.latb = std::min(latb, late); |
|
reg.late = std::max(latb, late); |
|
|
|
return {reg, ""}; |
|
}
|
|
|