From 52a935935286abd627af1422d9f366db5119bfc4 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Tue, 21 May 2019 23:40:56 -0400 Subject: [PATCH 1/9] doc: examples for Segmement Library in dox and as code --- doc/raster/r.example.segment/Makefile | 13 ++ doc/raster/r.example.segment/README.md | 20 ++ doc/raster/r.example.segment/main.c | 163 +++++++++++++++ .../r.example.segment/r.example.segment.html | 34 ++++ include/Make/Doxyfile_arch_html.in | 4 +- include/Make/Doxyfile_arch_latex.in | 4 +- lib/segment/segmentlib.dox | 192 ++++++++++++++++-- 7 files changed, 407 insertions(+), 23 deletions(-) create mode 100644 doc/raster/r.example.segment/Makefile create mode 100644 doc/raster/r.example.segment/README.md create mode 100644 doc/raster/r.example.segment/main.c create mode 100644 doc/raster/r.example.segment/r.example.segment.html diff --git a/doc/raster/r.example.segment/Makefile b/doc/raster/r.example.segment/Makefile new file mode 100644 index 00000000000..b974cc614fe --- /dev/null +++ b/doc/raster/r.example.segment/Makefile @@ -0,0 +1,13 @@ +# to use this file, make this relative to GRASS include/ directory +# or set -DMODULE_TOPDIR=... in make command line +# or (when everything fails) use absolute path to the GRASS source code +MODULE_TOPDIR = ../../.. + +PGM = r.example.segment + +LIBES = $(GISLIB) $(RASTERLIB) $(SEGMENTLIB) +DEPENDENCIES = $(GISDEP) $(RASTERDEP) $(SEGMENTDEP) + +include $(MODULE_TOPDIR)/include/Make/Module.make + +default: cmd diff --git a/doc/raster/r.example.segment/README.md b/doc/raster/r.example.segment/README.md new file mode 100644 index 00000000000..d098a21d308 --- /dev/null +++ b/doc/raster/r.example.segment/README.md @@ -0,0 +1,20 @@ +To compile the example simply use `make` (in this directory): + +``` +make +``` + +To run (the asterisks will match your operating system and version +specific directory and file): + +``` +../../../bin.*/grass* --tmp-location XY --exec bash <=v2). Read the file COPYING that comes with + * GRASS for details. + * + *****************************************************************************/ + +#include + +#include +#include +#include +#include + +/* function declaration */ +static void process(SEGMENT * raster_seg); + +/* main function driving the execution */ +int main(int argc, char *argv[]) +{ + /* input and output raster names and file descriptors */ + char *input_name; + char *output_name; + int input_fd; + int output_fd; + + /* buffer for reading and writing rasters */ + void *buffer; + + /* type of the map (CELL/DCELL/...) */ + RASTER_MAP_TYPE map_type; + + /* variables for current and maximum rows and columns */ + int nrows, ncols; + int row; + + /* history structure holds meta-data (title, comments,..) */ + struct History history; + + /* options and description */ + struct GModule *module; + struct Option *input; + struct Option *output; + + /* initialize GRASS GIS library */ + G_gisinit(argv[0]); + + /* initialize module and its description */ + module = G_define_module(); + G_add_keyword(_("raster")); + G_add_keyword(_("example")); + G_add_keyword(_("segment library")); + G_add_keyword(_("random access")); + module->description = + _("Random access to raster using the Segment Library"); + + /* define parameters */ + input = G_define_standard_option(G_OPT_R_INPUT); + output = G_define_standard_option(G_OPT_R_OUTPUT); + + /* options and flags parser */ + if (G_parser(argc, argv)) + exit(EXIT_FAILURE); + + /* stores options and flags to variables */ + input_name = input->answer; + output_name = output->answer; + + /* determine the input map type (CELL/FCELL/DCELL) */ + map_type = Rast_map_type(input_name, ""); + size_t cell_size = Rast_cell_size(map_type); + + /* open existing raster map for reading */ + input_fd = Rast_open_old(input_name, ""); + + /* open the raster for writing (checks if it possible) */ + output_fd = Rast_open_new(output_name, map_type); + + /* allocate input buffer */ + buffer = Rast_allocate_buf(map_type); + + nrows = Rast_window_rows(); + ncols = Rast_window_cols(); + + /* size of a segment */ + int srows = 64; + int scols = 64; + + /* number of segments in memory */ + int num_seg = 4; + + /* segment structure */ + SEGMENT raster_seg; + + /* initialize the segment structures */ + if (Segment_open(&raster_seg, G_tempfile(), + nrows, ncols, srows, scols, cell_size, num_seg) != 1) + G_fatal_error("Unable to create temporary segment file"); + + /* load data into the segment structures */ + for (row = 0; row < Rast_window_rows(); row++) { + Rast_get_row(input_fd, buffer, row, map_type); + if (Segment_put_row(&raster_seg, buffer, row) < 1) + G_fatal_error(_("Unable to write temporary segment file")); + } + + /* run the actual processing */ + process(&raster_seg); + + /* make sure any pending disk operations take place */ + Segment_flush(&raster_seg); + /* store the data permanently in a raster map */ + for (row = 0; row < Rast_window_rows(); row++) { + Segment_get_row(&raster_seg, buffer, row); + Rast_put_row(output_fd, buffer, map_type); + } + + /* memory cleanup */ + G_free(buffer); + + /* closing raster maps and segment structures */ + Segment_close(&raster_seg); + Rast_close(input_fd); + Rast_close(output_fd); + + /* add command line incantation to history file */ + Rast_short_history(output_name, "raster", &history); + Rast_command_history(&history); + Rast_write_history(output_name, &history); + + exit(EXIT_SUCCESS); +} + +/* This would be the main processing function. + * Here we just hardcode a cell to modify. + */ +static void process(SEGMENT * raster_seg) +{ + /* variable we use to hold the value */ + DCELL value; + + /* row and column to access */ + int row = 4; + int col = 2; + + /* pass the pointer, get the value */ + Segment_get(raster_seg, (void *)&value, row, col); + + value = value + 100; + + /* pass the pointer, set the value */ + Segment_put(raster_seg, (void *)&value, row, col); +} diff --git a/doc/raster/r.example.segment/r.example.segment.html b/doc/raster/r.example.segment/r.example.segment.html new file mode 100644 index 00000000000..702c22860b1 --- /dev/null +++ b/doc/raster/r.example.segment/r.example.segment.html @@ -0,0 +1,34 @@ +

DESCRIPTION

+ +r.example.segment changes one cell value in hardcoded location. +It is meant to demonstrate how to use the Segment Library together with +GRASS GIS raster maps. + +

EXAMPLE

+ +Create a modified version of the raster map "elevation" +(North Carolina sample dataset): + +
+g.region raster=elevation
+r.example.segment input=elevation output=modified_elevation
+r.univar raster_map_1
+r.univar raster_map_2
+
+ +

SEE ALSO

+ + +r.example +v.example + + + +GRASS Programmer's Manual + + +

AUTHORS

+ +Vaclav Petras + +

