diff --git a/modules/gmt/modgmt.cpp b/modules/gmt/modgmt.cpp index 222aa9c..558e382 100644 --- a/modules/gmt/modgmt.cpp +++ b/modules/gmt/modgmt.cpp @@ -28,14 +28,14 @@ int gmt_module_init(void* p) RegisterFunction("GET",Get); RegisterFunction("GET",Get); - RegisterFunction("Coord",GMT_Coord); - RegisterFunction("Region",GMT_Region); - RegisterFunction("Projection",GMT_Projection); - RegisterFunction("Color",GMT_Color); + RegisterFunction("Coord",GMT_Type); + RegisterFunction("Region",GMT_Type); + RegisterFunction("Projection",GMT_Type); + RegisterFunction("Color",GMT_Type); RegisterFunction("ColorGray",GMT_ColorGray); RegisterFunction("ColorRGB",GMT_ColorRGB); RegisterFunction("ColorHSV",GMT_ColorHSV); RegisterFunction("ColorCMYK",GMT_ColorCMYK); - RegisterFunction("Pen",GMT_Pen); + RegisterFunction("Pen",GMT_Type); return 0; } diff --git a/modules/gmt/modgmt_func.cpp b/modules/gmt/modgmt_func.cpp index 825caf1..b3c2b05 100644 --- a/modules/gmt/modgmt_func.cpp +++ b/modules/gmt/modgmt_func.cpp @@ -1,4 +1,3 @@ -#include "modgmt_internals.h" #include "modgmt_func.h" ObjectBase* GMT_Header(const ObjectList* input) @@ -11,1416 +10,6 @@ ObjectBase* GMT_Footer(const ObjectList* input) return new ObjectString(footer); } -template class Func, class... O> -class Base2Something: public OBTypeM -{ - Base2Something() = delete; - Base2Something(Base2Something&&) = delete; - Base2Something(Base2Something&) = delete; - - public: - Base2Something(const ObjectBase* arg):OBTypeM(arg) {}; - Base2Something(const ObjectList* input, const std::string& name):OBTypeM(input->Get(name)){}; - Base2Something(const ObjectList* input, const ObjectList::ListValues::size_type i):OBTypeM((iSize())?input->At(i):0){}; // Check index, because At is not safe - template - auto operator ()(bool* b, Args... args) const -> decltype(this->OBTypeM::template operator()(b, args...)) - { - if(!OBTypeM::operator bool()) *b=false; - return OBTypeM::operator ()(b,args...); - } -}; - -// Converting Int, Real or String to double -template -class DoubleConv -{ - public: - double operator ()(const O* q, bool* suc) const {return q->Value();} -}; -template<> -class DoubleConv -{ - public: - double operator ()(const ObjectString* q, bool* suc) const {double d=0; *suc=(*suc) && str2double(q->Value(),&d); return d;} -}; -typedef Base2Something Base2Double; - -// Additional policies to let GetDouble work with Base2Double -// We use rational representation of floating point number, because double type values not allowed as template parameter -template -class PDefaultVal -{ - public: - double operator()() const {return static_cast(num)/denum;} -}; - -class PMultiInputNames {}; - -// Special case for default value policy. This policy mast be last in policies list. -template -class GetDouble >: public Base2Double -{ - PDefaultVal p; - public: - GetDouble(GetDouble&&) = delete; - GetDouble(GetDouble&) = delete; - GetDouble() = delete; - GetDouble(const ObjectBase* arg):Base2Double(arg) {}; - GetDouble(const ObjectList* input, const std::string& name):Base2Double(input,name){}; - GetDouble(const ObjectList* input, const ObjectList::ListValues::size_type i):Base2Double(input,i){}; - double operator()(bool* suc) const - { - if(Exist()) return Base2Double::operator()(suc); - else return p(); - } -}; - -// Special case for multiple input names policy. This policy mast be last in policies list. -template<> -class GetDouble -{ - bool exist; - bool ok; - double val; - public: - GetDouble(GetDouble&&) = delete; - GetDouble(GetDouble&) = delete; - GetDouble() = delete; - template - GetDouble(const ObjectList* input, const std::string& name, Args... args):GetDouble(input,args...) - { - Base2Double a(input,name); - if(exist && a.Exist()) ok=false; - else if(a.Exist()) - { - exist=true; - ok=true; - val=a(&ok); - } - } - GetDouble(const ObjectList* input, const std::string& name) - { - Base2Double a(input,name); - exist=a.Exist(); - ok=true; - if(exist) val=a(&ok); - } - double operator()(bool* suc) const - { - if(!ok || !exist) *suc=false; - return val; - } - bool Exist() const {return exist;} -}; - -// Bottom of recursion -template<> -class GetDouble<>: public Base2Double -{ - public: - GetDouble(GetDouble&&) = delete; - GetDouble(GetDouble&) = delete; - GetDouble() = delete; - GetDouble(const ObjectBase* arg):Base2Double(arg) {}; - GetDouble(const ObjectList* input, const std::string& name):Base2Double(input,name){}; - GetDouble(const ObjectList* input, const ObjectList::ListValues::size_type i):Base2Double(input,i){}; - double operator()(bool* suc) const {return Base2Double::operator()(suc);} -}; - -// Converting Int, Real or String to GMTCoord -template class CoordConv -{ - public: - struct gmt_coord operator()(const O* o, bool* suc) const - { - struct gmt_coord c; *suc=(*suc) && c.Convert(o->Value()); return c; - } -}; -// Special case is GMTCoord -template<> -class CoordConv -{ - public: - struct gmt_coord operator()(const ObjectGMTCoord* o, bool* suc) const {return o->Data();} -}; -typedef Base2Something Base2Coord; - - -// Converting List or String to GMTRegion -template class RegionConv -{ - public: - struct gmt_region operator()(const O* o, bool* suc) const {*suc=false; return gmt_region();} // Must never used -}; -// Conversion from List -template<> -class RegionConv -{ - public: - // Cases see in description of GMT_Region function - struct gmt_region operator()(const ObjectList* input, bool* issuc) const - { - struct gmt_region r; - auto size=input->Size(); - - if(1==size) // Cases 1, 2 and 3 - { - const ObjectBase *arg=input->At(0); - OBType region(arg); - if(region) return r=region->Data(); // Case 1 - OBType string(arg); - if(string && r.Convert(string->Value())) return r; // Case 2 - OBType list(arg); - if(list) return r=operator()(list,issuc); // Case 3 - *issuc=false; - return r; // Conversion failed - } - - // Case 4 - { - bool upd=false; - - // Update case, arguments list contains parameter with type GMTRegion. - for(ObjectList::ListValues::size_type i=0;i region(input->At(i)); - if(region) - { - if(upd){*issuc=false; return r;} // We already have region to update - r=region->Data(); - upd=true; - } - } - - // Update case, arguments list contains parameter region. - if(0!=input->Get("region")) - { - if(upd) {*issuc=false; return r;} // We already have region to update - ObjectList* list=new ObjectList; - list->PushBack(input->Get("region")); - r=operator()(list,issuc); - delete list; - if(!*issuc) return r; // Fail to convert - upd=true; - } - - OBType type(input->Get("type")); - if(type && r.Convert(type->Value())) return r; // type is one of "global*" string, we can return, becuse upd is irrelevant - - Base2Coord ixb(input,"xb"), ixe(input,"xe"), iyb(input,"yb"), iye(input,"ye"); - bool suc=true; - struct gmt_coord xb=ixb(&suc),yb=iyb(&suc),xe=ixe(&suc),ye=iye(&suc); - bool isbbox=upd?(gmt_region::BBOX==r.type):false; - - if(type) - { - std::string s=type->Value(); - tolower(s); - if("bbox"==s) isbbox=true; - if("nobbox"==s) isbbox=false; - if("bbox"!=s && "nobbox"!=s) suc=false; // Unknown type - } - - if(upd) - { - if(!ixb.Exist()) xb=r.xb; - if(!ixe.Exist()) xe=r.xe; - if(!iyb.Exist()) yb=r.yb; - if(!iye.Exist()) ye=r.ye; - } - else suc=suc && ixb && ixe && iyb && iye; // In "new" mode all parameters must exists and have correct type - if(ixb.Exist()) xb=ixb(&suc); - if(ixe.Exist()) xe=ixe(&suc); - if(iyb.Exist()) yb=iyb(&suc); - if(iye.Exist()) ye=iye(&suc); - - if(suc) - { - if(!r.Convert(xb,xe,yb,ye,isbbox)) *issuc=false; - return r; // Case 3 with all parameters - } - } - - // Case 5 - if(4==size || 5==size) - { - bool isbbox=false; - if(5==size) - { - OBType type(input->At(4)); - if(!type) {*issuc=false; return r;} // Unknown fifth parameter - std::string str=type->Value(); - tolower(str); - if("bbox"!=str || "nobbox"!=str) {*issuc=false; return r;} // Unknown fifth parameter - if("bbox"==str) isbbox=true; - } - Base2Coord ixb(input,0), ixe(input,(isbbox?2:1)), iyb(input,(isbbox?1:2)), iye(input,3); - bool suc=true; - struct gmt_coord xb=ixb(&suc),yb=iyb(&suc),xe=ixe(&suc),ye=iye(&suc); - if(ixb && ixe && iyb && iye && suc) - { - if(!r.Convert(xb,xe,yb,ye,isbbox)) *issuc=false; - return r; // Case 4 with all parameters - } - } - *issuc=false; - return r; // Something go wrong - } -}; -// Conversion from String -template<> -class RegionConv -{ - public: - struct gmt_region operator()(const ObjectString* input, bool* issuc) const - { - struct gmt_region r; - if(!r.Convert(input->Value())) *issuc=false; - return r; - } -}; -// Special case is GMTRegion -template<> -class RegionConv -{ - public: - struct gmt_region operator()(const ObjectGMTRegion* o, bool* suc) const {return o->Data();} -}; -typedef Base2Something Base2Region; - - -// Converting List to GMTProjection -template class ProjConv -{ - public: - struct gmt_projection operator()(const O* o, bool* suc) const {*suc=false; return gmt_projection();} // Must never used -}; -// Conversion from List -template<> -class ProjConv -{ - public: - // Cases see in description of GMT_Projection function - struct gmt_projection operator()(const ObjectList* input, bool* issuc) const - { - struct gmt_projection p; - auto size=input->Size(); - - if(1==size) // Cases 1, and 2 - { - const ObjectBase *arg=input->At(0); - OBType proj(arg); - if(proj) return p=proj->Data(); // Case 1 - OBType list(arg); - if(list) return p=operator()(list,issuc); // Case 2 - goto fail; // Conversion failed - } - - // Case 3 - { - bool upd=false; - bool longisproj=false; // "projection" is good defined projection - bool shortisproj=false; // "proj" is good defined projection - bool changetype=false; // Projection type was specified in update mode. If new type is same as old type parameters of projection reset to default for this type. - - // Update case, arguments list contains parameter with type GMTProjection. - for(ObjectList::ListValues::size_type i=0;i proj(input->At(i)); - if(proj) - { - if(upd) goto fail; // Only one projection in list allowed - p=proj->Data(); - upd=true; - } - } - - // Update case, arguments list contains parameter proj or projection and this parameter is projection. - { - const ObjectBase* prg; - prg=input->Get("projection"); - if(0!=prg) - { - struct gmt_projection ps; - bool suc=true; - ObjectList* list=new ObjectList; - list->PushBack(prg->Copy()); - ps=operator()(list,&suc); - delete list; - if(suc) - { - if(upd) goto fail; // Already have projection to update - upd=true; - longisproj=true; - p=ps; - } - } - prg=input->Get("proj"); - if(0!=prg) - { - struct gmt_projection ps; - bool suc=true; - ObjectList* list=new ObjectList; - list->PushBack(prg->Copy()); - ps=operator()(list,&suc); - delete list; - if(suc) - { - if(upd) goto fail; // Already have projection to update - upd=true; - shortisproj=true; - p=ps; - } - } - } - - // Try to set type of projection - { - OBType type1(input->Get("projtype")); - OBType type2(input->Get("projection")); - OBType type3(input->Get("proj")); - if(!(type1.Exist() || type2.Exist() || type3.Exist() || upd)) goto case4; // No named parameter, not update mode, go to case 4 - // Check on redundant parameters - if(type1.Exist() && type2.Exist() && type3.Exist()) goto fail; // Too many variants - if(type1.Exist() && type2.Exist() && !type3.Exist() && !longisproj) goto fail; // "projtype" and "projection" exists, but "projection" is not parse to Projection - if(type1.Exist() && !type2.Exist() && type3.Exist() && !shortisproj) goto fail; // "projtype" and "proj" exists, but "proj" is not parse to Projection - if(!type1.Exist() && type2.Exist() && type3.Exist() && !(shortisproj || longisproj)) goto fail; // "projection" and "proj" exists, but no one is parse to Projection - // Check on type correctness - if(type1.Exist() && !type1) goto fail; // "projtype" is not String - if(type2.Exist() && !type2 && !longisproj) goto fail; // "projection" is not String or Projection - if(type3.Exist() && !type3 && !shortisproj) goto fail; // "proj" is not String or Projection - bool suc=false; - if(!(type1 || type2 || type3)) // No String parameter - { - if(!upd) goto fail; // No type - no projection - changetype=false; - } - else - { - changetype=true; - if(!suc && type1) suc=p.SetType(type1->Value()); - if(!suc && type2) suc=p.SetType(type2->Value()); - if(!suc && type3) suc=p.SetType(type3->Value()); - if(!suc) goto fail; // Incorrect projection type - } - } - - // We need the region - { - Base2Region reg(input,"region"); - // Try to find named parameter - if(reg.Exist()) - { - bool suc=true; - p.region=reg(&suc); - if(!suc) goto fail; // Conversion failed - } - else - { - bool suc=false; - for(ObjectList::ListValues::size_type i=0;iSize();i++) - { - OBType reg(input->At(i)); - if(reg) - { - if(suc) goto fail; // Too many regions - p.region=reg->Data(); - suc=true; - } - } - if(!suc && !upd) goto fail; // No such objects found in "new" mode - } - } - - // Get parameters of projection - switch(p.proj) - { - case(gmt_projection::XY): // x Parameter height (by default equal width) - { - Base2Double height(input,"height"); - if(height.Exist()) - { - bool suc=true; - p.x.height=height(&suc); - if(!suc) goto fail; // Parsing error - } - else - { - Base2Double width(input,"width"); - if(!width && changetype) p.x.height=gmt_projection::default_width; // We ignore case when parameter width exists but have wrong type. It will be handled later. - else - { - bool suc=true; - p.x.height=width(&suc); - if(!suc) goto fail; // Parsing error - } - } - break; - } - case(gmt_projection::CYL_EQU): // q Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region) - { - Base2Coord cmer(input,"cmer"), stpar(input,"stpar"); - if(cmer.Exist()) - { - bool suc=true; - p.q.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.q.cmer.Convert((p.region.xb+p.region.xe)*0.5); - if(stpar.Exist()) - { - bool suc=true; - p.q.stpar=stpar(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.q.stpar.Convert((p.region.yb+p.region.ye)*0.5); - break; - } - case(gmt_projection::MERCATOR): // m Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region) - { - Base2Coord cmer(input,"cmer"), stpar(input,"stpar"); - if(cmer.Exist()) - { - bool suc=true; - p.m.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.m.cmer.Convert((p.region.xb+p.region.xe)*0.5); - if(stpar.Exist()) - { - bool suc=true; - p.m.stpar=stpar(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.m.stpar.Convert((p.region.yb+p.region.ye)*0.5); - 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) - { - Base2Coord cmer(input,"cmer"), orlat(input,"orlat"); - Base2Double scale(input,"scale"); - if(cmer.Exist()) - { - bool suc=true; - p.t.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.t.cmer.Convert((p.region.xb+p.region.xe)*0.5); - if(orlat.Exist()) - { - bool suc=true; - p.t.orlat=orlat(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.t.orlat.Convert(0.0); - if(scale.Exist()) - { - bool suc=true; - p.t.scale=scale(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.t.scale=1.0; - 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). - { - Base2Coord clon(input,"clon"), clat(input,"clat"); - Base2Coord azimuth(input,"azimuth"), eqlon(input,"eqlon"), eqlat(input,"eqlat"), polelon(input,"polelon"), polelat(input,"polelat"); - if(changetype) p.o.type=gmt_projection::OType::NOTDEF; - if(clon.Exist()) - { - bool suc=true; - p.o.clon=clon(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.o.clon.Convert((p.region.xb+p.region.xe)*0.5); - if(clat.Exist()) - { - bool suc=true; - p.o.clat=clat(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.o.clat.Convert((p.region.yb+p.region.ye)*0.5); - // Variant 1 - if(azimuth) - { - p.o.type=gmt_projection::OType::A; - bool suc=true; - p.o.azimuth=azimuth(&suc); - if(!suc) goto fail; // Parsing error - } - if(eqlon && eqlat) - { - p.o.type=gmt_projection::OType::B; - bool suc=true; - p.o.eqlon=eqlon(&suc); - p.o.eqlat=eqlat(&suc); - if(!suc) goto fail; // Parsing error - } - if(polelon && polelat) - { - p.o.type=gmt_projection::OType::C; - bool suc=true; - p.o.polelon=polelon(&suc); - p.o.polelat=polelat(&suc); - if(!suc) goto fail; // Parsing error - } - if(gmt_projection::OType::NOTDEF==p.o.type) 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. - { - Base2Coord clon(input,"clon"), clat(input,"clat"); - if(clon.Exist()) - { - bool suc=true; - p.c.clon=clon(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.c.clon.Convert((p.region.xb+p.region.xe)*0.5); - if(clat.Exist()) - { - bool suc=true; - p.c.clat=clat(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.c.clat.Convert((p.region.yb+p.region.ye)*0.5); - break; - } - case(gmt_projection::CYL_EQA): // y Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region) - { - Base2Coord cmer(input,"cmer"), stpar(input,"stpar"); - if(cmer.Exist()) - { - bool suc=true; - p.y.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.y.cmer.Convert((p.region.xb+p.region.xe)*0.5); - if(stpar.Exist()) - { - bool suc=true; - p.y.stpar=stpar(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.y.stpar.Convert((p.region.yb+p.region.ye)*0.5); - break; - } - case(gmt_projection::MILLER): // j Parameters: central meridian (cmer, default is center of region) - { - Base2Coord cmer(input,"cmer"); - if(cmer.Exist()) - { - bool suc=true; - p.j.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.j.cmer.Convert((p.region.xb+p.region.xe)*0.5); - 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) - { - Base2Coord cmer(input,"cmer"), stpar(input,"stpar"); - if(cmer.Exist()) - { - bool suc=true; - p.cyl_stere.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.cyl_stere.cmer.Convert((p.region.xb+p.region.xe)*0.5); - if(stpar.Exist()) - { - bool suc=true; - p.cyl_stere.stpar=stpar(&suc); - if(!suc) goto fail; // Parsing error - } - else if(changetype) p.cyl_stere.stpar.Convert((p.region.yb+p.region.ye)*0.5); - break; - } - default: goto fail; // Unknown projection - } - - // Try to find width parameter - if(!upd) p.width=p.default_width; - { - Base2Double w(input,"width"),h(input,"height"); - bool suc=true; - if(w.Exist()) // width is present - { - p.width=w(&suc); - if(!suc) goto fail; // Parameter width exists, but can't be converted to double - if(!ProjectionRealSize(p)) goto fail; // Something go wrong with determining real dimensions - } - else if(h.Exist()) // width is not present, but height is present - { - double hval=h(&suc); - if(!suc) goto fail; // Parameter height exists, but can't be converted to double - if(gmt_projection::XY==p.proj) p.width=hval; // For decart projection we use height as width if width is not specified - if(!ProjectionRealSize(p,hval)) goto fail; // Something go wrong with determining real dimensions - } - else // No width, no height, using default or old width - if(!ProjectionRealSize(p)) goto fail; // Something go wrong with determining real dimensions - } - return p; // All parameters setted - } - - case4: - - // Case 4 - if(size>=3) - { - // First argument, try to set type of projection - { - OBType type(input->At(0)); - if(!type) goto fail; // No type - no projection - if(!p.SetType(type->Value())) goto fail; // Unknown type - no projection - } - // Second argument, set up width - { - Base2Double w(input,1); - bool suc=true; - p.width=w(&suc); - if(!suc) goto fail; // Conversion failed, no width - } - // Third argument, set up region - { - Base2Region reg(input,2); - bool suc=true; - p.region=reg(&suc); - if(!suc) goto fail; // Conversion failed, no region - } - - // Projection specific parameters - switch(p.proj) - { - case(gmt_projection::XY): // x Parameter 4 is height (by default equal width) - { - Base2Double height(input,3); - if(height.Exist()) - { - bool suc=true; - p.x.height=height(&suc); - if(!suc) goto fail; // Parsing error - } - else p.x.height=p.width; - if(size>4) goto fail; // Unknown parameter(s) - 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) - { - Base2Coord cmer(input,3), stpar(input,4); - if(cmer.Exist()) - { - bool suc=true; - p.q.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else p.q.cmer.Convert((p.region.xb+p.region.xe)*0.5); - if(stpar.Exist()) - { - bool suc=true; - p.q.stpar=stpar(&suc); - if(!suc) goto fail; // Parsing error - } - else p.q.stpar.Convert((p.region.yb+p.region.ye)*0.5); - if(size>5) goto fail; // Unknown parameter(s) - 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) - { - Base2Coord cmer(input,3), stpar(input,4); - if(cmer.Exist()) - { - bool suc=true; - p.m.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else p.m.cmer.Convert((p.region.xb+p.region.xe)*0.5); - if(stpar.Exist()) - { - bool suc=true; - p.m.stpar=stpar(&suc); - if(!suc) goto fail; // Parsing error - } - else p.m.stpar.Convert((p.region.yb+p.region.ye)*0.5); - if(size>5) goto fail; // Unknown parameter(s) - 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) - { - Base2Coord cmer(input,3), orlat(input,4); - Base2Double scale(input,5); - if(cmer.Exist()) - { - bool suc=true; - p.t.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else p.t.cmer.Convert((p.region.xb+p.region.xe)*0.5); - if(orlat.Exist()) - { - bool suc=true; - p.t.orlat=orlat(&suc); - if(!suc) goto fail; // Parsing error - } - else p.t.orlat.Convert(0.0); - if(scale.Exist()) - { - bool suc=true; - p.t.scale=scale(&suc); - if(!suc) goto fail; // Parsing error - } - else p.t.scale=1.0; - if(size>6) goto fail; // Unknown parameter(s) - 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). - { - if(size<4) goto fail; // Insufficient data for this projection - OBType stype(input->At(3)); - if(!stype) goto fail; // Incorrect parameter type - std::string subtype=stype->Value(); - tolower(subtype); - Base2Coord clon(input,4), clat(input,5); - Base2Coord azimuth(input,6), eqlon(input,6), eqlat(input,7), polelon(input,6), polelat(input,7); - p.o.type=gmt_projection::OType::NOTDEF; - { - bool suc=true; - p.o.clon=clon(&suc); - p.o.clat=clat(&suc); - if(!suc) goto fail; // Parsing error - } - // Variant 1 - if("a"==subtype || "azimuth"==subtype) - { - p.o.type=gmt_projection::OType::A; - bool suc=true; - p.o.azimuth=azimuth(&suc); - if(!suc) goto fail; // Parsing error - if(size>7) goto fail; // Unknown parameter(s) - } - if("b"==subtype || "equator"==subtype) - { - p.o.type=gmt_projection::OType::B; - bool suc=true; - p.o.eqlon=eqlon(&suc); - p.o.eqlat=eqlat(&suc); - if(!suc) goto fail; // Parsing error - if(size>8) goto fail; // Unknown parameter(s) - } - if("c"==subtype || "pole"==subtype) - { - p.o.type=gmt_projection::OType::C; - bool suc=true; - p.o.polelon=polelon(&suc); - p.o.polelat=polelat(&suc); - if(!suc) goto fail; // Parsing error - if(size>8) goto fail; // Unknown parameter(s) - } - if(gmt_projection::OType::NOTDEF==p.o.type) goto fail; // Insufficient data for this projection - 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. - { - Base2Coord clon(input,3), clat(input,4); - if(clon.Exist()) - { - bool suc=true; - p.c.clon=clon(&suc); - if(!suc) goto fail; // Parsing error - } - else p.c.clon.Convert((p.region.xb+p.region.xe)*0.5); - if(clat.Exist()) - { - bool suc=true; - p.c.clat=clat(&suc); - if(!suc) goto fail; // Parsing error - } - else p.c.clat.Convert((p.region.yb+p.region.ye)*0.5); - if(size>5) goto fail; // Unknown parameter(s) - break; - } - case(gmt_projection::CYL_EQA): // y Parameters: central meridian (4, default is center of region), standart parallel (5, default is center of region) - { - Base2Coord cmer(input,3), stpar(input,4); - if(cmer.Exist()) - { - bool suc=true; - p.y.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else p.y.cmer.Convert((p.region.xb+p.region.xe)*0.5); - if(stpar.Exist()) - { - bool suc=true; - p.y.stpar=stpar(&suc); - if(!suc) goto fail; // Parsing error - } - else p.y.stpar.Convert((p.region.yb+p.region.ye)*0.5); - if(size>5) goto fail; // Unknown parameter(s) - break; - } - case(gmt_projection::MILLER): // j Parameters: central meridian (4, default is center of region) - { - Base2Coord cmer(input,3); - if(cmer.Exist()) - { - bool suc=true; - p.j.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else p.j.cmer.Convert((p.region.xb+p.region.xe)*0.5); - if(size>4) goto fail; // Unknown parameter(s) - 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) - { - Base2Coord cmer(input,3), stpar(input,4); - if(cmer.Exist()) - { - bool suc=true; - p.cyl_stere.cmer=cmer(&suc); - if(!suc) goto fail; // Parsing error - } - else p.cyl_stere.cmer.Convert((p.region.xb+p.region.xe)*0.5); - if(stpar.Exist()) - { - bool suc=true; - p.cyl_stere.stpar=stpar(&suc); - if(!suc) goto fail; // Parsing error - } - else p.cyl_stere.stpar.Convert((p.region.yb+p.region.ye)*0.5); - if(size>5) goto fail; // Unknown parameter(s) - break; - } - default: goto fail; // Unknown projection - } - if(ProjectionRealSize(p)) return p; - } - - fail: - *issuc=false; - return p; // Something go wrong - } -}; -// Special case is GMTProjection -template<> -class ProjConv -{ - public: - struct gmt_projection operator()(const ObjectGMTProjection* o, bool* suc) const {return o->Data();} -}; -typedef Base2Something Base2Proj; - -// Helper types -typedef GetDouble,PMax<255>,PDefaultVal<>> Base2RGB; -typedef GetDouble,PMax<360>,PDefaultVal<>> Base2Hue; -typedef GetDouble,PMax<1>,PDefaultVal<>> Base2SV; -typedef GetDouble,PMax<100>,PDefaultVal<>> Base2CMYK; -typedef GetDouble,PMax<100>,PDefaultVal<>> Base2Transp; - -typedef GetDouble,PMax<255>,PMultiInputNames> BaseM2RGB; -typedef GetDouble,PMax<360>,PMultiInputNames> BaseM2Hue; -typedef GetDouble,PMax<1>,PMultiInputNames> BaseM2SV; -typedef GetDouble,PMax<100>,PMultiInputNames> BaseM2CMYK; -typedef GetDouble,PMax<100>,PMultiInputNames> BaseM2Transp; - -// Converting List to GMTColor -template class ColorConv -{ - public: - struct gmt_color operator()(const O* o, bool* issuc) const - { - struct gmt_color c; - if(!c.Convert(o->Value())) *issuc=false; - return c; - } -}; -// Special case is GMTColor -template<> -class ColorConv -{ - public: - struct gmt_color operator()(const ObjectGMTColor* o, bool* suc) const {return o->Data();} -}; -typedef Base2Something Base2Color; -// Conversion from List -template<> -class ColorConv -{ - public: - // Cases see in description of GMT_Color function - struct gmt_color operator()(const ObjectList* input, bool* issuc) const - { - struct gmt_color c; - auto size=input->Size(); - - if(1==size) // Cases 1, 2 and 4 - { - Base2Color a(input,0); - bool suc=true; - c=a(&suc); - if(suc) return c; - goto fail; - } - - // Case 3 - { - bool upd=false; - bool cmodset=false; - - // Update case, arguments list contains parameter with type GMTColor. - for(ObjectList::ListValues::size_type i=0;i color(input->At(i)); - if(color) - { - if(upd) goto fail; // Only one color in list allowed - c=color->Data(); - upd=true; - } - } - - // Update case, arguments list contains parameter color and this parameter is color. - { - Base2Color col(input,"color"); - if(col.Exist()) - { - if(upd) goto fail; // Already have color to update - bool suc=true; - struct gmt_color cs=col(&suc); - if(suc) - { - upd=true; - c=cs; - } - else goto fail; // Parse error - } - } - if(!upd) - { - // default color is black - c.transparency=0; - c.model=gmt_color::GRAY; - c.gray=0.0; - } - - { - BaseM2Transp t(input,"t","transp","transparency"); - if(t.Exist()) - { - bool suc=true; - c.transparency=t(&suc); - if(!suc) goto fail; // Parsing error - } - } - - // GRAY - { - BaseM2RGB g(input,"gray","grey"); - if(g.Exist()) - { - if(cmodset) goto fail; // Model already set - bool suc=true; - c.model=gmt_color::GRAY; - c.gray=g(&suc); // Update mode ignored in this case - if(!suc) goto fail; // Parsing error - cmodset=true; - } - } - - // RGB - { - BaseM2RGB r(input,"r","red"), g(input,"g","green"), b(input,"b","blue"); - if(r.Exist() || g.Exist() || b.Exist()) - { - if(cmodset) goto fail; // Model already set - bool suc=true; - c.ToRGB(); - if(r.Exist()) c.r=r(&suc); - if(g.Exist()) c.g=g(&suc); - if(b.Exist()) c.b=b(&suc); - if(!suc) goto fail; // Parsing error - cmodset=true; - } - } - - // HSV - { - BaseM2Hue h(input,"h","hue"); - BaseM2SV s(input,"s","sat","saturation"), v(input,"v","val","value"); - if(h.Exist() || s.Exist() || v.Exist()) - { - if(cmodset) goto fail; // Model already set - bool suc=true; - c.ToHSV(); - if(h.Exist()) c.hue=h(&suc); - if(s.Exist()) c.saturation=s(&suc); - if(v.Exist()) c.value=v(&suc); - if(!suc) goto fail; // Parsing error - cmodset=true; - } - } - - // CMYK - { - BaseM2CMYK cc(input,"c","cyan"), m(input,"m","magenta"), y(input,"y","yellow"), k(input,"k","black"); - if(cc.Exist() || m.Exist() || y.Exist() || k.Exist()) - { - if(cmodset) goto fail; // Model already set - bool suc=true; - c.ToCMYK(); - if(cc.Exist()) c.cyan=cc(&suc); - if(m.Exist()) c.magenta=m(&suc); - if(y.Exist()) c.yellow=y(&suc); - if(k.Exist()) c.black=k(&suc); - if(!suc) goto fail; // Parsing error - cmodset=true; - } - } - if(cmodset || upd) return c; // Color created or updated - } - - // Case 5 - if(3==size) - { - Base2RGB r(input,0), g(input,1), b(input,2); - c.model=gmt_color::RGB; - bool suc=true; - if(r && g && b) - { - c.r=r(&suc); - c.g=g(&suc); - c.b=b(&suc); - if(!suc) goto fail; // Parsing error - } - else goto fail; // Something wrong - return c; - } - - // Case 6 - if(4==size) - { - Base2CMYK cc(input,0), m(input,1), y(input,2), k(input,3); - c.model=gmt_color::CMYK; - bool suc=true; - if(cc && m && y && k) - { - c.cyan=cc(&suc); - c.magenta=m(&suc); - c.yellow=y(&suc); - c.black=k(&suc); - if(!suc) goto fail; // Parsing error - } - else goto fail; // Something wrong - return c; - } - - fail: - *issuc=false; - return c; // Something go wrong - } -}; - -// Helper types -typedef GetDouble,PDefaultVal<>> Base2Width; -typedef GetDouble,PMultiInputNames> BaseM2Width; - -// Converting String to GMTDash -template class DashConv -{ - public: - struct gmt_dash operator()(const O* o, bool* issuc, const double& w) const - { - struct gmt_dash d; - if(!d.Convert(o->Value(),w)) *issuc=false; - return d; - } -}; -// Special case is GMTDash -template<> -class DashConv -{ - public: - struct gmt_dash operator()(const ObjectGMTDash* o, bool* suc, const double& w) const {return o->Data();} -}; -typedef Base2Something Base2Dash; - -// Converting List to GMTPen -template class PenConv -{ - public: - struct gmt_pen operator()(const O* o, bool* issuc) const - { - struct gmt_pen p; - if(!p.Convert(o->Value())) *issuc=false; - return p; - } -}; -// Special case is GMTPen -template<> -class PenConv -{ - public: - struct gmt_pen operator()(const ObjectGMTPen* o, bool* suc) const {return o->Data();} -}; -typedef Base2Something Base2Pen; -// Conversion from List -template<> -class PenConv -{ - public: - // Cases see in description of GMT_Pen function - struct gmt_pen operator()(const ObjectList* input, bool* issuc) const - { - struct gmt_pen p; - auto size=input->Size(); - - if(1==size) // Cases 1, 2 and 4 - { - Base2Pen a(input,0); - bool suc=true; - p=a(&suc); - if(suc) return p; - goto fail; - } - - // Case 3 - { - bool upd=false; - bool casevalid=false; - - // Update case, arguments list contains parameter with type GMTPen. - for(ObjectList::ListValues::size_type i=0;i pen(input->At(i)); - if(pen) - { - if(upd) goto fail; // Only one pen in list allowed - p=pen->Data(); - upd=true; - } - } - - // Update case, arguments list contains parameter p or pen. - { - Base2Pen pshort(input,"p"); - Base2Pen plong(input,"pen"); - if(pshort.Exist() && plong.Exist()) goto fail; // Only one parameter allowed - if(pshort.Exist()) - { - if(upd) goto fail; // Already have pen to update - bool suc=true; - upd=true; - p=pshort(&suc); - if(!suc) goto fail; // Parse error - } - if(plong.Exist()) - { - if(upd) goto fail; // Already have pen to update - upd=true; - bool suc=true; - p=plong(&suc); - if(!suc) goto fail; // Parse error - } - } - if(!upd) - { - // default pen is solid black 1mm width - p.width=1; - p.color.Convert(0); - p.dash.Clear(); - } - - { - BaseM2Width w(input,"w","width"); - if(w.Exist()) - { - bool suc=true; - p.width=w(&suc); - if(!suc) goto fail; // Parsing error - casevalid=true; - } - } - - // Color - { - bool havec=false; - // Find parameter with type GMTColor. - for(ObjectList::ListValues::size_type i=0;i c(input->At(i)); - if(c) - { - if(havec) goto fail; // Only one color in list allowed - p.color=c->Data(); - havec=true; - } - } - Base2Color sh(input,"c"), lo(input,"color"); - if(sh.Exist() && lo.Exist()) goto fail; // Only one parameter allowed - if(sh.Exist()) - { - if(havec) goto fail; // Already have color - havec=true; - bool suc=true; - p.color=sh(&suc); - if(!suc) goto fail; // Parsing error - } - if(lo.Exist()) - { - if(havec) goto fail; // Already have color - havec=true; - bool suc=true; - p.color=lo(&suc); - if(!suc) goto fail; // Parsing error - } - if(havec) casevalid=true; - } - - // Dash - { - bool haved=false; - // Find parameter with type GMTDash. - for(ObjectList::ListValues::size_type i=0;i d(input->At(i)); - if(d) - { - if(haved) goto fail; // Only one color in list allowed - p.dash=d->Data(); - haved=true; - } - } - Base2Dash sh(input,"d"), lo(input,"dash"); - if(sh.Exist() && lo.Exist()) goto fail; // Only one parameter allowed - if(sh.Exist()) - { - if(haved) goto fail; // Already have color - haved=true; - bool suc=true; - p.dash=sh(&suc,p.width); - if(!suc) goto fail; // Parsing error - } - if(lo.Exist()) - { - if(haved) goto fail; // Already have color - haved=true; - bool suc=true; - p.dash=lo(&suc,p.width); - if(!suc) goto fail; // Parsing error - } - if(haved) casevalid=true; - } - - if(casevalid || upd) return p; // Pen created or updated - } - - // Case 5 and 6 - if(2==size || 3==size) - { - Base2Width w(input,0); - Base2Color c(input,1); - Base2Dash d(input,2); - bool suc=true; - if(w && c) - { - p.width=w(&suc); - p.color=c(&suc); - } - else goto fail; // Something wrong - if(d) p.dash=d(&suc,p.width); - if(!suc) goto fail; // Something wrong - return p; - } - - fail: - *issuc=false; - return p; // Something go wrong - } -}; - - -/* -Input is one argument, Int, Real, String or GMTCoord. -ObjectString can be number or in form "dd:mm[:ss]", where dd is degrees from -360 to 360, mm is minutes from 0 to 59 -*/ -ObjectBase* GMT_Coord(const ObjectList* input) -{ - if(input->Size()!=1) return 0; - Base2Coord a(input,0); - struct gmt_coord c; - bool suc=true; - - c=a(&suc); - if(a && suc) return new ObjectGMTCoord(c); - else return 0; -} - - -/* -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 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. -*/ -ObjectBase* GMT_Region(const ObjectList* input) -{ - bool suc=true; - struct gmt_region r=RegionConv()(input,&suc); - if(suc) return new ObjectGMTRegion(r); - else return 0; -} - - -/* -Input: -1) One argument, Projection. Return copy of this argument. -2) One argument, list. Recursively calling GMT_Projection. -3) Pairs list. Names are projtype (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 proj (or projection) 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. -*/ -ObjectBase* GMT_Projection(const ObjectList* input) -{ - bool suc=true; - struct gmt_projection p=ProjConv()(input,&suc); - if(suc) return new ObjectGMTProjection(p); - else return 0; -} - - -/* -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. -*/ -ObjectBase* GMT_Color(const ObjectList* input) -{ - bool suc=true; - struct gmt_color c=ColorConv()(input,&suc); - if(suc) return new ObjectGMTColor(c); - else return 0; -} - ObjectBase* GMT_ColorGray(const ObjectList* input) { struct gmt_color c; @@ -1488,23 +77,3 @@ ObjectBase* GMT_ColorCMYK(const ObjectList* input) if(suc) return new ObjectGMTColor(c); else return 0; } - -/* -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. -*/ -ObjectBase* GMT_Pen(const ObjectList* input) -{ - bool suc=true; - struct gmt_pen p=PenConv()(input,&suc); - if(suc) return new ObjectGMTPen(p); - else return 0; -} diff --git a/modules/gmt/modgmt_func.h b/modules/gmt/modgmt_func.h index 2f34305..a3c0345 100644 --- a/modules/gmt/modgmt_func.h +++ b/modules/gmt/modgmt_func.h @@ -1,17 +1,1217 @@ #ifndef MODGMT_FUNC_H #define MODGMT_FUNC_H +#include "modgmt_internals.h" #include "modgmt_objects.h" ObjectBase* GMT_Header(const ObjectList* input); ObjectBase* GMT_Footer(const ObjectList* input); -ObjectBase* GMT_Coord(const ObjectList* input); -ObjectBase* GMT_Region(const ObjectList* input); -ObjectBase* GMT_Projection(const ObjectList* input); -ObjectBase* GMT_Color(const ObjectList* input); ObjectBase* GMT_ColorGray(const ObjectList* input); ObjectBase* GMT_ColorRGB(const ObjectList* input); ObjectBase* GMT_ColorHSV(const ObjectList* input); ObjectBase* GMT_ColorCMYK(const ObjectList* input); -ObjectBase* GMT_Pen(const ObjectList* input); + +// Extension of OBTypeM template for easy work with lists +template class Func, class... O> +class Base2Something: public OBTypeM +{ + Base2Something() = delete; + Base2Something(Base2Something&&) = delete; + Base2Something(Base2Something&) = delete; + + public: + Base2Something(const ObjectBase* arg):OBTypeM(arg) {}; + Base2Something(const ObjectList* input, const std::string& name):OBTypeM(input->Get(name)){}; + Base2Something(const ObjectList* input, const ObjectList::ListValues::size_type i):OBTypeM((iSize())?input->At(i):0){}; // Check index, because At is not safe + template + auto operator ()(bool* b, Args... args) const -> decltype(this->OBTypeM::template operator()(b, args...)) + { + if(!OBTypeM::operator bool()) *b=false; + return OBTypeM::operator ()(b,args...); + } +}; + +// Generic converting template +template +class Convert2Struct +{ + public: + template + Struct operator()(const O* o, bool* suc, Args... args) const + { + Struct s; + if(!s.Convert(o->Value(),args...)) *suc=false; + return s; + } +}; +// Extract structure from corresponding class +template +class Convert2Struct > +{ + public: + template + Struct operator()(const ObjectGMTClass* o, bool* suc, Args... args) const { return o->Data(); } +}; + +// Converting Int or Real to double +template +class Convert2Struct +{ + public: + double operator ()(const O* q, bool* suc) const {return q->Value();} +}; +// Converting String to double +template<> +class Convert2Struct +{ + public: + double operator ()(const ObjectString* q, bool* suc) const {double d=0; *suc=(*suc) && str2double(q->Value(),&d); return d;} +}; + +// Get string +template<> +class Convert2Struct +{ + public: + std::string operator ()(const ObjectString* q, bool* suc) const {return q->Value();} +}; + +// Generic type storage class +template +class gTypeStorage +{ + template class Convertor: public Convert2Struct {}; // Conversion type + public: + typedef Base2Something Base2Type; +}; +// Specific type storage class +template +class TypeStorage; + +// Default ObjectList converter +template +class Convert2Struct +{ + public: + template + Struct operator()(const ObjectList* o, bool* suc, Args... args) const + { + Struct s; + if(o->Size()!=1) {*suc=false; return s;} + typename TypeStorage::Base2Type a(o,0); + s=a(suc,args...); + return s; + } +}; + +// Helper struct for class SearchParameter +template +// Search function for non-gmt_struct types do nothing +struct SearchHelper {void operator()(const ObjectList* input, Struct* s, bool* exist, bool* ok) const {}}; +template +// Search function for gmt_struct types find ObjectGMTClass in list +struct SearchHelper +{ + void operator()(const ObjectList* input, Struct* s, bool* exist, bool* ok) const + { + for(ObjectList::ListValues::size_type i=0;iSize();i++) + { + OBType > obj(input->At(i)); + if(obj) + { + if(*exist) {*ok=false; break;} // Object already found + *s=obj->Data(); + *exist=true; + } + } + } +}; + +// This class search parameter with specified names in the list, which may be converted to Struct. +// If Struct is derived from gmt_struct, additionaly search unnamed parameter of type ObjectGMTClass +template +class SearchParameter +{ + bool exist; // There is at least one parameter with given names + bool ok; // True if conversion to corresponding structure was successful and there is only one parameter with given names + Struct val; + typedef typename TypeStorage::Base2Type Base2Struct; + public: + // Recursive constructor + template + SearchParameter(const ObjectList* input, const std::string& name, Args... args):SearchParameter(input,args...) + { + if(ok) + { + Base2Struct a(input,name); + if(exist && a.Exist()) ok=false; + else if(a.Exist()) + { + exist=true; + ok=true; + val=a(&ok); + } + } + } + // Bottom of recursion + SearchParameter(const ObjectList* input):exist(false),ok(true) {SearchHelper::value>()(input,&val,&exist,&ok);} + Struct operator()(bool* suc) const + { + if(!ok || !exist) *suc=false; + return val; + } + Struct operator()(bool* ex, bool* suc) const + { + *ex=exist; + if(!ok || !exist) *suc=false; + return val; + } + bool Exist() const {return exist;} +}; + +// Definitions for double +template<> class TypeStorage: public gTypeStorage {}; +typedef TypeStorage::Base2Type Base2Double; // Copy definition to global namespace +// Definitions for string +template<> class TypeStorage: public gTypeStorage {}; +typedef TypeStorage::Base2Type Base2String; // Copy definition to global namespace + +// This source using Base2Double to get value of parameter. If parameter not exist, fallback to default value. +// Default value can be setted as template parameter or as parameter of constructor. +// We use rational representation of floating point number, because double type values not allowed as template parameter +template +class SourceDefaultVal: public Base2Double +{ + double defval; + public: + // Constructors without default value + SourceDefaultVal(const ObjectBase* arg):Base2Double(arg),defval(static_cast(num)/denum) {}; + SourceDefaultVal(const ObjectList* input, const std::string& name):Base2Double(input,name),defval(static_cast(num)/denum){}; + SourceDefaultVal(const ObjectList* input, const ObjectList::ListValues::size_type i):Base2Double(input,i),defval(static_cast(num)/denum){}; + // Constructors with default value + SourceDefaultVal(const ObjectBase* arg, double d):Base2Double(arg),defval(d) {}; + SourceDefaultVal(const ObjectList* input, const std::string& name, double d):Base2Double(input,name),defval(d){}; + SourceDefaultVal(const ObjectList* input, const ObjectList::ListValues::size_type i, double d):Base2Double(input,i),defval(d){}; + double operator()(bool* suc) const + { + if(Exist()) return Base2Double::operator()(suc); + else return defval; + } +}; + +// This source try to get value of named parameter from list, which may have different names. Only one name must be in list. +template +class SourceMultiInputNames +{ + bool exist; // There is at least one parameter with given names + bool ok; // True if conversion to Struct was successful and there is only one parameter with given names + Struct val; + typedef typename TypeStorage::Base2Type Base2Struct; + public: + // Recursive constructor + template + SourceMultiInputNames(const ObjectList* input, const std::string& name, Args... args):SourceMultiInputNames(input,args...) + { + Base2Struct a(input,name); + if(exist && a.Exist()) ok=false; + else if(a.Exist()) + { + exist=true; + ok=true; + val=a(&ok); + } + } + // Bottom of recursion + SourceMultiInputNames(const ObjectList* input, const std::string& name) + { + Base2Struct a(input,name); + exist=a.Exist(); + ok=true; + if(exist) val=a(&ok); + } + Struct operator()(bool* suc) const + { + if(!ok || !exist) *suc=false; + return val; + } + bool Exist() const {return exist;} +}; + +// Definitions for ObjectGMTCoord +template<> class TypeStorage: public gTypeStorage {}; +typedef TypeStorage::Base2Type Base2Coord; +// Definitions for ObjectGMTRegion +template<> class TypeStorage: public gTypeStorage {}; +typedef TypeStorage::Base2Type Base2Region; +// Definitions for ObjectGMTProjection +template<> class TypeStorage: public gTypeStorage {}; +typedef TypeStorage::Base2Type Base2Projection; +// Definitions for ObjectGMTColor +template<> class TypeStorage: public gTypeStorage {}; +typedef TypeStorage::Base2Type Base2Color; +// Definitions for ObjectGMTDash +template<> class TypeStorage: public gTypeStorage {}; +typedef TypeStorage::Base2Type Base2Dash; +// Definitions for ObjectGMTPen +template<> class TypeStorage: public gTypeStorage {}; +typedef TypeStorage::Base2Type Base2Pen; + + +// 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) const + { + struct gmt_region r; + auto size=input->Size(); + + if(1==size) // Cases 1, 2 and 3 + { + Base2Region region(input,0); + bool suc=true; + r=region(&suc); + if(!suc) goto fail; // Conversion failed + return r; + } + + // Case 4 + { + OBType type(input->Get("type")); + if(type && r.Convert(type->Value())) return r; // type is one of "global*" string, we can return, becuse upd is irrelevant + + bool upd; + { + SearchParameter updarg(input,"r","region"); + bool suc=true; + r=updarg(&upd,&suc); + if(upd && !suc) goto fail; // Conversion failed or too many arguments + } + + Base2Coord ixb(input,"xb"), ixe(input,"xe"), iyb(input,"yb"), iye(input,"ye"); + if(!(type.Exist() || ixb.Exist() || ixe.Exist() || iyb.Exist() || iye.Exist() || upd)) goto case5; // No named parameters, no update mode, possibly case 5 + bool suc=true; + struct gmt_coord xb,yb,xe,ye; + bool isbbox=upd?(gmt_region::BBOX==r.type):false; + + if(type) + { + std::string s=type->Value(); + tolower(s); + if("bbox"==s) isbbox=true; + if("nobbox"==s) isbbox=false; + if("bbox"!=s && "nobbox"!=s) goto fail; // Unknown type + } + + if(upd) + { + if(!ixb.Exist()) xb=r.xb; + if(!ixe.Exist()) xe=r.xe; + if(!iyb.Exist()) yb=r.yb; + if(!iye.Exist()) ye=r.ye; + } + else suc=suc && ixb && ixe && iyb && iye; // In "new" mode all parameters must exists and have correct type + if(ixb.Exist()) xb=ixb(&suc); + if(ixe.Exist()) xe=ixe(&suc); + if(iyb.Exist()) yb=iyb(&suc); + if(iye.Exist()) ye=iye(&suc); + if(!suc) goto fail; // Something wrong + if(!r.Convert(xb,xe,yb,ye,isbbox)) goto fail; // Conversion failed + return r; // Case 3 with all parameters + } + + case5: + // Case 5 + if(4==size || 5==size) + { + bool isbbox=false; + if(5==size) + { + OBType type(input->At(4)); + if(!type) goto fail; // Unknown fifth parameter + std::string str=type->Value(); + tolower(str); + if("bbox"!=str || "nobbox"!=str) goto fail; // Unknown fifth parameter + if("bbox"==str) isbbox=true; + } + Base2Coord ixb(input,0), ixe(input,(isbbox?2:1)), iyb(input,(isbbox?1:2)), iye(input,3); + bool suc=true; + struct gmt_coord xb=ixb(&suc),yb=iyb(&suc),xe=ixe(&suc),ye=iye(&suc); + if(ixb && ixe && iyb && iye && suc) + { + if(!r.Convert(xb,xe,yb,ye,isbbox)) 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) const + { + typedef GetDouble,PMin<0> > BaseM2Nonneg; + struct gmt_projection p; + auto size=input->Size(); + + if(1==size) // Cases 1, and 2 + { + Base2Projection proj(input,0); + bool suc=true; + p=proj(&suc); + if(!suc) goto fail; // Conversion failed + return p; + } + + // Case 3 + { + bool upd; + bool changetype=false; + { + SearchParameter updarg(input,"p","proj","projection"); + bool suc=true; + p=updarg(&upd,&suc); + if(upd && !suc) goto fail; // Conversion failed or too many arguments + } + + // Try to set type of projection + { + SearchParameter type(input,"t","type","projtype"); + if(!(type.Exist() || upd)) goto case4; // No named parameter, not update mode, go to case 4 + // Check on redundant parameters + bool suc=true; + if(type.Exist()) + { + std::string typestr=type(&suc); + if(!suc) goto fail; // Wrong parameter type + if(!p.SetType(typestr)) goto fail; // Incorrect projection type + changetype=true; + } + } + + // We need the region + { + SearchParameter region(input,"r","region"); + if(!(region.Exist() || upd)) goto fail; // Region must be defined in "new" mode + bool suc=true; + if(region.Exist()) p.region=region(&suc); + if(!suc) goto fail; // Conversion failed + } + + // Get parameters of projection + switch(p.proj) + { + case(gmt_projection::XY): // x Parameter height (by default equal width) + { + BaseM2Nonneg height(input,"height","h"); + if(height.Exist()) + { + bool suc=true; + p.x.height=height(&suc); + if(!suc) goto fail; // Parsing error + } + else + { + BaseM2Nonneg width(input,"width","w"); + if(!width.Exist() && changetype) p.x.height=gmt_projection::default_width; // We ignore case when parameter width exists but have wrong type. It will be handled later. + else + { + bool suc=true; + p.x.height=width(&suc); + if(!suc) goto fail; // Parsing error + } + } + break; + } + case(gmt_projection::CYL_EQU): // q Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region) + { + Base2Coord cmer(input,"cmer"), stpar(input,"stpar"); + if(cmer.Exist()) + { + bool suc=true; + p.q.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.q.cmer.Convert((p.region.xb+p.region.xe)*0.5); + if(stpar.Exist()) + { + bool suc=true; + p.q.stpar=stpar(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.q.stpar.Convert((p.region.yb+p.region.ye)*0.5); + break; + } + case(gmt_projection::MERCATOR): // m Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region) + { + Base2Coord cmer(input,"cmer"), stpar(input,"stpar"); + if(cmer.Exist()) + { + bool suc=true; + p.m.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.m.cmer.Convert((p.region.xb+p.region.xe)*0.5); + if(stpar.Exist()) + { + bool suc=true; + p.m.stpar=stpar(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.m.stpar.Convert((p.region.yb+p.region.ye)*0.5); + 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) + { + Base2Coord cmer(input,"cmer"), orlat(input,"orlat"); + Base2Double scale(input,"scale"); + if(cmer.Exist()) + { + bool suc=true; + p.t.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.t.cmer.Convert((p.region.xb+p.region.xe)*0.5); + if(orlat.Exist()) + { + bool suc=true; + p.t.orlat=orlat(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.t.orlat.Convert(0.0); + if(scale.Exist()) + { + bool suc=true; + p.t.scale=scale(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.t.scale=1.0; + 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). + { + Base2Coord clon(input,"clon"), clat(input,"clat"); + Base2Coord azimuth(input,"azimuth"), eqlon(input,"eqlon"), eqlat(input,"eqlat"), polelon(input,"polelon"), polelat(input,"polelat"); + if(changetype) p.o.type=gmt_projection::OType::NOTDEF; + if(clon.Exist()) + { + bool suc=true; + p.o.clon=clon(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.o.clon.Convert((p.region.xb+p.region.xe)*0.5); + if(clat.Exist()) + { + bool suc=true; + p.o.clat=clat(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.o.clat.Convert((p.region.yb+p.region.ye)*0.5); + // Variant 1 + if(azimuth) + { + bool suc=(gmt_projection::OType::NOTDEF==p.o.type); // If projection subtype already defined, this is an error + p.o.type=gmt_projection::OType::A; + p.o.azimuth=azimuth(&suc); + if(!suc) goto fail; // Parsing error + } + if(eqlon && eqlat) + { + bool suc=(gmt_projection::OType::NOTDEF==p.o.type); // If projection subtype already defined, this is an error + p.o.type=gmt_projection::OType::B; + p.o.eqlon=eqlon(&suc); + p.o.eqlat=eqlat(&suc); + if(!suc) goto fail; // Parsing error + } + if(polelon && polelat) + { + bool suc=(gmt_projection::OType::NOTDEF==p.o.type); // If projection subtype already defined, this is an error + p.o.type=gmt_projection::OType::C; + p.o.polelon=polelon(&suc); + p.o.polelat=polelat(&suc); + if(!suc) goto fail; // Parsing error + } + if(gmt_projection::OType::NOTDEF==p.o.type) 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. + { + Base2Coord clon(input,"clon"), clat(input,"clat"); + if(clon.Exist()) + { + bool suc=true; + p.c.clon=clon(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.c.clon.Convert((p.region.xb+p.region.xe)*0.5); + if(clat.Exist()) + { + bool suc=true; + p.c.clat=clat(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.c.clat.Convert((p.region.yb+p.region.ye)*0.5); + break; + } + case(gmt_projection::CYL_EQA): // y Parameters: central meridian (cmer, default is center of region), standart parallel (stpar, default is center of region) + { + Base2Coord cmer(input,"cmer"), stpar(input,"stpar"); + if(cmer.Exist()) + { + bool suc=true; + p.y.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.y.cmer.Convert((p.region.xb+p.region.xe)*0.5); + if(stpar.Exist()) + { + bool suc=true; + p.y.stpar=stpar(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.y.stpar.Convert((p.region.yb+p.region.ye)*0.5); + break; + } + case(gmt_projection::MILLER): // j Parameters: central meridian (cmer, default is center of region) + { + Base2Coord cmer(input,"cmer"); + if(cmer.Exist()) + { + bool suc=true; + p.j.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.j.cmer.Convert((p.region.xb+p.region.xe)*0.5); + 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) + { + Base2Coord cmer(input,"cmer"), stpar(input,"stpar"); + if(cmer.Exist()) + { + bool suc=true; + p.cyl_stere.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.cyl_stere.cmer.Convert((p.region.xb+p.region.xe)*0.5); + if(stpar.Exist()) + { + bool suc=true; + p.cyl_stere.stpar=stpar(&suc); + if(!suc) goto fail; // Parsing error + } + else if(changetype) p.cyl_stere.stpar.Convert((p.region.yb+p.region.ye)*0.5); + break; + } + default: goto fail; // Unknown projection + } + + // Try to find width parameter + if(!upd) p.width=p.default_width; + { + BaseM2Nonneg w(input,"width","w"),h(input,"height","h"); + bool suc=true; + if(w.Exist()) // width is present + { + p.width=w(&suc); + if(!suc) goto fail; // Parameter width exists, but can't be converted to double + if(!ProjectionRealSize(p)) goto fail; // Something go wrong with determining real dimensions + } + else if(h.Exist()) // width is not present, but height is present + { + double hval=h(&suc); + if(!suc) goto fail; // Parameter height exists, but can't be converted to double + if(gmt_projection::XY==p.proj) p.width=hval; // For decart projection we use height as width if width is not specified + if(!ProjectionRealSize(p,hval)) goto fail; // Something go wrong with determining real dimensions + } + else // No width, no height, using default or old width + if(!ProjectionRealSize(p)) goto fail; // Something go wrong with determining real dimensions + } + return p; // All parameters setted + } + + case4: + + // Case 4 + if(size>=3) + { + // First argument, try to set type of projection + { + Base2String type(input,0); + bool suc=true; + std::string typestr; + typestr=type(&suc); + if(!suc) goto fail; // No type - no projection + if(!p.SetType(typestr)) goto fail; // Unknown type - no projection + } + // Second argument, set up width + { + Base2Double w(input,1); + bool suc=true; + p.width=w(&suc); + if(!suc) goto fail; // Conversion failed, no width + } + // Third argument, set up region + { + Base2Region reg(input,2); + bool suc=true; + p.region=reg(&suc); + if(!suc) goto fail; // Conversion failed, no region + } + + // Projection specific parameters + switch(p.proj) + { + case(gmt_projection::XY): // x Parameter 4 is height (by default equal width) + { + Base2Double height(input,3); + if(height.Exist()) + { + bool suc=true; + p.x.height=height(&suc); + if(!suc) goto fail; // Parsing error + } + else p.x.height=p.width; + if(size>4) goto fail; // Unknown parameter(s) + 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) + { + Base2Coord cmer(input,3), stpar(input,4); + if(cmer.Exist()) + { + bool suc=true; + p.q.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else p.q.cmer.Convert((p.region.xb+p.region.xe)*0.5); + if(stpar.Exist()) + { + bool suc=true; + p.q.stpar=stpar(&suc); + if(!suc) goto fail; // Parsing error + } + else p.q.stpar.Convert((p.region.yb+p.region.ye)*0.5); + if(size>5) goto fail; // Unknown parameter(s) + 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) + { + Base2Coord cmer(input,3), stpar(input,4); + if(cmer.Exist()) + { + bool suc=true; + p.m.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else p.m.cmer.Convert((p.region.xb+p.region.xe)*0.5); + if(stpar.Exist()) + { + bool suc=true; + p.m.stpar=stpar(&suc); + if(!suc) goto fail; // Parsing error + } + else p.m.stpar.Convert((p.region.yb+p.region.ye)*0.5); + if(size>5) goto fail; // Unknown parameter(s) + 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) + { + Base2Coord cmer(input,3), orlat(input,4); + Base2Double scale(input,5); + if(cmer.Exist()) + { + bool suc=true; + p.t.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else p.t.cmer.Convert((p.region.xb+p.region.xe)*0.5); + if(orlat.Exist()) + { + bool suc=true; + p.t.orlat=orlat(&suc); + if(!suc) goto fail; // Parsing error + } + else p.t.orlat.Convert(0.0); + if(scale.Exist()) + { + bool suc=true; + p.t.scale=scale(&suc); + if(!suc) goto fail; // Parsing error + } + else p.t.scale=1.0; + if(size>6) goto fail; // Unknown parameter(s) + 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). + { + if(size<4) goto fail; // Insufficient data for this projection + OBType stype(input->At(3)); + if(!stype) goto fail; // Incorrect parameter type + std::string subtype=stype->Value(); + tolower(subtype); + Base2Coord clon(input,4), clat(input,5); + Base2Coord azimuth(input,6), eqlon(input,6), eqlat(input,7), polelon(input,6), polelat(input,7); + p.o.type=gmt_projection::OType::NOTDEF; + { + bool suc=true; + p.o.clon=clon(&suc); + p.o.clat=clat(&suc); + if(!suc) goto fail; // Parsing error + } + // Variant 1 + if("a"==subtype || "azimuth"==subtype) + { + p.o.type=gmt_projection::OType::A; + bool suc=true; + p.o.azimuth=azimuth(&suc); + if(!suc) goto fail; // Parsing error + if(size>7) goto fail; // Unknown parameter(s) + } + if("b"==subtype || "equator"==subtype) + { + p.o.type=gmt_projection::OType::B; + bool suc=true; + p.o.eqlon=eqlon(&suc); + p.o.eqlat=eqlat(&suc); + if(!suc) goto fail; // Parsing error + if(size>8) goto fail; // Unknown parameter(s) + } + if("c"==subtype || "pole"==subtype) + { + p.o.type=gmt_projection::OType::C; + bool suc=true; + p.o.polelon=polelon(&suc); + p.o.polelat=polelat(&suc); + if(!suc) goto fail; // Parsing error + if(size>8) goto fail; // Unknown parameter(s) + } + if(gmt_projection::OType::NOTDEF==p.o.type) goto fail; // Insufficient data for this projection + 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. + { + Base2Coord clon(input,3), clat(input,4); + if(clon.Exist()) + { + bool suc=true; + p.c.clon=clon(&suc); + if(!suc) goto fail; // Parsing error + } + else p.c.clon.Convert((p.region.xb+p.region.xe)*0.5); + if(clat.Exist()) + { + bool suc=true; + p.c.clat=clat(&suc); + if(!suc) goto fail; // Parsing error + } + else p.c.clat.Convert((p.region.yb+p.region.ye)*0.5); + if(size>5) goto fail; // Unknown parameter(s) + break; + } + case(gmt_projection::CYL_EQA): // y Parameters: central meridian (4, default is center of region), standart parallel (5, default is center of region) + { + Base2Coord cmer(input,3), stpar(input,4); + if(cmer.Exist()) + { + bool suc=true; + p.y.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else p.y.cmer.Convert((p.region.xb+p.region.xe)*0.5); + if(stpar.Exist()) + { + bool suc=true; + p.y.stpar=stpar(&suc); + if(!suc) goto fail; // Parsing error + } + else p.y.stpar.Convert((p.region.yb+p.region.ye)*0.5); + if(size>5) goto fail; // Unknown parameter(s) + break; + } + case(gmt_projection::MILLER): // j Parameters: central meridian (4, default is center of region) + { + Base2Coord cmer(input,3); + if(cmer.Exist()) + { + bool suc=true; + p.j.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else p.j.cmer.Convert((p.region.xb+p.region.xe)*0.5); + if(size>4) goto fail; // Unknown parameter(s) + 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) + { + Base2Coord cmer(input,3), stpar(input,4); + if(cmer.Exist()) + { + bool suc=true; + p.cyl_stere.cmer=cmer(&suc); + if(!suc) goto fail; // Parsing error + } + else p.cyl_stere.cmer.Convert((p.region.xb+p.region.xe)*0.5); + if(stpar.Exist()) + { + bool suc=true; + p.cyl_stere.stpar=stpar(&suc); + if(!suc) goto fail; // Parsing error + } + else p.cyl_stere.stpar.Convert((p.region.yb+p.region.ye)*0.5); + if(size>5) goto fail; // Unknown parameter(s) + break; + } + default: goto fail; // Unknown projection + } + if(ProjectionRealSize(p)) return p; + } + + fail: + *issuc=false; + return p; // Something go wrong + } +}; + +// Helper types +typedef GetDouble,PMin<0>,PMax<255> > Base2RGB; +typedef GetDouble,PMin<0>,PMax<360> > Base2Hue; +typedef GetDouble,PMin<0>,PMax<1> > Base2SV; +typedef GetDouble,PMin<0>,PMax<100> > Base2CMYK; +typedef GetDouble,PMin<0>,PMax<100> > Base2Transp; + +typedef GetDouble,PMin<0>,PMax<255> > BaseM2RGB; +typedef GetDouble,PMin<0>,PMax<360> > BaseM2Hue; +typedef GetDouble,PMin<0>,PMax<1> > BaseM2SV; +typedef GetDouble,PMin<0>,PMax<100> > BaseM2CMYK; +typedef GetDouble,PMin<0>,PMax<100> > BaseM2Transp; + +// 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) const + { + struct gmt_color c; + auto size=input->Size(); + + if(1==size) // Cases 1, 2 and 4 + { + Base2Color color(input,0); + bool suc=true; + c=color(&suc); + if(!suc) goto fail; // Conversion failed + return c; + } + + // Case 3 + { + bool cmodset=false; + bool upd; + { + SearchParameter updarg(input,"color"); + bool suc=true; + c=updarg(&upd,&suc); + if(upd && !suc) goto fail; // Conversion failed or too many arguments + } + if(!upd) + { + // default color is black + c.transparency=0; + c.model=gmt_color::GRAY; + c.gray=0.0; + } + + { + BaseM2Transp t(input,"t","transp","transparency"); + if(t.Exist()) + { + bool suc=true; + c.transparency=t(&suc); + if(!suc) goto fail; // Parsing error + } + } + + // GRAY + { + BaseM2RGB g(input,"gray","grey"); + if(g.Exist()) + { + if(cmodset) goto fail; // Model already set + bool suc=true; + c.model=gmt_color::GRAY; + c.gray=g(&suc); // Update mode ignored in this case + if(!suc) goto fail; // Parsing error + cmodset=true; + } + } + + // RGB + { + BaseM2RGB r(input,"r","red"), g(input,"g","green"), b(input,"b","blue"); + if(r.Exist() || g.Exist() || b.Exist()) + { + if(cmodset) goto fail; // Model already set + bool suc=true; + c.ToRGB(); + if(r.Exist()) c.r=r(&suc); + if(g.Exist()) c.g=g(&suc); + if(b.Exist()) c.b=b(&suc); + if(!suc) goto fail; // Parsing error + cmodset=true; + } + } + + // HSV + { + BaseM2Hue h(input,"h","hue"); + BaseM2SV s(input,"s","sat","saturation"), v(input,"v","val","value"); + if(h.Exist() || s.Exist() || v.Exist()) + { + if(cmodset) goto fail; // Model already set + bool suc=true; + c.ToHSV(); + if(h.Exist()) c.hue=h(&suc); + if(s.Exist()) c.saturation=s(&suc); + if(v.Exist()) c.value=v(&suc); + if(!suc) goto fail; // Parsing error + cmodset=true; + } + } + + // CMYK + { + BaseM2CMYK cc(input,"c","cyan"), m(input,"m","magenta"), y(input,"y","yellow"), k(input,"k","black"); + if(cc.Exist() || m.Exist() || y.Exist() || k.Exist()) + { + if(cmodset) goto fail; // Model already set + bool suc=true; + c.ToCMYK(); + if(cc.Exist()) c.cyan=cc(&suc); + if(m.Exist()) c.magenta=m(&suc); + if(y.Exist()) c.yellow=y(&suc); + if(k.Exist()) c.black=k(&suc); + if(!suc) goto fail; // Parsing error + cmodset=true; + } + } + if(cmodset || upd) return c; // Color created or updated + } + + // Case 5 + if(3==size) + { + Base2RGB r(input,0), g(input,1), b(input,2); + c.model=gmt_color::RGB; + bool suc=true; + if(r && g && b) + { + c.r=r(&suc); + c.g=g(&suc); + c.b=b(&suc); + if(!suc) goto fail; // Parsing error + } + else goto fail; // Something wrong + return c; + } + + // Case 6 + if(4==size) + { + Base2CMYK cc(input,0), m(input,1), y(input,2), k(input,3); + c.model=gmt_color::CMYK; + bool suc=true; + if(cc && m && y && k) + { + c.cyan=cc(&suc); + c.magenta=m(&suc); + c.yellow=y(&suc); + c.black=k(&suc); + if(!suc) goto fail; // Parsing error + } + else goto fail; // Something wrong + return c; + } + + fail: + *issuc=false; + return c; // Something go wrong + } +}; + +// Helper types +typedef GetDouble,PMin<0> > Base2Width; +typedef GetDouble,PMin<0> > BaseM2Width; + +// 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) const + { + struct gmt_pen p; + auto size=input->Size(); + + if(1==size) // Cases 1, 2 and 4 + { + Base2Pen pen(input,0); + bool suc=true; + p=pen(&suc); + if(!suc) goto fail; // Conversion failed + return p; + } + + // Case 3 + { + bool casevalid=false; + bool upd; + { + SearchParameter updarg(input,"pen","p"); + bool suc=true; + p=updarg(&upd,&suc); + if(upd && !suc) goto fail; // Conversion failed or too many arguments + } + + if(!upd) + { + // default pen is solid black 1mm width + p.width=gmt_pen::default_width; + p.color.Convert(0); + p.dash.Clear(); + } + + { + BaseM2Width w(input,"w","width"); + if(w.Exist()) + { + bool suc=true; + p.width=w(&suc); + if(!suc) goto fail; // Parsing error + casevalid=true; + } + } + + // Color + { + SearchParameter color(input,"color","c"); + if(color.Exist()) + { + bool suc=true; + p.color=color(&suc); + if(!suc) goto fail; // Parsing error + casevalid=true; + } + } + + // Dash + { + SearchParameter dash(input,"dash","d"); + if(dash.Exist()) + { + bool suc=true; + p.dash=dash(&suc); + if(!suc) goto fail; // Parsing error + p.dash.width=p.width; + casevalid=true; + } + } + + if(casevalid || upd) return p; // Pen created or updated + } + + // Case 5 and 6 + if(2==size || 3==size) + { + Base2Width w(input,0); + Base2Color c(input,1); + Base2Dash d(input,2); + bool suc=true; + if(w && c) + { + p.width=w(&suc); + p.color=c(&suc); + } + else goto fail; // Something wrong + if(d) p.dash=d(&suc,p.width); + if(!suc) goto fail; // Something wrong + return p; + } + + fail: + *issuc=false; + return p; // Something go wrong + } +}; + +// Template for generating GMTObject from ObjectList +template +ObjectBase* GMT_Type(const ObjectList* input) +{ + bool suc=true; + Struct s=Convert2Struct()(input,&suc); + if(suc) return new ObjectGMTClass(s); + else return 0; +} #endif diff --git a/modules/gmt/modgmt_objects.cpp b/modules/gmt/modgmt_objects.cpp index a54cc9f..4c86cfa 100644 --- a/modules/gmt/modgmt_objects.cpp +++ b/modules/gmt/modgmt_objects.cpp @@ -49,3 +49,5 @@ void gmt_projection::FillProjNames() } const double gmt_projection::default_width=10.0; +const double gmt_pen::default_width=1.0; +const double gmt_dash::default_width=gmt_pen::default_width; diff --git a/modules/gmt/modgmt_objects.h b/modules/gmt/modgmt_objects.h index cd5551f..5835f29 100644 --- a/modules/gmt/modgmt_objects.h +++ b/modules/gmt/modgmt_objects.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "common.h" #include "modgmt_structs.h" @@ -19,6 +20,7 @@ inline static double cm2GMT(double cm) template class ObjectGMTClass: public ObjectBase { + static_assert(std::is_base_of::value,"Template parameter of ObjectGMTClass must be gmt_struct-derived type"); const static std::string type; GMTStruct s; public: diff --git a/modules/gmt/modgmt_structs.h b/modules/gmt/modgmt_structs.h index ecd2551..4d0c849 100644 --- a/modules/gmt/modgmt_structs.h +++ b/modules/gmt/modgmt_structs.h @@ -6,8 +6,39 @@ #include "common.h" #include "modgmt_colornames.h" -// Helper classes for conversion of doubles from strings with varios checks +// Helper template for checking double numbers acquired from Source with some Policies. +// Main definition, never instantiated +template +class GetDouble; + +// Recursive definition +template +class GetDouble: public GetDouble +{ + Policy p; + public: + GetDouble(GetDouble&&) = delete; + GetDouble(GetDouble&) = delete; + template + GetDouble(Args... args):GetDouble(args...) {}; + double operator()(bool* suc) const {return p(GetDouble::operator()(suc),suc);} +}; + +// Bottom of recursion +template +class GetDouble: public Source +{ + public: + GetDouble(GetDouble&&) = delete; + GetDouble(GetDouble&) = delete; + GetDouble() = delete; + template + GetDouble(Args... args):Source(args...) {}; + double operator()(bool* suc) const {return Source::operator()(suc);} +}; + // We use rational representation of floating point number, because double type values not allowed as template parameter +// Policy to check if value is greater or equal num/denum template class PMin { @@ -15,6 +46,7 @@ class PMin double operator()(double v, bool* suc) const {if(v(num)/denum) *suc=false; return v;} }; +// Policy to check if value is lesser or equal num/denum template class PMax { @@ -22,13 +54,14 @@ class PMax double operator()(double v, bool* suc) const {if(v>static_cast(num)/denum) *suc=false; return v;} }; -class PFromValue +// Get double value from string or number +class SourceValue { double d; bool ok; public: - PFromValue(const std::string& s) {ok=str2double(s,&d);} - PFromValue(double s):d(s),ok(true) {} + SourceValue(const std::string& s) {ok=str2double(s,&d);} + SourceValue(double s):d(s),ok(true) {} double operator()(bool* suc) const { if(!ok) *suc=false; @@ -36,39 +69,19 @@ class PFromValue } }; -// Main definition, never instantiated -template -class GetDouble; - -// Recursive definition -template -class GetDouble: public GetDouble -{ - Policy p; - public: - GetDouble(GetDouble&&) = delete; - GetDouble(GetDouble&) = delete; - template - GetDouble(Args... args):GetDouble(args...) {}; - double operator()(bool* suc) const {return p(GetDouble::operator()(suc),suc);} -}; +// Helper types for colors +typedef GetDouble,PMax<255> > Value2RGB; +typedef GetDouble,PMax<360> > Value2Hue; +typedef GetDouble,PMax<1> > Value2SV; +typedef GetDouble,PMax<100> > Value2CMYK; +typedef GetDouble,PMax<100> > Value2Transp; +// Helper type for pens and dashes +typedef GetDouble > Value2Width; -// Special case for PFromString policy. This policy mast be last in policies list. -template<> -class GetDouble -{ - PFromValue p; - public: - GetDouble(GetDouble&&) = delete; - GetDouble(GetDouble&) = delete; - GetDouble() = delete; - GetDouble(const std::string& str):p(str) {}; - GetDouble(double d):p(d) {}; - double operator()(bool* suc) const {return p(suc);} -}; +class gmt_struct {}; // Base type for all gmt structures // Coordinate -struct gmt_coord +struct gmt_coord: public gmt_struct { bool isdeg; union @@ -138,7 +151,7 @@ struct gmt_coord // Region -struct gmt_region +struct gmt_region: public gmt_struct { enum Type {NORMAL,BBOX,GLOBAL360,GLOBAL180}; Type type; @@ -190,7 +203,7 @@ struct gmt_region // Projection -struct gmt_projection +struct gmt_projection: public gmt_struct { // OBLIQMERCATOR types enum class OType {NOTDEF,A,B,C}; @@ -280,17 +293,11 @@ struct gmt_projection static std::map projnames; }; -// Helper types for colors -typedef GetDouble,PMax<255>,PFromValue> Value2RGB; -typedef GetDouble,PMax<360>,PFromValue> Value2Hue; -typedef GetDouble,PMax<1>,PFromValue> Value2SV; -typedef GetDouble,PMax<100>,PFromValue> Value2CMYK; -typedef GetDouble,PMax<100>,PFromValue> Value2Transp; - // Color -struct gmt_color +struct gmt_color: public gmt_struct { public: + enum ColorModel {RGB,GRAY,HSV,CMYK}; ColorModel model; union @@ -601,39 +608,40 @@ struct gmt_color #include "modgmt_colortransform.h" }; -// Helper types for pens -typedef GetDouble,PFromValue> Value2Width; - // Dash -struct gmt_dash +struct gmt_dash: public gmt_struct { std::vector dash; double shift; + double width; + bool wisrel; + static const double default_width; std::string Value() const { std::string ret; if(dash.empty()) return ret; - for(auto i:dash) ret+=std::to_string(i/10.0)+"_"; + for(auto i:dash) ret+=std::to_string((wisrel?width:1.0)*i/10.0)+"_"; ret.back()=':'; ret+=std::to_string(shift)+"c"; return ret; } operator bool() const {return !dash.empty();} - void Clear() {dash.clear(); shift=0.0;} + void Clear() {dash.clear(); shift=0.0; wisrel=false;} - bool Convert(const std::string& in, const double& w) + bool Convert(const std::string& in, double w=0.0) { Clear(); shift=0; if(0==in.size()) return true; // No dash if(std::string::npos==in.find_first_not_of(".-")) // dot-dash form { - double width=(0==w)?1:w; + width=(0==w)?default_width:w; + wisrel=true; for(const auto& i:in) { - if('.'==i) dash.push_back(width); // Dot - else dash.push_back(8*width); // Dash - dash.push_back(4*width); // Gap + if('.'==i) dash.push_back(1); // Dot + else dash.push_back(8); // Dash + dash.push_back(4); // Gap } return true; } @@ -665,11 +673,12 @@ struct gmt_dash // Pen -struct gmt_pen +struct gmt_pen: public gmt_struct { double width; struct gmt_color color; struct gmt_dash dash; + static const double default_width; std::string Value() const {return std::to_string(width/10.0)+"c,"+color.Value()+(dash?(","+dash.Value()):"");} // Interpret one numeric argument as width value @@ -692,7 +701,7 @@ struct gmt_pen WordList wl=Split(str,",",true); // Defaults - width=1; + width=default_width; color.Convert(0); // Black dash.Clear();