Multipolygons in Bokeh

Hello everyone.

I have to make a choropleth map and I have a shapefile for this. I read the geometry of this shapefile and the geometry is a MultiPolygon, which I don’t know how to use it.

import geopandas as gpd

shape_adress = r"C:\Users\DIRECTORY\Bokeh"

geodf = gpd.read_file(shape_adress)

According to this link I used the following function:

def getPolyCoords(row, geom, coord_type):
    """Returns the coordinates ('x' or 'y') of edges of a Polygon exterior"""

    # Parse the exterior of the coordinate
    exterior = row[geom].exterior

    if coord_type == 'x':
        # Get the x coordinates of the exterior
        return list( exterior.coords.xy[0] )
    elif coord_type == 'y':
        # Get the y coordinates of the exterior
        return list( exterior.coords.xy[1] )
Then I wrote the following:
geodf['x'] = geodf.apply(getPolyCoords, geom='geometry', coord_type='x', axis=1)
geodf['y'] = geodf.apply(getPolyCoords, geom='geometry', coord_type='y', axis=1)

And I recieved this error:

AttributeError: ("‘MultiPolygon’ object has no attribute ‘exterior’", ‘occurred at index 0’)

Is there a way to overcome this? I have also attached a print screen of my shapefile. The color column is the one that I want to visualize and the PC4 column is the postal code distric that I want to visualize.
Thanks in advance!


<img src='//bokeh-discourse-uploads.s3.dualstack.us-east-1.amazonaws.com/original/1X/d16af1204de79e90bb828548e6e02511bca75b8b.png' width='711' height='292'>

Hey,

You should be able to export your GeoPandas DataFrame directly to json, and load that with Bokeh. See this for a full example:

It basically comes down to:

from bokeh.models import GeoJSONDataSource

geo_source = GeoJSONDataSource(geojson=geodf.to_json())

p.patches(‘xs’, ‘ys’, source=geo_source)

``


I hope this gets you on the right track.

Regards,
Rutger

···

On Wednesday, November 8, 2017 at 3:33:03 PM UTC+1, Lefteris Koulierakis wrote:

Hello everyone.

I have to make a choropleth map and I have a shapefile for this. I read the geometry of this shapefile and the geometry is a MultiPolygon, which I don’t know how to use it.

import geopandas as gpd

shape_adress = r"C:\Users\DIRECTORY\Bokeh"

geodf = gpd.read_file(shape_adress)

According to this link I used the following function:

def getPolyCoords(row, geom, coord_type):
    """Returns the coordinates ('x' or 'y') of edges of a Polygon exterior"""

    # Parse the exterior of the coordinate
    exterior = row[geom].exterior

    if coord_type == 'x':
        # Get the x coordinates of the exterior
        return list( exterior.coords.xy[0] )
    elif coord_type == 'y':
        # Get the y coordinates of the exterior
        return list( exterior.coords.xy[1] )
Then I wrote the following:
geodf['x'] = geodf.apply(getPolyCoords, geom='geometry', coord_type='x', axis=1)
geodf['y'] = geodf.apply(getPolyCoords, geom='geometry', coord_type='y', axis=1)

And I recieved this error:

AttributeError: ("‘MultiPolygon’ object has no attribute ‘exterior’", ‘occurred at index 0’)

Is there a way to overcome this? I have also attached a print screen of my shapefile. The color column is the one that I want to visualize and the PC4 column is the postal code distric that I want to visualize.
Thanks in advance!

Thanks for the help! It actually worked! I’ve been struggling a lot with this!

Unfortunately, the graph that I get from matplotlib is different from the bokeh graph and I can’t understand why… You can see the picture below. The column that defines the color is called ‘Color’.

This is the code that I wrote:

shape_adress = r"C:DIRECTORY\Bokeh\shp_brabant_lim.shp"

counties = gpd.read_file(shape_adress)

geojson = counties.to_json()

color_column = ‘Color’

geo_source = GeoJSONDataSource(geojson=geojson)

p = figure(title=“Accumulated”)

p.patches(‘xs’, ‘ys’, fill_alpha=0.7,

fill_color={‘field’: color_column, ‘transform’: LogColorMapper(palette=‘Blues8’)},

line_color=‘black’, line_width=0.5, source=geo_source)

show§

Hey,

Not being able to look at the data itself, i’m having to guess here. First of all, one of the colormaps seems to be reversed compared to the other. All standard Matplotlib colormaps have a reversed counterpart which you can use by adding the ‘_r’ postfix in the name. I’m not sure how that would work in Bokeh. Its possible of course, but reversing in Matplotlib might be easiest.

