240 lines
8.1 KiB
JavaScript
240 lines
8.1 KiB
JavaScript
/* Copyright (c) 2017 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_geom_Polygon from 'ol/geom/Polygon.js'
|
|
import ol_geom_MultiPolygon from 'ol/geom/MultiPolygon.js'
|
|
import ol_geom_LinearRing from 'ol/geom/LinearRing.js'
|
|
import ol_interaction_Draw from 'ol/interaction/Draw.js'
|
|
import ol_interaction_Select from 'ol/interaction/Select.js'
|
|
|
|
/** Interaction to draw holes in a polygon.
|
|
* It fires a drawstart, drawend event when drawing the hole
|
|
* and a modifystart, modifyend event before and after inserting the hole in the feature geometry.
|
|
* @constructor
|
|
* @extends {ol_interaction_Interaction}
|
|
* @fires drawstart
|
|
* @fires drawend
|
|
* @fires modifystart
|
|
* @fires modifyend
|
|
* @param {olx.interaction.DrawHoleOptions} options extend olx.interaction.DrawOptions
|
|
* @param {Array<ol.layer.Vector> | function | undefined} options.layers A list of layers from which polygons should be selected. Alternatively, a filter function can be provided. default: all visible layers
|
|
* @param {Array<ol.Feature> | ol.Collection<ol.Feature> | function | undefined} options.featureFilter An array or a collection of features the interaction applies on or a function that takes a feature and a layer and returns true if the feature is a candidate
|
|
* @param { ol.style.Style | Array<ol.style.Style> | StyleFunction | undefined } Style for the selected features, default: default edit style
|
|
* @param {function | undefined} options.geometryFunction Draw interaction geometry function to customize the hole
|
|
*/
|
|
var ol_interaction_DrawHole = class olinteractionDrawHole extends ol_interaction_Draw {
|
|
constructor(options) {
|
|
options = options || {}
|
|
// Geometry function that test points inside the current selection
|
|
var _geometryFn = function(coordinates, geometry) {
|
|
var coord = coordinates[0].pop()
|
|
if (!this.getPolygon() || this.getPolygon().intersectsCoordinate(coord)) {
|
|
this.lastOKCoord = [coord[0], coord[1]]
|
|
}
|
|
coordinates[0].push([this.lastOKCoord[0], this.lastOKCoord[1]])
|
|
|
|
if (geometry) {
|
|
geometry.setCoordinates([coordinates[0].concat([coordinates[0][0]])])
|
|
} else {
|
|
geometry = new ol_geom_Polygon(coordinates)
|
|
}
|
|
return geometry
|
|
}
|
|
var geomFn = options.geometryFunction
|
|
if (geomFn) {
|
|
options.geometryFunction = function (c, g, p) {
|
|
g = _geometryFn.bind(this)(c, g)
|
|
return geomFn.bind(this)(c, g, p)
|
|
}
|
|
} else {
|
|
options.geometryFunction = _geometryFn
|
|
}
|
|
|
|
// Create draw interaction
|
|
options.type = 'Polygon';
|
|
super(options)
|
|
|
|
// Select interaction for the current feature
|
|
this._select = new ol_interaction_Select({ style: options.style })
|
|
this._select.setActive(false)
|
|
|
|
// Layer filter function
|
|
if (options.layers) {
|
|
if (typeof (options.layers) === 'function') {
|
|
this.layers_ = options.layers
|
|
} else if (options.layers.indexOf) {
|
|
this.layers_ = function (l) {
|
|
return (options.layers.indexOf(l) >= 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Features to apply on
|
|
if (typeof (options.featureFilter) === 'function') {
|
|
this._features = options.featureFilter
|
|
} else if (options.featureFilter) {
|
|
var features = options.featureFilter
|
|
this._features = function (f) {
|
|
if (features.indexOf) {
|
|
return !!features[features.indexOf(f)]
|
|
} else {
|
|
return !!features.item(features.getArray().indexOf(f))
|
|
}
|
|
}
|
|
} else {
|
|
this._features = function () { return true }
|
|
}
|
|
|
|
// Start drawing if inside a feature
|
|
this.on('drawstart', this._startDrawing.bind(this))
|
|
// End drawing add the hole to the current Polygon
|
|
this.on('drawend', this._finishDrawing.bind(this))
|
|
}
|
|
|
|
/**
|
|
* Remove the interaction from its current map, if any, and attach it to a new
|
|
* map, if any. Pass `null` to just remove the interaction from the current map.
|
|
* @param {ol.Map} map Map.
|
|
* @api stable
|
|
*/
|
|
setMap(map) {
|
|
// Remove previous selection
|
|
if (this.getMap()) this.getMap().removeInteraction(this._select)
|
|
// Add new one
|
|
if (map) map.addInteraction(this._select)
|
|
super.setMap.call(this, map)
|
|
}
|
|
/**
|
|
* Activate/deactivate the interaction
|
|
* @param {boolean}
|
|
* @api stable
|
|
*/
|
|
setActive(b) {
|
|
if (this._select) this._select.getFeatures().clear()
|
|
super.setActive.call(this, b)
|
|
}
|
|
/**
|
|
* Remove last point of the feature currently being drawn
|
|
* (test if points to remove before).
|
|
*/
|
|
removeLastPoint() {
|
|
if (this._feature && this._feature.getGeometry().getCoordinates()[0].length > 2) {
|
|
super.removeLastPoint.call(this)
|
|
}
|
|
}
|
|
/**
|
|
* Get the current polygon to hole
|
|
* @return {ol.Feature}
|
|
*/
|
|
getPolygon() {
|
|
return this._polygon
|
|
// return this._select.getFeatures().item(0).getGeometry();
|
|
}
|
|
/**
|
|
* Get current feature to add a hole and start drawing
|
|
* @param {ol_interaction_Draw.Event} e
|
|
* @private
|
|
*/
|
|
_startDrawing(e) {
|
|
var map = this.getMap()
|
|
this._feature = e.feature
|
|
var coord = e.feature.getGeometry().getCoordinates()[0][0]
|
|
this._current = null
|
|
// Check object under the pointer
|
|
map.forEachFeatureAtPixel(
|
|
map.getPixelFromCoordinate(coord),
|
|
function (feature, layer) {
|
|
// Not yet found?
|
|
if (!this._current && this._features(feature, layer)) {
|
|
var poly = feature.getGeometry()
|
|
if (poly.getType() === "Polygon"
|
|
&& poly.intersectsCoordinate(coord)) {
|
|
this._polygonIndex = false
|
|
this._polygon = poly
|
|
this._current = feature
|
|
} else if (poly.getType() === "MultiPolygon"
|
|
&& poly.intersectsCoordinate(coord)) {
|
|
for (var i = 0, p; p = poly.getPolygon(i); i++) {
|
|
if (p.intersectsCoordinate(coord)) {
|
|
this._polygonIndex = i
|
|
this._polygon = p
|
|
this._current = feature
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}.bind(this), {
|
|
layerFilter: this.layers_
|
|
}
|
|
)
|
|
this._select.getFeatures().clear()
|
|
if (!this._current) {
|
|
this.setActive(false)
|
|
this.setActive(true)
|
|
} else {
|
|
this._select.getFeatures().push(this._current)
|
|
}
|
|
}
|
|
/**
|
|
* Stop drawing and add the sketch feature to the target feature.
|
|
* @param {ol_interaction_Draw.Event} e
|
|
* @private
|
|
*/
|
|
_finishDrawing(e) {
|
|
// The feature is the hole
|
|
e.hole = e.feature
|
|
// Get the current feature
|
|
e.feature = this._select.getFeatures().item(0)
|
|
this.dispatchEvent({ type: 'modifystart', features: [this._current] })
|
|
// Create the hole
|
|
var c = e.hole.getGeometry().getCoordinates()[0]
|
|
if (c.length > 3) {
|
|
if (this._polygonIndex !== false) {
|
|
var geom = e.feature.getGeometry()
|
|
var newGeom = new ol_geom_MultiPolygon([])
|
|
for (var i = 0, pi; pi = geom.getPolygon(i); i++) {
|
|
if (i === this._polygonIndex) {
|
|
pi.appendLinearRing(new ol_geom_LinearRing(c))
|
|
newGeom.appendPolygon(pi)
|
|
} else {
|
|
newGeom.appendPolygon(pi)
|
|
}
|
|
}
|
|
e.feature.setGeometry(newGeom)
|
|
} else {
|
|
this.getPolygon().appendLinearRing(new ol_geom_LinearRing(c))
|
|
}
|
|
}
|
|
this.dispatchEvent({ type: 'modifyend', features: [this._current] })
|
|
// reset
|
|
this._feature = null
|
|
this._select.getFeatures().clear()
|
|
}
|
|
/**
|
|
* Function that is called when a geometry's coordinates are updated.
|
|
* @param {Array<ol.coordinate>} coordinates
|
|
* @param {ol_geom_Polygon} geometry
|
|
* @return {ol_geom_Polygon}
|
|
* @private
|
|
*/
|
|
_geometryFn(coordinates, geometry) {
|
|
var coord = coordinates[0].pop()
|
|
if (!this.getPolygon() || this.getPolygon().intersectsCoordinate(coord)) {
|
|
this.lastOKCoord = [coord[0], coord[1]]
|
|
}
|
|
coordinates[0].push([this.lastOKCoord[0], this.lastOKCoord[1]])
|
|
|
|
if (geometry) {
|
|
geometry.setCoordinates([coordinates[0].concat([coordinates[0][0]])])
|
|
} else {
|
|
geometry = new ol_geom_Polygon(coordinates)
|
|
}
|
|
return geometry
|
|
}
|
|
}
|
|
|
|
export default ol_interaction_DrawHole
|