Heatmap printing with ArcGIS Javascript API Export_Web_Map task

5111
4
Jump to solution
12-10-2014 01:45 PM
XuejinRuan
New Contributor III

I have a point feature layer published as a FeatureService to ArcGIS Server. On the client side I use "esri/renderers/HeatmapRenderer" to render it as a heat map. And then I try to use ArcGIS Server default printing tool to print the heatmap, but I get an error message as below:

"Error executing tool.: Layer "graphicsLayer2": Unsupported 'type' in renderer: heatmap.
Failed to execute (Export Web Map).
Failed to execute (Export Web Map Task)."

I also try to create the heatmap using Patrick Weid's heatmap.js (heatmap.js | Dynamic Heatmaps for the Web). And again, I can't print it with ArcGIS Server's printing tool.

I am wondering whether there is anyone out there who has successfully printed out a heatmap.

Thanks,

Xuejin

0 Kudos
1 Solution

Accepted Solutions
NiklasKöhn
Esri Contributor

Hi Xuejin,
In case you're still looking for a solution - I just had the same problem and combined the Heatmap and Print sampels from the JS API Resource Center, and it works. Here's the code:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
    <title></title>
    <link rel="stylesheet" href="http://js.arcgis.com/3.13/dijit/themes/nihilo/nihilo.css">
    <link rel="stylesheet" href="http://js.arcgis.com/3.13/esri/css/esri.css">
    <style>
      html, body { 
        height: 100%; width: 100%;
        margin: 0; padding: 0;
      } 
      body{
        background-color: #fff; overflow:hidden; 
        font-family: sans-serif;
      } 
      label {
        display: inline-block;
        padding: 5px 5px 0 5px;
        font-weight: 400;
        font-size: 12pt;
      }
      .button {
        width: 100%;
        margin: 3px auto;
        text-align: center;
      }
      #header {
        padding-top: 4px;
        padding-right: 15px;
        color: #444; 
        font-size:16pt; text-align:right;font-weight:bold;
        height:55px;
        background: #fff;
        border-bottom: 1px solid #444;
      }
      #subheader {
        font-size:small;
        color: #444;
        text-align:right;
        padding-right:20px;
      }
      #rightPane{
        margin: 0;
        padding: 10px;
        background-color: #fff;
        color: #421b14;
        width: 180px;
      }

      .ds { background: #000; overflow: hidden; position: absolute; z-index: 2; }
      #ds-h div { width: 100%; }
      #ds-l div, #ds-r div { height: 100%; }
      #ds-r div { right: 0; }
      #ds .o1 { filter: alpha(opacity=10); opacity: .1; }
      #ds .o2 { filter: alpha(opacity=8); opacity: .08; }
      #ds .o3 { filter: alpha(opacity=6); opacity: .06; }
      #ds .o4 { filter: alpha(opacity=4); opacity: .04; }
      #ds .o5 { filter: alpha(opacity=2); opacity: .02; }
      #ds .h1 { height: 1px; }
      #ds .h2 { height: 2px; }
      #ds .h3 { height: 3px; }
      #ds .h4 { height: 4px; }
      #ds .h5 { height: 5px; }
      #ds .v1 { width: 1px; }
      #ds .v2 { width: 2px; }
      #ds .v3 { width: 3px; }
      #ds .v4 { width: 4px; }
      #ds .v5 { width: 5px; }

      /* make all dijit buttons the same width */
      .dijitButton .dijitButtonNode, #drawingWrapper, #printButton {
        width: 160px;
      }
      .esriPrint {
        padding: 0;
      }
    </style>

    <script src="http://js.arcgis.com/3.13/"></script>
    <script>
      var app = {};
      app.map = null; app.toolbar = null; app.tool = null; app.symbols = null; app.printer = null;
      require([
        "esri/map", "esri/toolbars/draw", "esri/dijit/Print",
        "esri/layers/ArcGISTiledMapServiceLayer", "esri/layers/ArcGISDynamicMapServiceLayer",
        "esri/layers/LayerDrawingOptions",
        "esri/symbols/SimpleMarkerSymbol", "esri/symbols/SimpleLineSymbol", 
        "esri/symbols/SimpleFillSymbol", "esri/graphic",
        "esri/renderers/ClassBreaksRenderer",
        "esri/config",
        "dojo/_base/array", "esri/Color", "dojo/parser", 
        "dojo/query", "dojo/dom", "dojo/dom-construct", 
        "dijit/form/CheckBox", "dijit/form/Button",
        "esri/InfoTemplate",
        "esri/layers/FeatureLayer",
        "esri/map",
        "esri/renderers/HeatmapRenderer",
        "dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dojo/domReady!"
      ], function(
        Map, Draw, Print,
        ArcGISTiledMapServiceLayer, ArcGISDynamicMapServiceLayer,
        LayerDrawingOptions,
        SimpleMarkerSymbol, SimpleLineSymbol,
        SimpleFillSymbol, Graphic,
        ClassBreaksRenderer,
        esriConfig,
        arrayUtils, Color, parser, 
        query, dom, domConstruct, 
        CheckBox, Button, InfoTemplate, FeatureLayer, Map, HeatmapRenderer
      ) {

        // --------------------------------------------------------------------
        // Formatting functions for attribute values in the InfoWindow
        // Data is not what you expect, turn your dirty data into
        // readable stuff in the infowindow
        // --------------------------------------------------------------------
        formatFatalities = function (value, key, data){
          var result = value > 1 ? value + " people " : value + " person ";
          return result;
        }
        formatGender = function (value, key, data){
          var lookup = {1: "male", 2: "female", 8: "unknown", 9: "unknown"};
          return lookup[value];
        }
        formatConditions = function (value, key, data){
          var lookup = {0: "No Additional Atmospheric Conditions", 1: "Clear", 2: "Rain", 3: "Sleet, Hail (Freezing Rain or Drizzle)", 4: "Snow", 5: "Fog, Smog, Smoke", 6: "Severe Crosswinds", 7: "Blowing Sand, Soil, Dirt", 8: "Other", 10: "Cloudy", 11: "Blowing Snow", 98: "Not Reported", 99: "Unknown"};
          if (value !== 1) {
            return "Road conditions: " + lookup[value] + "<br>";
          }
        }
        formatWorkZone = function (value, key, data){
          var lookup = {0: "None", 1: "Construction", 2: "Maintenance", 3: "Utility", 4: "Work Zone, Type Unknown"};
          if (value !== 0) {
            return "Work Zone: " + lookup[value] + "<br>";
          }
        }
        formatAlcoholTestResults = function (value, key, data){
          // --------------------------------------------------------------------
          // The field is a string and we only want values of 8 - 94 since the
          // California legal limit is 0.08% BAC. If value is between 8 and 94
          // then we will report that they were over the legal limit.
          // --------------------------------------------------------------------
          var isMatch = value.match(/\b(?!9[5-9])[0-9][0-9]\b|\b[8-9]{1}\b/m);
          if (isMatch) {
            return "Driver was over the legal limit for alcohol";
          }
        }



        parser.parse();

        esriConfig.defaults.io.proxyUrl = "/proxy/";
        
        app.map = new Map("map", {
          center: [-117.733, 36],
          zoom: 7
        });
        app.map.on("load", function() {
          app.toolbar = new Draw(app.map);
          app.toolbar.on("draw-end", addToMap);
        });

        // print dijit
        app.printer = new Print({
          map: app.map,
          url: "http://sampleserver6.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%2..."
        }, dom.byId("printButton"));
        app.printer.startup();

        var url = "http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer";
        var tiledLayer = new ArcGISTiledMapServiceLayer(url, { "id": "Ocean" });
        app.map.addLayer(tiledLayer);



        var infoContentDesc = "<p>${numfatal:formatFatalities} died when a ${age} year old ${sex:formatGender} was involved in a fatal speeding accident.</p>";
        var infoContentDetails = "${atmcond:formatConditions}${conszone:formatWorkZone}${alcres:formatAlcoholTestResults}";
        var infoContent = infoContentDesc + infoContentDetails;
        var infoTemplate = new InfoTemplate("Accident details", infoContent);

        var serviceURL = "//services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/2012_CA_NHTSA/FeatureServer/0";
        var heatmapFeatureLayerOptions = {
          mode: FeatureLayer.MODE_SNAPSHOT,
          infoTemplate: infoTemplate,
          outFields: [
            "atmcond",
            "numfatal",
            "conszone",
            "age",
            "alcres",
            "sex"
          ]
        };
        var heatmapFeatureLayer = new FeatureLayer(serviceURL, heatmapFeatureLayerOptions);
        var heatmapRenderer = new HeatmapRenderer();
        heatmapFeatureLayer.setRenderer(heatmapRenderer);
        app.map.addLayer(heatmapFeatureLayer);


        // create a check box for each map layer
        arrayUtils.forEach(["County Population", "Ocean"], function(id) {
          new CheckBox({
            id: "cb_" + id,
            name: "cb_" + id,
            checked: true,
            onChange: function(bool) {
              bool ? 
                app.map.getLayer(this.id.split("_")[1]).show() :
                app.map.getLayer(this.id.split("_")[1]).hide();
            }
          }, domConstruct.create("input", { 
            id: "lyr_" + id 
          })).placeAt(dom.byId("layerToggle"));

          // create a label for the check box
          var label = domConstruct.create('label', { 
            "for": "cb_" + id,
            "innerHTML": id
          });
          domConstruct.place(label, dom.byId("layerToggle"));
          domConstruct.place(domConstruct.create("br"), dom.byId("layerToggle"));
        });

        // set up symbols for the various geometry types
        app.symbols = {};
        app.symbols.point = new SimpleMarkerSymbol("square", 10, new SimpleLineSymbol(), new Color([0, 255, 0, 0.75]));
        app.symbols.polyline = new SimpleLineSymbol("solid", new Color([255, 128, 0]), 2);
        app.symbols.polygon = new SimpleFillSymbol().setColor(new Color([255,255,0,0.25]));
        app.symbols.circle = new SimpleFillSymbol().setColor(new Color([0, 0, 180, 0.25]));

        // find the divs for buttons
        query(".drawing").forEach(function(btn) {
          var button = new Button({
            label: btn.innerHTML,
            onClick: function() {
              activateTool(this.id);
            }
          }, btn);
        });

        function activateTool(type) {
          app.tool = type.replace("freehand", "");
          app.toolbar.activate(type);
          app.map.hideZoomSlider();
        }

        function addToMap(evt) {
          app.toolbar.deactivate();
          app.map.showZoomSlider();
          
          var graphic = new Graphic(evt.geometry, app.symbols[app.tool]);
          app.map.graphics.add(graphic);
        }
      });
    </script>
  </head>
  <body class="nihilo">
    <div id="mainWindow" 
         data-dojo-type="dijit/layout/BorderContainer" 
         data-dojo-props="design:'headline',gutters:false"
         style="width: 100%; height: 100%; margin: 0;">
      <div id="header" 
           data-dojo-type="dijit/layout/ContentPane"
           data-dojo-props="region:'top'">
        Print Dijit:  Out of the Box Printing for the ArcGIS API for JavaScript
        <div id="subheader">Requires ArcGIS Server 10.1</div>
      </div>
      <div id="map" class="shadow" 
           data-dojo-type="dijit/layout/ContentPane"
           data-dojo-props="region:'center'">
            
      </div>
      <div id="rightPane"
           data-dojo-type="dijit/layout/ContentPane"
           data-dojo-props="region:'right'">

        <div id="printButton"></div>
        <hr />
        
        <div id="drawingWrapper">
          Add some graphics:
          <div id="point" class="drawing">Point</div>
          <div id="freehandpolyline" class="drawing">Freehand Polyline</div>
          <div id="freehandpolygon" class="drawing">Freehand Polygon</div>
          <div id="circle" class="drawing">Circle</div>
        </div>
        <hr />
        <div id="layerToggle">
          Toggle Layers: <br />
          <!-- checkbox and labels inserted programmatically -->
        </div>
      </div>
    </div>
  </body>
</html>

View solution in original post

4 Replies
NiklasKöhn
Esri Contributor

Hi Xuejin,
In case you're still looking for a solution - I just had the same problem and combined the Heatmap and Print sampels from the JS API Resource Center, and it works. Here's the code:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
    <title></title>
    <link rel="stylesheet" href="http://js.arcgis.com/3.13/dijit/themes/nihilo/nihilo.css">
    <link rel="stylesheet" href="http://js.arcgis.com/3.13/esri/css/esri.css">
    <style>
      html, body { 
        height: 100%; width: 100%;
        margin: 0; padding: 0;
      } 
      body{
        background-color: #fff; overflow:hidden; 
        font-family: sans-serif;
      } 
      label {
        display: inline-block;
        padding: 5px 5px 0 5px;
        font-weight: 400;
        font-size: 12pt;
      }
      .button {
        width: 100%;
        margin: 3px auto;
        text-align: center;
      }
      #header {
        padding-top: 4px;
        padding-right: 15px;
        color: #444; 
        font-size:16pt; text-align:right;font-weight:bold;
        height:55px;
        background: #fff;
        border-bottom: 1px solid #444;
      }
      #subheader {
        font-size:small;
        color: #444;
        text-align:right;
        padding-right:20px;
      }
      #rightPane{
        margin: 0;
        padding: 10px;
        background-color: #fff;
        color: #421b14;
        width: 180px;
      }

      .ds { background: #000; overflow: hidden; position: absolute; z-index: 2; }
      #ds-h div { width: 100%; }
      #ds-l div, #ds-r div { height: 100%; }
      #ds-r div { right: 0; }
      #ds .o1 { filter: alpha(opacity=10); opacity: .1; }
      #ds .o2 { filter: alpha(opacity=8); opacity: .08; }
      #ds .o3 { filter: alpha(opacity=6); opacity: .06; }
      #ds .o4 { filter: alpha(opacity=4); opacity: .04; }
      #ds .o5 { filter: alpha(opacity=2); opacity: .02; }
      #ds .h1 { height: 1px; }
      #ds .h2 { height: 2px; }
      #ds .h3 { height: 3px; }
      #ds .h4 { height: 4px; }
      #ds .h5 { height: 5px; }
      #ds .v1 { width: 1px; }
      #ds .v2 { width: 2px; }
      #ds .v3 { width: 3px; }
      #ds .v4 { width: 4px; }
      #ds .v5 { width: 5px; }

      /* make all dijit buttons the same width */
      .dijitButton .dijitButtonNode, #drawingWrapper, #printButton {
        width: 160px;
      }
      .esriPrint {
        padding: 0;
      }
    </style>

    <script src="http://js.arcgis.com/3.13/"></script>
    <script>
      var app = {};
      app.map = null; app.toolbar = null; app.tool = null; app.symbols = null; app.printer = null;
      require([
        "esri/map", "esri/toolbars/draw", "esri/dijit/Print",
        "esri/layers/ArcGISTiledMapServiceLayer", "esri/layers/ArcGISDynamicMapServiceLayer",
        "esri/layers/LayerDrawingOptions",
        "esri/symbols/SimpleMarkerSymbol", "esri/symbols/SimpleLineSymbol", 
        "esri/symbols/SimpleFillSymbol", "esri/graphic",
        "esri/renderers/ClassBreaksRenderer",
        "esri/config",
        "dojo/_base/array", "esri/Color", "dojo/parser", 
        "dojo/query", "dojo/dom", "dojo/dom-construct", 
        "dijit/form/CheckBox", "dijit/form/Button",
        "esri/InfoTemplate",
        "esri/layers/FeatureLayer",
        "esri/map",
        "esri/renderers/HeatmapRenderer",
        "dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dojo/domReady!"
      ], function(
        Map, Draw, Print,
        ArcGISTiledMapServiceLayer, ArcGISDynamicMapServiceLayer,
        LayerDrawingOptions,
        SimpleMarkerSymbol, SimpleLineSymbol,
        SimpleFillSymbol, Graphic,
        ClassBreaksRenderer,
        esriConfig,
        arrayUtils, Color, parser, 
        query, dom, domConstruct, 
        CheckBox, Button, InfoTemplate, FeatureLayer, Map, HeatmapRenderer
      ) {

        // --------------------------------------------------------------------
        // Formatting functions for attribute values in the InfoWindow
        // Data is not what you expect, turn your dirty data into
        // readable stuff in the infowindow
        // --------------------------------------------------------------------
        formatFatalities = function (value, key, data){
          var result = value > 1 ? value + " people " : value + " person ";
          return result;
        }
        formatGender = function (value, key, data){
          var lookup = {1: "male", 2: "female", 8: "unknown", 9: "unknown"};
          return lookup[value];
        }
        formatConditions = function (value, key, data){
          var lookup = {0: "No Additional Atmospheric Conditions", 1: "Clear", 2: "Rain", 3: "Sleet, Hail (Freezing Rain or Drizzle)", 4: "Snow", 5: "Fog, Smog, Smoke", 6: "Severe Crosswinds", 7: "Blowing Sand, Soil, Dirt", 8: "Other", 10: "Cloudy", 11: "Blowing Snow", 98: "Not Reported", 99: "Unknown"};
          if (value !== 1) {
            return "Road conditions: " + lookup[value] + "<br>";
          }
        }
        formatWorkZone = function (value, key, data){
          var lookup = {0: "None", 1: "Construction", 2: "Maintenance", 3: "Utility", 4: "Work Zone, Type Unknown"};
          if (value !== 0) {
            return "Work Zone: " + lookup[value] + "<br>";
          }
        }
        formatAlcoholTestResults = function (value, key, data){
          // --------------------------------------------------------------------
          // The field is a string and we only want values of 8 - 94 since the
          // California legal limit is 0.08% BAC. If value is between 8 and 94
          // then we will report that they were over the legal limit.
          // --------------------------------------------------------------------
          var isMatch = value.match(/\b(?!9[5-9])[0-9][0-9]\b|\b[8-9]{1}\b/m);
          if (isMatch) {
            return "Driver was over the legal limit for alcohol";
          }
        }



        parser.parse();

        esriConfig.defaults.io.proxyUrl = "/proxy/";
        
        app.map = new Map("map", {
          center: [-117.733, 36],
          zoom: 7
        });
        app.map.on("load", function() {
          app.toolbar = new Draw(app.map);
          app.toolbar.on("draw-end", addToMap);
        });

        // print dijit
        app.printer = new Print({
          map: app.map,
          url: "http://sampleserver6.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%2..."
        }, dom.byId("printButton"));
        app.printer.startup();

        var url = "http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer";
        var tiledLayer = new ArcGISTiledMapServiceLayer(url, { "id": "Ocean" });
        app.map.addLayer(tiledLayer);



        var infoContentDesc = "<p>${numfatal:formatFatalities} died when a ${age} year old ${sex:formatGender} was involved in a fatal speeding accident.</p>";
        var infoContentDetails = "${atmcond:formatConditions}${conszone:formatWorkZone}${alcres:formatAlcoholTestResults}";
        var infoContent = infoContentDesc + infoContentDetails;
        var infoTemplate = new InfoTemplate("Accident details", infoContent);

        var serviceURL = "//services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/2012_CA_NHTSA/FeatureServer/0";
        var heatmapFeatureLayerOptions = {
          mode: FeatureLayer.MODE_SNAPSHOT,
          infoTemplate: infoTemplate,
          outFields: [
            "atmcond",
            "numfatal",
            "conszone",
            "age",
            "alcres",
            "sex"
          ]
        };
        var heatmapFeatureLayer = new FeatureLayer(serviceURL, heatmapFeatureLayerOptions);
        var heatmapRenderer = new HeatmapRenderer();
        heatmapFeatureLayer.setRenderer(heatmapRenderer);
        app.map.addLayer(heatmapFeatureLayer);


        // create a check box for each map layer
        arrayUtils.forEach(["County Population", "Ocean"], function(id) {
          new CheckBox({
            id: "cb_" + id,
            name: "cb_" + id,
            checked: true,
            onChange: function(bool) {
              bool ? 
                app.map.getLayer(this.id.split("_")[1]).show() :
                app.map.getLayer(this.id.split("_")[1]).hide();
            }
          }, domConstruct.create("input", { 
            id: "lyr_" + id 
          })).placeAt(dom.byId("layerToggle"));

          // create a label for the check box
          var label = domConstruct.create('label', { 
            "for": "cb_" + id,
            "innerHTML": id
          });
          domConstruct.place(label, dom.byId("layerToggle"));
          domConstruct.place(domConstruct.create("br"), dom.byId("layerToggle"));
        });

        // set up symbols for the various geometry types
        app.symbols = {};
        app.symbols.point = new SimpleMarkerSymbol("square", 10, new SimpleLineSymbol(), new Color([0, 255, 0, 0.75]));
        app.symbols.polyline = new SimpleLineSymbol("solid", new Color([255, 128, 0]), 2);
        app.symbols.polygon = new SimpleFillSymbol().setColor(new Color([255,255,0,0.25]));
        app.symbols.circle = new SimpleFillSymbol().setColor(new Color([0, 0, 180, 0.25]));

        // find the divs for buttons
        query(".drawing").forEach(function(btn) {
          var button = new Button({
            label: btn.innerHTML,
            onClick: function() {
              activateTool(this.id);
            }
          }, btn);
        });

        function activateTool(type) {
          app.tool = type.replace("freehand", "");
          app.toolbar.activate(type);
          app.map.hideZoomSlider();
        }

        function addToMap(evt) {
          app.toolbar.deactivate();
          app.map.showZoomSlider();
          
          var graphic = new Graphic(evt.geometry, app.symbols[app.tool]);
          app.map.graphics.add(graphic);
        }
      });
    </script>
  </head>
  <body class="nihilo">
    <div id="mainWindow" 
         data-dojo-type="dijit/layout/BorderContainer" 
         data-dojo-props="design:'headline',gutters:false"
         style="width: 100%; height: 100%; margin: 0;">
      <div id="header" 
           data-dojo-type="dijit/layout/ContentPane"
           data-dojo-props="region:'top'">
        Print Dijit:  Out of the Box Printing for the ArcGIS API for JavaScript
        <div id="subheader">Requires ArcGIS Server 10.1</div>
      </div>
      <div id="map" class="shadow" 
           data-dojo-type="dijit/layout/ContentPane"
           data-dojo-props="region:'center'">
            
      </div>
      <div id="rightPane"
           data-dojo-type="dijit/layout/ContentPane"
           data-dojo-props="region:'right'">

        <div id="printButton"></div>
        <hr />
        
        <div id="drawingWrapper">
          Add some graphics:
          <div id="point" class="drawing">Point</div>
          <div id="freehandpolyline" class="drawing">Freehand Polyline</div>
          <div id="freehandpolygon" class="drawing">Freehand Polygon</div>
          <div id="circle" class="drawing">Circle</div>
        </div>
        <hr />
        <div id="layerToggle">
          Toggle Layers: <br />
          <!-- checkbox and labels inserted programmatically -->
        </div>
      </div>
    </div>
  </body>
</html>
XuejinRuan
New Contributor III

Thanks so much Niklas! Can't believe I didn't see your reply until today.

Yes you are right. It works! I think the newer version of javascript API supports HeatmapRenderer.

Thanks again!

Sincerely,

Xuejin

NiklasKöhn
Esri Contributor

Glad I could help!

GokuSan
New Contributor II

Can anyone help me?

I have created a heat map using heatmapRendrerer using the next sample:

Samples | ArcGIS API for JavaScript

The renderer apply it to a featureLayer that in turn get a MapService . My question is:

Is there way to apply this renderer instead of using a FeatureLayer use a ArcGISDynamicMapServiceLayer ?

Thanks.

0 Kudos