(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; })();