WSDOT-GIS / arcgis-js-layer-list

Layer list UI control for ArcGIS API for JavaScript
The Unlicense
2 stars 1 forks source link

Add legends for Feature Server layers #5

Open JeffJacobson opened 9 years ago

JeffJacobson commented 9 years ago

Will need to read the layer's renderer and generate graphics (SVG or Canvas) to use in the legend.

See the following for more info

btfou commented 9 years ago

@JeffJacobson Saw your link to this in the TOC discussion on GeoNet. Here's a little utility class I use for making simple legends for various widgets. Creating surface nodes from symbols is in _createVectorLegend method. Probably should have another method just to create and return surface node.

define([
  'put-selector/put',
  'dojo/_base/array',
  'dojo/_base/lang',
  'dojo/has',
  'dojox/gfx',
  'esri/request'
], function (
  put,
  array,
  lang,
  has,
  gfx,
  esriRequest
) {
  'use strict';
  return {
    // makes a request for legend json and returns the esri/request deferred
    // note: does not work for FeatureLayer - use layer's renderer to create svg(s) on client
    // note: does not support AGS < 10.1
    // @param layer {Object} api layer
    getLegendJson: function (layer) {
      return esriRequest({
        url: layer.url + '/legend',
        callbackParamName: 'callback',
        content: {
          f: 'json',
          token: (typeof layer._getToken === 'function') ? layer._getToken() : null
        }
      });
    },

    // creates a legend and returns as a div dom node
    // @param layer {Object} api layer
    // @param className {String} optional class name for div
    // @param layerIds {Array} array of layerIds to include in legend otherwise includes all
    createLegend: function (layer, className, layerIds) {
      var div = put((className) ? 'div.' + className : 'div');
      layerIds = layerIds || false;
      if (layer.graphics) {
        var symbol = layer.renderer.symbol,
          infos = layer.renderer.infos;
        if (symbol) {
          this._createVectorLegend([{
            symbol: symbol,
            description: '',
            label: '',
            value: ''
          }], layer, div);
        } else if (infos) {
          this._createVectorLegend(infos, layer, div);
        } else {
          put(div, 'span.legend-error', 'No legend');
        }
      } else {
        this._createJsonLegend(layer, div, layerIds);
      }
      return div;
    },

    ///////////////////////////////////////////////////
    // make legend json request and handle result/error
    ///////////////////////////////////////////////////
    _createJsonLegend: function (layer, div, layerIds) {
      this.getLegendJson(layer).then(
        lang.hitch(this, '_createJsonLegendResult', div, layer, layerIds),
        lang.hitch(this, '_createJsonLegendError', div)
      );
    },
    _createJsonLegendResult: function (div, layer, layerIds, result) {
      var table = put(div, 'table.legend-table');
      array.forEach(result.layers, function (legendLayer) {
        if (layerIds && array.indexOf(layerIds, legendLayer.layerId) === -1) {
          return;
        }

        if (legendLayer.legend.length === 1) {
          var tr = put(table, 'tr'),
            imgtd = put(tr, 'td.legend-image');
          put(imgtd, this._createImage(legendLayer.legend[0], legendLayer.layerId, layer));
          put(tr, 'td.legend-label', legendLayer.layerName);
        } else {
          put(table, 'tr td.legend-heading[colspan=2]', legendLayer.layerName);
          array.forEach(legendLayer.legend, function (legend) {
            var tr = put(table, 'tr'),
              imgtd = put(tr, 'td.legend-image');
            put(imgtd, this._createImage(legend, legendLayer.layerId, layer));
            put(tr, 'td.legend-label', legend.label);
          }, this);
        }
      }, this);
    },
    _createJsonLegendError: function (div /*, error*/ ) {
      put(div, 'span.legend-error', 'Legend error');
    },
    // create a image from json and return img node
    _createImage: function (legend, layerId, layer) {
      var img, src = legend.url;
      if ((!has('ie') || has('ie') >= 9) && legend.imageData && legend.imageData.length > 0) {
        src = 'data:image/png;base64,' + legend.imageData;
      } else if (legend.url.indexOf('http') !== 0) {
        src = layer.url + '/' + layerId + '/images/' + legend.url;
        var token = layer._getToken();
        if (token) {
          src += '?token=' + token;
        }
      }
      img = put('img.' + layer.id + '-' + layerId + '-legend-image', {
        src: src,
        width: legend.width,
        height: legend.height
      });
      img.style.opacity = layer.opacity;
      layer.on('opacity-change', function (opacity) {
        img.style.opacity = opacity;
      });
      return img;
    },

    /////////////////////////////////////////////
    // create legend from graphics layer renderer
    /////////////////////////////////////////////
    _defaultSurfaceDims: [20, 20],
    _createVectorLegend: function (infos, layer, div) {
      var table = put(div, 'table.legend-table');
      array.forEach(infos, function (info) {
        var sym = info.symbol,
          descriptor = sym.getShapeDescriptors(),
          ds = descriptor.defaultShape,
          fill = descriptor.fill,
          stroke = descriptor.stroke,
          tr = put(table, 'tr'),
          symtd = put(tr, 'td.legend-image');
        put(tr, 'td.legend-label', info.label || '');
        if (!ds.src) {
          var w = this._defaultSurfaceDims[0],
            h = this._defaultSurfaceDims[1];
          if (sym.width && sym.height) {
            w = sym.width;
            h = sym.height;
          }
          var surfaceNode = put(symtd, 'span.' + layer.id + '-legend-image[style=width' + w + 'px;height:' + h + 'px;display:inline-block;]');
          var surface = gfx.createSurface(surfaceNode, w, h);
          var shape = surface.createShape(ds);
          if (fill) {
            shape.setFill(fill);
          }
          if (stroke) {
            shape.setStroke(stroke);
          }
          shape.applyTransform({
            dx: w / 2,
            dy: h / 2
          });
        } else {
          put(symtd, 'img.' + layer.id + '-legend-image', {
            src: ds.src,
            width: sym.width,
            height: sym.height
          });
        }
        // set opacity of td
        //  it works but is there a better way?
        symtd.style.opacity = layer.opacity;
        layer.on('opacity-change', function (opacity) {
          symtd.style.opacity = opacity;
        });
      }, this);
    }
  };
});
JeffJacobson commented 9 years ago

Thanks, Ben!