Print Task on Custom ArcGISImageServiceLayer

3180
13
Jump to solution
12-09-2013 10:29 AM
RobertWinterbottom
Occasional Contributor
Hi Everyone,

  I am working on executing a print task on a map with a custom ArcGISImageServiceLayer and am hitting a bit of a snag.  It seems that when I execute the print task it will work but it returns back a incorrect image in grey and black and not one including the colors or parameters currently shown in the layer.  I noticed in the json being sent in it does not include any params or a rendering rule which I am assuming might have something to do with this.  Is there anyway to override a method in the layer so when using the print task I can include the rendering rule and any other necessary params into the request?
0 Kudos
13 Replies
JianHuang
Occasional Contributor III
I looked at the source code. PrintTask doesn't work with any custom layer. Because it checks the property layer.declaredClass to decide what to serialize into the json output.
In your case, only when layer.declaredClass === "esri.layers.ArcGISImagerServiceLayer", it will serialize renderingRule.
Is your custom layer written in AMD style or legacy style? You can try to override .declaredClass property as "esri.layers.ArcGISImagerServiceLayer". It shouldn't cause any conflicts.
0 Kudos
RobertWinterbottom
Occasional Contributor
I looked at the source code. PrintTask doesn't work with any custom layer. Because it checks the property layer.declaredClass to decide what to serialize into the json output.
In your case, only when layer.declaredClass === "esri.layers.ArcGISImagerServiceLayer", it will serialize renderingRule.
Is your custom layer written in AMD style or legacy style? You can try to override .declaredClass property as "esri.layers.ArcGISImagerServiceLayer". It shouldn't cause any conflicts.


Thanks Jian, we are using the AMD style, the whole class is listed below if that will be of some help.  I tried overriding the .declaredClass property and it still did not seem like it wanted to pick it up.

Here is our version of the class, it makes use of Knockout to store values from sliders and checkboxes in Model.getVM().suitabilityCustomSettings().computeBinaryRaster as an array and through some helper functions we are able to construct our custom renderingRule.

define(["declare", "esri/layers/RasterFunction", "esri/layers/ImageServiceParameters", "toolsmodel", "dojo/_base/array", "dojo/_base/lang"],
  function (declare, RasterFunction, ImageServiceParameters, Model, arrayUtils, lang) {
    return declare("SuitabilityImageServiceLayer", esri.layers.ArcGISImageServiceLayer, {
      constructor: function () {

        // raster function arguments must be in same order as in image service moasic dataset
        this.rasterFunctionArguments = {};
        this.rasterFunctionArguments.ElevRaster = "$1";
        this.rasterFunctionArguments.SlopeRaster = "$2";
        this.rasterFunctionArguments.WaterRaster = "$3";
        this.rasterFunctionArguments.ConsRaster = "$4";
        this.rasterFunctionArguments.STypeRaster = "$5";
        this.rasterFunctionArguments.SDepthRaster = "$6";
        this.rasterFunctionArguments.PeatRaster = "$7";
        this.rasterFunctionArguments.SAcidRaster = "$8";
        this.rasterFunctionArguments.SDrainRaster = "$9";
        this.rasterFunctionArguments.RainfallRaster = "$10";
        this.rasterFunctionArguments.LCRaster = "$11";

        // scalars are harded-coded based on raster pre-processing scripts
        this.rasterFunctionScalars = {};
        this.rasterFunctionScalars.Elev = 10;
        this.rasterFunctionScalars.Slope = 1;
        this.rasterFunctionScalars.Water = 10;
        this.rasterFunctionScalars.Cons = 100;
        this.rasterFunctionScalars.SType = 1;
        this.rasterFunctionScalars.SDepth = 1;
        this.rasterFunctionScalars.Peat = 1;
        this.rasterFunctionScalars.SAcid = 1;
        this.rasterFunctionScalars.SDrain = 1;
        this.rasterFunctionScalars.Rainfall = 10;
        this.rasterFunctionScalars.LC = 1;

        this.renderingRule = {};
        this.declaredClass = "esri.layers.ArcGISImageServiceLayer";

        var rasterFunction = new RasterFunction();
        rasterFunction.functionName = "RemapColormap2Prashant";
        rasterFunction.arguments = this.rasterFunctionArguments;

        var params = new ImageServiceParameters();
        params.noData = 0;
        params.renderingRule = rasterFunction;
        this.imageServiceParameters = params;

      },

      getExportUrl: function(extent,width,height,callback,options){
          function callback(url){
              return;
          }
      },

      getRenderingRule: function () {
          //console.log("GET RENDER RULE");
          var _self = this;
          var suitabilitySettings = _self.returnRasterSettingsAsObject();
          var render_rule = {};

          lang.mixin(render_rule, this.rasterFunctionArguments);

          _self.addThresholdArgument(suitabilitySettings, render_rule, 'Elev', _self.rasterFunctionScalars, 'MAX');
          _self.addThresholdArgument(suitabilitySettings, render_rule, 'Slope', _self.rasterFunctionScalars, 'MAX');
          _self.addThresholdArgument(suitabilitySettings, render_rule, 'Water', _self.rasterFunctionScalars, 'MIN');
          _self.addThresholdArgument(suitabilitySettings, render_rule, 'Cons', _self.rasterFunctionScalars, 'MIN');
          _self.addThresholdArgument(suitabilitySettings, render_rule, 'Rainfall', _self.rasterFunctionScalars, 'BETWEEN');
          _self.addMembershipArgument(suitabilitySettings, render_rule, 'SDepth', [0, 1, 2, 3, 4, 5, 6, 7]);
          _self.addMembershipArgument(suitabilitySettings, render_rule, 'Peat', [0, 1, 2, 3, 4, 5, 6]);
          _self.addMembershipArgument(suitabilitySettings, render_rule, 'SAcid', [0, 1, 2, 3, 4, 5, 6, 7]);
          _self.addMembershipArgument(suitabilitySettings, render_rule, 'SDrain', [0, 1, 2, 3, 4]);
          _self.addMembershipArgument(suitabilitySettings, render_rule, 'LC', [0, 1, 2, 3, 4, 5, 6, 7, 8]);
          _self.addMembershipArgument(suitabilitySettings, render_rule, 'SType', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

          var rasterFunction = new RasterFunction();
          rasterFunction.rasterFunction = "PalmOilSuitabilityNew";
          rasterFunction.rasterFunctionArguments = render_rule;
          return rasterFunction;
      },

      getImageUrl: function (extent, width, height, callback, options) {
        var _self = this;
        var bbox = [extent.xmin, extent.ymin, extent.xmax, extent.ymax],
          params,
          min,
          max,
          tempArray,
          membership_arguments;

        var rasterFunction = _self.getRenderingRule();
        if (options) {
            params = options;
            params['renderingRule'] = JSON.stringify(rasterFunction);
            params['bbox'] = bbox.join(",");
            _self.renderingRule = rasterFunction;
            callback(_self.url + "/exportImage?",params);
        } else {
            params = {
                noData: 0,
                noDataInterpretation: "esriNoDataMatchAny",
                interpolation: "RSP_BilinearInterpolation",
                renderingRule: JSON.stringify(rasterFunction),
                format: "png8",
                size: width + "," + height,
                imageSR: 3857,
                bboxSR: 3857,
                f: "image",
                pixelType: 'U8',
                bbox: bbox.join(",")

            };

            _self.renderingRule = rasterFunction;
            callback(_self.url + "/exportImage?" + dojo.objectToQuery(params));
        }
        
      },

      returnRasterSettingsAsObject: function () {
        var result = {};
        var settingsArray = Model.getVM().suitabilityCustomSettings().computeBinaryRaster;
        arrayUtils.forEach(settingsArray, function (setting) {
          result[setting.name] = setting.values;
        });
        return result;
      },

      returnNumericArray : function (stringArray) {
          var temp = stringArray.split(",");
          var result = [];
          arrayUtils.forEach(temp, function (value) {
            result.push(parseInt(value, 10));
          });
          return result;
      },

      addThresholdArgument: function (suitabilitySettings, renderRule, variableName, scalars, breakType) {
        var _self = this;
        scalar = scalars[variableName];
        if (breakType === 'BETWEEN') {
            var tempArray = _self.returnNumericArray(suitabilitySettings[variableName + 'InpR']);
            var min = tempArray[0];
            var max = tempArray[tempArray.length - 1];
            renderRule[variableName + 'InpR'] = [0, min / scalar, min / scalar, max / scalar, max / scalar, 1000000];
            renderRule[variableName + 'OutV'] = [1, 1, 0];
        } else {
            var breakpoint = parseInt(suitabilitySettings[variableName + 'InpR'], 10);
            renderRule[variableName + 'InpR'] = [0, breakpoint / scalar, breakpoint / scalar, 1000000];
            renderRule[variableName + 'OutV'] = breakType === 'MIN' ? [0, 1] : [1, 0];
        }
      },

      addMembershipArgument: function (suitabilitySettings, renderRule, variableName, allValues) {
        var _self = this;
        var validValues = _self.returnNumericArray(suitabilitySettings[variableName + 'InpR']);
        var input_values = [];
        var output_values = [];
        var includeNulls = ["SDepth","SAcid","SDrain","LC"];
        var val;
        var i;

        if (includeNulls.indexOf(variableName) > -1 && validValues[0] !== 0)
          validValues.unshift(0);

        for (i = 0; i < allValues.length; i++) {
          val = allValues;
          input_values.push(val, val);
          if (validValues.indexOf(val) === -1) {
            output_values.push(0);
          } else {
            output_values.push(1);
          }
        }
        renderRule[variableName + 'InpR'] = input_values;
        renderRule[variableName + 'OutV'] = output_values;
      }

    });
  });
0 Kudos
JianHuang
Occasional Contributor III
Robert,

Here is a simplest sample which works on my side.

custom layer, which inherits from ArcGISImageServiceLayer but do nothing.
define( [   "dojo/_base/declare",   "esri/layers/ArcGISImageServiceLayer" ], function(   declare, ArcGISImageServiceLayer ) {    var MyImageServiceLayer = declare([ArcGISImageServiceLayer], {     declaredClass: "esri.layers.ArcGISImageServiceLayer"       });   return MyImageServiceLayer;   }); 


Test page:
  var map, layer;   require(["esri/map", "esri/layers/MyImageServiceLayer", "esri/dijit/Print", "dojo/domReady!"],   function (Map, MyImageServiceLayer, Print) {     esri.config.defaults.io.proxyUrl = "/proxy.ashx";//use your own proxy instead of this.     map = new Map("map", {       basemap: "gray",       center: [-80.85, 35.22],       zoom: 14     });      var print = new Print({       map: map,       url: "http://sampleserver6.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task"     }, "print");     print.startup();      layer = new MyImageServiceLayer("http://sampleserver6.arcgisonline.com/arcgis/rest/services/CharlotteLAS/ImageServer");      map.addLayer(layer);     layer.on("load", function () {       layer.renderingRule = {         "rasterFunction": "RFTShadedReliefElevationColorRamp"       };       layer.renderingRule.toJson = function () {         return {           "rasterFunction": "RFTShadedReliefElevationColorRamp"         };       };       layer.setRenderingRule(layer.renderingRule);     });   });
0 Kudos
RobertWinterbottom
Occasional Contributor
Hey Jian, Thanks for the snippet, that really helped, it is now working as it should, I had to just make a minor tweak to my class and everything is now working as expected.  It seems I was missing the "esri/layers/ArcGISImageServiceLayer" from my initial define, adding that in pretty much did the trick.  I also had to play with the renderingRule to JSON function to get it working correctly with our renderingRule but everything is working great now without the need for the esri.setRequestPreCallback. 🙂

Thanks again!!
0 Kudos