--- 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