diff options
author | luxagraf <sng@luxagraf.net> | 2019-12-29 18:19:26 -0500 |
---|---|---|
committer | luxagraf <sng@luxagraf.net> | 2019-12-29 18:19:26 -0500 |
commit | 09abece4982e8dceabe1dd8d678639205a4a6208 (patch) | |
tree | 8b27a3369ae6f61e7f0ed52d64e261b556157f00 | |
parent | e47d256783c9d466ab34b0632c70ac767011f91e (diff) |
Added new GPX tracking model and Walk to locations
-rw-r--r-- | app/locations/admin.py | 54 | ||||
-rw-r--r-- | app/locations/migrations/0019_gpxfile_gpxpoint_gpxtrack.py | 43 | ||||
-rw-r--r-- | app/locations/migrations/0020_auto_20191229_1600.py | 18 | ||||
-rw-r--r-- | app/locations/migrations/0021_walk.py | 30 | ||||
-rw-r--r-- | app/locations/migrations/0022_walk_slug.py | 19 | ||||
-rw-r--r-- | app/locations/migrations/0023_auto_20191229_1816.py | 22 | ||||
-rw-r--r-- | app/locations/migrations/0024_auto_20191229_1817.py | 18 | ||||
-rw-r--r-- | app/locations/models.py | 122 |
8 files changed, 324 insertions, 2 deletions
diff --git a/app/locations/admin.py b/app/locations/admin.py index 601ac03..0e78948 100644 --- a/app/locations/admin.py +++ b/app/locations/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from django.contrib.gis.admin import OSMGeoAdmin -from .models import Region, Country, Location, State, Route, LuxCheckIn, Campsite +from .models import Region, Country, Location, State, Route, LuxCheckIn, Campsite, GPXFile, GPXTrack, Walk from utils.widgets import OLAdminBase from utils.util import get_latlon @@ -277,3 +277,55 @@ class CampsiteAdmin(OLAdminBase): class Media: js = ('image-loader.js', 'next-prev-links.js') + + +@admin.register(GPXFile) +class GPXFileAdmin(OLAdminBase): + pass + + +@admin.register(GPXTrack) +class GPXTrackAdmin(OLAdminBase): + pass + + +@admin.register(Walk) +class WalkAdmin(OLAdminBase): + form = LGEntryForm + list_display = ('title', 'date_walked', 'rating', 'location') + list_filter = ('rating', 'location', 'date_walked') + prepopulated_fields = {'slug': ('title',)} + search_fields = ('tite',) + fieldsets = ( + ('Region', { + 'fields': ( + ('title', 'rating'), + 'body_markdown', + ('date_walked', 'slug'), + 'distance', + 'gpx_file', + 'point', + ), + 'classes': ( + 'show', + 'extrapretty' + ) + }), + ) + + # options for OSM map Using custom ESRI topo map + default_lon = -9285175 + default_lat = 4025046 + default_zoom = 6 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + openlayers_url = '/static/admin/js/OpenLayers.js' + + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } diff --git a/app/locations/migrations/0019_gpxfile_gpxpoint_gpxtrack.py b/app/locations/migrations/0019_gpxfile_gpxpoint_gpxtrack.py new file mode 100644 index 0000000..b8e698a --- /dev/null +++ b/app/locations/migrations/0019_gpxfile_gpxpoint_gpxtrack.py @@ -0,0 +1,43 @@ +# Generated by Django 2.1.2 on 2019-12-29 15:59 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import locations.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('locations', '0018_auto_20190414_2124'), + ] + + operations = [ + migrations.CreateModel( + name='GPXFile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=300)), + ('raw_data', models.TextField()), + ('gpx_file', models.FileField(blank=True, upload_to=locations.models.get_gpx_folder)), + ], + ), + migrations.CreateModel( + name='GPXPoint', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=50, verbose_name='Name')), + ('description', models.CharField(blank=True, max_length=250, verbose_name='Description')), + ('point', django.contrib.gis.db.models.fields.PointField(srid=4326)), + ('gpx_file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='locations.GPXFile')), + ], + ), + migrations.CreateModel( + name='GPXTrack', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('track', django.contrib.gis.db.models.fields.MultiLineStringField(srid=4326)), + ('gpx_file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='locations.GPXFile')), + ], + ), + ] diff --git a/app/locations/migrations/0020_auto_20191229_1600.py b/app/locations/migrations/0020_auto_20191229_1600.py new file mode 100644 index 0000000..773d985 --- /dev/null +++ b/app/locations/migrations/0020_auto_20191229_1600.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.2 on 2019-12-29 16:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('locations', '0019_gpxfile_gpxpoint_gpxtrack'), + ] + + operations = [ + migrations.AlterField( + model_name='gpxfile', + name='raw_data', + field=models.TextField(blank=True), + ), + ] diff --git a/app/locations/migrations/0021_walk.py b/app/locations/migrations/0021_walk.py new file mode 100644 index 0000000..02fe883 --- /dev/null +++ b/app/locations/migrations/0021_walk.py @@ -0,0 +1,30 @@ +# Generated by Django 2.1.2 on 2019-12-29 17:58 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('locations', '0020_auto_20191229_1600'), + ] + + operations = [ + migrations.CreateModel( + name='Walk', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=300)), + ('body_markdown', models.TextField()), + ('body_html', models.TextField(blank=True)), + ('date_walked', models.DateField(default=django.utils.timezone.now)), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, srid=4326)), + ('rating', models.CharField(blank=True, choices=[('1', '1 Star'), ('2', '2 Stars'), ('3', '3 Stars'), ('4', '4 Stars'), ('5', '5 Stars')], max_length=1, null=True)), + ('gpx_file', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='locations.GPXFile')), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ], + ), + ] diff --git a/app/locations/migrations/0022_walk_slug.py b/app/locations/migrations/0022_walk_slug.py new file mode 100644 index 0000000..b217021 --- /dev/null +++ b/app/locations/migrations/0022_walk_slug.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.2 on 2019-12-29 18:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('locations', '0021_walk'), + ] + + operations = [ + migrations.AddField( + model_name='walk', + name='slug', + field=models.SlugField(default='tk'), + preserve_default=False, + ), + ] diff --git a/app/locations/migrations/0023_auto_20191229_1816.py b/app/locations/migrations/0023_auto_20191229_1816.py new file mode 100644 index 0000000..b4f5ea9 --- /dev/null +++ b/app/locations/migrations/0023_auto_20191229_1816.py @@ -0,0 +1,22 @@ +# Generated by Django 2.1.2 on 2019-12-29 18:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('locations', '0022_walk_slug'), + ] + + operations = [ + migrations.AlterModelOptions( + name='gpxfile', + options={'ordering': ('title',)}, + ), + migrations.AddField( + model_name='walk', + name='distance', + field=models.PositiveSmallIntegerField(null=True), + ), + ] diff --git a/app/locations/migrations/0024_auto_20191229_1817.py b/app/locations/migrations/0024_auto_20191229_1817.py new file mode 100644 index 0000000..f66cb5e --- /dev/null +++ b/app/locations/migrations/0024_auto_20191229_1817.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.2 on 2019-12-29 18:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('locations', '0023_auto_20191229_1816'), + ] + + operations = [ + migrations.AlterField( + model_name='walk', + name='distance', + field=models.DecimalField(decimal_places=1, max_digits=3, null=True), + ), + ] diff --git a/app/locations/models.py b/app/locations/models.py index f160b1f..e9eb047 100644 --- a/app/locations/models.py +++ b/app/locations/models.py @@ -1,9 +1,12 @@ import json import requests +import datetime from django import forms +from django.dispatch import receiver +from django.db.models.signals import post_save from django.urls import reverse from django.apps import apps -from django.contrib.gis.geos import GEOSGeometry, fromstr, MultiPolygon +from django.contrib.gis.geos import GEOSGeometry, fromstr, MultiPolygon, Point, LineString, MultiLineString from django.contrib.gis.db import models from django.contrib.sitemaps import Sitemap from django.utils.safestring import mark_safe @@ -175,6 +178,7 @@ class Location(models.Model): self.description_html = markdown_to_html(self.description) super(Location, self).save() + class Route(models.Model): """Model to hold routes for longer trips""" name = models.CharField(max_length=200) @@ -380,3 +384,119 @@ def get_bounds(lat, lon): if poly.geom_type == 'Polygon': poly = MultiPolygon(fromstr(data[0]['geotext'])) return poly + + +def get_gpx_folder(instance, filename): + return "images/gpx/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +class GPXFile(models.Model): + title = models.CharField(max_length=300) + raw_data = models.TextField(blank=True) + gpx_file = models.FileField(upload_to=get_gpx_folder, blank=True) + + class Meta: + ordering = ('title',) + + def __str__(self): + return str(self.title) + + def save(self, *args, **kwargs): + created = self.pk is None + print(self.gpx_file.path) + super(GPXFile, self).save(*args, **kwargs) + + +class GPXPoint(models.Model): + name = models.CharField("Name", max_length=50, blank=True) + description = models.CharField("Description", max_length=250, blank=True) + gpx_file = models.ForeignKey(GPXFile, on_delete=models.CASCADE) + point = models.PointField() + + def __str__(self): + return str(self.name) + +class GPXTrack(models.Model): + track = models.MultiLineStringField() + gpx_file = models.ForeignKey(GPXFile, on_delete=models.CASCADE) + + +@receiver(post_save, sender=GPXFile) +def post_save_events(sender, update_fields, created, instance, **kwargs): + #if created: + with open(instance.gpx_file.path, 'r') as f: + instance.raw_data = f.read() + parse_gpx(instance, instance.raw_data) + post_save.disconnect(post_save_events, sender=GPXFile) + instance.save() + post_save.connect(post_save_events, sender=GPXFile) + + +import gpxpy +import gpxpy.gpx + +def parse_gpx(gpx_obj, gpx_data): + gpx = gpxpy.parse(gpx_data) + if gpx.waypoints: + for waypoint in gpx.waypoints: + new_waypoint = GPXPoint() + if waypoint.name: + new_waypoint.name = waypoint.name + else: + new_waypoint.name = 'unknown' + new_waypoint.point = Point(waypoint.longitude, waypoint.latitude) + new_waypoint.gpx_file = gpx_obj + new_waypoint.save() + + if gpx.tracks: + for track in gpx.tracks: + print("track name:" +str(track.name)) + new_track = GPXTrack() + for segment in track.segments: + + track_list_of_points = [] + for point in segment.points: + + point_in_segment = Point(point.longitude, point.latitude) + track_list_of_points.append(point_in_segment.coords) + + new_track_segment = LineString(track_list_of_points) + + new_track.track = MultiLineString(new_track_segment) + new_track.gpx_file = gpx_obj + new_track.save() + + +class Walk(models.Model): + title = models.CharField(max_length=300) + slug = models.SlugField() + body_markdown = models.TextField() + body_html = models.TextField(blank=True) + date_walked = models.DateField(default=timezone.now) + point = models.PointField(blank=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, blank=True, null=True) + gpx_file = models.ForeignKey(GPXFile, null=True, on_delete=models.SET_NULL) + RATINGS = ( + ('1', "1 Star"), + ('2', "2 Stars"), + ('3', "3 Stars"), + ('4', "4 Stars"), + ('5', "5 Stars"), + ) + rating = models.CharField(max_length=1, choices=RATINGS, null=True, blank=True) + distance = models.DecimalField(max_digits=3, decimal_places=1, null=True) + + def __str_(self): + return self.title + + def save(self, *args, **kwargs): + created = self.pk is None + if not created: + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + try: + self.location = Location.objects.filter(geometry__contains=self.point).get() + except Location.DoesNotExist: + raise forms.ValidationError("There is no location associated with that point, add it: %sadmin/locations/location/add/" % (settings.BASE_URL)) + super(Walk, self).save(*args, **kwargs) + |