像指南针一样旋转 ImageView(在其他地方设置“北极”)

发布于 2024-12-13 15:10:43 字数 3280 浏览 4 评论 0原文

我对如何实现“个人指南针”感到困惑,即指向特定方位而不是标准“北极”的指南针......不幸的是,我当前的尝试结果是错误的(没有指向给定轴承)。它还与加速器连接,能够根据用户转动的方向动态调整自身。

这是我当前的尝试(更新箭头的 onSensorChanged() 方法):

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("?"); 

        }

这是旋转 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 );
}

任何帮助都会非常感谢,因为我不太知道如何继续。我在尝试时得到的“读数”有些不准确,并且指向错误的方向。我是否做了一些非常糟糕的事情,或者我只是进行了一次非常糟糕的测试?

I'm stumped regarding how to implement a "personal compass", ie a compass that points to a specific bearing instead of the standard "north pole"... unfortunatly, my current attempt has come out wrong (doesn't point at the given bearing). It's also hooked up with the accelerator to be able to dynamically adjust itself based on which way the user is turning.

Here's my current attempt at it (the onSensorChanged()-method that updates the arrow):

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("?"); 

        }

And here's the method that rotates the 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 );
}

Any help would be much appreciated, as I don't quite know how to proceed. The "readings" I'm getting while trying it out is somewhat inaccurate and points in the wrong direction. Am I doing something really off, or did I just have a really bad test-run?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

早乙女 2024-12-20 15:10:43

您的rotateImageView 函数应该可以正常工作,但是旋转计算中需要更改一些内容。

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

问题是bearingTo会给你一个从-180到180的范围,这会让事情有点混乱。我们需要将此值转换为 0 到 360 的范围以获得正确的旋转。

这是我们真正想要的表格,与bearingTo给我们的内容进行比较

+-----------+--------------+
| bearingTo | Real bearing |
+-----------+--------------+
| 0         | 0            |
+-----------+--------------+
| 90        | 90           |
+-----------+--------------+
| 180       | 180          |
+-----------+--------------+
| -90       | 270          |
+-----------+--------------+
| -135      | 225          |
+-----------+--------------+
| -180      | 180          |
+-----------+--------------+

即使bearingTo在-180到180的范围内,0仍然是正北,这将让我们进行这个计算:

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

如果我们添加一些虚拟值来测试我们的新公式:

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

我们现在已经整理出了方位角,让我们开始确定方位角!

您需要减去偏角而不是相加,因为当我们将手机直接指向正北时,我们希望方位角为 0,而不是将偏角添加到方位角上,这样当我们将手机指向正北时,方位角就会加倍到真正的北方。通过减去磁偏角(而不是加上磁偏角)来纠正此问题。

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

当我们现在将手机转向正北时,方位角将等于 0

您不再需要用于校正方位角的代码。

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

现在我们将继续计算实际旋转。但首先我将总结我们现在拥有的值类型并解释它们的真正含义:

bearTo = 从我们当前站立的点到目标位置的角度。

方位角 = 您将手机从正北方向旋转的角度。

也就是说,如果您将手机直接指向正北,我们确实希望箭头旋转 bearTo 设置的角度。如果您将手机指向与正北方向 45 度,我们希望箭头旋转的角度比 bearTo 的角度小 45 度。这让我们进行以下计算:

float direction = bearTo - azimuth;

但是,如果我们输入一些虚拟值:
熊托 = 45;
方位角=180;

direction = 45 - 180 = -135;

这意味着箭头应逆时针旋转 135 度。我们需要放入一个类似的 if 条件,就像我们对 bearTo 所做的那样!

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

您的方位文本、N、E、S 和 W 已关闭,因此我已在下面的最终方法中更正了它们。

您的 onSensorChanged 方法应如下所示:

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

}

Your rotateImageView function should work just fine, however there are some things that needs to be changed in your rotation calculations.

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

The problem is that bearingTo will give you a range from -180 to 180, which will confuse things a bit. We will need to convert this value into a range from 0 to 360 to get the correct rotation.

This is a table of what we really want, comparing to what bearingTo gives us

+-----------+--------------+
| bearingTo | Real bearing |
+-----------+--------------+
| 0         | 0            |
+-----------+--------------+
| 90        | 90           |
+-----------+--------------+
| 180       | 180          |
+-----------+--------------+
| -90       | 270          |
+-----------+--------------+
| -135      | 225          |
+-----------+--------------+
| -180      | 180          |
+-----------+--------------+

Even though the bearingTo is in the range -180 to 180, 0 is still true north which will leave us to this calculation:

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

If we add some dummy values to test our new formula:

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

We've now sorted out the bearingTo, lets head on to the azimuth!

