299 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			299 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package org.insa.graph.io;
 | |
| 
 | |
| import java.io.DataInputStream;
 | |
| import java.io.IOException;
 | |
| import java.util.ArrayList;
 | |
| import java.util.EnumMap;
 | |
| import java.util.List;
 | |
| 
 | |
| import org.insa.graph.AccessRestrictions;
 | |
| import org.insa.graph.AccessRestrictions.AccessMode;
 | |
| import org.insa.graph.AccessRestrictions.AccessRestriction;
 | |
| import org.insa.graph.Arc;
 | |
| import org.insa.graph.Graph;
 | |
| import org.insa.graph.GraphStatistics;
 | |
| import org.insa.graph.Node;
 | |
| import org.insa.graph.Point;
 | |
| import org.insa.graph.RoadInformation;
 | |
| import org.insa.graph.RoadInformation.RoadType;
 | |
| 
 | |
| /**
 | |
|  * Implementation of {@link GraphReader} to read graph in binary format.
 | |
|  *
 | |
|  */
 | |
| public class BinaryGraphReader 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;
 | |
| 
 | |
|     // Length of the map id field (in bytes)
 | |
|     protected static final int MAP_ID_FIELD_LENGTH = 32;
 | |
| 
 | |
|     // List of observers
 | |
|     protected List<GraphReaderObserver> observers = new ArrayList<>();
 | |
| 
 | |
|     /**
 | |
|      * Parse the given long value into a new instance of AccessRestrictions.
 | |
|      * 
 | |
|      * @param access The value to parse.
 | |
|      * 
 | |
|      * @return New instance of access restrictions parsed from the given value.
 | |
|      */
 | |
|     protected static AccessRestrictions toAccessInformation(final long access) {
 | |
| 
 | |
|         // See the following for more information:
 | |
|         // https://github.com/Holt59/OSM2Graph/blob/master/src/main/org/laas/osm2graph/model/AccessData.java
 | |
| 
 | |
|         // The order of values inside this array is VERY IMPORTANT: For allRestrictions,
 | |
|         // the order correspond to the 4 bits value (i.e. FORBIDDEN is 0 or PRIVATE is
 | |
|         // 2) - UKNOWN is not included because value above 6 (FORESTRY) are all
 | |
|         // considered unknown.
 | |
|         final AccessRestriction[] allRestrictions = new AccessRestriction[]{
 | |
|                 AccessRestriction.FORBIDDEN, AccessRestriction.ALLOWED, AccessRestriction.PRIVATE,
 | |
|                 AccessRestriction.DESTINATION, AccessRestriction.DELIVERY,
 | |
|                 AccessRestriction.CUSTOMERS, AccessRestriction.FORESTRY };
 | |
| 
 | |
|         // The order of values inside this array is VERY IMPORTANT: The order is such
 | |
|         // that each 4-bits group of the long value is processed in the correct order,
 | |
|         // i.e. FOOT is processed first (4 lowest bits), and so on.
 | |
|         final AccessMode[] allModes = new AccessMode[]{ AccessMode.FOOT, null, AccessMode.BICYCLE,
 | |
|                 AccessMode.SMALL_MOTORCYCLE, AccessMode.AGRICULTURAL, AccessMode.MOTORCYCLE,
 | |
|                 AccessMode.MOTORCAR, AccessMode.HEAVY_GOODS, null, AccessMode.PUBLIC_TRANSPORT };
 | |
| 
 | |
|         // fill maps...
 | |
|         EnumMap<AccessMode, AccessRestriction> restrictions = new EnumMap<>(AccessMode.class);
 | |
|         long copyAccess = access;
 | |
|         for (AccessMode mode: allModes) {
 | |
|             if (mode == null) {
 | |
|                 continue; // filling cells
 | |
|             }
 | |
|             int value = (int) (copyAccess & 0xf);
 | |
|             if (value < allRestrictions.length) {
 | |
|                 restrictions.put(mode, allRestrictions[value]);
 | |
|             }
 | |
|             else {
 | |
|                 restrictions.put(mode, AccessRestriction.UNKNOWN);
 | |
|             }
 | |
|             copyAccess = copyAccess >> 4;
 | |
|         }
 | |
| 
 | |
|         return new AccessRestrictions(restrictions);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Convert a character to its corresponding road type.
 | |
|      * 
 | |
|      * @param ch Character to convert.
 | |
|      * 
 | |
|      * @return Road type corresponding to the given character.
 | |
|      * 
 | |
|      */
 | |
|     protected 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':
 | |
|         case 'l':
 | |
|             return RoadType.UNCLASSIFIED;
 | |
|         case 'm':
 | |
|             return RoadType.LIVING_STREET;
 | |
|         case 'n':
 | |
|             return RoadType.SERVICE;
 | |
|         case 'o':
 | |
|             return RoadType.ROUNDABOUT;
 | |
|         case 'p':
 | |
|             return RoadType.PEDESTRIAN;
 | |
|         case 'r':
 | |
|             return RoadType.CYCLEWAY;
 | |
|         case 's':
 | |
|             return RoadType.TRACK;
 | |
|         case 'z':
 | |
|             return RoadType.COASTLINE;
 | |
|         }
 | |
|         return RoadType.UNCLASSIFIED;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Create a new BinaryGraphReader that read from the given input stream.
 | |
|      * 
 | |
|      * @param dis Input stream to read from.
 | |
|      */
 | |
|     public BinaryGraphReader(DataInputStream dis) {
 | |
|         super(MAGIC_NUMBER, VERSION, dis);
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public void addObserver(GraphReaderObserver observer) {
 | |
|         observers.add(observer);
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public Graph read() throws IOException {
 | |
| 
 | |
|         // Read and check magic number and file version.
 | |
|         checkMagicNumberOrThrow(dis.readInt());
 | |
|         checkVersionOrThrow(dis.readInt());
 | |
| 
 | |
|         // Read map id.
 | |
|         String mapId;
 | |
|         String mapName = "";
 | |
| 
 | |
|         if (getCurrentVersion() < 6) {
 | |
|             mapId = "0x" + Integer.toHexString(dis.readInt());
 | |
|         }
 | |
|         else {
 | |
|             mapId = readFixedLengthString(MAP_ID_FIELD_LENGTH, "UTF-8");
 | |
|             mapName = dis.readUTF();
 | |
|         }
 | |
| 
 | |
|         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));
 | |
|         int maxSpeed = 0;
 | |
|         for (int descr = 0; descr < nbDesc; ++descr) {
 | |
|             final RoadInformation roadinf = readRoadInformation();
 | |
|             descs[descr] = roadinf;
 | |
|             observers.forEach((observer) -> observer.notifyNewDescriptorRead(roadinf));
 | |
| 
 | |
|             // Update max speed
 | |
|             maxSpeed = Math.max(roadinf.getMaximumSpeed(), maxSpeed);
 | |
|         }
 | |
| 
 | |
|         // Check format.
 | |
|         checkByteOrThrow(254);
 | |
| 
 | |
|         // Read successors and convert to arcs.
 | |
|         float maxLength = 0;
 | |
|         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.
 | |
|                 float length;
 | |
|                 if (getCurrentVersion() < 8) {
 | |
|                     length = dis.readUnsignedShort();
 | |
|                 }
 | |
|                 else {
 | |
|                     length = dis.readInt() / 1000.0f;
 | |
|                 }
 | |
|                 maxLength = Math.max(length, maxLength);
 | |
| 
 | |
|                 // 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 = Node.linkNodes(orig, dest, length, info, points);
 | |
|                 observers.forEach((observer) -> observer.notifyNewArcRead(arc));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Check format.
 | |
|         checkByteOrThrow(253);
 | |
| 
 | |
|         observers.forEach((observer) -> observer.notifyEndReading());
 | |
| 
 | |
|         this.dis.close();
 | |
| 
 | |
|         return new Graph(mapId, mapName, nodes, new GraphStatistics(maxSpeed, maxLength));
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Read the next road information from the stream.
 | |
|      * 
 | |
|      * @return The next RoadInformation in the stream.
 | |
|      * 
 | |
|      * @throws IOException if an error occurs while reading from the stream.
 | |
|      */
 | |
|     private RoadInformation readRoadInformation() throws IOException {
 | |
|         char type = (char) dis.readUnsignedByte();
 | |
|         int x = dis.readUnsignedByte();
 | |
|         AccessRestrictions access = new AccessRestrictions();
 | |
|         if (getCurrentVersion() >= 7) {
 | |
|             access = toAccessInformation(dis.readLong());
 | |
|         }
 | |
|         else if (getCurrentVersion() >= 6) {
 | |
|             // TODO: Try to create something...
 | |
|             dis.readUnsignedShort();
 | |
|         }
 | |
|         return new RoadInformation(toRoadType(type), access, (x & 0x80) > 0, (x & 0x7F) * 5,
 | |
|                 dis.readUTF());
 | |
|     }
 | |
| 
 | |
| }
 |