1dd6b2bfcSGreg Roach<?php use Fisharebest\Webtrees\I18N; ?> 2dd6b2bfcSGreg Roach 3dd6b2bfcSGreg Roach<div class="py-4"> 4dd6b2bfcSGreg Roach <div class="row gchart osm-wrapper"> 5dd6b2bfcSGreg Roach <div id="osm-map" class="col-sm-9 wt-ajax-load osm-user-map"></div> 6*8fca99e0SGreg 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 { 12*8fca99e0SGreg 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 } 75dd6b2bfcSGreg Roach return false; 76dd6b2bfcSGreg Roach }; 77dd6b2bfcSGreg Roach let anchor = L.DomUtil.create("a", "leaflet-control-reset", container); 78dd6b2bfcSGreg Roach anchor.href = "#"; 79dd6b2bfcSGreg Roach anchor.title = baseData.I18N.reset; 80dd6b2bfcSGreg Roach anchor.role = "button"; 81dd6b2bfcSGreg Roach $(anchor).attr("aria-label", "reset"); 82dd6b2bfcSGreg Roach let image = L.DomUtil.create("i", "fas fa-redo", anchor); 83dd6b2bfcSGreg Roach image.alt = baseData.I18N.reset; 84dd6b2bfcSGreg Roach 85dd6b2bfcSGreg Roach return container; 86dd6b2bfcSGreg Roach }, 87dd6b2bfcSGreg Roach }); 88dd6b2bfcSGreg Roach 89dd6b2bfcSGreg Roach /** 90dd6b2bfcSGreg Roach * 91dd6b2bfcSGreg Roach * @private 92dd6b2bfcSGreg Roach */ 93dd6b2bfcSGreg Roach let _drawMap = function () { 94dd6b2bfcSGreg Roach map = L.map("osm-map", { 95dd6b2bfcSGreg Roach center: [0, 0], 96dd6b2bfcSGreg Roach minZoom: baseData.minZoom, // maxZoom set by leaflet-providers.js 97dd6b2bfcSGreg Roach zoomControl: false, // remove default 98dd6b2bfcSGreg Roach }); 99dd6b2bfcSGreg Roach L.tileLayer.provider(baseData.providerName, baseData.providerOptions).addTo(map); 100dd6b2bfcSGreg Roach L.control.zoom({ // Add zoom with localised text 101dd6b2bfcSGreg Roach zoomInTitle: baseData.I18N.zoomInTitle, 102dd6b2bfcSGreg Roach zoomOutTitle: baseData.I18N.zoomOutTitle, 103dd6b2bfcSGreg Roach }).addTo(map); 104dd6b2bfcSGreg Roach }; 105dd6b2bfcSGreg Roach 106dd6b2bfcSGreg Roach let _addLayer = function () { 107dd6b2bfcSGreg Roach let geoJsonLayer; 108dd6b2bfcSGreg Roach let domObj = ".osm-sidebar"; 109dd6b2bfcSGreg Roach let sidebar = ""; 110dd6b2bfcSGreg Roach 111dd6b2bfcSGreg Roach let data = <?= json_encode($data) ?>; 112dd6b2bfcSGreg Roach 113dd6b2bfcSGreg Roach geoJsonLayer = L.geoJson(data, { 114dd6b2bfcSGreg Roach pointToLayer: function (feature, latlng) { 115dd6b2bfcSGreg Roach return new L.Marker(latlng, { 116dd6b2bfcSGreg Roach icon: L.BeautifyIcon.icon({ 117dd6b2bfcSGreg Roach icon: feature.properties.icon["name"], 118dd6b2bfcSGreg Roach borderColor: "transparent", 119dd6b2bfcSGreg Roach backgroundColor: feature.valid ? feature.properties.icon["color"] : "transparent", 120dd6b2bfcSGreg Roach iconShape: "marker", 121dd6b2bfcSGreg Roach textColor: feature.valid ? "white" : "transparent", 122dd6b2bfcSGreg Roach }), 123dd6b2bfcSGreg Roach title: feature.properties.tooltip, 124dd6b2bfcSGreg Roach alt: feature.properties.tooltip, 125dd6b2bfcSGreg Roach id: feature.id, 126dd6b2bfcSGreg Roach }) 127dd6b2bfcSGreg Roach .on("popupopen", function (e) { 128dd6b2bfcSGreg Roach let sidebar = $(".osm-sidebar"); 129dd6b2bfcSGreg Roach let item = sidebar.children(".gchart[data-id=" + e.target.feature.id + "]"); 130dd6b2bfcSGreg Roach item.addClass("messagebox"); 131dd6b2bfcSGreg Roach sidebar.scrollTo(item); 132dd6b2bfcSGreg Roach }) 133dd6b2bfcSGreg Roach .on("popupclose", function () { 134dd6b2bfcSGreg Roach $(".osm-sidebar").children(".gchart") 135dd6b2bfcSGreg Roach .removeClass("messagebox"); 136dd6b2bfcSGreg Roach }); 137dd6b2bfcSGreg Roach }, 138dd6b2bfcSGreg Roach onEachFeature: function (feature, layer) { 139dd6b2bfcSGreg Roach if (feature.properties.polyline) { 140dd6b2bfcSGreg Roach let pline = L.polyline(feature.properties.polyline.points, feature.properties.polyline.options); 141dd6b2bfcSGreg Roach markers.addLayer(pline); 142dd6b2bfcSGreg Roach } 143dd6b2bfcSGreg Roach layer.bindPopup(feature.properties.summary); 144dd6b2bfcSGreg Roach let myclass = feature.valid ? "gchart" : "border border-danger"; 145dd6b2bfcSGreg Roach sidebar += `<li class="${myclass}" data-id=${feature.id}>${feature.properties.summary}</li>`; 146dd6b2bfcSGreg Roach }, 147dd6b2bfcSGreg Roach }); 148dd6b2bfcSGreg Roach 149dd6b2bfcSGreg Roach if (data.features.length > 0) { 150dd6b2bfcSGreg Roach $(domObj).append(sidebar); 151dd6b2bfcSGreg Roach markers.addLayer(geoJsonLayer); 152dd6b2bfcSGreg Roach map 153dd6b2bfcSGreg Roach .addControl(new resetControl()) 154dd6b2bfcSGreg Roach .addLayer(markers); 155dd6b2bfcSGreg Roach 156dd6b2bfcSGreg Roach if (data.features.length === 1) { 157dd6b2bfcSGreg Roach map.setView(markers.getBounds().getCenter(), data.features[0].properties.zoom); 158dd6b2bfcSGreg Roach } else { 159dd6b2bfcSGreg Roach map.fitBounds(markers.getBounds().pad(0.2)) 160dd6b2bfcSGreg Roach } 161dd6b2bfcSGreg Roach } else { 162dd6b2bfcSGreg Roach map.fitWorld(); 163dd6b2bfcSGreg Roach $(domObj).append("<div class=\"bg-info text-white\">" + baseData.I18N.noData + "</div>"); 164dd6b2bfcSGreg Roach } 165dd6b2bfcSGreg Roach 166dd6b2bfcSGreg Roach $(domObj).slideDown(300); 167dd6b2bfcSGreg Roach }; 168dd6b2bfcSGreg Roach 169dd6b2bfcSGreg Roach /** 170dd6b2bfcSGreg Roach * 171dd6b2bfcSGreg Roach * @param elem 172dd6b2bfcSGreg Roach * @returns {$} 173dd6b2bfcSGreg Roach */ 174dd6b2bfcSGreg Roach 175dd6b2bfcSGreg Roach $.fn.scrollTo = function (elem) { 176dd6b2bfcSGreg Roach let _this = $(this); 177dd6b2bfcSGreg Roach _this.animate({ 178dd6b2bfcSGreg Roach scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(), 179dd6b2bfcSGreg Roach }); 180dd6b2bfcSGreg Roach return this; 181dd6b2bfcSGreg Roach }; 182dd6b2bfcSGreg Roach 183dd6b2bfcSGreg Roach return { 184dd6b2bfcSGreg Roach drawMap: function () { 185dd6b2bfcSGreg Roach _drawMap(); 186dd6b2bfcSGreg Roach _addLayer(); 187dd6b2bfcSGreg Roach 188dd6b2bfcSGreg Roach // Activate marker popup when sidebar entry clicked 189dd6b2bfcSGreg Roach $(".osm-sidebar") 190dd6b2bfcSGreg Roach .on("click", ".gchart", function (e) { 191dd6b2bfcSGreg Roach // first close any existing 192dd6b2bfcSGreg Roach map.closePopup(); 193dd6b2bfcSGreg Roach let eventId = $(this).data("id"); 194dd6b2bfcSGreg Roach //find the marker corresponding to the clicked event 195dd6b2bfcSGreg Roach let mkrLayer = markers.getLayers().filter(function (v) { 196dd6b2bfcSGreg Roach return typeof(v.feature) !== "undefined" && v.feature.id === eventId; 197dd6b2bfcSGreg Roach }); 198dd6b2bfcSGreg Roach let mkr = mkrLayer.pop(); 199dd6b2bfcSGreg Roach // Unfortunately zoomToShowLayer zooms to maxZoom 200dd6b2bfcSGreg Roach // when all marker in a cluster have exactly the 201dd6b2bfcSGreg Roach // same co-ordinates 202dd6b2bfcSGreg Roach markers.zoomToShowLayer(mkr, function (e) { 203dd6b2bfcSGreg Roach mkr.openPopup(); 204dd6b2bfcSGreg Roach }); 205dd6b2bfcSGreg Roach return false; 206dd6b2bfcSGreg Roach }) 207dd6b2bfcSGreg Roach .on("click", "a", function (e) { // stop click on a person also opening the popup 208dd6b2bfcSGreg Roach e.stopPropagation(); 209dd6b2bfcSGreg Roach }); 210dd6b2bfcSGreg Roach }, 211dd6b2bfcSGreg Roach }; 212dd6b2bfcSGreg Roach })(); 213dd6b2bfcSGreg Roach 214dd6b2bfcSGreg Roach WT_OSM.drawMap(); 215dd6b2bfcSGreg Roach</script> 216