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.
 
 
 
 
 
 

1064 lines
41 KiB

#ifndef MODGMT_FUNC_H
#define MODGMT_FUNC_H
#include "modgmt_internals.h"
#include "modgmt_objects.h"
#include "modgmt_param.h"
const ObjectBase* GMT_Header(const ObjectList* input);
const ObjectBase* GMT_Footer(const ObjectList* input);
const ObjectBase* GMT_ColorGray(const ObjectList* input);
const ObjectBase* GMT_ColorRGB(const ObjectList* input);
const ObjectBase* GMT_ColorHSV(const ObjectList* input);
const ObjectBase* GMT_ColorCMYK(const ObjectList* input);
const ObjectBase* GMT_LayerShift(const ObjectList* input);
// Generic converting template
template<class Struct, class O>
class Convert2Struct
{
public:
template<class... Args>
Struct operator()(const O* o, bool* suc, std::string& err, Args... args) const
{
Struct s;
if(!s.Convert(o->Value(),err,args...)) *suc=false;
return s;
}
};
// Extract structure from corresponding class
template<class Struct>
class Convert2Struct<Struct, ObjectGMTClass<Struct> >
{
public:
template<class... Args>
Struct operator()(const ObjectGMTClass<Struct>* o, bool* suc, std::string& err, Args... args) const { return o->Data(); } // Never return error
};
// Converting Int or Real to double
template<class O>
class Convert2Struct<double,O>
{
public:
double operator ()(const O* q, bool* suc, std::string& err) const {return q->Value();}
};
// Converting String to double
template<>
class Convert2Struct<double,ObjectString>
{
public:
double operator ()(const ObjectString* q, bool* suc, std::string& err) const
{
double d=0;
bool res=str2double(q->Value(),&d);
if(!res) err="Can't convert string "+q->Value()+" to double";
*suc=(*suc) && res;
return d;
}
};
// Converting Int or Real to bool
template<class O>
class Convert2Struct<bool,O>
{
public:
bool operator ()(const O* q, bool* suc, std::string& err) const {return q->Value()!=0;}
};
// Converting String to bool
template<>
class Convert2Struct<bool,ObjectString>
{
public:
bool operator ()(const ObjectString* q, bool* suc, std::string& err) const
{
std::string v=q->Value();
tolower(v);
if("y"==v || "yes"==v || "on"==v || "t"==v || "true"==v || "1"==v) return true;
if("n"==v || "no"==v || "off"==v || "f"==v || "false"==v || "0"==v) return false;
err="Can't convert string "+q->Value()+" to bool";
*suc=false;
return false;
}
};
// Get string
template<>
class Convert2Struct<std::string,ObjectString>
{
public:
std::string operator ()(const ObjectString* q, bool* suc, std::string& err) const {return q->Value();}
};
// Generic template for Converter class (using for read parameters)
template<class Struct, class... Types>
class BaseConverter
{
template<class O> class StructConvertor: public Convert2Struct<Struct, O> {}; // Conversion type
public:
using ValueType=Struct;
template<class... Args>
ValueType Convert(const ObjectBase* ob, bool* res, std::string& err, Args... args)
{
OBTypeM<StructConvertor,Types...> gp(ob);
switch(gp.Error())
{
case(OBTypeErr::OK): return gp(res,std::ref(err),args...);
case(OBTypeErr::NULLPTR): {err="Can't convert zero ObjectBase pointer to something meaningfull"; break;}
case(OBTypeErr::TYPEMISMATCH): {err="Can't convert "+ob->Type()+": type mismatch"; break;}
}
*res=false;
return ValueType();
}
};
// Generic template for Converter class with default value (using for read parameters)
template<class B>
class DefaultConverter: public B
{
public:
using ValueType=typename B::ValueType;
private:
ValueType def;
public:
DefaultConverter() = delete;
template<class... Args>
DefaultConverter(const ValueType& d, Args... args):B(args...),def(d) {}
template<class... Args>
DefaultConverter(ValueType&& d, Args... args):B(args...),def(std::move(d)) {}
const ValueType& Default() const {return def;}
};
// Here we store type of Converter to type Struct. Generic template must never be used.
template<class Struct> class TypeStorage;
template<> class TypeStorage<double> {public: typedef BaseConverter<double, ObjectReal, ObjectInt, ObjectString > Converter;};
template<> class TypeStorage<bool> {public: typedef BaseConverter<bool, ObjectInt, ObjectReal, ObjectString > Converter;};
template<> class TypeStorage<std::string> {public: typedef BaseConverter<std::string, ObjectString > Converter;};
template<> class TypeStorage<struct gmt_coord> {public: typedef BaseConverter<struct gmt_coord, ObjectReal, ObjectInt, ObjectString,ObjectGMTCoord,ObjectList> Converter;};
template<> class TypeStorage<struct gmt_region> {public: typedef BaseConverter<struct gmt_region, ObjectList, ObjectString,ObjectGMTRegion > Converter;};
template<> class TypeStorage<struct gmt_projection> {public: typedef BaseConverter<struct gmt_projection,ObjectList, ObjectGMTProjection > Converter;};
template<> class TypeStorage<struct gmt_color> {public: typedef BaseConverter<struct gmt_color, ObjectList, ObjectString,ObjectReal,ObjectInt, ObjectGMTColor > Converter;};
template<> class TypeStorage<struct gmt_dash> {public: typedef BaseConverter<struct gmt_dash, ObjectString,ObjectGMTDash > Converter;};
template<> class TypeStorage<struct gmt_pen> {public: typedef BaseConverter<struct gmt_pen, ObjectList, ObjectString,ObjectReal,ObjectInt, ObjectGMTPen > Converter;};
template<> class TypeStorage<struct gmt_font> {public: typedef BaseConverter<struct gmt_font, ObjectList, ObjectString,ObjectReal,ObjectInt, ObjectGMTFont > Converter;};
template<> class TypeStorage<struct gmt_layer> {public: typedef BaseConverter<struct gmt_layer, ObjectList, ObjectGMTLayer > Converter;};
// Default ObjectList converter
template<class Struct>
class Convert2Struct<Struct, ObjectList>
{
public:
template<class... Args>
Struct operator()(const ObjectList* o, bool* suc, std::string& err, Args... args) const
{
Struct s;
if(o->Size()!=1) {*suc=false; err="Can't convert list with more (or less) than one elements"; return s;}
typename TypeStorage<Struct>::Converter a;
s=a.Convert(o->At(0),suc,err,args...);
return s;
}
};
// Template for simple aliases of convertors
template<class Struct>
using Base2Struct=typename TypeStorage<Struct>::Converter;
// Template for simple aliases of convertors with default values
template<class Struct>
using Base2StructD=DefaultConverter<typename TypeStorage<Struct>::Converter>;
// Simple aliases of convertors
using Base2Bool =Base2Struct<bool>;
using Base2Double=Base2Struct<double>;
using Base2String=Base2Struct<std::string>;
using Base2Coord =Base2Struct<struct gmt_coord>;
using Base2Region=Base2Struct<struct gmt_region>;
using Base2Proj =Base2Struct<struct gmt_projection>;
using Base2Color =Base2Struct<struct gmt_color>;
using Base2Dash =Base2Struct<struct gmt_dash>;
using Base2Pen =Base2Struct<struct gmt_pen>;
using Base2Font =Base2Struct<struct gmt_font>;
using Base2Layer =Base2Struct<struct gmt_layer>;
// Simple aliases of convertors with default values
using Base2StringD=Base2StructD<std::string>;
using Base2DoubleD=Base2StructD<double>;
using Base2BoolD =Base2StructD<bool>;
using Base2PenD =Base2StructD<struct gmt_pen>;
using Base2FontD =Base2StructD<struct gmt_font>;
// Specialised convertors
// Convertor with default value for gmt_coord. Added constructor for creating default value from double argument
class Base2CoordD: public DefaultConverter<Base2Coord>
{
static ValueType Def(double d)
{
ValueType v;
std::string fake;
v.Convert(d,fake);
return v;
}
public:
using ValueType=DefaultConverter<Base2Coord>::ValueType;
Base2CoordD(double d):DefaultConverter<Base2Coord>(Def(d)) {}
};
// Convertor with default value for gmt_color. Added constructor for creating default value from double argument
class Base2ColorD: public DefaultConverter<Base2Color>
{
static ValueType Def(double d)
{
ValueType v;
std::string fake;
v.Convert(d,fake);
return v;
}
public:
using ValueType=DefaultConverter<Base2Color>::ValueType;
Base2ColorD(double d):DefaultConverter<Base2Color>(Def(d)) {}
};
// Convertor with default value for gmt_dash. Default value is always no dash.
class Base2DashD: public DefaultConverter<Base2Dash>
{
static ValueType Def()
{
ValueType v;
v.Clear();
return v;
}
public:
using ValueType=DefaultConverter<Base2Dash>::ValueType;
Base2DashD():DefaultConverter<Base2Dash>(Def()) {}
};
// Convertors for double positive value
class Base2Pos: public Base2Double
{
public:
double Convert(const ObjectBase* ob, bool* res, std::string& err)
{
double t=Base2Double::Convert(ob,res,err);
if(res && t<=0)
{
*res=false;
err="Value mast be greater then zero";
}
return t;
}
};
using Base2PosD=DefaultConverter<Base2Pos>;
// Convertors for double non-negative value
class Base2NonNeg: public Base2Double
{
public:
double Convert(const ObjectBase* ob, bool* res, std::string& err)
{
double t=Base2Double::Convert(ob,res,err);
if(res && t<0)
{
*res=false;
err="Value mast be non-negative";
}
return t;
}
};
using Base2NonNegD=DefaultConverter<Base2NonNeg>;
// Convertors for double values which must be in some interval
template<int32_t Min, int32_t Max>
class Base2InRange: public Base2Double
{
public:
double Convert(const ObjectBase* ob, bool* res, std::string& err)
{
double t=Base2Double::Convert(ob,res,err);
if(res && (t<Min || t>Max) )
{
*res=false;
err="Value mast be in the interval from "+ToString(Min)+" to "+ToString(Max);
}
return t;
}
};
template<int32_t Min, int32_t Max>
using Base2InRangeD=DefaultConverter<Base2InRange<Min,Max> >;
// Definitions for colors
constexpr int32_t TransMax = 100;
constexpr int32_t RGBMax = 255;
constexpr int32_t HueMax = 360;
constexpr int32_t SVMax = 1;
constexpr int32_t CMYKMax = 100;
using Base2Trans = Base2InRange <0,TransMax>;
using Base2RGB = Base2InRange <0,RGBMax >;
using Base2Hue = Base2InRange <0,HueMax >;
using Base2SV = Base2InRange <0,SVMax >;
using Base2CMYK = Base2InRange <0,CMYKMax >;
using Base2TransD = Base2InRangeD<0,TransMax>;
using Base2RGBD = Base2InRangeD<0,RGBMax >;
using Base2HueD = Base2InRangeD<0,HueMax >;
using Base2SVD = Base2InRangeD<0,SVMax >;
using Base2CMYKD = Base2InRangeD<0,CMYKMax >;
// Conversion from List to GMTRegion
/*
Input:
1) One argument, Region. Return copy of this argument.
2) One argument, case insensitive string "global180" (global domain -R-180/180/-90/90), "global360" (global domain -R0/360/-90/90) or "global" (synonym of "global360").
3) One argument, list. Recursively calling GMT_Region.
4) Pairs list. Names are xb, xe, yb, ye, and, optionally, type="bbox|nobbox|global180|global360|global". Names are case sensitive, values can be Int, Real, String or GMTCoord.
If pair with name region (r) exists in list, when recursively calling GMT_Region on the value of this parameter, when modify it with specified parameters.
If argument with type Region exists in list, when copy it and modify with specified parameters.
5) 4 or 5 parameters. Fifth parameter can be "bbox" or "nobbox" (default). If fifth parameter is string "bbox", when first four parameters interprets as xb,yb,xe,ye, else as xb,xe,yb,ye.
*/
template<>
class Convert2Struct<struct gmt_region, ObjectList>
{
public:
struct gmt_region operator()(const ObjectList* input, bool* issuc, std::string& err) const
{
struct gmt_region r;
bool suc=true;
if(1==input->Size()) // Cases 1, 2 and 3
{
Base2Region region;
r=region.Convert(input->At(0),&suc,err);
if(!suc) goto fail; // Conversion failed
return r;
}
// Case 4
{
ONFPar<Base2Region,ObjectGMTRegion> region("region");
ONPar<Base2StringD> type("type","nobbox");
ONPar<Base2Coord> xb("xb"),xe("xe"),yb("yb"),ye("ye");
{ParseNamedParameters params(input,type,region,xb,xe,yb,ye); if(!params) {err=params.Error(); goto fail;} } // Fail to parse by variant 4
bool typeexist=type.Exist();
{
std::string t;
if(r.Convert(type,t)) return r; // type is one of "global*" string, we can return, because upd is irrelevant
}
bool upd=region.Exist();
if(!(typeexist || xb.Exist() || xe.Exist() || yb.Exist() || ye.Exist() || upd)) goto case5; // No named parameters, no update mode, possibly case 5
struct gmt_coord cxb,cxe,cyb,cye;
bool isbbox=upd?(gmt_region::BBOX==r.type):false;
if(typeexist)
{
if("bbox"==type.Value()) isbbox=true;
if("nobbox"==type.Value()) isbbox=false;
if("bbox"!=type.Value() && "nobbox"!=type.Value()) goto fail; // Unknown type
}
if(upd)
{
cxb=xb.Exist()?xb:region->xb;
cxe=xe.Exist()?xe:region->xe;
cyb=yb.Exist()?yb:region->yb;
cye=ye.Exist()?ye:region->ye;
}
else
{
suc=suc && xb.Exist() && xe.Exist() && yb.Exist() && ye.Exist(); // In "new" mode all parameters must exists
cxb=xb; cxe=xe; cyb=yb; cye=ye;
}
if(!suc) {err="To specify region all four coordinates must be set"; goto fail;} // Something wrong
if(!r.Convert(cxb,cxe,cyb,cye,isbbox)) {err="Error conversion of coordinates to region"; goto fail;} // Conversion failed
return r; // Case 3 with all parameters
}
case5:
// Case 5
{
OPosPar<Base2StringD> type("type","nobbox");
RPosPar<Base2Coord> p1("p1"),p2("p2"),p3("p3"),p4("p4");
ParsePositionalParameters params(input,p1,p2,p3,p4,type);
if(!params) {err=params.Error(); goto fail;} // Fail to parse by variant 5
bool isbbox=("bbox"==type.Value());
if("bbox"!=type.Value() && "nobbox"!=type.Value()) {err="Fifth parameter can be \"bbox\" or \"nobbox\" only"; goto fail;} // Unknown fifth parameter
if(!r.Convert(p1,isbbox?p3:p2,isbbox?p2:p3,p4,isbbox)) {err="Error conversion of coordinates to region"; goto fail;} // Conversion failed
return r; // Case 4 with all parameters
}
fail:
*issuc=false;
return r; // Something go wrong
}
};
// Converting List to GMTProjection
/*
Input:
1) One argument, Projection. Return copy of this argument.
2) One argument, list. Recursively calling GMT_Projection.
3) Pairs list. Names are projtype (type) (string), region (GMTRegion, string or list, which can be converted to GMTRegion),
width (or height) in centimeters (may be absent, default width is 10 cm) and projection-dependent parameters.
Pair with name region may absent, in this case search in list and using as region object with ObjectGMTRegion type.
If pair with name projection (p or proj) exists in list, when recursively calling GMT_Projection on the value of this parameter, when modify it with specified parameters.
If argument with type Projection exists in list, when copy it and modify with specified parameters.
4) 3 or more parameters. First parameter is projection type (string), second - width in centimeters,
third - region (any type which can be converted to GMTRegion), other parameters are projection-dependent.
Height can be set only in form 3.
*/
template<>
class Convert2Struct<struct gmt_projection, ObjectList>
{
public:
struct gmt_projection operator()(const ObjectList* input, bool* issuc, std::string& err) const
{
struct gmt_projection p;
if(1==input->Size()) // Cases 1, and 2
{
Base2Proj proj;
bool suc=true;
p=proj.Convert(input->At(0),&suc,err);
if(!suc) goto fail; // Conversion failed
return p;
}
// Case 3
{
bool upd;
bool changetype=false;
ONFPar<Base2Proj,ObjectGMTProjection> proj("projection");
ONFPar<Base2Region,ObjectGMTRegion> region("region");
ONPar<Base2StringD> type("[projection][( |_)]t[ype]","mercator");
ONPar<Base2PosD> width("width",gmt_projection::default_width),height("height",gmt_projection::default_width);
{ParseNamedParameters params(input,type,region,proj,width,height); if(!params) {err=params.Error(); goto fail;}} // Fail to parse by variant 3
upd=proj.Exist();
if(upd) p=proj;
if(!(type.Exist() || upd)) goto case4; // No named parameter, not update mode, go to case 4
// Check projection type
if(type.Exist())
{
if(!p.SetType(type,err)) goto fail; // Incorrect projection type
changetype=true;
}
// Region required in new mode
if(!(region.Exist() || upd)) {err="Region must be specified"; goto fail;}
if(region.Exist()) p.region=region;
ONPar<Base2CoordD> cmer("c[entral][( |_)]meridian",(p.region.xb+p.region.xe)*0.5), stpar("s[tandar(t|d)][( |_)]parallel",(p.region.yb+p.region.ye)*0.5);
ONPar<Base2CoordD> clon("(l[ongitude][( |_)][of( |_)]center|c[enter][( |_)]longitude)",(p.region.xb+p.region.xe)*0.5);
ONPar<Base2CoordD> clat("(l[atitude][( |_)][of( |_)]center|c[enter][( |_)]latitude)",(p.region.yb+p.region.ye)*0.5);
// Get parameters of projection
switch(p.proj)
{
case(gmt_projection::XY): // x Parameter height (by default equal width)
{
// Width is processing later
if(height.Exist()) p.x.height=height;
else if(changetype) p.x.height=width; // By default, height equal width
break;
}
case(gmt_projection::CYL_EQU): // q Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region)
{
{ParseNamedParameters apar(input,cmer,stpar); if(!apar) {err=apar.Error(); goto fail;}} // Fail to parse additional parameters
if( cmer.Exist() || changetype) p.q.cmer =cmer;
if(stpar.Exist() || changetype) p.q.stpar=stpar;
break;
}
case(gmt_projection::MERCATOR): // m Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region)
{
{ParseNamedParameters apar(input,cmer,stpar); if(!apar) {err=apar.Error(); goto fail;}} // Fail to parse additional parameters
if( cmer.Exist() || changetype) p.m.cmer =cmer;
if(stpar.Exist() || changetype) p.m.stpar=stpar;
break;
}
case(gmt_projection::TRANSMERCATOR): // t Parameters: central meridian (cmer, default is center of region), latitude of origin (orlat, default is 0.0), scale factor (scale, default is 1.0)
{
ONPar<Base2PosD> scale("scale",1.0);
ONPar<Base2CoordD> orlat("(l[atitude][( |_)][of( |_)]origin|o[rigin][( |_)]latitude)",0.0);
{ParseNamedParameters apar(input,cmer,scale,orlat); if(!apar) {err=apar.Error(); goto fail;}} // Fail to parse additional parameters
if( cmer.Exist() || changetype) p.t.cmer =cmer;
if(orlat.Exist() || changetype) p.t.orlat=orlat;
if(scale.Exist() || changetype) p.t.scale=scale;
break;
}
case(gmt_projection::OBLIQMERCATOR): // o Parameters: longitude of projection center (clon, default is center of region), latitude of projection center (clat, default is center of region). Other parameters may form one of three combinations and doesn't have default values. 1) Azimuth of the oblique equator (azimuth). 2) Longitude and latitude of second point on oblique equator (eqlon, eqlat). 3) Longitude and latitude of projection pole (polelon, polelat).
{
ONPar<Base2Coord> eqlon("(l[ongitude][( |_)][of( |_)]equator|e[quator][( |_)]longitude)"), eqlat("(l[atitude][( |_)][of( |_)]equator|e[quator][( |_)]latitude)");
ONPar<Base2Coord> polelon("(l[ongitude][( |_)][of( |_)]pole|p[ole][( |_)]longitude)"), polelat("(l[atitude][( |_)][of( |_)]pole|p[ole][( |_)]latitude)");
ONPar<Base2Coord> azimuth("azimuth");
{ParseNamedParameters apar(input,clon,clat,eqlon,eqlat,polelon,polelat,azimuth); if(!apar) {err=apar.Error(); goto fail;}} // Fail to parse additional parameters
if(changetype) p.o.type=gmt_projection::OType::NOTDEF;
if(clon.Exist() || changetype) p.o.clon=clon;
if(clat.Exist() || changetype) p.o.clat=clat;
// Check conflict in parameters
if(
(azimuth.Exist() && (eqlon.Exist() || eqlat.Exist() || polelon.Exist() || polelat.Exist() ) ) ||
( (eqlon.Exist() || eqlat.Exist()) && (azimuth.Exist() || polelon.Exist() || polelat.Exist() ) ) ||
( (polelon.Exist() || polelat.Exist()) && (azimuth.Exist() || eqlon.Exist() || eqlat.Exist() ) )
)
{
err="Conflict in parameters for oblique Mercator projection, must specify only one variant: 1) azimuth of oblique equator, or 2) longitude and latitude of point on oblique equator, or 3) longitude and latitude of projection pole";
goto fail; // Insufficient data for this projection
}
// Variant 1
if(azimuth.Exist())
{
p.o.type=gmt_projection::OType::A;
p.o.azimuth=azimuth;
}
// Variant 2
if(eqlon.Exist() && eqlat.Exist())
{
p.o.type=gmt_projection::OType::B;
p.o.eqlon=eqlon;
p.o.eqlat=eqlat;
}
// Variant 3
if(polelon.Exist() && polelat.Exist())
{
p.o.type=gmt_projection::OType::C;
p.o.polelon=polelon;
p.o.polelat=polelat;
}
// Special processing of update case
if(!changetype)
{
if(eqlon.Exist() && !eqlat.Exist())
{
if(gmt_projection::OType::B!=p.o.type) {err="Insufficient parameters for oblique Mercator projection, must specify latitude of point on oblique equator"; goto fail;}
else p.o.eqlon=eqlon;
}
if(!eqlon.Exist() && eqlat.Exist())
{
if(gmt_projection::OType::B!=p.o.type) {err="Insufficient parameters for oblique Mercator projection, must specify longitude of point on oblique equator"; goto fail;}
else p.o.eqlat=eqlat;
}
if(polelon.Exist() && !polelat.Exist())
{
if(gmt_projection::OType::C!=p.o.type) {err="Insufficient parameters for oblique Mercator projection, must specify latitude of projection pole"; goto fail;}
else p.o.polelon=polelon;
}
if(!polelon.Exist() && polelat.Exist())
{
if(gmt_projection::OType::C!=p.o.type) {err="Insufficient parameters for oblique Mercator projection, must specify longitude of projection pole"; goto fail;}
else p.o.polelat=polelat;
}
}
if(gmt_projection::OType::NOTDEF==p.o.type)
{
err="Insufficient parameters for oblique Mercator projection, must specify azimuth of oblique equator, or longitude and latitude of point on oblique equator, or longitude and latitude of projection pole";
goto fail; // Insufficient data for this projection
}
break;
}
case(gmt_projection::CASSINI): // c Parameters: longitude (clon, default is center of region) and latitude (clat, default is center of region) of central point.
{
{ParseNamedParameters apar(input,clon,clat); if(!apar) {err=apar.Error(); goto fail;}} // Fail to parse additional parameters
if(clon.Exist() || changetype) p.c.clon =clon;
if(clat.Exist() || changetype) p.c.clat =clat;
break;
}
case(gmt_projection::CYL_EQA): // y Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region)
{
{ParseNamedParameters apar(input,cmer,stpar); if(!apar) {err=apar.Error(); goto fail;}} // Fail to parse additional parameters
if( cmer.Exist() || changetype) p.y.cmer =cmer;
if(stpar.Exist() || changetype) p.y.stpar=stpar;
break;
}
case(gmt_projection::MILLER): // j Parameters: central meridian (cmer, default is center of region)
{
{ParseNamedParameters apar(input,cmer); if(!apar) {err=apar.Error(); goto fail;}} // Fail to parse additional parameters
if(cmer.Exist() || changetype) p.j.cmer=cmer;
break;
}
case(gmt_projection::CYL_STERE): // cyl_stere Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region)
{
{ParseNamedParameters apar(input,cmer,stpar); if(!apar) {err=apar.Error(); goto fail;}} // Fail to parse additional parameters
if( cmer.Exist() || changetype) p.cyl_stere.cmer =cmer;
if(stpar.Exist() || changetype) p.cyl_stere.stpar=stpar;
break;
}
default: {err="Code error, unknown projection"; goto fail;} // Unknown projection
}
// Width and height manipulations
if(width.Exist() && height.Exist() && gmt_projection::XY!=p.proj) {err="For geographic projections only one of width or height can be specified"; goto fail;}
if(!upd) p.width=width;
{
if(width.Exist()) // width is present
{
p.width=width;
if(!ProjectionRealSize(p)) goto failrealsize; // Something go wrong with determining real dimensions
}
else if(height.Exist()) // width is not present, but height is present
{
if(gmt_projection::XY==p.proj) p.width=height; // For decart projection we use height as width if width is not specified
if(!ProjectionRealSize(p,height)) goto failrealsize;
}
else // No width, no height, using default or old width
if(!ProjectionRealSize(p)) goto failrealsize; // Something go wrong with determining real dimensions
}
return p; // All parameters setted
failrealsize:
err="Can't determine real size of projection "+p.Value();
goto fail;
}
case4:
// Case 4
{
RPosPar<Base2String> type("projection type");
RPosPar<Base2Pos> width("width");
RPosPar<Base2Region> region("region");
{ParsePositionalParameters params(input,0,3,type,width,region); if(!params) {err=params.Error(); goto fail;}} // Fail to parse by variant 4
if(!p.SetType(type,err)) goto fail; // Incorrect projection type
p.width=width;
p.region=region;
// Projection specific parameters
switch(p.proj)
{
case(gmt_projection::XY): // x Parameter 4 is height (by default equal width)
{
OPosPar<Base2PosD> height("height",p.width);
{ParsePositionalParameters params(input,3,input->Size(),height); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
p.x.height=height;
break;
}
case(gmt_projection::CYL_EQU): // q Parameters: 4 is central meridian (default is center of region), 5 is standart parallel (default is center of region)
{
OPosPar<Base2CoordD> cmer("central meridian",(p.region.xb+p.region.xe)*0.5);
OPosPar<Base2CoordD> stpar("standart parallel",(p.region.yb+p.region.ye)*0.5);
{ParsePositionalParameters params(input,3,input->Size(),cmer,stpar); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
p.q.cmer=cmer; p.q.stpar=stpar;
break;
}
case(gmt_projection::MERCATOR): // m Parameters: 4 is central meridian (default is center of region), 5 is standart parallel (default is center of region)
{
OPosPar<Base2CoordD> cmer("central meridian",(p.region.xb+p.region.xe)*0.5);
OPosPar<Base2CoordD> stpar("standart parallel",(p.region.yb+p.region.ye)*0.5);
{ParsePositionalParameters params(input,3,input->Size(),cmer,stpar); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
p.m.cmer=cmer; p.m.stpar=stpar;
break;
}
case(gmt_projection::TRANSMERCATOR): // t Parameters: 4 is central meridian (default is center of region), 5 is latitude of origin (default is 0.0), 6 is scale factor (default is 1.0)
{
OPosPar<Base2CoordD> cmer("central meridian",(p.region.xb+p.region.xe)*0.5);
OPosPar<Base2CoordD> orlat("latitude of origin",0.0);
OPosPar<Base2PosD> scale("scale",1.0);
{ParsePositionalParameters params(input,3,input->Size(),cmer,orlat,scale); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
p.t.cmer=cmer; p.t.orlat=orlat; p.t.scale=scale;
break;
}
case(gmt_projection::OBLIQMERCATOR): // o There is no default values for this projection. Parameters: 4 is subtype ("a" or "azimuth", "b" or "equator", "c" or "pole"), 5 is longitude of projection center, 6 is latitude of projection center. Other parameters may form one of three combinations. 1) 7 is azimuth of the oblique equator. 2) Longitude and latitude of second point on oblique equator (7, 8). 3) Longitude and latitude of projection pole (7, 8).
{
RPosPar<Base2String> pstype("subtype");
RPosPar<Base2Coord> clon("longitude of center");
RPosPar<Base2Coord> clat("latitude of center");
{ParsePositionalParameters params(input,3,6,pstype,clon,clat); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
TemplateComparator a("azimuth"),b("(b|equator)"),c("(c|pole)");
std::string stype=pstype;
tolower(stype);
p.o.clon=clon; p.o.clat=clat;
p.o.type=gmt_projection::OType::NOTDEF;
// Variant 1
if(a.Compare(stype))
{
RPosPar<Base2Coord> azimuth("azimuth");
{ParsePositionalParameters params(input,6,input->Size(),azimuth); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
p.o.type=gmt_projection::OType::A;
p.o.azimuth=azimuth;
}
if(b.Compare(stype))
{
RPosPar<Base2Coord> eqlon("longitude of equator");
RPosPar<Base2Coord> eqlat("latitude of equator");
{ParsePositionalParameters params(input,6,input->Size(),eqlon,eqlat); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
p.o.type=gmt_projection::OType::B;
p.o.eqlon=eqlon;
p.o.eqlat=eqlat;
}
if(c.Compare(stype))
{
RPosPar<Base2Coord> polelon("longitude of pole");
RPosPar<Base2Coord> polelat("latitude of pole");
{ParsePositionalParameters params(input,6,input->Size(),polelon,polelat); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
p.o.type=gmt_projection::OType::C;
p.o.polelon=polelon;
p.o.polelat=polelat;
}
if(gmt_projection::OType::NOTDEF==p.o.type) {err="String "+pstype.Value()+" is invalid subtype for oblique Mercator projection. The valid subtypes are azimuth, equator or pole"; goto fail;}
break;
}
case(gmt_projection::CASSINI): // c Parameters: longitude (4, default is center of region) and latitude (5, default is center of region) of central point.
{
OPosPar<Base2CoordD> clon("longitude of center",(p.region.xb+p.region.xe)*0.5);
OPosPar<Base2CoordD> clat("latitude of center",(p.region.yb+p.region.ye)*0.5);
{ParsePositionalParameters params(input,3,input->Size(),clon,clat); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
p.c.clon=clon; p.c.clat=clat;
break;
}
case(gmt_projection::CYL_EQA): // y Parameters: central meridian (4, default is center of region), standart parallel (5, default is center of region)
{
OPosPar<Base2CoordD> cmer("central meridian",(p.region.xb+p.region.xe)*0.5);
OPosPar<Base2CoordD> stpar("standart parallel",(p.region.yb+p.region.ye)*0.5);
{ParsePositionalParameters params(input,3,input->Size(),cmer,stpar); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
p.y.cmer=cmer; p.y.stpar=stpar;
break;
}
case(gmt_projection::MILLER): // j Parameters: central meridian (4, default is center of region)
{
OPosPar<Base2CoordD> cmer("central meridian",(p.region.xb+p.region.xe)*0.5);
{ParsePositionalParameters params(input,3,input->Size(),cmer); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
p.j.cmer=cmer;
break;
}
case(gmt_projection::CYL_STERE): // cyl_stere Parameters: central meridian (3, default is center of region), standart parallel (4, default is center of region)
{
OPosPar<Base2CoordD> cmer("central meridian",(p.region.xb+p.region.xe)*0.5);
OPosPar<Base2CoordD> stpar("standart parallel",(p.region.yb+p.region.ye)*0.5);
{ParsePositionalParameters params(input,3,input->Size(),cmer,stpar); if(!params) {err=params.Error(); goto fail;}} // Fail to parse additional parameters
p.cyl_stere.cmer=cmer; p.cyl_stere.stpar=stpar;
break;
}
default: goto fail; // Unknown projection
}
if(ProjectionRealSize(p)) return p;
err="Can't determine real size of projection "+p.Value();
}
fail:
*issuc=false;
return p; // Something go wrong
}
};
// Converting List to GMTColor
/*
Input:
1) One argument, Color. Return copy of this argument.
2) One argument, list. Recursively calling GMT_Color.
3) Pairs list. Names are gray (grey), r(red),g(green),b(blue), h(hue),s(sat,saturation),v(value), c(cyan),m(magenta),y(yellow),k(black) and t(transp,transparency).
The different color models can't be mixed.
Gray, r, g, b are doubles in the range 0-255, h is double in the range 0-360, s and v are doubles in the range 0-1 and c, m, y, k and transparency are doubles in the range 0-100. Default value for all parameters is 0.
If pair with name color exists in list, when recursively calling GMT_Color on the value of this parameter, when modify it with specified parameters.
If argument with type Color exists in list, when copy it and modify with specified parameters. In both cases color model changing as specified by parameters.
4) One argument, interprets as gray.
5) Three arguments, interprets as r, g, b.
6) Four arguments, interprets as c, m, y, k.
Transparency or HSV model can be set only in form 3.
*/
template<>
class Convert2Struct<struct gmt_color, ObjectList>
{
public:
struct gmt_color operator()(const ObjectList* input, bool* issuc, std::string& err) const
{
struct gmt_color C;
if(1==input->Size()) // Cases 1, 2 and 4
{
Base2Color color;
bool suc=true;
C=color.Convert(input->At(0),&suc,err);
if(!suc) goto fail; // Conversion failed
return C;
}
// Case 3
{
ONFPar<Base2Color,ObjectGMTColor> color("color");
ONPar<Base2TransD> trans("transparency",0);
ONPar<Base2RGB> gray("gr(a|e)y"), r("red"), g("green"), b("blue");
ONPar<Base2Hue> h("hue");
ONPar<Base2SV> s("saturation"), v("value");
ONPar<Base2CMYK> c("cyan"), m("magenta"), y("yellow"), k("[blac]k");
{ParseNamedParameters params(input,color,trans,gray,r,g,b,h,s,v,c,m,y,k); if(!params) {err=params.Error(); goto fail;} } // Fail to parse by variant 3
bool cmodset=false;
bool upd=color.Exist();
if(!upd)
{
// default color is black
C.transparency=trans;
C.model=gmt_color::GRAY;
C.gray=0.0;
}
else C=color;
if(trans.Exist()) C.transparency=trans;
// GRAY
if(gray.Exist())
{
if(cmodset) {err="Can't select between different color models"; goto fail;} // Model already set
C.model=gmt_color::GRAY;
C.gray=gray; // Update mode ignored in this case
cmodset=true;
}
// RGB
if(r.Exist() || g.Exist() || b.Exist())
{
if(cmodset) {err="Can't select between different color models"; goto fail;} // Model already set
C.ToRGB();
if(r.Exist()) C.r=r;
if(g.Exist()) C.g=g;
if(b.Exist()) C.b=b;
cmodset=true;
}
// HSV
if(h.Exist() || s.Exist() || v.Exist())
{
if(cmodset) {err="Can't select between different color models"; goto fail;} // Model already set
C.ToHSV();
if(h.Exist()) C.hue=h;
if(s.Exist()) C.saturation=s;
if(v.Exist()) C.value=v;
cmodset=true;
}
// CMYK
if(c.Exist() || m.Exist() || y.Exist() || k.Exist())
{
if(cmodset) {err="Can't select between different color models"; goto fail;} // Model already set
C.ToCMYK();
if(c.Exist()) C.cyan=c;
if(m.Exist()) C.magenta=m;
if(y.Exist()) C.yellow=y;
if(k.Exist()) C.black=k;
cmodset=true;
}
if(cmodset || upd) return C; // Color created or updated
}
// Case 5
if(3==input->Size())
{
RPosPar<Base2RGB> r("red"), g("green"), b("blue");
ParsePositionalParameters params(input,r,g,b);
if(!params) {err=params.Error(); goto fail;} // Fail to parse by variant 5
C.model=gmt_color::RGB;
C.r=r; C.g=g; C.b=b;
return C;
}
// Case 6
if(4==input->Size())
{
RPosPar<Base2CMYK> c("cyan"), m("magenta"), y("yellow"), k("black");
ParsePositionalParameters params(input,c,m,y,k);
if(!params) {err=params.Error(); goto fail;} // Fail to parse by variant 6
C.model=gmt_color::CMYK;
C.cyan=c; C.magenta=m; C.yellow=y; C.black=k;
return C;
}
err="Incorrect number of arguments";
fail:
*issuc=false;
return C; // Something go wrong
}
};
// Converting List to GMTPen
/*
Input:
1) One argument, Pen. Return copy of this argument.
2) One argument, list. Recursively calling GMT_Pen.
3) Pairs list. Names are width (w), color(c), dash(f). Default values is 1 for width, black for color and solid for dash.
If pair with name pen(p) exists in list, when recursively calling GMT_Line on the value of this parameter, when modify it with specified parameters.
If argument with type Pen exists in list, when copy it and modify with specified parameters.
Instead of color and dash unnamed parameters with Color and Dash types may be used.
4) One argument, interprets as width of black solid line.
5) Two arguments, interprets as width and color of solid line.
6) Three arguments, interprets as width, color and dash.
*/
template<>
class Convert2Struct<struct gmt_pen, ObjectList>
{
public:
struct gmt_pen operator()(const ObjectList* input, bool* issuc, std::string& err) const
{
struct gmt_pen p;
if(1==input->Size()) // Cases 1, 2 and 4
{
Base2Pen pen;
bool suc=true;
p=pen.Convert(input->At(0),&suc,err);
if(!suc) goto fail; // Conversion failed
return p;
}
// Case 5 and 6
{
OPosPar<Base2NonNegD> width("width",gmt_pen::default_width);
OPosPar<Base2ColorD> color("color",0.0);
OPosPar<Base2DashD> dash("dash");
ParsePositionalParameters params(input,width,color,dash);
if(!params) goto case3; // May be case3 will work
p.color=color;
p.dash=dash;
p.dash.width=p.width=width;
return p;
}
// Case 3
case3:
{
bool upd;
ONFPar<Base2Pen,ObjectGMTPen> pen("pen");
ONFPar<Base2ColorD,ObjectGMTColor> color("color",0.0);
ONFPar<Base2DashD,ObjectGMTDash> dash("dash");
ONPar<Base2NonNegD> width("width",gmt_pen::default_width);
{ParseNamedParameters params(input,color,pen,dash,width); if(!params) {err=params.Error(); goto fail;} } // Fail to parse by variant 3
upd=pen.Exist();
if(upd) p=pen;
if(color.Exist() || !upd) p.color=color;
if(dash.Exist() || !upd) p.dash=dash;
if(width.Exist() || !upd) p.width=width;
p.dash.width=p.width;
if(color.Exist() || dash.Exist() || width.Exist() || upd) return p; // Pen created or updated
err="Specify at least one of color, dash or width";
}
fail:
*issuc=false;
return p; // Something go wrong
}
};
// Converting List to GMTFont
/*
Input:
1) One argument, Font. Return copy of this argument.
2) One argument, list. Recursively calling GMT_Font.
3) Pairs list. Names are size (s), family (f) and color(c). Default values is 12pt for size, Times-Roman for family and black for color.
If pair with name font exists in list, when recursively calling GMT_Font on the value of this parameter, when modify it with specified parameters.
If argument with type Font exists in list, when copy it and modify with specified parameters.
Instead of color unnamed parameter with Color type may be used.
4) One numeric argument, interprets as size of Times-Roman black font.
5) One string argument, interprets as [size][,family][,color] or [family][,color].
6) Two arguments, interprets as size and family of black font.
7) Three arguments, interprets as size, family and color.
*/
template<>
class Convert2Struct<struct gmt_font, ObjectList>
{
public:
struct gmt_font operator()(const ObjectList* input, bool* issuc, std::string& err) const
{
struct gmt_font f;
if(1==input->Size()) // Cases 1, 2, 4 and 5
{
Base2Font font;
bool suc=true;
f=font.Convert(input->At(0),&suc,err);
if(!suc) goto fail; // Conversion failed
return f;
}
// Case 6 and 7
{
OPosPar<Base2PosD> size("size",gmt_font::default_size);
OPosPar<Base2ColorD> color("color",0.0);
OPosPar<Base2StringD> family("family",gmt_font::default_family);
ParsePositionalParameters params(input,size,family,color);
if(!params) goto case3; // May be case3 will work
f.color=color;
f.size=size;
f.family=family;
return f;
}
// Case 3
case3:
{
ONFPar<Base2Font,ObjectGMTFont> font("f[on]t");
ONFPar<Base2ColorD,ObjectGMTColor> color("color",0);
ONFPar<Base2StringD,ObjectString> family("f[a]m[i]ly",gmt_font::default_family);
ONFPar<Base2PosD,ObjectReal> size("size",gmt_font::default_size);
{ParseNamedParameters params(input,font,color,family,size); if(!params) {err=params.Error(); goto fail;} } // Fail to parse by variant 3
bool upd=font.Exist();
if(upd) f=font;
if(color.Exist() || !upd) f.color =color;
if(family.Exist() || !upd) f.family=family;
if(size.Exist() || !upd) f.size =size;
if(color.Exist() || family.Exist() || size.Exist() || upd) return f; // Font created or updated
err="Specify at least one of size, family or color";
}
fail:
*issuc=false;
return f; // Something go wrong
}
};
// Template for generating GMTObject from ObjectList
template<class Struct>
const ObjectBase* GMT_Type(const ObjectList* input)
{
bool suc=true;
std::string err;
Struct s=Convert2Struct<Struct,ObjectList>()(input,&suc,err);
if(suc) return new ObjectGMTClass<Struct>(s);
else return new ObjectError("GMT function",err);
}
// Shift position of layer
const ObjectBase* GMT_LayerShift(const ObjectList* input);
// Draw frame with tics
const ObjectBase* GMT_DrawFrame(const ObjectList* input);
#endif