init
This commit is contained in:
commit
116abafc09
58 changed files with 5749 additions and 0 deletions
231
vignettes/custom-styling.Rmd
Normal file
231
vignettes/custom-styling.Rmd
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
---
|
||||
title: "Custom Map Styling"
|
||||
output: rmarkdown::html_vignette
|
||||
vignette: >
|
||||
%\VignetteIndexEntry{Custom Map Styling}
|
||||
%\VignetteEngine{knitr::rmarkdown}
|
||||
%\VignetteEncoding{UTF-8}
|
||||
---
|
||||
|
||||
```{r, include = FALSE}
|
||||
knitr::opts_chunk$set(
|
||||
collapse = TRUE,
|
||||
comment = "#>",
|
||||
eval = FALSE
|
||||
)
|
||||
```
|
||||
|
||||
*Note: Code examples are not evaluated in this vignette. Copy and run them in your R console to see the interactive maps.*
|
||||
|
||||
## Introduction
|
||||
|
||||
protomapr provides extensive customization options for map styling. This vignette shows how to create a minimal basemap with custom colors and filtered labels.
|
||||
|
||||
## Simple Color Customization
|
||||
|
||||
Use `pmColors()` to override specific colors while keeping proper rendering:
|
||||
|
||||
```{r}
|
||||
library(leaflet)
|
||||
library(protomapr)
|
||||
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 12) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
colors = pmColors(earth = "#f5f5f5", water = "#1a3a5c")
|
||||
)
|
||||
```
|
||||
|
||||
## Creating a Minimal Basemap
|
||||
|
||||
This example creates a clean, minimal map with:
|
||||
|
||||
- Uniform gray land (no parks, forests, or other land use distinctions)
|
||||
- Dark blue water
|
||||
- No roads or buildings
|
||||
- Hierarchical city labels based on importance
|
||||
- Styled water and island labels
|
||||
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 8) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
colors = pmColors(
|
||||
# Base colors - all land features same gray
|
||||
background = "#d3d3d3",
|
||||
earth = "#d3d3d3",
|
||||
water = "#1a3a5c",
|
||||
|
||||
# Land use - all matching background
|
||||
park = "#d3d3d3",
|
||||
wood = "#d3d3d3",
|
||||
scrub_a = "#d3d3d3",
|
||||
scrub_b = "#d3d3d3",
|
||||
glacier = "#d3d3d3",
|
||||
sand = "#d3d3d3",
|
||||
beach = "#d3d3d3",
|
||||
hospital = "#d3d3d3",
|
||||
school = "#d3d3d3",
|
||||
industrial = "#d3d3d3",
|
||||
pedestrian = "#d3d3d3",
|
||||
zoo = "#d3d3d3",
|
||||
military = "#d3d3d3",
|
||||
aerodrome = "#d3d3d3",
|
||||
|
||||
# Hide roads
|
||||
highway = "#d3d3d3",
|
||||
major = "#d3d3d3",
|
||||
medium = "#d3d3d3",
|
||||
minor = "#d3d3d3",
|
||||
link = "#d3d3d3",
|
||||
other = "#d3d3d3",
|
||||
railway = "#d3d3d3",
|
||||
boundary = "#d3d3d3",
|
||||
pier = "#d3d3d3",
|
||||
buildings = "#d3d3d3"
|
||||
),
|
||||
labelRules = list(
|
||||
# States/regions - large italic
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "italic 20px sans-serif",
|
||||
fill = "#666",
|
||||
stroke = "white",
|
||||
width = 2
|
||||
), filter = "feature.props.kind === 'region'"),
|
||||
|
||||
# Major metros (min_zoom <= 4: LA, SF, San Diego, Phoenix, Vegas)
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "600 18px sans-serif",
|
||||
fill = "#111",
|
||||
stroke = "white",
|
||||
width = 2
|
||||
), filter = "feature.props.kind === 'locality' && feature.props.min_zoom <= 4"),
|
||||
|
||||
# Large cities (min_zoom 5-6: Fresno, Sacramento, Long Beach, etc.)
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "600 14px sans-serif",
|
||||
fill = "#222",
|
||||
stroke = "white",
|
||||
width = 2
|
||||
), filter = "feature.props.kind === 'locality' && feature.props.min_zoom > 4 && feature.props.min_zoom <= 6"),
|
||||
|
||||
# Medium cities (min_zoom 7)
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "600 12px sans-serif",
|
||||
fill = "#333",
|
||||
stroke = "white",
|
||||
width = 2
|
||||
), filter = "feature.props.kind === 'locality' && feature.props.min_zoom === 7"),
|
||||
|
||||
# Small towns (min_zoom >= 8)
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "500 10px sans-serif",
|
||||
fill = "#444",
|
||||
stroke = "white",
|
||||
width = 1
|
||||
), filter = "feature.props.kind === 'locality' && feature.props.min_zoom >= 8"),
|
||||
|
||||
# Water labels
|
||||
pmLabelRule("water", pmCenteredTextSymbolizer(
|
||||
font = "italic 11px sans-serif",
|
||||
fill = "#0d2840",
|
||||
stroke = "#d3d3d3",
|
||||
width = 1,
|
||||
lineHeight = 1.5
|
||||
)),
|
||||
|
||||
# Natural features
|
||||
pmLabelRule("natural", pmCenteredTextSymbolizer(
|
||||
font = "italic 11px sans-serif",
|
||||
fill = "#444",
|
||||
stroke = "white",
|
||||
width = 1,
|
||||
lineHeight = 1.5
|
||||
)),
|
||||
|
||||
# Earth features (islands)
|
||||
pmLabelRule("earth", pmCenteredTextSymbolizer(
|
||||
font = "italic 11px sans-serif",
|
||||
fill = "#444",
|
||||
stroke = "white",
|
||||
width = 1,
|
||||
lineHeight = 1.5
|
||||
))
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Understanding Label Filters
|
||||
|
||||
Labels are filtered using JavaScript expressions on feature properties:
|
||||
|
||||
### Place Properties
|
||||
|
||||
| Property | Description | Example Values |
|
||||
|----------|-------------|----------------|
|
||||
| `kind` | Feature type | "country", "region", "locality" |
|
||||
| `kind_detail` | Specific type | "city", "town", "village", "state" |
|
||||
| `min_zoom` | Importance (lower = more important) | LA=2, SF=3, Fresno=5 |
|
||||
| `population_rank` | Population size | Higher = larger |
|
||||
|
||||
### Filter Examples
|
||||
|
||||
```{r}
|
||||
# Only states/regions
|
||||
filter = "feature.props.kind === 'region'"
|
||||
|
||||
# Only cities (not towns/villages)
|
||||
filter = "feature.props.kind_detail === 'city'"
|
||||
|
||||
# Major cities only
|
||||
filter = "feature.props.kind === 'locality' && feature.props.min_zoom <= 4"
|
||||
|
||||
# Medium-sized cities
|
||||
filter = "feature.props.kind === 'locality' && feature.props.min_zoom > 4 && feature.props.min_zoom <= 6"
|
||||
```
|
||||
|
||||
## Symbolizer Options
|
||||
|
||||
### Text Symbolizers
|
||||
|
||||
```{r}
|
||||
pmCenteredTextSymbolizer(
|
||||
font = "600 14px sans-serif", # CSS font (weight size family)
|
||||
fill = "#222", # Text color
|
||||
stroke = "white", # Halo color
|
||||
width = 2, # Halo width
|
||||
lineHeight = 1.5 # Line spacing for multi-word labels
|
||||
)
|
||||
```
|
||||
|
||||
### Font Specifications
|
||||
|
||||
Fonts follow CSS syntax: `"[weight] [style] size family"`
|
||||
|
||||
```{r}
|
||||
# Bold
|
||||
font = "600 14px sans-serif"
|
||||
font = "bold 14px Arial"
|
||||
|
||||
# Italic
|
||||
font = "italic 12px sans-serif"
|
||||
|
||||
# Bold italic
|
||||
font = "italic 600 14px sans-serif"
|
||||
```
|
||||
|
||||
## Layer Reference
|
||||
|
||||
| Layer | Description |
|
||||
|-------|-------------|
|
||||
| `earth` | Land polygons, islands |
|
||||
| `water` | Water bodies |
|
||||
| `places` | City/region labels |
|
||||
| `natural` | Natural features |
|
||||
| `landuse` | Parks, forests, etc. |
|
||||
| `roads` | Streets and highways |
|
||||
| `buildings` | Building footprints |
|
||||
|
||||
See `?protomaps_layers` for complete documentation.
|
||||
190
vignettes/data-viz-basemaps.Rmd
Normal file
190
vignettes/data-viz-basemaps.Rmd
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
---
|
||||
title: "Basemaps for Data Visualization"
|
||||
output: rmarkdown::html_vignette
|
||||
vignette: >
|
||||
%\VignetteIndexEntry{Basemaps for Data Visualization}
|
||||
%\VignetteEngine{knitr::rmarkdown}
|
||||
%\VignetteEncoding{UTF-8}
|
||||
---
|
||||
|
||||
```{r, include = FALSE}
|
||||
knitr::opts_chunk$set(
|
||||
collapse = TRUE,
|
||||
comment = "#>",
|
||||
eval = FALSE
|
||||
)
|
||||
```
|
||||
|
||||
*Note: Code examples are not evaluated in this vignette. Copy and run them in your R console to see the interactive maps.*
|
||||
|
||||
## Introduction
|
||||
|
||||
When overlaying data on maps, busy basemaps can distract from your visualization. This vignette shows how to create minimal, muted basemaps that let your data stand out.
|
||||
|
||||
## Ultra-Minimal: Land and Water Only
|
||||
|
||||
Strip everything except land and water boundaries using `pmMinimal()`:
|
||||
|
||||
```{r}
|
||||
library(leaflet)
|
||||
library(protomapr)
|
||||
|
||||
# Using the convenience function - just two lines!
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 10) %>%
|
||||
addProtomaps(url = protomaps_url(), style = pmMinimal())
|
||||
|
||||
# Custom colors
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 10) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
style = pmMinimal(land = "#f5f5f0", water = "#c8dce8")
|
||||
)
|
||||
```
|
||||
|
||||
## Subtle Context: Faint Roads
|
||||
|
||||
Keep roads barely visible for orientation without distraction:
|
||||
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
setView(lng = -73.98, lat = 40.75, zoom = 12) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
colors = pmColors(
|
||||
background = "#ffffff",
|
||||
earth = "#fafafa",
|
||||
water = "#f0f4f8",
|
||||
# Muted land use
|
||||
park = "#f5f7f5",
|
||||
wood = "#f5f7f5",
|
||||
scrub_a = "#fafafa",
|
||||
scrub_b = "#fafafa",
|
||||
glacier = "#fafafa",
|
||||
sand = "#fafafa",
|
||||
beach = "#fafafa",
|
||||
hospital = "#fafafa",
|
||||
school = "#fafafa",
|
||||
industrial = "#fafafa",
|
||||
pedestrian = "#fafafa",
|
||||
zoo = "#fafafa",
|
||||
military = "#fafafa",
|
||||
aerodrome = "#fafafa",
|
||||
# Faint roads - just slightly darker than background
|
||||
highway = "#e8e8e8",
|
||||
major = "#efefef",
|
||||
medium = "#f5f5f5",
|
||||
minor = "#fafafa",
|
||||
link = "#fafafa",
|
||||
other = "#fafafa",
|
||||
railway = "#f0f0f0",
|
||||
pier = "#fafafa",
|
||||
boundary = "#fafafa",
|
||||
buildings = "#fafafa"
|
||||
),
|
||||
labelRules = list()
|
||||
)
|
||||
```
|
||||
|
||||
## Dark Mode for Bright Data
|
||||
|
||||
Dark basemaps make colorful data points pop. Use the `minimal-dark` preset:
|
||||
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 11) %>%
|
||||
addProtomaps(url = protomaps_url(), style = pmStyle("minimal-dark"))
|
||||
```
|
||||
|
||||
## Muted with Major Labels Only
|
||||
|
||||
Sometimes you need city names for context but nothing else. Use `pmMinimal()` with `labels = TRUE`:
|
||||
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
setView(lng = -100, lat = 40, zoom = 4) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
style = pmMinimal(land = "#f5f5f0", water = "#d4e4ed", labels = TRUE)
|
||||
)
|
||||
|
||||
# Or use the "muted" preset which includes subtle roads and labels
|
||||
leaflet() %>%
|
||||
setView(lng = -100, lat = 40, zoom = 4) %>%
|
||||
addProtomaps(url = protomaps_url(), style = pmStyle("muted"))
|
||||
```
|
||||
|
||||
## Choropleth-Friendly: No Fill Competition
|
||||
|
||||
For choropleth maps, you want boundaries visible but no competing fill colors:
|
||||
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
setView(lng = -98, lat = 38, zoom = 4) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
colors = pmColors(
|
||||
background = "#ffffff",
|
||||
earth = "#ffffff",
|
||||
water = "#ffffff",
|
||||
# Hide all land use
|
||||
park = "#ffffff",
|
||||
wood = "#ffffff",
|
||||
scrub_a = "#ffffff",
|
||||
scrub_b = "#ffffff",
|
||||
glacier = "#ffffff",
|
||||
sand = "#ffffff",
|
||||
beach = "#ffffff",
|
||||
hospital = "#ffffff",
|
||||
school = "#ffffff",
|
||||
industrial = "#ffffff",
|
||||
pedestrian = "#ffffff",
|
||||
zoo = "#ffffff",
|
||||
military = "#ffffff",
|
||||
aerodrome = "#ffffff",
|
||||
# Hide roads
|
||||
highway = "#ffffff",
|
||||
major = "#ffffff",
|
||||
medium = "#ffffff",
|
||||
minor = "#ffffff",
|
||||
link = "#ffffff",
|
||||
other = "#ffffff",
|
||||
railway = "#ffffff",
|
||||
pier = "#ffffff",
|
||||
buildings = "#ffffff",
|
||||
# Keep boundaries visible
|
||||
boundary = "#cccccc"
|
||||
),
|
||||
labelRules = list(
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "600 11px sans-serif",
|
||||
fill = "#333333",
|
||||
stroke = "#ffffff",
|
||||
width = 2
|
||||
), filter = "feature.props.kind === 'region'")
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Watercolor Style
|
||||
|
||||
Soft, hand-drawn aesthetic using the `watercolor` preset:
|
||||
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 12) %>%
|
||||
addProtomaps(url = protomaps_url(), style = pmStyle("watercolor"))
|
||||
```
|
||||
|
||||
## Tips for Data Visualization Basemaps
|
||||
|
||||
1. **Match your data colors**: If your data uses warm colors, use cool-toned basemaps (and vice versa)
|
||||
|
||||
2. **Consider accessibility**: Ensure sufficient contrast between your data and basemap
|
||||
|
||||
3. **Remove labels when zoomed in**: Dense point data doesn't need city labels competing for attention
|
||||
|
||||
4. **Use transparency**: Leaflet markers and shapes can use alpha values to blend with basemaps
|
||||
|
||||
5. **Test at all zoom levels**: A basemap that works at zoom 10 may be too busy at zoom 15
|
||||
258
vignettes/getting-started.Rmd
Normal file
258
vignettes/getting-started.Rmd
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
---
|
||||
title: "Getting Started with protomapr"
|
||||
output: rmarkdown::html_vignette
|
||||
vignette: >
|
||||
%\VignetteIndexEntry{Getting Started with protomapr}
|
||||
%\VignetteEngine{knitr::rmarkdown}
|
||||
%\VignetteEncoding{UTF-8}
|
||||
---
|
||||
|
||||
```{r, include = FALSE}
|
||||
knitr::opts_chunk$set(
|
||||
collapse = TRUE,
|
||||
comment = "#>",
|
||||
eval = FALSE
|
||||
)
|
||||
```
|
||||
|
||||
*Note: Code examples are not evaluated in this vignette. Copy and run them in your R console to see the interactive maps.*
|
||||
|
||||
## Introduction
|
||||
|
||||
protomapr adds [Protomaps](https://protomaps.com/) vector tile layers to Leaflet maps in R. Protomaps provides fast, customizable map tiles using the PMTiles format.
|
||||
|
||||
## Why Protomaps?
|
||||
|
||||
Standard Leaflet maps use `addProviderTiles()` to load raster tiles from services like OpenStreetMap, Stadia, or CartoDB. These work well but have limitations:
|
||||
|
||||
| Feature | Raster Tiles (providerTiles) | Vector Tiles (Protomaps) |
|
||||
|---------|------------------------------|--------------------------|
|
||||
| Customization | Limited to provider's styles | Full control over colors, labels, features |
|
||||
| Self-hosting | Requires tile server infrastructure | Single PMTiles file, no server needed |
|
||||
| File size | Large (pre-rendered images) | Small (compressed vectors) |
|
||||
| Zoom transitions | Can appear pixelated | Smooth at any zoom level |
|
||||
| Offline use | Difficult | Easy with local PMTiles file |
|
||||
| Privacy | Requests go to third-party servers | Self-host for complete privacy |
|
||||
| Rate limits | Often have API limits | No limits when self-hosted |
|
||||
| Feature filtering | Not possible | Hide roads, buildings, labels, etc. |
|
||||
|
||||
### When to use Protomaps
|
||||
|
||||
- **Custom branded maps**: Match your organization's color scheme
|
||||
- **Minimal basemaps**: Hide distracting features for data visualization overlays
|
||||
- **Offline/embedded applications**: Bundle a PMTiles file with your app
|
||||
- **Privacy-sensitive contexts**: No third-party tile requests
|
||||
- **High-traffic applications**: Avoid API rate limits
|
||||
|
||||
### When providerTiles may be simpler
|
||||
|
||||
- Quick prototypes where default styling is fine
|
||||
- Satellite/aerial imagery (Protomaps is vector-only)
|
||||
- When you don't need customization
|
||||
|
||||
## Installation
|
||||
|
||||
```{r, eval = FALSE}
|
||||
# Install from CRAN
|
||||
install.packages("protomapr")
|
||||
|
||||
# Or install development version
|
||||
# devtools::install_github("yourusername/protomapr")
|
||||
```
|
||||
|
||||
## API Key Setup
|
||||
|
||||
Before using the Protomaps tile API, you need a free API key:
|
||||
|
||||
1. Sign up at https://protomaps.com/
|
||||
2. Set your key for the session:
|
||||
|
||||
```{r}
|
||||
library(leaflet)
|
||||
library(protomapr)
|
||||
|
||||
# Set your API key (do this once per session)
|
||||
set_protomaps_key("your-api-key-here")
|
||||
|
||||
# Or set via environment variable
|
||||
Sys.setenv(PROTOMAPS_API_KEY = "your-api-key-here")
|
||||
```
|
||||
For persistent storage, add to your `.Renviron` file: `PROTOMAPS_API_KEY=your-key-here`
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```{r}
|
||||
# Create a map with default light theme
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 12) %>%
|
||||
addProtomaps(url = protomaps_url())
|
||||
```
|
||||
|
||||
## Built-in Flavors
|
||||
|
||||
protomapr includes five built-in flavors:
|
||||
```{r}
|
||||
# Light (default)
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 12) %>%
|
||||
addProtomaps(url = protomaps_url(), flavor = "light")
|
||||
|
||||
# Dark
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 12) %>%
|
||||
addProtomaps(url = protomaps_url(), flavor = "dark")
|
||||
|
||||
# White (minimal, good for data visualization overlays)
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 12) %>%
|
||||
addProtomaps(url = protomaps_url(), flavor = "white")
|
||||
|
||||
# Grayscale
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 12) %>%
|
||||
addProtomaps(url = protomaps_url(), flavor = "grayscale")
|
||||
|
||||
# Black
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 12) %>%
|
||||
addProtomaps(url = protomaps_url(), flavor = "black")
|
||||
```
|
||||
|
||||
## Data Sources
|
||||
|
||||
### Demo/Development
|
||||
Use `protomaps_url()` for testing (uses Protomaps daily OpenStreetMap build):
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
addProtomaps(url = protomaps_url())
|
||||
```
|
||||
|
||||
### Self-hosted (recommended for production)
|
||||
|
||||
For production use, self-host a PMTiles file. This eliminates API rate limits, ensures privacy, and enables offline use.
|
||||
|
||||
#### Getting PMTiles files
|
||||
|
||||
**Option 1: Regional extract (recommended)**
|
||||
|
||||
Use [Protomaps Slice](https://slice.openstreetmap.us/) to extract just the region you need:
|
||||
|
||||
1. Go to https://slice.openstreetmap.us/
|
||||
2. Draw a bounding box around your area of interest
|
||||
3. Download the resulting PMTiles file (typically 10-500MB depending on region size)
|
||||
|
||||
**Option 2: Daily world builds**
|
||||
|
||||
Full planet builds are available at https://build.protomaps.com/ (~100GB). Only recommended if you need global coverage.
|
||||
|
||||
**Option 3: pmtiles CLI**
|
||||
|
||||
For scripted workflows, use the [pmtiles CLI tool](https://github.com/protomaps/go-pmtiles):
|
||||
|
||||
```bash
|
||||
# Install (requires Go)
|
||||
go install github.com/protomaps/go-pmtiles/cmd/pmtiles@latest
|
||||
|
||||
# Extract a region from a larger file
|
||||
pmtiles extract world.pmtiles california.pmtiles --bbox=-124.5,32.5,-114.1,42.0
|
||||
```
|
||||
|
||||
#### Hosting options
|
||||
|
||||
**Cloud storage (simplest)**
|
||||
|
||||
Upload to S3, Google Cloud Storage, or Cloudflare R2. PMTiles supports HTTP range requests, so no special server is needed.
|
||||
|
||||
```{r}
|
||||
# S3
|
||||
leaflet() %>%
|
||||
addProtomaps(url = "https://your-bucket.s3.amazonaws.com/tiles.pmtiles")
|
||||
|
||||
# Cloudflare R2
|
||||
leaflet() %>%
|
||||
addProtomaps(url = "https://your-bucket.r2.cloudflarestorage.com/tiles.pmtiles")
|
||||
```
|
||||
|
||||
**Important:** Enable CORS on your bucket to allow browser requests. Example S3 CORS policy:
|
||||
|
||||
```json
|
||||
[{
|
||||
"AllowedOrigins": ["*"],
|
||||
"AllowedMethods": ["GET", "HEAD"],
|
||||
"AllowedHeaders": ["Range"],
|
||||
"ExposeHeaders": ["Content-Length", "Content-Range"]
|
||||
}]
|
||||
```
|
||||
|
||||
**Local file (Shiny apps)**
|
||||
|
||||
For Shiny applications, serve PMTiles from `www/`:
|
||||
|
||||
```r
|
||||
# In your Shiny app, place tiles.pmtiles in www/ folder
|
||||
leaflet() %>%
|
||||
addProtomaps(url = "tiles.pmtiles")
|
||||
```
|
||||
|
||||
**Local development**
|
||||
|
||||
For local testing, use a simple HTTP server:
|
||||
|
||||
```bash
|
||||
# Python
|
||||
cd /path/to/pmtiles/folder
|
||||
python -m http.server 8000
|
||||
|
||||
# Then in R
|
||||
leaflet() %>%
|
||||
addProtomaps(url = "http://localhost:8000/tiles.pmtiles")
|
||||
```
|
||||
|
||||
## Preset Styles
|
||||
|
||||
For common use cases, protomapr provides preset styles that are simpler than the built-in flavors:
|
||||
|
||||
```{r}
|
||||
# Ultra-minimal basemap for data visualization
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 10) %>%
|
||||
addProtomaps(url = protomaps_url(), style = pmMinimal())
|
||||
|
||||
# Minimal with city labels
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 10) %>%
|
||||
addProtomaps(url = protomaps_url(), style = pmMinimal(labels = TRUE))
|
||||
|
||||
# Dark minimal
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 10) %>%
|
||||
addProtomaps(url = protomaps_url(), style = pmStyle("minimal-dark"))
|
||||
|
||||
# Soft watercolor aesthetic
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 10) %>%
|
||||
addProtomaps(url = protomaps_url(), style = pmStyle("watercolor"))
|
||||
|
||||
# Muted with subtle roads
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 10) %>%
|
||||
addProtomaps(url = protomaps_url(), style = pmStyle("muted"))
|
||||
```
|
||||
|
||||
### Available presets
|
||||
|
||||
| Preset | Description |
|
||||
|--------|-------------|
|
||||
| `pmMinimal()` | Hides roads, buildings, land use - shows only land and water |
|
||||
| `pmStyle("minimal")` | Same as `pmMinimal()` |
|
||||
| `pmStyle("minimal-dark")` | Dark version of minimal |
|
||||
| `pmStyle("muted")` | Subtle colors, faint roads, major city labels |
|
||||
| `pmStyle("watercolor")` | Soft, hand-painted aesthetic |
|
||||
|
||||
## Next Steps
|
||||
|
||||
See `vignette("custom-styling")` to learn how to customize colors, labels, and map features.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
This package wraps [protomaps-leaflet](https://github.com/protomaps/protomaps-leaflet), created by [Brandon Liu](https://bdon.org/). The Protomaps project provides an open-source stack for self-hosted vector maps, including the PMTiles format. Thanks to Brandon for making beautiful, customizable maps accessible to everyone.
|
||||
327
vignettes/labels-and-filters.Rmd
Normal file
327
vignettes/labels-and-filters.Rmd
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
---
|
||||
title: "Labels, Filters, and Feature Selection"
|
||||
output: rmarkdown::html_vignette
|
||||
vignette: >
|
||||
%\VignetteIndexEntry{Labels, Filters, and Feature Selection}
|
||||
%\VignetteEngine{knitr::rmarkdown}
|
||||
%\VignetteEncoding{UTF-8}
|
||||
---
|
||||
|
||||
```{r, include = FALSE}
|
||||
knitr::opts_chunk$set(
|
||||
collapse = TRUE,
|
||||
comment = "#>",
|
||||
eval = FALSE
|
||||
)
|
||||
```
|
||||
|
||||
*Note: Code examples are not evaluated in this vignette. Copy and run them in your R console to see the interactive maps.*
|
||||
|
||||
## Introduction
|
||||
|
||||
Protomaps vector tiles contain rich metadata about each feature. This vignette shows how to use filters to selectively style and label features based on their properties.
|
||||
|
||||
## Understanding Feature Properties
|
||||
|
||||
Each feature in the vector tiles has a `props` object with metadata. Common properties include:
|
||||
|
||||
| Layer | Property | Values |
|
||||
|-------|----------|--------|
|
||||
| places | `kind` | "country", "region", "locality" |
|
||||
| places | `min_zoom` | 1-15 (lower = more important) |
|
||||
| roads | `kind` | "highway", "major_road", "minor_road", "path" |
|
||||
| water | `kind` | "ocean", "lake", "river" |
|
||||
| landuse | `kind` | "park", "forest", "residential", "industrial" |
|
||||
|
||||
## Hierarchical City Labels
|
||||
|
||||
Style cities by importance using `min_zoom`:
|
||||
|
||||
```{r}
|
||||
library(leaflet)
|
||||
library(protomapr)
|
||||
|
||||
leaflet() %>%
|
||||
setView(lng = -100, lat = 40, zoom = 5) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
flavor = "light",
|
||||
labelRules = list(
|
||||
# Capital cities and major metros (min_zoom 1-3)
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "700 16px sans-serif",
|
||||
fill = "#1a1a1a",
|
||||
stroke = "white",
|
||||
width = 3
|
||||
), filter = "feature.props.kind === 'locality' && feature.props.min_zoom <= 3"),
|
||||
|
||||
# Large cities (min_zoom 4-5)
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "600 13px sans-serif",
|
||||
fill = "#333333",
|
||||
stroke = "white",
|
||||
width = 2
|
||||
), filter = "feature.props.kind === 'locality' && feature.props.min_zoom > 3 && feature.props.min_zoom <= 5"),
|
||||
|
||||
# Medium cities (min_zoom 6-7)
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "500 11px sans-serif",
|
||||
fill = "#555555",
|
||||
stroke = "white",
|
||||
width = 2
|
||||
), filter = "feature.props.kind === 'locality' && feature.props.min_zoom > 5 && feature.props.min_zoom <= 7"),
|
||||
|
||||
# States/provinces
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "italic 300 14px sans-serif",
|
||||
fill = "#888888",
|
||||
stroke = "white",
|
||||
width = 1
|
||||
), filter = "feature.props.kind === 'region'")
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Road Hierarchy Styling
|
||||
|
||||
Style different road types with distinct colors and widths:
|
||||
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.78, zoom = 13) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
paintRules = list(
|
||||
# Base layers
|
||||
pmPaintRule("earth", pmPolygonSymbolizer(fill = "#f5f5f5")),
|
||||
pmPaintRule("water", pmPolygonSymbolizer(fill = "#cce0eb")),
|
||||
pmPaintRule("landuse", pmPolygonSymbolizer(fill = "#e8f0e8"),
|
||||
filter = "feature.props.kind === 'park'"),
|
||||
|
||||
# Highways - orange, thick
|
||||
pmPaintRule("roads", pmLineSymbolizer(color = "#f59e0b", width = 4),
|
||||
filter = "feature.props.kind === 'highway'"),
|
||||
|
||||
# Major roads - dark gray, medium
|
||||
pmPaintRule("roads", pmLineSymbolizer(color = "#6b7280", width = 2.5),
|
||||
filter = "feature.props.kind === 'major_road'"),
|
||||
|
||||
# Minor roads - light gray, thin
|
||||
pmPaintRule("roads", pmLineSymbolizer(color = "#9ca3af", width = 1.5),
|
||||
filter = "feature.props.kind === 'minor_road'"),
|
||||
|
||||
# Paths - dashed
|
||||
pmPaintRule("roads", pmLineSymbolizer(color = "#d1d5db", width = 1, dash = c(3, 2)),
|
||||
filter = "feature.props.kind === 'path'")
|
||||
),
|
||||
labelRules = list(
|
||||
pmLabelRule("roads", pmLineLabelSymbolizer(
|
||||
font = "600 10px sans-serif",
|
||||
fill = "#f59e0b",
|
||||
stroke = "white",
|
||||
width = 2
|
||||
), filter = "feature.props.kind === 'highway'", minzoom = 12),
|
||||
|
||||
pmLabelRule("roads", pmLineLabelSymbolizer(
|
||||
font = "500 9px sans-serif",
|
||||
fill = "#4b5563",
|
||||
stroke = "white",
|
||||
width = 2
|
||||
), filter = "feature.props.kind === 'major_road'", minzoom = 14)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Water Feature Labels
|
||||
|
||||
Style different water bodies appropriately:
|
||||
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.8, zoom = 10) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
colors = pmColors(
|
||||
earth = "#f0f0f0",
|
||||
water = "#1e40af"
|
||||
),
|
||||
labelRules = list(
|
||||
# Ocean labels - large, bold
|
||||
pmLabelRule("water", pmCenteredTextSymbolizer(
|
||||
font = "italic 700 18px sans-serif",
|
||||
fill = "#1e3a5f",
|
||||
stroke = "#e0f0ff",
|
||||
width = 2,
|
||||
lineHeight = 1.5
|
||||
), filter = "feature.props.kind === 'ocean'"),
|
||||
|
||||
# Bay/gulf labels
|
||||
pmLabelRule("water", pmCenteredTextSymbolizer(
|
||||
font = "italic 500 14px sans-serif",
|
||||
fill = "#1e4070",
|
||||
stroke = "#e0f0ff",
|
||||
width = 2,
|
||||
lineHeight = 1.5
|
||||
), filter = "feature.props.kind === 'bay' || feature.props.kind === 'gulf'"),
|
||||
|
||||
# Lakes
|
||||
pmLabelRule("water", pmCenteredTextSymbolizer(
|
||||
font = "italic 11px sans-serif",
|
||||
fill = "#2050a0",
|
||||
stroke = "#e0f0ff",
|
||||
width = 1,
|
||||
lineHeight = 1.5
|
||||
), filter = "feature.props.kind === 'lake' || feature.props.kind === 'water'")
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Zoom-Dependent Visibility
|
||||
|
||||
Show different features at different zoom levels:
|
||||
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.78, zoom = 14) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
paintRules = list(
|
||||
pmPaintRule("earth", pmPolygonSymbolizer(fill = "#f8f8f8")),
|
||||
pmPaintRule("water", pmPolygonSymbolizer(fill = "#d0e8f0")),
|
||||
pmPaintRule("landuse", pmPolygonSymbolizer(fill = "#e0f0e0")),
|
||||
|
||||
# Buildings only visible at zoom 14+
|
||||
pmPaintRule("buildings", pmPolygonSymbolizer(
|
||||
fill = "#e0e0e0",
|
||||
stroke = "#cccccc",
|
||||
width = 0.5
|
||||
), minzoom = 14),
|
||||
|
||||
# Roads
|
||||
pmPaintRule("roads", pmLineSymbolizer(color = "#888888", width = 2))
|
||||
),
|
||||
labelRules = list(
|
||||
# Neighborhoods at medium zoom
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "600 12px sans-serif",
|
||||
fill = "#444444",
|
||||
stroke = "white",
|
||||
width = 2
|
||||
), filter = "feature.props.kind === 'neighbourhood'", minzoom = 13, maxzoom = 16),
|
||||
|
||||
# Street names at high zoom only
|
||||
pmLabelRule("roads", pmLineLabelSymbolizer(
|
||||
font = "500 9px sans-serif",
|
||||
fill = "#666666",
|
||||
stroke = "white",
|
||||
width = 2
|
||||
), minzoom = 15)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## POI (Points of Interest) Styling
|
||||
|
||||
Show specific categories of points of interest:
|
||||
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.78, zoom = 15) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
flavor = "light",
|
||||
paintRules = list(
|
||||
# Restaurants
|
||||
pmPaintRule("pois", pmCircleSymbolizer(
|
||||
radius = 5,
|
||||
fill = "#ef4444",
|
||||
stroke = "white",
|
||||
width = 1
|
||||
), filter = "feature.props.kind === 'restaurant'"),
|
||||
|
||||
# Cafes
|
||||
pmPaintRule("pois", pmCircleSymbolizer(
|
||||
radius = 5,
|
||||
fill = "#8b5cf6",
|
||||
stroke = "white",
|
||||
width = 1
|
||||
), filter = "feature.props.kind === 'cafe'"),
|
||||
|
||||
# Parks
|
||||
pmPaintRule("pois", pmCircleSymbolizer(
|
||||
radius = 6,
|
||||
fill = "#22c55e",
|
||||
stroke = "white",
|
||||
width = 1
|
||||
), filter = "feature.props.kind === 'park'")
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Combining Multiple Filters
|
||||
|
||||
Use logical operators for complex filtering:
|
||||
|
||||
```{r}
|
||||
leaflet() %>%
|
||||
setView(lng = -122.4, lat = 37.78, zoom = 12) %>%
|
||||
addProtomaps(
|
||||
url = protomaps_url(),
|
||||
colors = pmColors(earth = "#f5f5f5", water = "#ddeeff"),
|
||||
labelRules = list(
|
||||
# Large cities in California (using bounding box approximation)
|
||||
pmLabelRule("places", pmCenteredTextSymbolizer(
|
||||
font = "600 14px sans-serif",
|
||||
fill = "#1a1a1a",
|
||||
stroke = "white",
|
||||
width = 2
|
||||
), filter = "feature.props.kind === 'locality' && feature.props.min_zoom <= 6"),
|
||||
|
||||
# Exclude small water features
|
||||
pmLabelRule("water", pmCenteredTextSymbolizer(
|
||||
font = "italic 11px sans-serif",
|
||||
fill = "#3366aa",
|
||||
stroke = "#ddeeff",
|
||||
width = 1,
|
||||
lineHeight = 1.5
|
||||
), filter = "feature.props.kind !== 'stream' && feature.props.kind !== 'river'")
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Filter Expression Reference
|
||||
|
||||
Filters are JavaScript expressions with access to `zoom` and `feature`:
|
||||
|
||||
```javascript
|
||||
// Equality
|
||||
feature.props.kind === 'highway'
|
||||
|
||||
// Inequality
|
||||
feature.props.min_zoom <= 5
|
||||
|
||||
// Logical AND
|
||||
feature.props.kind === 'locality' && feature.props.min_zoom <= 4
|
||||
|
||||
// Logical OR
|
||||
feature.props.kind === 'lake' || feature.props.kind === 'reservoir'
|
||||
|
||||
// NOT
|
||||
feature.props.kind !== 'path'
|
||||
|
||||
// Zoom-based (though minzoom/maxzoom params are preferred)
|
||||
zoom >= 12 && feature.props.kind === 'minor_road'
|
||||
|
||||
// Check if property exists
|
||||
feature.props.name !== undefined
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
1. **Use minzoom/maxzoom params** instead of zoom filters when possible - they're more efficient
|
||||
|
||||
2. **Order matters**: Rules are applied in order; later rules can override earlier ones
|
||||
|
||||
3. **Test with the built-in flavors first** to understand which features appear at which zooms
|
||||
|
||||
4. **Check the console**: Invalid filter expressions log errors to the browser console
|
||||
Loading…
Add table
Add a link
Reference in a new issue