akrishnamo
akrishnamo

Reputation: 459

Is it possible to draw lines with arrowheads in a Folium map?

I am running Folium 0.2.1' with Python 2.7.11 on Jupyter Notebook Server 4.2.1

I am trying to plot lines on a map, which have a arrowhead to convey direction

import folium

#DFW, LGA coordinates
coordinates=[(32.900908, -97.040335),(40.768571, -73.861603)]

m = folium.Map(location=[32.900908, -97.040335], zoom_start=4)

#line going from dfw to lga
aline=folium.PolyLine(locations=coordinates,weight=2,color = 'blue')
m.add_children(aline)

enter image description here Is there a way to add an arrowhead to the line?

Upvotes: 11

Views: 14705

Answers (3)

Marcos
Marcos

Reputation: 11

I found a solution for this issue: I calculate my own arrow points with trigonometry and then I call polyline function using that points.

    def arrow_points_calculate(self, ini_lat, ini_long, heading):
    lenght_scale = 0.00012
    sides_scale = 0.000025
    sides_angle = 25

    latA= ini_lat
    longA = ini_long

    latB = lenght_scale * math.cos(math.radians(heading)) + latA
    longB = lenght_scale * math.sin(math.radians(heading)) + longA

    latC = sides_scale * math.cos(math.radians(heading + 180 - sides_angle)) + latB
    longC = sides_scale * math.sin(math.radians(heading + 180 - sides_angle)) + longB

    latD = sides_scale * math.cos(math.radians(heading + 180 + sides_angle)) + latB
    longD = sides_scale * math.sin(math.radians(heading + 180 + sides_angle)) + longB

    pointA = (latA, longA)
    pointB = (latB, longB)
    pointC = (latC, longC)
    pointD = (latD, longD)

    point = [pointA, pointB, pointC, pointD, pointB]
    return point


folium.PolyLine(locations=points, color="purple").add_to(
                        position_plot)

Lenght_scale and Side_scale variables must be modified depends of the arrow size you want. If you have the coords of start and finish of the arrow, just use the final coords as point B and calculate Side Scale relative to the length between that points (20% of the length between points is a correct scale on my opinion).

Example of the result: Position plot + heading arrows

Hope it could help

Upvotes: 1

Tobias Triesch
Tobias Triesch

Reputation: 162

I may be a little late to the party, but I have another suggestions for other people bothered by this problem. I would suggest to use the pyproj package's Geod class, which can do geodetic and great circle calculations. We can use it to get forward and backward azimuth of a piece of a LineString. Then for each piece we add a small polygon marker(or something similar) on one end.

from pyproj import Geod
# loop your lines
for line in lines.itertuples():
    # format coordinates and draw line
    loc = [[j for j in reversed(i)] for i in line.geometry.coords]
    folium.PolyLine(loc, color="red").add_to(m)
    # get pieces of the line
    pairs = [(loc[idx], loc[idx-1]) for idx, val in enumerate(loc) if idx != 0]
    # get rotations from forward azimuth of the line pieces and add an offset of 90°
    geodesic = Geod(ellps='WGS84')
    rotations = [geodesic.inv(pair[0][1], pair[0][0], pair[1][1], pair[1][0])[0]+90 for pair in pairs]
    # create your arrow
    for pair, rot in zip(pairs, rotations):
        folium.RegularPolygonMarker(location=pair[0], color='red', fill=True, fill_color='red', fill_opacity=1,
                                    number_of_sides=3, rotation=rot).add_to(m)

I hope someone will find this snippet helpful. Have a great day! =)

Upvotes: 6

user3456239
user3456239

Reputation: 116

You could use a regular polygon marker to draw a triangle at the end point...

folium.RegularPolygonMarker(location=(32.900908, -97.040335), fill_color='blue', number_of_sides=3, radius=10, rotation=???).add_to(m)

You'll have to use some trigonometry to calculate the angle of rotation for the triangle to point in the correct direction. The initial point of any such marker points due east.

Upvotes: 9

Related Questions