38 Interactive plotting with Bokeh in GemGIS#

The bokeh package allows it to create interactive plots of the available geo data.

Set File Paths and download Tutorial Data#

If you downloaded the latest GemGIS version from the Github repository, append the path so that the package can be imported successfully. Otherwise, it is recommended to install GemGIS via pip install gemgis and import GemGIS using import gemgis as gg. In addition, the file path to the folder where the data is being stored is set. The tutorial data is downloaded using Pooch (https://www.fatiando.org/pooch/latest/index.html) and stored in the specified folder. Use pip install pooch if Pooch is not installed on your system yet.

[1]:
import gemgis as gg

file_path ='../../../../gemgis_data/data/38_interactive_plotting_with_bokeh_in gemgis/'
WARNING (theano.configdefaults): g++ not available, if using conda: `conda install m2w64-toolchain`
C:\Users\ale93371\Anaconda3\envs\test_gempy\lib\site-packages\theano\configdefaults.py:560: UserWarning: DeprecationWarning: there is no c++ compiler.This is deprecated and with Theano 0.11 a c++ compiler will be mandatory
  warnings.warn("DeprecationWarning: there is no c++ compiler."
WARNING (theano.configdefaults): g++ not detected ! Theano will be unable to execute optimized C-implementations (for both CPU and GPU) and will default to Python implementations. Performance will be severely degraded. To remove this warning, set Theano flags cxx to an empty string.
WARNING (theano.tensor.blas): Using NumPy C-API based implementation for BLAS functions.
[3]:
gg.download_gemgis_data.download_tutorial_data(filename="38_interactive_plotting_with_bokeh_in gemgis.zip", dirpath=file_path)
Downloading file '38_interactive_plotting_with_bokeh_in gemgis.zip' from 'https://rwth-aachen.sciebo.de/s/AfXRsZywYDbUF34/download?path=%2F38_interactive_plotting_with_bokeh_in gemgis.zip' to 'C:\Users\ale93371\Documents\gemgis_data\data\38_interactive_plotting_with_bokeh_in gemgis'.

Loading Libraries#

[2]:
import geopandas as gpd
import rasterio
WARNING (theano.configdefaults): g++ not available, if using conda: `conda install m2w64-toolchain`
C:\Users\ale93371\Anaconda3\envs\test_gempy\lib\site-packages\theano\configdefaults.py:560: UserWarning: DeprecationWarning: there is no c++ compiler.This is deprecated and with Theano 0.11 a c++ compiler will be mandatory
  warnings.warn("DeprecationWarning: there is no c++ compiler."
WARNING (theano.configdefaults): g++ not detected ! Theano will be unable to execute optimized C-implementations (for both CPU and GPU) and will default to Python implementations. Performance will be severely degraded. To remove this warning, set Theano flags cxx to an empty string.
WARNING (theano.tensor.blas): Using NumPy C-API based implementation for BLAS functions.

Importing Bokeh#

Bokeh is being imported and the output of the plots is set to be in the notebooks.

[3]:
from bokeh.plotting import figure, show
from bokeh.io import output_notebook, show
output_notebook()
Loading BokehJS ...

Plotting OSM Data#

The first data to be integrated is a Open Street Base Map. It can be downloaded by accessing the tile provider OSM via get_provider(..).

NB: When using a tile provider, all other data has to be reprojected to ``’EPSG:3857’`` (https://wiki.openstreetmap.org/wiki/EPSG:3857)

[4]:
from bokeh.tile_providers import OSM, get_provider

tile_provider = get_provider(OSM)
tile_provider
[4]:
WMTSTileSource(
id = '1002', …)

In order to display a plot, a figure needs to be created using figure(..) and the provided arguments. The range of the map was limited to the rough extent of the Münsterland Basin in Northern Germany.

[5]:
p = figure(title="Test",
           x_axis_label='X [m]',
           y_axis_label='Y [m]',
           match_aspect=True,
           plot_width=800,
           plot_height=600,
           x_range=(7.5e5, 10.5e5),
           y_range=(6.65e6, 6.90e6))

The OSM Map is now added to the figure using p.add_tile(..).

[6]:
p.add_tile(tile_provider)
[6]:
TileRenderer(
id = '1038', …)

Showing the plot#

The plot is shown using show(p).

[7]:
show(p)

Plotting Raster Data#

Loading Data#

The data used for GemGIS is obtained from OpenDataNRW. It will be used under Datenlizenz Deutschland – Namensnennung – Version 2.0 (https://www.govdata.de/dl-de/by-2-0) with © Geowissenschaftliche Daten: Digitales Höhenmodell NRW 1m (2020).

[8]:
data = rasterio.open(file_path + 'DEM50_reproj.tif')

The CRS of the raster was reprojected using gg.raster.reproject_raster(..) to match the CRS of the Open Street Map layer.

[9]:
data.crs
[9]:
CRS.from_epsg(3857)

A second figure was created.

[10]:
p = figure(title="Test",
           x_axis_label='X [m]',
           y_axis_label='Y [m]',
           match_aspect=True,
           plot_width=800,
           plot_height=600,
           x_range=(7.5e5, 10.5e5),
           y_range=(6.65e6, 6.90e6))

The OSM Layer will be added as well.

[11]:
p.add_tile(tile_provider)
[11]:
TileRenderer(
id = '1111', …)

Create GlypRenderer for Raster#

For a better representation of the raster, the nodata value of the raster will be replaced with the min value of the array/band.

[12]:
data.nodata
[12]:
9999.0
[13]:
data_cleaned = data.read(1)

data_cleaned[data_cleaned == data.nodata] = data.read(1).min()

An image will be added to the figure using the dimensions of the rasterio object.

[14]:
from bokeh.models.mappers import ContinuousColorMapper
import numpy as np

dem_renderer = p.image(image = [np.flipud(data_cleaned)],
                       x=data.bounds[0],
                       y=data.bounds[1],
                       dw=data.bounds[2]-data.bounds[0],
                       dh=data.bounds[3]-data.bounds[1],
                       legend_label="DEM",
                       palette = 'RdYlGn11'
                      )

Create Hover Tool for Bokeh Plot#

A HoverTool is created so that the height of the DEM will be returned at the respective cursor position when moving across the raster.

[15]:
from bokeh.models import HoverTool
dem_tool = HoverTool(tooltips=[('Height', '@image{0.00} m')],
                     renderers=[dem_renderer])

Showing the plot#

The plot can now be displayed. In addition, the HoverTool and and the option to hide the data is being added.

[16]:
p.add_tools(dem_tool)
p.legend.click_policy="hide"
show(p)

Plotting Vector Data - LineString Data#

Loading data#

Vector data can also be added to a Bokeh plot.

[17]:
faults = gpd.read_file(file_path + 'gg_nrw_geotekst_l.shp')
faults.head()
[17]:
OBJECTID_1 OBJECTID_2 Layer_Name Quelle ST_NAME OBJECTID Id ssymbol_QB ST_NR ST_ART_ID ... DIP_3D STOE_UEB_3 STOE_TOP_H STOE_BASIS ST_ART_3D Shape_Le_2 Versatz_TB Doku_Versa Shape_Le_3 geometry
0 1.00 1 Störung001_Swist-Sprung_SW Erft Scholle RWE Projekt 2015 Swist-Sprung 0 0 None 0.00 0 ... 62.50 überregionale Bedeutung DGM Tertiaer_b/Praeperm_t/Karbon_t None 37635.89 0.00 None 37635.89 LINESTRING (32361016.617 5608960.850, 32360902...
1 2.00 2 Störung002_Müggenhausener Sprung_SW Erft Scholle RWE Projekt 2015 Müggenhausener Sprung 0 0 None 0.00 0 ... 62.50 regionale Bedeutung DGM Tertiaer_b/Praeperm_t/Karbon_t None 21797.50 0.00 None 21797.50 LINESTRING (32358412.934 5611983.080, 32358293...
2 3.00 3 Störung003_Strassfelder Sprung_SW Erft Scholle RWE Projekt 2015 Strassfelder Sprung 0 0 None 0.00 0 ... 62.50 regionale Bedeutung DGM Tertiaer_b/Praeperm_t/Karbon_t None 23928.16 0.00 None 23928.16 LINESTRING (32360075.838 5610943.615, 32359971...
3 4.00 4 Störung003_Strassfelder Sprung_SW Erft Scholle RWE Projekt 2015 Strassfelder Sprung 0 0 None 0.00 0 ... 62.50 lokale Bedeutung DGM Tertiaer_b/Praeperm_t/Karbon_t None 56.58 0.00 None 56.58 LINESTRING (32341010.296 5623832.070, 32340983...
4 5.00 5 Störung004_Ludendorfer Sprung_Meckenheimer Sprung Erft Scholle RWE Projekt 2015 Ludendorfer Sprung_Meckenheimer Sprung 0 0 None 0.00 0 ... 62.50 regionale Bedeutung DGM Tertiaer_b/Praeperm_t/Karbon_t None 18578.06 0.00 None 18578.06 LINESTRING (32360936.158 5609018.641, 32360846...

5 rows × 47 columns

Empty and invalid entries are removed.

[18]:
faults = faults[~faults.is_empty]
faults = faults[faults.is_valid]

The data is reprojected to match the CRS of the OSM Layer.

[19]:
faults_reproj = faults.to_crs(epsg='3857')
faults_reproj.head()
[19]:
OBJECTID_1 OBJECTID_2 Layer_Name Quelle ST_NAME OBJECTID Id ssymbol_QB ST_NR ST_ART_ID ... DIP_3D STOE_UEB_3 STOE_TOP_H STOE_BASIS ST_ART_3D Shape_Le_2 Versatz_TB Doku_Versa Shape_Le_3 geometry
0 1.00 1 Störung001_Swist-Sprung_SW Erft Scholle RWE Projekt 2015 Swist-Sprung 0 0 None 0.00 0 ... 62.50 überregionale Bedeutung DGM Tertiaer_b/Praeperm_t/Karbon_t None 37635.89 0.00 None 37635.89 LINESTRING (783179.405 6553639.429, 782995.936...
1 2.00 2 Störung002_Müggenhausener Sprung_SW Erft Scholle RWE Projekt 2015 Müggenhausener Sprung 0 0 None 0.00 0 ... 62.50 regionale Bedeutung DGM Tertiaer_b/Praeperm_t/Karbon_t None 21797.50 0.00 None 21797.50 LINESTRING (778956.585 6558296.279, 778762.946...
2 3.00 3 Störung003_Strassfelder Sprung_SW Erft Scholle RWE Projekt 2015 Strassfelder Sprung 0 0 None 0.00 0 ... 62.50 regionale Bedeutung DGM Tertiaer_b/Praeperm_t/Karbon_t None 23928.16 0.00 None 23928.16 LINESTRING (781616.721 6556726.982, 781451.743...
3 4.00 4 Störung003_Strassfelder Sprung_SW Erft Scholle RWE Projekt 2015 Strassfelder Sprung 0 0 None 0.00 0 ... 62.50 lokale Bedeutung DGM Tertiaer_b/Praeperm_t/Karbon_t None 56.58 0.00 None 56.58 LINESTRING (751011.105 6576218.560, 750967.199...
4 5.00 5 Störung004_Ludendorfer Sprung_Meckenheimer Sprung Erft Scholle RWE Projekt 2015 Ludendorfer Sprung_Meckenheimer Sprung 0 0 None 0.00 0 ... 62.50 regionale Bedeutung DGM Tertiaer_b/Praeperm_t/Karbon_t None 18578.06 0.00 None 18578.06 LINESTRING (783050.468 6553727.191, 782907.509...

5 rows × 47 columns

The data is now being exploded to single LineStrings and the X and Y coordinates are being extracted.

[20]:
faults_reproj = gg.vector.explode_multilinestrings(gdf=faults_reproj)
faults_reproj = gg.vector.extract_xy_linestring(gdf=faults_reproj)

Creating ColumnDataSource#

The data is being converted to a ColumnDataSource. The geometry column of the GeoDataFrame must be dropped.

[33]:
faults_reproj = faults_reproj[['Shape_Le_2', 'X', 'Y', 'geometry']]
faults_reproj.head()
[33]:
Shape_Le_2 X Y geometry
0 37635.89 [783179.4051572308, 782995.935648582, 782902.3... [6553639.429076611, 6553792.618358821, 6553877... LINESTRING (783179.405 6553639.429, 782995.936...
1 21797.50 [778956.5848617974, 778762.9461795501, 778554.... [6558296.2788241105, 6558496.939616477, 655871... LINESTRING (778956.585 6558296.279, 778762.946...
2 23928.16 [781616.7214601886, 781451.7425967428, 781293.... [6556726.982401831, 6556744.251795183, 6556763... LINESTRING (781616.721 6556726.982, 781451.743...
3 56.58 [751011.1047211702, 750967.1987568956, 750960.... [6576218.559684751, 6576283.0530930245, 657629... LINESTRING (751011.105 6576218.560, 750967.199...
4 18578.06 [783050.4681690165, 782907.5090723939, 782818.... [6553727.191026189, 6553824.610592816, 6553889... LINESTRING (783050.468 6553727.191, 782907.509...
[34]:
from bokeh.models import ColumnDataSource
geo_data = ColumnDataSource(faults_reproj.drop('geometry', axis=1).sample(n=10))
geo_data
[34]:
ColumnDataSource(
id = '1522', …)
[35]:
p = figure(title="Test",
           x_axis_label='X [m]',
           y_axis_label='Y [m]',
           match_aspect=True,
           plot_width=800,
           plot_height=600,
           x_range=(7.5e5, 10.5e5),
           y_range=(6.65e6, 6.90e6))
[36]:
p.add_tile(tile_provider)
[36]:
TileRenderer(
id = '1560', …)

The data will now be added as multi_line to the the figure.

[37]:
faults_renderer = p.multi_line(xs='X',
                               ys='Y',
                               source=geo_data,
                               color='red',
                               line_width=3,
                               legend_label="Faults",
                               hover_line_alpha=1.0)
faults_renderer
[37]:
GlyphRenderer(
id = '1568', …)
[38]:
fault_hover_tool = HoverTool(tooltips=[('X_value', '$xs')],
                             renderers=[faults_renderer],
                             line_policy='nearest',
                             show_arrow=False,
                             mode='vline'
    )
#('FaultName', '@ST_NAME')
# ('LayerName', '@Layer_Name'),
# ('Dip', '@DIP_3D'),
# ('Length', '@Shape_Le_2')

Showing the plot#

The plot can now be displayed. In addition, the HoverTool and and the option to hide the data is being added.

[39]:
p.add_tools(fault_hover_tool)
#p.add_tools(dem_tool)
p.legend.click_policy="hide"
show(p)