forked from s_ranjbar/city_retrofit
Merge pull request 'geojson_exporter' (#50) from geojson_exporter into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/50
This commit is contained in:
commit
f62ab95cf0
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
112
hub/exports/formats/geojson.py
Normal file
112
hub/exports/formats/geojson.py
Normal file
|
@ -0,0 +1,112 @@
|
|||
"""
|
||||
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:
|
||||
if len(building.grounds) == 1:
|
||||
ground = building.grounds[0]
|
||||
feature = self._polygon(ground)
|
||||
else:
|
||||
feature = self._multipolygon(building.grounds)
|
||||
feature['id'] = building.name
|
||||
feature['properties']['height'] = f'{building.max_height - building.lower_corner[2]}'
|
||||
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]}'
|
||||
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)
|
||||
|
||||
def _polygon(self, ground):
|
||||
feature = {
|
||||
'type': 'Feature',
|
||||
'geometry': {
|
||||
'type': 'Polygon',
|
||||
'coordinates': []
|
||||
},
|
||||
'properties': {}
|
||||
}
|
||||
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)
|
||||
return feature
|
||||
|
||||
def _multipolygon(self, grounds):
|
||||
feature = {
|
||||
'type': 'Feature',
|
||||
'geometry': {
|
||||
'type': 'MultiPolygon',
|
||||
'coordinates': []
|
||||
},
|
||||
'properties': {}
|
||||
}
|
||||
polygons = []
|
||||
for ground in 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]])
|
||||
polygons.append(ground_coordinates)
|
||||
feature['geometry']['coordinates'].append(polygons)
|
||||
return feature
|
||||
|
||||
|
|
@ -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):
|
||||
|
|
|
@ -4,6 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||
"""
|
||||
import logging
|
||||
|
||||
import numpy as np
|
||||
import xmltodict
|
||||
|
@ -79,6 +80,7 @@ class CityGml:
|
|||
self._srs_name = envelope['@srsName']
|
||||
else:
|
||||
# If not coordinate system given assuming hub standard
|
||||
logging.warning('gml file contains no coordinate system assuming EPSG:26911 (North america with 4m error)')
|
||||
self._srs_name = "EPSG:26911"
|
||||
else:
|
||||
# get the boundary from the city objects instead
|
||||
|
|
|
@ -7,6 +7,7 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
|
|||
"""
|
||||
import json
|
||||
import logging.handlers
|
||||
import os
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
from hub.imports.geometry_factory import GeometryFactory
|
||||
|
@ -98,6 +99,19 @@ 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)
|
||||
geojson_file = Path(self._output_path / f'{self._city.name}.geojson')
|
||||
self.assertTrue(geojson_file.exists(), f'{geojson_file} doesn\'t exists')
|
||||
with open(geojson_file, 'r') as f:
|
||||
geojson = json.load(f)
|
||||
self.assertEqual(1, len(geojson['features']), 'Wrong number of buildings')
|
||||
geometry = geojson['features'][0]['geometry']
|
||||
self.assertEqual('Polygon', geometry['type'], 'Wrong geometry type')
|
||||
self.assertEqual(1, len(geometry['coordinates']), 'Wrong polygon structure')
|
||||
self.assertEqual(11, len(geometry['coordinates'][0]), 'Wrong number of vertices')
|
||||
os.unlink(geojson_file) # todo: this test need to cover a multipolygon example too
|
||||
|
||||
def test_energy_ade_export(self):
|
||||
"""
|
||||
export to energy ADE
|
||||
|
|
Loading…
Reference in New Issue
Block a user