forked from emeeks's block: Parallel Coordinates wired to a map of routes from ORBIS
xxxxxxxxxx
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Route Probability Exploration with Parallel Coordinates</title>
<style type="text/css">
svg {
font: 10px sans-serif;
}
path {
stroke-linejoin: round;
stroke-linecap: round;
}
.routes {
stroke-dasharray: 4, 7, 3, 8;
stroke-width: 2.5;
}
.background path {
fill: none;
stroke: #ccc;
stroke-opacity: .4;
shape-rendering: crispEdges;
}
.foreground path {
fill: none;
stroke: #e04242;
stroke-opacity: .5;
stroke-width: 2;
}
.brush .extent {
fill-opacity: .3;
stroke: #fff;
shape-rendering: crispEdges;
}
.axis line, .axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
text-shadow: 0 1px 0 #fff;
cursor: pointer;
}
</style>
</head>
<body>
<div id="map" style="border:0px solid gray; background-color:#fdfbf1;">
</div>
<div id="parallel" style="border:0px solid gray; background-color:#fdfbf1;">
</div>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://d3js.org/d3.geo.projection.v0.min.js"></script>
<script src="https://d3js.org/topojson.v0.min.js"></script>
<script type="text/javascript">
pathRamp=d3.scale.linear().domain([1,5,9,13]).range(["blue","green","orange","blue"]);
var m = [30, 10, 10, 10],
w = 1280 - m[1] - m[3],
h = 300 - m[0] - m[2];
var x = d3.scale.ordinal().rangePoints([0, w], 1),
y = {},
dragging = {};
var line = d3.svg.line(),
axis = d3.svg.axis().orient("left"),
background,
foreground;
mapsvg = d3.select("#map").append("svg:svg")
.attr("width", 1280)
.attr("height", 400)
;
var svg = d3.select("#parallel").append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
d3.csv("sites.csv", function(csvin) {
sitesdata = csvin;
d3.json("lcroutes.json", function(error, lcroutes) {
pathdata = topojson.object(lcroutes, lcroutes.objects.lcroutes).geometries;
// Extract the list of dimensions and create a scale for each.
x.domain(dimensions = d3.keys(pathdata[0]["properties"]).filter(function(d) {
return d != "type" && d != "geometry" && (y[d] = d3.scale.linear()
.domain(d3.extent(pathdata, function(p) { return +p["properties"][d]; }))
.range([h, 0]));
// .range([h, 0]));
}));
foreground = svg.append("svg:g")
.attr("class", "foreground")
.selectAll("path")
.data(pathdata)
.enter().append("svg:path")
.attr("d", parallelPath)
.style("stroke", function(d) {return (pathRamp(d.properties.month))})
;
// Add a group element for each dimension.
var g = svg.selectAll(".dimension")
.data(dimensions)
.enter().append("svg:g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d) + ")"; })
.on("click", titleClick)
// Add an axis and title.
g.append("svg:g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(axis.scale(y[d])); })
.append("svg:text")
.attr("text-anchor", "middle")
.attr("y", -9)
.attr("class", "dimensionText")
.text(String);
// Add and store a brush for each axis.
g.append("svg:g")
.attr("class", "brush")
.each(function(d) { d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brush", brush)); })
.selectAll("rect")
.attr("x", -8)
.attr("width", 16);
createMap();
})
});
function titleClick(d) {
var pmax = d3.max(pathdata, function(p) { return p["properties"][d]; })
var pmin = d3.min(pathdata, function(p) { return p["properties"][d]; })
var pq1 = (pmax * .25) + (pmin * .75);
var pq2 = (pmax * .5) + (pmin * .5);
var pq3 = (pmax * .75) + (pmin * .25);
pathRamp=d3.scale.linear().domain([pmin,pq1,pq2,pq3,pmax]).range(["blue","green","gold","orange","red"]);
foreground.style("stroke", function(p) {return (pathRamp(p["properties"][d]))});
d3.selectAll("path.routes").style("stroke", function(p) {return (pathRamp(p["properties"][d]))});
}
function position(d) {
var v = dragging[d];
return v == null ? x(d) : v;
}
function transition(g) {
return g.transition().duration(500);
}
// Returns the path for a given data point.
function parallelPath(d) {
return line(dimensions.map(function(p) { return [position(p), y[p](d["properties"][p])]; }));
}
// Handles a brush event, toggling the display of foreground lines.
function brush() {
var actives = dimensions.filter(function(p) { return !y[p].brush.empty(); }),
extents = actives.map(function(p) { return y[p].brush.extent(); });
foreground.style("display", function(d) {
return actives.every(function(p, i) {
return extents[i][0] <= d["properties"][p] && d["properties"][p] <= extents[i][1];
}) ? null : "none";
});
d3.selectAll("path.routes").style("display", function(d) {
return actives.every(function(p, i) {
return extents[i][0] <= d["properties"][p] && d["properties"][p] <= extents[i][1];
}) ? null : "none";
});
}
/////////////THE MAP
function createMap(){
rankRamp=d3.scale.linear().domain([0,.005]).range([1,10]).clamp(true);
projection = d3.geo.satellite()
.distance(1.1)
.scale(1000)
.rotate([-15, -43, -10])
.center([-3, -4.5])
.tilt(25)
.clipAngle(1000);
var graticule = d3.geo.graticule()
.extent([[180, 180], [-180, -180]])
.step([5, 5]);
path = d3.geo.path()
.projection(projection);
siteLabel = mapsvg.append("svg:text")
.attr("x", 20)
.attr("y", 20)
.text("Click site for details");
sprLabel = mapsvg.append("svg:text")
.attr("x", 20)
.attr("y", 40)
.text("PS");
smLabel = mapsvg.append("svg:text")
.attr("x", 20)
.attr("y", 60)
.text("PSM");
map = mapsvg.append("svg:g").attr("class", "map")
.attr("transform", "translate(2,3)")
d3.json("rltopo.json", function(error, rltopo) {
/*
map.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
*/
embossed = map.selectAll("path.routes")
.data(topojson.object(rltopo, rltopo.objects.romeland).geometries)
.enter().insert("path")
.attr("d", path)
.attr("class", "countries")
.style("fill", "ghostwhite")
.style("stroke", "lightgray")
.style("stroke-width", 2)
sites = map.selectAll("g.sites")
.data(sitesdata)
.enter()
.append("svg:g")
.attr("class", "foreground")
.attr("transform", function(d) {return "translate(" + projection([d.xcoord,d.ycoord]) + ")";})
.style("cursor", "pointer")
.on("click", siteClick)
;
circleRamp=d3.scale.linear().domain([0,4,7,11,15,19,23]).range(["red","purple","orange","blue","steelblue","green","yellow"]);
sites.append("svg:circle")
.attr('r', function(d) {return (rankRamp(d.ES))})
.attr("class", "sites")
.style("fill", function(d) {return (circleRamp(d.EM))})
.style("stroke", "white")
.style("opacity", 0)
.transition()
.delay(300)
.duration(1000)
.style("opacity", .35)
;
baseRoads = map.selectAll("path.routes")
.data(pathdata)
.enter().insert("path")
.attr("class", "lcroutes")
.attr("d", path)
.style("fill", "none")
.style("stroke-width", 2)
//.style("stroke", 2)
.style("stroke", function(d) {return (pathRamp(d.properties.month))})
.attr("class", "routes")
/* baseRoads = map.selectAll("path.routes")
.data(pathdata)
.enter().append("svg:path")
.style("fill", "none")
.style("stroke", function(d) {return (pathRamp(d.month))})
.attr("d", path)
.attr("class", "routes")
.style("opacity", 0)
.transition()
.duration(1000)
.style("opacity", .2)
;
*/
}
)
}
function siteClick(d,i) {
siteLabel.text(d.name);
sprLabel.text("Ranking - Economic: " + d.ES + " - Military: " + d.MS + " - Rapid: " + d.RS );
smLabel.text("Modules - Economic: " + d.EM + " - Military: " + d.MM + " - Rapid: " + d.RM );
}
mapsvg.on("click", function() {
p = projection.invert(d3.mouse(this));
console.log(p);
projection.rotate([-(p[0]), -(p[1])]);
mapsvg.selectAll("path").transition().duration(1000).attr("d", path);
sites.transition().duration(1000).attr("transform", function(d) {return "translate(" + projection([d.xcoord,d.ycoord]) + ")";})
});
</script>
</body>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
Modified http://d3js.org/d3.geo.projection.v0.min.js to a secure url
Modified http://d3js.org/topojson.v0.min.js to a secure url
https://d3js.org/d3.v3.min.js
https://d3js.org/d3.geo.projection.v0.min.js
https://d3js.org/topojson.v0.min.js