let ol = require('openlayers');
/**
* Устанавливает символ диаграммы для векторного объекта
* @constructor
* @param {object} params - параметрмы диаграммы
* @param {String} options.type - тип диаграммы - доступные значения: pie,pie3D, donut, bar (пока не закончен)
* @param {number} options.radius - радиус диаграммы - по умолчанию 20
* @param {number} options.rotation - угол поворота в радианах - 0
* @param {bool} options.snapToPixel - привязка к пикселам - true
* @param {object} - параметры стиля обводки
* @param {number} options.offsetX - отступ по горизонтали (px)
* @param {number} options.offsetY - отступ по вертикали (px)
* @extends {ol.style.RegularShape}
* @implements {ol.structs.IHasChecksum}
* @api
*/
let DiagramSymbol = function (params, feature) {
let options = params || {};
let strokeWidth = 0;
if (params.stroke) strokeWidth = params.stroke.getWidth();
ol.style.RegularShape.call(this,
{
radius: options.radius + strokeWidth,
fill: new ol.style.Fill({color: [0, 0, 0]}),
rotation: options.rotation,
snapToPixel: options.snapToPixel
});
if (options.scale) this.setScale(options.scale);
//this.stroke_ = params.default.stroke ? new ol.style.Stroke(params.default.stroke) : new ol.style.Stroke({color: "#FFF", width: 1});
this.radius_ = options.radius || 20;
this.donutRatio_ = options.donutRatio || 0.5;
this.type_ = options.type;
this.offset_ = [options.offsetX ? options.offsetX : 0, options.offsetY ? options.offsetY : 0];
this.animation_ = (typeof(options.animation) == 'number') ? {animate: true, step: options.animation} : this.animation_ = {animate: false, step: 1};
let properties = feature.getProperties();
let data = [];
let colors = [];
for (let key in options.values) {
let value = properties[key] || 0;
let color = options.values[key].fill.color || options.default.fill.color;
data.push(value);
colors.push(color);
}
this.data_ = data || options.data;
this.colors_ = colors;
this.renderChart_();
};
ol.inherits(DiagramSymbol, ol.style.RegularShape);
/**
* Копия символа (необходима для ускорения создания)
* @return {DiagramSymbol}
*/
DiagramSymbol.prototype.clone = function () {
let symbol = new DiagramSymbol(
{
type: this.type_,
radius: this.radius_,
rotation: this.getRotation(),
scale: this.getScale(),
data: this.getData(),
snapToPixel: this.getSnapToPixel(),
stroke: this.stroke_,
colors: this.colors_,
offsetX: this.offset_[0],
offsetY: this.offset_[1],
animation: this.animation_
});
symbol.setScale(this.getScale());
symbol.setOpacity(this.getOpacity());
return symbol;
};
DiagramSymbol.prototype.getData = function () {
return this.data_;
};
/**
* Принудительное обновление диаграммы
* @param data
*/
DiagramSymbol.prototype.setData = function (data) {
this.data_ = data;
this.renderChart_();
};
/**
* Получение радиуса
*/
DiagramSymbol.prototype.getRadius = function () {
return this.radius_;
};
/**
* Установка радиуса
* @param {number} radius - радиус
* @param {number} ratio - коэффициент сжатия
*/
DiagramSymbol.prototype.setRadius = function (radius, ratio) {
this.radius_ = radius;
this.donuratio_ = ratio || this.donuratio_;
this.renderChart_();
};
/**
* Включение анимации
* @param {false|number}
*/
DiagramSymbol.prototype.setAnimation = function (step) {
if (step === false) {
if (this.animation_.animate == false) return;
this.animation_.animate = false;
}
else {
if (this.animation_.step == step) return;
this.animation_.animate = true;
this.animation_.step = step;
}
this.renderChart_();
};
/**
* Отрисовка диаграммы
*/
DiagramSymbol.prototype.renderChart_ = function (atlasManager) {
let strokeStyle;
let strokeWidth = 0;
if (this.stroke_) {
strokeStyle = ol.Color.asString(this.stroke_.getColor());
strokeWidth = this.stroke_.getWidth();
}
let canvas = this.getImage();
let context = (canvas.getContext('2d'));
context.clearRect(0, 0, canvas.width, canvas.height);
context.lineJoin = "round";
let sum = this.data_.reduce((accumulator, value) => {
return accumulator + value
});
context.setTransform(1, 0, 0, 1, 0, 0);
context.translate(0, 0);
let step = this.animation_.animate ? this.animation_.step : 1;
let a, a0 = Math.PI * (step - 1.5);
let c = canvas.width / 2;
switch (this.type_) {
// Круговые диаграммы
case "donut":
case "pie3D":
case "pie": {
context.strokeStyle = strokeStyle;
context.lineWidth = strokeWidth;
context.save();
if (this.type_ === "pie3D") {
context.translate(0, c * 0.3);
context.scale(1, 0.7);
context.beginPath();
context.fillStyle = "#369";
context.arc(c, c * 1.4, this.radius_ * step, 0, 2 * Math.PI);
context.fill();
context.stroke();
}
if (this.type_ === "donut") {
context.save();
context.beginPath();
context.rect(0, 0, 2 * c, 2 * c);
context.arc(c, c, this.radius_ * step * this.donutRatio_, 0, 2 * Math.PI);
context.clip("evenodd");
}
for (let i = 0; i < this.data_.length; i++) {
context.beginPath();
context.moveTo(c, c);
context.fillStyle = this.colors_[i % this.colors_.length];
a = a0 + 2 * Math.PI * this.data_[i] / sum * step;
context.arc(c, c, this.radius_ * step, a0, a);
context.closePath();
context.fill();
context.stroke();
a0 = a;
}
if (this.type_ === "donut") {
context.restore();
context.beginPath();
context.strokeStyle = strokeStyle;
context.lineWidth = strokeWidth;
context.arc(c, c, this.radius_ * step * this.donutRatio_, Math.PI * (step - 1.5), a0);
context.stroke();
}
context.restore();
break;
}
// столбчатая диаграмма
case "bar":
default: {
let max = 0;
for (let i = 0; i < this.data_.length; i++) {
if (max < this.data_[i]) max = this.data_[i];
}
let s = Math.min(5, 2 * this.radius_ / this.data_.length);
let c = canvas.width / 2;
let b = canvas.width - strokeWidth;
let x, x0 = c - this.data_.length * s / 2
context.strokeStyle = strokeStyle;
context.lineWidth = strokeWidth;
for (let i = 0; i < this.data_.length; i++) {
context.beginPath();
context.fillStyle = this.colors_[i % this.colors_.length];
x = x0 + s;
let h = this.data_[i] / max * 2 * this.radius_ * step;
context.rect(x0, b - h, s, h);
context.closePath();
context.fill();
context.stroke();
x0 = x;
}
}
}
// установка привязки (смещения)
let anchor = this.getAnchor();
anchor[0] = c - this.offset_[0];
anchor[1] = c - this.offset_[1];
};
/**
* Переопределяемый метод - возвращает контрольную сумму символа
*/
DiagramSymbol.prototype.getChecksum = function () {
let strokeChecksum = (this.stroke_ !== null) ?
this.stroke_.getChecksum() : '-';
let recalculate = (this.checksums_ === null) ||
(strokeChecksum != this.checksums_[1] ||
fillChecksum != this.checksums_[2] ||
this.radius_ != this.checksums_[3] ||
this.data_.join('|') != this.checksums_[4]);
if (recalculate) {
let checksum = 'c' + strokeChecksum + fillChecksum
+ ((this.radius_ !== void 0) ? this.radius_.toString() : '-')
+ this.data_.join('|');
this.checksums_ = [checksum, strokeChecksum, fillChecksum, this.radius_, this.data_.join('|')];
}
return this.checksums_[0];
};
module.exports = DiagramSymbol;