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