Repository navigation
v0.28.0
Follow-ups from DataZooDE/anofox-visualization's #318 ports: a size aesthetic
on geom_sf points (#194), a public data-space spatial::polylabel (#195),
linked attribution text (#196), per-group fill/colour on geom_histogram /
geom_freqpoly with bins shared across groups (#197), and a reserved legend
margin for a layer-only fill/colour mapping such as geom_hex (#201).
No API breaks (cargo-semver-checks against v0.27.0). Visible change:
grouped histograms now share one set of bins across groups (ggplot2's
behaviour) instead of binning each group on its own range.
Fixed
- A
Fill/Color/… legend mapped only on a layer, not the plot, got no
panel margin and ran off the canvas (#201, upstream issue
DataZooDE/anofox-visualization#439).GGPlot::has_legend_mapping()(the
early, size-independent check that decides whether to reserve space for
a legend at all) only inspected the plot-level.aes(...)mapping — never
a layer's own.layer_aes(...)override, which is exactly how a stat-fed
fill (geom_hexpre-binned by the caller and mapped with
StatIdentity+.layer_aes(...).fill(...)) is normally wired up. The
legend still drew, with correct colours (the render path draws whatever
guides the trained scales produce, regardless of this flag), just with no
margin ever reserved for it: on a narrow panel that read as the legend
overlapping the plot, more generally as a colourbar clipped off past the
canvas edge — reproduced with one layer alone, and unaffected by whether
a second layer (geom_abline, mapping nothing new) was also present.
has_legend_mapping()now also checks every layer's own effective
mapping (itslayer_aes, merged with the plot-level one when the geom
inherits aes), skipping a layer that opted out withshow_legend(false).
Added
geom_histogram/geom_freqpoly: per-group fill/colour (aes(fill = group)
/aes(color = group)), overlaid (position_identity, the default — pair
with a lowalphato see through), stacked (position_stack, reads the
newyminGeomHistogramdraws) or dodged (position_dodge2, which
already narrowedxmin/xmaxon a continuous axis — itswidthmust be
set to the bar's own width, e.g. thebinwidth, the same way a caller
would set it for any other continuous-axis dodge).StatBinis now
panelwise(ggplot2's ownstat_binbehaviour): bins are computed once
over every group's pooled x range — not each group its own — so
overlaid/stacked/dodged bars from different groups land on the same grid
(GeomHistogram::drawresolves each bar's fill through the fill scale per
row, the samefill_col/map_coloridiomGeomBaralready uses;
GeomFreqpoly::drawnow splits its rows by group and draws one line per
group, the samerow_groupsidiomGeomQQLineuses — previously it
"worked" for a groupedaes(color=)only by accident, through the
per-group stat callspanelwisejust replaced, and would have drawn one
zigzag line through every group's points in a single colour onceStatBin
started receiving every group's rows together). A fill/colour legend
trains the same way any otheraes(fill=)/aes(color=)mapping does — no
geom-specific change needed.
(#197, needed by DataZooDE/anofox-visualization#423;
GeomHistogram::drawpreviously only ever used one literal fill colour,
never reading a per-row aesthetic the wayGeomDensityalready did.)
Changed
- Visible for existing grouped histograms:
geom_histogram/geom_freqpoly
withaes(fill=)/aes(color=)/aes(group=)already ranStatBinonce per
group, each computing its own bin edges from only that group's x range — so
two groups' bars could land on different grids (different widths, offset
edges).StatBinis nowpanelwise: bins are computed once over every
group's pooled x range and shared, matching ggplot2. A grouped
histogram/freqpoly already in use may shift bar positions/widths and bin
count to match. StatBin's per-group output (anyaes(fill=)/aes(color=)/aes(group=)
mapping) now includes a row for every bin of every group, even one a
group has no values in (count = 0), so a sparse group's bars/line don't
silently stop partway through the x range instead of continuing at zero —
matching ggplot2.densityis normalised within each group's own total
(not the pooled grand total), also ggplot2's default.StatBin's output now carries forward any other column present on its
input (previously dropped) — the same "carry the group's first row"
idiomStatAlign/StatQQDistalready use, and needed sofill/color
survive onto the per-group bins above. A pre-stat column that happens to
share a name with one ofStatBin's own reserved output columns (x,
y,count,density,xmin,xmax— e.g. a data column literally
named"y", mapped to the x aesthetic) is never carried, so it can't
silently overwrite the computed bin values.
Spatial
ggplot_rs::spatial::polylabel()/polylabel_with_distance()(featuresf):
the pole of inaccessibility of a polygon (Mapbox'spolylabel) as a public,
data-space function — works on lon/lat, projected metres or screen pixels,
whatever plane the caller's rings are in, unlikegeom_sf_text's own
(private, screen-space-only) anchor placement.Geometry::label_point()
wraps it per geometry type (a point is itself, aMultiPolygon/collection
the pole of its largest part by area).geom_sf_text/geom_sf_label's
SfLabelAnchor::Polylabelnow delegates to this instead of carrying its
own copy of the algorithm. DataZooDE/anofox-visualization#420, #195 — lets
a consumer placing symbols/labels at a region's visual centre (graduated
::SIZEsymbols on a polygon row) call the same algorithmgeom_sf_text
uses, on raw data-space geometry, before any screen projection.GeomSfgains asizeaesthetic onPoint/MultiPointrows
(aes(size = "pop"), featuresf) — graduated symbols, typically paired
withscale_size_area()so circle area is proportional to the value
(the same fallback chaingeom_point'sdraw_pointsuses: a trained
scale wins, else the raw numeric value, else the unmappedpoint_size
default). Rows now draw largest-size-first regardless of their order in
the data (stable for equal/missing sizes), so a big circle never hides a
smaller one at the same spot — the ggplot2/ggspatial graduated-symbol-map
convention. DataZooDE/anofox-visualization#420, #194.DrawBackend::draw_text_runs(featuresf): draw one line as consecutive
runs, each optionally anhttp(s)hyperlink. Default: concatenates the
runs and draws them plain viadraw_text(pixel/canvas,plotters
backends); the native SVG backend overrides it to wrap each linked run
in<a href="…" target="_blank" rel="noopener">with an underline,
inside one<text>sotext-anchorstill aligns the whole line.GeomAttribution::link(url)/.runs(segments)(annotation_attribution):
make the whole attribution text one hyperlink, or replace it with
several independently plain-or-linked segments (ggspatial-style — "©
BKG" linked, a licence code linked, plain connective text between
them). A non-http(s)URL (javascript:,data:, a bare path, …) is
dropped at render time with a warning rather than drawn as a link.
DataZooDE/anofox-visualization#420, #196 — the BKG/VG2500 source note
(© BKG (year) dl-de/by-2-0, Datenquellen: …, each term separately
linked, as the licence's attribution terms require) can retire its
hand-written<a>-wrapped<text>furniture onto this.