Get feature-count from a cached service for current extent?

2174
6
12-03-2012 02:17 AM
SimonJackson
Occasional Contributor III
I have one points layer in a cached map service, symbolised with a unique value renderer on one field.
e.g. petrol stations, symbolised by the company who owns them.

One of the main (and only) components I need, is a dynamic legend, that shows a feature count for the current extent.
e.g. Shell - 34, BP - 21, Texaco - 7, etc

I should point out that this is a cached service for a reason.  Theres a lot of points, and they need to be visible at all times (clustering/heat maps not an option). At full extent, your looking at nearly 80k points.

With this in mind, I need to find out a count for features in the layer, broken down by the unique value renderer, with the current extent as a bounding box.

Has anyone seen any code that could get me started on this or can provide some references/pseudocode on how one would go about this?

Or perhaps I am not being realistic, and it will not be responsive enough?

The actual idea I have in mind, is to have a vertical chart for the 5 types of petrol station.
This chart changes as the extent changes, pending how many total features are in the current extent.
This chart will serve as both a legend (as the 5 types are 5 distinct coloured points) and provide an idea of how the totals are different in different regions.
0 Kudos
6 Replies
AxelSchaefer
New Contributor II
Hmmm... featurelayer with on-demand or selection mode depending on your application logic. Or a query where you don't return geometries to reduce traffic. Adjust the featurelayer scales or make the query scale dependent. Check the limit of returned features of an AGS service. For displaying: The js-api documentation contain some samples with dojo-charts (query section). Maybe the query statistics of 10.1 may help for summaries.

So choose and test which way is appropriate.

Best regards,
Axel
0 Kudos
derekswingley1
Frequent Contributor
If your data is still on your server, you can use a queryTask to query it. Specify the map extent as query.geometry and you'll get back all features on that extent. Since you're really interested in attribute info, you could also set query.returnGeometry to false so less info is sent to the client. Once the query completes, you could figure out how many features fit each renderer category.
0 Kudos
SimonJackson
Occasional Contributor III
Thanks Derek.
At max extent, I have at least 5000 values for each type of petrol station.
This gets smaller as they zoom in.

I see that I can choose 'show count only' on a Query.
Would it be a good idea to do 5 separate queries (triggered on every pan), each returning the count for 5 individual WHERE clauses, and then using results to populate the graph.

OR

1 query, with a WHERE clause to return all the petrol stations, including the type field (but no geometries).

I imagine the latter would lead to a 'smoother' update of the graph component?

You mention:
Once the query completes, you could figure out how many features fit each renderer category.

What would be a good way to parse through the results to get a total tally for each petrol station type?
0 Kudos
SimonJackson
Occasional Contributor III
I have got a bit further, but still stuck.

Final Goal: Update a Dojo chart (pie or bar) to show the breakdown of petrol station companies within the current extent.

Currently, I have successfully got 5 queryTasks running smoothly, making use of executeForCount, to update 5 labels in a dojo panel. 

        //Shell
var shellQuery = new esri.tasks.Query();
var shQT = new esri.tasks.QueryTask(tweetsURL)
shellQuery .geometry = myMap.extent
shellQuery .returnGeometry = false;
shellQuery .where = "brand = 'Shell"
shQT .executeForCount(shellQuery , function(count){
    dojo.byId("shellCount").innerHTML = count;
  },function(error){
    console.log(error);
  });

//BP
var bpQuery = new esri.tasks.Query();
var bpQT = new esri.tasks.QueryTask(tweetsURL)
bpQuery .geometry = myMap.extent
bpQuery .returnGeometry = false;
bpQuery .where = "brand = 'BP'"
bpQT.executeForCount(bpQuery , function(count){
    dojo.byId("bpCount").innerHTML = count;
  },function(error){
    console.log(error);
  });


Im now scratching my head on the best way to send the results of these queries to a dojo chart.
I was perhaps thinking of creating a global array, and when each QT finishes, it updates the array with a language/count pair?

Can someone give me some pointers or samples that would help me out? 
If need be, I can re-write my code to work against something a layer from the Esri Sample Servers.
0 Kudos
SimonJackson
Occasional Contributor III
I got quite excited by the response over at Stack Overflow, which mentions how you can utilise Dojo Promises to ensure that all of the querytasks have finished executing before passing the results onto a Dojo Chart.

However, thats in Dojo 1.8 and 3.2 API is built on 1.7.
I imagine it may be possible to mix up versions of Dojo, but
1. This project is really for me to learn JS, and I imagine mixing versions may cause me some rookie confusion.
2. Was hoping to later evolve the app to be mobile friendly with something like a bootstrap template, and assume that loading in two versions of Dojo may slow initial load times?

I could do with some expert advice here.  Im sure there are a number of paths I could take, I just want the simplest approach which still follows best practise from a coding perspective.

I am now re-thinking on my approach of running 4 independent queries.  They work, and update labels in my app fairly quick, but perhaps thinking that I could be making use of Query Statistics and getting it all done in one query (bearing in mind that im dealing with a large number of points here).
0 Kudos
DerekMiller
New Contributor III
For the time being, I believe you can still use dojo DeferredList (deprecated in 1.8 for dojo promises). I'm not familiar with promises, but I'm currently doing something similar, and I'm using a DeferredList.

function chart1() {
    var cq1_extent = map.extent;
    var c1QT = new esri.tasks.QueryTask(gdaURL);
    var cq1_platinum = new esri.tasks.Query();
    cq1_platinum.where = "review = 'false' AND LEED_rating = 'Platinum'";
    var cq1_gold = new esri.tasks.Query();
    cq1_gold.where = "review = 'false' AND LEED_rating = 'Gold'";
    var cq1_silver = new esri.tasks.Query();
    cq1_silver.where = "review = 'false' AND LEED_rating = 'Silver'";
    var cq1_certified = new esri.tasks.Query();
    cq1_certified.where = "review = 'false' AND LEED_rating = 'Certified'";
    cq1_platinum.geometry = cq1_extent;
    cq1_gold.geometry = cq1_extent;
    cq1_silver.geometry = cq1_extent;
    cq1_certified.geometry = cq1_extent;
    cPlatinum = c1QT.executeForCount(cq1_platinum);
    cGold = c1QT.executeForCount(cq1_gold);
    cSilver = c1QT.executeForCount(cq1_silver);
    cCertified = c1QT.executeForCount(cq1_certified);
    c1List = new dojo.DeferredList([cPlatinum, cGold, cSilver, cCertified]);
    c1List.then(buildChart1);
  }
  function buildChart1(results) {
    console.log(results[0] + " platinum records");
    console.log(results[1] + " gold records");
    console.log(results[2] + " silver records");
    console.log(results[3] + " certified records");
  }


Based on this sample: http://help.arcgis.com/en/webapi/javascript/arcgis/help/jssamples_start.htm#jssamples/query_deferred...