loggi / loggibud

Real-world benchmarks for urban delivery problems, including vehicle routing and facility location problems.
MIT License
177 stars 31 forks source link

Add functionality to plot a solution #21

Closed fillipe-gsm closed 3 years ago

fillipe-gsm commented 3 years ago

Description

According to this issue, it would be interesting to have a function to plot also the solution of a VPR solver. This PR creates a module for that.

There are two functions for plotting solutions. The first shows the routes with street outlines, and thus it requires an OSRM server available.

image

The second is simpler and plots only the edges between each delivery:

image

Of course, in large problems the plotting may become too messy. A suggestion to overcome this is to plot less routes. Thus, instead of attempting to select which routes would be the most appropriate, we leave the decision to the user. For that, there is an auxiliary variable route_indices_to_plot to help with that task.

image

image

gabisurita commented 3 years ago

I suggest plotting the routes over streets instead. OSRM can help you with that.

There's is code I wrote in the past that might help you.

import polyline
import folium

def route_wiring(points):
    coords_uri = ";".join(
        ["{},{}".format(point.lng, point.lat) for point in points]
    )

    response = requests.get(
        f"{OSRM_HOST}/route/v1/driving/{coords_uri}?overview=simplified" , 
        timeout=600,
    )

    data = response.json()
    line = data["routes"][0]["geometry"]

    return [Point(lng, lat) for lat, lng in polyline.decode(line)]

def plot_route(points):

    wiring = route_wiring(points)

    m = folium.Map(
        location=np.mean([[p.lat, p.lng] for p in wiring], axis=0),
        zoom_start=13,
        tiles='cartodbpositron',
    )

    folium.PolyLine([[p.lat, p.lng] for p in wiring]).add_to(m)

    folium.CircleMarker([points[0].lat, points[0].lng], color="#ff0000", fill=True, radius=2).add_to(m)

    for point in points[1:]:
        folium.CircleMarker([point.lat, point.lng], color="#000000", fill=True, radius=1).add_to(m)

    return m
fillipe-gsm commented 3 years ago

I suggest plotting the routes over streets instead. OSRM can help you with that.

There's is code I wrote in the past that might help you.

import polyline
import folium

def route_wiring(points):
    coords_uri = ";".join(
        ["{},{}".format(point.lng, point.lat) for point in points]
    )

    response = requests.get(
        f"{OSRM_HOST}/route/v1/driving/{coords_uri}?overview=simplified" , 
        timeout=600,
    )

    data = response.json()
    line = data["routes"][0]["geometry"]

    return [Point(lng, lat) for lat, lng in polyline.decode(line)]

def plot_route(points):

    wiring = route_wiring(points)

    m = folium.Map(
        location=np.mean([[p.lat, p.lng] for p in wiring], axis=0),
        zoom_start=13,
        tiles='cartodbpositron',
    )

    folium.PolyLine([[p.lat, p.lng] for p in wiring]).add_to(m)

    folium.CircleMarker([points[0].lat, points[0].lng], color="#ff0000", fill=True, radius=2).add_to(m)

    for point in points[1:]:
        folium.CircleMarker([point.lat, point.lng], color="#000000", fill=True, radius=1).add_to(m)

    return m

Thanks for the suggestion! I adapted your code to plot all routes in a solution. Also, I decided to keep the previous function as fallback for a simpler visualization without the OSRM request (and mostly because it was already done :slightly_smiling_face: ), but if you wish we can remove and keep only the main one.