Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat project details geolocation integration #1374

Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React, { useState } from 'react';
import AssetModules from '@/shared/AssetModules';
import VectorLayer from 'ol/layer/Vector';
import CoreModules from '@/shared/CoreModules.js';
import { ProjectActions } from '@/store/slices/ProjectSlice';
import { useAppSelector } from '@/types/reduxTypes';

const MapControlComponent = ({ map }) => {
const btnList = [
Expand All @@ -26,6 +29,10 @@ const MapControlComponent = ({ map }) => {
},
];

const dispatch = CoreModules.useAppDispatch();
const [toggleCurrentLoc, setToggleCurrentLoc] = useState(false);
const geolocationStatus = useAppSelector((state) => state.project.geolocationStatus);

const handleOnClick = (btnId) => {
if (btnId === 'add') {
const actualZoom = map.getView().getZoom();
Expand All @@ -34,19 +41,8 @@ const MapControlComponent = ({ map }) => {
const actualZoom = map.getView().getZoom();
map.getView().setZoom(actualZoom - 1);
} else if (btnId === 'currentLocation') {
const layers = map.getAllLayers();
let extent;
layers.map((layer) => {
if (layer instanceof VectorLayer) {
const layerName = layer.getProperties().name;
if (layerName === 'geolocation') {
extent = layer.getSource().getExtent();
}
}
});
map.getView().fit(extent, {
padding: [10, 10, 10, 10],
});
setToggleCurrentLoc(!toggleCurrentLoc);
dispatch(ProjectActions.ToggleGeolocationStatus(!geolocationStatus));
} else if (btnId === 'taskBoundries') {
const layers = map.getAllLayers();
let extent;
Expand All @@ -69,7 +65,9 @@ const MapControlComponent = ({ map }) => {
{btnList.map((btn) => (
<div key={btn.id}>
<div
className="fmtm-bg-white fmtm-rounded-full fmtm-p-2 hover:fmtm-bg-gray-100 fmtm-cursor-pointer fmtm-duration-300"
className={`fmtm-bg-white fmtm-rounded-full fmtm-p-2 hover:fmtm-bg-gray-100 fmtm-cursor-pointer fmtm-duration-300 ${
geolocationStatus && btn.id === 'currentLocation' ? 'fmtm-text-primaryRed' : ''
}`}
onClick={() => handleOnClick(btn.id)}
title={btn.title}
>
Expand Down
209 changes: 0 additions & 209 deletions src/frontend/src/components/home/ProjectListMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ 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 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,16 +11,6 @@ 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';
import RedNavigationMarker from '@/assets/images/rednavigationmarker.svg';
import navigationMarker from '@/assets/images/navigation.png';
import pngbluedot from '@/assets/images/png bluedot.png';

type HomeProjectSummaryType = {
features: { geometry: any; properties: any; type: any }[];
Expand All @@ -34,21 +23,6 @@ type HomeProjectSummaryType = {
};
};

// const projectGeojsonLayerStyle = {
// ...defaultStyles,
// fillOpacity: 0,
// lineColor: '#ffffff',
// labelFontSize: 20,
// lineThickness: 7,
// lineOpacity: 40,
// showLabel: true,
// labelField: 'project_id',
// labelOffsetY: 35,
// labelFontWeight: 'bold',
// labelMaxResolution: 10000,
// icon: { scale: [0.09, 0.09], url: MarkerIcon },
// };

const getIndividualStyle = (featureProperty) => {
const style = new Style({
image: new Icon({
Expand Down Expand Up @@ -77,151 +51,7 @@ const ProjectListMap = () => {
zoom: 4,
maxZoom: 20,
});
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: true,
},
);
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: pngbluedot,
scale: 0.09,
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: 60,
referenceFrame: 'screen',
});
sensor.addEventListener('reading', (event) => {
console.log(event, 'event');
layer.on('postrender', 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 Expand Up @@ -261,45 +91,6 @@ const ProjectListMap = () => {
}}
>
<LayerSwitcherControl visible={'outdoors'} />
{/* {projectGeojson && projectGeojson?.features?.length > 0 && (
<VectorLayer
geojson={projectGeojson}
style={projectGeojsonLayerStyle}
viewProperties={{
size: map?.getSize(),
padding: [50, 50, 50, 50],
constrainResolution: true,
duration: 2000,
}}
mapOnClick={projectClickOnMap}
zoomToLayer
zIndex={5}
// hoverEffect={(selectedFeature, layer) => {
// if (!selectedFeature)
// return layer.setStyle((feature, resolution) =>
// getStyles({
// style: { ...projectGeojsonLayerStyle },
// feature,
// resolution,
// }),
// );
// else {
// selectedFeature.setStyle((feature, resolution) =>
// getStyles({
// style: { ...projectGeojsonLayerStyle, icon: { scale: [0.15, 0.15], url: MarkerIcon } },
// feature,
// resolution,
// }),
// );
// }
// // selectedFeature.setStyle({
// // ...projectGeojsonLayerStyle,
// // icon: { scale: [0.15, 0.15], url: MarkerIcon },
// // });
// // selectedFeature.setStyle();
// }}
/>
)} */}
{projectGeojson && projectGeojson?.features?.length > 0 && (
<ClusterLayer
map={map}
Expand Down
4 changes: 4 additions & 0 deletions src/frontend/src/store/slices/ProjectSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const initialState: ProjectStateTypes = {
last_active: '',
},
projectDashboardLoading: false,
geolocationStatus: false,
projectCommentsList: [],
projectPostCommentsLoading: false,
projectGetCommentsLoading: false,
Expand Down Expand Up @@ -99,6 +100,9 @@ const ProjectSlice = createSlice({
SetProjectDashboardLoading(state, action) {
state.projectDashboardLoading = action.payload;
},
ToggleGeolocationStatus(state, action) {
state.geolocationStatus = action.payload;
},
SetProjectCommentsList(state, action) {
state.projectCommentsList = action.payload;
},
Expand Down
1 change: 1 addition & 0 deletions src/frontend/src/store/types/IProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type ProjectStateTypes = {
projectDetailsLoading: boolean;
projectDashboardDetail: projectDashboardDetailTypes;
projectDashboardLoading: boolean;
geolocationStatus: boolean;
projectCommentsList: projectCommentsListTypes[];
projectPostCommentsLoading: boolean;
projectGetCommentsLoading: boolean;
Expand Down
Loading
Loading