RealWorldStreetsMovement.java 10.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
 * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
 *
 * This file is part of PeerfactSim.KOM.
 * 
 * PeerfactSim.KOM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * PeerfactSim.KOM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with PeerfactSim.KOM.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package de.tud.kom.p2psim.impl.topology.movement.local;

import com.graphhopper.GHRequest;
import com.graphhopper.GHResponse;
import com.graphhopper.GraphHopper;
import com.graphhopper.routing.util.EncodingManager;
Clemens Krug's avatar
Clemens Krug committed
27
import com.graphhopper.util.DistanceCalc2D;
28
import com.graphhopper.util.PointList;
29
import com.graphhopper.util.shapes.GHPoint;
30
import com.graphhopper.util.shapes.GHPoint3D;
31
import de.tud.kom.p2psim.api.topology.Topology;
32
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
33
import de.tud.kom.p2psim.impl.topology.PositionVector;
34
import de.tud.kom.p2psim.impl.topology.movement.modularosm.GPSCalculation;
35
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView;
36
37
import de.tud.kom.p2psim.impl.util.Either;
import de.tud.kom.p2psim.impl.util.Left;
38
import de.tudarmstadt.maki.simonstrator.api.Binder;
39
import de.tudarmstadt.maki.simonstrator.api.Monitor;
40
import de.tudarmstadt.maki.simonstrator.api.Monitor.Level;
41

Clemens Krug's avatar
Clemens Krug committed
42
import java.util.*;
Clemens Krug's avatar
Clemens Krug committed
43

44
45
/**
 * This movement strategy uses the data from osm and navigates the nodes throught streets to the destination
Clemens Krug's avatar
Clemens Krug committed
46
 *
47
 * 13.03.2017 Clemens Krug: Fixed a bug. When the GraphHopper routing had errors the nodes would move to the
Clemens Krug's avatar
Clemens Krug committed
48
 * top right corner and not, as intended, straight to their destination.
49
50
51
52
53
54
55
56
57
58
 * 
 * @author Martin Hellwig
 * @version 1.0, 07.07.2015
 */
public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
	
	private PositionVector worldDimensions;
	private GraphHopper hopper;
	private boolean init = false;
	
59
	private static HashMap<SimLocationActuator, RealWorldMovementPoints> movementPoints = new HashMap<>();
60
	
61
62
63
	private String osmFileLocation; //use pbf-format, because osm-format causes problems (xml-problems)
	private String graphFolderFiles;
	private String movementType; //car, bike or foot
Clemens Krug's avatar
Clemens Krug committed
64
	private String defaultMovement;
65
	private String navigationalType; //fastest,
66
67
	private double latLower; //Values from -90 to 90; always smaller than latUpper
	private double latUpper; //Values from -90 to 90
68
69
	private double lonLeft; //Values from -180 to 180; Always smaller than lonRight
	private double lonRight; //Values from -180 to 180
70
	private boolean uniqueFolders;
71

72
73
74
75
76
77
	/**
	 * Tolerance in meters (if the node reached a waypoint up to "tolerance"
	 * meters, it will select the next waypoint in the path.
	 */
	private double tolerance = 1;

78
	public RealWorldStreetsMovement() {
79
80
		this.worldDimensions = Binder.getComponentOrNull(Topology.class)
				.getWorldDimensions();
81
82
		latLower = GPSCalculation.getLatLower();
		latUpper = GPSCalculation.getLatUpper();
83
84
		lonLeft = GPSCalculation.getLonLeft();
		lonRight = GPSCalculation.getLonRight();
85
86
87
88
89
	}
	
	private void init() {
		hopper = new GraphHopper().forServer();
		hopper.setOSMFile(osmFileLocation);
Clemens Krug's avatar
Clemens Krug committed
90

91
		// where to store graphhopper files?
92
93
94
95
96
97
98
99
100
101
		if (uniqueFolders) {
			Monitor.log(RealWorldStreetsMovement.class, Level.WARN,
					"Using per-simulation unique folders for GraphHopper temporary data in %s. Remember to delete them to prevent your disk from filling up.",
					graphFolderFiles);
			hopper.setGraphHopperLocation(graphFolderFiles + "/"
					+ UUID.randomUUID().toString());
		} else {
			hopper.setGraphHopperLocation(graphFolderFiles + "/"
					+ osmFileLocation.hashCode() + movementType);
		}
102
103
		hopper.setEncodingManager(new EncodingManager(movementType));
		hopper.importOrLoad();
Clemens Krug's avatar
Clemens Krug committed
104

105
106
107
		init = true;
	}

108
109
    public Either<PositionVector, Boolean> nextPosition(SimLocationActuator comp, PositionVector destination)
    {
Clemens Krug's avatar
Clemens Krug committed
110
        return nextPosition(comp, destination, defaultMovement);
111
112
    }

113
	public Either<PositionVector, Boolean> nextPosition(SimLocationActuator comp,
114
			PositionVector destination, String movementType) {
Clemens Krug's avatar
Clemens Krug committed
115
116
117
118

		if(movementType == null || movementType.equals("") || !this.movementType.contains(movementType))
			throw new AssertionError("Invalid movement type: " + movementType);

119
        if(!init) init();
120
		PositionVector newPosition = null;
Clemens Krug's avatar
Clemens Krug committed
121
122
        if (destination.distanceTo(comp.getRealPosition()) > getMovementSpeed(comp))
        {
123
124
			//if not set already for this node or new destination is different than last one
			PointList pointList;
Björn Richerzhagen's avatar
Björn Richerzhagen committed
125
			if(!movementPoints.containsKey(comp) || destination.distanceTo(movementPoints.get(comp).getDestination()) > 1.0) {
126
127
128
129
130
131
132
				double[] startPosition = transformOwnWorldWindowToGPS(comp.getRealPosition().getX(), comp.getRealPosition().getY());
				double[] destinationPosition = transformOwnWorldWindowToGPS(destination.getX(), destination.getY());
				GHRequest req = new GHRequest(startPosition[0], startPosition[1], destinationPosition[0], destinationPosition[1]).
					    setWeighting(navigationalType).
					    setVehicle(movementType).
					    setLocale(Locale.GERMANY);
				GHResponse rsp = hopper.route(req);
133
134
				//If the requested point is not in the map data, simple return the destination as next point
				if(rsp.hasErrors()) {
135
					Monitor.log(this.getClass(), Monitor.Level.ERROR, "Routing request for Host %s with starting point (" +
Clemens Krug's avatar
Clemens Krug committed
136
137
							"%f,%f), destination (%f,%f) and type %s failed with error: %s.", comp.getHost().getId().valueAsString(),startPosition[0], startPosition[1],
							destinationPosition[0], destinationPosition[1], movementType, rsp.getErrors());
138
139
					
					pointList = new PointList();
Clemens Krug's avatar
Clemens Krug committed
140
					pointList.add(new GHPoint(destinationPosition[0], destinationPosition[1]));
Björn Richerzhagen's avatar
Björn Richerzhagen committed
141
					movementPoints.put(comp, new RealWorldMovementPoints(comp.getRealPosition(), destination, pointList, 0));
142
143
				}
				else {
Clemens Krug's avatar
Clemens Krug committed
144
					pointList = rsp.getBest().getPoints();
Björn Richerzhagen's avatar
Björn Richerzhagen committed
145
					movementPoints.put(comp, new RealWorldMovementPoints(comp.getRealPosition(), destination, pointList, 0));
146
				}
147
148
			}
			else {
Björn Richerzhagen's avatar
Björn Richerzhagen committed
149
				pointList = movementPoints.get(comp).getPointList();
150
			}
151
			
Björn Richerzhagen's avatar
Björn Richerzhagen committed
152
			int actualIndex = movementPoints.get(comp).getActualIndex();
153
			int i = 0;
154
			for(GHPoint3D temp : pointList) {
155
156
157
158
				if(i==actualIndex) {
					PositionVector nextPoint = transformGPSWindowToOwnWorld(temp.getLat(), temp.getLon());
					newPosition = comp.getRealPosition().moveStep(nextPoint, getMovementSpeed(comp));
					
159
160
161
162
					if (nextPoint
							.distanceTo(comp.getRealPosition()) < tolerance) {
						actualIndex++;
					}
163
164
				}
				i++;
165
166
			}
			
Björn Richerzhagen's avatar
Björn Richerzhagen committed
167
			movementPoints.put(comp, new RealWorldMovementPoints(movementPoints.get(comp).getStart(), destination, pointList, actualIndex));
168
169
170
171
172
173
174
175
176
177
178
179
		}
		return new Left<PositionVector, Boolean>(newPosition);
	}
	
	/**
	 * Projects the world coordinates in the given gps window to the gps-coordinates
	 * @param x
	 * @param y
	 * @return The projected position in gps-coordinates (lat, long)
	 */
	private double[] transformOwnWorldWindowToGPS(double x, double y) {
		double[] gps_coordinates = new double[2];
180
		gps_coordinates[0] = latLower + (latUpper - latLower) * (worldDimensions.getY() - y)/worldDimensions.getY();
181
182
183
184
185
186
187
188
189
190
191
192
		gps_coordinates[1] = lonLeft + (lonRight - lonLeft) * x/worldDimensions.getX();
		return gps_coordinates;
	}
	
	/**
	 * Projects the gps coordinates in the given gps window to the world-coordinates given in world-dimensions
	 * @param lat
	 * @param lon
	 * @return The projected position in world-dimensions
	 */
	private PositionVector transformGPSWindowToOwnWorld(double lat, double lon) {
		double x = worldDimensions.getX() * (lon - lonLeft)/(lonRight - lonLeft);
193
		double y = worldDimensions.getY() - worldDimensions.getY() * (lat - latLower)/(latUpper - latLower);
194
195
196
197
		x = Math.max(0, x);
		x = Math.min(worldDimensions.getX(), x);
		y = Math.max(0, y);
		y = Math.min(worldDimensions.getY(), y);
198
199
		return new PositionVector(x, y);
	}
Clemens Krug's avatar
Clemens Krug committed
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252

	/**
	 * Returns a list of points representing the current route of the component. Points are
	 * in x / y values of the own world.
	 * @param ms the component
	 * @return list of movement points.
	 */
	public List<PositionVector> getMovementPoints(SimLocationActuator ms)
	{
		List<PositionVector> positions = new LinkedList<>();
		PointList pointList = movementPoints.get(ms).getPointList();

		pointList.forEach(p -> positions.add(new PositionVector(transformGPSWindowToOwnWorld(p.getLat(), p.getLon()))));

		return positions;
	}

	/**
	 * Calculates the length of a route in meters.
	 * @param start Starting position in own world coordinates (x / y)
	 * @param destination Destination on own world coordinates (x / y)
	 * @return the length of the route in meters.
	 */
	public double calculateRouteLength(PositionVector start, PositionVector destination)
	{
		PointList pointList;
			double[] startPosition = transformOwnWorldWindowToGPS(start.getX(), start.getY());
			double[] destinationPosition = transformOwnWorldWindowToGPS(destination.getX(), destination.getY());
			GHRequest req = new GHRequest(startPosition[0], startPosition[1], destinationPosition[0], destinationPosition[1]).
					setWeighting(navigationalType).
					setVehicle(movementType).
					setLocale(Locale.GERMANY);
			GHResponse rsp = hopper.route(req);

			//If the requested point is not in the map data, return -1
			if(rsp.hasErrors()) {
				return -1;
			}
			else {
				pointList = rsp.getBest().getPoints();
				return pointList.calcDistance(new DistanceCalc2D());
			}
	}

	/**
	 * Calculates the length of the current route of the SimLocationActuator.
	 * @param ms the component
	 * @return the length of the current route
	 */
	public double getCurrentRouteLength(SimLocationActuator ms)
	{
		return movementPoints.get(ms).getPointList().calcDistance(new DistanceCalc2D());
	}
253
254
255
256
257
258
259
260
261
262
263
	
	public void setOsmFileLocation(String osmFileLocation) {
		this.osmFileLocation = osmFileLocation;
	}

	public void setGraphFolderFiles(String graphFolderFiles) {
		this.graphFolderFiles = graphFolderFiles;
	}

	public void setMovementType(String movementType) {
		this.movementType = movementType;
Clemens Krug's avatar
Clemens Krug committed
264
		defaultMovement = movementType.split(",")[0];
265
266
267
268
269
	}

	public void setNavigationalType(String navigationalType) {
		this.navigationalType = navigationalType;
	}
270
	
271
272
	public void setWaypointTolerance(double tolerance) {
		this.tolerance = tolerance;
273
	}
274

275
276
277
278
279
	/**
	 * For large batch simulations, we need to prevent same-time access to
	 * garphhopper temp data. Therefore, this flag creates unique folders for
	 * each run (which, obviously, wastes a lot of space and comp-resources and
	 * should not be used in standalone, single-threaded demo mode...)
280
	 *
281
282
283
284
285
	 * @param uniqueFolders
	 */
	public void setCreateUniqueFolders(boolean uniqueFolders) {
		this.uniqueFolders = uniqueFolders;
	}
286
}