Skip to content

Commit

Permalink
r.colors.out: added json output
Browse files Browse the repository at this point in the history
Signed-off-by: Nishant Bansal <[email protected]>
  • Loading branch information
NishantBansal2003 committed Oct 20, 2024
1 parent 1a803b4 commit 3e7b81e
Show file tree
Hide file tree
Showing 8 changed files with 355 additions and 10 deletions.
8 changes: 4 additions & 4 deletions raster/r.colors.out/Makefile
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
MODULE_TOPDIR = ../..

LIBES2 = $(RASTERLIB) $(GISLIB)
LIBES3 = $(RASTER3DLIB) $(RASTERLIB) $(GISLIB)
LIBES2 = $(RASTERLIB) $(GISLIB) $(PARSONLIB)
LIBES3 = $(RASTER3DLIB) $(RASTERLIB) $(GISLIB) $(PARSONLIB)
DEPENDENCIES = $(RASTER3DDEP) $(GISDEP) $(RASTERDEP)

PROGRAMS = r.colors.out r3.colors.out

r_colors_out_OBJS = raster_main.o
r3_colors_out_OBJS = raster3d_main.o
r_colors_out_OBJS = raster_main.o prt_json.o
r3_colors_out_OBJS = raster3d_main.o prt_json.o

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

Expand Down
10 changes: 10 additions & 0 deletions raster/r.colors.out/local_proto.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <grass/gis.h>
#include <grass/parson.h>

enum OutputFormat { PLAIN, JSON };

void write_json_rule(DCELL *val, DCELL *min, DCELL *max, int r, int g, int b,
FILE *fp, JSON_Array *root_array, int perc);

void Rast_json_print_colors(struct Colors *colors, DCELL min, DCELL max,
FILE *fp, int perc);
107 changes: 107 additions & 0 deletions raster/r.colors.out/prt_json.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/glocale.h>
#include <grass/parson.h>

void write_json_rule(DCELL *val, DCELL *min, DCELL *max, int r, int g, int b,
FILE *fp, JSON_Array *root_array, int perc)
{
static DCELL v0;
static int r0 = -1, g0 = -1, b0 = -1;

if (v0 == *val && r0 == r && g0 == g && b0 == b)
return;
v0 = *val, r0 = r, g0 = g, b0 = b;
JSON_Value *color_value = json_value_init_object();
JSON_Object *color_object = json_object(color_value);

char rgb_string[20];
snprintf(rgb_string, sizeof(rgb_string), "rgb(%d, %d, %d)", r, g, b);

if (perc)
json_object_set_number(color_object, "value",
100 * (*val - *min) / (*max - *min));
else
json_object_set_number(color_object, "value", *val);

json_object_set_string(color_object, "rgb", rgb_string);

json_array_append_value(root_array, color_value);
}

