xref: /webtrees/resources/views/modules/pedigree-map/chart.phtml (revision 8fca99e0b8c5295ab363e2becb96e3f73b4ba786)
13dcc812bSGreg Roach<?php use Fisharebest\Webtrees\I18N; ?>
23dcc812bSGreg Roach<?php use Fisharebest\Webtrees\View; ?>
33dcc812bSGreg Roach
43dcc812bSGreg Roach<div class="py-4">
53dcc812bSGreg Roach    <div class="row gchart osm-wrapper">
63dcc812bSGreg Roach        <div id="osm-map" class="col-sm-9 wt-ajax-load osm-user-map"></div>
7*8fca99e0SGreg Roach        <ul class="col-sm-3 osm-sidebar wt-page-options-value list-unstyled p-0"></ul>
83dcc812bSGreg Roach    </div>
93dcc812bSGreg Roach</div>
103dcc812bSGreg Roach
113dcc812bSGreg Roach<?php View::push('styles') ?>
123dcc812bSGreg Roach<style>
133dcc812bSGreg Roach    .osm-wrapper, .osm-user-map {
14*8fca99e0SGreg Roach        height: 75vh
153dcc812bSGreg Roach    }
163dcc812bSGreg Roach
173dcc812bSGreg Roach    .osm-sidebar {
183dcc812bSGreg Roach        height: 100%;
193dcc812bSGreg Roach        overflow-y: auto;
203dcc812bSGreg Roach        padding: 0;
213dcc812bSGreg Roach        margin: 0;
223dcc812bSGreg Roach        border: 0;
233dcc812bSGreg Roach        display: none;
243dcc812bSGreg Roach        font-size: small;
253dcc812bSGreg Roach    }
263dcc812bSGreg Roach
273dcc812bSGreg Roach    .osm-sidebar .gchart {
283dcc812bSGreg Roach        margin: 1px;
293dcc812bSGreg Roach        padding: 2px
303dcc812bSGreg Roach    }
313dcc812bSGreg Roach
323dcc812bSGreg Roach    .osm-sidebar .gchart img {
333dcc812bSGreg Roach        height: 15px;
343dcc812bSGreg Roach        width: 25px
353dcc812bSGreg Roach    }
363dcc812bSGreg Roach
373dcc812bSGreg Roach    .osm-sidebar .border-danger:hover {
383dcc812bSGreg Roach        cursor: not-allowed
393dcc812bSGreg Roach    }
403dcc812bSGreg Roach</style>
413dcc812bSGreg Roach<?php View::endpush() ?>
423dcc812bSGreg Roach
433dcc812bSGreg Roach<?php View::push('javascript') ?>
443dcc812bSGreg Roach<script type="application/javascript">
453dcc812bSGreg Roach  "use strict";
463dcc812bSGreg Roach
473dcc812bSGreg Roach  window.WT_OSM = (function() {
483dcc812bSGreg Roach    let baseData = {
493dcc812bSGreg Roach      minZoom: 2,
503dcc812bSGreg Roach      providerName: "OpenStreetMap.Mapnik",
513dcc812bSGreg Roach      providerOptions: [],
523dcc812bSGreg Roach      I18N: {
533dcc812bSGreg Roach        zoomInTitle: <?= json_encode(I18N::translate('Zoom in')) ?>,
543dcc812bSGreg Roach        zoomOutTitle: <?= json_encode(I18N::translate('Zoom out')) ?>,
553dcc812bSGreg Roach        reset: <?= json_encode(I18N::translate('Reset to initial map state')) ?>,
563dcc812bSGreg Roach        noData: <?= json_encode(I18N::translate('No mappable items')) ?>,
573dcc812bSGreg Roach        error: <?= json_encode(I18N::translate('An unknown error occurred')) ?>
583dcc812bSGreg Roach      }
593dcc812bSGreg Roach    };
603dcc812bSGreg Roach
613dcc812bSGreg Roach    let map          = null;
623dcc812bSGreg Roach    let zoom         = null;
633dcc812bSGreg Roach    let markers      = L.markerClusterGroup({
643dcc812bSGreg Roach      showCoverageOnHover: false
653dcc812bSGreg Roach    });
663dcc812bSGreg Roach
673dcc812bSGreg Roach    let resetControl = L.Control.extend({
683dcc812bSGreg Roach      options: {
693dcc812bSGreg Roach        position: 'topleft'
703dcc812bSGreg Roach      },
713dcc812bSGreg Roach
723dcc812bSGreg Roach      onAdd: function (map) {
733dcc812bSGreg Roach        let container     = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom');
743dcc812bSGreg Roach        container.onclick = function () {
753dcc812bSGreg Roach          if (zoom) {
763dcc812bSGreg Roach            map.flyTo(markers.getBounds().getCenter(), zoom);
773dcc812bSGreg Roach          } else {
783dcc812bSGreg Roach            map.flyToBounds(markers.getBounds().pad(0.2));
793dcc812bSGreg Roach          }
803dcc812bSGreg Roach          return false;
813dcc812bSGreg Roach        };
823dcc812bSGreg Roach        let anchor   = L.DomUtil.create('a', 'leaflet-control-reset', container);
833dcc812bSGreg Roach        anchor.href  = '#';
843dcc812bSGreg Roach        anchor.title = baseData.I18N.reset;
853dcc812bSGreg Roach        anchor.role  = 'button';
863dcc812bSGreg Roach        $(anchor).attr('aria-label', 'reset');
873dcc812bSGreg Roach        let image = L.DomUtil.create('i', 'fas fa-redo', anchor);
883dcc812bSGreg Roach        image.alt = baseData.I18N.reset;
893dcc812bSGreg Roach
903dcc812bSGreg Roach        return container;
913dcc812bSGreg Roach      },
923dcc812bSGreg Roach    });
933dcc812bSGreg Roach
943dcc812bSGreg Roach    /**
953dcc812bSGreg Roach     *
963dcc812bSGreg Roach     * @private
973dcc812bSGreg Roach     */
983dcc812bSGreg Roach    let _drawMap = function () {
993dcc812bSGreg Roach      map = L.map('osm-map', {
1003dcc812bSGreg Roach        center     : [0, 0],
1013dcc812bSGreg Roach        minZoom    : baseData.minZoom, // maxZoom set by leaflet-providers.js
1023dcc812bSGreg Roach        zoomControl: false, // remove default
1033dcc812bSGreg Roach      });
1043dcc812bSGreg Roach      L.tileLayer.provider(baseData.providerName, baseData.providerOptions).addTo(map);
1053dcc812bSGreg Roach      L.control.zoom({ // Add zoom with localised text
1063dcc812bSGreg Roach        zoomInTitle : baseData.I18N.zoomInTitle,
1073dcc812bSGreg Roach        zoomOutTitle: baseData.I18N.zoomOutTitle,
1083dcc812bSGreg Roach      }).addTo(map);
1093dcc812bSGreg Roach    };
1103dcc812bSGreg Roach
1113dcc812bSGreg Roach    /**
1123dcc812bSGreg Roach     *
1133dcc812bSGreg Roach     * @param reference
1143dcc812bSGreg Roach     * @param mapType
1153dcc812bSGreg Roach     * @param Generations
1163dcc812bSGreg Roach     * @private
1173dcc812bSGreg Roach     */
1183dcc812bSGreg Roach    let _addLayer = function (reference, mapType, Generations) {
1193dcc812bSGreg Roach      let geoJsonLayer;
1203dcc812bSGreg Roach      let domObj  = '.osm-sidebar';
1213dcc812bSGreg Roach      let sidebar = '';
1223dcc812bSGreg Roach
1233dcc812bSGreg Roach      $.getJSON("index.php", {
1243dcc812bSGreg Roach        route:       "module",
1253dcc812bSGreg Roach        module:      "pedigree-map",
1263dcc812bSGreg Roach        action:      "MapData",
1273dcc812bSGreg Roach        reference:   reference,
1283dcc812bSGreg Roach        type:        mapType,
1293dcc812bSGreg Roach        generations: Generations,
1303dcc812bSGreg Roach        ged:         <?= json_encode($tree->name()) ?>
1313dcc812bSGreg Roach      })
1323dcc812bSGreg Roach        .done(function (data, textStatus, jqXHR) {
1333dcc812bSGreg Roach          if (jqXHR.status === 200 && data.features.length === 1) {
1343dcc812bSGreg Roach            zoom = data.features[0].properties.zoom;
1353dcc812bSGreg Roach          }
1363dcc812bSGreg Roach          geoJsonLayer = L.geoJson(data, {
1373dcc812bSGreg Roach            pointToLayer : function (feature, latlng) {
1383dcc812bSGreg Roach              return new L.Marker(latlng, {
1393dcc812bSGreg Roach                icon : L.BeautifyIcon.icon({
1403dcc812bSGreg Roach                  icon           : feature.properties.icon['name'],
1413dcc812bSGreg Roach                  borderColor    : 'transparent',
1423dcc812bSGreg Roach                  backgroundColor: feature.valid ? feature.properties.icon['color'] : 'transparent',
1433dcc812bSGreg Roach                  iconShape      : 'marker',
1443dcc812bSGreg Roach                  textColor      : feature.valid ? 'white' : 'transparent',
1453dcc812bSGreg Roach                }),
1463dcc812bSGreg Roach                title: feature.properties.tooltip,
1473dcc812bSGreg Roach                alt  : feature.properties.tooltip,
1483dcc812bSGreg Roach                id   : feature.id
1493dcc812bSGreg Roach              })
1503dcc812bSGreg Roach                .on('popupopen', function (e) {
1513dcc812bSGreg Roach                  let sidebar = $('.osm-sidebar');
1523dcc812bSGreg Roach                  let item  = sidebar.children(".gchart[data-id=" + e.target.feature.id + "]");
1533dcc812bSGreg Roach                  item.addClass('messagebox');
1543dcc812bSGreg Roach                  sidebar.scrollTo(item);
1553dcc812bSGreg Roach                })
1563dcc812bSGreg Roach                .on('popupclose', function () {
1573dcc812bSGreg Roach                  $('.osm-sidebar').children(".gchart")
1583dcc812bSGreg Roach                    .removeClass('messagebox');
1593dcc812bSGreg Roach                });
1603dcc812bSGreg Roach            },
1613dcc812bSGreg Roach            onEachFeature: function (feature, layer) {
1623dcc812bSGreg Roach              if (feature.properties.polyline) {
1633dcc812bSGreg Roach                let pline = L.polyline(feature.properties.polyline.points, feature.properties.polyline.options);
1643dcc812bSGreg Roach                markers.addLayer(pline);
1653dcc812bSGreg Roach              }
1663dcc812bSGreg Roach              layer.bindPopup(feature.properties.summary);
1673dcc812bSGreg Roach              let myclass = feature.valid ? 'gchart' : 'border border-danger';
1683dcc812bSGreg Roach              sidebar += `<li class="${myclass}" data-id=${feature.id}>${feature.properties.summary}</li>`;
1693dcc812bSGreg Roach            },
1703dcc812bSGreg Roach          });
1713dcc812bSGreg Roach        })
1723dcc812bSGreg Roach        .fail(function (jqXHR, textStatus, errorThrown) {
1733dcc812bSGreg Roach          console.log(jqXHR, textStatus, errorThrown);
1743dcc812bSGreg Roach        })
1753dcc812bSGreg Roach        .always(function (data_jqXHR, textStatus, jqXHR_errorThrown) {
1763dcc812bSGreg Roach          switch (jqXHR_errorThrown.status) {
1773dcc812bSGreg Roach            case 200: // Success
1783dcc812bSGreg Roach              $(domObj).append(sidebar);
1793dcc812bSGreg Roach              markers.addLayer(geoJsonLayer);
1803dcc812bSGreg Roach              map
1813dcc812bSGreg Roach                .addControl(new resetControl())
1823dcc812bSGreg Roach                .addLayer(markers)
1833dcc812bSGreg Roach                .fitBounds(markers.getBounds().pad(0.2));
1843dcc812bSGreg Roach              if (zoom) {
1853dcc812bSGreg Roach                map.setView(markers.getBounds().getCenter(), zoom);
1863dcc812bSGreg Roach              }
1873dcc812bSGreg Roach              break;
1883dcc812bSGreg Roach            case 204: // No data
1893dcc812bSGreg Roach              map.fitWorld();
1903dcc812bSGreg Roach              $(domObj).append('<div class="bg-info text-white">' + baseData.I18N.noData + '</div>');
1913dcc812bSGreg Roach              break;
1923dcc812bSGreg Roach            default: // Anything else
1933dcc812bSGreg Roach              map.fitWorld();
1943dcc812bSGreg Roach              $(domObj).append('<div class="bg-danger text-white">' + baseData.I18N.error + '</div>');
1953dcc812bSGreg Roach          }
1963dcc812bSGreg Roach          $(domObj).slideDown(300);
1973dcc812bSGreg Roach        });
1983dcc812bSGreg Roach    };
1993dcc812bSGreg Roach
2003dcc812bSGreg Roach    /**
2013dcc812bSGreg Roach     *
2023dcc812bSGreg Roach     * @param elem
2033dcc812bSGreg Roach     * @returns {$}
2043dcc812bSGreg Roach     */
2053dcc812bSGreg Roach
2063dcc812bSGreg Roach    $.fn.scrollTo = function (elem) {
2073dcc812bSGreg Roach      let _this = $(this);
2083dcc812bSGreg Roach      _this.animate({
2093dcc812bSGreg Roach        scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop()
2103dcc812bSGreg Roach      });
2113dcc812bSGreg Roach      return this;
2123dcc812bSGreg Roach    };
2133dcc812bSGreg Roach
2143dcc812bSGreg Roach    /**
2153dcc812bSGreg Roach     *
2163dcc812bSGreg Roach     * @param reference string
2173dcc812bSGreg Roach     * @param mapType string
2183dcc812bSGreg Roach     * @param generations integer
2193dcc812bSGreg Roach     * @private
2203dcc812bSGreg Roach     */
2213dcc812bSGreg Roach    let _initialize = function (reference, mapType, generations) {
2223dcc812bSGreg Roach      // Activate marker popup when sidebar entry clicked
2233dcc812bSGreg Roach      $(function () {
2243dcc812bSGreg Roach        $('.osm-sidebar')
2253dcc812bSGreg Roach        // open marker popup if sidebar event is clicked
2263dcc812bSGreg Roach          .on('click', '.gchart', function (e) {
2273dcc812bSGreg Roach            // first close any existing
2283dcc812bSGreg Roach            map.closePopup();
2293dcc812bSGreg Roach            let eventId = $(this).data('id');
2303dcc812bSGreg Roach            //find the marker corresponding to the clicked event
2313dcc812bSGreg Roach            let mkrLayer = markers.getLayers().filter(function (v) {
2323dcc812bSGreg Roach              return typeof(v.feature) !== 'undefined' && v.feature.id === eventId;
2333dcc812bSGreg Roach            });
2343dcc812bSGreg Roach            let mkr = mkrLayer.pop();
2353dcc812bSGreg Roach            // Unfortunately zoomToShowLayer zooms to maxZoom
2363dcc812bSGreg Roach            // when all marker in a cluster have exactly the
2373dcc812bSGreg Roach            // same co-ordinates
2383dcc812bSGreg Roach            markers.zoomToShowLayer(mkr, function (e) {
2393dcc812bSGreg Roach              mkr.openPopup();
2403dcc812bSGreg Roach            });
2413dcc812bSGreg Roach            return false;
2423dcc812bSGreg Roach          })
2433dcc812bSGreg Roach          .on('click', 'a', function (e) { // stop click on a person also opening the popup
2443dcc812bSGreg Roach            e.stopPropagation();
2453dcc812bSGreg Roach          });
2463dcc812bSGreg Roach      });
2473dcc812bSGreg Roach
2483dcc812bSGreg Roach      _drawMap();
2493dcc812bSGreg Roach      _addLayer(reference, mapType, generations);
2503dcc812bSGreg Roach    };
2513dcc812bSGreg Roach
2523dcc812bSGreg Roach    return {
2533dcc812bSGreg Roach      /**
2543dcc812bSGreg Roach       *
2553dcc812bSGreg Roach       * @param reference string
2563dcc812bSGreg Roach       * @param mapType string
2573dcc812bSGreg Roach       * @param generations integer
2583dcc812bSGreg Roach       */
2593dcc812bSGreg Roach      drawMap: function (reference, mapType, generations) {
2603dcc812bSGreg Roach        mapType     = typeof mapType !== 'undefined' ? mapType : 'individual';
2613dcc812bSGreg Roach        generations = typeof generations !== 'undefined' ? generations : null;
2623dcc812bSGreg Roach        _initialize(reference, mapType, generations);
2633dcc812bSGreg Roach      }
2643dcc812bSGreg Roach    };
2653dcc812bSGreg Roach  })();
2663dcc812bSGreg Roach
2673dcc812bSGreg Roach    WT_OSM.drawMap(<?= json_encode($ref) ?>, <?= json_encode($type) ?>, <?= json_encode($generations ?? null) ?>);
2683dcc812bSGreg Roach</script>
2693dcc812bSGreg Roach<?php View::endpush() ?>
270