Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

r.mask.status: Check mask status through a tool #2390

Merged
merged 25 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
dd39de1
r.mask.status: Check mask status through module
wenzeslaus May 20, 2022
d88dd02
Add a new mask info/status function and add doc for the file.
wenzeslaus May 20, 2022
c92ae7f
Provide all information about the possible reclass with the API and t…
wenzeslaus May 20, 2022
ac64423
Merge remote-tracking branch 'upstream/main' into r_mask_status
neteler Nov 7, 2023
62c83b7
Merge branch 'main' into r_mask_status
echoix Sep 9, 2024
f84b041
Apply suggestions from code review
echoix Sep 9, 2024
8994d40
Apply suggestions from code review
echoix Sep 9, 2024
c7e55b6
Merge remote-tracking branch 'upstream/main' into r_mask_status
wenzeslaus Sep 24, 2024
ac83e11
Use parson for the JSON output
wenzeslaus Sep 24, 2024
b22c85b
Use lib function to get full name
wenzeslaus Sep 25, 2024
1f2fcdc
Make all three formats work in a similar way
wenzeslaus Sep 25, 2024
99929c8
Limit mask name hardcoding to the library. Polish comments.
wenzeslaus Sep 26, 2024
caad7ea
Add missing space
wenzeslaus Sep 26, 2024
cc53fb8
Add examples
wenzeslaus Sep 26, 2024
f522e5a
Add tests
wenzeslaus Sep 27, 2024
c68e41f
Add plain text output (YAML is not translatable and plain text is 'pl…
wenzeslaus Sep 27, 2024
2841aeb
Add test for return code
wenzeslaus Sep 27, 2024
3480a25
Add doc, optimize name reporting
wenzeslaus Oct 11, 2024
fd8d76c
Add more doc
wenzeslaus Oct 11, 2024
50a5f42
For now, use shell, not bash for format
wenzeslaus Oct 11, 2024
c8c9696
Merge remote-tracking branch 'upstream/main' into r_mask_status
wenzeslaus Oct 11, 2024
5f31745
For now, use shell, not bash for format
wenzeslaus Oct 11, 2024
2922763
Comment clean up
wenzeslaus Oct 11, 2024
7822abd
Fix typo
wenzeslaus Oct 11, 2024
12061b4
Remove parentheses for fixture without parameters
wenzeslaus Oct 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/grass/defs/raster.h
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ int Rast_option_to_interp_type(const struct Option *);

/* mask_info.c */
char *Rast_mask_info(void);
bool Rast_mask_status(char *, char *, bool *, char *, char *);
int Rast__mask_info(char *, char *);
bool Rast_mask_is_present(void);

Expand Down
2 changes: 1 addition & 1 deletion lib/init/grass.py
Original file line number Diff line number Diff line change
Expand Up @@ -1667,8 +1667,8 @@ def sh_like_startup(location, location_name, grass_env_file, sh):
)
)

mask2d_test = "r.mask.status -t"
# TODO: have a function and/or module to test this
mask2d_test = 'test -f "$MAPSET_PATH/cell/MASK"'
mask3d_test = 'test -d "$MAPSET_PATH/grid3/RASTER3D_MASK"'

specific_addition = ""
Expand Down
120 changes: 94 additions & 26 deletions lib/raster/mask_info.c
Original file line number Diff line number Diff line change
@@ -1,37 +1,33 @@
/*
*************************************************************
* char * Rast_mask_info ()
*
* returns a printable text of mask information
*
************************************************************
* Rast__mask_info (name, mapset)
*
* char name[GNAME_MAX], mapset[GMAPSET_MAX];
*
* function:
* determine the status off the automatic masking
* and the name of the cell file which forms the mask
/**
* \file lib/raster/mask_info.c
*
* (the mask file is actually MASK in the current mapset,
* but is usually a reclassed cell file, and the reclass
* name and mapset are returned)
* \brief Raster Library - Get mask information
*
* returns:
* -1 no masking (name, mapset undefined)
* name, mapset are undefined
* (C) 1999-2024 by Vaclav Petras and the GRASS Development Team
*
* 1 mask file present, masking on
* name, mapset hold mask file name, mapset
* This program is free software under the GNU General Public
* License (>=v2). Read the file COPYING that comes with GRASS
* for details.
*
***************************************************************/
* \author CERL
* \author Vaclav Petras, NC State University, Center for Geospatial Analytics
*/

#include <string.h>

#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/glocale.h>

/**
* @brief Get a printable text with information about raster mask
*
* Determines if 2D raster mask is present and returns textual information about
* the mask suitable for end-user display. The resulting text is translated.
* Caller is responsible for freeing the memory of the returned string.
*
* @return New string with textual information
*/
char *Rast_mask_info(void)
{
char text[GNAME_MAX + GMAPSET_MAX + 16];
Expand All @@ -53,16 +49,88 @@ char *Rast_mask_info(void)
return G_store(text);
}

/**
* @brief Get raster mask status information
*
* _is_mask_reclass_ is a pointer to a bool variable which
* will be set to true if mask raster is a reclass and false otherwise.
*
* If you are not interested in the underlying reclassified raster map,
* pass NULL pointers for the three reclass parameters:
*
* ```
* Rast_mask_status(name, mapset, NULL, NULL, NULL);
* ```
*
* @param[out] name Name of the raster map used as mask
* @param[out] mapset Name of the mapset the raster is in
* @param[out] is_mask_reclass Will be set to true if mask raster is a reclass
* @param[out] reclass_name Name of the underlying reclassified raster map
* @param[out] reclass_mapset Name of the mapset the reclassified raster is in
*
* @return true if mask is present, false otherwise
*/
bool Rast_mask_status(char *name, char *mapset, bool *is_mask_reclass,
char *reclass_name, char *reclass_mapset)
{
int present = Rast__mask_info(name, mapset);

if (is_mask_reclass && reclass_name && reclass_mapset) {
if (present) {
*is_mask_reclass = Rast_is_reclass("MASK", G_mapset(), reclass_name,
reclass_mapset) > 0;
if (*is_mask_reclass) {
// The original mask values were overwritten in the initial
// info call. Put back the original values, so that we can
// report them to the caller.
strcpy(name, "MASK");
strcpy(mapset, G_mapset());
}
}
else {
*is_mask_reclass = false;
}
}

if (present == 1)
return true;
else
return false;
}

