2010-11-29 4 views
46

Я хочу отобразить стрелку в своем местоположении на карте, отображающей мое направление относительно места назначения (вместо севера).Вычислить подшипник компаса/заголовок местоположения в Android

a) Я рассчитал север, используя значения датчика от магнитометра и акселерометра. Я знаю, что это правильно, потому что оно совпадает с компасом, используемым в представлении «Карта Google».

b) Я вычислил исходный подшипник из своего местоположения в место назначения с помощью myLocation.bearingTo (destLocation);

Я пропустил последний шаг; из этих двух значений (a & b) какую формулу я использую, чтобы получить направление, в котором указывает телефон относительно места назначения?

Цените любую помощь для умышленного ума!

+0

Вы можете проверить это сообщение блога на датчиках и ориентации экрана: http://android-developers.blogspot.com/2010/ 09/one-screen-turn-deserves-another.html – 2010-11-29 22:28:05

+0

Спасибо, я уже прочитал эту статью. К сожалению, это не касается проблемы, поставленной в вопросе. – Damian 2010-11-29 23:40:00

+1

Я нашел решение, в котором я работал хорошо http://stackoverflow.com/questions/7978618/rotating-an-imageview-like-a-compass-with-the-north-pole-set- где-то еще – dharan 2012-09-17 11:21:44

ответ

58

Хорошо, я понял это. Для тех, кто пытается это сделать, вам необходимо:

a) заголовок: ваш заголовок с аппаратного компаса. Это в градусах к востоку от магнитные север

b) подшипник: подшипник от вашего местоположения до места назначения. Это в градусах к востоку от true север.

myLocation.bearingTo(destLocation); 

с) склонением: разница между истинным и магнитным севером

заголовком, который возвращается из магнитометра + accelermometer в градусах к востоку от истинного (магнитного) севера (от -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) 
2

Я не являюсь экспертом в области чтения карт/навигации и т. Д., Но, безусловно, «направления» являются абсолютными, а не относительными или в действительности, они относятся к N или S, которые сами являются фиксированными/абсолютными.

Пример: предположим, что воображаемая линия, проведенная между вами и вашим пунктом назначения, соответствует «абсолютному» SE (подшипник 135 градусов относительно магнитного N). Теперь предположим, что ваш телефон указывает на NW - если вы нарисуете воображаемую линию от воображаемого объекта на горизонте до места назначения, он пройдет через ваше местоположение и будет иметь угол 180 градусов. Теперь 180 градусов в смысле компаса на самом деле относится к S, но пункт назначения не является «из-за S» воображаемого объекта, на который указывает ваш телефон, и, более того, если вы путешествовали в эту мнимую точку, ваше место назначения по-прежнему будет SE куда вы переехали.

В действительности, линия на 180 градусов на самом деле говорит вам, что пункт назначения находится «позади вас» относительно способа указания телефона (и предположительно вас).

Сказав это, однако, если подсчитать угол линии от мнимой точки до пункта назначения (проходящего через ваше местоположение), чтобы нарисовать указатель к месту назначения, вы хотите ... просто вычесть (абсолютное) отношение пункта назначения от абсолютного значения мнимого объекта и игнорирование отрицания (если оно имеется). например, NW - SE составляет 315 - 135 = 180, поэтому нарисуйте указатель так, чтобы он указывал в нижней части экрана, указывающий «позади вас».

EDIT: Я немного ошибся в математике ... вычитайте меньший из подшипников из большего, а затем вычтите результат из 360, чтобы получить угол, на который можно нарисовать указатель на экране.

+0

Спасибо за вашу помощь , Мне удалось разобраться в этом и дал ответ ниже для кого-то другого, столкнувшегося с той же проблемой. – Damian 2010-11-30 17:44:24

+0

Только 2 очка против 37, которые получил (неправильный) принятый ответ? О, хорошо, спасибо за сообщение Squonk, помог мне много. – cjm 2015-01-20 07:40:34

1

Если вы находитесь на том же часовом поясе

Преобразовать GPS для UTM

http://www.ibm.com/developerworks/java/library/j -coordconve к.т./ http://stackoverflow.com/questions/176137/java-convert-lat-lon-to-utm

UTM координаты получить вам Simples XY 2D

Вычислить угол между обоих местах

UTM

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

Это дает направление, как если бы вы смотрели на север

так что вы вращать связанные сделать North просто вычесть угол

Если обе точки имеют UTM 45º угол градусов, и вы 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); 
0

Терминология: Разница между TRUE north и Magnetic North известна как «вариация», а не отклонение. Разница между чтением вашего компаса и магнитным заголовком называется «отклонение» и изменяется с заголовком. Перекос компаса идентифицирует ошибки устройства и позволяет исправить поправки, если устройство имеет встроенную коррекцию. Магнитный компас будет иметь карту отклонения, которая описывает ошибку устройства для любого заголовка.

Склонность: Термин, используемый в навигации по Астро: склонение подобно широте. Он сообщает, как далеко звезда от небесного экватора. Чтобы найти склонение звезды, следуйте часовой круг «прямо вниз» от звезды до небесного экватора. Угол от звезды до небесного экватора по кругу часов - это склонение звезды.

0

Я сейчас разбираюсь в этом, но кажется, что математика зависит от того, где вы и ваша цель находитесь на земле относительно истинного и магнитного Севера. Например:

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

См. Sensor.TYPE_ORIENTATION для азимута.

См getDeclination() для склонения

Это предполагает склонение отрицательно (к западу от истинного севера) и theirBearing> yourBearing.

Если склонение положительно и yourBearing> theirBearing другой вариант:

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

это полностью, но играть с углами на бумаге у меня здесь, я не проверял.

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: ImageView с рисунком стрелки и параметрами 100f в postRotate является pivX и pivY).

Надеюсь, я помогу кому-нибудь.

1

Я знаю, что это немного старо, но ради таких людей, как я, от google, которые не нашли здесь полного ответа. Вот некоторые выдержки из моего приложения, которые помещают стрелки внутри пользовательских ListView ....

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

Я готов поспорить, что это может быть упрощено, но это работает! LastKnownМобильное расположение было использовано, так как этот код был из нового 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); 
} 

В случае, если вам интересно, я покончила с магнитные сенсорные драмы, не стоило хлопот в моем случае. Я надеюсь, что кто-то найдет это полезным, как обычно, когда google приносит мне stackoverflow!

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

формула даст подшипник, используя координаты начальной точки до конца точка 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)); 

}

Это работает для меня надеюсь, что он будет работать кто-нибудь найти те же

1

В этом стрелку на компасе показывает направление от вашего местоположения до Каабы (назначения Адреса)

вы можете просто использовать подшипники. Таким образом, вы получите прямой угол от вашего местоположения до места назначения.

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

Полный код для справки o е 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> 
Смежные вопросы