package pl.edu.agh.logic;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newArrayListWithCapacity;
import static pl.edu.agh.spatial.HaversineDistanceCalculator.EARTH_DISTANCE_CALCULATOR;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import pl.edu.agh.model.Way;
import pl.edu.agh.spatial.GeometryHelper;
import pl.edu.agh.spatial.Vector;
import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;
public class RoadNetwork {
private static final double ROAD_SHIFT = 0.00003;
private GeometryFactory geometryFactory;
private List<Road> roads;
private Multimap<Integer, Road> roadsBySource;
private Multimap<Integer, Road> roadsById;
public RoadNetwork(List<Way> ways, GeometryFactory geometryFactory) {
this.geometryFactory = geometryFactory;
this.roadsBySource = ArrayListMultimap.create();
this.roadsById = ArrayListMultimap.create();
this.roads = newArrayListWithCapacity(ways.size());
for (Way way : ways) {
registerRoad(way);
}
}
private void registerRoad(Way way) {
if (way.isOneWay()) {
registerOneWayRoad(way.getLineString(), way.getGid(), way.getSource(), way.getTarget());
} else {
registerTwoWayRoad(way.getLineString(), way.getGid(), way.getSource(), way.getTarget());
}
}
private void registerOneWayRoad(LineString lineString, int id, int source, int target) {
registerRoad(lineString, id, source, target, false, 0.0);
}
private void registerTwoWayRoad(LineString lineString, int id, int source, int target) {
registerRoad(lineString, id, source, target, false, ROAD_SHIFT);
registerRoad((LineString) lineString.reverse(), id, target, source, true, ROAD_SHIFT);
}
private void registerRoad(LineString lineString, int id, int source, int target, boolean reversed,
double translation) {
List<LineString> segments = newArrayListWithCapacity(lineString.getNumPoints() - 1);
for (int i = 0; i < lineString.getNumPoints() - 1; i++) {
segments.add(createRoadSegment(lineString.getPointN(i), lineString.getPointN(i + 1), translation));
}
Road registeredRoad = new Road(segments, id, source, target, reversed);
roads.add(registeredRoad);
roadsBySource.put(registeredRoad.getSource(), registeredRoad);
roadsById.put(registeredRoad.getId(), registeredRoad);
}
private LineString createRoadSegment(Point start, Point end, double distance) {
Vector segment = new Vector(start, end, geometryFactory).translatePerpendicularly(distance,
Vector.Direction.RIGHT);
return geometryFactory.createLineString(new Coordinate[] { segment.getBegin().getCoordinate(),
segment.getEnd().getCoordinate() });
}
public List<Path> getNearestPathsForPoint(Point point, double degreeRadius, double meterRadius) {
List<Path> paths = newArrayList();
final Envelope pointNeighbourhood = GeometryHelper.getExpandedEnvelope(point, degreeRadius);
Iterable<Road> roadsInsideNeighbourhood = Iterables.filter(roads, new Predicate<Road>() {
@Override
public boolean apply(Road road) {
return GeometryHelper.getLineSegmentListEnvelope(road).intersects(pointNeighbourhood);
}
});
for (Road road : roadsInsideNeighbourhood) {
if (EARTH_DISTANCE_CALCULATOR.distance(point.getCoordinate(), road.nearestPoint(point)) < meterRadius) {
paths.add(Path.createPath(point.getCoordinate(), road, (Date) point.getUserData()));
}
}
return paths;
}
public Collection<Road> getRoadsBySource(int source) {
return roadsBySource.get(source);
}
public Collection<Road> getRoadsById(int id) {
return roadsById.get(id);
}
}