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.

260 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, ""};
}