Estoy perplejo en cuanto a cómo implementar una «brújula personal», es decir, una brújula que apunta a un determinado teniendo en lugar de la estándar de «polo norte»… por desgracia, mi intento ha salido mal (no en el punto en el cojinete dado). También se enganchó con el acelerador que se puede ajustar de forma dinámica propia basada en el modo en el que el usuario está girando.

Aquí está mi intento (el onSensorChanged()-método que actualiza la flecha):

public void onSensorChanged( SensorEvent event ) {
//If we don't have a Location, we break out
if ( LocationObj == null ) return;
float azimuth = event.values[0];
float baseAzimuth = azimuth;
GeomagneticField geoField = new GeomagneticField( Double
.valueOf( LocationObj.getLatitude() ).floatValue(), Double
.valueOf( LocationObj.getLongitude() ).floatValue(),
Double.valueOf( LocationObj.getAltitude() ).floatValue(),
System.currentTimeMillis() );
azimuth += geoField.getDeclination(); //converts magnetic north into true north
//Correct the azimuth
azimuth = azimuth % 360;
//This is where we choose to point it
float direction = azimuth + LocationObj.bearingTo( destinationObj );
rotateImageView( arrow, R.drawable.arrow, direction );
//Set the field
if( baseAzimuth > 0 && baseAzimuth < 45 ) fieldBearing.setText("S");
else if( baseAzimuth >= 45 && baseAzimuth < 90 ) fieldBearing.setText("SW");
else if( baseAzimuth > 0 && baseAzimuth < 135 ) fieldBearing.setText("W");
else if( baseAzimuth > 0 && baseAzimuth < 180 ) fieldBearing.setText("NW");
else if( baseAzimuth > 0 && baseAzimuth < 225 ) fieldBearing.setText("N");
else if( baseAzimuth > 0 && baseAzimuth < 270 ) fieldBearing.setText("NE");
else if( baseAzimuth > 0 && baseAzimuth < 315 ) fieldBearing.setText("E");
else if( baseAzimuth > 0 && baseAzimuth < 360 ) fieldBearing.setText("SE");
else fieldBearing.setText("?"); 
}

Y aquí está el método que gira el ImageView (rotateImageView()):

private void rotateImageView( ImageView imageView, int drawable, float rotate ) {
//Decode the drawable into a bitmap
Bitmap bitmapOrg = BitmapFactory.decodeResource( getResources(),
drawable );
//Get the width/height of the drawable
DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = bitmapOrg.getWidth(), height = bitmapOrg.getHeight();
//Initialize a new Matrix
Matrix matrix = new Matrix();
//Decide on how much to rotate
rotate = rotate % 360;
//Actually rotate the image
matrix.postRotate( rotate, width, height );
//recreate the new Bitmap via a couple conditions
Bitmap rotatedBitmap = Bitmap.createBitmap( bitmapOrg, 0, 0, width, height, matrix, true );
//BitmapDrawable bmd = new BitmapDrawable( rotatedBitmap );
//imageView.setImageBitmap( rotatedBitmap );
imageView.setImageDrawable(new BitmapDrawable(getResources(), rotatedBitmap));
imageView.setScaleType( ScaleType.CENTER );
}

Cualquier ayuda sería muy apreciada, como yo no sé muy bien cómo proceder. Las «lecturas» me estoy poniendo al intentar cabo es algo impreciso y apunta en la dirección equivocada. Estoy haciendo algo realmente fuera de, o acabo de tener una muy mala test-run?

  • Puedo preguntar si este método de actualización de la imagen es demasiado CPU-costy? Como cuando sigo el código, la pantalla es muy lento y no puedo siquiera acercar o alejar el zoom.
  • Yo no noto nada de eso, y que fue casi dos años atrás. Podría ser la combinación de las cosas? 🙂
InformationsquelleAutor karllindmark | 2011-11-02

