geoscales 0.6.0
Breaking: crosswalk functions split by task
-
geoscale_map(gs, from, to)now takes the Geoscale first and maps between two of its geoframes only; the map between two Geoscales isgeoscale_map_between(from, to). The registry follows the same split:register_geoscale_map(gs, from, to, map)/get_geoscale_map(gs, from, to)for geoframes of one object,register_geoscale_map_between(from, to, map)/get_geoscale_map_between(from, to)for two objects.list_geoscale_maps()andclear_geoscale_maps()are unchanged. No deprecation aliases. - The shared core package is now called nestedscales (formerly multiscales).
data-raw/iceland_wind.rds(used by the README and the “Get started” vignette) was re-stamped withdata-raw/restamp_iceland_wind.R.
geoscales 0.5.4
The shared core
Geoscaleis now a subclass of thenestedscalesNestedScale. Every nestedscales verb accepts a Geoscale directly, and geometry follows any subset.@geoframesand the constructor are unchanged.-
Breaking: Geoscale objects saved with an earlier version must be rebuilt. An S7 object stores its class with it, so an old Geoscale is not a
NestedScale: simple accessors still answer, butrecast_geoscale(),filter_geoscale(),join_geoscale()and the other verbs fail with “xmust be a NestedScale object”. Rebuild it from its own stored parts:m <- attr(old, "meta") gs <- geoscale_from_leaftable(attr(old, "leaftable"), geoframes = attr(old, "geoframes"), weights = m$weights, name = m$name) if (!is.null(attr(old, "geometry"))) { gs <- attach_geometry_geoscale(gs, attr(old, "geometry")) } recast_geoscale(),join_geoscale(),geoscale_map(), the navigation verbs,filter_geoscale(),geoscale_share(),geoscale_coverage()and the rule and map registries now run onnestedscales. Results are unchanged. On lazy inputs (arrow, dtplyr)recast_geoscale()no longer scans the data for its warnings, and it accepts theregionkey asfrom.timescalesmoves fromImportstoSuggests. It was a hard dependency for exactly one symbol – therecast()generic – which is now owned bynestedscales. Installing geoscales no longer pulls in a calendar package andlubridate. timescales stays inSuggestsbecause the mirror-parity test compares the two packages’ export surfaces.nestedscalesis added toImportsand therecast()generic is re-exported from there.geoscales::recastresolves and dispatches onGeoscaleexactly as before; the source frame is stillfrom_geoframe, sincefromis the dispatch argument and names the scale.New rule
"share": share within parent. Each source region’s value over its parent-group total, keyed at the source geoframe –recast_geoscale(x, gs, from = "nuts3", to = "nuts0", rule = "share")gives each nuts3’s share of its country, summing to 1 per parent. The parent isto, or the newparent=argument (default: the geoframe abovefrom);frommust nest within it."logshare"is the same computation with a log-scale display intent. Mirrored in timescales.The stack and icicle data fills accept
rule = "share": each plane is coloured by its regions’ share within the nearest coarser plane it nests in (the coarsest by the grand total), legend “Share”."share"draws on a fixed linear 0..1 scale,"logshare"(the same computation) on a fixed log10 percent scale (0.01%..100%) for when sibling counts differ by orders of magnitude; fixed limits either way, so any two share figures are colour-comparable.palette = NULLplus your own scale overrides it.The default viridis palette option is
"H"(was"G"), and the icicle’s data fill now honourspalette=instead of hard-coding one.recast_pairs()aggregates data keyed by a pair of regions – transmission corridors, trade flows, commuting matrices. Both endpoints are mapped, pairs that land inside a single target region are dropped as internal, and the rest are aggregated by rule.directed = FALSEmergesA->BwithB->A.recast_from_geoatoms(weight =)now names a column ofx, so the weight can vary by identifier – a capacity-weighted efficiency differs by year and vintage. Previously only a column literally namedweightwas honoured.na_rm = TRUEinrecast_from_geoatoms()andrecast_pairs()reads anNAas “this member says nothing” instead of letting it make the whole groupNA. Only an all-NAgroup staysNA, and a weighted mean drops the weight of eachNAmember so the divisor still matches.ruleandweightaccept a named vector, one entry per value column. One slot of a model can hold an extensive and an intensive quantity side by side, which previously took two calls.summary()output now prints with its formatted view from user code: the localprintbinding S7 leaves in the namespace had captured theS3method()registration ofprint.summary_Geoscale, so the method was invisible outside the package. Registered against base’sprintin.onLoad(same fix in timescales).Icicle labels no longer overprint:
geoscale_autoplot()now draws labels widest-rectangle-first withcheck_overlap = TRUE(matching the timescales icicle), so narrow regions lose their label instead of smearing the band.Remotes:added to DESCRIPTION so CI andpakusers can resolve the GitHub-only timescales Import and energypal Suggests from GitHub (the packages are not on CRAN/r-universe yet). Drop the field at CRAN submission time.
Base-generic methods on Geoscale
-
summary()— the quantitative complement ofprint(): member and unassigned-atom counts, per-weight totals, sampled coverage (with the parent’s name), the adjacent-geoframe NESTING table (cross-cutting pairs flagged with offender counts), geometry status (sf-free). Returns a"summary_Geoscale"with its own print. -
names()— the geoframe names (identical togeoscale_geoframes()). -
as.data.frame()andggplot2::fortify()— the leaftable, soggplot(gs) + geom_*()works directly. All mirrored by the timescales twins.
geoscales 0.5.0
Hard-break release: the sibling APIs of timescales and geoscales were harmonized against each other (the pairing table and naming rules live in the stack-wide CONVENTIONS.md, “Sibling API mirror”). NO deprecation aliases are kept – old names are gone, not wrapped.
Breaking changes
- Registries follow
register_<class>_<thing>everywhere: thegeo_map/geo_rule/geo_providerregistry families are nowregister_geoscale_map/get_geoscale_map/list_geoscale_maps/clear_geoscale_maps,register_geoscale_rule/get_geoscale_rule/list_geoscale_rules/clear_geoscale_rules, andregister_geoscale_provider/get_geoscale_provider/list_geoscale_providers.GEO_RULESis nowGEOSCALE_RULES. - The deprecated shim family is REMOVED (29
geo_*aliases plusgeoscale_levels,is_valid_level,geoscale_from_leaves,CORE_LEVELS; archived underdrafts/). -
geoscale_regions(x, geoframe = NULL)–geoframenow defaults to the finest geoframe (the atoms). The required-geoframe rule is about arguments that take region CODES; this one only selects the output.
New
-
coords_to_region(x, gs, geoframe = NULL)– the spatial twin oftimescales::datetime_to_timeslice(): match point observations (sf/sfc or a data.frame withlon/latcolumns) to the regions whose geometry contains them;NAoutside every region. -
geoscale_leaftable(x)– exported accessor for the leaf table (stop reaching forx@leaftable).
geoscales 0.4.2
Breaking changes
- A filtered Geoscale is now book-kept as a SAMPLE (the spatial mirror of a partial calendar’s
year_fraction):filter_geoscale()writesmeta$coverage(per-weight kept fraction of the ROOT parent, so filters compose),meta$parent_totals(making coverage verifiable by the validator),meta$parent_name, and manglesmeta$nameto"parent[geoframe:codes]"– so two different samples of one parent can no longer poison each other’s crosswalk-registry entries or collide injoin_geoscale()column names.prune_geoscale()renames to"parent@geoframe"(theprune_calendar()convention), now preserves the FULL meta (crs,source,labels), reflects dropped unassigned atoms in coverage, and keeps attached geometry by dissolving it per new atom (keep_geometry = FALSEto opt out). Read the fraction with the newgeoscale_coverage(x, weight). Code that relied on a subset keeping its parent’s name can find the parent inmeta$parent_name.
New features
-
geom_geoscale()andtheme_geoscale(): composable ggplot2 choropleth layer and map theme, mirroringtimescales::geom_calendar()’s design (column-name arguments, one standardgeom_sf()layer, solid white background for dark-mode pages).plot()on a Geoscale now dispatches togeoscale_autoplot(). -
get_geo_map()andlist_geo_maps()complete the crosswalk registry;register_geo_map()/clear_geo_maps()gain examples. -
geoscale_geometry(),geom_geoscale()and the stack view gain an opt-inprecision=for snapping near-coincident boundaries before the dissolve (workaround for sources with floating-point-jittered shared edges; default off). -
geoscale_autoplot(type = "stack")draws the layer-stack view: one map plane per geoframe, the same atoms dissolved at every resolution (requires attached geometry). Points of view come as presets (view = "oblique"/"top-down"/"cavalier"/"cabinet"/"military"/ "isometric"/"dimetric"/"trimetric"/"perspective") or viaangle/ratio(oblique) and rawshear/depth;rotate=turns the plane in place (point North anywhere),direction=flips the stack, and the defaultgapleaves planes almost touching with a slight overlap. - The stack view takes data:
data/zcolour every plane by an atom-keyed value, recast onto each coarser plane withrecast_geoscale()(rule, default"weighted_mean") so the whole stack shares one continuous scale;labels=writes region names on chosen planes andpalette=picks the viridis option — orNULLto bring your own scale, e.g. energypal’s Global Wind Atlas colours on their absolute breaks. The stack canvas now hugs the content: tight x/y limits, left room sized to the geoframe names, legend pulled in close. - The structure icicle carries data too:
geoscale_autoplot(data =, z =)fills every band with the value recast to that geoframe (atoms keep their values, coarser bands aggregate byrule–"weighted_mean"default), with contrast-aware labels – the 2D twin of the stack’s data fill. The README gained a five-point “What geoscales offers” intro mirrored with timescales. - Stack guides and borders:
frame=draws each plane’s outline (its “sheet”) through the same projection,frame_fill=fills the sheets (best mostly transparent – glass panes), andconnectors=adds dashed corner-to-corner guides between planes – curved shapes become easy to read in oblique views.colour=/linewidth=style the region borders, recycled per plane (defaults"grey35"/0.2, ggplot2’s own sf polygon border). Same additions intimescales::calendar_autoplot(type = "stack"). The README front page shows it on Iceland’s onshore wind resource, clustered from the Global Wind Atlas per region (data-raw/iceland_wind.R; build walk-through invignette("geoscales")).
Bug fixes
-
geoscale_geometry()no longer fails with an opaque “differing number of rows” whensf::st_union()returns several parts for one code (seen with s2-invalid source polygons): the parts are collapsed into the code’s single dissolved geometry. Healing the source withsf::st_make_valid()before attaching remains the better fix.
Documentation
- The documentation is restructured to mirror timescales: the intro is a short 5-minute tour; new
vignette("concepts")(with the shared *scales glossary),vignette("data-structures")andvignette("data-manipulation")(including a runnable time-and-spacerecast()chain and backend examples); the plotting article is rewritten on the current API as “Visualization with ggplot2” with a real-map tour of Iceland built from Natural Earth, with area data attached (old URL redirects). Vignette code follows the stack-wide tidyverse +|>style; README drops the retired word “levels”.
geoscales 0.2.0.9000
First working line: the Geoscale class, a conserving conversion core mirroring timescales, navigation, plotting, and the Natural Earth provider – under one naming convention shared with timescales.
Breaking changes
-
Geoscale@levelsis now@geoframesand@leavesis@leaftable(@membersunchanged), matching timescales;level/levelsarguments aregeoframe/geoframesacross the API, and derived tables useparent_geoframe/child_geoframecolumns. - A value column with neither
rule=nor aregister_geo_rule()entry is now an error; the silentsumfallback is gone. - Materialised
recast_geoscale()results complete to the full target vocabulary in member order (NAwhere nothing landed); lazy backends return observed groups until collected. -
join_geoscale()attaches a label column named after the Geoscale, with membership and share/weight columns"<name>."-prefixed (meta$nameis now required for conversion and attach;geoscale_build()derives a default). Existing columns are never overwritten (error). - Conflicting registered per-column weights build one crosswalk per weight instead of silently splitting equally.
- The
recast()method argumentfrom_levelis nowfrom_geoframe.
New features
Conversion
-
recast_geoscale(x, gs, from, to, rule)converts between geoframes – aggregation and disaggregation as one operation through the atom layer, cross-cutting geoframes included; identifier (panel) columns are preserved as groups;to =also accepts another named Geoscale (targeting its atom layer via shared atom keys). -
recast_to_geoatoms()/recast_from_geoatoms()expose the route halves; chaining them across two Geoscales converts between region systems that share atom keys. -
geoscale_map(from, to, gs =, weight =)materialises the crosswalk (atom counts and weights per overlapping pair, within one Geoscale or across two);register_geo_map()installs exact crosswalks,clear_geo_maps()resets. - All converters run over
data.frame, tibble,data.table, dtplyr, and arrow inputs; results come back in the input’s class, and lazy inputs return the uncollected query unlesscollect = TRUE. - Rules
sum,weighted_mean,mean,copy, and (new)sd, each defined in both directions; per-column defaults viaregister_geo_rule()/get_geo_rule()/list_geo_rules()/clear_geo_rules();na_action = c("drop", "error", "keep")for partial coverage. - The
recast()generic (owned by timescales, now in Imports) chains time and space:x |> recast(cal_a, cal_b) |> recast(gs, to = "country"); the source geoframe is inferred fromx’s columns or passed asfrom_geoframe=.
Core, navigation, providers
-
Geoscale: an S7 class holding@leaftable(one row per atom),@geoframes(ordered coarsest first),@members, optional@geometry, and@meta; geoframes are partitions of atoms, not a tree, so cross-cutting region systems are first-class. - Construction layers:
geoscale_from_leaftable(),geoscale_build()(ragged parent-child crosswalks),geoscale_from_provider(); plusgeoscale_example(). - Navigation and subsetting:
geoscale_geoframes(),geoscale_regions(),geoscale_rank(),geoscale_family(),geoscale_nests(),geoscale_ancestry()(atom-mediated, never a transitive closure),geoscale_children()/_parents()/_descendants()/_ancestors(),geoscale_share(),filter_geoscale()/gs[geoframe, region],prune_geoscale(). Ageoframeargument is required throughout – codes are not unique across geoframes. - Geometry and plotting (
sfand ggplot2 in Suggests):attach_geometry_geoscale(),geoscale_geometry()(dissolve),add_area_geoscale(),geoscale_autoplot()(icicle, no geometry needed),geoscale_plot()(choropleth),geoscale_layout(). - Providers:
register_geo_provider()/get_geo_provider()/list_geo_providers(); Natural Earth viane_source()/ne_geoscale()with source and scale recorded inmeta. The package ships no maps and selects no boundaries; known Natural Earth pitfalls (iso_a3 == -99, unreliable areas at 1:110m) are handled.
Deprecations
Old names warn and forward; removal before 1.0: the geo_* family -> verb_geoscale() / geoscale_*() / register_geo_*() (e.g. geo_recast() -> recast_geoscale(), geo_levels() -> geoscale_geoframes()), and the 2026-08 lattice renames geoscale_from_leaves() -> geoscale_from_leaftable(), geoscale_levels() -> geoscale_geoframes(), is_valid_level() -> is_valid_geoframe(), CORE_LEVELS -> CORE_GEOFRAMES.
Documentation
-
vignette("geoscales")gains crosswalk, route-halves, attach, backends, and glossary sections; version files (DESCRIPTION,VERSION, NEWS) agree on 0.2.0.9000.
