#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_Datadir(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 Convert2Struct { public: template 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 Convert2Struct > { public: template Struct operator()(const ObjectGMTClass* o, bool* suc, std::string& err, Args... args) const { return o->Data(); } // Never return error }; // Converting Int or Real to double template class Convert2Struct { public: double operator ()(const O* q, bool* suc, std::string& err) const {return q->Value();} }; // Converting String to double template<> class Convert2Struct { 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 Convert2Struct { public: bool operator ()(const O* q, bool* suc, std::string& err) const {return q->Value()!=0;} }; // Converting String to bool template<> class Convert2Struct { 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 { 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 BaseConverter { template class StructConvertor: public Convert2Struct {}; // Conversion type public: using ValueType=Struct; template ValueType Convert(const ObjectBase* ob, bool* res, std::string& err, Args... args) { OBTypeM 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 DefaultConverter: public B { public: using ValueType=typename B::ValueType; private: ValueType def; public: DefaultConverter() = delete; template DefaultConverter(const ValueType& d, Args... args):B(args...),def(d) {} template 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 TypeStorage; template<> class TypeStorage {public: typedef BaseConverter Converter;}; template<> class TypeStorage {public: typedef BaseConverter Converter;}; template<> class TypeStorage {public: typedef BaseConverter Converter;}; template<> class TypeStorage {public: typedef BaseConverter Converter;}; template<> class TypeStorage {public: typedef BaseConverter Converter;}; template<> class TypeStorage {public: typedef BaseConverter Converter;}; template<> class TypeStorage {public: typedef BaseConverter Converter;}; template<> class TypeStorage {public: typedef BaseConverter Converter;}; template<> class TypeStorage {public: typedef BaseConverter Converter;}; template<> class TypeStorage {public: typedef BaseConverter Converter;}; template<> class TypeStorage {public: typedef BaseConverter Converter;}; // Default ObjectList converter template class Convert2Struct { public: template 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::Converter a; s=a.Convert(o->At(0),suc,err,args...); return s; } }; // Template for simple aliases of convertors template using Base2Struct=typename TypeStorage::Converter; // Template for simple aliases of convertors with default values template using Base2StructD=DefaultConverter::Converter>; // Simple aliases of convertors using Base2Bool =Base2Struct; using Base2Double=Base2Struct; using Base2String=Base2Struct; using Base2Coord =Base2Struct; using Base2Region=Base2Struct; using Base2Proj =Base2Struct; using Base2Color =Base2Struct; using Base2Dash =Base2Struct; using Base2Pen =Base2Struct; using Base2Font =Base2Struct; using Base2Layer =Base2Struct; // Simple aliases of convertors with default values using Base2StringD=Base2StructD; using Base2DoubleD=Base2StructD; using Base2BoolD =Base2StructD; using Base2PenD =Base2StructD; using Base2FontD =Base2StructD; // Specialised convertors // Convertor with default value for gmt_coord. Added constructor for creating default value from double argument class Base2CoordD: public DefaultConverter { static ValueType Def(double d) { ValueType v; std::string fake; v.Convert(d,fake); return v; } public: using ValueType=DefaultConverter::ValueType; Base2CoordD(double d):DefaultConverter(Def(d)) {} }; // Convertor with default value for gmt_color. Added constructor for creating default value from double argument class Base2ColorD: public DefaultConverter { static ValueType Def(double d) { ValueType v; std::string fake; v.Convert(d,fake); return v; } public: using ValueType=DefaultConverter::ValueType; Base2ColorD(double d):DefaultConverter(Def(d)) {} }; // Convertor with default value for gmt_dash. Default value is always no dash. class Base2DashD: public DefaultConverter { static ValueType Def() { ValueType v; v.Clear(); return v; } public: using ValueType=DefaultConverter::ValueType; Base2DashD():DefaultConverter(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; // 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; // Convertors for double values which must be in some interval template class Base2InRange: public Base2Double { public: double Convert(const ObjectBase* ob, bool* res, std::string& err) { double t=Base2Double::Convert(ob,res,err); if(res && (tMax) ) { *res=false; err="Value mast be in the interval from "+ToString(Min)+" to "+ToString(Max); } return t; } }; template using Base2InRangeD=DefaultConverter >; // 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 { 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 region("region"); ONPar type("type","nobbox"); ONPar 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 type("type","nobbox"); RPosPar 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 { 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 proj("projection"); ONFPar region("region"); ONPar type("[projection][( |_)]t[ype]","mercator"); ONPar 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 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 clon("(l[ongitude][( |_)][of( |_)]center|c[enter][( |_)]longitude)",(p.region.xb+p.region.xe)*0.5); ONPar 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 scale("scale",1.0); ONPar 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 eqlon("(l[ongitude][( |_)][of( |_)]equator|e[quator][( |_)]longitude)"), eqlat("(l[atitude][( |_)][of( |_)]equator|e[quator][( |_)]latitude)"); ONPar polelon("(l[ongitude][( |_)][of( |_)]pole|p[ole][( |_)]longitude)"), polelat("(l[atitude][( |_)][of( |_)]pole|p[ole][( |_)]latitude)"); ONPar 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 type("projection type"); RPosPar width("width"); RPosPar 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 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 cmer("central meridian",(p.region.xb+p.region.xe)*0.5); OPosPar 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 cmer("central meridian",(p.region.xb+p.region.xe)*0.5); OPosPar 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 cmer("central meridian",(p.region.xb+p.region.xe)*0.5); OPosPar orlat("latitude of origin",0.0); OPosPar 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 pstype("subtype"); RPosPar clon("longitude of center"); RPosPar 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 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 eqlon("longitude of equator"); RPosPar 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 polelon("longitude of pole"); RPosPar 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 clon("longitude of center",(p.region.xb+p.region.xe)*0.5); OPosPar 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 cmer("central meridian",(p.region.xb+p.region.xe)*0.5); OPosPar 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 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 cmer("central meridian",(p.region.xb+p.region.xe)*0.5); OPosPar 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 { 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 color("color"); ONPar trans("transparency",0); ONPar gray("gr(a|e)y"), r("red"), g("green"), b("blue"); ONPar h("hue"); ONPar s("saturation"), v("value"); ONPar 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 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 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 { 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 width("width",gmt_pen::default_width); OPosPar color("color",0.0); OPosPar 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 pen("pen"); ONFPar color("color",0.0); ONFPar dash("dash"); ONPar 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 { 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 size("size",gmt_font::default_size); OPosPar color("color",0.0); OPosPar 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 font("f[on]t"); ONFPar color("color",0); ONFPar family("f[a]m[i]ly",gmt_font::default_family); ONFPar 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 const ObjectBase* GMT_Type(const ObjectList* input) { bool suc=true; std::string err; Struct s=Convert2Struct()(input,&suc,err); if(suc) return new ObjectGMTClass(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