327 lines
9.3 KiB
Text
327 lines
9.3 KiB
Text
---
|
|
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
|