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