Speed optimizations #8
5 changed files with 129 additions and 146 deletions
|
@ -72,18 +72,31 @@ def query_style(args):
|
|||
|
||||
results = []
|
||||
for zoom in range(minzoom, maxzoom + 1):
|
||||
runtime_conditions_arr = []
|
||||
|
||||
all_runtime_conditions_arr = []
|
||||
# Get runtime conditions which are used for class 'cl' on zoom 'zoom'
|
||||
if "area" not in cltags:
|
||||
runtime_conditions_arr.extend(style.get_runtime_rules(clname, "line", cltags, zoom))
|
||||
runtime_conditions_arr.extend(style.get_runtime_rules(clname, "area", cltags, zoom))
|
||||
all_runtime_conditions_arr.extend(style.get_runtime_rules(clname, "line", cltags, zoom))
|
||||
all_runtime_conditions_arr.extend(style.get_runtime_rules(clname, "area", cltags, zoom))
|
||||
if "area" not in cltags:
|
||||
runtime_conditions_arr.extend(style.get_runtime_rules(clname, "node", cltags, zoom))
|
||||
all_runtime_conditions_arr.extend(style.get_runtime_rules(clname, "node", cltags, zoom))
|
||||
|
||||
# If there is no any runtime conditions, do not filter style by runtime conditions
|
||||
if len(runtime_conditions_arr) == 0:
|
||||
runtime_conditions_arr = []
|
||||
if len(all_runtime_conditions_arr) == 0:
|
||||
# If there is no runtime conditions, do not filter style by runtime conditions
|
||||
runtime_conditions_arr.append(None)
|
||||
elif len(all_runtime_conditions_arr) == 1:
|
||||
runtime_conditions_arr = all_runtime_conditions_arr
|
||||
else:
|
||||
# Keep unique conditions only
|
||||
runtime_conditions_arr.append(all_runtime_conditions_arr.pop(0))
|
||||
for new_rt_conditions in all_runtime_conditions_arr:
|
||||
conditions_unique = True
|
||||
for rt_conditions in runtime_conditions_arr:
|
||||
if new_rt_conditions == rt_conditions:
|
||||
conditions_unique = False
|
||||
break
|
||||
if conditions_unique:
|
||||
runtime_conditions_arr.append(new_rt_conditions)
|
||||
|
||||
for runtime_conditions in runtime_conditions_arr:
|
||||
has_icons_for_areas = False
|
||||
|
@ -224,21 +237,24 @@ def komap_mapswithme(options):
|
|||
|
||||
# Parse style mapcss
|
||||
global style
|
||||
style = MapCSS(options.minzoom, options.maxzoom + 1)
|
||||
style = MapCSS(options.minzoom, options.maxzoom)
|
||||
style.parse(filename=options.filename, static_tags=mapcss_static_tags,
|
||||
dynamic_tags=mapcss_dynamic_tags)
|
||||
|
||||
# Build optimization tree - class/type -> StyleChoosers
|
||||
# Build optimization tree - class/zoom/type -> StyleChoosers
|
||||
clname_cltag_unique = set()
|
||||
for cl in class_order:
|
||||
clname = cl if cl.find('-') == -1 else cl[:cl.find('-')]
|
||||
# Get first tag of the class/type.
|
||||
cltag = next(iter(classificator[cl].keys()))
|
||||
style.build_choosers_tree(clname, "line", cltag)
|
||||
style.build_choosers_tree(clname, "area", cltag)
|
||||
style.build_choosers_tree(clname, "node", cltag)
|
||||
style.restore_choosers_order("line")
|
||||
style.restore_choosers_order("area")
|
||||
style.restore_choosers_order("node")
|
||||
clname_cltag = clname + '$' + cltag
|
||||
if clname_cltag not in clname_cltag_unique:
|
||||
clname_cltag_unique.add(clname_cltag)
|
||||
style.build_choosers_tree(clname, "line", cltag)
|
||||
style.build_choosers_tree(clname, "area", cltag)
|
||||
style.build_choosers_tree(clname, "node", cltag)
|
||||
|
||||
style.finalize_choosers_tree()
|
||||
|
||||
# Get colors section from style
|
||||
style_colors = {}
|
||||
|
|
|
@ -33,42 +33,44 @@ class Condition:
|
|||
|
||||
def test(self, tags):
|
||||
"""
|
||||
Test a hash against this condition
|
||||
Test tags against this condition
|
||||
"""
|
||||
t = self.type
|
||||
params = self.params
|
||||
if t == 'eq': # don't compare tags against sublayers
|
||||
|
||||
if t == 'eq':
|
||||
# Don't compare tags against sublayers
|
||||
if params[0][:2] == "::":
|
||||
return params[1]
|
||||
try:
|
||||
if t == 'eq':
|
||||
return tags[params[0]] == params[1]
|
||||
if t == 'ne':
|
||||
return tags.get(params[0], "") != params[1]
|
||||
if t == 'regex':
|
||||
return bool(self.regex.match(tags[params[0]]))
|
||||
if t == 'true':
|
||||
return tags.get(params[0]) == 'yes'
|
||||
if t == 'untrue':
|
||||
return tags.get(params[0]) == 'no'
|
||||
if t == 'set':
|
||||
if params[0] in tags:
|
||||
return tags[params[0]] != ''
|
||||
return False
|
||||
if t == 'unset':
|
||||
if params[0] in tags:
|
||||
return tags[params[0]] == ''
|
||||
return True
|
||||
if t == '<':
|
||||
return (Number(tags[params[0]]) < Number(params[1]))
|
||||
if t == '<=':
|
||||
return (Number(tags[params[0]]) <= Number(params[1]))
|
||||
if t == '>':
|
||||
return (Number(tags[params[0]]) > Number(params[1]))
|
||||
if t == '>=':
|
||||
return (Number(tags[params[0]]) >= Number(params[1]))
|
||||
except KeyError:
|
||||
pass
|
||||
return (params[0] in tags and tags[params[0]] == params[1])
|
||||
if t == 'ne':
|
||||
return (params[0] not in tags or tags[params[0]] != params[1])
|
||||
if t == 'true':
|
||||
return tags.get(params[0]) == 'yes'
|
||||
if t == 'untrue':
|
||||
return tags.get(params[0]) == 'no'
|
||||
if t == 'set':
|
||||
if params[0] in tags:
|
||||
return tags[params[0]] != ''
|
||||
return False
|
||||
if t == 'unset':
|
||||
if params[0] in tags:
|
||||
return tags[params[0]] == ''
|
||||
return True
|
||||
|
||||
if params[0] not in tags:
|
||||
return False
|
||||
if t == 'regex':
|
||||
return bool(self.regex.match(tags[params[0]]))
|
||||
if t == '<':
|
||||
return (Number(tags[params[0]]) < Number(params[1]))
|
||||
if t == '<=':
|
||||
return (Number(tags[params[0]]) <= Number(params[1]))
|
||||
if t == '>':
|
||||
return (Number(tags[params[0]]) > Number(params[1]))
|
||||
if t == '>=':
|
||||
return (Number(tags[params[0]]) >= Number(params[1]))
|
||||
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
|
|
|
@ -25,7 +25,7 @@ type_matches = {
|
|||
|
||||
class Rule():
|
||||
def __init__(self, s=''):
|
||||
self.runtime_conditions = []
|
||||
self.runtime_conditions = None
|
||||
self.conditions = []
|
||||
# self.isAnd = True
|
||||
self.minZoom = 0
|
||||
|
@ -33,17 +33,12 @@ class Rule():
|
|||
if s == "*":
|
||||
s = ""
|
||||
self.subject = s # "", "way", "node" or "relation"
|
||||
self.type_matches = type_matches[s] if s in type_matches else set()
|
||||
|
||||
def __repr__(self):
|
||||
return "%s|z%s-%s %s %s" % (self.subject, self.minZoom, self.maxZoom, self.conditions, self.runtime_conditions)
|
||||
|
||||
def test(self, obj, tags, zoom):
|
||||
if (zoom < self.minZoom) or (zoom > self.maxZoom):
|
||||
return False
|
||||
|
||||
if (self.subject != '') and not _test_feature_compatibility(obj, self.subject, tags):
|
||||
return False
|
||||
|
||||
def test(self, tags):
|
||||
subpart = "::default"
|
||||
for condition in self.conditions:
|
||||
res = condition.test(tags)
|
||||
|
@ -66,28 +61,3 @@ class Rule():
|
|||
return set(["*"])
|
||||
|
||||
return a
|
||||
|
||||
|
||||
def _test_feature_compatibility(f1, f2, tags={}):
|
||||
"""
|
||||
Checks if feature of type f1 is compatible with f2.
|
||||
"""
|
||||
if f2 == f1:
|
||||
return True
|
||||
if f2 not in ("way", "area", "line"):
|
||||
return False
|
||||
elif f2 == "way" and f1 == "line":
|
||||
return True
|
||||
elif f2 == "way" and f1 == "area":
|
||||
return True
|
||||
elif f2 == "area" and f1 in ("way", "area"):
|
||||
# if ":area" in tags:
|
||||
return True
|
||||
# else:
|
||||
# return False
|
||||
elif f2 == "line" and f1 in ("way", "line", "area"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
# print f1, f2, True
|
||||
return True
|
||||
|
|
|
@ -87,6 +87,7 @@ class StyleChooser:
|
|||
self.selzooms = None
|
||||
self.compatible_types = set()
|
||||
self.has_evals = False
|
||||
self.has_runtime_conditions = False
|
||||
self.cached_tags = None
|
||||
|
||||
def extract_tags(self):
|
||||
|
@ -96,62 +97,34 @@ class StyleChooser:
|
|||
for r in self.ruleChains:
|
||||
a.update(r.extract_tags())
|
||||
if "*" in a:
|
||||
a.clear()
|
||||
a.add("*")
|
||||
a = set('*')
|
||||
break
|
||||
if self.has_evals and "*" not in a:
|
||||
for s in self.styles:
|
||||
for v in list(s.values()):
|
||||
if type(v) == self.eval_type:
|
||||
a.update(v.extract_tags())
|
||||
if "*" in a or len(a) == 0:
|
||||
a.clear()
|
||||
a.add("*")
|
||||
if len(a) == 0:
|
||||
a = set('*')
|
||||
self.cached_tags = a
|
||||
return a
|
||||
|
||||
def get_runtime_conditions(self, ftype, tags, zoom):
|
||||
if self.selzooms:
|
||||
if zoom < self.selzooms[0] or zoom > self.selzooms[1]:
|
||||
return None
|
||||
def get_runtime_conditions(self, tags):
|
||||
if not self.has_runtime_conditions:
|
||||
return None
|
||||
|
||||
rule_and_object_id = self.testChain(self.ruleChains, ftype, tags, zoom)
|
||||
rule_and_object_id = self.testChains(tags)
|
||||
|
||||
if not rule_and_object_id:
|
||||
return None
|
||||
|
||||
rule = rule_and_object_id[0]
|
||||
|
||||
if (len(rule.runtime_conditions) == 0):
|
||||
return None
|
||||
|
||||
return rule.runtime_conditions
|
||||
|
||||
def isCorrespondingRule(self, filter_by_runtime_conditions, rule):
|
||||
# If rule can be applied according to runtime conditions, then
|
||||
# function return true, else it returns false
|
||||
if len(rule.runtime_conditions) == 0:
|
||||
return True
|
||||
if filter_by_runtime_conditions is None:
|
||||
return True
|
||||
if filter_by_runtime_conditions == rule.runtime_conditions:
|
||||
return True
|
||||
# Actually we should check rule.runtime_conditions is a subset of filter_by_runtime_conditions
|
||||
for r in rule.runtime_conditions:
|
||||
if r not in filter_by_runtime_conditions:
|
||||
return False
|
||||
return True
|
||||
|
||||
def updateStyles(self, sl, ftype, tags, zoom, xscale, zscale, filter_by_runtime_conditions):
|
||||
def updateStyles(self, sl, tags, xscale, zscale, filter_by_runtime_conditions):
|
||||
# Are any of the ruleChains fulfilled?
|
||||
if self.selzooms:
|
||||
if zoom < self.selzooms[0] or zoom > self.selzooms[1]:
|
||||
return sl
|
||||
|
||||
#if ftype not in self.compatible_types:
|
||||
#return sl
|
||||
|
||||
rule_and_object_id = self.testChain(self.ruleChains, ftype, tags, zoom)
|
||||
rule_and_object_id = self.testChains(tags)
|
||||
|
||||
if not rule_and_object_id:
|
||||
return sl
|
||||
|
@ -159,7 +132,9 @@ class StyleChooser:
|
|||
rule = rule_and_object_id[0]
|
||||
object_id = rule_and_object_id[1]
|
||||
|
||||
if not self.isCorrespondingRule(filter_by_runtime_conditions, rule):
|
||||
if (filter_by_runtime_conditions is not None
|
||||
and rule.runtime_conditions is not None
|
||||
and filter_by_runtime_conditions != rule.runtime_conditions):
|
||||
return sl
|
||||
|
||||
for r in self.styles:
|
||||
|
@ -203,12 +178,12 @@ class StyleChooser:
|
|||
|
||||
return sl
|
||||
|
||||
def testChain(self, chain, obj, tags, zoom):
|
||||
def testChains(self, tags):
|
||||
"""
|
||||
Tests an object against a chain
|
||||
"""
|
||||
for r in chain:
|
||||
tt = r.test(obj, tags, zoom)
|
||||
for r in self.ruleChains:
|
||||
tt = r.test(tags)
|
||||
if tt:
|
||||
return r, tt
|
||||
return False
|
||||
|
@ -249,8 +224,11 @@ class StyleChooser:
|
|||
"""
|
||||
adds into the current ruleChain (existing Rule)
|
||||
"""
|
||||
self.ruleChains[-1].runtime_conditions.append(c)
|
||||
self.ruleChains[-1].runtime_conditions.sort()
|
||||
if self.ruleChains[-1].runtime_conditions is None:
|
||||
self.ruleChains[-1].runtime_conditions = [c]
|
||||
self.has_runtime_conditions = True
|
||||
else:
|
||||
self.ruleChains[-1].runtime_conditions.append(c)
|
||||
|
||||
def addStyles(self, a):
|
||||
# print "addStyle ", a
|
||||
|
|
|
@ -96,7 +96,7 @@ class MapCSS():
|
|||
self.scalepair = (minscale, maxscale)
|
||||
self.choosers = []
|
||||
self.choosers_by_type = {}
|
||||
self.choosers_by_type_and_tag = {}
|
||||
self.choosers_by_type_zoom_tag = {}
|
||||
self.variables = {}
|
||||
self.style_loaded = False
|
||||
|
||||
|
@ -113,44 +113,61 @@ class MapCSS():
|
|||
logging.error("unparsed zoom: %s" % s)
|
||||
|
||||
def build_choosers_tree(self, clname, type, cltag):
|
||||
if type not in self.choosers_by_type_and_tag:
|
||||
self.choosers_by_type_and_tag[type] = {}
|
||||
if clname not in self.choosers_by_type_and_tag[type]:
|
||||
self.choosers_by_type_and_tag[type][clname] = set()
|
||||
if type not in self.choosers_by_type_zoom_tag:
|
||||
self.choosers_by_type_zoom_tag[type] = {}
|
||||
for zoom in range(self.minscale, self.maxscale + 1):
|
||||
if zoom not in self.choosers_by_type_zoom_tag[type]:
|
||||
self.choosers_by_type_zoom_tag[type][zoom] = {}
|
||||
if clname not in self.choosers_by_type_zoom_tag[type][zoom]:
|
||||
self.choosers_by_type_zoom_tag[type][zoom][clname] = {'arr': [], 'set': set()}
|
||||
if type in self.choosers_by_type:
|
||||
for chooser in self.choosers_by_type[type]:
|
||||
for tag in chooser.extract_tags():
|
||||
if tag == "*" or tag == cltag:
|
||||
if chooser not in self.choosers_by_type_and_tag[type][clname]:
|
||||
self.choosers_by_type_and_tag[type][clname].add(chooser)
|
||||
break
|
||||
chooser_tags = chooser.extract_tags()
|
||||
if '*' in chooser_tags or cltag in chooser_tags:
|
||||
for zoom in range(int(chooser.selzooms[0]), int(chooser.selzooms[1]) + 1):
|
||||
if chooser not in self.choosers_by_type_zoom_tag[type][zoom][clname]['set']:
|
||||
self.choosers_by_type_zoom_tag[type][zoom][clname]['arr'].append(chooser)
|
||||
self.choosers_by_type_zoom_tag[type][zoom][clname]['set'].add(chooser)
|
||||
|
||||
def finalize_choosers_tree(self):
|
||||
for ftype in self.choosers_by_type_zoom_tag.keys():
|
||||
for zoom in self.choosers_by_type_zoom_tag[ftype].keys():
|
||||
for clname in self.choosers_by_type_zoom_tag[ftype][zoom].keys():
|
||||
# Discard unneeded unique set of choosers.
|
||||
self.choosers_by_type_zoom_tag[ftype][zoom][clname] = self.choosers_by_type_zoom_tag[ftype][zoom][clname]['arr']
|
||||
for i in range(0, len(self.choosers_by_type_zoom_tag[ftype][zoom][clname])):
|
||||
chooser = self.choosers_by_type_zoom_tag[ftype][zoom][clname][i]
|
||||
optimized = StyleChooser(chooser.scalepair)
|
||||
optimized.styles = chooser.styles
|
||||
optimized.eval_type = chooser.eval_type
|
||||
optimized.has_evals = chooser.has_evals
|
||||
optimized.has_runtime_conditions = chooser.has_runtime_conditions
|
||||
optimized.selzooms = [zoom, zoom]
|
||||
optimized.ruleChains = []
|
||||
for rule in chooser.ruleChains:
|
||||
# Discard chooser's rules that don't match type or zoom.
|
||||
if ftype in rule.type_matches and zoom >= rule.minZoom and zoom <= rule.maxZoom:
|
||||
optimized.ruleChains.append(rule)
|
||||
self.choosers_by_type_zoom_tag[ftype][zoom][clname][i] = optimized
|
||||
|
||||
def restore_choosers_order(self, type):
|
||||
ethalon_choosers = self.choosers_by_type[type]
|
||||
for tag, choosers_for_tag in list(self.choosers_by_type_and_tag[type].items()):
|
||||
tmp = []
|
||||
for ec in ethalon_choosers:
|
||||
if ec in choosers_for_tag:
|
||||
tmp.append(ec)
|
||||
self.choosers_by_type_and_tag[type][tag] = tmp
|
||||
|
||||
def get_runtime_rules(self, clname, type, tags, zoom):
|
||||
"""
|
||||
Returns array of runtime_conditions which are used for clname/type/tags/zoom
|
||||
"""
|
||||
runtime_rules = []
|
||||
if type in self.choosers_by_type_and_tag:
|
||||
for chooser in self.choosers_by_type_and_tag[type][clname]:
|
||||
runtime_conditions = chooser.get_runtime_conditions(type, tags, zoom)
|
||||
if type in self.choosers_by_type_zoom_tag:
|
||||
for chooser in self.choosers_by_type_zoom_tag[type][zoom][clname]:
|
||||
runtime_conditions = chooser.get_runtime_conditions(tags)
|
||||
if runtime_conditions:
|
||||
runtime_rules.append(runtime_conditions)
|
||||
return runtime_rules
|
||||
|
||||
def get_style(self, clname, type, tags, zoom, xscale, zscale, filter_by_runtime_conditions):
|
||||
style = []
|
||||
if type in self.choosers_by_type_and_tag:
|
||||
for chooser in self.choosers_by_type_and_tag[type][clname]:
|
||||
style = chooser.updateStyles(style, type, tags, zoom, xscale, zscale, filter_by_runtime_conditions)
|
||||
if type in self.choosers_by_type_zoom_tag:
|
||||
for chooser in self.choosers_by_type_zoom_tag[type][zoom][clname]:
|
||||
style = chooser.updateStyles(style, tags, xscale, zscale, filter_by_runtime_conditions)
|
||||
style = [x for x in style if x["object-id"] != "::*"]
|
||||
for x in style:
|
||||
for k, v in [('width', 0), ('casing-width', 0)]:
|
||||
|
|
Loading…
Add table
Reference in a new issue