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 26*74b9ba3fSGreg 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) 89597fb44bSDavid Drury .addLayer(L.tileLayer.provider(provider.name, 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 106597fb44bSDavid Drury zoom = geoJson_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 { 142597fb44bSDavid Drury map.fitBounds(markers.getBounds().pad(0.2)); 143dd6b2bfcSGreg Roach } 144dd6b2bfcSGreg Roach } 145597fb44bSDavid Drury sidebar.append(sidebar_content); 146dd6b2bfcSGreg Roach }; 147dd6b2bfcSGreg Roach 148dd6b2bfcSGreg Roach /** 149dd6b2bfcSGreg Roach * 150dd6b2bfcSGreg Roach * @param elem 151dd6b2bfcSGreg Roach * @returns {$} 152dd6b2bfcSGreg Roach */ 153dd6b2bfcSGreg Roach 154dd6b2bfcSGreg Roach $.fn.scrollTo = function(elem) { 155dd6b2bfcSGreg Roach let _this = $(this); 156dd6b2bfcSGreg Roach _this.animate({ 157dd6b2bfcSGreg Roach scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(), 158dd6b2bfcSGreg Roach }); 159dd6b2bfcSGreg Roach return this; 160dd6b2bfcSGreg Roach }; 161dd6b2bfcSGreg Roach 162dd6b2bfcSGreg Roach // Activate marker popup when sidebar entry clicked 163597fb44bSDavid Drury $(function() { 1640d123f04SDavid Drury sidebar 165597fb44bSDavid Drury // open marker popup if sidebar event is clicked 166597fb44bSDavid Drury .on('click', '.gchart', function(e) { 167dd6b2bfcSGreg Roach // first close any existing 168dd6b2bfcSGreg Roach map.closePopup(); 169597fb44bSDavid Drury let eventId = $(this).data('id'); 170dd6b2bfcSGreg Roach //find the marker corresponding to the clicked event 171dd6b2bfcSGreg Roach let mkrLayer = markers.getLayers().filter(function(v) { 172597fb44bSDavid Drury return typeof(v.feature) !== 'undefined' && v.feature.id === eventId; 173dd6b2bfcSGreg Roach }); 174dd6b2bfcSGreg Roach let mkr = mkrLayer.pop(); 175dd6b2bfcSGreg Roach // Unfortunately zoomToShowLayer zooms to maxZoom 176dd6b2bfcSGreg Roach // when all marker in a cluster have exactly the 177dd6b2bfcSGreg Roach // same co-ordinates 178dd6b2bfcSGreg Roach markers.zoomToShowLayer(mkr, function(e) { 179dd6b2bfcSGreg Roach mkr.openPopup(); 180dd6b2bfcSGreg Roach }); 181597fb44bSDavid Drury 182dd6b2bfcSGreg Roach return false; 183dd6b2bfcSGreg Roach }) 184597fb44bSDavid Drury .on('click', 'a', function(e) { // stop click on a person also opening the popup 185dd6b2bfcSGreg Roach e.stopPropagation(); 186dd6b2bfcSGreg Roach }); 187597fb44bSDavid Drury }); 188dd6b2bfcSGreg Roach 189597fb44bSDavid Drury _drawMap(); 190597fb44bSDavid Drury _buildMapData(); 191597fb44bSDavid Drury 192597fb44bSDavid Drury return "Leaflet map interface for webtrees-2"; 193597fb44bSDavid Drury })(); 194dd6b2bfcSGreg Roach</script> 195