Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Developer Documentation link to the sidebar #4634

Merged
merged 6 commits into from
Jan 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions doc/rst/source/devdocs.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#######################
Developer Documentation
#######################

This section contains low-level documentation for how some aspects of GMT have been
implemented or are in the planning-stages for design and implementation. Users with
ideas for simplifications and general improvements are encouraged to open an issue and
share their thoughts with us.

.. toctree::
:maxdepth: 1
:numbered:

devdocs/long_options
131 changes: 131 additions & 0 deletions doc/rst/source/devdocs/long_options.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
.. _Long_Options:

Proposed Long-Options Implementation
====================================

What is the problem?
--------------------

Since GMT was first initiated, the modules have used a terse UNIX-inspired command-line
syntax::

gmt blockmean -R0/5/0/6 -I1 my_table.txt > new_table.txt

It is not immediately obvious to a new user what the two options means. UNIX tools
have the same problem, e.g., for sorting data into numerical order, one may use

::

sort -g my_table.txt > sorted_table.txt

and nobody will know without reading the documentation for sort what **-g** means. However, one
can now use the equivalent command

::

sort --sort=general-numeric my_table.txt > sorted_table.txt

which is pretty self-explanatory. In the case of GMT, similar long-options alternatives will be
implemented. For instance, the above :doc:`/blockmean` command can also be written

::

gmt blockmean --region=0/5/0/6 --increment=1 my_table.txt > new_table.txt

which now becomes much easier to parse (for humans). Unfortunately, GMT syntax is a bit
more complicated for many options. Due to the inexorable growth of new capabilities,
many options have become more complex and may have optional *modifiers* appended to them.
Consider the common option **-i** that is used to specify which input columns the modules should read. Not only
can it select *which* columns (e.g., **-i**\ 3,2,7-9), it allows optional modifiers that may be
repeated for each column (or column group) that handles basic data transformations. For instance,
let us imagine that the above example needs column 3 to be used as is, but column 2 needs
to be converted by the log10 operator and columns 7-9 must be scaled by 10 and offset by -5. In
standard (short) GMT syntax we would write

::

-i3,2+l,7-9+s10+o-6

which only makes immediate sense to those who wrote the parser. In contrast, for the
long-format syntax it will instead be

::

--read-columns=3,2+log10,7-9+scale=10+offset=-5

which most users might be able to decipher.

Abstraction
-----------

So, there are several steps needed to implement this scheme across all GMT modules:

#. Build a set of long-option to short-option equivalences for the standard GMT
:doc:`../std-opts`. This is only about 30 options.
#. Build sets of long-option to short-option equivalences for all the unique
module options spread across ~150 modules (which includes the supplements).

Clearly, these translation tables will need to address not only the longer *names* for the options but
also the longer names for *modifiers*. We can now be more abstract and state that a general
GMT short-format option actually follows a specific syntax::

-option[directive][+modifier1[arg1]][+modifier2[arg2]][...]

where *option* and any *modifier* are single characters and there may be none, one,
or more modifiers following the initial option and the
optional *directive*. As we saw in the case of **-i**, the sequence of "optional directive
followed by optional modifiers" may in fact be repeated by separating these sequences with
a comma. The corresponding long-format syntax format is represented this way::

-long-option[=directive][+modifier1[=arg1]][+modifier2[=arg2]][...]

where the key differences are

#. The *option* is a mnemonic word and not a single letter
#. Optional directives are appended after an equal sign
#. Optional *modifiers* use mnemonic words and not a single letter
#. Optional modifier arguments are appended after another equal sign


Implementation Details
----------------------

Common Options
~~~~~~~~~~~~~~

The approach taken has been to create a master translation table that relates the short and long
option syntax formats so that a function can be used to translate any general long-option
argument to the equivalent short-option argument. That way, we only need to call this
function at the start of a module and do the replacement. Then, the specific
parsers we already have for common and module options will work as is. This design simplifies
the coding tremendously and only requires us to create the translation tables.
The approach has already been implemented and tested for the ~30 GMT Common Options and
developers can play with this by adding the compiler flag **-DUSE_COMMON_LONG_OPTIONS**
when building GMT.
The translations for the GMT common options are encapsulated in a single include file
(gmt_common_longoptions.h) that populates a *gmt_common_kw* structure and looks like this:

.. literalinclude:: ../../../../../src/gmt_common_longoptions.h
:language: C
:lines: 10-39

Here, *separator* is a comma if more than one repetition of the sequence is allowed, otherwise
it is 0.

Module Options
~~~~~~~~~~~~~~

For the ~150 individual modules it is probably not a good idea to introduce ~150 new
include files as was done for the common options above. Instead, the translation structure
can be stored directly in the module C file. For instance, the local *module_kw* structure
embedded in the blockmean.c module C code looks like this:

.. literalinclude:: ../../../../../src/blockmean.c
:language: C
:lines: 44-52

Given these translations we can execute long-format commands like this::

gmt blockmean --region=0/20/10/56 --increment=1 --registration=pixel --select=sum data.txt > sums.txt

