"""Definition of the Landmark class to handle visitable objects across the world.""" from typing import Optional, Literal, List from uuid import uuid4, UUID from pydantic import BaseModel, ConfigDict, Field # Output to frontend class Landmark(BaseModel) : """ A class representing a landmark or point of interest (POI) in the context of a trip. The Landmark class is used to model visitable locations, such as tourist attractions, natural sites, shopping locations, and start/end points in travel itineraries. It holds information about the landmark's attributes and supports comparisons and calculations, such as distance between landmarks. Attributes: name (str): The name of the landmark. type (Literal): The type of the landmark, which can be one of ['sightseeing', 'nature', 'shopping', 'start', 'finish']. location (tuple): A tuple representing the (latitude, longitude) of the landmark. osm_type (str): The OpenStreetMap (OSM) type of the landmark. osm_id (int): The OpenStreetMap (OSM) ID of the landmark. attractiveness (int): A score representing the attractiveness of the landmark. n_tags (int): The number of tags associated with the landmark. image_url (Optional[str]): A URL to an image of the landmark. website_url (Optional[str]): A URL to the landmark's official website. description (Optional[str]): A text description of the landmark. duration (Optional[int]): The estimated time to visit the landmark (in minutes). name_en (Optional[str]): The English name of the landmark. uuid (UUID): A unique identifier for the landmark, generated by default using uuid4. must_do (Optional[bool]): Whether the landmark is a "must-do" attraction. must_avoid (Optional[bool]): Whether the landmark should be avoided. is_secondary (Optional[bool]): Whether the landmark is secondary or less important. time_to_reach_next (Optional[int]): Estimated time (in minutes) to reach the next landmark. next_uuid (Optional[UUID]): UUID of the next landmark in sequence (if applicable). """ # Properties of the landmark name : str type: Literal['sightseeing', 'nature', 'shopping', 'start', 'finish'] location : tuple osm_type : str osm_id : int attractiveness : int n_tags : int # Optional properties to gather more information. image_url : Optional[str] = None website_url : Optional[str] = None wiki_url : Optional[str] = None description : Optional[str] = None duration : Optional[int] = 5 name_en : Optional[str] = None # Unique ID of a given landmark uuid: UUID = Field(default_factory=uuid4) # Additional properties depending on specific tour must_do : Optional[bool] = False must_avoid : Optional[bool] = False is_secondary : Optional[bool] = False time_to_reach_next : Optional[int] = 0 next_uuid : Optional[UUID] = None # More properties to define the score is_viewpoint : Optional[bool] = False is_place_of_worship : Optional[bool] = False class Config: json_encoders = { UUID: lambda v: str(v) # Ensure UUID is serialized as a string } def __str__(self) -> str: """ String representation of the Landmark object. Returns: str: A formatted string with the landmark's type, name, location, attractiveness score, time to the next landmark (if available), and whether the landmark is secondary. """ t_to_next_str = f", time_to_next={self.time_to_reach_next}" if self.time_to_reach_next else "" is_secondary_str = ", secondary" if self.is_secondary else "" type_str = '(' + self.type + ')' return (f'Landmark{type_str}: [{self.name} @{self.location}, ' f'score={self.attractiveness}{t_to_next_str}{is_secondary_str}]') def distance(self, value: 'Landmark') -> float: """ Calculates the squared distance between this landmark and another. Args: value (Landmark): Another Landmark object to calculate the distance to. Returns: float: The squared Euclidean distance between the two landmarks. """ return (self.location[0] - value.location[0])**2 + (self.location[1] - value.location[1])**2 def __hash__(self) -> int: """ Generates a hash for the Landmark based on its name. Returns: int: The hash of the landmark. """ return hash(self.name) def __eq__(self, value: 'Landmark') -> bool: """ Checks equality between two Landmark objects based on UUID, OSM ID, and name. Args: value (Landmark): Another Landmark object to compare. Returns: bool: True if the landmarks are equal, False otherwise. """ # eq and hash must be consistent # in particular, if two objects are equal, their hash must be equal # uuid and osm_id are just shortcuts to avoid comparing all the properties # if they are equal, we know that the name is also equal and in turn the hash is equal return (self.uuid == value.uuid or self.osm_id == value.osm_id or (self.name == value.name and self.distance(value) < 0.001)) class Toilets(BaseModel) : """ Model for toilets. When false/empty the information is either false either not known. """ location : tuple wheelchair : Optional[bool] = False changing_table : Optional[bool] = False fee : Optional[bool] = False opening_hours : Optional[str] = "" def __str__(self) -> str: """ String representation of the Toilets object. Returns: str: A formatted string with the toilets location. """ return f'Toilets @{self.location}' model_config = ConfigDict(from_attributes=True)