@progress/kendo-charts
Version:
Kendo UI platform-independent Charts library
500 lines (396 loc) • 13.6 kB
JavaScript
import {
geometry as g,
throttle
} from '@progress/kendo-drawing';
import {
Class,
deepExtend,
round,
limitValue,
hashKey,
setDefaultOptions,
isFunction
} from '../../common';
import { removeChildren } from '../utils';
import { Layer } from './layer';
import TemplateService from '../../services/template-service';
var math = Math,
Point = g.Point;
function compileTemplate(template) {
if (isFunction(template)) {
return template;
}
return TemplateService.compile(template);
}
function roundPoint(point) {
return new Point(round(point.x), round(point.y));
}
function renderSize(size) {
var newSize = size;
if (typeof(size) !== "string") {
newSize += "px";
}
return newSize;
}
export var TileLayer = (function (Layer) {
function TileLayer(map, options) {
Layer.call(this, map, options);
if (typeof this.options.subdomains === 'string') {
this.options.subdomains = this.options.subdomains.split('');
}
var viewType = this._viewType();
this._view = new viewType(this.element, this.options);
}
if ( Layer ) TileLayer.__proto__ = Layer;
TileLayer.prototype = Object.create( Layer && Layer.prototype );
TileLayer.prototype.constructor = TileLayer;
TileLayer.prototype.destroy = function destroy () {
Layer.prototype.destroy.call(this);
this._view.destroy();
this._view = null;
};
TileLayer.prototype._beforeReset = function _beforeReset () {
var map = this.map;
var origin = map.locationToLayer(map.extent().nw).round();
this._view.viewOrigin(origin);
};
TileLayer.prototype._reset = function _reset () {
Layer.prototype._reset.call(this);
this._updateView();
this._view.reset();
};
TileLayer.prototype._viewType = function _viewType () {
return TileView;
};
TileLayer.prototype._activate = function _activate () {
Layer.prototype._activate.call(this);
if (!this.support.mobileOS) {
if (!this._pan) {
this._pan = throttle(this._render.bind(this), 100);
}
this.map.bind('pan', this._pan);
}
};
TileLayer.prototype._deactivate = function _deactivate () {
Layer.prototype._deactivate.call(this);
if (this._pan) {
this.map.unbind('pan', this._pan);
}
};
TileLayer.prototype._updateView = function _updateView () {
var view = this._view,
map = this.map,
extent = map.extent(),
extentToPoint = {
nw: map.locationToLayer(extent.nw).round(),
se: map.locationToLayer(extent.se).round()
};
view.center(map.locationToLayer(map.center()));
view.extent(extentToPoint);
view.zoom(map.zoom());
};
TileLayer.prototype._resize = function _resize () {
this._render();
};
TileLayer.prototype._panEnd = function _panEnd (e) {
Layer.prototype._panEnd.call(this, e);
this._render();
};
TileLayer.prototype._render = function _render () {
this._updateView();
this._view.render();
};
return TileLayer;
}(Layer));
setDefaultOptions(TileLayer, {
tileSize: 256,
subdomains: ['a', 'b', 'c'],
urlTemplate: '',
zIndex: 1
});
export var TileView = (function (Class) {
function TileView(element, options) {
Class.call(this);
this.element = element;
this._initOptions(options);
this.pool = new TilePool();
}
if ( Class ) TileView.__proto__ = Class;
TileView.prototype = Object.create( Class && Class.prototype );
TileView.prototype.constructor = TileView;
TileView.prototype._initOptions = function _initOptions (options) {
this.options = deepExtend({}, this.options, options);
};
TileView.prototype.center = function center (center$1) {
this._center = center$1;
};
TileView.prototype.extent = function extent (extent$1) {
this._extent = extent$1;
};
TileView.prototype.viewOrigin = function viewOrigin (origin) {
this._viewOrigin = origin;
};
TileView.prototype.zoom = function zoom (zoom$1) {
this._zoom = zoom$1;
};
TileView.prototype.pointToTileIndex = function pointToTileIndex (point) {
return new Point(math.floor(point.x / this.options.tileSize), math.floor(point.y / this.options.tileSize));
};
TileView.prototype.tileCount = function tileCount () {
var size = this.size(),
firstTileIndex = this.pointToTileIndex(this._extent.nw),
nw = this._extent.nw,
point = this.indexToPoint(firstTileIndex).translate(-nw.x, -nw.y);
return {
x: math.ceil((math.abs(point.x) + size.width) / this.options.tileSize),
y: math.ceil((math.abs(point.y) + size.height) / this.options.tileSize)
};
};
TileView.prototype.size = function size () {
var nw = this._extent.nw,
se = this._extent.se,
diff = se.clone().translate(-nw.x, -nw.y);
return {
width: diff.x,
height: diff.y
};
};
TileView.prototype.indexToPoint = function indexToPoint (index) {
var x = index.x,
y = index.y;
return new Point(x * this.options.tileSize, y * this.options.tileSize);
};
TileView.prototype.subdomainText = function subdomainText () {
var subdomains = this.options.subdomains;
return subdomains[this.subdomainIndex++ % subdomains.length];
};
TileView.prototype.destroy = function destroy () {
removeChildren(this.element);
this.pool.empty();
};
TileView.prototype.reset = function reset () {
this.pool.reset();
this.subdomainIndex = 0;
this.render();
};
TileView.prototype.render = function render () {
var this$1 = this;
var size = this.tileCount(),
firstTileIndex = this.pointToTileIndex(this._extent.nw),
tile, x, y;
for (x = 0; x < size.x; x++) {
for (y = 0; y < size.y; y++) {
tile = this$1.createTile({
x: firstTileIndex.x + x,
y: firstTileIndex.y + y
});
if (!tile.visible) {
tile.show();
}
}
}
};
TileView.prototype.createTile = function createTile (currentIndex) {
var options = this.tileOptions(currentIndex);
var tile = this.pool.get(this._center, options);
if (!tile.element.parentNode) {
this.element.append(tile.element);
}
return tile;
};
TileView.prototype.tileOptions = function tileOptions (currentIndex) {
var index = this.wrapIndex(currentIndex),
point = this.indexToPoint(currentIndex),
origin = this._viewOrigin,
offset = point.clone().translate(-origin.x, -origin.y);
return {
index: index,
currentIndex: currentIndex,
point: point,
offset: roundPoint(offset),
zoom: this._zoom,
size: this.options.tileSize,
subdomain: this.subdomainText(),
urlTemplate: this.options.urlTemplate,
errorUrlTemplate: this.options.errorUrlTemplate
};
};
TileView.prototype.wrapIndex = function wrapIndex (index) {
var boundary = math.pow(2, this._zoom);
return {
x: this.wrapValue(index.x, boundary),
y: limitValue(index.y, 0, boundary - 1)
};
};
TileView.prototype.wrapValue = function wrapValue (value, boundary) {
var remainder = math.abs(value) % boundary;
var wrappedValue = value;
if (value >= 0) {
wrappedValue = remainder;
} else {
wrappedValue = boundary - (remainder === 0 ? boundary : remainder);
}
return wrappedValue;
};
return TileView;
}(Class));
export var ImageTile = (function (Class) {
function ImageTile(id, options) {
Class.call(this);
this.id = id;
this.visible = true;
this._initOptions(options);
this.createElement();
this.show();
}
if ( Class ) ImageTile.__proto__ = Class;
ImageTile.prototype = Object.create( Class && Class.prototype );
ImageTile.prototype.constructor = ImageTile;
ImageTile.prototype.destroy = function destroy () {
var element = this.element;
var parentNode = element ? element.parentNode : null;
if (element) {
if (parentNode) {
parentNode.removeChild(element);
}
this.element = null;
}
};
ImageTile.prototype._initOptions = function _initOptions (options) {
this.options = deepExtend({}, this.options, options);
};
ImageTile.prototype.createElement = function createElement () {
var el = document.createElement("img");
var size = this.options.size + "px";
el.setAttribute("alt", "");
el.style.position = "absolute";
el.style.display = "block";
el.style.width = el.style.maxWidth = size;
el.style.height = el.style.maxHeight = size;
this.element = el;
// todo
// add on error handler
// this.element =
// $('<img style=\'position: absolute; display: block;\' alt=\'\' />')
// .css({
// width: this.options.size,
// height: this.options.size
// })
// .on('error', proxy(function(e) {
// if (this.errorUrl()) {
// e.target.setAttribute('src', this.errorUrl());
// } else {
// e.target.removeAttribute('src');
// }
// }, this));
};
ImageTile.prototype.show = function show () {
var element = this.element;
element.style.top = renderSize(this.options.offset.y);
element.style.left = renderSize(this.options.offset.x);
var url = this.url();
if (url) {
element.setAttribute('src', url);
}
element.style.visibility = 'visible';
this.visible = true;
};
ImageTile.prototype.hide = function hide () {
this.element.style.visibility = 'hidden';
this.visible = false;
};
ImageTile.prototype.url = function url () {
var urlResult = compileTemplate(this.options.urlTemplate);
return urlResult(this.urlOptions());
};
ImageTile.prototype.errorUrl = function errorUrl () {
var urlResult = compileTemplate(this.options.errorUrlTemplate);
return urlResult(this.urlOptions());
};
ImageTile.prototype.urlOptions = function urlOptions () {
var options = this.options;
return {
zoom: options.zoom,
subdomain: options.subdomain,
z: options.zoom,
x: options.index.x,
y: options.index.y,
s: options.subdomain,
quadkey: options.quadkey,
q: options.quadkey,
culture: options.culture,
c: options.culture
};
};
return ImageTile;
}(Class));
setDefaultOptions(ImageTile, {
urlTemplate: '',
errorUrlTemplate: ''
});
export var TilePool = (function (Class) {
function TilePool() {
Class.call(this);
this._items = [];
}
if ( Class ) TilePool.__proto__ = Class;
TilePool.prototype = Object.create( Class && Class.prototype );
TilePool.prototype.constructor = TilePool;
TilePool.prototype.get = function get (center, options) {
if (this._items.length >= this.options.maxSize) {
this._remove(center);
}
return this._create(options);
};
TilePool.prototype.empty = function empty () {
var items = this._items;
for (var i = 0; i < items.length; i++) {
items[i].destroy();
}
this._items = [];
};
TilePool.prototype.reset = function reset () {
var items = this._items;
for (var i = 0; i < items.length; i++) {
items[i].hide();
}
};
TilePool.prototype._create = function _create (options) {
var items = this._items;
var tile;
var id = hashKey(options.point.toString() + options.offset.toString() + options.zoom + options.urlTemplate);
for (var i = 0; i < items.length; i++) {
if (items[i].id === id) {
tile = items[i];
break;
}
}
if (tile) {
tile.show();
} else {
tile = new ImageTile(id, options);
this._items.push(tile);
}
return tile;
};
TilePool.prototype._remove = function _remove (center) {
var items = this._items;
var maxDist = -1;
var index = -1;
for (var i = 0; i < items.length; i++) {
var dist = items[i].options.point.distanceTo(center);
if (dist > maxDist && !items[i].visible) {
index = i;
maxDist = dist;
}
}
if (index !== -1) {
items[index].destroy();
items.splice(index, 1);
}
};
return TilePool;
}(Class));
setDefaultOptions(TilePool, {
maxSize: 100
});