source: subversion/applications/editors/pyosmeditor/pyosmeditor.py @ 34614

Last change on this file since 34614 was 1846, checked in by joerg, 13 years ago

set executable property

  • Property svn:executable set to *
File size: 159.9 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2006  Michael Strecke
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License
8# as published by the Free Software Foundation; either version 2
9# of the License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19#
20
21import sys, os, re, stat
22import xml.dom.minidom
23import math, urllib2, httplib
24import pango
25import gzip, StringIO
26import time, datetime
27
28try:
29   import EXIF
30   exiflib = True
31except ImportError:
32   exiflib = False
33
34debug = False
35pyosmeditorversion = "0.1.2"
36DB_ENCODING = "utf-8"
37
38CONFIGFILENAME = "~/.pyosmeditor/pysomeditor.xml"
39
40try:
41   import pygtk
42   import gobject
43   pygtk.require("2.0")
44except:
45   pass
46try:
47   import gtk
48   import gtk.glade
49except:
50   sys.exit(1)
51
52##############################
53# Import translations for .glade and .py
54import locale
55import gettext
56APP = 'pyosmeditor'
57DIR = 'locale'
58
59gtk.glade.bindtextdomain(APP,DIR) 
60gtk.glade.textdomain(APP)
61gettext.bindtextdomain(APP,DIR) 
62gettext.textdomain(APP)
63_ = gettext.gettext
64
65localeencoding = locale.getpreferredencoding()
66
67######################
68# os helper functions
69def createsubdirforfile(fnm):
70   pa = os.path.split(fnm)[0]           # dir part
71   if pa:
72      if not os.path.exists(pa):
73         os.makedirs(pa)
74
75def add_extension(fnm,ext):
76   """ add extension to filename, if it has no exention
77   """
78   ro, ex = os.path.splitext(fnm)
79   if ex == "":                   # no extension
80     ex = ext
81     
82   return ro + ex
83   
84def filesize(fnm):
85  return os.stat(fnm)[stat.ST_SIZE]
86
87############
88#  Exif helper
89def exifdate(fnm):
90   if exiflib:
91      f=open(fnm, 'rb')
92      tags=EXIF.process_file(f)
93      try:
94         return str(tags['Image DateTime'])
95      except KeyError:
96         return None   
97   else:
98      return None
99
100
101######################
102# gtk helper
103def process_pending_gtk_events():
104   while gtk.events_pending(): gtk.main_iteration()
105
106def enable_widgets(options,enable = True):
107   for option in options:
108      option.set_sensitive(enable)
109
110def simple_dialog(type,message,buttons, modal = True):
111   # types: gtk.MESSAGE_INFO, gtk.MESSAGE_WARNING, gtk.MESSAGE_QUESTION, gtk.MESSAGE_ERROR
112   # buttons: gtk.BUTTONS_NONE, gtk.BUTTONS_OK, gtk.BUTTONS_CLOSE, gtk.BUTTONS_CANCEL,
113   #          gtk.BUTTONS_YES_NO, gtk.BUTTONS_OK_CANCEL
114   flags = 0
115   if modal:
116      flags |= gtk.DIALOG_MODAL
117   if buttons == None:
118      buttons = gtk.BUTTONS_NONE
119
120   dia = gtk.MessageDialog(None, flags = flags, type=type, buttons=buttons, message_format=None)
121   dia.set_markup(message)
122   if modal:
123      response = dia.run()
124      dia.destroy()
125      return response == gtk.RESPONSE_OK
126   else:
127      dia.show_now()
128      process_pending_gtk_events()
129      return dia
130 
131######################
132# XML helper functions
133
134def appendNodeAndText(doc,parent,element,content):
135   """ append an element node with corresponding text node to parent
136   
137       doc:     document
138       parent:  parent node to which the child will be appended
139       element: name of the text node
140       content: content of the text node (None -> empty node)
141   """
142   s = doc.createElement(element)
143   if content != None:
144      t = doc.createTextNode(str(content))
145      s.appendChild(t)
146   parent.appendChild(s)
147
148def getChildValue(node,childname):
149   """ get value of child of node with name childname
150   
151       return value
152         None: if node exists but no text node
153       raises ValueError if child does not exist
154   """ 
155   for ele in node.childNodes:
156      if ele.nodeType == xml.dom.minidom.Node.ELEMENT_NODE:
157         if childname == ele.localName:
158            value = None
159            for sub in ele.childNodes:
160               if sub.nodeType == xml.dom.minidom.Node.TEXT_NODE:
161                  return sub.nodeValue
162            return value
163   raise ValueError, "no such child"
164
165def setChildValue(doc,node,childname,value):
166   found = False
167   for ele in node.childNodes:
168      if ele.nodeType == xml.dom.minidom.Node.ELEMENT_NODE:
169         if childname == ele.localName:
170            # search child of element node for text nodes
171            for sub in ele.childNodes:
172               if sub.nodeType == xml.dom.minidom.Node.TEXT_NODE:
173                  found = True
174                  if value != None:
175                     # set new value, if not None
176                     sub.nodeValue = str(value)
177                  else:
178                     # remove text node, if new value *is* None
179                     ele.removeChild(sub)
180                  return
181           
182            # element node has no text child nodes
183            if not found:
184               # add one, if value is not None
185               if value != None:
186                  sub = doc.createTextNode(str(value))
187                  ele.appendChild(sub)
188            return
189   
190   # No element node with that name found
191   if not found:           
192      appendNodeAndText(doc,node,childname,value)
193
194###############
195
196def decode_time(s):
197   # 2006-07-07T10:20:56Z
198   erg = re.match("^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$",s)
199   if erg:
200      return datetime.datetime(int(erg.group(1)),int(erg.group(2)),int(erg.group(3)),int(erg.group(4)),int(erg.group(5)),int(erg.group(6)))   
201
202   # 2006:07:07 17:06:38 or 2006.07.07 17:06:38 or 2006-07-07 17:06:38
203   # JJJJ:MM:DD HH:MM:SS
204   erg = re.match("^(\d{4})[:|.|-](\d{2})[:|.|-](\d{2}) (\d{2}):(\d{2}):(\d{2})$",s)
205   if erg:
206      return datetime.datetime(int(erg.group(1)),int(erg.group(2)),int(erg.group(3)),int(erg.group(4)),int(erg.group(5)),int(erg.group(6)))   
207
208   raise ValueError,"Unknow date format: "+s
209
210def distance(longS, latS, longD, latD):
211   # http://obivan.uni-trier.de/p/h/vb/third_b_va.html
212   radius = 6370000.0
213   if longS == longD and latS == latD: return 0
214   dl = math.radians(abs(longS - longD))
215   latS = math.radians(latS)
216   latD = math.radians(latD)   
217   cos_d = math.sin(latS) * math.sin(latD) + math.cos(latS) * math.cos(latD) * math.cos(dl)
218   dist = math.acos(cos_d) * radius
219   
220   return dist
221   
222def timestampit(node):
223   """ set timestmp attribute on node
224       current time (in GMT, I presume)
225   """
226   node.setAttribute("timestamp",time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime()))
227
228######################
229# Simple config helper
230class simpleconfigfile:
231
232   def __init__(self,filename,rootnodename = None,version = None,elements=None,defaults=None):
233      """ filename:      name of the XML file with the config data
234          rootnodename:  name of the root node in the config file
235          version:       if set, version attribute in root node must have the same value
236          elements:      dictionary, key determines which node is part of "data", value has a translation function
237          defaults:      dictionary with default values (key, value -> <key>value</key>
238      """
239      self.doc = None              # pointer to xml doc in memory
240      self.root = None             # pointer to root element
241      self.data = {}               # selected config data goes here
242      self.elements = elements     # list of nodes we want in self.data
243      self.filename = filename     # we need that when we write the tree to disk
244      filename = os.path.expanduser(filename)
245      try:
246         # try to parse file
247         self.doc = xml.dom.minidom.parse(filename) 
248      except:
249         # Create default tree
250         self.doc = xml.dom.minidom.Document()    # empty tree
251         self.root = self.doc.createElement(rootnodename)
252         self.doc.appendChild(self.root)
253         if version != None:
254            self.root.setAttribute("version",str(version))
255   
256      # Now scan the (newly created or read) tree
257      # throw exeception, if root element of XML files is not the one we expect
258     
259      self.root = self.doc.getElementsByTagName(rootnodename)
260      assert self.root != None
261      self.root = self.root[0]    # As this is the root element, only one can be available
262
263      # check data version
264      if version != None:
265         v = self.root.getAttribute("version")
266         if (v != None) and (v != str(version)):
267            raise ValueError,"data version of config file is not compatible"
268
269      # populate sub-tree with defaults, if nodes are not already present
270      if defaults:
271         self.dict2tree(defaults, overwrite = False)
272      # fill local dictionary with (merged) data from the tree
273      self.data = self.tree2dict(elements)
274
275      if debug:
276         print self.data
277       
278   def dict2tree(self,di, overwrite):
279      if not di:
280         return
281         
282      for key in di:
283         nodelist = self.root.getElementsByTagName(key)
284         nodecnt = len(nodelist)
285         if nodecnt == 0:
286             setChildValue(self.doc,self.root,key,di[key])
287         elif nodecnt == 1:
288             if overwrite:
289                setChildValue(self.doc,self.root,key,di[key])
290         else:
291             raise ValueError,"unique key found more than once"
292             
293   def tree2dict(self,nodes):
294      if not nodes:
295         return {}
296       
297      di = {}
298      for nodename in nodes:
299         nodelist = self.root.getElementsByTagName(nodename)
300         nodecnt = len(nodelist)
301         if nodecnt == 1:
302             val = getChildValue(self.root,nodename)  # remember, values are always strings!
303             if val != None:
304                if nodes[nodename] != None:           # use supplied conversion function
305                   di[nodename] = nodes[nodename](val)
306                else:
307                   di[nodename] = val
308             else:                                    # None remains None, regardless of the conversion function
309                di[nodename] = None
310               
311         elif nodecnt == 0:
312             pass
313         else:
314             raise ValueError,"unique key found more than once"
315      return di
316             
317   def writedata(self,filename = None):
318      """ Write config file
319      """
320      assert self.doc != None
321      out = filename
322      if out == None:
323         out = self.filename
324      assert out != None
325     
326      out = os.path.expanduser(out)
327
328      createsubdirforfile(out)
329     
330      # merge local dictionary into XML tree
331      self.dict2tree(self.data, overwrite = True)
332      if debug:
333         print self.doc.toxml()
334      fl = open(out,"w")
335      fl.write(self.doc.toxml())     
336      fl.close()
337     
338#############
339def normalizebox_deg(lon1,lat1,lon2,lat2):
340   if lon1 < lon2:
341      alon = lon1
342      blon = lon2
343   else:
344      alon = lon2
345      blon = lon1
346   
347   if lat1 < lat2:
348      alat = lat1
349      blat = lat2
350   else:
351      alat = lat2
352      blat = lat1
353   
354   flag = False
355   if blon-alon > 200:
356     a = alon
357     alon = blon
358     blon = a
359     blon += 360
360     flag = True
361   
362   return (alon,alat,blon,blat,flag)
363
364def normalizebox_xy(x1,y1,x2,y2):
365   if x2<x1:
366     x1,x2 = x2,x1
367   if y2<y1:
368     y1,y2 = y2,y1
369   return (x1,y1,x2,y2)
370
371class box_xy:
372   def __init__(self,start = None):
373      if start == None: 
374         self.clear()
375      else:
376         if type(start) == tuple:
377            self.lx, self.ly, self.tx, self.ty = start
378         else:
379            self.lx = start.lx
380            self.ly = start.ly
381            self.tx = start.tx
382            self.ty = start.ty
383
384   def clear(self):
385      self.lx = None   # Nothing set
386      self.ly = None
387      self.tx = None
388      self.ty = None
389   
390   def empty(self):
391      return self.lx == None
392     
393   def put_point(self,x,y):
394      x = int(x)
395      y = int(y)
396      if self.empty(): # Nothing set
397         self.lx = x
398         self.ly = y
399         self.tx = x
400         self.ty = y
401      else:
402         self.lx = min(self.lx,x)
403         self.ly = min(self.ly,y)
404         self.tx = max(self.tx,x)
405         self.ty = max(self.ty,y)
406   
407   def putbox(self,x1,y1,x2,y2):
408      self.put_point(x1,y1)
409      self.put_point(x2,y2)
410   
411   def isinbox(self,x,y):
412      if self.empty():
413         return False
414      return (self.lx <= x) and (x <= self.tx) and (self.ly <= y) and (y <= self.ty)
415   
416   def getbox(self):
417      if self.lx == None:
418         return None
419      return (self.lx, self.ly, self.tx, self.ty)
420
421   def enlarge(self,fact):
422     dx = int((self.tx-self.lx) * (fact-1) / 2)
423     dy = int((self.ty-self.ly) * (fact-1) / 2)
424     self.lx -= dx
425     self.ly -= dy
426     self.tx += dx
427     self.ty += dy
428
429   def grow(self,dx1,dy1,dx2,dy2):
430     self.lx -= dx1
431     self.ly -= dy1
432     self.tx += dx2
433     self.ty += dy2
434       
435class box_deg:
436   # warp = True ->  negative longitude has been normalized to 180 .. 360
437   # internally the "normalized" values are used         
438
439   def __init__(self, start = None):
440      self.minlon = None
441      self.maxlon = None
442      self.minlat = None
443      self.maxlat = None
444      self.warp = False
445
446      if start != None:
447         self.put_point(start[0],start[1])
448         self.put_point(start[2],start[3])     
449     
450   def put_point(self,lon,lat):
451      if self.minlon == None:
452         self.minlon = lon
453         self.maxlon = lon
454         self.minlat = lat
455         self.maxlat = lat
456      else:
457         if not self.warp:
458            if lon - self.minlon > 200:
459               self.warp = True
460               if self.maxlon < 0:
461                  self.maxlon += 360
462               if self.minlon < 0:
463                  self.maxlon += 360
464         if self.warp:
465            if lon<0:
466               lon += 360
467         self.minlon = min(self.minlon,lon)
468         self.maxlon = max(self.maxlon,lon)
469         self.minlat = min(self.minlat,lat)
470         self.maxlat = max(self.maxlat,lat)
471         
472   def enlarge(self,faktor):
473      flon = (self.maxlon - self.minlon) * (faktor - 1.0)
474      flat = (self.maxlat - self.minlat) * (faktor - 1.0)
475      self.minlon -= flon
476      self.minlat -= flat
477      self.maxlon += flon
478      self.maxlat += flat
479     
480   def getbox(self):
481      mx = self.maxlon
482      if self.warp:
483         mx -= 360
484         
485      return [self.minlon,self.minlat,mx,self.maxlat]
486
487   def isinbox(self,lon,lat):
488      if self.warp and lon < 0 : lon += 360
489      return (self.minlon <= lon) and (lon <= self.maxlon) and \
490             (self.minlat <= lat) and (lat <= self.maxlat)
491 
492
493####### selection frame
494# Note: The selection frame is not an "infoelement" (see below)
495#       It is drawn DIRECTLY on the screen and NOT buffered in a pixmap
496class selectionframe:
497   def __init__(self,drawarea):
498      self.drawarea = drawarea
499      self.window = drawarea.widget.window
500      self.startx = None
501      self.endx = None
502
503   def framestart(self,x,y):
504      self.startx = int(x)
505      self.starty = int(y)
506      self.endx = None
507     
508   def drawit(self):
509      x1,y1,x2,y2 = normalizebox_xy(self.startx,self.starty,self.endx,self.endy)
510      self.window.draw_rectangle(self.drawarea.gc_selframe, False, x1,y1,x2-x1+1, y2-y1+1)
511     
512   def framemove(self,x,y):
513      if self.startx == None:
514         return
515
516      if self.endx != None:
517         self.drawit()
518         
519      self.endx = x
520      self.endy = y
521
522      self.drawit()     
523
524   def frameend(self):
525      if self.endx == None:
526         return None
527
528      self.drawit()         
529     
530      res = normalizebox_xy(self.startx, self.starty, self.endx, self.endy)
531     
532      self.startx = None
533      self.endx = None
534      return res     
535
536class unreliable_trackpoint:
537   """ This class tries to guess, if a point of a track makes sense
538       Current algorithm: check if the accelearation is below a threshold
539   """
540   def __init__(self,threshold):
541      self.warnthreshold = threshold
542      self.reset()
543     
544   def reset(self):
545      """ call at the begin of every new segment
546      """
547      self.insegmentcount = 0
548   
549   def put_point(self,lon,lat,time):
550      if time == None:        # no time (e.g. OSM tracks) / no check
551         self.reset()
552         return False
553         
554      unreliable = False
555      self.insegmentcount += 1
556      v = 0
557      if self.insegmentcount > 1:
558         dt = (time-self.ltime).seconds
559         if dt != 0:
560            ds = distance(self.llon,self.llat,lon,lat)
561            v = ds / dt
562      if self.insegmentcount > 2:
563         v2 = 0
564         if dt != 0:
565            v2 = (v - self.lv) / dt
566            if abs(v2)>self.warnthreshold:
567               unreliable = True
568      self.llon = lon
569      self.llat = lat
570      self.ltime = time
571      self.lv = v
572      return unreliable
573
574#################### CLASS INFOELEMENT #####################
575# Abstract class, that shows what a child has to implement
576class infoelement:
577   def __init__(self,drawarea):
578      self.drawarea = drawarea
579      self.data = None
580      self.visible = True
581      self.recalcwaiting = False
582   
583   def readdata(self,fil):
584      pass
585
586   def recalcdata(self):
587      if not self.must_i_recalc(): return                   # do this in child classes as well
588     
589   def draw(self, box = None, refresh = True):
590      if not self.drawarea.screencoordsset: return          # do this in child classes as well
591      pass
592     
593   def isvisible(self):
594      return (self.data != None) and self.visible
595     
596   def set_visible(self,visible):
597      needsrecalc = not self.visible and visible and self.recalcwaiting     
598      self.visible = visible
599      if needsrecalc: 
600         self.recalcdata()
601
602   def must_i_recalc(self):
603      if not self.drawarea.screencoordsset: 
604         return False
605      if not self.visible: 
606         self.recalcwaiting = True
607         return False
608      self.recalcwaiting = False
609      return True
610
611     
612####### local gpx track
613class localgpxtrack(infoelement):
614   def __init__(self,drawarea, threshold):
615      infoelement.__init__(self,drawarea)
616      self.trackpointindex = []
617      self.waypointindex = []
618      self.warnthreshold = threshold
619
620   def isvisible(self):
621      return self.trackpointindex != [] and self.visible
622
623   def readdata(self,fil):
624      self.data = xml.dom.minidom.parse(fil)
625      self.root = self.data.getElementsByTagName("gpx")[0]
626      assert self.root != None
627
628       # create indices
629         
630      self.waypointindex = []
631      self.waypointcount = 0
632      nodelist = self.root.getElementsByTagName("wpt")
633      for node in nodelist:
634         self.waypointcount += 1
635         lon = float(node.getAttribute("lon"))
636         lat = float(node.getAttribute("lat"))
637         try:
638            name = getChildValue(node,"name")
639         except ValueError:
640            name = None
641         self.waypointindex.append([lon,lat,node,None,None,name])
642     
643      self.trackpointindex = []
644      self.trackpointcount = 0
645     
646      unreliablecount = 0
647      unreliablecheck = unreliable_trackpoint(self.warnthreshold)
648     
649      tracklist = self.root.getElementsByTagName("trk")
650      for track in tracklist:
651         tracksegmentlist = track.getElementsByTagName("trkseg")
652         for tracksegment in tracksegmentlist:
653            unreliablecheck.reset()
654            first = True
655            trackpointlist = tracksegment.getElementsByTagName("trkpt")
656            for trackpoint in trackpointlist:
657               self.trackpointcount += 1
658               lon = float(trackpoint.getAttribute("lon"))
659               lat = float(trackpoint.getAttribute("lat"))
660               try:
661                  time = decode_time(getChildValue(trackpoint,"time"))
662               except ValueError:
663                  time = None
664                 
665               unreliable = unreliablecheck.put_point(lon,lat,time)
666               if unreliable:
667                  unreliablecount += 1
668               # 0 = lon, 1 = lat, 2 = Node, 3 = x, 4 = y, 5 = first in segment, 6 = selected, 7 = unreliable
669               self.trackpointindex.append([lon,lat,trackpoint,None,None,first, False, unreliable])
670               first = False
671               
672      return _("way points: %s\ntrack points: %s\nunreliable points: %s") % (self.waypointcount, self.trackpointcount, unreliablecount)
673   
674   def add_helper_track(self,fnm,box):
675      # A helper track is a gpx track which is added to the normal (first) one.
676      # They are clipped to the "box" area.
677      #
678      # Helper tracks are stored in the index only, not as XML
679      #
680      fdata = xml.dom.minidom.parse(fnm)
681      root = fdata.getElementsByTagName("gpx")[0]
682      assert root != None
683     
684      unreliablecheck = unreliable_trackpoint(self.warnthreshold)
685      newpoints = 0
686      tracklist = root.getElementsByTagName("trk")
687      for track in tracklist:
688         tracksegmentlist = track.getElementsByTagName("trkseg")
689         for tracksegment in tracksegmentlist:
690            unreliablecheck.reset()
691            first = True
692            trackpointlist = tracksegment.getElementsByTagName("trkpt")
693            for trackpoint in trackpointlist:
694               lon = float(trackpoint.getAttribute("lon"))
695               lat = float(trackpoint.getAttribute("lat"))
696               time = decode_time(getChildValue(trackpoint,"time"))
697               
698               unreliable = unreliablecheck.put_point(lon,lat,time)
699               if box.isinbox(lon,lat):
700                  # 0 = lon, 1 = lat, 2 = Node, 3 = x, 4 = y, 5 = first in segment, 6 = selected, 7 = unreliable
701                  self.trackpointindex.append([lon,lat,None,None,None,first, False, unreliable])
702                  first = False
703                  newpoints += 1
704               else: # skip point, next one will be a "first" one again
705                  first = True
706      return newpoints
707     
708   def minmax(self):
709      b = box_deg()
710      for poi in self.trackpointindex:
711         b.put_point(poi[0],poi[1])
712      return b.getbox()
713     
714   def recalcdata(self):
715      if not self.must_i_recalc(): return
716
717      for n in self.trackpointindex:
718         n[3], n[4] = self.drawarea.transform2xy(n[0], n[1])
719      for n in self.waypointindex:
720         n[3], n[4] = self.drawarea.transform2xy(n[0], n[1])
721
722   def draw(self, box = None, refresh = True):
723      if not self.drawarea.screencoordsset: return
724
725      if self.trackpointindex == []:
726         return
727     
728      if box == None:
729         b = box_xy((0,0,self.drawarea.da_width,self.drawarea.da_height))
730         b.enlarge(2.0)
731      else:
732         b = box_xy(box)
733     
734      first = True   
735      for n in self.trackpointindex:
736         if b.isinbox(n[3],n[4]):
737            if n[6]:                         # selected
738               color = self.drawarea.green
739            else:
740               if n[7]:                      # unreliable
741                  color = self.drawarea.blue
742               else:
743                  color = self.drawarea.red
744            self.drawarea.drawcircle(n[3],n[4],4,color,False)
745           
746            if not n[5] and not first:     # first in line
747               if ls or n[6]:              # line would go away if EITHER point is selected
748                  color = self.drawarea.green
749               else:
750                  if ld or n[7]:           # unreliable
751                     color = self.drawarea.blue
752                  else:
753                     color = self.drawarea.red
754#               print lx,ly,n[3],n[4]
755               self.drawarea.drawline(lx,ly,n[3],n[4],color,1,arrowtype = 2)
756            lx = n[3]
757            ly = n[4]
758            ls = n[6]
759            ld = n[7]
760            first = False
761         else:
762            first = True
763
764      for n in self.waypointindex:
765         if b.isinbox(n[3],n[4]):
766            self.drawarea.drawwaypoint(n,color = self.drawarea.red,refresh = False)
767     
768      if refresh: 
769         self.drawarea.refresh()
770
771   def select_nodes_xy(self,box,union = True):
772      b = box_xy(box)
773      for n in self.trackpointindex:
774         if n[2]:                         # only real track, not helper track
775            if union:
776               if b.isinbox(n[3],n[4]):
777                  n[6] = True
778            else:
779               n[6] = b.isinbox(n[3],n[4])
780         
781   def unselect_all_nodes(self):
782      for n in self.trackpointindex:
783         n[6] = False
784   
785   def remove_selected_nodes(self):
786      # delete selected nodes from trackpointindex
787      #
788      # Note: The entire track is still in memory in from of the XML tree
789      newtrack = []
790      setfirst = False
791      for n in self.trackpointindex: 
792         if n[6]:
793            setfirst = True                   # this node is gone, mark next point as first in line
794         else:
795            if setfirst: n[5] = True
796            newtrack.append(n)
797            setfirst = False
798      self.trackpointindex = newtrack   
799
800   def remove_unselected_nodes(self):
801      # delete unselected nodes from trackpointindex
802      #
803      # Note: The entire track is still in memory in from of the XML tree
804      newtrack = []
805      setfirst = False
806      for n in self.trackpointindex: 
807         if n[2]:                             # only "real" nodes
808            if not n[6]:
809               setfirst = True                # this node is gone, mark next point as first in line
810            else:
811               if setfirst: n[5] = True
812               newtrack.append(n)
813               setfirst = False
814         else:
815            setfirst = True                  # next real node is first, if there is a next real node
816      self.trackpointindex = newtrack   
817
818   def get_modified_gpx_track(self):
819      # creates an XML representation of the modified gpx track
820      # (modification (deletions) are stored in index only)
821      # Note: helper tracks are stored with Node = None. They are not saved.
822      data = """<?xml version="1.0" ?>
823<gpx creator="pypsmeditor" version="%s" xmlns="http://www.topografix.com/GPX/1/0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
824""" % (pyosmeditorversion,)
825     
826      segmentopen = False
827      for n in self.trackpointindex:
828         if n[2]:     # node not None
829            if n[5]:     # first in line
830               if segmentopen:
831                  data += "</trkseg></trk>\n"
832               data += "<trk><trkseg>\n"
833               segmentopen = True
834            data += n[2].toxml()
835           
836      if segmentopen:
837         data += "</trkseg></trk>\n"
838      data += "</gpx>\n"
839      return data
840   
841### remote gpx track
842
843class remotegpxtrack(localgpxtrack):
844
845   def draw(self, box = None, refresh = True):
846      if not self.drawarea.screencoordsset: return
847     
848      if self.data == None:
849         returnw
850     
851      if box == None:
852         b = box_xy((0,0,self.drawarea.da_width,self.drawarea.da_height))
853         b.enlarge(2.0)
854      else:
855         b = box_xy(box)
856     
857      for n in self.trackpointindex:
858         if b.isinbox(n[3],n[4]):
859            self.drawarea.drawcircle(n[3],n[4],4,self.drawarea.green,False)
860     
861      if refresh: 
862         self.drawarea.refresh()
863
864### selected items
865
866class drawselected(infoelement):
867   def __init__(self,drawarea,selectedhandler):
868      infoelement.__init__(self,drawarea)
869      self.drawarea = drawarea
870      self.data = selectedhandler
871         
872   def draw(self, box = None, refresh = True):
873      if not self.drawarea.screencoordsset: return          # do this in child classes as well
874      self.data.draw_selected(box,refresh = False)
875      if refresh:
876         self.drawarea.refresh()
877     
878
879### nodes, segments, ways
880
881class osmdata(infoelement):
882   # Desription of node/segment/way indices
883   #
884   # nodeindex:
885   # 0 = longitude
886   # 1 = latidtude
887   # 2 = pointer to XML node
888   # 3 = x    x/y will be changed when moving a node and must be recalculated each time
889   # 4 = y    the display is changed (zoom/move)
890   #
891   # segmentindex:
892   # 0 = id of "from" node
893   # 1 = id of "to" node
894   # 2 = pointer to XML node
895   #
896   # wayindex:
897   # 0 = pointer to XML node
898   # 1 = list of segments
899
900   def __init__(self,drawarea):
901      infoelement.__init__(self,drawarea)
902      self.waysvisible = True
903      self.nextownid = -1
904     
905   def get_next_own_id(self):
906      self.nextownid -= 1
907      return self.nextownid + 1
908
909   def create_segment_index(self,root):
910      segmentindex = {}
911      segcount = 0
912      segerror = 0
913      nodelist = root.getElementsByTagName("segment")
914      for node in nodelist:
915         id = int(node.getAttribute("id"))
916         segcount += 1
917         fromnode = int(node.getAttribute("from"))
918         tonode = int(node.getAttribute("to"))
919         segmentindex[id] = [fromnode, tonode, node]
920         if tonode == fromnode:
921            segerror += 1     
922     
923      return segmentindex, segcount, segerror
924
925   def create_way_or_area_index(self,name,root):
926      """ name: "way" or "area"
927          root: root of the XML tree
928         
929          result: dictionary: key: id of way/area
930                              value: [ pointer to node, array of segment ids ]
931      """
932      wayindex = {}
933      waycount = 0
934      nodelist = root.getElementsByTagName(name)
935      for node in nodelist:
936         id = int(node.getAttribute("id"))
937         wayindex[id] = [node]
938         waysegs = []
939         waycount += 1
940         seglist = node.getElementsByTagName("seg")
941         for seg in seglist:
942            sid = int(seg.getAttribute("id"))
943            waysegs.append(sid)
944         wayindex[id].append(waysegs)
945
946      return wayindex, waycount
947
948   def switch_segment_orientation(self,id):
949      f = self.segmentindex[id][0]
950      t = self.segmentindex[id][1]
951      n = self.get_segment_pointer(id)
952      n.setAttribute("from",str(t))
953      n.setAttribute("to",str(f))
954      self.segmentindex[id][0] = t
955      self.segmentindex[id][1] = f
956   
957   def get_segment_from_list_with_node(self, searchnode, seglist, maxallowed = 1, remove = True):
958      # seaches the segments in seglist for one with the "searchnode" as from or to node
959      # removes this node from the list if remove == True
960      # fails, if it found more than maxallowed segemtns that meet this criteria
961     
962      if searchnode == None: return None
963     
964      hits = []
965      otherid = None
966      for seg in seglist:
967         try:
968            fr = self.segmentindex[seg][0]
969            to = self.segmentindex[seg][1]
970           
971            if fr == searchnode:
972               otherid = to
973               hits.append(seg)
974            if to == searchnode:
975               otherid = fr
976               hits.append(seg)
977           
978         except KeyError:    # segment not visible
979            pass
980
981      if len(hits) == 0:
982         return None
983      if len(hits) > maxallowed: 
984         return None
985      if len(hits) > 1:
986         otherid = None
987      if remove:
988         for s in hits:
989            seglist.remove(s)
990      return (hits, otherid, seglist)     
991   
992   def is_regular_endpoint(self,id,list):
993      # an endpoint may only appear once in all segments
994      if list == []: return False
995     
996      # check how often node "id" appears in all segments
997      cnt = 0
998      for seg in list:
999         try:
1000            s = self.segmentindex[seg]
1001         except KeyError:    # segment not visible
1002            s = None
1003         if s:
1004            if s[0] == id: cnt += 1
1005            if s[1] == id: cnt += 1
1006         if cnt>1: return False
1007      return True         
1008   
1009   
1010   def add_segments_to_way(self,id,idlist):
1011      # This subroutine adds new segments to an exiting way.
1012      # It will try to add the segments in a way that is consistent
1013      # with the existing segments of the way.
1014      # It first checks, if the new segments have to be added to the beginning or the
1015      # end of the existing way, and will then add the new segments according to their
1016      # from/to nodes.
1017      # If it fails at any point, the segments will simply appended.
1018     
1019      action = 0                         # 0 = append, 1 = append with check, 2 = prepend with check
1020      wayseglist = self.wayindex[id][1]  # the segments of the current way
1021      firstid = None                     # ID of the common node (!) between new and old segments
1022
1023      # check the first node of the way
1024      ok = False
1025      found = False
1026      try:
1027         fr = self.segmentindex[wayseglist[0]][0]
1028         if not self.is_regular_endpoint(fr,wayseglist): fr = None
1029         to = self.segmentindex[wayseglist[0]][1]
1030         if not self.is_regular_endpoint(to,wayseglist): to = None
1031         ok = True
1032      except KeyError:               # This segment is not visible -> user could not have appended to it
1033         pass
1034           
1035      if ok:        # info for segment was found, i.e we can test it
1036         if self.get_segment_from_list_with_node(fr,idlist, maxallowed = 1, remove = False) != None:
1037            action = 2    # one of the new segments has the from-node of the first segment
1038            firstid = fr
1039            found = True
1040         elif self.get_segment_from_list_with_node(to,idlist, maxallowed = 1, remove = False) != None:
1041            action = 2    # one of the new segments has the to-node of the first segment
1042            firstid = to
1043            found = True
1044               
1045      if not found:
1046         # nothing found in the first segment of the way, let's test the last segment
1047         ok = False
1048         found = False
1049         try:
1050            fr = self.segmentindex[wayseglist[-1]][0]    # last in list
1051            if not self.is_regular_endpoint(fr,wayseglist): fr = None
1052            to = self.segmentindex[wayseglist[-1]][1]
1053            if not self.is_regular_endpoint(to,wayseglist): to = None
1054            ok = True
1055         except KeyError:           # This segment is not visible
1056            pass
1057
1058         if ok: 
1059            if self.get_segment_from_list_with_node(fr,idlist, maxallowed = 1, remove = False) != None:
1060               firstid = fr
1061               action = 1    # one of the new segments has the from-node of the last segment
1062               found = True
1063            elif self.get_segment_from_list_with_node(to,idlist, maxallowed = 1, remove = False) != None:
1064               firstid = to
1065               action = 1    # one of the new segments has the to-node of the last segment
1066               found = True
1067     
1068      n = self.get_way_pointer(id)                       # pointer to the way
1069         
1070      if firstid != None:    # we found common ground, let's try to chain the segments
1071         first_seg_node = n.getElementsByTagName("seg")[0]   # pointer to first segment in way
1072
1073         while idlist != []:
1074            res = self.get_segment_from_list_with_node(firstid,idlist, maxallowed = 1, remove = True)
1075            # res == None: id not found
1076            # else: res[0]: tuple with IDs of the segments with the requested node id
1077            #       res[1]: ID of the second node of the returned segment (if maxallowed == 1)
1078            #       res[2]: segment list without the segment res[0]
1079            if res == None: break
1080           
1081            idlist = res[2]
1082            if action == 1:
1083               w = self.data.createElement("seg")
1084               w.setAttribute("id",str(res[0][0]))
1085               n.appendChild(w)
1086            if action == 2:
1087               w = self.data.createElement("seg")
1088               w.setAttribute("id",str(res[0][0]))
1089               n.insertBefore(w,first_seg_node)
1090               first_seg_node = w
1091            firstid = res[1]
1092               
1093      # add anything that remains at the end without any tests     
1094      for sid in idlist:
1095         w = self.data.createElement("seg")
1096         w.setAttribute("id",str(sid))
1097         n.appendChild(w)
1098     
1099      # after manipulating the XML tree, update the index (including seg list index)
1100      self.update_index(n)
1101     
1102   ###################################
1103
1104   def readdata(self,fil):
1105      """ fil: filename or open file descriptor to XML file
1106      """
1107     
1108      self.data = xml.dom.minidom.parse(fil)
1109      self.root = self.data.getElementsByTagName("osm")[0]
1110      assert self.root != None
1111     
1112      # create indices
1113     
1114      self.nodeindex = {}
1115      self.nodecount = 0
1116      nodelist = self.root.getElementsByTagName("node")
1117      for node in nodelist:
1118         self.nodecount += 1
1119         id = int(node.getAttribute("id"))
1120         lon = float(node.getAttribute("lon"))
1121         lat = float(node.getAttribute("lat"))
1122         self.nodeindex[id] = [lon,lat,node,None,None]           
1123
1124      self.segmentindex, self.segcount, self.segerror = self.create_segment_index(self.root)
1125      self.wayindex, self.waycount = self.create_way_or_area_index("way",self.root)
1126         
1127      return _("Nodes: %s\nSegments: %s\nErrors: %s\nWays: %s") % (self.nodecount,self.segcount,self.segerror, self.waycount)
1128
1129   def recalcdata(self):
1130      if not self.must_i_recalc(): return
1131     
1132      if self.data != None:
1133         for n in self.nodeindex:
1134            self.nodeindex[n][3], self.nodeindex[n][4] = self.drawarea.transform2xy(self.nodeindex[n][0], self.nodeindex[n][1])
1135
1136   def draw(self, box = None, refresh = True):
1137      if not self.drawarea.screencoordsset: return
1138      if self.data == None:
1139         return
1140     
1141      if box == None:
1142         b = box_xy((0,0,self.drawarea.da_width,self.drawarea.da_height))
1143         b.enlarge(2.0)
1144      else:
1145         b = box_xy(box)
1146   
1147      for node in self.nodeindex:
1148         if b.isinbox(self.nodeindex[node][3],self.nodeindex[node][4]):
1149            self.drawarea.drawnode(node)
1150
1151      for node in self.segmentindex:
1152         fromnode = self.segmentindex[node][0]
1153         tonode = self.segmentindex[node][1]
1154                 
1155         if b.isinbox(self.nodeindex[fromnode][3],self.nodeindex[fromnode][4]):
1156            if b.isinbox(self.nodeindex[tonode][3],self.nodeindex[tonode][4]):
1157               self.drawarea.drawsegment(node, refresh = False, arrowtype = 1)
1158
1159      if self.waysvisible:
1160         for node in self.wayindex:
1161            segmentlist = self.wayindex[node][1]
1162            for segment in segmentlist:
1163               try:
1164                  fromnode = self.segmentindex[segment][0]
1165                  tonode = self.segmentindex[segment][1]
1166               except KeyError:
1167#                  print "Unknown way (%s) segment: %s" % (node, segment)
1168                  continue
1169                 
1170               if b.isinbox(self.nodeindex[fromnode][3],self.nodeindex[fromnode][4]) or \
1171                  b.isinbox(self.nodeindex[tonode][3],self.nodeindex[tonode][4]):
1172                  self.drawarea.drawsegment(segment,color = self.drawarea.brown, linewidth = 4, refresh = False, arrowtype = 0)
1173      if refresh:
1174         self.drawarea.refresh()
1175         
1176   def get_node_xy(self,id):
1177      return (self.nodeindex[id][3],self.nodeindex[id][4])
1178
1179   def set_ways_visible(self,setto,drawallroutine):
1180      if setto == self.waysvisible: return
1181      self.waysvisible = setto
1182      drawallroutine()
1183
1184   def findconnectingsegments(self,nodeid):
1185      res = []
1186      res2 = []
1187      for seg in self.segmentindex:
1188         if (self.segmentindex[seg][0] == nodeid) or (self.segmentindex[seg][1] == nodeid):           
1189            if not seg in res:
1190               res.append(seg)
1191      return res
1192
1193   def statistic_nodes_from_segments(self,seglist):
1194      # stat 0: nodes with exactly one occurence, 1: with more than 2, 2: dict with results
1195      counter = {}
1196      for seg in seglist:
1197         try:
1198            s = self.segmentindex[seg]
1199         except KeyError:    # info for this segment not in database
1200            s = None
1201         
1202         if s:
1203            try:
1204               counter[s[0]] += 1
1205            except KeyError:
1206               counter[s[0]] = 1
1207            try:
1208               counter[s[1]] += 1
1209            except KeyError:
1210               counter[s[1]] = 1
1211
1212      ones = []
1213      mores = []
1214      for d in counter:
1215         if counter[d] == 1:
1216            ones.append(d)
1217         elif counter[d] > 2:
1218            mores.append(d)
1219         else:
1220            pass
1221      return (ones, mores, counter)
1222       
1223   def find_ways_with_segment(self,seg):
1224      ways = []     
1225      for way in self.wayindex:
1226         try:
1227            x = self.wayindex[way][1].index(seg)
1228            ways.append(way)
1229         except ValueError:
1230            pass
1231      return ways
1232
1233   def find_segments_with_node(self,node):
1234      segments = []     
1235      for segment in self.segmentindex:
1236         if self.segmentindex[segment][0] == node or self.segmentindex[segment][1] == node:
1237            segments.append(segment)
1238      return segments
1239
1240   def allowed_to_be_deleted(self,od):
1241      odstr = od[0]
1242      odnode = od[1]
1243      odid = int(odnode.getAttribute("id"))
1244      odname = odnode.nodeName
1245     
1246      objection = None
1247      if odname == "node":
1248         g = self.find_segments_with_node(odid)
1249         if len(g) > 0:
1250            objection = "Node %s found in %s segments\nDelete the segments if you want to delete the node." % (odid,len(g))
1251      elif odname == "segment":
1252         g = self.find_ways_with_segment(odid)
1253         if len(g) > 0:
1254            objection = "Segment %s is part of %s ways.\nDelete the ways before you delete the segment." % (odid,len(g))
1255      elif odname == "way":
1256         pass
1257      else:
1258         objection = "Unknown element"
1259      return objection
1260               
1261   def delete_element(self,od):
1262      odstr = od[0]
1263      odnode = od[1]
1264      odid = int(odnode.getAttribute("id"))
1265      odname = odnode.nodeName
1266     
1267      self.root.removeChild(od[1])     
1268      if odname == "node":
1269         del self.nodeindex[odid]
1270      elif odname == "segment":
1271         del self.segmentindex[odid]
1272      elif odname == "way":
1273         del self.wayindex[odid]
1274      else:
1275         raise ValueError,"Unknown element: %s" % (odname,)
1276
1277   def segment_already_exists(self,id1,id2):
1278      for seg in self.segmentindex:
1279         fromid = self.segmentindex[seg][0]
1280         toid = self.segmentindex[seg][1]
1281         if ((id1 == fromid) and (id2 == toid)) or ((id1 == toid) and (id2 == fromid)):
1282            return True
1283      return False     
1284
1285   def update_index(self,node):
1286      x = node.nodeName
1287      id = int(node.getAttribute("id"))
1288     
1289      if x == "node":
1290         lon = float(node.getAttribute("lon"))
1291         lat = float(node.getAttribute("lat"))
1292         px, py = self.drawarea.transform2xy(lon.lat)
1293         self.nodeindex[id] = [lon,lat,node,px,py]
1294         return self.nodeindex[id]           
1295      elif x == "segment":
1296         id = int(node.getAttribute("id"))
1297         fromnode = int(node.getAttribute("from"))
1298         tonode = int(node.getAttribute("to"))
1299         self.segmentindex[id] = [fromnode, tonode, node]
1300         return self.segmentindex[id]
1301      elif x == "way":
1302         self.wayindex[id] = [node]
1303         waysegs = []
1304         seglist = node.getElementsByTagName("seg")
1305         for seg in seglist:
1306            sid = int(seg.getAttribute("id"))
1307            waysegs.append(sid)
1308         self.wayindex[id].append(waysegs)
1309         return self.wayindex[id]
1310      else:
1311         assert False,"Unknown node type"
1312                   
1313   def segments2boundingbox_xy(self,segmentlist):
1314      b = box_xy()
1315      for x in segmentlist:
1316         b.put_point(self.nodeindex[self.segmentindex[x][0]][3],self.nodeindex[self.segmentindex[x][0]][4])
1317         b.put_point(self.nodeindex[self.segmentindex[x][1]][3],self.nodeindex[self.segmentindex[x][1]][4])
1318      return b
1319   
1320   def minmax_deg(self):
1321      b = box_deg()
1322      for poi in self.nodeindex:
1323         b.put_point(self.nodeindex[poi][0],self.nodeindex[poi][1])
1324      return b.getbox()
1325
1326   def get_node_pointer(self,id):
1327      try:
1328         return self.nodeindex[id][2]
1329      except KeyError:
1330         return None
1331
1332   def get_segment_pointer(self,id):
1333      try:
1334         return self.segmentindex[id][2]
1335      except KeyError:
1336         return None
1337
1338   def get_way_pointer(self,id):
1339      try:
1340         return self.wayindex[id][0]
1341      except KeyError:
1342         return None
1343
1344   def update_node_position(self,id,lon,lat):
1345      self.nodeindex[id][0] = lon
1346      self.nodeindex[id][1] = lat
1347      x,y = self.drawarea.transform2xy(lon,lat)
1348      self.nodeindex[id][3] = x
1349      self.nodeindex[id][4] = y
1350      self.nodeindex[id][2].setAttribute("lon",str(lon))
1351      self.nodeindex[id][2].setAttribute("lat",str(lat))
1352      try:
1353         self.nodeindex[id][2].removeAttribute("timestamp")
1354      except xml.dom.NotFoundErr:
1355         pass
1356#      timestampit(self.nodeindex[id][2])
1357     
1358
1359   def split_segment(self,old_seg_id,new_node_id,new_seg_id,lon,lat):
1360      assert False,"obsolete function"
1361      if self.data == None: raise ValueError,"No OSM context"
1362      # create new node for the split point
1363      self.create_new_node(lon,lat,new_node_id)
1364     
1365      fromnode = self.segmentindex[old_seg_id][0]
1366      tonode = self.segmentindex[old_seg_id][1]
1367      old_seg_node = self.segmentindex[old_seg_id][2]
1368     
1369      # create a new segment with the fromnode of the old segment, to the split point
1370      # and insert it before the old segment
1371      self.create_new_segment(new_node_id,fromnode,new_seg_id,old_seg_node)
1372      # set the fromnode of the old segment to the split point
1373      self.segmentindex[old_seg_id][0] = new_node_id
1374
1375
1376      # look for ways that contain the old segment
1377      waylist = self.find_ways_with_segment(old_seg_id)
1378      for way in waylist:
1379         waynode = self.wayindex[way][0]
1380         seglistofway = self.wayindex[way][1]
1381
1382         if len(seglistofway) == 1:
1383            # a way with only one segment :(
1384            seglistofway.append(new_seg_id)
1385         else:
1386            pos = seglistofway.index(old_seg_id)
1387            if pos == 0:
1388               # edited segment is first in line,
1389               # i.e. we can not check the predecessor
1390               # the additional segment has the old "from id"
1391               # if the sucessor has it, the order is: old part - additional part - next segment
1392               #                         otherwise:    additional part - old part - next segment
1393               if self.segmentindex[seglistofway[pos+1]][0] == fromnode or self.segmentindex[seglistofway[pos+1]][1] == fromnode:
1394                  seglistofway.insert(pos+1,new_seg_id)
1395               else:
1396                  seglistofway.insert(pos,new_seg_id)
1397            else:
1398               # this time we check the predecessor
1399               # if the predecessor has the old "from id", the order is:
1400               #              predecessor - additional part - old part ....
1401               # otherwise:   predecessor - old part - additional part ....
1402               if self.segmentindex[seglistofway[pos-1]][0] == fromnode or self.segmentindex[seglistofway[pos-1]][1] == fromnode:
1403                  seglistofway.insert(pos,new_seg_id)
1404               else:
1405                  seglistofway.insert(pos+1,new_seg_id)
1406                 
1407         # at this point, seglistofway contains the updated segment list
1408         # we now delete the old one in the XML tree and replace it with an updated one
1409         for seg in waynode.getElementsByTagName("seg"):
1410            waynode.removeChild(seg)
1411         for seg in seglistofway:
1412            ele = self.data.createElement("seg")
1413            ele.setAttribute("id",seg)
1414            waynode.appendChild(ele)
1415           
1416
1417   def create_new_node(self,lon,lat,id):
1418      """ create an OSM node in the local data tree
1419      """
1420      if self.data == None: raise ValueError,"No OSM context"
1421      node = self.data.createElement("node")
1422      node.setAttribute("id",str(id))
1423      node.setAttribute("lon",str(lon))
1424      node.setAttribute("lat",str(lat))
1425      self.root.appendChild(node)
1426     
1427      x,y = self.drawarea.transform2xy(lon,lat)
1428      self.nodeindex[id] = [lon,lat,node,x,y]
1429
1430   def create_new_segment(self,fromid,toid,segid,insertBeforeNode = None):
1431      """ create an OSM segment in the local data tree
1432      """
1433      if self.data == None: raise ValueError,"No OSM context"
1434      node = self.data.createElement("segment")
1435      node.setAttribute("id",str(segid))
1436      node.setAttribute("from",str(fromid))
1437      node.setAttribute("to",str(toid))
1438      self.segmentindex[segid] = [fromid, toid, node]
1439      if insertBeforeNode != None:
1440         self.root.insertBefore(node,insertBeforeNode)
1441      else:
1442         self.root.appendChild(node)
1443
1444   def create_new_way(self,sids,id):
1445      if self.data == None: raise ValueError,"No OSM context"
1446      node = self.data.createElement("way")
1447      node.setAttribute("id",str(id))
1448     
1449      # OSM server requires at least on tag (s.a. create_new_way in OSMAPI)
1450      snode = self.data.createElement("tag")
1451      snode.setAttribute("k","created_by")
1452      snode.setAttribute("v","pyosmeditor")
1453      node.appendChild(snode)
1454     
1455      for s in sids:
1456         snode = self.data.createElement("seg")
1457         snode.setAttribute("id",str(s))
1458         node.appendChild(snode)
1459      self.wayindex[id] = [node,sids]
1460
1461#### CLASS GEOTAGHINTS ####
1462class geotaghints(infoelement):
1463   # index:
1464   # 0 - lon
1465   # 1 - lat
1466   # 2 - alt
1467   # 3 - x
1468   # 4 - y
1469   # 5 - fnm (path)
1470   def __init__(self,drawarea):
1471      self.drawarea = drawarea
1472      self.data = None
1473      self.index = []
1474      self.visible = True
1475      self.recalcwaiting = False
1476      self.iconsize = 10
1477   
1478   def readdata(self,fil):       # fil is name of subdir
1479      self.data = fil            # something not None
1480      files = os.listdir(fil)
1481      for fi in files:
1482         fnm = os.path.join(fil,fi)
1483         dat = self.get_gps_info(fnm)
1484         if dat:
1485            self.index.append([dat[0],dat[1],dat[2],0,0,fnm])
1486
1487   def recalcdata(self):
1488      if not self.must_i_recalc(): return                   # do this in child classes as well
1489      for n in self.index:
1490            n[3], n[4] = self.drawarea.transform2xy(n[0], n[1])
1491
1492   def draw(self, box = None, refresh = True):
1493      if not self.drawarea.screencoordsset: return          # do this in child classes as well
1494
1495      if box == None:
1496         b = box_xy((0,0,self.drawarea.da_width,self.drawarea.da_height))
1497      else:
1498         b = box_xy(box)
1499         
1500      for dat in self.index:
1501         if b.isinbox(dat[3],dat[4]):
1502            self.drawarea.drawtriangle(dat[3],dat[4],self.iconsize,refresh = False)
1503     
1504      if refresh:
1505         self.drawarea.refresh()
1506   
1507   def find_geohint(self,x,y):
1508      h = self.iconsize / 2
1509      b = box_xy((x-h,y-h,x+h,y+h))
1510      for dat in self.index:
1511         if b.isinbox(dat[3],dat[4]):
1512            return dat[5]
1513     
1514      return None
1515     
1516   def isvisible(self):
1517      return (self.data != None) and self.visible
1518
1519   def val_fract(self,s):
1520      return float(s.num) / float(s.den)
1521
1522   def val_deg(self,s):
1523      g = self.val_fract(s[0])
1524      m = self.val_fract(s[1])
1525      s = self.val_fract(s[2])
1526      return g + m / 60.0 + s / 3600.0
1527   
1528   def get_gps_info(self,fnm):
1529      try:
1530         f=open(fnm, 'rb')
1531         tags=EXIF.process_file(f)
1532
1533         lat = tags['GPS GPSLatitude']
1534         latR = tags['GPS GPSLatitudeRef']
1535         lon = tags['GPS GPSLongitude']
1536         lonR = tags['GPS GPSLongitudeRef']
1537         alt = tags['GPS GPSAltitude']
1538         altR = tags['GPS GPSAltitudeRef']
1539      except KeyError:
1540         return None
1541     
1542      lat = self.val_deg(lat.values)
1543      if latR == "S": lat = -lat
1544      lon = self.val_deg(lon.values)
1545      if lonR == "W": lon = -lon
1546      alt = self.val_fract(alt.values[0])
1547      if altR == "1": alt = -alt
1548      return (lon,lat,alt)
1549
1550     
1551     
1552
1553   
1554#################### CLASS DRAWARROW ####################
1555class calcarrow:
1556   # calculating arrowline
1557   # sin and cos are cached in a lookup table with a resolution of 1 degree
1558   # (should be sufficient for arrow heads)
1559   
1560   arrows = ( ((-10,5),(-10,-5)), \
1561              ((-10,3),(-10,-3)) ) 
1562
1563   def __init__(self):
1564      self.trigolookup = {}
1565     
1566   def getsincos(self,x):
1567      x = int(x)
1568      try:
1569         return self.trigolookup[x]
1570      except KeyError:
1571         r = math.radians(x)
1572         sin = math.sin(r)
1573         cos = math.cos(r)
1574         self.trigolookup[x] = (sin,cos)
1575         return (sin,cos)
1576         
1577   def docalc(self, p, a):
1578      s, c = self.getsincos(a)
1579      x = p[0]*c - p[1]*s
1580      y = p[0]*s + p[1]*c
1581      return int(x), int(y)
1582     
1583   def calcarrow(self,sx,sy,ex,ey,atype = 0):
1584      dy = ey-sy
1585      dx = ex-sx
1586     
1587      if dx == 0:
1588         if dy == 0: raise ZeroDivisionError,"dx and dy are zero"
1589         if dy > 0: 
1590            alpha = 90
1591         else:
1592            alpha = 270
1593      else:
1594         alpha = math.degrees(math.atan2(dy,dx))     # atan2 = clever atan which puts the angle into the right quadrant
1595           
1596      dx1, dy1 = self.docalc(self.arrows[atype][0],alpha)
1597      dx2, dy2 = self.docalc(self.arrows[atype][1],alpha)
1598     
1599      return (ex+dx1,ey+dy1,ex+dx2,ey+dy2)
1600     
1601#################### CLASS DRAWAREA #####################
1602
1603class drawarea:
1604   def __init__(self,widget):
1605      self.widget = widget
1606      self.osmdata = None
1607      self.osm_box = None
1608     
1609      x, y, self.da_width, self.da_height = self.widget.get_allocation()
1610         
1611      self.pixmap = gtk.gdk.Pixmap(self.widget.window, self.da_width, self.da_height)
1612   
1613      self.colormap = self.widget.get_colormap()
1614      self.white     = self.colormap.alloc_color("#ffffff")
1615      self.lightgrey = self.colormap.alloc_color("#eeeeee")
1616      self.black     = self.colormap.alloc_color("#000000")
1617      self.red       = self.colormap.alloc_color("#f80000")
1618      self.green     = self.colormap.alloc_color("#00d000")
1619      self.yellow    = self.colormap.alloc_color("#e0e000")
1620      self.brown     = self.colormap.alloc_color('#fba208')
1621      self.blue      = self.colormap.alloc_color('#0000ff')
1622      self.gc = self.widget.window.new_gc()
1623     
1624      # GC for selection frame
1625      self.gc_selframe = self.widget.window.new_gc()
1626      self.gc_selframe.set_function(gtk.gdk.INVERT)
1627      self.gc_selframe.set_foreground(self.black)
1628      self.gc_selframe.set_line_attributes(2,gtk.gdk.LINE_ON_OFF_DASH,gtk.gdk.CAP_BUTT,gtk.gdk.JOIN_MITER)
1629     
1630      # helper class
1631      self.calcarrow = calcarrow()
1632      self.arrowthreshold = 20000
1633      self.arrowvisible = True
1634     
1635      self.damagearea = box_xy()
1636      self.noderadius = 8
1637      self.waypointradius = 3
1638      self.cleardrawingarea()
1639#      self.font_desc = pango.FontDescription('Serif 12')
1640     
1641      self.lb_lon = None
1642      self.lb_lat = None
1643      self.rt_lon = None
1644      self.rt_lat = None
1645      self.screencoordsset = False
1646 
1647     
1648   def set_osm_box(self,box):
1649      self.osm_box = box
1650 
1651   def set_arrow_threshold(self,f):
1652      self.arrowthreshold = f
1653 
1654   def set_arrow_visible(self,flag):
1655      self.arrowvisible = flag
1656     
1657   def resizescreen(self):
1658      # calling routine must recalc and redraw
1659      x, y, self.da_width, self.da_height = self.widget.get_allocation()
1660      self.pixmap = gtk.gdk.Pixmap(self.widget.window, self.da_width, self.da_height)
1661      self.pixmap.draw_rectangle(self.widget.get_style().white_gc,True, 0, 0, self.da_width, self.da_height)
1662
1663      if not self.screencoordsset: return
1664      lon2 = self.lb_lon + self.da_width / self.factlong
1665      lat1 = self.rt_lat - self.da_height / self.factlat
1666     
1667      self.setscreencoords(self.lb_lon,lat1,lon2,self.rt_lat) 
1668 
1669   def normlon(self,lon):
1670      if lon > 180:
1671        lon -= 360
1672      if lon < -180:
1673        lon += 360
1674      return lon
1675     
1676   def zoom(self,fact):   
1677      lb_lon = self.lb_lon
1678      lb_lat = self.lb_lat
1679      rt_lon = self.rt_lon
1680      rt_lat = self.rt_lat
1681      difflong = rt_lon - lb_lon
1682      difflat = rt_lat - lb_lat
1683     
1684      newdiff = self.da_width / (self.factlong * fact)
1685      delta = (difflong - newdiff) / 2
1686
1687      if self.warpflag:
1688         lb_lon -= delta
1689      else:
1690         lb_lon += delta
1691     
1692      lb_lon = self.normlon(lb_lon)
1693         
1694      if self.warpflag:
1695         rt_lon += delta
1696      else:
1697         rt_lon -= delta
1698
1699      rt_lon = self.normlon(rt_lon)         
1700     
1701      newdiff = self.da_height / (self.factlat * fact)
1702      delta = (newdiff - difflat) / 2
1703     
1704      lb_lat -= delta
1705      rt_lat += delta
1706     
1707      if lb_lat < -90 or rt_lat > 90:
1708         return None
1709     
1710      if debug: 
1711        print "von:", self.lb_lon,self.lb_lat,self.rt_lon,self.rt_lat
1712        print "nach:", lb_lon,lb_lat,rt_lon,rt_lat
1713        print self.lb_lon,self.lb_lat,self.rt_lon,self.rt_lat
1714       
1715      return [lb_lon,lb_lat,rt_lon,rt_lat]
1716
1717   def move_xy(self,dx,dy):
1718      dlon = dx / self.factlong
1719      dlat = dy / self.factlat
1720
1721      lb_lon = self.normlon(self.lb_lon - dlon)
1722      lb_lat = self.lb_lat + dlat
1723      rt_lon = self.normlon(self.rt_lon - dlon)
1724      rt_lat = self.rt_lat + dlat
1725
1726      if lb_lat < -90 or rt_lat > 90:
1727         return None
1728
1729      return [lb_lon,lb_lat,rt_lon,rt_lat]
1730             
1731   def setscreencoords(self,lon1,lat1,lon2,lat2,center = False):
1732      # data needs to be recalculated after this function
1733      #
1734      # Note: when the main loop calls this function,
1735      # check if menu options and buttons must be enabled
1736     
1737      self.lb_lon = lon1
1738      self.lb_lat = lat1
1739      self.rt_lon = lon2
1740      self.rt_lat = lat2
1741      self.screencoordsset = True
1742     
1743      self.lb_lon, self.lb_lat, self.rt_lon, self.rt_lat, self.warpflag = normalizebox_deg(lon1,lat1,lon2,lat2)
1744      difflong = self.rt_lon - self.lb_lon
1745      if self.warpflag:
1746         difflong = 360 - difflong
1747         
1748      difflat = self.rt_lat - self.lb_lat
1749
1750      assert difflong != 0
1751      assert difflat != 0
1752           
1753      self.factlong =  self.da_width / difflong
1754      self.factlat  =  self.da_height / difflat
1755     
1756      if self.factlong > self.factlat:
1757         self.factlong = self.factlat
1758      else:
1759         self.factlat = self.factlong
1760
1761      if center:
1762         remainlong = (self.da_width  / self.factlong - difflong) / 2.0
1763         remainlat  = (self.da_height / self.factlat - difflat) / 2.0
1764         self.lb_lon -= remainlong
1765         self.lb_lat -= remainlat
1766         self.rt_lon += remainlong
1767         self.rt_lat += remainlat
1768   
1769   def set_bookmark(self,lon,lat,scale):
1770      dlon = self.da_width / scale / 2
1771      dlat = self.da_height / scale / 2
1772
1773      self.setscreencoords(lon - dlon, lat - dlat, lon + dlon, lat + dlat, True)
1774     
1775     
1776     
1777   def get_screencoordinates(self):
1778      return (self.lb_lon,self.lb_lat,self.rt_lon,self.rt_lat)
1779     
1780   def get_screenbookmark(self):
1781      m_lon = (self.lb_lon + self.rt_lon) / 2
1782      m_lat = (self.lb_lat + self.rt_lat) / 2
1783      m_fac = self.factlat
1784
1785      ##TODO: warp flag
1786      return (m_lon, m_lat, m_fac)
1787     
1788   def damage(self,x1,y1,x2,y2):
1789      """ Enlarge damaged area to include rectangle (x1,y1) (x2,y2)
1790      """
1791      dax = max(min(x1,x2),0)
1792      day = max(min(y1,y2),0)
1793      dbx = max(x1,x2)
1794      dby = max(y1,y2)
1795     
1796      self.damagearea.putbox(dax,day,dbx,dby)
1797
1798   def cleardrawingarea(self,color = None, box = None):
1799     if color == None:
1800        color1 = self.lightgrey
1801     if box == None:
1802        box = (0,0,self.da_width,self.da_height)
1803     
1804     x1, y1, x2, y2 = box
1805     self.gc.set_foreground(color1)
1806     self.pixmap.draw_rectangle(self.gc, True, x1, y1, x2 - x1 + 1, y2 - y1 + 1)
1807     
1808     if self.osm_box:
1809        # fill area that is covered by OSM data with white background
1810        lbx, lby = self.transform2xy(self.osm_box[0],self.osm_box[1])
1811        rtx, rty = self.transform2xy(self.osm_box[2],self.osm_box[3])
1812        nbx = max(lbx,x1)    # Note: 0/0 of the screen is in the top left corner
1813        nby = max(rty,y1)    #   but the boxes are defined by the left bottom and top right corner
1814        ntx = min(rtx,x2)
1815        nty = min(lby,y2)
1816     
1817        if nbx < ntx and nby < nty:
1818           self.gc.set_foreground(self.white)
1819           self.pixmap.draw_rectangle(self.gc, True, nbx, nby, ntx - nbx + 1, nty - nby + 1)
1820     
1821     self.damage(x1, y1, x2, y2)
1822   
1823   def refresh(self,all = False, box = None):
1824      """ refresh display from pixmap
1825          all:  True: entire area
1826                False: damaged area only
1827      """
1828      if all:
1829         self.widget.queue_draw_area(0,0,self.da_width,self.da_height)
1830      else:
1831         if not self.damagearea.empty():
1832            if box == None:
1833               box = self.damagearea.getbox()
1834            x1, y1, x2, y2 = box
1835            self.widget.queue_draw_area(x1,y1,x2-x1+1,y2-y1+1)
1836      self.damagearea.clear()
1837
1838   def exposed(self,widget,event):
1839      x , y, width, height = event.area
1840      self.widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL],self.pixmap, x, y, x, y, width, height)
1841       
1842   def drawline(self,x1,y1,x2,y2,color = None, linewidth = 1, refresh = True, arrowtype = 0):
1843     """ origin = left/top left top
1844         arrow = arrow at x2,y2
1845     """
1846     if color == None:
1847        color = self.black
1848     self.gc.set_foreground(color)
1849     self.gc.line_width = linewidth
1850     self.pixmap.draw_line(self.gc,x1,y1,x2,y2)
1851     self.damage(x1-linewidth,y1-linewidth,x2+linewidth,y2+linewidth)
1852
1853     arrow = (arrowtype > 0) and self.arrowvisible and (self.factlong >= self.arrowthreshold)
1854
1855     if arrow:
1856        try:
1857          ax1, ay1, ax2, ay2 = self.calcarrow.calcarrow(x1,y1,x2,y2,arrowtype-1)
1858          self.pixmap.draw_polygon(self.gc,True,[(x2,y2),(ax1,ay1), (ax2,ay2)])
1859          self.damagearea.put_point(ax1,ay1)
1860          self.damagearea.put_point(ax2,ay2)
1861        except ZeroDivisionError:    # caused by segments where from == to
1862          pass   
1863
1864     if refresh:
1865        self.refresh()
1866   
1867   def drawcircle(self,x,y,r,color = None, refresh = True):
1868     if color == None:
1869        color = self.black
1870     xn = int(x - r/2)
1871     yn = int(y - r/2)
1872       
1873     self.gc.set_foreground(color)
1874     self.pixmap.draw_arc(self.gc,True,xn,yn,r,r,0,360*64)
1875     self.damage(xn-r,yn-r,xn+r,yn+r)
1876     if refresh:
1877        self.refresh()
1878       
1879   def drawrectangle(self,x1,y1,x2,y2,color = None, refresh = True):
1880     if color == None:
1881        color = self.black
1882     self.gc.set_foreground(color)
1883     self.pixmap.draw_rectangle(self.gc,True,x1,y1,x2-x1+1,y2-y1+1)
1884
1885     self.damage(x1,y1,x2,y2)
1886     if refresh:
1887        self.refresh()
1888
1889   def drawtriangle(self,x,y,d,color = None, refresh = True):
1890     if color == None:
1891        color = self.green
1892     w = d / 2
1893     x1 = x - w
1894     y1 = y - w
1895     x2 = x + w
1896     y2 = y + w
1897     self.gc.set_foreground(color)
1898     self.pixmap.draw_polygon(self.gc,True,[(x1,y1),(x2,y1), (x,y2)])
1899
1900     self.damage(x1,y1,x2,y2)
1901     if refresh:
1902        self.refresh()
1903           
1904   
1905   def transform2xy(self,lon,lat):
1906      if self.warpflag and (lon<0):
1907         lon += 360.0
1908      lon -= self.lb_lon
1909      lat -= self.lb_lat
1910      x = int(lon * self.factlong)
1911      y = int(lat * self.factlat)
1912      y = self.da_height - y
1913      return (x,y)
1914
1915   def transform2lonlat(self,xy):
1916      y = self.da_height - xy[1]
1917      lon = xy[0] / self.factlong
1918      lat = y / self.factlat
1919     
1920      lon += self.lb_lon
1921      lat += self.lb_lat
1922      return (lon,lat)
1923     
1924   def drawnode(self,id, color = None, refresh = True):
1925      if color == None:
1926         color = self.black
1927      # 0 = lon, 1 = lat, 2 = node, 3 = x, 4 = y
1928      self.drawcircle(self.osmdata.nodeindex[id][3],self.osmdata.nodeindex[id][4],self.noderadius,color, refresh)
1929     
1930   def drawsegment(self,id, color = None, linewidth = 1, refresh = True, arrowtype = 0):
1931      if color == None:
1932         color = self.black
1933      # 0 = from, 1 = to, 2 = node
1934      n1 = self.osmdata.segmentindex[id][0]
1935      n2 = self.osmdata.segmentindex[id][1]
1936
1937      self.drawline(self.osmdata.nodeindex[n1][3],self.osmdata.nodeindex[n1][4],self.osmdata.nodeindex[n2][3],self.osmdata.nodeindex[n2][4],color,linewidth,refresh, arrowtype)
1938
1939   def drawway(self,id, color = None, linewidth = 4, refresh = True, arrowtype = 0):
1940      if color == None:
1941         color = self.brown
1942
1943      segs = self.osmdata.wayindex[id][1]
1944      for seg in segs:
1945         try:
1946            self.drawsegment(seg,color,linewidth,refresh,arrowtype = arrowtype)
1947         except KeyError:
1948            pass
1949
1950   def drawwaypoint(self,waypoint, color = None, refresh = True):
1951      TEXT_DX = -10
1952      TEXT_DY = 10
1953     
1954      if color == None:
1955         color = self.red
1956      # 0 = lon, 1 = lat, 2 = node, 3 = x, 4 = y, 5 = name
1957
1958      self.drawrectangle(waypoint[3]-self.waypointradius,waypoint[4]-self.waypointradius,waypoint[3] + self.waypointradius,waypoint[4]+self.waypointradius,color, refresh)
1959      if waypoint[5]:
1960           pangolayout = pango.Layout(self.widget.get_pango_context())
1961           pangolayout.set_text(waypoint[5])
1962           # optional
1963#           pangolayout.set_font_description(self.drawarea.font_desc)
1964           
1965           text_width, text_height = pangolayout.get_pixel_size()
1966           self.pixmap.draw_layout(self.gc, waypoint[3]+TEXT_DX, waypoint[4]+TEXT_DY, pangolayout, foreground = color)
1967           self.damage(waypoint[3]+TEXT_DX,waypoint[4]+TEXT_DY,waypoint[3]+TEXT_DX+text_width,waypoint[4]+TEXT_DY+text_height)
1968      if refresh:
1969         self.refresh()
1970
1971 
1972     
1973######################## CLASS OSMAPI ################
1974     
1975class osmapi:
1976   # API doc: http://wiki.openstreetmap.org/index.php/API
1977   APIVERSION = '0.3'
1978   
1979
1980   def __init__(self,sitename,passwordrequest,errorfunction):
1981      self.osmsite = sitename
1982      self.passwordrequest = passwordrequest
1983      self.errorfunction = errorfunction
1984 
1985   def httprequest(self,host,cmd,url,body = None):
1986      ##TODO: cancel pressed -> Exception
1987      username, password = self.passwordrequest()
1988     
1989      headers = {
1990                'User-Agent': 'pyosmeditor/%s' % (pyosmeditorversion,),
1991                 'Authorization': "Basic " + (username + ":" + password).encode("base64").rstrip(),
1992                 'Accept-Encoding': 'gzip'
1993                 }
1994                 
1995      if body != None:
1996         body = body.decode(localeencoding)
1997         body = body.encode(DB_ENCODING)
1998         headers["Content-Length"] = len(body)
1999   
2000      conn = httplib.HTTPConnection(host)
2001      conn.request(cmd,url,headers = headers, body = body)
2002      response = conn.getresponse()
2003      if response.status == 200:
2004         data = response.read()
2005         if response.getheader("content-encoding") == "gzip":
2006            data = gzip.GzipFile(fileobj=StringIO.StringIO(data))
2007         else:
2008            data = StringIO.StringIO(data)
2009      else:
2010         if self.errorfunction:
2011            self.errorfunction(response.status,response.reason)
2012         data = None
2013      response.close()
2014      conn.close()     
2015     
2016      return data, response.status
2017   
2018   def apiprefix(self):
2019      return "/api/%s/" % (self.APIVERSION,)
2020   
2021   def getmap(self,bllon,bllat,trlon,trlat):
2022      url = self.apiprefix() + "map?bbox=%s,%s,%s,%s" % (bllon,bllat,trlon,trlat)
2023      response, status = self.httprequest(self.osmsite,"GET",url)
2024      ## TODO: check f.status
2025      # 200 ok
2026      # 401 Auth required
2027      return response
2028   
2029   def getelements(self,ids,single,multiple = None):
2030      if type(ids) == tuple:
2031         assert multiple != None,"tuple but no name for multiple query"
2032         liste = ""
2033         for id in ids:
2034            if liste == "":
2035               liste = str(id)
2036            else:
2037               liste += "," + str(id)
2038         url = "%s%s%s" % (self.apiprefix(),multiple,liste)
2039      else:
2040         url = "%s%s/%s" % (self.apiprefix(),single,ids)
2041
2042      response, status = self.httprequest(self.osmsite,"GET",url)
2043      ## TODO: check f.status
2044      # 200 ok
2045      # 401 Auth required
2046      return response
2047   
2048   def getnodes(self,nodes):
2049      return self.getelements(nodes,"node","nodes?nodes=")
2050         
2051   def getway(self,way):
2052      return self.getelements(way,"way","ways/")
2053       
2054   def getsegment(self,segment):
2055      return self.getelements(segment,"segment")
2056     
2057   def ways_for_segment(self,segment):
2058      url = self.apiprefix() + "segment/%s/ways" % (segment,)
2059      response, status = self.httprequest(self.osmsite,"GET",url)
2060      return response
2061
2062   def areas_for_segment(self,segment):
2063      url = self.apiprefix() + "segment/%s/areas" % (segment,)
2064      response, status = self.httprequest(self.osmsite,"GET",url)
2065      return response
2066   
2067   def gettrackpoints(self,bllon,bllat,trlon,trlat,page = 0):
2068      ## TODO: catch HTTP errors
2069      url = self.apiprefix() + "trackpoints?bbox=%s,%s,%s,%s&page=%s" % (bllon,bllat,trlon,trlat,page)
2070      response, status = self.httprequest(self.osmsite,"GET",url)
2071      ## TODO: check f.status
2072      # 200 ok
2073      # 401 Auth required
2074      return response
2075
2076   def create_new_node(self,lon,lat):
2077      body = "<osm version=\"%s\" generator=\"pyosmeditor\"><node lon='%s' lat='%s' id='0'></node></osm>\n" % (self.APIVERSION,lon,lat)
2078      url = self.apiprefix() + "node/0"
2079      response, status = self.httprequest(self.osmsite,"PUT",url,body = body)
2080      if status == 200:
2081         res = response.read()
2082         response.close()
2083         try:
2084            res = int(res)
2085            return res
2086         except ValueError:
2087            return None
2088      else:
2089         return None
2090     
2091   def create_new_segment(self,fromid,toid):
2092      body = "<osm version=\"%s\" generator=\"pyosmeditor\"><segment from='%s' to='%s' id='0'></segment></osm>\n" % (self.APIVERSION,fromid,toid)
2093      url = self.apiprefix() + "segment/0"
2094      response, status = self.httprequest(self.osmsite,"PUT",url,body = body)
2095      if status == 200:
2096         res = response.read()
2097         response.close()
2098         try:
2099            res = int(res)
2100            return res
2101         except ValueError:
2102            return None
2103      else:
2104         return None
2105
2106   def create_new_way(self,seglist):
2107      liste = "<tag k='created_by' v='pyosmeditor' />" # dummy tag, as OSM server requires it
2108      for sid in seglist:
2109         liste += "<seg id='%s' />\n" % (sid,)
2110      body = "<osm version='%s'>\n<way id='0'>\n%s</way></osm>\n" % (self.APIVERSION,liste)
2111      url = self.apiprefix() + "way/0"
2112     
2113      response, status = self.httprequest(self.osmsite,"PUT",url,body = body)
2114      if status == 200:
2115         res = response.read()
2116         response.close()
2117#         print "way response:", res
2118         try:
2119            res = int(res)
2120            return res
2121         except ValueError:
2122            return None
2123      else:
2124         return None
2125
2126   def get_ways_of_segment(self,id):
2127      url = self.apiprefix() + "segment/%s/ways" % (id,)
2128      response, status = self.httprequest(self.osmsite,"GET",url)
2129      if status == 200:
2130         res = response.read()
2131         response.close()
2132         return res
2133      return None
2134   
2135   def count_ways_of_segment(self,id):
2136      data = self.get_ways_of_segment(id)
2137      if data == None: return None
2138      doc = xml.dom.minidom.parseString(data)
2139      root = doc.getElementsByTagName("osm")[0]
2140      assert root != None
2141      nodelist = root.getElementsByTagName("way")
2142      return len(nodelist)
2143
2144   def get_areas_of_segment(self,id):
2145      url = self.apiprefix() + "segment/%s/areas" % (id,)
2146      response, status = self.httprequest(self.osmsite,"GET",url)
2147      if status == 200:
2148         res = response.read()
2149         response.close()
2150         return res
2151      return None
2152
2153   def count_areas_of_segment(self,id):
2154      data = self.get_areas_of_segment(id)
2155      if data == None: return None
2156      doc = xml.dom.minidom.parseString(data)
2157      root = doc.getElementsByTagName("osm")[0]
2158      assert root != None
2159      nodelist = root.getElementsByTagName("area")
2160      return len(nodelist)
2161
2162   def delete_element(self,id):
2163      url = self.apiprefix() + "%s" % (id,)
2164      response, status = self.httprequest(self.osmsite,"DELETE",url)
2165      if status == 200:
2166         res = response.read()
2167         response.close()
2168         return True
2169      return False
2170     
2171   def send_updated_data(self,tree):
2172      # 200 ok
2173      # ??? BAD REQUEST
2174      name = tree.nodeName
2175      id = tree.getAttribute("id")
2176      url = "%s%s/%s" % (self.apiprefix(),name,id) # e.g. "baseurl/node/123"
2177      data = tree.toxml() 
2178      body = "<osm version=\"%s\" generator=\"pyosmeditor\">%s</osm>" % (self.APIVERSION,data)
2179     
2180      response, status = self.httprequest(self.osmsite,"PUT",url,body = body)
2181      if status == 200:
2182         res = response.read()
2183         response.close()
2184         return True
2185      return False
2186
2187 
2188########################## CLASS TAGEDITOR #########################
2189
2190class tageditor:
2191   def __init__(self,treeview,button_add,button_del,button_apply,editable):
2192      assert treeview != None
2193      self.tree = treeview
2194      self.doc = None                          # XML root of OSM data
2195      self.editable = editable
2196
2197      self.store = gtk.ListStore(str,str)
2198      self.tree.set_model(self.store)          # replaces glades empty store with ours
2199
2200      column = gtk.TreeViewColumn("key")
2201      self.tree.append_column(column)
2202      self.renderer1 = gtk.CellRendererText()
2203      self.renderer1.set_property('editable', editable)
2204      self.renderer1.connect('edited', self.cell_edited, 0)  # user data = no. of column
2205      column.pack_start(self.renderer1, True)
2206      column.add_attribute(self.renderer1, 'text', 0)
2207
2208      column = gtk.TreeViewColumn("value")
2209      self.tree.append_column(column)
2210      self.renderer2 = gtk.CellRendererText()
2211      self.renderer2.set_property('editable', editable)
2212      self.renderer2.connect('edited', self.cell_edited, 1) # user data = no. of column
2213      column.pack_start(self.renderer2, True)
2214      column.add_attribute(self.renderer2, 'text', 1)
2215           
2216      self.button_add = button_add
2217      self.button_del = button_del
2218      self.button_apply = button_apply
2219     
2220      self.datanode = None
2221      self.dirty = False
2222      self.setbuttonstatus()
2223     
2224   def set_xml_root(self,doc):
2225      # We need this information do modify the data in the XML tree
2226      self.doc = doc
2227     
2228   def setbuttonstatus(self):
2229      if self.datanode == None:
2230         # tree, add, del, apply
2231         enable = [False,False,False,False]
2232      else:
2233         if self.editable:
2234            (path,column) = self.tree.get_cursor()
2235            selected = (path != None)
2236            enable = [True,True,selected,self.dirty]
2237         else:
2238            enable = [True,False,False,False]
2239
2240      if self.tree != None:
2241         self.tree.set_sensitive(enable[0])
2242      self.button_add.set_sensitive(enable[1])
2243      self.button_del.set_sensitive(enable[2])
2244      self.button_apply.set_sensitive(enable[3])       
2245         
2246   def set_editable(self,editable):
2247      if editable == None: return
2248     
2249      self.editable = editable
2250      self.renderer1.set_property('editable', self.editable)
2251      self.renderer2.set_property('editable', self.editable)
2252      self.setbuttonstatus()
2253   
2254   def set_tags(self,datanode,editable = None):
2255      self.datanode = datanode
2256      self.set_editable(editable)
2257     
2258      self.store.clear()
2259      self.dirty = False
2260      self.setbuttonstatus()
2261      if datanode  == None: return
2262     
2263      liste = self.datanode.getElementsByTagName("tag")
2264      for tag in liste:
2265         k = tag.getAttribute("k")
2266         v = tag.getAttribute("v")
2267         self.store.append([k,v])
2268         
2269   def addbutton(self):
2270      self.dirty = True
2271      self.store.append(["key","value"])
2272      self.setbuttonstatus()
2273     
2274   def delbutton(self):
2275      self.dirty = True
2276      (path,column) = self.tree.get_cursor()
2277      if path == None: return
2278      iter = self.store.get_iter(path)
2279      self.store.remove(iter)
2280      self.setbuttonstatus()
2281     
2282   def applybutton(self):
2283      try:
2284         self.datanode.removeAttribute("timestamp")
2285      except xml.dom.NotFoundErr:
2286         pass
2287#      timestampit(self.datanode)
2288     
2289      liste = self.datanode.getElementsByTagName("tag")
2290      for tag in liste:
2291         self.datanode.removeChild(tag)
2292         
2293      it = self.store.get_iter_first()
2294      while it:
2295         k = self.store.get_value(it,0)
2296         v = self.store.get_value(it,1)
2297         tag = self.doc.createElement("tag")
2298         tag.setAttribute("k",k)
2299         tag.setAttribute("v",v)
2300         self.datanode.appendChild(tag)
2301         
2302         it = self.store.iter_next(it)
2303     
2304      self.dirty = False
2305      self.setbuttonstatus()
2306   
2307   def cell_edited(self,cell, path, new_text, data = None):
2308      # completion of edit in textcell
2309      # cell: edited cell, user_data: Nr. of column (see connect command)
2310      self.dirty = True
2311      self.store[path][data] = new_text
2312      self.setbuttonstatus()
2313
2314   def cursor_changed(self):
2315      self.setbuttonstatus()
2316
2317################ CLASS STATUSBARHANDLER ################
2318class statusbarhandler:
2319   def __init__(self,widget):
2320      self.statusbar = widget
2321      self.lonlat_context_id = self.statusbar.get_context_id("longitude and latitude")
2322      self.msgid = None
2323   
2324   def put(self,txt):
2325      if self.msgid != None:
2326         self.statusbar.pop(self.lonlat_context_id)
2327      self.msgid = self.statusbar.push(self.lonlat_context_id,txt)
2328
2329################ CLASS TOOLBAR HANDLER ################
2330class toolbarhandler:
2331
2332   def __init__(self,osmeditor):
2333      self.osmeditor = osmeditor
2334      self.last_mode = ""
2335     
2336      self.widgets = (self.osmeditor.xml.get_widget('tb_delete'),
2337                      self.osmeditor.xml.get_widget('tb_var1'),
2338                      self.osmeditor.xml.get_widget('tb_var2') )
2339 
2340      self.labels = {
2341         "node":         (_("delete"),None,None),
2342         "nodes":        (None,_("create\nsegment(s)"),None),
2343         "segment":      (_("delete"),_("create\nway"),_("from <> to")),
2344         "segments":     (None,_("create\nway"),_("align\nf->t")),
2345         "way":          (_("delete"),None,None),
2346         "way/segments": (None,_("add to\nway"),None)
2347         }
2348
2349      self.functions = {
2350         "node":          (self.osmeditor.tb_delete_element,
2351                           None,
2352                           None),
2353         "nodes":         (None,
2354                           self.osmeditor.tb_create_segments,
2355                           None),
2356         "segment":       (self.osmeditor.tb_delete_element,
2357                           self.osmeditor.tb_create_way,
2358                           self.osmeditor.tb_align_from_to),
2359         "segments":      (None,
2360                           self.osmeditor.tb_create_way,
2361                           self.osmeditor.tb_align_from_to),
2362         "way":           (self.osmeditor.tb_delete_element,
2363                           None,
2364                           None),
2365         "way/segments":  (None,
2366                           self.osmeditor.tb_add_segments_to_way,
2367                           None)
2368         }
2369     
2370   
2371   def set_tb_icons(self,mode):
2372      if self.osmeditor.tb_mode != 1: mode = ""     # No icons if not in Edit mode
2373 
2374      if mode == self.last_mode:
2375         return
2376         
2377      self.last_mode = mode
2378     
2379      try:
2380         labels = self.labels[mode]
2381      except KeyError:
2382         labels = (None,None,None)
2383
2384      for i in range(3):
2385         if labels[i] == None:
2386            if i>0:
2387              self.widgets[i].set_visible_horizontal(False)
2388            self.widgets[i].set_sensitive(False)
2389         else:
2390            if i>0:   # delete icon stays
2391               self.widgets[i].set_label(labels[i])
2392            self.widgets[i].set_visible_horizontal(True)
2393            self.widgets[i].set_sensitive(True)
2394           
2395   def call_function(self,no):
2396      try:
2397         function = self.functions[self.last_mode][no]
2398      except KeyError:
2399         function = None
2400         
2401      if function:
2402         function()
2403
2404
2405################ CLASS SELECTED_ELEMENTS_HANDLER ###############
2406
2407class selected_elements_handler:
2408   def __init__(self,osmeditor):
2409      self.osmeditor = osmeditor
2410      self.tageditor = self.osmeditor.tageditor
2411      self.selected_item_textentry = self.osmeditor.xml.get_widget('selected_item')
2412      self.notebook = self.osmeditor.xml.get_widget('notebook')
2413      self.editable = False
2414      self.selected_elements = []
2415   
2416   def clear(self):
2417      for ele in self.selected_elements:
2418         ele.draw(self.osmeditor.drawarea, box = None, selected = False)
2419      self.osmeditor.drawarea.refresh()
2420     
2421      self.selected_elements = []
2422     
2423      # clear info
2424      self.selected_item_textentry.set_text("")
2425     
2426      # clear tageditor
2427      self.tageditor.set_tags(None,False)
2428      # clear infopane
2429      self.osmeditor.print_to_infopane("")
2430
2431      self.osmeditor.toolbarhandler.set_tb_icons("")
2432   
2433   def get_selected_list(self):
2434      return self.selected_elements
2435   
2436   def get_selected_list_ids(self):
2437      res = []
2438      for l in self.selected_elements:
2439         res.append(l.get_id())
2440      return res
2441
2442   def get_count(self):
2443      return len(self.selected_elements)
2444     
2445   def get_type(self):
2446      if self.selected_elements == []:
2447         return None
2448      else:
2449         return self.selected_elements[0].get_type()
2450   
2451   def get_info(self):
2452      return (len(self.selected_elements), self.get_type())
2453   
2454   def already_exists(self,ele):
2455      info = ele.get_osm_info()
2456      for lele in self.selected_elements:
2457         if lele.get_osm_info() == info: return True
2458      return False
2459   
2460   def get_type(self):
2461      cnt = self.get_count()
2462      if cnt == 0: return None
2463      basetype = self.selected_elements[0].get_type()
2464      if cnt == 1: return basetype
2465      basetype2 = self.selected_elements[1].get_type()
2466      if basetype == "node" and basetype2 == "node": return "nodes"           
2467      if basetype == "segment" and basetype2 == "segment": return "segments"           
2468      if basetype == "way" and basetype2 == "segment": return "way/segments"
2469      return None           
2470       
2471   def allowed_to_be_added(self,element):
2472      # node node node Screen 1
2473      # seg seg seg    Screen 2
2474      # way seg seg    Screen 3
2475       
2476      if element == None: return False
2477      if self.already_exists(element): return False
2478     
2479      cnt = self.get_count()
2480      if cnt == 0: return True
2481      basetype = self.selected_elements[0].get_type()
2482      eletype = element.get_type()
2483     
2484      if cnt == 1:
2485         if eletype == "way": return False
2486         if basetype == eletype: return True
2487         if basetype == "way" and eletype == "segment": return True
2488         return False
2489       
2490      basetype2 = self.selected_elements[1].get_type()
2491      if eletype == basetype2: return True
2492      return False
2493           
2494   def set_element(self,element,editable = False, add = False):
2495      if add:
2496         if not self.allowed_to_be_added(element): return
2497      else:
2498         self.clear()
2499
2500      self.selected_elements.append(element)
2501      if self.get_count()>1: editable = False
2502
2503      # set info
2504      cnt = len(self.selected_elements)
2505      tp = self.get_type()
2506      assert tp, "unknow combination of elements"
2507     
2508      txt = element.get_short_info()
2509      if cnt > 1:
2510         txt = "(%s) - last: %s" % (len(self.selected_elements),txt)
2511         
2512      self.selected_item_textentry.set_text(txt)
2513      self.tageditor.set_tags(element.data)
2514      if cnt > 1:
2515         txt = ""
2516         for ele in self.selected_elements:
2517            txt += ele.get_short_info() + "\n"
2518      else:
2519         txt = element.get_long_info()
2520      self.osmeditor.print_to_infopane(txt)
2521
2522      if editable != None: self.set_editable(editable)
2523      element.draw(self.osmeditor.drawarea,selected = True, box = None, refresh = True)
2524     
2525      self.osmeditor.toolbarhandler.set_tb_icons(self.get_type())
2526   
2527   def set_editable(self,editable):
2528      if editable == None: return
2529      self.editable = editable
2530      self.editable = editable
2531      self.tageditor.set_editable(editable)
2532   
2533   def draw_selected(self,box,refresh):
2534      for ele in self.selected_elements:
2535         ele.draw(self.osmeditor.drawarea, box = box, selected = True)
2536     
2537   def mode_change(self,id):
2538      self.set_editable(id==1 and self.get_count() == 1)
2539     
2540     
2541########################## CLASS selectable_element #########################
2542
2543class selectable_element:
2544   def __init__(self,data):
2545      self.data = data
2546      self.id = self.get_id()
2547
2548   def get_type(self):
2549      return ""
2550     
2551   def get_id(self):
2552      return int(self.get_attribute("id"))
2553     
2554   def get_attribute(self,attr):
2555      if self.data == None: return None
2556      return self.data.getAttribute(attr)
2557         
2558   def get_short_info(self):
2559      return ""
2560     
2561   def get_long_info(self):
2562      return ""
2563   
2564   def get_osm_info(self):
2565      return None
2566     
2567   def draw(self,drawarea,selected,box,refresh=True): # box = None -> entire screen, not used yet anyway
2568      # something
2569      if refresh:                 # or refresh by the related drawarea function
2570         drawarea.refresh()
2571     
2572
2573
2574class selected_node(selectable_element):
2575   def get_type(self):
2576      return "node"
2577     
2578   def get_short_info(self):
2579      return "Node %s" % (self.id,)
2580     
2581   def get_long_info(self):
2582      lon = self.get_attribute("lon")
2583      lat = self.get_attribute("lat")
2584      ts = self.get_attribute("timestamp")
2585      return "Node ID: %s\nlon: %s\nlat: %s\ntime: %s" % (self.id,lon,lat,ts)
2586     
2587   def get_osm_info(self):
2588      return ("node/%s" % (self.id,),self.data)
2589
2590   def draw(self,drawarea,selected,box,refresh=False):
2591      color = drawarea.black
2592      if selected: color = drawarea.green
2593      drawarea.drawnode(self.id,color,refresh)
2594           
2595class selected_segment(selectable_element):
2596   def get_type(self):
2597      return "segment"
2598   
2599   def get_short_info(self):
2600      return _("Segment %s") % (self.id,)
2601
2602   def get_long_info(self):
2603      fromid = self.get_attribute("from")
2604      toid = self.get_attribute("to")
2605
2606      return _("Segment ID: %s\nfrom node #: %s\nto node #: %s") % (self.id, fromid, toid)
2607
2608   def get_osm_info(self):
2609      return ("segment/%s" % (self.id,),self.data)
2610
2611   def draw(self,drawarea,selected,box,refresh=False):
2612      color = drawarea.black
2613      if selected: color = drawarea.green
2614      drawarea.drawsegment(self.id,color,refresh)
2615
2616class selected_way(selectable_element):
2617   def get_type(self):
2618      return "way"
2619     
2620   def get_short_info(self):
2621      return _("Way %s") % (self.id,)
2622
2623   def get_long_info(self):
2624      ts = self.get_attribute("timestamp")
2625     
2626      text = _("Way ID %s\n%s\n\n") % (self.id,ts)
2627     
2628      for ele in self.data.getElementsByTagName("seg"):
2629         segid = ele.getAttribute("id")
2630         text += str(segid) + "\n"
2631
2632      return text
2633
2634   def get_osm_info(self):
2635      return ("way/%s" % (self.id,),self.data)
2636
2637   def draw(self,drawarea,selected,box,refresh=False):
2638      color = drawarea.brown
2639      if selected: color = drawarea.green
2640      drawarea.drawway(self.id,color = color,refresh = refresh, linewidth = 4)
2641
2642########################## CLASS BOOKMARKS_HANDLER #########################
2643class bookmarks_handler:
2644   def __init__(self,editor,bookmarks):
2645      self.osmeditor = editor
2646      self.bookmarks = bookmarks
2647      self.config    = self.osmeditor.config
2648     
2649      self.bookmark_window = self.osmeditor.xml.get_widget('bookmark_window')
2650      self.bookmark_name = self.osmeditor.xml.get_widget('bookmark_name')
2651      self.bookmark_description = self.osmeditor.xml.get_widget('bookmark_description')     
2652      self.bookmark_info = self.osmeditor.xml.get_widget('bookmark_info')
2653      self.bookmark_submenu = self.osmeditor.xml.get_widget('bookmark_select')
2654      self.bookmark_list = self.osmeditor.xml.get_widget('bookmark_list')
2655
2656      cell = gtk.CellRendererText()
2657      col = gtk.TreeViewColumn("bookmark", cell)
2658      col.set_cell_data_func(cell,self.bookmark_cell_to_str)
2659      self.bookmark_list.append_column(col)
2660      self.bookmark_list.set_property("reorderable",True)
2661
2662      self.update_bookmark_submenu_and_list()
2663     
2664      self.currentlon = None
2665      self.currentlat = None
2666      self.currentscale = None
2667     
2668   def bookmark_cell_to_str(self,column, cell, model, iter):
2669      bm = model.get_value(iter, 0)
2670      if bm:
2671         res = getChildValue(bm,"name")
2672      else:
2673         res = ""
2674
2675      cell.set_property('text', res)
2676     
2677   def add_bookmark(self,name,desc,lon,lat,scale):
2678      bookmark = self.config.doc.createElement("bookmark")
2679      appendNodeAndText(self.config.doc,bookmark,"name",name)
2680      appendNodeAndText(self.config.doc,bookmark,"description",desc)
2681      appendNodeAndText(self.config.doc,bookmark,"lon",lon)
2682      appendNodeAndText(self.config.doc,bookmark,"lat",lat)
2683      appendNodeAndText(self.config.doc,bookmark,"scale",scale)
2684      bm = self.bookmarks.appendChild(bookmark)
2685
2686      # editor menu
2687      self.update_bookmark_submenu_and_list()
2688      self.select_bookmark(bookmark)
2689      # edit it
2690      self.show_bookmark_dialog(bookmark)
2691      return bookmark
2692     
2693   def update_bookmark_submenu_and_list(self):
2694      bml = self.bookmarks.getElementsByTagName("bookmark")
2695
2696      menu = gtk.Menu()     
2697      store = gtk.ListStore(object)
2698      for bm in bml:
2699         piter = store.append([bm])
2700         menu_items = gtk.MenuItem(getChildValue(bm,"name"))
2701         menu_items.connect("activate", self.osmeditor.on_bookmark_selected, bm)   # data of hook: pointer to bookmark
2702         menu.append(menu_items)
2703         menu_items.show()
2704         
2705      self.bookmark_list.set_model(store)
2706      self.bookmark_submenu.set_submenu(menu)
2707         
2708   def set_info_field(self):
2709      if self.currentlon == None:
2710         info = ""
2711      else:
2712         info = _("Longitude (E): %s\nLatitude (N): %s\nScale: %s") % (self.currentlon,self.currentlat,self.currentscale)         
2713      self.bookmark_info.get_buffer().set_text(info)
2714   
2715   def bookmark_to_fields(self,bookmark):
2716      if bookmark == None:
2717         name = ""
2718         desc = ""
2719         info = ""
2720         self.currentlon = None
2721         self.currentlat = None
2722         self.currentscale = None
2723      else:
2724         name = getChildValue(bookmark,"name")
2725         desc = getChildValue(bookmark,"description")
2726         self.currentlon = getChildValue(bookmark,"lon")
2727         self.currentlat = getChildValue(bookmark,"lat")
2728         self.currentscale = getChildValue(bookmark,"scale")
2729     
2730      if name == None: name = ""
2731      if desc == None: desc = ""
2732      self.bookmark_name.set_text(name)
2733      self.bookmark_description.get_buffer().set_text(desc)
2734      self.set_info_field()
2735     
2736   def show_bookmark_dialog(self,bookmark):
2737      self.bookmark_to_fields(bookmark)
2738      self.select_bookmark(bookmark)
2739      self.bookmark_window.show_all()
2740   
2741   def select_bookmark(self,bookmark):
2742      selection = self.bookmark_list.get_selection()
2743      selection.unselect_all()
2744      if bookmark == None: return
2745      # search the bookmark in the listview store
2746      store = self.bookmark_list.get_model()
2747      iter = store.get_iter_first()
2748      while iter:
2749         if store.get_value(iter,0) == bookmark:
2750             selection.select_iter(iter)
2751             (path,column) = self.bookmark_list.get_cursor()
2752
2753             break
2754         iter = store.iter_next(iter)
2755   
2756   def show_edit(self):
2757      self.show_bookmark_dialog(None)
2758   
2759   def get_selected_bookmark(self):
2760      selection = self.bookmark_list.get_selection()
2761      if selection == None: return None
2762      (model, iter) = selection.get_selected()
2763      return model.get(iter,0)[0]
2764   
2765   def cursor_changed(self):
2766      bm = self.get_selected_bookmark()
2767      self.current_bookmark = bm
2768      self.bookmark_to_fields(bm)
2769     
2770   def button_remove(self): 
2771      bm = self.get_selected_bookmark()
2772      if bm == None: return
2773      self.bookmarks.removeChild(bm)
2774      self.current_bookmark = None
2775      self.update_bookmark_submenu_and_list()
2776
2777   def button_apply(self):
2778      bm = self.get_selected_bookmark()
2779      if bm == None: return
2780      buf = self.bookmark_description.get_buffer()
2781      name = self.bookmark_name.get_text()
2782      desc = buf.get_text(buf.get_start_iter(),buf.get_end_iter())
2783      setChildValue(self.config.doc,bm,"name",name)
2784      setChildValue(self.config.doc,bm,"description",desc)
2785      setChildValue(self.config.doc,bm,"lon",str(self.currentlon))
2786      setChildValue(self.config.doc,bm,"lat",str(self.currentlat))
2787      setChildValue(self.config.doc,bm,"scale",str(self.currentscale))
2788
2789      self.update_bookmark_submenu_and_list()
2790      self.bookmark_window.hide()
2791      return False
2792     
2793   def button_current(self):
2794      bm = self.get_selected_bookmark()
2795      if bm == None: return
2796      co = self.osmeditor.drawarea.get_screenbookmark()
2797      if co:
2798         self.currentlon, self.currentlat, self.currentscale = co
2799         self.set_info_field()
2800         
2801      return False 
2802           
2803   def drag_end(self):
2804      # order in treeview has changed
2805      # building new subtree to reflect changes
2806     
2807      childlist = self.bookmarks.getElementsByTagName("bookmark")
2808      for child in childlist: self.bookmarks.removeChild(child)
2809           
2810      store = self.bookmark_list.get_model()
2811      iter = store.get_iter_first()
2812      while iter: 
2813         self.bookmarks.appendChild(store.get_value(iter,0))
2814         iter = store.iter_next(iter)
2815      self.update_bookmark_submenu_and_list()
2816   
2817####################################################################
2818
2819class picture_viewer_handler:
2820   def __init__(self,window,drawarea,config,infolabel):
2821      self.drawarea = drawarea
2822      self.draw_width, self.draw_height = self.drawarea.size_request()
2823      self.win = window
2824      self.unscaled_picture = None
2825      self.scale = 1.0
2826      self.draw_pixmap = None   
2827      self.filename = None
2828      self.filesize = None     
2829      self.exifdate = None
2830      self.infolabel = infolabel
2831      self.main_config = config
2832      w = config.root.getElementsByTagName("geotagpics")
2833      if w:
2834         self.my_config = w[0]
2835      else:
2836         self.my_config = config.doc.createElement("geotagpics")
2837         config.root.appendChild(self.my_config)
2838     
2839   def get_picture_data(self,filename,filesize):
2840      wl = self.my_config.getElementsByTagName("pictureinfo")
2841      for w in wl:
2842         fnm = w.getAttribute("filename")
2843         fsz = int(w.getAttribute("filesize"))
2844         if fnm == filename and fsz == filesize:
2845            try:
2846               scale = float(getChildValue(w,"scale"))
2847               offsetx = int(getChildValue(w,"offsetx"))
2848               offsety = int(getChildValue(w,"offsety"))
2849            except ValueError:
2850               return None
2851            return (offsetx, offsety, scale)
2852      return None 
2853 
2854   def put_picture_data(self,filename,filesize,offsetx,offsety,scale):
2855      wl = self.my_config.getElementsByTagName("pictureinfo")
2856      found = None
2857      for w in wl:
2858         fnm = w.getAttribute("filename")
2859         fsz = int(w.getAttribute("filesize"))
2860         if fnm == filename and fsz == filesize:
2861            found = w
2862            break
2863      if found == None:
2864         found = self.main_config.doc.createElement("pictureinfo")
2865         found.setAttribute("filename",filename)
2866         found.setAttribute("filesize",str(filesize))
2867         self.my_config.appendChild(found)
2868      setChildValue(self.main_config.doc,found,"scale",str(scale))
2869      setChildValue(self.main_config.doc,found,"offsetx",str(offsetx))
2870      setChildValue(self.main_config.doc,found,"offsety",str(offsety))
2871           
2872   def scale_picture(self):
2873      pw = int(self.pic_width * self.scale)
2874      ph = int(self.pic_height * self.scale)
2875       
2876      self.scale_picbuf = self.unscaled_picture.scale_simple(pw,ph,gtk.gdk.INTERP_BILINEAR)
2877      self.draw_buffer()
2878     
2879   def draw_buffer(self):
2880      self.put_picture_data(self.filename,self.filesize,self.offsetX,self.offsetY,self.scale)
2881      info = _("%s - scale %.4f") % (self.filename,self.scale)
2882      if self.exifdate:
2883         info += "\n" + self.exifdate
2884      self.infolabel.set_text(info)
2885      pw = int(self.pic_width * self.scale)
2886      ph = int(self.pic_height * self.scale)
2887      w = min(pw,self.draw_width)
2888      h = min(ph,self.draw_height)
2889      sx = 0
2890      sy = 0
2891      dx = self.offsetX
2892      dy = self.offsetY
2893      if dx < 0:
2894         sx = -dx
2895         w = pw + dx
2896         dx = 0
2897      if dy < 0:
2898         sy = -dy
2899         h = ph + dy
2900         dy = 0
2901
2902#      print sx,sy,dx,dy,w,h,self.draw_width,self.draw_height
2903
2904      self.draw_pixmap.draw_rectangle(self.drawarea.get_style().white_gc,True, 0, 0, self.draw_width, self.draw_height)
2905      self.draw_pixmap.draw_pixbuf(None,self.scale_picbuf,sx,sy,dx,dy,w,h)
2906      self.drawarea.window.draw_drawable(self.drawarea.get_style().fg_gc[gtk.STATE_NORMAL],self.draw_pixmap, 0,0,0,0,self.draw_width,self.draw_height)   
2907     
2908      self.last_pw = pw
2909      self.last_ph = ph
2910   
2911   def load_image(self, fnm):
2912      self.win.show()
2913     
2914      self.unscaled_picture = gtk.gdk.pixbuf_new_from_file(fnm)
2915      self.filename = os.path.basename(fnm)
2916      self.filesize = filesize(fnm)
2917      self.exifdate = exifdate(fnm)
2918      self.pic_width = self.unscaled_picture.get_width()
2919      self.pic_height = self.unscaled_picture.get_height()
2920      self.last_pw = None
2921      self.last_ph = None
2922      dt = self.get_picture_data(self.filename,self.filesize)
2923      if dt:
2924         self.offsetX = dt[0]
2925         self.offsetY = dt[1]
2926         self.scale = dt[2]
2927         self.scale_picture()
2928         self.draw_buffer()
2929      else:
2930         self.fit()
2931      self.win.window.raise_()
2932     
2933   def exposed(self,widget,event):
2934      if self.draw_pixmap:
2935         x , y, width, height = event.area
2936         self.drawarea.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL],self.draw_pixmap, x, y, x, y, width, height)
2937         
2938   def configure(self,widget,event):
2939      x, y, self.draw_width, self.draw_height = self.drawarea.get_allocation()
2940      self.draw_pixmap = gtk.gdk.Pixmap(widget.window, self.draw_width, self.draw_height)
2941      self.draw_pixmap.draw_rectangle(widget.get_style().white_gc,True, 0, 0, self.draw_width, self.draw_height)
2942      self.scale_picbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,self.draw_width,self.draw_height)
2943      if self.filename:          # only if picture is already loaded
2944         self.scale_picture()
2945         self.draw_buffer()
2946
2947   def rescale(self,fact):
2948      self.scale *= fact
2949      nw = int(self.pic_width * self.scale)
2950      nh = int(self.pic_height * self.scale)
2951     
2952      if self.last_pw:
2953         h2 = self.draw_width / 2
2954         self.offsetX = int(h2 + ((self.offsetX - h2) * nw / self.last_pw))
2955
2956      if self.last_ph:
2957         h2 = self.draw_height / 2
2958         self.offsetY = int(h2 + ((self.offsetY - h2)*nh / self.last_ph))
2959
2960      self.scale_picture()
2961   
2962   def zoom_in(self):    self.rescale(1.5)
2963   def zoom_out(self):   self.rescale(1 / 1.5)
2964     
2965   def fit(self):
2966      assert self.pic_width != 0
2967      assert self.pic_height != 0
2968      self.scale = min(float(self.draw_width) / float(self.pic_width), float(self.draw_height) / float(self.pic_height))
2969      pw = int(self.pic_width * self.scale)
2970      ph = int(self.pic_height * self.scale)
2971
2972      self.offsetX = int((self.draw_width - pw) / 2)
2973      self.offsetY = int((self.draw_height - ph) / 2)
2974      self.last_pw = pw
2975      self.last_ph = ph
2976      self.scale_picture()
2977
2978   def mouse(self,pressed,widget,event):
2979      if event.button == 1:
2980         if pressed:
2981            self.pressX = event.x
2982            self.pressY = event.y           
2983         else:
2984            dx = int(event.x - self.pressX)
2985            dy = int(event.y - self.pressY)
2986           
2987            if dx != 0 or dy != 0:
2988               self.offsetX += dx
2989               self.offsetY += dy
2990               self.draw_buffer()
2991
2992########################## CLASS PREFERENCES_HANDLER #########################               
2993#
2994# Descripiton of the template structure
2995#
2996# Position     
2997# ..._WIDGETNAME   Name of the widget(s) in the GUI (e.g. one name for TextEntry,
2998#                      multiple names for Radio buttons)
2999# ..._DATA_OUT     when data is retrieved from the widget, this function will be
3000#                      used to convert them back, if an exception occurs the data will not
3001#                      be written back (e.g. float, int)
3002# ..._DATA_IN      data source type, see PREF_xxx
3003# ..._DATA         the actual data source, a dict or a pointer to an XML tree
3004# ..._DATA_PARAM1  parameter for data source, e.g. name of key in dict, name of XML node, etc.
3005PREF_TEMPL_WIDGETNAME = 0
3006PREF_TEMPL_DATA_OUT = 1
3007PREF_TEMPL_DATA_IN = 2
3008PREF_TEMPL_DATA = 3
3009PREF_TEMPL_DATA_PARAM1 = 4
3010
3011PREF_DICT = 1
3012
3013class preferences_handler:
3014   def __init__(self,win,xml,pageswitcher,pageswitcherpane,tabs,data_transfer_template):
3015      self.window = win
3016      self.xml = xml       # The widget tree
3017      self.data_transfer_template = data_transfer_template
3018      self.page_switcher = pageswitcher
3019      self.page_switcher_pane = pageswitcherpane
3020      self.tabs = tabs
3021   
3022      # Pageswitcher-ListStore
3023      liststore = gtk.ListStore(str)
3024      piter = liststore.append([_('OSM server')])
3025      piter = liststore.append([_('Display settings')])
3026
3027      cell = gtk.CellRendererText() 
3028      column = gtk.TreeViewColumn('section',cell,text = 0) 
3029      self.page_switcher.append_column(column)
3030      self.page_switcher.set_model(liststore)
3031      self.switch_to_page(0)
3032     
3033   def switcher_cursor_changed(self,widget):
3034      selection = widget.get_selection()
3035      if selection == None: return None
3036      (model, iter) = selection.get_selected()
3037      path = model.get_path(iter)
3038      page = path[0]
3039      self.tabs.set_current_page(page)
3040     
3041   def switch_to_page(self,num):
3042      store = self.page_switcher.get_model()
3043      selection = self.page_switcher.get_selection()
3044      if selection:
3045         selection.unselect_all()
3046      cnt = 0
3047      iter = store.get_iter_first()
3048      while iter:
3049         if cnt == num: 
3050            selection.select_iter(iter)
3051            self.tabs.set_current_page(num)
3052            break
3053         cnt += 1
3054         iter = store.iter_next(iter)
3055
3056   def data_to_widget(self):
3057      for dt in self.data_transfer_template:
3058         if type(dt[PREF_TEMPL_WIDGETNAME]) == tuple:
3059            wid = self.xml.get_widget(dt[PREF_TEMPL_WIDGETNAME][0])
3060         else:
3061            wid = self.xml.get_widget(dt[PREF_TEMPL_WIDGETNAME])
3062         
3063         assert wid != None,"pref widget '%s' does not exist" % (dt[PREF_TEMPL_WIDGETNAME],)
3064         if dt[PREF_TEMPL_DATA_IN] == PREF_DICT:
3065            data = dt[PREF_TEMPL_DATA][dt[PREF_TEMPL_DATA_PARAM1]]
3066         
3067         if type(wid) == gtk.Entry:
3068            wid.set_text(str(data))
3069           
3070         if type(wid) == gtk.RadioButton:
3071            wid = self.xml.get_widget(dt[PREF_TEMPL_WIDGETNAME][data])
3072            assert wid != None,"pref widget '%s' does not exist" % (dt[PREF_TEMPL_WIDGETNAME][data],)
3073            wid.set_active(True)
3074         
3075   def widget_to_data(self,forreal = False):
3076      for dt in self.data_transfer_template:
3077         dcf = dt[PREF_TEMPL_DATA_OUT]
3078         if type(dt[PREF_TEMPL_WIDGETNAME]) == tuple:
3079            self.curr_widget_name = dt[PREF_TEMPL_WIDGETNAME][0]
3080            wid = self.xml.get_widget(self.curr_widget_name)
3081         else:
3082            self.curr_widget_name = dt[PREF_TEMPL_WIDGETNAME]
3083            wid = self.xml.get_widget(self.curr_widget_name)
3084
3085         try:
3086            if type(wid) == gtk.Entry:
3087               data = dcf(wid.get_text())
3088           
3089            if type(wid) == gtk.RadioButton:
3090               res = None
3091               cnt = 0
3092               for wn in dt[PREF_TEMPL_WIDGETNAME]:
3093                  if self.xml.get_widget(wn).get_active():
3094                     res = cnt
3095                     break
3096                  cnt += 1
3097               if res != None:
3098                 data = dcf(res)
3099         except:
3100            raise ValueError,_("Invalid value in field %s") % (self.curr_widget_name,)
3101           
3102         if forreal:
3103            if dt[PREF_TEMPL_DATA_IN] == PREF_DICT:
3104               dt[PREF_TEMPL_DATA][dt[PREF_TEMPL_DATA_PARAM1]] = data
3105   
3106   def run(self,showonly = None):
3107      self.data_to_widget()
3108      if showonly == None:
3109         self.page_switcher_pane.show()
3110      else:
3111         self.switch_to_page(showonly)   
3112         self.page_switcher_pane.hide()
3113
3114      again = True
3115      while again:     
3116         response = self.window.run()
3117         if response == gtk.RESPONSE_OK:
3118            # store and close
3119            try:
3120               self.widget_to_data(forreal = False)
3121            except ValueError,info:
3122               ok = simple_dialog(gtk.MESSAGE_ERROR,str(info),gtk.BUTTONS_CLOSE,modal = True)
3123            else:
3124               self.widget_to_data(forreal = True)
3125               again = False
3126         else:
3127            again = False     
3128      self.window.hide()
3129      return response == gtk.RESPONSE_OK
3130   
3131         
3132########################## CLASS OSMEDITOR #########################
3133   
3134class osmeditor:
3135   ######################
3136   # Glade predefinied function(s)
3137   
3138   # If a window is closed, the ressources are freed and the window can not be shown again.
3139   # If the delete_event is set to gtk_widget_hide, this function is called, which only hides the
3140   # window, and eats the event, so that the ressources stay intact.
3141   def gtk_widget_hide(self, widget, data=None):
3142      widget.hide()
3143      return True            # eat event
3144     
3145   def __init__(self):
3146      self.bookmarks_handler = None
3147
3148      # Read GUI
3149      self.xml = gtk.glade.XML('pyosmeditor.glade', domain = APP)
3150      # connect signal routines to class functions with the same name
3151      self.xml.signal_autoconnect(self)
3152
3153     
3154      # read config file and get values
3155      self.config = simpleconfigfile(CONFIGFILENAME, \
3156                                     "osmedit",                        \
3157                                     "1",                              \
3158                                     
3159                    elements =   {"username": str, "password": str, "passsave": int,  \
3160                                  "savecurrentdisplaycoord": int,
3161                                  "currentdisplaycoord": str,
3162                                  "displaywidth": int, "displayheight": int, \
3163                                  "arrowthreshold": int, \
3164                                  "lastosmdir": str, \
3165                                  "lastgpxdir": str, \
3166                                  "geotagdir": str, \
3167                                  "osmserver": str, \
3168                                  "gpxwarnthreshold": float
3169                                 }, \
3170                    defaults =   {"username": "", "password": "", "passsave": 0,
3171                                  "savecurrentdisplaycoord": 1,
3172                                  "currentdisplaycoord": None, \
3173                                  "displaywidth": 500, "displayheight": 300, \
3174                                  "arrowthreshold": 20000, \
3175                                  "lastosmdir": None, \
3176                                  "lastgpxdir": None, \
3177                                  "geotagdir": "./geotags", \
3178                                  "osmserver": "www.openstreetmap.org", \
3179                                  "gpxwarnthreshold": 2.0
3180                                 })
3181
3182      # Get info that is not stored in the dict
3183      self.bookmarks = self.config.root.getElementsByTagName("bookmarks")
3184      # create empty sub-tree if no bookmarks       
3185      if self.bookmarks == []:
3186         self.bookmarks = self.config.doc.createElement("bookmarks")
3187         self.config.root.appendChild(self.bookmarks)
3188      else:
3189         self.bookmarks = self.bookmarks[0]
3190       
3191
3192      self.bookmarks_handler = bookmarks_handler(self, self.bookmarks)
3193
3194
3195      self.TBSM_NONE = 0
3196      self.TBSM_POSSIBLE_SCREEN_MOVE = 1
3197      self.TBSM_POSSIBLE_POINT_MOVE = 2
3198      self.TBSM_CREATING_NODES = 3
3199      self.TBSM_CONFIRMED_POINT_MOVE = 4
3200     
3201      self.preferences_handler = preferences_handler(self.xml.get_widget("preferences_dialog"), self.xml, \
3202         self.xml.get_widget("pref_page_switcher"),self.xml.get_widget("pref_page_switcher_pane"),self.xml.get_widget("preferences_pane"),
3203         [ ("pref_server",str,PREF_DICT,self.config.data,"osmserver"), \
3204           ("pref_password",str,PREF_DICT,self.config.data,"password"), \
3205           ("pref_email",str,PREF_DICT,self.config.data,"username"), \
3206           (("pref_store_option","pref_store_password_disk"),int,PREF_DICT,self.config.data,"passsave"), \
3207           ("pref_arrow_threshold",int,PREF_DICT,self.config.data,"arrowthreshold"), \
3208           ("pref_gpx_warn",float,PREF_DICT,self.config.data,"gpxwarnthreshold"), \
3209           
3210         ])
3211           
3212      # data now in self.config.data dictionary
3213
3214      self.drawarea = drawarea(self.xml.get_widget('drawingarea'))
3215      self.drawarea.set_arrow_threshold(self.config.data["arrowthreshold"])
3216      self.mainwindow = self.xml.get_widget('pyosmeditor')
3217      self.mainwindow.resize(self.config.data["displaywidth"],self.config.data["displayheight"])
3218     
3219      self.infopane = self.xml.get_widget('infopane')
3220      self.passworddialog = self.xml.get_widget('passworddialog')
3221      self.emailentry = self.xml.get_widget('emailentry')
3222      self.passwordentry = self.xml.get_widget('passwordentry')
3223      self.saveoption = self.xml.get_widget('pw_duration')
3224      self.pwsavebuttons = [self.xml.get_widget('pw_duration'), self.xml.get_widget('pw_save')]
3225      # set version number in about dialog
3226      self.aboutdialog = self.xml.get_widget('aboutdialog')
3227      self.aboutdialog.set_version(pyosmeditorversion)
3228
3229      self.view_geotag = self.xml.get_widget("view_geotag")
3230      self.geo_tag_enabled = False      # selection button
3231      # enable geotag options if exiflib is set
3232      if exiflib:
3233         enable_widgets([self.xml.get_widget('geo_scan_directory')])
3234           
3235      # Edit Mode selection: 0 = View, 1 = Edit
3236      self.tb_modes = [self.xml.get_widget('tb_viewmode'),self.xml.get_widget('tb_edit')]
3237      self.tb_mode  = 0
3238      self.tb_submode = 0
3239      self.tb_modes[0].set_active(True)
3240     
3241      self.livemode = False
3242     
3243      self.statusbar = statusbarhandler(self.xml.get_widget('statusbar'))
3244      self.picture_viewer_handler = picture_viewer_handler(self.xml.get_widget("image_window"),self.xml.get_widget("geo_image"),self.config,self.xml.get_widget("geo_info"))
3245     
3246      self.select_way_dialog_completion()
3247     
3248      self.tageditor = tageditor(self.xml.get_widget('treeview'),self.xml.get_widget('tagadd'),self.xml.get_widget('tagdel'),self.xml.get_widget('tagapply'),False)
3249      self.selected_elements_handler = selected_elements_handler(self)
3250      self.toolbarhandler = toolbarhandler(self)
3251     
3252      # menu options to be enabled ...
3253      # ... when a local track is being loaded
3254      self.localgpxdatamenuoptions = (self.xml.get_widget('fit_track'),self.xml.get_widget('unselect_gpx_nodes'), \
3255                                      self.xml.get_widget('remove_selected_gpx_nodes'),self.xml.get_widget('view_local_trace'), \
3256                                      self.xml.get_widget('remove_unselected_gpx_nodes'),self.xml.get_widget('save_gpx_data'))
3257      # ... when remote tracks are being loaded
3258      self.remotegpxdatamenuoptions = (self.xml.get_widget('view_remote_trace'),)
3259                                     
3260      # ... when the screen coordinates are being set
3261      #  e.g. if the coords are set via the config file
3262      #    or set if a local track is fit to the screen
3263      self.validscreencoordsoptions = (self.xml.get_widget('drawingarea'),
3264                                       self.xml.get_widget('zoomin'),self.xml.get_widget('zoomout'), \
3265                                       self.xml.get_widget('osmdata'),self.xml.get_widget('get_osm_tracks'))
3266      # ... when OSM data are present (either downloaded from the internet or loaded as file)
3267      self.osmdataloaded            = (self.xml.get_widget('saveosmdata'),self.xml.get_widget('viewways'))
3268
3269      self.osmapi = osmapi(self.config.data["osmserver"],self.passwordrequest,self.osm_error_display)
3270       
3271
3272      self.osmdata = osmdata(self.drawarea)             # data section == None by default
3273      self.localgpxdata = localgpxtrack(self.drawarea,self.config.data["gpxwarnthreshold"])
3274      self.remotegpxdata = remotegpxtrack(self.drawarea,self.config.data["gpxwarnthreshold"])
3275      self.drawselected = drawselected(self.drawarea,self.selected_elements_handler)
3276      self.geotaghints = geotaghints(self.drawarea)
3277     
3278      # select selection
3279      self.select_selects = [self.xml.get_widget("sel_node"),self.xml.get_widget("sel_segment"),self.xml.get_widget("sel_way"),self.xml.get_widget("sel_geo")]
3280     
3281      self.selectionframe = selectionframe(self.drawarea)
3282
3283      self.drawarea.osmdata = self.osmdata              # to convert ID -> x/y
3284
3285      # drawable elements
3286      # also determines order in which elements are drawn
3287      self.infoelements = [self.remotegpxdata, self.geotaghints, self.localgpxdata, self.osmdata, self.drawselected]
3288
3289      try:
3290         coord = self.config.data["currentdisplaycoord"]
3291         if coord != None:
3292            coord = coord.split(",")
3293            self.drawarea.setscreencoords(float(coord[0]),float(coord[1]),float(coord[2]),float(coord[3]))
3294            self.enable_screencoordset()
3295      except:   # set an arbitrary box
3296         self.drawarea.setscreencoords(6.929844,50.932309,6.942344,50.941684)
3297                 
3298      self.recalcall()
3299      self.drawall()
3300
3301   def get_select_selection(self):
3302      res = []
3303      for wid in self.select_selects:
3304         res.append(wid.get_active())
3305      return res
3306     
3307
3308
3309   def osm_error_display(self,status,reason):
3310      msg = _("<b>Server error %d</b>\n%s") % (status,reason)
3311      ok = simple_dialog(gtk.MESSAGE_ERROR,msg,gtk.BUTTONS_CLOSE,modal = True)
3312     
3313   ############
3314   # Select Way Dialog
3315   
3316   def select_way_dialog_completion(self):
3317      # the parts that glade could not do
3318      # to be executed once on start_up
3319      select_way_dialog_listview = self.xml.get_widget('select_way_dialog_listview')
3320      column = gtk.TreeViewColumn("way id")
3321      select_way_dialog_listview.append_column(column)
3322      renderer = gtk.CellRendererText()
3323      column.pack_start(renderer, True)
3324      column.add_attribute(renderer, 'text', 0)
3325     
3326   def select_way_dialog(self,id,waylist):
3327      # get widgets
3328      select_way_dialog = self.xml.get_widget('select_way_dialog')
3329      select_way_dialog_label = self.xml.get_widget('select_way_dialog_label')
3330      select_way_dialog_label.set_text(_("The selected segment %s\nis used in different ways.\nChoose one:") % (id,))
3331      select_way_dialog_ok_button = self.xml.get_widget('select_way_dialog_ok_button')
3332      select_way_dialog_ok_button.set_sensitive(False)
3333      select_way_dialog_listview = self.xml.get_widget('select_way_dialog_listview')
3334     
3335      # create list
3336      store = gtk.ListStore(str)
3337      for w in waylist:
3338         store.append([str(w)])
3339      select_way_dialog_listview.set_model(store)          # replaces glades empty store with ours
3340
3341
3342      response = select_way_dialog.run()
3343
3344      select_way_dialog.hide()
3345
3346      if response == gtk.RESPONSE_OK:   
3347         (path,column) = select_way_dialog_listview.get_cursor()
3348         if path == None: return None
3349         iter = store.get_iter(path)
3350         w = int(store.get_value(iter,0))
3351         return w
3352      else:
3353         return None
3354
3355   def on_select_way_dialog_listview_cursor_changed(self,widget,data = None):
3356      select_way_dialog_ok_button = self.xml.get_widget('select_way_dialog_ok_button')
3357      select_way_dialog_ok_button.set_sensitive(True)
3358
3359   def display_selected_item(self,tx):
3360      selecteditem = self.xml.get_widget('selected_item')
3361      selecteditem.set_text(tx)
3362
3363   def on_pref_page_switcher_cursor_changed(self,widget,data=None):
3364      self.preferences_handler.switcher_cursor_changed(widget)
3365
3366   ###########
3367
3368   def enable_screencoordset(self):
3369      enable_widgets(self.validscreencoordsoptions)
3370   
3371   def print_to_infopane(self,text):   
3372      if text == None: text = ""
3373      self.infopane.get_buffer().set_text(text)
3374      self.infopane.show_now()
3375     
3376   def drawall(self, drawbox = None, clearbox = None, refresh = True):
3377      self.drawarea.cleardrawingarea(box = clearbox)
3378     
3379      for element in self.infoelements:
3380        if element.isvisible():
3381           element.draw(box = drawbox, refresh = False)
3382
3383      if refresh:
3384         self.drawarea.refresh(box = clearbox)
3385
3386
3387   def recalcall(self):
3388     for element in self.infoelements:
3389        element.recalcdata()
3390           
3391   def passwordrequest(self,askalways = False):
3392      user = self.config.data["username"]
3393      if user == None: user = ""
3394      pw = self.config.data["password"]
3395      if pw == None: pw = ""
3396      save = self.config.data["passsave"]
3397     
3398      erg = None
3399      if user == "" or askalways:
3400         if self.preferences_handler.run(showonly = 0):
3401            user = self.config.data["username"]
3402            pw = self.config.data["password"]
3403            erg = (user,pw)       
3404      else:
3405         erg = (user,pw)
3406               
3407      return erg
3408
3409
3410   def showwaitdialog(self,info):
3411      self.waitdialog = simple_dialog(gtk.MESSAGE_INFO,info,buttons = None, modal = False)
3412      # wait until the widget is drawn
3413      process_pending_gtk_events()
3414     
3415   def destroywaitdialog(self):
3416      self.waitdialog.destroy()
3417
3418   def on_view_geotags_activate(self,widget):
3419      self.geotaghints.set_visible(widget.get_active())
3420      self.drawall()
3421
3422   def on_viewways_activate(self,widget):
3423      self.osmdata.set_ways_visible(widget.get_active(),self.drawall)
3424     
3425   def on_pyosmeditor_delete_event(self, widget, event, data=None):
3426      gtk.main_quit()
3427      return False
3428   
3429   def on_drawingarea_expose_event(self,widget, event):
3430      self.drawarea.exposed(widget,event)
3431      return False
3432   
3433   def on_drawingarea_configure_event(self,widget,event):
3434      self.drawarea.resizescreen()
3435      self.recalcall()
3436      self.drawall()
3437      return True                      ## why true?
3438     
3439   def on_geo_image_expose_event(self,widget,event):
3440      self.picture_viewer_handler.exposed(widget,event)    # load tags
3441      return False
3442
3443   def on_geo_image_configure_event(self,widget,event):
3444      self.picture_viewer_handler.configure(widget,event)
3445      return False
3446   
3447   def on_geo_zoom_in_clicked(self,widget):
3448      self.picture_viewer_handler.zoom_in()
3449
3450   def on_geo_zoom_out_clicked(self,widget):
3451      self.picture_viewer_handler.zoom_out()
3452 
3453   def on_geo_fit_clicked(self,widget):
3454      self.picture_viewer_handler.fit()   
3455
3456   def on_geo_image_button_press_event(self,widget, event):
3457      self.picture_viewer_handler.mouse(True,widget,event)
3458     
3459   def on_geo_image_button_release_event(self,widget, event):
3460      self.picture_viewer_handler.mouse(False,widget,event)
3461
3462 
3463     
3464   ##### MOUSE CLICKS + MOUSE MOVEMENT #########################################
3465   #
3466   ##############################################################################
3467   # Edit mode:
3468   #
3469   # clear area:         -> submode "move screen"
3470   # + ctrl              -> new node
3471   #
3472   # point:
3473   # without modifier    -> select single node, show info, -> submode "possible point move"
3474   # + shift             -> add to selection
3475   # + button "create segments" -> make segments (if multiple nodes are selected)
3476   #
3477   # segment:
3478   # without modifier    -> single segment, show info editable
3479   # + ctrl + same segment -> split segment
3480   # + shift
3481   #   if segment(s) are selected -> add to selection
3482   #   if way is selected         -> add to way
3483   # + button "create way" -> make way (single segment or multiple segments)
3484   #
3485   # way:
3486   # without modifier    -> single way, show info editable
3487   #
3488   # tb_submode:
3489   # 0 -> None
3490   # 1 -> possible screen move
3491   # 2 -> possible point move
3492   # 3 -> creating new nodes in progress
3493   # 4 -> confirmed point move
3494   # TBSM constants definied in __init__
3495   
3496   def on_drawingarea_button_press_event(self,widget, event):
3497      ctrl_key = (gtk.gdk.CONTROL_MASK & event.state) != 0
3498      shift_key = (gtk.gdk.SHIFT_MASK & event.state) != 0
3499     
3500      if not self.drawarea.screencoordsset: return True
3501     
3502      if event.button == 1:  # LMB
3503         clearsubmode = True                             # reset self.tb_submode, if not requested otherwise
3504
3505         sel_node, sel_seg, sel_way, sel_geo = self.get_select_selection()
3506         sel_geo = sel_geo and self.geo_tag_enabled
3507   
3508         # position is in event.x, event.y
3509         self.pressX = event.x
3510         self.pressY = event.y     
3511
3512         id = None
3513
3514         # did click hit a node ?
3515         # - don't look for nodes, if ctrl is pressed
3516         #   (in this case, we want to set a new node, points in the near vicinity would prevent that)
3517         if ctrl_key:
3518            id = None
3519         else:
3520            if sel_node:
3521               id = self.findnearestnode_xy(event.x,event.y)         
3522           
3523         if id != None:                     # A node was selected
3524            sele = selected_node(self.osmdata.get_node_pointer(id))
3525            if not ctrl_key and not shift_key:
3526               
3527               self.selected_elements_handler.set_element(sele, add = False,editable = (self.tb_mode == 1))
3528               if self.tb_mode == 1:
3529                  self.tb_submode = self.TBSM_POSSIBLE_POINT_MOVE
3530                  clearsubmode = False
3531                  # info needed for point move
3532                  self.selectedid = id
3533                  # calculate the box that must be refreshed
3534                  self.innerbox = self.osmdata.segments2boundingbox_xy(self.osmdata.findconnectingsegments(id))
3535                  px, py = self.osmdata.get_node_xy(id)
3536                  self.innerbox.put_point(px, py)
3537                  self.innerbox.grow(self.drawarea.noderadius, self.drawarea.noderadius, self.drawarea.noderadius, self.drawarea.noderadius)
3538            elif shift_key and not ctrl_key:
3539               if self.tb_mode == 1:                  # edit mode only
3540                  self.selected_elements_handler.set_element(sele,add = True, editable = True)
3541            else:
3542               pass
3543             
3544         # Did we hit a way?     
3545         else:
3546            if sel_way:
3547               id = self.findnearestway_xy(event.x,event.y)   
3548            if id != None:
3549               sele = selected_way(self.osmdata.get_way_pointer(id))
3550         
3551               if not ctrl_key and not shift_key:
3552                  self.selected_elements_handler.set_element(sele,add = False,editable = (self.tb_mode == 1))
3553
3554            # Did we hit a segment?
3555            else:
3556               if sel_seg:
3557                  id = self.findnearestsegment_xy(event.x,event.y)
3558               if id != None:                                          # A segment was selected
3559                  sele = selected_segment(self.osmdata.get_segment_pointer(id))
3560               
3561                  if not ctrl_key and not shift_key:
3562                     self.selected_elements_handler.set_element(sele,add = False,editable = self.tb_mode == 1)
3563                  elif ctrl_key and not shift_key:
3564                     if self.tb_mode == 1 and self.selected_elements_handler.get_type() == "segment" and self.selected_elements_handler.selected_elements[0].get_id() == id:
3565                        self.split_segment(id, event.x, event.y)
3566                       
3567                  elif shift_key and not ctrl_key:
3568                     if self.tb_mode == 1:
3569                        self.selected_elements_handler.set_element(sele,add = True, editable = True)       
3570
3571               # geohint?
3572               else:
3573                  if sel_geo:
3574                     id = self.geotaghints.find_geohint(event.x, event.y)
3575                  if id != None:
3576                     self.print_to_infopane(_("filename: ")+id)
3577                     self.picture_viewer_handler.load_image(id)
3578                     pass    # display picture
3579               
3580                     
3581                  # Nothing was hit:
3582                  else:
3583                                     
3584                     if not ctrl_key and not shift_key:
3585                        self.selected_elements_handler.clear()
3586                        self.tb_submode = self.TBSM_POSSIBLE_SCREEN_MOVE
3587                        clearsubmode = False
3588                        widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR))
3589                 
3590                     elif shift_key and not ctrl_key:  # same as last, but without unselecting elements
3591                        self.tb_submode = self.TBSM_POSSIBLE_SCREEN_MOVE
3592                        clearsubmode = False
3593                        widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR))
3594                                         
3595                     elif ctrl_key and not shift_key:
3596                        if self.tb_mode == 1:
3597                           if self.osmdata.data != None:
3598                              if self.tb_submode != self.TBSM_CREATING_NODES :         # first new node
3599                                 info = self.selected_elements_handler.get_info() # (len. type)
3600                                 if info != (1,"node"):                    # 1 existing node + new nodes
3601                                    self.selected_elements_handler.clear() # clear selected elements list
3602                             
3603                              id = self.create_new_node(event.x,event.y)   # create new node
3604                              if id != None:
3605                                 sele = selected_node(self.osmdata.get_node_pointer(id))       # add node to the list
3606                                 self.selected_elements_handler.set_element(sele,add = True,editable = False)
3607                              self.tb_submode = self.TBSM_CREATING_NODES        # don't clear the list next time
3608                              clearsubmode = False
3609                       
3610                           else:
3611                              simple_dialog(gtk.MESSAGE_ERROR,_("No OSM data\nLoad or download OSM data first"),gtk.BUTTONS_OK)
3612           
3613     
3614         if clearsubmode:
3615            self.tb_submode = self.TBSM_NONE
3616     
3617      if event.button == 3:
3618         self.selectionframe.framestart(event.x,event.y)
3619         
3620      return True         
3621
3622   def on_drawingarea_button_release_event(self,widget, event):
3623      mindistancetomovesquare = 100
3624
3625      if not self.drawarea.screencoordsset: return True
3626     
3627      if event.button == 1:
3628         dx = event.x - self.pressX
3629         dy = event.y - self.pressY
3630
3631         # drag point
3632         if self.tb_submode == self.TBSM_POSSIBLE_POINT_MOVE:
3633            self.tb_submode = self.TBSM_NONE         # drag end
3634           
3635         if self.tb_submode == self.TBSM_CONFIRMED_POINT_MOVE:   
3636            self.update_node_position(self.selectedid,dx,dy)                 
3637            self.tb_submode = self.TBSM_NONE         # drag end
3638               
3639         # drag screen
3640         elif self.tb_submode == self.TBSM_POSSIBLE_SCREEN_MOVE:
3641            widget.window.set_cursor(None)
3642            self.tb_submode = self.TBSM_NONE         # end drag
3643           
3644            # Move window only, if distance is over threshold
3645            if (dx * dx + dy * dy) > mindistancetomovesquare:
3646               new = self.drawarea.move_xy(dx, dy)
3647               if new != None:
3648                 self.drawarea.setscreencoords(new[0],new[1],new[2],new[3])
3649                 self.recalcall()
3650                 self.drawall()                 
3651     
3652      # right mouse button
3653      if event.button == 3:
3654        res = self.selectionframe.frameend()
3655        self.localgpxdata.select_nodes_xy(res)
3656        self.drawarea.damage(res[0],res[1],res[2],res[3])
3657        self.localgpxdata.draw(box = None,refresh = True)   # box = None: change may go beyond frame (1 selected node
3658                                                            # may cause selected 'segments' to be selected which may
3659                                                            # lay outside 'res'
3660      return True         
3661
3662   def on_drawingarea_motion_notify_event(self, widget, event):
3663      mindistancetomovepointsquare = 9
3664
3665      if not self.drawarea.screencoordsset: return True
3666     
3667      if event.is_hint:
3668         x, y, state = event.window.get_pointer()
3669       
3670         # update status bar
3671         lon, lat = self.drawarea.transform2lonlat((x,y))
3672         self.statusbar.put(_("lon %s, lat %s, x %s, y %s, f %s") % (lon,lat, x, y, self.drawarea.factlong))
3673         
3674         # LMB
3675         if (state & gtk.gdk.BUTTON1_MASK):
3676            dx = x - self.pressX
3677            dy = y - self.pressY
3678
3679            # drag point
3680            if self.tb_submode == self.TBSM_POSSIBLE_POINT_MOVE:
3681               if (dx * dx + dy * dy) > mindistancetomovepointsquare:
3682                  self.tb_submode = self.TBSM_CONFIRMED_POINT_MOVE
3683           
3684            if self.tb_submode == self.TBSM_CONFIRMED_POINT_MOVE:
3685               b = box_xy(self.innerbox)
3686               # enlarge with current point position (including radius to draw the point)
3687               b.put_point(event.x+self.drawarea.noderadius,event.y+self.drawarea.noderadius)
3688               b.put_point(event.x-self.drawarea.noderadius,event.y-self.drawarea.noderadius)
3689               # setXY point
3690               self.osmdata.nodeindex[self.selectedid][3] = int(event.x)
3691               self.osmdata.nodeindex[self.selectedid][4] = int(event.y)
3692
3693               c = box_xy(b)
3694               b.enlarge(1.25)
3695               self.drawall(drawbox = b.getbox(), clearbox = c.getbox(), refresh = False)
3696               self.drawarea.refresh(c.getbox())
3697         # RMB
3698         elif (state & gtk.gdk.BUTTON3_MASK):
3699            self.selectionframe.framemove(x,y)
3700           
3701      else:
3702         x = event.x
3703         y = event.y
3704         state = event.state
3705         
3706#         print "No hint",x,y,state
3707         if state & gtk.gdk.BUTTON1_MASK:
3708#            print "Button 1"
3709            pass
3710
3711         if state & gtk.gdk.BUTTON3_MASK:
3712#            print "Button 3"
3713            pass
3714      return True
3715
3716
3717   ########################################################################
3718
3719            # Update strategy (move point)
3720            # 1) bounding box around the moving node and all connected segments
3721            # 2) list of all segments that have one node in the bounding box
3722            # 3) clear the bounding box in the pixmap
3723            # 4) redraw listed nodes and segments in enlarged bounding box
3724            # 5) copy to drawarea
3725            # 6) once the move is finished, redraw the entire area
3726            # !) the coordinates of moving point are manipulated in the XY section of the node index
3727            #    before the big redraw, the XY coordinates have to be translated to lon/lat
3728   
3729   def update_node_position(self,nodeid,dx,dy):
3730      mindestancepointmovesquare = 1
3731               
3732      if (dx * dx + dy * dy) > mindestancepointmovesquare:
3733         # [3] and [4] have been used during mouse movement
3734         # recalculating values
3735         x, y = self.drawarea.transform2xy(self.osmdata.nodeindex[nodeid][0],self.osmdata.nodeindex[nodeid][1])
3736         lon, lat = self.drawarea.transform2lonlat((x+dx,y+dy))
3737         self.osmdata.update_node_position(nodeid,lon,lat)   
3738         self.drawall()
3739         if self.livemode:
3740            dia = simple_dialog(gtk.MESSAGE_INFO,_("Contacting OSM\nto update node position"),buttons = None, modal = False)
3741            id = self.osmapi.send_updated_data(self.osmdata.get_node_pointer(nodeid))
3742            dia.destroy()
3743
3744   def split_segment(self,click2_segment_id,x,y):
3745      if self.selected_elements_handler.get_count() != 1:
3746         return
3747         
3748      # get segment id of previously selected segment
3749      segid = self.selected_elements_handler.selected_elements[0].get_id()
3750      if segid != click2_segment_id: 
3751         return       # split point needs to be on the same segment
3752      lon, lat = self.drawarea.transform2lonlat((x,y))
3753
3754      if self.livemode:
3755         dia = simple_dialog(gtk.MESSAGE_INFO,_("Contacting OSM\nChecking dependencies"),buttons = None, modal = False)
3756         waycnt = self.osmapi.count_ways_of_segment(segid)
3757         areacnt = self.osmapi.count_areas_of_segment(segid)
3758         dia.destroy()
3759         
3760         if waycnt != 0 or areacnt != 0:
3761            ok = simple_dialog(gtk.MESSAGE_ERROR,_("This segment is part of a way or an area\nThis condition is not yet implemented"),buttons = gtk.BUTTONS_OK)
3762            return
3763
3764      new_node_id = self.create_new_node(x,y)
3765      if new_node_id == None: return None
3766
3767      # before:   from ----------> to
3768      # after:    from(old) ---new seg---> to [split point] from ---old seg--> to(old)
3769      old_seg_node  = self.osmdata.segmentindex[click2_segment_id][2]
3770      old_from_node = self.osmdata.segmentindex[click2_segment_id][0]
3771      old_to_node   = self.osmdata.segmentindex[click2_segment_id][1]
3772
3773      new_segment_id = self.create_new_segment(old_from_node,new_node_id)
3774      if new_segment_id == None: return None
3775     
3776      self.update_segment_from_to(click2_segment_id,new_node_id,old_to_node)
3777     
3778#         dia = simple_dialog(gtk.MESSAGE_INFO,"Contacting OSM\nto create segment",buttons = None, modal = False)
3779#            id = self.osmapi.create_new_segment(node1id,node2id)
3780#            dia.destroy()
3781#            if id == None:
3782#               ok = simple_dialog(gtk.MESSAGE_ERROR,"OSM error - segment",buttons = gtk.BUTTONS_OK)
3783#         else:
3784#            id = None
3785           
3786#         if id == None:
3787#            id = self.osmdata.get_next_own_id()
3788
3789#      new_node_id = self.osmdata.get_next_own_id()
3790#      new_seg_id = self.osmdata.get_next_own_id()
3791#      self.osmdata.split_segment(segid,new_node_id,new_seg_id,lon,lat)
3792      self.drawarea.drawnode(new_node_id)
3793      self.drawarea.drawsegment(new_segment_id)
3794   
3795   def extend_way(self,idlist):
3796      # extend way (first id) with segments on the OSM server and locally
3797      self.osmdata.add_segments_to_way(idlist[0],idlist[1:])
3798      n = self.osmdata.get_way_pointer(idlist[0])
3799      if self.livemode:
3800         dia = simple_dialog(gtk.MESSAGE_INFO,_("Contacting OSM\nto update way %s") % (idlist[0],),buttons = None, modal = False)
3801         ok = self.osmapi.send_updated_data(n)
3802         dia.destroy()
3803
3804      self.drawarea.drawway(idlist[0])
3805     
3806   def update_segment_from_to(self,id,fromid, toid):
3807      # update the from and to ID of an existing segment on the OSM server and locally
3808      n = self.osmdata.get_segment_pointer(id)
3809      # save old values
3810      f = n.getAttribute("from")
3811      t = n.getAttribute("to")
3812      n.setAttribute("from",str(fromid))
3813      n.setAttribute("to",str(toid))
3814      ok = True
3815      if self.livemode:
3816         ok = self.osmapi.send_updated_data(n)
3817      if not ok:
3818         n.setAttribute("from",f)
3819         n.setAttribute("to",t)
3820      self.osmdata.update_index(n)
3821      self.drawarea.drawsegment(id)
3822     
3823
3824   def create_new_node(self,x,y):
3825      # create new node on OSM and locally
3826      lon, lat = self.drawarea.transform2lonlat((x,y))
3827      if self.livemode:
3828         dia = simple_dialog(gtk.MESSAGE_INFO,_("Contacting OSM\nto create node"),buttons = None, modal = False)
3829         id = self.osmapi.create_new_node(lon,lat)
3830         dia.destroy()
3831         if id == None:
3832            ok = simple_dialog(gtk.MESSAGE_ERROR,_("OSM error - node"),buttons = gtk.BUTTONS_OK)
3833            return None
3834      else:
3835         id = None
3836           
3837      if id == None:
3838         id = self.osmdata.get_next_own_id()
3839      self.osmdata.create_new_node(lon,lat,id)
3840      self.drawarea.drawnode(id)
3841      return id
3842
3843   def create_new_segment(self,fromid,toid):
3844      # create new segment on OSM and locally
3845      if self.osmdata.segment_already_exists(fromid, toid):    # don't create already existing segments
3846         return None
3847         
3848      if self.livemode:
3849         dia = simple_dialog(gtk.MESSAGE_INFO,_("Contacting OSM\nto create segment"),buttons = None, modal = False)
3850         id = self.osmapi.create_new_segment(fromid, toid)
3851         dia.destroy()
3852         if id == None:
3853            ok = simple_dialog(gtk.MESSAGE_ERROR,_("OSM error - segment from node %s -> node %s") % [fromid,toid],buttons =gtk.BUTTONS_OK)
3854            return None
3855      else:
3856         id = self.osmdata.get_next_own_id()
3857
3858      self.osmdata.create_new_segment(fromid, toid, id)
3859      self.drawarea.drawsegment(id, arrowtype = 1)
3860      return id
3861     
3862   
3863   def on_tb_delete_clicked(self,widget):
3864      self.toolbarhandler.call_function(0)
3865
3866   def on_tb_var1_clicked(self,widget):
3867      self.toolbarhandler.call_function(1)
3868
3869   def on_tb_var2_clicked(self,widget):
3870      self.toolbarhandler.call_function(2)
3871     
3872
3873################################################# functions via toolbar
3874
3875   def tb_delete_element(self):
3876      # delete any kind of element, depending on what is selected
3877
3878      what = self.selected_elements_handler.selected_elements[0].get_osm_info()
3879      if what == None: return
3880     
3881      what = what[0].split("/")[0]
3882#      print what
3883      if not what in ["node","segment","way"]: return             # only single elements
3884     
3885      o = self.selected_elements_handler.selected_elements[0].get_osm_info()
3886      if o != "":
3887         objection = self.osmdata.allowed_to_be_deleted(o)
3888         if objection == None:
3889            if self.livemode:
3890               dia = simple_dialog(gtk.MESSAGE_INFO,_("Contacting OSM\nto <b>delete</b> %s") %(o[0],),buttons = None, modal = False)
3891               ok = self.osmapi.delete_element(o[0])
3892               dia.destroy()
3893            else:
3894               ok = True
3895
3896            if ok:     
3897               self.selected_elements_handler.clear()
3898               self.osmdata.delete_element(o)            # delete the element here as well
3899            self.drawall()
3900         else:
3901            ok = simple_dialog(gtk.MESSAGE_ERROR,objection,gtk.BUTTONS_CLOSE,modal = True)
3902
3903   def tb_add_segments_to_way(self):
3904      if self.selected_elements_handler.get_type() == "way/segments":
3905         # first id is way, following IDs are segments
3906         sl = self.selected_elements_handler.get_selected_list_ids()
3907         self.extend_way(sl)
3908
3909   def tb_create_segments(self):
3910      if self.selected_elements_handler.get_type() == "nodes":
3911         sl = self.selected_elements_handler.get_selected_list()
3912         first = True
3913         skipped = False
3914         tocreate = []
3915         for l in sl:
3916            if not first:
3917               fromid = ll.get_id()
3918               toid = l.get_id()
3919               if self.osmdata.segment_already_exists(fromid, toid):    # don't create already existing segments
3920                  skipped = True
3921               else:
3922                  tocreate.append((fromid,toid))
3923               
3924            first = False
3925            ll = l
3926
3927         if skipped:
3928            dia = simple_dialog(gtk.MESSAGE_INFO,_("Some segments already existed\nThey have been skipped"),buttons = gtk.BUTTONS_OK)
3929         
3930         if len(tocreate)>0:
3931            if self.livemode:
3932               dia = simple_dialog(gtk.MESSAGE_INFO,_("Contacting OSM\nto create segment"),buttons = None, modal = False)
3933               
3934            for seg in tocreate:
3935               fromid = seg[0]
3936               toid = seg[1]
3937               if self.livemode:
3938                  id = self.osmapi.create_new_segment(fromid, toid)
3939                  if id == None:
3940                     ok = simple_dialog(gtk.MESSAGE_ERROR,_("OSM error - segment from node %s -> node %s") % [fromid,toid],buttons =gtk.BUTTONS_OK)
3941               else: 
3942                  id = self.osmdata.get_next_own_id()
3943
3944               self.osmdata.create_new_segment(fromid, toid, id)
3945               self.drawarea.drawsegment(id, arrowtype = 1)
3946               
3947            if self.livemode:
3948               dia.destroy()
3949
3950   def tb_create_way(self):
3951      tp = self.selected_elements_handler.get_type()
3952      if  tp == "segments" or tp == "segment":
3953         seglist = self.selected_elements_handler.get_selected_list_ids()
3954         
3955         if len(seglist)>0:
3956            if self.livemode:
3957               dia = simple_dialog(gtk.MESSAGE_INFO,_("Contacting OSM\nto create way"),buttons = None, modal = False)
3958               
3959               id = self.osmapi.create_new_way(seglist)
3960               if id == None:
3961                  ok = simple_dialog(gtk.MESSAGE_ERROR,_("OSM error while creating way"),buttons =gtk.BUTTONS_OK)
3962                  return
3963            else: 
3964               id = self.osmdata.get_next_own_id()
3965
3966            self.osmdata.create_new_way(seglist,id)
3967            self.drawarea.drawway(id, arrowtype = 1)
3968               
3969            if self.livemode:
3970               dia.destroy()
3971               
3972   def tb_align_from_to(self):
3973      if self.selected_elements_handler.get_type() == "segment":          # single segment
3974         segments_to_switch = seglist = self.selected_elements_handler.get_selected_list_ids()
3975   
3976      elif self.selected_elements_handler.get_type() == "segments":         # multiple segments
3977         seglist = self.selected_elements_handler.get_selected_list_ids()
3978         stat = self.osmdata.statistic_nodes_from_segments(seglist)
3979         # stat 0: nodes with exactly one occurence, 1: with more than 2, 2: dict with results
3980##         print stat[0]
3981##         print stat[1]
3982##         print stat[2]
3983         if len(stat[0]) != 2 or len(stat[1]) > 0:
3984            ok = simple_dialog(gtk.MESSAGE_ERROR,_("That looks strange.\nSorry, I don't know how to align these segments"),buttons =gtk.BUTTONS_OK)
3985            return
3986
3987         rn = seglist[0]
3988         segments_to_modify = []
3989         startid = None
3990         if self.osmdata.segmentindex[rn][0] in stat[0]: 
3991            startid = self.osmdata.segmentindex[rn][0] 
3992         if self.osmdata.segmentindex[rn][1] in stat[0]: 
3993            startid = self.osmdata.segmentindex[rn][1] 
3994           
3995         if startid == None:
3996            ok = simple_dialog(gtk.MESSAGE_ERROR,_("First selected segment is not the start of the chain.\nSelect in correct order and try again."),buttons =gtk.BUTTONS_OK)
3997            return
3998         
3999         segments_to_switch = []
4000         while seglist != []:
4001            found = False
4002            for x in range(len(seglist)):
4003               if self.osmdata.segmentindex[seglist[x]][0] == startid:
4004                  startid = self.osmdata.segmentindex[seglist[x]][1]    # next id to look for
4005                  del seglist[x]                                # del from search
4006                  found = True
4007                  break
4008               elif self.osmdata.segmentindex[seglist[x]][1] == startid:
4009                  segments_to_switch.append(seglist[x])
4010                  startid = self.osmdata.segmentindex[seglist[x]][0]    # next id to look for
4011                  del seglist[x]                                # del from search
4012                  found = True
4013                  break
4014               else:
4015                  pass
4016            if not found:
4017               ok = simple_dialog(gtk.MESSAGE_ERROR,_("Node %s not found in way.") % (startid,), buttons =gtk.BUTTONS_OK)
4018               return
4019      else:
4020         return
4021                 
4022      if len(segments_to_switch)>0:
4023         if self.livemode:
4024            dia = simple_dialog(gtk.MESSAGE_INFO,_("Contacting OSM\nUpdating segments"),buttons = None, modal = False)
4025         for x in segments_to_switch:
4026            # modify local data first, so that we can send the modified "node" XML to the OSM server
4027            self.osmdata.switch_segment_orientation(x)
4028            if self.livemode:
4029               node = self.osmdata.get_segment_pointer(x)
4030               ok = self.osmapi.send_updated_data(node)
4031               if not ok:
4032                  ok = simple_dialog(gtk.MESSAGE_ERROR,_("Error updating segment %s.\nReload OSM data.") % (x,),buttons =gtk.BUTTONS_OK)
4033                  break
4034         self.drawall()
4035
4036         if self.livemode:
4037            dia.destroy()
4038
4039   ##################### Menu
4040   
4041   
4042   def on_httplibdebug_activate(self,widget):
4043      if widget.get_active(): 
4044         httplib.HTTPConnection.debuglevel = 2
4045      else:
4046         httplib.HTTPConnection.debuglevel = 0
4047   
4048   def on_preferences_activate(self,widget):
4049      ok = self.preferences_handler.run()
4050     
4051   def on_view_arrows_activate(self,widget):
4052      akt = widget.get_active()
4053      self.drawarea.set_arrow_visible(akt)
4054      self.drawall()
4055
4056   def on_view_remote_trace_activate(self,widget):
4057      self.remotegpxdata.set_visible(widget.get_active())
4058      self.drawall()
4059
4060   def on_view_local_trace_activate(self,widget):
4061      self.localgpxdata.set_visible(widget.get_active())
4062      self.drawall()
4063   
4064   def on_info_activate(self,widget):
4065      about = self.xml.get_widget('aboutdialog')
4066      about.show()
4067     
4068   def on_test_activate(self, widget): # at the moment for debug purposes
4069      httplib.HTTPConnection.debuglevel = 2
4070
4071      return
4072         
4073   # mode selection: view, edit
4074   def on_tb_mode_toggled(self,widget,data = None):
4075      mo = self.tb_modes.index(widget)
4076      akt = widget.get_active()
4077      if akt:
4078         self.tb_mode = mo
4079         self.selected_elements_handler.mode_change(mo)
4080         self.toolbarhandler.set_tb_icons(self.selected_elements_handler.get_type())
4081         
4082   def on_live_toggle_button_toggled(self,widget,data=None):
4083      akt = widget.get_active()
4084      if akt:
4085         w = self.passwordrequest()
4086         if w == None: 
4087            akt = False
4088            widget.set_active(False)
4089           
4090      self.livemode = akt
4091     
4092   def on_setosmpassword_activate(self,widget):
4093      w = self.passwordrequest(askalways = True)
4094
4095   def on_saveosmdata_activate(self,widget):
4096      dialog = gtk.FileChooserDialog("Save..",
4097                               None,
4098                               gtk.FILE_CHOOSER_ACTION_SAVE,
4099                               (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4100                                gtk.STOCK_SAVE, gtk.RESPONSE_OK))
4101      dialog.set_default_response(gtk.RESPONSE_OK)
4102
4103      filter = gtk.FileFilter()
4104      filter.set_name("All files")
4105      filter.add_pattern("*")
4106      dialog.add_filter(filter)
4107
4108      filter = gtk.FileFilter()
4109      filter.set_name("OSM data")
4110      filter.add_pattern("*.osm")
4111
4112      if self.config.data["lastosmdir"]:
4113         dialog.set_current_folder(self.config.data["lastosmdir"])
4114      response = dialog.run()
4115
4116      draw = False
4117      if response == gtk.RESPONSE_OK:
4118         fnm = add_extension(dialog.get_filename(),".osm")
4119         self.config.data["lastosmdir"] = os.path.dirname(fnm)
4120         fl = open(fnm,"w")
4121         fl.write(self.osmdata.data.toxml())     
4122         fl.close()
4123      dialog.destroy()
4124     
4125   def on_geo_scan_directory_activate(self,widget):
4126      try:
4127         dia = simple_dialog(gtk.MESSAGE_INFO,_("Scanning directory for\ngeotagged pictures"),buttons = None, modal = False)
4128         self.geotaghints.readdata(self.config.data["geotagdir"])
4129      except OSError:
4130         dia.destroy()
4131         ok = simple_dialog(gtk.MESSAGE_ERROR,_("%s not found") % (self.config.data["geotagdir"],),gtk.BUTTONS_CLOSE,modal = True)
4132         return
4133      dia.destroy()
4134
4135      enable_widgets([self.xml.get_widget("view_geotag"),self.xml.get_widget("sel_geo")])
4136      self.geo_tag_enabled = True
4137      self.geotaghints.set_visible(True)                   # and display them
4138      self.drawall()
4139      self.view_geotag.set_active(True)                    # update the GUI element
4140     
4141      ok = simple_dialog(gtk.MESSAGE_INFO,_("%s geotagged pictures found") % (len(self.geotaghints.index),),gtk.BUTTONS_CLOSE,modal = True)
4142
4143   def on_loadgpx_activate(self,widget):
4144      dialog = gtk.FileChooserDialog("Open GPX file..",
4145                               None,
4146                               gtk.FILE_CHOOSER_ACTION_OPEN,
4147                               (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4148                                gtk.STOCK_OPEN, gtk.RESPONSE_OK))
4149      dialog.set_default_response(gtk.RESPONSE_OK)
4150
4151      filter = gtk.FileFilter()
4152      filter.set_name(_("GPX files"))
4153      filter.add_pattern("*.gpx")
4154      dialog.add_filter(filter)
4155
4156      filter = gtk.FileFilter()
4157      filter.set_name(_("All files"))
4158      filter.add_pattern("*")
4159      dialog.add_filter(filter)
4160
4161      if self.config.data["lastgpxdir"]:
4162         dialog.set_current_folder(self.config.data["lastgpxdir"])
4163
4164      response = dialog.run()
4165
4166      draw = False
4167      if response == gtk.RESPONSE_OK:
4168         self.config.data["lastgpxdir"] = os.path.dirname(dialog.get_filename())
4169##TODO: catch exceptions     
4170         info = self.localgpxdata.readdata(dialog.get_filename())
4171         self.print_to_infopane(info)
4172         self.localgpxdata.recalcdata()
4173         draw = True
4174
4175      elif response == gtk.RESPONSE_CANCEL:
4176         pass
4177
4178      dialog.destroy()
4179      process_pending_gtk_events()
4180      if draw:
4181         self.drawall()
4182         
4183      # enable menu options relating to local tracks
4184      # if screen coords are invalied, fit track to screen
4185      if (self.localgpxdata.data != None):
4186         enable_widgets(self.localgpxdatamenuoptions)
4187         if not self.drawarea.screencoordsset:
4188            self.fitlocaltracktoscreen()
4189
4190   def on_load_gpx_helper_files_activate(self,widget):
4191      dialog = gtk.FileChooserDialog(_("Open multiple GPX files.."),
4192                               None,
4193                               gtk.FILE_CHOOSER_ACTION_OPEN,
4194                               (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4195                                gtk.STOCK_OPEN, gtk.RESPONSE_OK))
4196      dialog.set_default_response(gtk.RESPONSE_OK)
4197      dialog.set_select_multiple(True)
4198
4199      filter = gtk.FileFilter()
4200      filter.set_name(_("GPX files"))
4201      filter.add_pattern("*.gpx")
4202      dialog.add_filter(filter)
4203
4204      filter = gtk.FileFilter()
4205      filter.set_name(_("All files"))
4206      filter.add_pattern("*")
4207      dialog.add_filter(filter)
4208
4209      if self.config.data["lastgpxdir"]:
4210         dialog.set_current_folder(self.config.data["lastgpxdir"])
4211         
4212      response = dialog.run()
4213      filelist = dialog.get_filenames()
4214      dialog.destroy()
4215     
4216      draw = False
4217      if response == gtk.RESPONSE_OK:     
4218##TODO: catch exceptions
4219         box = box_deg(self.drawarea.get_screencoordinates())
4220         box.enlarge(2.0)
4221         info = ""
4222         
4223         dia = simple_dialog(gtk.MESSAGE_INFO,_("Scanning %s GPX tracks") %(len(filelist),),buttons = None, modal = False)
4224         
4225         for fnm in filelist:
4226            points = self.localgpxdata.add_helper_track(fnm,box)
4227            info += _("%s added %s points\n") % (os.path.basename(fnm),points)
4228            self.print_to_infopane(info)
4229
4230         self.localgpxdata.recalcdata()
4231
4232         if len(filelist)>0:
4233            enable_widgets(self.localgpxdatamenuoptions)
4234
4235         dia.destroy()
4236         
4237         self.localgpxdata.recalcdata()
4238         draw = True
4239
4240      elif response == gtk.RESPONSE_CANCEL:
4241         pass
4242
4243      process_pending_gtk_events()
4244      if draw:
4245         self.drawall()
4246         
4247      # enable menu options relating to local tracks
4248      # if screen coords are invalied, fit track to screen
4249      if (self.localgpxdata.data != None):
4250         enable_widgets(self.localgpxdatamenuoptions)
4251         if not self.drawarea.screencoordsset:
4252            self.fitlocaltracktoscreen()
4253
4254
4255   def on_save_gpx_data_activate(self,widget):
4256      dialog = gtk.FileChooserDialog(_("Save local GPX data.."),
4257                               None,
4258                               gtk.FILE_CHOOSER_ACTION_SAVE,
4259                               (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4260                                gtk.STOCK_SAVE, gtk.RESPONSE_OK))
4261      dialog.set_default_response(gtk.RESPONSE_OK)
4262
4263      filter = gtk.FileFilter()
4264      filter.set_name(_("All files"))
4265      filter.add_pattern("*")
4266      dialog.add_filter(filter)
4267
4268      filter = gtk.FileFilter()
4269      filter.set_name(_("GPX data"))
4270      filter.add_pattern("*.gpx")
4271
4272      if self.config.data["lastgpxdir"]:
4273         dialog.set_current_folder(self.config.data["lastgpxdir"])
4274      response = dialog.run()
4275
4276      draw = False
4277      if response == gtk.RESPONSE_OK:
4278         fnm = add_extension(dialog.get_filename(),".gpx")
4279         self.config.data["lastgpxdir"] = os.path.dirname(fnm)
4280         dt = self.localgpxdata.get_modified_gpx_track()
4281         fl = open(fnm,"w")
4282         fl.write(dt)     
4283         fl.close()
4284      dialog.destroy()
4285
4286
4287   def on_loadosmfile_activate(self,widget):
4288      dialog = gtk.FileChooserDialog(_("Open OSM file.."),
4289                               None,
4290                               gtk.FILE_CHOOSER_ACTION_OPEN,
4291                               (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
4292                                gtk.STOCK_OPEN, gtk.RESPONSE_OK))
4293      dialog.set_default_response(gtk.RESPONSE_OK)
4294
4295      filter = gtk.FileFilter()
4296      filter.set_name(_("OSM files"))
4297      filter.add_pattern("*.osm")
4298      dialog.add_filter(filter)
4299     
4300      filter = gtk.FileFilter()
4301      filter.set_name(_("All files"))
4302      filter.add_pattern("*")
4303      dialog.add_filter(filter)
4304
4305      if self.config.data["lastosmdir"]:
4306         dialog.set_current_folder(self.config.data["lastosmdir"])
4307      response = dialog.run()
4308
4309      draw = False
4310      if response == gtk.RESPONSE_OK:
4311         self.selected_elements_handler.clear()     
4312         self.config.data["lastosmdir"] = os.path.dirname(dialog.get_filename())
4313##TODO: catch exceptions     
4314         info = self.osmdata.readdata(dialog.get_filename())
4315         self.tageditor.set_xml_root(self.osmdata.data)
4316         self.print_to_infopane(info)
4317         box = self.osmdata.minmax_deg()
4318         self.drawarea.set_osm_box(box)
4319         self.drawarea.setscreencoords(box[0],box[1],box[2],box[3])
4320         self.recalcall()
4321         self.drawall()
4322
4323#      elif response == gtk.RESPONSE_CANCEL:
4324#         pass
4325
4326      dialog.destroy()
4327      if draw:
4328         self.drawall()
4329   
4330      enable_widgets(self.osmdataloaded,enable = (self.osmdata.data != None))
4331
4332
4333   def on_osmdata_activate(self,widget):
4334      # Note: This code is called from the menu as well as from a button in the toolbar.
4335      self.showwaitdialog(_("Accessing OSM server\nfor OSM street data"))
4336      f = self.osmapi.getmap(self.drawarea.lb_lon,self.drawarea.lb_lat,self.drawarea.rt_lon,self.drawarea.rt_lat)
4337      info = self.osmdata.readdata(f)
4338      f.close()
4339      # Note: the osm surrounding box is set to the REQUESTED box, not to the box derived
4340      #       from the received data (which might be smaller). Extreme case: horizontal
4341      #       motorway with no surronding nodes. If requested from OSM server the empty space
4342      #       is guaranteed to be empty. The calculated box would only be a small strip.
4343      self.drawarea.set_osm_box( (self.drawarea.lb_lon, self.drawarea.lb_lat, self.drawarea.rt_lon, self.drawarea.rt_lat))
4344
4345      self.tageditor.set_xml_root(self.osmdata.data)
4346      self.destroywaitdialog()
4347      self.print_to_infopane(info)
4348      self.osmdata.recalcdata()
4349      self.drawall()
4350      if self.osmdata.data:
4351         enable_widgets(self.osmdataloaded)
4352         
4353
4354   def on_get_osm_tracks_activate(self,widget):
4355      self.showwaitdialog(_("Accessing OSM server\nfor OSM tracks"))
4356      f = self.osmapi.gettrackpoints(self.drawarea.lb_lon, self.drawarea.lb_lat, self.drawarea.rt_lon, self.drawarea.rt_lat, 0)
4357      info = self.remotegpxdata.readdata(f)
4358      f.close()
4359      if self.remotegpxdata.data != None:
4360         enable_widgets(self.remotegpxdatamenuoptions)
4361
4362      self.print_to_infopane(info)
4363      self.destroywaitdialog()
4364      self.remotegpxdata.recalcdata()
4365      self.drawall()
4366     
4367   def on_unselect_gpx_nodes_activate(self,widget):
4368      self.localgpxdata.unselect_all_nodes()
4369      self.drawall()
4370
4371   def on_remove_selected_gpx_nodes_activate(self,widget):
4372      self.localgpxdata.remove_selected_nodes()
4373      self.drawall()
4374
4375   def on_remove_unselected_gpx_nodes_activate(self,widget):
4376      self.localgpxdata.remove_unselected_nodes()
4377      self.drawall()
4378   
4379   def on_fit_track_activate(self,widget):
4380      self.fitlocaltracktoscreen()
4381
4382############ Menu: Bookmarks
4383
4384   def on_bookmark_add_activate(self,widget):
4385      co = self.drawarea.get_screenbookmark()
4386      self.bookmarks_handler.add_bookmark(_("new bookmark"),None,co[0],co[1],co[2])
4387               
4388   def on_bookmark_selected(self,widget,data):
4389      if data:
4390#         name = getChildValue(data,"name")
4391         lon = float(getChildValue(data,"lon"))
4392         lat = float(getChildValue(data,"lat"))
4393         scale = float(getChildValue(data,"scale"))
4394         self.drawarea.set_bookmark(lon,lat,scale)
4395         self.recalcall()
4396         self.drawall()
4397
4398   def on_bookmark_edit_activate(self,widget,data=None):         self.bookmarks_handler.show_edit()
4399   def on_bookmark_list_cursor_changed(self,widget,data=None):   self.bookmarks_handler.cursor_changed()
4400   
4401   def on_bookmark_remove_clicked(self,widget):
4402      return self.bookmarks_handler.button_remove()
4403
4404   def on_bookmark_apply_clicked(self,widget):
4405      return self.bookmarks_handler.button_apply()
4406     
4407   def on_bookmark_list_drag_end(self,widget,data=None):
4408      return self.bookmarks_handler.drag_end()
4409     
4410   def on_bookmark_current_clicked(self,widget):
4411      return self.bookmarks_handler.button_current()
4412     
4413   def on_bookmark_list_row_activated(self,view, path, view_column, data=None):
4414      model = view.get_model()
4415      iter = model.get_iter(path)
4416      bm = model.get_value(iter,0) 
4417      if bm == None: return
4418     
4419      lon = float(getChildValue(bm,"lon"))
4420      lat = float(getChildValue(bm,"lat"))
4421      scale = float(getChildValue(bm,"scale"))
4422      self.drawarea.set_bookmark(lon,lat,scale)
4423      self.recalcall()
4424      self.drawall()
4425      return False
4426               
4427#################################################################
4428     
4429   def fitlocaltracktoscreen(self):
4430      if self.localgpxdata.data:
4431         b = self.localgpxdata.minmax()
4432         self.drawarea.setscreencoords(b[0],b[1],b[2],b[3],center = True)
4433         self.enable_screencoordset()
4434         self.recalcall()
4435         self.drawall()
4436
4437   def findnearestnode_xy(self,x,y):
4438      if self.osmdata.data == None:
4439         return None
4440     
4441      findradius = 10
4442      lx = x - findradius
4443      tx = x + findradius
4444      ly = y - findradius
4445      ty = y + findradius
4446     
4447      id = None
4448      for n in self.osmdata.nodeindex:
4449         px = self.osmdata.nodeindex[n][3]
4450         py = self.osmdata.nodeindex[n][4]
4451         if (lx <= px) and (px <= tx) and (ly <= py) and (py <= ty):
4452            px -= x
4453            py -= y
4454            # calculate distance (square thereof)
4455            dist = px * px + py * py
4456            if id != None:
4457               if dist < ldist:
4458                  id = n
4459                  ldist = dist
4460            else:
4461               id = n
4462               ldist = dist
4463      return id 
4464
4465   def on_tagadd_clicked(self,widget,data = None):
4466      self.tageditor.addbutton()
4467
4468   def on_tagdel_clicked(self,widget,data = None):
4469      self.tageditor.delbutton()
4470
4471   def on_tagapply_clicked(self,widget,data = None):
4472      self.tageditor.applybutton()
4473      if self.livemode:
4474         # apply only accessible if only one element is selected
4475         sel = self.selected_elements_handler.selected_elements[0]
4476         if sel:
4477            data = sel.get_osm_info()
4478            if data != None:
4479               self.showwaitdialog(_("Updating tag data on OSM server"))
4480               ok = self.osmapi.send_updated_data(data[1])
4481               self.destroywaitdialog()
4482   
4483   def on_treeview_cursor_changed(self,widget,data = None):
4484      self.tageditor.cursor_changed()
4485
4486   def on_quit_activate(self,widget):
4487      gtk.main_quit()
4488     
4489   def findnearestsegment_xy(self,px,py):
4490      if self.osmdata.data == None:
4491         return None
4492         
4493      maxdistance = 20
4494     
4495      nseg = None
4496      lh = None
4497     
4498      for seg in self.osmdata.segmentindex:
4499         fromid = self.osmdata.segmentindex[seg][0]
4500         toid = self.osmdata.segmentindex[seg][1]
4501         if fromid == toid:   # fault in database
4502            continue
4503         fromX = self.osmdata.nodeindex[fromid][3]
4504         fromY = self.osmdata.nodeindex[fromid][4]
4505         toX = self.osmdata.nodeindex[toid][3]
4506         toY = self.osmdata.nodeindex[toid][4]
4507                 
4508         toX -= fromX
4509         toY -= fromY
4510         p1X = px - fromX
4511         p1Y = py - fromY
4512         
4513         if (toX == 0) and (toY == 0):
4514            continue
4515           
4516         # scalar product p1 / to
4517         sc = toX * p1X + toY * p1Y
4518
4519         if sc < 0:
4520            continue
4521         
4522         toxl = math.sqrt(toX * toX + toY * toY)
4523         l = (p1X * toX + p1Y * toY) / toxl
4524         if l > toxl:
4525            continue
4526           
4527         hsq = p1X * p1X + p1Y * p1Y - l * l
4528         if hsq < 0:
4529            continue
4530         if nseg == None:
4531            nseg = seg
4532            lh = hsq
4533         else:
4534            if hsq < lh:
4535               nseg = seg
4536               lh = hsq
4537     
4538      if lh > maxdistance:
4539         return None   
4540      return nseg         
4541
4542   def findnearestway_xy(self,px,py):
4543      if self.osmdata.data == None: return None
4544      if not self.osmdata.waysvisible: return None
4545     
4546      seg = self.findnearestsegment_xy(px,py)
4547      if seg == None: return None
4548
4549      ways = self.osmdata.find_ways_with_segment(seg)
4550           
4551      if len(ways) == 0:
4552         return None
4553      if len(ways) == 1:
4554         return ways[0]
4555      w = self.select_way_dialog(seg,ways)
4556      return w
4557   
4558   def on_zoomin_clicked(self,widget):
4559      new = self.drawarea.zoom(1.25)
4560      if new != None:
4561         self.drawarea.setscreencoords(new[0],new[1],new[2],new[3])
4562         self.recalcall()
4563         self.drawall()
4564     
4565   def on_zoomout_clicked(self,widget):
4566      new = self.drawarea.zoom(1 / 1.25)
4567      if new != None:
4568         self.drawarea.setscreencoords(new[0],new[1],new[2],new[3])
4569         self.recalcall()
4570         self.drawall()
4571   
4572   def modifyconfigbeforeclosing(self):
4573      # don't save credentials, if the user doesnt want it
4574      if osmedit.config.data["passsave"] == 0:
4575         osmedit.config.data["username"] = ""
4576         osmedit.config.data["password"] = ""
4577     
4578      if osmedit.config.data["savecurrentdisplaycoord"] == 1: 
4579         co = self.drawarea.get_screencoordinates()
4580         osmedit.config.data["currentdisplaycoord"] = "%s,%s,%s,%s" % (co[0], co[1], co[2], co[3])
4581      else:
4582         del osmedit.config.data["currentdisplaycoord"]
4583
4584      xy = self.mainwindow.get_size()
4585      self.config.data["displaywidth"] = self.mainwindow.get_allocation().width
4586      self.config.data["displayheight"] = self.mainwindow.get_allocation().height
4587               
4588if __name__ == "__main__":
4589   osmedit = osmeditor()
4590   gtk.main()
4591   osmedit.modifyconfigbeforeclosing()
4592   osmedit.config.writedata()
Note: See TracBrowser for help on using the repository browser.