mirror of
https://github.com/Viglino/ol-ext.git
synced 2025-12-08 19:26:29 +00:00
338 lines
11 KiB
JavaScript
338 lines
11 KiB
JavaScript
|
|
import ol from 'ol'
|
|
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_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 ol_extent from 'ol/extent'
|
|
|
|
/** 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 {olx.interaction.GeolocationDrawOption} 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'). Required.
|
|
* @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 projection unit), use ol.geom.LineString.simplify() method, default 5
|
|
* @param {Number} options.zoom zoom for tracking, default 16
|
|
* @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={};
|
|
var self = this;
|
|
|
|
// Geolocation
|
|
var geoloc = this.geolocation = new ol_Geolocation(/** @type {olx.GeolocationOptions} */
|
|
({ projection: "EPSG:4326",
|
|
trackingOptions:
|
|
{ maximumAge: 10000,
|
|
enableHighAccuracy: true,
|
|
timeout: 600000
|
|
}
|
|
}));
|
|
this.geolocation.on('change', this.draw_, 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.setFollowTrack (options.followTrack===undefined ? true : options.followTrack);
|
|
|
|
this.setActive(false);
|
|
};
|
|
ol.inherits(ol_interaction_GeolocationDraw, 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_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)
|
|
{ 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});
|
|
}
|
|
}
|
|
};
|
|
|
|
/** 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;
|
|
};
|
|
|
|
/** 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 (follow !== false && !this.lastPosition_ && map)
|
|
{ var pos = this.path_[this.path_.length-1];
|
|
if (pos)
|
|
{ map.getView().animate({
|
|
center: pos,
|
|
zoom: (follow!="position" ? this.get("zoom") : undefined)
|
|
})
|
|
}
|
|
}
|
|
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(active)
|
|
{ var map = this.getMap();
|
|
if (!map) return;
|
|
|
|
// Current location
|
|
var loc = this.geolocation;
|
|
var accu = loc.getAccuracy();
|
|
var pos = loc.getPosition();
|
|
pos.push (Math.round((loc.getAltitude()||0)*100)/100);
|
|
pos.push (Math.round((new Date()).getTime()/1000));
|
|
var p = loc.getAccuracyGeometry();
|
|
|
|
// Center on point
|
|
// console.log(this.get('followTrack'))
|
|
switch (this.get('followTrack'))
|
|
{ // Follow center + zoom
|
|
case true:
|
|
// modify zoom
|
|
if (this.get('followTrack') == true)
|
|
{ map.getView().setZoom( this.get("zoom") || 16 );
|
|
if (!ol_extent.containsExtent(map.getView().calculateExtent(map.getSize()), p.getExtent()))
|
|
{ map.getView().fit(p.getExtent());
|
|
}
|
|
}
|
|
// 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("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.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');
|
|
geo.simplify (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');
|
|
geo.simplify (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",loc.getHeading());
|
|
// Drawing
|
|
this.dispatchEvent({ type:'tracking', feature: this.sketch_[1], geolocation: loc });
|
|
};
|
|
|
|
export default ol_interaction_GeolocationDraw |