web-dev-qa-db-fra.com

Calculer le relèvement/cap de la boussole sur Android

Je souhaite afficher une flèche à mon emplacement sur une vue Google Map indiquant ma direction par rapport à un emplacement de destination (au lieu de nord).

a) J'ai calculé le nord en utilisant les valeurs de capteur du magnétomètre et de l'accéléromètre. Je sais que cela est correct car il s’aligne sur la boussole utilisée dans la vue Google Map.

b) J'ai calculé le relèvement initial de mon emplacement à l'emplacement de destination en utilisant myLocation.bearingTo (destLocation); 

Il me manque la dernière étape; À partir de ces deux valeurs (a et b), quelle formule dois-je utiliser pour obtenir la direction dans laquelle le téléphone pointe par rapport à l'emplacement de destination?

Appréciez toute aide pour un esprit agité! 

51
Damian

Ok je l'ai compris. Pour ceux qui essaient de le faire, il vous faut:

a) cap: votre cap depuis la boussole matérielle. C'est en degrés à l'est de magnétique nord

b) relèvement: le relèvement de votre position à la destination. C'est en degrés à l'est de vrai nord.

myLocation.bearingTo(destLocation);

c) déclinaison: différence entre le nord vrai et le nord magnétique

Le cap renvoyé par le magnétomètre + accéléromètre est exprimé en degrés à l’est du nord vrai (magnétique) (-180 à +180). Vous devez donc connaître la différence entre le nord et le nord magnétique. Cette différence est variable selon l'endroit où vous vous trouvez sur la terre. Vous pouvez obtenir en utilisant la classe GeomagneticField.

GeomagneticField geoField;

private final LocationListener locationListener = new LocationListener() {
   public void onLocationChanged(Location location) {
      geoField = new GeomagneticField(
         Double.valueOf(location.getLatitude()).floatValue(),
         Double.valueOf(location.getLongitude()).floatValue(),
         Double.valueOf(location.getAltitude()).floatValue(),
         System.currentTimeMillis()
      );
      ...
   }
}

Armés de ceux-ci, vous calculez l'angle de la flèche à tracer sur votre carte pour indiquer votre position par rapport à votre objet de destination plutôt que par le nord géographique.

Ajustez d'abord votre cap avec la déclinaison:

heading += geoField.getDeclination();

Deuxièmement, vous devez décaler la direction dans laquelle le téléphone fait face (en-tête) par rapport à la destination cible plutôt que par le nord géographique. C'est la partie sur laquelle je suis resté coincé. La valeur de cap renvoyée par la boussole vous donne une valeur décrivant l'emplacement du nord magnétique (en degrés est du nord vrai) par rapport à l'orientation du téléphone. Donc, par exemple si la valeur est -10, vous savez que le nord magnétique est à 10 degrés sur votre gauche. Le relèvement vous donne l’angle de votre destination en degrés à l’est du nord vrai. Donc, après avoir compensé la déclinaison, vous pouvez utiliser la formule ci-dessous pour obtenir le résultat souhaité:

heading = myBearing - (myBearing + heading); 

Vous voudrez ensuite convertir les degrés situés à l'est du nord géographique (-180 à +180) en degrés normaux (0 à 360):

Math.round(-heading / 360 + 180)
64
Damian

@ Damian - L'idée est très bonne et je suis d'accord avec la réponse, mais lorsque j'ai utilisé votre code, j'ai eu de mauvaises valeurs. J'ai donc écrit cela tout seul (quelqu'un a dit la même chose dans vos commentaires). Compter le titre avec la déclinaison est bon, je pense, mais plus tard, j'ai utilisé quelque chose comme ça:

heading = (bearing - heading) * -1;

au lieu du code de Damian:

heading = myBearing - (myBearing + heading); 

et en passant de -180 à 180 pour les valeurs de 0 à 360:

      private float normalizeDegree(float value){
          if(value >= 0.0f && value <= 180.0f){
              return value;
          }else{
              return 180 + (180 + value);
          }

et quand vous voulez faire pivoter votre flèche, vous pouvez utiliser un code comme celui-ci:

      private void rotateArrow(float angle){

            Matrix matrix = new Matrix();
            arrowView.setScaleType(ScaleType.MATRIX);
            matrix.postRotate(angle, 100f, 100f);
            arrowView.setImageMatrix(matrix);
      }

arrowView est ImageView avec image de flèche et 100f paramètres dans postRotate est pivX et pivY).

