mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
Added functionality to export location of nearby missing pictures to GPX file and KML file (#5645)
* Fixed Grey empty screen at Upload wizard caption step after denying files permission * Empty commit * Fixed loop issue * Created docs for earlier commits * Fixed javadoc * Fixed spaces * Added added basic features to OSM Maps * Added search location feature * Added filter to Open Street Maps * Fixed chipGroup in Open Street Maps * Removed mapBox code * Removed mapBox's code * Reformat code * Reformatted code * Removed rotation feature to map * Removed rotation files and Fixed Marker click problem * Ignored failing tests * Added voice input feature * Fixed test cases * Changed caption and description text * Replaced mapbox to osmdroid in upload activity * Fixed Unit Tests * Made selected marker to be fixed on map * Changed color of map marker * Fixes #5439 by capitalizing first letter of voice input * Removed mapbox code1 * Removed mapbox code2 * Fixed failing tests * Fixed failing due to merging * Added feature to save nearby places as GPX and KML * Fixed error caused by null
This commit is contained in:
parent
dae1f2557e
commit
c41940241b
7 changed files with 443 additions and 0 deletions
|
|
@ -11,6 +11,8 @@ import fr.free.nrw.commons.campaigns.CampaignResponseDTO;
|
|||
import fr.free.nrw.commons.explore.depictions.DepictsClient;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import fr.free.nrw.commons.nearby.model.PlaceBindings;
|
||||
import fr.free.nrw.commons.nearby.model.ItemsClass;
|
||||
import fr.free.nrw.commons.nearby.model.NearbyResponse;
|
||||
import fr.free.nrw.commons.nearby.model.NearbyResultItem;
|
||||
import fr.free.nrw.commons.profile.achievements.FeaturedImages;
|
||||
|
|
@ -27,6 +29,8 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import okhttp3.HttpUrl;
|
||||
|
|
@ -393,6 +397,196 @@ public class OkHttpJsonApiClient {
|
|||
throw new Exception(response.message());
|
||||
}
|
||||
|
||||
/**
|
||||
* Make API Call to get Places
|
||||
*
|
||||
* @param leftLatLng Left lat long
|
||||
* @param rightLatLng Right lat long
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Nullable
|
||||
public String getPlacesAsKML(final LatLng leftLatLng, final LatLng rightLatLng)
|
||||
throws Exception {
|
||||
String kmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
|
||||
"<!--Created by wikidata-missing-pictures-offline -->\n" +
|
||||
"<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n" +
|
||||
" <Document>";
|
||||
|
||||
int increment = 1;
|
||||
double longitude = leftLatLng.getLongitude();
|
||||
|
||||
while (longitude <= rightLatLng.getLongitude()) {
|
||||
double NEXT_LONGITUDE =
|
||||
(increment + longitude) >= 0.0 && (increment + longitude) <= 1.0 ? 0.0
|
||||
: increment + longitude;
|
||||
|
||||
double latitude = leftLatLng.getLatitude();
|
||||
|
||||
while (latitude <= rightLatLng.getLatitude()) {
|
||||
double NEXT_LATITUDE =
|
||||
(increment + latitude) >= 0.0 && (increment + latitude) <= 1.0 ? 0.0
|
||||
: increment + latitude;
|
||||
List<PlaceBindings> placeBindings = runQuery(new LatLng(latitude, longitude, 0),
|
||||
new LatLng(NEXT_LATITUDE, NEXT_LONGITUDE, 0));
|
||||
if (placeBindings != null) {
|
||||
for (PlaceBindings item : placeBindings) {
|
||||
if (item.getItem() != null && item.getLabel() != null && item.getClas() != null) {
|
||||
String input = item.getLocation().getValue();
|
||||
Pattern pattern = Pattern.compile(
|
||||
"Point\\(([-+]?[0-9]*\\.?[0-9]+) ([-+]?[0-9]*\\.?[0-9]+)\\)");
|
||||
Matcher matcher = pattern.matcher(input);
|
||||
|
||||
if (matcher.find()) {
|
||||
String longStr = matcher.group(1);
|
||||
String latStr = matcher.group(2);
|
||||
String itemUrl = item.getItem().getValue();
|
||||
String itemName = item.getLabel().getValue().replace("&", "&");
|
||||
String itemLatitude = latStr;
|
||||
String itemLongitude = longStr;
|
||||
String itemClass = item.getClas().getValue();
|
||||
|
||||
String formattedItemName =
|
||||
!itemClass.isEmpty() ? itemName + " (" + itemClass + ")"
|
||||
: itemName;
|
||||
|
||||
String kmlEntry = "\n <Placemark>\n" +
|
||||
" <name>" + formattedItemName + "</name>\n" +
|
||||
" <description>" + itemUrl + "</description>\n" +
|
||||
" <Point>\n" +
|
||||
" <coordinates>" + itemLongitude + ","
|
||||
+ itemLatitude
|
||||
+ "</coordinates>\n" +
|
||||
" </Point>\n" +
|
||||
" </Placemark>";
|
||||
kmlString = kmlString + kmlEntry;
|
||||
} else {
|
||||
Timber.e("No match found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
latitude += increment;
|
||||
}
|
||||
longitude += increment;
|
||||
}
|
||||
kmlString = kmlString + "\n </Document>\n" +
|
||||
"</kml>\n";
|
||||
return kmlString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make API Call to get Places
|
||||
*
|
||||
* @param leftLatLng Left lat long
|
||||
* @param rightLatLng Right lat long
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Nullable
|
||||
public String getPlacesAsGPX(final LatLng leftLatLng, final LatLng rightLatLng)
|
||||
throws Exception {
|
||||
String gpxString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
|
||||
"<gpx\n" +
|
||||
" version=\"1.0\"\n" +
|
||||
" creator=\"ExpertGPS 1.1 - https://www.topografix.com\"\n" +
|
||||
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
|
||||
" xmlns=\"http://www.topografix.com/GPX/1/0\"\n" +
|
||||
" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">"
|
||||
+ "\n<bounds minlat=\"$MIN_LATITUDE\" minlon=\"$MIN_LONGITUDE\" maxlat=\"$MAX_LATITUDE\" maxlon=\"$MAX_LONGITUDE\"/>";
|
||||
|
||||
int increment = 1;
|
||||
double longitude = leftLatLng.getLongitude();
|
||||
|
||||
while (longitude <= rightLatLng.getLongitude()) {
|
||||
double NEXT_LONGITUDE =
|
||||
(increment + longitude) >= 0.0 && (increment + longitude) <= 1.0 ? 0.0
|
||||
: increment + longitude;
|
||||
|
||||
double latitude = leftLatLng.getLatitude();
|
||||
|
||||
while (latitude <= rightLatLng.getLatitude()) {
|
||||
double NEXT_LATITUDE =
|
||||
(increment + latitude) >= 0.0 && (increment + latitude) <= 1.0 ? 0.0
|
||||
: increment + latitude;
|
||||
List<PlaceBindings> placeBindings = runQuery(new LatLng(latitude, longitude, 0),
|
||||
new LatLng(NEXT_LATITUDE, NEXT_LONGITUDE, 0));
|
||||
if (placeBindings != null) {
|
||||
for (PlaceBindings item : placeBindings) {
|
||||
if (item.getItem() != null && item.getLabel() != null && item.getClas() != null) {
|
||||
String input = item.getLocation().getValue();
|
||||
Pattern pattern = Pattern.compile(
|
||||
"Point\\(([-+]?[0-9]*\\.?[0-9]+) ([-+]?[0-9]*\\.?[0-9]+)\\)");
|
||||
Matcher matcher = pattern.matcher(input);
|
||||
|
||||
if (matcher.find()) {
|
||||
String longStr = matcher.group(1);
|
||||
String latStr = matcher.group(2);
|
||||
String itemUrl = item.getItem().getValue();
|
||||
String itemName = item.getLabel().getValue().replace("&", "&");
|
||||
String itemLatitude = latStr;
|
||||
String itemLongitude = longStr;
|
||||
String itemClass = item.getClas().getValue();
|
||||
|
||||
String formattedItemName =
|
||||
!itemClass.isEmpty() ? itemName + " (" + itemClass + ")"
|
||||
: itemName;
|
||||
|
||||
String gpxEntry =
|
||||
"\n <wpt lat=\"" + itemLatitude + "\" lon=\"" + itemLongitude
|
||||
+ "\">\n" +
|
||||
" <name>" + itemName + "</name>\n" +
|
||||
" <url>" + itemUrl + "</url>\n" +
|
||||
" </wpt>";
|
||||
gpxString = gpxString + gpxEntry;
|
||||
|
||||
} else {
|
||||
Timber.e("No match found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
latitude += increment;
|
||||
}
|
||||
longitude += increment;
|
||||
}
|
||||
gpxString = gpxString + "\n</gpx>";
|
||||
return gpxString;
|
||||
}
|
||||
|
||||
private List<PlaceBindings> runQuery(final LatLng currentLatLng, final LatLng nextLatLng)
|
||||
throws IOException {
|
||||
|
||||
final String wikidataQuery = FileUtils.readFromResource("/queries/places_query.rq");
|
||||
final String query = wikidataQuery
|
||||
.replace("${LONGITUDE}",
|
||||
String.format(Locale.ROOT, "%.2f", currentLatLng.getLongitude()))
|
||||
.replace("${LATITUDE}", String.format(Locale.ROOT, "%.4f", currentLatLng.getLatitude()))
|
||||
.replace("${NEXT_LONGITUDE}",
|
||||
String.format(Locale.ROOT, "%.4f", nextLatLng.getLongitude()))
|
||||
.replace("${NEXT_LATITUDE}",
|
||||
String.format(Locale.ROOT, "%.4f", nextLatLng.getLatitude()));
|
||||
|
||||
final HttpUrl.Builder urlBuilder = HttpUrl
|
||||
.parse(sparqlQueryUrl)
|
||||
.newBuilder()
|
||||
.addQueryParameter("query", query)
|
||||
.addQueryParameter("format", "json");
|
||||
|
||||
final Request request = new Request.Builder()
|
||||
.url(urlBuilder.build())
|
||||
.build();
|
||||
|
||||
final Response response = okHttpClient.newCall(request).execute();
|
||||
if (response.body() != null && response.isSuccessful()) {
|
||||
final String json = response.body().string();
|
||||
final ItemsClass item = gson.fromJson(json, ItemsClass.class);
|
||||
return item.getResults().getBindings();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make API Call to get Nearby Places Implementation does not expects a custom query
|
||||
*
|
||||
|
|
|
|||
|
|
@ -118,6 +118,14 @@ public class NearbyController extends MapController {
|
|||
return nearbyPlacesInfo;
|
||||
}
|
||||
|
||||
public String getPlacesAsKML(LatLng leftLatLng, LatLng rightLatLng) throws Exception {
|
||||
return nearbyPlaces.getPlacesAsKML(leftLatLng, rightLatLng);
|
||||
}
|
||||
|
||||
public String getPlacesAsGPX(LatLng leftLatLng, LatLng rightLatLng) throws Exception {
|
||||
return nearbyPlaces.getPlacesAsGPX(leftLatLng, rightLatLng);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares Place list to make their distance information update later.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -119,4 +119,27 @@ public class NearbyPlaces {
|
|||
.getNearbyPlaces(screenTopRight, screenBottomLeft, lang, shouldQueryForMonuments,
|
||||
customQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the Wikidata query to retrieve the KML String
|
||||
*
|
||||
* @param leftLatLng coordinates of Left Most position
|
||||
* @param rightLatLng coordinates of Right Most position
|
||||
* @throws IOException if query fails
|
||||
*/
|
||||
public String getPlacesAsKML(LatLng leftLatLng, LatLng rightLatLng) throws Exception {
|
||||
return okHttpJsonApiClient.getPlacesAsKML(leftLatLng, rightLatLng);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the Wikidata query to retrieve the GPX String
|
||||
*
|
||||
* @param leftLatLng coordinates of Left Most position
|
||||
* @param rightLatLng coordinates of Right Most position
|
||||
* @throws IOException if query fails
|
||||
*/
|
||||
public String getPlacesAsGPX(LatLng leftLatLng, LatLng rightLatLng) throws Exception {
|
||||
return okHttpJsonApiClient.getPlacesAsGPX(leftLatLng, rightLatLng);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import android.location.Location;
|
|||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.Html;
|
||||
|
|
@ -80,6 +81,7 @@ import fr.free.nrw.commons.contributions.MainActivity;
|
|||
import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||
import fr.free.nrw.commons.location.LocationUpdateListener;
|
||||
import fr.free.nrw.commons.nearby.CheckBoxTriStates;
|
||||
|
|
@ -105,11 +107,15 @@ import fr.free.nrw.commons.wikidata.WikidataEditListener;
|
|||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -357,6 +363,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
@NonNull final MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.nearby_fragment_menu, menu);
|
||||
MenuItem listMenu = menu.findItem(R.id.list_sheet);
|
||||
MenuItem saveAsGPXButton = menu.findItem(R.id.list_item_gpx);
|
||||
MenuItem saveAsKMLButton = menu.findItem(R.id.list_item_kml);
|
||||
listMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
|
|
@ -364,6 +372,44 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
return false;
|
||||
}
|
||||
});
|
||||
saveAsGPXButton.setOnMenuItemClickListener(new OnMenuItemClickListener() {
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(@NonNull MenuItem item) {
|
||||
try {
|
||||
IGeoPoint screenTopRight = mapView.getProjection().fromPixels(mapView.getWidth(), 0);
|
||||
IGeoPoint screenBottomLeft = mapView.getProjection().fromPixels(0, mapView.getHeight());
|
||||
fr.free.nrw.commons.location.LatLng screenTopRightLatLng = new fr.free.nrw.commons.location.LatLng(
|
||||
screenBottomLeft.getLatitude(), screenBottomLeft.getLongitude(), 0);
|
||||
fr.free.nrw.commons.location.LatLng screenBottomLeftLatLng = new fr.free.nrw.commons.location.LatLng(
|
||||
screenTopRight.getLatitude(), screenTopRight.getLongitude(), 0);
|
||||
setProgressBarVisibility(true);
|
||||
savePlacesAsGPX(screenTopRightLatLng,screenBottomLeftLatLng);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
saveAsKMLButton.setOnMenuItemClickListener(new OnMenuItemClickListener() {
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(@NonNull MenuItem item) {
|
||||
try {
|
||||
IGeoPoint screenTopRight = mapView.getProjection().fromPixels(mapView.getWidth(), 0);
|
||||
IGeoPoint screenBottomLeft = mapView.getProjection().fromPixels(0, mapView.getHeight());
|
||||
fr.free.nrw.commons.location.LatLng screenTopRightLatLng = new fr.free.nrw.commons.location.LatLng(
|
||||
screenBottomLeft.getLatitude(), screenBottomLeft.getLongitude(), 0);
|
||||
fr.free.nrw.commons.location.LatLng screenBottomLeftLatLng = new fr.free.nrw.commons.location.LatLng(
|
||||
screenTopRight.getLatitude(), screenTopRight.getLongitude(), 0);
|
||||
setProgressBarVisibility(true);
|
||||
savePlacesAsKML(screenTopRightLatLng,screenBottomLeftLatLng);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1198,6 +1244,102 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
}
|
||||
}
|
||||
|
||||
private void savePlacesAsKML(LatLng latLng, LatLng nextlatLng) {
|
||||
final Observable<String> savePlacesObservable = Observable
|
||||
.fromCallable(() -> nearbyController
|
||||
.getPlacesAsKML(latLng, nextlatLng));
|
||||
compositeDisposable.add(savePlacesObservable
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(kmlString -> {
|
||||
if (kmlString != null) {
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
|
||||
Locale.getDefault()).format(new Date());
|
||||
String fileName =
|
||||
"KML_" + timeStamp + "_" + System.currentTimeMillis() + ".kml";
|
||||
boolean saved = saveFile(kmlString, fileName);
|
||||
setProgressBarVisibility(false);
|
||||
if (saved) {
|
||||
Toast.makeText(this.getContext(),
|
||||
"KML file saved successfully at /Downloads/" + fileName,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(this.getContext(), "Failed to save KML file.",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
},
|
||||
throwable -> {
|
||||
Timber.d(throwable);
|
||||
showErrorMessage(getString(R.string.error_fetching_nearby_places)
|
||||
+ throwable.getLocalizedMessage());
|
||||
setProgressBarVisibility(false);
|
||||
presenter.lockUnlockNearby(false);
|
||||
setFilterState();
|
||||
}));
|
||||
}
|
||||
|
||||
private void savePlacesAsGPX(LatLng latLng, LatLng nextlatLng) {
|
||||
final Observable<String> savePlacesObservable = Observable
|
||||
.fromCallable(() -> nearbyController
|
||||
.getPlacesAsGPX(latLng, nextlatLng));
|
||||
compositeDisposable.add(savePlacesObservable
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(gpxString -> {
|
||||
if (gpxString != null) {
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
|
||||
Locale.getDefault()).format(new Date());
|
||||
String fileName =
|
||||
"GPX_" + timeStamp + "_" + System.currentTimeMillis() + ".gpx";
|
||||
boolean saved = saveFile(gpxString, fileName);
|
||||
setProgressBarVisibility(false);
|
||||
if (saved) {
|
||||
Toast.makeText(this.getContext(),
|
||||
"GPX file saved successfully at /Downloads/" + fileName,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(this.getContext(), "Failed to save KML file.",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
},
|
||||
throwable -> {
|
||||
Timber.d(throwable);
|
||||
showErrorMessage(getString(R.string.error_fetching_nearby_places)
|
||||
+ throwable.getLocalizedMessage());
|
||||
setProgressBarVisibility(false);
|
||||
presenter.lockUnlockNearby(false);
|
||||
setFilterState();
|
||||
}));
|
||||
}
|
||||
|
||||
public static boolean saveFile(String string, String fileName) {
|
||||
|
||||
if (!isExternalStorageWritable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File downloadsDir = Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_DOWNLOADS);
|
||||
File kmlFile = new File(downloadsDir, fileName);
|
||||
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(kmlFile);
|
||||
fos.write(string.getBytes());
|
||||
fos.close();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isExternalStorageWritable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
return Environment.MEDIA_MOUNTED.equals(state);
|
||||
}
|
||||
|
||||
private void populatePlacesForCurrentLocation(
|
||||
final fr.free.nrw.commons.location.LatLng currentLatLng,
|
||||
final fr.free.nrw.commons.location.LatLng screenTopRight,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
package fr.free.nrw.commons.nearby.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class PlaceBindings(
|
||||
@SerializedName("item") val item: Item,
|
||||
@SerializedName("label") val label: Label,
|
||||
@SerializedName("location") val location: Location,
|
||||
@SerializedName("class") val clas: Clas
|
||||
)
|
||||
|
||||
data class ItemsClass(
|
||||
@SerializedName("head") val head: Head,
|
||||
@SerializedName("results") val results: Results
|
||||
)
|
||||
|
||||
data class Label(
|
||||
@SerializedName("xml:lang") val xml: String,
|
||||
@SerializedName("type") val type: String,
|
||||
@SerializedName("value") val value: String
|
||||
)
|
||||
|
||||
data class Location(
|
||||
@SerializedName("datatype") val datatype: String,
|
||||
@SerializedName("type") val type: String,
|
||||
@SerializedName("value") val value: String
|
||||
)
|
||||
|
||||
data class Results(
|
||||
@SerializedName("bindings") val bindings: List<PlaceBindings>
|
||||
)
|
||||
|
||||
data class Item(
|
||||
@SerializedName("type") val type: String,
|
||||
@SerializedName("value") val value: String
|
||||
)
|
||||
|
||||
data class Head(
|
||||
@SerializedName("vars") val vars: List<String>
|
||||
)
|
||||
|
||||
|
||||
data class Clas(
|
||||
@SerializedName("type") val type: String,
|
||||
@SerializedName("value") val value: String
|
||||
)
|
||||
|
|
@ -6,4 +6,12 @@
|
|||
app:showAsAction="ifRoom|withText"
|
||||
android:icon="@drawable/ic_list_white_24dp"
|
||||
/>
|
||||
<item android:id="@+id/list_item_gpx"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:title="Save as GPX file" />
|
||||
<item android:id="@+id/list_item_kml"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:title="Save as KML file" />
|
||||
</menu>
|
||||
|
|
|
|||
22
app/src/main/resources/queries/places_query.rq
Normal file
22
app/src/main/resources/queries/places_query.rq
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
SELECT
|
||||
?item
|
||||
(SAMPLE(COALESCE(?en_label, ?fr_label, ?id_label, ?item_label)) as ?label)
|
||||
(SAMPLE(?location) as ?location)
|
||||
(GROUP_CONCAT(DISTINCT ?class_label ; separator=",") as ?class)
|
||||
WHERE {
|
||||
SERVICE wikibase:box {
|
||||
?item wdt:P625 ?location .
|
||||
bd:serviceParam wikibase:cornerSouthWest "Point(${LONGITUDE} ${LATITUDE})"^^geo:wktLiteral .
|
||||
bd:serviceParam wikibase:cornerNorthEast "Point(${NEXT_LONGITUDE} ${NEXT_LATITUDE})"^^geo:wktLiteral .
|
||||
}
|
||||
MINUS {?item wdt:P18 ?image}
|
||||
MINUS {?item wdt:P582 ?endtime.}
|
||||
MINUS {?item wdt:P582 ?dissolvedOrAbolished.}
|
||||
MINUS {?item p:P31 ?instanceStatement. ?instanceStatement pq:P582 ?endtimeQualifier.}
|
||||
OPTIONAL {?item rdfs:label ?en_label . FILTER(LANG(?en_label) = "en")}
|
||||
OPTIONAL {?item rdfs:label ?fr_label . FILTER(LANG(?fr_label) = "fr")}
|
||||
OPTIONAL {?item rdfs:label ?vn_label . FILTER(LANG(?id_label) = "id")}
|
||||
OPTIONAL {?item rdfs:label ?item_label}
|
||||
OPTIONAL {?item wdt:P31 ?class. ?class rdfs:label ?class_label. FILTER(LANG(?class_label) = "en")}
|
||||
}
|
||||
GROUP BY ?item
|
||||
Loading…
Add table
Add a link
Reference in a new issue