19ec21bfdSGreg Roach<?php 2d70512abSGreg Roach 39ec21bfdSGreg Roachuse Fisharebest\Webtrees\I18N; 49ec21bfdSGreg Roachuse Fisharebest\Webtrees\View; 59ec21bfdSGreg Roach 69ec21bfdSGreg Roach/** 7*dc270d8cSGreg Roach * @var array<mixed> $data 8*dc270d8cSGreg Roach * @var array<mixed> $provider 99ec21bfdSGreg Roach */ 109ec21bfdSGreg Roach?> 119ec21bfdSGreg Roach 123dcc812bSGreg Roach<div class="py-4"> 133dcc812bSGreg Roach <div class="row gchart osm-wrapper"> 14a2e602dbSGreg 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> 163dcc812bSGreg Roach </div> 173dcc812bSGreg Roach</div> 183dcc812bSGreg Roach 193dcc812bSGreg Roach<?php View::push('styles') ?> 203dcc812bSGreg Roach<style> 213dcc812bSGreg Roach .osm-wrapper, .osm-user-map { 220d123f04SDavid Drury height: 70vh 233dcc812bSGreg Roach } 243dcc812bSGreg Roach 253dcc812bSGreg Roach .osm-sidebar { 263dcc812bSGreg Roach height: 100%; 273dcc812bSGreg Roach overflow-y: auto; 283dcc812bSGreg Roach font-size: small; 293dcc812bSGreg Roach } 303dcc812bSGreg Roach 313dcc812bSGreg Roach</style> 323dcc812bSGreg Roach<?php View::endpush() ?> 333dcc812bSGreg Roach 343dcc812bSGreg Roach<?php View::push('javascript') ?> 3529eb5762SGreg Roach<script> 3629eb5762SGreg Roach'use strict'; 373dcc812bSGreg Roach 383dcc812bSGreg Roachwindow.WT_OSM = (function () { 3998579324SDavid Drury const minZoom = 2; 403dcc812bSGreg Roach 413dcc812bSGreg Roach let map = null; 423dcc812bSGreg Roach let zoom = null; 430d123f04SDavid Drury let sidebar = $('.osm-sidebar'); 4498579324SDavid Drury let provider = <?= json_encode($provider) ?>; 4598579324SDavid Drury 4698579324SDavid Drury// Map components 473dcc812bSGreg Roach let markers = L.markerClusterGroup({ 4829eb5762SGreg Roach showCoverageOnHover: false, 493dcc812bSGreg Roach }); 503dcc812bSGreg Roach 513dcc812bSGreg Roach let resetControl = L.Control.extend({ 523dcc812bSGreg Roach options: { 5329eb5762SGreg Roach position: 'topleft', 543dcc812bSGreg Roach }, 553dcc812bSGreg Roach onAdd: function (map) { 563dcc812bSGreg Roach let container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom'); 573dcc812bSGreg Roach container.onclick = function () { 583dcc812bSGreg Roach if (zoom) { 593dcc812bSGreg Roach map.flyTo(markers.getBounds().getCenter(), zoom); 603dcc812bSGreg Roach } else { 613dcc812bSGreg Roach map.flyToBounds(markers.getBounds().pad(0.2)); 623dcc812bSGreg Roach } 6329eb5762SGreg Roach sidebar.scrollTo(sidebar.children(':first')); 6436409e74SGreg Roach 653dcc812bSGreg Roach return false; 663dcc812bSGreg Roach }; 6798579324SDavid Drury let reset = <?= json_encode(I18N::translate('Reload map')) ?>; 683dcc812bSGreg Roach let anchor = L.DomUtil.create('a', 'leaflet-control-reset', container); 6998579324SDavid Drury anchor.setAttribute('aria-label', reset); 703dcc812bSGreg Roach anchor.href = '#'; 71a4321966SGreg Roach anchor.title = reset; 723dcc812bSGreg Roach anchor.role = 'button'; 733dcc812bSGreg Roach let image = L.DomUtil.create('i', 'fas fa-redo', anchor); 74a4321966SGreg Roach image.alt = reset; 753dcc812bSGreg Roach 763dcc812bSGreg Roach return container; 7729eb5762SGreg Roach }, 7898579324SDavid Drury }); 7998579324SDavid Drury 8098579324SDavid Drury// Zoom control with localised text 8198579324SDavid Drury let newZoomControl = new L.control.zoom({ 8298579324SDavid Drury zoomInTitle: <?= json_encode(I18N::translate('Zoom in')) ?>, 8398579324SDavid Drury zoomOutTitle: <?= json_encode(I18N::translate('Zoom out')) ?>, 843dcc812bSGreg Roach }); 853dcc812bSGreg Roach 863dcc812bSGreg Roach /** 873dcc812bSGreg Roach * 883dcc812bSGreg Roach * @private 893dcc812bSGreg Roach */ 903dcc812bSGreg Roach let _drawMap = function () { 913dcc812bSGreg Roach map = L.map('osm-map', { 923dcc812bSGreg Roach center: [0, 0], 9398579324SDavid Drury minZoom: minZoom, // maxZoom set by leaflet-providers.js 943dcc812bSGreg Roach zoomControl: false, // remove default 9598579324SDavid Drury }) 9698579324SDavid Drury .addControl(new resetControl()) 9798579324SDavid Drury .addControl(newZoomControl) 982cbb0620SDavid Drury .addLayer(L.tileLayer(provider.url, provider.options)); 993dcc812bSGreg Roach }; 1003dcc812bSGreg Roach 1013dcc812bSGreg Roach /** 1023dcc812bSGreg Roach * @private 1033dcc812bSGreg Roach */ 10498579324SDavid Drury let _buildMapData = function () { 1050d123f04SDavid Drury let sidebar_content = ''; 10698579324SDavid Drury let geoJson_data = <?= json_encode($data) ?>; 1073dcc812bSGreg Roach 10898579324SDavid Drury if (geoJson_data.features.length === 0) { 10998579324SDavid Drury map.fitWorld(); 11098579324SDavid Drury sidebar_content += '<div class="bg-info text-white text-center">' + <?= json_encode(I18N::translate('Nothing to show')) ?> +'</div>'; 11198579324SDavid Drury } else { 11298579324SDavid Drury if (geoJson_data.features.length === 1) { 11398579324SDavid Drury //fudge factor - maps zooms to maximum permitted otherwise 11498579324SDavid Drury zoom = geoJson_data.features[0].properties.zoom; 1153dcc812bSGreg Roach } 11698579324SDavid Drury let geoJsonLayer = L.geoJson(geoJson_data, { 1173dcc812bSGreg Roach pointToLayer: function (feature, latlng) { 1183dcc812bSGreg Roach return new L.Marker(latlng, { 1193dcc812bSGreg Roach icon: L.BeautifyIcon.icon({ 1203130efd4SGreg Roach icon: 'bullseye fas', 1213dcc812bSGreg Roach borderColor: 'transparent', 1223130efd4SGreg Roach backgroundColor: feature.properties.iconcolor, 1233dcc812bSGreg Roach iconShape: 'marker', 1243130efd4SGreg Roach textColor: 'white', 1253dcc812bSGreg Roach }), 1263dcc812bSGreg Roach title: feature.properties.tooltip, 1273dcc812bSGreg Roach alt: feature.properties.tooltip, 12829eb5762SGreg Roach id: feature.id, 1293dcc812bSGreg Roach }) 1303dcc812bSGreg Roach .on('popupopen', function (e) { 13129eb5762SGreg Roach let item = sidebar.children('.gchart[data-id=' + e.target.feature.id + ']'); 1323dcc812bSGreg Roach item.addClass('messagebox'); 1333dcc812bSGreg Roach sidebar.scrollTo(item); 1343dcc812bSGreg Roach }) 1353dcc812bSGreg Roach .on('popupclose', function () { 13629eb5762SGreg Roach sidebar.children('.gchart').removeClass('messagebox'); 1373dcc812bSGreg Roach }); 1383dcc812bSGreg Roach }, 1393dcc812bSGreg Roach onEachFeature: function (feature, layer) { 1403dcc812bSGreg Roach if (feature.properties.polyline) { 1413dcc812bSGreg Roach let pline = L.polyline(feature.properties.polyline.points, feature.properties.polyline.options); 1423dcc812bSGreg Roach markers.addLayer(pline); 1433dcc812bSGreg Roach } 1443dcc812bSGreg Roach layer.bindPopup(feature.properties.summary); 1450d123f04SDavid Drury sidebar_content += `<li class="gchart px-md-2" data-id=${feature.id}>${feature.properties.summary}</li>`; 14629eb5762SGreg Roach }, 1473dcc812bSGreg Roach }); 1483dcc812bSGreg Roach markers.addLayer(geoJsonLayer); 14998579324SDavid Drury map.addLayer(markers); 1503dcc812bSGreg Roach if (zoom) { 1513dcc812bSGreg Roach map.setView(markers.getBounds().getCenter(), zoom); 15298579324SDavid Drury } else { 15345e60b72SGreg Roach map.fitBounds(markers.getBounds(), {padding: [50, 30]}); 1543dcc812bSGreg Roach } 1553dcc812bSGreg Roach } 15698579324SDavid Drury sidebar.append(sidebar_content); 1573dcc812bSGreg Roach }; 1583dcc812bSGreg Roach 1593dcc812bSGreg Roach /** 1603dcc812bSGreg Roach * @param elem 1613dcc812bSGreg Roach * @returns {$} 1623dcc812bSGreg Roach */ 1633dcc812bSGreg Roach $.fn.scrollTo = function (elem) { 1643dcc812bSGreg Roach let _this = $(this); 1653dcc812bSGreg Roach _this.animate({ 16629eb5762SGreg Roach scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(), 1673dcc812bSGreg Roach }); 1683dcc812bSGreg Roach return this; 1693dcc812bSGreg Roach }; 1703dcc812bSGreg Roach 1713dcc812bSGreg Roach// Activate marker popup when sidebar entry clicked 1723dcc812bSGreg Roach $(function () { 1730d123f04SDavid Drury sidebar 1743dcc812bSGreg Roach // open marker popup if sidebar event is clicked 1753dcc812bSGreg Roach .on('click', '.gchart', function (e) { 1763dcc812bSGreg Roach // first close any existing 1773dcc812bSGreg Roach map.closePopup(); 1783dcc812bSGreg Roach let eventId = $(this).data('id'); 1793dcc812bSGreg Roach //find the marker corresponding to the clicked event 1803dcc812bSGreg Roach let mkrLayer = markers.getLayers().filter(function (v) { 1813dcc812bSGreg Roach return typeof (v.feature) !== 'undefined' && v.feature.id === eventId; 1823dcc812bSGreg Roach }); 1833dcc812bSGreg Roach let mkr = mkrLayer.pop(); 1843dcc812bSGreg Roach // Unfortunately zoomToShowLayer zooms to maxZoom 1853dcc812bSGreg Roach // when all marker in a cluster have exactly the 1863dcc812bSGreg Roach // same co-ordinates 1873dcc812bSGreg Roach markers.zoomToShowLayer(mkr, function (e) { 1883dcc812bSGreg Roach mkr.openPopup(); 1893dcc812bSGreg Roach }); 19098579324SDavid Drury 1913dcc812bSGreg Roach return false; 1923dcc812bSGreg Roach }) 1933dcc812bSGreg Roach .on('click', 'a', function (e) { // stop click on a person also opening the popup 1943dcc812bSGreg Roach e.stopPropagation(); 1953dcc812bSGreg Roach }); 1963dcc812bSGreg Roach }); 1973dcc812bSGreg Roach 1983dcc812bSGreg Roach _drawMap(); 19998579324SDavid Drury _buildMapData(); 2003dcc812bSGreg Roach 20129eb5762SGreg Roach return 'Leaflet map interface for webtrees-2'; 2023dcc812bSGreg Roach})(); 2033dcc812bSGreg Roach</script> 2043dcc812bSGreg Roach<?php View::endpush() ?> 205