pixelmapLayer.js

var inherit = require('./inherit');
var tileLayer = require('./tileLayer');
var registry = require('./registry');
var quadFeature = require('./quadFeature');
var pixelmapFeature = require('./pixelmapFeature');
var util = require('./util');

/**
 * Object specification for a pixelmap layer.
 *
 * @typedef {geo.tileLayer.spec} geo.pixelmapLayer.spec
 * @extends {geo.tileLayer.spec}
 * @property {geo.geoColor|function} [color] The color that should be used
 *   for each data element.  Data elements correspond to the indices in the
 *   pixel map. If an index is larger than the number of data elements, it will
 *   be transparent.  If there is more data than there are indices, it is
 *   ignored.
 * @property {object} [style] An optional style object that could contain
 *   `color` or other style values.
 * @property {array} [data] A new data array.
 * @property {string} [crossDomain='anonymous'] Image CORS attribute.  This is
 *   used for the `crossorigin` property when loading images.
 */

/**
 * Create a new instance of pixelmapLayer.  This is a {@link geo.tileLayer} with
 * an OSM url and attribution defaults and with the tiles centered on the
 * origin.
 *
 * @class
 * @alias geo.pixelmapLayer
 * @extends geo.tileLayer
 *
 * @param {geo.pixelmapLayer.spec} [arg] Specification for the layer.
 */
var pixelmapLayer = function (arg) {

  var imageTile = require('./imageTile');

  if (!(this instanceof pixelmapLayer)) {
    return new pixelmapLayer(arg);
  }
  arg = arg || {};
  /* Don't extend data from args -- it can be very slow */
  let argdata;
  if (arg.data) {
    argdata = arg.data;
    delete arg.data;
  }
  arg = util.deepMerge(
    {},
    this.constructor.defaults,
    arg);
  tileLayer.call(this, arg);

  var s_init = this._init,
      m_pixelmapFeature,
      m_this = this;

  /**
   * Returns an instantiated imageTile object with the given indices.  This
   * method always returns a new tile object.  Use `_getTileCached` to use
   * the caching layer.
   *
   * @param {object} index The tile index.
   * @param {number} index.x
   * @param {number} index.y
   * @param {number} index.level
   * @param {object} source The tile index used for constructing the url.
   * @param {number} source.x
   * @param {number} source.y
   * @param {number} source.level
   * @returns {geo.tile}
   */
  this._getTile = function (index, source) {
    var urlParams = source || index;
    return imageTile({
      index: index,
      size: {x: m_this._options.tileWidth, y: m_this._options.tileHeight},
      queue: m_this._queue,
      overlap: m_this._options.tileOverlap,
      scale: m_this._options.tileScale,
      url: m_this._options.url.call(
        m_this, urlParams.x, urlParams.y, urlParams.level || 0,
        m_this._options.subdomains),
      crossDomain: m_this._options.crossDomain
    });
  };

  /**
   * Initialize.
   *
   * @returns {this} The current layer.
   */
  this._init = function () {
    // Call super class init
    s_init.apply(m_this, arguments);
    m_this.nearestPixel(true, true);

    const pixelmapArgs = {quadFeature: m_this.features()[0]};
    if (arg.style) {
      pixelmapArgs.style = arg.style;
    }
    if (arg.color) {
      pixelmapArgs.color = arg.color;
    }
    m_pixelmapFeature = m_this.createFeature('pixelmap', pixelmapArgs);
    if (argdata) {
      m_pixelmapFeature.data(argdata);
    }
    m_this.style = m_pixelmapFeature.style;
    m_this.data = m_pixelmapFeature.data;
    m_this.indexModified = m_pixelmapFeature.indexModified;
    const s_dataTimeModified = m_this.dataTime().modified;
    m_this.dataTime().modified = () => {
      m_pixelmapFeature.dataTime().modified();
      return s_dataTimeModified();
    };
    ['modified', 'geoOn', 'geoOff', 'geoOnce'].forEach((funcName) => {
      const superFunc = m_this[funcName];
      m_this[funcName] = function () {
        if (!Array.isArray(arguments[0])) {
          m_pixelmapFeature[funcName].apply(this, arguments);
        }
        return superFunc.apply(this, arguments);
      };
    });
    return m_this;
  };

  return m_this;
};

/**
 * This object contains the default options used to initialize the
 * pixelmapLayer.
 */
pixelmapLayer.defaults = Object.assign({}, tileLayer.defaults, {
  features: [quadFeature.capabilities.image, pixelmapFeature.capabilities.lookup],
  tileOffset : function (level) {
    var s = Math.pow(2, level - 1) * 256;
    return {x: s, y: s};
  },
  url: ''
});

inherit(pixelmapLayer, tileLayer);
/* By default, ask to support image quads.  If the user needs full
 * reprojection, they will need to require the
 * quadFeature.capabilities.imageFull feature */
registry.registerLayer('pixelmap', pixelmapLayer, [quadFeature.capabilities.image]);

module.exports = pixelmapLayer;