Skip to content

Commit 9a8d548

Browse files
committed
First FX route planner application added.
1 parent 6eabaad commit 9a8d548

File tree

8 files changed

+227
-28
lines changed

8 files changed

+227
-28
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package aimax.osm.gui.fx.applications;
2+
3+
import aima.gui.fx.framework.IntegrableApplication;
4+
import aimax.osm.data.DataResource;
5+
import aimax.osm.data.OsmMap;
6+
import aimax.osm.data.Position;
7+
import aimax.osm.data.entities.MapNode;
8+
import aimax.osm.data.entities.Track;
9+
import aimax.osm.gui.fx.viewer.MapPaneCtrl;
10+
import aimax.osm.routing.RouteCalculator;
11+
import javafx.geometry.Pos;
12+
import javafx.scene.control.*;
13+
import javafx.scene.layout.BorderPane;
14+
import javafx.scene.layout.Pane;
15+
import javafx.scene.layout.StackPane;
16+
import javafx.scene.text.Font;
17+
18+
import java.text.DecimalFormat;
19+
import java.util.List;
20+
21+
/**
22+
* Extendable application for route planning based on a real map of the city of Ulm.
23+
*
24+
* @author Ruediger Lunde
25+
*/
26+
public class OsmRoutePlannerApp extends IntegrableApplication {
27+
28+
public static void main(String[] args) {
29+
launch(args);
30+
}
31+
32+
private Button clearBtn;
33+
private ComboBox<String> taskCombo;
34+
private Button calcBtn;
35+
private Label statusLabel;
36+
MapPaneCtrl mapPaneCtrl;
37+
38+
RouteCalculator routeCalculator;
39+
40+
41+
@Override
42+
public String getTitle() {
43+
return "OSM Route Planner";
44+
}
45+
46+
/**
47+
* Simple pane to control the game.
48+
*/
49+
@Override
50+
public Pane createRootPane() {
51+
52+
routeCalculator = createRouteCalculator();
53+
54+
BorderPane root = new BorderPane();
55+
56+
ToolBar toolBar = new ToolBar();
57+
clearBtn = new Button("Clear");
58+
clearBtn.setOnAction(ev -> initialize());
59+
60+
taskCombo = new ComboBox<String>();
61+
taskCombo.getItems().addAll(routeCalculator.getTaskSelectionOptions());
62+
taskCombo.getSelectionModel().select(0);
63+
64+
calcBtn = new Button("Calculate Route");
65+
calcBtn.setOnAction(ev -> calculateRoute());
66+
toolBar.getItems().addAll(clearBtn, new Separator(), taskCombo, calcBtn);
67+
root.setTop(toolBar);
68+
69+
StackPane mapPane = new StackPane();
70+
mapPaneCtrl = new MapPaneCtrl(mapPane);
71+
mapPaneCtrl.getMap().addMapDataEventListener(ev -> updateEnabledState());
72+
mapPaneCtrl.loadMap(DataResource.getULMFileResource());
73+
74+
root.setCenter(mapPane);
75+
76+
statusLabel = new Label();
77+
statusLabel.setMaxWidth(Double.MAX_VALUE);
78+
statusLabel.setMaxWidth(Double.MAX_VALUE);
79+
statusLabel.setAlignment(Pos.CENTER);
80+
statusLabel.setFont(Font.font(16));
81+
root.setBottom(statusLabel);
82+
return root;
83+
}
84+
85+
/**
86+
* Factory method for the routing component. Subclasses can override it and
87+
* provide more advanced routing algorithms.
88+
*/
89+
protected RouteCalculator createRouteCalculator() {
90+
return new RouteCalculator();
91+
}
92+
93+
94+
@Override
95+
public void initialize() {
96+
mapPaneCtrl.getMap().clearMarkersAndTracks();
97+
statusLabel.setText("");
98+
}
99+
100+
@Override
101+
public void finalize() {
102+
// nothing to do here...
103+
}
104+
105+
/** Starts route generation after the calculate button has been pressed. */
106+
public void calculateRoute() {
107+
OsmMap map = mapPaneCtrl.getMap();
108+
List<Position> positions = routeCalculator.calculateRoute(
109+
map.getMarkers(), map, taskCombo.getSelectionModel().getSelectedIndex());
110+
mapPaneCtrl.getMap().createTrack("Route", positions);
111+
statusLabel.setText(getTrackInfo(mapPaneCtrl.getMap().getTrack("Route")));
112+
}
113+
114+
/**
115+
* Enables the Calculate button if at least two markers are set.
116+
*/
117+
protected void updateEnabledState() {
118+
calcBtn.setDisable(mapPaneCtrl.getMap().getMarkers().size() < 2);
119+
}
120+
121+
protected String getTrackInfo(Track track) {
122+
List<MapNode> nodes = track.getNodes();
123+
DecimalFormat f1 = new DecimalFormat("#0.00");
124+
double km = Position.getTrackLengthKM(nodes);
125+
String info = track.getName() + ": Length " + f1.format(km)
126+
+ " km";
127+
if (nodes.size() == 2) {
128+
DecimalFormat f2 = new DecimalFormat("#000");
129+
MapNode m1 = nodes.get(nodes.size() - 2);
130+
MapNode m2 = nodes.get(nodes.size() - 1);
131+
int course = new Position(m1).getCourseTo(m2);
132+
info += "; Direction " + f2.format(course);
133+
}
134+
return info;
135+
}
136+
}

