let AbstractLayer = require('./abstract');
let ol = require('openlayers');
let identifyResult = require('../../modules/tasks/identifyResult');
let searchResult = require('../../modules/tasks/searchResult');
let fuse = require('fuse.js');
let DataSource = require('./../dataSources/dataSource');
let Interaction = require('./../interactions/interaction');
let axios = require('axios');
/**
* Класс слоя типа HeatLayer - рендеринг производится на стороне клиента на основе сведений о геометрии и атрибутах объектов,
* а также назначенной функции стилизации (рендерера)
* Класс предназначен для создания слоев "тепловых карт"
* @extends AbstractLayer
*/
class HeatLayer extends AbstractLayer {
/**
* Создание экземляра слоя типа VectorLayer
* @param params
* @param params.url - адрес геосервера
* @param params.className - имя слоя
* @param params.radius - радиус окружности от точки
* @param params.blur - размытие
* @param params.shadow - размер тени
* @param params.weight - поле веса (атрибут) или функция
* @returns {ol.layer.HeatLayer}
*/
constructor(params) {
super(params);
this.initStore(params);
}
/**
* @param params
* @param params.className - имя класса
* @param params.mappings - объект, содержащий трансляцию полей (подстановка)
*/
initStore(params) {
this.store = {};
this.store.id = params.id;
this.store.alias = params.alias;
this.store.url = params.url;
this.store.className = params.className;
this.store.alias = params.alias;
this.store.layerType = "opengis.layers.vector";
this.store.mappings = params.mappings || {};
this.interactions = {};
this.layer = create(params);
this.store.layer = this.layer;
}
identify(params) {
let geometry = params.geometry || params.coordinate;
let radius = params.radius || 20;
let extent = [geometry[0] - radius, geometry[1] - radius, geometry[0] + radius, geometry[1] + radius];
let store = this.store;
let source = store.layer.getSource();
let features = source.getFeaturesInExtent(extent);
features = features.map(function (item) {
let featureAttributes = item.getProperties();
let featureGeometry = item.getGeometry();
delete featureAttributes.geometry;
let resultAttributes = generateField(featureAttributes, store);
return new identifyResult({
layerId: store.id,
layerName: store.alias,
queryGeometry: geometry,
attributes: featureAttributes,
preparedAttributes: resultAttributes,
geometry: featureGeometry,
dirtyData: item
});
});
return features;
}
/**
* Создание нового объекта и сохранение в dataSource
* @param params
*/
draw(params) {
let self = this;
let interactions = self.interactions;
if (!params) params = {};
params.source = self.layer.getSource();
interactions.draw = new Interaction.Draw(params);
for (let key in interactions) {
self.map.removeInteraction(interactions[key]);
}
self.map.addInteraction(interactions.draw);
if (params.snap === true) self.snap({active: true});
}
modify(params) {
let self = this;
let interactions = self.interactions;
if (!params) params = {};
params.source = self.layer.getSource();
interactions.modify = new Interaction.Modify(params);
for (let key in interactions) {
self.map.removeInteraction(interactions[key]);
}
self.map.addInteraction(interactions.modify);
if (params.snap === true) self.snap({active: true});
}
select(params) {
let self = this;
let interactions = self.interactions;
if (!params) params = {};
params.source = self.layer.getSource();
interactions.select = new Interaction.Select(params);
for (let key in interactions) {
self.map.removeInteraction(interactions[key]);
}
self.map.addInteraction(interactions.select);
if (params.snap === true) self.snap({active: true});
}
remove(params) {
let self = this;
let feature = params.feature;
let mode = params.mode;
self.layer.getSource().removeFeature(feature);
}
/**
* Включает или выключает привязку к другим объектам слоя
* @param params
*/
snap(params) {
let self = this;
if (!params) params = {};
params.source = self.layer.getSource();
let interaction = new Interaction.Snap(params);
if (params.active) {
self.map.addInteraction(interaction);
self.interactions.snap = interaction;
}
if (!params.active) {
self.map.removeInteraction(self.interactions.snap);
self.interactions.snap = undefined;
}
}
query(params) {
}
/**
* @param style - объект типа style
*/
setStyle(style) {
this.layer.setStyle(style.getStyle());
}
getStyle() {
return this.layer.getStyle();
}
/**
* Выполняет поисковый запрос к слою
* @param {object} params
* @param {string} params.query - поисковый запрос, например, ПС-12
* @param {number} params.minLength - минимальная длина запроса, по умолчанию - 1 символ
* @returns {*}
*/
search(params) {
let query = params.query;
let minQueryLength = params.minLength === undefined ? 1 : params.minLength;
let maxResultCount = params.maxResultCount === undefined ? 5 : params.maxResultCount;
if (query.length < minQueryLength) {
return [];
}
let layer = this.layer;
let store = this.store;
let source = layer.getSource();
let features = source.getFeatures().map(function (item) {
return item.getProperties();
});
let keys = features.length > 0 ? Object.keys(features[0]) : {};
let task = new fuse(features, {
caseSensitive: true,
includeScore: true,
shouldSort: true,
threshold: 0.2,
minMatchCharLength: 1,
keys: keys
});
let result = task.search(query);
result = result.map(function (element) {
let geometry = element.item.geometry;
let resultAttributes = generateField(element.item, store);
return new searchResult({
layerId: store.id,
layerName: store.alias,
queryGeometry: geometry,
attributes: element.item,
preparedAttributes: resultAttributes,
geometry: geometry,
dirtyData: new ol.Feature(element.item),
score: element.score
})
});
result = result.filter(function (item, index) {
return index < maxResultCount;
});
return result;
}
}
function create(params) {
let sourceFabric = new DataSource(params);
//создание векторного cлоя
let layer = new ol.layer.Heatmap({
source: sourceFabric.getSource(),
visible: params.visible || true,
opacity: params.opacity || 1,
minResolution: params.minResolution || 0,
maxResolution: params.maxResolution || 0,
renderBuffer: params.renderBuffer || 10,
style: params.style || undefined,
updateWhileAnimating: params.updateWhileAnimating || false,
updateWhileInteracting: params.updateWhileInteracting || false,
blur: params.blur || 10,
radius: params.radius || 10,
gradient: params.gradient || ['#00f', '#0ff', '#0f0', '#ff0', '#f00'],
weight: params.weight || "weight",
shadow: params.shadow || 250
});
layer.setProperties({
id: params.id,
className: params.className
});
return layer;
}
function generateEmptyMapping(name, value) {
let mapping = {
name: name,
alias: name,
group: "none",
value: value
};
return mapping;
}
function generateField(featureAttributes, store) {
let resultAttributes = [];
for (let key in featureAttributes) {
let localMapping = store.mappings[key] === undefined ? generateEmptyMapping(key, featureAttributes[key]) : store.mappings[key];
resultAttributes.push({
name: key,
alias: localMapping.alias,
group: localMapping.group,
value: featureAttributes[key]
});
}
return resultAttributes;
}
module.exports = HeatLayer;