From d3e0dd2cb51d443a95847be94d0e596016140d84 Mon Sep 17 00:00:00 2001 From: NANAE AUBRY Date: Tue, 22 Oct 2024 13:09:51 +0200 Subject: [PATCH] update map methods --- ...tes-for-als-clinics-using-location-allocation-analysis.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/04_gis_analysts_data_scientists/identifying-suitable-sites-for-als-clinics-using-location-allocation-analysis.ipynb b/samples/04_gis_analysts_data_scientists/identifying-suitable-sites-for-als-clinics-using-location-allocation-analysis.ipynb index eb06deb084..89de11a726 100644 --- a/samples/04_gis_analysts_data_scientists/identifying-suitable-sites-for-als-clinics-using-location-allocation-analysis.ipynb +++ b/samples/04_gis_analysts_data_scientists/identifying-suitable-sites-for-als-clinics-using-location-allocation-analysis.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# Identifying suitable sites for new ALS clinics using location allocation analysis"]}, {"cell_type": "markdown", "metadata": {"toc": true}, "source": ["

Table of Contents

\n", "
"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Clinic access for the chronically ill\n", "\n", "Location is everything for the chronically ill. For patients with amyotrophic lateral sclerosis (ALS), visits to clinics are exhausting full-day engagements involving sessions with highly trained specialists from several disciplines. Patients with long drives to their nearest clinic may also face the additional hardship of having to plan for travel days to and from the clinic as well as for food and lodging. \n", "\n", "This notebook demonstrates how ArcGIS can perform network analysis to identify potential sites for new ALS clinics in California to improve access for patients who do not live near a clinic.\n", "\n", "
Note: The examples in this notebook is intended to serve only as a technology demonstration. The analysis results should not be used for any planning purposes as the data for ALS patient locations are fictional. The ALS clinic locations were obtained from data that was publically available in October 2017.
"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Access the analysis data"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["# Import the ArcGIS API for Python\n", "from arcgis import *\n", "from IPython.display import display\n", "\n", "# Connect to the web GIS.\n", "gis = GIS('https://pythonapi.playground.esri.com/portal', 'arcgis_python', 'amazing_arcgis_123')"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["# Search for content tagged with 'ALS'\n", "search_als = gis.content.search('tags: ALS', 'Feature Layer')\n", "for item in search_als:\n", " display(item)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Map the challenge\n", "\n", "Many ALS patients in California have to drive for over 90 minutes to reach their nearest clinic. For the purposes of this notebook, those patients are considered to have poor clinic access. The next block of code displays a map that displays the locations of those ALS patients who have poor clinic access. \n", "\n", "Map Legend:\n", "\n", "

Locations of ALS patients that must travel for over 90 minutes to access a clinic.\n", "

Existing ALS clinics.\n", "

90 minute drive times from existing ALS clinics.\n", "

Candidate cities for new ALS clinics."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["# Display the titles of the items returned by the search.\n", "for item in search_als:\n", " print(item.title)"]}, {"cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [{"data": {"text/html": [""], "text/plain": [""]}, "execution_count": 2, "metadata": {}, "output_type": "execute_result"}], "source": ["# Create a map of California.\n", "map1 = gis.map(\"State of California, USA\")\n", "map1.basemap = 'dark-gray'\n", "display(map1)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["# Add the ALS content to the map.\n", "for item in search_als:\n", " if item.title == 'ALS_Clinics_CA':\n", " als_clinics = item \n", " if item.title == 'ALS_Patients_CA':\n", " patient_locations = item \n", " if item.title == 'ALS_Clinic_City_Candidates_CA':\n", " city_candidates = item \n", " elif item.title == 'ALS_Clinic_90minDriveTime_CA':\n", " drive_times = item\n", " \n", "map1.add_layer(drive_times)\n", "map1.add_layer(als_clinics)\n", "map1.add_layer(patient_locations)\n", "map1.add_layer(city_candidates)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Introduction to Location-Allocation\n", "### ArcGIS Network Analyst\n", "\n", "[ArcGIS Network Analyst](http://pro.arcgis.com/en/pro-app/help/analysis/networks/what-is-network-analyst-.htm) helps organizations run their operations more efficiently and improve strategic decision making by performing analysis on the travel costs between their facilities and demand locations to answer questions such as:\n", "\n", "* What is the quickest way to get from point A to point B?\n", "* What houses are within five minutes of our fire stations?\n", "* What markets do our businesses cover?\n", "* Where should we open a new branch of our business to maximize market share?\n", "\n", "To address these questions, ArcGIS Network Analyst creates origin-destination (OD) cost matrices along a travel network such as roads, to find solutions to reduce the overall costs of travel.\n", "\n", "![title](http://esri.github.io/arcgis-python-api/notebooks/nbimages/04_als_odcost.png)\n", "\n", "To perform network analysis, you need a network dataset, which models a transportation network. In the ArcGIS API for Python, the network dataset is accessed through a network service hosted in ArcGIS Online or Portal for ArcGIS.\n", "\n", "### Location-Allocation Analysis\n", "\n", "The goal of [location-allocation](http://pro.arcgis.com/en/pro-app/help/analysis/networks/location-allocation-analysis-layer.htm) is to locate facilities in a way that supplies the demand points most efficiently. As the name suggests, location-allocation is a two-fold problem that simultaneously locates facilities and allocates demand points to the facilities.\n", "\n", "![title](http://esri.github.io/arcgis-python-api/notebooks/nbimages/04_als_location_allocation_schematic.png)\n", "\n", "### The `solve_location_allocation` tool\n", "\n", "In this notebook, we will use the [solve_location_allocation](http://esri.github.io/arcgis-python-api/apidoc/html/arcgis.network.analysis.html#solve-location-allocation) tool to find the best locations for new ALS clinics in California. Inputs to this tool include [FeatureSet](http://esri.github.io/arcgis-python-api/apidoc/html/arcgis.features.toc.html#featureset)s containing the following data:\n", "* facilities\n", "* demand points. \n", "\n", "For the examples in this notebook, the facilities are a set of candidate locations for new ALS clinics. These locations could be actual addresses. However, for these examples, the candidate locations are cities that could potentially host new ALS clinics and are represented by their centroid points. The candidate cities were pre-selected based on the following criteria:\n", "* they are within California\n", "* they are outside of the 90 minute drive time areas from existing ALS clinics\n", "* they have populations of at least 60,000, and are therefore assumed to have sufficient health care facilities and professionals to support an ALS clinic\n", "\n", "The analyses in this notebook could lead to further multi-criteria analysis to identify specific locations within a city or other geographic area. For examples of Jupyter Notebooks with multi-criteria suitability analysis see [Calculating cost surfaces using weighted overlay analysis](https://developers.arcgis.com/python/sample-notebooks/calculating-cost-surfaces-using-weighted-overlay-analysis/) and [Finding suitable spots for placing heart defibrillator equipments in public](https://developers.arcgis.com/python/sample-notebooks/finding-suitable-spots-for-aed-devices-using-raster-analytics/)\n", "\n", "For the demand points, we will use point locations of fictional ALS patients. These locations are aggregated to zip code centroids and contain an estimated number of ALS patients based on the total population within each zip code.\n", "\n", "The output of the tool is a named tuple which contains the following data:\n", "* solve_succeeded (`bool`)\n", "* output_facilities (`FeatureSet`)\n", "* output_demand_points (`FeatureSet`)\n", "* output_allocation_lines (`FeatureSet`)\n", "\n", "To prepare to run the `solve_location_allocation` tool we will first perform the following steps:\n", "1. Extract the required input `FeatureSets` for the tool from `FeatureLayerCollection` items in the GIS.\n", "2. Define a function to extract, symbolize, and display the output results from the `solve_location_allocation` tool."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Analysis Preparation"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["# Extract the required input data for the analyses.\n", "\n", "# ALS clinic city candidates FeatureSet\n", "city_candidates_fset = city_candidates.layers[0].query()\n", "\n", "# ALS patient locations FeatureSet\n", "patient_locations_fset = patient_locations.layers[0].query()\n", "\n", "# Display the ALS patients FeatureSet in a pandas Dataframe\n", "patient_locations_sdf = patient_locations_fset.sdf\n", "patient_locations_sdf.head()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Note that the `num_patients` field contains the numbers of ALS patients at each location. We can use the `num_patients` field to \"weight\" the analysis in favor of candidate cities with the greatest numbers of patients near them.\n", "\n", "To accomplish this, add a column called `weight` to the `SpatialDataFrame` object and remove the old `num_patients` column."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["# create the 'weight' column\n", "patient_locations_sdf['weight'] = patient_locations_sdf['num_patients']\n", "\n", "# drop the 'num_patients' column\n", "patient_locations_sdf2 = patient_locations_sdf.drop('num_patients', axis=1)\n", "\n", "# convert SpatialDataFrame back to a FeatureSet\n", "patient_locations_weighted = patient_locations_sdf2.spatial.to_featureset()\n", "patient_locations_weighted.sdf.head()"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["# Define a function to display the output analysis results in a map\n", "import time\n", "\n", "def visualize_locate_allocate_results(map_widget, solve_locate_allocate_result, zoom_level):\n", " # The map widget\n", " m = map_widget\n", " # The locate-allocate analysis result\n", " result = solve_locate_allocate_result\n", " \n", " # 1. Parse the locate-allocate analysis results\n", " # Extract the output data from the analysis results\n", " # Store the output points and lines in pandas dataframes\n", " demand_df = result.output_demand_points.sdf\n", " lines_df = result.output_allocation_lines.sdf\n", "\n", " # Extract the allocated demand points (patients) data.\n", " demand_allocated_df = demand_df[demand_df['DemandOID'].isin(lines_df['DemandOID'])]\n", " demand_allocated_fset = features.FeatureSet.from_dataframe(demand_allocated_df)\n", "\n", " # Extract the un-allocated demand points (patients) data.\n", " demand_not_allocated_df = demand_df[~demand_df['DemandOID'].isin(lines_df['DemandOID'])]\n", " demand_not_allocated_fset = features.FeatureSet.from_dataframe(demand_not_allocated_df)\n", "\n", " # Extract the chosen facilities (candidate clinic sites) data.\n", " facilities_df = result.output_facilities.sdf[['Name', 'FacilityType', \n", " 'Weight','DemandCount', 'DemandWeight', 'SHAPE']]\n", " facilities_chosen_df = facilities_df[facilities_df['FacilityType'] == 3]\n", " facilities_chosen_fset = features.FeatureSet.from_dataframe(facilities_chosen_df)\n", "\n", " # 2. Define the map symbology\n", " # Allocation lines\n", " allocation_line_symbol_1 = {'type': 'esriSLS', 'style': 'esriSLSSolid',\n", " 'color': [255,255,255,153], 'width': 0.7}\n", "\n", " allocation_line_symbol_2 = {'type': 'esriSLS', 'style': 'esriSLSSolid',\n", " 'color': [0,255,197,39], 'width': 3}\n", "\n", " allocation_line_symbol_3 = {'type': 'esriSLS', 'style': 'esriSLSSolid',\n", " 'color': [0,197,255,39], 'width': 5}\n", " \n", " allocation_line_symbol_4 = {'type': 'esriSLS', 'style': 'esriSLSSolid',\n", " 'color': [0,92,230,39], 'width': 7}\n", " \n", " # Patient points within 90 minutes drive time to a proposed clinic location.\n", " allocated_demand_symbol = {\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriPMS\",\n", " \"url\":\"https://static.arcgis.com/images/Symbols/Firefly/FireflyA7.png\",\n", " \"contentType\":\"image/png\",\"width\":24,\"height\":24}\n", "\n", " # Patient points outside of a 90 minutes drive time to a proposed clinic location.\n", " unallocated_demand_symbol = {\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriPMS\",\n", " \"url\":\"https://static.arcgis.com/images/Symbols/Firefly/FireflyB3.png\",\n", " \"contentType\":\"image/png\",\"width\":24,\"height\":24}\n", "\n", " # Selected clinic\n", " selected_facilities_symbol = {\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriPMS\",\n", " \"url\":\"https://static.arcgis.com/images/Symbols/State-Government/State-Health-Clinics.png\",\n", " \"contentType\":\"image/png\",\"width\":24,\"height\":24}\n", " \n", " # 3. Display the analysis results in the map\n", " # Add a slight delay for drama. \n", " time.sleep(1.5) \n", " # Display the patient-clinic allocation lines.\n", " m.draw(shape=result.output_allocation_lines, symbol=allocation_line_symbol_4)\n", " m.draw(shape=result.output_allocation_lines, symbol=allocation_line_symbol_2)\n", " m.draw(shape=result.output_allocation_lines, symbol=allocation_line_symbol_1)\n", " \n", " # Display the locations of patients within the specified drive time to the selected clinic(s).\n", " m.draw(shape=demand_allocated_fset, symbol=allocated_demand_symbol)\n", "\n", " # Display the locations of patients outside the specified drive time to the selected clinic(s).\n", " m.draw(shape = demand_not_allocated_fset, symbol = unallocated_demand_symbol)\n", "\n", " # Display the chosen clinic site.\n", " m.draw(shape=facilities_chosen_fset, symbol=selected_facilities_symbol)\n", "\n", " # Zoom out to display all of the allocated patients points.\n", " m.zoom = zoom_level"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Analysis: Where could we add one new ALS clinic to reach the greatest number of ALS patients who currently have poor access to an existing clinic?\n", "\n", "To answer this question, we will use the **\"Maximize Coverage\"** problem type. This problem type chooses facilities such that as many demand points as possible are allocated to facilities within the impedance cutoff. In this example, the impedance cutoff is defined as a drive time of 90 minutes."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["# Identify the city which has the greatest number of patients within a 90 minute drive time.\n", "result1 = network.analysis.solve_location_allocation(problem_type='Maximize Coverage',\n", " travel_direction='Demand to Facility',\n", " number_of_facilities_to_find='1',\n", " demand_points=patient_locations_weighted,\n", " facilities=city_candidates_fset,\n", " measurement_units='Minutes',\n", " default_measurement_cutoff=90\n", ")\n", "print('Analysis succeeded? {}'.format(result1.solve_succeeded))"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["# Display the analysis results in a pandas dataframe.\n", "result1.output_facilities.sdf[['Name', 'FacilityType', 'DemandCount', 'DemandWeight']]"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The selected facility, i.e. ALS city candidate, is assigned the value \"3\" in the `FacilityType` field. The `DemandCount` and `DemandWeight` fields indicate the number of demand points (patient locations) and total number of patients at those locations which are allocated to the facility.\n", "\n", "Now, let us visualize the results on a map."]}, {"cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [{"data": {"text/html": [""], "text/plain": [""]}, "execution_count": 3, "metadata": {}, "output_type": "execute_result"}], "source": ["# Display the analysis results in a map.\n", "\n", "# Create a map of Visalia, California.\n", "map2 = gis.map('Visalia, CA')\n", "map2.basemap = 'dark-gray'\n", "display(map2)\n", "\n", "# Call custom function defined earlier in this notebook to \n", "# display the analysis results in the map.\n", "visualize_locate_allocate_results(map2, result1, zoom_level=7)"]}, {"cell_type": "markdown", "metadata": {"collapsed": true}, "source": ["## Analysis: Where could we add new ALS clinics to reach 50% of patients who currently must drive for over 90 minutes to reach their nearest clinic?\n", "\n", "To answer this question we will use the **\"Target Market Share\"** problem type. This problem type chooses the minimum number of facilities necessary to capture a specific percentage of the total market share."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["# Identify where to open new clinics that are within a 90 minute drive time\n", "# of 50% of ALS patients who currently have to drive for over 90 minutes to reach their nearest clinic.\n", "\n", "result2 = network.analysis.solve_location_allocation(\n", " problem_type='Target Market Share', \n", " target_market_share=70,\n", " facilities=city_candidates_fset, \n", " demand_points=patient_locations_weighted,\n", " travel_direction='Demand to Facility',\n", " measurement_units='Minutes', \n", " default_measurement_cutoff=90\n", ")\n", "\n", "print('Solve succeeded? {}'.format(result2.solve_succeeded))"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["# Display the analysis results in a table.\n", "result2.output_facilities.sdf[['Name', 'FacilityType', 'DemandCount', 'DemandWeight']]"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The `solve_location_allocation` tool selected two of the candidate cities to host new ALS clinics. If ALS clinics are established in both of those cities, 50% of the patients who must currently drive for over 90 minutes to reach an existing clinic would be able to access one of the new clinics in 90 minutes or less.\n", "\n", "Now, let us visualize the results on a map."]}, {"cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [{"data": {"text/html": [""], "text/plain": [""]}, "execution_count": 4, "metadata": {}, "output_type": "execute_result"}], "source": ["# Display the analysis results in a map.\n", "\n", "# Create a map of Visalia, California\n", "map3 = gis.map('Visalia, CA', zoomlevel=7)\n", "map3.basemap = 'dark-gray'\n", "\n", "# Display the map and add the analysis results to it\n", "from IPython.display import display\n", "display(map3)\n", "visualize_locate_allocate_results(map3, result2, zoom_level=6)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["# Conclusions\n", "\n", "The ArcGIS Network Analysis toolset is a versatile set of tools that assist strategic decision making to reduce the costs of travel. This examples in this notebook only scratch the surface of the situations where you could use these tools. By performing your analysis using Python and the Jupyter Notebook, you also document your methodology in a form that can be easily shared via email to colleagues and stake holders, enabling them to reproduce your results or repeat the analyses in an iterative fashion with different parameters or different input datasets. You can also [export](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.to_csv.html) the output tables from the solve_location_allocation tool by to CSV files to share with stake holders via email."]}], "metadata": {"esriNotebookRuntime": {"notebookRuntimeName": "ArcGIS Notebook Python 3 Standard", "notebookRuntimeVersion": "4.0"}, "kernelspec": {"display_name": "Python 3", "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.8.2"}, "toc": {"base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": true, "toc_position": {}, "toc_section_display": true, "toc_window_display": true}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file +{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Identifying suitable sites for new ALS clinics using location allocation analysis"]},{"cell_type":"markdown","metadata":{"toc":true},"source":["

Table of Contents

\n","
"]},{"cell_type":"markdown","metadata":{},"source":["## Clinic access for the chronically ill\n","\n","Location is everything for the chronically ill. For patients with amyotrophic lateral sclerosis (ALS), visits to clinics are exhausting full-day engagements involving sessions with highly trained specialists from several disciplines. Patients with long drives to their nearest clinic may also face the additional hardship of having to plan for travel days to and from the clinic as well as for food and lodging. \n","\n","This notebook demonstrates how ArcGIS can perform network analysis to identify potential sites for new ALS clinics in California to improve access for patients who do not live near a clinic.\n","\n","
Note: The examples in this notebook is intended to serve only as a technology demonstration. The analysis results should not be used for any planning purposes as the data for ALS patient locations are fictional. The ALS clinic locations were obtained from data that was publically available in October 2017.
"]},{"cell_type":"markdown","metadata":{},"source":["## Access the analysis data"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["# Import the ArcGIS API for Python\n","from arcgis import *\n","from IPython.display import display\n","\n","# Connect to the web GIS.\n","gis = GIS(profile='your_enterprise_profile')"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["# Search for content tagged with 'ALS'\n","search_als = gis.content.search('tags: ALS', 'Feature Layer')\n","for item in search_als:\n"," display(item)"]},{"cell_type":"markdown","metadata":{},"source":["## Map the challenge\n","\n","Many ALS patients in California have to drive for over 90 minutes to reach their nearest clinic. For the purposes of this notebook, those patients are considered to have poor clinic access. The next block of code displays a map that displays the locations of those ALS patients who have poor clinic access. \n","\n","Map Legend:\n","\n","

Locations of ALS patients that must travel for over 90 minutes to access a clinic.\n","

Existing ALS clinics.\n","

90 minute drive times from existing ALS clinics.\n","

Candidate cities for new ALS clinics."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["# Display the titles of the items returned by the search.\n","for item in search_als:\n"," print(item.title)"]},{"cell_type":"code","execution_count":2,"metadata":{},"outputs":[{"data":{"text/html":[""],"text/plain":[""]},"execution_count":2,"metadata":{},"output_type":"execute_result"}],"source":["# Create a map of California.\n","map1 = gis.map(\"California, USA\")\n","map1.basemap.basemap = 'dark-gray-vector'\n","display(map1)"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["# Add the ALS content to the map.\n","for item in search_als:\n"," if item.title == 'ALS_Clinics_CA':\n"," als_clinics = item \n"," if item.title == 'ALS_Patients_CA':\n"," patient_locations = item \n"," if item.title == 'ALS_Clinic_City_Candidates_CA':\n"," city_candidates = item \n"," elif item.title == 'ALS_Clinic_90minDriveTime_CA':\n"," drive_times = item\n"," \n","map1.content.add(drive_times)\n","map1.content.add(als_clinics)\n","map1.content.add(patient_locations)\n","map1.content.add(city_candidates)"]},{"cell_type":"markdown","metadata":{},"source":["## Introduction to Location-Allocation\n","### ArcGIS Network Analyst\n","\n","[ArcGIS Network Analyst](http://pro.arcgis.com/en/pro-app/help/analysis/networks/what-is-network-analyst-.htm) helps organizations run their operations more efficiently and improve strategic decision making by performing analysis on the travel costs between their facilities and demand locations to answer questions such as:\n","\n","* What is the quickest way to get from point A to point B?\n","* What houses are within five minutes of our fire stations?\n","* What markets do our businesses cover?\n","* Where should we open a new branch of our business to maximize market share?\n","\n","To address these questions, ArcGIS Network Analyst creates origin-destination (OD) cost matrices along a travel network such as roads, to find solutions to reduce the overall costs of travel.\n","\n","![title](http://esri.github.io/arcgis-python-api/notebooks/nbimages/04_als_odcost.png)\n","\n","To perform network analysis, you need a network dataset, which models a transportation network. In the ArcGIS API for Python, the network dataset is accessed through a network service hosted in ArcGIS Online or Portal for ArcGIS.\n","\n","### Location-Allocation Analysis\n","\n","The goal of [location-allocation](http://pro.arcgis.com/en/pro-app/help/analysis/networks/location-allocation-analysis-layer.htm) is to locate facilities in a way that supplies the demand points most efficiently. As the name suggests, location-allocation is a two-fold problem that simultaneously locates facilities and allocates demand points to the facilities.\n","\n","![title](http://esri.github.io/arcgis-python-api/notebooks/nbimages/04_als_location_allocation_schematic.png)\n","\n","### The `solve_location_allocation` tool\n","\n","In this notebook, we will use the [solve_location_allocation](http://esri.github.io/arcgis-python-api/apidoc/html/arcgis.network.analysis.html#solve-location-allocation) tool to find the best locations for new ALS clinics in California. Inputs to this tool include [FeatureSet](http://esri.github.io/arcgis-python-api/apidoc/html/arcgis.features.toc.html#featureset)s containing the following data:\n","* facilities\n","* demand points. \n","\n","For the examples in this notebook, the facilities are a set of candidate locations for new ALS clinics. These locations could be actual addresses. However, for these examples, the candidate locations are cities that could potentially host new ALS clinics and are represented by their centroid points. The candidate cities were pre-selected based on the following criteria:\n","* they are within California\n","* they are outside of the 90 minute drive time areas from existing ALS clinics\n","* they have populations of at least 60,000, and are therefore assumed to have sufficient health care facilities and professionals to support an ALS clinic\n","\n","The analyses in this notebook could lead to further multi-criteria analysis to identify specific locations within a city or other geographic area. For examples of Jupyter Notebooks with multi-criteria suitability analysis see [Calculating cost surfaces using weighted overlay analysis](https://developers.arcgis.com/python/sample-notebooks/calculating-cost-surfaces-using-weighted-overlay-analysis/) and [Finding suitable spots for placing heart defibrillator equipments in public](https://developers.arcgis.com/python/sample-notebooks/finding-suitable-spots-for-aed-devices-using-raster-analytics/)\n","\n","For the demand points, we will use point locations of fictional ALS patients. These locations are aggregated to zip code centroids and contain an estimated number of ALS patients based on the total population within each zip code.\n","\n","The output of the tool is a named tuple which contains the following data:\n","* solve_succeeded (`bool`)\n","* output_facilities (`FeatureSet`)\n","* output_demand_points (`FeatureSet`)\n","* output_allocation_lines (`FeatureSet`)\n","\n","To prepare to run the `solve_location_allocation` tool we will first perform the following steps:\n","1. Extract the required input `FeatureSets` for the tool from `FeatureLayerCollection` items in the GIS.\n","2. Define a function to extract, symbolize, and display the output results from the `solve_location_allocation` tool."]},{"cell_type":"markdown","metadata":{},"source":["## Analysis Preparation"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["# Extract the required input data for the analyses.\n","\n","# ALS clinic city candidates FeatureSet\n","city_candidates_fset = city_candidates.layers[0].query()\n","\n","# ALS patient locations FeatureSet\n","patient_locations_fset = patient_locations.layers[0].query()\n","\n","# Display the ALS patients FeatureSet in a pandas Dataframe\n","patient_locations_sdf = patient_locations_fset.sdf\n","patient_locations_sdf.head()"]},{"cell_type":"markdown","metadata":{},"source":["Note that the `num_patients` field contains the numbers of ALS patients at each location. We can use the `num_patients` field to \"weight\" the analysis in favor of candidate cities with the greatest numbers of patients near them.\n","\n","To accomplish this, add a column called `weight` to the `SpatialDataFrame` object and remove the old `num_patients` column."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["# create the 'weight' column\n","patient_locations_sdf['weight'] = patient_locations_sdf['num_patients']\n","\n","# drop the 'num_patients' column\n","patient_locations_sdf2 = patient_locations_sdf.drop('num_patients', axis=1)\n","\n","# convert SpatialDataFrame back to a FeatureSet\n","patient_locations_weighted = patient_locations_sdf2.spatial.to_featureset()\n","patient_locations_weighted.sdf.head()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["# Define a function to display the output analysis results in a map\n","import time\n","from arcgis.map.symbols import SimpleLineSymbolEsriSLS, PictureMarkerSymbolEsriPMS\n","\n","def visualize_locate_allocate_results(map_widget, solve_locate_allocate_result, zoom_level):\n"," # The map widget\n"," m = map_widget\n"," # The locate-allocate analysis result\n"," result = solve_locate_allocate_result\n"," \n"," # 1. Parse the locate-allocate analysis results\n"," # Extract the output data from the analysis results\n"," # Store the output points and lines in pandas dataframes\n"," demand_df = result.output_demand_points.sdf\n"," lines_df = result.output_allocation_lines.sdf\n","\n"," # Extract the allocated demand points (patients) data.\n"," demand_allocated_df = demand_df[demand_df['DemandOID'].isin(lines_df['DemandOID'])]\n"," demand_allocated_fset = features.FeatureSet.from_dataframe(demand_allocated_df)\n","\n"," # Extract the un-allocated demand points (patients) data.\n"," demand_not_allocated_df = demand_df[~demand_df['DemandOID'].isin(lines_df['DemandOID'])]\n"," demand_not_allocated_fset = features.FeatureSet.from_dataframe(demand_not_allocated_df)\n","\n"," # Extract the chosen facilities (candidate clinic sites) data.\n"," facilities_df = result.output_facilities.sdf[['Name', 'FacilityType', \n"," 'Weight','DemandCount', 'DemandWeight', 'SHAPE']]\n"," facilities_chosen_df = facilities_df[facilities_df['FacilityType'] == 3]\n"," facilities_chosen_fset = features.FeatureSet.from_dataframe(facilities_chosen_df)\n","\n"," # 2. Define the map symbology\n"," # Allocation lines\n"," allocation_line_symbol_1 = SimpleLineSymbolEsriSLS(**{'type': 'esriSLS', 'style': 'esriSLSSolid',\n"," 'color': [255,255,255,153], 'width': 0.7})\n","\n"," allocation_line_symbol_2 = SimpleLineSymbolEsriSLS(**{'type': 'esriSLS', 'style': 'esriSLSSolid',\n"," 'color': [0,255,197,39], 'width': 3})\n","\n"," allocation_line_symbol_4 = SimpleLineSymbolEsriSLS(**{'type': 'esriSLS', 'style': 'esriSLSSolid',\n"," 'color': [0,92,230,39], 'width': 7})\n"," \n"," # Patient points within 90 minutes drive time to a proposed clinic location.\n"," allocated_demand_symbol = PictureMarkerSymbolEsriPMS(**{\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriPMS\",\n"," \"url\":\"https://static.arcgis.com/images/Symbols/Firefly/FireflyA7.png\",\n"," \"contentType\":\"image/png\",\"width\":24,\"height\":24})\n","\n"," # Patient points outside of a 90 minutes drive time to a proposed clinic location.\n"," unallocated_demand_symbol = PictureMarkerSymbolEsriPMS(**{\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriPMS\",\n"," \"url\":\"https://static.arcgis.com/images/Symbols/Firefly/FireflyB3.png\",\n"," \"contentType\":\"image/png\",\"width\":24,\"height\":24})\n","\n"," # Selected clinic\n"," selected_facilities_symbol = PictureMarkerSymbolEsriPMS(**{\"angle\":0,\"xoffset\":0,\"yoffset\":0,\"type\":\"esriPMS\",\n"," \"url\":\"https://static.arcgis.com/images/Symbols/State-Government/State-Health-Clinics.png\",\n"," \"contentType\":\"image/png\",\"width\":24,\"height\":24})\n"," \n"," # 3. Display the analysis results in the map\n"," # Add a slight delay for drama. \n"," time.sleep(1.5) \n"," # Display the patient-clinic allocation lines.\n"," m.content.draw(shape=result.output_allocation_lines, symbol=allocation_line_symbol_4)\n"," m.content.draw(shape=result.output_allocation_lines, symbol=allocation_line_symbol_2)\n"," m.content.draw(shape=result.output_allocation_lines, symbol=allocation_line_symbol_1)\n"," \n"," # Display the locations of patients within the specified drive time to the selected clinic(s).\n"," m.content.draw(shape=demand_allocated_fset, symbol=allocated_demand_symbol)\n","\n"," # Display the locations of patients outside the specified drive time to the selected clinic(s).\n"," m.content.draw(shape = demand_not_allocated_fset, symbol = unallocated_demand_symbol)\n","\n"," # Display the chosen clinic site.\n"," m.content.draw(shape=facilities_chosen_fset, symbol=selected_facilities_symbol)\n","\n"," # Zoom out to display all of the allocated patients points.\n"," m.zoom = zoom_level"]},{"cell_type":"markdown","metadata":{},"source":["## Analysis: Where could we add one new ALS clinic to reach the greatest number of ALS patients who currently have poor access to an existing clinic?\n","\n","To answer this question, we will use the **\"Maximize Coverage\"** problem type. This problem type chooses facilities such that as many demand points as possible are allocated to facilities within the impedance cutoff. In this example, the impedance cutoff is defined as a drive time of 90 minutes."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["# Identify the city which has the greatest number of patients within a 90 minute drive time.\n","result1 = network.analysis.solve_location_allocation(problem_type='Maximize Coverage',\n"," travel_direction='Demand to Facility',\n"," number_of_facilities_to_find='1',\n"," demand_points=patient_locations_weighted,\n"," facilities=city_candidates_fset,\n"," measurement_units='Minutes',\n"," default_measurement_cutoff=90\n",")\n","print('Analysis succeeded? {}'.format(result1.solve_succeeded))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["# Display the analysis results in a pandas dataframe.\n","result1.output_facilities.sdf[['Name', 'FacilityType', 'DemandCount', 'DemandWeight']]"]},{"cell_type":"markdown","metadata":{},"source":["The selected facility, i.e. ALS city candidate, is assigned the value \"3\" in the `FacilityType` field. The `DemandCount` and `DemandWeight` fields indicate the number of demand points (patient locations) and total number of patients at those locations which are allocated to the facility.\n","\n","Now, let us visualize the results on a map."]},{"cell_type":"code","execution_count":3,"metadata":{},"outputs":[{"data":{"text/html":[""],"text/plain":[""]},"execution_count":3,"metadata":{},"output_type":"execute_result"}],"source":["# Display the analysis results in a map.\n","\n","# Create a map of Visalia, California.\n","map2 = gis.map('Visalia, CA')\n","map2.basemap.basemap = 'dark-gray-vector'\n","display(map2)\n","\n","# Call custom function defined earlier in this notebook to \n","# display the analysis results in the map.\n","visualize_locate_allocate_results(map2, result1, zoom_level=7)"]},{"cell_type":"markdown","metadata":{"collapsed":true},"source":["## Analysis: Where could we add new ALS clinics to reach 50% of patients who currently must drive for over 90 minutes to reach their nearest clinic?\n","\n","To answer this question we will use the **\"Target Market Share\"** problem type. This problem type chooses the minimum number of facilities necessary to capture a specific percentage of the total market share."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["# Identify where to open new clinics that are within a 90 minute drive time\n","# of 50% of ALS patients who currently have to drive for over 90 minutes to reach their nearest clinic.\n","\n","result2 = network.analysis.solve_location_allocation(\n"," problem_type='Target Market Share', \n"," target_market_share=70,\n"," facilities=city_candidates_fset, \n"," demand_points=patient_locations_weighted,\n"," travel_direction='Demand to Facility',\n"," measurement_units='Minutes', \n"," default_measurement_cutoff=90\n",")\n","\n","print('Solve succeeded? {}'.format(result2.solve_succeeded))"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["# Display the analysis results in a table.\n","result2.output_facilities.sdf[['Name', 'FacilityType', 'DemandCount', 'DemandWeight']]"]},{"cell_type":"markdown","metadata":{},"source":["The `solve_location_allocation` tool selected two of the candidate cities to host new ALS clinics. If ALS clinics are established in both of those cities, 50% of the patients who must currently drive for over 90 minutes to reach an existing clinic would be able to access one of the new clinics in 90 minutes or less.\n","\n","Now, let us visualize the results on a map."]},{"cell_type":"code","execution_count":4,"metadata":{},"outputs":[{"data":{"text/html":[""],"text/plain":[""]},"execution_count":4,"metadata":{},"output_type":"execute_result"}],"source":["# Display the analysis results in a map.\n","\n","# Create a map of Visalia, California\n","map3 = gis.map('Visalia, CA')\n","map3.basemap.basemap = 'dark-gray-vector'\n","\n","# Display the map and add the analysis results to it\n","from IPython.display import display\n","display(map3)\n","visualize_locate_allocate_results(map3, result2, zoom_level=6)"]},{"cell_type":"markdown","metadata":{},"source":["# Conclusions\n","\n","The ArcGIS Network Analysis toolset is a versatile set of tools that assist strategic decision making to reduce the costs of travel. This examples in this notebook only scratch the surface of the situations where you could use these tools. By performing your analysis using Python and the Jupyter Notebook, you also document your methodology in a form that can be easily shared via email to colleagues and stake holders, enabling them to reproduce your results or repeat the analyses in an iterative fashion with different parameters or different input datasets. You can also [export](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.to_csv.html) the output tables from the solve_location_allocation tool by to CSV files to share with stake holders via email."]}],"metadata":{"esriNotebookRuntime":{"notebookRuntimeName":"ArcGIS Notebook Python 3 Standard","notebookRuntimeVersion":"4.0"},"kernelspec":{"display_name":"Python 3","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.8.2"},"toc":{"base_numbering":1,"nav_menu":{},"number_sections":true,"sideBar":true,"skip_h1_title":false,"title_cell":"Table of Contents","title_sidebar":"Contents","toc_cell":true,"toc_position":{},"toc_section_display":true,"toc_window_display":true}},"nbformat":4,"nbformat_minor":2}