Accessing NSW Open Data via APIs

Accessing NSW Open Data via APIs

import folium
from folium import plugins
import httpx
import io
import json
import pandas as pd
import tomli
import unicodedata


from IPython.core.display import Image
from pathlib import Path
from PIL import Image as pImage

Read API key from .toml file

def read_config_file(config_file):
    if Path(config_file).exists() == True:
        print("Reading: " + config_file + "...")
        with open(Path(config_file), encoding="utf-8") as f:
            app_config = tomli.load(f)
        return app_config
    else:
        print("Error: " + config_file + " - file does not exist")
        return False
API_CONFIG_FILE = 'api_config.toml'
api_config = read_config_file(API_CONFIG_FILE)
Reading: api_config.toml...
def show_api_key(api_config, override=False):
    OBSCURE_LENGTH = 5
    _REPL_CHAR = unicodedata.lookup("FULL BLOCK")
    print("API key for: " + api_config["NSW_OPEN_DATA"]["URL"])
    len_api_key = len(api_config["NSW_OPEN_DATA"]["API_KEY"])
    print(str(len_api_key) + ' characters in length')
    if api_config['SHOW_KEY']:
        print(api_config["NSW_OPEN_DATA"]["API_KEY"])   # not recommended!
    elif not override:
        print(api_config["NSW_OPEN_DATA"]["API_KEY"][0:OBSCURE_LENGTH] + 
        (len(api_config["NSW_OPEN_DATA"]["API_KEY"]) - OBSCURE_LENGTH*2) * _REPL_CHAR + 
        api_config["NSW_OPEN_DATA"]["API_KEY"][-OBSCURE_LENGTH:])
    else:
        print("Are you sure? Change SHOW_KEY to true in .toml file")
show_api_key(api_config)
API key for: https://opendata.transport.nsw.gov.au/
36 characters in length
fwyH5██████████████████████████mWeUk

Example 1 - Look up cark park information

API: https://opendata.transport.nsw.gov.au/dataset/car-park-api

def get_carpark_info():
    URL = "https://api.transport.nsw.gov.au/v1/carpark"
    response = httpx.get(
        URL,
        headers={
            "Authorization": "apikey " + api_config["NSW_OPEN_DATA"]["API_KEY"],
            "Accept": "application/json",
        },
    )
    return pd.DataFrame.from_dict(response.json(), orient='index', columns=['Car Park Name'])
get_carpark_info()

Example 2 - Get toll/motorway information

API: https://opendata.transport.nsw.gov.au/dataset/toll-calculator-api

Part A - Get motorway information

def get_motorway_info():
    URL = "https://api.transport.nsw.gov.au/v2/roads/toll_calc/data"
    response = httpx.get(
        URL,
        headers={
            "Authorization": "apikey " + api_config["NSW_OPEN_DATA"]["API_KEY"],
            "Accept": "application/json",
        },
    )
    return pd.DataFrame(response.json()["motorways"])
get_motorway_info()

Part B - Toll calculation for route between two GPS positions

def get_toll_route():
    URL = "https://api.transport.nsw.gov.au/v2/roads/toll_calc/route"
    response = httpx.post(
        URL,
        headers={
            "Authorization": "apikey " + api_config["NSW_OPEN_DATA"]["API_KEY"],
            "Accept": "application/json",
            "Content-Type": "application/json"
        }, 
        data=data_json
    )
    return response.json()
data = {
    "origin": {"lat": -33.8819, "lng": 151.2517, "name": "string"},
    "destination": {"lat": -33.8509, "lng": 151.2207, "name": "string"},
    "vehicleClass": "A",
    "vehicleClassByMotorway": {
        "LCT": "A",
        "CCT": "A",
        "ED": "A",
        "M2": "A",
        "M5": "A",
        "M7": "A",
        "SHB": "A",
        "SHT": "A",
        "M4": "A",
    },
    "excludeToll": "false",
    "includeSteps": "false",
    "departureTime": "2021-08-07T05:21:12.342Z",
}

data_json = json.dumps(data).replace('"true"', 'true').replace('"false"', 'false')   # fix bool types
get_toll_route();

Example 3 - Lookup address

API: https://data.nsw.gov.au/data/dataset/lpi-web-services-address-location-service

## Address Lookup (legacy API example)

def get_address_info(houseNumber, roadName, roadType, suburb, postCode):
    URL = "http://maps.six.nsw.gov.au/services/public/Address_Location?"
    URL += "houseNumber=" + houseNumber
    URL += "&roadName=" + roadName
    URL += "&roadType=" + roadType
    URL += "&suburb=" + suburb
    URL += "&postCode" + postCode
    URL += "&projection=EPSG%3A4326"
    # print(URL)
    response = httpx.get(URL)
    return response.json()['addressResult']['addresses']
# Example - Kirribilli House
get_address_info('109', 'Kirribilli', 'Ave', 'Kirribilli', '2061')
[{'objectId': 5894870,
  'propid': 3589465,
  'houseNumberString': '109',
  'houseNumberFirst': 109,
  'houseNumberSecond': 0,
  'roadName': 'Kirribilli',
  'roadType': 'Avenue',
  'suburbName': 'Kirribilli',
  'postCode': 2061,
  'council': 'North Sydney',
  'addressType': 'Assigned',
  'shortAddressString': '109 KIRRIBILLI AVENUE KIRRIBILLI',
  'addressPoint': {'addressstringOid': 0,
   'coordRefSys': 'EPSG:4283',
   'centreX': 151.2186838704801,
   'centreY': -33.85184922241778},
  'addressString': '109  Kirribilli Avenue  Kirribilli 2061'}]
def map_address(houseNumber, roadName, roadType, suburb, postCode):
    longitude = get_address_info(houseNumber, roadName, roadType, suburb, postCode)[0]['addressPoint']['centreX']
    latitude = get_address_info(houseNumber, roadName, roadType, suburb, postCode)[0]['addressPoint']['centreY']
    addressString = get_address_info(houseNumber, roadName, roadType, suburb, postCode)[0]['addressString']
    fmap = folium.Map(location=[latitude, longitude], zoom_start=13)
    tooltip = "Click me!"
    folium.Marker([latitude, longitude], popup=addressString, tooltip=tooltip).add_to(fmap)
    return fmap
mini_map = plugins.MiniMap(toggle_display=True)
fmap = map_address('109', 'Kirribilli', 'Ave', 'Kirribilli', '2061')
fmap.add_child(mini_map);
def display_and_export_map(png_filename="mymap.png"):
    image_data = fmap._to_png()
    image = pImage.open(io.BytesIO(image_data))
    #image.show()
    image.save(png_filename)
    #display(Image(image_data))
    return png_filename
display_and_export_map()
'mymap.png'

My Map