GRASS Programming Howto

From GRASS-Wiki
Jump to: navigation, search

Introduction

HINT: this page origins from 2006 - outdated.

The reason for starting this Howto is the fact that it's very hard to get a good overview on the GRASS code base, and documentation is sparse, lacking or not clear on many places. The aim of this Howto therefore, is to do something about that, so it will be easier to contribute to, and better document the GRASS code base. I encourage everybody to submit changes, correct me where I'm wrong and add information on the places where I haven't been able to do so. For a start I've chosen to follow the outline of the Grass Programmer's manual with some changes where needed. So let's start (26-01-2003) and make it more fun/ease to program for GRASS.

Greetings

J.c.m van der Kwast (aka Sjors) Bsc Tropical Agriculture, Plant Production. The Netherlands

Update 2018: an initial overhaul has been done.

Tools

If you are new to programming I suggest that you start looking at a few tutorials on c, which are widely available on the internet. Also try to find somebody who has some experience and get them to help you on you're way (I luckily had a friend who could help me get started, thanks C.W. de Gier). Also have a look at the Programmer's manuals (link: https://grass.osgeo.org/programming7).

To get an impression of how the program/modules work there's a need to help you see which functions are called when, from which place and what it is they pass etc. To do this you'll have to us a debugger like DDD and GDB, here's a link to some debugging hints. This could be a separate chapter with some screen shots. We'll see how to do this.

To enable debug message up you have to give this command in the GRASS shell:

   g.gisenv set="DEBUG=#number"

where #number stands for the level. There are three levels, 1, 3, 5. Play around with these to see the differences in output. If you want output directed to a file it's necessary to edit you're $HOME/.bashrc or $HOME/.bash_profile and place a line like this:

   export GRASS_DEBUG_FILE=/whatever_path/debug.txt or any name you like

You have to logout and login again to make it work (i.e. open a new terminal). Then create an empty file with a text editor or simply with the command:

   $ touch /whatever_path/debug.txt 

To view some of the ouput while running Grass57 you can start a new terminal or xterm and use this command:

   $ tail -f /whatever_path/debug.txt 

This file can grow very big quickly, so remove it once in a while. To re-enable logging, recreate an empty file.

Emacs

Some users use Emacs as the main editor and viewer of the code. To get a overview, it's best to begin with making a TAGS file, which will make an overview of keywords (eg. function names, defines) and point to the places where it's used. To set this up use a command like this in the main directory of the Grass source code:

   $ /Path_to_grass_source_code/find . -name '*.[ch]' -print |etags -

Now you can start Emacs with emacs & from you're shell. For a complete reference read the Emacs manual, here are some of the commands to search the TAGS file. Here is used the Emacs notation, mainly C-, push ctrl key, M- push meta or alt key.

For DEFINES and Functions, this will point to the code of the DEFINE or function:

   'M-.' then type search string etc

For references of functions, this will point to any file where a function is used:

   'M-x tags-search' then type search string etc

Now get handy with the tools and notice how incredibly complex the Grass sources are. This however is you're main source of learning, viewing and understanding what already has been coded.

See grass-mode.el posted by Tyler Smith.

Various notes
  • to show tabs enter toggle-show-tabs-show-ws (install 'emacs-goodies-el' on Debian GNU/Linux), see more info at http://emacswiki.org/emacs/ShowWhiteSpace
  • don't use tabs (setq-default indent-tabs-mode nil)
  • type C-x h M-x untabify to remove all tabs in region

QtCreator

See main article Using QtCreator for GRASS C development.

Once you have the code (new one or fixed one)

Now if you decide to add or change something do this according to the rules explained in the SUBMITTING files (there is one for each programming language) and take a look at the "GNU Coding Standards". Some other hints and needs for the GRASS programmer:

  • Document you're new code well, and in browsing other code and gaining valuable insights, also document this either here, but especially in the code itself.
  • Use G_debug() abundantly in new code as needed.
  • In library functions, use doxygen to extract the comments and documentation out of the source. So learn how to use this when submitting you're findings or changes.

Existing API Documentation

In order to get quick documentation on functions structures while programming there are tools designed for extracting documentation directly from the source code so that documentation may be auto-generated at every update.

The GRASS GIS html API documentation is locally produced in the html/ subdirectory by:

   make htmldocs

or

   make pdfdocs

this will create the API documentation in the doxygenhtml directory just inside the GRASS source tree.


GIS programming in general

In GIS, from a user viewpoint, there's two main aspects in general to deal with. The geographical aspect (coordinates/location) and data we want tie to this position, in order to calculate, combine, interpolate (data and/or coordinates), or whatever it is you want to do with these two aspects. Then there are two main ways of presentation of these aspects, in the form of raster and vector maps. In Grass there's an addition to that in the form of sites. In my understanding this will be changed to either a cell file for raster (already a fact), a definition of point structure for vectors. So in the end this will be not be there anymore as a separate structure.

In GRASS GIS, vector to attribute links are done through a database connection, either directly or via odbc. There's the possibility to add more data then cat(egory) and an attribute label to a map.

Now for the programming part, we'll all have to think about how we can provide a structure which will ease all the tasks needed to do this. In general we should consider most probably that we have the main GRASS program environment, which will be responsible for things like display, i/o handling etc, and modules for specific tasks (eg digitize new map).

Libraries

To gain a insight here look at the directory in $Path_to_grass_source_code/lib/, where all the library code is placed. A division is made according to the several distinctions in tasks these libraries perform. Here's a list of the main libraries so far:

Core libraries:
    gis: GRASS GIS General Library (aka GIS Library)
    raster: GRASS Raster Library
    vector: GRASS Vector Library
    Temporal GIS API: See http://grass.osgeo.org/grass75/manuals/libpython/temporal_framework.html

Further libraries

(the name refers to the directory name in lib/ in the source code)
Display Libraries and Drivers

    display: GRASS Display Library (general display library)
    cairodriver: GRASS Cairo Display Driver
    driver: Graphics monitor driver
    htmldriver: htmldriverlib (HTML graphics driver)
    pngdriver: GRASS GIS PNG Display Driver Library
    psdriver: GRASS Postscript Display Driver Library

Math and Statistics Libraries

    arraystats: GRASS Array Statistics Library (library of statistics for arrays of doubles)
    cdhc: GRASS testing normality & exponentiality Library
    gmath: GRASS Numerical math interface (generic mathematical functions and BLAS/LAPACK library wrapper)
    gpde: GRASS Partial differential equations Library (GPDE)

Raster Libraries

    raster: GRASS Raster Library (2D raster library)
    raster3d: GRASS 3D Raster Volume Library (3D raster aka voxels or volumes)
    rowio: GRASS Row Input/Output Library (library for reading/writing raster rows)
    rst: GRASS Library for interpolation with regularized splines with tension (library for interpolation with regularized splines with tension)
    segment: GRASS Segment Library (segment library for segmented raster reading)
    stats: statslib (statistics library)

Imagery Libraries (image processing)

    cluster: GRASS Cluster analysis statistics Library (library for k-means style of cluster analysis processing)
    imagery: GRASS Imagery Library (library for image processing)

Vector Libraries

    vector: GRASS Vector Library (architecture description)
        dglib: GRASS Directed Graph Library
        vedit: GRASS Vedit Library (vector editing library)
        neta: GRASS Network Analysis Library
        rtree: rtree.h (R search tree library)

Search tree libraries

    btree: btree.h
    btree2: btree2 library
    rtree: rtree.h (R search tree library)

Database Management Libraries

    db: GRASS DataBase Management Interface

OpenGL Libraries and friends

    ogsf: GRASS GIS OGSF Library (OpenGL (R) ported gsurf library (required for NVIZ))
    nviz: GRASS Nviz Library (used by wxGUI Nviz extension and CLI-based Nviz module)

Python API

    python: See GRASS GIS Python library (http://grass.osgeo.org/grass75/manuals/libpython/)

Projection Libraries

    proj: GRASS and the PROJ4 projection library (wrapper to PROJ4 projection library)

Miscellaneous Libraries

    datetime: datetime (DateTime library)
    external: external (External libraries from other projects such as shapelib and CCMATH mathematics library source code)
    fonts: fonts (GRASS fonts library)
    init: Library Initialization (GRASS initialization code + scripts)
    iostream: iostream (fast I/O library)
    lidar: lidar.h (LiDAR data related library)
    linkm: linkm (linked list memory manager)
    manage: GRASS Data Elements Manage Library
    symbol: symbol (Drawing symbols for point vector data library)

Not really a library but it could be is the following:

  GUI programming

The main idea is to provide good documentation for functions in each library in the next paragraphs, and add usable examples of how to use and implement functions. Basically there's a need for a good overview, creating and/or explaining API's etc.

GIS library

Info and functions explained will come here:

https://grass.osgeo.org/programming7/gislib.html

Directory include/Make

 Compile.make
 DB.make
 Dir.make
 Docs.make
 Doxyfile_arch_html.in
 Doxyfile_arch_latex.in
 Doxygen.make
 Etc.make
 Grass.make
 GuiScript.make
 Html.make
 HtmlRules.make
 Install.make
 Lib.make
 Module.make
 Multi.make
 NoHtml.make
 Other.make
 Platform.make.in
 Python.make
 Rules.make
 Script.make
 ScriptRules.make
 Shlib.make
 ShScript.make
 ShScriptRules.make
 Sphinx.make
 Stlib.make
 Vars.make

Raster library

Used for raster map programming.

Vector library

Info and functions explained see next

VectLib

Vector library lib/vector/ consists in fact of 5 libraries. Here's a list:

 dgLib       the Directed Graph Library for network analysis
 digLib      lower level functions
 neta        vector network library
 rtree       spatial index for lower level functions
 vedit       vector editing
 Vlib        higher level functions, intended for programming modules

Imagery library

Info and functions explained will come here: ImgLib

Display Graphics Library

Info and functions explained will come here: DisplGraphicsLib

Rowio Library

Info and functions explained will come here: RowioLib

Segment Library

Info and functions explained will come here: SegLib

Vask Library

Info and functions explained will come here: VaskLib

Projection and Datum Libraries

Info and functions explained will come here: ProjLib

Grid3D Library

Info and functions explained will come here: Grd3dLib

DateTime Library

Info and functions explained will come here: DateTimeLib

OpenGL gsurf Library

Info and functions explained will come here: OpenglLib

GMath numerical Library

Info and functions explained will come here: GMathLib

Bitmap Library

Info and functions explained will come here: BitmapLib


Database Structure

Everything related to internal Grass Database structure

Vector Database Management

Raster Modules

Everything related to Raster Modules (make list in progress)

Example of raster module source code and Makefile: doc/raster/r.example/

List of commands: raster.html

Raster Module Programming

After running G_parse(), input and output file names should be checked to make sure that the input file exists, the output file is not the input file, and the output filename is legal. The third argument can be one of the following:

  • GR_FATAL_EXIT - Prints error and exits program
  • GR_FATAL_PRINT - Prints warning and returns error code
  • GR_FATAL_RETURN - Returns error code
 G_check_input_output_name(input_name, output_name, GR_FATAL_EXIT);

Vector Modules

Everything related to vector modules

Example of vector module source code and Makefile: doc/vector/v.example/

The vector model, API introduction and example module source code is in

https://grass.osgeo.org/programming7/vectorlib.html

Vector module list

Vector module source code is in $grass_source_home/vector/

List of commands: vector.html

Vector Module Programming

v.buffer: An Example

Well first try on description of a module in a almost dummy way. As I'm new to programming please try to help me out, I'm bound to make mistakes. I'll assume that you have a basic understanding of C and are familiar with pointers, arrays and structs. As I do often not have a nice picture in my head containing a array of structs, I often do to some extend know what's happening. Maybe in time we could visualize some of this in a nice way. This module has 1 file main.c and a makefile. I'll begin with main.c and the function main as this seems the most logical place to start. In GRASS modules almost behave like a standalone program as they have standard arguments found in c programs in general. The main thing where there is a difference to a standalone program is the GRASS parser. Let's take a look at standard command line arguments.

   v.buffer help 

Prints help.

   v.buffer input=map output=buffer type=line buffer=100 tolerance=1 debug=clean 

Arguments debug and tolerance are a bit unclear but the rest is self explanatory, this draws a 100 map units buffer around lines in "map", output it to "buffer" with a tolerance of 1 (default).

Now lets take a look at function main() in main.c which has to receive this and do something with it.

    int Main(int argc, char *argv[]);               /* with argc number of arguments including
                                                       program name and argv a pointer to a
                                                       arguments array, normal C form */

Variables depicting arguments for commands are always structured like this (I commented on this):

    {
        struct Map_info In, Out;                    /* info on map format */
        struct line_pnts *Points, *BPoints;         /* Structures to hold line *Points (old map)
                                                       *Bpoints (buffer map) see Dig_Structs.h */
        struct line_cats *Cats;                     /* structure to hold Cats see Dig_Structs.h*/
        char   *mapset;                             /* pointer to mapset */
        struct GModule *module;                     /* Simple description of module see GiS.h */
        struct Option *in_opt, *out_opt, *type_opt, *buffer_opt,  *tolerance_opt, *debug_opt;
                                                    /* pointers to program arguments */
        double buffer, tolerance;                   /* declaration to hold answer you give to
                                                       option arguments */
        int    type, debug;                         /* declaration to hold answer you give to
                                                       option arguments */
        int    ret, nareas, area, nlines, line;     /* declaration to hold answers to function
                                                       calls */
        char   *Areas, *Lines;                      /* declaration to hold answer you give to
                                                       option arguments */

There seems two are always be defined, being struct GModule * and struct Option *. GModule is for outputting a description of the module and what it does. Next function therefore always assigned to pointer

  • module:
        module=G_define_module()                    /* in GisLib ParseR.c  */
        module->description = "description"

Option refers usually to input and/or output map and other arguments. To have a better understanding see GiS.h as it also provides a way to define flags. So here also assigning to pointers, although some standard options have been constructed (vector map in/out etc).

        in_opt = G_define_standard_option(G_OPT_V_INPUT);
        out_opt = G_define_standard_option(G_OPT_V_OUTPUT);

snip see source code

        debug_opt = G_define_option();              /* assign new (not standard) option see GiS.h
                                                       and GisLib ParseR.c */
        debug_opt->key = "debug";
        debug_opt->type = TYPE_STRING;
        debug_opt->required = NO;
        debug_opt->options = "buffer,clean";
        debug_opt->description = "Stop the process in certain stage.";

This last option shows the main items of a Option structure. Key is argument given on the command line, type indicates what kind of answer is to be expected (string, int etc), required tells is an answer must be given in order to process command, options is to compare the answer given where multiple answers are possible, description is self explanatory.

Also Map_info could be considered as standard, see dig_structs.h as it gives info on all supported vector formats.

After declaring all variables to arguments the next function is:

    G_gisinit(argv[0])                              /* I'm not sure, seems some initialization with
                                                       the argument being program name see GisLib
                                                       GisInit.c */     

And then most important the call to parser:

    if (G_parser (argc, argv))                      /* parsing everything and also provides a call
                                                       to go interactively in shell or Gui IF no
                                                       arguments are given and if its possible for
                                                       given module in GiSlib ParseR.c, test if
                                                       argc is same as argv*/
        exit(EXIT_FAILURE);                         /* if not error code */

Input and output file names should be checked to make sure that the input file exists, the output file is not the input file, and the output filename is legal. The third argument can be one of the following:

  • GR_FATAL_EXIT - Prints error and exits program
  • GR_FATAL_PRINT - Prints warning and returns error code
  • GR_FATAL_RETURN - Returns error code
     Vect_check_input_output_name(input_name, output_name, GR_FATAL_EXIT);

Now passing answers to to options before doing the work:

    type = Vect_option_to_types ( type_opt );       /* get type see VliB TypE.c here type refers
                                                       to point, line, area etc */
    buffer = abs ( atof( buffer_opt->answer ) );    /* get buffer answer and convert to float */
    tolerance = atof( tolerance_opt->answer );      /* get tolerance answer and convert to float */
                                                                                
    debug = DEBUG_NONE;
    if ( debug_opt->answer ) {
        if ( debug_opt->answer[0] == 'b' )
           debug = DEBUG_BUFFER;
        else if ( debug_opt->answer[0] == 'c' )
           debug = DEBUG_CLEAN;
    }                                               /* decide if option debug and what kind of
                                                       debug */

    Points = Vect_new_line_struct ();               /* assign to pointer (link to VliB line.c) */
    BPoints = Vect_new_line_struct ();              /* assign to pointer (link to VliB line.c) */
    Cats = Vect_new_cats_struct ();                 /* assign to pointer (link to VliB cats.c) */

This all being done open the vector map

      /* open input vector */
    if ((mapset = G_find_vector2 (in_opt->answer, "")) == NULL) {
         G_fatal_error ( "Could not find input map <%s>\n", in_opt->answer);
    }                                               /* see GisLib Find_Vect.c */
                                                                                
    Vect_set_open_level (2);                        /* determine level 1 or 2 see VliB OpeN.c */
    Vect_open_old (&In, in_opt->answer, mapset);    /* Open map, take a good look at &In dealing
                                                       with reference? see VliB OpeN.c */ 
                                                                                
    Vect_set_fatal_error (GV_FATAL_PRINT);          /* error code see VliB ErroR.c */

    if (0 > Vect_open_new (&Out, out_opt->answer, 0) ) {
         Vect_close (&In);
         exit (EXIT_FAILURE);
    }                                               /* open new map for buffer if error close all
                                                       see VliB OpeN.c and VliB ClosE.c */

Some housekeeping: copying of header and history data

    Vect_copy_head_data (&In, &Out);                /* see VliB Init_Head.c */
    Vect_hist_copy (&In, &Out);                     /* copy see VliB HisT.c */
    Vect_hist_command ( &Out );                     /* actual write see VliB HisT.c */

(below notes are from 2006)

I'm wondering, haven't figured it out yet, if some of these aspects can be hidden better. Take the history and header part, can we not provide for one command which does it all? Also defining options, flags etc seems unnecessary difficult, or am I thinking to OO (object orientated)? Maybe a good template with strict names for all elementary components should be constructed, look at difference for type, it refers to

  1. type of variable, eg string int etc
  2. or type of feature, eg point, line, string.

Could be changed to typeVar and typeFea? Comments please.

Now finally we get to the actual work, making the buffer.

  /* Create buffers' boundaries */
                                                                                
    /* Lines */
    if ( (type & GV_POINTS) || (type & GV_LINES) ) {
        int nlines, line, ltype;                    /* declaring number of, line itself and
                                                       line type. somehow for points or lines? */
                                                                                
        nlines = Vect_get_num_lines ( &In );        /* see VliB Level_Two.c */
                                                                                
        fprintf ( stderr, "Lines buffers ... ");
        for ( line = 1; line <= nlines; line++ ) {
            G_debug ( 3, "line = %d", line );       /* precious system debug see GisLib DebuG.c */
            G_percent ( line, nlines, 2 );          /* give percentage see GisLib PercenT.c */
                                                                                
            ltype = Vect_read_line (&In, Points, NULL, line); /* reading line see VliB ReaD.c
            if ( !(ltype & type ) ) continue;       /* comparing type instructed (option argument)
                                                       and actual line type */
                                                                                
            Vect_line_buffer ( Points, buffer, tolerance, BPoints ); /* perform the math see VliB
                                                                        BuffeR.c */
            Vect_write_line ( &Out, GV_BOUNDARY, BPoints, Cats );    /* write the line to output
                                                                        map see Vlib WritE.c */
        }
        fprintf ( stderr, "\n");                    /* print newline */
    }

Continuing with areas, same procedure in basis, just other variables and functions

 if ( type & GV_AREA ) {
        int i, nareas, area, centroid, nisles, isle; /* declare counter i, number of areas, area
                                                        itself, centroid, number of isle, isle
                                                        itself. this is strictly areas */ 

        nareas = Vect_get_num_areas ( &In );        /* see VliB Level_Two.c */
                                                                                
        fprintf ( stderr, "Areas buffers ... ");
        for ( area = 1; area <= nareas; area++ ) {
            G_percent ( area, nareas, 2 );          /* give percentage see GisLib PercenT.c */
                                                                                
            centroid = Vect_get_area_centroid ( &In, area ); */ get centroid for given area see
                                                                VliB Level_Two.c */
            if ( centroid == 0 ) continue;          /* first found? */
                                                                                
            /* outer ring */
            Vect_get_area_points ( &In, area, Points ); /* see Vlib area.c */
            Vect_line_buffer ( Points, buffer, tolerance, BPoints ); /* perform the math see VliB
                                                                        BuffeR.c */
            Vect_write_line ( &Out, GV_BOUNDARY, BPoints, Cats );    /* write the line to output
                                                                        map see VliB WritE.c */
                                                                                
            /* islands */
            nisles = Vect_get_area_num_isles (&In, area); /* see VliB AreA.c */
            for ( i = 0; i < nisles; i++ ) {
                isle = Vect_get_area_isle (&In, area, i); /* see VliB AreA.c */ 
      Vect_get_isle_points ( &In, isle, Points );       /* get the points see VliB AreA.c */
                                                                                
                Vect_line_buffer ( Points, buffer, tolerance, BPoints ); */ perform the math see
                                                                            VliB BuffeR.c */ 
                Vect_write_line ( &Out, GV_BOUNDARY, BPoints, Cats );     /* write the line to
                                                                             output map see Vlib
                                                                             WritE.c */
            }
        }
        fprintf ( stderr, "\n");
    }
                                                                                
    if ( debug == DEBUG_BUFFER ) {
        stop ( &In, &Out );
        exit (EXIT_SUCCESS);
    }                                               /* if defined debug stop */

Now some after work must be done before writing and building the map. Radim has put some thoughts here, so see if they make sense. It's mainly to do with building a new topology, a mystery to most. In general in grass vector handling there are two levels of interaction. One that deals with simple geometry (level 1) and one with geometry and topology (level 2). This not being documented well and discussed lately on grass list (Jan,Feb 2004), is still confusing especially when dealing with code. Clearly new common boundaries have to be defined and duplicates deleted etc, and some issues with snapping. The point being that all buffer areas are now new areas, so the new map has old areas and new buffer areas.

    /* Create areas */
                                                                                
    /* Break lines */
    fprintf (stderr, "Building parts of topology ...\n" );
    Vect_build_partial ( &Out, GV_BUILD_BASE, stderr ); /* Partial Building the map for main area

                                                           not isles?, I'm still confused, see
                                                           VliB BuilD.c */
                                                                                

    /* Warning: snapping must be done, otherwise collinear boundaries are not broken and
     * topology cannot be built (the same angle). But snapping distance must be
       very, very
     * small, otherwise counterclockwise boundaries can appear in areas outside
       the buffer.
     * I have done some tests on real data (projected) and threshold 1e-8 was not enough,
     * Snapping threshold 1e-7 seems to work. Don't increase until we find example
     * where it is not sufficient. */
    /* TODO: look at snapping threshold better, calculate some theoretical value to avoid
     * the same angles of lines at nodes, don't forget about LongLat data, probably
     * calculate different threshold for each map, depending on map's bounding box */

    fprintf ( stderr, "Snapping boundaries ...\n" );
    Vect_snap_lines ( &Out, GV_BOUNDARY, 1e-7, NULL, stderr );  /* see VliB SnaP.c */
                                                                                
    fprintf ( stderr, "Breaking boundaries ...\n" );
    Vect_break_lines ( &Out, GV_BOUNDARY, NULL, stderr );       /* see VliB Break_Lines.c */
                                                                                
    fprintf ( stderr, "Removing duplicates ...\n" );
    Vect_remove_duplicates ( &Out, GV_BOUNDARY, NULL, stderr ); /* defining new common boundaries
                                                                   and removing duplicates?, see
                                                                   VliB Remove_Duplicates.c */

More thoughts and dealing with Isles

    /* Dangles and bridges don't seem to be necessary if snapping is small enough. */
    
    fprintf ( stderr, "Removing dangles ...\n" );
    Vect_remove_dangles ( &Out, GV_BOUNDARY, -1, NULL, stderr );    /* see VliB DangleS.c */     
                                                                                
    fprintf ( stderr, "Removing bridges ...\n" );
    Vect_remove_bridges ( &Out, NULL, stderr );                     /* see VliB BridgeS.c */
                                                                                
    fprintf ( stderr, "Attaching islands ...\n" );
    Vect_build_partial ( &Out, GV_BUILD_ATTACH_ISLES, stderr );     /* again build partial now for
                                                                       Isle see VliB BuilD.c */
                                                                                
    /* Calculate new centroids for all areas */
    nareas = Vect_get_num_areas ( &Out );                   /* see VliB AreA.c */
    Areas = (char *) G_calloc ( nareas+1, sizeof(char) );   /* dealing with memory for first time
                                                               why necessary here? see GisLib
                                                               alloc.c  */
    for ( area = 1; area <= nareas; area++ ) {
        G_debug ( 3, "area = %d", area );           /* see GisLib DebuG.c */
                                                                                
        ret = area_in_buffer ( &In, &Out, area, type, buffer, tolerance ); /* Function in main.c
                                                                              of v.buffer, is not
                                                                              yet explained but
                                                                              checking if area in
                                                                              output map is in
                                                                              buffer */
                                                                                
        if ( ret ) {
            G_debug ( 3, "  -> in buffer");         /* see GisLib DebuG.c */
      Areas[area] = 1;
        }                                           /* if in buffer (valid) assign 1 */

      /* Write out centroid (before check if correct, so that it is visible for debug option
         argument) */

        if ( debug == DEBUG_CLEAN ) {
            double x, y;
            ret = Vect_get_point_in_area ( &Out, area, &x, &y ); /* new centroid see VliB PolY.c */
            if ( ret < 0 ) {
                G_warning ("Cannot calculate area centroid" );
                continue;
            }                                                    /* Checking again */
            Vect_reset_cats ( Cats );                            /* see VliB CatS.c */
            if ( Areas[area] )
                Vect_cat_set (Cats, 1, 1 );                      /* see VliB CatS.c */
                                                                                
            Vect_reset_line ( Points );                          /* some reset? see VliB LinE.c */
            Vect_append_point ( Points, x, y, 0 );               /* append points for writing to
                                                                    new map see VliB LinE.c */
            Vect_write_line ( &Out, GV_CENTROID, Points, Cats ); /* write centroids to file see
                                                                    VliB WritE.c */
        }
    }

    if ( debug == DEBUG_CLEAN ) {
        stop ( &In, &Out );
        exit (EXIT_SUCCESS);
    }                                               /* again something for Option debug argument */
                                                                                
    /* Make a list of boundaries to be deleted (both sides inside) */

    nlines = Vect_get_num_lines ( &Out );           /* see Vlib Level_Two.c */
    G_debug ( 3, "nlines = %d", nlines );           /* see GisLib DebuG.c */
    Lines = (char *) G_calloc ( nlines+1, sizeof(char) ); /* again allocating memory is beyond me
                                                             now */
                                                                                
    for ( line = 1; line <= nlines; line++ ) {
        int j, side[2], areas[2];
                                                                                
        G_debug ( 3, "line = %d", line );           /* see GisLib DebuG.c */
                                                                                
        if ( !Vect_line_alive ( &Out, line ) ) continue; /* check for validness, see VliB ReaD.c */
                                                                                
        Vect_get_line_areas ( &Out, line, &side[0], &side[1] ); /* checking for number of areas or
                                                                   isle on right or left side of
                                                                   lines see VliB Level_Two */
                                                                                
        for ( j = 0; j < 2; j++ ) {
            if ( side[j] == 0 ) {                   /* area/isle not build */
                areas[j] = 0;
            } else if ( side[j] > 0 ) {             /* area */
                areas[j] = side[j];
            } else { /* < 0 -> island */
                areas[j] = Vect_get_isle_area ( &Out, abs ( side[j] ) ); */ not null, not area,
                                                                            must be isle see VliB
                                                                            AreA.c */ 
            }
        }
                                                                                
        G_debug ( 3, " areas = %d , %d -> Areas = %d, %d", areas[0], areas[1], Areas[areas[0]], Areas[areas[1]] ); 
                                                    /* see GisLib DebuG.c */
        if ( Areas[areas[0]] && Areas[areas[1]] )
            Lines[line] = 1;                        /* assign lines for deletion */
    }
    G_free( Areas );                                /* release memory see GisLib AlloC.c */


    /* Delete boundaries */

    for ( line = 1; line <= nlines; line++ ) {
        if ( Lines[line] ) {
            G_debug ( 3, " delete line %d", line ); /* see GisLib DebuG.c */
            Vect_delete_line ( &Out, line );        /* see VliB WritE.c */
        }
    }
                                                                                
    G_free( Lines );                                /* release memory see GisLib AlloC.c */

     /* Create new centroids */

    Vect_reset_cats ( Cats );                       /* some resetting see VliB CatS.c  */
    Vect_cat_set (Cats, 1, 1 );                     /* writing cats see VliB CatS.c */
    nareas = Vect_get_num_areas ( &Out );           /* See VliB AreA.c */
    for ( area = 1; area <= nareas; area++ ) {
        double x, y;
        G_debug ( 3, "area = %d", area );           /* see GisLib DebuG.c */
                                                                                
        if ( !Vect_area_alive ( &Out, area ) ) continue;    /* see VliB ReaD.c */ 
                                                                                
        ret = area_in_buffer ( &In, &Out, area, type, buffer, tolerance ); /* check areas in
                                                                              output map is in
                                                                              buffer see main.c in
                                                                              v.buffer */
                                                                                
        if ( ret ) {
            ret = Vect_get_point_in_area ( &Out, area, &x, &y );    /* get centroid in area not in
                                                                       isle see Vlib poly.c */ 
            if ( ret < 0 ) {
                G_warning ("Cannot calculate area centroid." );     /* see LibGis ErroR.c
                continue;
            }
                                                                                
            Vect_reset_line ( Points );             /* see Vlib LinE.c */ 
            Vect_append_point ( Points, x, y, 0 );  /* see Vlib LinE.c */
            Vect_write_line ( &Out, GV_CENTROID, Points, Cats ); /* write the centroids see Vlib
                                                                    WritE.c
        }

                                                                                
    fprintf ( stderr, "Attaching centroids ...\n" );
    Vect_build_partial ( &Out, GV_BUILD_CENTROIDS, stderr ); /* again build partial topology but
                                                                now for centroids, still
                                                                confusing, is everything build
                                                                now? */
                                                                                
    stop ( &In, &Out );                             /* defined in main.c of v.buffer */
    exit(EXIT_SUCCESS);                             /* exit program */
}

The workings of the main function should at least be somewhat clear now. Most functions are in the gis or vector library, however there remain the new functions of the module itself being area_in_buffer() to check whether area in output map is in buffer. Basically compare area in input map with output map. This function will also call another function input_distance() being defined in main.c of v.buffer and has something to do with defining the distance from a point of an area in the output map to the nearest feature in the input map. I'm myself not quite sure I understand what is happening, if you do add you're insights. If I understand it's defining the distance to the nearest feature in the buffer and if none are found outside the buffer. If I try to rationalize this, it makes a little more sense. Let's take a look at this:

    + - +       + - +               + - +
    + - +  A    + - +        B      + - +
    + - +       + - +               + - +

    + = buffer
    - = borderline 
    area = centroid

A centroid may fall into buffer but the area as a whole doesn't have to be. So distance to centroid can be smaller than buffer or bigger. That's I believe the main rationale for these functions. Again we'll walk through the code starting with area_in_buffer()

    /*
    * Test if area in Out is in buffer.
    * Return: 1 in buffer
    *         0 outside buffer
    */
    
    int area_in_buffer ( struct Map_info *In, struct Map_info *Out, int area, int type, double
                         buffer, double tolerance ) /* taking arguments which should by now mean
                                                       something */ 
    {
        double dist;                                /* think declaration for something returned by
                                                       function input_distance */
        int    ret, i;                              /* seen before, declaration for return value
                                                       and counter */
        static struct ilist *List = NULL;           /* declaration of list  of integers see
                                                       Dig_Struct.h */
        static struct line_pnts *Points = NULL;     /* setting point structure to NULL see
                                                       Dig_Struct.h */
        double x, y;                                /* declaring as double x and y coordinates? */
                                                                                
        G_debug ( 3, "  area_in_buffer(): area = %d", area ); /* see GisLib DebuG.c */
                                                                                
        if ( List == NULL )
        List = Vect_new_list ();                    /* make new list see VliB LisT.c */  
                                                                                
        if ( Points == NULL )
        Points = Vect_new_line_struct ();           /* new line structure see Vlib LinE.c */


    /* Warning: because of tolerance, centroid in area calculated by Vect_get_point_in_area()
     * may fall into buffer (arc interpolated by polygon) even if area is outside!!! */

    /* Because of finite number of decimal places in double representation, RET is used. */

    /* Test1: Centroid (the only reliable, I think).
     * There are 3 possible cases for the distance of centroid to nearest input feature:
     * 1) < (buffer - tolerance) => area inside the buffer 
     * 2) > buffer => area outside the buffer
     * 3) > (buffer - tolerance) and < buffer (that means within the tolerance) => uncertain */

    ret = Vect_get_point_in_area ( Out, area, &x, &y ); /* get coordinates for a point in an area
                                                           in output map see VliB PolY.c */
    if ( ret == 0 ) { /* centroid OK */
   /* test the distance to nearest input feature */
   dist = input_distance ( In, type, buffer, x, y);       /* this function is in main.c of
                                                              v.buffer explained later */  
   G_debug ( 3, "  centroid %f, %f  dist = %f", x, y, dist );          /* see GisLib DebuG.c */

   if ( dist < (buffer - tolerance - RET) ) { 
       G_debug ( 3, "    centroid in buffer -> area in buffer");       /* see GisLib DebuG.c */
       return 1;
   } else if ( dist > (buffer + RET) ) {
       G_debug ( 3, "centroid outside buffer -> area outside buffer"); /* see GisLib DebuG.c */
       return 0;
   }                                               /* testing for case 1 as described above by
                                                       Radim */

    /* Test2: counterclockwise (ccw) boundary
     * Boundaries around features are written in ccw order, that means area inside buffer is on the
     * left side. Area boundaries are listed in cw order (ccw boundaries as negative). 
     * So if any boundary in area list is negative, i.e. in cww order, i.e. buffer inside area,
     * whole area _must_ be in buffer. But, also areas without such boundary may be in buffer, 
     * see below. */ 
    /* TODO: The problem!!!: unfortunately it is not true. After snap, break and remove duplicate
     * it may happen that ccw line appears in the area outside the buffer!!! */

    Vect_get_area_boundaries ( Out, area, List );   /* get boundaries of areas of in output map and
                                                       put in list plus assign an integer id
                                                       number for looping through them see VliB
                                                       AreA.c */

    for ( i = 0; i <  List->n_values; i++ ) {
   if ( List->value[i] < 0 ) { 
       G_debug ( 3, "    negative boundary -> area in buffer"); /* see GisLib DebuG.c */
       return 1;
   }
    }                                               /* looping through the list to find negative
                                                       areas */

    /* Test3: Input feature within area.
     * I believe, that if no negative boundary was found and area is in buffer, 
     * the distance of the nearest input feature for at least one area vertex must be < (buffer-
       tolerance) 
     * This test is left as last because it is slow (check each vertex). */

    Vect_get_area_points ( Out, area, Points );     /* get the points=vertex of line see VliB
                                                       AreA.c */
    for ( i = 0; i < Points->n_points - 1; i++ ) {
   dist = input_distance ( In, type, buffer, Points->x[i], Points->y[i]); 
   G_debug ( 3, "  vertex dist = %f", dist );                     /* see GisLib DebuG.c */

   if ( dist < (buffer - tolerance - RET) ) {
       G_debug ( 3, "    vertex in buffer -> area in buffer");    /* see GisLib DebuG.c */
       return 1;
   }
    }                                             /* looping though the list and doing the math */


    /* Test4?: It may be that that Test3 does not cover all possible cases. If so, somebody must
     * find such example and send it to me */

    G_debug ( 3, "    -> area outside buffer");                     /* see GisLib DebuG.c */
    return 0;
} 

void stop (  struct Map_info *In,  struct Map_info *Out )   /* function stop called in main() */ 
{
    Vect_close (In);                                    /* see VliB ClosE.c */

    fprintf ( stderr, "Rebuilding topology ...\n" );
    Vect_build_partial ( Out, GV_BUILD_NONE, NULL );    /* see VliB BuilD.c */
    Vect_build (Out, stderr);                           /* see VliB BuilD.c */

    Vect_close (Out);                                   /* see VliB ClosE.c */
}                               /* closing the input map and building and closing the output map */ 


Ok now for the last function which support the last one
described. Though I do not fully understand, it has to with topology
and the distance of the a point to a other feature in the map.

    double input_distance ( struct Map_info *In, int type, double buffer, double x, double y ) 
    {
        int i;                                      /* for looping */
        static struct ilist *List = NULL;           /* setting the list to null */
        static struct line_pnts *Points = NULL;     /* setting the list of line points to null */
        double current_distance;                    /* declaration of */

    BOUND_BOX box;                                  /* see Dig_Structs.h assigning name box */ 

    if ( List == NULL ) 
   List = Vect_new_list ();                        /* See Vlib LisT.c */
    else
   Vect_reset_list ( List );

    if ( Points == NULL ) 
   Points = Vect_new_line_struct ();               /* See Vlib LinE.c */


    /* Inside area ? */
    if ( type & GV_AREA) {
   int area, centroid;
   /* inside */
   area = Vect_find_area ( In, x, y );            /* find a point in an area see Vlib find.c */
   centroid = 0;
   if ( area )
       centroid = Vect_get_area_centroid ( In, area ); /* get the centroid of that area see VLiB
                                                           AreA.c */

   G_debug ( 3, "area = %d centroid = %d", area, centroid );      /* see GisLib DebuG.c */
   if ( centroid ) 
       return 0.0;
    }                                      /* find the area and the centroid, if none return 0.0 */

    /* outside area, within buffer? */
    /* The centroid is in buffer if at least one point/line/boundary is in buffer distance */

    box.E = x + buffer; box.W = x - buffer;
    box.N = y + buffer; box.S = y - buffer;
    box.T = PORT_DOUBLE_MAX; box.B = -PORT_DOUBLE_MAX; /* defining the bounding box see 
                                                          Dig_Structs.h */
    
    Vect_select_lines_by_box ( In, &box, GV_POINTS | GV_LINES, List );  /* see Vlib SelecT.c */
    G_debug ( 3, "  %d lines selected by box", List->n_values );        /* see GisLib DebuG.c */

    current_distance = 2*buffer;               /* defining the the distance as 2* buffer */
    for ( i = 0; i < List->n_values; i++) {
   int line, ltype;                           /* declare line and line type */
   double dist;                               /* declare distance */
   line = List->value[i];                     /* make line addressable in list by a number */
    
   G_debug ( 3, "    line[%d] = %d", i, line );   /* see GisLib DebuG.c */

   ltype = Vect_read_line (In, Points, NULL, line); /* get line types in input map see VliB
                                                        ReaD.c */
   
   Vect_line_distance ( Points, x, y, 0, 0, NULL, NULL, NULL, &dist, NULL, NULL); 
                                                    /* distance of a line? see VliB line.c */ 
   G_debug ( 3, "    dist = %f", dist );           /* see GisLib DebuG.c */   
   if ( dist > buffer ) continue;
   
   /* lines */
   if ( type & ltype ) {
       if ( dist < current_distance )
           current_distance = dist;
   }                          /* test for initial distance if greater than buffer or smaller */

   /* Areas */ 
   if ( (type & GV_AREA) && ltype == GV_BOUNDARY ) {
       int j, side[2], centr[2], area_in;
       
       Vect_get_line_areas ( In, line, &side[0], &side[1] ); /* getting the lines for an area see
                                                                 VliB Level_Two.c */

       for ( j = 0; j < 2; j++ ) { 
      centr[j] = 0;

      if ( side[j] > 0 )
          area_in = side[j];
      else /* island */
          area_in = Vect_get_isle_area ( In, abs ( side[j] ) ); /* testing for isles see VliB
                                                                     AreA.c */

      if ( area_in > 0 )
          centr[j] = Vect_get_area_centroid ( In, area_in ); /* get centroid see VliB AreA.c */
       }
   
       if ( centr[0] || centr[1] ) {
      if ( dist < current_distance )
          current_distance = dist;
       }
   }       
    }
    return current_distance;
}

OK we should be able now to at least have a little understanding of what is happening now. Only thing we did not take a look at are the defines:

    #include <stdlib.h>                                 /* standard lib unixsystem */ 
    #include <string.h>                                 /* standard string lib unixsystem */
    #include "gis.h"                                    /* include gis.h standard Grass system for
                                                           all the structs we've seen */
    #include "Vect.h"                                   /*vect.h standard Grass system for all the
                                                          structs we've seen */

    #define DEBUG_NONE   0                              /* for the option debug */
    #define DEBUG_BUFFER 1                              /* for the option debug */
    #define DEBUG_CLEAN  2                              /* for the option debug */

    /* TODO: look at RET value and use, is it OK? */

    #define RET 0.000000001                             /* Representation error tolerance */ 
                                    /* for the math if the area centroid is in or outside buffer */

Well that's it for now. I'll hope you get the grasp and help out a little because it's by no means perfect now. Also a better system of referral must be thought of. I'm thinking of numbering everything like VliB 1.1 (1.= e.g AreA.c & .1 = first function declared) etc.

Image Data Modules

Everything related to Image Data Modules

List of commands: imagery.html

Region and Mask Modules

Everything related to Region and Mask Modules

g.region.html, r.mask.html

Environment Variables

Everything related to Environment Variables

List: variables.html

Display Symbols

For user contributed symbols see the IconSymbols wiki page.

To see custom symbols in the GUI, you need to make a thumbnail image with ps.map and inkscape. See gui/images/symbols/README in the grass source code for more instructions and a script to do that. Put the resulting pngs in the same ./gui/images/symbols/ directory structure.

File format description

Symbol drawing commands are stored in a text file (one file for each symbol)
which are located in $GISBASE/etc/symbol/group/name or $MAPSET/symbol/group/name

S_read() searches first in mapsets and then in $GISBASE (symbol names in 
$GISBASE may be overidden by symbols of the same group/name in mapset).


File format example: 
VERSION 1.0
BOX -10 -10 10 10
STRING
  LINE
    0 -10 
    0  10
  END
END

A symbol file is composed of objects, each object starts with a keyword and
ends with a new line or 'END'. Some objects may contain other objects.

* Drawing is composed of POLYGONS and STRINGS.
* POLYGONS are composed of RINGS. The first ring is outer (clockwise for now),
  following rings are inner (counter clockwise currently).
* STRINGS and RINGS are composed of LINES and ARCS.

POLYGONS and STRINGS are drawn using the default color and fill color
(specified in module options such as 'd.vect color='). This color may be 
overwritten by using the COLOR and FCOLOR keywords.

---- DESCRIPTION OF OBJECTS ----

In the following descriptions these variables are used:
  N     - integer number
  R G B - RGB color as 3 numbers ranging 0-255
  X,Y   - coordinates (double precision floating point)


Keywords:

VERSION N.N
 Version number

BOX X Y X Y
 Bounding box. Lower left and upper right corner. Larger side of box is taken
 as 1 for symbol scaling. That means that if size of box is 10 x 5, and scale
 in application is 2, all geometry will be multiplied by: 2 / 10.
 Origin of symbol is 0,0.

COLOR NONE | R G B
 Color used for string or outline of polygon. If NONE is used, then the polygon
 outline is not drawn.

FCOLOR NONE | R G B
 Fill color used for polygon. If NONE is used, then the polygon is not filled.

LINE 
  X Y
  X Y
 [X Y]
END
 Line constructed from coordinate pairs.

ARC X Y R A1 A2 [C]
 Arc specified by center (X Y), radius (R), start/end angle in degrees (A1/A2)
 and optionaly direction (C for clockwise, default counter clockwise)
 Note degrees are polar theta not navigational, i.e. 0 is east and 
 values increase counter-clockwise from there. Reducing the angle
 from a full circle does not make it draw like a pacman but rather
 with a straight chord-line from the start/end points on the circle
 as defined by the two angles.

STRING
 [COLOR R G B]
 LINE | ARC
 [LINE | ARC]
END
 String composed of any number of lines and/or arcs.

RING
 LINE | ARC
 [LINE | ARC]
END
 Ring composed of any number of lines and/or arcs. A ring is automatically
 closed with a line from last point to first point.

POLYGON
 [COLOR R G B]
 [FCOLOR R G B]
 RING
 [RING]
END
 A polygon is composed of any number of rings. The first ring is outer
 (clockwise) and others are inner (counter-clockwise).


------------------------------------

Note: restriction for clockwise/counter clockwise rings should be removed 
later and replaced by reordering in symbol library. Order is required by 
ps.map because PS uses even-odd rule for polygon filling.

Internal structure:

A symbol read into memory is stored in structure SYMBOL containing an array 
of parts (SYMBPART). Each part may be of type S_STRING or S_POLYGON, and 
contain an array of chains (SYMBCHAIN) where both STRING or POLYGONS are 
stored. One chain contains an array of elements (SYMBEL).

wxGUI Programming Howto

See main article wxGUI Programming Howto.

Compiling and Installing

Everything related to Compiling and Installing. Makefiles, configure files etc.

Numerical math interface

arraystats: https://grass.osgeo.org/programming7/arraystatslib.html (library of statistics for arrays of doubles)

cdhc: https://grass.osgeo.org/programming7/cdhclib.html (GRASS testing normality & exponentiality Library)

gmath: https://grass.osgeo.org/programming7/gmathlib.html (GRASS Numerical math interface - generic mathematical functions and BLAS/LAPACK library wrapper)

gpde: https://grass.osgeo.org/programming7/gpdelib.html (GRASS Partial differential equations Library (GPDE))