added comments for javadocs

This commit is contained in:
Ksan 2025-09-08 03:58:04 +02:00
parent 5d39a70e5b
commit 28a07a76e6
18 changed files with 855 additions and 525 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
transport_data.json
ticket_counter.txt
docs/
todo
receipts/
target/

3
.idea/misc.xml generated
View File

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="JavadocGenerationManager">
<option name="OUTPUT_DIRECTORY" value="$PROJECT_DIR$/docs" />
</component>
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>

View File

@ -4,23 +4,53 @@ import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
/**
* The TransportDataGenerator class is responsible for generating and saving transportation data,
* including a map of cities, stations, and departure details (bus and train).
* The data is saved in a JSON file format.
*
* <p>This class can generate transportation data for a grid of cities, each with bus and train stations,
* and associated departure schedules. It also allows configuring the number of departures per station.
*/
public class TransportDataGenerator {
// Default grid size
private static final int SIZE = 10;
// Number of rows and columns in the generated map
private int n;
private int m;
// Default number of departures per station
private static final int DEPARTURES_DEFAULT = 3;
private static int DEPARTURES_PER_STATION = DEPARTURES_DEFAULT;
// Random number generator for data generation
private static final Random random = new Random();
/**
* Generates a new transportation map with the default number of departures per station (3).
* The generated data is saved to a JSON file.
*
* @param n the number of rows in the generated grid (map)
* @param m the number of columns in the generated grid (map)
*/
public static void generateNewMap(int n, int m) {
DEPARTURES_PER_STATION= DEPARTURES_DEFAULT;
DEPARTURES_PER_STATION = DEPARTURES_DEFAULT;
TransportDataGenerator generator = new TransportDataGenerator(n, m);
TransportData data = generator.generateData();
generator.saveToJson(data, "transport_data.json");
System.out.println("Podaci su generisani i sacuvani kao transport_data.json");
}
/**
* Generates a new transportation map with a specified number of departures per station.
* The generated data is saved to a JSON file.
*
* @param n the number of rows in the generated grid (map)
* @param m the number of columns in the generated grid (map)
* @param departures the number of departures per station
*/
public static void generateNewMap(int n, int m, int departures) {
TransportDataGenerator generator = new TransportDataGenerator(n, m);
DEPARTURES_PER_STATION = departures;
@ -28,44 +58,79 @@ public class TransportDataGenerator {
generator.saveToJson(data, "transport_data.json");
System.out.println("Podaci su generisani i sacuvani kao transport_data.json");
}
/**
* Default constructor for generating a default grid size (10x10).
*/
TransportDataGenerator() {
this.n = SIZE;
this.m = SIZE;
}
/**
* Constructor to specify grid size as n x n.
*
* @param n the number of rows and columns in the generated grid (map)
*/
TransportDataGenerator(int n) {
this.n = n;
this.m = n;
}
/**
* Constructor to specify both grid size n x m.
*
* @param n the number of rows in the generated grid (map)
* @param m the number of columns in the generated grid (map)
*/
TransportDataGenerator(int n, int m) {
this.n = n;
this.m = m;
}
// struktura podataka koja sadrzi sve trazene ulazne podatke
/**
* A data structure that contains all the generated transport data:
* - A map of cities
* - A list of stations (bus and train stations)
* - A list of departure schedules (bus and train)
*/
public static class TransportData {
public String[][] countryMap;
public List<Station> stations;
public List<Departure> departures;
}
/**
* Represents a station, which includes the city and the corresponding bus and train stations.
*/
public static class Station {
public String city;
public String busStation;
public String trainStation;
}
/**
* Represents a departure (bus or train) including:
* - The type of transport (bus or train)
* - Departure city and station
* - Destination city and station
* - Departure time, duration, price, and minimum transfer time
*/
public static class Departure {
public String type; // "autobus" ili "voz"
public String type; // "autobus" or "voz"
public String from;
public String to;
public String departureTime;
public int duration; // u minutama
public int duration; // in minutes
public int price;
public int minTransferTime; // vrijeme potrebno za transfer (u minutama)
public int minTransferTime; // time needed for transfer (in minutes)
}
/**
* Generates the full transportation data, including country map, stations, and departures.
*
* @return the generated transportation data
*/
public TransportData generateData() {
TransportData data = new TransportData();
data.countryMap = generateCountryMap();
@ -74,7 +139,11 @@ public class TransportDataGenerator {
return data;
}
// generisanje gradova (G_X_Y)
/**
* Generates the country map as a 2D array of city names in the format "G_X_Y".
*
* @return the generated country map
*/
private String[][] generateCountryMap() {
String[][] countryMap = new String[n][m];
for (int x = 0; x < n; x++) {
@ -85,7 +154,11 @@ public class TransportDataGenerator {
return countryMap;
}
// generisanje autobuskih i zeljeznickih stanica
/**
* Generates the list of stations (bus and train) for each city in the grid.
*
* @return the list of generated stations
*/
private List<Station> generateStations() {
List<Station> stations = new ArrayList<>();
for (int x = 0; x < n; x++) {
@ -100,7 +173,12 @@ public class TransportDataGenerator {
return stations;
}
// generisanje vremena polazaka
/**
* Generates a list of departures (bus and train) for each station.
*
* @param stations the list of stations to generate departures for
* @return the list of generated departures
*/
private List<Departure> generateDepartures(List<Station> stations) {
List<Departure> departures = new ArrayList<>();
@ -108,12 +186,12 @@ public class TransportDataGenerator {
int x = Integer.parseInt(station.city.split("_")[1]);
int y = Integer.parseInt(station.city.split("_")[2]);
// generisanje polazaka autobusa
// Generate bus departures
for (int i = 0; i < DEPARTURES_PER_STATION; i++) {
departures.add(generateDeparture("autobus", station.busStation, x, y));
}
// generisanje polazaka vozova
// Generate train departures
for (int i = 0; i < DEPARTURES_PER_STATION; i++) {
departures.add(generateDeparture("voz", station.trainStation, x, y));
}
@ -121,31 +199,48 @@ public class TransportDataGenerator {
return departures;
}
/**
* Generates a single departure (bus or train) with random data.
*
* @param type the type of transport (bus or train)
* @param from the departure station
* @param x the city x-coordinate
* @param y the city y-coordinate
* @return the generated departure
*/
private Departure generateDeparture(String type, String from, int x, int y) {
Departure departure = new Departure();
departure.type = type;
departure.from = from;
// generisanje susjeda
// Generate neighboring cities
List<String> neighbors = getNeighbors(x, y);
departure.to = neighbors.isEmpty() ? from : neighbors.get(random.nextInt(neighbors.size()));
// generisanje vremena
// Generate departure time
int hour = random.nextInt(24);
int minute = random.nextInt(4) * 15; // 0, 15, 30, 45
departure.departureTime = String.format("%02d:%02d", hour, minute);
// geneirsanje cijene
// Generate duration (in minutes)
departure.duration = 30 + random.nextInt(151);
// Generate price (in currency units)
departure.price = 100 + random.nextInt(901);
// generisanje vremena transfera
// Generate minimum transfer time (in minutes)
departure.minTransferTime = 5 + random.nextInt(26);
return departure;
}
// pronalazak susjednih gradova
/**
* Finds neighboring cities based on the current city coordinates (x, y).
*
* @param x the current city x-coordinate
* @param y the current city y-coordinate
* @return the list of neighboring city names
*/
private List<String> getNeighbors(int x, int y) {
List<String> neighbors = new ArrayList<>();
int[][] directions = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
@ -160,13 +255,18 @@ public class TransportDataGenerator {
return neighbors;
}
// cuvanje podataka u JSON mapu
/**
* Saves the generated transportation data to a JSON file.
*
* @param data the generated transport data
* @param filename the name of the file to save the data to
*/
private void saveToJson(TransportData data, String filename) {
try (FileWriter file = new FileWriter(filename)) {
StringBuilder json = new StringBuilder();
json.append("{\n");
// mapa drzave
// Add country map
json.append(" \"countryMap\": [\n");
for (int i = 0; i < n; i++) {
json.append(" [");
@ -182,42 +282,42 @@ public class TransportDataGenerator {
}
json.append(" ],\n");
// stanice
// Add stations
json.append(" \"stations\": [\n");
for (int i = 0; i < data.stations.size(); i++) {
Station s = data.stations.get(i);
json.append(" {\"city\": \"")
.append(s.city)
.append("\", \"busStation\": \"")
.append(s.busStation)
.append("\", \"trainStation\": \"")
.append(s.trainStation)
.append("\"}");
.append(s.city)
.append("\", \"busStation\": \"")
.append(s.busStation)
.append("\", \"trainStation\": \"")
.append(s.trainStation)
.append("\"}");
if (i < data.stations.size() - 1)
json.append(",");
json.append("\n");
}
json.append(" ],\n");
// vremena polazaka
// Add departures
json.append(" \"departures\": [\n");
for (int i = 0; i < data.departures.size(); i++) {
Departure d = data.departures.get(i);
json.append(" {\"type\": \"")
.append(d.type)
.append("\", \"from\": \"")
.append(d.from)
.append("\", \"to\": \"")
.append(d.to)
.append("\", \"departureTime\": \"")
.append(d.departureTime)
.append("\", \"duration\": ")
.append(d.duration)
.append(", \"price\": ")
.append(d.price)
.append(", \"minTransferTime\": ")
.append(d.minTransferTime)
.append("}");
.append(d.type)
.append("\", \"from\": \"")
.append(d.from)
.append("\", \"to\": \"")
.append(d.to)
.append("\", \"departureTime\": \"")
.append(d.departureTime)
.append("\", \"duration\": ")
.append(d.duration)
.append(", \"price\": ")
.append(d.price)
.append(", \"minTransferTime\": ")
.append(d.minTransferTime)
.append("}");
if (i < data.departures.size() - 1)
json.append(",");
json.append("\n");
@ -230,4 +330,4 @@ public class TransportDataGenerator {
e.printStackTrace();
}
}
}
}

View File

@ -8,27 +8,54 @@ import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
/**
* The TravelPathOptimizerApplication class is the entry point for the JavaFX application
* that provides a user interface for optimizing travel paths.
* It loads the main user interface (FXML) and sets up the application window.
*/
public class TravelPathOptimizerApplication extends Application {
/**
* The start method is called when the application is launched.
* It sets up the main window of the application and loads the necessary resources.
*
* @param stage the primary stage for this application, onto which the scene will be set
* @throws IOException if an error occurs during FXML loading or resource fetching
*/
@Override
public void start(Stage stage) throws IOException {
// Load and initialize the ticket counter (profit and tickets sold)
TicketPrinter.loadCounter();
System.out.println(TicketPrinter.getTotalProfit() + " " + TicketPrinter.getTicketsSoldNum());
FXMLLoader fxmlLoader =
new FXMLLoader(TravelPathOptimizerApplication.class.getResource("main.fxml"));
// Load the FXML layout for the main interface
FXMLLoader fxmlLoader = new FXMLLoader(TravelPathOptimizerApplication.class.getResource("main.fxml"));
// Load the application's CSS stylesheet
String css = this.getClass().getResource("application.css").toExternalForm();
// Create the main scene with the loaded FXML layout
Scene scene = new Scene(fxmlLoader.load());
// Set the application's window icon
Image image = new Image(getClass().getResourceAsStream("/images/img1.jpg"));
scene.getStylesheets().add(css);
stage.getIcons().add(image);
scene.getStylesheets().add(css); // Add the CSS stylesheet to the scene
stage.getIcons().add(image); // Set the application icon
// Set the window title
stage.setTitle("Hello!");
// Set the scene for the stage (window) and show the stage
stage.setScene(scene);
stage.show();
}
/**
* The main method serves as the entry point to launch the JavaFX application.
* It invokes the launch() method to start the JavaFX runtime.
*
* @param args the command line arguments
*/
public static void main(String[] args) {
launch();
}

View File

@ -41,26 +41,24 @@ import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.graphstream.graph.Graph;
import org.graphstream.graph.implementations.MultiGraph;
/**
* The MainController class is the controller for the JavaFX application
* that handles the transportation system, allowing users to select start
* and end cities, view paths, buy tickets, and interact with a grid map of cities.
* It handles pathfinding, data loading, graph visualization, and ticket purchasing.
*/
public class MainController {
// UI Elements (FXML)
@FXML private HBox graphPane;
@FXML private GridPane map;
@FXML private Label welcomeText;
@FXML private Text selectedFileText;
@FXML private Button mapSelectButton;
private City startCity = null;
private City endCity = null;
private boolean selectingStart = false;
private boolean selectingEnd = false;
@FXML private TextField startCityText;
@FXML private TextField endCityText;
@FXML private Button startCityButton;
@FXML private Button endCityButton;
private HashMap<Integer, Departure> departuresMap = new HashMap<>();
private GraphSimulation graphSimulation;
private City[][] cities;
private File selectedFile;
@FXML private Button openFileButton;
@FXML private TextField nTextField;
@FXML private TextField mTextField;
@ -69,8 +67,6 @@ public class MainController {
@FXML private VBox mapSideBar;
@FXML private ChoiceBox categoryBox;
@FXML private Button startButton;
// routeView
@FXML private HBox routeView;
@FXML private Text totalTicketPriceText;
@FXML private Button buyButton;
@ -81,10 +77,23 @@ public class MainController {
@FXML private TableColumn<Departure, Double> tabCostCol;
@FXML private ChoiceBox<String> pathChoiceBox;
// Private instance variables
private City startCity = null;
private City endCity = null;
private boolean selectingStart = false;
private boolean selectingEnd = false;
private HashMap<Integer, Departure> departuresMap = new HashMap<>();
private GraphSimulation graphSimulation;
private City[][] cities;
private File selectedFile;
private Graph graph = new MultiGraph("map");
private GraphVisualizer visualizer;
private List<Departure> tempDepartureList = new ArrayList<>();
/**
* Toggles the visibility of the map and graph pane. If the map is visible, it hides the map
* and shows the graph pane, and vice versa. The method also adds the city nodes to the graph.
*/
@FXML
void showGraph() {
graph.clear();
@ -154,7 +163,10 @@ public class MainController {
visualizer.showGraph();
visualizer.highlightPath(tempDepartureList);
}
/**
* Initiates the ticket purchasing process by generating a ticket receipt based on the selected
* departures and the total cost. The receipt is printed using the TicketPrinter.
*/
@FXML
private void buyTicket() {
@ -162,7 +174,11 @@ public class MainController {
printer.generateTicketReceipt(
updateUiGetList(), Double.parseDouble(totalTicketPriceText.getText()));
}
/**
* Calculates and displays the top paths between the selected start and end cities based on the
* selected category (time, price, or hops). This method is run in a background task to avoid
* blocking the UI. Once the paths are calculated, they are displayed in the UI.
*/
@FXML
private void findTopPaths() {
graphSimulation.reset();
@ -236,7 +252,13 @@ public class MainController {
new Thread(task).start();
}
/**
* Updates the UI with the selected list of departures based on the current path selection.
* It updates the route details, including departure times, arrival times, and prices, and
* calculates the total ticket price.
*
* @return An observable list of departures.
*/
private synchronized ObservableList<Departure> updateUiGetList() {
ObservableList<Departure> departureList = FXCollections.observableArrayList();
@ -283,28 +305,37 @@ public class MainController {
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to JavaFX Application!");
}
/**
* Toggles the visibility of the "Route View" section, which shows the available paths and their details.
*/
@FXML
private void showRouteView() {
boolean visible = routeView.isVisible();
routeView.setVisible(!visible);
routeView.setManaged(!visible);
}
/**
* Toggles the visibility of the random sidebar, tbh forgot this part.
*/
@FXML
void showRandomSideBar() {
boolean visible = randomSideBar.isVisible();
randomSideBar.setVisible(!visible);
randomSideBar.setManaged(!visible);
}
/**
* Toggles the visibility of the map sidebar, which contains options for selecting a map or file.
*/
@FXML
void showMapSideBar() {
boolean visible = mapSideBar.isVisible();
mapSideBar.setVisible(!visible);
mapSideBar.setManaged(!visible);
}
/**
* Updates the map layout by re-generating the grid cells based on the current city locations.
* This method also updates the appearance of the grid to highlight the selected start and end cities.
*/
private void updateMap() {
map.getChildren().clear();
map.getRowConstraints().clear();
@ -378,21 +409,30 @@ public class MainController {
}
}
}
/**
* Sets the start city for the pathfinding. The user can select a city from the map grid to
* set it as the start city.
*/
@FXML
private void selectStart() {
this.selectingEnd = false;
this.selectingStart = true;
updateMap();
}
/**
* Sets the end city for the pathfinding. The user can select a city from the map grid to
* set it as the end city.
*/
@FXML
private void selectEnd() {
this.selectingStart = false;
this.selectingEnd = true;
updateMap();
}
/**
* Initializes the controller by setting up the UI components, event listeners, and default values.
* This method is called after the FXML file is loaded.
*/
@FXML
public void initialize() {
visualizer = new GraphVisualizer(graph, graphPane);
@ -506,7 +546,13 @@ public class MainController {
updateMap(); // Re-draw with highlight if matched
});
}
/**
* Calculates the total cost for the list of selected departures. The total cost is the sum of
* the prices of all selected departures.
*
* @param depList The list of selected departures.
* @return The total cost of the selected departures.
*/
private double calculateTotalCost(ObservableList<Departure> depList) {
double totalCost = 0.0;
for (Departure dep : depList) {
@ -514,7 +560,10 @@ public class MainController {
}
return totalCost;
}
/**
* Generates a new map based on user input for the number of rows, columns, and departures.
* If the departure field is empty, it generates a map with default number of departures.
*/
@FXML
void generateNewMap() {
if (!nTextField.getText().isEmpty() && !mTextField.getText().isEmpty()) {
@ -532,7 +581,10 @@ public class MainController {
}
}
}
/**
* Loads the transportation data from a JSON file, parses the cities, departures, and map grid,
* and updates the internal data structures with the loaded information.
*/
private void getData() {
CityManager.clear();
List<City> cities = JsonParser.parseCities("transport_data.json", "stations");
@ -557,7 +609,10 @@ public class MainController {
this.graphSimulation = new GraphSimulation(map);
}
/**
* Opens a file chooser dialog to allow the user to select a map file (in JSON format).
* Once the file is selected, it loads the data and updates the map display.
*/
@FXML
void loadMapFromFile() {
FileChooser fileChooser = new FileChooser();

View File

@ -9,81 +9,34 @@ import java.time.Duration;
import java.time.LocalTime;
import java.util.*;
/**
* Simulates pathfinding in a transportation network, managing paths between cities
* and calculating top paths based on different cost metrics (e.g., time, price, hops).
*/
public class GraphSimulation {
private City[][] matrix;
private List<PathResult> allPaths = new ArrayList<>();
private int pathIdCounter = 1;
private static int nextid = 0;
private static Set<String> visitedRoutes = new HashSet<>();
public static PriorityQueue<PathResult> topPaths =
new PriorityQueue<>(5, Comparator.comparingDouble(PathResult::getCost).reversed());
/*
public static void main(String[] args) {
List<City> cities = JsonParser.parseCities("transport_data.json", "stations");
List<Departure> departures = JsonParser.getDeparturesList("transport_data.json", "departures");
for (Departure dep : departures) {
for (City city : cities) {
if (dep.getTo().equals(city.getName())) {
dep.setToCity(city);
}
}
}
cities = JsonParser.loadDepartures(cities, departures);
City[][] map = JsonParser.loadMap("transport_data.json", "countryMap", cities);
new PriorityQueue<>(5, Comparator.comparingDouble(PathResult::getCost).reversed());
GraphSimulation graphSimulation = new GraphSimulation(map);
cities = JsonParser.loadDepartures(cities, departures);
for (City city : cities) {
CityManager.addCity(city);
}
System.out.println(cities.getFirst().getName() + " do " + cities.getLast().getName());
Map<Location, Double> result =
graphSimulation.calculateShortestPath(cities.getFirst(), cities.getLast(), "time");
System.out.println(
cities.get(1).getName() + " = " + result.get(cities.getLast().getLocation()));
/*
graph.calculateTopPaths(cities.getFirst(), cities.getLast());
List<PathResult> sorted = new ArrayList<>(graph.topPaths);
sorted.sort(Comparator.comparingDouble(PathResult::getCost));
for (PathResult res : sorted) {
System.out.println("Path ID: " + res.getId());
System.out.print("Cities: ");
for (City city : res.getPath()) {
System.out.print(city.getName() + "");
}
System.out.println("End");
System.out.println("Departures:");
System.out.println("Total Cost: " + res.getCost() + " min");
System.out.println("Arrival Time: " + res.getArrivalTime());
System.out.println("--------------------------------------------------");
}
*\/
List<City> path = new ArrayList<>();
List<Integer> departuress = new ArrayList<>();
LocalTime currentTime = LocalTime.of(1, 0);
double totalCost = 0.0;
calculateTopPaths(
cities.getFirst(), cities.getLast(), path, totalCost, currentTime, departuress, "time");
System.out.println(topPaths.size());
// Output the top 5 paths
printTopPaths();
}
*/
/**
* Gets the list of top 5 paths sorted by cost in ascending order.
*
* @return a list of the top 5 path results sorted by cost.
*/
public List<PathResult> getSortedPaths() {
List<PathResult> pathList = new ArrayList<>(topPaths);
pathList.sort(Comparator.comparingDouble(PathResult::getCost));
return pathList;
}
/**
* Resets the simulation, clearing all paths, visited routes, and top paths.
*/
public void reset() {
topPaths.clear();
pathIdCounter = 1;
@ -92,6 +45,12 @@ public class GraphSimulation {
visitedRoutes.clear();
}
/**
* Adds a new path result to the top paths if it has a lower cost than the current top paths.
* Ensures that no duplicate routes are added.
*
* @param newPath the new path result to be added.
*/
public static void addToTopPaths(PathResult newPath) {
String routeHash = generateRouteHash(newPath.getDeparturesUsed());
@ -112,6 +71,12 @@ public class GraphSimulation {
}
}
/**
* Generates a unique hash for a route based on the departure IDs used in the path.
*
* @param departures the list of departure IDs used in the path.
* @return a string hash representing the route.
*/
private static String generateRouteHash(List<Integer> departures) {
StringBuilder sb = new StringBuilder();
for (Integer depId : departures) {
@ -120,24 +85,35 @@ public class GraphSimulation {
return sb.toString();
}
/**
* Recursively calculates the top paths from a starting city to an ending city, considering
* the type of cost metric (e.g., time, price, or hops).
*
* @param currentCity the city where the journey is currently at.
* @param endCity the destination city of the journey.
* @param path the list of cities visited so far.
* @param totalCost the total cost accumulated up to the current city.
* @param currentTime the current time at the city.
* @param departures the list of departure IDs taken so far.
* @param type the type of cost metric to use ("time", "price", or "hops").
*/
public static void calculateTopPaths(
City currentCity,
City endCity,
List<City> path,
double totalCost,
LocalTime currentTime,
List<Integer> departures,
String type) {
City currentCity,
City endCity,
List<City> path,
double totalCost,
LocalTime currentTime,
List<Integer> departures,
String type) {
if (currentCity.getLocation().equals(endCity.getLocation())) {
addToTopPaths(
new PathResult(
nextid++,
new ArrayList<>(path),
new ArrayList<>(departures),
totalCost,
currentTime));
new PathResult(
nextid++,
new ArrayList<>(path),
new ArrayList<>(departures),
totalCost,
currentTime));
return;
}
@ -155,7 +131,6 @@ public class GraphSimulation {
duration = duration.abs();
if (type.equals("time")) {
// cost += dep.getDuration();
cost += duration.toMinutes();
cost += dep.getMinTransferTime();
} else if (type.equals("price")) {
@ -177,12 +152,14 @@ public class GraphSimulation {
departures.remove(departures.size() - 1);
path.remove(path.size() - 1);
if (type.equals("hops")) {
cost--;
}
}
}
/**
* Prints the top 5 paths to the console.
*/
public static void printTopPaths() {
if (topPaths.isEmpty()) {
System.out.println("No Paths");
@ -201,137 +178,14 @@ public class GraphSimulation {
}
}
/*
private PriorityQueue<PathResult> topPaths =
new PriorityQueue<>(Comparator.comparingDouble(PathResult::getCost).reversed());
private final AtomicInteger pathIdGenerator = new AtomicInteger(1); // Start from 1
private final Set<String> uniquePathKeys = new HashSet<>();
private int makeDeparturePathHash(Set<Departure> departures) {
return departures.stream()
.mapToInt(
dep ->
Objects.hash(
dep.getDepartureTime()
.toSecondOfDay(),
dep.getDestinationCity()
.getLocation(),
Double.valueOf(dep.getDuration()).hashCode()))
.sorted()
.reduce(1, (a, b) -> 31 * a + b);
}
private HashSet<Integer> uniquePathHashes = new HashSet<Integer>();
public void calculateTopPaths(City start, City end) {
topPaths.clear();
uniquePathKeys.clear();
pathIdGenerator.set(1);
for (Departure dep : start.getDestinations()) {
City next = dep.getDestinationCity();
double duration = dep.getDuration();
LocalTime depTime = dep.getDepartureTime();
LocalTime arrivalTime = depTime.plusMinutes((long) duration);
List<City> initialPath = new ArrayList<>();
Set<Departure> usedDepartures = new HashSet<>();
usedDepartures.add(dep);
initialPath.add(start);
initialPath.add(next);
List<Integer> initialDepartures = new ArrayList<>();
initialDepartures.add(dep.getIdCounter());
Set<Location> visited = new HashSet<>();
visited.add(start.getLocation());
findPaths(
next,
end,
initialPath,
usedDepartures,
initialDepartures,
duration,
arrivalTime,
visited);
}
}
public void findPaths(
City current,
City end,
List<City> pathSoFar,
Set<Departure> usedDepartures,
List<Integer> departuresSoFar,
double totalCost,
LocalTime currentTime,
Set<Location> visitedCities
) {
if (visitedCities.contains(current.getLocation())) return;
visitedCities.add(current.getLocation());
if (current.getLocation().equals(end.getLocation())) {
int pathHash = makeDeparturePathHash(usedDepartures);
if (uniquePathHashes.contains(pathHash)) {
visitedCities.remove(current.getLocation());
return;
}
uniquePathHashes.add(pathHash);
int pathId = pathIdGenerator.getAndIncrement();
topPaths.add(
new PathResult(
pathId,
new ArrayList<>(pathSoFar),
new ArrayList<>(departuresSoFar),
totalCost,
currentTime));
if (topPaths.size() > 5) topPaths.poll();
visitedCities.remove(current.getLocation()); // backtrack
return;
}
for (Departure dep : current.getDestinations()) {
if (usedDepartures.contains(dep)) continue;
if (dep.getDepartureTime().isBefore(currentTime)) continue;
City next = dep.getDestinationCity();
if (visitedCities.contains(next.getLocation())) continue; // skip if already visited
LocalTime arrivalTime = dep.getDepartureTime().plusMinutes((long) dep.getDuration());
usedDepartures.add(dep);
departuresSoFar.add(dep.getIdCounter());
pathSoFar.add(next);
if (!(topPaths.peek() == null)) {
if (totalCost + dep.getDuration() > topPaths.peek().getCost()) {
continue;
}
}
findPaths(
next,
end,
pathSoFar,
usedDepartures,
departuresSoFar,
totalCost + dep.getDuration(),
arrivalTime,
visitedCities);
// backtrack
pathSoFar.remove(pathSoFar.size() - 1);
departuresSoFar.remove(departuresSoFar.size() - 1);
usedDepartures.remove(dep);
}
visitedCities.remove(current.getLocation()); // backtrack
}
*/
/**
* Calculates the shortest path between two cities using a specified cost metric (e.g., price, time, hops).
*
* @param startCity the starting city of the path.
* @param endCity the destination city of the path.
* @param type the type of cost metric to use ("price", "time", or "hops").
* @return a map where the keys are city locations and the values are the shortest cost to that location.
*/
public Map<Location, Double> calculateShortestPath(City startCity, City endCity, String type) {
int n = matrix.length;
int m = matrix[0].length;
@ -346,7 +200,7 @@ public class GraphSimulation {
distances.put(startCity.getLocation(), 0.0);
PriorityQueue<City> pq =
new PriorityQueue<>(Comparator.comparingDouble(city -> distances.get(city.getLocation())));
new PriorityQueue<>(Comparator.comparingDouble(city -> distances.get(city.getLocation())));
pq.add(startCity);
while (!pq.isEmpty()) {
@ -380,27 +234,58 @@ public class GraphSimulation {
return distances;
}
/**
* Gets the list of all paths found during the simulation.
*
* @return a list of all PathResult objects representing the paths.
*/
public List<PathResult> getAllPaths() {
return allPaths;
}
/**
* Constructs a GraphSimulation object with the given matrix of cities.
*
* @param matrix a 2D array representing the map of cities.
*/
public GraphSimulation(City[][] matrix) {
this.matrix = matrix;
}
/**
* Updates the city matrix used in the simulation.
*
* @param matrix the new 2D array of cities.
*/
public void updateMatrix(City[][] matrix) {
this.matrix = matrix;
}
/**
* Gets the current matrix of cities used in the simulation.
*
* @return the current 2D array of cities.
*/
public City[][] getMatrix() {
return matrix;
}
/**
* Gets the city at a specific location in the matrix.
*
* @param loc the location of the city.
* @return the city at the specified location.
*/
public City getCity(Location loc) {
return matrix[loc.getX()][loc.getY()];
}
/**
* Gets the priority queue of top paths, sorted by cost in descending order.
*
* @return the priority queue of top paths.
*/
public static PriorityQueue<PathResult> getTopPaths() {
return topPaths;
}
}
}

View File

@ -6,13 +6,28 @@ import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
/**
* Represents the result of a path calculation, including the path (list of cities),
* departures used, total cost, and arrival time for the journey.
*/
public class PathResult {
private int id;
private int id;
private List<City> path = new ArrayList<>();
private List<Integer> departuresUsed = new ArrayList<>();
private double cost;
private LocalTime arrivalTime;
private double cost;
private LocalTime arrivalTime;
/**
* Constructs a PathResult object with the specified id, path, departures, cost,
* and arrival time.
*
* @param id the unique identifier for the path result
* @param path the list of cities in the path
* @param departuresUsed the list of departures used in the path
* @param cost the total cost of the journey
* @param arrivalTime the time of arrival at the destination
*/
public PathResult(int id, List<City> path, List<Integer> departuresUsed, double cost, LocalTime arrivalTime) {
this.id = id;
this.path = path;
@ -21,25 +36,57 @@ public class PathResult {
this.arrivalTime = arrivalTime;
}
/**
* Gets the unique identifier for the path result.
*
* @return the unique identifier of the path result.
*/
public int getId() {
return id;
}
/**
* Gets the list of cities in the path.
*
* @return a list of City objects representing the journey's path.
*/
public List<City> getPath() {
return path;
}
/**
* Gets the list of departures used during the journey.
*
* @return a list of integers representing the departure IDs used.
*/
public List<Integer> getDeparturesUsed() {
return departuresUsed;
}
/**
* Gets the total cost of the journey represented by this path result.
*
* @return the total cost of the journey.
*/
public double getCost() {
return cost;
}
/**
* Gets the arrival time at the destination.
*
* @return a LocalTime object representing the arrival time.
*/
public LocalTime getArrivalTime() {
return arrivalTime;
}
/**
* Returns a string representation of the PathResult object, including the id,
* total cost, path, and arrival time.
*
* @return a string representation of the PathResult object.
*/
@Override
public String toString() {
return "PathResult{id=" + id + " cost = " + cost + ", path=" + path + ", arrivalTime=" + arrivalTime + '}';

View File

@ -1,62 +0,0 @@
package dev.ksan.travelpathoptimizer.graphSimulation;
import dev.ksan.travelpathoptimizer.model.City;
import dev.ksan.travelpathoptimizer.model.Departure;
import java.util.List;
public class PathState {
private City city;
private List<City> path;
private List<Departure> departures;
double cost;
public PathState(City city, List<City> path, List<Departure> departures, double cost) {
this.city = city;
this.path = path;
this.departures = departures;
this.cost = cost;
}
public String getPathSignature() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < path.size(); i++) {
sb.append(path.get(i).getLocation().toString());
if (i < path.size() - 1) {
sb.append("->");
}
}
return sb.toString();
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
public List<City> getPath() {
return path;
}
public void setPath(List<City> path) {
this.path = path;
}
public List<Departure> getDepartures() {
return departures;
}
public void setDepartures(List<Departure> departures) {
this.departures = departures;
}
public double getCost() {
return cost;
}
public void setCost(double cost) {
this.cost = cost;
}
}

View File

@ -3,12 +3,27 @@ package dev.ksan.travelpathoptimizer.model;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a city with associated transportation stations (train and bus)
* and its location.
*/
public class City {
private String name;
private Station trainStation;
private Station busStation;
private Location location;
/**
* Constructs a City object with a specified name, bus station, train station,
* and location.
*
* @param name the name of the city
* @param bus the name of the bus station
* @param train the name of the train station
* @param row the row coordinate for the city's location
* @param col the column coordinate for the city's location
*/
public City(String name, String bus, String train, int row, int col) {
this.name = name;
this.trainStation = new Station(TransportType.TRAIN, train);
@ -16,6 +31,16 @@ public class City {
this.location = new Location(row, col);
}
/**
* Constructs a City object with a specified name, pre-existing bus and train stations,
* and location.
*
* @param name the name of the city
* @param bus the bus station object
* @param train the train station object
* @param row the row coordinate for the city's location
* @param col the column coordinate for the city's location
*/
public City(String name, Station bus, Station train, int row, int col) {
this.name = name;
this.trainStation = train;
@ -23,6 +48,12 @@ public class City {
this.location = new Location(row, col);
}
/**
* Gets a list of all departures (destinations) from both the bus and train stations
* in this city.
*
* @return a list of Departure objects representing the destinations from this city.
*/
public List<Departure> getDestinations() {
List<Departure> departures = new ArrayList<>();
@ -36,34 +67,75 @@ public class City {
return departures;
}
/**
* Gets the name of the city.
*
* @return the name of the city.
*/
public String getName() {
return name;
}
/**
* Sets the name of the city.
*
* @param name the name to set for the city.
*/
public void setName(String name) {
this.name = name;
}
/**
* Gets the train station of the city.
*
* @return the train station of the city.
*/
public Station getTrainStation() {
return trainStation;
}
/**
* Sets the train station for the city.
*
* @param trainStation the Station object to set as the city's train station.
*/
public void setTrainStation(Station trainStation) {
this.trainStation = trainStation;
}
/**
* Gets the bus station of the city.
*
* @return the bus station of the city.
*/
public Station getBusStation() {
return busStation;
}
/**
* Sets the bus station for the city.
*
* @param busStation the Station object to set as the city's bus station.
*/
public void setBusStation(Station busStation) {
this.busStation = busStation;
}
/**
* Gets the geographic location of the city.
*
* @return the Location object representing the city's location.
*/
public Location getLocation() {
return this.location;
}
/**
* Returns a string representation of the City object, including the city's name,
* and details of the train and bus stations.
*
* @return a string representation of the city.
*/
@Override
public String toString() {
return name + "\n\tTrain Station: " + trainStation + "\n\tBus Station: " + busStation;

View File

@ -18,13 +18,13 @@ public class Departure {
private static int idCounter = 0;
private int id;
public Departure(
TransportType type,
String from,
String to,
String departureTime,
int duration,
double price,
int minTransferTime) {
TransportType type,
String from,
String to,
String departureTime,
int duration,
double price,
int minTransferTime) {
this.id = idCounter++;
this.type = type;
this.from = from;
@ -80,22 +80,22 @@ public class Departure {
@Override
public String toString() {
return "Departure{"
+ "type="
+ type
+ ", from='"
+ from
+ '\''
+ ", to='"
+ to
+ '\''
+ ", departureTime="
+ departureTime.format(DateTimeFormatter.ofPattern("HH:mm"))
+ ", duration="
+ duration
+ ", price="
+ price
+ ", minTransferTime="
+ minTransferTime
+ "}";
+ "type="
+ type
+ ", from='"
+ from
+ '\''
+ ", to='"
+ to
+ '\''
+ ", departureTime="
+ departureTime.format(DateTimeFormatter.ofPattern("HH:mm"))
+ ", duration="
+ duration
+ ", price="
+ price
+ ", minTransferTime="
+ minTransferTime
+ "}";
}
}

View File

@ -2,32 +2,71 @@ package dev.ksan.travelpathoptimizer.model;
import java.util.Objects;
import java.util.Objects;
/**
* Represents a 2D location, defined by its x and y coordinates.
* Used to specify positions on a grid or map.
*/
public class Location {
private int x;
private int y;
/**
* Constructs a Location object with the specified x and y coordinates.
*
* @param x the x-coordinate of the location
* @param y the y-coordinate of the location
*/
public Location(int x, int y) {
this.x = x;
this.y = y;
}
/**
* Gets the x-coordinate of this location.
*
* @return the x-coordinate
*/
public int getX() {
return x;
}
/**
* Sets the x-coordinate of this location.
*
* @param x the new x-coordinate
*/
public void setX(int x) {
this.x = x;
}
/**
* Gets the y-coordinate of this location.
*
* @return the y-coordinate
*/
public int getY() {
return y;
}
/**
* Sets the y-coordinate of this location.
*
* @param y the new y-coordinate
*/
public void setY(int y) {
this.y = y;
}
/**
* Compares this location to another object for equality.
* Two locations are considered equal if their x and y coordinates are the same.
*
* @param o the object to compare this location to
* @return true if the object is a Location and has the same x and y coordinates, false otherwise
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -36,11 +75,22 @@ public class Location {
return x == location.x && y == location.y;
}
/**
* Returns a hash code value for this location.
* The hash code is computed based on the x and y coordinates.
*
* @return a hash code value for this location
*/
@Override
public int hashCode() {
return Objects.hash(x, y);
}
/**
* Returns a string representation of the location in the format "x_y".
*
* @return a string representation of the location
*/
@Override
public String toString() {
return x + "_" + y;

View File

@ -3,36 +3,77 @@ package dev.ksan.travelpathoptimizer.model;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a station, which can be of a specific transport type (e.g., bus, train),
* and contains a list of departures for that station.
*/
public class Station {
private TransportType type = TransportType.NOT_ASSIGNED;
private String name;
private List<Departure> departures = new ArrayList<Departure>();
private List<Departure> departures = new ArrayList<>();
/**
* Default constructor for a Station object. The transport type is set to NOT_ASSIGNED,
* and the departures list is initialized as empty.
*/
Station() {}
/**
* Constructs a Station object with the specified transport type and station name.
*
* @param type the type of transport (e.g., train, bus)
* @param name the name of the station
*/
Station(TransportType type, String name) {
this.type = type;
this.name = name;
}
/**
* Gets the transport type of this station (e.g., train, bus).
*
* @return the transport type of the station
*/
public TransportType getType() {
return type;
}
/**
* Gets the name of this station.
*
* @return the name of the station
*/
public String getName() {
return name;
}
/**
* Gets the list of departures from this station.
*
* @return a list of Departure objects associated with this station
*/
public List<Departure> getDepartures() {
return departures;
}
/**
* Adds a departure to the list of departures for this station.
*
* @param departure the departure to add to the list
*/
public void addDeparture(Departure departure) {
departures.add(departure);
}
/**
* Returns a string representation of this station, including its transport type, name, and departures.
*
* @return a string representation of the station
*/
@Override
public String toString() {
return "Station{" + "type=" + type + ", name=" + name + ", departures=" + departures;
return "Station{" + "type=" + type + ", name=" + name + ", departures=" + departures + "}";
}
}

View File

@ -1,16 +1,43 @@
package dev.ksan.travelpathoptimizer.model;
/**
* Enum representing different types of transport, such as BUS, TRAIN, and a default NOT_ASSIGNED type.
* Each transport type is associated with a string representation.
*/
public enum TransportType {
/**
* Represents an unassigned transport type. This is used as a placeholder.
*/
NOT_ASSIGNED("null"),
/**
* Represents a bus transport type.
*/
BUS("autobus"),
/**
* Represents a train transport type.
*/
TRAIN("voz");
private final String type;
/**
* Constructs a TransportType with the specified string representation.
*
* @param type the string representation of the transport type
*/
TransportType(String type) {
this.type = type;
}
/**
* Returns the string representation of the transport type.
* This is the string value that corresponds to the transport type (e.g., "autobus", "voz").
*
* @return the string representation of the transport type
*/
@Override
public String toString() {
return this.type;

View File

@ -1,50 +0,0 @@
package dev.ksan.travelpathoptimizer.model;
public class User {
private String name;
private int id;
private Location location;
private Location destination;
private static int idCounter = 1;
// :TODO:
private static final String ID_FILE = "id_counter.txt";
public User(String name) {
this.name = name;
this.id = idCounter++;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
public Location getDestination() {
return destination;
}
public void setDestination(Location destination) {
this.destination = destination;
}
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "'}";
}
}

View File

@ -5,25 +5,53 @@ import dev.ksan.travelpathoptimizer.model.Location;
import java.util.HashMap;
import java.util.Map;
public class CityManager {
private static Map<String, City> cities = new HashMap<>();
private static Map<Location, City> citiesByLocation = new HashMap<>();
/**
* The CityManager class manages a collection of cities in a travel optimization system.
* It allows adding cities, retrieving cities by name or location, and clearing the city data.
*/
public class CityManager {
private static Map<String, City> cities = new HashMap<>(); // Map to store cities by name
private static Map<Location, City> citiesByLocation = new HashMap<>(); // Map to store cities by location
/**
* Clears all stored cities from the manager.
* This method will remove all cities from both the name-based and location-based maps.
*/
public static void clear() {
citiesByLocation.clear();
cities.clear();
}
/**
* Adds a city to the city manager.
* The city will be stored in both the name-based and location-based maps.
*
* @param city the city to be added
*/
public static void addCity(City city) {
cities.put(city.getName(), city);
citiesByLocation.put(city.getLocation(), city);
cities.put(city.getName(), city); // Add city by name
citiesByLocation.put(city.getLocation(), city); // Add city by location
}
/**
* Retrieves a city by its location.
*
* @param loc the location of the city
* @return the city associated with the given location, or null if no city exists at that location
*/
public static City getCityByLocation(Location loc) {
return citiesByLocation.get(loc);
return citiesByLocation.get(loc); // Get city by location
}
/**
* Retrieves a city by its name.
*
* @param cityName the name of the city
* @return the city associated with the given name, or null if no city exists with that name
*/
public static City getCityByName(String cityName) {
return cities.get(cityName);
return cities.get(cityName); // Get city by name
}
}

View File

@ -12,19 +12,20 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A utility class for parsing JSON-like data from files and converting them into Java objects.
* This class is used to extract information from JSON files related to cities, departures, and transport maps.
*/
public class JsonParser {
/*
public static void main(String[] args) {
String[][] map = JsonParser.parseCountryMap("transport_data.json", "countryMap");
List<City> cities = JsonParser.parseCities("transport_data.json", "stations");
List<Departure> departures = JsonParser.getDeparturesList("transport_data.json", "departures");
cities = JsonParser.loadDepartures(cities, departures);
for (City city : cities) {
System.out.println(city);
}
}
*/
/**
* Retrieves the value associated with a given key from a JSON-like file.
* The value is expected to be an array, and the function extracts the array's content as a string.
*
* @param fileName the name of the file containing the JSON data
* @param key the key to search for in the JSON data
* @return the string value associated with the key, or {@code null} if the key is not found
*/
public static String getValue(String fileName, String key) {
StringBuilder json = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
@ -51,6 +52,14 @@ public class JsonParser {
return jsonString.substring(startIndex, endIndex);
}
/**
* Loads a map of cities from a JSON file, associating cities with their respective locations on the map.
*
* @param fileName the name of the file containing the city map data
* @param key the key used to identify the map data in the JSON file
* @param cities a list of cities to be mapped to their respective locations
* @return a 2D array representing the map of cities
*/
public static City[][] loadMap(String fileName, String key, List<City> cities) {
String[][] cityMap = parseCountryMap(fileName, key);
City[][] map = new City[cityMap.length][cityMap[0].length];
@ -62,6 +71,13 @@ public class JsonParser {
return map;
}
/**
* Parses a country map from a JSON file and converts it into a 2D array of strings.
*
* @param fileName the name of the file containing the map data
* @param key the key used to identify the map data in the JSON file
* @return a 2D array representing the country map
*/
public static String[][] parseCountryMap(String fileName, String key) {
String mapData = getValue(fileName, key);
@ -74,67 +90,75 @@ public class JsonParser {
for (String row : rows) {
if (row.trim().isEmpty()) continue;
String[] cities =
Arrays.stream(row.split(","))
.map(s -> s.replaceAll("\"", "").trim())
.filter(s -> !s.isEmpty())
.toArray(String[]::new);
Arrays.stream(row.split(","))
.map(s -> s.replaceAll("\"", "").trim())
.filter(s -> !s.isEmpty())
.toArray(String[]::new);
if (cities.length > 0) matrixList.add(cities);
}
String[][] result = new String[matrixList.size()][];
for (int i = 0; i < matrixList.size(); i++) {
result[i] = matrixList.get(i);
}
/*
for (int i = 0; i < result.length; i++) {
for (int j = 0; j < result[i].length; j++) {
System.out.println("result[" + i + "][" + j + "] = " + result[i][j]);
}
}
*/
return result;
}
/**
* Retrieves a list of departures from a JSON file based on the given key.
* Each departure is converted into a {@link Departure} object.
*
* @param fileName the name of the file containing the departure data
* @param key the key used to identify the departure data in the JSON file
* @return a list of {@link Departure} objects
*/
public static List<Departure> getDeparturesList(String fileName, String key) {
List<Departure> departures = new ArrayList<>();
String departuresJson = getValue(fileName, key);
String res =
departuresJson
.replaceAll("[\\{\\[\\]]", "")
.replaceAll("\\},", "\\|")
.replaceAll("\\}", "")
.replaceAll("\\\"", "")
.replaceAll(
"type:\\s|\\sfrom:\\s|\\sto:\\s|\\sdepartureTime:\\s|\\sduration:\\s|\\sprice:\\s|\\sminTransferTime:\\s",
"");
departuresJson
.replaceAll("[\\{\\[\\]]", "")
.replaceAll("\\},", "\\|")
.replaceAll("\\}", "")
.replaceAll("\\\"", "")
.replaceAll(
"type:\\s|\\sfrom:\\s|\\sto:\\s|\\sdepartureTime:\\s|\\sduration:\\s|\\sprice:\\s|\\sminTransferTime:\\s",
"");
String[] arr = res.split("\\|");
for (String a : arr) {
String[] temp = a.split(",");
departures.add(
new Departure(
parseTransportType(temp[0]),
temp[1],
temp[2],
temp[3],
Integer.parseInt(temp[4]),
Double.parseDouble(temp[5]),
Integer.parseInt(temp[6])));
new Departure(
parseTransportType(temp[0]),
temp[1],
temp[2],
temp[3],
Integer.parseInt(temp[4]),
Double.parseDouble(temp[5]),
Integer.parseInt(temp[6])));
}
return departures;
}
/**
* Parses the list of cities from a JSON file and returns a list of {@link City} objects.
*
* @param fileName the name of the file containing the city data
* @param key the key used to identify the city data in the JSON file
* @return a list of {@link City} objects
*/
public static List<City> parseCities(String fileName, String key) {
String cityData = getValue(fileName, key);
String res =
cityData
.replaceAll("[\\[\\]]+", "")
.replaceAll("\\},\\{", "\n")
.replaceAll("[\\{\\}]", "")
.replaceAll("\\n", "|");
cityData
.replaceAll("[\\[\\]]+", "")
.replaceAll("\\},\\{", "\n")
.replaceAll("[\\{\\}]", "")
.replaceAll("\\n", "|");
String[] arr = res.split("\\|");
@ -158,7 +182,7 @@ public class JsonParser {
for (int j = 0; j < temp.size(); j++) {
formatedList.add(
temp.get(j).replaceAll("\\\"[a-zA-Z]+\\\":\\s", "").trim().replaceAll("\\\"", ""));
temp.get(j).replaceAll("\\\"[a-zA-Z]+\\\":\\s", "").trim().replaceAll("\\\"", ""));
}
String[] parts = formatedList.get(0).split("_");
@ -172,28 +196,14 @@ public class JsonParser {
return cities;
}
// Way to slow for big sets of data because it has O(n*m)
public static List<City> loadDeparturesOld(List<City> cities, List<Departure> departures) {
for (Departure dep : departures) {
for (City city : cities) {
if (dep.getType() == TransportType.BUS) {
if (dep.getFrom().equals(city.getBusStation().getName())) {
city.getBusStation().addDeparture(dep);
}
} else if (dep.getType() == TransportType.TRAIN) {
if (dep.getFrom().equals(city.getTrainStation().getName())) {
city.getTrainStation().addDeparture(dep);
}
}
}
}
return cities;
}
/**
* Loads departure data into the corresponding cities' stations.
* This method maps departures to their respective bus or train stations in the city.
*
* @param cities a list of {@link City} objects
* @param departures a list of {@link Departure} objects
* @return the list of cities with departures loaded into their respective stations
*/
public static List<City> loadDepartures(List<City> cities, List<Departure> departures) {
Map<String, City> stationToCityMap = new HashMap();
@ -212,11 +222,11 @@ public class JsonParser {
if (city != null) {
if (dep.getType() == TransportType.BUS
&& city.getBusStation().getName().equals(dep.getFrom())) {
&& city.getBusStation().getName().equals(dep.getFrom())) {
city.getBusStation().addDeparture(dep);
} else if (dep.getType() == TransportType.TRAIN
&& city.getTrainStation().getName().equals(dep.getFrom())) {
&& city.getTrainStation().getName().equals(dep.getFrom())) {
city.getTrainStation().addDeparture(dep);
}
}
@ -224,6 +234,13 @@ public class JsonParser {
return cities;
}
/**
* Finds the closing pair for an open bracket or brace in a JSON-like string.
*
* @param str the string to search
* @param startIndex the index where the search starts
* @return the index of the closing pair, or -1 if no pair is found
*/
private static int findPair(String str, int startIndex) {
char open = str.charAt(startIndex);
@ -244,6 +261,12 @@ public class JsonParser {
return -1;
}
/**
* Parses a transport type from a string value.
*
* @param str the string representing the transport type
* @return the corresponding {@link TransportType}, or {@link TransportType#NOT_ASSIGNED} if the type is unknown
*/
public static TransportType parseTransportType(String str) {
for (TransportType t : TransportType.values()) {
if (t.toString().equalsIgnoreCase(str)) {
@ -252,4 +275,4 @@ public class JsonParser {
}
return TransportType.NOT_ASSIGNED;
}
}
}

View File

@ -7,12 +7,28 @@ import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.List;
public class TicketPrinter {
private static final String COUNTER_FILE = "ticket_counter.txt";
private static final String RECEIPTS_DIRECTORY = "receipts";
private static double totalProfit = 0.0;
private static int ticketIdCounter = 0;
import java.io.*;
import java.nio.file.*;
import java.time.LocalDate;
import java.util.List;
/**
* A utility class for generating and managing ticket receipts. This class handles the creation of ticket receipts,
* saving them to a file, and keeping track of ticket sales and profits. The receipts are saved in a specified
* directory, and a counter is used to generate unique ticket IDs.
*/
public class TicketPrinter {
private static final String COUNTER_FILE = "ticket_counter.txt"; // File to save the ticket ID counter and total profit
private static final String RECEIPTS_DIRECTORY = "receipts"; // Directory where receipts will be saved
private static double totalProfit = 0.0; // Total profit from ticket sales
private static int ticketIdCounter = 0; // Counter for ticket IDs
/**
* Generates a receipt for the given departures and total price, and saves it to a file.
*
* @param departures the list of departures for the ticket
* @param totalPrice the total price of the ticket
*/
public static void generateTicketReceipt(List<Departure> departures, double totalPrice) {
LocalDate currentDate = LocalDate.now();
@ -24,22 +40,21 @@ public class TicketPrinter {
receipt.append("=====================================================\n");
receipt.append("Ticket ID: ").append(getNextId()).append("\n");
receipt.append("Date: ").append(currentDate).append("\n");
receipt.append("From: ").append(departures.getFirst().getFrom()).append("\n");
receipt.append("To: ").append(departures.getLast().getTo()).append("\n");
receipt.append("From: ").append(departures.get(0).getFrom()).append("\n");
receipt.append("To: ").append(departures.get(departures.size() - 1).getTo()).append("\n");
receipt.append("-----------------------------------------------------\n");
receipt.append("Departure Prices:\n");
for (Departure dep : departures) {
receipt
.append(" - ")
.append(dep.getFrom())
.append(" -> ")
.append(dep.getTo())
.append(" | Price: $")
.append(String.format("%.2f", dep.getPrice()))
.append("\n");
.append(" - ")
.append(dep.getFrom())
.append(" -> ")
.append(dep.getTo())
.append(" | Price: $")
.append(String.format("%.2f", dep.getPrice()))
.append("\n");
}
receipt.append("-----------------------------------------------------\n");
@ -52,6 +67,12 @@ public class TicketPrinter {
writeReceipt(receipt.toString());
}
/**
* Writes the generated receipt to a file.
* The file is saved in the `receipts` directory with a unique ticket ID.
*
* @param receipt the receipt content to be written to the file
*/
private static void writeReceipt(String receipt) {
Path receiptFile = Paths.get(RECEIPTS_DIRECTORY, "receipt_" + ticketIdCounter + ".txt");
try {
@ -62,6 +83,9 @@ public class TicketPrinter {
}
}
/**
* Ensures the receipts directory exists. If not, it creates the directory.
*/
private static void folderExists() {
Path path = Paths.get(RECEIPTS_DIRECTORY);
@ -74,6 +98,10 @@ public class TicketPrinter {
}
}
/**
* Loads the ticket ID counter and total profit from a file. If the file doesn't exist or there is an error,
* it resets the values to 0.
*/
public static void loadCounter() {
try {
Path path = Paths.get(COUNTER_FILE);
@ -99,24 +127,41 @@ public class TicketPrinter {
}
}
/**
* Returns the total profit accumulated from ticket sales.
*
* @return the total profit
*/
public static double getTotalProfit() {
return totalProfit;
}
/**
* Returns the total number of tickets sold (based on the ticket ID counter).
*
* @return the number of tickets sold
*/
public static int getTicketsSoldNum() {
return ticketIdCounter;
}
/**
* Saves the current ticket ID counter and total profit to a file, so they can be persisted for future use.
*/
private static void saveCounter() {
try {
String str = "ticketIdCounter=" + ticketIdCounter + "\n" + "totalProfit=" + totalProfit;
Files.write(Paths.get(COUNTER_FILE), str.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Returns the next available ticket ID by incrementing the current ticket ID counter.
*
* @return the next ticket ID
*/
private static int getNextId() {
return ++ticketIdCounter;
}

View File

@ -12,36 +12,69 @@ import org.graphstream.ui.fx_viewer.FxDefaultView;
import org.graphstream.ui.fx_viewer.FxViewer;
import org.graphstream.ui.view.Viewer;
/**
* The GraphVisualizer class is responsible for visualizing a graph structure in a JavaFX HBox container.
* It allows the graph to be displayed with a custom stylesheet and also provides functionality
* to highlight a specific path of departures.
*/
public class GraphVisualizer {
// The file path to the CSS file that styles the graph visualization
static File cssFile = new File("src/main/resources/dev/ksan/travelpathoptimizer/app/graph.css");
// The graph to be visualized
private Graph graph;
// The HBox container in which the graph view will be displayed
private HBox container;
/**
* Constructs a GraphVisualizer with the specified graph and container.
*
* @param graph the graph to be visualized
* @param container the HBox container in which to display the graph
*/
public GraphVisualizer(Graph graph, HBox container) {
this.graph = graph;
this.container = container;
}
/**
* Displays the graph in the provided container.
* Applies a custom stylesheet and labels each node with its ID.
* Initializes a viewer to render the graph using JavaFX.
*/
public void showGraph() {
// Set the stylesheet for the graph visualization
graph.setAttribute("ui.stylesheet", "url('" + cssFile + "')");
// Label each node with its ID
for (Node node : graph) {
node.setAttribute("ui.label", node.getId());
}
// Create a viewer and enable auto-layout for the graph
Viewer viewer = new FxViewer(graph, FxViewer.ThreadingModel.GRAPH_IN_GUI_THREAD);
viewer.enableAutoLayout();
// Add a default view to the viewer and add it to the container
FxDefaultView view = (FxDefaultView) viewer.addDefaultView(false);
container.getChildren().clear();
container.getChildren().add(view);
}
/**
* Highlights a specific path of departures in the graph.
* It highlights the nodes and edges corresponding to the specified departures.
*
* @param departures the list of departures representing the path to be highlighted
*/
public synchronized void highlightPath(List<Departure> departures) {
List<String> nodes = new ArrayList();
// Lists to hold nodes and edges to be highlighted
List<String> nodes = new ArrayList<>();
List<String> edges = new ArrayList<>();
// Remove any existing highlight from all nodes and edges
for (Node node : graph) {
node.removeAttribute("ui.class");
}
@ -49,26 +82,31 @@ public class GraphVisualizer {
Edge edge = graph.getEdge(i);
edge.removeAttribute("ui.class");
}
for (Departure dep : departures) {
// Collect nodes and edges to highlight based on the departures list
for (Departure dep : departures) {
String from = dep.getFrom();
from = from.replaceAll("[A-Z]", "G");
from = from.replaceAll("[A-Z]", "G"); // Adjust formatting
if (!nodes.contains(from)) nodes.add(from);
if (!nodes.contains(dep.getTo())) nodes.add(dep.getTo());
edges.add(from + "-" + dep.getTo() + "-" + dep.getIdCounter());
}
for (String s : nodes) {
System.out.println(s);
// Print nodes for debugging
for (String nodeId : nodes) {
System.out.println(nodeId);
}
// Apply the highlight to nodes
for (String nodeId : nodes) {
Node node = graph.getNode(nodeId);
if (node != null) {
node.setAttribute("ui.class", "highlighted");
}
}
// Apply the highlight to edges
for (int i = 0; i < edges.size(); i++) {
String from = nodes.get(i);
String to = nodes.get(i + 1);
@ -83,4 +121,4 @@ public class GraphVisualizer {
}
System.out.println();
}
}
}