added graph visualization

This commit is contained in:
Ksan 2025-08-21 02:28:08 +02:00
parent 8548cf0acc
commit 462028e354
8 changed files with 138 additions and 22 deletions

BIN
img1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -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();
}

View File

@ -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<Integer, Departure> 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<Departure, Double> tabCostCol;
@FXML private ChoiceBox<String> 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<Void>() {
@Override
protected Void call() throws Exception {
graph.reset();
graphSimulation.reset();
if (startCity != null && endCity != null) {
List<City> path = new ArrayList<>();
List<Integer> 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<List<Integer>> 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

View File

@ -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<PathResult> 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<Location, Double> 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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}