I'm using Unity3D and I created simple distance, speed and acceleration calculator using latitude and longitude of last position. I'm calculating last distance, speed and acceleration in each GPS update (approximately once per second). But sometimes (2-3 second interval) latitude and longitude values changes rapidly (in clear weather and no obstacles). That's why speed and acceleration values gets unreal results. For example, at stable 40 km/h speed, speed value becomes 60 km/h and returns to 40 km/h within 2-3 second. I'm here to ask how can I avoid this inaccurate and rapid GPS data changes?
I'm using Nexus 5 device
There is my code:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class Manager : MonoBehaviour
{
public Text longitude, latitude, lonAText, latAText, lonBText, latBText;
public Text result, overallResult, speedText, lastTimeText, timerText, accelerationText, speed0Text;
float lonA, lonB, latA, latB, overallDistance, lastDistance, timer, lastTime, speed, speed0, acceleration;
bool firstTime, allowTimer;
public AudioSource audio;
void Awake()
{
overallDistance = 0;
lastDistance = 0;
timer = 0;
lastTime = 0;
speed = 0;
speed0 = 0;
firstTime = true;
allowTimer = true;
}
IEnumerator Start()
{
// First, check if user has location service enabled
if (!Input.location.isEnabledByUser)
yield break;
// Start service before querying location
Input.location.Start(1, 1);
// Wait until service initializes
int maxWait = 20;
while (Input.location.status == LocationServiceStatus.Initializing && maxWait > 0)
{
yield return new WaitForSeconds(1);
maxWait--;
}
// Service didn't initialize in 20 seconds
if (maxWait < 1)
{
print("Timed out");
yield break;
}
// Connection has failed
if (Input.location.status == LocationServiceStatus.Failed)
{
print("Unable to determine device location");
yield break;
}
else
{
// Access granted and location value could be retrieved
print("Location: " + Input.location.lastData.latitude + " " + Input.location.lastData.longitude + " " + Input.location.lastData.altitude + " " + Input.location.lastData.horizontalAccuracy + " " + Input.location.lastData.timestamp);
longitude.text = Input.location.lastData.longitude.ToString();
latitude.text = Input.location.lastData.latitude.ToString();
lonA = Input.location.lastData.longitude;
latA = Input.location.lastData.latitude;
}
// Stop service if there is no need to query location updates continuously
//Input.location.Stop();
}
void Update()
{
longitude.text = Input.location.lastData.longitude.ToString();
latitude.text = Input.location.lastData.latitude.ToString();
timer += Time.deltaTime;
timerText.text = timer.ToString();
if (lonA != Input.location.lastData.longitude || latA != Input.location.lastData.latitude)
{
audio.Play();
CalculateDistances(lonA, latA, Input.location.lastData.longitude, Input.location.lastData.latitude); // last distance and overall distanceS
lonA = Input.location.lastData.longitude;
latA = Input.location.lastData.latitude;
lastTime = timer;
lastTimeText.text = lastTime.ToString();
timer = 0;
speed0 = speed;
speed0Text.text = speed0.ToString();
CalculateSpeed();
CalculateAcceleration();
}
}
public static float Radians(float x)
{
return x * Mathf.PI / 180;
}
public void CalculateDistances(float firstLon, float firstLat, float secondLon, float secondLat)
{
lonAText.text = firstLon.ToString();
latAText.text = firstLat.ToString();
lonBText.text = secondLon.ToString();
latBText.text = secondLat.ToString();
float dlon = Radians(secondLon - firstLon);
float dlat = Radians(secondLat - firstLat);
float distance = Mathf.Pow(Mathf.Sin(dlat / 2), 2) + Mathf.Cos(Radians(firstLat)) * Mathf.Cos(Radians(secondLat)) * Mathf.Pow(Mathf.Sin(dlon / 2), 2);
float c = 2 * Mathf.Atan2(Mathf.Sqrt(distance), Mathf.Sqrt(1 - distance));
lastDistance = 6371 * c * 1000;
result.text = lastDistance.ToString() + " meters";
overallDistance += lastDistance; // bu 1 anliq 6.000.000-dan boyuk qiymet ala biler
StartCoroutine(Overall());
}
IEnumerator Overall()
{
if (firstTime)
{
firstTime = false;
yield return new WaitForSeconds(2);
if (overallDistance > 6000000)
{
overallDistance = 0;
lastDistance = 0;
}
}
overallDistance += lastDistance;
overallResult.text = overallDistance.ToString() + " meters";
}
void CalculateSpeed()
{
speed = lastDistance / lastTime * 3.6f;
speedText.text = speed.ToString();
}
void CalculateAcceleration()
{
acceleration = (speed - speed0) / lastTime;
accelerationText.text = acceleration.ToString();
}
}
Related
I have android TV app which will run for long time .And I have google map inside that but after 20-24 hours google map starts blinking. Following link contains video of issue.
https://www.dropbox.com/s/vp8pbqc5z4zopbz/20180611_095004.mp4?dl=0
Edit
I can't share the whole source code, but I am using two fragments. One fragment contains map and another contains a listview of images. I call a webservice every 10 seconds and update listview images and bound map with locations I get in response of web service.
if ( map!=null) {
if (activity.ambDetailList.size() > 0) {
int i;
double dist, currentLat = 0.0, currentLng = 0.0;
ambOnlineList.clear();
String ETA, SPEED;
Marker marker;
for (i = 0; i < activity.ambDetailList.size(); i++) {
ambulanceDetail = activity.ambDetailList.get(i);
dist = Math.sqrt(Math.pow(ambulanceDetail.getCurrentLat() - ambulanceDetail.getDestLat(), 2) + Math.pow(ambulanceDetail.getCurrentLng() - ambulanceDetail.getDestLng(), 2));
double minute = ((dist * 100) / (ambulanceDetail.getSpeed() * 60 / 1000));
double speed = ambulanceDetail.getSpeed() * 60 * 60 / 1000;
Log.i("Minute", "----->>>>" + minute);
if (speed >= 0.00 && speed <= 1.00) {
ETA = activity.getResources().getString(R.string.text_infinity);
SPEED = activity.getResources().getString(R.string.text_infinity);
} else if (minute > 60) {
double hour = minute / 60;
int roundHour = (int) (minute / 60);
int min = (int) ((hour - roundHour) * 60);
ETA = roundHour + " hour " + min + " min";
SPEED = (int) (ambulanceDetail.getSpeed() * 60 * 60 / 1000) + " km/h ";
} else {
ETA = (int) (minute) + " min ";
SPEED = (int) (ambulanceDetail.getSpeed() * 60 * 60 / 1000) + " km/h ";
}
ambulanceDetail.setEta(ETA);
ambulanceDetail.setDisplayETA(ETA);
ambulanceDetail.setDisplaySpeed(SPEED);
if (markerList.get(ambulanceDetail.getAmbulanceId()) == null) {
MarkerOptions markerOptions = createMarker(ambulanceDetail);
marker = map.addMarker(markerOptions);
markerList.put(ambulanceDetail.getAmbulanceId(), marker);
markerArrayList.add(marker);
detailMap.put(marker, ambulanceDetail);
} else {
marker = markerList.get(ambulanceDetail.getAmbulanceId());
if (marker.isVisible()) {
Location location = new Location("");
location.setLatitude(ambulanceDetail.getCurrentLat());
location.setLongitude(ambulanceDetail.getCurrentLng());
marker.setIcon(BitmapDescriptorFactory.fromBitmap(updateMarkerIcon(ambulanceDetail)));
marker.setPosition(new LatLng(ambulanceDetail.getCurrentLat(), ambulanceDetail.getCurrentLng()));
}
}
ArrayList<Integer> listId = new ArrayList<>();
for (int m = 0; m < activity.ambDetailList.size(); m++) {
listId.add(activity.ambDetailList.get(m).getAmbulanceId());
}
for (int k = 0; k < markerArrayList.size(); k++) {
Marker m = markerArrayList.get(k);
if (!listId.contains(detailMap.get(m).getAmbulanceId())) {
markerArrayList.remove(m);
markerList.remove(detailMap.get(m).getAmbulanceId());
detailMap.remove(m);
m.remove();
}
}
}
if (markerList.size() == 1 && activity.ambDetailList.size() == 1) {
map.moveCamera(CameraUpdateFactory.newLatLngZoom(markerList.get(activity.ambDetailList.get(0).getAmbulanceId()).getPosition(), 16));
} else {
boundLatLang();
}
if (isFirstCall) {
map.setBuildingsEnabled(true);
isFirstCall = false;
}
} else {
if (markerArrayList.size() > 0) {
for (int i = 0; i < markerArrayList.size(); i++) {
Marker marker = markerArrayList.get(i);
marker.remove();
}
markerList.clear();
markerArrayList.clear();
}
map.clear();
if (detailMap.size() > 0)
detailMap.clear();
}
}
Anyway, you can restart application (or if that didn't helps - reboot device) before map starts blinking, for example after 15 hours of work (or e.g. 500 calls of web service):
...
Intent restartIntent = getBaseContext().getPackageManager()
.getLaunchIntentForPackage( getBaseContext().getPackageName() );
restartIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(restartIntent);
...
can i computing the velocity with the accelerations from the sensor which type is SENSOR_TYPE_ACCELEROMETER?
or can i get the orientation of the the accelerations from the sensor which type is SENSOR_TYPE_ACCELEROMETER?
i have seen some blogs about checking the value of the vector of accelerations to detect whether the android phone is filling down or not.
but ,the reson may be the android phone is shaking and is not filling.
my code is:
/**
* how often analysising the accelerometer?(Millisecond)
*/
private static final int ANALYSIS_ACCELERATION_INTERVAL = 500;
/**
* how often checking the accelerometer?(Millisecond)
*/
private static final int CHECK_TIME_INTERVAL = 5000;
/**
* when the sMinorMaxAccelerationInOneSecoud[0] is less than
* ACCELERATION_CRITICAL_VALUE[0],and the
* sMinorMaxAccelerationInOneSecoud[1] is more than
* ACCELERATION_CRITICAL_VALUE[1],i think the phone dropped to the
* ground.
*/
private static float ACCELERATION_CRITICAL_VALUE[] = {2, 40};
/**
* during{#link #ANALYSIS_ACCELERATION_INTERVAL},the min accelerometer
* of the phone is sMinorMaxAccelerationInOneSecoud[0],the max
* accelerometer of the phone is saved at
* sMinorMaxAccelerationInOneSecoud[1].
*/
private static float sMinorMaxAccelerationInOneSecoud[] = {0, 0};
/**
* the last time when checking the accelerometer
*/
private static long sLastCheckTime = 0;
/**
* the last time when analysising the accelerometer
*/
private static long sLastAnalysisTime = 0;
/**
* during {#link #CHECK_TIME_INTERVAL},is phone dropped to ground?
*/
private static boolean sIsFallDown = false;
private SensorManager mSensorManager;
public void receiveCondition() {
// a dialog
Intent intent = new Intent();
intent.setClass(GlobalHolder.getApplicationContext(), PhoneDroppedDialog.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
GlobalHolder.getApplicationContext().startActivity(intent);
}
public void start() {
mSensorManager = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION),
SensorManager.SENSOR_DELAY_UI);
}
public void stop() {
mSensorManager.unregisterListener(this);
mSensorManager = null;
sMinorMaxAccelerationInOneSecoud = new float[]{0, 0};
sLastCheckTime = 0;
sLastAnalysisTime = 0;
}
#Override
public void onSensorChanged(SensorEvent event) {
int sensorType = event.sensor.getType();
if (sensorType != Sensor.TYPE_LINEAR_ACCELERATION) {
return;
}
// judge if the sMinorMaxAccelerationInOneSecoud[0] is less than
// ACCELERATION_CRITICAL_VALUE[0],and the
// sMinorMaxAccelerationInOneSecoud[1] is more than
// ACCELERATION_CRITICAL_VALUE[1]
analysisAcceleration(event.values);
long currentTime = System.currentTimeMillis();
long beta = currentTime - sLastCheckTime;
if (Math.abs(beta) < CHECK_TIME_INTERVAL) {
return;
}
// it have been more than one secound since the last check,so we need re-check if the phone have dropped to the ground.
checkIsFallDown();
// reset sLastCheckTime with the current time
sLastCheckTime = currentTime;
}
private void analysisAcceleration(float[] values) {
if (sIsFallDown) {
// if the phone have already dropped to the ground,we return directly.
return;
}
// whether the min sum vector of the accelerations on the x_axis,y_axis,zaxis is less than ACCELERATION_CRITICAL_VALUE[0]
// and the max sum vector of the accelerations on the x_axis,y_axis,zaxis is more than ACCELERATION_CRITICAL_VALUE[1]
if (sLastAnalysisTime == 0) {
sLastAnalysisTime = System.currentTimeMillis();
}
// the sum vector of the accelerations on the x_axis,y_axis,zaxis
float currentAcceleration = 0f;
for (int i = 0; i < values.length; i++) {
currentAcceleration += (values[i] * values[i]);
}
currentAcceleration = (float) Math.sqrt(currentAcceleration);
if (sMinorMaxAccelerationInOneSecoud[0] == 0 || sMinorMaxAccelerationInOneSecoud[0] > currentAcceleration) {
sMinorMaxAccelerationInOneSecoud[0] = currentAcceleration;
}
if (sMinorMaxAccelerationInOneSecoud[1] == 0 || sMinorMaxAccelerationInOneSecoud[1] < currentAcceleration) {
sMinorMaxAccelerationInOneSecoud[1] = currentAcceleration;
}
long currentTime = System.currentTimeMillis();
long delta = currentTime - sLastAnalysisTime;
if (delta < ANALYSIS_ACCELERATION_INTERVAL) {
return;
}
MLog.d(TAG, "during ANALYSIS_ACCELERATION_INTERVAL, the min value is " + sMinorMaxAccelerationInOneSecoud[0] + ", the max value is " + sMinorMaxAccelerationInOneSecoud[1]);
boolean isLessPre = sMinorMaxAccelerationInOneSecoud[0] < ACCELERATION_CRITICAL_VALUE[0];
boolean isMoreNext = sMinorMaxAccelerationInOneSecoud[1] > ACCELERATION_CRITICAL_VALUE[1];
sIsFallDown = isLessPre && isMoreNext;
sLastAnalysisTime = currentTime;
sMinorMaxAccelerationInOneSecoud = new float[]{0, 0};
}
private void checkIsFallDown() {
if (sIsFallDown) {
// the phone have dropped
receiveCondition();
MLog.d(TAG, "call the receiveCondition method.");
sMinorMaxAccelerationInOneSecoud = new float[]{0, 0};
sIsFallDown = false;
}
}
someone has a better idea?thanks!!
I am facing an issue right now I am developing an Android app similar to Uber or lyft but the problem that I am facing is the car is moving on the map smoothly but sometimes it is revolving at 360 degree at one position, I need to stop that please help, here is my animation code.
private void animateMarkr(double laat, double lnng,Location prevLocation)
{
final LatLng toPosition=new LatLng(laat,lnng);
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = mGoogleMap.getProjection();
Point startPoint = proj.toScreenLocation(carMarker.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final long duration = 3000;
final boolean hideMarker=false;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
LatLng pre=carMarker.getPosition();
float t = interpolator.getInterpolation((float) elapsed / duration);
double lng = t toPosition.longitude + (1 - t) startLatLng.longitude;
double lat = t toPosition.latitude + (1 - t) startLatLng.latitude;
// carMarker.setRotation(getBearing(pre,new LatLng(lat,lng)));
carMarker.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 20);
} else {
if (hideMarker) {
carMarker.setVisible(false);
} else {
carMarker.setVisible(true);
}
}
}
});
}
Here is my Rotation Code
private void rotateMarker(Location location,Location preLocation){
Location prevLocation = new Location(LocationManager.GPS_PROVIDER);
prevLocation.setLatitude(carMarker.getPosition().latitude);
prevLocation.setLongitude(carMarker.getPosition().longitude);
final float toRotation = prevLocation.bearingTo(location);
performRotaion(toRotation);
}
private void performRotaion(final float toRotation){
if(!isMarkerRotating) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final float startRotation = carMarker.getRotation();
final float totalDegree=0;
final long duration = 1000;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable() {
#Override
public void run() {
isMarkerRotating = true;
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed / duration);
float rot = t toRotation + (1 - t) startRotation;
if(carMarker != null){
carMarker.setAnchor(0.5f, 0.5f);
// if(rot<0.0f && rot>-355.0f) {
carMarker.setRotation(-rot > 180 ? rot / 2 : rot);
carMarker.setFlat(true);
}
if (t < 1.0) {
handler.postDelayed(this, 16);
} else {
isMarkerRotating = false;
}
}
});
}
}
// method call
rotateMarker(location,previcsLocation);
animateMarkr(location.getLatitude(), location.getLongitude(),previcsLocation);
I have implemented the check to stop rotation if the degree is 360 also I have checked if the degree is more than 180 degree than divide the same and move the car on that angle. But nothing works
Before calling these functions
rotateMarker(location,previcsLocation);
animateMarkr(location.getLatitude(), location.getLongitude(),previcsLocation);
in your code. You'll have to add a check for the new (Latitude,Logitude) which comes through the server and marker's (Latitude,Logitude) if both values are same then you should avoid to call the above functions.
Only you have to add this condition in it.
LatLng new_location = new LatLng(data.getLatitude(), data.getLongitude());
//new_location is coming through the server
LatLng current_location = marker.getPosition();
//this is the marker's location
if (current_location != new_location) {
if (current_location.equals(new_location)) {
Log.d(TAG, "LatLng " + "---------current_location-" + current_location);
Log.d(TAG, "LatLng " + "---------new_location-" + new_location);
return;
}
rotateMarker(location,previcsLocation);
animateMarkr(location.getLatitude(), location.getLongitude(),previcsLocation);
}
It worked in my project.
I'm writing Fall Detection application as a project for studies. I'm using accelerometer to detect impacts such as falls, jumps, and shakes, then I'm using barometer to check the height. If the height is bigger then 0.5 m between standing posture and lying posture after impact, it should detect it as fall.
I have a problem with saving barometer's pressure value before Impact from accelerometer and compare it with current pressure to see the difference.
Now I'm saving pressure value when Impact is detected and then later when currentstate is active or inactive. I need this to check the height and to see if the fall has really happend. Here is the code:
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
double ax = event.values[0];
double ay = event.values[1];
double az = event.values[2];
double acceleration = getMagnitude(ax, ay, az);
pushAcceleration(acceleration);
setKinematicState(accelerationFrame, ay);
/*
Checking Barometers Height in case of Falling
*/
if (currentState != previousState) {
if (currentState == KinematicState.FALL) {
PressuredataObject barometer;
barometer = pressuredataObjects.get(pressuredataObjects.size() - 1);
beforeFallAltitude = SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, barometer.getAirPressure());
impactDetected = true;
}
if ((currentState == KinematicState.INACTIVE || currentState == KinematicState.ACTIVE) && impactDetected) {
PressuredataObject barometer;
barometer = pressuredataObjects.get(pressuredataObjects.size() - 1);
afterFallAltitude = SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, barometer.getAirPressure());
float diffAltitude = beforeFallAltitude - afterFallAltitude;
float abs_diffAltitude = (diffAltitude < 0) ? -diffAltitude : diffAltitude;
if (abs_diffAltitude > 0.5) {
Log.d("abs_diffAltitude", "" + abs_diffAltitude);
state.setText("FALL DETECTED");
state.setTextColor(Color.parseColor("#FF0000"));
fallPlayer.start();
showDialog();
}
impactDetected = false;
}
systemState(currentState);
previousState = currentState;
}
}
if (event.sensor.getType() == Sensor.TYPE_PRESSURE) {
float[] values = event.values;
pressuredataObjects.add(new PressuredataObject(values[0], 0f, System.currentTimeMillis()));
if (pressuredataObjects.size() > BUFF_SIZE)
pressuredataObjects.remove(0);
PressuredataObject lastMeasure = pressuredataObjects.get(pressuredataObjects.size() - 1);
PressuredataObject medianValue = PressureUtilities.selectMedianValue(pressuredataObjects);
// Calculate speed and altitude
float speed = 0f;
float altitude = 0f;
if (pdoPrevious == null) {
medianValue.setSpeed(0);
} else {
speed = lastMeasure.getSpeed();
altitude = SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, lastMeasure.getAirPressure());
}
pdoPrevious = medianValue;
}
}
And here you can see the functions for accelerometer
private void setKinematicState(List<Double> buffer, double accelerationY) {
int zrc = getMagnitudeValues(buffer);
if (zrc <= 3) {
currentState = KinematicState.INACTIVE;
} else if (zrc > 3 && zrc < 6) {
currentState = KinematicState.ACTIVE;
}
else if (zrc > 6) {
currentState = KinematicState.FALL;
}
}
private int getMagnitudeValues(List<Double> accelerationFrame) {
int count = 0;
for (int i = 1; i < accelerationFrame.size(); i++) {
if ((accelerationFrame.get(i - 1) - GRAVITY_ACC) < APPROXIMATION_ERROR
&& (accelerationFrame.get(i) - GRAVITY_ACC) > APPROXIMATION_ERROR) {
count++;
}
}
return count;
}
private double getMagnitude(double accelerationX, double accelerationY, double accelerationZ) {
return Math.sqrt(accelerationX * accelerationX
+ accelerationY * accelerationY + accelerationZ * accelerationZ);
}
private void pushAcceleration(double acceleration) {
accelerationFrame.add(acceleration);
if (accelerationFrame.size() > BUFF_SIZE)
accelerationFrame.remove(0);
}
Does anyone has any idea how to save previous pressure value just before the impact from accelerometer?
We can set the surrounding area for particular location on map in iPhone as following
CLLocationCoordinate2D coord = {latitude:37.09024, longitude:-95.712891};
CLLocationDistance latitudinalMeters;
latitudinalMeters =NoOfMiles * 1609.344;
CLLocationDistance longitudinalMeters;
longitudinalMeters = NoOfMiles * 1609.344;
mapViewHome.region = MKCoordinateRegionMakeWithDistance(coord, latitudinalMeters, longitudinalMeters);
Is there any equivalent method for Android?
This code is not production quality. Use Chris suggestion from comments here instead: https://issuetracker.google.com/issues/35823607#comment4
This question was originally asked for Maps API v1. This answer is for v2, but can be easily changed to v1, so...
No easy way to do it.
You may want to request this feature on gmaps-api-issues.
As waiting for this to be implemented on Google side can take several months, so this is what I would do:
private static final double ASSUMED_INIT_LATLNG_DIFF = 1.0;
private static final float ACCURACY = 0.01f;
public static LatLngBounds boundsWithCenterAndLatLngDistance(LatLng center, float latDistanceInMeters, float lngDistanceInMeters) {
latDistanceInMeters /= 2;
lngDistanceInMeters /= 2;
LatLngBounds.Builder builder = LatLngBounds.builder();
float[] distance = new float[1];
{
boolean foundMax = false;
double foundMinLngDiff = 0;
double assumedLngDiff = ASSUMED_INIT_LATLNG_DIFF;
do {
Location.distanceBetween(center.latitude, center.longitude, center.latitude, center.longitude + assumedLngDiff, distance);
float distanceDiff = distance[0] - lngDistanceInMeters;
if (distanceDiff < 0) {
if (!foundMax) {
foundMinLngDiff = assumedLngDiff;
assumedLngDiff *= 2;
} else {
double tmp = assumedLngDiff;
assumedLngDiff += (assumedLngDiff - foundMinLngDiff) / 2;
foundMinLngDiff = tmp;
}
} else {
assumedLngDiff -= (assumedLngDiff - foundMinLngDiff) / 2;
foundMax = true;
}
} while (Math.abs(distance[0] - lngDistanceInMeters) > lngDistanceInMeters * ACCURACY);
LatLng east = new LatLng(center.latitude, center.longitude + assumedLngDiff);
builder.include(east);
LatLng west = new LatLng(center.latitude, center.longitude - assumedLngDiff);
builder.include(west);
}
{
boolean foundMax = false;
double foundMinLatDiff = 0;
double assumedLatDiffNorth = ASSUMED_INIT_LATLNG_DIFF;
do {
Location.distanceBetween(center.latitude, center.longitude, center.latitude + assumedLatDiffNorth, center.longitude, distance);
float distanceDiff = distance[0] - latDistanceInMeters;
if (distanceDiff < 0) {
if (!foundMax) {
foundMinLatDiff = assumedLatDiffNorth;
assumedLatDiffNorth *= 2;
} else {
double tmp = assumedLatDiffNorth;
assumedLatDiffNorth += (assumedLatDiffNorth - foundMinLatDiff) / 2;
foundMinLatDiff = tmp;
}
} else {
assumedLatDiffNorth -= (assumedLatDiffNorth - foundMinLatDiff) / 2;
foundMax = true;
}
} while (Math.abs(distance[0] - latDistanceInMeters) > latDistanceInMeters * ACCURACY);
LatLng north = new LatLng(center.latitude + assumedLatDiffNorth, center.longitude);
builder.include(north);
}
{
boolean foundMax = false;
double foundMinLatDiff = 0;
double assumedLatDiffSouth = ASSUMED_INIT_LATLNG_DIFF;
do {
Location.distanceBetween(center.latitude, center.longitude, center.latitude - assumedLatDiffSouth, center.longitude, distance);
float distanceDiff = distance[0] - latDistanceInMeters;
if (distanceDiff < 0) {
if (!foundMax) {
foundMinLatDiff = assumedLatDiffSouth;
assumedLatDiffSouth *= 2;
} else {
double tmp = assumedLatDiffSouth;
assumedLatDiffSouth += (assumedLatDiffSouth - foundMinLatDiff) / 2;
foundMinLatDiff = tmp;
}
} else {
assumedLatDiffSouth -= (assumedLatDiffSouth - foundMinLatDiff) / 2;
foundMax = true;
}
} while (Math.abs(distance[0] - latDistanceInMeters) > latDistanceInMeters * ACCURACY);
LatLng south = new LatLng(center.latitude - assumedLatDiffSouth, center.longitude);
builder.include(south);
}
return builder.build();
}
Usage:
LatLngBounds bounds = AndroidMapsExtensionsUtils.boundsWithCenterAndLatLngDistance(new LatLng(51.0, 19.0), 1000, 2000);
map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
Notes:
this code has not been fully tested, may not work for edge cases
you may want to adjust private constants to have it execute faster
you may remove 3rd part where LatLng south is calculated and do it like for longitudes: this will be accurate for small values of latDistance (guessing you will not see a difference under 100km)
code is ugly, so feel free to refactor
While the above answer might work, it does not really look straight forward as the author already mentioned. Here is some code that works for me. Please note the code assumes the earth is a perfect sphere.
double latspan = (latMeters/111325);
double longspan = (longMeters/111325)*(1/ Math.cos(Math.toRadians(location.latitude)));
LatLngBounds bounds = new LatLngBounds(
new LatLng(location.latitude-latspan, location.longitude-longspan),
new LatLng(location.latitude+latspan, location.longitude+longspan));