19ec21bfdSGreg Roach<?php 2d70512abSGreg Roach 39ec21bfdSGreg Roachuse Fisharebest\Webtrees\I18N; 49ec21bfdSGreg Roachuse Fisharebest\Webtrees\View; 59ec21bfdSGreg Roach 69ec21bfdSGreg Roach/** 7*98579324SDavid Drury * @var array $data 8*98579324SDavid Drury * @var array $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') ?> 353dcc812bSGreg Roach<script type="application/javascript"> 363dcc812bSGreg Roach"use strict"; 373dcc812bSGreg Roach 383dcc812bSGreg Roachwindow.WT_OSM = (function() { 39*98579324SDavid Drury const minZoom = 2; 403dcc812bSGreg Roach 413dcc812bSGreg Roach let map = null; 423dcc812bSGreg Roach let zoom = null; 430d123f04SDavid Drury let sidebar = $('.osm-sidebar'); 44*98579324SDavid Drury let provider = <?= json_encode($provider) ?>; 45*98579324SDavid Drury 46*98579324SDavid Drury // Map components 473dcc812bSGreg Roach let markers = L.markerClusterGroup({ 483dcc812bSGreg Roach showCoverageOnHover: false 493dcc812bSGreg Roach }); 503dcc812bSGreg Roach 513dcc812bSGreg Roach let resetControl = L.Control.extend({ 523dcc812bSGreg Roach options: { 533dcc812bSGreg 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 } 6336409e74SGreg Roach sidebar.scrollTo(sidebar.children(":first")); 6436409e74SGreg Roach 653dcc812bSGreg Roach return false; 663dcc812bSGreg Roach }; 67*98579324SDavid Drury let reset = <?= json_encode(I18N::translate('Reload map')) ?>; 683dcc812bSGreg Roach let anchor = L.DomUtil.create('a', 'leaflet-control-reset', container); 69*98579324SDavid 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; 77*98579324SDavid Drury } 78*98579324SDavid Drury }); 79*98579324SDavid Drury 80*98579324SDavid Drury // Zoom control with localised text 81*98579324SDavid Drury let newZoomControl = new L.control.zoom({ 82*98579324SDavid Drury zoomInTitle : <?= json_encode(I18N::translate('Zoom in')) ?>, 83*98579324SDavid 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], 93*98579324SDavid Drury minZoom : minZoom, // maxZoom set by leaflet-providers.js 943dcc812bSGreg Roach zoomControl: false, // remove default 95*98579324SDavid Drury }) 96*98579324SDavid Drury .addControl(new resetControl()) 97*98579324SDavid Drury .addControl(newZoomControl) 98*98579324SDavid Drury .addLayer(L.tileLayer.provider(provider.name, provider.options)) 993dcc812bSGreg Roach }; 1003dcc812bSGreg Roach 1013dcc812bSGreg Roach /** 1023dcc812bSGreg Roach * @private 1033dcc812bSGreg Roach */ 104*98579324SDavid Drury let _buildMapData = function() { 1050d123f04SDavid Drury let sidebar_content = ''; 106*98579324SDavid Drury let geoJson_data = <?= json_encode($data) ?>; 1073dcc812bSGreg Roach 108*98579324SDavid Drury if (geoJson_data.features.length === 0) { 109*98579324SDavid Drury map.fitWorld(); 110*98579324SDavid Drury sidebar_content += '<div class="bg-info text-white text-center">' + <?= json_encode(I18N::translate('Nothing to show')) ?> + '</div>'; 111*98579324SDavid Drury } else { 112*98579324SDavid Drury if (geoJson_data.features.length === 1) { 113*98579324SDavid Drury //fudge factor - maps zooms to maximum permitted otherwise 114*98579324SDavid Drury zoom = geoJson_data.features[0].properties.zoom; 1153dcc812bSGreg Roach } 116*98579324SDavid 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, 1283dcc812bSGreg Roach id : feature.id 1293dcc812bSGreg Roach }) 1303dcc812bSGreg Roach .on('popupopen', function(e) { 1313dcc812bSGreg 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() { 136*98579324SDavid Drury 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>`; 146*98579324SDavid Drury } 1473dcc812bSGreg Roach }); 1483dcc812bSGreg Roach markers.addLayer(geoJsonLayer); 149*98579324SDavid Drury map.addLayer(markers); 1503dcc812bSGreg Roach if (zoom) { 1513dcc812bSGreg Roach map.setView(markers.getBounds().getCenter(), zoom); 152*98579324SDavid Drury } else { 153*98579324SDavid Drury map.fitBounds(markers.getBounds().pad(0.2)); 1543dcc812bSGreg Roach } 1553dcc812bSGreg Roach } 156*98579324SDavid Drury sidebar.append(sidebar_content); 1573dcc812bSGreg Roach }; 1583dcc812bSGreg Roach 1593dcc812bSGreg Roach /** 1603dcc812bSGreg Roach * 1613dcc812bSGreg Roach * @param elem 1623dcc812bSGreg Roach * @returns {$} 1633dcc812bSGreg Roach */ 1643dcc812bSGreg Roach 1653dcc812bSGreg Roach $.fn.scrollTo = function(elem) { 1663dcc812bSGreg Roach let _this = $(this); 1673dcc812bSGreg Roach _this.animate({ 1683dcc812bSGreg Roach scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop() 1693dcc812bSGreg Roach }); 1703dcc812bSGreg Roach return this; 1713dcc812bSGreg Roach }; 1723dcc812bSGreg Roach 1733dcc812bSGreg Roach // Activate marker popup when sidebar entry clicked 1743dcc812bSGreg Roach $(function() { 1750d123f04SDavid Drury sidebar 1763dcc812bSGreg Roach // open marker popup if sidebar event is clicked 1773dcc812bSGreg Roach .on('click', '.gchart', function(e) { 1783dcc812bSGreg Roach // first close any existing 1793dcc812bSGreg Roach map.closePopup(); 1803dcc812bSGreg Roach let eventId = $(this).data('id'); 1813dcc812bSGreg Roach //find the marker corresponding to the clicked event 1823dcc812bSGreg Roach let mkrLayer = markers.getLayers().filter(function(v) { 1833dcc812bSGreg Roach return typeof(v.feature) !== 'undefined' && v.feature.id === eventId; 1843dcc812bSGreg Roach }); 1853dcc812bSGreg Roach let mkr = mkrLayer.pop(); 1863dcc812bSGreg Roach // Unfortunately zoomToShowLayer zooms to maxZoom 1873dcc812bSGreg Roach // when all marker in a cluster have exactly the 1883dcc812bSGreg Roach // same co-ordinates 1893dcc812bSGreg Roach markers.zoomToShowLayer(mkr, function(e) { 1903dcc812bSGreg Roach mkr.openPopup(); 1913dcc812bSGreg Roach }); 192*98579324SDavid Drury 1933dcc812bSGreg Roach return false; 1943dcc812bSGreg Roach }) 1953dcc812bSGreg Roach .on('click', 'a', function(e) { // stop click on a person also opening the popup 1963dcc812bSGreg Roach e.stopPropagation(); 1973dcc812bSGreg Roach }); 1983dcc812bSGreg Roach }); 1993dcc812bSGreg Roach 2003dcc812bSGreg Roach _drawMap(); 201*98579324SDavid Drury _buildMapData(); 2023dcc812bSGreg Roach 203*98579324SDavid Drury return "Leaflet map interface for webtrees-2"; 2043dcc812bSGreg Roach})(); 2053dcc812bSGreg Roach</script> 2063dcc812bSGreg Roach<?php View::endpush() ?> 207