You need to substract the declination instead of adding it, as we want azimuth to be 0 when we point the phone directly at true north instead of having the declination added to the azimuth, which will then give us double the declination when we point the phone to true north. Correct this by subtracting the declination instead of adding it.

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

When we turn the phone to true north now, azimuth will then equal to 0

Your code for correcting the azimuth is no longer necessary.

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

We will now continue to the point of where we calculate the real rotation. But first i will summarize what type of values we have now and explaining what they really are:

bearTo = The angle from true north to the destination location from the point we're your currently standing.

azimuth = The angle that you've rotated your phone from true north.

By saying this, if you point your phone directly at true north, we really want the arrow to rotate the angle that bearTo is set as. If you point your phone 45 degrees from true north, we want the arrow to rotate 45 degrees less than what bearTo is. This leaves us to the following calculations:

float direction = bearTo - azimuth;

However, if we put in some dummy values:
bearTo = 45;
azimuth = 180;

direction = 45 - 180 = -135;

This means that the arrow should rotate 135 degrees counter clockwise. We will need to put in a similiar if-condition as we did with the bearTo!

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

Your bearing text, the N, E, S and W is off, so i've corrected them in the final method below.

Your onSensorChanged method should look like this:

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

}
悲歌长辞 2024-12-20 15:10:43

您应该能够将矩阵设置为 ImageView,而不必每次都重新创建位图,并且呃..“标准化”(是这个词吗?)读数。

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

在上面的示例中,mLoc 是 GPS 提供商返回的位置,而 getBearing 返回当前行进方向以东偏北的度数。 item.mHeading 是使用 Location.bearingTo() 函数(使用 mLoc 和项目的位置)计算得出的。宽度和高度是图像视图的尺寸。

因此,请确保您的变量以度数而不是弧度为单位,并尝试“标准化”(将航向设置在 0-360 的范围内,而不是 -180-180 的范围内)。另外,如果结果相差 180 度,请确保您获得的是目标的方位,而不是目标到您的度数。

然后可以在具有 ScaleType.Matrix 的 ImageView 中设置上面的矩阵,

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

因为您围绕 imageView 的中心点旋转(postRotate 中的宽度/2、高度/2),所以您的可绘制对象应该指向上方并且将在绘制时旋转,而不是每次都重新创建新的位图。

You should be able to set the matrix to the ImageView without having to recreate the bitmap each time, and er.. 'normalise' (is that the word?) the readings.

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

In the above example mLoc is a Location returned by a gps provider and getBearing returns the number of degrees east of north of the current direction of travel. item.mHeading has been calculated using the Location.bearingTo() function using mLoc and the item's location. width and height are the dimensions of the image view.

So, make sure your variables are in degrees and not radians, and try 'normalising' (getting headings into the range of 0-360 and not -180-180). Also, if the results are off by 180 degrees, make sure you're getting the bearingTo your target, rather than the degrees from your target to you.

The above matrix can then be set in an ImageView that has a ScaleType.Matrix

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

Since you're rotating around the centre point of the imageView (the width/2, height/2 in the postRotate), your drawable should be pointing upwards and will be rotated at draw time, rather than re-creating a new bitmap each time.

浊酒尽余欢 2024-12-20 15:10:43

一个周末我花了大约 40 个小时尝试做这件事。

屁股痛,希望我能减轻你的痛苦。

好吧,我警告你,这是一些丑陋的代码。
我很急于完成它,它没有命名方案,但我尽力为你做出最好的评论。

它被用来定位存放在田野中的大堆坚果。

使用手机当前的纬度和经度、目的地的纬度/经度、罗盘传感器和一些代数,我能够计算到目的地的方向。

纬度/经度和传感器读数是从 MainApplication 类中提取的。

这是 arrow.class 的一些代码,我用它在画布上向某个方向绘制箭头。

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

希望你能读懂我的代码...如果我有时间,我会把它做得更漂亮一点。

如果您需要任何解释,请告诉我。

-桑德尔先生

I spent about 40 hours one weekend trying to do this.

Pain in the butt, hopefully I can spare you that pain.

Ok, I am warning you, this is some ugly code.
I was in a pinch to finish it, it has no naming schemes, but i tried to comment it as best as I could for you.

It was used to locate large piles of nuts laying out in fields for storage

Using the phones current latitude and longitude, the lat/lon of the destination, the compass sensor, and some algebra, I was able to calculate the direction to the destination.

Lat/lon and sensor readings are pulled from the MainApplication class

This is some of the code for arrow.class, which I used to draw an arrow on a canvas towards a direction.

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

Hopefully you can manage to read my code... If I get time, I will make it a bit prettier.

If you need any explaining, let me know.

-MrZander

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文