#!/usr/bin/env python import sys import time import logging from math import sqrt from sqlite3 import connect from svgfig import * from eveapi import EVEAPIConnection from cache import DbCacheHandler __version__ = (0,0,1) class CynoMap(object): def __init__(self, dbtype='sqlite3', conn={'database': 'cruc101-sqlite3-v1.db'}, keyid=None, vcode=None, jumprange=13): if dbtype == 'sqlite3': from sqlite3 import connect elif dbtype == 'mysql': from MySQLdb import connect elif dbtype == 'postgres': from psycopg2 import connect else: self.log.debug('Unknown DB Type %s, attempting to import %s' % (dbtype, dbtype)) try: connect = __import__(dbtype, fromlist=['connect']).connect except ImportError: self.log.critical('Error importing %s library, check your dbtype is correct' % dbtype) raise Exception('Invalid dbtype passed') self.factor = 20 self.sddconn = connect(**conn) self.keyid = keyid self.vcode = vcode self.jumprange = jumprange @property def log(self): if not hasattr(self, '_log'): self._log = logging.getLogger(self.__class__.__name__) return self._log @property def svg(self): if not hasattr(self, '_svg'): t1 = time.time() self._svg = self.render_map() t2 = time.time() self.log.debug('SVG generated (took %0.3f sec)' % ((t2-t1),)) return self._svg @staticmethod def calc_distance(sys1, sys2): """Calculate the distance between two sets of 3d coordinates""" return sqrt((sys1['x']-sys2['x'])**2+(sys1['y']-sys2['y'])**2+(sys1['z']-sys2['z'])**2) / 10000000000000000 @property def systems(self): if not hasattr(self, '_systems'): t1 = time.time() self._systems = self.get_systems_data() t2 = time.time() self.log.debug('%s systems data generated (took %0.3f ms)' % (len(self._systems), (t2-t1)*1000.0)) return self._systems def get_systems_data(self): """Takes the system data out of the SDD and mangles the coords""" sql = """SELECT solarSystemID as id, solarSystemName as name, security, x, y, z FROM mapSolarSystems WHERE regionID < 11000001""" data = {} csr = self.sddconn.cursor() csr.execute(sql) for id, name, security, x, y, z in csr: reduction = 10000000000000000 cx = ((x / reduction) * self.factor) + 100 cy = ((y / reduction) * self.factor) + 100 cz = ((z / reduction) * self.factor) + 100 data[int(id)] = {'id': id, 'name': name, 'security': security, 'x': x, 'y': y, 'z': z, 'cx': cx, 'cy': cy, 'cz': cz } return data @property def jumps(self): if not hasattr(self, '_jumps'): t1 = time.time() self._jumps = self.get_gate_data() t2 = time.time() self.log.debug('%s jump data generated (took %0.3f ms)' % (len(self._jumps), (t2-t1)*1000.0)) return self._jumps def get_gate_data(self): """Extracts the list of gate jumps out of the SDD""" sql = """SELECT fromSolarSystemID, toSolarSystemID FROM mapSolarSystemJumps""" data = [] csr = self.sddconn.cursor() csr.execute(sql) return [{'from': int(frm), 'to': int(to)} for frm, to in csr] def get_system_location(self, id): """Attempts to derrive a system ID based on a location ID provided""" stationsql = "SELECT solarSystemID from StaStations WHERE stationID = ?" systemsql = "SELECT solarSystemID from mapSolarSystems where solarSystemID = ?" apistation = {} if not hasattr(self, '_stations'): stations = EVEAPIConnection(cacheHandler=DbCacheHandler()).eve.ConquerableStationList().outposts for x in stations: apistation[x.stationID] = x.solarSystemID self._stations = apistation csr = self.sddconn.cursor() if csr.execute(systemsql, (id,)).fetchone(): return id elif id in self._stations: return self._stations[id] else: stid = csr.execute(stationsql, (id,)).fetchone() if stid: return stid[0] return None def get_cyno_locations(self): """Aquires the list of corporation members to check for character locations""" if not self.keyid or not self.vcode: return {} if not hasattr(self, '_cynochars'): auth = EVEAPIConnection(cacheHandler=DbCacheHandler()).auth(keyID=self.keyid, vCode=self.vcode) chars = auth.account.Characters() members = auth.corp.MemberTracking(characterID=chars.characters[0].characterID) self._cynochars = {} for member in members.members: loc = self.get_system_location(member.locationID) if not loc: continue info = {'name': member.name } if not int(loc) in self._cynochars: self._cynochars[loc] = [info] else: self._cynochars[loc].append(info) return self._cynochars @property def cynos(self): if not hasattr(self, '_cynos'): self._cynos = self.get_cyno_routes() return self._cynos def get_cyno_routes(self): """Calculates usable cyno routes between cyno alts""" routes = [] for cyno in self.get_cyno_locations().keys(): sys1 = self.systems[cyno] if not sys1['security'] < 0.5: continue for cyno2 in self.get_cyno_locations().keys(): sys2 = self.systems[cyno2] if not sys2['security'] < 0.5: continue dist = self.calc_distance(self.systems[cyno], self.systems[cyno2]) #self.log.debug('Checking %s to %s (%sly)' % (sys1['name'], sys2['name'], dist)) if not cyno == cyno2 and dist <= self.jumprange: self.log.info('Cyno route found: %s to %s (%sly)' % (sys1['name'], sys2['name'], dist)) routes.append((cyno, cyno2)) return routes def render_map(self, compressed=False): """Renders the map data into a SVG, with gate jumps and cynos where possible""" # Generate systems lowx = 0 highx = 0 lowz = 0 highz = 0 sysgroup = SVG('g', id='systems') for sys in self.systems.values(): if sys['id'] in self.get_cyno_locations(): fill = 'red' radius = 5 else: fill = 'gray' for loc in self.get_cyno_locations(): if self.calc_distance(sys, self.systems[loc]) < self.jumprange: fill = 'blue' radius = 2 if lowx > sys['cx']: lowx = sys['cx'] if lowz > sys['cz']: lowz = sys['cz'] if highx < sys['cx']: highx = sys['cx'] if highz < sys['cz']: highz = sys['cz'] attrs = {'cx': sys['cx'], 'cy': -sys['cz'], 'r': radius, 'id': 'system-%s' % sys['id'], 'class': 'system', 'fill': fill, 'stroke-width': 0} svg = SVG('circle', **attrs) sysgroup.append(svg) # Generate jumps jumpgroup = SVG('g', id='jumps') for jump in self.jumps: sys1 = self.systems[jump['from']] sys2 = self.systems[jump['to']] attrs = {'x1': sys1['cx'], 'y1': -sys1['cz'], 'x2': sys2['cx'], 'y2': -sys2['cz'], 'id': 'jump-%s-%s' % (jump['from'], jump['to']), 'class': 'jump', 'stroke': 'lightgray'} jmp = Line(**attrs).SVG() jumpgroup.append(jmp) # Generate cynos cynos = SVG('g', id='cynos') for frm, to in self.cynos: sys1 = self.systems[frm] sys2 = self.systems[to] attrs = {'x1': sys1['cx'], 'y1': -sys1['cz'], 'x2': sys2['cx'], 'y2': -sys2['cz'], 'id': 'cyno-%s-%s' % (frm, to), 'class': 'cyno', 'stroke': 'red', 'width': '2px'} cyn = Line(**attrs).SVG() cynos.append(cyn) c = canvas() c.attr.update({'viewBox': '-1000 -1100 2000 2000', 'height': '2000', 'width': '2000'}) c.extend([jumpgroup, sysgroup, cynos]) return c