feat: display organisations on map

Add buttons to toggle organisations and people

Resolves #87
This commit is contained in:
James Graham
2021-03-09 14:55:31 +00:00
parent 6670a87a52
commit 989c8141b3
8 changed files with 108 additions and 72 deletions

View File

@@ -84,7 +84,7 @@
</li>
<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 class="nav-item">

View File

@@ -3,7 +3,7 @@ const marker_edge_colour = 'white';
const marker_fill_colour = 'gray';
// 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)
const marker_label_offset = 0.27 * marker_scale;
// Width and transparency for the edges of the markers
@@ -13,6 +13,7 @@ const marker_edge_width = 1.0;
let map = null;
let selected_marker = null;
let selected_marker_info = null;
let markers = [];
function createMarker(map, marker_data) {
// Get the lat-long position from the data
@@ -75,7 +76,11 @@ function initMap() {
for (const marker_data of markers_data) {
try {
const marker = createMarker(map, marker_data);
marker.type = marker_data.type;
markers.push(marker);
bounds.extend(marker.position);
if (markers_data.length === 1) {
selected_marker = marker;
}

View 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 %}

View File

@@ -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 %}

View File

@@ -77,8 +77,8 @@ urlpatterns = [
############
# Data views
path('map',
views.person.PersonMapView.as_view(),
name='person.map'),
views.map.MapView.as_view(),
name='map'),
path('network',
views.network.NetworkView.as_view(),

View File

@@ -3,6 +3,7 @@ Views for displaying or manipulating models within the `people` app.
"""
from . import (
map,
network,
organisation,
person,
@@ -11,6 +12,7 @@ from . import (
__all__ = [
'map',
'network',
'organisation',
'person',

52
people/views/map.py Normal file
View 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

View File

@@ -5,11 +5,11 @@ Views for displaying or manipulating instances of :class:`Person`.
import typing
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from django.utils import timezone
from django.views.generic import CreateView, DetailView, ListView, UpdateView
from people import forms, models, permissions
from .map import get_map_data
class PersonCreateView(LoginRequiredMixin, CreateView):
@@ -130,42 +130,3 @@ class PersonUpdateView(permissions.UserIsLinkedPersonMixin, UpdateView):
answer_set.save()
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