Browse Source

Add french maps

pull/98/head
Florian Mounier 11 years ago
parent
commit
4fc5621e0d
  1. 65
      demo/moulinrouge/tests.py
  2. 8
      pygal/__init__.py
  3. 4
      pygal/css/style.css
  4. 13
      pygal/ghost.py
  5. 3
      pygal/graph/__init__.py
  6. 328
      pygal/graph/fr.departments.svg
  7. 91
      pygal/graph/fr.regions.svg
  8. 270
      pygal/graph/frenchmap.py
  9. 368
      pygal/graph/worldmap.svg
  10. 2
      setup.py

65
demo/moulinrouge/tests.py

@ -2,8 +2,11 @@
# This file is part of pygal
from pygal import (
Bar, Gauge, Pyramid, Funnel, Dot, StackedBar, XY,
CHARTS_BY_NAME, Config, Line, DateY, Worldmap, Histogram, Box)
CHARTS_BY_NAME, Config, Line, DateY, Worldmap, Histogram, Box,
FrenchMap_Departments, FrenchMap_Regions)
from pygal.style import styles
from pygal.graph.frenchmap import DEPARTMENTS, REGIONS
from random import randint, choice
def get_test_routes(app):
@ -329,14 +332,13 @@ def get_test_routes(app):
@app.route('/test/worldmap')
def test_worldmap():
import random
map = Worldmap(style=random.choice(styles.values()))
map.add('1st', [('fr', 100), ('us', 10)])
map.add('2nd', [('jp', 1), ('ru', 7), ('uk', 0)])
map.add('3rd', ['ch', 'cz', 'ca', 'cn'])
map.add('4th', {'br': 12, 'bo': 1, 'bu': 23, 'fr': 34})
map.add('5th', [{
wmap = Worldmap(style=choice(list(styles.values())))
wmap.add('1st', [('fr', 100), ('us', 10)])
wmap.add('2nd', [('jp', 1), ('ru', 7), ('uk', 0)])
wmap.add('3rd', ['ch', 'cz', 'ca', 'cn'])
wmap.add('4th', {'br': 12, 'bo': 1, 'bu': 23, 'fr': 34})
wmap.add('5th', [{
'value': ('tw', 10),
'label': 'First label',
'xlink': 'http://google.com?q=tw'
@ -348,8 +350,47 @@ def get_test_routes(app):
'value': ('mw', 40),
'label': 'Last'
}])
map.add('6th', [3, 5, 34, 12])
map.title = 'World Map !!'
return map.render_response()
wmap.add('6th', [3, 5, 34, 12])
wmap.title = 'World Map !!'
return wmap.render_response()
@app.route('/test/frenchmapdepartments')
def test_frenchmapdepartments():
fmap = FrenchMap_Departments(style=choice(list(styles.values())))
for i in range(10):
fmap.add('s%d' % i, [
(choice(list(DEPARTMENTS.keys())), randint(0, 100)) for _ in range(randint(1, 5))])
fmap.add('links', [{
'value': ('69', 10),
'label': '\o/',
'xlink': 'http://google.com?q=69'
}, {
'value': ('42', 20),
'label': 'Y',
}])
fmap.add('6th', [3, 5, 34, 12])
fmap.title = 'French map'
return fmap.render_response()
@app.route('/test/frenchmapregions')
def test_frenchmapregions():
fmap = FrenchMap_Regions(style=choice(list(styles.values())))
for i in range(10):
fmap.add('s%d' % i, [
(choice(list(REGIONS.keys())), randint(0, 100))
for _ in range(randint(1, 5))])
fmap.add('links', [{
'value': ('02', 10),
'label': '\o/',
'xlink': 'http://google.com?q=69'
}, {
'value': ('72', 20),
'label': 'Y',
}])
fmap.add('6th', [91, 2, 41])
fmap.title = 'French map'
return fmap.render_response()
return filter(lambda x: x.startswith('test'), locals())

8
pygal/__init__.py

@ -24,17 +24,17 @@ Pygal - A python svg graph plotting library
__version__ = '1.3.1'
import sys
from pygal.config import Config
from pygal.ghost import Ghost
from pygal.graph import CHARTS_NAMES
from pygal.ghost import Ghost, REAL_CHARTS
CHARTS = []
CHARTS_BY_NAME = {}
for NAME in CHARTS_NAMES:
for NAME in REAL_CHARTS.keys():
_CHART = type(NAME, (Ghost,), {})
CHARTS.append(_CHART)
CHARTS_BY_NAME[NAME] = _CHART
setattr(sys.modules[__name__], NAME, _CHART)
__all__ = CHARTS_NAMES + [Config.__name__, 'CHARTS', 'CHARTS_BY_NAME']
__all__ = list(CHARTS_BY_NAME.keys()) + [
Config.__name__, 'CHARTS', 'CHARTS_BY_NAME']

4
pygal/css/style.css

@ -113,7 +113,7 @@
fill: {{ style.foreground_light }};
}
{{ id }}.country {
{{ id }}.map-element {
fill: {{ style.foreground }};
stroke: {{ style.plot_background }} !important;
opacity: .9;
@ -124,7 +124,7 @@
transition: 250ms;
}
{{ id }}.country:hover {
{{ id }}.map-element:hover {
opacity: 1;
stroke-width: 10;
}

13
pygal/ghost.py

@ -33,12 +33,23 @@ from pygal.util import prepare_values
from uuid import uuid4
class ChartCollection(object):
pass
REAL_CHARTS = {}
for NAME in CHARTS_NAMES:
mod_name = 'pygal.graph.%s' % NAME.lower()
__import__(mod_name)
mod = sys.modules[mod_name]
REAL_CHARTS[NAME] = getattr(mod, NAME)
chart = getattr(mod, NAME)
if issubclass(chart, ChartCollection):
for name, chart in chart.__dict__.items():
if name.startswith('_'):
continue
REAL_CHARTS['%s_%s' % (NAME, name)] = chart
else:
REAL_CHARTS[NAME] = chart
class Ghost(object):

3
pygal/graph/__init__.py

@ -40,5 +40,6 @@ CHARTS_NAMES = [
'Worldmap',
'SupranationalWorldmap',
'Histogram',
'Box'
'Box',
'FrenchMap'
]

328
pygal/graph/fr.departments.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 584 KiB

91
pygal/graph/fr.regions.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 370 KiB

270
pygal/graph/frenchmap.py

@ -0,0 +1,270 @@
# -*- coding: utf-8 -*-
# This file is part of pygal
#
# A python svg graph plotting library
# Copyright © 2012-2014 Kozea
#
# This library is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
Worldmap chart
"""
from __future__ import division
from pygal.ghost import ChartCollection
from pygal.util import cut, cached_property, decorate
from pygal.graph.graph import Graph
from pygal._compat import u
from numbers import Number
from lxml import etree
import os
DEPARTMENTS = {
'01': u("Ain"),
'02': u("Aisne"),
'03': u("Allier"),
'04': u("Alpes-de-Haute-Provence"),
'05': u("Hautes-Alpes"),
'06': u("Alpes-Maritimes"),
'07': u("Ardèche"),
'08': u("Ardennes"),
'09': u("Ariège"),
'10': u("Aube"),
'11': u("Aude"),
'12': u("Aveyron"),
'13': u("Bouches-du-RhĂ´ne"),
'14': u("Calvados"),
'15': u("Cantal"),
'16': u("Charente"),
'17': u("Charente-Maritime"),
'18': u("Cher"),
'19': u("Corrèze"),
'2A': u("Corse-du-Sud"),
'2B': u("Haute-Corse"),
'21': u("CĂ´te-d'Or"),
'22': u("CĂ´tes-d'Armor"),
'23': u("Creuse"),
'24': u("Dordogne"),
'25': u("Doubs"),
'26': u("DrĂ´me"),
'27': u("Eure"),
'28': u("Eure-et-Loir"),
'29': u("Finistère"),
'30': u("Gard"),
'31': u("Haute-Garonne"),
'32': u("Gers"),
'33': u("Gironde"),
'34': u("HĂ©rault"),
'35': u("Ille-et-Vilaine"),
'36': u("Indre"),
'37': u("Indre-et-Loire"),
'38': u("Isère"),
'39': u("Jura"),
'40': u("Landes"),
'41': u("Loir-et-Cher"),
'42': u("Loire"),
'43': u("Haute-Loire"),
'44': u("Loire-Atlantique"),
'45': u("Loiret"),
'46': u("Lot"),
'47': u("Lot-et-Garonne"),
'48': u("Lozère"),
'49': u("Maine-et-Loire"),
'50': u("Manche"),
'51': u("Marne"),
'52': u("Haute-Marne"),
'53': u("Mayenne"),
'54': u("Meurthe-et-Moselle"),
'55': u("Meuse"),
'56': u("Morbihan"),
'57': u("Moselle"),
'58': u("Nièvre"),
'59': u("Nord"),
'60': u("Oise"),
'61': u("Orne"),
'62': u("Pas-de-Calais"),
'63': u("Puy-de-DĂ´me"),
'64': u("Pyrénées-Atlantiques"),
'65': u("Hautes-Pyrénées"),
'66': u("Pyrénées-Orientales"),
'67': u("Bas-Rhin"),
'68': u("Haut-Rhin"),
'69': u("RhĂ´ne"),
'70': u("Haute-SaĂ´ne"),
'71': u("SaĂ´ne-et-Loire"),
'72': u("Sarthe"),
'73': u("Savoie"),
'74': u("Haute-Savoie"),
'75': u("Paris"),
'76': u("Seine-Maritime"),
'77': u("Seine-et-Marne"),
'78': u("Yvelines"),
'79': u("Deux-Sèvres"),
'80': u("Somme"),
'81': u("Tarn"),
'82': u("Tarn-et-Garonne"),
'83': u("Var"),
'84': u("Vaucluse"),
'85': u("Vendée"),
'86': u("Vienne"),
'87': u("Haute-Vienne"),
'88': u("Vosges"),
'89': u("Yonne"),
'90': u("Territoire de Belfort"),
'91': u("Essonne"),
'92': u("Hauts-de-Seine"),
'93': u("Seine-Saint-Denis"),
'94': u("Val-de-Marne"),
'95': u("Val-d'Oise"),
'971': u("Guadeloupe"),
'972': u("Martinique"),
'973': u("Guyane"),
'974': u("RĂ©union"),
# Not a area anymore but in case of...
'975': u("Saint Pierre et Miquelon"),
'976': u("Mayotte")
}
REGIONS = {
'11': u("ĂŽle-de-France"),
'21': u("Champagne-Ardenne"),
'22': u("Picardie"),
'23': u("Haute-Normandie"),
'24': u("Centre"),
'25': u("Basse-Normandie"),
'26': u("Bourgogne"),
'31': u("Nord-Pas-de-Calais"),
'41': u("Lorraine"),
'42': u("Alsace"),
'43': u("Franche-Comté"),
'52': u("Pays-de-la-Loire"),
'53': u("Bretagne"),
'54': u("Poitou-Charentes"),
'72': u("Aquitaine"),
'73': u("Midi-Pyrénées"),
'74': u("Limousin"),
'82': u("RhĂ´ne-Alpes"),
'83': u("Auvergne"),
'91': u("Languedoc-Roussillon"),
'93': u("Provence-Alpes-CĂ´te d'Azur"),
'94': u("Corse"),
'01': u("Guadeloupe"),
'02': u("Martinique"),
'03': u("Guyane"),
'04': u("RĂ©union"),
# Not a region anymore but in case of...
'05': u("Saint Pierre et Miquelon"),
'06': u("Mayotte")
}
with open(os.path.join(
os.path.dirname(__file__),
'fr.departments.svg')) as file:
DPT_MAP = file.read()
with open(os.path.join(
os.path.dirname(__file__),
'fr.regions.svg')) as file:
REG_MAP = file.read()
class FrenchMapDepartments(Graph):
"""French department map"""
_dual = True
x_labels = list(DEPARTMENTS.keys())
area_names = DEPARTMENTS
area_prefix = 'z'
svg_map = DPT_MAP
@cached_property
def _values(self):
"""Getter for series values (flattened)"""
return [val[1]
for serie in self.series
for val in serie.values
if val[1] is not None]
def _plot(self):
map = etree.fromstring(self.svg_map)
map.set('width', str(self.view.width))
map.set('height', str(self.view.height))
for i, serie in enumerate(self.series):
safe_vals = list(filter(
lambda x: x is not None, cut(serie.values, 1)))
if not safe_vals:
continue
min_ = min(safe_vals)
max_ = max(safe_vals)
for j, (area_code, value) in enumerate(serie.values):
if isinstance(area_code, Number):
area_code = '%2d' % area_code
if value is None:
continue
if max_ == min_:
ratio = 1
else:
ratio = .3 + .7 * (value - min_) / (max_ - min_)
areae = map.xpath(
"//*[contains(concat(' ', normalize-space(@class), ' '),"
" ' %s%s ')]" % (self.area_prefix, area_code))
if not areae:
continue
for area in areae:
cls = area.get('class', '').split(' ')
cls.append('color-%d' % i)
area.set('class', ' '.join(cls))
area.set('style', 'fill-opacity: %f' % (ratio))
metadata = serie.metadata.get(j)
if metadata:
parent = area.getparent()
node = decorate(self.svg, area, metadata)
if node != area:
area.remove(node)
index = parent.index(area)
parent.remove(area)
node.append(area)
parent.insert(index, node)
last_node = len(area) > 0 and area[-1]
if last_node is not None and last_node.tag == 'title':
title_node = last_node
text = title_node.text + '\n'
else:
title_node = self.svg.node(area, 'title')
text = ''
title_node.text = text + '[%s] %s: %d' % (
serie.title,
self.area_names[area_code], value)
self.nodes['plot'].append(map)
class FrenchMapRegions(FrenchMapDepartments):
"""French regions map"""
x_labels = list(REGIONS.keys())
area_names = REGIONS
area_prefix = 'a'
svg_map = REG_MAP
class FrenchMap(ChartCollection):
Regions = FrenchMapRegions
Departments = FrenchMapDepartments

368
pygal/graph/worldmap.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 752 KiB

After

Width:  |  Height:  |  Size: 754 KiB

2
setup.py

@ -64,7 +64,7 @@ setup(
"svg", "chart", "graph", "diagram", "plot", "histogram", "kiviat"],
tests_require=["pytest", "pyquery", "flask", "cairosvg"],
cmdclass={'test': PyTest},
package_data={'pygal': ['css/*', 'graph/worldmap.svg']},
package_data={'pygal': ['css/*', 'graph/*.svg']},
install_requires=['lxml'],
classifiers=[
"Development Status :: 4 - Beta",

Loading…
Cancel
Save