352 lines
8.7 KiB
Markdown
352 lines
8.7 KiB
Markdown
|
|
# protomapr
|
||
|
|
|
||
|
|
An R package to add [Protomaps](https://protomaps.com/) vector tile layers to Leaflet maps.
|
||
|
|
|
||
|
|
## Why Protomaps?
|
||
|
|
|
||
|
|
Standard Leaflet maps use `addProviderTiles()` to load raster tiles from services like OpenStreetMap or CartoDB. Protomaps offers a vector tile alternative with key advantages:
|
||
|
|
|
||
|
|
| | Raster Tiles | Protomaps (Vector) |
|
||
|
|
|---|---|---|
|
||
|
|
| **Customization** | Limited to provider styles | Full control over colors, labels, features |
|
||
|
|
| **Self-hosting** | Requires tile server | Single PMTiles file, no server needed |
|
||
|
|
| **Feature control** | Show everything or nothing | Hide roads, buildings, labels selectively |
|
||
|
|
| **Privacy** | Requests to third-party servers | Self-host for complete privacy |
|
||
|
|
| **Rate limits** | Often have API quotas | No limits when self-hosted |
|
||
|
|
| **Zoom quality** | Can pixelate | Smooth at any zoom level |
|
||
|
|
| **File size** | Large (pre-rendered images) | Smaller (compressed vectors) |
|
||
|
|
|
||
|
|
**Use Protomaps when you need:**
|
||
|
|
- Custom branded maps matching your color scheme
|
||
|
|
- Minimal basemaps for data visualization (hide distracting features)
|
||
|
|
- Offline or embedded applications
|
||
|
|
- Privacy-sensitive contexts
|
||
|
|
- High-traffic apps without API rate limits
|
||
|
|
|
||
|
|
**Use providerTiles when:**
|
||
|
|
- Default styling is fine
|
||
|
|
- You need satellite/aerial imagery
|
||
|
|
- Quick prototypes
|
||
|
|
|
||
|
|
## Installation
|
||
|
|
|
||
|
|
```r
|
||
|
|
# Install from local source
|
||
|
|
devtools::install_local("path/to/protomapr")
|
||
|
|
|
||
|
|
# Or install dependencies and load
|
||
|
|
install.packages(c("leaflet", "htmltools", "htmlwidgets", "jsonlite"))
|
||
|
|
```
|
||
|
|
|
||
|
|
## Quick Start
|
||
|
|
|
||
|
|
```r
|
||
|
|
library(leaflet)
|
||
|
|
library(protomapr)
|
||
|
|
|
||
|
|
# Use the demo URL (free Protomaps daily build)
|
||
|
|
leaflet() %>%
|
||
|
|
setView(lng = -122.4, lat = 37.8, zoom = 12) %>%
|
||
|
|
addProtomaps(url = protomaps_demo_url())
|
||
|
|
```
|
||
|
|
|
||
|
|
## Data Sources
|
||
|
|
|
||
|
|
You have several options for PMTiles data:
|
||
|
|
|
||
|
|
### 1. Demo/Development (free)
|
||
|
|
|
||
|
|
Use `protomaps_demo_url()` which points to the Protomaps daily OpenStreetMap build:
|
||
|
|
|
||
|
|
```r
|
||
|
|
leaflet() %>%
|
||
|
|
addProtomaps(url = protomaps_demo_url())
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Self-hosted (recommended for production)
|
||
|
|
|
||
|
|
Download a PMTiles file and host it on cloud storage (S3, GCS, Cloudflare R2, etc.):
|
||
|
|
|
||
|
|
- Download daily builds: https://maps.protomaps.com/builds/
|
||
|
|
- Extract a region: https://slice.openstreetmap.us/
|
||
|
|
|
||
|
|
```r
|
||
|
|
leaflet() %>%
|
||
|
|
addProtomaps(url = "https://your-bucket.s3.amazonaws.com/tiles.pmtiles")
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Local file
|
||
|
|
|
||
|
|
For local development, you can serve a PMTiles file locally:
|
||
|
|
|
||
|
|
```r
|
||
|
|
# Serve with a local HTTP server, then:
|
||
|
|
leaflet() %>%
|
||
|
|
addProtomaps(url = "http://localhost:8080/tiles.pmtiles")
|
||
|
|
```
|
||
|
|
|
||
|
|
## Basic Usage
|
||
|
|
|
||
|
|
```r
|
||
|
|
library(leaflet)
|
||
|
|
library(protomapr)
|
||
|
|
|
||
|
|
leaflet() %>%
|
||
|
|
setView(lng = -122.4, lat = 37.8, zoom = 12) %>%
|
||
|
|
addProtomaps(
|
||
|
|
url = protomaps_demo_url(),
|
||
|
|
flavor = "light"
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Flavors
|
||
|
|
|
||
|
|
Five built-in flavors are available:
|
||
|
|
|
||
|
|
```r
|
||
|
|
# Light flavor (default)
|
||
|
|
leaflet() %>%
|
||
|
|
addProtomaps(url = protomaps_demo_url(), flavor = "light")
|
||
|
|
|
||
|
|
# Dark flavor
|
||
|
|
leaflet() %>%
|
||
|
|
addProtomaps(url = protomaps_demo_url(), flavor = "dark")
|
||
|
|
|
||
|
|
# White flavor (for data visualization)
|
||
|
|
leaflet() %>%
|
||
|
|
addProtomaps(url = protomaps_demo_url(), flavor = "white")
|
||
|
|
|
||
|
|
# Grayscale flavor
|
||
|
|
leaflet() %>%
|
||
|
|
addProtomaps(url = protomaps_demo_url(), flavor = "grayscale")
|
||
|
|
|
||
|
|
# Black flavor
|
||
|
|
leaflet() %>%
|
||
|
|
addProtomaps(url = protomaps_demo_url(), flavor = "black")
|
||
|
|
```
|
||
|
|
|
||
|
|
## Custom Styling
|
||
|
|
|
||
|
|
### Paint Rules
|
||
|
|
|
||
|
|
Control how features are rendered using paint rules:
|
||
|
|
```r
|
||
|
|
leaflet() %>%
|
||
|
|
setView(lng = -122.4, lat = 37.8, zoom = 12) %>%
|
||
|
|
addProtomaps(
|
||
|
|
url = protomaps_demo_url(),
|
||
|
|
paintRules = list(
|
||
|
|
pmPaintRule("water", pmPolygonSymbolizer(fill = "steelblue")),
|
||
|
|
pmPaintRule("earth", pmPolygonSymbolizer(fill = "#f0f0f0")),
|
||
|
|
pmPaintRule("roads", pmLineSymbolizer(color = "gray", width = 1)),
|
||
|
|
pmPaintRule("buildings", pmPolygonSymbolizer(
|
||
|
|
fill = "#d4d4d4",
|
||
|
|
stroke = "#999",
|
||
|
|
width = 0.5
|
||
|
|
))
|
||
|
|
)
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Label Rules
|
||
|
|
|
||
|
|
Add text labels to features:
|
||
|
|
|
||
|
|
```r
|
||
|
|
leaflet() %>%
|
||
|
|
setView(lng = -122.4, lat = 37.8, zoom = 14) %>%
|
||
|
|
addProtomaps(
|
||
|
|
url = protomaps_demo_url(),
|
||
|
|
labelRules = list(
|
||
|
|
pmLabelRule("places",
|
||
|
|
pmCenteredTextSymbolizer(
|
||
|
|
font = "14px Arial",
|
||
|
|
fill = "black",
|
||
|
|
stroke = "white",
|
||
|
|
width = 2
|
||
|
|
)
|
||
|
|
),
|
||
|
|
pmLabelRule("roads",
|
||
|
|
pmLineLabelSymbolizer(
|
||
|
|
font = "11px Arial",
|
||
|
|
fill = "#333"
|
||
|
|
),
|
||
|
|
minzoom = 14
|
||
|
|
)
|
||
|
|
)
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Filtering Features
|
||
|
|
|
||
|
|
Use JavaScript filter expressions to style features based on their properties:
|
||
|
|
|
||
|
|
```r
|
||
|
|
# Cities only (not towns/villages)
|
||
|
|
pmLabelRule("places", pmCenteredTextSymbolizer(font = "14px Arial", fill = "black"),
|
||
|
|
filter = "feature.props.kind_detail === 'city'")
|
||
|
|
|
||
|
|
# States/regions
|
||
|
|
pmLabelRule("places", pmCenteredTextSymbolizer(font = "18px Arial", fill = "#666"),
|
||
|
|
filter = "feature.props.kind === 'region'")
|
||
|
|
|
||
|
|
# Important places (low min_zoom = more important)
|
||
|
|
pmLabelRule("places", pmCenteredTextSymbolizer(font = "16px Arial", fill = "black"),
|
||
|
|
filter = "feature.props.min_zoom <= 6")
|
||
|
|
|
||
|
|
# Highways only
|
||
|
|
pmPaintRule("roads", pmLineSymbolizer(color = "orange", width = 3),
|
||
|
|
filter = "feature.props.kind === 'highway'")
|
||
|
|
|
||
|
|
# Show features at specific zoom levels
|
||
|
|
pmPaintRule("buildings", pmPolygonSymbolizer(fill = "#ccc"),
|
||
|
|
minzoom = 14, maxzoom = 18)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Layers and Properties
|
||
|
|
|
||
|
|
For full documentation run `?protomaps_layers` in R. Quick reference:
|
||
|
|
|
||
|
|
### Layer Names
|
||
|
|
|
||
|
|
| Layer | Description |
|
||
|
|
|-------|-------------|
|
||
|
|
| `earth` | Land polygons |
|
||
|
|
| `water` | Water bodies |
|
||
|
|
| `landuse` | Parks, forests, residential, etc. |
|
||
|
|
| `roads` | Streets and highways |
|
||
|
|
| `buildings` | Building footprints |
|
||
|
|
| `places` | City/town/region labels |
|
||
|
|
| `pois` | Points of interest |
|
||
|
|
| `boundaries` | Administrative boundaries |
|
||
|
|
|
||
|
|
### Key Properties for Filtering
|
||
|
|
|
||
|
|
**places:**
|
||
|
|
- `kind`: "country", "region", "locality"
|
||
|
|
- `kind_detail`: "city", "town", "village", "state", "province"
|
||
|
|
- `min_zoom`: importance (lower = more important)
|
||
|
|
- `population_rank`: size (higher = larger)
|
||
|
|
|
||
|
|
**water:**
|
||
|
|
- `kind`: "water", "lake", "ocean"
|
||
|
|
- `kind_detail`: "river", "lake", "reservoir", "stream"
|
||
|
|
|
||
|
|
**roads:**
|
||
|
|
- `kind`: "highway", "major_road", "minor_road", "path"
|
||
|
|
- `kind_detail`: "motorway", "primary", "residential", "footway"
|
||
|
|
|
||
|
|
**landuse:**
|
||
|
|
- `kind`: "park", "forest", "residential", "industrial"
|
||
|
|
|
||
|
|
## Symbolizer Reference
|
||
|
|
|
||
|
|
### pmPolygonSymbolizer
|
||
|
|
|
||
|
|
Style polygon features:
|
||
|
|
|
||
|
|
```r
|
||
|
|
pmPolygonSymbolizer(
|
||
|
|
fill = "#cccccc", # Fill color
|
||
|
|
stroke = "#333", # Outline color
|
||
|
|
width = 1, # Outline width
|
||
|
|
opacity = 0.8 # Fill opacity (0-1)
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### pmLineSymbolizer
|
||
|
|
|
||
|
|
Style line features:
|
||
|
|
|
||
|
|
```r
|
||
|
|
pmLineSymbolizer(
|
||
|
|
color = "#000000", # Line color
|
||
|
|
width = 2, # Line width in pixels
|
||
|
|
dash = c(4, 2), # Dash pattern (4px dash, 2px gap)
|
||
|
|
lineCap = "round", # "butt", "round", or "square"
|
||
|
|
lineJoin = "round", # "miter", "round", or "bevel"
|
||
|
|
opacity = 1 # Line opacity (0-1)
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### pmCircleSymbolizer
|
||
|
|
|
||
|
|
Style point features as circles:
|
||
|
|
|
||
|
|
```r
|
||
|
|
pmCircleSymbolizer(
|
||
|
|
radius = 6, # Circle radius in pixels
|
||
|
|
fill = "red", # Fill color
|
||
|
|
stroke = "black", # Outline color
|
||
|
|
width = 1 # Outline width
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### pmCenteredTextSymbolizer
|
||
|
|
|
||
|
|
Add centered text labels:
|
||
|
|
|
||
|
|
```r
|
||
|
|
pmCenteredTextSymbolizer(
|
||
|
|
font = "14px Arial", # CSS font specification
|
||
|
|
fill = "black", # Text color
|
||
|
|
stroke = "white", # Halo color
|
||
|
|
width = 2 # Halo width
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### pmLineLabelSymbolizer
|
||
|
|
|
||
|
|
Add labels along line features (e.g., street names):
|
||
|
|
|
||
|
|
```r
|
||
|
|
pmLineLabelSymbolizer(
|
||
|
|
font = "11px Arial",
|
||
|
|
fill = "#333",
|
||
|
|
stroke = "white",
|
||
|
|
width = 1
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### pmShieldSymbolizer
|
||
|
|
|
||
|
|
Add shield/badge labels (e.g., highway markers):
|
||
|
|
|
||
|
|
```r
|
||
|
|
pmShieldSymbolizer(
|
||
|
|
font = "10px Arial",
|
||
|
|
fill = "black",
|
||
|
|
background = "white",
|
||
|
|
stroke = "black",
|
||
|
|
padding = 2
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Additional Options
|
||
|
|
|
||
|
|
```r
|
||
|
|
leaflet() %>%
|
||
|
|
addProtomaps(
|
||
|
|
url = protomaps_demo_url(),
|
||
|
|
flavor = "light",
|
||
|
|
lang = "en", # Language for labels
|
||
|
|
attribution = "Protomaps", # Attribution text
|
||
|
|
options = protomapsOptions(
|
||
|
|
maxDataZoom = 14, # Max zoom for tile data
|
||
|
|
tileSize = 256, # Tile size in pixels
|
||
|
|
debug = FALSE # Debug mode
|
||
|
|
)
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Acknowledgments
|
||
|
|
|
||
|
|
This package is a wrapper around [protomaps-leaflet](https://github.com/protomaps/protomaps-leaflet), created by [Brandon Liu](https://bdon.org/). The Protomaps project provides an incredible open-source stack for self-hosted vector maps, including the [PMTiles](https://docs.protomaps.com/pmtiles/) single-file tile archive format. Thanks to Brandon for making beautiful, customizable maps accessible to everyone.
|
||
|
|
|
||
|
|
## Links
|
||
|
|
|
||
|
|
- [Protomaps](https://protomaps.com/)
|
||
|
|
- [protomaps-leaflet GitHub](https://github.com/protomaps/protomaps-leaflet)
|
||
|
|
- [PMTiles specification](https://docs.protomaps.com/pmtiles/)
|
||
|
|
- [Brandon Liu](https://bdon.org/)
|