Change the default highlight symbol and zoom level for URL parameter query results

3042
12
Jump to solution
09-23-2016 03:08 AM
Pin_PointAlerts
New Contributor

Hi,

I am looking to modify the default style symbols for a query result, where the query is generated using URL parameters, eg;

http://localhost/application/?query=LayerNameJSONCode_1234,ATTNAME,ATTVALUE

Right now the basic cyan outline (no fill) exists and I cannot find a way to edit this. Also, the zoom level is 100% surrounding the polygon with no buffer area displayed for local context. We are looking to change the display parameters and zoom levels to that we can link to the correct feature in our application from a URL query via an external application. The major problem for us is that the Cyan is difficult to interpret against neighbouring features.

I've tried tracing the setting back through the MXD, AGOL Map and the Web App Builder (V2). No joy.

Thanks a lot.

BC

Tags (2)
0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Emeritus

BC,

   The map url query code is all contained in the jimu/MapUrlParamsHandler.js file. In the selectFeatures function you will find the fact that this features uses the selection symbol of the layer in the map and you will also find the code where the map extent is set for the querys results.

Here is a thread where I answer how to change the selection symbol:

https://community.esri.com/thread/163700 

View solution in original post

12 Replies
RobertScheitlin__GISP
MVP Emeritus

BC,

   The map url query code is all contained in the jimu/MapUrlParamsHandler.js file. In the selectFeatures function you will find the fact that this features uses the selection symbol of the layer in the map and you will also find the code where the map extent is set for the querys results.

Here is a thread where I answer how to change the selection symbol:

https://community.esri.com/thread/163700 

Pin_PointAlerts
New Contributor

Hi Rob,

Thanks for that - getting the white screen of death once I try plug that code into the JS.

Hoping I'm close and that you can see the issue in half a second.

Cheers.

///////////////////////////////////////////////////////////////////////////
// Copyright © 2014 Esri. All Rights Reserved.
//
// Licensed under the Apache License Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///////////////////////////////////////////////////////////////////////////

