303 lines
9.3 KiB
JavaScript
303 lines
9.3 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 {unByKey as ol_Observable_unByKey} from 'ol/Observable.js'
|
|
import ol_control_Control from 'ol/control/Control.js'
|
|
import ol_geom_Polygon from 'ol/geom/Polygon.js'
|
|
import ol_geom_Point from 'ol/geom/Point.js'
|
|
import ol_interaction_Pointer from 'ol/interaction/Pointer.js'
|
|
import ol_Map from 'ol/Map.js'
|
|
import ol_Collection from 'ol/Collection.js'
|
|
import ol_View from 'ol/View.js'
|
|
import ol_source_Vector from 'ol/source/Vector.js'
|
|
import ol_style_Style from 'ol/style/Style.js'
|
|
import ol_style_Circle from 'ol/style/Circle.js'
|
|
import ol_style_Fill from 'ol/style/Fill.js'
|
|
import ol_style_Stroke from 'ol/style/Stroke.js'
|
|
import ol_layer_Vector from 'ol/layer/Vector.js'
|
|
import ol_Feature from 'ol/Feature.js'
|
|
|
|
/**
|
|
* OpenLayers 3 Layer Overview Control.
|
|
* The overview can rotate with map.
|
|
* Zoom levels are configurable.
|
|
* Click on the overview will center the map.
|
|
* Change width/height of the overview trough css.
|
|
*
|
|
* @constructor
|
|
* @extends {ol_control_Control}
|
|
* @param {Object=} options Control options.
|
|
* @param {ol.ProjectionLike} options.projection The projection. Default is EPSG:3857 (Spherical Mercator).
|
|
* @param {Number} options.minZoom default 0
|
|
* @param {Number} options.maxZoom default 18
|
|
* @param {boolean} options.rotation enable rotation, default false
|
|
* @param {top|bottom-left|right} options.align position
|
|
* @param {Array<ol.layer>} options.layers list of layers
|
|
* @param {ol.style.Style | Array.<ol.style.Style> | undefined} options.style style to draw the map extent on the overveiw
|
|
* @param {bool|elastic} options.panAnimation use animation to center map on click, default true
|
|
*/
|
|
var ol_control_Overview = class olcontrolOverview extends ol_control_Control {
|
|
constructor(options) {
|
|
options = options || {}
|
|
var element = document.createElement("div");
|
|
|
|
super({
|
|
element: element,
|
|
target: options.target
|
|
})
|
|
|
|
var self = this
|
|
|
|
// API
|
|
this.minZoom = options.minZoom || 0
|
|
this.maxZoom = options.maxZoom || 18
|
|
this.rotation = options.rotation
|
|
|
|
if (options.target) {
|
|
this.panel_ = options.target
|
|
} else {
|
|
element.classList.add('ol-overview', 'ol-unselectable', 'ol-control', 'ol-collapsed')
|
|
if (/top/.test(options.align))
|
|
element.classList.add('ol-control-top')
|
|
if (/right/.test(options.align))
|
|
element.classList.add('ol-control-right')
|
|
var button = document.createElement("button")
|
|
button.setAttribute('type', 'button')
|
|
button.addEventListener("touchstart", function (e) { self.toggleMap(); e.preventDefault() })
|
|
button.addEventListener("click", function () { self.toggleMap() })
|
|
element.appendChild(button)
|
|
this.panel_ = document.createElement("div")
|
|
this.panel_.classList.add("panel")
|
|
element.appendChild(this.panel_)
|
|
}
|
|
|
|
// Create a overview map
|
|
this.ovmap_ = new ol_Map({
|
|
controls: new ol_Collection(),
|
|
interactions: new ol_Collection(),
|
|
target: this.panel_,
|
|
view: new ol_View({
|
|
zoom: 2,
|
|
center: [0, 0],
|
|
projection: options.projection
|
|
}),
|
|
layers: options.layers
|
|
})
|
|
|
|
this.oview_ = this.ovmap_.getView()
|
|
|
|
// Cache extent
|
|
this.extentLayer = new ol_layer_Vector({
|
|
name: 'Cache extent',
|
|
source: new ol_source_Vector(),
|
|
style: options.style || [new ol_style_Style({
|
|
image: new ol_style_Circle({
|
|
fill: new ol_style_Fill({
|
|
color: 'rgba(255,0,0, 1)'
|
|
}),
|
|
stroke: new ol_style_Stroke({
|
|
width: 7,
|
|
color: 'rgba(255,0,0, 0.8)'
|
|
}),
|
|
radius: 5
|
|
}),
|
|
stroke: new ol_style_Stroke({
|
|
width: 5,
|
|
color: "rgba(255,0,0,0.8)"
|
|
})
|
|
}
|
|
)]
|
|
})
|
|
this.ovmap_.addLayer(this.extentLayer)
|
|
|
|
/** Elastic bounce
|
|
* @param {Int} bounce number of bounce
|
|
* @param {Number} amplitude amplitude of the bounce [0,1]
|
|
* @return {Number}
|
|
* /
|
|
var bounceFn = function (bounce, amplitude){
|
|
var a = (2*bounce+1) * Math.PI/2;
|
|
var b = amplitude>0 ? -1/amplitude : -100;
|
|
var c = - Math.cos(a) * Math.pow(2, b);
|
|
return function(t) {
|
|
t = 1-Math.cos(t*Math.PI/2);
|
|
return 1 + Math.abs( Math.cos(a*t) ) * Math.pow(2, b*t) + c*t;
|
|
}
|
|
}
|
|
|
|
/** Elastic bounce
|
|
* @param {Int} bounce number of bounce
|
|
* @param {Number} amplitude amplitude of the bounce [0,1]
|
|
* @return {Number}
|
|
*/
|
|
var elasticFn = function (bounce, amplitude) {
|
|
var a = 3 * bounce * Math.PI / 2
|
|
var b = amplitude > 0 ? -1 / amplitude : -100
|
|
var c = Math.cos(a) * Math.pow(2, b)
|
|
return function (t) {
|
|
t = 1 - Math.cos(t * Math.PI / 2)
|
|
return 1 - Math.cos(a * t) * Math.pow(2, b * t) + c * t
|
|
}
|
|
}
|
|
|
|
// Click on the preview center the map
|
|
this.ovmap_.addInteraction(new ol_interaction_Pointer({
|
|
handleDownEvent: function (evt) {
|
|
if (options.panAnimation !== false) {
|
|
if (options.panAnimation == "elastic" || options.elasticPan) {
|
|
self.getMap().getView().animate({
|
|
center: evt.coordinate,
|
|
easing: elasticFn(2, 0.3),
|
|
duration: 1000
|
|
})
|
|
} else {
|
|
self.getMap().getView().animate({
|
|
center: evt.coordinate,
|
|
duration: 300
|
|
})
|
|
}
|
|
}
|
|
else
|
|
self.getMap().getView().setCenter(evt.coordinate)
|
|
return false
|
|
}
|
|
}))
|
|
}
|
|
/** Get overview map
|
|
* @return {ol.Map}
|
|
*/
|
|
getOverviewMap() {
|
|
return this.ovmap_
|
|
}
|
|
/** Toggle overview map
|
|
*/
|
|
toggleMap() {
|
|
this.element.classList.toggle("ol-collapsed")
|
|
this.ovmap_.updateSize()
|
|
this.setView()
|
|
}
|
|
/** Set overview map position
|
|
* @param {top|bottom-left|right}
|
|
*/
|
|
setPosition(align) {
|
|
if (/top/.test(align))
|
|
this.element.classList.add("ol-control-top")
|
|
else
|
|
this.element.classList.remove("ol-control-top")
|
|
if (/right/.test(align))
|
|
this.element.classList.add("ol-control-right")
|
|
else
|
|
this.element.classList.remove("ol-control-right")
|
|
}
|
|
/**
|
|
* Set the map instance the control associated with.
|
|
* @param {ol.Map} map The map instance.
|
|
*/
|
|
setMap(map) {
|
|
if (this._listener) {
|
|
for (let i in this._listener) {
|
|
ol_Observable_unByKey(this._listener[i])
|
|
}
|
|
}
|
|
this._listener = {}
|
|
|
|
super.setMap(map)
|
|
|
|
if (map) {
|
|
this._listener.map = map.on('change:view', function () {
|
|
if (this._listener.view)
|
|
ol_Observable_unByKey(this._listener.view)
|
|
if (map.getView()) {
|
|
this._listener.view = map.getView().on('propertychange', this.setView.bind(this))
|
|
this.setView()
|
|
}
|
|
}.bind(this))
|
|
this._listener.view = map.getView().on('propertychange', this.setView.bind(this))
|
|
this.setView()
|
|
}
|
|
|
|
}
|
|
/** Calculate the extent of the map and draw it on the overview
|
|
*/
|
|
calcExtent_(extent) {
|
|
var map = this.getMap()
|
|
if (!map)
|
|
return
|
|
|
|
var source = this.extentLayer.getSource()
|
|
source.clear()
|
|
var f = new ol_Feature()
|
|
|
|
var size = map.getSize()
|
|
var resolution = map.getView().getResolution()
|
|
var rotation = map.getView().getRotation()
|
|
var center = map.getView().getCenter()
|
|
if (!resolution)
|
|
return
|
|
|
|
var dx = resolution * size[0] / 2
|
|
var dy = resolution * size[1] / 2
|
|
var res2 = this.oview_.getResolution()
|
|
if (dx / res2 > 5 || dy / res2 > 5) {
|
|
var cos = Math.cos(rotation)
|
|
var sin = Math.sin(rotation)
|
|
var i, x, y
|
|
extent = [[-dx, -dy], [-dx, dy], [dx, dy], [dx, -dy]]
|
|
for (i = 0; i < 4; ++i) {
|
|
x = extent[i][0]
|
|
y = extent[i][1]
|
|
extent[i][0] = center[0] + x * cos - y * sin
|
|
extent[i][1] = center[1] + x * sin + y * cos
|
|
}
|
|
f.setGeometry(new ol_geom_Polygon([extent]))
|
|
} else {
|
|
f.setGeometry(new ol_geom_Point(center))
|
|
}
|
|
source.addFeature(f)
|
|
}
|
|
/**
|
|
* @private
|
|
*/
|
|
setView(e) {
|
|
if (!e) {
|
|
// refresh all
|
|
this.setView({ key: 'rotation' })
|
|
this.setView({ key: 'resolution' })
|
|
this.setView({ key: 'center' })
|
|
return
|
|
}
|
|
// Set the view params
|
|
switch (e.key) {
|
|
case 'rotation': {
|
|
if (this.rotation)
|
|
this.oview_.setRotation(this.getMap().getView().getRotation())
|
|
else if (this.oview_.getRotation())
|
|
this.oview_.setRotation(0)
|
|
break
|
|
}
|
|
case 'center': {
|
|
var mapExtent = this.getMap().getView().calculateExtent(this.getMap().getSize())
|
|
var extent = this.oview_.calculateExtent(this.ovmap_.getSize())
|
|
if (mapExtent[0] < extent[0] || mapExtent[1] < extent[1]
|
|
|| mapExtent[2] > extent[2] || mapExtent[3] > extent[3]) {
|
|
this.oview_.setCenter(this.getMap().getView().getCenter())
|
|
}
|
|
break
|
|
}
|
|
case 'resolution': {
|
|
//var z = Math.round(this.getMap().getView().getZoom()/2)*2-4;
|
|
var z = Math.round(this.oview_.getZoomForResolution(this.getMap().getView().getResolution()) / 2) * 2 - 4
|
|
z = Math.min(this.maxZoom, Math.max(this.minZoom, z))
|
|
this.oview_.setZoom(z)
|
|
break
|
|
}
|
|
default: break
|
|
}
|
|
this.calcExtent_()
|
|
}
|
|
}
|
|
|
|
export default ol_control_Overview
|