From cfb59ac0f1c152e56993c482e1b8940e1b1473de Mon Sep 17 00:00:00 2001 From: Mikael Capelle Date: Fri, 16 Feb 2018 15:29:11 +0100 Subject: [PATCH] Update. --- .gitignore | 2 + src/main/org/insa/algo/AbstractAlgorithm.java | 39 +- src/main/org/insa/algo/AbstractObserver.java | 20 ++ .../connectivity/ConnectivityAlgorithm.java | 30 -- .../connectivity/ConnectivityInstance.java | 16 - .../connectivity/ConnectivitySolution.java | 20 -- .../StronglyConnectedComponentsAlgorithm.java | 16 + .../StronglyConnectedComponentsInstance.java | 16 + .../StronglyConnectedComponentsSolution.java | 29 ++ .../strongconnectivity/TarjanAlgorithm.java | 155 ++++++++ ...aklyConnectedComponentGraphicObserver.java | 46 +++ .../WeaklyConnectedComponentObserver.java | 38 ++ .../WeaklyConnectedComponentTextObserver.java | 37 ++ .../WeaklyConnectedComponentsAlgorithm.java | 127 +++++++ .../WeaklyConnectedComponentsInstance.java | 16 + .../WeaklyConnectedComponentsSolution.java | 29 ++ src/main/org/insa/base/Couleur.java | 58 --- src/main/org/insa/base/MainWindow.java | 340 ++++++++++++++++++ src/main/org/insa/base/Openfile.java | 115 ------ src/main/org/insa/base/Readarg.java | 86 ----- src/main/org/insa/base/Utils.java | 57 --- src/main/org/insa/drawing/Drawing.java | 7 +- .../org/insa/drawing/DrawingInvisible.java | 6 + src/main/org/insa/drawing/DrawingVisible.java | 119 +++--- .../org/insa/drawing/ZoomAndPanListener.java | 6 + .../insa/drawing/graph/BasicGraphPalette.java | 97 +++++ .../graph/BlackAndWhiteGraphPalette.java | 25 ++ .../org/insa/drawing/graph/GraphDrawing.java | 134 +++++++ .../org/insa/drawing/graph/GraphPalette.java | 33 ++ .../org/insa/drawing/graph/PathDrawing.java | 52 +++ src/main/org/insa/graph/Arc.java | 2 +- src/main/org/insa/graph/Graph.java | 12 + src/main/org/insa/graph/Node.java | 15 +- .../org/insa/graph/io/BinaryGraphReader.java | 7 +- .../org/insa/graph/io/BinaryPathReader.java | 34 +- src/main/org/insa/graph/io/Openfile.java | 83 +++++ .../insa/graph/io/BinaryGraphReaderTest.java | 60 ++++ 37 files changed, 1511 insertions(+), 473 deletions(-) create mode 100644 .gitignore create mode 100644 src/main/org/insa/algo/AbstractObserver.java delete mode 100644 src/main/org/insa/algo/connectivity/ConnectivityAlgorithm.java delete mode 100644 src/main/org/insa/algo/connectivity/ConnectivityInstance.java delete mode 100644 src/main/org/insa/algo/connectivity/ConnectivitySolution.java create mode 100644 src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsAlgorithm.java create mode 100644 src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsInstance.java create mode 100644 src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsSolution.java create mode 100644 src/main/org/insa/algo/strongconnectivity/TarjanAlgorithm.java create mode 100644 src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java create mode 100644 src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentObserver.java create mode 100644 src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentTextObserver.java create mode 100644 src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java create mode 100644 src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsInstance.java create mode 100644 src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsSolution.java delete mode 100644 src/main/org/insa/base/Couleur.java create mode 100644 src/main/org/insa/base/MainWindow.java delete mode 100644 src/main/org/insa/base/Openfile.java delete mode 100644 src/main/org/insa/base/Readarg.java delete mode 100644 src/main/org/insa/base/Utils.java create mode 100644 src/main/org/insa/drawing/graph/BasicGraphPalette.java create mode 100644 src/main/org/insa/drawing/graph/BlackAndWhiteGraphPalette.java create mode 100644 src/main/org/insa/drawing/graph/GraphDrawing.java create mode 100644 src/main/org/insa/drawing/graph/GraphPalette.java create mode 100644 src/main/org/insa/drawing/graph/PathDrawing.java create mode 100644 src/main/org/insa/graph/io/Openfile.java create mode 100644 src/test/org/insa/graph/io/BinaryGraphReaderTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a5ba85 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +bin diff --git a/src/main/org/insa/algo/AbstractAlgorithm.java b/src/main/org/insa/algo/AbstractAlgorithm.java index cdfb46c..bcb0d04 100644 --- a/src/main/org/insa/algo/AbstractAlgorithm.java +++ b/src/main/org/insa/algo/AbstractAlgorithm.java @@ -1,22 +1,40 @@ package org.insa.algo ; -import java.io.* ; +import java.util.ArrayList; -public abstract class AbstractAlgorithm { +public abstract class AbstractAlgorithm implements Runnable { - protected PrintStream output; protected AbstractInstance instance; protected AbstractSolution solution; + + protected ArrayList observers; + + protected AbstractAlgorithm(AbstractInstance instance) { + this.instance = instance; + this.observers = new ArrayList(); + this.solution = null; + } + + protected AbstractAlgorithm(AbstractInstance instance, ArrayList observers) { + this.instance = instance; + this.observers = observers;; + this.solution = null; + } /** + * Add an observer to this algorithm. * - * @param instance - * @param logOutput + * @param observer */ - protected AbstractAlgorithm(AbstractInstance instance, PrintStream logOutput) { - this.instance = instance; - this.output = logOutput; - this.solution = null; + public void addObserver(AbstractObserver observer) { + observers.add(observer); + } + + /** + * @return The list of observers for this algorithm. + */ + public ArrayList getObservers() { + return observers; } /** @@ -44,9 +62,8 @@ public abstract class AbstractAlgorithm { * * @return true if a feasible solution was found (even non-optimal). */ - public boolean run() { + public void run() { this.solution = this.doRun(); - return this.solution != null && this.solution.isFeasible(); } /** diff --git a/src/main/org/insa/algo/AbstractObserver.java b/src/main/org/insa/algo/AbstractObserver.java new file mode 100644 index 0000000..53ca878 --- /dev/null +++ b/src/main/org/insa/algo/AbstractObserver.java @@ -0,0 +1,20 @@ +package org.insa.algo; + +public abstract class AbstractObserver { + + // Specify if the observer is graphic or not. + private final boolean isgraphic; + + protected AbstractObserver(boolean isGraphic) { + this.isgraphic = isGraphic; + } + + /** + * @return true if this observer is graphic (use drawing to display + * information). + */ + public boolean isGraphic() { + return isgraphic; + } + +} \ No newline at end of file diff --git a/src/main/org/insa/algo/connectivity/ConnectivityAlgorithm.java b/src/main/org/insa/algo/connectivity/ConnectivityAlgorithm.java deleted file mode 100644 index ecd34aa..0000000 --- a/src/main/org/insa/algo/connectivity/ConnectivityAlgorithm.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.insa.algo.connectivity ; - -import java.io.* ; - -import org.insa.algo.AbstractAlgorithm; -import org.insa.algo.AbstractSolution; - -public class ConnectivityAlgorithm extends AbstractAlgorithm { - - /** - * - * @param instance - * @param logOutput - */ - public ConnectivityAlgorithm(ConnectivityInstance instance, PrintStream logOutput) { - super(instance, logOutput); - } - - /** - * {@inheritDoc} - */ - @Override - protected AbstractSolution doRun() { - ConnectivityInstance instance = (ConnectivityInstance)getInstance(); - ConnectivitySolution solution = null; - // TODO: - return solution; - } - -} diff --git a/src/main/org/insa/algo/connectivity/ConnectivityInstance.java b/src/main/org/insa/algo/connectivity/ConnectivityInstance.java deleted file mode 100644 index 987872a..0000000 --- a/src/main/org/insa/algo/connectivity/ConnectivityInstance.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.insa.algo.connectivity; - -import org.insa.algo.AbstractInstance; -import org.insa.graph.Graph; - -public class ConnectivityInstance extends AbstractInstance { - - /** - * - * @param graph - */ - public ConnectivityInstance(Graph graph) { - super(graph); - } - -} diff --git a/src/main/org/insa/algo/connectivity/ConnectivitySolution.java b/src/main/org/insa/algo/connectivity/ConnectivitySolution.java deleted file mode 100644 index 114e6a4..0000000 --- a/src/main/org/insa/algo/connectivity/ConnectivitySolution.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.insa.algo.connectivity; - -import java.time.Duration; - -import org.insa.algo.AbstractSolution; - -public class ConnectivitySolution extends AbstractSolution { - - protected ConnectivitySolution(ConnectivityInstance instance) { - super(instance); - } - - protected ConnectivitySolution(ConnectivityInstance instance, - Duration solvingTime, Status status) { - super(instance, solvingTime, status); - - // TODO: - } - -} diff --git a/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsAlgorithm.java b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsAlgorithm.java new file mode 100644 index 0000000..5973890 --- /dev/null +++ b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsAlgorithm.java @@ -0,0 +1,16 @@ +package org.insa.algo.strongconnectivity ; + +import org.insa.algo.AbstractAlgorithm; + +public abstract class StronglyConnectedComponentsAlgorithm extends AbstractAlgorithm { + + /** + * + * @param instance + * @param logOutput + */ + public StronglyConnectedComponentsAlgorithm(StronglyConnectedComponentsInstance instance) { + super(instance); + } + +} diff --git a/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsInstance.java b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsInstance.java new file mode 100644 index 0000000..41ceceb --- /dev/null +++ b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsInstance.java @@ -0,0 +1,16 @@ +package org.insa.algo.strongconnectivity; + +import org.insa.algo.AbstractInstance; +import org.insa.graph.Graph; + +public class StronglyConnectedComponentsInstance extends AbstractInstance { + + /** + * + * @param graph + */ + public StronglyConnectedComponentsInstance(Graph graph) { + super(graph); + } + +} diff --git a/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsSolution.java b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsSolution.java new file mode 100644 index 0000000..6018966 --- /dev/null +++ b/src/main/org/insa/algo/strongconnectivity/StronglyConnectedComponentsSolution.java @@ -0,0 +1,29 @@ +package org.insa.algo.strongconnectivity; + +import java.time.Duration; +import java.util.ArrayList; + +import org.insa.algo.AbstractSolution; +import org.insa.graph.Node; + +public class StronglyConnectedComponentsSolution extends AbstractSolution { + + // Components + private ArrayList> components; + + protected StronglyConnectedComponentsSolution(StronglyConnectedComponentsInstance instance) { + super(instance); + } + + protected StronglyConnectedComponentsSolution(StronglyConnectedComponentsInstance instance, + Duration solvingTime, Status status, ArrayList> components) { + super(instance, solvingTime, status); + this.components = components; + } + + /** + * @return Components of the solution, if any. + */ + public ArrayList> getComponents() { return components; } + +} diff --git a/src/main/org/insa/algo/strongconnectivity/TarjanAlgorithm.java b/src/main/org/insa/algo/strongconnectivity/TarjanAlgorithm.java new file mode 100644 index 0000000..a9a9a01 --- /dev/null +++ b/src/main/org/insa/algo/strongconnectivity/TarjanAlgorithm.java @@ -0,0 +1,155 @@ +package org.insa.algo.strongconnectivity; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Stack; + +import org.insa.algo.AbstractSolution; +import org.insa.algo.AbstractSolution.Status; +import org.insa.graph.Arc; +import org.insa.graph.Graph; +import org.insa.graph.Node; + +public class TarjanAlgorithm extends StronglyConnectedComponentsAlgorithm { + + private final static int UNDEFINED = -1; + + // Stack of nodes and flags. + private Stack stack; + private boolean[] inStack; + + // Current index. + private int index; + + // Information of nodes + private int[] indexes; + private int[] lowlink; + + // Array of strongly connected components + ArrayList> components; + + public TarjanAlgorithm(StronglyConnectedComponentsInstance instance) { + super(instance); + } + + /** + * Push the given node to the stack. + * + * @param node + */ + protected void pushNode(Node node) { + stack.push(node); + inStack[node.getId()] = true; + } + + /** + * Pop and return a node from the stack. + * + * @return Node popped from the stack + */ + protected Node popNode() { + Node top = stack.pop(); + inStack[top.getId()] = false; + return top; + } + + /** + * Check if the given node is in the stack. + * + * @param node + * + * @return true if the given node is in the stack, false otherwize. + */ + protected boolean isInStack(Node node) { + return inStack[node.getId()]; + } + + /** + * Find the strong component containing the given node. + * + * @param node + * + * @return The strong component containing the given node. + */ + protected void findAndAddStrongComponent(Node v) { + Graph graph = getInstance().getGraph(); + + // Update node info, index and push the node. + indexes[v.getId()] = index; + lowlink[v.getId()] = index; + index += 1; + pushNode(v); + + for (Arc a: v.getSuccessors()) { + Node w = a.getDestination(); + if (!hasBeenVisited(w)) { + findAndAddStrongComponent(w); + lowlink[v.getId()] = Math.min(lowlink[v.getId()], lowlink[w.getId()]); + } + else if (isInStack(w)) { + lowlink[v.getId()] = Math.min(lowlink[v.getId()], indexes[w.getId()]); + } + } + + // Compute the component (if any) + if (lowlink[v.getId()] == indexes[v.getId()]) { + ArrayList component = new ArrayList(); + Node w; + do { + w = popNode(); + component.add(w); + } while (!w.equals(v)); + components.add(component); + System.out.println("Size of the stack: " + stack.size()); + } + + } + + /** + * Check if the given node has not been visited yet. + * + * @return true if the node has been visited. + */ + protected boolean hasBeenVisited(Node node) { + return this.indexes[node.getId()] != UNDEFINED; + } + + @Override + protected AbstractSolution doRun() { + Graph graph = getInstance().getGraph(); + + components = new ArrayList>(); + + // Starting time... + Instant start = Instant.now(); + + // Initialize everything + final int nbNodes = graph.getNodes().size(); + stack = new Stack(); + inStack = new boolean[nbNodes]; + + // Current index. + index = 0; + + // Information of nodes + indexes = new int[nbNodes]; + Arrays.fill(indexes, UNDEFINED); + lowlink = new int[nbNodes]; + + // Find components + for (Node node: graph.getNodes()) { + if (!hasBeenVisited(node)) { + findAndAddStrongComponent(node); + } + } + + // Duration... + Duration solvingTime = Duration.between(start, Instant.now()); + + return new StronglyConnectedComponentsSolution((StronglyConnectedComponentsInstance)getInstance(), + solvingTime, Status.OPTIMAL, components); + } + +} diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java new file mode 100644 index 0000000..09db88a --- /dev/null +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentGraphicObserver.java @@ -0,0 +1,46 @@ +package org.insa.algo.weakconnectivity; + +import java.awt.Color; +import java.util.ArrayList; + +import org.insa.drawing.Drawing; +import org.insa.drawing.graph.GraphDrawing; +import org.insa.graph.Node; + +public class WeaklyConnectedComponentGraphicObserver extends WeaklyConnectedComponentObserver { + + private static final Color[] COLORS = { + Color.BLUE, Color.ORANGE, Color.GREEN, Color.YELLOW, Color.RED + }; + + // Drawing + Graph drawing + private Drawing drawing; + private GraphDrawing gdrawing; + + // Current index color + private int cindex = 0; + + public WeaklyConnectedComponentGraphicObserver(Drawing drawing) { + super(true); + this.drawing = drawing; + this.gdrawing = new GraphDrawing(drawing); + this.drawing.setAutoRepaint(true); + } + + @Override + public void notifyStartComponent(Node curNode) { + this.drawing.setColor(COLORS[cindex]); + cindex = (cindex + 1) % COLORS.length; + } + + @Override + public void notifyNewNodeInComponent(Node node) { + this.gdrawing.drawPoint(node.getPoint(), 5); + this.drawing.repaint(); + } + + @Override + public void notifyEndComponent(ArrayList nodes) { + } + +} diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentObserver.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentObserver.java new file mode 100644 index 0000000..9e6e0a6 --- /dev/null +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentObserver.java @@ -0,0 +1,38 @@ +package org.insa.algo.weakconnectivity; + +import java.util.ArrayList; + +import org.insa.algo.AbstractObserver; +import org.insa.graph.Node; + +public abstract class WeaklyConnectedComponentObserver extends AbstractObserver { + + /** + * {@inheritDoc} + */ + protected WeaklyConnectedComponentObserver(boolean isGraphic) { + super(isGraphic); + } + + /** + * Notify that the algorithm is entering a new component. + * + * @param curNode Starting node for the component. + */ + public abstract void notifyStartComponent(Node curNode); + + /** + * Notify that a new node has been found for the current component. + * + * @param node New node found for the current component. + */ + public abstract void notifyNewNodeInComponent(Node node); + + /** + * Notify that the algorithm has computed a new component. + * + * @param nodes List of nodes in the component. + */ + public abstract void notifyEndComponent(ArrayList nodes); + +} diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentTextObserver.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentTextObserver.java new file mode 100644 index 0000000..b4d4184 --- /dev/null +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentTextObserver.java @@ -0,0 +1,37 @@ +package org.insa.algo.weakconnectivity; + +import java.io.PrintStream; +import java.util.ArrayList; + +import org.insa.graph.Node; + +public class WeaklyConnectedComponentTextObserver extends WeaklyConnectedComponentObserver { + + // Number of the current component. + private int numComponent = 1; + + // Output stream + PrintStream stream; + + public WeaklyConnectedComponentTextObserver(PrintStream stream) { + super(false); + this.stream = stream; + } + + @Override + public void notifyStartComponent(Node curNode) { + stream.print("Entering component #" + numComponent + " from node #" + curNode.getId() + "... "); + } + + @Override + public void notifyNewNodeInComponent(Node node) { + } + + @Override + public void notifyEndComponent(ArrayList nodes) { + stream.println(nodes.size() + " nodes found."); + stream.flush(); + numComponent += 1; + } + +} diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java new file mode 100644 index 0000000..e9980d7 --- /dev/null +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsAlgorithm.java @@ -0,0 +1,127 @@ +package org.insa.algo.weakconnectivity; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Queue; +import java.util.HashSet; + +import org.insa.algo.AbstractAlgorithm; +import org.insa.algo.AbstractObserver; +import org.insa.algo.AbstractSolution; +import org.insa.algo.AbstractSolution.Status; +import org.insa.graph.Arc; +import org.insa.graph.Graph; +import org.insa.graph.Node; + +public class WeaklyConnectedComponentsAlgorithm extends AbstractAlgorithm { + + /** + * + * @param instance + * @param logOutput + */ + public WeaklyConnectedComponentsAlgorithm(WeaklyConnectedComponentsInstance instance) { + super(instance); + } + + /** + * @return An adjacency list for the undirected graph equivalent to the stored graph. + */ + protected ArrayList> createUndirectedGraph() { + int nNodes = getInstance().getGraph().getNodes().size(); + ArrayList> res = new ArrayList>(nNodes); + for (int i = 0; i < nNodes; ++i) { + res.add(new HashSet()); + } + + for (Node node: getInstance().getGraph().getNodes()) { + for (Arc arc: node.getSuccessors()) { + res.get(node.getId()).add(arc.getDestination().getId()); + if (arc.getInfo().isOneWay()) { + res.get(arc.getDestination().getId()).add(node.getId()); + } + } + } + + return res; + } + + /** + * Apply a breadth first search algorithm on the given undirected graph (adjacency list), + * starting at node cur, and marking nodes in marked. + * + * @param marked + * @param cur + * + * @return + */ + protected ArrayList bfs(ArrayList> ugraph, boolean[] marked, int cur) { + ArrayList nodes = getInstance().getGraph().getNodes(); + ArrayList component = new ArrayList(); + + // Using a queue because we are doing a BFS + Queue queue = new LinkedList(); + + for (AbstractObserver obs: getObservers()) { + ((WeaklyConnectedComponentObserver)obs).notifyStartComponent(nodes.get(cur)); + } + + // Add original node and loop until the queue is empty. + queue.add(cur); + marked[cur] = true; + while (!queue.isEmpty()) { + Node node = nodes.get(queue.remove()); + component.add(node); + + // notify observers + for (AbstractObserver obs: getObservers()) ((WeaklyConnectedComponentObserver)obs).notifyNewNodeInComponent(node); + + for (Integer destId: ugraph.get(node.getId())) { + Node dest = nodes.get(destId); + if (!marked[dest.getId()]) { + queue.add(destId); + marked[destId] = true; + } + } + } + + for (AbstractObserver obs: getObservers()) { + ((WeaklyConnectedComponentObserver)obs).notifyEndComponent(component); + } + + return component; + } + + @Override + protected AbstractSolution doRun() { + + Instant start = Instant.now(); + + Graph graph = getInstance().getGraph(); + ArrayList> ugraph = createUndirectedGraph(); + boolean[] marked = new boolean[graph.getNodes().size()]; + Arrays.fill(marked, false); + + ArrayList> components = new ArrayList>(); + + // perform algorithm + int cur = 0; + while (cur < marked.length) { + // Apply BFS + components.add(this.bfs(ugraph, marked, cur)); + + // Find next non-marked + for (; cur < marked.length && marked[cur]; ++cur); + } + + Duration solvingTime = Duration.between(start, Instant.now()); + + return new WeaklyConnectedComponentsSolution((WeaklyConnectedComponentsInstance)getInstance(), + solvingTime, Status.OPTIMAL, components); + } + +} diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsInstance.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsInstance.java new file mode 100644 index 0000000..0d9f615 --- /dev/null +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsInstance.java @@ -0,0 +1,16 @@ +package org.insa.algo.weakconnectivity; + +import org.insa.algo.AbstractInstance; +import org.insa.graph.Graph; + +public class WeaklyConnectedComponentsInstance extends AbstractInstance { + + /** + * + * @param graph + */ + public WeaklyConnectedComponentsInstance(Graph graph) { + super(graph); + } + +} diff --git a/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsSolution.java b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsSolution.java new file mode 100644 index 0000000..a8f82bc --- /dev/null +++ b/src/main/org/insa/algo/weakconnectivity/WeaklyConnectedComponentsSolution.java @@ -0,0 +1,29 @@ +package org.insa.algo.weakconnectivity; + +import java.time.Duration; +import java.util.ArrayList; + +import org.insa.algo.AbstractSolution; +import org.insa.graph.Node; + +public class WeaklyConnectedComponentsSolution extends AbstractSolution { + + // Components + private ArrayList> components; + + protected WeaklyConnectedComponentsSolution(WeaklyConnectedComponentsInstance instance) { + super(instance); + } + + protected WeaklyConnectedComponentsSolution(WeaklyConnectedComponentsInstance instance, + Duration solvingTime, Status status, ArrayList> components) { + super(instance, solvingTime, status); + this.components = components; + } + + /** + * @return Components of the solution, if any. + */ + public ArrayList> getComponents() { return components; } + +} diff --git a/src/main/org/insa/base/Couleur.java b/src/main/org/insa/base/Couleur.java deleted file mode 100644 index e137851..0000000 --- a/src/main/org/insa/base/Couleur.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.insa.base ; - -/** - * Choix des couleurs pour l'affichage. - */ - -import java.awt.* ; - -import org.insa.drawing.Drawing; - -public class Couleur { - - static final Color autoroute = Color.red ; - static final Color bigroute = new Color(255, 105, 0) ; - static final Color tiroute = new Color(255, 234, 0) ; - static final Color cote = Color.blue ; - - public static void set(Drawing d, char type) { - - // Voir le fichier Descripteur.java pour le type des routes. - switch (type) { - case 'a': - d.setWidth(2) ; - d.setColor(Color.red) ; - break ; - - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - case 'g': - d.setWidth(1) ; - d.setColor(bigroute) ; - break ; - case 'h': - case 'i': - case 'j': - case 'k': - case 'l': - case 'm': - case 'n': - case 'o': - d.setWidth(1) ; - d.setColor(tiroute) ; - break ; - - case 'z': - d.setWidth(4) ; - d.setColor(cote) ; - break ; - - default: - d.setWidth(1) ; - d.setColor(Color.black) ; - } - } -} diff --git a/src/main/org/insa/base/MainWindow.java b/src/main/org/insa/base/MainWindow.java new file mode 100644 index 0000000..4fc986e --- /dev/null +++ b/src/main/org/insa/base/MainWindow.java @@ -0,0 +1,340 @@ +package org.insa.base; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTextArea; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.filechooser.FileNameExtensionFilter; + +import org.insa.algo.weakconnectivity.WeaklyConnectedComponentGraphicObserver; +import org.insa.algo.weakconnectivity.WeaklyConnectedComponentTextObserver; +import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsAlgorithm; +import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsInstance; +import org.insa.drawing.DrawingVisible; +import org.insa.drawing.graph.BlackAndWhiteGraphPalette; +import org.insa.drawing.graph.GraphDrawing; +import org.insa.drawing.graph.PathDrawing; +import org.insa.graph.Graph; +import org.insa.graph.Path; +import org.insa.graph.io.BinaryGraphReader; +import org.insa.graph.io.BinaryPathReader; +import org.insa.graph.io.MapMismatchException; +import org.insa.graph.io.Openfile; + +import com.sun.glass.events.KeyEvent; + +public class MainWindow extends JFrame { + + public class JOutputStream extends OutputStream { + private JTextArea textArea; + + public JOutputStream(JTextArea textArea) { + this.textArea = textArea; + } + + @Override + public void write(int b) throws IOException { + // redirects data to the text area + textArea.setText(textArea.getText() + String.valueOf((char)b)); + // scrolls the text area to the end of data + textArea.setCaretPosition(textArea.getDocument().getLength()); + // keeps the textArea up to date + textArea.update(textArea.getGraphics()); + } + } + + /** + * + */ + private static final long serialVersionUID = -527660583705140687L; + + /** + * + */ + private static final String WINDOW_TITLE = "BE Graphes INSA"; + + /** + * + */ + private static final Dimension DEFAULT_DIMENSION = new Dimension(800, 600); + + // Current graph. + private Graph graph; + + // Current loaded path. + private Path currentPath; + + // List of item for the top menus. + private JMenuItem openMapItem; + + // List of items that cannot be used without a graph + private ArrayList graphItems = new ArrayList(); + + // Label containing the map ID of the current graph. + private JLabel mapIdPanel; + + // Log stream and print stream + private JOutputStream logStream; + private PrintStream printStream; + + /** + * + */ + private DrawingVisible drawing; + + public MainWindow() { + super(WINDOW_TITLE); + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + setLayout(new BorderLayout()); + setSize(DEFAULT_DIMENSION); + setJMenuBar(createMenuBar()); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + int confirmed = JOptionPane.showConfirmDialog(null, + "Are you sure you want to close the application?", "Exit Confirmation", + JOptionPane.YES_NO_OPTION); + + if (confirmed == JOptionPane.YES_OPTION) { + dispose(); + System.exit(0); + } + } + }); + + // Create graph area + JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + + drawing = new DrawingVisible(); + drawing.setBackground(Color.WHITE); + + JTextArea infoPanel = new JTextArea(); + infoPanel.setMinimumSize(new Dimension(200, 50)); + // infoPanel.setBorder(BorderFactory.createMatteBorder(0, 1, 0, 0, Color.GRAY)); + infoPanel.setBackground(Color.WHITE); + infoPanel.setLineWrap(true); + infoPanel.setEditable(false); + this.logStream = new JOutputStream(infoPanel); + this.printStream = new PrintStream(this.logStream); + + sp.setResizeWeight(0.8); + // sp.setEnabled(false); + sp.setDividerSize(5); + + sp.setBackground(Color.WHITE); + sp.add(drawing); + sp.add(new JScrollPane(infoPanel)); + this.add(sp, BorderLayout.CENTER); + + this.add(createStatusBar(), BorderLayout.SOUTH); + } + + private JMenuBar createMenuBar() { + + // Open Map item... + openMapItem = new JMenuItem("Open Map... ", + KeyEvent.VK_O); + openMapItem.setAccelerator(KeyStroke.getKeyStroke( + KeyEvent.VK_O, ActionEvent.ALT_MASK)); + openMapItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new JFileChooser(); + FileNameExtensionFilter filter = new FileNameExtensionFilter( + "Map & compressed map files", "map", "map.gz"); + chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); + chooser.setFileFilter(filter); + if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) { + BinaryGraphReader reader; + try { + reader = new BinaryGraphReader( + Openfile.open(chooser.getSelectedFile().getAbsolutePath())); + } catch (IOException e1) { + JOptionPane.showMessageDialog(MainWindow.this, "Cannot open the selected file."); + return ; + } + try { + graph = reader.read(); + } + catch (Exception exception) { + JOptionPane.showMessageDialog(MainWindow.this, "Unable to read graph from the selected file."); + return ; + } + drawing.clear(); + new GraphDrawing(drawing).drawGraph(graph); + + for (JMenuItem item: graphItems) { + item.setEnabled(true); + } + mapIdPanel.setText("Map ID: 0x" + Integer.toHexString(graph.getMapId())); + } + } + }); + + // Open Path item... + JMenuItem openPathItem = new JMenuItem("Open Path... ", KeyEvent.VK_P); + openPathItem.setAccelerator(KeyStroke.getKeyStroke( + KeyEvent.VK_P, ActionEvent.ALT_MASK)); + openPathItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new JFileChooser(); + FileNameExtensionFilter filter = new FileNameExtensionFilter( + "Path & compressed path files", "path", "path.gz"); + chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); + chooser.setFileFilter(filter); + if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) { + BinaryPathReader reader; + try { + reader = new BinaryPathReader( + Openfile.open(chooser.getSelectedFile().getAbsolutePath())); + } catch (IOException e1) { + JOptionPane.showMessageDialog(MainWindow.this, "Cannot open the selected file."); + return ; + } + try { + currentPath = reader.readPath(graph); + } + catch (MapMismatchException exception) { + JOptionPane.showMessageDialog(MainWindow.this, "The selected file does not contain a path for the current graph."); + return; + } + catch (Exception exception) { + JOptionPane.showMessageDialog(MainWindow.this, "Unable to read path from the selected file."); + return ; + } + new PathDrawing(drawing).drawPath(currentPath); + } + } + }); + graphItems.add(openPathItem); + + // Close item + JMenuItem closeItem = new JMenuItem("Quit", KeyEvent.VK_Q); + closeItem.setAccelerator(KeyStroke.getKeyStroke( + KeyEvent.VK_Q, ActionEvent.ALT_MASK)); + closeItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + MainWindow.this.dispatchEvent(new WindowEvent(MainWindow.this, WindowEvent.WINDOW_CLOSING)); + } + }); + + //Build the first menu. + JMenu fileMenu = new JMenu("File"); + fileMenu.add(openMapItem); + fileMenu.add(openPathItem); + fileMenu.addSeparator(); + fileMenu.add(closeItem); + + // Second menu + JMenuItem drawGraphItem = new JMenuItem("Redraw", KeyEvent.VK_R); + drawGraphItem.setAccelerator(KeyStroke.getKeyStroke( + KeyEvent.VK_R, ActionEvent.ALT_MASK)); + drawGraphItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + drawing.clear(); + drawing.setAutoRepaint(true); + new GraphDrawing(drawing).drawGraph(graph); + drawing.setAutoRepaint(false); + } + }); + graphItems.add(drawGraphItem); + JMenuItem drawGraphBWItem = new JMenuItem("Redraw (B&W)", KeyEvent.VK_B); + drawGraphBWItem.setAccelerator(KeyStroke.getKeyStroke( + KeyEvent.VK_B, ActionEvent.ALT_MASK)); + drawGraphBWItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + drawing.clear(); + drawing.setAutoRepaint(true); + new GraphDrawing(drawing, new BlackAndWhiteGraphPalette()).drawGraph(graph); + drawing.setAutoRepaint(false); + } + }); + graphItems.add(drawGraphBWItem); + + JMenu graphMenu = new JMenu("Graph"); + graphMenu.add(drawGraphItem); + graphMenu.add(drawGraphBWItem); + + // Algo menu + JMenu algoMenu = new JMenu("Algorithms"); + + JMenuItem wccItem = new JMenuItem("Weakly Connected Components"); + wccItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + + WeaklyConnectedComponentsInstance instance = new WeaklyConnectedComponentsInstance(graph); + WeaklyConnectedComponentsAlgorithm algo = new WeaklyConnectedComponentsAlgorithm(instance); + algo.addObserver(new WeaklyConnectedComponentGraphicObserver(drawing)); + + (new Thread(algo)).start(); + } + }); + graphItems.add(wccItem); + + algoMenu.add(wccItem); + + // Create the menu bar. + JMenuBar menuBar = new JMenuBar(); + + menuBar.add(fileMenu); + menuBar.add(graphMenu); + menuBar.add(algoMenu); + + for (JMenuItem item: graphItems) { + item.setEnabled(false); + } + + return menuBar; + } + + private JPanel createStatusBar() { + // create the status bar panel and shove it down the bottom of the frame + JPanel statusPanel = new JPanel(); + statusPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.GRAY)); + statusPanel.setPreferredSize(new Dimension(getWidth(), 20)); + statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.X_AXIS)); + + mapIdPanel = new JLabel(); + mapIdPanel.setHorizontalAlignment(SwingConstants.LEFT); + statusPanel.add(mapIdPanel); + + return statusPanel; + } + + public static void main(final String[] args) { + MainWindow w = new MainWindow(); + w.setExtendedState(JFrame.MAXIMIZED_BOTH); + w.setVisible(true); + } + + +} diff --git a/src/main/org/insa/base/Openfile.java b/src/main/org/insa/base/Openfile.java deleted file mode 100644 index 4f2e374..0000000 --- a/src/main/org/insa/base/Openfile.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.insa.base ; - -import java.io.* ; -import java.util.zip.* ; - -/* Ne lisez pas cette classe. Lancez javadoc et lisez la doc generee plutot. */ - -/** - * La classe Openfile permet de lire les fichiers contenant les cartes : - *
    - *
  • en trouvant le bon dossier parmi les dossiers pre-configures
  • - *
  • en dezippant automatiquement si besoin
  • - *
- * - */ -public class Openfile { - - // Le programme examine chaque dossier dans l'ordre jusqu'a trouver celui qui contient la carte voulue - private static final String[] datadirs = - { // NE MODIFIEZ PAS CELUI-CI - // car il permet de tester en etant a l'INSA. - "/home/commetud/3eme Annee MIC/Graphes-et-Algorithmes/Maps", - - // Celui-ci pour les chemins - "/home/commetud/3eme Annee MIC/Graphes-et-Algorithmes/", - - // On cherche aussi dans le sous-repertoire local "Maps" (s'il existe) - "Maps", - - // et dans le repertoire courant (Unix uniquement) - ".", - - // Si vous utilisez votre propre dossier pour les donnees, mettez-le ici. - "/home/votrepropredossier/a/vous", - } ; - - // Extension testees. Garder l'extension vide dans la liste. - private static final String[] extensions = { ".map", ".gz", ".map.gz", ".path", ".path.gz", "" } ; - - /** - * Ouvre le fichier indiqué et renvoie un DataInputStream sur ce fichier. - * Le fichier ne sera pas ferme avant la fin de l'application. - * @param filename Nom du fichier a ouvrir (sans chemin) - */ - public static DataInputStream open (String filename) { - - if (!filename.equals (new File(filename).getName())) { - System.out.println("Le nom du fichier ne doit pas contenir un chemin (ni absolu, ni relatif).") ; - System.out.println("Il doit juste contenir le nom du fichier contenant la carte.") ; - System.out.println("Si vous voulez utiliser un dossier specifique, configurez base/Openfile.java") ; - System.exit(1) ; - } - - boolean trouve = false ; - InputStream fileinput = null ; - String fname = null ; - String fullpath = null ; - - for (int extn = 0 ; !trouve && extn < extensions.length ; extn++) { - fname = filename + extensions[extn] ; - for (int index = 0 ; !trouve && index < datadirs.length ; index++) { - fullpath = datadirs[index] + File.separator + fname ; - File file = new File(fullpath) ; - if (file.canRead()) { - trouve = true ; - try { - fileinput = new FileInputStream(file) ; - } catch (IOException e) { - e.printStackTrace() ; - System.exit(1) ; - } - } - } - } - - if (!trouve) { - // Pas trouve - System.out.println("Impossible de trouver le fichier " + filename) ; - System.out.println(" pourtant j'ai cherche dans les dossiers : ") ; - int existepas = 0 ; - for (int i = 0 ; i < datadirs.length ; i++) { - System.out.println(" - " + datadirs[i]) ; - if (!new File(datadirs[i]).isDirectory()) { - switch (existepas) { - case 0: System.out.println(" (Ce dossier n'existe pas d'ailleurs)") ; break; - case 1: System.out.println(" (Ce dossier n'existe pas non plus)") ; break; - default: System.out.println(" (Celui-la non plus)") ; break; - } - existepas++ ; - } - System.out.println() ; - } - System.exit(1) ; - } - - System.out.println("Fichier utilisee : " + fullpath) ; - System.out.println() ; - - if (fname.endsWith(".gz")) { - // The file is gzipped. - try { - fileinput = new GZIPInputStream(fileinput) ; - } catch (IOException e) { - e.printStackTrace() ; - System.exit(1) ; - } - } - else { - fileinput = new BufferedInputStream(fileinput) ; - } - - return new DataInputStream(fileinput) ; - } - -} diff --git a/src/main/org/insa/base/Readarg.java b/src/main/org/insa/base/Readarg.java deleted file mode 100644 index 00c2723..0000000 --- a/src/main/org/insa/base/Readarg.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.insa.base ; - -import java.io.* ; - -/* Ne lisez pas cette classe. Lancez javadoc et lisez la doc generee plutot. */ - -/** - * La classe Readarg facilite la lecture de donnees depuis le clavier ou depuis la ligne de commande. - * - */ -public class Readarg { - - private final String[] args ; - private int next ; - - // Le Java est le langage prefere des Shadoks. - private final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - - public Readarg(String[] argz) { - this.args = argz ; - this.next = 0 ; - } - - /** - * Obtient une chaine, ou bien depuis la ligne de commande, ou depuis l'entree standard. - * @param msg Message affiche avant de demander la chaine - */ - public String lireString (String msg) { - - String resultat = "" ; - - System.out.print(msg) ; - - if (this.next >= this.args.length) { - try { - resultat = br.readLine () ; - } catch (Exception e) { - System.err.println ("Erreur de lecture de l'entree standard.") ; - System.exit(1) ; - } - } - else { - resultat = this.args[this.next] ; - this.next++ ; - System.out.println (resultat) ; - } - - return resultat ; - } - - - /** - * Obtient un entier, ou bien depuis la ligne de commande, ou depuis l'entree standard. - * @param msg Message affiche avant de demander l'entier - */ - public int lireInt (String msg) { - String lu = lireString (msg) ; - int result = 0 ; - try { - result = Integer.parseInt(lu) ; - } - catch (Exception e) { - System.err.println ("Un entier est attendu mais je lis " + lu) ; - System.exit(1) ; - } - return result ; - } - - /** - * Obtient un float, ou bien depuis la ligne de commande, ou depuis l'entree standard. - * @param msg Message affiche avant de demander le float. - */ - public float lireFloat (String msg) { - String lu = lireString (msg) ; - float result = 0 ; - try { - result = Float.parseFloat(lu) ; - } - catch (Exception e) { - System.err.println ("Un reel est attendu mais je lis " + lu) ; - System.exit(1) ; - } - - return result ; - } -} diff --git a/src/main/org/insa/base/Utils.java b/src/main/org/insa/base/Utils.java deleted file mode 100644 index f4c77e9..0000000 --- a/src/main/org/insa/base/Utils.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.insa.base ; - -import java.io.* ; - -import org.insa.drawing.Drawing; - -/** - * Fonctions accessoires dont vous n'avez pas a vous servir directement. - */ -public class Utils { - - - // Calibrer la sortie graphique en fonction de la carte - // Vous pouvez modifier les coordonnees pour ameliorer le rendu. - public static void calibrer(String nomCarte, Drawing dessin) { - - if (nomCarte.startsWith("insa")) { - // L'INSA - dessin.setBB (1.462, 1.473, 43.567, 43.5744) ; - } - else if (nomCarte.startsWith("paris")) { - // Ile de la Cité, Paris - dessin.setBB (2.329, 2.372, 48.839, 48.867) ; - } - else if (nomCarte.startsWith("mayot")) { - // Mayotte - dessin.setBB (44.5, 45.5, -13.25, -12.25) ; - } - else if (nomCarte.startsWith("reuni")) { - // La Réunion - dessin.setBB (55.0, 56.0, -21.5, -20.5) ; - } - else if (nomCarte.startsWith("midip")) { - dessin.setBB (-0.6, 3.8, 42.2, 45.3) ; - } - else if (nomCarte.startsWith("franc")) { - dessin.setBB (-5.2, 10.0, 41.0, 51.5) ; - } - else if (nomCarte.startsWith("pfranc")) { - dessin.setBB (-5.2, 10.0, 41.0, 51.5) ; - } - else if (nomCarte.startsWith("morbihan")) { - dessin.setBB (-3.53, -2.452, 47.27, 47.665) ; - } - else if (nomCarte.startsWith("newzealand")) { - dessin.setBB (153.415, 179.912, -47.931, -33.980) ; - } - else if (nomCarte.startsWith("fract") || nomCarte.startsWith("carr")) { - dessin.setBB (-0.05, 1.05, -0.05, 1.05) ; - } - else { - dessin.setBB (-20.0, 50.0, 20.0, 70.0) ; - } - } - - -} diff --git a/src/main/org/insa/drawing/Drawing.java b/src/main/org/insa/drawing/Drawing.java index c3d2685..e04d9fc 100644 --- a/src/main/org/insa/drawing/Drawing.java +++ b/src/main/org/insa/drawing/Drawing.java @@ -37,10 +37,15 @@ public interface Drawing { /** * Set the pencil color. * - * param color Color for the pencil. + * @param color Color for the pencil. * */ public void setColor(Color col); + + /** + * Clear the drawing. + */ + public void clear(); /** * Indique les bornes de la fenetre graphique. diff --git a/src/main/org/insa/drawing/DrawingInvisible.java b/src/main/org/insa/drawing/DrawingInvisible.java index ea572fd..5d53f8d 100644 --- a/src/main/org/insa/drawing/DrawingInvisible.java +++ b/src/main/org/insa/drawing/DrawingInvisible.java @@ -34,5 +34,11 @@ public class DrawingInvisible implements Drawing { @Override public void repaint() { } + + @Override + public void clear() { + // TODO Auto-generated method stub + + } } diff --git a/src/main/org/insa/drawing/DrawingVisible.java b/src/main/org/insa/drawing/DrawingVisible.java index 99aca86..416e171 100644 --- a/src/main/org/insa/drawing/DrawingVisible.java +++ b/src/main/org/insa/drawing/DrawingVisible.java @@ -1,28 +1,35 @@ package org.insa.drawing; -import java.awt.*; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; import java.awt.image.*; +import javax.swing.JPanel; + /** * Cette implementation de la classe Dessin produit vraiment un affichage * (au contraire de la classe DessinInvisible). */ -public class DrawingVisible extends Canvas implements Drawing { +public class DrawingVisible extends JPanel implements Drawing { /** * */ private static final long serialVersionUID = 96779785877771827L; - + private final Graphics2D gr; private float long1; private float long2; private float lat1; private float lat2; - private final float width; - private final float height; + + // Width and height of the image + private final int width, height; private boolean bb_is_set ; @@ -34,15 +41,18 @@ public class DrawingVisible extends Canvas implements Drawing { /** * Cree et affiche une nouvelle fenetre de dessin. */ - public DrawingVisible (int largeur, int hauteur) { + public DrawingVisible() { super(); - this.zoomAndPanListener = new ZoomAndPanListener(this, 0, ZoomAndPanListener.DEFAULT_MAX_ZOOM_LEVEL, 1.2); + this.zoomAndPanListener = new ZoomAndPanListener(this, ZoomAndPanListener.DEFAULT_MIN_ZOOM_LEVEL, 20, 1.2); this.addMouseListener(zoomAndPanListener); this.addMouseMotionListener(zoomAndPanListener); this.addMouseWheelListener(zoomAndPanListener); - BufferedImage img = new BufferedImage (largeur, hauteur, BufferedImage.TYPE_3BYTE_BGR); + this.width = 2000; + this.height = 1600; + + BufferedImage img = new BufferedImage (this.width, this.height, BufferedImage.TYPE_3BYTE_BGR); this.image = img; this.gr = img.createGraphics(); @@ -51,40 +61,24 @@ public class DrawingVisible extends Canvas implements Drawing { this.bb_is_set = false; - this.width = largeur; - this.height = hauteur; - this.long1 = (float)0.0; - this.long2 = (float)largeur; - this.lat1 = (float)0.0; - this.lat2 = (float)hauteur; + this.long1 = 0.0f; + this.long2 = this.width; + this.lat1 = 0.0f; + this.lat2 = this.height; - this.setColor(Color.white); - gr.fillRect(0,0, largeur, hauteur); + this.clear(); this.repaint(); } @Override - public void paint(Graphics g1) { + public void paintComponent(Graphics g1) { Graphics2D g = (Graphics2D)g1; g.setTransform(zoomAndPanListener.getCoordTransform()); g.drawImage(image, 0, 0, this); } - - @Override - public Dimension getPreferredSize() { - Dimension size = new Dimension(0, 0); - - if (image != null) { - int w = image.getWidth(null); - int h = image.getHeight(null); - size = new Dimension(w > 0 ? w : 0, h > 0 ? h : 0); - } - return size; - } - @Override public void setAutoRepaint(boolean autoRepaint) { this.autoRepaint = autoRepaint; @@ -96,48 +90,41 @@ public class DrawingVisible extends Canvas implements Drawing { } } - public void setWidth (int width) { + public void setWidth(int width) { this.gr.setStroke(new BasicStroke(width)); } - public void setColor (Color col) { - this.gr.setColor (col); + public void setColor(Color col) { + this.gr.setColor(col); } - public void setBB (double long1, double long2, double lat1, double lat2) { + public void clear() { + this.gr.setColor(Color.WHITE); + this.gr.fillRect(0, 0, this.width, this.height); + } + + public void setBB(double long1, double long2, double lat1, double lat2) { if (long1 > long2 || lat1 > lat2) { throw new Error("DessinVisible.setBB : mauvaises coordonnees."); } - - /* Adapte la BB en fonction de la taille du dessin, pour préserver le ratio largeur/hauteur */ - double deltalong = long2 - long1 ; - double deltalat = lat2 - lat1 ; - double ratiobb = deltalong / deltalat ; - double ratiogr = width / height ; - - /* On ne peut qu'agrandir la BB, pour ne rien perdre. - * Si le ratiobb est trop petit, il faut agrandir deltalong - * s'il est trop grand, il faut agrandir deltalat. */ - if (ratiobb < ratiogr) { - /* De combien faut-il agrandir ? */ - double delta = (ratiogr - ratiobb) * deltalat ; - - this.long1 = (float)(long1 - 0.5*delta) ; - this.long2 = (float)(long2 + 0.5*delta) ; - this.lat1 = (float)lat1 ; - this.lat2 = (float)lat2 ; - } - else { - double delta = (deltalong / ratiogr) - deltalat ; - - this.long1 = (float)long1 ; - this.long2 = (float)long2 ; - this.lat1 = (float)(lat1 - 0.5*delta); - this.lat2 = (float)(lat2 + 0.5*delta); - } - - this.bb_is_set = true ; + + this.long1 = (float)long1; + this.long2 = (float)long2; + this.lat1= (float)lat1; + this.lat2 = (float)lat2; + + this.bb_is_set = true; + + double scale = 1 / Math.max(this.width / (double)this.getWidth(), this.height / (double)this.getHeight()); + + this.zoomAndPanListener.getCoordTransform().setToIdentity(); + this.zoomAndPanListener.getCoordTransform().translate((this.getWidth() - this.width * scale) / 2, + (this.getHeight() - this.height * scale) / 2); + this.zoomAndPanListener.getCoordTransform().scale(scale, scale); + this.zoomAndPanListener.setZoomLevel(0); + this.repaint(); + } private int projx(float lon) { @@ -154,7 +141,7 @@ public class DrawingVisible extends Canvas implements Drawing { } } - public void drawLine (float long1, float lat1, float long2, float lat2) { + public void drawLine(float long1, float lat1, float long2, float lat2) { this.checkBB() ; int x1 = this.projx(long1) ; int x2 = this.projx(long2) ; @@ -165,7 +152,7 @@ public class DrawingVisible extends Canvas implements Drawing { this.doAutoPaint(); } - public void drawPoint (float lon, float lat, int width) { + public void drawPoint(float lon, float lat, int width) { this.checkBB() ; int x = this.projx(lon) - width / 2 ; int y = this.projy(lat) - width / 2 ; @@ -173,7 +160,7 @@ public class DrawingVisible extends Canvas implements Drawing { this.doAutoPaint(); } - public void putText (float lon, float lat, String txt) { + public void putText(float lon, float lat, String txt) { this.checkBB() ; int x = this.projx(lon) ; int y = this.projy(lat) ; diff --git a/src/main/org/insa/drawing/ZoomAndPanListener.java b/src/main/org/insa/drawing/ZoomAndPanListener.java index 9827265..0259307 100644 --- a/src/main/org/insa/drawing/ZoomAndPanListener.java +++ b/src/main/org/insa/drawing/ZoomAndPanListener.java @@ -1,4 +1,5 @@ package org.insa.drawing; + import java.awt.*; import java.awt.event.*; import java.awt.geom.AffineTransform; @@ -31,6 +32,11 @@ public class ZoomAndPanListener implements MouseListener, MouseMotionListener, M this.maxZoomLevel = maxZoomLevel; this.zoomMultiplicationFactor = zoomMultiplicationFactor; } + + public void translate(double dx, double dy) { + coordTransform.translate(dx, dy); + targetComponent.repaint(); + } public void mouseClicked(MouseEvent e) { diff --git a/src/main/org/insa/drawing/graph/BasicGraphPalette.java b/src/main/org/insa/drawing/graph/BasicGraphPalette.java new file mode 100644 index 0000000..5bef312 --- /dev/null +++ b/src/main/org/insa/drawing/graph/BasicGraphPalette.java @@ -0,0 +1,97 @@ +package org.insa.drawing.graph; + +import java.awt.Color; + +import org.insa.graph.RoadInformation.RoadType; + +public class BasicGraphPalette implements GraphPalette { + + // Color types for arc. + static final Color motorway = Color.RED; + static final Color bigroad = new Color(255, 105, 0); + static final Color smallroad = new Color(255, 234, 0); + static final Color coastline = Color.BLUE; + + // Default point width + static final int DEFAULT_POINT_WIDTH = 1; + + /** + * + */ + public BasicGraphPalette() { } + + @Override + public int getDefaultPointWidth() { + return 2; + } + + @Override + public Color getDefaultPointColor() { + return Color.GREEN; + } + + @Override + public Color getColorForType(RoadType type) { + Color color = Color.BLACK; + switch (type) { + case MOTORWAY: + color = motorway; + break; + case TRUNK: + case PRIMARY: + case SECONDARY: + case MOTORWAY_LINK: + case TRUNK_LINK: + case PRIMARY_LINK: + color = bigroad; + break; + case SECONDARY_LINK: + case TERTIARY: + case RESIDENTIAL: + case UNCLASSIFIED: + case ROAD: + case LIVING_STREET: + case SERVICE: + case ROUNDABOUT: + color = smallroad; + break; + case COASTLINE: + color = coastline; + break; + } + return color; + } + + @Override + public int getWidthForType(RoadType type) { + int width = 1; + switch (type) { + case MOTORWAY: + width = 2; + break; + case TRUNK: + case PRIMARY: + case SECONDARY: + case MOTORWAY_LINK: + case TRUNK_LINK: + case PRIMARY_LINK: + width = 1; + break; + case SECONDARY_LINK: + case TERTIARY: + case RESIDENTIAL: + case UNCLASSIFIED: + case ROAD: + case LIVING_STREET: + case SERVICE: + case ROUNDABOUT: + width = 1; + break; + case COASTLINE: + width = 4; + break; + } + return width; + } + +} diff --git a/src/main/org/insa/drawing/graph/BlackAndWhiteGraphPalette.java b/src/main/org/insa/drawing/graph/BlackAndWhiteGraphPalette.java new file mode 100644 index 0000000..d2066b6 --- /dev/null +++ b/src/main/org/insa/drawing/graph/BlackAndWhiteGraphPalette.java @@ -0,0 +1,25 @@ +package org.insa.drawing.graph; + +import java.awt.Color; + +import org.insa.graph.RoadInformation.RoadType; + +public class BlackAndWhiteGraphPalette extends BasicGraphPalette { + + // Road colors (index + private final static Color[] ROAD_COLOR_FROM_WIDTH = { + null, new Color(140, 140, 140), new Color(80, 80, 80), new Color(40, 40, 40), new Color(30, 30, 30) + }; + + @Override + public Color getDefaultPointColor() { + return Color.BLACK; + } + + @Override + public Color getColorForType(RoadType type) { + int width = getWidthForType(type); + return ROAD_COLOR_FROM_WIDTH[width]; + } + +} diff --git a/src/main/org/insa/drawing/graph/GraphDrawing.java b/src/main/org/insa/drawing/graph/GraphDrawing.java new file mode 100644 index 0000000..5bb0ea7 --- /dev/null +++ b/src/main/org/insa/drawing/graph/GraphDrawing.java @@ -0,0 +1,134 @@ +package org.insa.drawing.graph; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Iterator; + +import org.insa.drawing.Drawing; +import org.insa.graph.Arc; +import org.insa.graph.Graph; +import org.insa.graph.Node; +import org.insa.graph.Point; +import org.insa.graph.RoadInformation.RoadType; + +public class GraphDrawing { + + // Drawing + private Drawing drawing; + + // Palette + private GraphPalette palette; + + public GraphDrawing(Drawing drawing) { + this.drawing = drawing; + this.palette = new BasicGraphPalette(); + } + + public GraphDrawing(Drawing drawing, GraphPalette palette) { + this.drawing = drawing; + this.palette = palette; + } + + public void drawLine(Point p1, Point p2) { + drawing.drawLine(p1.getLongitude(), p1.getLatitude(), + p2.getLongitude(), p2.getLatitude()); + } + + public void drawPoint(Point p) { + drawPoint(p, palette.getDefaultPointWidth()); + } + + public void drawPoint(Point p, int width) { + drawing.drawPoint(p.getLongitude(), p.getLatitude(), width); + } + + public void drawPoint(Point p, int width, Color c) { + drawing.setColor(c); + drawing.drawPoint(p.getLongitude(), p.getLatitude(), width); + } + + /** + * Draw the given arc with automatic color and width depending + * on the road type. + * + * @param arc Arc to draw. + */ + public void drawArc(Arc arc) { + drawArc(arc, true); + } + + /** + * Draw the given arc. + * + * @param arc Arc to draw. + * @param autoColorAndWidth Set to true to set color and width based + * on the road type of the arc. + */ + public void drawArc(Arc arc, boolean autoColorAndWidth) { + ArrayList pts = arc.getPoints(); + if (!pts.isEmpty()) { + if (autoColorAndWidth) { + drawing.setColor(palette.getColorForType(arc.getInfo().getType())); + drawing.setWidth(palette.getWidthForType(arc.getInfo().getType())); + } + Iterator it1 = pts.iterator(); + Point prev = it1.next(); + while (it1.hasNext()) { + Point curr = it1.next(); + drawLine(prev, curr); + prev = curr; + } + } + } + + /** + * Initialize the drawing for the given graph. + * + * @param graph + */ + public void initialize(Graph graph) { + double minLon = Double.POSITIVE_INFINITY, minLat = Double.POSITIVE_INFINITY, + maxLon = Double.NEGATIVE_INFINITY, maxLat = Double.NEGATIVE_INFINITY; + for (Node node: graph.getNodes()) { + Point pt = node.getPoint(); + if (pt.getLatitude() < minLat) { + minLat = pt.getLatitude(); + } + if (pt.getLatitude() > maxLat) { + maxLat = pt.getLatitude(); + } + if (pt.getLongitude() < minLon) { + minLon = pt.getLongitude(); + } + if (pt.getLongitude() > maxLon) { + maxLon = pt.getLongitude(); + } + } + + double deltaLon = 0.02 * (maxLon - minLon), + deltaLat = 0.02 * (maxLat - minLat); + + drawing.setBB(minLon - deltaLon, maxLon + deltaLon, + minLat - deltaLat, maxLat + deltaLat); + } + + + /** + * Clear the drawing and draw the given graph on the drawing. + * + * @param graph Graph to draw. + */ + public void drawGraph(Graph graph) { + + drawing.clear(); + + initialize(graph); + + for (Node node: graph.getNodes()) { + for (Arc arc: node.getSuccessors()) { + drawArc(arc); + } + } + } + +} diff --git a/src/main/org/insa/drawing/graph/GraphPalette.java b/src/main/org/insa/drawing/graph/GraphPalette.java new file mode 100644 index 0000000..51d8d77 --- /dev/null +++ b/src/main/org/insa/drawing/graph/GraphPalette.java @@ -0,0 +1,33 @@ +package org.insa.drawing.graph; + +import java.awt.Color; + +import org.insa.graph.RoadInformation.RoadType; + +public interface GraphPalette { + + /** + * @return The default point width for this palette. + */ + public int getDefaultPointWidth(); + + /** + * @return The default point color for this palette. + */ + public Color getDefaultPointColor(); + + /** + * @param type Type of the road. + * + * @return Color associated to the given type of road. + */ + public Color getColorForType(RoadType type); + + /** + * @param type Type of the road. + * + * @return Width associated to the given type of road. + */ + public int getWidthForType(RoadType type); + +} diff --git a/src/main/org/insa/drawing/graph/PathDrawing.java b/src/main/org/insa/drawing/graph/PathDrawing.java new file mode 100644 index 0000000..3ff6b03 --- /dev/null +++ b/src/main/org/insa/drawing/graph/PathDrawing.java @@ -0,0 +1,52 @@ +package org.insa.drawing.graph; + +import java.awt.Color; + +import org.insa.drawing.Drawing; +import org.insa.graph.Arc; +import org.insa.graph.Path; + +public class PathDrawing { + + // Default color + public static final Color DEFAULT_PATH_COLOR = new Color(255, 0, 255); + + // Drawing + private Drawing drawing; + private GraphDrawing graphDrawing; + + /** + * @param drawing + */ + public PathDrawing(Drawing drawing) { + this.drawing = drawing; + this.graphDrawing = new GraphDrawing(drawing); + } + + /** + * Draw the given path with the given color. + * + * @param path + * @param color + */ + public void drawPath(Path path, Color color) { + this.graphDrawing.drawPoint(path.getFirstNode().getPoint(), 4, color); + this.drawing.setColor(color); + this.drawing.setWidth(2); + for (Arc arc: path.getArcs()) { + this.graphDrawing.drawArc(arc, false); + } + this.graphDrawing.drawPoint(path.getLastNode().getPoint(), 4, color); + } + + /** + * Draw the given path with default color. + * + * @param path + */ + public void drawPath(Path path) { + drawPath(path, DEFAULT_PATH_COLOR); + drawing.repaint(); + } + +} diff --git a/src/main/org/insa/graph/Arc.java b/src/main/org/insa/graph/Arc.java index 197db53..6405ba9 100644 --- a/src/main/org/insa/graph/Arc.java +++ b/src/main/org/insa/graph/Arc.java @@ -45,7 +45,7 @@ public class Arc { /** * @return Destination node of this arc. */ - public Node getDest() { + public Node getDestination() { return dest; } diff --git a/src/main/org/insa/graph/Graph.java b/src/main/org/insa/graph/Graph.java index b95a9a8..07b30f6 100644 --- a/src/main/org/insa/graph/Graph.java +++ b/src/main/org/insa/graph/Graph.java @@ -10,6 +10,10 @@ public class Graph { // Nodes of the graph. private ArrayList nodes; + /** + * @param mapId + * @param nodes + */ public Graph(int mapId, ArrayList nodes) { this.mapId = mapId; this.nodes = nodes; @@ -24,5 +28,13 @@ public class Graph { * @return Map ID of this graph. */ public int getMapId() { return mapId; } + + /** + * @return Return the transpose graph of this graph. + */ + public Graph transpose() { + // TODO: + return null; + } } diff --git a/src/main/org/insa/graph/Node.java b/src/main/org/insa/graph/Node.java index 07a4eb0..413da02 100644 --- a/src/main/org/insa/graph/Node.java +++ b/src/main/org/insa/graph/Node.java @@ -2,7 +2,7 @@ package org.insa.graph; import java.util.ArrayList; -public class Node { +public class Node implements Comparable { // ID of the node. private int id; @@ -49,4 +49,17 @@ public class Node { */ public Point getPoint() { return point; } + @Override + public boolean equals(Object other) { + if (other instanceof Node) { + return getId() == ((Node) other).getId(); + } + return false; + } + + @Override + public int compareTo(Node other) { + return Integer.compare(getId(), other.getId()); + } + } diff --git a/src/main/org/insa/graph/io/BinaryGraphReader.java b/src/main/org/insa/graph/io/BinaryGraphReader.java index e328344..f8d9446 100644 --- a/src/main/org/insa/graph/io/BinaryGraphReader.java +++ b/src/main/org/insa/graph/io/BinaryGraphReader.java @@ -3,6 +3,9 @@ package org.insa.graph.io; import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; + +import javax.sound.midi.ControllerEventListener; import org.insa.graph.Arc; import org.insa.graph.Graph; @@ -149,7 +152,9 @@ public class BinaryGraphReader extends BinaryReader implements AbstractGraphRead // And reverse arc if its a two-way road. if (!info.isOneWay()) { // Add without segments. - dest.addSuccessor(new Arc(orig, length, info)); + ArrayList rPoints = new ArrayList(points); + Collections.reverse(rPoints); + dest.addSuccessor(new Arc(orig, length, info, rPoints)); } } diff --git a/src/main/org/insa/graph/io/BinaryPathReader.java b/src/main/org/insa/graph/io/BinaryPathReader.java index 735092c..0819a51 100644 --- a/src/main/org/insa/graph/io/BinaryPathReader.java +++ b/src/main/org/insa/graph/io/BinaryPathReader.java @@ -4,6 +4,7 @@ import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; +import org.insa.graph.Arc; import org.insa.graph.Graph; import org.insa.graph.Node; import org.insa.graph.Path; @@ -35,23 +36,36 @@ public class BinaryPathReader extends BinaryReader implements AbstractPathReader // Number of nodes in the path (without first and last). int nbNodes = dis.readInt(); - ArrayList nodes = new ArrayList(nbNodes + 2); + ArrayList arcs = new ArrayList(); - // Read first node - nodes.add(readNode(graph)); - - // Read last node - Node lastNode = readNode(graph); + // Skip (duplicate) first and last node + readNode(graph); + readNode(graph); // Read intermediate nodes: - for (int node = 0; node < nbNodes; ++node) { + ArrayList nodes = new ArrayList(); + for (int i = 0; i < nbNodes; ++i) { nodes.add(readNode(graph)); } - // Add last node - nodes.add(lastNode); + Node current = nodes.get(0); + for (int i = 1; i < nodes.size(); ++i) { + Node node = nodes.get(i); + Arc minArc = null; + for (Arc arc: current.getSuccessors()) { + if (arc.getDestination().equals(node) + && (minArc == null || arc.getMinimumTravelTime() < minArc.getMinimumTravelTime())) { + minArc = arc; + } + } + arcs.add(minArc); + if (minArc == null) { + System.out.println("No arc found between nodes " + current.getId() + " and " + node.getId() + "\n"); + } + current = node; + } - return new Path(graph, nodes); + return new Path(graph, nodes.get(0), arcs); } /** diff --git a/src/main/org/insa/graph/io/Openfile.java b/src/main/org/insa/graph/io/Openfile.java new file mode 100644 index 0000000..3932dc1 --- /dev/null +++ b/src/main/org/insa/graph/io/Openfile.java @@ -0,0 +1,83 @@ +package org.insa.graph.io ; + +import java.io.* ; +import java.util.zip.* ; + +/** + * Class that can be used to open (compressed) files from a specified + * set of folders or for a full path. + * + */ +public class Openfile { + + /** + * These folders will be looked up for the files. + * + */ + private static final String[] datadirs = { + + // INSA folder containing maps. + "/home/commetud/3eme Annee MIC/Graphes-et-Algorithmes/Maps", + + // INSA folder containing paths. + "/home/commetud/3eme Annee MIC/Graphes-et-Algorithmes/", + + // Maps sub-folder. + "Maps", + + // Current folder. + "." + }; + + /** + * Available extensions. + * + */ + private static final String[] extensions = { ".map", ".gz", ".map.gz", ".path", ".path.gz", "" }; + + /** + * Open the given file and return a corresponding DataInputStream. + * + * @param filename Name of the file to open (without extension) or full path to the given file. + * @throws IOException + */ + public static DataInputStream open(String filename) throws IOException { + + File file = null; + String fullpath = null; + + // If the filename containing only a name (not a path): + if (filename.equals (new File(filename).getName())) { + + + for (String ext: extensions) { + String fname = filename + ext; + for (int index = 0; file == null && index < datadirs.length; ++index) { + fullpath = datadirs[index] + File.separator + fname; + file = new File(fullpath); + if (!file.exists()) { + file = null; + } + } + } + + } + else { + fullpath = filename; + file = new File(filename); + } + + InputStream fileInput = new FileInputStream(new File(fullpath)); + + // If the file is compressed. + if (fullpath.endsWith(".gz")) { + fileInput = new GZIPInputStream(fileInput) ; + } + else { + fileInput = new BufferedInputStream(fileInput) ; + } + + return new DataInputStream(fileInput) ; + } + +} diff --git a/src/test/org/insa/graph/io/BinaryGraphReaderTest.java b/src/test/org/insa/graph/io/BinaryGraphReaderTest.java new file mode 100644 index 0000000..6b0c676 --- /dev/null +++ b/src/test/org/insa/graph/io/BinaryGraphReaderTest.java @@ -0,0 +1,60 @@ +package org.insa.graph.io; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.util.ArrayList; + +import org.insa.graph.Graph; +import org.insa.graph.Node; +import org.insa.graph.Point; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class BinaryGraphReaderTest { + + // Epsilon for latitude and longitude. + private static final double EPS = 1e-5; + + private static Graph midip; + + @BeforeAll + static void initAll() throws IOException { + BinaryGraphReader reader = new BinaryGraphReader(Openfile.open("midip")); + midip = reader.read(); + } + + void assertPointAt(Point p1, double longitude, double latitude) { + assertEquals(p1.getLongitude(), longitude, EPS); + assertEquals(p1.getLatitude(), latitude, EPS); + } + + @Test + void testMidipNodes() { + ArrayList nodes = midip.getNodes(); + + assertEquals(nodes.size(), 150827); + + // Check the locations of some nodes. + assertPointAt(nodes.get(58411).getPoint(), 1.799864, 43.92864); + assertPointAt(nodes.get(133312).getPoint(), 0.539752, 43.317505); + assertPointAt(nodes.get(113688).getPoint(), 1.682739, 44.799774); + assertPointAt(nodes.get(118141).getPoint(), 0.274857, 43.47475); + assertPointAt(nodes.get(146918).getPoint(), 0.116148, 43.811386); + + } + + @Test + void testMidipArcs() { + // TODO: Check the number of edges. + // TODO: Check information for some edges. + } + +}