Drag and Drop a Pin with ArcGIS JS API 4.x

5061
7
Jump to solution
01-20-2020 08:51 PM
by Anonymous User
Not applicable

Is it possible to use the ArcGIS JS API to drag and drop a pin. The user would like to be able to see the pin as they move, I tried removing and re-adding the graphic layer, but I can't find any examples where the graphic marker symbol is visible to the user as they move it. I'm able to use the sketch view model, in the below example, but the marker disappears and is replaced with the default sketch symbol.

Thanks!

Test Graphics Layer

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>Test Graphics Layer</title>

  <link rel="stylesheet" href="https://js.arcgis.com/4.9/esri/css/main.css">
  <script src="https://js.arcgis.com/4.9/"></script>

  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>

  <script>
    require([
      "esri/Map",
      "esri/views/MapView",
      "esri/widgets/Sketch/SketchViewModel",
      "esri/Graphic",
      "esri/layers/GraphicsLayer",
      "esri/geometry/support/webMercatorUtils"
    ], function(
      Map, MapView, SketchViewModel, Graphic, GraphicsLayer, webMercatorUtils
    ) {

      let editGraphic;

      // GraphicsLayer to hold graphics created via sketch view model
      const graphicsLayer = new GraphicsLayer({
        id: "tempGraphics"
      });

      const map = new Map({
        basemap: "gray",
        layers: [graphicsLayer]
      });

      const view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-121.5, 38.555],
        zoom: 11
      });

 
      var point = {
        type: "point", // autocasts as new Point()
        longitude: -121.5,
        latitude: 38.555
      };

      var markerSymbol = {
        type: "picture-marker",  // autocasts as new PictureMarkerSymbol()
        url: "https://daraobeirne.github.io/kisspng-drawing-pin-world-map-logo-push-vector-5ae029f6ddeaf4.198342921524640246909.png",
        width: "30px",
        height: "30px"
      };


      var pointGraphic = new Graphic({
        geometry: webMercatorUtils.geographicToWebMercator(point),
        symbol: markerSymbol
      });


      view.when(function () {

        graphicsLayer.graphics.addMany([pointGraphic]);

        // create a new sketch view model
        const sketchViewModel = new SketchViewModel({
          view,
          layer: graphicsLayer,
          updateOnGraphicClick: true
        });

        setUpClickHandler();


        sketchViewModel.on("update-complete", updateGraphic);
        sketchViewModel.on("update-cancel", updateGraphic);

        function updateGraphic(event) {
          var graphic = new Graphic({
            geometry: event.geometry,
            symbol: editGraphic.symbol
          });
          graphicsLayer.add(graphic);

          editGraphic = null;
        }


        function setUpClickHandler() {
          view.on("click", function (event) {
            view.hitTest(event).then(function (response) {
              var results = response.results;
              if (results.length > 0) {
                for (var i = 0; i < results.length; i++) {
                  // Check if we're already editing a graphic
                  if (!editGraphic && results[i].graphic.layer.id === "tempGraphics") {
                    // Save a reference to the graphic we intend to update
                    editGraphic = results[i].graphic;
                    
                    // Remove the graphic from the GraphicsLayer
                    // Sketch will handle displaying the graphic while being updated
                    graphicsLayer.remove(editGraphic);
                    sketchViewModel.update(editGraphic);
                    break;
                  }
                }
              }
            });
          });
        }
      });
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
</body>

</html>
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Emeritus

Dara,

   If you move up to a newer release of the api then you can do this:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>Test Graphics Layer</title>

  <link rel="stylesheet" href="https://js.arcgis.com/4.14/esri/css/main.css">
  <script src="https://js.arcgis.com/4.14/"></script>

  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>

  <script>
    require([
      "esri/Map",
      "esri/views/MapView",
      "esri/widgets/Sketch/SketchViewModel",
      "esri/Graphic",
      "esri/layers/GraphicsLayer",
      "esri/geometry/support/webMercatorUtils"
    ], function(
      Map, MapView, SketchViewModel, Graphic, GraphicsLayer, webMercatorUtils
    ) {

      let editGraphic;

      // GraphicsLayer to hold graphics created via sketch view model
      const graphicsLayer = new GraphicsLayer({
        id: "tempGraphics"
      });

      const map = new Map({
        basemap: "gray",
        layers: [graphicsLayer]
      });

      const view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-121.5, 38.555],
        zoom: 11
      });

 
      var point = {
        type: "point", // autocasts as new Point()
        longitude: -121.5,
        latitude: 38.555
      };

      var markerSymbol = {
        type: "picture-marker",  // autocasts as new PictureMarkerSymbol()
        url: "https://daraobeirne.github.io/kisspng-drawing-pin-world-map-logo-push-vector-5ae029f6ddeaf4.198342921524640246909.png",
        width: "30px",
        height: "30px"
      };


      var pointGraphic = new Graphic({
        geometry: webMercatorUtils.geographicToWebMercator(point),
        symbol: markerSymbol
      });


      view.when(function () {

        graphicsLayer.graphics.add(pointGraphic);

        // create a new sketch view model
        const sketchViewModel = new SketchViewModel({
          view: view,
          layer: graphicsLayer,
          updateOnGraphicClick: true,
          pointSymbol: markerSymbol
        });

        setUpClickHandler();

        sketchViewModel.on("update-complete", updateGraphic);
        sketchViewModel.on("update-cancel", updateGraphic);
        sketchViewModel.on('update', function(evt){
          evt.graphics[0].symbol = markerSymbol;
        });

        function updateGraphic(event) {
          var graphic = new Graphic({
            geometry: event.geometry,
            symbol: editGraphic.symbol
          });
          graphicsLayer.add(graphic);

          editGraphic = null;
        }

        function setUpClickHandler() {
          view.on("click", function (event) {
            view.hitTest(event).then(function (response) {
              var results = response.results;
              if (results.length > 0) {
                for (var i = 0; i < results.length; i++) {
                  // Check if we're already editing a graphic
                  if (!editGraphic && results[i].graphic.layer.id === "tempGraphics") {
                    // Save a reference to the graphic we intend to update
                    editGraphic = results[i].graphic;
                    // Remove the graphic from the GraphicsLayer
                    // Sketch will handle displaying the graphic while being updated
                    graphicsLayer.remove(editGraphic);
                    sketchViewModel.update([editGraphic]);
                    break;
                  }
                }
              }
            });
          });
        }
      });
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
</body>

</html>

View solution in original post

7 Replies
RobertScheitlin__GISP
MVP Emeritus

Dara,

   If you move up to a newer release of the api then you can do this:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>Test Graphics Layer</title>

  <link rel="stylesheet" href="https://js.arcgis.com/4.14/esri/css/main.css">
  <script src="https://js.arcgis.com/4.14/"></script>

  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>

  <script>
    require([
      "esri/Map",
      "esri/views/MapView",
      "esri/widgets/Sketch/SketchViewModel",
      "esri/Graphic",
      "esri/layers/GraphicsLayer",
      "esri/geometry/support/webMercatorUtils"
    ], function(
      Map, MapView, SketchViewModel, Graphic, GraphicsLayer, webMercatorUtils
    ) {

      let editGraphic;

      // GraphicsLayer to hold graphics created via sketch view model
      const graphicsLayer = new GraphicsLayer({
        id: "tempGraphics"
      });

      const map = new Map({
        basemap: "gray",
        layers: [graphicsLayer]
      });

      const view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-121.5, 38.555],
        zoom: 11
      });

 
      var point = {
        type: "point", // autocasts as new Point()
        longitude: -121.5,
        latitude: 38.555
      };

      var markerSymbol = {
        type: "picture-marker",  // autocasts as new PictureMarkerSymbol()
        url: "https://daraobeirne.github.io/kisspng-drawing-pin-world-map-logo-push-vector-5ae029f6ddeaf4.198342921524640246909.png",
        width: "30px",
        height: "30px"
      };


      var pointGraphic = new Graphic({
        geometry: webMercatorUtils.geographicToWebMercator(point),
        symbol: markerSymbol
      });


      view.when(function () {

        graphicsLayer.graphics.add(pointGraphic);

        // create a new sketch view model
        const sketchViewModel = new SketchViewModel({
          view: view,
          layer: graphicsLayer,
          updateOnGraphicClick: true,
          pointSymbol: markerSymbol
        });

        setUpClickHandler();

        sketchViewModel.on("update-complete", updateGraphic);
        sketchViewModel.on("update-cancel", updateGraphic);
        sketchViewModel.on('update', function(evt){
          evt.graphics[0].symbol = markerSymbol;
        });

        function updateGraphic(event) {
          var graphic = new Graphic({
            geometry: event.geometry,
            symbol: editGraphic.symbol
          });
          graphicsLayer.add(graphic);

          editGraphic = null;
        }

        function setUpClickHandler() {
          view.on("click", function (event) {
            view.hitTest(event).then(function (response) {
              var results = response.results;
              if (results.length > 0) {
                for (var i = 0; i < results.length; i++) {
                  // Check if we're already editing a graphic
                  if (!editGraphic && results[i].graphic.layer.id === "tempGraphics") {
                    // Save a reference to the graphic we intend to update
                    editGraphic = results[i].graphic;
                    // Remove the graphic from the GraphicsLayer
                    // Sketch will handle displaying the graphic while being updated
                    graphicsLayer.remove(editGraphic);
                    sketchViewModel.update([editGraphic]);
                    break;
                  }
                }
              }
            });
          });
        }
      });
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
</body>

</html>
by Anonymous User
Not applicable

Great!! Thank you very much Robert this is perfect!

0 Kudos
CoMAGOL_ADMINISTRATOR
Occasional Contributor

Forgive my ignorance, as I a developer newly navigating the obscene complexities of ArcGIS JS, though can't that be simplified by just having an isDragging flag and updating the graphic's geometry directly?

// ... do your setup
const view = new WebMapView(...)
const graphic = new Graphic(...)
view.graphics.add(graphic);

let isDragging = false;
view.when().then(() =>
  view.on('drag', e => {
    // if you've started dragging on the graphic in question
    view.hitTest(e).then(res => {
      // set isDragging to true at the start
      if (res.results[0].graphic === graphic && e.action === "start")
        isDragging = true;
      // set isDragging to false at the end
      else if (e.action === "end")
        isDragging = false
    })

    if (isDragging) {
      // prevent the map being panned mid-drag
      e.stopPropagation();
      // Update the graphic's geometry to the new drag pos
      graphic.geometry = view.toMap(e)
    }
  })
)
RobertScheitlin__GISP
MVP Emeritus

Have you tested your code? Does it work?

CoMAGOL_ADMINISTRATOR
Occasional Contributor

Yes, it works.

0 Kudos
HenryKo2
Occasional Contributor

I can confirm this works for me - this mimicks the Google Maps dragging behaviour nicely and without the need to use any ArcGIS widgets.

0 Kudos
GerardoSevilla
New Contributor

Yes, it does. I am new here, can anybody care to explain, why? The movement takes place out of the hitTest callback, so is there a downside or a problem with this approach? Anyway, thank you @CoMAGOL_ADMINISTRATOR 

0 Kudos