Compare commits

..

2 Commits

Author SHA1 Message Date
f9c86261cb switch to osmnx 2024-07-07 14:49:10 +02:00
49ce8527a3 cleanup path handling for easier dockerization 2024-06-30 18:42:59 +02:00
19 changed files with 554 additions and 1035 deletions

View File

@ -2,6 +2,8 @@ on:
pull_request:
branches:
- main
paths:
- backend/**
name: Build and push docker image

View File

@ -6,6 +6,12 @@ COPY Pipfile Pipfile.lock .
RUN pip install pipenv
RUN pipenv install --deploy --system
COPY . /src
COPY src src
CMD ["pipenv", "run", "python", "/app/src/main.py"]
EXPOSE 8000
# Set environment variables used by the deployment. These can be overridden by the user using this image.
ENV NUM_WORKERS=1
ENV OSM_CACHE_DIR=/cache
CMD ["pipenv", "run", "fastapi", "run", "src/main.py", '--port 8000', '--workers $NUM_WORKERS']

View File

@ -7,8 +7,9 @@ name = "pypi"
numpy = "*"
scipy = "*"
fastapi = "*"
osmpythontools = "*"
pydantic = "*"
shapely = "*"
osmnx = "*"
networkx = "*"
[dev-packages]

943
backend/Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +0,0 @@
'leisure'='park'
geological
'natural'='geyser'
'natural'='hot_spring'
'natural'='arch'
'natural'='volcano'
'natural'='stone'
'tourism'='alpine_hut'
'tourism'='viewpoint'
'tourism'='zoo'
'waterway'='waterfall'

View File

@ -1,2 +0,0 @@
'shop'='department_store'
'shop'='mall'

View File

@ -1,8 +0,0 @@
'tourism'='museum'
'tourism'='attraction'
'tourism'='gallery'
historic
'amenity'='planetarium'
'amenity'='place_of_worship'
'amenity'='fountain'
'water'='reflecting_pool'

12
backend/src/constants.py Normal file
View File

@ -0,0 +1,12 @@
from pathlib import Path
import os
PARAMETERS_DIR = Path('src/parameters')
AMENITY_SELECTORS_PATH = PARAMETERS_DIR / 'amenity_selectors.yaml'
LANDMARK_PARAMETERS_PATH = PARAMETERS_DIR / 'landmark_parameters.yaml'
OPTIMIZER_PARAMETERS_PATH = PARAMETERS_DIR / 'optimizer_parameters.yaml'
cache_dir_string = os.getenv('OSM_CACHE_DIR', './cache')
OSM_CACHE_DIR = Path(cache_dir_string)

View File

@ -1,170 +1,71 @@
import math as m
import json, os
from typing import List, Tuple, Optional
from OSMPythonTools.overpass import Overpass, overpassQueryBuilder
import yaml
import os
import osmnx as ox
from shapely.geometry import Point, Polygon, LineString, MultiPolygon
from structs.landmarks import Landmark, LandmarkType
from structs.preferences import Preferences, Preference
import constants
SIGHTSEEING = LandmarkType(landmark_type='sightseeing')
NATURE = LandmarkType(landmark_type='nature')
SHOPPING = LandmarkType(landmark_type='shopping')
ox.config(cache_folder=constants.OSM_CACHE_DIR)
# Include the json here
# Create a list of all things to visit given some preferences and a city. Ready for the optimizer
def generate_landmarks(preferences: Preferences, coordinates: Tuple[float, float]) :
l_sights, l_nature, l_shop = get_amenities()
def generate_landmarks(preferences: Preferences, center_coordinates: tuple[float, float]) :
with constants.AMENITY_SELECTORS_PATH.open('r') as f:
amenity_selectors = yaml.safe_load(f)
with constants.LANDMARK_PARAMETERS_PATH.open('r') as f:
# even though we don't use the parameters here, we already load them to avoid unnecessary io operations
parameters = yaml.safe_load(f)
max_distance = parameters['city_bbox_side']
L = []
# List for sightseeing
if preferences.sightseeing.score != 0 :
L1 = get_landmarks(l_sights, SIGHTSEEING, coordinates=coordinates)
if preferences.sightseeing.score != 0:
score_func = lambda loc, n_tags: int((count_elements_within_radius(loc, parameters['radius_close_to']) + n_tags * parameters['tag_coeff']) * parameters['church_coeff'])
L1 = get_landmarks(amenity_selectors['sightseeing'], SIGHTSEEING, center_coordinates, max_distance, score_func)
correct_score(L1, preferences.sightseeing)
L += L1
# List for nature
if preferences.nature.score != 0 :
L2 = get_landmarks(l_nature, NATURE, coordinates=coordinates)
if preferences.nature.score != 0:
score_func = lambda loc, n_tags: int((count_elements_within_radius(loc, parameters['radius_close_to']) + n_tags * parameters['tag_coeff']) * parameters['park_coeff'])
L2 = get_landmarks(amenity_selectors['nature'], NATURE, center_coordinates, max_distance, score_func)
correct_score(L2, preferences.nature)
L += L2
# List for shopping
if preferences.shopping.score != 0 :
L3 = get_landmarks(l_shop, SHOPPING, coordinates=coordinates)
if preferences.shopping.score != 0:
score_func = lambda loc, n_tags: count_elements_within_radius(loc, parameters['radius_close_to']) + n_tags * parameters['tag_coeff']
L3 = get_landmarks(amenity_selectors['shopping'], SHOPPING, center_coordinates, max_distance, score_func)
correct_score(L3, preferences.shopping)
L += L3
L = remove_duplicates(L)
# remove duplicates
L = list(set(L))
print(len(L))
L_constrained = take_most_important(L, parameters['N_important'])
print(len(L_constrained))
return L, L_constrained
return L, take_most_important(L)
"""def generate_landmarks(preferences: Preferences, city_country: str = None, coordinates: Tuple[float, float] = None) -> Tuple[List[Landmark], List[Landmark]] :
l_sights, l_nature, l_shop = get_amenities()
L = []
# List for sightseeing
if preferences.sightseeing.score != 0 :
L1 = get_landmarks(l_sights, SIGHTSEEING, city_country=city_country, coordinates=coordinates)
correct_score(L1, preferences.sightseeing)
L += L1
# List for nature
if preferences.nature.score != 0 :
L2 = get_landmarks(l_nature, NATURE, city_country=city_country, coordinates=coordinates)
correct_score(L2, preferences.nature)
L += L2
# List for shopping
if preferences.shopping.score != 0 :
L3 = get_landmarks(l_shop, SHOPPING, city_country=city_country, coordinates=coordinates)
correct_score(L3, preferences.shopping)
L += L3
return remove_duplicates(L), take_most_important(L)
"""
# Helper function to gather the amenities list
def get_amenities() -> List[List[str]] :
# Get the list of amenities from the files
sightseeing = get_list('/amenities/sightseeing.am')
nature = get_list('/amenities/nature.am')
shopping = get_list('/amenities/shopping.am')
return sightseeing, nature, shopping
# Helper function to read a .am file and generate the corresponding list
def get_list(path: str) -> List[str] :
with open(os.path.dirname(os.path.abspath(__file__)) + path) as f :
content = f.readlines()
amenities = []
for line in content :
amenities.append(line.strip('\n'))
return amenities
# Take the most important landmarks from the list
def take_most_important(L: List[Landmark], N = 0) -> List[Landmark] :
# Read the parameters from the file
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/landmarks_manager.params', "r") as f :
parameters = json.loads(f.read())
N_important = parameters['N important']
L_copy = []
L_clean = []
scores = [0]*len(L)
names = []
name_id = {}
def take_most_important(landmarks: list[Landmark], n_max: int) -> list[Landmark]:
for i, elem in enumerate(L) :
if elem.name not in names :
names.append(elem.name)
name_id[elem.name] = [i]
L_copy.append(elem)
else :
name_id[elem.name] += [i]
scores = []
for j in name_id[elem.name] :
scores.append(L[j].attractiveness)
best_id = max(range(len(scores)), key=scores.__getitem__)
t = name_id[elem.name][best_id]
if t == i :
for old in L_copy :
if old.name == elem.name :
old.attractiveness = L[t].attractiveness
scores = [0]*len(L_copy)
for i, elem in enumerate(L_copy) :
scores[i] = elem.attractiveness
res = sorted(range(len(scores)), key = lambda sub: scores[sub])[-(N_important-N):]
for i, elem in enumerate(L_copy) :
if i in res :
L_clean.append(elem)
return L_clean
landmarks_sorted = sorted(landmarks, key=lambda x: x.attractiveness, reverse=True)
return landmarks_sorted[:n_max]
# Remove duplicate elements and elements with low score
def remove_duplicates(L: List[Landmark]) -> List[Landmark] :
"""
Removes duplicate landmarks based on their names from the given list.
Parameters:
L (List[Landmark]): A list of Landmark objects.
Returns:
List[Landmark]: A list of unique Landmark objects based on their names.
"""
L_clean = []
names = []
for landmark in L :
if landmark.name in names:
continue
else :
names.append(landmark.name)
L_clean.append(landmark)
return L_clean
# Correct the score of a list of landmarks by taking into account preference settings
def correct_score(L: List[Landmark], preference: Preference) :
def correct_score(L: list[Landmark], preference: Preference) :
if len(L) == 0 :
return
@ -177,189 +78,111 @@ def correct_score(L: List[Landmark], preference: Preference) :
# Function to count elements within a certain radius of a location
def count_elements_within_radius(coordinates: Tuple[float, float], radius: int) -> int:
def count_elements_within_radius(point: Point, radius: int) -> int:
lat = coordinates[0]
lon = coordinates[1]
center_coordinates = (point.x, point.y)
try:
landmarks = ox.features_from_point(
center_point = center_coordinates,
dist = radius,
tags = {'building': True} # this is a common tag to give an estimation of the number of elements in the area
)
return len(landmarks)
except ox._errors.InsufficientResponseError:
return 0
alpha = (180*radius)/(6371000*m.pi)
bbox = {'latLower':lat-alpha,'lonLower':lon-alpha,'latHigher':lat+alpha,'lonHigher': lon+alpha}
def get_landmarks(
amenity_selectors: list[dict],
landmarktype: LandmarkType,
center_coordinates: tuple[float, float],
distance: int,
score_function: callable
) -> list[Landmark]:
landmarks = ox.features_from_point(
center_point = center_coordinates,
dist = distance,
tags = amenity_selectors
)
# cleanup the list
# remove rows where name is None
landmarks = landmarks[landmarks['name'].notna()]
# TODO: remove rows that are part of another building
ret_landmarks = []
for element, description in landmarks.iterrows():
osm_type = element[0]
osm_id = element[1]
location = description['geometry']
n_tags = len(description['nodes']) if type(description['nodes']) == list else 1
# print(description['nodes'])
print(description['name'])
# print(location, type(location))
if type(location) == Point:
location = location
elif type(location) == Polygon or type(location) == MultiPolygon:
location = location.centroid
elif type(location) == LineString:
location = location.interpolate(location.length/2)
score = score_function(location, n_tags)
print(score)
landmark = Landmark(
name = description['name'],
type = landmarktype,
location = (location.x, location.y),
osm_type = osm_type,
osm_id = osm_id,
attractiveness = score,
must_do = False,
n_tags = n_tags
)
ret_landmarks.append(landmark)
return ret_landmarks
# for elem in G.iterrows():
# print(elem)
# print(elem.name)
# print(elem.address)
# name = elem.tag('name') # Add name
# location = (elem.centerLat(), elem.centerLon()) # Add coordinates (lat, lon)
# # skip if unprecise location
# if name is None or location[0] is None:
# continue
# # skip if unused
# if 'disused:leisure' in elem.tags().keys():
# continue
# Build the query to find elements within the radius
radius_query = overpassQueryBuilder(bbox=[bbox['latLower'],bbox['lonLower'],bbox['latHigher'],bbox['lonHigher']],
elementType=['node', 'way', 'relation'])
try :
overpass = Overpass()
radius_result = overpass.query(radius_query)
return radius_result.countElements()
except :
return None
# Creates a bounding box around given coordinates
def create_bbox(coordinates: Tuple[float, float], side_length: int) -> Tuple[float, float, float, float]:
lat = coordinates[0]
lon = coordinates[1]
# Half the side length in km (since it's a square bbox)
half_side_length_km = side_length / 2.0
# Convert distance to degrees
lat_diff = half_side_length_km / 111 # 1 degree latitude is approximately 111 km
lon_diff = half_side_length_km / (111 * m.cos(m.radians(lat))) # Adjust for longitude based on latitude
# Calculate bbox
min_lat = lat - lat_diff
max_lat = lat + lat_diff
min_lon = lon - lon_diff
max_lon = lon + lon_diff
return min_lat, min_lon, max_lat, max_lon
def get_landmarks(list_amenity: list, landmarktype: LandmarkType, coordinates: Tuple[float, float]) -> List[Landmark] :
# Read the parameters from the file
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/landmarks_manager.params', "r") as f :
parameters = json.loads(f.read())
tag_coeff = parameters['tag coeff']
park_coeff = parameters['park coeff']
church_coeff = parameters['church coeff']
radius = parameters['radius close to']
bbox_side = parameters['city bbox side']
# Create bbox around start location
bbox = create_bbox(coordinates, bbox_side)
# Initialize some variables
N = 0
L = []
overpass = Overpass()
for amenity in list_amenity :
query = overpassQueryBuilder(bbox=bbox, elementType=['way', 'relation'], selector=amenity, includeCenter=True, out='body')
result = overpass.query(query)
N += result.countElements()
for elem in result.elements():
name = elem.tag('name') # Add name
location = (elem.centerLat(), elem.centerLon()) # Add coordinates (lat, lon)
# skip if unprecise location
if name is None or location[0] is None:
continue
# skip if unused
if 'disused:leisure' in elem.tags().keys():
continue
# # skip if part of another building
# if 'building:part' in elem.tags().keys() and elem.tag('building:part') == 'yes':
# continue
# else :
# osm_type = elem.type() # Add type : 'way' or 'relation'
# osm_id = elem.id() # Add OSM id
# elem_type = landmarktype # Add the landmark type as 'sightseeing
# n_tags = len(elem.tags().keys()) # Add number of tags
# skip if part of another building
if 'building:part' in elem.tags().keys() and elem.tag('building:part') == 'yes':
continue
else :
osm_type = elem.type() # Add type : 'way' or 'relation'
osm_id = elem.id() # Add OSM id
elem_type = landmarktype # Add the landmark type as 'sightseeing
n_tags = len(elem.tags().keys()) # Add number of tags
# Add score of given landmark based on the number of surrounding elements. Penalty for churches as there are A LOT
if amenity == "'amenity'='place_of_worship'" :
score = int((count_elements_within_radius(location, radius) + n_tags*tag_coeff )*church_coeff)
elif amenity == "'leisure'='park'" :
score = int((count_elements_within_radius(location, radius) + n_tags*tag_coeff )*park_coeff)
else :
score = count_elements_within_radius(location, radius) + n_tags*tag_coeff
# # Add score of given landmark based on the number of surrounding elements. Penalty for churches as there are A LOT
# if amenity == "'amenity'='place_of_worship'" :
# score = int((count_elements_within_radius(location, parameters['radius_close_to']) + n_tags*parameters['tag_coeff'] )*parameters['church_coeff'])
# elif amenity == "'leisure'='park'" :
# score = int((count_elements_within_radius(location, parameters['radius_close_to']) + n_tags*parameters['tag_coeff'] )*parameters['park_coeff'])
# else :
# score = count_elements_within_radius(location, parameters['radius_close_to']) + n_tags*parameters['tag_coeff']
if score is not None :
# Generate the landmark and append it to the list
landmark = Landmark(name=name, type=elem_type, location=location, osm_type=osm_type, osm_id=osm_id, attractiveness=score, must_do=False, n_tags=n_tags)
L.append(landmark)
# if score is not None :
# # Generate the landmark and append it to the list
# landmark = Landmark(name=name, type=elem_type, location=location, osm_type=osm_type, osm_id=osm_id, attractiveness=score, must_do=False, n_tags=n_tags)
# L.append(landmark)
return L
# return L
"""def get_landmarks(list_amenity: list, landmarktype: LandmarkType, city_country: str = None, coordinates: Tuple[float, float] = None) -> List[Landmark] :
if city_country is None and coordinates is None :
raise ValueError("Either one of 'city_country' and 'coordinates' arguments must be specified")
if city_country is not None and coordinates is not None :
raise ValueError("Cannot specify both 'city_country' and 'coordinates' at the same time, please choose either one")
# Read the parameters from the file
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/landmarks_manager.params', "r") as f :
parameters = json.loads(f.read())
tag_coeff = parameters['tag coeff']
park_coeff = parameters['park coeff']
church_coeff = parameters['church coeff']
radius = parameters['radius close to']
bbox_side = parameters['city bbox side']
# If city_country is specified :
if city_country is not None :
nominatim = Nominatim()
areaId = nominatim.query(city_country).areaId()
bbox = None
# If coordinates are specified :
elif coordinates is not None :
bbox = create_bbox(coordinates, bbox_side)
areaId = None
else :
raise ValueError("Argument number is not corresponding.")
# Initialize some variables
N = 0
L = []
overpass = Overpass()
for amenity in list_amenity :
query = overpassQueryBuilder(area=areaId, bbox=bbox, elementType=['way', 'relation'], selector=amenity, includeCenter=True, out='body')
result = overpass.query(query)
N += result.countElements()
for elem in result.elements():
name = elem.tag('name') # Add name
location = (elem.centerLat(), elem.centerLon()) # Add coordinates (lat, lon)
# skip if unprecise location
if name is None or location[0] is None:
continue
# skip if unused
if 'disused:leisure' in elem.tags().keys():
continue
# skip if part of another building
if 'building:part' in elem.tags().keys() and elem.tag('building:part') == 'yes':
continue
else :
osm_type = elem.type() # Add type : 'way' or 'relation'
osm_id = elem.id() # Add OSM id
elem_type = landmarktype # Add the landmark type as 'sightseeing
n_tags = len(elem.tags().keys()) # Add number of tags
# Add score of given landmark based on the number of surrounding elements. Penalty for churches as there are A LOT
if amenity == "'amenity'='place_of_worship'" :
score = int((count_elements_within_radius(location, radius) + n_tags*tag_coeff )*church_coeff)
elif amenity == "'leisure'='park'" :
score = int((count_elements_within_radius(location, radius) + n_tags*tag_coeff )*park_coeff)
else :
score = count_elements_within_radius(location, radius) + n_tags*tag_coeff
if score is not None :
# Generate the landmark and append it to the list
landmark = Landmark(name=name, type=elem_type, location=location, osm_type=osm_type, osm_id=osm_id, attractiveness=score, must_do=False, n_tags=n_tags)
L.append(landmark)
return L
"""

View File

@ -1,23 +0,0 @@
import fastapi
from dataclasses import dataclass
@dataclass
class Destination:
name: str
location: tuple
attractiveness: int
d = Destination()
def get_route() -> list[Destination]:
return {"route": "Hello World"}
endpoint = ("/get_route", get_route)
end
if __name__ == "__main__":
fastapi.run()

View File

@ -1,13 +1,12 @@
import numpy as np
import json, os
import yaml
from typing import List, Tuple
from scipy.optimize import linprog
from math import radians, sin, cos, acos
from shapely import Polygon
from structs.landmarks import Landmark
import constants
# Function to print the result
def print_res(L: List[Landmark], L_tot):
@ -161,10 +160,11 @@ def get_distance(p1: Tuple[float, float], p2: Tuple[float, float], detour: float
# We want to maximize the sightseeing : max(c) st. A*x < b and A_eq*x = b_eq
def init_ub_dist(landmarks: List[Landmark], max_steps: int):
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
parameters = json.loads(f.read())
detour = parameters['detour factor']
speed = parameters['average walking speed']
# Read the parameters from the file
with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
parameters = yaml.safe_load(f)
detour = parameters['detour_factor']
speed = parameters['average_walking_speed']
# Objective function coefficients. a*x1 + b*x2 + c*x3 + ...
c = []
@ -194,9 +194,9 @@ def respect_number(L:int, A_ub, b_ub):
b_ub.append(1)
# Read the parameters from the file
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
parameters = json.loads(f.read())
max_landmarks = parameters['max landmarks']
with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
parameters = yaml.safe_load(f)
max_landmarks = parameters['max_landmarks']
A_ub = np.vstack((A_ub, ones*L))
b_ub.append(max_landmarks+1)
@ -300,13 +300,14 @@ def respect_order(N: int, A_eq, b_eq):
# Computes the time to reach from each landmark to the next
def link_list(order: List[int], landmarks: List[Landmark])->List[Landmark] :
def link_list(order: List[int], landmarks: List[Landmark]) -> List[Landmark]:
# Read the parameters from the file
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
parameters = json.loads(f.read())
detour_factor = parameters['detour factor']
speed = parameters['average walking speed']
with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
parameters = yaml.safe_load(f)
detour_factor = parameters['detour_factor']
speed = parameters['average_walking_speed']
L = []
j = 0
@ -329,10 +330,11 @@ def link_list(order: List[int], landmarks: List[Landmark])->List[Landmark] :
def link_list_simple(ordered_visit: List[Landmark])-> List[Landmark] :
# Read the parameters from the file
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
parameters = json.loads(f.read())
detour_factor = parameters['detour factor']
speed = parameters['average walking speed']
with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
parameters = yaml.safe_load(f)
detour_factor = parameters['detour_factor']
speed = parameters['average_walking_speed']
L = []
j = 0

View File

@ -0,0 +1,32 @@
nature:
leisure: park
geological: ''
natural:
- geyser
- hot_spring
- arch
- volcano
- stone
tourism:
- alpine_hut
- viewpoint
- zoo
waterway: waterfall
shopping:
shop:
- department_store
- mall
sightseeing:
tourism:
- museum
- attraction
- gallery
historic: ''
amenity:
- planetarium
- place_of_worship
- fountain
water:
- reflecting_pool

View File

@ -0,0 +1,6 @@
city_bbox_side: 1500 #m
radius_close_to: 30
church_coeff: 0.6
park_coeff: 1.5
tag_coeff: 100
N_important: 40

View File

@ -1,8 +0,0 @@
{
"city bbox side" : 10,
"radius close to" : 27.5,
"church coeff" : 0.6,
"park coeff" : 1.5,
"tag coeff" : 100,
"N important" : 40
}

View File

@ -1,5 +0,0 @@
{
"detour factor" : 1.4,
"average walking speed" : 4.8,
"max landmarks" : 10
}

View File

@ -0,0 +1,3 @@
detour_factor: 1.4
average_walking_speed: 4.8
max_landmarks: 10

View File

@ -1,7 +1,8 @@
from collections import defaultdict
from heapq import heappop, heappush
from itertools import permutations
import os, json
import os
import yaml
from shapely import buffer, LineString, Point, Polygon, MultiPoint, convex_hull, concave_hull, LinearRing
from typing import List, Tuple
@ -10,6 +11,7 @@ from math import pi
from structs.landmarks import Landmark
from landmarks_manager import take_most_important
from optimizer import solve_optimization, link_list_simple, print_res, get_distance
import constants
def create_corridor(landmarks: List[Landmark], width: float) :
@ -122,12 +124,12 @@ def total_path_distance(path: List[Landmark], detour, speed) -> float:
def find_shortest_path_through_all_landmarks(landmarks: List[Landmark]) -> List[Landmark]:
# Read from data
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
parameters = json.loads(f.read())
detour = parameters['detour factor']
speed = parameters['average walking speed']
# Read the parameters from the file
with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
parameters = yaml.safe_load(f)
detour = parameters['detour_factor']
speed = parameters['average_walking_speed']
# Step 1: Find 'start' and 'finish' landmarks
start_idx = next(i for i, lm in enumerate(landmarks) if lm.name == 'start')
@ -174,8 +176,10 @@ def get_minor_landmarks(all_landmarks: List[Landmark], visited_landmarks: List[L
for landmark in all_landmarks :
if is_in_area(area, landmark.location) and landmark.name not in visited_names:
second_order_landmarks.append(landmark)
return take_most_important(second_order_landmarks, len(visited_landmarks))
with constants.LANDMARK_PARAMETERS_PATH.open('r') as f:
parameters = yaml.safe_load(f)
return take_most_important(second_order_landmarks, parameters, len(visited_landmarks))
@ -195,10 +199,10 @@ def get_minor_landmarks(all_landmarks: List[Landmark], visited_landmarks: List[L
def refine_optimization(landmarks: List[Landmark], base_tour: List[Landmark], max_time: int, print_infos: bool) -> List[Landmark] :
# Read from the file
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
parameters = json.loads(f.read())
max_landmarks = parameters['max landmarks']
# Read the parameters from the file
with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
parameters = yaml.safe_load(f)
max_landmarks = parameters['max_landmarks']
if len(base_tour)-2 >= max_landmarks :
return base_tour

View File

@ -1,6 +1,5 @@
from typing import Optional
from pydantic import BaseModel, Field
from .landmarktype import LandmarkType
from uuid import uuid4
@ -28,3 +27,6 @@ class Landmark(BaseModel) :
time_to_reach_next : Optional[int] = 0 # TODO fix this in existing code
next_uuid : Optional[str] = None # TODO implement this ASAP
def __hash__(self) -> int:
return self.uuid.int

View File

@ -90,7 +90,7 @@ def test4(coordinates: tuple[float, float]) -> List[Landmark]:
#finish = Landmark(name='finish', type=LandmarkType(landmark_type='finish'), location=(48.847132, 2.312359), osm_type='finish', osm_id=0, attractiveness=0, must_do=True, n_tags = 0)
# Generate the landmarks from the start location
landmarks, landmarks_short = generate_landmarks(preferences=preferences, coordinates=start.location)
landmarks, landmarks_short = generate_landmarks(preferences=preferences, center_coordinates=start.location)
#write_data(landmarks, "landmarks.txt")
# Insert start and finish to the landmarks list
@ -98,7 +98,7 @@ def test4(coordinates: tuple[float, float]) -> List[Landmark]:
landmarks_short.append(finish)
# TODO use these parameters in another way
max_walking_time = 2 # hours
max_walking_time = 3 # hours
detour = 30 # minutes
# First stage optimization