Рассчитать направление компаса / курс на местоположение в Android

Я хочу отобразить стрелку в моем местоположении на карте Google, которая отображает мое направление относительно места назначения (вместо севера).

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

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

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

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

вопрос задан 29.11.2010
Damian
6512 репутация

12 ответов


  • 60 рейтинг

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

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

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

    myLocation.bearingTo(destLocation);
    

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

    Направление, возвращаемое от магнитометра + акселерометра, находится в градусах к востоку от истинного (магнитного) севера (от -180 до +180), поэтому вам нужно получить разницу между севером и магнитным севером для вашего местоположения. Эта разница является переменной в зависимости от того, где вы находитесь на земле. Вы можете получить с помощью класса GeomagnField.

    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, с репутацией 6512, 30.11.2010
  • 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).

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

    ответ дан CookieMonssster, с репутацией 451, 12.03.2013
  • 2 рейтинг

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

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

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

    подшипник даст вам диапазон от -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 код здесь

    
    
    
    
    
    
    
    
    
    
    
    ответ дан Muhammad Kashif Arif, с репутацией 122, 29.05.2017
  • 2 рейтинг

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

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

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

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

    РЕДАКТИРОВАТЬ: Я получил немного математики неправильно. , , вычтите меньшее из подшипников из большего, затем вычтите результат из 360, чтобы получить угол, под которым можно нарисовать указатель на экране.

    ответ дан Squonk, с репутацией 42097, 29.11.2010
  • 1 рейтинг

    Я знаю, что это немного старый, но ради людей, как я из 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 ()

    onLocationChanged содержит вызов notifyDataSetChanged ();

    код также из нового SimpleCursorAdapter. ViewBinder () для установки поворота изображения и цветов списка (применяется только в одном столбце, обратите внимание). , ,

    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!

    ответ дан dno, с репутацией 21, 9.11.2014
  • 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 );
    
    ответ дан Sileria, с репутацией 11101, 11.09.2011
  • 1 рейтинг

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

    Конвертировать GPS в UTM

    http: // www. IBM. ком / DeveloperWorks / Java / библиотека / J-coordconvert / Http: // StackOverflow. ru / questions / 176137 / java-convert-lat-lon-to-utm

    UTM координаты упрощают X Y 2D

    Рассчитать угол между обоими местоположениями UTM

    http: // forums. Groundspeak. ком / GC / индекс. PHP? showtopic = 146917

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

    Так что, что бы вы ни поворачивали, делайте Север, просто вычтите этот угол

    Если обе точки имеют угол UTM 45º, а вы находитесь на 5º к востоку от севера, ваша стрелка будет указывать на 40º к северу

    ответ дан Pedro, с репутацией 11, 30.06.2011
  • 0 рейтинг

    Вот код для расчета угла подшипника между двумя точками:

    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);
    
    ответ дан APP, с репутацией 31, 16.05.2018
  • 0 рейтинг

    Это лучший способ определения направления на объекте на карте Google: - & gt;

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

    ответ дан Ramkailash, с репутацией 2750, 13.06.2016
  • 0 рейтинг

    Формула даст направление, используя координаты начальной точки до конечной точки см.

    Следующий код даст вам подшипник (угол между 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));
    

    }

    Это работает для меня, надеюсь, что это будет работать и для других

    ответ дан K K, с репутацией 331, 29.09.2016
  • 0 рейтинг

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

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

    См. Датчик. TYPE_ORIENTATION для азимута.

    См. GetDeclination () для определения склонения

    Это предполагает, что склонение является отрицательным (к западу от истинного севера), а их отношение & gt; yourBearing.

    Если склонение положительное и вашBearing & gt; их носящий другой вариант:

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

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

    ответ дан tricknology, с репутацией 634, 17.08.2012
  • 0 рейтинг

    Терминология: Разница между ИСТИННЫМ севером и Магнитным севером известна как «вариация», а не склонение. Разница между показаниями вашего компаса и магнитным курсом называется «отклонением» и зависит от курса. Размах компаса определяет ошибки устройства и позволяет применять исправления, если устройство имеет встроенную коррекцию. Магнитный компас будет иметь карту отклонения, которая описывает ошибку устройства на любом курсе.

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

    ответ дан Simon, с репутацией 9, 8.08.2012