Skip to content

Commit

Permalink
v.boxplot: add layout/plot options
Browse files Browse the repository at this point in the history
- Add option
  - to set limits of value axis
  - add grid lines
- Reorganize tabs
- Lazy loading matplotlib
- Fix: selected colors checked and changed to matplotlib format
  • Loading branch information
ecodiv committed Dec 23, 2024
1 parent 08eac68 commit cda7076
Showing 1 changed file with 96 additions and 28 deletions.
124 changes: 96 additions & 28 deletions src/vector/v.boxplot/v.boxplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
# %end

# %option G_OPT_V_FIELD
# % guisection: Input
# %end

# %option G_OPT_DB_COLUMN
Expand All @@ -35,6 +36,13 @@
# % guisection: Input
# %end

# %option G_OPT_DB_COLUMN
# % key: group_by
# % description: Attribute column with categories to group the data by
# % required: no
# % guisection: Input
# %end

# %option G_OPT_DB_WHERE
# %guisection: Input
# %end
Expand All @@ -60,6 +68,7 @@
# % label: DPI
# % description: resolution of plot
# % required: no
# % answer: 100
# % guisection: Output
# %end

Expand All @@ -73,13 +82,6 @@
# % required: no
# %end

# %option G_OPT_DB_COLUMN
# % key: group_by
# % description: Attribute column with categories to group the data by
# % required: no
# % guisection: Plot format
# %end

# %option
# % key: order
# % type: string
Expand Down Expand Up @@ -118,6 +120,22 @@
# % guisection: Plot format
# %end

# %flag
# % key: g
# % label: Add grid lines
# % description: Add grid lines
# % guisection: Plot format
# %end

# %option
# % key: axis_limits
# % type: string
# % label: Limit value axis [min,max]
# % description: min and max value of y-axis, or x-axis if -h flag is set)
# % guisection: Plot format
# % required: no
# %end

# %option G_OPT_CN
# % key: bx_color
# % label: Color of the boxplots
Expand Down Expand Up @@ -205,55 +223,86 @@
# %end

import sys
import grass.script as gscript
import grass.script as gs
import operator
import numpy as np


def lazy_import_py_modules():
"""Lazy import Py modules"""
global matplotlib
global plt

# lazy import matplotlib
try:
import matplotlib

matplotlib.use("WXAgg")
from matplotlib import pyplot as plt
except ModuleNotFoundError:
gs.fatal(_("Matplotlib is not installed. Please, install it."))


def get_valid_color(color):
"""Get valid Matplotlib color
:param str color: input color
:return str|list: color e.g. blue|[0.0, 0.0, 1.0]
"""
if ":" in color:
color = [int(x) / 255 for x in color.split(":")]
if not matplotlib.colors.is_color_like(color):
gs.fatal(_("{} is not a valid color.".format(color)))
return color


def main():
import matplotlib # required by windows

matplotlib.use("wxAGG") # required by windows
import matplotlib.pyplot as plt
# lazy import matplotlib
lazy_import_py_modules()

# input
vector = options["map"]
column = options["column"]
if options["dpi"]:
dpi = float(options["dpi"])
else:
dpi = 300
dpi = float(options["dpi"])
grid = flags["g"]
if options["plot_dimensions"]:
dimensions = [float(x) for x in options["plot_dimensions"].split(",")]
else:
if flags["h"]:
dimensions = [6, 4]
else:
dimensions = [6, 4]
dimensions = [4, 6]
blcolor = get_valid_color(options["bx_blcolor"])
bxcolor = get_valid_color(options["bx_color"])
boxprops = {
"color": options["bx_blcolor"],
"facecolor": options["bx_color"],
"color": blcolor,
"facecolor": bxcolor,
"linewidth": float(options["bx_lw"]),
}
median_color = get_valid_color(options["median_color"])
medianprops = {
"color": options["median_color"],
"color": median_color,
"linewidth": float(options["median_lw"]),
}
whiskerprops = {
"linewidth": float(options["bx_lw"]),
"color": options["bx_blcolor"],
"color": blcolor,
}
capprops = {
"linewidth": float(options["bx_lw"]),
"color": options["bx_blcolor"],
"color": blcolor,
}
flier_color = get_valid_color(options["flier_color"])
flierprops = {
"marker": options["flier_marker"],
"markersize": float(options["flier_size"]),
"markerfacecolor": (options["flier_color"]),
"markerfacecolor": flier_color,
"markeredgecolor": flier_color,
"markeredgewidth": float(options["bx_lw"]),
}
bxp_width = float(options["bx_width"])

group_by = options["group_by"] if options["group_by"] else None
output = options["output"] if options["output"] else None
where = (
Expand All @@ -278,15 +327,15 @@ def main():
if where:
df = [
x
for x in gscript.read_command(
for x in gs.read_command(
"v.db.select", map_=vector, column=cols, where=where, flags="c"
).splitlines()
]
# Get all column data
else:
df = [
x
for x in gscript.read_command(
for x in gs.read_command(
"v.db.select", map_=vector, column=cols, flags="c"
).splitlines()
]
Expand All @@ -295,6 +344,12 @@ def main():
if bool(options["fontsize"]):
plt.rcParams["font.size"] = int(options["fontsize"])

# Closing message
if not options["output"]:
gs.message(
_("\n> Note, you need to close the figure to finish the script \n\n")
)

# Set plot dimensions and DPI
fig, ax = plt.subplots(figsize=dimensions, dpi=dpi)

Expand Down Expand Up @@ -323,7 +378,6 @@ def main():
ax.boxplot(
data,
notch=flag_n,
sym="gD",
labels=uid,
vert=flag_h,
showfliers=flag_o,
Expand All @@ -341,7 +395,6 @@ def main():
ax.boxplot(
data,
notch=flag_n,
sym="gD",
vert=flag_h,
showfliers=flag_o,
boxprops=boxprops,
Expand All @@ -355,12 +408,27 @@ def main():
if flag_r:
plt.xticks(rotation=90)
plt.tight_layout()

# Set limits value axis
if bool(options["axis_limits"]):
minlim, maxlim = map(float, options["axis_limits"].split(","))
if bool(flag_h):
plt.ylim([minlim, maxlim])
else:
plt.xlim([minlim, maxlim])

# Set grid (optional)
if flag_h:
ax.yaxis.grid(bool(grid))
else:
ax.xaxis.grid(bool(grid))

if output:
plt.savefig(output)
else:
plt.show()


if __name__ == "__main__":
options, flags = gscript.parser()
options, flags = gs.parser()
main()

0 comments on commit cda7076

Please sign in to comment.