1<?php 2 3use Fisharebest\Webtrees\Http\RequestHandlers\MapDataList; 4use Fisharebest\Webtrees\I18N; 5use Fisharebest\Webtrees\PlaceLocation; 6use Fisharebest\Webtrees\View; 7 8/** 9 * @var array<string,string> $breadcrumbs 10 * @var string $latitude 11 * @var string $longitude 12 * @var PlaceLocation $location 13 * @var array<array<float>> $map_bounds 14 * @var <array<float> $marker_position 15 * @var PlaceLocation $parent 16 * @var mixed $provider 17 * @var string $title 18 */ 19 20?> 21 22<?= view('components/breadcrumbs', ['links' => $breadcrumbs]) ?> 23 24<h1><?= $title ?></h1> 25 26<div class="form-group row"> 27 <div class="col-sm-12"> 28 <div id="osm-map" class="wt-ajax-load col-sm-12 osm-admin-map" dir="ltr"></div> 29 </div> 30</div> 31 32<form method="post"> 33 <?= csrf_field() ?> 34 <input type="hidden" name="place_id" value="<?= e($location->id()) ?>"> 35 36 <div class="form-group row"> 37 <label class="col-form-label col-sm-1" for="new_place_name"> 38 <?= I18N::translate('Place') ?> 39 </label> 40 <div class="col-sm-3"> 41 <input type="text" id="new_place_name" name="new_place_name" value="<?= e($location->locationName()) ?>" class="form-control" required maxlength="120" pattern="[^,]+"> 42 </div> 43 44 <input type="hidden" name="icon" id="icon" class="form-control" value="<?= e($location->icon()) ?>"> 45 </div> 46 47 <div class="form-group row"> 48 <label class="col-form-label col-sm-1" for="new_place_lati"> 49 <?= I18N::translate('Latitude') ?> 50 </label> 51 <div class="col-sm-3"> 52 <div class="input-group"> 53 <input type="text" dir="ltr" id="new_place_lati" class="editable form-control" name="new_place_lati" required placeholder="<?= I18N::translate('degrees') ?>" value="<?= e($latitude) ?>"> 54 </div> 55 </div> 56 </div> 57 58 <div class="form-group row"> 59 <label class="col-form-label col-sm-1" for="new_place_long"> 60 <?= I18N::translate('Longitude') ?> 61 </label> 62 <div class="col-sm-3"> 63 <div class="input-group"> 64 <input type="text" dir="ltr" id="new_place_long" class="editable form-control" name="new_place_long" required placeholder="<?= I18N::translate('degrees') ?>" value="<?= e($longitude) ?>"> 65 </div> 66 </div> 67 68 <input type="hidden" id="new_zoom_factor" name="new_zoom_factor" value="<?= e($location->zoom()) ?>" class="form-control"> 69 </div> 70 71 <div class="form-group row"> 72 <div class="col-sm-10 offset-sm-1"> 73 <button class="btn btn-primary" type="submit"> 74 <?= /* I18N: A button label. */ 75 I18N::translate('save') 76 ?> 77 </button> 78 <a class="btn btn-secondary" href="<?= e(route(MapDataList::class, ['parent_id' => $parent->id()])) ?>"> 79 <?= I18N::translate('cancel') ?> 80 </a> 81 </div> 82 </div> 83</form> 84 85<?php View::push('styles') ?> 86<style> 87 .osm-admin-map { 88 height: 55vh; 89 border: 1px solid darkGrey 90 } 91</style> 92<?php View::endpush() ?> 93 94<?php View::push('javascript') ?> 95<script> 96 'use strict'; 97 98 window.WT_OSM_ADMIN = (function () { 99 const minZoom = 2; 100 101 let provider = <?= json_encode($provider) ?>; 102 let map = null; 103 let add_place = <?= json_encode($location->id() === 0) ?>; 104 105 // map components 106 107 // postcss_image_inliner breaks the autodetection of image paths. 108 L.Icon.Default.imagePath = <?= json_encode(asset('css/images/')) ?>; 109 110 // draggable marker 111 let marker = L.marker(<?= json_encode($marker_position) ?>, { 112 draggable: true, 113 }) 114 .on('dragend', function () { 115 let coords = marker.getLatLng(); 116 map.panTo(coords); 117 $('#new_place_lati').val(Number(coords.lat).toFixed(5)); 118 $('#new_place_long').val(Number(coords.lng).toFixed(5)); 119 $('#new_zoom_factor').val(Number(map.getZoom())); 120 }); 121 122 //reset map to initial state 123 let resetControl = L.Control.extend({ 124 options: { 125 position: 'topleft' 126 }, 127 onAdd: function (map) { 128 let container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom'); 129 container.onclick = function () { 130 map.fitBounds(<?= json_encode($map_bounds) ?>, {padding: [50, 30]}); 131 marker.setLatLng(<?= json_encode([$location->latitude(), $location->longitude()]) ?>); 132 $('form').trigger('reset'); 133 return false; 134 }; 135 let reset = <?= json_encode(I18N::translate('Reset to initial map state')) ?>; 136 let anchor = L.DomUtil.create('a', 'leaflet-control-reset', container); 137 anchor.setAttribute('aria-label', reset); 138 anchor.href = '#'; 139 anchor.title = reset; 140 anchor.role = 'button'; 141 let image = L.DomUtil.create('i', 'fas fa-redo', anchor); 142 image.alt = reset; 143 144 return container; 145 } 146 }); 147 148 // zoom control with localised text 149 let zoomCtl = new L.control.zoom({ 150 zoomInTitle: <?= json_encode(I18N::translate('Zoom in')) ?>, 151 zoomOutTitle: <?= json_encode(I18N::translate('Zoom out')) ?>, 152 }); 153 154 // Geocoder (place lookup) 155 let geocoder = new L.Control.geocoder({ 156 defaultMarkGeocode: false, 157 expand: 'click', 158 showResultIcons: true, 159 query: <?= json_encode($location->locationName()) ?>, 160 placeholder: <?= json_encode(I18N::translate('Place')) ?>, 161 errorMessage: <?= json_encode(I18N::translate('Nothing found.')) ?>, 162 iconLabel: <?= json_encode(I18N::translate('Search')) ?> 163 }) 164 .on('markgeocode', function (result) { 165 let coords = result.geocode.center; 166 let place = result.geocode.name.split(',', 1); 167 marker.setLatLng(coords); 168 map.panTo(coords); 169 if (add_place) { 170 $('#new_place_name').val(place.shift()); 171 } 172 $('#new_place_lati').val(Number(coords.lat).toFixed(5)); 173 $('#new_place_long').val(Number(coords.lng).toFixed(5)); 174 $('#new_zoom_factor').val(Number(map.getZoom())); 175 }); 176 177 /** 178 * 179 * @private 180 */ 181 $(function () { 182 // geocoder button tooltip 183 $('.leaflet-control-geocoder-icon') 184 .attr('title', <?= json_encode(I18N::translate('Search')) ?>); 185 186 $('.editable').on('change', function () { 187 let lat = $('#new_place_lati').val(); 188 let lng = $('#new_place_long').val(); 189 marker.setLatLng([lat, lng]); 190 map.panTo([lat, lng]); 191 }); 192 }); 193 194 // Create the map with all controls and layers 195 map = L.map('osm-map', { 196 minZoom: minZoom, // maxZoom set by leaflet-providers.js 197 zoomControl: false, // remove default 198 }) 199 .addControl(new resetControl()) 200 .addControl(zoomCtl) 201 .addControl(geocoder) 202 .addLayer(marker) 203 .addLayer(L.tileLayer(provider.url, provider.options)) 204 .fitBounds(<?= json_encode($map_bounds) ?>, {padding: [50, 30]}) 205 .on('zoomend', function () { 206 $('#new_zoom_factor').val(map.getZoom()); 207 }); 208 209 return 'Leaflet map interface for webtrees-2'; 210 })(); 211</script> 212<?php View::endpush() ?> 213