Merge branch 'feature/adding-timed-visits'
This commit is contained in:
commit
f71aab22dc
@ -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
|
||||||
|
@ -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}]'
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user