Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of ClickableWay for solitary ways (piste / dirtbike / mtb) #21714

Merged
merged 30 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
933bcfc
Add skel classes for proto of ClickableWay feature
RZR-UA Jan 9, 2025
aee346d
Support ClickableWay for v1, polish isClickableWay
RZR-UA Jan 10, 2025
20ea52a
Split V1/V2 methods and implement calcSearchRadius
RZR-UA Jan 10, 2025
1ce52ec
Add skel of GpxFile with activity, name, tags
RZR-UA Jan 10, 2025
51db28b
Merge branch 'master' into rzr-select-way
RZR-UA Jan 10, 2025
2981f13
Implement public Algorithms.sanitizeFileName()
RZR-UA Jan 10, 2025
fb6e58c
Implement save/open GpxFile, refresh TODO list
RZR-UA Jan 10, 2025
e376691
Merge branch 'master' into rzr-select-way
RZR-UA Jan 13, 2025
4a301a7
Fix awkward "X (X)" when name == ref (rendering)
RZR-UA Jan 13, 2025
ef1731f
Implement gpxColors by difficulty/scale tags
RZR-UA Jan 13, 2025
9923629
Add isUniqueClickableWay(), refactor previous code
RZR-UA Jan 13, 2025
56a9dc2
Merge branch 'master' into rzr-select-way
RZR-UA Jan 14, 2025
9eefc8e
Draft ClickableWayReaderTask async task, refactor
RZR-UA Jan 14, 2025
c56c4a1
Merge branch 'master' into rzr-select-way
RZR-UA Jan 15, 2025
9d10303
Avoid useless error log
RZR-UA Jan 15, 2025
285209c
Simplify/refactor ClickableWay structure
RZR-UA Jan 15, 2025
338c133
Migrate to OsmAnd-shared in NetworkRouteSelector
RZR-UA Jan 15, 2025
c93de76
Refactor naming of ClickableWay classes
RZR-UA Jan 15, 2025
8a7cea9
Draft HeightDataLoader for ClickableWay (backend)
RZR-UA Jan 15, 2025
f7231de
Merge branch 'master' into rzr-select-way
RZR-UA Jan 16, 2025
51e0216
Merge branch 'master' into rzr-select-way
RZR-UA Jan 16, 2025
3f66cc9
Apply loadHeightDataAsWaypoints to clickableWay
RZR-UA Jan 16, 2025
15ac702
Fix potential NPE in readHeightData()
RZR-UA Jan 16, 2025
2591ab2
Merge branch 'master' into rzr-select-way
RZR-UA Jan 20, 2025
066c86b
Merge branch 'master' into rzr-select-way
RZR-UA Jan 20, 2025
a70018f
Merge branch 'master' into rzr-select-way
RZR-UA Jan 20, 2025
ca2c92f
Refactor (annotations, naming, consts)
RZR-UA Jan 21, 2025
8fd86b4
Overload V1/V2 methods, simplify code
RZR-UA Jan 21, 2025
f64e3aa
Cancel obf read with InterfaceCancellableCallback
RZR-UA Jan 21, 2025
383174e
Refactor isDerivedGpxSelected block
RZR-UA Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions OsmAnd-java/src/main/java/net/osmand/binary/HeightDataLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
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.QuadRect;
import net.osmand.shared.gpx.primitives.WptPt;
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<RouteSubregion, List<RouteDataObject>> loadedSubregions = new HashMap<>();
private final Map<BinaryMapIndexReader, List<RouteSubregion>> readers = new LinkedHashMap<>();

public HeightDataLoader(BinaryMapIndexReader[] readers) {
for (BinaryMapIndexReader r : readers) {
List<RouteSubregion> subregions = new ArrayList<>();
for (BinaryMapRouteReaderAdapter.RouteRegion rInd : r.getRoutingIndexes()) {
List<RouteSubregion> 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 List<WptPt> loadHeightDataAsWaypoints(long osmId, QuadRect bbox31) {
Map<Long, RouteDataObject> results = new HashMap<>();
ResultMatcher<RouteDataObject> 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);
}

RouteDataObject found = results.get(osmId);
if (found != null && found.getPointsLength() > 0) {
List<WptPt> waypoints = new ArrayList<>();
float[] heightArray = found.calculateHeightArray();
for (int i = 0; i < found.getPointsLength(); i++) {
WptPt point = new WptPt();
point.setLat(MapUtils.get31LatitudeY(found.getPoint31YTile(i)));
point.setLon(MapUtils.get31LongitudeX(found.getPoint31XTile(i)));
if (heightArray != null && heightArray.length > i * 2 + 1) {
point.setEle(heightArray[i * 2 + 1]);
}
waypoints.add(point);
}
return waypoints;
}

return null;
}

private boolean loadRouteDataObjects(QuadRect bbox31,
Map<Long, RouteDataObject> results,
ResultMatcher<RouteDataObject> 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 != null && matcher.isCancelled()) {
return loaded > 0;
}
loaded += loadRouteDataObjects(x, y, results, matcher);
}
}
return loaded > 0;
}

