Query points with in multiple Polygon Geometries

2354
8
Jump to solution
08-19-2014 12:28 AM
ChristianDebono
New Contributor II

I have a layer containing a set of points and another layer with polygons. I have queried the points in a single polygon with no problems. Now I want to query these points  with multiple polygon selection; i.e. the user clicks on the polygons which he wants to query and then selects the functionality to query these polygons. Currently, I am doing this procedure as the user clicks the polygons and array is populated with the geometries of the polygons and I looping through this array and query with the geometries.

for (var ai = 0; ai < multiplePolygons.length; ai++) {

    var query = new Query();

    var queryTask = new QueryTask(pointUrl);

    query.geometry = multiplePolygons[ai].areaGeometry;

    query.returnGeometry = true;

    query.outFields = ["*"];

                   

    queryTask.execute(query, function (results) {    

         for (var i = 0; i < results.features.length; i++) {

               var qResult = results.features.attributes["Attribute"];

         }

    });

}

Is there a more conventional way in order to query multiple polygons?

Tags (3)
0 Kudos
1 Solution

Accepted Solutions
OwenEarley
Occasional Contributor III

The ideal solution would be if the query.geometry property took an array as input. However the documentation seems to indicate that it is only a single geometry.

You could look into promises - check out the last example on this page: dojo/promise/all — The Dojo Toolkit - Reference Guide

The trick would be creating the Object that you provide as a parameter dynamically using your multiple polygons. Possibly something like this untested code:

// 1. make sure to add required modules ("dojo/promise/all", "dojo/Deferred", etc )

// 2. dynamically build object for dojo/promise/all parameter

queries = {};

for (var ai = 0; ai < multiplePolygons.length; ai++) {

    var qryName = "qry" + ai;

    queries[qryName] = queryPolygon(multiplePolygons[ai].areaGeometry);

}

// 3. return a deferred object from each query

function queryPolygon(geom) {

    var deferred = new Deferred();

    var query = new Query(); 

    var queryTask = new QueryTask(pointUrl); 

    query.geometry = geom; 

    query.returnGeometry = true; 

    query.outFields = ["*"];                       

    queryTask.execute(query, function (results) { 

        deferred.resolve(results);

    });

    return deferred.promise;

};

// 4. use all to process each polygon then respond to final results

all({queries}).then(function(results){

    // process your final results here

    console.log("All Results: ", results);

});

Please note that I haven't used promises/deferred in dojo, but I have had great success in AngularJS using them.

Owen

Spatial XP

View solution in original post

0 Kudos
8 Replies
OwenEarley
Occasional Contributor III

The ideal solution would be if the query.geometry property took an array as input. However the documentation seems to indicate that it is only a single geometry.

You could look into promises - check out the last example on this page: dojo/promise/all — The Dojo Toolkit - Reference Guide

The trick would be creating the Object that you provide as a parameter dynamically using your multiple polygons. Possibly something like this untested code:

// 1. make sure to add required modules ("dojo/promise/all", "dojo/Deferred", etc )

// 2. dynamically build object for dojo/promise/all parameter

queries = {};

for (var ai = 0; ai < multiplePolygons.length; ai++) {

    var qryName = "qry" + ai;

    queries[qryName] = queryPolygon(multiplePolygons[ai].areaGeometry);

}

// 3. return a deferred object from each query

function queryPolygon(geom) {

    var deferred = new Deferred();

    var query = new Query(); 

    var queryTask = new QueryTask(pointUrl); 

    query.geometry = geom; 

    query.returnGeometry = true; 

    query.outFields = ["*"];                       

    queryTask.execute(query, function (results) { 

        deferred.resolve(results);

    });

    return deferred.promise;

};

// 4. use all to process each polygon then respond to final results

all({queries}).then(function(results){

    // process your final results here

    console.log("All Results: ", results);

});

Please note that I haven't used promises/deferred in dojo, but I have had great success in AngularJS using them.

Owen

Spatial XP

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Christian,

  I prefer to on travel to the server once for this so I have created a Union function:

