explore_area
Analyze neighborhoods by generating detailed profiles of amenities and geographic features within a specified radius around coordinates for location-based research and comparisons.
Instructions
Generate a comprehensive profile of an area including all amenities and features.
This powerful analysis tool creates a detailed overview of a neighborhood or area by identifying and categorizing all geographic features, amenities, and points of interest. Results are organized by category for easy analysis. Excellent for neighborhood research, area comparisons, and location-based decision making.
Args: latitude: Center point latitude (decimal degrees) longitude: Center point longitude (decimal degrees) radius: Search radius in meters (defaults to 500m)
Returns: In-depth area profile including: - Address and location context - Total feature count - Features organized by category and subcategory - Each feature includes name, coordinates, and detailed metadata
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| latitude | Yes | ||
| longitude | Yes | ||
| radius | No |
Implementation Reference
- src/osm_mcp_server/server.py:615-731 (handler)Implementation of the explore_area tool handler. Queries OpenStreetMap Overpass API for multiple feature categories within a bounding box derived from center coordinates and radius. Groups results by subcategories and returns a comprehensive area profile including address information.async def explore_area( latitude: float, longitude: float, ctx: Context, radius: float = 500 ) -> Dict[str, Any]: """ Generate a comprehensive profile of an area including all amenities and features. This powerful analysis tool creates a detailed overview of a neighborhood or area by identifying and categorizing all geographic features, amenities, and points of interest. Results are organized by category for easy analysis. Excellent for neighborhood research, area comparisons, and location-based decision making. Args: latitude: Center point latitude (decimal degrees) longitude: Center point longitude (decimal degrees) radius: Search radius in meters (defaults to 500m) Returns: In-depth area profile including: - Address and location context - Total feature count - Features organized by category and subcategory - Each feature includes name, coordinates, and detailed metadata """ osm_client = ctx.request_context.lifespan_context.osm_client # Categories to search for categories = [ "amenity", "shop", "tourism", "leisure", "natural", "historic", "public_transport" ] results = {} for i, category in enumerate(categories): await ctx.report_progress(i, len(categories)) ctx.info(f"Exploring {category} features...") try: # Convert radius to bounding box lat_delta = radius / 111000 lon_delta = radius / (111000 * math.cos(math.radians(latitude))) bbox = ( longitude - lon_delta, latitude - lat_delta, longitude + lon_delta, latitude + lat_delta ) features = await osm_client.search_features_by_category(bbox, category) # Group by subcategory subcategories = {} for feature in features: tags = feature.get("tags", {}) subcategory = tags.get(category) if subcategory: if subcategory not in subcategories: subcategories[subcategory] = [] # Get coordinates based on feature type coords = {} if feature.get("type") == "node": coords = { "latitude": feature.get("lat"), "longitude": feature.get("lon") } elif "center" in feature: coords = { "latitude": feature.get("center", {}).get("lat"), "longitude": feature.get("center", {}).get("lon") } subcategories[subcategory].append({ "id": feature.get("id"), "name": tags.get("name", "Unnamed"), "coordinates": coords, "type": feature.get("type"), "tags": tags }) results[category] = subcategories except Exception as e: ctx.warning(f"Error fetching {category} features: {str(e)}") results[category] = {} # Get address information for the center point try: address_info = await osm_client.reverse_geocode(latitude, longitude) except Exception: address_info = {"error": "Could not retrieve address information"} # Report completion await ctx.report_progress(len(categories), len(categories)) # Count total features total_features = sum( len(places) for category_data in results.values() for places in category_data.values() ) return { "query": { "latitude": latitude, "longitude": longitude, "radius": radius }, "address": address_info, "categories": results, "total_features": total_features, "timestamp": datetime.now().isoformat() }
- src/osm_mcp_server/server.py:144-177 (helper)OSMClient helper method that performs the core Overpass API query to fetch OSM features (nodes, ways, relations) filtered by a specific category tag within a bounding box. Called repeatedly by explore_area for each category.async def search_features_by_category(self, bbox: Tuple[float, float, float, float], category: str, subcategories: List[str] = None) -> List[Dict]: """Search for OSM features by category and subcategories""" if not self.session: raise RuntimeError("OSM client not connected") overpass_url = "https://overpass-api.de/api/interpreter" # Build query for specified category and subcategories if subcategories: subcategory_filters = " or ".join([f'"{category}"="{sub}"' for sub in subcategories]) query_filter = f'({subcategory_filters})' else: query_filter = f'"{category}"' query = f""" [out:json]; ( node[{query_filter}]({bbox[1]},{bbox[0]},{bbox[3]},{bbox[2]}); way[{query_filter}]({bbox[1]},{bbox[0]},{bbox[3]},{bbox[2]}); relation[{query_filter}]({bbox[1]},{bbox[0]},{bbox[3]},{bbox[2]}); ); out body; """ async with self.session.post(overpass_url, data={"data": query}) as response: if response.status == 200: data = await response.json() return data.get("elements", []) else: raise Exception(f"Failed to search features by category: {response.status}")
- src/osm_mcp_server/server.py:45-64 (helper)OSMClient helper method used by explore_area to fetch address information for the center point of the exploration area via Nominatim reverse geocoding.async def reverse_geocode(self, lat: float, lon: float) -> Dict: """Reverse geocode coordinates to address""" if not self.session: raise RuntimeError("OSM client not connected") nominatim_url = "https://nominatim.openstreetmap.org/reverse" async with self.session.get( nominatim_url, params={ "lat": lat, "lon": lon, "format": "json" }, headers={"User-Agent": "OSM-MCP-Server/1.0"} ) as response: if response.status == 200: return await response.json() else: raise Exception(f"Failed to reverse geocode ({lat}, {lon}): {response.status}")
- src/osm_mcp_server/server.py:621-640 (schema)Tool schema documentation including description, input parameters (latitude, longitude, radius), and output structure description.""" Generate a comprehensive profile of an area including all amenities and features. This powerful analysis tool creates a detailed overview of a neighborhood or area by identifying and categorizing all geographic features, amenities, and points of interest. Results are organized by category for easy analysis. Excellent for neighborhood research, area comparisons, and location-based decision making. Args: latitude: Center point latitude (decimal degrees) longitude: Center point longitude (decimal degrees) radius: Search radius in meters (defaults to 500m) Returns: In-depth area profile including: - Address and location context - Total feature count - Features organized by category and subcategory - Each feature includes name, coordinates, and detailed metadata """