Skip to content

Commit

Permalink
Factor out MapProxy, split location map JS into separate file.
Browse files Browse the repository at this point in the history
  • Loading branch information
ropable committed Sep 5, 2024
1 parent 22273fa commit 8d51931
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 126 deletions.
11 changes: 4 additions & 7 deletions itassets/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
ROOT_URLCONF = "itassets.urls"
WSGI_APPLICATION = "itassets.wsgi.application"
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
MAPPROXY_URL = env("MAPPROXY_URL", "")
GEOSERVER_URL = env("GEOSERVER_URL", "")
LOGIN_URL = "/admin/login/"
LOGIN_REDIRECT_URL = "/"

Expand Down Expand Up @@ -225,10 +225,7 @@
"handlers": ["console"],
"level": "DEBUG" if DEBUG else "INFO",
},
"itassets": {
"handlers": ["console"],
"level": "INFO"
},
"itassets": {"handlers": ["console"], "level": "INFO"},
# Microsoft Authentication Libraries logging.
"msal": {
"handlers": ["console"],
Expand All @@ -240,8 +237,8 @@
"handlers": ["console"],
"level": "WARNING",
"propagate": False,
}
}
},
},
}


Expand Down
4 changes: 2 additions & 2 deletions kustomize/overlays/prod/deployment_patch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,11 @@ spec:
key: SENTRY_PROFILES_SAMPLE_RATE
- name: SENTRY_ENVIRONMENT
value: "prod"
- name: MAPPROXY_URL
- name: GEOSERVER_URL
valueFrom:
secretKeyRef:
name: itassets-env-prod
key: MAPPROXY_URL
key: GEOSERVER_URL
- name: REDIS_CACHE_HOST
valueFrom:
secretKeyRef:
Expand Down
4 changes: 2 additions & 2 deletions kustomize/overlays/uat/deployment_patch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,11 @@ spec:
key: SENTRY_PROFILES_SAMPLE_RATE
- name: SENTRY_ENVIRONMENT
value: "uat"
- name: MAPPROXY_URL
- name: GEOSERVER_URL
valueFrom:
secretKeyRef:
name: itassets-env-uat
key: MAPPROXY_URL
key: GEOSERVER_URL
- name: REDIS_CACHE_HOST
valueFrom:
secretKeyRef:
Expand Down
79 changes: 79 additions & 0 deletions organisation/static/js/location_map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"use strict";
// NOTE: some global constants are set in the base template.
const geoserver_wmts_url = geoserver_url + "/gwc/service/wmts?service=WMTS&request=GetTile&version=1.0.0&tilematrixset=gda94&tilematrix=gda94:{z}&tilecol={x}&tilerow={y}";
const geoserver_wmts_url_base = geoserver_wmts_url + "&format=image/jpeg";
const geoserver_wmts_url_overlay = geoserver_wmts_url + "&format=image/png";

// Define baselayer tile layers.
const mapboxStreets = L.tileLayer(
geoserver_wmts_url_base + "&layer=public:mapbox-streets",
{
tileSize: 1024,
zoomOffset: -2,
},
);

// Define overlay tile layers.
const dbcaRegions = L.tileLayer(
geoserver_wmts_url_overlay + "&layer=cddp:dbca_regions",
{
tileSize: 1024,
zoomOffset: -2,
},
);
const dbcaDistricts = L.tileLayer(
geoserver_wmts_url_overlay + "&layer=public:dbca_districts_public",
{
tileSize: 1024,
zoomOffset: -2,
},
);

// Define map.
const map = L.map('map', {
crs: L.CRS.EPSG4326, // WGS 84
center: [-31.96, 115.87],
minZoom: 4,
maxZoom: 18,
layers: [mapboxStreets, dbcaRegions], // Sets default selections.
attributionControl: false,
});

// Define layer groups.
const baseMaps = {
"Place names": mapboxStreets,
};
const overlayMaps = {
"DBCA regions": dbcaRegions,
"DBCA districts": dbcaDistricts,
};

// Define layer control.
L.control.layers(baseMaps, overlayMaps).addTo(map);

// Define scale bar
L.control.scale({ maxWidth: 500, imperial: false }).addTo(map);

// Function to define hover effect for location points.
function locationHover(feature, layer) {
layer.bindTooltip(
feature.properties.name,
{ className: 'leaflet-tooltip-wide' }
);
layer.bindPopup(`
<a href="?q=${feature.properties.ascender_desc}">${feature.properties.name}</a><br>
${feature.properties.ascender_desc}<br>
${feature.properties.phone}`);
}

