xref: /webtrees/resources/views/modules/places/tab.phtml (revision 45e60b72067c70760407f38ccbfe30c9715e7e45)
1d70512abSGreg Roach<?php
2d70512abSGreg Roach
3d70512abSGreg Roachuse Fisharebest\Webtrees\I18N;
4d70512abSGreg Roach
5d70512abSGreg Roach?>
6dd6b2bfcSGreg Roach
7dd6b2bfcSGreg Roach<div class="py-4">
8dd6b2bfcSGreg Roach    <div class="row gchart osm-wrapper">
9d21f0b10SGreg Roach        <div id="osm-map" class="col-sm-9 wt-ajax-load osm-user-map" dir="ltr"></div>
100d123f04SDavid Drury        <ul class="col-sm-3 osm-sidebar wt-page-options-value list-unstyled px-md-1"></ul>
11dd6b2bfcSGreg Roach    </div>
12dd6b2bfcSGreg Roach</div>
13dd6b2bfcSGreg Roach
14dd6b2bfcSGreg Roach<style>
15dd6b2bfcSGreg Roach    .osm-wrapper, .osm-user-map {
160d123f04SDavid Drury        height: 70vh
17dd6b2bfcSGreg Roach    }
18dd6b2bfcSGreg Roach
19dd6b2bfcSGreg Roach    .osm-sidebar {
20dd6b2bfcSGreg Roach        height: 100%;
21dd6b2bfcSGreg Roach        overflow-y: auto;
22dd6b2bfcSGreg Roach        font-size: small;
23dd6b2bfcSGreg Roach    }
24dd6b2bfcSGreg Roach</style>
25dd6b2bfcSGreg Roach
2674b9ba3fSGreg Roach<script>
27dd6b2bfcSGreg Roach    "use strict";
28dd6b2bfcSGreg Roach
29dd6b2bfcSGreg Roach    window.WT_OSM = (function() {
30597fb44bSDavid Drury        const minZoom = 2;
31dd6b2bfcSGreg Roach
32dd6b2bfcSGreg Roach        let map       = null;
33dd6b2bfcSGreg Roach        let zoom      = null;
34597fb44bSDavid Drury        let sidebar   = $('.osm-sidebar');
35597fb44bSDavid Drury        let provider  = <?=  json_encode($provider) ?>;
36597fb44bSDavid Drury
37597fb44bSDavid Drury        // Map components
38dd6b2bfcSGreg Roach        let markers = L.markerClusterGroup({
39dd6b2bfcSGreg Roach            showCoverageOnHover: false,
40dd6b2bfcSGreg Roach        });
41dd6b2bfcSGreg Roach
42dd6b2bfcSGreg Roach        let resetControl = L.Control.extend({
43dd6b2bfcSGreg Roach            options: {
44dd6b2bfcSGreg Roach                position: "topleft",
45dd6b2bfcSGreg Roach            },
46dd6b2bfcSGreg Roach            onAdd: function(map) {
47dd6b2bfcSGreg Roach                let container = L.DomUtil.create("div", "leaflet-bar leaflet-control leaflet-control-custom");
48dd6b2bfcSGreg Roach                container.onclick = function() {
49dd6b2bfcSGreg Roach                    if (zoom) {
50dd6b2bfcSGreg Roach                        map.flyTo(markers.getBounds().getCenter(), zoom);
51dd6b2bfcSGreg Roach                    } else {
52dd6b2bfcSGreg Roach                        map.flyToBounds(markers.getBounds().pad(0.2));
53dd6b2bfcSGreg Roach                    }
544125a3e1SGreg Roach                    sidebar.scrollTo(sidebar.children(":first"));
554125a3e1SGreg Roach
56dd6b2bfcSGreg Roach                    return false;
57dd6b2bfcSGreg Roach                };
58597fb44bSDavid Drury                let reset    = <?= json_encode(I18N::translate('Reload map')) ?>;
59597fb44bSDavid Drury                let anchor   = L.DomUtil.create('a', 'leaflet-control-reset', container);
60597fb44bSDavid Drury                anchor.setAttribute('aria-label', reset);
61597fb44bSDavid Drury                anchor.href  = '#';
6257862dd5SDavid Drury                anchor.title = reset;
63597fb44bSDavid Drury                anchor.role  = 'button';
64597fb44bSDavid Drury                let image    = L.DomUtil.create('i', 'fas fa-redo', anchor);
6557862dd5SDavid Drury                image.alt    = reset;
66dd6b2bfcSGreg Roach
67dd6b2bfcSGreg Roach                return container;
68dd6b2bfcSGreg Roach            },
69dd6b2bfcSGreg Roach        });
70dd6b2bfcSGreg Roach
71597fb44bSDavid Drury        // Zoom control with localised text
72597fb44bSDavid Drury        let newZoomControl = new L.control.zoom({
73597fb44bSDavid Drury            zoomInTitle : <?= json_encode(I18N::translate('Zoom in')) ?>,
74597fb44bSDavid Drury            zoomOutTitle: <?= json_encode(I18N::translate('Zoom out')) ?>,
75597fb44bSDavid Drury        });
76597fb44bSDavid Drury
77dd6b2bfcSGreg Roach        /**
78dd6b2bfcSGreg Roach         *
79dd6b2bfcSGreg Roach         * @private
80dd6b2bfcSGreg Roach         */
81dd6b2bfcSGreg Roach        let _drawMap = function() {
82597fb44bSDavid Drury            map = L.map('osm-map', {
83dd6b2bfcSGreg Roach                    center     : [0, 0],
84597fb44bSDavid Drury                    minZoom    : minZoom, // maxZoom set by leaflet-providers.js
85dd6b2bfcSGreg Roach                    zoomControl: false, // remove default
86597fb44bSDavid Drury                })
87597fb44bSDavid Drury                .addControl(new resetControl())
88597fb44bSDavid Drury                .addControl(newZoomControl)
892cbb0620SDavid Drury                .addLayer(L.tileLayer(provider.url, provider.options));
90dd6b2bfcSGreg Roach        };
91dd6b2bfcSGreg Roach
92597fb44bSDavid Drury        /**
93597fb44bSDavid Drury         *
94597fb44bSDavid Drury         * @private
95597fb44bSDavid Drury         */
96597fb44bSDavid Drury        let _buildMapData = function() {
970d123f04SDavid Drury            let sidebar_content = "";
98dd6b2bfcSGreg Roach            let data            = <?= json_encode($data) ?>;
99dd6b2bfcSGreg Roach
100597fb44bSDavid Drury            if (data.features.length === 0) {
101597fb44bSDavid Drury                map.fitWorld();
102597fb44bSDavid Drury                sidebar_content += '<div class="bg-info text-white text-center">' + <?= json_encode(I18N::translate('Nothing to show')) ?> + '</div>';
103597fb44bSDavid Drury            } else {
104597fb44bSDavid Drury                if (data.features.length === 1) {
105597fb44bSDavid Drury                    //fudge factor - maps zooms to maximum permitted otherwise
106ef9d8accSGreg Roach                    zoom = data.features[0].properties.zoom;
107597fb44bSDavid Drury                }
108597fb44bSDavid Drury                let geoJsonLayer = L.geoJson(data, {
109dd6b2bfcSGreg Roach                    pointToLayer: function(feature, latlng) {
110dd6b2bfcSGreg Roach                        return new L.Marker(latlng, {
111dd6b2bfcSGreg Roach                            icon: L.BeautifyIcon.icon({
112dd6b2bfcSGreg Roach                                icon           : feature.properties.icon["name"],
113dd6b2bfcSGreg Roach                                borderColor    : "transparent",
11480993423SGreg Roach                                backgroundColor: feature.properties.icon["color"],
115dd6b2bfcSGreg Roach                                iconShape      : "marker",
11680993423SGreg Roach                                textColor      : "white",
117dd6b2bfcSGreg Roach                            }),
118dd6b2bfcSGreg Roach                            title: feature.properties.tooltip,
119dd6b2bfcSGreg Roach                            alt  : feature.properties.tooltip,
120dd6b2bfcSGreg Roach                            id   : feature.id,
121dd6b2bfcSGreg Roach                        })
122dd6b2bfcSGreg Roach                        .on("popupopen", function(e) {
123dd6b2bfcSGreg Roach                            let item = sidebar.children(".gchart[data-id=" + e.target.feature.id + "]");
124dd6b2bfcSGreg Roach                            item.addClass("messagebox");
125dd6b2bfcSGreg Roach                            sidebar.scrollTo(item);
126dd6b2bfcSGreg Roach                        })
127dd6b2bfcSGreg Roach                        .on("popupclose", function() {
1280d123f04SDavid Drury                            sidebar.children(".gchart")
129dd6b2bfcSGreg Roach                                .removeClass("messagebox");
130dd6b2bfcSGreg Roach                        });
131dd6b2bfcSGreg Roach                    },
132dd6b2bfcSGreg Roach                    onEachFeature: function(feature, layer) {
133dd6b2bfcSGreg Roach                        layer.bindPopup(feature.properties.summary);
1340d123f04SDavid Drury                        sidebar_content += `<li class="gchart px-md-2" data-id=${feature.id}>${feature.properties.summary}</li>`;
135dd6b2bfcSGreg Roach                    },
136dd6b2bfcSGreg Roach                });
137dd6b2bfcSGreg Roach                markers.addLayer(geoJsonLayer);
138597fb44bSDavid Drury                map.addLayer(markers);
139597fb44bSDavid Drury                if (zoom) {
140597fb44bSDavid Drury                    map.setView(markers.getBounds().getCenter(), zoom);
141dd6b2bfcSGreg Roach                } else {
142*45e60b72SGreg Roach                    map.fitBounds(markers.getBounds(), {padding: [50, 30]});
143dd6b2bfcSGreg Roach                }
144dd6b2bfcSGreg Roach            }
145597fb44bSDavid Drury            sidebar.append(sidebar_content);
146dd6b2bfcSGreg Roach        };
147dd6b2bfcSGreg Roach
148dd6b2bfcSGreg Roach        /**
149dd6b2bfcSGreg Roach         * @param   elem
150dd6b2bfcSGreg Roach         * @returns {$}
151dd6b2bfcSGreg Roach         */
152dd6b2bfcSGreg Roach        $.fn.scrollTo = function(elem) {
153dd6b2bfcSGreg Roach            let _this = $(this);
154dd6b2bfcSGreg Roach            _this.animate({
155dd6b2bfcSGreg Roach                scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(),
156dd6b2bfcSGreg Roach            });
157dd6b2bfcSGreg Roach            return this;
158dd6b2bfcSGreg Roach        };
159dd6b2bfcSGreg Roach
160dd6b2bfcSGreg Roach        // Activate marker popup when sidebar entry clicked
161597fb44bSDavid Drury        $(function() {
1620d123f04SDavid Drury            sidebar
163597fb44bSDavid Drury            // open marker popup if sidebar event is clicked
164597fb44bSDavid Drury            .on('click', '.gchart', function(e) {
165dd6b2bfcSGreg Roach                // first close any existing
166dd6b2bfcSGreg Roach                map.closePopup();
167597fb44bSDavid Drury                let eventId = $(this).data('id');
168dd6b2bfcSGreg Roach                //find the marker corresponding to the clicked event
169dd6b2bfcSGreg Roach                let mkrLayer = markers.getLayers().filter(function(v) {
170597fb44bSDavid Drury                    return typeof(v.feature) !== 'undefined' && v.feature.id === eventId;
171dd6b2bfcSGreg Roach                });
172dd6b2bfcSGreg Roach                let mkr = mkrLayer.pop();
173dd6b2bfcSGreg Roach                // Unfortunately zoomToShowLayer zooms to maxZoom
174dd6b2bfcSGreg Roach                // when all marker in a cluster have exactly the
175dd6b2bfcSGreg Roach                // same co-ordinates
176dd6b2bfcSGreg Roach                markers.zoomToShowLayer(mkr, function(e) {
177dd6b2bfcSGreg Roach                    mkr.openPopup();
178dd6b2bfcSGreg Roach                });
179597fb44bSDavid Drury
180dd6b2bfcSGreg Roach                return false;
181dd6b2bfcSGreg Roach            })
182597fb44bSDavid Drury            .on('click', 'a', function(e) { // stop click on a person also opening the popup
183dd6b2bfcSGreg Roach                e.stopPropagation();
184dd6b2bfcSGreg Roach            });
185597fb44bSDavid Drury        });
186dd6b2bfcSGreg Roach
187597fb44bSDavid Drury        _drawMap();
188597fb44bSDavid Drury        _buildMapData();
189597fb44bSDavid Drury
190597fb44bSDavid Drury        return "Leaflet map interface for webtrees-2";
191597fb44bSDavid Drury    })();
192dd6b2bfcSGreg Roach</script>
193