aimax-osm/src/main/java/aimax/osm/gui/fx/applications/OsmViewerApp.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
/**
1414
* Shows a map of the city of Ulm with pan and zoom functionality (mouse drag, mouse wheel, arrow keys,
1515
* plus and minus keys). Set markers with Mouse-Left and remove them with Mouse-Right. Symbols can be changed in size
16-
* by zooming with Ctrl-Button pressed.
16+
* by zooming with Ctrl-Button pressed. Information about special map entities can be obtained by Mouse-Middle.
1717
*
1818
* @author Ruediger Lunde
1919
*/

aimax-osm/src/main/java/aimax/osm/gui/fx/applications/SimpleOsmViewerApp.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
package aimax.osm.gui.fx.applications;
22

33
import aimax.osm.data.DataResource;
4-
import aimax.osm.data.MapBuilder;
5-
import aimax.osm.data.OsmMap;
6-
import aimax.osm.data.impl.DefaultMap;
74
import aimax.osm.gui.fx.viewer.FXImageBuilder;
8-
import aimax.osm.reader.Bz2OsmReader;
9-
import aimax.osm.reader.MapReader;
10-
import aimax.osm.viewer.MapStyleFactory;
115
import aimax.osm.viewer.UnifiedMapDrawer;
126
import javafx.application.Application;
137
import javafx.scene.Group;

aimax-osm/src/main/java/aimax/osm/gui/fx/viewer/FXImageBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public void setFontSize(float size) {
136136
}
137137

138138
private void initStroke() {
139-
gc.setLineWidth(1);
139+
//gc.setLineWidth(1);
140140
gc.setLineDashes(null);
141141
gc.setLineJoin(StrokeLineJoin.ROUND);
142142
}

aimax-osm/src/main/java/aimax/osm/gui/fx/viewer/MapPaneCtrl.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
package aimax.osm.gui.fx.viewer;
22

33
import aimax.osm.data.OsmMap;
4+
import aimax.osm.data.entities.EntityAttribute;
5+
import aimax.osm.data.entities.MapEntity;
6+
import aimax.osm.data.entities.MapNode;
7+
import aimax.osm.data.entities.WayRef;
48
import aimax.osm.data.impl.DefaultMap;
59
import aimax.osm.viewer.AbstractEntityRenderer;
610
import aimax.osm.viewer.CoordTransformer;
711
import aimax.osm.viewer.UnifiedMapDrawer;
812
import javafx.scene.canvas.Canvas;
13+
import javafx.scene.control.Alert;
14+
import javafx.scene.control.ButtonType;
915
import javafx.scene.input.*;
1016
import javafx.scene.layout.StackPane;
1117

1218
import java.io.InputStream;
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.Optional;
1322