This function will union any type of geometries into on single geometry for use in a query


     function unionGeoms(gArray){


          var retGeom;


          var mPoint = new Multipoint(this.map.spatialReference);


          var mPoly = new Polygon(this.map.spatialReference);


          var mPolyL = new Polyline(this.map.spatialReference);


          var rType;


          this.polygonsToDiscard = [];


          if(gArray.length > 0 && gArray[0].geometry.type == "polygon"){


            //For each polygon, test if another polgon exists that contains the first polygon.


            //If it does, the polygon will not be included in union operation and it will added to the polygonsToDiscard array.


            dojo.forEach(gArray, lang.hitch(this, function(graphic){


              var poly1 = graphic.geometry;


              dojo.forEach(this.gArray, lang.hitch(this, function(aGraphic){


                var aPoly = aGraphic.geometry;


                  if(aPoly.extent.contains(this.graphic.geometry) && (aPoly.extent.center.x != poly1.extent.center.x || aPoly.extent.center.y != poly1.extent.center.y)){


                    this.polygonsToDiscard.push(poly1);


                  }


              }));


            }));


          }


          //globals


          var poly,


              ext,


              i,


              j,


              mp,


              ringArray;


          dojo.forEach(gArray, lang.hitch(this, function(graphic){


            if(graphic.geometry.type == "point" && !this.cbxAddTolerance.checked){


                mPoint.addPoint(graphic.geometry);


                rType = "point";


            }else if (graphic.geometry.type == "point" && this.cbxAddTolerance.checked){


                ext = this.pointToExtent(graphic.geometry, this.pointSearchTolerance);


                ringArray = this.extentToMPArray(ext);


                mPoly.addRing(ringArray);


                rType = "poly";


                mPoly.spatialReference = ext.spatialReference;


            }


            if(graphic.geometry.type == "multipoint"){


                var mp1 = graphic.geometry;


                var pnts;


                for (var p=0;p < mp1.points.length; p++){


                    mPoint.addPoint(mp1.points
);


                }


                rType = "point";


            }


            if(graphic.geometry.type == "polyline"){


                var polyl = graphic.geometry;


                for(var l=polyl.paths.length-1; l >= 0; l--){


                    var pathArray = [];


                    for (j = 0; j < polyl.paths.length; j++){


                        mp = polyl.getPoint(l,j);


                        mp.spatialReference = polyl.spatialReference;


                        pathArray.push(mp);


                    }


                    mPolyL.addPath(pathArray);


                }


                rType = "line";


            }


            if(graphic.geometry.type == "extent"){


                ext = graphic.geometry;


                ringArray = this.extentToMPArray(ext);


                mPoly.addRing(ringArray);


                rType = "poly";


                mPoly.spatialReference = ext.spatialReference;


            }


            if(graphic.geometry.type == "polygon"){


                poly = graphic.geometry;


                //Consider only the rings that not coincide with any polygon ring on polygonsToDiscard array.


                var targetRings = [];


                for (var m = 0; m < poly.rings.length; m++){


                    var polygonToDiscard;


                    var targetRing = [];


                    var targetPolygon = new Polygon([poly.rings], poly.spatialReference);


                    var add = true;


                    if (this.polygonsToDiscard.length > 0){


                        for (polygonToDiscard in this.polygonsToDiscard){


                            add = true;


                            if (targetPolygon.extent.center.x == polygonToDiscard.extent.center.x && targetPolygon.extent.center.y == polygonToDiscard.extent.center.y) {


                                add = false;


                                break;


                            }


                        }


                        if(add){


                            targetRing[0] = m;


                            targetRing[1] = poly.rings;


                            targetRings.push(targetRing);


                        }


                    }else{


                        targetRing[0] = m;


                        targetRing[1] = poly.rings;


                        targetRings.push(targetRing);


                    }


                }


                for (var i2 = targetRings.length - 1; i2 >=0; i2--){


                    ringArray = [];


                    for (var j1 = 0; j1 < targetRings[i2][1].length; j1++){


                        var mp2 = poly.getPoint(i2,j1);


                        mp2.spatialReference = poly.spatialReference;


                        ringArray.push(mp2);


                    }


                    mPoly.addRing(ringArray);


                }


                rType = "poly";


                mPoly.spatialReference = poly.spatialReference;


            }


          }));


       


          switch(rType){


              case "point":{


                  retGeom = mPoint;


                  break;


              }


              case "poly":{


                  retGeom = mPoly;


                  break;


              }


              case "line":{


                  retGeom = mPolyL;


                  break;


              }


          }


          this.garr = [];


          return retGeom;


      }

0 Kudos
JoshHevenor
Occasional Contributor II

It's a good idea, but there's a pre-made union provided by the GeometryService:

geometryservice-amd | API Reference | ArcGIS API for JavaScript

My first thought was a loop to add the rings to one polygon as well.

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Josh,

   I am always looking to cut down on round trips to the server so doing the union client side would prevent one round trip.

0 Kudos
JoshHevenor
Occasional Contributor II

Which is completely valid.

If round trips are our metric then for Christians problem, given N polygons, both solutions run in O(1) time. But, using the geometry service means 125 lines of code that don't need to be maintained.  Using the API where the functionality exists is what I would try first, knowing that I could push that work to the client if that union call looked to be a hang-up in my process.

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Josh,

   Good point. I think that I created that routine in Action Script (Flex) back in the day because of this note:

The union operation is performed on a geometry service resource (only available with ArcGIS Server 10.0 or above).
0 Kudos
OwenEarley
Occasional Contributor III

This gets back to my original point that ideal solution would be if the query.geometry property took an array as input. This would mean a single round trip to the server.

To limit round-trips and minimize code, using the geometry service first and then doing a single query task is probably as good as it gets at the moment.

In practice your server would have to be very busy to notice much of a performance difference unless you had a very large number of polygons. If the user is manually selecting polygons then this should not happen very often.

0 Kudos
ChristianDebono
New Contributor II

Thank you all for your replies. Owen's answer was the ideal solution.

0 Kudos