summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorluxagraf <sng@luxagraf.net>2019-12-29 18:19:26 -0500
committerluxagraf <sng@luxagraf.net>2019-12-29 18:19:26 -0500
commit09abece4982e8dceabe1dd8d678639205a4a6208 (patch)
tree8b27a3369ae6f61e7f0ed52d64e261b556157f00 /app
parente47d256783c9d466ab34b0632c70ac767011f91e (diff)
Added new GPX tracking model and Walk to locations
Diffstat (limited to 'app')
-rw-r--r--app/locations/admin.py54
-rw-r--r--app/locations/migrations/0019_gpxfile_gpxpoint_gpxtrack.py43
-rw-r--r--app/locations/migrations/0020_auto_20191229_1600.py18
-rw-r--r--app/locations/migrations/0021_walk.py30
-rw-r--r--app/locations/migrations/0022_walk_slug.py19
-rw-r--r--app/locations/migrations/0023_auto_20191229_1816.py22
-rw-r--r--app/locations/migrations/0024_auto_20191229_1817.py18
-rw-r--r--app/locations/models.py122
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)
+