From f34ab7676c8cc0852ea3f4fb61137385ad165e17 Mon Sep 17 00:00:00 2001 From: Michael Uleysky Date: Mon, 15 Feb 2016 12:02:27 +1000 Subject: [PATCH] Gmt module: Add function Map. --- modules/gmt/modgmt.cpp | 2 + modules/gmt/modgmt_filter_headfoot.l | 10 +- modules/gmt/modgmt_map.cpp | 223 +++++++++++++++++++++++++++ modules/gmt/modgmt_map.h | 34 ++++ modules/gmt/modgmt_objects.cpp | 4 +- modules/gmt/modgmt_objects.h | 9 -- modules/gmt/modgmt_structs.h | 11 ++ 7 files changed, 277 insertions(+), 16 deletions(-) create mode 100644 modules/gmt/modgmt_map.cpp create mode 100644 modules/gmt/modgmt_map.h diff --git a/modules/gmt/modgmt.cpp b/modules/gmt/modgmt.cpp index 93be950..a48f3f0 100644 --- a/modules/gmt/modgmt.cpp +++ b/modules/gmt/modgmt.cpp @@ -2,6 +2,7 @@ #include "modgmt_func.h" #include "modgmt_internals.h" #include "modgmt_gsfuncs.h" +#include "modgmt_map.h" // Initialisation function int gmt_module_init(void* p) @@ -43,6 +44,7 @@ int gmt_module_init(void* p) RegisterFunction("Font",GMT_Type); RegisterFunction("Shift",GMT_LayerShift); RegisterFunction("DrawFrame",GMT_DrawFrame); + RegisterFunction("Map",GMT_Map); CheckGhostscriptAbilities(); // Calculating bounding box is critical diff --git a/modules/gmt/modgmt_filter_headfoot.l b/modules/gmt/modgmt_filter_headfoot.l index dc1c1c5..586b19f 100644 --- a/modules/gmt/modgmt_filter_headfoot.l +++ b/modules/gmt/modgmt_filter_headfoot.l @@ -35,12 +35,12 @@ %% %%EndComments\n BEGIN(NOTCOMMENTS); %%PageTrailer\n yyextra->append(yytext,yyleng); BEGIN(ENDFOOTER); -\n%%Page:.*\n yyextra->append("\n",1); -\n%%BeginPageSetup.*\n yyextra->append("\n",1); -\n%%EndPageSetup.*\n yyextra->append("\n",1); BEGIN(ENDHEAD); -\n%%BeginSetup.*\n yyextra->append("\n",1); BEGIN(SETUP); +%%Page:.*\n +%%BeginPageSetup.*\n +%%EndPageSetup.*\n BEGIN(ENDHEAD); +%%BeginSetup.*\n BEGIN(SETUP); .*\n yyextra->append(yytext,yyleng); -\n%%EndSetup.*\n yyextra->append("\n",1); BEGIN(NOTCOMMENTS); +%%EndSetup.*\n BEGIN(NOTCOMMENTS); .*\n <*><> yyterminate; %% diff --git a/modules/gmt/modgmt_map.cpp b/modules/gmt/modgmt_map.cpp new file mode 100644 index 0000000..125d6db --- /dev/null +++ b/modules/gmt/modgmt_map.cpp @@ -0,0 +1,223 @@ +#include +#include +#include "modgmt_map.h" +#include "modgmt_func.h" +#include "modgmt_gsfuncs.h" + +struct input_runtime +{ + std::list >::const_iterator ci,end; + size_t pos; +}; + +static int gs_bbox_callback(void *caller_handle, char *buf, int len) +{ + struct input_runtime* r=static_cast(static_cast(caller_handle)->indata); + size_t buflen=static_cast(len); + size_t inbuf=0; + + if(r->ci==r->end) return 0; + while(inbuf!=buflen) + { + if((*r->ci)->length()-r->pos>=buflen-inbuf) // Remainder of string greater or equal remainder of buffer + { + // Just copy part of string to buffer + memcpy(buf+inbuf,(*r->ci)->c_str()+r->pos,buflen-inbuf); + r->pos+=buflen-inbuf; + inbuf=buflen; + } + else // Remainder of string lesser then remainder of buffer + { + // Copy remainder of string to buffer + memcpy(buf+inbuf,(*r->ci)->c_str()+r->pos,(*r->ci)->length()-r->pos); + inbuf+=(*r->ci)->length()-r->pos; + // Go to next string + r->ci++; r->pos=0; + if(r->ci==r->end) break; // No more strings, leave. + } + } + + return inbuf; +} + + +// Creating eps map from sequence of layers +/* +Input: +If first argument is a string, then this string is a title of map (%%Title parameter in eps file). +Other arguments are sequence of pairs and layers. +Pairs can be x, y, xr, yr, xg, yg, xgr, ygr. +x and y set shift for next layer in cm. This local shift overrides global (setted by xg, yg). +xr and yr do the same but respect to global shift. +xg and yg set global shift (for all subsequent layers). +xgr and ygr add corresponding values to global shift. +Resulted shift is added to own shift of layer (setted by function LayerShift). +*/ +ObjectBase* GMT_Map(const ObjectList* input) +{ + const ObjectList* list=input; + auto size=list->Size(); + if(0==size) return 0; + decltype(size) pos=0; + std::string title; + std::stack > lstack; + bool first=true; + double xg,yg,x,y; + bool xislocal=false, yislocal=false; + std::list > data; + + xg=yg=x=y=0.0; + + // FIXME: Workaround ghostscript bug 202735 + // See http://bugs.ghostscript.com/show_bug.cgi?id=202735 + data.emplace_back(new std::string("4000 4000 translate")); + data.emplace_back(new std::string(header)); + while(pos l(list->At(pos)); + // Descending + if(l) + { + if(0==l->Size()) goto next; + lstack.push(std::make_pair(list,pos)); + list=l; + pos=0; + size=l->Size(); + continue; + } + } + + // Check if first argument is string + if(first) + { + first=false; + OBType s(list->At(pos)); + if(s) + { + title=s->Value(); + goto next; + } + } + + // Check if argument is pair + { + OBType p(list->At(pos)); + if(p) + { + std::string name=p->Name(); + tolower(name); + Base2Double d(p->Value()); + bool suc=true; + double val=d(&suc); + if(!suc) goto fail; // Conversion failed + suc=false; + if("x"==name) {suc=true; xislocal=true; x=val;} + if("y"==name) {suc=true; yislocal=true; y=val;} + if("xr"==name) {suc=true; xislocal=true; x=val+xg;} + if("yr"==name) {suc=true; yislocal=true; y=val+yg;} + if("xg"==name) {suc=true; xg=val;} + if("yg"==name) {suc=true; yg=val;} + if("xgr"==name) {suc=true; xg+=val;} + if("ygr"==name) {suc=true; yg+=val;} + if(!suc) goto fail; // Unknown name + goto next; + } + } + + // Check if argument is layer + { + OBType l(list->At(pos)); + if(l) + { + struct gmt_layer layer=l->Data(); + layer.shiftx+=(xislocal?x:xg); + layer.shifty+=(yislocal?y:yg); + xislocal=yislocal=false; + if(layer.Shifted()) + { + data.emplace_back(new std::string(layer.BeginShift())); + data.emplace_back(layer.data); + data.emplace_back(new std::string(layer.EndShift())); + } + else data.emplace_back(layer.data); + goto next; + } + } + + goto fail; // Unknown type of argument + next: + pos++; + // Ascending + if(pos==size && !lstack.empty()) + { + list=lstack.top().first; + pos=lstack.top().second; + lstack.pop(); + size=list->Size(); + goto next; + } + } + data.emplace_back(new std::string(footer)); + + // Calculate bounding box + int64_t bblx,bbly,bbrx,bbry; + { + std::string bboxes; + struct input_runtime in={data.begin(),data.end(),0}; + int ret; + + ret=GhostRun("-sDEVICE=bbox",gs_bbox_callback,&in,0,&bboxes,0); // Bounding box is writed on stderr + if(0!=ret) goto fail; // Something wrong + + std::smatch m; + std::smatch::const_iterator ci; + std::regex r("%%BoundingBox:[[:space:]]+(-?[0-9]+)[[:space:]]+(-?[0-9]+)[[:space:]]+(-?[0-9]+)[[:space:]]+(-?[0-9]+)"); + bool ok=true; + + std::regex_search(bboxes,m,r); + if(5!=m.size()) goto fail; // This is strange and scary + ci=m.cbegin(); + ci++; // Skip full match + + ok=ok && str2int(*ci++,&bblx); + ok=ok && str2int(*ci++,&bbly); + ok=ok && str2int(*ci++,&bbrx); + ok=ok && str2int(*ci++,&bbry); + if(!ok) goto fail; // Unexpected! + // FIXME: Workaround ghostscript bug 202735 + // We add 5 points to each margin because ghostscript does'nt count linewidths when calculate bounding box + bblx-=4005; + bbly-=4005; + bbrx-=3995; + bbry-=3995; + } + data.pop_front(); + + // Creating eps file + { + std::string curtime; + { + const size_t bufsize=1024; + char buf[bufsize]; + time_t sec; + struct tm t; + + sec=time(0); + localtime_r(&sec,&t); + curtime.assign(buf, strftime(buf,bufsize,"%F %T %Z",&t)); + } + std::string* eps=new std::string("%!PS-Adobe-3.0 EPSF-3.0\n%%BoundingBox: "); + *eps+=ToString(bblx)+" "+ToString(bbly)+" "+ToString(bbrx)+" "+ToString(bbry); + *eps+="\n%%Title: "+(title.empty()?std::string("untitled"):title); + *eps+="\n%%Creator: gmt_makemap\n%%CreationDate: "+curtime; + *eps+="\n%%LanguageLevel: 2\n%%Orientation: Portrait\n%%EndComments\n"; + for(auto d: data) *eps+=*d; + return new ObjectGMTMap(eps,bblx,bbly,bbrx,bbry); + } + + + fail: + return 0; +} diff --git a/modules/gmt/modgmt_map.h b/modules/gmt/modgmt_map.h new file mode 100644 index 0000000..b91f8fc --- /dev/null +++ b/modules/gmt/modgmt_map.h @@ -0,0 +1,34 @@ +#ifndef MODGMT_MAP_H +#define MODGMT_MAP_H +#include "common.h" + +class ObjectGMTMap: public ObjectBase +{ + std::shared_ptr data; + int64_t bblx,bbly,bbrx,bbry; + + ObjectGMTMap(const ObjectGMTMap* p):data(p->data),bblx(p->bblx),bbly(p->bbly),bbrx(p->bbrx),bbry(p->bbry) {}; + +public: + ObjectGMTMap(std::string* s, int nbblx, int nbbly, int nbbrx, int nbbry):data(s),bblx(nbblx),bbly(nbbly),bbrx(nbbrx),bbry(nbbry) {}; + + // Pure virtual overrides + ObjectBase* Copy() const override {return new ObjectGMTMap(this);} + bool Print() const override + { + COUT(NORMAL)<size(); + return reinterpret_cast(data->data()); + } +}; + +// Creating eps map from set of layers +ObjectBase* GMT_Map(const ObjectList* input); + +#endif + diff --git a/modules/gmt/modgmt_objects.cpp b/modules/gmt/modgmt_objects.cpp index 125d65a..b9ee173 100644 --- a/modules/gmt/modgmt_objects.cpp +++ b/modules/gmt/modgmt_objects.cpp @@ -13,8 +13,8 @@ template<> const int8_t* ObjectGMTLayer::Blob(size_t* size) const { if(s.Shifted()) { - std::string b="V "+ToString(cm2GMT(s.shiftx))+" "+ToString(cm2GMT(s.shifty))+" T\n"; - std::string e="U\n"; + std::string b=s.BeginShift(); + std::string e=s.EndShift(); int8_t* p=new int8_t[b.size()+s.data->size()+e.size()]; memcpy(p,b.data(),b.size()); memcpy(p+b.size(),s.data->data(),s.data->size()); diff --git a/modules/gmt/modgmt_objects.h b/modules/gmt/modgmt_objects.h index 6e8e124..c802886 100644 --- a/modules/gmt/modgmt_objects.h +++ b/modules/gmt/modgmt_objects.h @@ -8,15 +8,6 @@ #include "common.h" #include "modgmt_structs.h" -// Centimeters to GMT points scale factor -// 1 inch = 72 pt = 2.54 cm --> 1 cm = 72/2.54 pt -// GMT's own scaling factor is 0.06 -inline static double cm2GMT(double cm) -{ - static const double scale=(72.0/2.54)/0.06; - return cm*scale; -} - template class ObjectGMTClass: public ObjectBase { diff --git a/modules/gmt/modgmt_structs.h b/modules/gmt/modgmt_structs.h index e79b6f6..a37d4ae 100644 --- a/modules/gmt/modgmt_structs.h +++ b/modules/gmt/modgmt_structs.h @@ -6,6 +6,15 @@ #include "common.h" #include "modgmt_colornames.h" +// Centimeters to GMT points scale factor +// 1 inch = 72 pt = 2.54 cm --> 1 cm = 72/2.54 pt +// GMT's own scaling factor is 0.06 +inline static double cm2GMT(double cm) +{ + static const double scale=(72.0/2.54)/0.06; + return cm*scale; +} + // Helper template for checking double numbers acquired from Source with some Policies. // Main definition, never instantiated template @@ -807,5 +816,7 @@ struct gmt_layer: public gmt_struct std::string Value() const {return creator+(Shifted()?("("+ToString(shiftx)+"x"+ToString(shifty)+")"):"");} bool Shifted() const {return shiftx!=0.0 || shifty!=0.0;} + std::string BeginShift() const {return (Shifted()?("V "+ToString(cm2GMT(shiftx))+" "+ToString(cm2GMT(shifty))+" T\n"):"");} + std::string EndShift() const {return (Shifted()?"U\n":"");} }; #endif