Skip to content

Instantly share code, notes, and snippets.

@madelfio
Last active December 26, 2015 04:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save madelfio/7094945 to your computer and use it in GitHub Desktop.
Save madelfio/7094945 to your computer and use it in GitHub Desktop.
Map Markers

A MapMarker widget for Google Maps API markers. Transitions are smooth even while zooming.

<!doctype html>
<meta charset="utf-8">
<style>
html, body, #map-canvas {height: 100%; width: 100%;}
.marker {
position:relative;
}
.marker-container {
position:relative;
left:-50%;
}
.marker-dot {
display:block;
position:relative;
width: 8px;
height: 8px;
left: 50%;
margin-left: -4px;
border-radius: 6;
background-color: red;
}
.marker-label {
display:block;
position:relative;
padding: 2px;
margin:2px;
border: 1px solid black;
background-color: rgba(255, 255, 255, 0.9);
border-radius:2px;
text-align:center;
white-space:nowrap;
}
</style>
<body>
<div id="map-canvas">
</div>
</body>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="map-marker.js"></script>
<script>
"use strict";
function initializeMap() {
var mapOptions = {
center: new google.maps.LatLng(10, 0),
zoom: 1,
mapTypeId: google.maps.MapTypeId.ROADMAP,
streetViewControl: false
};
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
// hack to get easy access to projection
// from http://stackoverflow.com/questions/1538681/
var overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(map);
var proj = function() {
return overlay.getProjection();
};
var data = [
{name: 'Point A', latitude: -20, longitude: 20},
{name: 'Point B', latitude: -1, longitude: 40},
];
var path1 = [
{lat: 38.895, lng: -77.036, dest: 'Washington'},
{lat: 51.507, lng: -0.127, dest: 'London'},
{lat: 12.967, lng: 77.567, dest: 'Bangalore'},
{lat: 39.914, lng: 116.391667, dest: 'Beijing'}
];
data[1].latitude = path1[0].lat;
data[1].longitude = path1[0].lng;
var wrapMarker = MapMarker().gmap(map);
var marker_divs = d3.select('#map-canvas').selectAll('div.marker').data(data);
var new_markers = marker_divs.enter()
.append('div')
.attr('class', 'marker')
.call(wrapMarker)
.append('div')
.attr('class', 'marker-container');
new_markers
.append('div')
.attr('class', 'marker-dot');
new_markers
.append('div')
.attr('class', 'marker-label')
.html(function(d) {return d.name;});
window.i = setInterval(function() {
data[0].longitude = data[0].longitude - 20;
marker_divs
.filter(function(d, i) {return i==0;})
.style('opacity', 0.6)
.transition().duration(1000).ease('linear')
.call(wrapMarker)
.each('end', function() {d3.select(this).style('opacity', 1.0);});
}, 1300);
var t = 0;
setInterval(function() {
t += 1;
var leg = path1[t % path1.length];
data[1].latitude = leg.lat;
data[1].longitude = leg.lng;
data[1].dest = leg.dest;
var md = marker_divs.filter(function(d, i) {return i==1;});
md.selectAll('div.marker-label')
.html(function(d) {return d.name + '<br />(to ' + d.dest + ')';});
md.transition().duration(4500).ease('linear')
.call(wrapMarker);
}, 5000);
}
google.maps.event.addDomListener(window, 'load', initializeMap);
</script>
(function() {
"use strict";
// Custom Overlay interfaces with Google Maps
function CustomOverlay(div, options) {
this.setValues(options);
this.div_ = div;
}
CustomOverlay.prototype = new google.maps.OverlayView();
CustomOverlay.prototype.onAdd = function() {
var pane = this.getPanes().overlayLayer;
pane.appendChild(this.div_);
};
CustomOverlay.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
};
CustomOverlay.prototype.ll = function(x, y) {
var projection = this.getProjection();
return projection.fromDivPixelToLatLng(new google.maps.Point(x, y));
};
CustomOverlay.prototype.xy = function(ll) {
var projection = this.getProjection();
return projection.fromLatLngToDivPixel(ll);
};
CustomOverlay.prototype.draw = function() {
var position = this.xy(this.position);
this.div_.style.left = position.x + 'px';
this.div_.style.top = position.y + 'px';
};
function MapMarker() {
var _gmap;
function map_marker(d3_selection) {
d3_selection.each(function(d) {
var position = new google.maps.LatLng(+d.latitude, +d.longitude);
if (!this.__overlay__) {
// initialize overlay if not already created
d3.select(this).style('position', 'absolute');
this.__overlay__ = new CustomOverlay(this, {map: _gmap, position: position});
} else {
// update existing overlay to correct position
var o = this.__overlay__,
sel = d3.select(this);
window.sel = sel;
o.old_position = o.position;
o.position = position;
console.log('old_position: ' + o.old_position + 'new_position: ' + o.position);
if (d3_selection.duration) {
// preserve transitions
var left = parseFloat(this.style.left),
top = parseFloat(this.style.top),
ll0 = o.old_position;//this.__overlay__.ll(left, top);
var translateMarker = function(attr) {
return function (d, i, a) {
return function (t) {
var v0 = o.xy(ll0)[attr],
v1 = o.xy(position)[attr];
return (v0 * (1 - t) + v1 * t) + 'px';
};
};
};
var translateMarkerGC = function(attr) {
return function (d, i, a) {
return function (t) {
var lat1 = ll0.lat() * (Math.PI/180),
lon1 = ll0.lng() * (Math.PI/180),
lat2 = position.lat() * (Math.PI/180),
lon2 = position.lng() * (Math.PI/180);
var d = Math.acos(Math.sin(lat1) * Math.sin(lat2) +
Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2)),
A = Math.sin((1 - t) * d) / Math.sin(d),
B = Math.sin(t * d) / Math.sin(d),
x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) * Math.cos(lon2),
y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) * Math.sin(lon2),
z = A * Math.sin(lat1) + B * Math.sin(lat2),
lat_f = Math.atan2(z, Math.sqrt(x*x + y*y)) * (180/Math.PI),
lon_f = Math.atan2(y, x) * (180/Math.PI);
return o.xy(new google.maps.LatLng(lat_f, lon_f))[attr] + 'px';
};
};
};
sel.transition()
.styleTween('left', translateMarkerGC('x'))
.styleTween('top', translateMarkerGC('y'));
} else {
var xy = o.xy(position);
sel.style('left', xy.x + 'px')
.style('top', xy.y + 'px');
}
}
});
}
map_marker.gmap = function(x) {
if (!arguments.length) return _gmap;
_gmap = x;
return map_marker;
};
return map_marker;
}
window.MapMarker = MapMarker;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment