All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rinde.sim.util.MapPreprocessor Maven / Gradle / Ivy

There is a newer version: 4.4.6
Show newest version
/**
 * 
 */
package rinde.sim.util;

import static java.util.Arrays.asList;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import rinde.sim.core.graph.Connection;
import rinde.sim.core.graph.ConnectionData;
import rinde.sim.core.graph.Graph;
import rinde.sim.core.graph.Graphs;
import rinde.sim.core.graph.LengthData;
import rinde.sim.core.graph.MultiAttributeData;
import rinde.sim.core.graph.MultimapGraph;
import rinde.sim.core.graph.PathNotFoundException;
import rinde.sim.core.graph.Point;
import rinde.sim.core.graph.TableGraph;
import rinde.sim.serializers.DotGraphSerializer;

import com.google.common.collect.Sets;

/**
 * @author Rinde van Lon ([email protected])
 * 
 */
public class MapPreprocessor {

    private static  Graph hack(Graph graph) {
        Graph newGraph = new MultimapGraph();
        newGraph.merge(graph);

        HashSet connected = new HashSet();
        HashSet neighbors = new HashSet();

        Point root = graph.getNodes().iterator().next();
        connected.add(root);
        neighbors.addAll(graph.getOutgoingConnections(root));

        fixCluster(newGraph, connected, neighbors, new HashSet());
        return newGraph;
    }

    public static  Graph connect2(Graph graph) {
        Graph newGraph = new MultimapGraph();
        newGraph.merge(graph);

        HashSet connected = new HashSet();
        HashSet neighbors = new HashSet();

        Point root = graph.getNodes().iterator().next();
        connected.add(root);
        neighbors.addAll(graph.getOutgoingConnections(root));
        fixCluster(newGraph, connected, neighbors, new HashSet());

        Set unconnected;
        while (!(unconnected = Sets.difference(newGraph.getNodes(), connected))
                .isEmpty()) {
            Point p = unconnected.iterator().next();
            System.out.println("unconnected: " + unconnected.size());
            HashSet cluster = new HashSet(asList(p));
            fixCluster(newGraph, cluster, new HashSet(
                    newGraph.getOutgoingConnections(p)), connected);
            // System.out.println("cluster: " + cluster);
            Tuple pair = findClosestPair(cluster, connected);

            if (!isConnected(newGraph, cluster, connected)) {
                // System.out.println("not connection from cluster -> main");
                newGraph.addConnection(pair.getKey(), pair.getValue());
                newGraph.addConnection(pair.getValue(), pair.getKey());
            }

            connected.addAll(cluster);
        }

        // newGraph.put(findClosest(p, connected), p);
        // connected.add(p);
        // }

        //
        // if( neighbour n isConnectedWith(n, connectedSet) )
        // add to connected set
        // else
        // create connection from n to one of the connectedSet (maybe make one
        // of them bidirectional)
        // endwhile

        // traverse all unvisited nodes, create connection between each
        // unvisited node and one of the connected nodes.

        return newGraph;
    }

    private static  void fixCluster(
            Graph newGraph, HashSet connected,
            HashSet neighbors, HashSet otherClusters) {
        // System.out.println(">> fixCluster");
        while (!neighbors.isEmpty()) {
            Point n = neighbors.iterator().next();
            assert n != null;
            // System.out.println(n);
            neighbors.remove(n);
            // if this point is also in a other cluster, we don't have to check
            // its neighbors
            if (!otherClusters.contains(n)) {
                for (Point b : newGraph.getOutgoingConnections(n)) {
                    if (b != null && !connected.contains(b)
                            && !neighbors.contains(b)) {

                        neighbors.add(b);
                    }
                }
            }
            if (!isConnectedWith(newGraph, n, connected)) {
                assert n != null;
                assert !connected.isEmpty();
                assert !newGraph.isEmpty();
                newGraph.addConnection(n, findClosest(n, connected));// connect
                                                                     // it
            }
            connected.add(n);
        }
    }

    /**
     * @param n
     * @param set
     */
    private static Point findClosest(Point n, Set set) {
        double minDist = Double.POSITIVE_INFINITY;
        Point closest = null;
        for (Point p : set) {
            assert p != null;
            double dist = Point.distance(p, n);
            if (dist < minDist) {
                minDist = dist;
                closest = p;
            }
        }
        assert closest != null;
        return closest;
    }

    private static Tuple findClosestPair(Set set1,
            Set set2) {
        double minDist = Double.POSITIVE_INFINITY;
        Tuple closestPair = null;
        for (Point p : set1) {
            Point c = findClosest(p, set2);
            double dist = Point.distance(p, c);
            if (dist < minDist) {
                minDist = dist;
                closestPair = new Tuple(p, c);
            }
        }
        return closestPair;
    }

    private static  boolean isConnected(
            Graph graph, Set set1, Set set2) {
        HashSet visited = new HashSet();
        HashSet queue = new HashSet();
        queue.addAll(set1);
        while (!queue.isEmpty()) {
            Point b = queue.iterator().next();
            queue.remove(b);
            Collection neighbours = graph.getOutgoingConnections(b);
            for (Point n : neighbours) {
                if (set2.contains(n)) {
                    return true;
                }
                if (!visited.contains(n)) {
                    queue.add(n);
                }
            }
            visited.add(b);
        }
        return false;
    }

    /**
     * checks if it is possible to reach a point in set by
     * following the outgoing arcs in point p.
     * @param graph
     * @param p
     * @param set
     * @return
     */
    private static boolean isConnectedWith(Graph graph, Point p, Set set) {
        return isConnected(graph, new HashSet(asList(p)), set);
    }

    public static  Graph removeUnconnectedSubGraphs(
            Graph graph, E empty) {
        Graph currentGraph = new TableGraph(empty);
        currentGraph.merge(graph);

        List> result = findNotFullyConnectedNodes(currentGraph);
        int totalSize = 0;
        int biggestIndex = -1;
        int biggestSize = -1;
        for (int i = 0; i < result.size(); i++) {
            totalSize += result.get(i).size();
            if (result.get(i).size() > biggestSize) {
                biggestSize = result.get(i).size();
                biggestIndex = i;
            }
        }

        for (int i = 0; i < result.size(); i++) {
            if (i != biggestIndex) {
                System.out.println("removing: " + i + " " + result.size());
                for (Point p : result.get(i)) {
                    currentGraph.removeNode(p);
                }
            }
        }

        System.out.println("Removed " + (result.size() - 1)
                + " subgraphs, with total size "
                + (totalSize - currentGraph.getNumberOfNodes())
                + " nodes, resulting graph has: "
                + currentGraph.getNumberOfNodes() + " nodes.");
        System.out.println(totalSize);
        System.out.println(currentGraph.getNumberOfNodes());

        return currentGraph;
    }

    public static  Graph connect(Graph graph) {
        Graph currentGraph = new MultimapGraph();
        currentGraph.merge(graph);

        List> result = findNotFullyConnectedNodes(currentGraph);

        boolean isFullyConnected = false;
        while (!isFullyConnected) {

            Set unconnected = result.get(0);
            Set connected = result.get(1);

            double minDist = Double.POSITIVE_INFINITY;
            Tuple connection = null;
            for (Point u : unconnected) {
                for (Point c : connected) {
                    double dist = Point.distance(u, c);
                    if (dist < minDist) {
                        minDist = dist;
                        connection = new Tuple(u, c);
                    }
                }
            }

            if (!currentGraph.hasConnection(connection.getKey(), connection
                    .getValue())) {
                currentGraph.addConnection(connection.getKey(), connection
                        .getValue());
            }
            if (!currentGraph.hasConnection(connection.getValue(), connection
                    .getKey())) {
                currentGraph.addConnection(connection.getValue(), connection
                        .getKey());
            }
            result = findNotFullyConnectedNodes(currentGraph);
            if (result.get(0).isEmpty()) {
                isFullyConnected = true;
            }
        }

        return currentGraph;
    }

