I'm looking for a way to use the mouse to select a group of features on a map by dragging a rectangle around them. I can select a single feature on the map by simply clicking on that feature - that much I have working. But now I want to be able to drag a rectangle around a group of features and select all of them so I can do something with all of them. So far I haven't found any code examples out there that discuss this.
Hi Dave,
Here is a quick example on how to select features using a freehand drawn polygon:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <!--The viewport meta tag is used to improve the presentation and behavior of the samples on iOS devices--> <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"> <title>Select Features</title> <link rel="stylesheet" href="http://js.arcgis.com/3.14/esri/css/esri.css"> <style> html, body, #mapDiv { padding:0; margin:0; height:100%; } </style> <script src="http://js.arcgis.com/3.14/"></script> <script> var map, tb, polygon; require([ "esri/map", "esri/toolbars/draw", "esri/layers/FeatureLayer", "esri/tasks/query", "esri/symbols/SimpleFillSymbol", "esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleMarkerSymbol", "esri/graphic", "esri/geometry/Polygon", "dojo/_base/Color", "dojo/dom", "dojo/on", "dojo/_base/array", "dojo/domReady!" ], function( Map, Draw, FeatureLayer, Query, SimpleFillSymbol, SimpleLineSymbol, SimpleMarkerSymbol, Graphic, Polygon, Color, dom, on, array ) { map = new Map("mapDiv", { basemap: "streets", center: [-81.792107, 26.150807], zoom: 8 }); map.on("load", initToolbar); var featureLayer = new FeatureLayer("http://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer/0",{ mode: FeatureLayer.MODE_ONDEMAND, }); var symbol = new SimpleMarkerSymbol( SimpleMarkerSymbol.STYLE_CIRCLE, 12, new SimpleLineSymbol( SimpleLineSymbol.STYLE_NULL, new Color([247, 34, 101, 0.9]), 1 ), new Color([207, 34, 171, 0.5]) ); featureLayer.setSelectionSymbol(symbol); map.addLayer(featureLayer); var fillSymbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_NULL, new SimpleLineSymbol(SimpleLineSymbol.STYLE_DASHDOT, new Color([255,0,0]), 2) ); function initToolbar() { tb = new Draw(map); tb.on("draw-end", addGraphic); //map.disableMapNavigation(); tb.activate(Draw.FREEHAND_POLYGON); } function addGraphic(evt) { map.graphics.clear() map.graphics.add(new Graphic(evt.geometry, fillSymbol)); polygon = new Polygon(evt.geometry) var query = new Query(); query.geometry = polygon.getExtent(); featureLayer.queryFeatures(query, selectInBuffer); } function selectInBuffer(response){ var feature; var features = response.features; var inBuffer = []; for (var i = 0; i < features.length; i++) { feature = features; if(polygon.contains(feature.geometry)){ inBuffer.push(feature.attributes[featureLayer.objectIdField]); } } var query = new Query(); query.objectIds = inBuffer; featureLayer.selectFeatures(query, FeatureLayer.SELECTION_NEW, function(features, index){ for(var i = 0; i < features.length; i++){ console.log(features.geometry.x + ", " + features.geometry.y) } }) } }); </script> </head> <body> <div id="mapDiv"></div> </body> </html>
Dave,
Will this example work for you?
Select graphic points within an extent | ArcGIS API for JavaScript
Thanks - let me look these over and see where they go.
I've been working with this code, but have a problem with this line:
featureLayer.queryFeatures(query, selectInBuffer)
The error I'm getting is that queryFeatures is not a member of featureLayer. Now there is a wrinkle on my end that isn't in your example code - I'm extracting the layer from the existing map object - not just creating one. Now as far as I know the layer is a featureLayer, but I may be wrong about that. The code that creates the layer was written by another developer - I haven't dug it out yet to see if something is amiss there.
With regard to the example you gave, I get the following:
if (extent.contains(graphic.geometry)) {
contains is not a member of extent.
I tried a hybrid of what you and Jake said, but still can't get things working. I would show you what I have, but for some reason this editor won't accept the pasted code.
It really sounds like you have your Requires out of line/out of sync with your vars. Here is an example:
require([
'dojo/on',
'dojo/aspect',
'dojo/_base/lang'], function(
on, aspect, lang) {
Did part of your reply get cut off?
David,
Just a heads-up, if you're having trouble with the editor, try:
-> copy code from application
-> paste into a blank notepad document
-> click "use advanced editor"
-> copy code from notepad
-> paste into geonet editor box
-> add a few newlines after the paste with some dummy text
-> select your code block
-> click the ">>" insert function
-> choose "syntax highlighting"
-> then select JavaScript
This should resolve any issues with adding code to geonet.
Also, you can try creating Fiddler samples. Here's some examples to get you started:
gavinr's Fiddles - JSFiddle - Online JavaScript Editor - jQuery, Angular, Backbone, Underscore, Knockout, YUI
c/o
Gavin Rehkemper
Here you go...
require(["esri/toolbars/draw"], function (draw) { tb = new draw(map); tb.on("draw-end", getFeatures); tb.activate(draw.EXTENT); }); getFeatures = function (evt) { require(["esri/geometry/Polygon", "esri/tasks/query"], function (polygon, query) { var extentPoly = new polygon(evt.geometry); var extentQuery = new query(); extentQuery.geometry = extentPoly.getExtent(); dojo.forEach(map.graphics.graphics, function (graphic) { if (extentQuery.contains(graphic.geometry)) { selectedFeatures.push(graphic.getContent()); } }); }); }
extentQuery says that it doesn't have a 'contains' method.
extentQuery is a query object and thus does not have a contains method (this is what the error is telling you). You should be using extentPoly.contains
if (extentPoly.contains(graphic.geometry)) {
Changing that line to extentPoly.contains gets me past that error, but something still isn't right. I don't get any items added to the selectedFeatures array, even though there were three features under the rectangle I selected. In fact, the forEach loop only loops once, even though there are many features on the map.
Let me ask you - if you were writing the code to do something like this, how would you approach it?
You cannot create a Polygon by using var extentPoly = new polygon(evt.geometry); since there is no constructor for Polygon which takes geometry/Extent for parameter. Atleast as per documentation.
The right way to do it is by using the fromExtent static method like this
var extentPoly = Polygon.fromExtent(evt.geometry);
Hope this helps
The way I would approach it is like the sample Chris Smith provided but written in AMD:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <!--The viewport meta tag is used to improve the presentation and behavior of the samples on iOS devices--> <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"> <title>Points in Extent</title> <link rel="stylesheet" href="http://js.arcgis.com/3.14/dijit/themes/claro/claro.css"> <link rel="stylesheet" href="http://js.arcgis.com/3.14/esri/css/esri.css"> <script src="http://js.arcgis.com/3.14/"></script> <script> var map; require([ "esri/map", "esri/toolbars/draw", "esri/tasks/QueryTask", "esri/tasks/query", "esri/symbols/SimpleFillSymbol", "esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleMarkerSymbol", "esri/graphic", "esri/geometry/Polygon", "esri/InfoTemplate", "dojo/_base/Color", "dojo/dom", "dojo/on", "dojo/_base/array", "dojo/domReady!" ], function( Map, Draw, QueryTask, Query, SimpleFillSymbol, SimpleLineSymbol, SimpleMarkerSymbol, Graphic, Polygon, InfoTemplate, Color, dom, on, array ) { //create map, set initial extent and disable default info window behavior map = new Map("mapDiv", { basemap: "streets", center: [-120.275, 47.485], zoom: 6, slider: false, showInfoWindowOnClick:false }); map.on("load", function(){ //initialize & execute query var queryTask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/0"); var query = new Query(); query.where = "STATE_NAME = 'Washington'"; query.outSpatialReference = map.spatialRefernce; query.returnGeometry = true; query.outFields = ["CITY_NAME"]; queryTask.execute(query, addPointsToMap); var toolbar = new Draw(map); //find points in Extent when user completes drawing extent toolbar.on("draw-end", findPointsInExtent); //set drawing mode to extent toolbar.activate(Draw.EXTENT); }); //initialize symbology defaultSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 16, new SimpleLineSymbol(), new Color([0,0,255])); highlightSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 16, new SimpleLineSymbol(), new Color([255,0,0])); //info template for points returned resultTemplate = new InfoTemplate("City", "<tr><td>${CITY_NAME}</tr></td>"); //add points to map and set their symbology + info template function addPointsToMap(featureSet) { array.forEach(featureSet.features, function(feature){ map.graphics.add(feature.setSymbol(defaultSymbol).setInfoTemplate(resultTemplate)); }); } //find all points within argument extent function findPointsInExtent(extent) { var results = []; array.forEach(map.graphics.graphics, function(graphic){ if (extent.geometry.contains(graphic.geometry)) { graphic.setSymbol(highlightSymbol); results.push(graphic.getContent()); } //else if point was previously highlighted, reset its symbology else if (graphic.symbol == highlightSymbol) { graphic.setSymbol(defaultSymbol); } }); //display number of points in extent dojo.byId("inextent").innerHTML = results.length; //display list of points in extent dojo.byId("results").innerHTML = "<table><tbody>" + results.join("") + "</tbody></table>"; } }); </script> </head> <body class="claro"> Draw an Extent on the map to find all points within this extent <!-- map div --> <div id="mapDiv" style="width:800px; height:400px; border:1px solid #000;"></div> <br /> <!-- display number of points in drawn extent --> <b># of points in extent = <span id="inextent">0</span></b> <!-- list points in extent --> <div id="results" style="width:400px; height:200px; border:1px solid #000; overflow:auto;"> </div> </body> </html>
When Robert refers to AMD, he is referring to Asynchronous Module Definition (Introduction to AMD Modules - Dojo Toolkit Tutorial). These are the modules included in the Require at the top of your main page, like so:
require([ 'dojo/on', 'dojo/aspect', 'dojo/_base/lang'], function( on, aspect, lang) {
It looks like the sample I gave used the Legacy Module Require, which looks like:
dojo.require("esri.layers.DataSource")
You will find the JSAPI generally shows both in reference, but you'll want to use AMD.
So here is where I currently am:
require(["esri/toolbars/draw"], function (draw) { tb = new draw(map); tb.on("draw-end", getFeatures); tb.activate(draw.EXTENT); });
getFeatures = function (extent) {
//third try require(["dojo/_base/array"], function (array) { array.forEach(map.graphics.graphics, function (graphic) { if (extent.geometry.contains(graphic.geometry)) { selectedFeatures.push(graphic.getContent()); } }); });
}
but again, I don't get any items added to the selectedFeatures array, even though there were three features under the rectangle I selected. In fact, the forEach loop only loops once, even though there are many features on the map.
Maybe it would help to point out that this functionality is associated with a button on a toolbar. When the user clicks the button, the tool is activated so they can select the features they want on the map. It's just one of a pretty extensive array of tools - not just a stand alone page.
I am wondering if you are targeting the correct layer. Please ensure the Graphics you intend to select are part of the map.graphics layer and not some other FeatureLayer/GraphicsLayer.
Les membres connectés peuvent publier, suivre les mises à jour, et plus encore. Nouveau ici ? Inscrivez-vous gratuitement.
Find useful guides, FAQs, and documents to help you navigate and make the most of Esri Community.