Repository: bokeh/bokeh

Issue:
GMapPlot doesn't display anything in Jupyter v.4
Hi,

Just started using Bokeh more extensively and I wanted to display maps within a Jupyter notebook. I tried running the example provided in the repo (with the world_cities dataset) and while it displays when ran alone, it only displays a blank area in Jupyter (and I could only see the data once (without a map) when I refreshed, but this is not consistent). I have both latest versions for bokeh and Jupyter (from Anaconda). 

```
import bokeh
import bokeh.plotting as plotting
plotting.output_notebook()
from bokeh.io import push_notebook

from bokeh.resources import INLINE
from bokeh.models.glyphs import Circle
from bokeh.models import (
    GMapPlot, Range1d, ColumnDataSource,
    PanTool, WheelZoomTool, GMapOptions)
from bokeh.resources import INLINE
from bokeh.sampledata.world_cities import data


x_range = Range1d(-160, 160)
y_range = Range1d(-80, 80)

map_options = GMapOptions(lat=15, lng=0, zoom=10)

plot = GMapPlot(
    x_range=x_range,
    y_range=y_range,
    plot_width=1000,
    plot_height=500,
    map_options=map_options,
    title="Cities of the world with a population over 5,000 people.",
    webgl=True,
)
circle = Circle(x="lng", y="lat", size=5, line_color=None, fill_color='firebrick', fill_alpha=0.2)
plot.add_glyph(ColumnDataSource(data), circle)
plot.add_tools(PanTool(), WheelZoomTool())
plotting.show(plot)
```



Discussion:
I can confirm this, no map on first run, no map and no glyphs after kernel restart. Page refresh causes glyphs to reappear, but still no map. No meaningful error messages in JS console. There are some related to comms when there are no glyphs rendered. I presume those errors obscure relevant information.

I didn't investigate this in detail yet, but kernel restart is a problem on its own. Just removing output cells and restarting the kernel doesn't reset the state entirely, because bokehjs is still loaded on the page (until page reload).

fwiw I'm finding this problem to be occurring more often than not, but definitely intermittently, sometimes the plot does render on first run:

<img width="631" alt="screen shot 2016-02-10 at 1 03 21 pm" src="https://cloud.githubusercontent.com/assets/1796208/12961679/b2e8bcea-cff6-11e5-8e71-b8d3f86d6bf9.png">

May be there's an event / ordering / callback problem?

I'm also facing this problem when using GMapPlot with jupyter notebook, it seems like the map will only appear when it is first loaded to the browser, any subsequent loads will result in a blank white map

Same thing here. This is kind of a showstopper for me personally. Would love to see it fixed

Ditto. Is a solution expected for this issue anytime soon?

Works great on load or re-load, won't refresh on subsequent runs of the same code without a reload.

Versions:
bokeh: 0.11.1
jupyter: 1.0.0
jupyter-client: 4.1.1
jupyter-console: 4.1.0
jupyter-core: 4.0.6
jupyter_client: 4.1.1
jupyter_console: 4.1.0
jupyter_core: 4.0.6

Can everyone report what versions they are using / have tried. It is always important to report version information with every issue. 

Updated per your request @bryevdv  

@mattpap I think the notebook comms are a red herring. I have issues even when comms succeed (which seems to happen on 0.11) and also when I put a `try` / `catch` around comms init to ignore errors. What about notebook versions? Perhaps something changed on the Jupyter side. 

