Run Query within Identify?

2649
17
Jump to solution
02-16-2016 09:36 AM
LauraMiles1
Occasional Contributor III

Hi All,

I have a point dataset which represents the x,y of where photos were taken. Each photo is a member of a route (as identified by an attribute column). When a particular photo is clicked, I would like all of the photos in that route to be selected and added to the resulting infoWindow so the user can scroll back and forth along the particular route.

I currently have an IdentifyTask setup on this layer and am wondering if it's easiest to just modify that in some way? Any hints would be much appreciated, I have no idea where to begin. My code for the identify is below, it's basically the same as in the ESRI sample.

    identifyAssetsParams = new IdentifyParameters();
    identifyAssetsParams.tolerance = 3;
    identifyAssetsParams.returnGeometry = true;
    identifyAssetsParams.layerOption = IdentifyParameters.LAYER_OPTION_VISIBLE;
    identifyAssetsParams.width = mapObj.width;
    identifyAssetsParams.height = mapObj.height; 

identifyAssetsParams.geometry = event.mapPoint;
    identifyAssetsParams.mapExtent = mapObj.extent;

        identifyAssetsParams.layerIds = layerAssets.visibleLayers;
     var assetsDeferred = identifyAssetsTask
        .execute(identifyAssetsParams)
        .addCallback(function (response) {

        return arrayUtils.map(response, function (result) {
            var feature = result.feature;            
            var layerName = result.layerName;

            feature.attributes.layerName = layerName;            
            if (layerName === 'Primary Direction'){
                            
            var assetsTemplate = new InfoTemplate();
            assetsTemplate.setTitle("Primary Direction");
            feature.setInfoTemplate(assetsTemplate);
            assetsTemplate.setContent("</br> ID:  ${ID} </br> Route:  ${ROUTE} \n\
                        </br><img src='${REL_PATH}'/>");
                }
                
            if (layerName === 'Opposite Direction'){
                            
            var assetsTemplate = new InfoTemplate();
            assetsTemplate.setTitle("Opposite Direction");
            feature.setInfoTemplate(assetsTemplate);
            assetsTemplate.setContent("</br> ID:  ${ID} </br> Route:  ${ROUTE} \n\
                        </br><img src='${REL_PATH}'/>");
                }
                
            if (layerName === 'SeaView'){
                            
            var assetsTemplate = new InfoTemplate();
            assetsTemplate.setTitle("SeaView");
            feature.setInfoTemplate(assetsTemplate);
            assetsTemplate.setContent("Series:  ${Series} \n\
                        </br><img src='${REL_PATH}' width='200'/>");
                }
   
            else {
                 var generalTemplate = new InfoTemplate(layerName);
                 feature.setInfoTemplate(generalTemplate);
            }
           
            return feature;
         });
     });

          mapObj.infoWindow.clearFeatures();
          mapObj.infoWindow.setFeatures([assetsDeferred]);
          mapObj.infoWindow.show(event.mapPoint);
0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Emeritus

Laura,

  Here is a working sample:

<!DOCTYPE html>
<html>

