feat: show radius and center map

This commit is contained in:
moanos [he/him] 2025-01-01 20:14:07 +01:00
parent 3881a4f3b4
commit 8b4488484d
4 changed files with 75 additions and 15 deletions

View File

@ -6,35 +6,81 @@
<script src="{% settings_value "MAP_TILE_SERVER" %}/assets/maplibre-gl/maplibre-gl.js"></script> <script src="{% settings_value "MAP_TILE_SERVER" %}/assets/maplibre-gl/maplibre-gl.js"></script>
<link href="{% settings_value "MAP_TILE_SERVER" %}/assets/maplibre-gl/maplibre-gl.css" rel="stylesheet"/> <link href="{% settings_value "MAP_TILE_SERVER" %}/assets/maplibre-gl/maplibre-gl.css" rel="stylesheet"/>
<!-- add Turf see https://maplibre.org/maplibre-gl-js/docs/examples/draw-a-radius/ -->
<script src="{% static 'fellchensammlung/js/turf.min.js' %}"></script>
<!-- add container for the map --> <!-- add container for the map -->
<div id="map" style="width:100%;aspect-ratio:16/9"></div> <div id="map" style="width:100%;aspect-ratio:16/9"></div>
<!-- start map --> <!-- start map -->
<script> <script>
{% if map_center %}
var center = [{{ map_center.longitude }}, {{ map_center.latitude }}];
{% else %}
var center = [10.49, 50.68];
{% endif %}
let map = new maplibregl.Map({ let map = new maplibregl.Map({
container: 'map', container: 'map',
style: '{% static "fellchensammlung/map/styles/colorful.json" %}', style: '{% static "fellchensammlung/map/styles/colorful.json" %}',
center: [10.49, 50.68], center: center,
zoom: 5 zoom: 5
}).addControl(new maplibregl.NavigationControl()); }).addControl(new maplibregl.NavigationControl());
{% for adoption_notice in adoption_notices_map %} {% for adoption_notice in adoption_notices_map %}
{% if adoption_notice.location %} {% if adoption_notice.location %}
// create the popup // create the popup
const popup_{{ forloop.counter }} = new maplibregl.Popup({offset: 25}).setHTML(`{% include "fellchensammlung/partials/partial-adoption-notice-minimal.html" %}`); const popup_{{ forloop.counter }} = new maplibregl.Popup({offset: 25}).setHTML(`{% include "fellchensammlung/partials/partial-adoption-notice-minimal.html" %}`);
// create DOM element for the marker // create DOM element for the marker
const el_{{ forloop.counter }} = document.createElement('div'); const el_{{ forloop.counter }} = document.createElement('div');
el_{{ forloop.counter }}.id = 'marker_{{ forloop.counter }}'; el_{{ forloop.counter }}.id = 'marker_{{ forloop.counter }}';
el_{{ forloop.counter }}.classList.add('marker'); el_{{ forloop.counter }}.classList.add('marker');
const location_popup_{{ forloop.counter }} = [{{ adoption_notice.location.longitude | pointdecimal }}, {{ adoption_notice.location.latitude | pointdecimal }}]; const location_popup_{{ forloop.counter }} = [{{ adoption_notice.location.longitude | pointdecimal }}, {{ adoption_notice.location.latitude | pointdecimal }}];
// create the marker // create the marker
new maplibregl.Marker({element: el_{{ forloop.counter }}}) new maplibregl.Marker({element: el_{{ forloop.counter }}})
.setLngLat(location_popup_{{ forloop.counter }}) .setLngLat(location_popup_{{ forloop.counter }})
.setPopup(popup_{{ forloop.counter }}) // sets a popup on this marker .setPopup(popup_{{ forloop.counter }}) // sets a popup on this marker
.addTo(map); .addTo(map);
{% endif %} {% endif %}
{% endfor %} {% endfor %}
map.on('load', () => {
const radius = {{ search_radius }}; // kilometer
const options = {
steps: 64,
units: 'kilometers'
};
const circle = turf.circle(center, radius, options);
// Add the circle as a GeoJSON source
map.addSource('location-radius', {
type: 'geojson',
data: circle
});
// Add a fill layer with some transparency
map.addLayer({
id: 'location-radius',
type: 'fill',
source: 'location-radius',
paint: {
'fill-color': '#8CCFFF',
'fill-opacity': 0.5
}
});
// Add a line layer to draw the circle outline
map.addLayer({
id: 'location-radius-outline',
type: 'line',
source: 'location-radius',
paint: {
'line-color': '#0094ff',
'line-width': 3
}
});
});
</script> </script>

View File

@ -1,4 +1,6 @@
import logging import logging
from collections import namedtuple
import requests import requests
import json import json
from math import radians, sqrt, sin, cos, atan2 from math import radians, sqrt, sin, cos, atan2
@ -6,6 +8,8 @@ from math import radians, sqrt, sin, cos, atan2
from notfellchen import __version__ as nf_version from notfellchen import __version__ as nf_version
from notfellchen import settings from notfellchen import settings
Position = namedtuple('Position', ['latitude', 'longitude'])
def calculate_distance_between_coordinates(position1, position2): def calculate_distance_between_coordinates(position1, position2):
""" """

View File

@ -1,7 +1,7 @@
import logging import logging
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .geo import GeoAPI, LocationProxy from .geo import LocationProxy, Position
from ..forms import AdoptionNoticeSearchForm from ..forms import AdoptionNoticeSearchForm
from ..models import SearchSubscription, AdoptionNotice, AdoptionNoticeNotification, SexChoicesWithAll, Location from ..models import SearchSubscription, AdoptionNotice, AdoptionNoticeNotification, SexChoicesWithAll, Location
@ -69,6 +69,13 @@ class Search:
except ValueError: except ValueError:
self.place_not_found = True self.place_not_found = True
@property
def position(self):
if self.area_search and not self.place_not_found:
return Position(latitude=self.location.latitude, longitude=self.location.longitude)
else:
return None
def adoption_notice_fits_search(self, adoption_notice: AdoptionNotice): def adoption_notice_fits_search(self, adoption_notice: AdoptionNotice):
# Make sure sex is set and sex is not set to all (then it can be disregarded) # Make sure sex is set and sex is not set to all (then it can be disregarded)
if self.sex is not None and self.sex != SexChoicesWithAll.ALL: if self.sex is not None and self.sex != SexChoicesWithAll.ALL:

View File

@ -195,12 +195,15 @@ def search(request):
subscribed_search = search.get_subscription_or_none(request.user) subscribed_search = search.get_subscription_or_none(request.user)
else: else:
subscribed_search = None subscribed_search = None
context = {"adoption_notices": search.get_adoption_notices(), context = {"adoption_notices": search.get_adoption_notices(),
"search_form": search.search_form, "search_form": search.search_form,
"place_not_found": search.place_not_found, "place_not_found": search.place_not_found,
"subscribed_search": subscribed_search, "subscribed_search": subscribed_search,
"searched": searched, "searched": searched,
"adoption_notices_map": AdoptionNotice.get_active_ANs()} "adoption_notices_map": AdoptionNotice.get_active_ANs(),
"map_center": search.position,
"search_radius": search.max_distance,}
return render(request, 'fellchensammlung/search.html', context=context) return render(request, 'fellchensammlung/search.html', context=context)