Display global views using a Hammer projection with d3.js, everything else in a web mercator with Leaflet.js.
Edit: now with additional smoke and mirrors.
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> | |
| <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.6.3/leaflet.css" /> | |
| <script src="http://cdn.leafletjs.com/leaflet-0.6.3/leaflet.js"></script> | |
| <script src="http://d3js.org/d3.v3.min.js"></script> | |
| <script src="http://d3js.org/d3.geo.projection.v0.min.js" charset="utf-8"></script> | |
| <script src="http://d3js.org/topojson.v1.min.js"></script> | |
| <style type="text/css"> | |
| .countries { | |
| stroke: #000; | |
| fill: #FEFBFB; | |
| stroke-width: 0.5; | |
| } | |
| #map { | |
| height:500px; | |
| width:100%; | |
| } | |
| #zoom { | |
| position: absolute; | |
| margin-top:10px; | |
| margin-left:10px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="zoom" class="leaflet-control-zoom leaflet-bar leaflet-control"> | |
| <a class="leaflet-control-zoom-in" href="#" title="Zoom in" id="zoomIn">+</a> | |
| <a class="leaflet-control-zoom-out leaflet-disabled" href="#" title="Zoom out">-</a> | |
| </div> | |
| <div id="svgMap"></div> | |
| <div id="map"></div> | |
| <script type="text/javascript"> | |
| var map = new L.Map('map', { | |
| center: new L.LatLng(7, 0), | |
| zoom: 2, | |
| maxZoom:10, | |
| minZoom: 2 | |
| }); | |
| var attrib = 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> — Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors'; | |
| var stamen = new L.TileLayer('http://{s}.tile.stamen.com/toner-background/{z}/{x}/{y}.png', {attribution: attrib}).addTo(map); | |
| map.on('moveend', function(d) { | |
| var zoom = map.getZoom(); | |
| if (zoom < 3) { | |
| d3.select("#map").style("width", 0); | |
| d3.select("#svgMap").style("display", "block"); | |
| d3.selectAll("path").attr("d", path); | |
| } | |
| }); | |
| d3.select("#map").style("width", 0); | |
| var width = window.innerWidth, | |
| height = 500; | |
| var projection = d3.geo.hammer() | |
| .scale(165) | |
| .translate([width / 2, height / 2]) | |
| .precision(.1); | |
| var mercator = d3.geo.mercator() | |
| .scale(165) | |
| .precision(.1) | |
| .translate([width / 2, height / 2]); | |
| var path = d3.geo.path() | |
| .projection(projection); | |
| function changeMaps(mouse) { | |
| var coords = mouse, | |
| projected = mercator.invert(coords); | |
| d3.select("#svgMap").style("display", "none"); | |
| d3.select("#map").style("width", "100%"); | |
| map.invalidateSize(); | |
| map.setView([parseInt(projected[1]), parseInt(projected[0])], 3, {animate:false}); | |
| } | |
| var zoom = d3.behavior.zoom() | |
| .on("zoom",function() { | |
| if (d3.event.sourceEvent.wheelDelta > 0) { | |
| changeMaps(d3.mouse(this)); | |
| } | |
| }); | |
| var svg = d3.select("#svgMap").append("svg") | |
| .attr("width", width) | |
| .attr("height", height) | |
| .call(zoom) | |
| .on("click", function() { | |
| changeMaps(d3.mouse(this)); | |
| }); | |
| svg.append("defs").append("path") | |
| .datum({type: "Sphere"}) | |
| .attr("id", "sphere") | |
| .attr("d", path); | |
| svg.append("use") | |
| .attr("class", "fill") | |
| .attr("xlink:href", "#sphere"); | |
| d3.json("countries.json", function(error, data) { | |
| svg.append("path") | |
| .datum(topojson.feature(data, data.objects.countries)) | |
| .attr("class", "countries") | |
| .attr("d", path); | |
| }); | |
| d3.select("#zoomIn").on("click", function() { | |
| d3.event.stopPropagation(); | |
| d3.select("#svgMap").style("display", "none"); | |
| d3.select("#map").style("width", "100%"); | |
| map.invalidateSize(); | |
| map.setView([7,0], 3, {animate:false}); | |
| }); | |
| </script> | |
| </body> | |
| </html> |