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, JSON_THROW_ON_ERROR) ?>; 36 37 let map = null; 38 let sidebar = $('.osm-sidebar'); 39 40 // Map components 41 let markers = L.markerClusterGroup({ 42 showCoverageOnHover: false, 43 }); 44 45 let resetControl = L.Control.extend({ 46 options: { 47 position: "topleft", 48 }, 49 onAdd: function(map) { 50 let container = L.DomUtil.create("div", "leaflet-bar leaflet-control leaflet-control-custom"); 51 container.onclick = function() { 52 map.flyToBounds(markers.getBounds(), {padding: [50, 30], maxZoom: 15}); 53 sidebar.scrollTo(sidebar.children(":first")); 54 55 return false; 56 }; 57 let reset = config.i18n.reset; 58 let anchor = L.DomUtil.create('a', 'leaflet-control-reset', container); 59 anchor.setAttribute('aria-label', reset); 60 anchor.href = '#'; 61 anchor.title = reset; 62 anchor.role = 'button'; 63 let image = L.DomUtil.create('i', 'fas fa-redo', anchor); 64 image.alt = reset; 65 66 return container; 67 }, 68 }); 69 70 /** 71 * 72 * @private 73 */ 74 let _drawMap = function() { 75 map = webtrees.buildLeafletJsMap('osm-map', config) 76 .addControl(new resetControl()); 77 }; 78 79 /** 80 * 81 * @private 82 */ 83 let _buildMapData = function() { 84 let sidebar_content = ""; 85 let data = <?= json_encode($data, JSON_THROW_ON_ERROR) ?>; 86 87 if (data.features.length === 0) { 88 map.fitWorld(); 89 sidebar_content += '<div class="bg-info text-white text-center">' + <?= json_encode(I18N::translate('Nothing to show'), JSON_THROW_ON_ERROR) ?> + '</div>'; 90 } else { 91 let geoJsonLayer = L.geoJson(data, { 92 pointToLayer: function(feature, latlng) { 93 return new L.Marker(latlng, { 94 icon: L.BeautifyIcon.icon({ 95 icon : feature.properties.icon["name"], 96 borderColor : "transparent", 97 backgroundColor: feature.properties.icon["color"], 98 iconShape : "marker", 99 textColor : "white", 100 }), 101 title: feature.properties.tooltip, 102 alt : feature.properties.tooltip, 103 id : feature.id, 104 }) 105 .on("popupopen", function(e) { 106 let item = sidebar.children(".gchart[data-wt-feature-id=" + e.target.feature.id + "]"); 107 item.addClass("messagebox"); 108 sidebar.scrollTo(item); 109 }) 110 .on("popupclose", function() { 111 sidebar.children(".gchart") 112 .removeClass("messagebox"); 113 }); 114 }, 115 onEachFeature: function(feature, layer) { 116 layer.bindPopup(feature.properties.summary); 117 sidebar_content += `<li class="gchart px-md-2" data-wt-feature-id=${feature.id}>${feature.properties.summary}</li>`; 118 }, 119 }); 120 markers.addLayer(geoJsonLayer); 121 map.addLayer(markers); 122 map.fitBounds(markers.getBounds(), {padding: [50, 30], maxZoom: 15}); 123 } 124 sidebar.append(sidebar_content); 125 }; 126 127 /** 128 * @param elem 129 * @returns {$} 130 */ 131 $.fn.scrollTo = function(elem) { 132 let _this = $(this); 133 _this.animate({ 134 scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(), 135 }); 136 return this; 137 }; 138 139 // Activate marker popup when sidebar entry clicked 140 $(function() { 141 sidebar 142 // open marker popup if sidebar event is clicked 143 .on('click', '.gchart', function(e) { 144 // first close any existing 145 map.closePopup(); 146 let eventId = $(this).data('wt-feature-id'); 147 //find the marker corresponding to the clicked event 148 let mkrLayer = markers.getLayers().filter(function(v) { 149 return typeof(v.feature) !== 'undefined' && v.feature.id === eventId; 150 }); 151 let mkr = mkrLayer.pop(); 152 // Unfortunately zoomToShowLayer zooms to maxZoom 153 // when all marker in a cluster have exactly the 154 // same co-ordinates 155 markers.zoomToShowLayer(mkr, function(e) { 156 mkr.openPopup(); 157 }); 158 159 return false; 160 }) 161 .on('click', 'a', function(e) { // stop click on a person also opening the popup 162 e.stopPropagation(); 163 }); 164 }); 165 166 _drawMap(); 167 _buildMapData(); 168 169 return "Leaflet map interface for webtrees-2"; 170 })(); 171</script> 172