1423
/**
1524
* Created by rlunde on 28.10.2016.
@@ -125,6 +134,10 @@ protected void handleMouseEvent(MouseEvent event) {
125134
getMap().addMarker(tr.lat((int) event.getY()), tr.lon((int) event.getX()));
126135
} else if (event.getButton() == MouseButton.SECONDARY) {
127136
getMap().clearMarkersAndTracks();
137+
} else if (event.getButton() == MouseButton.MIDDLE) {
138+
MapNode mNode = getRenderer().getNextNode((int) event.getX(), (int) event.getY());
139+
if (mNode != null)
140+
showMapEntityInfoDialog(mNode, true);
128141
}
129142
}
130143
}
@@ -157,4 +170,60 @@ else if (event.getCode() == KeyCode.MINUS)
157170
else
158171
zoom(0.7f, (int) pane.getWidth() / 2, (int) pane.getHeight() / 2);
159172
}
173+
174+
175+
176+
/**
177+
* Finds the visible entity next to the specified view coordinates and shows
178+
* informations about it.
179+
*
180+
* @param debug
181+
* Enables a more detailed view.
182+
*/
183+
private void showMapEntityInfoDialog(MapEntity entity, boolean debug) {
184+
List<MapEntity> entities = new ArrayList<MapEntity>();
185+
if (entity.getName() != null || entity.getAttributes().length > 0
186+
|| debug)
187+
entities.add(entity);
188+
if (entity instanceof MapNode) {
189+
MapNode mNode = (MapNode) entity;
190+
for (WayRef ref : mNode.getWayRefs()) {
191+
MapEntity me = ref.getWay();
192+
if (me.getName() != null || me.getAttributes().length > 0
193+
|| debug)
194+
entities.add(me);
195+
}
196+
}
197+
boolean done = false;
198+
for (int i = 0; i < entities.size() && !done; i++) {
199+
MapEntity me = entities.get(i);
200+
String header = (me.getName() != null) ? me.getName() : "";
201+
String content = "";
202+
if (debug)
203+
header += " (" + ((me instanceof MapNode) ? "Node " : "Way ")
204+
+ me.getId() + ")";
205+
if (me instanceof MapNode) {
206+
content = "Lat: " + ((MapNode) me).getLat() + " Lon: " +
207+
((MapNode) me).getLon() + " ";
208+
}
209+
if (me.getAttributes().length > 0) {
210+
EntityAttribute[] atts = me.getAttributes();
211+
content += "Attributs: ";
212+
for (int j = 0; j < atts.length; j++) {
213+
content += atts[j].getKey() + "=" + atts[j].getValue() + " ";
214+
}
215+
216+
}
217+
218+
Alert alert = new Alert(Alert.AlertType.INFORMATION);
219+
alert.setTitle("Map Entity Info");
220+
alert.setHeaderText(header);
221+
alert.setContentText(content);
222+
Optional<ButtonType> result = alert.showAndWait();
223+
if (!result.isPresent())
224+
break;
225+
}
226+
}
227+
228+
160229
}

aimax-osm/src/main/java/aimax/osm/gui/swing/applications/MiniNaviApp.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public MiniNaviApp(String[] args) {
9898
toolbar.add(gpsCombo);
9999

100100
waySelection = new JComboBox<String>(
101-
routeCalculator.getWaySelectionOptions());
101+
routeCalculator.getTaskSelectionOptions());
102102
toolbar.add(waySelection);
103103
toolbar.addSeparator();
104104
calcButton = new JButton("Calculate Route");

