xref: /webtrees/resources/views/modules/place-hierarchy/map.phtml (revision 728c8c2714cc197d45bad7363eb52e0bff3297bb)
1a634ddebSGreg Roach<?php
2a634ddebSGreg Roach
3a634ddebSGreg Roachuse Fisharebest\Webtrees\View;
4a634ddebSGreg Roach
57c2c99faSGreg Roach/**
67c2c99faSGreg Roach * @var array<mixed> $data
7c9c6f2ecSGreg Roach * @var object       $leaflet_config
87c2c99faSGreg Roach */
97c2c99faSGreg Roach
10a634ddebSGreg Roach?>
11a634ddebSGreg Roach
12a634ddebSGreg Roach<div class="py-4">
130d123f04SDavid Drury    <div class="row gchart osm-wrapper">
14a634ddebSGreg Roach        <div id="osm-map" class="col-sm-9 wt-ajax-load osm-user-map" dir="ltr"></div>
150d123f04SDavid Drury        <ul class="col-sm-3 osm-sidebar wt-page-options-value list-unstyled px-md-1"></ul>
16a634ddebSGreg Roach    </div>
17a634ddebSGreg Roach</div>
18a634ddebSGreg Roach
19a634ddebSGreg Roach<?php View::push('styles') ?>
20a634ddebSGreg Roach<style>
21a634ddebSGreg Roach    .osm-wrapper, .osm-user-map {
220d123f04SDavid Drury        height: 70vh
23a634ddebSGreg Roach    }
24a634ddebSGreg Roach
25a634ddebSGreg Roach    .osm-sidebar {
26a634ddebSGreg Roach        height: 100%;
270d123f04SDavid Drury        overflow-x: hidden;
28a634ddebSGreg Roach        overflow-y: auto;
29a634ddebSGreg Roach        font-size: small;
30a634ddebSGreg Roach    }
31a634ddebSGreg Roach
32a634ddebSGreg Roach    .flag {
33a634ddebSGreg Roach        border: 1px solid grey !important;
34a634ddebSGreg Roach        height: 15px;
35a634ddebSGreg Roach        width: 25px;
36a634ddebSGreg Roach    }
37a634ddebSGreg Roach</style>
38a634ddebSGreg Roach<?php View::endpush() ?>
39a634ddebSGreg Roach
40a634ddebSGreg Roach<?php View::push('javascript') ?>
4174b9ba3fSGreg Roach<script>
42c68bbde3SGreg Roach  'use strict';
43a634ddebSGreg Roach
44a634ddebSGreg Roach  window.WT_OSM = (function () {
45*728c8c27SGreg Roach    const config = <?= json_encode($leaflet_config, JSON_THROW_ON_ERROR) ?>;
46a634ddebSGreg Roach
47a634ddebSGreg Roach    let map = null;
48ab039d30SDavid Drury    let sidebar = $('.osm-sidebar');
49ab039d30SDavid Drury
50ab039d30SDavid Drury    // Map components
51a634ddebSGreg Roach    let markers = L.markerClusterGroup({
52a634ddebSGreg Roach      showCoverageOnHover: false,
53a634ddebSGreg Roach    });
54a634ddebSGreg Roach
55a634ddebSGreg Roach    let resetControl = L.Control.extend({
56a634ddebSGreg Roach      options: {
57c68bbde3SGreg Roach        position: 'topleft',
58a634ddebSGreg Roach      },
59a634ddebSGreg Roach
60a634ddebSGreg Roach      onAdd: function (map) {
61c68bbde3SGreg Roach        let container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom');
62a634ddebSGreg Roach        container.onclick = function () {
63c68bbde3SGreg Roach          map.flyToBounds(markers.getBounds(), { padding: [50, 30], maxZoom: 15 });
64c68bbde3SGreg Roach          sidebar.scrollTo(sidebar.children(':first'));
65a634ddebSGreg Roach          return false;
66a634ddebSGreg Roach        };
67c68bbde3SGreg Roach        let anchor = L.DomUtil.create('a', 'leaflet-control-reset', container);
68c9c6f2ecSGreg Roach        let reset = config.i18n.reset;
69ab039d30SDavid Drury        anchor.setAttribute('aria-label', reset);
70c68bbde3SGreg Roach        anchor.href = '#';
71a634ddebSGreg Roach        anchor.title = reset;
72c68bbde3SGreg Roach        anchor.role = 'button';
73c68bbde3SGreg Roach        let image = L.DomUtil.create('i', 'fas fa-redo', anchor);
74a634ddebSGreg Roach        image.alt = reset;
75a634ddebSGreg Roach
76a634ddebSGreg Roach        return container;
77a634ddebSGreg Roach      },
78a634ddebSGreg Roach    });
79a634ddebSGreg Roach
80ab039d30SDavid Drury    /**
81ab039d30SDavid Drury     * @private
82ab039d30SDavid Drury     */
83ab039d30SDavid Drury    let _drawMap = function () {
84c9c6f2ecSGreg Roach      map = webtrees.buildLeafletJsMap('osm-map', config)
85c9c6f2ecSGreg Roach        .addControl(new resetControl());
86a634ddebSGreg Roach    };
87a634ddebSGreg Roach
88ab039d30SDavid Drury    /**
89ab039d30SDavid Drury     * @private
90ab039d30SDavid Drury     */
91ab039d30SDavid Drury    let _buildMapData = function () {
92*728c8c27SGreg Roach      let data = <?= json_encode($data['markers'], JSON_THROW_ON_ERROR) ?>;
93a634ddebSGreg Roach
94ab039d30SDavid Drury      let geoJsonLayer = L.geoJson(data, {
95a634ddebSGreg Roach        pointToLayer: function (feature, latlng) {
96a634ddebSGreg Roach          return new L.Marker(latlng, {
97a634ddebSGreg Roach            icon: L.BeautifyIcon.icon({
98a634ddebSGreg Roach              icon: 'bullseye fas',
99c68bbde3SGreg Roach              borderColor: 'transparent',
100a634ddebSGreg Roach              backgroundColor: '#1e90ff',
101c68bbde3SGreg Roach              iconShape: 'marker',
102c68bbde3SGreg Roach              textColor: 'white',
103a634ddebSGreg Roach            }),
104a634ddebSGreg Roach            title: feature.properties.tooltip,
105a634ddebSGreg Roach            alt: feature.properties.tooltip,
106a634ddebSGreg Roach            id: feature.id,
107a634ddebSGreg Roach          })
108c68bbde3SGreg Roach            .on('popupopen', function (e) {
109d4786c66SGreg Roach              let item = sidebar.children('.mapped[data-wt-feature-id=' + e.target.feature.id + ']');
110c68bbde3SGreg Roach              item.addClass('messagebox');
111a634ddebSGreg Roach              sidebar.scrollTo(item);
112a634ddebSGreg Roach            })
113c68bbde3SGreg Roach            .on('popupclose', function () {
114c68bbde3SGreg Roach              sidebar.children('.mapped')
115c68bbde3SGreg Roach                .removeClass('messagebox');
116a634ddebSGreg Roach            });
117a634ddebSGreg Roach        },
118a634ddebSGreg Roach        onEachFeature: function (feature, layer) {
119a634ddebSGreg Roach          layer.bindPopup(feature.properties.popup);
120a634ddebSGreg Roach        },
121a634ddebSGreg Roach      });
122a634ddebSGreg Roach
123a634ddebSGreg Roach      if (data.features.length > 0) {
124a634ddebSGreg Roach        markers.addLayer(geoJsonLayer);
125c68bbde3SGreg Roach        map.addLayer(markers);
12679de923dSGreg Roach      }
127ab039d30SDavid Drury
128*728c8c27SGreg Roach      map.fitBounds(<?= json_encode($data['bounds'], JSON_THROW_ON_ERROR) ?>, { padding: [50, 30] });
129*728c8c27SGreg Roach      sidebar.append(<?= json_encode($data['sidebar'], JSON_THROW_ON_ERROR) ?>);
130a634ddebSGreg Roach    };
131a634ddebSGreg Roach
132a634ddebSGreg Roach    /**
133a634ddebSGreg Roach     * @param   elem
134a634ddebSGreg Roach     * @returns {$}
135a634ddebSGreg Roach     */
136a634ddebSGreg Roach    $.fn.scrollTo = function (elem) {
137a634ddebSGreg Roach      let _this = $(this);
138a634ddebSGreg Roach      _this.animate({
139a634ddebSGreg Roach        scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(),
140a634ddebSGreg Roach      });
141a634ddebSGreg Roach      return this;
142a634ddebSGreg Roach    };
143a634ddebSGreg Roach
144a634ddebSGreg Roach    // Activate marker popup when sidebar entry clicked
145a634ddebSGreg Roach    $(function () {
1460d123f04SDavid Drury      sidebar
147a634ddebSGreg Roach        // open marker popup if sidebar event is clicked
148c68bbde3SGreg Roach        .on('click', '.mapped', function (e) {
149a634ddebSGreg Roach          // first close any existing
150a634ddebSGreg Roach          map.closePopup();
151c68bbde3SGreg Roach          let eventId = $(this).data('id');
152a634ddebSGreg Roach          //find the marker corresponding to the clicked event
153a634ddebSGreg Roach          let mkrLayer = markers.getLayers().filter(function (v) {
154c68bbde3SGreg Roach            return typeof (v.feature) !== 'undefined' && v.feature.id === eventId;
155a634ddebSGreg Roach          });
156a634ddebSGreg Roach          let mkr = mkrLayer.pop();
157a634ddebSGreg Roach          // Unfortunately zoomToShowLayer zooms to maxZoom
158a634ddebSGreg Roach          // when all marker in a cluster have exactly the
159a634ddebSGreg Roach          // same co-ordinates
160a634ddebSGreg Roach          markers.zoomToShowLayer(mkr, function (e) {
161a634ddebSGreg Roach            mkr.openPopup();
162a634ddebSGreg Roach          });
163a634ddebSGreg Roach          return false;
164a634ddebSGreg Roach        })
165c68bbde3SGreg Roach        .on('click', 'a', function (e) { // stop click on a person also opening the popup
166a634ddebSGreg Roach          e.stopPropagation();
167a634ddebSGreg Roach        });
168a634ddebSGreg Roach    });
169a634ddebSGreg Roach
170a634ddebSGreg Roach    _drawMap();
171ab039d30SDavid Drury    _buildMapData();
172a634ddebSGreg Roach
173c68bbde3SGreg Roach    return 'Leaflet map interface for webtrees-2';
174a634ddebSGreg Roach  })();
175a634ddebSGreg Roach</script>
176a634ddebSGreg Roach<?php View::endpush() ?>
177