| @@ -0,0 +1,222 @@ | ||
| <?xml version="1.0"?> | ||
| <interface> | ||
| <requires lib="gtk+" version="2.16"/> | ||
| <!-- interface-naming-policy project-wide --> | ||
| <object class="GtkDialog" id="dialog1"> | ||
| <property name="border_width">5</property> | ||
| <property name="type">popup</property> | ||
| <property name="modal">True</property> | ||
| <property name="window_position">center-on-parent</property> | ||
| <property name="destroy_with_parent">True</property> | ||
| <property name="icon_name">applications-system</property> | ||
| <property name="type_hint">dialog</property> | ||
| <property name="has_separator">False</property> | ||
| <child internal-child="vbox"> | ||
| <object class="GtkVBox" id="dialog-vbox1"> | ||
| <property name="visible">True</property> | ||
| <property name="orientation">vertical</property> | ||
| <property name="spacing">2</property> | ||
| <child> | ||
| <object class="GtkFrame" id="frame1"> | ||
| <property name="visible">True</property> | ||
| <property name="label_xalign">0</property> | ||
| <property name="shadow_type">in</property> | ||
| <child> | ||
| <object class="GtkTable" id="table1"> | ||
| <property name="visible">True</property> | ||
| <property name="n_rows">3</property> | ||
| <property name="n_columns">4</property> | ||
| <child> | ||
| <object class="GtkLabel" id="label2"> | ||
| <property name="visible">True</property> | ||
| <property name="label" translatable="yes">Verzeichnis für heruntergeladene Geoaches:</property> | ||
| </object> | ||
| <packing> | ||
| <property name="right_attach">4</property> | ||
| </packing> | ||
| </child> | ||
| <child> | ||
| <object class="GtkEntry" id="entry1"> | ||
| <property name="visible">True</property> | ||
| <property name="can_focus">True</property> | ||
| <property name="invisible_char">●</property> | ||
| </object> | ||
| <packing> | ||
| <property name="right_attach">4</property> | ||
| <property name="top_attach">1</property> | ||
| <property name="bottom_attach">2</property> | ||
| </packing> | ||
| </child> | ||
| <child> | ||
| <object class="GtkCheckButton" id="checkbutton1"> | ||
| <property name="label" translatable="yes">heruntergeladene Bilder verkleinern auf</property> | ||
| <property name="visible">True</property> | ||
| <property name="can_focus">True</property> | ||
| <property name="receives_default">False</property> | ||
| <property name="draw_indicator">True</property> | ||
| </object> | ||
| <packing> | ||
| <property name="right_attach">2</property> | ||
| <property name="top_attach">2</property> | ||
| <property name="bottom_attach">3</property> | ||
| </packing> | ||
| </child> | ||
| <child> | ||
| <object class="GtkEntry" id="entry2"> | ||
| <property name="visible">True</property> | ||
| <property name="can_focus">True</property> | ||
| <property name="invisible_char">●</property> | ||
| <property name="width_chars">4</property> | ||
| </object> | ||
| <packing> | ||
| <property name="left_attach">2</property> | ||
| <property name="right_attach">3</property> | ||
| <property name="top_attach">2</property> | ||
| <property name="bottom_attach">3</property> | ||
| <property name="x_options">GTK_FILL</property> | ||
| </packing> | ||
| </child> | ||
| <child> | ||
| <object class="GtkLabel" id="label3"> | ||
| <property name="visible">True</property> | ||
| <property name="label" translatable="yes">Pixel</property> | ||
| </object> | ||
| <packing> | ||
| <property name="left_attach">3</property> | ||
| <property name="right_attach">4</property> | ||
| <property name="top_attach">2</property> | ||
| <property name="bottom_attach">3</property> | ||
| </packing> | ||
| </child> | ||
| </object> | ||
| </child> | ||
| <child type="label"> | ||
| <object class="GtkLabel" id="label1"> | ||
| <property name="visible">True</property> | ||
| <property name="label" translatable="yes"><b>Optionen zum Herunterladen</b></property> | ||
| <property name="use_markup">True</property> | ||
| </object> | ||
| </child> | ||
| </object> | ||
| <packing> | ||
| <property name="position">1</property> | ||
| </packing> | ||
| </child> | ||
| <child> | ||
| <object class="GtkFrame" id="frame2"> | ||
| <property name="visible">True</property> | ||
| <property name="label_xalign">0</property> | ||
| <property name="shadow_type">in</property> | ||
| <child> | ||
| <object class="GtkAlignment" id="alignment1"> | ||
| <property name="visible">True</property> | ||
| <property name="left_padding">12</property> | ||
| <child> | ||
| <object class="GtkTable" id="table2"> | ||
| <property name="visible">True</property> | ||
| <property name="n_rows">2</property> | ||
| <property name="n_columns">2</property> | ||
| <child> | ||
| <object class="GtkLabel" id="label5"> | ||
| <property name="visible">True</property> | ||
| <property name="label" translatable="yes">Benutzername</property> | ||
| </object> | ||
| </child> | ||
| <child> | ||
| <object class="GtkLabel" id="label6"> | ||
| <property name="visible">True</property> | ||
| <property name="label" translatable="yes">Passwort</property> | ||
| </object> | ||
| <packing> | ||
| <property name="top_attach">1</property> | ||
| <property name="bottom_attach">2</property> | ||
| </packing> | ||
| </child> | ||
| <child> | ||
| <object class="GtkEntry" id="entry3"> | ||
| <property name="visible">True</property> | ||
| <property name="can_focus">True</property> | ||
| <property name="invisible_char">●</property> | ||
| </object> | ||
| <packing> | ||
| <property name="left_attach">1</property> | ||
| <property name="right_attach">2</property> | ||
| </packing> | ||
| </child> | ||
| <child> | ||
| <object class="GtkEntry" id="entry4"> | ||
| <property name="visible">True</property> | ||
| <property name="can_focus">True</property> | ||
| <property name="invisible_char">●</property> | ||
| <property name="invisible_char_set">True</property> | ||
| </object> | ||
| <packing> | ||
| <property name="left_attach">1</property> | ||
| <property name="right_attach">2</property> | ||
| <property name="top_attach">1</property> | ||
| <property name="bottom_attach">2</property> | ||
| </packing> | ||
| </child> | ||
| </object> | ||
| </child> | ||
| </object> | ||
| </child> | ||
| <child type="label"> | ||
| <object class="GtkLabel" id="label4"> | ||
| <property name="visible">True</property> | ||
| <property name="label" translatable="yes"><b>Zugangsdaten</b></property> | ||
| <property name="use_markup">True</property> | ||
| </object> | ||
| </child> | ||
| </object> | ||
| <packing> | ||
| <property name="position">2</property> | ||
| </packing> | ||
| </child> | ||
| <child internal-child="action_area"> | ||
| <object class="GtkHButtonBox" id="dialog-action_area1"> | ||
| <property name="visible">True</property> | ||
| <property name="layout_style">end</property> | ||
| <child> | ||
| <object class="GtkButton" id="button2"> | ||
| <property name="label" translatable="yes">gtk-cancel</property> | ||
| <property name="visible">True</property> | ||
| <property name="can_focus">True</property> | ||
| <property name="receives_default">True</property> | ||
| <property name="use_stock">True</property> | ||
| </object> | ||
| <packing> | ||
| <property name="expand">False</property> | ||
| <property name="fill">False</property> | ||
| <property name="position">0</property> | ||
| </packing> | ||
| </child> | ||
| <child> | ||
| <object class="GtkButton" id="button1"> | ||
| <property name="label" translatable="yes">gtk-apply</property> | ||
| <property name="visible">True</property> | ||
| <property name="can_focus">True</property> | ||
| <property name="receives_default">True</property> | ||
| <property name="use_stock">True</property> | ||
| </object> | ||
| <packing> | ||
| <property name="expand">False</property> | ||
| <property name="fill">False</property> | ||
| <property name="position">1</property> | ||
| </packing> | ||
| </child> | ||
| </object> | ||
| <packing> | ||
| <property name="expand">False</property> | ||
| <property name="pack_type">end</property> | ||
| <property name="position">0</property> | ||
| </packing> | ||
| </child> | ||
| </object> | ||
| </child> | ||
| <action-widgets> | ||
| <action-widget response="0">button2</action-widget> | ||
| <action-widget response="1">button1</action-widget> | ||
| </action-widgets> | ||
| </object> | ||
| </interface> |
| @@ -0,0 +1,50 @@ | ||
| #!/usr/bin/python | ||
| # -*- coding: utf-8 -*- | ||
| import urllib | ||
| import urllib2, cookielib | ||
|
|
||
| class FileDownloader(): | ||
| USER_AGENT='User-Agent: Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.12) Gecko/2009070811 Windows NT Firefox/3.1' | ||
|
|
||
| def __init__(self, username, password): | ||
| self.username = username | ||
| self.password = password | ||
| self.logged_in = False | ||
|
|
||
| def update_userdata(self, username, password): | ||
| self.username = username | ||
| self.password = password | ||
| self.logged_in = False | ||
| print "Up" | ||
|
|
||
| def login(self): | ||
| cj = cookielib.CookieJar() | ||
| opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) | ||
| urllib2.install_opener(opener) | ||
|
|
||
| url = 'http://www.geocaching.com/Default.aspx' | ||
| values = {'ctl00$MiniProfile$loginUsername':self.username, | ||
| 'ctl00$MiniProfile$loginPassword':self.password, | ||
| 'ctl00$MiniProfile$loginRemember': 'on', | ||
| 'ctl00$MiniProfile$LoginBtn' : 'Go', | ||
| '__EVENTTARGET' : '', | ||
| '__EVENTARGUMENT' : '' | ||
| } | ||
|
|
||
| #headers = {'User-Agent' : self.USER_AGENT} | ||
| data = urllib.urlencode(values) | ||
| req = urllib2.Request(url, data) | ||
| response = urllib2.urlopen(req) | ||
| page = response.read() | ||
| if 'combination does not match' in page: | ||
| raise Exception("Passwort oder Benutzername falsch!") | ||
|
|
||
| def get_reader(self, url, values = None): | ||
| if not self.logged_in: | ||
| self.login() | ||
| #headers = {'User-Agent' : self.USER_AGENT} | ||
| if values == None: | ||
| return urllib2.urlopen(urllib2.Request(url)) | ||
| else: | ||
| data = urllib.urlencode(values) | ||
| return urllib2.urlopen(urllib2.Request(url, data)) |
| @@ -0,0 +1,114 @@ | ||
| #!/usr/bin/python | ||
| # -*- coding: utf-8 -*- | ||
|
|
||
| import math | ||
| import re | ||
|
|
||
| class Coordinate(): | ||
| SQLROW = {'lat': 'REAL', 'lon' : 'REAL', 'name' : 'TEXT'} | ||
|
|
||
| FORMAT_D = 0 | ||
| FORMAT_DM = 1 | ||
| re_to_dm_array = re.compile('^(\d?)(\d)(\d) (\d)(\d)\.(\d)(\d)(\d)$') | ||
| re_to_d_array = re.compile('^(\d?)(\d)(\d).(\d)(\d)(\d)(\d)(\d)$') | ||
|
|
||
| def __init__(self, lat, lon, name = "No Name"): | ||
| self.lat = lat | ||
| self.lon = lon | ||
| self.name = name | ||
|
|
||
| def from_d(self, lat, lon): | ||
| self.lat = lat | ||
| self.lon = lon | ||
|
|
||
| def from_dm(self, latdd, latmm, londd, lonmm): | ||
| self.lat = latdd + (latmm/60) | ||
| self.lon = londd + (lonmm/60) | ||
|
|
||
| def from_dm_array(self, sign_lat, lat, sign_lon, lon): | ||
| self.from_dm(sign_lat * (lat[0]*10 + lat[1]), | ||
| float(str(lat[2]) + str(lat[3]) + "." + str(lat[4]) + str(lat[5]) + str(lat[6])), | ||
| sign_lon * (lon[0] * 100 + lon[1] * 10 + lon[2]), | ||
| float(str(lon[3]) + str(lon[4]) + "." + str(lon[5]) + str(lon[6]) + str(lon[7]))) | ||
|
|
||
| def from_d_array(self, sign_lat, lat, sign_lon, lon): | ||
| self.lat = int(sign_lat) * float("%d%d.%d%d%d%d%d" % tuple(lat)) | ||
| self.lon = int(sign_lon) * float("%d%d%d.%d%d%d%d%d" % tuple(lon)) | ||
|
|
||
| def to_dm_array(self): | ||
| [[lat_d, lat_m],[lon_d, lon_m]] = self.to_dm() | ||
|
|
||
| d_lat = self.re_to_dm_array.search("%02d %06.3f" % (abs(lat_d), abs(lat_m))) | ||
| d_lon = self.re_to_dm_array.search("%03d %06.3f" % (abs(lon_d), abs(lon_m))) | ||
| return [ | ||
| [d_lat.group(i) for i in range (2, 9)], | ||
| [d_lon.group(i) for i in range (1, 9)] | ||
| ] | ||
|
|
||
| def to_d_array(self): | ||
|
|
||
| d_lat = self.re_to_d_array.search("%08.5f" % abs(self.lat)) | ||
| d_lon = self.re_to_d_array.search("%09.5f" % abs(self.lon)) | ||
| return [ | ||
| [d_lat.group(i) for i in range (2, 7)], | ||
| [d_lon.group(i) for i in range (1, 7)] | ||
| ] | ||
|
|
||
| def to_dm(self): | ||
| return [ [int(math.floor(self.lat)), (self.lat - math.floor(self.lat)) * 60] , | ||
| [int(math.floor(self.lon)), (self.lon - math.floor(self.lon)) * 60] ] | ||
|
|
||
| def bearing_to(self, target): | ||
| lat1 = math.radians(self.lat) | ||
| lat2 = math.radians(target.lat) | ||
| lon1 = math.radians(self.lon) | ||
| lon2 = math.radians(target.lon) | ||
|
|
||
| dlon = math.radians(target.lon - self.lon); | ||
| y = math.sin(dlon) * math.cos(lat2) | ||
| x = math.cos(lat1) * math.sin(lat2) - math.sin(lat1)*math.cos(lat2)*math.cos(dlon) | ||
| bearing = math.degrees(math.atan2(y, x)) | ||
| return (360 + bearing) % 360 | ||
|
|
||
| def get_lat(self, format): | ||
| l = abs(self.lat) | ||
| if self.lat > 0: | ||
| c = 'N' | ||
| else: | ||
| c = 'S' | ||
| if format == self.FORMAT_D: | ||
| return "%s%8.5f°" % (c, l) | ||
| elif format == self.FORMAT_DM: | ||
| return "%s%2d° %06.3f'" % (c, math.floor(l), (l - math.floor(l)) * 60) | ||
|
|
||
| def get_lon(self, format): | ||
| l = abs(self.lon) | ||
| if self.lon > 0: | ||
| c = 'E' | ||
| else: | ||
| c = 'W' | ||
| if format == self.FORMAT_D: | ||
| return "%s%9.5f°" % (c, l) | ||
| elif format == self.FORMAT_DM: | ||
| return "%s%3d° %06.3f'" % (c, math.floor(l), (l - math.floor(l)) * 60) | ||
|
|
||
| def distance_to (self, target): | ||
| R = 6371000; | ||
| dlat = math.pow(math.sin(math.radians(target.lat-self.lat)/2),2) | ||
| dlon = math.pow(math.sin(math.radians(target.lon-self.lon)/2),2) | ||
| a = dlat + math.cos(math.radians(self.lat)) * math.cos(math.radians(target.lat)) * dlon; | ||
| c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)); | ||
| return R * c; | ||
|
|
||
| def __str__(self): | ||
| return "%s %s" % (self.get_lat(Coordinate.FORMAT_DM), self.get_lon(Coordinate.FORMAT_DM)) | ||
|
|
||
| def serialize(self): | ||
|
|
||
| return {'lat': self.lat, 'lon' : self.lon, 'name' : self.name} | ||
|
|
||
| def unserialize(self, data): | ||
| self.lat = data['lat'] | ||
| self.lon = data['lon'] | ||
| self.name = data['name'] | ||
|
|
| @@ -0,0 +1,108 @@ | ||
| #!/usr/bin/python | ||
| # -*- coding: utf-8 -*- | ||
|
|
||
| import socket | ||
| import re | ||
| import geo | ||
|
|
||
| class GpsReader(): | ||
|
|
||
| EMPTY = { | ||
| 'position': None, | ||
| 'altitude': None, | ||
| 'bearing': None, | ||
| 'speed': None, | ||
| 'sats': 0, | ||
| 'sats_known': 0 | ||
| } | ||
|
|
||
| def __init__(self, gui): | ||
| self.gui = gui | ||
| self.status = "connecting..." | ||
| self.connected = False | ||
| self.connect() | ||
|
|
||
|
|
||
| def connect(self): | ||
| try: | ||
| global gpsd_connection | ||
| gpsd_connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| gpsd_connection.connect(("127.0.0.1", 2947)) | ||
| self.status = "connected" | ||
| self.connected = True | ||
| except: | ||
| self.status = "Could not connect to GPSD on Localhost, Port 2947" | ||
| print "Could not connect" | ||
| self.connected = False | ||
|
|
||
| def get_data(self): | ||
| try: | ||
| if not self.connected: | ||
| self.connect() | ||
| if not self.connected: | ||
| return self.EMPTY | ||
| gpsd_connection.send("%s\r\n" % 'o') | ||
| data = gpsd_connection.recv(512) | ||
| gpsd_connection.send("%s\r\n" % 'y') | ||
| quality_data = gpsd_connection.recv(512) | ||
|
|
||
| # 1: Parse Quality Data | ||
|
|
||
| # example output: | ||
| # GPSD,Y=- 1243847265.000 10:32 3 105 0 0:2 36 303 20 0:16 9 65 26 | ||
| # 1:13 87 259 35 1:4 60 251 30 1:23 54 60 37 1:25 51 149 24 0:8 2 | ||
| # 188 0 0:7 33 168 24 1:20 26 110 28 1: | ||
|
|
||
| if quality_data == "GPSD,Y=?": | ||
| sats = 0 | ||
| sats_known = 0 | ||
| else: | ||
| sats = 0 | ||
| groups = quality_data.split(':') | ||
| sats_known = int(groups[0].split(' ')[2]) | ||
| for i in range(1, sats_known): | ||
| if groups[i].split(' ')[4] == "1": | ||
| sats = sats + 1 | ||
|
|
||
| if data.strip() == "GPSD,O=?": | ||
| self.status = "No GPS signal" | ||
| return { | ||
| 'position': None, | ||
| 'altitude': None, | ||
| 'bearing': None, | ||
| 'speed': None, | ||
| 'sats': sats, | ||
| 'sats_known': sats_known | ||
| } | ||
|
|
||
| # 2: Get current position, altitude, bearing and speed | ||
|
|
||
| # example output: | ||
| # GPSD,O=- 1243530779.000 ? 49.736876 6.686998 271.49 1.20 1.61 49.8566 0.050 -0.175 ? ? ? 3 | ||
| # GPSD,O=- 1251325613.000 ? 49.734453 6.686360 ? 10.55 ? 180.1476 1.350 ? ? ? ? 2 | ||
|
|
||
| # or | ||
| # GPSD,O=? | ||
| try: | ||
| [tag, timestamp, time_error, lat, lon, alt, err_hor, err_vert, track, speed, delta_alt, err_track, err_speed, err_delta_alt, mode] = data.split(' ') | ||
| except: | ||
| print "GPSD Output: \n%s\n -- cannot be parsed." % data | ||
| self.status = "Could not read GPSD output." | ||
|
|
||
| return { | ||
| 'position': geo.Coordinate(float(lat), float(lon)), | ||
| 'altitude': self.to_float(alt), | ||
| 'bearing': self.to_float(track), | ||
| 'speed': self.to_float(speed), | ||
| 'sats': int(sats), | ||
| 'sats_known': sats_known | ||
| } | ||
| except Exception as e: | ||
| print "Fehler beim Auslesen der Daten: %s " % e | ||
| return self.EMPTY | ||
|
|
||
| def to_float(self, string): | ||
| try: | ||
| return float(string) | ||
| except: | ||
| return 0.0 |
| @@ -0,0 +1,200 @@ | ||
| #!/usr/bin/python | ||
| # -*- coding: utf-8 -*- | ||
|
|
||
| import urllib | ||
| import os | ||
| import threading | ||
| import thread | ||
| import math | ||
| import gobject | ||
| import geo | ||
| import gtk | ||
|
|
||
| class TileLoader(threading.Thread): | ||
| downloading = [] | ||
| semaphore = threading.Semaphore(5) | ||
| lock = thread.allocate_lock() #download-lock | ||
| drawlock = thread.allocate_lock() | ||
| running_threads = 0 | ||
|
|
||
| #steps: | ||
| # - check if file exists. | ||
| # - NO: Download tile | ||
| # - load pixbuf from file | ||
| # - find target position in current pixmap (lock!) | ||
| # - draw to pixmap (still locked!) | ||
| # - call queue_draw | ||
| # optional: acquire locks in all related parts of gui | ||
| def __init__(self, tile, zoom, gui, base_dir): | ||
| threading.Thread.__init__(self) | ||
| self.daemon = False | ||
| self.tile = tile | ||
| self.zoom = zoom | ||
| self.gui = gui | ||
| self.base_dir = base_dir | ||
|
|
||
| def run(self): | ||
| TileLoader.running_threads += 1 | ||
| filename = os.path.join("%d" % self.zoom, "%d" % self.tile[0], "%d.png" % self.tile[1]) | ||
| self.local_filename = "%s%s" % (self.base_dir, filename) | ||
| self.remote_filename = "http://tile.openstreetmap.org/mapnik/%s" % filename | ||
| answer = True | ||
| if not os.path.isfile(self.local_filename): | ||
| path_1 = "%s%d" % (self.base_dir, self.zoom) | ||
| path_2 = "%s/%d" % (path_1, self.tile[0]) | ||
| try: | ||
| if not os.path.exists(path_1): | ||
| os.mkdir(path_1) | ||
| if not os.path.exists(path_2): | ||
| os.mkdir(path_2) | ||
| except: | ||
| 1 #this may file due to threading issues. | ||
| # too lazy to do proper locking here | ||
| # so just forget about the error | ||
|
|
||
|
|
||
| if not os.path.isfile(self.local_filename): | ||
| answer = self.download(self.remote_filename, self.local_filename) | ||
| # now the file hopefully exists | ||
| if not (answer == False): | ||
| if self.load(): | ||
| gobject.idle_add(self.draw) | ||
|
|
||
| def load(self, tryno = 0): | ||
| # load the pixbuf to memory | ||
| try: | ||
| self.pbuf = gtk.gdk.pixbuf_new_from_file(self.local_filename) | ||
| return True | ||
| except Exception as inst: | ||
| if tryno == 0: | ||
| return self.recover() | ||
| else: | ||
| TileLoader.running_threads -= 1 | ||
| if TileLoader.running_threads == 0: | ||
| gobject.idle_add(self.gui.draw_marks) | ||
| return False | ||
|
|
||
| def recover(self): | ||
| try: | ||
| os.remove(self.local_filename) | ||
| except: | ||
| pass | ||
| self.download(self.remote_filename, self.local_filename) | ||
| return self.load(1) | ||
|
|
||
| def draw(self): | ||
| acquired = False | ||
| try: | ||
|
|
||
| widget = self.gui.drawing_area | ||
| gc = widget.get_style().fg_gc[gtk.STATE_NORMAL] | ||
| gc.set_function(gtk.gdk.COPY) | ||
| # to draw "night mode": INVERT | ||
|
|
||
| a, b, width, height = widget.get_allocation() | ||
| size = self.gui.ts.tile_size() | ||
| x = self.gui.map_center_x | ||
| y = self.gui.map_center_y | ||
| xi = int(self.gui.map_center_x) | ||
| yi = int(self.gui.map_center_y) | ||
| offset_x = int(self.gui.map_width/2 - (x - int(x)) * size) | ||
| offset_y = int(self.gui.map_height/2 -(y - int(y)) * size) | ||
| span_x = int(math.ceil(float(self.gui.map_width)/(size * 2.0))) | ||
| span_y = int(math.ceil(float(self.gui.map_height)/(size * 2.0))) | ||
| if self.tile[0] in range(xi - span_x, xi + span_x + 1, 1) and self.tile[1] in range(yi - span_y, yi + span_y + 1, 1) and self.zoom == self.gui.ts.zoom: | ||
| dx = (self.tile[0] - xi) * size + offset_x | ||
| dy = (self.tile[1] - yi) * size + offset_y | ||
|
|
||
| self.drawlock.acquire() | ||
| acquired = True | ||
| self.gui.pixmap.draw_pixbuf(gc, self.pbuf, 0, 0, dx, dy, -1, -1) | ||
|
|
||
| widget.queue_draw_area(max(self.gui.draw_root_x + self.gui.draw_at_x + dx, 0), max(self.gui.draw_root_y + self.gui.draw_at_y + dy, 0), size, size) | ||
|
|
||
|
|
||
| finally: | ||
| if acquired: | ||
| self.drawlock.release() | ||
|
|
||
| TileLoader.running_threads -= 1 | ||
|
|
||
| #if TileLoader.running_threads <= 0: | ||
| #gobject.idle_add(self.gui.draw_marks, self) | ||
|
|
||
| def download(self, remote, local): | ||
|
|
||
| self.lock.acquire() | ||
| try: | ||
| if (remote in self.downloading): | ||
| return None | ||
| if os.path.exists(local): | ||
| return None | ||
| self.downloading.append(remote) | ||
| finally: | ||
| self.lock.release() | ||
|
|
||
| self.semaphore.acquire() | ||
| try: | ||
| if not self.zoom == self.gui.ts.zoom: | ||
| return None | ||
| can_download = False | ||
| webFile = urllib.urlopen(remote) | ||
| if "text/html" in webFile.info()['Content-Type']: | ||
| print "File not found: %s" % remote | ||
| return False | ||
| localFile = open(local, 'wb') | ||
| localFile.write(webFile.read()) | ||
| webFile.close() | ||
| localFile.close() | ||
| can_download = True | ||
| except: | ||
| pass | ||
| finally: | ||
| self.semaphore.release() | ||
| #self.lock.acquire() | ||
| #try: | ||
| self.downloading.remove(remote) | ||
| #if len(self.downloading) == 0 and can_download: | ||
| # Gui.schedule_redraw = True | ||
| #finally: | ||
| # self.lock.release() | ||
| return True | ||
|
|
||
| class TileServer(): | ||
|
|
||
| def __init__(self): | ||
| self.zoom = 14 | ||
| self.max_zoom = 17 | ||
|
|
||
| def get_zoom(self): | ||
| return self.zoom | ||
|
|
||
| def set_zoom(self, zoom): | ||
| if zoom < 1 or zoom > self.max_zoom: | ||
| return | ||
| self.zoom = zoom | ||
|
|
||
| def tile_size(self): | ||
| return 256 | ||
|
|
||
| def deg2tilenum(self, lat_deg, lon_deg): | ||
| lat_rad = lat_deg * math.pi / 180.0 | ||
| n = 2.0 ** self.zoom | ||
| xtile = int((lon_deg + 180.0) / 360.0 * n) | ||
| ytile = int((1.0 - math.log(math.tan(lat_rad) + (1.0 / math.cos(lat_rad))) / math.pi) / 2.0 * n) | ||
| return(xtile, ytile) | ||
|
|
||
| def deg2num(self, coord): | ||
| pi = 3.1415927 | ||
| lat_rad = (coord.lat * math.pi) / 180.0 | ||
| n = 2.0 ** self.zoom | ||
| xtile = (coord.lon + 180.0) / 360.0 * n | ||
| ytile = (1.0 - math.log(math.tan(lat_rad) + (1.0 / math.cos(lat_rad))) / math.pi) / 2.0 * n | ||
| return(xtile, ytile) | ||
|
|
||
| def num2deg(self, xtile, ytile): | ||
| n = 2.0 ** self.zoom | ||
| lon_deg = xtile / n * 360.0 - 180.0 | ||
| lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n))) | ||
| lat_deg = lat_rad * 180.0 / math.pi | ||
| return geo.Coordinate(lat_deg, lon_deg) |
| @@ -0,0 +1,195 @@ | ||
| #!/usr/bin/python | ||
| # -*- coding: utf-8 -*- | ||
|
|
||
| #import downloader | ||
| import geocaching | ||
| import json | ||
| import sqlite3 | ||
| import re | ||
| import copy | ||
|
|
||
|
|
||
| class PointProvider(): | ||
| USER_AGENT='User-Agent: Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.0.12) Gecko/2009070811 Windows NT Firefox/3.1' | ||
|
|
||
| def __init__(self, filename, downloader, ctype, table): | ||
| self.filterstack = [] | ||
| self.conn = sqlite3.connect(filename) | ||
| self.conn.row_factory = sqlite3.Row | ||
| self.conn.text_factory = str | ||
| self.ctype = ctype | ||
| self.downloader = downloader | ||
| self.cache_table = table | ||
| self.filterstring = [] | ||
| self.filterargs = [] | ||
| c = self.conn.cursor() | ||
| c.execute('CREATE TABLE IF NOT EXISTS %s (%s)' % (self.cache_table, ', '.join([' '.join(m) for m in self.ctype.SQLROW.items()]))) | ||
| c.execute('CREATE INDEX IF NOT EXISTS %s_latlon ON %s (lat ASC, lon ASC)' % (self.cache_table, self.cache_table)) | ||
| c.close() | ||
|
|
||
| def save(self): | ||
| self.conn.commit() | ||
|
|
||
| def __del__(self): | ||
| print "closing" | ||
| self.conn.commit() | ||
| self.conn.close() | ||
|
|
||
| def add_point(self, p, replace = False): | ||
| c = self.conn.cursor() | ||
| if p.found: | ||
| f = 1 | ||
| else: | ||
| f = 0 | ||
| if replace: | ||
| mode = "REPLACE" | ||
| else: | ||
| mode = "IGNORE" | ||
| print p.serialize() | ||
| c.execute("INSERT OR %s INTO %s (`%s`) VALUES (%s)" % (mode, self.cache_table, '`, `'.join(self.ctype.SQLROW.keys()), ', '.join([':%s' % k for k in self.ctype.SQLROW.keys()])), p.serialize()) | ||
| c.close() | ||
|
|
||
| def get_points(self, c1, c2): | ||
|
|
||
| c = self.conn.cursor() | ||
|
|
||
| c.execute('SELECT * FROM %s WHERE (lat BETWEEN ? AND ?) AND (lon BETWEEN ? AND ?)' % self.cache_table, (min(c1.lat, c2.lat), max(c1.lat, c2.lat), min(c1.lon, c2.lon), max(c1.lon, c2.lon))) | ||
| points = [] | ||
| for row in c: | ||
| coord = self.ctype(row['lat'], row['lon']) | ||
| coord.unserialize(row) | ||
| points.append(coord) | ||
| c.close() | ||
| return points | ||
|
|
||
| def get_titles_and_names(self): | ||
| c = self.conn.cursor() | ||
| c.execute('SELECT name, title FROM %s' % self.cache_table) | ||
| strings = [] | ||
| for row in c: | ||
| strings.append(row['name']) | ||
| strings.append(row['title']) | ||
| c.close() | ||
| return strings | ||
|
|
||
|
|
||
| def get_nearest_point_filter(self, center, c1, c2): | ||
| filterstring = copy.copy(self.filterstring) | ||
| filterargs = copy.copy(self.filterargs) | ||
|
|
||
| filterstring.append('((lat BETWEEN ? AND ?) AND (lon BETWEEN ? AND ?))') | ||
| filterargs.append(min(c1.lat, c2.lat)) | ||
| filterargs.append(max(c1.lat, c2.lat)) | ||
| filterargs.append(min(c1.lon, c2.lon)) | ||
| filterargs.append(max(c1.lon, c2.lon)) | ||
|
|
||
| c = self.conn.cursor() | ||
| # we don't have 'power' or other advanced mathematic operators | ||
| # in sqlite, so doing fake distance calculation here | ||
| query = 'SELECT * FROM %s WHERE %s ORDER BY ABS(lat-?)*ABS(lon-?) DESC LIMIT 1' % (self.cache_table, " AND ".join(filterstring)) | ||
|
|
||
| filterargs.append(center.lat) | ||
| filterargs.append(center.lon) | ||
| c.execute(query, tuple(filterargs)) | ||
|
|
||
| points = [] | ||
| for row in c: | ||
| coord = self.ctype(row['lat'], row['lon']) | ||
| coord.unserialize(row) | ||
| return coord | ||
| return None | ||
|
|
||
| def set_filter(self, found = None, has_details = None, owner_search = '', name_search = '', size = None, terrain = None, diff = None, ctype = None, adapt_filter = False): | ||
| # a value "None" means: apply no filtering on this value | ||
|
|
||
| if adapt_filter: | ||
| filterstring = copy.copy(self.filterstring) | ||
| filterargs = copy.copy(self.filterargs) | ||
| else: | ||
| filterstring = [] | ||
| filterargs = [] | ||
|
|
||
| if found == True: | ||
| filterstring.append('(found = 1)') | ||
| elif found == False: | ||
| filterstring.append('(found = 0)') | ||
|
|
||
| if has_details == True: | ||
| filterstring.append("(desc != '' or shortdesc != '')") | ||
| elif has_details == False: | ||
| filterstring.append("NOT (desc != '' or shortdesc != '')") | ||
|
|
||
| if owner_search != None and len(owner_search) > 2: | ||
| filterstring.append("(owner LIKE '%%%s%%')" % owner_search) | ||
|
|
||
| if name_search != None and len(name_search) > 2: | ||
| filterstring.append("((name LIKE '%%%s%%') OR (title LIKE '%%%s%%'))" % (name_search, name_search)) | ||
|
|
||
| if size != None: | ||
| filterstring.append('(size IN (%s))' % (", ".join([str(b) for b in size]))) | ||
|
|
||
| if terrain != None: | ||
| filterstring.append('(terrain >= ?) AND (terrain <= ?)') | ||
| filterargs.append(terrain[0] * 10 ) | ||
| filterargs.append(terrain[1] * 10 ) | ||
|
|
||
| if diff != None: | ||
| filterstring.append('(difficulty >= ?) AND (difficulty <= ?)') | ||
| filterargs.append(diff[0] * 10 ) | ||
| filterargs.append(diff[1] * 10 ) | ||
|
|
||
| if ctype != None: | ||
| if len(ctype) > 0: | ||
| filterstring.append('(type IN (%s))' % (", ".join(['?' for b in ctype]))) | ||
| for b in ctype: | ||
| filterargs.append(b) | ||
|
|
||
| if len(filterstring) == 0: | ||
| filterstring.append('1') | ||
|
|
||
| self.filterstring = filterstring | ||
| self.filterargs = filterargs | ||
|
|
||
| def push_filter(self): | ||
| self.filterstack.append((self.filterstring, self.filterargs)) | ||
|
|
||
| def pop_filter(self): | ||
| self.filterstring, self.filterargs = self.filterstack.pop() | ||
|
|
||
| def get_points_filter(self, location = None): | ||
| filterstring = copy.copy(self.filterstring) | ||
| filterargs = copy.copy(self.filterargs) | ||
|
|
||
| if location != None: | ||
| c1, c2 = location | ||
| filterstring.append('((lat BETWEEN ? AND ?) AND (lon BETWEEN ? AND ?))') | ||
| filterargs.append(min(c1.lat, c2.lat)) | ||
| filterargs.append(max(c1.lat, c2.lat)) | ||
| filterargs.append(min(c1.lon, c2.lon)) | ||
| filterargs.append(max(c1.lon, c2.lon)) | ||
|
|
||
| c = self.conn.cursor() | ||
| query = 'SELECT * FROM %s WHERE %s' % (self.cache_table, " AND ".join(filterstring)) | ||
|
|
||
| c.execute(query, tuple(filterargs)) | ||
| points = [] | ||
| for row in c: | ||
| coord = self.ctype(row['lat'], row['lon']) | ||
| coord.unserialize(row) | ||
| points.append(coord) | ||
| c.close() | ||
| return points | ||
|
|
||
| def find_by_string(self, string): | ||
| query = 'SELECT * FROM %s WHERE name LIKE ? OR title LIKE ? LIMIT 2' % self.cache_table | ||
| c = self.conn.cursor() | ||
| c.execute(query, (string, string)) | ||
| row = c.fetchone() | ||
| coord = self.ctype(row['lat'], row['lon']) | ||
| coord.unserialize(row) | ||
|
|
||
| # we cannot reliably determine # of results, so using workaround here | ||
| if c.fetchone() != None: | ||
| return None | ||
| return coord | ||
|
|
| @@ -0,0 +1,15 @@ | ||
|
|
||
| [Desktop Entry] | ||
| Name=AdvancedCaching | ||
| Comment=Advanced Geocaching Tool For Linux | ||
| Encoding=UTF-8 | ||
| Version=0.0 | ||
| Type=Application | ||
| Exec=advancedcaching.py --simple | ||
| Icon=advancedcaching | ||
| Terminal=false | ||
| Categories=Office; | ||
| SingleInstance=true | ||
| StartupNotify=true | ||
| Comment[de_DE.UTF-8]=An Advanced Linux Geocaching tool | ||
| Name[de_DE]=advancedcaching.desktop |