// Add locations to the map display and zoom to their bounds.
const locationsLayer = L.geoJson(locationFeatures, {
onEachFeature: locationHover
});
const locations = L.markerClusterGroup();
locations.addLayer(locationsLayer);
map.addLayer(locations);
map.fitBounds(locations.getBounds());

// Move the map div inside the #locations-tab-pane div.
document.getElementById("locations-tab-pane").appendChild(document.getElementById("map"));
82 changes: 5 additions & 77 deletions organisation/templates/organisation/address_book.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% extends "base_itassets.html" %}
{% load static %}

{% block extra_style %}
{{ block.super }}
Expand Down Expand Up @@ -136,82 +137,9 @@ <h1>{{ page_title }}</h1>
{{ block.super }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js" integrity="sha512-puJW3E/qXDqYp9IfhAI54BJEaWIfloJ7JWs7OeD5i6ruC9JZL1gERT1wjtwXFlh7CjE7ZJ+/vcRZRkIYIb6p4g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.5.3/leaflet.markercluster.min.js" integrity="sha512-TiMWaqipFi2Vqt4ugRzsF8oRoGFlFFuqIi30FFxEPNw58Ov9mOy6LgC05ysfkxwLE0xVeZtmr92wVg9siAFRWA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="text/javascript">
// Define baselayer tile layers.
const mapboxStreets = L.tileLayer.wms('{{ mapproxy_url }}', {
layers: 'mapbox-streets',
format: 'image/png',
tileSize: 1024,
zoomOffset: -2,
});

// Define overlay tile layers.
const dbcaRegions = L.tileLayer.wms('{{ mapproxy_url }}', {
layers: 'dbca-regions',
format: 'image/png',
transparent: true,
opacity: 0.75,
tileSize: 1024,
zoomOffset: -2,
});
const dbcaDistricts = L.tileLayer.wms('{{ mapproxy_url }}', {
layers: 'dbca-districts',
format: 'image/png',
transparent: true,
opacity: 0.75,
tileSize: 1024,
zoomOffset: -2,
});

// Define map.
var map = L.map('map', {
crs: L.CRS.EPSG4326,
center: [-31.96, 115.87],
minZoom: 4,
maxZoom: 18,
layers: [mapboxStreets, dbcaRegions], // Sets default selections.
attributionControl: false,
});

// Define layer groups.
const baseMaps = {
"Place names": mapboxStreets,
};
const overlayMaps = {
"DBCA regions": dbcaRegions,
"DBCA districts": dbcaDistricts,
};

// Define layer control.
L.control.layers(baseMaps, overlayMaps).addTo(map);

// Define scale bar
L.control.scale({maxWidth: 500, imperial: false}).addTo(map);

// Function to define hover effect for location points.
function locationHover(feature, layer) {
layer.bindTooltip(
feature.properties.name,
{className: 'leaflet-tooltip-wide'}
);
layer.bindPopup(`
<a href="${'{% url "address_book" %}'}?q=${feature.properties.ascender_desc}">${feature.properties.name}</a><br>
${feature.properties.ascender_desc}<br>
${feature.properties.phone}
`);
}

// Add locations to the map display and zoom to their bounds.
const geojsonFeatures = JSON.parse('{{ locations_geojson|escapejs }}');
const locationsLayer = L.geoJson(geojsonFeatures, {
onEachFeature: locationHover
});
const locations = L.markerClusterGroup();
locations.addLayer(locationsLayer);
map.addLayer(locations);
map.fitBounds(locations.getBounds());

// Move the map div inside the #locations-tab-pane div.
document.getElementById("locations-tab-pane").appendChild(document.getElementById("map"));
<script>
const geoserver_url = "{{ geoserver_url }}";
const locationFeatures = JSON.parse("{{ locations_geojson|escapejs }}");
</script>
<script src="{% static 'js/location_map.js' %}"></script>
{% endblock extra_js %}
53 changes: 15 additions & 38 deletions organisation/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@ def get_context_data(self, **kwargs):
context["object_count"] = len(self.get_queryset())
context["previous_pages"] = get_previous_pages(context["page_obj"])
context["next_pages"] = get_next_pages(context["page_obj"])
context["mapproxy_url"] = settings.MAPPROXY_URL
context["geoserver_url"] = settings.GEOSERVER_URL
context["locations_geojson"] = serialize(
"geojson",
Location.objects.filter(
active=True, point__isnull=False, ascender_desc__isnull=False
),
Location.objects.filter(active=True, point__isnull=False, ascender_desc__isnull=False),
geometry_field="point",
srid=4283,
fields=["name", "phone", "ascender_desc"],
Expand Down Expand Up @@ -91,9 +89,7 @@ def get_queryset(self):
# Filter the queryset
if "q" in self.request.GET and self.request.GET["q"]:
query_str = self.request.GET["q"]
queryset = queryset.filter(
Q(name__icontains=query_str) | Q(cost_centre__code__icontains=query_str)
)
queryset = queryset.filter(Q(name__icontains=query_str) | Q(cost_centre__code__icontains=query_str))

# Last filter: Ascender job_end_date value is not in the past, or is absent.
# We need to turn our queryset into a list comprehension to use the model property for filtering.
Expand All @@ -102,10 +98,7 @@ def get_queryset(self):
for du in queryset
if (
(not du.get_job_end_date() or du.get_job_end_date() >= date.today())
and (
not du.get_job_start_date()
or du.get_job_start_date() <= date.today()
)
and (not du.get_job_start_date() or du.get_job_start_date() <= date.today())
)
]

Expand All @@ -114,9 +107,7 @@ def get_queryset(self):
def get(self, request, *args, **kwargs):
# Return an Excel spreadsheet if requested.
if "export" in self.request.GET and self.request.GET["export"]:
response = HttpResponse(
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)
response = HttpResponse(content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
response["Content-Disposition"] = (
f"attachment; filename=department_user_m365_licences_{date.today().isoformat()}_{datetime.now().strftime('%H%M')}.xlsx"
)
Expand Down Expand Up @@ -161,9 +152,7 @@ def get(self, request, *args, **kwargs):
queryset = queryset.filter(email__icontains=self.request.GET["q"])

# Tailor the API response.
if (
"selectlist" in request.GET
): # Smaller response, for use in HTML select lists.
if "selectlist" in request.GET: # Smaller response, for use in HTML select lists.
users = [{"id": user.pk, "text": user.email} for user in queryset]
else: # Normal API response.
users = [
Expand All @@ -172,17 +161,13 @@ def get(self, request, *args, **kwargs):
"name": user.name,
"given_name": user.given_name,
"surname": user.surname,
"preferred_name": user.preferred_name
if user.preferred_name
else None,
"preferred_name": user.preferred_name if user.preferred_name else None,
"email": user.email,
"title": user.title if user.title else None,
"telephone": user.telephone if user.telephone else None,
"extension": user.extension if user.extension else None,
"mobile_phone": user.mobile_phone if user.mobile_phone else None,
"location": {"id": user.location.pk, "name": user.location.name}
if user.location
else {},
"location": {"id": user.location.pk, "name": user.location.name} if user.location else {},
"cost_centre": user.cost_centre.code if user.cost_centre else None,
"employee_id": user.employee_id
if user.employee_id
Expand Down Expand Up @@ -216,20 +201,14 @@ def get(self, request, *args, **kwargs):
queryset = queryset.filter(name__icontains=self.request.GET["q"])

# Tailor the API response.
if (
"selectlist" in request.GET
): # Smaller response, for use in HTML select lists.
locations = [
{"id": location.pk, "text": location.name} for location in queryset
]
if "selectlist" in request.GET: # Smaller response, for use in HTML select lists.
locations = [{"id": location.pk, "text": location.name} for location in queryset]
else:
locations = [
{
"id": location.pk,
"name": location.name,
"point": {"type": "Point", "coordinates": location.point.coords}
if location.point
else {},
"point": {"type": "Point", "coordinates": location.point.coords} if location.point else {},
"address": location.address,
"pobox": location.pobox,
"phone": location.phone,
Expand Down Expand Up @@ -288,19 +267,17 @@ class DepartmentUserExport(View):
"""A custom view to export details of active Department users to an Excel spreadsheet."""

def get(self, request, *args, **kwargs):
response = HttpResponse(
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)
response = HttpResponse(content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
response["Content-Disposition"] = (
f"attachment; filename=department_users_{date.today().isoformat()}_{datetime.now().strftime('%H%M')}.xlsx"
)

if "all" in request.GET: # Return all objects.
users = DepartmentUser.objects.all()
else: # Default to active users only.
users = DepartmentUser.objects.filter(
**DepartmentUser.ACTIVE_FILTER
).exclude(account_type__in=DepartmentUser.ACCOUNT_TYPE_EXCLUDE)
users = DepartmentUser.objects.filter(**DepartmentUser.ACTIVE_FILTER).exclude(
account_type__in=DepartmentUser.ACCOUNT_TYPE_EXCLUDE
)

response = department_user_export(response, users)
return response

0 comments on commit 8d51931

Please sign in to comment.