I want to change the OSMdroid MapView orientation to face the direction which the user is going (calculated with Location.bearingTo between the previous and current user location on each onLocationChanged, and converted into normal degrees instead of the -180/180° East of True North degrees).
This direction is correct, I'm rotating an arrow image towards this direction and it points towards the right direction without fail.
However, when I want to orientate the MapView to these userDirection using the setMapOrientation method (documented here), this isn't working as I want it to be. When I orientate the map towards the user's direction, the arrow image should always be pointing north, right? Because this is want I want to achieve: to make it seem like the arrow is always pointing forward (like a GPS tracker: your location on GPS is always represented by an icon going forward, my arrow is pointing to all kinds of directions because the map orientation is wrong).
I'm guessing the osmdroid.MapView orientation is expecting another sort of degree value, but I've tried converting back to East of True North degrees, didn't work. Or my logic is completely wrong and it is working correctly.
How do set the orientation for the MapView so that it is always facing the user's current direction, so that the arrow is always pointing forward (and not going backwards, right or left, ... )?
I think what you are referring to as is "True North" orientation of Map using the compass True North. For this you need the device Compass or Sensor Listener to get the direction, after getting the heading you need to set it for the MapView. Here is the Snippet which is very helpful.
private void compassHeadingUp() {
if(enableCompassHeadUp){
mSensorManager.registerListener(mySensorEventListener,
SensorManager.SENSOR_ORIENTATION,
SensorManager.SENSOR_DELAY_FASTEST);
} else {
mSensorManager.unregisterListener(mySensorEventListener);
mDirection = 0;
}
}
public SensorListener mySensorEventListener = new SensorListener(){
#Override
public void onAccuracyChanged(int arg0, int arg1) {
}
#Override
public void onSensorChanged(int sensor, float[] values) {
synchronized (this) {
float mHeading = values[0];
if(Math.abs(mDirection-mHeading) > Constance.ROTATION_SENSITIVITY){
mMapView.setMapOrientation(-mHeading);
mDirection = mHeading;
}
Matrix matrix = new Matrix();
mCompassImageView.setScaleType(ScaleType.MATRIX);
matrix.postRotate((float) -mHeading, mCompassImageView.getDrawable().getBounds().width()/2, mCompassImageView.getDrawable().getBounds().height()/2);
//Set your Arrow image view to the matrix
mCompassImageView.setImageMatrix(matrix);
}
}
};
I have solved this issue by inverting degrees like this:
float bearing = location.getBearing();
float t = (360 - bearing);
if (t < 0) {
t += 360;
}
if (t > 360) {
t -= 360;
}
//help smooth everything out
t = (int) t;
t = t / 5;
t = (int) t;
t = t * 5;
mapOSM.setMapOrientation(t);
Related
I've generated 3D overlay using jMonkey in my Android app. Everything works fine - my ninja model is walking in loop. Awsome !
Now I want to rotate camera according to direction of the phone. I thought compass is the best way BUT unfortunatly I have a lot of problems. So here I go
I've created method that is invoked in activity
public void rotate(float x, float y, float z) {
Log.d(TAG, "simpleUpdate: Nowa rotacja: " + y);
newX = x;
newY = y;
newZ = z;
newPosition = true;
}
in 'simpleUpdate' method I've managed it this way
if(newPosition && ninja != null) {
Log.d(TAG, "simpleUpdate: rotacja: " + newY);
ninja.rotate((float)Math.toRadians(newX), (float)Math.toRadians(newY), (float)Math.toRadians(newZ));
newPosition = false;
}
in my activity I'm checking if phone moved
if(lastAzimuth != (int)azimuthInDegress) {
lastAzimuth = (int)azimuthInDegress;
I cast to int so the distortion won't be so big problem
if ((com.fixus.towerdefense.model.SuperimposeJME) app != null) {
((com.fixus.towerdefense.model.SuperimposeJME) app).rotate(0f, azimuthInDegress, 0f);
}
At the moment I want to rotate it only in Y axis
Now the main problem is that the rotations is more like jump that rotation. When I move my phone a bit and I have 6 degrees diffrence (i see this in my log) the model is rotated like for 90 degrees and he turns back. This has nothing to do with rotation or change taken from my compas.
Any ideas ?
UPDATE
I think I got it. Method rotate, rotates from current state with value I've set. So it looks more like old Y rotate + new value. So I'm setting the diffrence between current value and old value and it now look almoust fine. Is it the good way ?
Lets say you have the acceleration readings in all the 3 dimensions i.e X, Y and Z. How do you infer using the readings the phone was tilted left or right? The readings get generated every 20ms.
I actually want the logic of inferring the tilt from the readings. The tilt needs to be smooth.
A tilt can be detected in a sort of diferent ways. You can take into account 1 axis, 2 axis, or the 3 axis. Depending on how accurate you want it, and how much you feel like fighting with maths.
If you use only one axis, it is quite simple. Think the mobile is completely horizontal, and you move it like this:
using just one axis, lets say, axis x, will be enough, since you can detect accurately a change in that axis position, since even any small movement will do a change in the axis.
But, if your application is only reading that axis, and the user has the phone almost vertical, the difference in x axis will be really small even rotating the phone a big angle.
Anyways,for applications that only need coarse resolution, a single-axis can be used.
Referring to basic trigonometry, the projection of the gravity vector on the x-axis produces an output acceleration equal to the sine of the angle between the accelerometer x-axis and the horizon.
This means that having the values of an axis (those are acceleration values) you can calculate the angle in which the device is.
this means that the value given to you by the sensor, is = to 9,8 * sine of the angle, so doing the maths you can get the actual angle.
But don't worry, you don't even have to do this. Since the values are more or less proportional, as you can see in the table below, you can work directly with the value of the sensor, without taking much care of what angle represents, if you don't need it to be much accurate, since a change in that value means a proportional change in the angle, so with a few test, you will find out how big should be the change in order to be relevant to you.
So, if you take the value over the time, and compare to each other, you can figure out how big the rotation was. For this,
you consider just one axis. this will be axis X.
write a function to get the difference in the sensor value for that axis between one function call, and the next
Decide a maximum time and a minimum sensor difference, that you will consider a valid movement (e.g. a big rotation is good but only if it is fast enough, and a fast movement is good only if the difference in the angle is big enough)
if you detect two measurements that accomplish those conditions, you take note of half tilt done (in a boolean for instance), and start measuring again, but now, the new reference value is the value that was considered half tilt.
if the last difference was positive, now you need a negative difference, and if the last difference was negative, now you need a positive difference; this is, coming back. so start taking values comparing the new reference value with the new values coming from the sensor, and see if one accomplish what you decided in point 3.
if you find a valid value (accomplishing value difference and time conditions ), you have a tilt. But if you dont get a good value and the time is consumed, you reset everything: let your reference value be the last one, reset the timers, reset the half-tilt-done boolean to false, and keep measuring.
I hope this is good enough for you. For sure you can find some libraries or code snippets to help you out with this, but i think is good, as you say, to know the logic of inferring the tilt from the readings
The pictures was taken from this article, wich i recomend to read if you want to improve the accuracy and consider 2 o 3 axis for the tilt
The commonsware Sensor Monitor app does a pretty good job with this. It converts the sensor readouts to X, Y, Z values on each sensor reading, so it's pretty easy from there to determine which way the device is moving.
https://github.com/commonsguy/cw-omnibus/tree/master/Sensor/Monitor
Another item worth noting (from the Commonsware book):
There are four standard delay periods, defined as constants on the
SensorManager class:
SENSOR_DELAY_NORMAL, which is what most apps would use for broad changes, such as detecting a screen rotating from portrait to
landscape
SENSOR_DELAY_UI, for non-game cases where you want to update the UI continuously based upon sensor readings
SENSOR_DELAY_GAME, which is faster (less delay) than SENSOR_DELAY_UI, to try to drive a higher frame rate
SENSOR_DELAY_FASTEST, which is the “firehose” of sensor readings, without delay
You can use the accelerometer and magnetic field sensor to accomplish this. You can call this method in your OnSensorChanged method to detect if the phone was tilt upwards. This currently only works if the phone is held horizontally. Check the actual blog post for a more complete solution.
http://www.ahotbrew.com/how-to-detect-forward-and-backward-tilt/
public boolean isTiltUpward()
{
if (mGravity != null && mGeomagnetic != null)
{
float R[] = new float[9];
float I[] = new float[9];
boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
if (success)
{
float orientation[] = new float[3];
SensorManager.getOrientation(R, orientation);
/*
* If the roll is positive, you're in reverse landscape (landscape right), and if the roll is negative you're in landscape (landscape left)
*
* Similarly, you can use the pitch to differentiate between portrait and reverse portrait.
* If the pitch is positive, you're in reverse portrait, and if the pitch is negative you're in portrait.
*
* orientation -> azimut, pitch and roll
*
*
*/
pitch = orientation[1];
roll = orientation[2];
inclineGravity = mGravity.clone();
double norm_Of_g = Math.sqrt(inclineGravity[0] * inclineGravity[0] + inclineGravity[1] * inclineGravity[1] + inclineGravity[2] * inclineGravity[2]);
// Normalize the accelerometer vector
inclineGravity[0] = (float) (inclineGravity[0] / norm_Of_g);
inclineGravity[1] = (float) (inclineGravity[1] / norm_Of_g);
inclineGravity[2] = (float) (inclineGravity[2] / norm_Of_g);
//Checks if device is flat on ground or not
int inclination = (int) Math.round(Math.toDegrees(Math.acos(inclineGravity[2])));
/*
* Float obj1 = new Float("10.2");
* Float obj2 = new Float("10.20");
* int retval = obj1.compareTo(obj2);
*
* if(retval > 0) {
* System.out.println("obj1 is greater than obj2");
* }
* else if(retval < 0) {
* System.out.println("obj1 is less than obj2");
* }
* else {
* System.out.println("obj1 is equal to obj2");
* }
*/
Float objPitch = new Float(pitch);
Float objZero = new Float(0.0);
Float objZeroPointTwo = new Float(0.2);
Float objZeroPointTwoNegative = new Float(-0.2);
int objPitchZeroResult = objPitch.compareTo(objZero);
int objPitchZeroPointTwoResult = objZeroPointTwo.compareTo(objPitch);
int objPitchZeroPointTwoNegativeResult = objPitch.compareTo(objZeroPointTwoNegative);
if (roll < 0 && ((objPitchZeroResult > 0 && objPitchZeroPointTwoResult > 0) || (objPitchZeroResult < 0 && objPitchZeroPointTwoNegativeResult > 0)) && (inclination > 30 && inclination < 40))
{
return true;
}
else
{
return false;
}
}
}
return false;
}
Is this what you're looking for?
public class AccelerometerHandler implements SensorEventListener
{
float accelX;
float accelY;
float accelZ;
public AccelerometerHandler(Context paramContext)
{
SensorManager localSensorManager = (SensorManager)paramContext.getSystemService("sensor");
if (localSensorManager.getSensorList(1).size() != 0)
localSensorManager.registerListener(this, (Sensor)localSensorManager.getSensorList(1).get(0), 1);
}
public float getAccelX()
{
return this.accelX;
}
public float getAccelY()
{
return this.accelY;
}
public float getAccelZ()
{
return this.accelZ;
}
public void onAccuracyChanged(Sensor paramSensor, int paramInt)
{
}
public void onSensorChanged(SensorEvent paramSensorEvent)
{
this.accelX = paramSensorEvent.values[0];
this.accelY = paramSensorEvent.values[1];
this.accelZ = paramSensorEvent.values[2];
}
}
I'm trying to rotate my Bitmap using a readymade solution I found somewhere. The code is below:
public void onDraw(Canvas canvas) {
float x = ship.Position.left;
float y = ship.Position.top;
canvas.drawBitmap(ship.ship, x,y,null);
invalidate();
}
However, when I do it, the X and Y axii change their direction - if I increase the Y the image goes towards the top of the screen, not towards the bottom. Same happens to X if I rotate by 90 degrees.
I need to rotate it but without changing the Y and X axii directions.
Even rotated, I still want the Bitmap to go towards the bottom if I increase Y and to the right if I increase the X.
public void update()
{
if(!moving)
{
fall();
}
else //moving
{
move();
faceDirection();
}
Position.top += Speed;
}
private void move() {
if(Speed < MAXSPEED)
Speed -= 0.5f;
}
private void fall() {
if(Speed > MAXSPEED*-1)
Speed += 0.2f;
}
private void faceDirection() {
double OldDiretion = Direction;
Direction = DirectionHelper.FaceObject(Position, ClickedDiretion);
if (Direction != OldDiretion)
{
Matrix matrix = new Matrix();
matrix.postRotate((float)Direction);
ship = Bitmap.createBitmap(ship, 0, 0, ship.getWidth(),ship.getHeight(), matrix, false);
}
I tried the code above, but it's still changing the Y direction, It's going to bottom of the BitMap, not bottom of the screen.
Here is the project: https://docs.google.com/file/d/0B8V9oTk0eiOKOUZJMWtsSmUtV3M/edit?usp=sharing
You should first rotate, than translate:
matrix.postTranslate(x, y);
matrix.postRotate(degree);
alternative would be to try to use preRotate() instead of postRotate().
I also strongly recommend to translate/rotate the original while drawing. So your createBitmap() call shouldn't modify the orientation. At least not when you change it dynamically on user interaction. Otherwise you would create a lot of bitmaps to represent rotations over and over again which would impact the performance.
The problem is that you don't actually rotate the bitmap - you just draw it rotated. So, the next time you redraw it, you first push it towards the bottom or right by incrementing x/y and then rotate it.
You have to actually rotate the bitmap itself. You could use the following code:
ship.ship = Bitmap.createBitmap(ship.ship, 0, 0, ship.ship.getWidth(), ship.ship.getHeight(), matrix, false);
Here you create a new rotated bitmap and set your reference to point to it.
Note! You must do this only once! So you can't do it in the onDraw method, since then it will get rotated every time it's redrawn. You have to do it somewhere else and then draw it as usual in onDraw (without the matrix rotations).
In AndEngine game, I want to throw a ball physics body. The user sets its angle and force and throws it. Its scenario is same as we can see in Angry Birds. I have calculated both the force and angle but am confused how to apply both simultaneously on ball means the ball should be thrown at calculated angle but with particular force. Can anybody guide me to the right direction?
Here is my code snippet:
#Override
public boolean onAreaTouched(TouchEvent event,ITouchArea pTouchArea, float x, float y) {
// TODO Auto-generated method stub
if(event.isActionDown()) {
......
}
else if(event.isActionMove()) {
......
}
else if(event.isActionCancel() || event.isActionOutside() || event.isActionUp()) {
.....
launchHero(hero, string1.getX1()/PIXEL_TO_METER_RATIO_DEFAULT, string1.getY1()/PIXEL_TO_METER_RATIO_DEFAULT, x/PIXEL_TO_METER_RATIO_DEFAULT, y/PIXEL_TO_METER_RATIO_DEFAULT);
}
public void launchHero(Hero hero, float originX, float originY, float fingerX, float fingerY) {
Vector2 shoot = new Vector2((originX - fingerX), -(originY - fingerY));
shoot = shoot.nor().mul(10);
hero.getBody().setLinearVelocity(shoot);
}
}
return false;
}
I have added negative to (originY - fingerY) because if I don't do this, the ball first goes down then after colliding with base, it goes up.
It's very simple, use Body.setLinearVelocity(Vector2 pVector). Both direction and force are determined by the vector's parameters. Here's a sample code:
Vector2 shoot = new Vector2((originX - fingerX), (originY - fingerY));
body.setLinearVelocity(shoot);
This will shoot the body in the direction made by the finger and place of origin, or, in the case of Angry Birds, the finger and the sling.
If you want to make the force constant and multiply it by some number, you can do this before shooting:
shoot = shoot.nor().mul(multiplier);
You can use
body.applyLinearImpulse(Vector2 impulse, Vector2 point)
I prefer this function on setLinearVelocity because it gives more possibilities. To use this function you need to give the impulse in both coordinates and the body's point where to apply it.
I have a map application using an in-house map engine on Android. I'm working on a rotating Map view that rotates the map based on the phone's orientation using the Sensor Service. All works fine with the exception of dragging the map when the phone is pointing other than North. For example, if the phone is facing West, dragging the Map up still moves the Map to the South versus East as would be expected. I'm assuming translating the canvas is one possible solution but I'm honestly not sure the correct way to do this.
Here is the code I'm using to rotate the Canvas:
public void dispatchDraw(Canvas canvas)
{
canvas.save(Canvas.MATRIX_SAVE_FLAG);
// mHeading is the orientation from the Sensor
canvas.rotate(-mHeading, origin[X],origin[Y]);
mCanvas.delegate = canvas;
super.dispatchDraw(mCanvas);
canvas.restore();
}
What is the best approach to make dragging the map consistent regardless of the phones orientation? The sensormanager has a "remapcoordinates()" method but it's not clear that this will resolve my problem.
You can trivially get the delta x and delta y between two consecutive move events. To correct these values for your canvas rotation you can use some simple trignometry:
void correctPointForRotate(PointF delta, float rotation) {
// Get the angle of movement (0=up, 90=right, 180=down, 270=left)
double a = Math.atan2(-delta.x,delta.y);
a = Math.toDegrees(a); // a now ranges -180 to +180
a += 180;
// Adjust angle by amount the map is rotated around the center point
a += rotation;
a = Math.toRadians(a);
// Calculate new corrected panning deltas
double hyp = Math.sqrt(x*x + y*y);
delta.x = (float)(hyp * Math.sin(a));
delta.y = -(float)(hyp * Math.cos(a));
}