weacast/weacast

View on GitHub
packages/leaflet/src/layers/windbarb.js

Summary

Maintainability
C
7 hrs
Test Coverage
F
0%
/*
 (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 }