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> 38 "use strict"; 39 40 window.WT_OSM = (function () { 41 const minZoom = 2; 42 43 let map = null; 44 let zoom = null; 45 let sidebar = $('.osm-sidebar'); 46 let provider = <?= json_encode($provider) ?>; 47 48 // Map components 49 let markers = L.markerClusterGroup({ 50 showCoverageOnHover: false, 51 }); 52 53 let resetControl = L.Control.extend({ 54 options: { 55 position: "topleft", 56 }, 57 58 onAdd: function (map) { 59 let container = L.DomUtil.create("div", "leaflet-bar leaflet-control leaflet-control-custom"); 60 container.onclick = function () { 61 if (zoom) { 62 map.flyTo(markers.getBounds().getCenter(), zoom); 63 } else { 64 map.flyToBounds(markers.getBounds().pad(0.2)); 65 } 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('Reload map')) ?>; 72 anchor.setAttribute('aria-label', reset); 73 anchor.href = "#"; 74 anchor.title = reset; 75 anchor.role = "button"; 76 let image = L.DomUtil.create("i", "fas fa-redo", anchor); 77 image.alt = reset; 78 79 return container; 80 }, 81 }); 82 83 // Zoom control with localised text 84 let newZoomControl = new L.control.zoom({ 85 zoomInTitle : <?= json_encode(I18N::translate('Zoom in')) ?>, 86 zoomOutTitle: <?= json_encode(I18N::translate('Zoom out')) ?>, 87 }); 88 89 /** 90 * 91 * @private 92 */ 93 let _drawMap = function() { 94 map = L.map('osm-map', { 95 center : [0, 0], 96 minZoom : minZoom, // maxZoom set by leaflet-providers.js 97 zoomControl: false, // remove default 98 }) 99 .addControl(new resetControl()) 100 .addControl(newZoomControl) 101 .addLayer(L.tileLayer.provider(provider.name, provider.options)) 102 }; 103 104 /** 105 * 106 * @private 107 */ 108 let _buildMapData = function () { 109 let data = <?= json_encode($data['markers']) ?>; 110 111 if (data.features.length === 1) { 112 zoom = data.features[0].properties.zoom; 113 } 114 let 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 item = sidebar.children(".mapped[data-id=" + e.target.feature.id + "]"); 130 item.addClass("messagebox"); 131 sidebar.scrollTo(item); 132 }) 133 .on("popupclose", function () { 134 sidebar.children(".mapped") 135 .removeClass("messagebox"); 136 }); 137 }, 138 onEachFeature: function (feature, layer) { 139 layer.bindPopup(feature.properties.popup); 140 }, 141 }); 142 143 if (data.features.length > 0) { 144 markers.addLayer(geoJsonLayer); 145 map.addLayer(markers) 146 147 if (zoom) { 148 map.setView(markers.getBounds().getCenter(), zoom); 149 } else { 150 map.fitBounds(markers.getBounds().pad(0.2)); 151 } 152 } else { 153 map.fitWorld(); 154 } 155 sidebar.append(<?= json_encode($data['sidebar']) ?>); 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 // Activate marker popup when sidebar entry clicked 173 $(function () { 174 sidebar 175 // open marker popup if sidebar event is clicked 176 .on("click", ".mapped", function (e) { 177 // first close any existing 178 map.closePopup(); 179 let eventId = $(this).data("id"); 180 //find the marker corresponding to the clicked event 181 let mkrLayer = markers.getLayers().filter(function (v) { 182 return typeof (v.feature) !== "undefined" && v.feature.id === eventId; 183 }); 184 let mkr = mkrLayer.pop(); 185 // Unfortunately zoomToShowLayer zooms to maxZoom 186 // when all marker in a cluster have exactly the 187 // same co-ordinates 188 markers.zoomToShowLayer(mkr, function (e) { 189 mkr.openPopup(); 190 }); 191 return false; 192 }) 193 .on("click", "a", function (e) { // stop click on a person also opening the popup 194 e.stopPropagation(); 195 }); 196 }); 197 198 _drawMap(); 199 _buildMapData(); 200 201 return "Leaflet map interface for webtrees-2"; 202 })(); 203</script> 204<?php View::endpush() ?> 205