mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
feat(maps): add guest_house, camp_site, and dual-key bakery lookup (#13398)
Small follow-up inspired by stale PR #2421 (@poojandpatel). - bakery now searches both shop=bakery AND amenity=bakery in one Overpass query so indie bakeries tagged either way are returned. Reproduces #2421's Lawrenceville, NJ test case (The Gingered Peach, WildFlour Bakery). - Adds tourism=guest_house and tourism=camp_site as first-class categories. - CATEGORY_TAGS entries can now be a list of (key, value) tuples; new _tags_for() normaliser + tag_pairs= kwarg on build_overpass_nearby/bbox union the results in one query. Old single-tuple call sites unchanged (back-compat preserved). - SKILL.md: 44 → 46 categories, list updated.
This commit is contained in:
parent
c312e8ecf5
commit
c5a814b233
2 changed files with 71 additions and 26 deletions
|
|
@ -2,7 +2,7 @@
|
|||
name: maps
|
||||
description: >
|
||||
Location intelligence — geocode a place, reverse-geocode coordinates,
|
||||
find nearby places (44 POI categories), driving/walking/cycling
|
||||
find nearby places (46 POI categories), driving/walking/cycling
|
||||
distance + time, turn-by-turn directions, timezone lookup, bounding
|
||||
box + area for a named place, and POI search within a rectangle.
|
||||
Uses OpenStreetMap + Overpass + OSRM. Free, no API key.
|
||||
|
|
@ -83,12 +83,13 @@ python3 $MAPS nearby --near "90210" --category pharmacy
|
|||
python3 $MAPS nearby --near "downtown austin" --category restaurant --category bar --limit 10
|
||||
```
|
||||
|
||||
44 categories: restaurant, cafe, bar, hospital, pharmacy, hotel, supermarket,
|
||||
atm, gas_station, parking, museum, park, school, university, bank, police,
|
||||
fire_station, library, airport, train_station, bus_stop, church, mosque,
|
||||
synagogue, dentist, doctor, cinema, theatre, gym, swimming_pool, post_office,
|
||||
convenience_store, bakery, bookshop, laundry, car_wash, car_rental,
|
||||
bicycle_rental, taxi, veterinary, zoo, playground, stadium, nightclub.
|
||||
46 categories: restaurant, cafe, bar, hospital, pharmacy, hotel, guest_house,
|
||||
camp_site, supermarket, atm, gas_station, parking, museum, park, school,
|
||||
university, bank, police, fire_station, library, airport, train_station,
|
||||
bus_stop, church, mosque, synagogue, dentist, doctor, cinema, theatre, gym,
|
||||
swimming_pool, post_office, convenience_store, bakery, bookshop, laundry,
|
||||
car_wash, car_rental, bicycle_rental, taxi, veterinary, zoo, playground,
|
||||
stadium, nightclub.
|
||||
|
||||
Each result includes: `name`, `address`, `lat`/`lon`, `distance_m`,
|
||||
`maps_url` (clickable Google Maps link), `directions_url` (Google Maps
|
||||
|
|
|
|||
|
|
@ -58,7 +58,9 @@ CATEGORY_TAGS = {
|
|||
"restaurant": ("amenity", "restaurant"),
|
||||
"cafe": ("amenity", "cafe"),
|
||||
"bar": ("amenity", "bar"),
|
||||
"bakery": ("shop", "bakery"),
|
||||
# bakery is tagged as shop=bakery in the OSM wiki, but some mappers use
|
||||
# amenity=bakery. Search both so small indie bakeries aren't missed.
|
||||
"bakery": [("shop", "bakery"), ("amenity", "bakery")],
|
||||
"convenience_store": ("shop", "convenience"),
|
||||
# Health
|
||||
"hospital": ("amenity", "hospital"),
|
||||
|
|
@ -68,6 +70,8 @@ CATEGORY_TAGS = {
|
|||
"veterinary": ("amenity", "veterinary"),
|
||||
# Accommodation
|
||||
"hotel": ("tourism", "hotel"),
|
||||
"guest_house": ("tourism", "guest_house"),
|
||||
"camp_site": ("tourism", "camp_site"),
|
||||
# Shopping & Services
|
||||
"supermarket": ("shop", "supermarket"),
|
||||
"bookshop": ("shop", "books"),
|
||||
|
|
@ -120,6 +124,19 @@ RELIGION_FILTER = {
|
|||
|
||||
VALID_CATEGORIES = sorted(CATEGORY_TAGS.keys())
|
||||
|
||||
|
||||
def _tags_for(category):
|
||||
"""Return the CATEGORY_TAGS entry as a list of (key, value) pairs.
|
||||
|
||||
Most categories map to a single (tag_key, tag_val) tuple, but some
|
||||
(e.g. ``bakery``) are tagged under more than one OSM key and are
|
||||
represented as a list of tuples. Normalise both forms to a list.
|
||||
"""
|
||||
entry = CATEGORY_TAGS[category]
|
||||
if isinstance(entry, list):
|
||||
return list(entry)
|
||||
return [entry]
|
||||
|
||||
OSRM_PROFILES = {
|
||||
"driving": "driving",
|
||||
"walking": "foot",
|
||||
|
|
@ -338,36 +355,63 @@ def geocode_single(query):
|
|||
# ---------------------------------------------------------------------------
|
||||
|
||||
def build_overpass_nearby(tag_key, tag_val, lat, lon, radius, limit,
|
||||
religion=None):
|
||||
"""Build an Overpass QL query for nearby POIs around a point."""
|
||||
religion=None, tag_pairs=None):
|
||||
"""Build an Overpass QL query for nearby POIs around a point.
|
||||
|
||||
If ``tag_pairs`` is provided, the query unions across every
|
||||
``(key, value)`` pair (used for categories like ``bakery`` that are
|
||||
tagged under more than one OSM key). Otherwise falls back to the
|
||||
single ``tag_key``/``tag_val`` pair for back-compat.
|
||||
"""
|
||||
pairs = tag_pairs if tag_pairs else [(tag_key, tag_val)]
|
||||
religion_filter = ""
|
||||
if religion:
|
||||
religion_filter = f'["religion"="{religion}"]'
|
||||
body_lines = []
|
||||
for k, v in pairs:
|
||||
body_lines.append(
|
||||
f' node["{k}"="{v}"]{religion_filter}'
|
||||
f'(around:{radius},{lat},{lon});'
|
||||
)
|
||||
body_lines.append(
|
||||
f' way["{k}"="{v}"]{religion_filter}'
|
||||
f'(around:{radius},{lat},{lon});'
|
||||
)
|
||||
body = "\n".join(body_lines)
|
||||
return (
|
||||
f'[out:json][timeout:25];\n'
|
||||
f'(\n'
|
||||
f' node["{tag_key}"="{tag_val}"]{religion_filter}'
|
||||
f'(around:{radius},{lat},{lon});\n'
|
||||
f' way["{tag_key}"="{tag_val}"]{religion_filter}'
|
||||
f'(around:{radius},{lat},{lon});\n'
|
||||
f'{body}\n'
|
||||
f');\n'
|
||||
f'out center {limit};\n'
|
||||
)
|
||||
|
||||
|
||||
def build_overpass_bbox(tag_key, tag_val, south, west, north, east, limit,
|
||||
religion=None):
|
||||
"""Build an Overpass QL query for POIs within a bounding box."""
|
||||
religion=None, tag_pairs=None):
|
||||
"""Build an Overpass QL query for POIs within a bounding box.
|
||||
|
||||
See ``build_overpass_nearby`` for ``tag_pairs`` semantics.
|
||||
"""
|
||||
pairs = tag_pairs if tag_pairs else [(tag_key, tag_val)]
|
||||
religion_filter = ""
|
||||
if religion:
|
||||
religion_filter = f'["religion"="{religion}"]'
|
||||
body_lines = []
|
||||
for k, v in pairs:
|
||||
body_lines.append(
|
||||
f' node["{k}"="{v}"]{religion_filter}'
|
||||
f'({south},{west},{north},{east});'
|
||||
)
|
||||
body_lines.append(
|
||||
f' way["{k}"="{v}"]{religion_filter}'
|
||||
f'({south},{west},{north},{east});'
|
||||
)
|
||||
body = "\n".join(body_lines)
|
||||
return (
|
||||
f'[out:json][timeout:25];\n'
|
||||
f'(\n'
|
||||
f' node["{tag_key}"="{tag_val}"]{religion_filter}'
|
||||
f'({south},{west},{north},{east});\n'
|
||||
f' way["{tag_key}"="{tag_val}"]{religion_filter}'
|
||||
f'({south},{west},{north},{east});\n'
|
||||
f'{body}\n'
|
||||
f');\n'
|
||||
f'out center {limit};\n'
|
||||
)
|
||||
|
|
@ -605,10 +649,10 @@ def cmd_nearby(args):
|
|||
# appear twice.
|
||||
merged = {}
|
||||
for category in categories:
|
||||
tag_key, tag_val = CATEGORY_TAGS[category]
|
||||
tag_pairs = _tags_for(category)
|
||||
religion = RELIGION_FILTER.get(category)
|
||||
query = build_overpass_nearby(tag_key, tag_val, lat, lon, radius, limit,
|
||||
religion=religion)
|
||||
query = build_overpass_nearby(None, None, lat, lon, radius, limit,
|
||||
religion=religion, tag_pairs=tag_pairs)
|
||||
raw = overpass_query(query)
|
||||
elements = raw.get("elements", [])
|
||||
for place in parse_overpass_elements(elements, ref_lat=lat, ref_lon=lon):
|
||||
|
|
@ -945,10 +989,10 @@ def cmd_bbox(args):
|
|||
if limit <= 0:
|
||||
error_exit("Limit must be a positive integer.")
|
||||
|
||||
tag_key, tag_val = CATEGORY_TAGS[category]
|
||||
tag_pairs = _tags_for(category)
|
||||
religion = RELIGION_FILTER.get(category)
|
||||
query = build_overpass_bbox(tag_key, tag_val, south, west, north, east,
|
||||
limit, religion=religion)
|
||||
query = build_overpass_bbox(None, None, south, west, north, east,
|
||||
limit, religion=religion, tag_pairs=tag_pairs)
|
||||
|
||||
raw = overpass_query(query)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue