Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

edited __init__.py to use sentiment analysis for tag coloration #21

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 59 additions & 65 deletions pytagcloud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
import os
import pygame
import simplejson
from indicoio import sentiment


TAG_PADDING = 5
TAG_CLOUD_PADDING = 5 # Margins added to the whole image
STEP_SIZE = 2 # relative to base step size of each spiral function
RADIUS = 1
ECCENTRICITY = 1.5
Expand Down Expand Up @@ -48,14 +48,6 @@

LAST_COLLISON_HIT = None

FONTS = {}

def get_font(font, size):
name = font['ttf']
if (name, size) not in FONTS:
FONTS[name, size] = pygame.font.Font(os.path.join(FONT_DIR, name), size)
return FONTS[name, size]

class Tag(Sprite):
"""
Font tag sprite. Blit the font to a surface to correct the font padding
Expand All @@ -66,7 +58,9 @@ def __init__(self, tag, initial_position, fontname=DEFAULT_FONT):
self.rotation = 0

self.font_spec = load_font(fontname)
self.font = get_font(self.font_spec, self.tag['size'])
self.font = font.Font(os.path.join(FONT_DIR,
self.font_spec['ttf']),
self.tag['size'])
fonter = self.font.render(tag['tag'], True, tag['color'])
frect = fonter.get_bounding_rect()
frect.x = -frect.x
Expand All @@ -81,24 +75,25 @@ def __init__(self, tag, initial_position, fontname=DEFAULT_FONT):
self.rect.x = initial_position[0]
self.rect.y = initial_position[1]
self._update_mask()

def _update_mask(self):
self.mask = mask.from_surface(self.image)
self.mask = self.mask.convolve(CONVMASK, None, (TAG_PADDING, TAG_PADDING))

def flip(self):
def flip(self):
angle = 90 if self.rotation == 0 else - 90
self.rotate(angle)

def rotate(self, angle):
pos = (self.rect.x, self.rect.y)
self.image = transform.rotate(self.image, angle)
self.rect = self.image.get_rect()
self.rect.x, self.rect.y = pos
self._update_mask()

def update_fontsize(self):
self.font = get_font(self.font_spec, self.tag['size'])
self.font = font.Font(os.path.join(FONT_DIR, self.font_spec['ttf']),
self.tag['size'])

def load_font(name):
for font in FONT_CACHE:
Expand All @@ -120,19 +115,23 @@ def make_tags(wordcounts, minsize=3, maxsize=36, colors=None, scalef=defscale):
word appears in a text)
the tags are assigned sizes between minsize and maxsize, the function used
is determined by scalef (default: square root)
color is either chosen from colors (list of rgb tuples) if provided or random
color is determined by indicoio's sentiment analysis api
"""
counts = [tag[1] for tag in wordcounts]

if not len(counts):
return []

maxcount = max(counts)
mincount = min(counts)
tags = []
for word_count in wordcounts:
color = choice(colors) if colors else (randint(10, 220), randint(10, 220),
randint(10, 220))
sentiment_rgb = int((sentiment(word_count[0])["Sentiment"])*220)
if sentiment_rgb < 0:
sentiment_rgb = sentiment_rgb*-1
elif sentiment_rgb > 220:
sentiment_rgb = 220
color = (sentiment_rgb, sentiment_rgb, sentiment_rgb)
tags.append({'color': color, 'size': scalef(word_count[1], mincount,
maxcount, minsize, maxsize),
'tag': word_count[0]})
Expand All @@ -158,7 +157,7 @@ def _get_tags_bounding(tag_store):
return Rect(0,0,0,0)
rects = [tag.rect for tag in tag_store]
return rects[0].unionall(rects[1:])

def _get_group_bounding(tag_store, sizeRect):
if not isinstance(sizeRect, pygame.Rect):
sizeRect = Rect(0, 0, sizeRect[0], sizeRect[1])
Expand Down Expand Up @@ -221,54 +220,54 @@ def _search_place(current_tag, tag_store, canvas, spiral, ratio):
tag_store.add(current_tag)
return
else:
# get the distance from center
# get the distance from center
current_dist = (abs(cx - current_tag.rect.x) ** 2 +
abs(cy - current_tag.rect.y) ** 2) ** 0.5
abs(cy - current_tag.rect.y) ** 2) ** 0.5
if not min_dist or current_dist < min_dist:
opt_x = current_tag.rect.x
opt_y = current_tag.rect.y
min_dist = current_dist

# only add tag if the spiral covered the canvas boundaries
if abs(dx) > canvas.width / 2.0 and abs(dy) > canvas.height / 2.0:
current_tag.rect.x = opt_x
current_tag.rect.y = opt_y
tag_store.add(current_tag)

new_bounding = current_bounding.union(current_tag.rect)

delta_x = delta_y = 0.0
if new_bounding.w > canvas.width:
delta_x = new_bounding.w - canvas.width

canvas.width = new_bounding.w
delta_y = ratio * new_bounding.w - canvas.height
canvas.height = ratio * new_bounding.w

if new_bounding.h > canvas.height:
canvas.height = ratio * new_bounding.w
if new_bounding.h > canvas.height:
delta_y = new_bounding.h - canvas.height

canvas.height = new_bounding.h
canvas.width = new_bounding.h / ratio
delta_x = canvas.width - canvas.width

# realign
for tag in tag_store:
tag.rect.x += delta_x / 2.0
tag.rect.y += delta_y / 2.0


canvas = _get_tags_bounding(tag_store)

return
return

def _draw_cloud(
tag_list,
layout=LAYOUT_MIX,
size=(500,500),
fontname=DEFAULT_FONT,
rectangular=False):

# sort the tags by size and word length
tag_list.sort(key=lambda tag: len(tag['tag']))
tag_list.sort(key=lambda tag: tag['size'])
Expand All @@ -281,23 +280,23 @@ def _draw_cloud(
tag_sprite = Tag(tag, (0, 0), fontname=fontname)
area += tag_sprite.mask.count()
tag_sprites.append(tag_sprite)

canvas = Rect(0, 0, 0, 0)
ratio = float(size[1]) / size[0]

if rectangular:
spiral = _rectangular_spiral
else:
spiral = _archimedean_spiral

aligned_tags = Group()
for tag_sprite in tag_sprites:
angle = 0
if layout == LAYOUT_MIX and randint(0, 2) == 0:
angle = 90
elif layout == LAYOUT_VERTICAL:
angle = 90

tag_sprite.rotate(angle)

xpos = canvas.width - tag_sprite.rect.width
Expand All @@ -310,9 +309,9 @@ def _draw_cloud(
ypos = randint(int(ypos * LOWER_START), int(ypos * UPPER_START))
tag_sprite.rect.y = ypos

_search_place(tag_sprite, aligned_tags, canvas, spiral, ratio)
_search_place(tag_sprite, aligned_tags, canvas, spiral, ratio)
canvas = _get_tags_bounding(aligned_tags)

# resize cloud
zoom = min(float(size[0]) / canvas.w, float(size[1]) / canvas.h)

Expand All @@ -323,9 +322,9 @@ def _draw_cloud(
tag.rect.height *= zoom
tag.tag['size'] = int(tag.tag['size'] * zoom)
tag.update_fontsize()

canvas = _get_tags_bounding(aligned_tags)

return canvas, aligned_tags

def create_tag_image(
Expand All @@ -339,23 +338,20 @@ def create_tag_image(
"""
Create a png tag cloud image
"""

if not len(tags):
return

sizeRect, tag_store = _draw_cloud(tags,
layout,
size=size,
fontname=fontname,
rectangular=rectangular)

if type(output) == pygame.Surface:
image_surface = output
else:
image_surface = Surface((sizeRect.w + 2*TAG_CLOUD_PADDING, sizeRect.h + 2*TAG_CLOUD_PADDING), SRCALPHA, 32)
image_surface.fill(background)

image_surface = Surface((sizeRect.w, sizeRect.h), SRCALPHA, 32)
image_surface.fill(background)
for tag in tag_store:
image_surface.blit(tag.image, (tag.rect.x - sizeRect.x + TAG_CLOUD_PADDING, tag.rect.y - sizeRect.y + TAG_CLOUD_PADDING))
image_surface.blit(tag.image, tag.rect)
pygame.image.save(image_surface, output)

def create_html_data(tags,
Expand All @@ -366,23 +362,23 @@ def create_html_data(tags,
"""
Create data structures to be used for HTML tag clouds.
"""

if not len(tags):
return

sizeRect, tag_store = _draw_cloud(tags,
layout,
size=size,
fontname=fontname,
rectangular=rectangular)

tag_store = sorted(tag_store, key=lambda tag: tag.tag['size'])
tag_store.reverse()
data = {
'css': {},
'links': []
}

color_map = {}
for color_index, tag in enumerate(tags):
if not color_map.has_key(tag['color']):
Expand All @@ -401,11 +397,9 @@ def create_html_data(tags,

for stag in tag_store:
line_offset = 0

line_offset = stag.font.get_linesize() - (stag.font.get_ascent() + \
abs(stag.font.get_descent()) - \
stag.rect.height) - 4


line_offset = stag.font.get_linesize() - (stag.font.get_ascent() + abs(stag.font.get_descent()) - stag.rect.height) - 4

tag = {
'tag': stag.tag['tag'],
'cls': color_map[stag.tag['color']],
Expand All @@ -416,8 +410,8 @@ def create_html_data(tags,
'width': stag.rect.width,
'lh': line_offset
}

data['links'].append(tag)
data['size'] = (sizeRect.w, sizeRect.h * 1.15)

return data