diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index 8f6e6803d3b..76ae00f14dc 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -2762,11 +2762,6 @@ function makePlotFramework(gd) {
     fullLayout._glcontainer.enter().append('div')
         .classed('gl-container', true);
 
-    fullLayout._geocontainer = fullLayout._paperdiv.selectAll('.geo-container')
-        .data([0]);
-    fullLayout._geocontainer.enter().append('div')
-        .classed('geo-container', true);
-
     fullLayout._paperdiv.selectAll('.main-svg').remove();
 
     fullLayout._paper = fullLayout._paperdiv.insert('svg', ':first-child')
@@ -2810,6 +2805,9 @@ function makePlotFramework(gd) {
     // single ternary layer for the whole plot
     fullLayout._ternarylayer = fullLayout._paper.append('g').classed('ternarylayer', true);
 
+    // single geo layer for the whole plot
+    fullLayout._geolayer = fullLayout._paper.append('g').classed('geolayer', true);
+
     // upper shape layer
     // (only for shapes to be drawn above the whole plot, including subplots)
     var layerAbove = fullLayout._paper.append('g')
@@ -2824,7 +2822,6 @@ function makePlotFramework(gd) {
 
     // fill in image server scrape-svg
     fullLayout._glimages = fullLayout._paper.append('g').classed('glimages', true);
-    fullLayout._geoimages = fullLayout._paper.append('g').classed('geoimages', true);
 
     // lastly info (legend, annotations) and hover layers go on top
     // these are in a different svg element normally, but get collapsed into a single
diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js
index ceb2a4dbf64..c66c7e77216 100644
--- a/src/plot_api/subroutines.js
+++ b/src/plot_api/subroutines.js
@@ -306,12 +306,6 @@ exports.doModeBar = function(gd) {
     // no need to do this for gl2d subplots,
     // Plots.linkSubplots takes care of it all.
 
-    subplotIds = Plots.getSubplotIds(fullLayout, 'geo');
-    for(i = 0; i < subplotIds.length; i++) {
-        var geo = fullLayout[subplotIds[i]]._subplot;
-        geo.updateFx(fullLayout.hovermode);
-    }
-
     return Plots.previousPromises(gd);
 };
 
diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js
index 835873e5e39..a1a26928d28 100644
--- a/src/plots/cartesian/graph_interact.js
+++ b/src/plots/cartesian/graph_interact.js
@@ -974,6 +974,8 @@ function createHoverText(hoverData, opts) {
             contrastColor = tinycolor(traceColor).getBrightness() > 128 ?
                 '#000' : Color.background;
 
+        // to get custom 'name' labels pass cleanPoint
+        if(d.nameOverride !== undefined) d.name = d.nameOverride;
 
         if(d.name && d.zLabelVal === undefined) {
             // strip out our pseudo-html elements from d.name (if it exists at all)
diff --git a/src/plots/geo/geo.js b/src/plots/geo/geo.js
index 75bd8e37261..4d9549da7f2 100644
--- a/src/plots/geo/geo.js
+++ b/src/plots/geo/geo.js
@@ -25,7 +25,6 @@ var createGeoZoom = require('./zoom');
 var createGeoZoomReset = require('./zoom_reset');
 var constants = require('./constants');
 
-var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
 var topojsonUtils = require('../../lib/topojson_utils');
 var topojsonFeature = require('topojson-client').feature;
 
@@ -33,14 +32,12 @@ var topojsonFeature = require('topojson-client').feature;
 addProjectionsToD3(d3);
 
 
-function Geo(options, fullLayout) {
+function Geo(options) {
     this.id = options.id;
     this.graphDiv = options.graphDiv;
     this.container = options.container;
     this.topojsonURL = options.topojsonURL;
 
-    this.hoverContainer = null;
-
     this.topojsonName = null;
     this.topojson = null;
 
@@ -54,11 +51,7 @@ function Geo(options, fullLayout) {
     this.zoom = null;
     this.zoomReset = null;
 
-    this.xaxis = null;
-    this.yaxis = null;
-
     this.makeFramework();
-    this.updateFx(fullLayout.hovermode);
 
     this.traceHash = {};
 }
@@ -178,15 +171,6 @@ proto.onceTopojsonIsLoaded = function(geoCalcData, geoLayout) {
     this.render();
 };
 
-proto.updateFx = function(hovermode) {
-    this.showHover = (hovermode !== false);
-
-    // TODO should more strict, any layout.hovermode other
-    // then false will make all geo subplot display hover text.
-    // Instead each geo should have its own geo.hovermode
-    // to control hover visibility independently of other subplots.
-};
-
 proto.makeProjection = function(geoLayout) {
     var projLayout = geoLayout.projection,
         projType = projLayout.type,
@@ -232,38 +216,30 @@ proto.makePath = function() {
     this.path = d3.geo.path().projection(this.projection);
 };
 
-/*
- * <div this.container>
- *   <div this.geoDiv>
- *     <svg this.hoverContainer>
- *     <svg this.framework>
- */
 proto.makeFramework = function() {
-    var geoDiv = this.geoDiv = d3.select(this.container).append('div');
-    geoDiv
-        .attr('id', this.id)
-        .style('position', 'absolute');
-
-    // only choropleth traces use this,
-    // scattergeo traces use Fx.hover and fullLayout._hoverlayer
-    var hoverContainer = this.hoverContainer = geoDiv.append('svg');
-    hoverContainer
-        .attr(xmlnsNamespaces.svgAttrs)
-        .style({
-            'position': 'absolute',
-            'z-index': 20,
-            'pointer-events': 'none'
-        });
-
-    var framework = this.framework = geoDiv.append('svg');
+    var fullLayout = this.graphDiv._fullLayout;
+    var clipId = 'clip' + fullLayout._uid + this.id;
+
+    var defGroup = fullLayout._defs.selectAll('g.clips')
+        .data([0]);
+    defGroup.enter().append('g')
+        .classed('clips', true);
+
+    var clipDef = this.clipDef = defGroup.selectAll('#' + clipId)
+        .data([0]);
+
+    clipDef.enter().append('clipPath').attr('id', clipId)
+        .append('rect');
+
+    var framework = this.framework = d3.select(this.container).append('g');
+
     framework
-        .attr(xmlnsNamespaces.svgAttrs)
-        .attr({
-            'position': 'absolute',
-            'preserveAspectRatio': 'none'
-        });
+        .attr('class', 'geo ' + this.id)
+        .style('pointer-events', 'all')
+        .call(Drawing.setClipUrl, clipId);
 
-    framework.append('g').attr('class', 'bglayer')
+    framework.append('g')
+        .attr('class', 'bglayer')
         .append('rect');
 
     framework.append('g').attr('class', 'baselayer');
@@ -274,8 +250,6 @@ proto.makeFramework = function() {
     // N.B. disable dblclick zoom default
     framework.on('dblclick.zoom', null);
 
-    // TODO use clip paths instead of nested SVG
-
     this.xaxis = { _id: 'x' };
     this.yaxis = { _id: 'y' };
 };
@@ -286,28 +260,20 @@ proto.adjustLayout = function(geoLayout, graphSize) {
     var left = graphSize.l + graphSize.w * domain.x[0] + geoLayout._marginX,
         top = graphSize.t + graphSize.h * (1 - domain.y[1]) + geoLayout._marginY;
 
-    this.geoDiv.style({
-        left: left + 'px',
-        top: top + 'px',
-        width: geoLayout._width + 'px',
-        height: geoLayout._height + 'px'
-    });
+    Drawing.setTranslate(this.framework, left, top);
 
-    this.hoverContainer.attr({
+    var dimsAttrs = {
+        x: 0,
+        y: 0,
         width: geoLayout._width,
         height: geoLayout._height
-    });
+    };
 
-    this.framework.attr({
-        width: geoLayout._width,
-        height: geoLayout._height
-    });
+    this.clipDef.select('rect')
+        .attr(dimsAttrs);
 
     this.framework.select('.bglayer').select('rect')
-        .attr({
-            width: geoLayout._width,
-            height: geoLayout._height
-        })
+        .attr(dimsAttrs)
         .call(Color.fill, geoLayout.bgcolor);
 
     this.xaxis._offset = left;
diff --git a/src/plots/geo/index.js b/src/plots/geo/index.js
index 03cfbdf3393..baac5e1cf42 100644
--- a/src/plots/geo/index.js
+++ b/src/plots/geo/index.js
@@ -48,16 +48,13 @@ exports.plot = function plotGeo(gd) {
             geoCalcData = Plots.getSubplotCalcData(calcData, 'geo', geoId),
             geo = fullLayout[geoId]._subplot;
 
-        // If geo is not instantiated, create one!
         if(!geo) {
             geo = new Geo({
                 id: geoId,
                 graphDiv: gd,
-                container: fullLayout._geocontainer.node(),
+                container: fullLayout._geolayer.node(),
                 topojsonURL: gd._context.topojsonURL
-            },
-                fullLayout
-            );
+            });
 
             fullLayout[geoId]._subplot = geo;
         }
@@ -74,31 +71,8 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout)
         var oldGeo = oldFullLayout[oldGeoKey]._subplot;
 
         if(!newFullLayout[oldGeoKey] && !!oldGeo) {
-            oldGeo.geoDiv.remove();
+            oldGeo.framework.remove();
+            oldGeo.clipDef.remove();
         }
     }
 };
-
-exports.toSVG = function(gd) {
-    var fullLayout = gd._fullLayout,
-        geoIds = Plots.getSubplotIds(fullLayout, 'geo'),
-        size = fullLayout._size;
-
-    for(var i = 0; i < geoIds.length; i++) {
-        var geoLayout = fullLayout[geoIds[i]],
-            domain = geoLayout.domain,
-            geoFramework = geoLayout._subplot.framework;
-
-        geoFramework.attr('style', null);
-        geoFramework
-            .attr({
-                x: size.l + size.w * domain.x[0] + geoLayout._marginX,
-                y: size.t + size.h * (1 - domain.y[1]) + geoLayout._marginY,
-                width: geoLayout._width,
-                height: geoLayout._height
-            });
-
-        fullLayout._geoimages.node()
-            .appendChild(geoFramework.node());
-    }
-};
diff --git a/src/plots/geo/zoom_reset.js b/src/plots/geo/zoom_reset.js
index f022197219e..dc9a543d09f 100644
--- a/src/plots/geo/zoom_reset.js
+++ b/src/plots/geo/zoom_reset.js
@@ -9,9 +9,7 @@
 
 'use strict';
 
-var Fx = require('../cartesian/graph_interact');
-
-function createGeoZoomReset(geo, geoLayout) {
+module.exports = function createGeoZoomReset(geo, geoLayout) {
     var projection = geo.projection,
         zoom = geo.zoom;
 
@@ -22,12 +20,8 @@ function createGeoZoomReset(geo, geoLayout) {
         zoom.scale(projection.scale());
         zoom.translate(projection.translate());
 
-        Fx.loneUnhover(geo.hoverContainer);
-
         geo.render();
     };
 
     return zoomReset;
-}
-
-module.exports = createGeoZoomReset;
+};
diff --git a/src/traces/choropleth/event_data.js b/src/traces/choropleth/event_data.js
new file mode 100644
index 00000000000..4721025d943
--- /dev/null
+++ b/src/traces/choropleth/event_data.js
@@ -0,0 +1,17 @@
+/**
+* Copyright 2012-2017, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+
+'use strict';
+
+module.exports = function eventData(out, pt) {
+    out.location = pt.location;
+    out.z = pt.z;
+
+    return out;
+};
diff --git a/src/traces/choropleth/hover.js b/src/traces/choropleth/hover.js
new file mode 100644
index 00000000000..b9b4962102e
--- /dev/null
+++ b/src/traces/choropleth/hover.js
@@ -0,0 +1,68 @@
+/**
+* Copyright 2012-2017, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+
+'use strict';
+
+var Axes = require('../../plots/cartesian/axes');
+var attributes = require('./attributes');
+
+module.exports = function hoverPoints(pointData) {
+    var cd = pointData.cd;
+    var trace = cd[0].trace;
+    var geo = pointData.subplot;
+
+    // set on choropleth paths 'mouseover'
+    var pt = geo.choroplethHoverPt;
+
+    if(!pt) return;
+
+    var centroid = geo.projection(pt.properties.ct);
+
+    pointData.x0 = pointData.x1 = centroid[0];
+    pointData.y0 = pointData.y1 = centroid[1];
+
+    pointData.index = pt.index;
+    pointData.location = pt.id;
+    pointData.z = pt.z;
+
+    makeHoverInfo(pointData, trace, pt, geo.mockAxis);
+
+    return [pointData];
+};
+
+function makeHoverInfo(pointData, trace, pt, axis) {
+    var hoverinfo = trace.hoverinfo;
+
+    var parts = (hoverinfo === 'all') ?
+        attributes.hoverinfo.flags :
+        hoverinfo.split('+');
+
+    var hasName = (parts.indexOf('name') !== -1),
+        hasLocation = (parts.indexOf('location') !== -1),
+        hasZ = (parts.indexOf('z') !== -1),
+        hasText = (parts.indexOf('text') !== -1),
+        hasIdAsNameLabel = !hasName && hasLocation;
+
+    var text = [];
+
+    function formatter(val) {
+        return Axes.tickText(axis, axis.c2l(val), 'hover').text;
+    }
+
+    if(hasIdAsNameLabel) pointData.nameOverride = pt.id;
+    else {
+        if(hasName) pointData.nameOverride = trace.name;
+        if(hasLocation) text.push(pt.id);
+    }
+
+    if(hasZ) text.push(formatter(pt.z));
+    if(hasText) text.push(pt.tx);
+
+    pointData.extraText = text.join('<br>');
+}
diff --git a/src/traces/choropleth/index.js b/src/traces/choropleth/index.js
index 15cfae98a54..f358369cfd3 100644
--- a/src/traces/choropleth/index.js
+++ b/src/traces/choropleth/index.js
@@ -15,10 +15,9 @@ Choropleth.attributes = require('./attributes');
 Choropleth.supplyDefaults = require('./defaults');
 Choropleth.colorbar = require('../heatmap/colorbar');
 Choropleth.calc = require('./calc');
-Choropleth.plot = require('./plot').plot;
-
-// add dummy hover handler to skip Fx.hover w/o warnings
-Choropleth.hoverPoints = function() {};
+Choropleth.plot = require('./plot');
+Choropleth.hoverPoints = require('./hover');
+Choropleth.eventData = require('./event_data');
 
 Choropleth.moduleType = 'trace';
 Choropleth.name = 'choropleth';
diff --git a/src/traces/choropleth/plot.js b/src/traces/choropleth/plot.js
index 86d947a6fce..9f4ec41e87c 100644
--- a/src/traces/choropleth/plot.js
+++ b/src/traces/choropleth/plot.js
@@ -11,8 +11,6 @@
 
 var d3 = require('d3');
 
-var Axes = require('../../plots/cartesian/axes');
-var Fx = require('../../plots/cartesian/graph_interact');
 var Color = require('../../components/color');
 var Drawing = require('../../components/drawing');
 var Colorscale = require('../../components/colorscale');
@@ -22,42 +20,8 @@ var locationToFeature = require('../../lib/geo_location_utils').locationToFeatur
 var arrayToCalcItem = require('../../lib/array_to_calc_item');
 
 var constants = require('../../plots/geo/constants');
-var attributes = require('./attributes');
 
-var plotChoropleth = module.exports = {};
-
-
-plotChoropleth.calcGeoJSON = function(trace, topojson) {
-    var cdi = [],
-        locations = trace.locations,
-        len = locations.length,
-        features = getTopojsonFeatures(trace, topojson),
-        markerLine = (trace.marker || {}).line || {};
-
-    var feature;
-
-    for(var i = 0; i < len; i++) {
-        feature = locationToFeature(trace.locationmode, locations[i], features);
-
-        if(!feature) continue;  // filter the blank features here
-
-        // 'data_array' attributes
-        feature.z = trace.z[i];
-        if(trace.text !== undefined) feature.tx = trace.text[i];
-
-        // 'arrayOk' attributes
-        arrayToCalcItem(markerLine.color, feature, 'mlc', i);
-        arrayToCalcItem(markerLine.width, feature, 'mlw', i);
-
-        cdi.push(feature);
-    }
-
-    if(cdi.length > 0) cdi[0].trace = trace;
-
-    return cdi;
-};
-
-plotChoropleth.plot = function(geo, calcData, geoLayout) {
+module.exports = function plot(geo, calcData, geoLayout) {
 
     function keyFunc(d) { return d[0].trace.uid; }
 
@@ -79,54 +43,20 @@ plotChoropleth.plot = function(geo, calcData, geoLayout) {
 
     gChoroplethTraces.each(function(calcTrace) {
         var trace = calcTrace[0].trace,
-            cdi = plotChoropleth.calcGeoJSON(trace, geo.topojson),
-            cleanHoverLabelsFunc = makeCleanHoverLabelsFunc(geo, trace),
-            eventDataFunc = makeEventDataFunc(trace);
-
-        // keep ref to event data in this scope for plotly_unhover
-        var eventData = null;
-
-        function handleMouseOver(pt, ptIndex) {
-            if(!geo.showHover) return;
-
-            var xy = geo.projection(pt.properties.ct);
-            cleanHoverLabelsFunc(pt);
-
-            Fx.loneHover({
-                x: xy[0],
-                y: xy[1],
-                name: pt.nameLabel,
-                text: pt.textLabel
-            }, {
-                container: geo.hoverContainer.node()
-            });
-
-            eventData = eventDataFunc(pt, ptIndex);
+            cdi = calcGeoJSON(trace, geo.topojson);
 
-            geo.graphDiv.emit('plotly_hover', eventData);
-        }
-
-        function handleClick(pt, ptIndex) {
-            geo.graphDiv.emit('plotly_click', eventDataFunc(pt, ptIndex));
-        }
-
-        var paths = d3.select(this).selectAll('path.choroplethlocation')
-                .data(cdi);
+        var paths = d3.select(this)
+            .selectAll('path.choroplethlocation')
+            .data(cdi);
 
         paths.enter().append('path')
             .classed('choroplethlocation', true)
-            .on('mouseover', handleMouseOver)
-            .on('click', handleClick)
-            .on('mouseout', function() {
-                Fx.loneUnhover(geo.hoverContainer);
-
-                geo.graphDiv.emit('plotly_unhover', eventData);
-            })
-            .on('mousedown', function() {
-                // to simulate the 'zoomon' event
-                Fx.loneUnhover(geo.hoverContainer);
+            .on('mouseover', function(pt) {
+                geo.choroplethHoverPt = pt;
             })
-            .on('mouseup', handleMouseOver);  // ~ 'zoomend'
+            .on('mouseout', function() {
+                geo.choroplethHoverPt = null;
+            });
 
         paths.exit().remove();
     });
@@ -141,87 +71,62 @@ plotChoropleth.plot = function(geo, calcData, geoLayout) {
         geo.styleLayer(gBaseLayerOverChoropleth, layerName, geoLayout);
     }
 
-    plotChoropleth.style(geo);
+    style(geo);
 };
 
-plotChoropleth.style = function(geo) {
-    geo.framework.selectAll('g.trace.choropleth')
-        .each(function(calcTrace) {
-            var trace = calcTrace[0].trace,
-                s = d3.select(this),
-                marker = trace.marker || {},
-                markerLine = marker.line || {};
-
-            var sclFunc = Colorscale.makeColorScaleFunc(
-                Colorscale.extractScale(
-                    trace.colorscale,
-                    trace.zmin,
-                    trace.zmax
-                )
-            );
-
-            s.selectAll('path.choroplethlocation')
-                .each(function(pt) {
-                    d3.select(this)
-                        .attr('fill', function(pt) { return sclFunc(pt.z); })
-                        .call(Color.stroke, pt.mlc || markerLine.color)
-                        .call(Drawing.dashLine, '', pt.mlw || markerLine.width || 0);
-                });
-        });
-};
+function calcGeoJSON(trace, topojson) {
+    var cdi = [],
+        locations = trace.locations,
+        len = locations.length,
+        features = getTopojsonFeatures(trace, topojson),
+        markerLine = (trace.marker || {}).line || {};
 
-function makeCleanHoverLabelsFunc(geo, trace) {
-    var hoverinfo = trace.hoverinfo;
+    var feature;
 
-    if(hoverinfo === 'none' || hoverinfo === 'skip') {
-        return function cleanHoverLabelsFunc(pt) {
-            delete pt.nameLabel;
-            delete pt.textLabel;
-        };
-    }
+    for(var i = 0; i < len; i++) {
+        feature = locationToFeature(trace.locationmode, locations[i], features);
 
-    var hoverinfoParts = (hoverinfo === 'all') ?
-            attributes.hoverinfo.flags :
-            hoverinfo.split('+');
+        if(!feature) continue;  // filter the blank features here
 
-    var hasName = (hoverinfoParts.indexOf('name') !== -1),
-        hasLocation = (hoverinfoParts.indexOf('location') !== -1),
-        hasZ = (hoverinfoParts.indexOf('z') !== -1),
-        hasText = (hoverinfoParts.indexOf('text') !== -1),
-        hasIdAsNameLabel = !hasName && hasLocation;
+        // 'data_array' attributes
+        feature.z = trace.z[i];
+        if(trace.text !== undefined) feature.tx = trace.text[i];
 
-    function formatter(val) {
-        var axis = geo.mockAxis;
-        return Axes.tickText(axis, axis.c2l(val), 'hover').text;
-    }
+        // 'arrayOk' attributes
+        arrayToCalcItem(markerLine.color, feature, 'mlc', i);
+        arrayToCalcItem(markerLine.width, feature, 'mlw', i);
 
-    return function cleanHoverLabelsFunc(pt) {
-        // put location id in name label container
-        // if name isn't part of hoverinfo
-        var thisText = [];
+        // for event data
+        feature.index = i;
 
-        if(hasIdAsNameLabel) pt.nameLabel = pt.id;
-        else {
-            if(hasName) pt.nameLabel = trace.name;
-            if(hasLocation) thisText.push(pt.id);
-        }
+        cdi.push(feature);
+    }
 
-        if(hasZ) thisText.push(formatter(pt.z));
-        if(hasText) thisText.push(pt.tx);
+    if(cdi.length > 0) cdi[0].trace = trace;
 
-        pt.textLabel = thisText.join('<br>');
-    };
+    return cdi;
 }
 
-function makeEventDataFunc(trace) {
-    return function(pt, ptIndex) {
-        return {points: [{
-            data: trace._input,
-            fullData: trace,
-            curveNumber: trace.index,
-            pointNumber: ptIndex,
-            location: pt.id,
-            z: pt.z
-        }]};
-    };
+function style(geo) {
+    geo.framework.selectAll('g.trace.choropleth').each(function(calcTrace) {
+        var trace = calcTrace[0].trace,
+            s = d3.select(this),
+            marker = trace.marker || {},
+            markerLine = marker.line || {};
+
+        var sclFunc = Colorscale.makeColorScaleFunc(
+            Colorscale.extractScale(
+                trace.colorscale,
+                trace.zmin,
+                trace.zmax
+            )
+        );
+
+        s.selectAll('path.choroplethlocation').each(function(pt) {
+            d3.select(this)
+                .attr('fill', function(pt) { return sclFunc(pt.z); })
+                .call(Color.stroke, pt.mlc || markerLine.color)
+                .call(Drawing.dashLine, '', pt.mlw || markerLine.width || 0);
+        });
+    });
 }
diff --git a/test/image/baselines/geo_bg-color.png b/test/image/baselines/geo_bg-color.png
index 44df296e155..cebc25880d3 100644
Binary files a/test/image/baselines/geo_bg-color.png and b/test/image/baselines/geo_bg-color.png differ
diff --git a/test/image/baselines/geo_big-frame.png b/test/image/baselines/geo_big-frame.png
index cea96bd27b7..e4c849b971c 100644
Binary files a/test/image/baselines/geo_big-frame.png and b/test/image/baselines/geo_big-frame.png differ
diff --git a/test/image/baselines/geo_bubbles-colorscales.png b/test/image/baselines/geo_bubbles-colorscales.png
index 1f0fba6a561..642da7d688c 100644
Binary files a/test/image/baselines/geo_bubbles-colorscales.png and b/test/image/baselines/geo_bubbles-colorscales.png differ
diff --git a/test/image/baselines/geo_bubbles-sizeref.png b/test/image/baselines/geo_bubbles-sizeref.png
index 8a98a5b2f03..7c2e297f822 100644
Binary files a/test/image/baselines/geo_bubbles-sizeref.png and b/test/image/baselines/geo_bubbles-sizeref.png differ
diff --git a/test/image/baselines/geo_choropleth-text.png b/test/image/baselines/geo_choropleth-text.png
index 8ef3a928b00..015965b7a53 100644
Binary files a/test/image/baselines/geo_choropleth-text.png and b/test/image/baselines/geo_choropleth-text.png differ
diff --git a/test/image/baselines/geo_conic-conformal.png b/test/image/baselines/geo_conic-conformal.png
index eb8547b0b48..f5db3f93540 100644
Binary files a/test/image/baselines/geo_conic-conformal.png and b/test/image/baselines/geo_conic-conformal.png differ
diff --git a/test/image/baselines/geo_connectgaps.png b/test/image/baselines/geo_connectgaps.png
index a33c521dcdd..8526ed17636 100644
Binary files a/test/image/baselines/geo_connectgaps.png and b/test/image/baselines/geo_connectgaps.png differ
diff --git a/test/image/baselines/geo_country-names-text-chart.png b/test/image/baselines/geo_country-names-text-chart.png
index 259bba4bc9b..76b36ed7517 100644
Binary files a/test/image/baselines/geo_country-names-text-chart.png and b/test/image/baselines/geo_country-names-text-chart.png differ
diff --git a/test/image/baselines/geo_country-names.png b/test/image/baselines/geo_country-names.png
index 63f56d3e117..c81f6352039 100644
Binary files a/test/image/baselines/geo_country-names.png and b/test/image/baselines/geo_country-names.png differ
diff --git a/test/image/baselines/geo_custom-colorscale.png b/test/image/baselines/geo_custom-colorscale.png
index 0c3070d92b8..4277a04a27c 100644
Binary files a/test/image/baselines/geo_custom-colorscale.png and b/test/image/baselines/geo_custom-colorscale.png differ
diff --git a/test/image/baselines/geo_europe-bubbles.png b/test/image/baselines/geo_europe-bubbles.png
index 81b0f4df828..eb08662094d 100644
Binary files a/test/image/baselines/geo_europe-bubbles.png and b/test/image/baselines/geo_europe-bubbles.png differ
diff --git a/test/image/baselines/geo_kavrayskiy7.png b/test/image/baselines/geo_kavrayskiy7.png
index 0d5c26d9a85..9302a3c661d 100644
Binary files a/test/image/baselines/geo_kavrayskiy7.png and b/test/image/baselines/geo_kavrayskiy7.png differ
diff --git a/test/image/baselines/geo_legendonly.png b/test/image/baselines/geo_legendonly.png
index 658fe60eb7d..4eff55d0d65 100644
Binary files a/test/image/baselines/geo_legendonly.png and b/test/image/baselines/geo_legendonly.png differ
diff --git a/test/image/baselines/geo_multi-geos.png b/test/image/baselines/geo_multi-geos.png
index 6409d1099eb..6f0e5b43a5e 100644
Binary files a/test/image/baselines/geo_multi-geos.png and b/test/image/baselines/geo_multi-geos.png differ
diff --git a/test/image/baselines/geo_multiple-usa-choropleths.png b/test/image/baselines/geo_multiple-usa-choropleths.png
index 6d7caba2561..951982d2a30 100644
Binary files a/test/image/baselines/geo_multiple-usa-choropleths.png and b/test/image/baselines/geo_multiple-usa-choropleths.png differ
diff --git a/test/image/baselines/geo_orthographic.png b/test/image/baselines/geo_orthographic.png
index 0fd54ffd8ee..90835c2980e 100644
Binary files a/test/image/baselines/geo_orthographic.png and b/test/image/baselines/geo_orthographic.png differ
diff --git a/test/image/baselines/geo_scattergeo-locations.png b/test/image/baselines/geo_scattergeo-locations.png
index 60b48b26e15..6724ddebbac 100644
Binary files a/test/image/baselines/geo_scattergeo-locations.png and b/test/image/baselines/geo_scattergeo-locations.png differ
diff --git a/test/image/baselines/geo_second.png b/test/image/baselines/geo_second.png
index 3f7f9cfa7cd..d4aa1075cd0 100644
Binary files a/test/image/baselines/geo_second.png and b/test/image/baselines/geo_second.png differ
diff --git a/test/image/baselines/geo_winkel-tripel.png b/test/image/baselines/geo_winkel-tripel.png
index 9e14e104d1e..e0e864fdab9 100644
Binary files a/test/image/baselines/geo_winkel-tripel.png and b/test/image/baselines/geo_winkel-tripel.png differ
diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js
index 244db755d2c..9adf58c87e7 100644
--- a/test/jasmine/tests/geo_test.js
+++ b/test/jasmine/tests/geo_test.js
@@ -404,7 +404,7 @@ describe('Test geo interactions', function() {
         }
 
         function countGeos() {
-            return d3.select('div.geo-container').selectAll('div').size();
+            return d3.select('g.geolayer').selectAll('.geo').size();
         }
 
         function countColorBars() {
@@ -596,6 +596,7 @@ describe('Test geo interactions', function() {
         describe('choropleth hover labels', function() {
             beforeEach(function() {
                 mouseEventChoropleth('mouseover');
+                mouseEventChoropleth('mousemove');
             });
 
             it('should show one hover text group', function() {
@@ -625,6 +626,7 @@ describe('Test geo interactions', function() {
                 });
 
                 mouseEventChoropleth('mouseover');
+                mouseEventChoropleth('mousemove');
             });
 
             it('should contain the correct fields', function() {
@@ -650,6 +652,8 @@ describe('Test geo interactions', function() {
                     ptData = eventData.points[0];
                 });
 
+                mouseEventChoropleth('mouseover');
+                mouseEventChoropleth('mousemove');
                 mouseEventChoropleth('click');
             });
 
@@ -671,13 +675,18 @@ describe('Test geo interactions', function() {
         describe('choropleth unhover events', function() {
             var ptData;
 
-            beforeEach(function() {
+            beforeEach(function(done) {
                 gd.on('plotly_unhover', function(eventData) {
                     ptData = eventData.points[0];
                 });
 
                 mouseEventChoropleth('mouseover');
+                mouseEventChoropleth('mousemove');
                 mouseEventChoropleth('mouseout');
+                setTimeout(function() {
+                    mouseEvent('mousemove', 300, 235);
+                    done();
+                }, HOVERMINTIME + 100);
             });
 
             it('should contain the correct fields', function() {