mirror of
https://github.com/Viglino/ol-ext.git
synced 2025-12-08 19:26:29 +00:00
261 lines
8.1 KiB
JavaScript
261 lines
8.1 KiB
JavaScript
/* Copyright (c) 2016 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 from 'ol'
|
|
import ol_interaction_Interaction from 'ol/interaction/interaction'
|
|
import ol_style_Style from 'ol/style/style'
|
|
import ol_style_Stroke from 'ol/style/stroke'
|
|
import ol_source_Vector from 'ol/source/vector'
|
|
import ol_style_Fill from 'ol/style/fill'
|
|
import ol_style_Circle from 'ol/style/circle'
|
|
import ol_layer_Vector from 'ol/layer/vector'
|
|
import ol_coordinate from 'ol/coordinate'
|
|
import ol_geom_Point from 'ol/geom/point'
|
|
import ol_Feature from 'ol/feature'
|
|
import ol_geom_LineString from 'ol/geom/linestring'
|
|
|
|
/** Interaction split interaction for splitting feature geometry
|
|
* @constructor
|
|
* @extends {ol_interaction_Interaction}
|
|
* @fires beforesplit, aftersplit
|
|
* @param {olx.interaction.SplitOptions}
|
|
* - source {ol.source.Vector|Array{ol.source.Vector}} a list of source to split (configured with useSpatialIndex set to true)
|
|
* - features {ol.Collection.<ol.Feature>} collection of feature to split
|
|
* - snapDistance {integer} distance (in px) to snap to an object, default 25px
|
|
* - cursor {string|undefined} cursor name to display when hovering an objet
|
|
* - filter {function|undefined} a filter that takes a feature and return true if it can be clipped, default always split.
|
|
* - featureStyle {ol_style_Style | Array<ol_style_Style> | false | undefined} Style for the selected features, choose false if you don't want feature selection. By default the default edit style is used.
|
|
* - sketchStyle {ol_style_Style | Array<ol_style_Style> | undefined} Style for the sektch features.
|
|
* - tolerance {function|undefined} Distance between the calculated intersection and a vertex on the source geometry below which the existing vertex will be used for the split. Default is 1e-10.
|
|
*/
|
|
var ol_interaction_Split = function(options)
|
|
{ if (!options) options = {};
|
|
|
|
ol_interaction_Interaction.call(this,
|
|
{ handleEvent: function(e)
|
|
{ switch (e.type)
|
|
{ case "singleclick":
|
|
return this.handleDownEvent(e);
|
|
case "pointermove":
|
|
return this.handleMoveEvent(e);
|
|
default:
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
|
|
// Snap distance (in px)
|
|
this.snapDistance_ = options.snapDistance || 25;
|
|
// Split tolerance between the calculated intersection and the geometry
|
|
this.tolerance_ = options.tolerance || 1e-10;
|
|
// Cursor
|
|
this.cursor_ = options.cursor;
|
|
|
|
// List of source to split
|
|
this.sources_ = options.sources ? (options.sources instanceof Array) ? options.sources:[options.sources] : [];
|
|
|
|
if (options.features)
|
|
{ this.sources_.push (new ol_source_Vector({ features: features }));
|
|
}
|
|
|
|
// Get all features candidate
|
|
this.filterSplit_ = options.filter || function(){ return true; };
|
|
|
|
// Default style
|
|
var white = [255, 255, 255, 1];
|
|
var blue = [0, 153, 255, 1];
|
|
var width = 3;
|
|
var fill = new ol_style_Fill({ color: 'rgba(255,255,255,0.4)' });
|
|
var stroke = new ol_style_Stroke({
|
|
color: '#3399CC',
|
|
width: 1.25
|
|
});
|
|
var sketchStyle =
|
|
[ new ol_style_Style({
|
|
image: new ol_style_Circle({
|
|
fill: fill,
|
|
stroke: stroke,
|
|
radius: 5
|
|
}),
|
|
fill: fill,
|
|
stroke: stroke
|
|
})
|
|
];
|
|
var featureStyle =
|
|
[ new ol_style_Style({
|
|
stroke: new ol_style_Stroke({
|
|
color: white,
|
|
width: width + 2
|
|
})
|
|
}),
|
|
new ol_style_Style({
|
|
image: new ol_style_Circle({
|
|
radius: 2*width,
|
|
fill: new ol_style_Fill({
|
|
color: blue
|
|
}),
|
|
stroke: new ol_style_Stroke({
|
|
color: white,
|
|
width: width/2
|
|
})
|
|
}),
|
|
stroke: new ol_style_Stroke({
|
|
color: blue,
|
|
width: width
|
|
})
|
|
}),
|
|
];
|
|
|
|
// Custom style
|
|
if (options.sketchStyle) sketchStyle = options.sketchStyle instanceof Array ? options.sketchStyle : [options.sketchStyle];
|
|
if (options.featureStyle) featureStyle = options.featureStyle instanceof Array ? options.featureStyle : [options.featureStyle];
|
|
|
|
// Create a new overlay for the sketch
|
|
this.overlayLayer_ = new ol_layer_Vector(
|
|
{ source: new ol_source_Vector({
|
|
useSpatialIndex: false
|
|
}),
|
|
name:'Split overlay',
|
|
displayInLayerSwitcher: false,
|
|
style: function(f)
|
|
{ if (f._sketch_) return sketchStyle;
|
|
else return featureStyle;
|
|
}
|
|
});
|
|
|
|
};
|
|
ol.inherits(ol_interaction_Split, ol_interaction_Interaction);
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
ol_interaction_Split.prototype.setMap = function(map)
|
|
{ if (this.getMap()) this.getMap().removeLayer(this.overlayLayer_);
|
|
ol_interaction_Interaction.prototype.setMap.call (this, map);
|
|
this.overlayLayer_.setMap(map);
|
|
};
|
|
|
|
/** Get closest feature at pixel
|
|
* @param {ol.Pixel}
|
|
* @return {ol.feature}
|
|
* @private
|
|
*/
|
|
ol_interaction_Split.prototype.getClosestFeature = function(e)
|
|
{ var f, c, g, d = this.snapDistance_+1;
|
|
for (var i=0; i<this.sources_.length; i++)
|
|
{ var source = this.sources_[i];
|
|
f = source.getClosestFeatureToCoordinate(e.coordinate);
|
|
if (f.getGeometry().splitAt, this.tolerance_)
|
|
{ c = f.getGeometry().getClosestPoint(e.coordinate);
|
|
g = new ol_geom_LineString([e.coordinate,c]);
|
|
d = g.getLength() / e.frameState.viewState.resolution;
|
|
break;
|
|
}
|
|
}
|
|
if (d > this.snapDistance_) return false;
|
|
else
|
|
{ // Snap to node
|
|
var coord = this.getNearestCoord (c, f.getGeometry().getCoordinates());
|
|
var p = this.getMap().getPixelFromCoordinate(coord);
|
|
if (ol_coordinate.dist2d(e.pixel, p) < this.snapDistance_)
|
|
{ c = coord;
|
|
}
|
|
//
|
|
return { source:source, feature:f, coord: c, link: g };
|
|
}
|
|
}
|
|
|
|
/** Get nearest coordinate in a list
|
|
* @param {ol.coordinate} pt the point to find nearest
|
|
* @param {Array<ol.coordinate>} coords list of coordinates
|
|
* @return {ol.coordinate} the nearest coordinate in the list
|
|
*/
|
|
ol_interaction_Split.prototype.getNearestCoord = function(pt, coords)
|
|
{ var d, dm=Number.MAX_VALUE, p0;
|
|
for (var i=0; i < coords.length; i++)
|
|
{ d = ol_coordinate.dist2d (pt, coords[i]);
|
|
if (d < dm)
|
|
{ dm = d;
|
|
p0 = coords[i];
|
|
}
|
|
}
|
|
return p0;
|
|
};
|
|
|
|
/**
|
|
* @param {ol.MapBrowserEvent} evt Map browser event.
|
|
* @return {boolean} `true` to start the drag sequence.
|
|
*/
|
|
ol_interaction_Split.prototype.handleDownEvent = function(evt)
|
|
{ // Something to split ?
|
|
var current = this.getClosestFeature(evt);
|
|
|
|
if (current)
|
|
{ var self = this;
|
|
self.overlayLayer_.getSource().clear();
|
|
var split = current.feature.getGeometry().splitAt(current.coord, this.tolerance_);
|
|
if (split.length > 1)
|
|
{ var tosplit = [];
|
|
for (var i=0; i<split.length; i++)
|
|
{ var f = current.feature.clone();
|
|
f.setGeometry(split[i]);
|
|
tosplit.push(f);
|
|
}
|
|
self.dispatchEvent({ type:'beforesplit', original: current.feature, features: tosplit });
|
|
current.source.dispatchEvent({ type:'beforesplit', original: current.feature, features: tosplit });
|
|
current.source.removeFeature(current.feature);
|
|
for (var i=0; i<tosplit.length; i++)
|
|
{ current.source.addFeature(tosplit[i]);
|
|
}
|
|
self.dispatchEvent({ type:'aftersplit', original: current.feature, features: tosplit });
|
|
current.source.dispatchEvent({ type:'aftersplit', original: current.feature, features: tosplit });
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* @param {ol.MapBrowserEvent} evt Event.
|
|
*/
|
|
ol_interaction_Split.prototype.handleMoveEvent = function(e)
|
|
{ var map = e.map;
|
|
this.overlayLayer_.getSource().clear();
|
|
var current = this.getClosestFeature(e);
|
|
|
|
if (current && this.filterSplit_(current.feature))
|
|
{ var coord, p, l;
|
|
// Draw sketch
|
|
this.overlayLayer_.getSource().addFeature(current.feature);
|
|
p = new ol_Feature(new ol_geom_Point(current.coord));
|
|
p._sketch_ = true;
|
|
this.overlayLayer_.getSource().addFeature(p);
|
|
//
|
|
l = new ol_Feature(new ol_geom_LineString([e.coordinate,current.coord]));
|
|
l._sketch_ = true;
|
|
this.overlayLayer_.getSource().addFeature(l);
|
|
}
|
|
|
|
var element = map.getTargetElement();
|
|
if (this.cursor_)
|
|
{ if (current)
|
|
{ if (element.style.cursor != this.cursor_)
|
|
{ this.previousCursor_ = element.style.cursor;
|
|
element.style.cursor = this.cursor_;
|
|
}
|
|
}
|
|
else if (this.previousCursor_ !== undefined)
|
|
{ element.style.cursor = this.previousCursor_;
|
|
this.previousCursor_ = undefined;
|
|
}
|
|
}
|
|
};
|
|
|
|
export default ol_interaction_Split
|
|
|