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