select multiple features by mouse clicks

4224
4
Jump to solution
10-31-2019 04:36 PM
ARUNCHANUMALLA1
New Contributor

Guys,

I would like to select multiple features in a FeatureLayer.

I understand you can do this by using the sample (Highlight features by geometry | ArcGIS API for JavaScript 4.13 ).

But, my users would like to use mouse clicks instead of drawing a graphic.

I'm thinking something like SHIFT-CLICK or ALT CLICK for multi selection and releasing the SHIFT OR ALT key would trigger an action (if there are any features selected) like submitting the features selected to a web service.

Hope this all makes sense !

Regards

A

0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Emeritus

Here is a sample for that. Hold 'Ctrl' button fro multi select.

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
  <title>Select by Point</title>

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

  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>
  <script>
    require([
      "esri/views/draw/Draw",
      "esri/Map",
      "esri/Graphic",
      "esri/layers/GraphicsLayer",
      "esri/layers/FeatureLayer",
      "esri/geometry/Point",
      "esri/geometry/Multipoint",
      "esri/views/MapView",
      "dojo/dom",
      "dojo/domReady!"
    ], function (Draw, Map, Graphic, GraphicsLayer, FeatureLayer, Point, Multipoint, MapView, dom) {
      const pntGraphics = new GraphicsLayer();
      var renderer = {
          type: "simple", // autocasts as new SimpleRenderer()
          symbol: {
            type: "simple-fill", // autocasts as new SimpleFillSymbol()
            color: [255, 255, 255, 0.5],
            style: "none",
            outline: {  // autocasts as new SimpleLineSymbol()
              color: "white",
              width: 2
            }
          }
        };
      const statesLyr = new FeatureLayer({
        url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/2',
        renderer: renderer,
        outFields: ['*']
      });

      let drawPnt, graphic, ctrlKey = false, highlight, statesLyrView;

      const map = new Map({
        basemap: "dark-gray",
        layers: [pntGraphics, statesLyr]
      });

      const view = new MapView({
        container: "viewDiv",
        map: map,
        zoom: 5,
        center: [-100, 39]
      });

      statesLyr.when(function(){
        view.whenLayerView(statesLyr).then(function(layerView) {
          statesLyrView = layerView;
        });
      })

      const draw = new Draw({
        view: view
      });

      var sym = {
        type: "simple-marker",
        style: "circle",
        color: [0, 255, 255, 0.6],
        size: "8px",
        outline: {
          color: [0, 255, 255, 1],
          width: 1
        }
      };

      view.ui.add("point-button", "top-left");

      document.getElementById("point-button").onclick = drawPoint;

      function drawPoint() {
        if (highlight) {
          highlight.remove();
        }
        const action = draw.create("point");

        // Give a visual feedback to users as they move the pointer over the view
        action.on("cursor-update", function (evt) {
          view.graphics.removeAll();
          drawPnt = new Point({
            x: evt.coordinates[0],
            y: evt.coordinates[1],
            spatialReference: view.spatialReference
          });
          graphic = new Graphic({
            geometry: drawPnt,
            symbol: sym
          });
          view.graphics.add(graphic);
          if(ctrlKey && !evt.native.ctrlKey){
            draw.reset();
            view.graphics.removeAll();
            selectStates();
          }
        });

        action.on("draw-complete", function (evt) {
          if (evt.native.ctrlKey) {
            ctrlKey = true;
          }else{
            ctrlKey = false;
          }
          drawPnt = new Point({
            x: evt.coordinates[0],
            y: evt.coordinates[1],
            spatialReference: view.spatialReference
          });

          graphic = new Graphic({
            geometry: drawPnt,
            symbol: sym
          });
          pntGraphics.add(graphic);
          if (evt.native.ctrlKey) {
            drawPoint();
          } else {
            view.graphics.removeAll();
            selectStates();
          }
        });
        view.focus();
      };

      function selectStates(){
        let mp = new Multipoint({
          spatialReference: view.spatialReference
        });
        let pntArray = pntGraphics.graphics.map(function(gra){
          mp.addPoint(gra.geometry);
        });
        
        const query = {
          geometry: mp,
          outFields: ["*"],
          outSpatialReference: view.spatialReference,
          returnGeometry: true
        };
        // let query = statesLyr.createQuery();
        // query.geometry = mp;
        // query.outSpatialReference = view.spatialReference;
        // query.returnGeometry = true;
        statesLyr.queryFeatures(query)
        .then(function(results){
          const graphics = results.features;
          // remove existing highlighted features
          if (highlight) {
            highlight.remove();
          }

          // highlight query results
          highlight = statesLyrView.highlight(graphics);
          pntGraphics.removeAll();
        }).catch(function(err){
          console.error(err);
        })
      }
    });
  </script>
</head>

<body>
  <div id="viewDiv">
    <div id="point-button" class="esri-widget esri-widget--button esri-interactive" title="Select Countries">
      <span class="esri-icon-map-pin"></span>
    </div>
  </div>