    private static  Set unseenNeighbours(
            Graph g, Set seen, Set current) {
        HashSet set = new HashSet();
        for (Point p : current) {
            set.addAll(g.getIncomingConnections(p));
            set.addAll(g.getOutgoingConnections(p));
        }
        set.removeAll(seen);
        return set;
    }

    private static  boolean isConnected(Graph g,
            Point from, Point to, Set subgraph) {

        Point cur = from;
        Set seen = new HashSet();

        while (true) {
            seen.add(cur);
            Set ns = new HashSet(g.getOutgoingConnections(cur));

            ns.retainAll(subgraph);
            ns.removeAll(seen);

            if (ns.contains(to)) {
                return true;
            } else if (ns.isEmpty()) {
                return false;
            } else if (ns.size() == 1) {
                cur = ns.iterator().next();
            } else {
                throw new RuntimeException("not expected..");
            }

        }

    }

    public static  double getLength(Graph g,
            Point from, Point to) {
        E connectionData = g.connectionData(from, to);
        if (connectionData == null || Double.isNaN(connectionData.getLength())) {
            return Point.distance(from, to);
        } else {
            return connectionData.getLength();
        }
    }

    public static  Graph simplify(Graph g,
            E empty) {
        TableGraph newGraph = new TableGraph(empty);
        newGraph.merge(g);
        boolean working = true;

        // int iterations = 0;
        while (working) {
            // System.out.println("starting iteration: " + iterations);
            // iterations++;
            boolean edit = false;
            // System.out.println(newGraph.getConnections());

            HashSet> connections = new HashSet>(
                    newGraph.getConnections());
            HashSet> removeList = new HashSet>();
            for (Connection connection : connections) {
                if (removeList.contains(connection)) {
                    continue;
                }
                Point left = connection.from;
                Point right = connection.to;

                ContractType type = isContractable(newGraph, left, right);
                // System.out.println(type + " " + left + " " + right);
                if (type == ContractType.NO) {
                    continue;
                } else {
                    // double length = getLength(newGraph, left, right);

                    E removeEdgeData = newGraph.connectionData(left, right);
                    double removeLength = newGraph
                            .connectionLength(left, right);
                    removeList.add(newGraph.getConnection(left, right));
                    newGraph.removeConnection(left, right);

                    Point removeNode = (type == ContractType.RIGHT) ? right
                            : left;
                    Point mergeNode = (type == ContractType.RIGHT) ? left
                            : right;

                    // System.out.println("remove: " + removeNode);
                    // System.out.println("merge into: " + mergeNode);
                    for (Point outgoing : newGraph
                            .getOutgoingConnections(removeNode)) {
                        if (!outgoing.equals(mergeNode)) {
                            E edgeData = newGraph
                                    .connectionData(removeNode, outgoing);
                            double edgeLength = newGraph
                                    .connectionLength(removeNode, outgoing);
                            // double newLength = length + getLength(newGraph,
                            // removeNode, outgoing);

                            if (!newGraph.hasConnection(mergeNode, outgoing)) {
                                newGraph.addConnection(mergeNode, outgoing, mergeEdgeData(empty, removeEdgeData, removeLength, edgeData, edgeLength));
                            }
                            // if (clazz.equals(LengthEdgeData.class)) {
                            // newGraph.addConnection(mergeNode, outgoing, (E)
                            // new LengthEdgeData(newLength));
                            // } else if
                            // (clazz.equals(MultiAttributeEdgeData.class)) {
                            // throw new UnsupportedOperationException();
                            // TODO Merge the MultiAttributeEdgeData object
                            // here!
                            // MultiAttributeEdgeData maed =
                            // (MultiAttributeEdgeData)
                            // newGraph.connectionData(removeNode, outgoing);
                            //
                            // if(!Double.isNaN(maed.getMaxSpeed())){
                            //
                            // }
                            // newGraph.addConnection(mergeNode, outgoing, (E)
                            // new MultiAttributeEdgeData(newLength));
                            // } else {
                            // throw new UnsupportedOperationException();
                            // }
                        }
                    }
                    for (Point incoming : newGraph
                            .getIncomingConnections(removeNode)) {
                        if (!incoming.equals(mergeNode)) {
                            E edgeData = newGraph
                                    .connectionData(incoming, removeNode);
                            double edgeLength = newGraph
                                    .connectionLength(incoming, removeNode);

                            // double newLength = length + getLength(newGraph,
                            // incoming, removeNode);
                            if (!newGraph.hasConnection(incoming, mergeNode)) {
                                newGraph.addConnection(incoming, mergeNode, mergeEdgeData(empty, edgeData, edgeLength, removeEdgeData, removeLength));
                            }
                            // if (clazz.equals(LengthEdgeData.class)) {
                            //
                            // } else {
                            // throw new UnsupportedOperationException();
                            // }

                            // newGraph.addConnection(incoming, mergeNode, new
                            // LengthEdgeData(newLength));
                        }
                    }

                    Collection in = newGraph
                            .getIncomingConnections(removeNode);
                    for (Point p : in) {
                        removeList.add(newGraph.getConnection(p, removeNode));
                    }
                    Collection out = newGraph
                            .getOutgoingConnections(removeNode);
                    for (Point p : out) {
                        removeList.add(newGraph.getConnection(removeNode, p));
                    }

                    newGraph.removeNode(removeNode);
                    edit = true;
                    // break;
                }
            }
            if (!edit) {
                working = false;
            }
        }
        return newGraph;
    }