Also observed with with Bokeh 0.10 and notebook version 4.0.6 (and with 3.2 it seems most times) so the notebook comms are definitely not the proximate cause. Have gmap plots in notebooks ever been demonstrated? (Honestly am not aware / don't remember) Can anyone provide a known working configuration? 

Also seems to happen with Bokeh 0.8. As far as I can tell this has either never worked, or something external to Bokeh has changed to make it not work. 

In any case, unfortunately it seems this will be non-trivial work to investigate (I was hoping I might find a quick fix on this flight).  Debugging in the notebook is already time-consuming, throw in a third party library and it will be a real effort. There are several other immediately pressing priorities so I can't say that we'll be able to devote attention to it anytime particularly soon. I very much wish I could say something more promising, but the reality of finite time and resources and human limits is what it is. As a workaround, you might try the new `TileRenderer`, which seems to work reliably for me:

<img width="1152" alt="screen shot 2016-03-16 at 10 48 09 am" src="https://cloud.githubusercontent.com/assets/1078448/13822932/ca7ce0f0-eb64-11e5-954d-01e1a652990b.png">

Sorry this is so vague, but if it's of any value, gmap plots/jupyter used to work reliably for me. From memory that was before christmas and at a guess I'd say bokeh 0.10 (and possibly jupyter < 4).

To me it seems like they load correctly if I refresh a notebook (i.e. F5) with embedded graphs, but if I execute a cell to generate a graph then that may or may not create an empty gmap.

For the record it still seems to have the same broken behaviour in ipython notebook 4.0

Same issue, no map, no glyphs in the notebook.  I can get map to render in notebook if I re-zoom the page (ctrl-+) but glyph still do not render. 

bokeh == 0.11.1
ipython == 4.1.2
python == 2.7.3
jupyter == 4.1.0

Same issue here, with:
bokeh                     0.11.1
ipython                   4.1.2
python                    2.7.11

Same issue with 
bokeh 0.11.1
ipython 4.0.0
python 2.7.9
jupyter 1.0.0

I rolled back to bokeh 0.10.0 and the problem went away.

Have not forgotten this, but there is currently some fairly heavy lifting refactoring going on on some of the BokehJS codebase and it does not make sense to get into this until that work is complete. 

I continue to experience problems that appear to be aligned with the issues highlighted in this thread.

Please try the latest dev build 

I tried with 

> `pip install --upgrade --pre -i https://pypi.anaconda.org/bokeh/channel/dev/simple bokeh --extra-index-url https://pypi.python.org/simple/`  

but continue to experience the same issues.

@colliand In general it is important to provide as much information as possible (to err on the side of too much). This is especially true in cases where the issue is not happening to everyone / is difficult to reproduce. What is really needed:
- a minimal complete code sample to reproduce the problem
- relevant version information (libraries, OS, browsers)
- any shell console or browser JavaScript console output

Also we need a precise accurate description of what happened. Is there no output at all? Is there a plot with glyphs but no map? Is there a plot but neither glyphs nor a map? The notebook is a challenging environment to develop and debug, and there have been different sets of problems that sometimes look similar. Without detailed information, it is impossible to speculate on anything concrete or attempt to provide a diagnosis or solution. 

Can you also first confirm that you have configured the GMap with your google API key? Google made a recent change (that we have no control over at all) requiring all usage of Google Maps to include an API key, which means that all users must now supply their own API keys (via the new `api_key` property) for GMapPlot to function. 

Thanks @bryevdv. I appreciate your help. I've exposed my work here: [http://nbviewer.jupyter.org/github/colliand/public-notebooks/blob/master/google-maps/pims-map.ipynb](http://nbviewer.jupyter.org/github/colliand/public-notebooks/blob/master/google-maps/pims-map.ipynb).  I've made some progress but still have a few issues:  
1. The map does not load after executing the cell that generates the map. The frame appears around a white unfilled map area. I've observed that changing the size of the browser window triggers the appearance of the map.
2. The map that appears is not centered at the latitude, longitude pair requested in `GMapOptions`.
3. The requested glyphs do not appear on the map when it first appears. I've observed that executing wheel zoom causes the glyphs to appear. 

Here is a [screen capture of my JavaScript console](https://wwejubwfy.s3.amazonaws.com/Developer_Tools_-_httpspims.jupyter.causerjcolliannotebookscolliand-publicgoogle-mapspims-map.ipynb-2016-07-24-12-22-03.jpg). I welcome your advice.

@colliand Can you confirm that the version you are running in the notebook is actually the latest dev build? (printing `bokeh.__version__` will do) I would never expect to see that comms message anymore in the console with the latest dev build. 

0.12.1dev2

does passing `resources=INLINE` to `output_notebook` have any effect? 

I'm able to reproduce the issue described by @colliand and using his notebook from nbviewer.

With a hard-refreshed browser window (with the cache deleted), the gmap plot will load the first time. Rerunning the cell containing the `show` call causes a white window (containing the google watermark) to appear instead of the map:

<img width="943" alt="screen shot 2016-07-25 at 11 13 52 am" src="https://cloud.githubusercontent.com/assets/2198981/17108420/03c8bb9e-5259-11e6-8ccb-9a00db5b1b6c.png">

Opening the dev tools will cause the plot to be rendered (presumably doing so causes the page to reflow), but the map coords aren't the requested ones and the circle glyphs are missing:

<img width="938" alt="screen shot 2016-07-25 at 11 15 15 am" src="https://cloud.githubusercontent.com/assets/2198981/17108467/4a2136e8-5259-11e6-834d-01da7271de7a.png">

Inspecting the network activity tab in the dev tools, it seems that there isn't a gmap response payload when running the `show` method a second time even though AFAICT the request is happening. I would think that it's an issue of how we're interacting w/ the Google maps API, but it will require further investigation.

(thanks @colliand for the helpful notebook and bug description!)

I can confirm exactly the behavior that @canavandl has described: 
1. Map does not show but points do
2. Opening dev tools makes map appear but points do

I'd also add that a brand new instance of IPython ntb creates the plot for about ~1 second correctly: map loads, points appear wrongly (1 sec), points move to the correct position (1 sec), map disappears as:
![screen shot 2016-08-17 at 10 30 17 pm](https://cloud.githubusercontent.com/assets/1884004/17753985/40006840-64ca-11e6-9168-54d861e44181.png)

(Code almost unchanged from here http://bokeh.pydata.org/en/0.11.1/docs/user_guide/geo.html, the only thing is output_file -> output_notebook)

<pre> 
bokeh                     0.12.0                    <pip>
conda                     4.1.8                    py35_0  
conda-env                 2.5.2                    py35_0  
ipython                   5.0.0                    py35_0    conda-forge
ipywidgets                5.1.5                    py35_0    conda-forge
jsonschema                2.5.1                    py35_0    conda-forge
jupyter_client            4.3.0                    py35_0    conda-forge
jupyter_core              4.1.0                    py35_0    conda-forge
nbformat                  4.0.1                    py35_0    conda-forge
notebook                  4.2.1                    py35_0    conda-forge
numpy                     1.10.4          py35_blas_openblas_201  [blas_openblas]  conda-forge
openblas                  0.2.18                        3    conda-forge
pandas                    0.18.1              np110py35_0    conda-forge
</pre>

Oh one last thing: adding `api-key` argument does not change any of the behavior described above.

@bryevdv wrote:

> does passing resources=INLINE to output_notebook have any effect?

I changed the import calls as follows: 

```
## output_notebook()
from bokeh.resources import INLINE
output_notebook(resources=INLINE)
```

I observed no change in behavior. The map launches with a blank output panel. Resizing the browser causes a map to appear with no glyphs. Wheel zoom causes the glyphs to appear.

I also took the [example from the docs](http://bokeh.pydata.org/en/0.11.1/docs/user_guide/geo.html)
 and added by API key and set output_notebook(resources=INLINE)

On the first run the map is correct, on subsequent the map is blank. resizing the window refreshes the map but the dots are missing. Zooming with the wheel restores the dots. 

I also noticed the Wheel Zoom discards the first 10 wheel clicks, this make the zooming feel odd. 

bokeh.**version** '0.12.2'
notebook server is 4.2.3
Python 3.5.2 
running on a linux (AWS) server from aChromOS Version 53.0.2785.103  

 I get the same behaviour as @Kevin-McIsaac however reloading the page restores the map and markers for me. I also am getting a `MISSING_GOOGLE_API_KEY` warning despite providing an `api_key` to the GMapPlot (and it is included in the call to the google maps API).
@themrmax I once accidentally used a browser key.  Make sure it is a Google *server* API key.
I also have the zoom by scrolling delay.
@benuklove I'm having trouble working out the different types of keys; i'm using the one from https://developers.google.com/maps/documentation/javascript/get-api-key is this the correct one?

@themrmax Yes that looks right.  Make sure the JavaScript API is actually *enabled*.
On your API Manager dashboard, for your project, it lists the APIs that are enabled.  
There's alot of cruft in this issue, I am not sure if this is a dupe of #4835 It seems like it might be.
@bryevdv it does look like the same/similar issue.  
Also, I can remove my replies to themrmax as they seem Google interface related.  Is that what you mean?
@benuklove I just mean there have been various issues / possible issues and I think several of them got combined in this issue. It's fine, I am going to work off the other issue first and see if it solves the problems described here too. 

Resolving patch:
diff --git a/bokeh/models/map_plots.py b/bokeh/models/map_plots.py
--- a/bokeh/models/map_plots.py
+++ b/bokeh/models/map_plots.py
@@ -3,17 +3,17 @@
 '''
 from __future__ import absolute_import
 
-from ..core.has_props import abstract, HasProps
-from ..core.properties import Enum, Float, Instance, Int, JSON, Override, String
 from ..core.enums import MapType
+from ..core.has_props import abstract
+from ..core.properties import Enum, Float, Instance, Int, JSON, Override, String
 from ..core.validation import error, warning
 from ..core.validation.warnings import MISSING_RENDERERS, NO_DATA_RENDERERS
 from ..core.validation.errors import REQUIRED_RANGE, MISSING_GOOGLE_API_KEY
-
+from ..model import Model
 from .plots import Plot
 
 @abstract
-class MapOptions(HasProps):
+class MapOptions(Model):
     ''' Abstract base class for map options' models.
 
     '''
@@ -41,7 +41,7 @@ class GMapOptions(MapOptions):
 
     '''
 
-    map_type = Enum(MapType, help="""
+    map_type = Enum(MapType, default="roadmap", help="""
     The `map type`_ to use for the GMapPlot.
 
     .. _map type: https://developers.google.com/maps/documentation/javascript/reference#MapTypeId


Test patch:
diff --git a/bokehjs/test/core/has_props.coffee b/bokehjs/test/core/has_props.coffee
--- a/bokehjs/test/core/has_props.coffee
+++ b/bokehjs/test/core/has_props.coffee
@@ -2,6 +2,7 @@
 utils = require "../utils"
 fixtures = require "./fixtures/object"
 
+{ColumnDataSource} = utils.require("models/sources/column_data_source")
 {Models} = utils.require "base"
 {HasProps} = utils.require "core/has_props"
 p = utils.require "core/properties"
@@ -28,6 +29,25 @@ class SubSubclassWithMixins extends SubclassWithMixins
 class SubclassWithMultipleMixins extends HasProps
   @mixin('line', 'text:bar_')
 
+class SubclassWithNumberSpec extends HasProps
+  @define {
+    foo: [ p.NumberSpec, {field: 'colname'} ]
+    bar: [ p.Bool,       true               ]
+  }
+
+class SubclassWithDistanceSpec extends HasProps
+  @define {
+    foo: [ p.DistanceSpec, {field: 'colname'} ]
+    bar: [ p.Bool,         true               ]
+  }
+
+class SubclassWithOptionalSpec extends HasProps
+  @define {
+    foo: [ p.NumberSpec, {value: null}      ]
+    bar: [ p.Bool,       true               ]
+    baz: [ p.NumberSpec, {field: 'colname'} ]
+  }
+
 describe "has_properties module", ->
 
   before ->
@@ -65,6 +85,38 @@ describe "has_properties module", ->
       props = Object.keys(Object.assign(mixins.line(""), mixins.text("bar_")))
       expect(Object.keys(obj.properties)).to.be.deep.equal(['id'].concat(props))
 
+  describe "materialize_dataspecs", ->
+    it "should collect dataspecs", ->
+      r = new ColumnDataSource({data: {colname: [1, 2, 3, 4]}})
+      obj = new SubclassWithNumberSpec()
+      data = obj.materialize_dataspecs(r)
+      expect(data).to.be.deep.equal {_foo: [1, 2, 3, 4]}
+
+    it "should collect shapes when they are present", ->
+      r = new ColumnDataSource({data: {colname: [1, 2, 3, 4]}})
+      r._shapes.colname = [2, 2]
+      obj = new SubclassWithNumberSpec()
+      data = obj.materialize_dataspecs(r)
+      expect(data).to.be.deep.equal {_foo: [1, 2, 3, 4], _foo_shape: [2, 2]}
+
+    it "should collect max vals for distance specs", ->
+      r = new ColumnDataSource({data: {colname: [1, 2, 3, 4, 2]}})
+      obj = new SubclassWithDistanceSpec()
+
+      data = obj.materialize_dataspecs(r)
+      expect(data).to.be.deep.equal {_foo: [1, 2, 3, 4, 2], max_foo: 4}
+
+      r._shapes.colname = [2, 2]
+      data = obj.materialize_dataspecs(r)
+      expect(data).to.be.deep.equal {_foo: [1, 2, 3, 4, 2], _foo_shape: [2, 2], max_foo: 4}
+
+    it "should collect ignore optional specs with null values", ->
+      r = new ColumnDataSource({data: {colname: [1, 2, 3, 4]}})
+      obj = new SubclassWithOptionalSpec()
+      obj.properties.foo.optional = true
+      data = obj.materialize_dataspecs(r)
+      expect(data).to.be.deep.equal {_baz: [1, 2, 3, 4]}
+
   # describe "arrays of references", ->
   #   [model1, model2, model3, model4, doc] = [null, null, null, null, null]
   #   before ->
diff --git a/bokehjs/test/models/glyphs/rect.coffee b/bokehjs/test/models/glyphs/rect.coffee
--- a/bokehjs/test/models/glyphs/rect.coffee
+++ b/bokehjs/test/models/glyphs/rect.coffee
@@ -3,13 +3,7 @@ utils = require "../../utils"
 sinon = require "sinon"
 
 {create_glyph_view} = require("./glyph_utils")
-{Rect, RectView} = utils.require("models/glyphs/rect")
-{PlotCanvasView} = utils.require("models/plots/plot_canvas")
-{CanvasView} = utils.require("models/canvas/canvas")
-{CartesianFrame} = utils.require("models/canvas/cartesian_frame")
-{LinearMapper} = utils.require("models/mappers/linear_mapper")
-{LogMapper} = utils.require("models/mappers/log_mapper")
-{Range1d} = utils.require("models/ranges/range1d")
+{Rect, RectView} = utils.require('models/glyphs/rect')
 
 describe "Glyph (using Rect as a concrete Glyph)", ->
 
@@ -71,22 +65,6 @@ describe "Rect", ->
         height: {value: 20}
       })
 
-      @set_mappers = (glyph_view, type="linear") ->
-        if type == "linear"
-          mapper = new LinearMapper({
-            source_range: new Range1d({start: 0, end: 100})
-            target_range: new Range1d({start: 0, end: 200})
-          })
-        else
-          mapper = new LogMapper({
-            source_range: new Range1d({start: 0, end: 100})
-            target_range: new Range1d({start: 0, end: 200})
-          })
-        glyph_view.renderer.xmapper = mapper
-        glyph_view.renderer.ymapper = mapper
-        glyph_view.renderer.plot_view.frame.x_mappers['default'] = mapper
-        glyph_view.renderer.plot_view.frame.y_mappers['default'] = mapper
-
     it "should calculate bounds based on data including width and height", ->
       data = {x: [0, 1, 2, 3], y: [0, 1, 2, 3]}
       glyph_view = create_glyph_view(@glyph, data)
@@ -100,175 +78,3 @@ describe "Rect", ->
       log_bounds = glyph_view.log_bounds()
 
       expect(log_bounds).to.be.deep.equal({ minX: -4, minY: -9, maxX: 8, maxY: 13 })
-
-    it "`_map_data` should correctly map data if width and height units are 'data'", ->
-      data = {x: [1], y: [2]}
-      glyph_view = create_glyph_view(@glyph, data)
-
-      @set_mappers(glyph_view)
-      glyph_view.map_data()
-      expect(glyph_view.sw).to.be.deep.equal([20])
-      expect(glyph_view.sh).to.be.deep.equal([40])
-
-    it "`_map_data` should correctly map data if width and height units are 'screen'", ->
-      data = {x: [1], y: [2]}
-      glyph_view = create_glyph_view(@glyph, data)
-
-      glyph_view.model.properties.width.units = "screen"
-      glyph_view.model.properties.height.units = "screen"
-
-      @set_mappers(glyph_view)
-      glyph_view.map_data()
-      expect(glyph_view.sw).to.be.deep.equal([10])
-      expect(glyph_view.sh).to.be.deep.equal([20])
-
-    it "`_map_data` should map values for x0 and y1 when width/height units are 'data'", ->
-      data = {x: [1], y: [2]}
-      glyph_view = create_glyph_view(@glyph, data)
-
-      glyph_view.map_data()
-      expect(glyph_view.sx0).to.be.deep.equal({'0': 0})
-      expect(glyph_view.sy1).to.be.deep.equal({'0': -1})
-
-    it "`_map_data` should map values for x0 and y1 when width/height units are 'screen'", ->
-      data = {x: [1], y: [2]}
-      glyph_view = create_glyph_view(@glyph, data)
-
-      glyph_view.model.properties.width.units = "screen"
-      glyph_view.model.properties.height.units = "screen"
-
-      glyph_view.map_data()
-      expect(glyph_view.sx0).to.be.deep.equal([-5])
-      expect(glyph_view.sy1).to.be.deep.equal([-11])
-
-    describe "hit-testing", ->
-
-      afterEach ->
-        @canvas_set_dims_stub.restore()
-        @plot_canvas_resize_stub.restore()
-
-      beforeEach ->
-        set_dims = (dims) ->
-          @model._width._value = dims[0]
-          @model._height._value = dims[1]
-        @canvas_set_dims_stub = sinon.stub(CanvasView.prototype, 'set_dims', set_dims)
-
-        resize = () ->
-          @frame._width._value = @canvas._width._value
-          @frame._height._value = @canvas._height._value
-        @plot_canvas_resize_stub = sinon.stub(PlotCanvasView.prototype, 'resize', resize)
-
-      describe "_hit_point", ->
-
-        beforeEach ->
-          @geometry1 = { vx: 190, vy: 220 }
-          @geometry2 = { vx: 195, vy: 210 }
-          @geometry3 = { vx: 186, vy: 186 }
-
-          @geometry4 = { vx: 201, vy: 195.5 }
-          @geometry5 = { vx: 197.8, vy: 204 }
-
-        it "should return the indices of the rect that was hit", ->
-          data = {x: [60, 100, 140], y: [60, 100, 140]}
-          glyph_view = create_glyph_view(@glyph, data)
-
-          @set_mappers(glyph_view)
-          glyph_view.map_data()
-
-          result1 = glyph_view._hit_point(@geometry1)
-          result2 = glyph_view._hit_point(@geometry2)
-          result3 = glyph_view._hit_point(@geometry3)
-
-          expect(result1['1d'].indices).to.be.deep.equal([1])
-          expect(result2['1d'].indices).to.be.deep.equal([1])
-          expect(result3['1d'].indices).to.be.deep.equal([])
-
-        it "should work when width and height units are 'screen'", ->
-          data = {x: [60, 100, 140], y: [60, 100, 140]}
-          glyph_view = create_glyph_view(@glyph, data)
-
-          glyph_view.model.properties.width.units = "screen"
-          glyph_view.model.properties.height.units = "screen"
-
-          @set_mappers(glyph_view)
-          glyph_view.map_data()
-
-          result1 = glyph_view._hit_point(@geometry1)
-          result2 = glyph_view._hit_point(@geometry2)
-          result3 = glyph_view._hit_point(@geometry3)
-
-          expect(result1['1d'].indices).to.be.deep.equal([])
-          expect(result2['1d'].indices).to.be.deep.equal([1])
-          expect(result3['1d'].indices).to.be.deep.equal([])
-
-        it "should work when rects are rotated", ->
-          glyph = new Rect({
-            x: {field: "x"}
-            y: {field: "y"}
-            width: {value: 10}
-            height: {value: 20}
-            angle: {value: -0.785398}
-          })
-
-          data = {x: [60, 100, 140], y: [60, 100, 140]}
-          glyph_view = create_glyph_view(glyph, data)
-
-          @set_mappers(glyph_view)
-          glyph_view.map_data()
-
-          result1 = glyph_view._hit_point(@geometry1)
-          result2 = glyph_view._hit_point(@geometry2)
-          result3 = glyph_view._hit_point(@geometry3)
-
-          expect(result1['1d'].indices).to.be.deep.equal([])
-          expect(result2['1d'].indices).to.be.deep.equal([])
-          expect(result3['1d'].indices).to.be.deep.equal([1])
-
-        it "should work when rects are rotated and axes ranges are very different", ->
-          glyph = new Rect({
-            x: {field: "x"}
-            y: {field: "y"}
-            width: {value: 10}
-            height: {value: 20}
-            angle: {value: -0.785398}
-          })
-
-          data = {x: [60, 100, 140], y: [60, 100, 140]}
-          glyph_view = create_glyph_view(glyph, data)
-
-          xmapper = new LinearMapper({
-            source_range: new Range1d({start: 0, end: 100})
-            target_range: new Range1d({start: 95, end: 105})
-          })
-
-          ymapper = new LinearMapper({
-            source_range: new Range1d({start: 0, end: 100})
-            target_range: new Range1d({start: 0, end: 200})
-          })
-
-          glyph_view.renderer.xmapper = xmapper
-          glyph_view.renderer.ymapper = ymapper
-          glyph_view.renderer.plot_view.frame.x_mappers['default'] = xmapper
-          glyph_view.renderer.plot_view.frame.y_mappers['default'] = ymapper
-          glyph_view.map_data()
-
-          result1 = glyph_view._hit_point({vx: 105, vy: 200})
-          result2 = glyph_view._hit_point({vx: 105, vy: 220})
-          result3 = glyph_view._hit_point({vx: 91, vy: 186})
-
-          expect(result1['1d'].indices).to.be.deep.equal([1])
-          expect(result2['1d'].indices).to.be.deep.equal([])
-          expect(result3['1d'].indices).to.be.deep.equal([1])
-
-        it "should work when axis is log", ->
-          data = {x: [60, 100, 140], y: [60, 100, 140]}
-          glyph_view = create_glyph_view(@glyph, data)
-
-          @set_mappers(glyph_view, type="log")
-          glyph_view.map_data()
-
-          result4 = glyph_view._hit_point(@geometry4)
-          result5 = glyph_view._hit_point(@geometry5)
-
-          expect(result4['1d'].indices).to.be.deep.equal([1])
-          expect(result5['1d'].indices).to.be.deep.equal([1])
diff --git a/sphinx/source/docs/dev_guide/testing.rst b/sphinx/source/docs/dev_guide/testing.rst
--- a/sphinx/source/docs/dev_guide/testing.rst
+++ b/sphinx/source/docs/dev_guide/testing.rst
@@ -58,6 +58,14 @@ To run any of the tests without standard output captured use:
 
   py.test -s
 
+To run a subset of tests by name:
+
+.. code-block:: sh
+
+  py.test -k EXPRESSION
+
+This will run only the tests that include ``EXPRESSION`` in their names (function or class names).
+
 See the py.test documentation at http://pytest.org/latest/ for further information on py.test and it's options.
 
 Examples tests
@@ -155,7 +163,15 @@ For this command to be successful you will need the following:
  - pdiff (see examples tests)
  - ``SAUCELABS_USERNAME`` environment variable
  - ``SAUCELABS_API_KEY`` environment variable
- - sauce connect running (https://wiki.saucelabs.com/display/DOCS/Setting+Up+Sauce+Connect)
+ - Sauce Connect tunnel running
+
+To start up a Sauce Connect tunnel, download Sauce Connect from
+https://wiki.saucelabs.com/display/DOCS/Setting+Up+Sauce+Connect+Proxy. Extract
+the files and go into the install directory. Then you can establish the tunnel with:
+
+.. code-block:: sh
+
+    bin/sc -u SAUCELABS_USERNAME -k SAUCELABS_API_KEY
 
 For the ``SAUCELABS_USERNAME`` and ``SAUCELABS_API_KEY`` talk to the Bokeh Core
 Developers.
@@ -166,9 +182,11 @@ Adding (or updating) a screenshot test
 If you'd like to add a new screenshot test to the Bokeh repo, first make sure
 you can run the existing screenshot tests. Assuming this runs, then you'll be
 able to make a new screenshot test. Check-out the existing screenshot tests to
-see how to set-up your new test.
+see how to set-up your new test. Ideally, tests should contain the minimal amount
+of code to test specific features. This means that you should use the low-level models
+interface rather than the plotting interface (i.e. don't use ``bokeh.plotting.figure``).
 
-Once you have done this you need to generate a base image.
+Once you're set up and have written your test, you need to generate a base image.
 
 To do this add ``--set-new-base-screenshot`` to your test command. This will
 generate an image in a screenshots directory with the name
diff --git a/tests/integration/interaction/test_datatable.py b/tests/integration/interaction/test_datatable.py
--- a/tests/integration/interaction/test_datatable.py
+++ b/tests/integration/interaction/test_datatable.py
@@ -43,7 +43,7 @@ def test_editable_changes_data(output_file_url, selenium):
     actions = ActionChains(selenium)
     actions.move_to_element(row_1_cell)
     actions.double_click()
-    actions.send_keys("33\ue007")  # After the backslash is ENTER key
+    actions.send_keys(u"33\ue007")  # After the backslash is ENTER key
     actions.perform()
 
     # Click row_2 (which triggers alert again so we can inspect the data)
diff --git a/tests/integration/interaction/test_sizing_mode.py b/tests/integration/interaction/test_sizing_mode.py
--- a/tests/integration/interaction/test_sizing_mode.py
+++ b/tests/integration/interaction/test_sizing_mode.py
@@ -1,4 +1,4 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, division
 
 from bokeh.charts import Histogram
 from bokeh.io import save
diff --git a/tests/integration/plotting/screenshots/base__test_rect_glyph_with_log_axis__test_rect_rendering_with_log_axis.png b/tests/integration/plotting/screenshots/base__test_rect_glyph_with_log_axis__test_rect_rendering_with_log_axis.png
deleted file mode 100644
Binary files a/tests/integration/plotting/screenshots/base__test_rect_glyph_with_log_axis__test_rect_rendering_with_log_axis.png and /dev/null differ
diff --git a/tests/integration/plotting/test_rect_glyph_with_log_axis.py b/tests/integration/plotting/test_rect_glyph_with_log_axis.py
deleted file mode 100644
--- a/tests/integration/plotting/test_rect_glyph_with_log_axis.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from __future__ import absolute_import
-
-from bokeh.io import save
-from bokeh.plotting import figure
-from bokeh.models import ColumnDataSource, Plot, Rect, Range1d, LogAxis
-from tests.integration.utils import has_no_console_errors
-
-import pytest
-pytestmark = pytest.mark.integration
-
-@pytest.mark.screenshot
-def test_rect_rendering_with_log_axis(output_file_url, selenium, screenshot):
-
-    plot = Plot(plot_height=400, plot_width=400,
-       x_range=Range1d(0,30), y_range=Range1d(1,100),
-       y_mapper_type="log")
-
-    x = [10, 20]
-    y = [10, 20]
-
-    source = ColumnDataSource(data=dict(x=[(x[0]+x[1])/2], y=[(y[0]+y[1])/2], width=[x[1]-x[0]], height=[y[1]-y[0]]))
-    plot.add_glyph(source, Rect(x='x', y='y', width='width', height='height'))
-
-    plot.add_layout(LogAxis(), "left")
-
-    # Save the plot and start the test
-    save(plot)
-    selenium.get(output_file_url)
-    assert has_no_console_errors(selenium)
-    assert screenshot.is_valid()