I am having an issue calculating distance using the Location.distanceTo method.
private class MyLocationOverlay1 extends MyLocationOverlay {
#Override
public void drawMyLocation(Canvas canvas, MapView mapView, Location lastFix, GeoPoint myLocation, long when)
super.drawMyLocation(canvas,mapView,lastFix,myLocation,when);
Location bLocation = new Location("reverseGeocoded");
bLocation.setLatitude(FindList.gpslat); // Value = 3.294391E7
bLocation.setLongitude(FindList.gpslong); // Value = -9.6564615E7
Location aLocation = new Location("reverseGeocoded");
aLocation.setLatitude(myLocation.getLatitudeE6()); // Value = 3.2946164E7
aLocation.setLongitude(myLocation.getLongitudeE6()); // Value = -9.6505141E7
aLocation.set(aLocation); // Don't think I need this
bLocation.set(bLocation); // Don't think I need this either
int distance = (int)aLocation.distanceTo(bLocation); // Value = 12637795 ???
String str = " (" + String.valueOf(distance) + " meters)";
}
}
Can someone tell my why my distance calculation is showing 12,637,795 Meters?
You should Modify lines:
aLocation.setLatitude(myLocation.getLatitudeE6() / 1e6);
aLocation.setLongitude(myLocation.getLongitudeE6() / 1e6);
And remove the lines:
aLocation.set(aLocation);
bLocation.set(bLocation);
Related
I'm trying to calculate distance between LatLng points. It's easy to calculate for two coordinates. I have to calculate distance between more than two LatLngs and calculate the cumulative distance from a set of LatLngs. I calculated distance between two points as per the following code.
tvDistance=(TextView)findViewById(R.id.textView4);
Location loc1=new Location("");
loc1.setLatitude(11.2805);
loc1.setLongitude(77.5989);
Location loc2=new Location("");
loc2.setLatitude(11.2801);
loc2.setLongitude(77.5976);
DecimalFormat format=new DecimalFormat("#.##");
double distanceInMeters = loc1.distanceTo(loc2)/1000;
tvDistance.setText(format.format(distanceInMeters) + " Km's");
Now I have for example sixteen LatLng points. First is starting place and last is stopping place. I have the LatLngs in a ArrayList. tried the following code. But it caused ArrayIndexOutOfBoundException.
Do anybody know a method please share with me. Thanks.
private void calculateDistance() {
for (int i=0;i<coordList.size();i++){
LatLng l1=coordList.get(i);
double lat1=l1.latitude;
double lng1=l1.longitude;
Location location1=new Location("");
location1.setLatitude(lat1);
location1.setLongitude(lng1);
LatLng l2=coordList.get(i+1);
double lat2=l2.latitude;
double lng2=l2.longitude;
Location location2=new Location("");
location2.setLatitude(lat2);
location2.setLongitude(lng2);
DecimalFormat format=new DecimalFormat("#.##");
double distance=location1.distanceTo(location2)/1000;
Toast.makeText(MainPage.this,format.format(distance) + " Km's",Toast.LENGTH_SHORT).show();
}
}
You can use SphericalUtil.computeLength method:
http://googlemaps.github.io/android-maps-utils/javadoc/com/google/maps/android/SphericalUtil.html#computeLength-java.util.List-
Sources: https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/SphericalUtil.java
The line
LatLng l2=coordList.get(i+1);
causes the exception.
jon
->Its easy to calculate the distance if you have sets of latitudes and longitudes
ArrayList<RideRoutes> ride_route_list = new ArrayList<>();
//suppose "ride_route_list" contains collections of Latitudes and longitudes
String distance_covered_str;
double total_meters = 0.0;
for(int i = 0; i < (ride_route_list.size() - 1); i++){
double previous_latitude = Double.parseDouble(ride_route_list.get(i).getRout_latitude());
double previous_longitude = Double.parseDouble(ride_route_list.get(i).getRout_longitude());
double updated_latitude = Double.parseDouble(ride_route_list.get(i+1).getRout_latitude());
double updated_longitude = Double.parseDouble(ride_route_list.get(i+1).getRout_longitude());
Location start_latlng = new Location("location_previous");
start_latlng.setLatitude(previous_latitude);
start_latlng.setLongitude(previous_longitude);
Location end_latlng = new Location("location_updated");
end_latlng.setLatitude(updated_latitude);
end_latlng.setLongitude(updated_longitude);
total_meters += start_latlng.distanceTo(end_latlng);
}
double distance_covered_km = total_meters / 1000;
distance_covered_str = String.format(Locale.getDefault(), "%.2f", distance_covered_km);
Note:
->here ride_route_list is an ArrayList which contains the list of Latitude and longitude
RideRoutes Class Structure:
public class RideRoutes {
private String rout_latitude;
private String rout_longitude;
public RideRoutes(String rout_latitude, String rout_longitude) {
this.rout_latitude = rout_latitude;
this.rout_longitude = rout_longitude;
}
public String getRout_latitude() {
return rout_latitude;
}
public void setRout_latitude(String rout_latitude) {
this.rout_latitude = rout_latitude;
}
public String getRout_longitude() {
return rout_longitude;
}
public void setRout_longitude(String rout_longitude) {
this.rout_longitude = rout_longitude;
}
}
I'm displaying a Google Maps API v2 MapView in an Android app, but it's curiously not updating properly in a consistent fashion. I'm using the GPS to update the map position (tried both LocationManager and LocationClient) and although the map moves to the position, about fifty percent of the time either the street name layer fails to update or there's a fuzzy/blurry section that fails to update--until I do a manual drag (scroll) of the map. Then the entire map updates instantly. I've stripped out a lot of the processing in the app to see if I was somehow preventing a refresh, but it didn't make a difference.
I inserted a mapView.invalidate() call in onCameraChange but that oddly seemed to make the problem occur more readily (although still not 100% of the time).
I am implementing all of the Activity callbacks as required by MapView.
Has anyone encountered a problem like this with Google Map API v2 on Android? If so, did you identify the cause and how did you solve it?
You have to let the map breathe so to speak.
Use animateCamera with a CancelableCallback then when the animation is completed you'll get a call back to onFinish() start the next animation.
public class KmlReader extends ActionBarActivity implements
CancelableCallback {
#Override
public void onFinish() {
startAnimation(); // start next map movement
}
#Override
public void onCancel() {
//Called when user interacts with the map while it is moving.
}
public void startAnimation(){
cameraPosition = mMap.getCameraPosition();
LatLng ll = new LatLng(expectedLocation.getLatitude(),
expectedLocation.getLongitude());
cb.zoom(cameraPosition.zoom)
// previous camera tilt
.tilt(cameraPosition.tilt)
// new expected destination
.target(ll)
// north up or heading view
.bearing((isHeading) ? bearing : 0f);
cameraPosition = cb.build();
CameraUpdate update = CameraUpdateFactory
.newCameraPosition(cameraPosition);
mMap.animateCamera(update, working_interval, this);
}
* Edit this is the code I'm working on now.*
it uses an asynctask for the calcuations. I've given it a walking test but haven't tested it in a vehicle.
private static CameraPosition currentCameraPosition;
private static com.google.android.gms.maps.model.CameraPosition.Builder cameraPositionBuilder;
private volatile CameraUpdate nextCameraUpdate;
// updates
private static final long UPDATE_INTERVAL = 2500;
// fastest
private static final int FASTEST_INTERVAL = 2500;
private static int working_interval = 5000;
private volatile boolean isAnimating;
// Define the callback method that receives location updates
#SuppressLint("NewApi")
#Override
public void onLocationChanged(Location location) {
Log.d("test", Boolean.toString(isAnimating) +" onlocation");
currentCameraPosition = mMap.getCameraPosition();
NewCameraUpdateTask newCameraUpdateTask = new NewCameraUpdateTask();
// This task must run async
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
newCameraUpdateTask.executeOnExecutor(
AsyncTask.THREAD_POOL_EXECUTOR, location);
} else {
newCameraUpdateTask.execute(location);
}
// speed display
setMetersPerSecond(location.getSpeed());
}
// create a newCameraUpdate to move the map with
private class NewCameraUpdateTask extends
AsyncTask<Location, Void, CameraUpdate> {
#Override
protected CameraUpdate doInBackground(Location... params) {
Location workingLocation = null;
CameraUpdate newCameraUpdate = null;
float bearing = 0f;
float speed = 0f;
for (Location mlocation : params) {
speed = mlocation.getSpeed();
// camera position is saved before the start of each animation.
LatLng ll;
if (!mlocation.hasBearing() || speed == 0) {
workingLocation = mlocation;
// previous bearing
} else {
// current bearing
bearing = mlocation.getBearing();
// calculate the age of the location
// atempt for animation to end a little bit past when
// the
// next
// location arrives.
// (location.getSpeed()m/s)(1/1000 interval seconds)(
// 1/1000
// km/m)
// (1/6371 radians/km) = radians/6371000000.0
double expectedDistance = working_interval / 6371000000.0
* speed;
// latitude in Radians
double currentLatitude = Math.toRadians(mlocation
.getLatitude());
// longitude in Radians
double currentlongitude = Math.toRadians(mlocation
.getLongitude());
double calcBearing = Math.toRadians(bearing);
// the camera position is needed so I can put in the
// previous camera bearing when the location has no
// bearing. This should prevent the map from
// zooming to north when the device stops moving.
// calculate the expected latitude and longitude based
// on
// staring
// location
// , bearing, and distance
double sincurrentLatitude = Math.sin(currentLatitude);
double coscurrentLatitude = Math.cos(currentLatitude);
double cosexpectedDistance = Math.cos(expectedDistance);
double sinexpectedDistance = Math.sin(expectedDistance);
double expectedLatitude = Math.asin(sincurrentLatitude
* cosexpectedDistance + coscurrentLatitude
* sinexpectedDistance * Math.cos(calcBearing));
double a = Math.atan2(
Math.sin(calcBearing) * sinexpectedDistance
* coscurrentLatitude,
cosexpectedDistance - sincurrentLatitude
* Math.sin(expectedLatitude));
double expectedLongitude = currentlongitude + a;
expectedLongitude = (expectedLongitude + PI3) % PI2 - PI;
// convert to degrees for the expected destination
double expectedLongitudeDestination = Math
.toDegrees(expectedLongitude);
double expectedLatitudeDestination = Math
.toDegrees(expectedLatitude);
mlocation.setLatitude(expectedLatitudeDestination);
mlocation.setLongitude(expectedLongitudeDestination);
workingLocation = mlocation;
}
break;
}
if (workingLocation != null) {
if (workingLocation.hasBearing()) {
bearing = workingLocation.getBearing();
} else {
bearing = currentCameraPosition.bearing;
}
LatLng ll = new LatLng(workingLocation.getLatitude(),
workingLocation.getLongitude());
cameraPositionBuilder.zoom(currentCameraPosition.zoom)
// previous camera tilt
.tilt(currentCameraPosition.tilt)
// new expected destination
.target(ll)
// north up or heading view
.bearing((isHeading) ? bearing : 0f);
newCameraUpdate = CameraUpdateFactory
.newCameraPosition(cameraPositionBuilder.build());
}
return newCameraUpdate;
}
#Override
protected void onPostExecute(CameraUpdate result) {
Log.d("test", Boolean.toString(isAnimating) + " onPostExecute");
if (result != null) {
nextCameraUpdate = result;
// stop the currently playing animation
// there is a new one ready to start
if (isAnimating) {
if (mMap != null) {
mMap.stopAnimation();
}
}
// start the next animation
startAnimation();
Log.d("test", Boolean.toString(isAnimating) +" onPostExecuteComplete");
}
}
}
// called when map animation has been canceled
#Override
public void onCancel() {
Log.d("test", Boolean.toString(isAnimating) +" oncancel");
isAnimating = false;
}
#Override
public void onFinish() {
Log.d("test", Boolean.toString(isAnimating) +" onfinish");
isAnimating = false;
startAnimation();
// call to start saved animation.
}
private void startAnimation() {
Log.d("test", Boolean.toString(isAnimating) +" startAnimation");
if (action_track) {
if (isAnimating) {
return;
}
if (nextCameraUpdate == null) {
return;
}
// abort if animating
isAnimating = true;
CameraUpdate animateCameraUpdate = nextCameraUpdate;
nextCameraUpdate = null;
mMap.animateCamera(animateCameraUpdate, working_interval, this);
Log.d("test", Boolean.toString(isAnimating) +" startanimateCamera");
}
}
In case someone is having this issue, the key is to keep the animateCamera call's animation duration less than the frequency with which you are calling the animateCamera method.
For example if you call animateCamera every 1000ms set tha animate camera duration to be less than that.
map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, ZOOM_LEVEL), 900, null)
If your animateCamera call is not being called at a constant time, then danny117's answer to use the callback to trigger the next camera update will work perfectly.
Inspired from danny117's solution, I found a simpler solution.
I set the location request update for each 5 millisecond and I set the animate camera duration to be 2.5 ms. Problem solved
I use Google Maps in an app and it is likely that multiple markers are attached to same location where each marker represent a person. In this situation user will not know that there are other markers/persons behind this particular marker.
I looked around to handle this situation and one question on SO suggests that I can display a single marker and associate all persons with that single marker. When user taps that marker I should display a list of all the user associated with that marker. This is a good workaround but I would want to avoid displaying a view that mostly hides Google Maps.
Has anyone used any workaround in similar situation?
Workaround I used is to display markers with same location little bit apart on map so that user gets a slight idea of multiple marker.
I keep track of markers on map and their locations on map, and whenever I want to add a marker on map I make sure that no other marker is displayed on same location. If yes, then I add an offset to location of new marker that I want to add.
static final float COORDINATE_OFFSET = 0.00002f; // You can change this value according to your need
Below method returns the location that has to be used for new marker. This method takes as parameters new marker's current latitude and longitude.
// Check if any marker is displayed on given coordinate. If yes then decide
// another appropriate coordinate to display this marker. It returns an
// array with latitude(at index 0) and longitude(at index 1).
private String[] coordinateForMarker(float latitude, float longitude) {
String[] location = new String[2];
for (int i = 0; i <= MAX_NUMBER_OF_MARKERS; i++) {
if (mapAlreadyHasMarkerForLocation((latitude + i
* COORDINATE_OFFSET)
+ "," + (longitude + i * COORDINATE_OFFSET))) {
// If i = 0 then below if condition is same as upper one. Hence, no need to execute below if condition.
if (i == 0)
continue;
if (mapAlreadyHasMarkerForLocation((latitude - i
* COORDINATE_OFFSET)
+ "," + (longitude - i * COORDINATE_OFFSET))) {
continue;
} else {
location[0] = latitude - (i * COORDINATE_OFFSET) + "";
location[1] = longitude - (i * COORDINATE_OFFSET) + "";
break;
}
} else {
location[0] = latitude + (i * COORDINATE_OFFSET) + "";
location[1] = longitude + (i * COORDINATE_OFFSET) + "";
break;
}
}
return location;
}
// Return whether marker with same location is already on map
private boolean mapAlreadyHasMarkerForLocation(String location) {
return (markerLocation.containsValue(location));
}
In above code, markerLocation is a HashMap.
HashMap<String, String> markerLocation; // HashMap of marker identifier and its location as a string
This answer has code for android but same logic applies in iOS.
Maybe you should take a look at marker clustering. It's a common solution for showing a lot of markers on the same place.
Google article about it: https://developers.google.com/maps/articles/toomanymarkers
There are also existing libraries to do this, e.g.:
https://code.google.com/p/android-maps-extensions/
https://github.com/twotoasters/clusterkraf
You could handle the click event of the (single) marker, and use it to update an element that sits outside of the Google Map canvas with the details about the people at that marker.
Giving offset will make the markers faraway when the user zoom in to max. So i found a way to achieve that. this may not be a proper way but it worked very well.
for loop markers
{
//create marker
let mapMarker = GMSMarker()
mapMarker.groundAnchor = CGPosition(0.5, 0.5)
mapMarker.position = //set the CLLocation
//instead of setting marker.icon set the iconView
let image:UIIMage = UIIMage:init(named:"filename")
let imageView:UIImageView = UIImageView.init(frame:rect(0,0, ((image.width/2 * markerIndex) + image.width), image.height))
imageView.contentMode = .Right
imageView.image = image
mapMarker.iconView = imageView
mapMarker.map = mapView
}
set the zIndex of the marker so that you will see the marker icon which you want to see on top, otherwise it will animate the markers like auto swapping.
when the user tap the marker handle the zIndex to bring the marker on top using zIndex Swap.
Inspired by the Geek's answer, I created a function. This function returns a new latitude longitude pair, nearby the original value, almost in a circular fashion. COORINDATE_OFFSET is adjustable based on the need.
private static final float COORDINATE_OFFSET = 0.000085f;
private ArrayList<LatLng> markerCoordinates = new ArrayList<>();
private int offsetType = 0;
private LatLng getLatLng(LatLng latLng) {
LatLng updatedLatLng;
if (markerCoordinates.contains(latLng)) {
double latitude = 0;
double longitude = 0;
if (offsetType == 0) {
latitude = latLng.latitude + COORDINATE_OFFSET;
longitude = latLng.longitude;
} else if (offsetType == 1) {
latitude = latLng.latitude - COORDINATE_OFFSET;
longitude = latLng.longitude;
} else if (offsetType == 2) {
latitude = latLng.latitude;
longitude = latLng.longitude + COORDINATE_OFFSET;
} else if (offsetType == 3) {
latitude = latLng.latitude;
longitude = latLng.longitude - COORDINATE_OFFSET;
} else if (offsetType == 4) {
latitude = latLng.latitude + COORDINATE_OFFSET;
longitude = latLng.longitude + COORDINATE_OFFSET;
}
offsetType++;
if (offsetType == 5) {
offsetType = 0;
}
updatedLatLng = getLatLng(new LatLng(latitude, longitude));
} else {
markerCoordinates.add(latLng);
updatedLatLng = latLng;
}
return updatedLatLng;
}
I really like the accepted answer, but I believe it uses to much code. Here's a variation in JavaScript, you can try it in you browser's console:
var positions = Array([46,-2],[46,-2],[46,-2],[46.3,-2.1]);
var used_locations = [];
for (let position of positions) {
while (used_locations.includes(position[0].toString() + position[1].toString())) {
let lat = position[0];
let lng = position[1] * 1.001;
position = [lat, lng];
}
used_locations.push(position[0].toString() + position[1].toString());
//add_marker(position);
}
console.log(used_locations);
Summary
I need to be able to move from my current mapview to a latitude/longitude span. I want this move to happen in an animated sort of way (similar to how animateTo works). I need to go from my current view, and zoom in or out to end up at the target latitude/longitude span. Below is the code I'm using to do this when I use zoomLevels. But I have to use spans, not zoom levels to accomplish this.
[Edit]
Here's what I'm doing for animateTo. I need the equivalent of this, but using Lat/Lng Span to end up at my final view (i need to be zoomed and centered, showing the lat/lng span provided). As you'll notice, i'm using targetZoomLevel and currentZoomLevel to determine how much zooming to do... I need to be using the lat/lng span instead.
public static void smoothZoom(final MapController controller,
final int currentZoomLevel, final int targetZoomLevel,
int latitude, int longitude) {
final Handler handler = new Handler();
controller.animateTo(new GeoPoint(latitude, longitude), new Runnable() {
public void run() {
int currentZoom = currentZoomLevel;
int zoomTarget = targetZoomLevel;
// Smooth zoom handler
// int zoomLevel = _mapView.getZoomLevel();
// int targetZoomLevel = targetLevel;
long delay = 0;
if (zoomTarget > currentZoom) {
while (currentZoom++ < zoomTarget) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
controller.zoomIn();
}
}, delay);
delay += 150; // Change this to whatever is good on the
// device
}
}
}
});
}
[Edit]
I really need this code for my application now, there is no way around it. First person to help me get working code gets 250 rep.
This has worked for me:
MapController.setCenter(GeoPoint);
You can get MapController from:
MapView.getController();
And get a GeoPoint from:
new GeoPoint( (int) ( LAT * 1e6 ) , (int) ( LONG * 1e6 ) );
Is this the kind of solution you're looking for?
public static void zoomSpan(final MapView mapView, int northernLat, int southernLat, int easternLon, int westernLon) {
final int latSpan = northernLat - southernLat;
final int lonSpan = easternLon - westernLon;
latCenter = southernLat + latSpan / 2;
lonCenter = westernLon + lonSpan / 2;
final MapController controller = mapView.getController();
final Handler handler = new Handler();
final Runnable zoomRunnable = new Runnable() {
public void run() {
int viewLatSpan = mapView.getLatitudeSpan();
int viewLonSpan = mapView.getLongitudeSpan();
boolean actionTaken = false;
if (mapView.getZoomLevel() > 1 && (latSpan > viewLatSpan || lonSpan > viewLonSpan)) {
controller.zoomOut();
actionTaken = true;
}
if (mapView.getZoomLevel() < mapView.getMaxZoomLevel() && latSpan * 2 <= viewLatSpan && lonSpan * 2 <= viewLonSpan) {
controller.zoomIn();
actionTaken = true;
}
if (actionTaken)
handler.post(zoomRunnable);
}
};
controller.animateTo(latCenter, lonCenter, zoomRunnable);
}
In my map application I needed to implement a location search feature. If location found it moves to that location, add an overlay and zooms to a predefined level.
I used following code for this purpose:
final int SEARCH_ZOOM_LEVEL = 7;
controller = mMapView.getController();
controller.setZoom(SEARCH_ZOOM_LEVEL );
pointToLocation(s); // s is the location
pointToLocation() was implements like this:
private void pointToLocation(String s) {
Geocoder gc = new Geocoder(MapDemo.this, Locale.getDefault());
try {
List<Address> addresses = gc.getFromLocationName(
s, 5);
String add = "";
if (addresses.size() > 0) {
point = new GeoPoint(
(int) (addresses.get(0).getLatitude() * 1E6),
(int) (addresses.get(0).getLongitude() * 1E6));
controller.animateTo(point);
}
} catch (IOException e) {
Toast.makeText(MapDemo.this, "Location not found", Toast.LENGTH_LONG).show();
}
}
The usage of the function you're looking at is zoomToSpan(int latSpanE6, int lonSpanE6).
You have the current span (or can get it easily) and the target span (as you said you know it), so why don't you simply start calling zoomToSpan on a delay , as you incrementally move latSpanE6 and longSpanE6 towards the target. It shouldn't be that difficult. You already have most of the code there, but instead of zoomIn while checking the zoom level, you would zoomToSpan while checking if latSpanE6 and longSpanE6 have reached your target span.
Won't bother coding it since you can do it yourself ;)
I have list of stores and when i click on any one depending on their latitude and longitude values which i get from a web server i show them on the map. some of them show up but some wont...i know this is not a good question....but i am just expecting if someone who might have experienced the same problem might help.
here is the code:
Double Storelat = (FeedListViewActivity.lat);
Double Storelng = (FeedListViewActivity.lng);
storeLocation = new GeoPoint((int) (Storelat * 1E6), (int) (Storelng * 1E6));
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, shadow);
Point locationPoint1 = new Point();
Projection projection1 = mapView.getProjection();
projection1.toPixels(storeLocation, locationPoint1);
Paint containerPaint = new Paint();
containerPaint.setAntiAlias(true);
int containerX1 = locationPoint1.x;
int containerY1 = locationPoint1.y;
if (shadow) {
containerX1 += CONTAINER_SHADOW_OFFSET;
containerY1 += CONTAINER_SHADOW_OFFSET;
containerPaint.setARGB(90, 0, 0, 0);
canvas.drawCircle(containerX1, containerY1, CONTAINER_RADIUS,
containerPaint);
} else {
containerPaint.setColor(Color.RED);
canvas.drawCircle(containerX1, containerY1, CONTAINER_RADIUS,
containerPaint);
}
}
and these are the values i get from the webserver:
working:
lat = 18.5170002
lng = 73.858078
not working:
lat = 18.618679
lng = 73.8037491
your answer is in your question itself
working:
lat = 18.5170002 lng = 73.858078
not working:
lat = 18.618679 lng = 73.8037491
as per above provided thing you can see the precision difference i have also faced the same problem be 6 months, and i have made that 6 precision to 8 precision and was working well then.
if you pass 18.618679 then it will treat it as 0.18618679 for confirmation try to zoom out up-to level 2 or 3 you will get your point at unexpected place.