Add observers to GraphReader.

This commit is contained in:
Holt59
2018-02-27 23:23:22 +01:00
parent 7b2f3f3549
commit 823ed07770
6 changed files with 450 additions and 207 deletions

View File

@@ -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<Node> nodes = new ArrayList<Node>(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<Point> points = new ArrayList<Point>(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<Node> nodes = new ArrayList<Node>(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<Point> points = new ArrayList<Point>(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<Point> rPoints = new ArrayList<Point>(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<Point> rPoints = new ArrayList<Point>(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());
}
}

View File

@@ -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<GraphReaderObserver> 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();
}
}

View File

@@ -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.
*

View File

@@ -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);
}