ol-ext
Version:
A set of cool extensions for OpenLayers (ol) in node modules structure
224 lines (217 loc) • 7.35 kB
JavaScript
/* Copyright (c) 2019 Jean-Marc VIGLINO,
released under the CeCILL-B license (French BSD license)
(http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt).
*/
import ol_Feature from 'ol/Feature.js'
import ol_geom_Polygon from 'ol/geom/Polygon.js'
import ol_source_Vector from 'ol/source/Vector.js'
import { ol_coordinate_getFeatureCenter } from "../geom/GeomUtils.js";
import './Vector.js'
/** Abstract base class; normally only used for creating subclasses. Bin collector for data
* @constructor
* @extends {ol.source.Vector}
* @param {Object} options ol_source_VectorOptions + grid option
* @param {ol.source.Vector} options.source Source
* @param {boolean} options.listenChange listen changes (move) on source features to recalculate the bin, default true
* @param {fucntion} [options.geometryFunction] Function that takes an ol.Feature as argument and returns an ol.geom.Point as feature's center.
* @param {function} [options.flatAttributes] Function takes a bin and the features it contains and aggragate the features in the bin attributes when saving
*/
var ol_source_BinBase = class olsourceBinBase extends ol_source_Vector {
constructor(options) {
options = options || {};
super(options);
this._bindModify = this._onModifyFeature.bind(this);
this._watch = true;
this._origin = options.source;
this._listen = (options.listenChange !== false);
// Geometry function
this._geomFn = options.geometryFunction || ol_coordinate_getFeatureCenter || function (f) { return f.getGeometry().getFirstCoordinate(); };
// Future features
this._origin.on('addfeature', this._onAddFeature.bind(this));
this._origin.on('removefeature', this._onRemoveFeature.bind(this));
this._origin.on('clearstart', this._onClearFeature.bind(this));
this._origin.on('clearend', this._onClearFeature.bind(this));
if (typeof (options.flatAttributes) === 'function') {
this._flatAttributes = options.flatAttributes;
}
// Handle exsting feature (should be called from children when fully created)
// this.reset()
}
/**
* On add feature
* @param {ol.events.Event} e
* @param {ol.Feature} bin
* @private
*/
_onAddFeature(e, bin, listen) {
var f = e.feature || e.target;
bin = bin || this.getBinAt(this._geomFn(f), true);
if (bin)
bin.get('features').push(f);
if (this._listen && listen !== false)
f.on('change', this._bindModify);
}
/**
* On remove feature
* @param {ol.events.Event} e
* @param {ol.Feature} bin
* @private
*/
_onRemoveFeature(e, bin, listen) {
if (!this._watch)
return;
var f = e.feature || e.target;
bin = bin || this.getBinAt(this._geomFn(f));
if (bin) {
// Remove feature from bin
var features = bin.get('features');
for (var i = 0, fi; fi = features[i]; i++) {
if (fi === f) {
features.splice(i, 1);
break;
}
}
// Remove bin if no features
if (!features.length) {
this.removeFeature(bin);
}
} else {
// console.log("[ERROR:Bin] remove feature: feature doesn't exists anymore.");
}
if (this._listen && listen !== false)
f.un('change', this._bindModify);
}
/** When clearing features remove the listener
* @private
*/
_onClearFeature(e) {
if (e.type === 'clearstart') {
if (this._listen) {
this._origin.getFeatures().forEach(function (f) {
f.un('change', this._bindModify);
}.bind(this));
}
this.clear();
this._watch = false;
} else {
this._watch = true;
}
}
/**
* Get the bin that contains a feature
* @param {ol.Feature} f the feature
* @return {ol.Feature} the bin or null it doesn't exit
*/
getBin(feature) {
var bins = this.getFeatures();
for (var i = 0, b; b = bins[i]; i++) {
var features = b.get('features');
for (var j = 0, f; f = features[j]; j++) {
if (f === feature)
return b;
}
}
return null;
}
/** Get the grid geometry at the coord
* @param {ol.Coordinate} coord
* @param {Object} attributes add key/value to this object to add properties to the grid feature
* @returns {ol.geom.Polygon}
* @api
*/
getGridGeomAt(coord /*, attributes */) {
return new ol_geom_Polygon([coord]);
}
/** Get the bean at a coord
* @param {ol.Coordinate} coord
* @param {boolean} create true to create if doesn't exit
* @return {ol.Feature} the bin or null it doesn't exit
*/
getBinAt(coord, create) {
var attributes = {};
var g = this.getGridGeomAt(coord, attributes);
if (!g)
return null;
var center = g.getInteriorPoint ? g.getInteriorPoint().getCoordinates() : g.getInteriorPoints().getCoordinates()[0]; // ol_extent_getCenter(g.getExtent());
var features = this.getFeaturesAtCoordinate(center);
var bin = features[0];
if (!bin && create) {
attributes.geometry = g;
attributes.features = [];
attributes.center = center;
bin = new ol_Feature(attributes);
this.addFeature(bin);
}
return bin || null;
}
/**
* A feature has been modified
* @param {ol.events.Event} e
* @private
*/
_onModifyFeature(e) {
var bin = this.getBin(e.target);
var bin2 = this.getBinAt(this._geomFn(e.target), 'create');
if (bin !== bin2) {
// remove from the bin
if (bin) {
this._onRemoveFeature(e, bin, false);
}
// insert in the new bin
if (bin2) {
this._onAddFeature(e, bin2, false);
}
}
this.changed();
}
/** Clear all bins and generate a new one.
*/
reset() {
this.clear();
var features = this._origin.getFeatures();
for (var i = 0, f; f = features[i]; i++) {
this._onAddFeature({ feature: f });
}
this.changed();
}
/**
* Get features without circular dependencies (vs. getFeatures)
* @return {Array<ol.Feature>}
*/
getGridFeatures() {
var features = [];
this.getFeatures().forEach(function (f) {
var bin = new ol_Feature(f.getGeometry().clone());
for (var i in f.getProperties()) {
if (i !== 'features' && i !== 'geometry') {
bin.set(i, f.get(i));
}
}
bin.set('nb', f.get('features').length);
this._flatAttributes(bin, f.get('features'));
features.push(bin);
}.bind(this));
return features;
}
/** Create bin attributes using the features it contains when exporting
* @param {ol.Feature} bin the bin to export
* @param {Array<ol.Features>} features the features it contains
*/
_flatAttributes( /*bin, features*/) {
}
/** Set the flatAttribute function
* @param {function} fn Function that takes a bin and the features it contains and aggragate the features in the bin attributes when saving
*/
setFlatAttributesFn(fn) {
if (typeof (fn) === 'function')
this._flatAttributes = fn;
}
/**
* Get the orginal source
* @return {ol_source_Vector}
*/
getSource() {
return this._origin;
}
}
export default ol_source_BinBase