void Rast_json_print_colors(struct Colors *colors, DCELL min, DCELL max,
FILE *fp, int perc)
{
JSON_Value *root_value = json_value_init_array();
JSON_Array *root_array = json_array(root_value);

int i, count;

count = 0;
if (colors->version < 0) {
CELL lo, hi;

Rast_get_c_color_range(&lo, &hi, colors);

for (i = lo; i <= hi; i++) {
unsigned char r, g, b, set;
DCELL val = (DCELL)i;

Rast_lookup_c_colors(&i, &r, &g, &b, &set, 1, colors);
write_json_rule(&val, &min, &max, r, g, b, fp, root_array, perc);
}
}
else {
count = Rast_colors_count(colors);

for (i = 0; i < count; i++) {
DCELL val1, val2;
unsigned char r1, g1, b1, r2, g2, b2;

Rast_get_fp_color_rule(&val1, &r1, &g1, &b1, &val2, &r2, &g2, &b2,
colors, count - 1 - i);

write_json_rule(&val1, &min, &max, r1, g1, b1, fp, root_array,
perc);
write_json_rule(&val2, &min, &max, r2, g2, b2, fp, root_array,
perc);
}
}

{
int r, g, b;

Rast_get_null_value_color(&r, &g, &b, colors);
JSON_Value *nv_value = json_value_init_object();
JSON_Object *nv_object = json_object(nv_value);
char nv_rgb_string[20];
snprintf(nv_rgb_string, sizeof(nv_rgb_string), "rgb(%d, %d, %d)", r, g,
b);
json_object_set_string(nv_object, "value", "nv");
json_object_set_string(nv_object, "rgb", nv_rgb_string);
json_array_append_value(root_array, nv_value);

Rast_get_default_color(&r, &g, &b, colors);
JSON_Value *default_value = json_value_init_object();
JSON_Object *default_object = json_object(default_value);
char default_rgb_string[20];
snprintf(default_rgb_string, sizeof(default_rgb_string),
"rgb(%d, %d, %d)", r, g, b);
json_object_set_string(default_object, "value", "default");
json_object_set_string(default_object, "rgb", default_rgb_string);
json_array_append_value(root_array, default_value);
}

char *json_string = json_serialize_to_string_pretty(root_value);
if (!json_string) {
G_fatal_error(_("Failed to serialize JSON to pretty format."));
}

fputs(json_string, fp);

json_free_serialized_string(json_string);
json_value_free(root_value);

if (fp != stdout)
fclose(fp);
}
27 changes: 24 additions & 3 deletions raster/r.colors.out/raster3d_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@
#include <grass/raster.h>
#include <grass/raster3d.h>
#include <grass/glocale.h>
#include <grass/parson.h>

#include "local_proto.h"

/* Run in raster3d mode */
int main(int argc, char **argv)
{
struct GModule *module;
struct {
struct Option *map, *file;
struct Option *map, *file, *format;
} opt;
struct {
struct Flag *p;
Expand All @@ -38,6 +41,8 @@ int main(int argc, char **argv)
struct Colors colors;
struct FPRange range;

enum OutputFormat format;

G_gisinit(argv[0]);

module = G_define_module();
Expand All @@ -55,6 +60,9 @@ int main(int argc, char **argv)
opt.file->description = _("If not given write to standard output");
opt.file->required = NO;

opt.format = G_define_standard_option(G_OPT_F_FORMAT);
opt.format->guisection = _("Print");

flag.p = G_define_flag();
flag.p->key = 'p';
flag.p->description = _("Output values as percentages");
Expand All @@ -78,8 +86,21 @@ int main(int argc, char **argv)
G_fatal_error(_("Unable to open output file <%s>"), file);
}

Rast_print_colors(&colors, range.min, range.max, fp,
flag.p->answer ? 1 : 0);
if (strcmp(opt.format->answer, "json") == 0) {
format = JSON;
}
else {
format = PLAIN;
}

if (format == JSON) {
Rast_json_print_colors(&colors, range.min, range.max, fp,
flag.p->answer ? 1 : 0);
}
else {
Rast_print_colors(&colors, range.min, range.max, fp,
flag.p->answer ? 1 : 0);
}

exit(EXIT_SUCCESS);
}
27 changes: 24 additions & 3 deletions raster/r.colors.out/raster_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@
#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/glocale.h>
#include <grass/parson.h>

#include "local_proto.h"