Secondly, did you notice that my Bokeh example uses the “LogColorMapper”. Your Matplotlib colorbar looks linear, so you might want to use the “LinearColorMapper” with Bokeh as well.

If this doesn’t solve it, it might help if you share the source of the data so i can try it myself (or use some other public dataset). It looks like standard CBS data, but i assume you manually joined some tabular data to the ‘gemeenten’ shapefile?

Regards,
Rutger

···

On Thursday, November 9, 2017 at 9:49:51 AM UTC+1, Lefteris Koulierakis wrote:

Thanks for the help! It actually worked! I’ve been struggling a lot with this!

Unfortunately, the graph that I get from matplotlib is different from the bokeh graph and I can’t understand why… You can see the picture below. The column that defines the color is called ‘Color’.

This is the code that I wrote:

shape_adress = r"C:DIRECTORY\Bokeh\shp_brabant_lim.shp"

counties = gpd.read_file(shape_adress)

geojson = counties.to_json()

color_column = ‘Color’

geo_source = GeoJSONDataSource(geojson=geojson)

p = figure(title=“Accumulated”)

p.patches(‘xs’, ‘ys’, fill_alpha=0.7,

fill_color={‘field’: color_column, ‘transform’: LogColorMapper(palette=‘Blues8’)},

line_color=‘black’, line_width=0.5, source=geo_source)

show§

You were right! Changing it to LinearColorMapper made the difference.
The only difference is that now the colors are reversed: Instead of white in matplotlib, I have dark blue in bokeh and reversely, but this is not a problem.

Thank you very much again!

Hi,

I can't say for certain, but I very much doubt that it is the precision (i.e. the number of decimal positions) that would be an issue. Rather, it is almost certainly the total number of points being contributing to slowness.

Bokeh has a notion of "Level of Detail" (LOD) downsampling, that can help maintain interactivity when there are a large number of points. Basically, while there is a pan, zoom, etc Bokeh only draws a fraction of the points. But Bokeh only looks a the "top" level number of points to determine if it should turn LOD on. If you have a "smallish" number of patches, that each have a lot of points (or add up to alot points) Bokeh will not turn on LOD because it's only counting the number of polygons, which is not large.

However this is configurable. You can lower the LOD threshold to be less than your number of patches you have, then Bokeh will turn it on for interactive use:

  https://bokeh.pydata.org/en/dev/docs/user_guide/tools.html#controlling-level-of-detail

The other option would be to reduce the number of points in the polygons themselves, before plotting. There are libraries and utilities that will do this kind if simplification but I am not personally familiar with them (others here may know).

Thanks,

Bryan

···

On Nov 9, 2017, at 08:04, Lefteris Koulierakis <[email protected]> wrote:

I have one more question about this:
The interactive map produced by bokeh is quite slow to use (zoom, move, hover) and I think that this is because my shapefile has 15 decimal points. Do you know if there is a way to reduce the decimal points to five?
I have tried several things but I wasn't successful so far.

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/5175b3f1-1be4-4967-b4d2-4ddf05c8ef07%40continuum.io.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.

The name of my columns now is yyyy-mm.

Unfortunately, my HoverTool doesn’t display the data I want to. This is how my data looks like:

And this is what I get:

You see that the “Numbers” are not displayed correctly. This is my code for the hovertool:

hover = p.select_one(HoverTool)

hover.point_policy = “follow_mouse”

hover.tooltips = [

("Municipality", "@Municipality"),

("Numbers", "@2012-02"),

]

Bokeh probably things that there is a subtraction taking place here (“Numbers”, “@2012-02”)?

Use braces around the column name, e.g

  @{var-name}

For column names that are not valid JS identifies (i.e. if they have dashes inside them).

Thanks,

Bryan

···

On Nov 16, 2017, at 03:31, Lefteris Koulierakis <[email protected]> wrote:

The name of my columns now is yyyy-mm.
Unfortunately, my HoverTool doesn't display the data I want to. This is how my data looks like:

And this is what I get:

You see that the "Numbers" are not displayed correctly. This is my code for the hovertool:

hover = p.select_one(HoverTool)
hover.point_policy = "follow_mouse"
hover.tooltips = [
    ("Municipality", "@Municipality"),
    ("Numbers", "@2012-02"),
    ]
Bokeh probably things that there is a subtraction taking place here ("Numbers", "@2012-02")?

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/98a8a5a9-914f-47a1-8b51-87fbf8709323%40continuum.io.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.

It worked. Thank you very much.

Lefteris