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