Skip to main content
Glama

Fused MCP Agents

Official
by fusedio
{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 3. Vancounver Open Data Example: Creating an Agent with multiple UDFs\n", "\n", "In this notebook we'll build a multi-UDF Agent that can have access to a few UDFs that fetch information from [Vancouver's Open Data portal](https://opendata.vancouver.ca/pages/home/)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import fused\n", "import json\n", "import os\n", "import time\n", "from pathlib import Path" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# We still need your local paths\n", "PATH_TO_CLAUDE_CONFIG = (\n", " f\"{str(Path.home())}/Library/Application Support/Claude/claude_desktop_config.json\"\n", ")\n", "\n", "\n", "if not os.path.exists(PATH_TO_CLAUDE_CONFIG):\n", " # Creating the config file\n", " os.makedirs(os.path.dirname(PATH_TO_CLAUDE_CONFIG), exist_ok=True)\n", " with open(PATH_TO_CLAUDE_CONFIG, \"w\") as f:\n", " json.dump({}, f)\n", "\n", "assert os.path.exists(PATH_TO_CLAUDE_CONFIG), (\n", " \"Please update the PATH_TO_CLAUDE_CONFIG variable with the correct path to your Claude config file\"\n", ")" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Local path to the Claude app\n", "CLAUDE_APP_PATH = \"/Applications/Claude.app\"\n", "assert os.path.exists(CLAUDE_APP_PATH), (\n", " \"Please update the CLAUDE_APP_PATH variable with the correct path to your Claude app\"\n", ")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# Change this path if you're not running this from the repo root\n", "WORKING_DIR = os.getcwd()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# We'll load the commons folder once again to have our helper functions\n", "commit = \"5dda36c\"\n", "common = fused.load(\n", " f\"https://github.com/fusedio/udfs/tree/{commit}/public/common\"\n", ").utils" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'agents': [{'name': 'get_current_time', 'udfs': ['current_utc_time']},\n", " {'name': 'fused_docs', 'udfs': ['list_public_udfs', 'reading_fused_docs']},\n", " {'name': 'vancouver_open_data',\n", " 'udfs': ['hundred_parks_in_vancouver',\n", " 'electric_vehicle_chargers_in_vancouver',\n", " 'building_permits_in_vancouver',\n", " 'internet_speeds_for_lat_lon']},\n", " {'name': 'elevation_stats_for_lat_lon_area', 'udfs': ['elevation_stats']},\n", " {'name': 'apple_banana_orange', 'udfs': ['apple_banana_orange_udf']},\n", " {'name': 'dynamic_output_vector', 'udfs': ['dynamic_output_vector_udf']}]}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# And see which agents we have available\n", "json.load(open(os.path.join(WORKING_DIR, \"agents.json\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll make 5 UDFs:\n", "- Returning location, name & size of 100 parks in Vancouver\n", "- Returning the location of 100 EV chargers in the city\n", "- Returning yearly crime statistics over a point of interest in Vancouver\n", "- Returning the internet speed of any lat / lon (not just in Vancouver, but this also works for Vancouver area)\n", "- Returning the location of community gardens in the city" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "AGENT_NAME = \"vancouver_open_data_demo\"" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "@fused.udf\n", "def parks_vancouver():\n", " \"\"\"\n", " UDF to get the polygon geometries of parks in Vancouver based on Open Data Portal\n", " \"\"\"\n", " import requests\n", " import geopandas as gpd\n", " import pandas as pd\n", " from shapely.geometry import Polygon, MultiPolygon, shape\n", " import numpy as np\n", " import math\n", " from pandas import json_normalize\n", "\n", " @fused.cache\n", " def get_request(url):\n", " response = requests.get(url)\n", " response.raise_for_status() # Raise an exception for HTTP errors\n", " return response\n", "\n", " limit = 100\n", " parks_url = f\"https://opendata.vancouver.ca/api/explore/v2.1/catalog/datasets/parks-polygon-representation/records?limit={str(limit)}\"\n", " response = get_request(url=parks_url)\n", " json_data = response.json()\n", " \n", " # First extract all non-geometry data\n", " df = json_normalize(json_data['results'])\n", " \n", " # Drop the geom column which will be replaced with proper geometry\n", " if 'geom' in df.columns:\n", " df = df.drop(columns=['geom'])\n", "\n", " # Filter for valid polygons or multipolygons\n", " valid_items = [\n", " item for item in json_data['results']\n", " if 'geom' in item \n", " and item['geom'] is not None\n", " and isinstance(item['geom'], dict)\n", " and 'geometry' in item['geom'] \n", " and item['geom']['geometry'] is not None\n", " and isinstance(item['geom']['geometry'], dict)\n", " and 'type' in item['geom']['geometry']\n", " and item['geom']['geometry']['type'] in ['Polygon', 'MultiPolygon']\n", " ]\n", " \n", " # Create geometries using shapely's shape function\n", " geometries = []\n", " for item in valid_items:\n", " try:\n", " geom = shape(item['geom']['geometry'])\n", " geometries.append(geom)\n", " except Exception as e:\n", " print(f\"Error creating geometry: {e}\")\n", " continue\n", " \n", " # Create filtered dataframe with matching indices\n", " filtered_df = pd.DataFrame(valid_items).drop(columns=['geom'])\n", " \n", " # Create GeoDataFrame with geometries\n", " gdf = gpd.GeoDataFrame(\n", " filtered_df, \n", " geometry=geometries,\n", " crs=\"EPSG:4326\"\n", " )\n", "\n", " # Adding estimate of average size of park so next UDF knows bu how much it needs to buffer lat / lon point to get similar expecation\n", " gdf['buffer_radius'] = gdf['area_ha'].apply(\n", " lambda x: math.sqrt((x * 10000) / math.pi)\n", " )\n", "\n", " # # Adding centroid calculation\n", " # centroids = gdf.geometry.centroid\n", " # gdf['centroid_lon'] = centroids.x\n", " # gdf['centroid_lat'] = centroids.y\n", " \n", " print(f\"{gdf.sample()=}\")\n", " print(f\"{gdf.columns=}\")\n", " print(gdf.shape)\n", " return gdf" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>park_id</th>\n", " <th>park_name</th>\n", " <th>area_ha</th>\n", " <th>park_url</th>\n", " <th>geo_point_2d</th>\n", " <th>geometry</th>\n", " <th>buffer_radius</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>108.0</td>\n", " <td>Connaught Park</td>\n", " <td>5.993645</td>\n", " <td>http://covapp.vancouver.ca/parkfinder/parkdeta...</td>\n", " <td>{'lat': 49.26205550109892, 'lon': -123.1601050...</td>\n", " <td>POLYGON ((-123.1623 49.26292, -123.15784 49.26...</td>\n", " <td>138.124454</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>81.0</td>\n", " <td>Clark Park</td>\n", " <td>4.295203</td>\n", " <td>http://covapp.vancouver.ca/parkfinder/parkdeta...</td>\n", " <td>{'lat': 49.25710409057022, 'lon': -123.0723570...</td>\n", " <td>POLYGON ((-123.07002 49.25766, -123.07004 49.2...</td>\n", " <td>116.927561</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>37.0</td>\n", " <td>Chaldecott Park</td>\n", " <td>3.454305</td>\n", " <td>http://covapp.vancouver.ca/parkfinder/parkdeta...</td>\n", " <td>{'lat': 49.249047303072075, 'lon': -123.192237...</td>\n", " <td>POLYGON ((-123.19347 49.24991, -123.19099 49.2...</td>\n", " <td>104.858925</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>174.0</td>\n", " <td>Braemar Park</td>\n", " <td>1.258929</td>\n", " <td>http://covapp.vancouver.ca/parkfinder/parkdeta...</td>\n", " <td>{'lat': 49.24762496945391, 'lon': -123.1235807...</td>\n", " <td>POLYGON ((-123.12462 49.24801, -123.12251 49.2...</td>\n", " <td>63.303193</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>236.0</td>\n", " <td>Ebisu Park</td>\n", " <td>0.420569</td>\n", " <td>http://covapp.vancouver.ca/parkfinder/parkdeta...</td>\n", " <td>{'lat': 49.20538526928567, 'lon': -123.1324784...</td>\n", " <td>POLYGON ((-123.13189 49.20509, -123.13247 49.2...</td>\n", " <td>36.588417</td>\n", " </tr>\n", " <tr>\n", " <th>...</th>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " </tr>\n", " <tr>\n", " <th>95</th>\n", " <td>8.0</td>\n", " <td>Trafalgar Park</td>\n", " <td>4.860801</td>\n", " <td>http://covapp.vancouver.ca/parkfinder/parkdeta...</td>\n", " <td>{'lat': 49.25097540413386, 'lon': -123.1624811...</td>\n", " <td>POLYGON ((-123.16514 49.25106, -123.16512 49.2...</td>\n", " <td>124.388145</td>\n", " </tr>\n", " <tr>\n", " <th>96</th>\n", " <td>188.0</td>\n", " <td>Ross Park</td>\n", " <td>1.511835</td>\n", " <td>http://covapp.vancouver.ca/parkfinder/parkdeta...</td>\n", " <td>{'lat': 49.21726674556697, 'lon': -123.0823563...</td>\n", " <td>POLYGON ((-123.08296 49.21803, -123.08173 49.2...</td>\n", " <td>69.370880</td>\n", " </tr>\n", " <tr>\n", " <th>97</th>\n", " <td>140.0</td>\n", " <td>Robson Park</td>\n", " <td>1.563992</td>\n", " <td>http://covapp.vancouver.ca/parkfinder/parkdeta...</td>\n", " <td>{'lat': 49.258160306611416, 'lon': -123.092024...</td>\n", " <td>POLYGON ((-123.09127 49.25792, -123.09113 49.2...</td>\n", " <td>70.557371</td>\n", " </tr>\n", " <tr>\n", " <th>98</th>\n", " <td>168.0</td>\n", " <td>Riley Park</td>\n", " <td>2.703745</td>\n", " <td>http://covapp.vancouver.ca/parkfinder/parkdeta...</td>\n", " <td>{'lat': 49.24229347353439, 'lon': -123.1042653...</td>\n", " <td>POLYGON ((-123.1051 49.24326, -123.10334 49.24...</td>\n", " <td>92.770074</td>\n", " </tr>\n", " <tr>\n", " <th>99</th>\n", " <td>180.0</td>\n", " <td>Oppenheimer Park</td>\n", " <td>0.979111</td>\n", " <td>http://covapp.vancouver.ca/parkfinder/parkdeta...</td>\n", " <td>{'lat': 49.282640071926195, 'lon': -123.094347...</td>\n", " <td>POLYGON ((-123.09517 49.28302, -123.09349 49.2...</td>\n", " <td>55.826578</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "<p>100 rows × 7 columns</p>\n", "</div>" ], "text/plain": [ " park_id park_name area_ha \\\n", "0 108.0 Connaught Park 5.993645 \n", "1 81.0 Clark Park 4.295203 \n", "2 37.0 Chaldecott Park 3.454305 \n", "3 174.0 Braemar Park 1.258929 \n", "4 236.0 Ebisu Park 0.420569 \n", ".. ... ... ... \n", "95 8.0 Trafalgar Park 4.860801 \n", "96 188.0 Ross Park 1.511835 \n", "97 140.0 Robson Park 1.563992 \n", "98 168.0 Riley Park 2.703745 \n", "99 180.0 Oppenheimer Park 0.979111 \n", "\n", " park_url \\\n", "0 http://covapp.vancouver.ca/parkfinder/parkdeta... \n", "1 http://covapp.vancouver.ca/parkfinder/parkdeta... \n", "2 http://covapp.vancouver.ca/parkfinder/parkdeta... \n", "3 http://covapp.vancouver.ca/parkfinder/parkdeta... \n", "4 http://covapp.vancouver.ca/parkfinder/parkdeta... \n", ".. ... \n", "95 http://covapp.vancouver.ca/parkfinder/parkdeta... \n", "96 http://covapp.vancouver.ca/parkfinder/parkdeta... \n", "97 http://covapp.vancouver.ca/parkfinder/parkdeta... \n", "98 http://covapp.vancouver.ca/parkfinder/parkdeta... \n", "99 http://covapp.vancouver.ca/parkfinder/parkdeta... \n", "\n", " geo_point_2d \\\n", "0 {'lat': 49.26205550109892, 'lon': -123.1601050... \n", "1 {'lat': 49.25710409057022, 'lon': -123.0723570... \n", "2 {'lat': 49.249047303072075, 'lon': -123.192237... \n", "3 {'lat': 49.24762496945391, 'lon': -123.1235807... \n", "4 {'lat': 49.20538526928567, 'lon': -123.1324784... \n", ".. ... \n", "95 {'lat': 49.25097540413386, 'lon': -123.1624811... \n", "96 {'lat': 49.21726674556697, 'lon': -123.0823563... \n", "97 {'lat': 49.258160306611416, 'lon': -123.092024... \n", "98 {'lat': 49.24229347353439, 'lon': -123.1042653... \n", "99 {'lat': 49.282640071926195, 'lon': -123.094347... \n", "\n", " geometry buffer_radius \n", "0 POLYGON ((-123.1623 49.26292, -123.15784 49.26... 138.124454 \n", "1 POLYGON ((-123.07002 49.25766, -123.07004 49.2... 116.927561 \n", "2 POLYGON ((-123.19347 49.24991, -123.19099 49.2... 104.858925 \n", "3 POLYGON ((-123.12462 49.24801, -123.12251 49.2... 63.303193 \n", "4 POLYGON ((-123.13189 49.20509, -123.13247 49.2... 36.588417 \n", ".. ... ... \n", "95 POLYGON ((-123.16514 49.25106, -123.16512 49.2... 124.388145 \n", "96 POLYGON ((-123.08296 49.21803, -123.08173 49.2... 69.370880 \n", "97 POLYGON ((-123.09127 49.25792, -123.09113 49.2... 70.557371 \n", "98 POLYGON ((-123.1051 49.24326, -123.10334 49.24... 92.770074 \n", "99 POLYGON ((-123.09517 49.28302, -123.09349 49.2... 55.826578 \n", "\n", "[100 rows x 7 columns]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fused.run(parks_vancouver)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'description': \"\\nName: parks_vancouver\\nPurpose and Functionality:\\nThe UDF 'parks_vancouver' is designed to extract and format the geographical and geometrical data related to parks in Vancouver. It fetches the data from the Open Data Portal of the city of Vancouver and further structures it in a geo-referenced format suitable for subsequent spatial analysis or visualization tasks. The function operates by facilitating HTTP requests, performing data extraction, geometry creation, data formatting, and providing results as a GeoDataFrame. \\n\\nInput Parameters:\\n\\nThe function doesn't require any explicit input parameters from the user. Internal parameters such as the 'limit' and 'parks_url' variables are already set within the function. The 'limit' parameter controls the maximum number of entries the function fetches from the Open Data Portal with a current preset value of 100. The 'parks_url' parameter is set to the API endpoint of the Vancouver parks dataset.\\nOutput:\\nThe output of this UDF is a GeoDataFrame, a spatial variant of a pandas DataFrame from the GeoPandas library. This output contains the data about parks and related geometric entities with their characteristics included as column entries. This includes the park's area (in hectares), centroid coordinates, buffer radius, and geometric representation as shapely Polygon or MultiPolygon objects.\\n\\nTechnical Details and Limitations:\\nThe function makes HTTP requests to external APIs, therefore its functionality depends on the availability and continuity of those APIs. It also operates under the assumption that the data from the APIs follow a particular JSON structure. The script will fail to function properly if the web service changes its data structure or ceases to exist. To raise exceptions for HTTP errors and continue the function execution, it uses the 'raise_for_status()' method from the requests library.\\nFurthermore, the function also uses the Shapely library to generate geometric shapes from the data. Any inconsistencies or errors in the data may lead to exceptions during this process, but these are handled and printed via a try-except block. Finally, the output GeoDataFrame's 'buffer_radius' column is computed using the assumption that the park areas are circular, which may not always be accurate for irregularly shaped parks.\",\n", " 'parameters': [{'name': '', 'type': ''}]}" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "parks_mcp_metadata = {\n", " \"description\": \"\"\"\n", "Name: parks_vancouver\n", "Purpose and Functionality:\n", "The UDF 'parks_vancouver' is designed to extract and format the geographical and geometrical data related to parks in Vancouver. It fetches the data from the Open Data Portal of the city of Vancouver and further structures it in a geo-referenced format suitable for subsequent spatial analysis or visualization tasks. The function operates by facilitating HTTP requests, performing data extraction, geometry creation, data formatting, and providing results as a GeoDataFrame. \n", "\n", "Input Parameters:\n", "\n", "The function doesn't require any explicit input parameters from the user. Internal parameters such as the 'limit' and 'parks_url' variables are already set within the function. The 'limit' parameter controls the maximum number of entries the function fetches from the Open Data Portal with a current preset value of 100. The 'parks_url' parameter is set to the API endpoint of the Vancouver parks dataset.\n", "Output:\n", "The output of this UDF is a GeoDataFrame, a spatial variant of a pandas DataFrame from the GeoPandas library. This output contains the data about parks and related geometric entities with their characteristics included as column entries. This includes the park's area (in hectares), centroid coordinates, buffer radius, and geometric representation as shapely Polygon or MultiPolygon objects.\n", "\n", "Technical Details and Limitations:\n", "The function makes HTTP requests to external APIs, therefore its functionality depends on the availability and continuity of those APIs. It also operates under the assumption that the data from the APIs follow a particular JSON structure. The script will fail to function properly if the web service changes its data structure or ceases to exist. To raise exceptions for HTTP errors and continue the function execution, it uses the 'raise_for_status()' method from the requests library.\n", "Furthermore, the function also uses the Shapely library to generate geometric shapes from the data. Any inconsistencies or errors in the data may lead to exceptions during this process, but these are handled and printed via a try-except block. Finally, the output GeoDataFrame's 'buffer_radius' column is computed using the assumption that the park areas are circular, which may not always be accurate for irregularly shaped parks.\"\"\",\n", " \"parameters\": [\n", " {\n", " \"name\": \"\",\n", " \"type\": \"\",\n", " }\n", " ],\n", "}\n", "\n", "parks_mcp_metadata" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# Adding our UDF + MCP_metadata to our Agent\n", "common.save_to_agent(\n", " agent_json_path=os.path.join(WORKING_DIR, \"agents.json\"),\n", " udf=parks_vancouver,\n", " agent_name=AGENT_NAME,\n", " udf_name=\"hundred_parks_in_vancouver\",\n", " mcp_metadata=parks_mcp_metadata,\n", ")" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "@fused.udf\n", "def ev_chargers():\n", " \"\"\"\n", " UDF to get the location of electric chargers around Vancouver based on Open Data Portal\n", " \"\"\"\n", " import requests\n", " import geopandas as gpd\n", " import pandas as pd\n", " from shapely.geometry import Point\n", " import numpy as np\n", " from pandas import json_normalize\n", "\n", " @fused.cache\n", " def get_request(url):\n", " response = requests.get(url)\n", " response.raise_for_status() # Raise an exception for HTTP errors\n", " return response\n", "\n", " limit = 100\n", " building_permits_url = f\"https://opendata.vancouver.ca/api/explore/v2.1/catalog/datasets/electric-vehicle-charging-stations/records?limit={str(limit)}\"\n", " response = get_request(url=building_permits_url)\n", " json_data = response.json()\n", " \n", " # First extract all non-geometry data\n", " df = json_normalize(json_data['results'])\n", " \n", " # Drop the geom column which will be replaced with proper geometry\n", " if 'geom' in df.columns:\n", " df = df.drop(columns=['geom'])\n", "\n", " # Skipping any point that doesn't have valid geom\n", " valid_items = [\n", " item for item in json_data['results']\n", " if 'geom' in item \n", " and item['geom'] is not None\n", " and isinstance(item['geom'], dict)\n", " and 'geometry' in item['geom'] \n", " and item['geom']['geometry'] is not None\n", " and isinstance(item['geom']['geometry'], dict)\n", " and 'type' in item['geom']['geometry']\n", " and item['geom']['geometry']['type'] == 'Point'\n", " ]\n", " \n", " # Extract coordinates directly into arrays\n", " coords = np.array([\n", " item['geom']['geometry']['coordinates'] \n", " for item in valid_items\n", " ])\n", " \n", " # Create Points in a vectorized way\n", " geometries = [Point(x, y) for x, y in coords]\n", " \n", " # Create filtered dataframe with matching indices\n", " filtered_df = pd.DataFrame(valid_items).drop(columns=['geom'])\n", " \n", " # Create GeoDataFrame with geometries\n", " gdf = gpd.GeoDataFrame(\n", " filtered_df, \n", " geometry=geometries,\n", " crs=\"EPSG:4326\"\n", " )\n", " print(gdf.shape)\n", " return gdf\n", " \n" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>address</th>\n", " <th>lot_operator</th>\n", " <th>geo_local_area</th>\n", " <th>geo_point_2d</th>\n", " <th>geometry</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>Beach Ave. @ Cardero St</td>\n", " <td>Easy Park / Park Board</td>\n", " <td>West End</td>\n", " <td>{'lat': 49.283155, 'lon': -123.142173}</td>\n", " <td>POINT (-123.14217 49.28316)</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>1 Kingsway</td>\n", " <td>Easypark</td>\n", " <td>Mount Pleasant</td>\n", " <td>{'lat': 49.2641594, 'lon': -123.1002054}</td>\n", " <td>POINT (-123.10021 49.26416)</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>4575 Clancy Loranger Way</td>\n", " <td>Park Board</td>\n", " <td>Riley Park</td>\n", " <td>{'lat': 49.243665, 'lon': -123.106578}</td>\n", " <td>POINT (-123.10658 49.24366)</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>845 Avison Way</td>\n", " <td>Vancouver Aquarium</td>\n", " <td>None</td>\n", " <td>{'lat': 49.299534, 'lon': -123.13022}</td>\n", " <td>POINT (-123.13022 49.29953)</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>273 E 53rd Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>Sunset</td>\n", " <td>{'lat': 49.222235022762, 'lon': -123.100095218...</td>\n", " <td>POINT (-123.1001 49.22224)</td>\n", " </tr>\n", " <tr>\n", " <th>5</th>\n", " <td>3311 E. Hastings</td>\n", " <td>City of Vancouver</td>\n", " <td>Hastings-Sunrise</td>\n", " <td>{'lat': 49.281369, 'lon': -123.033786}</td>\n", " <td>POINT (-123.03379 49.28137)</td>\n", " </tr>\n", " <tr>\n", " <th>6</th>\n", " <td>959-979 Mainland St</td>\n", " <td>City of Vancouver</td>\n", " <td>Downtown</td>\n", " <td>{'lat': 49.2769232201811, 'lon': -123.11926106...</td>\n", " <td>POINT (-123.11926 49.27692)</td>\n", " </tr>\n", " <tr>\n", " <th>7</th>\n", " <td>451 Beach Crescent</td>\n", " <td>City of Vancouver</td>\n", " <td>Downtown</td>\n", " <td>{'lat': 49.272514, 'lon': -123.12833}</td>\n", " <td>POINT (-123.12833 49.27251)</td>\n", " </tr>\n", " <tr>\n", " <th>8</th>\n", " <td>646 E. 44th Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>Sunset</td>\n", " <td>{'lat': 49.229928611422, 'lon': -123.09135336453}</td>\n", " <td>POINT (-123.09135 49.22993)</td>\n", " </tr>\n", " <tr>\n", " <th>9</th>\n", " <td>5175 Dumfries St</td>\n", " <td>City of Vancouver</td>\n", " <td>Kensington-Cedar Cottage</td>\n", " <td>{'lat': 49.2385063171386, 'lon': -123.07548522...</td>\n", " <td>POINT (-123.07549 49.23851)</td>\n", " </tr>\n", " <tr>\n", " <th>10</th>\n", " <td>646 E. 44th Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>Kensington-Cedar Cottage</td>\n", " <td>{'lat': 49.237778, 'lon': -123.074953}</td>\n", " <td>POINT (-123.07495 49.23778)</td>\n", " </tr>\n", " <tr>\n", " <th>11</th>\n", " <td>3350 Maquinna Dr</td>\n", " <td>City of Vancouver</td>\n", " <td>Killarney</td>\n", " <td>{'lat': 49.215036, 'lon': -123.032265}</td>\n", " <td>POINT (-123.03226 49.21504)</td>\n", " </tr>\n", " <tr>\n", " <th>12</th>\n", " <td>3350 Maquinna Dr</td>\n", " <td>City of Vancouver</td>\n", " <td>Killarney</td>\n", " <td>{'lat': 49.215032, 'lon': -123.032306}</td>\n", " <td>POINT (-123.03231 49.21503)</td>\n", " </tr>\n", " <tr>\n", " <th>13</th>\n", " <td>6260 Killarney St</td>\n", " <td>City of Vancouver</td>\n", " <td>Killarney</td>\n", " <td>{'lat': 49.226948, 'lon': -123.044804}</td>\n", " <td>POINT (-123.0448 49.22695)</td>\n", " </tr>\n", " <tr>\n", " <th>14</th>\n", " <td>6260 Killarney St</td>\n", " <td>City of Vancouver</td>\n", " <td>Killarney</td>\n", " <td>{'lat': 49.226955, 'lon': -123.044843}</td>\n", " <td>POINT (-123.04484 49.22696)</td>\n", " </tr>\n", " <tr>\n", " <th>15</th>\n", " <td>2099 Beach Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>None</td>\n", " <td>{'lat': 49.291034, 'lon': -123.145028}</td>\n", " <td>POINT (-123.14503 49.29103)</td>\n", " </tr>\n", " <tr>\n", " <th>16</th>\n", " <td>860 Richards St</td>\n", " <td>City of Vancouver</td>\n", " <td>Downtown</td>\n", " <td>{'lat': 49.279351, 'lon': -123.118962}</td>\n", " <td>POINT (-123.11896 49.27935)</td>\n", " </tr>\n", " <tr>\n", " <th>17</th>\n", " <td>860 Richards St</td>\n", " <td>City of Vancouver</td>\n", " <td>Downtown</td>\n", " <td>{'lat': 49.279373, 'lon': -123.118924}</td>\n", " <td>POINT (-123.11892 49.27937)</td>\n", " </tr>\n", " <tr>\n", " <th>18</th>\n", " <td>1200 Thornton St</td>\n", " <td>City of Vancouver</td>\n", " <td>Strathcona</td>\n", " <td>{'lat': 49.274598, 'lon': -123.09242}</td>\n", " <td>POINT (-123.09242 49.2746)</td>\n", " </tr>\n", " <tr>\n", " <th>19</th>\n", " <td>1201 Thornton St</td>\n", " <td>City of Vancouver</td>\n", " <td>Strathcona</td>\n", " <td>{'lat': 49.27454, 'lon': -123.092452}</td>\n", " <td>POINT (-123.09245 49.27454)</td>\n", " </tr>\n", " <tr>\n", " <th>20</th>\n", " <td>2221 Main St</td>\n", " <td>Easypark</td>\n", " <td>Mount Pleasant</td>\n", " <td>{'lat': 49.265235, 'lon': -123.101577}</td>\n", " <td>POINT (-123.10158 49.26524)</td>\n", " </tr>\n", " <tr>\n", " <th>21</th>\n", " <td>4575 Clancy Loranger Way</td>\n", " <td>Park Board</td>\n", " <td>Riley Park</td>\n", " <td>{'lat': 49.243681, 'lon': -123.106551}</td>\n", " <td>POINT (-123.10655 49.24368)</td>\n", " </tr>\n", " <tr>\n", " <th>22</th>\n", " <td>701 W. Georgia St.</td>\n", " <td>Easy Park Lot</td>\n", " <td>Downtown</td>\n", " <td>{'lat': 49.283672, 'lon': -123.1173}</td>\n", " <td>POINT (-123.1173 49.28367)</td>\n", " </tr>\n", " <tr>\n", " <th>23</th>\n", " <td>Beach Ave. @ Broughton St</td>\n", " <td>Easy Park / Park Board</td>\n", " <td>West End</td>\n", " <td>{'lat': 49.281483, 'lon': -123.139793}</td>\n", " <td>POINT (-123.13979 49.28148)</td>\n", " </tr>\n", " <tr>\n", " <th>24</th>\n", " <td>1305 Arbutus St</td>\n", " <td>City of Vancouver</td>\n", " <td>Kitsilano</td>\n", " <td>{'lat': 49.273797, 'lon': -123.152654}</td>\n", " <td>POINT (-123.15265 49.2738)</td>\n", " </tr>\n", " <tr>\n", " <th>25</th>\n", " <td>4575 Clancy Loranger Way</td>\n", " <td>Park Board</td>\n", " <td>Riley Park</td>\n", " <td>{'lat': 49.243665, 'lon': -123.106617}</td>\n", " <td>POINT (-123.10662 49.24366)</td>\n", " </tr>\n", " <tr>\n", " <th>26</th>\n", " <td>1100 Granville St</td>\n", " <td>Best Western</td>\n", " <td>Downtown</td>\n", " <td>{'lat': 49.277692, 'lon': -123.124668}</td>\n", " <td>POINT (-123.12467 49.27769)</td>\n", " </tr>\n", " <tr>\n", " <th>27</th>\n", " <td>1001 Cotton Dr</td>\n", " <td>Park Board</td>\n", " <td>Grandview-Woodland</td>\n", " <td>{'lat': 49.276066, 'lon': -123.071246}</td>\n", " <td>POINT (-123.07125 49.27607)</td>\n", " </tr>\n", " <tr>\n", " <th>28</th>\n", " <td>225 N Renfrew St</td>\n", " <td>Pacific National Exhibition</td>\n", " <td>Hastings-Sunrise</td>\n", " <td>{'lat': 49.286384777239, 'lon': -123.043494521...</td>\n", " <td>POINT (-123.04349 49.28638)</td>\n", " </tr>\n", " <tr>\n", " <th>29</th>\n", " <td>453 W. 12th Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>Mount Pleasant</td>\n", " <td>{'lat': 49.26122, 'lon': -123.11374}</td>\n", " <td>POINT (-123.11374 49.26122)</td>\n", " </tr>\n", " <tr>\n", " <th>30</th>\n", " <td>646 E. 44th Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>Sunset</td>\n", " <td>{'lat': 49.2297756397356, 'lon': -123.09136002...</td>\n", " <td>POINT (-123.09136 49.22978)</td>\n", " </tr>\n", " <tr>\n", " <th>31</th>\n", " <td>5175 Dumfries St</td>\n", " <td>City of Vancouver</td>\n", " <td>Kensington-Cedar Cottage</td>\n", " <td>{'lat': 49.2385597229003, 'lon': -123.07533264...</td>\n", " <td>POINT (-123.07533 49.23856)</td>\n", " </tr>\n", " <tr>\n", " <th>32</th>\n", " <td>6260 Killarney St</td>\n", " <td>City of Vancouver</td>\n", " <td>Killarney</td>\n", " <td>{'lat': 49.226968, 'lon': -123.044818}</td>\n", " <td>POINT (-123.04482 49.22697)</td>\n", " </tr>\n", " <tr>\n", " <th>33</th>\n", " <td>2099 Beach Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>None</td>\n", " <td>{'lat': 49.291028, 'lon': -123.14499}</td>\n", " <td>POINT (-123.14499 49.29103)</td>\n", " </tr>\n", " <tr>\n", " <th>34</th>\n", " <td>2099 Beach Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>None</td>\n", " <td>{'lat': 49.291042, 'lon': -123.145062}</td>\n", " <td>POINT (-123.14506 49.29104)</td>\n", " </tr>\n", " <tr>\n", " <th>35</th>\n", " <td>1664 E. 11th</td>\n", " <td>City of Vancouver</td>\n", " <td>Kensington-Cedar Cottage</td>\n", " <td>{'lat': 49.260433, 'lon': -123.070249}</td>\n", " <td>POINT (-123.07025 49.26043)</td>\n", " </tr>\n", " <tr>\n", " <th>36</th>\n", " <td>3360 Victoria Dr</td>\n", " <td>City of Vancouver</td>\n", " <td>Kensington-Cedar Cottage</td>\n", " <td>{'lat': 49.256637, 'lon': -123.065749}</td>\n", " <td>POINT (-123.06575 49.25664)</td>\n", " </tr>\n", " <tr>\n", " <th>37</th>\n", " <td>3360 Victoria Dr</td>\n", " <td>City of Vancouver</td>\n", " <td>Kensington-Cedar Cottage</td>\n", " <td>{'lat': 49.256591, 'lon': -123.06573}</td>\n", " <td>POINT (-123.06573 49.25659)</td>\n", " </tr>\n", " <tr>\n", " <th>38</th>\n", " <td>3360 Victoria Dr</td>\n", " <td>City of Vancouver</td>\n", " <td>Kensington-Cedar Cottage</td>\n", " <td>{'lat': 49.256537, 'lon': -123.065717}</td>\n", " <td>POINT (-123.06572 49.25654)</td>\n", " </tr>\n", " <tr>\n", " <th>39</th>\n", " <td>845 Avison Way</td>\n", " <td>Vancouver Aquarium</td>\n", " <td>None</td>\n", " <td>{'lat': 49.300472054096, 'lon': -123.130229213...</td>\n", " <td>POINT (-123.13023 49.30047)</td>\n", " </tr>\n", " <tr>\n", " <th>40</th>\n", " <td>Beach Ave. @ Bute St</td>\n", " <td>Easy Park / Park Board</td>\n", " <td>West End</td>\n", " <td>{'lat': 49.278736, 'lon': -123.137209}</td>\n", " <td>POINT (-123.13721 49.27874)</td>\n", " </tr>\n", " <tr>\n", " <th>41</th>\n", " <td>775 Hamilton St</td>\n", " <td>Easypark</td>\n", " <td>Downtown</td>\n", " <td>{'lat': 49.2795168591257, 'lon': -123.11516601...</td>\n", " <td>POINT (-123.11517 49.27952)</td>\n", " </tr>\n", " <tr>\n", " <th>42</th>\n", " <td>1 Kingsway</td>\n", " <td>Easypark</td>\n", " <td>Mount Pleasant</td>\n", " <td>{'lat': 49.2641313946177, 'lon': -123.10017321...</td>\n", " <td>POINT (-123.10017 49.26413)</td>\n", " </tr>\n", " <tr>\n", " <th>43</th>\n", " <td>272 E 53rd Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>Sunset</td>\n", " <td>{'lat': 49.222282, 'lon': -123.10009}</td>\n", " <td>POINT (-123.10009 49.22228)</td>\n", " </tr>\n", " <tr>\n", " <th>44</th>\n", " <td>1025 W 49th Ave</td>\n", " <td>Telus / Retailer</td>\n", " <td>Oakridge</td>\n", " <td>{'lat': 49.226886, 'lon': -123.129085}</td>\n", " <td>POINT (-123.12908 49.22689)</td>\n", " </tr>\n", " <tr>\n", " <th>45</th>\n", " <td>959-979 Mainland St</td>\n", " <td>City of Vancouver</td>\n", " <td>Downtown</td>\n", " <td>{'lat': 49.27684, 'lon': -123.11887}</td>\n", " <td>POINT (-123.11887 49.27684)</td>\n", " </tr>\n", " <tr>\n", " <th>46</th>\n", " <td>453 W. 12th Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>Mount Pleasant</td>\n", " <td>{'lat': 49.261154, 'lon': -123.11318}</td>\n", " <td>POINT (-123.11318 49.26115)</td>\n", " </tr>\n", " <tr>\n", " <th>47</th>\n", " <td>453 W. 12th Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>Mount Pleasant</td>\n", " <td>{'lat': 49.261093, 'lon': -123.11318}</td>\n", " <td>POINT (-123.11318 49.26109)</td>\n", " </tr>\n", " <tr>\n", " <th>48</th>\n", " <td>456 Beach Crescent</td>\n", " <td>City of Vancouver</td>\n", " <td>Downtown</td>\n", " <td>{'lat': 49.272594, 'lon': -123.12836}</td>\n", " <td>POINT (-123.12836 49.27259)</td>\n", " </tr>\n", " <tr>\n", " <th>49</th>\n", " <td>646 E. 44th Ave</td>\n", " <td>City of Vancouver</td>\n", " <td>Sunset</td>\n", " <td>{'lat': 49.229904302024, 'lon': -123.091350766...</td>\n", " <td>POINT (-123.09135 49.2299)</td>\n", " </tr>\n", " <tr>\n", " <th>50</th>\n", " <td>3350 Maquinna Dr</td>\n", " <td>City of Vancouver</td>\n", " <td>Killarney</td>\n", " <td>{'lat': 49.215032, 'lon': -123.032341}</td>\n", " <td>POINT (-123.03234 49.21503)</td>\n", " </tr>\n", " <tr>\n", " <th>51</th>\n", " <td>860 Richards St</td>\n", " <td>City of Vancouver</td>\n", " <td>Downtown</td>\n", " <td>{'lat': 49.279403, 'lon': -123.118908}</td>\n", " <td>POINT (-123.11891 49.2794)</td>\n", " </tr>\n", " <tr>\n", " <th>52</th>\n", " <td>1664 E. 11th</td>\n", " <td>City of Vancouver</td>\n", " <td>Kensington-Cedar Cottage</td>\n", " <td>{'lat': 49.26043, 'lon': -123.070163}</td>\n", " <td>POINT (-123.07016 49.26043)</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " address lot_operator \\\n", "0 Beach Ave. @ Cardero St Easy Park / Park Board \n", "1 1 Kingsway Easypark \n", "2 4575 Clancy Loranger Way Park Board \n", "3 845 Avison Way Vancouver Aquarium \n", "4 273 E 53rd Ave City of Vancouver \n", "5 3311 E. Hastings City of Vancouver \n", "6 959-979 Mainland St City of Vancouver \n", "7 451 Beach Crescent City of Vancouver \n", "8 646 E. 44th Ave City of Vancouver \n", "9 5175 Dumfries St City of Vancouver \n", "10 646 E. 44th Ave City of Vancouver \n", "11 3350 Maquinna Dr City of Vancouver \n", "12 3350 Maquinna Dr City of Vancouver \n", "13 6260 Killarney St City of Vancouver \n", "14 6260 Killarney St City of Vancouver \n", "15 2099 Beach Ave City of Vancouver \n", "16 860 Richards St City of Vancouver \n", "17 860 Richards St City of Vancouver \n", "18 1200 Thornton St City of Vancouver \n", "19 1201 Thornton St City of Vancouver \n", "20 2221 Main St Easypark \n", "21 4575 Clancy Loranger Way Park Board \n", "22 701 W. Georgia St. Easy Park Lot \n", "23 Beach Ave. @ Broughton St Easy Park / Park Board \n", "24 1305 Arbutus St City of Vancouver \n", "25 4575 Clancy Loranger Way Park Board \n", "26 1100 Granville St Best Western \n", "27 1001 Cotton Dr Park Board \n", "28 225 N Renfrew St Pacific National Exhibition \n", "29 453 W. 12th Ave City of Vancouver \n", "30 646 E. 44th Ave City of Vancouver \n", "31 5175 Dumfries St City of Vancouver \n", "32 6260 Killarney St City of Vancouver \n", "33 2099 Beach Ave City of Vancouver \n", "34 2099 Beach Ave City of Vancouver \n", "35 1664 E. 11th City of Vancouver \n", "36 3360 Victoria Dr City of Vancouver \n", "37 3360 Victoria Dr City of Vancouver \n", "38 3360 Victoria Dr City of Vancouver \n", "39 845 Avison Way Vancouver Aquarium \n", "40 Beach Ave. @ Bute St Easy Park / Park Board \n", "41 775 Hamilton St Easypark \n", "42 1 Kingsway Easypark \n", "43 272 E 53rd Ave City of Vancouver \n", "44 1025 W 49th Ave Telus / Retailer \n", "45 959-979 Mainland St City of Vancouver \n", "46 453 W. 12th Ave City of Vancouver \n", "47 453 W. 12th Ave City of Vancouver \n", "48 456 Beach Crescent City of Vancouver \n", "49 646 E. 44th Ave City of Vancouver \n", "50 3350 Maquinna Dr City of Vancouver \n", "51 860 Richards St City of Vancouver \n", "52 1664 E. 11th City of Vancouver \n", "\n", " geo_local_area \\\n", "0 West End \n", "1 Mount Pleasant \n", "2 Riley Park \n", "3 None \n", "4 Sunset \n", "5 Hastings-Sunrise \n", "6 Downtown \n", "7 Downtown \n", "8 Sunset \n", "9 Kensington-Cedar Cottage \n", "10 Kensington-Cedar Cottage \n", "11 Killarney \n", "12 Killarney \n", "13 Killarney \n", "14 Killarney \n", "15 None \n", "16 Downtown \n", "17 Downtown \n", "18 Strathcona \n", "19 Strathcona \n", "20 Mount Pleasant \n", "21 Riley Park \n", "22 Downtown \n", "23 West End \n", "24 Kitsilano \n", "25 Riley Park \n", "26 Downtown \n", "27 Grandview-Woodland \n", "28 Hastings-Sunrise \n", "29 Mount Pleasant \n", "30 Sunset \n", "31 Kensington-Cedar Cottage \n", "32 Killarney \n", "33 None \n", "34 None \n", "35 Kensington-Cedar Cottage \n", "36 Kensington-Cedar Cottage \n", "37 Kensington-Cedar Cottage \n", "38 Kensington-Cedar Cottage \n", "39 None \n", "40 West End \n", "41 Downtown \n", "42 Mount Pleasant \n", "43 Sunset \n", "44 Oakridge \n", "45 Downtown \n", "46 Mount Pleasant \n", "47 Mount Pleasant \n", "48 Downtown \n", "49 Sunset \n", "50 Killarney \n", "51 Downtown \n", "52 Kensington-Cedar Cottage \n", "\n", " geo_point_2d \\\n", "0 {'lat': 49.283155, 'lon': -123.142173} \n", "1 {'lat': 49.2641594, 'lon': -123.1002054} \n", "2 {'lat': 49.243665, 'lon': -123.106578} \n", "3 {'lat': 49.299534, 'lon': -123.13022} \n", "4 {'lat': 49.222235022762, 'lon': -123.100095218... \n", "5 {'lat': 49.281369, 'lon': -123.033786} \n", "6 {'lat': 49.2769232201811, 'lon': -123.11926106... \n", "7 {'lat': 49.272514, 'lon': -123.12833} \n", "8 {'lat': 49.229928611422, 'lon': -123.09135336453} \n", "9 {'lat': 49.2385063171386, 'lon': -123.07548522... \n", "10 {'lat': 49.237778, 'lon': -123.074953} \n", "11 {'lat': 49.215036, 'lon': -123.032265} \n", "12 {'lat': 49.215032, 'lon': -123.032306} \n", "13 {'lat': 49.226948, 'lon': -123.044804} \n", "14 {'lat': 49.226955, 'lon': -123.044843} \n", "15 {'lat': 49.291034, 'lon': -123.145028} \n", "16 {'lat': 49.279351, 'lon': -123.118962} \n", "17 {'lat': 49.279373, 'lon': -123.118924} \n", "18 {'lat': 49.274598, 'lon': -123.09242} \n", "19 {'lat': 49.27454, 'lon': -123.092452} \n", "20 {'lat': 49.265235, 'lon': -123.101577} \n", "21 {'lat': 49.243681, 'lon': -123.106551} \n", "22 {'lat': 49.283672, 'lon': -123.1173} \n", "23 {'lat': 49.281483, 'lon': -123.139793} \n", "24 {'lat': 49.273797, 'lon': -123.152654} \n", "25 {'lat': 49.243665, 'lon': -123.106617} \n", "26 {'lat': 49.277692, 'lon': -123.124668} \n", "27 {'lat': 49.276066, 'lon': -123.071246} \n", "28 {'lat': 49.286384777239, 'lon': -123.043494521... \n", "29 {'lat': 49.26122, 'lon': -123.11374} \n", "30 {'lat': 49.2297756397356, 'lon': -123.09136002... \n", "31 {'lat': 49.2385597229003, 'lon': -123.07533264... \n", "32 {'lat': 49.226968, 'lon': -123.044818} \n", "33 {'lat': 49.291028, 'lon': -123.14499} \n", "34 {'lat': 49.291042, 'lon': -123.145062} \n", "35 {'lat': 49.260433, 'lon': -123.070249} \n", "36 {'lat': 49.256637, 'lon': -123.065749} \n", "37 {'lat': 49.256591, 'lon': -123.06573} \n", "38 {'lat': 49.256537, 'lon': -123.065717} \n", "39 {'lat': 49.300472054096, 'lon': -123.130229213... \n", "40 {'lat': 49.278736, 'lon': -123.137209} \n", "41 {'lat': 49.2795168591257, 'lon': -123.11516601... \n", "42 {'lat': 49.2641313946177, 'lon': -123.10017321... \n", "43 {'lat': 49.222282, 'lon': -123.10009} \n", "44 {'lat': 49.226886, 'lon': -123.129085} \n", "45 {'lat': 49.27684, 'lon': -123.11887} \n", "46 {'lat': 49.261154, 'lon': -123.11318} \n", "47 {'lat': 49.261093, 'lon': -123.11318} \n", "48 {'lat': 49.272594, 'lon': -123.12836} \n", "49 {'lat': 49.229904302024, 'lon': -123.091350766... \n", "50 {'lat': 49.215032, 'lon': -123.032341} \n", "51 {'lat': 49.279403, 'lon': -123.118908} \n", "52 {'lat': 49.26043, 'lon': -123.070163} \n", "\n", " geometry \n", "0 POINT (-123.14217 49.28316) \n", "1 POINT (-123.10021 49.26416) \n", "2 POINT (-123.10658 49.24366) \n", "3 POINT (-123.13022 49.29953) \n", "4 POINT (-123.1001 49.22224) \n", "5 POINT (-123.03379 49.28137) \n", "6 POINT (-123.11926 49.27692) \n", "7 POINT (-123.12833 49.27251) \n", "8 POINT (-123.09135 49.22993) \n", "9 POINT (-123.07549 49.23851) \n", "10 POINT (-123.07495 49.23778) \n", "11 POINT (-123.03226 49.21504) \n", "12 POINT (-123.03231 49.21503) \n", "13 POINT (-123.0448 49.22695) \n", "14 POINT (-123.04484 49.22696) \n", "15 POINT (-123.14503 49.29103) \n", "16 POINT (-123.11896 49.27935) \n", "17 POINT (-123.11892 49.27937) \n", "18 POINT (-123.09242 49.2746) \n", "19 POINT (-123.09245 49.27454) \n", "20 POINT (-123.10158 49.26524) \n", "21 POINT (-123.10655 49.24368) \n", "22 POINT (-123.1173 49.28367) \n", "23 POINT (-123.13979 49.28148) \n", "24 POINT (-123.15265 49.2738) \n", "25 POINT (-123.10662 49.24366) \n", "26 POINT (-123.12467 49.27769) \n", "27 POINT (-123.07125 49.27607) \n", "28 POINT (-123.04349 49.28638) \n", "29 POINT (-123.11374 49.26122) \n", "30 POINT (-123.09136 49.22978) \n", "31 POINT (-123.07533 49.23856) \n", "32 POINT (-123.04482 49.22697) \n", "33 POINT (-123.14499 49.29103) \n", "34 POINT (-123.14506 49.29104) \n", "35 POINT (-123.07025 49.26043) \n", "36 POINT (-123.06575 49.25664) \n", "37 POINT (-123.06573 49.25659) \n", "38 POINT (-123.06572 49.25654) \n", "39 POINT (-123.13023 49.30047) \n", "40 POINT (-123.13721 49.27874) \n", "41 POINT (-123.11517 49.27952) \n", "42 POINT (-123.10017 49.26413) \n", "43 POINT (-123.10009 49.22228) \n", "44 POINT (-123.12908 49.22689) \n", "45 POINT (-123.11887 49.27684) \n", "46 POINT (-123.11318 49.26115) \n", "47 POINT (-123.11318 49.26109) \n", "48 POINT (-123.12836 49.27259) \n", "49 POINT (-123.09135 49.2299) \n", "50 POINT (-123.03234 49.21503) \n", "51 POINT (-123.11891 49.2794) \n", "52 POINT (-123.07016 49.26043) " ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# We can run this UDF locally with `fused.run(udf)`\n", "fused.run(ev_chargers)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'description': 'This UDF returns the location of all the electric chargers in Vancouver as a GeoDataFrame with the name of the chargers and their lat lon',\n", " 'parameters': [{'name': '', 'type': ''}]}" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ev_charger_mcp_metadata = {\n", " \"description\": \"This UDF returns the location of all the electric chargers in Vancouver as a GeoDataFrame with the name of the chargers and their lat lon\",\n", " \"parameters\": [\n", " {\n", " \"name\": \"\",\n", " \"type\": \"\",\n", " }\n", " ],\n", "}\n", "\n", "ev_charger_mcp_metadata" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "# We add this new UDF + mcp_metadata to the same agent\n", "common.save_to_agent(\n", " agent_json_path=os.path.join(WORKING_DIR, \"agents.json\"),\n", " udf=ev_chargers,\n", " agent_name=AGENT_NAME,\n", " udf_name=\"electric_vehicle_chargers_in_vancouver\",\n", " mcp_metadata=ev_charger_mcp_metadata,\n", ")" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [], "source": [ "@fused.udf\n", "def yearly_crime_amount(\n", " up_to_year: int = 2021, \n", " lat: float=49.2806, \n", " lon: float=-123.1259,\n", " buffer_amount: float = 1000,\n", "):\n", " \"\"\"\n", " This UDF takes the lat / lon + buffer amount of any area within Vancouver and returns\n", " the number of different crimes per category that happened that year\n", " \"\"\"\n", " import datetime\n", " import geopandas as gpd\n", " import pandas as pd\n", " import shapely\n", " from shapely.geometry import Point\n", "\n", " current_year = int(datetime.datetime.now().year)\n", " \n", " list_of_years_to_process = [year for year in range(up_to_year, current_year)]\n", " print(f\"{list_of_years_to_process=}\")\n", "\n", " yearly_type_summaries = {}\n", " yearly_crime_location = {}\n", "\n", " @fused.cache()\n", " def getting_yearly_data(year):\n", " path = f\"s3://fused-users/fused/max/maxar_ted_demo/crimedata_csv_AllNeighbourhoods_{str(year)}.csv\"\n", " \n", " df = pd.read_csv(path)\n", " df[\"geometry\"] = gpd.points_from_xy(df[\"X\"], df[\"Y\"], crs=\"EPSG:32610\")\n", " \n", " gdf = gpd.GeoDataFrame(df)\n", " print(gdf.shape)\n", " gdf.to_crs(4326, inplace=True)\n", " \n", " aoi_gdf = gpd.GeoDataFrame(geometry=[Point(lon, lat)], crs=\"EPSG:4326\")\n", " \n", " # Project to a local UTM projection for accurate buffering in meters\n", " # Get UTM zone from longitude\n", " utm_zone = int(((lon + 180) / 6) % 60) + 1\n", " hemisphere = 'north' if lat >= 0 else 'south'\n", " utm_crs = f\"+proj=utm +zone={utm_zone} +{hemisphere} +ellps=WGS84 +datum=WGS84 +units=m +no_defs\"\n", " \n", " gdf_utm = aoi_gdf.to_crs(utm_crs)\n", " gdf_utm['geometry'] = gdf_utm.buffer(buffer_amount)\n", " gdf_buffered = gdf_utm.to_crs(\"EPSG:4326\")\n", " \n", " clipped_gdf = gpd.sjoin(gdf, gdf_buffered, predicate='intersects', how='inner')\n", "\n", " # Getting stats\n", " number_of_crimes = pd.DataFrame({f\"number_crimes\": [clipped_gdf.shape[0]]})\n", " grouped_by_type = clipped_gdf.groupby(\"TYPE\").size().reset_index(name=\"count\")\n", "\n", " # print(f\"{type(grouped_by_type)=}\")\n", " # print(f\"{grouped_by_type=}\")\n", " # print(f\"{grouped_by_type.columns=}\")\n", " return clipped_gdf, grouped_by_type\n", " \n", " for year in list_of_years_to_process:\n", " clipped_gdf, grouped_by_type = getting_yearly_data(year)\n", " yearly_crime_location[year] = clipped_gdf\n", " yearly_type_summaries[year] = grouped_by_type\n", "\n", " df_all_years = pd.concat(\n", " [df.assign(year=year) for year, df in yearly_type_summaries.items()],\n", " ignore_index=True\n", " )\n", " \n", " df = pd.concat([df.assign(year=year) for year, df in yearly_type_summaries.items()], ignore_index=True)\n", " print(f\"{df_all_years=}\")\n", " return df_all_years\n" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>TYPE</th>\n", " <th>count</th>\n", " <th>year</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>Break and Enter Commercial</td>\n", " <td>693</td>\n", " <td>2020</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>Break and Enter Residential/Other</td>\n", " <td>98</td>\n", " <td>2020</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>Mischief</td>\n", " <td>1296</td>\n", " <td>2020</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>Other Theft</td>\n", " <td>2252</td>\n", " <td>2020</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>Theft from Vehicle</td>\n", " <td>2264</td>\n", " <td>2020</td>\n", " </tr>\n", " <tr>\n", " <th>5</th>\n", " <td>Theft of Bicycle</td>\n", " <td>334</td>\n", " <td>2020</td>\n", " </tr>\n", " <tr>\n", " <th>6</th>\n", " <td>Theft of Vehicle</td>\n", " <td>84</td>\n", " <td>2020</td>\n", " </tr>\n", " <tr>\n", " <th>7</th>\n", " <td>Vehicle Collision or Pedestrian Struck (with F...</td>\n", " <td>1</td>\n", " <td>2020</td>\n", " </tr>\n", " <tr>\n", " <th>8</th>\n", " <td>Vehicle Collision or Pedestrian Struck (with I...</td>\n", " <td>97</td>\n", " <td>2020</td>\n", " </tr>\n", " <tr>\n", " <th>9</th>\n", " <td>Break and Enter Commercial</td>\n", " <td>525</td>\n", " <td>2021</td>\n", " </tr>\n", " <tr>\n", " <th>10</th>\n", " <td>Break and Enter Residential/Other</td>\n", " <td>70</td>\n", " <td>2021</td>\n", " </tr>\n", " <tr>\n", " <th>11</th>\n", " <td>Mischief</td>\n", " <td>1276</td>\n", " <td>2021</td>\n", " </tr>\n", " <tr>\n", " <th>12</th>\n", " <td>Other Theft</td>\n", " <td>2420</td>\n", " <td>2021</td>\n", " </tr>\n", " <tr>\n", " <th>13</th>\n", " <td>Theft from Vehicle</td>\n", " <td>1650</td>\n", " <td>2021</td>\n", " </tr>\n", " <tr>\n", " <th>14</th>\n", " <td>Theft of Bicycle</td>\n", " <td>240</td>\n", " <td>2021</td>\n", " </tr>\n", " <tr>\n", " <th>15</th>\n", " <td>Theft of Vehicle</td>\n", " <td>94</td>\n", " <td>2021</td>\n", " </tr>\n", " <tr>\n", " <th>16</th>\n", " <td>Vehicle Collision or Pedestrian Struck (with F...</td>\n", " <td>2</td>\n", " <td>2021</td>\n", " </tr>\n", " <tr>\n", " <th>17</th>\n", " <td>Vehicle Collision or Pedestrian Struck (with I...</td>\n", " <td>110</td>\n", " <td>2021</td>\n", " </tr>\n", " <tr>\n", " <th>18</th>\n", " <td>Break and Enter Commercial</td>\n", " <td>486</td>\n", " <td>2022</td>\n", " </tr>\n", " <tr>\n", " <th>19</th>\n", " <td>Break and Enter Residential/Other</td>\n", " <td>67</td>\n", " <td>2022</td>\n", " </tr>\n", " <tr>\n", " <th>20</th>\n", " <td>Mischief</td>\n", " <td>1336</td>\n", " <td>2022</td>\n", " </tr>\n", " <tr>\n", " <th>21</th>\n", " <td>Other Theft</td>\n", " <td>2889</td>\n", " <td>2022</td>\n", " </tr>\n", " <tr>\n", " <th>22</th>\n", " <td>Theft from Vehicle</td>\n", " <td>1716</td>\n", " <td>2022</td>\n", " </tr>\n", " <tr>\n", " <th>23</th>\n", " <td>Theft of Bicycle</td>\n", " <td>351</td>\n", " <td>2022</td>\n", " </tr>\n", " <tr>\n", " <th>24</th>\n", " <td>Theft of Vehicle</td>\n", " <td>77</td>\n", " <td>2022</td>\n", " </tr>\n", " <tr>\n", " <th>25</th>\n", " <td>Vehicle Collision or Pedestrian Struck (with F...</td>\n", " <td>3</td>\n", " <td>2022</td>\n", " </tr>\n", " <tr>\n", " <th>26</th>\n", " <td>Vehicle Collision or Pedestrian Struck (with I...</td>\n", " <td>140</td>\n", " <td>2022</td>\n", " </tr>\n", " <tr>\n", " <th>27</th>\n", " <td>Break and Enter Commercial</td>\n", " <td>441</td>\n", " <td>2023</td>\n", " </tr>\n", " <tr>\n", " <th>28</th>\n", " <td>Break and Enter Residential/Other</td>\n", " <td>72</td>\n", " <td>2023</td>\n", " </tr>\n", " <tr>\n", " <th>29</th>\n", " <td>Mischief</td>\n", " <td>1650</td>\n", " <td>2023</td>\n", " </tr>\n", " <tr>\n", " <th>30</th>\n", " <td>Other Theft</td>\n", " <td>3636</td>\n", " <td>2023</td>\n", " </tr>\n", " <tr>\n", " <th>31</th>\n", " <td>Theft from Vehicle</td>\n", " <td>1801</td>\n", " <td>2023</td>\n", " </tr>\n", " <tr>\n", " <th>32</th>\n", " <td>Theft of Bicycle</td>\n", " <td>232</td>\n", " <td>2023</td>\n", " </tr>\n", " <tr>\n", " <th>33</th>\n", " <td>Theft of Vehicle</td>\n", " <td>79</td>\n", " <td>2023</td>\n", " </tr>\n", " <tr>\n", " <th>34</th>\n", " <td>Vehicle Collision or Pedestrian Struck (with I...</td>\n", " <td>174</td>\n", " <td>2023</td>\n", " </tr>\n", " <tr>\n", " <th>35</th>\n", " <td>Break and Enter Commercial</td>\n", " <td>304</td>\n", " <td>2024</td>\n", " </tr>\n", " <tr>\n", " <th>36</th>\n", " <td>Break and Enter Residential/Other</td>\n", " <td>68</td>\n", " <td>2024</td>\n", " </tr>\n", " <tr>\n", " <th>37</th>\n", " <td>Mischief</td>\n", " <td>1364</td>\n", " <td>2024</td>\n", " </tr>\n", " <tr>\n", " <th>38</th>\n", " <td>Other Theft</td>\n", " <td>4346</td>\n", " <td>2024</td>\n", " </tr>\n", " <tr>\n", " <th>39</th>\n", " <td>Theft from Vehicle</td>\n", " <td>1652</td>\n", " <td>2024</td>\n", " </tr>\n", " <tr>\n", " <th>40</th>\n", " <td>Theft of Bicycle</td>\n", " <td>215</td>\n", " <td>2024</td>\n", " </tr>\n", " <tr>\n", " <th>41</th>\n", " <td>Theft of Vehicle</td>\n", " <td>59</td>\n", " <td>2024</td>\n", " </tr>\n", " <tr>\n", " <th>42</th>\n", " <td>Vehicle Collision or Pedestrian Struck (with F...</td>\n", " <td>1</td>\n", " <td>2024</td>\n", " </tr>\n", " <tr>\n", " <th>43</th>\n", " <td>Vehicle Collision or Pedestrian Struck (with I...</td>\n", " <td>163</td>\n", " <td>2024</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " TYPE count year\n", "0 Break and Enter Commercial 693 2020\n", "1 Break and Enter Residential/Other 98 2020\n", "2 Mischief 1296 2020\n", "3 Other Theft 2252 2020\n", "4 Theft from Vehicle 2264 2020\n", "5 Theft of Bicycle 334 2020\n", "6 Theft of Vehicle 84 2020\n", "7 Vehicle Collision or Pedestrian Struck (with F... 1 2020\n", "8 Vehicle Collision or Pedestrian Struck (with I... 97 2020\n", "9 Break and Enter Commercial 525 2021\n", "10 Break and Enter Residential/Other 70 2021\n", "11 Mischief 1276 2021\n", "12 Other Theft 2420 2021\n", "13 Theft from Vehicle 1650 2021\n", "14 Theft of Bicycle 240 2021\n", "15 Theft of Vehicle 94 2021\n", "16 Vehicle Collision or Pedestrian Struck (with F... 2 2021\n", "17 Vehicle Collision or Pedestrian Struck (with I... 110 2021\n", "18 Break and Enter Commercial 486 2022\n", "19 Break and Enter Residential/Other 67 2022\n", "20 Mischief 1336 2022\n", "21 Other Theft 2889 2022\n", "22 Theft from Vehicle 1716 2022\n", "23 Theft of Bicycle 351 2022\n", "24 Theft of Vehicle 77 2022\n", "25 Vehicle Collision or Pedestrian Struck (with F... 3 2022\n", "26 Vehicle Collision or Pedestrian Struck (with I... 140 2022\n", "27 Break and Enter Commercial 441 2023\n", "28 Break and Enter Residential/Other 72 2023\n", "29 Mischief 1650 2023\n", "30 Other Theft 3636 2023\n", "31 Theft from Vehicle 1801 2023\n", "32 Theft of Bicycle 232 2023\n", "33 Theft of Vehicle 79 2023\n", "34 Vehicle Collision or Pedestrian Struck (with I... 174 2023\n", "35 Break and Enter Commercial 304 2024\n", "36 Break and Enter Residential/Other 68 2024\n", "37 Mischief 1364 2024\n", "38 Other Theft 4346 2024\n", "39 Theft from Vehicle 1652 2024\n", "40 Theft of Bicycle 215 2024\n", "41 Theft of Vehicle 59 2024\n", "42 Vehicle Collision or Pedestrian Struck (with F... 1 2024\n", "43 Vehicle Collision or Pedestrian Struck (with I... 163 2024" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fused.run(yearly_crime_amount, up_to_year=2020)" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'description': \"\\nThe User-Defined Function (UDF) 'yearly_crime_per_category' is designed to perform a geo-statistical analysis of crime data for a specific area within Vancouver based on the geographical coordinates provided. The function calculates the total number of crimes committed yearly since a specified year up to the current year within a specific radius of the given coordinates.\\n\\nInput Parameters:\\nThe function takes four parameters: 'up_to_year', 'lat', 'lon', and 'buffer_amount'. \\n1. 'up_to_year': This integer value specifies the year since when the analysis should be performed up till the current year. The default value is set at 2021. \\n2. 'lat' and 'lon': These float values define the geographical coordinates of the area of interest, specifically, latitude('lat') and longitude('lon'). These values are set to default to a location in Vancouver city, but must be redefined by the users according to the area for which data analysis is to be done. \\n3. 'buffer_amount': This float value represents the radius (in meters) around the defined coordinate position within which the crime data analysis would be calculated. The buffer amount defaults to 1000 meters.\\n\\nFunctionality and Output: \\nThe function reads the crime data CSV files from an S3 bucket path for each year, starting from the specified 'up_to_year' to the current year. It then processes and clips the dataset according to the location and buffer amount parameter precisely. \\nAfterward, it performs a grouped statistical analysis based on various crime categories, calculates the total number of crimes, and returns progressive data about the yearly crime rates and their respective categories within the specified location buffer.\\n\\nTechnical Details and Limitations:\\n1. This UDF operates by using several specialized libraries including 'pandas', 'geopandas', 'shapely', and datetime. It converts CSV data into geospatial data, and then adjusts the coordinate system for accurate buffer calculations. It uses spatial join operations to select the relevant data within the area of interest.\\n2. The function assumes the availability of the requisite crime data CSV files for all the years from the given 'up_to_year' up till the current year at the specified S3 path.\\n3. The accuracy of the function heavily depends on the quality, detail, and format of the input CSV files, as well as the appropriateness and correctness of the provided geographical parameters.\\n4. The function analyzes the crime data per year. Thus, if a wide range 'up_to_year' is given, the function can take a substantial amount of time due to the large amount of data processing involved.\\n\\nBy carefully considering the input parameters and limitations, the 'yearly_crime_per_category' UDF proves to be a powerful tool in performing progressive geo-analysis of crime data based on geographical coordinates.\\n\",\n", " 'parameters': [{'name': 'up_to_year', 'type': 'int'},\n", " {'name': 'lat', 'type': 'float'},\n", " {'name': 'lon', 'type': 'float'},\n", " {'name': 'buffer_amount', 'type': 'float'}]}" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "yearly_crime_mcp_metadata = {\n", " \"description\": \"\"\"\n", "The User-Defined Function (UDF) 'yearly_crime_per_category' is designed to perform a geo-statistical analysis of crime data for a specific area within Vancouver based on the geographical coordinates provided. The function calculates the total number of crimes committed yearly since a specified year up to the current year within a specific radius of the given coordinates.\n", "\n", "Input Parameters:\n", "The function takes four parameters: 'up_to_year', 'lat', 'lon', and 'buffer_amount'. \n", "1. 'up_to_year': This integer value specifies the year since when the analysis should be performed up till the current year. The default value is set at 2021. \n", "2. 'lat' and 'lon': These float values define the geographical coordinates of the area of interest, specifically, latitude('lat') and longitude('lon'). These values are set to default to a location in Vancouver city, but must be redefined by the users according to the area for which data analysis is to be done. \n", "3. 'buffer_amount': This float value represents the radius (in meters) around the defined coordinate position within which the crime data analysis would be calculated. The buffer amount defaults to 1000 meters.\n", "\n", "Functionality and Output: \n", "The function reads the crime data CSV files from an S3 bucket path for each year, starting from the specified 'up_to_year' to the current year. It then processes and clips the dataset according to the location and buffer amount parameter precisely. \n", "Afterward, it performs a grouped statistical analysis based on various crime categories, calculates the total number of crimes, and returns progressive data about the yearly crime rates and their respective categories within the specified location buffer.\n", "\n", "Technical Details and Limitations:\n", "1. This UDF operates by using several specialized libraries including 'pandas', 'geopandas', 'shapely', and datetime. It converts CSV data into geospatial data, and then adjusts the coordinate system for accurate buffer calculations. It uses spatial join operations to select the relevant data within the area of interest.\n", "2. The function assumes the availability of the requisite crime data CSV files for all the years from the given 'up_to_year' up till the current year at the specified S3 path.\n", "3. The accuracy of the function heavily depends on the quality, detail, and format of the input CSV files, as well as the appropriateness and correctness of the provided geographical parameters.\n", "4. The function analyzes the crime data per year. Thus, if a wide range 'up_to_year' is given, the function can take a substantial amount of time due to the large amount of data processing involved.\n", "\n", "By carefully considering the input parameters and limitations, the 'yearly_crime_per_category' UDF proves to be a powerful tool in performing progressive geo-analysis of crime data based on geographical coordinates.\n", "\"\"\",\n", " \"parameters\": [\n", " {\n", " \"name\": \"up_to_year\",\n", " \"type\": \"int\"\n", " },\n", " {\n", " \"name\": \"lat\",\n", " \"type\": \"float\"\n", " },\n", " {\n", " \"name\": \"lon\",\n", " \"type\": \"float\"\n", " },\n", " {\n", " \"name\": \"buffer_amount\",\n", " \"type\": \"float\"\n", " }\n", "],\n", "}\n", "\n", "yearly_crime_mcp_metadata" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [], "source": [ "# We add this new UDF + mcp_metadata to the same agent\n", "common.save_to_agent(\n", " agent_json_path=os.path.join(WORKING_DIR, \"agents.json\"),\n", " udf=yearly_crime_amount,\n", " agent_name=AGENT_NAME,\n", " udf_name=\"yearly_crime_amount\",\n", " mcp_metadata=yearly_crime_mcp_metadata,\n", ")" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "@fused.udf\n", "def ookla_internet_speed(bounds: fused.types.Bounds=None, lat: float=37.7749, lon: float=-122.4194):\n", " \n", " file_path='s3://ookla-open-data/parquet/performance/type=mobile/year=2024/quarter=3/2024-07-01_performance_mobile_tiles.parquet'\n", " \n", " # Load pinned versions of utility functions.\n", " utils = fused.load(\"https://github.com/fusedio/udfs/tree/ee9bec5/public/common/\").utils\n", " \n", " # Sample usage: Set default lat/lon for San Francisco if none provided\n", " if lat is None and lon is None and bounds is None:\n", " print(\"Using sample coordinates for San Francisco\")\n", " lat = 37.7749\n", " lon = -122.4194\n", " \n", " # Check if we're using point query or bounds\n", " if lat is not None and lon is not None:\n", " # Create a small bounding box around the input lat/lon\n", " buffer = 0.01 # ~1km at equator\n", " total_bounds = [lon - buffer, lat - buffer, lon + buffer, lat + buffer]\n", " using_point_query = True\n", " else:\n", " # Use the provided bounds\n", " total_bounds = bounds.total_bounds\n", " using_point_query = False\n", " \n", " @fused.cache\n", " def get_data(total_bounds, file_path, h3_size):\n", " con = utils.duckdb_connect()\n", " # DuckDB query to:\n", " # 1. Convert lat/long to H3 cells\n", " # 2. Calculate average download speed per cell\n", " # 3. Filter by geographic bounds\n", " qr=f'''select h3_latlng_to_cell(tile_y, tile_x, {h3_size}) as hex, \n", " avg(avg_d_kbps) as metric\n", " from read_parquet(\"{file_path}\") \n", " where 1=1\n", " and tile_x between {total_bounds[0]} and {total_bounds[2]}\n", " and tile_y between {total_bounds[1]} and {total_bounds[3]}\n", " group by 1\n", " ''' \n", " df = con.sql(qr).df()\n", " return df\n", " \n", " # Calculate H3 resolution based on zoom level or use a fixed high resolution for point queries \n", " if using_point_query:\n", " res = 8 # Use high resolution for point queries\n", " else:\n", " res_offset = 0\n", " res = max(min(int(2+bounds.z[0]/1.5),8)-res_offset,2)\n", " \n", " df = get_data(total_bounds, file_path, h3_size=res)\n", " \n", " # For point queries, find the closest H3 cell and return its speed\n", " if using_point_query:\n", " con = utils.duckdb_connect()\n", " \n", " # Convert the input lat/lon to an H3 cell\n", " point_cell_query = f'''\n", " SELECT h3_latlng_to_cell({lat}, {lon}, {res}) as point_hex\n", " '''\n", " point_cell_df = con.sql(point_cell_query).df()\n", " \n", " if not point_cell_df.empty:\n", " point_cell = point_cell_df['point_hex'].iloc[0]\n", " \n", " # Find the cell in our results that matches the point's cell\n", " if 'hex' in df.columns and not df.empty:\n", " point_speed = df[df['hex'] == point_cell]\n", " \n", " if not point_speed.empty:\n", " print(f\"Speed at location ({lat}, {lon}): {point_speed['metric'].iloc[0]} kbps\")\n", " point_speed.rename(columns={'metric': 'internet_speed_kbs'}, inplace=True)\n", " print(f\"{point_speed=}\")\n", " return point_speed\n", " else:\n", " print(f\"No exact match found for location ({lat}, {lon}). Returning all cells in area.\")\n", " else:\n", " print(f\"No data found for location ({lat}, {lon})\")\n", " \n", " print(df) \n", " return df\n" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>hex</th>\n", " <th>internet_speed_kbs</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>6</th>\n", " <td>613196570331971583</td>\n", " <td>373333.333333</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " hex internet_speed_kbs\n", "6 613196570331971583 373333.333333" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fused.run(ookla_internet_speed)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'description': \"This example demonstrates how Ookla's mobile performance data can be dynamically processed into an H3 hexagonal grid system. The network metrics are aggregated (averaging download speeds) for H3 hexes at a resolution that adapts based on the zoom level. The performance data comes from Ookla's global speed test infrastructure, capturing real-world mobile network performance across diverse network operators and technologies. The data is stored in Parquet format on S3, structured by year and quarter, allowing for efficient geographic querying and temporal analysis. The resulting hexagonal grid provides a standardized way to visualize and analyze mobile network performance patterns across different geographic scales and regions.\",\n", " 'parameters': [{'name': 'lat', 'type': 'float'},\n", " {'name': 'lon', 'type': 'float'}]}" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "internet_speed_mcp_metadata = {\n", " \"description\": \"This example demonstrates how Ookla's mobile performance data can be dynamically processed into an H3 hexagonal grid system. The network metrics are aggregated (averaging download speeds) for H3 hexes at a resolution that adapts based on the zoom level. The performance data comes from Ookla's global speed test infrastructure, capturing real-world mobile network performance across diverse network operators and technologies. The data is stored in Parquet format on S3, structured by year and quarter, allowing for efficient geographic querying and temporal analysis. The resulting hexagonal grid provides a standardized way to visualize and analyze mobile network performance patterns across different geographic scales and regions.\",\n", " \"parameters\": [\n", " {\n", " \"name\": \"lat\",\n", " \"type\": \"float\"\n", " },\n", " {\n", " \"name\": \"lon\",\n", " \"type\": \"float\"\n", " }\n", " ],\n", "}\n", "\n", "internet_speed_mcp_metadata" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "# We add this new UDF + mcp_metadata to the same agent\n", "common.save_to_agent(\n", " agent_json_path=os.path.join(WORKING_DIR, \"agents.json\"),\n", " udf=ookla_internet_speed,\n", " agent_name=AGENT_NAME,\n", " udf_name=\"internet_speeds_for_lat_lon\",\n", " mcp_metadata=internet_speed_mcp_metadata,\n", ")" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "@fused.udf\n", "def community_gardens_vancouver():\n", " import requests\n", " import geopandas as gpd\n", " from shapely.geometry import shape\n", "\n", " params = {\n", " \"limit\": -1, # setting to max of amount of request we can do\n", " }\n", "\n", " url = \"https://opendata.vancouver.ca/api/explore/v2.1/catalog/datasets/community-gardens-and-food-trees/records\"\n", " r = requests.get(url, params=params)\n", "\n", " gdf = gpd.GeoDataFrame(r.json()[\"results\"])\n", " gdf[\"geometry\"] = gdf[\"geom\"].apply(lambda x: shape(x[\"geometry\"]))\n", " gdf = gdf.set_geometry(\"geometry\")\n", " del gdf[\"geom\"]\n", "\n", " print(f\"{gdf.shape=}\")\n", "\n", " \n", "\n", " return gdf" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>mapid</th>\n", " <th>year_created</th>\n", " <th>name</th>\n", " <th>street_number</th>\n", " <th>street_direction</th>\n", " <th>street_name</th>\n", " <th>street_type</th>\n", " <th>merged_address</th>\n", " <th>number_of_plots</th>\n", " <th>number_of_food_trees</th>\n", " <th>notes</th>\n", " <th>food_tree_varieties</th>\n", " <th>other_food_assets</th>\n", " <th>jurisdiction</th>\n", " <th>steward_or_managing_organization</th>\n", " <th>public_e_mail</th>\n", " <th>website</th>\n", " <th>geo_local_area</th>\n", " <th>geo_point_2d</th>\n", " <th>geometry</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>FA002</td>\n", " <td>2014</td>\n", " <td>15th Avenue Coop</td>\n", " <td>1255</td>\n", " <td>E</td>\n", " <td>15th</td>\n", " <td>Av</td>\n", " <td>1255 E 15th Av, Vancouver, BC</td>\n", " <td>8.0</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Private</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Mount Pleasant</td>\n", " <td>{'lat': 49.2571193, 'lon': -123.0788387}</td>\n", " <td>POINT (-123.07884 49.25712)</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>FA003</td>\n", " <td>2008</td>\n", " <td>16 Oaks</td>\n", " <td>1018</td>\n", " <td>W</td>\n", " <td>16th</td>\n", " <td>Av</td>\n", " <td>1018 W 16th Av, Vancouver, BC</td>\n", " <td>55.0</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Private</td>\n", " <td>None</td>\n", " <td>oak.16th.garden@gmail.com</td>\n", " <td>None</td>\n", " <td>Shaughnessy</td>\n", " <td>{'lat': 49.2567482, 'lon': -123.1276645}</td>\n", " <td>POINT (-123.12766 49.25675)</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>FA007</td>\n", " <td>1942</td>\n", " <td>East Boulevard Allotment Plots</td>\n", " <td>7176</td>\n", " <td>E</td>\n", " <td>None</td>\n", " <td>Boulevard</td>\n", " <td>7176 E Boulevard, Vancouver, BC</td>\n", " <td>71.0</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>City</td>\n", " <td>City of Vancouver</td>\n", " <td>communitygardens@vancouver.ca</td>\n", " <td>None</td>\n", " <td>Kerrisdale</td>\n", " <td>{'lat': 49.2209834, 'lon': -123.1505481}</td>\n", " <td>POINT (-123.15055 49.22098)</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>FA009</td>\n", " <td>2010</td>\n", " <td>Atira Community Garden</td>\n", " <td>400</td>\n", " <td>None</td>\n", " <td>Hawks</td>\n", " <td>Av</td>\n", " <td>400 Hawks Av, Vancouver, BC</td>\n", " <td>15.0</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Private</td>\n", " <td>Atira Community Resources</td>\n", " <td>None</td>\n", " <td>http://www.atira.bc.ca/community-garden-kitchens</td>\n", " <td>Strathcona</td>\n", " <td>{'lat': 49.2810913, 'lon': -123.0871212}</td>\n", " <td>POINT (-123.08712 49.28109)</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>FA011</td>\n", " <td>2015</td>\n", " <td>BC Seniors Living Assoc. Garden</td>\n", " <td>3355</td>\n", " <td>E</td>\n", " <td>5 th</td>\n", " <td>Av</td>\n", " <td>3355 E 5 th Av, Vancouver, BC</td>\n", " <td>11.0</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Private</td>\n", " <td>BC Seniors Living Assoc. Beulah Homes Society</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Hastings-Sunrise</td>\n", " <td>{'lat': 49.266474, 'lon': -123.032578}</td>\n", " <td>POINT (-123.03258 49.26647)</td>\n", " </tr>\n", " <tr>\n", " <th>...</th>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " </tr>\n", " <tr>\n", " <th>95</th>\n", " <td>FA166</td>\n", " <td>2017</td>\n", " <td>HFBC Housing Foundation</td>\n", " <td>2085</td>\n", " <td>W</td>\n", " <td>5th</td>\n", " <td>Av</td>\n", " <td>2085 W 5th Av, Vancouver, BC</td>\n", " <td>12.0</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Private</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Kitsilano</td>\n", " <td>{'lat': 49.267434, 'lon': -123.152475}</td>\n", " <td>POINT (-123.15248 49.26743)</td>\n", " </tr>\n", " <tr>\n", " <th>96</th>\n", " <td>FA169</td>\n", " <td>2018</td>\n", " <td>Brightside Home Foundation - King's Daughters</td>\n", " <td>1400</td>\n", " <td>E</td>\n", " <td>11th</td>\n", " <td>Av</td>\n", " <td>1400 E 11th Av, Vancouver, BC</td>\n", " <td>3.0</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Private</td>\n", " <td>Brightside Community Homes Foundation</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Kensington-Cedar Cottage</td>\n", " <td>{'lat': 49.260319, 'lon': -123.075082}</td>\n", " <td>POINT (-123.07508 49.26032)</td>\n", " </tr>\n", " <tr>\n", " <th>97</th>\n", " <td>FA174</td>\n", " <td>2016</td>\n", " <td>HFBC Housing Foundation</td>\n", " <td>2924</td>\n", " <td>None</td>\n", " <td>Venables</td>\n", " <td>St</td>\n", " <td>2924 Venables St, Vancouver, BC</td>\n", " <td>12.0</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Private</td>\n", " <td>HFBC Housing Foundation</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Hastings-Sunrise</td>\n", " <td>{'lat': 49.276211, 'lon': -123.043281}</td>\n", " <td>POINT (-123.04328 49.27621)</td>\n", " </tr>\n", " <tr>\n", " <th>98</th>\n", " <td>FA176</td>\n", " <td>None</td>\n", " <td>John Hendry (Trout Lake) Park</td>\n", " <td>3300</td>\n", " <td>None</td>\n", " <td>Victoria</td>\n", " <td>Drive</td>\n", " <td>3300 Victoria Drive, Vancouver, BC</td>\n", " <td>4.0</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Park Board</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>https://cedarcottagefoodnetwork.com/projects-e...</td>\n", " <td>Kensington-Cedar Cottage</td>\n", " <td>{'lat': 49.255098, 'lon': -123.06367871}</td>\n", " <td>POINT (-123.06368 49.2551)</td>\n", " </tr>\n", " <tr>\n", " <th>99</th>\n", " <td>FA177</td>\n", " <td>2018</td>\n", " <td>Riley Park</td>\n", " <td>50</td>\n", " <td>E</td>\n", " <td>30th</td>\n", " <td>Av</td>\n", " <td>50 E 30th Av, Vancouver, BC</td>\n", " <td>20.0</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>None</td>\n", " <td>Park Board</td>\n", " <td>None</td>\n", " <td>info@rileyparkgarden.org</td>\n", " <td>https://www.rileyparkgarden.org/</td>\n", " <td>Riley Park</td>\n", " <td>{'lat': 49.24298494, 'lon': -123.10448685}</td>\n", " <td>POINT (-123.10449 49.24298)</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "<p>100 rows × 20 columns</p>\n", "</div>" ], "text/plain": [ " mapid year_created name \\\n", "0 FA002 2014 15th Avenue Coop \n", "1 FA003 2008 16 Oaks \n", "2 FA007 1942 East Boulevard Allotment Plots \n", "3 FA009 2010 Atira Community Garden \n", "4 FA011 2015 BC Seniors Living Assoc. Garden \n", ".. ... ... ... \n", "95 FA166 2017 HFBC Housing Foundation \n", "96 FA169 2018 Brightside Home Foundation - King's Daughters \n", "97 FA174 2016 HFBC Housing Foundation \n", "98 FA176 None John Hendry (Trout Lake) Park \n", "99 FA177 2018 Riley Park \n", "\n", " street_number street_direction street_name street_type \\\n", "0 1255 E 15th Av \n", "1 1018 W 16th Av \n", "2 7176 E None Boulevard \n", "3 400 None Hawks Av \n", "4 3355 E 5 th Av \n", ".. ... ... ... ... \n", "95 2085 W 5th Av \n", "96 1400 E 11th Av \n", "97 2924 None Venables St \n", "98 3300 None Victoria Drive \n", "99 50 E 30th Av \n", "\n", " merged_address number_of_plots number_of_food_trees \\\n", "0 1255 E 15th Av, Vancouver, BC 8.0 None \n", "1 1018 W 16th Av, Vancouver, BC 55.0 None \n", "2 7176 E Boulevard, Vancouver, BC 71.0 None \n", "3 400 Hawks Av, Vancouver, BC 15.0 None \n", "4 3355 E 5 th Av, Vancouver, BC 11.0 None \n", ".. ... ... ... \n", "95 2085 W 5th Av, Vancouver, BC 12.0 None \n", "96 1400 E 11th Av, Vancouver, BC 3.0 None \n", "97 2924 Venables St, Vancouver, BC 12.0 None \n", "98 3300 Victoria Drive, Vancouver, BC 4.0 None \n", "99 50 E 30th Av, Vancouver, BC 20.0 None \n", "\n", " notes food_tree_varieties other_food_assets jurisdiction \\\n", "0 None None None Private \n", "1 None None None Private \n", "2 None None None City \n", "3 None None None Private \n", "4 None None None Private \n", ".. ... ... ... ... \n", "95 None None None Private \n", "96 None None None Private \n", "97 None None None Private \n", "98 None None None Park Board \n", "99 None None None Park Board \n", "\n", " steward_or_managing_organization \\\n", "0 None \n", "1 None \n", "2 City of Vancouver \n", "3 Atira Community Resources \n", "4 BC Seniors Living Assoc. Beulah Homes Society \n", ".. ... \n", "95 None \n", "96 Brightside Community Homes Foundation \n", "97 HFBC Housing Foundation \n", "98 None \n", "99 None \n", "\n", " public_e_mail \\\n", "0 None \n", "1 oak.16th.garden@gmail.com \n", "2 communitygardens@vancouver.ca \n", "3 None \n", "4 None \n", ".. ... \n", "95 None \n", "96 None \n", "97 None \n", "98 None \n", "99 info@rileyparkgarden.org \n", "\n", " website \\\n", "0 None \n", "1 None \n", "2 None \n", "3 http://www.atira.bc.ca/community-garden-kitchens \n", "4 None \n", ".. ... \n", "95 None \n", "96 None \n", "97 None \n", "98 https://cedarcottagefoodnetwork.com/projects-e... \n", "99 https://www.rileyparkgarden.org/ \n", "\n", " geo_local_area geo_point_2d \\\n", "0 Mount Pleasant {'lat': 49.2571193, 'lon': -123.0788387} \n", "1 Shaughnessy {'lat': 49.2567482, 'lon': -123.1276645} \n", "2 Kerrisdale {'lat': 49.2209834, 'lon': -123.1505481} \n", "3 Strathcona {'lat': 49.2810913, 'lon': -123.0871212} \n", "4 Hastings-Sunrise {'lat': 49.266474, 'lon': -123.032578} \n", ".. ... ... \n", "95 Kitsilano {'lat': 49.267434, 'lon': -123.152475} \n", "96 Kensington-Cedar Cottage {'lat': 49.260319, 'lon': -123.075082} \n", "97 Hastings-Sunrise {'lat': 49.276211, 'lon': -123.043281} \n", "98 Kensington-Cedar Cottage {'lat': 49.255098, 'lon': -123.06367871} \n", "99 Riley Park {'lat': 49.24298494, 'lon': -123.10448685} \n", "\n", " geometry \n", "0 POINT (-123.07884 49.25712) \n", "1 POINT (-123.12766 49.25675) \n", "2 POINT (-123.15055 49.22098) \n", "3 POINT (-123.08712 49.28109) \n", "4 POINT (-123.03258 49.26647) \n", ".. ... \n", "95 POINT (-123.15248 49.26743) \n", "96 POINT (-123.07508 49.26032) \n", "97 POINT (-123.04328 49.27621) \n", "98 POINT (-123.06368 49.2551) \n", "99 POINT (-123.10449 49.24298) \n", "\n", "[100 rows x 20 columns]" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fused.run(community_gardens_vancouver)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'description': \"\\n1) Purpose and Functionality:\\nThe User Defined Function (UDF) 'community_gardens_vancouver' is designed to fetch and process geospatial data relating to community gardens and food trees in the city of Vancouver. It operates by sending a request to the Vancouver Open Data API and returns a GeoDataFrame containing the geographical coordinates and corresponding information relating to the specified city assets.\\n\\n2) Input Parameters:\\nThe function doesn't require any input parameters from the user, as the parameters necessary for the API request ('limit') and URL are hard-coded within the function.\\n\\n3) Output:\\nThe UDF returns a GeoDataFrame object containing spatial data obtained from the response of the API request. Specifically, the data includes geographical coordinates (as shapely geometry data) and other corresponding details about Vancouver's community gardens and food trees. A print statement within the function also provides the dimensions of the returned GeoDataFrame in the format (num_rows, num_columns).\\n\\n4) Technical Details and Limitations:\\n- The function incorporates the use of the 'requests', 'geopandas' and 'shapely.geometry' Python libraries, which need to be installed and imported correctly for the UDF to operate.\\n- It connects to the Vancouver Open Data API, hence requires a stable internet connection for successful data retrieval.\\n- The 'limit' parameter for the API request is currently set to -1, the API's maximum data retrieval limit. Any changes to accommodate remote API updates may require adjustments to this parameter.\\n- 'geom', a temporary column for storing raw geometry data, is deleted after use to optimize memory.\\n- The function doesn't include any error handling capabilities, so it's not protected from potential issues such as unsuccessful API requests or changes in the data schema.\\n\\n5) Technical Style:\\nThe UDF adheres to Python's coding standards and practices, presenting a clean, unambiguous syntax beneficial for AI systems and human coders alike. Despite its complexity, its operations are orderly and segmented, boosting understanding and potential for extension or modification.\",\n", " 'parameters': [{'name': '', 'type': ''}]}" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "community_garden_mcp_metadata = {\n", " \"description\": \"\"\"\n", "1) Purpose and Functionality:\n", "The User Defined Function (UDF) 'community_gardens_vancouver' is designed to fetch and process geospatial data relating to community gardens and food trees in the city of Vancouver. It operates by sending a request to the Vancouver Open Data API and returns a GeoDataFrame containing the geographical coordinates and corresponding information relating to the specified city assets.\n", "\n", "2) Input Parameters:\n", "The function doesn't require any input parameters from the user, as the parameters necessary for the API request ('limit') and URL are hard-coded within the function.\n", "\n", "3) Output:\n", "The UDF returns a GeoDataFrame object containing spatial data obtained from the response of the API request. Specifically, the data includes geographical coordinates (as shapely geometry data) and other corresponding details about Vancouver's community gardens and food trees. A print statement within the function also provides the dimensions of the returned GeoDataFrame in the format (num_rows, num_columns).\n", "\n", "4) Technical Details and Limitations:\n", "- The function incorporates the use of the 'requests', 'geopandas' and 'shapely.geometry' Python libraries, which need to be installed and imported correctly for the UDF to operate.\n", "- It connects to the Vancouver Open Data API, hence requires a stable internet connection for successful data retrieval.\n", "- The 'limit' parameter for the API request is currently set to -1, the API's maximum data retrieval limit. Any changes to accommodate remote API updates may require adjustments to this parameter.\n", "- 'geom', a temporary column for storing raw geometry data, is deleted after use to optimize memory.\n", "- The function doesn't include any error handling capabilities, so it's not protected from potential issues such as unsuccessful API requests or changes in the data schema.\n", "\n", "5) Technical Style:\n", "The UDF adheres to Python's coding standards and practices, presenting a clean, unambiguous syntax beneficial for AI systems and human coders alike. Despite its complexity, its operations are orderly and segmented, boosting understanding and potential for extension or modification.\"\"\",\n", " \"parameters\": [\n", " {\n", " \"name\": \"\",\n", " \"type\": \"\",\n", " }\n", " ],\n", "}\n", "\n", "community_garden_mcp_metadata" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "# We add this new UDF + mcp_metadata to the same agent\n", "common.save_to_agent(\n", " agent_json_path=os.path.join(WORKING_DIR, \"agents.json\"),\n", " udf=community_gardens_vancouver,\n", " agent_name=AGENT_NAME,\n", " udf_name=\"community_gardens_vancouver\",\n", " mcp_metadata=community_garden_mcp_metadata,\n", ")" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"agents\": [\n", " {\n", " \"name\": \"get_current_time\",\n", " \"udfs\": [\n", " \"current_utc_time\"\n", " ]\n", " },\n", " {\n", " \"name\": \"fused_docs\",\n", " \"udfs\": [\n", " \"list_public_udfs\",\n", " \"reading_fused_docs\"\n", " ]\n", " },\n", " {\n", " \"name\": \"vancouver_open_data\",\n", " \"udfs\": [\n", " \"hundred_parks_in_vancouver\",\n", " \"internet_speeds_for_lat_lon\"\n", " ]\n", " },\n", " {\n", " \"name\": \"elevation_stats_for_lat_lon_area\",\n", " \"udfs\": [\n", " \"elevation_stats\"\n", " ]\n", " },\n", " {\n", " \"name\": \"apple_banana_orange\",\n", " \"udfs\": [\n", " \"apple_banana_orange_udf\"\n", " ]\n", " },\n", " {\n", " \"name\": \"dynamic_output_vector\",\n", " \"udfs\": [\n", " \"dynamic_output_vector_udf\"\n", " ]\n", " },\n", " {\n", " \"name\": \"vancouver_open_data_demo\",\n", " \"udfs\": [\n", " \"hundred_parks_in_vancouver\",\n", " \"electric_vehicle_chargers_in_vancouver\",\n", " \"yearly_crime_amount\",\n", " \"internet_speeds_for_lat_lon\",\n", " \"community_gardens_vancouver\"\n", " ]\n", " }\n", " ]\n", "}\n" ] } ], "source": [ "# Let's make sure we created our agent properly, with all our UDFs\n", "agents = json.load(open(os.path.join(WORKING_DIR, \"agents.json\")))\n", "print(json.dumps(agents, indent=4, sort_keys=True))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can tell Claude we want to use this `vancouver_open_data` Agent, defined in `AGENT_NAME`" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'vancouver_open_data_demo'" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "AGENT_NAME" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [], "source": [ "# Finally, we can select which Agent we want to pass to Claude in our MCP server config\n", "common.generate_local_mcp_config(\n", " config_path=PATH_TO_CLAUDE_CONFIG,\n", " agents_list=[AGENT_NAME],\n", " repo_path=WORKING_DIR,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Claude uses a specific config (that you passed under `PATH_TO_CLAUDE_CONFIG`) to know what to run under the hood. This is what we're editing for you each time you change the agent you want to run" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"mcpServers\": {\n", " \"vancouver_open_data_demo\": {\n", " \"args\": [\n", " \"run\",\n", " \"--directory\",\n", " \"/Users/maximelenormand/Library/CloudStorage/Dropbox/Mac/Documents/repos/fused-mcp\",\n", " \"main.py\",\n", " \"--runtime=local\",\n", " \"--udf-names=hundred_parks_in_vancouver,electric_vehicle_chargers_in_vancouver,yearly_crime_amount,internet_speeds_for_lat_lon,community_gardens_vancouver\",\n", " \"--name=vancouver_open_data_demo\"\n", " ],\n", " \"command\": \"uv\"\n", " }\n", " }\n", "}\n" ] } ], "source": [ "# Let's read this Claude Desktop config to see what we're passing\n", "claude_config = json.load(open(PATH_TO_CLAUDE_CONFIG))\n", "print(json.dumps(claude_config, indent=4, sort_keys=True))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Now let's restart Claude with this new agent!" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [], "source": [ "def restart_claude(claude_path: str = CLAUDE_APP_PATH):\n", " app_name = claude_path.split(\"/\")[-1]\n", "\n", " try:\n", " os.system(f\"pkill -f '{app_name}'\")\n", " print(f\"Killed {app_name}\")\n", " time.sleep(2) # Wait for shutdown\n", " except Exception:\n", " print(\"Claude wasn't running, so no need to kill it\")\n", "\n", " print(f\"Restarting {app_name}\")\n", " os.system(f\"open -a '{claude_path}'\") # Restart Claude" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Killed Claude.app\n", "Restarting Claude.app\n" ] } ], "source": [ "restart_claude()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.5" } }, "nbformat": 4, "nbformat_minor": 2 }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/fusedio/fused-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server