wake-up-neo.com

Berechnen Sie die Kompasspeilung/-richtung zur Position in Android

Ich möchte einen Pfeil an meinem Standort in einer Google-Kartenansicht anzeigen, der meine Richtung relativ zu einem Zielort (anstatt nach Norden) anzeigt.

a) Ich habe die Nordwerte anhand der Sensorwerte des Magnetometers und des Beschleunigungsmessers berechnet. Ich weiß, dass dies korrekt ist, da es mit dem in der Google Map-Ansicht verwendeten Kompass übereinstimmt.

b) Ich habe die anfängliche Peilung von meinem Standort zum Zielort mit myLocation.bearingTo (destLocation) berechnet. 

Ich vermisse den letzten Schritt. Welche Formel verwende ich aus diesen beiden Werten (a und b), um die Richtung zu ermitteln, in die das Telefon relativ zum Zielort zeigt?

Schätzen Sie jede Hilfe für einen verwirrten Geist! 

51
Damian

Ok, das habe ich herausgefunden. Für alle, die dies versuchen, benötigen Sie:

a) Überschrift: Ihre Überschrift aus dem Hardwarekompass. Dies ist in Grad östlich von magnetic north

b) Peilung: die Peilung von Ihrem Standort zum Zielort. Dies ist in Grad östlich von true north.

myLocation.bearingTo(destLocation);

c) Deklination: Der Unterschied zwischen dem wahren Norden und dem magnetischen Norden

Der vom Magnetometer + Beschleunigungsmesser zurückgegebene Kurs befindet sich in Grad östlich des wahren (magnetischen) Nordens (-180 bis +180). Daher müssen Sie die Differenz zwischen dem Norden und dem magnetischen Norden für Ihren Standort ermitteln. Dieser Unterschied hängt davon ab, wo Sie sich auf der Erde befinden. Sie können dies mit der GeomagneticField-Klasse erreichen.

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()
      );
      ...
   }
}

Bewaffnet damit berechnen Sie den Winkel des Pfeils, den Sie auf Ihrer Karte zeichnen, um zu zeigen, wo Sie sich in Bezug auf Ihr Zielobjekt befinden und nicht in Richtung Norden.

Passen Sie zuerst Ihre Überschrift mit der Deklination an:

heading += geoField.getDeclination();

Zweitens müssen Sie die Richtung, in die das Telefon zeigt (Richtung), vom Zielziel aus verschieben und nicht nach Norden. Dies ist der Teil, an dem ich festgeblieben bin. Der vom Kompass zurückgegebene Wert für die Überschrift gibt Ihnen einen Wert an, der beschreibt, wo der magnetische Norden (in Grad östlich des wahren Nordens) relativ zu der Position des Telefons ist. So z. Wenn der Wert -10 ist, wissen Sie, dass der magnetische Norden 10 Grad links von Ihnen liegt. Die Peilung gibt Ihnen den Winkel Ihres Ziels in Grad östlich des wahren Nordens an. Nachdem Sie die Deklination kompensiert haben, können Sie die folgende Formel verwenden, um das gewünschte Ergebnis zu erhalten:

heading = myBearing - (myBearing + heading); 

Sie wollen dann von Grad östlich des wahren Nordens (-180 in +180) in normale Grade (0 bis 360) umrechnen:

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

@Damian - Die Idee ist sehr gut und ich stimme der Antwort zu, aber als ich Ihren Code verwendet habe, hatte ich falsche Werte, also schrieb ich dies selbst (jemand hat das in Ihren Kommentaren gesagt). Ich denke, mit der Deklination zu zählen, ist gut, aber später habe ich so etwas verwendet:

heading = (bearing - heading) * -1;

anstelle von Damians Code:

heading = myBearing - (myBearing + heading); 

und Ändern von -180 bis 180 für 0 bis 360:

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

wenn Sie dann den Pfeil drehen möchten, können Sie Code wie folgt verwenden:

      private void rotateArrow(float angle){

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

wobei arrowViewImageView mit Pfeilbild und 100f-Parametern in postRotate PivX und PivY ist.

Ich hoffe ich helfe jemandem.

15
CookieMonssster

In diesem Pfeil auf dem Kompass zeigt die Richtung von Ihrem Standort zu Kaaba ( Zielort )

sie können auf diese Weise ganz einfach auf Lager auftreiben. Wenn Sie tragen, erhalten Sie den direkten Winkel von Ihrem Standort zum Zielort

  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);

mit LagerTo erhalten Sie einen Bereich von -180 bis 180, was die Dinge etwas verwirrt. Wir müssen diesen Wert in einen Bereich von 0 bis 360 konvertieren, um die korrekte Rotation zu erhalten.

Dies ist eine Tabelle mit dem, was wir wirklich wollen, im Vergleich zu dem, was uns die Po- sitionTo bietet

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

deshalb müssen wir diesen Code nach bearTo hinzufügen

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

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

sie müssen den SensorEventListener und seine Funktionen (onSensorChanged, onAcurracyChabge) implementieren und den gesamten Code in onSensorChanged schreiben 

Der vollständige Code ist hier für die Richtung des Qibla-Kompasses

 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;
}

xML-Code ist hier 

<?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>

Ich bin kein Experte für Kartenlesen/Navigation usw. Aber "Richtungen" sind absolut und nicht relativ oder in Wirklichkeit, sie sind relativ zu N oder S, die selbst fest/absolut sind.