define(['dojo/_base/declare',
  'dojo/_base/lang',
  'dojo/_base/array',
  'dojo/_base/html',
  'dojo/topic',
  'dojo/on',
  'dojo/aspect',
  'dojo/keys',
  'esri/dijit/InfoWindow',
  'esri/dijit/PopupMobile',
  'esri/InfoTemplate',
  'esri/request',
  'esri/geometry/Extent',
  'esri/geometry/Point',
  'require',
  './utils',
  './dijit/LoadingShelter',
  'jimu/LayerInfos/LayerInfos',
  'jimu/dijit/AppStatePopup',
  './MapUrlParamsHandler',
  './AppStateManager',
  './PopupManager',  
  'esri/symbols/SimpleFillSymbol'
  ], function(declare, lang, array, html, topic, on, aspect, keys, InfoWindow,
  PopupMobile, InfoTemplate, esriRequest, Extent, Point, require,
  jimuUtils, LoadingShelter, SimpleFillSymbol, LayerInfos, AppStatePopup, MapUrlParamsHandler,
  AppStateManager, PopupManager) {
  var instance = null,
    clazz = declare(null, {
      appConfig: null,
      mapDivId: '',
      map: null,
      previousInfoWindow: null,
      mobileInfoWindow: null,
      isMobileInfoWindow: false,

      layerInfosObj: null,

      constructor: function( /*Object*/ options, mapDivId) {
        this.appConfig = options.appConfig;
        this.urlParams = options.urlParams;
        this.mapDivId = mapDivId;
        this.id = mapDivId;
        this.appStateManager = AppStateManager.getInstance(this.urlParams);
        this.popupManager = PopupManager.getInstance(this);
        this.nls = window.jimuNls;
        topic.subscribe("appConfigChanged", lang.hitch(this, this.onAppConfigChanged));
        topic.subscribe("changeMapPosition", lang.hitch(this, this.onChangeMapPosition));
        topic.subscribe("syncExtent", lang.hitch(this, this.onSyncExtent));

        on(window, 'resize', lang.hitch(this, this.onWindowResize));
        on(window, 'beforeunload', lang.hitch(this, this.onBeforeUnload));
      },

      showMap: function() {
        // console.timeEnd('before map');
        this._showMap(this.appConfig);
      },

      _showMap: function(appConfig) {
        // console.timeEnd('before map');
        console.time('Load Map');
        this.loading = new LoadingShelter();
        this.loading.placeAt(this.mapDivId);
        this.loading.startup();
        //for now, we can't create both 2d and 3d map
        if (appConfig.map['3D']) {
          if (appConfig.map.itemId) {
            this._show3DWebScene(appConfig);
          } else {
            this._show3DLayersMap(appConfig);
          }
        } else {
          if (appConfig.map.itemId) {
            this._show2DWebMap(appConfig);
          } else {
            console.log('No webmap found. Please set map.itemId in config.json.');
          }
        }
      },

      onBeforeUnload: function() {
        if(this.appConfig.keepAppState) {
          this.appStateManager.saveWabAppState(this.map, this.layerInfosObj);
        }
      },

      onWindowResize: function() {
        if (this.map && this.map.resize) {
          this.map.resize();
          this.resetInfoWindow(false);
        }
      },

      getMapInfoWindow: function(){
        return {
          mobile: this._mapMobileInfoWindow,
          bigScreen: this._mapInfoWindow
        };
      },

      resetInfoWindow: function(isNewMap) {
        if(isNewMap){
          this._mapInfoWindow = this.map.infoWindow;
          if(this._mapMobileInfoWindow){
            this._mapMobileInfoWindow.destroy();
          }
          this._mapMobileInfoWindow =
          new PopupMobile(null, html.create("div", null, null, this.map.root));
          this.isMobileInfoWindow = false;
        }
        if (window.appInfo.isRunInMobile && !this.isMobileInfoWindow) {
          this.map.infoWindow.hide();
          this.map.setInfoWindow(this._mapMobileInfoWindow);
          this.isMobileInfoWindow = true;
        } else if (!window.appInfo.isRunInMobile && this.isMobileInfoWindow) {
          this.map.infoWindow.hide();
          this.map.setInfoWindow(this._mapInfoWindow);
          this.isMobileInfoWindow = false;
        }
      },

      onChangeMapPosition: function(position) {
        var pos = lang.clone(this.mapPosition);
        lang.mixin(pos, position);
        this.setMapPosition(pos);
      },

      setMapPosition: function(position){
        this.mapPosition = position;

        var posStyle = jimuUtils.getPositionStyle(position);
        html.setStyle(this.mapDivId, posStyle);
        if (this.map && this.map.resize) {
          this.map.resize();
        }
      },

      getMapPosition: function(){
        return this.mapPosition;
      },

      onSyncExtent: function(map){
        if(this.map){
          var extJson = map.extent;
          var ext = new Extent(extJson);
          this.map.setExtent(ext);
        }
      },

      _visitConfigMapLayers: function(appConfig, cb) {
        array.forEach(appConfig.map.basemaps, function(layerConfig, i) {
          layerConfig.isOperationalLayer = false;
          cb(layerConfig, i);
        }, this);

        array.forEach(appConfig.map.operationallayers, function(layerConfig, i) {
          layerConfig.isOperationalLayer = true;
          cb(layerConfig, i);
        }, this);
      },

      _show3DLayersMap: function(appConfig) {
        require(['esri3d/Map'], lang.hitch(this, function(Map) {
          var initCamera = appConfig.map.mapOptions.camera,
            map;
          map = new Map(this.mapDivId, {
            camera: initCamera
          });
          this._visitConfigMapLayers(appConfig, lang.hitch(this, function(layerConfig) {
            this.createLayer(map, '3D', layerConfig);
          }));
          map.usePlugin = Map.usePlugin;
          this._publishMapEvent(map);
        }));
      },

      _show3DWebScene: function(appConfig) {
        this._getWebsceneData(appConfig.map.itemId).then(lang.hitch(this, function(data) {
          require(['esri3d/Map'], lang.hitch(this, function(Map) {
            var map = new Map(this.mapDivId, appConfig.map.mapOptions);

            array.forEach(data.itemData.operationalLayers, function(layerConfig) {
              this.createLayer(map, '3D', layerConfig);
            }, this);

            array.forEach(data.itemData.baseMap.baseMapLayers, function(layerConfig) {
              layerConfig.type = "tile";
              this.createLayer(map, '3D', layerConfig);
            }, this);

            array.forEach(data.itemData.baseMap.elevationLayers, function(layerConfig) {
              layerConfig.type = "elevation";
              this.createLayer(map, '3D', layerConfig);
            }, this);

            map.toc = data.itemData.toc;
            map.bookmarks = data.itemData.bookmarks;
            map.tours = data.itemData.tours;
          }));
        }));
      },

      _publishMapEvent: function(map) {
        //add this property for debug purpose
        window._viewerMap = map;

        MapUrlParamsHandler.postProcessUrlParams(this.urlParams, map);

        console.timeEnd('Load Map');
        if (this.map) {
          this.map = map;
          this.resetInfoWindow(true);
          console.log('map changed.');
          topic.publish('mapChanged', this.map);
        } else {
          this.map = map;
          this.resetInfoWindow(true);
          topic.publish('mapLoaded', this.map);
        }
      },

      _getWebsceneData: function(itemId) {
        return esriRequest({
          url: 'http://127.0.0.1/sharing/rest/content/items/' + itemId + '/data',
          handleAs: "json"
        });
      },

      _show2DWebMap: function(appConfig) {
        //should use appConfig instead of this.appConfig, because appConfig is new.
        // if (appConfig.portalUrl) {
        //   var url = portalUrlUtils.getStandardPortalUrl(appConfig.portalUrl);
        //   agolUtils.arcgisUrl = url + "/sharing/content/items/";
        // }
        if(!appConfig.map.mapOptions){
          appConfig.map.mapOptions = {};
        }
        var mapOptions = this._processMapOptions(appConfig.map.mapOptions) || {};
        mapOptions.isZoomSlider = false;

        var webMapPortalUrl = appConfig.map.portalUrl;
        var webMapItemId = appConfig.map.itemId;
        var webMapOptions = {
          mapOptions: mapOptions,
          bingMapsKey: appConfig.bingMapsKey,
          usePopupManager: true
        };

        var mapDeferred = jimuUtils.createWebMap(webMapPortalUrl, webMapItemId,
          this.mapDivId, webMapOptions);

        mapDeferred.then(lang.hitch(this, function(response) {
          var map = response.map;

          //hide the default zoom slider
          map.hideZoomSlider();

          // set default size of infoWindow.
          map.infoWindow.resize(270, 316);
            
                        map.infoWindow.fillSymbol = new SimpleFillSymbol({  
  "type": "esriSFS",  
  "style": "esriSFSSolid",  
  "color": [255,0,0,25],  
  "outline": {  
     "type": "esriSLS",  
     "style": "esriSLSSolid",  
     "color": [255,0,0,255],  
     "width": 2  
     }  
  });  
  
          //var extent;
          map.itemId = appConfig.map.itemId;
          map.itemInfo = response.itemInfo;
          map.webMapResponse = response;
          // enable snapping
          var options = {
            snapKey: keys.copyKey
          };
          map.enableSnapping(options);

          html.setStyle(map.root, 'zIndex', 0);

          map._initialExtent = map.extent;
          this._publishMapEvent(map);
          setTimeout(lang.hitch(this, this._checkAppState), 500);

          this.loading.hide();

          this._addDataLoadingOnMapUpdate(map);
        }), lang.hitch(this, function() {
          this._destroyLoadingShelter();
          topic.publish('mapCreatedFailed');
        }));
      },

      _addDataLoadingOnMapUpdate: function(map) {
        var loadHtml = '<div class="load-container">' +
        '<div class="loader">Loading...</div>' +
        '</div>';
        var loadContainer = html.toDom(loadHtml);
        html.place(loadContainer, map.root);
        on(map, 'update-start', lang.hitch(this, function() {
          html.setStyle(loadContainer, 'display', '');
        }));
        on(map, 'update-end', lang.hitch(this, function() {
          html.setStyle(loadContainer, 'display', 'none');
        }));
        on(map, 'unload', lang.hitch(this, function() {
          html.destroy(loadContainer);
          loadContainer = null;
          this._destroyLoadingShelter();
        }));
      },

      _destroyLoadingShelter: function() {
        if (this.loading) {
          this.loading.destroy();
          this.loading = null;
        }
      },

      _checkAppState: function() {
        //URL parameters that affect map extent
        var urlKeys = ['extent', 'center', 'marker', 'find', 'query', 'scale', 'level'];
        var useAppState = this.appConfig.keepAppState;

        if(useAppState) {
          array.forEach(urlKeys, function(k){
            if(k in this.urlParams){
              useAppState = false;
            }
          }, this);
        }

        if(useAppState){
          this.appStateManager.getWabAppState().then(lang.hitch(this, function(stateData) {
            LayerInfos.getInstance(this.map, this.map.itemInfo)
            .then(lang.hitch(this, function(layerInfosObj) {
              this.layerInfosObj = layerInfosObj;
              if(stateData.extent || stateData.layers) {
                var appStatePopup = new AppStatePopup({
                  nls: {
                    title: this.nls.appState.title,
                    restoreMap: this.nls.appState.restoreMap
                  }
                });
                appStatePopup.placeAt('main-page');
                on(appStatePopup, 'applyAppState', lang.hitch(this, function(){
                  this._applyAppState(stateData, this.map);
                }));
                appStatePopup.startup();
                appStatePopup.show();
              }
            }));
          }));
        }
      },

      _applyAppState: function(stateData, map) {
        var layerOptions = stateData.layers;
        this.layerInfosObj.restoreState({
          layerOptions: layerOptions || null
        });
        if (stateData.extent) {
          map.setExtent(stateData.extent);
        }
        this._publishMapEvent(map);
      },

      _processMapOptions: function(mapOptions) {
        if (!mapOptions) {
          return;
        }

        if(!mapOptions.lods){
          delete mapOptions.lods;
        }
        if(mapOptions.lods && mapOptions.lods.length === 0){
          delete mapOptions.lods;
        }

        var ret = lang.clone(mapOptions);
        if (ret.extent) {
          ret.extent = new Extent(ret.extent);
        }
        if (ret.center && !lang.isArrayLike(ret.center)) {
          ret.center = new Point(ret.center);
        }
        if (ret.infoWindow) {
          ret.infoWindow = new InfoWindow(ret.infoWindow, html.create('div', {}, this.mapDivId));
        }

        return ret;
      },

      createLayer: function(map, maptype, layerConfig) {
        var layMap = {
          '2D_tiled': 'esri/layers/ArcGISTiledMapServiceLayer',
          '2D_dynamic': 'esri/layers/ArcGISDynamicMapServiceLayer',
          '2D_image': 'esri/layers/ArcGISImageServiceLayer',
          '2D_feature': 'esri/layers/FeatureLayer',
          '2D_rss': 'esri/layers/GeoRSSLayer',
          '2D_kml': 'esri/layers/KMLLayer',
          '2D_webTiled': 'esri/layers/WebTiledLayer',
          '2D_wms': 'esri/layers/WMSLayer',
          '2D_wmts': 'esri/layers/WMTSLayer',
          '3D_tiled': 'esri3d/layers/ArcGISTiledMapServiceLayer',
          '3D_dynamic': 'esri3d/layers/ArcGISDynamicMapServiceLayer',
          '3D_image': 'esri3d/layers/ArcGISImageServiceLayer',
          '3D_feature': 'esri3d/layers/FeatureLayer',
          '3D_elevation': 'esri3d/layers/ArcGISElevationServiceLayer',
          '3D_3dmodle': 'esri3d/layers/SceneLayer'
        };

        require([layMap[maptype + '_' + layerConfig.type]], lang.hitch(this, function(layerClass) {
          var layer, infoTemplate, options = {},
            keyProperties = ['label', 'url', 'type', 'icon', 'infoTemplate', 'isOperationalLayer'];
          for (var p in layerConfig) {
            if (keyProperties.indexOf(p) < 0) {
              options
 = layerConfig
;
            }
          }
          if (layerConfig.infoTemplate) {
            infoTemplate = new InfoTemplate(layerConfig.infoTemplate.title,
              layerConfig.infoTemplate.content);
            options.infoTemplate = infoTemplate;

            layer = new layerClass(layerConfig.url, options);

            if (layerConfig.infoTemplate.width && layerConfig.infoTemplate.height) {
              aspect.after(layer, 'onClick', lang.hitch(this, function() {
                map.infoWindow.resize(layerConfig.infoTemplate.width,
                  layerConfig.infoTemplate.height);
              }), true);
            }
          } else {
            layer = new layerClass(layerConfig.url, options);
          }

          layer.isOperationalLayer = layerConfig.isOperationalLayer;
          layer.label = layerConfig.label;
          layer.icon = layerConfig.icon;
          map.addLayer(layer);
        }));
      },

      onAppConfigChanged: function(appConfig, reason, changedJson) {
        // jshint unused:false
        this.appConfig = appConfig;
        if(reason === 'mapChange'){
          this._recreateMap(appConfig);
        }
        else if(reason === 'mapOptionsChange'){
          if(changedJson.lods){
            this._recreateMap(appConfig);
          }
        }
      },

      _recreateMap: function(appConfig){
        if(this.map){
          topic.publish('beforeMapDestory', this.map);
          this.map.destroy();
        }
        this._showMap(appConfig);
      },

      disableWebMapPopup: function() {
        this.map.setInfoWindowOnClick(false);
      },

      enableWebMapPopup: function() {
        this.map.setInfoWindowOnClick(true);
      }

    });

  clazz.getInstance = function(options, mapDivId) {
    if (instance === null) {
      instance = new clazz(options, mapDivId);
    }
    return instance;
  };

  return clazz;
});‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
Pin_PointAlerts
New Contributor

Okay, figured it out... had the ordering wrong:

  'jimu/dijit/AppStatePopup',
  './MapUrlParamsHandler',
  './AppStateManager',
  './PopupManager',  
  'esri/symbols/SimpleFillSymbol'
  ], function(declare, lang, array, html, topic, on, aspect, keys, InfoWindow,
  PopupMobile, InfoTemplate, esriRequest, Extent, Point, require,
  jimuUtils, LoadingShelter, LayerInfos, AppStatePopup, MapUrlParamsHandler,
  AppStateManager, PopupManager, SimpleFillSymbol) {

Not seeing the lines of code for the zoom level change - is it in that document?

Thanks a lot.

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

It is in the jimu.js/MapUrlParamsHandler.js selectFeatures function:

    layer.layerObject.selectFeatures(query).then(function(features){
      if(features.length === 0){
        console.log('No result from query URL parameter.');
        return;
      }
      if(layer.layerObject.geometryType === 'esriGeometryPoint' && features.length === 1){
        map.setExtent(scaleUtils.getExtentForScale(map, 1000));//same scale with search dijit
        map.centerAt(features[0].geometry);
      }else{
        var resultExtent = jimuUtils.graphicsExtent(features);
        map.setExtent(resultExtent);
      }
0 Kudos
SuzanneLiebergen1
New Contributor II

I was having a similar issue and it took me awhile to figure out how to adjust the zoom factor/ resultextent, so in case someone else is struggling to do the same, this is what I did:

daveostrander
Occasional Contributor II

Hi Robert,

Thanks for pointing out where to modify the code, but do you have a solution for adjusting the scale in the  jimu.js/MapUrlParamsHandler.js selectFeatures function when the url parameters are querying against a polygon map service? It would be so easy to set this up if it were point geometry like the solution Suzanne suggested, but unfortunately that won't work for polygons.

Thanks

0 Kudos
daveostrander
Occasional Contributor II

Hi Robert,

Looks like I rushed my comment too fast. So I suppose just disregard my previous comment. Instead, what would the code in the else statement of the selectFeatures function look like assuming I would like to zoom to the feature passed in by the query parameter, but only to a specific scale. Is there a way to the resultExtent variable with a combining of specific scale level, but also ensuring the graphicsExtent(features) is also centered? The way it sits unmodified the scale is more or less dependent on the screen size/shape.

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Dave,

  Hmm. How would you know what scale you want?

0 Kudos
daveostrander
Occasional Contributor II

It's a parcel map service, so I was just going to pick a scale that I know would function for our largest acreage parcels. Not a perfect solution, but I am not seeing a better workaround. I pretty much hate the graphicsExtent(features) solution, and I would rather have the geographic context while potentially being zoomed out too far. I was going to go with happy medium of say, 1:2,257 or 1:1,128. Ideally, it would be nice if the size of the parcel being selected could somehow factor into what scale gets selected. Perhaps, a few area related if statements.... 

0 Kudos