在Android中计算指南针方位/标题到位置
我想在我的位置显示一个箭头在谷歌地图视图,显示我的方向相对于目标位置(而不是北)。
a)我使用磁力计和加速计的传感器值计算了北。 我知道这是正确的,因为它与Google Map视图中使用的指南针alignment。
b)我已经使用myLocation.bearingTo(destLocation)计算了从我的位置到目标位置的初始方位。
我错过了最后一步; 从这两个值(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() ); ... } }
用这些武器来计算在地图上绘制的箭头的angular度,以显示与目标对象相对的位置,而不是真正的北面。
首先用偏angular调整你的标题:
heading += geoField.getDeclination();
其次,您需要将电话面对的方向(标题)从目标目的地偏移,而不是正北。 这是我被卡住的部分。 从指南针返回的标题值为您提供了一个值,该值与电话所指向的位置有关,其中描述了磁性北极的位置(以正北方向为度数)。 所以,例如,如果数值是-10,你知道磁北向左是10度。 方位为您提供真北方以东度数的目的地angular度。 因此,在您已经补偿了赤字之后,您可以使用下面的公式来获得期望的结果:
heading = myBearing - (myBearing + heading);
然后,您需要将真北(-180至+180)以东的度数转换为正常度数(0至360):
Math.round(-heading / 360 + 180)
@Damian – 这个想法非常好,我同意答案,但是当我使用你的代码时,我错误的值,所以我自己写了这个(有人告诉你的意见)。 我认为,用偏angular来衡量标题是好的,但后来我用了这样的东西:
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
,而arrowView
中的100f参数是pivx和pivy)。
我希望我会帮助别人。
我不是地图阅读/导航方面的专家,但是“方向”绝对是绝对的,而不是相对的,实际上,他们是相对于N或S本身是固定的/绝对的。
例如:假设在你和你的目的地之间画一条假想的线与'绝对'SE(相对于磁N的方位135度)相对应。 现在假设您的手机指向NW – 如果您从地平线上的虚拟物体到目的地画一条想象线,它将通过您的位置并具有180度的angular度。 现在指南针的180度实际上是指S,但是目的地不是你的手机指向的假想对象的“应有的S”,而且,如果你前往这个假想点,你的目的地仍然是你搬到哪里。
实际上,180度线实际上告诉你,相对于电话(可能是你)指向的方式,目的地在你后面。
话虽如此,如果计算从虚构点到目的地(通过您的位置)的线的angular度,以便绘制一个指向您的目的地的指针,就是您想要的…简单地减去(绝对)方位假想对象的绝对方位的目标,并忽略否定(如果存在)。 例如NW – SE是315 – 135 = 180,所以画指针指向屏幕底部,指示“在你后面”。
编辑:我得到的math有点错了…从较大的减去轴承的较小,然后从360减去结果得到的angular度,在其中画在屏幕上的指针。
如果你在同一个时区
将GPS转换为UTM
http://www.ibm.com/developerworks/java/library/j-coordconvert/ http://stackoverflow.com/questions/176137/java-convert-lat-lon-to-utm
UTM坐标为您提供简单的XY 2D
计算两个UTM位置之间的angular度
http://forums.groundspeak.com/GC/index.php?showtopic=146917
这给出了方向,就好像你在向北看
所以无论你旋转相关的北只是减去这个angular度
如果两个点的UTM都是45度angular,而你在北方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 );
我知道这是有点老,但为了像我这样的人从谷歌谁没有find一个完整的答案在这里。 这里是我的应用程序,把箭头里面的自定义列表视图的一些摘录….
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!
在这罗盘上的箭头显示从你的位置到天房的方向( 目的地位置 )
你可以简单的使用bearingTo,这样就可以给你从你的位置到目的地的直接angular度
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
完整的代码是在这里为朝拜指南针的方向
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>
术语:TRUE北和磁北之间的差异被称为“变化”而不是倾斜。 你的指南针读数和磁头之间的差异被称为“偏差”,并随着航向而变化。 罗盘摆动识别设备错误,并允许应用更正,如果设备有内置校正。磁罗盘将有一个偏差卡,描述任何航向设备错误。
赤纬:天文导航中使用的一个术语:赤纬就像纬度。 它报告一颗恒星距离天球赤道有多远。 要find恒星的偏angular,从恒星沿着“直下”直到天球赤道。 沿着小时圆从恒星到天赤道的angular度是恒星的赤纬。
我现在正在研究它的过程,但是好像这个math取决于你和你的目标在地球上相对于真北极的位置。 例如:
float thetaMeThem = 0.0; if (myLocation.bearingTo(targetLocation) > myLocation.getBearing()){ thetaMeThem = myLocation.bearingTo(targetLocation) - azimuth + declination;}
有关方位angular,请参阅Sensor.TYPE_ORIENTATION。
请参阅getDeclination()获取偏差
假定赤纬是负的(真正的北方的西边)和他们的承担。
如果赤字是积极的,你的承诺另一种select:
float thetaMeThem = 0.0; if (myLocation.bearingTo(targetLocation) < myLocation.getBearing()){ thetaMeThem = azimuth - (myLocation.bearingTo(targetLocation) - declination);}
我还没有完全testing过,但是在纸上的angular度玩弄了我。
这是从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之间的angular度)
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));
}
这是对我的作品,希望它会find相同的工作