From 822f3c5fcaca1f60dd9ea89060f666099fdb99e4 Mon Sep 17 00:00:00 2001 From: Darafei Praliaskouski Date: Tue, 24 Sep 2013 20:05:34 +0300 Subject: [PATCH] performance refactoring --- src/komap.py | 3 + src/libkomwm.py | 9 +-- src/mapcss/Rule.py | 10 +++ src/mapcss/StyleChooser.py | 125 +++++++++++++++++++++---------------- src/mapcss/__init__.py | 39 ++++++------ 5 files changed, 105 insertions(+), 81 deletions(-) diff --git a/src/komap.py b/src/komap.py index a7e1e5a..71b38f5 100755 --- a/src/komap.py +++ b/src/komap.py @@ -18,6 +18,9 @@ from debug import debug, Timer from mapcss import MapCSS +import gc +gc.disable() + import mapcss.webcolors whatever_to_hex = mapcss.webcolors.webcolors.whatever_to_hex diff --git a/src/libkomwm.py b/src/libkomwm.py index 6672652..e28fb37 100644 --- a/src/libkomwm.py +++ b/src/libkomwm.py @@ -9,7 +9,6 @@ whatever_to_cairo = mapcss.webcolors.webcolors.whatever_to_cairo WIDTH_SCALE = 1.0 - def komap_mapswithme(options, style): if options.outfile == "-": print "Please specify base output path." @@ -58,8 +57,6 @@ def komap_mapswithme(options, style): if prefix: prefix += "-" opacity = hex(255 - int(255 * float(st.get(prefix + "opacity", 1)))) - if opacity == "0x0": - opacity = "0x" color = whatever_to_hex(st.get(prefix + 'color', default)) color = color[1] + color[1] + color[3] + color[3] + color[5] + color[5] return int(opacity + color, 16) @@ -84,20 +81,20 @@ def komap_mapswithme(options, style): zstyle = {} if "area" not in txclass: - zstyle = style.get_style_dict("line", txclass, zoom, olddict=zstyle) + zstyle = style.get_style_dict("line", txclass, zoom, olddict=zstyle, cache=False) # for st in zstyle: # if "fill-color" in st: # del st["fill-color"] if True: - areastyle = style.get_style_dict("area", txclass, zoom, olddict=zstyle) + areastyle = style.get_style_dict("area", txclass, zoom, olddict=zstyle, cache=False) for st in areastyle.values(): if "icon-image" in st or 'symbol-shape' in st: has_icons_for_areas = True zstyle = areastyle if "area" not in txclass: - nodestyle = style.get_style_dict("node", txclass, zoom, olddict=zstyle) + nodestyle = style.get_style_dict("node", txclass, zoom, olddict=zstyle, cache=False) # for st in nodestyle: # if "fill-color" in st: # del st["fill-color"] diff --git a/src/mapcss/Rule.py b/src/mapcss/Rule.py index 7229e19..2a30bd3 100644 --- a/src/mapcss/Rule.py +++ b/src/mapcss/Rule.py @@ -15,6 +15,13 @@ # You should have received a copy of the GNU General Public License # along with kothic. If not, see . +type_matches = { + "": ('area', 'line', 'way', 'node'), + "area": ("area", "way"), + "node": ("node",), + "way": ("line", "area", "way"), + "line": ("line", "area"), + } class Rule(): def __init__(self, s=''): @@ -49,6 +56,9 @@ class Rule(): def test_zoom(self, zoom): return (zoom >= self.minZoom) and (zoom <= self.maxZoom) + def get_compatible_types(self): + return type_matches.get(self.subject, (self.subject,)) + def get_interesting_tags(self, obj, zoom): if obj: if (self.subject != '') and not _test_feature_compatibility(obj, self.subject, {}): diff --git a/src/mapcss/StyleChooser.py b/src/mapcss/StyleChooser.py index 6e89a5c..f876d92 100644 --- a/src/mapcss/StyleChooser.py +++ b/src/mapcss/StyleChooser.py @@ -22,22 +22,57 @@ from webcolors.webcolors import cairo_to_hex from Eval import Eval +def make_nice_style(r): + ra = {} + for a, b in r.iteritems(): + "checking and nicifying style table" + if type(b) == type(Eval()): + ra[a] = b + elif "color" in a: + "parsing color value to 3-tuple" + # print "res:", b + if b and (type(b) != tuple): + # if not b: + # print sl, ftype, tags, zoom, scale, zscale + # else: + ra[a] = colorparser(b) + elif b: + ra[a] = b + elif any(x in a for x in ("width", "z-index", "opacity", "offset", "radius", "extrude")): + "these things are float's or not in table at all" + try: + ra[a] = float(b) + except ValueError: + pass + elif "dashes" in a and type(b) != list: + "these things are arrays of float's or not in table at all" + try: + b = b.split(",") + b = [float(x) for x in b] + ra[a] = b + except ValueError: + ra[a] = [] + else: + ra[a] = b + return ra + + class StyleChooser: """ - A StyleChooser object is equivalent to one CSS selector+declaration. + A StyleChooser object is equivalent to one CSS selector+declaration. - Its ruleChains property is an array of all the selectors, which would - traditionally be comma-separated. For example: - h1, h2, h3 em - is three ruleChains. + Its ruleChains property is an array of all the selectors, which would + traditionally be comma-separated. For example: + h1, h2, h3 em + is three ruleChains. - Each ruleChain is itself an array of nested selectors. So the above - example would roughly be encoded as: - [[h1],[h2],[h3,em]] - ^^ ^^ ^^ ^^ each of these is a Rule + Each ruleChain is itself an array of nested selectors. So the above + example would roughly be encoded as: + [[h1],[h2],[h3,em]] + ^^ ^^ ^^ ^^ each of these is a Rule - The styles property is an array of all the style objects to be drawn - if any of the ruleChains evaluate to true. + The styles property is an array of all the style objects to be drawn + if any of the ruleChains evaluate to true. """ def __repr__(self): return "{(%s) : [%s] }\n" % (self.ruleChains, self.styles) @@ -48,6 +83,8 @@ class StyleChooser: self.eval_type = type(Eval()) self.scalepair = scalepair self.selzooms = None + self.compatible_types = set() + self.has_evals = False def get_numerics(self): """ @@ -103,6 +140,9 @@ class StyleChooser: if zoom < self.selzooms[0] or zoom > self.selzooms[1]: return sl + #if ftype not in self.compatible_types: +# return sl + object_id = self.testChain(self.ruleChains, ftype, tags, zoom) if not object_id: @@ -111,50 +151,24 @@ class StyleChooser: w = 0 for r in self.styles: - ra = {} - for a, b in r.iteritems(): - "calculating eval()'s" - if type(b) == self.eval_type: - combined_style = {} - for t in sl: - combined_style.update(t) - for p, q in combined_style.iteritems(): - if "color" in p: - combined_style[p] = cairo_to_hex(q) - b = b.compute(tags, combined_style, scale, zscale) - ra[a] = b - r = ra - ra = {} - - for a, b in r.iteritems(): - "checking and nicifying style table" - if "color" in a: - "parsing color value to 3-tuple" - # print "res:", b - if b: - # if not b: - # print sl, ftype, tags, zoom, scale, zscale - # else: - ra[a] = colorparser(b) - elif any(x in a for x in ("width", "z-index", "opacity", "offset", "radius", "extrude")): - "these things are float's or not in table at all" - try: - ra[a] = float(b) - except ValueError: - pass - elif "dashes" in a: - "these things are arrays of float's or not in table at all" - try: - b = b.split(",") - b = [float(x) for x in b] - ra[a] = b - except ValueError: - ra[a] = [] - else: + if self.has_evals: + ra = {} + for a, b in r.iteritems(): + "calculating eval()'s" + if type(b) == self.eval_type: + combined_style = {} + for t in sl: + combined_style.update(t) + for p, q in combined_style.iteritems(): + if "color" in p: + combined_style[p] = cairo_to_hex(q) + b = b.compute(tags, combined_style, scale, zscale) ra[a] = b - # for k,v in ra.items(): # if a value is empty, we don't need it - renderer will do as default. - # if not v: - # del ra[k] + #r = ra + ra = make_nice_style(ra) + else: + ra = r.copy() + ra["object-id"] = str(object_id) hasall = False allinit = {} @@ -224,6 +238,7 @@ class StyleChooser: else: self.selzooms[0] = min(self.selzooms[0], r.minZoom) self.selzooms[1] = max(self.selzooms[1], r.maxZoom) + self.compatible_types.update(r.get_compatible_types()) rb = [] for r in a: ra = {} @@ -242,6 +257,8 @@ class StyleChooser: b = "eval(tag(\"" + b + "\"))" if b[:5] == "eval(": b = Eval(b) + self.has_evals = True ra[a] = b + ra = make_nice_style(ra) rb.append(ra) self.styles = self.styles + rb diff --git a/src/mapcss/__init__.py b/src/mapcss/__init__.py index 29e6dd5..c421e51 100644 --- a/src/mapcss/__init__.py +++ b/src/mapcss/__init__.py @@ -85,15 +85,6 @@ CENTER = re.compile(r'^center$/i') HEX = re.compile(r'^#([0-9a-f]+)$/i') - -builtin_style = """ -canvas {fill-color: #cccccc} -way {width: 1; casing-width:1; casing-color: white} -""" - - - ## ** also needs to support @import rules - class MapCSS(): def __init__(self, minscale=0, maxscale=19): """ @@ -104,9 +95,8 @@ class MapCSS(): self.maxscale = maxscale self.scalepair = (minscale, maxscale) self.choosers = [] + self.choosers_by_type = {} self.style_loaded = False - self.parse(builtin_style) - self.style_loaded = False # override one after loading def parseZoom(self, s): if ZOOM_MINMAX.match(s): @@ -120,15 +110,16 @@ class MapCSS(): else: logging.error("unparsed zoom: %s" % s) - def get_style(self, type, tags={}, zoom=0, scale=1, zscale=.5): + def get_style(self, type, tags={}, zoom=0, scale=1, zscale=.5, cache=True): """ Kothic styling API """ - shash = md5(repr(type) + repr(tags) + repr(zoom)).digest() - if shash in self.cache["style"]: - return deepcopy(self.cache["style"][shash]) + if cache: + shash = md5(repr(type) + repr(tags) + repr(zoom)).digest() + if shash in self.cache["style"]: + return deepcopy(self.cache["style"][shash]) style = [] - for chooser in self.choosers: + for chooser in self.choosers_by_type[type]: style = chooser.updateStyles(style, type, tags, zoom, scale, zscale) style = [x for x in style if x["object-id"] != "::*"] st = [] @@ -143,11 +134,12 @@ class MapCSS(): st.append(x) style = st - self.cache["style"][shash] = style - return deepcopy(style) + if cache: + self.cache["style"][shash] = deepcopy(style) + return style - def get_style_dict(self, type, tags={}, zoom=0, scale=1, zscale=.5, olddict={}): - r = self.get_style(type, tags, zoom, scale, zscale) + def get_style_dict(self, type, tags={}, zoom=0, scale=1, zscale=.5, olddict={}, cache=True): + r = self.get_style(type, tags, zoom, scale, zscale, cache) d = olddict for x in r: if x.get('object-id', '') not in d: @@ -315,6 +307,12 @@ class MapCSS(): except TypeError: pass + for chooser in self.choosers: + for t in chooser.compatible_types: + if t not in self.choosers_by_type: + self.choosers_by_type[t] = [chooser] + else: + self.choosers_by_type[t].append(chooser) def parseCondition(s): @@ -347,7 +345,6 @@ def parseCondition(s): a = CONDITION_NE.match(s).groups() log.debug("condition NE: %s = %s" % (a[0], a[1])) return Condition('ne', a) - ## FIXME: convert other conditions to python if CONDITION_LE.match(s): a = CONDITION_LE.match(s).groups()