Last active
February 5, 2019 22:03
-
-
Save Andrew-Reid/7f1d1ae9cb8b9673791d15253bec7069 to your computer and use it in GitHub Desktop.
d3 geographic tile sets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Andrew Reid 2018 | |
(function (global, factory) { | |
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | |
typeof define === 'function' && define.amd ? define(['exports'], factory) : | |
(factory((global.d3 = global.d3 || {}),global.d3)); | |
}(this, function (exports) { 'use strict'; | |
function geoTile() { | |
// Basic Constants | |
const tau = Math.PI * 2; | |
var lim = 85.051133; | |
// Map Properties: | |
var w = 960; | |
var h = 500; | |
// Projection Values: | |
var pk = w/tau; // projection scale k - default reference for tile placement. | |
var pc = [0,0] // projection geographic center | |
var pr = 0; // central longitude for rotation. | |
// Zoom Transform Values: | |
var tk = 1; // zoom transform scale k | |
var tx = w/2; // zoom transform translate x | |
var ty = h/2; // zoom transform translate y | |
// Offsets for other projections: | |
var ox = 0; | |
var oy = 0; | |
// The Projection: | |
var p = d3.geoMercator() | |
.scale(pk) | |
.center(pc); | |
// Tile wrapping and zoom limits: | |
var z0 = 4; | |
var z1 = 13; | |
var extent = {left:-179.99999,top:lim,right:179.9999,bottom:-lim}; | |
var wrap = false; | |
// Tile ordering: | |
var xyz = true; | |
function geoTile(_) { | |
return p(_); | |
} | |
// Tile source & attribution | |
geoTile.xyz = function(_) { | |
return arguments.length ? (xyz = _, geoTile): xyz; | |
} | |
var source = function(d) { | |
return "http://" + "abc"[d.y % 3] + ".tile.openstreetmap.org/" + d.z + "/" + d.x + "/" + d.y + ".png"; | |
} | |
var a = "Tiles © OpenStreetMap contributors"; | |
// General Methods | |
geoTile.width = function(_) { | |
return arguments.length ? (w = _, geoTile) : w; | |
} | |
geoTile.height = function(_) { | |
return arguments.length ? (h = _, geoTile) : h; | |
} | |
geoTile.size = function(_) { | |
if(arguments.length) { | |
(_ instanceof d3.selection) ? (w = _.attr("width"), h = _.attr("height")) : (w = _[0], h = _[1]); | |
return geoTile; | |
} | |
else return [w,h] | |
} | |
geoTile.source = function(_) { | |
return arguments.length ? (source = _, geoTile) : source; | |
} | |
geoTile.projection = function(_) { | |
return arguments.length ? (p = _, geoTile) : p; | |
} | |
geoTile.attribution = function(_) { | |
return arguments.length ? (a = _, geoTile) : a; | |
} | |
geoTile.wrap = function(_) { | |
return arguments.length ? (wrap = _, geoTile) : wrap; | |
} | |
geoTile.limit = function(_) { | |
return arguments.length ? (lim = _, geoTile) : lim; | |
} | |
// Projection methods: | |
geoTile.invert = function(_) { | |
return p.invert(_); | |
} | |
geoTile.center = function(_) { | |
return arguments.length ? (pc = _, p.center(pc), geoTile): pc; | |
} | |
geoTile.scale = function(_) { | |
return arguments.length ? (/*ox = ox/pk*_, oy = oy/pk*_, tx = tx/pk*_, ty = ty/pk*_,*/ pk = _, p.scale(pk), geoTile) : pk; // Scale modifies translate & offset. | |
} | |
geoTile.rotate = function(_) { | |
return arguments.length ? (pr = _, p.rotate([pr,0]), geoTile) : pr; | |
} | |
geoTile.fit = function(_) { | |
return arguments.length ? (p.fitSize([w,h],_),tx = p.translate()[0],ty = p.translate()[1],pk = p.scale(), geoTile) : "n/a"; | |
} | |
geoTile.fitExtent = function(e,f) { | |
return arguments.length > 1 ? (p.fitExtent(e,f),pk = p.scale(),tx = p.translate()[0],ty = p.translate()[1], geoTile) : "n/a"; | |
} | |
geoTile.fitMargin = function(m,f) { | |
return arguments.length > 1 ? (p.fitExtent([[m,m],[w-m,h-m]],f), tx = p.translate()[0],ty = p.translate()[1],pk = p.scale(), geoTile) : "n/a"; | |
} | |
geoTile.offset = function(_) { | |
return arguments.length ? (ox = _[0], oy = _[1], geoTile): [ox,oy]; | |
} | |
// Zoom Methods: | |
geoTile.zoomScale = function(_) { | |
return arguments.length ? (tk = _, p.scale(pk*tk), geoTile) : tk; | |
} | |
geoTile.zoomTranslate = function(_) { | |
return arguments.length ? (tx = _[0], ty = _[1], p.translate([tx, ty]), geoTile): [tx,ty] | |
} | |
geoTile.zoomIdentity = function() { | |
return d3.zoomIdentity.translate(tx,ty).scale(tk).translate(0,0); | |
} | |
geoTile.zoomTransform = function(t) { | |
tx = t.x, ty = t.y, tk = t.k; p.translate([tx,ty]); p.scale(pk*tk); return geoTile; | |
} | |
// Convert between zoom k and tile depth. | |
geoTile.tileDepth = function(z) { | |
if(arguments.length) { | |
tk = Math.pow(Math.E, ((z + 8) * Math.LN2)) / pk / tau; | |
} | |
else { | |
var size = pk * tk * tau; | |
var z = Math.max(Math.log(size) / Math.LN2 - 8, 0); | |
return Math.round(z); | |
} | |
} | |
// Zoom extent methods: | |
geoTile.zoomScaleExtent = function(_) { | |
if (arguments.length) { | |
z0 = _[0]; | |
z1 = _[1]; | |
return geoTile; | |
} | |
else { | |
var size = pk * tk * tau; | |
var z = Math.max(Math.log(size) / Math.LN2 - 8, 0); | |
var max = Math.pow(2,z1)/Math.pow(2,z); | |
var min = Math.pow(2,z0)/Math.pow(2,z); | |
return [min,max]; | |
} | |
} | |
geoTile.zoomTranslateExtent = function(_) { | |
if (arguments.length) { | |
extent.left = _[0][0]; | |
extent.top = _[0][1]; | |
extent.right = _[1][0]; | |
extent.bottom = _[1][1]; | |
return geoTile; | |
} | |
else { | |
var x0 = p([extent.left-pr,extent.top])[0] - tx; | |
var y0 = p([extent.left-pr,extent.top])[1] - ty; | |
var x1 = p([extent.right-pr,extent.bottom])[0] - tx; | |
var y1 = p([extent.right-pr,extent.bottom])[1] - ty; | |
return [[x0,y0],[x1,y1]]; | |
} | |
} | |
geoTile.zoomTranslateConstrain = function() { | |
extent.left = p.invert([0,0])[0]; | |
extent.top = p.invert([0,0])[1]; | |
extent.right = p.invert([w,h])[0]; | |
extent.bottom = p.invert([w,h])[1]; | |
var x0 = p([extent.left-pr,extent.top])[0] - tx; | |
var y0 = p([extent.left-pr,extent.top])[1] - ty; | |
var x1 = p([extent.right-pr,extent.bottom])[0] - tx; | |
var y1 = p([extent.right-pr,extent.bottom])[1] - ty; | |
return [[x0,y0],[x1,y1]]; | |
} | |
// Tile Methods: | |
// Calculate Tiles: | |
geoTile.tiles = function() { | |
var size = pk * tk * tau; | |
var z = Math.max(Math.log(size) / Math.LN2 - 8, 0); // z, assuming image size of 256 (2^8). | |
var s = Math.pow(2, z - Math.round(z) + 8); | |
var y0 = p([-180,lim])[1] - oy * tk * pk/w*tau; | |
var x0 = p([-180,lim])[0] - ox * tk * pk/w*tau; | |
var set = []; | |
var cStart = wrap ? Math.floor((0 - x0) / s) : Math.max(0, Math.floor((0 - x0) / s)); | |
var cEnd = Math.max(0, Math.ceil((w - x0) / s)); | |
var rStart = Math.max(0,Math.floor((0 - y0) / s)); | |
var rEnd = Math.max(0, Math.ceil((h - y0) / s)); | |
for(var i = cStart; i < cEnd; i++) { | |
for(var j = rStart; j < rEnd; j++) { | |
var x = i; | |
if (wrap) { | |
var k = Math.pow(2,Math.round(z)); | |
x = (i+k)%k; | |
} | |
if(Math.pow(z,2) > j && Math.pow(z,2) > x) set.push({x:x,y:j,z:Math.round(z),tx:i,ty:j, id:i+"-"+j+"-"+z}) | |
} | |
} | |
if(!xyz) { | |
set.forEach(function(d) { | |
d.y = (Math.pow(2, d.z) - d.y - 1) | |
}) | |
} | |
set.translate = [x0 / s, y0 / s]; | |
set.scale = s; | |
return set; | |
} | |
// Assign Tiles to a Selection: | |
geoTile.tile = function(g) { | |
var set = geoTile.tiles(); | |
var images = g.attr("transform", stringify(set.scale, set.translate)) | |
.selectAll("image") | |
.data(set, function(d) { return d.id; }) | |
images.exit().remove(); | |
images.enter().append("image").merge(images) | |
.attr("xlink:href", source ) | |
.attr("x", function(d) { return d.tx * 256; }) | |
.attr("y", function(d) { return d.ty * 256; }) | |
.attr("width", 256) | |
.attr("height", 256); | |
} | |
// Draw on a canvas: | |
geoTile.canvas = function(context) { | |
var set = geoTile.tiles(); | |
var k = set.scale / 256, r = set.scale % 1 ? Number : Math.round; | |
var ox = r(set.translate[0] * set.scale); | |
var oy = r(set.translate[1] * set.scale); | |
set.forEach(function(d) { | |
var tile = new Image(); | |
tile.src = source(d); // can also be a remote URL e.g. http:// | |
tile.onload = function() { | |
context.drawImage(tile,d.tx*256*k+ox,d.ty*256*k+oy,256*k,256*k); | |
}; | |
}) | |
} | |
// Helper stringify | |
function stringify(scale, translate) { | |
var k = scale / 256, r = scale % 1 ? Number : Math.round; | |
return "translate(" + r(translate[0] * scale) + "," + r(translate[1] * scale) + ") scale(" + k + ")"; | |
} | |
// To break out in the future, at least use switch ?: | |
geoTile.tileSet = function(_) { | |
var m = function() { | |
ox = 0; | |
oy = 0; | |
p = d3.geoMercator().scale(pk).translate([0,0]); | |
lim = 85.05113; | |
xyz = true; | |
} | |
// arctic | |
var a0 = function(lon) { | |
lim = 90; | |
p = d3.geoAzimuthalEqualArea().rotate([lon,-90]) | |
ox = w/2 + (960-w)/2; | |
oy = h/2 + (960-h)/2; | |
xyz = true; | |
} | |
var sets = { | |
// CARTO DB //*********************************************************************** | |
"CartoDB_Positron" : function() { | |
m(), a = "© OpenStreetMap © CartoDB"; | |
source = function(d) { | |
return "https://cartodb-basemaps-b.global.ssl.fastly.net/light_all/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"CartoDB_PositronNoLabels" : function() { | |
m(), a = "© OpenStreetMap © CartoDB"; | |
source = function(d) { | |
return "https://cartodb-basemaps-b.global.ssl.fastly.net/light_nolabels/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"CartoDB_PositronOnlyLabels" : function() { | |
m(), a = "© OpenStreetMap © CartoDB"; | |
source = function(d) { | |
return "https://cartodb-basemaps-b.global.ssl.fastly.net/light_only_labels/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"CartoDB_DarkMatter" : function() { | |
m(), a = "© OpenStreetMap © CartoDB"; | |
source = function(d) { | |
return "https://cartodb-basemaps-b.global.ssl.fastly.net/dark_all/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"CartoDB_DarkMatterNoLabels" : function() { | |
m(), a = "© OpenStreetMap © CartoDB"; | |
source = function(d) { | |
return "https://cartodb-basemaps-b.global.ssl.fastly.net/dark_nolabels/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"CartoDB_DarkMatterOnlyLabels" : function() { | |
m(), a = "© OpenStreetMap © CartoDB"; | |
source = function(d) { | |
return "https://cartodb-basemaps-b.global.ssl.fastly.net/dark_only_labels/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"CartoDB_Voyager" : function() { | |
m(), a = "© OpenStreetMap © CartoDB"; | |
source = function(d) { | |
return "https://cartodb-basemaps-b.global.ssl.fastly.net/rastertiles/voyager/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
// ESRI //*********************************************************************** | |
"ESRI_WorldTerrain" : function() { | |
m(), a = "Tiles © Esri - Source: USGS, Esri, TANA, DeLorme, and NPS" | |
source = function(d) { | |
return "https://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/"+d.z+"/"+d.y+"/"+d.x+".png"; | |
} | |
}, | |
"ESRI_WorldShadedRelief" : function() { | |
m(), a = "Tiles © Esri - Source: Esri"; | |
source = function(d) { | |
return "https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/"+d.z+"/"+d.y+"/"+d.x+".png"; | |
} | |
}, | |
"ESRI_WorldPhysical" : function() { | |
m(), a = "Tiles © Esri - Source: US National Park Service"; | |
source = function(d) { | |
return "https://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer/tile/"+d.z+"/"+d.y+"/"+d.x+".png"; | |
} | |
}, | |
"ESRI_WorldStreetMap" : function() { | |
m(), a = "Tiles © Esri - Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom"; | |
source = function(d) { | |
return "https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/"+d.z+"/"+d.y+"/"+d.x+".png"; | |
} | |
}, | |
"ESRI_WorldTopoMap": function() { | |
m(), a = "Tiles © Esri - Source: Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community"; | |
source = function(d) { | |
return "https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/"+d.z+"/"+d.y+"/"+d.x+".png"; | |
} | |
}, | |
"ESRI_WorldImagery": function() { | |
m(), a = "Tiles © Esri - Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"; | |
source = function(d) { | |
return "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/"+d.z+"/"+d.y+"/"+d.x+".png"; | |
} | |
}, | |
"ESRI_OceanBasemap": function() { | |
m(), a = "Tiles © Esri - Source: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri"; | |
source = function(d) { | |
return "https://server.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/"+d.z+"/"+d.y+"/"+d.x+".png"; | |
} | |
}, | |
"ESRI_NGWorld" : function() { | |
m(), a = "Tiles © Esri - Source: National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC"; | |
source = function(d) { | |
return "https://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/"+d.z+"/"+d.y+"/"+d.x+".png"; | |
} | |
}, | |
"ESRI_Gray": function() { | |
m(), a = "Tiles © Esri - Source: Esri, DeLorme, NAVTEQ"; | |
source = function(d) { | |
return "https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/"+d.z+"/"+d.y+"/"+d.x+".png"; | |
} | |
}, | |
// Open Street Map // ********************************************************************* | |
"OSM_Topo" : function() { | |
m(), a = "Tiles © OpenStreetMap contributors"; | |
source = function(d) { | |
return "https://tile.opentopomap.org/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"OSM" : function() { | |
m(), a = "Tiles © OpenStreetMap contributors"; | |
source = function(d) { | |
return "https://tile.opentopomap.org/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
// STAMEN // ************************************************************************** | |
"Stamen_Toner" : function() { | |
m(), a = "Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL." | |
source = function(d) { | |
return "https://stamen-tiles.a.ssl.fastly.net/toner/" + d.z + "/" + d.x + "/" + d.y + ".png"; | |
} | |
}, | |
"Stamen_TonerBackground": function() { | |
m(), a = "Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL." | |
source = function(d) { | |
return "https://stamen-tiles.a.ssl.fastly.net/toner-background/" + d.z + "/" + d.x + "/" + d.y + ".png"; | |
} | |
}, | |
"Stamen_TonerLines": function() { | |
m(), a = "Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL." | |
source = function(d) { | |
return "https://stamen-tiles.a.ssl.fastly.net/toner-lines/" + d.z + "/" + d.x + "/" + d.y + ".png"; | |
} | |
}, | |
"Stamen_TonerLite": function() { | |
m(), a = "Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL." | |
source = function(d) { | |
return "https://stamen-tiles.a.ssl.fastly.net/toner-lite/" + d.z + "/" + d.x + "/" + d.y + ".png"; | |
} | |
}, | |
"Stamen_Terrain" : function() { | |
m(), a = "Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL." | |
source = function(d) { | |
return "https://stamen-tiles.a.ssl.fastly.net/terrain/" + d.z + "/" + d.x + "/" + d.y + ".png"; | |
} | |
}, | |
"Stamen_TerrainBackground" : function() { | |
m(), a = "Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL." | |
source = function(d) { | |
return "https://stamen-tiles.a.ssl.fastly.net/terrain-background/" + d.z + "/" + d.x + "/" + d.y + ".png"; | |
} | |
}, | |
"Stamen_TerrainLines": function() { | |
m(), a = "Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL." | |
source = function(d) { | |
return "https://stamen-tiles.a.ssl.fastly.net/terrain-lines/" + d.z + "/" + d.x + "/" + d.y + ".png"; | |
} | |
}, | |
"Stamen_Watercolor": function() { | |
m(); a = "Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under CC BY SA." | |
source = function(d) { | |
return "https://stamen-tiles.a.ssl.fastly.net/watercolor/" + d.z + "/" + d.x + "/" + d.y + ".png"; | |
} | |
}, | |
// Arctic Connect | |
"ArcticConnect_180": function() { | |
a0(180); a = "Map © ArcticConnect. Data © OpenStreetMap contributors", | |
source = function(d) { | |
return "http://a.tiles.arcticconnect.ca/osm_3571/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"ArcticConnect_150w": function() { | |
a0(150); a = "Map © ArcticConnect. Data © OpenStreetMap contributors", | |
source = function(d) { | |
return "http://a.tiles.arcticconnect.ca/osm_3572/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"ArcticConnect_100w": function() { | |
a0(100); a = "Map © ArcticConnect. Data © OpenStreetMap contributors", | |
source = function(d) { | |
return "http://a.tiles.arcticconnect.ca/osm_3573/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"ArcticConnect_40w": function() { | |
a0(40); a = "Map © ArcticConnect. Data © OpenStreetMap contributors", | |
source = function(d) { | |
return "http://a.tiles.arcticconnect.ca/osm_3574/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"ArcticConnect_10e": function() { | |
a0(-10); a = "Map © ArcticConnect. Data © OpenStreetMap contributors", | |
source = function(d) { | |
return "http://a.tiles.arcticconnect.ca/osm_3575/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
}, | |
"ArcticConnect_90e": function() { | |
a0(-90); a = "Map © ArcticConnect. Data © OpenStreetMap contributors", | |
source = function(d) { | |
return "http://a.tiles.arcticconnect.ca/osm_3576/"+d.z+"/"+d.x+"/"+d.y+".png"; | |
} | |
} | |
} // close sets. | |
if (_ == undefined) { | |
return Object.keys(sets); | |
} | |
else if(typeof _ == "function") { | |
source = _; return geoTile; | |
} | |
else if (sets[_]) { | |
sets[_](); return geoTile; | |
} | |
else { | |
console.log("Tileset not recognized, using OSM"); | |
sets.OSM(); | |
return geoTile; | |
} | |
return geoTile; | |
} | |
return geoTile; | |
} | |
exports.geoSlippy = geoTile; | |
Object.defineProperty(exports, '__esModule', { value: true }); | |
})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Further refinement of my [d3-slippy module](https://github.com/Andrew-Reid/d3-slippy/blob/master/README.md) to display geographic tiles/web map tiles. | |
This block runs through all the built in tilesets that can be accessed by string. I'll be changing how the tilesets are represented, but for the moment this serves as a test and demonstration of the various tilesets out there. There appear to be a few issues with some of the tilesets at certain zoom levels in certain areas, still a few kinks to sort out. | |
I'm not quite happy with the module's structure yet, but getting closer. Key challenges are implmenting clean non-cylindrical tile sets, this is causing some grief in terms of positioning. | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<svg width="960" height="500"></svg> | |
<script src="https://d3js.org/d3.v4.js"></script> | |
<script src="https://unpkg.com/topojson-client@3"></script> | |
<script src="d3-slippy.js"></script> | |
<script> | |
d3.json("https://unpkg.com/world-atlas@1/world/110m.json", function(error, world) { | |
if (error) throw error; | |
// svg | |
var svg = d3.select("svg"); | |
// Get list of tilesets: | |
var tilesets = d3.geoSlippy().tileSet(); | |
// Drop down menu for tilesets: | |
var select = d3.select("body") | |
.append("select") | |
.attr("style","position: absolute; top: 0; left: 0; z-index: 0"); | |
var options = select.selectAll("option") | |
.data(tilesets) | |
.enter() | |
.append("option") | |
.text(function(d) { return d; }) | |
.attr("style","position: absolute; top: 10px; left 10px; z-index: 999"); | |
// Set up slippy map initially: | |
var slippy = d3.geoSlippy() | |
.size(svg) | |
.tileSet("CartoDB_Positron") | |
.wrap(true); | |
// Set up zoom: | |
var zoom = d3.zoom() | |
.on("zoom",zoomed) | |
// Create g elements for tiles and vectors: | |
var tiles = svg.append("g"); | |
var vector = svg.append("g"); | |
// Append attribution: | |
var text = svg.append("text") | |
.attr("x", 10) | |
.attr("y", 490); | |
// Add features: | |
var path = d3.geoPath().projection(slippy.projection()); | |
var features = vector.selectAll("path") | |
.data(topojson.feature(world,world.objects.land).features) | |
.enter() | |
.append("path") | |
.attr("fill","none") | |
.attr("stroke","black") | |
.attr("stroke-width",1) | |
svg | |
.call(zoom) | |
.call(zoom.transform, slippy.zoomIdentity()); | |
// Apply zoom | |
function zoomed() { | |
// Update projection. | |
slippy.zoomTransform(d3.event.transform) | |
// Update raster tiles: | |
tiles.call(slippy.tile); | |
path = d3.geoPath().projection(slippy.projection()); | |
features.attr("d",path); | |
text.text(slippy.attribution()); | |
} | |
// Cycle through tilesets: | |
setInterval(showcase, 16000); | |
var index = 0; | |
function showcase( ) { | |
index++; | |
var tileset = options | |
.property("selected",false) | |
.filter(function(d,i) { return i == index%tilesets.length }) | |
.property("selected",true) | |
.text(); | |
slippy.tileSet(tileset); | |
svg.call(zoom.transform, slippy.zoomIdentity()); | |
} | |
select.on("change", function(d) { | |
// assign new tileset and redraw: | |
slippy.tileSet(this.value); | |
svg.call(zoom.transform, slippy.zoomIdentity()); | |
}) | |
}); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment