Create repository for split city tool
This commit is contained in:
parent
2258ddeea3
commit
92aaa64955
232
city_region_splitter.py
Normal file
232
city_region_splitter.py
Normal file
@ -0,0 +1,232 @@
|
||||
import json
|
||||
|
||||
from pathlib import Path
|
||||
from shapely.geometry import Polygon
|
||||
|
||||
"""
|
||||
CityRegionSplitter is a tool for splitting a city into a subregions with overlaps allowing for proper SRA calculations.
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2023 Concordia CERC group
|
||||
Project Coder Koa Wells kekoa.wells@concordia.ca
|
||||
"""
|
||||
|
||||
|
||||
class CityRegionSplitter:
|
||||
def __init__(self, city_file):
|
||||
self._city_file = city_file
|
||||
self._city = json.load(city_file)
|
||||
self._buildings = self._city['features']
|
||||
self._regions_dimension = -1
|
||||
self._regions = []
|
||||
|
||||
def _find_neighbors(self):
|
||||
regions = []
|
||||
count = 1
|
||||
for row in range(self._regions_dimension):
|
||||
regions.append([])
|
||||
for col in range(self._regions_dimension):
|
||||
regions[row].append(count)
|
||||
count += 1
|
||||
|
||||
rows = len(regions)
|
||||
cols = len(regions[0])
|
||||
|
||||
neighbors = {}
|
||||
|
||||
# Iterate over each element in the grid
|
||||
for i in range(rows):
|
||||
for j in range(cols):
|
||||
current_region = f'{regions[i][j]}_v1'
|
||||
|
||||
while len(current_region) < 7:
|
||||
current_region = f'0{current_region}'
|
||||
|
||||
upper_neighbor = 'None'
|
||||
lower_neighbor = 'None'
|
||||
left_neighbor = 'None'
|
||||
right_neighbor = 'None'
|
||||
upper_left_neighbor = 'None'
|
||||
upper_right_neighbor = 'None'
|
||||
lower_left_neighbor = 'None'
|
||||
lower_right_neighbor = 'None'
|
||||
|
||||
# find upper, lower, left, and right adjacent
|
||||
if i > 0:
|
||||
upper_neighbor = f'{regions[i - 1][j]}_v1'
|
||||
if i < rows - 1:
|
||||
lower_neighbor = f'{regions[i + 1][j]}_v1'
|
||||
if j > 0:
|
||||
left_neighbor = f'{regions[i][j - 1]}_v1'
|
||||
if j < cols - 1:
|
||||
right_neighbor = f'{regions[i][j + 1]}_v1'
|
||||
|
||||
# find diagonal neighbors
|
||||
# corners
|
||||
if i == 0 and j == 0:
|
||||
lower_right_neighbor = f'{regions[i + 1][j + 1]}_v1'
|
||||
if i == 0 and j == self._regions_dimension - 1:
|
||||
lower_left_neighbor = f'{regions[i + 1][j - 1]}_v1'
|
||||
if i == self._regions_dimension - 1 and j == 0:
|
||||
upper_right_neighbor = f'{regions[i - 1][j + 1]}_v1'
|
||||
if i == self._regions_dimension - 1 and j == self._regions_dimension - 1:
|
||||
upper_left_neighbor = f'{regions[i - 1][j - 1]}_v1'
|
||||
|
||||
# middle top row
|
||||
if i == 0 and 0 < j < self._regions_dimension - 1:
|
||||
lower_left_neighbor = f'{regions[i + 1][j - 1]}_v1'
|
||||
lower_right_neighbor = f'{regions[i + 1][j + 1]}_v1'
|
||||
# middle bottom row
|
||||
if i == self._regions_dimension - 1 and 0 < j < self._regions_dimension - 1:
|
||||
upper_left_neighbor = f'{regions[i - 1][j - 1]}_v1'
|
||||
upper_right_neighbor = f'{regions[i - 1][j + 1]}_v1'
|
||||
# middle left column
|
||||
if 0 < i < self._regions_dimension - 1 and j == 0:
|
||||
upper_right_neighbor = f'{regions[i - 1][j + 1]}_v1'
|
||||
lower_right_neighbor = f'{regions[i + 1][j + 1]}_v1'
|
||||
# middle right column
|
||||
if 0 < i < self._regions_dimension - 1 and j == self._regions_dimension - 1:
|
||||
upper_left_neighbor = f'{regions[i - 1][j - 1]}_v1'
|
||||
lower_left_neighbor = f'{regions[i + 1][j - 1]}_v1'
|
||||
|
||||
# everything else
|
||||
if 0 < i < self._regions_dimension - 1 and 0 < j < self._regions_dimension - 1:
|
||||
lower_right_neighbor = f'{regions[i + 1][j + 1]}_v1'
|
||||
lower_left_neighbor = f'{regions[i + 1][j - 1]}_v1'
|
||||
upper_right_neighbor = f'{regions[i - 1][j + 1]}_v1'
|
||||
upper_left_neighbor = f'{regions[i - 1][j - 1]}_v1'
|
||||
|
||||
neighbors[current_region] = [f'{upper_neighbor}',
|
||||
f'{lower_neighbor}',
|
||||
f'{left_neighbor}',
|
||||
f'{right_neighbor}',
|
||||
f'{upper_left_neighbor}',
|
||||
f'{upper_right_neighbor}',
|
||||
f'{lower_left_neighbor}',
|
||||
f'{lower_right_neighbor}']
|
||||
|
||||
final_neighbors = _add_trailing_zeros(neighbors)
|
||||
return final_neighbors
|
||||
|
||||
"""
|
||||
Generates n x n subregions with a percentage overlap using the top_left_coordinate and bottom_right_coordinate
|
||||
:param: top_left_coordinate: [longitude, latitude] of top left corner of city
|
||||
:param: bottom_right_coordinate: [longitude, latitude] of bottom right corner of city
|
||||
:param: region_dimensions: the number of regions in each directiory; for example 20 creates 20 x 20 subregions
|
||||
:param: percent_overlap: percentage of overlap between neighboring regions
|
||||
:param: output_file_name: name out output file
|
||||
:param: output_path: file path to save the region geojson file
|
||||
:return: None
|
||||
"""
|
||||
def generate_regions(self, top_left_coordinate, bottom_right_coordinate, regions_dimension, percent_overlap, output_file_name, output_path):
|
||||
|
||||
# points for
|
||||
# top_left_coordinate = [-74.022687, 45.722604]
|
||||
# bottom_right_coordinate = [-73.397182, 45.384048]
|
||||
top_left_coordinate = top_left_coordinate
|
||||
bottom_right_coordinate = bottom_right_coordinate
|
||||
regions_dimension = regions_dimension
|
||||
self._regions_dimension = regions_dimension
|
||||
percent_overlap = percent_overlap
|
||||
output_file_name = output_file_name
|
||||
output_path = Path(f'{output_path}/{output_file_name}').resolve()
|
||||
|
||||
x1 = top_left_coordinate[0]
|
||||
y1 = top_left_coordinate[1]
|
||||
x2 = bottom_right_coordinate[0]
|
||||
y2 = bottom_right_coordinate[1]
|
||||
|
||||
regions = [[[] for i in range(regions_dimension)] for j in range(regions_dimension)]
|
||||
|
||||
region_x_side_length = (x2 - x1) / regions_dimension
|
||||
region_y_side_length = (y2 - y1) / regions_dimension
|
||||
|
||||
x_current = x1
|
||||
y_current = y1
|
||||
|
||||
row_count = 1
|
||||
|
||||
for row in regions:
|
||||
for column in row:
|
||||
# append top left corner
|
||||
column.append([x_current, y_current])
|
||||
# append bottom left corner
|
||||
column.append([x_current, y_current + region_y_side_length * (1 + percent_overlap / 2)])
|
||||
# append bottom right corner
|
||||
column.append([x_current + region_x_side_length * (1 + percent_overlap / 2),
|
||||
y_current + region_y_side_length * (1 + percent_overlap / 2)])
|
||||
# append top right corner
|
||||
column.append([x_current + region_x_side_length * (1 + percent_overlap / 2), y_current])
|
||||
# append top left corner again to close the polygon
|
||||
column.append([x_current, y_current])
|
||||
x_current = x_current + region_x_side_length * (1 - percent_overlap / 2)
|
||||
|
||||
x_current = x1
|
||||
y_current = y_current + region_y_side_length * (1 - percent_overlap / 2)
|
||||
row_count += 1
|
||||
|
||||
id = 1
|
||||
|
||||
feature_collection = {
|
||||
"type": "FeatureCollection",
|
||||
"features": []
|
||||
}
|
||||
|
||||
for row in regions:
|
||||
for column in row:
|
||||
id_with_zeros = str(id)
|
||||
while len(id_with_zeros) < 4:
|
||||
id_with_zeros = f'0{id_with_zeros}'
|
||||
feature_collection['features'].append(
|
||||
{
|
||||
"type": "Feature",
|
||||
"id": id,
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [column]
|
||||
},
|
||||
"properties":
|
||||
{
|
||||
"region_id": f'{id_with_zeros}.bz2'
|
||||
}
|
||||
})
|
||||
id += 1
|
||||
|
||||
with open(f'{output_path}.geojson', 'w') as file:
|
||||
file.write(json.dumps(feature_collection, indent=2))
|
||||
print(f'Saving region file to {output_path}')
|
||||
self._regions = feature_collection['features']
|
||||
|
||||
def assign_buildings_to_regions(self):
|
||||
|
||||
for building in self._buildings:
|
||||
if building['geometry']['type'] == 'Polygon':
|
||||
building_centroid = Polygon(building["geometry"]["coordinates"][0]).centroid
|
||||
elif building['geometry']['type'] == 'MultiPolygon':
|
||||
# use the centroid of the first polygon inside of the multipolygon
|
||||
building_centroid = Polygon(building["geometry"]["coordinates"][0][0]).centroid
|
||||
|
||||
building['properties']['centroid'] = [building_centroid.x, building_centroid.y]
|
||||
building['properties']['district_property'] = []
|
||||
|
||||
target_regions = []
|
||||
region_assigned = False
|
||||
|
||||
target_region = self._regions[len(self._regions) / 2]
|
||||
while not region_assigned:
|
||||
if building_centroid.within(Polygon(target_region['geometry']['coordinates'])):
|
||||
region_assigned = True
|
||||
|
||||
break
|
||||
|
||||
target_region_centroid = Polygon(target_region['geometry']['coordinates']).centroid
|
||||
|
||||
if building_centroid.x <= target_region_centroid.x:
|
||||
if building_centroid.y >= target_region_centroid.y:
|
||||
regions = regions[0:len(regions) / 2]
|
||||
elif building_centroid.y < target_region_centroid.y:
|
||||
regions = regions[len(regions) / 2:len(regions) - 1]
|
||||
elif building_centroid.x > target_region_centroid.x:
|
||||
if building_centroid.y >= target_region_centroid.y:
|
||||
regions = regions[0:len(regions) / 2]
|
||||
elif building_centroid.y < target_region_centroid.y:
|
||||
regions = regions[len(regions) / 2:len(regions) - 1]
|
Loading…
Reference in New Issue
Block a user