private int loadRouteDataObjects(int x, int y,
Map<Long, RouteDataObject> results,
ResultMatcher<RouteDataObject> matcher) throws IOException {
int loaded = 0;
HashSet<Long> deletedIds = new HashSet<>();
Map<Long, BinaryMapRouteReaderAdapter.RouteRegion> usedIds = new HashMap<>();
BinaryMapIndexReader.SearchRequest<RouteDataObject> 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<BinaryMapIndexReader, List<RouteSubregion>> readerSubregions : readers.entrySet()) {
req.clearSearchResults();
BinaryMapIndexReader reader = readerSubregions.getKey();
synchronized (reader) {
List<RouteSubregion> routeSubregions = readerSubregions.getValue();
List<RouteSubregion> subregions = reader.searchRouteIndexTree(req, routeSubregions);
for (RouteSubregion sub : subregions) {
List<RouteDataObject> objects = loadedSubregions.get(sub);
if (objects == null) {
objects = reader.loadRouteIndexData(sub);
loadedSubregions.put(sub, objects);
}
for (RouteDataObject obj : objects) {
if (matcher != null && matcher.isCancelled()) {
return loaded;
}
if (matcher == null || 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);
Comment on lines +136 to +150
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume there should be an easier way to search RouteDataObjects without additional checks

}
}
}
}
}
return loaded;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.RouteDataObject;
import net.osmand.data.QuadRect;
import net.osmand.gpx.GPXFile;
import net.osmand.gpx.GPXUtilities;
import net.osmand.osm.OsmRouteType;
import net.osmand.router.network.NetworkRouteContext.NetworkRouteSegment;
import net.osmand.shared.gpx.GpxFile;
import net.osmand.shared.gpx.primitives.Track;
import net.osmand.shared.gpx.primitives.TrkSegment;
import net.osmand.shared.gpx.primitives.WptPt;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import net.osmand.util.TransliterationHelper;
Expand Down Expand Up @@ -81,20 +83,20 @@ public boolean isCancelled() {
return callback != null && callback.isCancelled();
}

