Nearby: No longer keeps loading until timeout when map is zoomed out (#6070)

* Nearby: Search for actual map center

* Add query syntax and methods

* Nearby: Added binary search for loading pins

* Add NearbyQueryParams and refactor

* Add unit tests and complete implementation

* Nearby: Increase max radius from 100km to 300km

* Nearby: Centermost pins now appear on top

* getNearbyItemCount: Added javadoc

---------

Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
This commit is contained in:
Tanmay Gupta 2024-12-24 11:51:45 +05:30 committed by GitHub
parent c963cd9ea4
commit 369e79be5e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 303 additions and 37 deletions

View file

@ -196,8 +196,9 @@ public class NearbyController extends MapController {
return null;
}
List<Place> places = nearbyPlaces.getFromWikidataQuery(screenTopRight, screenBottomLeft,
Locale.getDefault().getLanguage(), shouldQueryForMonuments, customQuery);
List<Place> places = nearbyPlaces.getFromWikidataQuery(currentLatLng, screenTopRight,
screenBottomLeft, Locale.getDefault().getLanguage(), shouldQueryForMonuments,
customQuery);
if (null != places && places.size() > 0) {
LatLng[] boundaryCoordinates = {

View file

@ -1,6 +1,8 @@
package fr.free.nrw.commons.nearby;
import android.location.Location;
import androidx.annotation.Nullable;
import fr.free.nrw.commons.nearby.model.NearbyQueryParams;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
@ -101,6 +103,7 @@ public class NearbyPlaces {
* Retrieves a list of places from a Wikidata query based on screen coordinates and optional
* parameters.
*
* @param centerPoint The center of the map, used for radius queries if required.
* @param screenTopRight The top right corner of the screen (latitude, longitude).
* @param screenBottomLeft The bottom left corner of the screen (latitude, longitude).
* @param lang The language for the query.
@ -111,13 +114,70 @@ public class NearbyPlaces {
* @throws Exception If an error occurs during the retrieval process.
*/
public List<Place> getFromWikidataQuery(
final fr.free.nrw.commons.location.LatLng centerPoint,
final fr.free.nrw.commons.location.LatLng screenTopRight,
final fr.free.nrw.commons.location.LatLng screenBottomLeft, final String lang,
final boolean shouldQueryForMonuments,
@Nullable final String customQuery) throws Exception {
return okHttpJsonApiClient
.getNearbyPlaces(screenTopRight, screenBottomLeft, lang, shouldQueryForMonuments,
customQuery);
if (customQuery != null) {
return okHttpJsonApiClient
.getNearbyPlaces(
new NearbyQueryParams.Rectangular(screenTopRight, screenBottomLeft), lang,
shouldQueryForMonuments,
customQuery);
}
final int lowerLimit = 1000, upperLimit = 1500;
final float[] results = new float[1];
Location.distanceBetween(centerPoint.getLatitude(), screenTopRight.getLongitude(),
centerPoint.getLatitude(), screenBottomLeft.getLongitude(), results);
final float longGap = results[0] / 1000f;
Location.distanceBetween(screenTopRight.getLatitude(), centerPoint.getLongitude(),
screenBottomLeft.getLatitude(), centerPoint.getLongitude(), results);
final float latGap = results[0] / 1000f;
if (Math.max(longGap, latGap) < 100f) {
final int itemCount = okHttpJsonApiClient.getNearbyItemCount(
new NearbyQueryParams.Rectangular(screenTopRight, screenBottomLeft));
if (itemCount < upperLimit) {
return okHttpJsonApiClient.getNearbyPlaces(
new NearbyQueryParams.Rectangular(screenTopRight, screenBottomLeft), lang,
shouldQueryForMonuments, null);
}
}
// minRadius, targetRadius and maxRadius are radii in decameters
// unlike other radii here, which are in kilometers, to avoid looping over
// floating point values
int minRadius = 0, maxRadius = Math.round(Math.min(300f, Math.min(longGap, latGap))) * 100;
int targetRadius = maxRadius / 2;
while (minRadius < maxRadius) {
targetRadius = minRadius + (maxRadius - minRadius + 1) / 2;
final int itemCount = okHttpJsonApiClient.getNearbyItemCount(
new NearbyQueryParams.Radial(centerPoint, targetRadius / 100f));
if (itemCount >= lowerLimit && itemCount < upperLimit) {
break;
}
if (targetRadius > maxRadius / 2 && itemCount < lowerLimit / 5) { // fast forward
minRadius = targetRadius;
targetRadius = minRadius + (maxRadius - minRadius + 1) / 2;
minRadius = targetRadius;
if (itemCount < lowerLimit / 10 && minRadius < maxRadius) { // fast forward again
targetRadius = minRadius + (maxRadius - minRadius + 1) / 2;
minRadius = targetRadius;
}
continue;
}
if (itemCount < upperLimit) {
minRadius = targetRadius;
} else {
maxRadius = targetRadius - 1;
}
}
return okHttpJsonApiClient.getNearbyPlaces(
new NearbyQueryParams.Radial(centerPoint, targetRadius / 100f), lang, shouldQueryForMonuments,
null);
}
/**

View file

@ -1101,19 +1101,19 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
eastCornerLong, 0);
if (currentLatLng.equals(
getLastMapFocus())) { // Means we are checking around current location
populatePlacesForCurrentLocation(getLastMapFocus(), screenTopRightLatLng,
populatePlacesForCurrentLocation(getMapFocus(), screenTopRightLatLng,
screenBottomLeftLatLng, currentLatLng, null);
} else {
populatePlacesForAnotherLocation(getLastMapFocus(), screenTopRightLatLng,
populatePlacesForAnotherLocation(getMapFocus(), screenTopRightLatLng,
screenBottomLeftLatLng, currentLatLng, null);
}
} else {
if (currentLatLng.equals(
getLastMapFocus())) { // Means we are checking around current location
populatePlacesForCurrentLocation(getLastMapFocus(), screenTopRightLatLng,
populatePlacesForCurrentLocation(getMapFocus(), screenTopRightLatLng,
screenBottomLeftLatLng, currentLatLng, null);
} else {
populatePlacesForAnotherLocation(getLastMapFocus(), screenTopRightLatLng,
populatePlacesForAnotherLocation(getMapFocus(), screenTopRightLatLng,
screenBottomLeftLatLng, currentLatLng, null);
}
}
@ -1887,9 +1887,12 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Override
public void replaceMarkerOverlays(final List<MarkerPlaceGroup> markerPlaceGroups) {
ArrayList<Marker> newMarkers = new ArrayList<>(markerPlaceGroups.size());
for (MarkerPlaceGroup markerPlaceGroup : markerPlaceGroups) {
// iterate in reverse so that the nearest pins get rendered on top
for (int i = markerPlaceGroups.size() - 1; i >= 0; i--) {
newMarkers.add(
convertToMarker(markerPlaceGroup.getPlace(), markerPlaceGroup.getIsBookmarked()));
convertToMarker(markerPlaceGroups.get(i).getPlace(),
markerPlaceGroups.get(i).getIsBookmarked())
);
}
clearAllMarkers();
binding.map.getOverlays().addAll(newMarkers);

View file

@ -0,0 +1,10 @@
package fr.free.nrw.commons.nearby.model
import fr.free.nrw.commons.location.LatLng
sealed class NearbyQueryParams {
class Rectangular(val screenTopRight: LatLng, val screenBottomLeft: LatLng) :
NearbyQueryParams()
class Radial(val center: LatLng, val radiusInKm: Float) : NearbyQueryParams()
}