1<?php 2 3use Fisharebest\Webtrees\I18N; 4 5/** 6 * @var array<mixed> $data 7 * @var object $leaflet_config 8 */ 9 10?> 11 12<div class="py-4"> 13 <div class="row gchart osm-wrapper"> 14 <div id="osm-map" class="col-sm-9 wt-ajax-load osm-user-map" dir="ltr"></div> 15 <ul class="col-sm-3 osm-sidebar wt-page-options-value list-unstyled px-md-1"></ul> 16 </div> 17</div> 18 19<style> 20 .osm-wrapper, .osm-user-map { 21 height: 70vh 22 } 23 24 .osm-sidebar { 25 height: 100%; 26 overflow-y: auto; 27 font-size: small; 28 } 29</style> 30 31<script> 32 "use strict"; 33 34 window.WT_OSM = (function() { 35 const config = <?= json_encode($leaflet_config) ?>; 36 37 let map = null; 38 let zoom = null; 39 let sidebar = $('.osm-sidebar'); 40 41 // Map components 42 let markers = L.markerClusterGroup({ 43 showCoverageOnHover: false, 44 }); 45 46 let resetControl = L.Control.extend({ 47 options: { 48 position: "topleft", 49 }, 50 onAdd: function(map) { 51 let container = L.DomUtil.create("div", "leaflet-bar leaflet-control leaflet-control-custom"); 52 container.onclick = function() { 53 if (zoom) { 54 map.flyTo(markers.getBounds().getCenter(), zoom); 55 } else { 56 map.flyToBounds(markers.getBounds().pad(0.2)); 57 } 58 sidebar.scrollTo(sidebar.children(":first")); 59 60 return false; 61 }; 62 let reset = config.i18n.reset; 63 let anchor = L.DomUtil.create('a', 'leaflet-control-reset', container); 64 anchor.setAttribute('aria-label', reset); 65 anchor.href = '#'; 66 anchor.title = reset; 67 anchor.role = 'button'; 68 let image = L.DomUtil.create('i', 'fas fa-redo', anchor); 69 image.alt = reset; 70 71 return container; 72 }, 73 }); 74 75 /** 76 * 77 * @private 78 */ 79 let _drawMap = function() { 80 map = webtrees.buildLeafletJsMap('osm-map', config) 81 .addControl(new resetControl()); 82 }; 83 84 /** 85 * 86 * @private 87 */ 88 let _buildMapData = function() { 89 let sidebar_content = ""; 90 let data = <?= json_encode($data) ?>; 91 92 if (data.features.length === 0) { 93 map.fitWorld(); 94 sidebar_content += '<div class="bg-info text-white text-center">' + <?= json_encode(I18N::translate('Nothing to show')) ?> + '</div>'; 95 } else { 96 if (data.features.length === 1) { 97 //fudge factor - maps zooms to maximum permitted otherwise 98 zoom = data.features[0].properties.zoom; 99 } 100 let geoJsonLayer = L.geoJson(data, { 101 pointToLayer: function(feature, latlng) { 102 return new L.Marker(latlng, { 103 icon: L.BeautifyIcon.icon({ 104 icon : feature.properties.icon["name"], 105 borderColor : "transparent", 106 backgroundColor: feature.properties.icon["color"], 107 iconShape : "marker", 108 textColor : "white", 109 }), 110 title: feature.properties.tooltip, 111 alt : feature.properties.tooltip, 112 id : feature.id, 113 }) 114 .on("popupopen", function(e) { 115 let item = sidebar.children(".gchart[data-id=" + e.target.feature.id + "]"); 116 item.addClass("messagebox"); 117 sidebar.scrollTo(item); 118 }) 119 .on("popupclose", function() { 120 sidebar.children(".gchart") 121 .removeClass("messagebox"); 122 }); 123 }, 124 onEachFeature: function(feature, layer) { 125 layer.bindPopup(feature.properties.summary); 126 sidebar_content += `<li class="gchart px-md-2" data-id=${feature.id}>${feature.properties.summary}</li>`; 127 }, 128 }); 129 markers.addLayer(geoJsonLayer); 130 map.addLayer(markers); 131 if (zoom) { 132 map.setView(markers.getBounds().getCenter(), zoom); 133 } else { 134 map.fitBounds(markers.getBounds(), {padding: [50, 30]}); 135 } 136 } 137 sidebar.append(sidebar_content); 138 }; 139 140 /** 141 * @param elem 142 * @returns {$} 143 */ 144 $.fn.scrollTo = function(elem) { 145 let _this = $(this); 146 _this.animate({ 147 scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(), 148 }); 149 return this; 150 }; 151 152 // Activate marker popup when sidebar entry clicked 153 $(function() { 154 sidebar 155 // open marker popup if sidebar event is clicked 156 .on('click', '.gchart', function(e) { 157 // first close any existing 158 map.closePopup(); 159 let eventId = $(this).data('id'); 160 //find the marker corresponding to the clicked event 161 let mkrLayer = markers.getLayers().filter(function(v) { 162 return typeof(v.feature) !== 'undefined' && v.feature.id === eventId; 163 }); 164 let mkr = mkrLayer.pop(); 165 // Unfortunately zoomToShowLayer zooms to maxZoom 166 // when all marker in a cluster have exactly the 167 // same co-ordinates 168 markers.zoomToShowLayer(mkr, function(e) { 169 mkr.openPopup(); 170 }); 171 172 return false; 173 }) 174 .on('click', 'a', function(e) { // stop click on a person also opening the popup 175 e.stopPropagation(); 176 }); 177 }); 178 179 _drawMap(); 180 _buildMapData(); 181 182 return "Leaflet map interface for webtrees-2"; 183 })(); 184</script> 185