Spaces:
Sleeping
Sleeping
Gordon Li
commited on
Commit
·
ae72b3f
1
Parent(s):
ecf5538
comment reformat
Browse files- app.py +47 -56
- cronjob/abstract_traffic_image_analyzer.py +64 -90
- cronjob/application_traffic_image_analyzer.py +25 -36
- cronjob/train_detr_traffic_image_analyzer.py +27 -38
- visualiser/hkust_bnb_visualiser.py +74 -98
- visualiser/td_traffic_spot_visualiser.py +56 -86
app.py
CHANGED
@@ -1,26 +1,24 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
-
|
11 |
-
|
12 |
-
-
|
13 |
-
|
14 |
-
-
|
15 |
-
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
Date: March 2025
|
23 |
-
"""
|
24 |
|
25 |
import os
|
26 |
import re
|
@@ -46,24 +44,24 @@ from constant.hkust_bnb_constant import (
|
|
46 |
)
|
47 |
|
48 |
|
49 |
-
|
50 |
-
Loads CSS styles from a file and applies them to the Streamlit application.
|
51 |
-
Parameters:
|
52 |
-
css_file: Path to the CSS file to be loaded
|
53 |
-
|
|
|
54 |
def load_css(css_file):
|
55 |
with open(css_file) as f:
|
56 |
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
|
57 |
|
58 |
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
"""
|
67 |
def highlight_search_terms(text, search_query):
|
68 |
if not search_query:
|
69 |
return text
|
@@ -80,17 +78,15 @@ def highlight_search_terms(text, search_query):
|
|
80 |
return highlighted_text
|
81 |
|
82 |
|
83 |
-
|
84 |
-
|
85 |
-
"""
|
86 |
def render_lottie_loading_animation():
|
87 |
components.html(LOTTIE_HTML, height=750)
|
88 |
|
89 |
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
"""
|
94 |
def render_review_dialog():
|
95 |
with st.container():
|
96 |
col_title = st.columns([5, 1])
|
@@ -122,10 +118,9 @@ def render_review_dialog():
|
|
122 |
st.info("No reviews available for this listing.")
|
123 |
|
124 |
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
"""
|
129 |
def initialize_session_state():
|
130 |
default_states = {
|
131 |
'center_lat': None,
|
@@ -156,11 +151,9 @@ def initialize_session_state():
|
|
156 |
st.session_state.loading_complete = True
|
157 |
|
158 |
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
pagination, and user interactions with the application elements.
|
163 |
-
"""
|
164 |
def main():
|
165 |
st.set_page_config(
|
166 |
layout="wide",
|
@@ -405,10 +398,8 @@ def main():
|
|
405 |
render_review_dialog()
|
406 |
|
407 |
|
408 |
-
|
409 |
-
|
410 |
-
then calls the main function to start the application.
|
411 |
-
"""
|
412 |
if __name__ == "__main__":
|
413 |
token = os.environ.get("HF_TOKEN")
|
414 |
if token:
|
|
|
1 |
+
# app.py
|
2 |
+
|
3 |
+
# This application provides a user interface for HKUST students to browse, search,
|
4 |
+
# and find accommodations in different neighborhoods of Hong Kong. It features an interactive map
|
5 |
+
# visualization, listing cards with pricing information, traffic-based discounts, and smart search
|
6 |
+
# functionality to match user preferences with available properties.
|
7 |
+
|
8 |
+
# Key features:
|
9 |
+
# - Interactive map displaying BNB listings with location markers
|
10 |
+
# - Neighborhood-based filtering of available accommodations
|
11 |
+
# - Smart search system that highlights matching terms in descriptions and reviews
|
12 |
+
# - Traffic-based discount system promoting eco-friendly housing options
|
13 |
+
# - Detailed view of property reviews with highlighted search terms
|
14 |
+
# - Responsive pagination for browsing through large sets of listings
|
15 |
+
# - Loading animations and informative UI elements for better user experience
|
16 |
+
|
17 |
+
# The application uses Folium for map visualization, Streamlit for the web interface
|
18 |
+
|
19 |
+
# Author: Gordon Li (20317033)
|
20 |
+
# Company : HKUST Sustainability
|
21 |
+
# Date: March 2025
|
|
|
|
|
22 |
|
23 |
import os
|
24 |
import re
|
|
|
44 |
)
|
45 |
|
46 |
|
47 |
+
|
48 |
+
# Loads CSS styles from a file and applies them to the Streamlit application.
|
49 |
+
# Parameters:
|
50 |
+
# css_file: Path to the CSS file to be loaded
|
51 |
+
|
52 |
+
|
53 |
def load_css(css_file):
|
54 |
with open(css_file) as f:
|
55 |
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
|
56 |
|
57 |
|
58 |
+
# Highlights search terms within text by wrapping them in a span with highlight class.
|
59 |
+
# Parameters:
|
60 |
+
# text: The original text to process
|
61 |
+
# search_query: The search terms to highlight within the text
|
62 |
+
# Returns:
|
63 |
+
# Text with highlighted search terms
|
64 |
+
|
|
|
65 |
def highlight_search_terms(text, search_query):
|
66 |
if not search_query:
|
67 |
return text
|
|
|
78 |
return highlighted_text
|
79 |
|
80 |
|
81 |
+
# Renders a loading animation using Lottie animation in HTML format.
|
82 |
+
|
|
|
83 |
def render_lottie_loading_animation():
|
84 |
components.html(LOTTIE_HTML, height=750)
|
85 |
|
86 |
|
87 |
+
# Renders a dialog containing reviews for the currently selected listing.
|
88 |
+
# Displays reviewer name, review date, and comments with search terms highlighted.
|
89 |
+
|
|
|
90 |
def render_review_dialog():
|
91 |
with st.container():
|
92 |
col_title = st.columns([5, 1])
|
|
|
118 |
st.info("No reviews available for this listing.")
|
119 |
|
120 |
|
121 |
+
# Initializes the session state with default values for various application parameters.
|
122 |
+
# Sets up the visualizer and loads required resources for the application.
|
123 |
+
|
|
|
124 |
def initialize_session_state():
|
125 |
default_states = {
|
126 |
'center_lat': None,
|
|
|
151 |
st.session_state.loading_complete = True
|
152 |
|
153 |
|
154 |
+
# Main function that sets up the Streamlit application interface.
|
155 |
+
# Handles page configuration, sidebar setup, map rendering, listing display,
|
156 |
+
# pagination, and user interactions with the application elements.
|
|
|
|
|
157 |
def main():
|
158 |
st.set_page_config(
|
159 |
layout="wide",
|
|
|
398 |
render_review_dialog()
|
399 |
|
400 |
|
401 |
+
# Main entry point for the application. Authenticates with Hugging Face if a token is available,
|
402 |
+
# then calls the main function to start the application.
|
|
|
|
|
403 |
if __name__ == "__main__":
|
404 |
token = os.environ.get("HF_TOKEN")
|
405 |
if token:
|
cronjob/abstract_traffic_image_analyzer.py
CHANGED
@@ -1,16 +1,14 @@
|
|
1 |
-
|
2 |
-
Traffic Image Analysis Module for HKUST BNB+ Platform
|
3 |
|
4 |
-
This module provides functionality for analyzing traffic camera images to detect and count vehicles.
|
5 |
-
It downloads images from traffic cameras, processes them using computer vision models, and records
|
6 |
-
traffic data that is used for traffic-based discount calculations in the BNB+ platform.
|
7 |
|
8 |
-
The analyzer connects to a database to retrieve camera locations, downloads and processes images,
|
9 |
-
detects vehicles, and stores the results for visualization and analysis.
|
10 |
|
11 |
-
Author: Gordon Li (20317033)
|
12 |
-
Date: March 2025
|
13 |
-
"""
|
14 |
|
15 |
|
16 |
import requests
|
@@ -27,16 +25,14 @@ import random
|
|
27 |
|
28 |
|
29 |
class AbstractTrafficImageAnalyzer:
|
30 |
-
|
31 |
-
Initializes the traffic image analyzer with database connection, signal handlers, and directories.
|
32 |
|
33 |
-
Sets up:
|
34 |
-
- Database connection parameters
|
35 |
-
- Signal handlers for graceful shutdown
|
36 |
-
- Vehicle class identifiers for detection
|
37 |
-
- Directory structure for storing downloaded images
|
38 |
-
- Logging configuration
|
39 |
-
"""
|
40 |
|
41 |
def __init__(self):
|
42 |
self.connection_params = {
|
@@ -60,22 +56,18 @@ class AbstractTrafficImageAnalyzer:
|
|
60 |
|
61 |
self.setup_logging()
|
62 |
|
63 |
-
|
64 |
-
Handles termination signals to ensure graceful shutdown.
|
65 |
|
66 |
-
Parameters:
|
67 |
-
|
68 |
-
|
69 |
-
"""
|
70 |
|
71 |
def signal_handler(self, signum, frame):
|
72 |
print("\nShutdown signal received. Completing current task...")
|
73 |
self.running = False
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
Creates log files with timestamps and configures console output.
|
78 |
-
"""
|
79 |
|
80 |
def setup_logging(self):
|
81 |
logging.basicConfig(
|
@@ -87,12 +79,10 @@ class AbstractTrafficImageAnalyzer:
|
|
87 |
]
|
88 |
)
|
89 |
|
90 |
-
|
91 |
-
Retrieves traffic camera locations and URLs from the database.
|
92 |
|
93 |
-
Returns:
|
94 |
-
|
95 |
-
"""
|
96 |
|
97 |
def get_camera_locations(self):
|
98 |
try:
|
@@ -104,15 +94,13 @@ class AbstractTrafficImageAnalyzer:
|
|
104 |
logging.error(f"Error fetching camera locations: {str(e)}")
|
105 |
raise
|
106 |
|
107 |
-
|
108 |
-
Downloads an image from a given URL.
|
109 |
|
110 |
-
Parameters:
|
111 |
-
|
112 |
|
113 |
-
Returns:
|
114 |
-
|
115 |
-
"""
|
116 |
|
117 |
def download_image(self, url):
|
118 |
try:
|
@@ -123,16 +111,14 @@ class AbstractTrafficImageAnalyzer:
|
|
123 |
logging.error(f"Error downloading image from {url}: {str(e)}")
|
124 |
raise
|
125 |
|
126 |
-
|
127 |
-
Detects vehicles in an image using a computer vision model.
|
128 |
|
129 |
-
Parameters:
|
130 |
-
|
131 |
-
|
132 |
|
133 |
-
Returns:
|
134 |
-
|
135 |
-
"""
|
136 |
|
137 |
def detect_vehicles(self, image, confidence_threshold=0.7):
|
138 |
try:
|
@@ -178,16 +164,14 @@ class AbstractTrafficImageAnalyzer:
|
|
178 |
logging.error(f"Error detecting vehicles: {str(e)}")
|
179 |
raise
|
180 |
|
181 |
-
|
182 |
-
Draws vehicle detection bounding boxes and labels on the image.
|
183 |
|
184 |
-
Parameters:
|
185 |
-
|
186 |
-
|
187 |
|
188 |
-
Returns:
|
189 |
-
|
190 |
-
"""
|
191 |
|
192 |
def draw_detections(self, image, detections):
|
193 |
try:
|
@@ -241,16 +225,14 @@ class AbstractTrafficImageAnalyzer:
|
|
241 |
logging.error(f"Error drawing detections: {str(e)}")
|
242 |
raise
|
243 |
|
244 |
-
|
245 |
-
Processes all traffic cameras, detects vehicles, and prepares data for storage.
|
246 |
|
247 |
-
This method:
|
248 |
-
1. Gets all camera locations
|
249 |
-
2. Downloads images from each camera
|
250 |
-
3. Detects vehicles in each image
|
251 |
-
4. Processes images to visualize detections
|
252 |
-
5. Prepares data for storage
|
253 |
-
"""
|
254 |
|
255 |
def process_traffic_cameras(self):
|
256 |
try:
|
@@ -338,45 +320,37 @@ class AbstractTrafficImageAnalyzer:
|
|
338 |
logging.error(f"Error in process_traffic_cameras: {str(e)}")
|
339 |
raise
|
340 |
|
341 |
-
|
342 |
-
|
343 |
-
This method must be implemented by subclasses.
|
344 |
|
345 |
-
Parameters:
|
346 |
-
|
347 |
-
|
348 |
-
"""
|
349 |
|
350 |
def update_huggingface_dataset(self, batch_data, timestamp_str):
|
351 |
raise NotImplementedError("Subclasses must implement update_huggingface_dataset method")
|
352 |
|
353 |
-
|
354 |
-
|
355 |
-
This method must be implemented by subclasses.
|
356 |
|
357 |
-
Parameters:
|
358 |
-
|
359 |
-
|
360 |
-
"""
|
361 |
|
362 |
def create_coco_annotation_files(self, dataset_dict, timestamp_str):
|
363 |
raise NotImplementedError("Subclasses must implement create_coco_annotation_files method")
|
364 |
|
365 |
-
|
366 |
-
|
367 |
-
This method must be implemented by subclasses.
|
368 |
|
369 |
-
Parameters:
|
370 |
-
|
371 |
-
|
372 |
-
"""
|
373 |
|
374 |
def update_readme(self, dataset_dict, timestamp_str):
|
375 |
raise NotImplementedError("Subclasses must implement update_readme method")
|
376 |
|
377 |
-
|
378 |
-
Runs the traffic image analyzer, processing all cameras and updating the dataset.
|
379 |
-
"""
|
380 |
|
381 |
def run(self):
|
382 |
try:
|
|
|
1 |
+
# Traffic Image Analysis Module for HKUST BNB+ Platform
|
|
|
2 |
|
3 |
+
# This module provides functionality for analyzing traffic camera images to detect and count vehicles.
|
4 |
+
# It downloads images from traffic cameras, processes them using computer vision models, and records
|
5 |
+
# traffic data that is used for traffic-based discount calculations in the BNB+ platform.
|
6 |
|
7 |
+
# The analyzer connects to a database to retrieve camera locations, downloads and processes images,
|
8 |
+
# detects vehicles, and stores the results for visualization and analysis.
|
9 |
|
10 |
+
# Author: Gordon Li (20317033)
|
11 |
+
# Date: March 2025
|
|
|
12 |
|
13 |
|
14 |
import requests
|
|
|
25 |
|
26 |
|
27 |
class AbstractTrafficImageAnalyzer:
|
28 |
+
# Initializes the traffic image analyzer with database connection, signal handlers, and directories.
|
|
|
29 |
|
30 |
+
# Sets up:
|
31 |
+
# - Database connection parameters
|
32 |
+
# - Signal handlers for graceful shutdown
|
33 |
+
# - Vehicle class identifiers for detection
|
34 |
+
# - Directory structure for storing downloaded images
|
35 |
+
# - Logging configuration
|
|
|
36 |
|
37 |
def __init__(self):
|
38 |
self.connection_params = {
|
|
|
56 |
|
57 |
self.setup_logging()
|
58 |
|
59 |
+
# Handles termination signals to ensure graceful shutdown.
|
|
|
60 |
|
61 |
+
# Parameters:
|
62 |
+
# signum: Signal number
|
63 |
+
# frame: Current stack frame
|
|
|
64 |
|
65 |
def signal_handler(self, signum, frame):
|
66 |
print("\nShutdown signal received. Completing current task...")
|
67 |
self.running = False
|
68 |
|
69 |
+
# Sets up logging configuration for the analyzer.
|
70 |
+
# Creates log files with timestamps and configures console output.
|
|
|
|
|
71 |
|
72 |
def setup_logging(self):
|
73 |
logging.basicConfig(
|
|
|
79 |
]
|
80 |
)
|
81 |
|
82 |
+
# Retrieves traffic camera locations and URLs from the database.
|
|
|
83 |
|
84 |
+
# Returns:
|
85 |
+
# List of tuples containing camera location key and URL
|
|
|
86 |
|
87 |
def get_camera_locations(self):
|
88 |
try:
|
|
|
94 |
logging.error(f"Error fetching camera locations: {str(e)}")
|
95 |
raise
|
96 |
|
97 |
+
# Downloads an image from a given URL.
|
|
|
98 |
|
99 |
+
# Parameters:
|
100 |
+
# url: URL of the traffic camera image
|
101 |
|
102 |
+
# Returns:
|
103 |
+
# PIL Image object
|
|
|
104 |
|
105 |
def download_image(self, url):
|
106 |
try:
|
|
|
111 |
logging.error(f"Error downloading image from {url}: {str(e)}")
|
112 |
raise
|
113 |
|
114 |
+
# Detects vehicles in an image using a computer vision model.
|
|
|
115 |
|
116 |
+
# Parameters:
|
117 |
+
# image: PIL Image object to analyze
|
118 |
+
# confidence_threshold: Minimum confidence score for detections (default: 0.7)
|
119 |
|
120 |
+
# Returns:
|
121 |
+
# List of vehicle detection dictionaries with bounding boxes and scores
|
|
|
122 |
|
123 |
def detect_vehicles(self, image, confidence_threshold=0.7):
|
124 |
try:
|
|
|
164 |
logging.error(f"Error detecting vehicles: {str(e)}")
|
165 |
raise
|
166 |
|
167 |
+
# Draws vehicle detection bounding boxes and labels on the image.
|
|
|
168 |
|
169 |
+
# Parameters:
|
170 |
+
# image: Original PIL Image
|
171 |
+
# detections: List of vehicle detection dictionaries
|
172 |
|
173 |
+
# Returns:
|
174 |
+
# New PIL Image with bounding boxes and labels drawn
|
|
|
175 |
|
176 |
def draw_detections(self, image, detections):
|
177 |
try:
|
|
|
225 |
logging.error(f"Error drawing detections: {str(e)}")
|
226 |
raise
|
227 |
|
228 |
+
# Processes all traffic cameras, detects vehicles, and prepares data for storage.
|
|
|
229 |
|
230 |
+
# This method:
|
231 |
+
# 1. Gets all camera locations
|
232 |
+
# 2. Downloads images from each camera
|
233 |
+
# 3. Detects vehicles in each image
|
234 |
+
# 4. Processes images to visualize detections
|
235 |
+
# 5. Prepares data for storage
|
|
|
236 |
|
237 |
def process_traffic_cameras(self):
|
238 |
try:
|
|
|
320 |
logging.error(f"Error in process_traffic_cameras: {str(e)}")
|
321 |
raise
|
322 |
|
323 |
+
# Updates the HuggingFace dataset with new traffic data.
|
324 |
+
# This method must be implemented by subclasses.
|
|
|
325 |
|
326 |
+
# Parameters:
|
327 |
+
# batch_data: Dictionary containing the batch data to add
|
328 |
+
# timestamp_str: Timestamp string for the current batch
|
|
|
329 |
|
330 |
def update_huggingface_dataset(self, batch_data, timestamp_str):
|
331 |
raise NotImplementedError("Subclasses must implement update_huggingface_dataset method")
|
332 |
|
333 |
+
# Creates COCO annotation files for the dataset.
|
334 |
+
# This method must be implemented by subclasses.
|
|
|
335 |
|
336 |
+
# Parameters:
|
337 |
+
# dataset_dict: Dictionary containing the dataset
|
338 |
+
# timestamp_str: Timestamp string for the current batch
|
|
|
339 |
|
340 |
def create_coco_annotation_files(self, dataset_dict, timestamp_str):
|
341 |
raise NotImplementedError("Subclasses must implement create_coco_annotation_files method")
|
342 |
|
343 |
+
# Updates the README file for the dataset.
|
344 |
+
# This method must be implemented by subclasses.
|
|
|
345 |
|
346 |
+
# Parameters:
|
347 |
+
# dataset_dict: Dictionary containing the dataset
|
348 |
+
# timestamp_str: Timestamp string for the current batch
|
|
|
349 |
|
350 |
def update_readme(self, dataset_dict, timestamp_str):
|
351 |
raise NotImplementedError("Subclasses must implement update_readme method")
|
352 |
|
353 |
+
# Runs the traffic image analyzer, processing all cameras and updating the dataset.
|
|
|
|
|
354 |
|
355 |
def run(self):
|
356 |
try:
|
cronjob/application_traffic_image_analyzer.py
CHANGED
@@ -1,16 +1,14 @@
|
|
1 |
-
|
2 |
-
Application Traffic Image Analyzer Module
|
3 |
|
4 |
-
This module extends the AbstractTrafficImageAnalyzer to provide specific implementation for
|
5 |
-
application-specific traffic analysis. It handles the processing of traffic camera images,
|
6 |
-
vehicle detection using the DETR model, and updating a HuggingFace dataset with the results.
|
7 |
|
8 |
-
The analyzer is used in the HKUST BNB+ platform to collect and analyze traffic data for
|
9 |
-
determining eco-friendly discounts based on traffic conditions.
|
10 |
|
11 |
-
Author: Gordon Li (20317033)
|
12 |
-
Date: March 2025
|
13 |
-
"""
|
14 |
|
15 |
from transformers import DetrImageProcessor, DetrForObjectDetection
|
16 |
from datasets import Dataset, Features, Value, load_dataset, DatasetDict, concatenate_datasets
|
@@ -24,13 +22,12 @@ import logging
|
|
24 |
|
25 |
|
26 |
class ApplicationTrafficImageAnalyzer(AbstractTrafficImageAnalyzer):
|
27 |
-
|
28 |
-
|
|
|
|
|
|
|
29 |
|
30 |
-
Sets up:
|
31 |
-
- DETR image processor and model for vehicle detection
|
32 |
-
- Application-specific directory for storing results
|
33 |
-
"""
|
34 |
def __init__(self):
|
35 |
super().__init__()
|
36 |
self.processor = DetrImageProcessor.from_pretrained("slliac/detr-group37-liaujianjie-resnet-50",
|
@@ -40,13 +37,11 @@ class ApplicationTrafficImageAnalyzer(AbstractTrafficImageAnalyzer):
|
|
40 |
self.application_dir = os.path.join(self.dataset_dir, "application")
|
41 |
os.makedirs(self.application_dir, exist_ok=True)
|
42 |
|
43 |
-
|
44 |
-
Updates the HuggingFace dataset with new traffic data.
|
45 |
|
46 |
-
Parameters:
|
47 |
-
|
48 |
-
|
49 |
-
"""
|
50 |
|
51 |
def update_huggingface_dataset(self, batch_data, timestamp_str):
|
52 |
try:
|
@@ -119,13 +114,11 @@ class ApplicationTrafficImageAnalyzer(AbstractTrafficImageAnalyzer):
|
|
119 |
logging.error(f"Error in update_huggingface_dataset: {str(e)}")
|
120 |
raise
|
121 |
|
122 |
-
|
123 |
-
Creates COCO annotation files for the dataset, which are standard format for object detection.
|
124 |
|
125 |
-
Parameters:
|
126 |
-
|
127 |
-
|
128 |
-
"""
|
129 |
|
130 |
def create_coco_annotation_files(self, dataset_dict, timestamp_str):
|
131 |
try:
|
@@ -244,11 +237,9 @@ class ApplicationTrafficImageAnalyzer(AbstractTrafficImageAnalyzer):
|
|
244 |
logging.error(f"Error creating COCO annotation files: {str(e)}")
|
245 |
|
246 |
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
and displays dataset information before and after the process.
|
251 |
-
"""
|
252 |
|
253 |
|
254 |
def main():
|
@@ -279,8 +270,6 @@ def main():
|
|
279 |
print("\nProgram terminated")
|
280 |
|
281 |
|
282 |
-
|
283 |
-
Entry point for the script.
|
284 |
-
"""
|
285 |
if __name__ == "__main__":
|
286 |
main()
|
|
|
1 |
+
# Application Traffic Image Analyzer Module
|
|
|
2 |
|
3 |
+
# This module extends the AbstractTrafficImageAnalyzer to provide specific implementation for
|
4 |
+
# application-specific traffic analysis. It handles the processing of traffic camera images,
|
5 |
+
# vehicle detection using the DETR model, and updating a HuggingFace dataset with the results.
|
6 |
|
7 |
+
# The analyzer is used in the HKUST BNB+ platform to collect and analyze traffic data for
|
8 |
+
# determining eco-friendly discounts based on traffic conditions.
|
9 |
|
10 |
+
# Author: Gordon Li (20317033)
|
11 |
+
# Date: March 2025
|
|
|
12 |
|
13 |
from transformers import DetrImageProcessor, DetrForObjectDetection
|
14 |
from datasets import Dataset, Features, Value, load_dataset, DatasetDict, concatenate_datasets
|
|
|
22 |
|
23 |
|
24 |
class ApplicationTrafficImageAnalyzer(AbstractTrafficImageAnalyzer):
|
25 |
+
# Initializes the application traffic analyzer with the DETR model and processor.
|
26 |
+
|
27 |
+
# Sets up:
|
28 |
+
# - DETR image processor and model for vehicle detection
|
29 |
+
# - Application-specific directory for storing results
|
30 |
|
|
|
|
|
|
|
|
|
31 |
def __init__(self):
|
32 |
super().__init__()
|
33 |
self.processor = DetrImageProcessor.from_pretrained("slliac/detr-group37-liaujianjie-resnet-50",
|
|
|
37 |
self.application_dir = os.path.join(self.dataset_dir, "application")
|
38 |
os.makedirs(self.application_dir, exist_ok=True)
|
39 |
|
40 |
+
# Updates the HuggingFace dataset with new traffic data.
|
|
|
41 |
|
42 |
+
# Parameters:
|
43 |
+
# batch_data: Dictionary containing batch data including capture time, location, images, and vehicle counts
|
44 |
+
# timestamp_str: Timestamp string for the current batch
|
|
|
45 |
|
46 |
def update_huggingface_dataset(self, batch_data, timestamp_str):
|
47 |
try:
|
|
|
114 |
logging.error(f"Error in update_huggingface_dataset: {str(e)}")
|
115 |
raise
|
116 |
|
117 |
+
# Creates COCO annotation files for the dataset, which are standard format for object detection.
|
|
|
118 |
|
119 |
+
# Parameters:
|
120 |
+
# dataset_dict: Dictionary containing the dataset with traffic observations
|
121 |
+
# timestamp_str: Timestamp string for the current batch
|
|
|
122 |
|
123 |
def create_coco_annotation_files(self, dataset_dict, timestamp_str):
|
124 |
try:
|
|
|
237 |
logging.error(f"Error creating COCO annotation files: {str(e)}")
|
238 |
|
239 |
|
240 |
+
# Main function to execute the traffic image analysis process.
|
241 |
+
# Initializes the analyzer, loads existing data if available, runs the analysis,
|
242 |
+
# and displays dataset information before and after the process.
|
|
|
|
|
243 |
|
244 |
|
245 |
def main():
|
|
|
270 |
print("\nProgram terminated")
|
271 |
|
272 |
|
273 |
+
# Entry point for the script.
|
|
|
|
|
274 |
if __name__ == "__main__":
|
275 |
main()
|
cronjob/train_detr_traffic_image_analyzer.py
CHANGED
@@ -1,18 +1,16 @@
|
|
1 |
-
|
2 |
-
Traffic Image Analyzer for DETR Model Training
|
3 |
|
4 |
-
This module extends the AbstractTrafficImageAnalyzer to provide implementation for training
|
5 |
-
data collection for the DETR object detection model. It processes traffic camera images,
|
6 |
-
detects vehicles using the pretrained Facebook DETR ResNet-50 model, and organizes the data
|
7 |
-
for model training purposes.
|
8 |
|
9 |
-
The data collected by this analyzer is used to train custom DETR models that improve vehicle
|
10 |
-
detection accuracy, which ultimately enhances the traffic analysis component of the HKUST BNB+
|
11 |
-
platform's eco-friendly discount system.
|
12 |
|
13 |
-
Author: Gordon Li (20317033)
|
14 |
-
Date: March 2025
|
15 |
-
"""
|
16 |
|
17 |
from transformers import DetrImageProcessor, DetrForObjectDetection
|
18 |
from datasets import Dataset, Features, Value, load_dataset, concatenate_datasets, DatasetDict
|
@@ -28,13 +26,12 @@ import logging
|
|
28 |
class TrainDETRTrafficImageAnalyzer(AbstractTrafficImageAnalyzer):
|
29 |
|
30 |
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
33 |
|
34 |
-
Sets up:
|
35 |
-
- Facebook DETR ResNet-50 image processor and model
|
36 |
-
- Directory structure for storing DETR training data
|
37 |
-
"""
|
38 |
def __init__(self):
|
39 |
super().__init__()
|
40 |
|
@@ -44,13 +41,11 @@ class TrainDETRTrafficImageAnalyzer(AbstractTrafficImageAnalyzer):
|
|
44 |
self.fb_detr_dir = os.path.join(self.dataset_dir, "fb_detr_res_50")
|
45 |
os.makedirs(self.fb_detr_dir, exist_ok=True)
|
46 |
|
47 |
-
|
48 |
-
Updates the HuggingFace dataset with new traffic data for DETR model training.
|
49 |
|
50 |
-
Parameters:
|
51 |
-
|
52 |
-
|
53 |
-
"""
|
54 |
|
55 |
def update_huggingface_dataset(self, batch_data, timestamp_str):
|
56 |
try:
|
@@ -110,13 +105,11 @@ class TrainDETRTrafficImageAnalyzer(AbstractTrafficImageAnalyzer):
|
|
110 |
logging.error(f"Error updating Hugging Face dataset: {str(e)}")
|
111 |
raise
|
112 |
|
113 |
-
|
114 |
-
Creates COCO annotation files for the DETR training dataset.
|
115 |
|
116 |
-
Parameters:
|
117 |
-
|
118 |
-
|
119 |
-
"""
|
120 |
|
121 |
def create_coco_annotation_files(self, dataset_dict, timestamp_str):
|
122 |
try:
|
@@ -230,11 +223,9 @@ class TrainDETRTrafficImageAnalyzer(AbstractTrafficImageAnalyzer):
|
|
230 |
logging.error(f"Error creating COCO annotation files: {str(e)}")
|
231 |
|
232 |
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
and displays dataset information before and after the process.
|
237 |
-
"""
|
238 |
|
239 |
|
240 |
def main():
|
@@ -264,8 +255,6 @@ def main():
|
|
264 |
print("\nProgram terminated")
|
265 |
|
266 |
|
267 |
-
|
268 |
-
Entry point for the script.
|
269 |
-
"""
|
270 |
if __name__ == "__main__":
|
271 |
main()
|
|
|
1 |
+
# Traffic Image Analyzer for DETR Model Training
|
|
|
2 |
|
3 |
+
# This module extends the AbstractTrafficImageAnalyzer to provide implementation for training
|
4 |
+
# data collection for the DETR object detection model. It processes traffic camera images,
|
5 |
+
# detects vehicles using the pretrained Facebook DETR ResNet-50 model, and organizes the data
|
6 |
+
# for model training purposes.
|
7 |
|
8 |
+
# The data collected by this analyzer is used to train custom DETR models that improve vehicle
|
9 |
+
# detection accuracy, which ultimately enhances the traffic analysis component of the HKUST BNB+
|
10 |
+
# platform's eco-friendly discount system.
|
11 |
|
12 |
+
# Author: Gordon Li (20317033)
|
13 |
+
# Date: March 2025
|
|
|
14 |
|
15 |
from transformers import DetrImageProcessor, DetrForObjectDetection
|
16 |
from datasets import Dataset, Features, Value, load_dataset, concatenate_datasets, DatasetDict
|
|
|
26 |
class TrainDETRTrafficImageAnalyzer(AbstractTrafficImageAnalyzer):
|
27 |
|
28 |
|
29 |
+
# Initializes the DETR training data collector with the Facebook pretrained model.
|
30 |
+
|
31 |
+
# Sets up:
|
32 |
+
# - Facebook DETR ResNet-50 image processor and model
|
33 |
+
# - Directory structure for storing DETR training data
|
34 |
|
|
|
|
|
|
|
|
|
35 |
def __init__(self):
|
36 |
super().__init__()
|
37 |
|
|
|
41 |
self.fb_detr_dir = os.path.join(self.dataset_dir, "fb_detr_res_50")
|
42 |
os.makedirs(self.fb_detr_dir, exist_ok=True)
|
43 |
|
44 |
+
# Updates the HuggingFace dataset with new traffic data for DETR model training.
|
|
|
45 |
|
46 |
+
# Parameters:
|
47 |
+
# batch_data: Dictionary containing traffic image data and annotations
|
48 |
+
# timestamp_str: Timestamp string for the current batch
|
|
|
49 |
|
50 |
def update_huggingface_dataset(self, batch_data, timestamp_str):
|
51 |
try:
|
|
|
105 |
logging.error(f"Error updating Hugging Face dataset: {str(e)}")
|
106 |
raise
|
107 |
|
108 |
+
# Creates COCO annotation files for the DETR training dataset.
|
|
|
109 |
|
110 |
+
# Parameters:
|
111 |
+
# dataset_dict: Dictionary containing the dataset splits
|
112 |
+
# timestamp_str: Timestamp string for the current batch
|
|
|
113 |
|
114 |
def create_coco_annotation_files(self, dataset_dict, timestamp_str):
|
115 |
try:
|
|
|
223 |
logging.error(f"Error creating COCO annotation files: {str(e)}")
|
224 |
|
225 |
|
226 |
+
# Main function to execute the DETR training data collection process.
|
227 |
+
# Initializes the analyzer, loads existing data if available, runs the analysis,
|
228 |
+
# and displays dataset information before and after the process.
|
|
|
|
|
229 |
|
230 |
|
231 |
def main():
|
|
|
255 |
print("\nProgram terminated")
|
256 |
|
257 |
|
258 |
+
# Entry point for the script.
|
|
|
|
|
259 |
if __name__ == "__main__":
|
260 |
main()
|
visualiser/hkust_bnb_visualiser.py
CHANGED
@@ -1,18 +1,16 @@
|
|
1 |
-
|
2 |
-
hkust_bnb_visualiser.py
|
3 |
|
4 |
-
This module provides the main visualization for the HKUST BNB+ platform.
|
5 |
-
It handles database connections, data retrieval, search relevance calculation, and map visualization
|
6 |
-
for BNB listings across different neighborhoods in Hong Kong. The class integrates with traffic data
|
7 |
-
to provide eco-friendly discount calculations based on traffic conditions.
|
8 |
|
9 |
-
Key capabilities:
|
10 |
-
- Semantic search functionality using sentence transformers
|
11 |
-
- Traffic spot integration for eco-friendly discount calculations
|
12 |
|
13 |
-
Author: Gordon Li (20317033)
|
14 |
-
Date: March 2025
|
15 |
-
"""
|
16 |
|
17 |
import oracledb
|
18 |
import pandas as pd
|
@@ -36,16 +34,12 @@ from constant.hkust_bnb_constant import (
|
|
36 |
)
|
37 |
|
38 |
class HKUSTBNBVisualiser:
|
39 |
-
|
40 |
-
|
41 |
-
Handles database connections, data retrieval, and rendering of interactive maps.
|
42 |
-
"""
|
43 |
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
and prepares neighborhood data with caching structures.
|
48 |
-
"""
|
49 |
|
50 |
def __init__(self):
|
51 |
self.connection_params = {
|
@@ -81,17 +75,15 @@ class HKUSTBNBVisualiser:
|
|
81 |
self.cached_listings = {}
|
82 |
self.cached_embeddings = {}
|
83 |
|
84 |
-
|
85 |
-
Finds the nearest traffic spot to a given BNB listing location.
|
86 |
|
87 |
-
Parameters:
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
|
92 |
-
Returns:
|
93 |
-
|
94 |
-
"""
|
95 |
|
96 |
def find_nearest_traffic_spot(self, airbnb_lat, airbnb_lng, max_distance_km=0.7):
|
97 |
nearest_spot = None
|
@@ -111,12 +103,10 @@ class HKUSTBNBVisualiser:
|
|
111 |
else:
|
112 |
return None, None
|
113 |
|
114 |
-
|
115 |
-
Retrieves all available neighborhoods from the database.
|
116 |
|
117 |
-
Returns:
|
118 |
-
|
119 |
-
"""
|
120 |
|
121 |
def get_all_neighborhoods(self):
|
122 |
connection = self.pool.acquire()
|
@@ -133,16 +123,14 @@ class HKUSTBNBVisualiser:
|
|
133 |
finally:
|
134 |
self.pool.release(connection)
|
135 |
|
136 |
-
|
137 |
-
Retrieves BNB listings for a specific neighborhood with caching.
|
138 |
|
139 |
-
Parameters:
|
140 |
-
|
141 |
-
|
142 |
|
143 |
-
Returns:
|
144 |
-
|
145 |
-
"""
|
146 |
|
147 |
def get_neighborhood_listings(self, neighborhood, limit=10):
|
148 |
if limit not in [10, 20, 30, 40, 50]:
|
@@ -174,15 +162,13 @@ class HKUSTBNBVisualiser:
|
|
174 |
finally:
|
175 |
self.pool.release(connection)
|
176 |
|
177 |
-
|
178 |
-
Retrieves reviews for a specific listing ID.
|
179 |
|
180 |
-
Parameters:
|
181 |
-
|
182 |
|
183 |
-
Returns:
|
184 |
-
|
185 |
-
"""
|
186 |
|
187 |
def get_listing_reviews(self, listing_id):
|
188 |
connection = self.pool.acquire()
|
@@ -211,15 +197,13 @@ class HKUSTBNBVisualiser:
|
|
211 |
finally:
|
212 |
self.pool.release(connection)
|
213 |
|
214 |
-
|
215 |
-
Retrieves review content for search functionality.
|
216 |
|
217 |
-
Parameters:
|
218 |
-
|
219 |
|
220 |
-
Returns:
|
221 |
-
|
222 |
-
"""
|
223 |
|
224 |
def get_listing_reviews_for_search(self, listing_id):
|
225 |
connection = self.pool.acquire()
|
@@ -246,16 +230,14 @@ class HKUSTBNBVisualiser:
|
|
246 |
finally:
|
247 |
self.pool.release(connection)
|
248 |
|
249 |
-
|
250 |
-
Computes cosine similarity between two embeddings.
|
251 |
|
252 |
-
Parameters:
|
253 |
-
|
254 |
-
|
255 |
|
256 |
-
Returns:
|
257 |
-
|
258 |
-
"""
|
259 |
|
260 |
def compute_similarity(self, query_embedding, target_embedding):
|
261 |
if query_embedding is None or target_embedding is None:
|
@@ -267,16 +249,14 @@ class HKUSTBNBVisualiser:
|
|
267 |
print(f"Error computing similarity: {str(e)}")
|
268 |
return 0.0
|
269 |
|
270 |
-
|
271 |
-
Computes relevance scores for listings based on search query.
|
272 |
|
273 |
-
Parameters:
|
274 |
-
|
275 |
-
|
276 |
|
277 |
-
Returns:
|
278 |
-
|
279 |
-
"""
|
280 |
|
281 |
def compute_search_scores(self, df, search_query):
|
282 |
if not search_query or self.model is None:
|
@@ -317,16 +297,14 @@ class HKUSTBNBVisualiser:
|
|
317 |
print(f"Error in search scoring: {str(e)}")
|
318 |
return [0.0] * len(df)
|
319 |
|
320 |
-
|
321 |
-
Sorts a DataFrame of listings by their relevance to a search query.
|
322 |
|
323 |
-
Parameters:
|
324 |
-
|
325 |
-
|
326 |
|
327 |
-
Returns:
|
328 |
-
|
329 |
-
"""
|
330 |
|
331 |
def sort_by_relevance(self, df, search_query):
|
332 |
if not search_query:
|
@@ -336,23 +314,21 @@ class HKUSTBNBVisualiser:
|
|
336 |
df['relevance_percentage'] = df['relevance_score'] * 100
|
337 |
return df.sort_values('relevance_score', ascending=False)
|
338 |
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
Tuple containing (folium_map, listings_dataframe)
|
355 |
-
"""
|
356 |
|
357 |
def create_map_and_data(self, neighborhood="Sha Tin", show_traffic=True, center_lat=None, center_lng=None,
|
358 |
selected_id=None, search_query=None, current_page=1, items_per_page=3, listings_limit=10):
|
|
|
1 |
+
# hkust_bnb_visualiser.py
|
|
|
2 |
|
3 |
+
# This module provides the main visualization for the HKUST BNB+ platform.
|
4 |
+
# It handles database connections, data retrieval, search relevance calculation, and map visualization
|
5 |
+
# for BNB listings across different neighborhoods in Hong Kong. The class integrates with traffic data
|
6 |
+
# to provide eco-friendly discount calculations based on traffic conditions.
|
7 |
|
8 |
+
# Key capabilities:
|
9 |
+
# - Semantic search functionality using sentence transformers
|
10 |
+
# - Traffic spot integration for eco-friendly discount calculations
|
11 |
|
12 |
+
# Author: Gordon Li (20317033)
|
13 |
+
# Date: March 2025
|
|
|
14 |
|
15 |
import oracledb
|
16 |
import pandas as pd
|
|
|
34 |
)
|
35 |
|
36 |
class HKUSTBNBVisualiser:
|
37 |
+
# Main class for BNB data visualization and management.
|
38 |
+
# Handles database connections, data retrieval, and rendering of interactive maps.
|
|
|
|
|
39 |
|
40 |
+
# Initializes the BNB visualizer with database connection, traffic spot manager, and NLP model.
|
41 |
+
# Sets up connection pool, loads traffic data, initializes sentence transformer model,
|
42 |
+
# and prepares neighborhood data with caching structures.
|
|
|
|
|
43 |
|
44 |
def __init__(self):
|
45 |
self.connection_params = {
|
|
|
75 |
self.cached_listings = {}
|
76 |
self.cached_embeddings = {}
|
77 |
|
78 |
+
# Finds the nearest traffic spot to a given BNB listing location.
|
|
|
79 |
|
80 |
+
# Parameters:
|
81 |
+
# airbnb_lat: The latitude of the BNB listing
|
82 |
+
# airbnb_lng: The longitude of the BNB listing
|
83 |
+
# max_distance_km: Maximum distance in kilometers to consider a traffic spot (default: 0.7)
|
84 |
|
85 |
+
# Returns:
|
86 |
+
# Tuple containing (nearest_traffic_spot, distance_in_km) or (None, None) if no spot is found
|
|
|
87 |
|
88 |
def find_nearest_traffic_spot(self, airbnb_lat, airbnb_lng, max_distance_km=0.7):
|
89 |
nearest_spot = None
|
|
|
103 |
else:
|
104 |
return None, None
|
105 |
|
106 |
+
# Retrieves all available neighborhoods from the database.
|
|
|
107 |
|
108 |
+
# Returns:
|
109 |
+
# List of neighborhood names as strings
|
|
|
110 |
|
111 |
def get_all_neighborhoods(self):
|
112 |
connection = self.pool.acquire()
|
|
|
123 |
finally:
|
124 |
self.pool.release(connection)
|
125 |
|
126 |
+
# Retrieves BNB listings for a specific neighborhood with caching.
|
|
|
127 |
|
128 |
+
# Parameters:
|
129 |
+
# neighborhood: The neighborhood name to retrieve listings for
|
130 |
+
# limit: Maximum number of listings to retrieve (default: 10)
|
131 |
|
132 |
+
# Returns:
|
133 |
+
# List of listing data rows from the database
|
|
|
134 |
|
135 |
def get_neighborhood_listings(self, neighborhood, limit=10):
|
136 |
if limit not in [10, 20, 30, 40, 50]:
|
|
|
162 |
finally:
|
163 |
self.pool.release(connection)
|
164 |
|
165 |
+
# Retrieves reviews for a specific listing ID.
|
|
|
166 |
|
167 |
+
# Parameters:
|
168 |
+
# listing_id: The ID of the listing to get reviews for
|
169 |
|
170 |
+
# Returns:
|
171 |
+
# List of tuples containing (review_date, reviewer_name, comments)
|
|
|
172 |
|
173 |
def get_listing_reviews(self, listing_id):
|
174 |
connection = self.pool.acquire()
|
|
|
197 |
finally:
|
198 |
self.pool.release(connection)
|
199 |
|
200 |
+
# Retrieves review content for search functionality.
|
|
|
201 |
|
202 |
+
# Parameters:
|
203 |
+
# listing_id: The ID of the listing to get reviews for
|
204 |
|
205 |
+
# Returns:
|
206 |
+
# List of review comment strings for semantic search
|
|
|
207 |
|
208 |
def get_listing_reviews_for_search(self, listing_id):
|
209 |
connection = self.pool.acquire()
|
|
|
230 |
finally:
|
231 |
self.pool.release(connection)
|
232 |
|
233 |
+
# Computes cosine similarity between two embeddings.
|
|
|
234 |
|
235 |
+
# Parameters:
|
236 |
+
# query_embedding: Embedding tensor for the search query
|
237 |
+
# target_embedding: Embedding tensor for the target text
|
238 |
|
239 |
+
# Returns:
|
240 |
+
# Float value representing similarity (0.0-1.0)
|
|
|
241 |
|
242 |
def compute_similarity(self, query_embedding, target_embedding):
|
243 |
if query_embedding is None or target_embedding is None:
|
|
|
249 |
print(f"Error computing similarity: {str(e)}")
|
250 |
return 0.0
|
251 |
|
252 |
+
# Computes relevance scores for listings based on search query.
|
|
|
253 |
|
254 |
+
# Parameters:
|
255 |
+
# df: DataFrame containing listing data
|
256 |
+
# search_query: User's search query string
|
257 |
|
258 |
+
# Returns:
|
259 |
+
# List of relevance scores for each listing in the DataFrame
|
|
|
260 |
|
261 |
def compute_search_scores(self, df, search_query):
|
262 |
if not search_query or self.model is None:
|
|
|
297 |
print(f"Error in search scoring: {str(e)}")
|
298 |
return [0.0] * len(df)
|
299 |
|
300 |
+
# Sorts a DataFrame of listings by their relevance to a search query.
|
|
|
301 |
|
302 |
+
# Parameters:
|
303 |
+
# df: DataFrame containing listing data
|
304 |
+
# search_query: User's search query string
|
305 |
|
306 |
+
# Returns:
|
307 |
+
# DataFrame sorted by relevance to the search query
|
|
|
308 |
|
309 |
def sort_by_relevance(self, df, search_query):
|
310 |
if not search_query:
|
|
|
314 |
df['relevance_percentage'] = df['relevance_score'] * 100
|
315 |
return df.sort_values('relevance_score', ascending=False)
|
316 |
|
317 |
+
# Creates an interactive map and DataFrame for display in the UI.
|
318 |
+
|
319 |
+
# Parameters:
|
320 |
+
# neighborhood: The neighborhood to display listings for (default: "Sha Tin")
|
321 |
+
# show_traffic: Whether to show traffic spots on the map (default: True)
|
322 |
+
# center_lat: Latitude to center the map on (default: None, will use mean of listings)
|
323 |
+
# center_lng: Longitude to center the map on (default: None, will use mean of listings)
|
324 |
+
# selected_id: ID of the currently selected listing (default: None)
|
325 |
+
# search_query: User's search query string (default: None)
|
326 |
+
# current_page: Current page number for pagination (default: 1)
|
327 |
+
# items_per_page: Number of items to show per page (default: 3)
|
328 |
+
# listings_limit: Maximum number of listings to retrieve (default: 10)
|
329 |
+
|
330 |
+
# Returns:
|
331 |
+
# Tuple containing (folium_map, listings_dataframe)
|
|
|
|
|
332 |
|
333 |
def create_map_and_data(self, neighborhood="Sha Tin", show_traffic=True, center_lat=None, center_lng=None,
|
334 |
selected_id=None, search_query=None, current_page=1, items_per_page=3, listings_limit=10):
|
visualiser/td_traffic_spot_visualiser.py
CHANGED
@@ -1,17 +1,15 @@
|
|
1 |
-
|
2 |
-
td_traffic_spot_visualiser.py
|
3 |
|
4 |
-
This module handles traffic data integration for the BNB+ platform, providing traffic-based
|
5 |
-
discount calculations and map visualization of traffic spots. It includes classes for
|
6 |
-
individual traffic spots and a manager to handle collections of spots.
|
7 |
|
8 |
-
The module integrates with a dataset of traffic observations to determine traffic conditions
|
9 |
-
and calculate eco-friendly discounts for BNB listings based on local traffic volume.
|
10 |
|
11 |
-
Author: Gordon Li (20317033)
|
12 |
-
Date: March 2025
|
13 |
|
14 |
-
"""
|
15 |
import folium
|
16 |
import oracledb
|
17 |
import logging
|
@@ -31,15 +29,13 @@ from constant.hkust_bnb_constant import (
|
|
31 |
|
32 |
|
33 |
class TDTrafficSpot:
|
34 |
-
|
35 |
-
Initializes a traffic spot with location and historical traffic data.
|
36 |
|
37 |
-
Parameters:
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
"""
|
43 |
|
44 |
def __init__(self, key, latitude, longitude, dataset_rows=None):
|
45 |
self.key = key
|
@@ -49,25 +45,21 @@ class TDTrafficSpot:
|
|
49 |
self.avg_vehicle_count = self.calculate_avg_vehicle_count()
|
50 |
self.recent_display_rows = self.get_recent_display_rows()
|
51 |
|
52 |
-
|
53 |
-
Checks if the traffic spot has valid geographic coordinates.
|
54 |
|
55 |
-
Returns:
|
56 |
-
|
57 |
-
"""
|
58 |
|
59 |
def is_valid(self):
|
60 |
return self.latitude is not None and self.longitude is not None
|
61 |
|
62 |
-
|
63 |
-
Gets the most recent traffic observations for display purposes.
|
64 |
|
65 |
-
Parameters:
|
66 |
-
|
67 |
|
68 |
-
Returns:
|
69 |
-
|
70 |
-
"""
|
71 |
|
72 |
def get_recent_display_rows(self, max_display=2):
|
73 |
if not self.dataset_rows:
|
@@ -76,12 +68,10 @@ class TDTrafficSpot:
|
|
76 |
sorted_rows = sorted(self.dataset_rows, key=lambda x: x['capture_time'], reverse=True)
|
77 |
return sorted_rows[:max_display]
|
78 |
|
79 |
-
|
80 |
-
Calculates the average vehicle count based on historical traffic observations.
|
81 |
|
82 |
-
Returns:
|
83 |
-
|
84 |
-
"""
|
85 |
|
86 |
def calculate_avg_vehicle_count(self):
|
87 |
if not self.dataset_rows:
|
@@ -94,12 +84,10 @@ class TDTrafficSpot:
|
|
94 |
|
95 |
return np.mean(vehicle_counts)
|
96 |
|
97 |
-
|
98 |
-
Determines the discount rate based on average traffic volume.
|
99 |
|
100 |
-
Returns:
|
101 |
-
|
102 |
-
"""
|
103 |
|
104 |
def get_discount_rate(self):
|
105 |
if self.avg_vehicle_count < 2:
|
@@ -109,12 +97,10 @@ class TDTrafficSpot:
|
|
109 |
else:
|
110 |
return 0.0
|
111 |
|
112 |
-
|
113 |
-
Generates a human-readable description of the traffic-based discount.
|
114 |
|
115 |
-
Returns:
|
116 |
-
|
117 |
-
"""
|
118 |
|
119 |
def get_discount_info(self):
|
120 |
discount_rate = self.get_discount_rate()
|
@@ -124,12 +110,10 @@ class TDTrafficSpot:
|
|
124 |
|
125 |
return f"{int(discount_rate * 100)}% discount! Low traffic area"
|
126 |
|
127 |
-
|
128 |
-
Creates HTML content for the traffic spot's popup on the map.
|
129 |
|
130 |
-
Returns:
|
131 |
-
|
132 |
-
"""
|
133 |
|
134 |
def create_popup_content(self):
|
135 |
discount_info = self.get_discount_info()
|
@@ -177,12 +161,10 @@ class TDTrafficSpot:
|
|
177 |
html += "</div>"
|
178 |
return html
|
179 |
|
180 |
-
|
181 |
-
Adds the traffic spot to a Folium map with appropriate styling.
|
182 |
|
183 |
-
Parameters:
|
184 |
-
|
185 |
-
"""
|
186 |
|
187 |
def add_to_map(self, folium_map):
|
188 |
if self.is_valid():
|
@@ -202,16 +184,12 @@ class TDTrafficSpot:
|
|
202 |
|
203 |
|
204 |
class TrafficSpotManager:
|
205 |
-
|
206 |
-
Manages a collection of traffic spots, handling data loading and map integration.
|
207 |
-
"""
|
208 |
|
209 |
-
|
210 |
-
Initializes the manager with database connection parameters and loads initial traffic spots.
|
211 |
|
212 |
-
Parameters:
|
213 |
-
|
214 |
-
"""
|
215 |
|
216 |
def __init__(self, connection_params):
|
217 |
self.connection_params = connection_params
|
@@ -219,12 +197,10 @@ class TrafficSpotManager:
|
|
219 |
self.spot_dict = {}
|
220 |
self.load_limited_traffic_spots()
|
221 |
|
222 |
-
|
223 |
-
Loads a limited number of traffic spots for initial display.
|
224 |
|
225 |
-
Parameters:
|
226 |
-
|
227 |
-
"""
|
228 |
|
229 |
def load_limited_traffic_spots(self, limit=10):
|
230 |
try:
|
@@ -286,12 +262,10 @@ class TrafficSpotManager:
|
|
286 |
self.traffic_spots = []
|
287 |
self.spot_dict = {}
|
288 |
|
289 |
-
|
290 |
-
Loads specific traffic spots by their keys when needed.
|
291 |
|
292 |
-
Parameters:
|
293 |
-
|
294 |
-
"""
|
295 |
|
296 |
def load_specific_traffic_spots(self, keys):
|
297 |
needed_keys = [key for key in keys if key not in self.spot_dict]
|
@@ -341,13 +315,11 @@ class TrafficSpotManager:
|
|
341 |
except Exception as e:
|
342 |
logging.error(f"Error loading specific traffic spots: {str(e)}")
|
343 |
|
344 |
-
|
345 |
-
Adds traffic spots to a Folium map.
|
346 |
|
347 |
-
Parameters:
|
348 |
-
|
349 |
-
|
350 |
-
"""
|
351 |
|
352 |
def add_spots_to_map(self, folium_map, spot_keys=None):
|
353 |
if spot_keys is None:
|
@@ -358,15 +330,13 @@ class TrafficSpotManager:
|
|
358 |
if key in self.spot_dict:
|
359 |
self.spot_dict[key].add_to_map(folium_map)
|
360 |
|
361 |
-
|
362 |
-
Retrieves a traffic spot by its key, loading it if necessary.
|
363 |
|
364 |
-
Parameters:
|
365 |
-
|
366 |
|
367 |
-
Returns:
|
368 |
-
|
369 |
-
"""
|
370 |
|
371 |
def get_spot_by_key(self, key):
|
372 |
if key in self.spot_dict:
|
|
|
1 |
+
# td_traffic_spot_visualiser.py
|
|
|
2 |
|
3 |
+
# This module handles traffic data integration for the BNB+ platform, providing traffic-based
|
4 |
+
# discount calculations and map visualization of traffic spots. It includes classes for
|
5 |
+
# individual traffic spots and a manager to handle collections of spots.
|
6 |
|
7 |
+
# The module integrates with a dataset of traffic observations to determine traffic conditions
|
8 |
+
# and calculate eco-friendly discounts for BNB listings based on local traffic volume.
|
9 |
|
10 |
+
# Author: Gordon Li (20317033)
|
11 |
+
# Date: March 2025
|
12 |
|
|
|
13 |
import folium
|
14 |
import oracledb
|
15 |
import logging
|
|
|
29 |
|
30 |
|
31 |
class TDTrafficSpot:
|
32 |
+
# Initializes a traffic spot with location and historical traffic data.
|
|
|
33 |
|
34 |
+
# Parameters:
|
35 |
+
# key: Unique identifier for the traffic spot
|
36 |
+
# latitude: Geographic latitude of the traffic spot
|
37 |
+
# longitude: Geographic longitude of the traffic spot
|
38 |
+
# dataset_rows: List of dictionaries containing historical traffic observations (default: None)
|
|
|
39 |
|
40 |
def __init__(self, key, latitude, longitude, dataset_rows=None):
|
41 |
self.key = key
|
|
|
45 |
self.avg_vehicle_count = self.calculate_avg_vehicle_count()
|
46 |
self.recent_display_rows = self.get_recent_display_rows()
|
47 |
|
48 |
+
# Checks if the traffic spot has valid geographic coordinates.
|
|
|
49 |
|
50 |
+
# Returns:
|
51 |
+
# Boolean indicating whether latitude and longitude are valid
|
|
|
52 |
|
53 |
def is_valid(self):
|
54 |
return self.latitude is not None and self.longitude is not None
|
55 |
|
56 |
+
# Gets the most recent traffic observations for display purposes.
|
|
|
57 |
|
58 |
+
# Parameters:
|
59 |
+
# max_display: Maximum number of recent records to return (default: 2)
|
60 |
|
61 |
+
# Returns:
|
62 |
+
# List of the most recent traffic observation records
|
|
|
63 |
|
64 |
def get_recent_display_rows(self, max_display=2):
|
65 |
if not self.dataset_rows:
|
|
|
68 |
sorted_rows = sorted(self.dataset_rows, key=lambda x: x['capture_time'], reverse=True)
|
69 |
return sorted_rows[:max_display]
|
70 |
|
71 |
+
# Calculates the average vehicle count based on historical traffic observations.
|
|
|
72 |
|
73 |
+
# Returns:
|
74 |
+
# Float representing the average number of vehicles observed
|
|
|
75 |
|
76 |
def calculate_avg_vehicle_count(self):
|
77 |
if not self.dataset_rows:
|
|
|
84 |
|
85 |
return np.mean(vehicle_counts)
|
86 |
|
87 |
+
# Determines the discount rate based on average traffic volume.
|
|
|
88 |
|
89 |
+
# Returns:
|
90 |
+
# Float representing the discount rate (0.0 to 0.20)
|
|
|
91 |
|
92 |
def get_discount_rate(self):
|
93 |
if self.avg_vehicle_count < 2:
|
|
|
97 |
else:
|
98 |
return 0.0
|
99 |
|
100 |
+
# Generates a human-readable description of the traffic-based discount.
|
|
|
101 |
|
102 |
+
# Returns:
|
103 |
+
# String describing the discount, if any
|
|
|
104 |
|
105 |
def get_discount_info(self):
|
106 |
discount_rate = self.get_discount_rate()
|
|
|
110 |
|
111 |
return f"{int(discount_rate * 100)}% discount! Low traffic area"
|
112 |
|
113 |
+
# Creates HTML content for the traffic spot's popup on the map.
|
|
|
114 |
|
115 |
+
# Returns:
|
116 |
+
# HTML string for the Folium popup
|
|
|
117 |
|
118 |
def create_popup_content(self):
|
119 |
discount_info = self.get_discount_info()
|
|
|
161 |
html += "</div>"
|
162 |
return html
|
163 |
|
164 |
+
# Adds the traffic spot to a Folium map with appropriate styling.
|
|
|
165 |
|
166 |
+
# Parameters:
|
167 |
+
# folium_map: Folium map object to add the marker to
|
|
|
168 |
|
169 |
def add_to_map(self, folium_map):
|
170 |
if self.is_valid():
|
|
|
184 |
|
185 |
|
186 |
class TrafficSpotManager:
|
187 |
+
# Manages a collection of traffic spots, handling data loading and map integration.
|
|
|
|
|
188 |
|
189 |
+
# Initializes the manager with database connection parameters and loads initial traffic spots.
|
|
|
190 |
|
191 |
+
# Parameters:
|
192 |
+
# connection_params: Dictionary containing Oracle database connection parameters
|
|
|
193 |
|
194 |
def __init__(self, connection_params):
|
195 |
self.connection_params = connection_params
|
|
|
197 |
self.spot_dict = {}
|
198 |
self.load_limited_traffic_spots()
|
199 |
|
200 |
+
# Loads a limited number of traffic spots for initial display.
|
|
|
201 |
|
202 |
+
# Parameters:
|
203 |
+
# limit: Maximum number of traffic spots to load initially (default: 10)
|
|
|
204 |
|
205 |
def load_limited_traffic_spots(self, limit=10):
|
206 |
try:
|
|
|
262 |
self.traffic_spots = []
|
263 |
self.spot_dict = {}
|
264 |
|
265 |
+
# Loads specific traffic spots by their keys when needed.
|
|
|
266 |
|
267 |
+
# Parameters:
|
268 |
+
# keys: List of traffic spot keys to load
|
|
|
269 |
|
270 |
def load_specific_traffic_spots(self, keys):
|
271 |
needed_keys = [key for key in keys if key not in self.spot_dict]
|
|
|
315 |
except Exception as e:
|
316 |
logging.error(f"Error loading specific traffic spots: {str(e)}")
|
317 |
|
318 |
+
# Adds traffic spots to a Folium map.
|
|
|
319 |
|
320 |
+
# Parameters:
|
321 |
+
# folium_map: Folium map object to add markers to
|
322 |
+
# spot_keys: Optional list of specific spot keys to add (default: None, adds all spots)
|
|
|
323 |
|
324 |
def add_spots_to_map(self, folium_map, spot_keys=None):
|
325 |
if spot_keys is None:
|
|
|
330 |
if key in self.spot_dict:
|
331 |
self.spot_dict[key].add_to_map(folium_map)
|
332 |
|
333 |
+
# Retrieves a traffic spot by its key, loading it if necessary.
|
|
|
334 |
|
335 |
+
# Parameters:
|
336 |
+
# key: The unique identifier of the traffic spot
|
337 |
|
338 |
+
# Returns:
|
339 |
+
# TDTrafficSpot object or None if not found
|
|
|
340 |
|
341 |
def get_spot_by_key(self, key):
|
342 |
if key in self.spot_dict:
|