###Google maps custom overlay with D3 for live position update
Based on http://bl.ocks.org/mbostock/899711
Demostrates:
- Live update of gps positions
- Move to new positions with transition with data update
###Google maps custom overlay with D3 for live position update
Based on http://bl.ocks.org/mbostock/899711
Demostrates:
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Google maps GpsSensor layer with D3 for live position update</title> | |
<script src="http://maps.google.com/maps/api/js??v=3.exp&sensor=true"></script> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.3.3/d3.min.js"></script> | |
<script src="./sample_data.js" charset="utf-8"></script> | |
<style type="text/css"> | |
html, body, #map { | |
width: 100%; | |
height: 100%; | |
margin: 0; | |
padding: 0; | |
} | |
.stations, .stations svg { | |
position: absolute; | |
} | |
.stations svg { | |
width: 60px; | |
height: 20px; | |
padding-right: 100px; | |
font: 10px sans-serif; | |
} | |
.stations circle { | |
fill: brown; | |
stroke: black; | |
stroke-width: 1.5px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="map"></div> | |
<script> | |
function GPSSensor(initData) { | |
//state information | |
var _div = null; | |
var _data = initData; | |
var _projection = null; | |
function transform(d) { | |
var padding = 10; | |
d = new google.maps.LatLng(d.Lat, d.Long); | |
d = _projection.fromLatLngToDivPixel(d); | |
return d3.select(this) | |
.style("left", (d.x - padding) + "px") | |
.style("top", (d.y - padding) + "px"); | |
} | |
function transformWithEase(d) { | |
var padding = 10; | |
d = new google.maps.LatLng(d.Lat, d.Long); | |
d = _projection.fromLatLngToDivPixel(d); | |
return d3.select(this) | |
.transition().duration(300) | |
.style("left", (d.x - padding) + "px") | |
.style("top", (d.y - padding) + "px"); | |
} | |
//superclass methods for google maps | |
this.onAdd = function() { | |
_div = d3.select(this.getPanes().overlayLayer) | |
.append("div") | |
.attr("class", "stations"); | |
}; | |
this.draw = function () { | |
var padding = 10; | |
_projection = this.getProjection(); | |
var marker = _div.selectAll("svg") | |
.data(_data, function (d) { return d.Key; }) | |
.each(transform) // update existing markers | |
.enter().append("svg:svg") | |
.each(transform) | |
.attr("class", "marker"); | |
// Add a circle. | |
marker.append("svg:circle") | |
.attr("r", 4.5) | |
.attr("cx", padding) | |
.attr("cy", padding); | |
// Add a label. | |
marker.append("svg:text") | |
.attr("x", padding + 7) | |
.attr("y", padding) | |
.attr("dy", ".31em") | |
.text(function (d) { return d.Key; }); | |
}; | |
this.onRemove = function () { | |
_div.remove(); | |
}; | |
this.update = function (data) { | |
//update internal data which drive redrawing on zoom_changed | |
for (var i = 0; i < data.length; i++) { | |
var found = false; | |
for (var j = 0; j < _data.length; j++) { | |
if (_data[j].Key === data[i].Key) { | |
found = true; | |
_data[j].Lat = data[i].Lat; | |
_data[j].Long = data[i].Long; | |
} | |
} | |
if (!found) | |
_data.push(data[i]); | |
} | |
//this.draw(); | |
_div.selectAll("svg") | |
.data(_data, function (d) { return d.Key; }) | |
.each(transformWithEase); | |
}; | |
} | |
//subclassing | |
GPSSensor.prototype = new google.maps.OverlayView(); | |
// Create the Google Map | |
var map = new google.maps.Map(d3.select("#map").node(), { | |
zoom: 15, | |
center: new google.maps.LatLng(-33.690126, 150.924187), | |
mapTypeId: google.maps.MapTypeId.ROADMAP | |
}); | |
var sensorData = [ | |
sensor1Data[0] | |
//,sensor2Data[0] | |
]; | |
var sensorLayer = new GPSSensor(sensorData); | |
sensorLayer.setMap(map); | |
//simulate position update by receiving positions at random interval | |
var d1Cnt = 1, d2Cnt = 1; | |
(function loop() { | |
var randTimeout = Math.round(Math.random() * (3000 - 500)) + 500; | |
setTimeout(function () { | |
var whichSensor = Math.floor(Math.random() * 2); | |
if (whichSensor==0 && d1Cnt < sensor1Data.length) | |
sensorLayer.update([sensor1Data[d1Cnt++]]); | |
else if (whichSensor==1 && d2Cnt < sensor2Data.length) | |
sensorLayer.update([sensor2Data[d2Cnt++]]); | |
loop(); | |
}, randTimeout); | |
}()); | |
</script> | |
</body> | |
</html> |
var sensor1Data=[{"Key":"sensor1","DateTime":"2013-09-04T09:25:00+10:00","Lat":-33.697746,"Long":150.948976,"Heading":142.0,"Speed":3},{"Key":"sensor1","DateTime":"2013-09-04T09:25:11+10:00","Lat":-33.697904,"Long":150.949018,"Heading":150.0,"Speed":14},{"Key":"sensor1","DateTime":"2013-09-04T09:25:21+10:00","Lat":-33.697899,"Long":150.949596,"Heading":71.0,"Speed":31},{"Key":"sensor1","DateTime":"2013-09-04T09:25:34+10:00","Lat":-33.697633,"Long":150.950278,"Heading":60.0,"Speed":6},{"Key":"sensor1","DateTime":"2013-09-04T09:25:44+10:00","Lat":-33.698053,"Long":150.95093,"Heading":136.0,"Speed":42},{"Key":"sensor1","DateTime":"2013-09-04T09:25:54+10:00","Lat":-33.699021,"Long":150.951631,"Heading":172.0,"Speed":42},{"Key":"sensor1","DateTime":"2013-09-04T09:26:04+10:00","Lat":-33.699581,"Long":150.951561,"Heading":249.0,"Speed":31},{"Key":"sensor1","DateTime":"2013-09-04T09:26:14+10:00","Lat":-33.699943,"Long":150.950365,"Heading":254.0,"Speed":33},{"Key":"sensor1","DateTime":"2013-09-04T09:26:24+10:00","Lat":-33.699609,"Long":150.949634,"Heading":312.0,"Speed":44},{"Key":"sensor1","DateTime":"2013-09-04T09:26:34+10:00","Lat":-33.698913,"Long":150.948713,"Heading":314.0,"Speed":43},{"Key":"sensor1","DateTime":"2013-09-04T09:26:44+10:00","Lat":-33.697978,"Long":150.947943,"Heading":350.0,"Speed":50},{"Key":"sensor1","DateTime":"2013-09-04T09:26:54+10:00","Lat":-33.696968,"Long":150.947936,"Heading":295.0,"Speed":25},{"Key":"sensor1","DateTime":"2013-09-04T09:27:04+10:00","Lat":-33.697046,"Long":150.946673,"Heading":251.0,"Speed":46},{"Key":"sensor1","DateTime":"2013-09-04T09:27:14+10:00","Lat":-33.697473,"Long":150.945416,"Heading":250.0,"Speed":50},{"Key":"sensor1","DateTime":"2013-09-04T09:27:24+10:00","Lat":-33.697876,"Long":150.943949,"Heading":257.0,"Speed":54},{"Key":"sensor1","DateTime":"2013-09-04T09:27:34+10:00","Lat":-33.697866,"Long":150.942326,"Heading":277.0,"Speed":50},{"Key":"sensor1","DateTime":"2013-09-04T09:27:44+10:00","Lat":-33.697496,"Long":150.941381,"Heading":342.0,"Speed":37},{"Key":"sensor1","DateTime":"2013-09-04T09:27:54+10:00","Lat":-33.696556,"Long":150.941026,"Heading":341.0,"Speed":19},{"Key":"sensor1","DateTime":"2013-09-04T09:28:04+10:00","Lat":-33.696424,"Long":150.940913,"Heading":294.0,"Speed":16},{"Key":"sensor1","DateTime":"2013-09-04T09:28:14+10:00","Lat":-33.696531,"Long":150.939619,"Heading":261.0,"Speed":54},{"Key":"sensor1","DateTime":"2013-09-04T09:28:24+10:00","Lat":-33.696653,"Long":150.938214,"Heading":264.0,"Speed":29},{"Key":"sensor1","DateTime":"2013-09-04T09:28:34+10:00","Lat":-33.696744,"Long":150.937811,"Heading":283.0,"Speed":21},{"Key":"sensor1","DateTime":"2013-09-04T09:28:44+10:00","Lat":-33.695909,"Long":150.937266,"Heading":319.0,"Speed":49},{"Key":"sensor1","DateTime":"2013-09-04T09:28:54+10:00","Lat":-33.695131,"Long":150.935999,"Heading":294.0,"Speed":53},{"Key":"sensor1","DateTime":"2013-09-04T09:29:04+10:00","Lat":-33.694929,"Long":150.934709,"Heading":269.0,"Speed":49},{"Key":"sensor1","DateTime":"2013-09-04T09:29:14+10:00","Lat":-33.695546,"Long":150.933127,"Heading":229.0,"Speed":64},{"Key":"sensor1","DateTime":"2013-09-04T09:29:24+10:00","Lat":-33.696386,"Long":150.931827,"Heading":243.0,"Speed":29},{"Key":"sensor1","DateTime":"2013-09-04T09:29:34+10:00","Lat":-33.695751,"Long":150.931546,"Heading":359.0,"Speed":46},{"Key":"sensor1","DateTime":"2013-09-04T09:29:44+10:00","Lat":-33.694866,"Long":150.931501,"Heading":9.0,"Speed":36},{"Key":"sensor1","DateTime":"2013-09-04T09:29:54+10:00","Lat":-33.693836,"Long":150.931094,"Heading":327.0,"Speed":51},{"Key":"sensor1","DateTime":"2013-09-04T09:30:04+10:00","Lat":-33.692824,"Long":150.929959,"Heading":316.0,"Speed":56},{"Key":"sensor1","DateTime":"2013-09-04T09:30:14+10:00","Lat":-33.691719,"Long":150.929204,"Heading":346.0,"Speed":55},{"Key":"sensor1","DateTime":"2013-09-04T09:30:24+10:00","Lat":-33.690373,"Long":150.928637,"Heading":330.0,"Speed":51},{"Key":"sensor1","DateTime":"2013-09-04T09:30:51+10:00","Lat":-33.690083,"Long":150.928431,"Heading":329.0,"Speed":15},{"Key":"sensor1","DateTime":"2013-09-04T09:31:01+10:00","Lat":-33.689213,"Long":150.927736,"Heading":323.0,"Speed":50},{"Key":"sensor1","DateTime":"2013-09-04T09:31:11+10:00","Lat":-33.688236,"Long":150.926652,"Heading":311.0,"Speed":53},{"Key":"sensor1","DateTime":"2013-09-04T09:31:21+10:00","Lat":-33.688266,"Long":150.925791,"Heading":230.0,"Speed":43},{"Key":"sensor1","DateTime":"2013-09-04T09:31:32+10:00","Lat":-33.688799,"Long":150.924939,"Heading":231.0,"Speed":3},{"Key":"sensor1","DateTime":"2013-09-04T09:32:14+10:00","Lat":-33.688856,"Long":150.924861,"Heading":230.0,"Speed":22},{"Key":"sensor1","DateTime":"2013-09-04T09:32:24+10:00","Lat":-33.689453,"Long":150.924032,"Heading":215.0,"Speed":16},{"Key":"sensor1","DateTime":"2013-09-04T09:32:34+10:00","Lat":-33.689794,"Long":150.924321,"Heading":145.0,"Speed":4},{"Key":"sensor1","DateTime":"2013-09-04T09:32:44+10:00","Lat":-33.689846,"Long":150.924357,"Heading":147.0,"Speed":16},{"Key":"sensor1","DateTime":"2013-09-04T09:32:56+10:00","Lat":-33.690116,"Long":150.924631,"Heading":93.0,"Speed":5},{"Key":"sensor1","DateTime":"2013-09-04T09:33:06+10:00","Lat":-33.689966,"Long":150.924704,"Heading":87.0,"Speed":1},{"Key":"sensor1","DateTime":"2013-09-04T09:33:19+10:00","Lat":-33.689864,"Long":150.924732,"Heading":87.0,"Speed":1}]; | |
var sensor2Data=[{"Key":"sensor2","DateTime":"2013-09-04T09:41:09+10:00","Lat":-33.690126,"Long":150.924187,"Heading":286.0,"Speed":22},{"Key":"sensor2","DateTime":"2013-09-04T09:41:24+10:00","Lat":-33.689758,"Long":150.924869,"Heading":57.0,"Speed":8},{"Key":"sensor2","DateTime":"2013-09-04T09:41:34+10:00","Lat":-33.689363,"Long":150.925171,"Heading":324.0,"Speed":29},{"Key":"sensor2","DateTime":"2013-09-04T09:41:48+10:00","Lat":-33.689008,"Long":150.924869,"Heading":319.0,"Speed":7},{"Key":"sensor2","DateTime":"2013-09-04T09:41:58+10:00","Lat":-33.688389,"Long":150.925307,"Heading":53.0,"Speed":52},{"Key":"sensor2","DateTime":"2013-09-04T09:42:15+10:00","Lat":-33.687908,"Long":150.926064,"Heading":68.0,"Speed":21},{"Key":"sensor2","DateTime":"2013-09-04T09:42:25+10:00","Lat":-33.688493,"Long":150.927111,"Heading":135.0,"Speed":53},{"Key":"sensor2","DateTime":"2013-09-04T09:42:35+10:00","Lat":-33.689559,"Long":150.928136,"Heading":146.0,"Speed":55},{"Key":"sensor2","DateTime":"2013-09-04T09:42:45+10:00","Lat":-33.690834,"Long":150.929006,"Heading":161.0,"Speed":58},{"Key":"sensor2","DateTime":"2013-09-04T09:42:55+10:00","Lat":-33.692134,"Long":150.929547,"Heading":155.0,"Speed":52},{"Key":"sensor2","DateTime":"2013-09-04T09:43:05+10:00","Lat":-33.693269,"Long":150.930692,"Heading":132.0,"Speed":60},{"Key":"sensor2","DateTime":"2013-09-04T09:43:15+10:00","Lat":-33.694543,"Long":150.931611,"Heading":166.0,"Speed":54},{"Key":"sensor2","DateTime":"2013-09-04T09:43:25+10:00","Lat":-33.695896,"Long":150.931636,"Heading":178.0,"Speed":56},{"Key":"sensor2","DateTime":"2013-09-04T09:43:35+10:00","Lat":-33.696059,"Long":150.932322,"Heading":53.0,"Speed":55},{"Key":"sensor2","DateTime":"2013-09-04T09:43:45+10:00","Lat":-33.695139,"Long":150.933694,"Heading":62.0,"Speed":59},{"Key":"sensor2","DateTime":"2013-09-04T09:43:55+10:00","Lat":-33.694844,"Long":150.935238,"Heading":98.0,"Speed":47},{"Key":"sensor2","DateTime":"2013-09-04T09:44:05+10:00","Lat":-33.695446,"Long":150.936783,"Heading":127.0,"Speed":59},{"Key":"sensor2","DateTime":"2013-09-04T09:44:15+10:00","Lat":-33.696499,"Long":150.937849,"Heading":138.0,"Speed":39},{"Key":"sensor2","DateTime":"2013-09-04T09:44:25+10:00","Lat":-33.696484,"Long":150.939318,"Heading":83.0,"Speed":58},{"Key":"sensor2","DateTime":"2013-09-04T09:44:35+10:00","Lat":-33.696318,"Long":150.940849,"Heading":67.0,"Speed":27},{"Key":"sensor2","DateTime":"2013-09-04T09:44:45+10:00","Lat":-33.696946,"Long":150.941224,"Heading":163.0,"Speed":53},{"Key":"sensor2","DateTime":"2013-09-04T09:44:55+10:00","Lat":-33.697781,"Long":150.941828,"Heading":96.0,"Speed":39},{"Key":"sensor2","DateTime":"2013-09-04T09:45:05+10:00","Lat":-33.697906,"Long":150.943258,"Heading":91.0,"Speed":52},{"Key":"sensor2","DateTime":"2013-09-04T09:45:15+10:00","Lat":-33.697646,"Long":150.944784,"Heading":71.0,"Speed":51},{"Key":"sensor2","DateTime":"2013-09-04T09:45:25+10:00","Lat":-33.697219,"Long":150.946111,"Heading":68.0,"Speed":45},{"Key":"sensor2","DateTime":"2013-09-04T09:45:35+10:00","Lat":-33.696919,"Long":150.947521,"Heading":89.0,"Speed":48},{"Key":"sensor2","DateTime":"2013-09-04T09:45:45+10:00","Lat":-33.697184,"Long":150.948046,"Heading":190.0,"Speed":36},{"Key":"sensor2","DateTime":"2013-09-04T09:45:55+10:00","Lat":-33.698413,"Long":150.948128,"Heading":149.0,"Speed":49},{"Key":"sensor2","DateTime":"2013-09-04T09:46:05+10:00","Lat":-33.699179,"Long":150.949118,"Heading":138.0,"Speed":41},{"Key":"sensor2","DateTime":"2013-09-04T09:46:15+10:00","Lat":-33.699896,"Long":150.950166,"Heading":115.0,"Speed":27},{"Key":"sensor2","DateTime":"2013-09-04T09:46:25+10:00","Lat":-33.699619,"Long":150.951386,"Heading":69.0,"Speed":57},{"Key":"sensor2","DateTime":"2013-09-04T09:46:35+10:00","Lat":-33.699071,"Long":150.951633,"Heading":353.0,"Speed":45},{"Key":"sensor2","DateTime":"2013-09-04T09:46:45+10:00","Lat":-33.697946,"Long":150.950795,"Heading":311.0,"Speed":57},{"Key":"sensor2","DateTime":"2013-09-04T09:46:55+10:00","Lat":-33.697844,"Long":150.949824,"Heading":247.0,"Speed":44},{"Key":"sensor2","DateTime":"2013-09-04T09:47:05+10:00","Lat":-33.697846,"Long":150.948998,"Heading":334.0,"Speed":27}]; |
see live at http://bl.ocks.org/marufbd/7191903