aboutsummaryrefslogtreecommitdiff
path: root/frontend/ts/src/pages/map/marker.ts
blob: b6907416dc59adc57c298aad40dbb659f55b5ea4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { mount, h, s } from 'lib/rx'
import * as Color from 'lib/color'
import * as M from 'lib/leaflet'
import * as icons from 'lib/icons'
import * as markerModel from 'models/marker'

interface CreateParams {
    marker: markerModel.Marker,
    onMove: (marker: M.Layer) => void,
    onClick: (markerElem: M.FeatureGroup) => void,
}

export function create({ marker, onMove, onClick }: CreateParams): M.FeatureGroup {
    const { lat, lng, color, icon, name, description, radius } = marker
    const pos = { lat, lng }

    const markerElem = M.marker(pos, {
      draggable: true,
      autoPan: true,
      icon: divIcon({ icon, color, name, description })
    })

    const circle =
      radius !== undefined && radius !== 0
        ? M.circle(pos, { radius, color, fillColor: color })
        : undefined

    const layer =
      circle !== undefined
        ? M.featureGroup([ markerElem, circle ])
        : M.featureGroup([ markerElem ])

    markerElem.addEventListener('drag', e => {
      circle && circle.setLatLng(markerElem.getLatLng())
    })

    markerElem.addEventListener('dragend', () => onMove(markerElem))

    markerElem.addEventListener('click', (e: M.MapEvent) => onClick(layer))

    return layer
}

interface CreateIconParams {
  icon?: string,
  color: string,
  name?: string,
  description?: string
}

function divIcon({ icon, color, name, description }: CreateIconParams): M.Icon {
    const c = Color.parse(color)
    const crBlack = Color.contrastRatio({ red: 0, green: 0, blue: 0 }, c)
    const crWhite = Color.contrastRatio({ red: 255, green: 255, blue: 255 }, c)
    const textCol = crBlack > crWhite ? 'black' : 'white'
    const width = 10
    const height = 15
    const stroke = 'black'
    const strokeWidth = 0.6
    // Triangle
    const t = [
      { x: width * 0.15, y: 7.46 },
      { x: width / 2, y: height },
      { x: width * 0.85, y: 7.46 }
    ]

    const html = document.createElement('div')
    html.className = 'g-Marker'
    if (description) html.title = description

    mount(html, [
        s('svg',
            {
                viewBox: `0 0 ${width} ${height}`,
                class: 'g-Marker__Base'
            },
            s('circle',
                {
                    cx: width / 2,
                    cy: width / 2,
                    r: (width - 2 * strokeWidth) / 2,
                    stroke,
                    'stroke-width': strokeWidth,
                    fill: color
                }
            ),
            s('polygon', { points: `${t[0].x},${t[0].y} ${t[1].x},${t[1].y} ${t[2].x},${t[2].y}`, fill: color }),
            s('line', {
                x1: t[0].x,
                y1: t[0].y,
                x2: t[1].x,
                y2: t[1].y,
                stroke,
                'stroke-width': strokeWidth
            }),
            s('line', {
                x1: t[1].x,
                y1: t[1].y,
                x2: t[2].x,
                y2: t[2].y,
                stroke,
                'stroke-width': strokeWidth
            }),
        ),
        icon && icons.svg(icon, {
            class: 'g-Marker__Icon',
            style: `fill: ${textCol}; stroke: ${textCol}`
        }),
        name && h('div',
            {
                className: 'g-Marker__Title',
                style: `text-shadow: ${textShadow('white', 1, 1)}`
            },
            name
        )
    ])

    return M.divIcon({
        className: '',
        popupAnchor: [ 0, -34 ],
        html
    })
}

function textShadow(color: string, w: number, blurr: number): string {
  return [[-w, -w], [-w, 0], [-w, w], [0, -w], [0, w], [w, -w], [w, 0], [w, w]]
    .map(xs => `${color} ${xs[0]}px ${xs[1]}px ${blurr}px`)
    .join(', ')
}