3 Comentarios

  1. 54

    Su rotateImageView función debe funcionar bien, sin embargo hay algunas cosas que necesita ser cambiado en su rotación de los cálculos.

    //This is where we choose to point it
    float direction = azimuth + LocationObj.bearingTo( destinationObj );
    rotateImageView( arrow, R.drawable.arrow, direction );

    El problema es que bearingTo le dará un rango de -180 a 180, que se confunden un poco las cosas. Vamos a necesitar para convertir este valor en un rango de 0 a 360 para conseguir la correcta rotación.

    Esta es una tabla de lo que realmente queremos, en comparación con lo que bearingTo nos da

    +-----------+--------------+ 
    | bearingTo | Real de rodamiento | 
    +-----------+--------------+ 
    | 0 | 0 | 
    +-----------+--------------+ 
    | 90 | 90 | 
    +-----------+--------------+ 
    | 180 | 180 | 
    +-----------+--------------+ 
    | -90 | 270 | 
    +-----------+--------------+ 
    | -135 | 225 | 
    +-----------+--------------+ 
    | -180 | 180 | 
    +-----------+--------------+ 
    

    Aunque el bearingTo está en el rango -180 a 180, 0 es todavía el norte verdadero, que nos dejará este cálculo:

    //Store the bearingTo in the bearTo variable
    float bearTo = LocationObj.bearingTo( destinationObj );
    //If the bearTo is smaller than 0, add 360 to get the rotation clockwise.
    if (bearTo < 0) {
    bearTo = bearTo + 360;
    }

    Si añadimos algunos valores ficticios para probar nuestra nueva fórmula:

    float bearTo = -100;
    //This will now equal to true
    if (-100 < 0) {
    bearTo = -100 + 360 = 360 - 100 = 260;
    }

    Ahora hemos resuelto el bearingTo, permite a la cabeza en el azimut!

    Necesita para que reste de la declinación en lugar de agregar, como queremos azimut a ser 0 cuando apuntamos directamente el teléfono en el norte verdadero, en lugar de tener la declinación añadido para el azimut, la cual nos dará el doble de la declinación cuando queremos apuntar el teléfono hacia el norte verdadero. Corregir esta restando la declinación en lugar de la adición.

    azimuth -= geoField.getDeclination(); //converts magnetic north into true north

    Cuando gire el teléfono hacia el norte verdadero ahora, azimut será entonces igual a 0

    El código para corregir el azimut es que ya no es necesario.

    //Remove /uncomment this line
    azimuth = azimuth % 360;

    Ahora continuaremos hasta el punto de donde calculamos el verdadero movimiento de rotación. Pero primero voy a resumir lo que el tipo de los valores que tenemos ahora y explicar lo que realmente son:

    bearTo = El ángulo desde el norte verdadero para la ubicación de destino desde el punto somos su actualidad permanente.

    azimut = El ángulo que ha girado el teléfono desde el norte verdadero.

    Por decir esto, si el punto en el teléfono directamente en el norte verdadero, realmente queremos que la flecha para girar el ángulo que bearTo es establecer como. Si el punto en el teléfono de 45 grados desde el norte verdadero, queremos que la flecha para girar 45 grados menos de lo que bearTo es. Esto nos deja a los siguientes cálculos:

    float direction = bearTo - azimuth;

    Sin embargo, si ponemos en algunos valores ficticios:
    bearTo = 45;
    azimut = 180;

    direction = 45 - 180 = -135;

    Esto significa que la flecha debe girar 135 grados en sentido antihorario. Tendremos que poner en una similar, si la condición de tal como hicimos con la bearTo!

    //If the direction is smaller than 0, add 360 to get the rotation clockwise.
    if (direction < 0) {
    direction = direction + 360;
    }

    Rodamiento de texto, el N, E, S y W está apagado, así que lo he corregido en el último método a continuación.

    Su onSensorChanged método debería tener este aspecto:

    public void onSensorChanged( SensorEvent event ) {
    //If we don't have a Location, we break out
    if ( LocationObj == null ) return;
    float azimuth = event.values[0];
    float baseAzimuth = azimuth;
    GeomagneticField geoField = new GeomagneticField( Double
    .valueOf( LocationObj.getLatitude() ).floatValue(), Double
    .valueOf( LocationObj.getLongitude() ).floatValue(),
    Double.valueOf( LocationObj.getAltitude() ).floatValue(),
    System.currentTimeMillis() );
    azimuth -= geoField.getDeclination(); //converts magnetic north into true north
    //Store the bearingTo in the bearTo variable
    float bearTo = LocationObj.bearingTo( destinationObj );
    //If the bearTo is smaller than 0, add 360 to get the rotation clockwise.
    if (bearTo < 0) {
    bearTo = bearTo + 360;
    }
    //This is where we choose to point it
    float direction = bearTo - azimuth;
    //If the direction is smaller than 0, add 360 to get the rotation clockwise.
    if (direction < 0) {
    direction = direction + 360;
    }
    rotateImageView( arrow, R.drawable.arrow, direction );
    //Set the field
    String bearingText = "N";
    if ( (360 >= baseAzimuth && baseAzimuth >= 337.5) || (0 <= baseAzimuth && baseAzimuth <= 22.5) ) bearingText = "N";
    else if (baseAzimuth > 22.5 && baseAzimuth < 67.5) bearingText = "NE";
    else if (baseAzimuth >= 67.5 && baseAzimuth <= 112.5) bearingText = "E";
    else if (baseAzimuth > 112.5 && baseAzimuth < 157.5) bearingText = "SE";
    else if (baseAzimuth >= 157.5 && baseAzimuth <= 202.5) bearingText = "S";
    else if (baseAzimuth > 202.5 && baseAzimuth < 247.5) bearingText = "SW";
    else if (baseAzimuth >= 247.5 && baseAzimuth <= 292.5) bearingText = "W";
    else if (baseAzimuth > 292.5 && baseAzimuth < 337.5) bearingText = "NW";
    else bearingText = "?";
    fieldBearing.setText(bearingText);
    }
    • Gracias Chris, marcado como respuesta. 🙂
    • Gran respuesta! Un ahorro de tiempo.
    • Después de algunas pruebas, he encontrado esto puede ser apagado cuando el teléfono gira en el modo horizontal.
    • Vea aquí cómo compensar a: android desarrolladores.blogspot.ca/2010/09/… especialmente el poco acerca de «La Correcta Revisión».
    • Interesante puntero @RyanR!
    • Puedo preguntar si este método de actualización de la imagen es demasiado CPU-costy? Como cuando sigo el código, la pantalla es muy lento y no puedo siquiera acercar o alejar el zoom.
    • Muy útil. Gracias
    • Esta es una gran respuesta con un estudio detallado de su caso

  2. 3

    Usted debe ser capaz de establecer la matriz para el ImageView sin tener que volver a crear el mapa de bits cada vez, y er.. ‘normalizar’ (que es la palabra?) las lecturas.

    float b = mLoc.getBearing();
    if(b < 0)
    b = 360 + b;
    float h = item.mHeading;
    if(h < 0)
    h = 360 + h;
    float r = (h - b) - 360;
    matrix.reset();
    matrix.postRotate(r, width/2, height/2);

    En el ejemplo anterior mLoc es una Ubicación devuelto por un proveedor de gps y getBearing devuelve el número de grados al este del norte de la actual dirección del viaje. elemento.mHeading ha sido calculado utilizando la Ubicación.bearingTo() la función con mLoc y la ubicación del elemento. la anchura y la altura son las dimensiones de la vista de la imagen.

    Así, asegúrese de que las variables se expresan en grados y no en radianes, y tratar de ‘normalización’ (obtención de títulos en el rango de 0-360 y no -180-180). También, si los resultados están fuera de los 180 grados, asegúrese de que está recibiendo la bearingTo su destino, en lugar de los grados de su destino para usted.

    La anterior matriz puede definirse en un ImageView que tiene un ScaleType.Matriz de

    imageView.setMatrix(matrix);
    imageview.setScaleType(ScaleType.Matrix);

    Ya que estás girando alrededor del centro de la imageView (ancho/2, height/2 en el postRotate), su imagen debe estar apuntando hacia arriba y será girado a dibujar tiempo, en lugar de volver a crear un nuevo mapa de bits cada vez.

    • Gracias por la respuesta FunkTheMonk, yo no sabía acerca de la Matriz cosa; voy a probarlo! Sin embargo, elegir Chris la respuesta de abajo como el correcto (así la adjudicación de él la recompensa). Gracias de nuevo por tu ayuda. 🙂
  3. 1

    Pasé cerca de 40 horas de un fin de semana tratando de hacer esto.

    Dolor en el trasero, espero que pueda evitar que el dolor.

    Ok, yo estoy advirtiendo que, esto es algo feo código.
    Yo estaba en un apuro para terminar, no tiene esquemas de asignación de nombres, pero he tratado de comentar es lo mejor que pude para usted.

    Fue utilizado para localizar grandes montones de nueces establecen en los campos de almacenamiento de

    El uso de los teléfonos actuales de la latitud y la longitud, la latitud y longitud del lugar de destino, el sensor de brújula, y algunos de álgebra, yo era capaz de calcular la dirección hacia el destino.

    Lat/lon y las lecturas del sensor se sacó de la MainApplication clase

    Este es parte del código para arrow.class que he usado para dibujar una flecha en un lienzo hacia una dirección.

        //The location you want to go to//
    //"Given North"
    double lat=0;
    double lon=0;
    //////////////////////////////////
    protected void onDraw(Canvas canvas) {
    //Sensor values from another class managing Sensor
    float[] v = MainApplication.getValues();
    //The current location of the device, retrieved from another class managing GPS
    double ourlat=  MainApplication.getLatitudeD();
    double ourlon=  MainApplication.getLongitudeD(); 
    //Manually calculate the direction of the pile from the device
    double a= Math.abs((lon-ourlon));
    double b= Math.abs((lat-ourlat));
    //archtangent of a/b is equal to the angle of the device from 0-degrees in the first quadrant. (Think of a unit circle)
    double thetaprime= Math.atan(a/b);
    double theta= 0;
    //Determine the 'quadrant' that the desired location is in
    //ASTC (All, Sin, Tan, Cos)  Determines which value is positive
    //Gotta love Highschool algebra
    if((lat<ourlat)&&(lon>ourlon)){//-+ 
    //theta is 180-thetaprime because it is in the 2nd quadrant
    theta= ((Math.PI)-thetaprime); 
    //subtract theta from the compass value retrieved from the sensor to get our final direction
    theta=theta - Math.toRadians(v[0]);
    }else if((lat<ourlat)&&(lon<ourlon)){//--
    //Add 180 degrees because it is in the third quadrant
    theta= ((Math.PI)+thetaprime);
    //subtract theta from the compass value retreived from the sensor to get our final direction
    theta=theta - Math.toRadians(v[0]);
    }else if((lat>ourlat)&&(lon>ourlon)){ //++
    //No change is needed in the first quadrant
    theta= thetaprime; 
    //subtract theta from the compass value retreived from the sensor to get our final direction
    theta=theta - Math.toRadians(v[0]);
    }else if((lat>ourlat)&&(lon<ourlon)){ //+-
    //Subtract thetaprime from 360 in the fourth quadrant
    theta= ((Math.PI*2)-thetaprime);
    //subtract theta from the compass value retreived from the sensor to get our final direction
    theta=theta - Math.toRadians(v[0]);
    }
    canvas.drawBitmap(_bitmap, 0, 0, paint);
    float[] results = {0}; //Store data
    Location.distanceBetween(ourlat, ourlon, lat, lon, results);
    try{
    //Note, pileboundary is a value retreived from a database
    //This changes the color of the canvas based upon how close you are to the destination
    //Green < 100 (or database value), Yellow < (100)*2, Otherwise red
    if((results[0])<(pileboundary==0?100:pileboundary)){
    _canvas.drawColor(Color.GREEN);
    }else if((results[0])<(pileboundary==0?100:pileboundary)*2){
    _canvas.drawColor(Color.YELLOW);
    }else{
    _canvas.drawColor(Color.rgb(0xff, 113, 116)); //RED-ish
    }
    //Draw the distance(in feet) from the destination
    canvas.drawText("Distance: "+Integer.toString((int) (results[0]*3.2808399))+ " Feet", 3, height-3, textpaint);
    }catch(IllegalArgumentException ex){
    //im a sloppy coder 
    }
    int w = canvas.getWidth();
    int h = height;
    int x = w / 2; //put arrow in center
    int y = h / 2;
    canvas.translate(x, y);
    if (v != null) {
    //Finally, we rotate the canvas to the desired direction
    canvas.rotate((float)Math.toDegrees(theta));
    }
    //Draw the arrow!
    canvas.drawPath(thearrow, paint);
    }   
    //Some of my declarations, once again sorry :P
    GeomagneticField gf;
    Bitmap _bitmap;
    Canvas _canvas;
    int _height;
    int _width; 
    Bitmap b;
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //Get the current GeomagneticField (Should be valid until 2016, according to android docs)
    gf = new GeomagneticField((float)lat,(float)lon,(float)MainApplication.getAltitude(),System.currentTimeMillis());
    _height = View.MeasureSpec.getSize(heightMeasureSpec);
    _width = View.MeasureSpec.getSize(widthMeasureSpec);
    setMeasuredDimension(_width, _height);
    _bitmap = Bitmap.createBitmap(_width, _height, Bitmap.Config.ARGB_8888);
    _canvas = new Canvas(_bitmap);
    b=Bitmap.createBitmap(_bitmap);
    drawBoard();
    invalidate();
    }
    //Here is the code to draw the arrow 
    thearrow.moveTo(0, -50);
    thearrow.lineTo(-20, 50);
    thearrow.lineTo(0, 50);
    thearrow.lineTo(20, 50);
    thearrow.close();
    thearrow.setFillType(FillType.EVEN_ODD);

    Esperemos que se puede administrar a leer mi código… Si me da tiempo, voy a hacer que sea un poco más bonito.

    Si usted necesita alguna explicación, hágamelo saber.

    -MrZander

    • Usted sabe lo que… yo miraba a su nombre… se hizo el BF3 Battlelog aplicación. Hice el BF3 estadísticas retriever. Acabo de ayudar a mi competidor 😛
    • Hola Alexander, gracias por compartir el código. Sin embargo, he seleccionado la de Chris respuesta que él realmente me ayudó a salir fuera de Stackoverflow, pero yo le dije a publicar la solución en el sitio web de modo que yo pudiera marcar su respuesta como «la respuesta» para más información. Le agradezco que se tome su tiempo, aunque, como voy a aprender una cosa o dos de tu post.:-)
    • Y sí, es muy gracioso que nos encontraríamos aquí de todos los lugares. 🙂
    • ¿tienes el código completo de este ejemplo?
    • Esto es en gran parte obsoleto ahora. Por mucho tiempo hemos dado refactorizado lejos de este código.

Dejar respuesta

Please enter your comment!
Please enter your name here