Network API in QGIS

Import RenewMap project data to a QGIS project.

📘

Check the API docs

https://renewmap.readme.io/reference/get-a-list-of-network-infra

You will need an API key to get started.

Check the Getting Started page for instructions.

In QGIS, you can run a Python script to fetch RenewMap API data into your project.

  1. Select Plugins > Python Console and then select 📝 Show editor
  2. Create a new blank script using the ➕ icon.
  3. Paste the code below into the blank script, replacing 'YOUR_API_KEY' with your actual API key.
  4. Run the script with the ▶️ icon.
  5. The latest RenewMap data will appear in your project:
    1. A LineString layer called Network with operating network infrastructure data.
    2. A MultiPolygon layer called Network - Development with network infrastructure in development.

Note: the layer is stored in memory and will be lost when you close the project. Re-run the script whenever you want to get the latest data.

If you want to persist the layer to your next session, right click the layer in the Layers pane and select Make permanent. However, this permanent layer will not update with new data.

import pandas as pd
import requests

from shapely.geometry import shape
from typing import List, Dict

API_KEY = "YOUR_API_KEY"  # Replace with your actual API key
BASE_URL = "https://api.renewmap.com.au/api/v1/network" 
DATA_KEY = "network"

headers = {
    "accept": "application/json",
    "Authorization": f"Bearer {API_KEY}"
}

class APIConfig:
    """Configuration class for API parameters.

    Attributes:
        base_url: Base URL for the API endpoint
        limit: Number of records per request
        headers: HTTP headers for API requests
        data_key: Key in API response containing the data array
    """
    base_url: str = BASE_URL
    limit: int = 1000
    headers: Dict = headers
    add_fields: bool = False  # Network API doesn't support fields
    data_key: str = DATA_KEY

    def get_url(self, offset=0):
        url = f"{self.base_url}?limit={self.limit}&offset={offset}"
        if self.add_fields:
            url = f"{url}&fields=all"
        return url

def fetch_data(config: APIConfig) -> pd.DataFrame:
    """Fetch data from API using pagination.

    Args:
        config: APIConfig object containing API configuration

    Returns:
        DataFrame containing all fetched data
    """
    data_list = []
    offset = 0
    
    # Fetch data in chunks using pagination
    while True:
        url = config.get_url(offset)
        response = requests.get(url, headers=config.headers)
        response.raise_for_status()
        data_dict = response.json()
        batch = data_dict[config.data_key]

        if len(batch) == 0:
            break  # No more data available
        
        data_list.extend(batch)
        offset += config.limit  # Increment offset for next request

    print(f"Fetched {len(data_list)} items")
    return pd.DataFrame(data_list)


def create_qgis_fields(gdf: pd.DataFrame) -> List[QgsField]:
    """Create QGIS fields based on GeoDataFrame columns.

    Args:
        gdf: Input GeoDataFrame with project data

    Returns:
        List of QgsField objects
    """
    # Loop through columns to dynamically create QgsField objects
    fields = []
    for column in gdf.columns:
        field_type = QVariant.String  # Adjust the type based on your data
        fields.append(QgsField(column, field_type))
    
    return fields

def create_feature(row: pd.Series) -> QgsFeature:
    """Create QGIS feature from GeoDataFrame row.

    Args:
        row: Series containing single project data row with geometry

    Returns:
        QgsFeature object with geometry and attributes
    """
    feature = QgsFeature()

    # Convert shapely geometry to QgsGeometry
    shapely_geom = shape(row['geometry'])
    qgs_geometry = QgsGeometry.fromWkt(shapely_geom.wkt)
    # Set geometry
    feature.setGeometry(qgs_geometry)

    # Set attributes for all columns except geometry
    attributes = [str(row[column]) for column in row.index if column != 'geometry']
    feature.setAttributes(attributes)
    return feature

def create_multilinestring_layer(gdf: pd.DataFrame) -> QgsVectorLayer:
    """Create QGIS vector layer from DataFrame.

    Args:
        df: DataFrame containing project data

    Returns:
        QgsVectorLayer object with all features
    """
    # Create a memory layer for MultiLineStrings
    layer = QgsVectorLayer("LineString?crs=epsg:4326", "Network", "memory")

    # Add fields to the layer
    fields = create_qgis_fields(gdf)
    layer.dataProvider().addAttributes(fields)
    layer.updateFields()

    # Add features to the multilinestring layer
    multiline_data = gdf[gdf['geometry'].apply(lambda x: shape(x).geom_type == 'MultiLineString')]
    for _, row in multiline_data.iterrows():
        feature = create_feature(row)
        if not feature.geometry().isNull():
            layer.dataProvider().addFeature(feature)

    layer.updateExtents()
    return layer

def create_multipolygon_layer(gdf: pd.DataFrame) -> QgsVectorLayer:
    # Create a memory layer for MultiPolygons
    layer = QgsVectorLayer("Polygon?crs=epsg:4326", "Network - Development", "memory")

    # Add fields to the layer
    fields = create_qgis_fields(gdf)
    layer.dataProvider().addAttributes(fields)
    layer.updateFields()

    # Add features to the multipolygon layer
    polygon_data = gdf[gdf['geometry'].apply(lambda x: shape(x).geom_type == 'MultiPolygon')]
    for _, row in polygon_data.iterrows():
        feature = create_feature(row)
        if not feature.geometry().isNull():
            layer.dataProvider().addFeature(feature)

    layer.updateExtents()
    return layer

config = APIConfig()
gdf = fetch_data(config)

multilinestring_layer = create_multilinestring_layer(gdf)
multipolygon_layer = create_multipolygon_layer(gdf)
QgsProject.instance().addMapLayer(multilinestring_layer)
QgsProject.instance().addMapLayer(multipolygon_layer)


Troubleshooting

Check the Troubleshooting guide for some solutions to common problems.