Skip to content

Commit

Permalink
gpx module
Browse files Browse the repository at this point in the history
  • Loading branch information
manu committed Apr 1, 2017
1 parent 27821be commit a71b149
Show file tree
Hide file tree
Showing 7 changed files with 313 additions and 3 deletions.
2 changes: 2 additions & 0 deletions README.org
Expand Up @@ -51,6 +51,8 @@ For more information about the modules, visit [[https://github.com/novoid/Memacs
- [[http://en.wikipedia.org/wiki/Internet_Message_Access_Protocol][IMAP]]: see docs/memacs_imap.org
- [[http://www.djcbsoftware.nl/code/mu/][Mu]]: see docs/memacs_mumail.org
- *RSS*: see docs/memacs_rss.org i.e. for twitter, github, …
- *GPS*:
- [[http://code.mendhak.com/gpslogger/][GPX]] files: see docs/memacs_gpx.org
- *versioning systems*:
- [[http://en.wikipedia.org/wiki/Git_(software)][git]]: see docs/memacs_git.org
- [[http://en.wikipedia.org/wiki/Subversion][Subversion]]: see docs/svn/memacs_svn.org
Expand Down
22 changes: 22 additions & 0 deletions bin/memacs_gpx.py
@@ -0,0 +1,22 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-

from memacs.gpx import GPX

PROG_VERSION_NUMBER = u"0.1"
PROG_VERSION_DATE = u"2017-03-02"
PROG_SHORT_DESCRIPTION = u"Memacs for GPX files"
PROG_TAG = u"gps"

COPYRIGHT_YEAR = "2017"
COPYRIGHT_AUTHORS = """Manuel Koell <mankoell@gmail.com>"""


if __name__ == "__main__":
memacs = GPX(prog_version=PROG_VERSION_NUMBER,
prog_version_date=PROG_VERSION_DATE,
prog_short_description=PROG_SHORT_DESCRIPTION,
prog_tag=PROG_TAG,
copyright_year=COPYRIGHT_YEAR,
copyright_authors=COPYRIGHT_AUTHORS)
memacs.handle_main()
63 changes: 63 additions & 0 deletions docs/memacs_gpx.org
@@ -0,0 +1,63 @@
## This file is best viewed with GNU Emacs Org-mode: http://orgmode.org/

* memacs-gpx

Parse GPX files + reverse geocoding. Make sure to read about the usage policy and limits of both,
[[https://developers.google.com/maps/documentation/geocoding/usage-limits?hl=de][Google]] and
[[https://operations.osmfoundation.org/policies/nominatim/][OSM]] or setup your own
[[https://github.com/openstreetmap/Nominatim][Nominatim]] server.
There is also a great app for android, called [[https://play.google.com/store/apps/details?id=com.mendhak.gpslogger&hl=de][GPSLogger]],
a battery efficient GPS logging application to log your position throughout the day.
It is free software and open source (FOSS), thus available on [[https://github.com/mendhak/gpslogger][GitHub]] and
[[https://f-droid.org/repository/browse/?fdfilter=gps+logger&fdid=com.mendhak.gpslogger][F-Droid]] as well.

** Options
- ~-f~, ~--file~ path to gpx file or folder
- ~-p~, ~--provider~ geocode provider, ~google~ (default) or ~osm~
- ~-u~, ~--url~ url to nominatim server (osm only)
- ~--output-format~ use ~--verbose~ flag to see available placeholder, default ~{address}~

** Example
#+BEGIN_EXAMPLE
memacs_gpx.py -f memacs/tests/data/sample.gpx
#+END_EXAMPLE

#+BEGIN_EXAMPLE
* Memacs for GPX files :Memacs:gps:
** <2017-04-01 Sat 10:50> Eggenberger Allee 9, 8020 Graz, Austria :network:
:PROPERTIES:
:LATITUDE: 47.0693
:LONGITUDE: 15.4076001
:ID: c2dc4f2289d79cff4cf27faa95863f8cb5b8cb21
:END:

** <2017-04-01 Sat 10:51> Alte Poststraße 149, 8020 Graz, Austria :network:
:PROPERTIES:
:LATITUDE: 47.0690816
:LONGITUDE: 15.4099195
:ID: 17ad7a67128b74b85d04998bca02d11abe66dd1f
:END:
#+END_EXAMPLE

-----

#+BEGIN_EXAMPLE
memacs_gpx.py -f memacs/tests/data/sample.gpx -p osm
#+END_EXAMPLE

#+BEGIN_EXAMPLE
* Memacs for GPX files :Memacs:location:
** <2017-04-01 Sat 10:50> FH Joanneum - Prüffeld, 150, Alte Poststraße, Gries, Graz, Steiermark, 8020, Österreich :network:
:PROPERTIES:
:LATITUDE: 47.0693
:LONGITUDE: 15.4076001
:ID: c2dc4f2289d79cff4cf27faa95863f8cb5b8cb21
:END:

** <2017-04-01 Sat 10:51> FH Joanneum, 149, Alte Poststraße, Gries, Graz, Steiermark, 8020, Österreich :network:
:PROPERTIES:
:LATITUDE: 47.0690816
:LONGITUDE: 15.4099195
:ID: 17ad7a67128b74b85d04998bca02d11abe66dd1f
:END:
#+END_EXAMPLE
138 changes: 138 additions & 0 deletions memacs/gpx.py
@@ -0,0 +1,138 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import logging
import time
import json
import sys
import os

import gpxpy
import gpxpy.gpx
import geocoder

from lib.orgproperty import OrgProperties
from lib.orgformat import OrgFormat
from lib.memacs import Memacs


class GPX(Memacs):
def _parser_add_arguments(self):
"""
overwritten method of class Memacs
add additional arguments
"""
Memacs._parser_add_arguments(self)

self._parser.add_argument(
"-f", "--folder", dest="source",
action="store", required=True,
help="path to gpx file or folder")

self._parser.add_argument(
"-p", "--provider", dest="provider",
action="store", default="google",
help="geocode provider, default google")

self._parser.add_argument(
"-u", "--url", dest="url",
action="store", help="url to nominatim server (osm only)")

self._parser.add_argument(
"--output-format", dest="output_format",
action="store", default="{address}",
help="format string to use for the headline")

def _parser_parse_args(self):
"""
overwritten method of class Memacs
all additional arguments are parsed in here
"""
Memacs._parser_parse_args(self)

if not os.path.exists(self._args.source):
self._parser.error("source file or folder does not exist")

if self._args.url and not self._args.url.startswith("http"):
self._parser.error("invalid url given")

def reverse_geocode(self, lat, lng):
"""get address for latitude/longitude"""

if 'google' in self._args.provider:
geocode = geocoder.google([lat, lng], method='reverse')

elif 'osm' in self._args.provider:
if not self._args.url:
geocode = geocoder.osm([lat, lng], method='reverse')
time.sleep(1) # Nominatim Usage Policy
else:
if 'localhost' in self._args.url:
geocode = geocoder.osm([lat, lng], method='reverse', url='http://localhost/nominatim/search')
else:
geocode = geocoder.osm([lat, lng], method='reverse', url=self._args.url)

else:
self._parser.error("invalid provider given")
sys.exit(1)

if not geocode.ok:
logging.error("geocoding failed or api limit exceeded")
sys.exit(1)
else:
logging.debug(geocode.json)
return geocode.json

def write_point(self, p):
"""write a point (including geocoding)"""

timestamp = OrgFormat.datetime(p.time)
geocode = self.reverse_geocode(p.latitude, p.longitude)
output = self._args.output_format.decode('utf-8').format(**geocode)

tags = []

properties = OrgProperties(data_for_hashing=timestamp)

if p.latitude:
properties.add('LATITUDE', p.latitude)

if p.longitude:
properties.add('LONGITUDE', p.longitude)

if p.source:
tags.append(p.source.lower())

if timestamp:
self._writer.write_org_subitem(timestamp=timestamp,
output=output,
properties=properties,
tags=tags)

def handle_file(self, f):
"""iterate through a file"""

data = open(f)
gpx = gpxpy.parse(data)

for track in gpx.tracks:
for segment in track.segments:
for point in segment.points:
self.write_point(point)
logging.debug(point)

def _main(self):
"""
get's automatically called from Memacs class
"""

if os.path.isfile(self._args.source):
self.handle_file(self._args.source)

else:
for root, dirs, files in os.walk(self._args.source):
for f in files:
if f.endswith('.gpx'):
self.handle_file(os.path.join(root, f))
11 changes: 11 additions & 0 deletions memacs/tests/data/sample.gpx
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>

<gpx version="1.0" creator="GPSLogger 79 - http://gpslogger.mendhak.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/0" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
<time>2017-04-01T10:15:24Z</time>
<trk>
<trkseg>
<trkpt lat="47.0693" lon="15.4076001"><time>2017-04-01T10:50:00Z</time><src>network</src></trkpt>
<trkpt lat="47.0690816" lon="15.4099195"><time>2017-04-01T10:51:00Z</time><src>network</src></trkpt>
</trkseg>
</trk>
</gpx>
72 changes: 72 additions & 0 deletions memacs/tests/gpx_test.py
@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-

import unittest
import os

from memacs.gpx import GPX


class TestGPX(unittest.TestCase):

def test_google(self):
sample = os.path.join(
os.path.dirname(os.path.abspath(__file__)), 'data', 'sample.gpx'
)

argv = []
argv.append('-f')
argv.append(sample)

memacs = GPX(argv=argv)
data = memacs.test_get_entries()

self.assertEqual(
data[0],
u"** <2017-04-01 Sat 10:50> Eggenberger Allee 9, 8020 Graz, Austria :network:")
self.assertEqual(
data[1],
" :PROPERTIES:")
self.assertEqual(
data[2],
" :LATITUDE: 47.0693")
self.assertEqual(
data[3],
" :LONGITUDE: 15.4076001")
self.assertEqual(
data[4],
" :ID: c2dc4f2289d79cff4cf27faa95863f8cb5b8cb21")
self.assertEqual(
data[5],
" :END:")

def test_osm(self):
sample = os.path.join(
os.path.dirname(os.path.abspath(__file__)), 'data', 'sample.gpx'
)

argv = []
argv.append('-f')
argv.append(sample)
argv.append('-p osm')

memacs = GPX(argv=argv)
data = memacs.test_get_entries()

self.assertEqual(
data[0],
u"** <2017-04-01 Sat 10:50> FH Joanneum - Prüffeld, 150, Alte Poststraße, Gries, Graz, Steiermark, 8020, Österreich :network:")
self.assertEqual(
data[1],
" :PROPERTIES:")
self.assertEqual(
data[2],
" :LATITUDE: 47.0693")
self.assertEqual(
data[3],
" :LONGITUDE: 15.4076001")
self.assertEqual(
data[4],
" :ID: c2dc4f2289d79cff4cf27faa95863f8cb5b8cb21")
self.assertEqual(
data[5],
" :END:")
8 changes: 5 additions & 3 deletions requirements.txt
@@ -1,8 +1,10 @@
nose
nose==1.3.7
Pillow==2.4.0
argparse==1.2.1
feedparser==5.1.2
icalendar==3.1
pylast==1.8.0
twython
batinfo
batinfo==0.4.2
twython==3.4.0
gpxpy==1.1.2
geocoder==1.20.0

0 comments on commit a71b149

Please sign in to comment.