__all__ = ["OsmResult", "OsmQuery", "OsmQueryDetail", "OsmReverse"]
from typing import List, Optional
from geocoder.base import MultipleResultsQuery, OneResult
from geocoder.location import Location
[docs]class OsmResult(OneResult):
def __init__(self, json_content):
# create safe shortcuts
self._address = json_content.get("address", {})
# proceed with super.__init__
super(OsmResult, self).__init__(json_content)
# ============================ #
# Geometry - Points & Polygons #
# ============================ #
@property
def lat(self) -> Optional[float]:
lat = self.object_raw_json.get("lat")
return float(lat) if lat else None
@property
def lng(self) -> Optional[float]:
lng = self.object_raw_json.get("lon")
return float(lng) if lng else None
@property
def bbox(self) -> List[float]:
"""Output answer as GeoJSON bbox if it can be calculated/retrieved."""
_boundingbox = self.object_raw_json.get("boundingbox")
if _boundingbox:
south = _boundingbox[0]
west = _boundingbox[2]
north = _boundingbox[1]
east = _boundingbox[3]
return [float(west), float(south), float(east), float(north)]
return []
# ========================== #
# Tags for individual houses #
# ========================== #
@property
def address(self) -> Optional[str]:
"""Full comma-separated address"""
return self.object_raw_json.get("display_name")
@property
def house_number(self) -> Optional[str]:
return self._address.get("house_number")
@property
def street(self) -> Optional[str]:
return self._address.get("road")
@property
def postal(self) -> Optional[str]:
return self._address.get("postcode")
# ============================ #
# Populated settlements, urban #
# ============================ #
@property
def neighborhood(self) -> Optional[str]:
"""place=neighborhood
A named part of a place=village, a place=town or a place=city. Smaller
than place=suburb and place=quarter.
The tag can be used for any kind of landuse or mix of landuse (such as
residential, commercial, industrial etc). Usage of this term depends
greatly on local history, culture, politics, economy and organization
of settlements. More specific rules are intentionally avoided.
Note: the British English spelling is used rather than the
American English spelling of neighborhood.
"""
return self._address.get("neighbourhood")
@property
def suburb(self) -> Optional[str]:
"""place=suburb
A distinct section of an urban settlement (city, town, etc.) with its
own name and identity. e.g.
- annexed towns or villages which were formerly independent,
- independent (or dependent) municipalities within a city or next to a
much bigger town
- historical districts of settlements
- industrial districts or recreation areas within a settlements with
specific names.
"""
return self._address.get("suburb")
@property
def quarter(self) -> Optional[str]:
"""place=quarter
A named part of a bigger settlement where this part is smaller than
a suburb and bigger than a neighbourhood. This does not have to be
an administrative entity.
The term quarter is sometimes used synonymously for neighbourhood.
"""
return self._address.get("quarter")
# ====================================== #
# Populated settlements, urban and rural #
# ====================================== #
@property
def allotments(self) -> Optional[str]:
"""place=allotments
Dacha or cottage settlement, which is located outside other
inhabited locality. This value is used mainly in Russia and other
countries of the former Soviet Union, where a lot of such unofficial
settlements exist
"""
return self._address.get("hamlet")
@property
def farm(self) -> Optional[str]:
"""place=farm
A farm that has its own name. If the farm is not a part of bigger
settlement use place=isolated_dwelling. See also landuse=farmyard
"""
return self._address.get("hamlet")
@property
def locality(self) -> Optional[str]:
"""place=isolated_dwelling
For an unpopulated named place.
"""
return self._address.get("locality")
@property
def isolated_dwelling(self) -> Optional[str]:
"""place=isolated_dwelling
Smallest kind of human settlement. No more than 2 households.
"""
return self._address.get("hamlet")
@property
def hamlet(self) -> Optional[str]:
"""place=hamlet
A smaller rural community typically with less than 100-200 inhabitants,
few infrastructure.
"""
return self._address.get("hamlet")
@property
def village(self) -> Optional[str]:
"""place=village
A smaller distinct settlement, smaller than a town with few facilities
available with people traveling to nearby towns to access these.
Populations of villages vary widely in different territories but will
nearly always be less than 10,000 people, often a lot less.
See place=neighbourhood on how to tag divisions within a larger village
"""
return self._address.get("village")
@property
def town(self) -> Optional[str]:
"""place=town
A second tier urban settlement of local importance, often with a
population of 10,000 people and good range of local facilities
including schools, medical facilities etc and traditionally a market.
In areas of low population, towns may have significantly
lower populations.
See place=neighbourhood and possibly also place=suburb on how to tag
divisions within a town.
"""
return self._address.get("town")
@property
def island(self) -> Optional[str]:
"""place=island
Identifies the coastline of an island (> 1 km2), also consider
place=islet for very small islandsIdentifies the coastline of an
island (> 1 km2), also consider place=islet for very small islands
"""
return self._address.get("island")
@property
def city(self) -> Optional[str]:
"""place=city
The largest urban settlements in the territory, normally including the
national, state and provincial capitals. These are defined by charter
or other governmental designation in some territories and are a matter
of judgement in others. Should normally have a population of at
least 100,000 people and be larger than nearby towns.
See place=suburb and place=neighbourhood on how to tag divisions
within a city. The outskirts of urban settlements may or may not match
the administratively declared boundary of the city.
"""
return self._address.get("city")
# ================================ #
# Administratively declared places #
# ================================ #
@property
def municipality(self) -> Optional[str]:
"""admin_level=8"""
return self._address.get("municipality")
@property
def county(self) -> Optional[str]:
"""admin_level=6"""
return self._address.get("county")
@property
def district(self) -> Optional[str]:
"""admin_level=5/6"""
return self._address.get("city_district")
@property
def state(self) -> Optional[str]:
"""admin_level=4"""
return self._address.get("state")
@property
def region(self) -> Optional[str]:
"""admin_level=3"""
return self._address.get("state")
@property
def country(self) -> Optional[str]:
"""admin_level=2"""
return self._address.get("country")
@property
def country_code(self) -> Optional[str]:
"""admin_level=2"""
return self._address.get("country_code")
# ======================== #
# Quality Control & Others #
# ======================== #
@property
def accuracy(self) -> Optional[str]:
return self.importance
@property
def quality(self) -> Optional[str]:
return self.type
@property
def population(self) -> Optional[str]:
return self.object_raw_json.get("population")
@property
def license(self) -> Optional[str]:
return self.object_raw_json.get("license")
@property
def type(self) -> Optional[str]:
return self.object_raw_json.get("type")
@property
def importance(self) -> Optional[str]:
return self.object_raw_json.get("importance")
@property
def icon(self) -> Optional[str]:
return self.object_raw_json.get("icon")
@property
def osm_type(self) -> Optional[str]:
return self.object_raw_json.get("osm_type")
@property
def osm_id(self) -> Optional[str]:
return self.object_raw_json.get("osm_id")
@property
def place_id(self) -> Optional[str]:
return self.object_raw_json.get("place_id")
@property
def place_rank(self) -> Optional[str]:
return self.object_raw_json.get("place_rank")
[docs]class OsmQuery(MultipleResultsQuery):
"""
Nominatim API Reference: https://nominatim.org/release-docs/develop/api/Overview/
"""
_PROVIDER = "osm"
_METHOD = "geocode"
_URL = "https://nominatim.openstreetmap.org/search"
_RESULT_CLASS = OsmResult
_KEY_MANDATORY = False
def _build_params(
self,
location,
provider_key: str,
max_results: int = 1,
**kwargs,
) -> dict:
return {
"q": location,
"format": "jsonv2",
"addressdetails": 1,
"limit": max_results,
}
class OsmQueryDetail(OsmQuery):
_METHOD = "details"
def _build_params(
self,
location,
provider_key,
max_results: int = 1,
**kwargs,
) -> dict:
query = {
"format": "jsonv2",
"addressdetails": 1,
"limit": max_results,
}
query.update(kwargs)
return query
class OsmReverse(OsmQuery):
_METHOD = "reverse"
def _build_params(
self,
location,
provider_key: str,
max_results: int = 1,
**kwargs,
) -> dict:
params = {
"q": str(Location(location)),
"format": "jsonv2",
"addressdetails": 1,
"limit": max_results,
}
if "lang_code" in kwargs:
params["accept-language"] = kwargs.get("lang_code")
return params