Compare commits

..

32 Commits

Author SHA1 Message Date
Michael Uleysky 6c6b52f5b2 Gmt module: Fix bad assignment to uninitialised pointer in previous commit. 8 years ago
Michael Uleysky a47ff9fdcc Gmt module: Fix warnings of gcc6. 8 years ago
Michael Uleysky 995da9cf3b Gmt module: regenerate more compact hash table for color names. 8 years ago
Michael Uleysky 0b4ed61542 Gmr module: Add function ToModel in gmt_color structure. 8 years ago
Michael Uleysky 4709ebe1fb Gmt module: Functions Map, Convert2PNG and Convert2JPEG now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky 6a70f5ffbf Gmt module: Function DrawFrame now using new parameter reading scheme and error reporting 8 years ago
Michael Uleysky 0a92d40792 Gmt module: Function LayerShift now using new parameter reading scheme and error reporting 8 years ago
Michael Uleysky 920a240bd0 Gmt module. Error reporting in GMT_Type template. 8 years ago
Michael Uleysky c27463ba62 Gmt module: Conversion function for GMTFont now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky 750b1aa8ce Gmt module: Remove another std::move from Parameter class. 8 years ago
Michael Uleysky 8d843a7607 Gmt module: Fix error in gmt_font::Convert(). 8 years ago
Michael Uleysky cbfd462ff0 Gmt module: Conversion function for GMTPen now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky d02fd289f8 Gmt module: Functions ColorGray, ColorRGB, ColorHSV, ColorCMYK now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky 591bc837eb Gmt module: Fix error in conversion function from string to gmt_dash. 8 years ago
Michael Uleysky b3c628f704 Tests: Add test for creating different GMTProjection's. 8 years ago
Michael Uleysky 55eef96865 Gmt module: Rename incorrect template name Base2NonNeg to right Base2Pos. 8 years ago
Michael Uleysky 41d13f16bd Gmt module: Conversion function for GMTColor now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky a3a6fa88f9 Gmt module: Error reporting in all Convert functions. 8 years ago
Michael Uleysky 59dfac220a Gmt module: Conversion function for GMTProjection now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky 5b466460d6 Gmt module: Conversion function for GMTRegion now using new parameter reading scheme and error reporting. 8 years ago
Michael Uleysky 8a088d14c3 Gmt module: Rewrite Conver2Struct and its derivatives in form appropriate for new parameters parsing scheme. 8 years ago
Michael Uleysky e485d26369 Gmt module. Add error reporting in function Convert of gmt_coord. 8 years ago
Michael Uleysky 6e9dfbf0e7 Gmt module: Using template-based projections names. Fix errors in string representations of projections. 8 years ago
Michael Uleysky 218d8c0f00 Gmt module: Add possibility for ParsePositionalParameters to parse only part of list. 8 years ago
Michael Uleysky 3d1f1ea499 Gmt module: Fix segfault. 8 years ago
Michael Uleysky 3be794e42c Gmt module: Simplify code. 8 years ago
Michael Uleysky 8a24425a16 Gmt module: generate parameter name from template. 8 years ago
Michael Uleysky 4578ce31ad Gmt module: Add templates and classes for parsing parameters from ObjectList. 8 years ago
Michael Uleysky 9c0776b92e Gmt module: Cleanup modgmt_structs.h 8 years ago
Michael Uleysky 4e9addae93 Simple tests system. 8 years ago
Michael Uleysky c59ac16589 Fix false error if more then one function have same name. 8 years ago
Michael Uleysky 5827eebde3 Add header guard in builtin.h. 8 years ago
  1. 34
      dotests
  2. 3
      include/builtin.h
  3. 34
      maketest
  4. 1922
      modules/gmt/modgmt_colornames.cpp
  5. 56
      modules/gmt/modgmt_colornames.h
  6. 388
      modules/gmt/modgmt_func.cpp
  7. 1505
      modules/gmt/modgmt_func.h
  8. 5
      modules/gmt/modgmt_gsfuncs.cpp
  9. 5
      modules/gmt/modgmt_internals.cpp
  10. 355
      modules/gmt/modgmt_map.cpp
  11. 23
      modules/gmt/modgmt_map.h
  12. 40
      modules/gmt/modgmt_objects.cpp
  13. 297
      modules/gmt/modgmt_param.h
  14. 292
      modules/gmt/modgmt_strcomp.h
  15. 223
      modules/gmt/modgmt_structs.h
  16. 2
      src/object.cpp
  17. 11
      tests/GMTCoord
  18. 8
      tests/GMTCoord_e1
  19. 18
      tests/GMTProjection
  20. 10
      tests/GMTRegion1
  21. 9
      tests/GMTRegion2
  22. 13
      tests/GMTRegion3
  23. 8
      tests/GMTRegion_e1

34
dotests

@ -0,0 +1,34 @@
#!/bin/bash
MAKEMAP="${1:-/tmp/save/build/src/makemap}"
TPATH=tests
SCRW=`tput cols`
set -o pipefail
for n in "$TPATH"/*; do
status=`cat $n|grep "# Status: "|sed "s/# Status: //"`
desc=`cat $n|grep "# Description: "|sed "s/# Description: //"`
hash=`cat $n|grep "# Output hash: "|sed "s/.*: //"`
while [ ${#desc} -lt $((SCRW-5)) ]; do desc+=" "; done
echo -n "$desc"
t=`mktemp`
grep -v -E "# .*: " "$n">$t
thash=`$MAKEMAP "$t" 2>&1 | sha256sum|sed "s/ .*//"`
ret=$?
if [ "$status" == "ok" ]; then
if [ "$hash" == "$thash" -a "$ret" == "0" ]; then
echo -e "\033[32mOk \033[0m"
else
echo -e "\033[31mFail\033[0m"
fi
else
if [ "$hash" == "$thash" -a "$ret" != "0" ]; then
echo -e "\033[32mOk \033[0m"
else
echo -e "\033[31mFail\033[0m"
fi
fi
rm $t
done

3
include/builtin.h

@ -1,3 +1,5 @@
#ifndef BUILTIN_H
#define BUILTIN_H
#include <cmath>
#include "object.h"
@ -114,3 +116,4 @@ const ObjectBase* Arifm1(const ObjectList* input)
return new ObjectError("Arifmetic unary operator","unknown error"); // Impossible case
}
#endif

34
maketest

@ -0,0 +1,34 @@
#!/bin/bash
name="$1"
desc="$2"
TLOC="${3:-/tmp/save/test}"
MAKEMAP="${4:-/tmp/save/build/src/makemap}"
if [ -f tests/"$name" ]; then
echo "Test $name already exist."
exit 1
fi
if [ ! -f "$TLOC" ]; then
echo "Configuration file $TLOC not found."
exit 2
fi
if [ ! -x "$MAKEMAP" ]; then
echo "Can't exec file $MAKEMAP."
exit 3
fi
echo "# Description: $desc" >tests/$name
if "$MAKEMAP" "$TLOC" &>/dev/null; then
hash=`"$MAKEMAP" "$TLOC" 2>/dev/null|sha256sum|sed "s/ .*//"`
status=ok
else
hash=`"$MAKEMAP" "$TLOC" 2>&1|sha256sum|sed "s/ .*//"`
status=fail
fi
echo "# Status: $status" >>tests/$name
echo "# Output hash: $hash" >>tests/$name
cat "$TLOC" >>tests/$name

1922
modules/gmt/modgmt_colornames.cpp

File diff suppressed because it is too large Load Diff

56
modules/gmt/modgmt_colornames.h

@ -13,38 +13,38 @@ public:
};
inline unsigned int
ColorHash::colornamehash ( const char *str, unsigned int len)
ColorHash::colornamehash (const char *str, unsigned int len)
{
static const unsigned short asso_values[] =
{
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 800, 25,
20, 5, 0, 845, 785, 780, 640, 620, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 80, 0, 686, 5, 0,
260, 0, 85, 770, 20, 950, 210, 95, 160, 195,
995, 335, 0, 0, 135, 425, 55, 863, 4084, 60,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 80, 0, 686,
5, 0, 260, 0, 85, 770, 20, 950, 210, 95,
160, 195, 995, 335, 0, 0, 135, 425, 55, 863,
4084, 60, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084,
4084, 4084, 4084, 4084, 4084, 4084
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 666, 27,
26, 23, 22, 789, 588, 587, 501, 436, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 61, 96, 222, 98, 22,
117, 22, 561, 559, 22, 708, 179, 301, 42, 101,
242, 46, 23, 22, 116, 171, 821, 809, 2728, 169,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 61, 96, 222,
98, 22, 117, 22, 561, 559, 22, 708, 179, 301,
42, 101, 242, 46, 23, 22, 116, 171, 821, 809,
2728, 169, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728, 2728,
2728, 2728, 2728, 2728, 2728, 2728
};
int hval = len;
int hval = len;
switch (hval)
{

388
modules/gmt/modgmt_func.cpp

@ -24,126 +24,111 @@ const ObjectBase* GMT_Footer(const ObjectList* input)
const ObjectBase* GMT_ColorGray(const ObjectList* input)
{
struct gmt_color c;
bool suc=true;
Base2RGB g(input,0);
Base2Transp t(input,1);
RPosPar<Base2RGB> g("gray");
OPosPar<Base2TransD> t("transparency",0.0);
ParsePositionalParameters params(input,g,t);
if(!params) return new ObjectError("ColorGray",params.Error());
c.model=gmt_color::GRAY;
c.gray=g(&suc);
c.transparency=t(&suc);
c.gray=g; c.transparency=t;
if(suc) return new ObjectGMTColor(c);
else return 0;
return new ObjectGMTColor(c);
}
const ObjectBase* GMT_ColorRGB(const ObjectList* input)
{
struct gmt_color c;
bool suc=true;
Base2RGB r(input,0),g(input,1),b(input,2);
Base2Transp t(input,3);
RPosPar<Base2RGB> r("red"),g("green"),b("blue");
OPosPar<Base2TransD> t("transparency",0.0);
ParsePositionalParameters params(input,r,g,b,t);
if(!params) return new ObjectError("ColorRGB",params.Error());
c.model=gmt_color::RGB;
c.r=r(&suc);
c.g=g(&suc);
c.b=b(&suc);
c.transparency=t(&suc);
c.r=r; c.g=g; c.b=b; c.transparency=t;
if(suc) return new ObjectGMTColor(c);
else return 0;
return new ObjectGMTColor(c);
}
const ObjectBase* GMT_ColorHSV(const ObjectList* input)
{
struct gmt_color c;
bool suc=true;
Base2Hue h(input,0);
Base2SV s(input,1),v(input,2);
Base2Transp t(input,3);
RPosPar<Base2Hue> h("hue");
RPosPar<Base2SV> s("saturation"), v("value");
OPosPar<Base2TransD> t("transparency",0.0);
ParsePositionalParameters params(input,h,s,v,t);
if(!params) return new ObjectError("ColorHSV",params.Error());
c.model=gmt_color::HSV;
c.hue=h(&suc);
c.saturation=s(&suc);
c.value=v(&suc);
c.transparency=t(&suc);
c.hue=h; c.saturation=s; c.value=v; c.transparency=t;
if(suc) return new ObjectGMTColor(c);
else return 0;
return new ObjectGMTColor(c);
}
const ObjectBase* GMT_ColorCMYK(const ObjectList* input)
{
struct gmt_color c;
bool suc=true;
Base2CMYK cyan(input,0),m(input,1),y(input,2),k(input,3);
Base2Transp t(input,4);
c.model=gmt_color::CMYK;
c.cyan=cyan(&suc);
c.magenta=m(&suc);
c.yellow=y(&suc);
c.black=k(&suc);
c.transparency=t(&suc);
if(suc) return new ObjectGMTColor(c);
else return 0;
struct gmt_color C;
RPosPar<Base2CMYK> c("cyan"),m("magenta"),y("yellow"),k("black");
OPosPar<Base2TransD> t("transparency",0.0);
ParsePositionalParameters params(input,c,m,y,k,t);
if(!params) return new ObjectError("ColorCMYK",params.Error());
C.model=gmt_color::CMYK;
C.cyan=c; C.magenta=m; C.yellow=y; C.black=k; C.transparency=t;
return new ObjectGMTColor(C);
}
// Shifting layer
/*
Input:
1) Three arguments, first is Layer, second and third are double. Interprets as new absolute position in cm.
2) Pairs list. Names are l (layer), x, y, xrel (xr), yrel (yr). Pair with name l may be absent, in this case search in list and using as layer object with ObjectGMTLayer type. x and y are absolute positions in cm, xrel and yrel are shift from current position. x (y) and xrel (yrel) are mutually exlusive, but x (y) and yrel (xrel) can be used simultaneously. If position for some axis is absent, then this position is unchanged.
2) Pairs list. Names are l (layer), x, y, relx (rx), rely (ry). Pair with name l may be absent, in this case search in list and using as layer object with ObjectGMTLayer type. x and y are absolute positions in cm, relx and rely are shift from current position. x (y) and relx (rely) are mutually exlusive, but x (y) and rely (relx) can be used simultaneously. If position for some axis is absent, then this position is unchanged.
*/
const ObjectBase* GMT_LayerShift(const ObjectList* input)
{
auto size=input->Size();
struct gmt_layer layer;
std::string err;
// Case 1
if(3==size)
{
Base2Layer l(input,0);
Base2Double x(input,1), y(input,2);
bool suc=true;
layer=l(&suc);
if(!suc) goto case2;
layer.shiftx=x(&suc);
layer.shifty=y(&suc);
if(!suc) goto case2;
RPosPar<Base2Layer> l("layer");
OPosPar<Base2DoubleD> x("x",0.0), y("y",0.0);
{ParsePositionalParameters params(input,l,x,y); if(!params) goto case2;} // Try case 2, if case 1 failed.
layer=l; layer.shiftx=x; layer.shifty=y;
return new ObjectGMTLayer(layer);
}
case2:
// Search layer for shifting
{
SearchParameter<struct gmt_layer> original(input,"layer","l");
bool suc=true;
layer=original(&suc);
if(!suc) goto fail; // Conversion failed or too many arguments
}
// Do shift
{
BaseM2Double xr(input,"xrel","xr"), yr(input,"yrel","yr");
Base2Double x(input,"x"), y(input,"y");
bool suc=true;
RNPar<Base2Layer> l("layer");
ONPar<Base2Double> x("x"),y("y"),rx("r[el]x"),ry("r[el]y");
{ParseNamedParameters params(input,l,x,y); if(!params) {err=params.Error(); goto fail;}} // Parsing error
// Check duplicate parameters
if( ( x.Exist() && xr.Exist() ) || ( y.Exist() && yr.Exist() ) ) goto fail;
if(x.Exist()) layer.shiftx=x(&suc);
if(y.Exist()) layer.shifty=y(&suc);
if(xr.Exist()) layer.shiftx+=x(&suc);
if(yr.Exist()) layer.shifty+=y(&suc);
if(!suc) goto fail;
if(x.Exist() && rx.Exist()) {err="Only one of "+x.Name()+" or "+rx.Name()+" can be specified."; goto fail;}
if(y.Exist() && ry.Exist()) {err="Only one of "+y.Name()+" or "+ry.Name()+" can be specified."; goto fail;}
// Do shift
if( x.Exist()) layer.shiftx=x;
if( y.Exist()) layer.shifty=y;
if(rx.Exist()) layer.shiftx+=rx;
if(ry.Exist()) layer.shifty+=ry;
}
return new ObjectGMTLayer(layer);
fail:
return 0;
return new ObjectError("LayerShift",err);
}
// Draw frame with tics
/*
Input: pairs list.
@ -161,169 +146,146 @@ framepen - pen for drawing frame. Default is 0.35,black.
ticklen - lenght of tick-marks in cm. Default is 0.18.
tickpen - pen for drawing ticks. Default is 0.3,black.
trans or transparency - transparency level, double from 0 to 100. Default is 0 (opaque).
transpmodel - transparency model, string. Choose from Color, ColorBurn, ColorDodge, Darken, Difference, Exclusion, HardLight, Hue, Lighten, Luminosity, Multiply, Normal, Overlay, Saturation, SoftLight, and Screen. Default is Normal.
x, xr or xrel - shift layer on x cm in horisontal direction. Default is 0.
y, yr or yrel - shift layer on y cm in vertical direction. Default is 0.
model - transparency model, string. Choose from Color, ColorBurn, ColorDodge, Darken, Difference, Exclusion, HardLight, Hue, Lighten, Luminosity, Multiply, Normal, Overlay, Saturation, SoftLight, and Screen. Default is Normal.
rx or relx - shift layer on x cm in horisontal direction. Default is 0.
ry or rely - shift layer on y cm in vertical direction. Default is 0.
*/
template<class O>
using SConvertor=Convert2Struct<std::string, O>;
template<class O>
using DConvertor=Convert2Struct<double, O>;
const ObjectBase* GMT_DrawFrame(const ObjectList* input)
{
std::string opts="-P -O -K ";
bool suc=true;
struct gmt_layer layer;
// Defaults
std::string defxtics="down", defytics="left", defint="auto", defdomain="pos", deftranspmodel="Normal";
bool defmark=false;
gmt_font deffont; deffont.Convert("12,Times-Roman,black");
double defoffset=0.18, defframewidth=0.1, defticklen=0.18;
gmt_pen defframepen, deftickpen; defframepen.Convert("0.35,black"); deftickpen.Convert("0.3,black");
{ // Get projection
BaseMT2Projection proj(input,"proj","projection");
layer.proj=proj(&suc);
if(!suc) goto fail;
opts+=layer.proj.Value()+" ";
}
{ // Get xint and yint
BaseMD2String xint(input,defint,"xint"), yint(input,defint,"yint");
double dx,dy;
std::string str;
struct XYIntVal // Representing intervals for tics intervals
{
bool isauto; double val;
XYIntVal():isauto(true),val(0.0) {} // Default is auto
};
str=xint(&suc);
if(suc && "auto"==str) dx=AutoStep(layer.proj.region.xb,layer.proj.region.xe);
else // No auto, get numeric value
{
Base2Double xint(input,"xint");
suc=true;
dx=xint(&suc);
if(!suc) goto fail; // Parameter exists, but conversion failed
}
class Base2TInt // Custom convertor class for xy tics intervals
{
public:
using ValueType=struct XYIntVal;
str=yint(&suc);
if(suc && "auto"==str) dy=AutoStep(layer.proj.region.yb,layer.proj.region.ye);
else // No auto, get numeric value
ValueType Convert(const ObjectBase* ob, bool* res, std::string& err)
{
Base2Double yint(input,"yint");
suc=true;
dy=yint(&suc);
if(!suc) goto fail; // Parameter exists, but conversion failed
ValueType ret;
ret.isauto=false;
OBTypeM<SConvertor,ObjectString> sp(ob);
OBTypeM<DConvertor,ObjectReal,ObjectInt,ObjectString> dp(ob);
// Check string "auto"
if(sp.Error()==OBTypeErr::OK)
{
std::string s=sp(res,std::ref(err));
if(!res) {err="Unknown error"; goto fail;} // Impossible case
tolower(s);
if("auto"==s) {ret.isauto=true; return ret;}
}
// Check double value
switch(dp.Error())
{
case(OBTypeErr::OK):
{
ret.val=dp(res,std::ref(err));
if(ret.val<=0.0) {err="Tics interval must be greater the zero"; break;}
return ret;
};
case(OBTypeErr::NULLPTR): {err="Can't convert zero ObjectBase pointer to something meaningfull"; break;}
case(OBTypeErr::TYPEMISMATCH): {err="Can't convert "+ob->Type()+" to double: type mismatch"; break;}
}
fail:
*res=false;
return ret;
}
opts+="-Bx"+ToString(dx)+" -By"+ToString(dy)+" ";
}
};
{ // Get xtics and ytics
BaseMD2String x(input,defxtics,"xtics"), y(input,defytics,"ytics");
std::string xtics,ytics;
std::string xaxis,yaxis;
xtics=x(&suc);
if(!suc) goto fail; // Conversion failed
if("none"==xtics) xaxis="sn";
if("down"==xtics) xaxis="Sn";
if("up"==xtics) xaxis="sN";
if("both"==xtics) xaxis="SN";
std::string err,fakerr;
std::string opts="-P -O -K ";
ytics=y(&suc);
if(!suc) goto fail; // Conversion failed
if("none"==ytics) yaxis="we";
if("left"==ytics) yaxis="We";
if("right"==ytics) yaxis="wE";
if("both"==ytics) yaxis="WE";
struct gmt_layer layer;
if(xaxis.empty() || yaxis.empty()) goto fail; // Unknown value of xtics and/or ytics
struct gmt_font deffont;
struct gmt_pen defframepen,deftickpen;
deffont.Convert("12,Times-Roman,black",fakerr);
defframepen.Convert("0.35,black",fakerr);
deftickpen.Convert("0.3,black",fakerr);
RNFPar<Base2Proj,ObjectGMTProjection> proj("projection");
ONPar<Base2TInt> xint("x[ ]interval"), yint("y[ ]interval");
ONPar<Base2StringD> xtics("x[ ]tics[ ]position","down"), ytics("y[ ]t[ics[ ]position","left");
ONPar<Base2StringD> domain("domain","pos");
ONPar<Base2BoolD> mark("mark",false);
ONPar<Base2FontD> font("font",deffont);
ONPar<Base2DoubleD> offset("offset",0.18);
ONPar<Base2NonNegD> framewidth("f[rame][ ]width",0.1);
ONPar<Base2PenD> framepen("f[rame][ ]pen",defframepen), tickpen("t[icks][ ]pen",deftickpen);
ONPar<Base2DoubleD> ticklen("t[icks][ ]length",0.18);
ONPar<Base2TransD> transp("transparency",0.0);
ONPar<Base2StringD> transpmodel("model","Normal");
ONPar<Base2DoubleD> rx("r[el]x",0.0),ry("r[el]y",0.0);
ParseNamedParameters params(input,proj,xint,yint,xtics,ytics,domain,mark,font,offset,framewidth,framepen,tickpen,ticklen,transp,transpmodel,rx,ry); if(!params) {err=params.Error(); goto fail;} // Error parsing parameters
layer.proj=proj; // Get projection
opts+=layer.proj.Value()+" ";
{ // Get tics intervals
XYIntVal dx=xint,dy=yint;
opts+="-Bx"+ToString(dx.isauto?AutoStep(layer.proj.region.xb,layer.proj.region.xe):dx.val)+" -By"+ToString(dy.isauto?AutoStep(layer.proj.region.yb,layer.proj.region.ye):dy.val)+" ";
}
{ // Get xtics and ytics positions
std::string xt=xtics, yt=ytics;
std::string xaxis, yaxis;
TemplateComparator none("none"), both("both");
TemplateComparator up("up"), down("down");
TemplateComparator left("left"), right("right");
tolower(xt); tolower(yt);
if(none.Compare(xt)) xaxis="sn";
if(down.Compare(xt)) xaxis="Sn";
if( up.Compare(xt)) xaxis="sN";
if(both.Compare(xt)) xaxis="SN";
if( none.Compare(yt)) yaxis="we";
if( left.Compare(yt)) yaxis="We";
if(right.Compare(yt)) yaxis="wE";
if( both.Compare(yt)) yaxis="WE";
if(xaxis.empty()) {err="Incorrect xtics position "+xtics.Value(); goto fail;} // Unknown value of xtics
if(yaxis.empty()) {err="Incorrect xtics position "+ytics.Value(); goto fail;} // Unknown value of ytics
opts+="-B"+xaxis+yaxis+" ";
}
{ // Get domain and mark
BaseMD2String d(input,defdomain,"domain");
BaseMD2Bool m(input,defmark,"mark");
std::string dom=domain;
std::string format;
TemplateComparator p("positive"),n("negative"),c("centered");
tolower(dom);
std::string domain=d(&suc);
if(!suc) goto fail; // Conversion failed
if("pos"==domain) format="+ddd:mm:ss";
if("neg"==domain) format="-ddd:mm:ss";
if("center"==domain) format="ddd:mm:ss";
if(format.empty()) goto fail; // Unknown domain
if(m(&suc)) format+="F";
if(!suc) goto fail; // Conversion failed
if(p.Compare(dom)) format="+ddd:mm:ss";
if(n.Compare(dom)) format="-ddd:mm:ss";
if(c.Compare(dom)) format="ddd:mm:ss";
if(format.empty()) {err="Incorrect domain value "+domain.Value(); goto fail;} // Unknown domain
if(mark) format+="F";
opts+="--FORMAT_GEO_MAP="+format+" ";
}
{ // Get font
BaseMD2Font f(input,deffont,"font");
gmt_font font;
font=f(&suc);
if(!suc) goto fail; // Conversion failed
opts+="--FONT_ANNOT_PRIMARY="+font.Value()+" ";
}
{ // Get offset
BaseMD2Double o(input,defoffset,"offset");
double offset;
offset=o(&suc);
if(!suc) goto fail; // Conversion failed
opts+="--MAP_ANNOT_OFFSET_PRIMARY="+ToString(offset)+"c ";
}
{ // Get framewidth
SearchParameterWDef<double,false,PMin<0> > f(input,defframewidth,"framewidth");
double framewidth;
framewidth=f(&suc);
if(!suc) goto fail; // Conversion failed
opts+="--MAP_FRAME_WIDTH="+ToString(framewidth)+"c ";
}
{ // Get framepen
BaseMD2Pen p(input,defframepen,"framepen");
gmt_pen framepen;
framepen=p(&suc);
if(!suc) goto fail; // Conversion failed
opts+="--MAP_FRAME_PEN="+framepen.Value()+" ";
}
{ // Get ticklen
BaseMD2Double t(input,defticklen,"ticklen","ticklength");
double ticklen;
ticklen=t(&suc);
if(!suc) goto fail; // Conversion failed
opts+="--MAP_TICK_LENGTH_PRIMARY="+ToString(ticklen)+"c ";
}
{ // Get tickpen
BaseMD2Pen t(input,deftickpen,"tickpen");
gmt_pen tickpen;
tickpen=t(&suc);
if(!suc) goto fail; // Conversion failed
opts+="--MAP_TICK_PEN_PRIMARY="+tickpen.Value()+" ";
}
{ // Get transparency
Base2Transp t(input,"trans","transp","transparency");
double transp;
transp=t(&suc);
if(!suc) goto fail; // Conversion failed
if(transp!=0) opts+="-t"+ToString(transp)+" ";
}
opts+="--FONT_ANNOT_PRIMARY="+font.Value().Value()+" "; // Get font
opts+="--MAP_ANNOT_OFFSET_PRIMARY="+ToString(offset.Value())+"c "; // Get offset
opts+="--MAP_FRAME_WIDTH="+ToString(framewidth.Value())+"c "; // Get framewidth
opts+="--MAP_FRAME_PEN="+framepen.Value().Value()+" "; // Get framepen
opts+="--MAP_TICK_LENGTH_PRIMARY="+ToString(ticklen.Value())+"c "; // Get ticklen
opts+="--MAP_TICK_PEN_PRIMARY="+tickpen.Value().Value()+" "; // Get tickpen
if(transp!=0) opts+="-t"+ToString(transp.Value())+" "; // Get transparency
opts+="--PS_TRANSPARENCY="+transpmodel.Value()+" "; // Get transparency model
{ // Get transpmodel
BaseMD2String tm(input,deftranspmodel,"transpmodel","transparencymodel");
std::string transpmodel;
transpmodel=tm(&suc);
if(!suc) goto fail; // Conversion failed
opts+="--PS_TRANSPARENCY="+transpmodel+" ";
}
{ // Get x, y
BaseMD2Double x(input,0.0,"x","xr","xrel"), y(input,0.0,"y","yr","yrel");
layer.shiftx=x(&suc);
layer.shifty=y(&suc);
if(!suc) goto fail; // Conversion failed
}
// Get shift
layer.shiftx=rx;
layer.shifty=ry;
{ // Calling psbasemap
void* gmtapi;
@ -342,5 +304,5 @@ const ObjectBase* GMT_DrawFrame(const ObjectList* input)
return new ObjectGMTLayer(layer);
fail:
return 0;
return new ObjectError("DrawFrame",err);
}

1505
modules/gmt/modgmt_func.h

File diff suppressed because it is too large Load Diff

5
modules/gmt/modgmt_gsfuncs.cpp

@ -82,6 +82,7 @@ static int GhostRun(const std::string& opts, struct gs_runtime* r, std::string*
{
struct gsworkthreadpars wp;
int* pret;
int ret=0;
wp.r=r;
wp.input_callback=input;
@ -96,7 +97,7 @@ static int GhostRun(const std::string& opts, struct gs_runtime* r, std::string*
ssize_t br;
char buffer[4096];
pipe(pipefd);
if(0!=pipe(pipefd)) {ret=-1; goto end;}
wp.fd=pipefd[1];
out->erase();
@ -112,6 +113,8 @@ static int GhostRun(const std::string& opts, struct gs_runtime* r, std::string*
}
else pret=reinterpret_cast<int*>(gsworkthread(&wp));
end:
if(0!=ret) return ret;
return *pret;
}

5
modules/gmt/modgmt_internals.cpp

@ -44,8 +44,9 @@ int callgmtmodule(void *api, const char *module, struct GMT_OPTION *opts, std::s
pthread_t wthr;
struct gmtworkthreadpars p;
int *pret;
int ret=0;
pipe(pipefd);
if(0!=pipe(pipefd)) {ret=-1; goto end;}
p.api=api;
p.module=module;
p.opts=opts;
@ -59,6 +60,8 @@ int callgmtmodule(void *api, const char *module, struct GMT_OPTION *opts, std::s
res->shrink_to_fit();
pthread_join(wthr,reinterpret_cast<void**>(&pret));
end:
if(0!=ret) return ret;
return *pret;
}
// Overloaded variant with opts as std::string

355
modules/gmt/modgmt_map.cpp

@ -61,13 +61,9 @@ Resulted shift is added to own shift of layer (setted by function LayerShift).
*/
const 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 err;
std::string title;
std::stack<std::pair<const ObjectList*,decltype(pos)> > lstack;
bool first=true;
double xg,yg,x,y;
bool xislocal=false, yislocal=false;
std::list<std::shared_ptr<std::string> > data;
@ -78,45 +74,29 @@ const ObjectBase* GMT_Map(const ObjectList* input)
// 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<size)
for(auto i=input->begin(); i!=input->end(); ++i)
{
// Check if next argument is list
{
OBType<ObjectList> 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)
if(input->begin()==i)
{
first=false;
OBType<ObjectString> s(list->At(pos));
OBType<ObjectString> s(*i);
if(s)
{
title=s->Value();
goto next;
continue;
}
}
// Check if argument is pair
{
OBType<ObjectPair> p(list->At(pos));
OBType<ObjectPair> p(*i);
if(p)
{
std::string name=p->Name();
tolower(name);
Base2Double d(p->Value());
Base2Double d;
bool suc=true;
double val=d(&suc);
double val=d.Convert(p->Value(),&suc,err);
if(!suc) goto fail; // Conversion failed
suc=false;
if("x"==name) {suc=true; xislocal=true; x=val;}
@ -127,14 +107,14 @@ const ObjectBase* GMT_Map(const ObjectList* input)
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;
if(!suc) {err="Unknown parameter name: "+p->Name(); goto fail;} // Unknown name
continue;
}
}
// Check if argument is layer
{
OBType<ObjectGMTLayer> l(list->At(pos));
OBType<ObjectGMTLayer> l(*i);
if(l)
{
struct gmt_layer layer=l->Data();
@ -148,22 +128,11 @@ const ObjectBase* GMT_Map(const ObjectList* input)
data.emplace_back(new std::string(layer.EndShift()));
}
else data.emplace_back(layer.data);
goto next;
continue;
}
}
err="Unknown argument type: "+i->Type();
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));
@ -175,7 +144,7 @@ const ObjectBase* GMT_Map(const ObjectList* input)
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
if(0!=ret) {err="Can't determine BoundingBox"; goto fail;} // Something wrong
std::smatch m;
std::smatch::const_iterator ci;
@ -183,7 +152,7 @@ const ObjectBase* GMT_Map(const ObjectList* input)
bool ok=true;
std::regex_search(bboxes,m,r);
if(5!=m.size()) goto fail; // This is strange and scary
if(5!=m.size()) {err="Something go wrong, this is strange and scary"; goto fail; }// This is strange and scary
ci=m.cbegin();
ci++; // Skip full match
@ -191,7 +160,7 @@ const ObjectBase* GMT_Map(const ObjectList* input)
ok=ok && str2int(*ci++,&bbly);
ok=ok && str2int(*ci++,&bbrx);
ok=ok && str2int(*ci++,&bbry);
if(!ok) goto fail; // Unexpected!
if(!ok) {err="Unexpected error!"; 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;
@ -224,7 +193,7 @@ const ObjectBase* GMT_Map(const ObjectList* input)
}
fail:
return 0;
return new ObjectError("Map",err);
}
// Creating pdf from eps
@ -235,39 +204,24 @@ Optionally, resolution can be specified by pair with name resolution, res or r a
*/
const ObjectBase* GMT_Convert2PDF(const ObjectList* input)
{
if(!gs_abilities.havepdf) return 0; // No pdf support
double r;
bool suc=true;
const ObjectGMTMap* map=0;
{
SearchParameterWDefO<double,DoubleDefaultVal<720>,false,PMin<1> > res(input,"r","res","resolution");
r=res(&suc);
if(!suc) goto fail; // Error
}
std::string err;
RNFPar<SearchGMTMap,ObjectGMTMap> map("map");
ONPar<Base2PosD> r("resolution",720);
ParseNamedParameters params(input,map,r);
const ObjectGMTMap* m=map;
{
for(ObjectList::IndexType i=0;i<input->Size();i++)
{
OBType<ObjectGMTMap> m(input->At(i));
if(m)
{
if(0!=map) goto fail; // Duplicate
map=m;
}
}
if(0==map) goto fail; // Map not found
}
if(!gs_abilities.havepdf) {err="No PDF support in Ghostscript"; goto fail;} // No pdf support
if(!params) {err=params.Error(); goto fail;} // Parsing error
{
std::string* out=new std::string;
int ret=eps2pdf(*(map->pValue()),out,r);
if(0!=ret) { delete out; goto fail; } // Something wrong
return new ObjectGMTMapPDF(out,map->Bblx(),map->Bbly(),map->Bbrx(),map->Bbry());
int ret=eps2pdf(*(m->pValue()),out,r);
if(0!=ret) {err="Conversion of EPS to PDF failed"; delete out; goto fail; } // Something wrong
return new ObjectGMTMapPDF(out,m->Bblx(),m->Bbly(),m->Bbrx(),m->Bbry());
}
fail:
return 0;
return new ObjectError("EPS2PDF",err);
}
@ -286,112 +240,108 @@ antialiasing or aa - can be used to set graphics and text antialiasing simultane
*/
const ObjectBase* GMT_Convert2PNG(const ObjectList* input)
{
if(!gs_abilities.havepdf) return 0; // No pdf support, so, no png
double r;
bool suc=true;
const GMTMap* map=0;
std::string gsdev("");
bool ispdf;
uint aat=0,aag=0;
std::string aats("full"),aags("full");
uint dscale=0;
// Map to convert
std::string gsdev,err;
double res;
ONPar<Base2PosD> r("resolution",300),ds("d[own]scale",1.0);
ONPar<Base2Pos> w("width"),h("height");
ONPar<Base2StringD> cs("c[olor][ ]space","color"),taa("t[ext]a[nti]aliasing","full"),gaa("g[raphics]a[nti]aliasing","full"),aa("a[nti]aliasing","full");
const ObjectGMTMap* me;
const ObjectGMTMapPDF* mp;
const GMTMap* m;
uint aat,aag;
uint dscale;
if(!gs_abilities.havepdf) {err="No PDF support in Ghostscript"; goto fail;} // No pdf support, so, no png
// Search map
{
for(ObjectList::IndexType i=0;i<input->Size();i++)
{
OBType<ObjectGMTMap> m(input->At(i));
OBType<ObjectGMTMapPDF> p(input->At(i));
if(m)
{
if(0!=map) goto fail; // Duplicate
map=m;
ispdf=false;
}
if(p)
{
if(0!=map) goto fail; // Duplicate
map=p;
ispdf=true;
}
}
if(0==map) goto fail; // Map not found
RNFPar<SearchGMTMap,ObjectGMTMap> mapeps("map");
RNFPar<SearchGMTMapPDF,ObjectGMTMap> mappdf("map");
ParseNamedParameters peps(input,mapeps);
ParseNamedParameters ppdf(input,mappdf);
if(!(peps || ppdf)) {err="Error parsing map parameter"; goto fail;}
me=mapeps; mp=mappdf;
if(nullptr==me && nullptr==mp) {err="Map parameter not specified"; goto fail;} // This must never happened
if(nullptr!=me) m=me; else m=mp;
}
// Parse other parameters
{
ParseNamedParameters params(input,r,ds,w,h,cs,taa,gaa,aa);
if(!params) {err=params.Error(); goto fail;}
}
// Determine resolution
{
SearchParameter<double,false,PMin<1> > res(input,"r","res","resolution");
BaseM2Double w(input,"width","w"), h(input,"height","h");
// Check existence
if( ( res.Exist() && w.Exist() ) || ( res.Exist() && h.Exist() ) || ( w.Exist() && h.Exist() )) goto fail; // Only one parameter allowed
r=300; // Default value
if(res.Exist()) r=res(&suc);
if(w.Exist()) r=w(&suc)*72.0/(map->Bbrx()-map->Bblx());
if(h.Exist()) r=h(&suc)*72.0/(map->Bbry()-map->Bbly());
if(!suc) goto fail; // Error
if( ( r.Exist() && w.Exist() ) || ( r.Exist() && h.Exist() ) || ( w.Exist() && h.Exist() )) {err="Only one of resolution, width or height may be specified"; goto fail;} // Only one parameter allowed
if(r.Exist()) res=r;
if(w.Exist()) res=w*72.0/(m->Bbrx()-m->Bblx());
if(h.Exist()) res=h*72.0/(m->Bbry()-m->Bbly());
}
// Color model
{
std::string cs;
BaseMD2String cspace(input,"8bit","colorspace","cspace","cs");
cs=cspace(&suc);
if(!suc) goto fail; // Error
tolower(cs);
if(("mono"==cs || "monochrome"==cs) && gs_abilities.havepngmono) gsdev="pngmono";
if(("monod"==cs || "monodiffused"==cs) && gs_abilities.havepngmonod) gsdev="pngmonod";
if(("16c"==cs || "16"==cs) && gs_abilities.havepng16) gsdev="png16";
if(("256c"==cs || "256"==cs) && gs_abilities.havepng256) gsdev="png256";
if(("gray"==cs || "grey"==cs || "g"==cs) && gs_abilities.havepnggray) gsdev="pnggray";
if(("full"==cs || "8bit"==cs || "color"==cs) && gs_abilities.havepng16m) gsdev="png16m";
if(""==gsdev) goto fail; // Incorrect value
std::string cspace=cs;
tolower(cspace);
if(("mono" ==cspace || "monochrome" ==cspace ) && gs_abilities.havepngmono) gsdev="pngmono";
if(("monod"==cspace || "monodiffused"==cspace ) && gs_abilities.havepngmonod) gsdev="pngmonod";
if(("16c" ==cspace || "16" ==cspace ) && gs_abilities.havepng16) gsdev="png16";
if(("256c" ==cspace || "256" ==cspace ) && gs_abilities.havepng256) gsdev="png256";
if(("gray" ==cspace || "grey" ==cspace || "g" ==cspace) && gs_abilities.havepnggray) gsdev="pnggray";
if(("full" ==cspace || "8bit" ==cspace || "color"==cspace) && gs_abilities.havepng16m) gsdev="png16m";
if(gsdev.empty()) {err="Colorspace "+cs.Value()+" is bad or unsupported"; goto fail;} // Incorrect value
}
// Antialiasing
{
BaseM2String aa(input,"antialiasing","aa"),taa(input,"textantialiasing","taa"),gaa(input,"graphicsantialiasing","gaa");
if(aa.Exist()) aags=aats=aa(&suc);
if(taa.Exist()) aats=taa(&suc);
if(gaa.Exist()) aags=gaa(&suc);
if(!suc) goto fail; // Error
std::string aags=aa,aats=aa;
if(taa.Exist()) aats=taa;
if(gaa.Exist()) aags=gaa;
tolower(aags); tolower(aats);
if("none"==aags || "no"==aags || "n"==aags) aag=1; if("none"==aats || "no"==aats || "n"==aats) aat=1;
if("small"==aags || "s"==aags) aag=2; if("small"==aats || "s"==aats) aat=2;
if("full"==aags || "f"==aags) aag=4; if("full"==aats || "f"==aats) aat=4;
if(0==aat || 0==aag) goto fail; // Incorrect value
if("none" ==aags || "no"==aags || "n"==aags) aag=1;
if("none" ==aats || "no"==aats || "n"==aats) aat=1;
if("small"==aags || "s" ==aags ) aag=2;
if("small"==aats || "s" ==aats ) aat=2;
if("full" ==aags || "f" ==aags ) aag=4;
if("full" ==aats || "f" ==aats ) aat=4;
if(0==aat) {err="Incorrect value for text antialiasing: "+aats; goto fail;}
if(0==aag) {err="Incorrect value for graphics antialiasing: "+aags; goto fail;}
}
// Downscale
if("pngmonod"==gsdev || "pnggray"==gsdev || "png16m"==gsdev)
{
SearchParameterWDefO<double,DoubleDefaultVal<1>,false,PMin<1>,PMax<10>,PInt > ds(input,"downscale","dscale","ds");
dscale=static_cast<uint>(ds(&suc));
if(!suc) goto fail; // Error
r*=dscale;
dscale=static_cast<uint>(ds);
if(dscale<1 || dscale>10) {err="Downscale must be in interval from 1 to 10"; goto fail;}
res*=dscale;
}
// Go!
{
std::string* pdf=0;
int ret;
if(!ispdf)
if(nullptr!=me)
{
pdf=new std::string;
ret=eps2pdf(*(map->pValue()),pdf,r,aat,aag);
if(0!=ret) { delete pdf; goto fail; } // Something wrong
ret=eps2pdf(*(me->pValue()),pdf,r,aat,aag);
if(0!=ret) {err="Can't convert EPS to PDF"; delete pdf; goto fail; } // Something wrong
}
{
std::string* out=new std::string;
ret=GhostRun("-r"+ToString(r)+" -dTextAlphaBits="+ToString(aat)+" -dGraphicAlphaBits="+ToString(aag)+" -sDEVICE="+gsdev+((dscale>1)?(" -dDownScaleFactor="+ToString(dscale)):""),((0!=pdf)?*pdf:*(map->pValue())),0,0,out);
ret=GhostRun("-r"+ToString(res)+" -dTextAlphaBits="+ToString(aat)+" -dGraphicAlphaBits="+ToString(aag)+" -sDEVICE="+gsdev+((dscale>1)?(" -dDownScaleFactor="+ToString(dscale)):""),((0!=pdf)?*pdf:*(mp->pValue())),0,0,out);
if(0!=pdf) delete pdf;
if(0!=ret) { delete out; goto fail; } // Something wrong
if(0!=ret) {err="Can't convert PDF to PNG"; delete out; goto fail; } // Something wrong
return new ObjectGMTImage(out);
}
}
fail:
return 0;
return new ObjectError("EPS2PNG",err);
}
@ -410,104 +360,101 @@ antialiasing or aa - can be used to set graphics and text antialiasing simultane
*/
const ObjectBase* GMT_Convert2JPG(const ObjectList* input)
{
if(!gs_abilities.havepdf) return 0; // No pdf support, so, no jpeg
double r;
bool suc=true;
const GMTMap* map=0;
std::string gsdev("");
bool ispdf;
uint aat=0,aag=0;
std::string aats("full"),aags("full");
std::string gsdev,err;
ONPar<Base2PosD> r("resolution",300);
ONPar<Base2Pos> w("width"),h("height");
ONPar<Base2StringD> cs("c[olor][ ]space","color"),taa("t[ext]a[nti]aliasing","full"),gaa("g[raphics]a[nti]aliasing","full"),aa("a[nti]aliasing","full");
ONPar<Base2NonNegD> q("quality",75);
const ObjectGMTMap* me;
const ObjectGMTMapPDF* mp;
const GMTMap* m;
double res;
uint aat,aag;
uint qual;
// Map to convert
if(!gs_abilities.havepdf) {err="No PDF support in Ghostscript"; goto fail;} // No pdf support, so, no jpeg
// Search map
{
for(ObjectList::IndexType i=0;i<input->Size();i++)
{
OBType<ObjectGMTMap> m(input->At(i));
OBType<ObjectGMTMapPDF> p(input->At(i));
if(m)
{
if(0!=map) goto fail; // Duplicate
map=m;
ispdf=false;
}
if(p)
{
if(0!=map) goto fail; // Duplicate
map=p;
ispdf=true;
}
}
if(0==map) goto fail; // Map not found
RNFPar<SearchGMTMap,ObjectGMTMap> mapeps("map");
RNFPar<SearchGMTMapPDF,ObjectGMTMap> mappdf("map");
ParseNamedParameters peps(input,mapeps);
ParseNamedParameters ppdf(input,mappdf);
if(!(peps || ppdf)) {err="Error parsing map parameter"; goto fail;}
me=mapeps; mp=mappdf;
if(nullptr==me && nullptr==mp) {err="Map parameter not specified"; goto fail;} // This must never happened
if(nullptr!=me) m=me; else m=mp;
}
// Parse other parameters
{
ParseNamedParameters params(input,r,q,w,h,cs,taa,gaa,aa);
if(!params) {err=params.Error(); goto fail;}
}
// Determine resolution
{
SearchParameter<double,false,PMin<1> > res(input,"r","res","resolution");
BaseM2Double w(input,"width","w"), h(input,"height","h");
// Check existence
if( ( res.Exist() && w.Exist() ) || ( res.Exist() && h.Exist() ) || ( w.Exist() && h.Exist() )) goto fail; // Only one parameter allowed
r=300; // Default value
if(res.Exist()) r=res(&suc);
if(w.Exist()) r=w(&suc)*72.0/(map->Bbrx()-map->Bblx());
if(h.Exist()) r=h(&suc)*72.0/(map->Bbry()-map->Bbly());
if(!suc) goto fail; // Error
if( ( r.Exist() && w.Exist() ) || ( r.Exist() && h.Exist() ) || ( w.Exist() && h.Exist() )) {err="Only one of resolution, width or height may be specified"; goto fail;} // Only one parameter allowed
if(r.Exist()) res=r;
if(w.Exist()) res=w*72.0/(m->Bbrx()-m->Bblx());
if(h.Exist()) res=h*72.0/(m->Bbry()-m->Bbly());
}
// Color model
{
std::string cs;
BaseMD2String cspace(input,"8bit","colorspace","cspace","cs");
cs=cspace(&suc);
if(!suc) goto fail; // Error
tolower(cs);
if(("gray"==cs || "grey"==cs || "g"==cs) && gs_abilities.havejpeggray) gsdev="jpeggray";
if(("full"==cs || "8bit"==cs || "color"==cs) && gs_abilities.havejpeg) gsdev="jpeg";
if(""==gsdev) goto fail; // Incorrect value
std::string cspace=cs;
tolower(cspace);
if(("gray"==cspace || "grey"==cspace || "g" ==cspace) && gs_abilities.havejpeggray) gsdev="jpeggray";
if(("full"==cspace || "8bit"==cspace || "color"==cspace) && gs_abilities.havejpeg) gsdev="jpeg";
if(gsdev.empty()) {err="Colorspace "+cs.Value()+" is bad or unsupported"; goto fail;} // Incorrect value
}
// Antialiasing
{
BaseM2String aa(input,"antialiasing","aa"),taa(input,"textantialiasing","taa"),gaa(input,"graphicsantialiasing","gaa");
if(aa.Exist()) aags=aats=aa(&suc);
if(taa.Exist()) aats=taa(&suc);
if(gaa.Exist()) aags=gaa(&suc);
if(!suc) goto fail; // Error
std::string aags=aa,aats=aa;
if(taa.Exist()) aats=taa;
if(gaa.Exist()) aags=gaa;
tolower(aags); tolower(aats);
if("none"==aags || "no"==aags || "n"==aags) aag=1; if("none"==aats || "no"==aats || "n"==aats) aat=1;
if("small"==aags || "s"==aags) aag=2; if("small"==aats || "s"==aats) aat=2;
if("full"==aags || "f"==aags) aag=4; if("full"==aats || "f"==aats) aat=4;
if(0==aat || 0==aag) goto fail; // Incorrect value
if("none" ==aags || "no"==aags || "n"==aags) aag=1;
if("none" ==aats || "no"==aats || "n"==aats) aat=1;
if("small"==aags || "s" ==aags ) aag=2;
if("small"==aats || "s" ==aats ) aat=2;
if("full" ==aags || "f" ==aags ) aag=4;
if("full" ==aats || "f" ==aats ) aat=4;
if(0==aat) {err="Incorrect value for text antialiasing: "+aats; goto fail;}
if(0==aag) {err="Incorrect value for graphics antialiasing: "+aags; goto fail;}
}
// Quality
{
SearchParameterWDefO<double,DoubleDefaultVal<75>,false,PMin<0>,PMax<100>,PInt > q(input,"quality","qual","q");
qual=static_cast<uint>(q(&suc));
if(!suc) goto fail; // Error
qual=static_cast<uint>(q);
if(q>100) {err="JPEG quality must not exceed 100"; goto fail;} // Error
}
// Go!
{
std::string* pdf=0;
int ret;
if(!ispdf)
if(nullptr!=me)
{
pdf=new std::string;
ret=eps2pdf(*(map->pValue()),pdf,r,aat,aag);
if(0!=ret) { delete pdf; goto fail; } // Something wrong
ret=eps2pdf(*(me->pValue()),pdf,r,aat,aag);
if(0!=ret) {err="Can't convert EPS to PDF"; delete pdf; goto fail; } // Something wrong
}
{
std::string* out=new std::string;
ret=GhostRun("-r"+ToString(r)+" -dTextAlphaBits="+ToString(aat)+" -dGraphicAlphaBits="+ToString(aag)+" -sDEVICE="+gsdev+" -dJPEGQ="+ToString(qual),((0!=pdf)?*pdf:*(map->pValue())),0,0,out);
ret=GhostRun("-r"+ToString(res)+" -dTextAlphaBits="+ToString(aat)+" -dGraphicAlphaBits="+ToString(aag)+" -sDEVICE="+gsdev+" -dJPEGQ="+ToString(qual),((0!=pdf)?*pdf:*(mp->pValue())),0,0,out);
if(0!=pdf) delete pdf;
if(0!=ret) { delete out; goto fail; } // Something wrong
if(0!=ret) {err="Can't convert PDF to JPEG"; delete out; goto fail; } // Something wrong
return new ObjectGMTImage(out);
}
}
fail:
return 0;
return new ObjectError("EPS2JPEG",err);
}

23
modules/gmt/modgmt_map.h

@ -72,12 +72,29 @@ class ObjectGMTImage: public GMTBlob
std::string Type() const override {return "GMTImage";}
};
// Policy for get integer value
class PInt
// Convertor to search object
template<class Object>
class SearchObject
{
public:
double operator()(double v, bool* suc) const {if(v!=floor(v)) *suc=false; return v;}
using ValueType=const Object*;
template<class... Args>
ValueType Convert(const ObjectBase* ob, bool* res, std::string& err, Args... args)
{
OBType<Object> gp(ob);
switch(gp.Error())
{
case(OBTypeErr::OK): return gp;
case(OBTypeErr::NULLPTR): {err="Can't convert zero ObjectBase pointer to something meaningfull"; break;}
case(OBTypeErr::TYPEMISMATCH): {err=ob->Type()+" is not correct: type mismatch"; break;}
}
*res=false;
return nullptr;
}
};
// Convertor to search GMTMap objects
using SearchGMTMap=SearchObject<ObjectGMTMap>;
using SearchGMTMapPDF=SearchObject<ObjectGMTMapPDF>;
// Creating eps map from set of layers
const ObjectBase* GMT_Map(const ObjectList* input);

40
modules/gmt/modgmt_objects.cpp

@ -36,41 +36,15 @@ std::map<std::string,gmt_projection::projection> gmt_projection::projnames;
void gmt_projection::FillProjNames()
{
projnames["x"]=XY;
projnames["xy"]=XY;
projnames["decart"]=XY;
projnames["q"]=CYL_EQU;
projnames["cyl_equid"]=CYL_EQU;
projnames["cylindrical equidistant"]=CYL_EQU;
projnames["m"]=MERCATOR;
projnames["merc"]=MERCATOR;
projnames["(xy|decart)"]=XY;
projnames["c[ylindrical][( |_)]equidistant"]=CYL_EQU;
projnames["mercator"]=MERCATOR;
projnames["t"]=TRANSMERCATOR;
projnames["tmerc"]=TRANSMERCATOR;
projnames["transverse mercator"]=TRANSMERCATOR;
projnames["o"]=OBLIQMERCATOR;
projnames["omerc"]=OBLIQMERCATOR;
projnames["oblique mercator"]=OBLIQMERCATOR;
projnames["c"]=CASSINI;
projnames["t[ransverse][( |_)]mercator"]=TRANSMERCATOR;
projnames["o[blique][( |_)]mercator"]=OBLIQMERCATOR;
projnames["cassini"]=CASSINI;
projnames["y"]=CYL_EQA;
projnames["cyl_equala"]=CYL_EQA;
projnames["cylindrical equal-area"]=CYL_EQA;
projnames["cylindrical equalarea"]=CYL_EQA;
projnames["j"]=MILLER;
projnames["cyl_miller"]=MILLER;
projnames["miller"]=MILLER;
projnames["cylindrical miller"]=MILLER;
projnames["cyl_stere"]=CYL_STERE;
projnames["cylindrical stereographic"]=CYL_STERE;
projnames["c[ylindrical][( |_)]eq[ua(l|l-)]area"]=CYL_EQA;
projnames["[c[ylindrical][( |_)]]miller"]=MILLER;
projnames["c[ylindrical][( |_)]stereographic"]=CYL_STERE;
}
bool gmt_font::FillFontNames()

297
modules/gmt/modgmt_param.h

@ -0,0 +1,297 @@
#ifndef MODGMT_PARAM_H
#define MODGMT_PARAM_H
#include "modgmt_strcomp.h"
#include "common.h"
// Common ancestor for all parameters
template<class Converter, bool Optional>
class Parameter
{
using ValueType=typename Converter::ValueType;
std::string name; // Used for error reporting
bool initialised;
Converter conv;
ValueType val;
template<bool hasDefault, class Conv=Converter>
struct Adapter;
template<class Conv>
struct Adapter<true,Conv> {static const typename Conv::ValueType& Val(const Conv& cnv) {return cnv.Default();}};
template<class Conv>
struct Adapter<false,Conv> {static typename Conv::ValueType Val(const Conv& cnv) {return typename Conv::ValueType();}};
template<class C>
struct CheckDefault
{
private:
static void detect(...);
template<class T> static decltype(std::declval<T>().Default()) detect(T);
public:
static constexpr bool value=std::is_same<ValueType, typename std::decay<decltype(detect(std::declval<C>()))>::type>::value;
};
protected:
Parameter(Parameter&&) = delete;
Parameter(const Parameter&) = delete;
Parameter() = delete;
template<class... Args>
Parameter(std::string&& str, Args... args): name(std::move(str)), initialised(false), conv(args...)
{
val=Adapter<CheckDefault<Converter>::value>::Val(conv);
}
template<class... Args>
Parameter(const std::string& str, Args... args): name(str), initialised(false), conv(args...)
{
val=Adapter<CheckDefault<Converter>::value>::Val(conv);
}
void SetState(bool newini) {initialised=newini;}
public:
bool Init(const ObjectBase* p, std::string& err)
{
std::string cerr;
bool res=true;
ValueType tval=conv.Convert(p,&res,cerr);
SetState(res);
if(res) val=std::move(tval);
else err=std::move(cerr);
return res;
}
bool Initialised() const {return initialised;}
bool Exist() const {return initialised;}
const std::string& Name() const {return name;}
constexpr bool isOptional() const {return Optional;}
const ValueType& Value() {return val;}
const ValueType* operator->() const {return &val;}
operator const ValueType&() {return val;}
void Reset() {initialised=false; val=Adapter<CheckDefault<Converter>::value>::Val(conv);}
static constexpr bool optional=Optional;
using ConverterType=Converter;
};
// Class for parameter which must be in named pair
template <class Converter, bool O>
class NamedParameter: public TemplateComparator, public Parameter<Converter,O>
{
public:
using AcceptableObject=void;
template<class... Args>
NamedParameter(const std::string& t, Args... args):TemplateComparator(t),Parameter<Converter,O>(Template2Name(),args...) {}
};
// Class for parameter which can be in named pair or as object of type Object
template <class Converter, bool O, class Object>
class NamedFreeParameter: public NamedParameter<Converter,O>
{
public:
using AcceptableObject=Object;
template<class... Args>
NamedFreeParameter(Args... args):NamedParameter<Converter,O>(args...) {}
};
// Class for parameter which can be in some positions in list
template <class Converter, bool O>
class PositionalParameter: public Parameter<Converter,O>
{
public:
template<class... Args>
PositionalParameter(Args... args):Parameter<Converter,O>(args...) {}
};
// Base class for ParseNamedParameters and ParsePositionalParameters
class ParseParameters
{
protected:
std::string err;
ParseParameters():err() {}
ParseParameters(const ParseParameters&) = delete;
ParseParameters(ParseParameters&&) = delete;
public:
std::string Error() const {return err;}
bool Ok() const {return err.empty();}
operator bool() const {return Ok();}
};
// Parsing positional parameters
class ParsePositionalParameters: public ParseParameters
{
ParsePositionalParameters() = delete;
ParsePositionalParameters(const ParsePositionalParameters&) = delete;
ParsePositionalParameters(ParsePositionalParameters&&) = delete;
// Main parsing function
template <class Par, class... Args>
void Parse(ObjectList::IndexType i, ObjectList::IndexType max, const ObjectList* ol, Par& param, Args&... args)
{
// Check types of arguments
static_assert(std::is_same<PositionalParameter<typename Par::ConverterType, Par::optional>,Par>::value,"ParsePositionalParameters argument(s) must be PositionalParameter");
// Check if parameter already initialised. This is code error.
if(param.Initialised())
{
err="Parameter "+param.Name()+" already initialised. This is code error.";
return;
}
// List is ended
if(i>=max)
{
// Parameter is optional, skip it
if(Par::optional) Parse(i,max,ol,args...);
// Parameter is required, this is an error
else err="Parameter "+param.Name()+" is required, but can't be setted because list is ended";
}
// Initialise from some list element
else
{
bool res=param.Init(ol->At(i),err);
// All Ok, continue to next element in list
if(res) Parse(i+1,max,ol,args...);
else
{
// All Ok, optional parameter may be absent, try to initialise next parameter by same list element
if(Par::optional) Parse(i,max,ol,args...);
// Error, required parameter not initialised
else err="Can't initialise parameter "+param.Name()+" from list element number "+ToString(i)+": "+err;
}
}
}
// Bottom of recursion
void Parse(ObjectList::IndexType i, ObjectList::IndexType max, const ObjectList* ol) {if(i<max) err="There are excess elements in list";}
public:
template <class Converter, bool optional, class... Args>
ParsePositionalParameters(const ObjectList* ol, PositionalParameter<Converter,optional>& p1, Args&... args) {Parse(0,ol->Size(),ol,p1,args...);}
template <class Converter, bool optional, class... Args>
ParsePositionalParameters(const ObjectList* ol, ObjectList::IndexType min, ObjectList::IndexType max, PositionalParameter<Converter,optional>& p1, Args&... args) {Parse(min,std::min(max,ol->Size()),ol,p1,args...);}
};
// Parsing named parameters
class ParseNamedParameters: public ParseParameters
{
ParseNamedParameters() = delete;
ParseNamedParameters(const ParseNamedParameters&) = delete;
ParseNamedParameters(ParseNamedParameters&&) = delete;
// Parsing function for elements without names
// ob - object from which we initialise parameter param.
// init - is ob was already used for initialise something.
// args - other parameters
// Function try to initialise parameter param, if param accepted real type of ob for initialisation, else function try to initialise next parameter in args.
// Function return error if initialisation of parameter failed or if two parameters can be initialised from ob.
template <class Par, class... Args>
bool ParseSingle(const ObjectBase* ob, bool init, Par& param, Args&... args)
{
// Check types of arguments
static_assert(std::is_same<NamedParameter<typename Par::ConverterType, Par::optional>,Par>::value || std::is_same<NamedFreeParameter<typename Par::ConverterType, Par::optional, typename Par::AcceptableObject>,Par>::value,"ParseNamedParameters argument(s) must be NamedParameter or NamedFreeParameter");
OBType<typename Par::AcceptableObject> o(ob);
if(o && init)
{
err="Object of type "+ob->Type()+" used for initialisation of two parameters. This is code error.";
return false;
}
if(!o) return ParseSingle(ob,init,args...); // Type mismatch, goto next parameter
if(param.Initialised())
{
err="Parameter "+param.Name()+" can't be initialised from object of type "+ob->Type()+" because it already initialised.";
return false;
}
std::string ierr;
if(!param.Init(ob,ierr))
{
err="Parameter "+param.Name()+" can't be initialised from object of type "+ob->Type()+": "+ierr;
return false;
}
return ParseSingle(ob,true,args...);
}
// Bottom of recursion
bool ParseSingle(const ObjectBase* ob, bool init) const {return true;}
// Parsing function for elements in pairs
// op - pair from which we initialise parameter param.
// pname - name of parameter already initialised from op (or empty string).
// args - other parameters
// Function try to initialise parameter param, if pair name corresponding to param template, else function try to initialise next parameter in args.
// Function return error if initialisation of parameter failed or if two parameters can be initialised from op.
template <class Par, class... Args>
bool ParsePair(const ObjectPair* op, std::string& pname, Par& param, Args&... args)
{
// Check types of arguments
static_assert(std::is_same<NamedParameter<typename Par::ConverterType, Par::optional>,Par>::value || std::is_same<NamedFreeParameter<typename Par::ConverterType, Par::optional, typename Par::AcceptableObject>,Par>::value,"ParseNamedParameters argument(s) must be NamedParameter or NamedFreeParameter");
bool cmp=param.Compare(op->Name());
if(cmp && !pname.empty())
{
err="Element "+op->Name()+" can be used for initialisation of two parameters: "+pname+" and "+param.Name();
return false;
}
if(!cmp) return ParsePair(op,pname,args...); // Name mismatch, goto next parameter
pname=param.Name();
if(param.Initialised())
{
err="Parameter "+param.Name()+" can't be initialised from element "+op->Name()+" because it already initialised.";
return false;
}
std::string ierr;
if(!param.Init(op->Value(),ierr))
{
err="Parameter "+param.Name()+" can't be initialised from element "+op->Name()+": "+ierr;
return false;
}
return ParsePair(op,pname,args...);
}
// Bottom of recursion
bool ParsePair(const ObjectPair* op, std::string& pname) const {return true;}
template <class Par, class... Args>
void CheckRequired(const Par& param, Args&... args)
{
if((Par::optional || param.Initialised())) CheckRequired(args...);
else err="Parameter "+param.Name()+" is required, but not initialised";
}
// Bottom of recursion
void CheckRequired() const {}
public:
template <class... Args>
ParseNamedParameters(const ObjectList* ol, Args&... args)
{
// Initialisation
std::string pname;
for(ObjectList::const_iterator i=ol->begin();i!=ol->end();++i)
{
pname.erase();
OBType<ObjectPair> p(*i);
if( !(p?ParsePair(p,pname,args...):ParseSingle(*i,false,args...)) ) break;
}
if(!Ok()) return; // Error on initialisation phase
// Check if all required parameters are initialised
CheckRequired(args...);
}
};
template<class Converter>
using ONPar=NamedParameter<Converter,true>;
template<class Converter>
using RNPar=NamedParameter<Converter,false>;
template<class Converter, class Object>
using ONFPar=NamedFreeParameter<Converter,true,Object>;
template<class Converter, class Object>
using RNFPar=NamedFreeParameter<Converter,false,Object>;
template<class Converter>
using OPosPar=PositionalParameter<Converter,true>;
template<class Converter>
using RPosPar=PositionalParameter<Converter,false>;
#endif

292
modules/gmt/modgmt_strcomp.h

@ -1,9 +1,8 @@
#include <iostream>
#ifndef MODGMT_STRCOMP_H
#define MODGMT_STRCOMP_H
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "common.h"
// Compare string with template
class TemplateComparator
@ -49,286 +48,51 @@ class TemplateComparator
Cursors cursors;
const std::string s;
public:
TemplateComparator(const char* str):s(str) {root=Parse();}
TemplateComparator(const std::string& str):s(str) {root=Parse();}
TemplateComparator(std::string&& str):s(std::move(str)) {root=Parse();}
bool Compare(const std::string& str)
{
Reset();
for(size_t pos=0; pos<str.length(); ++pos) if(!CmpSmb(str[pos])) return false;
return true;
}
void Reset()
{
cursors.clear();
InitCursors(root.get());
}
};
// Common ancestor for all parameters
template<bool Optional>
class Parameter
{
std::string name;
bool initialised;
protected:
Parameter() = default;
Parameter(const Parameter&) = delete;
Parameter(std::string&& str): name(std::move(str)), initialised(false) {}
Parameter(const std::string& str): name(str), initialised(false) {}
void SetState(bool newini) {initialised=newini;}
public:
bool Initialised() const {return initialised;}
const std::string& Name() {return name;}
constexpr bool isOptional() const {return Optional;}
static constexpr bool optional=Optional;
};
// Class for parameter which must be in named pair
template <class Converter, bool O>
class NamedParameter: public Converter, public Parameter<O>
{
NamedParameter() = delete;
NamedParameter(const NamedParameter&) = delete;
using Parameter<O>::Initialised;
protected:
TemplateComparator compar;
public:
typedef Converter ConverterType;
// Constructor specifies template for comparison
template<class N, class T>
NamedParameter(N&& n, T&& t):compar(std::forward(t)) {Parameter<O>(std::forward(n));}
// Return error in case of repeated initialisation
bool Init(const ObjectBase* p, const std::string& str, std::string& err)
{
if(!compar.Compare(str)) return true;
if(Initialised()) return !Converter::Convert(p,err);
else SetState(Converter::Convert(p,err));
return true;
}
};
template<class Converter>
using ONPar=NamedParameter<Converter,true>;
template<class Converter>
using RNPar=NamedParameter<Converter,false>;
// Class for parameter which can be in named pair or not
template <class Converter, bool O>
class NamedFreeParameter: public NamedParameter<Converter,O>
{
NamedFreeParameter() = delete;
NamedFreeParameter(const NamedFreeParameter&) = delete;
using NamedParameter<Converter,O>::compar;
using NamedParameter<Converter,O>::Initialised;
public:
typedef Converter ConverterType;
// Constructor specifies template for comparison
template<class N, class T>
NamedFreeParameter(N&& n, T&& t):NamedParameter<Converter,O>(std::forward(n),std::forward(t)) {}
// Return error in case of repeated initialisation
bool Init(const ObjectBase* p, const std::string& str)
{
if(!compar.Compare(str)) return true;
if(Initialised()) return !Converter::Convert(p);
else SetState(Converter::Convert(p));
return true;
}
bool Init(const ObjectBase* p)
{
if(Initialised()) return !Converter::Convert(p);
else SetState(Converter::Convert(p));
return true;
}
};
template<class Converter>
using ONFPar=NamedFreeParameter<Converter,true>;
template<class Converter>
using RNFPar=NamedFreeParameter<Converter,false>;
// Class for parameter which can be in some positions in list
template <class Converter, bool O>
class PositionalParameter: public Converter, public Parameter<O>
{
using IType=ObjectList::IndexType;
PositionalParameter() = delete;
PositionalParameter(const PositionalParameter&) = delete;
std::set<IType> positions;
IType initpos;
using Parameter<O>::Initialised;
using Parameter<O>::SetState;
// Constructor specifies list of possible positions
PositionalParameter(IType i):initpos(0) {positions.insert(i);}
template<class... Args>
PositionalParameter(IType i, Args... args): PositionalParameter(args...) {positions.insert(i);}
public:
typedef Converter ConverterType;
template<class S>
PositionalParameter(S&& str, IType i):initpos(0) {Parameter<O>(std::forward(str)); positions.insert(i);}
template<class S, class... Args>
PositionalParameter(S&& str, IType i, Args... args): PositionalParameter(args...) {Parameter<O>(std::forward(str)); positions.insert(i);}
TemplateComparator(const char* str):s(str) {root=Parse();}
TemplateComparator(const std::string& str):s(str) {root=Parse();}
TemplateComparator(std::string&& str):s(std::move(str)) {root=Parse();}
// Return error in case of repeated initialisation
bool Init(IType i, const ObjectBase* p, std::string& err)
bool Compare(const std::string& str)
{
std::string cerr;
if(positions.cend()==positions.find(i)) return true;
if(Initialised()) return !Converter::Convert(p,cerr);
else
{
bool res=Converter::Convert(p,cerr);
if(res) initpos=i;
else
{
if(1==positions.size() && !O) // Only one position for required parameter. Error message must describe reason of failed conversion
{
err=std::move(cerr);
return false;
}
}
SetState(res);
}
Reset();
for(size_t pos=0; pos<str.length(); ++pos) if(!CmpSmb(str[pos])) return false;
return true;
}
IType InitialisedFromPosition() const {return initpos;}
};
template<class Converter>
using OPosPar=PositionalParameter<Converter,true>;
template<class Converter>
using RPosPar=PositionalParameter<Converter,false>;
// Parsing positional parameters
// Generic template
template<class... Args>
class ParsePositionalParameters;
// Recursive template
template<class Arg, class... Args>
class ParsePositionalParameters<Arg, Args...>: ParsePositionalParameters<Args...>
{
typedef PositionalParameter<typename Arg::ConverterType, Arg::optional> PType;
static_assert(std::is_same<PType,Arg>::value,"ParsePositionalParameters template argument(s) must be PositionalParameter");
PType& par;
ParsePositionalParameters() = delete;
ParsePositionalParameters(const ParsePositionalParameters&) = delete;
ParsePositionalParameters(ParsePositionalParameters&&) = delete;
protected:
template<class Str>
void SetError(Str&& str) {ParsePositionalParameters<Args...>::SetError(std::forward(str));}
bool TryInit(ObjectList::IndexType i, const ObjectBase* ob)
std::string Template2Name() const
{
std::string err;
bool res=par.Init(i,ob,err);
if(!res) SetError(err.empty()?("Ambiguous definition of parameter "+par.Name()):("Parameter "+par.Name()+" in position "+ToString(i)+": "+err));
return res && ParsePositionalParameters<Args...>::TryInit(i,ob);
}
bool Check()
{
if(!(par.Initialised() || par.isOptional()))
const struct Block* b=root.get();
std::string res;
while(true)
{
SetError("Parameter "+par.Name()+" required, but not set");
return false;
}
return ParsePositionalParameters<Args...>::Check();
}
// Return value: number of parameters initialised from position i (count only first two)
size_t isUsed(ObjectList::IndexType i, bool used=false) const
{
if(!par.Initialised() || (par.InitialisedFromPosition()!=i)) return ParsePositionalParameters<Args...>::isUsed(i,used);
if(used) // Error, two parameters initialised from one position
{
SetError(par.Name());
return 1;
}
auto res=ParsePositionalParameters<Args...>::isUsed(i,true);
if(res==1) // Two parameters initialised from one position, name of second parameter in error string
{
SetError("Parameters "+Error()+" and "+par.Name()+" are initialised from object in position "+ToString(i));
return 2;
}
else return 1; // All ok
}
public:
ParsePositionalParameters(PType& p, Args... args):par(p) {ParsePositionalParameters<Args...>(args...);}
std::string Error() const {return ParsePositionalParameters<Args...>::Error();}
bool Parse(const ObjectList* list)
{
std::string err;
for(ObjectList::IndexType i=0; i<list->Size(); i++)
{
if(!TryInit(i,list->At(i),err))
// Processing current block
switch(b->type)
{
SetError("Error parsing argument "+ToString(i)+": "+err);
return false;
case(Block::TEXT): {res+=s.substr(b->b,b->e-b->b); break;}
case(Block::OPTIONAL):
case(Block::VARIANTS): {b=b->child.get(); goto next;}
case(Block::DELIM): {b=b->parent; break;}
default: {}
}
}
if(!Check()) return false;
for(ObjectList::IndexType i=0; i<list->Size(); i++)
{
auto n=isUsed(i);
if(n>1) return false; // Error message already set
if(0==n)
// Go to next block
while(true)
{
SetError("Unused object in position "+ToString(i));
return false;
if(nullptr!=b->next) {b=b->next.get(); goto next;}
if(nullptr!=b->parent) b=b->parent;
else break;
}
if(nullptr==b->next && nullptr==b->parent) break;
next: ;
}
return true;
return res;
}
};
// Bottom of recursion
template<>
class ParsePositionalParameters<>
{
std::string err;
ParsePositionalParameters(const ParsePositionalParameters&) = delete;
ParsePositionalParameters(ParsePositionalParameters&&) = delete;
protected:
ParsePositionalParameters() = default;
bool TryInit(ObjectList::IndexType i, const ObjectBase* ob) const {return true;}
size_t isUsed(ObjectList::IndexType i, bool used=false) const {return 0;}
bool Check() const {return true;}
template<class Str>
void SetError(Str&& str) {err=std::forward(str);}
std::string Error() const {return err;}
};
#endif

223
modules/gmt/modgmt_structs.h

@ -6,6 +6,7 @@
#include <vector>
#include "common.h"
#include "modgmt_colornames.h"
#include "modgmt_strcomp.h"
// Centimeters to GMT points scale factor
// 1 inch = 72 pt = 2.54 cm --> 1 cm = 72/2.54 pt
@ -31,7 +32,7 @@ class GetDouble<Source, Policy, Policies...>: public GetDouble<Source, Policies.
GetDouble(GetDouble&) = delete;
template<class... Args>
GetDouble(Args... args):GetDouble<Source,Policies...>(args...) {};
double operator()(bool* suc) const {return p(GetDouble<Source,Policies...>::operator()(suc),suc);}
double operator()(bool* suc, std::string& err) const {return p(GetDouble<Source,Policies...>::operator()(suc,err),suc,err);}
};
// Bottom of recursion
@ -44,37 +45,57 @@ class GetDouble<Source>: public Source
GetDouble() = delete;
template<class... Args>
GetDouble(Args... args):Source(args...) {};
double operator()(bool* suc) const {return Source::operator()(suc);}
double operator()(bool* suc, std::string& err) const {return Source::operator()(suc, err);}
};
// 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<int32_t num, uint32_t denum=1>
template<int32_t num, uint32_t denum=1, bool equal=true>
class PMin
{
static bool Compare(double d1, double d2) {return equal?(d1<=d2):(d1<d2);}
public:
double operator()(double v, bool* suc) const {if(v<static_cast<double>(num)/denum) *suc=false; return v;}
double operator()(double v, bool* suc, std::string& err) const
{
if(Compare(v,static_cast<double>(num)/denum))
{
*suc=false;
if(err.empty()) err="Value "+ToString(v)+" must be greater "+(equal?"or equal ":"")+"then "+ToString(num)+((denum==1)?"":("/"+ToString(denum)));
}
return v;
}
};
// Policy to check if value is lesser or equal num/denum
template<int32_t num, uint32_t denum=1>
template<int32_t num, uint32_t denum=1, bool equal=true>
class PMax
{
static bool Compare(double d1, double d2) {return equal?(d1>=d2):(d1>d2);}
public:
double operator()(double v, bool* suc) const {if(v>static_cast<double>(num)/denum) *suc=false; return v;}
double operator()(double v, bool* suc, std::string& err) const
{
if(Compare(v,static_cast<double>(num)/denum))
{
*suc=false;
if(err.empty()) err="Value "+ToString(v)+" must be lesser "+(equal?"or equal ":"")+"then "+ToString(num)+((denum==1)?"":("/"+ToString(denum)));
}
return v;
}
};
// Get double value from string or number
class SourceValue
{
double d;
bool ok;
std::string err;
public:
SourceValue(const std::string& s) {ok=str2double(s,&d);}
SourceValue(double s):d(s),ok(true) {}
double operator()(bool* suc) const
SourceValue(const std::string& s) {if(!str2double(s,&d)) err="Can't convert string \""+s+"\" to double value";}
SourceValue(double s):d(s) {}
double operator()(bool* suc, std::string& ierr) const
{
if(!ok) *suc=false;
if(!err.empty()) {*suc=false; ierr=err;}
return d;
}
};
@ -82,7 +103,7 @@ class SourceValue
// Helper types for colors
typedef GetDouble<SourceValue,PMin<0>,PMax<255> > Value2RGB;
typedef GetDouble<SourceValue,PMin<0>,PMax<360> > Value2Hue;
typedef GetDouble<SourceValue,PMin<0>,PMax<1> > Value2SV;
typedef GetDouble<SourceValue,PMin<0>,PMax< 1 > > Value2SV;
typedef GetDouble<SourceValue,PMin<0>,PMax<100> > Value2CMYK;
typedef GetDouble<SourceValue,PMin<0>,PMax<100> > Value2Transp;
// Helper type for pens and dashes
@ -116,7 +137,8 @@ struct gmt_coord: public gmt_struct
if(isdeg) return (d+(m+s/60.0)/60.0)*(sign?1:-1);
else return r;
}
bool Convert(const std::string& str)
bool Convert(const std::string& str, std::string& err)
{
WordList wl;
WordList::const_iterator cw;
@ -124,34 +146,37 @@ struct gmt_coord: public gmt_struct
if(1==wl.size()) // No dd:mm
{
isdeg=false;
return str2double(str,&r);
bool res=str2double(str,&r);
if(!res) err="Can't convert string "+str+" to double value";
return res;
}
if(0==wl.size() || wl.size()>3) return false;
if(0==wl.size() || wl.size()>3) {err="String "+str+" have incorrect format for geographic coordinate, must be ddd[:mm[:ss[.fff]]]"; return false;}
isdeg=true;
int64_t res;
// degrees
cw=wl.begin();
if(!str2int(*cw,&res)) return false;
if(!str2int(*cw,&res)) {err="Can't convert "+*cw+" to integer"; return false;}
if(res>360 || res<-360) res%=360;
sign=(std::string::npos==cw->find('-'));
d=static_cast<uint16_t>(sign?res:-res);
// minutes
cw++;
if(!str2int(*cw,&res)) return false;
if(res<0 || res>=60) return false;
if(!str2int(*cw,&res)) {err="Can't convert "+*cw+" to integer"; return false;}
if(res<0 || res>=60) {err="Minutes must be not lesser then 0 and lesser then 60"; return false;}
m=static_cast<uint8_t>(res);
s=0;
// seconds
cw++;
if(wl.end()==cw) return true; // No seconds
if(!str2double(*cw,&s) ) return false;
if(s<0.0 || s>=60.0) return false;
if(!str2double(*cw,&s) ) {err="Can't convert "+*cw+" to double"; return false;}
if(s<0.0 || s>=60.0) {err="Seconds must be not lesser then 0 and lesser then 60"; return false;}
return true;
}
bool Convert(double num)
bool Convert(double num, std::string& err)
{
isdeg=false;
r=num;
@ -181,24 +206,25 @@ struct gmt_region: public gmt_struct
// Only "global", "global180" and "global360"
// TODO: add parsing of "-R" strings
bool Convert(const std::string& istr)
bool Convert(const std::string& istr, std::string& err)
{
std::string str=istr;
tolower(str);
if("global180"==str)
{
type=GLOBAL180;
xb.Convert(-180.0); xe.Convert(180.0);
yb.Convert(-90.0); ye.Convert(90.0);
xb.Convert(-180.0,err); xe.Convert(180.0,err);
yb.Convert(-90.0,err); ye.Convert(90.0,err);
return true;
}
if("global360"==str || "global"==str)
{
type=GLOBAL360;
xb.Convert(0.0); xe.Convert(360.0);
yb.Convert(-90.0); ye.Convert(90.0);
xb.Convert(0.0,err); xe.Convert(360.0,err);
yb.Convert(-90.0,err); ye.Convert(90.0,err);
return true;
}
err="Can't convert "+istr+" to region.";
return false;
}
@ -277,24 +303,58 @@ struct gmt_projection: public gmt_struct
case(OType::C): {ret="-JOb"+o.clon.Value()+"/"+o.clat.Value()+"/"+o.polelon.Value()+"/"+o.polelat.Value()+"/"+ToString(width)+"c"; break;}
default: return "";
}
break;
}
case(CASSINI): {ret="-JC"+c.clon.Value()+"/"+c.clat.Value()+"/"+ToString(width)+"c"; break;}
case(CYL_EQA): {ret="-JY"+y.stpar.Value()+"/"+y.cmer.Value()+"/"+ToString(width)+"c"; break;}
case(CYL_EQA): {ret="-JY"+y.cmer.Value()+"/"+y.stpar.Value()+"/"+ToString(width)+"c"; break;}
case(MILLER): {ret="-JJ"+j.cmer.Value()+"/"+ToString(width)+"c"; break;}
case(CYL_STERE): {ret="-JCyl_stere"+cyl_stere.stpar.Value()+"/"+cyl_stere.cmer.Value()+"/"+ToString(width)+"c"; break;}
case(CYL_STERE): {ret="-JCyl_stere/"+cyl_stere.stpar.Value()+"/"+cyl_stere.cmer.Value()+"/"+ToString(width)+"c"; break;}
default: return "";
}
ret+=" "+region.Value();
return ret;
}
bool SetType(const std::string& s)
bool SetType(const std::string& s, std::string& err)
{
proj=NOTDEF;
std::string str=s;
tolower(str);
if(projnames.end()==projnames.find(str)) return false;
proj=projnames[str];
return true;
// Handle one symbol cases
if(str.size()==1) switch(str[0])
{
case('c'): {proj=CASSINI; return true;}
case('j'): {proj=MILLER; return true;}
case('m'): {proj=MERCATOR; return true;}
case('o'): {proj=OBLIQMERCATOR; return true;}
case('q'): {proj=CYL_EQU; return true;}
case('t'): {proj=TRANSMERCATOR; return true;}
case('x'): {proj=XY; return true;}
case('y'): {proj=CYL_EQA; return true;}
default: break;
}
// Handle long GMT names
if("cyl_stere"==str) {proj=CYL_STERE; return true;}
// Handle common names
std::string t1;
for(const auto& m: projnames)
{
TemplateComparator cmp(m.first);
bool res=cmp.Compare(str);
if(!res) continue;
if(NOTDEF!=proj) // Ambiguos string
{
err="Ambiguous projection definition: did you mean \""+t1+"\" or \""+cmp.Template2Name()+"\"?";
proj=NOTDEF;
return false;
}
t1=cmp.Template2Name();
proj=m.second;
}
if(NOTDEF!=proj) return true; // All Ok
err="Unknown projection: "+s;
return false;
}
static void FillProjNames();
@ -505,18 +565,29 @@ struct gmt_color: public gmt_struct
cyan=cc.cyan; magenta=cc.magenta; yellow=cc.yellow; black=cc.black;
}
void ToModel(ColorModel newmodel)
{
switch(newmodel)
{
case(RGB): ToRGB(); return;
case(GRAY): ToGray(); return;
case(HSV): ToHSV(); return;
case(CMYK): ToCMYK(); return;
}
}
// Interpret one numeric argument as gray value
bool Convert(double gr)
bool Convert(double gr, std::string& err)
{
Value2RGB g(gr);
bool suc=true;
model=GRAY;
transparency=0.0;
gray=g(&suc);
gray=g(&suc,err);
return suc;
}
bool Convert(const std::string& istr)
bool Convert(const std::string& istr, std::string& err)
{
std::string cstr=istr;
tolower(cstr);
@ -533,7 +604,7 @@ struct gmt_color: public gmt_struct
cstr=*ci;
ci++;
Value2Transp t(*ci);
transparency=t(&suc);
transparency=t(&suc,err);
if(!suc) return false; // Parse error
}
}
@ -541,9 +612,9 @@ struct gmt_color: public gmt_struct
WordList wl_slash=Split(cstr,"/"),wl_hyphen=Split(cstr,"-");
WordList::size_type slash_size=wl_slash.size(),hyphen_size=wl_hyphen.size();
// Checks
if(slash_size>1 && hyphen_size>1) return false; // Delimiter can be "/" or "-", not both
if(2==slash_size || slash_size>4) return false; // Size can be 1 or 3 for rgb or 4 for cmyk
if(2==hyphen_size || hyphen_size>3) return false; // Size can be 1 or 3 for hsv
if(slash_size>1 && hyphen_size>1) {err="Delimeter of color components must be \"/\" or \"-\", not both"; return false;}
if(2==slash_size || slash_size>4) {err="Number of color components must be 1 for GRAY, 3 for RGB or 4 for CMYK"; return false;}
if(2==hyphen_size || hyphen_size>3) {err="There is 3 color components in HSV model"; return false;}
// Gray or name
// TODO: Hex representation
if(1==slash_size && 1==hyphen_size)
@ -552,7 +623,8 @@ struct gmt_color: public gmt_struct
Value2RGB g(cstr);
bool suc=true;
model=GRAY;
gray=g(&suc);
gray=g(&suc,err);
err="Can't understand string "+cstr+", this is nor color name, nor gray value (0-255)";
return suc;
}
// RGB
@ -563,9 +635,9 @@ struct gmt_color: public gmt_struct
Value2RGB blue(wl_slash.front());
bool suc=true;
model=RGB;
r=red(&suc);
g=green(&suc);
b=blue(&suc);
r=red(&suc,err); if(!suc) return suc;
g=green(&suc,err);if(!suc) return suc;
b=blue(&suc,err);
return suc;
}
// HSV
@ -576,9 +648,9 @@ struct gmt_color: public gmt_struct
Value2SV v(wl_hyphen.front());
bool suc=true;
model=HSV;
hue=h(&suc);
saturation=s(&suc);
value=v(&suc);
hue=h(&suc,err); if(!suc) return suc;
saturation=s(&suc,err);if(!suc) return suc;
value=v(&suc,err);
return suc;
}
// CMYK
@ -590,10 +662,10 @@ struct gmt_color: public gmt_struct
Value2CMYK k(wl_slash.front());
bool suc=true;
model=CMYK;
cyan=c(&suc);
magenta=m(&suc);
yellow=y(&suc);
black=k(&suc);
cyan=c(&suc,err); if(!suc) return suc;
magenta=m(&suc,err);if(!suc) return suc;
yellow=y(&suc,err); if(!suc) return suc;
black=k(&suc,err);
return suc;
}
}
@ -638,7 +710,7 @@ struct gmt_dash: public gmt_struct
operator bool() const {return !dash.empty();}
void Clear() {dash.clear(); shift=0.0; wisrel=false;}
bool Convert(const std::string& in, double w=0.0)
bool Convert(const std::string& in, std::string& err, double w=0.0)
{
Clear();
shift=0;
@ -659,12 +731,12 @@ struct gmt_dash: public gmt_struct
// Determine shift
{
WordList wl=Split(in,":");
if(wl.size()>1) return false;
if(1==wl.size())
if(wl.size()>2) {err="Incorrect dash format"; return false;}
if(2==wl.size())
{
Value2Width s(wl.back());
bool suc=true;
shift=s(&suc);
shift=s(&suc,err);
if(!suc) return false; // Parse error
dstr=wl.front();
}
@ -674,7 +746,7 @@ struct gmt_dash: public gmt_struct
{
Value2Width d(i);
bool suc=true;
dash.push_back(d(&suc));
dash.push_back(d(&suc,err));
if(!suc) return false; // Parse error
}
return true;
@ -692,18 +764,18 @@ struct gmt_pen: public gmt_struct
std::string Value() const {return ToString(width/10.0)+"c,"+color.Value()+(dash?(","+dash.Value()):"");}
// Interpret one numeric argument as width value
bool Convert(double in)
bool Convert(double in, std::string& err)
{
Value2Width w(in);
bool suc=true;
color.Convert(0); // Black
color.Convert(0,err); // Black
dash.Clear();
width=w(&suc);
width=w(&suc,err);
return suc;
}
// Convert from string
bool Convert(const std::string& istr)
bool Convert(const std::string& istr, std::string& err)
{
std::string str=istr;
WordList::const_iterator ci;
@ -712,7 +784,7 @@ struct gmt_pen: public gmt_struct
// Defaults
width=default_width;
color.Convert(0); // Black
color.Convert(0,err); // Black
dash.Clear();
if(wl.size()>3) return false; // String is [width][,color][,dash]
@ -721,18 +793,18 @@ struct gmt_pen: public gmt_struct
{
Value2Width w(*ci);
bool suc=true;
width=w(&suc);
width=w(&suc,err);
if(!suc) return false; // Parse error
}
if(wl.end()!=ci) ci++;
if(wl.end()!=ci && 0!=ci->size())
{
if(!color.Convert(*ci)) return false; // Parse error
if(!color.Convert(*ci,err)) return false; // Parse error
}
if(wl.end()!=ci) ci++;
if(wl.end()!=ci && 0!=ci->size())
{
if(!dash.Convert(*ci,width)) return false; // Parse error
if(!dash.Convert(*ci,err,width)) return false; // Parse error
}
return true;
}
@ -752,53 +824,58 @@ struct gmt_font: public gmt_struct
std::string Value() const {return ToString(size)+"p,"+family+","+color.Value();}
// Interpret one numeric argument as size of default black font
bool Convert(double in)
bool Convert(double in, std::string& err)
{
Value2Width s(in);
bool suc=true;
color.Convert(0); // Black
size=s(&suc);
color.Convert(0,err); // Black
size=s(&suc,err);
family=default_family;
return suc;
}
// Convert from string
bool Convert(const std::string& str)
bool Convert(const std::string& str, std::string& err)
{
WordList::const_iterator ci;
WordList wl=Split(str,",",true);
std::string fakeerr;
// Defaults
size=default_size;
family=default_family;
color.Convert(0); // Black
color.Convert(0,fakeerr); // Black
if(wl.size()>3) return false; // String is [size][,family][,color] or [family][,color]
if(wl.size()>3) {err="String "+str+" is not font string"; return false;} // String is [size][,family][,color] or [family][,color]
ci=wl.begin();
if(wl.end()!=ci && 0!=ci->size())
{
Value2Width s(*ci);
bool suc=true;
size=s(&suc);
{
std::string fake;
double newsize=s(&suc,fake);
if(suc) size=newsize;
}
if(!suc) // Parse error. check if argument is font name
{
if(0==families.count(*ci)) return false; // No, argument is not allowed font name
family=*ci;
if(wl.size()>2) return false; // If first word is font name, then words count is 1 or 2.
if(wl.size()>2) {err="String "+str+" is not font string"; return false;} // If first word is font name, then words count is 1 or 2.
goto read_color;
}
}
if(wl.end()!=ci) ci++;
if(wl.end()!=ci && 0!=ci->size())
{
if(0==families.count(*ci)) return false; // Argument is not allowed font name
if(0==families.count(*ci)) {err="Unknown font family: "+(*ci); return false;} // Argument is not allowed font name
family=*ci;
}
read_color:
if(wl.end()!=ci) ci++;
if(wl.end()!=ci && 0!=ci->size())
{
if(!color.Convert(*ci)) return false; // Parse error
if(!color.Convert(*ci,err)) return false; // Parse error
}
return true;
}

2
src/object.cpp

@ -170,7 +170,7 @@ const ObjectBase* Evaluate(ExecExpr& exp, bool* err)
exp.erase(pr);
// Error handling
if(!errl.empty())
if( res==nullptr || res->isError() )
{
const struct grammatic_location& loc=cse->Location();
*err=true; // Raise error flag

11
tests/GMTCoord

@ -0,0 +1,11 @@
# Description: Coord tests. Must passed.
# Status: ok
# Output hash: 1a1ca4aba24d0fe39407eef250167aca5c21373722f5f2a9bd6495cee34abaf2
@use "gmt"
a=Coord("10:30:18");
b=Coord(10.5);
c=Coord(":10:10");
d=Coord("-0:15:18");
print(a,a.n,b,b.n,c,c.n,d,d.n);

8
tests/GMTCoord_e1

@ -0,0 +1,8 @@
# Description: Non-integer degrees in dd:mm:ss mode.
# Status: fail
# Output hash: 9469953f706df02349e019f8b47a499d169113b654aee46cacb966d01a27848c
@use "gmt"
a=Coord("1.0:10");
print(a);

18
tests/GMTProjection

@ -0,0 +1,18 @@
# Description: Projections test
# Status: ok
# Output hash: 18656de8c59c9a218b71dc730a3a1e8f1016179622392b11f052c64210f6fa2d
@use "gmt"
r=Region("130",150,30,50);
px=Projection("dec",10,(10,20,10,20),15);
p=Projection(r,type="cyl equid",h=10,stpar=0);
p1=Projection(p,type="m",w=10);
p2b=Projection("ob",10,r,"eq",(r.xe.n+r.xb.n)/2,(r.ye.n+r.yb.n)/2,140,15);
p2=Projection(p2b,he=10);
p3=Projection("cas",11,r);
p4=Projection(r,type="cequa",11);
p5=Projection(p4,type="j",centralm="130:30");
p6=Projection("cylstereo",10,r);
print(px,p,p1,p2,p3,p4,p5,p6);

10
tests/GMTRegion1

@ -0,0 +1,10 @@
# Description: Global regions test.
# Status: ok
# Output hash: e661246caef9b2a5fa5a96b9d75c0542d2e0e655de9d63d755aff01cd3b4ceba
@use "gmt"
r1=Region("global");
r2=Region("global360");
r3=Region("global180");
print(r1,r1.xb,r1.xe,r2,r2.xb,r2.xe,r3,r3.xb,r3.xe);

9
tests/GMTRegion2

@ -0,0 +1,9 @@
# Description: Test sequential form of Region.
# Status: ok
# Output hash: a8006abe2c42b9e34c692ab88cf2f869ad6a01d1f32e6da842c1320f0d253d1c
@use "gmt"
r1=Region("10:30",20.5,-10.5,100,"bbox");
r2=Region("10:30",-10.5,20.5,100);
print(r1,r2,r1.yb,r2.yb);

13
tests/GMTRegion3

@ -0,0 +1,13 @@
# Description: Check named pairs form of Region.
# Status: ok
# Output hash: cf4acbeb2da3156fe52f1f374d4dc83a1aa0ee129a8aa2b1975ec828dd23cabe
@use "gmt"
r=Region("10:30",-10.5,20.5,100);
r1=Region(r,type="bbox");
r2=Region(r,type="global180");
r3=Region(r,ye=80.5);
l=(xb=r.xb.n-0.5,(xe=20+r.xb.n-10,yb=-10),(ye="11:30:28"));
rr=Region(l, type="bbox");
print(r,r1,r2,r3,rr);

8
tests/GMTRegion_e1

@ -0,0 +1,8 @@
# Description: Check string argument in sequential form of Region.
# Status: fail
# Output hash: 8421e39ea376a83938c3d79d803749f038ecd493a490228add4b8d179d4e8f75
@use "gmt"
r=Region("10:30",-10.5,20.5,100,"global");
print(r);
Loading…
Cancel
Save