Reputation: 929
How can I snap the direction of the compass to the direction of the map?
I add a Compass to my MapView and enable map rotation like this:
MapView mapView = findViewById(R.id.mapView);
// Enable & add compass
CompassOverlay compassOverlay = new CompassOverlay(this, mapView);
compassOverlay.enableCompass();
mapView.getOverlays().add(compassOverlay);
// Enable map rotation with gestures
mapView.getOverlays().add(new RotationGestureOverlay(mapView));
Now when the map is not rotated, so up is north, right is east etc, the compass works correctly. But when I rotate the map with gesture, the compass does not move accordingly. So when the device is faced north and the map is at its default rotation, the compass shows north according to the map, which is correct. But when I rotate the map by 90° clockwise, the compass still points up instead of turning 90° clockwise.
I tried using compassOverlay.setPointerMode(true);
but that only changed the appearance of the compass.
Upvotes: 3
Views: 1628
Reputation: 13505
Edit - use Raul's solution - leaving this answer as a historical artifact only
This post https://github.com/osmdroid/osmdroid/issues/1647 has the solution. Copying from there, with light editing...
Make a new class: MapAlignedCompassOverlay.java
, which is just a copy/rename of class CompassOverlay.java, change the package name to match your package, and change the class name to MapAlignedCompassOverlay
.
Change a line in the function drawCompass()
from
mCompassMatrix.setRotate(-bearing, mCompassRoseCenterX, mCompassRoseCenterY);
to
mCompassMatrix.setRotate(proj.getOrientation(), mCompassRoseCenterX, mCompassRoseCenterY);
In the setup code (e.g. onCreate
if you're in an Activity) use MapAlignedCompassOverlay
as a replacement for CompassOverlay
in my app, and just give it a fake OrientationProvider to make it happy.
E.g. (Kotlin):
val fakeOrientationProvider = object: IOrientationProvider{
override fun startOrientationProvider(orientationConsumer: IOrientationConsumer?) = true
override fun stopOrientationProvider() = Unit
override fun getLastKnownOrientation(): Float = 0f
override fun destroy() = Unit
}
mapView.overlays.add(MapAlignedCompassOverlay(context, fakeOrientationProvider, mapView).apply {
enableCompass()
onOrientationChanged(0f, fakeOrientationProvider) // Magically makes it show up.
})
And now you have a compass that actually matches the map. Note to demonstrate this, you can enable "pinch to rotate" with
mapView.overlays.add(RotationGestureOverlay(mapView))
And observe that the compass stays pointing (True) north when you rotate.
Upvotes: 0
Reputation: 21
While digging a bit and considering Peter's answer I found a simple solution (Kotlin):
CompassOverlay
instance and override the draw()
methodval mapNorthCompassOverlay = object: CompassOverlay(context, mapView) {
override fun draw(c: Canvas?, pProjection: Projection?) {
drawCompass(c, -mapView.mapOrientation, pProjection?.screenRect)
}
}
mapView.overlays.add(mapNorthCompassOverlay)
And that's it.
Explanation: drawCompass()
is a protected method, but we can override the public draw()
that uses the former method to fetch the phone magnetometer data to get the bearing direction. Since drawCompass()
inverts the bearing data to print on your map and that is not our expected behavior, in this case, we pass -mapView.mapOrientation
as a parameter, as it contains the information of the north of our map. Also, this CompassOverlay
does not require you to use enableCompass()
, which is the method that starts listening for sensor changes in direction, so you won't need to create a fake OrientationProvider
to avoid your phone wasting resources.
Upvotes: 2
Reputation: 71
I edited my previous answer: it seems that the compass works only on actual hardware.
See: Adding Compass to Rotate MapView
Upvotes: 0