1<?php 2 3use Fisharebest\Webtrees\I18N; 4use Fisharebest\Webtrees\View; 5 6?> 7 8<div class="py-4"> 9 <div class="row gchart osm-wrapper"> 10 <div id="osm-map" class="col-sm-9 wt-ajax-load osm-user-map" dir="ltr"></div> 11 <ul class="col-sm-3 osm-sidebar wt-page-options-value list-unstyled px-md-1"></ul> 12 </div> 13</div> 14 15<?php View::push('styles') ?> 16<style> 17 .osm-wrapper, .osm-user-map { 18 height: 70vh 19 } 20 21 .osm-sidebar { 22 height: 100%; 23 overflow-x: hidden; 24 overflow-y: auto; 25 font-size: small; 26 } 27 28 .flag { 29 border: 1px solid grey !important; 30 height: 15px; 31 width: 25px; 32 } 33</style> 34<?php View::endpush() ?> 35 36<?php View::push('javascript') ?> 37<script> 38 "use strict"; 39 40 window.WT_OSM = (function () { 41 const minZoom = 2; 42 43 let map = null; 44 let zoom = null; 45 let sidebar = $('.osm-sidebar'); 46 let provider = <?= json_encode($provider) ?>; 47 48 // Map components 49 let markers = L.markerClusterGroup({ 50 showCoverageOnHover: false, 51 }); 52 53 let resetControl = L.Control.extend({ 54 options: { 55 position: "topleft", 56 }, 57 58 onAdd: function (map) { 59 let container = L.DomUtil.create("div", "leaflet-bar leaflet-control leaflet-control-custom"); 60 container.onclick = function () { 61 if (zoom) { 62 map.flyTo(markers.getBounds().getCenter(), zoom); 63 } else { 64 map.flyToBounds(markers.getBounds().pad(0.2)); 65 } 66 sidebar.scrollTo(sidebar.children(":first")); 67 68 return false; 69 }; 70 let anchor = L.DomUtil.create("a", "leaflet-control-reset", container); 71 let reset = <?= json_encode(I18N::translate('Reload map')) ?>; 72 anchor.setAttribute('aria-label', reset); 73 anchor.href = "#"; 74 anchor.title = reset; 75 anchor.role = "button"; 76 let image = L.DomUtil.create("i", "fas fa-redo", anchor); 77 image.alt = reset; 78 79 return container; 80 }, 81 }); 82 83 // Zoom control with localised text 84 let newZoomControl = new L.control.zoom({ 85 zoomInTitle : <?= json_encode(I18N::translate('Zoom in')) ?>, 86 zoomOutTitle: <?= json_encode(I18N::translate('Zoom out')) ?>, 87 }); 88 89 /** 90 * 91 * @private 92 */ 93 let _drawMap = function() { 94 map = L.map('osm-map', { 95 center : [0, 0], 96 minZoom : minZoom, // maxZoom set by leaflet-providers.js 97 zoomControl: false, // remove default 98 }) 99 .addControl(new resetControl()) 100 .addControl(newZoomControl) 101 .addLayer(L.tileLayer(provider.url, provider.options)); 102 }; 103 104 /** 105 * 106 * @private 107 */ 108 let _buildMapData = function () { 109 let data = <?= json_encode($data['markers']) ?>; 110 111 let geoJsonLayer = L.geoJson(data, { 112 pointToLayer: function (feature, latlng) { 113 return new L.Marker(latlng, { 114 icon: L.BeautifyIcon.icon({ 115 icon : 'bullseye fas', 116 borderColor : "transparent", 117 backgroundColor: '#1e90ff', 118 iconShape : "marker", 119 textColor : "white", 120 }), 121 title: feature.properties.tooltip, 122 alt : feature.properties.tooltip, 123 id : feature.id, 124 }) 125 .on("popupopen", function (e) { 126 let item = sidebar.children(".mapped[data-id=" + e.target.feature.id + "]"); 127 item.addClass("messagebox"); 128 sidebar.scrollTo(item); 129 }) 130 .on("popupclose", function () { 131 sidebar.children(".mapped") 132 .removeClass("messagebox"); 133 }); 134 }, 135 onEachFeature: function (feature, layer) { 136 layer.bindPopup(feature.properties.popup); 137 }, 138 }); 139 140 if (data.features.length > 0) { 141 markers.addLayer(geoJsonLayer); 142 map.addLayer(markers) 143 } 144 145 map.fitBounds(<?= json_encode($data['bounds']) ?>); 146 sidebar.append(<?= json_encode($data['sidebar']) ?>); 147 }; 148 149 /** 150 * @param elem 151 * @returns {$} 152 */ 153 $.fn.scrollTo = function (elem) { 154 let _this = $(this); 155 _this.animate({ 156 scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(), 157 }); 158 return this; 159 }; 160 161 // Activate marker popup when sidebar entry clicked 162 $(function () { 163 sidebar 164 // open marker popup if sidebar event is clicked 165 .on("click", ".mapped", function (e) { 166 // first close any existing 167 map.closePopup(); 168 let eventId = $(this).data("id"); 169 //find the marker corresponding to the clicked event 170 let mkrLayer = markers.getLayers().filter(function (v) { 171 return typeof (v.feature) !== "undefined" && v.feature.id === eventId; 172 }); 173 let mkr = mkrLayer.pop(); 174 // Unfortunately zoomToShowLayer zooms to maxZoom 175 // when all marker in a cluster have exactly the 176 // same co-ordinates 177 markers.zoomToShowLayer(mkr, function (e) { 178 mkr.openPopup(); 179 }); 180 return false; 181 }) 182 .on("click", "a", function (e) { // stop click on a person also opening the popup 183 e.stopPropagation(); 184 }); 185 }); 186 187 _drawMap(); 188 _buildMapData(); 189 190 return "Leaflet map interface for webtrees-2"; 191 })(); 192</script> 193<?php View::endpush() ?> 194