diff --git a/network/management/commands/edges.py b/network/management/commands/edges.py index c7cd42e..95eb06d 100644 --- a/network/management/commands/edges.py +++ b/network/management/commands/edges.py @@ -41,6 +41,12 @@ def handle(self, *args, **kwargs): "start_date": x.start_date, "end_date": x.end_date, } + if source_kind == "place": + item["source_lat"] = source_obj.lat + item["source_lng"] = source_obj.lng + if target_kind == "place": + item["target_lat"] = target_obj.lat + item["target_lng"] = target_obj.lng try: Edge.objects.create(**item) except Exception as e: diff --git a/network/migrations/0003_edge_source_lat_edge_source_lng_edge_target_lat_and_more.py b/network/migrations/0003_edge_source_lat_edge_source_lng_edge_target_lat_and_more.py new file mode 100644 index 0000000..2b689f2 --- /dev/null +++ b/network/migrations/0003_edge_source_lat_edge_source_lng_edge_target_lat_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.1.3 on 2024-12-09 12:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("network", "0002_alter_edge_options_alter_edge_source_kind_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="edge", + name="source_lat", + field=models.FloatField(blank=True, null=True, verbose_name="Breitengrad"), + ), + migrations.AddField( + model_name="edge", + name="source_lng", + field=models.FloatField(blank=True, null=True, verbose_name="Längengrad"), + ), + migrations.AddField( + model_name="edge", + name="target_lat", + field=models.FloatField(blank=True, null=True, verbose_name="Breitengrad"), + ), + migrations.AddField( + model_name="edge", + name="target_lng", + field=models.FloatField(blank=True, null=True, verbose_name="Längengrad"), + ), + ] diff --git a/network/migrations/0004_alter_edge_source_lat_alter_edge_source_lng_and_more.py b/network/migrations/0004_alter_edge_source_lat_alter_edge_source_lng_and_more.py new file mode 100644 index 0000000..c20d51a --- /dev/null +++ b/network/migrations/0004_alter_edge_source_lat_alter_edge_source_lng_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 5.1.3 on 2024-12-09 12:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("network", "0003_edge_source_lat_edge_source_lng_edge_target_lat_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="edge", + name="source_lat", + field=models.FloatField( + blank=True, null=True, verbose_name="Breitengrad (Start)" + ), + ), + migrations.AlterField( + model_name="edge", + name="source_lng", + field=models.FloatField( + blank=True, null=True, verbose_name="Längengrad (Start)" + ), + ), + migrations.AlterField( + model_name="edge", + name="target_lat", + field=models.FloatField( + blank=True, null=True, verbose_name="Breitengrad (Ziel)" + ), + ), + migrations.AlterField( + model_name="edge", + name="target_lng", + field=models.FloatField( + blank=True, null=True, verbose_name="Längengrad (Ziel)" + ), + ), + ] diff --git a/network/models.py b/network/models.py index 765e282..3fb9c38 100644 --- a/network/models.py +++ b/network/models.py @@ -47,6 +47,12 @@ class Edge(models.Model): verbose_name="Art der Quelle", help_text="Art der Quelle (Person, Ort, Werk, Institution, Ereignis)", ) + source_lat = models.FloatField( + blank=True, null=True, verbose_name="Breitengrad (Start)" + ) + source_lng = models.FloatField( + blank=True, null=True, verbose_name="Längengrad (Start)" + ) source_id = models.IntegerField( verbose_name="ID der Quelle", help_text="ID der Quelle" ) @@ -66,6 +72,12 @@ class Edge(models.Model): verbose_name="Art des Ziels", help_text="Art des Ziels (Person, Ort, Werk, Institution, Ereignis)", ) + target_lat = models.FloatField( + blank=True, null=True, verbose_name="Breitengrad (Ziel)" + ) + target_lng = models.FloatField( + blank=True, null=True, verbose_name="Längengrad (Ziel)" + ) target_id = models.IntegerField( verbose_name="ID des Ziels", help_text="ID des Ziels" ) diff --git a/network/templates/network/list_view.html b/network/templates/network/list_view.html index 4670364..f263f26 100644 --- a/network/templates/network/list_view.html +++ b/network/templates/network/list_view.html @@ -16,6 +16,8 @@

