Calculez le relèvement / Cap de la boussole à destination de L'Androïde
je veux afficher une flèche à mon emplacement sur une vue de carte de google qui affiche ma direction relative à un emplacement de destination (au lieu de Nord).
a) j'ai calculé le Nord en utilisant les valeurs des capteurs du magnétomètre et de l'accéléromètre. Je sais que c'est correct parce qu'il s'aligne avec la boussole utilisée sur la vue de carte de Google.
b) j'ai calculé le relèvement initial de mon emplacement à l'emplacement de destination en utilisant myLocation.bearingTo (destination);
je manque la dernière étape; de ces deux valeurs (a & b) quelle formule dois-je utiliser pour obtenir la direction dans laquelle le téléphone pointe par rapport à l'endroit de destination?
appréciez toute aide pour un esprit embrouillé!
12 réponses
Ok, j'ai compris. Pour quelqu'un d'autre essayant de faire ceci vous avez besoin:
a) cap: votre Cap à partir du compas matériel. C'est en degrés à l'est de magnétique nord
B) relèvement: relèvement de votre emplacement à l'emplacement de destination. C'est en degrés à l'est de vrai nord.
myLocation.bearingTo(destLocation);
c) déclinaison: la différence entre le nord vrai et magnétique Nord
le cap qui est retourné du magnétomètre + accelermomètre est en degrés à l'est Du Vrai (magnétique) nord (-180 à +180) donc vous devez obtenir la différence entre le nord et le nord magnétique pour votre emplacement. Cette différence est variable selon l'endroit où vous êtes sur terre. Vous pouvez obtenir en utilisant la classe 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()
);
...
}
}
armé de ces vous calculez l'angle de la flèche pour dessiner sur votre carte pour montrer où vous êtes face par rapport à votre objet de destination plutôt que par rapport au vrai nord.
ajuster D'abord votre titre avec la déclinaison:
heading += geoField.getDeclination();
Deuxièmement, vous devez décaler la direction dans laquelle le téléphone fait face (Cap) à partir de la destination cible plutôt que le nord vrai. C'est la partie que j'ai coincé sur. L'en-tête valeur renvoyée par la boussole donne une valeur qui décrit où le nord magnétique est (en degrés à l'est du nord vrai) rapport à l'endroit où le téléphone est orienté. Par exemple, si la valeur est -10, vous savez que le nord magnétique est à 10 degrés sur votre gauche. Le roulement vous donne l'angle de votre destination en degrés à l'est du nord vrai. Ainsi, après avoir compensé la déclinaison, vous pouvez utiliser la formule ci-dessous pour obtenir le résultat désiré:
heading = myBearing - (myBearing + heading);
vous voudrez alors convertir des degrés à l'est du vrai nord (-180 à + 180) en degrés normaux (0 à 360):
Math.round(-heading / 360 + 180)
@Damian-l'idée est très bonne et je suis d'accord avec la réponse, mais quand j'ai utilisé votre code, j'ai eu de mauvaises valeurs, donc j'ai écrit cela sur mon propre (quelqu'un a dit la même chose dans vos commentaires). Je pense que compter le titre avec la déclinaison est bien, mais plus tard j'ai utilisé quelque chose comme ça:
heading = (bearing - heading) * -1;
au lieu du code de Damian:
heading = myBearing - (myBearing + heading);
et en changeant -180 à 180 pour 0 à 360:
private float normalizeDegree(float value){
if(value >= 0.0f && value <= 180.0f){
return value;
}else{
return 180 + (180 + value);
}
et puis, quand vous voulez rotation de votre flèche vous pouvez utiliser le code comme ceci:
private void rotateArrow(float angle){
Matrix matrix = new Matrix();
arrowView.setScaleType(ScaleType.MATRIX);
matrix.postRotate(angle, 100f, 100f);
arrowView.setImageMatrix(matrix);
}
où arrowView
est ImageView
avec image de flèche et 100F paramètres dans postRotate
est pivX et pivY).
j'espère aider quelqu'un.
Je ne suis pas un expert en lecture de carte / navigation et ainsi de suite mais sûrement 'directions' sont absolu et pas relatif ou en réalité, ils sont relatifs à N ou S qui eux-mêmes sont fixes/absolu.
exemple: supposons qu'une ligne imaginaire tracée entre vous et votre destination correspond à un SE 'absolu' (un relèvement de 135 degrés par rapport à n magnétique). Supposons maintenant que votre téléphone pointe NW - si vous dessinez une ligne imaginaire à partir d'un objet imaginaire à l'horizon pour votre destination, elle passera par votre emplacement et aura un angle de 180 degrés. Maintenant 180 degrés dans le sens d'une boussole se réfère effectivement à S mais la destination n'est pas "due S" de l'objet imaginaire que votre téléphone pointe et, de plus, si vous avez voyagé à ce point imaginaire, votre destination serait toujours SE de l'endroit où vous avez déménagé.
en réalité, la ligne à 180 degrés indique en fait que la destination est "derrière vous" par rapport à la façon dont le téléphone (et probablement vous) vers.
ayant dit que, cependant, si le calcul de l'angle d'une ligne à partir du point imaginaire à votre destination (en passant par votre emplacement) afin de tirer un pointeur vers votre destination est ce que vous voulez...il suffit de soustraire le relèvement (absolu) de la destination du relèvement absolu de l'objet imaginaire et d'ignorer une négation (si présente). par exemple, NW-SE est de 315-135 = 180 donc tirer le pointeur pour pointer au bas de la l'écran indiquant "derrière vous".
EDIT: j'ai eu l'Maths un peu mal...soustrayez le plus petit des roulements du plus grand puis soustrayez le résultat de 360 pour obtenir l'angle dans lequel dessiner le pointeur sur l'écran.
dans cette flèche sur la boussole montre la direction de votre emplacement à Kaaba ( destination Location )
vous pouvez utiliser simplement en supportant de cette façon.roulement à vous donnera l'angle de votre emplacement à l'emplacement de destination
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 vous donnera une plage de -180 à 180, ce qui va embrouiller les choses un peu. Nous aurons besoin de convertir cette valeur dans une plage de 0 pour 360 pour obtenir la bonne rotation.
Ceci est un tableau de ce que nous voulons vraiment, en comparant à ce que bearingTo nous donne
+-----------+--------------+
| bearingTo | Real bearing |
+-----------+--------------+
| 0 | 0 |
+-----------+--------------+
| 90 | 90 |
+-----------+--------------+
| 180 | 180 |
+-----------+--------------+
| -90 | 270 |
+-----------+--------------+
| -135 | 225 |
+-----------+--------------+
| -180 | 180 |
+-----------+--------------+
nous devons donc ajouter ce code après 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;
}
vous devez implémenter le SensorEventListener et ses fonctions (onSensorChanged, onAcurracyChabge) et écrire tout le code à l'intérieur de onSensorChanged
le code complet est ici pour la Direction de Qibla 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;
}
le code xml est ici
<?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>
Si vous êtes sur le même fuseau horaire
convertir GPS en UTM
http://www.ibm.com/developerworks/java/library/j-coordconvert/ http://stackoverflow.com/questions/176137/java-convert-lat-lon-to-utm
UTM coordonnées get you a simples X Y 2D
calculer l'angle entre les deux emplacements UTM
http://forums.groundspeak.com/GC/index.php?showtopic=146917
cela donne la direction comme si vous regardiez vers le nord
donc, quel que soit l'angle de rotation, faites-le vers le Nord en soustrayant cet angle
si les deux points ont un angle UTM de 45º et que vous êtes à 5º à l'est du nord, votre flèche pointera à 40º du Nord
Voici comment je l'ai fait:
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 );
je sais que c'est un peu vieux mais pour le bien des gens comme moi de google qui n'ont pas trouvé une réponse complète ici. Voici quelques extraits de mon application qui ont mis les flèches dans un listview personnalisé....
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;
}
- je prêt à parier qu'il pourrait être simplifié, mais il fonctionne! La dernière localisation connue a été utilisée puisque ce code était tiré de New SimpleCursorAdapter.ViewBinder ()
onLocationChanged contient un appel à notifyDataSetChanged();
code également de nouveau SimpleCursorAdapter.ViewBinder () pour définir la rotation de l'image et les couleurs de listrow (seulement appliqué dans un colonnindex simple vous Esprit)...
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);
}
au cas où vous vous demandiez si j'ai fait disparaître les drames du capteur magnétique, ça n'en valait pas la peine dans mon cas. J'espère que quelqu'un trouvera cela aussi utile que d'habitude quand google m'amène à stackoverflow!
terminologie: la différence entre le nord vrai et le nord magnétique est connue sous le nom de "variation" et non de déclinaison. La différence entre ce que votre compas lit et le cap magnétique est appelée "déviation" et varie selon le cap. Une balançoire compas identifie les erreurs de l'appareil et permet des corrections à appliquer si l'appareil a une correction intégrée. Un compas magnétique aura une carte de déviation qui décrit l'erreur de l'appareil sur n'importe quel Cap.
déclinaison : Un terme utilisé dans la navigation Astro : la déclinaison est comme la latitude. Il indique la distance entre une étoile et l'équateur céleste. Pour trouver la déclinaison d'une étoile suivre un cercle d'une heure "tout droit" de l'étoile à l'équateur céleste. L'angle de l'étoile à l'équateur céleste le long du cercle des heures est la déclinaison de l'étoile.
je suis en train de le calculer maintenant, mais il semble que les maths dépendent de l'endroit où vous et votre cible êtes sur la terre par rapport au nord vrai et magnétique. Par exemple:
float thetaMeThem = 0.0;
if (myLocation.bearingTo(targetLocation) > myLocation.getBearing()){
thetaMeThem = myLocation.bearingTo(targetLocation) - azimuth + declination;}
Voir Capteur.TYPE_ORIENTATION for azimuth.
voir déclinaison getDeclination ()
cela suppose que la déclinaison est négative (à l'ouest du vrai nord) et que leur port > votre port.
si la déclinaison est positive et votre porteur > leur porteur une autre option:
float thetaMeThem = 0.0;
if (myLocation.bearingTo(targetLocation) < myLocation.getBearing()){
thetaMeThem = azimuth - (myLocation.bearingTo(targetLocation) - declination);}
Je ne l'ai pas testé complètement mais jouer avec les angles sur le papier m'a amené ici.
C'est la meilleure façon de détecter un relèvement d'un objet de localisation sur Google Map: ->
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);
code pour le calcul de l'angle de roulement entre deux points:
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);
}
appel à la fonction:
float angle = CalculateBearingAngle(lat1, lon1, lat2, lon2);
la formule donnera le roulement en utilisant les coordonnées du point de départ au point final voir
"le code suivant vous donnera le roulement (angle entre 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));
}
Cela fonctionne pour moi, espérons que cela fonctionnera autres