block by wboykinm 9518619

vectors from tilestache test

Full Screen

Fun with vector maps

Quick TopoJSON vector tile demo map, derived from Nelson Minar’s awesome work.

A goofy slippy map of various vector tile data sources.

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.

index.html

<!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="//cdn.leafletjs.com/leaflet-0.5/leaflet.css" />
<!--[if lte IE 8]>
    <link rel="stylesheet" href="//cdn.leafletjs.com/leaflet-0.5/leaflet.ie.css" />
<![endif]-->
<script src="//cdn.leafletjs.com/leaflet-0.5/leaflet.js"></script>
<script src="//www.somebits.com/rivers/lib/leaflet-hash.js"></script>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script src="TileLayer.d3_topoJSON.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; stroke-width: 0; fill: #cc2; opacity: 0.4 }
path.building { stroke: #f00; stroke-width: 0; fill: #f00; }
img { -webkit-filter: grayscale(100%) brightness(40%) contrast(150%);}

</style>

</head><body>

<div id="map"></div>

<script type="text/javascript">

// Construct map, center if no location provided
var map = L.map('map', { minZoom: 7, maxZoom: 15 } );

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 = '//server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}'
var basemap = L.tileLayer(esriRelief, {
        attribution: '<a href="//www.arcgis.com/home/item.html?id=9c5370d0b54f4de1b48a3792d7377ff2">ESRI shaded relief</a>, <a href="//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_topoJSON("//tile.openstreetmap.us/vectiles-land-usages/{z}/{x}/{y}.topojson", {
  class: "landuse",
  layerName: "vectile",
  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);

// Water Areas from OpenStreetMap
new L.TileLayer.d3_topoJSON("//tile.openstreetmap.us/vectiles-water-areas/{z}/{x}/{y}.topojson", {
  class: "water",
  layerName: "vectile",
  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_topoJSON("//tile.openstreetmap.us/vectiles-highroad/{z}/{x}/{y}.topojson", {
  class: "road",
  layerName: "vectile",
  style: function(d) { return "stroke-width: " + roadSizes[d.properties.kind]; }
}).addTo(map);


</script>
</body></html>

TileLayer.d3_topoJSON.js

/* Experimental vector tile layer for Leaflet
 * Uses D3 to render TopoJSON. Derived from a GeoJSON thing that was
 * 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_topoJSON =  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(error, tjData) {
                if (error) {
                    console.log(error);
                } else {
                    var geoJson = topojson.feature(tjData, tjData.objects[self.options.layerName]);
                    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);
                }
            });
        }
    }
});