/* Run in raster mode */
int main(int argc, char **argv)
{
struct GModule *module;
struct {
struct Option *map, *file;
struct Option *map, *file, *format;
} opt;
struct {
struct Flag *p;
Expand All @@ -37,6 +40,8 @@ int main(int argc, char **argv)
struct Colors colors;
struct FPRange range;

enum OutputFormat format;

G_gisinit(argv[0]);

module = G_define_module();
Expand All @@ -54,6 +59,9 @@ int main(int argc, char **argv)
opt.file->description = _("If not given write to standard output");
opt.file->required = NO;

opt.format = G_define_standard_option(G_OPT_F_FORMAT);
opt.format->guisection = _("Print");

flag.p = G_define_flag();
flag.p->key = 'p';
flag.p->description = _("Output values as percentages");
Expand All @@ -77,8 +85,21 @@ int main(int argc, char **argv)
G_fatal_error(_("Unable to open output file <%s>"), file);
}

Rast_print_colors(&colors, range.min, range.max, fp,
flag.p->answer ? 1 : 0);
if (strcmp(opt.format->answer, "json") == 0) {
format = JSON;
}
else {
format = PLAIN;
}

if (format == JSON) {
Rast_json_print_colors(&colors, range.min, range.max, fp,
flag.p->answer ? 1 : 0);
}
else {
Rast_print_colors(&colors, range.min, range.max, fp,
flag.p->answer ? 1 : 0);
}

exit(EXIT_SUCCESS);
}
116 changes: 116 additions & 0 deletions raster/r.colors.out/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""Fixture for r.colors.out and r3.colors.out test"""

from types import SimpleNamespace

import os
import pytest
import grass.script as gs


def setup_grass_location(tmp_path, location_name):
"""Initialize a new GRASS location."""
gs.core._create_location_xy(tmp_path, location_name)
return tmp_path / location_name


def configure_grass_region(session_env):
"""Configure the GRASS region for testing with defined bounds and resolution."""
gs.run_command(
"g.region", s=0, n=90, w=0, e=100, b=0, t=50, res=10, res3=10, env=session_env
)


def create_test_rasters(session_env):
"""Generate raster layers with specific values for testing."""
gs.run_command(
"r.mapcalc",
expression="test_elev_int_1 = int(rand(-15.0, 5.0))",
seed=1,
env=session_env,
)
gs.run_command(
"r.mapcalc",
expression="test_elev_int_2 = int(rand(0.0, 10.0))",
seed=1,
env=session_env,
)
gs.run_command(
"r.mapcalc",
expression="test_elev_int_3 = int(rand(5.0, 15.0))",
seed=1,
env=session_env,
)

return "test_elev_int_1,test_elev_int_2,test_elev_int_3"


def apply_random_color_to_rasters(raster_names, session_env):
"""Apply random colors to the specified rasters."""
gs.run_command("r.colors", map=raster_names, color="random", env=session_env)


def create_test_rasters3(session_env):
"""Generate raster3 layers with specific values for testing."""
gs.run_command(
"r3.mapcalc",
expression="volume_double = double(col() + row() + depth())",
env=session_env,
)
gs.run_command(
"r3.mapcalc",
expression=(
"volume_double_null = if("
"row() == 1 || row() == 5, "
"null(), volume_double)"
),
env=session_env,
)

return "volume_double_null"


def apply_random_color_to_rasters3(raster3_names, session_env):
"""Apply elevation colors to the specified raster3."""
gs.run_command("r3.colors", map=raster3_names, color="elevation", env=session_env)


@pytest.fixture
def raster_color_dataset(tmp_path_factory):
"""Set up a GRASS session and create test rasters with color rules."""

tmp_path = tmp_path_factory.mktemp("raster_color_test")
location_name = "test_location"
location_path = setup_grass_location(tmp_path, location_name)

with gs.setup.init(location_path, env=os.environ.copy()) as session:
configure_grass_region(session.env)

raster_names = create_test_rasters(session.env)
apply_random_color_to_rasters(raster_names, session.env)

yield SimpleNamespace(
session=session,
raster_names="test_elev_int_3",
env=session.env,
)


@pytest.fixture
def raster3_color_dataset(tmp_path_factory):
"""Set up a GRASS session and create test raster3 with color rules."""

tmp_path = tmp_path_factory.mktemp("raster3_color_test")
location_name = "test_location"
location_path = setup_grass_location(tmp_path, location_name)

with gs.setup.init(location_path, env=os.environ.copy()) as session:
configure_grass_region(session.env)

raster3_names = create_test_rasters3(session.env)
apply_random_color_to_rasters3(raster3_names, session.env)

yield SimpleNamespace(
session=session,
raster3_names=raster3_names,
env=session.env,
)
Loading

0 comments on commit 3e7b81e

Please sign in to comment.