Context-Aware Intelligent Travel Companion – Implementation Overview

This report summarizes the implementation of the Montreal Travel Companion web application and explains how it satisfies the functional, usability, and LLM-related requirements of the course project. The app is a mobile-first React + TypeScript single-page application that uses a Leaflet map, a centralized state store (Zustand), OpenAI’s web-search capable models, and a Firebase Realtime Database–backed login system.

1. Functional Requirements (25 points)

1.1 Detects user’s location and provides location-based recommendations (5 pts)

The application tracks two kinds of location:

In MapPane, the ClickToPin component listens for map click events via useMapEvents and updates the pinned coordinates; a notification (“Pinned location set”) is also triggered. The bottom-right floating “📍” button in geolocate() uses the browser’s navigator.geolocation.getCurrentPosition API to set the pin to the user’s actual GPS location and pushes a toast with the fixed coordinates.

In the main App component, the “active” context location is always activeLoc = pinned ?? location, i.e., the pin overrides GPS until cleared; this active location is passed into the recommender. The recommendation engine (recommend()) computes distances from this location using a Haversine helper distanceKm() and filters POIs within a configurable radius (3 km by default). The final recommendations are then shown both in the side “Top Picks Nearby” panel and as map markers.

1.2 Detects weather and provides weather-based recommendations (5 pts)

Weather is detected via an LLM-powered helper fetchWeatherFromLLM(), which calls OpenAI’s Responses API with the web_search tool to fetch current conditions for the user’s coordinates. The model is instructed to respond with strict JSON including a short condition string and temperatures in °C and °F. The function parses the JSON, normalizes the condition, infers missing values (e.g., converting °F to °C if needed), and maps it to the internal Weather enum via mapToWeatherEnum().

The App component calls fetchWeatherFromLLM() on mount and then every 30 minutes in a useEffect, updating the global weather and tempC fields in the store. If the call fails, a toast “Weather refresh failed, using last known data” is shown to the user.

The recommendation pipeline enforces weather-aware behavior: outdoor POIs are filtered out in weatherOK() whenever the weather is bad for being outside (rain, snow, or extreme heat). Thus, on a rainy evening, the app will bias toward indoor museums, cinemas, cafés, and restaurants instead of parks or viewpoints. The current weather and temperature are also summarized in the mobile “Dynamic Island” top bar, showing an emoji glyph (☀️, 🌧️, ❄️, etc.) plus the mapped condition and temperature.

1.3 Detects time of day and provides relevant recommendations (5 pts)

Time is stored globally as an ISO timestamp (timeISO) in the Zustand store and is initialized to the current datetime. The “Wizard-of-Oz” controls expose a datetime-local input for the instructor account, allowing simulated time-of-day testing. Because native datetime inputs expect local time strings, a helper toLocalDateTimeInput() converts the stored ISO UTC time into the correct YYYY-MM-DDTHH:mm format, and on change, the value is converted back to a canonical ISO string.

Time directly affects recommendations in two ways:

1.4 User preferences embedded; recommendations change based on preferences (5 pts)

User preferences are represented by a structured Preferences object in the global store, including:

Preferences are stored in and restored from localStorage, so they persist between sessions. The onboarding screen (OnboardPreferences) is shown after login to encourage users to configure their preferences using the shared Controls component; “Skip” and “Done” both set an onboarded flag and advance to the main map.

The Controls panel lets users:

The recommend() function filters POIs by:

This ensures that adjusting preferences (e.g., switching from “parks” to “museums”, or from “pizza” to “asian”) immediately changes the set of recommended POIs. Preference changes also trigger a notification “Preferences updated” through the store’s setPrefs method.

1.5 Sends notifications when recommendations are updated (5 pts)

Notifications are implemented centrally in the store as a list of { id, msg } items. The pushNotify() method appends a new item and automatically schedules its removal after a timeout. The main App component renders a “toast stack” in the bottom-right corner, with the newest notification at the bottom; clicking a toast dismisses it.

Recommendation updates trigger notifications in several ways:

2. Usability & Design (5 points)

The interface has been designed with Nielsen’s 10 Usability Heuristics in mind. Key mappings are summarized below (0.5 points per heuristic).

2.1 Visibility of system status

2.2 Match between system and the real world

2.3 User control and freedom

2.4 Consistency and standards

2.5 Error prevention

2.6 Recognition rather than recall

2.7 Flexibility and efficiency of use

2.8 Aesthetic and minimalist design

2.9 Help users recognize, diagnose, and recover from errors

2.10 Help and documentation

3. LLM Usage (5 points)

3.1 LLM usage inside the application

The project uses OpenAI models in two main runtime features:

(a) LLM-powered POI discovery and recommendations

The module useLLM.tsx defines askLLMJSON() and fetchPOIsFromLLM(). These functions:

MapPane uses fetchPOIsFromLLM() in two ways:

Dynamic POIs are deduplicated by ID so that static and AI-suggested markers do not clash, and the recommendation panel also deduplicates AI results by name.

(b) LLM-powered weather detection

fetchWeatherFromLLM() similarly uses web_search to query a reliable weather provider and returns structured JSON with condition, temperatures, and an optional natural-language summary. This summary can be logged for debugging and is used to validate that the model’s mapping makes sense.

3.2 LLM usage during development

Beyond runtime features, an LLM (ChatGPT) was used extensively as an assistive tool during development in the following ways:

3.3 Difficulties and how LLMs helped

Key difficulties and the role of LLMs in overcoming them include:

4. Conclusion

The implemented Montreal Travel Companion meets the functional requirements by automatically detecting location, weather, and time; incorporating detailed user preferences; and updating recommendations with clear notifications. A combination of a rule-based recommender and LLM-powered discovery allows the system to provide both reliable, locally constrained suggestions and flexible, web-enhanced ideas. The interface follows the 10 usability heuristics through clear feedback, real-world mapping, strong user control, and a mobile-first, minimalist design. Throughout the project, large language models were used not only at runtime (for weather and POI search) but also as a development partner to refine architecture, fix bugs, and iterate on the user experience.