Merge pull request #692 from tobias47n9e/feature/location-marker

Add a marker and circle of accuracy for current location
This commit is contained in:
Josephine Lim 2017-05-29 16:33:25 +10:00 committed by GitHub
commit 8104556c65
12 changed files with 140 additions and 44 deletions

View file

@ -29,7 +29,7 @@ public class NearbyControllerTest {
} }
@Test public void testNullAttractions() { @Test public void testNullAttractions() {
LatLng location = new LatLng(0, 0); LatLng location = new LatLng(0, 0, 0);
List<NearbyBaseMarker> options = List<NearbyBaseMarker> options =
NearbyController.loadAttractionsFromLocationToBaseMarkerOptions( NearbyController.loadAttractionsFromLocationToBaseMarkerOptions(
@ -42,7 +42,7 @@ public class NearbyControllerTest {
} }
@Test public void testEmptyList() { @Test public void testEmptyList() {
LatLng location = new LatLng(0, 0); LatLng location = new LatLng(0, 0, 0);
List<Place> emptyList = new ArrayList<>(); List<Place> emptyList = new ArrayList<>();
List<NearbyBaseMarker> options = List<NearbyBaseMarker> options =

View file

@ -262,7 +262,7 @@ public class MediaDataExtractor {
NodeList childNodes = parentNode.getChildNodes(); NodeList childNodes = parentNode.getChildNodes();
double latitudeText = Double.parseDouble(childNodes.item(1).getTextContent()); double latitudeText = Double.parseDouble(childNodes.item(1).getTextContent());
double longitudeText = Double.parseDouble(childNodes.item(2).getTextContent()); double longitudeText = Double.parseDouble(childNodes.item(2).getTextContent());
LatLng coordinates = new LatLng(latitudeText, longitudeText); LatLng coordinates = new LatLng(latitudeText, longitudeText, 0);
return coordinates.getPrettyCoordinateString(); return coordinates.getPrettyCoordinateString();
} }

View file

@ -2,8 +2,9 @@ package fr.free.nrw.commons.location;
public class LatLng { public class LatLng {
public final double latitude; private final double latitude;
public final double longitude; private final double longitude;
private final float accuracy;
/** Accepts latitude and longitude. /** Accepts latitude and longitude.
* North and South values are cut off at 90° * North and South values are cut off at 90°
@ -11,13 +12,14 @@ public class LatLng {
* @param latitude double value * @param latitude double value
* @param longitude double value * @param longitude double value
*/ */
public LatLng(double latitude, double longitude) { public LatLng(double latitude, double longitude, float accuracy) {
if(-180.0D <= longitude && longitude < 180.0D) { if(-180.0D <= longitude && longitude < 180.0D) {
this.longitude = longitude; this.longitude = longitude;
} else { } else {
this.longitude = ((longitude - 180.0D) % 360.0D + 360.0D) % 360.0D - 180.0D; this.longitude = ((longitude - 180.0D) % 360.0D + 360.0D) % 360.0D - 180.0D;
} }
this.latitude = Math.max(-90.0D, Math.min(90.0D, latitude)); this.latitude = Math.max(-90.0D, Math.min(90.0D, latitude));
this.accuracy = accuracy;
} }
public int hashCode() { public int hashCode() {
@ -93,4 +95,31 @@ public class LatLng {
return formatCoordinate(this.latitude) + " " + this.getNorthSouth() + ", " return formatCoordinate(this.latitude) + " " + this.getNorthSouth() + ", "
+ formatCoordinate(this.longitude) + " " + this.getEastWest(); + formatCoordinate(this.longitude) + " " + this.getEastWest();
} }
/**
* Return the location accuracy in meter.
*
* @return float
*/
public float getAccuracy() {
return accuracy;
}
/**
* Return the longitude in degrees.
*
* @return double
*/
public double getLongitude() {
return longitude;
}
/**
* Return the latitude in degrees.
*
* @return double
*/
public double getLatitude() {
return latitude;
}
} }

View file

@ -14,6 +14,7 @@ public class LocationServiceManager implements LocationListener {
private String provider; private String provider;
private LocationManager locationManager; private LocationManager locationManager;
private LatLng latestLocation; private LatLng latestLocation;
private Float latestLocationAccuracy;
public LocationServiceManager(Context context) { public LocationServiceManager(Context context) {
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
@ -24,6 +25,16 @@ public class LocationServiceManager implements LocationListener {
return latestLocation; return latestLocation;
} }
/**
* Returns the accuracy of the location. The measurement is
* given as a radius in meter of 68 % confidence.
*
* @return Float
*/
public Float getLatestLocationAccuracy() {
return latestLocationAccuracy;
}
/** Registers a LocationManager to listen for current location. /** Registers a LocationManager to listen for current location.
*/ */
public void registerLocationManager() { public void registerLocationManager() {
@ -57,9 +68,11 @@ public class LocationServiceManager implements LocationListener {
public void onLocationChanged(Location location) { public void onLocationChanged(Location location) {
double currentLatitude = location.getLatitude(); double currentLatitude = location.getLatitude();
double currentLongitude = location.getLongitude(); double currentLongitude = location.getLongitude();
Timber.d("Latitude: %f Longitude: %f", currentLatitude, currentLongitude); latestLocationAccuracy = location.getAccuracy();
Timber.d("Latitude: %f Longitude: %f Accuracy %f",
currentLatitude, currentLongitude, latestLocationAccuracy);
latestLocation = new LatLng(currentLatitude, currentLongitude); latestLocation = new LatLng(currentLatitude, currentLongitude, latestLocationAccuracy);
} }
@Override @Override

View file

@ -109,8 +109,8 @@ public class NearbyController {
nearbyBaseMarker.title(place.name); nearbyBaseMarker.title(place.name);
nearbyBaseMarker.position( nearbyBaseMarker.position(
new com.mapbox.mapboxsdk.geometry.LatLng( new com.mapbox.mapboxsdk.geometry.LatLng(
place.location.latitude, place.location.getLatitude(),
place.location.longitude)); place.location.getLongitude()));
nearbyBaseMarker.place(place); nearbyBaseMarker.place(place);
nearbyBaseMarker.icon(icon); nearbyBaseMarker.icon(icon);

View file

@ -57,7 +57,7 @@ public class NearbyInfoDialog extends OverlayDialog {
Bundle bundle = getArguments(); Bundle bundle = getArguments();
placeTitle.setText(bundle.getString(ARG_TITLE)); placeTitle.setText(bundle.getString(ARG_TITLE));
placeDescription.setText(bundle.getString(ARG_DESC)); placeDescription.setText(bundle.getString(ARG_DESC));
location = new LatLng(bundle.getDouble(ARG_LATITUDE), bundle.getDouble(ARG_LONGITUDE)); location = new LatLng(bundle.getDouble(ARG_LATITUDE), bundle.getDouble(ARG_LONGITUDE), 0);
getArticleLink(bundle); getArticleLink(bundle);
} }
@ -122,8 +122,8 @@ public class NearbyInfoDialog extends OverlayDialog {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putString(ARG_TITLE, place.name); bundle.putString(ARG_TITLE, place.name);
bundle.putString(ARG_DESC, place.description); bundle.putString(ARG_DESC, place.description);
bundle.putDouble(ARG_LATITUDE, place.location.latitude); bundle.putDouble(ARG_LATITUDE, place.location.getLatitude());
bundle.putDouble(ARG_LONGITUDE, place.location.longitude); bundle.putDouble(ARG_LONGITUDE, place.location.getLongitude());
bundle.putParcelable(ARG_SITE_LINK, place.siteLinks); bundle.putParcelable(ARG_SITE_LINK, place.siteLinks);
mDialog.setArguments(bundle); mDialog.setArguments(bundle);
DialogUtil.showSafely(fragmentActivity, mDialog); DialogUtil.showSafely(fragmentActivity, mDialog);
@ -138,7 +138,8 @@ public class NearbyInfoDialog extends OverlayDialog {
@OnClick(R.id.link_preview_directions_button) @OnClick(R.id.link_preview_directions_button)
void onDirectionsClick() { void onDirectionsClick() {
//Open map app at given position //Open map app at given position
Uri gmmIntentUri = Uri.parse("geo:0,0?q=" + location.latitude + "," + location.longitude); Uri gmmIntentUri = Uri.parse(
"geo:0,0?q=" + location.getLatitude() + "," + location.getLongitude());
Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
if (mapIntent.resolveActivity(getActivity().getPackageManager()) != null) { if (mapIntent.resolveActivity(getActivity().getPackageManager()) != null) {

View file

@ -85,8 +85,8 @@ public class NearbyListFragment extends ListFragment {
Place place = (Place) listview.getItemAtPosition(position); Place place = (Place) listview.getItemAtPosition(position);
LatLng placeLatLng = place.location; LatLng placeLatLng = place.location;
double latitude = placeLatLng.latitude; double latitude = placeLatLng.getLatitude();
double longitude = placeLatLng.longitude; double longitude = placeLatLng.getLongitude();
Timber.d("Item at position %d has coords: Lat: %f Long: %f", position, latitude, longitude); Timber.d("Item at position %d has coords: Lat: %f Long: %f", position, latitude, longitude);

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.nearby; package fr.free.nrw.commons.nearby;
import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
@ -14,6 +15,8 @@ import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLng;
@ -24,6 +27,7 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.services.android.telemetry.MapboxTelemetry; import com.mapbox.services.android.telemetry.MapboxTelemetry;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
@ -79,7 +83,7 @@ public class NearbyMapFragment extends android.support.v4.app.Fragment {
MapboxMapOptions options = new MapboxMapOptions() MapboxMapOptions options = new MapboxMapOptions()
.styleUrl(Style.OUTDOORS) .styleUrl(Style.OUTDOORS)
.camera(new CameraPosition.Builder() .camera(new CameraPosition.Builder()
.target(new LatLng(curLatLng.latitude, curLatLng.longitude)) .target(new LatLng(curLatLng.getLatitude(), curLatLng.getLongitude()))
.zoom(11) .zoom(11)
.build()); .build());
@ -102,6 +106,8 @@ public class NearbyMapFragment extends android.support.v4.app.Fragment {
return false; return false;
} }
}); });
addCurrentLocationMarker(mapboxMap);
} }
}); });
if (PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("theme",true)) { if (PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("theme",true)) {
@ -111,6 +117,53 @@ public class NearbyMapFragment extends android.support.v4.app.Fragment {
} }
} }
/**
* Adds a marker for the user's current position. Adds a
* circle which uses the accuracy * 2, to draw a circle
* which represents the user's position with an accuracy
* of 95%.
*/
public void addCurrentLocationMarker(MapboxMap mapboxMap) {
MarkerOptions currentLocationMarker = new MarkerOptions()
.position(new LatLng(curLatLng.getLatitude(), curLatLng.getLongitude()));
mapboxMap.addMarker(currentLocationMarker);
List<LatLng> circle = createCircleArray(curLatLng.getLatitude(), curLatLng.getLongitude(),
curLatLng.getAccuracy() * 2, 100);
mapboxMap.addPolygon(
new PolygonOptions()
.addAll(circle)
.strokeColor(Color.parseColor("#55000000"))
.fillColor(Color.parseColor("#11000000"))
);
}
/**
* Creates a series of points that create a circle on the map.
* Takes the center latitude, center longitude of the circle,
* the radius in meter and the number of nodes of the circle.
*
* @return List List of LatLng points of the circle.
*/
public List<LatLng> createCircleArray(
double centerLat, double centerLong, float radius, int nodes) {
List<LatLng> circle = new ArrayList<>();
float radiusKilometer = radius / 1000;
double radiusLong = radiusKilometer
/ (111.320 * Math.cos(centerLat * Math.PI / 180));
double radiusLat = radiusKilometer / 110.574;
for (int i = 0; i < nodes; i++) {
double theta = ((double) i / (double) nodes) * (2 * Math.PI);
double nodeLongitude = centerLong + radiusLong * Math.cos(theta);
double nodeLatitude = centerLat + radiusLat * Math.sin(theta);
circle.add(new LatLng(nodeLatitude, nodeLongitude));
}
return circle;
}
@Override @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);

View file

@ -74,8 +74,8 @@ public class NearbyPlaces {
String query = wikidataQuery String query = wikidataQuery
.replace("${RAD}", String.format(Locale.ROOT, "%.2f", radius)) .replace("${RAD}", String.format(Locale.ROOT, "%.2f", radius))
.replace("${LAT}", String.format(Locale.ROOT, "%.4f", cur.latitude)) .replace("${LAT}", String.format(Locale.ROOT, "%.4f", cur.getLatitude()))
.replace("${LONG}", String.format(Locale.ROOT, "%.4f", cur.longitude)) .replace("${LONG}", String.format(Locale.ROOT, "%.4f", cur.getLongitude()))
.replace("${LANG}", lang); .replace("${LANG}", lang);
Timber.v("# Wikidata query: \n" + query); Timber.v("# Wikidata query: \n" + query);
@ -125,7 +125,7 @@ public class NearbyPlaces {
type, // list type, // list
type, // details type, // details
Uri.parse(icon), Uri.parse(icon),
new LatLng(latitude, longitude), new LatLng(latitude, longitude, 0),
new Sitelinks.Builder() new Sitelinks.Builder()
.setWikipediaLink(wikipediaSitelink) .setWikipediaLink(wikipediaSitelink)
.setCommonsLink(commonsSitelink) .setCommonsLink(commonsSitelink)
@ -187,7 +187,7 @@ public class NearbyPlaces {
type, // list type, // list
type, // details type, // details
null, null,
new LatLng(latitude, longitude), new LatLng(latitude, longitude, 0),
new Sitelinks.Builder().build() new Sitelinks.Builder().build()
)); ));
} }

View file

@ -38,10 +38,10 @@ public class LengthUtils {
} }
private static double computeAngleBetween(LatLng from, LatLng to) { private static double computeAngleBetween(LatLng from, LatLng to) {
return distanceRadians(Math.toRadians(from.latitude), return distanceRadians(Math.toRadians(from.getLatitude()),
Math.toRadians(from.longitude), Math.toRadians(from.getLongitude()),
Math.toRadians(to.latitude), Math.toRadians(to.getLatitude()),
Math.toRadians(to.longitude)); Math.toRadians(to.getLongitude()));
} }
private static double distanceRadians(double lat1, double lng1, double lat2, double lng2) { private static double distanceRadians(double lat1, double lng1, double lat2, double lng2) {

View file

@ -9,55 +9,55 @@ import org.junit.Test;
public class LatLngTests { public class LatLngTests {
@Test public void testZeroZero() { @Test public void testZeroZero() {
LatLng place = new LatLng(0, 0); LatLng place = new LatLng(0, 0, 0);
String prettyString = place.getPrettyCoordinateString(); String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("0.0 N, 0.0 E")); Assert.assertThat(prettyString, is("0.0 N, 0.0 E"));
} }
@Test public void testAntipode() { @Test public void testAntipode() {
LatLng place = new LatLng(0, 180); LatLng place = new LatLng(0, 180, 0);
String prettyString = place.getPrettyCoordinateString(); String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("0.0 N, 180.0 W")); Assert.assertThat(prettyString, is("0.0 N, 180.0 W"));
} }
@Test public void testNorthPole() { @Test public void testNorthPole() {
LatLng place = new LatLng(90, 0); LatLng place = new LatLng(90, 0, 0);
String prettyString = place.getPrettyCoordinateString(); String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("90.0 N, 0.0 E")); Assert.assertThat(prettyString, is("90.0 N, 0.0 E"));
} }
@Test public void testSouthPole() { @Test public void testSouthPole() {
LatLng place = new LatLng(-90, 0); LatLng place = new LatLng(-90, 0, 0);
String prettyString = place.getPrettyCoordinateString(); String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("90.0 S, 0.0 E")); Assert.assertThat(prettyString, is("90.0 S, 0.0 E"));
} }
@Test public void testLargerNumbers() { @Test public void testLargerNumbers() {
LatLng place = new LatLng(120, 380); LatLng place = new LatLng(120, 380, 0);
String prettyString = place.getPrettyCoordinateString(); String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("90.0 N, 20.0 E")); Assert.assertThat(prettyString, is("90.0 N, 20.0 E"));
} }
@Test public void testNegativeNumbers() { @Test public void testNegativeNumbers() {
LatLng place = new LatLng(-120, -30); LatLng place = new LatLng(-120, -30, 0);
String prettyString = place.getPrettyCoordinateString(); String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("90.0 S, 30.0 W")); Assert.assertThat(prettyString, is("90.0 S, 30.0 W"));
} }
@Test public void testTooBigWestValue() { @Test public void testTooBigWestValue() {
LatLng place = new LatLng(20, -190); LatLng place = new LatLng(20, -190, 0);
String prettyString = place.getPrettyCoordinateString(); String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("20.0 N, 170.0 E")); Assert.assertThat(prettyString, is("20.0 N, 170.0 E"));
} }
@Test public void testRounding() { @Test public void testRounding() {
LatLng place = new LatLng(0.1234567, -0.33333333); LatLng place = new LatLng(0.1234567, -0.33333333, 0);
String prettyString = place.getPrettyCoordinateString(); String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("0.1235 N, 0.3333 W")); Assert.assertThat(prettyString, is("0.1235 N, 0.3333 W"));
} }
@Test public void testRoundingAgain() { @Test public void testRoundingAgain() {
LatLng place = new LatLng(-0.000001, -0.999999); LatLng place = new LatLng(-0.000001, -0.999999, 0);
String prettyString = place.getPrettyCoordinateString(); String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("0.0 S, 1.0 W")); Assert.assertThat(prettyString, is("0.0 S, 1.0 W"));
} }

