Patches - Annotation Centroid

Hello,

I was asking myself if it was possible to put annotation (Label) in the center of glyph Patches. I know that hovertool find the centroid of polygon. I would like to label each polygon in an easy way.

Thank you

@Eola

The centroid is the geometric center, which is the mean position of the points. numpy has a built-in mean() function for numpy arrays. As does Pandas for its Series and DataFrames.

The bokeh Patches use lists of lists to plot the glyphs so you’ll need to coerce your data differently to generate the plots and annotations, but the calculations are easy.

Also, if you’re plotting a number of shapes, and not just one, use the LabelSet to add annotations as it is more efficient than calling Label once for each shape. Depending on where you want to place the labels relative to the centroid, set the text_align and text_baseline properties to adjust left/right and up/down respectively.

I would actually like to expose the “center” coordinates for glyphs to be available for use in hover tools, transforms, etc. but it’s only ever been an idea in the back of my mind so far. AFAIK there is not an issue, so if you want to submit an issue that would be helpful (a concrete use case always helps when prioritizing)

Thank you for all your responses. I wrote this code (maybe not the best) that compute the centroid of patches. The result is not bad but sometimes, label is outside of the patches. Maybe i miss a fonctionnality of patches that permit to know where is the area of patches. In fact, Hovertool knows if your cursor is in or out of the patch.

import json
from bokeh.models.tools import BoxZoomTool,WheelZoomTool
from bokeh.io import output_file, show, output_notebook
from bokeh.models import GeoJSONDataSource, ColumnDataSource
from bokeh.plotting import figure
from bokeh.sampledata.sample_geojson import geojson
from bokeh.plotting import figure
from bokeh.models import BBoxTileSource, WMTSTileSource, TileRenderer, HoverTool
from bokeh.io import show
from bokeh.io import output_file
from bokeh.layouts import column, row, gridplot
from bokeh.models import LabelSet
import requests
import pandas as pd

RADIUS=6378137.0
import math
def lat2y(a):
    if (type(a)==int) or (type(a)==float):
        return math.log(math.tan(math.pi / 4 + math.radians(a) / 2)) * RADIUS
    if type(a)==list or type(a)==tuple:
        re=[]
        for i in a:
            re.append(math.log(math.tan(math.pi / 4 + math.radians(i) / 2)) * RADIUS)
        return re

def lon2x(a):
    if type(a)==int or (type(a)==float):
        return math.radians(a) * RADIUS
    if type(a)==list or type(a)==tuple:
        re=[]
        for i in a:
            re.append(math.radians(i) * RADIUS)
        return re
    
    
    
rawdata = requests.get('https://cadastre.data.gouv.fr/bundler/cadastre-etalab/communes/83047/geojson/parcelles')
alldata = rawdata.json()


dfLabel=pd.DataFrame(columns=['Longitude','Latitude','numero'])

for i,feature in enumerate(alldata['features']):
    new_coords = []

    for ring in feature['geometry']['coordinates']:
        X=lon2x(list(list(zip(*ring))[0]))
        Y=lat2y(list(list(zip(*ring))[1]))
        new_coords.append([[i,j] for i,j in (zip(X,Y))])
    alldata['features'][i]['geometry']['coordinates'] = new_coords 
    
for k,l in enumerate(alldata['features']):
    Lon=0
    La=0
    for i,j in enumerate(alldata['features'][k]['geometry']['coordinates'][0]):
        Lon+=alldata['features'][k]['geometry']['coordinates'][0][i][0]
        La+=alldata['features'][k]['geometry']['coordinates'][0][i][1]

    La=La/(i+1)
    Lon=Lon/(i+1)
    dfLabel.loc[k,'Longitude']=Lon
    dfLabel.loc[k,'Latitude']=La
    dfLabel.loc[k,'numero']= alldata['features'][k]['properties']['section'] +  alldata['features'][k]['properties']['numero']
    

    
    
TOOLS = "pan,wheel_zoom,reset,hover,save"
geosource = WMTSTileSource(url='https://tile.openstreetmap.org/{Z}/{X}/{Y}.png',max_zoom=25,min_zoom=0)
p = figure(x_range=(lon2x(6.05), lon2x(6.15)), y_range=(lat2y(43.1), lat2y(43.24)),
           x_axis_type="mercator", y_axis_type="mercator",output_backend='webgl', tools=["pan,wheel_zoom,reset,hover,save"],tooltips=[
        ("Name", "@numero"), ("(Long, Lat)", "($x, $y)")
    ],active_scroll="wheel_zoom",active_drag="pan")
p.add_tile(geosource)

geo_source = GeoJSONDataSource(geojson=json.dumps(alldata))
p.patches(xs="xs", ys="ys", line_width=1.5,  line_color='black',color= None, source=geo_source,alpha=0.5)
source2=ColumnDataSource(dfLabel)
labels = LabelSet(
            x='Longitude',
            y='Latitude',
            text='numero',
            source=source2,render_mode='canvas' ,text_font_size='10px')
p.add_layout(labels)

show(p)

I welcome criticism of my code.

@Bryan P.S : Your Library is awesome. When 3d plotting will be integrate, everyone would use your library. Thanks