Last changed: $Date$ diff --git a/include/Make/Doxyfile_arch_html.in b/include/Make/Doxyfile_arch_html.in index 0b4d8fb7b73..ee6b1c8de14 100644 --- a/include/Make/Doxyfile_arch_html.in +++ b/include/Make/Doxyfile_arch_html.in @@ -748,7 +748,7 @@ EXCLUDE_SYMBOLS = # directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = +EXAMPLE_PATH = doc # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp @@ -762,7 +762,7 @@ EXAMPLE_PATTERNS = # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. -EXAMPLE_RECURSIVE = NO +EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see diff --git a/include/Make/Doxyfile_arch_latex.in b/include/Make/Doxyfile_arch_latex.in index a50a1f87b8a..66793d15ee7 100644 --- a/include/Make/Doxyfile_arch_latex.in +++ b/include/Make/Doxyfile_arch_latex.in @@ -748,7 +748,7 @@ EXCLUDE_SYMBOLS = # directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = +EXAMPLE_PATH = doc # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp @@ -762,7 +762,7 @@ EXAMPLE_PATTERNS = # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. -EXAMPLE_RECURSIVE = NO +EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see diff --git a/lib/segment/segmentlib.dox b/lib/segment/segmentlib.dox index e3352674d00..3beacdfb44a 100644 --- a/lib/segment/segmentlib.dox +++ b/lib/segment/segmentlib.dox @@ -1,9 +1,14 @@ /*! \page segmentlib GRASS Segment Library - +\tableofcontents + \author CERL +\author Markus Metz \section segmentintro Introduction @@ -64,21 +69,168 @@ or undocumented, start with the prefix \c Segment_. To avoid name conflicts, programmers should not create variables or routines in their own modules which use this prefix. +\section Loading_the_Segment_Library Including and Loading the Segment Library -\section Segment_Routines Segment Routines +

+The functions and data structures needed in oder to use the Segment +Library are defined in the header file called +\c grass/segment.h and included using: + +\code +#include +\endcode

-The routines in the Segment Library are described below, more or -less in the order they would logically be used in a module. They use a data -structure called SEGMENT which is defined in the header file -\c grass/segment.h that must be included in any code using these -routines: +To compile (link) the code code, the library needs to be specified +by adding the associated variables to the Makefile: + +\code +LIBES = ... $(SEGMENTLIB) +DEPENDENCIES = ... $(SEGMENTDEP) +\endcode + +

+See \ref Compiling_and_Installing_GRASS_Modules for a complete +discussion of Makefiles. + +\section How_to_Use_the_Library How to Use the Library for Raster Maps + +The most typical use of the *Segment Library* in GRASS GIS is with +raster maps. Raster maps are read using row-by-row API and, at the same +time, they might be too large to store in memory. The *Segment Library* +can be used to make them accessible with random access when random +access is needed. +Here we discuss usage with one raster map of type \c DCELL +(double precision floating point). + +To have everything we need available, you need to do several includes: \code #include +#include +#include +#include +\endcode + +First, you need to initialize size-related variables: + +\code +/* size of the whole raster (aka input matrix) */ +int nrows = Rast_window_rows(); +int ncols = Rast_window_cols(); + +/* type and cell size we will work with */ +RASTER_MAP_TYPE map_type = DCELL_TYPE; +size_t cell_size = Rast_cell_size(map_type); + +/* size of a segment */ +int srows = 64; +int scols = 64; + +/* number of segments in memory */ +int num_seg = 4; +\endcode + +Then, you prepare for reading raster map as in any other case +and additionally you also initialize the segment storage: + +\code +/* typically, name of map would be a parameter */ +char *map_name = "raster_map_1"; + +/* buffer for holding one raster row */ +void *buffer = Rast_allocate_buf(map_type); + +/* open existing raster map for reading */ +int raster_fd = Rast_open_old(map_name, ""); + +SEGMENT raster_seg; + +/* initialize the segment structures */ +if (Segment_open(&raster_seg, G_tempfile(), + nrows, ncols, srows, scols, cell_size, num_seg) != 1) + G_fatal_error("Unable to create temporary segment file"); +\endcode + +Now you can set the values in the segment storage to the values of +the raster you need to access: + +\code +for (int row = 0; row < Rast_window_rows(); row++) { + Rast_get_row(raster_fd, buffer, row, map_type); + if (Segment_put_row(&raster_seg, buffer, row) < 1) + G_fatal_error(_("Unable to write temporary segment file")); +} \endcode -\see \ref Loading_the_Segment_Library. +At this point, you can use the *Segment Library* to access the values +in the loaded raster map in a random manner specifying row and column: + +\code +/* variable we use to hold the value */ +DCELL value; + +/* row and column to access */ +int row = 4; +int col = 2; + +/* pass the pointer, get the value */ +Segment_get(&raster_seg, (void *) &value, row, col); + +/* use value here */ +\endcode + +Similarly, we can also modify the values by specifying a row and column: + +\code +/* this is the value we want to use */ +value = 100; + +/* pass the pointer, set the value */ +Segment_put(&raster_seg, (void *) &value, row, col); +\endcode + +In both cases, the function takes pointer to void as a parameter, +so we cast to `void *` to make it clear to compiler that this is what +we intent to do. The library function will handle the value according to +the size of the \c DCELL type we set with Segment_open(). + +Now, we are done with processing and we want to save the data to a +GRASS GIS raster map. Often we this would be a different segment +structure, but here with will store the data we were operating on. + +\code +/* typically, name of map would be a parameter */ +char *output_map_name = "raster_map_2"; + +/* open new raster map for writing */ +int output_raster_fd = Rast_open_new(output_map_name, map_type); + +/* make sure any pending disk operations take place */ +Segment_flush(&raster_seg); + +/* store the data permanently in a raster map */ +for (int row = 0; row < Rast_window_rows(); row++) { + Segment_get_row(&raster_seg, buffer, row); + Rast_put_row(output_raster_fd, buffer, map_type); +} +\endcode + +At the end, close, free memory, and clean up: + +\code +G_free(buffer); +Segment_close(&raster_seg); +Rast_close(output_raster_fd); +Rast_close(raster_fd); +\endcode + +\section Segment_Routines Segment Routines + +

+The routines in the Segment Library are described below, more or +less in the order they would logically be used in a module. They use a data +structure called SEGMENT.

A temporary file needs to be prepared and a SEGMENT structure needs to @@ -393,17 +545,19 @@ the best performance. Calculating segment size as a fraction of the data matrix size, e.g. srows = nrows / 4 + 1, will result in very poor performance, particularly for larger datasets. -\section Loading_the_Segment_Library Loading the Segment Library +\section A_Complete_Example_With_Two_Rasters A Complete Example With Two Rasters -

-The library is loaded by specifying -\code -$(SEGMENTLIB) -\endcode -in the Makefile. +This complete example of a module which loads a raster map into +the *Segment Library* data structures, modifies the values, and +creates a new raster map with these values. -

