Skip to content

Commit

Permalink
feat: navigation WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
varun2948 committed Mar 17, 2024
1 parent 06fb7cf commit 6a05fc3
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/frontend/src/assets/images/navigation.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 153 additions & 1 deletion src/frontend/src/components/home/ProjectListMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { useOLMap } from '@/components/MapComponent/OpenLayersComponent';
import { MapContainer as MapComponent } from '@/components/MapComponent/OpenLayersComponent';
import LayerSwitcherControl from '@/components/MapComponent/OpenLayersComponent/LayerSwitcher/index.js';
import { VectorLayer } from '@/components/MapComponent/OpenLayersComponent/Layers';
import OLVectorLayer from 'ol/layer/Vector';
import { ClusterLayer } from '@/components/MapComponent/OpenLayersComponent/Layers';
import CoreModules from '@/shared/CoreModules';
import { geojsonObjectModel } from '@/constants/geojsonObjectModal';
Expand All @@ -12,6 +12,13 @@ import { useNavigate } from 'react-router-dom';
import environment from '@/environment';
import { Style, Text, Icon, Fill } from 'ol/style';
import { projectType } from '@/models/home/homeModel';
import Control from 'ol/control/Control';
import { Feature } from 'ol';
import { circular } from 'ol/geom/Polygon';
import { Point } from 'ol/geom';
import { fromLonLat } from 'ol/proj';
import VectorSource from 'ol/source/Vector';
import logo from '@/assets/images/navigation.svg';

type HomeProjectSummaryType = {
features: { geometry: any; properties: any; type: any }[];
Expand Down Expand Up @@ -67,6 +74,151 @@ const ProjectListMap = () => {
zoom: 4,
maxZoom: 17,
});
useEffect(() => {
if (!map) return;
const source = new VectorSource();
const layer = new OLVectorLayer({
source: source,
});
map?.addLayer(layer);

navigator.geolocation.watchPosition(
function (pos) {
const coords = [pos.coords.longitude, pos.coords.latitude];
source.clear(true);
source.addFeatures([
new Feature(circular(coords, pos.coords.accuracy).transform('EPSG:4326', map.getView().getProjection())),
new Feature(new Point(fromLonLat(coords))),
]);
},
function (error) {
alert(`ERROR: ${error.message}`);
},
{
enableHighAccuracy: false,
},
);
const locate = document.createElement('div');
locate.className = 'ol-control ol-unselectable locate';
locate.innerHTML = '<button title="Locate me">◎</button>';
locate.addEventListener('click', function () {
if (!source.isEmpty()) {
map.getView().fit(source.getExtent(), {
maxZoom: 18,
duration: 500,
});
}
});
map.addControl(
new Control({
element: locate,
}),
);
//! [style]
const style = new Style({
fill: new Fill({
color: 'rgba(0, 0, 255, 0.2)',
}),
image: new Icon({
src: logo,
scale: 0.02,
imgSize: [27, 55],
rotateWithView: true,
}),
});
layer.setStyle(style);

function handleReading(quaternion) {
// https://w3c.github.io/orientation-sensor/#model explains the order of
// the 4 elements in the sensor.quaternion array.
let [qx, qy, qz, qw] = quaternion;

// When the phone is lying flat, we want to treat the direction toward the
// top of the phone as the "forward" direction; when the phone is held
// upright, we want to treat the direction out the back of the phone as the
// "forward" direction. So, let's determine the compass heading of the
// phone based on the vector between these directions, i.e. at a 45-degree
// angle between the positive Y-axis and the negative Z-axis in this figure:
// https://w3c.github.io/orientation-sensor/#absoluteorientationsensor-model

// To find the current "forward" direction of the phone, we want to take this
// vector, (0, 1, -1), and apply the same rotation as the phone's rotation.
const y = 1;
const z = -1;

// From experimentation, it looks like the quaternion from the sensor is
// the inverse rotation, so we need to flip the fourth component.
qw = -qw;

// This section explains how to convert the quaternion to a rotation matrix:
// https://w3c.github.io/orientation-sensor/#convert-quaternion-to-rotation-matrix
// Now let's multiply the forward vector by the rotation matrix.
const rx = y * (2 * qx * qy + 2 * qw * qz) + z * (2 * qx * qz - 2 * qw * qy);
const ry = y * (1 - 2 * qx * qx - 2 * qz * qz) + z * (2 * qy * qz + 2 * qw * qx);
const rz = y * (2 * qy * qz + 2 * qw * qx) + z * (1 - 2 * qx * qx - 2 * qy * qy);

// This gives us a rotated vector indicating the "forward" direction of the
// phone with respect to the earth. We only care about the orientation of
// this vector in the XY plane (the plane tangential to the ground), i.e.
// the heading of the (rx, ry) vector, where (0, 1) is north.

const radians = Math.atan2(ry, rx);
const degrees = (radians * 180) / Math.PI; // counterclockwise from +X axis
let heading = 90 - degrees;
if (heading < 0) heading += 360;
heading = Math.round(heading);

// info.value =
// qx.toFixed(3) +
// "\n" +
// qy.toFixed(3) +
// "\n" +
// qz.toFixed(3) +
// "\n" +
// qw.toFixed(3) +
// "\n\n" +
// rx.toFixed(3) +
// "\n" +
// ry.toFixed(3) +
// "\n" +
// rz.toFixed(3) +
// "\n\nHeading: " +
// heading;
console.log(heading, 'heading');
console.log((Math.PI / 180) * heading, '(Math.PI / 180) * heading');
// To make the arrow point north, rotate it opposite to the phone rotation.
style.getImage().setRotation((Math.PI / 180) * heading);
}

// See the API specification at: https://w3c.github.io/orientation-sensor
// We use referenceFrame: 'screen' because the web page will rotate when
// the phone switches from portrait to landscape.
const sensor = new AbsoluteOrientationSensor({
frequency: 10,
referenceFrame: 'screen',
});
sensor.addEventListener('reading', () => {
handleReading(sensor.quaternion);
});

// handleReading([0.509, -0.071, -0.19, 0.836]);

Promise.all([
navigator.permissions.query({ name: 'accelerometer' }),
navigator.permissions.query({ name: 'magnetometer' }),
navigator.permissions.query({ name: 'gyroscope' }),
]).then((results) => {
if (results.every((result) => result.state === 'granted')) {
sensor.start();
console.log('Sensor started!');

// stat.value = "Sensor started!";
} else {
console.log('No permissions to use AbsoluteOrientationSensor.');
// stat.value = "No permissions to use AbsoluteOrientationSensor.";
}
});
}, [map]);
const homeProjectSummary: projectType[] = CoreModules.useAppSelector((state) => state.home.homeProjectSummary);
useEffect(() => {
if (homeProjectSummary?.length === 0) return;
Expand Down

0 comments on commit 6a05fc3

Please sign in to comment.