Repository: bokeh/bokeh

Issue:
[FEATURE] need to add tile provider demo
This Issue is to request a tile provider demo to showcase all Bokeh tile providers.

In particular, it would be nice to compare the newly added OSM, WIKIMEDIA, and ESRI_IMAGERY tile providers to each other and the existing STAMEN and CARTODBPOSITRON tile providers.

This tile provider demonstration will allow Bokeh to provide open-source alternatives to Google Maps with Open Street Maps as the standard basemap with streets, buildings and other features along with the ESRI_IMAGERY for the satellite view of the same location.

![bokeh_tile_demo_screenshot](https://user-images.githubusercontent.com/29280226/75051681-52dbf680-549c-11ea-816e-2f7b77d94ddd.jpg)



Discussion:

Resolving patch:
diff --git a/examples/plotting/file/tile_demo.py b/examples/plotting/file/tile_demo.py
new file mode 100644
--- /dev/null
+++ b/examples/plotting/file/tile_demo.py
@@ -0,0 +1,83 @@
+# Demonstrate Bokeh's various map tile providers including OSM, WIKIMEDIA,
+# and ESRI_IMAGERY tile providers.
+#
+# All maps are connected with common pan and zoom.
+#
+# Usage:
+#   python3 tile_demo.py
+
+import numpy as np
+
+from bokeh.layouts import layout
+from bokeh.models.ranges import DataRange1d
+from bokeh.models.widgets import Div
+from bokeh.plotting import figure, output_file, show
+from bokeh.tile_providers import Vendors, get_provider
+
+output_file("tile_demo.html")
+
+# helper function for coordinate conversion between lat/lon in decimal degrees to web mercator
+def lnglat_to_meters(longitude, latitude):
+    """
+    Projects the given (longitude, latitude) values into Web Mercator
+    coordinates (meters East of Greenwich and meters North of the Equator).
+
+    Longitude and latitude can be provided as scalars, Pandas columns,
+    or Numpy arrays, and will be returned in the same form.  Lists
+    or tuples will be converted to Numpy arrays.
+
+    Examples:
+       easting, northing = lnglat_to_meters(-74,40.71)
+
+       easting, northing = lnglat_to_meters(np.array([-74]),np.array([40.71]))
+
+       df=pandas.DataFrame(dict(longitude=np.array([-74]),latitude=np.array([40.71])))
+       df.loc[:, 'longitude'], df.loc[:, 'latitude'] = lnglat_to_meters(df.longitude,df.latitude)
+    """
+    if isinstance(longitude, (list, tuple)):
+        longitude = np.array(longitude)
+    if isinstance(latitude, (list, tuple)):
+        latitude = np.array(latitude)
+
+    origin_shift = np.pi * 6378137
+    easting = longitude * origin_shift / 180.0
+    northing = np.log(np.tan((90 + latitude) * np.pi / 360.0)) * origin_shift / np.pi
+    return (easting, northing)
+
+
+description = Div(text="""<b><code>tile_demo.py</code></b> - Bokeh tile provider examples. Linked Pan and Zoom on all maps!""")
+
+# pick a location and generate a 4-point window around it: bottom-left, upper-right
+lat = 30.268801   # Lady Bird Lake, Austin Texas
+lon = -97.763347
+
+EN = lnglat_to_meters(lon,lat)
+dE = 1000 # (m) Easting  plus-and-minus from map center
+dN = 1000 # (m) Northing plus-and-minus from map center
+
+x_range = DataRange1d(start=EN[0]-dE , end=EN[0]+dE) # (m) Easting  x_lo, x_hi
+y_range = DataRange1d(start=EN[1]-dN , end=EN[1]+dN) # (m) Northing y_lo, y_hi
+
+plot = [0]*len(Vendors) # initialize list to store Vendor plots
+idx = 0
+for vendor_name in Vendors:
+    print("cnt={0}: Vendor={1}".format(idx,vendor_name))
+    tile_provider = get_provider(vendor_name)
+
+    plot[idx] = figure( x_range=x_range, y_range=y_range,
+                        x_axis_type="mercator", y_axis_type="mercator",
+                        plot_height=200, plot_width=300, title=vendor_name)
+
+    plot[idx].add_tile(tile_provider)
+    idx += 1
+
+
+## arrange all map views in a grid layout
+layout = layout([
+    [description],
+    [plot[0], plot[1], plot[2]],
+    [plot[3], plot[4], plot[5]],
+    [plot[6], plot[7], plot[8]],
+    [plot[9]                  ]])
+
+show(layout)


Test patch: