Use JSON SPARQL query for fetching nearby places (#2398)

* Use JSON response for nearby places

* Move okhttp calls to a different class

* wip

* Fetch picture of the day using JSON API

* Search images using JSON APIs

* tests

* Fix injection based on code review comments
This commit is contained in:
Vivek Maskara 2019-02-06 10:40:30 +05:30 committed by Ashish Kumar
parent 323527b3be
commit f12837650a
44 changed files with 1472 additions and 418 deletions

View file

@ -112,7 +112,7 @@ public class NearbyController {
* @param placeList list of nearby places in Place data type
* @return Place list that holds nearby places
*/
public static List<Place> loadAttractionsFromLocationToPlaces(
static List<Place> loadAttractionsFromLocationToPlaces(
LatLng curLatLng,
List<Place> placeList) {
placeList = placeList.subList(0, Math.min(placeList.size(), MAX_RESULTS));

View file

@ -1,49 +1,37 @@
package fr.free.nrw.commons.nearby;
import android.net.Uri;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import fr.free.nrw.commons.Utils;
import javax.inject.Inject;
import javax.inject.Singleton;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.upload.FileUtils;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import timber.log.Timber;
/**
* Handles the Wikidata query to obtain Places around search location
*/
@Singleton
public class NearbyPlaces {
private static final double INITIAL_RADIUS = 1.0; // in kilometers
private static final double RADIUS_MULTIPLIER = 1.618;
private static final Uri WIKIDATA_QUERY_URL = Uri.parse("https://query.wikidata.org/sparql");
private static final Uri WIKIDATA_QUERY_UI_URL = Uri.parse("https://query.wikidata.org/");
private final String wikidataQuery;
public double radius = INITIAL_RADIUS;
private final OkHttpJsonApiClient okHttpJsonApiClient;
/**
* Reads Wikidata query to check nearby wikidata items which needs picture, with a circular
* search. As a point is center of a circle with a radius will be set later.
* @param okHttpJsonApiClient
*/
public NearbyPlaces() {
try {
wikidataQuery = FileUtils.readFromResource("/queries/nearby_query.rq");
Timber.v(wikidataQuery);
} catch (IOException e) {
throw new RuntimeException(e);
}
@Inject
public NearbyPlaces(OkHttpJsonApiClient okHttpJsonApiClient) {
this.okHttpJsonApiClient = okHttpJsonApiClient;
}
/**
@ -104,81 +92,6 @@ public class NearbyPlaces {
* @throws IOException if query fails
*/
private List<Place> getFromWikidataQuery(LatLng cur, String lang, double radius) throws IOException {
List<Place> places = new ArrayList<>();
String query = wikidataQuery
.replace("${RAD}", String.format(Locale.ROOT, "%.2f", radius))
.replace("${LAT}", String.format(Locale.ROOT, "%.4f", cur.getLatitude()))
.replace("${LONG}", String.format(Locale.ROOT, "%.4f", cur.getLongitude()))
.replace("${LANG}", lang);
Timber.v("# Wikidata query: \n" + query);
// format as a URL
Timber.d(WIKIDATA_QUERY_UI_URL.buildUpon().fragment(query).build().toString());
String url = WIKIDATA_QUERY_URL.buildUpon().appendQueryParameter("query", query).build().toString();
URLConnection conn = new URL(url).openConnection();
conn.setRequestProperty("Accept", "text/tab-separated-values");
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
Timber.d("Reading from query result...");
while ((line = in.readLine()) != null) {
Timber.v(line);
line = line + "\n"; // to pad columns and make fields a fixed size
if (!line.startsWith("\"Point")) {
continue;
}
String[] fields = line.split("\t");
Timber.v("Fields: " + Arrays.toString(fields));
String point = fields[0];
String wikiDataLink = Utils.stripLocalizedString(fields[1]);
String name = Utils.stripLocalizedString(fields[2]);
//getting icon link here
String identifier = Utils.stripLocalizedString(fields[3]);
//getting the ID which is at the end of link
identifier = identifier.split("/")[Utils.stripLocalizedString(fields[3]).split("/").length-1];
//replaced the extra > char from fields
identifier = identifier.replace(">","");
String type = Utils.stripLocalizedString(fields[4]);
String icon = fields[5];
String wikipediaSitelink = Utils.stripLocalizedString(fields[7]);
String commonsSitelink = Utils.stripLocalizedString(fields[8]);
String category = Utils.stripLocalizedString(fields[9]);
Timber.v("Name: " + name + ", type: " + type + ", category: " + category + ", wikipediaSitelink: " + wikipediaSitelink + ", commonsSitelink: " + commonsSitelink);
double latitude;
double longitude;
Matcher matcher = Pattern.compile("Point\\(([^ ]+) ([^ ]+)\\)").matcher(point);
if (!matcher.find()) {
continue;
}
try {
longitude = Double.parseDouble(matcher.group(1));
latitude = Double.parseDouble(matcher.group(2));
} catch (NumberFormatException e) {
throw new RuntimeException("LatLng parse error: " + point);
}
places.add(new Place(
name,
Label.fromText(identifier), // list
type, // details
Uri.parse(icon),
new LatLng(latitude, longitude, 0),
category,
new Sitelinks.Builder()
.setWikipediaLink(wikipediaSitelink)
.setCommonsLink(commonsSitelink)
.setWikidataLink(wikiDataLink)
.build()
));
}
in.close();
return places;
return okHttpJsonApiClient.getNearbyPlaces(cur, lang, radius).blockingSingle();
}
}

View file

@ -7,6 +7,9 @@ import android.os.Parcelable;
import android.support.annotation.Nullable;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.nearby.model.NearbyResultItem;
import fr.free.nrw.commons.utils.PlaceUtils;
import fr.free.nrw.commons.utils.StringUtils;
import timber.log.Timber;
/**
@ -48,6 +51,26 @@ public class Place implements Parcelable {
this.siteLinks = in.readParcelable(Sitelinks.class.getClassLoader());
}
public static Place from(NearbyResultItem item) {
String itemClass = item.getClassName().getValue();
String classEntityId = "";
if(!StringUtils.isNullOrWhiteSpace(itemClass)) {
classEntityId = itemClass.replace("http://www.wikidata.org/entity/", "");
}
return new Place(
item.getLabel().getValue(),
Label.fromText(classEntityId), // list
item.getClassLabel().getValue(), // details
Uri.parse(item.getIcon().getValue()),
PlaceUtils.latLngFromPointString(item.getLocation().getValue()),
item.getCommonsCategory().getValue(),
new Sitelinks.Builder()
.setWikipediaLink(item.getWikipediaArticle().getValue())
.setCommonsLink(item.getCommonsArticle().getValue())
.setWikidataLink(item.getItem().getValue())
.build());
}
/**
* Gets the name of the place
* @return name

View file

@ -15,6 +15,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.pedrogomez.renderers.Renderer;
import java.util.ArrayList;
@ -43,7 +44,7 @@ public class PlaceRenderer extends Renderer<Place> {
@BindView(R.id.tvName) TextView tvName;
@BindView(R.id.tvDesc) TextView tvDesc;
@BindView(R.id.distance) TextView distance;
@BindView(R.id.icon) ImageView icon;
@BindView(R.id.icon) SimpleDraweeView icon;
@BindView(R.id.buttonLayout) LinearLayout buttonLayout;
@BindView(R.id.cameraButton) LinearLayout cameraButton;
@ -215,7 +216,9 @@ public class PlaceRenderer extends Renderer<Place> {
}
tvDesc.setText(descriptionText);
distance.setText(place.distance);
icon.setImageResource(place.getLabel().getIcon());
icon.setImageURI(place.getSecondaryImageUrl());
directionsButton.setOnClickListener(view -> {
//Open map app at given position

View file

@ -0,0 +1,13 @@
package fr.free.nrw.commons.nearby.model;
public class NearbyResponse {
private final NearbyResults results;
public NearbyResponse(NearbyResults results) {
this.results = results;
}
public NearbyResults getResults() {
return results;
}
}

View file

@ -0,0 +1,70 @@
package fr.free.nrw.commons.nearby.model;
import com.google.gson.annotations.SerializedName;
public class NearbyResultItem {
private final ResultTuple item;
private final ResultTuple wikipediaArticle;
private final ResultTuple commonsArticle;
private final ResultTuple location;
private final ResultTuple label;
private final ResultTuple icon;
@SerializedName("class") private final ResultTuple className;
@SerializedName("class_label") private final ResultTuple classLabel;
@SerializedName("Commons_category") private final ResultTuple commonsCategory;
public NearbyResultItem(ResultTuple item,
ResultTuple wikipediaArticle,
ResultTuple commonsArticle,
ResultTuple location,
ResultTuple label,
ResultTuple icon, ResultTuple className,
ResultTuple classLabel,
ResultTuple commonsCategory) {
this.item = item;
this.wikipediaArticle = wikipediaArticle;
this.commonsArticle = commonsArticle;
this.location = location;
this.label = label;
this.icon = icon;
this.className = className;
this.classLabel = classLabel;
this.commonsCategory = commonsCategory;
}
public ResultTuple getItem() {
return item == null ? new ResultTuple(): item;
}
public ResultTuple getWikipediaArticle() {
return wikipediaArticle == null ? new ResultTuple():wikipediaArticle;
}
public ResultTuple getCommonsArticle() {
return commonsArticle == null ? new ResultTuple():commonsArticle;
}
public ResultTuple getLocation() {
return location == null ? new ResultTuple():location;
}
public ResultTuple getLabel() {
return label == null ? new ResultTuple():label;
}
public ResultTuple getIcon() {
return icon == null ? new ResultTuple():icon;
}
public ResultTuple getClassName() {
return className == null ? new ResultTuple():className;
}
public ResultTuple getClassLabel() {
return classLabel == null ? new ResultTuple():classLabel;
}
public ResultTuple getCommonsCategory() {
return commonsCategory == null ? new ResultTuple():commonsCategory;
}
}

View file

@ -0,0 +1,15 @@
package fr.free.nrw.commons.nearby.model;
import java.util.List;
public class NearbyResults {
private final List<NearbyResultItem> bindings;
public NearbyResults(List<NearbyResultItem> bindings) {
this.bindings = bindings;
}
public List<NearbyResultItem> getBindings() {
return bindings;
}
}

View file

@ -0,0 +1,24 @@
package fr.free.nrw.commons.nearby.model;
public class ResultTuple {
private final String type;
private final String value;
public ResultTuple(String type, String value) {
this.type = type;
this.value = value;
}
public ResultTuple() {
this.type = "";
this.value = "";
}
public String getType() {
return type;
}
public String getValue() {
return value;
}
}