像指南针一样旋转 ImageView(在其他地方设置“北极”)
我对如何实现“个人指南针”感到困惑,即指向特定方位而不是标准“北极”的指南针......不幸的是,我当前的尝试结果是错误的(没有指向给定轴承)。它还与加速器连接,能够根据用户转动的方向动态调整自身。
这是我当前的尝试(更新箭头的 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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您的rotateImageView 函数应该可以正常工作,但是旋转计算中需要更改一些内容。
问题是bearingTo会给你一个从-180到180的范围,这会让事情有点混乱。我们需要将此值转换为 0 到 360 的范围以获得正确的旋转。
这是我们真正想要的表格,与bearingTo给我们的内容进行比较
即使bearingTo在-180到180的范围内,0仍然是正北,这将让我们进行这个计算:
如果我们添加一些虚拟值来测试我们的新公式:
我们现在已经整理出了方位角,让我们开始确定方位角!
您需要减去偏角而不是相加,因为当我们将手机直接指向正北时,我们希望方位角为 0,而不是将偏角添加到方位角上,这样当我们将手机指向正北时,方位角就会加倍到真正的北方。通过减去磁偏角(而不是加上磁偏角)来纠正此问题。
当我们现在将手机转向正北时,方位角将等于 0
您不再需要用于校正方位角的代码。
现在我们将继续计算实际旋转。但首先我将总结我们现在拥有的值类型并解释它们的真正含义:
bearTo = 从我们当前站立的点到目标位置的角度。
方位角 = 您将手机从正北方向旋转的角度。
也就是说,如果您将手机直接指向正北,我们确实希望箭头旋转 bearTo 设置的角度。如果您将手机指向与正北方向 45 度,我们希望箭头旋转的角度比 bearTo 的角度小 45 度。这让我们进行以下计算:
但是,如果我们输入一些虚拟值:
熊托 = 45;
方位角=180;
这意味着箭头应逆时针旋转 135 度。我们需要放入一个类似的 if 条件,就像我们对 bearTo 所做的那样!
您的方位文本、N、E、S 和 W 已关闭,因此我已在下面的最终方法中更正了它们。
您的 onSensorChanged 方法应如下所示:
Your rotateImageView function should work just fine, however there are some things that needs to be changed in your rotation calculations.
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
Even though the bearingTo is in the range -180 to 180, 0 is still true north which will leave us to this calculation:
If we add some dummy values to test our new formula:
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.
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.
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:
However, if we put in some dummy values:
bearTo = 45;
azimuth = 180;
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!
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:
您应该能够将矩阵设置为 ImageView,而不必每次都重新创建位图,并且呃..“标准化”(是这个词吗?)读数。
在上面的示例中,mLoc 是 GPS 提供商返回的位置,而 getBearing 返回当前行进方向以东偏北的度数。 item.mHeading 是使用 Location.bearingTo() 函数(使用 mLoc 和项目的位置)计算得出的。宽度和高度是图像视图的尺寸。
因此,请确保您的变量以度数而不是弧度为单位,并尝试“标准化”(将航向设置在 0-360 的范围内,而不是 -180-180 的范围内)。另外,如果结果相差 180 度,请确保您获得的是目标的方位,而不是目标到您的度数。
然后可以在具有 ScaleType.Matrix 的 ImageView 中设置上面的矩阵,
因为您围绕 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.
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
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.
一个周末我花了大约 40 个小时尝试做这件事。
屁股痛,希望我能减轻你的痛苦。
好吧,我警告你,这是一些丑陋的代码。
我很急于完成它,它没有命名方案,但我尽力为你做出最好的评论。
它被用来定位存放在田野中的大堆坚果。
使用手机当前的纬度和经度、目的地的纬度/经度、罗盘传感器和一些代数,我能够计算到目的地的方向。
纬度/经度和传感器读数是从 MainApplication 类中提取的。
这是 arrow.class 的一些代码,我用它在画布上向某个方向绘制箭头。
希望你能读懂我的代码...如果我有时间,我会把它做得更漂亮一点。
如果您需要任何解释,请告诉我。
-桑德尔先生
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.
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