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

TravelGpx improvements (fix OOM, fix duplicates in v1, fix selection, read multiple maps for OSM relations) #21901

Merged
merged 12 commits into from
Feb 14, 2025
Merged
3 changes: 3 additions & 0 deletions OsmAnd-java/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ tasks.register('collectMiscResources', Copy) {
from("../../resources/poi") {
include "poi_types.xml"
}
from("../../resources/poi") {
include "activities.json"
}
}

tasks.register('collectRenderingStylesResources', Sync) {
Expand Down
9 changes: 0 additions & 9 deletions OsmAnd-java/src/main/java/net/osmand/NativeLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -704,15 +704,6 @@ public String getRouteID() {
return null;
}

public String getGpxFileName() {
for (String name : getOriginalNames()) {
if (name.endsWith(GPX_FILE_EXT) || name.endsWith(GPX_GZ_FILE_EXT)) {
return name;
}
}
return null;
}

@Override
public String toString() {
String s = getClass().getSimpleName() + " " + name;
Expand Down
4 changes: 1 addition & 3 deletions OsmAnd/src/net/osmand/plus/configmap/ConfigureMapMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,7 @@ private void createRouteAttributeItems(@NonNull List<RenderingRuleProperty> cust
}
customRules.remove(property);
}
ResourceManager manager = app.getResourceManager();
if (PluginsHelper.isDevelopment() &&
(!Algorithms.isEmpty(manager.getTravelMapRepositories()) || !Algorithms.isEmpty(manager.getTravelRepositories()))) {
if (PluginsHelper.isDevelopment()) {
adapter.addItem(createTravelRoutesItem(activity, nightMode));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,11 @@ void openTrack(MapActivity mapActivity) {
travelHelper.openTrackMenu(article, mapActivity, name, amenity.getLocation(), false);
}
} else if (ROUTE_TRACK_POINT.equals(amenity.getSubType())) {
TravelGpx travelGpx = travelHelper.searchGpx(amenity.getLocation(), amenity.getRouteId(), amenity.getRef());
TravelGpx travelGpx = travelHelper.searchTravelGpx(amenity.getLocation(), amenity.getRouteId());
if (travelGpx != null) {
travelHelper.openTrackMenu(travelGpx, mapActivity, travelGpx.getTitle(), amenity.getLocation(), false);
} else {
LOG.error("openTrack() searchGpx() travelGpx is null");
LOG.error("openTrack() searchTravelGpx() travelGpx is null");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,11 @@ private void showResultWithLocation(SearchResult searchResult) {
if (pair.second instanceof Amenity && ((Amenity) pair.second).isRouteTrack()) {
Amenity amenity = (Amenity) pair.second;
TravelHelper travelHelper = app.getTravelHelper();
TravelGpx travelGpx = travelHelper.searchGpx(amenity.getLocation(), amenity.getRouteId(), amenity.getRef());
TravelGpx travelGpx = travelHelper.searchTravelGpx(amenity.getLocation(), amenity.getRouteId());
if (travelGpx != null) {
travelHelper.openTrackMenu(travelGpx, getMapActivity(), amenity.getGpxFileName(null), amenity.getLocation(), true);
} else {
LOG.error("showResultWithLocation() searchGpx() travelGpx is null");
LOG.error("showResultWithLocation() searchTravelGpx() travelGpx is null");
}
} else {
showOnMap(getMapActivity(), dialogFragment,
Expand Down
96 changes: 46 additions & 50 deletions OsmAnd/src/net/osmand/plus/views/layers/MapSelectionHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
import org.apache.commons.logging.Log;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -225,15 +224,18 @@ private void selectObjectsFromNative(@NonNull MapSelectionResult result, @NonNul
if (renderedObjects != null) {
double cosRotateTileSize = Math.cos(Math.toRadians(rc.rotate)) * TILE_SIZE;
double sinRotateTileSize = Math.sin(Math.toRadians(rc.rotate)) * TILE_SIZE;
boolean isDerivedGpxSelected = false;
Set<Long> uniqueRenderedObjectIds = new HashSet<>();
boolean osmRoutesAlreadyAdded = false;
for (RenderedObject renderedObject : renderedObjects) {
String routeID = renderedObject.getRouteID();
String fileName = renderedObject.getGpxFileName();
String travelGpxFilter = routeID != null ? routeID : fileName;

Long objectId = renderedObject.getId();
if (objectId != null && uniqueRenderedObjectIds.contains(objectId)) {
log.warn("selectObjectsFromNative(v1) got duplicate: " + renderedObject);
continue;
}
Map<String, String> tags = renderedObject.getTags();
String travelGpxFilter = renderedObject.getRouteID();

boolean isTravelGpx = !Algorithms.isEmpty(travelGpxFilter);
boolean isTravelGpx = app.getTravelHelper().isTravelGpxTags(tags);
boolean isOsmRoute = !Algorithms.isEmpty(OsmRouteType.getRouteKeys(tags));
boolean isClickableWay = clickableWayHelper.isClickableWay(renderedObject);

Expand Down Expand Up @@ -267,17 +269,12 @@ private void selectObjectsFromNative(@NonNull MapSelectionResult result, @NonNul
}
LatLon searchLatLon = result.objectLatLon != null ? result.objectLatLon : result.pointLatLon;

if (!isDerivedGpxSelected) {
if (isOsmRoute) {
isDerivedGpxSelected |= addOsmRoute(result, tileBox, point, createRouteFilter());
}
if (isTravelGpx) {
isDerivedGpxSelected |= addTravelGpx(result, travelGpxFilter, renderedObject.getTagValue("ref"));
}
if (isClickableWay) {
isDerivedGpxSelected |= addClickableWay(result,
clickableWayHelper.loadClickableWay(result.pointLatLon, renderedObject));
}
if (isOsmRoute && !osmRoutesAlreadyAdded) {
osmRoutesAlreadyAdded = addOsmRoutesAround(result, tileBox, point, createRouteFilter());
} else if (isTravelGpx) {
addTravelGpx(result, travelGpxFilter);
} else if (isClickableWay) {
addClickableWay(result, clickableWayHelper.loadClickableWay(result.pointLatLon, renderedObject));
}

if (!isClickableWay && !isOsmRoute && !isTravelGpx) {
Expand All @@ -286,6 +283,9 @@ private void selectObjectsFromNative(@NonNull MapSelectionResult result, @NonNul
result.selectedObjects.put(renderedObject, null);
}
}
if (objectId != null) {
uniqueRenderedObjectIds.add(objectId);
}
}
}
}
Expand All @@ -297,7 +297,7 @@ private void selectObjectsFromOpenGl(@NonNull MapSelectionResult result, @NonNul
int delta = 20;
PointI tl = new PointI((int) point.x - delta, (int) point.y - delta);
PointI br = new PointI((int) point.x + delta, (int) point.y + delta);
boolean isDerivedGpxSelected = false;
boolean osmRoutesAlreadyAdded = false;
MapSymbolInformationList symbols = rendererView.getSymbolsIn(new AreaI(tl, br), false);
for (int i = 0; i < symbols.size(); i++) {
MapSymbolInformation symbolInfo = symbols.get(i);
Expand Down Expand Up @@ -359,32 +359,28 @@ private void selectObjectsFromOpenGl(@NonNull MapSelectionResult result, @NonNul
boolean isOsmRoute = !Algorithms.isEmpty(OsmRouteType.getRouteKeys(tags));
boolean isClickableWay = clickableWayHelper.isClickableWay(obfMapObject, tags);

if (!isDerivedGpxSelected) {
if (isOsmRoute) {
isDerivedGpxSelected |= addOsmRoute(result, tileBox, point, createRouteFilter());
}
if (isTravelGpx) {
isDerivedGpxSelected |= addTravelGpx(result, tags.get(ROUTE_ID), null);
}
if (isClickableWay) {
isDerivedGpxSelected |= addClickableWay(result,
clickableWayHelper.loadClickableWay(result.pointLatLon, obfMapObject, tags));
}
}

IOnPathMapSymbol onPathMapSymbol = getOnPathMapSymbol(symbolInfo);
if (onPathMapSymbol == null) {
LatLon latLon = result.objectLatLon;
if (tags.containsKey(TAG_POI_LAT_LON)) {
LatLon l = parsePoiLatLon(tags.get(TAG_POI_LAT_LON));
latLon = l == null ? latLon : l;
tags.remove(TAG_POI_LAT_LON);
}
amenity = getAmenity(latLon, obfMapObject, tags);
if (amenity != null) {
amenity.setMapIconName(getMapIconName(symbolInfo));
} else if (!isOsmRoute && !isTravelGpx && !isClickableWay) {
addRenderedObject(result, symbolInfo, obfMapObject, tags);
if (isOsmRoute && !osmRoutesAlreadyAdded) {
osmRoutesAlreadyAdded = addOsmRoutesAround(result, tileBox, point, createRouteFilter());
} else if (isTravelGpx) {
addTravelGpx(result, tags.get(ROUTE_ID));
} else if (isClickableWay) {
addClickableWay(result,
clickableWayHelper.loadClickableWay(result.pointLatLon, obfMapObject, tags));
} else {
IOnPathMapSymbol onPathMapSymbol = getOnPathMapSymbol(symbolInfo);
if (onPathMapSymbol == null) {
LatLon latLon = result.objectLatLon;
if (tags.containsKey(TAG_POI_LAT_LON)) {
LatLon l = parsePoiLatLon(tags.get(TAG_POI_LAT_LON));
latLon = l == null ? latLon : l;
tags.remove(TAG_POI_LAT_LON);
}
amenity = getAmenity(latLon, obfMapObject, tags);
if (amenity != null) {
amenity.setMapIconName(getMapIconName(symbolInfo));
} else if (!isOsmRoute && !isTravelGpx && !isClickableWay) {
addRenderedObject(result, symbolInfo, obfMapObject, tags);
}
}
}
}
Expand Down Expand Up @@ -483,8 +479,8 @@ private Amenity getAmenity(LatLon latLon, ObfMapObject obfMapObject, Map<String,
return amenity;
}

private boolean addTravelGpx(@NonNull MapSelectionResult result, @Nullable String routeId, @Nullable String ref) {
TravelGpx travelGpx = app.getTravelHelper().searchGpx(result.pointLatLon, routeId, ref);
private boolean addTravelGpx(@NonNull MapSelectionResult result, @Nullable String routeId) {
TravelGpx travelGpx = app.getTravelHelper().searchTravelGpx(result.pointLatLon, routeId);
if (travelGpx != null && isUniqueTravelGpx(result.selectedObjects, travelGpx)) {
WptPt selectedPoint = new WptPt();
selectedPoint.setLat(result.pointLatLon.getLatitude());
Expand All @@ -493,7 +489,7 @@ private boolean addTravelGpx(@NonNull MapSelectionResult result, @Nullable Strin
result.selectedObjects.put(new Pair<>(travelGpx, selectedGpxPoint), mapLayers.getTravelSelectionLayer());
return true;
} else if (travelGpx == null) {
log.error("addTravelGpx() searchGpx() travelGpx is null");
log.error("addTravelGpx() searchTravelGpx() travelGpx is null");
}
return false;
}
Expand Down Expand Up @@ -545,8 +541,8 @@ private boolean isUniqueTravelGpx(@NonNull Map<Object, IContextMenuProvider> sel
return isUniqueGpxFileName(selectedObjects, travelGpx.getGpxFileName() + GPX_FILE_EXT);
}

private boolean addOsmRoute(@NonNull MapSelectionResult result, @NonNull RotatedTileBox tileBox, @NonNull PointF point,
@NonNull NetworkRouteSelectorFilter selectorFilter) {
private boolean addOsmRoutesAround(@NonNull MapSelectionResult result, @NonNull RotatedTileBox tileBox,
@NonNull PointF point, @NonNull NetworkRouteSelectorFilter selectorFilter) {
if (Algorithms.isEmpty(selectorFilter.typeFilter)) {
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -609,11 +609,11 @@ public boolean showMenuAction(@Nullable Object object) {
travelHelper.openTrackMenu(article, mapActivity, name, amenity.getLocation(), false);
return true;
} else if (amenity.isRouteTrack()) {
TravelGpx travelGpx = travelHelper.searchGpx(amenity.getLocation(), amenity.getRouteId(), amenity.getRef());
TravelGpx travelGpx = travelHelper.searchTravelGpx(amenity.getLocation(), amenity.getRouteId());
if (travelGpx != null) {
travelHelper.openTrackMenu(travelGpx, mapActivity, amenity.getGpxFileName(null), amenity.getLocation(), false);
} else {
log.error("showMenuAction() searchGpx() travelGpx is null");
log.error("showMenuAction() searchTravelGpx() travelGpx is null");
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ public boolean isTravelGpxTags(@NonNull Map<String, String> tags) {

@Nullable
@Override
public TravelGpx searchGpx(@NonNull LatLon location, @Nullable String fileName, @Nullable String ref) {
public TravelGpx searchTravelGpx(@NonNull LatLon location, @Nullable String routeId) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ interface GpxReadCallback {
boolean isTravelGpxTags(@NonNull Map<String, String> tags);

@Nullable
TravelGpx searchGpx(@NonNull LatLon location, @Nullable String fileName, @Nullable String ref);
TravelGpx searchTravelGpx(@NonNull LatLon location, @Nullable String routeId);

void openTrackMenu(@NonNull TravelArticle article, @NonNull MapActivity mapActivity,
@NonNull String gpxFileName, @NonNull LatLon location, boolean adjustMapPosition);
Expand Down
43 changes: 25 additions & 18 deletions OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,12 @@ public boolean isTravelGpxTags(@NonNull Map<String, String> tags) {
}

@Nullable
public synchronized TravelGpx searchGpx(@NonNull LatLon location, @Nullable String filter, @Nullable String ref) {
final String lcFilter = filter != null ? filter.toLowerCase() : null;
final String lcSearchRef = ref != null ? ref.toLowerCase() : null;
public synchronized TravelGpx searchTravelGpx(@NonNull LatLon location, @Nullable String routeId) {
final String lcSearchRouteId = routeId != null ? routeId.toLowerCase() : null;
if (Algorithms.isEmpty(lcSearchRouteId)) {
LOG.error(String.format("searchTravelGpx(%s, null) failed due to empty routeId", location));
return null;
}
List<Pair<File, Amenity>> foundAmenities = new ArrayList<>();
int searchRadius = ARTICLE_SEARCH_RADIUS;
TravelGpx travelGpx = null;
Expand All @@ -254,22 +257,17 @@ public synchronized TravelGpx searchGpx(@NonNull LatLon location, @Nullable Stri
}
for (Pair<File, Amenity> foundGpx : foundAmenities) {
Amenity amenity = foundGpx.second;
final String aRef = amenity.getRef();
final String aName = amenity.getName();
final String aRouteId = amenity.getRouteId();
final String lcRef = aRef != null ? aRef.toLowerCase() : null;
final String lcName = aName != null ? aName.toLowerCase() : null;
final String lcRouteId = aRouteId != null ? aRouteId.toLowerCase() : null;
if ((Algorithms.objectEquals(lcRouteId, lcFilter) || Algorithms.objectEquals(lcName, lcFilter))
&& (lcSearchRef == null || Algorithms.objectEquals(lcRef, lcSearchRef))) {
if (lcSearchRouteId.equals(lcRouteId)) {
travelGpx = getTravelGpx(foundGpx.first, amenity);
break;
}
}
searchRadius *= 2;
} while (travelGpx == null && searchRadius < MAX_SEARCH_RADIUS);
if (travelGpx == null) {
LOG.error(String.format("searchGpx(%s, %s, %s) failed", location, filter, ref));
LOG.error(String.format("searchTravelGpx(%s, %s) failed", location, routeId));
}
return travelGpx;
}
Expand Down Expand Up @@ -1121,10 +1119,11 @@ private void fetchSegmentsAndPoints(List<BinaryMapIndexReader> readers, TravelAr
List<BinaryMapDataObject> segmentList, List<Amenity> pointList,
Map<String, String> gpxFileExtensions, List<String> pgNames,
List<String> pgIcons, List<String> pgColors, List<String> pgBackgrounds) {
boolean allowReadFromMultipleMaps = article.hasOsmRouteId() && article.routeRadius > 0;
for (BinaryMapIndexReader reader : readers) {
try {
if (article.file != null && !article.file.equals(reader.getFile())) {
continue;
if (!allowReadFromMultipleMaps && !reader.getFile().equals(article.file)) {
continue; // fast up read Wikivoyage and User's GPX files in OBF
}
if (article instanceof TravelGpx) {
BinaryMapIndexReader.SearchRequest<BinaryMapDataObject> sr = BinaryMapIndexReader.buildSearchRequest(
Expand All @@ -1148,8 +1147,8 @@ private void fetchSegmentsAndPoints(List<BinaryMapIndexReader> readers, TravelAr
} else {
reader.searchPoi(pointRequest);
}
if (!Algorithms.isEmpty(segmentList)) {
break;
if (!allowReadFromMultipleMaps && !Algorithms.isEmpty(segmentList)) {
break; // fast up read User's GPX files
}
} catch (Exception e) {
LOG.error(e.getMessage());
Expand Down Expand Up @@ -1243,10 +1242,18 @@ private ResultMatcher<BinaryMapDataObject> matchSegmentsByRefTitleRouteId(
@Override
public boolean publish(BinaryMapDataObject object) {
if (object.getPointsLength() > 1) {
if (object.getTagValue(REF).equals(article.ref)
&& (object.getTagValue(ROUTE_ID).equals(article.routeId)
|| object.getName().equals(article.getTitle()))) {
segmentList.add(object);
String routeId = article.getRouteId();
boolean equalRouteId = !Algorithms.isEmpty(routeId) && routeId.equals(object.getTagValue(ROUTE_ID));

if (article instanceof TravelGpx && equalRouteId) {
segmentList.add(object); // GPX-in-OBF requires mandatory route_id
} else {
String name = article.getTitle(), ref = article.ref;
boolean equalName = !Algorithms.isEmpty(name) && name.equals(object.getName());
boolean equalRef = !Algorithms.isEmpty(ref) && ref.equals(object.getTagValue(REF));
if ((equalRouteId && (equalRef || equalName) || (equalRef && equalName))) {
segmentList.add(object); // Wikivoyage is allowed to match mixed tags
}
}
}
return false;
Expand Down
Loading