Real Compass on mobile browsers with Javascript
Tags: tutorial, javascript, webdev
Published: 2020-09-11 10:59:13

---
title: Real Compass on mobile browsers with Javascript
published: true
description: Tutorial about How to create real compass for iOS/Android browsers and upgrade it.
tags: tutorial, javascript, webdev
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/2nh0zt1tjmye0spg6vc0.png
---

Well, one day I had a challenge from my muslim friend to code a map that's gonna show an arrow from his current geolocation to [Qibla](https://en.wikipedia.org/wiki/Qibla) or any geopoint.

That wasn't the best solution, cause a compass will solve it better way and make people's life easier. So, I've started to find any package/lib to put the compass into his webpage.

Found these solutions [Compass.js](https://ai.github.io/compass.js/) or [this one](https://github.com/lamplightdev/compass), but none of them work ~~at all~~ well. Cause last commits were 6-7 years ago.

### Let's make own real compass for mobile browsers!
Result we're gonna have ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/x6t1g1buc8kfcc9u5kdt.gif)

We will need several html elements.

```html
<div class="compass">
  <div class="arrow"></div>
  <div class="compass-circle"></div>
  <div class="my-point"></div>
</div>
<button class="start-btn">Start compass</button>
```

Let's add css for that

```css
.compass {
  position: relative;
  width: 320px;
  height: 320px;
  border-radius: 50%;
  box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
  margin: auto;
}

.compass > .arrow {
  position: absolute;
  width: 0;
  height: 0;
  top: -20px;
  left: 50%;
  transform: translateX(-50%);
  border-style: solid;
  border-width: 30px 20px 0 20px;
  border-color: red transparent transparent transparent;
  z-index: 1;
}

.compass > .compass-circle,
.compass > .my-point {
  position: absolute;
  width: 80%;
  height: 80%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  transition: transform 0.1s ease-out;
  background: url(https://cdn.onlinewebfonts.com/svg/img_467023.png) center
    no-repeat;
  background-size: contain;
}

.compass > .my-point {
  opacity: 0;
  width: 20%;
  height: 20%;
  background: rgb(8, 223, 69);
  border-radius: 50%;
  transition: opacity 0.5s ease-out;
}
```

### JavaScript time!

Define our html elements first and add event for button that starts it.
iOS needs to have manipulation by user to start `DeviceOrientationEvent`, but for Android it works without it.

```js
const compassCircle = document.querySelector(".compass-circle");
const startBtn = document.querySelector(".start-btn");
const myPoint = document.querySelector(".my-point");
let compass;
const isIOS = !(
  navigator.userAgent.match(/(iPod|iPhone|iPad)/) &&
  navigator.userAgent.match(/AppleWebKit/)
);
```

```js
function init() {
  startBtn.addEventListener("click", startCompass);
}

function startCompass() {
  if (isIOS) {
    DeviceOrientationEvent.requestPermission()
      .then((response) => {
        if (response === "granted") {
          window.addEventListener("deviceorientation", handler, true);
        } else {
          alert("has to be allowed!");
        }
      })
      .catch(() => alert("not supported"));
  } else {
    window.addEventListener("deviceorientationabsolute", handler, true);
  }
}

function handler(e) {
  compass = e.webkitCompassHeading || Math.abs(e.alpha - 360);
  compassCircle.style.transform = `translate(-50%, -50%) rotate(${-compass}deg)`;
}

init();
```

Done! Our compass is working for both iOS and Android.

## Upgrade our compass to reach the goal

On this step we need to find correct angle/degree to our **point** (Qibla).
We put the point coordinates and calculate degree from out current geolocation.

**How it works?** <br/>

1. We're getting our current geolocation
2. Define point coordinates (where we should turn to)
3. Calculate degree from our position to defined point
4. Display point when we're in correct position
   <br/>

Define `pointDegree` and our functions for this.

```js
let pointDegree;

function locationHandler(position) {
  const { latitude, longitude } = position.coords;
  pointDegree = calcDegreeToPoint(latitude, longitude);

  if (pointDegree < 0) {
    pointDegree = pointDegree + 360;
  }
}

function calcDegreeToPoint(latitude, longitude) {
  // Qibla geolocation
  const point = {
    lat: 21.422487,
    lng: 39.826206,
  };

  const phiK = (point.lat * Math.PI) / 180.0;
  const lambdaK = (point.lng * Math.PI) / 180.0;
  const phi = (latitude * Math.PI) / 180.0;
  const lambda = (longitude * Math.PI) / 180.0;
  const psi =
    (180.0 / Math.PI) *
    Math.atan2(
      Math.sin(lambdaK - lambda),
      Math.cos(phi) * Math.tan(phiK) -
        Math.sin(phi) * Math.cos(lambdaK - lambda)
    );
  return Math.round(psi);
}
```

We put our location handler into `init` function to listen Geolocation API. Add some code to `handler` that's gonna update our point state.

```js
function init() {
  startBtn.addEventListener("click", startCompass);
  navigator.geolocation.getCurrentPosition(locationHandler);
}

function handler(e) {
  compass = e.webkitCompassHeading || Math.abs(e.alpha - 360);
  compassCircle.style.transform = `translate(-50%, -50%) rotate(${-compass}deg)`;

  // ±15 degree
  if (
    (pointDegree < Math.abs(compass) && pointDegree + 15 > Math.abs(compass)) ||
    pointDegree > Math.abs(compass + 15) ||
    pointDegree < Math.abs(compass)
  ) {
    myPoint.style.opacity = 0;
  } else if (pointDegree) {
    myPoint.style.opacity = 1;
  }
}
```

We're done! We have a real compass in our mobile browsers.

### [Demo link](https://idzgd.csb.app/)

Here's a [source link](https://codesandbox.io/s/flamboyant-flower-idzgd?fontsize=14&hidenavigation=1&theme=dark)

by @gigantz