package net.osmand.binary; import gnu.trove.map.hash.TIntObjectHashMap; import java.text.MessageFormat; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule; import net.osmand.util.Algorithms; public class RouteDataObject { /*private */static final int RESTRICTION_SHIFT = 3; /*private */static final int RESTRICTION_MASK = 7; public final RouteRegion region; // all these arrays supposed to be immutable! // These fields accessible from C++ public int[] types; public int[] pointsX; public int[] pointsY; public long[] restrictions; public int[][] pointTypes; public long id; public TIntObjectHashMap names; public final static float NONE_MAX_SPEED = 40f; public RouteDataObject(RouteRegion region) { this.region = region; } public RouteDataObject(RouteRegion region, int[] nameIds, String[] nameValues) { this.region = region; if (nameIds.length > 0) { names = new TIntObjectHashMap(); } for (int i = 0; i < nameIds.length; i++) { names.put(nameIds[i], nameValues[i]); } } public RouteDataObject(RouteDataObject copy) { this.region = copy.region; this.pointsX = copy.pointsX; this.pointsY = copy.pointsY; this.types = copy.types; this.names = copy.names; this.restrictions = copy.restrictions; this.pointTypes = copy.pointTypes; this.id = copy.id; } public long getId() { return id; } public String getName(){ if(names != null ) { return names.get(region.nameTypeRule); } return null; } public TIntObjectHashMap getNames() { return names; } public String getRef(){ if (names != null) { String ref = names.get(region.destinationRefTypeRule); if (ref != null) { return ref; } return names.get(region.refTypeRule); } return null; } public String getDestinationName(){ if(names != null) { return names.get(region.destinationTypeRule); } return null; } public int getPoint31XTile(int i) { return pointsX[i]; } public int getPoint31YTile(int i) { return pointsY[i]; } public int getPointsLength() { return pointsX.length; } public int getRestrictionLength() { return restrictions == null ? 0 : restrictions.length; } public int getRestrictionType(int i) { return (int) (restrictions[i] & RESTRICTION_MASK); } public long getRestrictionId(int i) { return restrictions[i] >> RESTRICTION_SHIFT; } public void insert(int pos, int x31, int y31) { int[] opointsX = pointsX; int[] opointsY = pointsY; int[][] opointTypes = pointTypes; pointsX = new int[pointsX.length + 1]; pointsY = new int[pointsY.length + 1]; boolean insTypes = this.pointTypes != null && this.pointTypes.length > pos; if (insTypes) { pointTypes = new int[opointTypes.length + 1][]; } int i = 0; for (; i < pos; i++) { pointsX[i] = opointsX[i]; pointsY[i] = opointsY[i]; if (insTypes) { pointTypes[i] = opointTypes[i]; } } pointsX[i] = x31; pointsY[i] = y31; if (insTypes) { pointTypes[i] = null; } for (i = i + 1; i < pointsX.length; i++) { pointsX[i] = opointsX[i - 1]; pointsY[i] = opointsY[i - 1]; if (insTypes && i < pointTypes.length) { pointTypes[i] = opointTypes[i - 1]; } } } public int[] getPointTypes(int ind) { if (pointTypes == null || ind >= pointTypes.length) { return null; } return pointTypes[ind]; } public int[] getTypes() { return types; } public float getMaximumSpeed(){ int sz = types.length; float maxSpeed = 0; for (int i = 0; i < sz; i++) { RouteTypeRule r = region.quickGetEncodingRule(types[i]); float mx = r.maxSpeed(); if (mx > 0) { maxSpeed = mx; // conditional has priority if(r.conditional()) { break; } } } return maxSpeed ; } public static float parseSpeed(String v, float def) { if(v.equals("none")) { return RouteDataObject.NONE_MAX_SPEED; } else { int i = Algorithms.findFirstNumberEndIndex(v); if (i > 0) { float f = Float.parseFloat(v.substring(0, i)); f /= 3.6; // km/h -> m/s if (v.contains("mph")) { f *= 1.6; } return f; } } return def; } public static float parseLength(String v, float def) { // Supported formats: // 10 m // 10m // 14'10" // 14.5' // 15ft // Even values with any more characters after these formats are rejected, e.g. (14'10"x) or (10 metres). int i = Algorithms.findFirstNumberEndIndex(v); if (i > 0) { float f = Float.parseFloat(v.substring(0, i)); if (i == v.Length()) { // Value with no unit means meters. Done. return f; } else { if (v.charAt(i) == " ") { i++; } if (v.charAt(i) == "m") { // Meters, no conversion needed, done. if (i == v.Length()) { return f; } else { return def; } } if (v.charAt(i) == "'") { // Convert feet to meters. f *= 0.3048; i++; } else if (v.charAt(i) == "f" && i < v.Length() && v.charAt(i+1) != "t") { // 'ft' is a discouraged unit per the wiki, but we know what it means. // Convert feet to meters. f *= 0.3048; // When 'ft' is used, no inches are expected. if (i == v.Length()) { return f; } else { return def; } } if (i < v.Length()) { String w = v.substring(i); int j = Algorithms.findFirstNumberEndIndex(w); if (j > 0 && w.charAt(0) == "\"") { float g = Float.parseFloat(w.substring(0, j)) // Convert inches to meters. f += g * 0.0254; if (i + j == v.Length()) { return f; } else { return def; } } } else { return f; } } } return def; } public static float parseWeightInTon(String v, float def) { int i = Algorithms.findFirstNumberEndIndex(v); if (i > 0) { float f = Float.parseFloat(v.substring(0, i)); if (v.contains("\"") || v.contains("lbs")) { // lbs -> kg -> ton f = (f * 0.4535f) / 1000f; } return f; } return def; } public boolean loop(){ return pointsX[0] == pointsX[pointsX.length - 1] && pointsY[0] == pointsY[pointsY.length - 1] ; } public boolean roundabout(){ int sz = types.length; for(int i=0; i 0) { return ln; } } return -1; } public double directionRoute(int startPoint, boolean plus) { // same goes to C++ // Victor : the problem to put more than 5 meters that BinaryRoutePlanner will treat // 2 consequent Turn Right as UT and here 2 points will have same turn angle // So it should be fix in both places return directionRoute(startPoint, plus, 5); } public double distance(int startPoint, int endPoint) { if(startPoint > endPoint) { int k = endPoint; endPoint = startPoint; startPoint = k; } double d = 0; for(int k = startPoint; k < endPoint && k < getPointsLength() -1; k++) { int x = getPoint31XTile(k); int y = getPoint31YTile(k); int kx = getPoint31XTile(k + 1); int ky = getPoint31YTile(k + 1); d += simplifyDistance(kx, ky, x, y); } return d; } // Gives route direction of EAST degrees from NORTH ]-PI, PI] public double directionRoute(int startPoint, boolean plus, float dist) { int x = getPoint31XTile(startPoint); int y = getPoint31YTile(startPoint); int nx = startPoint; int px = x; int py = y; double total = 0; do { if (plus) { nx++; if (nx >= getPointsLength()) { break; } } else { nx--; if (nx < 0) { break; } } px = getPoint31XTile(nx); py = getPoint31YTile(nx); // translate into meters total += simplifyDistance(x, y, px, py); } while (total < dist); return -Math.atan2( x - px, y - py ); } private double simplifyDistance(int x, int y, int px, int py) { return Math.abs(px - x) * 0.011d + Math.abs(py - y) * 0.01863d; } @Override public String toString() { String name = getName(); String rf = getRef(); return MessageFormat.format("Road id {0} name {1} ref {2}", getId()+"", name == null ? "" : name, rf == null ? "" : rf); } }