</body>

</html>

View solution in original post

4 Replies
RobertScheitlin__GISP
MVP Emeritus

Here is a sample for that. Hold 'Ctrl' button fro multi select.

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
  <title>Select by Point</title>

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

  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>
  <script>
    require([
      "esri/views/draw/Draw",
      "esri/Map",
      "esri/Graphic",
      "esri/layers/GraphicsLayer",
      "esri/layers/FeatureLayer",
      "esri/geometry/Point",
      "esri/geometry/Multipoint",
      "esri/views/MapView",
      "dojo/dom",
      "dojo/domReady!"
    ], function (Draw, Map, Graphic, GraphicsLayer, FeatureLayer, Point, Multipoint, MapView, dom) {
      const pntGraphics = new GraphicsLayer();
      var renderer = {
          type: "simple", // autocasts as new SimpleRenderer()
          symbol: {
            type: "simple-fill", // autocasts as new SimpleFillSymbol()
            color: [255, 255, 255, 0.5],
            style: "none",
            outline: {  // autocasts as new SimpleLineSymbol()
              color: "white",
              width: 2
            }
          }
        };
      const statesLyr = new FeatureLayer({
        url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/2',
        renderer: renderer,
        outFields: ['*']
      });

      let drawPnt, graphic, ctrlKey = false, highlight, statesLyrView;

      const map = new Map({
        basemap: "dark-gray",
        layers: [pntGraphics, statesLyr]
      });

      const view = new MapView({
        container: "viewDiv",
        map: map,
        zoom: 5,
        center: [-100, 39]
      });

      statesLyr.when(function(){
        view.whenLayerView(statesLyr).then(function(layerView) {
          statesLyrView = layerView;
        });
      })

      const draw = new Draw({
        view: view
      });

      var sym = {
        type: "simple-marker",
        style: "circle",
        color: [0, 255, 255, 0.6],
        size: "8px",
        outline: {
          color: [0, 255, 255, 1],
          width: 1
        }
      };

      view.ui.add("point-button", "top-left");

      document.getElementById("point-button").onclick = drawPoint;

      function drawPoint() {
        if (highlight) {
          highlight.remove();
        }
        const action = draw.create("point");

        // Give a visual feedback to users as they move the pointer over the view
        action.on("cursor-update", function (evt) {
          view.graphics.removeAll();
          drawPnt = new Point({
            x: evt.coordinates[0],
            y: evt.coordinates[1],
            spatialReference: view.spatialReference
          });
          graphic = new Graphic({
            geometry: drawPnt,
            symbol: sym
          });
          view.graphics.add(graphic);
          if(ctrlKey && !evt.native.ctrlKey){
            draw.reset();
            view.graphics.removeAll();
            selectStates();
          }
        });

        action.on("draw-complete", function (evt) {
          if (evt.native.ctrlKey) {
            ctrlKey = true;
          }else{
            ctrlKey = false;
          }
          drawPnt = new Point({
            x: evt.coordinates[0],
            y: evt.coordinates[1],
            spatialReference: view.spatialReference
          });

          graphic = new Graphic({
            geometry: drawPnt,
            symbol: sym
          });
          pntGraphics.add(graphic);
          if (evt.native.ctrlKey) {
            drawPoint();
          } else {
            view.graphics.removeAll();
            selectStates();
          }
        });
        view.focus();
      };

      function selectStates(){
        let mp = new Multipoint({
          spatialReference: view.spatialReference
        });
        let pntArray = pntGraphics.graphics.map(function(gra){
          mp.addPoint(gra.geometry);
        });
        
        const query = {
          geometry: mp,
          outFields: ["*"],
          outSpatialReference: view.spatialReference,
          returnGeometry: true
        };
        // let query = statesLyr.createQuery();
        // query.geometry = mp;
        // query.outSpatialReference = view.spatialReference;
        // query.returnGeometry = true;
        statesLyr.queryFeatures(query)
        .then(function(results){
          const graphics = results.features;
          // remove existing highlighted features
          if (highlight) {
            highlight.remove();
          }

          // highlight query results
          highlight = statesLyrView.highlight(graphics);
          pntGraphics.removeAll();
        }).catch(function(err){
          console.error(err);
        })
      }
    });
  </script>
</head>

<body>
  <div id="viewDiv">
    <div id="point-button" class="esri-widget esri-widget--button esri-interactive" title="Select Countries">
      <span class="esri-icon-map-pin"></span>
    </div>
  </div>

</body>

</html>
ARUNCHANUMALLA1
New Contributor

Thanks Robert ! That's exactly what i'm looking for.

A

0 Kudos
Valgenmap
New Contributor III

Hi @RobertScheitlin__GISP 

I am trying to include the solution in my ReactJS app but I'm getting error.

Can you help me in this.

Here is my code,

/* eslint-disable */
import React, { useRef, useEffect } from 'react';
import MapView from '@arcgis/core/views/MapView';
import WebMap from '@arcgis/core/Map';
import FeatureLayer from '@arcgis/core/layers/FeatureLayer';
import Draw from '@arcgis/core/views/draw/Draw';
import Graphic from '@arcgis/core/Graphic';
import GraphicsLayer from '@arcgis/core/layers/GraphicsLayer';
import Point from '@arcgis/core/geometry/Point';
import MultiPoint from '@arcgis/core/geometry/Multipoint';

import '../../assets/css/map.css';

const Map = () => {
  const mapDiv = useRef(null);

  useEffect(() => {
    if (mapDiv.current) {
      /**
       * Initialize application
       */
      const pntGraphics = new GraphicsLayer();

      let renderer = {
        type: 'simple', // autocasts as new SimpleRenderer()
        symbol: {
          type: 'simple-fill', // autocasts as new SimpleFillSymbol()
          color: [255, 255, 255, 0.5],
          style: 'none',
          outline: {
            // autocasts as new SimpleLineSymbol()
            color: 'white',
            width: 2,
          },
        },
      };

      let drawPnt,
        graphic,
        ctrlKey = false,
        highlight,
        statesLyrView;

      const webmap = new WebMap({
        basemap: 'streets-vector',
      });

      const view = new MapView({
        container: mapDiv.current,
        map: webmap,
        center: [-98.43750059604514, 38.1986442207947],
        scale: 25000000,
        ui: {
          components: ['zoom', 'compass'],
        },
      });

      const stateFeatureLayer = new FeatureLayer({
        url:
          'https://services2.arcgis.com/JoecHEvChY6qFe2m/arcgis/rest/services/USA_States/FeatureServer/0',
        outFields: ['*'],
      });

      const cityFeatureLayer = new FeatureLayer({
        url:
          'https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/2',
        outFields: ['*'],
      });

      webmap.addMany([stateFeatureLayer, cityFeatureLayer]);

      stateFeatureLayer.when(function () {
        view.whenLayerView(stateFeatureLayer).then(function (layerView) {
          statesLyrView = layerView;
        });
      });

      const draw = new Draw({
        view: view,
      });

      var sym = {
        type: 'simple-marker',
        style: 'circle',
        color: [0, 255, 255, 0.6],
        size: '8px',
        outline: {
          color: [0, 255, 255, 1],
          width: 1,
        },
      };

      view.ui.add('point-button', 'top-left');

      function drawPoint() {
        console.log('in ');
        if (highlight) {
          highlight.remove();
        }
        const action = draw.create('point');
        action.on('cursor-update', function (evt) {
          view.graphics.removeAll();
          drawPnt = new Point({
            x: evt.coordinates[0],
            y: evt.coordinates[1],
            spatialReference: view.spatialReference,
          });
          graphic = new Graphic({
            geometry: drawPnt,
            symbol: sym,
          });
          view.graphics.add(graphic);
          /*if (ctrlKey && !evt.native.ctrlKey) {
              draw.reset();
              view.graphics.removeAll();
              selectStates();
          }*/
        });
        action.on('draw-complete', function (evt) {
          if (evt.native.ctrlKey) {
            ctrlKey = true;
          } else {
            ctrlKey = false;
          }
          drawPnt = new Point({
            x: evt.coordinates[0],
            y: evt.coordinates[1],
            spatialReference: view.spatialReference,
          });
          graphic = new Graphic({
            geometry: drawPnt,
            symbol: sym,
          });
          pntGraphics.add(graphic);
          if (evt.native.ctrlKey) {
            drawPoint();
          } else {
            view.graphics.removeAll();
            selectStates();
          }
        });
        view.focus();
      }

      function selectStates() {
        console.log('in selectstate');
      }

      document.getElementById('point-button').onclick = drawPoint;

      // view.on('click', function (event) {
      //   // only include graphics from hurricanesLayer in the hitTest
      //   const opts = {
      //     include: [stateFeatureLayer, cityFeatureLayer],
      //   };
      //   view.hitTest(event, opts).then(function (response) {
      //     // check if a feature is returned from the hurricanesLayer
      //     if (response.results.length) {
      //       const graphic = response.results[0].graphic;
      //       // do something with the graphic
      //       console.log(graphic.attributes);
      //     }
      //   });
      // });
    }
  }, []);

  return (
    <div className="mapDiv" ref={mapDiv}>
      <div
        id="point-button"
        className="esri-widget esri-widget--button esri-interactive"
        title="Select Countries"
      >
        <span className="esri-icon-map-pin"></span>
      </div>
    </div>
  );
};

export default Map;

 

When I am trying to multi-select then I'm receiving this error at line 120 [native undefined].

native undefined.PNG

 Thank you.

 

0 Kudos
RyanPrice
New Contributor

I am seeing the same. 
version 4.13, in the first example, is okay. 
version 4.24 is NOT okay.  The draw-complete event is broken.  It does not have the native property. 

0 Kudos