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.
 
 
 

253 lines
6.9 KiB

#pragma once
#include "DataAdapters/ncfilealt.h"
#include "ParseArgs.h"
#include "gsw.h"
#include "mdatetime.h"
#include "simple2ddata.h"
#include "uvdata.h"
#include <algorithm>
#include <memory>
#include <set>
using michlib::Ceil;
using michlib::DetGeoDomain;
using michlib::Floor;
using michlib::GPL;
using michlib::int2;
using michlib::MDateTime;
using michlib::MString;
using michlib::NCFileA;
using michlib::ToGeoDomain;
class LayeredData
{
public:
using Data = Simple2DData;
private:
class NC
{
MString url;
NCFileA nc;
std::vector<MDateTime> times;
public:
NC(MString&& newurl): url(std::move(newurl)) { nc.Reset(url + "#cache&noprefetch"); }
MString ReadTimes()
{
if(!nc) return "File not open";
auto time = nc.VR("time");
if(!time) return "Can't read times";
MDateTime refdate;
{
auto units = nc.Attribute<MString>("time", "units");
if(!units) return "Can't read refdate";
MString rstr;
auto words = michlib::Split_on_words(units);
auto ci = words.begin();
if(ci != words.end()) ci++; // skip "hours"
if(ci != words.end()) ci++; // skip "since"
if(ci != words.end()) rstr = *ci; // Day
if(ci != words.end()) ci++;
if(ci != words.end()) rstr += " " + *ci; // Hours
if(!refdate.FromString(rstr)) return "Can't parse " + rstr + " to refdate";
}
times.resize(time.DimLen(0));
for(size_t i = 0; i < times.size(); i++) times[i] = refdate + static_cast<time_t>(time(i)) * 3600;
return "";
}
const NCFileA* operator->() const { return &nc; }
explicit operator bool() const { return nc; }
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;
std::vector<MDateTime> times;
MString lonname, latname;
size_t nx, ny;
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;
Data Read(const MString& vname, const BaseParameters* ip, size_t i) const;
UVData ReadUV(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 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]) : 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
}
bool CheckVar(const MString& vname) const
{
if(!HaveVar(vname))
{
bool varexist = false;
if(vname == "temp" && HaveVar("ptemp") && HaveVar("sal")) varexist = true;
if(vname == "ptemp" && HaveVar("temp") && HaveVar("sal")) varexist = true;
if(vname == "pdens" && (HaveVar("ptemp") || HaveVar("temp")) && HaveVar("sal")) varexist = true;
if((vname == "U" || vname == "U2") && HaveVar("u") && HaveVar("v")) varexist = true;
if(!varexist) return false;
}
return true;
}
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++)
{
auto head = nc[i]->Header();
for(const auto& v: head.Variables())
{
auto stname = nc[i]->A<MString>(v.Name(), "standard_name");
if(!stname) continue;
if(StName2Name(stname) == 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 head = nc[i]->Header();
auto tind = nc[i].Index(tm);
if(tind == 0) continue;
for(const auto& v: head.Variables())
{
auto stname = nc[i]->A<MString>(v.Name(), "standard_name");
if(!stname) continue;
if(StName2Name(stname) == vname) return {v.Name(), i, tind - 1};
}
}
return {"", 0, 0};
}
static MString StName2Name(const MString& stname)
{
if(stname == "sea_water_potential_temperature") return "ptemp";
if(stname == "sea_water_temperature") return "temp";
if(stname == "sea_water_salinity") return "sal";
if(stname == "ocean_mixed_layer_thickness_defined_by_sigma_theta") return "mld";
if(stname == "sea_surface_height_above_geoid") return "ssh";
if(stname == "sea_surface_elevation") return "ssh";
if(stname == "eastward_sea_water_velocity") return "u";
if(stname == "northward_sea_water_velocity") return "v";
if(stname == "surface_geostrophic_eastward_sea_water_velocity") return "u";
if(stname == "surface_geostrophic_northward_sea_water_velocity") return "v";
if(stname == "upward_sea_water_velocity") return "w";
return "";
}
};