From ed445529f9a027ff694f7ddd68491b0eacc909d9 Mon Sep 17 00:00:00 2001 From: Koa Wells Date: Tue, 12 Dec 2023 18:34:30 -0500 Subject: [PATCH 1/4] Initial commit --- .gitignore | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 3 + 2 files changed, 165 insertions(+) create mode 100644 .gitignore create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5d381cc4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,162 @@ +# ---> Python +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + diff --git a/README.md b/README.md new file mode 100644 index 00000000..283dce4c --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# building_selection_tool + +Used for selecting a subset of buildings from a larger geojson dataset. \ No newline at end of file From 5da9766da659a0d738bad9e82b9e764114c9af51 Mon Sep 17 00:00:00 2001 From: Koa Wells Date: Fri, 15 Dec 2023 13:50:41 -0500 Subject: [PATCH 2/4] Add initial version of building selection tool --- building_region_selector.py | 52 +++++++++++++++++++++++++++++++++++++ data/.gitignore | 2 ++ main.py | 34 ++++++++++++++++++++++++ map_view_simple_example.py | 27 +++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 building_region_selector.py create mode 100644 data/.gitignore create mode 100644 main.py create mode 100644 map_view_simple_example.py diff --git a/building_region_selector.py b/building_region_selector.py new file mode 100644 index 00000000..82a7c1c1 --- /dev/null +++ b/building_region_selector.py @@ -0,0 +1,52 @@ +import json + +from pathlib import Path +from shapely.geometry import Polygon + +""" +BuildingSelectionTool is a tool that allows for a subset of buildings to be selected from a larger geojson dataset. +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2023 Concordia CERC group +Project Coder Koa Wells kekoa.wells@concordia.ca +""" + +class BuildingSelectionTool: + def __init__(self, city_file, output_file): + self._city_file = city_file + self._city = json.load(city_file) + self._buildings = self._city['features'] + self._polygon_points = [] + + def _assign_buildings_to_regions(self): + for building in self._buildings: + if building['geometry']['type'] == 'Polygon': + building_centroid = Polygon(building["geometry"]["coordinates"][0]).centroid + elif building['geometry']['type'] == 'MultiPolygon': + # use the centroid of the first polygon inside of the multipolygon + building_centroid = Polygon(building["geometry"]["coordinates"][0][0]).centroid + + building['properties']['centroid'] = [building_centroid.x, building_centroid.y] + building['properties']['district_property'] = [] + + target_regions = [] + region_assigned = False + + target_region = self._regions[len(self._regions) / 2] + while not region_assigned: + if building_centroid.within(Polygon(target_region['geometry']['coordinates'])): + region_assigned = True + break + + target_region_centroid = Polygon(target_region['geometry']['coordinates']).centroid + + if building_centroid.x <= target_region_centroid.x: + if building_centroid.y >= target_region_centroid.y: + regions = regions[0:len(regions) / 2] + elif building_centroid.y < target_region_centroid.y: + regions = regions[len(regions) / 2:len(regions) - 1] + elif building_centroid.x > target_region_centroid.x: + if building_centroid.y >= target_region_centroid.y: + regions = regions[0:len(regions) / 2] + elif building_centroid.y < target_region_centroid.y: + regions = regions[len(regions) / 2:len(regions) - 1] + diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 00000000..c96a04f0 --- /dev/null +++ b/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 00000000..0e23d69e --- /dev/null +++ b/main.py @@ -0,0 +1,34 @@ +import json +from shapely import Polygon +from shapely import Point +from pathlib import Path + +# Make sure to enter your points in the clockwise direction +# and in the longitude, latitude format +selection_box = Polygon([[-73.543833, 45.575932] , + [-73.541834, 45.575245], + [-73.542275, 45.574652], + [-73.544235, 45.575329], + [-73.543833, 45.575932]]) + +geojson_file = Path('./data/collinear_clean.geojson').resolve() +output_file = Path('./output_buildings.geojson').resolve() +buildings_in_region = [] + +with open(geojson_file, 'r') as file: + city = json.load(file) + buildings = city['features'] + +for building in buildings: + coordinates = building['geometry']['coordinates'][0] + building_polygon = Polygon(coordinates) + centroid = Point(building_polygon.centroid) + + if centroid.within(selection_box): + buildings_in_region.append(building) + +output_region = {"type": "FeatureCollection", + "features": buildings_in_region} + +with open(output_file, 'w') as file: + file.write(json.dumps(output_region, indent=2)) diff --git a/map_view_simple_example.py b/map_view_simple_example.py new file mode 100644 index 00000000..fabc09f8 --- /dev/null +++ b/map_view_simple_example.py @@ -0,0 +1,27 @@ +import tkinter +import tkintermapview + +# create tkinter window +root_tk = tkinter.Tk() +root_tk.geometry(f"{1000}x{700}") +root_tk.title("Building Selection Tool") + +# create map widget +map_widget = tkintermapview.TkinterMapView(root_tk, width=1000, height=700, corner_radius=0) +map_widget.pack(fill="both", expand=True) + +# set other tile server (standard is OpenStreetMap) +map_widget.set_tile_server("https://mt0.google.com/vt/lyrs=m&hl=en&x={x}&y={y}&z={z}&s=Ga", max_zoom=22) # google normal +# map_widget.set_tile_server("https://mt0.google.com/vt/lyrs=s&hl=en&x={x}&y={y}&z={z}&s=Ga", max_zoom=22) # google satellite + +# set current position and zoom +map_widget.set_position(45.497059, -73.578451, marker=False) # Berlin, Germany +map_widget.set_zoom(11) + +# set current position with address +# map_widget.set_address("Berlin Germany", marker=False) + +def marker_click(marker): + print(f"marker clicked - text: {marker.text} position: {marker.position}") + +root_tk.mainloop() From 9377e80ff73b1027f82241989b33fc35a69d2b6b Mon Sep 17 00:00:00 2001 From: Koa Wells Date: Fri, 15 Dec 2023 15:17:14 -0500 Subject: [PATCH 3/4] Remove excess files --- building_region_selector.py | 52 ------------------------------------- map_view_simple_example.py | 27 ------------------- 2 files changed, 79 deletions(-) delete mode 100644 building_region_selector.py delete mode 100644 map_view_simple_example.py diff --git a/building_region_selector.py b/building_region_selector.py deleted file mode 100644 index 82a7c1c1..00000000 --- a/building_region_selector.py +++ /dev/null @@ -1,52 +0,0 @@ -import json - -from pathlib import Path -from shapely.geometry import Polygon - -""" -BuildingSelectionTool is a tool that allows for a subset of buildings to be selected from a larger geojson dataset. -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2023 Concordia CERC group -Project Coder Koa Wells kekoa.wells@concordia.ca -""" - -class BuildingSelectionTool: - def __init__(self, city_file, output_file): - self._city_file = city_file - self._city = json.load(city_file) - self._buildings = self._city['features'] - self._polygon_points = [] - - def _assign_buildings_to_regions(self): - for building in self._buildings: - if building['geometry']['type'] == 'Polygon': - building_centroid = Polygon(building["geometry"]["coordinates"][0]).centroid - elif building['geometry']['type'] == 'MultiPolygon': - # use the centroid of the first polygon inside of the multipolygon - building_centroid = Polygon(building["geometry"]["coordinates"][0][0]).centroid - - building['properties']['centroid'] = [building_centroid.x, building_centroid.y] - building['properties']['district_property'] = [] - - target_regions = [] - region_assigned = False - - target_region = self._regions[len(self._regions) / 2] - while not region_assigned: - if building_centroid.within(Polygon(target_region['geometry']['coordinates'])): - region_assigned = True - break - - target_region_centroid = Polygon(target_region['geometry']['coordinates']).centroid - - if building_centroid.x <= target_region_centroid.x: - if building_centroid.y >= target_region_centroid.y: - regions = regions[0:len(regions) / 2] - elif building_centroid.y < target_region_centroid.y: - regions = regions[len(regions) / 2:len(regions) - 1] - elif building_centroid.x > target_region_centroid.x: - if building_centroid.y >= target_region_centroid.y: - regions = regions[0:len(regions) / 2] - elif building_centroid.y < target_region_centroid.y: - regions = regions[len(regions) / 2:len(regions) - 1] - diff --git a/map_view_simple_example.py b/map_view_simple_example.py deleted file mode 100644 index fabc09f8..00000000 --- a/map_view_simple_example.py +++ /dev/null @@ -1,27 +0,0 @@ -import tkinter -import tkintermapview - -# create tkinter window -root_tk = tkinter.Tk() -root_tk.geometry(f"{1000}x{700}") -root_tk.title("Building Selection Tool") - -# create map widget -map_widget = tkintermapview.TkinterMapView(root_tk, width=1000, height=700, corner_radius=0) -map_widget.pack(fill="both", expand=True) - -# set other tile server (standard is OpenStreetMap) -map_widget.set_tile_server("https://mt0.google.com/vt/lyrs=m&hl=en&x={x}&y={y}&z={z}&s=Ga", max_zoom=22) # google normal -# map_widget.set_tile_server("https://mt0.google.com/vt/lyrs=s&hl=en&x={x}&y={y}&z={z}&s=Ga", max_zoom=22) # google satellite - -# set current position and zoom -map_widget.set_position(45.497059, -73.578451, marker=False) # Berlin, Germany -map_widget.set_zoom(11) - -# set current position with address -# map_widget.set_address("Berlin Germany", marker=False) - -def marker_click(marker): - print(f"marker clicked - text: {marker.text} position: {marker.position}") - -root_tk.mainloop() From fef4dd012a73daeb19f7f5750a90fc2fb93bcbcb Mon Sep 17 00:00:00 2001 From: jgavalda Date: Tue, 19 Dec 2023 11:37:24 -0500 Subject: [PATCH 4/4] Including a central point and plotting buildings in a square --- latlong2.py | 42 ++++++++++++++++++++++++++++++++++++++++++ main.py | 20 +++++++++++++------- 2 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 latlong2.py diff --git a/latlong2.py b/latlong2.py new file mode 100644 index 00000000..525b7b0a --- /dev/null +++ b/latlong2.py @@ -0,0 +1,42 @@ +import requests + +def get_lat_lng(api_key, address): + base_url = "https://api.opencagedata.com/geocode/v1/json" + params = { + 'q': address, + 'key': api_key, + } + + response = requests.get(base_url, params=params) + data = response.json() + + if response.status_code == 200 and data['status']['code'] == 200: + location = data['results'][0]['geometry'] + return location['lat'], location['lng'] + else: + print(f"Error: {data['status']['message']}") + return None, None + +def get_lat_lng_for_addresses(api_key, addresses): + coordinates = [] + + for address in addresses: + lat, lng = get_lat_lng(api_key, address) + if lat is not None and lng is not None: + coordinates.append((lat, lng)) + + return coordinates + +# Replace 'YOUR_API_KEY' with your OpenCage Geocoding API key +api_key = '744ad0d2d58e49d1ac57d6fb04fe5d82' + +# Example list of addresses +addresses = ["1600 Amphitheatre Parkway, Mountain View, CA", + "Eiffel Tower, Paris, France", + "Big Ben, London, UK"] + +coordinates = get_lat_lng_for_addresses(api_key, addresses) + +# Print the coordinates +for address, coord in zip(addresses, coordinates): + print(f"{address} -> Latitude: {coord[0]}, Longitude: {coord[1]}") \ No newline at end of file diff --git a/main.py b/main.py index 0e23d69e..9ca691dd 100644 --- a/main.py +++ b/main.py @@ -5,13 +5,19 @@ from pathlib import Path # Make sure to enter your points in the clockwise direction # and in the longitude, latitude format -selection_box = Polygon([[-73.543833, 45.575932] , - [-73.541834, 45.575245], - [-73.542275, 45.574652], - [-73.544235, 45.575329], - [-73.543833, 45.575932]]) - -geojson_file = Path('./data/collinear_clean.geojson').resolve() +#selection_box = Polygon([[-73.543833, 45.575932] , +# [-73.541834, 45.575245], +# [-73.542275, 45.574652], +# [-73.544235, 45.575329], +# [-73.543833, 45.575932]]) +x=-73.61574532884296 +y=45.603911965131 +diff=0.001 +selection_box = Polygon([[x+diff, y-diff], + [x-diff, y-diff], + [x-diff, y+diff], + [x+diff, y+diff]]) +geojson_file = Path('./data/collinear_clean 1.geojson').resolve() output_file = Path('./output_buildings.geojson').resolve() buildings_in_region = []