Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ and execute tests from those files.
## Running integration tests

File `integration-tests/full_drules_gen.py` is intended to generate drules
files for all 6 themes from main Organic Maps repo. It could be used to understand
files for all 8 themes from main Organic Maps repo. It could be used to understand
which parts of the project are actually used by Organic Maps repo.

Usage:
Expand All @@ -33,5 +33,5 @@ python3 full_drules_gen.py -d ../../../data -o drules --txt
```

This command will run generation for styles - default light, default dark,
outdoors light, outdoors dark, vehicle light, vehicle dark and put `*.bin`
and `*.txt` files into 'drules' subfolder.
outdoors light, outdoors dark, cycling light, cycling dark, vehicle light,
vehicle dark and put `*.bin` and `*.txt` files into 'drules' subfolder.
34 changes: 16 additions & 18 deletions integration-tests/full_drules_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,32 @@
log = logging.getLogger('test_drules_gen')
log.setLevel(logging.INFO)

styles = {
'default_light': ['styles/default/light/style.mapcss', 'styles/default/include'],
'default_dark': ['styles/default/dark/style.mapcss', 'styles/default/include'],
'outdoors_light': ['styles/outdoors/light/style.mapcss', 'styles/outdoors/include'],
'outdoors_dark': ['styles/outdoors/dark/style.mapcss', 'styles/outdoors/include'],
'vehicle_light': ['styles/vehicle/light/style.mapcss', 'styles/vehicle/include'],
'vehicle_dark': ['styles/vehicle/dark/style.mapcss', 'styles/vehicle/include'],
}
# Keep vehicle last: every style rewrites visibility.txt & classificator.txt in the data dir,
# and the apps ship the ones produced by the vehicle style.
STYLES = ('default', 'outdoors', 'cycling', 'vehicle')
VARIANTS = ('light', 'dark')


def full_styles_regenerate(options):
log.info("Start generating styles")
libkomwm.MULTIPROCESSING = False
prio_ranges_orig = deepcopy(libkomwm.prio_ranges)

for name, (style_path, include_path) in styles.items():
log.info(f"Generating {name} style ...")
for style in STYLES:
for variant in VARIANTS:
name = f'{style}_{variant}'
log.info(f"Generating {name} style ...")

# Restore initial state
libkomwm.prio_ranges = deepcopy(prio_ranges_orig)
libkomwm.visibilities = {}
# Restore initial state
libkomwm.prio_ranges = deepcopy(prio_ranges_orig)
libkomwm.visibilities = {}

options.filename = options.data + '/' + style_path
options.priorities_path = options.data + '/' + include_path
options.outfile = options.outdir + '/' + name
options.filename = f'{options.data}/styles/{style}/{variant}/style.mapcss'
options.priorities_path = f'{options.data}/styles/{style}/include'
options.outfile = f'{options.outdir}/{name}'

# Run generation
libkomwm.komap_mapswithme(options)
# Run generation
libkomwm.komap_mapswithme(options)
log.info("Done!")

def main():
Expand Down
32 changes: 21 additions & 11 deletions src/libkomwm.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from multiprocessing import Pool, set_start_method
from collections import OrderedDict
import mapcss.webcolors
from drules import (BEVELJOIN, BUTTCAP, NOJOIN, ROUNDCAP, ROUNDJOIN,
from drules import (BEVELJOIN, BUTTCAP, NOJOIN, ROUNDCAP, ROUNDJOIN, SQUARECAP,
ClassifElement, ColorElement, Container, DrawElement, LineRule,
serialize_binary, serialize_text)

Expand Down Expand Up @@ -434,6 +434,16 @@ def dump_priorities(prio_range, path, maxzoom):

outfile.write(f'{group}{group_comment}=== {group_prio}\n')

def get_line_style(values, st, key, default, cl, zoom):
"""Maps a MapCSS linecap/linejoin value onto its drules constant."""
value = st.get(key, default)
if value not in values:
print(f'ERROR: unsupported {key} value "{value}" for z{zoom} {cl}')
global validation_errors_count
validation_errors_count += 1
value = default
return values[value]

def get_drape_priority(cl, dr_type, object_id, auto_dr_type = None, auto_comment = None, auto_prio_mod = 0):
if object_id == '::default':
object_id = ''
Expand Down Expand Up @@ -606,7 +616,7 @@ def addPattern(dashes):

visibility = {}

dr_linecaps = {'none': BUTTCAP, 'butt': BUTTCAP, 'round': ROUNDCAP}
dr_linecaps = {'none': BUTTCAP, 'butt': BUTTCAP, 'round': ROUNDCAP, 'square': SQUARECAP}
dr_linejoins = {'none': NOJOIN, 'bevel': BEVELJOIN, 'round': ROUNDJOIN}

# Build drules tree
Expand Down Expand Up @@ -708,7 +718,7 @@ def rule_sort_key(dict_):
for st in zstyle:
if st.get('casing-width') not in (None, 0) or st.get('casing-width-add') is not None: # and (st.get('width') or st.get('fill-color')):
is_area_st = 'fill-color' in st
if has_lines and not is_area_st and st.get('casing-linecap', 'butt') == 'butt':
if has_lines and not is_area_st:
dr_line = LineRule()

base_width = st.get('width', 0)
Expand All @@ -718,10 +728,10 @@ def rule_sort_key(dict_):
# Rail bridge styles use width from ::dash object instead of ::default.
if base_width == 0 or wst.get('object-id') != '::default':
base_width = wst.get('width', 0)
# 'casing-width' has precedence over 'casing-width-add'.
if st.get('casing-width') in (None, 0):
st['casing-width'] = base_width + st.get('casing-width-add')
base_width = 0
# 'casing-width' has precedence over 'casing-width-add'.
if st.get('casing-width') in (None, 0):
st['casing-width'] = base_width + st.get('casing-width-add')
base_width = 0

dr_line.width = round(base_width + st.get('casing-width') * 2, 2)
dr_line.color = mwm_encode_color(colors, st, "casing")
Expand All @@ -737,8 +747,8 @@ def rule_sort_key(dict_):
for i in st.get('casing-dashes', st.get('dashes', [])):
dr_line.dashdot.dd.extend([float(i)])
addPattern(dr_line.dashdot.dd)
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_line.cap = get_line_style(dr_linecaps, st, 'casing-linecap', 'butt', cl, zoom)
dr_line.join = get_line_style(dr_linejoins, st, 'casing-linejoin', 'round', cl, zoom)
dr_element.lines.extend([dr_line])

if has_fills and is_area_st and float(st.get('fill-opacity', 1)) > 0:
Expand All @@ -753,8 +763,8 @@ def rule_sort_key(dict_):
for i in st.get('dashes', []):
dr_line.dashdot.dd.extend([float(i)])
addPattern(dr_line.dashdot.dd)
dr_line.cap = dr_linecaps.get(st.get('linecap', 'butt'), BUTTCAP)
dr_line.join = dr_linejoins.get(st.get('linejoin', 'round'), ROUNDJOIN)
dr_line.cap = get_line_style(dr_linecaps, st, 'linecap', 'butt', cl, zoom)
dr_line.join = get_line_style(dr_linejoins, st, 'linejoin', 'round', cl, zoom)
dr_line.priority = get_drape_priority(cl, 'line', st.get('object-id'))
store_visibility(cl, 'line', st.get('object-id'), zoom)
dr_element.lines.extend([dr_line])
Expand Down
9 changes: 9 additions & 0 deletions tests/assets/case-2-generate-drules-mini/include/Roads.mapcss
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,12 @@ line|z10[highway=secondary],
line|z10[highway=secondary],
{width: 1.2;}

/* Automatic (::default) casings, exercised by testLibkomwm:
any casing-linecap is honoured, and casing-width-add is resolved
even when the line carries its own width. */

line|z4-9[highway=world_level],
{casing-width: 1; casing-color: #FF0000; casing-linecap: round;}

line|z6-9[highway=world_towns_level],
{casing-width-add: 1; casing-color: #00FF00;}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ highway-motorway-tunnel # line z6- (also has pathtex
highway-trunk # line z6- (also has pathtext z10-, shield::shield z10-)
highway-trunk-bridge # line z6- (also has pathtext z10-, shield::shield z10-)
highway-trunk-tunnel # line z6- (also has pathtext z10-, shield::shield z10-)
highway-world_level # line z4-9
highway-world_towns_level # line z6-9
highway-world_level # line z4-9 (also has line(casing) z4-9)
highway-world_towns_level # line z6-9 (also has line(casing) z6-9)
=== 310

# highway-world_level # line(casing) z4-9 (also has line z4-9)
# highway-world_towns_level # line(casing) z6-9 (also has line z6-9)
# === 309

highway-primary # line z8- (also has pathtext z10-, shield::shield z10-)
highway-primary-bridge # line z8- (also has pathtext z10-, shield::shield z10-)
highway-primary-tunnel # line z8- (also has pathtext z10-, shield::shield z10-)
Expand Down
18 changes: 18 additions & 0 deletions tests/testLibkomwm.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@ class Options(object):
self.assertEqual(len(container.cont), 20,
"Generated style_output.bin should contain 20 types with drawing rules")

def lines_at(type_name, zoom):
classif = next(c for c in container.cont if c.name == type_name)
return next(e for e in classif.element if e.scale == zoom).lines

# An automatic casing is rendered below its line (priority - 1), keeps its own
# linecap and is 2 * casing-width wider than the line. Both casing rules in
# include/Roads.mapcss use a width of 1.
casing, line = lines_at("highway-world_level", 4)
self.assertEqual((line.cap, line.priority), (drules.BUTTCAP, 310))
self.assertEqual((casing.cap, casing.priority), (drules.ROUNDCAP, 309))
self.assertAlmostEqual(casing.width, line.width + 2, places=5)

# 'casing-width-add' widens the line first, so the casing is 2 * (width + add).
# It must be resolved even when the line carries its own width.
casing, line = lines_at("highway-world_towns_level", 6)
self.assertEqual(casing.priority, line.priority - 1)
self.assertAlmostEqual(casing.width, (line.width + 1) * 2, places=5)

finally:
# Clean up generated files
files2delete = ["classificator.txt", "colors.txt", "patterns.txt", "style_output.bin",
Expand Down
Loading