UNPKG

kmap-ui

Version:

A components of zmap base on vue2.X

774 lines (727 loc) 23.5 kB
/* 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_ext_inherits from '../util/ext' import ol_interaction_Pointer from 'ol/interaction/Pointer' 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_geom_Point from 'ol/geom/Point' import ol_Feature from 'ol/Feature' import ol_geom_LineString from 'ol/geom/LineString' import ol_interaction_Interaction from 'ol/interaction/Interaction' import {ol_coordinate_dist2d, ol_coordinate_equal} from "../geom/GeomUtils"; import {boundingExtent as ol_extent_boundingExtent} from 'ol/extent' import {buffer as ol_extent_buffer} from 'ol/extent' import {altKeyOnly as ol_events_condition_altKeyOnly} from 'ol/events/condition' import {primaryAction as ol_events_condition_primaryAction} from 'ol/events/condition' import {always as ol_events_condition_always} from 'ol/events/condition' import '../geom/LineStringSplitAt' /** Interaction for modifying feature geometries. Similar to the core ol/interaction/Modify. * The interaction is more suitable to use to handle feature modification: only features concerned * by the modification are passed to the events (instead of all feature with ol/interaction/Modify) * - the modifystart event is fired before the feature is modified (no points still inserted) * - the modifyend event is fired after the modification * - it fires a modifying event * @constructor * @extends {ol_interaction_Pointer} * @fires modifystart * @fires modifying * @fires modifyend * @param {*} options * @param {ol.source.Vector|Array<ol.source.Vector>} options.source a list of source to modify (configured with useSpatialIndex set to true) * @param {ol.Collection.<ol.Feature>} options.features collection of feature to modify * @param {integer} options.pixelTolerance Pixel tolerance for considering the pointer close enough to a segment or vertex for editing. Default is 10. * @param {function|undefined} options.filter a filter that takes a feature and return true if it can be modified, default always true. * @param {ol.style.Style | Array<ol.style.Style> | undefined} options.style Style for the sketch features. * @param {ol.EventsConditionType | undefined} options.condition A function that takes an ol.MapBrowserEvent and returns a boolean to indicate whether that event will be considered to add or move a vertex to the sketch. Default is ol.events.condition.primaryAction. * @param {ol.EventsConditionType | undefined} options.deleteCondition A function that takes an ol.MapBrowserEvent and returns a boolean to indicate whether that event should be handled. By default, ol.events.condition.singleClick with ol.events.condition.altKeyOnly results in a vertex deletion. * @param {ol.EventsConditionType | undefined} options.insertVertexCondition A function that takes an ol.MapBrowserEvent and returns a boolean to indicate whether a new vertex can be added to the sketch features. Default is ol.events.condition.always */ var ol_interaction_ModifyFeature = function(options){ if (!options) options = {}; var dragging, modifying; ol_interaction_Pointer.call(this,{ /* handleDownEvent: this.handleDownEvent, handleDragEvent: this.handleDragEvent, handleMoveEvent: this.handleMoveEvent, handleUpEvent: this.handleUpEvent, */ handleEvent: function(e) { switch(e.type) { case 'pointerdown': { dragging = this.handleDownEvent(e); modifying = dragging || this._deleteCondition(e); return !dragging; } case 'pointerup': { dragging = false; return this.handleUpEvent(e); } case 'pointerdrag': { if (dragging) return this.handleDragEvent(e); else return true; } case 'pointermove': { if (!dragging) return this.handleMoveEvent(e); else return true; } case 'singleclick': case 'click': { // Prevent click when modifying return !modifying; } default: return true; } } }); // Snap distance (in px) this.snapDistance_ = options.pixelTolerance || 10; // Split tolerance between the calculated intersection and the geometry this.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: options.features })); } // Get all features candidate this.filterSplit_ = options.filter || function(){ return true; }; this._condition = options.condition || ol_events_condition_primaryAction; this._deleteCondition = options.deleteCondition || ol_events_condition_altKeyOnly; this._insertVertexCondition = options.insertVertexCondition || ol_events_condition_always; // Default style var sketchStyle = function() { return [ new ol_style_Style({ image: new ol_style_Circle({ radius: 6, fill: new ol_style_Fill({ color: [0, 153, 255, 1] }), stroke: new ol_style_Stroke({ color: '#FFF', width: 1.25 }) }) }) ]; } // Custom style if (options.style) { if (typeof(options.style) === 'function') { sketchStyle = options.style } else { sketchStyle = function() { return options.style; } } } // Create a new overlay for the sketch this.overlayLayer_ = new ol_layer_Vector({ source: new ol_source_Vector({ useSpatialIndex: false }), name:'Modify overlay', displayInLayerSwitcher: false, style: sketchStyle }); }; ol_ext_inherits(ol_interaction_ModifyFeature, ol_interaction_Pointer); /** * 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_ModifyFeature.prototype.setMap = function(map) { if (this.getMap()) this.getMap().removeLayer(this.overlayLayer_); ol_interaction_Interaction.prototype.setMap.call (this, map); this.overlayLayer_.setMap(map); }; /** * Activate or deactivate the interaction + remove the sketch. * @param {boolean} active. * @api stable */ ol_interaction_ModifyFeature.prototype.setActive = function(active) { ol_interaction_Interaction.prototype.setActive.call (this, active); if (this.overlayLayer_) this.overlayLayer_.getSource().clear(); }; /** Get closest feature at pixel * @param {ol.Pixel} * @return {*} * @private */ ol_interaction_ModifyFeature.prototype.getClosestFeature = function(e) { var f, c, 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 && this.filterSplit_(f)) { var ci = f.getGeometry().getClosestPoint(e.coordinate); var di = ol_coordinate_dist2d(e.coordinate,ci) / e.frameState.viewState.resolution; if (di < d){ d = di; c = ci; } break; } } if (d > this.snapDistance_) { return false; } else { // Snap to node var coord = this.getNearestCoord (c, f.getGeometry()); if (coord) { coord = coord.coord; var p = this.getMap().getPixelFromCoordinate(coord); if (ol_coordinate_dist2d(e.pixel, p) < this.snapDistance_) { c = coord; } // return { source:source, feature:f, coord: c }; } } } /** Get nearest coordinate in a list * @param {ol.coordinate} pt the point to find nearest * @param {ol.geom} coords list of coordinates * @return {*} the nearest point with a coord (projected point), dist (distance to the geom), ring (if Polygon) */ ol_interaction_ModifyFeature.prototype.getNearestCoord = function(pt, geom) { var i, l, p, p0, dm; switch (geom.getType()) { case 'Point': { return { coord: geom.getCoordinates(), dist: ol_coordinate_dist2d(geom.getCoordinates(), pt) }; } case 'MultiPoint': { return this.getNearestCoord (pt, new ol_geom_LineString(geom.getCoordinates())); } case 'LineString': case 'LinearRing': { var d; dm = Number.MAX_VALUE; var coords = geom.getCoordinates(); for (i=0; i < coords.length; i++) { d = ol_coordinate_dist2d (pt, coords[i]); if (d < dm) { dm = d; p0 = coords[i]; } } return { coord: p0, dist: dm }; } case 'MultiLineString': { var lstring = geom.getLineStrings(); p0 = false, dm = Number.MAX_VALUE; for (i=0; l=lstring[i]; i++) { p = this.getNearestCoord(pt, l); if (p && p.dist<dm) { p0 = p; dm = p.dist; p0.ring = i; } } return p0; } case 'Polygon': { var lring = geom.getLinearRings(); p0 = false; dm = Number.MAX_VALUE; for (i=0; l=lring[i]; i++) { p = this.getNearestCoord(pt, l); if (p && p.dist<dm) { p0 = p; dm = p.dist; p0.ring = i; } } return p0; } case 'MultiPolygon': { var poly = geom.getPolygons(); p0 = false; dm = Number.MAX_VALUE; for (i=0; l=poly[i]; i++) { p = this.getNearestCoord(pt, l); if (p && p.dist<dm) { p0 = p; dm = p.dist; p0.poly = i; } } return p0; } case 'GeometryCollection': { var g = geom.getGeometries(); p0 = false; dm = Number.MAX_VALUE; for (i=0; l=g[i]; i++) { p = this.getNearestCoord(pt, l); if (p && p.dist<dm) { p0 = p; dm = p.dist; p0.geom = i; } } return p0; } default: return false; } }; /** Get arcs concerned by a modification * @param {ol.geom} geom the geometry concerned * @param {ol.coordinate} coord pointed coordinates */ ol_interaction_ModifyFeature.prototype.getArcs = function(geom, coord) { var arcs = false; var coords, i, s, l; switch(geom.getType()) { case 'Point': { if (ol_coordinate_equal(coord, geom.getCoordinates())) { arcs = { geom: geom, type: geom.getType(), coord1: [], coord2: [], node: true } } break; } case 'MultiPoint': { coords = geom.getCoordinates(); for (i=0; i < coords.length; i++) { if (ol_coordinate_equal(coord, coords[i])) { arcs = { geom: geom, type: geom.getType(), index: i, coord1: [], coord2: [], node: true } break; } } break; } case 'LinearRing': case 'LineString': { var p = geom.getClosestPoint(coord); if (ol_coordinate_dist2d(p,coord) < 1.5*this.tolerance_) { var split; // Split the line in two if (geom.getType() === 'LinearRing') { var g = new ol_geom_LineString(geom.getCoordinates()); split = g.splitAt(coord, this.tolerance_); } else { split = geom.splitAt(coord, this.tolerance_); } // If more than 2 if (split.length>2) { coords = split[1].getCoordinates(); for (i=2; s=split[i]; i++) { var c = s.getCoordinates(); c.shift(); coords = coords.concat(c); } split = [ split[0], new ol_geom_LineString(coords) ]; } // Split in two if (split.length === 2) { var c0 = split[0].getCoordinates(); var c1 = split[1].getCoordinates(); var nbpt = c0.length + c1.length -1; c0.pop(); c1.shift(); arcs = { geom: geom, type: geom.getType(), coord1: c0, coord2: c1, node: (geom.getCoordinates().length === nbpt), closed: false } } else if (split.length === 1) { s = split[0].getCoordinates(); var start = ol_coordinate_equal(s[0], coord); var end = ol_coordinate_equal(s[s.length-1], coord); // Move first point if (start) { s.shift(); if (end) s.pop(); arcs = { geom: geom, type: geom.getType(), coord1: [], coord2: s, node: true, closed: end } } else if (end) { // Move last point s.pop() arcs = { geom: geom, type: geom.getType(), coord1: s, coord2: [], node: true, closed: false } } } } break; } case 'MultiLineString': { var lstring = geom.getLineStrings(); for (i=0; l=lstring[i]; i++) { arcs = this.getArcs(l, coord); if (arcs) { arcs.geom = geom; arcs.type = geom.getType(); arcs.lstring = i; break; } } break; } case 'Polygon': { var lring = geom.getLinearRings(); for (i=0; l=lring[i]; i++) { arcs = this.getArcs(l, coord); if (arcs) { arcs.geom = geom; arcs.type = geom.getType(); arcs.index = i; break; } } break; } case 'MultiPolygon': { var poly = geom.getPolygons(); for (i=0; l=poly[i]; i++) { arcs = this.getArcs(l, coord); if (arcs) { arcs.geom = geom; arcs.type = geom.getType(); arcs.poly = i; break; } } break; } case 'GeometryCollection': { // var g = geom.getGeometries(); for (i=0; l=g[i]; i++) { arcs = this.getArcs(l, coord); if (arcs) { arcs.geom = geom; arcs.g = i; arcs.typeg = arcs.type; arcs.type = geom.getType(); break; } } break; } default: { console.error('ol/interaction/ModifyFeature '+geom.getType()+' not supported!'); break; } } return arcs; }; /** * @param {ol.MapBrowserEvent} evt Map browser event. * @return {boolean} `true` to start the drag sequence. */ ol_interaction_ModifyFeature.prototype.handleDownEvent = function(evt) { if (!this.getActive()) return false; // Something to split ? var current = this.getClosestFeature(evt); if (current && (this._condition(evt) || this._deleteCondition(evt))) { var features = []; this.arcs = []; // Get features concerned this.sources_.forEach(function(s) { var extent = ol_extent_buffer (ol_extent_boundingExtent([current.coord]), this.tolerance_); features = features.concat(features, s.getFeaturesInExtent(extent)); }.bind(this)); // Get arcs concerned this._modifiedFeatures = []; features.forEach(function(f) { var a = this.getArcs(f.getGeometry(), current.coord); if (a) { if (this._insertVertexCondition(evt) || a.node) { a.feature = f; this._modifiedFeatures.push(f); this.arcs.push(a); } } }.bind(this)); if (this._modifiedFeatures.length) { if (this._deleteCondition(evt)) { return !this._removePoint(current, evt); } else { this.dispatchEvent({ type:'modifystart', coordinate: current.coord, originalEvent: evt.originalEvent, features: this._modifiedFeatures }); this.handleDragEvent({ coordinate: current.coord }) return true; } } else { return true; } } else { return false; } }; /** Get modified features * @return {Array<ol.Feature>} list of modified features */ ol_interaction_ModifyFeature.prototype.getModifiedFeatures = function() { return this._modifiedFeatures || []; }; /** Removes the vertex currently being pointed. */ ol_interaction_ModifyFeature.prototype.removePoint = function() { this._removePoint({},{}); }; /** * @private */ ol_interaction_ModifyFeature.prototype._getModification = function(a) { var coords = a.coord1.concat(a.coord2); switch (a.type) { case 'LineString': { if (a.closed) coords.push(coords[0]); if (coords.length>1) { if (a.geom.getCoordinates().length != coords.length) { a.coords = coords; return true; } } break; } case 'MultiLineString': { if (a.closed) coords.push(coords[0]); if (coords.length>1) { var c = a.geom.getCoordinates(); if (c[a.lstring].length != coords.length) { c[a.lstring] = coords; a.coords = c; return true; } } break; } case 'Polygon': { if (a.closed) coords.push(coords[0]); if (coords.length>3) { c = a.geom.getCoordinates(); if (c[a.index].length != coords.length) { c[a.index] = coords; a.coords = c; return true; } } break; } case 'MultiPolygon': { if (a.closed) coords.push(coords[0]); if (coords.length>3) { c = a.geom.getCoordinates(); if (c[a.poly][a.index].length != coords.length) { c[a.poly][a.index] = coords; a.coords = c; return true; } } break; } case 'GeometryCollection': { a.type = a.typeg; var geom = a.geom; var geoms = geom.getGeometries(); a.geom = geoms[a.g]; var found = this._getModification(a); // Restore current arc geom.setGeometries(geoms); a.geom = geom; a.type = 'GeometryCollection'; return found; } default: { //console.error('ol/interaction/ModifyFeature '+a.type+' not supported!'); break; } } return false; }; /** Removes the vertex currently being pointed. * @private */ ol_interaction_ModifyFeature.prototype._removePoint = function(current, evt) { if (!this.arcs) return false; this.overlayLayer_.getSource().clear(); var found = false; // Get all modifications this.arcs.forEach(function(a) { found = found || this._getModification(a); }.bind(this)); // Almost one point is removed if (found) { this.dispatchEvent({ type:'modifystart', coordinate: current.coord, originalEvent: evt.originalEvent, features: this._modifiedFeatures }); this.arcs.forEach(function(a) { if (a.geom.getType() === 'GeometryCollection') { if (a.coords) { var geoms = a.geom.getGeometries(); geoms[a.g].setCoordinates(a.coords); a.geom.setGeometries(geoms); } } else { if (a.coords) a.geom.setCoordinates(a.coords); } }.bind(this)); this.dispatchEvent({ type:'modifyend', coordinate: current.coord, originalEvent: evt.originalEvent, features: this._modifiedFeatures }); } this.arcs = []; return found; }; /** * @private */ ol_interaction_ModifyFeature.prototype.handleUpEvent = function(e) { if (!this.getActive()) return false; if (!this.arcs || !this.arcs.length) return true; this.overlayLayer_.getSource().clear(); this.dispatchEvent({ type:'modifyend', coordinate: e.coordinate, originalEvent: e.originalEvent, features: this._modifiedFeatures }); this.arcs = []; return true; }; /** * @private */ ol_interaction_ModifyFeature.prototype.setArcCoordinates = function(a, coords) { var c; switch (a.type) { case 'Point': { a.geom.setCoordinates(coords[0]); break; } case 'MultiPoint': { c = a.geom.getCoordinates(); c[a.index] = coords[0]; a.geom.setCoordinates(c); break; } case 'LineString': { a.geom.setCoordinates(coords); break; } case 'MultiLineString': { c = a.geom.getCoordinates(); c[a.lstring] = coords; a.geom.setCoordinates(c); break; } case 'Polygon': { c = a.geom.getCoordinates(); c[a.index] = coords; a.geom.setCoordinates(c); break; } case 'MultiPolygon': { c = a.geom.getCoordinates(); c[a.poly][a.index] = coords; a.geom.setCoordinates(c); break; } case 'GeometryCollection': { a.type = a.typeg; var geom = a.geom; var geoms = geom.getGeometries(); a.geom = geoms[a.g]; this.setArcCoordinates(a, coords); geom.setGeometries(geoms); a.geom = geom; a.type = 'GeometryCollection'; break; } } }; /** * @private */ ol_interaction_ModifyFeature.prototype.handleDragEvent = function(e) { if (!this.getActive()) return false; if (!this.arcs) return true; // Show sketch this.overlayLayer_.getSource().clear(); var p = new ol_Feature(new ol_geom_Point(e.coordinate)); this.overlayLayer_.getSource().addFeature(p); // Nothing to do if (!this.arcs.length) return true; // Move arcs this.arcs.forEach(function(a) { var coords = a.coord1.concat([e.coordinate], a.coord2); if (a.closed) coords.push(e.coordinate); this.setArcCoordinates(a, coords); }.bind(this)); this.dispatchEvent({ type:'modifying', coordinate: e.coordinate, originalEvent: e.originalEvent, features: this._modifiedFeatures }); return true; }; /** * @param {ol.MapBrowserEvent} evt Event. * @private */ ol_interaction_ModifyFeature.prototype.handleMoveEvent = function(e) { if (!this.getActive()) return false; this.overlayLayer_.getSource().clear(); var current = this.getClosestFeature(e); // Draw sketch if (current) { var p = new ol_Feature(new ol_geom_Point(current.coord)); this.overlayLayer_.getSource().addFeature(p); } // Show cursor var element = e.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_ModifyFeature