UNPKG

kmap-ui

Version:

A components of zmap base on vue2.X

500 lines (468 loc) 17.2 kB
import ol_ext_inherits from '../util/ext' import ol_interaction_Interaction from 'ol/interaction/Interaction' import ol_Geolocation from 'ol/Geolocation' import ol_style_Circle from 'ol/style/Circle' import ol_style_Stroke from 'ol/style/Stroke' import ol_geom_Point from 'ol/geom/Point' import ol_geom_LineString from 'ol/geom/LineString' import ol_geom_Polygon from 'ol/geom/Polygon' import ol_geom_Circle from 'ol/geom/Circle' import { fromCircle as ol_geom_Polygon_fromCircle } from 'ol/geom/Polygon' import ol_style_Style from 'ol/style/Style' import ol_style_RegularShape from 'ol/style/RegularShape' import ol_style_Fill from 'ol/style/Fill' import ol_layer_Vector from 'ol/layer/Vector' import ol_source_Vector from 'ol/source/Vector' import ol_Feature from 'ol/Feature' import ol_interaction_Pointer from 'ol/interaction/Pointer' import {containsCoordinate as ol_extent_containsCoordinate, containsExtent as ol_extent_containsExtent} from 'ol/extent' // import {ol_coordinate_dist2d, ol_coordinate_equal} from '../geom/GeomUtils' import {transform as ol_proj_transform} from 'ol/proj' import {getDistance as ol_spehre_getDistance} from 'ol/sphere' /** Interaction to draw on the current geolocation * It combines a draw with a ol_Geolocation * @constructor * @extends {ol_interaction_Interaction} * @fires drawstart, drawend, drawing, tracking, follow * @param {any} options * @param { ol.Collection.<ol.Feature> | undefined } option.features Destination collection for the drawn features. * @param { ol.source.Vector | undefined } options.source Destination source for the drawn features. * @param {ol.geom.GeometryType} options.type Drawing type ('Point', 'LineString', 'Polygon'), default LineString. * @param {Number | undefined} options.minAccuracy minimum accuracy underneath a new point will be register (if no condition), default 20 * @param {function | undefined} options.condition a function that take a ol_Geolocation object and return a boolean to indicate whether location should be handled or not, default return true if accuraty < minAccuraty * @param {Object} options.attributes a list of attributes to register as Point properties: {accuracy:true,accuracyGeometry:true,heading:true,speed:true}, default none. * @param {Number} options.tolerance tolerance to add a new point (in meter), default 5 * @param {Number} options.zoom zoom for tracking, default 16 * @param {Number} options.minZoom min zoom for tracking, if zoom is less it will zoom to it, default use zoom option * @param {boolean|auto|position|visible} options.followTrack true if you want the interaction to follow the track on the map, default true * @param { ol.style.Style | Array.<ol.style.Style> | ol.StyleFunction | undefined } options.style Style for sketch features. */ var ol_interaction_GeolocationDraw = function(options) { if (!options) options={}; // Geolocation this.geolocation = new ol_Geolocation(({ projection: "EPSG:4326", trackingOptions: { maximumAge: 10000, enableHighAccuracy: true, timeout: 600000 } })); this.geolocation.on('change', this.draw_.bind(this)); // Current path this.path_ = []; this.lastPosition_ = false; // Default style var white = [255, 255, 255, 1]; var blue = [0, 153, 255, 1]; var width = 3; var circle = new ol_style_Circle({ radius: width * 2, fill: new ol_style_Fill({ color: blue }), stroke: new ol_style_Stroke({ color: white, width: width / 2 }) }); var style = [ new ol_style_Style({ stroke: new ol_style_Stroke({ color: white, width: width + 2 }) }), new ol_style_Style({ stroke: new ol_style_Stroke({ color: blue, width: width }), fill: new ol_style_Fill({ color: [255, 255, 255, 0.5] }) }) ]; var triangle = new ol_style_RegularShape({ radius: width * 3.5, points: 3, rotation: 0, fill: new ol_style_Fill({ color: blue }), stroke: new ol_style_Stroke({ color: white, width: width / 2 }) }); // stretch the symbol var c = triangle.getImage(); var ctx = c.getContext("2d"); var c2 = document.createElement('canvas'); c2.width = c2.height = c.width; c2.getContext("2d").drawImage(c, 0,0); ctx.clearRect(0,0,c.width,c.height); ctx.drawImage(c2, 0,0, c.width, c.height, width, 0, c.width-2*width, c.height); var defaultStyle = function(f) { if (f.get('heading')===undefined) { style[1].setImage(circle); } else { style[1].setImage(triangle); triangle.setRotation( f.get('heading') || 0); } return style; } // Style for the accuracy geometry this.locStyle = { error: new ol_style_Style({ fill: new ol_style_Fill({ color: [255, 0, 0, 0.2] }) }), warn: new ol_style_Style({ fill: new ol_style_Fill({ color: [255, 192, 0, 0.2] }) }), ok: new ol_style_Style({ fill: new ol_style_Fill({ color: [0, 255, 0, 0.2] }) }), }; // Create a new overlay layer for the sketch this.overlayLayer_ = new ol_layer_Vector({ source: new ol_source_Vector(), name:'GeolocationDraw overlay', style: options.style || defaultStyle }); this.sketch_ = [new ol_Feature(), new ol_Feature(), new ol_Feature()]; this.overlayLayer_.getSource().addFeatures(this.sketch_); this.features_ = options.features; this.source_ = options.source; this.condition_ = options.condition || function(loc) { return loc.getAccuracy() < this.get("minAccuracy") }; // Prevent interaction when tracking ol_interaction_Interaction.call(this, { handleEvent: function() { return (!this.get('followTrack') || this.get('followTrack')=='auto');// || !geoloc.getTracking()); } }); this.set('type', options.type||"LineString"); this.set('attributes', options.attributes||{}); this.set('minAccuracy', options.minAccuracy||20); this.set('tolerance', options.tolerance||5); this.set('zoom', options.zoom); this.set('minZoom', options.minZoom); this.setFollowTrack (options.followTrack===undefined ? true : options.followTrack); this.setActive(false); }; ol_ext_inherits(ol_interaction_GeolocationDraw, ol_interaction_Interaction); /** Simplify 3D geometry * @param {ol.geom.Geometry} geo * @param {number} tolerance */ ol_interaction_GeolocationDraw.prototype.simplify3D = function(geo, tolerance) { var geom = geo.getCoordinates(); var proj = this.getMap().getView().getProjection(); if (this.get("type")==='Polygon') { geom = geom[0]; } var simply = [geom[0]]; var pi, p = ol_proj_transform(geom[0], proj, 'EPSG:4326') for (var i=1; i<geom.length; i++) { pi = ol_proj_transform(geom[i], proj, 'EPSG:4326') var d = ol_spehre_getDistance(p, pi); if (d > tolerance) { simply.push(geom[i]); p = pi; } } if (simply[simply.length-1] !== geom[geom.length-1]) { simply.push(geom[geom.length-1]); } /* var simply = geo.simplify(tolerance).getCoordinates(); if (this.get("type")==='Polygon') { simply = simply[0]; } var step=0; simply.forEach(function(p) { for (; step<this.path_.length; step++) { if (ol_coordinate_equal(p, this.path_[step])) { p[2] = this.path_[step][2]; p[3] = this.path_[step][3]; break; } } }.bind(this)); */ // Get 3D geom if (this.get("type")==='Polygon') { geo = new ol_geom_Polygon([simply], 'XYZM'); } else { geo = new ol_geom_LineString(simply, 'XYZM'); } return geo; }; /** * 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_GeolocationDraw.prototype.setMap = function(map) { if (this.getMap()) this.getMap().removeLayer(this.overlayLayer_); ol_interaction_Pointer.prototype.setMap.call (this, map); this.overlayLayer_.setMap(map); if (map) this.geolocation.setProjection(map.getView().getProjection()); }; /** Activate or deactivate the interaction. * @param {boolean} active */ ol_interaction_GeolocationDraw.prototype.setActive = function(active) { if (active === this.getActive()) return; ol_interaction_Interaction.prototype.setActive.call(this, active); this.overlayLayer_.setVisible(active); if (this.getMap()) { this.geolocation.setTracking(active); this.getMap().renderSync(); } this.pause(!active); if (active) { // Start drawing this.reset(); this.dispatchEvent({ type:'drawstart', feature: this.sketch_[1]}); } else { var f = this.sketch_[1].clone(); if (f.getGeometry()) { if (this.features_) this.features_.push(f); if (this.source_) this.source_.addFeature(f); this.dispatchEvent({ type:'drawend', feature: f}); } } }; /** Simulate a track and override current geolocation * @param {Array<ol.coordinate>|boolean} track a list of point or false to stop * @param {*} options * @param {number} delay delay in ms, default 1000 (1s) * @param {number} accuracy gps accuracy, default 10 * @param {boolean} repeat repeat track, default true */ ol_interaction_GeolocationDraw.prototype.simulate = function(track, options) { if (this._track) { clearTimeout(this._track.timeout); } if (!track) { this._track = false; return; } options = options || {}; var delay = options.delay || 1000; function handleTrack() { if (this._track.pos >= this._track.track.length) { this._track = false; return; } var coord = this._track.track[this._track.pos]; coord[2] = coord[3] || 0; coord[3] = (new Date()).getTime(); this._track.pos++; if (options.repeat !== false) { this._track.pos = this._track.pos % this._track.track.length; } if (this.getActive()) this.draw_(true, coord, options.accuracy); this._track.timeout = setTimeout(handleTrack.bind(this), delay); } this._track = { track: track, pos: 0, timeout: setTimeout(handleTrack.bind(this), 0) } }; /** Is simulation on ? * @returns {boolean} */ ol_interaction_GeolocationDraw.prototype.simulating = function() { return !!this._track; }; /** Reset drawing */ ol_interaction_GeolocationDraw.prototype.reset = function() { this.sketch_[1].setGeometry(); this.path_ = []; this.lastPosition_ = false; }; /** Start tracking = setActive(true) */ ol_interaction_GeolocationDraw.prototype.start = function() { this.setActive(true); }; /** Stop tracking = setActive(false) */ ol_interaction_GeolocationDraw.prototype.stop = function() { this.setActive(false); }; /** Pause drawing * @param {boolean} b */ ol_interaction_GeolocationDraw.prototype.pause = function(b) { this.pause_ = (b!==false); }; /** Is paused * @return {boolean} b */ ol_interaction_GeolocationDraw.prototype.isPaused = function() { return this.pause_; }; /** Enable following the track on the map * @param {boolean|auto|position|visible} follow, * false: don't follow, * true: follow (position+zoom), * 'position': follow only position, * 'auto': start following until user move the map, * 'visible': center when position gets out of the visible extent */ ol_interaction_GeolocationDraw.prototype.setFollowTrack = function(follow) { this.set('followTrack', follow); var map = this.getMap(); // Center if wanted if (this.getActive() && map) { var zoom; if (follow !== 'position') { if (this.get('minZoom')) { zoom = Math.max(this.get('minZoom'), map.getView().getZoom()); } else { zoom = this.get('zoom'); } } if (follow !== false && !this.lastPosition_) { var pos = this.path_[this.path_.length-1]; if (pos) { map.getView().animate({ center: pos, zoom: zoom }); } } else if (follow==='auto' && this.lastPosition_) { map.getView().animate({ center: this.lastPosition_, zoom: zoom }); } } this.lastPosition_ = false; this.dispatchEvent({ type:'follow', following: follow!==false }); }; /** Add a new point to the current path * @private */ ol_interaction_GeolocationDraw.prototype.draw_ = function(simulate, coord, accuracy) { var map = this.getMap(); if (!map) return; var accu, pos, p, loc, heading; // Simulation mode if (this._track) { if (simulate!==true) return; pos = coord; accu = accuracy || 10; if (this.path_ && this.path_.length) { var pt = this.path_[this.path_.length-1]; heading = Math.atan2(coord[0]-pt[0],coord[1]-pt[1]) } var circle = new ol_geom_Circle(pos, map.getView().getResolution()*accu); p = ol_geom_Polygon_fromCircle(circle); } else { // Current location loc = this.geolocation; accu = loc.getAccuracy(); pos = loc.getPosition(); pos.push (Math.round((loc.getAltitude()||0)*100)/100); pos.push (Math.round((new Date()).getTime()/1000)); p = loc.getAccuracyGeometry(); heading = loc.getHeading(); } // Center on point // console.log(this.get('followTrack')) switch (this.get('followTrack')) { // Follow center + zoom case true: { // modify zoom if (this.get('followTrack') == true) { if (this.get('minZoom')) { if (this.get('minZoom') > map.getView().getZoom()) { map.getView().setZoom(this.get('minZoom')); } } else { map.getView().setZoom( this.get('zoom') || 16 ); } if (!ol_extent_containsExtent(map.getView().calculateExtent(map.getSize()), p.getExtent())) { map.getView().fit(p.getExtent()); } } map.getView().setCenter( pos ); break; } // Follow position case 'position': { // modify center map.getView().setCenter( pos ); break; } // Keep on following case 'auto': { if (this.lastPosition_) { var center = map.getView().getCenter(); // console.log(center,this.lastPosition_) if (center[0]!=this.lastPosition_[0] || center[1]!=this.lastPosition_[1]) { //this.dispatchEvent({ type:'follow', following: false }); this.setFollowTrack (false); } else { map.getView().setCenter( pos ); this.lastPosition_ = pos; } } else { map.getView().setCenter( pos ); if (this.get('minZoom')) { if (this.get('minZoom') > map.getView().getZoom()) { map.getView().setZoom(this.get('minZoom')); } } else if (this.get('zoom')) { map.getView().setZoom( this.get('zoom')); } this.lastPosition_ = pos; } break; } // Force to stay on the map case 'visible': { if (!ol_extent_containsCoordinate(map.getView().calculateExtent(map.getSize()), pos)) { map.getView().setCenter (pos); } break; } // Don't follow default: break; } // Draw occuracy var f = this.sketch_[0]; f.setGeometry(p); if (accu < this.get("minAccuracy")/2) f.setStyle(this.locStyle.ok); else if (accu < this.get("minAccuracy")) f.setStyle(this.locStyle.warn); else f.setStyle(this.locStyle.error); var geo; if (this.pause_) { this.lastPosition_ = pos; } if (!this.pause_ && (!loc || this.condition_.call(this, loc))) { f = this.sketch_[1]; this.path_.push(pos); switch (this.get("type")) { case "Point": this.path_ = [pos]; f.setGeometry(new ol_geom_Point(pos, 'XYZM')); var attr = this.get('attributes'); if (attr.heading) f.set("heading",loc.getHeading()); if (attr.accuracy) f.set("accuracy",loc.getAccuracy()); if (attr.altitudeAccuracy) f.set("altitudeAccuracy",loc.getAltitudeAccuracy()); if (attr.speed) f.set("speed",loc.getSpeed()); break; case "LineString": if (this.path_.length>1) { geo = new ol_geom_LineString(this.path_, 'XYZM'); if (this.get("tolerance")) geo = this.simplify3D (geo, this.get("tolerance")); f.setGeometry(geo); } else { f.setGeometry(); } break; case "Polygon": if (this.path_.length>2) { geo = new ol_geom_Polygon([this.path_], 'XYZM'); if (this.get("tolerance")) geo = this.simplify3D (geo, this.get("tolerance")); f.setGeometry(geo); } else f.setGeometry(); break; } this.dispatchEvent({ type:'drawing', feature: this.sketch_[1], geolocation: loc }); } this.sketch_[2].setGeometry(new ol_geom_Point(pos)); this.sketch_[2].set("heading", heading); // Drawing this.dispatchEvent({ type:'tracking', feature: this.sketch_[1], geolocation: loc }); }; export default ol_interaction_GeolocationDraw