在Android中计算罗盘方位/前往位置

Dam*_*ian 54 android location bearing heading compass-geolocation

我想在谷歌地图视图上的我的位置显示一个箭头,显示我相对于目的地位置(而不是北方)的方向.

a)我使用磁力计和加速度计的传感器值计算了北方.我知道这是正确的,因为它与谷歌地图视图中使用的指南针对齐.

b)我使用myLocation.bearingTo(destLocation)计算了从我的位置到目的地位置的初始方位;

我错过了最后一步; 从这两个值(a和b)我用什么公式来得出手机相对于目的地位置的方向?

感谢任何有关心灵的帮助!

Dam*_*ian 65

好的,我想出来了.对于其他试图这样做的人,你需要:

a)航向:从硬件指南针前进.这是北以东的度数

b)轴承:轴承从您的位置到目的地.这是在北的东边.

myLocation.bearingTo(destLocation);
Run Code Online (Sandbox Code Playgroud)

c)赤纬:真北和磁北之间的差异

从磁力计+加速度计返回的航向是真(磁)北(-180到+180)以东的度数,因此您需要获得北和磁北的区别.这种差异是可变的,取决于你在地球上的位置.您可以使用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()
      );
      ...
   }
}
Run Code Online (Sandbox Code Playgroud)

有了这些,您可以计算在地图上绘制的箭头的角度,以显示您相对于目标对象所面对的位置,而不是真正的北方.

首先用赤纬调整你的标题:

heading += geoField.getDeclination();
Run Code Online (Sandbox Code Playgroud)

其次,您需要从目标目的地而不是真北偏移手机朝向(航向)的方向.这是我被困住的部分.从罗盘返回的航向值为您提供一个值,该值描述磁北向(相对于手机指向的位置,以真北为东)的位置.因此,例如,如果值为-10,则您知道磁北向左侧10度.轴承为您提供目的地的角度,以真北为东.因此,在您补偿了赤纬后,您可以使用下面的公式来获得所需的结果:

heading = myBearing - (myBearing + heading); 
Run Code Online (Sandbox Code Playgroud)

然后,您将要从真北(-180到+180)以东的度数转换为正常度数(0到360):

Math.round(-heading / 360 + 180)
Run Code Online (Sandbox Code Playgroud)

  • @Nikolas实际上就像是在说标题= -heading. (7认同)
  • @lespommes你有没有想到这一点?我也总是使用这段代码获得180度的标题: - /! (6认同)
  • 你到底怎么样?它与从location.getBearing()检索的值相同吗? (3认同)
  • 当你说"heading = myBearing - (myBearing + heading);"那里有错吗?这就像是说标题=标题. (2认同)

Coo*_*ter 17

@Damian - 这个想法非常好,我同意答案,但是当我使用你的代码时我的错误值,所以我自己写了这个(有人在你的评论中说了同样的话).我想,计算带有赤纬的标题是好的,但后来我使用了类似的东西:

heading = (bearing - heading) * -1;
Run Code Online (Sandbox Code Playgroud)

而不是达米安的代码:

heading = myBearing - (myBearing + heading); 
Run Code Online (Sandbox Code Playgroud)

并将-180更改为180表示0到360:

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

然后当你想旋转你的箭头时你可以使用这样的代码:

      private void rotateArrow(float angle){

            Matrix matrix = new Matrix();
            arrowView.setScaleType(ScaleType.MATRIX);
            matrix.postRotate(angle, 100f, 100f);
            arrowView.setImageMatrix(matrix);
      }
Run Code Online (Sandbox Code Playgroud)

其中arrowViewImageView与箭头图像和100F参数postRotate是pivX和pivY).

我希望我会帮助别人.

  • normalizeDegree是不必要的复杂.你可以用'return(value + 360)%360`来做同样的事情 (5认同)

Muh*_*rif 13

在此指南针上的箭头显示了从您所在位置到天房目的地位置)的方向

您可以通过这种方式简单地使用bearingTo。bearing to 将为您提供从您的位置到目的地位置的直接角度

  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);
Run Code Online (Sandbox Code Playgroud)

BearingTo 会给你一个从 -180 到 180 的范围,这会让事情有点混乱。我们需要将此值转换为 0 到 360 之间的范围以获得正确的旋转。

这是一张我们真正想要的表格,对比了bearingTo给我们的东西

+-----------+--------------+
| bearingTo | Real bearing |
+-----------+--------------+
| 0         | 0            |
+-----------+--------------+
| 90        | 90           |
+-----------+--------------+
| 180       | 180          |
+-----------+--------------+
| -90       | 270          |
+-----------+--------------+
| -135      | 225          |
+-----------+--------------+
| -180      | 180          |
+-----------+--------------+
Run Code Online (Sandbox Code Playgroud)

所以我们必须在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;
}
Run Code Online (Sandbox Code Playgroud)

您需要实现 SensorEventListener 及其函数(onSensorChanged,onAcurracyChabge)并在 onSensorChanged 中编写所有代码

完整的代码在这里为朝拜指南针的方向

 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;
}
Run Code Online (Sandbox Code Playgroud)

xml代码在这里

<?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>
Run Code Online (Sandbox Code Playgroud)