package aimax.osm.gui.fx.applications; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import aima.core.agent.*; import aima.core.environment.map.BidirectionalMapProblem; import aima.core.environment.map.MapEnvironment; import aima.core.environment.map.MapFunctionFactory; import aima.core.environment.map.MoveToAction; import aima.core.search.framework.Metrics; import aima.core.search.framework.evalfunc.HeuristicFunction; import aima.core.search.framework.problem.Problem; import aima.core.search.online.LRTAStarAgent; import aima.core.search.online.OnlineSearchProblem; import aima.core.util.CancelableThread; import aima.core.util.math.geom.shapes.Point2D; import aima.gui.fx.framework.IntegrableApplication; import aima.gui.fx.framework.Parameter; import aima.gui.fx.framework.SimulationPaneBuilder; import aima.gui.fx.framework.SimulationPaneCtrl; import aimax.osm.data.DataResource; import aimax.osm.data.MapWayAttFilter; import aimax.osm.data.Position; import aimax.osm.data.entities.MapNode; import aimax.osm.gui.fx.viewer.MapPaneCtrl; import aimax.osm.routing.MapAdapter; import javafx.application.Platform; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; /** * Integrable application which demonstrates how the Learning Real-Time A* * (LRTA*) search algorithm performs in a route finding scenario based on a real * OSM map. This GUI does not provide a text pane beside the map view. * * @author Ruediger Lunde * */ public class OsmLRTAStarAgentApp extends IntegrableApplication { public static void main(String[] args) { launch(args); } public static String PARAM_WAY_SELECTION = "waySelection"; public static String PARAM_HEURISTIC = "heuristic"; public static String TRACK_NAME = "Track"; protected MapPaneCtrl mapPaneCtrl; protected SimulationPaneCtrl simPaneCtrl; protected MapAdapter map; protected MapEnvironment env; @Override public String getTitle() { return "OSM LRTA* Agent App"; } /** Loads a map of the city of Ulm, Germany. Override to change the map. */ protected void loadMap() { mapPaneCtrl.loadMap(DataResource.getULMFileResource()); } protected List<Parameter> createParameters() { Parameter p1 = new Parameter(PARAM_WAY_SELECTION, "Use any way", "Travel by car", "Travel by bicycle"); Parameter p2 = new Parameter(PARAM_HEURISTIC, "0", "SLD"); p2.setDefaultValueIndex(1); return Arrays.asList(p1, p2); } /** * Factory method which creates a new agent based on the current parameter * settings. */ protected Agent createAgent(List<String> locations) { HeuristicFunction heuristic; switch (simPaneCtrl.getParamValueIndex(PARAM_HEURISTIC)) { case 0: heuristic = MapFunctionFactory.getZeroHeuristicFunction(); break; default: heuristic = MapFunctionFactory.getSLDHeuristicFunction(locations.get(1), map); } Problem p = new BidirectionalMapProblem(map, null, locations.get(1)); OnlineSearchProblem osp = new OnlineSearchProblem(p.getActionsFunction(), p.getGoalTest(), p.getStepCostFunction()); return new LRTAStarAgent(osp, MapFunctionFactory.getPerceptToStateFunction(), heuristic); } /** * Defines state view, parameters, and call-back functions and calls the * simulation pane builder to create layout and controller objects. */ @Override public Pane createRootPane() { BorderPane root = new BorderPane(); List<Parameter> params = createParameters(); StackPane mapPane = new StackPane(); mapPaneCtrl = new MapPaneCtrl(mapPane); loadMap(); SimulationPaneBuilder builder = new SimulationPaneBuilder(); builder.defineParameters(params); builder.defineStateView(mapPane); builder.defineInitMethod(this::initialize); builder.defineSimMethod(this::simulate); simPaneCtrl = builder.getResultFor(root); simPaneCtrl.setParam(SimulationPaneCtrl.PARAM_SIM_SPEED, 0); return root; } /** * Is called after each parameter selection change. This implementation * prepares the map for different kinds of vehicles and clears the currently * displayed track. */ @Override public void initialize() { map = new MapAdapter(mapPaneCtrl.getMap()); switch (simPaneCtrl.getParamValueIndex(PARAM_WAY_SELECTION)) { case 0: map.setMapWayFilter(MapWayAttFilter.createAnyWayFilter()); map.ignoreOneways(true); break; case 1: map.setMapWayFilter(MapWayAttFilter.createCarWayFilter()); map.ignoreOneways(false); break; case 2: map.setMapWayFilter(MapWayAttFilter.createBicycleWayFilter()); map.ignoreOneways(false); break; } map.getOsmMap().clearTrack(TRACK_NAME); } /** Starts the experiment. */ public void simulate() { List<MapNode> markers = map.getOsmMap().getMarkers(); if (markers.size() < 2) { simPaneCtrl.setStatus("Error: Please set two markers with mouse-left."); } else { List<String> locations = new ArrayList<>(markers.size()); for (MapNode node : markers) { Point2D pt = new Point2D(node.getLon(), node.getLat()); locations.add(map.getNearestLocation(pt)); } Agent agent = createAgent(locations); env = new MapEnvironment(map); env.addEnvironmentView(new TrackUpdater()); env.addAgent(agent, locations.get(0)); while (!env.isDone() && !CancelableThread.currIsCanceled()) { env.step(); simPaneCtrl.waitAfterStep(); } } } @Override public void cleanup() { simPaneCtrl.cancelSimulation(); } /** Visualizes agent positions. Call from simulation thread. */ private void updateTrack(Agent agent, Metrics metrics) { MapAdapter map = (MapAdapter) env.getMap(); MapNode node = map.getWayNode(env.getAgentLocation(agent)); if (node != null) { Platform.runLater(() -> map.getOsmMap().addToTrack(TRACK_NAME, new Position(node.getLat(), node.getLon()))); } simPaneCtrl.setStatus(metrics.toString()); } // helper classes... class TrackUpdater implements EnvironmentView { int actionCounter = 0; @Override public void notify(String msg) {} @Override public void agentAdded(Agent agent, Environment source) { updateTrack(agent, new Metrics()); } /** * Reacts on environment changes and updates the tracks. */ @Override public void agentActed(Agent agent, Percept percept, Action command, Environment source) { if (command instanceof MoveToAction) { Metrics metrics = new Metrics(); Double travelDistance = env.getAgentTravelDistance(env.getAgents().get(0)); if (travelDistance != null) metrics.set("travelDistance[km]", travelDistance); metrics.set("actions", ++actionCounter); updateTrack(agent, metrics); } } } }