Last active
August 29, 2015 13:57
-
-
Save ahwolf/9547046 to your computer and use it in GitHub Desktop.
Locating where to have a meetup
This file contains 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
#map { | |
height: 667px; | |
} | |
svg { | |
width:700px; | |
} | |
.dragging { | |
fill:rgba(0,255,0,1); | |
} | |
#legend_row{ | |
height:200px; | |
} | |
#distance{ | |
margin-left: 10px; | |
} | |
#distance_traveled{ | |
font-size: 70px; | |
} | |
.dragging{ | |
stroke:rgb(96,96,96); | |
stroke-width: 4px; | |
} |
This file contains 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
var data = [{"lat": 42.5868882, "city": "Clinton Township, MI", "lon": -82.9195514, "count": 1}, {"lat": 42.331427, "city": "Detroit, MI", "lon": -83.0457538, "count": 10}, {"lat": 42.5467012, "city": "Birmingham, MI", "lon": -83.21131919999999, "count": 1}, {"lat": 42.5678534, "city": "West Bloomfield, MI", "lon": -83.373339, "count": 1}, {"lat": 42.583645, "city": "Bloomfield Hills, MI", "lon": -83.24548829999999, "count": 1}, {"lat": 42.2411499, "city": "Ypsilanti, MI", "lon": -83.61299389999999, "count": 1}, {"lat": 41.9164343, "city": "Monroe, MI", "lon": -83.3977101, "count": 1}, {"lat": 42.48059, "city": "Novi, MI", "lon": -83.4754913, "count": 1}, {"lat": 42.722257, "city": "Okemos, MI", "lon": -84.4274744, "count": 1}, {"lat": 42.4733688, "city": "Southfield, MI", "lon": -83.2218731, "count": 2}, {"lat": 42.9461395, "city": "Dryden, MI", "lon": -83.12382649999999, "count": 1}, {"lat": 42.49299999999999, "city": "Warren, MI", "lon": -83.02819699999999, "count": 1}, {"lat": 42.6055893, "city": "Troy, MI", "lon": -83.1499304, "count": 1}, {"lat": 42.3086444, "city": "Canton, MI", "lon": -83.48211599999999, "count": 1}, {"lat": 42.36837, "city": "Livonia, MI", "lon": -83.35270969999999, "count": 1}, {"lat": 42.3928151, "city": "Hamtramck, MI", "lon": -83.0496438, "count": 1}, {"lat": 42.0639323, "city": "South Rockwood, MI", "lon": -83.2610406, "count": 1}, {"lat": 42.4644795, "city": "Farmington, MI", "lon": -83.37632180000001, "count": 1}, {"lat": 42.4594803, "city": "Oak Park, MI", "lon": -83.18270509999999, "count": 1}, {"lat": 42.680588, "city": "Rochester, MI", "lon": -83.1338214, "count": 1}, {"lat": 42.5294773, "city": "Brighton, MI", "lon": -83.7802214, "count": 1}, {"lat": 42.3861485, "city": "Grosse Pointe, MI", "lon": -82.9118591, "count": 1}, {"lat": 42.4894801, "city": "Royal Oak, MI", "lon": -83.1446485, "count": 5}, {"lat": 42.2808256, "city": "Ann Arbor, MI", "lon": -83.7430378, "count": 10}, {"lat": 42.2808256, "city": "Sterling Heights, MI", "lon": -83.7430378, "count": 1}, {"lat": 42.5222567, "city": "Franklin, MI", "lon": -83.30604319999999, "count": 1}]; | |
var centroid = [{"lat": 42.31379293333334, "city": "Dual centroid", "lon": -83.65812481111111, "count": ""}, {"lat": 42.52342532941176, "city": "Dual centroid", "lon": -83.16335478235293, "count": ""}]; | |
var single_centroid = [{"lat": 42.442370302714494, "city": "Geomedian approximation", "lon": -83.2267860571865, "count": ""}]; |
This file contains 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> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Meetup locator</title> | |
<meta http-equiv="X-UA-Compatible" content="chrome=1"> | |
<link rel="stylesheet" href="app.css"> | |
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css"> | |
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap-theme.min.css"> | |
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.2/leaflet.css"> | |
<link rel="stylesheet" href="http://rawgithub.com/Caged/d3-tip/master/examples/example-styles.css"> | |
</head> | |
<body> | |
<div class="row"> | |
<div id="map" class="col-md-6"></div> | |
<div class="row" id="legend_row"> | |
<div id="legend" class="col-md-6"></div> | |
</div> | |
<div class="row"> | |
<div id="distance" class="col-md-3"> | |
Average distance traveled: <p><span id="distance_traveled">17.0</span> miles </p> | |
<p></p> | |
<p>Drag the green dot around to view average travel distances for all Meetup data scientists. </p> | |
</div> | |
</div> | |
</div> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.2/leaflet.js"></script> | |
<script type="text/javascript" src="http://maps.stamen.com/js/tile.stamen.js?v1.2.4"></script> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.3.11/d3.min.js"></script> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.6.3/d3-tip.min.js"></script> | |
<script src="http://d3js.org/colorbrewer.v1.min.js"></script> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script> | |
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script> | |
<!-- // <script src="js/srs_visual.js"></script> --> | |
<script src="data.js"></script> | |
<script src="maps.js"></script> | |
</body> | |
</html> |
This file contains 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
var layer = new L.StamenTileLayer("toner"); | |
var lat = 42.36837, | |
lon = -83.35270969999999; // the coordinates of Livonia, MI | |
var map = new L.Map("map", { | |
center: new L.LatLng(lat,lon), | |
zoom: 10, | |
minZoom: 10, | |
maxZoom: 10, | |
doubleClickZoom: false, | |
}); | |
map.addLayer(layer); | |
var svg = d3.select(map.getPanes().overlayPane).append("svg"); | |
var g = svg.append("g").attr("class", "leaflet-zoom-hide"); | |
// get centroid/center of gravity | |
// var total_count = d3.sum(data, function(d){return d.count}) | |
// var cent_x = d3.sum(data, function(d) {return d2point(d).x * d.count}) / total_count | |
// var cent_y = d3.sum(data, function(d) {return d2point(d).y * d.count}) / total_count | |
// var single_centroid = [{x: cent_x, | |
// y: cent_y, | |
// city: "Centroid", | |
// count:""}]; | |
console.log("centroid is: ", single_centroid); | |
var tip = d3.tip().attr('class', 'd3-tip').html(function(d) { return d.city + " " + d.count; }); | |
svg.call(tip) | |
var side_length = 60; | |
var padding = 1; | |
// These are transformation functions for d3, that allow my lat long | |
// data to interface with leaflet | |
function projectPoint(x, y) { | |
// var point = latLng2point(x,y); | |
// this.stream.point(point.x, point.y); | |
this.stream.point(x,y); | |
} | |
function latLng2point(x,y){ | |
return map.latLngToLayerPoint(new L.LatLng(y, x)); | |
} | |
function d2point(d){ | |
return latLng2point(d.lon,d.lat); | |
} | |
function point2latLng(x,y){ | |
return map.layerPointToLatLng(L.point(x,y)); | |
} | |
var transform = d3.geo.transform({point: projectPoint}); | |
var path = d3.geo.path().projection(transform); | |
var points = _.map(data, function(d){ | |
var xy_point = d2point(d); | |
return {x: xy_point.x, | |
y: xy_point.y, | |
count: d.count} | |
}); | |
console.log(points); | |
var drag = d3.behavior.drag() | |
.on("dragstart", dragstarted) | |
.on("drag", dragged) | |
.on("dragend", dragended); | |
//example | |
var circle_colors = ["rgba(255,0,0,.8)", "rgba(0,255,0,.5)", "rgba(0,0,255,.5)"]; | |
g.selectAll(".member") | |
.data(data) | |
.enter().append("circle") | |
.attr("class","member") | |
.attr("cx",function(d){return d2point(d).x; }) | |
.attr("cy",function(d){return d2point(d).y; }) | |
.attr("r", function(d){ return Math.pow(d.count,.5)*5;}) | |
.attr("fill", circle_colors[0]) | |
.on('mouseover', tip.show) | |
.on('mouseout', tip.hide); | |
g.selectAll(".centroid") | |
.data(single_centroid) | |
.enter().append("circle") | |
.attr("class","centroid") | |
.attr("cx",function(d){return d2point(d).x; }) | |
.attr("cy",function(d){return d2point(d).y; }) | |
.attr("r", 25) | |
.attr("fill", circle_colors[1]) | |
.on('mouseover', tip.show) | |
.on('mouseout', tip.hide) | |
.call(drag); | |
g.selectAll(".two_centroid") | |
.data(centroid) | |
.enter().append("circle") | |
.attr("class","two_centroid") | |
.attr("cx",function(d){return d2point(d).x; }) | |
.attr("cy",function(d){return d2point(d).y; }) | |
.attr("r", 20) | |
.attr("fill", circle_colors[2]) | |
.on('mouseover', tip.show) | |
.on('mouseout', tip.hide); | |
// make the legend for the various circles | |
var legend_svg = d3.select("#legend").append("svg"); | |
var circle_radius = 20; | |
var padding = 10; | |
legend_svg.selectAll("circle") | |
.data(circle_colors) | |
.enter().append("circle") | |
.attr("cx", circle_radius * 1.5) | |
.attr("cy", function(d, i) { return i*circle_radius*2.2 + circle_radius + padding; }) | |
.attr("r", circle_radius) | |
.attr("fill", function(d) { return d; }); | |
var text_legend = ["Meetup data scientists", "Optimal single location", "Optimal dual locations"]; | |
legend_svg.selectAll("text") | |
.data(text_legend) | |
.enter().append("text") | |
.text(function(d) { return d; }) | |
.attr("x", circle_radius * 3) | |
.attr("y", function(d, i) { return i*circle_radius*2.2 + circle_radius + padding + 6; }) | |
.attr("font-family", "Helvetica, Arial, sans-serif") | |
.attr("font-size", "16px"); | |
// transform the data into lt long | |
// drag stuff | |
function dragstarted(d) { | |
d3.event.sourceEvent.stopPropagation(); | |
d3.select(this).classed("dragging", true); | |
} | |
function dragged(d) { | |
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y); | |
mean_distance(d); | |
} | |
function dragended(d) { | |
d3.select(this).classed("dragging", false); | |
} | |
function mean_distance(d) { | |
// calculates the mean distance from all other red points, weighted b | |
// number of data scientists d is an object: d.x = number, d.y = | |
// number points is a list of objects. each obj has x and | |
// attribute. multiples, .e.i Ann Arbor, are listed multiple times. | |
var total_distance = _.reduce(points, function(memo, point){ | |
var distance = point.count * distance_formula(point,d) | |
return memo + distance; }, 0); | |
console.log(Math.round(total_distance / 50)); | |
$("#distance_traveled").html((total_distance/50).toFixed(1)) | |
} | |
function distance_formula(a,b) { | |
var first = point2latLng(a.x,a.y); | |
var second = point2latLng(b.x,b.y); | |
return getDistanceFromLatLonInKm(first.lat, first.lng, second.lat, second.lng); | |
// return Math.pow( (Math.pow( (b.x - a.x), 2) + Math.pow( (b.y - a.y), 2) ), 0.5) | |
} | |
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) { | |
var R = 6371 * 0.621371; // Radius of the earth in miles | |
var dLat = deg2rad(lat2-lat1); // deg2rad below | |
var dLon = deg2rad(lon2-lon1); | |
var a = | |
Math.sin(dLat/2) * Math.sin(dLat/2) + | |
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * | |
Math.sin(dLon/2) * Math.sin(dLon/2) | |
; | |
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); | |
var d = R * c; // Distance in miles | |
return d; | |
} | |
function deg2rad(deg) { | |
return deg * (Math.PI/180) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment