Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7e8690360 | ||
|
|
9503f105a3 | ||
|
|
427bdca611 | ||
|
|
cf9bd5bdf6 | ||
|
|
10316bd247 | ||
|
|
ea5d41a8ad | ||
|
|
cc8833037f | ||
|
|
ce33e15a59 | ||
|
|
539aa0fcf8 | ||
|
|
880bca3501 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
.DS_Store
|
||||
__pycache__/
|
||||
*.egg-info/
|
||||
|
||||
dist-env/
|
||||
dist/
|
||||
|
||||
@@ -4,6 +4,11 @@ Adds a fully-static location field (Leaflet) to manage location-based data witho
|
||||
|
||||

|
||||
|
||||
Features:
|
||||
- Click on map or drag&drop pin to select location
|
||||
- Reset button & use map-center button
|
||||
- Display selected location or map center coordinates with zoom level
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
@@ -32,6 +37,9 @@ class Place(models.Model):
|
||||
# 'tileOptions': {
|
||||
# attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||
# },
|
||||
# 'locate': {
|
||||
# 'showPopup': False,
|
||||
# },
|
||||
})
|
||||
```
|
||||
|
||||
@@ -44,6 +52,7 @@ class Place(models.Model):
|
||||
| markerZoom | Initial zoom scale (on load) – if a marker is set. (default: `18`)
|
||||
| tileLayer | [TileLayer](https://leafletjs.com/reference.html#tilelayer) urlTemplate (default: `"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"`)
|
||||
| tileOptions | [TileLayer Options](https://leafletjs.com/reference.html#tilelayer-option) (default: `{}`)
|
||||
| locate | [Leaflet.Locate Options](https://github.com/domoritz/leaflet-locatecontrol#possible-options) (default: `{showPopup: false}`)
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from typing import NamedTuple
|
||||
from django.db import models
|
||||
from django.forms import Widget
|
||||
|
||||
import json
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class Position(NamedTuple):
|
||||
lat: float
|
||||
@@ -24,10 +26,12 @@ class MapLocationWidget(Widget):
|
||||
'leaflet/locate/L.Control.Locate.min.js',
|
||||
'map-location.js']
|
||||
|
||||
# def get_context(self, name, value, attrs):
|
||||
# context = super().get_context(name, value, attrs)
|
||||
# context['id'] = attrs.get('id')
|
||||
# return context
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super().get_context(name, value, attrs)
|
||||
context['id'] = context['widget']['attrs']['id']
|
||||
context['map_options'] = json.dumps(
|
||||
context['widget']['attrs'].get('options'))
|
||||
return context
|
||||
|
||||
|
||||
class LocationField(models.Field):
|
||||
|
||||
0
map_location/static/leaflet/locate/L.Control.Locate.css
Executable file → Normal file
0
map_location/static/leaflet/locate/L.Control.Locate.css
Executable file → Normal file
0
map_location/static/leaflet/locate/L.Control.Locate.min.js
vendored
Executable file → Normal file
0
map_location/static/leaflet/locate/L.Control.Locate.min.js
vendored
Executable file → Normal file
0
map_location/static/leaflet/locate/L.Control.Locate.min.js.map
Executable file → Normal file
0
map_location/static/leaflet/locate/L.Control.Locate.min.js.map
Executable file → Normal file
@@ -1,12 +1,19 @@
|
||||
function MapLocationInit(mapId, options = {}) {
|
||||
const valField = document.getElementById(mapId + '_value');
|
||||
const theMap = document.getElementById(mapId + '_map');
|
||||
const resetButton = document.getElementById(mapId + '_reset');
|
||||
const btnAction = document.getElementById(mapId + '_btn');
|
||||
const display = document.getElementById(mapId + '_disp');
|
||||
|
||||
function isZero(latlng) {
|
||||
return latlng.lat === 0 && latlng.lng === 0;
|
||||
}
|
||||
|
||||
function asString(latlng) {
|
||||
if (isZero(latlng)) { return ''; }
|
||||
const pos = latlng.wrap();
|
||||
return pos.lat.toFixed(6) + ',' + pos.lng.toFixed(6);
|
||||
}
|
||||
|
||||
const osm = L.tileLayer(options.tileLayer || 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', options.tileOptions);
|
||||
const map = L.map(mapId + '_map', {
|
||||
layers: [osm],
|
||||
@@ -14,10 +21,7 @@ function MapLocationInit(mapId, options = {}) {
|
||||
zoom: 2,
|
||||
...(options.map || {})
|
||||
});
|
||||
L.control.locate({
|
||||
returnToPrevBounds: true,
|
||||
showPopup: false,
|
||||
}).addTo(map);
|
||||
L.control.locate({ showPopup: false, ...(options.locate || {}) }).addTo(map);
|
||||
|
||||
function loadPos() {
|
||||
if (!valField.value) {
|
||||
@@ -26,11 +30,10 @@ function MapLocationInit(mapId, options = {}) {
|
||||
return valField.value.split(',');
|
||||
}
|
||||
}
|
||||
const marker = L.marker(loadPos(), { draggable: true }).addTo(map);
|
||||
const marker = L.marker(loadPos(), { draggable: true });
|
||||
marker.on('move', function (e) {
|
||||
const pos = map.wrapLatLng(e.latlng);
|
||||
const flag = isZero(pos);
|
||||
valField.value = flag ? null : pos.lat.toFixed(6) + ',' + pos.lng.toFixed(6);
|
||||
valField.value = asString(e.latlng);
|
||||
updatePrint();
|
||||
});
|
||||
map.on('click', function (e) {
|
||||
if (isZero(marker.getLatLng())) {
|
||||
@@ -38,20 +41,33 @@ function MapLocationInit(mapId, options = {}) {
|
||||
setMapState(false);
|
||||
}
|
||||
});
|
||||
resetButton.onclick = function () {
|
||||
marker.setLatLng([0, 0]);
|
||||
setMapState(true);
|
||||
btnAction.onclick = function () {
|
||||
const isReset = !!btnAction.dataset.reset;
|
||||
marker.setLatLng(isReset ? [0, 0] : map.getCenter());
|
||||
setMapState(isReset ? true : false);
|
||||
return false;
|
||||
};
|
||||
|
||||
function setMapState(initial) {
|
||||
theMap.style.cursor = initial ? 'crosshair' : null;
|
||||
marker.setOpacity(initial ? 0 : 1);
|
||||
initial ? marker.remove() : marker.addTo(map);
|
||||
btnAction.dataset.reset = initial ? '' : '1';
|
||||
btnAction.innerText = initial ? 'Set center' : 'Reset';
|
||||
}
|
||||
|
||||
if (isZero(marker.getLatLng())) {
|
||||
setMapState(true);
|
||||
} else {
|
||||
function updatePrint() {
|
||||
const prefix = valField.value
|
||||
? ('pin: ' + valField.value)
|
||||
: ('center: ' + asString(map.getCenter()));
|
||||
display.innerText = prefix + ' zoom: ' + map.getZoom();
|
||||
}
|
||||
map.on('zoomend', updatePrint);
|
||||
map.on('move', updatePrint);
|
||||
|
||||
setMapState(isZero(marker.getLatLng()));
|
||||
updatePrint();
|
||||
|
||||
if (valField.value) {
|
||||
map.setView(valField.value.split(','), options.markerZoom || 18);
|
||||
}
|
||||
// re-center map
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<div style="width: 100%">
|
||||
<input name="{{ widget.name }}" id="{{ widget.attrs.id }}_value" type="hidden" value="{{ widget.value|default:'' }}" />
|
||||
<div id="{{ widget.attrs.id }}_map" style="width: 100%; height: 400px"></div>
|
||||
<a href="" id="{{ widget.attrs.id }}_reset">Remove</a>
|
||||
<input name="{{ widget.name }}" id="{{id}}_value" type="hidden" value="{{ widget.value|default:'' }}" />
|
||||
<div id="{{id}}_map" style="width: 100%; height: 400px"></div>
|
||||
<a href="" id="{{id}}_btn"></a>
|
||||
<span id="{{id}}_disp" style="opacity: 0.5; margin-left: 1em"></span>
|
||||
<script>
|
||||
MapLocationInit("{{ widget.attrs.id }}", {{ widget.attrs.options|safe }});
|
||||
MapLocationInit("{{id}}", {{ map_options|safe }});
|
||||
</script>
|
||||
</div>
|
||||
BIN
screenshot.jpg
BIN
screenshot.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 135 KiB |
Reference in New Issue
Block a user