Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

Fun with vector maps

A goofy slippy map of various vector tile data sources. With some fun colours, greetz to Aaron and Mike and Mike and the whole Prettymaps crew.

Sacramento, CASF bay area, CANew Orleans, LABoulder, COAlbuquerque, NMCrater Lake, ORBagdad Cafe, CAHillsboro, KSGalveston Bay, TXCape Cod, MA

Layers used here:

Want to make maps like this? See my vector tiles tutorial.

Tested with Chrome. If the background tiles are pepto-bismol pink instead of gunmetal grey, your browser doesn't support -webkit-filter.

<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0"/>
<title>Leaflet vector tile map of rivers</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.5/leaflet.css" />
<!--[if lte IE 8]>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.5/leaflet.ie.css" />
<![endif]-->
<script src="http://cdn.leafletjs.com/leaflet-0.5/leaflet.js"></script>
<script src="http://www.somebits.com/rivers/lib/leaflet-hash.js"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="TileLayer.d3_geoJSON.js"></script>
<style type="text/css">
html, body { height: 100% }
#map { min-height: 100%; }
body {
margin: 0;
font-family: Helvetica, Arial, sans-serif; font-size: 12px;
overflow: hidden;
background-color: #f00;
}
.leaflet-popup-content-wrapper {
-webkit-border-radius: 5px;
border-radius: 5px;
}
path { stroke-linejoin; round; stroke-linecap: round; fill: none}
path.river { stroke : #24b; }
path.road { stroke: #b42; }
path.water { stroke: #bcf; fill: #abf; }
path.landuse { stroke: #bb2; fill: #cc2; opacity: 0.4 }
path.building { stroke: #f00; fill: #f00; }
img { -webkit-filter: grayscale(100%) brightness(40%) contrast(150%);}
button#demake {
font-size: 12px; padding: 7px 14px;
color: #ffffff; background: #b42;
border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px;
border: 3px solid #ffffff;
position: absolute; top: 20px; right: 20px; z-index: 3;
}
button.animating { background: #444 !important; }
</style>
</head><body>
<div id="map"></div>
<button id="demake">DEMAKE</button>
<script type="text/javascript">
// Construct map, center if no location provided
var map = L.map('map', { minZoom: 10, maxZoom: 13 } );
var hash = new L.Hash(map);
if (!window.location.hash) {
map.setView([39.28, -121.18], 10);
}
// Make the base map; a raster tile relief map from ESRI
var esriRelief = 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}'
var basemap = L.tileLayer(esriRelief, {
attribution: '<a href="http://www.arcgis.com/home/item.html?id=9c5370d0b54f4de1b48a3792d7377ff2">ESRI shaded relief</a>, <a href="http://www.horizon-systems.com/NHDPlus/NHDPlusV2_home.php">NHDPlus v2</a>, OpenStreetMap',
maxZoom: 13
});
basemap.addTo(map);
// Add a fake GeoJSON line to coerce Leaflet into creating the <svg> tag that d3_geoJson needs
new L.geoJson({"type": "LineString","coordinates":[[0,0],[0,0]]}).addTo(map);
// Land use areas from OpenStreetMap
var landColors = {
"farm": 1,
"meadow": 1,
"scrub": 1,
"forest": 1,
"farmyard": 1,
"farmland": 1,
"wood": 1,
"park": 1,
"cemetery": 1,
"golf_course": 1,
"grass": 1,
"nature_reserve": 1,
"pitch": 1,
"common": 1,
"residential": "#ddd",
"industrial": "#b3c",
"commercial": "#b3c",
"retail": "#b3c",
"parking": "#b3c",
"quarry": "#b3c",
"school": "#b3c",
"hospital": "#b3c",
"college": "#b3c",
"university": "#b3c",
}
function rando(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
new L.TileLayer.d3_geoJSON("http://tile.openstreetmap.us/vectiles-land-usages/{z}/{x}/{y}.json", {
class: "landuse",
style: function(d) {
var c = landColors[d.properties.kind];
if (!c) { c = "#fff"; }
if (c == 1) { // random greens
c = "hsl(" + rando(100, 130) + ", " + rando(50,70) + "%, " + rando(30, 50) + "%)";
}
return "fill: " + c;
}
}).addTo(map);
// Rivers from Nelson's vector river tutorial
new L.TileLayer.d3_geoJSON("http://somebits.com:8001/rivers/{z}/{x}/{y}.json", {
class: "river",
style: function(d) { return "stroke-width: " + d.properties.strahler * map.getZoom()/13 + "px"; }
}).addTo(map);
// Water Areas from OpenStreetMap
new L.TileLayer.d3_geoJSON("http://tile.openstreetmap.us/vectiles-water-areas/{z}/{x}/{y}.json", {
class: "water",
style: ""
}).addTo(map);
// Highways from OpenStreetMap
var roadSizes = {
"highway": "5px",
"major_road": "3px",
"minor_road": "1px",
"rail": "0px",
"path": "0.5px"
};
new L.TileLayer.d3_geoJSON("http://tile.openstreetmap.us/vectiles-highroad/{z}/{x}/{y}.json", {
class: "road",
style: function(d) { return "stroke-width: " + roadSizes[d.properties.kind]; }
}).addTo(map);
var animating = false;
var transformParser = /translate\((-?\d+),(-?\d+)\)/;
function step() {
var paths = document.getElementsByTagName("path");
for (var i = 0; i < 3 * Math.floor(Math.sqrt(paths.length-1)); i++) {
var r = rando(0, paths.length-1);
if (!paths[r]) { continue; }
var dx = rando(-5, 5), dy = rando(-5, 5);
var t = paths[r].getAttribute("transform");
if (t) {
var p = transformParser.exec(t);
if (!p) { console.log(t); continue; }
dx += parseInt(p[1]);
dy += parseInt(p[2]);
}
paths[r].setAttribute("transform", "translate(" + dx + "," + dy + ")");
}
if (animating) {
requestAnimationFrame(step);
}
}
function toggleAnimation() {
animating = !animating;
d3.select("button#demake").classed("animating", animating);
if (animating) {
requestAnimationFrame(step);
} else {
d3.selectAll("path").attr("transform", null);
}
}
d3.select("button#demake").on("click", toggleAnimation);
</script>
</body></html>
/* Experimental vector tile layer for Leaflet
* Uses D3 to render GeoJSON; faster than Leaflet's native.
* Originally by Ziggy Jonsson: http://bl.ocks.org/ZJONSSON/5602552
* Reworked by Nelson Minar: http://bl.ocks.org/NelsonMinar/5624141
*
* Todo:
* Make this work even if <svg> isn't in the DOM yet
* Make this work for tile types that aren't FeatureCollection
* Match D3 idioms for .classed(), .style(), etc
* Work on allowing feature popups, etc.
*/
L.TileLayer.d3_geoJSON = L.TileLayer.extend({
onAdd : function(map) {
L.TileLayer.prototype.onAdd.call(this,map);
this._path = d3.geo.path().projection(function(d) {
var point = map.latLngToLayerPoint(new L.LatLng(d[1],d[0]));
return [point.x,point.y];
});
this.on("tileunload",function(d) {
if (d.tile.xhr) d.tile.xhr.abort();
if (d.tile.nodes) d.tile.nodes.remove();
d.tile.nodes = null;
d.tile.xhr = null;
});
},
_loadTile : function(tile,tilePoint) {
var self = this;
this._adjustTilePoint(tilePoint);
if (!tile.nodes && !tile.xhr) {
tile.xhr = d3.json(this.getTileUrl(tilePoint),function(geoJson) {
tile.xhr = null;
tile.nodes = d3.select(map._container).select("svg").append("g");
tile.nodes.selectAll("path")
.data(geoJson.features).enter()
.append("path")
.attr("d", self._path)
.attr("class", self.options.class)
.attr("style", self.options.style);
});
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment