Select to view content in your preferred language

ArcGIS JS 4 Rotate Graphic

1153
13
04-26-2023 12:49 PM
BobCowling2
New Contributor III

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;

 

 

Tags (1)
0 Kudos
13 Replies
AndyGup
Esri Regular Contributor

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?

0 Kudos
BobCowling2
New Contributor III

@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. 

0 Kudos
AndyGup
Esri Regular Contributor

@BobCowling2  if the enhancements make it in, 4.27 is scheduled for early summer (2023).

AndyGup
Esri Regular Contributor

@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.

 

0 Kudos
BobCowling2
New Contributor III

@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? 

0 Kudos
AndyGup
Esri Regular Contributor

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`;
})

 

0 Kudos
AndyGup
Esri Regular Contributor

> Does this use the GPS compass heading or the device orientation? 

The widget just uses the heading value.

0 Kudos
BobCowling2
New Contributor III

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>
0 Kudos
AndyGup
Esri Regular Contributor

> 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.

0 Kudos