diff --git a/modules/gmt/modgmt.cpp b/modules/gmt/modgmt.cpp index a992855..f3f4a4e 100644 --- a/modules/gmt/modgmt.cpp +++ b/modules/gmt/modgmt.cpp @@ -1,87 +1,35 @@ #include "modgmt.h" #include "modgmt_func.h" - -std::string header,footer; - -#if defined MODGMT_WORKAROUND_EXIT -// Exit handler. -static void gmtonexithandler(int ret, void* x) -{ - reinterpret_cast(x)->ret=ret; - close(reinterpret_cast(x)->fd); - pthread_exit(&(reinterpret_cast(x)->ret)); -} -#endif - -// This function call GMT_Call_Module in separate thread. It just a hack to workaround absence of callbacks. -static void* gmtworkthread(void* x) -{ - struct gmtworkthreadpars* p=reinterpret_cast(x); -#if defined MODGMT_WORKAROUND_EXIT - on_exit(gmtonexithandler,x); -#endif - GMT_Append_Option(p->api,GMT_Make_Option(p->api,'>',const_cast(("/dev/fd/"+std::to_string(p->fd)).c_str())),p->opts); - p->ret=GMT_Call_Module(p->api,p->module,GMT_MODULE_OPT,p->opts); -#if defined MODGMT_WORKAROUND_EXIT - exit(p->ret); -#endif - close(p->fd); - return &p->ret; -} - -// Wrapper for GMT_Call_Module, res is output. -int callgmtmodule(void *api, const char *module, struct GMT_OPTION *opts, std::string* res, gmt_filter filt, void* filtpar) -{ - int pipefd[2]; - pthread_t wthr; - struct gmtworkthreadpars p; - int *pret; - - pipe(pipefd); - p.api=api; - p.module=module; - p.opts=opts; - p.fd=pipefd[1]; - - pthread_create(&wthr,0,&gmtworkthread,&p); - res->erase(); - - (*filt)(pipefd[0],res,filtpar); - - res->shrink_to_fit(); - pthread_join(wthr,reinterpret_cast(&pret)); - - return *pret; -} - +#include "modgmt_internals.h" // Initialisation function int gmt_module_init(void* p) { // Fill header and footer - struct GMT_OPTION* opts; void* gmtapi; - int ret; - const char* ch="--GMT_HISTORY=f -C -P -K"; - const char* cf="--GMT_HISTORY=f -C -P -O"; - char* h=strdup(ch); - char* f=strdup(cf); + int ret=0; - gmtapi=GMT_Create_Session("gmt_makemap",2,3,0); + gmtapi=GMT_Create_Session("gmt_makemap",2,GMTMODE,0); if(0==gmtapi) return 1; - opts=GMT_Create_Options(gmtapi,0,h); - ret=callgmtmodule(gmtapi,"psclip",opts,&header,gmt_filter_headfoot); - GMT_Destroy_Options(gmtapi,&opts); - opts=GMT_Create_Options(gmtapi,0,f); - if(0==ret) ret=callgmtmodule(gmtapi,"psclip",opts,&footer,gmt_filter_headfoot); - GMT_Destroy_Options(gmtapi,&opts); + if(0==ret) ret=callgmtmodule(gmtapi,"psclip","-C -P -K",&header,gmt_filter_headfoot); + if(0==ret) ret=callgmtmodule(gmtapi,"psclip","-C -P -O",&footer,gmt_filter_headfoot); GMT_Destroy_Session(gmtapi); - free(h); free(f); if(0!=ret) return ret; gmt_projection::FillProjNames(); RegisterFunction("GMT_Header",GMT_Header); RegisterFunction("GMT_Footer",GMT_Footer); + RegisterFunction("GET",Get); + RegisterFunction("GET",Get); + RegisterFunction("GET",Get); + RegisterFunction("GET",Get); + RegisterFunction("GET",Get); + RegisterFunction("GET",Get); + RegisterFunction("GET",Get); + + RegisterFunction("Coord",GMT_Coord); + RegisterFunction("Region",GMT_Region); + RegisterFunction("Projection",GMT_Projection); return 0; } diff --git a/modules/gmt/modgmt.h b/modules/gmt/modgmt.h index ba69593..11d7cb4 100644 --- a/modules/gmt/modgmt.h +++ b/modules/gmt/modgmt.h @@ -1,35 +1,9 @@ #ifndef MODGMT_H #define MODGMT_H -#include -#include -#include #include "common.h" -#include "filters.h" - -// Workaround exit() in GMT_Call_Module. May need because return mode of gmt api is not very reliable -#if defined MODGMT_WORKAROUND_EXIT -#define GMTMODE 0 -#else -#define GMTMODE 3 -#endif - -// here we save header and footer of gmt-produced eps files -extern std::string header,footer; - -// Parameters for working thread -struct gmtworkthreadpars -{ - void* api; - const char* module; - struct GMT_OPTION* opts; - int fd; - int ret; -}; extern "C" { EXPORT int gmt_module_init(void* p); } -int callgmtmodule(void *api, const char *module, struct GMT_OPTION *opts, std::string* res, gmt_filter filt=gmt_filter_default, void* filtpar=0); - #endif diff --git a/modules/gmt/modgmt_func.cpp b/modules/gmt/modgmt_func.cpp index a2783ec..0036955 100644 --- a/modules/gmt/modgmt_func.cpp +++ b/modules/gmt/modgmt_func.cpp @@ -1,5 +1,5 @@ -#include "modgmt.h" -#include "object.h" +#include "modgmt_internals.h" +#include "modgmt_func.h" ObjectBase* GMT_Header(const ObjectList* input) { @@ -10,3 +10,798 @@ 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 + auto operator ()(bool* b) -> decltype(this->OBTypeM::template operator()(b)) const {return OBTypeM::operator ()(b);} +}; + +// Converting Int, Real or String to double +template +class DoubleConv +{ + public: + double operator ()(const O* q, bool* suc) {return q->Value();} +}; +template<> +class DoubleConv +{ + public: + double operator ()(const ObjectString* q, bool* suc) {double d=0; *suc=(*suc) && str2double(q->Value(),&d); return d;} +}; +typedef Base2Something Base2Double; + +// 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; + + // Case 1 + for(ObjectList::ListValues::size_type i=0;iSize();i++) + { + OBType region(input->At(i)); + if(region) return r=region->Data(); // Case 1 + } + + // Case 4 + { + // Special case, arguments list contains parameter region. + if(0!=input->Get("region")) + { + ObjectList* list=new ObjectList; + list->PushBack(input->Get("region")); + r=operator()(list,issuc); + delete list; + return r; + } + + OBType type(input->Get("type")); + if(type && r.Convert(type->Value())) return r; // type is one of "global*" string + + 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=false; + + if(type) + { + std::string s=type->Value(); + tolower(s); + if("bbox"==s) isbbox=true; + } + + if(ixb && ixe && iyb && iye && suc) + { + if(!r.Convert(xb,xe,yb,ye,isbbox)) *issuc=false; + return r; // Case 3 with all parameters + } + } + + if(input->Size()==1) // Cases 2 and 3 + { + const ObjectBase *arg=input->At(0); + OBType string(arg); + if(string && r.Convert(string->Value())) return r; // Case 2 + OBType list(arg); + if(list) r=operator()(list,issuc); // Case 3 + else *issuc=false; // Unknown argument + return r; + } + + // Case 5 + if(4==input->Size() || 5==input->Size()) + { + bool isbbox=false; + if(5==input->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) {*issuc=false; return r;} // Unknown fifth parameter + 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(); + + // Case 1 + for(ObjectList::ListValues::size_type i=0;i proj(input->At(i)); + if(proj) return p=proj->Data(); // Case 1 + } + + // Case 3 + { + // Special case, arguments list contains parameter proj or projection and this parameter is projection. + { + const ObjectBase* prg; + prg=input->Get("projection"); + if(0!=prg) + { + bool suc=true; + ObjectList* list=new ObjectList; + list->PushBack(prg->Copy()); + p=operator()(list,&suc); + delete list; + if(suc) return p; // Projection found, return + } + prg=input->Get("proj"); + if(0!=prg) + { + bool suc=true; + ObjectList* list=new ObjectList; + list->PushBack(prg->Copy()); + p=operator()(list,&suc); + delete list; + if(suc) return p; // Projection found, return + } + } + + // 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())) goto case24; // No named parameter, go to another case + bool suc=false; + if(!(type1 || type2 || type3) ) goto fail; // No type - no projection + 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; // Unknown type - no projection + } + + // 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) {p.region=reg->Data(); suc=true;} + } + if(!suc) goto fail; // No such objects found + } + } + + // 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) 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 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); + 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 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); + 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 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; + 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"); + 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 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 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 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); + 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 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); + 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 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 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); + break; + } + default: goto fail; // Unknown projection + } + + // Try to find width parameter + 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 width + if(!ProjectionRealSize(p)) goto fail; // Something go wrong with determining real dimensions + } + return p; // All parameters setted + } + + case24: + if(input->Size()==1) // Case 2 + { + OBType list(input->At(0)); + if(list) p=operator()(list,issuc); + else *issuc=false; // Unknown argument + return p; + } + + // 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; + + +/* +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; + const ObjectBase *arg=input->At(0); + Base2Coord a(arg); + struct gmt_coord c; + bool suc=true; + + c=a(&suc); + if(a && suc) return new ObjectGMTCoord(c); + else return 0; +} + + +/* +Input: +1) Any number of arguments, search argument with ObjectGMTRegion type and copy it. +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|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, instead. +5) 4 or 5 parameters. 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) Any number of arguments, search argument with ObjectGMTProjection type and copy it. +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_Region on the value of this parameter, instead. +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; +} diff --git a/modules/gmt/modgmt_func.h b/modules/gmt/modgmt_func.h index f865b1c..e163901 100644 --- a/modules/gmt/modgmt_func.h +++ b/modules/gmt/modgmt_func.h @@ -1,7 +1,11 @@ #ifndef MODGMT_FUNC_H #define MODGMT_FUNC_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); #endif diff --git a/modules/gmt/modgmt_internals.cpp b/modules/gmt/modgmt_internals.cpp new file mode 100644 index 0000000..3cbe5b5 --- /dev/null +++ b/modules/gmt/modgmt_internals.cpp @@ -0,0 +1,131 @@ +#include +#include "common.h" +#include "modgmt_internals.h" + +std::string header,footer; + +// Workaround exit() in GMT_Call_Module. May need because return mode of gmt api is not very reliable +#if defined MODGMT_WORKAROUND_EXIT +const int GMTMODE=GMT_SESSION_NORMAL; +#else +const int GMTMODE=GMT_SESSION_NOEXIT; +#endif + +#if defined MODGMT_WORKAROUND_EXIT +// Exit handler. +static void gmtonexithandler(int ret, void* x) +{ + reinterpret_cast(x)->ret=ret; + close(reinterpret_cast(x)->fd); + pthread_exit(&(reinterpret_cast(x)->ret)); +} +#endif + +// This function call GMT_Call_Module in separate thread. It just a hack to workaround absence of callbacks. +static void* gmtworkthread(void* x) +{ + struct gmtworkthreadpars* p=reinterpret_cast(x); +#if defined MODGMT_WORKAROUND_EXIT + on_exit(gmtonexithandler,x); +#endif + GMT_Append_Option(p->api,GMT_Make_Option(p->api,'>',const_cast(("/dev/fd/"+std::to_string(p->fd)).c_str())),p->opts); + p->ret=GMT_Call_Module(p->api,p->module,GMT_MODULE_OPT,p->opts); +#if defined MODGMT_WORKAROUND_EXIT + exit(p->ret); +#endif + close(p->fd); + return &p->ret; +} + +// Wrapper for GMT_Call_Module, res is output. +int callgmtmodule(void *api, const char *module, struct GMT_OPTION *opts, std::string* res, gmt_filter filt, void* filtpar) +{ + int pipefd[2]; + pthread_t wthr; + struct gmtworkthreadpars p; + int *pret; + + pipe(pipefd); + p.api=api; + p.module=module; + p.opts=opts; + p.fd=pipefd[1]; + + pthread_create(&wthr,0,&gmtworkthread,&p); + res->erase(); + + (*filt)(pipefd[0],res,filtpar); + + res->shrink_to_fit(); + pthread_join(wthr,reinterpret_cast(&pret)); + + return *pret; +} +// Overloaded variant with opts as std::string +int callgmtmodule(void *api, const char *module, const std::string& opts, std::string* res, gmt_filter filt, void* filtpar) +{ + struct GMT_OPTION *gopts; + int ret; + gopts=str2options(api,opts); + ret=callgmtmodule(api,module,gopts,res,filt,filtpar); + GMT_Destroy_Options(api,&gopts); + return ret; +} +// Overloaded variant with opts as char* +int callgmtmodule(void *api, const char *module, const char* opts, std::string* res, gmt_filter filt, void* filtpar) +{ + struct GMT_OPTION *gopts; + int ret; + gopts=str2options(api,opts); + ret=callgmtmodule(api,module,gopts,res,filt,filtpar); + GMT_Destroy_Options(api,&gopts); + return ret; +} + +// Calculate real width and height of projection. If height!=0 recalculate width accordingly. +bool ProjectionRealSize(struct gmt_projection& p, double height) +{ + // Decart projection is special case + if(gmt_projection::XY==p.proj) + { + p.rwidth=p.width; + p.rheight=p.x.height; + return true; + } + + void* gmtapi; + int ret=0; + std::string wh; + double w,h; + double sw=p.width; + size_t pos; + + gmtapi=GMT_Create_Session("ProjectionRealSize",2,GMTMODE,0); + if(0==gmtapi) return false; + p.width=1.0; + ret=callgmtmodule(gmtapi,"mapproject",p.Value()+" -W",&wh); + p.width=sw; + GMT_Destroy_Session(gmtapi); + if(0!=ret) return false; + + pos=wh.find('\n'); + if(std::string::npos!=pos) wh.erase(pos); + pos=wh.find_first_of(" \t",0,2); + if(std::string::npos==pos) return false; + if(!str2double(std::string(wh.c_str(),wh.c_str()+pos),&w)) return false; + if(!str2double(wh.substr(pos+1),&h)) return false; + if(height>0.0) + { + p.width=height/h; + p.rwidth=w*height/h; + p.rheight=height; + } + else + { + p.width=sw/w; + p.rwidth=sw; + p.rheight=h*sw/w; + } + + return true; +} diff --git a/modules/gmt/modgmt_internals.h b/modules/gmt/modgmt_internals.h new file mode 100644 index 0000000..d2fac2a --- /dev/null +++ b/modules/gmt/modgmt_internals.h @@ -0,0 +1,51 @@ +#ifndef MODGMT_INTERNALS_H +#define MODGMT_INTERNALS_H +#include +#include +#include "filters.h" +#include "modgmt_structs.h" + +// here we save header and footer of gmt-produced eps files +extern std::string header,footer; +extern const int GMTMODE; + +// Parameters for working thread +struct gmtworkthreadpars +{ + void* api; + const char* module; + struct GMT_OPTION* opts; + int fd; + int ret; +}; + +// Calling GMT module with filtering +int callgmtmodule(void *api, const char *module, struct GMT_OPTION *opts, std::string* res, gmt_filter filt=gmt_filter_default, void* filtpar=0); +int callgmtmodule(void *api, const char *module, const std::string& opts, std::string* res, gmt_filter filt=gmt_filter_default, void* filtpar=0); +int callgmtmodule(void *api, const char *module, const char* opts, std::string* res, gmt_filter filt=gmt_filter_default, void* filtpar=0); + +// Workaround non-const pointer in GMT_Create_Options +inline struct GMT_OPTION* str2options(void *api, const char* str, size_t size=0) +{ + static char default_gmt_options[]="--GMT_HISTORY=f --PS_LINE_CAP=round --PS_LINE_JOIN=round --GMT_COMPATIBILITY=5 --PS_PAGE_ORIENTATION=portrait --PS_IMAGE_COMPRESS=deflate,9 --PROJ_LENGTH_UNIT=cm"; + char* t; + if(0==size) t=strdup(str); + else + { + t=static_cast(malloc(size+1)); + memcpy(t,str,size+1); + } + struct GMT_OPTION* opts=GMT_Create_Options(api,0,t); + GMT_Append_Option(api,GMT_Create_Options(api,0,default_gmt_options),opts); + free(t); + return opts; +} +inline struct GMT_OPTION* str2options(void *api, const std::string& str) +{ + return str2options(api,str.data(),str.length()); +} + +// Calculate real width and height of projection. If height!=0 recalculate width accordingly. +bool ProjectionRealSize(struct gmt_projection& p, double height=0.0); + +#endif