-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
326ef46
commit 50bfe1b
Showing
13 changed files
with
277 additions
and
1 deletion.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.contrib import admin | ||
|
||
# Register your models here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class CitiesConfig(AppConfig): | ||
default_auto_field = 'django.db.models.BigAutoField' | ||
name = 'cities' |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.db import models | ||
|
||
# Create your models here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.test import TestCase | ||
|
||
# Create your tests here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from django.urls import path | ||
from .views import CitiesListView, CitiesDetailView | ||
|
||
urlpatterns = [ | ||
path('', CitiesListView, name='cities-list'), | ||
path('<str:pk>/', CitiesDetailView, name='cities-detail'), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import requests | ||
from datetime import datetime, timezone, timedelta | ||
from collections import defaultdict | ||
from django.shortcuts import render, get_object_or_404 | ||
from django.http import Http404 | ||
from django.conf import settings | ||
from main.enums import Dimension, SensorModel, OutputFormat, Precision, Order | ||
|
||
def CitiesDetailView(request, pk): | ||
api_url = f"{settings.API_URL}/city/current?city_slug={pk}" | ||
print(api_url) | ||
try: | ||
response = requests.get(api_url) | ||
response.raise_for_status() # Prüft, ob die Anfrage erfolgreich war | ||
station_data = response.json() # Daten im JSON-Format | ||
|
||
# Extract information from the JSON | ||
geometry = station_data.get("geometry", {}) | ||
properties = station_data.get("properties", {}) | ||
|
||
# Prepare city info | ||
city_info = { | ||
'name': properties.get('name'), | ||
'country': properties.get('country'), | ||
'timezone': properties.get('timezone'), | ||
'coordinates': geometry.get('coordinates'), | ||
'last_updated': properties.get('time'), | ||
'values': [] | ||
} | ||
|
||
# Parse "values" and translate dimensions | ||
values = properties.get("values", []) | ||
for value in values: | ||
dimension_id = value.get("dimension") | ||
translated_dimension = Dimension.get_name(dimension_id) | ||
city_info['values'].append({ | ||
'dimension': translated_dimension, | ||
'value': value.get("value"), | ||
'unit': Dimension.get_unit(dimension_id) | ||
}) | ||
|
||
except requests.exceptions.HTTPError as err: | ||
raise Http404(f"City {pk} not found: {err}") | ||
|
||
except requests.exceptions.RequestException as e: | ||
return render(request, 'stations/error.html', {'error': str(e)}) | ||
|
||
# Render die Daten im Template | ||
return render(request, 'cities/detail.html', {'city': city_info}) | ||
|
||
def CitiesListView(request): | ||
api_url = f"{settings.API_URL}/city/all" | ||
|
||
try: | ||
# Perform the GET request | ||
response = requests.get(api_url) | ||
response.raise_for_status() # Raise an error for unsuccessful requests | ||
data = response.json() # Parse JSON response | ||
|
||
# Extract and sort the list of cities alphabetically by name | ||
cities = data.get("cities", []) | ||
cities = sorted(cities, key=lambda city: city.get("name", "").lower()) # Sort case-insensitively | ||
|
||
except requests.exceptions.HTTPError as err: | ||
raise Http404(f"Cities not found.") | ||
|
||
return render(request, "cities/list.html", {"cities": cities}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
{% extends "_base.html" %} | ||
{% load i18n %} | ||
{% load static %} | ||
|
||
{% block title %}{% trans "City/municipality" %}: {{ station.id }}{% endblock title %} | ||
|
||
{% block styles %} | ||
<!-- Leaflet CSS --> | ||
<link rel="stylesheet" href="{% static 'css/leaflet.css' %}" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""> | ||
<link rel="stylesheet" href="{% static 'css/MarkerCluster.css' %}" crossorigin=""> | ||
<link rel="stylesheet" href="{% static 'css/MarkerCluster.Default.css' %}" crossorigin=""> | ||
|
||
<!-- Leaflet JS --> | ||
<script src="{% static 'js/leaflet.js' %}" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script> | ||
<script src="{% static 'js/chart.umd.js' %}" crossorigin=""></script> | ||
{% endblock styles %} | ||
|
||
{% block content %} | ||
<div class="row"> | ||
<div class="col-lg-12 mb-4"> | ||
<div id="map" style="width: 100%; height: 400px;"></div> | ||
</div> | ||
</div> | ||
|
||
<div class="row"> | ||
<div class="col-lg-12"> | ||
<h1>{{ city.name }}</h1> | ||
</div> | ||
</div> | ||
<div class="row"> | ||
<h3>Aktuelle Durchschnittswerte</h3> | ||
{% for value in city.values %} | ||
{% if value.dimension in "PM1.0,PM2.5,PM10.0,Humidity,Temperature" %} | ||
<div class="col-sm-4 mb-4"> | ||
<div class="card text-center"> | ||
<div class="card-body"> | ||
<h6 class="card-subtitle mb-2 text-muted">{{ value.dimension }}</h6> | ||
<p class="card-text display-4">{{ value.value|floatformat:2 }} {{ value.unit }}</p> | ||
</div> | ||
</div> | ||
</div> | ||
{% endif %} | ||
{% endfor %} | ||
<p>Achtung, die Daten stammen aus Citizen Science Quellen. Bei einer niedrigen Anzahl an Stationen, können die Durchschnittswerte unter Umständen leicht verschoben sein.</p> | ||
</div> | ||
|
||
<script> | ||
// Define color steps for PM2.5 | ||
const colorStepsPM25 = [ | ||
[0, [120, 1, 0.75]], | ||
[9, [60, 1, 1]], | ||
[35, [30, 1, 1]], | ||
[55, [0, 1, 1]], | ||
[125, [300, 1, 0.7]], | ||
[250.5, [330, 1, 0.5]] | ||
]; | ||
|
||
function interpolate(a, b, fraction) { | ||
return [ | ||
a[0] + (b[0] - a[0]) * fraction, | ||
a[1] + (b[1] - a[1]) * fraction, | ||
a[2] + (b[2] - a[2]) * fraction | ||
]; | ||
} | ||
|
||
function getColorForPM(value, colorSteps) { | ||
if (isNaN(value)) return [0, 0, 0.7]; // Grau für ungültige Werte | ||
for (let i = 0; i < colorSteps.length - 1; i++) { | ||
if (value >= colorSteps[i][0] && value < colorSteps[i + 1][0]) { | ||
const fraction = (value - colorSteps[i][0]) / (colorSteps[i + 1][0] - colorSteps[i][0]); | ||
return interpolate(colorSteps[i][1], colorSteps[i + 1][1], fraction); | ||
} | ||
} | ||
return colorSteps[colorSteps.length - 1][1]; | ||
} | ||
|
||
function hsvToRgb(h, s, v) { | ||
h = h % 360; // Ensure h is within 0-360 degrees | ||
let c = v * s; | ||
let x = c * (1 - Math.abs(((h / 60) % 2) - 1)); | ||
let m = v - c; | ||
let rPrime, gPrime, bPrime; | ||
|
||
if (h < 60) { | ||
rPrime = c; gPrime = x; bPrime = 0; | ||
} else if (h < 120) { | ||
rPrime = x; gPrime = c; bPrime = 0; | ||
} else if (h < 180) { | ||
rPrime = 0; gPrime = c; bPrime = x; | ||
} else if (h < 240) { | ||
rPrime = 0; gPrime = x; bPrime = c; | ||
} else if (h < 300) { | ||
rPrime = x; gPrime = 0; bPrime = c; | ||
} else { | ||
rPrime = c; gPrime = 0; bPrime = x; | ||
} | ||
|
||
let r = Math.round((rPrime + m) * 255); | ||
let g = Math.round((gPrime + m) * 255); | ||
let b = Math.round((bPrime + m) * 255); | ||
|
||
return [r, g, b]; | ||
} | ||
|
||
// Initialisierung der Leaflet-Karte | ||
var map = L.map('map').setView([{{ city.coordinates.1|stringformat:"f" }}, {{ city.coordinates.0|stringformat:"f" }}], 13); | ||
|
||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { | ||
attribution: '<a href="https://luftdaten.at">CC BY 2024 Luftdaten.at</a> | © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' | ||
}).addTo(map); | ||
|
||
async function fetchMarkerData() { | ||
const api_url = "{{API_URL}}"; | ||
const response = await fetch(`${api_url}/station/current/all`); | ||
|
||
const stationData = []; | ||
|
||
// Parse response CSV | ||
const text = await response.text(); | ||
const items = text.split("\n"); | ||
for (let row in items) { | ||
if (items[row].length == 0 || row == 0) continue; | ||
var data = items[row].split(","); | ||
const [stationID, lat, lon, pm1, pm25, pm10] = data; | ||
const marker = {stationID, lat, lon, pm25}; | ||
stationData.push(marker); | ||
} | ||
return stationData; | ||
} | ||
|
||
async function addMarkersForPM25() { | ||
const stationData = await fetchMarkerData(); | ||
|
||
// Add markers for PM2.5 | ||
for (let stationIndex in stationData) { | ||
const station = stationData[stationIndex]; | ||
const pmValue = station.pm25; | ||
const colorArrayHSV = getColorForPM(pmValue, colorStepsPM25); | ||
const rgb = hsvToRgb(colorArrayHSV[0], colorArrayHSV[1], colorArrayHSV[2]); | ||
const colorString = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; | ||
const brightness = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; | ||
const textColor = brightness > 125 ? "black" : "white"; | ||
|
||
const html = ` | ||
<a | ||
class="hiddenlink clickable" | ||
href="/stations/${station.stationID}" | ||
> | ||
<div style="height: 3.5em; width: 3.5em; background-color: ${colorString}; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 10pt;"> | ||
<span style="color: ${textColor};"> | ||
${isNaN(pmValue) ? '' : Number(pmValue).toFixed(1)} | ||
</span> | ||
</div> | ||
</a>`; | ||
|
||
L.marker([station.lat, station.lon], { | ||
icon: L.divIcon({iconSize: [40, 40], className: '', html: html}) | ||
}).addTo(map); | ||
} | ||
} | ||
|
||
// Add markers for PM2.5 | ||
addMarkersForPM25(); | ||
</script> | ||
|
||
|
||
{% endblock content %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{% extends "_base.html" %} | ||
{% load i18n %} | ||
{% load static %} | ||
|
||
{% block title %}{% trans "Cities and municipalities" %}{% endblock title %} | ||
|
||
{% block styles %} | ||
{% endblock styles %} | ||
|
||
{% block content %} | ||
<h2>{% trans "Cities and municipalities" %}</h2> | ||
<p> | ||
{% for city in cities %} | ||
<a href="/cities/{{ city.slug }}">{{ city.name }}</a> ({{ city.country.name }}){% if not forloop.last %}, {% endif %} | ||
{% endfor %} | ||
</p> | ||
{% endblock content %} |