Packaged at last...
authorRoman Bazalevskiy <rvb@rvb.name>
Fri, 11 Dec 2020 13:17:02 +0000 (16:17 +0300)
committerRoman Bazalevskiy <rvb@rvb.name>
Fri, 11 Dec 2020 13:17:02 +0000 (16:17 +0300)
18 files changed:
.gitignore [new file with mode: 0644]
generate_image.py [deleted file]
parsegpx.py [deleted file]
pygeocode.py [deleted file]
pygpx.py [deleted file]
pyosmname.py [deleted file]
pyrungps [deleted file]
pyrungps.py [deleted file]
pyrungps/__init__.py [new file with mode: 0644]
pyrungps/generate_image.py [new file with mode: 0644]
pyrungps/parsegpx.py [new file with mode: 0644]
pyrungps/pygeocode.py [new file with mode: 0644]
pyrungps/pygpx.py [new file with mode: 0644]
pyrungps/pyrungps_sync.py [new file with mode: 0644]
pyrungps/render_tiles.py [new file with mode: 0755]
pyrungps_sync [new file with mode: 0755]
render_tiles.py [deleted file]
setup.py [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..bee8a64
--- /dev/null
@@ -0,0 +1 @@
+__pycache__
diff --git a/generate_image.py b/generate_image.py
deleted file mode 100644 (file)
index 12169ca..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-#!/usr/bin/env python
-
-import mapnik
-
-import sys, os
-
-from lxml import html,etree
-from optparse import OptionParser
-
-from pprint import pprint
-
-import pygpx
-
-# Set up projections
-# spherical mercator (most common target map projection of osm data imported with osm2pgsql)
-merc = mapnik.Projection('+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs +over')
-
-# long/lat in degrees, aka ESPG:4326 and "WGS 84" 
-longlat = mapnik.Projection('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs')
-# can also be constructed as:
-#longlat = mapnik.Projection('+init=epsg:4326')
-
-# ensure minimum mapnik version
-if not hasattr(mapnik,'mapnik_version') and not mapnik.mapnik_version() >= 600:
-    raise SystemExit('This script requires Mapnik >=0.6.0)')
-
-
-def render_map(mapfile,map_uri,gpx_file,imgx,imgy):
-
-    with open(gpx_file,'r') as f:
-      data = f.read()
-
-    xml = etree.fromstring(data)
-    gpx = pygpx.GPX()
-    gpx.ReadTree(xml)
-
-    bbox = gpx.bound_box()
-    pprint(bbox)
-    cx=(bbox[0][1]+bbox[1][1])/2
-    cy=(bbox[0][0]+bbox[1][0])/2
-    print cx,cy
-    w=(bbox[1][1]-bbox[0][1])*1.2
-    h=(bbox[1][0]-bbox[0][0])*1.2
-    print w,h
-    bounds = (cx-w/2,cy-h/2,cx+w/2,cy+h/2)
-    print bounds
-
-    m = mapnik.Map(imgx,imgy)
-    mapnik.load_map(m,mapfile)
-
-    m.background = mapnik.Color('rgb(255,255,255)')
-    
-    # ensure the target map projection is mercator
-    m.srs = merc.params()
-
-    bbox = mapnik.Box2d(*bounds)
-
-    pprint(bbox)
-
-    transform = mapnik.ProjTransform(longlat,merc)
-    merc_bbox = transform.forward(bbox)
-    m.zoom_to_box(merc_bbox)
-
-    style = mapnik.Style()
-    rule = mapnik.Rule()
-
-    line_symbolizer = mapnik.LineSymbolizer()
-
-    line_symbolizer.stroke = mapnik.Color('rgb(0,0,127)')
-    line_symbolizer.stroke_width = 4.0
-    line_symbolizer.stroke_opacity = 0.5
-    
-    rule.symbols.append(line_symbolizer)
-
-    style.rules.append(rule)
-    m.append_style('GPS_tracking_points', style)
-
-    layer = mapnik.Layer('GPS_tracking_points')
-    layer.datasource = mapnik.Ogr(file=gpx_file, layer='tracks')
-    layer.styles.append('GPS_tracking_points')
-    m.layers.append(layer)
-
-    im = mapnik.Image(imgx,imgy)
-    mapnik.render(m, im)
-    im.save(map_uri,'png')
-    sys.stdout.write('output image to %s!\n' % map_uri)
-
-
-def render_all(db,mapfile,imgx,imgy):
-
-    import sqlite3
-    from os.path import dirname    
-
-    conn = sqlite3.connect(db)
-    conn.text_factory = str
-    cur = conn.cursor()
-    updcur = conn.cursor()
-
-    cur.execute ("select id,filename from tracks where preview_name is null")
-    
-    list=cur.fetchall()
-    
-    for rec in list:        
-    
-      id = rec[0]
-      filename = rec[1]
-      preview_name = dirname(filename) + '/' + str(id) + '.png'
-
-      print id,filename,preview_name
-
-      try:      
-        render_map(mapfile,preview_name.encode('utf8'),filename,imgx,imgy)
-        updcur.execute("update tracks set preview_name=? where id=?", (preview_name,id))
-        conn.commit()
-      except:
-        raise
-
-
-def main():
-
-    parser = OptionParser()
-    parser.add_option("-m", "--map", dest="mapfile",
-      help="use map file", metavar="MAP")
-    parser.add_option("-o", "--output", dest="outfile",
-      help="output image to file", metavar="OUT")
-    parser.add_option("-g", "--gpx", dest="gpxfile",
-      help="track to render", metavar="GPX")
-    parser.add_option("-x", "--x-size", dest="x",
-      help="image width", metavar="X")
-    parser.add_option("-y", "--y-size", dest="y",
-      help="image height", metavar="Y")
-    parser.add_option("-d", "--db", dest="db",
-      help="render all not process files in database", metavar="DB")
-    (options, args) = parser.parse_args()
-
-    if options.mapfile:  
-      mapfile = options.mapfile
-    else:
-      mapfile = "/etc/mapnik-osm-carto-data/veloroad-imposm.xml"
-
-    if options.outfile:  
-      map_uri = options.outfile
-    else:
-      map_uri = "image.png"
-
-    if options.x:  
-      imgx = int(options.x)
-    else:
-      imgx= 640
-    if options.y:  
-      imgy = int(options.y)
-    else:
-      imgy= 640
-
-    if options.db:
-      render_all(options.db,mapfile,imgx,imgy) 
-    else:
-      if options.gpxfile:  
-        gpx_file = options.gpxfile
-        render_map(mapfile,map_uri,gpx_file,imgx,imgy)
-      else:
-        print "No input file"
-        exit(1)
-
-if __name__ == "__main__":
-
-    main()                    
diff --git a/parsegpx.py b/parsegpx.py
deleted file mode 100644 (file)
index c8ca599..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/usr/bin/env python
-# coding: UTF-8
-
-import sys
-import os
-from lxml import etree
-from urllib2 import unquote
-import pygpx
-import pygeocode
-import sqlite3
-import datetime
-
-def check_db_for_training(db,sport,timestamp):
-
-  conn = sqlite3.connect(db)
-  conn.text_factory = str
-  cur = conn.cursor()
-
-  cur.execute ("select count(*) from tracks where sport=? and start_time=?" , (sport,timestamp))
-  return cur.fetchall()[0][0]
-
-def write_parsed_to_db(db,gpx,filename):
-
-  conn = sqlite3.connect(db)
-  conn.text_factory = str
-  cur = conn.cursor()
-
-  if type(filename) is str:
-    filename=filename.decode('UTF-8')
-
-  cur.execute ("delete from tracks where filename=?" , (filename,))
-  
-  tracks = gpx.tracks
-  
-  for track in tracks:
-
-      try:
-        author = gpx.author
-      except:
-        author = None 
-        
-      try:
-        name = gpx.name
-        if type(name) is str:
-          name=name.decode('UTF-8')
-      except:
-        name = None
-        
-      if author:
-        try:
-          cur.execute("insert into authors(name,description) values(?,?)", (author,''))         
-          print "created author %s" % (author)
-        except:
-          print "failed to create author %s" % (author)
-          pass
-
-      try:
-
-        print "processing..."
-        start = track.start()
-        if start:
-          printable = pygeocode.GeoName(start.lat,start.lon).printable
-          start_time = track.start_time()
-          full_duration = track.full_duration().total_seconds()
-          distance = track.distance()
-          filtered_distance = track.filtered_distance(max_speed=50)
-          ascent = track.elevation_gain()
-          descent = track.elevation_loss()
-          ((minlat,minlon),(maxlat,maxlon)) = track.bound_box()
-          params = ( 
-            gpx.author,name,filename,
-            track.sport,start_time,full_duration,
-            distance,filtered_distance,ascent,descent,
-            start.lat,start.lon,
-            printable,
-            minlat,minlon,maxlat,maxlon
-             )
-          print(params)
-          cur.execute("""
-            insert into tracks(
-              author,name,filename,sport,
-              start_time,duration,
-              distance,distance_filtered,
-              ascent,descent,
-              lat,lon,printable_location,minlat,minlon,maxlat,maxlon) 
-            values(
-              ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
-              """ 
-              , params )
-          conn.commit()
-          print "created track %s" % (filename)
-
-      except:
-      
-        raise
-
-def write_tree_to_db(db,tree,filename):
-
-  gpx = pygpx.GPX()
-  gpx.ReadTree(tree)
-
-  write_parsed_to_db(db,gpx,filename)
-
-def print_parsed_file(filename):
-
-  gpx = pygpx.GPX()
-  gpx.ReadFile(filename)
-
-  for track in gpx.tracks:
-
-    try:
-      author = gpx.author
-    except:
-      author = None 
-       
-    try:
-      name = gpx.name
-    except:
-      name = None
-        
-    start = track.start()
-    if start:
-      printable = pygeocode.GeoName(start.lat,start.lon).printable
-      start_time = track.start_time()
-      full_duration = track.full_duration().total_seconds()
-      distance = track.distance()
-      filtered_distance = track.filtered_distance(max_speed=50)
-      ascent = track.elevation_gain()
-      descent = track.elevation_loss()
-      ((minlat,minlon),(maxlat,maxlon)) = track.bound_box()
-      params = ( 
-        gpx.author,name,filename.decode('UTF-8'),
-        track.sport,start_time,full_duration,
-        distance,filtered_distance,ascent,descent,
-        start.lat,start.lon,
-        printable,
-        minlat,minlon,maxlat,maxlon
-      )
-      print(params)
diff --git a/pygeocode.py b/pygeocode.py
deleted file mode 100644 (file)
index f9fb0fb..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-# coding=UTF-8
-"""
-nominatim.openstreetmap.org API
-"""
-
-import urllib2
-
-def GetDescr(lat,lon):
-
-    reqstr = "http://maps.rvb.name/geocode.php?lat=%s&lon=%s" %(lat,lon)
-    headers = { 'User-Agent' : 'PyRunGPS/1.0', 'Referer' : 'https://maps.rvb.name' }    
-    req = urllib2.Request(reqstr, None, headers)
-    page = urllib2.urlopen(req).read()
-
-    return page
-
-class GeoName:
-
-    def __init__(self, lat, lon):
-
-        self.printable = GetDescr(lat,lon)
-          
\ No newline at end of file
diff --git a/pygpx.py b/pygpx.py
deleted file mode 100644 (file)
index 49ccdff..0000000
--- a/pygpx.py
+++ /dev/null
@@ -1,662 +0,0 @@
-# coding: UTF-8
-#------------------------
-#  Работа с GPX-файлами
-#------------------------
-from lxml import etree
-import os
-import math
-import datetime
-from dateutil.parser import parse
-from pprint import pprint
-
-def deg2rad(deg):
-# Преобразование из градусов в радианы.
-    return deg / (180 / math.pi)
-
-tagprefix = { '1.0': '{http://www.topografix.com/GPX/1/0}', '1.1': '{http://www.topografix.com/GPX/1/1}'}
-
-def StripPrefix(tag,version):
-  nstag=tagprefix[version]
-  return tag.replace(nstag,'')
-  
-def formattime(dt):
-  return dt.strftime("%Y-%m-%dT%H:%M:%S")+"."+str(int(dt.microsecond/1000)).zfill(3)+"Z"
-
-class Link:
-    # Ссылка. Может присутствовать в точке, треке, файле.
-    def __init__(self, node, version):
-        self.version = version
-        self.href = node.get("href")
-        self.text = None
-        self.type = None
-        if node is not None:
-          for child in node:
-            if child.tag == tagprefix[version] + "text":
-              self.text = child.text
-            elif child.tag == tagprefix[version] + "type":
-              self.type = child.text
-            else:
-              raise ValueError("Неизвестный тип узла: '%s' " % child.tag)  
-
-    def write(self,root):
-      linknode = etree.SubElement(root,"link");
-      linknode.set("href",self.href)
-      if self.text:
-        etree.SubElement(linknode,"text").text = self.text
-      if self.type:
-        etree.SubElement(linknode,"type").text = self.type
-
-class GPXTrackPt:
-    # Точка с координатами. Может присутствовать в последовательности точек в треке, маршруте, а также в списке путевых точек.# 
-
-    def __init__(self, node, version):
-        # Извлекаем данные из GPX-тэгов.# 
-        self.version = version
-        # Сначала обязательные.# 
-        if node is not None:
-          self.lat = float(node.get("lat"))
-          self.lon = float(node.get("lon"))
-        self.elevation = None
-        self.time = None
-        self.speed = None
-        self.link = None
-        self.additional_info = {}
-       # Потом необязательные.# 
-       if node is not None:
-          for child in node:
-            if child.tag == tagprefix[version] + "time":
-                self.time = parse(child.text)
-            elif child.tag == tagprefix[version] + "ele":
-                self.elevation = float(child.text)
-            # Стандартом скорость не предусмотрена, но де-факто в Run.GPS используется.# 
-            elif child.tag == tagprefix[version] + "speed":
-                self.speed = float(child.text)
-            elif child.tag == tagprefix[version] + "link":
-                self.link = Link(child,version)
-            elif StripPrefix(child.tag,version) in ['magvar','geoidheight','name','cmt','desc',
-                'src','sym','type','fix','sat','hdop','vdop','pdop','ageofdgpsdata','dgpsid']:
-                self.additional_info[StripPrefix(child.tag,version)]=child.text
-            elif child.tag == tagprefix[version] + "extensions":
-                pass
-            else:
-                raise ValueError("Неизвестный тип узла: '%s'" % child.tag)
-  
-        for i in self.additional_info.keys():
-          val = self.additional_info[i];
-          if val:
-            if not val.strip(' \n\t'):
-              del self.additional_info[i]
-
-
-    def write(self,root,tag ="wpt"):
-      wptnode = etree.SubElement(root,tag)
-      wptnode.set("lat",repr(self.lat));
-      wptnode.set("lon",repr(self.lon));
-      if self.elevation:
-        etree.SubElement(wptnode,"ele").text = repr(self.elevation)
-      if self.time:
-        etree.SubElement(wptnode,"time").text = formattime(self.time)
-      if self.speed:
-        etree.SubElement(wptnode,"speed").text = repr(self.speed)
-      if self.link:
-        self.link.write(wptnode)
-      for i in self.additional_info.keys():
-        etree.SubElement(wptnode,i).text = self.additional_info[i]
-
-    def distance(self, other):
-        # Расстояние между двумя точками на глобусе.# 
-        try:
-            # http://www.platoscave.net/blog/2009/oct/5/calculate-distance-latitude-longitude-python/
-
-            radius = 6378700.0 # meters
-
-            lat1, lon1 = self.lat, self.lon
-            lat2, lon2 = other.lat, other.lon
-
-            dlat = math.radians(lat2-lat1)
-            dlon = math.radians(lon2-lon1)
-            a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.radians(lat1)) \
-                * math.cos(math.radians(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
-            c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
-            d = radius * c
-
-        except ValueError, e:
-            raise ValueError(e)
-        return d
-
-    def duration(self, other):
-        # Время между двумя точками.# 
-        return other.time - self.time
-
-class GPXRoute:
-       # Маршрут.# 
-
-    def __init__(self, node, version):
-       self.name = None
-        self.version = version
-        self.rtepts = []
-       self.additional_info = {}
-       self.link = None
-       if node is not None:
-          for child in node:
-            if child.tag == tagprefix[version] + "name":
-              self.name = child.text
-            elif child.tag == tagprefix[version] + "link":
-              self.link = Link(child,version)
-            elif StripPrefix(child.tag,version) in [ "cmt", "desc", "src", "number", "type" ]:
-              self.additional_info[StripPrefix(child.tag,version)]=child.text
-            elif child.tag == tagprefix[version] + "rtept":
-              self.rtepts.append(GPXTrackPt(child, version))
-            elif child.tag == tagprefix[version] + "extensions":
-              pass
-            else:
-              raise ValueError("Неизвестный тип узла <%s>" % child.tag)
-        for i in self.additional_info.keys():
-          val = self.additional_info[i];
-          if val:
-            if not val.strip(' \n\t'):
-              del self.additional_info[i]
-
-    def write(self,root):
-      rtenode = etree.SubElement(root,"rte")
-      if self.name:
-        etree.SubElement(rtenode,"name").text = self.name
-      if self.link:
-        self.link.write(rtenode)
-      for i in self.additional_info.keys():
-        print "storing %s " % ( i )
-        etree.SubElement(rtenode,i).text = self.additional_info[i]
-      for i in self.rtepts:
-        i.write(rtenode,"rtept") 
-       
-class GPXTrackSeg:
-    # Один сегмент трека.# 
-
-    def __init__(self, node, version):
-        self.version = version
-        self.trkpts = []
-        self.elevation_gain = 0.0
-        self.elevation_loss = 0.0
-        if node is not None:
-          for child in node:
-            if child.tag == tagprefix[version] + "trkpt":
-              self.trkpts.append(GPXTrackPt(child, self.version))
-            else:
-              raise ValueError("Неизвестный тип узла <%s>" % node.nodeName)
-        self._get_elevation()
-
-    def write(self,root):
-      trksegnode = etree.SubElement(root,"trkseg")
-      for i in self.trkpts:
-        i.write(trksegnode,"trkpt")      
-
-    def _get_elevation(self):
-
-        elev_data = []
-        for pt in self.trkpts:
-          if pt.elevation:
-            elev_data.append(pt.elevation)
-
-        gain = 0.0
-        loss = 0.0
-        last_elevation = None
-
-        window_size = 5
-        i = 0
-        moving_averages = []
-
-        while i < len(elev_data) - window_size + 1:
-          this_window = elev_data[i : i + window_size]
-          window_average = sum(this_window) / window_size
-          moving_averages.append(window_average)
-          i += 1
-          
-        if len(moving_averages)>2:
-          elev_data = moving_averages
-
-        for pt in elev_data:    
-          if last_elevation is not None:
-            try:
-              if pt > last_elevation:
-                  gain += pt - last_elevation
-              else:
-                  loss += last_elevation - pt
-            except:
-              pass      
-          last_elevation=pt    
-
-        self.elevation_gain = gain
-        self.elevation_loss = loss
-
-    def distance(self):
-        # Расчет длины сегмента.# 
-        _length = 0.0
-        last_pt = None
-        for pt in self.trkpts:
-          if last_pt is not None:
-            _length += last_pt.distance(pt)
-          last_pt = pt
-        return _length
-
-    def filtered_distance(self,max_speed):
-        # Расчет длины сегмента с фильтрацией предположительно сбойных участков.# 
-        _length = 0.0
-        last_pt = None
-        for pt in self.trkpts:
-          if last_pt is not None:
-            _delta = last_pt.distance(pt)
-            _time_delta = pt.time - last_pt.time
-            _time_delta_s = _time_delta.days*86400 + _time_delta.seconds + _time_delta.microseconds/1000000
-            if _time_delta_s > 0:
-                if _delta/_time_delta_s < max_speed:
-                    _length += _delta
-            else:
-                _length += _delta
-          last_pt = pt      
-        return _length
-
-    def duration(self):
-        # Расчет продолжительности сегмента.# 
-        return self.trkpts[0].duration(self.trkpts[-1])
-
-    def bound_box(self):
-         # Обрамляющий прямоугольник для сегмента.# 
-      minlat =360
-      maxlat = -360
-      minlon = 360
-      maxlon = -360
-      for pt in self.trkpts:
-        if pt.lat<minlat:
-          minlat=pt.lat
-        if pt.lat>maxlat:
-          maxlat=pt.lat
-        if pt.lon<minlon:
-          minlon=pt.lon
-        if pt.lon>maxlon:
-          maxlon=pt.lon
-      return ((minlat,minlon),(maxlat,maxlon))      
-
-class GPXTrack:
-    # Трек.# 
-
-    def __init__(self, node, version):
-        # Создаем трек из GPX-данных.# 
-
-        self.version = version
-        self.trksegs = []
-        self.sport= None
-        self.additional_info = {}
-        self.name = None
-        self.link = None
-  
-        if node is not None:
-          for child in node:
-            if child.tag == tagprefix[version] + "name":
-              self.name = child.text
-            elif child.tag == tagprefix[version] + "trkseg":
-              if len(child) > 0:
-                self.trksegs.append(GPXTrackSeg(child, self.version))
-            elif child.tag == tagprefix[version] + "link":
-              self.link = Link(child,version)
-            elif StripPrefix(child.tag,version) in [ "number", "desc", "cmt", "src", "type" ]:
-              self.additional_info[StripPrefix(child.tag,version)] = child.text
-            elif child.tag == tagprefix[version] + "extensions":
-              for ext in child:
-                # Из расширений извлекаем вид спорта - специфично для Run.GPS.# 
-                if ext.tag == tagprefix[version] + "sport":
-                  self.sport = ext.text
-  
-        for i in self.additional_info.keys():
-          val = self.additional_info[i];
-          if val:
-            if not val.strip(' \n\t'):
-              del self.additional_info[i]
-
-    def write(self,root):
-      trknode = etree.SubElement(root,"trk")
-      if self.name:
-        etree.SubElement(trknode,"name").text = self.name
-      if self.sport:
-        etree.SubElement(etree.SubElement(trknode,"extensions"),"sport").text=self.sport  
-      if self.link:
-        self.link.write(trknode)
-      for i in self.additional_info.keys():
-        etree.SubElement(trknode,i).text = self.additional_info[i]
-      for i in self.trksegs:
-        i.write(trknode)      
-
-    def elevation_gain(self):
-               # Набор высоты по треку.# 
-        return sum([trkseg.elevation_gain for trkseg in self.trksegs])
-
-    def elevation_loss(self):
-               # Сброс высоты по треку.# 
-        return sum([trkseg.elevation_loss for trkseg in self.trksegs])
-
-    def distance(self):
-        # Длина трека.# 
-        try:
-            return sum([trkseg.distance() for trkseg in self.trksegs])
-        except IndexError:
-            print "Пустой сегмент трека, расчет длины невозможен."
-
-    def filtered_distance(self,max_speed=100):
-        # Длина трека с фильтрацией сбойных участков.# 
-        try:
-            return sum([trkseg.filtered_distance(max_speed=max_speed) for trkseg in self.trksegs])
-        except IndexError:
-            print "Пустой сегмент трека, расчет длины невозможен."
-
-    def duration(self):
-        # Продолжительность трека, не включая паузы между сегментами.# 
-        dur = datetime.timedelta(0)
-        for trkseg in self.trksegs:
-            try:
-              dur += trkseg.duration()
-            except:
-              pass  
-        return dur
-
-    def full_duration(self):
-        # Продолжительность трека, включая паузы между сегментами.# 
-        try:
-          return self.start().duration(self.end())
-        except:
-          return None
-
-    def start(self):
-        # Стартовая точка.# 
-        try:
-            return self.trksegs[0].trkpts[0]
-        except IndexError:
-            return None
-
-    def end(self):
-        # Финишная точка.# 
-        try:
-            return self.trksegs[-1].trkpts[-1]
-        except IndexError:
-            return None
-
-    def start_time(self):
-        # Время старта.# 
-        try:
-            return self.start().time
-        except:
-            return None
-
-    def end_time(self):
-        # Время финиша.# 
-        try:
-            return self.end().time
-        except:
-            return None    
-
-    def bound_box(self):
-      # Обрамляющий прямоугольник для всего трека.# 
-      minlat =360
-      maxlat = -360
-      minlon = 360
-      maxlon = -360
-      for trkseg in self.trksegs:
-        for pt in trkseg.trkpts:
-          if pt.lat<minlat:
-            minlat=pt.lat
-          if pt.lat>maxlat:
-            maxlat=pt.lat
-          if pt.lon<minlon:
-            minlon=pt.lon
-          if pt.lon>maxlon:
-            maxlon=pt.lon
-      return ((minlat,minlon),(maxlat,maxlon))      
-
-
-class GPX:
-    # Работа с GPX-документами.# 
-
-    def __init__(self):
-               # Создание пустого GPX-объекта# 
-        PATH = os.path.dirname(__file__)
-        self.creator = None
-        self.time = None
-        self.tracks = []
-        self.routes = []
-        self.waypoints = []
-        self.version = ""
-        self.author = None
-        self.name = None
-        self.link = None
-        self.copyright = None
-        self.meta = {}
-
-    def ReadTree(self,tree):
-               # Загрузка из дерева etree.# 
-        root = tree
-
-        # Test if this is a GPX file or not and if it's version 1.1
-        if root.tag == "{http://www.topografix.com/GPX/1/1}gpx" and root.get("version") == "1.1":
-            self.version = "1.1"
-        elif root.tag == "{http://www.topografix.com/GPX/1/0}gpx" and root.get("version") == "1.0":
-            self.version = "1.0"
-        elif root.get("version") not in [ "1.1", "1.0"]:
-            raise ValueError("Версия формата %s не поддерживается. Используйте GPX 1.1." % root.get("version"))
-        else:
-            raise ValueError("Неизвестный формат дерева")
-        # attempt to validate the xml file against the schema
-
-        # initalize the GPX document for parsing.
-        self._init_version(root)
-        
-
-    def ReadFile(self, fd):
-        # Загрузка из файла.# 
-        gpx_doc = etree.parse(fd)
-        root = gpx_doc.getroot()
-
-        # Test if this is a GPX file or not and if it's version 1.1
-        if root.tag == "{http://www.topografix.com/GPX/1/1}gpx" and root.get("version") == "1.1":
-            self.version = "1.1"
-        elif root.tag == "{http://www.topografix.com/GPX/1/0}gpx" and root.get("version") == "1.0":
-            self.version = "1.0"
-        elif root.get("version") not in [ "1.1", "1.0"]:
-            raise ValueError("Версия формата %s не поддерживается. Используйте GPX 1.1." % root.get("version"))
-        else:
-            raise ValueError("Неизвестный формат файла")
-        # initalize the GPX document for parsing.
-        self._init_version(root)
-
-    def _init_version(self,root):
-        # Инициализация объекта.# 
-        self.creator = root.get("creator")
-        if root is not None:
-          for child in root:
-            if child.tag == tagprefix[self.version] + "author":
-              self.author = child.text
-            elif child.tag == tagprefix[self.version] + "trk":
-              self.tracks.append(GPXTrack(child, self.version))
-            elif child.tag == tagprefix[self.version] + "rte":
-              self.routes.append(GPXRoute(child, self.version))
-            elif child.tag == tagprefix[self.version] + "wpt":
-              self.waypoints.append(GPXTrackPt(child, self.version))
-            elif child.tag == tagprefix[self.version] + "metadata":
-                for meta in child:
-                    if meta.tag == tagprefix[self.version] + "author":
-                      self.author = meta.text
-                    elif meta.tag == tagprefix[self.version] + "name":
-                      self.name = meta.text  
-                    elif meta.tag == tagprefix[self.version] + "bounds":
-                      # Границы пропускаем - их будем вычислять сами заново
-                      pass
-                    elif meta.tag == tagprefix[self.version] + "copyright":
-                      self.copyright = meta.text
-                    elif meta.tag == tagprefix[self.version] + "link":
-                      self.link = Link(meta,self.version) 
-                    else:
-                      self.meta[StripPrefix(meta.tag,self.version)]=meta.text
-                        
-        for i in self.meta.keys():
-          val = self.meta[i];
-          if val:
-            if not val.strip(' \n\t'):
-              del self.meta[i]
-
-    def elevation_gain(self):
-               # Суммарный набор высоты.# 
-        return sum([track.elevation_gain() for track in self.tracks])
-
-    def elevation_loss(self):
-               # Суммарная потеря высоты.# 
-        return sum([track.elevation_loss() for track in self.tracks])
-
-    def distance(self):
-        # Суммарная дистанция.# 
-        try:
-            return sum([track.distance() for track in self.tracks])
-        except IndexError:
-            print "Пустой файл!"
-
-    def filtered_distance(self):
-        # Суммарная дистанция с фильтрацией сбойных данных.# 
-        try:
-            return sum([track.filtered_distance() for track in self.tracks])
-        except IndexError:
-            print "Пустой файл!"
-
-    def duration(self):
-        # Суммарная продолжительность, не включая паузы.# 
-        dur = datetime.timedelta(0)
-        for track in self.tracks:
-            dur += track.duration()
-        return dur
-
-    def full_duration(self):
-        # Суммарная продолжительность, включая паузы# 
-        if hasattr(self.start(), 'duration'):
-            return self.start().duration(self.end())
-        else:
-            return None
-
-    def start(self):
-        # Точка старта.# 
-        return self.tracks[0].start()
-
-    def end(self):
-        # Точка финиша.# 
-        return self.tracks[-1].end()
-
-    def start_time(self):
-        # Время старта.# 
-        if self.start():
-            return self.start().time
-        else:
-            return None
-
-    def end_time(self):
-        # Время финиша.# 
-        return self.end().time
-
-    def bound_box(self):
-      # Обрамляющий прямоугольник для всех треков в файле.#         
-      minlat =360
-      maxlat = -360
-      minlon = 360
-      maxlon = -360
-      for track in self.tracks:
-        for trkseg in track.trksegs:
-          for pt in trkseg.trkpts:
-            if pt.lat<minlat:
-              minlat=pt.lat
-            if pt.lat>maxlat:
-              maxlat=pt.lat
-            if pt.lon<minlon:
-              minlon=pt.lon
-            if pt.lon>maxlon:
-              maxlon=pt.lon
-      return ((minlat,minlon),(maxlat,maxlon))      
-
-    def FixNames(self,linkname):
-      pprint(linkname)
-      pprint(type(linkname))
-      if type(linkname) is unicode:
-        goodname=linkname
-      else:  
-        goodname = linkname.decode('UTF-8')
-      badname = goodname.encode('ascii','replace')
-      if self.name and self.name.startswith(badname):
-        self.name=self.name.replace(badname,goodname)
-      for i in self.tracks:
-        if i.name and i.name.startswith(badname):
-          i.name = i.name.replace(badname,goodname)
-
-    def ProcessTrackSegs(self,seconds_gap=120):
-      for tr in self.tracks:
-        # Объединяем все точки трека в единую последовательность
-        all_trackpoints = []
-        for trseg in tr.trksegs:
-          all_trackpoints.extend(trseg.trkpts)
-        new_segs = []
-        current_seg = None
-        prev_pt = None
-        for pt in all_trackpoints:
-          if not current_seg:
-          # Начало нового сегмента
-            current_seg = GPXTrackSeg(None,self.version)
-            current_seg.trkpts.append(pt)
-          else: 
-            delta = pt.time - prev_pt.time
-            if delta.days > 0 or delta.seconds > seconds_gap:
-              # Нашли место разрыва
-              new_segs.append(current_seg)
-              current_seg = None  
-            else:
-              current_seg.trkpts.append(pt)  
-          prev_pt=pt   
-        # Если есть, что закинуть в хвост - пишем
-        if current_seg is not None:
-          new_segs.append(current_seg)  
-        # Обработали
-        for trs in new_segs:
-          trs._get_elevation()
-        tr.trksegs = new_segs
-        
-
-    def XMLTree(self):
-      root = etree.Element("gpx", attrib ={"creator": "pygpx",
-                                  "version": "1.1",
-                                  "xmlns": "http://www.topografix.com/GPX/1/1"}); 
-
-      # meta subtree
-
-      meta = etree.SubElement( root, "metadata" );
-      if self.name:
-        metarecord = etree.SubElement( meta, "name" )
-        metarecord.text = self.name
-      if self.author:
-        metarecord = etree.SubElement( meta, "author" )
-        metarecord.text = self.author
-      if self.copyright:
-        metarecord = etree.SubElement( meta, "copyright" )
-        metarecord.text = self.copyright
-      if self.link:  
-        self.link.write(meta)
-      for metatag in self.meta.iterkeys():
-        metarecord = etree.SubElement( meta, metatag )
-        metarecord.text = self.meta[metatag]
-
-      # wpts subtree
-
-      for i in self.waypoints:
-        i.write(root)      
-
-      # routes subtree
-      
-      for i in self.routes:
-        i.write(root)
-
-      # tracks subtree
-      
-      for i in self.tracks:
-        i.write(root)
-        
-      return root
-      
\ No newline at end of file
diff --git a/pyosmname.py b/pyosmname.py
deleted file mode 100644 (file)
index e6dc29e..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-# coding=UTF-8
-"""
-nominatim.openstreetmap.org API
-"""
-
-import urllib2
-from lxml import etree
-
-def GetXMLDescr(lat,lon):
-
-    reqstr = "http://nominatim.openstreetmap.org/reverse?lat=%s&lon=%s&accept-language=ru,en" %(lat,lon)
-    print reqstr
-    headers = { 'User-Agent' : 'PyRunGPS/1.0', 'Referer' : 'https://maps.rvb.name' }    
-    req = urllib2.Request(reqstr, None, headers)
-    page = urllib2.urlopen(req).read()
-
-    return etree.fromstring(page)
-
-def printable_name(addr):
-
-    if 'country' in addr:
-      if 'state' in addr:
-        if addr['country'] == u"Российская Федерация" and addr['state'] == u"Москва":
-          if 'road' in addr:
-            str = addr['road'] + ', ' + addr['state'] + ', ' + addr['country']
-          else:
-            str = addr['state'] + ', ' + addr['country']
-        else:
-          if 'city' in addr:
-            if 'road' in addr:
-              str = addr['road'] + ', ' + addr['city'] + ', ' + addr['state'] + ', ' + addr['country']
-            else:
-              str = addr['city'] + ', ' + addr['state'] + ', ' + addr['country']
-          elif 'town' in addr:
-            if 'road' in addr:
-              str = addr['road'] + ', ' + addr['town'] + ', ' + addr['state'] + ', ' + addr['country']
-            else:
-              str = addr['town'] + ', ' + addr['state'] + ', ' + addr['country']
-          elif 'village' in addr:
-            if 'road' in addr:
-              str = addr['road'] + ', ' + addr['village'] + ', ' + addr['state'] + ', ' + addr['country']
-            else:
-              str = addr['village'] + ', ' + addr['state'] + ', ' + addr['country']
-          elif 'county' in addr:
-            if 'road' in addr:
-              str = addr['road'] + ', ' + addr['county'] + ', ' + addr['state'] + ', ' + addr['country']
-            else:
-              str = addr['county'] + ', ' + addr['state'] + ', ' + addr['country']
-          else:
-            if 'road' in addr:
-              str = addr['road'] + ', ' + addr['state'] + ', ' + addr['country']
-            else:
-              str = addr['state'] + ', ' + addr['country']
-      else:
-        if 'city' in addr:
-          if 'road' in addr:
-            str = addr['road'] + ', ' + addr['city'] + ', ' + addr['country']
-          else:
-            str = addr['city'] + ', ' + addr['country']
-        elif 'village' in addr:
-          if 'road' in addr:
-            str = addr['road'] + ', ' + addr['village'] +', ' + addr['country']
-          else:
-            str = addr['village'] + ', ' + addr['country']
-        else:
-          if 'road' in addr:
-            str = addr['road'] + ', ' + addr['country']
-          else:
-            str = addr['country']
-    else:
-      str = u"Планета Земля, "
-      if addr['lat'] > 0:
-        str = str + u"%s с.ш.," % (addr['lat'])
-      else:
-        str = str + u"%s ю.ш.," % (-addr['lat'])
-      if addr['lon'] > 0:
-        str = str + u"%s в.д." % (addr['lon'])
-      else:
-        str = str + u"%s з.д." % (-addr['lon'])
-
-    return str
-
-class GeoName:
-
-    def __init__(self, lat, lon):
-
-        for elem in GetXMLDescr(lat,lon):
-          self.addr = {'lat':lat,'lon':lon}
-          self.road = ''
-          self.city = ''
-          self.country = '' 
-
-          if elem.tag == "addressparts":
-            for addrpart in elem:
-              tag = addrpart.tag
-              text = urllib2.unquote(addrpart.text)
-              self.addr[tag] = text
-              if tag == "road":
-                self.road = text
-              elif tag == "city":
-                self.city = text
-              elif tag == "country":
-                self.country = text
-                
-        self.printable = printable_name(self.addr)
-                 
-          
\ No newline at end of file
diff --git a/pyrungps b/pyrungps
deleted file mode 100755 (executable)
index 314e177..0000000
--- a/pyrungps
+++ /dev/null
@@ -1 +0,0 @@
-#!/usr/bin/python pyrungps.py
diff --git a/pyrungps.py b/pyrungps.py
deleted file mode 100644 (file)
index ed3fce5..0000000
+++ /dev/null
@@ -1,416 +0,0 @@
-#!/usr/bin/env python
-# coding: UTF-8
-
-import requests
-
-from urllib3.exceptions import InsecureRequestWarning
-requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
-    
-#import sys
-import os
-from urllib2 import quote,unquote
-from lxml import html,etree
-from optparse import OptionParser
-from datetime import date,datetime
-from mx.DateTime.ISO import ParseDateTimeUTC
-from parsegpx import write_parsed_to_db,check_db_for_training
-import pygpx
-
-from tempfile import NamedTemporaryFile
-
-from pprint import pprint
-
-import render_tiles
-
-import generate_image
-
-def get_page(uname,year,month):
-  
-  trainings = []
-
-  req = requests.get("http://www.gps-sport.net/services/getMonthlyTrainingDataHTML_V2.jsp?userName=%s&year=%s&month=%s&rnd=0.645673"% (uname,year,month), headers = {'User-agent': 'Mozilla/5.0'}, verify=False)
-  page = req.text.encode('utf-8')
-  dom = html.document_fromstring(page)
-
-  for element, attribute, link, pos in dom.iterlinks():
-    if attribute == "href":
-      if link.startswith("/trainings/"):
-        dummy, dummy, link = link.split('/')
-        name, id = link.split('_')
-        trainings.append([ unquote(name), id ])
-      
-  return trainings      
-
-def get_gpx_track(trid,name):
-
-  print "trid=",trid
-
-  req = requests.get("http://www.gps-sport.net/services/trainingGPX.jsp?trainingID=%s&tz=-180" % (trid), verify=False)
-  xml = etree.fromstring(req.text.encode('utf-8'))
-
-  return xml
-
-def get_osm_list(username,password,year,month):
-
-  url = "https://www.openstreetmap.org/api/0.6/user/gpx_files"
-
-  req = requests.get(url,auth=(username,password))
-  
-  xml = etree.fromstring(req.text.encode('utf-8'))
-
-  res=[]
-
-  for gpx_file in xml:
-    attrib = dict(gpx_file.attrib)
-    try:
-      timestamp=datetime.fromtimestamp(ParseDateTimeUTC(attrib['timestamp']))
-      id = attrib['id']
-      description=None
-      for descr in gpx_file.iter('description'):
-        description=descr.text
-      sport=None
-      for tag in gpx_file.iter('tag'):
-        sport=tag.text
-      if timestamp.year==year and timestamp.month==month and description=='Run.GPS Track':
-        record={ 'id': id, 'sport': sport }
-        res.append(record)
-    except:
-      None    
-      
-  return res      
-
-def get_osm_gpx(username,password,track_id):
-
-  url = "https://www.openstreetmap.org/api/0.6/gpx/"+track_id+"/data"
-
-  req = requests.get(url,auth=(username,password))
-  xml = etree.fromstring(req.text.encode('utf-8'))
-
-  return xml
-
-def get_db_gpx(dbx,track_id):
-
-  md, res = dbx.files_download(track_id)
-
-  fs = NamedTemporaryFile(suffix='.tcx',delete = False)
-  tmp_tcx_name=fs.name
-  fs.write(res.content)
-  fs.close()
-  fs = NamedTemporaryFile(suffix='.gpx',delete = False)
-  tmp_gpx_name=fs.name
-  fs.close()
-  
-  os.system("gpsbabel -i gtrnctr -f "+tmp_tcx_name+" -o gpx -F "+tmp_gpx_name)
-
-  with open (tmp_gpx_name, "r") as gpxfile:
-    data=gpxfile.read()
-    
-  os.remove(tmp_gpx_name)  
-  os.remove(tmp_tcx_name)
-
-  xml = etree.fromstring(data.encode('utf-8'))
-  return xml
-
-def get_dbx_list(dbx,username,year,month):
-
-    gpx_list_id = None
-
-    for entry in dbx.files_list_folder('').entries:
-      if entry.name == u'Приложения':
-        for entry_app in dbx.files_list_folder(entry.id).entries:
-          if entry_app.name == u'Run.GPS':
-            print "id="+entry_app.id
-            gpx_list_id=entry_app.id
-            break
-        break
-
-    res = []
-        
-    if gpx_list_id:
-    
-      for file in dbx.files_list_folder(gpx_list_id).entries:
-      
-        filename,ext = os.path.splitext(file.name)
-        if ext == '.tcx':
-          try:
-            fyear = int(filename[0:4])
-            fmonth = int(filename[5:7])
-            if fyear == year and fmonth == month:
-              sport = filename[41:]
-              timestamp = datetime.strptime(filename[0:19],'%Y-%m-%dT%H_%M_%S')
-              record={ 'id': file.id, 'timestamp': timestamp, 'sport': sport }
-              res.append(record)
-          except:
-            try:
-              fyear = int(filename[0:4])
-              fmonth = int(filename[5:7])
-              if fyear == year and fmonth == month:
-                sport = filename[18:]
-                timestamp = datetime.strptime(filename[0:16],'%Y-%m-%d_%H%M%S')
-                record={ 'id': file.id, 'timestamp': timestamp, 'sport': sport }
-                res.append(record)
-            except:
-              None
-
-    return res        
-      
-
-def sync_db(dbx,username,year,month,dir=".",verbose=False,force=False):
-
-    training_list = get_dbx_list(dbx,username,year,month)
-
-    for training in training_list:
-    
-      filename = "%s/%04d/%02d/%s_%s.gpx" % (dir,year,month,training['sport'],training['id'][3:])   
-      dirname = "%s/%04d/%02d" % (dir,year,month)
-      
-      if os.path.exists(filename) and not force:
-
-        if verbose:
-          print "training %s exists, skipping" % (filename)
-
-      else:  
-
-        try:
-          os.makedirs(dirname)
-        except:
-          None
-
-        xml = get_db_gpx(dbx,training['id'])
-
-        if verbose:
-          print "writing training %s" % (filename)
-
-        gpx = pygpx.GPX()
-        gpx.ReadTree(xml)
-
-        sport = training['sport']
-        timestamp = gpx.tracks[0].start_time()
-        
-        print sport, timestamp
-        
-        if check_db_for_training(db,sport,timestamp):
-          print "The same training found"
-          continue
-
-        gpx.author = username
-        gpx.FixNames(training['sport'])
-        gpx.ProcessTrackSegs()
-        for track in gpx.tracks:
-          track.sport=training['sport']
-
-        xml = gpx.XMLTree();
-        f = open(filename,"w")
-        f.write(etree.tostring(xml,encoding='UTF-8',pretty_print=True))
-        f.close
-        write_parsed_to_db(db,gpx,filename)
-        try:
-          render_tiles.queue_render(db,filename)
-        except:
-          None  
-
-def sync_osm(username,password,year,month,dir=".",verbose=False,force=False):
-
-    training_list = get_osm_list(username,password,year,month)
-
-    for training in training_list:
-    
-      filename = "%s/%04d/%02d/%s_%s.gpx" % (dir,year,month,training['sport'],training['id'])   
-      dirname = "%s/%04d/%02d" % (dir,year,month)
-
-      if os.path.exists(filename) and not force:
-
-        if verbose:
-          print "training %s exists, skipping" % (filename)
-
-      else:  
-      
-        try:
-          os.makedirs(dirname)    
-        except:
-          None
-
-        xml = get_osm_gpx(username,password,training['id'])
-
-        if verbose:
-          print "writing training %s" % (filename)
-
-        gpx = pygpx.GPX()
-        gpx.ReadTree(xml)
-
-        sport = training['sport']
-        timestamp = gpx.tracks[0].start_time()
-        
-        if check_db_for_training(db,sport,timestamp):
-          print "The same training found"
-          continue
-
-        gpx.author = username
-        gpx.FixNames(training['sport'])
-        gpx.ProcessTrackSegs()
-        for track in gpx.tracks:
-          track.sport=training['sport']
-
-        xml = gpx.XMLTree();
-        f = open(filename,"w")
-        f.write(etree.tostring(xml,encoding='UTF-8',pretty_print=True))
-        f.close
-        write_parsed_to_db(db,gpx,filename)
-        try:
-          render_tiles.queue_render(db,filename)
-        except:
-          None  
-
-def sync_folder(username,year,month,dir=".",verbose=False,force=False):
-
-    training_list = get_page(username,year,month)
-    for tr in training_list:
-     try:
-
-      filename = "%s/%04d/%02d/%s_%s.gpx" % (dir,year,(month+1),tr[0],tr[1])   
-      dirname = "%s/%04d/%02d" % (dir,year,(month+1))
-
-      if os.path.exists(filename) and not force:
-
-        if verbose:
-          print "training %s exists, skipping" % (filename)
-
-      else:  
-
-        try:
-          os.makedirs(dirname)    
-        except:
-          None
-          
-        xml=get_gpx_track(tr[1],tr[0])
-
-        if verbose:
-          print "writing training %s" % (filename)
-
-        gpx = pygpx.GPX()
-        gpx.ReadTree(xml)
-
-        sport = tr[0]
-        timestamp = gpx.tracks[0].start_time()
-        
-        if check_db_for_training(db,sport,timestamp):
-          print "The same training found"
-          continue
-
-        gpx.FixNames(tr[0])
-        gpx.ProcessTrackSegs()
-        
-        xml = gpx.XMLTree();
-        f = open(filename,"w")
-        f.write(etree.tostring(xml,encoding='UTF-8',pretty_print=True))
-        f.close
-        write_parsed_to_db(db,gpx,filename)
-        try:
-          render_tiles.queue_render(db,filename)
-        except:
-          None  
-
-     except:
-       raise
-
-def main():
-
-    global db;
-    parser = OptionParser()
-    parser.add_option("-d", "--dir", dest="dirname",
-      help="write data to directory", metavar="DIR")
-    parser.add_option("-q", "--quiet",
-      action="store_false", dest="verbose", default=True,
-      help="don't print status messages to stdout")
-    parser.add_option("-f", "--force",
-      action="store_true", dest="force", default=False,
-      help="rewrite all files")
-    parser.add_option("-y", "--yearmonth", dest="yearmonth",
-      help="year and month in YYYY-MM format", metavar="YYYY-MM")                                                          
-    parser.add_option("-u", "--username", dest="username",
-      help="Run.GPS username")                                                          
-    parser.add_option("-o", "--osm-username", dest="osmname",
-      help="OpenStreetMap username")                                                          
-    parser.add_option("-p", "--osm-passwd", dest="osmpasswd",
-      help="OpenStreetMap password")                                                          
-    parser.add_option("-b", "--dropbox", dest="dropboxauth",
-      help="DropBox Auth token")                                                          
-    (options, args) = parser.parse_args()
-
-    username = options.username
-    osmname = options.osmname
-    osmpwd = options.osmpasswd
-    dbauth = options.dropboxauth
-    if not (username or (osmname and osmpwd) or dbauth):
-      print "Run.GPS username or OSM username/password or Dropbox Auth token is mandatory!"
-      return
-
-    if username:
-      pusername = username+'@Run.GPS'
-    elif osmname:
-      pusername = osmname+'@OSM'  
-    elif dbauth:
-      import dropbox
-      dbx = dropbox.Dropbox(dbauth)  
-      pusername = dbx.users_get_current_account().email+'@DBX'
-
-    try:
-      if options.yearmonth:
-        year,month = options.yearmonth.split('-')
-        month = int(month) -1
-        year = int(year)
-        if month<0 or month>11:
-          raise invalid_number
-      else:
-        year = None
-        month = None
-    except:
-      print "Year and month should be in YYYY-MM format!"
-      return 
-      
-    if options.dirname:  
-      outdir = options.dirname
-    else:
-      outdir = '.'
-    
-    db = outdir + '/gpx.db'
-    
-    if year:
-      if options.verbose:
-        print "retrieving trainings for user %s, year %s, month %s to %s" % (pusername,year,month+1,outdir)
-      if username:
-        sync_folder(username,year,month,outdir,options.verbose,options.force)
-      elif osmname:
-        sync_osm(osmname,osmpwd,year,month+1,outdir,options.verbose,options.force)  
-      elif dbauth:
-        sync_db(dbx,pusername,year,month+1,outdir,options.verbose,options.force)  
-    else:
-      if options.verbose:
-        print "retrieving two last months for user %s to %s" % (pusername ,outdir)
-      now = date.today()
-      current_year = now.year
-      current_month = now.month
-      if username:
-        sync_folder(username,current_year,current_month-1,outdir,options.verbose,options.force)
-      elif osmname:
-        sync_osm(osmname,osmpwd,current_year,current_month,outdir,options.verbose,options.force)  
-      elif dbauth:
-        sync_db(dbx,pusername,current_year,current_month,outdir,options.verbose,options.force)  
-      current_month = current_month -1
-      if current_month == 0:
-        current_month = 12
-        current_year = current_year -1
-      if username:
-        sync_folder(username,current_year,current_month-1,outdir,options.verbose,options.force)
-      elif osmname:  
-        sync_osm(osmname,osmpwd,current_year,current_month,outdir,options.verbose,options.force)  
-      elif dbauth:
-        sync_db(dbx,pusername,current_year,current_month,outdir,options.verbose,options.force)  
-
-    generate_image.render_all(db,'/etc/mapnik-osm-carto-data/veloroad-imposm.xml',640,640)
-
-if __name__ == "__main__":
-
-    main()                    
-    
\ No newline at end of file
diff --git a/pyrungps/__init__.py b/pyrungps/__init__.py
new file mode 100644 (file)
index 0000000..94b233f
--- /dev/null
@@ -0,0 +1,16 @@
+from .render_tiles import deg2num, queue_render, queue_tiles, process_queue
+from .parsegpx import check_db_for_training, write_parsed_to_db, write_tree_to_db, print_parsed_file
+from .pygeocode import GetDescr, GeoName
+from .pygpx import deg2rad, StripPrefix, formattime, Link, GPXTrackPt, GPXRoute, GPXTrackSeg, GPXTrack, GPX
+from .pyrungps_sync import get_page, get_gpx_track, get_osm_list, get_osm_gpx, get_db_gpx, get_dbx_list, sync_db, sync_osm, sync_folder
+from .generate_image import render_map, render_all
+
+__all__ = [ 
+  "deg2num", "queue_render", "queue_tiles", "process_queue",
+  "check_db_for_training", "write_parsed_to_db", "write_tree_to_db", "print_parsed_file",
+  "GetDescr", "GeoName",
+  "deg2rad", "StripPrefix", "formattime", "Link", "GPXTrackPt", "GPXRoute", "GPXTrackSeg", "GPXTrack", "GPX",
+  "get_page", "get_gpx_track", "get_osm_list", "get_osm_gpx", "get_db_gpx", "get_dbx_list", "sync_db", "sync_osm", "sync_folder",
+  "render_map", "render_all"
+  ]
+  
\ No newline at end of file
diff --git a/pyrungps/generate_image.py b/pyrungps/generate_image.py
new file mode 100644 (file)
index 0000000..6e1fa3a
--- /dev/null
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+
+import mapnik
+
+import sys, os
+
+from lxml import html,etree
+from optparse import OptionParser
+
+from pprint import pprint
+
+import pyrungps.pygpx
+
+# Set up projections
+# spherical mercator (most common target map projection of osm data imported with osm2pgsql)
+merc = mapnik.Projection('+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs +over')
+
+# long/lat in degrees, aka ESPG:4326 and "WGS 84" 
+longlat = mapnik.Projection('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs')
+# can also be constructed as:
+#longlat = mapnik.Projection('+init=epsg:4326')
+
+# ensure minimum mapnik version
+if not hasattr(mapnik,'mapnik_version') and not mapnik.mapnik_version() >= 600:
+    raise SystemExit('This script requires Mapnik >=0.6.0)')
+
+
+def render_map(mapfile,map_uri,gpx_file,imgx,imgy):
+
+    with open(gpx_file,'r') as f:
+      data = f.read()
+
+    xml = etree.fromstring(data)
+    gpx = pygpx.GPX()
+    gpx.ReadTree(xml)
+
+    bbox = gpx.bound_box()
+    pprint(bbox)
+    cx=(bbox[0][1]+bbox[1][1])/2
+    cy=(bbox[0][0]+bbox[1][0])/2
+    print(cx,cy)
+    w=(bbox[1][1]-bbox[0][1])*1.2
+    h=(bbox[1][0]-bbox[0][0])*1.2
+    print(w,h)
+    bounds = (cx-w/2,cy-h/2,cx+w/2,cy+h/2)
+    print(bounds)
+
+    m = mapnik.Map(imgx,imgy)
+    mapnik.load_map(m,mapfile)
+
+    m.background = mapnik.Color('rgb(255,255,255)')
+    
+    # ensure the target map projection is mercator
+    m.srs = merc.params()
+
+    bbox = mapnik.Box2d(*bounds)
+
+    pprint(bbox)
+
+    transform = mapnik.ProjTransform(longlat,merc)
+    merc_bbox = transform.forward(bbox)
+    m.zoom_to_box(merc_bbox)
+
+    style = mapnik.Style()
+    rule = mapnik.Rule()
+
+    line_symbolizer = mapnik.LineSymbolizer()
+
+    line_symbolizer.stroke = mapnik.Color('rgb(0,0,127)')
+    line_symbolizer.stroke_width = 4.0
+    line_symbolizer.stroke_opacity = 0.5
+    
+    rule.symbols.append(line_symbolizer)
+
+    style.rules.append(rule)
+    m.append_style('GPS_tracking_points', style)
+
+    layer = mapnik.Layer('GPS_tracking_points')
+    layer.datasource = mapnik.Ogr(file=gpx_file, layer='tracks')
+    layer.styles.append('GPS_tracking_points')
+    m.layers.append(layer)
+
+    im = mapnik.Image(imgx,imgy)
+    mapnik.render(m, im)
+    im.save(map_uri,'png')
+    sys.stdout.write('output image to %s!\n' % map_uri)
+
+
+def render_all(db,mapfile,imgx,imgy):
+
+    import sqlite3
+    from os.path import dirname    
+
+    conn = sqlite3.connect(db)
+    conn.text_factory = str
+    cur = conn.cursor()
+    updcur = conn.cursor()
+
+    cur.execute ("select id,filename from tracks where preview_name is null")
+    
+    list=cur.fetchall()
+    
+    for rec in list:        
+    
+      id = rec[0]
+      filename = rec[1]
+      preview_name = dirname(filename) + '/' + str(id) + '.png'
+
+      print(id,filename,preview_name)
+
+      try:      
+        render_map(mapfile,preview_name.encode('utf8'),filename,imgx,imgy)
+        updcur.execute("update tracks set preview_name=? where id=?", (preview_name,id))
+        conn.commit()
+      except:
+        raise
+
+
+def main():
+
+    parser = OptionParser()
+    parser.add_option("-m", "--map", dest="mapfile",
+      help="use map file", metavar="MAP")
+    parser.add_option("-o", "--output", dest="outfile",
+      help="output image to file", metavar="OUT")
+    parser.add_option("-g", "--gpx", dest="gpxfile",
+      help="track to render", metavar="GPX")
+    parser.add_option("-x", "--x-size", dest="x",
+      help="image width", metavar="X")
+    parser.add_option("-y", "--y-size", dest="y",
+      help="image height", metavar="Y")
+    parser.add_option("-d", "--db", dest="db",
+      help="render all not process files in database", metavar="DB")
+    (options, args) = parser.parse_args()
+
+    if options.mapfile:  
+      mapfile = options.mapfile
+    else:
+      mapfile = "/etc/mapnik-osm-carto-data/veloroad-imposm.xml"
+
+    if options.outfile:  
+      map_uri = options.outfile
+    else:
+      map_uri = "image.png"
+
+    if options.x:  
+      imgx = int(options.x)
+    else:
+      imgx= 640
+    if options.y:  
+      imgy = int(options.y)
+    else:
+      imgy= 640
+
+    if options.db:
+      render_all(options.db,mapfile,imgx,imgy) 
+    else:
+      if options.gpxfile:  
+        gpx_file = options.gpxfile
+        render_map(mapfile,map_uri,gpx_file,imgx,imgy)
+      else:
+        print("No input file")
+        raise IOError("No input file")
+
+if __name__ == "__main__":
+
+    try:
+        main()                    
+    except:
+        exit(1)    
diff --git a/pyrungps/parsegpx.py b/pyrungps/parsegpx.py
new file mode 100644 (file)
index 0000000..c774aaa
--- /dev/null
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+import sys
+import os
+from lxml import etree
+from urllib.parse import unquote
+import pyrungps.pygpx
+import pyrungps.pygeocode
+import sqlite3
+import datetime
+
+def check_db_for_training(db,sport,timestamp):
+
+  conn = sqlite3.connect(db)
+  conn.text_factory = str
+  cur = conn.cursor()
+
+  cur.execute ("select count(*) from tracks where sport=? and start_time=?" , (sport,timestamp))
+  return cur.fetchall()[0][0]
+
+def write_parsed_to_db(db,gpx,filename):
+
+  conn = sqlite3.connect(db)
+  conn.text_factory = str
+  cur = conn.cursor()
+
+  cur.execute ("delete from tracks where filename=?" , (filename,))
+  
+  tracks = gpx.tracks
+  
+  for track in tracks:
+
+      try:
+        author = gpx.author
+      except:
+        author = None 
+        
+      try:
+        name = gpx.name
+      except:
+        name = None
+        
+      if author:
+        try:
+          cur.execute("insert into authors(name,description) values(?,?)", (author,''))         
+          print("created author %s" % (author))
+        except:
+          print("failed to create author %s" % (author))
+          pass
+
+      try:
+
+        print("processing...")
+        start = track.start()
+        if start:
+          printable = pygeocode.GeoName(start.lat,start.lon).printable
+          start_time = track.start_time()
+          full_duration = track.full_duration().total_seconds()
+          distance = track.distance()
+          filtered_distance = track.filtered_distance(max_speed=50)
+          ascent = track.elevation_gain()
+          descent = track.elevation_loss()
+          ((minlat,minlon),(maxlat,maxlon)) = track.bound_box()
+          params = ( 
+            gpx.author,name,filename,
+            track.sport,start_time,full_duration,
+            distance,filtered_distance,ascent,descent,
+            start.lat,start.lon,
+            printable,
+            minlat,minlon,maxlat,maxlon
+             )
+          print(params)
+          cur.execute("""
+            insert into tracks(
+              author,name,filename,sport,
+              start_time,duration,
+              distance,distance_filtered,
+              ascent,descent,
+              lat,lon,printable_location,minlat,minlon,maxlat,maxlon) 
+            values(
+              ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
+              """ 
+              , params )
+          conn.commit()
+          print("created track %s" % (filename))
+
+      except:
+      
+        raise
+
+def write_tree_to_db(db,tree,filename):
+
+  gpx = pygpx.GPX()
+  gpx.ReadTree(tree)
+
+  write_parsed_to_db(db,gpx,filename)
+
+def print_parsed_file(filename):
+
+  gpx = pygpx.GPX()
+  gpx.ReadFile(filename)
+
+  for track in gpx.tracks:
+
+    try:
+      author = gpx.author
+    except:
+      author = None 
+       
+    try:
+      name = gpx.name
+    except:
+      name = None
+        
+    start = track.start()
+    if start:
+      printable = pygeocode.GeoName(start.lat,start.lon).printable
+      start_time = track.start_time()
+      full_duration = track.full_duration().total_seconds()
+      distance = track.distance()
+      filtered_distance = track.filtered_distance(max_speed=50)
+      ascent = track.elevation_gain()
+      descent = track.elevation_loss()
+      ((minlat,minlon),(maxlat,maxlon)) = track.bound_box()
+      params = ( 
+        gpx.author,name,filename,
+        track.sport,start_time,full_duration,
+        distance,filtered_distance,ascent,descent,
+        start.lat,start.lon,
+        printable,
+        minlat,minlon,maxlat,maxlon
+      )
+      print(params)
diff --git a/pyrungps/pygeocode.py b/pyrungps/pygeocode.py
new file mode 100644 (file)
index 0000000..d048622
--- /dev/null
@@ -0,0 +1,21 @@
+# coding=UTF-8
+"""
+nominatim.openstreetmap.org API
+"""
+
+import urllib3
+
+def GetDescr(lat,lon):
+
+    reqstr = "http://maps.rvb.name/geocode.php?lat=%s&lon=%s" %(lat,lon)
+    headers = { 'User-Agent' : 'PyRunGPS/1.0', 'Referer' : 'https://maps.rvb.name' }    
+    http_pool = urllib3.connection_from_url(reqstr, headers=headers)
+    req = http_pool.urlopen('GET',reqstr)
+    return req.data.decode('utf-8','ignore')
+
+class GeoName:
+
+    def __init__(self, lat, lon):
+
+        self.printable = GetDescr(lat,lon)
+          
\ No newline at end of file
diff --git a/pyrungps/pygpx.py b/pyrungps/pygpx.py
new file mode 100644 (file)
index 0000000..e80077d
--- /dev/null
@@ -0,0 +1,656 @@
+# coding: UTF-8
+#------------------------
+#  Работа с GPX-файлами
+#------------------------
+from lxml import etree
+import os
+import math
+import datetime
+from dateutil.parser import parse
+from pprint import pprint
+
+def deg2rad(deg):
+# Преобразование из градусов в радианы.
+    return deg / (180 / math.pi)
+
+tagprefix = { '1.0': '{http://www.topografix.com/GPX/1/0}', '1.1': '{http://www.topografix.com/GPX/1/1}'}
+
+def StripPrefix(tag,version):
+  nstag=tagprefix[version]
+  return tag.replace(nstag,'')
+  
+def formattime(dt):
+  return dt.strftime("%Y-%m-%dT%H:%M:%S")+"."+str(int(dt.microsecond/1000)).zfill(3)+"Z"
+
+class Link:
+    # Ссылка. Может присутствовать в точке, треке, файле.
+    def __init__(self, node, version):
+        self.version = version
+        self.href = node.get("href")
+        self.text = None
+        self.type = None
+        if node is not None:
+          for child in node:
+            if child.tag == tagprefix[version] + "text":
+              self.text = child.text
+            elif child.tag == tagprefix[version] + "type":
+              self.type = child.text
+            else:
+              raise ValueError("Неизвестный тип узла: '%s' " % child.tag)  
+
+    def write(self,root):
+      linknode = etree.SubElement(root,"link");
+      linknode.set("href",self.href)
+      if self.text:
+        etree.SubElement(linknode,"text").text = self.text
+      if self.type:
+        etree.SubElement(linknode,"type").text = self.type
+
+class GPXTrackPt:
+    # Точка с координатами. Может присутствовать в последовательности точек в треке, маршруте, а также в списке путевых точек.# 
+
+    def __init__(self, node, version):
+        # Извлекаем данные из GPX-тэгов.# 
+        self.version = version
+        # Сначала обязательные.# 
+        if node is not None:
+          self.lat = float(node.get("lat"))
+          self.lon = float(node.get("lon"))
+        self.elevation = None
+        self.time = None
+        self.speed = None
+        self.link = None
+        self.additional_info = {}
+       # Потом необязательные.# 
+        if node is not None:
+          for child in node:
+            if child.tag == tagprefix[version] + "time":
+                self.time = parse(child.text)
+            elif child.tag == tagprefix[version] + "ele":
+                self.elevation = float(child.text)
+            # Стандартом скорость не предусмотрена, но де-факто в Run.GPS используется.# 
+            elif child.tag == tagprefix[version] + "speed":
+                self.speed = float(child.text)
+            elif child.tag == tagprefix[version] + "link":
+                self.link = Link(child,version)
+            elif StripPrefix(child.tag,version) in ['magvar','geoidheight','name','cmt','desc',
+                'src','sym','type','fix','sat','hdop','vdop','pdop','ageofdgpsdata','dgpsid']:
+                self.additional_info[StripPrefix(child.tag,version)]=child.text
+            elif child.tag == tagprefix[version] + "extensions":
+                pass
+            else:
+                raise ValueError("Неизвестный тип узла: '%s'" % child.tag)
+  
+        for i in self.additional_info.keys():
+          val = self.additional_info[i];
+          if val:
+            if not val.strip(' \n\t'):
+              del self.additional_info[i]
+
+
+    def write(self,root,tag ="wpt"):
+      wptnode = etree.SubElement(root,tag)
+      wptnode.set("lat",repr(self.lat));
+      wptnode.set("lon",repr(self.lon));
+      if self.elevation:
+        etree.SubElement(wptnode,"ele").text = repr(self.elevation)
+      if self.time:
+        etree.SubElement(wptnode,"time").text = formattime(self.time)
+      if self.speed:
+        etree.SubElement(wptnode,"speed").text = repr(self.speed)
+      if self.link:
+        self.link.write(wptnode)
+      for i in self.additional_info.keys():
+        etree.SubElement(wptnode,i).text = self.additional_info[i]
+
+    def distance(self, other):
+        # Расстояние между двумя точками на глобусе.# 
+        # http://www.platoscave.net/blog/2009/oct/5/calculate-distance-latitude-longitude-python/
+
+        radius = 6378700.0 # meters
+
+        lat1, lon1 = self.lat, self.lon
+        lat2, lon2 = other.lat, other.lon
+
+        dlat = math.radians(lat2-lat1)
+        dlon = math.radians(lon2-lon1)
+        a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.radians(lat1)) \
+            * math.cos(math.radians(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
+        c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
+        d = radius * c
+
+        return d
+
+    def duration(self, other):
+        # Время между двумя точками.# 
+        return other.time - self.time
+
+class GPXRoute:
+       # Маршрут.# 
+
+    def __init__(self, node, version):
+        self.name = None
+        self.version = version
+        self.rtepts = []
+        self.additional_info = {}
+        self.link = None
+        if node is not None:
+          for child in node:
+            if child.tag == tagprefix[version] + "name":
+              self.name = child.text
+            elif child.tag == tagprefix[version] + "link":
+              self.link = Link(child,version)
+            elif StripPrefix(child.tag,version) in [ "cmt", "desc", "src", "number", "type" ]:
+              self.additional_info[StripPrefix(child.tag,version)]=child.text
+            elif child.tag == tagprefix[version] + "rtept":
+              self.rtepts.append(GPXTrackPt(child, version))
+            elif child.tag == tagprefix[version] + "extensions":
+              pass
+            else:
+              raise ValueError("Неизвестный тип узла <%s>" % child.tag)
+        for i in self.additional_info.keys():
+          val = self.additional_info[i];
+          if val:
+            if not val.strip(' \n\t'):
+              del self.additional_info[i]
+
+    def write(self,root):
+      rtenode = etree.SubElement(root,"rte")
+      if self.name:
+        etree.SubElement(rtenode,"name").text = self.name
+      if self.link:
+        self.link.write(rtenode)
+      for i in self.additional_info.keys():
+        print("storing %s " % ( i ))
+        etree.SubElement(rtenode,i).text = self.additional_info[i]
+      for i in self.rtepts:
+        i.write(rtenode,"rtept") 
+       
+class GPXTrackSeg:
+    # Один сегмент трека.# 
+
+    def __init__(self, node, version):
+        self.version = version
+        self.trkpts = []
+        self.elevation_gain = 0.0
+        self.elevation_loss = 0.0
+        if node is not None:
+          for child in node:
+            if child.tag == tagprefix[version] + "trkpt":
+              self.trkpts.append(GPXTrackPt(child, self.version))
+            else:
+              raise ValueError("Неизвестный тип узла <%s>" % node.nodeName)
+        self._get_elevation()
+
+    def write(self,root):
+      trksegnode = etree.SubElement(root,"trkseg")
+      for i in self.trkpts:
+        i.write(trksegnode,"trkpt")      
+
+    def _get_elevation(self):
+
+        elev_data = []
+        for pt in self.trkpts:
+          if pt.elevation:
+            elev_data.append(pt.elevation)
+
+        gain = 0.0
+        loss = 0.0
+        last_elevation = None
+
+        window_size = 5
+        i = 0
+        moving_averages = []
+
+        while i < len(elev_data) - window_size + 1:
+          this_window = elev_data[i : i + window_size]
+          window_average = sum(this_window) / window_size
+          moving_averages.append(window_average)
+          i += 1
+          
+        if len(moving_averages)>2:
+          elev_data = moving_averages
+
+        for pt in elev_data:    
+          if last_elevation is not None:
+            try:
+              if pt > last_elevation:
+                  gain += pt - last_elevation
+              else:
+                  loss += last_elevation - pt
+            except:
+              pass      
+          last_elevation=pt    
+
+        self.elevation_gain = gain
+        self.elevation_loss = loss
+
+    def distance(self):
+        # Расчет длины сегмента.# 
+        _length = 0.0
+        last_pt = None
+        for pt in self.trkpts:
+          if last_pt is not None:
+            _length += last_pt.distance(pt)
+          last_pt = pt
+        return _length
+
+    def filtered_distance(self,max_speed):
+        # Расчет длины сегмента с фильтрацией предположительно сбойных участков.# 
+        _length = 0.0
+        last_pt = None
+        for pt in self.trkpts:
+          if last_pt is not None:
+            _delta = last_pt.distance(pt)
+            _time_delta = pt.time - last_pt.time
+            _time_delta_s = _time_delta.days*86400 + _time_delta.seconds + _time_delta.microseconds/1000000
+            if _time_delta_s > 0:
+                if _delta/_time_delta_s < max_speed:
+                    _length += _delta
+            else:
+                _length += _delta
+          last_pt = pt      
+        return _length
+
+    def duration(self):
+        # Расчет продолжительности сегмента.# 
+        return self.trkpts[0].duration(self.trkpts[-1])
+
+    def bound_box(self):
+         # Обрамляющий прямоугольник для сегмента.# 
+      minlat =360
+      maxlat = -360
+      minlon = 360
+      maxlon = -360
+      for pt in self.trkpts:
+        if pt.lat<minlat:
+          minlat=pt.lat
+        if pt.lat>maxlat:
+          maxlat=pt.lat
+        if pt.lon<minlon:
+          minlon=pt.lon
+        if pt.lon>maxlon:
+          maxlon=pt.lon
+      return ((minlat,minlon),(maxlat,maxlon))      
+
+class GPXTrack:
+    # Трек.# 
+
+    def __init__(self, node, version):
+        # Создаем трек из GPX-данных.# 
+
+        self.version = version
+        self.trksegs = []
+        self.sport= None
+        self.additional_info = {}
+        self.name = None
+        self.link = None
+  
+        if node is not None:
+          for child in node:
+            if child.tag == tagprefix[version] + "name":
+              self.name = child.text
+            elif child.tag == tagprefix[version] + "trkseg":
+              if len(child) > 0:
+                self.trksegs.append(GPXTrackSeg(child, self.version))
+            elif child.tag == tagprefix[version] + "link":
+              self.link = Link(child,version)
+            elif StripPrefix(child.tag,version) in [ "number", "desc", "cmt", "src", "type" ]:
+              self.additional_info[StripPrefix(child.tag,version)] = child.text
+            elif child.tag == tagprefix[version] + "extensions":
+              for ext in child:
+                # Из расширений извлекаем вид спорта - специфично для Run.GPS.# 
+                if ext.tag == tagprefix[version] + "sport":
+                  self.sport = ext.text
+  
+        for i in self.additional_info.keys():
+          val = self.additional_info[i];
+          if val:
+            if not val.strip(' \n\t'):
+              del self.additional_info[i]
+
+    def write(self,root):
+      trknode = etree.SubElement(root,"trk")
+      if self.name:
+        etree.SubElement(trknode,"name").text = self.name
+      if self.sport:
+        etree.SubElement(etree.SubElement(trknode,"extensions"),"sport").text=self.sport  
+      if self.link:
+        self.link.write(trknode)
+      for i in self.additional_info.keys():
+        etree.SubElement(trknode,i).text = self.additional_info[i]
+      for i in self.trksegs:
+        i.write(trknode)      
+
+    def elevation_gain(self):
+               # Набор высоты по треку.# 
+        return sum([trkseg.elevation_gain for trkseg in self.trksegs])
+
+    def elevation_loss(self):
+               # Сброс высоты по треку.# 
+        return sum([trkseg.elevation_loss for trkseg in self.trksegs])
+
+    def distance(self):
+        # Длина трека.# 
+        try:
+            return sum([trkseg.distance() for trkseg in self.trksegs])
+        except IndexError:
+            print("Пустой сегмент трека, расчет длины невозможен.")
+
+    def filtered_distance(self,max_speed=100):
+        # Длина трека с фильтрацией сбойных участков.# 
+        try:
+            return sum([trkseg.filtered_distance(max_speed=max_speed) for trkseg in self.trksegs])
+        except IndexError:
+            print("Пустой сегмент трека, расчет длины невозможен.")
+
+    def duration(self):
+        # Продолжительность трека, не включая паузы между сегментами.# 
+        dur = datetime.timedelta(0)
+        for trkseg in self.trksegs:
+            try:
+              dur += trkseg.duration()
+            except:
+              pass  
+        return dur
+
+    def full_duration(self):
+        # Продолжительность трека, включая паузы между сегментами.# 
+        try:
+          return self.start().duration(self.end())
+        except:
+          return None
+
+    def start(self):
+        # Стартовая точка.# 
+        try:
+            return self.trksegs[0].trkpts[0]
+        except IndexError:
+            return None
+
+    def end(self):
+        # Финишная точка.# 
+        try:
+            return self.trksegs[-1].trkpts[-1]
+        except IndexError:
+            return None
+
+    def start_time(self):
+        # Время старта.# 
+        try:
+            return self.start().time
+        except:
+            return None
+
+    def end_time(self):
+        # Время финиша.# 
+        try:
+            return self.end().time
+        except:
+            return None    
+
+    def bound_box(self):
+      # Обрамляющий прямоугольник для всего трека.# 
+      minlat =360
+      maxlat = -360
+      minlon = 360
+      maxlon = -360
+      for trkseg in self.trksegs:
+        for pt in trkseg.trkpts:
+          if pt.lat<minlat:
+            minlat=pt.lat
+          if pt.lat>maxlat:
+            maxlat=pt.lat
+          if pt.lon<minlon:
+            minlon=pt.lon
+          if pt.lon>maxlon:
+            maxlon=pt.lon
+      return ((minlat,minlon),(maxlat,maxlon))      
+
+
+class GPX:
+    # Работа с GPX-документами.# 
+
+    def __init__(self):
+               # Создание пустого GPX-объекта# 
+        PATH = os.path.dirname(__file__)
+        self.creator = None
+        self.time = None
+        self.tracks = []
+        self.routes = []
+        self.waypoints = []
+        self.version = ""
+        self.author = None
+        self.name = None
+        self.link = None
+        self.copyright = None
+        self.meta = {}
+
+    def ReadTree(self,tree):
+               # Загрузка из дерева etree.# 
+        root = tree
+
+        # Test if this is a GPX file or not and if it's version 1.1
+        if root.tag == "{http://www.topografix.com/GPX/1/1}gpx" and root.get("version") == "1.1":
+            self.version = "1.1"
+        elif root.tag == "{http://www.topografix.com/GPX/1/0}gpx" and root.get("version") == "1.0":
+            self.version = "1.0"
+        elif root.get("version") not in [ "1.1", "1.0"]:
+            raise ValueError("Версия формата %s не поддерживается. Используйте GPX 1.1." % root.get("version"))
+        else:
+            raise ValueError("Неизвестный формат дерева")
+        # attempt to validate the xml file against the schema
+
+        # initalize the GPX document for parsing.
+        self._init_version(root)
+        
+
+    def ReadFile(self, fd):
+        # Загрузка из файла.# 
+        gpx_doc = etree.parse(fd)
+        root = gpx_doc.getroot()
+
+        # Test if this is a GPX file or not and if it's version 1.1
+        if root.tag == "{http://www.topografix.com/GPX/1/1}gpx" and root.get("version") == "1.1":
+            self.version = "1.1"
+        elif root.tag == "{http://www.topografix.com/GPX/1/0}gpx" and root.get("version") == "1.0":
+            self.version = "1.0"
+        elif root.get("version") not in [ "1.1", "1.0"]:
+            raise ValueError("Версия формата %s не поддерживается. Используйте GPX 1.1." % root.get("version"))
+        else:
+            raise ValueError("Неизвестный формат файла")
+        # initalize the GPX document for parsing.
+        self._init_version(root)
+
+    def _init_version(self,root):
+        # Инициализация объекта.# 
+        self.creator = root.get("creator")
+        if root is not None:
+          for child in root:
+            if child.tag == tagprefix[self.version] + "author":
+              self.author = child.text
+            elif child.tag == tagprefix[self.version] + "trk":
+              self.tracks.append(GPXTrack(child, self.version))
+            elif child.tag == tagprefix[self.version] + "rte":
+              self.routes.append(GPXRoute(child, self.version))
+            elif child.tag == tagprefix[self.version] + "wpt":
+              self.waypoints.append(GPXTrackPt(child, self.version))
+            elif child.tag == tagprefix[self.version] + "metadata":
+                for meta in child:
+                    if meta.tag == tagprefix[self.version] + "author":
+                      self.author = meta.text
+                    elif meta.tag == tagprefix[self.version] + "name":
+                      self.name = meta.text  
+                    elif meta.tag == tagprefix[self.version] + "bounds":
+                      # Границы пропускаем - их будем вычислять сами заново
+                      pass
+                    elif meta.tag == tagprefix[self.version] + "copyright":
+                      self.copyright = meta.text
+                    elif meta.tag == tagprefix[self.version] + "link":
+                      self.link = Link(meta,self.version) 
+                    else:
+                      self.meta[StripPrefix(meta.tag,self.version)]=meta.text
+                        
+        for i in self.meta.keys():
+          val = self.meta[i];
+          if val:
+            if not val.strip(' \n\t'):
+              del self.meta[i]
+
+    def elevation_gain(self):
+               # Суммарный набор высоты.# 
+        return sum([track.elevation_gain() for track in self.tracks])
+
+    def elevation_loss(self):
+               # Суммарная потеря высоты.# 
+        return sum([track.elevation_loss() for track in self.tracks])
+
+    def distance(self):
+        # Суммарная дистанция.# 
+        try:
+            return sum([track.distance() for track in self.tracks])
+        except IndexError:
+            print("Пустой файл!")
+
+    def filtered_distance(self):
+        # Суммарная дистанция с фильтрацией сбойных данных.# 
+        try:
+            return sum([track.filtered_distance() for track in self.tracks])
+        except IndexError:
+            print("Пустой файл!")
+
+    def duration(self):
+        # Суммарная продолжительность, не включая паузы.# 
+        dur = datetime.timedelta(0)
+        for track in self.tracks:
+            dur += track.duration()
+        return dur
+
+    def full_duration(self):
+        # Суммарная продолжительность, включая паузы# 
+        if hasattr(self.start(), 'duration'):
+            return self.start().duration(self.end())
+        else:
+            return None
+
+    def start(self):
+        # Точка старта.# 
+        return self.tracks[0].start()
+
+    def end(self):
+        # Точка финиша.# 
+        return self.tracks[-1].end()
+
+    def start_time(self):
+        # Время старта.# 
+        if self.start():
+            return self.start().time
+        else:
+            return None
+
+    def end_time(self):
+        # Время финиша.# 
+        return self.end().time
+
+    def bound_box(self):
+      # Обрамляющий прямоугольник для всех треков в файле.#         
+      minlat =360
+      maxlat = -360
+      minlon = 360
+      maxlon = -360
+      for track in self.tracks:
+        for trkseg in track.trksegs:
+          for pt in trkseg.trkpts:
+            if pt.lat<minlat:
+              minlat=pt.lat
+            if pt.lat>maxlat:
+              maxlat=pt.lat
+            if pt.lon<minlon:
+              minlon=pt.lon
+            if pt.lon>maxlon:
+              maxlon=pt.lon
+      return ((minlat,minlon),(maxlat,maxlon))      
+
+    def FixNames(self,linkname):
+      pprint(linkname)
+      pprint(type(linkname))
+      goodname = linkname
+      badname = goodname.encode('ascii','replace')
+      if self.name and self.name.startswith(badname):
+        self.name=self.name.replace(badname,goodname)
+      for i in self.tracks:
+        if i.name and i.name.startswith(badname):
+          i.name = i.name.replace(badname,goodname)
+
+    def ProcessTrackSegs(self,seconds_gap=120):
+      for tr in self.tracks:
+        # Объединяем все точки трека в единую последовательность
+        all_trackpoints = []
+        for trseg in tr.trksegs:
+          all_trackpoints.extend(trseg.trkpts)
+        new_segs = []
+        current_seg = None
+        prev_pt = None
+        for pt in all_trackpoints:
+          if not current_seg:
+          # Начало нового сегмента
+            current_seg = GPXTrackSeg(None,self.version)
+            current_seg.trkpts.append(pt)
+          else: 
+            delta = pt.time - prev_pt.time
+            if delta.days > 0 or delta.seconds > seconds_gap:
+              # Нашли место разрыва
+              new_segs.append(current_seg)
+              current_seg = None  
+            else:
+              current_seg.trkpts.append(pt)  
+          prev_pt=pt   
+        # Если есть, что закинуть в хвост - пишем
+        if current_seg is not None:
+          new_segs.append(current_seg)  
+        # Обработали
+        for trs in new_segs:
+          trs._get_elevation()
+        tr.trksegs = new_segs
+        
+
+    def XMLTree(self):
+      root = etree.Element("gpx", attrib ={"creator": "pygpx",
+                                  "version": "1.1",
+                                  "xmlns": "http://www.topografix.com/GPX/1/1"}); 
+
+      # meta subtree
+
+      meta = etree.SubElement( root, "metadata" );
+      if self.name:
+        metarecord = etree.SubElement( meta, "name" )
+        metarecord.text = self.name
+      if self.author:
+        metarecord = etree.SubElement( meta, "author" )
+        metarecord.text = self.author
+      if self.copyright:
+        metarecord = etree.SubElement( meta, "copyright" )
+        metarecord.text = self.copyright
+      if self.link:  
+        self.link.write(meta)
+      for metatag in self.meta.keys():
+        metarecord = etree.SubElement( meta, metatag )
+        metarecord.text = self.meta[metatag]
+
+      # wpts subtree
+
+      for i in self.waypoints:
+        i.write(root)      
+
+      # routes subtree
+      
+      for i in self.routes:
+        i.write(root)
+
+      # tracks subtree
+      
+      for i in self.tracks:
+        i.write(root)
+        
+      return root
+      
\ No newline at end of file
diff --git a/pyrungps/pyrungps_sync.py b/pyrungps/pyrungps_sync.py
new file mode 100644 (file)
index 0000000..b87a17e
--- /dev/null
@@ -0,0 +1,416 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+import requests
+
+import urllib3
+urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
+#import sys
+import os
+from urllib.parse import quote,unquote
+from lxml import html,etree
+from optparse import OptionParser
+from datetime import date,datetime
+from dateutil.parser import isoparse
+from pyrungps.parsegpx import write_parsed_to_db,check_db_for_training
+import pyrungps.pygpx
+
+from tempfile import NamedTemporaryFile
+
+from pprint import pprint
+
+import pyrungps.render_tiles
+
+import pyrungps.generate_image
+
+def get_page(uname,year,month):
+  
+  trainings = []
+
+  req = requests.get("http://www.gps-sport.net/services/getMonthlyTrainingDataHTML_V2.jsp?userName=%s&year=%s&month=%s&rnd=0.645673"% (uname,year,month), headers = {'User-agent': 'Mozilla/5.0'}, verify=False)
+  page = req.text.encode('utf-8')
+  dom = html.document_fromstring(page)
+
+  for element, attribute, link, pos in dom.iterlinks():
+    if attribute == "href":
+      if link.startswith("/trainings/"):
+        dummy, dummy, link = link.split('/')
+        name, id = link.split('_')
+        trainings.append([ unquote(name), id ])
+      
+  return trainings      
+
+def get_gpx_track(trid,name):
+
+  print("trid=",trid)
+
+  req = requests.get("http://www.gps-sport.net/services/trainingGPX.jsp?trainingID=%s&tz=-180" % (trid), verify=False)
+  xml = etree.fromstring(req.text.encode('utf-8'))
+
+  return xml
+
+def get_osm_list(username,password,year,month):
+
+  url = "https://www.openstreetmap.org/api/0.6/user/gpx_files"
+
+  req = requests.get(url,auth=(username,password))
+  
+  xml = etree.fromstring(req.text.encode('utf-8'))
+
+  res=[]
+
+  for gpx_file in xml:
+    attrib = dict(gpx_file.attrib)
+    try:
+      timestamp=datetime.fromtimestamp(isoparse(attrib['timestamp']))
+      id = attrib['id']
+      description=None
+      for descr in gpx_file.iter('description'):
+        description=descr.text
+      sport=None
+      for tag in gpx_file.iter('tag'):
+        sport=tag.text
+      if timestamp.year==year and timestamp.month==month and description=='Run.GPS Track':
+        record={ 'id': id, 'sport': sport }
+        res.append(record)
+    except:
+      None    
+      
+  return res      
+
+def get_osm_gpx(username,password,track_id):
+
+  url = "https://www.openstreetmap.org/api/0.6/gpx/"+track_id+"/data"
+
+  req = requests.get(url,auth=(username,password))
+  xml = etree.fromstring(req.text.encode('utf-8'))
+
+  return xml
+
+def get_db_gpx(dbx,track_id):
+
+  md, res = dbx.files_download(track_id)
+
+  fs = NamedTemporaryFile(suffix='.tcx',delete = False)
+  tmp_tcx_name=fs.name
+  fs.write(res.content)
+  fs.close()
+  fs = NamedTemporaryFile(suffix='.gpx',delete = False)
+  tmp_gpx_name=fs.name
+  fs.close()
+  
+  os.system("gpsbabel -i gtrnctr -f "+tmp_tcx_name+" -o gpx -F "+tmp_gpx_name)
+
+  with open (tmp_gpx_name, "r") as gpxfile:
+    data=gpxfile.read()
+    
+  os.remove(tmp_gpx_name)  
+  os.remove(tmp_tcx_name)
+
+  xml = etree.fromstring(data.encode('utf-8'))
+  return xml
+
+def get_dbx_list(dbx,username,year,month):
+
+    gpx_list_id = None
+
+    for entry in dbx.files_list_folder('').entries:
+      if entry.name == u'Приложения':
+        for entry_app in dbx.files_list_folder(entry.id).entries:
+          if entry_app.name == u'Run.GPS':
+            print("id="+entry_app.id)
+            gpx_list_id=entry_app.id
+            break
+        break
+
+    res = []
+        
+    if gpx_list_id:
+    
+      for file in dbx.files_list_folder(gpx_list_id).entries:
+      
+        filename,ext = os.path.splitext(file.name)
+        if ext == '.tcx':
+          try:
+            fyear = int(filename[0:4])
+            fmonth = int(filename[5:7])
+            if fyear == year and fmonth == month:
+              sport = filename[41:]
+              timestamp = datetime.strptime(filename[0:19],'%Y-%m-%dT%H_%M_%S')
+              record={ 'id': file.id, 'timestamp': timestamp, 'sport': sport }
+              res.append(record)
+          except:
+            try:
+              fyear = int(filename[0:4])
+              fmonth = int(filename[5:7])
+              if fyear == year and fmonth == month:
+                sport = filename[18:]
+                timestamp = datetime.strptime(filename[0:16],'%Y-%m-%d_%H%M%S')
+                record={ 'id': file.id, 'timestamp': timestamp, 'sport': sport }
+                res.append(record)
+            except:
+              None
+
+    return res        
+      
+
+def sync_db(dbx,username,year,month,dir=".",verbose=False,force=False):
+
+    training_list = get_dbx_list(dbx,username,year,month)
+
+    for training in training_list:
+    
+      filename = "%s/%04d/%02d/%s_%s.gpx" % (dir,year,month,training['sport'],training['id'][3:])   
+      dirname = "%s/%04d/%02d" % (dir,year,month)
+      
+      if os.path.exists(filename) and not force:
+
+        if verbose:
+          print("training %s exists, skipping" % (filename))
+
+      else:  
+
+        try:
+          os.makedirs(dirname)
+        except:
+          None
+
+        xml = get_db_gpx(dbx,training['id'])
+
+        if verbose:
+          print("writing training %s" % (filename))
+
+        gpx = pygpx.GPX()
+        gpx.ReadTree(xml)
+
+        sport = training['sport']
+        timestamp = gpx.tracks[0].start_time()
+        
+        print(sport, timestamp)
+        
+        if check_db_for_training(db,sport,timestamp):
+          print("The same training found")
+          continue
+
+        gpx.author = username
+        gpx.FixNames(training['sport'])
+        gpx.ProcessTrackSegs()
+        for track in gpx.tracks:
+          track.sport=training['sport']
+
+        xml = gpx.XMLTree();
+        f = open(filename,"wb")
+        f.write(etree.tostring(xml,encoding='UTF-8',pretty_print=True))
+        f.close
+        write_parsed_to_db(db,gpx,filename)
+        try:
+          render_tiles.queue_render(db,filename)
+        except:
+          None  
+
+def sync_osm(username,password,year,month,dir=".",verbose=False,force=False):
+
+    training_list = get_osm_list(username,password,year,month)
+
+    for training in training_list:
+    
+      filename = "%s/%04d/%02d/%s_%s.gpx" % (dir,year,month,training['sport'],training['id'])   
+      dirname = "%s/%04d/%02d" % (dir,year,month)
+
+      if os.path.exists(filename) and not force:
+
+        if verbose:
+          print("training %s exists, skipping" % (filename))
+
+      else:  
+      
+        try:
+          os.makedirs(dirname)    
+        except:
+          None
+
+        xml = get_osm_gpx(username,password,training['id'])
+
+        if verbose:
+          print("writing training %s" % (filename))
+
+        gpx = pygpx.GPX()
+        gpx.ReadTree(xml)
+
+        sport = training['sport']
+        timestamp = gpx.tracks[0].start_time()
+        
+        if check_db_for_training(db,sport,timestamp):
+          print("The same training found")
+          continue
+
+        gpx.author = username
+        gpx.FixNames(training['sport'])
+        gpx.ProcessTrackSegs()
+        for track in gpx.tracks:
+          track.sport=training['sport']
+
+        xml = gpx.XMLTree();
+        f = open(filename,"wb")
+        f.write(etree.tostring(xml,encoding='UTF-8',pretty_print=True))
+        f.close
+        write_parsed_to_db(db,gpx,filename)
+        try:
+          render_tiles.queue_render(db,filename)
+        except:
+          None  
+
+def sync_folder(username,year,month,dir=".",verbose=False,force=False):
+
+    training_list = get_page(username,year,month)
+    for tr in training_list:
+     try:
+
+      filename = "%s/%04d/%02d/%s_%s.gpx" % (dir,year,(month+1),tr[0],tr[1])   
+      dirname = "%s/%04d/%02d" % (dir,year,(month+1))
+
+      if os.path.exists(filename) and not force:
+
+        if verbose:
+          print("training %s exists, skipping" % (filename))
+
+      else:  
+
+        try:
+          os.makedirs(dirname)    
+        except:
+          None
+          
+        xml=get_gpx_track(tr[1],tr[0])
+
+        if verbose:
+          print("writing training %s" % (filename))
+
+        gpx = pygpx.GPX()
+        gpx.ReadTree(xml)
+
+        sport = tr[0]
+        timestamp = gpx.tracks[0].start_time()
+        
+        if check_db_for_training(db,sport,timestamp):
+          print("The same training found")
+          continue
+
+        gpx.FixNames(tr[0])
+        gpx.ProcessTrackSegs()
+        
+        xml = gpx.XMLTree();
+        f = open(filename,"wb")
+        f.write(etree.tostring(xml,encoding='UTF-8',pretty_print=True))
+        f.close
+        write_parsed_to_db(db,gpx,filename)
+        try:
+          render_tiles.queue_render(db,filename)
+        except:
+          None  
+
+     except:
+       raise
+
+def main():
+
+    global db;
+    parser = OptionParser()
+    parser.add_option("-d", "--dir", dest="dirname",
+      help="write data to directory", metavar="DIR")
+    parser.add_option("-q", "--quiet",
+      action="store_false", dest="verbose", default=True,
+      help="don't print status messages to stdout")
+    parser.add_option("-f", "--force",
+      action="store_true", dest="force", default=False,
+      help="rewrite all files")
+    parser.add_option("-y", "--yearmonth", dest="yearmonth",
+      help="year and month in YYYY-MM format", metavar="YYYY-MM")                                                          
+    parser.add_option("-u", "--username", dest="username",
+      help="Run.GPS username")                                                          
+    parser.add_option("-o", "--osm-username", dest="osmname",
+      help="OpenStreetMap username")                                                          
+    parser.add_option("-p", "--osm-passwd", dest="osmpasswd",
+      help="OpenStreetMap password")                                                          
+    parser.add_option("-b", "--dropbox", dest="dropboxauth",
+      help="DropBox Auth token")                                                          
+    (options, args) = parser.parse_args()
+
+    username = options.username
+    osmname = options.osmname
+    osmpwd = options.osmpasswd
+    dbauth = options.dropboxauth
+    if not (username or (osmname and osmpwd) or dbauth):
+      print("Run.GPS username or OSM username/password or Dropbox Auth token is mandatory!")
+      return
+
+    if username:
+      pusername = username+'@Run.GPS'
+    elif osmname:
+      pusername = osmname+'@OSM'  
+    elif dbauth:
+      import dropbox
+      dbx = dropbox.Dropbox(dbauth)  
+      pusername = dbx.users_get_current_account().email+'@DBX'
+
+    try:
+      if options.yearmonth:
+        year,month = options.yearmonth.split('-')
+        month = int(month) -1
+        year = int(year)
+        if month<0 or month>11:
+          raise invalid_number
+      else:
+        year = None
+        month = None
+    except:
+      print("Year and month should be in YYYY-MM format!")
+      return 
+      
+    if options.dirname:  
+      outdir = options.dirname
+    else:
+      outdir = '.'
+    
+    db = outdir + '/gpx.db'
+    
+    if year:
+      if options.verbose:
+        print("retrieving trainings for user %s, year %s, month %s to %s" % (pusername,year,month+1,outdir))
+      if username:
+        sync_folder(username,year,month,outdir,options.verbose,options.force)
+      elif osmname:
+        sync_osm(osmname,osmpwd,year,month+1,outdir,options.verbose,options.force)  
+      elif dbauth:
+        sync_db(dbx,pusername,year,month+1,outdir,options.verbose,options.force)  
+    else:
+      if options.verbose:
+        print("retrieving two last months for user %s to %s" % (pusername ,outdir))
+      now = date.today()
+      current_year = now.year
+      current_month = now.month
+      if username:
+        sync_folder(username,current_year,current_month-1,outdir,options.verbose,options.force)
+      elif osmname:
+        sync_osm(osmname,osmpwd,current_year,current_month,outdir,options.verbose,options.force)  
+      elif dbauth:
+        sync_db(dbx,pusername,current_year,current_month,outdir,options.verbose,options.force)  
+      current_month = current_month -1
+      if current_month == 0:
+        current_month = 12
+        current_year = current_year -1
+      if username:
+        sync_folder(username,current_year,current_month-1,outdir,options.verbose,options.force)
+      elif osmname:  
+        sync_osm(osmname,osmpwd,current_year,current_month,outdir,options.verbose,options.force)  
+      elif dbauth:
+        sync_db(dbx,pusername,current_year,current_month,outdir,options.verbose,options.force)  
+
+    generate_image.render_all(db,'/etc/mapnik-osm-carto-data/veloroad-imposm.xml',640,640)
+
+if __name__ == "__main__":
+
+    main()                    
+    
\ No newline at end of file
diff --git a/pyrungps/render_tiles.py b/pyrungps/render_tiles.py
new file mode 100755 (executable)
index 0000000..ea342e8
--- /dev/null
@@ -0,0 +1,174 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+import sqlite3
+import math
+from pprint import pprint
+
+def queue_render(db,filename,forced_max_zoom=None):
+
+  conn = sqlite3.connect(db)
+  conn.text_factory = str
+  cur = conn.cursor()
+        
+  cur.execute("select minlat,minlon,maxlat,maxlon from tracks where filename=?" , (filename,))
+  minlat,minlon,maxlat,maxlon=cur.fetchone()
+  queue_tiles(db,minlat,minlon,maxlat,maxlon,forced_max_zoom)
+
+def deg2num(lat_deg, lon_deg, zoom):
+  lat_rad = math.radians(lat_deg)
+  n = 2.0 ** zoom
+  xtile = int((lon_deg + 180.0) / 360.0 * n)
+  ytile = int((1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n)
+  return (xtile, ytile)
+            
+def queue_tiles(db,minlat,minlon,maxlat,maxlon,forced_max_zoom=None):
+
+  conn = sqlite3.connect(db)
+  
+  # определяем примерный стартовый зум
+
+  minzoom=8
+
+  if forced_max_zoom:
+    maxzoom=forced_max_zoom
+  else:
+    maxzoom=minzoom
+    while True:
+      minx,miny=deg2num(minlat,minlon,maxzoom)
+      maxx,maxy=deg2num(maxlat,maxlon,maxzoom)
+      
+      print(maxzoom,':',minx,'-',maxx,'/',miny,'-',maxy)
+      if (maxx-minx>16) or (maxy-miny>12) or (maxzoom==16):
+        break
+      else:
+        maxzoom=maxzoom+1   
+  
+  if maxzoom<minzoom:
+    minzoom=maxzoom
+  
+  ins = conn.cursor()
+  print(minlat,minlon,maxlat,maxlon,minzoom,maxzoom)
+  ins.execute('insert into render_queue(minlat,maxlat,minlon,maxlon,minzoom,maxzoom) values(?,?,?,?,?,?)',(minlat,maxlat,minlon,maxlon,minzoom,maxzoom))
+
+  conn.commit()        
+
+def process_queue(db,map,force=False,backend="renderd"):
+
+  from os import system
+
+  conn = sqlite3.connect(db)
+  cur = conn.cursor()
+  cur.execute('select id,minlat,maxlat,minlon,maxlon,minzoom,maxzoom from render_queue')
+  list=cur.fetchall()
+
+  for rec in list:
+  
+    id,minlat,maxlat,minlon,maxlon,minzoom,maxzoom=rec
+    
+    if backend == "tirex" or backend == "default":    
+
+      command = 'map='+map+ \
+        ' z='+str(minzoom)+'-'+str(maxzoom)+ \
+        ' lat='+str(minlat)+','+str(maxlat)+ \
+        ' lon='+str(minlon)+','+str(maxlon)
+
+      if force:
+        command = 'tirex-batch -n 0 --prio=50 '+command
+      else:
+        command = 'tirex-batch -n 0 --prio=50 '+command+' -f not-exists'  
+
+      print(command  )
+      
+      if system(command)==0:
+        dcur=conn.cursor()
+        dcur.execute('delete from render_queue where id=?',(id,))
+        conn.commit()
+
+    elif backend == "renderd":
+    
+      print(minlat,minlon,maxlat,maxlon)
+    
+      for zoom in range(minzoom,maxzoom+1):
+        minx,miny=deg2num(minlat,minlon,zoom)
+        maxx,maxy=deg2num(maxlat,maxlon,zoom)
+
+        if minx>maxx:
+          tx=minx
+          maxx=minx
+          minx=tx
+
+        if miny>maxy:
+          ty=miny
+          maxy=miny
+          miny=ty
+
+        print(zoom,minx,miny,maxx,maxy)
+
+        maps = map.split(',')
+        
+        for map_name in maps:
+
+          command = 'render_list -a -m '+map+ \
+                    ' -z '+str(zoom)+' -Z '+str(zoom)+ \
+                    ' -x '+str(minx)+' -X '+str(maxx)+ \
+                    ' -y '+str(miny)+' -Y '+str(maxy)
+          if force:
+            command = command+ ' --force'
+    
+          print(command  )
+      
+          if not system(command) == 0:
+            return
+
+      dcur=conn.cursor()
+      dcur.execute('delete from render_queue where id=?',(id,))
+      conn.commit()
+
+def main():
+
+  from optparse import OptionParser
+  
+  parser = OptionParser()
+  parser.add_option("-d", "--data", dest="directory",
+    help="Data directory", metavar="DIR")
+  parser.add_option("-m", "--map", dest="map",
+    help="Map name", metavar="MAP")
+  parser.add_option("-z", "--zoom", dest="zoom",
+    help="Maximal zoom (forced), used with coordinates pairs (minlat minlon maxlat maxlon) or filename in arguments", metavar="MAP")
+  parser.add_option("-f", "--force", dest="force", 
+    help="Force tile regeneration (on/off), default off")
+  parser.add_option("-r", "--renderer", dest="renderer", 
+    help="Rendering backend: tirex or renderd")
+  (options, args) = parser.parse_args()  
+
+  db=options.directory+'/gpx.db'
+  map=options.map  
+  zoom=options.zoom
+  if not zoom:
+    zoom=12
+  force=(options.force=='on')
+
+  if options.renderer:
+    renderer=options.renderer
+  else:
+    print("Using default rendering backend...")
+    renderer="default"
+
+  if len(args)==1:
+    filename,=args
+    print("Rendering file: "+filename+"\n")
+    queue_render(db,filename)
+  elif len(args)==4:
+    minlat,minlon,maxlat,maxlon=args
+    print("Rendering region "+minlat+'..'+maxlat+' / '+minlon+'..'+maxlon+"\n")
+    queue_tiles(db,float(minlat),float(minlon),float(maxlat),float(maxlon),int(zoom))
+
+  if map:
+    print("Processing map(s) "+map+"\n")
+    process_queue(db,map,force,renderer)
+  
+if __name__ == "__main__":
+
+  main()          
+  
\ No newline at end of file
diff --git a/pyrungps_sync b/pyrungps_sync
new file mode 100755 (executable)
index 0000000..314e177
--- /dev/null
@@ -0,0 +1 @@
+#!/usr/bin/python pyrungps.py
diff --git a/render_tiles.py b/render_tiles.py
deleted file mode 100755 (executable)
index 443ae80..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-#!/usr/bin/env python
-# coding: UTF-8
-
-import sqlite3
-import math
-from pprint import pprint
-
-def queue_render(db,filename,forced_max_zoom=None):
-
-  conn = sqlite3.connect(db)
-  conn.text_factory = str
-  cur = conn.cursor()
-        
-  cur.execute("select minlat,minlon,maxlat,maxlon from tracks where filename=?" , (filename.decode('UTF-8'),))
-  minlat,minlon,maxlat,maxlon=cur.fetchone()
-  queue_tiles(db,minlat,minlon,maxlat,maxlon,forced_max_zoom)
-
-def deg2num(lat_deg, lon_deg, zoom):
-  lat_rad = math.radians(lat_deg)
-  n = 2.0 ** zoom
-  xtile = int((lon_deg + 180.0) / 360.0 * n)
-  ytile = int((1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n)
-  return (xtile, ytile)
-            
-def queue_tiles(db,minlat,minlon,maxlat,maxlon,forced_max_zoom=None):
-
-  conn = sqlite3.connect(db)
-  
-  # определяем примерный стартовый зум
-
-  minzoom=8
-
-  if forced_max_zoom:
-    maxzoom=forced_max_zoom
-  else:
-    maxzoom=minzoom
-    while True:
-      minx,miny=deg2num(minlat,minlon,maxzoom)
-      maxx,maxy=deg2num(maxlat,maxlon,maxzoom)
-      
-      print maxzoom,':',minx,'-',maxx,'/',miny,'-',maxy
-      if (maxx-minx>16) or (maxy-miny>12) or (maxzoom==16):
-        break
-      else:
-        maxzoom=maxzoom+1   
-  
-  if maxzoom<minzoom:
-    minzoom=maxzoom
-  
-  ins = conn.cursor()
-  print minlat,minlon,maxlat,maxlon,minzoom,maxzoom
-  ins.execute('insert into render_queue(minlat,maxlat,minlon,maxlon,minzoom,maxzoom) values(?,?,?,?,?,?)',(minlat,maxlat,minlon,maxlon,minzoom,maxzoom))
-
-  conn.commit()        
-
-def process_queue(db,map,force=False,backend="renderd"):
-
-  from os import system
-
-  conn = sqlite3.connect(db)
-  cur = conn.cursor()
-  cur.execute('select id,minlat,maxlat,minlon,maxlon,minzoom,maxzoom from render_queue')
-  list=cur.fetchall()
-
-  for rec in list:
-  
-    id,minlat,maxlat,minlon,maxlon,minzoom,maxzoom=rec
-    
-    if backend == "tirex" or backend == "default":    
-
-      command = 'map='+map+ \
-        ' z='+str(minzoom)+'-'+str(maxzoom)+ \
-        ' lat='+str(minlat)+','+str(maxlat)+ \
-        ' lon='+str(minlon)+','+str(maxlon)
-
-      if force:
-        command = 'tirex-batch -n 0 --prio=50 '+command
-      else:
-        command = 'tirex-batch -n 0 --prio=50 '+command+' -f not-exists'  
-
-      print command  
-      
-      if system(command)==0:
-        dcur=conn.cursor()
-        dcur.execute('delete from render_queue where id=?',(id,))
-        conn.commit()
-
-    elif backend == "renderd":
-    
-      print minlat,minlon,maxlat,maxlon
-    
-      for zoom in range(minzoom,maxzoom+1):
-        minx,miny=deg2num(minlat,minlon,zoom)
-        maxx,maxy=deg2num(maxlat,maxlon,zoom)
-
-        if minx>maxx:
-          tx=minx
-          maxx=minx
-          minx=tx
-
-        if miny>maxy:
-          ty=miny
-          maxy=miny
-          miny=ty
-
-        print zoom,minx,miny,maxx,maxy
-
-        maps = map.split(',')
-        
-        for map_name in maps:
-
-          command = 'render_list -a -m '+map+ \
-                    ' -z '+str(zoom)+' -Z '+str(zoom)+ \
-                    ' -x '+str(minx)+' -X '+str(maxx)+ \
-                    ' -y '+str(miny)+' -Y '+str(maxy)
-          if force:
-            command = command+ ' --force'
-    
-          print command  
-      
-          if system(command)<>0:
-            return
-
-      dcur=conn.cursor()
-      dcur.execute('delete from render_queue where id=?',(id,))
-      conn.commit()
-
-def main():
-
-  from optparse import OptionParser
-  
-  parser = OptionParser()
-  parser.add_option("-d", "--data", dest="directory",
-    help="Data directory", metavar="DIR")
-  parser.add_option("-m", "--map", dest="map",
-    help="Map name", metavar="MAP")
-  parser.add_option("-z", "--zoom", dest="zoom",
-    help="Maximal zoom (forced), used with coordinates pairs (minlat minlon maxlat maxlon) or filename in arguments", metavar="MAP")
-  parser.add_option("-f", "--force", dest="force", 
-    help="Force tile regeneration (on/off), default off")
-  parser.add_option("-r", "--renderer", dest="renderer", 
-    help="Rendering backend: tirex or renderd")
-  (options, args) = parser.parse_args()  
-
-  db=options.directory+'/gpx.db'
-  map=options.map  
-  zoom=options.zoom
-  if not zoom:
-    zoom=12
-  force=(options.force=='on')
-
-  if options.renderer:
-    renderer=options.renderer
-  else:
-    print "Using default rendering backend..."
-    renderer="default"
-
-  if len(args)==1:
-    filename,=args
-    print "Rendering file: "+filename+"\n"
-    queue_render(db,filename)
-  elif len(args)==4:
-    minlat,minlon,maxlat,maxlon=args
-    print "Rendering region "+minlat+'..'+maxlat+' / '+minlon+'..'+maxlon+"\n"
-    queue_tiles(db,float(minlat),float(minlon),float(maxlat),float(maxlon),int(zoom))
-
-  if map:
-    print "Processing map(s) "+map+"\n"
-    process_queue(db,map,force,renderer)
-  
-if __name__ == "__main__":
-
-  main()          
-  
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..1ccb8be
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup, find_packages
+
+setup(
+  name='pyrungps',
+  version='0.1',
+  description='Simple python tool to sync gpx tracks from GPS-SPORT site',
+  url='http://rvb.name/gpx',
+  author='Roman Bazalevskiy',
+  author_email='rvb@rvb.name',
+  license='WTFPL?',
+  packages=['pyrungps'],
+  zip_safe=False
+)