meta { title: "MTB Trailmap (MTB Difficulty, uphill difficulty, path width, visibility and surface)"; icon: "presets/sport/cycling.svg"; description: "Easily visualize MTB trail difficulty, uphill difficulty, trail width, visibility, and surface type with color-coded indicators and Text labels. Inspired by Trailmap.fi, this style ensures your edits are accurately reflected, providing clear and detailed trail information for mountain biking. Includes toggle options allow you to choose which trail attributes are displayed on the map layer. Designed to work seamlessly with JOSM's default styles, such as JOSM Standard and Potlatch 2."; version: "1.2.2_2025-05-07"; author: "Tommi Contursi"; link: "https://github.com/TommiContursi/MTB-TrailMap-JOSM-Style"; min-josm-version: 7110; } /* Settings toggles for users */ /* Group labels */ settings::mtb_difficulty { label: "MTB Difficulty (mtb:scale)"; } settings::mtb_uphill_difficulty { label: "MTB Uphill Difficulty (mtb:scale:uphill)"; } settings::path_surface { label: "Path Surface (surface)"; } settings::trail_visibility { label: "Trail Visibility (trail_visibility)"; } settings::surface_quality { label: "Surface Quality (smoothness)"; } settings::path_width { label: "Path Width (width)"; } /* MTB Difficulty */ setting::mtb_scale_display { default: true; type: boolean; label: "Color-coded line (mtb:scale)"; group: "mtb_difficulty"; } setting::mtb_scale_text_display { default: true; type: boolean; label: "Text label (mtb:scale)"; group: "mtb_difficulty"; } /* MTB Uphill Difficulty */ setting::mtb_uphill_display { default: true; type: boolean; label: "Separate color-coded line (mtb:scale:uphill)"; group: "mtb_uphill_difficulty"; } setting::mtb_uphill_display_text { default: true; type: boolean; label: "Text label (mtb:scale:uphill)"; group: "mtb_uphill_difficulty"; } /* Path Surface */ setting::surface_display_path { default: true; type: boolean; label: "Color-coded line (surface)"; group: "path_surface"; } setting::surface_display_text { default: true; type: boolean; label: "Text label (surface)"; group: "path_surface"; } setting::surface_display { default: false; type: boolean; label: "Separate color-coded line (surface)"; group: "path_surface"; } /* Trail Visibility */ setting::trail_visibility_display { default: false; type: boolean; label: "Color-coded line (trail_visibility)"; group: "trail_visibility"; } setting::trail_visibility_text_display { default: false; type: boolean; label: "Text label (trail_visibility)"; group: "trail_visibility"; } setting::path_opacity { default: true; type: boolean; label: "Opacity (trail_visibility)"; group: "trail_visibility"; } /* Surface Quality */ setting::smoothness_display { default: false; type: boolean; label: "Color-coded line (smoothness)"; group: "surface_quality"; } setting::smoothness_text_display { default: false; type: boolean; label: "Text label (smoothness)"; group: "surface_quality"; } /* Path Width */ setting::width_text_display { default: false; type: boolean; label: "Text label (width)"; group: "path_width"; } /* Basic canvas background color canvas { fill-color: #606060; } */ /* Node styles for different zoom levels */ node { symbol-shape: circle; symbol-fill-color: darkgrey; symbol-stroke-color: lightgreen; } node, area { text-color: black; } node|z-15 { symbol-size: 0.1; } node|z16 { symbol-size: 2; } node|z17 { symbol-size: 3; symbol-stroke-width: 1; } node|z18- { symbol-size: 4; symbol-stroke-width: 1; } node:connection { symbol-fill-color: yellow; } /*Gates and barriers*/ node { text-anchor-horizontal: center; text-anchor-vertical: below; } node[barrier=gate], node[highway=gate] {icon-width:"25"; icon-image: "presets/barrier/gate.svg"; text: "barrier"; text-offset:0; font-size: 10; text-halo-color: white; text-halo-radius: 1; z-index: 20;} node[barrier=lift_gate] {icon-width:"25";icon-image: "presets/barrier/lift_gate.svg"; text-offset:0; text: "barrier"; text-offset:0; font-size: 10; text-halo-color: white; text-halo-radius: 1; z-index: 20; } node[barrier=kissing_gate] {icon-width:"25"; icon-image: "presets/barrier/kissing_gate.svg"; text-offset:0; text: "barrier"; text-offset:0; font-size: 10; text-halo-color: white; text-halo-radius: 1; z-index: 20; } node[barrier=cycle_barrier] {icon-width:"25"; icon-image: "presets/barrier/cycle_barrier.svg"; text-offset:0; text: "barrier"; text-offset:0; font-size: 10; text-halo-color: white; text-halo-radius: 1; z-index: 20; } node[barrier=log] {icon-width:"25"; icon-image: "presets/barrier/log.svg"; text-offset:0; text: "barrier"; text-offset:0; font-size: 10; text-halo-color: white; text-halo-radius: 1; z-index: 20; } node[obstacle=fallen_tree] {icon-width:"25"; icon-image: "presets/barrier/log.svg"; text-offset:0; text: "obstacle"; text-offset:0; font-size: 10; text-halo-color: white; text-halo-radius: 1; z-index: 20; } node[noexit=yes] {icon-width:"25"; icon-image: "presets/vehicle/restriction/dead_end.svg";z-index: 20; } node[/^fixme/] {icon-width:"25"; icon-image: "presets/misc/fixme.svg"; text-offset:0; text: "fixme"; text-offset:0; font-size: 10; text-halo-color: white; text-halo-radius: 1; z-index: 20; } node[fixme=continue] {icon-width:"25"; icon-image: "presets/misc/danger.svg"; z-index: 20; } /* Basic highway styles for cycleway, track, and path */ way[highway=~/track|path/] { color: #000; width: 2; casing-width: 2; dashes: 10, 5; } way[highway=track] { casing-color: #ffffff00; } way[highway=cycleway] { color: #3146d7; width: 3; dashes: 15, 5; casing-width: 1; dashes-background-color: #fff; casing-color: #fff; } way[highway=footway] { color: #a43820; width: 4; dashes: 5, 5; } /* Width based on track width */ way[highway=~/track|path/][width] { width: eval( tag("width") < 0.6 ? 2 : tag("width") < 0.9 ? 2 : tag("width") < 1.8 ? 3 : tag("width") > 1.8 ? 4 : 2 ); casing-width: eval( tag("width") < 0.6 ? 2 : tag("width") < 0.9 ? 2 : tag("width") < 1.8 ? 3 : tag("width") > 1.8 ? 4 : 2 ); } /* Visibility based styles */ way[trail_visibility=excellent] { dashes: 10, 6; } way[trail_visibility=good] { dashes: 10, 6; } way[trail_visibility=intermediate] { dashes: 2, 2; } way[trail_visibility=bad] { dashes: 2, 2; } way[trail_visibility=horrible] { dashes: 2, 2; } way[trail_visibility=no] { dashes: 2, 2; } way[setting("path_opacity")][trail_visibility=intermediate]{ casing-opacity: 0.75; dashes-background-opacity: 0.75; } way[setting("path_opacity")][trail_visibility=bad]{ casing-opacity: 0.5; dashes-background-opacity: 0.5; } way[setting("path_opacity")][trail_visibility=horrible]{ casing-opacity: 0.5; dashes-background-opacity: 0.5; } way[setting("path_opacity")][trail_visibility=no]{ casing-opacity: 0.5; dashes-background-opacity: 0.5; } /* surface based styles let's draw these on the trail by defaul and the style will be overridden with the trail difficulty*/ way[highway=~/track|path/][setting("surface_display_path")][surface] { casing-color: eval( tag("surface") == "paved" ? "#555555" : tag("surface") == "asphalt" ? "#333333" : tag("surface") == "gravel" ? "#cccccc" : tag("surface") == "dirt" ? "#a52a2a" : tag("surface") == "grass" ? "#006400" : tag("surface") == "sand" ? "#f4a460" : tag("surface") == "wood" ? "#deb887" : tag("surface") == "concrete" ? "#808080" : tag("surface") == "cobblestone" ? "#8b4513" : tag("surface") == "pebblestone" ? "#a9a9a9" : tag("surface") == "compacted" ? "#b8860b" : tag("surface") == "fine_gravel" ? "#d3d3d3" : tag("surface") == "grass_paver" ? "#006400" : tag("surface") == "paving_stones" ? "#a0522d" : tag("surface") == "metal" ? "#b0c4de" : tag("surface") == "bricks" ? "#b22222" : tag("surface") == "earth" ? "#a52a2a" : tag("surface") == "clay" ? "#d2691e" : tag("surface") == "mud" ? "#8b4513" : tag("surface") == "ground" ? "#8b4513" : tag("surface") == "rock" ? "#808080" : "#d3d3d3" ); } /* Basic road styles */ way[highway=~/unclassified|residential|tertiary|service/] { color: darkgrey; opacity: 0.5; casing-color: grey; casing-width: 2; } way|z-14[highway=~/unclassified|residential|tertiary|service/] { width: 3; } way|z15-16[highway=~/unclassified|residential|tertiary|service/] { width: 4; } way|z17-[highway=~/unclassified|residential|tertiary|service/] { width: 6; } way[highway=~/motorway|trunk|primary|secondary/] { color: darkgrey; opacity: 0.5; casing-color: grey; casing-width: 2; } way|z-14[highway=~/motorway|trunk|primary|secondary/] { width: 3; } way|z15-16[highway=~/motorway|trunk|primary|secondary/] { width: 4; } way|z17-[highway=~/motorway|trunk|primary|secondary/] { width: 6; } /* Waterways */ way[waterway] { color: #6CCEE4; width: 3; } /* Bicycle route relation underlay */ relation[type=route][route=~/bicycle|mtb/][network=ncn] > way::relation_underlay { z-index: -200; width: 15; color: red; opacity: 0.15; linecap: none; } relation[type=route][route=~/bicycle|mtb/][network=rcn] > way::relation_underlay { z-index: -200; width: 15; color: cyan; opacity: 0.15; linecap: none; } relation[type=route][route=~/bicycle|mtb/][network=lcn] > way::relation_underlay { z-index: -200; width: 15; color: blue; opacity: 0.15; linecap: none; } /* Show mtb:scale as text */ way|z18-[mtb:scale][setting("mtb_scale_text_display")]::mtbscale_layer { font-family: Arial; text: concat("S", tag("mtb:scale")); text-color: eval( tag("mtb:scale") == "0-" ? "#50d6eb" : tag("mtb:scale") == "0" ? "#75e009" : tag("mtb:scale") == "1" ? "#e3e800" : tag("mtb:scale") == "2" ? "#feb13e" : tag("mtb:scale") == "3" ? "#ff4454" : tag("mtb:scale") == "4" ? "#f20bab" : tag("mtb:scale") == "5" ? "#bf1cf5" : tag("mtb:scale") == "6" ? "#8100ac" : "#ffffff" ); font-size: 10; font-weight: bold; text-position: line; z-index: 2; text-halo-color: black; text-halo-radius: 1; } way|z19-[mtb:scale][setting("mtb_scale_text_display")]::mtbscale_layer { font-size: 14; } way|z20-[mtb:scale][setting("mtb_scale_text_display")]::mtbscale_layer { font-size: 16; } /* Surface information display for tracks and paths */ way[highway=~/track|path/][surface][setting("surface_display")]::path_surface { color: eval( tag("surface") == "paved" ? "#555555" : tag("surface") == "asphalt" ? "#333333" : tag("surface") == "gravel" ? "#555555" : tag("surface") == "dirt" ? "#a52a2a" : tag("surface") == "grass" ? "#006400" : tag("surface") == "sand" ? "#f4a460" : tag("surface") == "wood" ? "#deb887" : tag("surface") == "concrete" ? "#808080" : tag("surface") == "cobblestone" ? "#8b4513" : tag("surface") == "pebblestone" ? "#555555" : tag("surface") == "compacted" ? "#b8860b" : tag("surface") == "fine_gravel" ? "#555555" : tag("surface") == "grass_paver" ? "#006400" : tag("surface") == "paving_stones" ? "#a0522d" : tag("surface") == "metal" ? "#b0c4de" : tag("surface") == "bricks" ? "#b22222" : tag("surface") == "earth" ? "#a52a2a" : tag("surface") == "clay" ? "#d2691e" : tag("surface") == "mud" ? "#8b4513" : tag("surface") == "ground" ? "#8b4513" : tag("surface") == "rock" ? "#808080" : "#000000" ); width: eval( tag("width") < 0.6 ? 2 : tag("width") < 0.9 ? 2 : tag("width") < 1.8 ? 3 : tag("width") > 1.8 ? 4 : 2 ); dashes: 3, 3; offset: -5; /* opacity: eval( tag("trail_visibility") == "excellent" ? 1 : tag("trail_visibility") == "good" ? 1 : tag("trail_visibility") == "intermediate" ? 0.75 : tag("trail_visibility") == "bad" ? 0.5 : tag("trail_visibility") == "horrible" ? 0.5 : 1 ); */ } /* Text display for surface information */ way|z18-[highway=~/track|path/][surface][setting("surface_display_text")]::surface_text { text: tag("surface"); text-color: eval( tag("surface") == "paved" ? "#444444" : tag("surface") == "asphalt" ? "#222222" : tag("surface") == "gravel" ? "#999999" : tag("surface") == "dirt" ? "#8b0000" : tag("surface") == "grass" ? "#006400" : tag("surface") == "sand" ? "#d2b48c" : tag("surface") == "wood" ? "#8b4513" : tag("surface") == "concrete" ? "#505050" : tag("surface") == "cobblestone" ? "#654321" : tag("surface") == "pebblestone" ? "#696969" : tag("surface") == "compacted" ? "#8b5a2b" : tag("surface") == "fine_gravel" ? "#a9a9a9" : tag("surface") == "grass_paver" ? "#006400" : tag("surface") == "paving_stones" ? "#6b4423" : tag("surface") == "metal" ? "#4682b4" : tag("surface") == "bricks" ? "#800000" : tag("surface") == "earth" ? "#8b0000" : tag("surface") == "clay" ? "#a0522d" : tag("surface") == "mud" ? "#4b2e0f" : tag("surface") == "ground" ? "#4b2e0f" : tag("surface") == "rock" ? "#505050" : "#a9a9a9" ); font-size: 10; font-family: Arial; font-weight: bold; text-position: line; text-offset: -10; z-index: 3; text-halo-color: #ffffff; text-halo-radius: 1; } way|z19-[highway=~/track|path/][surface][setting("surface_display_text")]::surface_text { font-size: 11; text-offset: -12; } way|z20-[highway=~/track|path/][surface][setting("surface_display_text")]::surface_text { font-size: 13; text-offset: -14; } /* Show mtb:scale as colors on the path */ way[mtb:scale][setting("mtb_scale_display")] { casing-color: eval( tag("mtb:scale") == "0-" ? "#10c5a1" : tag("mtb:scale") == "0" ? "#75df08" : tag("mtb:scale") == "1" ? "#d8dc00" : tag("mtb:scale") == "2" ? "#feb13e" : tag("mtb:scale") == "3" ? "#ff4355" : tag("mtb:scale") == "4" ? "#e410dd" : tag("mtb:scale") == "5" ? "#9600c8" : tag("mtb:scale") == "6" ? "#8101ad" : "#ffffff" ); dashes-background-color: eval( tag("mtb:scale") == "0-" ? "#10c5a1" : tag("mtb:scale") == "0" ? "#75df08" : tag("mtb:scale") == "1" ? "#d8dc00" : tag("mtb:scale") == "2" ? "#feb13e" : tag("mtb:scale") == "3" ? "#ff4355" : tag("mtb:scale") == "4" ? "#e410dd" : tag("mtb:scale") == "5" ? "#9600c8" : tag("mtb:scale") == "6" ? "#8101ad" : "#ffffff" ); } /* Text display for mtb:scale:uphill */ way|z18-[mtb:scale][mtb:scale:uphill][setting("mtb_uphill_display_text")]::uphill_difficulty { text: concat("↑ S", tag("mtb:scale:uphill")); text-color: eval( tag("mtb:scale:uphill") == "0" ? "#75df08" : tag("mtb:scale:uphill") == "1" ? "#d8dc00" : tag("mtb:scale:uphill") == "2" ? "#feb13e" : tag("mtb:scale:uphill") == "3" ? "#ff4355" : tag("mtb:scale:uphill") == "4" ? "#e410dd" : tag("mtb:scale:uphill") == "5" ? "#8101ad" : "#ffffff" ); font-size: 10; font-weight: bold; text-offset: 12; text-position: line; z-index: 2; text-halo-color: black; text-halo-radius: 1; } way|z19-[mtb:scale][mtb:scale:uphill][setting("mtb_uphill_display_text")]::uphill_difficulty { font-size: 12; text-offset: 14; } way|z20-[mtb:scale][mtb:scale:uphill][setting("mtb_uphill_display_text")]::uphill_difficulty { font-size: 14; text-offset: 16; } /* Show mtb:scale:uphill as colors on the path */ way[mtb:scale:uphill][setting("mtb_uphill_display")]::uphill_difficulty { color: eval( tag("mtb:scale:uphill") == "0" ? "#3e5a04" : tag("mtb:scale:uphill") == "1" ? "#6d7300" : tag("mtb:scale:uphill") == "2" ? "#8b4f20" : tag("mtb:scale:uphill") == "3" ? "#8b202c" : tag("mtb:scale:uphill") == "4" ? "#710555" : tag("mtb:scale:uphill") == "5" ? "#5b0d7a" : "#999999" ); width: eval( tag("width") < 0.6 ? 4 : tag("width") < 0.9 ? 5 : tag("width") < 1.8 ? 6 : tag("width") > 1.8 ? 7 : 5 ); offset: eval( tag("width") < 0.6 ? 4 : tag("width") < 0.9 ? 6 : tag("width") < 1.8 ? 8 : tag("width") > 1.8 ? 10 : 6 ); /* opacity: eval( tag("trail_visibility") == "excellent" ? 1 : tag("trail_visibility") == "good" ? 1 : tag("trail_visibility") == "intermediate" ? 0.75 : tag("trail_visibility") == "bad" ? 0.5 : tag("trail_visibility") == "horrible" ? 0.5 : 1 ); */ dashes-background-opacity: 0.25; dashes-background-color: eval( tag("mtb:scale:uphill") == "0" ? "#75e009" : tag("mtb:scale:uphill") == "1" ? "#e3e800" : tag("mtb:scale:uphill") == "2" ? "#feb13e" : tag("mtb:scale:uphill") == "3" ? "#ff4454" : tag("mtb:scale:uphill") == "4" ? "#f20bab" : tag("mtb:scale:uphill") == "5" ? "#bf1cf5" : "#ffffff" ); } /*Eval doesn't work with dashes so this needs to be here */ way[mtb:scale:uphill=0][setting("mtb_uphill_display")]::uphill_difficulty { dashes: 0, 15; } way[mtb:scale:uphill=1][setting("mtb_uphill_display")]::uphill_difficulty { dashes: 1.4, 15; } way[mtb:scale:uphill=2][setting("mtb_uphill_display")]::uphill_difficulty { dashes: 1.4, 3, 1.4, 15; } way[mtb:scale:uphill=3][setting("mtb_uphill_display")]::uphill_difficulty { dashes: 1.4, 3, 1.4, 3, 1.4, 17; } way[mtb:scale:uphill=4][setting("mtb_uphill_display")]::uphill_difficulty { dashes: 1.4, 3, 1.4, 3, 1.4, 3, 1.4, 18; } way[mtb:scale:uphill=5][setting("mtb_uphill_display")]::uphill_difficulty { dashes: 1.4, 3, 1.4, 3, 1.4, 3, 1.4, 3, 1.4, 20; } /* Text display for trail_visibility */ way|z18-[trail_visibility][setting("trail_visibility_text_display")]::trail_visibility_text { text: tag("trail_visibility"); text-color: #000000; font-size: 10; font-family: Arial; font-weight: bold; text-offset: 16; text-position: line; z-index: 3; text-halo-color: #ffffff; text-halo-radius: 1; } way|z19-[trail_visibility][setting("trail_visibility_text_display")]::trail_visibility_text { font-size: 11; text-offset: 18; } way|z20-[trail_visibility][setting("trail_visibility_text_display")]::trail_visibility_text { font-size: 13; text-offset: 20; } /* Text display for smoothness */ way|z18-[smoothness][setting("smoothness_text_display")]::smoothness_text { text: tag("smoothness"); text-color: #202020; font-size: 10; font-family: Arial; font-weight: bold; text-offset: 18; text-position: line; z-index: 3; text-halo-color: #ffffff; text-halo-radius: 1; } way|z19-[smoothness][setting("smoothness_text_display")]::smoothness_text { font-size: 11; text-offset: 18; } way|z20-[smoothness][setting("smoothness_text_display")]::smoothness_text { font-size: 13; text-offset: 20; } /* Text display for width */ way|z18-[width][setting("width_text_display")]::width_text { text: concat(tag("width"), " m"); text-color: #000000; font-size: 10; font-family: Arial; font-weight: bold; text-offset: 20; text-position: line; z-index: 3; text-halo-color: #ffffff; text-halo-radius: 1; } way|z19-[width][setting("width_text_display")]::width_text { font-size: 11; text-offset: 18; } way|z20-[width][setting("width_text_display")]::width_text { font-size: 13; text-offset: 20; } /* Show trail_visibility as colors on the path */ way[highway=~/track|path/][trail_visibility][setting("trail_visibility_display")] { casing-color: eval( tag("trail_visibility") == "excellent" ? "#00cc00" : /* green */ tag("trail_visibility") == "good" ? "#66cc66" : /* light green */ tag("trail_visibility") == "intermediate" ? "#cccc00" :/* yellow */ tag("trail_visibility") == "bad" ? "#ff9900" : /* orange */ tag("trail_visibility") == "horrible" ? "#ff3300" : /* red */ tag("trail_visibility") == "no" ? "#990000" : /* dark red */ "#cccccc" /* default / unknown */ ); } /* Show smoothness as colors on the path */ way[highway=~/track|path/][smoothness][setting("smoothness_display")] { casing-color: eval( tag("smoothness") == "excellent" ? "#00cc00" : /* green */ tag("smoothness") == "good" ? "#66cc66" : /* light green */ tag("smoothness") == "intermediate" ? "#cccc00" : /* yellow */ tag("smoothness") == "bad" ? "#ff9900" : /* orange */ tag("smoothness") == "very_bad" ? "#ff6600" : /* reddish orange */ tag("smoothness") == "horrible" ? "#ff3300" : /* red */ tag("smoothness") == "very_horrible" ? "#990000" : /* dark red */ tag("smoothness") == "impassable" ? "#000000" : /* black */ "#999999" /* default / unknown */ ); } /* Indicates no bicycle access or restricted access (no, private, permissive) for tracks and paths */ way[highway=~/track|path/][bicycle=permissive]::core_access, way[highway=~/track|path/][access=permissive]::core_access { z-index: -1; width: eval( tag("width") < 0.6 ? 10 : tag("width") < 0.9 ? 12 : tag("width") > 1.8 ? 14 : 12 ); color: #ff0000; dashes: 2, 15; } way[highway=~/track|path/][bicycle=no]::core_access, way[highway=~/track|path/][bicycle=private]::core_access, way[highway=~/track|path/][access=no]::core_access, way[highway=~/track|path/][access=private]::core_access { z-index: -1; width: eval( tag("width") < 0.6 ? 10 : tag("width") < 0.9 ? 12 : tag("width") > 1.8 ? 14 : 12 ); color: #ff0000; dashes: 4, 4; } /* Obstacles based styles */ way[highway=~/track|path/][obstacle=vegetation]::core_access { z-index: -1; color: #008300; dashes: 3, 7; offset: 8; width: eval( tag("width") < 0.6 ? 4 : tag("width") < 0.9 ? 6 : tag("width") > 1.8 ? 9 : 6 ); } way[highway=~/track|path/][obstacle=mud]::core_access { z-index: -1; color: #0071ff; dashes: 3, 7; offset: 8; width: eval( tag("width") < 0.6 ? 4 : tag("width") < 0.9 ? 6 : tag("width") > 1.8 ? 9 : 6 ); }