Skip to content

Instantly share code, notes, and snippets.

@bobuss
Created March 6, 2012 10:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bobuss/1985684 to your computer and use it in GitHub Desktop.
Save bobuss/1985684 to your computer and use it in GitHub Desktop.
d3 great arcs over polymaps
<!DOCTYPE html>
<html>
<head>
<title>D3 over Polymap example</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="map.css" />
</head>
<body onload="init();">
<div id="map_container"></div>
<script type="text/javascript" src="http://polymaps.org/polymaps.min.js?2.5.0"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.min.js"></script>
<script type="text/javascript" src="map.js"></script>
<script>
var points = [];
var map ;
function createMap() {
map = new Map({
container: "map_container",
zoom: 3,
zoom_range: [1,9],
points_limit: 10,
arcs_limit: 9
});
}
function updateMap(longitude, latitude) {
console.log(longitude + ' - ' + latitude);
points.push(map.addPoint(longitude, latitude));
if (points.length >= 2) {
map.addGreatArc(points[points.length-1], points[points.length-2]);
points.shift();
}
}
function init() {
createMap();
setInterval(function(){
updateMap(300 * Math.random() - 150, 150 * Math.random() - 75);
}, 1000);
}
</script>
</body>
</html>
#map_container svg.map {
background-color: #2F2C2B;
}
.compass .back {
fill: #eee;
fill-opacity: .8;
}
.compass .fore {
stroke: #999;
stroke-width: 1.5px;
}
.compass rect.back.fore {
fill: #999;
fill-opacity: .3;
stroke: #eee;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.compass .direction {
fill: none;
}
.compass .chevron {
fill: none;
stroke: #999;
stroke-width: 5px;
}
.compass .zoom .chevron {
stroke-width: 4px;
}
.compass .active .chevron, .compass .chevron.active {
stroke: #fff;
}
.compass.active .active .direction {
fill: #999;
}
circle.marker {
fill: #E03434;
stroke-width : 1px;
stroke: #444;
}
text.label {
font-size: 11px;
fill: #FFF;
font-weight: bold;
text-anchor: middle;
}
path.label {
fill: #000;
}
circle.point:hover {
fill: #C90134;
}
path.great_arc {
fill: none;
stroke: #000;
stroke-width: 0.5px;
stroke-opacity: 0.5;
}
var Map = function(options) {
this.options = options;
var po = org.polymaps;
var center = {lat: 39, lon: 10};
this.map = po.map()
.container(document.getElementById(this.options.container).appendChild(po.svg("svg")))
.center(center)
.zoom(undefined != this.options.zoom ? this.options.zoom : 3)
.zoomRange(undefined != this.options.zoom_range ? this.options.zoom_range : [3, 9])
.add(po.interact());
this.map.add(po.image()
.url(po.url("http://{S}tile.cloudmade.com"
+ "/1a1b06b230af4efdbb989ea99e9841af"
+ "/998/256/{Z}/{X}/{Y}.png")
.hosts(["a.", "b.", "c.", ""])));
this.map.add(po.compass()
.pan("none"));
this.d3layer = d3.select("#"+this.options.container+" svg").insert("svg:g", ".compass");
this.points = [];
this.arcs = [];
};
Map.prototype.addGreatArc = function(source, target) {
var that = this;
var path = function(arc) {
var scale = Math.pow(2, that.map.zoom()) * 256;
var lp = that.map.locationPoint({lat:0, lon:0});
var projection = d3.geo.mercator().scale(scale).translate([lp.x, lp.y]);
// check the arc to prevent some display bugs
var sanitized = {
type : "MultiLineString",
coordinates : []
};
var coords = [arc.coordinates[0]];
var s1, s2 = 0;
for (var i=1 ; i<arc.coordinates.length ; i++) {
if (Math.abs(arc.coordinates[i][0] - arc.coordinates[i-1][0]) > 180) {
sanitized.coordinates.push(coords);
coords = [];
}
coords.push(arc.coordinates[i]);
}
sanitized.coordinates.push(coords);
return d3.geo.path().projection(projection)(sanitized);
};
var id = ("arc_" + source[0] + "_" + source[1] + "_" + target[0] + "_" + target[1]).replace(/[^0-9a-z_]/g, '');
this.arcs.push({
id: id,
source: source,
target: target
});
// optionnaly trim the arcs array
if (undefined != this.options.arcs_limit && this.arcs.length >= this.options.arcs_limit) {
this.arcs.shift();
}
var arc = d3.geo.greatArc().precision(1);
var elements = this.d3layer.selectAll("path.great_arc")
.data(this.arcs, function(d) { return d.id; });
elements.enter().append("path")
.attr("class", "great_arc")
.attr("d", function(d) { return path(arc(d)); });
elements.exit().remove();
// interactions
this.map.on("move", function() {
that.d3layer.selectAll(".great_arc").attr("d", function(d) { return path(arc(d)); });
});
this.map.on("resize", function() {
that.d3layer.selectAll(".great_arc").attr("d", function(d) { return path(arc(d)); });
});
};
// from polymaps to d3
// https://gist.github.com/899670
Map.prototype.addPoint = function(longitude, latitude) {
var that = this;
var transform = function(d) {
var td = that.map.locationPoint({lon: d.longitude, lat: d.latitude});
return "translate(" + td.x + "," + td.y + ")";
};
var id = ("mark_" + longitude + "_" + latitude).replace(/[^0-9a-z_]/g, '');
this.points.push({id: id, longitude: longitude, latitude: latitude});
// optionnaly trim the points array
if (undefined != this.options.points_limit && this.points.length >= this.options.points_limit) {
this.points.shift();
}
// Add an svg:g for each station.
var markers = this.d3layer.selectAll(".marker")
.data(this.points, function(d) { return d.id; });
markers.enter().append("circle")
.attr("class", "marker")
.attr("transform", transform)
.attr("r", 4.5);
markers.exit().transition()
.duration(200)
.attr("r", 0)
.remove();
// interactions
this.map.on("move", function() {
that.d3layer.selectAll(".marker").attr("transform", transform);
});
this.map.on("resize", function() {
that.d3layer.selectAll(".marker").attr("transform", transform);
});
return [longitude, latitude];
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment