mirror of
https://github.com/Southampton-RSG/breccia-mapper.git
synced 2026-03-03 03:17:07 +00:00
feat: display organisations on map
Add buttons to toggle organisations and people Resolves #87
This commit is contained in:
@@ -84,7 +84,7 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="{% url 'people:person.map' %}" class="nav-link">Map</a>
|
<a href="{% url 'people:map' %}" class="nav-link">Map</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const marker_edge_colour = 'white';
|
|||||||
const marker_fill_colour = 'gray';
|
const marker_fill_colour = 'gray';
|
||||||
|
|
||||||
// Size of the arrow markers used on the map
|
// Size of the arrow markers used on the map
|
||||||
const marker_scale = 9;
|
const marker_scale = 7;
|
||||||
// Offset for the place type icon (multiplier for marker scale)
|
// Offset for the place type icon (multiplier for marker scale)
|
||||||
const marker_label_offset = 0.27 * marker_scale;
|
const marker_label_offset = 0.27 * marker_scale;
|
||||||
// Width and transparency for the edges of the markers
|
// Width and transparency for the edges of the markers
|
||||||
@@ -13,6 +13,7 @@ const marker_edge_width = 1.0;
|
|||||||
let map = null;
|
let map = null;
|
||||||
let selected_marker = null;
|
let selected_marker = null;
|
||||||
let selected_marker_info = null;
|
let selected_marker_info = null;
|
||||||
|
let markers = [];
|
||||||
|
|
||||||
function createMarker(map, marker_data) {
|
function createMarker(map, marker_data) {
|
||||||
// Get the lat-long position from the data
|
// Get the lat-long position from the data
|
||||||
@@ -75,7 +76,11 @@ function initMap() {
|
|||||||
for (const marker_data of markers_data) {
|
for (const marker_data of markers_data) {
|
||||||
try {
|
try {
|
||||||
const marker = createMarker(map, marker_data);
|
const marker = createMarker(map, marker_data);
|
||||||
|
marker.type = marker_data.type;
|
||||||
|
markers.push(marker);
|
||||||
|
|
||||||
bounds.extend(marker.position);
|
bounds.extend(marker.position);
|
||||||
|
|
||||||
if (markers_data.length === 1) {
|
if (markers_data.length === 1) {
|
||||||
selected_marker = marker;
|
selected_marker = marker;
|
||||||
}
|
}
|
||||||
|
|||||||
44
people/templates/people/map.html
Normal file
44
people/templates/people/map.html
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block extra_head %}
|
||||||
|
{{ map_markers|json_script:'map-markers' }}
|
||||||
|
|
||||||
|
{% load staticfiles %}
|
||||||
|
<script src="{% static 'js/map.js' %}"></script>
|
||||||
|
|
||||||
|
<script async defer
|
||||||
|
src="https://maps.googleapis.com/maps/api/js?key={{ settings.GOOGLE_MAPS_API_KEY }}&callback=initMap"
|
||||||
|
type="text/javascript"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">Map</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<h1>Map</h1>
|
||||||
|
|
||||||
|
<script type="application/javascript">
|
||||||
|
function toggleMarkerType(type) {
|
||||||
|
for (var i = 0; i < markers.length; i++) {
|
||||||
|
if (markers[i].type === type) {
|
||||||
|
markers[i].setVisible(!markers[i].getVisible());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="row mb-2">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<button class="btn btn-info btn-block" onclick="toggleMarkerType('Person');">Toggle People</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<button class="btn btn-info btn-block" onclick="toggleMarkerType('Organisation');">Toggle Organisations</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="map" style="height: 800px; width: 100%"></div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block extra_head %}
|
|
||||||
{{ map_markers|json_script:'map-markers' }}
|
|
||||||
|
|
||||||
{% load staticfiles %}
|
|
||||||
<script src="{% static 'js/map.js' %}"></script>
|
|
||||||
|
|
||||||
<script async defer
|
|
||||||
src="https://maps.googleapis.com/maps/api/js?key={{ settings.GOOGLE_MAPS_API_KEY }}&callback=initMap"
|
|
||||||
type="text/javascript"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<nav aria-label="breadcrumb">
|
|
||||||
<ol class="breadcrumb">
|
|
||||||
<li class="breadcrumb-item">
|
|
||||||
<a href="{% url 'people:person.list' %}">People</a>
|
|
||||||
</li>
|
|
||||||
<li class="breadcrumb-item active" aria-current="page">Map</li>
|
|
||||||
</ol>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<h1>Map</h1>
|
|
||||||
|
|
||||||
<div id="map" style="height: 800px; width: 100%"></div>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@@ -77,8 +77,8 @@ urlpatterns = [
|
|||||||
############
|
############
|
||||||
# Data views
|
# Data views
|
||||||
path('map',
|
path('map',
|
||||||
views.person.PersonMapView.as_view(),
|
views.map.MapView.as_view(),
|
||||||
name='person.map'),
|
name='map'),
|
||||||
|
|
||||||
path('network',
|
path('network',
|
||||||
views.network.NetworkView.as_view(),
|
views.network.NetworkView.as_view(),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ Views for displaying or manipulating models within the `people` app.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
|
map,
|
||||||
network,
|
network,
|
||||||
organisation,
|
organisation,
|
||||||
person,
|
person,
|
||||||
@@ -11,6 +12,7 @@ from . import (
|
|||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
'map',
|
||||||
'network',
|
'network',
|
||||||
'organisation',
|
'organisation',
|
||||||
'person',
|
'person',
|
||||||
|
|||||||
52
people/views/map.py
Normal file
52
people/views/map.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import typing
|
||||||
|
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.db.models import QuerySet
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
|
from people import forms, models, permissions
|
||||||
|
|
||||||
|
|
||||||
|
def get_map_data(obj: typing.Union[models.Person, models.Organisation]) -> typing.Dict[str, typing.Any]:
|
||||||
|
"""Prepare data to mark people or organisations on a map."""
|
||||||
|
answer_set = obj.current_answers
|
||||||
|
organisation = getattr(answer_set, 'organisation', None)
|
||||||
|
|
||||||
|
try:
|
||||||
|
country = answer_set.country_of_residence.name
|
||||||
|
|
||||||
|
except AttributeError:
|
||||||
|
country = None
|
||||||
|
|
||||||
|
return {
|
||||||
|
'name': obj.name,
|
||||||
|
'lat': getattr(answer_set, 'latitude', None),
|
||||||
|
'lng': getattr(answer_set, 'longitude', None),
|
||||||
|
'organisation': getattr(organisation, 'name', None),
|
||||||
|
'org_lat': getattr(organisation, 'latitude', None),
|
||||||
|
'org_lng': getattr(organisation, 'longitude', None),
|
||||||
|
'country': country,
|
||||||
|
'url': obj.get_absolute_url(),
|
||||||
|
'type': type(obj).__name__,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MapView(LoginRequiredMixin, TemplateView):
|
||||||
|
"""View displaying a map of :class:`Person` and :class:`Organisation` locations."""
|
||||||
|
template_name = 'people/map.html'
|
||||||
|
|
||||||
|
def get_context_data(self,
|
||||||
|
**kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
map_markers = []
|
||||||
|
|
||||||
|
map_markers.extend(
|
||||||
|
get_map_data(person) for person in models.Person.objects.all())
|
||||||
|
map_markers.extend(
|
||||||
|
get_map_data(org) for org in models.Organisation.objects.all())
|
||||||
|
context['map_markers'] = map_markers
|
||||||
|
|
||||||
|
return context
|
||||||
@@ -5,11 +5,11 @@ Views for displaying or manipulating instances of :class:`Person`.
|
|||||||
import typing
|
import typing
|
||||||
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.generic import CreateView, DetailView, ListView, UpdateView
|
from django.views.generic import CreateView, DetailView, ListView, UpdateView
|
||||||
|
|
||||||
from people import forms, models, permissions
|
from people import forms, models, permissions
|
||||||
|
from .map import get_map_data
|
||||||
|
|
||||||
|
|
||||||
class PersonCreateView(LoginRequiredMixin, CreateView):
|
class PersonCreateView(LoginRequiredMixin, CreateView):
|
||||||
@@ -130,42 +130,3 @@ class PersonUpdateView(permissions.UserIsLinkedPersonMixin, UpdateView):
|
|||||||
answer_set.save()
|
answer_set.save()
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def get_map_data(person: models.Person) -> typing.Dict[str, typing.Any]:
|
|
||||||
"""Prepare data to mark people on a map."""
|
|
||||||
answer_set = person.current_answers
|
|
||||||
organisation = getattr(answer_set, 'organisation', None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
country = answer_set.country_of_residence.name
|
|
||||||
|
|
||||||
except AttributeError:
|
|
||||||
country = None
|
|
||||||
|
|
||||||
return {
|
|
||||||
'name': person.name,
|
|
||||||
'lat': getattr(answer_set, 'latitude', None),
|
|
||||||
'lng': getattr(answer_set, 'longitude', None),
|
|
||||||
'organisation': getattr(organisation, 'name', None),
|
|
||||||
'org_lat': getattr(organisation, 'latitude', None),
|
|
||||||
'org_lng': getattr(organisation, 'longitude', None),
|
|
||||||
'country': country,
|
|
||||||
'url': reverse('people:person.detail', kwargs={'pk': person.pk})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PersonMapView(LoginRequiredMixin, ListView):
|
|
||||||
"""View displaying a map of :class:`Person` locations."""
|
|
||||||
model = models.Person
|
|
||||||
template_name = 'people/person/map.html'
|
|
||||||
|
|
||||||
def get_context_data(self,
|
|
||||||
**kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
|
|
||||||
context['map_markers'] = [
|
|
||||||
get_map_data(person) for person in self.object_list
|
|
||||||
]
|
|
||||||
|
|
||||||
return context
|
|
||||||
|
|||||||
Reference in New Issue
Block a user