I'm currently trying to build an app where the user gets a notification when he is near to a marker or in my case I'm using the ItemizedOverlay of Osmdroid and I was wondering if there is a way to do that also for several hundreds of markers without emptying the battery in a few minutes.
I saw a few methods but there all were just for the case, when you have just a few markers.
I would be very happy if someone could help me.
To complement #Barns answer, 2 remarks:
when using osmdroid, you don't have LatLng, you have GeoPoint instead,
and you don't need to write your own getDistanceMeters because GeoPoint already has
this method:
GeoPoint.distanceToAsDouble(final IGeoPoint other)
If the Markers have static locations you won't need to load them repeatedly.
If all you want is to calculate the distance between two points on the earth and you have the coordinates in latitude and longitude then there is no need for any Google API's Maps or other libraries. That just costs extra overhead and maintenance. Just make a static method like this:
public static double getDistanceMeters(LatLng pt1, LatLng pt2){
double distance = 0d;
try{
double theta = pt1.longitude - pt2.longitude;
double dist = Math.sin(Math.toRadians(pt1.latitude)) * Math.sin(Math.toRadians(pt2.latitude))
+ Math.cos(Math.toRadians(pt1.latitude)) * Math.cos(Math.toRadians(pt2.latitude)) * Math.cos(Math.toRadians(theta));
dist = Math.acos(dist);
dist = Math.toDegrees(dist);
distance = dist * 60 * 1853.1596;
}
catch (Exception ex){
System.out.println(ex.getMessage());
}
return distance;
}
Then you can do this:
public static boolean checkDistanceIsClose(LatLng pt1, LatLng pt2, double distance){
boolean isInDistance = false;
try{
double calcDistance = getDistanceMeters(pt1, pt2)
if(distance <= calcDistance){
isInDistance = true;
}
}
catch (Exception ex){
System.out.println(ex.getMessage());
}
return isInDistance;
}
Same algorithm will work for any platform. Just translate it to the appropriate program language.
Related
I've got a problem with my osm app. I get Set of points from my server side application and than I want to visualise it as some area. So I create a polygon from this points and add it to my mapView object. It works but as the area becomes bigger there occures a problem with scrolling which just lags until I reach the area without my Overlays.
How can I improve map redrawing performance?
I thought about deviding my area polygon into smaller polygons but than they just intersect with each other and the color isn't uniform.
View of my map
Here is part of my code. Normally I call it everytime I get message with new data from the server in AsyncTask and invalidate mapView on postExecute method but it doesn't really matter since the problem is not with just drawing but scrolling, does it?
List<Overlay> mapOverlays = new ArrayList<>();
Drawable icon = getIcon();
Marker marker = new Marker(mapView);
marker.setPosition(new GeoPoint(newPosition));
marker.setTitle(title);
marker.setIcon(icon);
mapOverlays.add(marker);
List<GeoPoint> listWithAreaPoints = new ArrayList<>();
listWithAreaPoints.addAll(setWithAreaPoints);
Polygon searchedArea = new Polygon(activity.getApplicationContext());
searchedArea.setPoints(listWithAreaPoints);
searchedArea.setFillColor(0x12121212);
searchedArea.setStrokeColor(0x12121212);
searchedArea.setStrokeWidth(0);
mapOverlays.add(searchedArea);
mapView.getOverlays().clear();
mapView.getOverlays().addAll(mapOverlays);
mapView.invalidate();
Thanks for any advice.
I've found out what was wrong.
The data which I was receiving from the server contained a lot of points (hundreds maybe thousands) and I was giving them to Polygon object's method setPoints() in chaotic sequence. In consequence the polygon method draw() drew successive lines without any order what can be seen on my printscreen (blank areas). As the number of points increased the number of polygon's "sides" increased also and redrawing performance decreased.
The solution was to sort List of points by distance so it would represent List of successive corners of the polygon and remove repetitions before giving them as argument to method Polygon.setPoints().
Maybe it will help someone in the future so I'll live here my method to sort GeoPoints as successive polygon's points:
private List<GeoPoint> sortGeoPointsListByDistance(List<GeoPoint> searchedArea){
List<GeoPoint> orderedSearchedArea = new ArrayList<>();
orderedSearchedArea.add(searchedArea.remove(0));
while (searchedArea.size() > 0) {
GeoPoint point = orderedSearchedArea.get(orderedSearchedArea.size() - 1);
int nearestPointIndex = findNearestPointIndex(point, searchedArea);
GeoPoint nearestPoint = searchedArea.get(nearestPointIndex);
if(nearesPointIsTheSamePoint(point, nearestPoint)){
searchedArea.remove(nearestPointIndex);
} else {
orderedSearchedArea.add(searchedArea.remove(nearestPointIndex));
}
}
return orderedSearchedArea;
}
private int findNearestPointIndex(GeoPoint point, List<GeoPoint> listToSearch) {
int index =0;
double dist = 0;
for(int i=0;i<listToSearch.size();i++){
GeoPoint currentPoint = listToSearch.get(i);
double currentPointDist = distFrom( point.getLatitude(), point.getLongitude(), currentPoint.getLatitude(), currentPoint.getLongitude());
if(i==0){
index = i;
dist = currentPointDist;
} else if(currentPointDist<dist){
index = i;
dist = currentPointDist;
}
}
return index;
}
private boolean nearesPointIsTheSamePoint(GeoPoint point, GeoPoint nearestPoint){
if(point.getLatitude()==nearestPoint.getLatitude() && point.getLongitude()==nearestPoint.getLongitude()){
return true;
} else{
return false;
}
}
private double distFrom(double lat1, double lng1, double lat2, double lng2) {
double earthRadius = 6371000; //meters
double dLat = Math.toRadians(lat2-lat1);
double dLng = Math.toRadians(lng2-lng1);
double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
Math.sin(dLng/2) * Math.sin(dLng/2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
double dist = (earthRadius * c);
return dist;
}
I am using the new Android place Api to get autocomplete predictions while the user type.
From what I saw so far the API takes a LatLngBounds object which is created using two locations.
Is there a way to generate a LatLngBounds object using one LatLng as a center point and a radius?
Thank you.
As promised Distwo, here is how I am creating a viewport based on a single latitude and longitude. The reason for me wanting to do this is that my store locator script searches from the viewport returned by the Places API, and so if a viewport isn't returned (rare but it happens) I go ahead and create one from the lat and lng returned by places. Please note that I am not using the android app, this is for regular web viewing.
console.log("no viewport found so lets spherically compute the bounds");
var sw = google.maps.geometry.spherical.computeOffset(place.geometry.location, 1609, 225);
var ne = google.maps.geometry.spherical.computeOffset(place.geometry.location, 1609, 45);
swlat = sw.lat().toFixed(6);
swlng = sw.lng().toFixed(6);
nelat = ne.lat().toFixed(6);
nelng = ne.lng().toFixed(6);
Not sure this is of use to you. In my case the radius is fixed, but in your situation it sounds though it's a variable and hence you'll have to refer to it as a variable.
OK so a little edit: I've changed 1423 to 1000, which represents a lilometer radius. If you're using kilometers then 1000 is the number to use, but if you're using miles then use "1609.3440006" instead.
Thanks to #luke_mclachlan, I found the method I was looking for on the google map github.
https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/SphericalUtil.java
Here is the method I was looking for:
public static LatLng computeOffset(LatLng from, double distance, double heading) {
distance /= EARTH_RADIUS;
heading = toRadians(heading);
// http://williams.best.vwh.net/avform.htm#LL
double fromLat = toRadians(from.latitude);
double fromLng = toRadians(from.longitude);
double cosDistance = cos(distance);
double sinDistance = sin(distance);
double sinFromLat = sin(fromLat);
double cosFromLat = cos(fromLat);
double sinLat = cosDistance * sinFromLat + sinDistance * cosFromLat * cos(heading);
double dLng = atan2(
sinDistance * cosFromLat * sin(heading),
cosDistance - sinFromLat * sinLat);
return new LatLng(toDegrees(asin(sinLat)), toDegrees(fromLng + dLng));
}
Note that Heart radius here is expected in meters.
Using this method, I find the southwest (heading = 225) and northeast (heading = 45) coordinates and then create my LatLongBounds object.
I am fairly new to Android programming, but I am getting pretty good at it (I think: ))
What I am doing is building a situated stories app. It is an app that places audio files at certain GPS markers and enables the user to listen to them at specific locations.
The next step is moving audio files. What I want to do is set a marker at a specific position in a city. (done). Next I want to check the location of a second marker that moves in a circle around it.
What I have so far is this:
public void checkCircularPosition(){
/*
* could be a solution?
*
radius = 250; //offset in meters
gpsLatCenter = 5.1164; //?how can i make this accurate in meters?
gpsLonCenter = 52.0963; //??how can i make this accurate in meters?
degree = 0; //should be variable over time (full circle in 1Hr, 3600sec --> 360/3600 = 0,1 deg/s)
radian;
radian = (degree/180)*Math.PI;
gpsCircleLat = gpsLatCenter+Math.cos(radian)*radius;
gpsCircleLon = gpsLonCenter-Math.sin(radian)*radius;
*/
}
Now, I have checked this code in adobe flash, which made a movie clip move around in a circle. So I know the calculations are somewhat right. But, I have no way of calculating the latitude and longitude of the resulting coordinates.
EDIT!!
i found the solution with the help posted below. still a lot of work to figure out how to use the results. anyway, i posted the resulting function below.
to make this work, you need _radius wich is 6371 (earth's radius), a bearing, a distance, and a start location.
thanks a lot guys!
public static void destinationPoint(double brng, double dist) {
dist = dist/_radius; // convert dist to angular distance in radians
brng = Math.toRadians(brng); //
double lat1 = Math.toRadians(_lat);
double lon1 = Math.toRadians(_lon);
double lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) + Math.cos(lat1)*Math.sin(dist)*Math.cos(brng) );
double lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1), Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2));
lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to -180..+180º
Log.i(APPTAG, ""+Math.toDegrees(lat2));
Log.i(APPTAG, ""+Math.toDegrees(lon2));
Location movLoc = new Location("");
movLoc.setLatitude(Math.toDegrees(lat2));
movLoc.setLongitude(Math.toDegrees(lon2));
Log.i(APPTAG, ""+movLoc);
}
You should check the section Destination point given distance and bearing from start point at this website: http://www.movable-type.co.uk/scripts/latlong.html
That website has the proper formula for using your start point (gpsLatCenter/gpsLonCenter) and bearing (degree in you code) to compute the final lat/lon (gpsCircleLat/gpsCircleLon).
I'm trying to create GeoPoints, but I couldn't so far.
double latitudeUpperLeft = -34.567645;
double longitudeUpperLeft = -58.497734;
double latitudeBottomRight = -34.62558;
double longitudeBottomRight = -58.42495;
GeoPoint geoPointUpperLeft = calculateGeoPoint(latitudeUpperLeft, longitudeUpperLeft);
GeoPoint geoPointBottomRight = calculateGeoPoint(latitudeBottomRight, longitudeBottomRight);
This is the helper method:
public static GeoPoint calculateGeoPoint(double latitude, double longitude) {
Double latE6 = latitude * 1E6;
Double lngE6 = longitude * 1E6;
return new GeoPoint(latE6.intValue(), lngE6.intValue());
}
I'm getting an InvocationTargetException on the return of the helper method, What could be wrong?
Thanks. Guillermo.
can you tell me if this works.
public static GeoPoint calculateGeoPoint(double latitude, double longitude) {
return new GeoPoint((int)(latitude * 1E6), (int)(longitude * 1E6));
}
I've found the answer in the anddev.org forum.
The problem was in the manifest, somehow it was missing this entry:
<uses-library android:name="com.google.android.maps" />
After adding it, it started to work.
If you want to use Directly GeoPoint .You will follow the steps:
1)First you have data(Location) in degree formate.
2)By the Equation you have to multiply it by 1000000
3)now ,you have particular GEO POINT Location .
Like here ;if I have location like : Latitude:23.0395677 and Longitude:72.5660045° So, your GeoPoint(23039568,72566045);
You get the Perfect Location for it.
I’d like to take a series of samples of coordinates returned by GPS and calculate the (straight line) distance between them so I can graph the distances via Excel. I see the method distanceBetween and distanceTo of the Location class, but I’m concerned these don’t return the straight line distance.
Does anyone know what distance is returned by these methods or if there are any ways to calculate straight line distance based on latitude/longitude values returned by the Location class?
A Google search will offer solutions should you somehow desire to do this calculation yourself. For example the Haversine approach:
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
is reported here. Note that this is straight line and does not account for irregularities in elevation, etc.
How can I measure distance and create a bounding box based on two latitude+longitude points in Java?
provides a Java implementation of the Haversine approach.
Here my code
float[] result=new float[1];
if(cordenatalar.size()>1)
{
LatLong add_kor=(LatLong)cordenatalar.get(cordenatalar.size()-1);
Location.distanceBetween(add_kor.getLat(), add_kor.getLongg(), location.getLatitude(), location.getLongitude(), result);
kilometr+=result[0];
//KMTextView.setText(String.valueOf(kilometr));
}
KMTextView.setText("sss: "+String.valueOf(cordenatalar.size()+" res: "+kilometr+" metr"));
cordenatalar.add(new LatLong(location.getLatitude(), location.getLongitude()));
source = starting location;
destination = current location;
/* this method will fetch you the distance between two geo points. */
source.distanceTo(destination);