A quadtree map of NYC Wi-Fi hotspot locations built with D3.js. Data are from the NYC OpenData.
Last active
October 1, 2016 22:59
-
-
Save gmculp/137bf0a362ab32c30b13e87c13aa144d to your computer and use it in GitHub Desktop.
Quadtree Map of NYC WiFi Hotspots
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"> | |
| <title>Quadtree Map of NYC WiFi Hotspots</title> | |
| <style> | |
| html { | |
| display: table; | |
| margin: auto; | |
| } | |
| .full_node { | |
| fill: OrangeRed; | |
| stroke: #808080; | |
| shape-rendering: crispEdges; | |
| } | |
| .empty_node { | |
| fill: none; | |
| stroke: #808080; | |
| shape-rendering: crispEdges; | |
| } | |
| body { | |
| background: ivory; | |
| display: table-cell; | |
| vertical-align: middle; | |
| } | |
| </style> | |
| <body> | |
| <script src="http://d3js.org/d3.v3.js"></script> | |
| <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> | |
| <script> | |
| //declare dimensions of page | |
| //scale based on window width | |
| var w = ($( window ).height()/850)>1?1:$( window ).height()/850; | |
| var width = w*800, | |
| height = w*800; | |
| //initializa projection | |
| var projection = d3.geo.albers(); | |
| var path = d3.geo.path().projection(projection); | |
| //data source URL | |
| var URL = "https://data.cityofnewyork.us/resource/7agf-bcsq.json?$where=(lat IS NOT NULL)&$select=lat,long_" | |
| URL = encodeURI(URL); //encode special characters such as spaces and quotes | |
| //geojson URL | |
| var countyJSON = "https://rawgit.com/gmculp/b76fbaa2bf72f92c638f/raw/4d0235b7548e869e373f079e7abd1e8f1e9e81ee/borough.json"; | |
| var quadtree; | |
| generate_quadtree_map(); | |
| function generate_quadtree_map(){ | |
| var pt_arr = []; | |
| $.getJSON(countyJSON, function (json) { | |
| //set the projection | |
| set_projection(projection, path, json, width, height); | |
| $.getJSON(URL, function(json_data){ | |
| $.each(json_data, function(i, json_obj){ | |
| var cxy = [json_obj.long_,json_obj.lat]; | |
| var p = projection(cxy); | |
| pt_arr.push(p); | |
| }); | |
| quadtree = d3.geom.quadtree() | |
| .extent([[-1, -1], [width + 1, height + 1]]) | |
| (pt_arr); | |
| var svg = d3.select("body").append("svg") | |
| .attr("width", width) | |
| .attr("height", height); | |
| var this_quadtree = nodes(quadtree); | |
| var this_min = d3.min(this_quadtree, function(d) { if (d.pts) return +d.width;}); | |
| var this_max = d3.max(this_quadtree, function(d) { if (d.pts) return +d.width;}); | |
| svg.selectAll(".node") | |
| .data(this_quadtree) | |
| .enter().append("rect") | |
| .attr("title", function(d) { return d.node_no; }) | |
| .attr("x", function(d) { return d.x; }) | |
| .attr("y", function(d) { return d.y; }) | |
| .attr("width", function(d) { return d.width; }) | |
| .attr("height", function(d) { return d.height; }) | |
| .style("stroke-opacity", 1.0) | |
| .attr('fill-opacity',function(d) { | |
| return 1.0 - (d.width/this_max); | |
| }) | |
| .attr("class", function(d) { | |
| if (d.pts){ | |
| return "full_node"; | |
| } | |
| else{ | |
| return "empty_node"; | |
| } | |
| }); | |
| //add county layer to map | |
| var counties = svg.append("g") | |
| .attr("id", "counties"); | |
| counties.selectAll("path") | |
| .data(json.features) | |
| .enter().append("path") | |
| .attr("d", path) | |
| .attr("pointer-events", "none") | |
| .attr("stroke", "black") | |
| .attr("stroke-width", 1.25) | |
| .style("fill", "none"); | |
| }); | |
| }); | |
| } | |
| // Collapse the quadtree into an array of rectangles. | |
| function nodes(quadtree) { | |
| var nodes = []; | |
| var node_cnt = 0; | |
| quadtree.visit(function(node, x1, y1, x2, y2) { | |
| //var node.leaf; | |
| node_cnt++; | |
| //alert(node.nodes.length); //4 | |
| var np = node.point; | |
| nodes.push({x: x1, y: y1, width: x2 - x1, height: y2 - y1, area: ((y2 - y1)*(x2-x1)), pts: np, node_no: node_cnt, is_leaf: node.leaf}); | |
| }); | |
| //alert("node count: " + node_cnt); | |
| return nodes; | |
| } | |
| function set_projection(projection, path, json, map_width, map_height) { | |
| //reset projection | |
| projection | |
| .scale(1) | |
| .translate([0, 0]); | |
| //adjust projection to zoom in on NYC | |
| var this_c = d3.geo.centroid(json); | |
| //first rotate projection to NYC | |
| projection | |
| .rotate([(this_c[0] * -1), 0]) | |
| .center([0, this_c[1]]) | |
| .precision(.1); | |
| var b = path.bounds(json), | |
| s = .95 / Math.max((b[1][0] - b[0][0]) / map_width, (b[1][1] - b[0][1]) / map_height), | |
| t = [(map_width - s * (b[1][0] + b[0][0])) / 2, (map_height - s * (b[1][1] + b[0][1])) / 2]; | |
| //then set scale and center | |
| projection | |
| .scale(s) | |
| .translate(t); | |
| } | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment