#!/usr/bin/env python # -*- coding: koi8-r -*- import pygtk pygtk.require('2.0') import gtk import gobject import os import sys import time import zipfile import StringIO PROGRAM = 'metromap' MAPINI = 'Metro.ini' MD = None MetroMap = None Finder = None DTlist = dict() DeletedTransfers = dict() scalelist = list() for sc in xrange(5, 21): scalelist.append([str(sc * 10) + '%', sc / 10.0]) scale = 1.0 XSIZE, YSIZE = None, None compact = False dw, dh = gtk.gdk.screen_width(), gtk.gdk.screen_height() if dw < 800: compact = True delta = 5 dtime = 0 h = time.localtime()[3] if h >= 21 or h <= 7: dtime = 1 CITY = None import gettext binpath = os.path.realpath(os.path.dirname(sys.argv[0])) if binpath.endswith('/bin'): #FHS PREFIX = binpath[:binpath.rfind('/bin')] DATAPATH = PREFIX + '/share/' + PROGRAM + '/data/' MODULEPATH = PREFIX + '/share/' + PROGRAM + '/modules/' gettext.bindtextdomain(PROGRAM, PREFIX + '/share/locale/') else: PREFIX = binpath DATAPATH = PREFIX + '/data/' MODULEPATH = PREFIX + '/modules/' gettext.bindtextdomain(PROGRAM, PREFIX + '/locale/') sys.path.insert(1, MODULEPATH) gettext.textdomain(PROGRAM) _ = gettext.gettext from ReadMap import ReadMap, GetMapName from FindPath import FindPath import MapDisplay MapDisplay._ = _ import Interface Interface._ = _ RCDIR = os.environ.get('HOME') + "/." + PROGRAM if not os.path.isdir(RCDIR): try: os.mkdir(RCDIR) except: pass #load config if os.path.isfile(RCDIR + "/rc"): f = file(RCDIR + "/rc", 'r') done = False while not done: s = f.readline() if not s: break p = s.split('=') if len(p) == 2: if p[0] == 'city': CITY = p[1].rstrip() elif p[0] == 'zoom': scale = float(p[1].rstrip()) if not scale in map(lambda a: a[1], scalelist): scale = 1 elif p[0] == 'xsize': XSIZE = int(p[1].rstrip()) elif p[0] == 'ysize': YSIZE = int(p[1].rstrip()) elif p[0] == 'delta': delta = int(p[1].rstrip()) if delta < 0: delta = 0 elif delta > 99: delta = 99 elif p[0] == 'compact': if p[1].rstrip().lower() == "true": compact = True elif p[0].startswith('dt[') and p[0][-1] == ']': DTlist[p[0][3:-1]] = list() sp = p[1].rstrip().split(',') for pair in sp: l = pair.rstrip().lstrip().split('-', 2) if len(l) == 2 and l[0].isdigit() and l[1].isdigit(): DTlist[p[0][3:-1]].append((int(l[0]), int(l[1]))) f.close() #save config def save_config(): try: f = file(RCDIR + "/rc", 'w') f.write('city=' + CITY + '\n') f.write('xsize=' + str(Iface.xsize) + '\n') f.write('ysize=' + str(Iface.ysize) + '\n') f.write('delta=' + str(delta) + '\n') if Iface.compact_mode: f.write('compact=true\n') else: f.write('compact=false\n') f.write('zoom=' + str(scale) + '\n') for c in DTlist.keys(): if len(DTlist[c]): st = 'dt[' + c + ']=' for p in DTlist[c]: st += str(p[0]) + '-' + str(p[1]) + ',' st = st[:-1] + '\n' f.write(st) f.close() except: pass def sync_to_dt(): DTlist[CITY] = list() for dt in DeletedTransfers.keys(): if not (dt[0], dt[1]) in DTlist[CITY] and not (dt[1], dt[0]) in DTlist[CITY]: DTlist[CITY].append((dt[0], dt[1])) def sync_from_dt(): global DeletedTransfers DeletedTransfers = dict() if DTlist.has_key(CITY): Finder.set_graph(dt_disable(Finder.graph, DTlist[CITY])) def atexit(foo): save_config() gtk.main_quit() st_start = None st_end = None path_list = list() def ciext_file_find(path, name, ext): for f in os.listdir(path): if f.startswith(name) and len(f) == len(name) + len(ext) + 1 and \ f[-len(ext):].lower() == ext.lower(): return f #not found, return some shit return name + '.' + ext def get_data_full(datapath, city, filename): f = None n = None try: n = datapath + city + '/' + filename f = open(n) except: try: n = datapath + ciext_file_find(datapath, city, 'pmz') z = zipfile.ZipFile(n, 'r') f = StringIO.StringIO(z.read(filename)) z.close() except: try: n = datapath + ciext_file_find(datapath, city, 'zip') z = zipfile.ZipFile(n, 'r') intn = city + '.pmz' for zn in z.namelist(): if zn.lower() == city.lower() + '.pmz': intn = zn break pmzf = StringIO.StringIO(z.read(intn)) z.close() z = zipfile.ZipFile(pmzf, 'r') f = StringIO.StringIO(z.read(filename)) z.close() except: n = _("unknown") f = None return n, f def get_data(city, filename): for dp in DATAPATH, RCDIR + '/': n, f = get_data_full(dp, city, filename) if f: break return n, f citylist = list() for dp in DATAPATH, RCDIR + '/': if not os.path.isdir(dp): continue for c in os.listdir(dp): cn = c if c.lower().endswith('.zip') or c.lower().endswith('.pmz'): cn = c[:-4] n, f = get_data_full(dp, cn, 'Metro.ini') if f: name = GetMapName(f) f.close() if name: citylist.append((name, cn)) if len(citylist) == 0: print unicode(_('No data files found, please install some.')) sys.exit(0) if not CITY or not len(filter(lambda a: a[1] == CITY, citylist)): if len(filter(lambda a: a[1] == "Moscow", citylist)): CITY = "Moscow" else: CITY = citylist[0][1] def station_selected(station, isstart): global st_start, st_end, path_list if st_start != station and st_end != station: if isstart: st_start = station Iface.set_from(station) MD.set_start_station(station) else: st_end = station Iface.set_to(station) MD.set_stop_station(station) if st_start != None and st_end != None: path_list = Finder.waves_accurate(st_start, st_end, MetroMap.WaitLen) path_list.sort() if len(path_list): MD.set_path_list(path_list[0][2:]) else: MD.set_path_list(None) Iface.set_path_list(map(lambda a: _("Minutes: %d Changes: %d") % (a[0], a[1]), path_list)) def list_selected(num): global path_list if path_list and num < len(path_list): MD.set_path_list(path_list[num][2:]) def zoom_changed(zoom): global scale if scale != zoom: scale = zoom city_changed(None) def city_changed(name): global st_start, st_end, path_list, MetroMap, MD, Finder, prefs, CITY, dtime, delta, scale if name != None: CITY = name st_start = st_end = None path_list = list() n, f = get_data(CITY, MAPINI) if not f: print unicode(_('Cannot find Metro.ini for city "') + CITY + '"') sys.exit(0) MetroMap = ReadMap(f) f.close() MetroMap.SetDelayTime(dtime) snames = map(lambda a: ["%s (%s)" % (a['name'], MetroMap.Lines[a['line']]['name']), a['number']], MetroMap.Stations.values()) snames.sort() sh = False #hack for StPetersburg map if CITY.lower() == 'peterburg': sh = True mdlines = MetroMap.Lines mdstations = MetroMap.Stations mdvectors = MetroMap.Map['vectors'] if scale != 1: for k in mdlines.keys(): mdlines[k]['diameter'] = int(round(mdlines[k]['diameter'] * scale)) for k in mdstations.keys(): mdstations[k]['diameter'] = int(round(mdstations[k]['diameter'] * scale)) for ak in mdstations[k]['add'].keys(): for i in xrange(len(mdstations[k]['add'][ak]['coords'])): foo = mdstations[k]['add'][ak]['coords'][i] mdstations[k]['add'][ak]['coords'][i] = (int(round(foo[0] * scale)), int(round(foo[1] * scale))) mdstations[k]['x'] = int(round(mdstations[k]['x'] * scale)) mdstations[k]['y'] = int(round(mdstations[k]['y'] * scale)) mdstations[k]['namerect'][0] = int(round(mdstations[k]['namerect'][0] * scale)) mdstations[k]['namerect'][1] = int(round(mdstations[k]['namerect'][1] * scale)) mdstations[k]['namerect'][2] = int(round(mdstations[k]['namerect'][2] * scale)) mdstations[k]['namerect'][3] = int(round(mdstations[k]['namerect'][3] * scale)) if mdvectors.has_key('lineswidth'): mdvectors['lineswidth'] = int(round(mdvectors['lineswidth'] * scale)) for i in xrange(len(mdvectors['instructions'])): if mdvectors['instructions'][i][0] == 'spline': mdvectors['instructions'][i][1] = int(round(mdvectors['instructions'][i][1] * scale)) for j in xrange(len(mdvectors['instructions'][i][2])): foo = mdvectors['instructions'][i][2][j] mdvectors['instructions'][i][2][j] = (int(round(foo[0] * scale)), int(round(foo[1] * scale))) elif mdvectors['instructions'][i][0] == 'text': mdvectors['instructions'][i][1]['size'] = int(round(mdvectors['instructions'][i][1]['size'] * scale)) mdvectors['instructions'][i][1]['x'] = int(round(mdvectors['instructions'][i][1]['x'] * scale)) mdvectors['instructions'][i][1]['y'] = int(round(mdvectors['instructions'][i][1]['y'] * scale)) MD = MapDisplay.MapDisplay(mdlines, mdstations, mdvectors, station_selected, sh) #MD = MapDisplay.MapDisplay(MetroMap.Lines, MetroMap.Stations, MetroMap.Map['vectors'], station_selected, sh) MD.set_menu(Iface.menu_callback, Iface.menu_items) MD.set_dtcb(transfers_cb) if name != None: Iface.set_path_list([]) Iface.set_da(MD.da, MD.pw, MD.ph) Iface.set_st_list(snames) Iface.set_dtime(dtime) Iface.set_win_title(PROGRAM + ' - ' + MetroMap.Map['city']) if name == None: MD.set_start_station(st_start) MD.set_stop_station(st_end) Iface.set_from(st_start) Iface.set_to(st_end) Iface.path_selected(None) if name != None: if not Finder: Finder = FindPath(MetroMap.Graph, delta) else: Finder.set_graph(MetroMap.Graph) sync_from_dt() def dt_enable(g, pairs): for p in pairs: for tr in DeletedTransfers[(p[0], p[1])]: g[p[0]].append(tr) g[p[1]].append(tr) del DeletedTransfers[(p[0], p[1])] del DeletedTransfers[(p[1], p[0])] return g def dt_disable(g, pairs): for p in pairs: DeletedTransfers[(p[0], p[1])] = list() DeletedTransfers[(p[1], p[0])] = list() for tr in g[p[0]]: if tr[0] == p[1]: DeletedTransfers[(p[0], p[1])].append(tr) DeletedTransfers[(p[1], p[0])].append(tr) g[p[0]].remove(tr) break for tr in g[p[1]]: if tr[0] == p[0]: DeletedTransfers[(p[0], p[1])].append(tr) DeletedTransfers[(p[1], p[0])].append(tr) g[p[1]].remove(tr) break return g def transfers_cb(act, sfrom = None, sto = None): if act == 'get': return DeletedTransfers elif act == 'set': if sfrom == None or sto == None: #enable all disabled transfers Finder.set_graph(dt_enable(Finder.graph, DTlist[CITY])) else: if DeletedTransfers.has_key((sfrom, sto)): Finder.set_graph(dt_enable(Finder.graph, [[sfrom, sto]])) else: Finder.set_graph(dt_disable(Finder.graph, [[sfrom, sto]])) recalc_paths() sync_to_dt() def recalc_paths(): global st_start if st_start != None: station = st_start st_start = None station_selected(station, True) def dtime_changed(num): global MetroMap, dtime if num != dtime: dtime = num MetroMap.SetDelayTime(dtime) recalc_paths() def delta_changed(num): global delta if num != delta: delta = num Finder.maxerror = delta recalc_paths() Iface = Interface.Interface(PROGRAM, None, citylist = citylist, compact = compact, delta = delta, citynow = CITY, xsize = XSIZE, ysize = YSIZE, zoomlist = scalelist, zoomnow = scale) Iface.win.connect('destroy', atexit) Iface.set_path_cb(list_selected) Iface.set_station_cb(station_selected) Iface.set_city_cb(city_changed) Iface.set_dtime_cb(dtime_changed) Iface.set_delta_cb(delta_changed) Iface.set_zoom_cb(zoom_changed) Iface.quit_cb = atexit city_changed(CITY) gtk.main()