package aimax.osm.gui.fx.applications; import aima.gui.fx.framework.IntegrableApplication; import aimax.osm.data.DataResource; import aimax.osm.data.OsmMap; import aimax.osm.data.Position; import aimax.osm.data.entities.MapNode; import aimax.osm.data.entities.Track; import aimax.osm.gui.fx.viewer.MapPaneCtrl; import aimax.osm.routing.RouteCalculator; import javafx.geometry.Pos; import javafx.scene.control.*; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.text.Font; import java.text.DecimalFormat; import java.util.List; /** * Extendable application for route planning based on a real map of the city of Ulm. * * @author Ruediger Lunde */ public class OsmRoutePlannerApp extends IntegrableApplication { public static void main(String[] args) { launch(args); } private Button clearBtn; private ComboBox<String> taskCombo; private Button calcBtn; private Label statusLabel; private MapPaneCtrl mapPaneCtrl; private RouteCalculator routeCalculator; @Override public String getTitle() { return "OSM Route Planner App"; } /** * Simple pane to control the game. */ @Override public Pane createRootPane() { routeCalculator = createRouteCalculator(); BorderPane root = new BorderPane(); ToolBar toolBar = new ToolBar(); clearBtn = new Button("Clear"); clearBtn.setOnAction(ev -> initialize()); taskCombo = new ComboBox<String>(); taskCombo.getItems().addAll(routeCalculator.getTaskSelectionOptions()); taskCombo.getSelectionModel().select(0); calcBtn = new Button("Calculate Route"); calcBtn.setOnAction(ev -> calculateRoute()); toolBar.getItems().addAll(clearBtn, new Separator(), taskCombo, calcBtn); root.setTop(toolBar); StackPane mapPane = new StackPane(); mapPaneCtrl = new MapPaneCtrl(mapPane); mapPaneCtrl.getMap().addMapDataEventListener(ev -> updateEnabledState()); mapPaneCtrl.loadMap(DataResource.getULMFileResource()); root.setCenter(mapPane); statusLabel = new Label(); statusLabel.setMaxWidth(Double.MAX_VALUE); statusLabel.setMaxWidth(Double.MAX_VALUE); statusLabel.setAlignment(Pos.CENTER); statusLabel.setFont(Font.font(16)); root.setBottom(statusLabel); return root; } /** * Factory method for the routing component. Subclasses can override it and * provide more advanced routing algorithms. */ protected RouteCalculator createRouteCalculator() { return new RouteCalculator(); } @Override public void initialize() { mapPaneCtrl.getMap().clearMarkersAndTracks(); statusLabel.setText(""); } @Override public void cleanup() { // nothing to do here... } /** Starts route generation after the calculate button has been pressed. */ public void calculateRoute() { OsmMap map = mapPaneCtrl.getMap(); List<Position> positions = routeCalculator.calculateRoute( map.getMarkers(), map, taskCombo.getSelectionModel().getSelectedIndex()); mapPaneCtrl.getMap().createTrack("Route", positions); statusLabel.setText(getTrackInfo(mapPaneCtrl.getMap().getTrack("Route"))); } /** * Enables the Calculate button if at least two markers are set. */ protected void updateEnabledState() { calcBtn.setDisable(mapPaneCtrl.getMap().getMarkers().size() < 2); } protected String getTrackInfo(Track track) { List<MapNode> nodes = track.getNodes(); DecimalFormat f1 = new DecimalFormat("#0.00"); double km = Position.getTrackLengthKM(nodes); String info = track.getName() + ": Length " + f1.format(km) + " km"; if (nodes.size() == 2) { DecimalFormat f2 = new DecimalFormat("#000"); MapNode m1 = nodes.get(nodes.size() - 2); MapNode m2 = nodes.get(nodes.size() - 1); int course = new Position(m1).getCourseTo(m2); info += "; Direction " + f2.format(course); } return info; } }