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.profile: add JSON support #3872

Merged
merged 2 commits into from
Jul 2, 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.profile/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ MODULE_TOPDIR = ../..

PGM = r.profile

LIBES = $(RASTERLIB) $(GISLIB) $(MATHLIB)
LIBES = $(RASTERLIB) $(GISLIB) $(MATHLIB) $(PARSONLIB)
DEPENDENCIES = $(RASTERDEP) $(GISDEP)

include $(MODULE_TOPDIR)/include/Make/Module.make
Expand Down
10 changes: 7 additions & 3 deletions raster/r.profile/local_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <grass/parson.h>
#include <grass/gis.h>
#include <grass/raster.h>

enum OutputFormat { PLAIN, JSON };

/* main.c */
int do_profile(double, double, double, double, int, double, int, int, FILE *,
char *, const char *, double);
char *, const char *, double, enum OutputFormat, char *,
JSON_Array *);

/* read_rast.c */
int read_rast(double, double, double, int, int, RASTER_MAP_TYPE, FILE *,
char *);
int read_rast(double, double, double, int, int, RASTER_MAP_TYPE, FILE *, char *,
enum OutputFormat, char *, JSON_Array *);

/* input.c */
int input(char *, char *, char *, char *, char *, FILE *);
Expand Down
72 changes: 51 additions & 21 deletions raster/r.profile/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

#include <stdlib.h>
#include <grass/parson.h>
#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/glocale.h>
Expand Down Expand Up @@ -39,10 +40,13 @@ int main(int argc, char *argv[])
struct Cell_head window;
struct {
struct Option *opt1, *profile, *res, *output, *null_str, *coord_file,
*units;
*units, *format;
struct Flag *g, *c, *m;
} parm;
struct GModule *module;
enum OutputFormat format;
JSON_Value *array_value;
JSON_Array *array;

G_gisinit(argv[0]);

Expand Down Expand Up @@ -102,6 +106,9 @@ int main(int argc, char *argv[])
_("If units are not specified, current project units are used. "
"Meters are used by default in geographic (latlon) projects.");

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

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

Expand Down Expand Up @@ -147,6 +154,15 @@ int main(int argc, char *argv[])
res = (window.ew_res + window.ns_res) / 2;
}

if (strcmp(parm.format->answer, "json") == 0) {
format = JSON;
array_value = json_value_init_array();
array = json_array(array_value);
}
else {
format = PLAIN;
}

G_message(_("Using resolution: %g [%s]"), res / factor, unit);

G_begin_distance_calculations();
Expand Down Expand Up @@ -177,17 +193,19 @@ int main(int argc, char *argv[])
data_type = Rast_get_map_type(fd);
/* Done with file */

/* Show message giving output format */
G_message(_("Output columns:"));
if (coords == 1)
sprintf(formatbuff,
_("Easting, Northing, Along track dist. [%s], Elevation"),
unit);
else
sprintf(formatbuff, _("Along track dist. [%s], Elevation"), unit);
if (clr)
strcat(formatbuff, _(" RGB color"));
G_message("%s", formatbuff);
if (format == PLAIN) {
/* Show message giving output format */
G_message(_("Output columns:"));
if (coords == 1)
sprintf(formatbuff,
_("Easting, Northing, Along track dist. [%s], Elevation"),
unit);
else
sprintf(formatbuff, _("Along track dist. [%s], Elevation"), unit);
if (clr)
strcat(formatbuff, _(" RGB color"));
G_message("%s", formatbuff);
}

/* Get Profile Start Coords */
if (parm.coord_file->answer) {
Expand All @@ -207,7 +225,7 @@ int main(int argc, char *argv[])

if (havefirst)
do_profile(e1, e2, n1, n2, coords, res, fd, data_type, fp,
null_string, unit, factor);
null_string, unit, factor, format, name, array);
e1 = e2;
n1 = n2;
havefirst = TRUE;
Expand All @@ -232,7 +250,7 @@ int main(int argc, char *argv[])

/* Get profile info */
do_profile(e1, e2, n1, n2, coords, res, fd, data_type, fp,
null_string, unit, factor);
null_string, unit, factor, format, name, array);
}
else {
for (i = 0; i <= k - 2; i += 2) {
Expand All @@ -246,11 +264,21 @@ int main(int argc, char *argv[])

/* Get profile info */
do_profile(e1, e2, n1, n2, coords, res, fd, data_type, fp,
null_string, unit, factor);
null_string, unit, factor, format, name, array);
}
}
}

if (format == JSON) {
char *serialized_string = json_serialize_to_string_pretty(array_value);
if (serialized_string == NULL) {
G_fatal_error(_("Failed to initialize pretty JSON string."));
}
puts(serialized_string);
json_free_serialized_string(serialized_string);
json_value_free(array_value);
}

Rast_close(fd);
fclose(fp);