public Map<RouteKey, GPXFile> getRoutes(RenderedObject renderedObject) throws IOException {
public Map<RouteKey, GpxFile> getRoutes(RenderedObject renderedObject) throws IOException {
int x = renderedObject.getX().get(0);
int y = renderedObject.getY().get(0);
return getRoutes(x, y, true);
}

public Map<RouteKey, GPXFile> getRoutes(RenderedObject renderedObject, boolean loadRoutes) throws IOException {
public Map<RouteKey, GpxFile> getRoutes(RenderedObject renderedObject, boolean loadRoutes) throws IOException {
int x = renderedObject.getX().get(0);
int y = renderedObject.getY().get(0);
return getRoutes(x, y, loadRoutes);
}

public Map<RouteKey, GPXFile> getRoutes(int x, int y, boolean loadRoutes) throws IOException {
Map<RouteKey, GPXFile> res = new LinkedHashMap<>();
public Map<RouteKey, GpxFile> getRoutes(int x, int y, boolean loadRoutes) throws IOException {
Map<RouteKey, GpxFile> res = new LinkedHashMap<>();
for (NetworkRouteSegment segment : rCtx.loadRouteSegment(x, y)) {
if (res.containsKey(segment.routeKey)) {
continue;
Expand All @@ -112,13 +114,13 @@ public Map<RouteKey, GPXFile> getRoutes(int x, int y, boolean loadRoutes) throws
return res;
}

public Map<RouteKey, GPXFile> getRoutes(QuadRect bBox, boolean loadRoutes, RouteKey selected) throws IOException {
public Map<RouteKey, GpxFile> getRoutes(QuadRect bBox, boolean loadRoutes, RouteKey selected) throws IOException {
int y31T = MapUtils.get31TileNumberY(Math.max(bBox.bottom, bBox.top));
int y31B = MapUtils.get31TileNumberY(Math.min(bBox.bottom, bBox.top));
int x31L = MapUtils.get31TileNumberX(bBox.left);
int x31R = MapUtils.get31TileNumberX(bBox.right);
Map<RouteKey, List<NetworkRouteSegment>> routeSegmentTile = rCtx.loadRouteSegmentsBbox(x31L, y31T, x31R, y31B, null);
Map<RouteKey, GPXFile> gpxFileMap = new LinkedHashMap<>();
Map<RouteKey, GpxFile> gpxFileMap = new LinkedHashMap<>();
for (RouteKey routeKey : routeSegmentTile.keySet()) {
if (selected != null && !selected.equals(routeKey)) {
continue;
Expand Down Expand Up @@ -224,7 +226,7 @@ private List<NetworkRouteSegmentChain> getByPoint(Map<Long, List<NetworkRouteSeg
return list;
}

private void connectAlgorithm(NetworkRouteSegment segment, Map<RouteKey, GPXFile> res) throws IOException {
private void connectAlgorithm(NetworkRouteSegment segment, Map<RouteKey, GpxFile> res) throws IOException {
RouteKey rkey = segment.routeKey;
List<NetworkRouteSegment> loaded = new ArrayList<>();
debug("START ", null, segment);
Expand All @@ -234,7 +236,7 @@ private void connectAlgorithm(NetworkRouteSegment segment, Map<RouteKey, GPXFile
}


List<NetworkRouteSegmentChain> getNetworkRouteSegmentChains(RouteKey routeKey, Map<RouteKey, GPXFile> res, List<NetworkRouteSegment> loaded) {
List<NetworkRouteSegmentChain> getNetworkRouteSegmentChains(RouteKey routeKey, Map<RouteKey, GpxFile> res, List<NetworkRouteSegment> loaded) {
System.out.println("About to merge: " + loaded.size());
Map<Long, List<NetworkRouteSegmentChain>> chains = createChainStructure(loaded);
Map<Long, List<NetworkRouteSegmentChain>> endChains = prepareEndChain(chains);
Expand All @@ -247,7 +249,7 @@ List<NetworkRouteSegmentChain> getNetworkRouteSegmentChains(RouteKey routeKey, M
connectSimpleMerge(chains, endChains, 0, CONNECT_POINTS_DISTANCE_STEP);
connectSimpleMerge(chains, endChains, CONNECT_POINTS_DISTANCE_MAX / 2, CONNECT_POINTS_DISTANCE_MAX);
List<NetworkRouteSegmentChain> lst = flattenChainStructure(chains);
GPXFile gpxFile = createGpxFile(lst, routeKey);
GpxFile gpxFile = createGpxFile(lst, routeKey);
res.put(routeKey, gpxFile);
return lst;
}
Expand Down Expand Up @@ -570,7 +572,7 @@ private void addEnclosedTiles(TLongArrayList queue, long tile) {
}
}

private void growAlgorithm(NetworkRouteSegment segment, Map<RouteKey, GPXFile> res) throws IOException {
private void growAlgorithm(NetworkRouteSegment segment, Map<RouteKey, GpxFile> res) throws IOException {
List<NetworkRouteSegment> lst = new ArrayList<>();
TLongHashSet visitedIds = new TLongHashSet();
visitedIds.add(segment.getId());
Expand Down Expand Up @@ -641,37 +643,37 @@ private boolean grow(List<NetworkRouteSegment> lst, TLongHashSet visitedIds, boo
return false;
}

private GPXFile createGpxFile(List<NetworkRouteSegmentChain> chains, RouteKey routeKey) {
GPXFile gpxFile = new GPXFile(null, null, null);
GPXUtilities.Track track = new GPXUtilities.Track();
GPXUtilities.TrkSegment trkSegment;
private GpxFile createGpxFile(List<NetworkRouteSegmentChain> chains, RouteKey routeKey) {
GpxFile gpxFile = new GpxFile(null, null, null);
Track track = new Track();
TrkSegment trkSegment;
List<Integer> sizes = new ArrayList<>();
for (NetworkRouteSegmentChain c : chains) {
List<NetworkRouteSegment> segmentList = new ArrayList<>();
segmentList.add(c.start);
if (c.connected != null) {
segmentList.addAll(c.connected);
}
trkSegment = new GPXUtilities.TrkSegment();
track.segments.add(trkSegment);
trkSegment = new TrkSegment();
track.getSegments().add(trkSegment);
int l = 0;
GPXUtilities.WptPt prev = null;
WptPt prev = null;
for (NetworkRouteSegment segment : segmentList) {
float[] heightArray = null;
if (segment.robj != null) {
heightArray = segment.robj.calculateHeightArray();
}
int inc = segment.start < segment.end ? 1 : -1;
for (int i = segment.start; ; i += inc) {
GPXUtilities.WptPt point = new GPXUtilities.WptPt();
point.lat = MapUtils.get31LatitudeY(segment.getPoint31YTile(i));
point.lon = MapUtils.get31LongitudeX(segment.getPoint31XTile(i));
WptPt point = new WptPt();
point.setLat(MapUtils.get31LatitudeY(segment.getPoint31YTile(i)));
point.setLon(MapUtils.get31LongitudeX(segment.getPoint31XTile(i)));
if (heightArray != null && heightArray.length > i * 2 + 1) {
point.ele = heightArray[i * 2 + 1];
point.setEle(heightArray[i * 2 + 1]);
}
trkSegment.points.add(point);
trkSegment.getPoints().add(point);
if (prev != null) {
l += MapUtils.getDistance(prev.lat, prev.lon, point.lat, point.lon);
l += MapUtils.getDistance(prev.getLat(), prev.getLon(), point.getLat(), point.getLon());
}
prev = point;
if (i == segment.end) {
Expand All @@ -681,8 +683,8 @@ private GPXFile createGpxFile(List<NetworkRouteSegmentChain> chains, RouteKey ro
}
sizes.add(l);
}
System.out.println(String.format("Segments size %d: %s", track.segments.size(), sizes.toString()));
gpxFile.tracks.add(track);
System.out.println(String.format("Segments size %d: %s", track.getSegments().size(), sizes.toString()));
gpxFile.getTracks().add(track);
gpxFile.addRouteKeyTags(routeKey.tagsToGpx());
return gpxFile;
}
Expand Down
21 changes: 21 additions & 0 deletions OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java
Original file line number Diff line number Diff line change
Expand Up @@ -1365,4 +1365,25 @@ public static String splitAndClearRepeats(String ref, String symbol) {
return res;
}

public static String sanitizeFileName(String fileName) {
return fileName
RZR-UA marked this conversation as resolved.
Show resolved Hide resolved
.replace("/", "_")
.replace("\\", "_")
.replace(":", "_")
.replace(";", "_")
.replace("*", "_")
.replace("?", "_")
.replace("`", "_")
.replace("\'", "_")
.replace("\"", "_")
.replace("<", "_")
.replace(">", "_")
.replace("|", "_")
.replace("&", "_")
.replace("\0", "_")
.replace("\n", "_")
.replace("\r", "_")
.replace("\t", " ")
.trim();
}
}
4 changes: 4 additions & 0 deletions OsmAnd/src/net/osmand/plus/mapcontextmenu/MenuController.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import net.osmand.plus.plugins.parking.ParkingPositionMenuController;
import net.osmand.plus.plugins.srtm.SRTMPlugin;
import net.osmand.plus.resources.SearchOsmandRegionTask;
import net.osmand.plus.track.clickable.ClickableWay;
import net.osmand.plus.track.helpers.GpxDisplayItem;
import net.osmand.plus.transport.TransportStopRoute;
import net.osmand.plus.utils.NativeUtilities;
Expand Down Expand Up @@ -219,6 +220,9 @@ public static MenuController getMenuController(@NonNull MapActivity mapActivity,
menuController = new MapillaryMenuController(mapActivity, pointDescription, (MapillaryImage) object);
} else if (object instanceof SelectedGpxPoint) {
menuController = new SelectedGpxMenuController(mapActivity, pointDescription, (SelectedGpxPoint) object);
} else if (object instanceof ClickableWay) {
SelectedGpxPoint point = ((ClickableWay) object).getSelectedGpxPoint();
menuController = new SelectedGpxMenuController(mapActivity, pointDescription, point);
} else if (object instanceof Pair) {
Pair<?, ?> pair = (Pair<?, ?>) object;
if (pair.second instanceof SelectedGpxPoint) {
Expand Down
2 changes: 1 addition & 1 deletion OsmAnd/src/net/osmand/plus/render/TextRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ private void createTextDrawInfo(BinaryMapDataObject o, RenderingRuleSearchReques
public boolean execute(int tagid, String nname) {
String tagNameN2 = o.getMapIndex().decodeType(tagid).tag;
if (tagName2.equals(tagNameN2)) {
if (nname != null && nname.trim().length() > 0) {
if (nname != null && nname.trim().length() > 0 && !nname.equals(text.text)) {
text.text += " (" + nname + ")";
}
return false;
Expand Down
6 changes: 4 additions & 2 deletions OsmAnd/src/net/osmand/plus/track/cards/DescriptionCard.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ public void updateContent() {
String imageUrl = getMetadataImageLink(gpxFile.getMetadata());
String descriptionHtml = gpxFile.getMetadata().getDescription();

AppCompatImageView imageView = view.findViewById(R.id.main_image);
PicassoUtils.setupImageViewByUrl(app, imageView, imageUrl, true);
if (PicassoUtils.isImageUrl(imageUrl)) {
AppCompatImageView imageView = view.findViewById(R.id.main_image);
PicassoUtils.setupImageViewByUrl(app, imageView, imageUrl, true);
}

if (Algorithms.isBlank(descriptionHtml)) {
showAddBtn();
Expand Down
Loading
Loading