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 type="application/javascript"> 38 "use strict"; 39 40 window.WT_OSM = (function () { 41 let baseData = { 42 minZoom: 2, 43 providerName: "OpenStreetMap.Mapnik", 44 providerOptions: [], 45 }; 46 47 let map = null; 48 let zoom = null; 49 let sidebar = $(".osm-sidebar"); 50 let markers = L.markerClusterGroup({ 51 showCoverageOnHover: false, 52 }); 53 54 let resetControl = L.Control.extend({ 55 options: { 56 position: "topleft", 57 }, 58 59 onAdd: function (map) { 60 let container = L.DomUtil.create("div", "leaflet-bar leaflet-control leaflet-control-custom"); 61 container.onclick = function () { 62 if (zoom) { 63 map.flyTo(markers.getBounds().getCenter(), zoom); 64 } else { 65 map.flyToBounds(markers.getBounds().pad(0.2)); 66 } 67 sidebar.scrollTo(sidebar.children(":first")); 68 69 return false; 70 }; 71 let anchor = L.DomUtil.create("a", "leaflet-control-reset", container); 72 let reset = <?= json_encode(I18N::translate('Reset to initial map state')) ?>; 73 anchor.href = "#"; 74 anchor.title = reset; 75 anchor.role = "button"; 76 $(anchor).attr("aria-label", "reset"); 77 let image = L.DomUtil.create("i", "fas fa-redo", anchor); 78 image.alt = reset; 79 80 return container; 81 }, 82 }); 83 84 let _drawMap = function () { 85 map = L.map("osm-map", { 86 center: [0, 0], 87 minZoom: baseData.minZoom, // maxZoom set by leaflet-providers.js 88 zoomControl: false, // remove default 89 }); 90 L.tileLayer.provider(baseData.providerName, baseData.providerOptions).addTo(map); 91 L.control.zoom({ // Add zoom with localised text 92 zoomInTitle: <?= json_encode(I18N::translate('Zoom in')) ?>, 93 zoomOutTitle: <?= json_encode(I18N::translate('Zoom out')) ?>, 94 }).addTo(map); 95 }; 96 97 let _addLayer = function () { 98 let geoJsonLayer; 99 let data = <?= json_encode($data['markers']) ?>; 100 101 if (data.features.length === 1) { 102 zoom = data.features[0].properties.zoom; 103 } 104 geoJsonLayer = L.geoJson(data, { 105 pointToLayer: function (feature, latlng) { 106 return new L.Marker(latlng, { 107 icon: L.BeautifyIcon.icon({ 108 icon: 'bullseye fas', 109 borderColor: "transparent", 110 backgroundColor: '#1e90ff', 111 iconShape: "marker", 112 textColor: "white", 113 }), 114 title: feature.properties.tooltip, 115 alt: feature.properties.tooltip, 116 id: feature.id, 117 }) 118 .on("popupopen", function (e) { 119 let item = sidebar.children(".mapped[data-id=" + e.target.feature.id + "]"); 120 item.addClass("messagebox"); 121 sidebar.scrollTo(item); 122 }) 123 .on("popupclose", function () { 124 sidebar.children(".mapped") 125 .removeClass("messagebox"); 126 }); 127 }, 128 onEachFeature: function (feature, layer) { 129 layer.bindPopup(feature.properties.popup); 130 }, 131 }); 132 133 if (data.features.length > 0) { 134 markers.addLayer(geoJsonLayer); 135 map 136 .addControl(new resetControl()) 137 .addLayer(markers) 138 .fitBounds(markers.getBounds().pad(0.2)); 139 if (zoom) { 140 map.setView(markers.getBounds().getCenter(), zoom); 141 } 142 } else { 143 map.fitWorld(); 144 } 145 sidebar.append(<?= json_encode($data['sidebar']) ?>); 146 }; 147 148 /** 149 * 150 * @param elem 151 * @returns {$} 152 */ 153 154 $.fn.scrollTo = function (elem) { 155 let _this = $(this); 156 _this.animate({ 157 scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(), 158 }); 159 return this; 160 }; 161 162 /** 163 * @private 164 */ 165 let _initialize = function () { 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 _addLayer(); 194 }; 195 196 return { 197 drawMap: function () { 198 _initialize(); 199 }, 200 }; 201 })(); 202 203 WT_OSM.drawMap(); 204</script> 205<?php View::endpush() ?> 206