J'espère que j'aiderai quelqu'un.

15
CookieMonssster

En cela, une flèche sur la boussole indique la direction à partir de votre position vers Kaaba ( destination Location )

vous pouvez simplement utiliser portant de cette façon. porter vous donnera l'angle direct entre votre position et la destination.

  Location userLoc=new Location("service Provider");
    //get longitudeM Latitude and altitude of current location with gps class and  set in userLoc
    userLoc.setLongitude(longitude); 
    userLoc.setLatitude(latitude);
    userLoc.setAltitude(altitude);

   Location destinationLoc = new Location("service Provider");
  destinationLoc.setLatitude(21.422487); //kaaba latitude setting
  destinationLoc.setLongitude(39.826206); //kaaba longitude setting
  float bearTo=userLoc.bearingTo(destinationLoc);

bearingTo vous donnera une plage de -180 à 180, ce qui compliquera un peu les choses. Nous devrons convertir cette valeur dans une plage de 0 à 360 pour obtenir la rotation correcte.

Ceci est un tableau de ce que nous voulons vraiment, comparant à ce que portantTo nous donne

+-----------+--------------+
| bearingTo | Real bearing |
+-----------+--------------+
| 0         | 0            |
+-----------+--------------+
| 90        | 90           |
+-----------+--------------+
| 180       | 180          |
+-----------+--------------+
| -90       | 270          |
+-----------+--------------+
| -135      | 225          |
+-----------+--------------+
| -180      | 180          |
+-----------+--------------+

nous devons donc ajouter ce code après bearTo

// If the bearTo is smaller than 0, add 360 to get the rotation clockwise.

  if (bearTo < 0) {
    bearTo = bearTo + 360;
    //bearTo = -100 + 360  = 260;
}

vous devez implémenter SensorEventListener et ses fonctions (onSensorChanged, onAcurracyChabge) et écrire tout le code à l'intérieur de onSensorChanged 

Le code complet est indiqué ici pour la direction du compas Qibla

 public class QiblaDirectionCompass extends Service implements SensorEventListener{
 public static ImageView image,arrow;

// record the compass picture angle turned
private float currentDegree = 0f;
private float currentDegreeNeedle = 0f;
Context context;
Location userLoc=new Location("service Provider");
// device sensor manager
private static SensorManager mSensorManager ;
private Sensor sensor;
public static TextView tvHeading;
   public QiblaDirectionCompass(Context context, ImageView compass, ImageView needle,TextView heading, double longi,double lati,double alti ) {

    image = compass;
    arrow = needle;


    // TextView that will tell the user what degree is he heading
    tvHeading = heading;
    userLoc.setLongitude(longi);
    userLoc.setLatitude(lati);
    userLoc.setAltitude(alti);

  mSensorManager =  (SensorManager) context.getSystemService(SENSOR_SERVICE);
    sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
    if(sensor!=null) {
        // for the system's orientation sensor registered listeners
        mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME);//SensorManager.SENSOR_DELAY_Fastest
    }else{
        Toast.makeText(context,"Not Supported", Toast.LENGTH_SHORT).show();
    }
    // initialize your Android device sensor capabilities
this.context =context;
@Override
public void onCreate() {
    // TODO Auto-generated method stub
    Toast.makeText(context, "Started", Toast.LENGTH_SHORT).show();
    mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME); //SensorManager.SENSOR_DELAY_Fastest
    super.onCreate();
}

