xref: /webtrees/resources/views/admin/location-edit.phtml (revision 68136c4f6e10efb8b69e797562a7efe2d96fde70)
1<?php
2
3use Fisharebest\Webtrees\I18N;
4use Fisharebest\Webtrees\View;
5
6?>
7
8<?= view('components/breadcrumbs', ['links' => $breadcrumbs]) ?>
9
10<h1><?= $title ?></h1>
11
12<div class="form-group row">
13    <div class="col-sm-10 offset-sm-1">
14        <div id="osm-map" class="wt-ajax-load col-sm-12 osm-admin-map" dir="ltr"></div>
15    </div>
16</div>
17
18<form method="post">
19    <?= csrf_field() ?>
20    <input type="hidden" name="place_id" value="<?= e($place_id) ?>">
21
22    <div class="form-group row">
23        <label class="col-form-label col-sm-1" for="new_place_name">
24            <?= I18N::translate('Place') ?>
25        </label>
26        <div class="col-sm-5">
27            <input type="text" id="new_place_name" name="new_place_name" value="<?= e($location->locationName()) ?>"
28            class="form-control" required>
29        </div>
30        <label class="col-form-label col-sm-1" for="icon">
31            <?= I18N::translate('Flag') ?>
32        </label>
33        <div class="col-sm-4">
34            <div class="input-group" dir="ltr">
35                <input type="text" name="icon" id="icon" class="form-control" value="<?= e($location->icon()) ?>">
36            </div>
37        </div>
38    </div>
39
40    <div class="form-group row">
41        <label class="col-form-label col-sm-1" for="new_place_lati">
42            <?= I18N::translate('Latitude') ?>
43        </label>
44        <div class="col-sm-3">
45            <div class="input-group">
46                <input type="text" dir="ltr" id="new_place_lati" class="editable form-control" name="new_place_lati" required
47                placeholder="<?= I18N::translate('degrees') ?>" value="<?= e($lat) ?>"
48                >
49            </div>
50        </div>
51
52        <label class="col-form-label col-sm-1" for="new_place_long">
53            <?= I18N::translate('Longitude') ?>
54        </label>
55        <div class="col-sm-3">
56            <div class="input-group">
57                <input type="text" dir="ltr" id="new_place_long" class="editable form-control" name="new_place_long" required
58                placeholder="<?= I18N::translate('degrees') ?>" value="<?= e($lng) ?>"
59                >
60            </div>
61        </div>
62        <label class="col-form-label col-sm-1" for="new_zoom_factor">
63            <?= I18N::translate('Zoom') ?>
64        </label>
65        <div class="col-sm-2">
66            <input type="text" id="new_zoom_factor" name="new_zoom_factor" value="<?= e($location->zoom()) ?>"
67            class="form-control" required readonly>
68        </div>
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('map-data', ['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
98window.WT_OSM_ADMIN = (function () {
99  const minZoom = 2;
100
101  let provider = <?= json_encode($provider) ?>;
102  let data = <?= json_encode($data) ?>;
103  let map = null;
104  let add_place = <?= json_encode($place_id === 0) ?>;
105
106  // map components
107
108  // postcss_image_inliner breaks the autodetection of image paths.
109  L.Icon.Default.imagePath = <?= json_encode(asset('css/images/')) ?>;
110
111  // draggable marker
112  let marker = L.marker(data.coordinates, {
113    draggable: true,
114  })
115    .on('dragend', function (e) {
116      let coords = marker.getLatLng();
117      map.panTo(coords);
118      _update_Controls({
119        place:  '',
120        coords: coords,
121        zoom:   map.getZoom(),
122      });
123    });
124
125  //reset map to initial state
126  let resetControl = L.Control.extend({
127    options: {
128      position: 'topleft'
129    },
130    onAdd:   function (map) {
131      let container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom');
132      container.onclick = function () {
133        map.setView(data.coordinates, data.zoom);
134        marker.setLatLng(data.coordinates);
135        $('form').trigger('reset');
136        return false;
137      };
138      let reset = <?= json_encode(I18N::translate('Reset to initial map state')) ?>;
139      let anchor = L.DomUtil.create('a', 'leaflet-control-reset', container);
140      anchor.setAttribute('aria-label', reset);
141      anchor.href = '#';
142      anchor.title = reset;
143      anchor.role = 'button';
144      let image = L.DomUtil.create('i', 'fas fa-redo', anchor);
145      image.alt = reset;
146
147      return container;
148    }
149  });
150
151  // zoom control with localised text
152  let zoomCtl = new L.control.zoom({
153    zoomInTitle:  <?= json_encode(I18N::translate('Zoom in')) ?>,
154    zoomOutTitle: <?= json_encode(I18N::translate('Zoom out')) ?>,
155  });
156
157  // Geocoder (place lookup)
158  let geocoder = new L.Control.geocoder({
159    defaultMarkGeocode: false,
160    expand:             'click',
161    showResultIcons:    true,
162    query:              '<?= e($location->locationName()) ?>',
163    placeholder: <?= json_encode(I18N::translate('Place')) ?>,
164    errorMessage: <?= json_encode(I18N::translate('Nothing found.')) ?>,
165    iconLabel: <?= json_encode(I18N::translate('Search')) ?>
166  })
167    .on('markgeocode', function (result) {
168      let coords = result.geocode.center;
169      let place = result.geocode.name.split(',', 1);
170      marker.setLatLng(coords);
171      map.panTo(coords);
172      _update_Controls({
173        place:  place.shift(),
174        coords: coords,
175        zoom:   map.getZoom(),
176      });
177    });
178
179  /**
180   *
181   * @param newData
182   * @private
183   */
184  let _update_Controls = function (newData) {
185
186    if (add_place) {
187      $('#new_place_name').val(newData.place);
188    }
189    $('#new_place_lati').val(Number(newData.coords.lat).toFixed(5)); // 5 decimal places (about 1 metre accuracy)
190    $('#new_place_long').val(Number(newData.coords.lng).toFixed(5));
191    $('#new_zoom_factor').val(Number(newData.zoom));
192  };
193
194  /**
195   *
196   * @private
197   */
198  $(function () {
199    // geocoder button tooltip
200    $('.leaflet-control-geocoder-icon')
201      .attr('title', <?= json_encode(I18N::translate('Search')) ?>);
202
203    $('.editable').on('change', function (e) {
204      let lat = $('#new_place_lati').val();
205      let lng = $('#new_place_long').val();
206      marker.setLatLng([lat, lng]);
207      map.panTo([lat, lng]);
208    });
209  });
210
211  // Create the map with all controls and layers
212  map = L.map('osm-map', {
213    minZoom:     minZoom, // maxZoom set by leaflet-providers.js
214    zoomControl: false,   // remove default
215  })
216    .addControl(new resetControl())
217    .addControl(zoomCtl)
218    .addControl(geocoder)
219    .addLayer(marker)
220    .addLayer(L.tileLayer(provider.url, provider.options))
221    .setView(data.coordinates, data.zoom)
222    .on('zoomend', function (e) {
223      $('#new_zoom_factor').val(map.getZoom());
224      map.panTo(marker.getLatLng());
225    });
226
227  return 'Leaflet map interface for webtrees-2';
228})();
229</script>
230<?php View::endpush() ?>
231