-See \ref Compiling_and_Installing_GRASS_Modules for a complete -discussion of Makefiles. +\include doc/raster/r.example.segment/main.c + +Makefile for this module looks like this: + +\dontinclude doc/raster/r.example.segment/Makefile + +\skip MODULE_TOPDIR = .. +\until default */ From acd56eb7ac5b3192de5b6e83c75f07e4c745ec93 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Thu, 23 May 2019 16:19:29 -0400 Subject: [PATCH 2/9] doc: advanced example for segmentlib in dox from example code --- .../r.example.segment/r.example.segment.html | 1 + doc/raster/r.example.segmulti/Makefile | 13 ++ doc/raster/r.example.segmulti/README.md | 34 ++++ doc/raster/r.example.segmulti/main.c | 189 ++++++++++++++++++ .../r.example.segmulti.html | 44 ++++ lib/segment/segmentlib.dox | 85 ++++++++ 6 files changed, 366 insertions(+) create mode 100644 doc/raster/r.example.segmulti/Makefile create mode 100644 doc/raster/r.example.segmulti/README.md create mode 100644 doc/raster/r.example.segmulti/main.c create mode 100644 doc/raster/r.example.segmulti/r.example.segmulti.html diff --git a/doc/raster/r.example.segment/r.example.segment.html b/doc/raster/r.example.segment/r.example.segment.html index 702c22860b1..09cc8a5b01d 100644 --- a/doc/raster/r.example.segment/r.example.segment.html +++ b/doc/raster/r.example.segment/r.example.segment.html @@ -20,6 +20,7 @@

SEE ALSO

r.example +r.example.segmulti v.example diff --git a/doc/raster/r.example.segmulti/Makefile b/doc/raster/r.example.segmulti/Makefile new file mode 100644 index 00000000000..305e809dbfc --- /dev/null +++ b/doc/raster/r.example.segmulti/Makefile @@ -0,0 +1,13 @@ +# to use this file, make this relative to GRASS include/ directory +# or set -DMODULE_TOPDIR=... in make command line +# or (when everything fails) use absolute path to the GRASS source code +MODULE_TOPDIR = ../../.. + +PGM = r.example.segmulti + +LIBES = $(GISLIB) $(RASTERLIB) $(SEGMENTLIB) +DEPENDENCIES = $(GISDEP) $(RASTERDEP) $(SEGMENTDEP) + +include $(MODULE_TOPDIR)/include/Make/Module.make + +default: cmd diff --git a/doc/raster/r.example.segmulti/README.md b/doc/raster/r.example.segmulti/README.md new file mode 100644 index 00000000000..489896d490d --- /dev/null +++ b/doc/raster/r.example.segmulti/README.md @@ -0,0 +1,34 @@ +To compile the example simply use `make` (in this directory): + +``` +make +``` + +To run (the asterisks will match your operating system and version +specific directory and file): + +``` +../../../bin.*/grass* --tmp-location XY --exec bash <=v2). Read the file COPYING that comes with + * GRASS for details. + * + *****************************************************************************/ + +#include + +#include +#include +#include +#include + + +/* function declaration */ +static void process(SEGMENT * raster_seg, int ninputs); + + +/* main function driving the execution */ +int main(int argc, char *argv[]) +{ + /* input and output raster names and file descriptors */ + char *output_name; + int output_fd; + + /* type of the map (CELL/DCELL/...) */ + RASTER_MAP_TYPE map_type; + + /* variables for current and maximum dimensions */ + int nrows, ncols; + int row, col; + int input, ninputs; + + /* history structure holds meta-data (title, comments,..) */ + struct History history; + + /* options and description */ + struct GModule *module; + struct Option *opt_inputs; + struct Option *opt_output; + + /* initialize GRASS GIS library */ + G_gisinit(argv[0]); + + /* initialize module and its description */ + module = G_define_module(); + G_add_keyword(_("raster")); + G_add_keyword(_("example")); + G_add_keyword(_("segment library")); + G_add_keyword(_("random access")); + module->description = + _("Code explains use of Segment Library with multiple rasters"); + + /* define parameters */ + opt_inputs = G_define_standard_option(G_OPT_R_INPUTS); + opt_output = G_define_standard_option(G_OPT_R_OUTPUT); + + /* options and flags parser */ + if (G_parser(argc, argv)) + exit(EXIT_FAILURE); + + /* stores options and flags to variables */ + output_name = opt_output->answer; + + /* count input rasters */ + for (input = 0; opt_inputs->answers[input] != NULL; input++) ; + ninputs = input; + + nrows = Rast_window_rows(); + ncols = Rast_window_cols(); + + /* size of a segment */ + int srows = 64; + int scols = 64; + + /* number of segments in memory */ + int nsegs = 4; + + map_type = DCELL_TYPE; + size_t cell_size = Rast_cell_size(map_type); + size_t segment_cell_size = cell_size * ninputs; + + /* open the raster for writing (checks if it possible) */ + output_fd = Rast_open_new(output_name, map_type); + + /* segment structure */ + SEGMENT raster_seg; + + /* initialize the segment structures */ + if (Segment_open(&raster_seg, G_tempfile(), + nrows, ncols, srows, scols, segment_cell_size, + nsegs) != 1) + G_fatal_error(_("Unable to create temporary segment file")); + + /* array to store file descriptors */ + int *input_fds = G_malloc(ninputs * sizeof(int)); + + G_message(_("Loading %d raster maps"), ninputs); + + /* open existing raster maps for reading */ + for (input = 0; input < ninputs; input++) { + input_fds[input] = Rast_open_old(opt_inputs->answers[input], ""); + } + + /* allocate input buffer */ + DCELL *row_buffer = Rast_allocate_d_buf(); + DCELL *seg_buffer = G_malloc(ncols * ninputs * sizeof(DCELL)); + + for (row = 0; row < nrows; row++) { + for (input = 0; input < ninputs; input++) { + Rast_get_d_row(input_fds[input], row_buffer, row); + for (col = 0; col < ncols; col++) { + seg_buffer[col * ninputs + input] = row_buffer[col]; + } + } + if (Segment_put_row(&raster_seg, seg_buffer, row) < 1) + G_fatal_error(_("Unable to write temporary segment file")); + } + + /* now run the actual processing */ + process(&raster_seg, ninputs); + + /* make sure any pending disk operations take place */ + Segment_flush(&raster_seg); + /* store the data permanently in a raster map */ + + for (row = 0; row < nrows; row++) { + for (col = 0; col < ncols; col++) { + row_buffer[col] = 0; + } + Segment_get_row(&raster_seg, seg_buffer, row); + for (input = 0; input < ninputs; input++) { + for (col = 0; col < ncols; col++) { + row_buffer[col] += seg_buffer[col * ninputs + input]; + } + } + Rast_put_row(output_fd, row_buffer, map_type); + } + + /* memory cleanup */ + G_free(row_buffer); + G_free(seg_buffer); + + /* closing raster maps and segment structures */ + Segment_close(&raster_seg); + for (input = 0; input < ninputs; input++) { + Rast_close(input_fds[input]); + } + Rast_close(output_fd); + + /* add command line incantation to history file */ + Rast_short_history(output_name, "raster", &history); + Rast_command_history(&history); + Rast_write_history(output_name, &history); + + exit(EXIT_SUCCESS); +} + +/* This would be the main processing function. + * Here we just hardcode a cell to modify. + */ +static void process(SEGMENT * raster_seg, int ninputs) +{ + /* buffer we use to hold the values */ + DCELL *values = G_malloc(ninputs * sizeof(DCELL *)); + + /* row and column to access */ + int row = 1; + int col = 3; + + /* pass the pointer, get the value */ + Segment_get(raster_seg, values, row, col); + + for (int input = 0; input < ninputs; input++) { + values[input] = values[input] + 10000; + } + + /* pass the pointer, set the value */ + Segment_put(raster_seg, values, row, col); +} diff --git a/doc/raster/r.example.segmulti/r.example.segmulti.html b/doc/raster/r.example.segmulti/r.example.segmulti.html new file mode 100644 index 00000000000..601c7ff8d43 --- /dev/null +++ b/doc/raster/r.example.segmulti/r.example.segmulti.html @@ -0,0 +1,44 @@ +

