fixed input as coordinates
This commit is contained in:
parent
bcc91c638d
commit
c58c10b057
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
cache/
|
@ -2,77 +2,190 @@ from OSMPythonTools.api import Api
|
||||
from OSMPythonTools.overpass import Overpass, overpassQueryBuilder, Nominatim
|
||||
from dataclasses import dataclass
|
||||
from pydantic import BaseModel
|
||||
import math as m
|
||||
from structs.landmarks import Landmark, LandmarkType
|
||||
from structs.preferences import Preferences, Preference
|
||||
from typing import List
|
||||
from typing import Tuple
|
||||
|
||||
RADIUS = 0.0005 # size of the bbox in degrees. 0.0005 ~ 50m
|
||||
BBOX_SIDE = 10 # size of bbox in km for general area, 10km
|
||||
RADIUS_CLOSE_TO = 50 # size of area in m for close features, 5àm radius
|
||||
MIN_SCORE = 100 # discard elements with score < 100
|
||||
MIN_TAGS = 5 # discard elements withs less than 5 tags
|
||||
|
||||
|
||||
# Defines the landmark class (aka some place there is to visit)
|
||||
@dataclass
|
||||
class Landmarkkkk :
|
||||
name : str
|
||||
attractiveness : int
|
||||
id : int
|
||||
# Include th json here
|
||||
# Create a list of all things to visit given some preferences and a city. Ready for the optimizer
|
||||
def generate_landmarks(coordinates: Tuple[float, float], preferences: Preferences) :
|
||||
|
||||
l_sights = ["'tourism'='museum'", "'tourism'='attraction'", "'tourism'='gallery'", 'historic', "'amenity'='arts_centre'", "'amenity'='planetarium'", "'amenity'='place_of_worship'", "'amenity'='fountain'", '"water"="reflecting_pool"']
|
||||
l_nature = ["'leisure'='park'", 'geological', "'natural'='geyser'", "'natural'='hot_spring'", '"natural"="arch"', '"natural"="cave_entrance"', '"natural"="volcano"', '"natural"="stone"', '"tourism"="alpine_hut"', '"tourism"="picnic_site"', '"tourism"="viewpoint"', '"tourism"="zoo"', '"waterway"="waterfall"']
|
||||
l_shop = ["'shop'='department_store'", "'shop'='mall'"] #, '"shop"="collector"', '"shop"="antiques"']
|
||||
|
||||
# List for sightseeing
|
||||
L1 = get_landmarks(coordinates, l_sights, LandmarkType(landmark_type='sightseeing'))
|
||||
correct_score(L1, preferences.sightseeing)
|
||||
|
||||
class Landmark(BaseModel) :
|
||||
name : str
|
||||
attractiveness : int
|
||||
loc : tuple
|
||||
# List for nature
|
||||
L2 = get_landmarks(coordinates, l_nature, LandmarkType(landmark_type='nature'))
|
||||
correct_score(L2, preferences.nature)
|
||||
|
||||
# List for shopping
|
||||
L3 = get_landmarks(coordinates, l_shop, LandmarkType(landmark_type='shopping'))
|
||||
correct_score(L3, preferences.shopping)
|
||||
|
||||
# Converts a OSM id to a landmark
|
||||
def add_from_id(id: int, score: int) :
|
||||
L = L1 + L2 + L3
|
||||
|
||||
try :
|
||||
s = 'way/' + str(id) # prepare string for query
|
||||
obj = api.query(s) # object to add
|
||||
return cleanup_list(L)
|
||||
|
||||
# Determines if two locations are close to each other
|
||||
def is_close_to(loc1: Tuple[float, float], loc2: Tuple[float, float]) :
|
||||
|
||||
alpha = (180*RADIUS_CLOSE_TO)/(6371000*m.pi)
|
||||
if abs(loc1[0] - loc2[0]) + abs(loc1[1] - loc2[1]) < alpha*2 :
|
||||
return True
|
||||
else :
|
||||
return False
|
||||
|
||||
# Remove duplicate elements and elements with low score
|
||||
def cleanup_list(L: List[Landmark]) :
|
||||
L_clean = []
|
||||
names = []
|
||||
|
||||
for landmark in L :
|
||||
|
||||
if landmark.name in names : # Remove duplicates
|
||||
continue
|
||||
|
||||
elif landmark.attractiveness < MIN_SCORE : # Remove uninteresting
|
||||
continue
|
||||
|
||||
elif landmark.n_tags < MIN_TAGS : # Remove uninteresting 2.0
|
||||
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 preferences and the number of tags
|
||||
def correct_score(L: List[Landmark], preference: Preference) :
|
||||
|
||||
if len(L) == 0 :
|
||||
return
|
||||
|
||||
if L[0].type != preference.type :
|
||||
raise TypeError(f"LandmarkType {preference.type} does not match the type of Landmark {L[0].name}")
|
||||
|
||||
for elem in L :
|
||||
elem.attractiveness = int(elem.attractiveness/100) + elem.n_tags # arbitrary correction of the balance score vs number of tags
|
||||
elem.attractiveness = elem.attractiveness*preference.score # arbitrary computation
|
||||
|
||||
# Correct the score of a list of landmarks by taking into account preferences and the number of tags
|
||||
def correct_score_test(L: List[Landmark], preference: Preference) :
|
||||
|
||||
if len(L) == 0 :
|
||||
return
|
||||
|
||||
if L[0].type != preference.type :
|
||||
raise TypeError(f"LandmarkType {preference.type} does not match the type of Landmark {L[0].name}")
|
||||
|
||||
for elem in L :
|
||||
elem.attractiveness = int(elem.attractiveness/100) + elem.n_tags # arbitrary correction of the balance score vs number of tags
|
||||
elem.attractiveness = elem.attractiveness*preference.score # arbitrary computation
|
||||
|
||||
# Function to count elements within a 25m radius of a location
|
||||
def count_elements_within_radius(coordinates: Tuple[float, float]) -> int:
|
||||
|
||||
lat = coordinates[0]
|
||||
lon = coordinates[1]
|
||||
|
||||
bbox = {'latLower':lat-RADIUS,'lonLower':lon-RADIUS,'latHigher':lat+RADIUS,'lonHigher': lon+RADIUS}
|
||||
overpass = Overpass()
|
||||
|
||||
# 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 :
|
||||
radius_result = overpass.query(radius_query)
|
||||
|
||||
# The count is the number of elements found
|
||||
return radius_result.countElements()
|
||||
|
||||
except :
|
||||
s = 'relation/' + str(id) # prepare string for query
|
||||
obj = api.query(s) # object to add
|
||||
return None
|
||||
|
||||
return Landmarkkkk(obj.tag('name:fr'), score, id) # create Landmark out of it
|
||||
# Creates a bounding box around precise coordinates
|
||||
def create_bbox(coordinates: Tuple[float, float], side_length: int) -> Tuple[float, float, float, float]:
|
||||
"""
|
||||
Create a simple bounding box around given coordinates.
|
||||
:param coordinates: tuple (lat, lon)
|
||||
-> lat: Latitude of the center point.
|
||||
-> lon: Longitude of the center point.
|
||||
:param side_length: int - side length of the bbox in km
|
||||
:return: Bounding box as (min_lat, min_lon, max_lat, max_lon).
|
||||
"""
|
||||
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
|
||||
|
||||
# Generates the list of landmarks for a given Landmarktype. Needs coordinates, a list of amenities and the corresponding LandmarkType
|
||||
def get_landmarks(coordinates: Tuple[float, float], l: List[Landmark], landmarktype: LandmarkType):
|
||||
|
||||
def get_sights(city_country: str):
|
||||
nominatim = Nominatim()
|
||||
areaId = nominatim.query(city_country).areaId()
|
||||
overpass = Overpass()
|
||||
|
||||
# list of stuff we want to define as sights
|
||||
l = ["'tourism'='museum'", "'tourism'='attraction'", "'tourism'='gallery'", 'historic', "'amenity'='arts_centre'", "'amenity'='planetarium'", '"amenity"="place_of_worship"']
|
||||
score = 0
|
||||
# Generate a bbox around currunt coordinates
|
||||
bbox = create_bbox(coordinates, BBOX_SIDE)
|
||||
|
||||
# Initialize some variables
|
||||
N = 0
|
||||
L = []
|
||||
|
||||
for amenity in l :
|
||||
query = overpassQueryBuilder(area=areaId, elementType=['way', 'relation'], selector=amenity, includeGeometry=True)
|
||||
query = overpassQueryBuilder(bbox=bbox, elementType=['way', 'relation'], selector=amenity, includeCenter=True, out='body')
|
||||
result = overpass.query(query)
|
||||
score += result.countElements()
|
||||
N += result.countElements()
|
||||
|
||||
return score
|
||||
for elem in result.elements():
|
||||
|
||||
name = elem.tag('name') # Add name
|
||||
location = (elem.centerLat(), elem.centerLon()) # Add coordinates (lat, lon)
|
||||
|
||||
# take a lsit of tuples (id, score) to generate a list of landmarks
|
||||
def generate_landmarks(ids_and_scores: list) :
|
||||
# skip if unprecise location
|
||||
if name is None or location[0] is None:
|
||||
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
|
||||
score = count_elements_within_radius(location)
|
||||
|
||||
L = []
|
||||
for tup in ids_and_scores :
|
||||
L.append(add_from_id(tup[0], tup[1]))
|
||||
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
|
||||
"""
|
||||
api = Api()
|
||||
|
||||
|
||||
l = (7515426, 70)
|
||||
t = (5013364, 100)
|
||||
n = (201611261, 99)
|
||||
a = (226413508, 50)
|
||||
m = (23762981, 30)
|
||||
|
||||
|
||||
ids_and_scores = [t, l, n, a, m]
|
||||
|
||||
landmarks = generate_landmarks(ids_and_scores)
|
||||
|
||||
|
||||
for obj in landmarks :
|
||||
print(obj)"""
|
||||
|
||||
|
||||
|
||||
print(get_sights('Paris, France'))
|
@ -1,7 +1,9 @@
|
||||
from optimizer import solve_optimization
|
||||
from landmarks_manager import generate_landmarks
|
||||
from structs.landmarks import LandmarkTest
|
||||
from structs.landmarks import Landmark
|
||||
from structs.preferences import Preferences
|
||||
from structs.landmarktype import LandmarkType
|
||||
from structs.preferences import Preferences, Preference
|
||||
from fastapi import FastAPI, Query, Body
|
||||
from typing import List
|
||||
|
||||
@ -12,13 +14,22 @@ app = FastAPI()
|
||||
#"http://127.0.0.1:8000/process?param1={param1}¶m2={param2}"
|
||||
# This should become main at some point
|
||||
@app.post("/optimizer/{longitude}/{latitude}")
|
||||
def main(longitude: float, latitude: float, prefrences: Preferences = Body(...)) -> List[Landmark]:
|
||||
def main(longitude: float, latitude: float, preferences: Preferences = Body(...)) -> List[Landmark]:
|
||||
# From frontend get longitude, latitude and prefence list
|
||||
|
||||
# Generate the landmark list
|
||||
landmarks = generate_landmarks(tuple((longitude, latitude)), preferences)
|
||||
|
||||
# Set the max distance
|
||||
max_steps = 90
|
||||
|
||||
# Compute the visiting order
|
||||
visiting_order = solve_optimization(landmarks, max_steps, True)
|
||||
|
||||
return visiting_order
|
||||
|
||||
landmarks = []
|
||||
|
||||
|
||||
return landmarks
|
||||
|
||||
@app.get("test")
|
||||
def test():
|
||||
@ -46,6 +57,16 @@ def test():
|
||||
#return("max steps :", max_steps, "\n", visiting_order)
|
||||
|
||||
|
||||
"""# keep this for debug
|
||||
if __name__ == "__main__":
|
||||
main()"""
|
||||
# input city, country in the form of 'Paris, France'
|
||||
@app.post("/test2/{city_country}")
|
||||
def test2(city_country: str, preferences: Preferences = Body(...)) -> List[Landmark]:
|
||||
|
||||
landmarks = generate_landmarks(city_country, preferences)
|
||||
|
||||
max_steps = 9000000
|
||||
|
||||
visiting_order = solve_optimization(landmarks, max_steps, True)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -212,7 +212,7 @@ def init_ub_dist(landmarks: list, max_steps: int):
|
||||
dist_table = [0]*len(landmarks)
|
||||
c.append(-spot1.attractiveness)
|
||||
for j, spot2 in enumerate(landmarks) :
|
||||
dist_table[j] = manhattan_distance(spot1.loc, spot2.loc)
|
||||
dist_table[j] = manhattan_distance(spot1.location, spot2.location)
|
||||
A.append(dist_table)
|
||||
c = c*len(landmarks)
|
||||
A_ub = []
|
||||
@ -238,7 +238,7 @@ def respect_user_mustsee(landmarks: list, A_eq: list, b_eq: list) :
|
||||
for k in range(L-1) :
|
||||
l[k*L+L-1] = 1
|
||||
|
||||
H += manhattan_distance(elem.loc, elem_prev.loc)
|
||||
H += manhattan_distance(elem.location, elem_prev.location)
|
||||
elem_prev = elem
|
||||
|
||||
"""for i in range(7):
|
||||
|
@ -1,4 +1,5 @@
|
||||
from pydantic import BaseModel
|
||||
from OSMPythonTools.api import Api
|
||||
from .landmarktype import LandmarkType
|
||||
from .preferences import Preferences
|
||||
|
||||
@ -12,19 +13,10 @@ class Landmark(BaseModel) :
|
||||
name : str
|
||||
type: LandmarkType # De facto mapping depending on how the query was executed with overpass. Should still EXACTLY correspond to the preferences
|
||||
location : tuple
|
||||
|
||||
# loop through the preferences and assign a score to the landmark
|
||||
def score(self, preferences: Preferences):
|
||||
|
||||
for preference_name, preference in preferences.__dict__.items():
|
||||
|
||||
if (preference_name == self.type.landmark_type) :
|
||||
score = preference.score
|
||||
|
||||
if (not score) :
|
||||
raise Exception(f"Could not determine score for landmark {self.name}")
|
||||
|
||||
else :
|
||||
return score
|
||||
osm_type : str
|
||||
osm_id : int
|
||||
attractiveness : int
|
||||
must_do : bool
|
||||
n_tags : int
|
||||
|
||||
|
||||
|
@ -3,8 +3,8 @@ from .landmarktype import LandmarkType
|
||||
|
||||
class Preference(BaseModel) :
|
||||
name: str
|
||||
type: LandmarkType
|
||||
score: int
|
||||
type: LandmarkType # should match the attributes of the Preferences class
|
||||
score: int # score could be from 1 to 5
|
||||
|
||||
# Input for optimization
|
||||
class Preferences(BaseModel) :
|
||||
@ -17,11 +17,11 @@ class Preferences(BaseModel) :
|
||||
# Shopping (diriger plutôt vers des zones / rues commerçantes)
|
||||
shopping : Preference
|
||||
|
||||
# Food (price low or high. Combien on veut dépenser pour manger à midi/soir)
|
||||
""" # Food (price low or high. Combien on veut dépenser pour manger à midi/soir)
|
||||
food_budget : Preference
|
||||
|
||||
# Tolérance au détour (ce qui détermine (+ ou -) le chemin emprunté)
|
||||
detour_tol : Preference
|
||||
detour_tol : Preference"""
|
||||
|
||||
|
||||
|
||||
|
67
backend/src/tester.py
Normal file
67
backend/src/tester.py
Normal file
@ -0,0 +1,67 @@
|
||||
from optimizer import solve_optimization
|
||||
from landmarks_manager import generate_landmarks
|
||||
from structs.landmarks import LandmarkTest
|
||||
from structs.landmarks import Landmark
|
||||
from structs.landmarktype import LandmarkType
|
||||
from structs.preferences import Preferences, Preference
|
||||
from fastapi import FastAPI, Query, Body
|
||||
from typing import List
|
||||
|
||||
|
||||
def test3(city_country: str) -> List[Landmark]:
|
||||
|
||||
|
||||
preferences = Preferences(
|
||||
sightseeing=Preference(
|
||||
name='sightseeing',
|
||||
type=LandmarkType(landmark_type='sightseeing'),
|
||||
score = 5),
|
||||
nature=Preference(
|
||||
name='nature',
|
||||
type=LandmarkType(landmark_type='nature'),
|
||||
score = 0),
|
||||
shopping=Preference(
|
||||
name='shopping',
|
||||
type=LandmarkType(landmark_type='shopping'),
|
||||
score = 5))
|
||||
|
||||
landmarks = generate_landmarks(city_country, preferences)
|
||||
|
||||
max_steps = 9
|
||||
|
||||
visiting_order = solve_optimization(landmarks, max_steps, True)
|
||||
|
||||
print(len(visiting_order))
|
||||
|
||||
return len(visiting_order)
|
||||
|
||||
|
||||
def test4(coordinates: tuple[float, float]) -> List[Landmark]:
|
||||
|
||||
|
||||
preferences = Preferences(
|
||||
sightseeing=Preference(
|
||||
name='sightseeing',
|
||||
type=LandmarkType(landmark_type='sightseeing'),
|
||||
score = 5),
|
||||
nature=Preference(
|
||||
name='nature',
|
||||
type=LandmarkType(landmark_type='nature'),
|
||||
score = 0),
|
||||
shopping=Preference(
|
||||
name='shopping',
|
||||
type=LandmarkType(landmark_type='shopping'),
|
||||
score = 5))
|
||||
|
||||
landmarks = generate_landmarks(coordinates, preferences)
|
||||
|
||||
max_steps = 90
|
||||
|
||||
visiting_order = solve_optimization(landmarks, max_steps, True)
|
||||
|
||||
print(len(visiting_order))
|
||||
|
||||
return len(visiting_order)
|
||||
|
||||
|
||||
test3(tuple((48.834378, 2.322113)))
|
Loading…
x
Reference in New Issue
Block a user