Skip to content

Instantly share code, notes, and snippets.

@jamieeeeeeeee
Created March 27, 2018 04:13
Show Gist options
  • Select an option

  • Save jamieeeeeeeee/0b02b300cae2ed9c3a4dc6c45b5e1117 to your computer and use it in GitHub Desktop.

Select an option

Save jamieeeeeeeee/0b02b300cae2ed9c3a4dc6c45b5e1117 to your computer and use it in GitHub Desktop.
// Copyright (c) 2013, Jason Davies, http://www.jasondavies.com
// See LICENSE.txt for details.
(function() {
var radians = Math.PI / 180,
degrees = 180 / Math.PI;
// TODO make incremental rotate optional
d3.geo.zoom = function() {
var projection,
zoomPoint,
event = d3.dispatch("zoomstart", "zoom", "zoomend"),
zoom = d3.behavior.zoom()
.on("zoomstart", function() {
var mouse0 = d3.mouse(this),
rotate = quaternionFromEuler(projection.rotate()),
point = position(projection, mouse0);
if (point) zoomPoint = point;
zoomOn.call(zoom, "zoom", function() {
projection.scale(d3.event.scale);
var mouse1 = d3.mouse(this),
between = rotateBetween(zoomPoint, position(projection, mouse1));
projection.rotate(eulerFromQuaternion(rotate = between
? multiply(rotate, between)
: multiply(bank(projection, mouse0, mouse1), rotate)));
mouse0 = mouse1;
event.zoom.apply(this, arguments);
});
event.zoomstart.apply(this, arguments);
})
.on("zoomend", function() {
zoomOn.call(zoom, "zoom", null);
event.zoomend.apply(this, arguments);
}),
zoomOn = zoom.on;
zoom.projection = function(_) {
return arguments.length ? zoom.scale((projection = _).scale()) : projection;
};
return d3.rebind(zoom, event, "on");
};
function bank(projection, p0, p1) {
var t = projection.translate(),
angle = Math.atan2(p0[1] - t[1], p0[0] - t[0]) - Math.atan2(p1[1] - t[1], p1[0] - t[0]);
return [Math.cos(angle / 2), 0, 0, Math.sin(angle / 2)];
}
function position(projection, point) {
var t = projection.translate(),
spherical = projection.invert(point);
return spherical && isFinite(spherical[0]) && isFinite(spherical[1]) && cartesian(spherical);
}
function quaternionFromEuler(euler) {
var λ = .5 * euler[0] * radians,
φ = .5 * euler[1] * radians,
γ = .5 * euler[2] * radians,
sinλ = Math.sin(λ), cosλ = Math.cos(λ),
sinφ = Math.sin(φ), cosφ = Math.cos(φ),
sinγ = Math.sin(γ), cosγ = Math.cos(γ);
return [
cosλ * cosφ * cosγ + sinλ * sinφ * sinγ,
sinλ * cosφ * cosγ - cosλ * sinφ * sinγ,
cosλ * sinφ * cosγ + sinλ * cosφ * sinγ,
cosλ * cosφ * sinγ - sinλ * sinφ * cosγ
];
}
function multiply(a, b) {
var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3],
b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
return [
a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3,
a0 * b1 + a1 * b0 + a2 * b3 - a3 * b2,
a0 * b2 - a1 * b3 + a2 * b0 + a3 * b1,
a0 * b3 + a1 * b2 - a2 * b1 + a3 * b0
];
}
function rotateBetween(a, b) {
if (!a || !b) return;
var axis = cross(a, b),
norm = Math.sqrt(dot(axis, axis)),
halfγ = .5 * Math.acos(Math.max(-1, Math.min(1, dot(a, b)))),
k = Math.sin(halfγ) / norm;
return norm && [Math.cos(halfγ), axis[2] * k, -axis[1] * k, axis[0] * k];
}
function eulerFromQuaternion(q) {
return [
Math.atan2(2 * (q[0] * q[1] + q[2] * q[3]), 1 - 2 * (q[1] * q[1] + q[2] * q[2])) * degrees,
Math.asin(Math.max(-1, Math.min(1, 2 * (q[0] * q[2] - q[3] * q[1])))) * degrees,
Math.atan2(2 * (q[0] * q[3] + q[1] * q[2]), 1 - 2 * (q[2] * q[2] + q[3] * q[3])) * degrees
];
}
function cartesian(spherical) {
var λ = spherical[0] * radians,
φ = spherical[1] * radians,
cosφ = Math.cos(φ);
return [
cosφ * Math.cos(λ),
cosφ * Math.sin(λ),
Math.sin(φ)
];
}
function dot(a, b) {
for (var i = 0, n = a.length, s = 0; i < n; ++i) s += a[i] * b[i];
return s;
}
function cross(a, b) {
return [
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0]
];
}
})();
<!DOCTYPE html>
<meta charset="utf-8">
<style>
#globe{
background: none;
}
.stroke {
fill: none;
stroke: #000;
stroke-width: 3px;
}
.fill {
fill: #A7DBD7;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
.land {
fill: #484A49;
}
.boundary {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
.overlay {
fill: none;
pointer-events: all;
}
.trip{
fill: none;
stroke: #F88A8A;
stroke-width: 5px;
opacity: 0.8;
stroke-linecap: round;
stroke-linejoin: round;
}
.city{
fill: #FEB24C;
}
.flights{
fill: none;
stroke: #537F76;
stroke-dasharray: 5,5;
stroke-width: 5px;
opacity: 0.8;
stroke-linecap: round;
stroke-linejoin: round;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="d3.geo.zoom.js"></script>
<script src="//d3js.org/queue.v1.min.js"></script>
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
<div>
<div id="globe"></div>
</div>
<script>
var width = window.innerWidth/1.5;
if (width > 640) {
width = 640;
}
var height = width;
// Map configuration
var rScale = d3.scale.linear();
var peoplePerPixel = 50000;
var max_population = [];
var projection = d3.geo.orthographic()
.scale(width/2.37) //value of 270 is working
.translate([width / 2, height / 2])
.clipAngle(90)
.precision(.1)
.rotate([90,10,0]);
var zoom = d3.behavior.zoom()
.scaleExtent([1,6])
.on("zoom",zoomed);
var zoomEnhanced = d3.geo.zoom().projection(projection)
.on("zoom",zoomedEnhanced);
var drag = d3.behavior.drag()
.origin(function() { var r = projection.rotate(); return {x: r[0], y: -r[1]}; })
.on("drag", dragged)
.on("dragstart", dragstarted)
.on("dragend", dragended);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("#globe").append("svg")
.attr("width", width)
.attr("height", height)
.attr("id", "globe-svg");
var pathG = svg.append("g");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.call(zoomEnhanced)
pathG.append("defs").append("path")
.datum({type: "Sphere"})
.attr("id", "sphere")
.attr("d", path);
pathG.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere");
pathG.append("use")
.attr("class", "fill")
.attr("xlink:href", "#sphere");
pathG.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
d3.json("world-topo-cities-route.json", function(error, world) {
// to render meridians/graticules on top of lands, use insert which adds new path before graticule in the selection
pathG.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
//this line of code draw countries on to the map
pathG.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
// Add the path of our road trip
pathG.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.trip_geo_v2))
.attr("class", "trip")
.attr("d", path);
pathG.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.flights))
.attr("class", "flights")
.attr("d", path);
data_array = [];
cities = topojson.feature(world, world.objects.cities);
console.log(cities);
console.log(cities.features);
for (i = 0; i < cities.features.length; i++) {
if ( cities.features[i].properties ) {
data_array.push(cities.features[i].properties.nights);
} else {
data_array.push(.5);
}
}
console.log(data_array);
max_value = data_array.sort(d3.descending)[0]
var rMin = 1;
var rMax = 30;//Math.sqrt(max_population / (peoplePerPixel * Math.PI));
rScale.domain(data_array);
rScale.range([rMin, rMax]);
var color = d3.scale.threshold()
.domain(data_array)
.range(["#feb24c","#fd8d3c","#fc4e2a","#e31a1c","#bd0026","#800026"]);
console.log(color(1));
console.log(color(20));
// Add the cities we stop in
pathG.insert("path", ".graticule")
.datum(cities)
.attr("class", "city")
.attr("d", path)
.style("fill", function(d) {
return d.properties ? color(d.properties.nights) : color([1]);
});
});
d3.select(window).on("resize", resize);
// Make globe responsive
function resize() {
// adjust things when the window size changes
//width = parseInt(d3.select("#globe").style("width"));
var width = window.innerWidth,
height = width;
// update projection
projection
.translate([width / 2, height / 2])
.scale(width/3.5);
// resize the map container
svg
.style("width", width + "px")
.style("height", height + "px");
// resize the map
svg.select(".land").attr("d", path);
svg.selectAll(".graticule").attr("d", path);
svg.selectAll(".boundary").attr("d", path);
svg.selectAll("#sphere").attr("d", path);
svg.selectAll(".trip").attr("d", path);
svg.selectAll(".city").attr("d", path);
svg.selectAll(".flights").attr("d", path);
}
// animate svg away
var logo_svg = d3.selectAll(".ij-inner-centered-image-big")
.transition()
.duration(300)
.ease("linear")
.attr("car");
// apply transformations to map and all elements on it
function zoomed()
{
pathG.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
//grids.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
//geofeatures.select("path.graticule").style("stroke-width", 0.5 / d3.event.scale);
pathG.selectAll("path.boundary").style("stroke-width", 0.5 / d3.event.scale);
}
function zoomedEnhanced()
{
pathG.selectAll("path").attr("d",path);
//pathG.selectAll("circle").attr("cx");
//pathG.selectAll("circle").attr("cy");
}
function dragstarted(d)
{
//stopPropagation prevents dragging to "bubble up" which triggers same event for all elements below this object
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
}
function dragged() {
projection.rotate([d3.event.x, -d3.event.y]);
pathG.selectAll("path").attr("d", path);
//pathG.selectAll("circle").attr("cx");
//pathG.selectAll("circle").attr("cy");
}
function dragended(d)
{
d3.select(this).classed("dragging", false);
}
d3.select(self.frameElement).style("height", height + "px");
</script>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment