overpass as class

This commit is contained in:
Helldragon67 2025-01-23 16:02:33 +01:00
parent 1cc935fb34
commit 577ee232fc
5 changed files with 134 additions and 126 deletions

File diff suppressed because one or more lines are too long

@ -5,13 +5,27 @@ import logging
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from .caching_strategy import get_cache_key, CachingStrategy from .caching_strategy import get_cache_key, CachingStrategy
from ..constants import OSM_CACHE_DIR
logger = logging.getLogger('Overpass')
osm_types = List[Literal['way', 'node', 'relation']]
logger = logging.getLogger('overpass') class Overpass :
ElementTypes = List[Literal['way', 'node', 'relation']] """
Overpass class to manage the query building and sending to overpass api.
The caching strategy is a part of this class and initialized upon creation of the Overpass object.
"""
def __init__(self, caching_strategy: str = 'XML', cache_dir: str = OSM_CACHE_DIR) :
"""
Initialize the Overpass instance with the url, headers and caching strategy.
"""
self.overpass_url = "https://overpass-api.de/api/interpreter"
self.headers = {'User-Agent': 'Mozilla/5.0 (compatible; OverpassQuery/1.0; +http://example.com)',}
self.caching_strategy = CachingStrategy.use(caching_strategy, cache_dir=cache_dir)
def build_query(area: tuple, element_types: ElementTypes, def build_query(self, area: tuple, osm_types: osm_types,
selector: str, conditions=[], out='center'): selector: str, conditions=[], out='center'):
""" """
Constructs a query string for the Overpass API to retrieve OpenStreetMap (OSM) data. Constructs a query string for the Overpass API to retrieve OpenStreetMap (OSM) data.
@ -21,7 +35,7 @@ def build_query(area: tuple, element_types: ElementTypes,
(radius, latitude, longitude). The first element is a string like "around:2000" (radius, latitude, longitude). The first element is a string like "around:2000"
specifying the search radius, and the second and third elements represent specifying the search radius, and the second and third elements represent
the latitude and longitude as floats or strings. the latitude and longitude as floats or strings.
element_types (list[str]): A list of OSM element types to search for. Must be one or more of osm_types (list[str]): A list of OSM element types to search for. Must be one or more of
'Way', 'Node', or 'Relation'. 'Way', 'Node', or 'Relation'.
selector (str): The key or tag to filter the OSM elements (e.g., 'amenity', 'highway', etc.). selector (str): The key or tag to filter the OSM elements (e.g., 'amenity', 'highway', etc.).
conditions (list, optional): A list of conditions to apply as additional filters for the conditions (list, optional): A list of conditions to apply as additional filters for the
@ -41,8 +55,8 @@ def build_query(area: tuple, element_types: ElementTypes,
""" """
if not isinstance(conditions, list) : if not isinstance(conditions, list) :
conditions = [conditions] conditions = [conditions]
if not isinstance(element_types, list) : if not isinstance(osm_types, list) :
element_types = [element_types] osm_types = [osm_types]
query = '(' query = '('
@ -61,7 +75,7 @@ def build_query(area: tuple, element_types: ElementTypes,
else : else :
conditions = '' conditions = ''
for elem in element_types : for elem in osm_types :
query += elem + '[' + selector + ']' + conditions + search_area + ';' query += elem + '[' + selector + ']' + conditions + search_area + ';'
query += ');' + f'out {out};' query += ');' + f'out {out};'
@ -69,7 +83,7 @@ def build_query(area: tuple, element_types: ElementTypes,
return query return query
def send_query(query: str) -> dict: def send_query(self, query: str) -> dict:
""" """
Sends the Overpass QL query to the Overpass API and returns the parsed JSON response. Sends the Overpass QL query to the Overpass API and returns the parsed JSON response.
@ -84,25 +98,17 @@ def send_query(query: str) -> dict:
cache_key = get_cache_key(query) cache_key = get_cache_key(query)
# Try to fetch the result from the cache # Try to fetch the result from the cache
cached_response = CachingStrategy.get(cache_key) cached_response = self.caching_strategy.get(cache_key)
if cached_response is not None : if cached_response is not None :
logger.debug("Cache hit.") logger.debug("Cache hit.")
return cached_response return cached_response
# Define the Overpass API endpoint
overpass_url = "https://overpass-api.de/api/interpreter"
# Prepare the data to be sent as POST request, encoded as bytes # Prepare the data to be sent as POST request, encoded as bytes
data = urllib.parse.urlencode({'data': query}).encode('utf-8') data = urllib.parse.urlencode({'data': query}).encode('utf-8')
# Create a custom header with a User-Agent
headers = {
'User-Agent': 'Mozilla/5.0 (compatible; OverpassQuery/1.0; +http://example.com)',
}
try: try:
# Create a Request object with the specified URL, data, and headers # Create a Request object with the specified URL, data, and headers
request = urllib.request.Request(overpass_url, data=data, headers=headers) request = urllib.request.Request(self.overpass_url, data=data, headers=self.headers)
# Send the request and read the response # Send the request and read the response
with urllib.request.urlopen(request) as response: with urllib.request.urlopen(request) as response:
@ -111,7 +117,7 @@ def send_query(query: str) -> dict:
root = ET.fromstring(response_data) root = ET.fromstring(response_data)
# Cache the response data as an ElementTree root # Cache the response data as an ElementTree root
CachingStrategy.set(cache_key, root) self.caching_strategy.set(cache_key, root)
logger.debug("Response data added to cache.") logger.debug("Response data added to cache.")
return root return root

@ -6,15 +6,14 @@ import numpy as np
from sklearn.cluster import DBSCAN from sklearn.cluster import DBSCAN
from pydantic import BaseModel from pydantic import BaseModel
from ..overpass.overpass import build_query, send_query from ..overpass.overpass import Overpass
from ..overpass.caching_strategy import CachingStrategy
from ..structs.landmark import Landmark from ..structs.landmark import Landmark
from .get_time_distance import get_distance from .get_time_distance import get_distance
from ..constants import OSM_CACHE_DIR from ..constants import OSM_CACHE_DIR
# silence the overpass logger # silence the overpass logger
logging.getLogger('overpass').setLevel(level=logging.CRITICAL) logging.getLogger('Overpass').setLevel(level=logging.CRITICAL)
class Cluster(BaseModel): class Cluster(BaseModel):
@ -79,7 +78,9 @@ class ClusterManager:
Args: Args:
bbox: The bounding box coordinates (around:radius, center_lat, center_lon). bbox: The bounding box coordinates (around:radius, center_lat, center_lon).
""" """
CachingStrategy.use('XML', cache_dir=OSM_CACHE_DIR) # Setup the caching in the Overpass class.
self.overpass = Overpass(caching_strategy='XML', cache_dir=OSM_CACHE_DIR)
self.cluster_type = cluster_type self.cluster_type = cluster_type
if cluster_type == 'shopping' : if cluster_type == 'shopping' :
@ -94,16 +95,16 @@ class ClusterManager:
raise NotImplementedError("Please choose only an available option for cluster detection") raise NotImplementedError("Please choose only an available option for cluster detection")
# Initialize the points for cluster detection # Initialize the points for cluster detection
query = build_query( query = self.overpass.build_query(
area = bbox, area = bbox,
element_types = osm_types, osm_types = osm_types,
selector = sel, selector = sel,
out = out out = out
) )
self.logger.debug(f"Cluster query: {query}") self.logger.debug(f"Cluster query: {query}")
try: try:
result = send_query(query) result = self.overpass.send_query(query)
except Exception as e: except Exception as e:
self.logger.error(f"Error fetching landmarks: {e}") self.logger.error(f"Error fetching landmarks: {e}")
@ -243,15 +244,15 @@ class ClusterManager:
osm_types = ['node', 'way', 'relation'] osm_types = ['node', 'way', 'relation']
for sel in selectors : for sel in selectors :
query = build_query( query = self.overpass.build_query(
area = bbox, area = bbox,
element_types = osm_types, osm_types = osm_types,
selector = sel, selector = sel,
out = 'ids center' out = 'ids center'
) )
try: try:
result = send_query(query) result = self.overpass.send_query(query)
except Exception as e: except Exception as e:
self.logger.error(f"Error fetching landmarks: {e}") self.logger.error(f"Error fetching landmarks: {e}")
continue continue

@ -8,13 +8,12 @@ from ..structs.preferences import Preferences
from ..structs.landmark import Landmark from ..structs.landmark import Landmark
from .take_most_important import take_most_important from .take_most_important import take_most_important
from .cluster_manager import ClusterManager from .cluster_manager import ClusterManager
from ..overpass.overpass import build_query, send_query from ..overpass.overpass import Overpass
from ..overpass.caching_strategy import CachingStrategy
from ..constants import AMENITY_SELECTORS_PATH, LANDMARK_PARAMETERS_PATH, OPTIMIZER_PARAMETERS_PATH, OSM_CACHE_DIR from ..constants import AMENITY_SELECTORS_PATH, LANDMARK_PARAMETERS_PATH, OPTIMIZER_PARAMETERS_PATH, OSM_CACHE_DIR
# silence the overpass logger # silence the overpass logger
logging.getLogger('overpass').setLevel(level=logging.CRITICAL) logging.getLogger('Overpass').setLevel(level=logging.CRITICAL)
class LandmarkManager: class LandmarkManager:
@ -56,7 +55,8 @@ class LandmarkManager:
self.walking_speed = parameters['average_walking_speed'] self.walking_speed = parameters['average_walking_speed']
self.detour_factor = parameters['detour_factor'] self.detour_factor = parameters['detour_factor']
CachingStrategy.use('XML', cache_dir=OSM_CACHE_DIR) # Setup the caching in the Overpass class.
self.overpass = Overpass(caching_strategy='XML', cache_dir=OSM_CACHE_DIR)
self.logger.info('LandmakManager successfully initialized.') self.logger.info('LandmakManager successfully initialized.')
@ -189,15 +189,15 @@ class LandmarkManager:
for sel in dict_to_selector_list(amenity_selector): for sel in dict_to_selector_list(amenity_selector):
# self.logger.debug(f"Current selector: {sel}") # self.logger.debug(f"Current selector: {sel}")
element_types = ['way', 'relation'] osm_types = ['way', 'relation']
if 'viewpoint' in sel : if 'viewpoint' in sel :
query_conditions = [] query_conditions = []
element_types.append('node') osm_types.append('node')
query = build_query( query = self.overpass.build_query(
area = bbox, area = bbox,
element_types = element_types, osm_types = osm_types,
selector = sel, selector = sel,
conditions = query_conditions, # except for nature.... conditions = query_conditions, # except for nature....
out = 'center' out = 'center'
@ -205,7 +205,7 @@ class LandmarkManager:
self.logger.debug(f"Query: {query}") self.logger.debug(f"Query: {query}")
try: try:
result = send_query(query) result = self.overpass.send_query(query)
except Exception as e: except Exception as e:
self.logger.error(f"Error fetching landmarks: {e}") self.logger.error(f"Error fetching landmarks: {e}")
continue continue

@ -2,14 +2,13 @@
import logging import logging
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from ..overpass.overpass import build_query, send_query from ..overpass.overpass import Overpass
from ..overpass.caching_strategy import CachingStrategy
from ..structs.landmark import Toilets from ..structs.landmark import Toilets
from ..constants import OSM_CACHE_DIR from ..constants import OSM_CACHE_DIR
# silence the overpass logger # silence the overpass logger
logging.getLogger('overpass').setLevel(level=logging.CRITICAL) logging.getLogger('Overpass').setLevel(level=logging.CRITICAL)
class ToiletsManager: class ToiletsManager:
""" """
@ -40,7 +39,9 @@ class ToiletsManager:
self.radius = radius self.radius = radius
self.location = location self.location = location
CachingStrategy.use('XML', cache_dir=OSM_CACHE_DIR)
# Setup the caching in the Overpass class.
self.overpass = Overpass(caching_strategy='XML', cache_dir=OSM_CACHE_DIR)
def generate_toilet_list(self) -> list[Toilets] : def generate_toilet_list(self) -> list[Toilets] :
@ -56,16 +57,16 @@ class ToiletsManager:
osm_types = ['node', 'way', 'relation'] osm_types = ['node', 'way', 'relation']
toilets_list = [] toilets_list = []
query = build_query( query = self.overpass.build_query(
area = bbox, area = bbox,
element_types = osm_types, osm_types = osm_types,
selector = '"amenity"="toilets"', selector = '"amenity"="toilets"',
out = 'ids center tags' out = 'ids center tags'
) )
self.logger.debug(f"Query: {query}") self.logger.debug(f"Query: {query}")
try: try:
result = send_query(query) result = self.overpass.send_query(query)
except Exception as e: except Exception as e:
self.logger.error(f"Error fetching landmarks: {e}") self.logger.error(f"Error fetching landmarks: {e}")
return None return None