var h = 700;
var w = 700;
var projection = d3.geo.mercator()
.center([-76.612223, 39.294504])
.scale([150000])
.translate([310, 290]);
var comma_format = d3.format(",");
// viridis colors
var viridis_colors = ["#440154","#440256","#450457","#450559","#46075a","#46085c","#460a5d","#460b5e","#470d60","#470e61","#471063","#471164","#471365","#481467","#481668","#481769","#48186a","#481a6c","#481b6d","#481c6e","#481d6f","#481f70","#482071","#482173","#482374","#482475","#482576","#482677","#482878","#482979","#472a7a","#472c7a","#472d7b","#472e7c","#472f7d","#46307e","#46327e","#46337f","#463480","#453581","#453781","#453882","#443983","#443a83","#443b84","#433d84","#433e85","#423f85","#424086","#424186","#414287","#414487","#404588","#404688","#3f4788","#3f4889","#3e4989","#3e4a89","#3e4c8a","#3d4d8a","#3d4e8a","#3c4f8a","#3c508b","#3b518b","#3b528b","#3a538b","#3a548c","#39558c","#39568c","#38588c","#38598c","#375a8c","#375b8d","#365c8d","#365d8d","#355e8d","#355f8d","#34608d","#34618d","#33628d","#33638d","#32648e","#32658e","#31668e","#31678e","#31688e","#30698e","#306a8e","#2f6b8e","#2f6c8e","#2e6d8e","#2e6e8e","#2e6f8e","#2d708e","#2d718e","#2c718e","#2c728e","#2c738e","#2b748e","#2b758e","#2a768e","#2a778e","#2a788e","#29798e","#297a8e","#297b8e","#287c8e","#287d8e","#277e8e","#277f8e","#27808e","#26818e","#26828e","#26828e","#25838e","#25848e","#25858e","#24868e","#24878e","#23888e","#23898e","#238a8d","#228b8d","#228c8d","#228d8d","#218e8d","#218f8d","#21908d","#21918c","#20928c","#20928c","#20938c","#1f948c","#1f958b","#1f968b","#1f978b","#1f988b","#1f998a","#1f9a8a","#1e9b8a","#1e9c89","#1e9d89","#1f9e89","#1f9f88","#1fa088","#1fa188","#1fa187","#1fa287","#20a386","#20a486","#21a585","#21a685","#22a785","#22a884","#23a983","#24aa83","#25ab82","#25ac82","#26ad81","#27ad81","#28ae80","#29af7f","#2ab07f","#2cb17e","#2db27d","#2eb37c","#2fb47c","#31b57b","#32b67a","#34b679","#35b779","#37b878","#38b977","#3aba76","#3bbb75","#3dbc74","#3fbc73","#40bd72","#42be71","#44bf70","#46c06f","#48c16e","#4ac16d","#4cc26c","#4ec36b","#50c46a","#52c569","#54c568","#56c667","#58c765","#5ac864","#5cc863","#5ec962","#60ca60","#63cb5f","#65cb5e","#67cc5c","#69cd5b","#6ccd5a","#6ece58","#70cf57","#73d056","#75d054","#77d153","#7ad151","#7cd250","#7fd34e","#81d34d","#84d44b","#86d549","#89d548","#8bd646","#8ed645","#90d743","#93d741","#95d840","#98d83e","#9bd93c","#9dd93b","#a0da39","#a2da37","#a5db36","#a8db34","#aadc32","#addc30","#b0dd2f","#b2dd2d","#b5de2b","#b8de29","#bade28","#bddf26","#c0df25","#c2df23","#c5e021","#c8e020","#cae11f","#cde11d","#d0e11c","#d2e21b","#d5e21a","#d8e219","#dae319","#dde318","#dfe318","#e2e418","#e5e419","#e7e419","#eae51a","#ece51b","#efe51c","#f1e51d","#f4e61e","#f6e620","#f8e621","#fbe723","#fde725"];
var svg1 = d3.select("#map1")
.append("svg")
.attr("height", h)
.attr("width", w);
var svg2 = d3.select("#map2")
.append("svg")
.attr("height", h)
.attr("width", w);
var path = d3.geo.path()
.projection(projection);
var hexbin = d3.hexbin()
.size([w, h])
.radius(3.5);
// first of two scales for linear fill
// tip from SO: http://stackoverflow.com/questions/17671252/d3-create-a-continous-color-scale-with-many-strings-inputs-for-the-range-and-dy
var fill_scale1 = d3.scale.linear()
.domain(d3.range(0, 1, 1.0 / (viridis_colors.length - 1)))
.range(viridis_colors);
// declare basemap first to render before (i.e. below) points or hexbins
var basemap1 = svg1.append("g");
var points = svg1.append("g");
var basemap2 = svg2.append("g");
var hex = svg2.append("g").attr("clip-path", "url(#map_clip)");
// GeoJSON file is modified from the Crime & Safety (2010-2012) - Shape.geojson file available at https://data.baltimorecity.gov/Neighborhoods/Crime-Safety-2010-2012-Shape/bywi-mtiu
// neighborhood data from original file replaced with number of vacant buildings ('see R analysis')
var map = d3.json("/data/Baltimore_vacant_buildings_Shape.geojson", function(json) {
// basemap for point plot
basemap1.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.attr("stroke", "grey")
.attr("fill", "white")
.on("mouseover", function(d){
d3.select(this)
.attr("fill", "rgb(243, 100, 42)");
d3.select("#tooltip")
.select("#neighborhood")
.text(d.properties.csa);
d3.select("#tooltip")
.select("#datum")
.text(function() {
var count = parseFloat(d.properties.vacant_buildings);
return comma_format(count) + " vacant buildings";
});
})
.on("mouseout", function() {
d3.select(this)
.transition()
.duration(50)
.attr("fill", "white");
d3.select("#tooltip")
.select("#neighborhood")
.text("\u00A0"); // Unicode equivalent of HTML  
d3.select("#tooltip")
.select("#datum")
.text("\u00A0");
});
// clippling mask for hexbin plot
var dims = path.bounds(json);
var clip_w = dims[1][0] - dims[0][0];
var clip_h = dims[1][1] - dims[0][1];
svg2.append("clipPath")
.attr("id", "map_clip")
.append("rect")
.attr("width", clip_w)
.attr("height", 700)
.attr("transform", function() {
return "translate(" + 50 + "," + 29 + ")";
});
// basemap for hexbin plot
basemap2.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.attr("stroke", "grey")
.attr("fill", "#595959");
});
var vacancy = d3.csv("/data/Baltimore_vacant_buildings.csv", function(data) {
// convert lat/lng string to numeric
data.forEach(function(d) {
d.latitude = +d.latitude;
d.longitude = +d.longitude;
});
// plot points
points.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([d.latitude, d.longitude])[0]; }) // project lat
.attr("cy", function(d) {
return projection([d.latitude, d.longitude])[1]; }) // project lng
.attr("r", 1)
.attr("fill", "black")
.style("opacity", 0.35)
.style("pointer-events", "none");
points2 = [];
data.forEach(function(d) {
d.latitude = +d.latitude;
d.longitude = +d.longitude;
var x = projection([d.latitude, d.longitude])[0];
var y = projection([d.latitude, d.longitude])[1];
points2.push([x, y]);
});
var bins = hexbin(points2); // bin lat/lng
var bins_n = []; // points per hexbin
bins.forEach(function(d) {
bins_n.push(d.length);
});
// second of two scales for linear fill color
var extent = d3.extent(bins_n);
var fill_scale2 = d3.scale.linear()
.domain([extent[0], extent[1]])
.range([0,1]);
hex.selectAll(".hexagon")
.data(hexbin(points2))
.enter()
.append("path")
.attr("class", "hexagon")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
.style("fill", function(d) {
return fill_scale1(fill_scale2(d.length));
});
});