diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csstree/BlockSplitter.py b/src/csstree/BlockSplitter.py new file mode 100644 index 00000000..13354735 --- /dev/null +++ b/src/csstree/BlockSplitter.py @@ -0,0 +1,236 @@ +from __future__ import print_function +import re +import logging +import logging_config #to configure logging, no calls needed +from CssTree.CssElement import CssElement +from collections import namedtuple +from Util import StringWithSource + + +Block = namedtuple("Block", "name attrs") + + +MIN_ZOOM = 1 +MAX_ZOOM = 19 + + +BLOCK_SPLITTER = re.compile(r'([^@{]*)\s*\{(.*?)\}', re.DOTALL | re.MULTILINE) +ZOOM = re.compile(r'(.*?)(\|z[\d\-]*?)?(\[.*)?') #deprecated +ONE_ZOOM = re.compile(r'(\d{1,2})$') +ZOOM_RANGE = re.compile(r'(\d{1,2})-(\d{1,2})$') +ZOOM_TO_MAX = re.compile(r'(\d{1,2})-$') + + +TAG_RE = re.compile(r'(^.*?)[\|\[$:]', re.MULTILINE) +ZOOM_RE = re.compile(r'.*?\|z([\d\-]*?)[\[$:]') +SELECTORS_RE = re.compile(r'(\[.*?\])') +SUB_RE = re.compile(r'.*:(.*)$') +ONE_SELECTOR_RE = re.compile(r'\[(.*?)\]') + + +class BlockSplitter: + """ + Should also be initializeable by a preprocessed file + """ + def __init__(self, preprocessed_blocks, write_to_file=False): + self.blocks = preprocessed_blocks + self.write_to_file = write_to_file + self.blocks_by_zoom_level = dict(map(lambda i: (i, {}), range(MIN_ZOOM, MAX_ZOOM +1))) + + + def process(self): + self.split_commas() + self.process_blocks_by_zoom_level() + + if self.write_to_file: + self.write() + + + def process_blocks_by_zoom_level(self): + for zoom in self.blocks_by_zoom_level: + self.process_zoom_level(zoom, self.blocks_by_zoom_level[zoom]) + + + def process_zoom_level(self, zoom, block): + clean_block = {} + block_keys = sorted(block.keys()) + old_block = Block("", []) + for tag in block_keys: + attrs = block[tag] + if tag == old_block.name: + old_block.attrs.extend(attrs) + else: + if old_block.name: + clean_block[old_block.name] = self.parse_attributes(old_block.attrs, tag, zoom) + old_block = Block(tag, attrs) + self.blocks_by_zoom_level[zoom] = clean_block + + + def parse_attributes(self, list_of_attrs, tag, zoom): + ret = {} #attribute : StringWithSource(value, imported_from) + for attr, source in list_of_attrs.iteritems(): + key, val = map(str.strip, attr.split(":", 1)) + if key in ret: + logging.warning("Duplicate value for attribute {} ({}) for tag {} on zoom {}. First declared in {}".format(key, val, tag, zoom, ret[key].source)) + + ret[key] = StringWithSource(val, source) + return ret + + + def clean_split_by(self, string, separator): + return filter(lambda x: x != "", map(lambda x: x.strip(), string.split(separator))) + + + def all_zooms_in_css_range(self, str_range): + min_zoom = -1 + max_zoom = -1 + + if not str_range: + min_zoom = MIN_ZOOM + max_zoom = MAX_ZOOM + + elif ONE_ZOOM.match(str_range): + min_zoom = int(str_range) + max_zoom = min_zoom + + elif ZOOM_TO_MAX.match(str_range): + min_zoom = int(str_range[:-1]) + max_zoom = MAX_ZOOM + + elif ZOOM_RANGE.match(str_range): + found = ZOOM_RANGE.findall(str_range)[0] + (min_zoom, max_zoom) = map(lambda x: int(x), found) + + if max_zoom < 0 or min_zoom < 0 or max_zoom < min_zoom: + raise Exception("Failed to parse the zoom levels") + + max_zoom = MAX_ZOOM if max_zoom > MAX_ZOOM else max_zoom + min_zoom = MIN_ZOOM if min_zoom < MIN_ZOOM else min_zoom + + return range(min_zoom, max_zoom + 1) + + + def split_keys_by_zoom(self, keys): + ret = [] + for key in keys: + parts_list = ZOOM.findall(key) + if not parts_list: + logging.error("Unparseable key {}".format(key)) + continue + parts = parts_list[0] + if parts: + selector, zoom = parts[0], parts[1][2:] + print(">>>> {} : {}".format(selector, zoom)) + all_zooms = self.all_zooms_in_css_range(zoom) + ret.append(map(lambda x: (selector, x), all_zooms)) + else: + logging.error("Got an unparseable node and zoom level: {}".format(key)) + logging.warning("NOTE THAT THIS TAG HAS BEEN IGNORED AND MUST BE PROCESSED IN THE FUTURE!") + return ret + + + def clean_up_attribute_block(self, attributes, key, imported_from): + last_attr = "" + clean_attributes = [] + for a in attributes: + if a == last_attr: + logging.warning("Duplicate attribute {} for tag/zoom {} imported from {}".format(a, key, imported_from)) + continue + clean_attributes.append(a) + return clean_attributes + + + def filter_attributes_against_processed(self, clean_attributes, element, imported_from): + filtered_attributes = [] + for a in clean_attributes: + if a in self.blocks_by_zoom_level[element.zoom][element]: + logging.warning("Duplicate attribute {} for tag {} imported from {}".format(a, element, imported_from)) + else: + filtered_attributes.append(a) + return filtered_attributes + + + def split_commas(self): + for block, imported_from in self.blocks: + found = BLOCK_SPLITTER.findall(block) + for entry in found: + keys = self.clean_split_by(entry[0], ",") + attributes = sorted(self.clean_split_by(entry[1], ";")) #TODO attributes should also be a dictionary. Or should they? + + clean_attributes = self.clean_up_attribute_block(attributes, entry[0], imported_from) + + for key in keys: + elements = self.css_key_factory(key) + self.add_elements_to_zoom_level(elements, clean_attributes, imported_from) + + + def add_elements_to_zoom_level(self, elements, clean_attributes, imported_from): + for element in elements: + if element in self.blocks_by_zoom_level[element.zoom]: + filtered_attributes = self.filter_attributes_against_processed(clean_attributes, element, imported_from) + self.blocks_by_zoom_level[element.zoom][element].update(self.map_attrs_to_import_source(filtered_attributes, imported_from)) + else: + self.blocks_by_zoom_level[element.zoom][element] = self.map_attrs_to_import_source(clean_attributes, imported_from) + + + def map_attrs_to_import_source(self, attributes, imported_from): + return dict(map(lambda x: (x, imported_from), attributes)) + + + def write(self): + print("Writing split blocks by zoom, num blocks {}".format(len(self.blocks_by_zoom_level))) + with open("../../out/split_by_commas.mapcss", "w") as out_file: + for zoom in sorted(self.blocks_by_zoom_level.keys()): + blocks = self.blocks_by_zoom_level[zoom] + # for zoom, blocks in self.blocks_by_zoom_level: + out_file.write(" /* ===== ZOOM {} ===== */\n\n".format(zoom)) + keys = blocks.keys() + keys.sort(key=lambda x : x.tag) + for tag in keys: + attrs = blocks[tag] + out_file.write("{} {{\n".format(tag)) + for attr in attrs: + out_file.write(" {}: {}; /* == {} == */\n".format(attr, attrs[attr].string, attrs[attr].source)) + out_file.write("}\n\n") + + + def css_key_factory(self, str_key): + # type: (str) -> [CssElement] + tag_list = TAG_RE.findall(str_key) + tag = tag_list[0] if tag_list else str_key + + zoom_list = ZOOM_RE.findall(str_key) + zoom = zoom_list[0] if zoom_list else "" + + selectors_list = SELECTORS_RE.findall(str_key) + + str_key = TAG_RE.sub("", str_key) + str_key = ZOOM_RE.sub("", str_key) + str_key = SELECTORS_RE.sub("", str_key) + + subclass_list = SUB_RE.findall(str_key) + subclass = subclass_list[0] if subclass_list else "" + all_zooms = self.all_zooms_in_css_range(zoom) + ret = map(lambda z: CssElement(tag, z, selectors_list, subclass), all_zooms) + return ret + + + +if __name__ == "__main__": + + + blockSplitter = BlockSplitter([]) + # print(blockSplitter.all_zooms_in_css_range("10")) + # print(blockSplitter.all_zooms_in_css_range("10-")) + # print(blockSplitter.all_zooms_in_css_range("10-12")) + # print(blockSplitter.all_zooms_in_css_range("10-25")) + + # print(blockSplitter.split_key_by_components("*::*")) + # print(blockSplitter.split_key_by_components("*")) + # print(blockSplitter.split_key_by_components("*|z12")) + # print(blockSplitter.split_key_by_components("*::int_name ")) + # print(blockSplitter.split_key_by_components("line|z5[highway=world_level]")) + # print(blockSplitter.css_key_factory("line|z17-18[highway=footway][tunnel?]::tunnelBackground")) + + pass + diff --git a/src/csstree/CssTree/CssElement.py b/src/csstree/CssTree/CssElement.py new file mode 100644 index 00000000..b36a7110 --- /dev/null +++ b/src/csstree/CssTree/CssElement.py @@ -0,0 +1,67 @@ +WILDCARD = "*" + +class CssElement: + def __init__(self, tag, zoom, selectors, subclass): + self.tag = tag + self.zoom = zoom + self.selectors = selectors #[] + self.subclass = subclass + + + def __repr__(self): + return self._my_str_repr() + + + def _my_str_repr(self, must_sort=False): + selectors = self.selectors + if must_sort: + selectors = sorted(selectors) + + str_selectors = "".join(selectors) if selectors else "" + str_subclass = "::{}".format(self.subclass) if self.subclass else "" + return "{}|z{}{}{}".format(self.tag, self.zoom, str_selectors, str_subclass) + + + def __eq__(self, other): + return (isinstance(other, self.__class__) + and self.__dict__ == other.__dict__) + + + def __ne__(self, other): + return not self.__eq__(other) + + + def __hash__(self): + return hash(self._my_str_repr(must_sort=True)) + + + def can_adopt(self, css_element): + if self.zoom != css_element.zoom: + return False + #my tag must be * or the same as theirs, my subclass must be * or the same as theirs, and my selectors must count less than theirs and be a subset of theirs. + if self.tag != WILDCARD and self.tag != css_element.tag: + return False + if self.subclass != WILDCARD and self.subclass != css_element.subclass: + return False + if len(self.selectors) >= len(css_element.selectors): + return False + if not set(self.selectors).issubset(css_element.selectors): + return False + + return True + + + + + +if __name__ == "__main__": + e1 = CssElement("line", 14, ["[piste:type=downhill]", "[piste:difficulty=intermediate]"], "") + e2 = CssElement("line", 14, ["[piste:type=downhill]"], "") + + print(e1.can_adopt(e1)) + print(e1.can_adopt(e2)) + print(e2.can_adopt(e1)) + # class CssBlock: +# def __init__(self, element, styles): +# self.element = element +# self.styles = styles diff --git a/src/csstree/CssTree/__init__.py b/src/csstree/CssTree/__init__.py new file mode 100644 index 00000000..459c7d57 --- /dev/null +++ b/src/csstree/CssTree/__init__.py @@ -0,0 +1,134 @@ +from __future__ import print_function +import logging +from CssElement import CssElement + +WILDCARD = "*" +TAGS = ["node", "line", "way", "area"] + + +class CssTree: + def __init__(self, min_zoom=1, max_zoom=19): + self.root_nodes_by_zoom = {} + for i in range(min_zoom, max_zoom + 1): + self.root_nodes_by_zoom[i] = CssNode(None, None, root=True) + + + def add(self, node): + self.root_nodes_by_zoom[node.element.zoom].add(node) + + + def finilize_tree(self): + for tag in TAGS: + for zoom, subtree in self.root_nodes_by_zoom.iteritems(): + subtree.add(CssNode(CssElement(tag, zoom, [], None), [])) + self.trickle_down_styles() + + + def trickle_down_styles(self): + for zoom, subtree in self.root_nodes_by_zoom.iteritems(): + subtree.trickle_down_styles() + + + def write(self): + with open("../../out/inflated.mapcss", "w") as out_file: + for zoom, subtree in self.root_nodes_by_zoom.iteritems(): + out_file.write(" /* === ZOOM {} === */\n\n".format(zoom)) + subtree.write(out_file) + + +class CssNode: + def __init__(self, element, styles, root=False): + self.children = [] + self.element = element + self.styles = styles # {: } + self.parent = None + self.am_root = root + + + def write(self, out_file): + if not self.is_root(): + out_file.write("{} {{\n".format(self.element)) + if self.styles: + for attr, val in self.styles.iteritems(): + # out_file.write(" {}: {}; /* {} */\n".format(attr, val.string, val.source)) #our old Kothic doesn't like comments on the same line as an attribute + out_file.write(" {}: {};\n".format(attr, val.string)) + out_file.write("}\n\n") + + for child in self.children: + child.write(out_file) + + + def is_root(self): + return self.am_root + + + def can_adopt(self, node): + if self.is_root(): + return True + return self.element.can_adopt(node.element) + + + def add(self, node): + if not self.can_adopt(node): + return False + + if not self.children: #self has no children, + self.children.append(node) + node.parent = self + return True + else: + for child in self.children: + if child.can_adopt(node): + return child.add(node) # self has children, and one of the children can adopt the new node + + # none of the children could adopt the new node, maybe the node can adopt the children (or some of them) + possible_children_for_the_node = [] + for child in self.children: + if node.can_adopt(child): + possible_children_for_the_node.append(child) + + if possible_children_for_the_node: #if there are children that can be adopted by the node + for child in possible_children_for_the_node: + self.children.remove(child) + node.add(child) + child.parent = node + self.children.append(node) + node.parent = self + return True + + + def trickle_down_styles(self): + for child in self.children: + child.inherit_styles(self.styles) + child.trickle_down_styles() + + + def inherit_styles(self, styles): + if not styles: + return + for attr, value in styles.iteritems(): + if attr in self.styles: + logging.info("Rewriting a value from a higher element, {}: {} -> {}".format(attr, value.string, self.styles[attr].string)) + continue + self.styles[attr] = value + + + def __repr__(self): + # children_repr = "".join(map(lambda x: str(x), self.children)) + return str(self.element) + +if __name__ == "__main__": + node1 = CssNode(CssElement("tag", "10", ["a", "b", "c"], None), []) + node2 = CssNode(CssElement("tag", "10", ["a", "b", "c", "d"], None), []) + node3 = CssNode(CssElement("*", "10", ["a", "b", "c", "d"], None), []) + +# node|z19 + + css_subtree = CssNode(None, None, root=True) + print(css_subtree.add(node2)) + print(css_subtree.add(node1)) + print(css_subtree.add(node3)) + + print(css_subtree) + + # print(node1.can_adopt(node2)) \ No newline at end of file diff --git a/src/csstree/Preprocessor.py b/src/csstree/Preprocessor.py new file mode 100644 index 00000000..fc717c4d --- /dev/null +++ b/src/csstree/Preprocessor.py @@ -0,0 +1,129 @@ +from __future__ import print_function +from os.path import exists +import logging +import re +import logging_config # Used to configure logging, you don't have to call anything from it. +from os.path import dirname, realpath, join +from Util import StringWithSource + +IMPORT = re.compile(r'^\s*?@import\s*?\(\s*?\"(.*?)\"\s*?\)\s*?;', re.DOTALL | re.MULTILINE) +COMMENT = re.compile(r'/\*.*?\*/', re.DOTALL) +VARIABLE = re.compile(r'^\s*(@(?!import).*?)\s*?:\s*?(.*?);', re.DOTALL | re.MULTILINE) +BLOCK = re.compile(r'^\s*([^@{]*\{.*?\})', re.DOTALL | re.MULTILINE) + +""" +We also need to remember where a certain block comes from so that we could warn about duplicate declarations +""" +class Preprocessor: + + def __init__(self, filepath, is_recursive=False, write_to_file=False, already_imported=[]): + self.blocks = [] + self.variables = {} + self.text = "" + self.base_folder = "" + self.blocks = [] # list of tuples (block, imported_from_url) + self.filepath = realpath(filepath) + self.base_folder = realpath(dirname(filepath)) + self.is_recursive = is_recursive + self.write_to_file = write_to_file + self.already_imported = already_imported + + + def clean(self): + self.text = COMMENT.sub("", self.text) + self.text = IMPORT.sub("", self.text) + self.text = VARIABLE.sub("", self.text) + self.text = BLOCK.sub("", self.text) + self.text = re.sub("\s*", "", self.text) + + + def process(self): + self.readfile() + + self.text = COMMENT.sub("", self.text) + self.process_variables() + self.process_imports() + self.process_blocks() + + self.clean() + if self.text: + logging.warning("Some text in the mapcss file couldn't be parsed:\n{}\n{}".format(self.filepath, self.text)) + + if not self.is_recursive: + self.substitute_variables() + + if self.write_to_file: + self.write() + + def write(self): + with open("{}.preprocessed".format(self.filepath), "w") as out_file: + out_file.writelines(map(lambda x : "{} /* {} */\n\n".format(x[0], x[1]), self.blocks)) + + + def process_blocks(self): + blocks = BLOCK.findall(self.text) + for b in blocks: + self.blocks.append((re.sub("\s+", " ", b), self.filepath)) + + + def _print_vars(self): + for var in self.variables: + print("~{} : {} ({})".format(var, self.variables[var].string, self.variables[var].source)) + print("Finished vars") + + + + def process_variables(self): + variables = VARIABLE.findall(self.text) + for var in variables: + #self.variables[var[0].strip()] = (var[1].strip(), self.filepath) + self.variables[var[0].strip()] = StringWithSource(var[1].strip(), self.filepath) + + + def process_imports(self): + imports = IMPORT.findall(self.text) + for imp in imports: + imp_path = realpath(join(self.base_folder, imp)) + if imp_path in self.already_imported: + continue + preprocessor = Preprocessor(imp_path, is_recursive=True, already_imported=self.already_imported) + preprocessor.process() + self.blocks.extend(preprocessor.blocks) + self.merge_variables(preprocessor.variables) + self.already_imported.append(imp_path) + self.already_imported.extend(preprocessor.already_imported) + + + def merge_variables(self, other_variables): + for key in other_variables: + if key in self.variables: + logging.warning("Variable redeclaration, setting the new value: \n{}\nold: {}\nnew: {}\nFirst declared in {}\nRedeclared in {}" + .format(key, self.variables[key].string, other_variables[key].string, self.variables[key].source, other_variables[key].source)) + first_declared = other_variables[key].source if key not in self.variables else self.variables[key].source + self.variables[key] = StringWithSource(other_variables[key].string, first_declared) + + + def substitute_variables(self): + substituted_blocks = [] + + variable_keys = self.variables.keys() + variable_keys.sort(key=len, reverse=True) #reverse sort the var names by + # length so that we don't substitute parts of longer var names with values from vars with shorter names + + for block, imported_from in self.blocks: + for var in variable_keys: + block = block.replace(var, self.variables[var].string) + substituted_blocks.append((block, imported_from)) + if "@" in block: + logging.warning("Unbound variable found in block {}".format(block)) + + self.blocks = substituted_blocks + + + def readfile(self): + if not exists(self.filepath): + logging.error("The file {file} doesn't exist!".format(file=self.filepath)) + exit(1) + + with open(self.filepath) as f: + self.text = f.read() diff --git a/src/csstree/ToolChain.py b/src/csstree/ToolChain.py new file mode 100644 index 00000000..40cd4038 --- /dev/null +++ b/src/csstree/ToolChain.py @@ -0,0 +1,43 @@ +from __future__ import print_function +from Preprocessor import Preprocessor +from BlockSplitter import BlockSplitter +from CssTree import CssTree +from CssTree import CssNode + +if __name__ == "__main__": + + print("Toolchain") + + prep = Preprocessor("../../testdata/clear/style-clear/test.mapcss", write_to_file=True) + prep.process() + print("Toolchain, num blocks: {}".format(len(prep.blocks))) + # prep.substitute_variables() + + + + block_splitter = BlockSplitter(prep.blocks, write_to_file=True) + block_splitter.process() + + css_tree = CssTree() + for zoom in block_splitter.blocks_by_zoom_level: + for block in block_splitter.blocks_by_zoom_level[zoom]: + css_node = CssNode(block, block_splitter.blocks_by_zoom_level[zoom][block]) + css_tree.add(css_node) + + css_tree.finilize_tree() + css_tree.write() + + + + + + # for zoom in block_splitter.blocks_by_zoom_level: + # print("Zoom {}".format(zoom)) + # selectors = map(lambda x: x.selectors, block_splitter.blocks_by_zoom_level[zoom]) + # selectors = sorted(selectors, key=len) + # # for block in block_splitter.blocks_by_zoom_level[zoom]: + # # selectors = sorted(block.selectors, key = lambda x: len(x)) + # for selector in selectors: + # print("> {}".format(selector)) + + print("hello") \ No newline at end of file diff --git a/src/csstree/Util.py b/src/csstree/Util.py new file mode 100644 index 00000000..fb9d985a --- /dev/null +++ b/src/csstree/Util.py @@ -0,0 +1,3 @@ +from collections import namedtuple + +StringWithSource = namedtuple("StringWithSource", "string source") diff --git a/src/csstree/__init__.py b/src/csstree/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/csstree/logging_config.py b/src/csstree/logging_config.py new file mode 100644 index 00000000..b6f10995 --- /dev/null +++ b/src/csstree/logging_config.py @@ -0,0 +1,4 @@ +import logging.config + +logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG) + diff --git a/src/libkomwm.py b/src/libkomwm.py index 87636f92..65d9d205 100644 --- a/src/libkomwm.py +++ b/src/libkomwm.py @@ -1,8 +1,12 @@ +from __future__ import print_function + from mapcss import MapCSS from optparse import OptionParser import os import csv import sys +import traceback + import mapcss.webcolors whatever_to_hex = mapcss.webcolors.webcolors.whatever_to_hex whatever_to_cairo = mapcss.webcolors.webcolors.whatever_to_cairo @@ -30,6 +34,7 @@ def to_boolean(s): else: return False, False # Invalid + def mwm_encode_color(colors, st, prefix='', default='black'): if prefix: prefix += "-" @@ -39,6 +44,7 @@ def mwm_encode_color(colors, st, prefix='', default='black'): colors.add(result) return result + def mwm_encode_image(st, prefix='icon', bgprefix='symbol'): if prefix: prefix += "-" @@ -50,67 +56,115 @@ def mwm_encode_image(st, prefix='icon', bgprefix='symbol'): handle = st.get(prefix + "image")[:-4] return handle, handle -def komap_mapswithme(options): - ddir = os.path.dirname(options.outfile) - classificator = {} - class_order = [] - class_tree = {} - - colors_file_name = os.path.join(ddir, 'colors.txt') +def read_colors_file(filepath): colors = set() - if os.path.exists(colors_file_name): - colors_in_file = open(colors_file_name, "r") - for colorLine in colors_in_file: - colors.add(int(colorLine)) - colors_in_file.close() + if os.path.exists(filepath): + with open(filepath) as color_file: + colors.update(map(lambda x: int(x), color_file)) + return colors - patterns = [] - def addPattern(dashes): - if dashes and dashes not in patterns: - patterns.append(dashes) - patterns_file_name = os.path.join(ddir, 'patterns.txt') - if os.path.exists(patterns_file_name): - patterns_in_file = open(patterns_file_name, "r") - for patternsLine in patterns_in_file: - addPattern([float(x) for x in patternsLine.split()]) - patterns_in_file.close() +def append_pattern(patterns, to_append): + if to_append: + patterns.add(to_append) + return patterns + + +def read_patterns_file(filepath): + patterns = set() + if os.path.exists(filepath): + with open(filepath, "r") as patterns_in_file: + for string_pattern in map(lambda x: x.split(), patterns_in_file): + patterns = append_pattern(patterns, tuple(map(lambda x: float(x), string_pattern))) + + return patterns + + +def parse_pair(pair): + """ + Args: + pair: list of 1 or 2 elements from the mapccs-mapping.csv + + Returns: + if the list contains 2 elements, returns {1st : 2nd} + if the list contains 1 element that starts with !, trips the ! and the ? from the key and returns : {1st : no} + if 1 element with no ! at the beginning, strips the ? from the key and returns: {1st : yes} + + >>> parse_pair(["one", "two"]) + {'one': 'two'} + + >>> parse_pair(["!one"]) + {'one': 'no'} + + >>> parse_pair(["!one?"]) + {'one': 'no'} + + >>> parse_pair(["one?"]) + {'one': 'yes'} + """ + kv = {} + if len(pair) == 1: + if pair[0]: + if pair[0].startswith("!"): + key = pair[0][1:].strip('?') + kv[key] = "no" + else: + key = pair[0].strip('?') + kv[key] = "yes" + else: + kv[pair[0]] = pair[1] + return kv + + +def read_class_hierarchy(ddir): + classificator = {} + class_order = [] + class_tree = {} - # Build classificator tree from mapcss-mapping.csv file types_file = open(os.path.join(ddir, 'types.txt'), "w") cnt = 1 for row in csv.reader(open(os.path.join(ddir, 'mapcss-mapping.csv')), delimiter=';'): while int(row[5]) > cnt: - print >> types_file, "mapswithme" + print(types_file.name + " row 5 > cnt: {} > {}".format(row[5], cnt)) cnt += 1 cnt += 1 cl = row[0].replace("|", "-") pairs = [i.strip(']').split("=") for i in row[1].split(',')[0].split('[')] kv = {} - for i in pairs: - if len(i) == 1: - if i[0]: - if i[0][0] == "!": - kv[i[0][1:].strip('?')] = "no" - else: - kv[i[0].strip('?')] = "yes" - else: - kv[i[0]] = i[1] + for pair in pairs: + kv.update(parse_pair(pair)) + classificator[cl] = kv if row[2] != "x": class_order.append(cl) - print >> types_file, row[0] + print("Appended {}".format(row[0])) else: # compatibility mode if row[6]: - print >> types_file, row[6] + print("Did not append {}, because {}".format(row[0], row[6])) else: - print >> types_file, "mapswithme" + print("Didn't append {}, col 6 doesnt exist".format(row[0])) class_tree[cl] = row[0] class_order.sort() types_file.close() + return class_order, class_tree, classificator + + +def komap_mapswithme(options): + ddir = os.path.dirname(options.outfile) + + + # debug files. Not quite sure why we read them. + colors_file_name = os.path.join(ddir, "colors.txt") + colors = read_colors_file(colors_file_name) + + patterns_file_name = os.path.join(ddir, 'patterns.txt') + patterns = read_patterns_file(patterns_file_name) + + # Build classificator tree from mapcss-mapping.csv file + (class_order, class_tree, classificator) = read_class_hierarchy(ddir) # Get all mapcss static tags which are used in mapcss-mapping.csv mapcss_static_tags = set() @@ -120,6 +174,7 @@ def addPattern(dashes): # Get all mapcss dynamic tags from mapcss-dynamic.txt mapcss_dynamic_tags = set([line.rstrip() for line in open(os.path.join(ddir, 'mapcss-dynamic.txt'))]) + # we can reuse the code above, we don't need to rewrite it. Just refactor it. # Parse style mapcss style = MapCSS(options.minzoom, options.maxzoom + 1) @@ -253,7 +308,7 @@ def addPattern(dashes): dr_line.priority = min(int(st.get('z-index', 0) + 999), 20000) dashes = st.get('casing-dashes', st.get('dashes', [])) dr_line.dashdot.dd.extend(dashes) - addPattern(dr_line.dashdot.dd) + patterns = append_pattern(patterns, tuple(dr_line.dashdot.dd)) #debug thing dr_line.cap = dr_linecaps.get(st.get('casing-linecap', 'butt'), BUTTCAP) dr_line.join = dr_linejoins.get(st.get('casing-linejoin', 'round'), ROUNDJOIN) dr_element.lines.extend([dr_line]) @@ -277,7 +332,7 @@ def addPattern(dashes): dr_line.color = mwm_encode_color(colors, st) for i in st.get('dashes', []): dr_line.dashdot.dd.extend([max(float(i), 1) * WIDTH_SCALE]) - addPattern(dr_line.dashdot.dd) + patterns = append_pattern(patterns, tuple(dr_line.dashdot.dd)) #debug thing dr_line.cap = dr_linecaps.get(st.get('linecap', 'butt'), BUTTCAP) dr_line.join = dr_linejoins.get(st.get('linejoin', 'round'), ROUNDJOIN) if '-x-me-line-priority' in st: @@ -433,26 +488,32 @@ def cmprepl(a, b): for k in viskeys: offset = " " * (k.count("|") - 1) for i in range(len(oldoffset) / 4, len(offset) / 4, -1): - print >> visibility_file, " " * i + "{}" - print >> classificator_file, " " * i + "{}" + print(visibility_file.name + (" " * i) + "{}") + print(classificator_file.name + (" " * i) + "{}") oldoffset = offset end = "-" if k in visnodes: end = "+" - print >> visibility_file, offset + k.split("|")[-2] + " " + visibility.get(k, "0" * (options.maxzoom + 1)) + " " + end - print >> classificator_file, offset + k.split("|")[-2] + " " + end + print(visibility_file.name + offset + k.split("|")[-2] + " " + visibility.get(k, "0" * (options.maxzoom + 1)) + " " + end) + print(classificator_file.name + offset + k.split("|")[-2] + " " + end) for i in range(len(offset) / 4, 0, -1): - print >> visibility_file, " " * i + "{}" - print >> classificator_file, " " * i + "{}" + print(visibility_file.name + (" " * i) + "{}") + print(classificator_file.name + (" " * i) + "{}") visibility_file.close() classificator_file.close() +# write debug files + write_colors_file(colors_file_name, colors) + write_patterns_file(patterns_file_name, patterns) + +def write_colors_file(colors_file_name, colors): colors_file = open(colors_file_name, "w") for c in sorted(colors): colors_file.write("%d\n" % (c)) colors_file.close() +def write_patterns_file(patterns_file_name, patterns): patterns_file = open(patterns_file_name, "w") for p in patterns: patterns_file.write("%s\n" % (' '.join(str(elem) for elem in p))) @@ -461,6 +522,9 @@ def cmprepl(a, b): # Main try: + # import doctest + # doctest.testmod() + parser = OptionParser() parser.add_option("-s", "--stylesheet", dest="filename", help="read MapCSS stylesheet from FILE", metavar="FILE") @@ -486,5 +550,6 @@ def cmprepl(a, b): exit(0) except Exception as e: - print >> sys.stderr, "Error\n" + str(e) + traceback.print_exc(e) + # print(sys.stderr, "Error\n" + str(e) exit(-1) diff --git a/test/compare_files.py b/test/compare_files.py new file mode 100644 index 00000000..ad6c420f --- /dev/null +++ b/test/compare_files.py @@ -0,0 +1,10 @@ +# a very simple test to compare two resulting files + +import filecmp + +string = "" + +if not filecmp.cmp("../testdata/initial.txt.txt", "../testdata/output.txt.txt"): + string = "NOT " + +print("The files are {}the same".format(string))