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