Note : The examples presented below are the copy of the ones presented on https://github.com/bbecquet/Leaflet.PolylineOffset
The three are distinct Polyline objects but uses the same coordinate array
import folium
from folium import plugins
m = folium.Map(location=[58.0, -11.0], zoom_start=4, tiles="cartodbpositron")
coords = [
[58.44773, -28.65234],
[53, -23.33496],
[53, -14.32617],
[58.1707, -10.37109],
[59, -13],
[57, -15],
[57, -18],
[60, -18],
[63, -5],
[59, -7],
[58, -3],
[56, -3],
[60, -4],
]
plugins.PolyLineOffset(
coords, weight=2, dash_array="5,10", color="black", opacity=1
).add_to(m)
plugins.PolyLineOffset(coords, color="#f00", opacity=1, offset=-5).add_to(m)
plugins.PolyLineOffset(coords, color="#080", opacity=1, offset=10).add_to(m)
m
A more complex demo. Offsets are computed automatically depending on the number of bus lines using the same segment. Other non-offset polylines are used to achieve the white and black outline effect.
m = folium.Map(location=[48.868, 2.365], zoom_start=15)
geojson = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {"lines": [0, 1]},
"geometry": {
"type": "LineString",
"coordinates": [
[2.357919216156006, 48.87621773324153],
[2.357339859008789, 48.874834693731664],
[2.362983226776123, 48.86855408432749],
[2.362382411956787, 48.86796126699168],
[2.3633265495300293, 48.86735432768131],
],
},
},
{
"type": "Feature",
"properties": {"lines": [2, 3]},
"geometry": {
"type": "LineString",
"coordinates": [
[2.351503372192383, 48.86443950493823],
[2.361609935760498, 48.866775611250205],
[2.3633265495300293, 48.86735432768131],
],
},
},
{
"type": "Feature",
"properties": {"lines": [1, 2]},
"geometry": {
"type": "LineString",
"coordinates": [
[2.369627058506012, 48.86619159489603],
[2.3724031448364253, 48.8626397112042],
[2.3728322982788086, 48.8616233285001],
[2.372767925262451, 48.86080456075567],
],
},
},
{
"type": "Feature",
"properties": {"lines": [0]},
"geometry": {
"type": "LineString",
"coordinates": [
[2.3647427558898926, 48.86653565369396],
[2.3647642135620117, 48.86630981023694],
[2.3666739463806152, 48.86314789481612],
[2.3673176765441895, 48.86066339254944],
],
},
},
{
"type": "Feature",
"properties": {"lines": [0, 1, 2, 3]},
"geometry": {
"type": "LineString",
"coordinates": [
[2.3633265495300293, 48.86735432768131],
[2.3647427558898926, 48.86653565369396],
],
},
},
{
"type": "Feature",
"properties": {"lines": [1, 2, 3]},
"geometry": {
"type": "LineString",
"coordinates": [
[2.3647427558898926, 48.86653565369396],
[2.3650002479553223, 48.86660622956524],
[2.365509867668152, 48.866987337550164],
[2.369627058506012, 48.86619159489603],
],
},
},
{
"type": "Feature",
"properties": {"lines": [3]},
"geometry": {
"type": "LineString",
"coordinates": [
[2.369627058506012, 48.86619159489603],
[2.372349500656128, 48.865702850895744],
],
},
},
],
}
# manage overlays in groups to ease superposition order
outlines = folium.FeatureGroup("outlines")
line_bg = folium.FeatureGroup("lineBg")
bus_lines = folium.FeatureGroup("busLines")
bus_stops = folium.FeatureGroup("busStops")
line_weight = 6
line_colors = ["red", "#08f", "#0c0", "#f80"]
stops = []
for line_segment in geojson["features"]:
# Get every bus line coordinates
segment_coords = [[x[1], x[0]] for x in line_segment["geometry"]["coordinates"]]
# Get bus stops coordinates
stops.append(segment_coords[0])
stops.append(segment_coords[-1])
# Get number of bus lines sharing the same coordinates
lines_on_segment = line_segment["properties"]["lines"]
# Width of segment proportional to the number of bus lines
segment_width = len(lines_on_segment) * (line_weight + 1)
# For the white and black outline effect
folium.PolyLine(
segment_coords, color="#000", weight=segment_width + 5, opacity=1
).add_to(outlines)
folium.PolyLine(
segment_coords, color="#fff", weight=segment_width + 3, opacity=1
).add_to(line_bg)
# Draw parallel bus lines with different color and offset
for j, line_number in enumerate(lines_on_segment):
plugins.PolyLineOffset(
segment_coords,
color=line_colors[line_number],
weight=line_weight,
opacity=1,
offset=j * (line_weight + 1) - (segment_width / 2) + ((line_weight + 1) / 2),
).add_to(bus_lines)
# Draw bus stops
for stop in stops:
folium.CircleMarker(
stop,
color="#000",
fill_color="#ccc",
fill_opacity=1,
radius=10,
weight=4,
opacity=1,
).add_to(bus_stops)
outlines.add_to(m)
line_bg.add_to(m)
bus_lines.add_to(m)
bus_stops.add_to(m)
m