/*
|
(c) 2016, Manuel Bär
|
Leaflet.idw, a tiny and fast inverse distance weighting plugin for Leaflet.
|
Largely based on the source code of Leaflet.heat by Vladimir Agafonkin (c) 2014
|
https://github.com/Leaflet/Leaflet.heat
|
*/
|
!(function() {
|
'use strict'
|
|
function simpleidw(canvas) {
|
if (!(this instanceof simpleidw)) return new simpleidw(canvas)
|
|
this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas
|
|
this._ctx = canvas.getContext('2d')
|
this._width = canvas.width
|
this._height = canvas.height
|
|
this._max = 1
|
this._data = []
|
}
|
|
simpleidw.prototype = {
|
|
defaultCellSize: 20,
|
|
defaultGradient: {
|
0.0: '#000066',
|
0.1: '#00e400',
|
0.2: '#ffff00',
|
0.3: '#ff7e00',
|
0.4: '#ff0000',
|
0.5: '#99004c',
|
0.6: '#7e0023',
|
0.7: 'Maroon',
|
0.8: '#660066',
|
0.9: '#990099',
|
1.0: '#ff66ff'
|
},
|
|
data: function(data) {
|
this._data = data
|
return this
|
},
|
|
max: function(max) {
|
this._max = max
|
return this
|
},
|
|
add: function(point) {
|
this._data.push(point)
|
return this
|
},
|
|
clear: function() {
|
this._data = []
|
return this
|
},
|
|
cellSize: function(r) {
|
var cell = this._cell = document.createElement('canvas')
|
var ctx = cell.getContext('2d')
|
|
this._r = r
|
|
return this
|
},
|
|
resize: function() {
|
this._width = this._canvas.width
|
this._height = this._canvas.height
|
},
|
|
gradient: function(grad) {
|
// create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one
|
var canvas = document.createElement('canvas')
|
var ctx = canvas.getContext('2d')
|
var gradient = ctx.createLinearGradient(0, 0, 0, 256)
|
|
canvas.width = 1
|
canvas.height = 256
|
|
for (var i in grad) {
|
gradient.addColorStop(+i, grad[i])
|
}
|
|
ctx.fillStyle = gradient
|
ctx.fillRect(0, 0, 1, 256)
|
|
this._grad = ctx.getImageData(0, 0, 1, 256).data
|
|
return this
|
},
|
|
draw: function(opacity) {
|
if (!this._cell) this.cellSize(this.defaultCellSize)
|
if (!this._grad) this.gradient(this.defaultGradient)
|
|
var ctx = this._ctx
|
var grad = this._grad
|
|
ctx.clearRect(0, 0, this._width, this._height)
|
|
for (var i = 0, len = this._data.length, p; i < len; i++) {
|
var p = this._data[i]
|
var j = Math.round((p[2] / this._max) * 255) * 4
|
ctx.fillStyle = 'rgba(' + grad[j] + ',' + grad[j + 1] + ',' + grad[j + 2] + ',' + opacity + ')'
|
ctx.fillRect(p[0] - this._r, p[1] - this._r, this._r, this._r)
|
}
|
|
return this
|
}
|
},
|
window.simpleidw = simpleidw
|
}()),
|
|
L.IdwLayer = (L.Layer ? L.Layer : L.Class).extend({
|
options: {
|
opacity: 0,
|
maxZoom: 18,
|
cellSize: 1,
|
exp: 2,
|
max: 100
|
},
|
initialize: function(latlngs, options) {
|
this._latlngs = latlngs
|
this._bound = options.bound
|
console.log(latlngs)
|
|
L.setOptions(this, options)
|
},
|
|
setLatLngs: function(latlngs) {
|
this._latlngs = latlngs
|
return this.redraw()
|
},
|
|
addLatLng: function(latlng) {
|
this._latlngs.push(latlng)
|
return this.redraw()
|
},
|
|
setOptions: function(options) {
|
L.setOptions(this, options)
|
if (this._idw) {
|
this._updateOptions()
|
}
|
return this.redraw()
|
},
|
|
redraw: function() {
|
if (this._idw && !this._frame && !this._map._animating) {
|
this._frame = L.Util.requestAnimFrame(this._redraw, this)
|
}
|
return this
|
},
|
|
onAdd: function(map) {
|
this._map = map
|
|
if (!this._canvas) {
|
this._initCanvas()
|
}
|
|
map._panes.overlayPane.appendChild(this._canvas)
|
|
map.on('moveend', this._reset, this)
|
|
if (map.options.zoomAnimation && L.Browser.any3d) {
|
map.on('zoomanim', this._animateZoom, this)
|
}
|
|
this._reset()
|
},
|
|
onRemove: function(map) {
|
map.getPanes().overlayPane.removeChild(this._canvas)
|
|
map.off('moveend', this._reset, this)
|
|
if (map.options.zoomAnimation) {
|
map.off('zoomanim', this._animateZoom, this)
|
}
|
},
|
|
addTo: function(map) {
|
map.addLayer(this)
|
return this
|
},
|
|
_initCanvas: function() {
|
var canvas = this._canvas = L.DomUtil.create('canvas', 'leaflet-idwmap-layer leaflet-layer')
|
|
var originProp = L.DomUtil.testProp(['transformOrigin', 'WebkitTransformOrigin', 'msTransformOrigin'])
|
canvas.style[originProp] = '50% 50%'
|
|
var size = this._map.getSize()
|
canvas.width = size.x
|
canvas.height = size.y
|
|
var animated = this._map.options.zoomAnimation && L.Browser.any3d
|
L.DomUtil.addClass(canvas, 'leaflet-zoom-' + (animated ? 'animated' : 'hide'))
|
|
this._idw = simpleidw(canvas)
|
this._updateOptions()
|
},
|
|
_updateOptions: function() {
|
this._idw.cellSize(this.options.cellSize || this._idw.defaultCellSize)
|
|
if (this.options.gradient) {
|
this._idw.gradient(this.options.gradient)
|
}
|
if (this.options.max) {
|
this._idw.max(this.options.max)
|
}
|
},
|
|
_reset: function() {
|
var topLeft = this._map.containerPointToLayerPoint([0, 0])
|
L.DomUtil.setPosition(this._canvas, topLeft)
|
|
var size = this._map.getSize()
|
|
if (this._idw._width !== size.x) {
|
this._canvas.width = this._idw._width = size.x
|
}
|
if (this._idw._height !== size.y) {
|
this._canvas.height = this._idw._height = size.y
|
}
|
|
this._redraw()
|
},
|
|
_redraw: function() {
|
if (!this._map) {
|
return
|
}
|
|
console.log(this._idw._r)
|
|
var data = []
|
var r = this._idw._r
|
var size = this._map.getSize()
|
var bounds = new L.Bounds(
|
L.point([-r, -r]),
|
size.add([r, r]))
|
|
var exp = this.options.exp === undefined ? 1 : this.options.exp
|
var max = this.options.max === undefined ? 1 : this.options.max
|
var maxZoom = this.options.maxZoom === undefined ? this._map.getMaxZoom() : this.options.maxZoom
|
var v = 1
|
var cellCen = r / 2
|
var grid = []
|
var nCellX = Math.ceil((bounds.max.x - bounds.min.x) / r) + 1
|
var nCellY = Math.ceil((bounds.max.y - bounds.min.y) / r) + 1
|
var panePos = this._map._getMapPanePos()
|
var offsetX = 0 // panePos.x % cellSize,
|
var offsetY = 0 // panePos.y % cellSize,
|
var i; var len; var p; var cell; var x; var y; var j; var len2; var k
|
|
console.log('===========================================')
|
|
for (i = 0, len = nCellY; i < len; i++) {
|
// grid[i] = [];
|
for (j = 0, len2 = nCellX; j < len2; j++) {
|
var x = i * r; var y = j * r
|
var numerator = 0; var denominator = 0
|
let len3
|
for (k = 0, len3 = this._latlngs.length; k < len3; k++) {
|
// Get distance between cell and point
|
var p = this._map.latLngToContainerPoint(this._latlngs[k])
|
var cp = L.point((y - cellCen), (x - cellCen))
|
var dist = cp.distanceTo(p)
|
var dist2 = Math.pow(dist, exp)
|
|
var val =
|
this._latlngs[k].alt !== undefined ? this._latlngs[k].alt
|
: this._latlngs[k][2] !== undefined ? +this._latlngs[k][2] : 1
|
|
numerator += val / dist2
|
denominator += 1 / dist2
|
}
|
|
var interpolVal = numerator / denominator
|
|
cell = [j * r, i * r, interpolVal]
|
|
if (cell) {
|
data.push([
|
Math.round(cell[0]),
|
Math.round(cell[1]),
|
Math.min(cell[2], max)
|
])
|
}
|
}
|
}
|
|
this._idw.data(data).draw(this.options.opacity)
|
|
this._pic = this._canvas.toDataURL('image/png')
|
var imageBounds = this._bound
|
if (this._image == undefined) {
|
this._image = L.imageOverlay(this._pic, imageBounds, { opacity: 0.8 }).addTo(this._map)
|
}
|
|
this._frame = null
|
},
|
|
_animateZoom: function(e) {
|
var scale = this._map.getZoomScale(e.zoom)
|
var offset = this._map._getCenterOffset(e.center)._multiplyBy(-scale).subtract(this._map._getMapPanePos())
|
|
if (L.DomUtil.setTransform) {
|
L.DomUtil.setTransform(this._canvas, offset, scale)
|
} else {
|
this._canvas.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(offset) + ' scale(' + scale + ')'
|
}
|
}
|
})
|
|
L.idwLayer = function(latlngs, options) {
|
return new L.IdwLayer(latlngs, options)
|
}
|