Crosshatch
A Mapzen classic. Lower Manhattan as a pen-and-ink drawing, after the crosshatch style Patricio Gonzalez Vivo wrote for Mapzen’s Tangram in 2015. Ground, parks and water use Tangram’s own hatch and paper textures. Buildings are hatched extrusions, and labels are set in a serif italic.
The static tier
Section titled “The static tier”This page is the static tier of the classic. It is one YAML document,
plain style spec from top to bottom: no page JavaScript, no shader, no
runtime beyond MapLibre itself. As a result the document is its own export:
mlym emit --strict compiles it with zero warnings. Nothing is
declared absent, nothing is lossy, and the emitted style.json renders the
same map in vanilla MapLibre.
What it reproduces exactly, and what it approximates:
- Exact: ground, landuse and water. Tangram draws these unlit: a
hatch texture repeated per tile, mixed from ink to paper, and a
crumpled-paper normal map under fixed lights. Each one bakes to a plain
image, and a
*-patternrepeats that image just as Tangram did. - Approximate: buildings. In Tangram the buildings are the lit part,
with stroke density following the light on each face. Here every face
gets the same light pen hatch, and the document’s
light:shades the faces. Tangram’s height exaggeration (×2.25 at z13 down to ×1 at z18) is kept, as oneinterpolate.
What the effect adds
Section titled “What the effect adds”The tonal hatching on building faces is the job of the experimental
tonal-hatch effect: a MapLibre custom layer that
keeps this document as its fallback and upgrades the buildings layer in
place. Where the effect isn’t loaded, and in every exported style, this
page’s look is what renders. The Mapzen classics page shows
both side by side.
The complete document
Section titled “The complete document”# yaml-language-server: $schema=https://docs.maplibre-yaml.org/schema/latest/map.schema.json# Mapzen classic: crosshatch — after Tangram's crosshatch style# (tangrams/tangram-sandbox styles/crosshatch.yaml, @patriciogv 2015, MIT).## The static tier: every layer is plain style spec, so this document is its# own export — `mlym emit --strict` compiles it with nothing lost. Ground,# landuse and water reproduce Tangram exactly (Tangram draws them unlit:# tile-repeated hatch textures and a pre-lit crumpled-paper water, baked by# `mlym bake crosshatch`). Buildings are the one lit layer: here every face# gets the same light pen hatch, shaded by `light:`. Tangram's tonal# hatching — stroke density following light per face — is the runtime# effect's job, not this document's.## Image and glyph URLs are absolute so emit can fetch them into the sprite# and the exported style can find its fonts.type: mapid: crosshatchconfig: center: [-74.0118, 40.7075] zoom: 15.9 pitch: 60 bearing: 29 # An inline base style that carries only the self-hosted serif italic # glyphs (Libre Baskerville Italic, OFL — Tangram labelled in Baskerville # italic). The document owns every layer. mapStyle: version: 8 glyphs: "https://docs.maplibre-yaml.org/fonts/{fontstack}/{range}.pbf" sources: {} layers: []# Building shading. Tangram's lights are fixed to the world, so this one is# anchored to the map: from the south-west (239°), 30° from overhead — the# same light MapLibre's default gives at this camera's bearing, now staying# put as the map turns. Tune `position`/`intensity` to change face contrast.light: anchor: map position: [1.15, 239, 30] intensity: 0.5images: earth: { url: "https://docs.maplibre-yaml.org/classics/crosshatch/earth.png", pixelRatio: 2 } landuse: { url: "https://docs.maplibre-yaml.org/classics/crosshatch/landuse.png", pixelRatio: 2 } water: { url: "https://docs.maplibre-yaml.org/classics/crosshatch/water.png", pixelRatio: 1 } building-hatch: { url: "https://docs.maplibre-yaml.org/classics/crosshatch/building-fallback.png", pixelRatio: 4 }sources: omt: type: vector url: "https://tiles.openfreemap.org/planet" attribution: '<a href="https://openfreemap.org">OpenFreeMap</a> © <a href="https://www.openmaptiles.org/">OpenMapTiles</a> Data from <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'layers: # Tangram `earth`: paper, hatched with hatch_0 repeated per tile. - id: earth type: background paint: background-pattern: earth # Tangram `landuse`: the denser hatch_2. - id: parks type: fill source: omt source-layer: park paint: fill-pattern: landuse # A data-driven pattern: open sand reads as bare ground (the light earth # hatch); vegetation and farmland take the dense landuse hatch. - id: landcover type: fill source: omt source-layer: landcover filter: ["match", ["get", "class"], ["grass", "wood", "farmland", "sand"], true, false] paint: fill-pattern: ["match", ["get", "class"], "sand", "earth", "landuse"] # Tangram `water`: #343434 lit through a crumpled-paper normal map. - id: water type: fill source: omt source-layer: water paint: fill-pattern: water - id: roads type: line source: omt source-layer: transportation filter: ["match", ["get", "class"], ["motorway", "trunk", "primary", "secondary", "tertiary", "minor", "service"], true, false] layout: line-join: round line-cap: round paint: line-color: "#343434" line-width: - interpolate - ["exponential", 1.6] - ["zoom"] - 13 - ["match", ["get", "class"], ["motorway", "trunk"], 2, 0.6] - 18 - ["match", ["get", "class"], ["motorway", "trunk"], 16, ["primary", "secondary"], 10, 6] - id: buildings type: fill-extrusion source: omt source-layer: building minzoom: 13 paint: fill-extrusion-pattern: building-hatch # Tangram exaggerates height by max(1, 0.5 + (1 - zoom/20) * 5) — linear # in zoom (2.25x at z13 → 1x at z18), so one interpolate reproduces it. fill-extrusion-height: - interpolate - ["linear"] - ["zoom"] - 13 - ["*", ["coalesce", ["get", "render_height"], 10], 2.25] - 18 - ["coalesce", ["get", "render_height"], 10] fill-extrusion-base: ["coalesce", ["get", "render_min_height"], 0] fill-extrusion-vertical-gradient: true - id: place-labels type: symbol source: omt source-layer: place filter: ["match", ["get", "class"], ["neighbourhood", "suburb", "quarter"], true, false] layout: text-field: ["coalesce", ["get", "name:en"], ["get", "name"]] text-font: ["Libre Baskerville Italic"] text-size: 22 text-max-width: 7 text-pitch-alignment: viewport paint: text-color: "#000000" text-halo-color: "#f9f3e3" text-halo-width: 3.5What to notice
Section titled “What to notice”images:+*-pattern. Every texture is a declared image. On export the textures are fetched into the document sprite and the references are rewritten tomlym:<name>. Their URLs are absolute, because emit fetches at compile time and has no page to resolve a relative path against.- A data-driven pattern.
landcoverselects its texture with["match", ["get", "class"], "sand", "earth", "landuse"]. Both outputs are rewritten on export, with no warning. light:. This is the style-spec light, anchored to the map so the shading stays put as the map turns, the way Tangram’s world-fixed lights do. Changepositionorintensityto retune the contrast between faces. See Light.- Self-hosted glyphs. The inline base style exists only to point
glyphsat this site’s Libre Baskerville Italic ranges. That is an OFL font standing in for Tangram’s Baskerville italic. - Reproducible textures.
mlym bake crosshatch --out <dir>regenerates all four PNGs from Tangram’s MIT-licensed source textures. Seebake.
Map data © OpenStreetMap contributors, served by OpenFreeMap. Textures derived from Tangram (MIT, © Mapzen). Font: Libre Baskerville (OFL).