Logging to remove sub polygons that are holes

This commit is contained in:
Peter Yefi 2023-04-04 22:31:54 -04:00
parent a9caad7cf5
commit 77824fd1cb
3 changed files with 70 additions and 61 deletions

View File

@ -18,8 +18,8 @@ from hub.city_model_structure.attributes.polygon import Polygon
from hub.city_model_structure.building import Building from hub.city_model_structure.building import Building
from hub.city_model_structure.building_demand.surface import Surface from hub.city_model_structure.building_demand.surface import Surface
from hub.city_model_structure.city import City from hub.city_model_structure.city import City
from typing import Dict, Tuple from typing import List
from shapely.geometry import Polygon from shapely.geometry import Polygon as Poly
class Geojson: class Geojson:
@ -47,6 +47,7 @@ class Geojson:
self._year_of_construction_field = year_of_construction_field self._year_of_construction_field = year_of_construction_field
self._function_field = function_field self._function_field = function_field
self._function_to_hub = function_to_hub self._function_to_hub = function_to_hub
self._building_coordinates = []
with open(path) as json_file: with open(path) as json_file:
self._geojson = json.loads(json_file.read()) self._geojson = json.loads(json_file.read())
@ -80,6 +81,7 @@ class Geojson:
lod0_buildings = Geojson._create_buildings_lod0(name, year_of_construction, function, surface_coordinates) lod0_buildings = Geojson._create_buildings_lod0(name, year_of_construction, function, surface_coordinates)
surfaces = [] surfaces = []
buildings = [] buildings = []
for zone, lod0_building in enumerate(lod0_buildings): for zone, lod0_building in enumerate(lod0_buildings):
for surface in lod0_building.grounds: for surface in lod0_building.grounds:
@ -176,18 +178,40 @@ class Geojson:
percentage += percentage_ground * percentage_height percentage += percentage_ground * percentage_height
wall.percentage_shared = percentage wall.percentage_shared = percentage
'''def _remove_sub_polygons(self, building_coordinates: Dict, new_coordinate: Tuple) -> Dict: def _unwind_coordinates(self, polygons, coordinates):
new_polygon = Polygon(new_coordinate[1]) """
is_sub_polygon = False Makes list of coordinates from complex coordinate list
for coordinates in building_coordinates['coords']: :param polygons: complex list of coordinates e.g. [[[3.5, 2.7], [10.7, 11.19]], [[13.5, 22.7], [18.7, 11.9]]]
if Polygon(coordinates).contains(new_polygon): :param coordinates: and empty list to hold list of coordinates
is_sub_polygon = True :return: list of coordinates, e.g. [[3.5, 2.7], [10.7, 11.19], [13.5, 22.7], [18.7, 11.9]]
"""
for polygon in polygons:
if len(polygon) != 2:
return self._unwind_coordinates(polygons[0], coordinates)
else:
coordinates.append(polygon)
return coordinates
if not is_sub_polygon: def _remove_sub_polygons(self, new_building_coordinate: List):
building_coordinates['coords'].append(new_coordinate[1]) """
building_coordinates['parts'].append(new_coordinate[0]) building a pool of building cooordinates (GeoJSON polygons) while
ignoring polygons that are inside other polygons
:param new_building_coordinate: a new coordinate to be added, this is checked
to make sure it is not inside a polygon or a polygon is not inside it
:return:
"""
processed_coordinates = self._unwind_coordinates(new_building_coordinate[0], [])
is_sub_coordinate = False
for coordinates in self._building_coordinates[:]:
if Poly(processed_coordinates).contains(Poly(coordinates[0])):
self._building_coordinates.remove(coordinates)
elif Poly(coordinates[0]).contains(Poly(processed_coordinates)):
is_sub_coordinate = True
break
return building_coordinates''' if is_sub_coordinate is False:
new_building_coordinate[0] = processed_coordinates
self._building_coordinates.append(new_building_coordinate)
@property @property
def city(self) -> City: def city(self) -> City:
@ -223,39 +247,37 @@ class Geojson:
else: else:
building_name = f'building_{building_id}' building_name = f'building_{building_id}'
building_id += 1 building_id += 1
polygons = [] for coordinates in geometry['coordinates']:
'''building_coordinates = {'coords': [], 'parts': []} self._remove_sub_polygons([coordinates, building_name, year_of_construction,
for part, coordinates in enumerate(geometry['coordinates']): function, extrusion_height])
building_coordinates = self._remove_sub_polygons(building_coordinates, (part, coordinates))
# remove polygon inside other polygons for part, coordinates in enumerate(self._building_coordinates):
print(building_coordinates)''' polygons = []
for part, coordinates in enumerate(geometry['coordinates']): polygons = self._get_polygons(polygons, coordinates[0])
polygons = self._get_polygons(polygons, coordinates) for zone, polygon in enumerate(polygons):
for zone, polygon in enumerate(polygons): if coordinates[4] == 0:
if extrusion_height == 0: buildings = buildings + Geojson._create_buildings_lod0(f'{coordinates[1]}_part_{part}',
buildings = buildings + Geojson._create_buildings_lod0(f'{building_name}_part_{part}', coordinates[2],
year_of_construction, coordinates[3],
function, [polygon])
[polygon]) lod = 0
lod = 0 else:
else: if self._max_z < coordinates[4]:
if self._max_z < extrusion_height: self._max_z = coordinates[4]
self._max_z = extrusion_height buildings = buildings + Geojson._create_buildings_lod1(f'{coordinates[1]}_part_{part}',
buildings = buildings + Geojson._create_buildings_lod1(f'{building_name}_part_{part}', coordinates[2],
year_of_construction, coordinates[3],
function, coordinates[4],
extrusion_height, [polygon])
[polygon]) self._city = City([self._min_x, self._min_y, 0.0], [self._max_x, self._max_y, self._max_z], 'epsg:26911')
for building in buildings:
if building.floor_area >= 25:
self._city.add_city_object(building)
self._city.level_of_detail.geometry = lod
if lod == 1:
lines_information = GeometryHelper.city_mapping(self._city, plot=False)
self._store_shared_percentage_to_walls(self._city, lines_information)
if len(missing_functions) > 0:
print(f'There are unknown functions {missing_functions}')
self._city = City([self._min_x, self._min_y, 0.0], [self._max_x, self._max_y, self._max_z], 'epsg:26911')
for building in buildings:
# Do not include "small building-like structures" to buildings
if building.floor_area >= 25:
self._city.add_city_object(building)
self._city.level_of_detail.geometry = lod
if lod == 1:
lines_information = GeometryHelper.city_mapping(self._city, plot=False)
self._store_shared_percentage_to_walls(self._city, lines_information)
if len(missing_functions) > 0:
print(f'There are unknown functions {missing_functions}')
return self._city return self._city

View File

@ -144,20 +144,6 @@ class TestGeometryFactory(TestCase):
# including 25 square meter condition for a building reduces buildings number from 2289 to 2057 # including 25 square meter condition for a building reduces buildings number from 2289 to 2057
self.assertEqual(2057, len(city.buildings), 'wrong number of buildings') self.assertEqual(2057, len(city.buildings), 'wrong number of buildings')
def test_import_geojson_2(self):
"""
Test geojson import
"""
file = 'test.geojson'
city = GeometryFactory('geojson',
path=(self._example_path / file).resolve(),
height_field='citygml_me',
year_of_construction_field='ANNEE_CONS',
function_field='CODE_UTILI',
function_to_hub=MontrealFunctionToHubFunction().dictionary).city
for building in city.buildings:
print(building.floor_area)
def test_map_neighbours(self): def test_map_neighbours(self):
""" """
Test neighbours map creation Test neighbours map creation

View File

@ -24,3 +24,4 @@ geopandas
triangle triangle
psycopg2-binary psycopg2-binary
Pillow Pillow
shapely==2.0.1