From de239719479ffab1172555c59656995a0be3cfa4 Mon Sep 17 00:00:00 2001 From: Konstantin Pastbin Date: Tue, 19 Sep 2023 21:46:46 +0300 Subject: [PATCH] WIP Rewrite mapcss files from pseudo-tags to OM types Signed-off-by: Konstantin Pastbin --- src/libkomwm.py | 37 ++++++++++++- src/mapcss/__init__.py | 121 ++++++++++++++++++++++++++--------------- 2 files changed, 112 insertions(+), 46 deletions(-) diff --git a/src/libkomwm.py b/src/libkomwm.py index 2a85102..94698ab 100644 --- a/src/libkomwm.py +++ b/src/libkomwm.py @@ -461,6 +461,7 @@ def komap_mapswithme(options): ddir = os.path.dirname(options.outfile) classificator = {} + tags_to_cl = {} class_order = [] class_tree = {} @@ -514,17 +515,26 @@ def addPattern(dashes): raise Exception('Duplicate type: {0}'.format(row[0])) pairs = [i.strip(']').split("=") for i in row[1].split(',')[0].split('[')] kv = OrderedDict() + match_tags = '' for i in pairs: if len(i) == 1: if i[0]: if i[0][0] == "!": kv[i[0][1:].strip('?')] = "no" + # Skip match_tags. else: kv[i[0].strip('?')] = "yes" + match_tags += '[' + i[0] + ']' else: kv[i[0]] = i[1] + match_tags += '[' + i[0] + '=' + i[1] + ']' if row[2] != "x": classificator[cl] = kv + if match_tags not in tags_to_cl: + tags_to_cl[match_tags] = cl + else: + print(f'WARNING: not unique match_tags: {match_tags}={cl}, previous ={tags_to_cl[match_tags]}') + #tags_to_cl[row[1].split(',')[0]] = cl class_order.append(cl) unique_types_check.add(cl) # Mark original type to distinguish it among replacing types. @@ -539,6 +549,28 @@ def addPattern(dashes): class_order.sort() types_file.close() + #pastk + #cl = 'leisure-track-area' + #print(cl, classificator[cl]) #OrderedDict([('leisure', 'track'), ('area', 'yes')]) + #tags = '[leisure=track][area?]' + #print(tags, tags_to_cl[tags]) + #tags = '[railway=rail][highspeed?]' #'[railway=rail][highspeed?][!service]' + #print(tags, tags_to_cl[tags]) + + # Manually-assisted conversions: + tags_to_cl['[place]'] = 'place' + tags_to_cl['[leisure]'] = 'leisure' + tags_to_cl['[waterway]'] = 'waterway' + tags_to_cl['[isoline]'] = 'isoline' + tags_to_cl['[sport]'] = 'sport' + tags_to_cl['[tourism]'] = 'tourism' + tags_to_cl['[craft]'] = 'craft' + tags_to_cl['[shop]'] = 'shop' + tags_to_cl['[highway]'] = 'highway' + tags_to_cl['[landuse=military][military=danger_area]'] = 'landuse-military-danger_area' + tags_to_cl['[railway=rail][bridge?]'] = 'railway-rail-bridge' + tags_to_cl['[railway=rail][tunnel?]'] = 'railway-rail-tunnel' + output = '' for prio_range in prio_ranges.keys(): load_priorities(prio_range, options.priorities_path, unique_types_check, compress = False) @@ -560,9 +592,8 @@ def addPattern(dashes): # Parse style mapcss global style style = MapCSS(options.minzoom, options.maxzoom) - style.parse(clamp=False, stretch=LAYER_PRIORITY_RANGE, - filename=options.filename, static_tags=mapcss_static_tags, - dynamic_tags=mapcss_dynamic_tags) + style.parse(filename=options.filename, static_tags=mapcss_static_tags, + dynamic_tags=mapcss_dynamic_tags, rewrite = True, tags_to_cl = tags_to_cl) # Build optimization tree - class/zoom/type -> StyleChoosers clname_cltag_unique = set() diff --git a/src/mapcss/__init__.py b/src/mapcss/__init__.py index 8af9730..ce5b49c 100644 --- a/src/mapcss/__init__.py +++ b/src/mapcss/__init__.py @@ -32,8 +32,8 @@ #NOT_CLASS = re.compile(r'!([\.:]\w+) \s* ', re.S | re.X) ZOOM = re.compile(r'\| \s* z([\d\-]+) \s* ', re.I | re.S | re.X) GROUP = re.compile(r', \s* ', re.I | re.S | re.X) -CONDITION = re.compile(r'\[(.+?)\] \s* ', re.S | re.X) -OBJECT = re.compile(r'(\*|[\w]+) \s* ', re.S | re.X) +CONDITION = re.compile(r'\[(.+?)\] (\s*) ', re.S | re.X) +OBJECT = re.compile(r'(\*|[\w]+) (\s*) ', re.S | re.X) DECLARATION = re.compile(r'\{(.*?)\} \s* ', re.S | re.X) IMPORT = re.compile(r'@import\("(.+?)"\); \s* ', re.S | re.X) VARIABLE_SET = re.compile(r'@([a-z][\w\d]*) \s* : \s* (.+?) \s* ; \s* ', re.S | re.X | re.I) @@ -211,7 +211,7 @@ def get_variable(self, m): raise Exception("Variable not found: " + str(format(name))) return self.variables[name] if name in self.variables else m.group() - def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={}, dynamic_tags=set()): + def parse(self, css=None, filename=None, static_tags={}, dynamic_tags=set(), rewrite = False, tags_to_cl = {}): """ Parses MapCSS given as string """ @@ -226,9 +226,24 @@ def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={ log = logging.getLogger('mapcss.parser') previous = oNONE # what was the previous CSS word? sc = StyleChooser(self.scalepair) # currently being assembled + full_condition = '' - stck = [] # filename, original, remained + stck = [] # filename, remained, original stck.append([filename, css, css]) + if rewrite: + out_file = open(stck[-1][0], 'w') + + def rewrite_file(s): + if rewrite: + out_file.write(s) + + def convert_to_cl(tags): + if tags in tags_to_cl: + #return tags # DEBUG To focus on non-matches only. + return '|' + tags_to_cl[tags] + # There is no OM type for this combination of tags. Have to migrate manually. + return '|' + tags + ' /*NO-TYPE-MATCH*/' + try: while (len(stck) > 0): css = stck[-1][1].lstrip() # remained @@ -240,28 +255,24 @@ def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={ if previous == oDECLARATION: self.choosers.append(sc) sc = StyleChooser(self.scalepair) - cond = CLASS.match(css).groups()[0] + if full_condition and previous == oCONDITION: + rewrite_file(convert_to_cl(full_condition)) + full_condition = '' + m = CLASS.match(css) + rewrite_file(m.group(0)) + cond = m.group(1) log.debug("class found: %s" % (cond)) css = CLASS.sub("", css, 1) sc.addCondition(Condition('eq', ("::class", cond))) previous = oCONDITION - ## Not class - !.motorway, !.builtup, !:hover - #elif NOT_CLASS.match(css): - #if (previous == oDECLARATION): - #self.choosers.append(sc) - #sc = StyleChooser(self.scalepair) - #cond = NOT_CLASS.match(css).groups()[0] - #log.debug("not_class found: %s" % (cond)) - #css = NOT_CLASS.sub("", css, 1) - #sc.addCondition(Condition('ne', ("::class", cond))) - #previous = oCONDITION - # Zoom elif ZOOM.match(css): if (previous != oOBJECT & previous != oCONDITION): sc.newObject() - cond = ZOOM.match(css).groups()[0] + m = ZOOM.match(css) + rewrite_file(m.group(0)) + cond = m.group(1) log.debug("zoom found: %s" % (cond)) css = ZOOM.sub("", css, 1) sc.addZoom(self.parseZoom(cond)) @@ -269,6 +280,10 @@ def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={ # Grouping - just a comma elif GROUP.match(css): + if full_condition and previous == oCONDITION: + rewrite_file(convert_to_cl(full_condition)) + full_condition = '' + rewrite_file(GROUP.match(css).group(0)) css = GROUP.sub("", css, 1) sc.newGroup() had_main_tag = False @@ -283,12 +298,18 @@ def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={ if (previous != oOBJECT) and (previous != oZOOM) and (previous != oCONDITION): sc.newObject() had_main_tag = False - cond = CONDITION.match(css).groups()[0] + m = CONDITION.match(css) + cond = m.group(1) c = parseCondition(cond) tag = c.extract_tag() tag_type = static_tags.get(tag, None) if tag == "*" or tag_type is not None: if tag_type and had_main_tag: + if rewrite: + if full_condition: + rewrite_file(convert_to_cl(full_condition)) + full_condition = '' + rewrite_file('[' + cond + ']' + m.group(2)) if '!' in cond: condType = 'ne' cond = cond.replace('!', '') @@ -296,10 +317,17 @@ def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={ condType = 'eq' sc.addRuntimeCondition(Condition(condType, ('extra_tag', cond))) else: + if rewrite: + full_condition += '[' + cond + ']' sc.addCondition(c) if tag_type: had_main_tag = True elif tag in dynamic_tags: + if rewrite: + if full_condition: + rewrite_file(convert_to_cl(full_condition)) + full_condition = '' + rewrite_file('[' + cond + ']' + m.group(2)) sc.addRuntimeCondition(c) else: raise Exception("Unknown tag '" + tag + "' in condition " + cond) @@ -308,10 +336,15 @@ def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={ # Object - way, node, relation elif OBJECT.match(css): + if full_condition and previous == oCONDITION: + rewrite_file(convert_to_cl(full_condition) + ',\n') + full_condition = '' if (previous == oDECLARATION): self.choosers.append(sc) sc = StyleChooser(self.scalepair) - obj = OBJECT.match(css).groups()[0] + m = OBJECT.match(css) + obj = m.group(1) + rewrite_file(obj + m.group(2)) log.debug("object found: %s" % (obj)) css = OBJECT.sub("", css, 1) sc.newObject(obj) @@ -320,9 +353,16 @@ def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={ # Declaration - {...} elif DECLARATION.match(css): + #previous = oGROUP + if full_condition and previous == oCONDITION: + rewrite_file(convert_to_cl(full_condition) + '\n') + #rewrite_file(convert_to_cl(full_condition) + ',\n') + full_condition = '' if previous == oDECLARATION or previous == oNONE: raise Exception("Declaration without conditions") - decl = DECLARATION.match(css).groups()[0] + m = DECLARATION.match(css) + rewrite_file(m.group(0)) + decl = m.group(1) log.debug("declaration found: %s" % (decl)) sc.addStyles(self.subst_variables(parseDeclaration(decl))) css = DECLARATION.sub("", css, 1) @@ -331,17 +371,23 @@ def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={ # CSS comment elif COMMENT.match(css): log.debug("comment found") + rewrite_file(COMMENT.match(css).group(0)) css = COMMENT.sub("", css, 1) # @import("filename.css"); elif IMPORT.match(css): + m = IMPORT.match(css) + rewrite_file(m.group(0)) log.debug("import found") - import_filename = os.path.join(basepath, IMPORT.match(css).groups()[0]) + import_filename = os.path.join(basepath, m.group(1)) try: css = IMPORT.sub("", css, 1) import_text = open(import_filename, "r").read() stck[-1][1] = css # store remained part stck.append([import_filename, import_text, import_text]) + if rewrite: + out_file.close() + out_file = open(stck[-1][0], 'w') wasBroken = True break except IOError as e: @@ -349,9 +395,11 @@ def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={ # Variables elif VARIABLE_SET.match(css): - name = VARIABLE_SET.match(css).groups()[0] + m = VARIABLE_SET.match(css) + rewrite_file(m.group(0)) + name = m.group(1) log.debug("variable set found: %s" % name) - self.variables[name] = VARIABLE_SET.match(css).groups()[1] + self.variables[name] = m.group(2) css = VARIABLE_SET.sub("", css, 1) previous = oVARIABLE_SET @@ -367,11 +415,18 @@ def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={ if not wasBroken: stck.pop() + if rewrite: + out_file.close() + if (len(stck) > 0): + out_file = open(stck[-1][0], 'a') if (previous == oDECLARATION): self.choosers.append(sc) sc = StyleChooser(self.scalepair) + if rewrite: + out_file.close() + except Exception as e: filename = stck[-1][0] # filename css_orig = stck[-1][2] # original @@ -380,26 +435,6 @@ def parse(self, css=None, clamp=True, stretch=1000, filename=None, static_tags={ msg = str(e) + "\nFile: " + filename + "\nLine: " + str(line) raise Exception(msg) - try: - if clamp: - "clamp z-indexes, so they're tightly following integers" - zindex = set() - for chooser in self.choosers: - for stylez in chooser.styles: - zindex.add(float(stylez.get('z-index', 0))) - zindex = list(zindex) - zindex.sort() - for chooser in self.choosers: - for stylez in chooser.styles: - if 'z-index' in stylez: - res = zindex.index(float(stylez.get('z-index', 0))) - if stretch: - stylez['z-index'] = stretch * res / len(zindex) - else: - stylez['z-index'] = res - except TypeError: - pass - for chooser in self.choosers: for t in chooser.compatible_types: if t not in self.choosers_by_type: