diff --git a/imports/geometry/citygml.py b/imports/geometry/citygml.py index 63c8b7a6..34b6abd9 100644 --- a/imports/geometry/citygml.py +++ b/imports/geometry/citygml.py @@ -64,6 +64,9 @@ class CityGml: self._upper_corner = np.fromstring(envelope['upperCorner'], dtype=float, sep=' ') if '@srsName' in envelope: self._srs_name = envelope['@srsName'] + else: + # If not coordinate system given assuming hub standard + self._srs_name = "EPSG:4326" else: # get the boundary from the city objects instead for city_object_member in self._gml['CityModel']['cityObjectMember']: diff --git a/imports/geometry/geojson.py b/imports/geometry/geojson.py new file mode 100644 index 00000000..53eea012 --- /dev/null +++ b/imports/geometry/geojson.py @@ -0,0 +1,110 @@ +""" +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 diff --git a/imports/geometry_factory.py b/imports/geometry_factory.py index e9daa30b..61c2fa27 100644 --- a/imports/geometry_factory.py +++ b/imports/geometry_factory.py @@ -48,6 +48,16 @@ class GeometryFactory: self._data_frame = geopandas.read_file(self._path) return GPandas(self._data_frame).city + @property + def _geojson(self) -> City: + """ + Enrich the city by using Geojson information as data source + :return: City + """ + if self._data_frame is None: + self._data_frame = geopandas.read_file(self._path) + return Geojson(self._data_frame).city + @property def _osm_subway(self) -> City: """