meb_debugging #8

Merged
g_gutierrez merged 15 commits from meb_debugging into main 2023-03-20 14:21:25 -04:00
30 changed files with 796 additions and 611 deletions
Showing only changes of commit 78f3921447 - Show all commits

View File

@ -152,6 +152,10 @@ class Polygon:
self._area += np.linalg.norm(np.cross(ab, ac)) / 2
return self._area
@area.setter
def area(self, value):
self._area = value
@property
def normal(self) -> np.ndarray:
"""

View File

@ -459,6 +459,7 @@ class City:
for surface in city_object.surfaces:
radiation = surface.global_irradiance
if 'year' not in radiation and 'month' not in radiation:
continue
elif "year" in radiation:
building_radiation += radiation["year"].iloc[0]

View File

@ -34,6 +34,7 @@ class CityObject:
self._max_y = ConfigurationHelper().min_coordinate
self._max_z = ConfigurationHelper().min_coordinate
self._centroid = None
self._volume = None
self._external_temperature = dict()
self._ground_temperature = dict()
self._global_horizontal = dict()
@ -64,7 +65,13 @@ class CityObject:
Get city object volume in cubic meters
:return: float
"""
return self.simplified_polyhedron.volume
if self._volume is None:
self._volume = self.simplified_polyhedron.volume
return self._volume
@volume.setter
def volume(self, value):
self._volume = value
@property
def detailed_polyhedron(self) -> Polyhedron:

View File

@ -18,5 +18,7 @@ convective_heat_transfer_coefficient_exterior = 20
soil_conductivity = 3
#m
soil_thickness = 0.5
short_wave_reflectance = 0.3
#C
cold_water_temperature = 10

View File

