A MapMarker widget for Google Maps API markers. Transitions are smooth even while zooming.
Last active
December 26, 2015 04:39
-
-
Save madelfio/7094945 to your computer and use it in GitHub Desktop.
Map Markers
This file contains 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"> | |
<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> |
This file contains 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
(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