web-dev-qa-db-ja.com

Google Play Service Location APIが時々間違った場所を返す

注文時に、Geotag土地などのトランザクションごとに、Google Play Service Location APIを使用してユーザーの位置をキャプチャするアプリがあります。

これらの場所をモバイルからキャプチャした後、サーバーに同期してWebダッシュボードに表示します。

数秒以内にキャプチャされたトランザクションがさまざまな距離のロケーション結果を持っていることに気づきました。



ユーザーの場所は

Mandsaur、Madhya Pradesh、インド、アジア
(緯度-24.057291、経度-75.0970672、取得日-2017-01-04 09:19:48)。

しかし、後続のトランザクションは、

パリ 6772 km先
(緯度-48.8581074、経度-2.3525187、取得日-2017-01-04 09:20:01


そのフェッチが誤って記述されているインドのように、ユーザーはgujratから来て、Bihar、Maharastra、クウェート(インド以外)の場所のフェッチのように、インドの開発者にとって本当に頭痛の種です


これは、ユーザーの干渉なしで発生し、ユーザーのデバイスにインストールされたモックロケーションアプリはありません。

なぜこれが起こるのか、そしてどのようにしてこれらのシナリオを回避できるのか誰でも説明できますか?


注:
これらの取引場所は通常、GPSがオンになっていて高精度モードに設定されたオープンフィールドでキャプチャされます

34

APIから取得する場所には、メートル単位の精度があります。また、場所の古さも確認する必要があります。

https://developer.Android.com/reference/Android/location/Location.html#getAccuracy()

https://developer.Android.com/reference/Android/location/Location.html#getTime()

精度が50メートルまたは100メートルを超える場合、一般に場所は破棄されます。


なぜこれが起こるのですか?

デバイスのGPSが衛星を見つけて信号を取得するまでには時間がかかります。また、APIはネットワークに基づいて現在地を特定しようとします。 GPSが正確なデータを提供するまでは、ネットワークとGPSの間をジャンプするようなものです。


これを回避する方法は?

ロケーションリスナーで、精度を確認し、精度が上がるまで待ちます。

10
Froyo

公式のAndroid開発者サイトには、成功のための Location Strategies 専用のページがありますAndroidアプリ。詳細については、公式ドキュメントには次のように記載されています...

最新の位置修正が最も正確であると期待するかもしれません。ただし、ロケーション修正の精度はさまざまであるため、最新の修正が常に最良であるとは限りません。いくつかの基準に基づいて位置フィックスを選択するためのロジックを含める必要があります。基準は、アプリケーションとフィールドテストのユースケースによっても異なります。

ロケーションフィックスの正確性を検証するために実行できるいくつかの手順を次に示します。

  • 取得した場所が以前の見積もりより大幅に新しいかどうかを確認します。
  • 場所によって要求された精度が以前の推定よりも良いか悪いかを確認します。
  • 新しい場所の提供元を確認し、さらに信頼できるかどうかを判断します。

また、アプリに基本的な カルマンフィルター を実装して、ユーザーの位置の推定値を維持および更新することも検討してください。幸運を。

4
v1bri

グーグルロケーション融合APIの場合、以下のコードを使用できます

package com.hydrometcloud.location;

import Android.app.Activity;
import Android.content.Context;
import Android.content.IntentSender;
import Android.location.Location;
import Android.os.Bundle;
import Android.support.annotation.NonNull;
import Android.support.annotation.Nullable;
import Android.util.Log;

import com.google.Android.gms.common.ConnectionResult;
import com.google.Android.gms.common.api.GoogleApiClient;
import com.google.Android.gms.common.api.PendingResult;
import com.google.Android.gms.common.api.ResultCallback;
import com.google.Android.gms.common.api.Status;
import com.google.Android.gms.location.LocationListener;
import com.google.Android.gms.location.LocationRequest;
import com.google.Android.gms.location.LocationServices;
import com.google.Android.gms.location.LocationSettingsRequest;
import com.google.Android.gms.location.LocationSettingsResult;
import com.google.Android.gms.location.LocationSettingsStatusCodes;


public class GoogleLocation implements
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener,
        LocationListener,
        ResultCallback<LocationSettingsResult> {

    private LocationSettingsRequest mLocationSettingsRequest;
    private GoogleApiClient mGoogleApiClient;
    private LocationRequest mLocationRequest;
    private String TAG = "GoogleLocation";

    private Context context;
    private long UPDATE_INTERVAL = 10 * 1000;  /* 10 secs */
    private long FASTEST_INTERVAL = 2000; /* 2 sec */
    private GoogleLocationCallback googleLocationCallback;

    private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
    public static final int REQUEST_CHECK_SETTINGS = 0x1;

    public GoogleLocation(Context context) {
        this.context = context;
        setUpLocation();
    }

    public void setGoogleLocationCallback(GoogleLocationCallback googleLocationCallback) {
        this.googleLocationCallback = googleLocationCallback;
    }

    private void setUpLocation() {

        mGoogleApiClient = new GoogleApiClient.Builder(context.getApplicationContext())
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();

        // Create the LocationRequest object
        mLocationRequest = LocationRequest.create()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(UPDATE_INTERVAL)        // 10 seconds, in milliseconds
                .setFastestInterval(FASTEST_INTERVAL); // 1 second, in milliseconds

        locationEnable();
    }

    public void googleClientConnect() {
        mGoogleApiClient.connect();
    }

    public void googleClientDisConnect() {
        if (mGoogleApiClient.isConnected()) {
            mGoogleApiClient.unregisterConnectionCallbacks(this);
            mGoogleApiClient.unregisterConnectionFailedListener(this);
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
            mGoogleApiClient.disconnect();
            mGoogleApiClient = null;
        }
    }

    private void locationEnable() {
        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
        builder.addLocationRequest(mLocationRequest);
        mLocationSettingsRequest = builder.build();
        checkLocationSettings();
    }

    private void checkLocationSettings() {
        PendingResult<LocationSettingsResult> result =
                LocationServices.SettingsApi.checkLocationSettings(
                        mGoogleApiClient,
                        mLocationSettingsRequest
                );
        result.setResultCallback(this);
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        if (location == null) {
            updateLocation();
        } else {
            handleNewLocation(location);
        }
    }

    private void handleNewLocation(Location location) {

        double currentLatitude = location.getLatitude();
        double currentLongitude = location.getLongitude();

        Log.e(TAG, "---currentLatitude--" + currentLatitude);
        Log.e(TAG, "---currentLongitude--" + currentLongitude);

        if (googleLocationCallback != null) {
            googleLocationCallback.updateLocationListner(currentLatitude, currentLongitude);
        }

    }

    public void updateLocation() {
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
                     /*
         * Google Play services can resolve some errors it detects.
         * If the error has a resolution, try sending an Intent to
         * start a Google Play services activity that can resolve
         * error.
         */
        if (connectionResult.hasResolution()) {
            try {
                // Start an Activity that tries to resolve the error
                connectionResult.startResolutionForResult((Activity) context, CONNECTION_FAILURE_RESOLUTION_REQUEST);
                /*
                 * Thrown if Google Play services canceled the original
                 * PendingIntent
                 */
            } catch (IntentSender.SendIntentException e) {
                // Log the error
                e.printStackTrace();
            }
        } else {
            /*
             * If no resolution is available, display a dialog to the
             * user with the error.
             */
            Log.e(TAG, "Location services connection failed with code " + connectionResult.getErrorCode());
        }
    }

    @Override
    public void onResult(@NonNull LocationSettingsResult locationSettingsResult) {
        final Status status = locationSettingsResult.getStatus();
        switch (status.getStatusCode()) {
            case LocationSettingsStatusCodes.SUCCESS:
                Log.e(TAG, "All location settings are satisfied.");
                break;
            case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                Log.e(TAG, "Location settings are not satisfied. Show the user a dialog to" +
                        "upgrade location settings ");

                try {
                    // Show the dialog by calling startResolutionForResult(), and check the result
                    // in onActivityResult().
                    status.startResolutionForResult((Activity) context, REQUEST_CHECK_SETTINGS);
                } catch (IntentSender.SendIntentException e) {
                    Log.e(TAG, "PendingIntent unable to execute request.");
                }
                break;
            case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                Log.e(TAG, "Location settings are inadequate, and cannot be fixed here. Dialog " +
                        "not created.");
                break;
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        handleNewLocation(location);
    }

    public interface GoogleLocationCallback {
        void updateLocationListner(double latitude, double longitude);
    }
}

次に、コールバックを実装しますGoogleLocation.GoogleLocationCallback取得するアクティビティまたはフラグメントに位置を取得し、その後、位置を取得するアクティビティまたはフラグメントに関するコードを以下に記述します

googleLocation = new GoogleLocation(this);
googleLocation.setGoogleLocationCallback(this);
3
Darshan Mistry

これを解決する最良の方法は、結果をフィルタリングすることです。 Fuseサービスから返される正しい場所の確率は67%です。これは、1/3で間違った場所を取得できることを意味します。

場所を取得する前に、以前の場所(または複数の場所を確認するために、リストに保持しておく)と現在の場所を比較する必要があります。顕著な違いがある場合は、最新のものを使用しないでください。

注:ユーザーの場所をファイルやフォルダに保存しないでください。プライバシーポリシーに違反します。lastKnownLocationまたは現在のセッションで受信した場所と比較してください。

もう1つのアイデアは、現在使用しているGoogleApiClientを使用している場合、融合した場所の新しいクラス FusedLocationProvider があることです。これを確認できます。

また、私が理解している限り、Android 8.0(APIレベル26)以降のデバイスの場合、1時間あたり4つの場所の取得制限を設定します。 このドキュメント を確認して、新しい動作。

2
Thracian

インドのプネーでも同じ問題がありました。テストに使用したデバイスはスワイプタブレットでした。

ソリューション:保持最後の緯度と経度と更新時間、次の緯度と経度が更新されたときに、距離が100より大きい場合、その2つの場所の間の距離を計算しますKMの場合、カウントされません。

方法iは距離の計算に使用

public static double calculateDistance(double lat1,double lon1,double lat2,double lon2) {

    double theta = lon1 - lon2;
    double dist = Math.sin(deg2rad(lat1))
            * Math.sin(deg2rad(lat2))
            + Math.cos(deg2rad(lat1))
            * Math.cos(deg2rad(lat2))
            * Math.cos(deg2rad(theta));
    dist = Math.acos(dist);
    dist = rad2deg(dist);
    dist = dist * 60 * 1.1515;
    return (dist);
}    

なぜそれが起こるのか

Androidデバイスでは、位置を取得するための2つのオプションがあります

  • GPS
  • インターネット

[〜#〜] gps [〜#〜](LocationManager.GPS_PROVIDER):インターネットに比べて遅いですが、インターネットよりも正確です。遠隔地ではGPS接続が遅くなり、接続が困難になる場合があります。

インターネット(LocationManager.NETWORK_PROVIDER):GPSよりはるかに高速ですが、GPSよりも正確ではありません

GPSに到達できず、現在地が必要な場合、LocationManagerはインターネットから取得した利用可能なデータを提供します。 (この場合、位置情報プロバイダーも確認できます。)

1
Anu Martin

あなたはこれを使うことができます:

package com.amolood.news.manager;

/**
 * Created by nasnasa on 14/02/2017.
 */

import Android.app.AlertDialog;
import Android.app.Service;
import Android.content.Context;
import Android.content.DialogInterface;
import Android.content.Intent;
import Android.location.Location;
import Android.location.LocationListener;
import Android.location.LocationManager;
import Android.os.Bundle;
import Android.os.IBinder;
import Android.provider.Settings;
import Android.util.Log;


/**
 * Created by nasnasa on 14/01/2017.
 */

public class GPSTracker extends Service implements LocationListener {

    private final Context mContext;

    // flag for GPS status
    boolean isGPSEnabled = false;

    // flag for network status
    boolean isNetworkEnabled = false;

    // flag for GPS status
    boolean canGetLocation = false;

    Location location; // location
    double latitude; // latitude
    double longitude; // longitude

    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute

    // Declaring a Location Manager
    protected LocationManager locationManager;

    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;
                // First get location from Network Provider
                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;
    }

}

あなたの活動についてこれのように

GPSTracker gps = new GPSTracker(this);

gps.getlocation();
double latitude = gps.getLatitude();
double longitude = gps.getLongitude();
0