Search code examples
javascriptandroidhtmlcompassdevice-orientation

DeviceOrientation Compass Android


I try to implement a compass using the deviceorientation event. I want to use the rotation to rotate an arrow facing in the viewing direction. I'm simply rotating the Image when the deviceorientation changes.

On iOS this works like charm by doing the following:

if (typeof DeviceOrientationEvent.requestPermission === "function") {
          //@ts-ignore
          DeviceOrientationEvent.requestPermission()
            .then(permissionState => {
              if (permissionState === "granted") {
                this.compassActive = true;
                window.addEventListener(
                  "deviceorientation",
                  eventData =>
                    this.zone.run(() => {
                      if(!this.compassActive){
                        return false;
                      }
                      //@ts-ignore
                      this.rotatePlayerIcon(eventData.webkitCompassHeading);

                    }),
                  false
                );
              }
            })
            .catch(console.error);

Since the DeviceOrientationEvent.webkitCompassHeading gives me a clockwise world based presentation of the rotation of the device.

I try the same on Android but I can't find a world based solution. webkitCompassHeading does not exist on android so I tried using just eventData.alpha. But this gives 0 based on the rotation it was when the event was fired, not based on world north.

All the Guides I find seem outdated.

How do I get a clockwise compass on android like I get on iOS?


Solution

  • The problem is that alpha is not absolute, means 0 is not north instead 0 is where the device is pointing on activation.

    The best way to fix for chrome-based browsers, like most standard Android Browsers ist using an AbsoluteOrientationSensor from W3C Generic Sensor API polyfills. Github

    The Project I used it in is Typescript based. So here is a Typescript example:

    import {AbsoluteOrientationSensor} from 'motion-sensors-polyfill'
    
    const options = { frequency: 60, referenceFrame: 'device' };
            const sensor = new AbsoluteOrientationSensor(options);
            sensor.addEventListener('reading', e => {
              this.zone.run(() => {
                var q = e.target.quaternion;
                let alpha = Math.atan2(2*q[0]*q[1] + 2*q[2]*q[3], 1 - 2*q[1]*q[1] - 2*q[2]*q[2])*(180/Math.PI);
                if(alpha < 0) alpha = 360+ alpha;
                this.alpha = 360 - alpha;
                this.rotatePlayerIcon(360 - alpha)
              })
            });
            sensor.start();