Created
March 27, 2018 04:13
-
-
Save jamieeeeeeeee/0b02b300cae2ed9c3a4dc6c45b5e1117 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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] | |
| ]; | |
| } | |
| })(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment