111 lines
3.5 KiB
Python
111 lines
3.5 KiB
Python
|
"""
|
||
|
Geojson module parses geojson files and import the geometry into the city model structure
|
||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||
|
Copyright © 2022 Concordia CERC group
|
||
|
Project Coder Guillermo Gutierrez Guillermo.GutierrezMorote@concordia.ca
|
||
|
"""
|
||
|
|
||
|
import trimesh
|
||
|
import trimesh.exchange.load
|
||
|
import trimesh.geometry
|
||
|
import trimesh.creation
|
||
|
import trimesh.repair
|
||
|
from shapely.geometry import Point
|
||
|
from shapely.geometry import Polygon as ShapelyPoly
|
||
|
from trimesh import Scene
|
||
|
|
||
|
from city_model_structure.attributes.polygon import Polygon
|
||
|
from city_model_structure.building import Building
|
||
|
from city_model_structure.building_demand.surface import Surface
|
||
|
from city_model_structure.city import City
|
||
|
|
||
|
import helpers.constants as cte
|
||
|
|
||
|
|
||
|
class Geojson:
|
||
|
"""
|
||
|
GeoPandas class
|
||
|
"""
|
||
|
|
||
|
def __init__(self, path):
|
||
|
"""
|
||
|
"""
|
||
|
|
||
|
self._srs_name = srs_name
|
||
|
self._city = None
|
||
|
self._scene = dataframe
|
||
|
self._scene = self._scene.to_crs(self._srs_name)
|
||
|
min_x, min_y, max_x, max_y = self._scene.total_bounds
|
||
|
self._lower_corner = [min_x, min_y, 0]
|
||
|
self._upper_corner = [max_x, max_y, 0]
|
||
|
|
||
|
@property
|
||
|
def scene(self) -> Scene:
|
||
|
"""
|
||
|
Get GeoPandas scene
|
||
|
"""
|
||
|
return self._scene
|
||
|
|
||
|
@property
|
||
|
def city(self) -> City:
|
||
|
"""
|
||
|
Get city out of a GeoPandas Table
|
||
|
"""
|
||
|
if self._city is None:
|
||
|
self._city = City(self._lower_corner, self._upper_corner, self._srs_name)
|
||
|
for scene_index, bldg in self._scene.iterrows():
|
||
|
geom = bldg.geom
|
||
|
polygon = ShapelyPoly(geom['coordinates'][0])
|
||
|
height = float(bldg['height_mean'])
|
||
|
building_mesh = trimesh.creation.extrude_polygon(polygon, height)
|
||
|
trimesh.repair.fill_holes(building_mesh)
|
||
|
trimesh.repair.fix_winding(building_mesh)
|
||
|
year_of_construction = int(bldg['year_built'])
|
||
|
name = str(scene_index)
|
||
|
lod = 1
|
||
|
if year_of_construction > 2000:
|
||
|
function = cte.RESIDENTIAL
|
||
|
else:
|
||
|
function = cte.INDUSTRY
|
||
|
|
||
|
surfaces = []
|
||
|
for face_index, face in enumerate(building_mesh.faces):
|
||
|
points = []
|
||
|
for vertex_index in face:
|
||
|
points.append(building_mesh.vertices[vertex_index])
|
||
|
solid_polygon = Polygon(points)
|
||
|
perimeter_polygon = solid_polygon
|
||
|
surface = Surface(solid_polygon, perimeter_polygon)
|
||
|
surfaces.append(surface)
|
||
|
building = Building(name, lod, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
|
||
|
self._city.add_city_object(building)
|
||
|
return self._city
|
||
|
|
||
|
@staticmethod
|
||
|
def resize_polygon(poly, factor=0.10, expand=False) -> ShapelyPoly:
|
||
|
"""
|
||
|
returns the shapely polygon which is smaller or bigger by passed factor.
|
||
|
Arguments:
|
||
|
poly {shapely.geometry.Polygon} -- an input geometry in shapely polygon format
|
||
|
|
||
|
Keyword Arguments:
|
||
|
factor {float} -- factor of expansion (default: {0.10})
|
||
|
expand {bool} -- If expand = True , then it returns bigger polygon, else smaller (default: {False})
|
||
|
|
||
|
Returns:
|
||
|
{shapely.geometry.Polygon} -- output geometry in shapely polygon format
|
||
|
"""
|
||
|
xs = list(poly.exterior.coords.xy[0])
|
||
|
ys = list(poly.exterior.coords.xy[1])
|
||
|
x_center = 0.5 * min(xs) + 0.5 * max(xs)
|
||
|
y_center = 0.5 * min(ys) + 0.5 * max(ys)
|
||
|
min_corner = Point(min(xs), min(ys))
|
||
|
center = Point(x_center, y_center)
|
||
|
shrink_distance = center.distance(min_corner) * factor
|
||
|
|
||
|
if expand:
|
||
|
poly_resized = poly.buffer(shrink_distance) # expand
|
||
|
else:
|
||
|
poly_resized = poly.buffer(-shrink_distance) # shrink
|
||
|
return poly_resized
|