xref: /webtrees/resources/views/admin/location-edit.phtml (revision 77b78a639354e1c10922895e3e57357d6da52b86)
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