From ea48586ee341d412749c0284f87dead23b62240a Mon Sep 17 00:00:00 2001 From: Merle-dej Date: Tue, 4 Nov 2025 14:05:00 +0100 Subject: [PATCH 1/2] opschonen --- data_combining.ipynb | 257 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 233 insertions(+), 24 deletions(-) diff --git a/data_combining.ipynb b/data_combining.ipynb index 08719c5..03b7d2f 100644 --- a/data_combining.ipynb +++ b/data_combining.ipynb @@ -2,30 +2,90 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 60, "id": "133bc69e", "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "0 4\n", - "1 3\n", - "2 4\n", - "3 4\n", - "4 11\n", - " ..\n", - "2395 9\n", - "2396 1\n", - "2397 2\n", - "2398 1\n", - "2399 1\n", - "Name: CMSA-GAKH-01_180, Length: 2400, dtype: int64" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + " Objectummer Locatienaam Lat/Long \\\n", + "0 CMSA-GAKH-01 Kalverstraat t.h.v. 1 52.372634, 4.892071 \n", + "1 CMSA-GAWW-11 Korte Niezel 52.374616, 4.899830 \n", + "2 CMSA-GAWW-12 Oudekennissteeg 52.373860, 4.898690 \n", + "3 CMSA-GAWW-13 Stoofsteeg 52.372439, 4.897689 \n", + "4 CMSA-GAWW-14 Oudezijds Voorburgwal t.h.v. 91 52.373538, 4.898166 \n", + "5 CMSA-GAWW-15 Oudezijds Achterburgwal t.h.v. 86 52.372916, 4.898207 \n", + "6 CMSA-GAWW-16 Oudezijds Achterburgwal t.h.v. 91 52.372628, 4.898233 \n", + "7 CMSA-GAWW-17 Oudezijds Voorburgwal t.h.v. 206 52.372782, 4.896649 \n", + "8 CMSA-GAWW-19 Molensteeg 52.373587, 4.899815 \n", + "9 CMSA-GAWW-20 Oudebrugsteeg 52.375350, 4.897480 \n", + "10 CMSA-GAWW-21 Damstraat 52.371930, 4.895600 \n", + "11 CMSA-GAWW-23 Bloedstraat 52.372764, 4.899829 \n", + "12 GACM-04 Damrak 1-5 52.377027, 4.898059 \n", + "13 GVCV-01 CS - ri. NDSM 52.380686, 4.899041 \n", + "14 GVCV-03 CS - ri. Buiksloterweg (West) 52.380566,4.899360 \n", + "15 GVCV-04 CS - ri. Buiksloterweg (Oost) 52.380516,4.899524 \n", + "16 GVCV-05-A CS - ri. IJplein (West) 52.378526, 4.905811 \n", + "17 GVCV-05-B CS - ri. IJplein (Oost) 52.378526, 4.905811 \n", + "18 GVCV-06 Azartplein 52.378101,4.937281 \n", + "19 GVCV-07 Pontsteiger N (ri. NDSM) 52.392143,4.885861 \n", + "20 GVCV-08 Pontsteiger Z (ri. Distelweg) 52.392029,4.886005 \n", + "21 GVCV-09 Zeeburg 52.374414, 4.949579 \n", + "22 GVCV-11 NDSMkade 52.401060, 4.891368 \n", + "23 GVCV-13 Buiksloterweg 52.382352, 4.903016 \n", + "24 GVCV-14 Buiksloterweg 52.382169, 4.903385 \n", + "25 GASA-06 Azartplein B 52.378020, 4.937150 \n", + "26 GASA-01-A1 IJBoulevardA1 52.380450, 4.900310 \n", + "27 GASA-01-A2 IJBoulevardA2 52.380420, 4.900280 \n", + "28 GASA-01-B IJBoulevardB 52.380340, 4.899920 \n", + "29 GASA-01-C IJBoulevardC 52.380280, 4.899850 \n", + "30 GASA-02-01 BrugOostertoegang1 52.378640, 4.904720 \n", + "31 GASA-02-02 BrugOostertoegang2 52.378580, 4.904660 \n", + "32 GASA-03 Oosterdoksbrug 52.377620, 4.911010 \n", + "33 GASA-04 CSriOosterdokskade 52.377020, 4.903520 \n", + "34 GASA-05-O NoorderparkOost 52.388980, 4.919630 \n", + "35 GASA-05-W NoorderparkWest 52.389260, 4.918930 \n", + "\n", + " Breedte Effectieve breedte \n", + "0 8 6,7 \n", + "1 3,8 3,4 \n", + "2 3 2,6 \n", + "3 2,6 2,2 \n", + "4 4 3,6 \n", + "5 3,2 2,8 \n", + "6 3,1 2,7 \n", + "7 5,1 4,7 \n", + "8 2,9 2,5 \n", + "9 5,7 5,3 \n", + "10 9,7 9,3 \n", + "11 5,7 5,3 \n", + "12 7 6,2 \n", + "13 3,5 3,1 \n", + "14 3,5 3,1 \n", + "15 3,5 3,1 \n", + "16 3,5 3,1 \n", + "17 3,5 3,1 \n", + "18 3,5 3,1 \n", + "19 3,5 3,1 \n", + "20 3,5 3,1 \n", + "21 3,5 3,1 \n", + "22 3,5 3,1 \n", + "23 3,5 3,1 \n", + "24 3,5 3,1 \n", + "25 NaN 3,1 \n", + "26 NaN 4 \n", + "27 NaN 4 \n", + "28 NaN 6 \n", + "29 NaN 6 \n", + "30 NaN 6 \n", + "31 NaN 6 \n", + "32 NaN 8 \n", + "33 NaN 7 \n", + "34 NaN 4 \n", + "35 NaN 4 \n" + ] } ], "source": [ @@ -46,14 +106,163 @@ "time_cols = [c for c in time_cols if c in df_data.columns] # only keep existing ones\n", "df_combined = df_data[time_cols].copy()\n", "\n", + "#here we combine the data\n", "for base in base_names:\n", " related_cols = [c for c in df_data.columns if c.startswith(base + \"_\")]\n", " if len(related_cols) >1: \n", - " df_combined[base] = df_data[related_cols].sum(axis=1)\n", + " df_combined[base] = df_data[related_cols].sum(axis=1) \n", + "print(df_sensors)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "521729d1", + "metadata": {}, + "outputs": [], + "source": [ + "#effectieve breedte bruikbaar maken om mee te rekenen\n", + "df_sensors[\"Effectieve breedte\"] = (\n", + " df_sensors[\"Effectieve breedte\"]\n", + " .astype(str)\n", + " .str.replace(\",\", \".\")\n", + " .astype(float)\n", + ")\n", + "\n", + "#directional flow\n", + "directional_cols = [c for c in df_data.columns if \"_\" in c]\n", + "id_cols = [\"timestamp\", \"hour\", \"minute\", \"day\", \"month\", \"weekday\", \"is_weekend\"]\n", + "\n", + "df_long = df_data.melt(\n", + " id_vars=id_cols,\n", + " value_vars=directional_cols,\n", + " var_name=\"sensor_direction\",\n", + " value_name=\"count\"\n", + ")\n", + "\n", + "df_long[\"Objectummer\"] = df_long[\"sensor_direction\"].str.rsplit(\"_\", n=1).str[0]\n", + "\n", + "\n", + "df_long = df_long.merge(\n", + " df_sensors[[\"Objectummer\", \"Effectieve breedte\"]],\n", + " on=\"Objectummer\",\n", + " how=\"left\"\n", + ")\n", + "\n", + "df_long[\"flow\"] = df_long[\"count\"] / (3 * df_long[\"Effectieve breedte\"]) # flow = count /( 3 (min) * effective witdh field)\n", + "\n", + "df_long_sorted = df_long.sort_values(by=\"flow\", ascending=False)\n", + "\n", + "\n", + "#combined flow\n", + "df_combined_long = (\n", + " df_long\n", + " .groupby([\"timestamp\", \"Objectummer\"], as_index=False)\n", + " .agg({\"flow\": \"sum\"})\n", + ")\n", + "\n", + "df_combined_long_sorted = df_combined_long.sort_values(by=\"flow\", ascending=False)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "3491b2ec", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "import pandas as pd\n", + "import plotly.express as px\n", + "\n", + "\n", + "# Ensure timestamp is datetime\n", + "df_long_sorted[\"timestamp\"] = pd.to_datetime(df_long_sorted[\"timestamp\"])\n", + "sensors = df_sensors[\"Objectummer\"]\n", + "for sensor in sensors:\n", + " df_plot = df_long_sorted[df_long_sorted[\"Objectummer\"] == sensor]\n", + "\n", + " plt.figure(figsize=(12,5))\n", + " sns.lineplot(\n", + " data=df_plot,\n", + " x=\"timestamp\",\n", + " y=\"flow\",\n", + " hue=\"sensor_direction\", # each direction gets its own line\n", + " marker=\"o\"\n", + " )\n", + " plt.title(f\"Directional flow over time for {sensor}\")\n", + " plt.xlabel(\"Time\")\n", + " plt.ylabel(\"Flow (person/min/meter)\")\n", + " plt.xticks(rotation=45)\n", + " plt.grid(True)\n", + " plt.tight_layout()\n", + " # plt.show()\n", + " # os.makedirs(\"plots\", exist_ok=True)\n", + " plt.savefig(f\"plots/{sensor}.png\")\n", + " plt.close() \n" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "6be7ce3c", + "metadata": {}, + "outputs": [], + "source": [ + "import base64\n", + "import folium\n", + "from folium import IFrame\n", + "\n", + "\n", + "# Center map roughly at sensor locations\n", + "df_sensors[['latitude', 'longitude']] = df_sensors['Lat/Long'].str.split(',', expand=True)\n", + "df_sensors['latitude'] = pd.to_numeric(df_sensors['latitude'])\n", + "df_sensors['longitude'] = pd.to_numeric(df_sensors['longitude'])\n", + "\n", + "\n", + "map_center = [df_sensors['latitude'].mean(), df_sensors['longitude'].mean()]\n", + "m = folium.Map(location=map_center, zoom_start=15)\n", + "\n", + "# Add zones as colored polygons\n", + "# print(gdf_zones)\n", + "gdf_zones = gdf_zones.to_crs(epsg=4326) # ensure lat/lon\n", + "# print(gdf_zones)\n", + "folium.GeoJson(\n", + " gdf_zones,\n", + " style_function=lambda feature: {\n", + " 'fillColor': feature['properties']['color'],\n", + " 'color': 'black',\n", + " 'weight': 1,\n", + " 'fillOpacity': 0.5\n", + " }\n", + ").add_to(m)\n", "\n", - "df_combined[\"CMSA-GAKH-01\"]\n", + "# Add sensor markers with popups showing the plots\n", + "for idx, row in df_sensors.iterrows():\n", + " sensor = row['Objectummer']\n", + " plot_path = f\"plots/{sensor}.png\"\n", + " \n", + " if os.path.exists(plot_path):\n", + " encoded = base64.b64encode(open(plot_path, 'rb').read()).decode()\n", + " html = f''\n", + " iframe = IFrame(html, width=800, height=330)\n", + " popup = folium.Popup(iframe, max_width=1000)\n", + " else:\n", + " popup = None\n", + " \n", + " folium.CircleMarker(\n", + " location=[row['latitude'], row['longitude']],\n", + " radius=7,\n", + " color='red',\n", + " fill=True,\n", + " fill_color='red',\n", + " popup=popup\n", + " ).add_to(m)\n", "\n", - "\n" + "# Save map to HTML\n", + "# m\n", + "m.save(\"interactive_sensor_map.html\")" ] } ], @@ -73,7 +282,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.13.9" } }, "nbformat": 4, From 80dac5eac8b55e61470066a148811fa70ae74002 Mon Sep 17 00:00:00 2001 From: Merle-dej Date: Thu, 6 Nov 2025 15:04:06 +0100 Subject: [PATCH 2/2] Area overview stuk geschreven --- project.ipynb | 265 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 235 insertions(+), 30 deletions(-) diff --git a/project.ipynb b/project.ipynb index 2de3fa1..1bf1264 100644 --- a/project.ipynb +++ b/project.ipynb @@ -6,12 +6,12 @@ "source": [ "# Project Group - 25\n", "\n", - "Members: \n", + "**Members:**\n", "- Thijmen van der Geer\n", "- Merle de Jong\n", "- Julie van der Linde\n", "- Max Beishuizen\n", - "- Wouter Gerritsen" + "- Wouter Gerritsen\n" ] }, { @@ -28,10 +28,9 @@ "source": [ "# Research Objective\n", "\n", - "- Develop an interactive dashboard for crowd monitoring at SAIL 2025 \n", - "- Consolidate multiple real-time data streams (crowd density, mobility, traffic, etc.) into a single platform. \n", - "- Improve situational awareness and decision-making efficiency for crowd managers. \n", - "- Provide visual alerts and predictive insights" + "The objective of this research is to design and develop an interactive dashboard for crowd monitoring during SAIL 2025. The dashboard will integrate multiple real-time data streams, including crowd density, mobility patterns, and traffic conditions, into one clear and accessible platform. By presenting these data sources in a unified interface, the system aims to improve situational awareness for crowd managers and support faster, more informed decision-making in the field.\n", + "\n", + "In addition to monitoring live conditions, the dashboard will also provide visual alerts and predictive insights. These features are intended to help identify potential congestion or safety risks before they escalate, allowing organizers to take proactive measures. Overall, the dashboard should contribute to safer visitor flows, more efficient coordination among stakeholders, and a smoother operational experience throughout the event." ] }, { @@ -46,7 +45,7 @@ "\n", "1. What are the most important crowd-related indicators for crowd managers during SAIL 2025? \n", "2. How can different data sources (historical, event specific, mobility, geospatial) be integrated to unify to one single monitoring dashboard? \n", - "3. How can real-time (and historical?) data be leveraged to anticipate crowding issues before they can escalate by forecasting potential bottlenecks? \n", + "3. How can real-time and historical data be leveraged to anticipate crowding issues before they can escalate by forecasting potential bottlenecks? \n", "4. In what way can the UI and UX of the monitoring dashboard be designed to maximize clear and usable real-time decision-making?" ] }, @@ -54,57 +53,260 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Contribution Statement\n", + "# Geographical and Temporal Scale\n", "\n", - "*Be specific. Some of the tasks can be coding (expect everyone to do this), background research, conceptualisation, visualisation, data analysis, data modelling*" + "The data that will be used was collected during the SAIL event in August of 2025\\. The event had a duration of 5 days, from August 20th to August 24th. The temporal scale therefore encompasses these five days. The geographical scale of this project includes the event area, which is the IJHaven in Amsterdam and the surrounding neighbourhoods of the IJ river (city center connections). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Author 1**:\n", + "# Data Used\n", "\n", - "**Author 2**:\n", + "The following data has been used for this project:\n", "\n", - "**Author 3**:" + "- Pedestrian Sensor Data\n", + "- Pedestrian Sensor Location Data\n", + "- Vessel Position Data\n", + "- TomTom Carflow Data\n", + "\n", + "This data has been given together with the assignment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Data Used" + "# Data Pipeline\n", + "\n", + "The Data Pipelines of the 4 datasets are described in the following way:\n", + "1. Description of the given dataset\n", + "2. Processing/filtering of the data into useful information\n", + "3. How the information will be used for the dashboard" ] }, { "cell_type": "markdown", "metadata": {}, - "source": [] + "source": [ + "**Dataset 1: Pedestrian Sensor Data**\n", + "\n", + "1.\tDataset Description
\n", + "The pedestrian sensors placed around the SAIL 2025 event area record how many people pass by within each three-minute interval. Each sensor measures movement in two opposite directions (180 degrees from each other). This means the dataset includes two separate count values per sensor: one for individuals moving in direction A and one for individuals moving in direction B. Over time, these values create a continuous flow-based representation of pedestrian movements across the monitored zone.\n", + "\n", + "2.\tProcessing and Filtering into Useful Information
\n", + "To make this data meaningful, it is processed in two ways:\n", + "- Directional Flow Analysis: \n", + " By keeping the two count directions separate, we can determine the dominant walking direction, identify flow patterns (e.g., crowd moving toward an event stage or exiting the area), and detect potential one-way or congested movement.\n", + "- Total Crowd Estimate per Sensor Location:\n", + " The two directional counts can also be combined to estimate the total number of people passing through that location in a given time window. This helps determine general crowd intensity in specific zones.\n", + "\n", + "\n", + "\n", + "3.\tUse in the Dashboard
\n", + "In the dashboard, this processed data will be visualized to provide both high-level and detailed crowd insights. \n", + "- Heatmaps and density indicators will show which areas are experiencing high pedestrian volume.\n", + "- Directional flow will help crowd managers understand where people are moving and whether circulation is happening as intended.\n", + "- Automated alerts can be triggered when thresholds are exceeded, signaling that crowd pressure is increasing in a particular zone.\n", + "\n", + "This allows decision-makers to respond proactively, redirect flows, adjust entry/exit routes, deploy additional staff, or coordinate with traffic and mobility teams to maintain a safe and smooth movement of visitors during the event.\n" + ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Intended Data Pipeline" + "**Dataset 2: Pedestrian Sensor Location Data**\n", + "\n", + "1. The dataset contained the locations of the pedestrian sensors, their name and their ID to connect to the sensor data.\n", + "2. This data did not need filtering\n", + "3. This data was used to make a map together with the SAIL 2025 \"zones\" to show to the importance of the different sensors to the measuring of the crowd sizes of SAIL" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "- Maps of the IJHaven, road traffic, walking routes, entry/exit points, visitor counts \n", - "- Crowd density mapping, heatmaps, flow trajectories \n", - "- Alerts for crowd density threshold exceedances" + "**Dataset 3: Vessel Position Data**\n", + "\n", + "1. The given dataset contained data on all ships in the nearby area. Positions, speed, function (in the port, for example: tugboat), length and some other data.\n", + "2. The dataset has been filtered so only the important data for this project was included, only big ships that don't have a role inside of the port with a length of more than 45 meters have been included.\n", + "3. This data is used to make a map of the positions of these vessels in the last 3 minutes. With the size of the marker indicating the size of the ship. This will show the ships that visitors most likely want to see" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Geographical and Temporal Scale\n", + "**Dataset 4: TomTom Carflow Data**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Goal of the dashboard\n", "\n", - "The data that will be used was collected during the SAIL event in August of 2025\\. The event had a duration of 5 days, from August 20th to August 24th. The temporal scale therefore encompasses these five days. The geographical scale of this project includes the event area, which is the IJHaven in Amsterdam and the surrounding neighbourhoods of the IJ river (city center connections). " + "Provide a dashboard for crowd managers to real-time awarenes:\n", + "- Identify overcrowded areas or potential bottlenecks\n", + "- Monitor flows at entrances, bridges, and ferry points\n", + "- Quickly prioritize operational decisions\n", + "- Consolidate multiple data sources into one intuitive interface" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Functionalities of the dashboard\n", + "\n", + "The dashboard can be divided into to 5 segments. A live map per minute, a zone status panel, an alert panel and an overview panel that works as a Dashboard header. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Part 1: Area Overview**\n", + "\n", + "The initial design objective for the area overview was to create a clear and interactive visual representation of pedestrian activity across the monitored area. This overview combines spatial and temporal data to show how pedestrian density changes over time at different sensor locations. Directional pedestrian counts were normalized by effective walkway width to calculate flow in persons per minute per meter, providing a standardized measure of crowd intensity. Each sensor’s time series was plotted individually, and these plots were integrated into an interactive map that displays sensor locations along with surrounding zones. By selecting a sensor on the map, users can view how pedestrian flow varied during the analyzed period. Unlike the real-time heat map, which visualizes live movement patterns, this overview presents historical and periodic trends—offering insights into when and where pedestrian activity tends to be highest. This enables better understanding of temporal crowd dynamics and supports planning or design decisions based on recurring movement patterns." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Part 2: Vessel Overview**\n", + "\n", + "Nog doen!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Part 3: Pedestrian Overview**\n", + "\n", + "**Objective:** The initial design objective for the dashboard was to provide a high-level, visual overview of pedestrian density across the entire SAIL event area. The proposed solution was a dynamic density heatmap plotted on a map of Amsterdam.\n", + "\n", + "**Methodology:** This system was designed to process real-time crowd counts from all sensors simultaneously.\n", + "\n", + "**Data Normalization:** To compare a narrow alley with a wide-open square, raw counts (e.g., 50 people) are meaningless. This system would first normalize all sensor counts, typically to a scale of 0.0 to 1.0, where 1.0 represents the highest count recorded anywhere in the dataset.\n", + "\n", + "**Heatmap Generation:** Using this normalized 0.0-1.0 value as the \"heat\" (z-index), the map would generate a density_map overlay. This visualization renders \"hot\" areas (in yellow or white) where normalized counts are high, and \"cold\" areas (in blue or purple) where counts are low.\n", + "\n", + "**Animation:** This visualization would be tied to the time slider, allowing an operator to play back the event and watch the \"blob\" of crowd density move, expand, and contract.\n", + "\n", + "**Identified Limitations:** While visually impressive, this heatmap approach was determined to be insufficient for active crowd management due to several critical limitations:\n", + "\n", + "**The Normalization Problem:** The system's \"heat\" is relative to the single busiest moment of the entire event (e.g., the Sail-In Parade). A count of 10,000 at this peak would make a later, problematic count of 500 (which is 5% of the max) appear \"cold\" (blue), even though 500 people in a narrow street is a critical issue.\n", + "\n", + "\n", + "This analysis led to the conclusion that a visualization of density was less valuable than a visualization of problems, so the heatmap needed a addition to give a broader view of the problems. This insight drove the evolution toward the Dynamic Alert Map." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Part 4: Car Traffic Overview**\n", + "\n", + "Nog doen!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Part 5: Alerts & Rerouting Advice**\n", + "\n", + "**Objective:** This is the multi-active system. Its design objective is to eliminate operator guesswork and provide immediate, scannable, and actionable intelligence. It provides a good addition to the heatmap with a clean \"mission control\" map that only displays problems and solutions.\n", + "\n", + "**Methodology**: This system operates on a three-level statistical model that is calculated per sensor. Instead of comparing a sensor to the entire dataset, it compares a sensor's current count to its own history.\n", + "\n", + "**Multi-Level Thresholds:** Before the dashboard even loads, the system analyzes the data for each sensor and determines its unique thresholds for:\n", + "\n", + "- \"Quiet\" (below 50th percentile): The sensor is below its normal median traffic. It is considered \"green\" and available to receive rerouted crowds.\n", + "\n", + "- \"High Traffic\" (above 90th percentile): The sensor is in the top 10% of its normal traffic. This is unusual and requires monitoring.\n", + "\n", + "- \"Capacity Exceeded\" (above 98th percentile): The sensor is at a critical level (top 2%). This is a red-level emergency that requires immediate action.\n", + "\n", + "**Real-time Alert Generation:** When the operator moves the time slider, the system instantly evaluates all sensors against their unique thresholds and generates two types of alerts:\n", + "\n", + "- Yellow Alert (High Traffic): A yellow circle appears on the map, and a yellow card appears in the alert list with the advice \"High traffic. Monitor situation.\"\n", + "\n", + "- Red Alert (Capacity Exceeded): A red circle appears on the map, and a red card appears in the alert list. This also triggers the dynamic rerouting logic.\n", + "\n", + "**Dynamic Rerouting Logic (The \"Solution\"):** When a Red Alert is triggered, the system does not use a fixed, static plan. It finds the best dynamic solution.\n", + "\n", + "**Neighbor Map:** A core component of this system is the NEIGHBOR_MAP, a pre-defined list that tells the system the logical, walkable alternatives for every key sensor. For example, the NEIGHBOR_MAP defines the \"neighbor\" of \"Pontsteiger N\" as \"Pontsteiger Z\".\n", + "\n", + "The Search: When \"Pontsteiger N\" turns Red, the system looks up its neighbors and checks their current status.\n", + "\n", + "**The Advice:**\n", + "\n", + "Solution Found: If it finds a neighbor (like \"Pontsteiger Z\") that is currently in a \"Quiet\" state, it generates the advice: \"ADVICE: Reroute people to Pontsteiger Z.\" Simultaneously, a Green Circle automatically appears on the map at the precise location of \"Pontsteiger Z\".\n", + "\n", + "No Solution Found: If all the neighbors (e.g., Pontsteiger Z) are also \"Busy\" or \"At Capacity,\" the system generates the most critical alert of all: \"ADVICE: All nearby routes are also busy. Monitor situation!\"\n", + "\n", + "This entire process evaluating alerts, finding neighbors, checking their status, and plotting all red, yellow, and green pins happens instantly every time the time slider is moved, providing a live, synchronized, and actionable view of the event." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Integrated Dashboard**\n", + "\n", + "Nog een deel schrijven over hoe het total dashboard werkt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Conclusion\n", + "This project successfully demonstrates the design and implementation of a real-time crowd monitoring dashboard that directly improves situational awareness and provides critical decision support for crowd managers at SAIL. The solution answers the main research question by transforming raw, multi-source data into a single operational view.\n", + "\n", + "The dashboard's success hinges on its answers to the project's sub-questions. It was determined that the most critical indicators are not fixed counts, but dynamic statistical thresholds (e.g., the 90th and 98th percentile) unique to each sensor. This approach effectively identifies abnormal crowding relative to a location's specific context. This logic is powered by the integration of geospatial data (sensor locations and names) with real-time mobility data (aggregated crowd counts).\n", + "\n", + "**Nog een deel over de vessels en carflows**\n", + "\n", + "In summary, the dashboard provides a robust \"single source of truth.\" While it effectively integrates various overviews (pedestrian, ship, car traffic) to create a comprehensive operational picture, its true power lies in its automated overview support. By not only flagging a exceded capacity or , but also giving optional solutions for pedestrianflows and foreshadowing urgent situations using vessel and car movements. The dashboard delivers on its primary goal: it transforms complex data into immediate, actionable intelligence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Discussion & Recommendations\n", + "\n", + "Nog doen!\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Contribution Statement\n", + "\n", + "Everything was done in group form for the proposal:\n", + "\n", + "- **Thijmen van der Geer**: background research, writing, pedestrian overview, alerts & rerouting advice\n", + "\n", + "- **Merle de Jong**: background research, writing\n", + "\n", + "- **Julie van der Linde**: background research, writing\n", + "\n", + "- **Max Beishuizen**: background research, writing\n", + "\n", + "- **Wouter Gerritsen**: background research, writing\n" ] }, { @@ -112,28 +314,31 @@ "metadata": {}, "source": [ "# References\n", - "https://www.amsterdam.nl/nieuws/nieuwsoverzicht/sail-2025-50-jaar-magie-water/ \n", "\n", - "https://www.avineon-tensing.com/nl-nl/ons-werk/case-study-digital-twin-crowdmanagement-sail \n" + "- https://www.amsterdam.nl/nieuws/nieuwsoverzicht/sail-2025-50-jaar-magie-water/ \n", + "\n", + "- https://www.avineon-tensing.com/nl-nl/ons-werk/case-study-digital-twin-crowdmanagement-sail \n", + "\n", + "- **Real-Time Crowd Monitoring and Management (IRJAEH, 2025) (irjaeh.com)**:\n", + "Applies computer vision (background subtraction, contour detection, optical flow) to detect people and map crowd density; visualizes with heatmaps; dashboard interface developed with Flask.\n", + "- **Crowd Management Dashboard – Argaleo (’s-Hertogenbosch) (argaleo.com)**:\n", + "Live insights into pedestrian flows in city centers; historical and real-time analysis; integration of visitor flows with city facilities; shows direction and timing of flows.\n", + "\n", + "- \"Digital Twin voor preventief crowdmanagement\" Bignieuws. 21/09/2025 https://www.bignieuws.nl/digital-twin-voor-preventief-crowdmanagement/\n" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "base", "language": "python", "name": "python3" }, "language_info": { "name": "python", - "version": "3.8.2" + "version": "3.13.9" }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" - } - } + "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2