MVT output with simplification = FALSE now preserves
line positions when a sub-pixel line part disappears during encoding.
This affects multipart lines and single lines split by tile clipping.
Default simplification and MLT output are unaffected.
Requests for native features omitted from the CRAN build now explain how to install from R-Universe, including the platform limits of Rust DuckDB and streaming and the R DuckDB fallback.
Native GeoParquet and DuckDB readers preserve DATE and timestamp
properties as ISO 8601 text instead of silently replacing them with
nulls (#18). Timestamps use a T separator; zoned timestamps
are normalized to UTC with a Z suffix, independent of the
DuckDB session time zone. Genuine nulls remain null. The R DuckDB
fallback uses the same timestamp format; direct freestile()
calls on sf objects retain their existing behavior. Formatting zoned
query timestamps does not require DuckDB’s ICU extension.
Rust DuckDB queries now export geometry only as WKB, fixing
failures with CRS-tagged geometry such as ST_Read()
shapefiles (#19). CRS-tagged query results are now automatically
reprojected to WGS84, including streaming queries. Reprojection
preserves longitude/latitude axis order in both the native reader and
the R DuckDB fallback used by CRAN builds.
Experimental ordered categorical clustering is available through
freestile_file() in R and Python (category,
category_values, cluster_min_points). It uses
the pinned Supercluster 8.0.1/KDBush 4.1.0 port, with 512-tile pixel
radius, typed category counts and expansion zoom. This increment
requires a POINT GeoParquet file,
cluster_maxzoom = max_zoom, and no thinning/coalescing; use
a separate dot source above the cutoff. The global index is compact but
resident, not out-of-core. Existing noncategorical clustering is
unchanged pending the broader API migration. Output is spooled one tile
at a time, exact singleton originals live on disk, and
FREESTILER_CLUSTER_TILE_BUDGET_MB (default 256) guards
decoded tile buffers and the tile-coordinate index. Numeric/string-union
abbreviation fields are omitted from TileJSON’s single-type field
map.
freestile_h3() now supports aggregate-only archives
with include_points = FALSE, covering the complete
requested zoom range without fetching a raw-point frame. Optional
category and category_values add
point_count, <category>:<value>
counts, an _other count for missing or unlisted values, and
modal category/count/share/tie fields. Ties have no selected category. R
and Python share these semantics. All finalized hex layers are still
held in memory; bounded polygon output is separate work.
H3 integer aggregates are checked before host conversion and encoded as integers, including values above R’s 32-bit integer range (supported exact range: +/- (2^53 - 1)). Antimeridian repair now touches only crossing cells.
PMTiles field metadata now uses TileJSON Number,
String, and Boolean types, with a
field_order extension preserving requested property order.
view_h3_tiles() defaults to point_count for
categorical output and skips H3 bookkeeping fields when choosing a
numeric aggregate.
The streaming point pipeline (freestile_query()) now
partitions the query result on disk and tiles each partition
independently instead of sorting the whole dataset at once. This removes
the global sort that previously spilled tens of gigabytes on very large
inputs and makes billion-point tiling feasible with bounded memory. Runs
that would exhaust a resource now stop with a clear error (naming the
tile or region and suggesting drop_rate or a higher
min_zoom) rather than degrading, and a failed run never
disturbs an existing output. Optional environment variables:
FREESTILER_TEMP_DIR (where bulk temporary data goes),
FREESTILER_DUCKDB_MEMORY, and
FREESTILER_STREAM_WORKERS.
With drop_rate, streaming point thinning is now
computed per partition: the per-zoom density is unchanged, but the exact
set of retained points can differ from earlier releases (the sampling
phase restarts per partition, so membership shifts are not limited to
partition boundaries). Without drop_rate output is
unchanged.
Tile generation now uses dramatically less memory on large inputs. Encoded tiles are compressed and spooled to a temporary file as they are produced instead of being accumulated in RAM across all zoom levels, and the PMTiles archive is assembled by streaming from that spool. Together with allocation fixes in the GeoParquet reader and MVT encoder, peak memory for multi-million-feature polygon inputs drops to roughly the cost of the decoded features themselves. Note the tradeoff: tile data is staged on disk, so a build transiently needs about twice the final archive size in free space.
Output archives are now written atomically: tiles are assembled
in a temporary sibling file that is renamed over the destination only on
success. A failed run no longer leaves a partial archive, and an
existing output file is preserved until its replacement is complete.
Relatedly, overwrite = TRUE is now respected through the
DuckDB and CRS-reprojection fallback paths instead of being reset to
FALSE internally.
Identical tiles are no longer deduplicated within the archive (a
micro-optimization measured at ~0.01% of archive size on real data); the
PMTiles clustered header flag is now computed from the
actual tile data layout rather than always claimed.
macOS builds now use Rust’s baseline deployment target for Rust and dependency C objects instead of the host OS version. This prevents warnings when R links for an older macOS version. Explicit user deployment targets are preserved.
freestile_h3() is a new function for dynamic hexagonal
binning. It aggregates points into H3 hexagons at zoom-appropriate
resolutions via DuckDB’s H3 community extension and writes a multi-layer
.pmtiles archive where low zooms show coarse hexes,
intermediate zooms show progressively finer hexes, and zooms at or above
base_zoom show the raw points. Aggregation rules are
user-defined SQL expressions
(e.g. c(n = "COUNT(*)", avg_pop = "AVG(pop)")). Opt-in
fade = TRUE produces overlapping zoom windows so adjacent
hex resolutions can cross-fade visually.view_h3_tiles() is a companion viewer that auto-styles
a freestile_h3() archive in mapgl, detecting
clean-break vs cross-fade mode from the PMTiles metadata.vignette("h3-hexagonal-binning") for a
walkthrough.Initial release.
freestile() creates PMTiles archives from sf data
frames with zero external dependencies (no tippecanoe, no Java, no
Go).freestile_layer()
per-layer zoom control.drop_rate exponential feature thinning with
Morton-curve spatial ordering for points and area-based ordering for
polygons/lines.base_zoom control for ensuring all features present at
higher zooms.cluster_distance point clustering with
point_count attribute.coalesce line merging and polygon grouping.