From 823ed077707270d5d2398b481cd562c27e1b89f3 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Tue, 27 Feb 2018 23:23:22 +0100 Subject: [PATCH] Add observers to GraphReader. --- .../insa/graph/io/BinaryGraphReaderV2.java | 323 ++++++++++-------- src/main/org/insa/graph/io/BinaryReader.java | 131 +++---- src/main/org/insa/graph/io/GraphReader.java | 7 + .../insa/graph/io/GraphReaderObserver.java | 64 ++++ .../insa/graphics/GraphReaderProgressBar.java | 131 +++++++ src/main/org/insa/graphics/MainWindow.java | 1 + 6 files changed, 450 insertions(+), 207 deletions(-) create mode 100644 src/main/org/insa/graph/io/GraphReaderObserver.java create mode 100644 src/main/org/insa/graphics/GraphReaderProgressBar.java diff --git a/src/main/org/insa/graph/io/BinaryGraphReaderV2.java b/src/main/org/insa/graph/io/BinaryGraphReaderV2.java index 408cab9..c7788f9 100644 --- a/src/main/org/insa/graph/io/BinaryGraphReaderV2.java +++ b/src/main/org/insa/graph/io/BinaryGraphReaderV2.java @@ -13,158 +13,187 @@ import org.insa.graph.RoadInformation; import org.insa.graph.RoadInformation.RoadType; public class BinaryGraphReaderV2 extends BinaryReader implements GraphReader { - - // Map version and magic number targeted for this reader. - private static final int VERSION = 5; - private static final int MAGIC_NUMBER = 0x208BC3B3; - /** - * Convert a character to its corresponding road type. - * - * @param ch Character to convert. - * - * @return Road type corresponding to ch. - * - * @see http://wiki.openstreetmap.org/wiki/Highway_tag_usage. - */ - public static RoadType toRoadType(char ch) { - switch (ch) { - case 'a': return RoadType.MOTORWAY; - case 'b': return RoadType.TRUNK; - case 'c': return RoadType.PRIMARY; - case 'd': return RoadType.SECONDARY; - case 'e': return RoadType.MOTORWAY_LINK; - case 'f': return RoadType.TRUNK_LINK; - case 'g': return RoadType.PRIMARY_LINK; - case 'h': return RoadType.SECONDARY_LINK; - case 'i': return RoadType.TERTIARY; - case 'j': return RoadType.RESIDENTIAL; - case 'k': return RoadType.UNCLASSIFIED; - case 'l': return RoadType.ROAD; - case 'm': return RoadType.LIVING_STREET; - case 'n': return RoadType.SERVICE; - case 'o': return RoadType.ROUNDABOUT; - case 'z': return RoadType.COASTLINE; - } - return RoadType.UNCLASSIFIED; - } + // Map version and magic number targeted for this reader. + private static final int VERSION = 5; + private static final int MAGIC_NUMBER = 0x208BC3B3; - /** - * Create a new BinaryGraphReader using the given DataInputStream. - * - * @param dis - */ - public BinaryGraphReaderV2(DataInputStream dis) { - super(MAGIC_NUMBER, VERSION, dis); - } + /** + * Convert a character to its corresponding road type. + * + * @param ch Character to convert. + * + * @return Road type corresponding to ch. + * + * @see http://wiki.openstreetmap.org/wiki/Highway_tag_usage. + */ + public static RoadType toRoadType(char ch) { + switch (ch) { + case 'a': + return RoadType.MOTORWAY; + case 'b': + return RoadType.TRUNK; + case 'c': + return RoadType.PRIMARY; + case 'd': + return RoadType.SECONDARY; + case 'e': + return RoadType.MOTORWAY_LINK; + case 'f': + return RoadType.TRUNK_LINK; + case 'g': + return RoadType.PRIMARY_LINK; + case 'h': + return RoadType.SECONDARY_LINK; + case 'i': + return RoadType.TERTIARY; + case 'j': + return RoadType.RESIDENTIAL; + case 'k': + return RoadType.UNCLASSIFIED; + case 'l': + return RoadType.ROAD; + case 'm': + return RoadType.LIVING_STREET; + case 'n': + return RoadType.SERVICE; + case 'o': + return RoadType.ROUNDABOUT; + case 'z': + return RoadType.COASTLINE; + } + return RoadType.UNCLASSIFIED; + } - @Override - public Graph read() throws IOException { - - // Read and check magic number and file version. - checkMagicNumberOrThrow(dis.readInt()); - checkVersionOrThrow(dis.readInt()); - - // Read map id. - int mapId = dis.readInt(); + /** + * Create a new BinaryGraphReader using the given DataInputStream. + * + * @param dis + */ + public BinaryGraphReaderV2(DataInputStream dis) { + super(MAGIC_NUMBER, VERSION, dis); + } - // Number of descriptors and nodes. - int nbDesc = dis.readInt(); - int nbNodes = dis.readInt(); - - // Number of successors for each nodes. - int[] nbSuccessors = new int[nbNodes]; + @Override + public Graph read() throws IOException { - // Construct an array list with initial capacity of nbNodes. - ArrayList nodes = new ArrayList(nbNodes); - - // Read nodes. - for (int node = 0; node < nbNodes; ++node) { - float longitude = ((float)dis.readInt ()) / 1E6f; - float latitude = ((float)dis.readInt ()) / 1E6f; - nbSuccessors[node] = dis.readUnsignedByte(); - nodes.add(new Node(node, new Point(longitude, latitude))); - } - - // Check format. - checkByteOrThrow(255); - - // Read descriptors. - RoadInformation[] descs = new RoadInformation[nbDesc]; - - // Read - for (int descr = 0; descr < nbDesc; ++descr) { - descs[descr] = readRoadInformation(); - } - - // Check format. - checkByteOrThrow(254); - - // Read successors and convert to arcs. - for (int node = 0; node < nbNodes; ++node) { - for (int succ = 0; succ < nbSuccessors[node]; ++succ) { - - // Read target node number. - int destNode = this.read24bits(); - - // Read information number. - int descrNum = this.read24bits(); - - // Length of the arc. - int length = dis.readUnsignedShort(); - - // Number of segments. - int nbSegments = dis.readUnsignedShort(); + // Read and check magic number and file version. + checkMagicNumberOrThrow(dis.readInt()); + checkVersionOrThrow(dis.readInt()); - // Chain of points corresponding to the segments. - ArrayList points = new ArrayList(nbSegments + 2); - points.add(nodes.get(node).getPoint()); - - for (int seg = 0; seg < nbSegments; ++seg) { - Point lastPoint = points.get(points.size() - 1); - - float dlon = (dis.readShort()) / 2.0e5f; - float dlat = (dis.readShort()) / 2.0e5f; - - points.add(new Point(lastPoint.getLongitude() + dlon, - lastPoint.getLatitude() + dlat)); - } - - points.add(nodes.get(destNode).getPoint()); - - RoadInformation info = descs[descrNum]; - Node orig = nodes.get(node); - Node dest = nodes.get(destNode); + // Read map id. + int mapId = dis.readInt(); + + observers.forEach((observer) -> observer.notifyStartReading(mapId)); + + // Number of descriptors and nodes. + int nbDesc = dis.readInt(); + int nbNodes = dis.readInt(); + + // Number of successors for each nodes. + int[] nbSuccessors = new int[nbNodes]; + int nbTotalSuccessors = 0; + + // Construct an array list with initial capacity of nbNodes. + ArrayList nodes = new ArrayList(nbNodes); + + // Read nodes. + observers.forEach((observer) -> observer.notifyStartReadingNodes(nbNodes)); + for (int node = 0; node < nbNodes; ++node) { + float longitude = ((float) dis.readInt()) / 1E6f; + float latitude = ((float) dis.readInt()) / 1E6f; + nbSuccessors[node] = dis.readUnsignedByte(); + nbTotalSuccessors += nbSuccessors[node]; + final Node aNode = new Node(node, new Point(longitude, latitude)); + nodes.add(aNode); + observers.forEach((observer) -> observer.notifyNewNodeRead(aNode)); + } + + // Check format. + checkByteOrThrow(255); + + // Read descriptors. + RoadInformation[] descs = new RoadInformation[nbDesc]; + + // Read + observers.forEach((observer) -> observer.notifyStartReadingDescriptors(nbDesc)); + for (int descr = 0; descr < nbDesc; ++descr) { + final RoadInformation roadinf = readRoadInformation(); + descs[descr] = roadinf; + observers.forEach((observer) -> observer.notifyNewDescriptorRead(roadinf)); + } + + // Check format. + checkByteOrThrow(254); + + // Read successors and convert to arcs. + final int copyNbTotalSuccesors = nbTotalSuccessors; // Stupid Java... + observers.forEach((observer) -> observer.notifyStartReadingArcs(copyNbTotalSuccesors)); + for (int node = 0; node < nbNodes; ++node) { + for (int succ = 0; succ < nbSuccessors[node]; ++succ) { + + // Read target node number. + int destNode = this.read24bits(); + + // Read information number. + int descrNum = this.read24bits(); + + // Length of the arc. + int length = dis.readUnsignedShort(); + + // Number of segments. + int nbSegments = dis.readUnsignedShort(); + + // Chain of points corresponding to the segments. + ArrayList points = new ArrayList(nbSegments + 2); + points.add(nodes.get(node).getPoint()); + + for (int seg = 0; seg < nbSegments; ++seg) { + Point lastPoint = points.get(points.size() - 1); + + float dlon = (dis.readShort()) / 2.0e5f; + float dlat = (dis.readShort()) / 2.0e5f; + + points.add(new Point(lastPoint.getLongitude() + dlon, lastPoint.getLatitude() + dlat)); + } + + points.add(nodes.get(destNode).getPoint()); + + RoadInformation info = descs[descrNum]; + Node orig = nodes.get(node); + Node dest = nodes.get(destNode); + + // Add successor to initial arc. + Arc arc = new Arc(orig, dest, length, info, points); + + // And reverse arc if its a two-way road. + if (!info.isOneWay()) { + // Add without segments. + ArrayList rPoints = new ArrayList(points); + Collections.reverse(rPoints); + new Arc(dest, orig, length, info, rPoints); + } + observers.forEach((observer) -> observer.notifyNewArcRead(arc)); + } + } + + // Check format. + checkByteOrThrow(253); + + observers.forEach((observer) -> observer.notifyEndReading()); + + return new Graph(mapId, nodes); + } + + /** + * Read the next road information from the stream. + * + * @throws IOException + */ + private RoadInformation readRoadInformation() throws IOException { + char type = (char) dis.readUnsignedByte(); + int x = dis.readUnsignedByte(); + return new RoadInformation(toRoadType(type), (x & 0x80) > 0, (x & 0x7F) * 5, dis.readUTF()); + } - // Add successor to initial arc. - new Arc(orig, dest, length, info, points); - - // And reverse arc if its a two-way road. - if (!info.isOneWay()) { - // Add without segments. - ArrayList rPoints = new ArrayList(points); - Collections.reverse(rPoints); - new Arc(dest, orig, length, info, rPoints); - } - - } - } - - // Check format. - checkByteOrThrow(253); - - return new Graph(mapId, nodes); - } - - /** - * Read the next road information from the stream. - * - * @throws IOException - */ - private RoadInformation readRoadInformation() throws IOException { - char type = (char)dis.readUnsignedByte(); - int x = dis.readUnsignedByte() ; - return new RoadInformation(toRoadType(type), (x & 0x80) > 0, (x & 0x7F) * 5, dis.readUTF()); - } - } diff --git a/src/main/org/insa/graph/io/BinaryReader.java b/src/main/org/insa/graph/io/BinaryReader.java index 33a7400..2992316 100644 --- a/src/main/org/insa/graph/io/BinaryReader.java +++ b/src/main/org/insa/graph/io/BinaryReader.java @@ -2,66 +2,77 @@ package org.insa.graph.io; import java.io.DataInputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; public abstract class BinaryReader { - - // Map version and magic number targeted for this reader. - private int version; - private int magicNumber; - - // InputStream - protected DataInputStream dis; - - protected BinaryReader(int magicNumber, int version, DataInputStream dis) { - this.magicNumber = magicNumber; - this.version = version; - this.dis = dis; - } - - /** - * @param version - * @throws BadVersionException - */ - public void checkVersionOrThrow(int version) throws BadVersionException { - if (this.version != version) { - throw new BadVersionException(version, this.version); - } - } - - /** - * @param magicNumber - * @throws BadMagicNumberException - */ - public void checkMagicNumberOrThrow(int magicNumber) throws BadMagicNumberException { - if (this.magicNumber != magicNumber) { - throw new BadMagicNumberException(magicNumber, this.magicNumber); - } - } - - /** - * Check if the next byte in the input stream correspond to the - * given byte. This function consumes the next byte in the input - * stream. - * - * @param i Byte to check against. - * - * @throws IOException - */ - public void checkByteOrThrow(int i) throws IOException { - if (dis.readUnsignedByte() != i) { - throw new BadFormatException(); - } - } - - /** - * Read 24 bits from the stream and return the corresponding integer value. - * - * @return Integer value read from the next 24 bits of the stream. - * - * @throws IOException - */ - protected int read24bits() throws IOException { - int x = dis.readUnsignedShort() ; - return (x << 8) | dis.readUnsignedByte() ; - } + + // Map version and magic number targeted for this reader. + private int version; + private int magicNumber; + + // InputStream + protected DataInputStream dis; + + // List of observers + protected List observers = new ArrayList<>(); + + protected BinaryReader(int magicNumber, int version, DataInputStream dis) { + this.magicNumber = magicNumber; + this.version = version; + this.dis = dis; + } + + /** + * {@inheritDoc} + */ + public void addObserver(GraphReaderObserver observer) { + observers.add(observer); + } + + /** + * @param version + * @throws BadVersionException + */ + public void checkVersionOrThrow(int version) throws BadVersionException { + if (this.version != version) { + throw new BadVersionException(version, this.version); + } + } + + /** + * @param magicNumber + * @throws BadMagicNumberException + */ + public void checkMagicNumberOrThrow(int magicNumber) throws BadMagicNumberException { + if (this.magicNumber != magicNumber) { + throw new BadMagicNumberException(magicNumber, this.magicNumber); + } + } + + /** + * Check if the next byte in the input stream correspond to the given byte. This + * function consumes the next byte in the input stream. + * + * @param i Byte to check against. + * + * @throws IOException + */ + public void checkByteOrThrow(int i) throws IOException { + if (dis.readUnsignedByte() != i) { + throw new BadFormatException(); + } + } + + /** + * Read 24 bits from the stream and return the corresponding integer value. + * + * @return Integer value read from the next 24 bits of the stream. + * + * @throws IOException + */ + protected int read24bits() throws IOException { + int x = dis.readUnsignedShort(); + return (x << 8) | dis.readUnsignedByte(); + } } diff --git a/src/main/org/insa/graph/io/GraphReader.java b/src/main/org/insa/graph/io/GraphReader.java index bef9946..113ac56 100644 --- a/src/main/org/insa/graph/io/GraphReader.java +++ b/src/main/org/insa/graph/io/GraphReader.java @@ -6,6 +6,13 @@ import org.insa.graph.Graph; public interface GraphReader { + /** + * Add a new observer to this graph reader. + * + * @param observer + */ + public void addObserver(GraphReaderObserver observer); + /** * Read a graph an returns it. * diff --git a/src/main/org/insa/graph/io/GraphReaderObserver.java b/src/main/org/insa/graph/io/GraphReaderObserver.java new file mode 100644 index 0000000..44d5753 --- /dev/null +++ b/src/main/org/insa/graph/io/GraphReaderObserver.java @@ -0,0 +1,64 @@ +package org.insa.graph.io; + +import org.insa.graph.Arc; +import org.insa.graph.Node; +import org.insa.graph.RoadInformation; + +public interface GraphReaderObserver { + + /** + * Notify observer about information on the graph, this method is always the + * first called + * + * @param mapId ID of the graph. + */ + public void notifyStartReading(int mapId); + + /** + * Notify that the graph has been fully read. + */ + public void notifyEndReading(); + + /** + * Notify that the reader is starting to read node. + * + * @param nNodes Number of nodes to read. + */ + public void notifyStartReadingNodes(int nNodes); + + /** + * Notify that a new nodes has been read. + * + * @param node + */ + public void notifyNewNodeRead(Node node); + + /** + * Notify that the reader is starting to read descriptor/road informations. + * + * @param nDesc Number of descriptors to read. + */ + public void notifyStartReadingDescriptors(int nDesc); + + /** + * Notify that a new descriptor has been read. + * + * @param desc + */ + public void notifyNewDescriptorRead(RoadInformation desc); + + /** + * Notify that the reader is starting to read arcs. + * + * @param nArcs Number of arcs to read (!= number of arcs in the graph). + */ + public void notifyStartReadingArcs(int nArcs); + + /** + * Notify that a new arc has been read. + * + * @param arc + */ + public void notifyNewArcRead(Arc arc); + +} diff --git a/src/main/org/insa/graphics/GraphReaderProgressBar.java b/src/main/org/insa/graphics/GraphReaderProgressBar.java new file mode 100644 index 0000000..04ff7f3 --- /dev/null +++ b/src/main/org/insa/graphics/GraphReaderProgressBar.java @@ -0,0 +1,131 @@ +package org.insa.graphics; + +import java.awt.Component; +import java.awt.Dimension; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.border.EmptyBorder; + +import org.insa.graph.Arc; +import org.insa.graph.Node; +import org.insa.graph.RoadInformation; +import org.insa.graph.io.GraphReaderObserver; + +/** + * One-time use GraphReaderObserver that display progress in three different + * JProgressBar. + * + * @author Mikael + * + */ +public class GraphReaderProgressBar extends JDialog implements GraphReaderObserver { + + /** + * + */ + private static final long serialVersionUID = -1; + + // Index... + private static final int NODE = 0, DESC = 1, ARC = 2; + + // Progress bar + private final JProgressBar[] progressBars = new JProgressBar[3]; + + // Current element read, and modulo. + private int[] counters = new int[] { 0, 0, 0 }; + private int[] modulos = new int[3]; + + public GraphReaderProgressBar(JFrame owner) { + super(owner); + this.setVisible(false); + setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); + + final String[] infos = { "nodes", "road informations", "arcs" }; + + JPanel pane = new JPanel(); + pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); + pane.setBorder(new EmptyBorder(15, 15, 15, 15)); + pane.add(Box.createVerticalGlue()); + + for (int i = 0; i < 3; ++i) { + JLabel label = new JLabel("Reading " + infos[i] + "... "); + label.setAlignmentX(Component.LEFT_ALIGNMENT); + progressBars[i] = new JProgressBar(); + progressBars[i].setAlignmentX(Component.LEFT_ALIGNMENT); + pane.add(label); + pane.add(progressBars[i]); + } + + pane.add(Box.createVerticalGlue()); + + pane.setPreferredSize(new Dimension(300, 120)); + + setContentPane(pane); + + // setModal(true); + setLocationRelativeTo(owner); + + pack(); + + } + + @Override + public void notifyStartReading(int mapId) { + setTitle(String.format("Reading graph %#x... ", mapId)); + setVisible(true); + } + + @Override + public void notifyEndReading() { + setVisible(false); + } + + protected void initProgressBar(int index, int max) { + progressBars[index].setMaximum(max); + modulos[index] = max / 100; + } + + protected void incCounter(int index) { + counters[index] += 1; + if (counters[index] % modulos[index] == 0) { + progressBars[index].setValue(counters[index]); + } + } + + @Override + public void notifyStartReadingNodes(int nNodes) { + initProgressBar(NODE, nNodes); + } + + @Override + public void notifyNewNodeRead(Node node) { + incCounter(NODE); + } + + @Override + public void notifyStartReadingDescriptors(int nDesc) { + initProgressBar(DESC, nDesc); + } + + @Override + public void notifyNewDescriptorRead(RoadInformation desc) { + incCounter(DESC); + } + + @Override + public void notifyStartReadingArcs(int nArcs) { + initProgressBar(ARC, nArcs); + } + + @Override + public void notifyNewArcRead(Arc arc) { + incCounter(ARC); + } + +} diff --git a/src/main/org/insa/graphics/MainWindow.java b/src/main/org/insa/graphics/MainWindow.java index 831b892..d6f53c3 100644 --- a/src/main/org/insa/graphics/MainWindow.java +++ b/src/main/org/insa/graphics/MainWindow.java @@ -518,6 +518,7 @@ public class MainWindow extends JFrame { else { reader = new BinaryGraphReader(stream); } + reader.addObserver(new GraphReaderProgressBar(MainWindow.this)); try { graph = reader.read(); }