diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/HeightDataLoader.java b/OsmAnd-java/src/main/java/net/osmand/binary/HeightDataLoader.java new file mode 100644 index 00000000000..d6763c53ee8 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/binary/HeightDataLoader.java @@ -0,0 +1,136 @@ +package net.osmand.binary; + +import static net.osmand.router.RouteResultPreparation.SHIFT_ID; + +import net.osmand.PlatformUtil; +import net.osmand.ResultMatcher; +import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion; +import net.osmand.data.LatLon; +import net.osmand.data.QuadRect; +import net.osmand.router.network.NetworkRouteContext; +import net.osmand.router.network.NetworkRouteSelector; +import net.osmand.util.MapUtils; + +import org.apache.commons.logging.Log; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class HeightDataLoader { + public static final int ZOOM_TO_LOAD_TILES = 15; + public static final int ZOOM_TO_LOAD_TILES_SHIFT_L = ZOOM_TO_LOAD_TILES + 1; + public static final int ZOOM_TO_LOAD_TILES_SHIFT_R = 31 - ZOOM_TO_LOAD_TILES; + + private final static Log log = PlatformUtil.getLog(HeightDataLoader.class); + private final Map> loadedSubregions = new HashMap<>(); + private final Map> readers = new LinkedHashMap<>(); + + public HeightDataLoader(BinaryMapIndexReader[] readers) { + for (BinaryMapIndexReader r : readers) { + List subregions = new ArrayList<>(); + for (BinaryMapRouteReaderAdapter.RouteRegion rInd : r.getRoutingIndexes()) { + List subregs = rInd.getSubregions(); + // create a copy to avoid leaks to the original structure + for (RouteSubregion rs : subregs) { + subregions.add(new RouteSubregion(rs)); + } + } + this.readers.put(r, subregions); + } + } + + public void loadHeightData(long osmId, QuadRect bbox31) { + Map results = new HashMap<>(); + ResultMatcher matcher = new ResultMatcher<>() { + @Override + public boolean publish(RouteDataObject routeDataObject) { + return routeDataObject != null && routeDataObject.getId() >> SHIFT_ID == osmId; + } + + @Override + public boolean isCancelled() { + return results.containsKey(osmId); // fast up search + } + }; + + try { + loadRouteDataObjects(bbox31, results, matcher); + } catch (IOException e) { + log.error(e); + } + + // TODO calculateHeightArray() and return results + } + + private boolean loadRouteDataObjects(QuadRect bbox31, + Map results, + ResultMatcher matcher) throws IOException { + int loaded = 0; + int left = (int) bbox31.left >> ZOOM_TO_LOAD_TILES_SHIFT_R; + int top = (int) bbox31.top >> ZOOM_TO_LOAD_TILES_SHIFT_R; + int right = (int) bbox31.right >> ZOOM_TO_LOAD_TILES_SHIFT_R; + int bottom = (int) bbox31.bottom >> ZOOM_TO_LOAD_TILES_SHIFT_R; + for (int x = left; x <= right; x++) { + for (int y = top; y <= bottom; y++) { + if (matcher.isCancelled()) { + return loaded > 0; + } + loaded += loadRouteDataObjects(x, y, results, matcher); + } + } + return loaded > 0; + } + + private int loadRouteDataObjects(int x, int y, + Map results, + ResultMatcher matcher) throws IOException { + int loaded = 0; + HashSet deletedIds = new HashSet<>(); + Map usedIds = new HashMap<>(); + BinaryMapIndexReader.SearchRequest req = BinaryMapIndexReader.buildSearchRouteRequest( + x << ZOOM_TO_LOAD_TILES_SHIFT_L, (x + 1) << ZOOM_TO_LOAD_TILES_SHIFT_L, + y << ZOOM_TO_LOAD_TILES_SHIFT_L, (y + 1) << ZOOM_TO_LOAD_TILES_SHIFT_L, null); + for (Map.Entry> readerSubregions : readers.entrySet()) { + req.clearSearchResults(); + BinaryMapIndexReader reader = readerSubregions.getKey(); + synchronized (reader) { + List routeSubregions = readerSubregions.getValue(); + List subregions = reader.searchRouteIndexTree(req, routeSubregions); + for (RouteSubregion sub : subregions) { + List objects = loadedSubregions.get(sub); + if (objects == null) { + objects = reader.loadRouteIndexData(sub); + loadedSubregions.put(sub, objects); + } + for (RouteDataObject obj : objects) { + if (matcher.isCancelled()) { + return loaded; + } + if (matcher.publish(obj)) { + if (deletedIds.contains(obj.id)) { + // live-updates, osmand_change=delete + continue; + } + if (obj.isRoadDeleted()) { + deletedIds.add(obj.id); + continue; + } + if (usedIds.containsKey(obj.id) && usedIds.get(obj.id) != obj.region) { + // live-update, changed tags + continue; + } + loaded += (results.put(obj.getId() >> SHIFT_ID, obj) == null) ? 1 : 0; + usedIds.put(obj.id, obj.region); + } + } + } + } + } + return loaded; + } +} diff --git a/OsmAnd/src/net/osmand/plus/track/clickable/ClickableWayHelper.java b/OsmAnd/src/net/osmand/plus/track/clickable/ClickableWayHelper.java index 27e7162517d..cda888d3c72 100644 --- a/OsmAnd/src/net/osmand/plus/track/clickable/ClickableWayHelper.java +++ b/OsmAnd/src/net/osmand/plus/track/clickable/ClickableWayHelper.java @@ -10,6 +10,7 @@ import androidx.annotation.Nullable; import net.osmand.NativeLibrary.RenderedObject; +import net.osmand.binary.HeightDataLoader; import net.osmand.binary.ObfConstants; import net.osmand.core.jni.ObfMapObject; import net.osmand.core.jni.QVectorPointI; @@ -64,8 +65,6 @@ public class ClickableWayHelper { private final OsmandMapTileView view; private final ClickableWayMenuActivator activator; -// private static final Log log = PlatformUtil.getLog(ClickableWay.class); - public ClickableWayHelper(@NonNull OsmandApplication app, @NonNull OsmandMapTileView view) { this.app = app; this.view = view; @@ -195,23 +194,9 @@ private boolean isClickableWayTags(@NonNull Map tags) { } private boolean readHeightData(ClickableWay clickableWay) { - // TODO read height data, implement simple cache -// BinaryMapIndexReader[] readers = app.getResourceManager().getReverseGeocodingMapFiles(); -// NetworkRouteSelector.NetworkRouteSelectorFilter selectorFilter = new NetworkRouteSelector.NetworkRouteSelectorFilter(); -// NetworkRouteSelector routeSelector = new NetworkRouteSelector(readers, selectorFilter, null); -// try { -// QuadRect bbox31 = clickableWay.getBbox(); -// QuadRect bboxLatLon = new QuadRect( -// MapUtils.get31LongitudeX((int)bbox31.left), -// MapUtils.get31LatitudeY((int)bbox31.top), -// MapUtils.get31LongitudeX((int)bbox31.right), -// MapUtils.get31LatitudeY((int)bbox31.bottom) -// ); -// Map routes = routeSelector.getRoutes(bboxLatLon, true, null); -// System.err.printf("XXX routes\n"); -// } catch (IOException e) { -// log.error(e); -// } + HeightDataLoader loader = new HeightDataLoader(app.getResourceManager().getReverseGeocodingMapFiles()); + loader.loadHeightData(clickableWay.getOsmId(), clickableWay.getBbox()); + // TODO read height data, apply to the GpxFile points return true; }