View file

@ -10,36 +10,36 @@ import org.junit.Test;
public class LengthUtilsTest { public class LengthUtilsTest {
@Test public void testZeroDistance() { @Test public void testZeroDistance() {
LatLng pointA = new LatLng(0, 0); LatLng pointA = new LatLng(0, 0, 0);
LatLng pointB = new LatLng(0, 0); LatLng pointB = new LatLng(0, 0, 0);
String distance = LengthUtils.formatDistanceBetween(pointA, pointB); String distance = LengthUtils.formatDistanceBetween(pointA, pointB);
Assert.assertThat(distance, is("0m")); Assert.assertThat(distance, is("0m"));
} }
@Test public void testOneDegreeOnEquator() { @Test public void testOneDegreeOnEquator() {
LatLng pointA = new LatLng(0, 0); LatLng pointA = new LatLng(0, 0, 0);
LatLng pointB = new LatLng(0, 1); LatLng pointB = new LatLng(0, 1, 0);
String distance = LengthUtils.formatDistanceBetween(pointA, pointB); String distance = LengthUtils.formatDistanceBetween(pointA, pointB);
Assert.assertThat(distance, is("111.2km")); Assert.assertThat(distance, is("111.2km"));
} }
@Test public void testOneDegreeFortyFiveDegrees() { @Test public void testOneDegreeFortyFiveDegrees() {
LatLng pointA = new LatLng(45, 0); LatLng pointA = new LatLng(45, 0, 0);
LatLng pointB = new LatLng(45, 1); LatLng pointB = new LatLng(45, 1, 0);
String distance = LengthUtils.formatDistanceBetween(pointA, pointB); String distance = LengthUtils.formatDistanceBetween(pointA, pointB);
Assert.assertThat(distance, is("78.6km")); Assert.assertThat(distance, is("78.6km"));
} }
@Test public void testOneDegreeSouthPole() { @Test public void testOneDegreeSouthPole() {
LatLng pointA = new LatLng(-90, 0); LatLng pointA = new LatLng(-90, 0, 0);
LatLng pointB = new LatLng(-90, 1); LatLng pointB = new LatLng(-90, 1, 0);
String distance = LengthUtils.formatDistanceBetween(pointA, pointB); String distance = LengthUtils.formatDistanceBetween(pointA, pointB);
Assert.assertThat(distance, is("0m")); Assert.assertThat(distance, is("0m"));
} }
@Test public void testPoleToPole() { @Test public void testPoleToPole() {
LatLng pointA = new LatLng(90, 0); LatLng pointA = new LatLng(90, 0, 0);
LatLng pointB = new LatLng(-90, 0); LatLng pointB = new LatLng(-90, 0, 0);
String distance = LengthUtils.formatDistanceBetween(pointA, pointB); String distance = LengthUtils.formatDistanceBetween(pointA, pointB);
Assert.assertThat(distance, is("20,015.1km")); Assert.assertThat(distance, is("20,015.1km"));
} }