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