1dd6b2bfcSGreg Roach<?php use Fisharebest\Webtrees\I18N; ?> 2dd6b2bfcSGreg Roach 3dd6b2bfcSGreg Roach<div class="py-4"> 4dd6b2bfcSGreg Roach <div class="row gchart osm-wrapper"> 5d21f0b10SGreg Roach <div id="osm-map" class="col-sm-9 wt-ajax-load osm-user-map" dir="ltr"></div> 68fca99e0SGreg Roach <ul class="col-sm-3 osm-sidebar wt-page-options-value list-unstyled p-0"></ul> 7dd6b2bfcSGreg Roach </div> 8dd6b2bfcSGreg Roach</div> 9dd6b2bfcSGreg Roach 10dd6b2bfcSGreg Roach<style> 11dd6b2bfcSGreg Roach .osm-wrapper, .osm-user-map { 128fca99e0SGreg Roach height: 75vh 13dd6b2bfcSGreg Roach } 14dd6b2bfcSGreg Roach 15dd6b2bfcSGreg Roach .osm-sidebar { 16dd6b2bfcSGreg Roach height: 100%; 17dd6b2bfcSGreg Roach overflow-y: auto; 18dd6b2bfcSGreg Roach padding: 0; 19dd6b2bfcSGreg Roach margin: 0; 20dd6b2bfcSGreg Roach border: 0; 21dd6b2bfcSGreg Roach display: none; 22dd6b2bfcSGreg Roach font-size: small; 23dd6b2bfcSGreg Roach } 24dd6b2bfcSGreg Roach 25dd6b2bfcSGreg Roach .osm-sidebar .gchart { 26dd6b2bfcSGreg Roach margin: 1px; 27dd6b2bfcSGreg Roach padding: 2px 28dd6b2bfcSGreg Roach } 29dd6b2bfcSGreg Roach 30dd6b2bfcSGreg Roach .osm-sidebar .gchart img { 31dd6b2bfcSGreg Roach height: 15px; 32dd6b2bfcSGreg Roach width: 25px 33dd6b2bfcSGreg Roach } 34dd6b2bfcSGreg Roach 35dd6b2bfcSGreg Roach .osm-sidebar .border-danger:hover { 36dd6b2bfcSGreg Roach cursor: not-allowed 37dd6b2bfcSGreg Roach } 38dd6b2bfcSGreg Roach</style> 39dd6b2bfcSGreg Roach 40dd6b2bfcSGreg Roach<script type="application/javascript"> 41dd6b2bfcSGreg Roach "use strict"; 42dd6b2bfcSGreg Roach 43dd6b2bfcSGreg Roach window.WT_OSM = (function () { 44dd6b2bfcSGreg Roach let baseData = { 45dd6b2bfcSGreg Roach minZoom: 2, 46dd6b2bfcSGreg Roach providerName: "OpenStreetMap.Mapnik", 47dd6b2bfcSGreg Roach providerOptions: [], 48dd6b2bfcSGreg Roach I18N: { 49dd6b2bfcSGreg Roach zoomInTitle: <?= json_encode(I18N::translate('Zoom in')) ?>, 50dd6b2bfcSGreg Roach zoomOutTitle: <?= json_encode(I18N::translate('Zoom out')) ?>, 51dd6b2bfcSGreg Roach reset: <?= json_encode(I18N::translate('Reset to initial map state')) ?>, 52dd6b2bfcSGreg Roach noData: <?= json_encode(I18N::translate('No mappable items')) ?> 53dd6b2bfcSGreg Roach } 54dd6b2bfcSGreg Roach }; 55dd6b2bfcSGreg Roach 56dd6b2bfcSGreg Roach let map = null; 57dd6b2bfcSGreg Roach let zoom = null; 58dd6b2bfcSGreg Roach let markers = L.markerClusterGroup({ 59dd6b2bfcSGreg Roach showCoverageOnHover: false, 60dd6b2bfcSGreg Roach }); 61dd6b2bfcSGreg Roach 62dd6b2bfcSGreg Roach let resetControl = L.Control.extend({ 63dd6b2bfcSGreg Roach options: { 64dd6b2bfcSGreg Roach position: "topleft", 65dd6b2bfcSGreg Roach }, 66dd6b2bfcSGreg Roach 67dd6b2bfcSGreg Roach onAdd: function (map) { 68dd6b2bfcSGreg Roach let container = L.DomUtil.create("div", "leaflet-bar leaflet-control leaflet-control-custom"); 69dd6b2bfcSGreg Roach container.onclick = function () { 70dd6b2bfcSGreg Roach if (zoom) { 71dd6b2bfcSGreg Roach map.flyTo(markers.getBounds().getCenter(), zoom); 72dd6b2bfcSGreg Roach } else { 73dd6b2bfcSGreg Roach map.flyToBounds(markers.getBounds().pad(0.2)); 74dd6b2bfcSGreg Roach } 75*4125a3e1SGreg Roach let sidebar = $(".osm-sidebar"); 76*4125a3e1SGreg Roach sidebar.scrollTo(sidebar.children(":first")); 77*4125a3e1SGreg Roach 78dd6b2bfcSGreg Roach return false; 79dd6b2bfcSGreg Roach }; 80dd6b2bfcSGreg Roach let anchor = L.DomUtil.create("a", "leaflet-control-reset", container); 81dd6b2bfcSGreg Roach anchor.href = "#"; 82dd6b2bfcSGreg Roach anchor.title = baseData.I18N.reset; 83dd6b2bfcSGreg Roach anchor.role = "button"; 84dd6b2bfcSGreg Roach $(anchor).attr("aria-label", "reset"); 85dd6b2bfcSGreg Roach let image = L.DomUtil.create("i", "fas fa-redo", anchor); 86dd6b2bfcSGreg Roach image.alt = baseData.I18N.reset; 87dd6b2bfcSGreg Roach 88dd6b2bfcSGreg Roach return container; 89dd6b2bfcSGreg Roach }, 90dd6b2bfcSGreg Roach }); 91dd6b2bfcSGreg Roach 92dd6b2bfcSGreg Roach /** 93dd6b2bfcSGreg Roach * 94dd6b2bfcSGreg Roach * @private 95dd6b2bfcSGreg Roach */ 96dd6b2bfcSGreg Roach let _drawMap = function () { 97dd6b2bfcSGreg Roach map = L.map("osm-map", { 98dd6b2bfcSGreg Roach center: [0, 0], 99dd6b2bfcSGreg Roach minZoom: baseData.minZoom, // maxZoom set by leaflet-providers.js 100dd6b2bfcSGreg Roach zoomControl: false, // remove default 101dd6b2bfcSGreg Roach }); 102dd6b2bfcSGreg Roach L.tileLayer.provider(baseData.providerName, baseData.providerOptions).addTo(map); 103dd6b2bfcSGreg Roach L.control.zoom({ // Add zoom with localised text 104dd6b2bfcSGreg Roach zoomInTitle: baseData.I18N.zoomInTitle, 105dd6b2bfcSGreg Roach zoomOutTitle: baseData.I18N.zoomOutTitle, 106dd6b2bfcSGreg Roach }).addTo(map); 107dd6b2bfcSGreg Roach }; 108dd6b2bfcSGreg Roach 109dd6b2bfcSGreg Roach let _addLayer = function () { 110dd6b2bfcSGreg Roach let geoJsonLayer; 111dd6b2bfcSGreg Roach let domObj = ".osm-sidebar"; 112dd6b2bfcSGreg Roach let sidebar = ""; 113dd6b2bfcSGreg Roach 114dd6b2bfcSGreg Roach let data = <?= json_encode($data) ?>; 115dd6b2bfcSGreg Roach 116dd6b2bfcSGreg Roach geoJsonLayer = L.geoJson(data, { 117dd6b2bfcSGreg Roach pointToLayer: function (feature, latlng) { 118dd6b2bfcSGreg Roach return new L.Marker(latlng, { 119dd6b2bfcSGreg Roach icon: L.BeautifyIcon.icon({ 120dd6b2bfcSGreg Roach icon: feature.properties.icon["name"], 121dd6b2bfcSGreg Roach borderColor: "transparent", 122dd6b2bfcSGreg Roach backgroundColor: feature.valid ? feature.properties.icon["color"] : "transparent", 123dd6b2bfcSGreg Roach iconShape: "marker", 124dd6b2bfcSGreg Roach textColor: feature.valid ? "white" : "transparent", 125dd6b2bfcSGreg Roach }), 126dd6b2bfcSGreg Roach title: feature.properties.tooltip, 127dd6b2bfcSGreg Roach alt: feature.properties.tooltip, 128dd6b2bfcSGreg Roach id: feature.id, 129dd6b2bfcSGreg Roach }) 130dd6b2bfcSGreg Roach .on("popupopen", function (e) { 131dd6b2bfcSGreg Roach let sidebar = $(".osm-sidebar"); 132dd6b2bfcSGreg Roach let item = sidebar.children(".gchart[data-id=" + e.target.feature.id + "]"); 133dd6b2bfcSGreg Roach item.addClass("messagebox"); 134dd6b2bfcSGreg Roach sidebar.scrollTo(item); 135dd6b2bfcSGreg Roach }) 136dd6b2bfcSGreg Roach .on("popupclose", function () { 137dd6b2bfcSGreg Roach $(".osm-sidebar").children(".gchart") 138dd6b2bfcSGreg Roach .removeClass("messagebox"); 139dd6b2bfcSGreg Roach }); 140dd6b2bfcSGreg Roach }, 141dd6b2bfcSGreg Roach onEachFeature: function (feature, layer) { 142dd6b2bfcSGreg Roach if (feature.properties.polyline) { 143dd6b2bfcSGreg Roach let pline = L.polyline(feature.properties.polyline.points, feature.properties.polyline.options); 144dd6b2bfcSGreg Roach markers.addLayer(pline); 145dd6b2bfcSGreg Roach } 146dd6b2bfcSGreg Roach layer.bindPopup(feature.properties.summary); 147dd6b2bfcSGreg Roach let myclass = feature.valid ? "gchart" : "border border-danger"; 148dd6b2bfcSGreg Roach sidebar += `<li class="${myclass}" data-id=${feature.id}>${feature.properties.summary}</li>`; 149dd6b2bfcSGreg Roach }, 150dd6b2bfcSGreg Roach }); 151dd6b2bfcSGreg Roach 152dd6b2bfcSGreg Roach if (data.features.length > 0) { 153dd6b2bfcSGreg Roach $(domObj).append(sidebar); 154dd6b2bfcSGreg Roach markers.addLayer(geoJsonLayer); 155dd6b2bfcSGreg Roach map 156dd6b2bfcSGreg Roach .addControl(new resetControl()) 157dd6b2bfcSGreg Roach .addLayer(markers); 158dd6b2bfcSGreg Roach 159dd6b2bfcSGreg Roach if (data.features.length === 1) { 160dd6b2bfcSGreg Roach map.setView(markers.getBounds().getCenter(), data.features[0].properties.zoom); 161dd6b2bfcSGreg Roach } else { 162dd6b2bfcSGreg Roach map.fitBounds(markers.getBounds().pad(0.2)) 163dd6b2bfcSGreg Roach } 164dd6b2bfcSGreg Roach } else { 165dd6b2bfcSGreg Roach map.fitWorld(); 166dd6b2bfcSGreg Roach $(domObj).append("<div class=\"bg-info text-white\">" + baseData.I18N.noData + "</div>"); 167dd6b2bfcSGreg Roach } 168dd6b2bfcSGreg Roach 169dd6b2bfcSGreg Roach $(domObj).slideDown(300); 170dd6b2bfcSGreg Roach }; 171dd6b2bfcSGreg Roach 172dd6b2bfcSGreg Roach /** 173dd6b2bfcSGreg Roach * 174dd6b2bfcSGreg Roach * @param elem 175dd6b2bfcSGreg Roach * @returns {$} 176dd6b2bfcSGreg Roach */ 177dd6b2bfcSGreg Roach 178dd6b2bfcSGreg Roach $.fn.scrollTo = function (elem) { 179dd6b2bfcSGreg Roach let _this = $(this); 180dd6b2bfcSGreg Roach _this.animate({ 181dd6b2bfcSGreg Roach scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(), 182dd6b2bfcSGreg Roach }); 183dd6b2bfcSGreg Roach return this; 184dd6b2bfcSGreg Roach }; 185dd6b2bfcSGreg Roach 186dd6b2bfcSGreg Roach return { 187dd6b2bfcSGreg Roach drawMap: function () { 188dd6b2bfcSGreg Roach _drawMap(); 189dd6b2bfcSGreg Roach _addLayer(); 190dd6b2bfcSGreg Roach 191dd6b2bfcSGreg Roach // Activate marker popup when sidebar entry clicked 192dd6b2bfcSGreg Roach $(".osm-sidebar") 193dd6b2bfcSGreg Roach .on("click", ".gchart", function (e) { 194dd6b2bfcSGreg Roach // first close any existing 195dd6b2bfcSGreg Roach map.closePopup(); 196dd6b2bfcSGreg Roach let eventId = $(this).data("id"); 197dd6b2bfcSGreg Roach //find the marker corresponding to the clicked event 198dd6b2bfcSGreg Roach let mkrLayer = markers.getLayers().filter(function (v) { 199dd6b2bfcSGreg Roach return typeof(v.feature) !== "undefined" && v.feature.id === eventId; 200dd6b2bfcSGreg Roach }); 201dd6b2bfcSGreg Roach let mkr = mkrLayer.pop(); 202dd6b2bfcSGreg Roach // Unfortunately zoomToShowLayer zooms to maxZoom 203dd6b2bfcSGreg Roach // when all marker in a cluster have exactly the 204dd6b2bfcSGreg Roach // same co-ordinates 205dd6b2bfcSGreg Roach markers.zoomToShowLayer(mkr, function (e) { 206dd6b2bfcSGreg Roach mkr.openPopup(); 207dd6b2bfcSGreg Roach }); 208dd6b2bfcSGreg Roach return false; 209dd6b2bfcSGreg Roach }) 210dd6b2bfcSGreg Roach .on("click", "a", function (e) { // stop click on a person also opening the popup 211dd6b2bfcSGreg Roach e.stopPropagation(); 212dd6b2bfcSGreg Roach }); 213dd6b2bfcSGreg Roach }, 214dd6b2bfcSGreg Roach }; 215dd6b2bfcSGreg Roach })(); 216dd6b2bfcSGreg Roach 217dd6b2bfcSGreg Roach WT_OSM.drawMap(); 218dd6b2bfcSGreg Roach</script> 219