Expand All @@ -264,7 +292,8 @@ int main(int argc, char *argv[])
/* Establish parameters */
int do_profile(double e1, double e2, double n1, double n2, int coords,
double res, int fd, int data_type, FILE *fp, char *null_string,
const char *unit, double factor)
const char *unit, double factor, enum OutputFormat format,
char *name, JSON_Array *array)
{
double rows, cols, LEN;
double Y, X, k;
Expand All @@ -284,7 +313,8 @@ int do_profile(double e1, double e2, double n1, double n2, int coords,
/* Special case for no movement */
e = e1;
n = n1;
read_rast(e, n, dist / factor, fd, coords, data_type, fp, null_string);
read_rast(e, n, dist / factor, fd, coords, data_type, fp, null_string,
format, name, array);
}

k = res / hypot(rows, cols);
Expand All @@ -303,7 +333,7 @@ int do_profile(double e1, double e2, double n1, double n2, int coords,
/* SE Quad or due east */
for (e = e1, n = n1; e < e2 || n > n2; e += X, n -= Y) {
read_rast(e, n, dist / factor, fd, coords, data_type, fp,
null_string);
null_string, format, name, array);
/* d+=res; */
dist += G_distance(e - X, n + Y, e, n);
}
Expand All @@ -313,7 +343,7 @@ int do_profile(double e1, double e2, double n1, double n2, int coords,
/* NE Quad or due north */
for (e = e1, n = n1; e < e2 || n < n2; e += X, n += Y) {
read_rast(e, n, dist / factor, fd, coords, data_type, fp,
null_string);
null_string, format, name, array);
/* d+=res; */
dist += G_distance(e - X, n - Y, e, n);
}
Expand All @@ -323,7 +353,7 @@ int do_profile(double e1, double e2, double n1, double n2, int coords,
/* SW Quad or due south */
for (e = e1, n = n1; e > e2 || n > n2; e -= X, n -= Y) {
read_rast(e, n, dist / factor, fd, coords, data_type, fp,
null_string);
null_string, format, name, array);
/* d+=res; */
dist += G_distance(e + X, n + Y, e, n);
}
Expand All @@ -333,7 +363,7 @@ int do_profile(double e1, double e2, double n1, double n2, int coords,
/* NW Quad or due west */
for (e = e1, n = n1; e > e2 || n < n2; e -= X, n += Y) {
read_rast(e, n, dist / factor, fd, coords, data_type, fp,
null_string);
null_string, format, name, array);
/* d+=res; */
dist += G_distance(e + X, n - Y, e, n);
}
Expand Down
103 changes: 103 additions & 0 deletions raster/r.profile/r.profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ <h2>OUTPUT FORMAT</h2>
If the units are not specified, current coordinate reference system's units will be used.
In case of geographic CRS (latitude/longitude), meters are used as default unit.

Finally, the output from <em>r.info</em> can be output in JSON by passing the <b>format=json</b> option.

<h2>NOTES</h2>

The profile resolution is measured exactly from the supplied end or
Expand Down Expand Up @@ -151,6 +153,107 @@ <h3>Extraction of values along profile defined by coordinates (variant 2)</h3>
4054.027749 73.988029
</pre></div>

<h3>JSON Output</h3>
<div class="code"><pre>
r.profile -g input=elevation coordinates=641712,226095,641546,224138,641546,222048,641049,221186 -c format=json resolution=1000
</pre></div>

The output looks as follows:

<div class="code"><pre>
[
{
"easting": 641712,
"northing": 226095,
"distance": 0,
"elevation": 84.661506652832031,
"red": 113,
"green": 255,
"blue": 0
},
{
"easting": 641627.47980925441,
"northing": 225098.57823319823,
"distance": 1000.0000000000125,
"elevation": 98.179061889648438,
"red": 255,
"green": 241,
"blue": 0
},
{
"easting": 641546,
"northing": 224138,
"distance": 1964.0277492948007,
"elevation": 83.638137817382812,
"red": 100,
"green": 255,
"blue": 0
},
{
"easting": 641546,
"northing": 223138,
"distance": 2964.0277492948007,
"elevation": 89.141029357910156,
"red": 169,
"green": 255,
"blue": 0
},
{
"easting": 641546,
"northing": 222138,
"distance": 3964.0277492948007,
"elevation": 78.497756958007812,
"red": 35,
"green": 255,
"blue": 0
},
{
"easting": 641546,
"northing": 222048,
"distance": 4054.0277492948007,
"elevation": 73.988029479980469,
"red": 0,
"green": 249,
"blue": 17
}
]
</pre></div>

kritibirda26 marked this conversation as resolved.
Show resolved Hide resolved
<h3>Using JSON output with Python for plotting data</h3>

The JSON output makes for ease of integration with popular python data science libraries. For instance, here
is an example of creating a scatterplot of distance vs elevation with color coding.

<div class="code"><pre>
import grass.script as gs
import pandas as pd
import matplotlib.pyplot as plt

# Run r.profile command
elevation = gs.read_command(
"r.profile",
input="elevation",
coordinates="641712,226095,641546,224138,641546,222048,641049,221186",
format="json",
flags="gc"
)

# Load the JSON data into a dataframe
df = pd.read_json(elevation)

# Convert the RGB color values to hex format for matplotlib
df["color"] = df.apply(lambda x: "#{:02x}{:02x}{:02x}".format(int(x["red"]), int(x["green"]), int(x["blue"])), axis=1)

# Create the scatter plot
plt.figure(figsize=(10, 6))
plt.scatter(df['distance'], df['elevation'], c=df['color'], marker='o')
plt.title('Profile of Distance vs. Elevation with Color Coding')
plt.xlabel('Distance (meters)')
plt.ylabel('Elevation')
plt.grid(True)
plt.show()
</pre></div>

<h2>SEE ALSO</h2>

<em>
Expand Down
Loading
Loading