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.horizon: add json output #3534

Merged
merged 3 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion raster/r.horizon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ MODULE_TOPDIR = ../..

PGM = r.horizon

LIBES = $(GPROJLIB) $(RASTERLIB) $(GISLIB) $(MATHLIB) $(PROJLIB)
LIBES = $(PARSONLIB) $(GPROJLIB) $(RASTERLIB) $(GISLIB) $(MATHLIB) $(PROJLIB)
DEPENDENCIES = $(GPROJDEP) $(RASTERDEP) $(GISDEP)
EXTRA_INC = $(PROJINC) $(GDALCFLAGS)

Expand Down
84 changes: 76 additions & 8 deletions raster/r.horizon/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Program was refactored by Anna Petrasova to remove most global variables.
#include <grass/raster.h>
#include <grass/gprojects.h>
#include <grass/glocale.h>
#include <grass/parson.h>

#define WHOLE_RASTER 1
#define SINGLE_POINT 0
Expand Down Expand Up @@ -112,6 +113,8 @@ typedef struct {
const char *horizon_basename;
} Settings;

enum OutputFormat { PLAIN, JSON };

int INPUT(Geometry *geometry, const char *elevin);
int OUTGR(const Settings *settings, char *shad_filename,
struct Cell_head *cellhd);
Expand All @@ -120,7 +123,8 @@ void com_par(const Geometry *geometry, OriginAngle *origin_angle, double angle,
double horizon_height(const Geometry *geometry, const OriginPoint *origin_point,
const OriginAngle *origin_angle);
void calculate_point_mode(const Settings *settings, const Geometry *geometry,
double xcoord, double ycoord, FILE *fp);
double xcoord, double ycoord, FILE *fp,
enum OutputFormat format);
int new_point(const Geometry *geometry, const OriginPoint *origin_point,
const OriginAngle *origin_angle, SearchPoint *search_point,
HorizonProperties *horizon);
Expand Down Expand Up @@ -149,12 +153,13 @@ double distance(double x1, double x2, double y1, double y2, double coslatsq)
int main(int argc, char *argv[])
{
double xcoord, ycoord;
enum OutputFormat format;

struct GModule *module;
struct {
struct Option *elevin, *dist, *coord, *direction, *horizon, *step,
*start, *end, *bufferzone, *e_buff, *w_buff, *n_buff, *s_buff,
*maxdistance, *output;
*maxdistance, *format, *output;
} parm;

struct {
Expand Down Expand Up @@ -288,6 +293,17 @@ int main(int argc, char *argv[])
parm.dist->description = _("Sampling distance step coefficient (0.5-1.5)");
parm.dist->guisection = _("Optional");

parm.format = G_define_option();
parm.format->key = "format";
parm.format->type = TYPE_STRING;
parm.format->required = YES;
parm.format->label = _("Output format used for point mode");
parm.format->options = "plain,json";
parm.format->descriptions = "plain;Plain text output;"
"json;JSON (JavaScript Object Notation);";
parm.format->answer = "plain";
parm.format->guisection = _("Point mode");

parm.output = G_define_standard_option(G_OPT_F_OUTPUT);
parm.output->key = "file";
parm.output->required = NO;
Expand Down Expand Up @@ -356,6 +372,10 @@ int main(int argc, char *argv[])
else {
G_debug(1, "Setting mode: SINGLE_POINT");
mode = SINGLE_POINT;
if (strcmp(parm.format->answer, "json") == 0)
format = JSON;
else
format = PLAIN;
if (sscanf(parm.coord->answer, "%lf,%lf", &xcoord, &ycoord) != 2) {
G_fatal_error(_(
"Can't read the coordinates from the \"coordinate\" option."));
Expand Down Expand Up @@ -556,7 +576,7 @@ int main(int argc, char *argv[])
INPUT(&geometry, elevin);
if (mode == SINGLE_POINT) {
/* Calculate the horizon for one single point */
calculate_point_mode(&settings, &geometry, xcoord, ycoord, fp);
calculate_point_mode(&settings, &geometry, xcoord, ycoord, fp, format);
}
else {
calculate_raster_mode(&settings, &geometry, &cellhd, &new_cellhd,
Expand Down Expand Up @@ -733,7 +753,8 @@ void com_par(const Geometry *geometry, OriginAngle *origin_angle, double angle,
}

void calculate_point_mode(const Settings *settings, const Geometry *geometry,
double xcoord, double ycoord, FILE *fp)
double xcoord, double ycoord, FILE *fp,
enum OutputFormat format)
{
/*
xg0 = xx0 = (double)xcoord * stepx;
Expand Down Expand Up @@ -774,8 +795,29 @@ void calculate_point_mode(const Settings *settings, const Geometry *geometry,
double printangle = settings->single_direction;

origin_point.maxlength = settings->fixedMaxLength;
fprintf(fp, "azimuth,horizon_height\n");

/* JSON variables and formating */
JSON_Value *root_value, *origin_value, *azimuths_value, *horizons_value;
JSON_Array *coordinates, *azimuths, *horizons;
JSON_Object *origin;
json_set_float_serialization_format("%lf");

switch (format) {
case PLAIN:
fprintf(fp, "azimuth,horizon_height\n");
break;
case JSON:
root_value = json_value_init_array();
coordinates = json_value_get_array(root_value);
origin_value = json_value_init_object();
origin = json_value_get_object(origin_value);
json_object_set_number(origin, "x", xcoord);
json_object_set_number(origin, "y", ycoord);
azimuths_value = json_value_init_array();
azimuths = json_value_get_array(azimuths_value);
horizons_value = json_value_init_array();
horizons = json_value_get_array(horizons_value);
break;
}
for (int i = 0; i < printCount; i++) {
OriginAngle origin_angle;
com_par(geometry, &origin_angle, angle, xp, yp);
Expand All @@ -793,10 +835,26 @@ void calculate_point_mode(const Settings *settings, const Geometry *geometry,
tmpangle = 360. - printangle + 90.;
if (tmpangle >= 360.)
tmpangle = tmpangle - 360.;
fprintf(fp, "%lf,%lf\n", tmpangle, shadow_angle);
switch (format) {
case PLAIN:
fprintf(fp, "%lf,%lf\n", tmpangle, shadow_angle);
break;
case JSON:
json_array_append_number(azimuths, tmpangle);
json_array_append_number(horizons, shadow_angle);
break;
}
}
else {
fprintf(fp, "%lf,%lf\n", printangle, shadow_angle);
switch (format) {
case PLAIN:
fprintf(fp, "%lf,%lf\n", printangle, shadow_angle);
break;
case JSON:
json_array_append_number(azimuths, printangle);
json_array_append_number(horizons, shadow_angle);
break;
}
}

angle += dfr_rad;
Expand All @@ -812,6 +870,16 @@ void calculate_point_mode(const Settings *settings, const Geometry *geometry,
else if (printangle > 360.)
printangle -= 360;
} /* end of for loop over angles */

if (format == JSON) {
json_object_set_value(origin, "azimuth", azimuths_value);
json_object_set_value(origin, "horizon_height", horizons_value);
json_array_append_value(coordinates, origin_value);
char *json_string = json_serialize_to_string_pretty(root_value);
fprintf(fp, "%s\n", json_string);
json_free_serialized_string(json_string);
json_value_free(root_value);
}
fclose(fp);
}

Expand Down
28 changes: 28 additions & 0 deletions raster/r.horizon/testsuite/test_r_horizon.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
License (>=v2). Read the file COPYING that comes with GRASS
for details.
"""
import json

from grass.gunittest.case import TestCase
from grass.gunittest.main import test
Expand Down Expand Up @@ -154,6 +155,33 @@ def test_point_mode_multiple_direction(self):
stdout = module.outputs.stdout
self.assertMultiLineEqual(first=ref2, second=stdout)

def test_point_mode_multiple_direction_json(self):
"""Test mode with 1 point and multiple directions with JSON"""
module = SimpleModule(
"r.horizon",
elevation="elevation",
coordinates=(634720, 216180),
output=self.horizon,
direction=180,
step=20,
format="json",
)
self.assertModule(module)
stdout = json.loads(module.outputs.stdout)
azimuths = []
horizons = []
reference = {}
for line in ref2.splitlines()[1:]:
azimuth, horizon = line.split(",")
azimuths.append(float(azimuth))
horizons.append(float(horizon))
reference["x"] = 634720.0
reference["y"] = 216180.0
reference["azimuth"] = azimuths
reference["horizon_height"] = horizons

self.assertListEqual([reference], stdout)

def test_point_mode_multiple_direction_artificial(self):
"""Test mode with 1 point and multiple directions with artificial surface"""
module = SimpleModule(
Expand Down
Loading