IT이야기

Android에서 나침반 방위 / 방향 계산

cyworld 2021. 4. 27. 21:55
반응형

Android에서 나침반 방위 / 방향 계산


목적지 위치 (북쪽 대신)를 기준으로 내 방향을 표시하는 Google지도보기의 내 위치에 화살표를 표시하고 싶습니다.

a) 자력계와 가속도계의 센서 값을 사용하여 북쪽을 계산했습니다. Google지도보기에서 사용되는 나침반과 일치하기 때문에 이것이 정확하다는 것을 알고 있습니다.

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()
      );
      ...
   }
}

이것들로 무장하면 진북이 아닌 목적지 객체와 관련하여 향하고있는 위치를 표시하기 위해지도에 그릴 화살표의 각도를 계산합니다.

먼저 적위로 방향을 조정하십시오.

heading += geoField.getDeclination();

둘째, 진북이 아닌 대상에서 전화가 향하는 방향 (방향)을 오프셋해야합니다. 이것이 제가 붙은 부분입니다. 나침반에서 반환 된 표제 값은 휴대 전화가 가리키는 위치와 관련하여 자북의 위치 (진북의 동쪽 각도)를 나타내는 값을 제공합니다. 예를 들어 값이 -10이면 자북이 왼쪽으로 10 도라는 것을 알 수 있습니다. 방위는 진북의 동쪽 각도로 목적지의 각도를 제공합니다. 따라서 적위를 보정 한 후 아래 공식을 사용하여 원하는 결과를 얻을 수 있습니다.

heading = myBearing - (myBearing + heading); 

그런 다음 진북의 동쪽 각도 (-180 ~ +180)에서 일반 각도 (0 ~ 360)로 변환 할 수 있습니다.

Math.round(-heading / 360 + 180)

@Damian-아이디어는 매우 좋고 대답에 동의하지만 코드를 사용했을 때 잘못된 값이 있었기 때문에 직접 작성했습니다 (누군가 귀하의 의견에서 동일하게 말했습니다). 적위로 헤딩을 세는 것이 좋다고 생각하지만 나중에 다음과 같은 것을 사용했습니다.

heading = (bearing - heading) * -1;

Damian의 코드 대신 :

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

여기서 arrowViewImageView화살표 화상 및 100F의 파라미터로하여 postRotate) pivX 및 pivY이다.

누군가를 도울 수 있기를 바랍니다.


나침반의 화살표는 현재 위치에서 Kaaba ( 목적지 위치 ) 까지의 방향을 보여줍니다.

이 방식으로 bearingTo를 간단하게 사용할 수 있습니다 .bearing to는 현재 위치에서 목적지 위치까지의 직접적인 각도를 제공합니다.

  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 내부에 모든 코드를 작성해야합니다.

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를 의미하지만 목적지는 휴대 전화가 가리키는 가상 물체의 '인정 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-lon-to-utm

UTM 좌표는 간단한 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 ()에서 가져온 것이므로 LastKnownLocation이 사용되었습니다.

onLocationChanged에는 notifyDataSetChanged ();

이미지 회전 및 목록 행 색상을 설정하는 새로운 SimpleCursorAdapter.ViewBinder () 코드 (단일 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로 가져올 때 평소처럼 누군가가 유용하다고 생각하기를 바랍니다.


다음은 두 점 사이의 베어링 각도를 계산하는 코드입니다.

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

용어 : 진북과 자북의 차이를 편각이 아닌 "변동"이라고합니다. 나침반이 읽는 것과 자기 방향의 차이를 "편차"라고하며 방향에 따라 다릅니다. 나침반 스윙은 장치 오류를 식별하고 장치에 수정 기능이 내장 된 경우 수정을 적용 할 수 있습니다. 자기 나침반에는 모든 제목의 장치 오류를 설명하는 편차 카드가 있습니다.

Declination : Astro 네비게이션에서 사용되는 용어 : Declination은 위도와 같습니다. 별이 천구 적도에서 얼마나 멀리 떨어져 있는지보고합니다. 별의 적위를 찾으려면 별에서 천구의 적도까지 "똑바로 아래로"한 시간 원을 따르십시오. 시간 원을 따라 별에서 천구 적도까지의 각도가 별의 적위입니다.


나는 지금 그것을 알아내는 과정에 있지만 수학은 당신과 당신의 목표가 진북과 자북에 비해 지구상의 어디에 있는지에 달려있는 것처럼 보입니다. 예를 들면 :

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

나는 이것을 완전히 테스트하지는 않았지만 종이에 앵글을 가지고 노는 것은 나를 여기에 데려왔다.


이것은 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));

}

이것은 다른 사람들에게도 효과가 있기를 바랍니다.

참조 URL : https://stackoverflow.com/questions/4308262/calculate-compass-bearing-heading-to-location-in-android

반응형