diff --git a/hub/exports/exports_factory.py b/hub/exports/exports_factory.py index 7a63c1da..8778e758 100644 --- a/hub/exports/exports_factory.py +++ b/hub/exports/exports_factory.py @@ -9,6 +9,7 @@ from pathlib import Path from hub.exports.formats.glb import Glb from hub.exports.formats.obj import Obj +from hub.exports.formats.geojson import Geojson from hub.exports.formats.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm from hub.exports.formats.stl import Stl from hub.exports.formats.cesiumjs_tileset import CesiumjsTileset @@ -85,6 +86,10 @@ class ExportsFactory: def _glb(self): return Glb(self._city, self._path, target_buildings=self._target_buildings) + @property + def _geojson(self): + return Geojson(self._city, self._path, target_buildings=self._target_buildings) + def export(self): """ Export the city given to the class using the given export type handler diff --git a/hub/exports/formats/cesiumjs_tileset.py b/hub/exports/formats/cesiumjs_tileset.py index 2f23c99d..df1adb52 100644 --- a/hub/exports/formats/cesiumjs_tileset.py +++ b/hub/exports/formats/cesiumjs_tileset.py @@ -1,3 +1,9 @@ +""" +export a city into Cesium tileset format +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +""" import json import math diff --git a/hub/exports/formats/geojson.py b/hub/exports/formats/geojson.py new file mode 100644 index 00000000..429c6fc3 --- /dev/null +++ b/hub/exports/formats/geojson.py @@ -0,0 +1,74 @@ +""" +export a city into Geojson format +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +""" +import json +from pathlib import Path + +import numpy as np +import pyproj +from pyproj import Transformer + +from hub.helpers.geometry_helper import GeometryHelper + + +class Geojson: + """ + Export to geojson format + """ + def __init__(self, city, path, target_buildings): + self._city = city + self._file_path = Path(path / f'{self._city.name}.geojson').resolve() + try: + srs_name = self._city.srs_name + if self._city.srs_name in GeometryHelper.srs_transformations: + srs_name = GeometryHelper.srs_transformations[self._city.srs_name] + input_reference = pyproj.CRS(srs_name) # Projected coordinate system from input data + except pyproj.exceptions.CRSError as err: + raise pyproj.exceptions.CRSError from err + self._to_gps = Transformer.from_crs(input_reference, pyproj.CRS('EPSG:4326')) + if target_buildings is None: + target_buildings = [b.name for b in self._city.buildings] + self._geojson_skeleton = { + 'type': 'FeatureCollection', + 'features': [] + } + self._feature_skeleton = { + 'type': 'Feature', + 'geometry': { + 'type': 'Polygon', + 'coordinates': [] + }, + 'properties': {} + } + self._export() + + def _export(self): + for building in self._city.buildings: + feature = self._feature_skeleton.copy() + feature['id'] = building.name + feature['properties']['height'] = f'{building.max_height}' + feature['properties']['function'] = f'{building.function}' + feature['properties']['year_of_construction'] = f'{building.year_of_construction}' + feature['properties']['aliases'] = building.aliases + feature['properties']['elevation'] = f'{building.lower_corner[2]}' + for ground in building.grounds: + + ground_coordinates = [] + for coordinate in ground.solid_polygon.coordinates: + gps_coordinate = self._to_gps.transform(coordinate[0], coordinate[1]) + ground_coordinates.insert(0, [gps_coordinate[1], gps_coordinate[0]]) + + first_gps_coordinate = self._to_gps.transform( + ground.solid_polygon.coordinates[0][0], + ground.solid_polygon.coordinates[0][1] + ) + ground_coordinates.insert(0, [first_gps_coordinate[1], first_gps_coordinate[0]]) + feature['geometry']['coordinates'].append(ground_coordinates) + self._geojson_skeleton['features'].append(feature) + with open(self._file_path, 'w', encoding='utf-8') as f: + json.dump(self._geojson_skeleton, f, indent=2) + + diff --git a/hub/exports/formats/glb.py b/hub/exports/formats/glb.py index 6329788d..d8c0a942 100644 --- a/hub/exports/formats/glb.py +++ b/hub/exports/formats/glb.py @@ -1,3 +1,9 @@ +""" +export a city into Glb format +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +""" import os import shutil import subprocess @@ -13,6 +19,9 @@ class GltExceptionError(Exception): class Glb: + """ + Glb class + """ def __init__(self, city, path, target_buildings=None): self._city = city self._path = path @@ -23,10 +32,6 @@ class Glb: @property def _obj2gtl(self): - """ - Get the SRA installation path - :return: str - """ return shutil.which('obj2gltf') def _export(self): diff --git a/tests/test_exports.py b/tests/test_exports.py index a617e67d..c53a717c 100644 --- a/tests/test_exports.py +++ b/tests/test_exports.py @@ -98,6 +98,10 @@ class TestExports(TestCase): glb_file = Path(self._output_path / f'{building.name}.glb') self.assertTrue(glb_file.exists(), f'{building.name} Building glb wasn\'t correctly generated') + def test_geojson_export(self): + self._export('geojson', False) + self.assertTrue(False, "False is not True") + def test_energy_ade_export(self): """ export to energy ADE