127 lines
4.5 KiB
Python

"""Simple reader that reads OSMmap data from overpass API"""
from typing import List,Optional,Literal
import random
import string
import warnings
from llama_index.readers.base import BaseReader
from llama_index.readers.schema.base import Document
warnings.filterwarnings('ignore')
class OpenMap(BaseReader):
"""OpenMap Reader.
Get the map Features from the overpass api(osm) for the given location/area
Args:
localarea(str) - Area or location you are seaching for
tag_values(str) - filter for the give area
search_tag(str) - Tag that you are looking for
if you not sure about the search_tag and tag_values visit https://taginfo.openstreetmap.org/tags
remove_keys(list) - list of keys that need to be removed from the response
by default following keys will be removed ['nodes','geometry','members']
"""
def __init__(self) -> None:
"""Initialize with parameters."""
super().__init__()
@staticmethod
def _get_user()->str:
# choose from all lowercase letter
letters = string.ascii_lowercase
result_str = ''.join(random.choice(letters) for i in range(10))
return result_str
@staticmethod
def _get_latlon(locarea:str,user_agent:str)->tuple:
try:
from geopy.geocoders import Nominatim
except:
raise ImportError('install geopy using `pip3 install geopy`')
geolocator = Nominatim(user_agent=user_agent)
location = geolocator.geocode(locarea)
return(location.latitude,location.longitude) if location else (None, None)
def load_data(
self,
localarea:str,
search_tag:Optional[str] = 'amenity',
remove_keys : Optional [List]= ['nodes','geometry','members'],
tag_only:Optional[bool]=True ,
tag_values:Optional[List]=[''],
local_area_buffer : Optional[int] = 2000,
) -> List[Document]:
"""
This loader will bring you the all the node values from the open street maps for the given location
Args:
localarea(str) - Area or location you are seaching for
search_tag(str) - Tag that you are looking for
if you not sure about the search_tag and tag_values visit https://taginfo.openstreetmap.org/tags
remove_keys(list) - list of keys that need to be removed from the response
by default it those keys will be removed ['nodes','geometry','members']
tag_only(bool) - if True it return the nodes which has tags if False returns all the nodes
tag_values(str) - filter for the give area
local_area_buffer(int) - range that you wish to cover (Default 2000(2km))
"""
try:
from osmxtract import overpass, location
from osmxtract.errors import OverpassBadRequest
except:
raise ImportError('install osmxtract using `pip3 install osmxtract`')
null_list=['','null','none',None]
extra_info={}
local_area=localarea
if (local_area.lower().strip() in null_list):
raise Exception('The Area should not be null')
user=self._get_user()
lat, lon = self._get_latlon(local_area,user)
try:
bounds = location.from_buffer(lat, lon, buffer_size=int(local_area_buffer))
except TypeError:
raise TypeError('Please give valid location name or check for spelling')
#overpass query generation and execution
tag_values=[str(i).lower().strip() for i in tag_values ]
query = overpass.ql_query(bounds, tag=search_tag.lower(), values=tag_values,timeout=500)
extra_info['overpass_query']=query
try:
response = overpass.request(query)
except OverpassBadRequest :
raise TypeError(f'Error while executing the Query {query} please check the Args')
res=response['elements']
_meta=response.copy()
del _meta['elements']
extra_info['overpass_meta']=str(_meta)
extra_info['lat']=lat
extra_info['lon']=lon
# filtering for only the tag values
filtered=[i for i in res if 'tags' in i.keys()] if tag_only else res
for key in remove_keys:
extras_poped =[i.pop(key,None) for i in filtered ]
if filtered:
return Document(str(filtered),extra_info=extra_info)
else:
return Document(str(res),extra_info=extra_info)