tchen-vuelayers
Version:
Web map Vue components with the power of OpenLayers
484 lines (414 loc) • 12.6 kB
JavaScript
/**
* VueLayers
* Web map Vue components with the power of OpenLayers
*
* @package vuelayers
* @author Vladimir Vershinin <ghettovoice@gmail.com>
* @version 0.11.1
* @license MIT
* @copyright (c) 2017-2019, Vladimir Vershinin <ghettovoice@gmail.com>
*/
import _Object$defineProperties from '@babel/runtime-corejs2/core-js/object/define-properties';
import _objectSpread from '@babel/runtime-corejs2/helpers/esm/objectSpread';
import _Promise from '@babel/runtime-corejs2/core-js/promise';
import { never, shiftKeyOnly, singleClick } from 'ol/events/condition';
import Feature from 'ol/Feature';
import SelectInteraction from 'ol/interaction/Select';
import Vue from 'vue';
import interaction from '../mixin/interaction';
import projTransforms from '../mixin/proj-transforms';
import stylesContainer from '../mixin/styles-container';
import { getFeatureId } from '../ol-ext/feature';
import { createStyle, defaultEditStyle } from '../ol-ext/style';
import observableFromOlEvent from '../rx-ext/from-ol-event';
import { hasInteraction, hasMap } from '../util/assert';
import { constant, difference, forEach, isFunction, mapValues, stubArray, pick } from '../util/minilo';
import mergeDescriptors from '../util/multi-merge-descriptors';
import { makeWatchers } from '../util/vue-helpers';
import _Object$assign from '@babel/runtime-corejs2/core-js/object/assign';
/**
* @vueProps
*/
var props = {
/**
* A function that takes an `ol.Feature` and an `ol.layer.Layer` and returns `true` if the feature may be selected or `false` otherwise.
* @type {function|undefined}
*/
filter: {
type: Function,
default: constant(true)
},
/**
* Hit-detection tolerance. Pixels inside the radius around the given position will be checked for features.
* This only works for the canvas renderer and not for WebGL.
* @type {number}
*/
hitTolerance: {
type: Number,
default: 0
},
/**
* A boolean that determines if the default behaviour should select only single features or all (overlapping)
* features at the clicked map position.
* @type {boolean}
*/
multi: {
type: Boolean,
default: false
},
/**
* Selected features as array of GeoJSON features with coordinates in the map view projection.
* @type {string[]|number[]|Object[]}
*/
features: {
type: Array,
default: stubArray
},
/**
* Wrap the world horizontally on the selection overlay.
* @type {boolean}
*/
wrapX: {
type: Boolean,
default: true
},
/**
* A function that takes an `ol.MapBrowserEvent` and returns a boolean to indicate whether that event should
* be handled. By default, this is `ol.events.condition.never`. Use this if you want to use different events
* for `add` and `remove` instead of `toggle`.
* @type {function|undefined}
*/
addCondition: {
type: Function,
default: never
},
/**
* A function that takes an `ol.MapBrowserEvent` and returns a boolean to indicate whether that event should be handled.
* This is the event for the selected features as a whole. By default, this is `ol.events.condition.singleClick`.
* Clicking on a feature selects that feature and removes any that were in the selection. Clicking outside any feature
* removes all from the selection.
* @type {function|undefined}
*/
condition: {
type: Function,
default: singleClick
},
/**
* A function that takes an `ol.MapBrowserEvent` and returns a boolean to indicate whether that event should be handled.
* By default, this is `ol.events.condition.never`. Use this if you want to use different events for `add` and `remove`
* instead of `toggle`.
* @type {function|undefined}
*/
removeCondition: {
type: Function,
default: never
},
/**
* A function that takes an `ol.MapBrowserEvent` and returns a boolean to indicate whether that event should be handled.
* This is in addition to the `condition` event. By default, `ol.events.condition.shiftKeyOnly`, i.e. pressing `shift`
* as well as the `condition` event, adds that feature to the current selection if it is not currently selected,
* and removes it if it is.
* @type {function|undefined}
*/
toggleCondition: {
type: Function,
default: shiftKeyOnly
}
/**
* @vueComputed
*/
};
var computed = {};
/**
* @vueMethods
*/
var methods = {
/**
* @return {Select}
* @protected
*/
createInteraction: function createInteraction() {
return new SelectInteraction({
multi: this.multi,
wrapX: this.wrapX,
filter: this.filter,
style: this.createStyleFunc(),
addCondition: this.addCondition,
condition: this.condition,
removeCondition: this.removeCondition,
toggleCondition: this.toggleCondition
});
},
/**
* @return {function(feature: Feature): Style}
* @protected
*/
getDefaultStyles: function getDefaultStyles() {
var defaultStyles = mapValues(defaultEditStyle(), function (styles) {
return styles.map(createStyle);
});
return function __selectDefaultStyleFunc(feature) {
if (feature.getGeometry()) {
return defaultStyles[feature.getGeometry().getType()];
}
};
},
/**
* @return {Feature[]}
*/
getFeatures: function getFeatures() {
return this.$interaction && this.$interaction.getFeatures().getArray() || [];
},
/**
* @returns {Object}
* @protected
*/
getServices: function getServices() {
return mergeDescriptors(interaction.methods.getServices.call(this), stylesContainer.methods.getServices.call(this));
},
/**
* @return {Interaction|undefined}
* @protected
*/
getStyleTarget: function getStyleTarget() {
return this.$interaction;
},
/**
* @return {void}
* @protected
*/
mount: function mount() {
interaction.methods.mount.call(this);
this.features.forEach(this.select);
},
/**
* @return {void}
* @protected
*/
unmount: function unmount() {
this.unselectAll();
interaction.methods.unmount.call(this);
},
/**
* @param {Object|Vue|Feature|string|number} feature
* @return {void}
* @throws {Error}
*/
select: function select(feature) {
hasMap(this);
hasInteraction(this);
var id = getFeatureId(feature);
if (!id) {
throw new Error('Undefined feature id');
}
if (feature instanceof Vue) {
feature = feature.$feature;
}
var selectedIds = this.$features.map(getFeatureId);
if (selectedIds.includes(id)) return;
if (!(feature instanceof Feature)) {
feature = undefined;
forEach(this.$map.getLayers().getArray(), function (layer) {
var source = layer.getSource();
if (source && isFunction(source.getFeatureById)) {
feature = source.getFeatureById(id);
}
return !feature;
});
}
feature && this.$interaction.getFeatures().push(feature);
},
/**
* @param {Object|Vue|Feature|string|number} feature
* @return {void}
*/
unselect: function unselect(feature) {
hasInteraction(this);
var id = getFeatureId(feature);
if (!id) {
throw new Error('Undefined feature id');
}
if (feature instanceof Vue) {
feature = feature.$feature;
}
var selectedIds = this.$features.map(getFeatureId);
var idx = selectedIds.findIndex(function (x) {
return x === id;
});
if (idx !== -1) {
this.$interaction.getFeatures().removeAt(idx);
}
},
/**
* @param {Array<{style: Style, condition: (function|boolean|undefined)}>|function(feature: Feature): Style|Vue|undefined} styles
* @return {void}
* @protected
*/
setStyle: function setStyle(styles) {
if (styles !== this._styles) {
this._styles = styles;
this.scheduleRefresh();
}
},
/**
* @return {Promise}
*/
refresh: function refresh() {
var _this = this;
return _Promise.all([new _Promise(function (resolve) {
if (_this.$interaction) {
var featuresCollection = _this.$interaction.getFeatures();
featuresCollection.once('change', function () {
return resolve();
});
featuresCollection.changed();
} else {
resolve();
}
}), interaction.methods.refresh.call(this)]);
},
/**
* @return {void}
* @protected
*/
subscribeAll: function subscribeAll() {
subscribeToInteractionChanges.call(this);
},
/**
* Removes all features from selection.
* @return {void}
*/
unselectAll: function unselectAll() {
hasInteraction(this);
this.$interaction.getFeatures().clear();
}
};
var watch = _objectSpread({}, makeWatchers(['filter', 'hitTolerance', 'multi', 'wrapX', 'addCondition', 'condition', 'removeCondition', 'toggleCondition'], function () {
return function () {
this.scheduleRecreate();
};
}), {
features: function features(value) {
if (!this.$interaction) return;
var diffById = function diffById(a, b) {
return getFeatureId(a) === getFeatureId(b);
};
var forSelect = difference(value, this.$features, diffById);
var forUnselect = difference(this.$features, value, diffById);
forSelect.forEach(this.select);
forUnselect.forEach(this.unselect);
}
});
/**
* @vueProto
* @alias module:select-interaction/interaction
* @title vl-interaction-select
*/
var script = {
name: 'vl-interaction-select',
mixins: [interaction, stylesContainer, projTransforms],
props: props,
computed: computed,
methods: methods,
watch: watch,
stubVNode: {
empty: false,
attrs: function attrs() {
return {
class: this.$options.name
};
}
},
created: function created() {
_Object$defineProperties(this, {
$features: {
enumerable: true,
get: this.getFeatures
}
});
}
};
/**
* @return {void}
* @private
*/
function subscribeToInteractionChanges() {
var _this2 = this;
hasInteraction(this);
var events = observableFromOlEvent(this.$interaction, 'select');
this.subscribeTo(events, function (_ref) {
var selected = _ref.selected,
deselected = _ref.deselected,
mapBrowserEvent = _ref.mapBrowserEvent;
++_this2.rev;
deselected.forEach(function (feature) {
return _this2.$emit('unselect', {
feature: feature,
mapBrowserEvent: mapBrowserEvent
});
});
selected.forEach(function (feature) {
return _this2.$emit('select', {
feature: feature,
mapBrowserEvent: mapBrowserEvent
});
});
_this2.$emit('update:features', _this2.$features.map(_this2.writeFeatureInDataProj.bind(_this2)));
});
}
/* script */
var __vue_script__ = script;
/* template */
var __vue_render__ = function __vue_render__() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _c('i', {
class: [_vm.$options.name],
staticStyle: {
"display": "none !important"
}
}, [_vm._t("default", null, {
features: _vm.features
})], 2);
};
var __vue_staticRenderFns__ = [];
/* style */
var __vue_inject_styles__ = undefined;
/* scoped */
var __vue_scope_id__ = undefined;
/* module identifier */
var __vue_module_identifier__ = undefined;
/* functional template */
var __vue_is_functional_template__ = false;
/* component normalizer */
function __vue_normalize__(template, style, script$$1, scope, functional, moduleIdentifier, createInjector, createInjectorSSR) {
var component = (typeof script$$1 === 'function' ? script$$1.options : script$$1) || {}; // For security concerns, we use only base name in production mode.
component.__file = "interaction.vue";
if (!component.render) {
component.render = template.render;
component.staticRenderFns = template.staticRenderFns;
component._compiled = true;
if (functional) component.functional = true;
}
component._scopeId = scope;
return component;
}
/* style inject */
/* style inject SSR */
var Interaction = __vue_normalize__({
render: __vue_render__,
staticRenderFns: __vue_staticRenderFns__
}, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, undefined, undefined);
function plugin(Vue$$1) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (plugin.installed) {
return;
}
plugin.installed = true;
options = pick(options, 'dataProjection');
_Object$assign(Interaction, options);
Vue$$1.component(Interaction.name, Interaction);
}
export default plugin;
export { Interaction, plugin as install };