@Override
public void onDestroy() {
    mSensorManager.unregisterListener(this);
Toast.makeText(context, "Destroy", Toast.LENGTH_SHORT).show();

    super.onDestroy();

}
@Override
public void onSensorChanged(SensorEvent sensorEvent) {


Location destinationLoc = new Location("service Provider");

destinationLoc.setLatitude(21.422487); //kaaba latitude setting
destinationLoc.setLongitude(39.826206); //kaaba longitude setting
float bearTo=userLoc.bearingTo(destinationLoc);

  //bearTo = The angle from true north to the destination location from the point we're your currently standing.(asal image k N se destination taak angle )

  //head = The angle that you've rotated your phone from true north. (jaise image lagi hai wo true north per hai ab phone jitne rotate yani jitna image ka n change hai us ka angle hai ye)



GeomagneticField geoField = new GeomagneticField( Double.valueOf( userLoc.getLatitude() ).floatValue(), Double
        .valueOf( userLoc.getLongitude() ).floatValue(),
        Double.valueOf( userLoc.getAltitude() ).floatValue(),
        System.currentTimeMillis() );
head -= geoField.getDeclination(); // converts magnetic north into true north

if (bearTo < 0) {
    bearTo = bearTo + 360;
    //bearTo = -100 + 360  = 260;
}

//This is where we choose to point it
float direction = bearTo - head;

// If the direction is smaller than 0, add 360 to get the rotation clockwise.
if (direction < 0) {
    direction = direction + 360;
}
 tvHeading.setText("Heading: " + Float.toString(degree) + " degrees" );

RotateAnimation raQibla = new RotateAnimation(currentDegreeNeedle, direction, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
raQibla.setDuration(210);
raQibla.setFillAfter(true);

arrow.startAnimation(raQibla);

currentDegreeNeedle = direction;

// create a rotation animation (reverse turn degree degrees)
RotateAnimation ra = new RotateAnimation(currentDegree, -degree, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);

// how long the animation will take place
ra.setDuration(210);


// set the animation after the end of the reservation status
ra.setFillAfter(true);

// Start the animation
image.startAnimation(ra);

currentDegree = -degree;
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {

}
@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

le code XML est ici 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:orientation="vertical"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:background="@drawable/flag_pakistan">
<TextView
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:id="@+id/heading"
    Android:textColor="@color/colorAccent"
    Android:layout_centerHorizontal="true"
    Android:layout_marginBottom="100dp"
    Android:layout_marginTop="20dp"
    Android:text="Heading: 0.0" />
<RelativeLayout
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_below="@+id/heading"
Android:scaleType="centerInside"
Android:layout_centerVertical="true"
Android:layout_centerHorizontal="true">

<ImageView
    Android:id="@+id/imageCompass"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:scaleType="centerInside"
    Android:layout_centerVertical="true"
    Android:layout_centerHorizontal="true"
    Android:src="@drawable/images_compass"/>

<ImageView
    Android:id="@+id/needle"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_centerVertical="true"
    Android:layout_centerHorizontal="true"
    Android:scaleType="centerInside"
    Android:src="@drawable/arrow2"/>
</RelativeLayout>
</RelativeLayout>
3

Je ne suis pas un expert en lecture de carte/navigation, etc., mais les «directions» sont sûrement absolues et non relatives ou en réalité, elles sont relatives à N ou S, qui sont eux-mêmes fixes/absolus.

Exemple: supposons qu'une ligne imaginaire dessinée entre vous et votre destination corresponde à la SE absolue (relèvement de 135 degrés par rapport au N magnétique). Supposons maintenant que votre téléphone est dirigé vers le nord-ouest. Si vous tracez une ligne imaginaire d'un objet imaginaire à l'horizon jusqu'à votre destination, elle traversera votre position et aura un angle de 180 degrés. Désormais, 180 degrés au sens d'une boussole désigne en fait un S, mais la destination n'est pas «due à» de l'objet imaginaire pointé vers votre téléphone. De plus, si vous vous rendez à ce point imaginaire, votre destination sera toujours le SE de où vous avez déménagé.

En réalité, la ligne à 180 degrés vous indique en fait que la destination est "derrière vous" par rapport à la position du téléphone (et probablement de vous).

Cela dit, si vous voulez calculer l’angle d’une ligne du point imaginaire à votre destination (en passant par votre position) afin de tracer un pointeur vers votre destination, c’est ce que vous voulez ... soustrayez simplement le relèvement (absolu) de la destination à partir du relèvement absolu de l'objet imaginaire et ignorer une négation (si présente). Par exemple, NW - SE correspond à 315 - 135 = 180, faites donc glisser le pointeur de la souris vers le bas de l’écran en indiquant «derrière vous».

EDIT: Je me suis un peu trompé dans les calculs ... soustrayez le plus petit des relèvements du plus grand puis soustrayez le résultat de 360 ​​pour obtenir l'angle dans lequel le pointeur sera tracé à l'écran.

2
Squonk

Je sais que c'est un peu vieux, mais pour l'amour de gens comme moi de Google qui n'ont pas trouvé de réponse complète ici. Voici quelques extraits de mon application qui placent les flèches dans une liste personnalisée ....

Location loc;   //Will hold lastknown location
Location wptLoc = new Location("");    // Waypoint location 
float dist = -1;
float bearing = 0;
float heading = 0;
float arrow_rotation = 0;

LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);

if(loc == null) {   //No recent GPS fix
    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    criteria.setAltitudeRequired(false);
    criteria.setBearingRequired(true);
    criteria.setCostAllowed(true);
    criteria.setSpeedRequired(false);
    loc = lm.getLastKnownLocation(lm.getBestProvider(criteria, true));
}

if(loc != null) {
    wptLoc.setLongitude(cursor.getFloat(2));    //Cursor is from SimpleCursorAdapter
    wptLoc.setLatitude(cursor.getFloat(3));
    dist = loc.distanceTo(wptLoc);
    bearing = loc.bearingTo(wptLoc);    // -180 to 180
    heading = loc.getBearing();         // 0 to 360
    // *** Code to calculate where the arrow should point ***
    arrow_rotation = (360+((bearing + 360) % 360)-heading) % 360;
}

Je suis prêt à parier que cela pourrait être simplifié mais que cela fonctionne! LastKnownLocation a été utilisé car ce code provenait du nouveau SimpleCursorAdapter.ViewBinder ()

onLocationChanged contient un appel à notifyDataSetChanged ();

code également à partir de new SimpleCursorAdapter.ViewBinder () pour définir la rotation des images et les couleurs listrow (uniquement appliquées dans un seul columnIndex, remarquez-vous) ...

LinearLayout ll = ((LinearLayout)view.getParent());
ll.setBackgroundColor(bc); 
int childcount = ll.getChildCount();
for (int i=0; i < childcount; i++){
    View v = ll.getChildAt(i);
    if(v instanceof TextView) ((TextView)v).setTextColor(fc);
    if(v instanceof ImageView) {
        ImageView img = (ImageView)v;
        img.setImageResource(R.drawable.ic_arrow);
        Matrix matrix = new Matrix();
        img.setScaleType(ScaleType.MATRIX);
        matrix.postRotate(arrow_rotation, img.getWidth()/2, img.getHeight()/2);
        img.setImageMatrix(matrix); 
}

Au cas où vous vous demanderiez si je m'étais débarrassé des séries de capteurs magnétiques, cela ne valait pas la peine dans mon cas… .. J'espère que quelqu'un trouvera cela aussi utile que je le fais habituellement quand Google m'amène à stackoverflow!

1
dno

Voici comment je l'ai fait:

Canvas g = new Canvas( compass );
Paint p = new Paint( Paint.ANTI_ALIAS_FLAG );

float rotation = display.getOrientation() * 90;

g.translate( -box.left, -box.top );
g.rotate( -bearing - rotation, box.exactCenterX(), box.exactCenterY() );
drawCompass( g, p );
drawNeedle( g, p );
1
Sileria

Si vous êtes sur le même fuseau horaire

Convertir GPS en UTM

http://www.ibm.com/developerworks/Java/library/j-coordconvert/http://stackoverflow.com/questions/176137/Java-convert-lat-lon-to-utm

Les coordonnées UTM vous permettent d'obtenir un simple X Y 2D

Calculer l'angle entre les deux emplacements UTM

http://forums.groundspeak.com/GC/index.php?showtopic=146917

Cela donne la direction comme si vous regardiez au nord

Donc, quel que soit votre rotation, le Nord ne fait que soustraire cet angle

Si les deux points ont un angle UTM de 45º et que vous vous trouvez à 5º à l’est du nord, votre flèche indiquera 40º de nord.

1
Pedro

Voici le code pour calculer l'angle de relèvement entre deux points:

public float CalculateBearingAngle(double lat1,double lon1, double lat2, double lon2){
    double Phi1 = Math.toRadians(lat1);
    double Phi2 = Math.toRadians(lat2);
    double DeltaLambda = Math.toRadians(lon2 - lon1);
    double Theta = atan2((sin(DeltaLambda)*cos(Phi2)),
        (cos(Phi1)*sin(Phi2) - sin(Phi1)*cos(Phi2)*cos(DeltaLambda)));
    return (float)Math.toDegrees(Theta);
}

Appel à fonction:

float angle = CalculateBearingAngle(lat1, lon1, lat2, lon2);
0
APP

C’est le meilleur moyen de détecter le relèvement d’un objet de localisation sur Google Map: ->

 float targetBearing=90;

      Location endingLocation=new Location("ending point"); 

      Location
       startingLocation=new Location("starting point");
       startingLocation.setLatitude(mGoogleMap.getCameraPosition().target.latitude);
       startingLocation.setLongitude(mGoogleMap.getCameraPosition().target.longitude);
       endingLocation.setLatitude(mLatLng.latitude);
       endingLocation.setLongitude(mLatLng.longitude);
      targetBearing =
       startingLocation.bearingTo(endingLocation);

0
Ramkailash

La formule donnera le relèvement en utilisant les coordonnées du point de départ au point final voir

Le code suivant vous donnera le relèvement (angle compris entre 0 et 360)

private double bearing(Location startPoint, Location endPoint) {
    double longitude1 = startPoint.getLongitude();
    double latitude1 = Math.toRadians(startPoint.getLatitude());

    double longitude2 = endPoint.getLongitude();        
    double latitude2 = Math.toRadians(endPoint.getLatitude());

    double longDiff = Math.toRadians(longitude2 - longitude1);

    double y = Math.sin(longDiff) * Math.cos(latitude2);
    double x = Math.cos(latitude1) * Math.sin(latitude2) - Math.sin(latitude1) * Math.cos(latitude2) * Math.cos(longDiff);

    return Math.toDegrees(Math.atan2(y, x));

}

Cela fonctionne pour moi, j'espère que cela fonctionnera aussi

0
K K

Je suis en train de le comprendre, mais il semble que le calcul dépende de l'endroit où vous et votre cible êtes sur la Terre par rapport au Nord vrai et magnétique. Par exemple: 

float thetaMeThem = 0.0;
if (myLocation.bearingTo(targetLocation) > myLocation.getBearing()){ 
     thetaMeThem = myLocation.bearingTo(targetLocation) - azimuth + declination;} 

Voir Sensor.TYPE_ORIENTATION pour l'azimut.

Voir getDeclination () pour la déclinaison

Cela suppose que la déclinaison est négative (ouest du nord vrai) et que vous portez> votre porteur.

Si la déclinaison est positive et que votre porteur> leur porteur porte une autre option:

float thetaMeThem = 0.0;
if (myLocation.bearingTo(targetLocation) < myLocation.getBearing()){ 
     thetaMeThem = azimuth - (myLocation.bearingTo(targetLocation) - declination);} 

Je n'ai pas complètement testé cela, mais jouer avec les angles sur le papier m'a amené ici.

0
tricknology

Terminologie: La différence entre le nord vrai et le nord magnétique est connue sous le nom de "variation" et non de déclinaison. La différence entre ce que lit votre boussole et le cap magnétique est appelée "déviation" et varie avec le cap. Un mouvement de boussole identifie les erreurs de l'appareil et permet d'appliquer des corrections si l'appareil comporte une correction intégrée. Un compas magnétique aura une carte de déviation décrivant l'erreur de l'appareil dans n'importe quelle direction.

Déclinaison: terme utilisé dans la navigation Astro: la déclinaison s'apparente à la latitude. Il rapporte à quelle distance une étoile est de l'équateur céleste. Pour trouver la déclinaison d'une étoile, suivez un cercle horaire "droit" de l'étoile à l'équateur céleste. L'angle de l'étoile à l'équateur céleste le long du cercle des heures est la déclinaison de l'étoile.

0
Simon