Skip to content

Commit 724e4db

Browse files
authored
Made Nearby show all pins that could be presented on the screen, rather than a circle (commons-app#5553)
* Changed nearby from circle to rectangle * Fixed bug * Removed MAPBOX Token * Fixed minor issues * Fixed minor issues * Changed query files * Changed monuments query file * Fixed Unit Tests * Fixed failing tests * Fixed errors due to merging
1 parent f404ac9 commit 724e4db

15 files changed

+577
-220
lines changed

app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java

Lines changed: 143 additions & 110 deletions
Large diffs are not rendered by default.

app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.java

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -269,27 +269,25 @@ public Single<FeedbackResponse> getAchievements(String userName) {
269269
/**
270270
* Make API Call to get Nearby Places
271271
*
272-
* @param cur Search lat long
273-
* @param language Language
274-
* @param radius Search Radius
275-
* @param shouldQueryForMonuments : Should we query for monuments
272+
* @param cur Search lat long
273+
* @param language Language
274+
* @param radius Search Radius
276275
* @return
277276
* @throws Exception
278277
*/
279278
@Nullable
280279
public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius,
281-
final boolean shouldQueryForMonuments, final String customQuery)
280+
final String customQuery)
282281
throws Exception {
283282

284283
Timber.d("Fetching nearby items at radius %s", radius);
285284
Timber.d("CUSTOM_SPARQL%s", String.valueOf(customQuery != null));
286285
final String wikidataQuery;
287286
if (customQuery != null) {
288287
wikidataQuery = customQuery;
289-
} else if (!shouldQueryForMonuments) {
290-
wikidataQuery = FileUtils.readFromResource("/queries/nearby_query.rq");
291288
} else {
292-
wikidataQuery = FileUtils.readFromResource("/queries/nearby_query_monuments.rq");
289+
wikidataQuery = FileUtils.readFromResource(
290+
"/queries/radius_query_for_upload_wizard.rq");
293291
}
294292
final String query = wikidataQuery
295293
.replace("${RAD}", String.format(Locale.ROOT, "%.2f", radius))
@@ -307,6 +305,74 @@ public List<Place> getNearbyPlaces(final LatLng cur, final String language, fina
307305
.url(urlBuilder.build())
308306
.build();
309307

308+
final Response response = okHttpClient.newCall(request).execute();
309+
if (response.body() != null && response.isSuccessful()) {
310+
final String json = response.body().string();
311+
final NearbyResponse nearbyResponse = gson.fromJson(json, NearbyResponse.class);
312+
final List<NearbyResultItem> bindings = nearbyResponse.getResults().getBindings();
313+
final List<Place> places = new ArrayList<>();
314+
for (final NearbyResultItem item : bindings) {
315+
final Place placeFromNearbyItem = Place.from(item);
316+
placeFromNearbyItem.setMonument(false);
317+
places.add(placeFromNearbyItem);
318+
}
319+
return places;
320+
}
321+
throw new Exception(response.message());
322+
}
323+
324+
/**
325+
* Retrieves nearby places based on screen coordinates and optional query parameters.
326+
*
327+
* @param screenTopRight The top right corner of the screen (latitude, longitude).
328+
* @param screenBottomLeft The bottom left corner of the screen (latitude, longitude).
329+
* @param language The language for the query.
330+
* @param shouldQueryForMonuments Flag indicating whether to include monuments in the query.
331+
* @param customQuery Optional custom SPARQL query to use instead of default
332+
* queries.
333+
* @return A list of nearby places.
334+
* @throws Exception If an error occurs during the retrieval process.
335+
*/
336+
@Nullable
337+
public List<Place> getNearbyPlaces(
338+
final fr.free.nrw.commons.___location.LatLng screenTopRight,
339+
final fr.free.nrw.commons.___location.LatLng screenBottomLeft, final String language,
340+
final boolean shouldQueryForMonuments, final String customQuery)
341+
throws Exception {
342+
343+
Timber.d("CUSTOM_SPARQL%s", String.valueOf(customQuery != null));
344+
345+
final String wikidataQuery;
346+
if (customQuery != null) {
347+
wikidataQuery = customQuery;
348+
} else if (!shouldQueryForMonuments) {
349+
wikidataQuery = FileUtils.readFromResource("/queries/rectangle_query_for_nearby.rq");
350+
} else {
351+
wikidataQuery = FileUtils.readFromResource(
352+
"/queries/rectangle_query_for_nearby_monuments.rq");
353+
}
354+
355+
final double westCornerLat = screenTopRight.getLatitude();
356+
final double westCornerLong = screenTopRight.getLongitude();
357+
final double eastCornerLat = screenBottomLeft.getLatitude();
358+
final double eastCornerLong = screenBottomLeft.getLongitude();
359+
360+
final String query = wikidataQuery
361+
.replace("${LAT_WEST}", String.format(Locale.ROOT, "%.4f", westCornerLat))
362+
.replace("${LONG_WEST}", String.format(Locale.ROOT, "%.4f", westCornerLong))
363+
.replace("${LAT_EAST}", String.format(Locale.ROOT, "%.4f", eastCornerLat))
364+
.replace("${LONG_EAST}", String.format(Locale.ROOT, "%.4f", eastCornerLong))
365+
.replace("${LANG}", language);
366+
final HttpUrl.Builder urlBuilder = HttpUrl
367+
.parse(sparqlQueryUrl)
368+
.newBuilder()
369+
.addQueryParameter("query", query)
370+
.addQueryParameter("format", "json");
371+
372+
final Request request = new Request.Builder()
373+
.url(urlBuilder.build())
374+
.build();
375+
310376
final Response response = okHttpClient.newCall(request).execute();
311377
if (response.body() != null && response.isSuccessful()) {
312378
final String json = response.body().string();
@@ -330,18 +396,16 @@ public List<Place> getNearbyPlaces(final LatLng cur, final String language, fina
330396
/**
331397
* Make API Call to get Nearby Places Implementation does not expects a custom query
332398
*
333-
* @param cur Search lat long
334-
* @param language Language
335-
* @param radius Search Radius
336-
* @param shouldQueryForMonuments : Should we query for monuments
399+
* @param cur Search lat long
400+
* @param language Language
401+
* @param radius Search Radius
337402
* @return
338403
* @throws Exception
339404
*/
340405
@Nullable
341-
public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius,
342-
final boolean shouldQueryForMonuments)
406+
public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius)
343407
throws Exception {
344-
return getNearbyPlaces(cur, language, radius, shouldQueryForMonuments, null);
408+
return getNearbyPlaces(cur, language, radius, null);
345409
}
346410

347411
/**

app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java

Lines changed: 120 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,34 +39,34 @@ public NearbyController(NearbyPlaces nearbyPlaces) {
3939
/**
4040
* Prepares Place list to make their distance information update later.
4141
*
42-
* @param curLatLng current ___location for user
43-
* @param searchLatLng the ___location user wants to search around
42+
* @param curLatLng current ___location for user
43+
* @param searchLatLng the ___location user wants to search around
4444
* @param returnClosestResult if this search is done to find closest result or all results
45-
* @param customQuery if this search is done via an advanced query
46-
* @return NearbyPlacesInfo a variable holds Place list without distance information
47-
* and boundary coordinates of current Place List
45+
* @param customQuery if this search is done via an advanced query
46+
* @return NearbyPlacesInfo a variable holds Place list without distance information and
47+
* boundary coordinates of current Place List
4848
*/
49-
public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng, final LatLng searchLatLng,
49+
public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng,
50+
final LatLng searchLatLng,
5051
final boolean returnClosestResult, final boolean checkingAroundCurrentLocation,
51-
final boolean shouldQueryForMonuments, @Nullable final String customQuery) throws Exception {
52+
@Nullable final String customQuery) throws Exception {
5253

5354
Timber.d("Loading attractions near %s", searchLatLng);
5455
NearbyPlacesInfo nearbyPlacesInfo = new NearbyPlacesInfo();
55-
5656
if (searchLatLng == null) {
5757
Timber.d("Loading attractions nearby, but curLatLng is null");
5858
return null;
5959
}
6060
List<Place> places = nearbyPlaces
6161
.radiusExpander(searchLatLng, Locale.getDefault().getLanguage(), returnClosestResult,
62-
shouldQueryForMonuments, customQuery);
62+
customQuery);
6363

6464
if (null != places && places.size() > 0) {
65-
LatLng[] boundaryCoordinates = {places.get(0).___location, // south
66-
places.get(0).___location, // north
67-
places.get(0).___location, // west
68-
places.get(0).___location};// east, init with a random ___location
69-
65+
LatLng[] boundaryCoordinates = {
66+
places.get(0).___location, // south
67+
places.get(0).___location, // north
68+
places.get(0).___location, // west
69+
places.get(0).___location};// east, init with a random ___location
7070

7171
if (curLatLng != null) {
7272
Timber.d("Sorting places by distance...");
@@ -88,11 +88,11 @@ public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng, fina
8888
}
8989
}
9090
Collections.sort(places,
91-
(lhs, rhs) -> {
92-
double lhsDistance = distances.get(lhs);
93-
double rhsDistance = distances.get(rhs);
94-
return (int) (lhsDistance - rhsDistance);
95-
}
91+
(lhs, rhs) -> {
92+
double lhsDistance = distances.get(lhs);
93+
double rhsDistance = distances.get(rhs);
94+
return (int) (lhsDistance - rhsDistance);
95+
}
9696
);
9797
}
9898
nearbyPlacesInfo.curLatLng = curLatLng;
@@ -104,11 +104,11 @@ public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng, fina
104104
if (!returnClosestResult) {
105105
// To remember latest search either around user or any point on map
106106
latestSearchLocation = searchLatLng;
107-
latestSearchRadius = nearbyPlaces.radius*1000; // to meter
107+
latestSearchRadius = nearbyPlaces.radius * 1000; // to meter
108108

109109
// Our radius searched around us, will be used to understand when user search their own ___location, we will follow them
110110
if (checkingAroundCurrentLocation) {
111-
currentLocationSearchRadius = nearbyPlaces.radius*1000; // to meter
111+
currentLocationSearchRadius = nearbyPlaces.radius * 1000; // to meter
112112
currentLocation = curLatLng;
113113
}
114114
}
@@ -118,6 +118,98 @@ public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng, fina
118118
return nearbyPlacesInfo;
119119
}
120120

121+
/**
122+
* Prepares Place list to make their distance information update later.
123+
*
124+
* @param curLatLng The current latitude and longitude.
125+
* @param screenTopRight The top right corner of the screen (latitude,
126+
* longitude).
127+
* @param screenBottomLeft The bottom left corner of the screen (latitude,
128+
* longitude).
129+
* @param searchLatLng The latitude and longitude of the search ___location.
130+
* @param returnClosestResult Flag indicating whether to return the closest result.
131+
* @param checkingAroundCurrentLocation Flag indicating whether to check around the current
132+
* ___location.
133+
* @param shouldQueryForMonuments Flag indicating whether to include monuments in the
134+
* query.
135+
* @param customQuery Optional custom SPARQL query to use instead of default
136+
* queries.
137+
* @return An object containing information about nearby places.
138+
* @throws Exception If an error occurs during the retrieval process.
139+
*/
140+
public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng,
141+
final fr.free.nrw.commons.___location.LatLng screenTopRight,
142+
final fr.free.nrw.commons.___location.LatLng screenBottomLeft, final LatLng searchLatLng,
143+
final boolean returnClosestResult, final boolean checkingAroundCurrentLocation,
144+
final boolean shouldQueryForMonuments, @Nullable final String customQuery)
145+
throws Exception {
146+
147+
Timber.d("Loading attractions near %s", searchLatLng);
148+
NearbyPlacesInfo nearbyPlacesInfo = new NearbyPlacesInfo();
149+
150+
if (searchLatLng == null) {
151+
Timber.d("Loading attractions nearby, but curLatLng is null");
152+
return null;
153+
}
154+
155+
List<Place> places = nearbyPlaces.getFromWikidataQuery(screenTopRight, screenBottomLeft,
156+
Locale.getDefault().getLanguage(), shouldQueryForMonuments, customQuery);
157+
158+
if (null != places && places.size() > 0) {
159+
LatLng[] boundaryCoordinates = {
160+
places.get(0).___location, // south
161+
places.get(0).___location, // north
162+
places.get(0).___location, // west
163+
places.get(0).___location};// east, init with a random ___location
164+
165+
if (curLatLng != null) {
166+
Timber.d("Sorting places by distance...");
167+
final Map<Place, Double> distances = new HashMap<>();
168+
for (Place place : places) {
169+
distances.put(place, computeDistanceBetween(place.___location, curLatLng));
170+
// Find boundaries with basic find max approach
171+
if (place.___location.getLatitude() < boundaryCoordinates[0].getLatitude()) {
172+
boundaryCoordinates[0] = place.___location;
173+
}
174+
if (place.___location.getLatitude() > boundaryCoordinates[1].getLatitude()) {
175+
boundaryCoordinates[1] = place.___location;
176+
}
177+
if (place.___location.getLongitude() < boundaryCoordinates[2].getLongitude()) {
178+
boundaryCoordinates[2] = place.___location;
179+
}
180+
if (place.___location.getLongitude() > boundaryCoordinates[3].getLongitude()) {
181+
boundaryCoordinates[3] = place.___location;
182+
}
183+
}
184+
Collections.sort(places,
185+
(lhs, rhs) -> {
186+
double lhsDistance = distances.get(lhs);
187+
double rhsDistance = distances.get(rhs);
188+
return (int) (lhsDistance - rhsDistance);
189+
}
190+
);
191+
}
192+
nearbyPlacesInfo.curLatLng = curLatLng;
193+
nearbyPlacesInfo.searchLatLng = searchLatLng;
194+
nearbyPlacesInfo.placeList = places;
195+
nearbyPlacesInfo.boundaryCoordinates = boundaryCoordinates;
196+
197+
// Returning closes result means we use the controller for nearby card. So no need to set search this area flags
198+
if (!returnClosestResult) {
199+
// To remember latest search either around user or any point on map
200+
latestSearchLocation = searchLatLng;
201+
latestSearchRadius = nearbyPlaces.radius * 1000; // to meter
202+
203+
// Our radius searched around us, will be used to understand when user search their own ___location, we will follow them
204+
if (checkingAroundCurrentLocation) {
205+
currentLocationSearchRadius = nearbyPlaces.radius * 1000; // to meter
206+
currentLocation = curLatLng;
207+
}
208+
}
209+
}
210+
return nearbyPlacesInfo;
211+
}
212+
121213
/**
122214
* Prepares Place list to make their distance information update later.
123215
*
@@ -129,10 +221,10 @@ public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng, fina
129221
*/
130222
public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng,
131223
final LatLng searchLatLng,
132-
final boolean returnClosestResult, final boolean checkingAroundCurrentLocation,
133-
final boolean shouldQueryForMonuments) throws Exception {
224+
final boolean returnClosestResult, final boolean checkingAroundCurrentLocation)
225+
throws Exception {
134226
return loadAttractionsFromLocation(curLatLng, searchLatLng, returnClosestResult,
135-
checkingAroundCurrentLocation, shouldQueryForMonuments, null);
227+
checkingAroundCurrentLocation, null);
136228
}
137229

138230
/**
@@ -171,12 +263,14 @@ public static List<BaseMarker> loadAttractionsFromLocationToBaseMarkerOptions(
171263

172264
/**
173265
* Updates makerLabelList item isBookmarked value
174-
* @param place place which is bookmarked
266+
*
267+
* @param place place which is bookmarked
175268
* @param isBookmarked true is bookmarked, false if bookmark removed
176269
*/
177270
@MainThread
178271
public static void updateMarkerLabelListBookmark(Place place, boolean isBookmarked) {
179-
for (ListIterator<MarkerPlaceGroup> iter = markerLabelList.listIterator(); iter.hasNext();) {
272+
for (ListIterator<MarkerPlaceGroup> iter = markerLabelList.listIterator();
273+
iter.hasNext(); ) {
180274
MarkerPlaceGroup markerPlaceGroup = iter.next();
181275
if (markerPlaceGroup.getPlace().getWikiDataEntityId().equals(place.getWikiDataEntityId())) {
182276
iter.set(new MarkerPlaceGroup(isBookmarked, place));

0 commit comments

Comments
 (0)