Slippy Maps,
Cartographic Mediums
& Computation

Brandon Liu
Protomaps LLC
Interactive slippy maps are everywhere on the web.
OpenStreetMap; geojson.io
Many special purpose tools for tiled mapmaking on the web are free and open source.
TileMill, Maputnik
Though the tools are specialized, the principles are the same: represent points, lines, and polygons via variables like fill, stroke, color, & texture.

Jacques Bertin, Semiology of Graphics

These principles can be implemented in a physical medium, like ink on paper or woodblock.

National Palace Museum

Cartographic principles can be implemented in a digital medium...

Harvard Laboratory for Computer Graphics and Spatial Analysis

cairographics.org


                    cairo_set_line_width (cr, 0.1);
                    cairo_set_source_rgb (cr, 0, 0, 0);
                    cairo_rectangle (cr, 0.25, 0.25, 0.5, 0.5);
                    cairo_stroke (cr);
                    

Modern computer graphics are low-level and not specific to mapmaking.

Productive computer cartography requires higher level abstractions.

Jacques Bertin, Semiology of Graphics

Are we all done?

A global slippy map demands automated, rules-based styling based on scale.

Proportion: Create a relationship between scale and symbol properties.

Filtering: Select a subset of features.

Repetition: symbolize the same feature more than once.
Labeling: dynamic text placement based on nearby labels.
As the rules grow, we need a higher-level abstraction — A cartographic language.
A small amount of computation gives us expressive power — via Cascadenik, CartoCSS, JSON, YAML...

Digital Equipment Corporation

What would make a language successful?

Affordance: The qualities of an object that makes clear how it can be used.

Ergonomics: Efficiency of use, or how directly the medium can express cartographic ideas.

Re-use
 // CartoCSS
.country-boundary-10m[scalerank<6] {
  line-color: #ffdddd;
  line-width: 1;

  [zoom>=6] {
    line-width: 1.6;
  }

  [zoom>=7] {
    line-width: 2.0;
  }
}
Parameterization
@color_water: #0000ff;
#water-bodies-high[zoom>=15]
{ 
  polygon-fill: @color_water;
}
#water-bodies-low[zoom>=10]
{ 
  polygon-fill: @color_water;
}
Variants of a single style

Stamen Design, OpenStreetMap contributors

Math functions for zoom-based styling
"paint": {
    "line-width": [
        "interpolate",
        ["exponential",1.6],
        ["zoom"],
        14,0,
        14.5,0.5,
        20,12
    ]
}
Direct access to the underlying medium
shaders:
...
float stripesDF (vec2 st) {
    return abs(sin(st.y*PI));
}
float stripes (vec2 st, float width) {
    return aastep(width, stripesDF(st));
}
color.rgb = mix(u_l, color.rgb, gl_FragCoord.x / u_res.x);
color = mix(color,vec4(u_l2,1.0),stripes(st*92.,.5))*1.0;
Mapzen Refill, OpenStreetMap contributors
Observations
  • Practical basemapping can demand hundreds of rules.
  • Familiarity with CSS or JSON is a double-edged sword.
  • More computational power lends more cartographic expressiveness.
Instead of assembling a language from cartographic principles, embed cartography inside an existing programming language.
protomaps.js
  • A cartographic language inside JavaScript.
  • Runs inside the web browser.
  • Targets the Canvas 2D API.
Basics: Variables

let NATURAL_COLOR = "seagreen"
let RULES = [
    {
        dataLayer: "natural",
        symbolizer: new PolygonSymbolizer({
            fill: NATURAL_COLOR
        })
    }
]
                    
Familiar concepts for d3.js users.

        dataLayer: "landuse",
        symbolizer: new PolygonSymbolizer({
            fill:(z:number,f:Feature) => {
                if (f.props.kind === "natural") return "green"
                else return "yellow"
            }
        })
        filter:(z:number,f:Feature) => { 
            return f.props.scalerank < 6
        }
                    
Attributes as Functions

symbolizer: new LineSymbolizer({
    color:"steelblue",
    width: z => {
        return Math.pow(2,z-10)
    }
}),
                    
Define your own symbology, using the underlying 2D drawing API.
class TrianglePopulationSymbolizer {
    place(layout,geom,feature) {
        let a = geom[0][0]
        let bbox = {minX:a.x-8, minY:a.y-8,maxX:a.x+8,maxY:a.y+8}
        let draw = ctx => {
            ctx.fillStyle = "black"
            ctx.beginPath()
            ctx.moveTo(-5,0)
            ctx.lineTo(0,-9)
            ctx.lineTo(5,0)
            ctx.fill()
            ctx.fillText(feature.props.population,8,0)
        }
        return [{anchor:anchor,bboxes:[bbox],draw:draw}]
    }
}
Composition
Composition
Composition
  • Practical to use with Leaflet for basemaps.
  • Consumes popular MVT vector tile format.
  • Free and open source software on GitHub.
    Benefits
    • A complete programming language.
    • Composition, parameterization are powerful cartographic tools.
    • Direct access to the underlying 2D API.
    Drawbacks
    • A complete programming language.
    • Not a useful intermediate form for "buttons and sliders" styling.
    • Suited only for CPU rendering, not GPU-accelerated maps.
    Conclusion