cleanup path handling for easier dockerization

This commit is contained in:
Remy Moll 2024-06-30 18:42:59 +02:00
parent bec1827891
commit 49ce8527a3
12 changed files with 126 additions and 118 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,11 @@ 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
CMD ["pipenv", "run", "fastapi", "run", "src/main.py", '--port 8000', '--workers $NUM_WORKERS']

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

@ -0,0 +1,9 @@
from pathlib import Path
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'
OSM_CACHE_DIR = Path('cache')

View File

@ -1,9 +1,11 @@
import math as m
import json, os
import yaml
from typing import List, Tuple, Optional
from OSMPythonTools.overpass import Overpass, overpassQueryBuilder
import constants
from structs.landmarks import Landmark, LandmarkType
from structs.preferences import Preferences, Preference
@ -13,34 +15,40 @@ NATURE = LandmarkType(landmark_type='nature')
SHOPPING = LandmarkType(landmark_type='shopping')
# 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]) :
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)
l_sights, l_nature, l_shop = get_amenities()
L = []
# List for sightseeing
if preferences.sightseeing.score != 0 :
L1 = get_landmarks(l_sights, SIGHTSEEING, coordinates=coordinates)
L1 = get_landmarks(amenity_selectors['sightseeing'], SIGHTSEEING, coordinates, parameters)
correct_score(L1, preferences.sightseeing)
L += L1
# List for nature
if preferences.nature.score != 0 :
L2 = get_landmarks(l_nature, NATURE, coordinates=coordinates)
L2 = get_landmarks(amenity_selectors['nature'], NATURE, coordinates, parameters)
correct_score(L2, preferences.nature)
L += L2
# List for shopping
if preferences.shopping.score != 0 :
L3 = get_landmarks(l_shop, SHOPPING, coordinates=coordinates)
L3 = get_landmarks(amenity_selectors['shopping'], SHOPPING, coordinates, parameters)
correct_score(L3, preferences.shopping)
L += L3
L = remove_duplicates(L)
return L, take_most_important(L)
return L, take_most_important(L, parameters)
"""def generate_landmarks(preferences: Preferences, city_country: str = None, coordinates: Tuple[float, float] = None) -> Tuple[List[Landmark], List[Landmark]] :
@ -69,37 +77,8 @@ def generate_landmarks(preferences: Preferences, coordinates: Tuple[float, float
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']
def take_most_important(L: List[Landmark], parameters: dict, N: int = 0) -> List[Landmark]:
L_copy = []
L_clean = []
scores = [0]*len(L)
@ -127,7 +106,8 @@ def take_most_important(L: List[Landmark], N = 0) -> List[Landmark] :
for i, elem in enumerate(L_copy) :
scores[i] = elem.attractiveness
res = sorted(range(len(scores)), key = lambda sub: scores[sub])[-(N_important-N):]
res = sorted(range(len(scores)), key = lambda sub: scores[sub])[-(parameters['N_important']-N):]
for i, elem in enumerate(L_copy) :
if i in res :
@ -220,19 +200,25 @@ def create_bbox(coordinates: Tuple[float, float], side_length: int) -> Tuple[flo
return min_lat, min_lon, max_lat, max_lon
def get_landmarks(list_amenity: list, landmarktype: LandmarkType, coordinates: Tuple[float, float]) -> List[Landmark] :
def get_landmarks(
list_amenity: list,
landmarktype: LandmarkType,
coordinates: Tuple[float, float],
parameters: dict
) -> 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']
# # 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)
bbox = create_bbox(coordinates, parameters['city_bbox_side'])
# Initialize some variables
N = 0
@ -269,11 +255,11 @@ def get_landmarks(list_amenity: list, landmarktype: LandmarkType, coordinates: T
# 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)
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, radius) + n_tags*tag_coeff )*park_coeff)
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, radius) + n_tags*tag_coeff
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

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,13 @@
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 +161,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 +195,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 +301,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 +331,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,26 @@
nature:
- "'leisure'='park'"
- "geological"
- "'natural'='geyser'"
- "'natural'='hot_spring'"
- "'natural'='arch'"
- "'natural'='volcano'"
- "'natural'='stone'"
- "'tourism'='alpine_hut'"
- "'tourism'='viewpoint'"
- "'tourism'='zoo'"
- "'waterway'='waterfall'"
shopping:
- "'shop'='department_store'"
- "'shop'='mall'"
sightseeing:
- "'tourism'='museum'"
- "'tourism'='attraction'"
- "'tourism'='gallery'"
- "historic"
- "'amenity'='planetarium'"
- "'amenity'='place_of_worship'"
- "'amenity'='fountain'"
- "'water'='reflecting_pool'"

View File

@ -0,0 +1,6 @@
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,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