Skip to content

Commit

Permalink
Fix for #1350: Add support for GPX tracks with multiple segments
Browse files Browse the repository at this point in the history
  • Loading branch information
bohare committed May 18, 2017
1 parent 1c93912 commit 18236cf
Show file tree
Hide file tree
Showing 11 changed files with 1,798 additions and 42 deletions.
31 changes: 16 additions & 15 deletions cadasta/core/static/js/map_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function renderFeatures(map, featuresUrl, options) {
if (options.fitBounds === 'locations') {
var bounds = markers.getBounds();
if (bounds.isValid()) {
map.fitBounds(bounds);
map.fitBounds(bounds);
}
}
}
Expand Down Expand Up @@ -96,7 +96,7 @@ function renderFeatures(map, featuresUrl, options) {
} else {
map.fitBounds([[-45.0, -180.0], [45.0, 180.0]]);
}

var geoJson = L.geoJson(null, {
style: { weight: 2 },
onEachFeature: function(feature, layer) {
Expand All @@ -105,7 +105,7 @@ function renderFeatures(map, featuresUrl, options) {
"<h2><span class=\"entity\">Location</span>" +
feature.properties.type + "</h2></div>" +
"<div class=\"btn-wrap\"><a href='" + feature.properties.url + "' class=\"btn btn-primary btn-sm btn-block\">" + options.trans['open'] + "</a>" +
"</div>");
"</div>");
}
}
});
Expand All @@ -116,7 +116,7 @@ function renderFeatures(map, featuresUrl, options) {

if (options.location) {
options.location.addTo(map);
map.fitBounds(options.location.getBounds());
map.fitBounds(options.location.getBounds());
} else if (projectBounds) {
map.fitBounds(projectBounds);
}
Expand Down Expand Up @@ -149,23 +149,24 @@ function switch_layer_controls(map, options){

function add_spatial_resources(map, url){
$.ajax(url).done(function(data){
if (data.length == 0) return;
if (data.count == 0) return;
var spatialResources = {};
$.each(data, function(idx, resource){
$.each(data.results, function (idx, resource) {
var name = resource.name;
var layers = {};
var group = new L.LayerGroup();
$.each(resource.spatial_resources, function(i, spatial_resource){
var layer = L.geoJson(spatial_resource.geom).addTo(group);
layers['name'] = spatial_resource.name;
layers['group'] = group;
$.each(resource.spatial_resources, function (i, spatial_resource) {
var group = new L.LayerGroup();
var layer = L.geoJson(spatial_resource.geom).addTo(group);
layers[spatial_resource.name] = group
});
spatialResources[name] = layers;
});
$.each(spatialResources, function(sr){
var layer = spatialResources[sr];
map.layerscontrol.addOverlay(layer['group'], layer['name'], sr);
})
$.each(spatialResources, function (sr) {
var layers = spatialResources[sr];
$.each(layers, function (layer) {
map.layerscontrol.addOverlay(layers[layer], layer, sr);
});
});
});
}

Expand Down
14 changes: 6 additions & 8 deletions cadasta/resources/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import logging
from datetime import datetime

import magic
Expand All @@ -9,7 +10,6 @@
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.gis.db.models import GeometryCollectionField
from django.contrib.gis.gdal.error import GDALException
from django.contrib.postgres.fields import JSONField
from django.db import models
from django.dispatch import receiver
Expand All @@ -30,6 +30,8 @@

GPX_MIME_TYPES = ('application/xml', 'text/xml', 'application/gpx+xml')

logger = logging.getLogger(__name__)


@permissioned_model
class Resource(RandomIDModel):
Expand Down Expand Up @@ -192,17 +194,13 @@ def create_spatial_resource(sender, instance, created, **kwargs):
mime_type = str(mime.from_file(write_path), 'utf-8')

if mime_type in GPX_MIME_TYPES:
try:
processor = GPXProcessor(write_path)
layers = processor.get_layers()
except GDALException:
raise InvalidGPXFile(
_('Invalid GPX file')
)
processor = GPXProcessor(write_path)
layers = processor.get_layers()
for layer in layers.keys():
if len(layers[layer]) > 0:
SpatialResource.objects.create(
resource=instance, name=layer, geom=layers[layer])
os.remove(write_path)
else:
os.remove(write_path)
raise InvalidGPXFile(
Expand Down
74 changes: 65 additions & 9 deletions cadasta/resources/processors/gpx.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,75 @@
from django.contrib.gis.gdal import DataSource
from django.contrib.gis.geos import GeometryCollection
import logging

KEEP_LAYERS = ['tracks', 'routes', 'waypoints']
import gpxpy
from django.contrib.gis.geos import (GeometryCollection, LineString,
MultiLineString, MultiPoint, Point)
from django.utils.translation import ugettext as _

from ..exceptions import InvalidGPXFile

logger = logging.getLogger(__name__)


class GPXProcessor:

def __init__(self, gpx_file):
self.ds = DataSource(gpx_file)
with open(gpx_file) as f:
try:
self.gpx = gpxpy.parse(f)
except gpxpy.gpx.GPXException as e:
logger.exception(e)
raise InvalidGPXFile(_("Invalid GPX file: %s" % e))

def get_layers(self):
layers = {}
for layer in self.ds:
name = layer.name
if name in KEEP_LAYERS:
# geom = self._get_features(layer)
layers[name] = GeometryCollection(layer.get_geoms(geos=True))
if self.gpx.tracks:
layers['tracks'] = GeometryCollection(
MultiLineString(parse_tracks(self.gpx.tracks))
)
if self.gpx.routes:
layers['routes'] = GeometryCollection(
MultiLineString(parse_routes(self.gpx.routes))
)
if self.gpx.waypoints:
layers['waypoints'] = GeometryCollection(
MultiPoint(parse_waypoints(self.gpx.waypoints))
)
if not layers:
raise InvalidGPXFile(
_('Error parsing GPX file: no geometry found.'))

return layers


def parse_segment(segment):
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)
return track_list_of_points


def parse_tracks(tracks):
multiline = []
for track in tracks:
for segment in track.segments:
track_list_of_points = parse_segment(segment)
if len(track_list_of_points) > 1:
multiline.append(LineString(track_list_of_points))
return multiline


def parse_waypoints(waypoints):
multipoint = []
for point in waypoints:
multipoint.append(Point(point.longitude, point.latitude))
return multipoint


def parse_routes(routes):
multiline = []
for route in routes:
track_list_of_points = parse_segment(route)
if len(track_list_of_points) > 1:
multiline.append(LineString(track_list_of_points))
return multiline
113 changes: 113 additions & 0 deletions cadasta/resources/tests/files/invalid_xml_version.gpx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?xml version="2.0" encoding="UTF-8" standalone="no" ?><!-- invalid version only 1.0 or 1.1 supported -->
<gpx
xmlns="http://www.topografix.com/GPX/1/1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/WaypointExtension/v1"
xmlns:gpxtrx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
creator="Oregon 550"
version="1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/WaypointExtension/v1 http://www8.garmin.com/xmlschemas/WaypointExtensionv1.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd">
<metadata>
<link href="http://www.garmin.com">
<text>Garmin International</text>
</link>
<time>2016-05-18T09:20:06Z</time>
</metadata>
<wpt lat="52.941277" lon="-8.034792">
<ele>159.297363</ele>
<time>2016-05-18T09:20:06Z</time>
<name>001</name>
<sym>Lodging</sym>
</wpt>
<wpt lat="52.946419" lon="-8.039001">
<ele>88.808327</ele>
<time>2016-05-18T10:04:17Z</time>
<name>002</name>
<sym>Lodging</sym>
</wpt>
<wpt lat="52.946421" lon="-8.039008">
<ele>89.445007</ele>
<time>2016-05-18T10:06:58Z</time>
<name>003</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946064" lon="-8.038319">
<ele>87.765770</ele>
<time>2016-05-18T10:08:56Z</time>
<name>004</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946153" lon="-8.037743">
<ele>88.544846</ele>
<time>2016-05-18T10:09:42Z</time>
<name>005</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946521" lon="-8.037001">
<ele>90.230118</ele>
<time>2016-05-18T10:10:45Z</time>
<name>006</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946283" lon="-8.036948">
<ele>91.034340</ele>
<time>2016-05-18T10:11:28Z</time>
<name>007</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946500" lon="-8.036520">
<ele>90.365944</ele>
<time>2016-05-18T10:12:52Z</time>
<name>008</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946646" lon="-8.036945">
<ele>91.291801</ele>
<time>2016-05-18T10:14:18Z</time>
<name>009</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946733" lon="-8.037329">
<ele>92.031578</ele>
<time>2016-05-18T10:14:57Z</time>
<name>010</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946578" lon="-8.037304">
<ele>89.177856</ele>
<time>2016-05-18T10:15:30Z</time>
<name>011</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946655" lon="-8.037608">
<ele>88.838951</ele>
<time>2016-05-18T10:16:14Z</time>
<name>012</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946620" lon="-8.037936">
<ele>89.070808</ele>
<time>2016-05-18T10:16:53Z</time>
<name>013</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946616" lon="-8.037954">
<ele>92.048050</ele>
<time>2016-05-18T10:17:13Z</time>
<name>014</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946628" lon="-8.037980">
<ele>88.055138</ele>
<time>2016-05-18T10:17:40Z</time>
<name>015</name>
<sym>Crossing</sym>
</wpt>
<wpt lat="52.946757" lon="-8.037970">
<ele>89.512466</ele>
<time>2016-05-18T10:18:39Z</time>
<name>016</name>
<sym>Crossing</sym>
</wpt>
</gpx>
2 changes: 1 addition & 1 deletion cadasta/resources/tests/files/routes.gpx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="2.0" encoding="UTF-8" standalone="no" ?>
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<gpx
xmlns="http://www.topografix.com/GPX/1/1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/WaypointExtension/v1"
Expand Down
59 changes: 59 additions & 0 deletions cadasta/resources/tests/files/routes_tracks.gpx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<gpx
xmlns="http://www.topografix.com/GPX/1/1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/WaypointExtension/v1"
xmlns:gpxtrx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
creator="Oregon 550"
version="1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/WaypointExtension/v1 http://www8.garmin.com/xmlschemas/WaypointExtensionv1.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd">
<metadata>
<link href="http://www.garmin.com">
<text>Garmin International</text>
</link>
<time>2016-09-01T08:50:59Z</time>
</metadata>
<rte>
<name>TEST</name>
<rtept lat="52.946419" lon="-8.039839">
<name>05973230688404</name>
</rtept>
<rtept lat="53.063194" lon="-8.229833">
<name>Lough Derg</name>
</rtept>
<rtept lat="53.537029" lon="-7.907280">
<name>06061460754125</name>
</rtept>
<rtept lat="54.116417" lon="-8.027824">
<name>05981810818596</name>
</rtept>
</rte>
<trk>
<trkseg>
<trkpt lat="45.5366218" lon="-122.5179408">
<ele>67.0</ele>
<time>2017-03-27T20:48:01Z</time>
<hdop>3.0</hdop>
</trkpt>
<trkpt lat="45.5367998" lon="-122.5180683">
<ele>56.0</ele>
<time>2017-03-27T20:48:16Z</time>
<hdop>3.0</hdop>
<extensions>
<speed>1.5299999713897705</speed>
</extensions>
</trkpt>
</trkseg>
<trkseg>
<trkpt lat="45.5368517" lon="-122.5180993">
<ele>59.0</ele>
<time>2017-03-27T20:48:23Z</time>
<hdop>4.0</hdop>
<extensions>
<speed>0.47999998927116394</speed>
</extensions>
</trkpt>
</trkseg>
</trk>
</gpx>
Loading

0 comments on commit 18236cf

Please sign in to comment.