packages/leaflet/src/layers/windbarb.js
/*
(c) 2016, Manuel Bär (www.geonet.ch)
Leaflet.WindBarbs, a wind barb plugin for Leaflet.
This plugin enables the automatic creation of
wind barb icons in Leaflet.
version: 0.0.5
*/
import L from 'leaflet'
const WindBarbIcon = L.Icon.extend({
options: {
fillColor: '#2B85C7',
pointRadius: 8,
pointStroke: '#010101',
strokeColor: '#010101',
strokeWidth: 2,
strokeLength: 15,
barbSpaceing: 5,
barbHeight: 15,
forceDir: false
},
initialize: function (options) {
options = L.Util.setOptions(this, options)
},
createIcon: function () {
const div = document.createElement('div')
const svg = this._createPoint()
div.appendChild(svg)
this._setIconStyles(div)
return div
},
_createPoint: function () {
const sc = this.options.pointStroke
const sw = this.options.strokeWidth
const r = this.options.pointRadius
const fc = this.options.fillColor
const w = 2 * sw + 2 * r
const h = w
const xmlns = 'http://www.w3.org/2000/svg'
const svg = document.createElementNS(xmlns, 'svg')
svg.setAttributeNS(null, 'width', w)
svg.setAttributeNS(null, 'height', h)
const c = document.createElementNS(xmlns, 'circle')
c.setAttributeNS(null, 'stroke', sc)
c.setAttributeNS(null, 'stroke-width', sw)
c.setAttributeNS(null, 'fill', fc)
c.setAttributeNS(null, 'cx', w / 2)
c.setAttributeNS(null, 'cy', h / 2)
c.setAttributeNS(null, 'r', r)
svg.appendChild(c)
return svg
},
_createBarbs: function (speed) {
// We expect speed in m/s and not knots
let s = speed / 0.514
const b = { 5: 0, 10: 0, 50: 0 }
const bs = this.options.barbSpaceing
const bh = this.options.barbHeight
const r = this.options.pointRadius
const sc = this.options.strokeColor
const sw = this.options.strokeWidth
const sl = this.options.strokeLength
const fd = this.options.forceDir
const xmlns = 'http://www.w3.org/2000/svg'
const svg = document.createElementNS(xmlns, 'svg')
const g = document.createElementNS(xmlns, 'g')
// Calculate number of each stroke and add path elements to svg group
s = (s % 5) >= 2.5 ? parseInt(s / 5) * 5 + 5 : parseInt(s / 5) * 5
for (let i = s; i > 0;) {
if (i - 50 >= 0) {
b[50] += 1
i -= 50
} else if (i - 10 >= 0) {
b[10] += 1
i -= 10
} else if (i - 5 >= 0) {
b[5] += 1
i -= 5
} else {
break
}
};
// Calculate height and Width of S
const bn = b[5] + b[10] + b[50]
let bw
if (bn === 0) {
bw = 0
} else {
bw = (bn * bs)
if ((b[5] === 1 && b[10] > 0) || (b[5] === 1 && b[50] > 0)) {
bw -= bs
}
if (b[50] > 0) {
bw += 3 * b[50] * bs
}
}
// calculate additional padding needed
const p = Math.round(Math.sqrt(bh * bh + bw * bw)) + 2
// calculate width and height
const w = r * 2 + sw * 2 + 2 * (bw + sl) + 2 * p
const h = w
// vb = h - (r * 2 + sw * 2 + bh)
// calculate center of circle
const cx = w / 2
const cy = h / 2
svg.setAttributeNS(null, 'width', w)
svg.setAttributeNS(null, 'height', h)
svg.appendChild(g)
let px, py, pt, M, H, L, path
if (fd === true) {
// set the x pointer
px = cx - r - sw * 0.5
// set the y pointer
py = cy
// set first M
M = px + ',' + py
// set first h
H = px - sl
// set the top x pointer
pt = H - (2 * bs)
// draw first line
path = document.createElementNS(xmlns, 'path')
path.setAttributeNS(null, 'stroke', sc)
path.setAttributeNS(null, 'stroke-width', sw)
path.setAttributeNS(null, 'stroke-linecap', 'butt')
path.setAttributeNS(null, 'd', 'M ' + M + ' H ' + H)
g.appendChild(path)
// update pointer
px = H
}
if (bn !== 0) {
if (fd !== true) {
// set the x pointer
px = cx - r - sw * 0.5
// set the y pointer
py = cy
// set first M
M = px + ',' + py
// set first h
H = px - sl
// set the top x pointer
pt = H - (2 * bs)
// draw first line
path = document.createElementNS(xmlns, 'path')
path.setAttributeNS(null, 'stroke', sc)
path.setAttributeNS(null, 'stroke-width', sw)
path.setAttributeNS(null, 'stroke-linecap', 'butt')
path.setAttributeNS(null, 'd', 'M ' + M + ' H ' + H)
g.appendChild(path)
// update pointer
px = H
}
// Check if there is a 5kn barb
if (b[5] === 1) {
// calculate length of 10kn barb
const bl10 = Math.sqrt((2 * bs) * (2 * bs) + (bh * bh))
// calculate angle of 10kn barb
const ang10 = Math.atan(bh / (2 * bs))
// calculate length of 5kn barb
const bl5 = bl10 / 2
// get starting point of barb
M = (px) + ',' + py
// calculate x of 5kn barb using angle of 10kn barb
L = ((px) - (bl5 * Math.cos(ang10))) + ',' + (cy - bh * 0.5)
path = document.createElementNS(xmlns, 'path')
path.setAttributeNS(null, 'stroke', sc)
path.setAttributeNS(null, 'stroke-width', sw)
path.setAttributeNS(null, 'stroke-linecap', 'butt')
path.setAttributeNS(null, 'd', 'M ' + M + ' L ' + L)
g.appendChild(path)
// if no other bars exist, draw little end line
if (b[10] === 0 && b[50] === 0) {
px -= (bs)
H = px
path = document.createElementNS(xmlns, 'path')
path.setAttributeNS(null, 'stroke', sc)
path.setAttributeNS(null, 'stroke-width', sw)
path.setAttributeNS(null, 'stroke-linecap', 'butt')
path.setAttributeNS(null, 'd', 'M ' + M + ' H ' + H)
g.appendChild(path)
}
}
for (let i = 0; i < b[10]; i++) {
M = px + ',' + py
px -= bs
H = px
path = document.createElementNS(xmlns, 'path')
path.setAttributeNS(null, 'stroke', sc)
path.setAttributeNS(null, 'stroke-width', sw)
path.setAttributeNS(null, 'stroke-linecap', 'butt')
path.setAttributeNS(null, 'd', 'M ' + M + ' H ' + H)
g.appendChild(path)
M = H + ',' + cy
pt -= bs
L = pt + ',' + (cy - bh)
path = document.createElementNS(xmlns, 'path')
path.setAttributeNS(null, 'stroke', sc)
path.setAttributeNS(null, 'stroke-width', sw)
path.setAttributeNS(null, 'stroke-linecap', 'butt')
path.setAttributeNS(null, 'd', 'M ' + M + ' L ' + L)
g.appendChild(path)
}
if (b[50] > 0) {
M = px + ',' + py
px -= bs
H = px
path = document.createElementNS(xmlns, 'path')
path.setAttributeNS(null, 'stroke', sc)
path.setAttributeNS(null, 'stroke-width', sw)
path.setAttributeNS(null, 'stroke-linecap', 'butt')
path.setAttributeNS(null, 'd', 'M ' + M + ' H ' + H)
g.appendChild(path)
for (let i = 0; i < b[50]; i++) {
pt -= bs
const p1 = px + ',' + cy
const p2 = pt + ',' + (cy - bh)
const p3 = pt + ',' + cy
path = document.createElementNS(xmlns, 'polygon')
path.setAttributeNS(null, 'stroke', sc)
path.setAttributeNS(null, 'stroke-width', sw)
path.setAttributeNS(null, 'fill', '#000000')
path.setAttributeNS(null, 'points', p1 + ' ' + p2 + ' ' + p3)
g.appendChild(path)
px -= 2 * bs
pt -= bs
}
}
}
return { ax: cx, ay: cy, svg: svg }
},
_setIconStyles: function (img, name, a) {
let anchor
const options = this.options
const size = L.point(options[name === 'shadow' ? 'shadowSize' : 'iconSize'])
const sw = this.options.strokeWidth
const r = this.options.pointRadius
if (name === 'shadow') {
anchor = a
img.style.width = anchor.x + 'px'
img.style.height = anchor.y + 'px'
} else {
img.style.position = 'absolute'
const w = 2 * sw + 2 * r
const h = w
const x = w / 2
const y = h / 2
anchor = L.point([x, y])
}
if (!anchor && size) {
anchor = size.divideBy(2, true)
}
if (anchor) {
img.style.marginLeft = (-anchor.x) + 'px'
img.style.marginTop = (-anchor.y) + 'px'
}
if (size) {
img.style.width = size.x + 'px'
img.style.height = size.y + 'px'
}
},
createShadow: function () {
const d = this.options.deg + 90
const s = this.options.speed
const b = this._createBarbs(s)
const div = document.createElement('div')
if (this.options.mirrorVel) {
b.svg.style.transform = 'rotate(' + d + 'deg) scaleY(-1)'
b.svg.style.MozTransform = 'rotate(' + d + 'deg) scaleY(-1)'
b.svg.style.webkitTransform = 'rotate(' + d + 'deg) scaleY(-1)'
b.svg.style.msTransform = 'rotate(' + d + 'deg) scaleY(-1)'
} else {
b.svg.style.transform = 'rotate(' + d + 'deg)'
b.svg.style.MozTransform = 'rotate(' + d + 'deg)'
b.svg.style.webkitTransform = 'rotate(' + d + 'deg)'
b.svg.style.msTransform = 'rotate(' + d + 'deg)'
}
div.appendChild(b.svg)
const anchor = { x: b.ax, y: b.ay }
this._setIconStyles(div, 'shadow', anchor)
return div
}
})
L.WindBarb = {}
L.WindBarb.version = '0.0.5'
L.WindBarb.Icon = WindBarbIcon
L.WindBarb.icon = function (options) {
return new L.WindBarb.Icon(options)
}
export { WindBarbIcon }