Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bridge rendering #934

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5d5d643
add bridge rendering
claysmalley Sep 24, 2023
e8c2a9e
Merge branch 'main' into clay-bridge-areas
ZeLonewolf Sep 27, 2023
dad7da7
change bridge color and add guardrail
claysmalley Sep 26, 2023
13b51fc
change bridge outline to separate layer
claysmalley Sep 30, 2023
8992147
Merge branch 'main' into clay-bridge-areas
ZeLonewolf Oct 4, 2023
7a9fba2
Merge branch 'main' into clay-bridge-areas
claysmalley Oct 11, 2023
a27d6a3
Merge branch 'main' into clay-bridge-areas
claysmalley Oct 13, 2023
e1b258c
Merge branch 'main' into clay-bridge-areas
claysmalley Oct 15, 2023
089bddb
Merge branch 'main' into clay-bridge-areas
claysmalley Oct 18, 2023
55ec1c3
Merge branch 'main' into clay-bridge-areas
claysmalley Oct 20, 2023
62dfc89
de-emphasize road casing on bridges at high zoom
claysmalley Oct 20, 2023
0f36c5c
undo road casing opacity change
claysmalley Oct 21, 2023
7091942
Merge branch 'main' into clay-bridge-areas
claysmalley Oct 23, 2023
05049a3
Merge branch 'main' into clay-bridge-areas
ZeLonewolf Oct 24, 2023
aab8f3f
Merge branch 'main' into clay-bridge-areas
claysmalley Oct 24, 2023
ef34e68
Merge branch 'main' into clay-bridge-areas
claysmalley Oct 25, 2023
80c9688
change bridge outline to knockout at high zoom
claysmalley Oct 26, 2023
5ca7325
Merge branch 'main' into clay-bridge-areas
claysmalley Oct 29, 2023
49fa8e8
Merge branch 'main' into clay-bridge-areas
claysmalley Oct 29, 2023
483ad6f
factor out width expression
claysmalley Nov 5, 2023
e359c4f
Merge branch 'main' into clay-bridge-areas
ZeLonewolf Apr 22, 2024
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
2 changes: 2 additions & 0 deletions src/constants/color.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export const backgroundFill = `hsl(30, 44%, 96%)`;
export const backgroundFillTranslucent = `hsla(30, 44%, 96%, 0.8)`;

export const bridgeFill = "hsl(0, 20%, 80%)";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rose-colored fill easily creates an association with the red fill color on freeways. Placing this color beneath every non-freeway bridge muddles the distinction between these road classifications.


export const waterFill = "hsl(211, 50%, 85%)";
export const waterFillTranslucent = "hsla(211, 50%, 85%, 0.5)";
export const waterIntermittentFill = "hsla(211, 60%, 85%, 0.3)";
Expand Down
32 changes: 32 additions & 0 deletions src/js/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,35 @@ export function zoomMultiply(arr, multiplier) {
}
return transformedArray;
}

//Create a zoom interpolation expression, given width at zoom 20
export function zoomInterpolate(widthZ20) {
return [
"interpolate",
["exponential", 1.2],
["zoom"],
8,
multiplyMatchExpression(widthZ20, 1 / 16),
12,
multiplyMatchExpression(widthZ20, 1 / 4),
20,
widthZ20,
];
}

export function multiplyMatchExpression(value, factor) {
if (Array.isArray(value)) {
var result = [value[0], value[1]];
for (let i = 2; i < value.length - 1; i++) {
if (i % 2 == 0) {
result.push(value[i]);
} else {
result.push(multiplyMatchExpression(value[i], factor));
}
}
result.push(multiplyMatchExpression(value[value.length - 1], factor));
return result;
} else {
return value * factor;
}
}
123 changes: 123 additions & 0 deletions src/layer/bridge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"use strict";

import * as Color from "../constants/color.js";
import * as Util from "../js/util.js";

// Bridge areas
export const bridge = {
type: "fill-extrusion",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a fill extrusion layer? Is it to avoid having to create a separate layer for each layer value? This is clever, but the downside is that, in a stack interchange, the bottommost roads get buried under layers of translucency that make it difficult to tell where roads are going. A knockout effect would also obscure the road, but it would be much cleaner; it wouldn’t sort of draw you in.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without a fill extrusion, bridges over buildings looked wonky. I'll see if the guardrail approach improves this.

image

source: "openmaptiles",
"source-layer": "transportation",
id: "bridge",
minzoom: 13,
layout: {
visibility: "visible",
},
paint: {
"fill-extrusion-color": Color.bridgeFill,
"fill-extrusion-height": ["+", 3, ["coalesce", ["get", "layer"], 0]],
"fill-extrusion-opacity": 0.6,
},
filter: ["all", ["==", ["get", "class"], "bridge"]],
};