    // TODO also check if input values are valid!!
    // TODO do something with maxSpeed!!
    @SuppressWarnings("unchecked")
    static  E mergeEdgeData(E empty, E e1, double l1,
            E e2, double l2) {
        if (empty instanceof LengthData) {
            return (E) new LengthData(l1 + l2);
        } else if (empty instanceof MultiAttributeData) {
            return (E) new MultiAttributeData(l1 + l2);
        }
        throw new IllegalArgumentException(
                "EdgeData objects are of unknown type");
    }

    enum ContractType {
        BOTH, LEFT, RIGHT, NO
    }

    // TODO fix this method to also take the EdgeData into account
    static ContractType isContractable(Graph g,
            Point node1, Point node2) {
        boolean n12 = g.getOutgoingConnections(node1).contains(node2);
        boolean n21 = g.getOutgoingConnections(node2).contains(node1);

        if (!(n12 || n21)) {
            throw new IllegalArgumentException(
                    "There is no connection between the nodes.");
        }
        boolean bidi1 = n12 && n21;
        boolean bidi0 = false, bidi2 = false;

        Set outgoing1 = new HashSet(
                g.getOutgoingConnections(node1));
        Set incoming1 = new HashSet(
                g.getIncomingConnections(node1));
        outgoing1.remove(node2);
        incoming1.remove(node2);

        Set outgoing2 = new HashSet(
                g.getOutgoingConnections(node2));
        Set incoming2 = new HashSet(
                g.getIncomingConnections(node2));
        outgoing2.remove(node1);
        incoming2.remove(node1);

        Set neighbors1 = new HashSet();
        neighbors1.addAll(outgoing1);
        neighbors1.addAll(incoming1);

        if (neighbors1.size() == 1) {
            Point node0 = neighbors1.iterator().next();
            bidi0 = outgoing1.contains(node0) && incoming1.contains(node0);
        }

        Set neighbors2 = new HashSet();
        neighbors2.addAll(outgoing2);
        neighbors2.addAll(incoming2);

        if (neighbors2.size() == 1) {
            Point node3 = neighbors2.iterator().next();
            bidi2 = outgoing2.contains(node3) && incoming2.contains(node3);
        }

        if (neighbors1.size() != 1 && neighbors2.size() != 1) {
            return ContractType.NO;
        } else if (neighbors1.size() == 1 && neighbors2.size() == 1) {
            boolean sameneigh = neighbors1.iterator().next()
                    .equals(neighbors2.iterator().next());

            if (sameneigh) {
                if (!bidi0 && !bidi1 && !bidi2) {
                    return ContractType.BOTH;
                }
                return ContractType.NO;
            }

            if ((bidi0 == bidi1) && (bidi1 == bidi2)) {
                return ContractType.BOTH;
            } else if ((bidi0 == bidi1) && (bidi1 != bidi2)) {
                return ContractType.LEFT;
            } else if ((bidi0 != bidi1) && (bidi1 == bidi2)) {
                return ContractType.RIGHT;
            } else {
                return ContractType.NO;
            }
        } else if (neighbors1.size() == 1 && neighbors2.size() != 1) {
            return bidi0 == bidi1 ? ContractType.LEFT : ContractType.NO;
        } else if (neighbors1.size() != 1 && neighbors2.size() == 1) {
            return bidi1 == bidi2 ? ContractType.RIGHT : ContractType.NO;
        }

        throw new IllegalStateException("Unexpected node configuration..");
    }

