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.
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.
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.
Dave,
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.
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>
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
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?
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)) {
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.
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
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.
Did part of your reply get cut off?
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) {
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.
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.
Thanks - let me look these over and see where they go.
Will this example work for you?
Select graphic points within an extent | ArcGIS API for JavaScript
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>
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.