that will sum up all the values that fell inside each bin.
1 change: 1 addition & 0 deletions doc/rst/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,4 @@ Quick links
Debugging GMT <debug>
GMT C API <api>
PostScriptLight C API <postscriptlight>
devdocs
27 changes: 0 additions & 27 deletions src/block_subs.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,33 +87,6 @@ struct BLOCK_CTRL {
} W;
};

static struct GMT_KEYWORD_DICTIONARY module_kw[] = { /* Local options for all the block* modules */
/* separator, short-option, long-option, short-directives, long-directives, short-modifiers, long-modifiers */
{ 0, 'A', "fields", "", "", "", "" },
{ 0, 'C', "center", "", "", "", "" },
#if defined(BLOCKMODE) /* Only blockmode has a -D option */
{ 0, 'D', "bin-width", "", "", "a,c,h,l", "average,center,high,low" },
#endif
#if defined(BLOCKMEAN)
{ 0, 'E', "extend", "", "", "P,p", "prop-simple,prop-weighted" },
#elif defined(BLOCKMODE)
{ 0, 'E', "extend", "r,s", "record,source", "l,h", "lower,higher" },
#else
{ 0, 'E', "extend", "b,r,s", "box-whisker,record,source", "l,h", "lower,higher" },
#endif
{ 0, 'G', "gridfile", "", "", "", "" },
GMT_INCREMENT_KW,
#if !defined(BLOCKMEAN) /* Only blockmedian & blockmode have a -Q option */
{ 0, 'Q', "quicker", "", "", "", "" },
#endif
{ 0, 'S', "select", "m,n,s,w", "mean,count,sum,weight", "", "" },
#if defined(BLOCKMEDIAN) /* Only blockmedian has a -T option */
{ 0, 'T', "quantile", "", "", "", "" },
#endif
{ 0, 'W', "weights", "i,o", "in,out", "s", "sigma" },
{ 0, '\0', "", "", "", "", ""} /* End of list marked with empty option and strings */
};