    public static  List> findNotFullyConnectedNodes(
            Graph graph) {
        if (graph == null || graph.isEmpty()) {
            throw new IllegalArgumentException(
                    "Graph may not be null and must contain at least one node.");
        }
        // just get a 'random' starting point
        return findNotFullyConnectedNodes(graph, new ArrayList(
                graph.getNodes()).get(0));
    }

    public static  List> findNotFullyConnectedNodes(
            Graph graph, Point root) {

        HashSet fullyConnectedSet = new HashSet();
        HashSet neighbours = new HashSet();
        HashSet notConnectedSet = new HashSet();

        fullyConnectedSet.add(root);
        neighbours.addAll(graph.getOutgoingConnections(root));

        while (!neighbours.isEmpty()) {
            List path = null;
            Point current = neighbours.iterator().next();
            neighbours.remove(current);
            if (graph.containsNode(current)) {
                try {
                    path = Graphs
                            .shortestPathEuclideanDistance(graph, current, root);
                } catch (PathNotFoundException e) {/*
                                                    * this is intentionally
                                                    * empty
                                                    */}
            }

            if (path == null) {
                notConnectedSet.add(current);
            } else {
                for (Point p : path) {
                    fullyConnectedSet.add(p);
                    if (neighbours.contains(p)) {
                        neighbours.remove(p);
                    }
                    for (Point q : graph.getOutgoingConnections(p)) {
                        if (!fullyConnectedSet.contains(q)) {
                            neighbours.add(q);
                        }
                    }
                }
            }
        }
        for (Point p : graph.getNodes()) {
            if (!fullyConnectedSet.contains(p)) {
                notConnectedSet.add(p);
            }
        }

        return new ArrayList>(
                asList(notConnectedSet, fullyConnectedSet));
    }

    // when executing there should be two folders at the same level as this
    // class (or jar):
    // 1. osm-files/
    // 2. dot-files/
    // when calling main("brussels") a file named brussels.osm is expected in
    // osm-files. All .dot output is written in dot-files.
    public static void main(String[] args) throws FileNotFoundException,
            IOException {
        DotGraphSerializer serializer = DotGraphSerializer
                .getMultiAttributeGraphSerializer();

        String name = args[0];// "wroclaw";

        System.out.println(name);
        Graph g = OSM.parse("osm-files/" + name + ".osm");
        serializer.write(g, "dot-files/" + name + "-raw.dot");
        System.out.println(g);

        long startRead = System.currentTimeMillis();
        Graph g2 = serializer.read("dot-files/" + name
                + "-raw.dot");
        System.out.println("loading took: "
                + (System.currentTimeMillis() - startRead));
        Graph graph = new TableGraph(
                MultiAttributeData.EMPTY);
        graph.merge(g2);
        System.out.println("(V,E) = (" + graph.getNumberOfNodes() + ","
                + graph.getNumberOfConnections() + ")");

        long startSimplify = System.currentTimeMillis();
        graph = MapPreprocessor.simplify(graph, MultiAttributeData.EMPTY);
        System.out.println("simplifying took: "
                + (System.currentTimeMillis() - startSimplify));

        long startFix = System.currentTimeMillis();
        graph = MapPreprocessor
                .removeUnconnectedSubGraphs(graph, MultiAttributeData.EMPTY);
        System.out.println("fixing took: "
                + (System.currentTimeMillis() - startFix));
        serializer.write(graph, "dot-files/" + name + "-simple.dot");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy