1<?php 2 3use Fisharebest\Webtrees\I18N; 4use Fisharebest\Webtrees\View; 5 6/** 7 * @var array<mixed> $data 8 * @var array<mixed> $provider 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 minZoom = 2; 47 48 let map = null; 49 let zoom = null; 50 let sidebar = $('.osm-sidebar'); 51 let provider = <?= json_encode($provider) ?>; 52 53 // Map components 54 let markers = L.markerClusterGroup({ 55 showCoverageOnHover: false, 56 }); 57 58 let resetControl = L.Control.extend({ 59 options: { 60 position: "topleft", 61 }, 62 63 onAdd: function (map) { 64 let container = L.DomUtil.create("div", "leaflet-bar leaflet-control leaflet-control-custom"); 65 container.onclick = function () { 66 if (zoom) { 67 map.flyTo(markers.getBounds().getCenter(), zoom); 68 } else { 69 map.flyToBounds(markers.getBounds().pad(0.2)); 70 } 71 sidebar.scrollTo(sidebar.children(":first")); 72 73 return false; 74 }; 75 let anchor = L.DomUtil.create("a", "leaflet-control-reset", container); 76 let reset = <?= json_encode(I18N::translate('Reload map')) ?>; 77 anchor.setAttribute('aria-label', reset); 78 anchor.href = "#"; 79 anchor.title = reset; 80 anchor.role = "button"; 81 let image = L.DomUtil.create("i", "fas fa-redo", anchor); 82 image.alt = reset; 83 84 return container; 85 }, 86 }); 87 88 // Zoom control with localised text 89 let newZoomControl = new L.control.zoom({ 90 zoomInTitle : <?= json_encode(I18N::translate('Zoom in')) ?>, 91 zoomOutTitle: <?= json_encode(I18N::translate('Zoom out')) ?>, 92 }); 93 94 /** 95 * 96 * @private 97 */ 98 let _drawMap = function() { 99 map = L.map('osm-map', { 100 center : [0, 0], 101 minZoom : minZoom, // maxZoom set by leaflet-providers.js 102 zoomControl: false, // remove default 103 }) 104 .addControl(new resetControl()) 105 .addControl(newZoomControl) 106 .addLayer(L.tileLayer(provider.url, provider.options)); 107 }; 108 109 /** 110 * 111 * @private 112 */ 113 let _buildMapData = function () { 114 let data = <?= json_encode($data['markers']) ?>; 115 116 let geoJsonLayer = L.geoJson(data, { 117 pointToLayer: function (feature, latlng) { 118 return new L.Marker(latlng, { 119 icon: L.BeautifyIcon.icon({ 120 icon : 'bullseye fas', 121 borderColor : "transparent", 122 backgroundColor: '#1e90ff', 123 iconShape : "marker", 124 textColor : "white", 125 }), 126 title: feature.properties.tooltip, 127 alt : feature.properties.tooltip, 128 id : feature.id, 129 }) 130 .on("popupopen", function (e) { 131 let item = sidebar.children(".mapped[data-id=" + e.target.feature.id + "]"); 132 item.addClass("messagebox"); 133 sidebar.scrollTo(item); 134 }) 135 .on("popupclose", function () { 136 sidebar.children(".mapped") 137 .removeClass("messagebox"); 138 }); 139 }, 140 onEachFeature: function (feature, layer) { 141 layer.bindPopup(feature.properties.popup); 142 }, 143 }); 144 145 if (data.features.length > 0) { 146 markers.addLayer(geoJsonLayer); 147 map.addLayer(markers) 148 } 149 150 map.fitBounds(<?= json_encode($data['bounds']) ?>, {padding: [50, 30]}); 151 sidebar.append(<?= json_encode($data['sidebar']) ?>); 152 }; 153 154 /** 155 * @param elem 156 * @returns {$} 157 */ 158 $.fn.scrollTo = function (elem) { 159 let _this = $(this); 160 _this.animate({ 161 scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(), 162 }); 163 return this; 164 }; 165 166 // Activate marker popup when sidebar entry clicked 167 $(function () { 168 sidebar 169 // open marker popup if sidebar event is clicked 170 .on("click", ".mapped", function (e) { 171 // first close any existing 172 map.closePopup(); 173 let eventId = $(this).data("id"); 174 //find the marker corresponding to the clicked event 175 let mkrLayer = markers.getLayers().filter(function (v) { 176 return typeof (v.feature) !== "undefined" && v.feature.id === eventId; 177 }); 178 let mkr = mkrLayer.pop(); 179 // Unfortunately zoomToShowLayer zooms to maxZoom 180 // when all marker in a cluster have exactly the 181 // same co-ordinates 182 markers.zoomToShowLayer(mkr, function (e) { 183 mkr.openPopup(); 184 }); 185 return false; 186 }) 187 .on("click", "a", function (e) { // stop click on a person also opening the popup 188 e.stopPropagation(); 189 }); 190 }); 191 192 _drawMap(); 193 _buildMapData(); 194 195 return "Leaflet map interface for webtrees-2"; 196 })(); 197</script> 198<?php View::endpush() ?> 199