/**
* @brief Get information about the current mask
*
* Determines the status of the automatic masking and the name of the 2D
* raster which forms the mask. Typically, mask is raster called MASK in the
* current mapset, but when used with r.mask, it is usually a reclassed
* raster, and so when a MASK raster is present and it is a reclass raster,
* the name and mapset of the underlying reclassed raster are returned.
*
* The name and mapset is written to the parameter which need to be defined
* with a sufficient size, least as `char name[GNAME_MAX], mapset[GMAPSET_MAX]`.
*
* When the masking is not active, -1 is returned and name and mapset are
* undefined. When the masking is active, 1 is returned and name and mapset
* will hold the name and mapset of the underlying raster.
*
* @param[out] name Name of the raster map used as mask
* @param[out] mapset Name of the map's mapset
*
* @return 1 if mask is present, -1 otherwise
*/
int Rast__mask_info(char *name, char *mapset)
{
char rname[GNAME_MAX], rmapset[GMAPSET_MAX];

strcpy(name, "MASK");
strcpy(mapset, G_mapset());
strcpy(rname, "MASK");
strcpy(rmapset, G_mapset());

if (!G_find_raster(name, mapset))
if (!G_find_raster(rname, rmapset))
return -1;

strcpy(name, rname);
strcpy(mapset, rmapset);
if (Rast_is_reclass(name, mapset, rname, rmapset) > 0) {
strcpy(name, rname);
strcpy(mapset, rmapset);
Expand Down
1 change: 1 addition & 0 deletions raster/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ SUBDIRS = \
r.lake \
r.li \
r.mapcalc \
r.mask.status \
r.mfilter \
r.mode \
r.neighbors \
Expand Down
10 changes: 10 additions & 0 deletions raster/r.mask.status/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
MODULE_TOPDIR = ../..

PGM = r.mask.status

LIBES = $(MANAGELIB) $(RASTERLIB) $(GISLIB) $(PARSONLIB)
DEPENDENCIES = $(MANAGEDEP) $(RASTERDEP) $(GISDEP)

include $(MODULE_TOPDIR)/include/Make/Module.make

default: cmd
184 changes: 184 additions & 0 deletions raster/r.mask.status/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/****************************************************************************
*
* MODULE: r.mask.status
* AUTHORS: Vaclav Petras
* PURPOSE: Report status of raster mask
* COPYRIGHT: (C) 2024 by Vaclav Petras and the GRASS Development Team
*
* This program is free software under the GNU General Public
* License (>=v2). Read the file COPYING that comes with GRASS
* for details.
*
*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <grass/gis.h>
#include <grass/parson.h>
#include <grass/raster.h>
#include <grass/glocale.h>

struct Parameters {
struct Option *format;
struct Flag *like_test;
};

void parse_parameters(struct Parameters *params, int argc, char **argv)
{
struct GModule *module;

module = G_define_module();
G_add_keyword(_("raster"));
G_add_keyword(_("mask"));
G_add_keyword(_("reclassification"));
module->label = _("Reports presence or absence of a raster mask");
module->description =
_("Provides information about the presence of a 2D raster mask"
" as text output or return code");

params->format = G_define_option();
params->format->key = "format";
params->format->type = TYPE_STRING;
params->format->required = NO;
params->format->answer = "plain";
params->format->options = "plain,json,shell,yaml";
params->format->descriptions =
"plain;Plain text output;"
"json;JSON (JavaScript Object Notation);"
"shell;Shell script style output;"
"yaml;YAML (human-friendly data serialization language)";
params->format->description = _("Format for reporting");

params->like_test = G_define_flag();
params->like_test->key = 't';
params->like_test->label =
_("Return code 0 when mask present, 1 otherwise");
params->like_test->description =
_("Behave like the test utility, 0 for true, 1 for false, no output");
// suppress_required is not required given the default value for format.
// Both no parameters and only -t work as expected.

if (G_parser(argc, argv))
exit(EXIT_FAILURE);
}

int report_status(struct Parameters *params)
{

char name[GNAME_MAX];
char mapset[GMAPSET_MAX];
char reclass_name[GNAME_MAX];
char reclass_mapset[GMAPSET_MAX];

bool is_mask_reclass;
bool present = Rast_mask_status(name, mapset, &is_mask_reclass,
reclass_name, reclass_mapset);

// This does not have to be exclusive with the printing, but leaving this
// to a different boolean flag which could do the return code and printing.
// The current implementation really behaves like the test utility which
// facilitates the primary usage of this which is prompt building
// (and there any output would be noise).
if (params->like_test->answer) {
if (present)
return 0;
return 1;
}

// Mask raster
char *full_mask = G_fully_qualified_name(name, mapset);
// Underlying raster if applicable
char *full_underlying = NULL;
if (is_mask_reclass)
full_underlying = G_fully_qualified_name(reclass_name, reclass_mapset);

if (strcmp(params->format->answer, "json") == 0) {
JSON_Value *root_value = json_value_init_object();
JSON_Object *root_object = json_object(root_value);
json_object_set_boolean(root_object, "present", present);
if (present)
json_object_set_string(root_object, "full_name", full_mask);
else
json_object_set_null(root_object, "full_name");
if (is_mask_reclass)
json_object_set_string(root_object, "is_reclass_of",
full_underlying);
else
json_object_set_null(root_object, "is_reclass_of");
char *serialized_string = json_serialize_to_string_pretty(root_value);
puts(serialized_string);
json_free_serialized_string(serialized_string);
json_value_free(root_value);
}
else if (strcmp(params->format->answer, "shell") == 0) {
printf("present=");
if (present)
printf("1");
else
printf("0");
printf("\nfull_name=");
if (present)
printf("%s", full_mask);
printf("\nis_reclass_of=");
if (is_mask_reclass)
printf("%s", full_underlying);
printf("\n");
}
else if (strcmp(params->format->answer, "yaml") == 0) {
printf("present: ");
if (present)
printf("true");
else
printf("false");
printf("\nfull_name: ");
if (present)
printf("|-\n %s", full_mask);
else
printf("null");
// Null values in YAML can be an empty (no) value (rather than null),
// so we could use that, but using the explicit null as a reasonable
// starting point.
printf("\nis_reclass_of: ");
// Using block scalar with |- to avoid need for escaping.
// Alternatively, we could check mapset naming limits against YAML
// escaping needs for different types of strings and do the necessary
// escaping here.
if (is_mask_reclass)
printf("|-\n %s", full_underlying);
else
printf("null");
printf("\n");
}
else {
if (present)
printf(_("Mask is active"));
else
printf(_("Mask is not present"));
if (present) {
printf("\n");
printf(_("Mask name: %s"), full_mask);
}
if (is_mask_reclass) {
printf("\n");
printf(_("Mask is a raster reclassified from: %s"),
full_underlying);
}
printf("\n");
}

G_free(full_mask);
G_free(full_underlying);
return EXIT_SUCCESS;
}

int main(int argc, char **argv)
{
struct Parameters params;

G_gisinit(argv[0]);
parse_parameters(&params, argc, argv);
return report_status(&params);
}
Loading
Loading