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