Hello Dear users, I am Abhimanyu Gusain and I have made this project with my team, Harshita Mehta and Prableen Kaur. Without them I won't be able to make it happen and can't thank them enough :(
There are some prerequisites :
- Qt framework (and that too Qt 6)
- Tkinter and Folium installed on the device
- CMake version 3.10 or higher (ideally 3.16+ for modern Qt integration).
- Any code editor ( I have used an IDE CLion )
- Python and C++ installed on your device
So this is the workflow how the code works, we have two main parts: the C++ Qt application and the Python script.
Part 1: The C++ Qt Application (MainWindow, MetroSystem)
A. Initialization and Data Loading (MainWindow::loadData, MetroSystem::loadMetroData)
main.cppstartsQApplicationand createsMainWindow.MainWindowConstructor:- Calls
setupUi()to create all the GUI elements (ComboBoxes, Button,QTextEditfor textual output). - Calls
loadData().
- Calls
MainWindow::loadData():- Determines the path to
metroFinalData.csv(first checking next to the executable, then a relative path). - Calls
metroSystem_.loadMetroData(filePath, errorMessage);.
- Determines the path to
MetroSystem::loadMetroData(filename, errorMsg):- Opens
metroFinalData.csv. - Reads the header line (and prints it if debugging
qDebugis active). - Loops through each data line in the CSV:
- Uses
std::stringstreamandgetline(ss, field, ',')to split the line into 10 expected string fields (FromStation, ToStation, Time, Dist, Cost, SegmentLine, FromLat, FromLon, ToLat, ToLon). trim()s whitespace from each field.- Validation: Checks if any of these critical trimmed fields are empty. If so, it prints a warning (if
qDebugactive) and skips the line. - Conversion & Error Handling (try-catch):
- Tries to convert
timeStr,costStr,distStr, and the four coordinate strings into their respective numeric types (int,double) usingstd::stoiandstd::stod. - If conversion fails (e.g., non-numeric characters), it catches the exception, prints a warning, and skips the line.
- Tries to convert
- Populating
stationCoordinates_:- For the
fromStationandtoStationof the current segment, if their coordinates aren't already in thestationCoordinates_map (std::unordered_map<std::string, QPointF>), it adds them.QPointF(longitude, latitude)is used.
- For the
- Populating
graph_:- Creates two
Edgeobjects (since the graph is undirected): one fromfromStationtotoStation, and one fromtoStationtofromStation. - The
Edgestores thetoStationname,timeVal,distanceVal,costVal, and crucially, thesegmentLine(from the 6th column of the CSV). - These
Edgeobjects are added to thegraph_(std::unordered_map<std::string, std::vector<Edge>>).
- Creates two
- Populating
stationNames_: AddsfromStationandtoStationto astd::setto keep a unique list of all station names.
- Uses
- After the loop, it checks if
graph_is empty. If it is (and lines were processed), it sets an error message and returnsfalse.
- Opens
- Back in
MainWindow::loadData():- If
metroSystem_.loadMetroDatawas successful, it callspopulateComboBoxes().
- If
MainWindow::populateComboBoxes():- Gets the sorted list of unique station names from
metroSystem_.getStationNames(). - Populates the
sourceComboBox_anddestinationComboBox_. - Enables UI controls.
- Gets the sorted list of unique station names from
B. User Interaction and Pathfinding (MainWindow::findPath)
- User Selects Source, Destination, Criteria and Clicks "Find Route".
MainWindow::findPath()is called:- The
outputOpacityEffect_for theQTextEditis set to 0 (transparent) in preparation for the fade-in animation. - Basic input validation (are source/destination selected?).
- Retrieves selected
sourceStdStr,destStdStr, andcriteriaChoice. - Handles Same Source/Destination: If source and destination are the same, sets a simple HTML message in
outputDisplay_and returns. - Calls
MetroSystemfor Pathfinding:- Based on
criteriaChoice, it calls one of:metroSystem_.findPathLeastStops(sourceStdStr, destStdStr)metroSystem_.findPathByCost(sourceStdStr, destStdStr)metroSystem_.findPathByTime(sourceStdStr, destStdStr)
- These
MetroSystemmethods internally use either BFS or Dijkstra.
- Based on
- The
- Inside
MetroSystem's Pathfinding (e.g.,dijkstra):- The algorithm explores the
graph_, usingEdge.timeorEdge.costas weights. - It builds up a
parentNodemap to reconstruct the path. - Path Reconstruction:
- If a path to
endis found, it traces back fromendtostartusingparentNode. - For each step (e.g., from
prevStationtocurrentStation):- It calls
findEdge(prevStation, currentStation)to get the specificEdgeobject that was traversed. - It creates a
PathSegmentobject containingcurrentStation's name, and theline,time, andcostfrom the foundEdge.
- It calls
- The start station is added as a
PathSegmentwithisFirstSegment = true. - The list of
PathSegmentobjects is reversed to be in the correct order (start to end) and returned toMainWindow.
- If a path to
- The algorithm explores the
- Back in
MainWindow::findPath()- Processing Path Results:- If
pathSegmentsis empty (no path found):- Sets an appropriate "No path found" HTML message in
outputDisplay_.
- Sets an appropriate "No path found" HTML message in
- If
pathSegmentsis NOT empty:- HTML Generation for Textual Output:
- Calculates totals (time, cost, stops, line changes) by iterating through
pathSegments. - Builds a rich HTML string (
htmlOutputContent) with:- Headers (Route from X to Y, Optimized for Z).
- Summary section.
- Step-by-step directions, using
PathSegment.stationName,PathSegment.lineTakenToReach(withgetLineColorfor styling),PathSegment.timeForSegment,PathSegment.costForSegment. It also logic to print "Board Line X" and "Change to Line Y".
- Calculates totals (time, cost, stops, line changes) by iterating through
- JSON Preparation for Python Script:
- Creates a
QJsonArray(pathForPythonJsonArray). - Iterates through
pathSegmentsagain. - For each
PathSegment.stationName, it looks up its coordinates (Longitude, Latitude) frommetroSystem_.getStationCoordinates(). - Creates a
QJsonObjectfor each station:{"name": "Station Name", "lat": latitude_value, "lng": longitude_value}. - Adds this
QJsonObjectto theQJsonArray. - Converts the
QJsonArrayinto a compact JSON string (jsonDataString).
- Creates a
- Launch Python Script (
QProcess):- Creates a
QStringList pythonArgs. - First argument: path to
map_generator.py(derived fromQCoreApplication::applicationDirPath()). - Second argument: the
jsonDataString. - Determines the Python executable name (
python3orpython). - Calls
pythonMapProcess->startDetached(pythonExecutable, pythonArgs);. This runs the Python script as a separate, independent process.
- Creates a
- HTML Generation for Textual Output:
- If
- Display Textual Output with Animation (
MainWindow::findPath):outputDisplay_->setHtml(htmlOutputContent);(sets the generated HTML, widget is still transparent).- A
QPropertyAnimationis created to animateoutputOpacityEffect_'sopacityproperty from 0.0 to 1.0, making the textual output fade in.
Part 2: The Python Script (map_generator.py)
- Launched by
QProcessfrom C++:- Receives the JSON string of path data as its first command-line argument (
sys.argv[1]).
- Receives the JSON string of path data as its first command-line argument (
- Argument Parsing:
json_arg = sys.argv[1]path_data = json.loads(json_arg): Parses the JSON string into a Python list of dictionaries.
- Map Generation (
folium):metro_map = folium.Map(...): Creates a map object, centered (e.g., on the first station or a default), using "OpenStreetMap" tiles by default (no API key needed for this).- Iterates through
path_data(list of station dictionaries):- For each station:
- Extracts
lat,lng, andname. folium.Marker(...).add_to(metro_map): Adds a marker for the station on the map with a popup/tooltip.- Collects
(lat, lng)into astation_pointslist.
- Extracts
- For each station:
- Draws Polyline:
- If
station_pointshas at least two points,folium.PolyLine(locations=station_points, ...).add_to(metro_map)draws a red line connecting all the station points in sequence.
- If
- Fits Bounds:
metro_map.fit_bounds(...)adjusts the map's zoom and center to ensure the entire drawn route is visible.
- Saving and Displaying HTML Map:
metro_map.save("generated_route_map.html"): Saves the Folium map object as an HTML file in the same directory as the Python script (which is the C++ build directory).webbrowser.open('file://' + os.path.realpath("generated_route_map.html")): Opens this newly created HTML file in the user's default web browser.
Flow Summary:
User Input (Qt) -> C++ Pathfinding -> PathSegments (C++) -> 1. HTML Generation (C++) -> Display in Qt QTextEdit (with animation) 2. JSON Generation (C++) -> Launch Python Script (QProcess) -> Python Script Receives JSON -> Folium Generates HTML Map -> Open HTML in Web Browser.
How to Run
Most of the GUI part is done with the help of AI, but still knowing the basics of Qt is must. To run this code you just have to download Qt application, make a new file using Cmake and move all the files where CMakeList exist. AND MOST IMPORTANTLY - Replace your CMakeList with mine !!!