I have two overlapping isochrones -- 10 minutes by bike from 2 neighbouring railway station -- and I want to split them in a balanced way, so as to determine the bikeshed of each railway station. The separation should occur at equi-distance from each station.

two overlapping isochrones and the desired split limit in red

On the image form QGIS the green polygons are the isochrones around railway stations represented by red stars. I have added a red line showing where I would like the two isochrones to be separated. How could I process this in QGIS?

  • 3
    What defines that red line? Is it half way between the red stars? Does it divide the green polygons in half? What are the green polygons? What do you mean by "split in a balanced way"?
    – Spacedman
    Feb 9, 2017 at 10:04
  • thanks Spacedman for helping me to precise my problem; indeed as my problem is separating areas of influence of stations, the red line is half way, so a solutions lies in using Voronoi polygons for splitting the buffers Feb 9, 2017 at 13:55
  • 1
    Given that you've gone to the trouble of generating isochrones, you're obviously interested in more realistic travel choices. You might want to consider why someone in the isochrone for the RH station but left of the red line, might still want to go to the RH station, even if they are physically closer to the left: the RH station might have more frequent/faster/useful services. Feb 10, 2017 at 9:22
  • I agree with what you say George of all trades, this is a matter of travel choice. Before refining with railway services, it would be better to trace the limit between the two stations based on actual travel travel times and not on geometry, even if the second option gives already a good approximation in my sense. Feb 13, 2017 at 9:44

2 Answers 2


To split overlapping isochrones to determine areas of influence, a possibility is to generate a set of Voronoi polygons based on central points of the isochrones, in this case railway stations. Voronoi lines will be geometrically at equi-distance between neighbouring railway stations, which is an approximate of the limit of the respective areas of influence. The steps in QGIS are then:

  1. Dissolve all isochrones in one single poly-polygon
  2. Generate Voronoi polygons based on railway stations
  3. Transform Voronoi polygons into lines (not sure this is necessary)
  4. Split the dissolved isochron with the Voronoi lines

The end result is the bikeshed of each station without overlap.


Here is a code that first uses Graphhoper API to calculate isochrones then splits them with with geovoronoi library(https://pypi.org/project/geovoronoi/)

from routingpy import Graphhopper
import geopandas as gdp 
from shapely.geometry import Polygon, MultiPolygon, MultiLineString, Point
from geovoronoi import voronoi_regions_from_coords
import numpy as np 
from datetime import datetime

#This algorithm calculates influence areas of store points based on the isochrone method from Graphhopper library 
# which is run on a local server. The algorithm has the following steps:
#1) Calculate isochrones for input coordinates
#2) For isochrones that intersect each other merge them togteher into a single polygon
#3) Convert the input coordinates into numpy arrays
#4) Split the merged polygons into voronoi regions associated with the points that are contained within them 

begin = datetime.now()
print("Welcome to Isochrone Maker!")
# input coordiniates 
coords = [[13.413706, 52.490202], 
        [13.421838, 52.514105],
        [13.453649, 52.507987]]
# set the projection of the output
crs = {'init': 'epsg:4326'}
list_of_polygons = []
# loop through the coordinates
for i in coords:
    # call Graphhopper API and calculate isochrones
    client = Graphhopper(base_url='http://localhost:8989')
    route = client.isochrones(
        locations = i,
        intervals = [20000]
    # convert isochrone geometry to shapely format
    polygon_geom = Polygon(route[0].geometry)
    # find the intersection of polygons
    if len(list_of_polygons) > 1:
    # start the loop from the one end of the polygon list
        for t in reversed(list_of_polygons):
        # start loop through the other side of the polygon list
            for x in list_of_polygons:
                if x != t:
                    if t.is_valid & x.is_valid:
                        if t.intersects(x):
                            union = t.union(x)

dif_points = []
new_array = []
for i in list_of_polygons:
    points_list = []
    for x in coords:
        y = Point(x)
        if y.within(i):
    new_array = np.array(new_array)

list_of_shit = []
for i in list_of_polygons:
    for x in dif_points:
        poly_shapes, pts, poly_to_pt_assingments = voronoi_regions_from_coords(x, i)


for i in list_of_shit:
    if len(list_of_shit) == 1:            
        multi = MultiPolygon(list_of_shit[0])
        multi_1 = gdp.GeoDataFrame(index=[0], crs=crs, geometry=[multi])
        multi_1.to_file("shape.geojson", driver="GeoJSON")
        multi = MultiPolygon(list_of_shit)
        multi_1 = gdp.GeoDataFrame(index=[0], crs=crs, geometry=[multi])
        multi_1.to_file("shape.geojson", driver="GeoJSON")

print("The algorithm finished! The new file called Shape has been created in geojson format")
print("{} isochrones have been created for {} input points".format(len(multi), len(coords)))
print("Elapsed time: {}".format(datetime.now() - begin))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.