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

问题描述 投票:51回答:12

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

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

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

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

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

android location compass-geolocation heading bearing
12个回答
64
投票

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

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

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

myLocation.bearingTo(destLocation);

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

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

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

heading += geoField.getDeclination();

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

heading = myBearing - (myBearing + heading); 

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

Math.round(-heading / 360 + 180)

0
投票

这是在Google地图上检测来自位置对象的方位的最佳方法: - >

 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
投票

以下是计算两点之间轴承角度的代码:

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

征集功能:

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

0
投票

公式将使用起点坐标到终点see给出方位

以下代码将为您提供轴承(角度在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));

}

这对我有用,希望它也会对其他人有用


15
投票

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

heading = (bearing - heading) * -1;

而不是达米安的代码:

heading = myBearing - (myBearing + heading); 

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

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

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

      private void rotateArrow(float angle){

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

其中arrowView是带有箭头图片的ImageViewpostRotate中的100f参数是枢轴和旋转)。

我希望我会帮助别人。


3
投票

在此指南针上的箭头显示从您的位置到Kaaba的方向(目的地位置)

你可以通过这种方式简单地使用bearing.bearing将给你从你的位置到目的地的直接角度

  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会给你-180到180的范围,这会让事情有点混乱。我们需要将此值转换为0到360的范围才能获得正确的旋转。

这是我们真正想要的表格,与轴承给我们的内容相比较

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

所以我们必须在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;
}

你需要实现SensorEventListener及其函数(onSensorChanged,onAcurracyChabge)并写入onSensorChanged中的所有代码

完整的代码在这里为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;
}

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>

2
投票

我不是地图阅读/导航等方面的专家,但肯定'方向'是绝对的而不是相对的,或者实际上,它们是相对于N或S本身是固定的/绝对的。

示例:假设您和目的地之间绘制的假想线对应于“绝对”SE(相对于磁力N的轴承为135度)。现在假设您的手机指向NW - 如果您从地平线上的假想物体到目的地绘制一条假想线,它将通过您的位置并具有180度的角度。现在指南针意义上的180度实际上是指S但是目的地不是你的手机指向的假想物体的'到期',而且,如果你走到那个想象的点,你的目的地仍然是SE的你搬到哪里。

实际上,180度线实际上告诉你目的地是“在你身后”相对于手机(可能是你)指向的方式。

然而,话虽如此,如果计算从虚点到目的地的线的角度(通过你的位置)以便绘制指向目的地的指针是你想要的...简单地减去(绝对)方位从假想对象的绝对方位起的目的地,忽略否定(如果存在)。例如,NW - SE为315 - 135 = 180,因此将指针绘制在指向“在你后面”的屏幕底部。

编辑:我的数学略有错误...从较大的轴承中减去较小的轴承,然后从360减去结果,得到在屏幕上绘制指针的角度。


1
投票

如果你在同一时区

将GPS转换为UTM

HTTP://呜呜呜.IBM.com/developerWorks/Java/library/就-COO RD convert/ HTTP://stack overflow.com/questions/176137/Java-convert-邋遢-咯你-to-UTM

UTM坐标为您提供简单的X Y 2D

计算两个UTM位置之间的角度

HTTP://forums.ground speak.com/GC/index.PHP?show topic=146917

这给出了方向,好像你向北看

所以无论你旋转相关的是什么,北方只是减去这个角度

如果两个点都具有UTM45º角度并且您位于北纬5º,则箭头将指向北纬40°


1
投票

我是这样做的:

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
投票

我知道这有点旧,但是为了像谷歌这样的人,他们在这里找不到完整的答案。以下是我的应用程序的一些摘录,它将箭头放在自定义列表视图中....

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

我愿意打赌它可以简化但是有效!之前使用了LastKnownLocation,因为此代码来自新的SimpleCursorAdapter.ViewBinder()

onLocationChanged包含对notifyDataSetChanged()的调用;

代码也来自新的SimpleCursorAdapter.ViewBinder()来设置图像旋转和listrow颜色(仅适用于单个columnIndex中)...

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

如果你想知道我放弃了磁传感器的戏剧,在我的情况下不值得麻烦。我希望有人发现这和我通常做的一样有用,当谷歌把我带到stackoverflow时!


0
投票

术语:TRUE north和Magnetic North之间的差异被称为“变化”而不是赤纬。指南针读数和磁航向之间的差异称为“偏差”,随航向而变化。指南针摆动识别设备错误,并允许在设备内置校正时应用校正。磁罗盘将有一个偏差卡,用于描述任何航向上的设备错误。

赤纬:Astro导航中使用的术语:赤纬就像纬度。它报告一颗恒星离天体赤道有多远。要找到恒星的赤纬,请遵循从恒星到天体赤道的“直线向下”的小时圈。沿着小时圈从恒星到天体赤道的角度是恒星的赤纬。


0
投票

我现在正在计算它,但似乎数学取决于你和你的目标在地球上相对于真实和磁北的位置。例如:

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

有关方位角,请参阅Sensor.TYPE_ORIENTATION。

请参阅getDeclination()了解偏角

这假设赤纬是负的(正北的西侧)和它们的轴承>你的轴承。

如果赤纬是积极的,你的承担>他们的另一个选择:

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

我没有完全测试过,但是在纸上玩角度让我在这里。

© www.soinside.com 2019 - 2024. All rights reserved.