Skip to content

v0.28.0

Choose a tag to compare

@sipemu sipemu released this 24 Oct 09:33
6a01527

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_hex pre-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 (its layer_aes, merged with the plot-level one when the geom
    inherits aes), skipping a layer that opted out with show_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 low alpha to see through), stacked (position_stack, reads the
    new ymin GeomHistogram draws) or dodged (position_dodge2, which
    already narrowed xmin/xmax on a continuous axis — its width must be
    set to the bar's own width, e.g. the binwidth, the same way a caller
    would set it for any other continuous-axis dodge). StatBin is now
    panelwise (ggplot2's own stat_bin behaviour): 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::draw resolves each bar's fill through the fill scale per
    row, the same fill_col/map_color idiom GeomBar already uses;
    GeomFreqpoly::draw now splits its rows by group and draws one line per
    group, the same row_groups idiom GeomQQLine uses — previously it
    "worked" for a grouped aes(color=) only by accident, through the
    per-group stat calls panelwise just replaced, and would have drawn one
    zigzag line through every group's points in a single colour once StatBin
    started receiving every group's rows together). A fill/colour legend
    trains the same way any other aes(fill=)/aes(color=) mapping does — no
    geom-specific change needed.
    (#197, needed by DataZooDE/anofox-visualization#423;
    GeomHistogram::draw previously only ever used one literal fill colour,
    never reading a per-row aesthetic the way GeomDensity already did.)

Changed

  • Visible for existing grouped histograms: geom_histogram/geom_freqpoly
    with aes(fill=)/aes(color=)/aes(group=) already ran StatBin once 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). StatBin is now panelwise: 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 (any aes(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. density is 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"
    idiom StatAlign/StatQQDist already use, and needed so fill/color
    survive onto the per-group bins above. A pre-stat column that happens to
    share a name with one of StatBin'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() (feature sf):
    the pole of inaccessibility of a polygon (Mapbox's polylabel) as a public,
    data-space function — works on lon/lat, projected metres or screen pixels,
    whatever plane the caller's rings are in, unlike geom_sf_text's own
    (private, screen-space-only) anchor placement. Geometry::label_point()
    wraps it per geometry type (a point is itself, a MultiPolygon/collection
    the pole of its largest part by area). geom_sf_text/geom_sf_label's
    SfLabelAnchor::Polylabel now 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
    ::SIZE symbols on a polygon row) call the same algorithm geom_sf_text
    uses, on raw data-space geometry, before any screen projection.
  • GeomSf gains a size aesthetic on Point/MultiPoint rows
    (aes(size = "pop"), feature sf) — graduated symbols, typically paired
    with scale_size_area() so circle area is proportional to the value
    (the same fallback chain geom_point's draw_points uses: a trained
    scale wins, else the raw numeric value, else the unmapped point_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 (feature sf): draw one line as consecutive
    runs, each optionally an http(s) hyperlink. Default: concatenates the
    runs and draws them plain via draw_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> so text-anchor still 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.