Files
ecopcr/obitools/carto/__init__.py

377 lines
13 KiB
Python

# -*- coding: latin1 -*-
from obitools import SVGdraw
import math
class Map(object):
"""
Map represente une instance d'une carte genetique physique.
Une telle carte est definie par la longueur de la sequence
qui lui est associe.
A une carte est associe un certain nombre de niveaux (Level)
eux meme decoupe en sous-niveau (SubLevel)
Les sous niveaux contiennent eux des features
"""
def __init__(self,name,seqlength,scale=1):
"""
Constructeur d'une nouvelle carte
*Param*:
name
nom de la carte
seqlength
longueur de la sequence associee a la carte
scale
echelle de la carte indicant combien de pixel
correspondent a une unite de la carte
"""
self.name = name
self.seqlength = seqlength
self.scale = scale
self.levels = {}
self.basicHSize = 10
def __str__(self):
return '<%s>' % self.name
def __getitem__(self,level):
"""
retourne le niveau *level* de la carte et
le cree s'il n'existe pas
"""
if not isinstance(level,int):
raise TypeError('level must be an non Zero integer value')
elif level==0:
raise AssertionError('Level cannot be set to 0')
try:
return self.levels[level]
except KeyError:
self.levels[level] = Level(level,self)
return self.levels[level]
def getBasicHSize(self):
"""
retourne la hauteur de base d'un element de cartographie
exprimee en pixel
"""
return self.basicHSize
def getScale(self):
"""
Retourne l'echelle de la carte en nombre de pixels par
unite physique de la carte
"""
return self.scale
def getNegativeBase(self):
return reduce(lambda x,y:x-y,[self.levels[z].getHeight()
for z in self.levels
if z < 0],self.getHeight())
def getPositiveBase(self):
return self.getNegativeBase() - 3 * self.getBasicHSize()
def getHeight(self):
return reduce(lambda x,y:x+y,[z.getHeight() for z in self.levels.values()],0) \
+ 4 * self.getBasicHSize()
def toXML(self,file=None,begin=0,end=None):
dessin = SVGdraw.drawing()
if end==None:
end = self.seqlength
hauteur= self.getHeight()
largeur=(end-begin+1)*self.scale
svg = SVGdraw.svg((begin*self.scale,0,largeur,hauteur),
'%fpx' % (self.seqlength * self.scale),
'%dpx' % hauteur)
centre = self.getPositiveBase() + (1 + 1/4) * self.getBasicHSize()
svg.addElement(SVGdraw.rect(0,centre,self.seqlength * self.scale,self.getBasicHSize()/2))
for e in self.levels.values():
svg.addElement(e.getElement())
dessin.setSVG(svg)
return dessin.toXml(file)
class Feature(object):
pass
class Level(object):
def __init__(self,level,map):
if not isinstance(map,Map):
raise AssertionError('map is not an instance of class Map')
if level in map.levels:
raise AssertionError('Level %d already define for map %s' % (level,map))
else:
map.levels[level] = self
self.map = map
self.level = level
self.sublevels = {}
def __getitem__(self,sublevel):
"""
retourne le niveau *sublevel* du niveau en
le creant s'il n'existe pas
"""
if not isinstance(sublevel,int):
raise TypeError('sublevel must be a positive integer value')
elif sublevel<0:
raise AssertionError('Level cannot be negative')
try:
return self.sublevels[sublevel]
except KeyError:
self.sublevels[sublevel] = SubLevel(sublevel,self)
return self.sublevels[sublevel]
def getBase(self):
if self.level < 0:
base = self.map.getNegativeBase()
base += reduce(lambda x,y:x+y,[self.map.levels[z].getHeight()
for z in self.map.levels
if z <0 and z >= self.level],0)
return base
else:
base = self.map.getPositiveBase()
base -= reduce(lambda x,y:x+y,[self.map.levels[z].getHeight()
for z in self.map.levels
if z >0 and z < self.level],0)
return base
def getElement(self):
objet = SVGdraw.group('level%d' % self.level)
for e in self.sublevels.values():
objet.addElement(e.getElement())
return objet
def getHeight(self):
return reduce(lambda x,y:x+y,[z.getHeight() for z in self.sublevels.values()],0) \
+ 2 * self.map.getBasicHSize()
class SubLevel(object):
def __init__(self,sublevel,level):
if not isinstance(level,Level):
raise AssertionError('level is not an instance of class Level')
if level in level.sublevels:
raise AssertionError('Sublevel %d already define for level %s' % (sublevel,level))
else:
level.sublevels[sublevel] = self
self.level = level
self.sublevel = sublevel
self.features = {}
def getHeight(self):
return max([x.getHeight() for x in self.features.values()]+[0]) + 4 * self.level.map.getBasicHSize()
def getBase(self):
base = self.level.getBase()
if self.level.level < 0:
base -= self.level.getHeight() - 2 * self.level.map.getBasicHSize()
base += reduce(lambda x,y:x+y,[self.level.sublevels[z].getHeight()
for z in self.level.sublevels
if z <= self.sublevel],0)
base -= 2* self.level.map.getBasicHSize()
else:
base -= reduce(lambda x,y:x+y,[self.level.sublevels[z].getHeight()
for z in self.level.sublevels
if z < self.sublevel],0)
base -= self.level.map.getBasicHSize()
return base
def getElement(self):
base = self.getBase()
objet = SVGdraw.group('sublevel%d' % self.sublevel)
for e in self.features.values():
objet.addElement(e.getElement(base))
return objet
def add(self,feature):
if not isinstance(feature,Feature):
raise TypeError('feature must be an instance oof Feature')
if feature.name in self.features:
raise AssertionError('A feature with the same name (%s) have already be insert in this sublevel'
% feature.name)
self.features[feature.name]=feature
feature.sublevel=self
class SimpleFeature(Feature):
def __init__(self,name,begin,end,visiblename=False,color=0):
self.begin = begin
self.end = end
self.name = name
self.color = color
self.sublevel = None
self.visiblename=visiblename
def getHeight(self):
if not self.sublevel:
raise AssertionError('Not affected Simple feature')
if self.visiblename:
return self.sublevel.level.map.getBasicHSize() * 2
else:
return self.sublevel.level.map.getBasicHSize()
def getElement(self,base):
scale = self.sublevel.level.map.getScale()
y = base - self.sublevel.level.map.getBasicHSize()
x = self.begin * scale
width = (self.end - self.begin + 1) * scale
heigh = self.sublevel.level.map.getBasicHSize()
objet = SVGdraw.rect(x,y,width,heigh,stroke=self.color)
objet.addElement(SVGdraw.description(self.name))
return objet
class BoxFeature(SimpleFeature):
def getHeight(self):
if not self.sublevel:
raise AssertionError('Not affected Box feature')
if self.visiblename:
return self.sublevel.level.map.getBasicHSize() * 4
else:
return self.sublevel.level.map.getBasicHSize() * 3
def getElement(self,base):
scale = self.sublevel.level.map.getScale()
y = base - self.sublevel.level.map.getBasicHSize() * 2
x = self.begin * scale
width = (self.end - self.begin + 1) * scale
height = self.sublevel.level.map.getBasicHSize() * 3
objet = SVGdraw.rect(x,y,width,height,stroke=self.color,fill="none")
objet.addElement(SVGdraw.description(self.name))
return objet
class MultiPartFeature(Feature):
def __init__(self,name,*args,**kargs):
self.limits = args
self.name = name
try:
self.color = kargs['color']
except KeyError:
self.color = "black"
try:
self.visiblename=kargs['visiblename']
except KeyError:
self.visiblename=None
try:
self.flatlink=kargs['flatlink']
except KeyError:
self.flatlink=False
try:
self.roundlink=kargs['roundlink']
except KeyError:
self.roundlink=False
self.sublevel = None
def getHeight(self):
if not self.sublevel:
raise AssertionError('Not affected Simple feature')
if self.visiblename:
return self.sublevel.level.map.getBasicHSize() * 3
else:
return self.sublevel.level.map.getBasicHSize() * 2
def getElement(self,base):
scale = self.sublevel.level.map.getScale()
y = base - self.sublevel.level.map.getBasicHSize()
height = self.sublevel.level.map.getBasicHSize()
objet = SVGdraw.group(self.name)
for (debut,fin) in self.limits:
x = debut * scale
width = (fin - debut + 1) * scale
part = SVGdraw.rect(x,y,width,height,fill=self.color)
objet.addElement(part)
debut = self.limits[0][1]
for (fin,next) in self.limits[1:]:
debut*=scale
fin*=scale
path = SVGdraw.pathdata(debut,y + height / 2)
delta = height / 2
if self.roundlink:
path.qbezier((debut+fin)/2, y - delta,fin,y + height / 2)
else:
if self.flatlink:
delta = - height / 2
path.line((debut+fin)/2, y - delta)
path.line(fin,y + height / 2)
path = SVGdraw.path(path,fill="none",stroke=self.color)
objet.addElement(path)
debut = next
objet.addElement(SVGdraw.description(self.name))
return objet
class TagFeature(Feature):
def __init__(self,name,begin,length,ratio,visiblename=False,color=0):
self.begin = begin
self.length = length
self.ratio = ratio
self.name = name
self.color = color
self.sublevel = None
self.visiblename=visiblename
def getHeight(self):
if not self.sublevel:
raise AssertionError('Not affected Tag feature')
return self.sublevel.level.map.getBasicHSize()*11
def getElement(self,base):
scale = self.sublevel.level.map.getScale()
height = math.floor(max(1,self.sublevel.level.map.getBasicHSize()* 10 * self.ratio))
y = base + self.sublevel.level.map.getBasicHSize() - height
x = self.begin * scale
width = self.length * scale
objet = SVGdraw.rect(x,y,width,height,stroke=self.color)
objet.addElement(SVGdraw.description(self.name))
return objet
if __name__ == '__main__':
carte = Map('essai',20000,scale=0.5)
carte[-1][0].add(SimpleFeature('toto',100,300))
carte[1][0].add(SimpleFeature('toto',100,300))
carte[1][1].add(SimpleFeature('toto',200,1000))
carte[1][0].add(MultiPartFeature('bout',(1400,1450),(1470,1550),(1650,1800),color='red',flatlink=True))
carte[1][0].add(MultiPartFeature('titi',(400,450),(470,550),(650,800),color='red',flatlink=True))
carte[-1][1].add(MultiPartFeature('titi',(400,450),(470,550),(650,800),color='green'))
carte[-1][2].add(MultiPartFeature('titi',(400,450),(470,550),(650,800),color='purple',roundlink=True))
carte[-1][1].add(BoxFeature('tutu',390,810,color='purple'))
carte[1][0].add(BoxFeature('tutu',390,810,color='red'))
carte[2][0].add(TagFeature('t1',1400,20,0.8))
carte[2][0].add(TagFeature('t2',1600,20,0.2))
carte.basicHSize=6
print carte.toXML('truc.svg',begin=0,end=1000)
print carte.toXML('truc2.svg',begin=460,end=2000)