web-dev-qa-db-ja.com

Androidの場所への方位と方位を計算します

Googleマップビューの場所に矢印を表示し、目的地の場所(北ではなく)に対する相対的な方向を表示します。

a)磁力計と加速度計のセンサー値を使用して北を計算しました。 Googleマップビューで使用されるコンパスと一致するため、これが正しいことはわかっています。

b)myLocation.bearingTo(destLocation)を使用して、現在地から目的地までの初期方位を計算しました。

最後のステップがありません。これら2つの値(a&b)から、電話が目的地の位置を基準に指している方向を取得するためにどの式を使用しますか?

心を癒す助けを感謝します!

53
Damian

わかった。これをしようとする他の人には、以下が必要です。

a)見出し:ハードウェアコンパスからの見出し。これは、magnetic northの東の角度です

b)方位:あなたの場所から目的地までの方位。これは、true northの東の角度です。

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)
64
Damian

@Damian-アイデアはとてもよく、答えに同意しますが、あなたのコードを使用したとき、間違った値を持っていたので、自分でこれを書きました(誰かがあなたのコメントで同じことを言った)。赤緯で見出しを数えるのは良いと思いますが、後で私はそのようなものを使用しました:

heading = (bearing - heading) * -1;

ダミアンのコードの代わりに:

heading = myBearing - (myBearing + heading); 

0から360で-180から180に変更:

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

ここで、arrowViewImageViewで、矢印画像とpostRotateの100fパラメーターはpivXおよびpivYです)。

私は誰かを助けることを願っています。

16
CookieMonssster

これで、コンパスの矢印はあなたの場所からKaabadestination Location)への方向を示します

あなたはこのように簡単にbearingToを使用することができます。

  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が提供するものと比較して、本当に欲しいものの表です

+-----------+--------------+
| 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内にすべてのコードを記述する必要があります。

キブラコンパスの方向の完全なコードはこちら

 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>

私は地図の読み取り/ナビゲーションなどの専門家ではありませんが、確かに「方向」は絶対的なものであり、相対的ではありません。実際には、N/S自体が固定/絶対です。

例:あなたと目的地の間に描かれた想像上の線が「絶対」SE(磁気Nに対して135度の方位)に対応するとします。電話機がNWを指していると仮定します。地平線上の架空のオブジェクトから目的地まで架空の線を引くと、位置を通過して180度の角度になります。コンパスの意味での180度は実際にはSを指しますが、目的地はあなたの電話が指している架空の物体の「期限S」ではなく、さらにその想像上の点に移動した場合、目的地はまだあなたが移動した場所。

実際には、180度の線は、目的地が電話(およびおそらくあなた)が指している方法に対して「あなたの後ろ」にあることを実際に示しています。

ただし、目的地に向かってポインターを描画するために、架空のポイントから目的地までの線の角度を計算する(位置を通過する)場合は、単に(絶対的な)方位を減算します架空のオブジェクトの絶対方位からの宛先。否定を無視します(存在する場合)。たとえば、NW-SEは315-135 = 180ですので、画面の下部を指すようにポインターを描画して、「あなたの後ろ」を示します。

編集:数学が少し間違っています...大きい方から小さい方のベアリングを引き、360から結果を差し引いて、画面にポインターを描く角度を取得します。

2
Squonk

2点間の方位角を計算するコードは次のとおりです。

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

私はこれが少し古いことを知っていますが、ここで完全な答えを見つけられなかったGoogleの私のような人々のために。カスタムリストビュー内に矢印を配置するアプリからの抜粋を次に示します。

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

私はそれが簡素化される可能性があると確信していますが、それは機能します!このコードは新しいSimpleCursorAdapter.ViewBinder()からのものであるため、LastKnownLocationが使用されました

onLocationChangedには、notifyDataSetChanged()への呼び出しが含まれています。

また、新しいSimpleCursorAdapter.ViewBinder()のコードを使用して、画像の回転とリスト行の色を設定します(単一の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); 
}

私が磁気センサーのドラマを廃止したと思っているなら、私の場合は面倒の価値はありませんでした。 Googleが私をstackoverflowに連れて行ったとき、私が通常するのと同じように誰かがこれを役に立つと思うことを願っています!

1
dno

ここに私がそれをやった方法があります:

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

同じタイムゾーンにいる場合

GPSをUTMに変換する

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

UTM座標により、シンプルなX Y 2Dが得られます

両方のUTMロケーション間の角度を計算する

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

これは、あなたが北を見ているかのように方向を与えます

あなたが関連して回転するものは何でも、この角度を引くだけです

両方のポイントがUTM45ºの角度で、北の5º東にいる場合、矢印は北の40ºを指します。

1
Pedro

これは、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
Ramkailash

式は、開始点から終了点までの座標を使用して方位を与えます 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));

}

これは私にとってはうまくいきます

0
K K

私は今、それを解明する過程にありますが、数学は、あなたとあなたの目標が真の磁北と比較して地球上のどこにあるかに依存するようです。例えば:

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

方位角については、Sensor.TYPE_ORIENTATIONを参照してください。

偏角についてはgetDeclination()を参照してください

これは、赤緯が負(真北の西)で、Bearing> yourBearingであると仮定しています。

赤緯が正で、yourBearing> theirBearingの別のオプションの場合:

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

私はこれを完全にテストしていませんが、紙の上の角度で遊んで、ここに来ました。

0
tricknology

用語:真北と磁北の違いは、偏角ではなく「変動」として知られています。コンパスの読み取り値と磁気方位の違いは「偏差」と呼ばれ、方位によって異なります。コンパススイングはデバイスのエラーを識別し、デバイスに修正が組み込まれている場合は修正を適用できます。磁気コンパスには、方位のデバイスエラーを説明する偏差カードがあります。

赤緯:Astroナビゲーションで使用される用語:赤緯は緯度に似ています。星が赤道からどれだけ離れているかを報告します。星の赤緯を調べるには、星から天の赤道までの「真っ直ぐ」の時間円をたどります。星から赤道への時間円に沿った角度は、星の赤緯です。

0
Simon