CSV JSON Als Netzwerk + GeoJson + Karte diff --git a/network/templates/network/map.html b/network/templates/network/map.html new file mode 100644 index 0000000..f4a2193 --- /dev/null +++ b/network/templates/network/map.html @@ -0,0 +1,105 @@ +{% extends "base.html" %} +{% load static %} +{% block title %}Map{% endblock %} +{% block scriptHeader %} +{% endblock %} +{% block content %} + + +
+

Beziehungen zu Orten

+
+
+
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/network/urls.py b/network/urls.py index e37ca70..c25271d 100644 --- a/network/urls.py +++ b/network/urls.py @@ -1,10 +1,12 @@ from django.urls import path -from network.views import EdgeListViews, network_data, NetworkView +from network.views import EdgeListViews, network_data, NetworkView, edges_as_geojson, MapView app_name = "network" urlpatterns = [ path("edges/", EdgeListViews.as_view(), name="edges_browse"), - path("csv/", network_data, name="data"), + path("network-data/", network_data, name="data"), path("network/", NetworkView.as_view(), name="network"), + path("geojson-data/", edges_as_geojson, name="geojson"), + path("map/", MapView.as_view(), name="map"), ] diff --git a/network/utils.py b/network/utils.py new file mode 100644 index 0000000..a2aa805 --- /dev/null +++ b/network/utils.py @@ -0,0 +1,37 @@ +import pandas as pd + + +def df_to_geojson_vect( + df: pd.DataFrame, properties: list, lat="latitude", lon="longitude" +) -> tuple: + """converts a dataframe into a geojson + taken from https://blog.finxter.com/5-best-ways-to-convert-a-pandas-dataframe-to-geojson/ + + Args: + df (pd.DataFrame): a pandas DataFrame + properties (list): column keys which should be used as properties + lat (str, optional): the name of the column holding the latitute. Defaults to 'latitude'. + lon (str, optional): the anem of the column holding the longitute. Defaults to 'longitude'. + + Returns: + tuple: (lat, long) + """ + features = df.apply( + lambda row: { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [row[lon], row[lat]], + }, + "properties": {prop: row[prop] for prop in properties}, + }, + axis=1, + ).tolist() + return {"type": "FeatureCollection", "features": features} + + +def get_coords(row): + if pd.isna(row["source_lat"]): + return (row["target_lat"], row["target_lng"]) + else: + return row["source_lat"], row["source_lng"] diff --git a/network/views.py b/network/views.py index 748a685..253b14d 100644 --- a/network/views.py +++ b/network/views.py @@ -13,6 +13,7 @@ from network.forms import EdgeFilterFormHelper from network.models import Edge from network.tables import EdgeTable +from network.utils import get_coords, df_to_geojson_vect class NetworkView(TemplateView): @@ -36,6 +37,10 @@ def get_context_data(self, **kwargs): return context +class MapView(TemplateView): + template_name = "network/map.html" + + class EdgeListViews(GenericListView): model = Edge filter_class = EdgeListFilter @@ -51,6 +56,29 @@ class EdgeListViews(GenericListView): template_name = "network/list_view.html" +def edges_as_geojson(request): + values_list = [x.name for x in Edge._meta.get_fields()] + qs = ( + Edge.objects.filter(edge_kind__icontains="place") + .exclude(source_lat__isnull=True, target_lat__isnull=True) + .exclude(edge_kind="placeplace") + ) + items = EdgeListFilter(request.GET, queryset=qs).qs.values_list(*values_list) + df = pd.DataFrame(list(items), columns=values_list) + try: + df["label"] = df[["source_label", "edge_label", "target_label"]].agg( + " ".join, axis=1 + ) + except ValueError: + return JsonResponse(data={}) + df[["latitude", "longitude"]] = df.apply( + lambda row: pd.Series(get_coords(row)), axis=1 + ) + data = df_to_geojson_vect(df, ["label", "edge_id"]) + data["metadata"] = {"number of objects": len(df)} + return JsonResponse(data=data) + + def network_data(request): values_list = [x.name for x in Edge._meta.get_fields()] qs = EdgeListFilter(request.GET, queryset=Edge.objects.all()).qs diff --git a/templates/partials/head.html b/templates/partials/head.html index 2fb727e..017c69e 100644 --- a/templates/partials/head.html +++ b/templates/partials/head.html @@ -19,10 +19,12 @@ - - + + + + + +