Beispiel: Angenommen, eine imaginäre Linie zwischen Ihnen und Ihrem Ziel entspricht der absoluten SE (einer Peilung von 135 Grad in Bezug auf das magnetische N). Nehmen Sie jetzt an, dass Ihr Telefon in NW-Richtung zeigt. Wenn Sie eine imaginäre Linie von einem imaginären Objekt am Horizont bis zu Ihrem Ziel ziehen, wird es Ihren Standort passieren und einen Winkel von 180 Grad haben. 180 Grad im Sinne eines Kompasses bezieht sich tatsächlich auf S, aber das Ziel ist nicht "aufgrund von S" des imaginären Objekts, auf das Ihr Telefon zeigt, und wenn Sie zu diesem imaginären Punkt gereist sind, wäre Ihr Ziel immer noch SE wo du hingezogen bist.

In Wirklichkeit zeigt die 180-Grad-Linie an, dass das Ziel relativ zu der Richtung, in der das Telefon (und vermutlich auch Sie) zeigt, "hinter Ihnen" liegt.

Allerdings, wenn Sie den Winkel einer Linie vom imaginären Punkt zu Ihrem Ziel (durch Ihren Standort) berechnen, um einen Zeiger auf Ihr Ziel zu zeichnen, wollen Sie ... einfach die (absolute) Peilung von subtrahieren das Ziel aus der absoluten Peilung des imaginären Objekts und ignorieren Sie eine Negation (falls vorhanden). z. B. NW - SE ist 315 - 135 = 180, ziehen Sie den Zeiger so, dass er am unteren Rand des Bildschirms zeigt und "hinter Ihnen" anzeigt.

EDIT: Ich habe die Mathematik etwas falsch gemacht ... subtrahiere die kleinere Peilung von der größeren und dann das Ergebnis von 360, um den Winkel zu ermitteln, in dem der Zeiger auf dem Bildschirm gezeichnet wird.

2
Squonk

Ich weiß, das ist ein bisschen alt, aber für Leute wie mich von Google, die hier keine vollständige Antwort gefunden haben. Hier sind einige Auszüge aus meiner App, die die Pfeile in eine benutzerdefinierte Listenansicht einfügen.

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;
}

Ich bin bereit zu wetten, dass es vereinfacht werden könnte, aber es funktioniert! LastKnownLocation wurde verwendet, da dieser Code aus dem neuen SimpleCursorAdapter.ViewBinder () stammt.

onLocationChanged enthält einen Aufruf an notifyDataSetChanged ();

code auch aus dem neuen SimpleCursorAdapter.ViewBinder (), um die Farben für die Bildrotation und den Farbverlauf festzulegen (wird nur in einem einzelnen columnIndex angewendet).

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); 
}

Falls Sie sich fragen, ob ich mit den Magnetsensordramen aufgehört habe, war es in meinem Fall nicht die Mühe wert.

1
dno

So habe ich es gemacht:

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

Wenn Sie sich in derselben Zeitzone befinden

Konvertieren Sie GPS in UTM

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

Mit UTM-Koordinaten erhalten Sie ein einfaches XY-2D

Berechnen Sie den Winkel zwischen den beiden UTM-Positionen

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

Dies gibt die Richtung vor, als würden Sie nach Norden schauen

Was auch immer Sie also drehen, zieht den Winkel nach Norden ab

Wenn beide Punkte einen UTM-Winkel von 45 ° haben und Sie sich 5 ° östlich von Norden befinden, zeigt Ihr Pfeil auf 40 ° nach Norden

1
Pedro

Hier ist der Code zur Berechnung des Peilwinkels zwischen zwei Punkten:

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);
}

Aufruf zur Funktion:

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

Dies ist der beste Weg, um Peilung vom Positionsobjekt in Google Map zu erkennen: ->

 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

Die Formel gibt die Peilung unter Verwendung der Koordinaten des Startpunkts bis zum Endpunkt an siehe

Der folgende Code gibt die Richtung an (Winkel zwischen 0-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));

}

Das funktioniert für mich, ich hoffe, dass es auch für andere funktionieren wird

0
K K

Ich bin gerade dabei, es herauszufinden, aber es scheint, als ob die Mathematik davon abhängt, wo Sie und Ihr Ziel auf der Erde relativ zum wahren und magnetischen Norden sind. Zum Beispiel: 

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

Siehe Azimut unter Sensor.TYPE_ORIENTATION.

Siehe auch getDeclination () für die Deklination

Dies setzt voraus, dass die Deklination negativ ist (westlich des wahren Nordens) und deren> Tragen.

Wenn die Deklination positiv ist und Sie eine andere Option verwenden:

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

Ich habe das noch nicht vollständig getestet, aber das Spiel mit den Winkeln auf dem Papier hat mich hierher gebracht.

0
tricknology

Terminologie: Der Unterschied zwischen TRUE North und Magnetic North wird als "Variation" und nicht als Deklination bezeichnet. Der Unterschied zwischen dem, was Ihr Kompass liest, und dem magnetischen Kurs ist als "Abweichung" bekannt und variiert mit dem Kurs. Ein Kompassausschlag identifiziert Gerätefehler und ermöglicht die Anwendung von Korrekturen, wenn das Gerät über eine eingebaute Korrektur verfügt. Ein Magnetkompass verfügt über eine Abweichungskarte, die den Gerätefehler in jeder Richtung beschreibt.

Deklination: Ein in der Astronavigation verwendeter Begriff: Deklination ist wie Breitengrad. Es berichtet, wie weit ein Stern vom Himmelsäquator entfernt ist. Um die Deklination eines Sterns zu finden, folgen Sie einem Stundenzirkel vom Stern zum Himmelsäquator. Der Winkel vom Stern zum Himmelsäquator entlang des Stundenkreises ist die Deklination des Sterns.

0
Simon