/** * Container for link between two Osm nodes (pre-pocessor version) * * @author ab */ package btools.mapcreator; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import btools.expressions.BExpressionContextWay; import btools.util.CheapRuler; import btools.util.CompactLongMap; import btools.util.FrozenLongMap; public class OsmTrafficMap { int minLon; int minLat; int maxLon; int maxLat; private BExpressionContextWay expctxWay; private OsmTrafficMap oldTrafficClasses; private DataOutputStream newTrafficDos; private File oldTrafficFile; private File newTrafficFile; private int totalChanges = 0; private int supressedChanges = 0; private boolean doNotAdd = false; private boolean debug = false; public OsmTrafficMap(BExpressionContextWay expctxWay) { this.expctxWay = expctxWay; debug = Boolean.getBoolean("debugTrafficMap"); } public static class OsmTrafficElement { public long node2; public int traffic; public OsmTrafficElement next; } private CompactLongMap map = new CompactLongMap<>(); public void loadAll(File file, int minLon, int minLat, int maxLon, int maxLat, boolean includeMotorways) throws Exception { load(file, minLon, minLat, maxLon, maxLat, includeMotorways); // check for old traffic data oldTrafficFile = new File(file.getParentFile(), file.getName() + "_old"); if (oldTrafficFile.exists()) { oldTrafficClasses = new OsmTrafficMap(null); oldTrafficClasses.doNotAdd = true; oldTrafficClasses.load(oldTrafficFile, minLon, minLat, maxLon, maxLat, false); } // check for old traffic data newTrafficFile = new File(file.getParentFile(), file.getName() + "_new"); newTrafficDos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(newTrafficFile))); } public void finish() throws Exception { if (newTrafficDos != null) { newTrafficDos.close(); newTrafficDos = null; oldTrafficFile.delete(); newTrafficFile.renameTo(oldTrafficFile); System.out.println("TrafficMap: changes total=" + totalChanges + " supressed=" + supressedChanges); } } public void load(File file, int minLon, int minLat, int maxLon, int maxLat, boolean includeMotorways) throws Exception { this.minLon = minLon; this.minLat = minLat; this.maxLon = maxLon; this.maxLat = maxLat; int trafficElements = 0; DataInputStream is = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); try { for (; ; ) { long n1 = is.readLong(); long n2 = is.readLong(); int traffic = is.readInt(); if (traffic == -1 && !includeMotorways) { continue; } if (isInsideBounds(n1) || isInsideBounds(n2)) { if (addElement(n1, n2, traffic)) { trafficElements++; } } } } catch (EOFException eof) { } finally { is.close(); } map = new FrozenLongMap<>(map); System.out.println("read traffic-elements: " + trafficElements); } public boolean addElement(long n1, long n2, int traffic) { OsmTrafficElement e = getElement(n1, n2); if (e == null) { e = new OsmTrafficElement(); e.node2 = n2; e.traffic = traffic; OsmTrafficElement e0 = map.get(n1); if (e0 != null) { while (e0.next != null) { e0 = e0.next; } e0.next = e; } else { map.fastPut(n1, e); } return true; } if (doNotAdd) { e.traffic = Math.max(e.traffic, traffic); } else { e.traffic = e.traffic == -1 || traffic == -1 ? -1 : e.traffic + traffic; } return false; } private boolean isInsideBounds(long id) { int ilon = (int) (id >> 32); int ilat = (int) (id & 0xffffffff); return ilon >= minLon && ilon < maxLon && ilat >= minLat && ilat < maxLat; } public int getTrafficClass(long n1, long n2) { // used for the old data, where we stpre traffic-classes, not volumes OsmTrafficElement e = getElement(n1, n2); return e == null ? 0 : e.traffic; } public int getTrafficClassForTraffic(int traffic) { if (traffic < 0) return -1; if (traffic < 40000) return 0; if (traffic < 80000) return 2; if (traffic < 160000) return 3; if (traffic < 320000) return 4; if (traffic < 640000) return 5; if (traffic < 1280000) return 6; return 7; } private int getTraffic(long n1, long n2) { OsmTrafficElement e1 = getElement(n1, n2); int traffic1 = e1 == null ? 0 : e1.traffic; OsmTrafficElement e2 = getElement(n2, n1); int traffic2 = e2 == null ? 0 : e2.traffic; return traffic1 == -1 || traffic2 == -1 ? -1 : traffic1 > traffic2 ? traffic1 : traffic2; } public void freeze() { } private OsmTrafficElement getElement(long n1, long n2) { OsmTrafficElement e = map.get(n1); while (e != null) { if (e.node2 == n2) { return e; } e = e.next; } return null; } public OsmTrafficElement getElement(long n) { return map.get(n); } public byte[] addTrafficClass(List linkNodes, byte[] description) throws IOException { double distance = 0.; double sum = 0.; for (int i = 0; i < linkNodes.size() - 1; i++) { OsmNodeP n1 = linkNodes.get(i); OsmNodeP n2 = linkNodes.get(i + 1); int traffic = getTraffic(n1.getIdFromPos(), n2.getIdFromPos()); double dist = CheapRuler.distance(n1.ilon, n1.ilat, n2.ilon, n2.ilat); distance += dist; sum += dist * traffic; } if (distance == 0.) { return description; } int traffic = (int) (sum / distance + 0.5); long id0 = linkNodes.get(0).getIdFromPos(); long id1 = linkNodes.get(linkNodes.size() - 1).getIdFromPos(); int trafficClass = getTrafficClassForTraffic(traffic); // delta suppression: keep old traffic classes within some buffer range if (oldTrafficClasses != null) { int oldTrafficClass = oldTrafficClasses.getTrafficClass(id0, id1); if (oldTrafficClass != trafficClass) { totalChanges++; boolean supressChange = oldTrafficClass == getTrafficClassForTraffic((int) (traffic * 1.3)) || oldTrafficClass == getTrafficClassForTraffic((int) (traffic * 0.77)); if (debug) { System.out.println("traffic class change " + oldTrafficClass + "->" + trafficClass + " supress=" + supressChange); } if (supressChange) { trafficClass = oldTrafficClass; supressedChanges++; } } } if (trafficClass > 0) { newTrafficDos.writeLong(id0); newTrafficDos.writeLong(id1); newTrafficDos.writeInt(trafficClass); expctxWay.decode(description); expctxWay.addLookupValue("estimated_traffic_class", trafficClass + 1); return expctxWay.encode(); } return description; } }