#if 0
enum GMT_grdval_blks { /* mode for selected item for gridding */
BLK_ITEM_MEAN = 0,
Expand Down
12 changes: 12 additions & 0 deletions src/blockmean.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@

#include "block_subs.h"

static struct GMT_KEYWORD_DICTIONARY module_kw[] = { /* Local options for this module */
/* separator, short_option, long_option, short_directives, long_directives, short_modifiers, long_modifiers */
{ 0, 'A', "fields", "", "", "", "" },
{ 0, 'C', "center", "", "", "", "" },
{ 0, 'E', "extend", "", "", "P,p", "prop-simple,prop-weighted" },
{ 0, 'G', "gridfile", "", "", "", "" },
GMT_INCREMENT_KW, /* Defined in gmt_constant.h since not a true GMT common option (but almost) */
{ 0, 'S', "select", "m,n,s,w", "mean,count,sum,weight", "", "" },
{ 0, 'W', "weights", "i,o", "in,out", "s", "sigma" },
{ 0, '\0', "", "", "", "", ""} /* End of list marked with empty option and strings */
};

enum Block_Modes {
BLK_MODE_NOTSET = 0, /* No -E+p|P (or -Ep) set */
BLK_MODE_OBSOLETE = 1, /* Old -Ep for backwards compatibility; assumes input weights are already set to 1/s^2 */
Expand Down
14 changes: 14 additions & 0 deletions src/blockmedian.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@

#include "block_subs.h"

static struct GMT_KEYWORD_DICTIONARY module_kw[] = { /* Local options for all the block* modules */
/* separator, short_option, long_option, short_directives, long_directives, short_modifiers, long_modifiers */
{ 0, 'A', "fields", "", "", "", "" },
{ 0, 'C', "center", "", "", "", "" },
{ 0, 'E', "extend", "b,r,s", "box-whisker,record,source", "l,h", "lower,higher" },
{ 0, 'G', "gridfile", "", "", "", "" },
GMT_INCREMENT_KW, /* Defined in gmt_constant.h since not a true GMT common option (but almost) */
{ 0, 'Q', "quicker", "", "", "", "" },
{ 0, 'S', "select", "m,n,s,w", "mean,count,sum,weight", "", "" },
{ 0, 'T', "quantile", "", "", "", "" },
{ 0, 'W', "weights", "i,o", "in,out", "s", "sigma" },
{ 0, '\0', "", "", "", "", ""} /* End of list marked with empty option and strings */
};

/* Note: For external calls to block* we do not allow explicit -G options; these should be added by examining -A which
* is required for external calls to make grids, even if just z is requested. This differs from the command line where
* -Az is the default and -G is required to set file name format. */
Expand Down
14 changes: 14 additions & 0 deletions src/blockmode.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@

#include "block_subs.h"

static struct GMT_KEYWORD_DICTIONARY module_kw[] = { /* Local options for all the block* modules */
/* separator, short_option, long_option, short_directives, long_directives, short_modifiers, long_modifiers */
{ 0, 'A', "fields", "", "", "", "" },
{ 0, 'C', "center", "", "", "", "" },
{ 0, 'D', "bin-width", "", "", "a,c,h,l", "average,center,high,low" },
{ 0, 'E', "extend", "r,s", "record,source", "l,h", "lower,higher" },
{ 0, 'G', "gridfile", "", "", "", "" },
GMT_INCREMENT_KW, /* Defined in gmt_constant.h since not a true GMT common option (but almost) */
{ 0, 'Q', "quicker", "", "", "", "" },
{ 0, 'S', "select", "m,n,s,w", "mean,count,sum,weight", "", "" },
{ 0, 'W', "weights", "i,o", "in,out", "s", "sigma" },
{ 0, '\0', "", "", "", "", ""} /* End of list marked with empty option and strings */
};

struct BIN_MODE_INFO { /* Used for histogram binning */
double width; /* The binning width used */
double i_offset; /* 0.5 if we are to bin using the center the bins on multiples of width, else 0.0 */
Expand Down
21 changes: 10 additions & 11 deletions src/gmt_common_longoptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,34 @@
* --<long_option>[=<long_directives][+<long_modifiers>[=<argument>]]
*
* The items below correspond to the named parameters in the GMT_KEYWORD_DICTIONARY structure:
*
* separator, short_option, long_option, short_directives, long_directives, short_modifiers, long_modifiers
*/
/* separator, short_option, long_option, short_directives, long_directives, short_modifiers, long_modifiers */
{ 0, 'B', "frame", "", "", "b,g,n,o,t,s", "box,fill,noframe,oblique-pole,title,subtitle" },
{ 0, 'B', "axis", "x,y,z", "x,y,z", "a,f,l,L,p,s,S,u", "angle,fancy,label,Label,prefix,second-label,Second-label,unit" },
{ 0, 'B', "axis", "x,y,z", "x,y,z", "a,f,l,L,p,s,S,u", "angle,fancy,label,Label,prefix,second_label,Second_label,unit" },
{ 0, 'J', "projection", "", "", "", ""},
{ 0, 'R', "region", "", "", "r,u", "rectangular,unit"},
{ 0, 'U', "timestamp", "", "", "c,j,o", "command,justify,offset"},
{ 0, 'V', "verbosity", "", "", "", ""},
{ 0, 'X', "xshift", "a,c,f,r", "absolute,center,fixed,relative", "", ""},
{ 0, 'Y', "yshift", "a,c,f,r", "absolute,center,fixed,relative", "", ""},
{ 0, 'a', "aspatial", "", "", "", ""},
{ 0, 'b', "binary", "", "", "b,l", "big-endian,little-endian"},
{ 0, 'b', "binary", "", "", "b,l", "bigendian,littleendian"},
{ 0, 'c', "panel", "", "", "", ""},
{ 0, 'd', "nodata", "i,o", "in,out", "", ""},
{ 0, 'e', "find", "", "", "f", "file"},
{ ',', 'f', "coltypes", "i,o", "in,out", "", ""},
{ 0, 'g', "gap", "", "", "n,p", "negative,positive"},
{ 0, 'h', "header", "i,o", "in,out", "c,d,r,t", "columns,delete,remark,title"},
{ ',', 'i', "read-columns", "", "", "l,o,s", "log10,offset,scale"},
{ 0, 'j', "spherical", "e,f,g", "ellipsoidal,flat-earth,great-circle", "", ""},
{ ',', 'i', "incols", "", "", "l,o,s", "log10,offset,scale"},
{ 0, 'j', "spherical", "e,f,g", "ellipsoidal,flatearth,greatcircle", "", ""},
{ 0, 'l', "legend", "", "", "D,G,H,L,N,S,V,f,g,j,o,p,s,w", "drawline,gap,header,linetext,ncols,size,vertline,font,fill,justify,offset,pen,scale,width"},
{ 0, 'n', "interpolation", "b,c,l,n", "b-spline,bicubic,linear,nearest-neighbor", "a,b,c,t", "antialias,bc,clip,threshold"},
{ ',', 'o', "write-columns", "", "", "", ""},
{ 0, 'n', "interpolation", "b,c,l,n", "bspline,bicubic,linear,nearestneighbor", "a,b,c,t", "antialias,bc,clip,threshold"},
{ ',', 'o', "outcols", "", "", "", ""},
{ 0, 'p', "perspective", "x,y,z", "x,y,z", "v,w", "view,world"},
{ ',', 'q', "read-rows", "~", "invert", "a,c,f,s", "per-set,column,per-file,per-segment"},
{ ',', 'q', "write-rows", "~", "invert", "a,c,f,s", "per-set,column,per-file,per-segment"},
{ ',', 'q', "inrows", "~", "invert", "a,c,f,s", "perset,column,perfile,persegment"},
{ ',', 'q', "outrows", "~", "invert", "a,c,f,s", "perset,column,perfile,persegment"},
{ 0, 'r', "registration", "g,p", "gridline,pixel", "", ""},
{ 0, 's', "skip-rows", "", "", "a,r", "any,reverse"},
{ 0, 's', "skiprows", "", "", "a,r", "any,reverse"},
{ 0, 't', "transparency", "", "", "", ""},
{ 0, 'x', "cores", "", "", "", ""},
{ 0, '\0', "", "", "", "", ""} /* End of list is marked with empty short-option code and strings */