|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
body { |
|
width:1000px; |
|
margin: 50px auto 0px auto; |
|
} |
|
|
|
#container { |
|
position: relative; |
|
overflow: hidden; |
|
background: #555; |
|
} |
|
|
|
#map{ |
|
width:100%; |
|
height:100%; |
|
} |
|
|
|
#points{ |
|
width:100%; |
|
height:100%; |
|
position:relative; |
|
z-index:100; |
|
} |
|
|
|
.layer { |
|
position: absolute; |
|
} |
|
|
|
.tile { |
|
pointer-events: none; |
|
position: absolute; |
|
width: 256px; |
|
height: 256px; |
|
} |
|
|
|
.info { |
|
position: absolute; |
|
bottom: 0px; |
|
left: 0px; |
|
padding: 20px; |
|
background: #000; |
|
color: #fff; |
|
width: 100%; |
|
z-index: 1000; |
|
font-family:Helvetica, Arial, sans-serif; |
|
font-size:16px; |
|
} |
|
|
|
.d3-tip { |
|
line-height: 1.5; |
|
font-weight: normal; |
|
font-family: Helvetica, Arial, sans-serif; |
|
font-size:13px; |
|
padding: 10px; |
|
background: rgba(0, 0, 0, 0.8); |
|
color: #fff; |
|
border-radius: 3px; |
|
position:relative; |
|
z-index:101; |
|
} |
|
|
|
/* Creates a small triangle extender for the tooltip */ |
|
.d3-tip:after { |
|
box-sizing: border-box; |
|
display: inline; |
|
font-size: 20px; |
|
width: 100%; |
|
line-height: .5; |
|
color: rgba(0, 0, 0, 0.8); |
|
content: "\25BC"; |
|
position: absolute; |
|
text-align: center; |
|
} |
|
|
|
/* Style northward tooltips differently */ |
|
.d3-tip.n:after { |
|
margin: -1px 0 0 0; |
|
top: 100%; |
|
left: 0; |
|
} |
|
|
|
.checkpoint{ |
|
opacity: .6; |
|
stroke-width:1px; |
|
stroke:#ccc; |
|
} |
|
|
|
.checkpoint:hover{ |
|
opacity: 1.0; |
|
stroke: #000; |
|
stroke-width:2px; |
|
} |
|
#legend{ |
|
position:absolute; |
|
top:0px; |
|
height:50px; |
|
background: #333; |
|
width: 975px; |
|
padding-left:25px; |
|
font-size:14px; |
|
} |
|
.legend{ |
|
float:left; |
|
width:135px; |
|
} |
|
.legend.labels{ |
|
color: #fff; |
|
font-family:Arial; |
|
padding-top:5px; |
|
line-height:130%; |
|
width: 100px; |
|
} |
|
.point.legend, |
|
.rte.legend{ |
|
width:25px; |
|
} |
|
.point.legend.labels{ |
|
padding-top:13px; |
|
width:125px; |
|
} |
|
|
|
.rte.legend.labels{ |
|
padding-top:13px; |
|
width:150px; |
|
} |
|
</style> |
|
<body> |
|
<script src="http://d3js.org/d3.v3.min.js"></script> |
|
<script src="http://d3js.org/d3.geo.tile.v0.min.js"></script> |
|
<script src="d3.tip.js"></script> |
|
<script src="//code.jquery.com/jquery-1.10.2.js"></script> |
|
<div id="legend"> |
|
<svg class="path legend"> |
|
<line x1="10" y1="15" x2="120" y2="15" stroke-width="2" stroke="#ffffff" /> |
|
<line x1="10" y1="35" x2="120" y2="35" stroke-width="2" stroke="#ffffff" stroke-dasharray = "10,10" /> |
|
</svg> |
|
<div class="path legend labels"> |
|
route<br/> |
|
flyover |
|
</div> |
|
<svg class="point legend"> |
|
<circle cx="10" cy="23" r="7" fill="#ffffff" /> |
|
</svg> |
|
<div class="point legend labels"> |
|
checkpoint |
|
</div> |
|
<svg class="rte legend"> |
|
<rect x="0" y="15" width="14" height="14" fill="#ff4800"/> |
|
</svg> |
|
<div class="rte legend labels"> |
|
Fairbanks Route |
|
</div> |
|
<svg class="rte legend"> |
|
<rect x="0" y="15" width="14" height="14" fill="#007dab"/> |
|
</svg> |
|
<div class="rte legend labels"> |
|
Northern Route |
|
</div> |
|
<svg class="rte legend"> |
|
<rect x="0" y="15" width="14" height="14" fill="#08ab00"/> |
|
</svg> |
|
<div class="rte legend labels"> |
|
Southern Route |
|
</div> |
|
</div> |
|
<script> |
|
|
|
var width = 1000, |
|
height = 700, |
|
prefix = prefixMatch(["webkit", "ms", "Moz", "O"]); |
|
|
|
d3.csv("iditarod.csv", function(error, dataset) { createMap(dataset) }); |
|
|
|
var tile = d3.geo.tile() |
|
.size([width, height]); |
|
|
|
var projection = d3.geo.mercator() |
|
.scale((1 << 14) / 2 / Math.PI) |
|
.translate([-width / 2, -height / 2]); // just temporary |
|
|
|
var tileProjection = d3.geo.mercator(); |
|
|
|
var tilePath = d3.geo.path() |
|
.projection(tileProjection); |
|
|
|
var zoom = d3.behavior.zoom() |
|
.scale(projection.scale() * 2 * Math.PI) |
|
.scaleExtent([1 << 9, 1 << 25]) |
|
.translate(projection([-155.259395, 63.401919]).map(function(x) { return -x; })) |
|
.on("zoom", zoomed); |
|
|
|
var container = d3.select("body").append("div") |
|
.attr("id", "container") |
|
.style("width", width + "px") |
|
.style("height", height + "px") |
|
.call(zoom); |
|
|
|
var map = container.append("g") |
|
.attr("id", "map") |
|
|
|
var points = container.append("svg") |
|
.attr("id", "points") |
|
|
|
var layer = map.append("div") |
|
.attr("class", "layer"); |
|
|
|
zoomed(); |
|
|
|
var tip = d3.tip() |
|
.attr('class', 'd3-tip') |
|
.offset([-10, 0]) |
|
.html(function(d) { return d.Checkpoint;}) |
|
|
|
points.call(tip); |
|
|
|
function createMap(dataset) { |
|
d3.select("#points").selectAll("circle").data(dataset) //plotted locations on map |
|
.enter() |
|
.append("circle") |
|
.attr("r", 6) |
|
.attr("cx", function(d) {return projection([d.y,d.x])[0]}) |
|
.attr("cy", function(d) {return projection([d.y,d.x])[1]}) |
|
.attr("fill", function(d){ if(d.Route == "SR"){ return "#08ab00"} |
|
else if (d.Route == "NR"){ return "#007dab"} |
|
else { return "#ff4800";}}) |
|
.attr("class", function(d){return "checkpoint " + d.Route}) |
|
.on('mouseover', tip.show) |
|
.on('mouseout', tip.hide) |
|
|
|
zoomed(); |
|
|
|
} |
|
|
|
function reDraw() { |
|
d3.selectAll(".route") |
|
.remove(); |
|
} |
|
|
|
function zoomed() { |
|
var tiles = tile |
|
.scale(zoom.scale()) |
|
.translate(zoom.translate()) |
|
(); |
|
|
|
projection |
|
.scale(zoom.scale() / 2 / Math.PI) |
|
.translate(zoom.translate()); |
|
|
|
var circles = d3.select("#points").selectAll("circle") |
|
.attr("cx", function(d) {return projection([d.y,d.x])[0]}) |
|
.attr("cy", function(d) {return projection([d.y,d.x])[1]}); |
|
|
|
SRA =[{x:61.2185, y:-149.8812}, |
|
{x:61.1562, y:-149.7955}]; |
|
SRB = [{x:61.1562, y:-149.7955}, |
|
{x:61.771615, y:-149.993809}]; |
|
SR = [{x:61.771615, y:-149.993809}, |
|
{x:61.7322, y:-150.6789}, |
|
{x:61.9651, y:-151.1727}, |
|
{x:61.9793, y:-152.0728}, |
|
{x:62.0872, y:-152.7232}, |
|
{x:62.3031, y:-153.3788}, |
|
{x:63.0133, y:-154.375}, |
|
{x:62.9532, y:-155.5959}, |
|
{x:62.9886, y:-156.0641}, |
|
{x:63.146, y:-156.5299}, |
|
{x:62.5446, y:-158.0984}, |
|
{x:62.655, y:-159.5323}, |
|
{x:62.6522, y:-160.199}, |
|
{x:62.9054, y:-160.0642}, |
|
{x:63.5612, y:-159.5182}, |
|
{x:64.3272, y:-158.7219}, |
|
{x:63.8731, y:-160.7881}, |
|
{x:64.3339, y:-161.1539}, |
|
{x:64.9319, y:-161.1569}, |
|
{x:64.6175, y:-162.2606}, |
|
{x:64.563947, y:-162.99303}, |
|
{x:64.6813, y:-163.4055}, |
|
{x:64.5356, y:-164.4742}, |
|
{x:64.5011, y:-165.4063}]; |
|
|
|
FRA = [{x:61.2185, y:-149.8812}, |
|
{x:61.1562, y:-149.7955}]; |
|
FRB = [{x:61.1562, y:-149.7955}, |
|
{x:64.833767, y:-147.676556}]; |
|
FR = [{x:64.833767, y:-147.676556}, |
|
{x:64.538563, y:-149.082897}, |
|
{x:65.015584, y:-150.684809}, |
|
{x:65.174148, y:-152.07792}, |
|
{x:64.719639, y:-155.506494}, |
|
{x:64.7333, y:-156.9275}, |
|
{x:65.698943, y:-156.333292}, |
|
{x:64.90067, y:-157.699833}, |
|
{x:64.7194, y:-158.1031}, |
|
{x:64.3272, y:-158.7219}, |
|
{x:63.8731, y:-160.7881}, |
|
{x:64.3339, y:-161.1539}, |
|
{x:64.9319, y:-161.1569}, |
|
{x:64.6175, y:-162.2606}, |
|
{x:64.563947, y:-162.99303}, |
|
{x:64.6813, y:-163.4055}, |
|
{x:64.5356, y:-164.4742}, |
|
{x:64.5011, y:-165.4063}]; |
|
|
|
NRA = [{x:61.2185, y:-149.8812}, |
|
{x:61.1562, y:-149.7955}]; |
|
NRB = [{x:61.1562, y:-149.7955}, |
|
{x:61.771615, y:-149.993809}]; |
|
NR = [{x:61.771615, y:-149.993809}, |
|
{x:61.7322, y:-150.6789}, |
|
{x:61.9651, y:-151.1727}, |
|
{x:61.9793, y:-152.0728}, |
|
{x:62.0872, y:-152.7232}, |
|
{x:62.3031, y:-153.3788}, |
|
{x:63.0133, y:-154.375}, |
|
{x:62.9532, y:-155.5959}, |
|
{x:62.9886, y:-156.0641}, |
|
{x:63.146, y:-156.5299}, |
|
{x:63.41, y:-156.2}, |
|
{x:64.7394, y:-155.4869}, |
|
{x:64.7333, y:-156.9275}, |
|
{x:64.7194, y:-158.1031}, |
|
{x:64.3272, y:-158.7219}, |
|
{x:63.8731, y:-160.7881}, |
|
{x:64.3339, y:-161.1539}, |
|
{x:64.9319, y:-161.1569}, |
|
{x:64.6175, y:-162.2606}, |
|
{x:64.563947, y:-162.99303}, |
|
{x:64.6813, y:-163.4055}, |
|
{x:64.5356, y:-164.4742}, |
|
{x:64.5011, y:-165.4063}]; |
|
|
|
var routepath = d3.svg.line() |
|
.x(function(d){return projection([d.y,d.x])[0];}) |
|
.y(function(d){return projection([d.y,d.x])[1];}) |
|
.interpolate("linear") |
|
reDraw(); |
|
|
|
route1 = d3.select("#points").append("g") |
|
.attr("id", "paths") |
|
|
|
route1.append("svg:path") |
|
.attr("d", routepath(SR)) |
|
.attr("class", "route southern") |
|
.style("stroke-width", 2) |
|
.style("stroke", "#08ab00") |
|
.style("fill", "none"); |
|
|
|
route2 = d3.select("#points").append("g") |
|
.attr("id", "paths2") |
|
|
|
route2.append("svg:path") |
|
.attr("d", routepath(FR)) |
|
.attr("class", "route fairbanks") |
|
.style("stroke-width", 2) |
|
.style("stroke", "#ff4800") |
|
.style("fill", "none"); |
|
|
|
route3 = d3.select("#points").append("g") |
|
.attr("id", "paths3") |
|
|
|
route3.append("svg:path") |
|
.attr("d", routepath(NR)) |
|
.attr("class", "route northern") |
|
.style("stroke-width", 2) |
|
.style("stroke", "#007dab") |
|
.style("fill", "none"); |
|
|
|
route4 = d3.select("#points").append("g") |
|
.attr("id", "paths4") |
|
|
|
route4.append("svg:path") |
|
.attr("d", routepath(FRA)) |
|
.attr("class", "route fairbanks") |
|
.style("stroke-width", 3) |
|
.style("stroke", "#ff4800") |
|
.style("fill", "none"); |
|
|
|
route5 = d3.select("#points").append("g") |
|
.attr("id", "paths5") |
|
|
|
route5.append("svg:path") |
|
.attr("d", routepath(FRB)) |
|
.attr("class", "route fairbanks") |
|
.style("stroke-width", 3) |
|
.style("stroke", "#ff4800") |
|
.style("stroke-dasharray", "10,10") |
|
.style("fill", "none"); |
|
|
|
route6 = d3.select("#points").append("g") |
|
.attr("id", "paths6") |
|
|
|
route6.append("svg:path") |
|
.attr("d", routepath(SRA)) |
|
.attr("class", "route southern") |
|
.style("stroke-width", 3) |
|
.style("stroke", "#08ab00") |
|
.style("fill", "none"); |
|
|
|
route7 = d3.select("#points").append("g") |
|
.attr("id", "paths7") |
|
|
|
route7.append("svg:path") |
|
.attr("d", routepath(SRB)) |
|
.attr("class", "route southern") |
|
.style("stroke-width", 3) |
|
.style("stroke", "#08ab00") |
|
.style("stroke-dasharray", "10,10") |
|
.style("fill", "none"); |
|
|
|
route8 = d3.select("#points").append("g") |
|
.attr("id", "paths8") |
|
|
|
route8.append("svg:path") |
|
.attr("d", routepath(NRA)) |
|
.attr("class", "route northern") |
|
.style("stroke-width", 3) |
|
.style("stroke", "#007dab") |
|
.style("fill", "none"); |
|
|
|
route9 = d3.select("#points").append("g") |
|
.attr("id", "paths9") |
|
|
|
route9.append("svg:path") |
|
.attr("d", routepath(NRB)) |
|
.attr("class", "route northern") |
|
.style("stroke-width", 3) |
|
.style("stroke", "#007dab") |
|
.style("stroke-dasharray", "10,10") |
|
.style("fill", "none"); |
|
|
|
var image = layer |
|
.style(prefix + "transform", matrix3d(tiles.scale, tiles.translate)) |
|
.selectAll(".tile") |
|
.data(tiles, function(d) { return d; }); |
|
|
|
image.exit() |
|
.remove(); |
|
|
|
image.enter().append("img") |
|
.attr("class", "tile") |
|
.attr("src", function(d) { return "http://otile" + ["1", "2", "3", "4"][Math.random() * 4 | 0] + ".mqcdn.com/tiles/1.0.0/map/" + d[2] + "/" + d[0] + "/" + d[1] + ".jpg"; }) |
|
.style("left", function(d) { return (d[0] << 8) + "px"; }) |
|
.style("top", function(d) { return (d[1] << 8) + "px"; }); |
|
} |
|
|
|
function matrix3d(scale, translate) { |
|
var k = scale / 256, r = scale % 1 ? Number : Math.round; |
|
return "matrix3d(" + [k, 0, 0, 0, 0, k, 0, 0, 0, 0, k, 0, r(translate[0] * scale), r(translate[1] * scale), 0, 1 ] + ")"; |
|
} |
|
|
|
function prefixMatch(p) { |
|
var i = -1, n = p.length, s = document.body.style; |
|
while (++i < n) if (p[i] + "Transform" in s) return "-" + p[i].toLowerCase() + "-"; |
|
return ""; |
|
} |
|
|
|
function formatLocation(p, k) { |
|
var format = d3.format("." + Math.floor(Math.log(k) / 2 - 2) + "f"); |
|
return (p[1] < 0 ? format(-p[1]) + "°S" : format(p[1]) + "°N") + " " |
|
+ (p[0] < 0 ? format(-p[0]) + "°W" : format(p[0]) + "°E"); |
|
} |
|
|
|
</script> |
|
</body> |
|
</html> |