<head>
  <title>Maps on Request</title>
  <script src="http://js.arcgis.com/3.15/"></script>
  <link rel="stylesheet" href="http://js.arcgis.com/3.15/dijit/themes/claro/claro.css">
  <link rel="stylesheet" href="http://js.arcgis.com/3.15/esri/css/esri.css">
  <style>
    html,
    body {
      height: 100%;
    }

    #mapDiv {
      width: 100%;
      height: 100%;
      z-index: 0;
      position: absolute;
    }

    #container {
      overflow: hidden;
      height: 100%;
    }

    #Coords {
      display: block;
      position: absolute;
      z-index: 2;
      bottom: 25px;
      left: 50%;
      alignment-adjust: 50%;
      margin-left: -140px;
      background-color: lightgray;
      -moz-border-radius: 15px;
      border-radius: 15px;
      padding: 5px;
      padding-left: 15px;
      padding-right: 15px;
      opacity: .8;
      text-align: center;
      cursor: pointer;
    }

    #testdiv {
      float: right;
      width: 50px;
    }

    #btnMenuToggle {
      width: 50px;
      height: 50px;
      margin-left: -50px;
      margin-bottom: -90px;
      position: relative;
      z-index: 1;
    }

    #TOC {
      margin: 10px;
      padding: 10px;
      font-family: verdana, helvetica, arial;
      z-index: 10;
      -moz-border-radius: 5px;
      border-radius: 5px;
      border-style: solid;
      border-color: black;
      border-width: 1px;
    }

    #leftPanel {
      height: 100%;
      overflow: hidden;
      width: 275px;
      z-index: 10;
    }

    #rightPanel {
      font-family: verdana, helvetica, arial;
      height: 100%;
      top: 0px;
      position: absolute;
      float: right;
      width: 275px;
      background-color: white;
      -moz-border-radius: 5px;
      border-radius: 5px;
      border-style: solid;
      border-color: black;
      border-width: 1px;
      z-index: 10;
      right: 0%;
    }

    #BasemapToggle {
      //position: fixed;
      //right: 350px;
      //top: 10px;
      position: relative;
      float: right;
      margin: 10px;
      margin-right: 70px;
      margin-bottom: -90px;
      width: 80px;
      height: 80px;
      z-index: 1;
    }

    #search {
      display: block;
      position: relative;
      z-index: 2;
      margin-top: 20px;
      margin-bottom: -54px;
      left: 50%;
      alignment-adjust: 50%;
      margin-left: -140px;
    }

    #menuHeader {
      padding: 10px;
      font-size: 18px;
      font-weight: bold;
      background-color: lightgray;
      border-bottom: solid 1px black;
    }

    #menuContent {
      padding: 10px;
      border-bottom: solid 1px black;
    }

    .nav {
      padding: 5px 10px;
      background: #4479BA;
      color: #FFF;
      border-radius: 5px;
      border: solid 1px #20538D;
      text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);
      -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
      -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
    }

    .accordion-toggle {
      cursor: pointer;
      margin: 0;
    }

    .accordion-content {
      display: none;
    }

    .accordion-content.default {
      display: block;
    }
  </style>

  <script type="application/javascript">
    var map;

    require([
    "esri/InfoTemplate",
    "esri/map",
    "esri/dijit/Popup",
    "esri/tasks/query",
    "esri/tasks/QueryTask",
    "esri/Color",
    "esri/layers/ArcGISDynamicMapServiceLayer",
    "esri/symbols/SimpleFillSymbol",
    "esri/symbols/SimpleLineSymbol",
    "esri/symbols/SimpleMarkerSymbol",
    "esri/tasks/IdentifyTask",
    "esri/tasks/IdentifyParameters",
    "dojo/_base/array",
    "dojo/_base/lang",
    "dojo/on",
    "dojo/dom-construct",
    "dojo/parser",
    "dojo/domReady!"
    ],
      function (
        InfoTemplate,
        Map,
        Popup,
        Query,
        QueryTask,
        Color,
        ArcGISDynamicMapServiceLayer,
        SimpleFillSymbol,
        SimpleLineSymbol,
        SimpleMarkerSymbol,
        IdentifyTask,
        IdentifyParameters,
        arrayUtils,
        lang,
        on,
        domConstruct,
        parser
      ) {

        var identifyCadastralTask, identifyCadastralParams, queryAPTask, queryAP;

        var popup = new Popup({
          fillSymbol: new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID,
            new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,
              new Color([255, 0, 0]), 2), new Color([255, 255, 0, 0.25])),
        }, domConstruct.create("div"));

        var mapObj;
        mapObj = new Map("mapDiv", {
          basemap: "topo",
          center: [-95.249, 38.954],
          zoom: 14,
          infoWindow: popup,
          autoResize: true
        });

        var symbol = new SimpleMarkerSymbol({
          "color": [0, 0, 0, 1],
          "size": 0,
          "angle": -30,
          "xoffset": 0,
          "yoffset": 0,
          "type": "esriSMS",
          "style": "esriSMSCircle",
        });


        var layerCadastral = new ArcGISDynamicMapServiceLayer("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer");

        mapObj.addLayers([layerCadastral]);
        identifyTaskSetup();

        function identifyTaskSetup() {
          //create identify tasks and setup parameters
          identifyCadastralTask = new IdentifyTask("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer");
          queryAPTask = new QueryTask("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/0");
          queryAP = new Query();
          queryAP.returnGeometry = true;
          queryAP.outFields = ["*"];

          identifyCadastralParams = new IdentifyParameters();
          identifyCadastralParams.tolerance = 3;
          identifyCadastralParams.returnGeometry = true;
          identifyCadastralParams.layerOption = IdentifyParameters.LAYER_OPTION_VISIBLE;
          identifyCadastralParams.width = mapObj.width;
          identifyCadastralParams.height = mapObj.height;
        };

        mapObj.on("click", executeIdentifyTask);

        function executeIdentifyTask(event) {
          identifyCadastralParams.geometry = event.mapPoint;
          identifyCadastralParams.mapExtent = mapObj.extent;
          identifyCadastralParams.layerIds = layerCadastral.visibleLayers;

          var cadastralDeferred = identifyCadastralTask
            .execute(identifyCadastralParams)
            .addCallback(lang.hitch(this, function (response) {
              // response is an array of identify result objects
              // Let's return an array of features.
              return arrayUtils.map(response, lang.hitch(this, function (result) {
                var feature = result.feature;
                var layerName = result.layerName;
                var generalTemplate = new InfoTemplate(layerName);
                feature.attributes.layerName = layerName;
                if (layerName === 'Census Block Points') {
                  queryAP.where = "TRACT = '" + feature.attributes.TRACT + "'";
                  var queryDeferred = queryAPTask
                    .execute(queryAP)
                    .addCallback(lang.hitch(this, function (qresponse) {
                      return arrayUtils.map(qresponse.features, function (qresult) {
                        qresult.setInfoTemplate(generalTemplate);
                        return qresult;
                      });
                    }));
                  mapObj.infoWindow.clearFeatures();
                  mapObj.infoWindow.set("markerSymbol", symbol);
                  mapObj.infoWindow.setFeatures([queryDeferred]);
                  mapObj.infoWindow.show(event.mapPoint);
                }
                return feature;
              }));
            }));
        };

      });
  </script>

</head>

<body>

  <div id="container">
    <div id="leftPanel">
      <div id="mapDiv">
      </div>
    </div>
  </div>

</body>

</html>

View solution in original post

17 Replies
RobertScheitlin__GISP
MVP Emeritus

Laura,

   Do you still plan to use an IdentifyTask as the means of clicking on a feature in the map and then get that route id from those results and have the maps popup show all the routes photos based on that route id?

LauraMiles1
Occasional Contributor III

Hi Robert, yes I do, if that makes the most sense. I'm not sure how to access the selected photo's attributes though.

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Laura,

  This me writing freehand code (untested). But this is what I came up with:

var queryTask = new QueryTask("your photos service url");
var query = new Query();
query.returnGeometry = true;
query.outFields = ["*"];
identifyAssetsTask.execute(identifyAssetsParams, lang.hitch(this, function(response){
  arrayUtils.map(response, function (result) {  
    var feature = result.feature;
    var layerName = result.layerName;
    if (layerName === 'Primary Direction' || layerName === 'Opposite Direction'){
      query.where = "ROUTE = '" + feature.attributes.route + "'";
      var queryDeferred = queryTask.execute(query, lang.hitch(this, function(qresponse){
        return arrayUtils.map(qresponse, function (qresult) {
          return qresult;
        }
      });
    }
  });
  mapObj.infoWindow.clearFeatures();  
  mapObj.infoWindow.setFeatures([queryDeferred]);  
  mapObj.infoWindow.show(event.mapPoint);
});
LauraMiles1
Occasional Contributor III

Hi Robert,

Thanks so much for your guidance. I'm trying to get this working but I'm getting an error on lang.hitch. Looking at the API reference, I changed it to esriLang.hitch but still getting an error ("TypeError: esriLang.hitch" is not a function) - hitch isn't mentioned in the "lang" section of the reference, what does it do?

esri/lang | API Reference | ArcGIS API for JavaScript

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Laura,

   Sorry lang is in the dojo _base library. You need to add the require for "dojo/_base/lang" to your code.

LauraMiles1
Occasional Contributor III

Thanks Robert, that fixed it. I've not quite gotten it working, I think something might be going wrong with the query but I'm not sure what or why. I have a jsfiddle: Edit fiddle - JSFiddle

I don't have queryDeferred in mapObj.infoWindow.setFeatures because when I add it, the infoWindow doesn't pop up at all.

0 Kudos
LauraMiles1
Occasional Contributor III

Wanted to add - if you comment out the "if" section, the identify still runs and shows data for the points in the infoWindow. As you can see it's saying "No information available".

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Laura,

  Here is a working sample:

<!DOCTYPE html>
<html>

<head>
  <title>Maps on Request</title>
  <script src="http://js.arcgis.com/3.15/"></script>
  <link rel="stylesheet" href="http://js.arcgis.com/3.15/dijit/themes/claro/claro.css">
  <link rel="stylesheet" href="http://js.arcgis.com/3.15/esri/css/esri.css">
  <style>
    html,
    body {
      height: 100%;
    }

    #mapDiv {
      width: 100%;
      height: 100%;
      z-index: 0;
      position: absolute;
    }

    #container {
      overflow: hidden;
      height: 100%;
    }

    #Coords {
      display: block;
      position: absolute;
      z-index: 2;
      bottom: 25px;
      left: 50%;
      alignment-adjust: 50%;
      margin-left: -140px;
      background-color: lightgray;
      -moz-border-radius: 15px;
      border-radius: 15px;
      padding: 5px;
      padding-left: 15px;
      padding-right: 15px;
      opacity: .8;
      text-align: center;
      cursor: pointer;
    }

    #testdiv {
      float: right;
      width: 50px;
    }

    #btnMenuToggle {
      width: 50px;
      height: 50px;
      margin-left: -50px;
      margin-bottom: -90px;
      position: relative;
      z-index: 1;
    }

    #TOC {
      margin: 10px;
      padding: 10px;
      font-family: verdana, helvetica, arial;
      z-index: 10;
      -moz-border-radius: 5px;
      border-radius: 5px;
      border-style: solid;
      border-color: black;
      border-width: 1px;
    }

    #leftPanel {
      height: 100%;
      overflow: hidden;
      width: 275px;
      z-index: 10;
    }

    #rightPanel {
      font-family: verdana, helvetica, arial;
      height: 100%;
      top: 0px;
      position: absolute;
      float: right;
      width: 275px;
      background-color: white;
      -moz-border-radius: 5px;
      border-radius: 5px;
      border-style: solid;
      border-color: black;
      border-width: 1px;
      z-index: 10;
      right: 0%;
    }

    #BasemapToggle {
      //position: fixed;
      //right: 350px;
      //top: 10px;
      position: relative;
      float: right;
      margin: 10px;
      margin-right: 70px;
      margin-bottom: -90px;
      width: 80px;
      height: 80px;
      z-index: 1;
    }

    #search {
      display: block;
      position: relative;
      z-index: 2;
      margin-top: 20px;
      margin-bottom: -54px;
      left: 50%;
      alignment-adjust: 50%;
      margin-left: -140px;
    }

    #menuHeader {
      padding: 10px;
      font-size: 18px;
      font-weight: bold;
      background-color: lightgray;
      border-bottom: solid 1px black;
    }

    #menuContent {
      padding: 10px;
      border-bottom: solid 1px black;
    }

    .nav {
      padding: 5px 10px;
      background: #4479BA;
      color: #FFF;
      border-radius: 5px;
      border: solid 1px #20538D;
      text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);
      -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
      -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
    }

    .accordion-toggle {
      cursor: pointer;
      margin: 0;
    }

    .accordion-content {
      display: none;
    }

    .accordion-content.default {
      display: block;
    }
  </style>

  <script type="application/javascript">
    var map;

    require([
    "esri/InfoTemplate",
    "esri/map",
    "esri/dijit/Popup",
    "esri/tasks/query",
    "esri/tasks/QueryTask",
    "esri/Color",
    "esri/layers/ArcGISDynamicMapServiceLayer",
    "esri/symbols/SimpleFillSymbol",
    "esri/symbols/SimpleLineSymbol",
    "esri/symbols/SimpleMarkerSymbol",
    "esri/tasks/IdentifyTask",
    "esri/tasks/IdentifyParameters",
    "dojo/_base/array",
    "dojo/_base/lang",
    "dojo/on",
    "dojo/dom-construct",
    "dojo/parser",
    "dojo/domReady!"
    ],
      function (
        InfoTemplate,
        Map,
        Popup,
        Query,
        QueryTask,
        Color,
        ArcGISDynamicMapServiceLayer,
        SimpleFillSymbol,
        SimpleLineSymbol,
        SimpleMarkerSymbol,
        IdentifyTask,
        IdentifyParameters,
        arrayUtils,
        lang,
        on,
        domConstruct,
        parser
      ) {

        var identifyCadastralTask, identifyCadastralParams, queryAPTask, queryAP;

        var popup = new Popup({
          fillSymbol: new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID,
            new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,
              new Color([255, 0, 0]), 2), new Color([255, 255, 0, 0.25])),
        }, domConstruct.create("div"));

        var mapObj;
        mapObj = new Map("mapDiv", {
          basemap: "topo",
          center: [-95.249, 38.954],
          zoom: 14,
          infoWindow: popup,
          autoResize: true
        });

        var symbol = new SimpleMarkerSymbol({
          "color": [0, 0, 0, 1],
          "size": 0,
          "angle": -30,
          "xoffset": 0,
          "yoffset": 0,
          "type": "esriSMS",
          "style": "esriSMSCircle",
        });


        var layerCadastral = new ArcGISDynamicMapServiceLayer("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer");

        mapObj.addLayers([layerCadastral]);
        identifyTaskSetup();

        function identifyTaskSetup() {
          //create identify tasks and setup parameters
          identifyCadastralTask = new IdentifyTask("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer");
          queryAPTask = new QueryTask("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/0");
          queryAP = new Query();
          queryAP.returnGeometry = true;
          queryAP.outFields = ["*"];

          identifyCadastralParams = new IdentifyParameters();
          identifyCadastralParams.tolerance = 3;
          identifyCadastralParams.returnGeometry = true;
          identifyCadastralParams.layerOption = IdentifyParameters.LAYER_OPTION_VISIBLE;
          identifyCadastralParams.width = mapObj.width;
          identifyCadastralParams.height = mapObj.height;
        };

        mapObj.on("click", executeIdentifyTask);

        function executeIdentifyTask(event) {
          identifyCadastralParams.geometry = event.mapPoint;
          identifyCadastralParams.mapExtent = mapObj.extent;
          identifyCadastralParams.layerIds = layerCadastral.visibleLayers;

          var cadastralDeferred = identifyCadastralTask
            .execute(identifyCadastralParams)
            .addCallback(lang.hitch(this, function (response) {
              // response is an array of identify result objects
              // Let's return an array of features.
              return arrayUtils.map(response, lang.hitch(this, function (result) {
                var feature = result.feature;
                var layerName = result.layerName;
                var generalTemplate = new InfoTemplate(layerName);
                feature.attributes.layerName = layerName;
                if (layerName === 'Census Block Points') {
                  queryAP.where = "TRACT = '" + feature.attributes.TRACT + "'";
                  var queryDeferred = queryAPTask
                    .execute(queryAP)
                    .addCallback(lang.hitch(this, function (qresponse) {
                      return arrayUtils.map(qresponse.features, function (qresult) {
                        qresult.setInfoTemplate(generalTemplate);
                        return qresult;
                      });
                    }));
                  mapObj.infoWindow.clearFeatures();
                  mapObj.infoWindow.set("markerSymbol", symbol);
                  mapObj.infoWindow.setFeatures([queryDeferred]);
                  mapObj.infoWindow.show(event.mapPoint);
                }
                return feature;
              }));
            }));
        };

      });
  </script>

</head>

<body>

  <div id="container">
    <div id="leftPanel">
      <div id="mapDiv">
      </div>
    </div>
  </div>

</body>

</html>
LauraMiles1
Occasional Contributor III

Hi Robert, this has been a huge help. I have it working with my data. I have one final question, because I would like for the first feature in the array (first feature that shows up in the infoWindow) to be the feature the user clicked on. Currently it starts with the first item in the array, which geographically could be elsewhere on the map. I don't have a great grasp of callback functions or deferred lists. I know I would have to reorder the array depending on which feature is the "selected feature", which is fine enough if I know how to access the array elements. As far as I can tell queryDeferred is an array of arrays? If I log it to the console it tells me there are two results, one empty and one containing the 1000 items from the query. Do you know how I can access these 1000 items to reorder them?

0 Kudos