Skip to content

Instantly share code, notes, and snippets.

@ResidentMario
Last active July 26, 2016 02:17
Show Gist options
  • Save ResidentMario/2159ca63831998f05c3fe64933c0df09 to your computer and use it in GitHub Desktop.
Save ResidentMario/2159ca63831998f05c3fe64933c0df09 to your computer and use it in GitHub Desktop.
New York City Sample Bike Path (D3)
<!DOCTYPE html>
<html>
<head>
<title>A Week in the Life of a CitiBike</title>
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0; padding: 0 }
#map-canvas { height: 100% }
</style>
<link href='https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.css'
rel='stylesheet' type='text/css'/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
</head>
<body>
<div id="map-canvas"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.js"></script>
<script src="https://d3js.org/d3.v3.js"></script>
<script src="https://d3js.org/d3-queue.v3.js"></script>
<script src="L.D3SvgOverlay.js"></script>
<script>
// Initializes the basemap.
var map = L.map("map-canvas",{center:[40.773889, -73.983611],zoom:12});
L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <a href="http://cartodb.com/attributions">CartoDB</a>',
subdomains: 'abcd',
maxZoom: 19
}).addTo(map);
// D3-SVG-Overlay callback wrapper. Leaflet is used only for the base map, all of the programmatic action occurs
// in d3 code inside of this callback.
var mapOverlay = L.d3SvgOverlay(function(sel,proj){
// Paints a single sampler path.
function paintPath(linearray) {
console.log(linearray);
// Define x and y conversions.
var line = d3.svg.line()
.x(function(d) { console.log(d); return proj.latLngToLayerPoint(d).x})
.y(function(d) { console.log(d); return proj.latLngToLayerPoint(d).y});
console.log(line);
sel.append("path")
.datum(linearray)
// TODO: Troubleshoot why the line is so funky.
.attr({
"class": "sample-line",
"d": line,
"fill": "transparent",
"stroke": "steelblue",
"stroke-width": 0.1,
"shape-rendering": "crispEdges"
})
}
// Paints a single sampler path (paints things as a pile of points).
// Test method, now disused.
function paintPointPath(pointarray) {
console.log(pointarray);
sel.append('g')
.selectAll('circle')
.data(pointarray)
.enter()
.append('circle')
.attr({
"cx": function (d) {
return proj.latLngToLayerPoint(d).x;
},
"cy": function (d) {
return proj.latLngToLayerPoint(d).y;
},
"r": 0.2
});
}
// Paints all of the paths.
function paintPathSampler() {
d3.json("path_sampler.json", function (data) {
console.log(data[0]);
paintPath(data[0]);
paintPath(data[1]);
// paintPointPath(data[0]);
});
}
// function paintPathSampler() {
// d3.json("../../static/post_assets/citibike/bike_week_sampler.json", function (data) {
// console.log(data);
// console.log(data[0]);
//
// paintPointPath(data[0]);
//
// });
// }
function delayedHello(callback) {
setTimeout(function() {
console.log("Hello!");
callback(null);
}, 10);
}
q = d3.queue();
q.defer(paintPathSampler);
q.defer(delayedHello);
q.await(function(error) {
if (error) throw error;
console.log("Goodbye!");
});
// ACTUAL CODE
// Now we define the functions to be called as part of the visualization.
// Set up a d3 queue for managing event order.
// var q = d3.queue();
// d3.csv("nyc-starbucks-locations.csv", function(data) {
// sel.selectAll('circle')
// .data(data)
// .enter()
// .append('circle')
// .attr({
// "cx": function(d) { return proj.latLngToLayerPoint([d.latitude, d.longitude]).x; },
// "cy": function(d) { return proj.latLngToLayerPoint([d.latitude, d.longitude]).y; },
// "r": 4
// });
// });
});
// Add overlay to map.
mapOverlay.addTo(map);
</script>
</body>
</html>
/**
* Copyright 2015 Teralytics AG
*
* @author Kirill Zhuravlev <kirill.zhuravlev@teralytics.ch>
*
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
define(['leaflet', 'd3'], factory);
} else if (typeof module === 'object' && module.exports) {
module.exports = factory(require('leaflet', 'd3'));
} else {
factory(L, d3);
}
}(function (L, d3) {
// Check requirements
if (typeof d3 == "undefined") {
throw "D3 SVG Overlay for Leaflet requires D3 library loaded first";
}
if (typeof L == "undefined") {
throw "D3 SVG Overlay for Leaflet requires Leaflet library loaded first";
}
// Tiny stylesheet bundled here instead of a separate file
if (L.version >= "1.0") {
d3.select("head")
.append("style").attr("type", "text/css")
.text("g.d3-overlay *{pointer-events:visiblePainted;}");
}
// Class definition
L.D3SvgOverlay = (L.version < "1.0" ? L.Class : L.Layer).extend({
includes: (L.version < "1.0" ? L.Mixin.Events : []),
_undef: function(a){ return typeof a == "undefined" },
_options: function (options) {
if (this._undef(options)) {
return this.options;
}
options.zoomHide = this._undef(options.zoomHide) ? false : options.zoomHide;
options.zoomDraw = this._undef(options.zoomDraw) ? true : options.zoomDraw;
return this.options = options;
},
_disableLeafletRounding: function(){
this._leaflet_round = L.Point.prototype._round;
L.Point.prototype._round = function(){ return this; };
},
_enableLeafletRounding: function(){
L.Point.prototype._round = this._leaflet_round;
},
draw: function () {
this._disableLeafletRounding();
this._drawCallback(this.selection, this.projection, this.map.getZoom());
this._enableLeafletRounding();
},
initialize: function (drawCallback, options) { // (Function(selection, projection)), (Object)options
this._options(options || {});
this._drawCallback = drawCallback;
},
// Handler for "viewreset"-like events, updates scale and shift after the animation
_zoomChange: function (evt) {
this._disableLeafletRounding();
var newZoom = this._undef(evt.zoom) ? this.map._zoom : evt.zoom; // "viewreset" event in Leaflet has not zoom/center parameters like zoomanim
this._zoomDiff = newZoom - this._zoom;
this._scale = Math.pow(2, this._zoomDiff);
this.projection.scale = this._scale;
this._shift = this.map.latLngToLayerPoint(this._wgsOrigin)
._subtract(this._wgsInitialShift.multiplyBy(this._scale));
var shift = ["translate(", this._shift.x, ",", this._shift.y, ") "];
var scale = ["scale(", this._scale, ",", this._scale,") "];
this._rootGroup.attr("transform", shift.concat(scale).join(""));
if (this.options.zoomDraw) { this.draw() }
this._enableLeafletRounding();
},
onAdd: function (map) {
this.map = map;
var _layer = this;
// SVG element
if (L.version < "1.0") {
map._initPathRoot();
this._svg = d3.select(map._panes.overlayPane)
.select("svg");
this._rootGroup = this._svg.append("g");
} else {
this._svg = L.svg();
map.addLayer(this._svg);
this._rootGroup = d3.select(this._svg._rootGroup).classed("d3-overlay", true);
}
this._rootGroup.classed("leaflet-zoom-hide", this.options.zoomHide);
this.selection = this._rootGroup;
// Init shift/scale invariance helper values
this._pixelOrigin = map.getPixelOrigin();
this._wgsOrigin = L.latLng([0, 0]);
this._wgsInitialShift = this.map.latLngToLayerPoint(this._wgsOrigin);
this._zoom = this.map.getZoom();
this._shift = L.point(0, 0);
this._scale = 1;
// Create projection object
this.projection = {
latLngToLayerPoint: function (latLng, zoom) {
zoom = _layer._undef(zoom) ? _layer._zoom : zoom;
var projectedPoint = _layer.map.project(L.latLng(latLng), zoom)._round();
return projectedPoint._subtract(_layer._pixelOrigin);
},
layerPointToLatLng: function (point, zoom) {
zoom = _layer._undef(zoom) ? _layer._zoom : zoom;
var projectedPoint = L.point(point).add(_layer._pixelOrigin);
return _layer.map.unproject(projectedPoint, zoom);
},
unitsPerMeter: 256 * Math.pow(2, _layer._zoom) / 40075017,
map: _layer.map,
layer: _layer,
scale: 1
};
this.projection._projectPoint = function(x, y) {
var point = _layer.projection.latLngToLayerPoint(new L.LatLng(y, x));
this.stream.point(point.x, point.y);
};
this.projection.pathFromGeojson =
d3.geo.path().projection(d3.geo.transform({point: this.projection._projectPoint}));
// Compatibility with v.1
this.projection.latLngToLayerFloatPoint = this.projection.latLngToLayerPoint;
this.projection.getZoom = this.map.getZoom.bind(this.map);
this.projection.getBounds = this.map.getBounds.bind(this.map);
this.selection = this._rootGroup;
if (L.version < "1.0") map.on("viewreset", this._zoomChange, this);
// Initial draw
this.draw();
},
// Leaflet 1.0
getEvents: function() { return {zoomend: this._zoomChange}; },
onRemove: function (map) {
if (L.version < "1.0") {
map.off("viewreset", this._zoomChange, this);
this._rootGroup.remove();
} else {
this._svg.remove();
}
},
addTo: function (map) {
map.addLayer(this);
return this;
}
});
L.D3SvgOverlay.version = "2.2";
// Factory method
L.d3SvgOverlay = function (drawCallback, options) {
return new L.D3SvgOverlay(drawCallback, options);
};
}));
[[[40.76529, -73.98208], [40.76476, -73.98225], [40.76457, -73.98232], [40.76441, -73.98238], [40.76438, -73.98239], [40.76434, -73.98241], [40.76423, -73.98245], [40.76412, -73.98249], [40.76405, -73.98252], [40.76402, -73.98254], [40.76396, -73.98257], [40.76351, -73.98281], [40.76339, -73.98288], [40.7633, -73.98293], [40.76318, -73.983], [40.76282, -73.98321], [40.76273, -73.98326], [40.76264, -73.98332], [40.76219, -73.9836], [40.76207, -73.98368], [40.76178, -73.98387], [40.76145, -73.98411], [40.7608, -73.98452], [40.76072, -73.98456], [40.76052, -73.98465], [40.76013, -73.98487], [40.76013, -73.98487], [40.75999, -73.98453], [40.75984, -73.98418], [40.75984, -73.98418], [40.75952, -73.98441], [40.7592, -73.98461], [40.75858, -73.98507], [40.75833, -73.98526], [40.75796, -73.98553], [40.75778, -73.98568], [40.75734, -73.986], [40.7567, -73.98648], [40.75603, -73.98695], [40.75603, -73.98695], [40.75582, -73.98644], [40.75582, -73.98644], [40.75505, -73.98668], [40.75434, -73.98691], [40.75362, -73.98713], [40.75332, -73.98723], [40.75303, -73.98732], [40.75291, -73.98735], [40.75218, -73.98754], [40.75187, -73.98761], [40.75183, -73.98762], [40.75145, -73.98771], [40.7512, -73.98774], [40.7507, -73.98783], [40.75036, -73.98789], [40.74988, -73.98798], [40.74968, -73.98804], [40.74957, -73.98809], [40.74948, -73.98814], [40.74925, -73.9883], [40.74924, -73.9883], [40.74923, -73.98829], [40.74919, -73.98819], [40.74912, -73.98804], [40.74912, -73.98802], [40.74911, -73.98802], [40.7491, -73.98801], [40.749, -73.98803], [40.74898, -73.98805], [40.74896, -73.98807], [40.74894, -73.98808], [40.74881, -73.9881], [40.74869, -73.98812], [40.74863, -73.98813], [40.74858, -73.98814], [40.74845, -73.98817], [40.74831, -73.9882], [40.74811, -73.98824], [40.74754, -73.98836], [40.74735, -73.98839], [40.74721, -73.98841], [40.74713, -73.98843], [40.74707, -73.98844], [40.74679, -73.98849], [40.74604, -73.98858], [40.74582, -73.98867], [40.74559, -73.98872], [40.7453, -73.98877], [40.74455, -73.9889], [40.74435, -73.98894], [40.74412, -73.98899], [40.74392, -73.98903], [40.7438, -73.98904], [40.74368, -73.98905], [40.74337, -73.98911], [40.74316, -73.98915], [40.74303, -73.98917], [40.74294, -73.98919], [40.74261, -73.98924], [40.74257, -73.98924], [40.74253, -73.98924], [40.74252, -73.98923], [40.7425, -73.98922], [40.74231, -73.98902], [40.74231, -73.98902], [40.74225, -73.98906], [40.7419, -73.98931], [40.7418, -73.9894], [40.74155, -73.98959], [40.74087, -73.9901], [40.74025, -73.99053], [40.73967, -73.99096], [40.73908, -73.99139], [40.73849, -73.99181], [40.73792, -73.99222], [40.73732, -73.99267], [40.73669, -73.99315], [40.73624, -73.99347], [40.73618, -73.99352], [40.73602, -73.99363], [40.7353, -73.99416], [40.73467, -73.99459], [40.73403, -73.99507], [40.73344, -73.99552], [40.73284, -73.99594], [40.73225, -73.99638], [40.73185, -73.99668], [40.73141, -73.99698], [40.73141, -73.99698], [40.73147, -73.9971], [40.73216, -73.9985], [40.73222, -73.99863], [40.73222, -73.99863], [40.73211, -73.99873], [40.73164, -73.99913], [40.73105, -73.99962], [40.73105, -73.99962], [40.73067, -73.99886], [40.73067, -73.99886], [40.73046, -73.99903]]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment