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 String getName(String lang){ if(names != null ) { if(Algorithms.isEmpty(lang)) { return names.get(region.nameTypeRule); } int[] kt = names.keys(); for(int i = 0 ; i < kt.length; i++) { int k = kt[i]; if(region.routeEncodingRules.size() > k) { if(("name:"+lang).equals(region.routeEncodingRules.get(k).getTag())) { return names.get(k); } } } 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(String lang){ if(names != null) { if(Algorithms.isEmpty(lang)) { return names.get(region.destinationTypeRule); } int[] kt = names.keys(); for(int i = 0 ; i < kt.length; i++) { int k = kt[i]; if(region.routeEncodingRules.size() > k) { if(("destination:"+lang).equals(region.routeEncodingRules.get(k).getTag())) { return names.get(k); } } } 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) { float f = 0; // 14'10" 14 - inches, 10 feet int i = Algorithms.findFirstNumberEndIndex(v); if (i > 0) { f += Float.parseFloat(v.substring(0, i)); String pref = v.substring(i, v.length()).trim(); float add = 0; for(int ik = 0; ik < pref.length(); ik++) { if(Character.isDigit(pref.charAt(ik)) || pref.charAt(ik) == '.' || pref.charAt(ik) == '-') { int first = Algorithms.findFirstNumberEndIndex(pref.substring(ik)); if(first != -1) { add = parseLength(pref.substring(ik), 0); pref = pref.substring(0, ik); } break; } } if(pref.contains("km")) { f *= 1000; } if(pref.contains("\"")) { f *= 0.0254; } else if (pref.contains("\'") || pref.contains("ft")) { // foot to meters f *= 0.3048; } return f + add; } 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; } private static void assertTrueLength(String vl, float exp){ float dest = parseLength(vl, 0); if(exp != dest) { System.err.println("FAIL " + vl + " " + dest); } else { System.out.println("OK " + vl); } } public static void main(String[] args) { assertTrueLength("10 km", 10000); assertTrueLength("0.01 km", 10); assertTrueLength("0.01 km 10 m", 20); assertTrueLength("10 m", 10); assertTrueLength("10m", 10); assertTrueLength("3.4 m", 3.4f); assertTrueLength("14'10\"", 4.5212f); assertTrueLength("14.5'", 4.4196f); assertTrueLength("15ft", 4.572f); } @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); } }