{ "cells": [ { "cell_type": "markdown", "id": "f6b30b49", "metadata": {}, "source": [ "# Community Mapping Demo\n", "Author: Rebecca Vandewalle\n", " \n", "This is a short demo for reading a set of text files and mapping counts on an interactive map." ] }, { "cell_type": "code", "execution_count": 1, "id": "a204cde8", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/tmp/ipykernel_197/3691977100.py:3: DeprecationWarning: Shapely 2.0 is installed, but because PyGEOS is also installed, GeoPandas still uses PyGEOS by default. However, starting with version 0.14, the default will switch to Shapely. To force to use Shapely 2.0 now, you can either uninstall PyGEOS or set the environment variable USE_PYGEOS=0. You can do this before starting the Python process, or in your code before importing geopandas:\n", "\n", "import os\n", "os.environ['USE_PYGEOS'] = '0'\n", "import geopandas\n", "\n", "In the next release, GeoPandas will switch to using Shapely by default, even if PyGEOS is installed. If you only have PyGEOS installed to get speed-ups, this switch should be smooth. However, if you are using PyGEOS directly (calling PyGEOS functions on geometries from GeoPandas), this will then stop working and you are encouraged to migrate from PyGEOS to Shapely 2.0 (https://shapely.readthedocs.io/en/latest/migration_pygeos.html).\n", " import geopandas as gpd\n" ] } ], "source": [ "# import needed libraries for running the notebook\n", "# this cell must be run 1st for the other cells in this demo to work\n", "import geopandas as gpd\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import folium\n", "import branca.colormap as cm\n", "import os\n", "import ipywidgets as widgets\n", "from ipywidgets.embed import embed_data\n", "import json\n", "from bs4 import BeautifulSoup as bs" ] }, { "cell_type": "markdown", "id": "f127cf31", "metadata": {}, "source": [ "The first dataset is the Chicago dataset, showing information about communities within the city of Chicago. In this example, the base data file is a geojson file, which has the extension `.geojson`. The raw data is read with the `geopandas` library." ] }, { "cell_type": "code", "execution_count": 2, "id": "89d66a8e", "metadata": {}, "outputs": [], "source": [ "# read the geospatial data (import into a geodataframe) with the geopandas library\n", "chicago = gpd.read_file('Chicago_Community.geojson')" ] }, { "cell_type": "markdown", "id": "b9249e7a", "metadata": {}, "source": [ "We can investigate the base data by viewing attributes, plotting it, and viewing the first several lines of the database." ] }, { "cell_type": "code", "execution_count": 3, "id": "3f7e5d1b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "RangeIndex: 77 entries, 0 to 76\n", "Data columns (total 10 columns):\n", " # Column Non-Null Count Dtype \n", "--- ------ -------------- ----- \n", " 0 community 77 non-null object \n", " 1 area 77 non-null object \n", " 2 shape_area 77 non-null object \n", " 3 perimeter 77 non-null object \n", " 4 area_num_1 77 non-null object \n", " 5 area_numbe 77 non-null object \n", " 6 comarea_id 77 non-null object \n", " 7 comarea 77 non-null object \n", " 8 shape_len 77 non-null object \n", " 9 geometry 77 non-null geometry\n", "dtypes: geometry(1), object(9)\n", "memory usage: 6.1+ KB\n" ] } ], "source": [ "# get basic info about the chicago dataset\n", "chicago.info()" ] }, { "cell_type": "code", "execution_count": 4, "id": "efe04856", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# plot the geospatial data\n", "chicago.plot()" ] }, { "cell_type": "code", "execution_count": 5, "id": "0acfa272", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
communityareashape_areaperimeterarea_num_1area_numbecomarea_idcomareashape_lengeometry
0DOUGLAS046004621.1581035350031027.0545098MULTIPOLYGON (((-87.60914 41.84469, -87.60915 ...
1OAKLAND016913961.0408036360019565.5061533MULTIPOLYGON (((-87.59215 41.81693, -87.59231 ...
2FULLER PARK019916704.8692037370025339.0897503MULTIPOLYGON (((-87.62880 41.80189, -87.62879 ...
3GRAND BOULEVARD048492503.1554038380028196.8371573MULTIPOLYGON (((-87.60671 41.81681, -87.60670 ...
4KENWOOD029071741.9283039390023325.1679062MULTIPOLYGON (((-87.59215 41.81693, -87.59215 ...
\n", "
" ], "text/plain": [ " community area shape_area perimeter area_num_1 area_numbe \\\n", "0 DOUGLAS 0 46004621.1581 0 35 35 \n", "1 OAKLAND 0 16913961.0408 0 36 36 \n", "2 FULLER PARK 0 19916704.8692 0 37 37 \n", "3 GRAND BOULEVARD 0 48492503.1554 0 38 38 \n", "4 KENWOOD 0 29071741.9283 0 39 39 \n", "\n", " comarea_id comarea shape_len \\\n", "0 0 0 31027.0545098 \n", "1 0 0 19565.5061533 \n", "2 0 0 25339.0897503 \n", "3 0 0 28196.8371573 \n", "4 0 0 23325.1679062 \n", "\n", " geometry \n", "0 MULTIPOLYGON (((-87.60914 41.84469, -87.60915 ... \n", "1 MULTIPOLYGON (((-87.59215 41.81693, -87.59231 ... \n", "2 MULTIPOLYGON (((-87.62880 41.80189, -87.62879 ... \n", "3 MULTIPOLYGON (((-87.60671 41.81681, -87.60670 ... \n", "4 MULTIPOLYGON (((-87.59215 41.81693, -87.59215 ... " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# look through the first several lines\n", "chicago.head()" ] }, { "cell_type": "markdown", "id": "8167faa6", "metadata": {}, "source": [ "# Input data (Communities and counts)\n", "For this demo, I've created a few text files with a row of community names and a count attribute. Each text file is named with the original community name." ] }, { "cell_type": "code", "execution_count": 6, "id": "c8769d6e", "metadata": {}, "outputs": [], "source": [ "# read in the text file (import into a dataframe) with the pandas library\n", "douglas_txt = pd.read_csv('communities/DOUGLAS.txt')" ] }, { "cell_type": "code", "execution_count": 7, "id": "8a561837", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
communitycount
0OAKLAND10
1FULLER PARK5
2KENWOOD5
3OHARE12
4EDGEWATER3
5MORGAN PARK6
\n", "
" ], "text/plain": [ " community count\n", "0 OAKLAND 10\n", "1 FULLER PARK 5\n", "2 KENWOOD 5\n", "3 OHARE 12\n", "4 EDGEWATER 3\n", "5 MORGAN PARK 6" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# look through the first several lines\n", "douglas_txt" ] }, { "cell_type": "code", "execution_count": 8, "id": "88b7934a", "metadata": {}, "outputs": [], "source": [ "# merge douglas data with chicago dataset\n", "douglas_comm = chicago.merge(douglas_txt, how='left', on='community')\n", "# Replace NaNs with 0 (community count is 0)\n", "douglas_comm = douglas_comm.fillna(value=0)\n", "# rename 'count' column to 'douglas'\n", "douglas_comm = douglas_comm.rename(columns={\"count\": \"douglas\"})" ] }, { "cell_type": "code", "execution_count": 9, "id": "6fbdf9b4", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
communityareashape_areaperimeterarea_num_1area_numbecomarea_idcomareashape_lengeometrydouglas
0DOUGLAS046004621.1581035350031027.0545098MULTIPOLYGON (((-87.60914 41.84469, -87.60915 ...0.0
1OAKLAND016913961.0408036360019565.5061533MULTIPOLYGON (((-87.59215 41.81693, -87.59231 ...10.0
2FULLER PARK019916704.8692037370025339.0897503MULTIPOLYGON (((-87.62880 41.80189, -87.62879 ...5.0
3GRAND BOULEVARD048492503.1554038380028196.8371573MULTIPOLYGON (((-87.60671 41.81681, -87.60670 ...0.0
4KENWOOD029071741.9283039390023325.1679062MULTIPOLYGON (((-87.59215 41.81693, -87.59215 ...5.0
\n", "
" ], "text/plain": [ " community area shape_area perimeter area_num_1 area_numbe \\\n", "0 DOUGLAS 0 46004621.1581 0 35 35 \n", "1 OAKLAND 0 16913961.0408 0 36 36 \n", "2 FULLER PARK 0 19916704.8692 0 37 37 \n", "3 GRAND BOULEVARD 0 48492503.1554 0 38 38 \n", "4 KENWOOD 0 29071741.9283 0 39 39 \n", "\n", " comarea_id comarea shape_len \\\n", "0 0 0 31027.0545098 \n", "1 0 0 19565.5061533 \n", "2 0 0 25339.0897503 \n", "3 0 0 28196.8371573 \n", "4 0 0 23325.1679062 \n", "\n", " geometry douglas \n", "0 MULTIPOLYGON (((-87.60914 41.84469, -87.60915 ... 0.0 \n", "1 MULTIPOLYGON (((-87.59215 41.81693, -87.59231 ... 10.0 \n", "2 MULTIPOLYGON (((-87.62880 41.80189, -87.62879 ... 5.0 \n", "3 MULTIPOLYGON (((-87.60671 41.81681, -87.60670 ... 0.0 \n", "4 MULTIPOLYGON (((-87.59215 41.81693, -87.59215 ... 5.0 " ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# look through the first several lines of merged dataset\n", "douglas_comm.head()" ] }, { "cell_type": "code", "execution_count": 10, "id": "e4c354da", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
communityareashape_areaperimeterarea_num_1area_numbecomarea_idcomareashape_lengeometrydouglas
1OAKLAND016913961.0408036360019565.5061533MULTIPOLYGON (((-87.59215 41.81693, -87.59231 ...10.0
2FULLER PARK019916704.8692037370025339.0897503MULTIPOLYGON (((-87.62880 41.80189, -87.62879 ...5.0
4KENWOOD029071741.9283039390023325.1679062MULTIPOLYGON (((-87.59215 41.81693, -87.59215 ...5.0
73MORGAN PARK091877340.6988075750046396.419362MULTIPOLYGON (((-87.64215 41.68508, -87.64249 ...6.0
74OHARE0371835607.6870767600173625.98466MULTIPOLYGON (((-87.83658 41.98640, -87.83658 ...12.0
75EDGEWATER048449990.8397077770031004.8309456MULTIPOLYGON (((-87.65456 41.99817, -87.65456 ...3.0
\n", "
" ], "text/plain": [ " community area shape_area perimeter area_num_1 area_numbe \\\n", "1 OAKLAND 0 16913961.0408 0 36 36 \n", "2 FULLER PARK 0 19916704.8692 0 37 37 \n", "4 KENWOOD 0 29071741.9283 0 39 39 \n", "73 MORGAN PARK 0 91877340.6988 0 75 75 \n", "74 OHARE 0 371835607.687 0 76 76 \n", "75 EDGEWATER 0 48449990.8397 0 77 77 \n", "\n", " comarea_id comarea shape_len \\\n", "1 0 0 19565.5061533 \n", "2 0 0 25339.0897503 \n", "4 0 0 23325.1679062 \n", "73 0 0 46396.419362 \n", "74 0 0 173625.98466 \n", "75 0 0 31004.8309456 \n", "\n", " geometry douglas \n", "1 MULTIPOLYGON (((-87.59215 41.81693, -87.59231 ... 10.0 \n", "2 MULTIPOLYGON (((-87.62880 41.80189, -87.62879 ... 5.0 \n", "4 MULTIPOLYGON (((-87.59215 41.81693, -87.59215 ... 5.0 \n", "73 MULTIPOLYGON (((-87.64215 41.68508, -87.64249 ... 6.0 \n", "74 MULTIPOLYGON (((-87.83658 41.98640, -87.83658 ... 12.0 \n", "75 MULTIPOLYGON (((-87.65456 41.99817, -87.65456 ... 3.0 " ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# check joined data (which communities have a douglas count value)\n", "douglas_comm[douglas_comm['douglas'] > 0]" ] }, { "cell_type": "markdown", "id": "ab7b0b5e", "metadata": {}, "source": [ "# The Folium package\n", "The `Folium` package is an easy way to create a map using Python. The mapping capability is based on the `Leaflet` package, originally designed for JavaScript." ] }, { "cell_type": "code", "execution_count": 11, "id": "940d718a", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# create the base map object\n", "m = folium.Map([42, -87.5], zoom_start=10)\n", "\n", "# create a linear colormap to display data\n", "linear_cm = cm.linear.YlGnBu_09.scale(0, 20)\n", "\n", "# add the community counts data to the map\n", "folium.GeoJson(\n", " douglas_comm,\n", " style_function=lambda feature: {\n", " \"fillColor\": linear_cm(feature[\"properties\"][\"douglas\"]),\n", " \"fillOpacity\": 0.9 \n", " if feature[\"properties\"][\"douglas\"] > 0\n", " else 0.0,\n", " \"color\": \"black\",\n", " \"weight\": 1,\n", " },\n", " control=True).add_to(m)\n", "\n", "# add the color scale to the map\n", "m.add_child(linear_cm)\n", "\n", "# add a tooltip layer view the counts on hover\n", "tooltip = folium.GeoJsonTooltip(\n", " fields=[\"community\", \"douglas\"],\n", " aliases=[\"Community:\", \"Migration from Douglas:\"],\n", " localize=True,\n", " sticky=False,\n", " labels=True,\n", " max_width=800,\n", ")\n", "\n", "# add a geojson layer to view the tooltip\n", "# make fill and stroke false because style is set in choropleth layer\n", "g = folium.GeoJson(\n", " douglas_comm,\n", " style_function=lambda x: {\n", " \"fillOpacity\": 0.0,\n", " \"stroke\": False},\n", " tooltip=tooltip,\n", " control=False).add_to(m)\n", "\n", "# add a layer control tool\n", "folium.LayerControl().add_to(m)\n", "\n", "# display the map\n", "m" ] }, { "cell_type": "markdown", "id": "a4e271be", "metadata": {}, "source": [ "# Batch import & create an interactive map" ] }, { "cell_type": "markdown", "id": "9f3336d5", "metadata": {}, "source": [ "The previous section contains a demo on how to add count features from a text file to an interactive web map. In this section, we'll use a script to add count features from multiple text files.\n", "\n", "## Assumptions:\n", "- All community counts are in text files within the `communities` folder\n", "- The name of the text file is the source community of the count\n", "- Communities listed within in the text file contain counts for the destination community of the community listed in the text file filename\n", "- Community names match exactly between the textfile names and the community names in the geospatial file" ] }, { "cell_type": "markdown", "id": "f556a078", "metadata": {}, "source": [ "## Data processing\n", "First we can process all the community text files in the `communities` folder." ] }, { "cell_type": "code", "execution_count": 12, "id": "c620f7f8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Communities from file: ['DOUGLAS', 'EDISON PARK', 'KENWOOD']\n" ] } ], "source": [ "# Gather community names from files\n", "# set the path to the communities directory (from within the current directory)\n", "path = \"./communities\"\n", "# get all filenames in the communities directory\n", "community_list = os.listdir(path)\n", "# get community names for only files that end in .txt\n", "community_list = [x[:-4] for x in community_list if x[-4:] == '.txt']\n", "print(\"Communities from file: \", community_list)\n" ] }, { "cell_type": "code", "execution_count": 13, "id": "94e82461", "metadata": {}, "outputs": [], "source": [ "# load chicago dataset (to get a clean version)\n", "full_gdf = gpd.read_file('Chicago_Community.geojson')" ] }, { "cell_type": "markdown", "id": "c2c938b1", "metadata": {}, "source": [ "Now we can create a geodataframe containing all the community counts from the text files." ] }, { "cell_type": "code", "execution_count": 14, "id": "14252c6d", "metadata": {}, "outputs": [], "source": [ "# COMMUNITY LOOP\n", "\n", "# Load communities in a loop and join with chicago dataset\n", "for community_name in community_list:\n", " community_path = 'communities/'+community_name+'.txt'\n", " community_txt = pd.read_csv(community_path)\n", " \n", " # merge community data with chicago dataset\n", " full_gdf = full_gdf.merge(community_txt, how='left', on='community')\n", " # Replace NaNs with 0 (community count is 0)\n", " full_gdf = full_gdf.fillna(value=0)\n", " # rename 'count' column to community name\n", " full_gdf = full_gdf.rename(columns={\"count\": community_name.title()})\n", " \n", "# END COMMUNITY LOOP" ] }, { "cell_type": "code", "execution_count": 15, "id": "2bb6ca9d", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
communityareashape_areaperimeterarea_num_1area_numbecomarea_idcomareashape_lengeometryDouglasEdison ParkKenwood
0DOUGLAS046004621.1581035350031027.0545098MULTIPOLYGON (((-87.60914 41.84469, -87.60915 ...0.00.00.0
1OAKLAND016913961.0408036360019565.5061533MULTIPOLYGON (((-87.59215 41.81693, -87.59231 ...10.00.01.0
2FULLER PARK019916704.8692037370025339.0897503MULTIPOLYGON (((-87.62880 41.80189, -87.62879 ...5.02.00.0
3GRAND BOULEVARD048492503.1554038380028196.8371573MULTIPOLYGON (((-87.60671 41.81681, -87.60670 ...0.00.010.0
4KENWOOD029071741.9283039390023325.1679062MULTIPOLYGON (((-87.59215 41.81693, -87.59215 ...5.05.01.0
\n", "
" ], "text/plain": [ " community area shape_area perimeter area_num_1 area_numbe \\\n", "0 DOUGLAS 0 46004621.1581 0 35 35 \n", "1 OAKLAND 0 16913961.0408 0 36 36 \n", "2 FULLER PARK 0 19916704.8692 0 37 37 \n", "3 GRAND BOULEVARD 0 48492503.1554 0 38 38 \n", "4 KENWOOD 0 29071741.9283 0 39 39 \n", "\n", " comarea_id comarea shape_len \\\n", "0 0 0 31027.0545098 \n", "1 0 0 19565.5061533 \n", "2 0 0 25339.0897503 \n", "3 0 0 28196.8371573 \n", "4 0 0 23325.1679062 \n", "\n", " geometry Douglas Edison Park \\\n", "0 MULTIPOLYGON (((-87.60914 41.84469, -87.60915 ... 0.0 0.0 \n", "1 MULTIPOLYGON (((-87.59215 41.81693, -87.59231 ... 10.0 0.0 \n", "2 MULTIPOLYGON (((-87.62880 41.80189, -87.62879 ... 5.0 2.0 \n", "3 MULTIPOLYGON (((-87.60671 41.81681, -87.60670 ... 0.0 0.0 \n", "4 MULTIPOLYGON (((-87.59215 41.81693, -87.59215 ... 5.0 5.0 \n", "\n", " Kenwood \n", "0 0.0 \n", "1 1.0 \n", "2 0.0 \n", "3 10.0 \n", "4 1.0 " ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Check full database\n", "full_gdf.head()" ] }, { "cell_type": "markdown", "id": "1b17d3e2", "metadata": {}, "source": [ "We can get the min and max values from the community counts to generate a range for coloring." ] }, { "cell_type": "code", "execution_count": 16, "id": "7e4200c5", "metadata": {}, "outputs": [], "source": [ "# Get the min and max values for the counts for all the loaded communities\n", "count_min = int(full_gdf[[community_name.title() for community_name in community_list]].agg(['min']).min(axis=1).iloc[0])\n", "count_max = int(full_gdf[[community_name.title() for community_name in community_list]].agg(['max']).max(axis=1).iloc[0])" ] }, { "cell_type": "markdown", "id": "5c3ec414", "metadata": {}, "source": [ "## Mapping\n", "Now we can create an interactive map. First we can create a function that creates a map for a given community." ] }, { "cell_type": "code", "execution_count": 17, "id": "c1429202", "metadata": {}, "outputs": [], "source": [ "# create a function to create community maps\n", "def create_community_map_html(community_name, geodataframe, color_bar_min, color_bar_max):\n", " \n", " # create a linear colormap to display data\n", " linear_comm_cm = cm.linear.YlGnBu_09.scale(color_bar_min, color_bar_max)\n", "\n", " # Map output with Folium webmap\n", " # create the base map object\n", " m = folium.Map([42, -87.5], zoom_start=10, zoom_control=\"topleft\")\n", "\n", " # Load the Map\n", " # Add the community counts data to the map\n", " folium.GeoJson(\n", " geodataframe,\n", " name=community_name,\n", " style_function=lambda feature: {\n", " \"fillColor\": linear_cm(feature[\"properties\"][community_name]),\n", " \"fillOpacity\": 0.9 \n", " if feature[\"properties\"][community_name] > 0\n", " else 0.0,\n", " \"color\": \"black\",\n", " \"weight\": 1,\n", " },\n", " control=True).add_to(m)\n", "\n", " # Add a tooltip layer view the counts on hover\n", " tooltip_string = \"Migration from \"+community_name+\":\"\n", " tooltip = folium.GeoJsonTooltip(\n", " fields=[\"community\", community_name],\n", " aliases=[\"Community:\", tooltip_string],\n", " localize=True,\n", " sticky=False,\n", " labels=True,\n", " max_width=800,\n", " )\n", "\n", " # Add a geojson layer to view the tooltip\n", " # Make fill and stroke false because style is set in choropleth layer\n", " g = folium.GeoJson(\n", " full_gdf,\n", " style_function=lambda x: {\n", " \"fillOpacity\": 0.0,\n", " \"stroke\": False},\n", " tooltip=tooltip,\n", " control=False).add_to(m)\n", "\n", " # Finish setting up Folium map\n", " # Add a map title\n", " title_text = \"Community Counts for \"+community_name\n", " title_html = f'

{title_text}

'\n", " m.get_root().html.add_child(folium.Element(title_html))\n", " \n", " # Add the color scale to the map\n", " m.add_child(linear_comm_cm)\n", "\n", " # Add a layer control tool\n", " folium.LayerControl(position=\"topleft\").add_to(m)\n", " \n", " return(m)" ] }, { "cell_type": "markdown", "id": "4a96754b", "metadata": {}, "source": [ "Now we can create a dictionary to hold maps for each community, and iterate through communities, creating a map for each community and storing it in an `Output` widget (the type of widget used for display)." ] }, { "cell_type": "code", "execution_count": 18, "id": "deb9fb6f", "metadata": {}, "outputs": [], "source": [ "# create a list to hold community maps\n", "out_widgets_list = []\n", "\n", "# containing output widgets with maps for each community\n", "for community_name in community_list:\n", " # create an output widget to display the map\n", " out = widgets.Output()\n", " m = create_community_map_html(community_name.title(), full_gdf, count_min, count_max)\n", " out.append_display_data(m)\n", " out_widgets_list.append(out)\n" ] }, { "cell_type": "markdown", "id": "14c28fb3", "metadata": {}, "source": [ "Once we have created the map series we can view a community map using the community name (in title case) as a key." ] }, { "cell_type": "code", "execution_count": 19, "id": "9a3ff163", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "26f204a83bb54637a23f9e428eb0cbbc", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': ''…" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# See an example map\n", "out_widgets_list[0]" ] }, { "cell_type": "markdown", "id": "f002c7ab", "metadata": {}, "source": [ "## Export the map to html\n", "You can export the created map to an html file and then load the html file to view and interact with the exported map. Here we have additional code to view the final html file with a dropdown." ] }, { "cell_type": "markdown", "id": "2163da86", "metadata": {}, "source": [ "First we'll create a html template used to build the html file. The first section contains the start of the needed html code. Primarily, it includes the scripts needed for the widget dislay to be embedded. Then maps for each of the communities are added and finally the html closing tags are appended." ] }, { "cell_type": "code", "execution_count": 20, "id": "f03b42ff", "metadata": {}, "outputs": [], "source": [ "# create the start of the html template\n", "\n", "html_template = '''\n", "\n", " \n", "\n", " Community Map\n", "\n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", "\n", " \n", "

Community Count Maps

\n", " \n", " \n", "'''\n", "\n", "# add each community map\n", "for i, e in enumerate(community_list):\n", " community_id = \"\".join(e.split())\n", " # add index for each community output widget\n", " html_template += '''\n", "
\n", " \n", " \n", "
\n", " ''' \n", "\n", "# add the closing html tags\n", "html_template += '''\n", "\n", " \n", "\n", "'''" ] }, { "cell_type": "markdown", "id": "53fbb2c1", "metadata": {}, "source": [ "Now we create the base html file from the widget." ] }, { "cell_type": "code", "execution_count": 21, "id": "e86521ee", "metadata": {}, "outputs": [], "source": [ "data = embed_data(views=[*out_widgets_list])\n", "\n", "manager_state = json.dumps(data['manager_state'])\n", "widget_views = [json.dumps(view) for view in data['view_specs']]\n", "rendered_template = html_template.format(manager_state=manager_state, widget_views=widget_views)\n", "with open('community_map.html', 'w') as fp:\n", " fp.write(rendered_template)" ] }, { "cell_type": "markdown", "id": "d5ecbb18", "metadata": {}, "source": [ "Finally, we can use the BeautifulSoup html parser library to add a style and script to the html for a nicer display using the dropdown to select the map." ] }, { "cell_type": "code", "execution_count": 22, "id": "64a3b92e", "metadata": {}, "outputs": [], "source": [ "# open the document with beautiful soup\n", "html=open('community_map.html')\n", "soup=bs(html, 'html.parser')\n", "\n", "# Add the style info to the placeholder style tag.\n", "style_tag = soup.find(id=\"main_style\")\n", "style_tag.string = '''\n", ".inv {\n", " display: none;\n", "}\n", "'''\n", "\n", "# Add the script to the placeholder script tag.\n", "script_tag = soup.find(id=\"end_script\")\n", "script_tag.string = '''\n", " document\n", " .getElementById('target')\n", " .addEventListener('change', function () {\n", " 'use strict';\n", " var vis = document.querySelector('.vis'), \n", " target = document.getElementById(this.value);\n", " if (vis !== null) {\n", " vis.className = 'inv';\n", " }\n", " if (target !== null ) {\n", " target.className = 'vis';\n", " }\n", " });\n", " '''\n", "# Save the new html document with the added script.\n", "with open(\"community_map.html\", \"wb\") as f_out:\n", " f_out.write(soup.prettify('utf-8'))\n" ] }, { "cell_type": "markdown", "id": "b942a203", "metadata": {}, "source": [ "### Open the HTML file\n", "Now you should be able to open the `community_map.html` file to see the different maps loaded when the community is selected using the dropdown. The html file may take a while to initially load due to the information needed for each folium map.\n", "\n", "Note: You will likely need to download the html file to your computer and then open it on an internet browser. It will not display the interactive maps within a JupyterLab tab." ] }, { "cell_type": "code", "execution_count": null, "id": "53723a87", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "iguide", "language": "python", "name": "iguide" }, "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.16" } }, "nbformat": 4, "nbformat_minor": 5 }