@ -7,6 +7,8 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
import numpy as np
from pathlib import Path
import sys
from hub.hub_logger import logger
from hub.exports.formats.insel import Insel
from hub.imports.weather.helpers.weather import Weather
@ -38,6 +40,10 @@ class InselMonthlyEnergyBalance(Insel):
if building.internal_zones is not None:
for internal_zone in building.internal_zones:
if internal_zone.thermal_zones is None:
logger.error(f'Building {building.name} has missing values. '
f'Monthly Energy Balance cannot be processed\n')
sys.stderr.write(f'Building {building.name} has missing values. '
f'Monthly Energy Balance cannot be processed\n')
break
self._contents.append(
self.generate_meb_template(building, output_path, self._radiation_calculation_method,self._weather_format)

View File

@ -71,7 +71,7 @@ class ExportsFactory:
Export the city geometry to obj with grounded coordinates
:return: None
"""
return Obj(self._city, self._path).to_ground_points()
return Obj(self._city, self._path)
@property
def _sra(self):

View File

@ -11,24 +11,51 @@ from hub.exports.formats.triangular import Triangular
from hub.imports.geometry_factory import GeometryFactory
class Obj(Triangular):
class Obj:
"""
Export to obj format
"""
def __init__(self, city, path):
super().__init__(city, path, 'obj')
self._city = city
self._path = path
self._export()
def _to_vertex(self, coordinate):
x = coordinate[0] - self._city.lower_corner[0]
y = coordinate[1] - self._city.lower_corner[1]
z = coordinate[2] - self._city.lower_corner[2]
return f'v {x} {y} {z}\n'
def _export(self):
if self._city.name is None:
self._city.name = 'unknown_city'
file_name = self._city.name + '.obj'
file_path = (Path(self._path).resolve() / file_name).resolve()
vertices = {}
with open(file_path, 'w') as obj:
obj.write("# cerc-hub export\n")
vertex_index = 0
faces = []
for building in self._city.buildings:
obj.write(f'# building {building.name}\n')
for surface in building.surfaces:
obj.write(f'# surface {surface.name}\n')
face = 'f '
for coordinate in surface.perimeter_polygon.coordinates:
vertex = self._to_vertex(coordinate)
if vertex not in vertices.keys():
vertex_index += 1
vertices[vertex] = vertex_index
current = vertex_index
obj.write(vertex)
else:
current = vertices[vertex]
face = f'{face} {current}'
faces.append(f'{face} {face.split(" ")[1]}\n')
obj.writelines(faces)
faces = []
def to_ground_points(self):
"""
Move closer to the origin
"""
file_name_in = self._city.name + '.' + self._triangular_format
file_name_out = self._city.name + '_ground.' + self._triangular_format
file_path_in = (Path(self._path).resolve() / file_name_in).resolve()
file_path_out = (Path(self._path).resolve() / file_name_out).resolve()
obj = GeometryFactory('obj', path=file_path_in)
scene = obj.scene
scene.rezero()
obj_file = trimesh.exchange.obj.export_obj(scene)
with open(file_path_out, 'w') as file:
file.write(obj_file)

View File

@ -8,6 +8,7 @@ import xmltodict
from hub.imports.weather_factory import WeatherFactory
import hub.helpers.constants as cte
from hub.helpers.configuration_helper import ConfigurationHelper
class SimplifiedRadiosityAlgorithm:
@ -88,10 +89,15 @@ class SimplifiedRadiosityAlgorithm:
'@Simulate': f'{simulate}'
}
walls, roofs, floors = [], [], []
default_short_wave_reflectance = ConfigurationHelper().short_wave_reflectance
for surface in building.surfaces:
if surface.short_wave_reflectance is None:
short_wave_reflectance = default_short_wave_reflectance
else:
short_wave_reflectance = surface.short_wave_reflectance
surface_dict = {
'@id': f'{surface.id}',
'@ShortWaveReflectance': f'{surface.short_wave_reflectance}'
'@ShortWaveReflectance': f'{short_wave_reflectance}'
}
for point_index, point in enumerate(surface.perimeter_polygon.coordinates):
point = self._correct_point(point)

View File

@ -139,6 +139,14 @@ class ConfigurationHelper:
"""
return self._config.getfloat('buildings', 'soil_thickness').real
@property
def short_wave_reflectance(self) -> float:
"""
Get configured short wave reflectance for surfaces that don't have construction assigned
:return: 0.3
"""
return self._config.getfloat('buildings', 'short_wave_reflectance').real
@property
def cold_water_temperature(self) -> float:
"""

View File

@ -8,6 +8,7 @@ Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca
import hub.helpers.constants as cte
class AlkisFunctionToHubFunction:
def __init__(self):

View File

@ -34,16 +34,16 @@ class HubUsageToNrcanUsage:
cte.SECONDARY_SCHOOL: 'School/university',
cte.UNIVERSITY: 'School/university',
cte.LABORATORY_AND_RESEARCH_CENTER: 'School/university',
cte.STAND_ALONE_RETAIL: 'Retail',
cte.STAND_ALONE_RETAIL: 'Retail area',
cte.HOSPITAL: 'Hospital',
cte.OUT_PATIENT_HEALTH_CARE: 'Health-care clinic',
cte.HEALTH_CARE: 'Health-care clinic',
cte.RETIREMENT_HOME_OR_ORPHANAGE: 'Health-care clinic',
cte.COMMERCIAL: 'Retail',
cte.STRIP_MALL: 'Retail',
cte.SUPERMARKET: 'Retail',
cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD: 'Retail',
cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD: 'Retail',
cte.COMMERCIAL: 'Retail area',
cte.STRIP_MALL: 'Retail area',
cte.SUPERMARKET: 'Retail area',
cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD: 'Retail area',
cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD: 'Retail area',
cte.RESTAURANT: 'Dining - bar/lounge',
cte.QUICK_SERVICE_RESTAURANT: 'Dining - cafeteria',
cte.FULL_SERVICE_RESTAURANT: 'Dining - bar/lounge',

View File

@ -16,6 +16,8 @@ from hub.city_model_structure.attributes.polygon import Polygon
from hub.city_model_structure.attributes.polyhedron import Polyhedron
from hub.helpers.location import Location
from PIL import Image
class MapPoint:
def __init__(self, x, y):
@ -63,7 +65,7 @@ class GeometryHelper:
return MapPoint(((city.upper_corner[0] - coordinate[0]) * 0.5), ((city.upper_corner[1] - coordinate[1]) * 0.5))
@staticmethod
def city_mapping(city, building_names=None):
def city_mapping(city, building_names=None, plot=False):
"""
Returns a shared_information dictionary like
@ -78,6 +80,8 @@ class GeometryHelper:
y = int((city.upper_corner[1] - city.lower_corner[1]) * 0.5) + 1
city_map = [['' for _ in range(y + 1)] for _ in range(x + 1)]
map_info = [[{} for _ in range(y + 1)] for _ in range(x + 1)]
img = Image.new('RGB', (x + 1, y + 1), "black") # create a new black image
city_image = img.load() # create the pixel map
for building_name in building_names:
building = city.city_object(building_name)
line = 0
@ -103,13 +107,14 @@ class GeometryHelper:
'line_start': (coordinate[0], coordinate[1]),
'line_end': (next_coordinate[0], next_coordinate[1]),
}
city_image[x, y] = (100, 0, 0)
elif city_map[x][y] != building.name:
neighbour = city.city_object(city_map[x][y])
neighbour_info = map_info[x][y]
# prepare the keys
neighbour_start_coordinate = f'{neighbour_info["line_start"][0]}_{neighbour_info["line_start"][1]}'
building_start_coordinate = f'{coordinate[0]}_{coordinate[1]}'
neighbour_start_coordinate = f'{GeometryHelper.coordinate_to_map_point(neighbour_info["line_start"], city)}'
building_start_coordinate = f'{GeometryHelper.coordinate_to_map_point(coordinate, city)}'
neighbour_key = f'{neighbour.name}_{neighbour_start_coordinate}_{building_start_coordinate}'
building_key = f'{building.name}_{building_start_coordinate}_{neighbour_start_coordinate}'
@ -126,6 +131,10 @@ class GeometryHelper:
'line_end': (next_coordinate[0], next_coordinate[1]),
'neighbour_line_start': neighbour_info['line_start'],
'neighbour_line_end': neighbour_info['line_end'],
'coordinate_start': f"{GeometryHelper.coordinate_to_map_point(coordinate, city)}",
'coordinate_end': f"{GeometryHelper.coordinate_to_map_point(next_coordinate, city)}",
'neighbour_start': f"{GeometryHelper.coordinate_to_map_point(neighbour_info['line_start'], city)}",
'neighbour_end': f"{GeometryHelper.coordinate_to_map_point(neighbour_info['line_end'], city)}",
'shared_points': 1
}
@ -142,6 +151,10 @@ class GeometryHelper:
'line_end': neighbour_info['line_end'],
'neighbour_line_start': (coordinate[0], coordinate[1]),
'neighbour_line_end': (next_coordinate[0], next_coordinate[1]),
'neighbour_start': f"{GeometryHelper.coordinate_to_map_point(coordinate, city)}",
'neighbour_end': f"{GeometryHelper.coordinate_to_map_point(next_coordinate, city)}",
'coordinate_start': f"{GeometryHelper.coordinate_to_map_point(neighbour_info['line_start'], city)}",
'coordinate_end': f"{GeometryHelper.coordinate_to_map_point(neighbour_info['line_end'], city)}",
'shared_points': 1
}
@ -154,6 +167,8 @@ class GeometryHelper:
elif building not in neighbour.neighbours:
neighbour.neighbours.append(building)
line += 1
if plot:
img.show()
return lines_information
@staticmethod

