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