I am attempting to rotate an arrow based on a mobile phones device orientation. However simply changing the symbol angle does not seem to work. What is the proper way to rotate a graphic?
Thank you very much.
const arrowSymbol = {
type: "simple-marker",
path: "M10,0 L20,30 L10,20 L0,30 Z",
color: [0, 0, 255, 1],
outline: {
color: [0, 0, 0, 1],
width: 1
},
size: 25
};
const arrowGraphic = new Graphic({
symbol: arrowSymbol
});
const track = new Track({
view: view,
graphic: arrowGraphic,
useHeadingEnabled: false
});
// Add the Track widget to the view
view.ui.add(track, "top-right");
window.addEventListener("deviceorientation", (event) => {
console.log(`${event.alpha} : ${event.beta} : ${event.gamma} : ${event.absolute} : ${event.webkitCompassHeading}`);
// get the heading value from the device
const heading = event.alpha ? event.alpha * -1 : 0;
// Rotate the arrow symbol based on the device's heading.
arrowGraphic.symbol.angle = heading;
Hi @BobCowling2 we are working on similar functionality for the Track widget that is tentatively scheduled for the 4.27 release. I can post an update when that is available for testing on the 'next' build, or do you need something sooner than that?
@AndyGup I was hoping to get something up for summer use on our field app. Do you have an estimated date when this could be available? Even testing is fine. Thank you.
@BobCowling2 if the enhancements make it in, 4.27 is scheduled for early summer (2023).
@BobCowling2 you can try out the new 2D heading graphic for the Track widget using the 4.27 'next' build: https://github.com/Esri/feedback-js-api-next/. The heading symbol will automatically display when the speed is greater than 0 and heading values are provided by the browser's Geolocation API.
@AndyGup Thanks! I did play around with it a little bit today, seems to bounce around in the wrong direction quite a bit but I will test it in more depth tomorrow. Does this use the GPS compass heading or the device orientation?
The widget simply passes through all Geolocation API results provided by the browser. Be sure to set the high-accuracy property in the geolocation options. Browser Geolocation API implementations haven't been improved in years, so they can be very flaky. You can monitor what's being returned using something like this:
// https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-Track.html#event-track
track.on("track", (data) => {
const { longitude, latitude, altitude, heading, speed, accuracy } = data.position.coords;
geolocationResultsDiv.innerText = `
Latitude: ${latitude?.toFixed(4)}
Longitude: ${longitude?.toFixed(4)}
Altitude: ${altitude?.toFixed(1)}
Heading: ${heading?.toFixed(0)}
Speed: ${speed?.toFixed(2)}
Accuracy: ${accuracy?.toFixed(0)}m`;
})
> Does this use the GPS compass heading or the device orientation?
The widget just uses the heading value.
Thanks. I did try this out today and it worked much better walking around in the field. I think for our particular use case, which is having users walk around viewing historical buildings, I would prefer to use the device heading as the person is not always going to be moving. I did get the arrow to rotate with the device heading by cloning the graphic as suggested below but could not figure out to remove and replace it correctly. The arrow always draws multiple times when there is a new GPS position. So I am not sure if I was calling the arrowGraphic.removeAll() in the wrong spot or adding it the wrong way.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>ArcGIS JS API - Track Widget Example</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.26/esri/themes/light/main.css">
<script src="https://js.arcgis.com/4.26/"></script>
<style>
html, body, #viewDiv {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
</style>
</head>
<div id="viewDiv"></div>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/widgets/Track",
"esri/Graphic",
"esri/symbols/SimpleLineSymbol",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleFillSymbol",
"esri/geometry/Point"
], function(Map, MapView, Track, Graphic, SimpleLineSymbol, SimpleMarkerSymbol, SimpleFillSymbol, Point) {
// Create a new Map instance
const map = new Map({
basemap: "satellite"
});
// Create a new MapView instance
const view = new MapView({
container: "viewDiv",
map: map,
center: [-118.2437, 34.0522],
zoom: 13
});
const arrowSymbol = {
type: "simple-marker",
path: "M10,0 L20,30 L10,20 L0,30 Z",
color: [0, 0, 255, 1],
outline: {
color: [0, 0, 0, 1],
width: 1
},
size: 25
};
const arrowGraphic = new Graphic({
symbol: arrowSymbol
});
const track = new Track({
view: view,
graphic: arrowGraphic,
useHeadingEnabled: false
});
// Add the Track widget to the view
view.ui.add(track, "top-right");
function onGeolocationSuccess(position) {
console.log(position);
let previousGraphic;
window.addEventListener("deviceorientation", (event) => {
// Get the updated heading from the device orientation event
let heading = `${event.alpha}` * -1;
// Remove the previous graphic from the view, if there is one
if (previousGraphic) {
view.graphics.remove(previousGraphic);
//view.graphics.remove(arrowGraphic);
}
// Clone the original graphic and update its symbol angle with the heading
const newGraphic = arrowGraphic.clone();
view.graphics.removeAll();
newGraphic.symbol.angle = heading;
// Add the new graphic to the view and store it as the previous graphic
view.graphics.add(newGraphic);
previousGraphic = newGraphic;
});
}
function onGeolocationError(error) {
console.error("Geolocation error: ", error);
}
const geolocationOptions = {
enableHighAccuracy: true,
maximumAge: 0,
timeout: 50
};
if (navigator.geolocation) {
navigator.geolocation.watchPosition(
onGeolocationSuccess,
onGeolocationError,
geolocationOptions
);
} else {
console.error("Geolocation is not supported by this browser.");
}
});
</script>
</body>
</html>
> The arrow always draws multiple times when there is a new GPS position.
Two graphics makes sense. There is the Track widget's graphic being drawn, and you are also adding a graphic to the View's graphic collection. Also, I'm fairly certain you can only set the Track widget's graphic property once, when the widget is first initialized.