I'm trying to create an android app that gives accurate location (1m or less) indoors. It seems the recommended way is to use the
a FusedLocationProviderClient
However the current horizontal accuracy I'm getting is between 5m and 15m for longitude and latitude while the vertical accuracy is often up to 50m out.
Not sure if it makes a difference but the the test device is an HTC U11?
I've updated google play service location com.google.android.gms:play-services-location:17.0.0
I've included android.permission.ACCESS_FINE_LOCATION in my app manifest and locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); in the location request.
I've got some code in there which turns the GPS on.
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(3 * 1000); // 3 seconds
locationRequest.setFastestInterval(1 * 1000); // 3 seconds
new GpsUtils(this).turnGPSOn(new GpsUtils.onGpsListener() {
#Override
public void gpsStatus(boolean isGPSEnable) {
// turn on GPS
isGPS = isGPSEnable;
}
});
locationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null) {
return;
}
for (Location location : locationResult.getLocations()) {
if (location != null) {
wayLatitude = location.getLatitude();
wayLongitude = location.getLongitude();
Lat.setText(Double.toString(location.getLatitude()));
Longs.setText(Double.toString(location.getLongitude()));
Acc.setText(String.format("%.2f", location.getAccuracy()));
if(location.hasAltitude()) {
Alt.setText(String.format("%.2f",location.getAltitude() - 108));
VAcc.setText(String.format("%.2f",location.getVerticalAccuracyMeters()));
}
else {
Alt.setText("Lost");
VAcc.setText("Lost");
}
SendToBackend(wayLatitude,wayLongitude, location);
if (!isContinue && mFusedLocationClient != null) {
mFusedLocationClient.removeLocationUpdates(locationCallback);
}
}
}
}
};
According to google it should be possible to get below 1m accuracy indoors. a link.
Thanks in advance :-)
Related
So I'm making this app which finds restaurants near you, fetching information from a food-delivery app, using JSoup library.
The only problem with it is that sometimes the latitude and the longitude are getting null value.
Situations in which my application is working:
-turning on GPS and the waiting at least 1-2 minutes;
-opening google maps, closing it, and then returning to the application;
So the main problem: I can't fetch the location right after I enable it and hit the 'Find restaurants' button, I need to wait 1-2 minutes after enabling location, then it's working.
private TextView result;
private FusedLocationProviderClient client;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestPermission();
client = LocationServices.getFusedLocationProviderClient(this);
getBtn = findViewById(R.id.getRestaurants);
result = findViewById(R.id.restaurantsList);
getBtn.setOnClickListener(this);
}
private void requestPermission(){
ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION}, 1 );
}
public void onClick(View v) {
if (ActivityCompat.checkSelfPermission(MainActivity.this, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
return;
}
client.getLastLocation().addOnSuccessListener(MainActivity.this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
result.setText("Getting location...");
if(location != null){
double latitude = getLat(location);
double longitude = getLng(location);
result.setText("Finding restaurants near you...");
getWebsite(latitude, longitude);
}else{
result.setText("Couldn't fetch location!");
}
}
});
Here is a good way to implement the FusedLocationProvider in Kotlin (you might adapt to Java or use Java and Kotlin side by side) :
private fun startLoc() {
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
try {
fusedLocationClient.lastLocation
.addOnSuccessListener { location: Location? ->
//showDialog(this#MapsActivity, TAG, "last know loc = ${location?.latitude} + ${location?.longitude}")
if (location != null){
lastKnowLoc = LatLng(location.latitude, location.longitude)
addMarkerToLocation(lastKnowLoc)
}
}
val locationRequest = LocationRequest()
locationRequest.interval = 10000
locationRequest.fastestInterval = 10000
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
for (location in locationResult.locations){
//showDialog(this#MapsActivity, "locationResult", "location=${location.latitude};${location.longitude}")
addMarkerToLocation(LatLng(location.latitude, location.longitude))
val speed = location.speed
updateCamera(location.bearing)
}
}
}
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null)
btnStartLoc.isEnabled = false
btnStopLoc.isEnabled = true
}catch (e:SecurityException){}
}
you must use requestLocationUpdates()
you are using getLastLocation() and when the GPS is off and turned on after the last known location becomes null so you must call requestLocationUpdates()
you can find more information in the below link
https://developer.android.com/training/location/receive-location-updates
In the documentation it has been stated clearly that LocationListener "Used for receiving notifications from FusedLocationProviderApi when the location has changed". On the other hand FusedLocationProviderApi has been marked as deprecated and they recommended to use FusedLocationProviderClient. Then, how to detect when a location has changed when using FusedLocationProviderClient?.
I need that because if I only use the requestLocationUpdate callback, it will update the value regardless of whether the location value changes or not.
I only need changes in location values. That way, it won't waste if I update the location value to the database. In short, updating the location values only that location changes. instead of updating location values based on time span.
The Code:
private void createLocationCallback() {
mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
//every 5 seconds
mLastLocation = locationResult.getLastLocation();
mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
updateUI();//need onLocationChanged
}
};
}
private void startLocationUpdates(){
Log.e(TAG, "startLocationUpdate");
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSIONS_REQUEST_FINE_LOCATION);
}
return;
}
mFusedLocationClient.requestLocationUpdates(mLocationRequest,mLocationCallback, Looper.myLooper());
}
In your LocationRequest, you can use setSmallestDisplacement.
Example
locationRequest.setSmallestDisplacement(10) // 10 meters. location callback only fired when location change more than 10 metter
Another thing is the location accuracy can not 100% so sometime you put don't move the phone but location still change.
It is OK when I open the GPS long times, I can get my current location, but when I turn off and then turn on the GPS, it returns null as the current location. I need to refresh or reinstall the app to make the current location work. Is it possible to get the location after a few seconds of turning on the GPS?
Here is my code:
client = LocationServices.getFusedLocationProviderClient(this);
LocationManager locationManager = (LocationManager)getSystemService(LOCATION_SERVICE);
if(!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
finish();
} else {
if (ActivityCompat.checkSelfPermission(MapsActivity.this, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ) {
return;
}
client.getLastLocation().addOnSuccessListener(MapsActivity.this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
if(location!= null){
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
});
}
I found out that I need to use requestsLoctionUpdates, but I cannot find any tutorial that explains me how to do it in the new version of android studio (Location : 11.8.0). Can anyone can tell me how to use this code?
Im facing some problem with my application, specifically with route plot/draw on my google maps. Ive made test route around my house and found out, that GPS providers are not as accurate as similar applications like runtastic or endomondo.
Sometimes Location listener makes incomprehensible changes on my map and the polyline then draws any lines on the map near my location even with perfect GPS signal.
Some other time, it just doesnot work. It does not listen to location change.
Can anybody explain me (like Im five) how does other fitness application get their current position and the plot route onto the google map? Thanks!
//Map Fragment
map = ((MapFragment) getFragmentManager().findFragmentById(R.id.fragment1)).getMap();
map.setMyLocationEnabled(true);
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
Criteria criteria = new Criteria();
String provider = locationManager.getBestProvider(criteria, true);
//Permission gain
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION},
MY_PERMISSION_ACCESS_COURSE_LOCATION);
return;
}
isLocationEnabled(getApplicationContext());
Location myLocation = locationManager.getLastKnownLocation(provider);
if (myLocation != null) {
latitude = myLocation.getLatitude();
longitude = myLocation.getLongitude();
float zoom = (float) 17.0;
map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(latitude, longitude), zoom));
Log.e("TAG", "GPS is on");
Toast.makeText(MainActivity.this, "latitude:" + latitude + " longitude:" + longitude, Toast.LENGTH_SHORT).show();
} else {
locationManager.requestLocationUpdates(provider, 5000, 0, this);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 10, this);
}
public void onLocationChanged(Location mylocation) {
if (lastLocationloc == null) {
lastLocationloc = mylocation;
}
LatLng lastLatLng = locationToLatLng(lastLocationloc);
LatLng thisLatLng = locationToLatLng(mylocation);
map.addPolyline(new PolylineOptions().add(lastLatLng).add(thisLatLng).width(6).color(Color.RED));
lastLocationloc = mylocation;
Toast.makeText(MainActivity.this, "!!!!Location CHANGE!!!!", Toast.LENGTH_SHORT).show();
}
public static LatLng locationToLatLng(Location loc) {
if (loc != null)
return new LatLng(loc.getLatitude(), loc.getLongitude());
return null;
}
For example, I want to plot it like this:
But my application works its own way..
First of all, you need to take into account the accuracy of the received location. You can get the accuracy using the Location.getAccuracy() method (documentation). The accuracy is measured in meters, so the lower the better:
if (location.getAccuracy() < MINIMUM_ACCURACY) {
// Add the new location to your polyline
}
You can set your MINIMUM_ACCURACY to be 10 meter for example.
On the other hand, you may want to add a new location to your polyline only if your new location is farther than a given distance to your last added location. As an example:
private static final float MINIMUM_ACCURACY = 10;
private static final float MINIMUM_DISTANCE_BETWEEN_POINTS = 20;
private Location lastLocationloc;
// ...
public void onLocationChanged(Location mylocation) {
if (mylocation.getAccuracy() < MINIMUM_ACCURACY) {
if (lastLocationloc == null || lastLocationloc.distanceTo(mylocation) > MINIMUM_DISTANCE_BETWEEN_POINTS) {
// Add the new location to your polyline
lastLocationloc = mylocation;
}
}
}
I am developing an indoor map app for my college. I have stored the longitude and latitude for each place in the map in the database. The next step is getting the user's location from the GPS and comparing with the database to be able to give the user the name of their location. My problem is with the comparing step - how could I do it? The GPS tracking works perfectly.
The class for the GPS tracking:
public class GPSTracker {
public GPSTracker(Context context) {
this.mContext = context;
getLocation();
}
public Location getLocation() {
try {
locationManager = (LocationManager) mContext
.getSystemService(LOCATION_SERVICE);
// getting GPS status
isGPSEnabled = locationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
// getting network status
isNetworkEnabled = locationManager
.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (!isGPSEnabled && !isNetworkEnabled) {
// no network provider is enabled
} else {
this.canGetLocation = true;
if (isNetworkEnabled) {
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
Log.d("Network", "Network");
if (locationManager != null) {
location = locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
}
// if GPS Enabled get lat/long using GPS Services
if (isGPSEnabled) {
if (location == null) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
Log.d("GPS Enabled", "GPS Enabled");
if (locationManager != null) {
location = locationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return location;
}
/**
* Stop using GPS listener
* Calling this function will stop using GPS in your app
* */
public void stopUsingGPS(){
if(locationManager != null){
locationManager.removeUpdates(GPSTracker.this);
}
}
/**
* Function to get latitude
* */
public double getLatitude(){
if(location != null){
latitude = location.getLatitude();
}
// return latitude
return latitude;
}
/**
* Function to get longitude
* */
public double getLongitude(){
if(location != null){
longitude = location.getLongitude();
}
// return longitude
return longitude;
}
/**
* Function to check GPS/wifi enabled
* #return boolean
* */
public boolean canGetLocation() {
return this.canGetLocation;
}
/**
* Function to show settings alert dialog
* On pressing Settings button will lauch Settings Options
* */
public void showSettingsAlert(){
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
// Setting Dialog Title
alertDialog.setTitle("GPS is settings");
// Setting Dialog Message
alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");
// On pressing Settings button
alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int which) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(intent);
}
});
// on pressing cancel button
alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
}
#Override
public void onLocationChanged(Location location) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
}
The code to display tracking in MainActivity::
// check if GPS enabled
if(gps.canGetLocation()){
double latitude = gps.getLatitude();
double longitude = gps.getLongitude();
// \n is for new line
Toast.makeText(getApplicationContext(), "Your Location is - \nLat: " + latitude + "\nLong: " + longitude, Toast.LENGTH_LONG).show();
} else {
// can't get location
// GPS or Network is not enabled
// Ask user to enable GPS/network in settings
gps.showSettingsAlert();
}
As you told you have already made collection of database in which all the places of you collage Lat and Long. Now any user who is near to canteen of your collage open your app now
app should find the nearest place where user standing right now.
I assuming above requirement of your collage.
For the above requirement the best way is Haversine formula. check this wiki link : Link
and make some R&D on it. Now by using this formula technical flow is like below :
1 : Get user's lat and long using GPS.
2 : Send this GSP Co-ordinates to your php web service where you have implemented Haversin formula. By using this formula you will find the nearest place from you current place from your database.
3 : Now you have nearest place from your existing place.
For use Haversin Formula in php web service please refer this links : Link
Hope you get what you want to do. Please do some R&D on it and I am sure that you will solve it.
You compare two Lat Lon coordinates, where at least one is not perfect because measured by e.g GPS, by calculating the distance in meters inbetween them.
If the distance is lower than a specific threshold, you can consider them as matching.
Android has a method for that distance calculation.