DESCRIPTION

+ +r.example.segmulti changes one cell value in hardcoded location +and sums input rasters together. +It is meant to demonstrate how to use the Segment Library together with +GRASS GIS raster maps. Specifically is focuses on case when multiple +rasters are always accessed together (e.g. image bands) and when their +values can be stored as a same type (here double). + +

EXAMPLE

+ +Set computational region and generate synthetic data: + +
+g.region cols=100 rows=50 -p
+r.mapcalc -s expression='raster_1 = row()'
+r.mapcalc -s expression='raster_2 = 10 * col()'
+r.mapcalc -s expression='raster_3 = 1000'
+
+ +Test the module: + +
+r.example.segmulti input=raster_1,raster_2,raster_3 output=raster_out
+
+ +

SEE ALSO

+ + +r.example +r.example.segment +v.example + + + +GRASS Programmer's Manual + + +

AUTHORS

+ +Vaclav Petras, +NCSU Center for Geospatial Analytics + +

Last changed: $Date$ diff --git a/lib/segment/segmentlib.dox b/lib/segment/segmentlib.dox index 3beacdfb44a..fbdb1f07865 100644 --- a/lib/segment/segmentlib.dox +++ b/lib/segment/segmentlib.dox @@ -225,6 +225,91 @@ Rast_close(output_raster_fd); Rast_close(raster_fd); \endcode +\section Using_the_library_with_multiple_raster_maps Using the Library with Multiple Raster Maps + +In case we want to manipulate multiple raster maps at the same time, +we can use the fact that the *Segment Library* supports storage of +arbitrary data in cell. This is particularly advantageous when we are +always accessing values from all rasters at once since the look up +and possible reading from disk will need to happen just once for +each cell we access. We cover the case when the number of input raster +maps is not known in advance. + +In the following examples, we assume we can convert values from all +raster maps into the same type (we are using double). +We also assume that we got the raster names in command +line options as a GRASS GIS module. +Note that the code is not complete and some definitions or calls are +not shown. + +\dontinclude doc/raster/r.example.segmulti/main.c + +First, we count number of inputs to have the value at hand: + +\skip count +\until ninputs + +We define segment sizes (and other variables) as in the case of one +raster: + +\skip Rast_window_rows +\until nsegs + +However, we define size of one cell (item) stored in the segment +structures as multiplication of size of the raster cell and +number of input rasters: + +\skip DCELL_TYPE +\until segment_cell_size + +We proceed with creating and opening the segment structures in the usual +way: + +\skip Segment_open +\until G_fatal_error + +The opening of rasters needs to happen in a loop. We assume that we can +open that many raster maps at the same time (there is a limit of how +many open file operating system can handle): + +\skip file descriptors +\until } + +We allocate two buffers we will use. One for holding raster map row +and one for holding a row of cell (items) stored in the segment +structures. We will access the row of segment cells as a two-dimensional +field with first dimension being a column and second the ordinal number +of input raster map. + +\skip allocate +\until seg_buffer + +Now, we loop over rows and we load the given row for each of the raster +maps. Then, for each column, i.e., value in a row of the given raster, +we copy the value to its place in the segmentation buffer. + +\skip for (row +\until actual processing + +Now we can do the actual processing which we will discuss later as this +would be in a separate function anyway. After the processing, we write +the resulting rasters. In this example, we do additional processing and +store a sum of all the rasters, so we are writing only one raster: + +\skip Segment_flush +\until memory cleanup + +At the end, we free memory and close rasters: + +\skip G_free +\until Rast_close(output_fd); + +The actual processing with random access needs to get the individual +values stored in each cell in the segment storage. + +\skip hold the values +\until Segment_put + \section Segment_Routines Segment Routines

From 08f9480be79082d10059fc38fec4f4af42f2a8a7 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Thu, 23 May 2019 22:56:22 -0400 Subject: [PATCH 3/9] doc: improved desc for open and get_row in segmentlib --- lib/segment/get_row.c | 17 +++++++++++------ lib/segment/open.c | 25 ++++++++++++++++++++++--- lib/segment/segmentlib.dox | 2 +- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/segment/get_row.c b/lib/segment/get_row.c index 9cb1bd0092c..dd2b8bad7dd 100644 --- a/lib/segment/get_row.c +++ b/lib/segment/get_row.c @@ -21,17 +21,22 @@ /** - * \fn int Segment_get_row (SEGMENT *SEG, void *buf, int row) - * * \brief Read row from segment file. * * Transfers data from a segment file, row by row, into memory - * (which can then be written to a regular matrix file). Seg is the + * (which can then be written to a regular matrix file, typically raster + * map). *SEG* is the * segment structure that was configured from a call to - * Segment_init(). + * Segment_init() or Segment_open(). + * + * *buf* will be filled with ncols*len bytes of data + * corresponding to the *row* in the data matrix. + * + * \pre Segment_flush() needs to be called beforehand in order to make + * all recently written values available. * - * Buf will be filled with ncols*len bytes of data - * corresponding to the row in the data matrix. + * \pre *buf* points to an allocated array of length ncols*len + * where *len* is size of one stored value (cell). * * \param[in] seg segment * \param[in,out] buf diff --git a/lib/segment/open.c b/lib/segment/open.c index 1cf76050d87..a4258f4630a 100644 --- a/lib/segment/open.c +++ b/lib/segment/open.c @@ -21,10 +21,28 @@ /** * \brief Initialize segment structure and open segment file. * - * Initializes the seg structure and prepares a temporary file. - * This fn is a wrapper for Segment_format() and Segment_init() + * Initializes the *seg* structure and prepares a temporary file. * - * Note: The file with name fname will be created anew. + * This function is a wrapper for Segment_format_nofill(), + * Segment_init(), all-in-memory mode initialization, and temporary + * file handling. + * + * The values are not guaranteed to be initialized to zero or NULL. + * You need to initialize them if needed. Typically, initialization + * is done when loading values from existing raster map. + * + * The number of non-segmented rows and columns (*nrows* and *ncols*) + * is typically a computational region. + * + * The number of segments to keep in memory is automatically adjusted + * if it is larger than number of total segments required. + * + * In case of an error, a specific warning will be printed (using + * G_warning()) and a negative number will be returned. + * In case out of memory occurs in all-in-memory mode, + * G_fatal_error() is called. + * + * Note: The file with name *fname* will be created anew. * * \param[in,out] SEG segment * \param[in] fname file name @@ -61,6 +79,7 @@ Segment_open(SEGMENT *SEG, char *fname, off_t nrows, off_t ncols, SEG->len = len; SEG->nseg = nseg; SEG->cache = G_calloc(sizeof(char) * SEG->nrows * SEG->ncols, SEG->len); + /* scb is used to decide if we are in the all-in-memory mode */ SEG->scb = NULL; SEG->open = 1; diff --git a/lib/segment/segmentlib.dox b/lib/segment/segmentlib.dox index fbdb1f07865..0d82806bfdb 100644 --- a/lib/segment/segmentlib.dox +++ b/lib/segment/segmentlib.dox @@ -2,7 +2,7 @@ \tableofcontents From 137e2a1467732ced7f7a5c948bb7973b759221db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:14:17 -0400 Subject: [PATCH 4/9] Apply suggestions from clang-format Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- doc/raster/r.example.segment/main.c | 8 ++++---- doc/raster/r.example.segmulti/main.c | 22 ++++++++++------------ lib/segment/get_row.c | 2 +- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/doc/raster/r.example.segment/main.c b/doc/raster/r.example.segment/main.c index a4edf7c7e46..9b8d085f556 100644 --- a/doc/raster/r.example.segment/main.c +++ b/doc/raster/r.example.segment/main.c @@ -23,7 +23,7 @@ #include /* function declaration */ -static void process(SEGMENT * raster_seg); +static void process(SEGMENT *raster_seg); /* main function driving the execution */ int main(int argc, char *argv[]) @@ -103,8 +103,8 @@ int main(int argc, char *argv[]) SEGMENT raster_seg; /* initialize the segment structures */ - if (Segment_open(&raster_seg, G_tempfile(), - nrows, ncols, srows, scols, cell_size, num_seg) != 1) + if (Segment_open(&raster_seg, G_tempfile(), nrows, ncols, srows, scols, + cell_size, num_seg) != 1) G_fatal_error("Unable to create temporary segment file"); /* load data into the segment structures */ @@ -144,7 +144,7 @@ int main(int argc, char *argv[]) /* This would be the main processing function. * Here we just hardcode a cell to modify. */ -static void process(SEGMENT * raster_seg) +static void process(SEGMENT *raster_seg) { /* variable we use to hold the value */ DCELL value; diff --git a/doc/raster/r.example.segmulti/main.c b/doc/raster/r.example.segmulti/main.c index ec8a57dba16..26340f4f521 100644 --- a/doc/raster/r.example.segmulti/main.c +++ b/doc/raster/r.example.segmulti/main.c @@ -20,10 +20,8 @@ #include #include - /* function declaration */ -static void process(SEGMENT * raster_seg, int ninputs); - +static void process(SEGMENT *raster_seg, int ninputs); /* main function driving the execution */ int main(int argc, char *argv[]) @@ -72,7 +70,8 @@ int main(int argc, char *argv[]) output_name = opt_output->answer; /* count input rasters */ - for (input = 0; opt_inputs->answers[input] != NULL; input++) ; + for (input = 0; opt_inputs->answers[input] != NULL; input++) + ; ninputs = input; nrows = Rast_window_rows(); @@ -96,9 +95,8 @@ int main(int argc, char *argv[]) SEGMENT raster_seg; /* initialize the segment structures */ - if (Segment_open(&raster_seg, G_tempfile(), - nrows, ncols, srows, scols, segment_cell_size, - nsegs) != 1) + if (Segment_open(&raster_seg, G_tempfile(), nrows, ncols, srows, scols, + segment_cell_size, nsegs) != 1) G_fatal_error(_("Unable to create temporary segment file")); /* array to store file descriptors */ @@ -119,8 +117,8 @@ int main(int argc, char *argv[]) for (input = 0; input < ninputs; input++) { Rast_get_d_row(input_fds[input], row_buffer, row); for (col = 0; col < ncols; col++) { - seg_buffer[col * ninputs + input] = row_buffer[col]; - } + seg_buffer[col * ninputs + input] = row_buffer[col]; + } } if (Segment_put_row(&raster_seg, seg_buffer, row) < 1) G_fatal_error(_("Unable to write temporary segment file")); @@ -135,11 +133,11 @@ int main(int argc, char *argv[]) for (row = 0; row < nrows; row++) { for (col = 0; col < ncols; col++) { - row_buffer[col] = 0; + row_buffer[col] = 0; } Segment_get_row(&raster_seg, seg_buffer, row); for (input = 0; input < ninputs; input++) { - for (col = 0; col < ncols; col++) { + for (col = 0; col < ncols; col++) { row_buffer[col] += seg_buffer[col * ninputs + input]; } } @@ -168,7 +166,7 @@ int main(int argc, char *argv[]) /* This would be the main processing function. * Here we just hardcode a cell to modify. */ -static void process(SEGMENT * raster_seg, int ninputs) +static void process(SEGMENT *raster_seg, int ninputs) { /* buffer we use to hold the values */ DCELL *values = G_malloc(ninputs * sizeof(DCELL *)); diff --git a/lib/segment/get_row.c b/lib/segment/get_row.c index 2bf64f7413a..281f6fef500 100644 --- a/lib/segment/get_row.c +++ b/lib/segment/get_row.c @@ -24,7 +24,7 @@ * Transfers data from a segment file, row by row, into memory * (which can then be written to a regular matrix file, typically raster * map). *SEG* is the - * segment structure that was configured from a call to + * segment structure that was configured from a call to * Segment_init() or Segment_open(). * * *buf* will be filled with ncols*len bytes of data From 0feedfc3ef62d3f09c051e766311929666e753c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Fri, 20 Dec 2024 19:22:31 -0500 Subject: [PATCH 5/9] Add language in markdown fenced code blocks --- doc/raster/r.example.segment/README.md | 4 ++-- doc/raster/r.example.segmulti/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/raster/r.example.segment/README.md b/doc/raster/r.example.segment/README.md index d098a21d308..72b5700a285 100644 --- a/doc/raster/r.example.segment/README.md +++ b/doc/raster/r.example.segment/README.md @@ -1,13 +1,13 @@ To compile the example simply use `make` (in this directory): -``` +```shell make ``` To run (the asterisks will match your operating system and version specific directory and file): -``` +```shell ../../../bin.*/grass* --tmp-location XY --exec bash < Date: Sat, 21 Dec 2024 17:50:31 -0500 Subject: [PATCH 6/9] Update README.md --- doc/raster/r.example.segmulti/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/raster/r.example.segmulti/README.md b/doc/raster/r.example.segmulti/README.md index a59f870df09..ddee9993d73 100644 --- a/doc/raster/r.example.segmulti/README.md +++ b/doc/raster/r.example.segmulti/README.md @@ -1,6 +1,6 @@ To compile the example simply use `make` (in this directory): -``` +```shell make ``` From c8024cd69b06b4893fcb7232aeb7bb2ee330be52 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 17 Jul 2026 11:08:01 -0400 Subject: [PATCH 7/9] doc: Update segment library examples to current layout and Markdown Move r.example.segment and r.example.segmulti from doc/raster to doc/examples/raster to follow the directory structure introduced in #4815, and adjust the Makefiles and READMEs for the new depth. Add Markdown documentation files as the canonical documentation and keep the HTML files in sync with them. Fix the cross-links in the SEE ALSO sections (review comment), use the current programming manual URL, link the Segment Library page directly, use the map names from the example in the r.example.segment EXAMPLE section, and drop the obsolete CVS date keyword. Use --tmp-project in the READMEs to match the current command line interface. In the example code, process the raster as DCELL regardless of the input map type. Previously, the segment cell size followed the input map type while process() assumed DCELL, so for CELL and FCELL inputs the modification was silently lost (verified by running the tool before and after the change). Free the value buffer in r.example.segmulti (review comment), allocate it with sizeof(DCELL) instead of sizeof(DCELL *), and mark a message for translation. In the library manual page, point the example includes to the new paths, mention Segment_close() in the cleanup step (review comment), show the buffer deallocation in the processing extract, rename the complete example section to match its content (one input raster), and fix typos. Use an absolute path for the doxygen EXAMPLE_PATH so the examples are found both from the top directory and from per-directory runs such as make -C lib/segment htmldox. Update prepared with AI assistance (Claude). --- .../raster/r.example.segment/Makefile | 2 +- .../raster/r.example.segment/README.md | 4 +- .../raster/r.example.segment/main.c | 10 ++--- .../r.example.segment/r.example.segment.html | 18 ++++---- .../r.example.segment/r.example.segment.md | 32 +++++++++++++++ .../raster/r.example.segmulti/Makefile | 2 +- .../raster/r.example.segmulti/README.md | 5 +-- .../raster/r.example.segmulti/main.c | 5 ++- .../r.example.segmulti.html | 18 ++++---- .../r.example.segmulti/r.example.segmulti.md | 38 +++++++++++++++++ include/Make/Doxyfile_arch_html.in | 2 +- include/Make/Doxyfile_arch_latex.in | 2 +- lib/segment/segmentlib.dox | 41 ++++++++++--------- 13 files changed, 126 insertions(+), 53 deletions(-) rename doc/{ => examples}/raster/r.example.segment/Makefile (93%) rename doc/{ => examples}/raster/r.example.segment/README.md (77%) rename doc/{ => examples}/raster/r.example.segment/main.c (93%) rename doc/{ => examples}/raster/r.example.segment/r.example.segment.html (53%) create mode 100644 doc/examples/raster/r.example.segment/r.example.segment.md rename doc/{ => examples}/raster/r.example.segmulti/Makefile (93%) rename doc/{ => examples}/raster/r.example.segmulti/README.md (85%) rename doc/{ => examples}/raster/r.example.segmulti/main.c (98%) rename doc/{ => examples}/raster/r.example.segmulti/r.example.segmulti.html (57%) create mode 100644 doc/examples/raster/r.example.segmulti/r.example.segmulti.md diff --git a/doc/raster/r.example.segment/Makefile b/doc/examples/raster/r.example.segment/Makefile similarity index 93% rename from doc/raster/r.example.segment/Makefile rename to doc/examples/raster/r.example.segment/Makefile index b974cc614fe..a5d74b981a0 100644 --- a/doc/raster/r.example.segment/Makefile +++ b/doc/examples/raster/r.example.segment/Makefile @@ -1,7 +1,7 @@ # to use this file, make this relative to GRASS include/ directory # or set -DMODULE_TOPDIR=... in make command line # or (when everything fails) use absolute path to the GRASS source code -MODULE_TOPDIR = ../../.. +MODULE_TOPDIR = ../../../.. PGM = r.example.segment diff --git a/doc/raster/r.example.segment/README.md b/doc/examples/raster/r.example.segment/README.md similarity index 77% rename from doc/raster/r.example.segment/README.md rename to doc/examples/raster/r.example.segment/README.md index 72b5700a285..2f87b84fd08 100644 --- a/doc/raster/r.example.segment/README.md +++ b/doc/examples/raster/r.example.segment/README.md @@ -8,7 +8,7 @@ To run (the asterisks will match your operating system and version specific directory and file): ```shell -../../../bin.*/grass* --tmp-location XY --exec bash <answer; output_name = output->answer; - /* determine the input map type (CELL/FCELL/DCELL) */ - map_type = Rast_map_type(input_name, ""); + /* we process the raster as doubles (DCELL) regardless of the input + * map type; the reading function converts the values accordingly */ + map_type = DCELL_TYPE; size_t cell_size = Rast_cell_size(map_type); /* open existing raster map for reading */ @@ -105,7 +105,7 @@ int main(int argc, char *argv[]) /* initialize the segment structures */ if (Segment_open(&raster_seg, G_tempfile(), nrows, ncols, srows, scols, cell_size, num_seg) != 1) - G_fatal_error("Unable to create temporary segment file"); + G_fatal_error(_("Unable to create temporary segment file")); /* load data into the segment structures */ for (row = 0; row < Rast_window_rows(); row++) { diff --git a/doc/raster/r.example.segment/r.example.segment.html b/doc/examples/raster/r.example.segment/r.example.segment.html similarity index 53% rename from doc/raster/r.example.segment/r.example.segment.html rename to doc/examples/raster/r.example.segment/r.example.segment.html index 09cc8a5b01d..ec56674d3a5 100644 --- a/doc/raster/r.example.segment/r.example.segment.html +++ b/doc/examples/raster/r.example.segment/r.example.segment.html @@ -1,8 +1,8 @@

DESCRIPTION

-r.example.segment changes one cell value in hardcoded location. +r.example.segment changes one cell value in a hardcoded location. It is meant to demonstrate how to use the Segment Library together with -GRASS GIS raster maps. +GRASS raster maps.

EXAMPLE

@@ -12,24 +12,24 @@

EXAMPLE

 g.region raster=elevation
 r.example.segment input=elevation output=modified_elevation
-r.univar raster_map_1
-r.univar raster_map_2
+r.univar elevation
+r.univar modified_elevation
 

SEE ALSO

-r.example -r.example.segmulti +r.example, +r.example.segmulti, v.example -GRASS Programmer's Manual +Segment Library +in the +GRASS Programmer's Manual

AUTHORS

Vaclav Petras - -

Last changed: $Date$ diff --git a/doc/examples/raster/r.example.segment/r.example.segment.md b/doc/examples/raster/r.example.segment/r.example.segment.md new file mode 100644 index 00000000000..7ec62fddac3 --- /dev/null +++ b/doc/examples/raster/r.example.segment/r.example.segment.md @@ -0,0 +1,32 @@ +## DESCRIPTION + +*r.example.segment* changes one cell value in a hardcoded location. +It is meant to demonstrate how to use the Segment Library together with +GRASS raster maps. + +## EXAMPLE + +Create a modified version of the raster map "elevation" (North Carolina +sample dataset) and inspect the single changed cell: + +```sh +g.region raster=elevation +r.example.segment input=elevation output=modified_elevation +r.mapcalc expression='difference = modified_elevation - elevation' +r.univar difference +``` + +The difference is zero everywhere except the one hardcoded cell, where +the value increased by 100. + +## SEE ALSO + +*[r.example](r.example.md), [r.example.segmulti](r.example.segmulti.md), +[v.example](v.example.md)* + +*[Segment Library](https://grass.osgeo.org/programming8/segmentlib.html) +in the [GRASS Programmer's Manual](https://grass.osgeo.org/programming8/)* + +## AUTHORS + +Vaclav Petras diff --git a/doc/raster/r.example.segmulti/Makefile b/doc/examples/raster/r.example.segmulti/Makefile similarity index 93% rename from doc/raster/r.example.segmulti/Makefile rename to doc/examples/raster/r.example.segmulti/Makefile index 305e809dbfc..73addb94a90 100644 --- a/doc/raster/r.example.segmulti/Makefile +++ b/doc/examples/raster/r.example.segmulti/Makefile @@ -1,7 +1,7 @@ # to use this file, make this relative to GRASS include/ directory # or set -DMODULE_TOPDIR=... in make command line # or (when everything fails) use absolute path to the GRASS source code -MODULE_TOPDIR = ../../.. +MODULE_TOPDIR = ../../../.. PGM = r.example.segmulti diff --git a/doc/raster/r.example.segmulti/README.md b/doc/examples/raster/r.example.segmulti/README.md similarity index 85% rename from doc/raster/r.example.segmulti/README.md rename to doc/examples/raster/r.example.segmulti/README.md index ddee9993d73..7704449bd9f 100644 --- a/doc/raster/r.example.segmulti/README.md +++ b/doc/examples/raster/r.example.segmulti/README.md @@ -8,7 +8,7 @@ To run (the asterisks will match your operating system and version specific directory and file): ```shell -../../../bin.*/grass* --tmp-location XY --exec bash <DESCRIPTION -r.example.segmulti changes one cell value in hardcoded location +r.example.segmulti changes one cell value in a hardcoded location and sums input rasters together. It is meant to demonstrate how to use the Segment Library together with -GRASS GIS raster maps. Specifically is focuses on case when multiple +GRASS raster maps. Specifically, it focuses on the case when multiple rasters are always accessed together (e.g. image bands) and when their -values can be stored as a same type (here double). +values can be stored as the same type (here double).

EXAMPLE

@@ -27,18 +27,18 @@

EXAMPLE

SEE ALSO

-r.example -r.example.segment +r.example, +r.example.segment, v.example -GRASS Programmer's Manual +Segment Library +in the +GRASS Programmer's Manual

AUTHORS

Vaclav Petras, -NCSU Center for Geospatial Analytics - -

Last changed: $Date$ +NCSU Center for Geospatial Analytics diff --git a/doc/examples/raster/r.example.segmulti/r.example.segmulti.md b/doc/examples/raster/r.example.segmulti/r.example.segmulti.md new file mode 100644 index 00000000000..61362bb6073 --- /dev/null +++ b/doc/examples/raster/r.example.segmulti/r.example.segmulti.md @@ -0,0 +1,38 @@ +## DESCRIPTION + +*r.example.segmulti* changes one cell value in a hardcoded location +and sums input rasters together. +It is meant to demonstrate how to use the Segment Library together with +GRASS raster maps. Specifically, it focuses on the case when multiple +rasters are always accessed together (e.g. image bands) and when their +values can be stored as the same type (here double). + +## EXAMPLE + +Set computational region and generate synthetic data: + +```sh +g.region cols=100 rows=50 -p +r.mapcalc expression='raster_1 = row()' +r.mapcalc expression='raster_2 = 10 * col()' +r.mapcalc expression='raster_3 = 1000' +``` + +Test the module: + +```sh +r.example.segmulti input=raster_1,raster_2,raster_3 output=raster_out +``` + +## SEE ALSO + +*[r.example](r.example.md), [r.example.segment](r.example.segment.md), +[v.example](v.example.md)* + +*[Segment Library](https://grass.osgeo.org/programming8/segmentlib.html) +in the [GRASS Programmer's Manual](https://grass.osgeo.org/programming8/)* + +## AUTHORS + +Vaclav Petras, +[NCSU Center for Geospatial Analytics](https://geospatial.ncsu.edu) diff --git a/include/Make/Doxyfile_arch_html.in b/include/Make/Doxyfile_arch_html.in index 3d9ff901962..1cc11d3c45b 100644 --- a/include/Make/Doxyfile_arch_html.in +++ b/include/Make/Doxyfile_arch_html.in @@ -982,7 +982,7 @@ EXCLUDE_SYMBOLS = # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = doc +EXAMPLE_PATH = @abs_top_srcdir@/doc # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and diff --git a/include/Make/Doxyfile_arch_latex.in b/include/Make/Doxyfile_arch_latex.in index 89eca497c13..4080f6af1e4 100644 --- a/include/Make/Doxyfile_arch_latex.in +++ b/include/Make/Doxyfile_arch_latex.in @@ -982,7 +982,7 @@ EXCLUDE_SYMBOLS = # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = doc +EXAMPLE_PATH = @abs_top_srcdir@/doc # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and diff --git a/lib/segment/segmentlib.dox b/lib/segment/segmentlib.dox index 93f66f31672..71c479db2a2 100644 --- a/lib/segment/segmentlib.dox +++ b/lib/segment/segmentlib.dox @@ -72,7 +72,7 @@ modules which use this prefix. \section Loading_the_Segment_Library Including and Loading the Segment Library

-The functions and data structures needed in oder to use the Segment +The functions and data structures needed in order to use the Segment Library are defined in the header file called \c grass/segment.h and included using: @@ -81,7 +81,7 @@ Library are defined in the header file called \endcode

-To compile (link) the code code, the library needs to be specified +To compile (link) the code, the library needs to be specified by adding the associated variables to the Makefile: \code @@ -90,12 +90,12 @@ DEPENDENCIES = ... $(SEGMENTDEP) \endcode

-See \ref Compiling_and_Installing_GRASS_Modules for a complete +See \ref Compiling_and_Installing_GRASS_Modules for a complete discussion of Makefiles. \section How_to_Use_the_Library How to Use the Library for Raster Maps -The most typical use of the *Segment Library* in GRASS GIS is with +The most typical use of the *Segment Library* in GRASS is with raster maps. Raster maps are read using row-by-row API and, at the same time, they might be too large to store in memory. The *Segment Library* can be used to make them accessible with random access when random @@ -149,7 +149,7 @@ SEGMENT raster_seg; /* initialize the segment structures */ if (Segment_open(&raster_seg, G_tempfile(), nrows, ncols, srows, scols, cell_size, num_seg) != 1) - G_fatal_error("Unable to create temporary segment file"); + G_fatal_error(_("Unable to create temporary segment file")); \endcode Now you can set the values in the segment storage to the values of @@ -191,13 +191,13 @@ Segment_put(&raster_seg, (void *) &value, row, col); \endcode In both cases, the function takes pointer to void as a parameter, -so we cast to `void *` to make it clear to compiler that this is what -we intent to do. The library function will handle the value according to -the size of the \c DCELL type we set with Segment_open(). +so we cast to `void *` to make it clear to the compiler that this is +what we intend to do. The library function will handle the value +according to the size of the \c DCELL type we set with Segment_open(). Now, we are done with processing and we want to save the data to a -GRASS GIS raster map. Often we this would be a different segment -structure, but here with will store the data we were operating on. +GRASS raster map. Often, this would be a different segment +structure, but here we will store the data we were operating on. \code /* typically, name of map would be a parameter */ @@ -238,11 +238,11 @@ maps is not known in advance. In the following examples, we assume we can convert values from all raster maps into the same type (we are using double). We also assume that we got the raster names in command -line options as a GRASS GIS module. +line options as a GRASS module. Note that the code is not complete and some definitions or calls are not shown. -\dontinclude doc/raster/r.example.segmulti/main.c +\dontinclude doc/examples/raster/r.example.segmulti/main.c First, we count number of inputs to have the value at hand: @@ -299,16 +299,19 @@ store a sum of all the rasters, so we are writing only one raster: \skip Segment_flush \until memory cleanup -At the end, we free memory and close rasters: +At the end, we free the memory, close the segment structure with +Segment_close() (which also removes the temporary file), and close +the rasters: \skip G_free \until Rast_close(output_fd); The actual processing with random access needs to get the individual -values stored in each cell in the segment storage. +values stored in each cell in the segment storage. The buffer +allocated for the values is freed at the end. \skip hold the values -\until Segment_put +\until G_free \section Segment_Routines Segment Routines @@ -630,17 +633,17 @@ the best performance. Calculating segment size as a fraction of the data matrix size, e.g. srows = nrows / 4 + 1, will result in very poor performance, particularly for larger datasets. -\section A_Complete_Example_With_Two_Rasters A Complete Example With Two Rasters +\section A_Complete_Example_Module A Complete Example Module -This complete example of a module which loads a raster map into +This is a complete example of a module which loads a raster map into the *Segment Library* data structures, modifies the values, and creates a new raster map with these values. -\include doc/raster/r.example.segment/main.c +\include doc/examples/raster/r.example.segment/main.c Makefile for this module looks like this: -\dontinclude doc/raster/r.example.segment/Makefile +\dontinclude doc/examples/raster/r.example.segment/Makefile \skip MODULE_TOPDIR = .. \until default From 1f45f0374a62215223b0d205558740c2320df585 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 17 Jul 2026 11:08:30 -0400 Subject: [PATCH 8/9] lib/segment: Clarify Segment_open and Segment_get_row docs Address review comments on the changed function documentation: In Segment_open(), state that the underlying bytes read as zeros in both the all-in-memory mode (G_calloc) and the temporary file mode (file created by seeking), instead of the incorrect claim that no initialization is guaranteed, while still noting that values are not initialized to raster NULL values. Describe when the all-in-memory mode is used instead of the vague automatic adjustment wording, and attribute the out of memory fatal error to the underlying G_calloc(). Move the comment about the mode marker to the cache allocation because the library detects the all-in-memory mode by the non-NULL cache, not by scb. In Segment_get_row(), extend the description of the stored value size with examples (review comment) and smooth the wording. Update prepared with AI assistance (Claude). --- lib/segment/get_row.c | 10 +++++----- lib/segment/open.c | 26 +++++++++++++++++--------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/lib/segment/get_row.c b/lib/segment/get_row.c index 03f915f7b71..ec0a3ec1fd0 100644 --- a/lib/segment/get_row.c +++ b/lib/segment/get_row.c @@ -25,10 +25,9 @@ * \brief Read row from segment file. * * Transfers data from a segment file, row by row, into memory - * (which can then be written to a regular matrix file, typically raster - * map). *SEG* is the - * segment structure that was configured from a call to - * Segment_init() or Segment_open(). + * (which can then be written to a regular matrix file, typically + * a raster map). *SEG* is the segment structure that was configured + * from a call to Segment_init() or Segment_open(). * * *buf* will be filled with ncols*len bytes of data * corresponding to the *row* in the data matrix. @@ -37,7 +36,8 @@ * all recently written values available. * * \pre *buf* points to an allocated array of length ncols*len - * where *len* is size of one stored value (cell). + * where *len* is the size of one stored value (e.g., a cell or + * a custom data structure). * * \param[in] seg segment * \param[in,out] buf diff --git a/lib/segment/open.c b/lib/segment/open.c index 7870d122574..535b4086274 100644 --- a/lib/segment/open.c +++ b/lib/segment/open.c @@ -26,20 +26,27 @@ * Segment_init(), all-in-memory mode initialization, and temporary * file handling. * - * The values are not guaranteed to be initialized to zero or NULL. - * You need to initialize them if needed. Typically, initialization - * is done when loading values from existing raster map. + * In both modes, the underlying bytes are initialized to zero: + * the all-in-memory mode allocates the memory with G_calloc() + * and in the temporary file mode, the file is created by seeking + * to its end, so the skipped-over bytes read as zeros. + * Consequently, values read as zeros of the used data type, + * but they are not initialized to, e.g., raster NULL values. + * If other initial values are needed, you need to write them + * first. Typically, values are initialized by loading them from + * an existing raster map. * * The number of non-segmented rows and columns (*nrows* and *ncols*) - * is typically a computational region. + * is typically given by the computational region. * - * The number of segments to keep in memory is automatically adjusted - * if it is larger than number of total segments required. + * If the number of segments to keep in memory (*nseg*) covers the + * whole data matrix, no temporary file is created and all data are + * kept in memory (the all-in-memory mode). * * In case of an error, a specific warning will be printed (using * G_warning()) and a negative number will be returned. - * In case out of memory occurs in all-in-memory mode, - * G_fatal_error() is called. + * If the memory allocation fails in the all-in-memory mode, + * G_fatal_error() is called (by the underlying G_calloc()). * * Note: The file with name *fname* will be created anew. * @@ -74,8 +81,9 @@ int Segment_open(SEGMENT *SEG, char *fname, off_t nrows, off_t ncols, int srows, SEG->ncols = ncols; SEG->len = len; SEG->nseg = nseg; + /* the non-NULL cache marks the all-in-memory mode + * for the other functions in the library */ SEG->cache = G_calloc(sizeof(char) * SEG->nrows * SEG->ncols, SEG->len); - /* scb is used to decide if we are in the all-in-memory mode */ SEG->scb = NULL; SEG->open = 1; From 090d5744a86a858eef1cd3092c734c3447004f0f Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 17 Jul 2026 11:08:30 -0400 Subject: [PATCH 9/9] CI: Compile segment library example modules Compile r.example.segment and r.example.segmulti in the step which compiles the other example modules, so the examples stay buildable without being part of the regular build. Update prepared with AI assistance (Claude). --- .github/workflows/python-code-quality.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index a3c4d440c90..b7bc72e712c 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -201,6 +201,8 @@ jobs: - name: Test compiling example modules run: | ( cd doc/examples/raster/r.example/ && make ) + ( cd doc/examples/raster/r.example.segment/ && make ) + ( cd doc/examples/raster/r.example.segmulti/ && make ) ( cd doc/examples/vector/v.example/ && make ) python-success: