RealWorldStreetsMovement.java 12.8 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
/*
 * 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;

23
24
25
26
27
28
29
30
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;

31
32
33
import com.graphhopper.GHRequest;
import com.graphhopper.GHResponse;
import com.graphhopper.GraphHopper;
34
import com.graphhopper.routing.util.EdgeFilter;
35
import com.graphhopper.routing.util.EncodingManager;
36
37
import com.graphhopper.storage.index.LocationIndex;
import com.graphhopper.storage.index.QueryResult;
Clemens Krug's avatar
Clemens Krug committed
38
import com.graphhopper.util.DistanceCalc2D;
39
import com.graphhopper.util.EdgeIteratorState;
40
import com.graphhopper.util.PointList;
41
import com.graphhopper.util.shapes.GHPoint;
42

43
import de.tud.kom.p2psim.api.topology.Topology;
44
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
45
import de.tud.kom.p2psim.impl.topology.PositionVector;
46
import de.tud.kom.p2psim.impl.topology.movement.modularosm.GPSCalculation;
47
import de.tud.kom.p2psim.impl.topology.movement.modularosm.RouteSensorComponent;
48
49
import de.tud.kom.p2psim.impl.util.Either;
import de.tud.kom.p2psim.impl.util.Left;
50
import de.tudarmstadt.maki.simonstrator.api.Binder;
51
import de.tudarmstadt.maki.simonstrator.api.Monitor;
52
import de.tudarmstadt.maki.simonstrator.api.Monitor.Level;
53
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException;
Clemens Krug's avatar
Clemens Krug committed
54

55
56
/**
 * This movement strategy uses the data from osm and navigates the nodes throught streets to the destination
Clemens Krug's avatar
Clemens Krug committed
57
 *
58
 * 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
59
 * top right corner and not, as intended, straight to their destination.
60
61
62
63
64
65
 * 
 * @author Martin Hellwig
 * @version 1.0, 07.07.2015
 */
public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
	
66
	private static PositionVector worldDimensions;
67
	private GraphHopper hopper;
68
	private LocationIndex index;
69
70
	private boolean init = false;
	
71
72
73
74
	private static HashMap<SimLocationActuator, RouteImpl> currentRoutes = new HashMap<>();

	private Map<SimLocationActuator, RouteSensorComponent> routeSensorComponents = new LinkedHashMap<>();

75
76
77
	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
78
	private String defaultMovement;
79
	private String navigationalType; //fastest,
80
81
82
83
	private static double latLower; //Values from -90 to 90; always smaller than latUpper
	private static double latUpper; //Values from -90 to 90
	private static double lonLeft; //Values from -180 to 180; Always smaller than lonRight
	private static double lonRight; //Values from -180 to 180
84
	private boolean uniqueFolders;
85

86
87
88
89
90
91
	/**
	 * 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;

92
	public RealWorldStreetsMovement() {
93
		worldDimensions = Binder.getComponentOrNull(Topology.class)
94
				.getWorldDimensions();
95
96
		latLower = GPSCalculation.getLatLower();
		latUpper = GPSCalculation.getLatUpper();
97
98
		lonLeft = GPSCalculation.getLonLeft();
		lonRight = GPSCalculation.getLonRight();
99

100
101
102
103
104
	}
	
	private void init() {
		hopper = new GraphHopper().forServer();
		hopper.setOSMFile(osmFileLocation);
Clemens Krug's avatar
Clemens Krug committed
105

106
		// where to store graphhopper files?
107
108
109
110
111
112
113
114
115
116
		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);
		}
117
118
		hopper.setEncodingManager(new EncodingManager(movementType));
		hopper.importOrLoad();
119
		index = hopper.getLocationIndex();
Clemens Krug's avatar
Clemens Krug committed
120

121
122
123
		init = true;
	}

124
125
126
	@Override
	public Either<PositionVector, Boolean> nextPosition(
			SimLocationActuator comp, PositionVector destination) {
Clemens Krug's avatar
Clemens Krug committed
127
        return nextPosition(comp, destination, defaultMovement);
128
129
    }

130
	public Either<PositionVector, Boolean> nextPosition(SimLocationActuator comp,
131
			PositionVector destination, String movementType) {
Clemens Krug's avatar
Clemens Krug committed
132

133
134
		if (movementType == null || movementType.equals("")
				|| !this.movementType.contains(movementType)) {
Clemens Krug's avatar
Clemens Krug committed
135
			throw new AssertionError("Invalid movement type: " + movementType);
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
		}

		if (!init) {
			init();
		}

		RouteSensorComponent routeSensor = routeSensorComponents.get(comp);
		if (routeSensor == null) {
			try {
				routeSensorComponents.put(comp, comp.getHost()
						.getComponent(RouteSensorComponent.class));
				routeSensor = routeSensorComponents.get(comp);
			} catch (ComponentNotAvailableException e) {
				throw new AssertionError(
						"The movement model needs to provide RouteSensorComponents.");
			}
		}

Clemens Krug's avatar
Clemens Krug committed
154

155
		PositionVector newPosition = null;
156
157
		if (destination
				.distanceTo(comp.getRealPosition()) > getMovementSpeed(comp)) {
158
			//if not set already for this node or new destination is different than last one
159
			RouteImpl trajectory = currentRoutes.get(comp);
160
			if(trajectory == null || destination.distanceTo(trajectory.getDestination()) > 1.0) {
161
162
163
164
165
166
167
				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);
168
169
				//If the requested point is not in the map data, simple return the destination as next point
				if(rsp.hasErrors()) {
170
					Monitor.log(this.getClass(), Monitor.Level.ERROR, "Routing request for Host %s with starting point (" +
Clemens Krug's avatar
Clemens Krug committed
171
172
							"%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());
173
					
174
					PointList pointList = new PointList();
Clemens Krug's avatar
Clemens Krug committed
175
					pointList.add(new GHPoint(destinationPosition[0], destinationPosition[1]));
176
177
178
					trajectory = new RouteImpl(comp.getRealPosition(), destination, pointList);
					currentRoutes.put(comp, trajectory);

179
180
				} else {
					PointList pointList = rsp.getBest().getPoints();
181
182
183
184
185
186
187
188
189
190
191
192
193
194

					if (isCalculateRouteSegments()) {
						/*
						 * Obtain segment IDs along the route.
						 */
						trajectory = new RouteImpl(comp.getRealPosition(),
								destination, pointList,
								routeSensor.getSegmentListeners(),
								calculateSegments(pointList));
					} else {
						trajectory = new RouteImpl(comp.getRealPosition(),
								destination, pointList);
					}
					currentRoutes.put(comp, trajectory);
195
				}
196
				routeSensor.setNewRoute(trajectory);
197
			}
198
			newPosition = trajectory.updateCurrentLocation(comp, getMovementSpeed(comp), tolerance);
199
200
201
202
203
		}
		return new Left<PositionVector, Boolean>(newPosition);
	}
	
	/**
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
	 * Calculate the route segments along the given point list.
	 * 
	 * @return
	 */
	private List<RouteSegmentImpl> calculateSegments(PointList pointList) {
		int ptIdx = 0;
		int prevEdgeId = -1;
		List<RouteSegmentImpl> segments = new LinkedList<>();
		RouteSegmentImpl lastSegment = null;
		for (GHPoint point : pointList) {
			QueryResult qr = index.findClosest(point.getLat(), point.getLon(),
					EdgeFilter.ALL_EDGES);
			EdgeIteratorState nearEdge = qr.getClosestEdge();
			if (nearEdge != null) {
				int edgeId = nearEdge.getEdge();
				if (edgeId != prevEdgeId) {
					if (lastSegment != null) {
						lastSegment.addRouteLocation(
								transformGPSWindowToOwnWorld(point.getLat(),
										point.getLon()));
					}
					lastSegment = new RouteSegmentImpl(Integer.toString(edgeId),
							ptIdx, transformGPSWindowToOwnWorld(point.getLat(),
									point.getLon()));
					segments.add(lastSegment);
				} else {
					lastSegment.addRouteLocation(transformGPSWindowToOwnWorld(
							point.getLat(), point.getLon()));
				}
			}
			ptIdx++;
		}
		return segments;
	}

	/**
	 * Projects the world coordinates in the given gps window to the
	 * gps-coordinates
	 * 
243
244
245
246
247
248
	 * @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];
249
		gps_coordinates[0] = latLower + (latUpper - latLower) * (worldDimensions.getY() - y)/worldDimensions.getY();
250
251
252
253
254
255
256
257
258
259
		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
	 */
260
	public static PositionVector transformGPSWindowToOwnWorld(double lat, double lon) {
261
		double x = worldDimensions.getX() * (lon - lonLeft)/(lonRight - lonLeft);
262
		double y = worldDimensions.getY() - worldDimensions.getY() * (lat - latLower)/(latUpper - latLower);
263
264
265
266
		x = Math.max(0, x);
		x = Math.min(worldDimensions.getX(), x);
		y = Math.max(0, y);
		y = Math.min(worldDimensions.getY(), y);
267
268
		return new PositionVector(x, y);
	}
Clemens Krug's avatar
Clemens Krug committed
269
270
271
272
273
274
275
276
277
278

	/**
	 * 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<>();
279
		PointList pointList = currentRoutes.get(ms).getPointList();
Clemens Krug's avatar
Clemens Krug committed
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319

		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)
	{
320
		return currentRoutes.get(ms).getPointList().calcDistance(new DistanceCalc2D());
Clemens Krug's avatar
Clemens Krug committed
321
	}
322
323
324
325
326
327
328
329
330
331
332
	
	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
333
		defaultMovement = movementType.split(",")[0];
334
335
336
337
338
	}

	public void setNavigationalType(String navigationalType) {
		this.navigationalType = navigationalType;
	}
339
	
340
341
	public void setWaypointTolerance(double tolerance) {
		this.tolerance = tolerance;
342
	}
343

344
345
346
347
348
	/**
	 * 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...)
349
	 *
350
351
352
353
354
	 * @param uniqueFolders
	 */
	public void setCreateUniqueFolders(boolean uniqueFolders) {
		this.uniqueFolders = uniqueFolders;
	}
355
}