{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": "# Bonus: Working with APIs" }, { "cell_type": "markdown", "metadata": {}, "source": "Apparently the best Pizza place in New York Lombardi's pizza. Let's assume you want to find a hotel with is as close as possible to this place. " }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "lombardi_pizza_coords = (40.7215577, -73.9956097)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise 3.1**: Create an interactive map showing all hotels and the Lombardi's pizza. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise 3.2:** Calculate the distance in meters between Lomabardi's pizza and each hotel. Don't forget to reproject the dataframe into a suitable coordinate references system. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise 3.3:** Create an interactive map which shows the points colored by the distance to the pizza place. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Calculate distance using openrouteservice\n", "\n", "The euclidean distance is not always a good estimate for the real distance. So we will use the openrouteservice API to calculate the real routes. \n", "\n", "#### Preparation\n", "\n", "1. Install openrouteservice-py in your environment using `pip install openrouteservice`.\n", "2. Create an account at [https://openrouteservice.org/dev/#/login](https://openrouteservice.org/dev/#/login) and generate a token/API key. Paste the API key below" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "api_key = ''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Example\n", "Here's an example of how to generate a route by sending a request to the openrouteservice API." ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "from openrouteservice import Client" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "client = Client(key=api_key)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "start_coords = (-73.9684581756592, 40.75580720140376)\n", "destination_coords = (-73.9956097, 40.7215577)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "response = client.directions([start_coords, destination_coords], \n", " profile='foot-walking', \n", " instructions=False, \n", " format='geojson')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Convert response to geodataframe" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "route_df = gpd.GeoDataFrame.from_features(response, crs='epsg:4326')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plot route in interactive map" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "route_df.explore()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Routes between Pizza and hotels\n", "\n", "To reduce the amount of request to the openrouteservice API, we will only look at hotels within 4km distance of the pizza place. \n", "\n", "**Exercise:** Select all hotels which are in 3 km euclidean distance of the pizza place. \n", "Iterate over each row in the hotels dataframe and calculate the route between the hotel and the pizza place. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "selected_hotels = " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** Iterate over the hotels geodataframe and calculate the route between each of the selected hotels and the pizza place. This includes:\n", "\n", "1. Store all routes in one `GeoDataFrame`.\n", "2. Extract the distance from the openrouteservice response and store it in a new column called `distance_ors` in the `selected_hotels` dataframe." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "3. Calculate the difference between the euclidean distance and the ors distance" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "4. Plot everything on a nicely styled interactive map. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Find the nearest pizza place of each hotel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is an example of how to download data from OSM using the ohsome API. " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from ohsome import OhsomeClient" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "ohsome_client = OhsomeClient()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Send request to ohsome API (see [API endpoints](https://api.ohsome.org/v1/swagger-ui.html) and [documentation](https://docs.ohsome.org/ohsome-api/v1/))." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "ohsome_response = ohsome_client.elements.geometry.post(filter='cuisine=pizza', \n", " properties='tags',\n", " bboxes=[-74.01722, 40.69631, -73.98163, 40.7473])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Convert to geopandas dataframe" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "pizza_df = ohsome_response.as_dataframe()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pizza_df.explore()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now find the nearest pizza place for each hotel." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### More on Pandas and vectorization\n", "Watch this talk by __Sofia Heisler's repository [PyCon 2017: Optimizing Pandas Code for Performance](https://github.com/s-heisler/pycon2017-optimizing-pandas)__ to get a more indepth look into vecotrized computation using pandas. \n", "\n", "→ Watch her talk on [YouTube](https://www.youtube.com/watch?v=HN5d490_KKk) I really recommend it (especially if you like panda GIFs)\n", "\n", "→ Read her [blog post](https://engineering.upside.com/a-beginners-guide-to-optimizing-pandas-code-for-speed-c09ef2c6a4d6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### GeoPandas is great, and now even fast!\n", "\n", "Geopandas make spatial analysis in Python a lot easier, but it used to have a bottleneck: geometric opertions are performed using shapely, which - as we have seen - is not the fastest, since it is performed in Python. In addition, operations along a series of shapely objects cannot be vectorized in Python. " ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "But the GeoPandas developers also found a solution for this problem: Yet another package - PyGEOS. The PyGEOS packages allows vectorized geometric calculations based on the C library GEOS. In 2023, PyGEOS was merged with shapely in version 2.0. So now shapely and therefore also geopandas can perform vectorized geospatial operations. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "--> [FOSS4G Talk: State of GeoPandas and friends]() Unfortunately the presentation is not online, but [here](https://www.youtube.com/watch?v=rWrn9XO-UxM) is a similar one.\n", "\n", "**Watch conference talks on YouTube or do tutorials on GitHub**, they are a great way to stay up-to-date with current developments in the scientific Python world and great resource to learn" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Resources" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Introducing pygeos](https://caspervdw.github.io/Introducing-Pygeos/)\n", "\n", "[PyGEOS Documentation](https://pygeos.readthedocs.io/en/latest/)\n", " \n", "\n", "[Cythonize Pandas](https://pandas.pydata.org/pandas-docs/stable/user_guide/enhancingperf.html)\n", "\n", "https://stackoverflow.com/questions/52673285/performance-of-pandas-apply-vs-np-vectorize-to-create-new-column-from-existing-c\n", "\n", "https://www.google.com/url?q=http://homepages.math.uic.edu/~jan/mcs275/running_cython.pdf&sa=U&ved=2ahUKEwiq_M3-vfrqAhWF-KQKHXBXCfwQFjAAegQICRAB&usg=AOvVaw0jX9BZrTt2aPsxKo30zmDb\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.10.12" } }, "nbformat": 4, "nbformat_minor": 4 }