Рассчитайте Compass Purning / Goodting to Location в Android
-
29-09-2019 - |
Вопрос
Я хочу отобразить стрелку в моем месте на представлении карты Google, который отображает мое направление относительно места назначения (вместо севера).
а) Я рассчитал север, используя значения датчика от магнитометра и акселерометра. Я знаю, что это правильно, потому что он направляется с компасом, используемым на видео Google Map.
б) я рассчитал начальный подшипник из моего местоположения в место назначения, используя mylocation.pearingto (destlocation);
Я скучаю по последнему шагу; Из этих двух значений (A & B), какую формулу я использую, чтобы получить направление, в котором телефон указывает относительно места назначения?
Цените любую помощь для придвоенного ума!
Решение
Хорошо, я понял это. Для тех, кто пытается сделать это, вам нужно:
а) заголовок: ваша направляющаяся из аппаратного компаса. Это в градусах к востоку от магнитный север
б) Подшипник: подшипник из вашего местоположения до места назначения. Это в градусах к востоку от истинный север.
myLocation.bearingTo(destLocation);
в) склонение: разница между истинным севером и магнитным севером
Заголовок, который возвращается из магнитометра + акселерометр, находится в градусах к востоку от истинного (магнитного) северного (от -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)
Другие советы
@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
является ImageView
со стрелкой и параметрами 100F в postRotate
это Pivx и ypy).
Я надеюсь, что я кому-то помогу.
В этом стрелка на компасе показывает направление от вашего местоположения до Кааба(Место назначения)
Вы можете просто использовать 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
Полный код здесь для направления Cibla Compass
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 (подшипник 135 градусов относительно магнитного n). Теперь предположим, что ваш телефон указывает NW - если вы нарисуете воображаемую строку из воображаемого объекта на горизонте до пункта назначения, он пройдет через ваше местоположение и имеет угол на 180 градусов. Сейчас на 180 градусов в смысле компаса фактически относится к S, но пункт назначения не «обусловлен» воображаемого объекта, ваш телефон указывает на и, кроме того, если вы путешествовали на этот воображаемый момент, ваш пункт назначения все равно будет где вы переехали.
В действительности, линия на 180 градусов на самом деле говорит вам, что пункт назначения находится «позади вас» по сравнению с тем, как наказывает телефон (и, по -видимому, вы).
Сказав это, однако, при расчете угла линии от воображаемой точки до пункта назначения (проходящего через ваше место), чтобы нарисовать указатель к пункту назначения - это то, что вы хотите ... просто вычтите (абсолютный) подшипник пункт назначения от абсолютного подшипника воображаемого объекта и игнорирует отрицание (если присутствует). Например, северо -запад - SE 315 - 135 = 180, так что нарисуйте указатель в точку в нижней части экрана, указывающего «позади вас».
РЕДАКТИРОВАТЬ: Я получил математику немного неправильно ... вычтите меньший из подшипников из более крупных, а затем вычтите результат из 360, чтобы получить угол, чтобы нарисовать указатель на экране.
Если вы находитесь на одном и том же часовом поясе
Преобразовать GPS в UTM
http://www.ibm.com/developerworks/java/library/j-coordconvert/ http://stackoverflow.com/questions/176137/java-convert-lat-lon-to-utm
UTM координаты получают вам STRPLES 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;
}
Я готов поспорить, что это может быть упрощено, но это работает! Lastknownlocation использовался, поскольку этот код был из нового SimpleCursorAdapter.ViewBinder ()
onLocationChangedChanged содержит вызов для уведомленияDataSethetheted ();
Код также от New SimpleCursorAdapter.viewbinder () для установки вращения изображения и цветов Listrow (применяется только в одном столбце -Index 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!
Вот код для расчета угла подшипника между двумя точками:
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);
Терминология: разница между истинным севером и магнитным севером известна как «вариация», а не склонение. Разница между тем, что читает ваш компас и магнитным заголовком, известна как «отклонение» и изменяется в зависимости от заголовка. Свинг компаса идентифицирует ошибки устройства и позволяет применять исправления, если устройство имеет встроенное коррекцию. Магнитный компас будет иметь карту отклонения, которая описывает ошибку устройства на любом заголовке.
Склонение: термин, используемый в Astro Navigation: склонение похоже на широту. Он сообщает, как далеко находится звезда от небесного экватора. Чтобы найти склонение звезды, следуйте по часу кругу «прямо вниз» от звезды к небесному экватору. Угол от звезды до небесного экватора вдоль часового круга - это склонение звезды.
Я сейчас нахожусь в процессе выяснения этого, но кажется, что математика зависит от того, где вы и ваша цель находятся на земле относительно истинного и магнитного севера. Например:
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);}
Я не проверил это полностью, но играя с углами на бумаге, получил меня здесь.
Это лучший способ обнаружить подшипник из объекта местоположения на карте 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));
}
Это работает для меня, надеюсь, что это будет работать и другие