View File

@ -6,7 +6,7 @@ log_dir = (Path(__file__).parent.parent / 'logs').resolve()
log_file = (log_dir / 'hub.log').resolve()
try:
if not os.path.isfile(log_file):
if not os.path.exists:
if not os.path.exists(log_dir):
os.mkdir(log_dir)
with open(log_file, 'x'):
pass

View File

@ -4,8 +4,10 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import math
import sys
from hub.hub_logger import logger
import hub.helpers.constants as cte
from hub.catalog_factories.construction_catalog_factory import ConstructionCatalogFactory
@ -37,10 +39,13 @@ class NrcanPhysicsParameters:
function = Dictionaries().hub_function_to_nrcan_construction_function[building.function]
archetype = self._search_archetype(nrcan_catalog, function, building.year_of_construction, self._climate_zone)
except KeyError:
logger.error(f'Building {building.name} has unknown construction archetype for building function: '
f'{building.function}, building year of construction: {building.year_of_construction} '
f'and climate zone {self._climate_zone}\n')
sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: '
f'{building.function}, building year of construction: {building.year_of_construction} '
f'and climate zone {self._climate_zone}\n')
return
continue
# if building has no thermal zones defined from geometry, and the building will be divided in storeys,
# one thermal zone per storey is assigned
if len(building.internal_zones) == 1:

View File

@ -39,14 +39,14 @@ class NrelPhysicsParameters:
archetype = self._search_archetype(nrel_catalog, function, building.year_of_construction,
self._climate_zone)
except KeyError:
logger.error(f'Building {building.name} has unknown archetype for building function: {building.function} '
f'and building year of construction: {building.year_of_construction} '
logger.error(f'Building {building.name} has unknown construction archetype for building function: '
f'{building.function} and building year of construction: {building.year_of_construction} '
f'and climate zone reference norm {self._climate_zone}\n')
sys.stderr.write(f'Building {building.name} has unknown archetype for building function: {building.function} '
f'and building year of construction: {building.year_of_construction} '
sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: '
f'{building.function} and building year of construction: {building.year_of_construction} '
f'and climate zone reference norm {self._climate_zone}\n')
return
continue
# if building has no thermal zones defined from geometry, and the building will be divided in storeys,
# one thermal zone per storey is assigned

View File

@ -6,6 +6,7 @@ Project Coder Guillermo Gutierrez Guillermo.GutierrezMorote@concordia.ca
"""
import json
import numpy as np
import trimesh.creation
from pyproj import Transformer
@ -64,8 +65,11 @@ class Geojson:
buildings = []
for zone, surface_coordinates in enumerate(surfaces_coordinates):
points = igh.points_from_string(igh.remove_last_point_from_string(surface_coordinates))
# geojson provides the roofs, need to be transform into grounds
points = igh.invert_points(points)
polygon = Polygon(points)
surfaces.append(Surface(polygon, polygon, surface_type=cte.GROUND))
polygon.area = igh.ground_area(points)
surfaces.append(Surface(polygon, polygon))
buildings.append(Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function))
return buildings
@ -74,22 +78,41 @@ class Geojson:
lod0_buildings = Geojson._create_buildings_lod0(name, year_of_construction, function, surface_coordinates)
surfaces = []
buildings = []
for zone, lod0_building in enumerate(lod0_buildings):
for surface in lod0_building.surfaces:
shapely_polygon = ShapelyPolygon(surface.solid_polygon.coordinates)
if not shapely_polygon.is_valid:
print(surface.solid_polygon.area)
print('error?', name, surface_coordinates)
continue
mesh = trimesh.creation.extrude_polygon(shapely_polygon, height)
for face in mesh.faces:
points = []
for vertex_index in face:
points.append(mesh.vertices[vertex_index])
polygon = Polygon(points)
surface = Surface(polygon, polygon)
for surface in lod0_building.grounds:
volume = surface.solid_polygon.area * height
surfaces.append(surface)
buildings.append(Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function))
roof_coordinates = []
# adding a roof means invert the polygon coordinates and change the Z value
for coordinate in surface.solid_polygon.coordinates:
roof_coordinate = np.array([coordinate[0], coordinate[1], height])
# insert the roof rotated already
roof_coordinates.insert(0, roof_coordinate)
polygon = Polygon(roof_coordinates)
roof = Surface(polygon, polygon)
surfaces.append(roof)
# adding a wall means add the point coordinates and the next point coordinates with Z's height and 0
coordinates_length = len(roof.solid_polygon.coordinates)
for i, coordinate in enumerate(roof.solid_polygon.coordinates):
j = i + 1
if j == coordinates_length:
j = 0
next_coordinate = roof.solid_polygon.coordinates[j]
wall_coordinates = [
np.array([coordinate[0], coordinate[1], 0.0]),
np.array([next_coordinate[0], next_coordinate[1], 0.0]),
np.array([next_coordinate[0], next_coordinate[1], next_coordinate[2]]),
np.array([coordinate[0], coordinate[1], coordinate[2]])
]
polygon = Polygon(wall_coordinates)
wall = Surface(polygon, polygon)
surfaces.append(wall)
building = Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function)
building.volume = volume
buildings.append(building)
return buildings
def _get_polygons(self, polygons, coordinates):
@ -119,6 +142,8 @@ class Geojson:
def _store_shared_percentage_to_walls(self, city, city_mapped):
for building in city.buildings:
if building.name not in city_mapped.keys():
continue
building_mapped = city_mapped[building.name]
for wall in building.walls:
percentage = 0

View File

@ -4,6 +4,8 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import math
import sys
import numpy as np
@ -45,3 +47,69 @@ class GeometryHelper:
array = points.split(' ')
res = " "
return res.join(array[0:len(array) - 3])
@staticmethod
def invert_points(points):
res = []
for point in points:
res.insert(0,point)
return res
@staticmethod
def ground_area(points):
"""
Get ground surface area in square meters
:return: float
"""
# New method to calculate area
if len(points) < 3:
sys.stderr.write('Warning: the area of a line or point cannot be calculated 1. Area = 0\n')
return 0
alpha = 0
vec_1 = points[1] - points[0]
for i in range(2, len(points)):
vec_2 = points[i] - points[0]
alpha += GeometryHelper.angle_between_vectors(vec_1, vec_2)
if alpha == 0:
sys.stderr.write('Warning: the area of a line or point cannot be calculated 2. Area = 0\n')
return 0
#
horizontal_points = points
area = 0
for i in range(0, len(horizontal_points) - 1):
point = horizontal_points[i]
next_point = horizontal_points[i + 1]
area += (next_point[1] + point[1]) / 2 * (next_point[0] - point[0])
next_point = horizontal_points[0]
point = horizontal_points[len(horizontal_points) - 1]
area += (next_point[1] + point[1]) / 2 * (next_point[0] - point[0])
_area = abs(area)
return _area
@staticmethod
def angle_between_vectors(vec_1, vec_2):
"""
angle between vectors in radians
:param vec_1: vector
:param vec_2: vector
:return: float
"""
if np.linalg.norm(vec_1) == 0 or np.linalg.norm(vec_2) == 0:
sys.stderr.write("Warning: impossible to calculate angle between planes' normal. Return 0\n")
return 0
cosine = np.dot(vec_1, vec_2) / np.linalg.norm(vec_1) / np.linalg.norm(vec_2)
if cosine > 1 and cosine - 1 < 1e-5:
cosine = 1
elif cosine < -1 and cosine + 1 > -1e-5:
cosine = -1
alpha = math.acos(cosine)
return alpha
@staticmethod
def invert_points(points):
res = []
for point in points:
res.insert(0,point)
return res

View File

@ -44,7 +44,7 @@ class ComnetUsageParameters:
except KeyError:
sys.stderr.write(f'Building {building.name} has unknown usage archetype for building function:'
f' {building.function}')
return
continue
for internal_zone in building.internal_zones:
if internal_zone.area is None:

View File

@ -43,7 +43,7 @@ class NrcanUsageParameters:
except KeyError:
sys.stderr.write(f'Building {building.name} has unknown usage archetype for building function:'
f' {building.function}')
return
continue
usage_name = Dictionaries().hub_usage_to_comnet_usage[building.function]
try:
@ -51,7 +51,7 @@ class NrcanUsageParameters:
except KeyError:
sys.stderr.write(f'Building {building.name} has unknown usage archetype for building function:'
f' {building.function}')
return
continue
for internal_zone in building.internal_zones:
if internal_zone.area is None:

View File

@ -23,3 +23,4 @@ shapely
geopandas
triangle
psycopg2-binary
PIL

View File

@ -4,11 +4,13 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import datetime
from pathlib import Path
from unittest import TestCase
from hub.helpers.geometry_helper import GeometryHelper
from numpy import inf
import hub.exports.exports_factory
from hub.imports.construction_factory import ConstructionFactory
from hub.imports.geometry_factory import GeometryFactory
@ -116,7 +118,6 @@ class TestGeometryFactory(TestCase):
city = self._get_city(file, 'rhino')
self.assertIsNotNone(city, 'city is none')
self.assertTrue(len(city.buildings) == 36)
i = 0
def test_import_obj(self):
"""
@ -133,18 +134,20 @@ class TestGeometryFactory(TestCase):
"""
Test geojson import
"""
file = 'neighbours.geojson'
file = '2000_buildings.geojson'
start = datetime.datetime.now()
city = self._get_city(file, 'geojson',
height_field='citygml_me',
height_field='building_height',
year_of_construction_field='ANNEE_CONS',
function_field='CODE_UTILI')
end = datetime.datetime.now()
print(f'geometry load in {end-start} s')
start = datetime.datetime.now()
hub.exports.exports_factory.ExportsFactory('obj', city, self._output_path).export()
self.assertEqual(207, len(city.buildings), 'wrong number of buildings')
end = datetime.datetime.now()
print(f'geometry export in {end - start} s')
self.assertEqual(2356, len(city.buildings), 'wrong number of buildings')
self._check_buildings(city)
for building in city.buildings:
for wall in building.walls:
self.assertIsNotNone(wall.percentage_shared, 'wall percentage shared is not assigned')
def test_map_neighbours(self):
"""
@ -155,7 +158,13 @@ class TestGeometryFactory(TestCase):
height_field='citygml_me',
year_of_construction_field='ANNEE_CONS',
function_field='LIBELLE_UT')
print(GeometryHelper.city_mapping(city))
info_lod1 = GeometryHelper.city_mapping(city, plot=False)
city = self._get_city(file, 'geojson',
year_of_construction_field='ANNEE_CONS',
function_field='LIBELLE_UT')
info_lod0 = GeometryHelper.city_mapping(city, plot=False)
hub.exports.exports_factory.ExportsFactory('obj', city, self._output_path).export()
self.assertEqual(info_lod0, info_lod1)
for building in city.buildings:
self.assertEqual(2, len(building.neighbours))
@ -165,4 +174,3 @@ class TestGeometryFactory(TestCase):
self.assertEqual('3_part_0_zone_0',city.city_object('2_part_0_zone_0').neighbours[1].name)
self.assertEqual('1_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[0].name)
self.assertEqual('2_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[1].name)

File diff suppressed because one or more lines are too long

View File

@ -12,18 +12,18 @@
-73.580414175680588,
45.497641136608358
],
[
-73.581414175680588,
45.497641136608358
],
[
-73.581414175680588,
45.498641136608358
],
[
-73.580414175680588,
45.498641136608358
],
[
-73.581414175680588,
45.498641136608358
],
[
-73.581414175680588,
45.497641136608358
],
[
-73.580414175680588,
45.497641136608358
@ -204,19 +204,20 @@
[
-73.581414175680588,
45.497641136608358
]
,
[
-73.581414175680588,
45.498441136608358
],
[
-73.582214175680588,
45.498441136608358
],
[
-73.582214175680588,
45.497641136608358
],
[
-73.582214175680588,
45.498441136608358
],
[
-73.581414175680588,
45.498441136608358
],
[
-73.581414175680588,
45.497641136608358
@ -399,31 +400,30 @@
-73.581914175680588,
45.498441136608358
],
[
-73.581914175680588,
45.499641136608358
],
[
-73.580914175680588,
45.499641136608358
],
[
-73.580914175680588,
45.498641136608358
],
[
-73.581414175680588,
45.498641136608358
],
[
-73.581414175680588,
45.498441136608358
],
[
-73.581414175680588,
45.498641136608358
],
[
-73.580914175680588,
45.498641136608358
],
[
-73.580914175680588,
45.499641136608358
],
[
-73.581914175680588,
45.499641136608358
],
[
-73.581914175680588,
45.498441136608358
]
]
]
},

View File

@ -1 +1 @@
__version__ = '0.1.7.9'
__version__ = '0.1.7.10'