diff --git a/DataExtractionOSM/src/net/osmand/binary/BinaryRouteDataReader.java b/DataExtractionOSM/src/net/osmand/binary/BinaryRouteDataReader.java index 8621e8a2ee..8eb2a1ceb6 100644 --- a/DataExtractionOSM/src/net/osmand/binary/BinaryRouteDataReader.java +++ b/DataExtractionOSM/src/net/osmand/binary/BinaryRouteDataReader.java @@ -12,9 +12,11 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.PriorityQueue; import net.osmand.binary.BinaryMapIndexReader.MapIndex; import net.osmand.binary.BinaryMapIndexReader.SearchRequest; @@ -52,17 +54,17 @@ public class BinaryRouteDataReader { autoValues.put("trunk_link", 110d); autoValues.put("primary", 70d); autoValues.put("primary_link", 70d); - autoValues.put("secondary", 50d); - autoValues.put("secondary_link", 50d); - autoValues.put("tertiary", 50d); - autoValues.put("tertiary_link", 50d); - autoValues.put("residential", 50d); - autoValues.put("service", 40d); - autoValues.put("unclassified", 40d); - autoValues.put("road", 40d); - autoValues.put("track", 30d); - autoValues.put("path", 30d); - autoValues.put("living_street", 30d); + autoValues.put("secondary", 60d); + autoValues.put("secondary_link", 60d); + autoValues.put("tertiary", 30d); + autoValues.put("tertiary_link", 30d); + autoValues.put("residential", 30d); + autoValues.put("service", 5d); + autoValues.put("unclassified", 5d); + autoValues.put("road", 25d); + autoValues.put("track", 20d); + autoValues.put("path", 20d); + autoValues.put("living_street", 20d); } private boolean acceptLine(TagValuePair pair){ @@ -75,14 +77,39 @@ public class BinaryRouteDataReader { private boolean acceptPoint(TagValuePair pair){ if(pair.tag.equals("traffic_calming")){ return true; - } else if(pair.tag.equals("highway") && pair.tag.equals("traffic_signals")){ + } else if(pair.tag.equals("highway") && pair.value.equals("traffic_signals")){ return true; - } else if(pair.tag.equals("highway") && pair.tag.equals("speed_camera")){ + } else if(pair.tag.equals("highway") && pair.value.equals("speed_camera")){ return true; } return false; } + public boolean isOneWay(int highwayAttributes){ + // TODO correctly define roundabout!!! + return MapRenderingTypes.isOneWayWay(highwayAttributes) || + MapRenderingTypes.isRoundabout(highwayAttributes); + } + + /** + * return speed in m/s + */ + public double defineSpeed(BinaryMapDataObject road) { + double speed = MapRenderingTypes.getMaxSpeedIfDefined(road.getHighwayAttributes()) / 3.6d; + if(speed != 0) { + return speed; + } + TagValuePair pair = road.getTagValue(0); + if("highway".equals(pair.tag) && autoValues.containsKey(pair.value)){ + return autoValues.get(pair.value) / 3.6d; + } + return 9; + } + + public double getMinDefaultSpeed() { + return 10; + } + } public static class RoutingContext { @@ -92,12 +119,15 @@ public class BinaryRouteDataReader { TIntSet loadedTiles = new TIntHashSet(); + int timeToLoad = 0; + int timeToCalculate = 0; + public Collection values(){ return idObjects.valueCollection(); } } - private final static int ZOOM_LOAD_TILES = 15; + private final static int ZOOM_LOAD_TILES = 13; public static class RouteSegment { int segmentStart = 0; @@ -105,10 +135,15 @@ public class BinaryRouteDataReader { BinaryMapDataObject road; // needed to store intersection of routes RouteSegment next = null; + // search context (needed for searching route) - int parentRoute = 0; + RouteSegment parentRoute = null; int parentSegmentEnd = 0; + // distance measured in time (seconds) + double distanceFromStart = 0; + double distanceToEnd = 0; + public BinaryMapDataObject getRoad() { return road; } @@ -122,15 +157,22 @@ public class BinaryRouteDataReader { public int endPointIndex; } - private static double dist(int x1, int y1, int x2, int y2) { - return Math.sqrt(((double)x1 - x2) * ((double)x1 - x2) + ((double)y1 - y2) * ((double)y1 - y2)); + private static double squareRootDist(int x1, int y1, int x2, int y2) { + double dy = (y1 - y2) * 0.01863d; + double dx = (x1 - x2) * 0.011d; + return Math.sqrt(dx * dx + dy * dy); + } + + private static int absDist(int x1, int y1, int x2, int y2) { + return Math.abs(x1 - x2) + Math.abs(y1 - y2); } public void loadRoutes(final RoutingContext ctx, int tileX, int tileY) throws IOException { - int tileC = tileX << ZOOM_LOAD_TILES + tileY; + int tileC = (tileX << ZOOM_LOAD_TILES) + tileY; if(ctx.loadedTiles.contains(tileC)){ return; } + long now = System.currentTimeMillis(); SearchRequest request = new SearchRequest(); request.left = tileX << (31 - ZOOM_LOAD_TILES); @@ -178,6 +220,7 @@ public class BinaryRouteDataReader { } } ctx.loadedTiles.add(tileC); + ctx.timeToLoad += (System.currentTimeMillis() - now); } @@ -193,10 +236,10 @@ public class BinaryRouteDataReader { int py = MapUtils.get31TileNumberY(lat); for(BinaryMapDataObject r : ctx.values()){ if(r.getPointsLength() > 1){ - double prevDist = dist(r.getPoint31XTile(0), r.getPoint31YTile(0), px, py); + double prevDist = squareRootDist(r.getPoint31XTile(0), r.getPoint31YTile(0), px, py); for (int j = 1; j < r.getPointsLength(); j++) { - double cDist = dist(r.getPoint31XTile(j), r.getPoint31YTile(j), px, py); - double mDist = dist(r.getPoint31XTile(j), r.getPoint31YTile(j), r.getPoint31XTile(j - 1), r.getPoint31YTile(j - 1)); + double cDist = squareRootDist(r.getPoint31XTile(j), r.getPoint31YTile(j), px, py); + double mDist = squareRootDist(r.getPoint31XTile(j), r.getPoint31YTile(j), r.getPoint31XTile(j - 1), r.getPoint31YTile(j - 1)); if (road == null || prevDist + cDist - mDist < dist) { road = new RouteSegment(); road.road = r; @@ -211,68 +254,155 @@ public class BinaryRouteDataReader { return road; } + + public List searchRoute(RoutingContext ctx, RouteSegment start, RouteSegment end) throws IOException { List result = new ArrayList(); - - List searchList = new ArrayList(); + long now = System.currentTimeMillis(); + + PriorityQueue graphSegments = new PriorityQueue(50, new Comparator(){ + @Override + public int compare(RouteSegment o1, RouteSegment o2) { + return Double.compare(o1.distanceFromStart + o1.distanceToEnd, o2.distanceFromStart + o2.distanceToEnd); + } + + }); TLongHashSet visitedPoints = new TLongHashSet(); - searchList.add(start); - int stIndex = 0; - int endIndex = -1; - while(stIndex < searchList.size() && endIndex == -1){ - RouteSegment segment = searchList.get(stIndex); + graphSegments.add(start); + RouteSegment endRoute = null; + int endX = end.road.getPoint31XTile(end.segmentEnd); + int endY = end.road.getPoint31YTile(end.segmentEnd); + int startX = start.road.getPoint31XTile(start.segmentStart); + int startY = start.road.getPoint31YTile(start.segmentStart); + start.distanceToEnd = squareRootDist(startX, startY, endX, endY) / 10; + +// double maxAreaDist = 100 * squareRootDist(startX, startY, endX, endY); + + while(!graphSegments.isEmpty() && endRoute == null){ + RouteSegment segment = graphSegments.poll(); BinaryMapDataObject road = segment.road; - // try to find all ways - // TODO check for one way (?) and start from segment end - for (int j = 0; j < road.getPointsLength() && endIndex == -1; j++) { + boolean oneway = ctx.router.isOneWay(road.getHighwayAttributes()); + + int middle = segment.segmentEnd; + boolean minus = true; + boolean plus = true; + int d = 0; + int middlex = road.getPoint31XTile(middle); + int middley = road.getPoint31YTile(middle); + double trafficSignalsTime = 0; + while(endRoute == null && ((!oneway && minus) || plus)) { + int j = middle + d; + if(oneway){ + d++; + } else { + if(d <= 0){ + d = -d + 1; + } else { + d = -d; + } + } + + if(j < 0){ + minus = false; + continue; + } + if(j >= road.getPointsLength()){ + plus = false; + continue; + } + if(end.road.id == road.id && end.segmentStart == j){ + endRoute = segment; + break; + } + long l = (((long) road.getPoint31XTile(j)) << 31) + (long) road.getPoint31YTile(j); if(visitedPoints.contains(l)){ continue; } - // TODO efficient (?) loadRoutes(ctx, (road.getPoint31XTile(j) >> (31 - ZOOM_LOAD_TILES)), (road.getPoint31YTile(j) >> (31 - ZOOM_LOAD_TILES))); RouteSegment next = ctx.routes.get(l); - if(next != null){ + + if (next != null) { visitedPoints.add(l); + if (d != 0) { + RouteSegment trafficSignalsTest = next; + while (trafficSignalsTest != null) { + if ((trafficSignalsTest.road.getTypes()[0] & 3) == MapRenderingTypes.POINT_TYPE) { + TagValuePair pair = trafficSignalsTest.road.getTagValue(0); + if (pair != null && pair.tag.equals("highway") && pair.value.equals("traffic_signals")) { + trafficSignalsTime += 25; + } + } + trafficSignalsTest = trafficSignalsTest.next; + } + } } while(next != null){ + // TODO consider restrictions ! if(next.road.id != road.id){ - next.parentRoute = stIndex; + next.parentRoute = segment; next.parentSegmentEnd = j; - searchList.add(next); - // TODO check that there is way to that point !!! - if(end.road.id == next.road.id){ - endIndex = searchList.size() - 1; - break; + int x = road.getPoint31XTile(j); + int y = road.getPoint31YTile(j); + + // Using A* routing algorithm + // g(x) - calculate distance to that point and calculate time + + double speed = ctx.router.defineSpeed(road); + if(speed == 0){ + speed = ctx.router.getMinDefaultSpeed(); } + + next.distanceFromStart = segment.distanceFromStart + squareRootDist(x, y, middlex, middley) / speed; + // for each turn add 45 seconds + // TODO consider right turn 20 seconds and left turn 45 seconds + if (j < road.getPointsLength() - 1) { + next.distanceFromStart += 30; + } + // traffic signals time + next.distanceFromStart += trafficSignalsTime; + // h(x) - calculate approximate distance to the end point and divide to 37 km/h = 10 m/s + // max speed + next.distanceToEnd = squareRootDist(x, y, endX, endY) / 30; + + graphSegments.add(next); } next = next.next; } + } - stIndex++; } - stIndex = endIndex; - start.parentRoute = -1; - int parentSegmentEnd = end.segmentEnd; - while(stIndex != -1){ - RouteSegment segment = searchList.get(stIndex); + // reverse start and end point for end if needed + int parentSegmentEnd = endRoute != null && endRoute.segmentStart <= end.segmentStart ? + end.segmentEnd : end.segmentStart; + RouteSegment segment = endRoute; + while(segment != null){ RouteSegmentResult res = new RouteSegmentResult(); res.object = segment.road; res.endPointIndex = parentSegmentEnd; res.startPointIndex = segment.segmentStart; - res.startPoint = convertPoint(res.object, res.startPointIndex); - res.endPoint = convertPoint(res.object, res.endPointIndex); + parentSegmentEnd = segment.parentSegmentEnd; + System.out.println(segment.road.name + " time to go " + + (segment.distanceFromStart / 60) + " estimate time " + (segment.distanceToEnd / 60)); + segment = segment.parentRoute; if(res.startPointIndex != res.endPointIndex) { // skip duplicates result.add(0, res); + // reverse start and end point for start if needed + if(segment == null && res.startPointIndex > res.endPointIndex){ + res.startPointIndex = start.segmentEnd; + } } - parentSegmentEnd = segment.parentSegmentEnd; - stIndex = segment.parentRoute; + res.startPoint = convertPoint(res.object, res.startPointIndex); + res.endPoint = convertPoint(res.object, res.endPointIndex); + } + ctx.timeToCalculate = (int) (System.currentTimeMillis() - now); + System.out.println("Time to calculate : " + ctx.timeToCalculate +", time to load : " + ctx.timeToLoad + ", loaded tiles : " + ctx.loadedTiles.size()); return result; } @@ -336,6 +466,11 @@ public class BinaryRouteDataReader { Collection res = ctx.values(); System.out.println(res.size() + " objects for " + (System.currentTimeMillis() - ms) + " ms"); + LatLon ls = new LatLon(0, 5); + LatLon le = new LatLon(1, 5); + System.out.println("X equator " + MapUtils.getDistance(ls, le) / (MapUtils.get31TileNumberX(ls.getLongitude()) - MapUtils.get31TileNumberX(le.getLongitude()))); + System.out.println("Y equator " + MapUtils.getDistance(ls, le) / (MapUtils.get31TileNumberY(ls.getLatitude()) - MapUtils.get31TileNumberY(le.getLatitude()))); + // for(BinaryMapDataObject r : res){ // TagValuePair pair = r.mapIndex.decodeType(r.getTypes()[0]); // if(r.name != null){ diff --git a/DataExtractionOSM/src/net/osmand/osm/MapRenderingTypes.java b/DataExtractionOSM/src/net/osmand/osm/MapRenderingTypes.java index c9bcd65def..b5c4dc1b8e 100644 --- a/DataExtractionOSM/src/net/osmand/osm/MapRenderingTypes.java +++ b/DataExtractionOSM/src/net/osmand/osm/MapRenderingTypes.java @@ -354,6 +354,10 @@ public class MapRenderingTypes { return (highwayAttributes & 1) > 0; } + public static boolean isRoundabout(int highwayAttributes){ + return ((highwayAttributes >> 2) & 1) > 0; + } + // 0 - normal, 1 - under, 2 - bridge,over public static int getWayLayer(int type){ return (3 & (type >> 12)); @@ -370,11 +374,38 @@ public class MapRenderingTypes { return 0; } + // return 0 if not defined + public static int getMaxSpeedIfDefined(int highwayAttributes){ + switch((highwayAttributes >> 4) & 7) { + case 0: + return 20; + case 1: + return 40; + case 2: + // for old format it should return 0; + // TODO it should be uncommented because now it is fixed + // return 60; + return 0; + case 3: + return 80; + case 4: + return 100; + case 5: + return 120; + case 6: + return 140; + case 7: + return 0; + + } + return 0; + } + // HIGHWAY special attributes : // o/oneway 1 bit // f/free toll 1 bit // r/roundabout 2 bit (+ 1 bit direction) - // s/max speed 3 bit [0 - 30km/h, 1 - 50km/h, 2 - 70km/h, 3 - 90km/h, 4 - 110km/h, 5 - 130 km/h, 6 >] + // s/max speed 3 bit [0 - 30km/h, 1 - 50km/h, 2 - 70km/h, 3 - 90km/h, 4 - 110km/h, 5 - 130 km/h, 6 >, 7 - 0/not specified] // a/vehicle access 4 bit (height, weight?) - one bit bicycle // p/parking 1 bit // c/cycle oneway 1 bit @@ -427,7 +458,7 @@ public class MapRenderingTypes { } catch (NumberFormatException es) { } if(kmh <= 0){ - attr |= 2; + attr |= 7; } else if(kmh <= 30){ attr |= 0; } else if(kmh <= 50){ @@ -444,7 +475,7 @@ public class MapRenderingTypes { attr |= 6; } } else { - attr |= 2; + attr |= 7; } @@ -948,6 +979,8 @@ public class MapRenderingTypes { // } // System.out.println(getAmenityNameToType()); // long ts = System.currentTimeMillis(); + System.out.println(MapUtils.getTileNumberX(13, 23.95)+ " " + MapUtils.getTileNumberY(13, 52.136)); + MapRenderingTypes def = MapRenderingTypes.getDefault(); def.initAmenityMap(); System.out.println(def.amenityTypeNameToTagVal); diff --git a/DataExtractionOSM/src/net/osmand/swing/DataExtractionSettings.java b/DataExtractionOSM/src/net/osmand/swing/DataExtractionSettings.java index 153665332e..a47474fea0 100644 --- a/DataExtractionOSM/src/net/osmand/swing/DataExtractionSettings.java +++ b/DataExtractionOSM/src/net/osmand/swing/DataExtractionSettings.java @@ -41,6 +41,19 @@ public class DataExtractionSettings { preferences.put("working_dir", path.getAbsolutePath()); } + + public File getDefaultRoutingFile(){ + String routingFile = preferences.get("routing_file", null); + if(routingFile == null || !new File(routingFile).exists()){ + return null; + } + return new File(routingFile); + } + + public void setDefaultRoutingPath(String path){ + preferences.put("routing_file", path); + } + public LatLon getDefaultLocation(){ double lat = preferences.getDouble("default_lat", 53.9); double lon = preferences.getDouble("default_lon", 27.56); diff --git a/DataExtractionOSM/src/net/osmand/swing/MapRouterLayer.java b/DataExtractionOSM/src/net/osmand/swing/MapRouterLayer.java index 8ee19beb95..a8760a7d0e 100644 --- a/DataExtractionOSM/src/net/osmand/swing/MapRouterLayer.java +++ b/DataExtractionOSM/src/net/osmand/swing/MapRouterLayer.java @@ -13,11 +13,11 @@ import java.io.StringReader; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; +import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -89,7 +89,7 @@ public class MapRouterLayer implements MapPanelLayer { } }; menu.add(end); - Action route = new AbstractAction("Calculate route") { + Action route = new AbstractAction("Calculate YOURS route") { private static final long serialVersionUID = 507156107455281238L; public void actionPerformed(ActionEvent e) { @@ -104,7 +104,7 @@ public class MapRouterLayer implements MapPanelLayer { } }; menu.add(route); - Action altroute = new AbstractAction("Calculate alternative route") { + Action altroute = new AbstractAction("Calculate CloudMade route") { private static final long serialVersionUID = 507156107455281238L; public void actionPerformed(ActionEvent e) { @@ -119,18 +119,20 @@ public class MapRouterLayer implements MapPanelLayer { } }; menu.add(altroute); - Action selfRoute = new AbstractAction("Calculate self route") { + Action selfRoute = new AbstractAction("Calculate OsmAnd route") { private static final long serialVersionUID = 507156107455281238L; public void actionPerformed(ActionEvent e) { List ways = selfRoute(startRoute, endRoute); - DataTileManager points = new DataTileManager(); - points.setZoom(11); - for(Way w : ways){ - LatLon n = w.getLatLon(); - points.registerObject(n.getLatitude(), n.getLongitude(), w); + if (ways != null) { + DataTileManager points = new DataTileManager(); + points.setZoom(11); + for (Way w : ways) { + LatLon n = w.getLatLon(); + points.registerObject(n.getLatitude(), n.getLongitude(), w); + } + map.setPoints(points); } - map.setPoints(points); } }; @@ -303,11 +305,17 @@ public class MapRouterLayer implements MapPanelLayer { public List selfRoute(LatLon start, LatLon end) { List res = new ArrayList(); long time = System.currentTimeMillis(); + File file = DataExtractionSettings.getSettings().getDefaultRoutingFile(); + if(file == null){ + JOptionPane.showMessageDialog(OsmExtractionUI.MAIN_APP.getFrame(), "Please specify obf file in settings", "Obf file not found", + JOptionPane.ERROR_MESSAGE); + return null; + } System.out.println("Self made route from " + start + " to " + end); if (start != null && end != null) { try { - RandomAccessFile raf = new RandomAccessFile(new File("d:\\android\\data\\Belarus.obf"), "r"); //$NON-NLS-1$ //$NON-NLS-2$ + RandomAccessFile raf = new RandomAccessFile(file, "r"); //$NON-NLS-1$ //$NON-NLS-2$ BinaryMapIndexReader reader = new BinaryMapIndexReader(raf); BinaryRouteDataReader router = new BinaryRouteDataReader(reader); RoutingContext ctx = new RoutingContext(); diff --git a/DataExtractionOSM/src/net/osmand/swing/OsmExtractionPreferencesDialog.java b/DataExtractionOSM/src/net/osmand/swing/OsmExtractionPreferencesDialog.java index c2f160aa93..c0b23e4437 100644 --- a/DataExtractionOSM/src/net/osmand/swing/OsmExtractionPreferencesDialog.java +++ b/DataExtractionOSM/src/net/osmand/swing/OsmExtractionPreferencesDialog.java @@ -6,8 +6,10 @@ import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; +import java.awt.Label; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.File; import javax.swing.BorderFactory; import javax.swing.Box; @@ -31,8 +33,11 @@ public class OsmExtractionPreferencesDialog extends JDialog { private JTextField streetDefaultSuffixes; private JTextField mapZooms; private JTextField renderingTypesFile; + private JTextField pathToObfRoutingFile; private JCheckBox useInternet; + + // private JCheckBox supressWarning; // private JCheckBox loadWholeOsmInfo; @@ -78,7 +83,8 @@ public class OsmExtractionPreferencesDialog extends JDialog { private void createGeneralSection(JPanel root) { JPanel panel = new JPanel(); // panel.setLayout(new GridLayout(3, 1, 5, 5)); - panel.setLayout(new GridLayout(1, 1, 5, 5)); + GridBagLayout l = new GridBagLayout(); + panel.setLayout(l); panel.setBorder(BorderFactory.createTitledBorder(Messages.getString("OsmExtractionPreferencesDialog.GENERAL"))); //$NON-NLS-1$ root.add(panel); @@ -86,6 +92,34 @@ public class OsmExtractionPreferencesDialog extends JDialog { useInternet.setText(Messages.getString("OsmExtractionPreferencesDialog.INTERNET.TO.DOWNLOAD.FILES")); //$NON-NLS-1$ useInternet.setSelected(DataExtractionSettings.getSettings().useInternetToLoadImages()); panel.add(useInternet); + GridBagConstraints constr = new GridBagConstraints(); + constr.ipadx = 5; + constr.gridx = 0; + constr.gridy = 0; + constr.gridwidth = 2; + constr.anchor = GridBagConstraints.WEST; + l.setConstraints(useInternet, constr); + + JLabel label = new JLabel("Path to obf file (test routing purpose) : "); + panel.add(label); + constr = new GridBagConstraints(); + constr.ipadx = 5; + constr.gridx = 0; + constr.gridy = 1; + constr.anchor = GridBagConstraints.WEST; + l.setConstraints(label, constr); + + pathToObfRoutingFile = new JTextField(); + File file = DataExtractionSettings.getSettings().getDefaultRoutingFile(); + pathToObfRoutingFile.setText(file == null ? "" : file.getAbsolutePath()); + panel.add(pathToObfRoutingFile); + constr = new GridBagConstraints(); + constr.weightx = 1; + constr.fill = GridBagConstraints.HORIZONTAL; + constr.ipadx = 5; + constr.gridx = 1; + constr.gridy = 1; + l.setConstraints(pathToObfRoutingFile, constr); // supressWarning = new JCheckBox(); // supressWarning.setText(Messages.getString("OsmExtractionPreferencesDialog.DUPLICATED.ID")); //$NON-NLS-1$ @@ -228,6 +262,11 @@ public class OsmExtractionPreferencesDialog extends JDialog { if(!settings.getMapRenderingTypesFile().equals(renderingTypesFile.getText())){ settings.setMapRenderingTypesFile(renderingTypesFile.getText()); } + File f = settings.getDefaultRoutingFile(); + String routingFile = f == null ? "" : f.getAbsolutePath(); + if(!routingFile.equals(pathToObfRoutingFile.getText())){ + settings.setDefaultRoutingPath(pathToObfRoutingFile.getText()); + } // if(settings.isSupressWarningsForDuplicatedId() != supressWarning.isSelected()){ // settings.setSupressWarningsForDuplicatedId (supressWarning.isSelected()); // }