mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +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.explore.depictions.DepictsClient;
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
import fr.free.nrw.commons.location.LatLng;
|
||||||
import fr.free.nrw.commons.nearby.Place;
|
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.NearbyResponse;
|
||||||
import fr.free.nrw.commons.nearby.model.NearbyResultItem;
|
import fr.free.nrw.commons.nearby.model.NearbyResultItem;
|
||||||
import fr.free.nrw.commons.profile.achievements.FeaturedImages;
|
import fr.free.nrw.commons.profile.achievements.FeaturedImages;
|
||||||
|
|
@ -27,6 +29,8 @@ import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
|
|
@ -393,6 +397,196 @@ public class OkHttpJsonApiClient {
|
||||||
throw new Exception(response.message());
|
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
|
* 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;
|
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.
|
* Prepares Place list to make their distance information update later.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -119,4 +119,27 @@ public class NearbyPlaces {
|
||||||
.getNearbyPlaces(screenTopRight, screenBottomLeft, lang, shouldQueryForMonuments,
|
.getNearbyPlaces(screenTopRight, screenBottomLeft, lang, shouldQueryForMonuments,
|
||||||
customQuery);
|
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.location.LocationManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.Html;
|
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.contributions.MainActivity.ActiveFragment;
|
||||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
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.LocationServiceManager;
|
||||||
import fr.free.nrw.commons.location.LocationUpdateListener;
|
import fr.free.nrw.commons.location.LocationUpdateListener;
|
||||||
import fr.free.nrw.commons.nearby.CheckBoxTriStates;
|
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.Observable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
@ -357,6 +363,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
||||||
@NonNull final MenuInflater inflater) {
|
@NonNull final MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.nearby_fragment_menu, menu);
|
inflater.inflate(R.menu.nearby_fragment_menu, menu);
|
||||||
MenuItem listMenu = menu.findItem(R.id.list_sheet);
|
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() {
|
listMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem item) {
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
|
|
@ -364,6 +372,44 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
||||||
return false;
|
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
|
@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(
|
private void populatePlacesForCurrentLocation(
|
||||||
final fr.free.nrw.commons.location.LatLng currentLatLng,
|
final fr.free.nrw.commons.location.LatLng currentLatLng,
|
||||||
final fr.free.nrw.commons.location.LatLng screenTopRight,
|
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"
|
app:showAsAction="ifRoom|withText"
|
||||||
android:icon="@drawable/ic_list_white_24dp"
|
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>
|
</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