Skip to content

Commit b0520c3

Browse files
committed
feat: add per-type route styling via style presets
Route types (royal, main, market, town, trail, footpath, major, local) now render in SVG sub-groups with distinct stroke-widths instead of sharing the parent group's single width. Style presets define per-type widths as multipliers of the group base, and royal/main roads override stroke-dasharray to render as solid lines. Zoom visibility now toggles sub-groups instead of individual paths.
1 parent bbd37de commit b0520c3

16 files changed

Lines changed: 473 additions & 45 deletions

public/main.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ let options = {
161161
};
162162

163163
// global style object; in v2.0 to be used for all map styles and render settings
164-
let style = {burgLabels: {}, burgIcons: {}, anchors: {}};
164+
let style = {burgLabels: {}, burgIcons: {}, anchors: {}, routes: {}};
165165

166166
let biomesData = Biomes.getDefault();
167167
let nameBases = Names.getNameBases(); // cultures-related data
@@ -528,10 +528,9 @@ function invokeActiveZooming() {
528528
footpath: 10
529529
};
530530

531-
routes.selectAll("path").each(function () {
532-
const type = this.dataset.type;
533-
if (!type) return;
534-
const minZoom = ROUTE_MIN_ZOOM[type] || 0;
531+
routes.selectAll("g g").each(function () {
532+
const minZoom = ROUTE_MIN_ZOOM[this.id];
533+
if (minZoom === undefined) return;
535534
if (scale < minZoom) this.classList.add("hidden");
536535
else this.classList.remove("hidden");
537536
});

public/modules/ui/layers.js

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -778,38 +778,69 @@ function toggleRoutes(event) {
778778
if (event && isCtrlClick(event)) editStyle("routes");
779779
} else {
780780
if (event && isCtrlClick(event)) return editStyle("routes");
781-
routes.selectAll("path").remove();
781+
routes.selectAll("#roads, #trails, #searoutes, #airroutes").html("");
782782
turnButtonOff("toggleRoutes");
783783
}
784784
}
785785

786786
function drawRoutes() {
787787
TIME && console.time("drawRoutes");
788-
const routePaths = {};
788+
const typedPaths = {};
789789

790790
for (const route of pack.routes) {
791791
const {i, group, points} = route;
792792
if (!points || points.length < 2) continue;
793-
if (!routePaths[group]) routePaths[group] = [];
794-
const typeAttr = route.type ? ` data-type="${route.type}"` : "";
795-
routePaths[group].push(`<path id="route${i}"${typeAttr} d="${Routes.getPath(route)}"/>`);
793+
const type = route.type || "";
794+
const key = type ? `${group}/${type}` : group;
795+
if (!typedPaths[key]) typedPaths[key] = {group, type, paths: []};
796+
typedPaths[key].paths.push(`<path id="route${i}" d="${Routes.getPath(route)}"/>`);
796797
}
797798

798-
routes.selectAll("path").remove();
799-
for (const group in routePaths) {
800-
routes.select("#" + group).html(routePaths[group].join(""));
799+
// clear all content from route group elements (sub-groups and paths)
800+
routes.selectAll("#roads, #trails, #searoutes, #airroutes").html("");
801+
802+
for (const key in typedPaths) {
803+
const {group, type, paths} = typedPaths[key];
804+
const groupEl = routes.select("#" + group);
805+
if (groupEl.empty()) continue;
806+
if (type) {
807+
const subGroup = groupEl.append("g").attr("id", type);
808+
applyRouteTypeStyle(subGroup.node(), type);
809+
subGroup.html(paths.join(""));
810+
} else {
811+
groupEl.html(groupEl.html() + paths.join(""));
812+
}
801813
}
802814

803815
TIME && console.timeEnd("drawRoutes");
804816
}
805817

806818
function drawRoute(route) {
807-
const el = routes
808-
.select("#" + route.group)
809-
.append("path")
810-
.attr("d", Routes.getPath(route))
811-
.attr("id", "route" + route.i);
812-
if (route.type) el.attr("data-type", route.type);
819+
const groupEl = routes.select("#" + route.group);
820+
if (groupEl.empty()) return;
821+
if (route.type) {
822+
let subGroup = groupEl.select("#" + route.type);
823+
if (subGroup.empty()) {
824+
subGroup = groupEl.append("g").attr("id", route.type);
825+
applyRouteTypeStyle(subGroup.node(), route.type);
826+
}
827+
subGroup.append("path").attr("d", Routes.getPath(route)).attr("id", "route" + route.i);
828+
} else {
829+
groupEl.append("path").attr("d", Routes.getPath(route)).attr("id", "route" + route.i);
830+
}
831+
}
832+
833+
function applyRouteTypeStyle(el, type) {
834+
const typeStyle = style.routes[type];
835+
if (!typeStyle) return;
836+
for (const attr in typeStyle) {
837+
const value = typeStyle[attr];
838+
if (value === null || value === "null") {
839+
el.removeAttribute(attr);
840+
} else if (value !== undefined) {
841+
el.setAttribute(attr, value);
842+
}
843+
}
813844
}
814845

815846
function toggleMilitary(event) {

public/modules/ui/style-presets.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ function applyStyle(styleJSON) {
8888
style.anchors[group] = styleJSON[selector];
8989
}
9090

91+
if (selector.startsWith("#routes ")) {
92+
const type = selector.split("#").pop();
93+
style.routes[type] = styleJSON[selector];
94+
}
95+
9196
const el = document.querySelector(selector);
9297
if (!el) continue;
9398

@@ -362,6 +367,11 @@ function addStylePreset() {
362367
attributes[`#anchors > g#${name}`] = anchorsAttributes;
363368
});
364369

370+
const routeTypeAttrs = ["stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "opacity"];
371+
for (const type of ["royal", "main", "market", "town", "trail", "footpath", "major", "local"]) {
372+
attributes[`#routes [id="${type}"]`] = routeTypeAttrs;
373+
}
374+
365375
for (const selector in attributes) {
366376
const el = document.querySelector(selector);
367377
if (!el) continue;

public/styles/ancient.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,32 @@
225225
"filter": null,
226226
"mask": null
227227
},
228+
"#routes #royal": {
229+
"stroke-width": 1.12,
230+
"stroke-dasharray": null
231+
},
232+
"#routes #main": {
233+
"stroke-width": 0.88,
234+
"stroke-dasharray": null
235+
},
236+
"#routes #market": {
237+
"stroke-width": 0.68
238+
},
239+
"#routes #town": {
240+
"stroke-width": 0.52
241+
},
242+
"#routes #trail": {
243+
"stroke-width": 0.5
244+
},
245+
"#routes #footpath": {
246+
"stroke-width": 0.3
247+
},
248+
"#routes #major": {
249+
"stroke-width": 0.84
250+
},
251+
"#routes #local": {
252+
"stroke-width": 0.48
253+
},
228254
"#burgLabels > g#skyburg": {
229255
"opacity": 1,
230256
"fill": "#5a3e2b",

public/styles/atlas.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,32 @@
227227
"filter": null,
228228
"mask": null
229229
},
230+
"#routes #royal": {
231+
"stroke-width": 1.47,
232+
"stroke-dasharray": null
233+
},
234+
"#routes #main": {
235+
"stroke-width": 1.16,
236+
"stroke-dasharray": null
237+
},
238+
"#routes #market": {
239+
"stroke-width": 0.89
240+
},
241+
"#routes #town": {
242+
"stroke-width": 0.68
243+
},
244+
"#routes #trail": {
245+
"stroke-width": 0.43
246+
},
247+
"#routes #footpath": {
248+
"stroke-width": 0.26
249+
},
250+
"#routes #major": {
251+
"stroke-width": 0.63
252+
},
253+
"#routes #local": {
254+
"stroke-width": 0.36
255+
},
230256
"#burgLabels > g#skyburg": {
231257
"opacity": 1,
232258
"fill": "#5a3497",

public/styles/clean.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,32 @@
228228
"filter": null,
229229
"mask": null
230230
},
231+
"#routes #royal": {
232+
"stroke-width": 0.98,
233+
"stroke-dasharray": null
234+
},
235+
"#routes #main": {
236+
"stroke-width": 0.77,
237+
"stroke-dasharray": null
238+
},
239+
"#routes #market": {
240+
"stroke-width": 0.6
241+
},
242+
"#routes #town": {
243+
"stroke-width": 0.45
244+
},
245+
"#routes #trail": {
246+
"stroke-width": 0.25
247+
},
248+
"#routes #footpath": {
249+
"stroke-width": 0.15
250+
},
251+
"#routes #major": {
252+
"stroke-width": 0.63
253+
},
254+
"#routes #local": {
255+
"stroke-width": 0.36
256+
},
231257
"#burgLabels > g#skyburg": {
232258
"opacity": 1,
233259
"fill": "#5a3497",

public/styles/cyberpunk.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,32 @@
227227
"filter": null,
228228
"mask": null
229229
},
230+
"#routes #royal": {
231+
"stroke-width": 1.26,
232+
"stroke-dasharray": null
233+
},
234+
"#routes #main": {
235+
"stroke-width": 0.99,
236+
"stroke-dasharray": null
237+
},
238+
"#routes #market": {
239+
"stroke-width": 0.77
240+
},
241+
"#routes #town": {
242+
"stroke-width": 0.59
243+
},
244+
"#routes #trail": {
245+
"stroke-width": 0.2
246+
},
247+
"#routes #footpath": {
248+
"stroke-width": 0.12
249+
},
250+
"#routes #major": {
251+
"stroke-width": 0.84
252+
},
253+
"#routes #local": {
254+
"stroke-width": 0.48
255+
},
230256
"#burgLabels > g#skyburg": {
231257
"opacity": 1,
232258
"fill": "#ff00ff",

public/styles/darkSeas.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,32 @@
216216
"filter": null,
217217
"mask": null
218218
},
219+
"#routes #royal": {
220+
"stroke-width": 1.75,
221+
"stroke-dasharray": null
222+
},
223+
"#routes #main": {
224+
"stroke-width": 1.38,
225+
"stroke-dasharray": null
226+
},
227+
"#routes #market": {
228+
"stroke-width": 1.06
229+
},
230+
"#routes #town": {
231+
"stroke-width": 0.81
232+
},
233+
"#routes #trail": {
234+
"stroke-width": 0.75
235+
},
236+
"#routes #footpath": {
237+
"stroke-width": 0.45
238+
},
239+
"#routes #major": {
240+
"stroke-width": 1.4
241+
},
242+
"#routes #local": {
243+
"stroke-width": 0.8
244+
},
219245
"#burgLabels > g#skyburg": {
220246
"opacity": 1,
221247
"fill": "#9040bf",

public/styles/default.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,32 @@
227227
"filter": null,
228228
"mask": null
229229
},
230+
"#routes #royal": {
231+
"stroke-width": 0.98,
232+
"stroke-dasharray": null
233+
},
234+
"#routes #main": {
235+
"stroke-width": 0.77,
236+
"stroke-dasharray": null
237+
},
238+
"#routes #market": {
239+
"stroke-width": 0.6
240+
},
241+
"#routes #town": {
242+
"stroke-width": 0.45
243+
},
244+
"#routes #trail": {
245+
"stroke-width": 0.25
246+
},
247+
"#routes #footpath": {
248+
"stroke-width": 0.15
249+
},
250+
"#routes #major": {
251+
"stroke-width": 0.63
252+
},
253+
"#routes #local": {
254+
"stroke-width": 0.36
255+
},
230256
"#burgLabels > g#skyburg": {
231257
"opacity": 1,
232258
"fill": "#6a0dad",

public/styles/gloom.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,32 @@
228228
"filter": null,
229229
"mask": null
230230
},
231+
"#routes #royal": {
232+
"stroke-width": 1.26,
233+
"stroke-dasharray": null
234+
},
235+
"#routes #main": {
236+
"stroke-width": 0.99,
237+
"stroke-dasharray": null
238+
},
239+
"#routes #market": {
240+
"stroke-width": 0.77
241+
},
242+
"#routes #town": {
243+
"stroke-width": 0.59
244+
},
245+
"#routes #trail": {
246+
"stroke-width": 0.2
247+
},
248+
"#routes #footpath": {
249+
"stroke-width": 0.12
250+
},
251+
"#routes #major": {
252+
"stroke-width": 0.84
253+
},
254+
"#routes #local": {
255+
"stroke-width": 0.48
256+
},
231257
"#burgLabels > g#skyburg": {
232258
"opacity": 1,
233259
"fill": "#5a1870",

0 commit comments

Comments
 (0)