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.
Also, you can try creating Fiddler samples. Here's some examples to get you started:
c/o
Thanks - let me look these over and see where they go.
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.
Dave,
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
Thanks, thejus - that tells me that the example code I looked at was not correct.
Dave,
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/Map..."); 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>
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.
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.