From 462028e35426a7b8f1dd8aef05cffbea70a44611 Mon Sep 17 00:00:00 2001 From: Ksan Date: Thu, 21 Aug 2025 02:28:08 +0200 Subject: [PATCH] added graph visualization --- img1.jpg | Bin 0 -> 49722 bytes .../app/TravelPathOptimizerApplication.java | 2 + .../controller/MainController.java | 94 +++++++++++++++--- .../GraphSimulation.java} | 10 +- .../PathResult.java | 3 +- .../{graph => graphSimulation}/PathState.java | 3 +- .../visualize/GraphVisualizer.java | 32 ++++++ .../ksan/travelpathoptimizer/app/graph.css | 16 +++ 8 files changed, 138 insertions(+), 22 deletions(-) create mode 100644 img1.jpg rename src/main/java/dev/ksan/travelpathoptimizer/{graph/Graph.java => graphSimulation/GraphSimulation.java} (97%) rename src/main/java/dev/ksan/travelpathoptimizer/{graph => graphSimulation}/PathResult.java (91%) rename src/main/java/dev/ksan/travelpathoptimizer/{graph => graphSimulation}/PathState.java (93%) create mode 100644 src/main/java/dev/ksan/travelpathoptimizer/visualize/GraphVisualizer.java create mode 100644 src/main/resources/dev/ksan/travelpathoptimizer/app/graph.css diff --git a/img1.jpg b/img1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..328e8b12bdab98a78ea14b0c6a15b6abc94c6f76 GIT binary patch literal 49722 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 WgqK;&2ZNCT&2;7Zs+aK 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 literal 0 HcmV?d00001 diff --git a/src/main/java/dev/ksan/travelpathoptimizer/app/TravelPathOptimizerApplication.java b/src/main/java/dev/ksan/travelpathoptimizer/app/TravelPathOptimizerApplication.java index f34cf08..75346fc 100644 --- a/src/main/java/dev/ksan/travelpathoptimizer/app/TravelPathOptimizerApplication.java +++ b/src/main/java/dev/ksan/travelpathoptimizer/app/TravelPathOptimizerApplication.java @@ -8,6 +8,7 @@ import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; + public class TravelPathOptimizerApplication extends Application { @Override public void start(Stage stage) throws IOException { @@ -27,6 +28,7 @@ public class TravelPathOptimizerApplication extends Application { stage.show(); } + public static void main(String[] args) { launch(); } diff --git a/src/main/java/dev/ksan/travelpathoptimizer/controller/MainController.java b/src/main/java/dev/ksan/travelpathoptimizer/controller/MainController.java index c424009..0cb63f7 100644 --- a/src/main/java/dev/ksan/travelpathoptimizer/controller/MainController.java +++ b/src/main/java/dev/ksan/travelpathoptimizer/controller/MainController.java @@ -1,14 +1,16 @@ package dev.ksan.travelpathoptimizer.controller; import dev.ksan.travelpathoptimizer.app.TransportDataGenerator; -import dev.ksan.travelpathoptimizer.graph.Graph; -import dev.ksan.travelpathoptimizer.graph.PathResult; +import dev.ksan.travelpathoptimizer.graphSimulation.GraphSimulation; +import dev.ksan.travelpathoptimizer.graphSimulation.PathResult; import dev.ksan.travelpathoptimizer.model.City; import dev.ksan.travelpathoptimizer.model.Departure; import dev.ksan.travelpathoptimizer.model.Location; import dev.ksan.travelpathoptimizer.service.CityManager; import dev.ksan.travelpathoptimizer.util.JsonParser; import dev.ksan.travelpathoptimizer.util.TicketPrinter; +import dev.ksan.travelpathoptimizer.visualize.GraphVisualizer; + import java.io.File; import java.time.LocalTime; import java.util.ArrayList; @@ -38,9 +40,14 @@ import javafx.scene.layout.VBox; import javafx.scene.text.Text; import javafx.stage.FileChooser; import javafx.stage.Stage; +import org.graphstream.graph.Graph; +import org.graphstream.graph.implementations.MultiGraph; +import org.graphstream.graph.implementations.SingleGraph; public class MainController { + + @FXML private HBox graphPane; @FXML private GridPane map; @FXML private Label welcomeText; @FXML private Text selectedFileText; @@ -54,7 +61,7 @@ public class MainController { @FXML private Button startCityButton; @FXML private Button endCityButton; private HashMap departuresMap = new HashMap<>(); - private Graph graph; + private GraphSimulation graphSimulation; private City[][] cities; private File selectedFile; @FXML private Button openFileButton; @@ -77,6 +84,67 @@ public class MainController { @FXML private TableColumn tabCostCol; @FXML private ChoiceBox pathChoiceBox; + + private Graph graph = new MultiGraph("map"); + + +@FXML +void showGraph() { + graph.clear(); + + if (map.isVisible()) { + map.setVisible(false); + map.setManaged(false); + graphPane.setVisible(true); + graphPane.setManaged(true); + } else { + graphPane.setManaged(false); + graphPane.setVisible(false); + + map.setManaged(true); + map.setVisible(true); + } + + for (City[] row : cities) { + for (City city : row) { + if (city != null) { + graph.addNode(city.getName()); + } + } + } + + for (City[] row : cities) { + for (City city : row) { + if (city != null) { + for (Departure dep : city.getDestinations()) { + City destinationCity = dep.getDestinationCity(); + if (destinationCity != null) { + if (!city.getName().equals(destinationCity.getName())) { + String edgeId = city.getName() + "-" + destinationCity.getName() + "-" + dep.getIdCounter(); + + if (graph.getEdge(edgeId) != null) { + System.out.println("Edge already exists: " + edgeId); + System.out.println("skip"); + } else { + try { + graph.addEdge(edgeId, city.getName(), destinationCity.getName(), true); + System.out.println("Added directed edge: " + edgeId + " from " + city.getName() + " to " + destinationCity.getName()); + } catch (org.graphstream.graph.EdgeRejectedException e) { + System.out.println("Edge rejected: " + edgeId + " from " + city.getName() + " to " + destinationCity.getName()); + e.printStackTrace(); + } + } + } + } + } + } + } + } + + + GraphVisualizer.showGraph(graph,graphPane); + + } @FXML private void buyTicket() { @@ -87,7 +155,7 @@ public class MainController { @FXML private void findTopPaths() { - graph.reset(); + graphSimulation.reset(); updateUiGetList(); pathChoiceBox.getItems().clear(); startButton.setDisable(true); @@ -100,7 +168,7 @@ public class MainController { new Task() { @Override protected Void call() throws Exception { - graph.reset(); + graphSimulation.reset(); if (startCity != null && endCity != null) { List path = new ArrayList<>(); List departures = new ArrayList<>(); @@ -108,7 +176,7 @@ public class MainController { double totalCost = 0.0; System.out.println(categoryBox.getValue().toString()); - graph.calculateTopPaths( + graphSimulation.calculateTopPaths( startCity, endCity, path, @@ -117,16 +185,16 @@ public class MainController { departures, categoryBox.getValue().toString()); - System.out.println(graph.getTopPaths().size()); + System.out.println(graphSimulation.getTopPaths().size()); System.out.println(startCity.getName() + endCity.getName()); - if (graph.getTopPaths().isEmpty()) return null; + if (graphSimulation.getTopPaths().isEmpty()) return null; Platform.runLater( () -> { - for (PathResult pathResult : graph.getSortedPaths()) { + for (PathResult pathResult : graphSimulation.getSortedPaths()) { pathChoiceBox.getItems().add("Route: " + String.valueOf(pathResult.getId())); } pathChoiceBox.setValue( - "Route: " + String.valueOf(graph.getSortedPaths().getFirst().getId())); + "Route: " + String.valueOf(graphSimulation.getSortedPaths().getFirst().getId())); updateUiGetList(); }); } @@ -168,9 +236,9 @@ public class MainController { e.printStackTrace(); } } - if (graph.getTopPaths().size() > 0) { + if (graphSimulation.getTopPaths().size() > 0) { Optional> departuresOpt = - graph.getSortedPaths().stream() + graphSimulation.getSortedPaths().stream() .filter( item -> item.getId() @@ -467,7 +535,7 @@ public class MainController { } this.cities = map; - this.graph = new Graph(map); + this.graphSimulation = new GraphSimulation(map); } @FXML diff --git a/src/main/java/dev/ksan/travelpathoptimizer/graph/Graph.java b/src/main/java/dev/ksan/travelpathoptimizer/graphSimulation/GraphSimulation.java similarity index 97% rename from src/main/java/dev/ksan/travelpathoptimizer/graph/Graph.java rename to src/main/java/dev/ksan/travelpathoptimizer/graphSimulation/GraphSimulation.java index 34d3d83..bf37170 100644 --- a/src/main/java/dev/ksan/travelpathoptimizer/graph/Graph.java +++ b/src/main/java/dev/ksan/travelpathoptimizer/graphSimulation/GraphSimulation.java @@ -1,4 +1,4 @@ -package dev.ksan.travelpathoptimizer.graph; +package dev.ksan.travelpathoptimizer.graphSimulation; import dev.ksan.travelpathoptimizer.model.City; import dev.ksan.travelpathoptimizer.model.Departure; @@ -9,7 +9,7 @@ import java.time.Duration; import java.time.LocalTime; import java.util.*; -public class Graph { +public class GraphSimulation { private City[][] matrix; private List allPaths = new ArrayList<>(); private int pathIdCounter = 1; @@ -31,7 +31,7 @@ public class Graph { cities = JsonParser.loadDepartures(cities, departures); City[][] map = JsonParser.loadMap("transport_data.json", "countryMap", cities); - Graph graph = new Graph(map); + GraphSimulation graphSimulation = new GraphSimulation(map); cities = JsonParser.loadDepartures(cities, departures); for (City city : cities) { CityManager.addCity(city); @@ -39,7 +39,7 @@ public class Graph { System.out.println(cities.getFirst().getName() + " do " + cities.getLast().getName()); Map result = - graph.calculateShortestPath(cities.getFirst(), cities.getLast(), "time"); + graphSimulation.calculateShortestPath(cities.getFirst(), cities.getLast(), "time"); System.out.println( cities.get(1).getName() + " = " + result.get(cities.getLast().getLocation())); @@ -382,7 +382,7 @@ public class Graph { return allPaths; } - public Graph(City[][] matrix) { + public GraphSimulation(City[][] matrix) { this.matrix = matrix; } diff --git a/src/main/java/dev/ksan/travelpathoptimizer/graph/PathResult.java b/src/main/java/dev/ksan/travelpathoptimizer/graphSimulation/PathResult.java similarity index 91% rename from src/main/java/dev/ksan/travelpathoptimizer/graph/PathResult.java rename to src/main/java/dev/ksan/travelpathoptimizer/graphSimulation/PathResult.java index a76843c..deb6164 100644 --- a/src/main/java/dev/ksan/travelpathoptimizer/graph/PathResult.java +++ b/src/main/java/dev/ksan/travelpathoptimizer/graphSimulation/PathResult.java @@ -1,7 +1,6 @@ -package dev.ksan.travelpathoptimizer.graph; +package dev.ksan.travelpathoptimizer.graphSimulation; import dev.ksan.travelpathoptimizer.model.City; -import dev.ksan.travelpathoptimizer.model.Departure; import java.time.LocalTime; import java.util.ArrayList; diff --git a/src/main/java/dev/ksan/travelpathoptimizer/graph/PathState.java b/src/main/java/dev/ksan/travelpathoptimizer/graphSimulation/PathState.java similarity index 93% rename from src/main/java/dev/ksan/travelpathoptimizer/graph/PathState.java rename to src/main/java/dev/ksan/travelpathoptimizer/graphSimulation/PathState.java index d7c2fa2..9871f31 100644 --- a/src/main/java/dev/ksan/travelpathoptimizer/graph/PathState.java +++ b/src/main/java/dev/ksan/travelpathoptimizer/graphSimulation/PathState.java @@ -1,9 +1,8 @@ -package dev.ksan.travelpathoptimizer.graph; +package dev.ksan.travelpathoptimizer.graphSimulation; import dev.ksan.travelpathoptimizer.model.City; import dev.ksan.travelpathoptimizer.model.Departure; import java.util.List; -import java.util.stream.Collectors; public class PathState { private City city; diff --git a/src/main/java/dev/ksan/travelpathoptimizer/visualize/GraphVisualizer.java b/src/main/java/dev/ksan/travelpathoptimizer/visualize/GraphVisualizer.java new file mode 100644 index 0000000..3592cb2 --- /dev/null +++ b/src/main/java/dev/ksan/travelpathoptimizer/visualize/GraphVisualizer.java @@ -0,0 +1,32 @@ +package dev.ksan.travelpathoptimizer.visualize; + +import java.io.File; + +import javafx.scene.layout.HBox; +import org.graphstream.graph.Graph; +import org.graphstream.ui.fx_viewer.FxDefaultView; +import org.graphstream.ui.fx_viewer.FxViewer; +import org.graphstream.ui.view.Viewer; + +public class GraphVisualizer { + + static File cssFile = new File("src/main/resources/dev/ksan/travelpathoptimizer/app/graph.css"); + + public static void showGraph(Graph graph, HBox container) { + graph.setAttribute("ui.stylesheet", "url('" + cssFile + "')"); + + Viewer viewer = new FxViewer(graph, FxViewer.ThreadingModel.GRAPH_IN_GUI_THREAD); + viewer.enableAutoLayout(); + + FxDefaultView view = (FxDefaultView) viewer.addDefaultView(false); + + + container.getChildren().clear(); + + container.getChildren().add(view); + } + + + + +} diff --git a/src/main/resources/dev/ksan/travelpathoptimizer/app/graph.css b/src/main/resources/dev/ksan/travelpathoptimizer/app/graph.css new file mode 100644 index 0000000..b2d6ad4 --- /dev/null +++ b/src/main/resources/dev/ksan/travelpathoptimizer/app/graph.css @@ -0,0 +1,16 @@ +node { + size: 12px; + shape: box; + text-alignment: under; + fill-color: #4a148c; + text-color: #980b0b; + text-background-mode: rounded-box; + text-background-color: #333; + text-padding: 4px; +} + +edge { + size: 2px; + fill-color: #979797; + arrow-shape: none; +}