Java tutorial
/** * Copyright (C) 2012 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onebusaway.uk.network_rail.gtfs_realtime.graph; import java.awt.geom.Point2D; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Queue; import java.util.Set; import javax.inject.Inject; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.Parser; import org.apache.commons.cli.PosixParser; import org.onebusaway.collections.Min; import org.onebusaway.uk.atoc.timetable_parser.StationElement; import org.onebusaway.uk.network_rail.gtfs_realtime.NetworkRailGtfsRealtimeModule; import org.onebusaway.uk.network_rail.gtfs_realtime.TimetableService; import org.onebusaway.uk.network_rail.gtfs_realtime.graph.RailwayGraph.Node; import org.onebusaway.uk.network_rail.gtfs_realtime.graph.RailwayGraph.RailwayPath; import org.onebusaway.uk.network_rail.gtfs_realtime.graph.RawGraph.BerthPath; import org.onebusaway.uk.parser.ProjectionSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scala.actors.threadpool.Arrays; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; public class PositionBerthToStanoxGraphMain { private static Logger _log = LoggerFactory.getLogger(PositionBerthToStanoxGraphMain.class); private static final String ARG_ATOC_TIMETABLE_PATH = "atocTimetablePath"; private static final String ARG_GRAPH_PATH = "graphPath"; private static final String ARG_STATIONS_PATH = "stationsPath"; private static final String ARG_OSM_SHAPES_PATH = "osmShapesPath"; public static void main(String[] args) throws ParseException, IOException { PositionBerthToStanoxGraphMain m = new PositionBerthToStanoxGraphMain(); m.run(args); } private RawGraph _graph = new RawGraph(); private TimetableService _timetableService; private RailwayShapeService _railwayShapeService; private GraphLayout _graphLayout; private Map<RawStanoxNode, Location> _stanoxLocations = new HashMap<RawStanoxNode, Location>(); private Map<RawBerthNode, Location> _berthNodesToLocations = new HashMap<RawBerthNode, Location>(); private Map<RawBerthNode, List<Point2D.Double>> _berthNodesToPotentialLocations = new HashMap<RawBerthNode, List<Point2D.Double>>(); @Inject public void setTimetableService(TimetableService timetableService) { _timetableService = timetableService; } @Inject public void setRailwayShapeService(RailwayShapeService railwayShapeService) { _railwayShapeService = railwayShapeService; } @Inject public void setGraphLayout(GraphLayout graphLayout) { _graphLayout = graphLayout; } private void run(String[] args) throws ParseException, IOException { Options options = new Options(); buildOptions(options); Parser parser = new PosixParser(); CommandLine cli = parser.parse(options, args); Set<Module> modules = new HashSet<Module>(); NetworkRailGtfsRealtimeModule.addModuleAndDependencies(modules); Injector injector = Guice.createInjector(modules); injector.injectMembers(this); _timetableService.readScheduleData(new File(cli.getOptionValue(ARG_ATOC_TIMETABLE_PATH))); if (cli.hasOption(ARG_STATIONS_PATH)) { _timetableService.readStationLocations(new File(cli.getOptionValue(ARG_STATIONS_PATH))); } if (cli.hasOption(ARG_OSM_SHAPES_PATH)) { _railwayShapeService.readOsmShapeData(new File(cli.getOptionValue(ARG_OSM_SHAPES_PATH))); } _graph.read(new File(cli.getOptionValue(ARG_GRAPH_PATH))); buildStanoxLocations(); setBerthLocationsFromStanox(); loop(); exportLocationsToKml(); } private Location averagePoints(List<Point2D.Double> points) { double xTotal = 0; double yTotal = 0; for (Point2D.Double point : points) { xTotal += point.x; yTotal += point.y; } Location l = new Location(); l.x = xTotal / points.size(); l.y = yTotal / points.size(); Point2D.Double p = ProjectionSupport.convertToLatLon(l.x, l.y); l.lat = p.y; l.lon = p.x; return l; } private void buildOptions(Options options) { options.addOption(ARG_ATOC_TIMETABLE_PATH, true, "atoc timetable path"); options.addOption(ARG_GRAPH_PATH, true, "graph path"); options.addOption(ARG_STATIONS_PATH, true, ""); options.addOption(ARG_OSM_SHAPES_PATH, true, ""); } private void buildStanoxLocations() { long stanoxCount = 0; long stanoxWithLocation = 0; for (RawNode node : _graph.getNodes()) { if (node instanceof RawStanoxNode) { stanoxCount++; RawStanoxNode stanoxNode = (RawStanoxNode) node; Point2D.Double point = _timetableService.getBestLocationForStanox(stanoxNode.getId().getStanox()); if (point != null) { Location location = new Location(point); _stanoxLocations.put(stanoxNode, location); stanoxWithLocation++; } } } _log.info("stanox with location=" + stanoxWithLocation + "/" + stanoxCount); } private void setBerthLocationsFromStanox() { long total = 0; long hits = 0; for (RawBerthNode node : _graph.getBerthNodes()) { if (node.getId().toString().equals("WH_0347-WH_COUT")) { System.out.println("here"); } Location location = getStanoxLocationForBerth(node); if (location != null) { hits++; _berthNodesToLocations.put(node, location); } total++; } _log.info("berth nodes attached directly to stanox=" + hits + "/" + total); } private Location getStanoxLocationForBerth(RawBerthNode node) { for (RawStanoxNode stanoxNode : node.getStanox()) { Location location = _stanoxLocations.get(stanoxNode); if (location != null) { return location; } } return null; } private void loop() { while (true) { _log.info("nodes with locations=" + _berthNodesToLocations.size()); interpolateBerthLocations(); if (_berthNodesToPotentialLocations.isEmpty()) { return; } averageBerthLocations(); } } private void interpolateBerthLocations() { int index = 0; for (RawBerthNode rootNode : _berthNodesToLocations.keySet()) { if (index % 100 == 0) { _log.info("node=" + index + "/" + _berthNodesToLocations.keySet().size()); } index++; Location fromLocation = _berthNodesToLocations.get(rootNode); Queue<OrderedRawBerthNode> queue = new PriorityQueue<OrderedRawBerthNode>(); queue.add(new OrderedRawBerthNode(rootNode, null, 0.0)); Map<RawBerthNode, RawBerthNode> parents = new HashMap<RawBerthNode, RawBerthNode>(); Set<RawBerthNode> visited = new HashSet<RawBerthNode>(); while (!queue.isEmpty()) { OrderedRawBerthNode currentNode = queue.poll(); RawBerthNode node = currentNode.getNode(); if (!visited.add(node)) { continue; } parents.put(node, currentNode.getParent()); Location toLocation = _berthNodesToLocations.get(node); if (currentNode.getParent() != null && toLocation != null) { List<RawBerthNode> path = new ArrayList<RawBerthNode>(); RawBerthNode last = node; while (last != null) { path.add(last); last = parents.get(last); } if (path.size() <= 2) { break; } Collections.reverse(path); BerthPath berthPath = new BerthPath(path, currentNode.getDistance()); double d = fromLocation.getDistance(toLocation); if (d > 30000) { continue; } RailwayPath railwayPath = _railwayShapeService.getPath(fromLocation.getPoint(), toLocation.getPoint()); if (railwayPath != null) { snapBerthsToRailwayPath(berthPath, railwayPath); } break; } else { for (Map.Entry<RawBerthNode, List<Integer>> entry : node.getOutgoing().entrySet()) { RawBerthNode outgoing = entry.getKey(); int avgDuration = RawNode.average(entry.getValue()); queue.add(new OrderedRawBerthNode(outgoing, node, currentNode.getDistance() + avgDuration)); } } } } } private void averageBerthLocations() { for (Map.Entry<RawBerthNode, List<Point2D.Double>> entry : _berthNodesToPotentialLocations.entrySet()) { Location p = averagePoints(entry.getValue()); _berthNodesToLocations.put(entry.getKey(), p); } _berthNodesToPotentialLocations.clear(); } private void explore2(RawStanoxNode stanoxNode) { Location p = _stanoxLocations.get(stanoxNode); if (p == null) { return; } for (RawNode edge : stanoxNode.getOutgoing().keySet()) { if (!(edge instanceof RawStanoxNode)) { continue; } RawStanoxNode nearbyStanoxNode = (RawStanoxNode) edge; Location pTo = _stanoxLocations.get(nearbyStanoxNode); if (pTo == null) { continue; } double distance = p.getDistance(pTo); if (distance > 50000) { continue; } _log.info("from=" + stanoxNode + " to=" + nearbyStanoxNode + " distance=" + distance); _log.info(p.lat + " " + p.lon + " " + pTo.lat + " " + pTo.lon); BerthPath berthPath = getShortestPathBetweenNodes(stanoxNode, nearbyStanoxNode, distance); if (berthPath != null) { _log.info("railway path"); RailwayPath railwayPath = _railwayShapeService.getPath(p.getPoint(), pTo.getPoint()); if (railwayPath != null) { snapBerthsToRailwayPath(berthPath, railwayPath); } } } } private BerthPath getShortestPathBetweenNodes(RawStanoxNode fromStanoxNode, RawStanoxNode toStanoxNode, double distance) { double maxTime = (distance * 1.5 /* meters */) / (50 /* m/s */); Min<BerthPath> minPath = new Min<RawGraph.BerthPath>(); for (RawBerthNode fromNode : fromStanoxNode.getBerthConnections()) { for (RawBerthNode toNode : toStanoxNode.getBerthConnections()) { BerthPath path = _graph.getShortestPath(fromNode, toNode, maxTime); if (path != null) { minPath.add(path.distance, path); } } } return minPath.getMinElement(); } private void snapBerthsToRailwayPath(BerthPath berthPath, RailwayPath railwayPath) { double[] relativeBerthDistance = computeRelativeDistance(berthPath); double[] relativeShapeDistance = computeRelativeDistance(railwayPath); for (int i = 0; i < relativeBerthDistance.length; ++i) { double d = relativeBerthDistance[i]; Point2D.Double p = getBestPoint(railwayPath.nodes, relativeShapeDistance, d); RawBerthNode node = berthPath.nodes.get(i); List<Point2D.Double> locations = _berthNodesToPotentialLocations.get(node); if (locations == null) { locations = new ArrayList<Point2D.Double>(); _berthNodesToPotentialLocations.put(node, locations); } locations.add(p); } } private Point2D.Double getBestPoint(List<Node> nodes, double[] relativeShapeDistance, double d) { int index = Arrays.binarySearch(relativeShapeDistance, d); if (index >= 0) { return nodes.get(index).getPoint(); } if (index < 0) { index = -(index + 1); } if (index == 0) { return nodes.get(0).getPoint(); } if (index >= nodes.size()) { return nodes.get(nodes.size() - 1).getPoint(); } Node nodeFrom = nodes.get(index - 1); Node nodeTo = nodes.get(index); double dFrom = relativeShapeDistance[index - 1]; double dTo = relativeShapeDistance[index]; double ratio = (d - dFrom) / (dTo - dFrom); double x = nodeFrom.getX() + ratio * (nodeTo.getX() - nodeFrom.getX()); double y = nodeFrom.getY() + ratio * (nodeTo.getY() - nodeFrom.getY()); return new Point2D.Double(x, y); } private double[] computeRelativeDistance(BerthPath berthPath) { double[] distances = new double[berthPath.nodes.size()]; double totalDistance = berthPath.distance; double cumulativeDistance = 0.0; for (int i = 0; i < berthPath.nodes.size(); ++i) { RawBerthNode node = berthPath.nodes.get(i); if (i > 0) { RawBerthNode prev = berthPath.nodes.get(i - 1); double duration = prev.getOutgoingAverageDuration(node); cumulativeDistance += duration; } distances[i] = cumulativeDistance / totalDistance; } return distances; } private double[] computeRelativeDistance(RailwayPath railwayPath) { double[] distances = new double[railwayPath.nodes.size()]; double totalDistance = railwayPath.distance; double cumulativeDistance = 0.0; for (int i = 0; i < railwayPath.nodes.size(); ++i) { RailwayGraph.Node node = railwayPath.nodes.get(i); if (i > 0) { RailwayGraph.Node prev = railwayPath.nodes.get(i - 1); double distance = prev.getDistance(node); cumulativeDistance += distance; } distances[i] = cumulativeDistance / totalDistance; } return distances; } private void explore(RawStanoxNode stanoxNode) { Set<RawBerthNode> connections = stanoxNode.getBerthConnections(); if (connections.isEmpty()) { return; } explore(connections, stanoxNode); } private void explore(Set<RawBerthNode> connections, RawStanoxNode stanoxNode) { Queue<OrderedRawBerthNode> queue = new PriorityQueue<OrderedRawBerthNode>(); Set<RawBerthNode> visited = new HashSet<RawBerthNode>(); int openCount = 0; for (RawBerthNode connection : connections) { queue.add(new OrderedRawBerthNode(connection, null, 0.0)); openCount++; } Map<RawBerthNode, RawBerthNode> parents = new HashMap<RawBerthNode, RawBerthNode>(); while (!queue.isEmpty()) { OrderedRawBerthNode currentNode = queue.poll(); RawBerthNode node = currentNode.getNode(); boolean isOpen = currentNode.isOpen(); if (isOpen) { openCount--; } else if (openCount == 0) { return; } if (visited.contains(node)) { continue; } visited.add(node); parents.put(node, currentNode.getParent()); Set<RawStanoxNode> stanoxes = node.getStanox(); if (stanoxes.size() > 0 && !stanoxes.contains(stanoxNode)) { _log.info(node + " stanoxes=" + stanoxes + " " + currentNode.getDistance() + " open=" + openCount); RawBerthNode c = node; while (c != null) { _log.info(" " + c); c = parents.get(c); } isOpen = false; } for (Map.Entry<RawBerthNode, List<Integer>> entry : node.getOutgoing().entrySet()) { RawBerthNode outgoing = entry.getKey(); int avgDuration = RawNode.average(entry.getValue()); queue.add(new OrderedRawBerthNode(outgoing, node, currentNode.getDistance() + avgDuration, isOpen)); if (isOpen) { openCount++; } } } } private void fillLocationsForKnownStanox(Map<RawBerthNode, Location> nodesToLocations) { } private Location getLocationForStanox(int stanox) { double xTotal = 0; double yTotal = 0; int count = 0; for (String tiploc : _timetableService.getTiplocsForStanox(stanox)) { StationElement station = _timetableService.getStationForTiploc(tiploc); if (station != null) { xTotal += station.getEasting(); yTotal += station.getNorthing(); count++; } } if (count == 0) { return null; } Location location = new Location(); location.fixed = true; location.x = xTotal / count; location.y = yTotal / count; return location; } private void fillLocationsForUnknownStanox(Map<RawBerthNode, Location> nodesToLocations) { int total = 0; int problem = 0; Map<RawBerthNode, Location> updates = new HashMap<RawBerthNode, Location>(); _log.info("updates=" + updates.size()); nodesToLocations.putAll(updates); } private Map<Location, Integer> getNearbyNodesWithLocation(Map<RawBerthNode, Location> nodesToLocations, RawBerthNode source, int minCount) { Map<Location, Integer> locationsAndTime = new HashMap<Location, Integer>(); PriorityQueue<OrderedNode> queue = new PriorityQueue<OrderedNode>(); queue.add(new OrderedNode(source, 0)); Set<RawBerthNode> visited = new HashSet<RawBerthNode>(); visited.add(source); Map<RawBerthNode, Integer> minTimeToSource = new HashMap<RawBerthNode, Integer>(); while (!queue.isEmpty()) { OrderedNode orderedNode = queue.poll(); RawBerthNode node = orderedNode.node; if (minTimeToSource.containsKey(node)) { continue; } int time = orderedNode.value; minTimeToSource.put(node, time); if (nodesToLocations.containsKey(node)) { locationsAndTime.put(nodesToLocations.get(node), time); if (locationsAndTime.size() >= minCount) { return locationsAndTime; } } for (Edge edge : node.getEdges()) { RawBerthNode to = edge.getTo(); int proposedTime = edge.getAverageDuration() + time; if (!minTimeToSource.containsKey(to)) { queue.add(new OrderedNode(to, proposedTime)); } } } return locationsAndTime; } private void exportLocationsToKml() throws FileNotFoundException { PrintWriter out = new PrintWriter("/Users/bdferris/Documents/uk-rail/graph.kml"); out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); out.println("<kml xmlns=\"http://www.opengis.net/kml/2.2\">"); out.println("<Document>"); out.println(" <Style id=\"stanox\">"); out.println(" <IconStyle>"); out.println(" <color>ff000000</color>"); out.println(" </IconStyle>"); out.println(" </Style>"); for (Map.Entry<RawStanoxNode, Location> entry : _stanoxLocations.entrySet()) { RawStanoxNode node = entry.getKey(); Location location = entry.getValue(); out.println(" <Placemark>"); out.println(" <name>" + node.getId() + "</name>"); out.println(" <styleUrl>stanox</styleUrl>"); out.println(" <Point>"); out.println(" <coordinates>" + location.lon + "," + location.lat + ",0</coordinates>"); out.println(" </Point>"); out.println(" </Placemark>"); } for (Map.Entry<RawBerthNode, Location> entry : _berthNodesToLocations.entrySet()) { RawBerthNode node = entry.getKey(); Location location = entry.getValue(); out.println(" <Placemark>"); out.println(" <name>" + node.getId() + "</name>"); out.println(" <MultiGeometry>"); out.println(" <Point>"); out.println(" <coordinates>" + location.lon + "," + location.lat + ",0</coordinates>"); out.println(" </Point>"); for (RawBerthNode edge : node.getOutgoing().keySet()) { Location edgeLocation = _berthNodesToLocations.get(edge); if (edgeLocation != null) { out.println(" <LineString>"); out.println(" <coordinates>" + location.lon + "," + location.lat + ",0 " + edgeLocation.lon + "," + edgeLocation.lat + ",0</coordinates>"); out.println(" </LineString>"); } } out.println(" </MultiGeometry>"); out.println(" </Placemark>"); } out.println("</Document>"); out.println("</kml>"); out.close(); // RailwayGraph graph = _railwayShapeService.getGraph(); // Set<RailwayGraph.Node> remaining = new HashSet<RailwayGraph.Node>( // graph.getNodes()); // while (!remaining.isEmpty()) { // Node node = remaining.iterator().next(); // while (true) { // if (!remaining.remove(node)) { // break; // } // } // List<Node> path = exploreRailwayPath(node, remaining); // } } private Location computeCentroid(Set<Location> locations) { double xTotal = 0; double yTotal = 0; for (Location location : locations) { xTotal += location.x; yTotal += location.y; } Location location = new Location(); location.x = xTotal / locations.size(); location.y = yTotal / locations.size(); return location; } private static class OrderedNode implements Comparable<OrderedNode> { public final RawBerthNode node; public final int value; public OrderedNode(RawBerthNode node, int value) { this.node = node; this.value = value; } @Override public int compareTo(OrderedNode o) { return Double.compare(this.value, o.value); } } }