aboutsummaryrefslogtreecommitdiff
path: root/src/View/Map.ml
blob: 8f74b769fb218eca48b7aa659b5ac2ffc6f2d4f0 (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
let mapView state =
  H.div
    [| HA.class_ "g-Layout__Page" |]
    [| H.div
        [| HA.class_ "g-Layout__Header" |]
        [| H.a
            [| HA.class_ "g-Layout__Home"
            ;  HA.href "#"
            |]
            [| H.text "Map" |]
        ;  Button.text
            [| HE.on_click (fun _ -> File.download "map.csv" (State.to_csv_string !state)) |]
            [| H.text "Export" |]
        |]
    ; H.div
        [| HA.class_ "g-Map" |]
        [| H.div
            [| HA.id "g-Map__Content" |]
            [||]
        |]
    |]

let state_from_hash () =
  let hash = Js.String.sliceToEnd ~from:1 (Location.hash Document.location) in
  State.from_url_string hash

let installMap state =
  let map = Leaflet.map "g-Map__Content" in
  let title_layer = Leaflet.title_layer "http://{s}.tile.osm.org/{z}/{x}/{y}.png" in
  let markers = Leaflet.feature_group [| |] in
  let () = Leaflet.add_layer map markers in
  let () = Leaflet.add_layer map title_layer in

  let rec reload_from_hash focus =
    let update_state new_state =
      let () = History.push_state "" "" ("#" ^ State.to_url_string new_state) () in
      reload_from_hash false
    in

    let on_remove pos =
      update_state (State.remove !state pos) in

    let on_update previousPos pos name color icon =
      update_state (State.update !state previousPos { pos = pos; name = name; color = color; icon = icon }) in

    let () =
      if Js.Array.length (Leaflet.get_layers markers ()) > 0 then
        Leaflet.clear_layers markers
      else
        ()
    in
    let () = state := state_from_hash () in
    let colors = State.colors !state in
    let () =
      Js.Array.forEach
        (fun (m: State.marker_state) -> Leaflet.add_layer markers (Marker.create on_remove on_update colors m.pos m.name m.color m.icon))
        !state
    in
    if focus then
      if Js.Array.length (Leaflet.get_layers markers ()) > 0 then
        Leaflet.fit_bounds map (Leaflet.get_bounds markers ()) { padding = [| 50.; 50. |] }
      else
        Leaflet.setView map [| 51.505; -0.09 |] 2
    else
      ()
  in

  (* Init markers from url *)
  let () = reload_from_hash true in

  (* Reload the map if the URL changes *)
  let () = Element.add_event_listener Window.window "popstate" (fun _ ->
    reload_from_hash true)
  in

  let add_marker pos name color icon =
    let new_marker = { State.pos = pos; name = name; color = color; icon = icon } in
    let new_state = State.update !state pos new_marker in
    let () = History.push_state "" "" ("#" ^ State.to_url_string new_state) () in
    reload_from_hash false
  in

  (* Context menu *)
  Leaflet.on map "contextmenu" (fun event ->
    ContextMenu.show
      (Leaflet.original_event event)
      [| { label = "Add a marker"
         ; action = (fun _ ->
             let pos = Leaflet.lat_lng event in
             let marker =
               match State.last_added !state with
               | Some m -> { m with pos = pos; name = "" }
               | _ -> { pos = pos; name = ""; color = "#3f92cf"; icon = "" }
             in
            let colors = State.colors !state in
             Modal.show (Marker.form (add_marker pos) colors marker.name marker.color marker.icon))
         }
      |])

let render () =
  let state = ref (state_from_hash ()) in
  let _ = Js.Global.setTimeout (fun _ -> installMap state) 0 in
  mapView state