xref: /webtrees/resources/views/modules/pedigree-map/chart.phtml (revision 985793245869e051c3438a56b44f5662c8529182)
19ec21bfdSGreg Roach<?php
2d70512abSGreg Roach
39ec21bfdSGreg Roachuse Fisharebest\Webtrees\I18N;
49ec21bfdSGreg Roachuse Fisharebest\Webtrees\View;
59ec21bfdSGreg Roach
69ec21bfdSGreg Roach/**
7*98579324SDavid Drury * @var array $data
8*98579324SDavid Drury * @var array $provider
99ec21bfdSGreg Roach */
109ec21bfdSGreg Roach?>
119ec21bfdSGreg Roach
123dcc812bSGreg Roach<div class="py-4">
133dcc812bSGreg Roach    <div class="row gchart osm-wrapper">
14a2e602dbSGreg Roach        <div id="osm-map" class="col-sm-9 wt-ajax-load osm-user-map" dir="ltr"></div>
150d123f04SDavid Drury        <ul class="col-sm-3 osm-sidebar wt-page-options-value list-unstyled px-md-1"></ul>
163dcc812bSGreg Roach    </div>
173dcc812bSGreg Roach</div>
183dcc812bSGreg Roach
193dcc812bSGreg Roach<?php View::push('styles') ?>
203dcc812bSGreg Roach<style>
213dcc812bSGreg Roach    .osm-wrapper, .osm-user-map {
220d123f04SDavid Drury        height: 70vh
233dcc812bSGreg Roach    }
243dcc812bSGreg Roach
253dcc812bSGreg Roach    .osm-sidebar {
263dcc812bSGreg Roach        height: 100%;
273dcc812bSGreg Roach        overflow-y: auto;
283dcc812bSGreg Roach        font-size: small;
293dcc812bSGreg Roach    }
303dcc812bSGreg Roach
313dcc812bSGreg Roach</style>
323dcc812bSGreg Roach<?php View::endpush() ?>
333dcc812bSGreg Roach
343dcc812bSGreg Roach<?php View::push('javascript') ?>
353dcc812bSGreg Roach<script type="application/javascript">
363dcc812bSGreg Roach"use strict";
373dcc812bSGreg Roach
383dcc812bSGreg Roachwindow.WT_OSM = (function() {
39*98579324SDavid Drury    const minZoom = 2;
403dcc812bSGreg Roach
413dcc812bSGreg Roach    let map      = null;
423dcc812bSGreg Roach    let zoom     = null;
430d123f04SDavid Drury    let sidebar  = $('.osm-sidebar');
44*98579324SDavid Drury    let provider = <?= json_encode($provider) ?>;
45*98579324SDavid Drury
46*98579324SDavid Drury    // Map components
473dcc812bSGreg Roach    let markers = L.markerClusterGroup({
483dcc812bSGreg Roach        showCoverageOnHover: false
493dcc812bSGreg Roach    });
503dcc812bSGreg Roach
513dcc812bSGreg Roach    let resetControl = L.Control.extend({
523dcc812bSGreg Roach        options: {
533dcc812bSGreg Roach            position: 'topleft'
543dcc812bSGreg Roach        },
553dcc812bSGreg Roach        onAdd: function(map) {
563dcc812bSGreg Roach            let container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom');
573dcc812bSGreg Roach            container.onclick = function() {
583dcc812bSGreg Roach                if (zoom) {
593dcc812bSGreg Roach                    map.flyTo(markers.getBounds().getCenter(), zoom);
603dcc812bSGreg Roach                } else {
613dcc812bSGreg Roach                    map.flyToBounds(markers.getBounds().pad(0.2));
623dcc812bSGreg Roach                }
6336409e74SGreg Roach                sidebar.scrollTo(sidebar.children(":first"));
6436409e74SGreg Roach
653dcc812bSGreg Roach                return false;
663dcc812bSGreg Roach            };
67*98579324SDavid Drury            let reset    = <?= json_encode(I18N::translate('Reload map')) ?>;
683dcc812bSGreg Roach            let anchor   = L.DomUtil.create('a', 'leaflet-control-reset', container);
69*98579324SDavid Drury            anchor.setAttribute('aria-label', reset);
703dcc812bSGreg Roach            anchor.href  = '#';
71a4321966SGreg Roach            anchor.title = reset;
723dcc812bSGreg Roach            anchor.role  = 'button';
733dcc812bSGreg Roach            let image    = L.DomUtil.create('i', 'fas fa-redo', anchor);
74a4321966SGreg Roach            image.alt    = reset;
753dcc812bSGreg Roach
763dcc812bSGreg Roach            return container;
77*98579324SDavid Drury        }
78*98579324SDavid Drury    });
79*98579324SDavid Drury
80*98579324SDavid Drury    // Zoom control with localised text
81*98579324SDavid Drury    let newZoomControl = new L.control.zoom({
82*98579324SDavid Drury        zoomInTitle : <?= json_encode(I18N::translate('Zoom in')) ?>,
83*98579324SDavid Drury        zoomOutTitle: <?= json_encode(I18N::translate('Zoom out')) ?>,
843dcc812bSGreg Roach    });
853dcc812bSGreg Roach
863dcc812bSGreg Roach    /**
873dcc812bSGreg Roach     *
883dcc812bSGreg Roach     * @private
893dcc812bSGreg Roach     */
903dcc812bSGreg Roach    let _drawMap = function() {
913dcc812bSGreg Roach        map = L.map('osm-map', {
923dcc812bSGreg Roach            center     : [0, 0],
93*98579324SDavid Drury            minZoom    : minZoom,   // maxZoom set by leaflet-providers.js
943dcc812bSGreg Roach            zoomControl: false,     // remove default
95*98579324SDavid Drury        })
96*98579324SDavid Drury        .addControl(new resetControl())
97*98579324SDavid Drury        .addControl(newZoomControl)
98*98579324SDavid Drury        .addLayer(L.tileLayer.provider(provider.name, provider.options))
993dcc812bSGreg Roach    };
1003dcc812bSGreg Roach
1013dcc812bSGreg Roach    /**
1023dcc812bSGreg Roach     * @private
1033dcc812bSGreg Roach     */
104*98579324SDavid Drury    let _buildMapData = function() {
1050d123f04SDavid Drury        let sidebar_content = '';
106*98579324SDavid Drury        let geoJson_data    = <?= json_encode($data) ?>;
1073dcc812bSGreg Roach
108*98579324SDavid Drury        if (geoJson_data.features.length === 0) {
109*98579324SDavid Drury            map.fitWorld();
110*98579324SDavid Drury            sidebar_content += '<div class="bg-info text-white text-center">' + <?= json_encode(I18N::translate('Nothing to show')) ?> + '</div>';
111*98579324SDavid Drury        } else {
112*98579324SDavid Drury            if (geoJson_data.features.length === 1) {
113*98579324SDavid Drury                //fudge factor - maps zooms to maximum permitted otherwise
114*98579324SDavid Drury                zoom = geoJson_data.features[0].properties.zoom;
1153dcc812bSGreg Roach            }
116*98579324SDavid Drury            let geoJsonLayer = L.geoJson(geoJson_data, {
1173dcc812bSGreg Roach                pointToLayer: function(feature, latlng) {
1183dcc812bSGreg Roach                    return new L.Marker(latlng, {
1193dcc812bSGreg Roach                            icon: L.BeautifyIcon.icon({
1203130efd4SGreg Roach                                icon           : 'bullseye fas',
1213dcc812bSGreg Roach                                borderColor    : 'transparent',
1223130efd4SGreg Roach                                backgroundColor: feature.properties.iconcolor,
1233dcc812bSGreg Roach                                iconShape      : 'marker',
1243130efd4SGreg Roach                                textColor      : 'white',
1253dcc812bSGreg Roach                            }),
1263dcc812bSGreg Roach                            title: feature.properties.tooltip,
1273dcc812bSGreg Roach                            alt  : feature.properties.tooltip,
1283dcc812bSGreg Roach                            id   : feature.id
1293dcc812bSGreg Roach                        })
1303dcc812bSGreg Roach                        .on('popupopen', function(e) {
1313dcc812bSGreg Roach                            let item = sidebar.children(".gchart[data-id=" + e.target.feature.id + "]");
1323dcc812bSGreg Roach                            item.addClass('messagebox');
1333dcc812bSGreg Roach                            sidebar.scrollTo(item);
1343dcc812bSGreg Roach                        })
1353dcc812bSGreg Roach                        .on('popupclose', function() {
136*98579324SDavid Drury                            sidebar.children(".gchart").removeClass('messagebox');
1373dcc812bSGreg Roach                        });
1383dcc812bSGreg Roach                },
1393dcc812bSGreg Roach                onEachFeature: function(feature, layer) {
1403dcc812bSGreg Roach                    if (feature.properties.polyline) {
1413dcc812bSGreg Roach                        let pline = L.polyline(feature.properties.polyline.points, feature.properties.polyline.options);
1423dcc812bSGreg Roach                        markers.addLayer(pline);
1433dcc812bSGreg Roach                    }
1443dcc812bSGreg Roach                    layer.bindPopup(feature.properties.summary);
1450d123f04SDavid Drury                    sidebar_content += `<li class="gchart px-md-2" data-id=${feature.id}>${feature.properties.summary}</li>`;
146*98579324SDavid Drury                }
1473dcc812bSGreg Roach            });
1483dcc812bSGreg Roach            markers.addLayer(geoJsonLayer);
149*98579324SDavid Drury            map.addLayer(markers);
1503dcc812bSGreg Roach            if (zoom) {
1513dcc812bSGreg Roach                map.setView(markers.getBounds().getCenter(), zoom);
152*98579324SDavid Drury            } else {
153*98579324SDavid Drury                map.fitBounds(markers.getBounds().pad(0.2));
1543dcc812bSGreg Roach            }
1553dcc812bSGreg Roach        }
156*98579324SDavid Drury        sidebar.append(sidebar_content);
1573dcc812bSGreg Roach    };
1583dcc812bSGreg Roach
1593dcc812bSGreg Roach    /**
1603dcc812bSGreg Roach     *
1613dcc812bSGreg Roach     * @param elem
1623dcc812bSGreg Roach     * @returns {$}
1633dcc812bSGreg Roach     */
1643dcc812bSGreg Roach
1653dcc812bSGreg Roach    $.fn.scrollTo = function(elem) {
1663dcc812bSGreg Roach        let _this = $(this);
1673dcc812bSGreg Roach        _this.animate({
1683dcc812bSGreg Roach            scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop()
1693dcc812bSGreg Roach        });
1703dcc812bSGreg Roach        return this;
1713dcc812bSGreg Roach    };
1723dcc812bSGreg Roach
1733dcc812bSGreg Roach    // Activate marker popup when sidebar entry clicked
1743dcc812bSGreg Roach    $(function() {
1750d123f04SDavid Drury        sidebar
1763dcc812bSGreg Roach            // open marker popup if sidebar event is clicked
1773dcc812bSGreg Roach            .on('click', '.gchart', function(e) {
1783dcc812bSGreg Roach                // first close any existing
1793dcc812bSGreg Roach                map.closePopup();
1803dcc812bSGreg Roach                let eventId = $(this).data('id');
1813dcc812bSGreg Roach                //find the marker corresponding to the clicked event
1823dcc812bSGreg Roach                let mkrLayer = markers.getLayers().filter(function(v) {
1833dcc812bSGreg Roach                    return typeof(v.feature) !== 'undefined' && v.feature.id === eventId;
1843dcc812bSGreg Roach                });
1853dcc812bSGreg Roach                let mkr = mkrLayer.pop();
1863dcc812bSGreg Roach                // Unfortunately zoomToShowLayer zooms to maxZoom
1873dcc812bSGreg Roach                // when all marker in a cluster have exactly the
1883dcc812bSGreg Roach                // same co-ordinates
1893dcc812bSGreg Roach                markers.zoomToShowLayer(mkr, function(e) {
1903dcc812bSGreg Roach                    mkr.openPopup();
1913dcc812bSGreg Roach                });
192*98579324SDavid Drury
1933dcc812bSGreg Roach                return false;
1943dcc812bSGreg Roach            })
1953dcc812bSGreg Roach            .on('click', 'a', function(e) { // stop click on a person also opening the popup
1963dcc812bSGreg Roach                e.stopPropagation();
1973dcc812bSGreg Roach            });
1983dcc812bSGreg Roach    });
1993dcc812bSGreg Roach
2003dcc812bSGreg Roach    _drawMap();
201*98579324SDavid Drury    _buildMapData();
2023dcc812bSGreg Roach
203*98579324SDavid Drury    return "Leaflet map interface for webtrees-2";
2043dcc812bSGreg Roach})();
2053dcc812bSGreg Roach</script>
2063dcc812bSGreg Roach<?php View::endpush() ?>
207