DEV Community

Eamon Magd
Eamon Magd

Posted on

Using Python in QGIS to scale up London's cycle infrastructure to match Amsterdam.

I've created a tutorial that shows you how to use code to scale up roads, and cycle paths, or even modify highway types between cities!
Bonus: There's also a separate video diving deeper into a more advanced method specifically for modifying cycle paths.
Clip from a youtube video that shows the lack of cycle lanes in the UKIn the UK, transport is the largest sector for carbon emissions, with cars being the main contributor. Cars even produce more emissions than planes, which is surprising! So, let's find a way to scale up cycle data using object detection and a geographic platform.
In the UK, transport is the largest sector for carbon emissions, with cars being the main contributors. Cars even produce more emissions than planes, which is surprising! So, let's find a way to scale up cycle data using object detection and a geographic platform.
Final output :

Image description

Step 1: Getting Polygon Data for Cycleways

For this tutorial, we will use QGIS to modify and manage shape files (.shp), which are commonly used for maps.
Install QuickOSM Plugin: In QGIS, go to Manage and Install Plugins, and find the QuickOSM plugin. QuickOSM uses OpenStreetMap data to obtain cycle data.
Configure QuickOSM: Click the QuickOSM logo. For the key, type highway, and for the value, type cycleway.
Input Location: Under the input presets, use the dropdown to select where you want the data to be searched-for example, type London for our case. Click Run Query. Repeat this process for the other city you are interested in. Ensure to remove any layers that are points.

Image description
The output for your city's cycleway will be shown in a couple of layers from points and polygons for the city of choice.

Image description
London cycle infrastructure before removing point data

Delete any layers that are not line polygons so the only layer will look like this

Image description

Lastly do the same steps for Amsterdam so we can compare and contrast the values.

Step 2: Getting the Total Length of All Cycleways

To determine how much we need to scale the data, follow these steps:
Open Python Console in QGIS: We'll use the qgis.core and PyQt5 modules.
Find and Print Layers: Replace the code for layer_name with highway=cycleway.
Calculate Total Length: Iterate through each layer, to sum up the total kilometers of cycleways. Do this for both cities (e.g., London and Amsterdam).

You can run the Python code below to get the output in kilometres

# Import necessary modules
from qgis.core import QgsProject

# Print all layer names to debug and find the correct name
layer_names = [layer.name() for layer in QgsProject.instance().mapLayers().values()]
print("Available layers:", layer_names)

# Replace with the actual name of your cycleway layer
layer_name = 'highway_cycleway_Amsterdam'

# Load the layer by name
layers = QgsProject.instance().mapLayersByName(layer_name)

if not layers:
    raise ValueError(f"Layer '{layer_name}' not found. Available layers: {layer_names}")

layer = layers[0]

# Initialize total length variable
total_length = 0

# Iterate over each feature in the layer
for feature in layer.getFeatures():
    geom = feature.geometry()
    if geom:
        total_length += geom.length()

# Convert length to kilometers (assuming the layer CRS is in meters)
total_length_km = total_length  

# Print the total length in kilometers
print("Total length of cycleways in the Amsterdam: {:.2f} km".format(total_length_km))
Enter fullscreen mode Exit fullscreen mode

Step 3: Finding the Total Area for Your City

To find the rate of change, we first need a scale. For this, we'll use polygon shapefiles provided by OSM boundaries and download them for each city.
Load Shapefiles in QGIS: Right-click the polygon layer in the layer panel. Select Open Attribute Table, then click the Field Calculator button.
Calculate Area: Create a new field named area_km2 and use the expression $area / 1000000 to calculate the total area in square kilometres. Alternatively, you can search for the area of the cities online.
Determine Rate of Cycle Lanes per Area: Divide the total cycleway length from Step 2 by the total area of each city to get the rate of cycle lanes per square kilometre.

Image description
Example polygon to find the total area

Calculate the Rate of Increase: Divide the rate of the city you want to scale up (e.g., London) by the rate of the reference city (e.g., Amsterdam) to get the rate of increase.

Step 4: Scaling the Cycleway Data

Finally, we'll scale the cycleways:
Use QgsProject Package: We will use the QgsProject package and its extendLine function.
Apply Scaling Factor: For our example, we will increase the cycleway lengths by a factor of six. This involves finding each part of the polyline and multiplying the length by six.

Scaling Factor Code below:


from qgis.core import QgsProject, QgsFeature, QgsGeometry, QgsPointXY, QgsVectorLayer
from PyQt5.QtCore import QVariant

# Load the layer
layer_name = 'highway_cycleway_London'
layers = QgsProject.instance().mapLayersByName(layer_name)

if not layers:
    raise ValueError(f"Layer '{layer_name}' not found.")
layer = layers[0]

# Create a new layer for the extended lines
extended_layer = QgsVectorLayer("LineString?crs={}".format(layer.crs().authid()), 
                                "extended_highway_cycle_London", "memory")
provider = extended_layer.dataProvider()
provider.addAttributes(layer.fields())
extended_layer.updateFields()

# Function to extend a line
def extend_line(line, factor):
    if line.isMultipart():
        new_points = []
        for part in line.asMultiPolyline():
            new_part = extend_single_line(part, factor)
            new_points.append(new_part)
        return QgsGeometry.fromMultiPolylineXY(new_points)
    else:
        points = line.asPolyline()
        new_points = extend_single_line(points, factor)
        return QgsGeometry.fromPolylineXY(new_points)

def extend_single_line(points, factor):
    if len(points) < 2:
        return points  # not enough points to extend
    start_point = points[0]
    end_point = points[-1]

    # Calculate vector from start to end
    vec_x = end_point.x() - start_point.x()
    vec_y = end_point.y() - start_point.y()

    # Extend vector
    extended_end_point = QgsPointXY(
        start_point.x() + vec_x * factor,
        start_point.y() + vec_y * factor
    )

    new_points = [start_point]
    for point in points[1:-1]:
        new_points.append(point)
    new_points.append(extended_end_point)
    return new_points

# Extend each feature and add to the new layer
for feature in layer.getFeatures():
    geom = feature.geometry()
    extended_geom = extend_line(geom, 6)
    new_feature = QgsFeature()
    new_feature.setGeometry(extended_geom)
    new_feature.setAttributes(feature.attributes())
    provider.addFeature(new_feature)

# Add the new layer to the project
QgsProject.instance().addMapLayer(extended_layer)
print("Extended lines have been created and added as a new layer.")
Enter fullscreen mode Exit fullscreen mode

Create Output Layer: The output layer will be named extended_highway_London for our case. By overlaying the original and scaled layers, you can see the larger scale for London if it followed the same rate of cyclists as Amsterdam.
Image description

If you enjoyed this tutorial, please check out the full video on YouTube, where I use object detection on footage from London and Amsterdam and apply the rate of cyclists for an increased length.
Have more questions? Feel free to reach out - I'm always here to help.

Top comments (0)