ArcGIS Javascript IdentifyTask Returns Scale Dependent Layers

Problem: IdentifyTask returns scale dependent layers outside of scale range. It’s different between jsapi 2.x and 3.x. It’s taken me about six attempts to get this right, each time declaring that I’ve nailed it!!.. in mock irony, because one of my C++ professor’s favorite lines was “…Son, programming is like nailing jello to a wall.” Guess the post modern irony didn’t help.

A Solution: Note the checking of subLayerIds for null, scale checking, after getting the visibleLayers from service.


function doIdentify(evt) {
    var referenceLayer = new esri.layers.ArcGISDynamicMapServiceLayer(referenceMapServiceName, {id: 'referenceLayer'});
    var aVisibleSubLayers = referenceLayer.visibleLayers,
        aLayersInScale = [],
        identifyParams = new esri.tasks.IdentifyParameters(),
        identifyTask = new esri.tasks.IdentifyTask(referenceLayer.url);

    //loop thru visible sublayers that are not group layers, then check within scale
    if (referenceLayer.visible === true && aVisibleSubLayers.length !== 0 && aVisibleSubLayers[0] !== -1) {
        dojo.forEach(referenceLayer.createDynamicLayerInfosFromLayerInfos(), function (dLayInfo) {
            if (dLayInfo.subLayerIds === null && dojo.indexOf(aVisibleSubLayers, dLayInfo.id) !== -1 &&
                    (dLayInfo.minScale === undefined || dLayInfo.minScale === 0 || map.getScale() <= dLayInfo.minScale) &&
                    (dLayInfo.maxScale === undefined || dLayInfo.maxScale === 0 || map.getScale() >= dLayInfo.maxScale)) {
                aLayersInScale.push(dLayInfo.id);
            }
        });
    }

    if (aLayersInScale.length > 0) {
        identifyParams.geometry = evt.mapPoint;
        identifyParams.mapExtent = map.extent;
        identifyParams.tolerance = 3;
        identifyParams.returnGeometry = true;
        identifyParams.layerIds = aLayersInScale;
        identifyParams.layerOption = esri.tasks.IdentifyParameters.LAYER_OPTION_ALL;
        identifyParams.width = map.width;
        identifyParams.height = map.height;
        identifyTask.execute(identifyParams, function(idResults) { 
            doSomethingWithIdentifyResults(idResults);
        });
    }
}

Bonus: Identifies on multiple services at once and returns results as one array using dojo.DeferredList


dojo.require("dojo.DeferredList");

referenceLayer = new esri.layers.ArcGISDynamicMapServiceLayer(referenceMapServiceName, {id: 'referenceLayer'});

//Operational layers
operationalLayer = new esri.layers.ArcGISDynamicMapServiceLayer(operationalMapServiceName, {id: 'operationalLayer'});

function executeIdentify(geom, serviceName, aLayerIds) {
    var identifyParams = new esri.tasks.IdentifyParameters(),
        identifyTask = new esri.tasks.IdentifyTask(serviceName);
    identifyParams.geometry = geom;
    identifyParams.mapExtent = map.extent;
    identifyParams.tolerance = 3;
    identifyParams.returnGeometry = true;
    identifyParams.layerIds = aLayerIds;
    identifyParams.layerOption = esri.tasks.IdentifyParameters.LAYER_OPTION_ALL;
    identifyParams.width = map.width;
    identifyParams.height = map.height;
    return identifyTask.execute(identifyParams);
}

function doIdentify(evt) {
    var geom = evt.mapPoint,
        aLayersInScale,
        aDeferreds = [],
        deferredList,
        currentScale = map.getScale(),
        aVisibleSubLayers,
        aAGSDynMapServicesToIdentify = [referenceLayer, operationalLayer];

    map.graphics.clear();
    identifyLayerKeyPrevious = undefined;

    //loop thru dynamic services, then thru visible sublayers that are not group layers, then check within scale
    dojo.forEach(aAGSDynMapServicesToIdentify, function (agsDynMapService) {
        aVisibleSubLayers = agsDynMapService.visibleLayers;
        if (agsDynMapService.visible === true && aVisibleSubLayers.length !== 0 && aVisibleSubLayers[0] !== -1) {
            aLayersInScale = [];
            dojo.forEach(agsDynMapService.createDynamicLayerInfosFromLayerInfos(), function (dLayInfo) {
                if (dLayInfo.subLayerIds === null && dojo.indexOf(aVisibleSubLayers, dLayInfo.id) !== -1 &&
                        (dLayInfo.minScale === undefined || dLayInfo.minScale === 0 || currentScale <= dLayInfo.minScale) &&
                        (dLayInfo.maxScale === undefined || dLayInfo.maxScale === 0 || currentScale >= dLayInfo.maxScale)) {
                    aLayersInScale.push(dLayInfo.id);
                }
            });
            if (aLayersInScale.length > 0) {
                aDeferreds.push(executeIdentify(evt.mapPoint, agsDynMapService.url, aLayersInScale));
            }
        }
    });

    // create a deferred list to aggregate the state for multiple asynchronous identify queries
    deferredList = new dojo.DeferredList(aDeferreds);
    deferredList.then(function (aIdentifyResults) {
        // "aIdentifyResults" is 2D array of results
        // array[n][0] boolean true or false, success or failure of individual call
        // array[n][1] is the array of identity results returned
        doSomethingWithIdentifyResults(aIdentifyResults);
    });
}
Update July 2013

Here’s an example of doSomethingWithIdentifyResults(), but I’m kind of embarassed because I’ve need to clean this up for awhile and just haven’t had the occassion to do it, but maybe it’s still useful.

I’m creating an jQuery accordion from the results that functions somewhat like the ArcGIS Desktop Identify tool. Here you go –

Additional Caveat: Not sure how this will work with the latest API, but it was working fine with 3.2 I believe.


//html fragment

//Use custom theme copy of jQuery ui -->
<script type="text/javascript" src="js/jquery-ui-1.8.23.custom/js/jquery-1.8.0.min.js"></script>
<script type="text/javascript" src="js/jquery-ui-1.8.23.custom/js/jquery-ui-1.8.23.custom.min.js"></script>
<link href="js/jquery-ui-1.8.23.custom/css/dark-hive/jquery-ui-1.8.23.custom.css" rel="stylesheet" type="text/css" />

<body>    
    <div id="dialogIdentify" title="Identify Features">
        <div id="accordionIdentify">
        </div>
    </div>
</body>

function doSomethingWithIdentifyResults(res) {
    var finalResults = [],
        shouldReturn = false;

    $("#accordionIdentify").accordion("destroy");
    $("#accordionIdentify").empty();

    $.each(res, function (k1, v1) {
        // array[n][0] indicates success or failure
        if (v1[0] === false) {
            $("#accordionIdentify").append("

An error occurred during the identify query.

"); shouldReturn = true; return false; //only breaks loop } // array[n][1] is the array of identity results returned if (v1 !== undefined && v1[1].length > 0) { v1[1].reverse(); finalResults = finalResults.concat(v1[1]); } }); if (shouldReturn) { return; } if (finalResults.length === 0) { $("#accordionIdentify").append("

No results were found.

"); return; } idResultsForHighlighting = finalResults; //build the accordian widget var lastPanel = 0; var isAnotherFeature = 0; var prevlayerName = finalResults[0].layerName; var innerHtml = "

" + prevlayerName + "

"; $.each(finalResults, function (layerKey, layerValue) { if (prevlayerName === layerValue.layerName) { //layer has not changed if (isAnotherFeature > 0) { innerHtml = innerHtml + "
"; //start another feature by drawing hr } } else { //layer has changed innerHtml = innerHtml + "
"; //close innerHtml = innerHtml + "

" + layerValue.layerName + "

"; //start new accordian fold isAnotherFeature = 0; //reset feature count for layer lastPanel++; } innerHtml = innerHtml + "Highlight Feature"; //create table of attributes and values for feature innerHtml = innerHtml + ""; $.each(layerValue.feature.attributes, function (key, value) { innerHtml = innerHtml + ""; }); innerHtml = innerHtml + "
" + key + "" + value + "
"; prevlayerName = layerValue.layerName; //check for layer change isAnotherFeature++; }); innerHtml = innerHtml + "
"; $("#accordionIdentify").append(innerHtml); $("#accordionIdentify").accordion({ autoHeight: false, collapsible: true, active: lastPanel //activate:false has all h3's closed, activate:activatePanel opens last added h3 }); $("#accordionIdentify .identifyHL").on('click', function () { showFeature($(this).attr('id')); }); } var idResultsForHighlighting; var identifyLayerKeyPrevious; var isIdentifyHighlighted = false; function showFeature(identifyLayerKey) { map.graphics.clear(); //this if statement makes the highlight link toggle, if feature already highlighted if (isIdentifyHighlighted === false || identifyLayerKeyPrevious === undefined || identifyLayerKeyPrevious !== identifyLayerKey) { var idResult = idResultsForHighlighting[identifyLayerKey]; var symbol; if (idResult.geometryType === "esriGeometryPoint") { symbol = new esri.symbol.SimpleMarkerSymbol(); symbol.setColor(new dojo.Color([0, 255, 255, 0.7])); } else if (idResult.geometryType === "esriGeometryPolygon") { symbol = new esri.symbol.SimpleFillSymbol(); symbol.setColor(new dojo.Color([0, 255, 255, 0.7])); } else { //"esriGeometryPolyline" symbol = new esri.symbol.SimpleLineSymbol(); symbol.setColor(new dojo.Color([0, 255, 255, 0.7])); } idResult.feature.setSymbol(symbol); map.graphics.add(idResult.feature); isIdentifyHighlighted = true; } else { isIdentifyHighlighted = false; } identifyLayerKeyPrevious = identifyLayerKey; }

4 comments

  1. Simon says:

    This looks very helpful. Can you also share the “doSomethingWithIdentifyResults” function?

    Thanks
    Simon

  2. Stu says:

    Very helpful. I had a situation where I was looping through a set of geometries and buffering them if they fit my requirements. I was wondering how to do push the jobs into an deferredList so I could work with the end result set from the various buffer calls. A+ to you. For the record I am a fan of po-mo irony.

Leave a Reply

Your email address will not be published. Required fields are marked *