// Bridge casing for highways and railways
export const bridgeCasing = {
type: "line",
source: "openmaptiles",
"source-layer": "transportation",
id: "bridge_casing",
minzoom: 13,
layout: {
"line-cap": "butt",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This layer and the main road bridge layer need a line cap of square. Otherwise, the bridge area seems to extend beyond the roadway, obscuring a small segment of the roadway past either end of the bridge.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that and it looks weirder to me:

"line-cap": "butt",
image

"line-cap": "square",
image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, maybe just the normal bridge roadway layer then?

"line-join": "bevel",
visibility: "visible",
},
paint: {
"line-color": Color.bridgeFill,
"line-opacity": 0.8,
"line-width": Util.zoomInterpolate([
"match",
["get", "class"],
["rail", "transit"],
["match", ["get", "service"], ["siding", "spur", "yard"], 10, 14],
"motorway",
["match", ["get", "ramp"], 1, 18, 28],
"trunk",
[
"match",
["get", "expressway"],
1,
34,
["match", ["get", "ramp"], 1, 16, 28],
],
"primary",
[
"match",
["get", "expressway"],
1,
32,
["match", ["get", "ramp"], 1, 15, 24],
],
"secondary",
[
"match",
["get", "expressway"],
1,
26,
["match", ["get", "ramp"], 1, 14, 20],
],
["tertiary", "busway", "bus_guideway"],
[
"match",
["get", "expressway"],
1,
20,
["match", ["get", "ramp"], 1, 13, 18],
],
"minor",
14,
"service",
[
"match",
["get", "service"],
["alley", "driveway", "drive-through", "parking_aisle"],
9,
11,
],
20,
]),
},
filter: [
"all",
["==", ["get", "brunnel"], "bridge"],
[
"in",
["get", "class"],
[
"literal",
[
"motorway",
"trunk",
"primary",
"secondary",
"tertiary",
"busway",
"bus_guideway",
"minor",
"service",
"rail",
"transit",
],
],
],
],
};

export const legendEntries = [
{
description: "Bridge",
layers: [bridge.id],
filter: ["==", ["get", "class"], "bridge"],
},
];
4 changes: 3 additions & 1 deletion src/layer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as lyrAerialway from "./aerialway.js";
import * as lyrAeroway from "./aeroway.js";
import * as lyrBackground from "./background.js";
import * as lyrBoundary from "./boundary.js";
import * as lyrBridge from "./bridge.js";
import * as lyrConstruction from "./construction.js";
import * as lyrHighwayShield from "./highway_shield.js";
import * as lyrLanduse from "./landuse.js";
Expand Down Expand Up @@ -143,7 +144,8 @@ export function build(locales) {
layers.push(lyrBuilding.building);

var bridgeLayers = [
lyrRail.bridgeCasing,
lyrBridge.bridge,
lyrBridge.bridgeCasing,

lyrRoad.trunkLinkBridge.casing(),
lyrRoad.motorwayLinkBridge.casing(),
Expand Down
60 changes: 3 additions & 57 deletions src/layer/rail.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,6 @@ import * as Util from "../js/util.js";
// Exponent base for inter-zoom interpolation
let railExp = 1.2;

// Helper functions to create zoom interpolation expressions
function multiplyMatchExpression(value, factor) {
if (Array.isArray(value)) {
var result = [value[0], value[1]];
for (let i = 2; i < value.length - 1; i++) {
if (i % 2 == 0) {
result.push(value[i]);
} else {
result.push(multiplyMatchExpression(value[i], factor));
}
}
result.push(multiplyMatchExpression(value[value.length - 1], factor));
return result;
} else {
return value * factor;
}
}

function zoomInterpolate(widthZ20) {
return [
"interpolate",
["exponential", railExp],
["zoom"],
8,
multiplyMatchExpression(widthZ20, 1 / 16),
12,
multiplyMatchExpression(widthZ20, 1 / 4),
20,
widthZ20,
];
}

// Helper function to create a "filter" block for a particular railway class.
function filterRail(brunnel) {
return [
Expand Down Expand Up @@ -103,28 +71,6 @@ var opacity = [
1,
];

// Bridge casing layers
export const bridgeCasing = {
...defRail,
id: "rail_bridge-casing",
filter: [
"all",
["==", ["get", "brunnel"], "bridge"],
["in", ["get", "class"], ["literal", ["rail", "transit"]]],
],
minzoom: 13,
layout: {
"line-cap": "butt",
"line-join": "bevel",
visibility: "visible",
},
paint: {
"line-color": Color.backgroundFill,
"line-opacity": opacity,
"line-width": zoomInterpolate([...serviceSelector, 4, 6]),
},
};

// Generate a unique layer ID
function uniqueLayerID(part, brunnel, constraints) {
var layerID = ["rail", part, brunnel].join("_");
Expand Down Expand Up @@ -168,7 +114,7 @@ class Railway {
layer.paint = {
"line-color": lineColor,
"line-opacity": opacity,
"line-width": zoomInterpolate(lineWidth),
"line-width": Util.zoomInterpolate(lineWidth),
};
if (this.constraints != null) {
layer.filter.push(this.constraints);
Expand All @@ -191,8 +137,8 @@ class Railway {
layer.paint = {
"line-color": lineColor,
"line-opacity": opacity,
"line-width": zoomInterpolate(
multiplyMatchExpression(lineWidth, this.dashWidthFactor)
"line-width": Util.zoomInterpolate(
Util.multiplyMatchExpression(lineWidth, this.dashWidthFactor)
),
"line-dasharray": this.dashArray.map(
(stop) => stop / 2 / this.dashWidthFactor
Expand Down