diff --git a/_toc.yml b/_toc.yml index cc57b67..7eab632 100644 --- a/_toc.yml +++ b/_toc.yml @@ -17,6 +17,7 @@ parts: chapters: - file: book/02a_buildings_currentness_DuckDB_PyIceberg.ipynb - file: book/02_vandalism_detection.ipynb + - file: book/02_attribute_completeness.ipynb - caption: Data Integration chapters: - file: book/03_hot_tm_project_analysis.ipynb diff --git a/book/02_attribute_completeness.ipynb b/book/02_attribute_completeness.ipynb new file mode 100644 index 0000000..886edbc --- /dev/null +++ b/book/02_attribute_completeness.ipynb @@ -0,0 +1,924 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6878ccac-f936-438b-80f3-43ec4af03f02", + "metadata": {}, + "source": [ + "# Attribute Completeness for Street Surface Tags\n", + "In this notebook we demonstrate how can assess the completeness of tags in OSM.\n", + "\n", + "These are the steps you see further down:\n", + "\n", + "* Set the connection parameters.\n", + "* Prepare your input parameters, e.g. define area of interest and attribute filters.\n", + "* **Download data** using PyIceberg and DuckDB.\n", + "* Filter and process data with DuckDB.\n", + "* Visualize the results on a map." + ] + }, + { + "cell_type": "markdown", + "id": "e8a6cd1b-89bb-4475-bfab-f9996e019f4a", + "metadata": {}, + "source": [ + "## Getting started\n", + "Set connection params." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6d3a0f6c-f845-4bd6-9f4d-96b330e88729", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "s3_user = os.environ[\"S3_ACCESS_KEY_ID\"] # add your user here\n", + "s3_password = os.environ[\"S3_SECRET_ACCESS_KEY\"] # add your password here" + ] + }, + { + "cell_type": "markdown", + "id": "8e49489b-dfa6-41ef-96c8-b0418904415e", + "metadata": {}, + "source": [ + "Configure DuckDB." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bff7f6db-767b-476b-95a8-7335c248084c", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install duckdb==1.0.0" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "eba19d51-892b-4447-8c69-321799a90e67", + "metadata": {}, + "outputs": [], + "source": [ + "import duckdb\n", + "\n", + "con = duckdb.connect(\n", + " config={\n", + " 'threads': 8,\n", + " 'max_memory': '8GB',\n", + " }\n", + ")\n", + "con.install_extension(\"spatial\")\n", + "con.load_extension(\"spatial\")" + ] + }, + { + "cell_type": "markdown", + "id": "cb32de22-3022-4568-9a73-28c84e8c9600", + "metadata": {}, + "source": [ + "Set the connection params to Iceberg Rest Catalog." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4de25b6f-c440-4c71-b93b-b26682cd9175", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install \"pyiceberg[s3fs,duckdb,sql-sqlite,pyarrow]\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "de3fedad-a111-4d45-a81e-bc5a4c1cca41", + "metadata": {}, + "outputs": [], + "source": [ + "from pyiceberg.catalog.rest import RestCatalog\n", + "\n", + "catalog = RestCatalog(\n", + " name=\"default\",\n", + " **{\n", + " \"uri\": \"https://sotm2024.iceberg.ohsome.org\",\n", + " \"s3.endpoint\": \"https://sotm2024.minio.heigit.org\",\n", + " \"py-io-impl\": \"pyiceberg.io.pyarrow.PyArrowFileIO\",\n", + " \"s3.access-key-id\": s3_user,\n", + " \"s3.secret-access-key\": s3_password,\n", + " \"s3.region\": \"eu-central-1\"\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "41123367-d6d7-4b2c-b4a7-400f1513e88d", + "metadata": {}, + "source": [ + "Set connection to MinIO object storage." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9f09eb04-35f2-44b4-b69d-aaca90b2b5cc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "┌─────────┐\n", + "│ Success │\n", + "│ boolean │\n", + "├─────────┤\n", + "│ true │\n", + "└─────────┘\n", + "\n" + ] + } + ], + "source": [ + "query = f\"\"\"\n", + "DROP SECRET IF EXISTS \"__default_s3\";\n", + "CREATE SECRET (\n", + " TYPE S3,\n", + " KEY_ID '{s3_user}',\n", + " SECRET '{s3_password}',\n", + " REGION 'eu-central-1',\n", + " endpoint 'sotm2024.minio.heigit.org',\n", + " use_ssl true,\n", + " url_style 'path'\n", + " );\n", + "\"\"\"\n", + "con.sql(query).show()" + ] + }, + { + "cell_type": "markdown", + "id": "a70d9e71-0fe8-4ed4-83b6-96f83c93c8aa", + "metadata": {}, + "source": [ + "## Prepare the input parameters for your analysis" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "a3ed4fd7-aedb-4c58-9c9d-e9f44918afb4", + "metadata": {}, + "outputs": [], + "source": [ + "# Set iceberg table\n", + "namespace = 'geo_sort'\n", + "tablename = 'contributions'\n", + "icebergtable = catalog.load_table((namespace, tablename))\n", + "\n", + "\n", + "# Define status filter\n", + "status = 'latest'\n", + "\n", + "\n", + "# Define location filter\n", + "bboxes = {\n", + " 'heidelberg': (8.629761, 49.379556, 8.742371, 49.437890),\n", + " 'nairobi': (36.650938, -1.444471, 37.103887, -1.163522),\n", + " 'mannheim': (8.41416, 49.410362, 8.58999, 49.590489)\n", + "}\n", + "\n", + "selected_region = 'mannheim'\n", + "xmin, ymin, xmax, ymax = bboxes[selected_region]\n", + "area_of_interest_file = f\"s3a://heigit-ohsome-sotm24/data/sample_data/{selected_region}.geojson\"\n", + "\n", + "\n", + "# Define geometry type filter\n", + "geometry_type = 'LineString'" + ] + }, + { + "cell_type": "markdown", + "id": "efe47258-eb04-4031-a517-28ef88ba05ed", + "metadata": {}, + "source": [ + "## Get the Data\n", + "First, we do an iceberg table scan with a pre-filter. This is a fast way to download all potential OSM elements that are needed for our analysis." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9dbe857e-b355-46cd-b7a3-6bd309914ba2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "download took 18.676 sec.\n" + ] + } + ], + "source": [ + "import time\n", + "start_time = time.time()\n", + "\n", + "icebergtable.scan(\n", + " row_filter=(\n", + " f\"status = '{status}' \"\n", + " f\"and geometry_type = '{geometry_type}' \"\n", + " f\"and (xmax >= {xmin} and xmin <= {xmax}) \"\n", + " f\"and (ymax >= {ymin} and ymin <= {ymax}) \"\n", + " ),\n", + " selected_fields=(\n", + " \"osm_id\",\n", + " \"tags\",\n", + " \"length\",\n", + " \"geometry\",\n", + " ),\n", + ").to_duckdb('raw_osm_data',connection=con)\n", + "\n", + "download_time = round(time.time() - start_time, 3)\n", + "print(f\"download took {download_time} sec.\")" + ] + }, + { + "cell_type": "markdown", + "id": "1d7b4c77-1c03-4371-9eee-f1e242898ea3", + "metadata": {}, + "source": [ + "## Filter and process data with DuckDB\n", + "Here we extract the tag values for highways and their correspoding road surface type (if mapped). We also clip the road geometry to the area of interest and calculate the length for the clipped geometry." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "a11509b0-66c4-4c1b-8b04-9cfc34441167", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "962e47d4e319425b8be86f4067868eb8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, layout=Layout(width='auto'), style=ProgressStyle(bar_color='black'))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "processing took 7.358 sec.\n" + ] + } + ], + "source": [ + "import time\n", + "start_time = time.time()\n", + "\n", + "query = f\"\"\"\n", + "DROP TABLE IF EXISTS osm_data;\n", + "CREATE TABLE osm_data AS\n", + "(\n", + "SELECT\n", + " tags['highway'][1] as highway_tag_value,\n", + " tags['surface'][1] as surface_tag_value,\n", + " ST_GeomFromText(a.geometry) as osm_geom,\n", + " CASE\n", + " WHEN ST_Within(osm_geom, aoi.geom) THEN osm_geom\n", + " ELSE ST_Intersection(osm_geom, aoi.geom)\n", + " END as clipped_geometry,\n", + " CASE\n", + " WHEN ST_Within(osm_geom, aoi.geom) THEN length\n", + " ELSE ST_Length_Spheroid(clipped_geometry) / 1000\n", + " END as length_km\n", + "FROM\n", + " raw_osm_data as a,\n", + " st_read('{area_of_interest_file}') as aoi\n", + "WHERE 1=1\n", + " and tags['highway'][1] is not null\n", + " -- spatial filtering part\n", + " and ST_Intersects(st_GeomFromText(a.geometry), aoi.geom)\n", + ")\n", + ";\n", + "\"\"\"\n", + "con.sql(query)\n", + "\n", + "processing_time = round(time.time() - start_time, 3)\n", + "print(f\"processing took {processing_time} sec.\")" + ] + }, + { + "cell_type": "markdown", + "id": "51cf91c1-6de4-4081-8ee1-1c0ffe717551", + "metadata": {}, + "source": [ + "## Proporation of Roads with Surface Tag\n", + "Let's inspect how many roads have a surface tag." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4a979e9a-0f5f-404b-879d-cb403328c53d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "┌──────────────────┬────────────────────────────┬─────────────────────────────┐\n", + "│ length_km_total │ length_km_with_surface_tag │ proportion_with_surface_tag │\n", + "│ double │ double │ double │\n", + "├──────────────────┼────────────────────────────┼─────────────────────────────┤\n", + "│ 3285612.61274888 │ 2259704.672583999 │ 0.688 │\n", + "└──────────────────┴────────────────────────────┴─────────────────────────────┘" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = f\"\"\"\n", + "SELECT\n", + " SUM(length_km) as length_km_total,\n", + " SUM(CASE\n", + " WHEN surface_tag_value IS NOT NULL THEN length_km\n", + " ELSE 0\n", + " END) as length_km_with_surface_tag,\n", + " round(length_km_with_surface_tag / length_km_total, 3) as proportion_with_surface_tag\n", + "FROM osm_data\n", + "\"\"\"\n", + "con.sql(query)" + ] + }, + { + "cell_type": "markdown", + "id": "dc8ac015-aa8f-4de6-894a-c7adf964e50a", + "metadata": {}, + "source": [ + "## Proporation of Roads with Surface Tag per Road Type\n", + "We can break down by road type. For some road types the surface tag is mapped better than for others." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bfb223db-ceae-4ec1-a688-dd8f587b8adb", + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
highway_tag_valuelength_km_totallength_km_with_surface_tagproportion_with_surface_tag
0service694704.602918278210.1481660.400
1footway546853.269568307684.6514410.563
2track477267.685579285274.5862970.598
3residential464907.754558447053.6297560.962
4path446605.435393343686.0390510.770
5unclassified118270.02187097051.0218700.821
6tertiary110620.269124109950.2691240.994
7secondary83886.04952483724.0495240.998
8primary70041.95126669604.9512660.994
9living_street48703.00000042401.0000000.871
10motorway47689.84784747511.8478470.996
11cycleway36750.00000031285.0000000.851
12trunk26581.87131925673.8691330.966
13bridleway25781.88163318633.8715210.723
14motorway_link18060.63057717632.6305770.976
15trunk_link14148.10701113349.1070110.944
16pedestrian12229.00000010507.0000000.859
17secondary_link11996.00000011982.0000000.999
18steps11948.0028845219.0000000.437
19primary_link10418.00000010171.0000000.976
20construction2905.231679620.0000000.213
21tertiary_link1958.0000001902.0000000.971
22corridor935.000000339.0000000.363
23busway818.00000049.0000000.060
24proposed612.0000000.0000000.000
25platform441.0000000.0000000.000
26raceway189.000000189.0000001.000
27fi177.0000000.0000000.000
28bus_stop82.0000000.0000000.000
29street_lamp32.0000000.0000000.000
\n", + "
" + ], + "text/plain": [ + " highway_tag_value length_km_total length_km_with_surface_tag \\\n", + "0 service 694704.602918 278210.148166 \n", + "1 footway 546853.269568 307684.651441 \n", + "2 track 477267.685579 285274.586297 \n", + "3 residential 464907.754558 447053.629756 \n", + "4 path 446605.435393 343686.039051 \n", + "5 unclassified 118270.021870 97051.021870 \n", + "6 tertiary 110620.269124 109950.269124 \n", + "7 secondary 83886.049524 83724.049524 \n", + "8 primary 70041.951266 69604.951266 \n", + "9 living_street 48703.000000 42401.000000 \n", + "10 motorway 47689.847847 47511.847847 \n", + "11 cycleway 36750.000000 31285.000000 \n", + "12 trunk 26581.871319 25673.869133 \n", + "13 bridleway 25781.881633 18633.871521 \n", + "14 motorway_link 18060.630577 17632.630577 \n", + "15 trunk_link 14148.107011 13349.107011 \n", + "16 pedestrian 12229.000000 10507.000000 \n", + "17 secondary_link 11996.000000 11982.000000 \n", + "18 steps 11948.002884 5219.000000 \n", + "19 primary_link 10418.000000 10171.000000 \n", + "20 construction 2905.231679 620.000000 \n", + "21 tertiary_link 1958.000000 1902.000000 \n", + "22 corridor 935.000000 339.000000 \n", + "23 busway 818.000000 49.000000 \n", + "24 proposed 612.000000 0.000000 \n", + "25 platform 441.000000 0.000000 \n", + "26 raceway 189.000000 189.000000 \n", + "27 fi 177.000000 0.000000 \n", + "28 bus_stop 82.000000 0.000000 \n", + "29 street_lamp 32.000000 0.000000 \n", + "\n", + " proportion_with_surface_tag \n", + "0 0.400 \n", + "1 0.563 \n", + "2 0.598 \n", + "3 0.962 \n", + "4 0.770 \n", + "5 0.821 \n", + "6 0.994 \n", + "7 0.998 \n", + "8 0.994 \n", + "9 0.871 \n", + "10 0.996 \n", + "11 0.851 \n", + "12 0.966 \n", + "13 0.723 \n", + "14 0.976 \n", + "15 0.944 \n", + "16 0.859 \n", + "17 0.999 \n", + "18 0.437 \n", + "19 0.976 \n", + "20 0.213 \n", + "21 0.971 \n", + "22 0.363 \n", + "23 0.060 \n", + "24 0.000 \n", + "25 0.000 \n", + "26 1.000 \n", + "27 0.000 \n", + "28 0.000 \n", + "29 0.000 " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "query = f\"\"\"\n", + "SELECT\n", + " highway_tag_value,\n", + " SUM(length_km) as length_km_total,\n", + " SUM(CASE\n", + " WHEN surface_tag_value IS NOT NULL THEN length_km\n", + " ELSE 0\n", + " END) as length_km_with_surface_tag,\n", + " round(length_km_with_surface_tag / length_km_total, 3) as proportion_with_surface_tag\n", + "FROM osm_data\n", + "GROUP BY highway_tag_value\n", + "ORDER BY length_km_total DESC\n", + "\"\"\"\n", + "df = con.sql(query).df()\n", + "\n", + "display(df)" + ] + }, + { + "cell_type": "markdown", + "id": "177c63c8-62e3-48ff-806b-a4ec8a2a6495", + "metadata": {}, + "source": [ + "## Map of roads with missing road surface information" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f3d72fd5-db1a-4b8f-a33e-81cf84e016ca", + "metadata": {}, + "outputs": [], + "source": [ + "import geopandas as gpd\n", + "\n", + "\n", + "query = f\"\"\"\n", + "SELECT\n", + " highway_tag_value,\n", + " surface_tag_value,\n", + " CASE \n", + " WHEN surface_tag_value IS NULL THEN 'missing'\n", + " WHEN list_contains(['paved', 'asphalt', 'chipseal', 'concrete', 'concrete:lanes', 'concrete:plates', 'paving_stones', 'sett', 'unhewn_cobblestone', 'cobblestone', 'bricks', 'metal', 'wood'], surface_tag_value) THEN 'paved'\n", + " WHEN list_contains(['unpaved', 'compacted', 'fine_gravel', 'gravel', 'shells', 'rock', 'pebblestone', 'ground', 'dirt', 'earth', 'grass', 'grass_paver', 'metal_grid', 'mud', 'sand', 'woodchips', 'snow', 'ice', 'salt'], surface_tag_value) THEN 'unpaved'\n", + " ELSE 'other'\n", + " END as surface_type,\n", + " ST_AsText(clipped_geometry) as geometry\n", + "FROM osm_data\n", + "WHERE ST_GeometryType(clipped_geometry) = 'LINESTRING'\n", + "\"\"\"\n", + "df = con.sql(query).df()\n", + "\n", + "# convert the data to geodata\n", + "gdf = gpd.GeoDataFrame(\n", + " df,\n", + " geometry=gpd.GeoSeries.from_wkt(df['geometry'])\n", + ").set_crs('epsg:4326')" + ] + }, + { + "cell_type": "markdown", + "id": "c387402a-34a3-465b-966b-eaeba44b5560", + "metadata": {}, + "source": [ + "Define map parameters and style." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "a1fa5af5-c232-4178-ba62-5b98d318e917", + "metadata": {}, + "outputs": [], + "source": [ + "import lonboard\n", + "from palettable.matplotlib import Viridis_20\n", + "\n", + "\n", + "gdf[\"surface_type_codes\"] = gdf[\"surface_type\"].astype('category').cat.codes\n", + "\n", + "surface_type_colormap = {\n", + " \"missing\": [228,26,28, 255],\n", + " \"other\": [152,78,163, 255],\n", + " \"paved\": [55,126,184, 255],\n", + " \"unpaved\": [77,175,74, 255]\n", + "}\n", + "\n", + "\n", + "# the lonboard map definition\n", + "layer = lonboard.PathLayer.from_geopandas(\n", + " gdf,\n", + " get_color=lonboard.colormap.apply_categorical_cmap(gdf[\"surface_type\"], surface_type_colormap, alpha=1),\n", + " get_width=1.2,\n", + " width_units='pixels',\n", + " extensions=[lonboard.layer_extension.DataFilterExtension(filter_size=1)],\n", + " get_filter_value=gdf[\"surface_type_codes\"].astype('float'), # replace with desired column\n", + " filter_range=[0.0, 3.0], # replace with desired filter range\n", + ")\n", + "\n", + "\n", + "highway_map = lonboard.Map(\n", + " basemap_style=lonboard.basemap.CartoBasemap.Positron,\n", + " layers=[layer],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "3d98394c-e227-408f-98dc-47b36bc6e97d", + "metadata": {}, + "source": [ + "Set toggle buttons for different road surface types." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "c4cdda8f-681d-4cbc-b915-b643e52a9850", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import ipywidgets\n", + "from traitlets import directional_link\n", + "\n", + "group_options = [(key, i) for i, key in enumerate(surface_type_colormap.keys()) ]\n", + "\n", + "surface_toggle = ipywidgets.ToggleButtons(\n", + " options=group_options,\n", + " description='Surface Type:',\n", + " disabled=False,\n", + " button_style='', # 'success', 'info', 'warning', 'danger' or ''\n", + ")\n", + "\n", + "directional_link(\n", + " (surface_toggle, 'value'),\n", + " (layer, \"filter_range\"),\n", + " transform= lambda v: (v, v)\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "id": "91873c70-2ff9-48a7-b9f1-7bb52f911821", + "metadata": {}, + "source": [ + "Display the map. You can click on the buttons to change the surface type displayed." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "8892ef4a-e21f-4e5d-b065-ff5cdbe7a743", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "217caff9b18d4751bb58caf8fc9b20b8", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "Map(basemap_style=, la…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2b2bdbc137a3414ca7be45141369e425", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "ToggleButtons(description='Surface Type:', options=(('missing', 0), ('other', 1), ('paved', 2), ('unpaved', 3)…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(highway_map, surface_toggle)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14a91654-4dda-4028-adba-0def30527471", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/book/04_corporate_edits.ipynb b/book/04_corporate_edits.ipynb index 12d690a..539a301 100644 --- a/book/04_corporate_edits.ipynb +++ b/book/04_corporate_edits.ipynb @@ -501,17 +501,7 @@ " disabled=False\n", ")\n", "\n", - "corporate_slider = ipywidgets.SelectionSlider(\n", - " options=group_options,\n", - " index=len(group_options)-1,\n", - " description='Group:',\n", - " layout=ipywidgets.Layout(width='1000px'),\n", - " disabled=False,\n", - " min=0,\n", - " max=25,\n", - ")\n", - "\n", - "corporate_slider = ipywidgets.ToggleButtons(\n", + "corporate_toggle = ipywidgets.ToggleButtons(\n", " options=group_options,\n", " description='Group:',\n", " disabled=False,\n", @@ -520,7 +510,7 @@ "\n", "\n", "directional_link(\n", - " (corporate_slider, 'value'),\n", + " (corporate_toggle, 'value'),\n", " (corporate_slider_range, \"value\"),\n", " transform=lambda v: (v, v)\n", ")\n",