Merge branch 'feature/adding-timed-visits'
Some checks failed
Build and deploy the backend to production / Build and push image (push) Successful in 2m3s
/ push-to-remote (push) Failing after 22s
Build and deploy the backend to production / Deploy to production (push) Successful in 12s

This commit is contained in:
Remy Moll 2024-09-22 15:04:56 +02:00
commit f71aab22dc
7 changed files with 48 additions and 34 deletions

View File

@ -2,5 +2,5 @@ detour_factor: 1.4
detour_corridor_width: 300 detour_corridor_width: 300
average_walking_speed: 4.8 average_walking_speed: 4.8
max_landmarks: 10 max_landmarks: 10
max_landmarks_refiner: 20 max_landmarks_refiner: 30
overshoot: 1.3 overshoot: 1.8

View File

@ -15,8 +15,11 @@ class Landmark(BaseModel) :
attractiveness : int attractiveness : int
n_tags : int n_tags : int
image_url : Optional[str] = None # TODO future image_url : Optional[str] = None # TODO future
website_url : Optional[str] = None
wikipedia_url : Optional[str] = None
description : Optional[str] = None # TODO future description : Optional[str] = None # TODO future
duration : Optional[int] = 0 # TODO future duration : Optional[int] = 0 # TODO future
name_en : Optional[str] = None
# Unique ID of a given landmark # Unique ID of a given landmark
uuid: str = Field(default_factory=uuid4) # TODO implement this ASAP uuid: str = Field(default_factory=uuid4) # TODO implement this ASAP
@ -33,7 +36,9 @@ class Landmark(BaseModel) :
return self.uuid.int return self.uuid.int
def __str__(self) -> str: def __str__(self) -> str:
time_to_next_str = f"time_to_next={self.time_to_reach_next}" if self.time_to_reach_next else "" time_to_next_str = f", time_to_next={self.time_to_reach_next}" if self.time_to_reach_next else ""
# return f'Landmark({self.type}): [{self.name} @{self.location}, score={self.attractiveness}{time_to_next_str}]' is_secondary_str = f", secondary" if self.is_secondary else ""
return f'({self.type[:4]}), score={self.attractiveness}\tmain:{not self.is_secondary}\tduration={self.duration}\t{time_to_next_str}\t{self.name}' type_str = '(' + self.type + ')'
if self.type in ["start", "finish", "nature", "shopping"] : type_str += '\t '
return f'Landmark{type_str}: [{self.name} @{self.location}, score={self.attractiveness}{time_to_next_str}{is_secondary_str}]'

View File

@ -52,7 +52,7 @@ class LinkedLandmarks:
# Update 'is_secondary' for landmarks with attractiveness below the threshold score # Update 'is_secondary' for landmarks with attractiveness below the threshold score
for landmark in self._landmarks: for landmark in self._landmarks:
if landmark.attractiveness < threshold_score: if landmark.attractiveness < threshold_score and landmark.type not in ["start", "finish"]:
landmark.is_secondary = True landmark.is_secondary = True

View File

@ -23,9 +23,8 @@ def test(start_coords: tuple[float, float], finish_coords: tuple[float, float] =
sightseeing=Preference(type='sightseeing', score = 5), sightseeing=Preference(type='sightseeing', score = 5),
nature=Preference(type='nature', score = 5), nature=Preference(type='nature', score = 5),
shopping=Preference(type='shopping', score = 5), shopping=Preference(type='shopping', score = 5),
max_time_minute=100,
max_time_minute=300, detour_tolerance_minute=0
detour_tolerance_minute=15
) )
# Create start and finish # Create start and finish
@ -64,10 +63,7 @@ def test(start_coords: tuple[float, float], finish_coords: tuple[float, float] =
logger.info("Optimized route : ") logger.info("Optimized route : ")
for l in linked_tour : for l in linked_tour :
logger.info(f"{l}") logger.info(f"{l}")
total_time += l.duration logger.info(f"Estimated length of tour : {linked_tour.total_time} mintutes and visiting {len(linked_tour._landmarks)} landmarks.")
total_time += l.time_to_reach_next
logger.info(f"Total time: {total_time}")
# with open('linked_tour.yaml', 'w') as f: # with open('linked_tour.yaml', 'w') as f:
# yaml.dump(linked_tour.asdict(), f) # yaml.dump(linked_tour.asdict(), f)
@ -78,6 +74,6 @@ def test(start_coords: tuple[float, float], finish_coords: tuple[float, float] =
# test(tuple((48.8344400, 2.3220540))) # Café Chez César # test(tuple((48.8344400, 2.3220540))) # Café Chez César
# test(tuple((48.8375946, 2.2949904))) # Point random # test(tuple((48.8375946, 2.2949904))) # Point random
# test(tuple((47.377859, 8.540585))) # Zurich HB # test(tuple((47.377859, 8.540585))) # Zurich HB
test(tuple((45.7576485, 4.8330241))) # Lyon Bellecour # test(tuple((45.758217, 4.831814))) # Lyon Bellecour
# test(tuple((48.5848435, 7.7332974))) # Strasbourg Gare test(tuple((48.5848435, 7.7332974))) # Strasbourg Gare
# test(tuple((48.2067858, 16.3692340))) # Vienne # test(tuple((48.2067858, 16.3692340))) # Vienne

View File

@ -81,19 +81,19 @@ class LandmarkManager:
bbox = self.create_bbox(center_coordinates, reachable_bbox_side) bbox = self.create_bbox(center_coordinates, reachable_bbox_side)
# list for sightseeing # list for sightseeing
if preferences.sightseeing.score != 0: if preferences.sightseeing.score != 0:
score_function = lambda score: int(score*10)*preferences.sightseeing.score/5 # self.count_elements_close_to(loc) + score_function = lambda score: int(score*10*preferences.sightseeing.score/5) # self.count_elements_close_to(loc) +
L1 = self.fetch_landmarks(bbox, self.amenity_selectors['sightseeing'], preferences.sightseeing.type, score_function) L1 = self.fetch_landmarks(bbox, self.amenity_selectors['sightseeing'], preferences.sightseeing.type, score_function)
L += L1 L += L1
# list for nature # list for nature
if preferences.nature.score != 0: if preferences.nature.score != 0:
score_function = lambda score: int(score*10*self.nature_coeff)*preferences.nature.score/5 # self.count_elements_close_to(loc) + score_function = lambda score: int(score*10*self.nature_coeff*preferences.nature.score/5) # self.count_elements_close_to(loc) +
L2 = self.fetch_landmarks(bbox, self.amenity_selectors['nature'], preferences.nature.type, score_function) L2 = self.fetch_landmarks(bbox, self.amenity_selectors['nature'], preferences.nature.type, score_function)
L += L2 L += L2
# list for shopping # list for shopping
if preferences.shopping.score != 0: if preferences.shopping.score != 0:
score_function = lambda score: int(score*10)*preferences.shopping.score/5 # self.count_elements_close_to(loc) + score_function = lambda score: int(score*10*preferences.shopping.score/5) # self.count_elements_close_to(loc) +
L3 = self.fetch_landmarks(bbox, self.amenity_selectors['shopping'], preferences.shopping.type, score_function) L3 = self.fetch_landmarks(bbox, self.amenity_selectors['shopping'], preferences.shopping.type, score_function)
L += L3 L += L3
@ -290,7 +290,10 @@ class LandmarkManager:
elem_type = landmarktype # Add the landmark type as 'sightseeing, elem_type = landmarktype # Add the landmark type as 'sightseeing,
n_tags = len(elem.tags().keys()) # Add number of tags n_tags = len(elem.tags().keys()) # Add number of tags
score = n_tags**self.tag_exponent # Add score score = n_tags**self.tag_exponent # Add score
website_url = None
wikpedia_url = None
image_url = None
name_en = None
# remove specific tags # remove specific tags
skip = False skip = False
@ -304,7 +307,7 @@ class LandmarkManager:
if "wiki" in tag: if "wiki" in tag:
score += self.wikipedia_bonus # wikipedia entries count more score += self.wikipedia_bonus # wikipedia entries count more
# if tag == "wikidata": # if tag == "wikidata":
# Q = elem.tag('wikidata') # Q = elem.tag('wikidata')
# site = Site("wikidata", "wikidata") # site = Site("wikidata", "wikidata")
@ -315,6 +318,7 @@ class LandmarkManager:
if "viewpoint" in tag: if "viewpoint" in tag:
score += self.viewpoint_bonus score += self.viewpoint_bonus
duration = 10
if "image" in tag: if "image" in tag:
score += self.image_bonus score += self.image_bonus
@ -331,26 +335,31 @@ class LandmarkManager:
if tag == "building" and elem.tag('building') in ['retail', 'supermarket', 'parking']: if tag == "building" and elem.tag('building') in ['retail', 'supermarket', 'parking']:
skip = True skip = True
break break
# Get additional information
# if tag == 'wikipedia' :
# wikpedia_url = elem.tag('wikipedia')
if tag in ['website', 'contact:website'] :
website_url = elem.tag(tag)
if tag == 'image' :
image_url = elem.tag('image')
if tag =='name:en' :
name_en = elem.tag('name:en')
if skip: if skip:
continue continue
score = score_function(score) score = score_function(score)
if "place_of_worship" in elem.tags().values() : if "place_of_worship" in elem.tags().values() :
score = int(score*self.church_coeff) score = int(score*self.church_coeff)
duration = 20 duration = 15
elif "museum" in elem.tags().values() : elif "museum" in elem.tags().values() :
score = int(score*self.church_coeff) score = int(score*self.church_coeff)
duration = 60 duration = 60
elif "fountain" in elem.tags().values() :
duration = 5
elif "park" in elem.tags().values() :
duration = 30
else : else :
duration = 15 duration = 5
# Generate the landmark and append it to the list # Generate the landmark and append it to the list
landmark = Landmark( landmark = Landmark(
@ -362,7 +371,11 @@ class LandmarkManager:
attractiveness=score, attractiveness=score,
must_do=False, must_do=False,
n_tags=int(n_tags), n_tags=int(n_tags),
duration = duration duration = duration,
name_en=name_en,
image_url=image_url,
# wikipedia_url=wikpedia_url,
website_url=website_url
) )
return_list.append(landmark) return_list.append(landmark)

View File

@ -21,7 +21,7 @@ class Optimizer:
detour_factor: float # detour factor of straight line vs real distance in cities detour_factor: float # detour factor of straight line vs real distance in cities
average_walking_speed: float # average walking speed of adult average_walking_speed: float # average walking speed of adult
max_landmarks: int # max number of landmarks to visit max_landmarks: int # max number of landmarks to visit
overshoot: float # experimentally determined overshoot possibility to return long enough tours overshoot: float # overshoot to allow maxtime to overflow. Optimizer is a bit restrictive
def __init__(self) : def __init__(self) :
@ -178,7 +178,7 @@ class Optimizer:
Args: Args:
landmarks (list[Landmark]): List of landmarks. landmarks (list[Landmark]): List of landmarks.
max_time (int): Maximum time allowed for tour. max_time (int): Maximum time of visit allowed.
Returns: Returns:
Tuple[list[float], list[float], list[int]]: Objective function coefficients, inequality constraint coefficients, and the right-hand side of the inequality constraint. Tuple[list[float], list[float], list[int]]: Objective function coefficients, inequality constraint coefficients, and the right-hand side of the inequality constraint.
@ -195,7 +195,7 @@ class Optimizer:
for j, spot2 in enumerate(landmarks) : for j, spot2 in enumerate(landmarks) :
t = get_time(spot1.location, spot2.location) + spot1.duration t = get_time(spot1.location, spot2.location) + spot1.duration
dist_table[j] = t dist_table[j] = t
closest = sorted(dist_table)[:22] closest = sorted(dist_table)[:25]
for i, dist in enumerate(dist_table) : for i, dist in enumerate(dist_table) :
if dist not in closest : if dist not in closest :
dist_table[i] = 32700 dist_table[i] = 32700
@ -476,7 +476,7 @@ class Optimizer:
A, b = self.respect_start_finish(L) # Force start and finish positions A, b = self.respect_start_finish(L) # Force start and finish positions
A_eq = np.vstack((A_eq, A), dtype=np.int8) A_eq = np.vstack((A_eq, A), dtype=np.int8)
b_eq += b b_eq += b
A, b = self.respect_order(L) # Respect order of visit (only works when max_steps is limiting factor) A, b = self.respect_order(L) # Respect order of visit (only works when max_time is limiting factor)
A_eq = np.vstack((A_eq, A), dtype=np.int8) A_eq = np.vstack((A_eq, A), dtype=np.int8)
b_eq += b b_eq += b

View File

@ -214,7 +214,7 @@ class Refiner :
if self.is_in_area(area, landmark.location) and landmark.name not in visited_names: if self.is_in_area(area, landmark.location) and landmark.name not in visited_names:
second_order_landmarks.append(landmark) second_order_landmarks.append(landmark)
return take_most_important.take_most_important(second_order_landmarks, len(visited_landmarks)) return take_most_important.take_most_important(second_order_landmarks, int(self.max_landmarks_refiner*0.75))
# Try fix the shortest path using shapely # Try fix the shortest path using shapely