colouring-montreal/etl/join_building_data/load_shapefile.py

141 lines
3.4 KiB
Python

"""Join shapefile data to buildings
This is effectively an example script using the HTTP API, tailored to particular collected
datasets for Camden (age data) and Fitzrovia (number of storeys).
- read through shapes
- locate building by toid
- else locate building by representative point
- update building with data
"""
import json
import os
import sys
from functools import partial
import fiona
import pyproj
import requests
from shapely.geometry import shape
from shapely.ops import transform
osgb_to_ll = partial(
pyproj.transform,
pyproj.Proj(init='epsg:27700'),
pyproj.Proj(init='epsg:4326')
)
def main(base_url, api_key, process, source_file):
"""Read from file, update buildings
"""
with fiona.open(source_file, 'r') as source:
for feature in source:
props = feature['properties']
if process == "camden":
toid, data = process_camden(props)
else:
toid, data = process_fitzrovia(props)
if data is None:
continue
building_id = find_building(toid, feature['geometry'], base_url)
if not building_id:
print("no_match", toid, "-")
continue
save_data(building_id, data, api_key, base_url)
def process_camden(props):
toid = osgb_toid(props['TOID'])
data = {
'date_year': props['Year_C'],
'date_source_detail': props['Date_sou_1']
}
return toid, data
def process_fitzrovia(props):
toid = osgb_toid(props['TOID'])
storeys = props['Storeys']
if storeys is None:
return toid, None
if props['Basement'] == 'Yes':
data = {
'size_storeys_core': int(storeys) - 1,
'size_storeys_basement': 1
}
else:
data = {
'size_storeys_core': int(storeys),
'size_storeys_basement': 0
}
return toid, data
def osgb_toid(toid):
if toid is None:
toid = ""
return "osgb" + toid.lstrip("0")
def save_data(building_id, data, api_key, base_url):
"""Save data to a building
"""
r = requests.post(
"{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key),
json=data
)
def find_building(toid, geom, base_url):
"""Find building_id by TOID or location
"""
r = requests.get(base_url + "/buildings/reference", params={
'key': 'toid',
'id': toid
})
buildings = r.json()
if buildings and len(buildings) == 1:
bid = buildings[0]['building_id']
print("match_by_toid", toid, bid)
return bid
# try location
poly = shape(geom)
point_osgb = poly.centroid
if not poly.contains(point_osgb):
point_osgb = poly.representative_point()
point_ll = transform(osgb_to_ll, point_osgb)
r = requests.get(base_url + "/buildings/locate", params={
'lng': point_ll.x,
'lat': point_ll.y
})
buildings = r.json()
if buildings and len(buildings) == 1:
bid = buildings[0]['building_id']
print("match_by_location", toid, bid)
return bid
return None
if __name__ == '__main__':
try:
url, api_key, process, filename = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
except IndexError:
print(
"Usage: {} <URL> <api_key> <camden|fitzrovia> ./path/to/camden.shp".format(
os.path.basename(__file__)
))
exit()
main(url, api_key, process, filename)