diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d83334ceb..0d77efa8b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -160,12 +160,17 @@ boilerplate in `scripts/taginfo_template.json`.
1. Please prettify all files prior to submission. Run `npm run code_format` to
format code files with [prettier][90] and SVG files with [SVGO][svgo].
-2. If you are introducing a novel approach to depicting a layer or feature
+2. If you are introducing a new kind of feature to the style, add a section to
+ `src/js/legend_config.js` or a legend entry in the corresponding file in
+ `src/layer/` that tells the Legend control how to find and render a
+ representative feature. Also try out the Samples button to catch any visual
+ conflicts.
+3. If you are introducing a novel approach to depicting a layer or feature
property from the OpenMapTiles schema, document how the corresponding
OpenStreetMap key or tag is used in `scripts/taginfo_template.json`.
-3. If any shield background icons are introduced, add lines to `src/shieldtest.js`
+4. If any shield background icons are introduced, add lines to `src/shieldtest.js`
to demonstrate overlaid text on each of them.
-4. If you are introducing new JavaScript code that can run independently of a
+5. If you are introducing new JavaScript code that can run independently of a
browser environment, add automated unit tests for it to `test/spec/`, then
run `npm test` to ensure that they pass. This project structures unit tests
using [Chai](https://www.chaijs.com/guide/styles/) for assertions.
diff --git a/README.md b/README.md
index 80bc0c53e..75addefd4 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ The Americana style is the first digital map to achieve concurrent, state-specif
## How to use
-You can install the OpenStreetMap Americana package and [deploy it anywhere](CONTRIBUTING.md#Production%20builds) as a static webpage. For your convenience, we’ve deployed it [on GitHub Pages](https://zelonewolf.github.io/openstreetmap-americana/). To explore how the style depicts various features, click the Samples button.
+You can install the OpenStreetMap Americana package and [deploy it anywhere](CONTRIBUTING.md#Production%20builds) as a static webpage. For your convenience, we’ve deployed it [on GitHub Pages](https://zelonewolf.github.io/openstreetmap-americana/). Click the Legend button to learn the meaning of each symbol, line, and color based on the features currently visible on the map.
The style tries to label places in [your browser’s preferred language](https://www.w3.org/International/questions/qa-lang-priorities). To change this preference, consult your browser’s documentation: [Chrome](https://support.google.com/chrome/answer/173424), [Firefox](https://support.mozilla.org/en-US/kb/use-firefox-another-language), [Safari for macOS](https://support.apple.com/guide/mac-help/change-the-system-language-mh26684/mac), [Safari for iOS](https://support.apple.com/en-us/HT204031). You can also override this preference by adding `&language=` to the URL, followed by a comma-separated list of [IETF language tags](https://www.w3.org/International/articles/language-tags/). For example, here’s a map labeled [in Portuguese, falling back to Spanish](https://zelonewolf.github.io/openstreetmap-americana/#language=pt,es). If we don’t have the name of a place in any of your preferred languages, the style shows the name in the local language as a last resort.
@@ -55,6 +55,8 @@ The OpenStreetMap Americana style is built upon the [OpenMapTiles schema](https:
- Translated name labels from [Wikidata](https://www.wikidata.org/wiki/Wikidata:Main_Page) for places, POIs, airports, roads, bodies of water, parks, and mountain peaks.
- Low-zoom ocean/water, boundary, and urbanized area data from [Natural Earth](https://www.naturalearthdata.com/).
+The legend’s “Route markers” section is labeled using labels of Wikidata items that are tagged with the [corresponding OSM tag](https://www.wikidata.org/wiki/Property:P1282).
+
## Coverage
Americana is compatible with vector tiles covering the entire world.
diff --git a/src/americana.js b/src/americana.js
index e8bb49180..797eb53c2 100644
--- a/src/americana.js
+++ b/src/americana.js
@@ -4,7 +4,6 @@ import config from "./config.js";
import * as Label from "./constants/label.js";
-import * as Util from "./js/util.js";
import * as Shield from "./js/shield.js";
import * as ShieldDef from "./js/shield_defs.js";
@@ -31,6 +30,8 @@ import * as maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import * as search from "./search.js";
+import LegendControl from "./js/legend_control.js";
+import * as LegendConfig from "./js/legend_config.js";
import SampleControl from "openmapsamples-maplibre/OpenMapSamplesControl.js";
import { default as OpenMapTilesSamples } from "openmapsamples/samples/OpenMapTiles/index.js";
@@ -205,28 +206,7 @@ function buildLayers() {
lyrOneway.bridgeLink,
];
- //Render bridge without layer on the lowest bridge layer
- bridgeLayers.forEach((layer) =>
- layers.push(
- Util.filteredClone(layer, ["!", ["has", "layer"]], "_layer_bottom")
- )
- );
-
- //One layer at a time to handle stacked bridges
- for (let i = 1; i <= 4; i++) {
- bridgeLayers.forEach((layer) => layers.push(Util.restrictLayer(layer, i)));
- }
-
- //If layer is more than 5, just give up and render on a single layer.
- bridgeLayers.forEach((layer) =>
- layers.push(
- Util.filteredClone(
- layer,
- [">=", ["coalesce", ["get", "layer"], 0], 5],
- "_layer_top"
- )
- )
- );
+ layers.push(...lyrRail.getLayerSeparatedBridgeLayers(bridgeLayers));
layers.push(
//The labels at the end of the list draw on top of the layers at the beginning.
@@ -326,14 +306,17 @@ map.on("styleimagemissing", function (e) {
Shield.missingIconHandler(map, e);
});
-export function hotReloadMap() {
+function hotReloadMap() {
map.setStyle(buildStyle());
}
export function updateLanguageLabel() {
languageLabel.displayLocales(Label.getLocales());
+ legendControl.onLanguageChange();
}
+let legendControl = new LegendControl();
+legendControl.sections = LegendConfig.sections;
window.addEventListener("languagechange", (event) => {
console.log(`Changed to ${navigator.languages}`);
hotReloadMap();
@@ -372,6 +355,8 @@ if (config.ATTRIBUTION_LOGO != undefined) {
map.addControl(new search.PhotonSearchControl(), "top-left");
map.addControl(new maplibregl.NavigationControl(), "top-left");
+map.addControl(legendControl, "bottom-left");
+
// Add our sample data.
let sampleControl = new SampleControl({ permalinks: true });
OpenMapTilesSamples.forEach((sample, i) => {
diff --git a/src/index.html b/src/index.html
index 5a8c34873..4c8d2c26c 100644
--- a/src/index.html
+++ b/src/index.html
@@ -30,6 +30,58 @@
.openmapsamples-control-container {
margin-left: 70px !important;
}
+
+ #legend-container {
+ max-height: 60vh;
+ overflow-y: scroll;
+ }
+ .legend-section > summary {
+ background-color: #eee;
+ }
+ .legend-row,
+ .legend-row img {
+ vertical-align: middle;
+ }
+ .legend-row a {
+ color: black;
+ }
+ .legend-row > .label,
+ .legend-row > .icon {
+ width: 0;
+ text-align: center;
+ }
+ .legend-row > .icon {
+ white-space: nowrap;
+ }
+ .legend-row .shield + .shield {
+ margin-left: 0.2em;
+ }
+ .legend-row > .swatch,
+ .legend-row > .line {
+ vertical-align: middle;
+ width: 4em;
+ }
+ .legend-row > .swatch {
+ border-style: solid;
+ }
+ .legend-row > .line > svg {
+ vertical-align: middle;
+ width: 100%;
+ }
+ .legend-row .language {
+ font-style: italic;
+ font-size: 80%;
+ line-height: 1;
+ vertical-align: super;
+ }
+ .legend-source {
+ font-size: 80%;
+ text-align: right;
+ }
+ .legend-source,
+ .legend-source a {
+ color: #aaa;
+ }
@@ -53,5 +105,48 @@
data-recalc-dims="1"
style="position: absolute; top: 0; right: 0; border: 0; z-index: 100"
/>
+
+