[android] Optimize bookmark search with native implementation

Replace inefficient Java bookmark search methods with a C++ implementation that leverages the core place page infrastructure. The previous approach had significant computational overhead due to redundant iterations over all bookmark categories.

Signed-off-by: coderang-gk <coderang.gk@gmail.com>
This commit is contained in:
Gaurang Khatavkar 2025-03-07 15:59:08 +05:30
parent 2504282e67
commit 28545429ac
5 changed files with 66 additions and 196 deletions

View file

@ -971,6 +971,48 @@ Java_app_organicmaps_Framework_nativeRemovePlacePageActivationListener(JNIEnv *e
g_placePageActivationListener = nullptr;
}
JNIEXPORT jlong JNICALL
Java_app_organicmaps_bookmarks_data_BookmarkManager_nativeFindBookmarkAtPoint(JNIEnv * env, jobject thiz, jdouble lat, jdouble lon)
{
LOG(LINFO, ("Native: Searching for bookmark at", lat, lon));
auto * framework = g_framework->NativeFramework();
auto const mercatorPoint = mercator::FromLatLon(lat, lon);
place_page::BuildInfo buildInfo;
buildInfo.m_mercator = mercatorPoint;
framework->BuildAndSetPlacePageInfo(buildInfo);
if (framework->HasPlacePageInfo())
{
auto const & placePageInfo = framework->GetCurrentPlacePageInfo();
if (placePageInfo.IsBookmark())
{
auto bookmarkId = placePageInfo.GetBookmarkId();
return static_cast<jlong>(bookmarkId);
}
}
double const searchRadiusM = 2.0;
auto const & bmManager = framework->GetBookmarkManager();
auto const rect = mercator::RectByCenterXYAndSizeInMeters(mercatorPoint, searchRadiusM);
m2::AnyRectD searchRect(rect);
auto const * userMark = bmManager.FindNearestUserMark(searchRect);
if (userMark && userMark->GetMarkType() == UserMark::Type::BOOKMARK)
{
auto const bookmarkId = userMark->GetId();
return static_cast<jlong>(bookmarkId);
}
return -1;
}
JNIEXPORT jstring JNICALL
Java_app_organicmaps_Framework_nativeGetGe0Url(JNIEnv * env, jclass, jdouble lat, jdouble lon, jdouble zoomLevel, jstring name)
{

View file

@ -331,7 +331,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
if (intent != null && "app.organicmaps.action.SHOW_BOOKMARK".equals(intent.getAction()))
{
boolean fromWidget = intent.getBooleanExtra("FROM_WIDGET", false);
String bookmarkName = intent.getStringExtra("BOOKMARK_NAME");
double lat = intent.getDoubleExtra("BOOKMARK_LAT", Double.NaN);
@ -340,23 +339,15 @@ public class MwmActivity extends BaseMwmFragmentActivity
if (!Double.isNaN(lat) && !Double.isNaN(lon))
{
try
BookmarkInfo nearestBookmark = BookmarkManager.INSTANCE.findBookmarkByCoordinates(
lat, lon, bookmarkName, categoryName
);
if (nearestBookmark != null)
{
BookmarkInfo nearestBookmark = BookmarkManager.INSTANCE.findBookmarkByCoordinates(
lat, lon, bookmarkName, categoryName
);
if (nearestBookmark != null)
{
BookmarkManager.INSTANCE.showBookmarkOnMap(nearestBookmark.getBookmarkId());
return;
}
} catch (Exception e)
{
BookmarkManager.INSTANCE.showBookmarkOnMap(nearestBookmark.getBookmarkId());
return;
}
Framework.nativeZoomToPoint(lat, lon, 16, true);
}
}

View file

@ -13,7 +13,6 @@ import app.organicmaps.R;
import app.organicmaps.bookmarks.data.BookmarkCategory;
import app.organicmaps.bookmarks.data.BookmarkInfo;
import app.organicmaps.bookmarks.data.BookmarkManager;
import app.organicmaps.bookmarks.data.Icon;
public class FavoriteBookmarkWidget extends AppWidgetProvider
{
@ -55,7 +54,6 @@ public class FavoriteBookmarkWidget extends AppWidgetProvider
bookmarkInfo = BookmarkManager.INSTANCE.findBookmarkByCoordinates(lat, lon, bookmarkName, categoryName);
}
// Always set the bookmark name, using stored name as fallback
String displayName = bookmarkInfo != null ? bookmarkInfo.getName() :
(bookmarkName != null && !bookmarkName.isEmpty() ? bookmarkName :
context.getString(R.string.select_bookmark));
@ -64,7 +62,6 @@ public class FavoriteBookmarkWidget extends AppWidgetProvider
if (bookmarkInfo != null)
{
int color = iconColor != 0 ? iconColor : bookmarkInfo.getIcon().getColor();
int iconResId = BookmarkManager.INSTANCE.getBookmarkIcon(bookmarkInfo.getBookmarkId());
if (iconResId == 0)
@ -119,19 +116,19 @@ public class FavoriteBookmarkWidget extends AppWidgetProvider
{
SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
prefs.putFloat(PREF_PREFIX_KEY + appWidgetId + SUFFIX_LAT, (float) bookmarkInfo.getLat());
prefs.putFloat(PREF_PREFIX_KEY + appWidgetId + SUFFIX_LON, (float) bookmarkInfo.getLon());
prefs.putString(PREF_PREFIX_KEY + appWidgetId + SUFFIX_NAME, bookmarkInfo.getName());
prefs.putString(PREF_PREFIX_KEY + appWidgetId + "_category_name", category.getName());
prefs.putString(PREF_PREFIX_KEY + appWidgetId + SUFFIX_CATEGORY_NAME, category.getName());
prefs.putLong(PREF_PREFIX_KEY + appWidgetId + "_bookmark_id", bookmarkInfo.getBookmarkId());
prefs.putLong(PREF_PREFIX_KEY + appWidgetId + "_category_id", category.getId());
prefs.putString(PREF_PREFIX_KEY + appWidgetId + "_name_hash",
String.valueOf(bookmarkInfo.getName().hashCode()));
prefs.putString(PREF_PREFIX_KEY + appWidgetId + "_lat_lon_hash",
String.valueOf((bookmarkInfo.getLat() + "," + bookmarkInfo.getLon()).hashCode()));
boolean success = prefs.commit();
prefs.apply();
}
@Override

View file

@ -148,189 +148,26 @@ public enum BookmarkManager
}
@Nullable
public BookmarkInfo findBookmarkByCoordinates(double latitude, double longitude, @Nullable String name, @Nullable String categoryName)
public BookmarkInfo findBookmarkByCoordinates(double lat, double lon, @Nullable String name, @Nullable String categoryName)
{
final double MAX_DISTANCE_TOLERANCE = 50.0;
long bookmarkId = nativeFindBookmarkAtPoint(lat, lon);
List<BookmarkCategory> categories = getCategories();
if (bookmarkId == -1)
return null;
BookmarkInfo closestMatch = null;
double closestDistance = Double.MAX_VALUE;
BookmarkInfo info = getBookmarkInfo(bookmarkId);
for (BookmarkCategory category : categories)
if (info != null &&
(name == null || name.isEmpty() || name.equals(info.getName())) &&
(categoryName == null || categoryName.isEmpty() ||
categoryName.equals(getCategoryById(info.getCategoryId()).getName())))
{
if (categoryName != null && !categoryName.isEmpty() &&
!category.getName().equals(categoryName))
{
continue;
}
for (int i = 0; i < category.getBookmarksCount(); i++)
{
long bookmarkId = getBookmarkIdByPosition(category.getId(), i);
BookmarkInfo bookmarkInfo = getBookmarkInfo(bookmarkId);
if (bookmarkInfo == null)
continue;
boolean nameMatches = name == null || name.isEmpty() ||
bookmarkInfo.getName().equals(name);
double distance = calculateDistance(latitude, longitude,
bookmarkInfo.getLat(),
bookmarkInfo.getLon());
if (nameMatches && distance <= MAX_DISTANCE_TOLERANCE)
{
if (distance < closestDistance)
{
closestMatch = bookmarkInfo;
closestDistance = distance;
}
}
}
}
return closestMatch;
}
@Nullable
private BookmarkInfo findExactBookmarkByCoordinates(double lat, double lon, @Nullable String name, @Nullable String categoryName)
{
final double COORD_TOLERANCE = 0.0001;
List<BookmarkCategory> categories = getCategories();
if (categoryName != null && !categoryName.isEmpty())
{
for (BookmarkCategory category : categories)
{
if (categoryName.equals(category.getName()))
{
for (int i = 0; i < category.getBookmarksCount(); i++)
{
long bookmarkId = getBookmarkIdByPosition(category.getId(), i);
BookmarkInfo bookmark = getBookmarkInfo(bookmarkId);
if (bookmark != null &&
(name == null || name.isEmpty() || name.equals(bookmark.getName())) &&
Math.abs(bookmark.getLat() - lat) < COORD_TOLERANCE &&
Math.abs(bookmark.getLon() - lon) < COORD_TOLERANCE)
{
return bookmark;
}
}
}
}
}
for (BookmarkCategory category : categories)
{
for (int i = 0; i < category.getBookmarksCount(); i++)
{
long bookmarkId = getBookmarkIdByPosition(category.getId(), i);
BookmarkInfo bookmark = getBookmarkInfo(bookmarkId);
if (bookmark != null &&
(name == null || name.isEmpty() || name.equals(bookmark.getName())) &&
Math.abs(bookmark.getLat() - lat) < COORD_TOLERANCE &&
Math.abs(bookmark.getLon() - lon) < COORD_TOLERANCE)
{
return bookmark;
}
}
return info;
}
return null;
}
@Nullable
private BookmarkInfo findNearestBookmark(double lat, double lon, double maxDistanceMeters,
@Nullable String name, @Nullable String categoryName)
{
List<BookmarkCategory> categories = getCategories();
BookmarkInfo closestBookmark = null;
double closestDistance = Double.MAX_VALUE;
if (categoryName != null && !categoryName.isEmpty())
{
for (BookmarkCategory category : categories)
{
if (categoryName.equals(category.getName()))
{
BookmarkInfo categoryResult = findNearestInCategory(category, lat, lon,
maxDistanceMeters, name, closestDistance);
if (categoryResult != null)
{
return categoryResult;
}
break;
}
}
}
for (BookmarkCategory category : categories)
{
if (categoryName != null && !categoryName.isEmpty() && categoryName.equals(category.getName()))
{
continue;
}
BookmarkInfo categoryResult = findNearestInCategory(category, lat, lon,
maxDistanceMeters, name, closestDistance);
if (categoryResult != null)
{
closestBookmark = categoryResult;
closestDistance = calculateDistance(lat, lon, closestBookmark.getLat(), closestBookmark.getLon());
}
}
return closestBookmark;
}
@Nullable
private BookmarkInfo findNearestInCategory(BookmarkCategory category, double lat, double lon,
double maxDistanceMeters, @Nullable String name, double currentClosestDistance)
{
BookmarkInfo closestBookmark = null;
double closestDistance = currentClosestDistance;
for (int i = 0; i < category.getBookmarksCount(); i++)
{
long bookmarkId = getBookmarkIdByPosition(category.getId(), i);
BookmarkInfo bookmark = getBookmarkInfo(bookmarkId);
if (bookmark != null && (name == null || name.isEmpty() || name.equals(bookmark.getName())))
{
double distance = calculateDistance(lat, lon, bookmark.getLat(), bookmark.getLon());
if (distance < closestDistance && distance <= maxDistanceMeters)
{
closestBookmark = bookmark;
closestDistance = distance;
}
}
}
return closestBookmark;
}
private double calculateDistance(double lat1, double lon1, double lat2, double lon2)
{
final double earthRadius = 6371000;
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return earthRadius * c;
}
// Called from JNI.
@Keep
@SuppressWarnings("unused")
@ -927,6 +764,8 @@ public enum BookmarkManager
return nativeGetElevationActivePointDistance(trackId);
}
private native long nativeFindBookmarkAtPoint(double lat, double lon);
@Nullable
private native Bookmark nativeUpdateBookmarkPlacePage(long bmkId);

View file

@ -9,4 +9,5 @@
android:widgetCategory="home_screen"
android:description="@string/widget_description"
android:configure="app.organicmaps.bookmarks.FavoriteBookmarkWidgetConfigActivity" />