aimax-osm/src/main/java/aimax/osm/gui/swing/applications/RoutePlannerApp.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class RoutePlannerApp implements ActionListener {
2929
public final static String ROUTE_TRACK_NAME = "Route";
3030

3131
protected MapViewFrame frame;
32-
protected JComboBox<String> waySelection;
32+
protected JComboBox<String> taskSelection;
3333
protected JButton calcButton;
3434
protected RouteCalculator routeCalculator;
3535

@@ -43,9 +43,9 @@ public RoutePlannerApp(String[] args) {
4343
routeCalculator = createRouteCalculator();
4444
JToolBar toolbar = frame.getToolbar();
4545
toolbar.addSeparator();
46-
waySelection = new JComboBox<String>(
47-
routeCalculator.getWaySelectionOptions());
48-
toolbar.add(waySelection);
46+
taskSelection = new JComboBox<String>(
47+
routeCalculator.getTaskSelectionOptions());
48+
toolbar.add(taskSelection);
4949
toolbar.addSeparator();
5050
calcButton = new JButton("Calculate Route");
5151
calcButton.addActionListener(this);
@@ -87,7 +87,7 @@ public void actionPerformed(ActionEvent e) {
8787
if (e.getSource() == calcButton) {
8888
OsmMap map = frame.getMap();
8989
List<Position> positions = routeCalculator.calculateRoute(
90-
map.getMarkers(), map, waySelection.getSelectedIndex());
90+
map.getMarkers(), map, taskSelection.getSelectedIndex());
9191
frame.getMap().createTrack(ROUTE_TRACK_NAME, positions);
9292
}
9393
}

aimax-osm/src/main/java/aimax/osm/routing/RouteCalculator.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
public class RouteCalculator {
2626

2727
/** Returns the names of all supported way selection options. */
28-
public String[] getWaySelectionOptions() {
28+
public String[] getTaskSelectionOptions() {
2929
return new String[] { "Distance", "Distance (Car)", "Distance (Bike)" };
3030
}
3131

@@ -42,23 +42,23 @@ public String[] getWaySelectionOptions() {
4242
* start, last node as finish, all others as via nodes.
4343
* @param map
4444
* The information source.
45-
* @param waySelection
45+
* @param taskSelection
4646
* Number, indicating which kinds of ways are relevant.
4747
*/
4848
public List<Position> calculateRoute(List<MapNode> markers, OsmMap map,
49-
int waySelection) {
49+
int taskSelection) {
5050
List<Position> result = new ArrayList<Position>();
5151
try {
52-
MapWayFilter wayFilter = createMapWayFilter(map, waySelection);
53-
boolean ignoreOneways = (waySelection == 0);
52+
MapWayFilter wayFilter = createMapWayFilter(map, taskSelection);
53+
boolean ignoreOneways = (taskSelection == 0);
5454
List<MapNode[]> pNodeList = subdivideProblem(markers, map, wayFilter);
5555
for (int i = 0; i < pNodeList.size()
5656
&& !CancelableThread.currIsCanceled(); i++) {
5757
Problem problem = createProblem(pNodeList.get(i), map, wayFilter,
58-
ignoreOneways, waySelection);
58+
ignoreOneways, taskSelection);
5959
HeuristicFunction hf = createHeuristicFunction(pNodeList.get(i),
60-
waySelection);
61-
Search search = createSearch(hf, waySelection);
60+
taskSelection);
61+
Search search = createSearch(hf, taskSelection);
6262
List<Action> actions = search.search(problem);
6363
if (actions.isEmpty())
6464
break;
@@ -80,10 +80,10 @@ public List<Position> calculateRoute(List<MapNode> markers, OsmMap map,
8080
}
8181

8282
/** Factory method, responsible for way filter creation. */
83-
protected MapWayFilter createMapWayFilter(OsmMap map, int waySelection) {
84-
if (waySelection == 1)
83+
protected MapWayFilter createMapWayFilter(OsmMap map, int taskSelection) {
84+
if (taskSelection == 1)
8585
return MapWayAttFilter.createCarWayFilter();
86-
else if (waySelection == 2)
86+
else if (taskSelection == 2)
8787
return MapWayAttFilter.createBicycleWayFilter();
8888
else
8989
return MapWayAttFilter.createAnyWayFilter();
@@ -111,19 +111,19 @@ protected List<MapNode[]> subdivideProblem(List<MapNode> markers,
111111

112112
/** Factory method, responsible for problem creation. */
113113
protected Problem createProblem(MapNode[] pNodes, OsmMap map,
114-
MapWayFilter wayFilter, boolean ignoreOneways, int waySelection) {
114+
MapWayFilter wayFilter, boolean ignoreOneways, int taskSelection) {
115115
return new RouteFindingProblem(pNodes[0], pNodes[1], wayFilter,
116116
ignoreOneways);
117117
}
118118

119119
/** Factory method, responsible for heuristic function creation. */
120120
protected HeuristicFunction createHeuristicFunction(MapNode[] pNodes,
121-
int waySelection) {
121+
int taskSelection) {
122122
return new OsmSldHeuristicFunction(pNodes[1]);
123123
}
124124

125125
/** Factory method, responsible for search creation. */
126-
protected Search createSearch(HeuristicFunction hf, int waySelection) {
126+
protected Search createSearch(HeuristicFunction hf, int taskSelection) {
127127
return new AStarSearch(new GraphSearch(), hf);
128128
}
129129
}

0 commit comments

Comments
 (0)