Androidの場所へのコンパスベアリング /見出しを計算します
-
29-09-2019 - |
質問
目的地の位置(北ではなく)に比べて私の方向を表示するGoogleマップビューで自分の場所に矢印を表示したいと思います。
a)磁力計と加速度計からのセンサー値を使用して北に計算しました。 Googleマップビューで使用されているコンパスと並んでいるため、これは正しいことを知っています。
b)mylocation.bearingto(destlocation)を使用して、自分の場所から目的地の場所への初期ベアリングを計算しました。
最後のステップがありません。これらの2つの値(A&B)から、携帯電話が宛先の場所に比べて指している方向を取得するためにどの式を使用しますか?
調整された心の助けに感謝します!
解決
わかりました、私はこれを理解しました。これをやろうとしている他の人のためにあなたは必要です:
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度であることを知っています。ベアリングは、True Northの東の度で目的地の角度を与えます。したがって、偏角を補償した後、以下の式を使用して、目的の結果を得ることができます。
heading = myBearing - (myBearing + heading);
その後、True North(-180から+180)の東の程度から通常の程度(0〜360)に変換する必要があります。
Math.round(-heading / 360 + 180)
他のヒント
@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);
}
どこ arrowView
は ImageView
矢印の画像と100Fパラメーターがあります postRotate
pivxとpivyです)。
私は誰かを助けてくれることを願っています。
これでコンパスの矢印はあなたの場所からの方向を示しています カーバ(宛先の場所)
この方法でベアリングを簡単に使用できます。
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 |
+-----------+--------------+
したがって、このコードをベアトの後に追加する必要があります
// 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>
私は地図の読み取り /ナビゲーションの専門家ではありませんが、確かに「方向」は絶対的であり、相対的または実際には、それ自体が固定 /絶対的なnまたはsに関連しています。
例:あなたと目的地の間に描かれた想像上の線が「絶対」SE(磁気Nと比較して135度のベアリング)に対応しているとします。携帯電話がNWを指していると仮定します - 地平線上の想像上のオブジェクトから目的地まで想像上の線を描くと、それはあなたの場所を通過し、180度の角度を持っています。コンパスの意味で180度は実際にはsを指しますが、目的地はあなたの携帯電話が指している想像上のオブジェクトの「デュース」ではなく、さらに、その想像上のポイントに旅行した場合、あなたの目的地はまだのSEですあなたが引っ越した場所。
実際には、180度のラインは、実際に目的地が電話(およびおそらくあなた)が指している方法に比べて「あなたの後ろ」であることを伝えます。
ただし、想像上の点から目的地への線の角度を計算する場合(場所を通過する)、目的地に向かってポインターを引き付けることがあなたが望むものです。想像上のオブジェクトの絶対的なベアリングからの目的地と否定(存在する場合)を無視します。たとえば、nw -seは315-135 = 180ですので、ポインターを描画して、「あなたの後ろ」を示す画面の下部をポイントします。
編集: 数学が少し間違っています...より大きい方からベアリングの小さい方を差し引いてから、360から結果を減算して、画面にポインターを描く角度を取得します。
あなたが同じタイムゾーンにいる場合
GPSをUTMに変換します
http://www.ibm.com/developerworks/java/library/j-coordconvert/ http://stackoverflow.com/questions/176137/java-convert-lat-to-utm
UTM座標は、SimpleS XY 2Dを取得します
両方のUTM位置間の角度を計算します
http://forums.groundspeak.com/gc/index.php?showtopic=146917
これはあなたが北を見ているかのように方向を与えます
だからあなたが関連するものは何でも北にしますこの角度を差し引くだけです
両方の点がUTM 45度角度を持ち、北から5º東にある場合、矢印は北の40ºを指します
これが私がそれをした方法です:
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 );
私はこれが少し古いことを知っていますが、ここで完全な答えを見つけられなかった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;
}
私はそれが単純化される可能性があると賭けて喜んでいますが、それはうまくいきます!このコードはnew simplecursoradapter.viewbinder()からのものであるため、lastluckinglocationが使用されました
onLocationChangedには、NotifyDataSeTchAged()への呼び出しが含まれています。
また、新しいsimplecursoradapter.viewbinder()からのコードは、画像の回転とリストローの色を設定します(単一のcolumnindex mind youにのみ適用されます)...
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に連れて行ってくれたときと同じように、誰かがこれを私と同じように便利だと思うことを願っています!
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時間の円をたどります。星から時間の円に沿った天の赤道への角度は、星の偏りです。
私は今それを理解する過程にありますが、それはまるで数学があなたとあなたのターゲットが真と磁気北と比較して地球上の場所に依存しているかのようです。例えば:
float thetaMeThem = 0.0;
if (myLocation.bearingTo(targetLocation) > myLocation.getBearing()){
thetaMeThem = myLocation.bearingTo(targetLocation) - azimuth + declination;}
Azimuthについては、sensor.type_orientationを参照してください。
Dedinationについては、getDeclination()を参照してください
これは、偏角が負(真の北の西)であり、それらのbear延>あなたのbearingであると仮定します。
偏角が肯定的であり、あなたの有力な場合、彼らのベアリング別のオプション:
float thetaMeThem = 0.0;
if (myLocation.bearingTo(targetLocation) < myLocation.getBearing()){
thetaMeThem = azimuth - (myLocation.bearingTo(targetLocation) - declination);}
私はこれを完全にテストしていませんが、紙の上で角度で遊ぶことは私をここに連れて行ってくれました。
これは、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-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));
}
これは私のために働きますそれが他の人も働くことを願っています