From cbe6f7364ee5a4cd2b561ab4653deaaa51e94195 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Thu, 7 Jul 2022 21:11:56 -0400 Subject: [PATCH 001/514] Add busways --- src/americana.js | 8 +++++++ src/layer/oneway.js | 9 +++---- src/layer/road.js | 53 +++++++++++++++++++++++++++++++++++++++++ src/layer/road_label.js | 11 +++++++++ 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/americana.js b/src/americana.js index 1a51f84e6..f612b7d2b 100644 --- a/src/americana.js +++ b/src/americana.js @@ -86,6 +86,7 @@ americanaLayers.push( lyrRoad.secondaryTunnel.casing(), lyrRoad.tertiaryExpresswayTunnel.casing(), lyrRoad.tertiaryTunnel.casing(), + lyrRoad.buswayTunnel.casing(), lyrRoad.minorTunnel.casing(), lyrRoad.serviceTunnel.casing(), lyrRoad.smallServiceTunnel.casing(), @@ -124,6 +125,7 @@ americanaLayers.push( lyrRoad.secondaryTunnel.fill(), lyrRoad.tertiaryExpresswayTunnel.fill(), lyrRoad.tertiaryTunnel.fill(), + lyrRoad.buswayTunnel.fill(), lyrRoad.minorTunnel.fill(), lyrRoad.serviceTunnel.fill(), lyrRoad.smallServiceTunnel.fill(), @@ -170,6 +172,7 @@ americanaLayers.push( lyrRoad.secondary.casing(), lyrRoad.tertiaryExpressway.casing(), lyrRoad.tertiary.casing(), + lyrRoad.busway.casing(), lyrRoad.minor.casing(), lyrRoad.service.casing(), lyrRoad.smallService.casing(), @@ -201,6 +204,7 @@ americanaLayers.push( lyrRoad.smallService.fill(), lyrRoad.service.fill(), + lyrRoad.busway.fill(), lyrRoad.minor.fill(), lyrRoad.tertiary.fill(), lyrRoad.tertiaryExpressway.fill(), @@ -240,6 +244,7 @@ americanaLayers.push( lyrRoad.smallService.surface(), lyrRoad.service.surface(), + lyrRoad.busway.surface(), lyrRoad.minor.surface(), lyrRoad.tertiary.surface(), lyrRoad.tertiaryExpressway.surface(), @@ -283,6 +288,7 @@ var bridgeLayers = [ lyrRoad.smallServiceBridge.casing(), lyrRoad.serviceBridge.casing(), lyrRoad.minorBridge.casing(), + lyrRoad.buswayBridge.casing(), lyrRoad.tertiaryBridge.casing(), lyrRoad.tertiaryExpresswayBridge.casing(), lyrRoad.secondaryBridge.casing(), @@ -321,6 +327,7 @@ var bridgeLayers = [ lyrRoad.smallServiceBridge.fill(), lyrRoad.serviceBridge.fill(), lyrRoad.minorBridge.fill(), + lyrRoad.buswayBridge.fill(), lyrRoad.tertiaryBridge.fill(), lyrRoad.tertiaryExpresswayBridge.fill(), lyrRoad.secondaryBridge.fill(), @@ -404,6 +411,7 @@ americanaLayers.push( lyrRoadLabel.primary, lyrRoadLabel.secondary, lyrRoadLabel.tertiary, + lyrRoadLabel.busway, lyrRoadLabel.minor, lyrRoadLabel.service, lyrRoadLabel.smallService, diff --git a/src/layer/oneway.js b/src/layer/oneway.js index ed1b598f0..fbe2b30cc 100644 --- a/src/layer/oneway.js +++ b/src/layer/oneway.js @@ -19,6 +19,7 @@ export const road = { // "primary", // "secondary", // "tertiary", + // "busway", // "minor", // "service", ], @@ -61,6 +62,7 @@ export const tunnel = { // "primary", // "secondary", // "tertiary", + // "busway", // "minor", // "service", ], @@ -104,6 +106,7 @@ export const bridge = { // "primary", // "secondary", // "tertiary", + // "busway", // "minor", // "service", ], @@ -146,8 +149,6 @@ export const link = { // "primary", // "secondary", // "tertiary", - // "minor", - // "service", ], ], layout: { @@ -188,8 +189,6 @@ export const tunnelLink = { // "primary", // "secondary", // "tertiary", - // "minor", - // "service", ], ], layout: { @@ -231,8 +230,6 @@ export const bridgeLink = { // "primary", // "secondary", // "tertiary", - // "minor", - // "service", ], ], layout: { diff --git a/src/layer/road.js b/src/layer/road.js index 27cb85e04..2f080e097 100644 --- a/src/layer/road.js +++ b/src/layer/road.js @@ -697,6 +697,39 @@ class TertiaryExpresswayToll extends TertiaryToll { } } +class Busway extends Road { + constructor() { + super(); + this.highwayClass = "busway"; + this.brunnel = "surface"; + this.link = false; + this.toll = false; + this.hue = 270; + + this.minZoomFill = 11; + this.minZoomCasing = 11; + + this.fillWidth = Util.zoomMultiply(trunkFillWidth, 0.5); + this.casingWidth = Util.zoomMultiply(trunkCasingWidth, 0.5); + + this.fillColor = [ + "interpolate", + ["exponential", roadExp], + ["zoom"], + this.minZoomFill, + `hsl(${this.hue}, 60%, 75%)`, + this.minZoomFill + 2, + `hsl(${this.hue}, 60%, 40%)`, + 14.9999, + `hsl(${this.hue}, 30%, 40%)`, + 15, + `hsl(${this.hue}, 30%, 75%)`, + ]; + this.casingColor = roadCasingColor(this.hue, this.minZoomCasing); + this.surfaceColor = `hsl(${this.hue}, 0%, 80%)`; + } +} + class Minor extends Road { constructor() { super(); @@ -1167,6 +1200,14 @@ class TertiaryExpresswayTollBridge extends TertiaryExpresswayToll { } } +class BuswayBridge extends Busway { + constructor() { + //undifferentiated + super(); + this.brunnel = "bridge"; + } +} + class MinorBridge extends Minor { constructor() { //undifferentiated @@ -1457,6 +1498,15 @@ class TertiaryExpresswayTollTunnel extends TertiaryExpresswayToll { } } +class BuswayTunnel extends Busway { + constructor() { + super(); + this.brunnel = "tunnel"; + this.casingColor = `hsl(${this.hue}, 0%, 80%)`; + this.fillColor = `hsl(${this.hue}, 30%, 95%)`; + } +} + class MinorTunnel extends Minor { constructor() { super(); @@ -1620,6 +1670,7 @@ export const tertiary = new Tertiary(); export const tertiaryToll = new TertiaryToll(); export const tertiaryExpressway = new TertiaryExpressway(); export const tertiaryExpresswayToll = new TertiaryExpresswayToll(); +export const busway = new Busway(); export const minor = new Minor(); export const minorToll = new MinorToll(); export const service = new Service(); @@ -1646,6 +1697,7 @@ export const tertiaryBridge = new TertiaryBridge(); export const tertiaryTollBridge = new TertiaryTollBridge(); export const tertiaryExpresswayBridge = new TertiaryExpresswayBridge(); export const tertiaryExpresswayTollBridge = new TertiaryExpresswayTollBridge(); +export const buswayBridge = new BuswayBridge(); export const minorBridge = new MinorBridge(); export const minorTollBridge = new MinorTollBridge(); export const serviceBridge = new ServiceBridge(); @@ -1672,6 +1724,7 @@ export const tertiaryTunnel = new TertiaryTunnel(); export const tertiaryTollTunnel = new TertiaryTollTunnel(); export const tertiaryExpresswayTunnel = new TertiaryExpresswayTunnel(); export const tertiaryExpresswayTollTunnel = new TertiaryExpresswayTollTunnel(); +export const buswayTunnel = new BuswayTunnel(); export const minorTunnel = new MinorTunnel(); export const minorTollTunnel = new MinorTollTunnel(); export const serviceTunnel = new ServiceTunnel(); diff --git a/src/layer/road_label.js b/src/layer/road_label.js index 078a3d02a..332d88ebe 100644 --- a/src/layer/road_label.js +++ b/src/layer/road_label.js @@ -91,6 +91,17 @@ export const tertiary = { "source-layer": "transportation_name", }; +export const busway = { + id: "busway_label", + type: "symbol", + paint: textPaint, + filter: ["all", ["==", "class", "busway"]], + minzoom: 13, + layout: Object.assign(zoomDependentLayout(17), textLayout), + source: "openmaptiles", + "source-layer": "transportation_name", +}; + export const minor = { id: "minor_label", type: "symbol", From 56a67a69754dd45b71eaaa5b4711a6bc13ec917e Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Fri, 8 Jul 2022 09:56:21 -0400 Subject: [PATCH 002/514] Change busways to dull gray --- src/layer/road.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/layer/road.js b/src/layer/road.js index 2f080e097..c43bcb989 100644 --- a/src/layer/road.js +++ b/src/layer/road.js @@ -704,10 +704,10 @@ class Busway extends Road { this.brunnel = "surface"; this.link = false; this.toll = false; - this.hue = 270; + this.hue = 0; - this.minZoomFill = 11; - this.minZoomCasing = 11; + this.minZoomFill = 12; + this.minZoomCasing = 12; this.fillWidth = Util.zoomMultiply(trunkFillWidth, 0.5); this.casingWidth = Util.zoomMultiply(trunkCasingWidth, 0.5); @@ -717,13 +717,13 @@ class Busway extends Road { ["exponential", roadExp], ["zoom"], this.minZoomFill, - `hsl(${this.hue}, 60%, 75%)`, + `hsl(${this.hue}, 0%, 85%)`, this.minZoomFill + 2, - `hsl(${this.hue}, 60%, 40%)`, + `hsl(${this.hue}, 0%, 75%)`, 14.9999, - `hsl(${this.hue}, 30%, 40%)`, + `hsl(${this.hue}, 0%, 75%)`, 15, - `hsl(${this.hue}, 30%, 75%)`, + `hsl(${this.hue}, 0%, 80%)`, ]; this.casingColor = roadCasingColor(this.hue, this.minZoomCasing); this.surfaceColor = `hsl(${this.hue}, 0%, 80%)`; @@ -1503,7 +1503,7 @@ class BuswayTunnel extends Busway { super(); this.brunnel = "tunnel"; this.casingColor = `hsl(${this.hue}, 0%, 80%)`; - this.fillColor = `hsl(${this.hue}, 30%, 95%)`; + this.fillColor = `hsl(${this.hue}, 0%, 95%)`; } } From 9d9455e2819e5d8e9061fb5a53e831ef9882dddc Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 12 Jul 2022 12:31:28 -0400 Subject: [PATCH 003/514] Adopt the OpenStreetMap US Code of Conduct This proposes to adopt the OSM US Code of Conduct. This PR is initially draft so that we can work out the language. Adoption procedure: * [ ] All maintainers concur with adopting this CoC * [ ] OSM US CoC Committee concurs with the text of this file * [ ] OSM US concurs with a release of copyright from portions excerpted from the OSM US CoC --- CODE_OF_CONDUCT.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..d54e90811 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,57 @@ +# Code of Conduct + +The OpenStreetMap Americana project has opted in (PR link) to the [OpenStreetMap US Code of Conduct](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct) (CoC) and [Process for Moderation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/ModerationProcess). + +This document outlines the standards for behavior in this project, and describes the procedure that users may follow if they experience unacceptable behavior. + +## Encouraged Behaviors +* **Be welcoming**. OpenStreetMap US strives to be a community that welcomes and supports people of all backgrounds and identities. Some examples of behavior that contribute to creating a positive environment include: + * The use of welcoming and inclusive language; + * Respect for differing viewpoints and experiences; + * Empathy towards other community members. +* **Be considerate and patient**. Actively seek to acknowledge, respect, and understand fellow community members. Our community depends on the work of each other to maintain and strengthen the health and integrity of OpenStreetMap. Any decision you make may affect others, and those consequences should be taken into account. While critique is a natural and important part of our culture, good critiques are kind, respectful, clear, and constructive. +* **Assume good faith**. It is surprisingly easy to misunderstand each other, be it online or in person; particularly in such a culturally and linguistically diverse setting as OpenStreetMap US. Misunderstandings can easily arise when we are in a rush, or otherwise distracted. Please ask clarifying questions before assuming that a communication was inappropriate. +* **Be respectful**. Enthusiastic discussions are part of the lifeblood of a successful project but can also lead to disagreements. We should strive to keep our discussions and disagreements appropriate. Members of the OpenStreetMap US community should be respectful when dealing with others, within and outside of the global OpenStreetMap community. We note, however, that non-OpenStreetMap US spaces are not part of this Code of Conduct. +* **When we disagree, try to understand why**. Disagreements, both social and technical, happen easily and often. It is important that we seek to understand each other and work to resolve disagreements and differing views constructively. When someone contradicts your own perceptions, try to understand where the other person is coming from. Try to ask questions that will serve to clarify, rather than to escalate, an issue. + +## Untolerated Behaviors + +Examples of untolerated behaviors include, but are not limited to: + +* Violent threats or language directed against another person, including deliberate intimidation or harassment (online or in-person); +* Verbal, written, or physical abuse; +* Discrimination of any person or group of persons; +* Discriminatory jokes and language; +* Conduct or speech which might be considered sexist, racist, homophobic, transphobic, xenophobic, ableist or otherwise discriminatory or offensive in nature; +* The use of unwelcome, suggestive, derogatory or inappropriate nicknames or terms; +* Disrespect towards others (ex. personal insults, innuendo); +* Posting sexually explicit or violent material, or including this content in events such as conference presentations, talks, workshops, or parties; +* Posting (or threatening to post) other people's personally identifying information ("doxing"); +* Inappropriate attention or contact. Be aware of how your actions affect others. If it makes someone uncomfortable, stop. This includes: + * Continued unwelcomed one-on-one communication after a request to cease + * Unwelcome sexual attention + * Repeated harassment of others. In general, if someone asks you to stop, then stop. +* Sustained disruptions of community events and discussions; +* Advocating for, or encouraging, any of the above behavior + +## Process for Moderation + +We encourage users to [work out issues directly](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct#How_to_Handle_a_Complaint) with the people involved in lieu of a formal report. +If you believe someone has violated the CoC, we ask that you report it to OpenStreetMap US Code of Conduct Committee via the process outlined in the [Process for Moderation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/ModerationProcess). The CoC Committee will adjudicate reports submitted via this process, however, they will not actively monitor project communication channels. + +## Maintainer Responsibilities + +* The maintainer team agrees to abide by the CoC and to implement and be bound by moderation decisions made by the CoC Committee. This includes making a best effort to resolve disputes +* Maintainers agree not to act in CoC cases in which a maintainer has a conflict of interest, such as in the case of a complaint made against the maintainer. + +## Applicability + +* This CoC applies to the OpenStreetMap Americana github repository. however, the CoC committee may consider a holistic examination of communications made via other channels when evaluating a complaint. +* This CoC remains in force until or unless revoked by the maintainer team, the OSM US CoC Committee, or OSM US. Should the CoC be revoked, it remains applicable for all behavior which occurred prior to revocation. +* The list of encouraged and untolerated behaviors are adopted as written at the time of the approval of this CoC. Should the standards of behavior in the OSM US CoC change, the maintainer team reserves the right to adopt the change, withdraw from the Process for Moderation, or neither. +* The OSM US CoC Committee may at any time change the procedures outlined in the Process for Moderation and/or the Statement of Scope and Limitations as documented in the Committee's [online documentation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct). The project adopts these procedures as currently written, even if they differ from the procedures that were in force at the time of the project's adoption of the CoC. +* If the locations of the official online documentation of the OSM US CoC and Process for Moderation change, the presently applicable documentation applies, and updates to these links may be made as an administrative change. + +## License + +Per a *(Date)* decision of the OpenStreetMap US Board of Directors, OpenStreetMap US releases the text in this document from any claim of copyright, and grants permission for its inclusion in public domain-compatible works, with or without the inclusion of this statement. From 7a88bcade72a1aa5aff63fd6d02ee157699d7881 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 12 Jul 2022 12:36:33 -0400 Subject: [PATCH 004/514] Update CODE_OF_CONDUCT.md Update PR link --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index d54e90811..a251e348b 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,6 +1,6 @@ # Code of Conduct -The OpenStreetMap Americana project has opted in (PR link) to the [OpenStreetMap US Code of Conduct](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct) (CoC) and [Process for Moderation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/ModerationProcess). +The OpenStreetMap Americana project has opted in (#489) to the [OpenStreetMap US Code of Conduct](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct) (CoC) and [Process for Moderation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/ModerationProcess). This document outlines the standards for behavior in this project, and describes the procedure that users may follow if they experience unacceptable behavior. From d5cd3c68538ae7109497f26a829199a0c9930348 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 12 Jul 2022 12:37:14 -0400 Subject: [PATCH 005/514] Update CODE_OF_CONDUCT.md Fix PR Link again --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a251e348b..ad2716d32 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,6 +1,6 @@ # Code of Conduct -The OpenStreetMap Americana project has opted in (#489) to the [OpenStreetMap US Code of Conduct](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct) (CoC) and [Process for Moderation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/ModerationProcess). +The OpenStreetMap Americana project has [opted in](https://github.com/ZeLonewolf/openstreetmap-americana/pull/489) to the [OpenStreetMap US Code of Conduct](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct) (CoC) and [Process for Moderation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/ModerationProcess). This document outlines the standards for behavior in this project, and describes the procedure that users may follow if they experience unacceptable behavior. From c3548faee2753580ab46416cfb1d7f6ff007eddb Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 12 Jul 2022 14:56:00 -0400 Subject: [PATCH 006/514] Prettier --- CODE_OF_CONDUCT.md | 63 +++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index ad2716d32..d6749f31a 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -5,34 +5,35 @@ The OpenStreetMap Americana project has [opted in](https://github.com/ZeLonewolf This document outlines the standards for behavior in this project, and describes the procedure that users may follow if they experience unacceptable behavior. ## Encouraged Behaviors -* **Be welcoming**. OpenStreetMap US strives to be a community that welcomes and supports people of all backgrounds and identities. Some examples of behavior that contribute to creating a positive environment include: - * The use of welcoming and inclusive language; - * Respect for differing viewpoints and experiences; - * Empathy towards other community members. -* **Be considerate and patient**. Actively seek to acknowledge, respect, and understand fellow community members. Our community depends on the work of each other to maintain and strengthen the health and integrity of OpenStreetMap. Any decision you make may affect others, and those consequences should be taken into account. While critique is a natural and important part of our culture, good critiques are kind, respectful, clear, and constructive. -* **Assume good faith**. It is surprisingly easy to misunderstand each other, be it online or in person; particularly in such a culturally and linguistically diverse setting as OpenStreetMap US. Misunderstandings can easily arise when we are in a rush, or otherwise distracted. Please ask clarifying questions before assuming that a communication was inappropriate. -* **Be respectful**. Enthusiastic discussions are part of the lifeblood of a successful project but can also lead to disagreements. We should strive to keep our discussions and disagreements appropriate. Members of the OpenStreetMap US community should be respectful when dealing with others, within and outside of the global OpenStreetMap community. We note, however, that non-OpenStreetMap US spaces are not part of this Code of Conduct. -* **When we disagree, try to understand why**. Disagreements, both social and technical, happen easily and often. It is important that we seek to understand each other and work to resolve disagreements and differing views constructively. When someone contradicts your own perceptions, try to understand where the other person is coming from. Try to ask questions that will serve to clarify, rather than to escalate, an issue. + +- **Be welcoming**. OpenStreetMap US strives to be a community that welcomes and supports people of all backgrounds and identities. Some examples of behavior that contribute to creating a positive environment include: + - The use of welcoming and inclusive language; + - Respect for differing viewpoints and experiences; + - Empathy towards other community members. +- **Be considerate and patient**. Actively seek to acknowledge, respect, and understand fellow community members. Our community depends on the work of each other to maintain and strengthen the health and integrity of OpenStreetMap. Any decision you make may affect others, and those consequences should be taken into account. While critique is a natural and important part of our culture, good critiques are kind, respectful, clear, and constructive. +- **Assume good faith**. It is surprisingly easy to misunderstand each other, be it online or in person; particularly in such a culturally and linguistically diverse setting as OpenStreetMap US. Misunderstandings can easily arise when we are in a rush, or otherwise distracted. Please ask clarifying questions before assuming that a communication was inappropriate. +- **Be respectful**. Enthusiastic discussions are part of the lifeblood of a successful project but can also lead to disagreements. We should strive to keep our discussions and disagreements appropriate. Members of the OpenStreetMap US community should be respectful when dealing with others, within and outside of the global OpenStreetMap community. We note, however, that non-OpenStreetMap US spaces are not part of this Code of Conduct. +- **When we disagree, try to understand why**. Disagreements, both social and technical, happen easily and often. It is important that we seek to understand each other and work to resolve disagreements and differing views constructively. When someone contradicts your own perceptions, try to understand where the other person is coming from. Try to ask questions that will serve to clarify, rather than to escalate, an issue. ## Untolerated Behaviors Examples of untolerated behaviors include, but are not limited to: -* Violent threats or language directed against another person, including deliberate intimidation or harassment (online or in-person); -* Verbal, written, or physical abuse; -* Discrimination of any person or group of persons; -* Discriminatory jokes and language; -* Conduct or speech which might be considered sexist, racist, homophobic, transphobic, xenophobic, ableist or otherwise discriminatory or offensive in nature; -* The use of unwelcome, suggestive, derogatory or inappropriate nicknames or terms; -* Disrespect towards others (ex. personal insults, innuendo); -* Posting sexually explicit or violent material, or including this content in events such as conference presentations, talks, workshops, or parties; -* Posting (or threatening to post) other people's personally identifying information ("doxing"); -* Inappropriate attention or contact. Be aware of how your actions affect others. If it makes someone uncomfortable, stop. This includes: - * Continued unwelcomed one-on-one communication after a request to cease - * Unwelcome sexual attention - * Repeated harassment of others. In general, if someone asks you to stop, then stop. -* Sustained disruptions of community events and discussions; -* Advocating for, or encouraging, any of the above behavior +- Violent threats or language directed against another person, including deliberate intimidation or harassment (online or in-person); +- Verbal, written, or physical abuse; +- Discrimination of any person or group of persons; +- Discriminatory jokes and language; +- Conduct or speech which might be considered sexist, racist, homophobic, transphobic, xenophobic, ableist or otherwise discriminatory or offensive in nature; +- The use of unwelcome, suggestive, derogatory or inappropriate nicknames or terms; +- Disrespect towards others (ex. personal insults, innuendo); +- Posting sexually explicit or violent material, or including this content in events such as conference presentations, talks, workshops, or parties; +- Posting (or threatening to post) other people's personally identifying information ("doxing"); +- Inappropriate attention or contact. Be aware of how your actions affect others. If it makes someone uncomfortable, stop. This includes: + - Continued unwelcomed one-on-one communication after a request to cease + - Unwelcome sexual attention + - Repeated harassment of others. In general, if someone asks you to stop, then stop. +- Sustained disruptions of community events and discussions; +- Advocating for, or encouraging, any of the above behavior ## Process for Moderation @@ -41,17 +42,17 @@ If you believe someone has violated the CoC, we ask that you report it to OpenSt ## Maintainer Responsibilities -* The maintainer team agrees to abide by the CoC and to implement and be bound by moderation decisions made by the CoC Committee. This includes making a best effort to resolve disputes -* Maintainers agree not to act in CoC cases in which a maintainer has a conflict of interest, such as in the case of a complaint made against the maintainer. +- The maintainer team agrees to abide by the CoC and to implement and be bound by moderation decisions made by the CoC Committee. This includes making a best effort to resolve disputes +- Maintainers agree not to act in CoC cases in which a maintainer has a conflict of interest, such as in the case of a complaint made against the maintainer. ## Applicability -* This CoC applies to the OpenStreetMap Americana github repository. however, the CoC committee may consider a holistic examination of communications made via other channels when evaluating a complaint. -* This CoC remains in force until or unless revoked by the maintainer team, the OSM US CoC Committee, or OSM US. Should the CoC be revoked, it remains applicable for all behavior which occurred prior to revocation. -* The list of encouraged and untolerated behaviors are adopted as written at the time of the approval of this CoC. Should the standards of behavior in the OSM US CoC change, the maintainer team reserves the right to adopt the change, withdraw from the Process for Moderation, or neither. -* The OSM US CoC Committee may at any time change the procedures outlined in the Process for Moderation and/or the Statement of Scope and Limitations as documented in the Committee's [online documentation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct). The project adopts these procedures as currently written, even if they differ from the procedures that were in force at the time of the project's adoption of the CoC. -* If the locations of the official online documentation of the OSM US CoC and Process for Moderation change, the presently applicable documentation applies, and updates to these links may be made as an administrative change. +- This CoC applies to the OpenStreetMap Americana github repository. however, the CoC committee may consider a holistic examination of communications made via other channels when evaluating a complaint. +- This CoC remains in force until or unless revoked by the maintainer team, the OSM US CoC Committee, or OSM US. Should the CoC be revoked, it remains applicable for all behavior which occurred prior to revocation. +- The list of encouraged and untolerated behaviors are adopted as written at the time of the approval of this CoC. Should the standards of behavior in the OSM US CoC change, the maintainer team reserves the right to adopt the change, withdraw from the Process for Moderation, or neither. +- The OSM US CoC Committee may at any time change the procedures outlined in the Process for Moderation and/or the Statement of Scope and Limitations as documented in the Committee's [online documentation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct). The project adopts these procedures as currently written, even if they differ from the procedures that were in force at the time of the project's adoption of the CoC. +- If the locations of the official online documentation of the OSM US CoC and Process for Moderation change, the presently applicable documentation applies, and updates to these links may be made as an administrative change. ## License -Per a *(Date)* decision of the OpenStreetMap US Board of Directors, OpenStreetMap US releases the text in this document from any claim of copyright, and grants permission for its inclusion in public domain-compatible works, with or without the inclusion of this statement. +Per a _(Date)_ decision of the OpenStreetMap US Board of Directors, OpenStreetMap US releases the text in this document from any claim of copyright, and grants permission for its inclusion in public domain-compatible works, with or without the inclusion of this statement. From ab0caa56f414bd916ba28dfc64cc7e97f9fce153 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 13 Jul 2022 11:21:12 -0400 Subject: [PATCH 007/514] Update README.md Add CC0 + exception notice --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 6760ae997..df730233a 100644 --- a/README.md +++ b/README.md @@ -57,3 +57,8 @@ and for routes in the following countries: Countries We are hoping that it will support more areas, you can [help us](CONTRIBUTING.md)! + +## License + +CC0 +Except where otherwise noted, to the extend possible under law, the contributors to OpenStreetMap Americana have waived all copyright and related or neighboring rights to OpenStreetMap Americana. From 9385a158b9a279c4181d24f92ebd7a7a025ed63a Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 13 Jul 2022 11:24:41 -0400 Subject: [PATCH 008/514] Update README.md Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df730233a..3f6783186 100644 --- a/README.md +++ b/README.md @@ -61,4 +61,4 @@ We are hoping that it will support more areas, you can [help us](CONTRIBUTING.md ## License CC0 -Except where otherwise noted, to the extend possible under law, the contributors to OpenStreetMap Americana have waived all copyright and related or neighboring rights to OpenStreetMap Americana. +Except where otherwise noted, to the extent possible under law, the contributors to OpenStreetMap Americana have waived all copyright and related or neighboring rights to OpenStreetMap Americana. From bd74a8127e44952f1a476089d89eb745e56b5c53 Mon Sep 17 00:00:00 2001 From: Adam Franco Date: Wed, 13 Jul 2022 11:37:44 -0400 Subject: [PATCH 009/514] Update License block to note CC-BY-SA of source. Linking to the OpenStreetMap-US Code of Conduct and including its sources as well. --- CODE_OF_CONDUCT.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index d6749f31a..d060edbbe 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -55,4 +55,6 @@ If you believe someone has violated the CoC, we ask that you report it to OpenSt ## License -Per a _(Date)_ decision of the OpenStreetMap US Board of Directors, OpenStreetMap US releases the text in this document from any claim of copyright, and grants permission for its inclusion in public domain-compatible works, with or without the inclusion of this statement. +This code of conduct file (`CODE_OF_CONDUCT.md`) includes excerpts from the [OpenStreetMap-US Code of Conduct](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct) and is provided under the [Creative Commons Attribution-ShareAlike 3.0](http://creativecommons.org/licenses/by-sa/3.0/) license. + +Credits for the sources and inspiration of this code of conduct go to the [Speak Up! Project](http://web.archive.org/web/20141109123859/http://speakup.io/coc.html), [HOT Code of Conduct](https://www.hotosm.org/code-of-conduct.html), [Django Code of Conduct](https://www.djangoproject.com/conduct/reporting/), [Geek Feminism Code of Conduct](https://geekfeminismdotorg.wordpress.com/about/code-of-conduct/), [Slack Code of Conduct](https://api.slack.com/docs/community-code-of-conduct) and [The Ada Initiative](http://adainitiative.org/2014/02/18/howto-design-a-code-of-conduct-for-your-community/) under a [Creative Commons Attribution-ShareAlike](http://creativecommons.org/licenses/by-sa/3.0/) license. From db6cc3bbf96f6f8cdc1f7c782c33250ea5d4237c Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 13 Jul 2022 11:46:46 -0400 Subject: [PATCH 010/514] Update CODE_OF_CONDUCT.md Fix minor typos --- CODE_OF_CONDUCT.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index d060edbbe..d6429ddfa 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -42,8 +42,8 @@ If you believe someone has violated the CoC, we ask that you report it to OpenSt ## Maintainer Responsibilities -- The maintainer team agrees to abide by the CoC and to implement and be bound by moderation decisions made by the CoC Committee. This includes making a best effort to resolve disputes -- Maintainers agree not to act in CoC cases in which a maintainer has a conflict of interest, such as in the case of a complaint made against the maintainer. +- The maintainer team agrees to abide by the CoC and to implement and be bound by moderation decisions made by the CoC Committee. This includes making a best effort to resolve disputes. +- Maintainers agree not to act in CoC cases in which the maintainer has a conflict of interest, such as in the case of a complaint made against the maintainer. ## Applicability From d988595a4e005f4a1334b16973854347a5378761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sun, 9 Oct 2022 02:28:02 -0700 Subject: [PATCH 011/514] Added Kosovo motorway shields --- doc-img/shield_map_world.svg | 2 +- src/js/shield_defs.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc-img/shield_map_world.svg b/doc-img/shield_map_world.svg index 247cabc95..179aefef7 100644 --- a/doc-img/shield_map_world.svg +++ b/doc-img/shield_map_world.svg @@ -151,6 +151,7 @@ See the end of this file for a list of available jurisdictions and their codes. .hu, .is, .it, +.xk, .lt, .lu, .lv, @@ -167,7 +168,6 @@ See the end of this file for a list of available jurisdictions and their codes. .si, .sk, .ua, -.xk, .au, .nz, .cw, diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index ddd42e870..4c12c655c 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -3149,6 +3149,9 @@ export function loadShields(shieldImages) { // Italy shields["IT:A-road"] = octagonShieldGreen; + // Kosovo + shields["XK:motorway"] = hexagonVerticalShieldGreen; + // Lithuania shields["lt:national"] = roundedRectShield( Color.shields.red, From f78900d8b56980aff3651a688eb3ffecca9c4dae Mon Sep 17 00:00:00 2001 From: Jeroen van der Gun Date: Mon, 10 Oct 2022 23:01:09 +0200 Subject: [PATCH 012/514] Add rendering for capital=3, same as capital=4 --- scripts/taginfo_template.json | 8 ++++++++ src/layer/place.js | 2 ++ 2 files changed, 10 insertions(+) diff --git a/scripts/taginfo_template.json b/scripts/taginfo_template.json index 177abfd69..7a6e6d035 100644 --- a/scripts/taginfo_template.json +++ b/scripts/taginfo_template.json @@ -18,6 +18,14 @@ "doc_url": "https://openmaptiles.org/schema/#capital", "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/star_nation_capital.svg" }, + { + "key": "capital", + "value": "3", + "object_types": ["node"], + "description": "Marks the city with a state capital icon.", + "doc_url": "https://openmaptiles.org/schema/#capital", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/star_state_capital.svg" + }, { "key": "capital", "value": "4", diff --git a/src/layer/place.js b/src/layer/place.js index 57bb566ab..4420c1019 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -12,6 +12,8 @@ const cityIcon = [ ["get", "capital"], 2, "star_nation_capital", + 3, + "star_state_capital", 4, "star_state_capital", "dot_city", From 405ce0dab4f280a0101dec4223ea8d5b553daaad Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Mon, 10 Oct 2022 19:49:00 -0400 Subject: [PATCH 013/514] Add Blue Ridge Parkway shield --- icons/shield_us_nps_brp.svg | 4 ++++ src/js/shield_defs.js | 5 +++++ 2 files changed, 9 insertions(+) create mode 100644 icons/shield_us_nps_brp.svg diff --git a/icons/shield_us_nps_brp.svg b/icons/shield_us_nps_brp.svg new file mode 100644 index 000000000..e1e50cbc7 --- /dev/null +++ b/icons/shield_us_nps_brp.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 4c12c655c..de197a918 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -888,6 +888,11 @@ export function loadShields(shieldImages) { }, }; + shields["US:NPS:Blue_Ridge"] = { + norefImage: shieldImages.shield_us_nps_brp, + notext: true, + }; + // Alaska shields["US:AK"] = { backgroundImage: shieldImages.shield_us_ak, From e811b9fb063eca6069945d070cafbdd6aabd6ba7 Mon Sep 17 00:00:00 2001 From: Jeroen van der Gun Date: Tue, 11 Oct 2022 20:01:53 +0200 Subject: [PATCH 014/514] capital=yes is equivalent to capital=2 See: https://github.com/openmaptiles/openmaptiles/blob/master/layers/place/capital.sql --- scripts/taginfo_template.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/taginfo_template.json b/scripts/taginfo_template.json index 7a6e6d035..b03d4675e 100644 --- a/scripts/taginfo_template.json +++ b/scripts/taginfo_template.json @@ -10,6 +10,14 @@ "contact_email": "zelonewolf@gmail.com" }, "tags": [ + { + "key": "capital", + "value": "yes", + "object_types": ["node"], + "description": "Marks the city with a national capital icon.", + "doc_url": "https://openmaptiles.org/schema/#capital", + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/star_nation_capital.svg" + }, { "key": "capital", "value": "2", From b4666e056c989f2a0305aa71ffe258fa2115d1e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Thu, 24 Nov 2022 21:37:27 -0800 Subject: [PATCH 015/514] Fixed noref shields Draw shield blank for noref relation when a separate noref image is undefined. --- src/js/shield.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/shield.js b/src/js/shield.js index 3e8e42204..8c2a723e9 100644 --- a/src/js/shield.js +++ b/src/js/shield.js @@ -85,7 +85,7 @@ function getRasterShieldBlank(shieldDef, routeDef) { //Special case where there's a defined fallback shield when no ref is tagged //Example: PA Turnpike - if (!isValidRef(routeDef.ref)) { + if (!isValidRef(routeDef.ref) && "norefImage" in shieldDef) { return shieldDef.norefImage; } From bcd967d5b3fa770807d04f4a09a2137cdecbf3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Thu, 24 Nov 2022 21:39:13 -0800 Subject: [PATCH 016/514] Added Atlantic City Expressway Connector shield --- src/js/shield_defs.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index de197a918..9c0b39660 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -1751,6 +1751,9 @@ export function loadShields(shieldImages) { backgroundImage: shieldImages.shield_us_nj_ace_noref, notext: true, }; + shields["US:NJ:ACE:Connector"] = banneredShield(shields["US:NJ:ACE"], [ + "CONN", + ]); shields["US:NJ:GSP"] = { backgroundImage: shieldImages.shield_us_nj_gsp_noref, notext: true, From ae63bd3b24e256154a03a461924e9492839328db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 25 Nov 2022 17:46:02 -0800 Subject: [PATCH 017/514] Iraq highway shields (#557) * Added Iraq highway shields * Update src/js/shield_defs.js Co-authored-by: Clay Smalley Co-authored-by: Clay Smalley --- doc-img/shield_map_world.svg | 1 + src/js/shield_defs.js | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/doc-img/shield_map_world.svg b/doc-img/shield_map_world.svg index 179aefef7..e85b377d4 100644 --- a/doc-img/shield_map_world.svg +++ b/doc-img/shield_map_world.svg @@ -126,6 +126,7 @@ See the end of this file for a list of available jurisdictions and their codes. .cn, .hk, .ir, +.iq, .jp, .kr, .my, diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 9c0b39660..340c80905 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -2853,6 +2853,14 @@ export function loadShields(shieldImages) { Color.shields.white ); + // Iraq + shields["IQ:national"] = roundedRectShield( + Color.shields.green, + Color.shields.white, + Color.shields.white, + 34 + ); + // Japan shields["JP:E"] = roundedRectShield(Color.shields.green, Color.shields.white); shields["JP:national"] = triangleConvexDownShieldBlue; From 0ff8c2434ab61496b2548687ed1cc7436179bfd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 25 Nov 2022 21:27:35 -0800 Subject: [PATCH 018/514] Added Algeria shields --- doc-img/shield_map_world.svg | 1 + src/js/shield_defs.js | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/doc-img/shield_map_world.svg b/doc-img/shield_map_world.svg index e85b377d4..f4abcf63f 100644 --- a/doc-img/shield_map_world.svg +++ b/doc-img/shield_map_world.svg @@ -120,6 +120,7 @@ See the end of this file for a list of available jurisdictions and their codes. .co, .uy, .ve, +.dz, .gh, .am, .bd, diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 340c80905..c59a5ca92 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -2728,6 +2728,16 @@ export function loadShields(shieldImages) { // AFRICA + // Algeria + shields["DZ:highway"] = shields["DZ:national"] = roundedRectShield( + Color.shields.red, + Color.shields.white + ); + shields["DZ:regional"] = roundedRectShield( + Color.shields.yellow, + Color.shields.black + ); + // Ghana shields["GH:national"] = shields["GH:inter-regional"] = From 150fda426c849d31de3db9c490e09ab8d5971c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 26 Nov 2022 15:21:53 -0800 Subject: [PATCH 019/514] Document name usage for taginfo --- scripts/taginfo_template.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/taginfo_template.json b/scripts/taginfo_template.json index b03d4675e..6c26db5da 100644 --- a/scripts/taginfo_template.json +++ b/scripts/taginfo_template.json @@ -10,6 +10,16 @@ "contact_email": "zelonewolf@gmail.com" }, "tags": [ + { + "key": "name:en", + "object_types": ["node", "way", "relation", "area"], + "description": "Labels are in English if available." + }, + { + "key": "name", + "object_types": ["node", "way", "relation", "area"], + "description": "Labels fall back to the local language if name:en is unavailable." + }, { "key": "capital", "value": "yes", From 794a8b4c3fdfe3c49344b944dfb7479106fdd400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 26 Nov 2022 15:22:54 -0800 Subject: [PATCH 020/514] Consolidated name expressions for consistency --- src/constants/label.js | 2 +- src/layer/aeroway.js | 12 +++--------- src/layer/park.js | 13 +++---------- src/layer/place.js | 20 ++++++++++---------- src/layer/water.js | 12 +++--------- 5 files changed, 20 insertions(+), 39 deletions(-) diff --git a/src/constants/label.js b/src/constants/label.js index a18a80674..0f0e973f2 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -1,7 +1,7 @@ "use strict"; // Name fields in order of preference -export const name_en = [ +export const localizedName = [ "coalesce", ["get", "name:en"], ["get", "name:latin"], diff --git a/src/layer/aeroway.js b/src/layer/aeroway.js index b3c7ff124..d65cee895 100644 --- a/src/layer/aeroway.js +++ b/src/layer/aeroway.js @@ -1,14 +1,8 @@ "use strict"; +import * as Label from "../constants/label.js"; import * as Color from "../constants/color.js"; -const name_en = [ - "coalesce", - ["get", "name:en"], - ["get", "name:latin"], - ["get", "name"], -]; - const minorAirport = [ "any", ["!", ["has", "iata"]], @@ -221,7 +215,7 @@ export const airportLabel = { }, layout: { visibility: "visible", - "text-field": name_en, + "text-field": Label.localizedName, "text-font": ["Metropolis Bold"], "text-size": 10, ...iconLayout, @@ -245,7 +239,7 @@ export const minorAirportLabel = { }, layout: { visibility: "visible", - "text-field": name_en, + "text-field": Label.localizedName, "text-font": ["Metropolis Bold"], "text-size": 10, }, diff --git a/src/layer/park.js b/src/layer/park.js index 0e53e70ae..8d58424ba 100644 --- a/src/layer/park.js +++ b/src/layer/park.js @@ -1,15 +1,8 @@ "use strict"; +import * as Label from "../constants/label.js"; import * as Color from "../constants/color.js"; -// Name fields in order of preference -const name_en = [ - "coalesce", - ["get", "name:en"], - ["get", "name:latin"], - ["get", "name"], -]; - export const fill = { id: "protected-area-fill", type: "fill", @@ -50,7 +43,7 @@ export const label = { }, layout: { visibility: "visible", - "text-field": name_en, + "text-field": Label.localizedName, "text-font": ["Metropolis Bold"], "text-size": 10, "symbol-sort-key": ["get", "rank"], @@ -102,7 +95,7 @@ export const parkLabel = { }, layout: { visibility: "visible", - "text-field": name_en, + "text-field": Label.localizedName, "text-font": ["Metropolis Bold"], "text-size": 10, "symbol-sort-key": ["get", "rank"], diff --git a/src/layer/place.js b/src/layer/place.js index 4420c1019..5df441b6d 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -1,4 +1,4 @@ -import * as label from "../constants/label.js"; +import * as Label from "../constants/label.js"; const cityLabelPaint = { "text-color": "#444", @@ -59,7 +59,7 @@ export const village = { [11, 0.5], ], }, - "text-field": label.name_en, + "text-field": Label.localizedName, "text-anchor": "bottom", "text-variable-anchor": [ "bottom", @@ -122,7 +122,7 @@ export const town = { [11, 0.7], ], }, - "text-field": label.name_en, + "text-field": Label.localizedName, "text-anchor": "bottom", "text-variable-anchor": [ "bottom", @@ -181,7 +181,7 @@ export const city = { [11, 0.9], ], }, - "text-field": label.name_en, + "text-field": Label.localizedName, "text-anchor": "bottom", "text-variable-anchor": [ "bottom", @@ -223,7 +223,7 @@ export const state = { [6, 14], ], }, - "text-field": label.name_en, + "text-field": Label.localizedName, "text-padding": 1, "text-transform": "uppercase", "text-letter-spacing": 0.04, @@ -266,7 +266,7 @@ export const countryOther = { [7, 15], ], }, - "text-field": label.name_en, + "text-field": Label.localizedName, "text-max-width": 6.25, "text-transform": "none", }, @@ -296,7 +296,7 @@ export const country3 = { [7, 17], ], }, - "text-field": label.name_en, + "text-field": Label.localizedName, "text-max-width": 6.25, "text-transform": "none", }, @@ -326,7 +326,7 @@ export const country2 = { [5, 17], ], }, - "text-field": label.name_en, + "text-field": Label.localizedName, "text-max-width": 6.25, "text-transform": "none", }, @@ -357,7 +357,7 @@ export const country1 = { [6, 19], ], }, - "text-field": label.name_en, + "text-field": Label.localizedName, "text-max-width": ["step", ["zoom"], 6.25, 3, 12], "text-transform": "none", "text-offset": [ @@ -383,7 +383,7 @@ export const continent = { layout: { "text-font": ["Metropolis Light"], "text-size": 13, - "text-field": label.name_en, + "text-field": Label.localizedName, "text-justify": "center", "text-transform": "uppercase", }, diff --git a/src/layer/water.js b/src/layer/water.js index 580f5855a..9d532612b 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -1,5 +1,6 @@ "use strict"; +import * as Label from "../constants/label.js"; import * as Color from "../constants/color.js"; const bigRivers = ["river", "canal"]; @@ -75,16 +76,9 @@ const labelPaintProperties = { "text-halo-blur": 0.25, }; -const nameField = [ - "coalesce", - ["get", "name:en"], - ["get", "name_en"], - ["get", "name"], -]; - const labelLayoutProperties = { "symbol-placement": "line", - "text-field": nameField, + "text-field": Label.localizedName, "text-font": ["Metropolis Bold Italic"], "text-max-angle": 55, }; @@ -153,7 +147,7 @@ export const waterPointLabel = { "source-layer": "water_name", filter: ["all", ["==", ["geometry-type"], "Point"]], layout: { - "text-field": nameField, + "text-field": Label.localizedName, "text-font": ["Metropolis Bold Italic"], "text-size": [ "interpolate", From 3ff1f9c006857d1cf0b36f65cce2dfed179b1655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 26 Nov 2022 16:10:11 -0800 Subject: [PATCH 021/514] Use user-preferred languages in labels --- scripts/taginfo_template.json | 7 +------ src/constants/label.js | 27 +++++++++++++++++++++------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/scripts/taginfo_template.json b/scripts/taginfo_template.json index 6c26db5da..3ce35e689 100644 --- a/scripts/taginfo_template.json +++ b/scripts/taginfo_template.json @@ -10,15 +10,10 @@ "contact_email": "zelonewolf@gmail.com" }, "tags": [ - { - "key": "name:en", - "object_types": ["node", "way", "relation", "area"], - "description": "Labels are in English if available." - }, { "key": "name", "object_types": ["node", "way", "relation", "area"], - "description": "Labels fall back to the local language if name:en is unavailable." + "description": "Labels fall back to the local language if none of the user-preferred languages is available." }, { "key": "capital", diff --git a/src/constants/label.js b/src/constants/label.js index 0f0e973f2..37ae6f77d 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -1,9 +1,24 @@ "use strict"; // Name fields in order of preference -export const localizedName = [ - "coalesce", - ["get", "name:en"], - ["get", "name:latin"], - ["get", "name"], -]; +export const localizedName = (function () { + let userLocales = + "languages" in navigator ? [...navigator.languages] : [navigator.language]; + let locales = []; + let localeSet = new Set(); // avoid duplicates + for (let locale of userLocales) { + // Add progressively less specific variants of each user-specified locale. + let components = locale.split("-"); + while (components.length > 0) { + let parent = components.join("-"); + if (!localeSet.has(parent)) locales.push(parent); + localeSet.add(parent); + components.pop(); + } + } + if (locales.at(-1) === "en") { + locales.push("latin"); + } + let nameFields = [...locales.map((l) => `name:${l}`), "name"]; + return ["coalesce", ...nameFields.map((f) => ["get", f])]; +})(); From 275dde8358ae0e85f552bf77f9b89bc5411754f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 26 Nov 2022 19:59:31 -0800 Subject: [PATCH 022/514] Update src/constants/label.js Co-authored-by: Josh Lee --- src/constants/label.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/constants/label.js b/src/constants/label.js index 37ae6f77d..5db32f0f3 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -2,8 +2,7 @@ // Name fields in order of preference export const localizedName = (function () { - let userLocales = - "languages" in navigator ? [...navigator.languages] : [navigator.language]; + let userLocales = navigator.languages ?? [navigator.language]; let locales = []; let localeSet = new Set(); // avoid duplicates for (let locale of userLocales) { From f5db5eed5e325f1eb0eb9564b7655b0353320a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 26 Nov 2022 21:13:21 -0800 Subject: [PATCH 023/514] Localize road names (into German) --- src/constants/label.js | 28 ++++++++++++++++++++++++---- src/layer/transportation_label.js | 8 ++------ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/constants/label.js b/src/constants/label.js index 5db32f0f3..e25addfdd 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -1,7 +1,14 @@ "use strict"; -// Name fields in order of preference -export const localizedName = (function () { +/** + * Returns a `coalesce` expression that resolves to the feature's name in a + * language that the user prefers. + * + * @param {boolean} includesLegacyFields - Whether to include the older fields + * that include underscores, for layers that have not transitioned to the + * colon syntax. + */ +function getLocalizedNameExpression(includesLegacyFields) { let userLocales = navigator.languages ?? [navigator.language]; let locales = []; let localeSet = new Set(); // avoid duplicates @@ -18,6 +25,19 @@ export const localizedName = (function () { if (locales.at(-1) === "en") { locales.push("latin"); } - let nameFields = [...locales.map((l) => `name:${l}`), "name"]; + let nameFields = [ + ...locales.flatMap((l) => { + let fields = [`name:${l}`]; + // transportation_label uses an underscore instead of a colon. + // https://github.com/openmaptiles/openmaptiles/issues/769 + if (includesLegacyFields && (l === "de" || l === "en")) + fields.push(`name_${l}`); + return fields; + }), + "name", + ]; return ["coalesce", ...nameFields.map((f) => ["get", f])]; -})(); +} + +export const localizedName = getLocalizedNameExpression(false); +export const legacyLocalizedName = getLocalizedNameExpression(true); diff --git a/src/layer/transportation_label.js b/src/layer/transportation_label.js index 243db3a53..d448d540a 100644 --- a/src/layer/transportation_label.js +++ b/src/layer/transportation_label.js @@ -1,5 +1,6 @@ "use strict"; +import * as Label from "../constants/label.js"; import * as Color from "../constants/color.js"; const highwaySelector = ["match", ["get", "class"]]; @@ -79,12 +80,7 @@ export const label = { ["literal", ["Metropolis Regular Italic"]], ["literal", ["Metropolis Light"]], ], - "text-field": [ - "concat", - ["get", "name:latin"], - " ", - ["get", "name:nonlatin"], - ], + "text-field": Label.legacyLocalizedName, "text-max-angle": 20, "symbol-placement": "line", "text-size": [ From a258c24c6f4730de83285ab8bd4bcf0ee61e72d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 26 Nov 2022 10:55:46 -0800 Subject: [PATCH 024/514] Added Haiti national, departmental route shields --- doc-img/shield_map_world.svg | 1 + src/js/shield_defs.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/doc-img/shield_map_world.svg b/doc-img/shield_map_world.svg index f4abcf63f..08ad8a453 100644 --- a/doc-img/shield_map_world.svg +++ b/doc-img/shield_map_world.svg @@ -115,6 +115,7 @@ The lines may be combined if desired: See the end of this file for a list of available jurisdictions and their codes. */ .ca, +.ht, .us, .cl, .co, diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index c59a5ca92..42bdc434b 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -801,6 +801,12 @@ export function loadShields(shieldImages) { // Yukon shields["CA:YT"] = roundedRectShield(Color.shields.white, Color.shields.red); + // Haiti + shields["HT:RN-road"] = shields["HT:RD-road"] = roundedRectShield( + Color.shields.blue, + Color.shields.white + ); + // United States // Interstate Highways From 18ff9729d8ec022297f56ed573b98afdf7360eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 26 Nov 2022 23:08:30 -0800 Subject: [PATCH 025/514] Rebuild style layers when user language changes --- src/americana.js | 429 ++++++++++++++++-------------- src/constants/label.js | 6 +- src/layer/aeroway.js | 8 +- src/layer/park.js | 8 +- src/layer/place.js | 27 ++ src/layer/transportation_label.js | 5 +- src/layer/water.js | 9 + 7 files changed, 283 insertions(+), 209 deletions(-) diff --git a/src/americana.js b/src/americana.js index 37d56370a..a31e27bec 100644 --- a/src/americana.js +++ b/src/americana.js @@ -2,6 +2,8 @@ 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"; @@ -29,269 +31,289 @@ import * as search from "./search.js"; import SampleControl from "openmapsamples-maplibre/OpenMapSamplesControl.js"; import { default as OpenMapTilesSamples } from "openmapsamples/samples/OpenMapTiles/index.js"; -/* - This is a list of the layers in the Americana style, from bottom to top. -*/ -var americanaLayers = []; +function buildLayers() { + // Layers from bottom to top + let layers = []; -americanaLayers.push( - lyrBackground.base, - lyrPark.fill, - lyrAeroway.fill, - lyrPark.parkFill, + layers.push( + lyrBackground.base, + lyrPark.fill, + lyrAeroway.fill, + lyrPark.parkFill, - lyrBoundary.countyCasing, - lyrBoundary.stateCasing, - lyrBoundary.countryCasing, + lyrBoundary.countyCasing, + lyrBoundary.stateCasing, + lyrBoundary.countryCasing, - lyrWater.water, - lyrWater.waterway, - lyrWater.waterwayIntermittent, + lyrWater.water, + lyrWater.waterway, + lyrWater.waterwayIntermittent, - lyrPark.outline, - lyrAeroway.outline, - lyrPark.parkOutline, + lyrPark.outline, + lyrAeroway.outline, + lyrPark.parkOutline, - lyrBoundary.city, - lyrBoundary.county, - lyrBoundary.state, - lyrBoundary.country, + lyrBoundary.city, + lyrBoundary.county, + lyrBoundary.state, + lyrBoundary.country, - lyrBackground.pierArea, - lyrBackground.pierLine, + lyrBackground.pierArea, + lyrBackground.pierLine, - lyrRail.railTunnel.dashes(), - lyrRail.railServiceTunnel.dashes(), + lyrRail.railTunnel.dashes(), + lyrRail.railServiceTunnel.dashes(), - lyrRail.narrowGaugeTunnel.dashes(), - lyrRail.narrowGaugeServiceTunnel.dashes(), + lyrRail.narrowGaugeTunnel.dashes(), + lyrRail.narrowGaugeServiceTunnel.dashes(), - lyrRail.lightRailTramTunnel.dashes(), - lyrRail.lightRailTramServiceTunnel.dashes(), + lyrRail.lightRailTramTunnel.dashes(), + lyrRail.lightRailTramServiceTunnel.dashes(), - lyrRail.funicularTunnel.dashes(), + lyrRail.funicularTunnel.dashes(), - lyrRail.railwayTunnel.fill(), + lyrRail.railwayTunnel.fill(), - lyrConstruction.road, + lyrConstruction.road, - lyrRoad.roadTunnel.casing(), + lyrRoad.roadTunnel.casing(), - lyrRoad.roadTunnel.fill(), + lyrRoad.roadTunnel.fill(), - lyrOneway.tunnel, - lyrOneway.tunnelLink, + lyrOneway.tunnel, + lyrOneway.tunnelLink, - lyrFerry.ferry, + lyrFerry.ferry, - lyrAeroway.runway, - lyrAeroway.runwayArea, - lyrAeroway.taxiway, - lyrAeroway.taxiwayArea, + lyrAeroway.runway, + lyrAeroway.runwayArea, + lyrAeroway.taxiway, + lyrAeroway.taxiwayArea, - lyrRoad.motorwayLink.casing(), - lyrRoad.trunkLink.casing(), + lyrRoad.motorwayLink.casing(), + lyrRoad.trunkLink.casing(), - lyrRoad.roadLinkSimpleCasing.casing(), + lyrRoad.roadLinkSimpleCasing.casing(), - lyrRoad.motorway.casing(), - lyrRoad.trunk.casing(), - lyrRoad.primaryExpressway.casing(), - lyrRoad.secondaryExpressway.casing(), - lyrRoad.tertiaryExpressway.casing(), + lyrRoad.motorway.casing(), + lyrRoad.trunk.casing(), + lyrRoad.primaryExpressway.casing(), + lyrRoad.secondaryExpressway.casing(), + lyrRoad.tertiaryExpressway.casing(), - lyrRoad.roadSimpleCasing.casing(), + lyrRoad.roadSimpleCasing.casing(), - lyrRoad.motorwayLink.fill(), - lyrRoad.roadLinkSimpleFill.fill(), - lyrRoad.primaryLink.fill(), - lyrRoad.primaryLinkToll.fill(), - lyrRoad.secondaryLink.fill(), - lyrRoad.secondaryLinkToll.fill(), - lyrRoad.tertiaryLink.fill(), - lyrRoad.tertiaryLinkToll.fill(), + lyrRoad.motorwayLink.fill(), + lyrRoad.roadLinkSimpleFill.fill(), + lyrRoad.primaryLink.fill(), + lyrRoad.primaryLinkToll.fill(), + lyrRoad.secondaryLink.fill(), + lyrRoad.secondaryLinkToll.fill(), + lyrRoad.tertiaryLink.fill(), + lyrRoad.tertiaryLinkToll.fill(), - lyrRoad.minor.fill(), - lyrRoad.minorToll.fill(), - lyrRoad.tertiary.fill(), - lyrRoad.tertiaryToll.fill(), - lyrRoad.secondary.fill(), - lyrRoad.secondaryToll.fill(), - lyrRoad.primary.fill(), - lyrRoad.primaryToll.fill(), - lyrRoad.roadSimpleFill.fill(), - lyrRoad.motorway.fill(), + lyrRoad.minor.fill(), + lyrRoad.minorToll.fill(), + lyrRoad.tertiary.fill(), + lyrRoad.tertiaryToll.fill(), + lyrRoad.secondary.fill(), + lyrRoad.secondaryToll.fill(), + lyrRoad.primary.fill(), + lyrRoad.primaryToll.fill(), + lyrRoad.roadSimpleFill.fill(), + lyrRoad.motorway.fill(), - lyrRoad.road.surface(), + lyrRoad.road.surface(), - lyrRail.rail.dashes(), - lyrRail.railService.dashes(), + lyrRail.rail.dashes(), + lyrRail.railService.dashes(), - lyrRail.narrowGauge.dashes(), - lyrRail.narrowGaugeService.dashes(), + lyrRail.narrowGauge.dashes(), + lyrRail.narrowGaugeService.dashes(), - lyrRail.lightRailTram.dashes(), - lyrRail.lightRailTramService.dashes(), + lyrRail.lightRailTram.dashes(), + lyrRail.lightRailTramService.dashes(), - lyrRail.funicular.dashes(), + lyrRail.funicular.dashes(), - lyrRail.railway.fill(), + lyrRail.railway.fill(), - lyrOneway.road, - lyrOneway.link -); + lyrOneway.road, + lyrOneway.link + ); -americanaLayers.push(lyrBuilding.building); + layers.push(lyrBuilding.building); -var bridgeLayers = [ - lyrRail.bridgeCasing, + var bridgeLayers = [ + lyrRail.bridgeCasing, - lyrRoad.trunkLinkBridge.casing(), - lyrRoad.motorwayLinkBridge.casing(), + lyrRoad.trunkLinkBridge.casing(), + lyrRoad.motorwayLinkBridge.casing(), - lyrRoad.roadLinkSimpleCasingBridge.casing(), + lyrRoad.roadLinkSimpleCasingBridge.casing(), - lyrRoad.tertiaryExpresswayBridge.casing(), - lyrRoad.secondaryExpresswayBridge.casing(), - lyrRoad.primaryExpresswayBridge.casing(), - lyrRoad.trunkBridge.casing(), - lyrRoad.motorwayBridge.casing(), + lyrRoad.tertiaryExpresswayBridge.casing(), + lyrRoad.secondaryExpresswayBridge.casing(), + lyrRoad.primaryExpresswayBridge.casing(), + lyrRoad.trunkBridge.casing(), + lyrRoad.motorwayBridge.casing(), - lyrRoad.roadSimpleCasingBridge.casing(), + lyrRoad.roadSimpleCasingBridge.casing(), - lyrRoad.tertiaryLinkBridge.fill(), - lyrRoad.tertiaryLinkTollBridge.fill(), - lyrRoad.secondaryLinkBridge.fill(), - lyrRoad.secondaryLinkTollBridge.fill(), - lyrRoad.primaryLinkBridge.fill(), - lyrRoad.primaryLinkTollBridge.fill(), - lyrRoad.roadLinkSimpleFillBridge.fill(), - lyrRoad.motorwayLinkBridge.fill(), + lyrRoad.tertiaryLinkBridge.fill(), + lyrRoad.tertiaryLinkTollBridge.fill(), + lyrRoad.secondaryLinkBridge.fill(), + lyrRoad.secondaryLinkTollBridge.fill(), + lyrRoad.primaryLinkBridge.fill(), + lyrRoad.primaryLinkTollBridge.fill(), + lyrRoad.roadLinkSimpleFillBridge.fill(), + lyrRoad.motorwayLinkBridge.fill(), - lyrRoad.minorBridge.fill(), - lyrRoad.minorTollBridge.fill(), - lyrRoad.tertiaryBridge.fill(), - lyrRoad.tertiaryTollBridge.fill(), - lyrRoad.secondaryBridge.fill(), - lyrRoad.secondaryTollBridge.fill(), - lyrRoad.primaryBridge.fill(), - lyrRoad.primaryTollBridge.fill(), - lyrRoad.roadSimpleFillBridge.fill(), - lyrRoad.motorwayBridge.fill(), + lyrRoad.minorBridge.fill(), + lyrRoad.minorTollBridge.fill(), + lyrRoad.tertiaryBridge.fill(), + lyrRoad.tertiaryTollBridge.fill(), + lyrRoad.secondaryBridge.fill(), + lyrRoad.secondaryTollBridge.fill(), + lyrRoad.primaryBridge.fill(), + lyrRoad.primaryTollBridge.fill(), + lyrRoad.roadSimpleFillBridge.fill(), + lyrRoad.motorwayBridge.fill(), - lyrRoad.roadBridge.surface(), + lyrRoad.roadBridge.surface(), - lyrRail.railBridge.dashes(), - lyrRail.railServiceBridge.dashes(), + lyrRail.railBridge.dashes(), + lyrRail.railServiceBridge.dashes(), - lyrRail.narrowGaugeBridge.dashes(), - lyrRail.narrowGaugeServiceBridge.dashes(), + lyrRail.narrowGaugeBridge.dashes(), + lyrRail.narrowGaugeServiceBridge.dashes(), - lyrRail.lightRailTramBridge.dashes(), - lyrRail.lightRailTramServiceBridge.dashes(), + lyrRail.lightRailTramBridge.dashes(), + lyrRail.lightRailTramServiceBridge.dashes(), - lyrRail.funicularBridge.dashes(), + lyrRail.funicularBridge.dashes(), - lyrRail.railwayBridge.fill(), + lyrRail.railwayBridge.fill(), - lyrOneway.bridge, - lyrOneway.bridgeLink, -]; + lyrOneway.bridge, + lyrOneway.bridgeLink, + ]; -//Render bridge without layer on the lowest bridge layer -bridgeLayers.forEach((layer) => - americanaLayers.push( - Util.filteredClone(layer, ["!", ["has", "layer"]], "_layer_bottom") - ) -); + //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++) { + //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) => - americanaLayers.push(Util.restrictLayer(layer, i)) + layers.push( + Util.filteredClone( + layer, + [">=", ["coalesce", ["get", "layer"], 0], 5], + "_layer_top" + ) + ) + ); + + layers.push( + //The labels at the end of the list draw on top of the layers at the beginning. + lyrWater.waterwayLabel, + lyrWater.waterLabel, + lyrWater.waterPointLabel, + + lyrTransportationLabel.bridgeSpacer, + lyrTransportationLabel.label, + + lyrPark.label, + lyrPark.parkLabel, + /* The ref label shows up at lower zoom levels and when the long name doesn't fit */ + lyrAeroway.airportRefLabel, + lyrAeroway.minorAirportRefLabel, + lyrAeroway.airportLabel, + lyrAeroway.minorAirportLabel, + lyrAeroway.airportGate, + + lyrHighwayShield.shield, + + lyrHighwayExit.exits, + + lyrPlace.state, + lyrPlace.village, + lyrPlace.town, + lyrPlace.city, + lyrPlace.countryOther, + lyrPlace.country3, + lyrPlace.country2, + lyrPlace.country1, + lyrPlace.continent ); + + // Resolve localized name placeholders. + let localizedNameExpression = Label.getLocalizedNameExpression(false); + let legacyLocalizedNameExpression = Label.getLocalizedNameExpression(true); + for (let layer of layers) { + if ( + "metadata" in layer && + "layout" in layer && + layer.metadata["americana:text-field-localized"] === true + ) { + // https://github.com/openmaptiles/openmaptiles/issues/769 + layer.layout["text-field"] = + layer["source-layer"] === "transportation_name" + ? legacyLocalizedNameExpression + : localizedNameExpression; + } + } + + return layers; } -//If layer is more than 5, just give up and render on a single layer. -bridgeLayers.forEach((layer) => - americanaLayers.push( - Util.filteredClone( - layer, - [">=", ["coalesce", ["get", "layer"], 0], 5], - "_layer_top" - ) - ) -); - -americanaLayers.push( - //The labels at the end of the list draw on top of the layers at the beginning. - lyrWater.waterwayLabel, - lyrWater.waterLabel, - lyrWater.waterPointLabel, - - lyrTransportationLabel.bridgeSpacer, - lyrTransportationLabel.label, - - lyrPark.label, - lyrPark.parkLabel, - /* The ref label shows up at lower zoom levels and when the long name doesn't fit */ - lyrAeroway.airportRefLabel, - lyrAeroway.minorAirportRefLabel, - lyrAeroway.airportLabel, - lyrAeroway.minorAirportLabel, - lyrAeroway.airportGate, - - lyrHighwayShield.shield, - - lyrHighwayExit.exits, - - lyrPlace.state, - lyrPlace.village, - lyrPlace.town, - lyrPlace.city, - lyrPlace.countryOther, - lyrPlace.country3, - lyrPlace.country2, - lyrPlace.country1, - lyrPlace.continent -); - -console.debug(`Loaded ${americanaLayers.length} layers`); - -var getUrl = window.location; -var baseUrl = getUrl.protocol + "//" + getUrl.host + getUrl.pathname; - -var style = { - id: "streets", - name: "Americana", - glyphs: "https://fonts.openmaptiles.org/{fontstack}/{range}.pbf", - layers: americanaLayers, - sources: { - openmaptiles: { - url: config.OPENMAPTILES_URL, - type: "vector", +function buildStyle() { + var getUrl = window.location; + var baseUrl = getUrl.protocol + "//" + getUrl.host + getUrl.pathname; + + return { + id: "streets", + name: "Americana", + glyphs: "https://fonts.openmaptiles.org/{fontstack}/{range}.pbf", + layers: buildLayers(), + sources: { + openmaptiles: { + url: config.OPENMAPTILES_URL, + type: "vector", + }, }, - }, - sprite: new URL("sprites/sprite", baseUrl).href, - light: { - anchor: "viewport", - color: "white", - intensity: 0.12, - }, - version: 8, -}; + sprite: new URL("sprites/sprite", baseUrl).href, + light: { + anchor: "viewport", + color: "white", + intensity: 0.12, + }, + version: 8, + }; +} export const map = (window.map = new maplibregl.Map({ container: "map", // container id hash: true, antialias: true, - style: style, + style: buildStyle(), center: [-94, 40.5], // starting position [lng, lat] zoom: 4, // starting zoom attributionControl: false, })); -map.on("styledata", function () { +map.on("styledata", function (event) { + if (event.dataType === "style") { + console.debug(`Loaded ${map.getStyle().layers.length} layers`); + } ShieldDef.loadShields(map.style.imageManager.images); }); @@ -299,6 +321,11 @@ map.on("styleimagemissing", function (e) { Shield.missingIconHandler(map, e); }); +window.addEventListener("languagechange", (event) => { + console.log(`Changed to ${navigator.languages}`); + map.setStyle(buildStyle()); +}); + let attributionConfig = { customAttribution: "", }; diff --git a/src/constants/label.js b/src/constants/label.js index e25addfdd..23e1cb98b 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -8,7 +8,7 @@ * that include underscores, for layers that have not transitioned to the * colon syntax. */ -function getLocalizedNameExpression(includesLegacyFields) { +export function getLocalizedNameExpression(includesLegacyFields) { let userLocales = navigator.languages ?? [navigator.language]; let locales = []; let localeSet = new Set(); // avoid duplicates @@ -39,5 +39,5 @@ function getLocalizedNameExpression(includesLegacyFields) { return ["coalesce", ...nameFields.map((f) => ["get", f])]; } -export const localizedName = getLocalizedNameExpression(false); -export const legacyLocalizedName = getLocalizedNameExpression(true); +// Placeholder to be resolved by buildLayers() +export const localizedName = "$$localizedName"; diff --git a/src/layer/aeroway.js b/src/layer/aeroway.js index d65cee895..9b81edcc3 100644 --- a/src/layer/aeroway.js +++ b/src/layer/aeroway.js @@ -221,7 +221,9 @@ export const airportLabel = { ...iconLayout, }, source: "openmaptiles", - metadata: {}, + metadata: { + "americana:text-field-localized": true, + }, "source-layer": "aerodrome_label", }; @@ -244,7 +246,9 @@ export const minorAirportLabel = { "text-size": 10, }, source: "openmaptiles", - metadata: {}, + metadata: { + "americana:text-field-localized": true, + }, "source-layer": "aerodrome_label", }; diff --git a/src/layer/park.js b/src/layer/park.js index 8d58424ba..69968619e 100644 --- a/src/layer/park.js +++ b/src/layer/park.js @@ -49,7 +49,9 @@ export const label = { "symbol-sort-key": ["get", "rank"], }, source: "openmaptiles", - metadata: {}, + metadata: { + "americana:text-field-localized": true, + }, "source-layer": "park", }; @@ -101,6 +103,8 @@ export const parkLabel = { "symbol-sort-key": ["get", "rank"], }, source: "openmaptiles", - metadata: {}, + metadata: { + "americana:text-field-localized": true, + }, "source-layer": "poi", }; diff --git a/src/layer/place.js b/src/layer/place.js index 5df441b6d..601c7b80a 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -80,6 +80,9 @@ export const village = { minzoom: 11, maxzoom: 14, "source-layer": "place", + metadata: { + "americana:text-field-localized": true, + }, }; export const town = { @@ -143,6 +146,9 @@ export const town = { minzoom: 4, maxzoom: 13, "source-layer": "place", + metadata: { + "americana:text-field-localized": true, + }, }; export const city = { @@ -202,6 +208,9 @@ export const city = { minzoom: 4, maxzoom: 12, "source-layer": "place", + metadata: { + "americana:text-field-localized": true, + }, }; export const state = { @@ -243,6 +252,9 @@ export const state = { maxzoom: 7, minzoom: 3, "source-layer": "place", + metadata: { + "americana:text-field-localized": true, + }, }; export const countryOther = { id: "country_other", @@ -272,6 +284,9 @@ export const countryOther = { }, source: "openmaptiles", "source-layer": "place", + metadata: { + "americana:text-field-localized": true, + }, }; export const country3 = { id: "country_3", @@ -302,6 +317,9 @@ export const country3 = { }, source: "openmaptiles", "source-layer": "place", + metadata: { + "americana:text-field-localized": true, + }, }; export const country2 = { id: "country_2", @@ -332,6 +350,9 @@ export const country2 = { }, source: "openmaptiles", "source-layer": "place", + metadata: { + "americana:text-field-localized": true, + }, }; export const country1 = { id: "country_1", @@ -370,6 +391,9 @@ export const country1 = { }, source: "openmaptiles", "source-layer": "place", + metadata: { + "americana:text-field-localized": true, + }, }; export const continent = { id: "continent", @@ -390,4 +414,7 @@ export const continent = { source: "openmaptiles", maxzoom: 1, "source-layer": "place", + metadata: { + "americana:text-field-localized": true, + }, }; diff --git a/src/layer/transportation_label.js b/src/layer/transportation_label.js index d448d540a..2c8bb0dff 100644 --- a/src/layer/transportation_label.js +++ b/src/layer/transportation_label.js @@ -80,7 +80,7 @@ export const label = { ["literal", ["Metropolis Regular Italic"]], ["literal", ["Metropolis Light"]], ], - "text-field": Label.legacyLocalizedName, + "text-field": Label.localizedName, "text-max-angle": 20, "symbol-placement": "line", "text-size": [ @@ -119,6 +119,9 @@ export const label = { }, source: "openmaptiles", "source-layer": "transportation_name", + metadata: { + "americana:text-field-localized": true, + }, }; // A spacer label on each bridge to push any waterway label away from the bridge. diff --git a/src/layer/water.js b/src/layer/water.js index 9d532612b..d62703817 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -112,6 +112,9 @@ export const waterwayLabel = { "text-letter-spacing": 0.15, }, paint: labelPaintProperties, + metadata: { + "americana:text-field-localized": true, + }, }; //Lake labels rendered as a linear feature @@ -137,6 +140,9 @@ export const waterLabel = { "text-letter-spacing": 0.25, }, paint: labelPaintProperties, + metadata: { + "americana:text-field-localized": true, + }, }; //Lake labels rendered as a point feature @@ -163,4 +169,7 @@ export const waterPointLabel = { "text-letter-spacing": 0.25, }, paint: labelPaintProperties, + metadata: { + "americana:text-field-localized": true, + }, }; From 79fc899b3ae877ee2f5a32d726359e7ee0663424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 26 Nov 2022 23:22:53 -0800 Subject: [PATCH 026/514] Moved zoom, center to faux hash parameter --- src/americana.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/americana.js b/src/americana.js index a31e27bec..67c945316 100644 --- a/src/americana.js +++ b/src/americana.js @@ -300,9 +300,18 @@ function buildStyle() { }; } +function upgradeLegacyHash() { + let hash = window.location.hash.substr(1); + if (!hash.includes("=")) { + hash = `#map=${hash}`; + } + window.location.hash = hash; +} +upgradeLegacyHash(); + export const map = (window.map = new maplibregl.Map({ container: "map", // container id - hash: true, + hash: "map", antialias: true, style: buildStyle(), center: [-94, 40.5], // starting position [lng, lat] @@ -326,6 +335,10 @@ window.addEventListener("languagechange", (event) => { map.setStyle(buildStyle()); }); +window.addEventListener("hashchange", (event) => { + upgradeLegacyHash(); +}); + let attributionConfig = { customAttribution: "", }; From 57d79921c40b83b6dbfe3883145d988f480d7423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 26 Nov 2022 23:51:19 -0800 Subject: [PATCH 027/514] Override user language preference with hash parameter --- src/americana.js | 6 ++++++ src/constants/label.js | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/americana.js b/src/americana.js index 67c945316..c6f1c5bfa 100644 --- a/src/americana.js +++ b/src/americana.js @@ -337,6 +337,12 @@ window.addEventListener("languagechange", (event) => { window.addEventListener("hashchange", (event) => { upgradeLegacyHash(); + let oldLanguage = Label.getLanguageFromURL(new URL(event.oldURL)); + let newLanguage = Label.getLanguageFromURL(new URL(event.newURL)); + if (oldLanguage !== newLanguage) { + console.log(`Changed to ${newLanguage}`); + map.setStyle(buildStyle()); + } }); let attributionConfig = { diff --git a/src/constants/label.js b/src/constants/label.js index 23e1cb98b..7287a0ee2 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -1,5 +1,12 @@ "use strict"; +/** + * Returns a list of languages as a comma-delimited string from the given URL hash. + */ +export function getLanguageFromURL(url) { + return new URLSearchParams(url.hash.substr(1)).get("language"); +} + /** * Returns a `coalesce` expression that resolves to the feature's name in a * language that the user prefers. @@ -9,7 +16,10 @@ * colon syntax. */ export function getLocalizedNameExpression(includesLegacyFields) { - let userLocales = navigator.languages ?? [navigator.language]; + // Check the language "parameter" in the hash. + let parameter = getLanguageFromURL(window.location)?.split(","); + // Fall back to the user's language preference. + let userLocales = parameter ?? navigator.languages ?? [navigator.language]; let locales = []; let localeSet = new Set(); // avoid duplicates for (let locale of userLocales) { From 4d8d6c4291e69034194d0d7e2761c3371f424dc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 26 Nov 2022 23:59:13 -0800 Subject: [PATCH 028/514] Removed layer count log statement The layer count was misleadingly logged any time an image loaded. --- src/americana.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/americana.js b/src/americana.js index c6f1c5bfa..43ccc4a74 100644 --- a/src/americana.js +++ b/src/americana.js @@ -320,9 +320,6 @@ export const map = (window.map = new maplibregl.Map({ })); map.on("styledata", function (event) { - if (event.dataType === "style") { - console.debug(`Loaded ${map.getStyle().layers.length} layers`); - } ShieldDef.loadShields(map.style.imageManager.images); }); From 2d641cb26d554948d18dd8bcd9e0c18860b2c068 Mon Sep 17 00:00:00 2001 From: Josh Lee Date: Sun, 27 Nov 2022 07:43:21 -0500 Subject: [PATCH 029/514] Update esbuild Improved cross platform compatibility, yarn support. --- package-lock.json | 384 ++++++++++++++++++++++++++-------------------- package.json | 4 +- 2 files changed, 219 insertions(+), 169 deletions(-) diff --git a/package-lock.json b/package-lock.json index 30ad534a1..78fc58c92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ }, "devDependencies": { "create-serve": "^1.0.1", - "esbuild": "^0.14.43", + "esbuild": "^0.15.15", "maplibre-gl": "^2.1.9", "openmapsamples": "github:adamfranco/OpenMapSamples", "openmapsamples-maplibre": "github:adamfranco/OpenMapSamples-MapLibre", @@ -43,6 +43,38 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/@esbuild/android-arm": { + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.15.tgz", + "integrity": "sha512-JJjZjJi2eBL01QJuWjfCdZxcIgot+VoK6Fq7eKF9w4YHm9hwl7nhBR1o2Wnt/WcANk5l9SkpvrldW1PLuXxcbw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.15.tgz", + "integrity": "sha512-lhz6UNPMDXUhtXSulw8XlFAtSYO26WmHQnCi2Lg2p+/TMiJKNLtZCYUxV4wG6rZMzXmr8InGpNwk+DLT2Hm0PA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@mapbox/geojson-rewind": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.1.tgz", @@ -651,9 +683,9 @@ } }, "node_modules/esbuild": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.43.tgz", - "integrity": "sha512-Uf94+kQmy/5jsFwKWiQB4hfo/RkM9Dh7b79p8yqd1tshULdr25G2szLz631NoH3s2ujnKEKVD16RmOxvCNKRFA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.15.tgz", + "integrity": "sha512-TEw/lwK4Zzld9x3FedV6jy8onOUHqcEX3ADFk4k+gzPUwrxn8nWV62tH0udo8jOtjFodlEfc4ypsqX3e+WWO6w==", "dev": true, "hasInstallScript": true, "bin": { @@ -663,32 +695,34 @@ "node": ">=12" }, "optionalDependencies": { - "esbuild-android-64": "0.14.43", - "esbuild-android-arm64": "0.14.43", - "esbuild-darwin-64": "0.14.43", - "esbuild-darwin-arm64": "0.14.43", - "esbuild-freebsd-64": "0.14.43", - "esbuild-freebsd-arm64": "0.14.43", - "esbuild-linux-32": "0.14.43", - "esbuild-linux-64": "0.14.43", - "esbuild-linux-arm": "0.14.43", - "esbuild-linux-arm64": "0.14.43", - "esbuild-linux-mips64le": "0.14.43", - "esbuild-linux-ppc64le": "0.14.43", - "esbuild-linux-riscv64": "0.14.43", - "esbuild-linux-s390x": "0.14.43", - "esbuild-netbsd-64": "0.14.43", - "esbuild-openbsd-64": "0.14.43", - "esbuild-sunos-64": "0.14.43", - "esbuild-windows-32": "0.14.43", - "esbuild-windows-64": "0.14.43", - "esbuild-windows-arm64": "0.14.43" + "@esbuild/android-arm": "0.15.15", + "@esbuild/linux-loong64": "0.15.15", + "esbuild-android-64": "0.15.15", + "esbuild-android-arm64": "0.15.15", + "esbuild-darwin-64": "0.15.15", + "esbuild-darwin-arm64": "0.15.15", + "esbuild-freebsd-64": "0.15.15", + "esbuild-freebsd-arm64": "0.15.15", + "esbuild-linux-32": "0.15.15", + "esbuild-linux-64": "0.15.15", + "esbuild-linux-arm": "0.15.15", + "esbuild-linux-arm64": "0.15.15", + "esbuild-linux-mips64le": "0.15.15", + "esbuild-linux-ppc64le": "0.15.15", + "esbuild-linux-riscv64": "0.15.15", + "esbuild-linux-s390x": "0.15.15", + "esbuild-netbsd-64": "0.15.15", + "esbuild-openbsd-64": "0.15.15", + "esbuild-sunos-64": "0.15.15", + "esbuild-windows-32": "0.15.15", + "esbuild-windows-64": "0.15.15", + "esbuild-windows-arm64": "0.15.15" } }, "node_modules/esbuild-android-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.43.tgz", - "integrity": "sha512-kqFXAS72K6cNrB6RiM7YJ5lNvmWRDSlpi7ZuRZ1hu1S3w0zlwcoCxWAyM23LQUyZSs1PbjHgdbbfYAN8IGh6xg==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.15.tgz", + "integrity": "sha512-F+WjjQxO+JQOva3tJWNdVjouFMLK6R6i5gjDvgUthLYJnIZJsp1HlF523k73hELY20WPyEO8xcz7aaYBVkeg5Q==", "cpu": [ "x64" ], @@ -702,9 +736,9 @@ } }, "node_modules/esbuild-android-arm64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.43.tgz", - "integrity": "sha512-bKS2BBFh+7XZY9rpjiHGRNA7LvWYbZWP87pLehggTG7tTaCDvj8qQGOU/OZSjCSKDYbgY7Q+oDw8RlYQ2Jt2BA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.15.tgz", + "integrity": "sha512-attlyhD6Y22jNyQ0fIIQ7mnPvDWKw7k6FKnsXlBvQE6s3z6s6cuEHcSgoirquQc7TmZgVCK5fD/2uxmRN+ZpcQ==", "cpu": [ "arm64" ], @@ -718,9 +752,9 @@ } }, "node_modules/esbuild-darwin-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.43.tgz", - "integrity": "sha512-/3PSilx011ttoieRGkSZ0XV8zjBf2C9enV4ScMMbCT4dpx0mFhMOpFnCHkOK0pWGB8LklykFyHrWk2z6DENVUg==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.15.tgz", + "integrity": "sha512-ohZtF8W1SHJ4JWldsPVdk8st0r9ExbAOSrBOh5L+Mq47i696GVwv1ab/KlmbUoikSTNoXEhDzVpxUR/WIO19FQ==", "cpu": [ "x64" ], @@ -734,9 +768,9 @@ } }, "node_modules/esbuild-darwin-arm64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.43.tgz", - "integrity": "sha512-1HyFUKs8DMCBOvw1Qxpr5Vv/ThNcVIFb5xgXWK3pyT40WPvgYIiRTwJCvNs4l8i5qWF8/CK5bQxJVDjQvtv0Yw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.15.tgz", + "integrity": "sha512-P8jOZ5zshCNIuGn+9KehKs/cq5uIniC+BeCykvdVhx/rBXSxmtj3CUIKZz4sDCuESMbitK54drf/2QX9QHG5Ag==", "cpu": [ "arm64" ], @@ -750,9 +784,9 @@ } }, "node_modules/esbuild-freebsd-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.43.tgz", - "integrity": "sha512-FNWc05TPHYgaXjbPZO5/rJKSBslfG6BeMSs8GhwnqAKP56eEhvmzwnIz1QcC9cRVyO+IKqWNfmHFkCa1WJTULA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.15.tgz", + "integrity": "sha512-KkTg+AmDXz1IvA9S1gt8dE24C8Thx0X5oM0KGF322DuP+P3evwTL9YyusHAWNsh4qLsR80nvBr/EIYs29VSwuA==", "cpu": [ "x64" ], @@ -766,9 +800,9 @@ } }, "node_modules/esbuild-freebsd-arm64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.43.tgz", - "integrity": "sha512-amrYopclz3VohqisOPR6hA3GOWA3LZC1WDLnp21RhNmoERmJ/vLnOpnrG2P/Zao+/erKTCUqmrCIPVtj58DRoA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.15.tgz", + "integrity": "sha512-FUcML0DRsuyqCMfAC+HoeAqvWxMeq0qXvclZZ/lt2kLU6XBnDA5uKTLUd379WYEyVD4KKFctqWd9tTuk8C/96g==", "cpu": [ "arm64" ], @@ -782,9 +816,9 @@ } }, "node_modules/esbuild-linux-32": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.43.tgz", - "integrity": "sha512-KoxoEra+9O3AKVvgDFvDkiuddCds6q71owSQEYwjtqRV7RwbPzKxJa6+uyzUulHcyGVq0g15K0oKG5CFBcvYDw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.15.tgz", + "integrity": "sha512-q28Qn5pZgHNqug02aTkzw5sW9OklSo96b5nm17Mq0pDXrdTBcQ+M6Q9A1B+dalFeynunwh/pvfrNucjzwDXj+Q==", "cpu": [ "ia32" ], @@ -798,9 +832,9 @@ } }, "node_modules/esbuild-linux-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.43.tgz", - "integrity": "sha512-EwINwGMyiJMgBby5/SbMqKcUhS5AYAZ2CpEBzSowsJPNBJEdhkCTtEjk757TN/wxgbu3QklqDM6KghY660QCUw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.15.tgz", + "integrity": "sha512-217KPmWMirkf8liO+fj2qrPwbIbhNTGNVtvqI1TnOWJgcMjUWvd677Gq3fTzXEjilkx2yWypVnTswM2KbXgoAg==", "cpu": [ "x64" ], @@ -814,9 +848,9 @@ } }, "node_modules/esbuild-linux-arm": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.43.tgz", - "integrity": "sha512-e6YzQUoDxxtyamuF12eVzzRC7bbEFSZohJ6igQB9tBqnNmIQY3fI6Cns3z2wxtbZ3f2o6idkD2fQnlvs2902Dg==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.15.tgz", + "integrity": "sha512-RYVW9o2yN8yM7SB1yaWr378CwrjvGCyGybX3SdzPHpikUHkME2AP55Ma20uNwkNyY2eSYFX9D55kDrfQmQBR4w==", "cpu": [ "arm" ], @@ -830,9 +864,9 @@ } }, "node_modules/esbuild-linux-arm64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.43.tgz", - "integrity": "sha512-UlSpjMWllAc70zYbHxWuDS3FJytyuR/gHJYBr8BICcTNb/TSOYVBg6U7b3jZ3mILTrgzwJUHwhEwK18FZDouUQ==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.15.tgz", + "integrity": "sha512-/ltmNFs0FivZkYsTzAsXIfLQX38lFnwJTWCJts0IbCqWZQe+jjj0vYBNbI0kmXLb3y5NljiM5USVAO1NVkdh2g==", "cpu": [ "arm64" ], @@ -846,9 +880,9 @@ } }, "node_modules/esbuild-linux-mips64le": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.43.tgz", - "integrity": "sha512-f+v8cInPEL1/SDP//CfSYzcDNgE4CY3xgDV81DWm3KAPWzhvxARrKxB1Pstf5mB56yAslJDxu7ryBUPX207EZA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.15.tgz", + "integrity": "sha512-PksEPb321/28GFFxtvL33yVPfnMZihxkEv5zME2zapXGp7fA1X2jYeiTUK+9tJ/EGgcNWuwvtawPxJG7Mmn86A==", "cpu": [ "mips64el" ], @@ -862,9 +896,9 @@ } }, "node_modules/esbuild-linux-ppc64le": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.43.tgz", - "integrity": "sha512-5wZYMDGAL/K2pqkdIsW+I4IR41kyfHr/QshJcNpUfK3RjB3VQcPWOaZmc+74rm4ZjVirYrtz+jWw0SgxtxRanA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.15.tgz", + "integrity": "sha512-ek8gJBEIhcpGI327eAZigBOHl58QqrJrYYIZBWQCnH3UnXoeWMrMZLeeZL8BI2XMBhP+sQ6ERctD5X+ajL/AIA==", "cpu": [ "ppc64" ], @@ -878,9 +912,9 @@ } }, "node_modules/esbuild-linux-riscv64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.43.tgz", - "integrity": "sha512-lYcAOUxp85hC7lSjycJUVSmj4/9oEfSyXjb/ua9bNl8afonaduuqtw7hvKMoKuYnVwOCDw4RSfKpcnIRDWq+Bw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.15.tgz", + "integrity": "sha512-H5ilTZb33/GnUBrZMNJtBk7/OXzDHDXjIzoLXHSutwwsLxSNaLxzAaMoDGDd/keZoS+GDBqNVxdCkpuiRW4OSw==", "cpu": [ "riscv64" ], @@ -894,9 +928,9 @@ } }, "node_modules/esbuild-linux-s390x": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.43.tgz", - "integrity": "sha512-27e43ZhHvhFE4nM7HqtUbMRu37I/4eNSUbb8FGZWszV+uLzMIsHDwLoBiJmw7G9N+hrehNPeQ4F5Ujad0DrUKQ==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.15.tgz", + "integrity": "sha512-jKaLUg78mua3rrtrkpv4Or2dNTJU7bgHN4bEjT4OX4GR7nLBSA9dfJezQouTxMmIW7opwEC5/iR9mpC18utnxQ==", "cpu": [ "s390x" ], @@ -910,9 +944,9 @@ } }, "node_modules/esbuild-netbsd-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.43.tgz", - "integrity": "sha512-2mH4QF6hHBn5zzAfxEI/2eBC0mspVsZ6UVo821LpAJKMvLJPBk3XJO5xwg7paDqSqpl7p6IRrAenW999AEfJhQ==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.15.tgz", + "integrity": "sha512-aOvmF/UkjFuW6F36HbIlImJTTx45KUCHJndtKo+KdP8Dhq3mgLRKW9+6Ircpm8bX/RcS3zZMMmaBLkvGY06Gvw==", "cpu": [ "x64" ], @@ -926,9 +960,9 @@ } }, "node_modules/esbuild-openbsd-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.43.tgz", - "integrity": "sha512-ZhQpiZjvqCqO8jKdGp9+8k9E/EHSA+zIWOg+grwZasI9RoblqJ1QiZqqi7jfd6ZrrG1UFBNGe4m0NFxCFbMVbg==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.15.tgz", + "integrity": "sha512-HFFX+WYedx1w2yJ1VyR1Dfo8zyYGQZf1cA69bLdrHzu9svj6KH6ZLK0k3A1/LFPhcEY9idSOhsB2UyU0tHPxgQ==", "cpu": [ "x64" ], @@ -942,9 +976,9 @@ } }, "node_modules/esbuild-sunos-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.43.tgz", - "integrity": "sha512-DgxSi9DaHReL9gYuul2rrQCAapgnCJkh3LSHPKsY26zytYppG0HgkgVF80zjIlvEsUbGBP/GHQzBtrezj/Zq1Q==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.15.tgz", + "integrity": "sha512-jOPBudffG4HN8yJXcK9rib/ZTFoTA5pvIKbRrt3IKAGMq1EpBi4xoVoSRrq/0d4OgZLaQbmkHp8RO9eZIn5atA==", "cpu": [ "x64" ], @@ -958,9 +992,9 @@ } }, "node_modules/esbuild-windows-32": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.43.tgz", - "integrity": "sha512-Ih3+2O5oExiqm0mY6YYE5dR0o8+AspccQ3vIAtRodwFvhuyGLjb0Hbmzun/F3Lw19nuhPMu3sW2fqIJ5xBxByw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.15.tgz", + "integrity": "sha512-MDkJ3QkjnCetKF0fKxCyYNBnOq6dmidcwstBVeMtXSgGYTy8XSwBeIE4+HuKiSsG6I/mXEb++px3IGSmTN0XiA==", "cpu": [ "ia32" ], @@ -974,9 +1008,9 @@ } }, "node_modules/esbuild-windows-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.43.tgz", - "integrity": "sha512-8NsuNfI8xwFuJbrCuI+aBqNTYkrWErejFO5aYM+yHqyHuL8mmepLS9EPzAzk8rvfaJrhN0+RvKWAcymViHOKEw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.15.tgz", + "integrity": "sha512-xaAUIB2qllE888SsMU3j9nrqyLbkqqkpQyWVkfwSil6BBPgcPk3zOFitTTncEKCLTQy3XV9RuH7PDj3aJDljWA==", "cpu": [ "x64" ], @@ -990,9 +1024,9 @@ } }, "node_modules/esbuild-windows-arm64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.43.tgz", - "integrity": "sha512-7ZlD7bo++kVRblJEoG+cepljkfP8bfuTPz5fIXzptwnPaFwGS6ahvfoYzY7WCf5v/1nX2X02HDraVItTgbHnKw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.15.tgz", + "integrity": "sha512-ttuoCYCIJAFx4UUKKWYnFdrVpoXa3+3WWkXVI6s09U+YjhnyM5h96ewTq/WgQj9LFSIlABQvadHSOQyAVjW5xQ==", "cpu": [ "arm64" ], @@ -2637,6 +2671,20 @@ "sharp": "^0.30.2" } }, + "@esbuild/android-arm": { + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.15.tgz", + "integrity": "sha512-JJjZjJi2eBL01QJuWjfCdZxcIgot+VoK6Fq7eKF9w4YHm9hwl7nhBR1o2Wnt/WcANk5l9SkpvrldW1PLuXxcbw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.15.tgz", + "integrity": "sha512-lhz6UNPMDXUhtXSulw8XlFAtSYO26WmHQnCi2Lg2p+/TMiJKNLtZCYUxV4wG6rZMzXmr8InGpNwk+DLT2Hm0PA==", + "dev": true, + "optional": true + }, "@mapbox/geojson-rewind": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.1.tgz", @@ -3112,170 +3160,172 @@ } }, "esbuild": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.43.tgz", - "integrity": "sha512-Uf94+kQmy/5jsFwKWiQB4hfo/RkM9Dh7b79p8yqd1tshULdr25G2szLz631NoH3s2ujnKEKVD16RmOxvCNKRFA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.15.tgz", + "integrity": "sha512-TEw/lwK4Zzld9x3FedV6jy8onOUHqcEX3ADFk4k+gzPUwrxn8nWV62tH0udo8jOtjFodlEfc4ypsqX3e+WWO6w==", "dev": true, "requires": { - "esbuild-android-64": "0.14.43", - "esbuild-android-arm64": "0.14.43", - "esbuild-darwin-64": "0.14.43", - "esbuild-darwin-arm64": "0.14.43", - "esbuild-freebsd-64": "0.14.43", - "esbuild-freebsd-arm64": "0.14.43", - "esbuild-linux-32": "0.14.43", - "esbuild-linux-64": "0.14.43", - "esbuild-linux-arm": "0.14.43", - "esbuild-linux-arm64": "0.14.43", - "esbuild-linux-mips64le": "0.14.43", - "esbuild-linux-ppc64le": "0.14.43", - "esbuild-linux-riscv64": "0.14.43", - "esbuild-linux-s390x": "0.14.43", - "esbuild-netbsd-64": "0.14.43", - "esbuild-openbsd-64": "0.14.43", - "esbuild-sunos-64": "0.14.43", - "esbuild-windows-32": "0.14.43", - "esbuild-windows-64": "0.14.43", - "esbuild-windows-arm64": "0.14.43" + "@esbuild/android-arm": "0.15.15", + "@esbuild/linux-loong64": "0.15.15", + "esbuild-android-64": "0.15.15", + "esbuild-android-arm64": "0.15.15", + "esbuild-darwin-64": "0.15.15", + "esbuild-darwin-arm64": "0.15.15", + "esbuild-freebsd-64": "0.15.15", + "esbuild-freebsd-arm64": "0.15.15", + "esbuild-linux-32": "0.15.15", + "esbuild-linux-64": "0.15.15", + "esbuild-linux-arm": "0.15.15", + "esbuild-linux-arm64": "0.15.15", + "esbuild-linux-mips64le": "0.15.15", + "esbuild-linux-ppc64le": "0.15.15", + "esbuild-linux-riscv64": "0.15.15", + "esbuild-linux-s390x": "0.15.15", + "esbuild-netbsd-64": "0.15.15", + "esbuild-openbsd-64": "0.15.15", + "esbuild-sunos-64": "0.15.15", + "esbuild-windows-32": "0.15.15", + "esbuild-windows-64": "0.15.15", + "esbuild-windows-arm64": "0.15.15" } }, "esbuild-android-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.43.tgz", - "integrity": "sha512-kqFXAS72K6cNrB6RiM7YJ5lNvmWRDSlpi7ZuRZ1hu1S3w0zlwcoCxWAyM23LQUyZSs1PbjHgdbbfYAN8IGh6xg==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.15.tgz", + "integrity": "sha512-F+WjjQxO+JQOva3tJWNdVjouFMLK6R6i5gjDvgUthLYJnIZJsp1HlF523k73hELY20WPyEO8xcz7aaYBVkeg5Q==", "dev": true, "optional": true }, "esbuild-android-arm64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.43.tgz", - "integrity": "sha512-bKS2BBFh+7XZY9rpjiHGRNA7LvWYbZWP87pLehggTG7tTaCDvj8qQGOU/OZSjCSKDYbgY7Q+oDw8RlYQ2Jt2BA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.15.tgz", + "integrity": "sha512-attlyhD6Y22jNyQ0fIIQ7mnPvDWKw7k6FKnsXlBvQE6s3z6s6cuEHcSgoirquQc7TmZgVCK5fD/2uxmRN+ZpcQ==", "dev": true, "optional": true }, "esbuild-darwin-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.43.tgz", - "integrity": "sha512-/3PSilx011ttoieRGkSZ0XV8zjBf2C9enV4ScMMbCT4dpx0mFhMOpFnCHkOK0pWGB8LklykFyHrWk2z6DENVUg==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.15.tgz", + "integrity": "sha512-ohZtF8W1SHJ4JWldsPVdk8st0r9ExbAOSrBOh5L+Mq47i696GVwv1ab/KlmbUoikSTNoXEhDzVpxUR/WIO19FQ==", "dev": true, "optional": true }, "esbuild-darwin-arm64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.43.tgz", - "integrity": "sha512-1HyFUKs8DMCBOvw1Qxpr5Vv/ThNcVIFb5xgXWK3pyT40WPvgYIiRTwJCvNs4l8i5qWF8/CK5bQxJVDjQvtv0Yw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.15.tgz", + "integrity": "sha512-P8jOZ5zshCNIuGn+9KehKs/cq5uIniC+BeCykvdVhx/rBXSxmtj3CUIKZz4sDCuESMbitK54drf/2QX9QHG5Ag==", "dev": true, "optional": true }, "esbuild-freebsd-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.43.tgz", - "integrity": "sha512-FNWc05TPHYgaXjbPZO5/rJKSBslfG6BeMSs8GhwnqAKP56eEhvmzwnIz1QcC9cRVyO+IKqWNfmHFkCa1WJTULA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.15.tgz", + "integrity": "sha512-KkTg+AmDXz1IvA9S1gt8dE24C8Thx0X5oM0KGF322DuP+P3evwTL9YyusHAWNsh4qLsR80nvBr/EIYs29VSwuA==", "dev": true, "optional": true }, "esbuild-freebsd-arm64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.43.tgz", - "integrity": "sha512-amrYopclz3VohqisOPR6hA3GOWA3LZC1WDLnp21RhNmoERmJ/vLnOpnrG2P/Zao+/erKTCUqmrCIPVtj58DRoA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.15.tgz", + "integrity": "sha512-FUcML0DRsuyqCMfAC+HoeAqvWxMeq0qXvclZZ/lt2kLU6XBnDA5uKTLUd379WYEyVD4KKFctqWd9tTuk8C/96g==", "dev": true, "optional": true }, "esbuild-linux-32": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.43.tgz", - "integrity": "sha512-KoxoEra+9O3AKVvgDFvDkiuddCds6q71owSQEYwjtqRV7RwbPzKxJa6+uyzUulHcyGVq0g15K0oKG5CFBcvYDw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.15.tgz", + "integrity": "sha512-q28Qn5pZgHNqug02aTkzw5sW9OklSo96b5nm17Mq0pDXrdTBcQ+M6Q9A1B+dalFeynunwh/pvfrNucjzwDXj+Q==", "dev": true, "optional": true }, "esbuild-linux-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.43.tgz", - "integrity": "sha512-EwINwGMyiJMgBby5/SbMqKcUhS5AYAZ2CpEBzSowsJPNBJEdhkCTtEjk757TN/wxgbu3QklqDM6KghY660QCUw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.15.tgz", + "integrity": "sha512-217KPmWMirkf8liO+fj2qrPwbIbhNTGNVtvqI1TnOWJgcMjUWvd677Gq3fTzXEjilkx2yWypVnTswM2KbXgoAg==", "dev": true, "optional": true }, "esbuild-linux-arm": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.43.tgz", - "integrity": "sha512-e6YzQUoDxxtyamuF12eVzzRC7bbEFSZohJ6igQB9tBqnNmIQY3fI6Cns3z2wxtbZ3f2o6idkD2fQnlvs2902Dg==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.15.tgz", + "integrity": "sha512-RYVW9o2yN8yM7SB1yaWr378CwrjvGCyGybX3SdzPHpikUHkME2AP55Ma20uNwkNyY2eSYFX9D55kDrfQmQBR4w==", "dev": true, "optional": true }, "esbuild-linux-arm64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.43.tgz", - "integrity": "sha512-UlSpjMWllAc70zYbHxWuDS3FJytyuR/gHJYBr8BICcTNb/TSOYVBg6U7b3jZ3mILTrgzwJUHwhEwK18FZDouUQ==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.15.tgz", + "integrity": "sha512-/ltmNFs0FivZkYsTzAsXIfLQX38lFnwJTWCJts0IbCqWZQe+jjj0vYBNbI0kmXLb3y5NljiM5USVAO1NVkdh2g==", "dev": true, "optional": true }, "esbuild-linux-mips64le": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.43.tgz", - "integrity": "sha512-f+v8cInPEL1/SDP//CfSYzcDNgE4CY3xgDV81DWm3KAPWzhvxARrKxB1Pstf5mB56yAslJDxu7ryBUPX207EZA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.15.tgz", + "integrity": "sha512-PksEPb321/28GFFxtvL33yVPfnMZihxkEv5zME2zapXGp7fA1X2jYeiTUK+9tJ/EGgcNWuwvtawPxJG7Mmn86A==", "dev": true, "optional": true }, "esbuild-linux-ppc64le": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.43.tgz", - "integrity": "sha512-5wZYMDGAL/K2pqkdIsW+I4IR41kyfHr/QshJcNpUfK3RjB3VQcPWOaZmc+74rm4ZjVirYrtz+jWw0SgxtxRanA==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.15.tgz", + "integrity": "sha512-ek8gJBEIhcpGI327eAZigBOHl58QqrJrYYIZBWQCnH3UnXoeWMrMZLeeZL8BI2XMBhP+sQ6ERctD5X+ajL/AIA==", "dev": true, "optional": true }, "esbuild-linux-riscv64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.43.tgz", - "integrity": "sha512-lYcAOUxp85hC7lSjycJUVSmj4/9oEfSyXjb/ua9bNl8afonaduuqtw7hvKMoKuYnVwOCDw4RSfKpcnIRDWq+Bw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.15.tgz", + "integrity": "sha512-H5ilTZb33/GnUBrZMNJtBk7/OXzDHDXjIzoLXHSutwwsLxSNaLxzAaMoDGDd/keZoS+GDBqNVxdCkpuiRW4OSw==", "dev": true, "optional": true }, "esbuild-linux-s390x": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.43.tgz", - "integrity": "sha512-27e43ZhHvhFE4nM7HqtUbMRu37I/4eNSUbb8FGZWszV+uLzMIsHDwLoBiJmw7G9N+hrehNPeQ4F5Ujad0DrUKQ==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.15.tgz", + "integrity": "sha512-jKaLUg78mua3rrtrkpv4Or2dNTJU7bgHN4bEjT4OX4GR7nLBSA9dfJezQouTxMmIW7opwEC5/iR9mpC18utnxQ==", "dev": true, "optional": true }, "esbuild-netbsd-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.43.tgz", - "integrity": "sha512-2mH4QF6hHBn5zzAfxEI/2eBC0mspVsZ6UVo821LpAJKMvLJPBk3XJO5xwg7paDqSqpl7p6IRrAenW999AEfJhQ==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.15.tgz", + "integrity": "sha512-aOvmF/UkjFuW6F36HbIlImJTTx45KUCHJndtKo+KdP8Dhq3mgLRKW9+6Ircpm8bX/RcS3zZMMmaBLkvGY06Gvw==", "dev": true, "optional": true }, "esbuild-openbsd-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.43.tgz", - "integrity": "sha512-ZhQpiZjvqCqO8jKdGp9+8k9E/EHSA+zIWOg+grwZasI9RoblqJ1QiZqqi7jfd6ZrrG1UFBNGe4m0NFxCFbMVbg==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.15.tgz", + "integrity": "sha512-HFFX+WYedx1w2yJ1VyR1Dfo8zyYGQZf1cA69bLdrHzu9svj6KH6ZLK0k3A1/LFPhcEY9idSOhsB2UyU0tHPxgQ==", "dev": true, "optional": true }, "esbuild-sunos-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.43.tgz", - "integrity": "sha512-DgxSi9DaHReL9gYuul2rrQCAapgnCJkh3LSHPKsY26zytYppG0HgkgVF80zjIlvEsUbGBP/GHQzBtrezj/Zq1Q==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.15.tgz", + "integrity": "sha512-jOPBudffG4HN8yJXcK9rib/ZTFoTA5pvIKbRrt3IKAGMq1EpBi4xoVoSRrq/0d4OgZLaQbmkHp8RO9eZIn5atA==", "dev": true, "optional": true }, "esbuild-windows-32": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.43.tgz", - "integrity": "sha512-Ih3+2O5oExiqm0mY6YYE5dR0o8+AspccQ3vIAtRodwFvhuyGLjb0Hbmzun/F3Lw19nuhPMu3sW2fqIJ5xBxByw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.15.tgz", + "integrity": "sha512-MDkJ3QkjnCetKF0fKxCyYNBnOq6dmidcwstBVeMtXSgGYTy8XSwBeIE4+HuKiSsG6I/mXEb++px3IGSmTN0XiA==", "dev": true, "optional": true }, "esbuild-windows-64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.43.tgz", - "integrity": "sha512-8NsuNfI8xwFuJbrCuI+aBqNTYkrWErejFO5aYM+yHqyHuL8mmepLS9EPzAzk8rvfaJrhN0+RvKWAcymViHOKEw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.15.tgz", + "integrity": "sha512-xaAUIB2qllE888SsMU3j9nrqyLbkqqkpQyWVkfwSil6BBPgcPk3zOFitTTncEKCLTQy3XV9RuH7PDj3aJDljWA==", "dev": true, "optional": true }, "esbuild-windows-arm64": { - "version": "0.14.43", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.43.tgz", - "integrity": "sha512-7ZlD7bo++kVRblJEoG+cepljkfP8bfuTPz5fIXzptwnPaFwGS6ahvfoYzY7WCf5v/1nX2X02HDraVItTgbHnKw==", + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.15.tgz", + "integrity": "sha512-ttuoCYCIJAFx4UUKKWYnFdrVpoXa3+3WWkXVI6s09U+YjhnyM5h96ewTq/WgQj9LFSIlABQvadHSOQyAVjW5xQ==", "dev": true, "optional": true }, diff --git a/package.json b/package.json index bb5509c33..3cef78d08 100644 --- a/package.json +++ b/package.json @@ -30,10 +30,10 @@ }, "devDependencies": { "create-serve": "^1.0.1", - "esbuild": "^0.14.43", + "esbuild": "^0.15.15", "maplibre-gl": "^2.1.9", - "openmapsamples-maplibre": "github:adamfranco/OpenMapSamples-MapLibre", "openmapsamples": "github:adamfranco/OpenMapSamples", + "openmapsamples-maplibre": "github:adamfranco/OpenMapSamples-MapLibre", "prettier": "2.3.2", "shx": "^0.3.4", "svgo": "^2.8.0" From a28ff44b396f50ab96995c1978e09caba7eac65d Mon Sep 17 00:00:00 2001 From: Josh Lee Date: Sun, 27 Nov 2022 08:21:05 -0500 Subject: [PATCH 030/514] Tweak build verbosity - Make code_format shut up - prettier can print names of changed files - svgo can only print all or nothing, unfortunately - Make sprites script report its output --- .prettierignore | 1 + package.json | 4 ++-- scripts/sprites.js | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.prettierignore b/.prettierignore index a87633d58..500cd1776 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,3 +2,4 @@ build sprites js/maplibre-gl.js dist +.pnp.* diff --git a/package.json b/package.json index bb5509c33..0faa0438c 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ "scripts": { "config": "shx cp src/configs/config.maptiler.js src/config.js", "code_format": "run-s code_format:prettier code_format:svgo", - "code_format:prettier": "prettier --write .", - "code_format:svgo": "svgo -f icons/", + "code_format:prettier": "prettier --write --list-different .", + "code_format:svgo": "svgo -q -f icons/", "clean": "shx rm -rf dist", "presprites": "shx rm -rf dist/sprites", "sprites": "node scripts/sprites.js", diff --git a/scripts/sprites.js b/scripts/sprites.js index e311994bf..f26e7b987 100644 --- a/scripts/sprites.js +++ b/scripts/sprites.js @@ -19,6 +19,8 @@ const sprites = await Promise.all( }) ); +console.log(`Building ${sprites.length} sprites`); + const generated = await Sprites.generate(sprites, [1, 2]); for (const result of generated) { @@ -28,4 +30,6 @@ for (const result of generated) { await fs.writeFile(outputPng, result.buffer); await fs.writeFile(outputJson, JSON.stringify(result.layout, null, 2)); + const kb = (result.buffer.length / 1024).toFixed(1); + console.log(`Wrote ${kb}KiB to ${outputPng}`); } From 437cb16f213283af9ddcb9c465c526a0767d42fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sun, 27 Nov 2022 09:13:13 -0800 Subject: [PATCH 031/514] 49-Mile Scenic Drive shield (#576) * Added 49-Mile Scenic Drive shield * Recolored 49-Mile Scenic Drive border to blue Also removed a stray curve. --- icons/shield_us_ca_sf_49.svg | 74 ++++++++++++++++++++++++++++++++++++ src/js/shield_defs.js | 4 ++ 2 files changed, 78 insertions(+) create mode 100644 icons/shield_us_ca_sf_49.svg diff --git a/icons/shield_us_ca_sf_49.svg b/icons/shield_us_ca_sf_49.svg new file mode 100644 index 000000000..5642bfdc9 --- /dev/null +++ b/icons/shield_us_ca_sf_49.svg @@ -0,0 +1,74 @@ + + + + + + + + diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 42bdc434b..ae1bdad96 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -1097,6 +1097,10 @@ export function loadShields(shieldImages) { Color.shields.green, Color.shields.white ); + shields["US:CA:San_Francisco:49_Mile_Scenic_Drive"] = { + backgroundImage: shieldImages.shield_us_ca_sf_49, + notext: true, + }; // Colorado shields["US:CO"] = { From 5c9bd114368b075daa7e651f9d2eccf4a2cfc2ba Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 27 Nov 2022 13:45:42 -0500 Subject: [PATCH 032/514] Switch font stack to OHM --- src/americana.js | 3 ++- src/layer/aeroway.js | 10 +++++----- src/layer/highway_exit.js | 2 +- src/layer/highway_shield.js | 2 +- src/layer/park.js | 4 ++-- src/layer/place.js | 18 +++++++++--------- src/layer/transportation_label.js | 4 ++-- src/layer/water.js | 4 ++-- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/americana.js b/src/americana.js index 43ccc4a74..34ff08869 100644 --- a/src/americana.js +++ b/src/americana.js @@ -282,7 +282,8 @@ function buildStyle() { return { id: "streets", name: "Americana", - glyphs: "https://fonts.openmaptiles.org/{fontstack}/{range}.pbf", + glyphs: + "https://openhistoricalmap.github.io/map-styles/fonts/{fontstack}/{range}.pbf", layers: buildLayers(), sources: { openmaptiles: { diff --git a/src/layer/aeroway.js b/src/layer/aeroway.js index 9b81edcc3..b816a411b 100644 --- a/src/layer/aeroway.js +++ b/src/layer/aeroway.js @@ -169,7 +169,7 @@ export const airportRefLabel = { layout: { visibility: "visible", "text-field": ["coalesce", ["get", "iata"], ["get", "icao"]], - "text-font": ["Metropolis Bold"], + "text-font": ["OpenHistorical Bold"], "text-size": 10, ...iconLayout, }, @@ -193,7 +193,7 @@ export const minorAirportRefLabel = { layout: { visibility: "visible", "text-field": ["coalesce", ["get", "iata"], ["get", "icao"]], - "text-font": ["Metropolis Bold"], + "text-font": ["OpenHistorical Bold"], "text-size": 10, }, source: "openmaptiles", @@ -216,7 +216,7 @@ export const airportLabel = { layout: { visibility: "visible", "text-field": Label.localizedName, - "text-font": ["Metropolis Bold"], + "text-font": ["OpenHistorical Bold"], "text-size": 10, ...iconLayout, }, @@ -242,7 +242,7 @@ export const minorAirportLabel = { layout: { visibility: "visible", "text-field": Label.localizedName, - "text-font": ["Metropolis Bold"], + "text-font": ["OpenHistorical Bold"], "text-size": 10, }, source: "openmaptiles", @@ -266,7 +266,7 @@ export const airportGate = { layout: { visibility: "visible", "text-field": "{ref}", - "text-font": ["Metropolis Bold"], + "text-font": ["OpenHistorical Bold"], "text-size": 10, }, source: "openmaptiles", diff --git a/src/layer/highway_exit.js b/src/layer/highway_exit.js index fe444e0c1..8355c7ab7 100644 --- a/src/layer/highway_exit.js +++ b/src/layer/highway_exit.js @@ -11,7 +11,7 @@ export const exits = { minzoom: 14, layout: { "text-field": ["get", "ref"], - "text-font": ["Metropolis Black"], + "text-font": ["OpenHistorical Bold"], "text-size": 9, "text-line-height": 1, }, diff --git a/src/layer/highway_shield.js b/src/layer/highway_shield.js index 5dea6de7a..362a666cd 100644 --- a/src/layer/highway_shield.js +++ b/src/layer/highway_shield.js @@ -35,7 +35,7 @@ for (var i = 1; i <= 6; i++) { let shieldLayout = { "text-rotation-alignment": "viewport-glyph", - "text-font": ["Metropolis Light"], + "text-font": ["OpenHistorical"], "text-field": shieldTextField, "text-anchor": "center", "text-letter-spacing": 0.7, diff --git a/src/layer/park.js b/src/layer/park.js index 69968619e..f17e3744a 100644 --- a/src/layer/park.js +++ b/src/layer/park.js @@ -44,7 +44,7 @@ export const label = { layout: { visibility: "visible", "text-field": Label.localizedName, - "text-font": ["Metropolis Bold"], + "text-font": ["OpenHistorical Bold"], "text-size": 10, "symbol-sort-key": ["get", "rank"], }, @@ -98,7 +98,7 @@ export const parkLabel = { layout: { visibility: "visible", "text-field": Label.localizedName, - "text-font": ["Metropolis Bold"], + "text-font": ["OpenHistorical Bold"], "text-size": 10, "symbol-sort-key": ["get", "rank"], }, diff --git a/src/layer/place.js b/src/layer/place.js index 601c7b80a..c0058e5fa 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -41,7 +41,7 @@ export const village = { ], ], layout: { - "text-font": ["Metropolis Bold"], + "text-font": ["OpenHistorical Bold"], "text-size": { base: 1.0, stops: [ @@ -107,7 +107,7 @@ export const town = { ], ], layout: { - "text-font": ["Metropolis Bold"], + "text-font": ["OpenHistorical Bold"], "text-size": { base: 1.2, stops: [ @@ -169,7 +169,7 @@ export const city = { ], ], layout: { - "text-font": ["Metropolis Bold"], + "text-font": ["OpenHistorical Bold"], "text-size": { base: 1.2, stops: [ @@ -224,7 +224,7 @@ export const state = { }, filter: ["==", ["get", "class"], "state"], layout: { - "text-font": ["Metropolis Regular"], + "text-font": ["OpenHistorical"], "text-size": { base: 1.2, stops: [ @@ -271,7 +271,7 @@ export const countryOther = { ["!", ["has", "iso_a2"]], ], layout: { - "text-font": ["Metropolis Regular"], + "text-font": ["OpenHistorical"], "text-size": { stops: [ [3, 9], @@ -304,7 +304,7 @@ export const country3 = { ["has", "iso_a2"], ], layout: { - "text-font": ["Metropolis Regular"], + "text-font": ["OpenHistorical"], "text-size": { stops: [ [3, 11], @@ -337,7 +337,7 @@ export const country2 = { ["has", "iso_a2"], ], layout: { - "text-font": ["Metropolis Regular"], + "text-font": ["OpenHistorical"], "text-size": { stops: [ [2, 11], @@ -370,7 +370,7 @@ export const country1 = { ["has", "iso_a2"], ], layout: { - "text-font": ["Metropolis Regular"], + "text-font": ["OpenHistorical"], "text-size": { stops: [ [1, 11], @@ -405,7 +405,7 @@ export const continent = { }, filter: ["==", ["get", "class"], "continent"], layout: { - "text-font": ["Metropolis Light"], + "text-font": ["OpenHistorical"], "text-size": 13, "text-field": Label.localizedName, "text-justify": "center", diff --git a/src/layer/transportation_label.js b/src/layer/transportation_label.js index 2c8bb0dff..c671a557f 100644 --- a/src/layer/transportation_label.js +++ b/src/layer/transportation_label.js @@ -77,8 +77,8 @@ export const label = { "text-font": [ ...highwaySelector, "ferry", - ["literal", ["Metropolis Regular Italic"]], - ["literal", ["Metropolis Light"]], + ["literal", ["OpenHistorical Italic"]], + ["literal", ["OpenHistorical"]], ], "text-field": Label.localizedName, "text-max-angle": 20, diff --git a/src/layer/water.js b/src/layer/water.js index d62703817..f2127493d 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -79,7 +79,7 @@ const labelPaintProperties = { const labelLayoutProperties = { "symbol-placement": "line", "text-field": Label.localizedName, - "text-font": ["Metropolis Bold Italic"], + "text-font": ["OpenHistorical Italic"], "text-max-angle": 55, }; @@ -154,7 +154,7 @@ export const waterPointLabel = { filter: ["all", ["==", ["geometry-type"], "Point"]], layout: { "text-field": Label.localizedName, - "text-font": ["Metropolis Bold Italic"], + "text-font": ["OpenHistorical Italic"], "text-size": [ "interpolate", ["exponential", 2], From e38f7ce37413bcd536fb4ff50c2fb4dc7e86d1fb Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 27 Nov 2022 14:55:17 -0500 Subject: [PATCH 033/514] Update CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 57 ---------------------------------------------- 1 file changed, 57 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index d6429ddfa..528a944bf 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,60 +1,3 @@ # Code of Conduct The OpenStreetMap Americana project has [opted in](https://github.com/ZeLonewolf/openstreetmap-americana/pull/489) to the [OpenStreetMap US Code of Conduct](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct) (CoC) and [Process for Moderation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/ModerationProcess). - -This document outlines the standards for behavior in this project, and describes the procedure that users may follow if they experience unacceptable behavior. - -## Encouraged Behaviors - -- **Be welcoming**. OpenStreetMap US strives to be a community that welcomes and supports people of all backgrounds and identities. Some examples of behavior that contribute to creating a positive environment include: - - The use of welcoming and inclusive language; - - Respect for differing viewpoints and experiences; - - Empathy towards other community members. -- **Be considerate and patient**. Actively seek to acknowledge, respect, and understand fellow community members. Our community depends on the work of each other to maintain and strengthen the health and integrity of OpenStreetMap. Any decision you make may affect others, and those consequences should be taken into account. While critique is a natural and important part of our culture, good critiques are kind, respectful, clear, and constructive. -- **Assume good faith**. It is surprisingly easy to misunderstand each other, be it online or in person; particularly in such a culturally and linguistically diverse setting as OpenStreetMap US. Misunderstandings can easily arise when we are in a rush, or otherwise distracted. Please ask clarifying questions before assuming that a communication was inappropriate. -- **Be respectful**. Enthusiastic discussions are part of the lifeblood of a successful project but can also lead to disagreements. We should strive to keep our discussions and disagreements appropriate. Members of the OpenStreetMap US community should be respectful when dealing with others, within and outside of the global OpenStreetMap community. We note, however, that non-OpenStreetMap US spaces are not part of this Code of Conduct. -- **When we disagree, try to understand why**. Disagreements, both social and technical, happen easily and often. It is important that we seek to understand each other and work to resolve disagreements and differing views constructively. When someone contradicts your own perceptions, try to understand where the other person is coming from. Try to ask questions that will serve to clarify, rather than to escalate, an issue. - -## Untolerated Behaviors - -Examples of untolerated behaviors include, but are not limited to: - -- Violent threats or language directed against another person, including deliberate intimidation or harassment (online or in-person); -- Verbal, written, or physical abuse; -- Discrimination of any person or group of persons; -- Discriminatory jokes and language; -- Conduct or speech which might be considered sexist, racist, homophobic, transphobic, xenophobic, ableist or otherwise discriminatory or offensive in nature; -- The use of unwelcome, suggestive, derogatory or inappropriate nicknames or terms; -- Disrespect towards others (ex. personal insults, innuendo); -- Posting sexually explicit or violent material, or including this content in events such as conference presentations, talks, workshops, or parties; -- Posting (or threatening to post) other people's personally identifying information ("doxing"); -- Inappropriate attention or contact. Be aware of how your actions affect others. If it makes someone uncomfortable, stop. This includes: - - Continued unwelcomed one-on-one communication after a request to cease - - Unwelcome sexual attention - - Repeated harassment of others. In general, if someone asks you to stop, then stop. -- Sustained disruptions of community events and discussions; -- Advocating for, or encouraging, any of the above behavior - -## Process for Moderation - -We encourage users to [work out issues directly](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct#How_to_Handle_a_Complaint) with the people involved in lieu of a formal report. -If you believe someone has violated the CoC, we ask that you report it to OpenStreetMap US Code of Conduct Committee via the process outlined in the [Process for Moderation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/ModerationProcess). The CoC Committee will adjudicate reports submitted via this process, however, they will not actively monitor project communication channels. - -## Maintainer Responsibilities - -- The maintainer team agrees to abide by the CoC and to implement and be bound by moderation decisions made by the CoC Committee. This includes making a best effort to resolve disputes. -- Maintainers agree not to act in CoC cases in which the maintainer has a conflict of interest, such as in the case of a complaint made against the maintainer. - -## Applicability - -- This CoC applies to the OpenStreetMap Americana github repository. however, the CoC committee may consider a holistic examination of communications made via other channels when evaluating a complaint. -- This CoC remains in force until or unless revoked by the maintainer team, the OSM US CoC Committee, or OSM US. Should the CoC be revoked, it remains applicable for all behavior which occurred prior to revocation. -- The list of encouraged and untolerated behaviors are adopted as written at the time of the approval of this CoC. Should the standards of behavior in the OSM US CoC change, the maintainer team reserves the right to adopt the change, withdraw from the Process for Moderation, or neither. -- The OSM US CoC Committee may at any time change the procedures outlined in the Process for Moderation and/or the Statement of Scope and Limitations as documented in the Committee's [online documentation](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct). The project adopts these procedures as currently written, even if they differ from the procedures that were in force at the time of the project's adoption of the CoC. -- If the locations of the official online documentation of the OSM US CoC and Process for Moderation change, the presently applicable documentation applies, and updates to these links may be made as an administrative change. - -## License - -This code of conduct file (`CODE_OF_CONDUCT.md`) includes excerpts from the [OpenStreetMap-US Code of Conduct](https://wiki.openstreetmap.org/wiki/Foundation/Local_Chapters/United_States/Code_of_Conduct_Committee/OSM_US_Code_of_Conduct) and is provided under the [Creative Commons Attribution-ShareAlike 3.0](http://creativecommons.org/licenses/by-sa/3.0/) license. - -Credits for the sources and inspiration of this code of conduct go to the [Speak Up! Project](http://web.archive.org/web/20141109123859/http://speakup.io/coc.html), [HOT Code of Conduct](https://www.hotosm.org/code-of-conduct.html), [Django Code of Conduct](https://www.djangoproject.com/conduct/reporting/), [Geek Feminism Code of Conduct](https://geekfeminismdotorg.wordpress.com/about/code-of-conduct/), [Slack Code of Conduct](https://api.slack.com/docs/community-code-of-conduct) and [The Ada Initiative](http://adainitiative.org/2014/02/18/howto-design-a-code-of-conduct-for-your-community/) under a [Creative Commons Attribution-ShareAlike](http://creativecommons.org/licenses/by-sa/3.0/) license. From 5a10b6f1ae5f7295cf13ba367faec10fc4cc7c0d Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 27 Nov 2022 14:56:07 -0500 Subject: [PATCH 034/514] Update README.md --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index c8492a11a..3c13a1fc6 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,4 @@ Americana displays custom route shields for routes in all U.S. states and territ Countries -We are hoping that it will support more areas, you can [help us](CONTRIBUTING.md)! - -## License - -CC0 -Except where otherwise noted, to the extent possible under law, the contributors to OpenStreetMap Americana have waived all copyright and related or neighboring rights to OpenStreetMap Americana. +We are hoping that it will support more countries; you can [help us](https://github.com/ZeLonewolf/openstreetmap-americana/projects/1)! From 12072523c69f46d145645c3c4759a5b3ab9f00b2 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Sun, 27 Nov 2022 15:27:06 -0500 Subject: [PATCH 035/514] Replace trapezoid SVGs with math --- icons/shield_trapezoid_2.svg | 3 - icons/shield_trapezoid_3.svg | 3 - icons/shield_trapezoid_black_yellow_2.svg | 3 - icons/shield_trapezoid_black_yellow_3.svg | 3 - icons/shield_trapezoid_blue_2.svg | 3 - icons/shield_trapezoid_blue_3.svg | 3 - icons/shield_trapezoid_green_yellow_2.svg | 3 - icons/shield_trapezoid_rounded_brown_2.svg | 3 - icons/shield_trapezoid_rounded_brown_3.svg | 3 - icons/shield_us_ca_sf_49.svg | 79 +---------- src/js/shield_canvas_draw.js | 73 ++++++++++ src/js/shield_defs.js | 156 ++++++++++++--------- 12 files changed, 166 insertions(+), 169 deletions(-) delete mode 100644 icons/shield_trapezoid_2.svg delete mode 100644 icons/shield_trapezoid_3.svg delete mode 100644 icons/shield_trapezoid_black_yellow_2.svg delete mode 100644 icons/shield_trapezoid_black_yellow_3.svg delete mode 100644 icons/shield_trapezoid_blue_2.svg delete mode 100644 icons/shield_trapezoid_blue_3.svg delete mode 100644 icons/shield_trapezoid_green_yellow_2.svg delete mode 100644 icons/shield_trapezoid_rounded_brown_2.svg delete mode 100644 icons/shield_trapezoid_rounded_brown_3.svg diff --git a/icons/shield_trapezoid_2.svg b/icons/shield_trapezoid_2.svg deleted file mode 100644 index 107951c01..000000000 --- a/icons/shield_trapezoid_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_trapezoid_3.svg b/icons/shield_trapezoid_3.svg deleted file mode 100644 index 9afcc2bf3..000000000 --- a/icons/shield_trapezoid_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_trapezoid_black_yellow_2.svg b/icons/shield_trapezoid_black_yellow_2.svg deleted file mode 100644 index 6aed65e5c..000000000 --- a/icons/shield_trapezoid_black_yellow_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_trapezoid_black_yellow_3.svg b/icons/shield_trapezoid_black_yellow_3.svg deleted file mode 100644 index 4d699e5c3..000000000 --- a/icons/shield_trapezoid_black_yellow_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_trapezoid_blue_2.svg b/icons/shield_trapezoid_blue_2.svg deleted file mode 100644 index daa96e61e..000000000 --- a/icons/shield_trapezoid_blue_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_trapezoid_blue_3.svg b/icons/shield_trapezoid_blue_3.svg deleted file mode 100644 index b261ae316..000000000 --- a/icons/shield_trapezoid_blue_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_trapezoid_green_yellow_2.svg b/icons/shield_trapezoid_green_yellow_2.svg deleted file mode 100644 index 95a1d8e1f..000000000 --- a/icons/shield_trapezoid_green_yellow_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_trapezoid_rounded_brown_2.svg b/icons/shield_trapezoid_rounded_brown_2.svg deleted file mode 100644 index 4f27785ec..000000000 --- a/icons/shield_trapezoid_rounded_brown_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_trapezoid_rounded_brown_3.svg b/icons/shield_trapezoid_rounded_brown_3.svg deleted file mode 100644 index 07f9347fa..000000000 --- a/icons/shield_trapezoid_rounded_brown_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_us_ca_sf_49.svg b/icons/shield_us_ca_sf_49.svg index 5642bfdc9..d1c67685c 100644 --- a/icons/shield_us_ca_sf_49.svg +++ b/icons/shield_us_ca_sf_49.svg @@ -1,74 +1,7 @@ - - - - - - - + + + + + + diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index d401c6359..d2e9ed5b0 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -181,3 +181,76 @@ export function roundedRectangle( return ctx; } + +export function trapezoid( + angle, + fill, + outline, + ref, + radius, + outlineWidth, + rectWidth +) { + let angleInRadians = (angle * Math.PI) / 180; + let angleSign = Math.sign(angle); + let sine = Math.sin(angleInRadians); + let cosine = Math.cos(angleInRadians); + let tangent = Math.tan(angleInRadians); + + if (rectWidth == null) { + var shieldWidth = Math.ceil( + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + + (2 + (CS * sine) / 2) * PXR + ); + var width = Math.max( + minGenericShieldWidth, + Math.min(maxGenericShieldWidth, shieldWidth) + ); + } else { + var width = rectWidth * PXR; + } + + var ctx = Gfx.getGfxContext({ width: width, height: CS }); + + let lineThick = outlineWidth * PXR; + let lineWidth = lineThick / 2; + let drawRadius = radius * PXR; + + let x0 = lineWidth; + let x11 = width - lineWidth; + let y0 = angle > 0 ? lineWidth : CS - lineWidth; + let y3 = angle > 0 ? CS - lineWidth : lineWidth; + + let y1 = y0 + angleSign * drawRadius * (1 + sine); + let y2 = y3 - angleSign * drawRadius * (1 - sine); + + let x1 = x0 + (y1 - y0) * tangent; + let x2 = x1 + drawRadius * cosine; + let x3 = x0 + (y2 - y0) * tangent; + let x4 = x0 + (y3 - y0) * tangent; + let x5 = x3 + drawRadius * cosine; + // let x6 = width - x5; + let x7 = width - x4; + let x8 = width - x3; + let x9 = width - x2; + // let x10 = width - x1; + + ctx.beginPath(); + ctx.moveTo(x9, y0); + ctx.arcTo(x11, y0, x8, y2, drawRadius); + ctx.arcTo(x7, y3, x5, y3, drawRadius); + ctx.arcTo(x4, y3, x1, y1, drawRadius); + ctx.arcTo(x0, y0, x9, y0, drawRadius); + ctx.closePath(); + + ctx.lineWidth = lineThick; + ctx.fillStyle = fill; + ctx.fill(); + + if (outline != null) { + ctx.strokeStyle = outline; + ctx.stroke(); + } + + return ctx; +} diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index ae1bdad96..54f2ad284 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -90,6 +90,50 @@ function roundedRectShield( }; } +/** + * Draws a shield with a trapezoid background + * + * @param {*} angle - Angle (in degrees) at which sides deviate from vertical + * @param {*} fillColor - Color of rectangle background fill + * @param {*} strokeColor - Color of rectangle outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of rectangle (defaults to variable-width) + * @param {*} radius - Corner radius of rectangle (defaults to 2) + * @returns a shield definition object + */ +function trapezoidShield( + angle, + fillColor, + strokeColor, + textColor, + radius, + rectWidth +) { + textColor = textColor ?? strokeColor; + radius = radius ?? 0; + return { + backgroundDraw: (ref) => + ShieldDraw.trapezoid( + angle, + fillColor, + strokeColor, + ref, + radius, + 1, + rectWidth + ), + textLayoutConstraint: (spaceBounds, textBounds) => + ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius), + padding: { + left: 3, + right: 3, + top: 3 - Math.sign(angle), + bottom: 3 + Math.sign(angle), + }, + textColor: textColor, + }; +} + /** * Draws a shield with a pill-shaped background * @@ -239,63 +283,6 @@ export function loadShields(shieldImages) { textColor: Color.shields.white, }; - // Trapezoid shields - let trapezoidUpShield = { - backgroundImage: [ - shieldImages.shield_trapezoid_2, - shieldImages.shield_trapezoid_3, - ], - textColor: Color.shields.black, - padding: { - left: 4, - right: 4, - top: 2, - bottom: 5, - }, - }; - - let trapezoidUpShieldRoundedBrown = { - ...trapezoidUpShield, - backgroundImage: [ - shieldImages.shield_trapezoid_rounded_brown_2, - shieldImages.shield_trapezoid_rounded_brown_3, - ], - textColor: Color.shields.white, - }; - - let trapezoidUpShieldBlue = { - ...trapezoidUpShieldRoundedBrown, - backgroundImage: [ - shieldImages.shield_trapezoid_blue_2, - shieldImages.shield_trapezoid_blue_3, - ], - }; - - let trapezoidUpShieldBlackYellow = { - ...trapezoidUpShield, - backgroundImage: [ - shieldImages.shield_trapezoid_black_yellow_2, - shieldImages.shield_trapezoid_black_yellow_3, - ], - textColor: Color.shields.yellow, - }; - - let trapezoidUpShieldGreenYellow = { - ...trapezoidUpShieldBlackYellow, - backgroundImage: shieldImages.shield_trapezoid_green_yellow_2, - }; - - let trapezoidDownShield = { - ...trapezoidUpShield, - verticalReflect: true, - padding: { - left: 4, - right: 4, - top: 5, - bottom: 2, - }, - }; - // Pentagon shields let pentagonShield = { backgroundImage: shieldImages.shield_pent_3, @@ -651,20 +638,37 @@ export function loadShields(shieldImages) { pillShield(Color.shields.white, Color.shields.blue, Color.shields.black), ["ETR"] ); - shields["CA:ON:secondary"] = trapezoidDownShield; + shields["CA:ON:secondary"] = trapezoidShield( + -10, + Color.shields.white, + Color.shields.black + ); shields["CA:ON:tertiary"] = roundedRectShield( Color.shields.white, Color.shields.black ); - shields["CA:ON:Halton"] = trapezoidUpShieldGreenYellow; - shields["CA:ON:Peel"] = trapezoidUpShieldBlackYellow; - shields["CA:ON:Simcoe"] = { - ...trapezoidUpShield, - textColor: Color.shields.blue, - colorLighten: Color.shields.blue, - }; + shields["CA:ON:Halton"] = trapezoidShield( + 10, + Color.shields.green, + Color.shields.yellow + ); + shields["CA:ON:Peel"] = trapezoidShield( + 10, + Color.shields.black, + Color.shields.yellow + ); + shields["CA:ON:Simcoe"] = trapezoidShield( + 10, + Color.shields.white, + Color.shields.blue + ); ["Grey", "Hamilton", "Niagara"].forEach( - (county) => (shields[`CA:ON:${county}`] = trapezoidUpShieldBlue) + (county) => + (shields[`CA:ON:${county}`] = trapezoidShield( + 10, + Color.shields.blue, + Color.shields.white + )) ); [ "Brant", @@ -711,7 +715,11 @@ export function loadShields(shieldImages) { "York", ].forEach( (countyTownshipOrCity) => - (shields[`CA:ON:${countyTownshipOrCity}`] = trapezoidUpShield) + (shields[`CA:ON:${countyTownshipOrCity}`] = trapezoidShield( + 10, + Color.shields.white, + Color.shields.black + )) ); shields["CA:ON:Hastings:Wollaston"] = banneredShield( roundedRectShield(Color.shields.white, Color.shields.black), @@ -1570,7 +1578,13 @@ export function loadShields(shieldImages) { textColor: Color.shields.white, }, roundedRectShield(Color.shields.white, Color.shields.black), - trapezoidUpShieldRoundedBrown, + trapezoidShield( + 10, + Color.shields.brown, + Color.shields.white, + Color.shields.white, + 2 + ), ]) ); @@ -1736,7 +1750,11 @@ export function loadShields(shieldImages) { ); // Nebraska - shields["US:NE"] = trapezoidUpShield; + shields["US:NE"] = trapezoidShield( + 10, + Color.shields.white, + Color.shields.black + ); shields["US:NE:Business"] = banneredShield(shields["US:NE"], ["BUS"]); shields["US:NE:Link"] = banneredShield(shields["US:NE"], ["LINK"]); shields["US:NE:Rec"] = banneredShield(shields["US:NE"], ["REC"]); From 93707ad1ff96d1131fe8219de6db4d392a1af434 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 27 Nov 2022 15:47:42 -0500 Subject: [PATCH 036/514] Add bold italic labels --- src/layer/water.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layer/water.js b/src/layer/water.js index f2127493d..443327c63 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -154,7 +154,7 @@ export const waterPointLabel = { filter: ["all", ["==", ["geometry-type"], "Point"]], layout: { "text-field": Label.localizedName, - "text-font": ["OpenHistorical Italic"], + "text-font": ["Open Sans Bold Italic"], "text-size": [ "interpolate", ["exponential", 2], From 15de03b474db0354b1d8bbc4a5024c96e20d2117 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 27 Nov 2022 18:11:33 -0500 Subject: [PATCH 037/514] Fix CR/LF --- icons/shield_us_ca_sf_49.svg | 79 +++--------------------------------- 1 file changed, 6 insertions(+), 73 deletions(-) diff --git a/icons/shield_us_ca_sf_49.svg b/icons/shield_us_ca_sf_49.svg index 5642bfdc9..d1c67685c 100644 --- a/icons/shield_us_ca_sf_49.svg +++ b/icons/shield_us_ca_sf_49.svg @@ -1,74 +1,7 @@ - - - - - - - + + + + + + From 9f23f665bbe50cc0f088929fdfc1469a0035d979 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 27 Nov 2022 18:21:02 -0500 Subject: [PATCH 038/514] Add .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..176a458f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto From c9a5ca9f4319a7205b333bc2a7741c211e7f60bd Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 27 Nov 2022 18:29:19 -0500 Subject: [PATCH 039/514] Acknowledge OHM --- ACKNOWLEDGEMENTS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md index dca018039..0ebf945cb 100644 --- a/ACKNOWLEDGEMENTS.md +++ b/ACKNOWLEDGEMENTS.md @@ -2,3 +2,4 @@ - The stylized highway shields are inspired by "rebusurance" by Minh Nguyễn (https://github.com/1ec5/rebusurance) - Initial layer code is derived from Oliver Wipfli's openmaptiles-starter style: https://github.com/wipfli/openmaptiles-starter +- The font stack was developed by the OpenHistoricalMap project: https://github.com/OpenHistoricalMap/map-styles From e15bbabb7e7cd5c95f871d7cf754c118b1cc565b Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Sun, 27 Nov 2022 18:29:34 -0500 Subject: [PATCH 040/514] fix drawing of trapezoids with negative angles --- src/js/shield_canvas_draw.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index d2e9ed5b0..9cab424b4 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -193,7 +193,7 @@ export function trapezoid( ) { let angleInRadians = (angle * Math.PI) / 180; let angleSign = Math.sign(angle); - let sine = Math.sin(angleInRadians); + let sine = Math.sin(Math.abs(angleInRadians)); let cosine = Math.cos(angleInRadians); let tangent = Math.tan(angleInRadians); From 78cbbeaf02c76825f09cbfc3614e60248a212366 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 27 Nov 2022 18:31:30 -0500 Subject: [PATCH 041/514] Fix wording --- ACKNOWLEDGEMENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md index 0ebf945cb..31ae3b130 100644 --- a/ACKNOWLEDGEMENTS.md +++ b/ACKNOWLEDGEMENTS.md @@ -2,4 +2,4 @@ - The stylized highway shields are inspired by "rebusurance" by Minh Nguyễn (https://github.com/1ec5/rebusurance) - Initial layer code is derived from Oliver Wipfli's openmaptiles-starter style: https://github.com/wipfli/openmaptiles-starter -- The font stack was developed by the OpenHistoricalMap project: https://github.com/OpenHistoricalMap/map-styles +- The font stack was packaged by the OpenHistoricalMap project: https://github.com/OpenHistoricalMap/map-styles From 25142158d07b2964ccdf36c5b1f592971899d69e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sun, 27 Nov 2022 17:20:22 -0800 Subject: [PATCH 042/514] Lazily load mapbox-gl-rtl-text plugin --- src/americana.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/americana.js b/src/americana.js index 34ff08869..0a84c20b2 100644 --- a/src/americana.js +++ b/src/americana.js @@ -310,6 +310,12 @@ function upgradeLegacyHash() { } upgradeLegacyHash(); +maplibregl.setRTLTextPlugin( + "https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js", + null, + true +); + export const map = (window.map = new maplibregl.Map({ container: "map", // container id hash: "map", From 12b16ee944a4dc70c816fad745d117ffa3c41d50 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Mon, 28 Nov 2022 20:09:22 -0500 Subject: [PATCH 043/514] Draw home plate and vertical hexagon shields with math --- icons/shield_hex_vert_2.svg | 3 - icons/shield_hex_vert_green_3.svg | 3 - icons/shield_hex_vert_orange_3.svg | 3 - icons/shield_hex_vert_yellow_3.svg | 3 - icons/shield_home_2.svg | 3 - icons/shield_home_3.svg | 3 - icons/shield_home_blue_2.svg | 3 - icons/shield_home_blue_3.svg | 3 - icons/shield_home_blue_red_3.svg | 3 - icons/shield_home_green_yellow_2.svg | 3 - icons/shield_home_green_yellow_3.svg | 3 - icons/shield_home_yellow_2.svg | 3 - src/js/shield_canvas_draw.js | 138 ++++++++++++ src/js/shield_defs.js | 325 ++++++++++++++++++--------- src/shieldtest.js | 1 + 15 files changed, 359 insertions(+), 141 deletions(-) delete mode 100644 icons/shield_hex_vert_2.svg delete mode 100644 icons/shield_hex_vert_green_3.svg delete mode 100644 icons/shield_hex_vert_orange_3.svg delete mode 100644 icons/shield_hex_vert_yellow_3.svg delete mode 100644 icons/shield_home_2.svg delete mode 100644 icons/shield_home_3.svg delete mode 100644 icons/shield_home_blue_2.svg delete mode 100644 icons/shield_home_blue_3.svg delete mode 100644 icons/shield_home_blue_red_3.svg delete mode 100644 icons/shield_home_green_yellow_2.svg delete mode 100644 icons/shield_home_green_yellow_3.svg delete mode 100644 icons/shield_home_yellow_2.svg diff --git a/icons/shield_hex_vert_2.svg b/icons/shield_hex_vert_2.svg deleted file mode 100644 index 2a53c6a6e..000000000 --- a/icons/shield_hex_vert_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_hex_vert_green_3.svg b/icons/shield_hex_vert_green_3.svg deleted file mode 100644 index fe8d38848..000000000 --- a/icons/shield_hex_vert_green_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_hex_vert_orange_3.svg b/icons/shield_hex_vert_orange_3.svg deleted file mode 100644 index 34c9960d3..000000000 --- a/icons/shield_hex_vert_orange_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_hex_vert_yellow_3.svg b/icons/shield_hex_vert_yellow_3.svg deleted file mode 100644 index 67301a53e..000000000 --- a/icons/shield_hex_vert_yellow_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_home_2.svg b/icons/shield_home_2.svg deleted file mode 100644 index 6dfe09e44..000000000 --- a/icons/shield_home_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_home_3.svg b/icons/shield_home_3.svg deleted file mode 100644 index 249b122f5..000000000 --- a/icons/shield_home_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_home_blue_2.svg b/icons/shield_home_blue_2.svg deleted file mode 100644 index f350d2d98..000000000 --- a/icons/shield_home_blue_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_home_blue_3.svg b/icons/shield_home_blue_3.svg deleted file mode 100644 index e9a0e3644..000000000 --- a/icons/shield_home_blue_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_home_blue_red_3.svg b/icons/shield_home_blue_red_3.svg deleted file mode 100644 index d7583985e..000000000 --- a/icons/shield_home_blue_red_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_home_green_yellow_2.svg b/icons/shield_home_green_yellow_2.svg deleted file mode 100644 index 21d738230..000000000 --- a/icons/shield_home_green_yellow_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_home_green_yellow_3.svg b/icons/shield_home_green_yellow_3.svg deleted file mode 100644 index 29169e766..000000000 --- a/icons/shield_home_green_yellow_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_home_yellow_2.svg b/icons/shield_home_yellow_2.svg deleted file mode 100644 index af873b79c..000000000 --- a/icons/shield_home_yellow_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index 9cab424b4..51490621d 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -254,3 +254,141 @@ export function trapezoid( return ctx; } + +export function homePlate( + offset, + fill, + outline, + ref, + radius, + outlineWidth, + rectWidth +) { + if (rectWidth == null) { + var shieldWidth = Math.ceil( + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR + ); + var width = Math.max( + minGenericShieldWidth, + Math.min(maxGenericShieldWidth, shieldWidth) + ); + } else { + var width = rectWidth * PXR; + } + + var ctx = Gfx.getGfxContext({ width: width, height: CS }); + + let lineThick = outlineWidth * PXR; + let lineWidth = lineThick / 2; + let drawRadius = radius * PXR; + let drawOffset = Math.abs(offset) * PXR; + + let x0 = lineWidth; + let x4 = width - lineWidth; + let y0 = lineWidth; + let y4 = CS - lineWidth; + + let x1 = x0 + drawRadius; + let x2 = (x0 + x4) / 2; + let x3 = x4 - drawRadius; + let y1 = y0 + drawRadius; + let y3 = y4 - drawOffset; + + let drawOffsetTangent = + drawRadius * Math.tan(Math.PI / 4 - Math.asin(drawOffset / (x2 - x0)) / 2); + let y2 = y3 - drawOffsetTangent; + + if (offset < 0) { + y0 = CS - y0; + y1 = CS - y1; + y2 = CS - y2; + y3 = CS - y3; + y4 = CS - y4; + } + + ctx.beginPath(); + ctx.moveTo(x2, y4); + ctx.arcTo(x0, y3, x0, y2, drawRadius); + ctx.arcTo(x0, y0, x1, y0, drawRadius); + ctx.lineTo(x3, y0); + ctx.arcTo(x4, y0, x4, y2, drawRadius); + ctx.arcTo(x4, y3, x2, y4, drawRadius); + ctx.closePath(); + + ctx.lineWidth = lineThick; + ctx.fillStyle = fill; + ctx.fill(); + + if (outline != null) { + ctx.strokeStyle = outline; + ctx.stroke(); + } + + return ctx; +} + +export function hexagonVertical( + offset, + fill, + outline, + ref, + radius, + outlineWidth, + rectWidth +) { + if (rectWidth == null) { + var shieldWidth = Math.ceil( + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR + ); + var width = Math.max( + minGenericShieldWidth, + Math.min(maxGenericShieldWidth, shieldWidth) + ); + } else { + var width = rectWidth * PXR; + } + + var ctx = Gfx.getGfxContext({ width: width, height: CS }); + + let lineThick = outlineWidth * PXR; + let lineWidth = lineThick / 2; + let drawRadius = radius * PXR; + let drawOffset = offset * PXR; + + let x0 = lineWidth; + let x4 = width - lineWidth; + let y0 = lineWidth; + let y5 = CS - lineWidth; + + let x1 = x0 + drawRadius; + let x2 = (x0 + x4) / 2; + let x3 = x4 - drawRadius; + let y1 = y0 + drawOffset; + let y4 = y5 - drawOffset; + + let drawOffsetTangent = + drawRadius * Math.tan(Math.PI / 4 - Math.asin(drawOffset / (x2 - x0)) / 2); + let y2 = y1 + drawOffsetTangent; + let y3 = y4 - drawOffsetTangent; + + ctx.beginPath(); + ctx.moveTo(x2, y5); + ctx.arcTo(x0, y4, x0, y3, drawRadius); + ctx.arcTo(x0, y1, x2, y0, drawRadius); + ctx.lineTo(x2, y0); + ctx.arcTo(x4, y1, x4, y2, drawRadius); + ctx.arcTo(x4, y4, x2, y5, drawRadius); + ctx.lineTo(x2, y5); + ctx.closePath(); + + ctx.lineWidth = lineThick; + ctx.fillStyle = fill; + ctx.fill(); + + if (outline != null) { + ctx.strokeStyle = outline; + ctx.stroke(); + } + + return ctx; +} diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 54f2ad284..7021b0d1b 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -94,11 +94,11 @@ function roundedRectShield( * Draws a shield with a trapezoid background * * @param {*} angle - Angle (in degrees) at which sides deviate from vertical - * @param {*} fillColor - Color of rectangle background fill - * @param {*} strokeColor - Color of rectangle outline stroke + * @param {*} fillColor - Color of trapezoid background fill + * @param {*} strokeColor - Color of trapezoid outline stroke * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of rectangle (defaults to variable-width) - * @param {*} radius - Corner radius of rectangle (defaults to 2) + * @param {*} rectWidth - Width of trapezoid (defaults to variable-width) + * @param {*} radius - Corner radius of trapezoid (defaults to 2) * @returns a shield definition object */ function trapezoidShield( @@ -134,6 +134,94 @@ function trapezoidShield( }; } +/** + * Draws a shield with a home plate background + * + * @param {*} offset - Height of diagonal edges + * @param {*} fillColor - Color of home plate background fill + * @param {*} strokeColor - Color of home plate outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of home plate (defaults to variable-width) + * @param {*} radius - Corner radius of home plate (defaults to 2) + * @returns a shield definition object + */ +function homePlateShield( + offset, + fillColor, + strokeColor, + textColor, + radius, + rectWidth +) { + textColor = textColor ?? strokeColor; + radius = radius ?? 2; + return { + backgroundDraw: (ref) => + ShieldDraw.homePlate( + offset, + fillColor, + strokeColor, + ref, + radius, + 1, + rectWidth + ), + textLayoutConstraint: (spaceBounds, textBounds) => + ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius), + padding: { + left: 2, + right: 2, + top: 2 + (offset < 0 ? -offset - 1 : 0), + bottom: 2 + (offset > 0 ? offset - 1 : 0), + }, + textColor: textColor, + }; +} + +/** + * Draws a shield with a vertically-aligned hexagon background + * + * @param {*} offset - Height of diagonal edges + * @param {*} fillColor - Color of hexagon background fill + * @param {*} strokeColor - Color of hexagon outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of hexagon (defaults to variable-width) + * @param {*} radius - Corner radius of hexagon (defaults to 2) + * @returns a shield definition object + */ +function hexagonVerticalShield( + offset, + fillColor, + strokeColor, + textColor, + radius, + rectWidth +) { + textColor = textColor ?? strokeColor; + radius = radius ?? 2; + return { + backgroundDraw: (ref) => + ShieldDraw.hexagonVertical( + offset, + fillColor, + strokeColor, + ref, + radius, + 1, + rectWidth + ), + textLayoutConstraint: (spaceBounds, textBounds) => + ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius), + padding: { + left: 2, + right: 2, + top: 1 + offset, + bottom: 1 + offset, + }, + textColor: textColor, + }; +} + /** * Draws a shield with a pill-shaped background * @@ -327,74 +415,7 @@ export function loadShields(shieldImages) { ], }; - // Home plate shields - let homeDownShield = { - backgroundImage: [shieldImages.shield_home_2, shieldImages.shield_home_3], - textColor: Color.shields.black, - padding: { - left: 2, - right: 2, - top: 2, - bottom: 6, - }, - }; - - let homeDownShieldYellow = { - ...homeDownShield, - backgroundImage: shieldImages.shield_home_yellow_2, - }; - - let homeDownShieldBlue = { - ...homeDownShield, - backgroundImage: [ - shieldImages.shield_home_blue_2, - shieldImages.shield_home_blue_3, - ], - textColor: Color.shields.white, - }; - - let homeDownShieldBlueRed = { - ...homeDownShieldBlue, - backgroundImage: shieldImages.shield_home_blue_red_3, - }; - - let homeDownShieldGreenYellow = { - ...homeDownShield, - backgroundImage: [ - shieldImages.shield_home_green_yellow_2, - shieldImages.shield_home_green_yellow_3, - ], - textColor: Color.shields.yellow, - }; - // Hexagon shields - let hexagonVerticalShield = { - backgroundImage: shieldImages.shield_hex_vert_2, - textColor: Color.shields.black, - padding: { - left: 2, - right: 2, - top: 4.5, - bottom: 4.5, - }, - }; - - let hexagonVerticalShieldYellow = { - ...hexagonVerticalShield, - backgroundImage: shieldImages.shield_hex_vert_yellow_3, - }; - - let hexagonVerticalShieldOrange = { - ...hexagonVerticalShield, - backgroundImage: shieldImages.shield_hex_vert_orange_3, - }; - - let hexagonVerticalShieldGreen = { - ...hexagonVerticalShield, - backgroundImage: shieldImages.shield_hex_vert_green_3, - textColor: Color.shields.white, - }; - let hexagonHorizontalShieldBlue = { backgroundImage: [ shieldImages.shield_hex_horz_blue_2, @@ -530,7 +551,11 @@ export function loadShields(shieldImages) { }; // Alberta - shields["CA:AB:primary"] = homeDownShield; + shields["CA:AB:primary"] = homePlateShield( + 5, + Color.shields.white, + Color.shields.black + ); shields["CA:AB:secondary"] = ovalShield( Color.shields.white, Color.shields.black, @@ -551,7 +576,11 @@ export function loadShields(shieldImages) { }; // Manitoba - shields["CA:MB:PTH"] = homeDownShield; + shields["CA:MB:PTH"] = homePlateShield( + 5, + Color.shields.white, + Color.shields.black + ); shields["CA:MB:PR"] = ovalShield( Color.shields.black, Color.shields.white, @@ -789,7 +818,11 @@ export function loadShields(shieldImages) { }; // Saskatchewan - shields["CA:SK:primary"] = homeDownShieldBlue; + shields["CA:SK:primary"] = homePlateShield( + 5, + Color.shields.blue, + Color.shields.white + ); shields["CA:SK:secondary"] = { backgroundImage: shieldImages.shield_ca_sk_secondary, textColor: Color.shields.green, @@ -800,11 +833,11 @@ export function loadShields(shieldImages) { bottom: 2, }, }; - shields["CA:SK:tertiary"] = { - ...homeDownShield, - textColor: Color.shields.blue, - colorLighten: Color.shields.blue, - }; + shields["CA:SK:tertiary"] = homePlateShield( + 5, + Color.shields.white, + Color.shields.blue + ); // Yukon shields["CA:YT"] = roundedRectShield(Color.shields.white, Color.shields.red); @@ -2395,7 +2428,12 @@ export function loadShields(shieldImages) { shields["US:TX:CTRMA:Express"] = banneredShield(shields["US:TX:CTRMA"], [ "EXPR", ]); - shields["US:TX:Montgomery:MCTRA"] = homeDownShieldBlueRed; + shields["US:TX:Montgomery:MCTRA"] = homePlateShield( + 5, + Color.shields.blue, + Color.shields.red, + Color.shields.white + ); shields["US:TX:Fort_Bend:FBCTRA"] = { backgroundImage: shieldImages.shield_us_tx_fbctra, textColor: Color.shields.white, @@ -2704,7 +2742,7 @@ export function loadShields(shieldImages) { }; // Uruguay - shields["UY"] = homeDownShieldBlue; + shields["UY"] = homePlateShield(5, Color.shields.blue, Color.shields.white); // Venezuela [ @@ -2986,7 +3024,14 @@ export function loadShields(shieldImages) { ); // Myanmar - shields["MY:E"] = shields["my:federal"] = hexagonVerticalShieldYellow; + shields["MY:E"] = shields["my:federal"] = hexagonVerticalShield( + 3, + Color.shields.yellow, + Color.shields.black, + Color.shields.black, + 2, + 34 + ); // Nepal shields["np:national"] = roundedRectShield( @@ -2999,8 +3044,16 @@ export function loadShields(shieldImages) { ); // Philippines - shields["PH:N"] = homeDownShield; - shields["PH:E"] = homeDownShieldYellow; + shields["PH:N"] = homePlateShield( + 5, + Color.shields.white, + Color.shields.black + ); + shields["PH:E"] = homePlateShield( + 5, + Color.shields.yellow, + Color.shields.black + ); // Pakistan shields["PK:national"] = hexagonHorizontalShieldBlue; @@ -3017,7 +3070,14 @@ export function loadShields(shieldImages) { }; // Turkey - shields["TR:motorway"] = hexagonVerticalShieldOrange; + shields["TR:motorway"] = hexagonVerticalShield( + 2, + Color.shields.orange, + Color.shields.black, + Color.shields.black, + 0, + 34 + ); // Taiwan shields["TW:freeway"] = { @@ -3183,14 +3243,24 @@ export function loadShields(shieldImages) { ); // Greece - shields["GR:national"] = hexagonVerticalShieldGreen; - shields["GR:motorway"] = shields["GR:national"]; + shields["GR:motorway"] = shields["GR:national"] = hexagonVerticalShield( + 3, + Color.shields.green, + Color.shields.white, + Color.shields.white, + 0, + 34 + ); // Hungary - shields["HU:national"] = { - ...homeDownShieldBlue, - backgroundImage: shieldImages.shield_home_blue_3, - }; + shields["HU:national"] = homePlateShield( + 4, + Color.shields.blue, + Color.shields.white, + Color.shields.white, + 2, + 30 + ); // Iceland shields["IS"] = roundedRectShield( @@ -3204,7 +3274,14 @@ export function loadShields(shieldImages) { shields["IT:A-road"] = octagonShieldGreen; // Kosovo - shields["XK:motorway"] = hexagonVerticalShieldGreen; + shields["XK:motorway"] = hexagonVerticalShield( + 3, + Color.shields.green, + Color.shields.white, + Color.shields.white, + 0, + 34 + ); // Lithuania shields["lt:national"] = roundedRectShield( @@ -3257,7 +3334,14 @@ export function loadShields(shieldImages) { ); // North Macedonia - shields["mk:national"] = hexagonVerticalShieldGreen; + shields["mk:national"] = hexagonVerticalShield( + 3, + Color.shields.green, + Color.shields.white, + Color.shields.white, + 0, + 34 + ); // Netherlands // https://wiki.openstreetmap.org/wiki/The_Netherlands_road_network @@ -3329,7 +3413,14 @@ export function loadShields(shieldImages) { ); // Serbia - shields["RS:national"] = hexagonVerticalShieldGreen; + shields["RS:national"] = hexagonVerticalShield( + 3, + Color.shields.green, + Color.shields.white, + Color.shields.white, + 0, + 34 + ); // Russia shields["ru:national"] = roundedRectShield( @@ -3376,7 +3467,14 @@ export function loadShields(shieldImages) { ); // Slovenia - shields["SI:AC"] = hexagonVerticalShieldGreen; + shields["SI:AC"] = hexagonVerticalShield( + 3, + Color.shields.green, + Color.shields.white, + Color.shields.white, + 0, + 34 + ); // Slovakia shields["sk:national"] = roundedRectShield( @@ -3393,7 +3491,14 @@ export function loadShields(shieldImages) { ); // Kosovo - shields["XK:motorway"] = hexagonVerticalShieldGreen; + shields["XK:motorway"] = hexagonVerticalShield( + 3, + Color.shields.green, + Color.shields.white, + Color.shields.white, + 0, + 34 + ); // OCEANIA @@ -3411,24 +3516,30 @@ export function loadShields(shieldImages) { shields[`AU:${state_or_territory}:ALT_S`], ] = [ roundedRectShield(Color.shields.green, Color.shields.yellow), - homeDownShieldGreenYellow, - homeDownShield, + homePlateShield(5, Color.shields.green, Color.shields.yellow), + homePlateShield(5, Color.shields.white, Color.shields.black), fishheadShieldBlue, pentagonShieldBrown, banneredShield( roundedRectShield(Color.shields.green, Color.shields.yellow), ["ALT"] ), - banneredShield(homeDownShield, ["ALT"]), + banneredShield( + homePlateShield(5, Color.shields.white, Color.shields.black), + ["ALT"] + ), banneredShield(fishheadShieldBlue, ["ALT"]), ]) ); - shields["AU:QLD:MR"] = { - ...hexagonVerticalShield, - colorLighten: Color.shields.blue, - textColor: Color.shields.blue, - }; + shields["AU:QLD:MR"] = hexagonVerticalShield( + 4, + Color.shields.white, + Color.shields.blue, + Color.shields.blue, + 0, + 20 + ); shields["AU:QLD:SSTR"] = roundedRectShield( Color.shields.brown, Color.shields.yellow @@ -3436,7 +3547,11 @@ export function loadShields(shieldImages) { // New Zealand shields["NZ:SH"] = fishheadShieldRed; - shields["NZ:UR"] = homeDownShield; + shields["NZ:UR"] = homePlateShield( + 5, + Color.shields.white, + Color.shields.black + ); shields["NZ:WRR"] = circleShield(Color.shields.white, Color.shields.black); // Ref-specific cases. Each entry should be documented in CONTRIBUTE.md diff --git a/src/shieldtest.js b/src/shieldtest.js index 6f668d3cb..666b721a1 100644 --- a/src/shieldtest.js +++ b/src/shieldtest.js @@ -52,6 +52,7 @@ let networks = [ "AU:WA:NH", "PH:E", "US:TX:Montgomery:MCTRA", + "HU:national", "US:TN:secondary", "US:NC", From b52421c3e094d926d55577432bb1f4ea28ce5a0c Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Tue, 29 Nov 2022 19:35:49 -0500 Subject: [PATCH 044/514] add diamond, horizontal hexagon draw functions --- icons/shield_diamond.svg | 3 - icons/shield_diamond_brown.svg | 3 - icons/shield_hex_horz_blue_2.svg | 3 - icons/shield_hex_horz_blue_3.svg | 3 - src/js/shield_canvas_draw.js | 145 +++++++++++++++++++++++++++++++ src/js/shield_defs.js | 143 ++++++++++++++++++++---------- 6 files changed, 244 insertions(+), 56 deletions(-) delete mode 100644 icons/shield_diamond.svg delete mode 100644 icons/shield_diamond_brown.svg delete mode 100644 icons/shield_hex_horz_blue_2.svg delete mode 100644 icons/shield_hex_horz_blue_3.svg diff --git a/icons/shield_diamond.svg b/icons/shield_diamond.svg deleted file mode 100644 index 7fdfd1b8d..000000000 --- a/icons/shield_diamond.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_diamond_brown.svg b/icons/shield_diamond_brown.svg deleted file mode 100644 index 7355f8b62..000000000 --- a/icons/shield_diamond_brown.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_hex_horz_blue_2.svg b/icons/shield_hex_horz_blue_2.svg deleted file mode 100644 index dd6820c59..000000000 --- a/icons/shield_hex_horz_blue_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_hex_horz_blue_3.svg b/icons/shield_hex_horz_blue_3.svg deleted file mode 100644 index 5fadcff05..000000000 --- a/icons/shield_hex_horz_blue_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index 51490621d..3e8227bc0 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -255,6 +255,72 @@ export function trapezoid( return ctx; } +export function diamond(fill, outline, ref, radius, outlineWidth, rectWidth) { + let extraSpace = 4 * PXR; + let height = CS + extraSpace; + + if (rectWidth == null) { + var shieldWidth = Math.ceil( + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR + ); + var width = Math.max( + minGenericShieldWidth + extraSpace, + Math.min(maxGenericShieldWidth, shieldWidth) + ); + } else { + var width = rectWidth * PXR; + } + + var ctx = Gfx.getGfxContext({ width: width, height: height }); + + let lineThick = outlineWidth * PXR; + let lineWidth = lineThick / 2; + let drawRadius = radius * PXR; + + let x0 = lineWidth; + let x6 = width - lineWidth; + let y0 = lineWidth; + let y6 = height - lineWidth; + + let x3 = (x0 + x6) / 2; + let y3 = (y0 + y6) / 2; + + let angle = Math.atan((y6 - y0) / (x6 - x0)); + let xInnerOffset = drawRadius * Math.sin(angle); + let yInnerOffset = drawRadius * Math.cos(angle); + let xOuterOffset = yInnerOffset / Math.tan(angle); + let yOuterOffset = xInnerOffset * Math.tan(angle); + + let x1 = x0 + xOuterOffset; + let x2 = x3 - xInnerOffset; + let x4 = x3 + xInnerOffset; + let x5 = x6 - xOuterOffset; + + let y1 = y0 + yOuterOffset; + let y2 = y3 - yInnerOffset; + let y4 = y3 + yInnerOffset; + let y5 = y6 - yOuterOffset; + + ctx.beginPath(); + ctx.moveTo(x1, y2); + ctx.arcTo(x3, y0, x4, y1, drawRadius); + ctx.arcTo(x6, y3, x5, y4, drawRadius); + ctx.arcTo(x3, y6, x2, y5, drawRadius); + ctx.arcTo(x0, y3, x1, y2, drawRadius); + ctx.closePath(); + + ctx.lineWidth = lineThick; + ctx.fillStyle = fill; + ctx.fill(); + + if (outline != null) { + ctx.strokeStyle = outline; + ctx.stroke(); + } + + return ctx; +} + export function homePlate( offset, fill, @@ -392,3 +458,82 @@ export function hexagonVertical( return ctx; } + +export function hexagonHorizontal( + angle, + fill, + outline, + ref, + radius, + outlineWidth, + rectWidth +) { + let angleInRadians = (angle * Math.PI) / 180; + let angleSign = Math.sign(angle); + let sine = Math.sin(Math.abs(angleInRadians)); + let cosine = Math.cos(angleInRadians); + let tangent = Math.tan(angleInRadians); + let halfComplementTangent = Math.tan(Math.PI / 4 - angleInRadians / 2); + + if (rectWidth == null) { + var shieldWidth = Math.ceil( + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + + (2 + (CS * sine) / 2) * PXR + ); + var width = Math.max( + minGenericShieldWidth + 4 * PXR, + Math.min(maxGenericShieldWidth, shieldWidth) + ); + } else { + var width = rectWidth * PXR; + } + + var ctx = Gfx.getGfxContext({ width: width, height: CS }); + + let lineThick = outlineWidth * PXR; + let lineWidth = lineThick / 2; + let drawRadius = radius * PXR; + + let x0 = lineWidth; + let x9 = width - lineWidth; + let y0 = lineWidth; + let y6 = CS - lineWidth; + + let y3 = (y0 + y6) / 2; + + let y1 = y0 + drawRadius * halfComplementTangent * cosine; + let y2 = y3 - drawRadius * sine; + let y4 = y3 + drawRadius * sine; + let y5 = y6 - drawRadius * halfComplementTangent * cosine; + + let x1 = x0 + (y3 - y2) * tangent; + let x3 = x0 + (y3 - y0) * tangent; + let x6 = x9 - (y3 - y0) * tangent; + let x8 = x9 - (y3 - y2) * tangent; + + let x2 = x3 - drawRadius * halfComplementTangent * sine; + let x4 = x3 + drawRadius * halfComplementTangent; + let x5 = x6 - drawRadius * halfComplementTangent; + let x7 = x6 + drawRadius * halfComplementTangent * sine; + + ctx.beginPath(); + ctx.moveTo(x4, y0); + ctx.arcTo(x6, y0, x7, y1, drawRadius); + ctx.arcTo(x9, y3, x8, y4, drawRadius); + ctx.arcTo(x6, y6, x5, y6, drawRadius); + ctx.arcTo(x3, y6, x2, y5, drawRadius); + ctx.arcTo(x0, y3, x1, y2, drawRadius); + ctx.arcTo(x3, y0, x4, y0, drawRadius); + ctx.closePath(); + + ctx.lineWidth = lineThick; + ctx.fillStyle = fill; + ctx.fill(); + + if (outline != null) { + ctx.strokeStyle = outline; + ctx.stroke(); + } + + return ctx; +} diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 7021b0d1b..1376569b5 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -134,6 +134,33 @@ function trapezoidShield( }; } +/** + * Draws a shield with a diamond background + * + * @param {*} fillColor - Color of diamond background fill + * @param {*} strokeColor - Color of diamond outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of diamond (defaults to variable-width) + * @param {*} radius - Corner radius of diamond (defaults to 2) + * @returns a shield definition object + */ +function diamondShield(fillColor, strokeColor, textColor, radius, rectWidth) { + textColor = textColor ?? strokeColor; + radius = radius ?? 2; + return { + backgroundDraw: (ref) => + ShieldDraw.diamond(fillColor, strokeColor, ref, radius, 1, rectWidth), + textLayoutConstraint: ShieldText.ellipseTextConstraint, + padding: { + left: 4.5, + right: 4.5, + top: 5, + bottom: 5, + }, + textColor: textColor, + }; +} + /** * Draws a shield with a home plate background * @@ -222,6 +249,49 @@ function hexagonVerticalShield( }; } +/** + * Draws a shield with a horizontally-aligned hexagon background + * + * @param {*} angle - Angle (in degrees) at which sides deviate from vertical + * @param {*} fillColor - Color of hexagon background fill + * @param {*} strokeColor - Color of hexagon outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of hexagon (defaults to variable-width) + * @param {*} radius - Corner radius of hexagon (defaults to 2) + * @returns a shield definition object + */ +function hexagonHorizontalShield( + angle, + fillColor, + strokeColor, + textColor, + radius, + rectWidth +) { + textColor = textColor ?? strokeColor; + radius = radius ?? 2; + return { + backgroundDraw: (ref) => + ShieldDraw.hexagonHorizontal( + angle, + fillColor, + strokeColor, + ref, + radius, + 1, + rectWidth + ), + textLayoutConstraint: ShieldText.ellipseTextConstraint, + padding: { + left: 3, + right: 3, + top: 2, + bottom: 2, + }, + textColor: textColor, + }; +} + /** * Draws a shield with a pill-shaped background * @@ -352,25 +422,6 @@ export function loadShields(shieldImages) { }, }; - // Diamond shields - let diamondShield = { - backgroundImage: shieldImages.shield_diamond, - textLayoutConstraint: ShieldText.ellipseTextConstraint, - textColor: Color.shields.black, - padding: { - left: 2.5, - right: 2.5, - top: 4.5, - bottom: 4.5, - }, - }; - - let diamondShieldBrown = { - ...diamondShield, - backgroundImage: shieldImages.shield_diamond_brown, - textColor: Color.shields.white, - }; - // Pentagon shields let pentagonShield = { backgroundImage: shieldImages.shield_pent_3, @@ -415,22 +466,6 @@ export function loadShields(shieldImages) { ], }; - // Hexagon shields - let hexagonHorizontalShieldBlue = { - backgroundImage: [ - shieldImages.shield_hex_horz_blue_2, - shieldImages.shield_hex_horz_blue_3, - ], - textLayoutConstraint: ShieldText.ellipseTextConstraint, - textColor: Color.shields.white, - padding: { - left: 3, - right: 3, - top: 2, - bottom: 2, - }, - }; - // Octagon shields let octagonShieldGreen = { backgroundImage: [ @@ -1481,7 +1516,13 @@ export function loadShields(shieldImages) { ); // Michigan - shields["US:MI"] = diamondShield; + shields["US:MI"] = diamondShield( + Color.shields.white, + Color.shields.black, + Color.shields.black, + 2, + 24 + ); ["CR", "Benzie", "Gogebic", "Kalkaska", "Montcalm", "Roscommon"].forEach( (county) => (shields[`US:MI:${county}`] = pentagonShieldBlueYellow) ); @@ -1712,7 +1753,13 @@ export function loadShields(shieldImages) { ); // North Carolina - shields["US:NC"] = diamondShield; + shields["US:NC"] = diamondShield( + Color.shields.white, + Color.shields.black, + Color.shields.black, + 2, + 24 + ); shields["US:NC:Bypass"] = banneredShield(shields["US:NC"], ["BYP"]); shields["US:NC:Business"] = banneredShield(shields["US:NC"], ["BUS"]); shields["US:NC:Truck"] = banneredShield(shields["US:NC"], ["TRK"]); @@ -2788,7 +2835,7 @@ export function loadShields(shieldImages) { }, }, circleShield(Color.shields.white, Color.shields.black), - diamondShield, + diamondShield(Color.shields.white, Color.shields.black), ]) ); @@ -2940,10 +2987,14 @@ export function loadShields(shieldImages) { // Japan shields["JP:E"] = roundedRectShield(Color.shields.green, Color.shields.white); shields["JP:national"] = triangleConvexDownShieldBlue; - shields["JP:prefectural"] = { - ...hexagonHorizontalShieldBlue, - backgroundImage: shieldImages.shield_hex_horz_blue_2, - }; + shields["JP:prefectural"] = hexagonHorizontalShield( + 30, + Color.shields.blue, + Color.shields.white, + Color.shields.white, + 2, + 24 + ); [ "aichi", "akita", @@ -3056,7 +3107,11 @@ export function loadShields(shieldImages) { ); // Pakistan - shields["PK:national"] = hexagonHorizontalShieldBlue; + shields["PK:national"] = hexagonHorizontalShield( + 30, + Color.shields.blue, + Color.shields.white + ); shields["PK:motorway"] = { backgroundImage: shieldImages.shield_pk_motorway, textLayoutConstraint: ShieldText.southHalfellipseTextConstraint, @@ -3610,7 +3665,7 @@ export function loadShields(shieldImages) { }; shields["US:MI"].overrideByRef = { - 185: diamondShieldBrown, + 185: diamondShield(Color.shields.brown, Color.shields.white), }; shields["US:MO:Taney:Branson"].overrideByRef = { From a784bf5e3d18f46a98f036a5a110b0a5946787fd Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Wed, 30 Nov 2022 15:57:19 -0500 Subject: [PATCH 045/514] factor out Math.ceil() --- src/js/shield_canvas_draw.js | 31 ++++++++++++------------------- src/js/shield_text.js | 2 +- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index 3e8227bc0..ac9a780b4 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -122,7 +122,6 @@ export function blank(ref) { minGenericShieldWidth, Math.min(maxGenericShieldWidth, shieldWidth) ); - width = Math.ceil(width); return Gfx.getGfxContext({ width: width, height: CS }); } @@ -135,9 +134,8 @@ export function roundedRectangle( rectWidth ) { if (rectWidth == null) { - var shieldWidth = Math.ceil( - ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR - ); + var shieldWidth = + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR; var width = Math.max( minGenericShieldWidth, Math.min(maxGenericShieldWidth, shieldWidth) @@ -198,10 +196,9 @@ export function trapezoid( let tangent = Math.tan(angleInRadians); if (rectWidth == null) { - var shieldWidth = Math.ceil( + var shieldWidth = ShieldText.calculateTextWidth(ref, genericShieldFontSize) + - (2 + (CS * sine) / 2) * PXR - ); + (2 + (CS * sine) / 2) * PXR; var width = Math.max( minGenericShieldWidth, Math.min(maxGenericShieldWidth, shieldWidth) @@ -260,9 +257,8 @@ export function diamond(fill, outline, ref, radius, outlineWidth, rectWidth) { let height = CS + extraSpace; if (rectWidth == null) { - var shieldWidth = Math.ceil( - ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR - ); + var shieldWidth = + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR; var width = Math.max( minGenericShieldWidth + extraSpace, Math.min(maxGenericShieldWidth, shieldWidth) @@ -331,9 +327,8 @@ export function homePlate( rectWidth ) { if (rectWidth == null) { - var shieldWidth = Math.ceil( - ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR - ); + var shieldWidth = + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR; var width = Math.max( minGenericShieldWidth, Math.min(maxGenericShieldWidth, shieldWidth) @@ -403,9 +398,8 @@ export function hexagonVertical( rectWidth ) { if (rectWidth == null) { - var shieldWidth = Math.ceil( - ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR - ); + var shieldWidth = + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR; var width = Math.max( minGenericShieldWidth, Math.min(maxGenericShieldWidth, shieldWidth) @@ -476,10 +470,9 @@ export function hexagonHorizontal( let halfComplementTangent = Math.tan(Math.PI / 4 - angleInRadians / 2); if (rectWidth == null) { - var shieldWidth = Math.ceil( + var shieldWidth = ShieldText.calculateTextWidth(ref, genericShieldFontSize) + - (2 + (CS * sine) / 2) * PXR - ); + (2 + (CS * sine) / 2) * PXR; var width = Math.max( minGenericShieldWidth + 4 * PXR, Math.min(maxGenericShieldWidth, shieldWidth) diff --git a/src/js/shield_text.js b/src/js/shield_text.js index ec0c5f8d0..d8c199157 100644 --- a/src/js/shield_text.js +++ b/src/js/shield_text.js @@ -280,5 +280,5 @@ export function drawBannerHaloText(ctx, text, bannerIndex) { export function calculateTextWidth(text, fontSize) { var ctx = Gfx.getGfxContext({ width: 1, height: 1 }); //dummy canvas ctx.font = Gfx.shieldFont(fontSize); - return ctx.measureText(text).width; + return Math.ceil(ctx.measureText(text).width); } From 45fcf570c88833f4dd03450498a97b43ddc0ec77 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Wed, 30 Nov 2022 18:59:32 -0500 Subject: [PATCH 046/514] remove extra horizontal hexagon spacing --- src/js/shield_canvas_draw.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index ac9a780b4..0114aae38 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -471,8 +471,7 @@ export function hexagonHorizontal( if (rectWidth == null) { var shieldWidth = - ShieldText.calculateTextWidth(ref, genericShieldFontSize) + - (2 + (CS * sine) / 2) * PXR; + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR; var width = Math.max( minGenericShieldWidth + 4 * PXR, Math.min(maxGenericShieldWidth, shieldWidth) From dcc6ff8c536061307f25f2c65f68b4d7417dd096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Thu, 1 Dec 2022 10:57:10 -0800 Subject: [PATCH 047/514] Installed Mocha, Chai for automated testing --- .github/workflows/test-build.yml | 2 +- package-lock.json | 1682 +++++++++++++++++++++++++++++- package.json | 5 +- 3 files changed, 1676 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 20205e75d..7ac5b48be 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -18,5 +18,5 @@ jobs: run: | npm ci --include=dev sed 's//53iZvB2drcamS0Ge0xiD/g' src/configs/config.maptiler.js > src/config.js - npm run build + npm run build test # MapTiler key 53iZvB2drcamS0Ge0xiD only allows requests from zelonewolf.github.io diff --git a/package-lock.json b/package-lock.json index 78fc58c92..000ed83ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,11 @@ "open": "^8.4.0" }, "devDependencies": { + "chai": "^4.3.7", "create-serve": "^1.0.1", "esbuild": "^0.15.15", "maplibre-gl": "^2.1.9", + "mocha": "^10.1.0", "openmapsamples": "github:adamfranco/OpenMapSamples", "openmapsamples-maplibre": "github:adamfranco/OpenMapSamples-MapLibre", "prettier": "2.3.2", @@ -201,6 +203,15 @@ "integrity": "sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ==", "dev": true }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -209,6 +220,34 @@ "node": ">=0.10.0" } }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -231,6 +270,15 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -255,6 +303,15 @@ } ] }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -293,6 +350,24 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -328,11 +403,160 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -501,6 +725,41 @@ "node": ">=8.0.0" } }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -515,6 +774,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/deep-eql": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.2.tgz", + "integrity": "sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -547,6 +818,15 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dom-serializer": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", @@ -617,6 +897,12 @@ "integrity": "sha512-iRDI1QeCQIhMCZk48DRDMVgQSSBDmbzzNhnxIo+pwx3swkfjMh6vh0nWLq1NdvGHLKH6wIrAM3vQWeTj6qeoug==", "dev": true }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1039,6 +1325,15 @@ "node": ">=12" } }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -1055,6 +1350,43 @@ "node": ">=6" } }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -1065,6 +1397,20 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -1100,6 +1446,24 @@ "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==", "dev": true }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -1169,6 +1533,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/glob/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1212,6 +1588,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", @@ -1242,6 +1627,15 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -1323,6 +1717,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -1388,6 +1794,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -1399,6 +1814,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -1410,6 +1837,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-number-object": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", @@ -1424,6 +1860,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -1475,6 +1920,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -1507,6 +1964,24 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -1544,6 +2019,46 @@ "node": ">=4" } }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1623,15 +2138,138 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/mocha": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", + "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/murmurhash-js": { "version": "1.0.0", @@ -1639,6 +2277,18 @@ "integrity": "sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=", "dev": true }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", @@ -1684,6 +2334,15 @@ "semver": "bin/semver" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/npm-run-all": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", @@ -1879,6 +2538,45 @@ "openmapsamples": "github:adamfranco/OpenMapSamples" } }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1901,6 +2599,15 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/pbf": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", @@ -1920,6 +2627,18 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/pidtree": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", @@ -2017,6 +2736,15 @@ "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", "dev": true }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -2074,6 +2802,18 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -2086,6 +2826,15 @@ "node": ">= 0.10" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -2144,6 +2893,15 @@ "node": ">=10" } }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -2488,6 +3246,21 @@ "kdbush": "^3.0.0" } }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -2565,6 +3338,18 @@ "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", "dev": true }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -2576,6 +3361,15 @@ "node": "*" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -2649,15 +3443,189 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } }, "dependencies": { @@ -2796,11 +3764,36 @@ "integrity": "sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ==", "dev": true }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -2823,6 +3816,12 @@ "sprintf-js": "~1.0.2" } }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2833,6 +3832,12 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -2870,6 +3875,21 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -2884,8 +3904,72 @@ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" } }, "chownr": { @@ -2893,6 +3977,51 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -3024,6 +4153,29 @@ "css-tree": "^1.1.2" } }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, "decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -3032,6 +4184,15 @@ "mimic-response": "^3.1.0" } }, + "deep-eql": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.2.tgz", + "integrity": "sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -3055,6 +4216,12 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, "dom-serializer": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", @@ -3106,6 +4273,12 @@ "integrity": "sha512-iRDI1QeCQIhMCZk48DRDMVgQSSBDmbzzNhnxIo+pwx3swkfjMh6vh0nWLq1NdvGHLKH6wIrAM3vQWeTj6qeoug==", "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -3329,6 +4502,12 @@ "dev": true, "optional": true }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -3339,6 +4518,31 @@ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -3349,6 +4553,13 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3381,6 +4592,18 @@ "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==", "dev": true }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true + }, "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -3447,6 +4670,15 @@ } } }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -3465,6 +4697,12 @@ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", @@ -3483,6 +4721,12 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -3541,6 +4785,15 @@ "has-bigints": "^1.0.1" } }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -3576,6 +4829,12 @@ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -3584,11 +4843,26 @@ "number-is-nan": "^1.0.0" } }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "is-number-object": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", @@ -3597,6 +4871,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -3627,6 +4907,12 @@ "has-symbols": "^1.0.2" } }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -3653,6 +4939,23 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + } + } + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -3686,6 +4989,34 @@ } } }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -3759,12 +5090,112 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, + "mocha": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", + "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", + "dev": true, + "requires": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "murmurhash-js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", "integrity": "sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=", "dev": true }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true + }, "napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", @@ -3806,6 +5237,12 @@ } } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "npm-run-all": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", @@ -3954,6 +5391,30 @@ "openmapsamples": "github:adamfranco/OpenMapSamples" } }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -3970,6 +5431,12 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, "pbf": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", @@ -3986,6 +5453,12 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, "pidtree": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", @@ -4061,6 +5534,15 @@ "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", "dev": true }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -4113,6 +5595,15 @@ } } }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -4122,6 +5613,12 @@ "resolve": "^1.1.6" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, "resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -4154,6 +5651,15 @@ "lru-cache": "^6.0.0" } }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -4411,6 +5917,15 @@ "kdbush": "^3.0.0" } }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -4472,6 +5987,15 @@ "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", "dev": true }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -4480,6 +6004,12 @@ "safe-buffer": "^5.0.1" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -4544,15 +6074,145 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/package.json b/package.json index d6ef46454..27f8f3408 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "start": "run-s clean sprites serve", "serve": "node scripts/serve.js", "build:code": "node scripts/build.js", - "build": "run-s clean sprites build:code taginfo status_map" + "build": "run-s clean sprites build:code taginfo status_map", + "test": "mocha test/spec/**/*.js" }, "dependencies": { "@basemaps/sprites": "^6.28.1", @@ -29,9 +30,11 @@ "open": "^8.4.0" }, "devDependencies": { + "chai": "^4.3.7", "create-serve": "^1.0.1", "esbuild": "^0.15.15", "maplibre-gl": "^2.1.9", + "mocha": "^10.1.0", "openmapsamples": "github:adamfranco/OpenMapSamples", "openmapsamples-maplibre": "github:adamfranco/OpenMapSamples-MapLibre", "prettier": "2.3.2", From f55df25b1b26b32982c1152c6b2e4ed294bb1d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Thu, 1 Dec 2022 10:57:31 -0800 Subject: [PATCH 048/514] Test URL hash parsing --- test/spec/label.js | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test/spec/label.js diff --git a/test/spec/label.js b/test/spec/label.js new file mode 100644 index 000000000..876d1095b --- /dev/null +++ b/test/spec/label.js @@ -0,0 +1,37 @@ +"use strict"; + +import chai, { expect } from "chai"; +import * as Label from "../../src/constants/label.js"; + +describe("label", function () { + describe("#getLanguageFromURL", function () { + it("accepts an unset language", function () { + expect( + Label.getLanguageFromURL(new URL("http://localhost:1776/#map=1/2/3")) + ).to.be.null; + }); + it("accepts an empty language", function () { + expect( + Label.getLanguageFromURL( + new URL("http://localhost:1776/#map=1/2/3&language=") + ) + ).to.eql(""); + }); + it("accepts an ISO 639 code", function () { + expect( + Label.getLanguageFromURL( + new URL("http://localhost:1776/#map=1/2/3&language=tlh") + ) + ).to.eql("tlh"); + }); + it("accepts arbitrary text", function () { + expect( + Label.getLanguageFromURL( + new URL( + "http://localhost:1776/#map=1/2/3&language=the King's English" + ) + ) + ).to.eql("the King's English"); + }); + }); +}); From 696e777f77a0814443d4c1818334bc666c82ff62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Thu, 1 Dec 2022 12:50:40 -0800 Subject: [PATCH 049/514] Updated documentation Added basic usage instructions to the readme, including about configuring the language. Formatted the acknowledgements as Markdown and added the mapbox-gl-rtl-text plugin. Mentioned unit tests in the contributing guide. --- ACKNOWLEDGEMENTS.md | 7 ++++--- CONTRIBUTING.md | 8 ++++++-- README.md | 15 +++++++++++---- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md index 31ae3b130..814170112 100644 --- a/ACKNOWLEDGEMENTS.md +++ b/ACKNOWLEDGEMENTS.md @@ -1,5 +1,6 @@ # Thanks -- The stylized highway shields are inspired by "rebusurance" by Minh Nguyễn (https://github.com/1ec5/rebusurance) -- Initial layer code is derived from Oliver Wipfli's openmaptiles-starter style: https://github.com/wipfli/openmaptiles-starter -- The font stack was packaged by the OpenHistoricalMap project: https://github.com/OpenHistoricalMap/map-styles +- The stylized highway shields are inspired by "[Rebusurance](https://github.com/1ec5/rebusurance/)" by Minh Nguyễn. +- Initial layer code is derived from Oliver Wipfli's [openmaptiles-starter](https://github.com/wipfli/openmaptiles-starter/) style. +- The font stack was [packaged by the OpenHistoricalMap project](https://github.com/OpenHistoricalMap/map-styles/). +- Support for right-to-left Arabic and Hebrew text is provided by the [mapbox-gl-rtl-text](https://github.com/mapbox/mapbox-gl-rtl-text/) plugin. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a640a8f2a..0d6382dd4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,9 +43,9 @@ It may be necessary to prefix these with `sudo` depending where NPM is installed ## Platform Specific Notes -### MacOS +### macOS -MacOS doesn't include a default package manager, but Node.js and NPM can be installed via +macOS doesn't include a default package manager, but Node.js and NPM can be installed via [Homebrew][50] or [MacPorts][51]: - Homebrew - `brew install node` @@ -165,6 +165,10 @@ boilerplate in `scripts/taginfo_template.json`. 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` to demonstrate overlaid text on each of them. +4. 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. [90]: https://prettier.io/ [svgo]: https://github.com/svg/svgo/ diff --git a/README.md b/README.md index 3c13a1fc6..41104b1fa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# OpenStreetMap Americana Style +# OpenStreetMap Americana _A quintessentially American map style_ [🗺 View the map](https://zelonewolf.github.io/openstreetmap-americana/) @@ -12,7 +12,13 @@ The purpose of the Americana style is to: The Americana style is the first digital map to achieve concurrent, state-specific highway shields arranged along the path of road. Representative highway shield rendering is of considerable cartographic importance to the American community. We do this proudly in an open source project using vector tile technology. -## Contributor's Guide +## How to use + +You can install the OpenStreetMap Americana package and [deploy it anywhere](CONTRIBUTING.md#Production builds) 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. + +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. + +## Contributor’s guide _Contributors welcome!_ @@ -20,6 +26,7 @@ The repository is organized as follows: - **src/** - The map style. See [CONTRIBUTING.md](CONTRIBUTING.md). - **dev/** - Development tools used for style development. See [Style Developer Tools](dev/README.md) +- **test/** - Automated unit tests. - _Coming soon! Other customized parts of the tech stack._ Some general guidelines: @@ -30,13 +37,13 @@ Some general guidelines: - This style operates on the principle of consensus. Maintainers should ensure that changes represent a broad consensus within the American mapping community. - Maintainers are also responsible to ensure that this principle of consensus does not cause stagnation or inaction. Contributor time is valuable; accepting the responsibility of being a maintainer means committing to responding to issues and PRs on a reasonable timeline that encourages community participation. -## Technology Stack +## Technology stack The technology stack for this style can be summarized below: Americana technology stack -## Data Sources +## Data sources The OpenStreetMap Americana style is built upon the [OpenMapTiles schema](https://openmaptiles.org/schema/), which includes: From ea66fe846029de637dd889b8839d2066a44b14b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 2 Dec 2022 00:07:21 -0800 Subject: [PATCH 050/514] Moved tests to separate step --- .github/workflows/test-build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 7ac5b48be..dcb368d57 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -18,5 +18,8 @@ jobs: run: | npm ci --include=dev sed 's//53iZvB2drcamS0Ge0xiD/g' src/configs/config.maptiler.js > src/config.js - npm run build test + npm run build # MapTiler key 53iZvB2drcamS0Ge0xiD only allows requests from zelonewolf.github.io + - name: Test 🧪 + run: | + npm test From 178ab61f345eb692300c193c8febf699cf298ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 2 Dec 2022 00:07:51 -0800 Subject: [PATCH 051/514] Removed glob from package.json --- .mocharc.yml | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 .mocharc.yml diff --git a/.mocharc.yml b/.mocharc.yml new file mode 100644 index 000000000..7fd0cbb99 --- /dev/null +++ b/.mocharc.yml @@ -0,0 +1 @@ +recursive: true diff --git a/package.json b/package.json index 27f8f3408..71f73e9fa 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "serve": "node scripts/serve.js", "build:code": "node scripts/build.js", "build": "run-s clean sprites build:code taginfo status_map", - "test": "mocha test/spec/**/*.js" + "test": "mocha" }, "dependencies": { "@basemaps/sprites": "^6.28.1", From 95ae9ef0eb7b8c53effad8ca953ed10e05c1e796 Mon Sep 17 00:00:00 2001 From: Josh Lee Date: Fri, 2 Dec 2022 10:59:48 -0500 Subject: [PATCH 052/514] Change to github pages from branch to workflow --- .github/workflows/deploy.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 40ee647ed..4b11fd52d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,6 +8,9 @@ on: jobs: build-and-deploy: runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} steps: - name: Checkout 🛎️ uses: actions/checkout@v2.3.1 @@ -20,8 +23,10 @@ jobs: npm ci --include=dev cp src/configs/config.aws.js src/config.js npm run build - - name: Deploy 🚀 - uses: JamesIves/github-pages-deploy-action@4.1.4 + - name: Upload 🏗 + uses: actions/upload-pages-artifact@v1 with: - branch: gh-pages # The branch the action should deploy to. - folder: dist/ # The folder the action should deploy. + path: ./dist + - name: Deploy 🚀 + id: deployment + uses: actions/deploy-pages@v1 From efc8b1f3cc843a5a2e8abfba84ab00a7295c4f81 Mon Sep 17 00:00:00 2001 From: Josh Lee Date: Sat, 3 Dec 2022 02:14:32 -0500 Subject: [PATCH 053/514] Update deploy.yml Add missing permission flag to workflow --- .github/workflows/deploy.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4b11fd52d..94977e7c8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -5,6 +5,10 @@ on: push: branches: - main +permissions: + contents: read + pages: write + id-token: write jobs: build-and-deploy: runs-on: ubuntu-latest From 7ce164826c2043ccd670e478bf1c1f0b63096c50 Mon Sep 17 00:00:00 2001 From: Josh Lee Date: Sat, 3 Dec 2022 15:57:44 -0500 Subject: [PATCH 054/514] Add business routes in Maine --- src/js/shield_defs.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 1376569b5..eff4414e0 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -1514,6 +1514,7 @@ export function loadShields(shieldImages) { Color.shields.white, Color.shields.black ); + shields["US:ME:Business"] = banneredShield(shields["US:ME"], ["BUS"]); // Michigan shields["US:MI"] = diamondShield( From 65beb67336f4151c0d1d7e862ecf28aa8651507b Mon Sep 17 00:00:00 2001 From: Josh Lee Date: Mon, 5 Dec 2022 06:10:25 -0500 Subject: [PATCH 055/514] Indicate minimum nodejs version --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 71f73e9fa..ac264fcb6 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "svgo": "^2.8.0" }, "engines": { - "npm": ">=8.3.0" + "npm": ">=8.3.0", + "node": ">=14" } } From 70f164b9557b42beca92ba78f91c51c644700e28 Mon Sep 17 00:00:00 2001 From: Josh Lee Date: Mon, 5 Dec 2022 11:53:29 +0000 Subject: [PATCH 056/514] Deprecation warnings in workflow actions Boring details: - Node.js 12 actions are deprecated. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/. Please update the following actions to use Node.js 16: actions/checkout@v2.3.1, actions/setup-node@v1 - The `save-state` command is deprecated and will be disabled soon. Please upgrade to using Environment Files. For more information see: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ --- .github/workflows/deploy.yml | 4 ++-- .github/workflows/lint.yml | 2 +- .github/workflows/test-build.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 94977e7c8..b2bd7ace9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -17,9 +17,9 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - name: Checkout 🛎️ - uses: actions/checkout@v2.3.1 + uses: actions/checkout@v3 - name: Use Node.js 17.x - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: 17.x - name: Install and Build 🔧 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7b630953b..04f98f9bb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: # Make sure the actual branch is checked out when running on pull requests repository: ${{ github.event.pull_request.head.repo.full_name }} diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index dcb368d57..b76f04f8d 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -9,9 +9,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v2.3.1 + uses: actions/checkout@v3 - name: Use Node.js 17.x - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: 17.x - name: Install and Build 🔧 From b0e0f6e38cd090df9272210185c527e0f4af682b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Mon, 28 Nov 2022 01:54:44 -0800 Subject: [PATCH 057/514] Dynamically adapt text-field expressions to language Replaced the metadata-based approach to localizing text-field with a top-level expression variable that can be replaced safely each time the language preference changes. This approach can easily be extended to support additional variables that depend on the environment. --- src/americana.js | 25 +++++++++++++++---------- src/constants/label.js | 29 +++++++++++++++++++++-------- src/layer/aeroway.js | 6 ------ src/layer/park.js | 6 ------ src/layer/place.js | 28 +--------------------------- src/layer/transportation_label.js | 3 --- src/layer/water.js | 9 --------- 7 files changed, 37 insertions(+), 69 deletions(-) diff --git a/src/americana.js b/src/americana.js index 0a84c20b2..4319205d4 100644 --- a/src/americana.js +++ b/src/americana.js @@ -259,16 +259,21 @@ function buildLayers() { let localizedNameExpression = Label.getLocalizedNameExpression(false); let legacyLocalizedNameExpression = Label.getLocalizedNameExpression(true); for (let layer of layers) { - if ( - "metadata" in layer && - "layout" in layer && - layer.metadata["americana:text-field-localized"] === true - ) { - // https://github.com/openmaptiles/openmaptiles/issues/769 - layer.layout["text-field"] = - layer["source-layer"] === "transportation_name" - ? legacyLocalizedNameExpression - : localizedNameExpression; + if ("layout" in layer && "text-field" in layer.layout) { + let textField = layer.layout["text-field"]; + if (textField && textField[0] === "let") { + if (textField.indexOf("localizedName") % 2 === 1) { + let variableNameIndex = textField.indexOf("localizedName"); + if (variableNameIndex % 2 === 1) { + // https://github.com/openmaptiles/openmaptiles/issues/769 + let expr = + layer["source-layer"] === "transportation_name" + ? legacyLocalizedNameExpression + : localizedNameExpression; + layer.layout["text-field"][variableNameIndex + 1] = expr; + } + } + } } } diff --git a/src/constants/label.js b/src/constants/label.js index 7287a0ee2..c10fd360f 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -8,14 +8,9 @@ export function getLanguageFromURL(url) { } /** - * Returns a `coalesce` expression that resolves to the feature's name in a - * language that the user prefers. - * - * @param {boolean} includesLegacyFields - Whether to include the older fields - * that include underscores, for layers that have not transitioned to the - * colon syntax. + * Returns the languages that the user prefers. */ -export function getLocalizedNameExpression(includesLegacyFields) { +export function getLocales() { // Check the language "parameter" in the hash. let parameter = getLanguageFromURL(window.location)?.split(","); // Fall back to the user's language preference. @@ -32,6 +27,19 @@ export function getLocalizedNameExpression(includesLegacyFields) { components.pop(); } } + return locales; +} + +/** + * Returns a `coalesce` expression that resolves to the feature's name in a + * language that the user prefers. + * + * @param {boolean} includesLegacyFields - Whether to include the older fields + * that include underscores, for layers that have not transitioned to the + * colon syntax. + */ +export function getLocalizedNameExpression(includesLegacyFields) { + let locales = getLocales(); if (locales.at(-1) === "en") { locales.push("latin"); } @@ -50,4 +58,9 @@ export function getLocalizedNameExpression(includesLegacyFields) { } // Placeholder to be resolved by buildLayers() -export const localizedName = "$$localizedName"; +export const localizedName = [ + "let", + "localizedName", + "", + ["var", "localizedName"], +]; diff --git a/src/layer/aeroway.js b/src/layer/aeroway.js index b816a411b..b190e78be 100644 --- a/src/layer/aeroway.js +++ b/src/layer/aeroway.js @@ -221,9 +221,6 @@ export const airportLabel = { ...iconLayout, }, source: "openmaptiles", - metadata: { - "americana:text-field-localized": true, - }, "source-layer": "aerodrome_label", }; @@ -246,9 +243,6 @@ export const minorAirportLabel = { "text-size": 10, }, source: "openmaptiles", - metadata: { - "americana:text-field-localized": true, - }, "source-layer": "aerodrome_label", }; diff --git a/src/layer/park.js b/src/layer/park.js index f17e3744a..0ae036420 100644 --- a/src/layer/park.js +++ b/src/layer/park.js @@ -49,9 +49,6 @@ export const label = { "symbol-sort-key": ["get", "rank"], }, source: "openmaptiles", - metadata: { - "americana:text-field-localized": true, - }, "source-layer": "park", }; @@ -103,8 +100,5 @@ export const parkLabel = { "symbol-sort-key": ["get", "rank"], }, source: "openmaptiles", - metadata: { - "americana:text-field-localized": true, - }, "source-layer": "poi", }; diff --git a/src/layer/place.js b/src/layer/place.js index c0058e5fa..b7a5956f0 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -80,9 +80,6 @@ export const village = { minzoom: 11, maxzoom: 14, "source-layer": "place", - metadata: { - "americana:text-field-localized": true, - }, }; export const town = { @@ -146,9 +143,6 @@ export const town = { minzoom: 4, maxzoom: 13, "source-layer": "place", - metadata: { - "americana:text-field-localized": true, - }, }; export const city = { @@ -208,9 +202,7 @@ export const city = { minzoom: 4, maxzoom: 12, "source-layer": "place", - metadata: { - "americana:text-field-localized": true, - }, + metadata: {}, }; export const state = { @@ -252,9 +244,6 @@ export const state = { maxzoom: 7, minzoom: 3, "source-layer": "place", - metadata: { - "americana:text-field-localized": true, - }, }; export const countryOther = { id: "country_other", @@ -284,9 +273,6 @@ export const countryOther = { }, source: "openmaptiles", "source-layer": "place", - metadata: { - "americana:text-field-localized": true, - }, }; export const country3 = { id: "country_3", @@ -317,9 +303,6 @@ export const country3 = { }, source: "openmaptiles", "source-layer": "place", - metadata: { - "americana:text-field-localized": true, - }, }; export const country2 = { id: "country_2", @@ -350,9 +333,6 @@ export const country2 = { }, source: "openmaptiles", "source-layer": "place", - metadata: { - "americana:text-field-localized": true, - }, }; export const country1 = { id: "country_1", @@ -391,9 +371,6 @@ export const country1 = { }, source: "openmaptiles", "source-layer": "place", - metadata: { - "americana:text-field-localized": true, - }, }; export const continent = { id: "continent", @@ -414,7 +391,4 @@ export const continent = { source: "openmaptiles", maxzoom: 1, "source-layer": "place", - metadata: { - "americana:text-field-localized": true, - }, }; diff --git a/src/layer/transportation_label.js b/src/layer/transportation_label.js index c671a557f..e6c9bda68 100644 --- a/src/layer/transportation_label.js +++ b/src/layer/transportation_label.js @@ -119,9 +119,6 @@ export const label = { }, source: "openmaptiles", "source-layer": "transportation_name", - metadata: { - "americana:text-field-localized": true, - }, }; // A spacer label on each bridge to push any waterway label away from the bridge. diff --git a/src/layer/water.js b/src/layer/water.js index 443327c63..2426a1c8e 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -112,9 +112,6 @@ export const waterwayLabel = { "text-letter-spacing": 0.15, }, paint: labelPaintProperties, - metadata: { - "americana:text-field-localized": true, - }, }; //Lake labels rendered as a linear feature @@ -140,9 +137,6 @@ export const waterLabel = { "text-letter-spacing": 0.25, }, paint: labelPaintProperties, - metadata: { - "americana:text-field-localized": true, - }, }; //Lake labels rendered as a point feature @@ -169,7 +163,4 @@ export const waterPointLabel = { "text-letter-spacing": 0.25, }, paint: labelPaintProperties, - metadata: { - "americana:text-field-localized": true, - }, }; From 7cec299977ce2e834312eeaf73a91f0c84ff6958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Mon, 28 Nov 2022 13:26:18 -0800 Subject: [PATCH 058/514] Gloss city names in local language --- src/americana.js | 15 ++++++++++++++- src/layer/place.js | 26 +++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/americana.js b/src/americana.js index 4319205d4..354993917 100644 --- a/src/americana.js +++ b/src/americana.js @@ -255,7 +255,8 @@ function buildLayers() { lyrPlace.continent ); - // Resolve localized name placeholders. + // Updated localizable variables at the top level of each layer's text-field expression based on the current language preference. + let locale = Label.getLocales()[0]; let localizedNameExpression = Label.getLocalizedNameExpression(false); let legacyLocalizedNameExpression = Label.getLocalizedNameExpression(true); for (let layer of layers) { @@ -273,6 +274,18 @@ function buildLayers() { layer.layout["text-field"][variableNameIndex + 1] = expr; } } + if (textField.indexOf("localizedCollator") % 2 === 1) { + let variableNameIndex = textField.indexOf("localizedCollator"); + if (variableNameIndex % 2 === 1) { + let options = { + "case-sensitive": false, + "diacritic-sensitive": true, + locale: locale, + }; + let expr = ["collator", options]; + layer.layout["text-field"][variableNameIndex + 1] = expr; + } + } } } } diff --git a/src/layer/place.js b/src/layer/place.js index b7a5956f0..e4a0b302e 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -181,7 +181,31 @@ export const city = { [11, 0.9], ], }, - "text-field": Label.localizedName, + "text-field": [ + "let", + "localizedName", + "", + "localizedCollator", + ["collator", {}], + [ + "case", + [ + "==", + ["var", "localizedName"], + ["get", "name"], + ["var", "localizedCollator"], + ], + ["var", "localizedName"], + // Gloss the name in the local language if it differs from the localized name. + [ + "format", + ["var", "localizedName"], + "\n", + ["concat", "(", ["get", "name"], ")"], + { "font-scale": 0.8 }, + ], + ], + ], "text-anchor": "bottom", "text-variable-anchor": [ "bottom", From 68eb25330503a1ca0aa4746614b8ecaa5fb999bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 29 Nov 2022 12:34:45 -0800 Subject: [PATCH 059/514] Isolate glossed name from parentheses --- src/layer/place.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/layer/place.js b/src/layer/place.js index e4a0b302e..c68c459f6 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -201,7 +201,17 @@ export const city = { "format", ["var", "localizedName"], "\n", - ["concat", "(", ["get", "name"], ")"], + "(\u200B", + { "font-scale": 0.8 }, + // GL JS lacks support for bidirectional isolating characters, so use a character from the localized name to insulate the parentheses from the embedded text's writing direction. Make it so small that GL JS doesn't bother rendering it. + ["concat", ["slice", ["var", "localizedName"], 0, 1], " "], + { "font-scale": 0.001 }, + ["get", "name"], + { "font-scale": 0.8 }, + ["concat", " ", ["slice", ["var", "localizedName"], 0, 1]], + { "font-scale": 0.001 }, + // A ZWSP prevents GL JS from combining this component with the preceding one, which would cause it to vanish along with the faux isolating character. + "\u200B)", { "font-scale": 0.8 }, ], ], From 98e9a41e984b7cf6448c50e8bfc93982355d6b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 29 Nov 2022 19:19:36 -0800 Subject: [PATCH 060/514] Fold diacritics in English to determine gloss visibility --- src/americana.js | 15 +++++++++++++++ src/layer/place.js | 28 +++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/americana.js b/src/americana.js index 354993917..655f5aa95 100644 --- a/src/americana.js +++ b/src/americana.js @@ -286,6 +286,21 @@ function buildLayers() { layer.layout["text-field"][variableNameIndex + 1] = expr; } } + if (textField.indexOf("diacriticInsensitiveCollator") % 2 === 1) { + let variableNameIndex = textField.indexOf( + "diacriticInsensitiveCollator" + ); + if (variableNameIndex % 2 === 1) { + // Only perform diacritic folding in English. English normally uses few diacritics except when labeling foreign place names on maps. + let options = { + "case-sensitive": false, + "diacritic-sensitive": !/^en\b/.test(locale), + locale: locale, + }; + let expr = ["collator", options]; + layer.layout["text-field"][variableNameIndex + 1] = expr; + } + } } } } diff --git a/src/layer/place.js b/src/layer/place.js index c68c459f6..6220398bb 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -187,15 +187,37 @@ export const city = { "", "localizedCollator", ["collator", {}], + "diacriticInsensitiveCollator", + ["collator", {}], [ "case", [ - "==", + "any", + [ + "==", + ["var", "localizedName"], + ["get", "name"], + ["var", "localizedCollator"], + ], + [ + "==", + ["var", "localizedName"], + ["get", "name"], + ["var", "diacriticInsensitiveCollator"], + ], + ], + [ + "case", + [ + "!=", + ["var", "localizedName"], + ["get", "name"], + ["var", "diacriticInsensitiveCollator"], + ], ["var", "localizedName"], + // Replace the local name with the localized name if the local name is the same plus diacritics. ["get", "name"], - ["var", "localizedCollator"], ], - ["var", "localizedName"], // Gloss the name in the local language if it differs from the localized name. [ "format", From 64e61319660bc93f129a8388645aacd0114b401d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Wed, 30 Nov 2022 03:01:30 -0800 Subject: [PATCH 061/514] Simplified label expression generation --- src/americana.js | 72 ++++++++++++++++++++++------------------------ src/layer/place.js | 30 ++++++------------- 2 files changed, 43 insertions(+), 59 deletions(-) diff --git a/src/americana.js b/src/americana.js index 655f5aa95..e6a1d97c2 100644 --- a/src/americana.js +++ b/src/americana.js @@ -259,48 +259,44 @@ function buildLayers() { let locale = Label.getLocales()[0]; let localizedNameExpression = Label.getLocalizedNameExpression(false); let legacyLocalizedNameExpression = Label.getLocalizedNameExpression(true); + let updateVariable = (prop, variable, expr) => { + let variableNameIndex = prop.indexOf(variable); + if (variableNameIndex % 2 === 1) { + prop[variableNameIndex + 1] = expr; + } + }; + for (let layer of layers) { if ("layout" in layer && "text-field" in layer.layout) { let textField = layer.layout["text-field"]; if (textField && textField[0] === "let") { - if (textField.indexOf("localizedName") % 2 === 1) { - let variableNameIndex = textField.indexOf("localizedName"); - if (variableNameIndex % 2 === 1) { - // https://github.com/openmaptiles/openmaptiles/issues/769 - let expr = - layer["source-layer"] === "transportation_name" - ? legacyLocalizedNameExpression - : localizedNameExpression; - layer.layout["text-field"][variableNameIndex + 1] = expr; - } - } - if (textField.indexOf("localizedCollator") % 2 === 1) { - let variableNameIndex = textField.indexOf("localizedCollator"); - if (variableNameIndex % 2 === 1) { - let options = { - "case-sensitive": false, - "diacritic-sensitive": true, - locale: locale, - }; - let expr = ["collator", options]; - layer.layout["text-field"][variableNameIndex + 1] = expr; - } - } - if (textField.indexOf("diacriticInsensitiveCollator") % 2 === 1) { - let variableNameIndex = textField.indexOf( - "diacriticInsensitiveCollator" - ); - if (variableNameIndex % 2 === 1) { - // Only perform diacritic folding in English. English normally uses few diacritics except when labeling foreign place names on maps. - let options = { - "case-sensitive": false, - "diacritic-sensitive": !/^en\b/.test(locale), - locale: locale, - }; - let expr = ["collator", options]; - layer.layout["text-field"][variableNameIndex + 1] = expr; - } - } + // https://github.com/openmaptiles/openmaptiles/issues/769 + updateVariable( + textField, + "localizedName", + layer["source-layer"] === "transportation_name" + ? legacyLocalizedNameExpression + : localizedNameExpression + ); + + updateVariable(textField, "localizedCollator", [ + "collator", + { + "case-sensitive": false, + "diacritic-sensitive": true, + locale: locale, + }, + ]); + + // Only perform diacritic folding in English. English normally uses few diacritics except when labeling foreign place names on maps. + updateVariable(textField, "diacriticInsensitiveCollator", [ + "collator", + { + "case-sensitive": false, + "diacritic-sensitive": !/^en\b/.test(locale), + locale: locale, + }, + ]); } } } diff --git a/src/layer/place.js b/src/layer/place.js index 6220398bb..e665c631d 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -192,32 +192,20 @@ export const city = { [ "case", [ - "any", - [ - "==", - ["var", "localizedName"], - ["get", "name"], - ["var", "localizedCollator"], - ], - [ - "==", - ["var", "localizedName"], - ["get", "name"], - ["var", "diacriticInsensitiveCollator"], - ], + "==", + ["var", "localizedName"], + ["get", "name"], + ["var", "localizedCollator"], ], + ["var", "localizedName"], + // If the name in the local language and preferred language match except for diacritics, show only the localized name. [ - "case", - [ - "!=", - ["var", "localizedName"], - ["get", "name"], - ["var", "diacriticInsensitiveCollator"], - ], + "==", ["var", "localizedName"], - // Replace the local name with the localized name if the local name is the same plus diacritics. ["get", "name"], + ["var", "diacriticInsensitiveCollator"], ], + ["get", "name"], // Gloss the name in the local language if it differs from the localized name. [ "format", From dfa0bf53d5bc93f090c25e4c553567deadc3bfed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Wed, 30 Nov 2022 04:38:05 -0800 Subject: [PATCH 062/514] Conflate local and preferred language names more aggressively MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the name in the preferred language matches the name in the local language except for the addition of a prefix or suffix, which is likely to be an insignificant word like “City”, splice the local-language name into the preferred-language name to reduce redundancy. --- src/layer/place.js | 83 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/src/layer/place.js b/src/layer/place.js index e665c631d..280fb4cc8 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -198,14 +198,87 @@ export const city = { ["var", "localizedCollator"], ], ["var", "localizedName"], - // If the name in the local language and preferred language match except for diacritics, show only the localized name. + // If the name in the preferred language is the same as the name in the local language except for the omission of diacritics and/or the addition of a suffix (e.g., "City" in English), replace the common prefix with the local name. [ - "==", - ["var", "localizedName"], + "all", + [ + "==", + ["slice", ["var", "localizedName"], 0, ["length", ["get", "name"]]], + ["get", "name"], + ["var", "diacriticInsensitiveCollator"], + ], + [ + "in", + [ + "slice", + // "Montreal" vs. "Montréal" + ["concat", ["var", "localizedName"], " "], + ["length", ["get", "name"]], + ["+", ["length", ["get", "name"]], 1], + ], + // "Quebec City" vs. "Québec", "Washington, D.C." vs. "Washington" + " ,", + ], + ], + [ + "concat", + ["get", "name"], + ["slice", ["var", "localizedName"], ["length", ["get", "name"]]], + ], + // If the name in the preferred language is the same as the name in the local language except for the omission of diacritics and/or the addition of a prefix (e.g., "City of" in English or "Ciudad de" in Spanish), replace the common suffix with the local name. + [ + "all", + [ + "==", + [ + "slice", + ["var", "localizedName"], + [ + "-", + ["length", ["var", "localizedName"]], + ["length", ["get", "name"]], + ], + ], + ["get", "name"], + ["var", "diacriticInsensitiveCollator"], + ], + [ + "==", + [ + "slice", + ["var", "localizedName"], + [ + "-", + [ + "-", + ["length", ["var", "localizedName"]], + ["length", ["get", "name"]], + ], + 1, + ], + [ + "-", + ["length", ["var", "localizedName"]], + ["length", ["get", "name"]], + ], + ], + " ", + ], + ], + [ + "concat", + [ + "slice", + ["var", "localizedName"], + 0, + [ + "-", + ["length", ["var", "localizedName"]], + ["length", ["get", "name"]], + ], + ], ["get", "name"], - ["var", "diacriticInsensitiveCollator"], ], - ["get", "name"], // Gloss the name in the local language if it differs from the localized name. [ "format", From d62da7e872a9978aa91c990914f5d646026821f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 3 Dec 2022 01:10:24 -0800 Subject: [PATCH 063/514] Refactored layer localization --- src/americana.js | 46 +--------- src/constants/label.js | 65 +++++++++++++- test/spec/label.js | 189 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 253 insertions(+), 47 deletions(-) diff --git a/src/americana.js b/src/americana.js index e6a1d97c2..15e0f8ae8 100644 --- a/src/americana.js +++ b/src/americana.js @@ -255,51 +255,7 @@ function buildLayers() { lyrPlace.continent ); - // Updated localizable variables at the top level of each layer's text-field expression based on the current language preference. - let locale = Label.getLocales()[0]; - let localizedNameExpression = Label.getLocalizedNameExpression(false); - let legacyLocalizedNameExpression = Label.getLocalizedNameExpression(true); - let updateVariable = (prop, variable, expr) => { - let variableNameIndex = prop.indexOf(variable); - if (variableNameIndex % 2 === 1) { - prop[variableNameIndex + 1] = expr; - } - }; - - for (let layer of layers) { - if ("layout" in layer && "text-field" in layer.layout) { - let textField = layer.layout["text-field"]; - if (textField && textField[0] === "let") { - // https://github.com/openmaptiles/openmaptiles/issues/769 - updateVariable( - textField, - "localizedName", - layer["source-layer"] === "transportation_name" - ? legacyLocalizedNameExpression - : localizedNameExpression - ); - - updateVariable(textField, "localizedCollator", [ - "collator", - { - "case-sensitive": false, - "diacritic-sensitive": true, - locale: locale, - }, - ]); - - // Only perform diacritic folding in English. English normally uses few diacritics except when labeling foreign place names on maps. - updateVariable(textField, "diacriticInsensitiveCollator", [ - "collator", - { - "case-sensitive": false, - "diacritic-sensitive": !/^en\b/.test(locale), - locale: locale, - }, - ]); - } - } - } + Label.localizeLayers(layers, Label.getLocales()); return layers; } diff --git a/src/constants/label.js b/src/constants/label.js index c10fd360f..34872434e 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -34,12 +34,12 @@ export function getLocales() { * Returns a `coalesce` expression that resolves to the feature's name in a * language that the user prefers. * + * @param {[string]} locales - Locales of the name fields to include. * @param {boolean} includesLegacyFields - Whether to include the older fields * that include underscores, for layers that have not transitioned to the * colon syntax. */ -export function getLocalizedNameExpression(includesLegacyFields) { - let locales = getLocales(); +export function getLocalizedNameExpression(locales, includesLegacyFields) { if (locales.at(-1) === "en") { locales.push("latin"); } @@ -57,6 +57,67 @@ export function getLocalizedNameExpression(includesLegacyFields) { return ["coalesce", ...nameFields.map((f) => ["get", f])]; } +/** + * Replaces the value of a variable in the given `let` expression. + * + * @param {array} letExpr - Expression to update. + * @param {string} variable - Name of the variable to set. + * @param {*} value - The variable's new value. + */ +export function updateVariable(letExpr, variable, value) { + if (!letExpr || letExpr[0] !== "let") return; + + let variableNameIndex = letExpr.indexOf(variable); + if (variableNameIndex % 2 === 1) { + letExpr[variableNameIndex + 1] = value; + } +} + +/** + * Updates localizable variables at the top level of each layer's `text-field` expression based on the given locales. + * + * @param {[object]} layers - The style layers to localize. + * @param {[string]} locales - The locales to insert into each layer. + */ +export function localizeLayers(layers, locales) { + let localizedNameExpression = getLocalizedNameExpression(locales, false); + let legacyLocalizedNameExpression = getLocalizedNameExpression(locales, true); + + for (let layer of layers) { + if ("layout" in layer && "text-field" in layer.layout) { + let textField = layer.layout["text-field"]; + + updateVariable( + textField, + "localizedName", + // https://github.com/openmaptiles/openmaptiles/issues/769 + layer["source-layer"] === "transportation_name" + ? legacyLocalizedNameExpression + : localizedNameExpression + ); + + updateVariable(textField, "localizedCollator", [ + "collator", + { + "case-sensitive": false, + "diacritic-sensitive": true, + locale: locales[0], + }, + ]); + + // Only perform diacritic folding in English. English normally uses few diacritics except when labeling foreign place names on maps. + updateVariable(textField, "diacriticInsensitiveCollator", [ + "collator", + { + "case-sensitive": false, + "diacritic-sensitive": !/^en\b/.test(locales[0]), + locale: locales[0], + }, + ]); + } + } +} + // Placeholder to be resolved by buildLayers() export const localizedName = [ "let", diff --git a/test/spec/label.js b/test/spec/label.js index 876d1095b..acdb42fa6 100644 --- a/test/spec/label.js +++ b/test/spec/label.js @@ -34,4 +34,193 @@ describe("label", function () { ).to.eql("the King's English"); }); }); + + describe("#getLocales", function () { + beforeEach(function () { + global.window = {}; + global.navigator = {}; + }); + afterEach(function () { + delete global.window; + delete global.navigator; + }); + + it("gets locales from preferences", function () { + window.location = new URL("http://localhost:1776/#map=1/2/3"); + navigator = { languages: ["tlh-UN", "ase"], language: "tlh" }; + expect(Label.getLocales()).to.eql(["tlh-UN", "tlh", "ase"]); + }); + it("gets locales from the URL", function () { + window.location = new URL( + "http://localhost:1776/#map=1/2/3&language=tlh-UN,ase" + ); + expect(Label.getLocales()).to.eql(["tlh-UN", "tlh", "ase"]); + }); + }); + + describe("#getLocalizedNameExpression", function () { + it("coalesces names in each locale", function () { + expect( + Label.getLocalizedNameExpression(["en-US", "en", "fr"], false) + ).to.eql([ + "coalesce", + ["get", "name:en-US"], + ["get", "name:en"], + ["get", "name:fr"], + ["get", "name"], + ]); + }); + it("falls back from English to Romanization", function () { + expect(Label.getLocalizedNameExpression(["en-US", "en"], false)).to.eql([ + "coalesce", + ["get", "name:en-US"], + ["get", "name:en"], + ["get", "name:latin"], + ["get", "name"], + ]); + }); + it("includes legacy fields", function () { + expect( + Label.getLocalizedNameExpression(["en-US", "en", "de"], true) + ).to.eql([ + "coalesce", + ["get", "name:en-US"], + ["get", "name:en"], + ["get", "name_en"], + ["get", "name:de"], + ["get", "name_de"], + ["get", "name"], + ]); + }); + }); + + describe("#updateVariable", function () { + it("replaces the value at the correct index", function () { + let expr = [ + "let", + "one", + "won", + "two", + "too", + "three", + "tree", + ["get", "fore"], + ]; + Label.updateVariable(expr, "one", 1); + Label.updateVariable(expr, "two", 2); + Label.updateVariable(expr, "three", 3); + expect(expr).to.eql([ + "let", + "one", + 1, + "two", + 2, + "three", + 3, + ["get", "fore"], + ]); + }); + it("avoids updating non-let expressions", function () { + let expr = ["get", "fore"]; + Label.updateVariable(expr, "fore", 4); + expect(expr).to.eql(["get", "fore"]); + }); + }); + + describe("#localizeLayers", function () { + it("updates localized name", function () { + let layers = [ + { + layout: { + "text-field": "Null Island", + }, + }, + { + layout: { + "text-field": Label.localizedName, + }, + }, + ]; + Label.localizeLayers(layers, ["en"]); + expect(layers[0].layout["text-field"]).to.eql("Null Island"); + expect(layers[1].layout["text-field"][2]).to.deep.include([ + "get", + "name:en", + ]); + }); + it("uses legacy name fields in transportation name layers", function () { + let layers = [ + { + "source-layer": "transportation_name", + layout: { + "text-field": Label.localizedName, + }, + }, + ]; + Label.localizeLayers(layers, ["en"]); + expect(layers[0].layout["text-field"][2]).to.deep.include([ + "get", + "name_en", + ]); + }); + it("updates collator", function () { + let layers = [ + { + layout: { + "text-field": [ + "let", + "localizedCollator", + "", + ["var", "localizedCollator"], + ], + }, + }, + ]; + Label.localizeLayers(layers, ["tlh"]); + expect(layers[0].layout["text-field"][2][0]).to.eql("collator"); + expect(layers[0].layout["text-field"][2][1]["case-sensitive"]).to.be + .false; + expect(layers[0].layout["text-field"][2][1]["diacritic-sensitive"]).to.be + .true; + expect(layers[0].layout["text-field"][2][1].locale).to.eql("tlh"); + }); + it("updates diacritic-insensitive collator in English", function () { + let layers = [ + { + layout: { + "text-field": [ + "let", + "diacriticInsensitiveCollator", + "", + ["var", "diacriticInsensitiveCollator"], + ], + }, + }, + ]; + Label.localizeLayers(layers, ["en-US"]); + expect(layers[0].layout["text-field"][2][0]).to.eql("collator"); + expect(layers[0].layout["text-field"][2][1]["case-sensitive"]).to.be + .false; + expect(layers[0].layout["text-field"][2][1]["diacritic-sensitive"]).to.be + .false; + expect(layers[0].layout["text-field"][2][1].locale).to.eql("en-US"); + }); + it("updates diacritic-insensitive collator in a language other than English", function () { + let layers = [ + { + layout: { + "text-field": [ + "let", + "diacriticInsensitiveCollator", + "", + ["var", "diacriticInsensitiveCollator"], + ], + }, + }, + ]; + Label.localizeLayers(layers, ["enm"]); + expect(layers[0].layout["text-field"][2][1]["diacritic-sensitive"]).to.be + .true; + }); + }); }); From cc8b28def77b79102894542ec61902af16692317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 3 Dec 2022 04:08:02 -0800 Subject: [PATCH 064/514] Install style specification --- package-lock.json | 128 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 129 insertions(+) diff --git a/package-lock.json b/package-lock.json index 000ed83ed..c411f3dbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "open": "^8.4.0" }, "devDependencies": { + "@maplibre/maplibre-gl-style-spec": "^17.0.1", "chai": "^4.3.7", "create-serve": "^1.0.1", "esbuild": "^0.15.15", @@ -149,6 +150,33 @@ "node": ">=6.0.0" } }, + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-17.0.1.tgz", + "integrity": "sha512-pohuxZke5fAJmY7g9EM7tQHjFXOegG58R66tTGrHvdndJOr8hTDUOdgkmq3wCNNOJL8dIf014RVhvPua53P2ZQ==", + "dev": true, + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.0", + "csscolorparser": "~1.0.2", + "json-stringify-pretty-compact": "^2.0.0", + "minimist": "^1.2.5", + "rw": "^1.3.3", + "sort-object": "^0.3.2" + }, + "bin": { + "gl-style-composite": "bin/gl-style-composite", + "gl-style-format": "bin/gl-style-format", + "gl-style-migrate": "bin/gl-style-migrate", + "gl-style-validate": "bin/gl-style-validate" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==", + "dev": true + }, "node_modules/@rushstack/ts-command-line": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.11.0.tgz", @@ -1987,6 +2015,12 @@ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, + "node_modules/json-stringify-pretty-compact": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz", + "integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==", + "dev": true + }, "node_modules/kdbush": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", @@ -2860,6 +2894,12 @@ "protocol-buffers-schema": "^3.3.1" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3088,6 +3128,37 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "node_modules/sort-asc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz", + "integrity": "sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-desc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz", + "integrity": "sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-object": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz", + "integrity": "sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA==", + "dev": true, + "dependencies": { + "sort-asc": "^0.1.0", + "sort-desc": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -3713,6 +3784,29 @@ "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", "dev": true }, + "@maplibre/maplibre-gl-style-spec": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-17.0.1.tgz", + "integrity": "sha512-pohuxZke5fAJmY7g9EM7tQHjFXOegG58R66tTGrHvdndJOr8hTDUOdgkmq3wCNNOJL8dIf014RVhvPua53P2ZQ==", + "dev": true, + "requires": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.0", + "csscolorparser": "~1.0.2", + "json-stringify-pretty-compact": "^2.0.0", + "minimist": "^1.2.5", + "rw": "^1.3.3", + "sort-object": "^0.3.2" + }, + "dependencies": { + "@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==", + "dev": true + } + } + }, "@rushstack/ts-command-line": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.11.0.tgz", @@ -4961,6 +5055,12 @@ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, + "json-stringify-pretty-compact": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz", + "integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==", + "dev": true + }, "kdbush": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", @@ -5638,6 +5738,12 @@ "protocol-buffers-schema": "^3.3.1" } }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5787,6 +5893,28 @@ } } }, + "sort-asc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz", + "integrity": "sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==", + "dev": true + }, + "sort-desc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz", + "integrity": "sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw==", + "dev": true + }, + "sort-object": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz", + "integrity": "sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA==", + "dev": true, + "requires": { + "sort-asc": "^0.1.0", + "sort-desc": "^0.1.1" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/package.json b/package.json index ac264fcb6..008bca188 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "open": "^8.4.0" }, "devDependencies": { + "@maplibre/maplibre-gl-style-spec": "^17.0.1", "chai": "^4.3.7", "create-serve": "^1.0.1", "esbuild": "^0.15.15", From c32b11a2d50cfd5058c88a0eb86ff7681e9718c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 3 Dec 2022 04:09:07 -0800 Subject: [PATCH 065/514] Test place label with gloss --- src/americana.js | 1 + src/constants/label.js | 126 +++++++++++++++++++++++++++++++- src/layer/place.js | 119 +----------------------------- test/spec/label.js | 159 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 284 insertions(+), 121 deletions(-) diff --git a/src/americana.js b/src/americana.js index 15e0f8ae8..275360b62 100644 --- a/src/americana.js +++ b/src/americana.js @@ -301,6 +301,7 @@ maplibregl.setRTLTextPlugin( true ); +window.maplibregl = maplibregl; export const map = (window.map = new maplibregl.Map({ container: "map", // container id hash: "map", diff --git a/src/constants/label.js b/src/constants/label.js index 34872434e..9277fd9b1 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -118,10 +118,134 @@ export function localizeLayers(layers, locales) { } } -// Placeholder to be resolved by buildLayers() +/** + * The name in the user's preferred language. + */ export const localizedName = [ "let", "localizedName", "", ["var", "localizedName"], ]; + +/** + * The name in the user's preferred language, followed by the name in the local language in parentheses if it differs. + */ +export const localizedNameWithLocalGloss = [ + "let", + "localizedName", + "", + "localizedCollator", + ["collator", {}], + "diacriticInsensitiveCollator", + ["collator", {}], + [ + "case", + [ + "==", + ["var", "localizedName"], + ["get", "name"], + ["var", "localizedCollator"], + ], + ["var", "localizedName"], + // If the name in the preferred language is the same as the name in the local language except for the omission of diacritics and/or the addition of a suffix (e.g., "City" in English), replace the common prefix with the local name. + [ + "all", + [ + "==", + ["slice", ["var", "localizedName"], 0, ["length", ["get", "name"]]], + ["get", "name"], + ["var", "diacriticInsensitiveCollator"], + ], + [ + "in", + [ + "slice", + // "Montreal" vs. "Montréal" + ["concat", ["var", "localizedName"], " "], + ["length", ["get", "name"]], + ["+", ["length", ["get", "name"]], 1], + ], + // "Quebec City" vs. "Québec", "Washington, D.C." vs. "Washington" + " ,", + ], + ], + [ + "concat", + ["get", "name"], + ["slice", ["var", "localizedName"], ["length", ["get", "name"]]], + ], + // If the name in the preferred language is the same as the name in the local language except for the omission of diacritics and/or the addition of a prefix (e.g., "City of" in English or "Ciudad de" in Spanish), replace the common suffix with the local name. + [ + "all", + [ + "==", + [ + "slice", + ["var", "localizedName"], + [ + "-", + ["length", ["var", "localizedName"]], + ["length", ["get", "name"]], + ], + ], + ["get", "name"], + ["var", "diacriticInsensitiveCollator"], + ], + [ + "==", + [ + "slice", + ["var", "localizedName"], + [ + "-", + [ + "-", + ["length", ["var", "localizedName"]], + ["length", ["get", "name"]], + ], + 1, + ], + [ + "-", + ["length", ["var", "localizedName"]], + ["length", ["get", "name"]], + ], + ], + " ", + ], + ], + [ + "concat", + [ + "slice", + ["var", "localizedName"], + 0, + [ + "-", + ["length", ["var", "localizedName"]], + ["length", ["get", "name"]], + ], + ], + ["get", "name"], + ], + // Gloss the name in the local language if it differs from the localized name. + [ + "format", + ["var", "localizedName"], + "\n", + "(\u200B", + { "font-scale": 0.8 }, + // GL JS lacks support for bidirectional isolating characters, so use a character from the localized name to insulate the parentheses from the embedded text's writing direction. Make it so small that GL JS doesn't bother rendering it. + ["concat", ["slice", ["var", "localizedName"], 0, 1], " "], + { "font-scale": 0.001 }, + ["get", "name"], + { "font-scale": 0.8 }, + ["concat", " ", ["slice", ["var", "localizedName"], 0, 1]], + { "font-scale": 0.001 }, + // A ZWSP prevents GL JS from combining this component with the preceding one, which would cause it to vanish along with the faux isolating character. + "\u200B)", + { "font-scale": 0.8 }, + ], + ], +]; diff --git a/src/layer/place.js b/src/layer/place.js index 280fb4cc8..201653964 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -181,124 +181,7 @@ export const city = { [11, 0.9], ], }, - "text-field": [ - "let", - "localizedName", - "", - "localizedCollator", - ["collator", {}], - "diacriticInsensitiveCollator", - ["collator", {}], - [ - "case", - [ - "==", - ["var", "localizedName"], - ["get", "name"], - ["var", "localizedCollator"], - ], - ["var", "localizedName"], - // If the name in the preferred language is the same as the name in the local language except for the omission of diacritics and/or the addition of a suffix (e.g., "City" in English), replace the common prefix with the local name. - [ - "all", - [ - "==", - ["slice", ["var", "localizedName"], 0, ["length", ["get", "name"]]], - ["get", "name"], - ["var", "diacriticInsensitiveCollator"], - ], - [ - "in", - [ - "slice", - // "Montreal" vs. "Montréal" - ["concat", ["var", "localizedName"], " "], - ["length", ["get", "name"]], - ["+", ["length", ["get", "name"]], 1], - ], - // "Quebec City" vs. "Québec", "Washington, D.C." vs. "Washington" - " ,", - ], - ], - [ - "concat", - ["get", "name"], - ["slice", ["var", "localizedName"], ["length", ["get", "name"]]], - ], - // If the name in the preferred language is the same as the name in the local language except for the omission of diacritics and/or the addition of a prefix (e.g., "City of" in English or "Ciudad de" in Spanish), replace the common suffix with the local name. - [ - "all", - [ - "==", - [ - "slice", - ["var", "localizedName"], - [ - "-", - ["length", ["var", "localizedName"]], - ["length", ["get", "name"]], - ], - ], - ["get", "name"], - ["var", "diacriticInsensitiveCollator"], - ], - [ - "==", - [ - "slice", - ["var", "localizedName"], - [ - "-", - [ - "-", - ["length", ["var", "localizedName"]], - ["length", ["get", "name"]], - ], - 1, - ], - [ - "-", - ["length", ["var", "localizedName"]], - ["length", ["get", "name"]], - ], - ], - " ", - ], - ], - [ - "concat", - [ - "slice", - ["var", "localizedName"], - 0, - [ - "-", - ["length", ["var", "localizedName"]], - ["length", ["get", "name"]], - ], - ], - ["get", "name"], - ], - // Gloss the name in the local language if it differs from the localized name. - [ - "format", - ["var", "localizedName"], - "\n", - "(\u200B", - { "font-scale": 0.8 }, - // GL JS lacks support for bidirectional isolating characters, so use a character from the localized name to insulate the parentheses from the embedded text's writing direction. Make it so small that GL JS doesn't bother rendering it. - ["concat", ["slice", ["var", "localizedName"], 0, 1], " "], - { "font-scale": 0.001 }, - ["get", "name"], - { "font-scale": 0.8 }, - ["concat", " ", ["slice", ["var", "localizedName"], 0, 1]], - { "font-scale": 0.001 }, - // A ZWSP prevents GL JS from combining this component with the preceding one, which would cause it to vanish along with the faux isolating character. - "\u200B)", - { "font-scale": 0.8 }, - ], - ], - ], + "text-field": Label.localizedNameWithLocalGloss, "text-anchor": "bottom", "text-variable-anchor": [ "bottom", diff --git a/test/spec/label.js b/test/spec/label.js index acdb42fa6..935f72ac7 100644 --- a/test/spec/label.js +++ b/test/spec/label.js @@ -2,6 +2,25 @@ import chai, { expect } from "chai"; import * as Label from "../../src/constants/label.js"; +import { expression } from "@maplibre/maplibre-gl-style-spec"; + +function localizedTextField(textField, locales) { + let layers = [ + { + layout: { + "text-field": textField, + }, + }, + ]; + Label.localizeLayers(layers, locales); + return layers[0].layout["text-field"]; +} + +function expressionContext(properties) { + return { + properties: () => properties, + }; +} describe("label", function () { describe("#getLanguageFromURL", function () { @@ -137,7 +156,7 @@ describe("label", function () { }, { layout: { - "text-field": Label.localizedName, + "text-field": [...Label.localizedName], }, }, ]; @@ -153,7 +172,7 @@ describe("label", function () { { "source-layer": "transportation_name", layout: { - "text-field": Label.localizedName, + "text-field": [...Label.localizedName], }, }, ]; @@ -223,4 +242,140 @@ describe("label", function () { .true; }); }); + + describe("#localizedName", function () { + let evaluatedExpression = (locales, properties) => + expression + .createExpression(localizedTextField([...Label.localizedName], ["en"])) + .value.expression.evaluate(expressionContext(properties)); + + it("is empty by default", function () { + expect( + expression + .createExpression(Label.localizedName) + .value.expression.evaluate( + expressionContext({ + name: "Null Island", + }) + ) + ).to.be.eql(""); + }); + it("localizes to preferred language", function () { + expect( + evaluatedExpression(["en"], { + "name:en": "Null Island", + name: "Insula Nullius", + }) + ).to.be.eql("Null Island"); + }); + }); + + describe("#localizedNameWithLocalGloss", function () { + let evaluatedExpression = (locales, properties) => + expression + .createExpression( + localizedTextField([...Label.localizedNameWithLocalGloss], locales) + ) + .value.expression.evaluate(expressionContext(properties)); + + let evaluatedLabelAndGloss = (locales, properties) => { + let evaluated = evaluatedExpression(locales, properties); + if (typeof evaluated === "string") { + return [evaluated, null]; + } + return [evaluated.sections[0].text, evaluated.sections[4].text]; + }; + + let expectGloss = ( + locale, + localized, + local, + expectedLabel, + expectedGloss + ) => { + let properties = { + name: local, + }; + properties[`name:${locale}`] = localized; + expect(evaluatedLabelAndGloss([locale], properties)).to.be.deep.equal([ + expectedLabel, + expectedGloss, + ]); + }; + + it("puts an unlocalized name by itself", function () { + expect( + evaluatedExpression(["en"], { + name: "Null Island", + }) + ).to.be.eql("Null Island"); + }); + it("glosses an anglicized name with the local name", function () { + let evaluated = evaluatedExpression(["en"], { + "name:en": "Null Island", + name: "Insula Nullius", + }); + + expect(evaluated.sections.length).to.be.eql(7); + expect(evaluated.sections[0].text).to.be.eql("Null Island"); + expect(evaluated.sections[1].text).to.be.eql("\n"); + expect(evaluated.sections[2].text).to.be.eql("(\u200B"); + expect(evaluated.sections[3].text).to.be.eql("Null Island"[0] + " "); + expect(evaluated.sections[4].text).to.be.eql("Insula Nullius"); + expect(evaluated.sections[5].text).to.be.eql(" " + "Null Island"[0]); + expect(evaluated.sections[6].text).to.be.eql("\u200B)"); + + expect(evaluated.sections[3].scale).to.be.below(0.1); + expect(evaluated.sections[4].scale).to.be.below(1); + expect(evaluated.sections[5].scale).to.be.below(0.1); + }); + it("deduplicates matching anglicized and local names", function () { + expectGloss("en", "Null Island", "Null Island", "Null Island", null); + expectGloss("en", "Null Island", "NULL Island", "Null Island", null); + expectGloss("en", "Montreal", "Montréal", "Montréal", null); + expectGloss("en", "Quebec City", "Québec", "Québec City", null); + expectGloss("en", "Da Nang", "Đà Nẵng", "Đà Nẵng", null); + expectGloss("en", "Nūll Island", "Ñüłl Íşlåńđ", "Ñüłl Íşlåńđ", null); + expectGloss("en", "New York City", "New York", "New York City", null); + expectGloss( + "en", + "Washington, D.C.", + "Washington", + "Washington, D.C.", + null + ); + expectGloss( + "en", + "Santiago de Querétaro", + "Querétaro", + "Santiago de Querétaro", + null + ); + + // Suboptimal but expected cases + + expectGloss("en", "Córdobaaa", "Córdoba", "Córdobaaa", "Córdoba"); + expectGloss( + "en", + "Derry", + "Derry/Londonderry", + "Derry", + "Derry/Londonderry" + ); + expectGloss("en", "L’Aquila", "L'Aquila", "L’Aquila", "L'Aquila"); + }); + it("glosses non-English localized name with lookalike local name", function () { + expectGloss( + "es", + "Los Ángeles", + "Los Angeles", + "Los Ángeles", + "Los Angeles" + ); + expectGloss("es", "Montreal", "Montréal", "Montreal", "Montréal"); + expectGloss("es", "Quebec", "Québec", "Quebec", "Québec"); + expectGloss("pl", "Ryga", "Rīga", "Ryga", "Rīga"); + expectGloss("pl", "Jurmała", "Jūrmala", "Jurmała", "Jūrmala"); + }); + }); }); From 8025a7d2c400865e3c111ae2952aca00232779e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 3 Dec 2022 04:42:20 -0800 Subject: [PATCH 066/514] Refactored glossy city label expression --- src/constants/label.js | 183 ++++++++++++++++++++++------------------- 1 file changed, 100 insertions(+), 83 deletions(-) diff --git a/src/constants/label.js b/src/constants/label.js index 9277fd9b1..c52ee58ae 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -129,7 +129,77 @@ export const localizedName = [ ]; /** - * The name in the user's preferred language, followed by the name in the local language in parentheses if it differs. + * Returns an expression that tests whether the target has the given prefix, + * respecting word boundaries. + */ +function startsWithExpression(target, candidatePrefix, collator) { + // "Quebec City" vs. "Québec", "Washington, D.C." vs. "Washington" + let wordBoundaries = " ,"; + return [ + "all", + [ + "==", + ["slice", target, 0, ["length", candidatePrefix]], + candidatePrefix, + collator, + ], + [ + "in", + [ + "slice", + // Pad the target in case the prefix matches exactly. + // "Montreal " vs. "Montréal" + ["concat", target, wordBoundaries[0]], + ["length", candidatePrefix], + ["+", ["length", candidatePrefix], 1], + ], + wordBoundaries, + ], + ]; +} + +function overwritePrefixExpression(target, newPrefix) { + return ["concat", newPrefix, ["slice", target, ["length", newPrefix]]]; +} + +/** + * Returns an expression that tests whether the target has the given suffix, + * respecting word boundaries. + */ +function endsWithExpression(target, candidateSuffix, collator) { + let wordBoundary = " "; + return [ + "all", + [ + "==", + ["slice", target, ["-", ["length", target], ["length", candidateSuffix]]], + candidateSuffix, + collator, + ], + [ + "==", + [ + "slice", + target, + ["-", ["-", ["length", target], ["length", candidateSuffix]], 1], + ["-", ["length", target], ["length", candidateSuffix]], + ], + wordBoundary, + ], + ]; +} + +function overwriteSuffixExpression(target, newSuffix) { + return [ + "concat", + ["slice", target, 0, ["-", ["length", target], ["length", newSuffix]]], + newSuffix, + ]; +} + +/** + * The name in the user's preferred language, followed by the name in the local + * language in parentheses if it differs. */ export const localizedNameWithLocalGloss = [ "let", @@ -141,109 +211,56 @@ export const localizedNameWithLocalGloss = [ ["collator", {}], [ "case", + // If the name in the preferred and local languages match exactly... [ "==", ["var", "localizedName"], ["get", "name"], ["var", "localizedCollator"], ], + // ...just pick one. ["var", "localizedName"], - // If the name in the preferred language is the same as the name in the local language except for the omission of diacritics and/or the addition of a suffix (e.g., "City" in English), replace the common prefix with the local name. - [ - "all", - [ - "==", - ["slice", ["var", "localizedName"], 0, ["length", ["get", "name"]]], - ["get", "name"], - ["var", "diacriticInsensitiveCollator"], - ], - [ - "in", - [ - "slice", - // "Montreal" vs. "Montréal" - ["concat", ["var", "localizedName"], " "], - ["length", ["get", "name"]], - ["+", ["length", ["get", "name"]], 1], - ], - // "Quebec City" vs. "Québec", "Washington, D.C." vs. "Washington" - " ,", - ], - ], - [ - "concat", + // If the name in the preferred language is the same as the name in the + // local language except for the omission of diacritics and/or the addition + // of a suffix (e.g., "City" in English)... + startsWithExpression( + ["var", "localizedName"], ["get", "name"], - ["slice", ["var", "localizedName"], ["length", ["get", "name"]]], - ], - // If the name in the preferred language is the same as the name in the local language except for the omission of diacritics and/or the addition of a prefix (e.g., "City of" in English or "Ciudad de" in Spanish), replace the common suffix with the local name. - [ - "all", - [ - "==", - [ - "slice", - ["var", "localizedName"], - [ - "-", - ["length", ["var", "localizedName"]], - ["length", ["get", "name"]], - ], - ], - ["get", "name"], - ["var", "diacriticInsensitiveCollator"], - ], - [ - "==", - [ - "slice", - ["var", "localizedName"], - [ - "-", - [ - "-", - ["length", ["var", "localizedName"]], - ["length", ["get", "name"]], - ], - 1, - ], - [ - "-", - ["length", ["var", "localizedName"]], - ["length", ["get", "name"]], - ], - ], - " ", - ], - ], - [ - "concat", - [ - "slice", - ["var", "localizedName"], - 0, - [ - "-", - ["length", ["var", "localizedName"]], - ["length", ["get", "name"]], - ], - ], + ["var", "diacriticInsensitiveCollator"] + ), + // ...then replace the common prefix with the local name. + overwritePrefixExpression(["var", "localizedName"], ["get", "name"]), + // If the name in the preferred language is the same as the name in the + // local language except for the omission of diacritics and/or the addition + // of a prefix (e.g., "City of" in English or "Ciudad de" in Spanish)... + endsWithExpression( + ["var", "localizedName"], ["get", "name"], - ], - // Gloss the name in the local language if it differs from the localized name. + ["var", "diacriticInsensitiveCollator"] + ), + // ...then replace the common suffix with the local name. + overwriteSuffixExpression(["var", "localizedName"], ["get", "name"]), + // Otherwise, gloss the name in the local language if it differs from the + // localized name. [ "format", ["var", "localizedName"], "\n", "(\u200B", { "font-scale": 0.8 }, - // GL JS lacks support for bidirectional isolating characters, so use a character from the localized name to insulate the parentheses from the embedded text's writing direction. Make it so small that GL JS doesn't bother rendering it. + // GL JS lacks support for bidirectional isolating characters, so use a + // character from the localized name to insulate the parentheses from the + // embedded text's writing direction. Make it so small that GL JS doesn't + // bother rendering it. ["concat", ["slice", ["var", "localizedName"], 0, 1], " "], { "font-scale": 0.001 }, ["get", "name"], { "font-scale": 0.8 }, ["concat", " ", ["slice", ["var", "localizedName"], 0, 1]], { "font-scale": 0.001 }, - // A ZWSP prevents GL JS from combining this component with the preceding one, which would cause it to vanish along with the faux isolating character. + // A ZWSP prevents GL JS from combining this component with the preceding + // one, which would cause it to vanish along with the faux isolating + // character. "\u200B)", { "font-scale": 0.8 }, ], From d56371d032c983031b00bdff661b4337eb291f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Mon, 5 Dec 2022 13:58:47 -0800 Subject: [PATCH 067/514] Nixed name:latin fallback from English As currently implemented, name:latin is generally unsuitable for display. Removing this fallback also improves consistency between user-preferred languages. Also updated the taginfo project file to note the new city label glosses. --- scripts/taginfo_template.json | 2 +- src/constants/label.js | 3 --- test/spec/label.js | 9 --------- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/scripts/taginfo_template.json b/scripts/taginfo_template.json index 3ce35e689..f9ecf7d98 100644 --- a/scripts/taginfo_template.json +++ b/scripts/taginfo_template.json @@ -13,7 +13,7 @@ { "key": "name", "object_types": ["node", "way", "relation", "area"], - "description": "Labels fall back to the local language if none of the user-preferred languages is available." + "description": "Labels fall back to the local language if none of the user-preferred languages is available. City labels include the local-language name in parentheses." }, { "key": "capital", diff --git a/src/constants/label.js b/src/constants/label.js index c52ee58ae..504174c65 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -40,9 +40,6 @@ export function getLocales() { * colon syntax. */ export function getLocalizedNameExpression(locales, includesLegacyFields) { - if (locales.at(-1) === "en") { - locales.push("latin"); - } let nameFields = [ ...locales.flatMap((l) => { let fields = [`name:${l}`]; diff --git a/test/spec/label.js b/test/spec/label.js index 935f72ac7..282888ba1 100644 --- a/test/spec/label.js +++ b/test/spec/label.js @@ -89,15 +89,6 @@ describe("label", function () { ["get", "name"], ]); }); - it("falls back from English to Romanization", function () { - expect(Label.getLocalizedNameExpression(["en-US", "en"], false)).to.eql([ - "coalesce", - ["get", "name:en-US"], - ["get", "name:en"], - ["get", "name:latin"], - ["get", "name"], - ]); - }); it("includes legacy fields", function () { expect( Label.getLocalizedNameExpression(["en-US", "en", "de"], true) From aade326fe933efa935a04ee3d2a03a354e85947a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 2 Dec 2022 01:00:55 -0800 Subject: [PATCH 068/514] Added Indonesian national road shield --- doc-img/shield_map_world.svg | 1 + icons/shield_id_national.svg | 5 +++++ src/js/shield_defs.js | 13 +++++++++++++ src/shieldtest.js | 1 + 4 files changed, 20 insertions(+) create mode 100644 icons/shield_id_national.svg diff --git a/doc-img/shield_map_world.svg b/doc-img/shield_map_world.svg index 08ad8a453..829f19352 100644 --- a/doc-img/shield_map_world.svg +++ b/doc-img/shield_map_world.svg @@ -127,6 +127,7 @@ See the end of this file for a list of available jurisdictions and their codes. .bd, .cn, .hk, +.id, .ir, .iq, .jp, diff --git a/icons/shield_id_national.svg b/icons/shield_id_national.svg new file mode 100644 index 000000000..30459bade --- /dev/null +++ b/icons/shield_id_national.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index eff4414e0..f2e649e3a 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -2971,6 +2971,19 @@ export function loadShields(shieldImages) { // Hong Kong shields["HK"] = escutcheonShieldYellow; + // Indonesia + shields["ID:national"] = { + backgroundImage: [shieldImages.shield_id_national], + textLayoutConstraint: ShieldText.ellipseTextConstraint, + textColor: Color.shields.black, + padding: { + left: 3, + right: 3, + top: 4, + bottom: 2, + }, + }; + // Iran shields["ir:freeways"] = roundedRectShield( Color.shields.blue, diff --git a/src/shieldtest.js b/src/shieldtest.js index 666b721a1..f3ed838c4 100644 --- a/src/shieldtest.js +++ b/src/shieldtest.js @@ -66,6 +66,7 @@ let networks = [ "JP:prefectural", "PK:national", + "ID:national", "AU:QLD:MR", "GR:national", From b13289ee93110fa1e0cdce49a31c9cdc380d3cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 2 Dec 2022 08:58:08 -0800 Subject: [PATCH 069/514] Indonesian toll road shield --- src/js/shield_defs.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index f2e649e3a..9339c2537 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -2983,6 +2983,7 @@ export function loadShields(shieldImages) { bottom: 2, }, }; + shields["ID:toll"] = banneredShield(shields["ID:national"], ["TOL"]); // Iran shields["ir:freeways"] = roundedRectShield( From a58b4feef9af209995650bd65ae3b9ea562411ee Mon Sep 17 00:00:00 2001 From: Zeke Farwell Date: Mon, 5 Dec 2022 22:02:33 -0500 Subject: [PATCH 070/514] Change place label halos from pure white, to background fill color --- src/layer/park.js | 8 ++++---- src/layer/place.js | 29 +++++++++++++++-------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/layer/park.js b/src/layer/park.js index 0ae036420..34a8fef72 100644 --- a/src/layer/park.js +++ b/src/layer/park.js @@ -37,8 +37,8 @@ export const label = { filter: ["has", "rank"], paint: { "text-color": Color.parkLabel, - "text-halo-blur": 1, - "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-blur": 0.5, + "text-halo-color": Color.backgroundFill, "text-halo-width": 1, }, layout: { @@ -88,8 +88,8 @@ export const parkLabel = { filter: ["==", ["get", "class"], "park"], paint: { "text-color": Color.parkLabel, - "text-halo-blur": 1, - "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-blur": 0.5, + "text-halo-color": Color.backgroundFill, "text-halo-width": 1, }, layout: { diff --git a/src/layer/place.js b/src/layer/place.js index 201653964..c71cb16bb 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -1,8 +1,9 @@ import * as Label from "../constants/label.js"; +import * as Color from "../constants/color.js"; const cityLabelPaint = { "text-color": "#444", - "text-halo-color": "rgb(255,255,255)", + "text-halo-color": Color.backgroundFill, "text-halo-width": 2, "text-halo-blur": 0.5, }; @@ -210,7 +211,7 @@ export const state = { type: "symbol", paint: { "text-color": "hsl(45, 6%, 10%)", - "text-halo-color": "rgb(255,255,255)", + "text-halo-color": Color.backgroundFill, "text-halo-width": 2, "text-halo-blur": 0.5, }, @@ -250,8 +251,8 @@ export const countryOther = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 1, - "text-halo-color": "rgba(255,255,255,0.8)", + "text-halo-blur": 0.5, + "text-halo-color": Color.backgroundFill, "text-halo-width": 2.0, }, filter: [ @@ -279,9 +280,9 @@ export const country3 = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 1, - "text-halo-color": "rgba(255,255,255,0.8)", - "text-halo-width": 2.0, + "text-halo-blur": 0.5, + "text-halo-color": Color.backgroundFill, + "text-halo-width": 2, }, filter: [ "all", @@ -309,9 +310,9 @@ export const country2 = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 1, - "text-halo-color": "rgba(255,255,255,1)", - "text-halo-width": 3.0, + "text-halo-blur": 0.5, + "text-halo-color": Color.backgroundFill, + "text-halo-width": 2, }, filter: [ "all", @@ -339,9 +340,9 @@ export const country1 = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 1, - "text-halo-color": "rgba(255,255,255,1)", - "text-halo-width": 3.0, + "text-halo-blur": 0.5, + "text-halo-color": Color.backgroundFill, + "text-halo-width": 2, }, filter: [ "all", @@ -377,7 +378,7 @@ export const continent = { type: "symbol", paint: { "text-color": "#633", - "text-halo-color": "rgba(255,255,255,0.7)", + "text-halo-color": Color.backgroundFill, "text-halo-width": 1, }, filter: ["==", ["get", "class"], "continent"], From 4fe190a8600e57f912c18ea949fbcee483121d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 6 Dec 2022 01:42:29 -0800 Subject: [PATCH 071/514] Fixed syntax error in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41104b1fa..e7cab6f6d 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,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 builds) 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/). To explore how the style depicts various features, click the Samples button. 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. From 4200d444e846092a7bea4364bbaf638704328e7b Mon Sep 17 00:00:00 2001 From: Zeke Farwell Date: Tue, 6 Dec 2022 10:45:04 -0500 Subject: [PATCH 072/514] make large label halos thicker and interpolate thickness with zoom. also set halo blurs to zero. --- src/layer/place.js | 50 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/layer/place.js b/src/layer/place.js index c71cb16bb..34e937302 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -5,7 +5,7 @@ const cityLabelPaint = { "text-color": "#444", "text-halo-color": Color.backgroundFill, "text-halo-width": 2, - "text-halo-blur": 0.5, + "text-halo-blur": 0, }; const cityIcon = [ @@ -212,8 +212,13 @@ export const state = { paint: { "text-color": "hsl(45, 6%, 10%)", "text-halo-color": Color.backgroundFill, - "text-halo-width": 2, - "text-halo-blur": 0.5, + "text-halo-width": [ + 'interpolate', ['exponential', 1.2], + ['zoom'], + 3, 1.5, + 7, 2.7, + ], + "text-halo-blur": 0, }, filter: ["==", ["get", "class"], "state"], layout: { @@ -251,9 +256,14 @@ export const countryOther = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 0.5, + "text-halo-blur": 0, "text-halo-color": Color.backgroundFill, - "text-halo-width": 2.0, + "text-halo-width": [ + 'interpolate', ['linear'], + ['zoom'], + 3, 1.6, + 7, 2.7, + ], }, filter: [ "all", @@ -280,9 +290,14 @@ export const country3 = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 0.5, + "text-halo-blur": 0, "text-halo-color": Color.backgroundFill, - "text-halo-width": 2, + "text-halo-width": [ + 'interpolate', ['linear'], + ['zoom'], + 3, 1.8, + 7, 3, + ], }, filter: [ "all", @@ -310,9 +325,14 @@ export const country2 = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 0.5, + "text-halo-blur": 0, "text-halo-color": Color.backgroundFill, - "text-halo-width": 2, + "text-halo-width": [ + 'interpolate', ['linear'], + ['zoom'], + 2, 2, + 5, 3, + ], }, filter: [ "all", @@ -326,7 +346,7 @@ export const country2 = { stops: [ [2, 11], [5, 17], - ], + ], }, "text-field": Label.localizedName, "text-max-width": 6.25, @@ -340,9 +360,15 @@ export const country1 = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 0.5, + "text-halo-blur": 0, "text-halo-color": Color.backgroundFill, - "text-halo-width": 2, + "text-halo-width": [ + 'interpolate', ['linear'], + ['zoom'], + 1, 2, + 4, 3, + 6, 3, + ], }, filter: [ "all", From 25ed0bce4224c25f8e8a06c633e56974c0f5e83d Mon Sep 17 00:00:00 2001 From: Zeke Farwell Date: Thu, 8 Dec 2022 11:59:08 -0500 Subject: [PATCH 073/514] Change low zoom label halos from opaque to partially transparent --- src/constants/color.js | 3 +- src/layer/place.js | 87 ++++++++++++++++++++++-------------------- 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/src/constants/color.js b/src/constants/color.js index 88009eeb2..e5836d465 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -1,4 +1,5 @@ -export const backgroundFill = "#faf6f2"; +export const backgroundHsl = "30, 44%, 96%"; +export const backgroundFill = `hsl(${backgroundHsl})`; export const waterFill = "hsl(211, 42%, 70%)"; export const waterLine = "hsl(211, 73%, 78%)"; diff --git a/src/layer/place.js b/src/layer/place.js index 34e937302..4f0996b43 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -1,11 +1,23 @@ import * as Label from "../constants/label.js"; import * as Color from "../constants/color.js"; +const labelHaloColor = [ + "interpolate", + ["linear"], + ["zoom"], + 4, + `hsla(${Color.backgroundHsl}, 0.8)`, + 5, + `hsl(${Color.backgroundHsl})`, +]; + +const labelHaloBlur = ["interpolate", ["linear"], ["zoom"], 4, 0.5, 5, 0]; + const cityLabelPaint = { "text-color": "#444", - "text-halo-color": Color.backgroundFill, + "text-halo-color": labelHaloColor, "text-halo-width": 2, - "text-halo-blur": 0, + "text-halo-blur": labelHaloBlur, }; const cityIcon = [ @@ -211,14 +223,17 @@ export const state = { type: "symbol", paint: { "text-color": "hsl(45, 6%, 10%)", - "text-halo-color": Color.backgroundFill, + "text-halo-color": labelHaloColor, "text-halo-width": [ - 'interpolate', ['exponential', 1.2], - ['zoom'], - 3, 1.5, - 7, 2.7, + "interpolate", + ["exponential", 1.2], + ["zoom"], + 3, + 1.5, + 6, + 2.5, ], - "text-halo-blur": 0, + "text-halo-blur": labelHaloBlur, }, filter: ["==", ["get", "class"], "state"], layout: { @@ -256,14 +271,9 @@ export const countryOther = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 0, - "text-halo-color": Color.backgroundFill, - "text-halo-width": [ - 'interpolate', ['linear'], - ['zoom'], - 3, 1.6, - 7, 2.7, - ], + "text-halo-blur": 0.5, + "text-halo-color": labelHaloColor, + "text-halo-width": ["interpolate", ["linear"], ["zoom"], 3, 1.5, 7, 2.5], }, filter: [ "all", @@ -290,14 +300,9 @@ export const country3 = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 0, - "text-halo-color": Color.backgroundFill, - "text-halo-width": [ - 'interpolate', ['linear'], - ['zoom'], - 3, 1.8, - 7, 3, - ], + "text-halo-blur": labelHaloBlur, + "text-halo-color": labelHaloColor, + "text-halo-width": ["interpolate", ["linear"], ["zoom"], 3, 1.5, 7, 2.5], }, filter: [ "all", @@ -325,14 +330,9 @@ export const country2 = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 0, - "text-halo-color": Color.backgroundFill, - "text-halo-width": [ - 'interpolate', ['linear'], - ['zoom'], - 2, 2, - 5, 3, - ], + "text-halo-blur": labelHaloBlur, + "text-halo-color": labelHaloColor, + "text-halo-width": ["interpolate", ["linear"], ["zoom"], 1, 1, 5, 2.4], }, filter: [ "all", @@ -346,7 +346,7 @@ export const country2 = { stops: [ [2, 11], [5, 17], - ], + ], }, "text-field": Label.localizedName, "text-max-width": 6.25, @@ -360,14 +360,18 @@ export const country1 = { type: "symbol", paint: { "text-color": "#334", - "text-halo-blur": 0, - "text-halo-color": Color.backgroundFill, + "text-halo-blur": labelHaloBlur, + "text-halo-color": labelHaloColor, "text-halo-width": [ - 'interpolate', ['linear'], - ['zoom'], - 1, 2, - 4, 3, - 6, 3, + "interpolate", + ["linear"], + ["zoom"], + 1, + 1, + 4, + 2.5, + 6, + 3, ], }, filter: [ @@ -404,7 +408,8 @@ export const continent = { type: "symbol", paint: { "text-color": "#633", - "text-halo-color": Color.backgroundFill, + "text-halo-color": labelHaloColor, + "text-halo-blur": labelHaloBlur, "text-halo-width": 1, }, filter: ["==", ["get", "class"], "continent"], From ee224fe204e6e269c333c235cacf86da08544036 Mon Sep 17 00:00:00 2001 From: Zeke Farwell Date: Thu, 8 Dec 2022 13:05:05 -0500 Subject: [PATCH 074/514] change park label halo to slightly greenish off white for a subtler effect --- src/constants/color.js | 1 + src/layer/park.js | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/constants/color.js b/src/constants/color.js index e5836d465..84965f5ec 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -15,6 +15,7 @@ export const borderCasing = `hsl(${hueBorderCasing}, 35%, 86%)`; export const parkFill = "hsl(136, 41%, 89%)"; export const parkOutline = "hsl(136, 41%, 79%)"; export const parkLabel = "hsl(136, 71%, 29%)"; +export const parkLabelHalo = "hsl(90, 27%, 94%)"; export const airportFill = "hsl(250, 41%, 95%)"; export const airportOutline = "hsl(250, 41%, 79%)"; diff --git a/src/layer/park.js b/src/layer/park.js index 34a8fef72..aaf34cf1f 100644 --- a/src/layer/park.js +++ b/src/layer/park.js @@ -37,8 +37,8 @@ export const label = { filter: ["has", "rank"], paint: { "text-color": Color.parkLabel, - "text-halo-blur": 0.5, - "text-halo-color": Color.backgroundFill, + "text-halo-blur": 1, + "text-halo-color": Color.parkLabelHalo, "text-halo-width": 1, }, layout: { @@ -88,8 +88,8 @@ export const parkLabel = { filter: ["==", ["get", "class"], "park"], paint: { "text-color": Color.parkLabel, - "text-halo-blur": 0.5, - "text-halo-color": Color.backgroundFill, + "text-halo-blur": 1, + "text-halo-color": Color.parkLabelHalo, "text-halo-width": 1, }, layout: { From 39010b72b98fba94963bad6bf9d88ca4956e826b Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Thu, 8 Dec 2022 23:23:21 -0500 Subject: [PATCH 075/514] Display language codes --- src/americana.js | 3 +++ src/constants/label.js | 38 ++++++++++++++++++++++++++++++++++++++ src/index.html | 2 +- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/americana.js b/src/americana.js index 275360b62..3124a5891 100644 --- a/src/americana.js +++ b/src/americana.js @@ -346,6 +346,7 @@ if (config.ATTRIBUTION_TEXT != undefined) { } map.addControl(new maplibregl.AttributionControl(attributionConfig)); +map.addControl(Label.langLabel, "bottom-right"); if (config.ATTRIBUTION_LOGO != undefined) { document.getElementById("attribution-logo").innerHTML = @@ -363,3 +364,5 @@ OpenMapTilesSamples.forEach((sample, i) => { map.addControl(sampleControl, "bottom-left"); map.getCanvas().focus(); + +Label.getLocales(); diff --git a/src/constants/label.js b/src/constants/label.js index c52ee58ae..6a754102f 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -1,5 +1,42 @@ "use strict"; +var langField = document.createElement("div"); +langField.id = "language-field"; + +/** + * Label for displaying the current language being used + */ +class LanguageControl { + onAdd(map) { + this._map = map; + this._container = langField; + this._container.style.textAlign = "right"; + this._container.style.paddingRight = "5px"; + this._container.style.paddingLeft = "5px"; + this._container.style.color = "#444"; + this._container.style.backgroundColor = "#ffffff80"; + this._container.style.float = "right"; + this._container.style.display = "table"; + this._container.textContent = ""; + return this._container; + } + + onRemove() { + this._container.parentNode.removeChild(this._container); + this._map = undefined; + } +} + +export var langLabel = new LanguageControl(); + +function displayLocales(localeSet) { + let languageField = document.getElementById("language-field"); + if (languageField) { + // console.log(localeSet); + languageField.textContent = Array.from(localeSet).join(", "); + } +} + /** * Returns a list of languages as a comma-delimited string from the given URL hash. */ @@ -27,6 +64,7 @@ export function getLocales() { components.pop(); } } + displayLocales(localeSet); return locales; } diff --git a/src/index.html b/src/index.html index aab26f7c6..b7feb0cf4 100644 --- a/src/index.html +++ b/src/index.html @@ -12,7 +12,6 @@ margin: 0; padding: 0; } - #map { position: absolute; top: 0; @@ -35,6 +34,7 @@
+
Date: Thu, 8 Dec 2022 23:31:44 -0500 Subject: [PATCH 076/514] Remove unneeded div --- src/index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.html b/src/index.html index b7feb0cf4..04fd4f76b 100644 --- a/src/index.html +++ b/src/index.html @@ -34,7 +34,6 @@
-
Date: Thu, 8 Dec 2022 23:32:17 -0500 Subject: [PATCH 077/514] Restore original line spacing --- src/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.html b/src/index.html index 04fd4f76b..aab26f7c6 100644 --- a/src/index.html +++ b/src/index.html @@ -12,6 +12,7 @@ margin: 0; padding: 0; } + #map { position: absolute; top: 0; From ace77d1c113111c08ed341cfbc38d5c744c43e88 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Thu, 8 Dec 2022 23:41:32 -0500 Subject: [PATCH 078/514] Separate DOM code from utility classes --- src/americana.js | 6 ++++-- src/constants/label.js | 38 -------------------------------------- src/js/language_label.js | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 40 deletions(-) create mode 100644 src/js/language_label.js diff --git a/src/americana.js b/src/americana.js index 3124a5891..a4d692eea 100644 --- a/src/americana.js +++ b/src/americana.js @@ -24,6 +24,8 @@ import * as lyrBuilding from "./layer/building.js"; import * as lyrHighwayExit from "./layer/highway_exit.js"; import * as lyrFerry from "./layer/ferry.js"; +import * as languageLabel from "./js/language_label.js"; + import * as maplibregl from "maplibre-gl"; import "maplibre-gl/dist/maplibre-gl.css"; import * as search from "./search.js"; @@ -346,7 +348,7 @@ if (config.ATTRIBUTION_TEXT != undefined) { } map.addControl(new maplibregl.AttributionControl(attributionConfig)); -map.addControl(Label.langLabel, "bottom-right"); +map.addControl(languageLabel.label, "bottom-right"); if (config.ATTRIBUTION_LOGO != undefined) { document.getElementById("attribution-logo").innerHTML = @@ -365,4 +367,4 @@ map.addControl(sampleControl, "bottom-left"); map.getCanvas().focus(); -Label.getLocales(); +languageLabel.displayLocales(Label.getLocales()); diff --git a/src/constants/label.js b/src/constants/label.js index 6a754102f..c52ee58ae 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -1,42 +1,5 @@ "use strict"; -var langField = document.createElement("div"); -langField.id = "language-field"; - -/** - * Label for displaying the current language being used - */ -class LanguageControl { - onAdd(map) { - this._map = map; - this._container = langField; - this._container.style.textAlign = "right"; - this._container.style.paddingRight = "5px"; - this._container.style.paddingLeft = "5px"; - this._container.style.color = "#444"; - this._container.style.backgroundColor = "#ffffff80"; - this._container.style.float = "right"; - this._container.style.display = "table"; - this._container.textContent = ""; - return this._container; - } - - onRemove() { - this._container.parentNode.removeChild(this._container); - this._map = undefined; - } -} - -export var langLabel = new LanguageControl(); - -function displayLocales(localeSet) { - let languageField = document.getElementById("language-field"); - if (languageField) { - // console.log(localeSet); - languageField.textContent = Array.from(localeSet).join(", "); - } -} - /** * Returns a list of languages as a comma-delimited string from the given URL hash. */ @@ -64,7 +27,6 @@ export function getLocales() { components.pop(); } } - displayLocales(localeSet); return locales; } diff --git a/src/js/language_label.js b/src/js/language_label.js new file mode 100644 index 000000000..cbf3739fb --- /dev/null +++ b/src/js/language_label.js @@ -0,0 +1,38 @@ +"use strict"; + +var langField = document.createElement("div"); +langField.id = "language-field"; + +/** + * Label for displaying the current language being used + */ +class LanguageControl { + onAdd(map) { + this._map = map; + this._container = langField; + this._container.style.textAlign = "right"; + this._container.style.paddingRight = "5px"; + this._container.style.paddingLeft = "5px"; + this._container.style.color = "#444"; + this._container.style.backgroundColor = "#ffffff80"; + this._container.style.float = "right"; + this._container.style.display = "table"; + this._container.textContent = ""; + return this._container; + } + + onRemove() { + this._container.parentNode.removeChild(this._container); + this._map = undefined; + } +} + +export var label = new LanguageControl(); + +export function displayLocales(localeSet) { + let languageField = document.getElementById("language-field"); + if (languageField) { + // console.log(localeSet); + languageField.textContent = Array.from(localeSet).join(", "); + } +} From 28dc04f7e11e485fccc15e829059c5b175ddc782 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Thu, 8 Dec 2022 23:44:58 -0500 Subject: [PATCH 079/514] Remove unneeded code --- src/js/language_label.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index cbf3739fb..fc668a366 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -30,9 +30,6 @@ class LanguageControl { export var label = new LanguageControl(); export function displayLocales(localeSet) { - let languageField = document.getElementById("language-field"); - if (languageField) { - // console.log(localeSet); - languageField.textContent = Array.from(localeSet).join(", "); - } + document.getElementById("language-field").textContent = + Array.from(localeSet).join(", "); } From 991f7290cd2d1fc5756c1c58a7f5fdb1278d091b Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Thu, 8 Dec 2022 23:48:28 -0500 Subject: [PATCH 080/514] Ensure label updates on hash refresh --- src/americana.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/americana.js b/src/americana.js index a4d692eea..c15989765 100644 --- a/src/americana.js +++ b/src/americana.js @@ -325,6 +325,7 @@ map.on("styleimagemissing", function (e) { window.addEventListener("languagechange", (event) => { console.log(`Changed to ${navigator.languages}`); map.setStyle(buildStyle()); + languageLabel.displayLocales(Label.getLocales()); }); window.addEventListener("hashchange", (event) => { @@ -334,6 +335,7 @@ window.addEventListener("hashchange", (event) => { if (oldLanguage !== newLanguage) { console.log(`Changed to ${newLanguage}`); map.setStyle(buildStyle()); + languageLabel.displayLocales(Label.getLocales()); } }); From df867e24e6aada7249a67e8e2ee128806486b709 Mon Sep 17 00:00:00 2001 From: Zeke Farwell Date: Fri, 9 Dec 2022 11:00:46 -0500 Subject: [PATCH 081/514] simplify color constants and label halo expression --- src/constants/color.js | 4 ++-- src/layer/place.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/constants/color.js b/src/constants/color.js index 84965f5ec..95a27eee7 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -1,5 +1,5 @@ -export const backgroundHsl = "30, 44%, 96%"; -export const backgroundFill = `hsl(${backgroundHsl})`; +export const backgroundFill = `hsl(30, 44%, 96%)`; +export const backgroundFillTranslucent = `hsla(30, 44%, 96%, 0.8)`; export const waterFill = "hsl(211, 42%, 70%)"; export const waterLine = "hsl(211, 73%, 78%)"; diff --git a/src/layer/place.js b/src/layer/place.js index 4f0996b43..07519476b 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -6,9 +6,9 @@ const labelHaloColor = [ ["linear"], ["zoom"], 4, - `hsla(${Color.backgroundHsl}, 0.8)`, + Color.backgroundFillTranslucent, 5, - `hsl(${Color.backgroundHsl})`, + Color.backgroundFill, ]; const labelHaloBlur = ["interpolate", ["linear"], ["zoom"], 4, 0.5, 5, 0]; From 6c54eafc4d442134007d7ed72a83f9dca4f0a6af Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Fri, 9 Dec 2022 12:22:55 -0500 Subject: [PATCH 082/514] Update src/js/language_label.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Minh Nguyễn --- src/js/language_label.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index fc668a366..4d0361910 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -29,7 +29,9 @@ class LanguageControl { export var label = new LanguageControl(); -export function displayLocales(localeSet) { +export function displayLocales(locales) { + let languageNames = new Intl.DisplayNames(locales, {type: "language"}); + let listFormat = new Intl.ListFormat(locales, {type: "disjunction"}); document.getElementById("language-field").textContent = - Array.from(localeSet).join(", "); + listFormat.format(locales.map(locale => languageNames.of(locale))); } From ee711587c69244d24950a8be379037ec2d8db720 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Fri, 9 Dec 2022 12:26:17 -0500 Subject: [PATCH 083/514] Code format --- src/js/language_label.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index 4d0361910..2242b084a 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -30,8 +30,9 @@ class LanguageControl { export var label = new LanguageControl(); export function displayLocales(locales) { - let languageNames = new Intl.DisplayNames(locales, {type: "language"}); - let listFormat = new Intl.ListFormat(locales, {type: "disjunction"}); - document.getElementById("language-field").textContent = - listFormat.format(locales.map(locale => languageNames.of(locale))); + let languageNames = new Intl.DisplayNames(locales, { type: "language" }); + let listFormat = new Intl.ListFormat(locales, { type: "disjunction" }); + document.getElementById("language-field").textContent = listFormat.format( + locales.map((locale) => languageNames.of(locale)) + ); } From 2ada4a7e72e1d89b1c4d5bc01c365a6b841c1b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Wed, 7 Dec 2022 19:20:16 -0800 Subject: [PATCH 084/514] Lightened, outlined bodies of water --- src/americana.js | 3 ++- src/constants/color.js | 5 ++--- src/layer/water.js | 17 ++++++++++++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/americana.js b/src/americana.js index c15989765..ce0917079 100644 --- a/src/americana.js +++ b/src/americana.js @@ -47,9 +47,10 @@ function buildLayers() { lyrBoundary.stateCasing, lyrBoundary.countryCasing, - lyrWater.water, lyrWater.waterway, lyrWater.waterwayIntermittent, + lyrWater.waterLine, + lyrWater.water, lyrPark.outline, lyrAeroway.outline, diff --git a/src/constants/color.js b/src/constants/color.js index 95a27eee7..7c61a22df 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -1,9 +1,8 @@ export const backgroundFill = `hsl(30, 44%, 96%)`; export const backgroundFillTranslucent = `hsla(30, 44%, 96%, 0.8)`; -export const waterFill = "hsl(211, 42%, 70%)"; -export const waterLine = "hsl(211, 73%, 78%)"; -export const waterIntermittent = "hsl(205, 89%, 83%)"; +export const waterFill = "hsl(211, 50%, 80%)"; +export const waterLine = "hsl(211, 42%, 70%)"; export const waterLabel = "hsl(211, 43%, 28%)"; export const hueBorder = 0; diff --git a/src/layer/water.js b/src/layer/water.js index 2426a1c8e..e67e43b39 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -18,7 +18,7 @@ export const waterway = { }, filter: ["!=", ["get", "intermittent"], 1], paint: { - "line-color": Color.waterFill, + "line-color": Color.waterLine, "line-width": [ "interpolate", ["exponential", 2], @@ -69,6 +69,21 @@ export const water = { "source-layer": "water", }; +export const waterLine = { + id: "water-line", + type: "line", + paint: { + "line-color": Color.waterLine, + "line-width": 3, + }, + layout: { + "line-cap": "round", + "line-join": "round", + }, + source: "openmaptiles", + "source-layer": "water", +}; + const labelPaintProperties = { "text-halo-color": "#fff", "text-color": Color.waterLabel, From a1820a2c6a90d48f34f9a389d12477cf9b59c68c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Wed, 7 Dec 2022 19:21:41 -0800 Subject: [PATCH 085/514] Coordinated halo color of sea, ocean labels with bodies of water --- src/layer/water.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/layer/water.js b/src/layer/water.js index e67e43b39..0d19c75c3 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -85,7 +85,13 @@ export const waterLine = { }; const labelPaintProperties = { - "text-halo-color": "#fff", + "text-halo-color": [ + "match", + ["get", "class"], + ["sea", "ocean"], + Color.waterFill, + Color.backgroundFill, + ], "text-color": Color.waterLabel, "text-halo-width": 0.75, "text-halo-blur": 0.25, From 90d1baf4d47a03f3b11d145c06e53010056b5dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Thu, 8 Dec 2022 01:16:10 -0800 Subject: [PATCH 086/514] Sometimes hide waterlines --- src/layer/water.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/layer/water.js b/src/layer/water.js index 0d19c75c3..6f5237543 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -72,9 +72,20 @@ export const water = { export const waterLine = { id: "water-line", type: "line", + filter: [ + "match", + ["get", "class"], + bigRivers, + [">=", ["zoom"], 8], + mediumRivers, + [">=", ["zoom"], 16], + "lake", + [">=", ["zoom"], 8], + true, + ], paint: { "line-color": Color.waterLine, - "line-width": 3, + "line-width": ["match", ["get", "class"], "ocean", 3, 2], }, layout: { "line-cap": "round", From 4b83ae26514a6e01c710f9971633f88282f7ced5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Thu, 8 Dec 2022 15:43:47 -0800 Subject: [PATCH 087/514] Blend waterlines with park borders --- src/constants/color.js | 4 +++- src/layer/water.js | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/constants/color.js b/src/constants/color.js index 7c61a22df..97594b99d 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -2,7 +2,9 @@ export const backgroundFill = `hsl(30, 44%, 96%)`; export const backgroundFillTranslucent = `hsla(30, 44%, 96%, 0.8)`; export const waterFill = "hsl(211, 50%, 80%)"; +export const waterFillTranslucent = "hsla(211, 50%, 80%, 0.5)"; export const waterLine = "hsl(211, 42%, 70%)"; +export const waterLineBold = "hsl(211, 42%, 50%)"; export const waterLabel = "hsl(211, 43%, 28%)"; export const hueBorder = 0; @@ -12,7 +14,7 @@ export const border = "hsl(0, 2%, 47%)"; export const borderCasing = `hsl(${hueBorderCasing}, 35%, 86%)`; export const parkFill = "hsl(136, 41%, 89%)"; -export const parkOutline = "hsl(136, 41%, 79%)"; +export const parkOutline = "hsla(136, 41%, 70%, 50%)"; export const parkLabel = "hsl(136, 71%, 29%)"; export const parkLabelHalo = "hsl(90, 27%, 94%)"; diff --git a/src/layer/water.js b/src/layer/water.js index 6f5237543..0a14b73dc 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -64,6 +64,7 @@ export const water = { 0.3, 1, ], + "fill-outline-color": Color.waterFillTranslucent, }, source: "openmaptiles", "source-layer": "water", @@ -84,8 +85,7 @@ export const waterLine = { true, ], paint: { - "line-color": Color.waterLine, - "line-width": ["match", ["get", "class"], "ocean", 3, 2], + "line-color": Color.waterLineBold, }, layout: { "line-cap": "round", From 89db2fd67450b81e811adac7b3f14cc63a66e58d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 9 Dec 2022 00:50:59 -0800 Subject: [PATCH 088/514] Lightened water further Ensure contrast with marine park boundaries. --- src/constants/color.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/constants/color.js b/src/constants/color.js index 97594b99d..39c17c082 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -1,8 +1,8 @@ export const backgroundFill = `hsl(30, 44%, 96%)`; export const backgroundFillTranslucent = `hsla(30, 44%, 96%, 0.8)`; -export const waterFill = "hsl(211, 50%, 80%)"; -export const waterFillTranslucent = "hsla(211, 50%, 80%, 0.5)"; +export const waterFill = "hsl(211, 50%, 85%)"; +export const waterFillTranslucent = "hsla(211, 50%, 85%, 0.5)"; export const waterLine = "hsl(211, 42%, 70%)"; export const waterLineBold = "hsl(211, 42%, 50%)"; export const waterLabel = "hsl(211, 43%, 28%)"; From d346ce0eae5e14abd1d206e5c5f179c45c05ef1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 9 Dec 2022 01:17:33 -0800 Subject: [PATCH 089/514] Dash intermittent lake outline --- src/americana.js | 1 + src/layer/water.js | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/americana.js b/src/americana.js index ce0917079..a293c98f6 100644 --- a/src/americana.js +++ b/src/americana.js @@ -50,6 +50,7 @@ function buildLayers() { lyrWater.waterway, lyrWater.waterwayIntermittent, lyrWater.waterLine, + lyrWater.waterLineIntermittent, lyrWater.water, lyrPark.outline, diff --git a/src/layer/water.js b/src/layer/water.js index 0a14b73dc..8ea6d497d 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -71,7 +71,7 @@ export const water = { }; export const waterLine = { - id: "water-line", + id: "water_line", type: "line", filter: [ "match", @@ -81,7 +81,7 @@ export const waterLine = { mediumRivers, [">=", ["zoom"], 16], "lake", - [">=", ["zoom"], 8], + ["all", ["!=", ["get", "intermittent"], 1], [">=", ["zoom"], 8]], true, ], paint: { @@ -95,6 +95,27 @@ export const waterLine = { "source-layer": "water", }; +export const waterLineIntermittent = { + id: "water_line_intermittent", + type: "line", + filter: [ + "all", + ["==", ["get", "intermittent"], 1], + ["==", ["get", "class"], "lake"], + [">=", ["zoom"], 8], + ], + paint: { + "line-color": Color.waterLine, + "line-dasharray": [6, 4], + }, + layout: { + "line-cap": "round", + "line-join": "round", + }, + source: "openmaptiles", + "source-layer": "water", +}; + const labelPaintProperties = { "text-halo-color": [ "match", From e18735acfcd5ce4e537afbc2bfbf4751fdf4bb73 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sat, 10 Dec 2022 22:06:46 -0500 Subject: [PATCH 090/514] Initial label widgets --- src/americana.js | 1 + src/js/language_label.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/americana.js b/src/americana.js index c15989765..33214df27 100644 --- a/src/americana.js +++ b/src/americana.js @@ -351,6 +351,7 @@ if (config.ATTRIBUTION_TEXT != undefined) { map.addControl(new maplibregl.AttributionControl(attributionConfig)); map.addControl(languageLabel.label, "bottom-right"); +map.addControl(languageLabel.switcher, "bottom-right"); if (config.ATTRIBUTION_LOGO != undefined) { document.getElementById("attribution-logo").innerHTML = diff --git a/src/js/language_label.js b/src/js/language_label.js index 2242b084a..acb8890ce 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -1,8 +1,13 @@ "use strict"; +import * as maplibregl from "maplibre-gl"; + var langField = document.createElement("div"); langField.id = "language-field"; +var langChanger = document.createElement("div"); +langChanger.id = "language-switcher"; + /** * Label for displaying the current language being used */ @@ -27,7 +32,35 @@ class LanguageControl { } } +/** + * Label for changing the current language being used + */ + class LanguageChanger extends maplibregl.Evented { + constructor() { + super(); + } + onAdd(map) { + this._map = map; + this._container = langChanger; + this._container.style.textAlign = "right"; + this._container.style.paddingRight = "5px"; + this._container.style.paddingLeft = "5px"; + this._container.style.color = "#444"; + this._container.style.backgroundColor = "#ffffff80"; + this._container.style.float = "right"; + this._container.style.display = "table"; + this._container.textContent = ""; + return this._container; + } + + onRemove() { + this._container.parentNode.removeChild(this._container); + this._map = undefined; + } +} + export var label = new LanguageControl(); +export var switcher = new LanguageChanger(); export function displayLocales(locales) { let languageNames = new Intl.DisplayNames(locales, { type: "language" }); @@ -35,4 +68,5 @@ export function displayLocales(locales) { document.getElementById("language-field").textContent = listFormat.format( locales.map((locale) => languageNames.of(locale)) ); + document.getElementById("language-switcher").innerHTML = '[Change]'; } From 4f41333475dca019a8676b9ab0768dfdc523c805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sun, 11 Dec 2022 02:58:55 -0800 Subject: [PATCH 091/514] Lighten high-zoom waterways; place them above waterlines --- src/americana.js | 4 ++-- src/layer/water.js | 20 +++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/americana.js b/src/americana.js index a293c98f6..a8d06130c 100644 --- a/src/americana.js +++ b/src/americana.js @@ -47,10 +47,10 @@ function buildLayers() { lyrBoundary.stateCasing, lyrBoundary.countryCasing, - lyrWater.waterway, - lyrWater.waterwayIntermittent, lyrWater.waterLine, lyrWater.waterLineIntermittent, + lyrWater.waterway, + lyrWater.waterwayIntermittent, lyrWater.water, lyrPark.outline, diff --git a/src/layer/water.js b/src/layer/water.js index 8ea6d497d..f218da98b 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -18,7 +18,20 @@ export const waterway = { }, filter: ["!=", ["get", "intermittent"], 1], paint: { - "line-color": Color.waterLine, + "line-color": [ + "interpolate", + ["exponential", 2], + ["zoom"], + 13, + Color.waterLine, + 14, + [ + "case", + ["==", ["get", "intermittent"], 1], + Color.waterLine, + Color.waterFill, + ], + ], "line-width": [ "interpolate", ["exponential", 2], @@ -108,10 +121,7 @@ export const waterLineIntermittent = { "line-color": Color.waterLine, "line-dasharray": [6, 4], }, - layout: { - "line-cap": "round", - "line-join": "round", - }, + layout: waterLine.layout, source: "openmaptiles", "source-layer": "water", }; From b6c1974b89d6bc79f5bbd7f0465e33b66c2fa4d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sun, 11 Dec 2022 03:25:32 -0800 Subject: [PATCH 092/514] Lightened ferry lines --- src/layer/ferry.js | 4 +++- src/layer/transportation_label.js | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/layer/ferry.js b/src/layer/ferry.js index 2951f1d02..57f123c4f 100644 --- a/src/layer/ferry.js +++ b/src/layer/ferry.js @@ -1,5 +1,7 @@ "use strict"; +import * as Color from "../constants/color.js"; + // Filter properties in this layer should be updated to reflect consensus once // https://github.com/openmaptiles/openmaptiles/issues/1373 is closed @@ -7,7 +9,7 @@ export const ferry = { id: "ferry", type: "line", paint: { - "line-color": "hsl(211, 30%, 38%)", + "line-color": Color.waterLineBold, "line-dasharray": [7, 5], "line-width": 1.5, }, diff --git a/src/layer/transportation_label.js b/src/layer/transportation_label.js index e6c9bda68..5f9f82a33 100644 --- a/src/layer/transportation_label.js +++ b/src/layer/transportation_label.js @@ -31,13 +31,13 @@ export const label = { minorConstruction, "slategray", "ferry", - "hsl(211, 53%, 15%)", + Color.waterLineBold, "#333", ], "text-halo-color": [ ...highwaySelector, "ferry", - "hsl(211, 70%, 90%)", + Color.waterFill, Color.backgroundFill, ], "text-halo-blur": 0.5, From 6242ddb62a6ce59b17e3675f2635e121c7a4d590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sun, 11 Dec 2022 03:51:10 -0800 Subject: [PATCH 093/514] Updated taginfo project file --- scripts/taginfo_template.json | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/scripts/taginfo_template.json b/scripts/taginfo_template.json index f9ecf7d98..c5b07b539 100644 --- a/scripts/taginfo_template.json +++ b/scripts/taginfo_template.json @@ -298,6 +298,34 @@ "object_types": ["way"], "description": "Displays railroads with a thinner line width but normal tie spacing.", "doc_url": "https://openmaptiles.org/schema/#transportation" + }, + { + "key": "natural", + "value": "coastline", + "object_types": ["way"], + "description": "Coastlines are represented by a hairline at all zoom levels, except across the mouth of a river.", + "doc_url": "https://openmaptiles.org/schema/#water" + }, + { + "key": "natural", + "value": "water", + "object_types": ["area"], + "description": "Lakeshores and riverbanks are represented by a hairline at high zoom levels.", + "doc_url": "https://openmaptiles.org/schema/#water" + }, + { + "key": "intermittent", + "value": "yes", + "object_types": ["way"], + "description": "Intermittent waterways are dashed.", + "doc_url": "https://openmaptiles.org/schema/#waterway" + }, + { + "key": "intermittent", + "value": "yes", + "object_types": ["area"], + "description": "Intermittent lakes are translucent with a dashed line representing the lakeshore.", + "doc_url": "https://openmaptiles.org/schema/#water" } ] } From f647f00d62e37f00603c71762d20ffe3ff5de176 Mon Sep 17 00:00:00 2001 From: Josh Lee Date: Sun, 11 Dec 2022 19:33:51 -0500 Subject: [PATCH 094/514] Make language label clickable --- src/js/language_label.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index 2242b084a..457644813 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -10,13 +10,13 @@ class LanguageControl { onAdd(map) { this._map = map; this._container = langField; - this._container.style.textAlign = "right"; - this._container.style.paddingRight = "5px"; - this._container.style.paddingLeft = "5px"; - this._container.style.color = "#444"; - this._container.style.backgroundColor = "#ffffff80"; - this._container.style.float = "right"; - this._container.style.display = "table"; + this._container.className = "maplibregl-ctrl"; + Object.assign(this._container.style, { + margin: "0", + padding: "0 5px", + color: "#444", + backgroundColor: "#ffffff80", + }); this._container.textContent = ""; return this._container; } From 538131c1a2d92702e83b5c33b70d752c5babb7f7 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 11 Dec 2022 21:52:10 -0500 Subject: [PATCH 095/514] Add control elements --- src/americana.js | 1 - src/js/language_label.js | 75 ++++++++++++++++++++++------------------ 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/americana.js b/src/americana.js index 20e4b74af..a8d06130c 100644 --- a/src/americana.js +++ b/src/americana.js @@ -353,7 +353,6 @@ if (config.ATTRIBUTION_TEXT != undefined) { map.addControl(new maplibregl.AttributionControl(attributionConfig)); map.addControl(languageLabel.label, "bottom-right"); -map.addControl(languageLabel.switcher, "bottom-right"); if (config.ATTRIBUTION_LOGO != undefined) { document.getElementById("attribution-logo").innerHTML = diff --git a/src/js/language_label.js b/src/js/language_label.js index e8afd5e27..a1af242ec 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -2,11 +2,41 @@ import * as maplibregl from "maplibre-gl"; -var langField = document.createElement("div"); -langField.id = "language-field"; +var langField = labelControlElement("span", "language-field"); -var langChanger = document.createElement("div"); -langChanger.id = "language-switcher"; +var langChanger = labelControlElement("span", "language-switcher"); +langChanger.innerHTML = '[Change]'; + +var langReset = labelControlElement("span", "language-reset"); +langReset.innerHTML = '[Reset]'; + +var langCancel = labelControlElement("span", "language-cancel"); +langCancel.innerHTML = '[X]'; + +var langPicker = labelControlElement("select", "language-picker"); + +function addLanguage(name, code) { + var langEntry = document.createElement("option"); + langEntry.setAttribute("value", code); + langEntry.textContent = name; + langPicker.appendChild(langEntry); +} + +addLanguage("Select...", ""); +addLanguage("English", "en"); +addLanguage("Esperanto", "eo"); +addLanguage("French", "fr"); +addLanguage("Spanish", "es"); + +function labelControlElement(tag, id) { + var element = document.createElement(tag); + element.id = id; + Object.assign(element.style, { + margin: "0 2.5px", + color: "#444", + }); + return element; +} /** * Label for displaying the current language being used @@ -14,7 +44,7 @@ langChanger.id = "language-switcher"; class LanguageControl { onAdd(map) { this._map = map; - this._container = langField; + this._container = document.createElement("div"); this._container.className = "maplibregl-ctrl"; Object.assign(this._container.style, { margin: "0", @@ -23,33 +53,11 @@ class LanguageControl { backgroundColor: "#ffffff80", }); this._container.textContent = ""; - return this._container; - } - - onRemove() { - this._container.parentNode.removeChild(this._container); - this._map = undefined; - } -} - -/** - * Label for changing the current language being used - */ - class LanguageChanger extends maplibregl.Evented { - constructor() { - super(); - } - onAdd(map) { - this._map = map; - this._container = langChanger; - this._container.style.textAlign = "right"; - this._container.style.paddingRight = "5px"; - this._container.style.paddingLeft = "5px"; - this._container.style.color = "#444"; - this._container.style.backgroundColor = "#ffffff80"; - this._container.style.float = "right"; - this._container.style.display = "table"; - this._container.textContent = ""; + this._container.appendChild(langField); + this._container.appendChild(langChanger); + this._container.appendChild(langReset); + this._container.appendChild(langPicker); + this._container.appendChild(langCancel); return this._container; } @@ -60,7 +68,6 @@ class LanguageControl { } export var label = new LanguageControl(); -export var switcher = new LanguageChanger(); export function displayLocales(locales) { let languageNames = new Intl.DisplayNames(locales, { type: "language" }); @@ -68,5 +75,5 @@ export function displayLocales(locales) { document.getElementById("language-field").textContent = listFormat.format( locales.map((locale) => languageNames.of(locale)) ); - document.getElementById("language-switcher").innerHTML = '[Change]'; + // document.getElementById("language-switcher").innerHTML = '[Change]'; } From 1e4a5c5e9b5ab958bb0e0dd9a2ca2c09589c9db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Mon, 12 Dec 2022 14:58:15 -0800 Subject: [PATCH 096/514] Enlarged ocean, sea labels --- src/layer/water.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/layer/water.js b/src/layer/water.js index f218da98b..c75ee0ab3 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -217,11 +217,11 @@ export const waterPointLabel = { ["exponential", 2], ["zoom"], 3, - 8, + ["match", ["get", "class"], "ocean", 16, "sea", 12, 8], 12, - 14, + ["match", ["get", "class"], "ocean", 28, "sea", 21, 14], 20, - 40, + ["match", ["get", "class"], "ocean", 80, "sea", 60, 40], ], "text-letter-spacing": 0.25, }, From c397e9408f0f121cadf2f24e2b6fdcbaf2882d9a Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 13 Dec 2022 10:18:15 -0500 Subject: [PATCH 097/514] Menu in progress --- src/js/language_label.js | 127 +++++++++++++++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 12 deletions(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index a1af242ec..073f5a9ac 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -4,16 +4,23 @@ import * as maplibregl from "maplibre-gl"; var langField = labelControlElement("span", "language-field"); -var langChanger = labelControlElement("span", "language-switcher"); -langChanger.innerHTML = '[Change]'; +var langChanger = labelControlElement("button", "language-switcher"); +langChanger.textContent = "Change"; -var langReset = labelControlElement("span", "language-reset"); -langReset.innerHTML = '[Reset]'; +var langAdd = labelControlElement("button", "language-add"); +langAdd.textContent = "Add"; +hide(langAdd); -var langCancel = labelControlElement("span", "language-cancel"); -langCancel.innerHTML = '[X]'; +var langReset = labelControlElement("button", "language-reset"); +langReset.textContent = "Reset"; +hide(langReset); + +var langCancel = labelControlElement("button", "language-cancel"); +langCancel.textContent = "X"; +hide(langCancel); var langPicker = labelControlElement("select", "language-picker"); +hide(langPicker); function addLanguage(name, code) { var langEntry = document.createElement("option"); @@ -22,11 +29,95 @@ function addLanguage(name, code) { langPicker.appendChild(langEntry); } +function hide(element) { + Object.assign(element.style, { + display: "none", + visibility: "hidden", + }); +} +function show(element) { + Object.assign(element.style, { + display: "initial", + visibility: "visible", + }); +} + addLanguage("Select...", ""); -addLanguage("English", "en"); -addLanguage("Esperanto", "eo"); -addLanguage("French", "fr"); -addLanguage("Spanish", "es"); + +let langCodes = [ + "am", + "ar", + "az", + "be", + "bg", + "br", + "bs", + "ca", + "co", + "cs", + "cy", + "da", + "de", + "el", + "en", + "eo", + "es", + "et", + "eu", + "fi", + "fr", + "fy", + "ga", + "gd", + "he", + "hi", + "hr", + "hu", + "hy", + "id", + "is", + "it", + "ja", + "ja_kana", + "ja_rm", + "ja-Latn", + "ja-Hira", + "ka", + "kk", + "kn", + "ko", + "ko-Latn", + "ku", + "la", + "lb", + "lt", + "lv", + "mk", + "mt", + "ml", + "nl", + "no", + "oc", + "pl", + "pt", + "rm", + "ro", + "ru", + "sk", + "sl", + "sq", + "sr", + "sr-Latn", + "sv", + "ta", + "te", + "th", + "tr", + "uk", + "zh", +].forEach(lang => { + addLanguage(lang, lang); +}); function labelControlElement(tag, id) { var element = document.createElement(tag); @@ -38,6 +129,18 @@ function labelControlElement(tag, id) { return element; } +langChanger.onclick = function () { + hide(langField); + hide(langChanger); + show(langPicker); + show(langAdd); + show(langCancel); +}; + +langAdd.onclick = function () { + alert(langPicker.value); +}; + /** * Label for displaying the current language being used */ @@ -55,8 +158,9 @@ class LanguageControl { this._container.textContent = ""; this._container.appendChild(langField); this._container.appendChild(langChanger); - this._container.appendChild(langReset); this._container.appendChild(langPicker); + this._container.appendChild(langAdd); + this._container.appendChild(langReset); this._container.appendChild(langCancel); return this._container; } @@ -75,5 +179,4 @@ export function displayLocales(locales) { document.getElementById("language-field").textContent = listFormat.format( locales.map((locale) => languageNames.of(locale)) ); - // document.getElementById("language-switcher").innerHTML = '[Change]'; } From 0d4f6173290a5524fc8a694f11dce6338a19536f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 13 Dec 2022 01:20:03 -0800 Subject: [PATCH 098/514] Highlight urbanized areas at low zoom levels --- README.md | 2 +- src/americana.js | 2 ++ src/layer/landuse.js | 28 ++++++++++++++++++++++++++++ src/layer/water.js | 2 +- 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 src/layer/landuse.js diff --git a/README.md b/README.md index e7cab6f6d..c1038369f 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ The OpenStreetMap Americana style is built upon the [OpenMapTiles schema](https: - Feature data from OpenStreetMap - 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 and boundary data from [Natural Earth](https://www.naturalearthdata.com/). +- Low-zoom ocean/water, boundary, and urbanized area data from [Natural Earth](https://www.naturalearthdata.com/). ## Coverage diff --git a/src/americana.js b/src/americana.js index a8d06130c..aee086232 100644 --- a/src/americana.js +++ b/src/americana.js @@ -13,6 +13,7 @@ import * as lyrBackground from "./layer/background.js"; import * as lyrBoundary from "./layer/boundary.js"; import * as lyrConstruction from "./layer/construction.js"; import * as lyrHighwayShield from "./layer/highway_shield.js"; +import * as lyrLanduse from "./layer/landuse.js"; import * as lyrOneway from "./layer/oneway.js"; import * as lyrPark from "./layer/park.js"; import * as lyrPlace from "./layer/place.js"; @@ -39,6 +40,7 @@ function buildLayers() { layers.push( lyrBackground.base, + lyrLanduse.urbanizedArea, lyrPark.fill, lyrAeroway.fill, lyrPark.parkFill, diff --git a/src/layer/landuse.js b/src/layer/landuse.js new file mode 100644 index 000000000..7536dbc99 --- /dev/null +++ b/src/layer/landuse.js @@ -0,0 +1,28 @@ +"use strict"; + +import * as Color from "../constants/color.js"; + +export const urbanizedArea = { + id: "urbanized-area", + type: "fill", + minzoom: 4, + maxzoom: 6, + filter: ["==", ["get", "class"], "residential"], + paint: { + "fill-color": [ + "interpolate-lab", + ["linear"], + ["zoom"], + 4, + "hsl(41, 90%, 85%)", + 5, + "hsl(41, 90%, 80%)", + 5.5, + "hsl(41, 90%, 80%)", + 6, + Color.backgroundFill, + ], + }, + source: "openmaptiles", + "source-layer": "landuse", +}; diff --git a/src/layer/water.js b/src/layer/water.js index c75ee0ab3..9e0d11088 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -111,11 +111,11 @@ export const waterLine = { export const waterLineIntermittent = { id: "water_line_intermittent", type: "line", + minzoom: 8, filter: [ "all", ["==", ["get", "intermittent"], 1], ["==", ["get", "class"], "lake"], - [">=", ["zoom"], 8], ], paint: { "line-color": Color.waterLine, From 478e36466b2f0ec0b5064b79bb0cf0ab9dbe9c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Wed, 14 Dec 2022 12:31:37 -0800 Subject: [PATCH 099/514] Decouple transportation_name localization from other expressions --- src/layer/transportation_label.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layer/transportation_label.js b/src/layer/transportation_label.js index 5f9f82a33..0298f0b09 100644 --- a/src/layer/transportation_label.js +++ b/src/layer/transportation_label.js @@ -80,7 +80,7 @@ export const label = { ["literal", ["OpenHistorical Italic"]], ["literal", ["OpenHistorical"]], ], - "text-field": Label.localizedName, + "text-field": [...Label.localizedName], "text-max-angle": 20, "symbol-placement": "line", "text-size": [ From 9d02c45543c821a944d9d0c52691e3815aa3389c Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sat, 17 Dec 2022 22:22:39 -0500 Subject: [PATCH 100/514] TokenField implementation --- package-lock.json | 32 ++++++- package.json | 4 +- src/index.html | 7 ++ src/js/language_label.js | 194 ++++++++++++++++++++------------------- 4 files changed, 139 insertions(+), 98 deletions(-) diff --git a/package-lock.json b/package-lock.json index c411f3dbb..f74582837 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,15 +18,18 @@ "chai": "^4.3.7", "create-serve": "^1.0.1", "esbuild": "^0.15.15", + "events": "^3.3.0", "maplibre-gl": "^2.1.9", "mocha": "^10.1.0", "openmapsamples": "github:adamfranco/OpenMapSamples", "openmapsamples-maplibre": "github:adamfranco/OpenMapSamples-MapLibre", "prettier": "2.3.2", "shx": "^0.3.4", - "svgo": "^2.8.0" + "svgo": "^2.8.0", + "tokenfield": "^1.5.2" }, "engines": { + "node": ">=14", "npm": ">=8.3.0" } }, @@ -1370,6 +1373,15 @@ "node": ">=0.8.0" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -3421,6 +3433,12 @@ "node": ">=8.0" } }, + "node_modules/tokenfield": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/tokenfield/-/tokenfield-1.5.2.tgz", + "integrity": "sha512-G1mSawKJLSf3RqcI00uojA5BD2WAo+gc3ZyU4ElHFY08RPaksnJPcyEV1PAGPgAVZmQTjFGQ2uLK6WVDJrgTLA==", + "dev": true + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -4607,6 +4625,12 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, "expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -6124,6 +6148,12 @@ "is-number": "^7.0.0" } }, + "tokenfield": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/tokenfield/-/tokenfield-1.5.2.tgz", + "integrity": "sha512-G1mSawKJLSf3RqcI00uojA5BD2WAo+gc3ZyU4ElHFY08RPaksnJPcyEV1PAGPgAVZmQTjFGQ2uLK6WVDJrgTLA==", + "dev": true + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", diff --git a/package.json b/package.json index 008bca188..ac103aec2 100644 --- a/package.json +++ b/package.json @@ -34,13 +34,15 @@ "chai": "^4.3.7", "create-serve": "^1.0.1", "esbuild": "^0.15.15", + "events": "^3.3.0", "maplibre-gl": "^2.1.9", "mocha": "^10.1.0", "openmapsamples": "github:adamfranco/OpenMapSamples", "openmapsamples-maplibre": "github:adamfranco/OpenMapSamples-MapLibre", "prettier": "2.3.2", "shx": "^0.3.4", - "svgo": "^2.8.0" + "svgo": "^2.8.0", + "tokenfield": "^1.5.2" }, "engines": { "npm": ">=8.3.0", diff --git a/src/index.html b/src/index.html index aab26f7c6..5a8c34873 100644 --- a/src/index.html +++ b/src/index.html @@ -24,12 +24,19 @@ bottom: 10px; z-index: 100; } + #language-picker { + width: 13em; + } .openmapsamples-control-container { margin-left: 70px !important; } + diff --git a/src/js/language_label.js b/src/js/language_label.js index 073f5a9ac..02b3ec9b4 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -1,15 +1,15 @@ "use strict"; import * as maplibregl from "maplibre-gl"; +import Tokenfield from "tokenfield"; var langField = labelControlElement("span", "language-field"); var langChanger = labelControlElement("button", "language-switcher"); langChanger.textContent = "Change"; -var langAdd = labelControlElement("button", "language-add"); -langAdd.textContent = "Add"; -hide(langAdd); +var langPicker = labelControlElement("input", "language-picker"); +hide(langPicker); var langReset = labelControlElement("button", "language-reset"); langReset.textContent = "Reset"; @@ -19,16 +19,6 @@ var langCancel = labelControlElement("button", "language-cancel"); langCancel.textContent = "X"; hide(langCancel); -var langPicker = labelControlElement("select", "language-picker"); -hide(langPicker); - -function addLanguage(name, code) { - var langEntry = document.createElement("option"); - langEntry.setAttribute("value", code); - langEntry.textContent = name; - langPicker.appendChild(langEntry); -} - function hide(element) { Object.assign(element.style, { display: "none", @@ -42,82 +32,77 @@ function show(element) { }); } -addLanguage("Select...", ""); - -let langCodes = [ - "am", - "ar", - "az", - "be", - "bg", - "br", - "bs", - "ca", - "co", - "cs", - "cy", - "da", - "de", - "el", - "en", - "eo", - "es", - "et", - "eu", - "fi", - "fr", - "fy", - "ga", - "gd", - "he", - "hi", - "hr", - "hu", - "hy", - "id", - "is", - "it", - "ja", - "ja_kana", - "ja_rm", - "ja-Latn", - "ja-Hira", - "ka", - "kk", - "kn", - "ko", - "ko-Latn", - "ku", - "la", - "lb", - "lt", - "lv", - "mk", - "mt", - "ml", - "nl", - "no", - "oc", - "pl", - "pt", - "rm", - "ro", - "ru", - "sk", - "sl", - "sq", - "sr", - "sr-Latn", - "sv", - "ta", - "te", - "th", - "tr", - "uk", - "zh", -].forEach(lang => { - addLanguage(lang, lang); -}); +var langCodes = [ + { id: "am", name: "Amharic" }, + { id: "ar", name: "Arabic" }, + { id: "az", name: "Azerbaijani" }, + { id: "be", name: "Belarusian" }, + { id: "bg", name: "Bulgarian" }, + { id: "br", name: "Breton" }, + { id: "bs", name: "Bosnian" }, + { id: "ca", name: "Catalan; Valencian" }, + { id: "co", name: "Corsican" }, + { id: "cs", name: "Czech" }, + { id: "cy", name: "Welsh" }, + { id: "da", name: "Danish" }, + { id: "de", name: "German" }, + { id: "el", name: "Greek, Modern" }, + { id: "en", name: "English" }, + { id: "eo", name: "Esperanto" }, + { id: "es", name: "Spanish" }, + { id: "et", name: "Estonian" }, + { id: "eu", name: "Basque" }, + { id: "fi", name: "Finnish" }, + { id: "fr", name: "French" }, + { id: "fy", name: "Western Frisian" }, + { id: "ga", name: "Irish" }, + { id: "gd", name: "Gaelic" }, + { id: "he", name: "Hebrew" }, + { id: "hi", name: "Hindi" }, + { id: "hr", name: "Croatian" }, + { id: "hu", name: "Hungarian" }, + { id: "hy", name: "Armenian" }, + { id: "id", name: "Indonesian" }, + { id: "is", name: "Icelandic" }, + { id: "it", name: "Italian" }, + { id: "ja", name: "Japanese" }, + { id: "ja_kana", name: "Japanese Kana" }, + { id: "ja-Latn", name: "Japanese Romanized" }, + { id: "ja-Hira", name: "Japanese Hiragana" }, + { id: "ka", name: "Georgian" }, + { id: "kk", name: "Kazakh" }, + { id: "kn", name: "Kannada" }, + { id: "ko", name: "Korean" }, + { id: "ko-Latn", name: "Korean Romanized" }, + { id: "ku", name: "Kurdish" }, + { id: "la", name: "Latin" }, + { id: "lb", name: "Luxembourgish" }, + { id: "lt", name: "Lithuanian" }, + { id: "lv", name: "Latvian" }, + { id: "mk", name: "Macedonian" }, + { id: "mt", name: "Maltese" }, + { id: "ml", name: "Malayalam" }, + { id: "nl", name: "Dutch" }, + { id: "no", name: "Norwegian" }, + { id: "oc", name: "Occitan" }, + { id: "pl", name: "Polish" }, + { id: "pt", name: "Portuguese" }, + { id: "rm", name: "Romansh" }, + { id: "ro", name: "Romanian" }, + { id: "ru", name: "Russian" }, + { id: "sk", name: "Slovak" }, + { id: "sl", name: "Slovenian" }, + { id: "sq", name: "Albanian" }, + { id: "sr", name: "Serbian" }, + { id: "sr-Latn", name: "Serbian Romanized" }, + { id: "sv", name: "Swedish" }, + { id: "ta", name: "Tamil" }, + { id: "te", name: "Telugu" }, + { id: "th", name: "Thai" }, + { id: "tr", name: "Turkish" }, + { id: "uk", name: "Ukrainian" }, + { id: "zh", name: "Chinese" }, +]; function labelControlElement(tag, id) { var element = document.createElement(tag); @@ -129,16 +114,34 @@ function labelControlElement(tag, id) { return element; } +var tf; + langChanger.onclick = function () { + if (langPicker.style.visibility == "visible") { + console.log(tf); + } + hide(langField); - hide(langChanger); show(langPicker); - show(langAdd); show(langCancel); -}; -langAdd.onclick = function () { - alert(langPicker.value); + langPicker.style.width = "10em"; + + if (!tf) { + tf = new Tokenfield({ + el: document.querySelector("#language-picker"), // Attach Tokenfield to the input element with class "text-input" + items: langCodes, + newItems: false, + }); + tf.on("change", function () { + let items = tf.getItems(); + let langCodes = []; + items.forEach((element) => langCodes.push(element.id)); + let langQuery = langCodes.join(","); + //now add &language={langQuery} and go + }); + } + tf.focus(); }; /** @@ -157,9 +160,8 @@ class LanguageControl { }); this._container.textContent = ""; this._container.appendChild(langField); - this._container.appendChild(langChanger); this._container.appendChild(langPicker); - this._container.appendChild(langAdd); + this._container.appendChild(langChanger); this._container.appendChild(langReset); this._container.appendChild(langCancel); return this._container; From 24dcbf0bc2d424b8e9f283e671e604e944472b23 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sat, 17 Dec 2022 23:58:58 -0500 Subject: [PATCH 101/514] Language reload --- src/americana.js | 2 +- src/js/language_label.js | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/americana.js b/src/americana.js index a8d06130c..d758825b5 100644 --- a/src/americana.js +++ b/src/americana.js @@ -264,7 +264,7 @@ function buildLayers() { return layers; } -function buildStyle() { +export function buildStyle() { var getUrl = window.location; var baseUrl = getUrl.protocol + "//" + getUrl.host + getUrl.pathname; diff --git a/src/js/language_label.js b/src/js/language_label.js index 02b3ec9b4..7dcc91f01 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -1,7 +1,7 @@ "use strict"; -import * as maplibregl from "maplibre-gl"; import Tokenfield from "tokenfield"; +import { map, buildStyle } from "../americana"; var langField = labelControlElement("span", "language-field"); @@ -118,10 +118,11 @@ var tf; langChanger.onclick = function () { if (langPicker.style.visibility == "visible") { - console.log(tf); + return; } hide(langField); + hide(langChanger); show(langPicker); show(langCancel); @@ -138,12 +139,22 @@ langChanger.onclick = function () { let langCodes = []; items.forEach((element) => langCodes.push(element.id)); let langQuery = langCodes.join(","); - //now add &language={langQuery} and go + let rawHash = window.location.hash.split("&")[0]; + var currentURL = `${window.location.protocol}//${window.location.host}${window.location.pathname}${rawHash}&language=${langQuery}`; + window.history.pushState({ path: currentURL }, "", currentURL); + map.setStyle(buildStyle()); }); } tf.focus(); }; +langCancel.onclick = function () { + show(langField); + show(langChanger); + hide(langPicker); + hide(langCancel); +}; + /** * Label for displaying the current language being used */ From 53046b9e9307584330db2a9cbdeefbb5cd36efce Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 18 Dec 2022 14:07:44 -0500 Subject: [PATCH 102/514] Working close tray --- src/js/language_label.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/language_label.js b/src/js/language_label.js index 7dcc91f01..646ce1cf2 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -153,6 +153,7 @@ langCancel.onclick = function () { show(langChanger); hide(langPicker); hide(langCancel); + document.querySelectorAll('.tokenfield').forEach(e => hide(e)); }; /** From d9ee018659f2c399b5ce52e9ceeebd1cd79a1ebf Mon Sep 17 00:00:00 2001 From: MoiraPrime Date: Sun, 18 Dec 2022 15:41:49 -0600 Subject: [PATCH 103/514] Add "node": ">=14" to package-lock.json Technically NPM did this --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index c411f3dbb..cfa0f57b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "svgo": "^2.8.0" }, "engines": { + "node": ">=14", "npm": ">=8.3.0" } }, From 32b963e171629d4e22fb47a31c9fdc43b69f1ce9 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 18 Dec 2022 17:42:18 -0500 Subject: [PATCH 104/514] Fix show/hide logic --- src/americana.js | 18 +++++++++++++----- src/js/language_label.js | 30 +++++++++++++++++++----------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/americana.js b/src/americana.js index d758825b5..70f4fb79b 100644 --- a/src/americana.js +++ b/src/americana.js @@ -324,10 +324,18 @@ map.on("styleimagemissing", function (e) { Shield.missingIconHandler(map, e); }); -window.addEventListener("languagechange", (event) => { - console.log(`Changed to ${navigator.languages}`); +export function hotReloadMap() { map.setStyle(buildStyle()); +} + +export function updateLanguageLabel() { languageLabel.displayLocales(Label.getLocales()); +} + +window.addEventListener("languagechange", (event) => { + console.log(`Changed to ${navigator.languages}`); + hotReloadMap(); + updateLanguageLabel(); }); window.addEventListener("hashchange", (event) => { @@ -336,8 +344,8 @@ window.addEventListener("hashchange", (event) => { let newLanguage = Label.getLanguageFromURL(new URL(event.newURL)); if (oldLanguage !== newLanguage) { console.log(`Changed to ${newLanguage}`); - map.setStyle(buildStyle()); - languageLabel.displayLocales(Label.getLocales()); + hotReloadMap(); + updateLanguageLabel(); } }); @@ -371,4 +379,4 @@ map.addControl(sampleControl, "bottom-left"); map.getCanvas().focus(); -languageLabel.displayLocales(Label.getLocales()); +updateLanguageLabel(); diff --git a/src/js/language_label.js b/src/js/language_label.js index 646ce1cf2..d04c35748 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -1,7 +1,12 @@ "use strict"; import Tokenfield from "tokenfield"; -import { map, buildStyle } from "../americana"; +import { + map, + buildStyle, + hotReloadMap, + updateLanguageLabel, +} from "../americana"; var langField = labelControlElement("span", "language-field"); @@ -20,16 +25,14 @@ langCancel.textContent = "X"; hide(langCancel); function hide(element) { + console.log("hide: " + element.id); + Object.assign(element.style, { display: "none", - visibility: "hidden", }); } function show(element) { - Object.assign(element.style, { - display: "initial", - visibility: "visible", - }); + element.style.removeProperty("display"); } var langCodes = [ @@ -118,12 +121,14 @@ var tf; langChanger.onclick = function () { if (langPicker.style.visibility == "visible") { + console.log("nothing to do"); return; } + console.log("field change!"); + hide(langField); hide(langChanger); - show(langPicker); show(langCancel); langPicker.style.width = "10em"; @@ -142,18 +147,21 @@ langChanger.onclick = function () { let rawHash = window.location.hash.split("&")[0]; var currentURL = `${window.location.protocol}//${window.location.host}${window.location.pathname}${rawHash}&language=${langQuery}`; window.history.pushState({ path: currentURL }, "", currentURL); - map.setStyle(buildStyle()); + hotReloadMap(); }); } + document.querySelectorAll(".tokenfield").forEach((e) => show(e)); tf.focus(); }; langCancel.onclick = function () { + document.querySelectorAll(".tokenfield").forEach((e) => hide(e)); + hide(langCancel); + + updateLanguageLabel(); + show(langField); show(langChanger); - hide(langPicker); - hide(langCancel); - document.querySelectorAll('.tokenfield').forEach(e => hide(e)); }; /** From 5528f23095da768638efd984d8e040ec2ca3d193 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 18 Dec 2022 17:45:15 -0500 Subject: [PATCH 105/514] Handle null-language case --- src/js/language_label.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index d04c35748..07e2b4fbe 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -145,7 +145,9 @@ langChanger.onclick = function () { items.forEach((element) => langCodes.push(element.id)); let langQuery = langCodes.join(","); let rawHash = window.location.hash.split("&")[0]; - var currentURL = `${window.location.protocol}//${window.location.host}${window.location.pathname}${rawHash}&language=${langQuery}`; + let langQueryString = + langCodes.length > 0 ? `&language=${langQuery}` : ""; + var currentURL = `${window.location.protocol}//${window.location.host}${window.location.pathname}${rawHash}${langQueryString}`; window.history.pushState({ path: currentURL }, "", currentURL); hotReloadMap(); }); From 8c9242924c263f777a67ad2a5286edb340c4c87c Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 18 Dec 2022 17:53:17 -0500 Subject: [PATCH 106/514] Remove reset references (not needed) --- src/js/language_label.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index 07e2b4fbe..9f4be270f 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -16,10 +16,6 @@ langChanger.textContent = "Change"; var langPicker = labelControlElement("input", "language-picker"); hide(langPicker); -var langReset = labelControlElement("button", "language-reset"); -langReset.textContent = "Reset"; -hide(langReset); - var langCancel = labelControlElement("button", "language-cancel"); langCancel.textContent = "X"; hide(langCancel); @@ -139,6 +135,9 @@ langChanger.onclick = function () { items: langCodes, newItems: false, }); + Object.assign(tf.style, { + height: "15em", + }); tf.on("change", function () { let items = tf.getItems(); let langCodes = []; @@ -184,7 +183,6 @@ class LanguageControl { this._container.appendChild(langField); this._container.appendChild(langPicker); this._container.appendChild(langChanger); - this._container.appendChild(langReset); this._container.appendChild(langCancel); return this._container; } From abe84d40700bab4d6e2132feff09f203e1230db2 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 18 Dec 2022 18:40:51 -0500 Subject: [PATCH 107/514] Improve widget behavior --- src/js/language_label.js | 41 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index 9f4be270f..51496047a 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -1,12 +1,7 @@ "use strict"; import Tokenfield from "tokenfield"; -import { - map, - buildStyle, - hotReloadMap, - updateLanguageLabel, -} from "../americana"; +import { hotReloadMap, updateLanguageLabel } from "../americana"; var langField = labelControlElement("span", "language-field"); @@ -21,8 +16,6 @@ langCancel.textContent = "X"; hide(langCancel); function hide(element) { - console.log("hide: " + element.id); - Object.assign(element.style, { display: "none", }); @@ -113,30 +106,26 @@ function labelControlElement(tag, id) { return element; } -var tf; +var tf = null; langChanger.onclick = function () { - if (langPicker.style.visibility == "visible") { - console.log("nothing to do"); - return; - } - - console.log("field change!"); - - hide(langField); hide(langChanger); show(langCancel); - langPicker.style.width = "10em"; - - if (!tf) { + if (tf == null) { tf = new Tokenfield({ el: document.querySelector("#language-picker"), // Attach Tokenfield to the input element with class "text-input" items: langCodes, newItems: false, }); - Object.assign(tf.style, { - height: "15em", + document.querySelectorAll(".tokenfield").forEach((e) => { + Object.assign(e.style, { + height: "3.5em", + width: "15em", + "margin-bottom": "4em", + "margin-top": "0.3em", + }); + e.setAttribute("placeholder", "hi"); }); tf.on("change", function () { let items = tf.getItems(); @@ -151,6 +140,7 @@ langChanger.onclick = function () { hotReloadMap(); }); } + document.querySelectorAll(".tokenfield").forEach((e) => show(e)); tf.focus(); }; @@ -158,10 +148,7 @@ langChanger.onclick = function () { langCancel.onclick = function () { document.querySelectorAll(".tokenfield").forEach((e) => hide(e)); hide(langCancel); - updateLanguageLabel(); - - show(langField); show(langChanger); }; @@ -180,10 +167,10 @@ class LanguageControl { backgroundColor: "#ffffff80", }); this._container.textContent = ""; - this._container.appendChild(langField); + this._container.appendChild(langCancel); this._container.appendChild(langPicker); + this._container.appendChild(langField); this._container.appendChild(langChanger); - this._container.appendChild(langCancel); return this._container; } From ffe7776a7a97b3485327c8ca350656bf1af209f4 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 18 Dec 2022 18:59:12 -0500 Subject: [PATCH 108/514] Fix header label --- src/js/language_label.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index 51496047a..d0d9249aa 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -11,9 +11,20 @@ langChanger.textContent = "Change"; var langPicker = labelControlElement("input", "language-picker"); hide(langPicker); +var langHeader = labelControlElement("span", "lang-header"); +var langHints = labelControlElement("span", "lang-hints"); +langHints.textContent = "Begin typing to add languages"; + var langCancel = labelControlElement("button", "language-cancel"); langCancel.textContent = "X"; -hide(langCancel); +Object.assign(langCancel.style, { + "margin-top": "0.3em", +}); + +langHeader.appendChild(langCancel); +langHeader.appendChild(langHints); + +hide(langHeader); function hide(element) { Object.assign(element.style, { @@ -110,7 +121,7 @@ var tf = null; langChanger.onclick = function () { hide(langChanger); - show(langCancel); + show(langHeader); if (tf == null) { tf = new Tokenfield({ @@ -120,8 +131,8 @@ langChanger.onclick = function () { }); document.querySelectorAll(".tokenfield").forEach((e) => { Object.assign(e.style, { - height: "3.5em", - width: "15em", + height: "5em", + width: "20em", "margin-bottom": "4em", "margin-top": "0.3em", }); @@ -137,6 +148,7 @@ langChanger.onclick = function () { langCodes.length > 0 ? `&language=${langQuery}` : ""; var currentURL = `${window.location.protocol}//${window.location.host}${window.location.pathname}${rawHash}${langQueryString}`; window.history.pushState({ path: currentURL }, "", currentURL); + updateLanguageLabel(); hotReloadMap(); }); } @@ -147,7 +159,7 @@ langChanger.onclick = function () { langCancel.onclick = function () { document.querySelectorAll(".tokenfield").forEach((e) => hide(e)); - hide(langCancel); + hide(langHeader); updateLanguageLabel(); show(langChanger); }; @@ -167,7 +179,7 @@ class LanguageControl { backgroundColor: "#ffffff80", }); this._container.textContent = ""; - this._container.appendChild(langCancel); + this._container.appendChild(langHeader); this._container.appendChild(langPicker); this._container.appendChild(langField); this._container.appendChild(langChanger); From 5ab915f16844fcaf087d19e357e6c9162b939a43 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 18 Dec 2022 20:11:29 -0500 Subject: [PATCH 109/514] Update src/js/language_label.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Minh Nguyễn --- src/js/language_label.js | 145 ++++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index d0d9249aa..64153f420 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -35,77 +35,80 @@ function show(element) { element.style.removeProperty("display"); } -var langCodes = [ - { id: "am", name: "Amharic" }, - { id: "ar", name: "Arabic" }, - { id: "az", name: "Azerbaijani" }, - { id: "be", name: "Belarusian" }, - { id: "bg", name: "Bulgarian" }, - { id: "br", name: "Breton" }, - { id: "bs", name: "Bosnian" }, - { id: "ca", name: "Catalan; Valencian" }, - { id: "co", name: "Corsican" }, - { id: "cs", name: "Czech" }, - { id: "cy", name: "Welsh" }, - { id: "da", name: "Danish" }, - { id: "de", name: "German" }, - { id: "el", name: "Greek, Modern" }, - { id: "en", name: "English" }, - { id: "eo", name: "Esperanto" }, - { id: "es", name: "Spanish" }, - { id: "et", name: "Estonian" }, - { id: "eu", name: "Basque" }, - { id: "fi", name: "Finnish" }, - { id: "fr", name: "French" }, - { id: "fy", name: "Western Frisian" }, - { id: "ga", name: "Irish" }, - { id: "gd", name: "Gaelic" }, - { id: "he", name: "Hebrew" }, - { id: "hi", name: "Hindi" }, - { id: "hr", name: "Croatian" }, - { id: "hu", name: "Hungarian" }, - { id: "hy", name: "Armenian" }, - { id: "id", name: "Indonesian" }, - { id: "is", name: "Icelandic" }, - { id: "it", name: "Italian" }, - { id: "ja", name: "Japanese" }, - { id: "ja_kana", name: "Japanese Kana" }, - { id: "ja-Latn", name: "Japanese Romanized" }, - { id: "ja-Hira", name: "Japanese Hiragana" }, - { id: "ka", name: "Georgian" }, - { id: "kk", name: "Kazakh" }, - { id: "kn", name: "Kannada" }, - { id: "ko", name: "Korean" }, - { id: "ko-Latn", name: "Korean Romanized" }, - { id: "ku", name: "Kurdish" }, - { id: "la", name: "Latin" }, - { id: "lb", name: "Luxembourgish" }, - { id: "lt", name: "Lithuanian" }, - { id: "lv", name: "Latvian" }, - { id: "mk", name: "Macedonian" }, - { id: "mt", name: "Maltese" }, - { id: "ml", name: "Malayalam" }, - { id: "nl", name: "Dutch" }, - { id: "no", name: "Norwegian" }, - { id: "oc", name: "Occitan" }, - { id: "pl", name: "Polish" }, - { id: "pt", name: "Portuguese" }, - { id: "rm", name: "Romansh" }, - { id: "ro", name: "Romanian" }, - { id: "ru", name: "Russian" }, - { id: "sk", name: "Slovak" }, - { id: "sl", name: "Slovenian" }, - { id: "sq", name: "Albanian" }, - { id: "sr", name: "Serbian" }, - { id: "sr-Latn", name: "Serbian Romanized" }, - { id: "sv", name: "Swedish" }, - { id: "ta", name: "Tamil" }, - { id: "te", name: "Telugu" }, - { id: "th", name: "Thai" }, - { id: "tr", name: "Turkish" }, - { id: "uk", name: "Ukrainian" }, - { id: "zh", name: "Chinese" }, -]; +let languageNames = new Intl.DisplayNames(locales, { type: "language" }); +let langCodes = [ + "am", + "ar", + "az", + "be", + "bg", + "br", + "bs", + "ca", + "co", + "cs", + "cy", + "da", + "de", + "el", + "en", + "eo", + "es", + "et", + "eu", + "fi", + "fr", + "fy", + "ga", + "gd", + "he", + "hi", + "hr", + "hu", + "hy", + "id", + "is", + "it", + "ja", + // "ja_kana", + "ja-Latn", + "ja-Hira", + "ka", + "kk", + "kn", + "ko", + "ko-Latn", + "ku", + "la", + "lb", + "lt", + "lv", + "mk", + "mt", + "ml", + "nl", + "no", + "oc", + "pl", + "pt", + "rm", + "ro", + "ru", + "sk", + "sl", + "sq", + "sr", + "sr-Latn", + "sv", + "ta", + "te", + "th", + "tr", + "uk", + "zh", +].map((id) => { + return { id, name: languageNames.of(id) }; +}); function labelControlElement(tag, id) { var element = document.createElement(tag); From b3f625ed674f1bec5f363e191d19cf959a6e2fa5 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 18 Dec 2022 20:11:42 -0500 Subject: [PATCH 110/514] Update src/js/language_label.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Minh Nguyễn --- src/js/language_label.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index 64153f420..5073b0e9e 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -146,13 +146,10 @@ langChanger.onclick = function () { let langCodes = []; items.forEach((element) => langCodes.push(element.id)); let langQuery = langCodes.join(","); - let rawHash = window.location.hash.split("&")[0]; - let langQueryString = - langCodes.length > 0 ? `&language=${langQuery}` : ""; - var currentURL = `${window.location.protocol}//${window.location.host}${window.location.pathname}${rawHash}${langQueryString}`; - window.history.pushState({ path: currentURL }, "", currentURL); - updateLanguageLabel(); - hotReloadMap(); + let hash = window.location.hash.substr(1); // omit # + let searchParams = new URLSearchParams(hash); + url.searchParams.set("language", langQuery); + window.location.hash = `#${searchParams}`; }); } From 13ac630592f7f50bb1783c7c3a2fe54a5c4000e3 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 18 Dec 2022 20:15:57 -0500 Subject: [PATCH 111/514] Fix import --- src/js/language_label.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index 5073b0e9e..f1c1cc6fe 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -1,7 +1,7 @@ "use strict"; import Tokenfield from "tokenfield"; -import { hotReloadMap, updateLanguageLabel } from "../americana"; +import { updateLanguageLabel } from "../americana"; var langField = labelControlElement("span", "language-field"); From bac317499acc2db97352c3fe40870b34d744c9bc Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 18 Dec 2022 20:19:17 -0500 Subject: [PATCH 112/514] fix locale getter --- src/js/language_label.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index f1c1cc6fe..4b174ccee 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -2,6 +2,7 @@ import Tokenfield from "tokenfield"; import { updateLanguageLabel } from "../americana"; +import * as Label from "../constants/label.js"; var langField = labelControlElement("span", "language-field"); @@ -35,7 +36,9 @@ function show(element) { element.style.removeProperty("display"); } -let languageNames = new Intl.DisplayNames(locales, { type: "language" }); +let languageNames = new Intl.DisplayNames(Label.getLocales(), { + type: "language", +}); let langCodes = [ "am", "ar", From 6595ff4e1672699a9d64fe15e445c89407e90c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sun, 18 Dec 2022 17:36:00 -0800 Subject: [PATCH 113/514] Fixed stray URL reference --- src/js/language_label.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index 4b174ccee..3fab37b9d 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -151,7 +151,7 @@ langChanger.onclick = function () { let langQuery = langCodes.join(","); let hash = window.location.hash.substr(1); // omit # let searchParams = new URLSearchParams(hash); - url.searchParams.set("language", langQuery); + searchParams.set("language", langQuery); window.location.hash = `#${searchParams}`; }); } From 004bb96b4163ff78a4c3e3886c21bc6f0f6fe33b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sun, 18 Dec 2022 17:36:26 -0800 Subject: [PATCH 114/514] Ignore empty language parameter --- src/constants/label.js | 3 ++- test/spec/label.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/constants/label.js b/src/constants/label.js index 504174c65..3799d3ad4 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -4,7 +4,8 @@ * Returns a list of languages as a comma-delimited string from the given URL hash. */ export function getLanguageFromURL(url) { - return new URLSearchParams(url.hash.substr(1)).get("language"); + let language = new URLSearchParams(url.hash.substr(1)).get("language"); + return language === "" ? null : language; } /** diff --git a/test/spec/label.js b/test/spec/label.js index 282888ba1..22910f60b 100644 --- a/test/spec/label.js +++ b/test/spec/label.js @@ -34,7 +34,7 @@ describe("label", function () { Label.getLanguageFromURL( new URL("http://localhost:1776/#map=1/2/3&language=") ) - ).to.eql(""); + ).to.be.null; }); it("accepts an ISO 639 code", function () { expect( From 17117a710b7d449b672bf06f112782a74687f26b Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 18 Dec 2022 20:41:27 -0500 Subject: [PATCH 115/514] Remove unneeded placeholder --- src/js/language_label.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/language_label.js b/src/js/language_label.js index 3fab37b9d..82dc805be 100644 --- a/src/js/language_label.js +++ b/src/js/language_label.js @@ -142,7 +142,6 @@ langChanger.onclick = function () { "margin-bottom": "4em", "margin-top": "0.3em", }); - e.setAttribute("placeholder", "hi"); }); tf.on("change", function () { let items = tf.getItems(); From 8f2c98a7cebe56f6c8cbd7094e6d0cf34eba3cac Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 21 Dec 2022 11:33:08 -0500 Subject: [PATCH 116/514] Provide a definition of the word Americana I thought it would be a nice touch to add a definition of "Americana" as explained by an American author, for the benefit of those not familiar with American culture and zeitgeist. I'll start the bidding with this one but I invite consideration of other quotes or ideas. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index c1038369f..e4ae84b48 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ _A quintessentially American map style_ [🗺 View the map](https://zelonewolf.g Americana map style logo +>*The United States of America is such a glorious mess of contradiction, such a crazy quilt of competing themes, such a fecund mishmash of people and ideas, that defining us is pretty much pointless. There is, of course, a kind of faded notion of "Americana", one that concerns Route 66, diners, freak rock formations, and the like—but even in its halcyon days this "roadside attraction" version of America was never an accurate or nuanced distillation of our massively complicated culture.* +>
-Hampton Sides, *[Americana: Dispatches from the New Frontier](https://en.wikipedia.org/wiki/Americana_(book))* (2004) + +
+ The purpose of the Americana style is to: - Promote collaboration and common purpose in the American mapping community From 71b4ad94594118cd3290f015555d0d9cf9e655c4 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 21 Dec 2022 11:43:24 -0500 Subject: [PATCH 117/514] Formatting --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index e4ae84b48..e7d6ca306 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ _A quintessentially American map style_ [🗺 View the map](https://zelonewolf.g Americana map style logo ->*The United States of America is such a glorious mess of contradiction, such a crazy quilt of competing themes, such a fecund mishmash of people and ideas, that defining us is pretty much pointless. There is, of course, a kind of faded notion of "Americana", one that concerns Route 66, diners, freak rock formations, and the like—but even in its halcyon days this "roadside attraction" version of America was never an accurate or nuanced distillation of our massively complicated culture.* ->
-Hampton Sides, *[Americana: Dispatches from the New Frontier](https://en.wikipedia.org/wiki/Americana_(book))* (2004) +> _The United States of America is such a glorious mess of contradiction, such a crazy quilt of competing themes, such a fecund mishmash of people and ideas, that defining us is pretty much pointless. There is, of course, a kind of faded notion of "Americana", one that concerns Route 66, diners, freak rock formations, and the like—but even in its halcyon days this "roadside attraction" version of America was never an accurate or nuanced distillation of our massively complicated culture._ >
-Hampton Sides, _[Americana: Dispatches from the New Frontier]()_ (2004)
From 7343a536c124a262358eafab82c49c604e23e185 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 21 Dec 2022 11:53:19 -0500 Subject: [PATCH 118/514] Prettier again --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e7d6ca306..1871dce99 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ _A quintessentially American map style_ [🗺 View the map](https://zelonewolf.g Americana map style logo -> _The United States of America is such a glorious mess of contradiction, such a crazy quilt of competing themes, such a fecund mishmash of people and ideas, that defining us is pretty much pointless. There is, of course, a kind of faded notion of "Americana", one that concerns Route 66, diners, freak rock formations, and the like—but even in its halcyon days this "roadside attraction" version of America was never an accurate or nuanced distillation of our massively complicated culture._ >
-Hampton Sides, _[Americana: Dispatches from the New Frontier]()_ (2004) +> _The United States of America is such a glorious mess of contradiction, such a crazy quilt of competing themes, such a fecund mishmash of people and ideas, that defining us is pretty much pointless. There is, of course, a kind of faded notion of "Americana", one that concerns Route 66, diners, freak rock formations, and the like—but even in its halcyon days this "roadside attraction" version of America was never an accurate or nuanced distillation of our massively complicated culture._ >
-Hampton Sides, _[Americana: Dispatches from the New Frontier]()\_ (2004)
From 0527faadab901fc89751bb3bc907519b21a555cd Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 21 Dec 2022 12:21:24 -0500 Subject: [PATCH 119/514] Remove stray angle bracket --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1871dce99..8040e110d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ _A quintessentially American map style_ [🗺 View the map](https://zelonewolf.g Americana map style logo -> _The United States of America is such a glorious mess of contradiction, such a crazy quilt of competing themes, such a fecund mishmash of people and ideas, that defining us is pretty much pointless. There is, of course, a kind of faded notion of "Americana", one that concerns Route 66, diners, freak rock formations, and the like—but even in its halcyon days this "roadside attraction" version of America was never an accurate or nuanced distillation of our massively complicated culture._ >
-Hampton Sides, _[Americana: Dispatches from the New Frontier]()\_ (2004) +> _The United States of America is such a glorious mess of contradiction, such a crazy quilt of competing themes, such a fecund mishmash of people and ideas, that defining us is pretty much pointless. There is, of course, a kind of faded notion of "Americana", one that concerns Route 66, diners, freak rock formations, and the like—but even in its halcyon days this "roadside attraction" version of America was never an accurate or nuanced distillation of our massively complicated culture._
-Hampton Sides, _[Americana: Dispatches from the New Frontier]()\_ (2004)
From 764d3fb0e47373acec225b54d4b2d56b1be8fafa Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 21 Dec 2022 12:25:46 -0500 Subject: [PATCH 120/514] Hopefully fixing italics --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8040e110d..80bc0c53e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ _A quintessentially American map style_ [🗺 View the map](https://zelonewolf.g Americana map style logo -> _The United States of America is such a glorious mess of contradiction, such a crazy quilt of competing themes, such a fecund mishmash of people and ideas, that defining us is pretty much pointless. There is, of course, a kind of faded notion of "Americana", one that concerns Route 66, diners, freak rock formations, and the like—but even in its halcyon days this "roadside attraction" version of America was never an accurate or nuanced distillation of our massively complicated culture._
-Hampton Sides, _[Americana: Dispatches from the New Frontier]()\_ (2004) +> _The United States of America is such a glorious mess of contradiction, such a crazy quilt of competing themes, such a fecund mishmash of people and ideas, that defining us is pretty much pointless. There is, of course, a kind of faded notion of "Americana", one that concerns Route 66, diners, freak rock formations, and the like—but even in its halcyon days this "roadside attraction" version of America was never an accurate or nuanced distillation of our massively complicated culture._
-Hampton Sides, [Americana: Dispatches from the New Frontier]() (2004)
From bddf951443343f9e9397ff3103e9281e190708da Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 21 Dec 2022 20:10:57 -0500 Subject: [PATCH 121/514] Add Mexico Federal Route Shields --- doc-img/shield_map_world.svg | 1 + icons/shield_mx_mx.svg | 5 +++++ src/js/shield_defs.js | 13 +++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 icons/shield_mx_mx.svg diff --git a/doc-img/shield_map_world.svg b/doc-img/shield_map_world.svg index 829f19352..6c2ad8743 100644 --- a/doc-img/shield_map_world.svg +++ b/doc-img/shield_map_world.svg @@ -116,6 +116,7 @@ See the end of this file for a list of available jurisdictions and their codes. .ca, .ht, +.mx, .us, .cl, .co, diff --git a/icons/shield_mx_mx.svg b/icons/shield_mx_mx.svg new file mode 100644 index 000000000..12dcdc1dd --- /dev/null +++ b/icons/shield_mx_mx.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 9339c2537..ee992c29b 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -883,6 +883,19 @@ export function loadShields(shieldImages) { Color.shields.white ); + // Mexico + + // Carreteras Federales + shields["MX:MX"] = { + backgroundImage: shieldImages.shield_mx_mx, + padding: { + left: 2, + right: 2, + top: 6, + bottom: 2, + }, + }; + // United States // Interstate Highways From 751b7edbec52df38c1a4d519c9130ff62fd9399f Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 21 Dec 2022 22:24:21 -0500 Subject: [PATCH 122/514] Squished crown shields --- icons/shield_mx_mx.svg | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/icons/shield_mx_mx.svg b/icons/shield_mx_mx.svg index 12dcdc1dd..27dc1162b 100644 --- a/icons/shield_mx_mx.svg +++ b/icons/shield_mx_mx.svg @@ -1,5 +1,17 @@ - - - - + + + + + + + image/svg+xml + + + + + + + + + From 9888bd604ea4d416bcad4cb681106910dc712eae Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 21 Dec 2022 22:26:41 -0500 Subject: [PATCH 123/514] Adjust padding --- src/js/shield_defs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index ee992c29b..e9ee2d313 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -891,7 +891,7 @@ export function loadShields(shieldImages) { padding: { left: 2, right: 2, - top: 6, + top: 3, bottom: 2, }, }; From a1a27406266e2e4d36f2c62f3b1a51e3d20284de Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Fri, 23 Dec 2022 14:38:39 -0500 Subject: [PATCH 124/514] Add definition for UK motorway/trunk --- src/js/shield_defs.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index e9ee2d313..ed61457c9 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -3325,6 +3325,17 @@ export function loadShields(shieldImages) { Color.shields.white ); + // Great Britain + shields["omt-gb-motorway"] = roundedRectShield( + Color.shields.blue, + Color.shields.white + ); + + shields["omt-gb-trunk"] = roundedRectShield( + Color.shields.green, + Color.shields.yellow + ); + // Greece shields["GR:motorway"] = shields["GR:national"] = hexagonVerticalShield( 3, From 0d30ae8bf1cc60bfe8a0c485bb74245b2ac3b90a Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Fri, 23 Dec 2022 14:42:28 -0500 Subject: [PATCH 125/514] Update status map --- doc-img/shield_map_world.svg | 3 ++- scripts/status_map.js | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc-img/shield_map_world.svg b/doc-img/shield_map_world.svg index 6c2ad8743..18f15d8f7 100644 --- a/doc-img/shield_map_world.svg +++ b/doc-img/shield_map_world.svg @@ -176,7 +176,8 @@ See the end of this file for a list of available jurisdictions and their codes. .au, .nz, .cw, -.ust { fill: #8250df; } +.ust, +.gb { fill: #8250df; } /* Color individual borders For example, to color the border between Angola and Namibia in green, add this line in the space below: diff --git a/scripts/status_map.js b/scripts/status_map.js index 930cc2e6c..d986b10d5 100644 --- a/scripts/status_map.js +++ b/scripts/status_map.js @@ -13,6 +13,8 @@ function fillPaths(svg, codes) { // Routes in United States insular areas use US prefix with the U.S. selectors.add(".ust"); } + // Great Britain uses OpenMapTiles special processing + selectors.add(".gb"); return svg.replace(".supported", new Array(...selectors).join(",\n")); } From ba73f4895e2bfe0ec2fd852b4edac4930dbfb838 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sat, 24 Dec 2022 17:52:12 -0500 Subject: [PATCH 126/514] Add not about UK route situation --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0d6382dd4..92cc74a3e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -297,6 +297,7 @@ This style strives to draw representative highway shields wherever they are tagg - Shields with a stacked ref configuration, with `/` separating the two lines of text in the `ref` value. Currently, these `ref` values are displayed verbatim on one line, and the code necessary for stacked ref rendering has not been written yet ([#366](https://github.com/ZeLonewolf/openstreetmap-americana/issues/366)). Such cases include: - **Italy "Diramazione" (branch) motorways**. Between their main autostrade "A" roads, the Italian motorway network has branch motorways which carry the name of both highways that they connect. For example, the A7 and A26 motorways have a branch motorway named A7/A26, which is correctly tagged `ref=A7/A26` and shown on shields with the two numbers stacked vertically. - **West Virginia County Routes**. The West Virginia Department of Transportation posts County Routes, which can have shields with two stacked numbers. For example, in Mercer County, County Route 460/1 is a branch off U.S. Route 460, and County Route 27/6 is a branch off County Route 27. These routes are correctly tagged `ref=460/1` and `ref=27/6` respectively, and shown on shields with the two numbers stacked vertically. +- The [highway classification system of the United Kingdom](https://wiki.openstreetmap.org/wiki/Roads_in_the_United_Kingdom). In the UK, the color and style of route signage is based on a strict 1:1 correspondence with the `highway=*` value of the underlying road. While "M" roads are always motorways with blue route symbology, "A" roads can anything from primary through motorway, and thus may take one of three colors and may change along a single route. As a result, a route relation containing a through "A" road is unhelpful for determining route symbology. Additionally, there are no route concurrencies in the UK that would demand route relations; all roads carry a single `ref` value. Accordingly, few route relations have been constructed in the UK, as they do not provide additional route information not already present. Additionally, data consumers support this highway classification-based symbology system, most notably OpenMapTiles, which has provided pseudo-network values for UK routes since the project's inception. Therefore, this project consumes the UK pseudo-network scheme established by OpenMapTiles and colors UK route network symbology strictly based on `highway=` consistent with UK signage. ### Shield Test Gallery From ab1aaae0cb95c2316280189f0d87a424b72e2842 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sat, 24 Dec 2022 18:15:37 -0500 Subject: [PATCH 127/514] Revise UK special case description --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 92cc74a3e..d83334ceb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -297,7 +297,7 @@ This style strives to draw representative highway shields wherever they are tagg - Shields with a stacked ref configuration, with `/` separating the two lines of text in the `ref` value. Currently, these `ref` values are displayed verbatim on one line, and the code necessary for stacked ref rendering has not been written yet ([#366](https://github.com/ZeLonewolf/openstreetmap-americana/issues/366)). Such cases include: - **Italy "Diramazione" (branch) motorways**. Between their main autostrade "A" roads, the Italian motorway network has branch motorways which carry the name of both highways that they connect. For example, the A7 and A26 motorways have a branch motorway named A7/A26, which is correctly tagged `ref=A7/A26` and shown on shields with the two numbers stacked vertically. - **West Virginia County Routes**. The West Virginia Department of Transportation posts County Routes, which can have shields with two stacked numbers. For example, in Mercer County, County Route 460/1 is a branch off U.S. Route 460, and County Route 27/6 is a branch off County Route 27. These routes are correctly tagged `ref=460/1` and `ref=27/6` respectively, and shown on shields with the two numbers stacked vertically. -- The [highway classification system of the United Kingdom](https://wiki.openstreetmap.org/wiki/Roads_in_the_United_Kingdom). In the UK, the color and style of route signage is based on a strict 1:1 correspondence with the `highway=*` value of the underlying road. While "M" roads are always motorways with blue route symbology, "A" roads can anything from primary through motorway, and thus may take one of three colors and may change along a single route. As a result, a route relation containing a through "A" road is unhelpful for determining route symbology. Additionally, there are no route concurrencies in the UK that would demand route relations; all roads carry a single `ref` value. Accordingly, few route relations have been constructed in the UK, as they do not provide additional route information not already present. Additionally, data consumers support this highway classification-based symbology system, most notably OpenMapTiles, which has provided pseudo-network values for UK routes since the project's inception. Therefore, this project consumes the UK pseudo-network scheme established by OpenMapTiles and colors UK route network symbology strictly based on `highway=` consistent with UK signage. +- The [highway classification system of the United Kingdom](https://wiki.openstreetmap.org/wiki/Roads_in_the_United_Kingdom). In the UK, mappers need to and are able to tag the actual official road classifications independently of route networks. The color and style of route signage is based on a strict 1:1 correspondence with the `highway=*` value of the underlying road, and **not** based on M/A/B highway network type. While "M" roads are always motorways with blue route symbology, "A" roads can anything from primary through motorway, and thus may take one of three colors and may change along a single route. Even if mappers were to create route relations containing all roads with the same route number, these relations would not be usable for determining how to render route symbology. Additionally, there are no route concurrencies in the UK; all roads that are `highway=secondary` or higher carry a single `ref` value that can be directly rendered into a shield without pre-processing. There is established data consumers support for this highway classification-based symbology system, most notably OpenMapTiles, which has provided pseudo-network values for UK routes since the project's inception. Therefore, this project consumes the UK pseudo-network scheme established by OpenMapTiles and colors UK route network symbology strictly based on `highway=` consistent with UK signage. ### Shield Test Gallery From 0de69952ceb3963dac06ec0a2dd5f3d6736a710d Mon Sep 17 00:00:00 2001 From: Zeke Farwell Date: Mon, 26 Dec 2022 14:20:08 -0500 Subject: [PATCH 128/514] Adjust intermittent and regular water line filters so the proper outline is always used Closes #634 --- src/layer/water.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/layer/water.js b/src/layer/water.js index 9e0d11088..4afa573e0 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -87,16 +87,20 @@ export const waterLine = { id: "water_line", type: "line", filter: [ + "all", + ["!=", ["get", "intermittent"], 1], + [ "match", ["get", "class"], - bigRivers, - [">=", ["zoom"], 8], - mediumRivers, - [">=", ["zoom"], 16], - "lake", - ["all", ["!=", ["get", "intermittent"], 1], [">=", ["zoom"], 8]], + bigRivers, + [">=", ["zoom"], 8], + mediumRivers, + [">=", ["zoom"], 16], + "lake", + [">=", ["zoom"], 8], true, ], + ], paint: { "line-color": Color.waterLineBold, }, @@ -112,11 +116,7 @@ export const waterLineIntermittent = { id: "water_line_intermittent", type: "line", minzoom: 8, - filter: [ - "all", - ["==", ["get", "intermittent"], 1], - ["==", ["get", "class"], "lake"], - ], + filter: ["all", ["==", ["get", "intermittent"], 1]], paint: { "line-color": Color.waterLine, "line-dasharray": [6, 4], From 3d371a1ec90fd0966ac0d109a55a0c1517f7e065 Mon Sep 17 00:00:00 2001 From: Zeke Farwell Date: Mon, 26 Dec 2022 14:22:01 -0500 Subject: [PATCH 129/514] Slightly increase intermittent water fill saturation to better differentiate from park fill color --- src/constants/color.js | 1 + src/layer/water.js | 15 +++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/constants/color.js b/src/constants/color.js index 39c17c082..4bbd8c891 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -3,6 +3,7 @@ export const backgroundFillTranslucent = `hsla(30, 44%, 96%, 0.8)`; 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)"; export const waterLine = "hsl(211, 42%, 70%)"; export const waterLineBold = "hsl(211, 42%, 50%)"; export const waterLabel = "hsl(211, 43%, 28%)"; diff --git a/src/layer/water.js b/src/layer/water.js index 4afa573e0..264071275 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -66,16 +66,15 @@ export const water = { id: "water", type: "fill", paint: { - "fill-color": Color.waterFill, - "fill-opacity": [ + "fill-color": [ "case", [ "any", ["==", ["get", "intermittent"], 1], ["==", ["get", "brunnel"], "tunnel"], ], - 0.3, - 1, + Color.waterIntermittentFill, + Color.waterFill, ], "fill-outline-color": Color.waterFillTranslucent, }, @@ -90,16 +89,16 @@ export const waterLine = { "all", ["!=", ["get", "intermittent"], 1], [ - "match", - ["get", "class"], + "match", + ["get", "class"], bigRivers, [">=", ["zoom"], 8], mediumRivers, [">=", ["zoom"], 16], "lake", [">=", ["zoom"], 8], - true, - ], + true, + ], ], paint: { "line-color": Color.waterLineBold, From 7472d4aabf7136b8cb678433829a544bebff147a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Mon, 26 Dec 2022 12:41:56 -0800 Subject: [PATCH 130/514] Suppress shields for recreational TLA networks --- src/js/shield.js | 18 ++++++++++++------ test/spec/shield.js | 25 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 test/spec/shield.js diff --git a/src/js/shield.js b/src/js/shield.js index 8c2a723e9..dfcc92602 100644 --- a/src/js/shield.js +++ b/src/js/shield.js @@ -62,11 +62,15 @@ function compoundShieldSize(dimension, bannerCount) { }; } -function isValidRef(ref) { - if (ref == null || ref.length == 0 || ref.length > 6) { - return false; - } - return true; +export function isValidNetwork(network) { + // On recreational route relations, network=* indicates the network's scope, + // not the network itself. + // https://github.com/ZeLonewolf/openstreetmap-americana/issues/94 + return !/^[lrni][chimpw]n$/.test(network); +} + +export function isValidRef(ref) { + return ref !== null && ref.length !== 0 && ref.length <= 6; } /** @@ -267,7 +271,9 @@ function getShieldDef(routeDef) { if (shieldDef == null) { // Default to plain black text with halo and no background shield console.debug("Generic shield for", JSON.stringify(routeDef)); - return isValidRef(routeDef.ref) ? ShieldDef.shields["default"] : null; + return isValidNetwork(routeDef.network) && isValidRef(routeDef.ref) + ? ShieldDef.shields.default + : null; } if (shieldDef.overrideByRef) { diff --git a/test/spec/shield.js b/test/spec/shield.js new file mode 100644 index 000000000..f067903e0 --- /dev/null +++ b/test/spec/shield.js @@ -0,0 +1,25 @@ +"use strict"; + +import chai, { expect } from "chai"; +import * as Shield from "../../src/js/shield.js"; + +describe("shield", function () { + describe("#isValidNetwork", function () { + it("rejects a recreational network", function () { + expect(Shield.isValidNetwork("BAB")).to.be.true; + expect(Shield.isValidNetwork("rwn")).to.be.false; + }); + }); + describe("#isValidRef", function () { + it("rejects a null ref", function () { + expect(Shield.isValidRef(null)).to.be.false; + }); + it("rejects an empty ref", function () { + expect(Shield.isValidRef("")).to.be.false; + }); + it("rejects a long ref", function () { + expect(Shield.isValidRef("ABC123")).to.be.true; + expect(Shield.isValidRef("Equator")).to.be.false; + }); + }); +}); From 23d5ad1664ec6124b6a95ab87a09ef0803cf743d Mon Sep 17 00:00:00 2001 From: Zeke Farwell Date: Mon, 26 Dec 2022 17:48:50 -0500 Subject: [PATCH 131/514] Reduce intermittent water outline thickness and adjust color to better match regular water outline --- src/constants/color.js | 1 + src/layer/water.js | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/constants/color.js b/src/constants/color.js index 4bbd8c891..bba97b525 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -4,6 +4,7 @@ export const backgroundFillTranslucent = `hsla(30, 44%, 96%, 0.8)`; 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)"; +export const waterIntermittentOutline = "hsl(211, 100%, 30%)"; export const waterLine = "hsl(211, 42%, 70%)"; export const waterLineBold = "hsl(211, 42%, 50%)"; export const waterLabel = "hsl(211, 43%, 28%)"; diff --git a/src/layer/water.js b/src/layer/water.js index 264071275..563e9d619 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -117,8 +117,9 @@ export const waterLineIntermittent = { minzoom: 8, filter: ["all", ["==", ["get", "intermittent"], 1]], paint: { - "line-color": Color.waterLine, - "line-dasharray": [6, 4], + "line-color": Color.waterIntermittentOutline, + "line-dasharray": [10, 6], + "line-width": 0.5, }, layout: waterLine.layout, source: "openmaptiles", From a46ee60a306762b01e8265003c02163eb8cd9db5 Mon Sep 17 00:00:00 2001 From: Zeke Farwell Date: Mon, 26 Dec 2022 23:40:40 -0500 Subject: [PATCH 132/514] lighten intermittent waterway color at high zoom to match non-intermittent --- src/layer/water.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/layer/water.js b/src/layer/water.js index 563e9d619..a088ddfc9 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -20,17 +20,12 @@ export const waterway = { paint: { "line-color": [ "interpolate", - ["exponential", 2], + ["exponential", 0.5], ["zoom"], 13, Color.waterLine, - 14, - [ - "case", - ["==", ["get", "intermittent"], 1], - Color.waterLine, - Color.waterFill, - ], + 15, + Color.waterFill, ], "line-width": [ "interpolate", From 25348abcba324c3a8f410b3dffece3c26f0629b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Wed, 28 Dec 2022 15:15:58 -0800 Subject: [PATCH 133/514] Distinguish Greek national roads from motorways --- src/js/shield_defs.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index e9ee2d313..d05dbd117 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -3326,7 +3326,7 @@ export function loadShields(shieldImages) { ); // Greece - shields["GR:motorway"] = shields["GR:national"] = hexagonVerticalShield( + shields["GR:motorway"] = hexagonVerticalShield( 3, Color.shields.green, Color.shields.white, @@ -3334,6 +3334,12 @@ export function loadShields(shieldImages) { 0, 34 ); + shields["GR:national"] = roundedRectShield( + Color.shields.blue, + Color.shields.white, + Color.shields.white, + 34 + ); // Hungary shields["HU:national"] = homePlateShield( From e4222df55b509f6ddaab8fc3580153070d960ad2 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Fri, 30 Dec 2022 16:07:11 -0500 Subject: [PATCH 134/514] Draw octagon shields mathematically --- icons/shield_oct_green_2.svg | 3 -- icons/shield_oct_green_3.svg | 3 -- icons/shield_oct_green_4.svg | 3 -- src/js/shield_canvas_draw.js | 98 +++++++++++++++++++++++++++++++++++- src/js/shield_defs.js | 71 +++++++++++++++++++------- 5 files changed, 151 insertions(+), 27 deletions(-) delete mode 100644 icons/shield_oct_green_2.svg delete mode 100644 icons/shield_oct_green_3.svg delete mode 100644 icons/shield_oct_green_4.svg diff --git a/icons/shield_oct_green_2.svg b/icons/shield_oct_green_2.svg deleted file mode 100644 index 98f888561..000000000 --- a/icons/shield_oct_green_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_oct_green_3.svg b/icons/shield_oct_green_3.svg deleted file mode 100644 index a6d1970d5..000000000 --- a/icons/shield_oct_green_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_oct_green_4.svg b/icons/shield_oct_green_4.svg deleted file mode 100644 index 961b92a7d..000000000 --- a/icons/shield_oct_green_4.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index 0114aae38..cc5e1c6e8 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -463,7 +463,6 @@ export function hexagonHorizontal( rectWidth ) { let angleInRadians = (angle * Math.PI) / 180; - let angleSign = Math.sign(angle); let sine = Math.sin(Math.abs(angleInRadians)); let cosine = Math.cos(angleInRadians); let tangent = Math.tan(angleInRadians); @@ -529,3 +528,100 @@ export function hexagonHorizontal( return ctx; } + +export function octagon( + offset, + angle, + fill, + outline, + ref, + radius, + outlineWidth, + rectWidth +) { + let angleInRadians = (angle * Math.PI) / 180; + let sine = Math.sin(angleInRadians); + let cosine = Math.cos(angleInRadians); + let tangent = Math.tan(angleInRadians); + + if (rectWidth == null) { + var shieldWidth = + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR; + var width = Math.max( + minGenericShieldWidth, + Math.min(maxGenericShieldWidth, shieldWidth) + ); + } else { + var width = rectWidth * PXR; + } + + var ctx = Gfx.getGfxContext({ width: width, height: CS }); + + let lineThick = outlineWidth * PXR; + let lineWidth = lineThick / 2; + let drawRadius = radius * PXR; + let drawOffset = offset * PXR; + + let x0 = lineWidth; + let x10 = width - lineWidth; + let y0 = lineWidth; + let y10 = CS - lineWidth; + + let x1 = x0 + drawRadius * tangent * sine; + let x5 = (x0 + x10) / 2; + let x9 = x10 - drawRadius * tangent * sine; + let y2 = y0 + drawOffset; + let y5 = (y0 + y10) / 2; + let y8 = y10 - drawOffset; + + let x3 = x0 + (y5 - y2) * tangent; + let x7 = x10 - (y5 - y2) * tangent; + let y4 = y5 - drawRadius * tangent * cosine; + let y6 = y5 + drawRadius * tangent * cosine; + + let offsetAngle = Math.atan(drawOffset / (x5 - x3)); + let offsetSine = Math.sin(offsetAngle); + let offsetCosine = Math.cos(offsetAngle); + + let halfComplementAngle = (Math.PI / 2 - angleInRadians - offsetAngle) / 2; + let halfComplementCosine = Math.cos(halfComplementAngle); + + let dx = + (drawRadius * Math.cos(angleInRadians + halfComplementAngle)) / + halfComplementCosine; + let dy = + (drawRadius * Math.sin(angleInRadians + halfComplementAngle)) / + halfComplementCosine; + + let x2 = x3 + dx - drawRadius * cosine; + let x4 = x3 + dx - drawRadius * offsetSine; + let x6 = x7 - dx + drawRadius * offsetSine; + let x8 = x7 - dx + drawRadius * cosine; + let y1 = y2 + dy - drawRadius * offsetCosine; + let y3 = y2 + dy - drawRadius * sine; + let y7 = y8 - dy + drawRadius * sine; + let y9 = y8 - dy + drawRadius * offsetCosine; + + ctx.beginPath(); + ctx.moveTo(x5, y10); + ctx.arcTo(x3, y8, x2, y7, drawRadius); + ctx.arcTo(x0, y5, x1, y4, drawRadius); + ctx.arcTo(x3, y2, x4, y1, drawRadius); + ctx.lineTo(x5, y0); + ctx.arcTo(x7, y2, x8, y3, drawRadius); + ctx.arcTo(x10, y5, x9, y6, drawRadius); + ctx.arcTo(x7, y8, x6, y9, drawRadius); + ctx.lineTo(x5, y10); + ctx.closePath(); + + ctx.lineWidth = lineThick; + ctx.fillStyle = fill; + ctx.fill(); + + if (outline != null) { + ctx.strokeStyle = outline; + ctx.stroke(); + } + + return ctx; +} diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index d05dbd117..83bdc20a6 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -292,6 +292,52 @@ function hexagonHorizontalShield( }; } +/** + * Draws a shield with an octagon background + * + * @param {*} offset - Height of diagonal edges + * @param {*} angle - Angle (in degrees) at which sides deviate from vertical + * @param {*} fillColor - Color of octagon background fill + * @param {*} strokeColor - Color of octagon outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of octagon (defaults to variable-width) + * @param {*} radius - Corner radius of octagon (defaults to 2) + * @returns a shield definition object + */ +function octagonShield( + offset, + angle, + fillColor, + strokeColor, + textColor, + radius, + rectWidth +) { + textColor = textColor ?? strokeColor; + radius = radius ?? 2; + return { + backgroundDraw: (ref) => + ShieldDraw.octagon( + offset, + angle, + fillColor, + strokeColor, + ref, + radius, + 1, + rectWidth + ), + textLayoutConstraint: ShieldText.ellipseTextConstraint, + padding: { + left: 2, + right: 2, + top: 2, + bottom: 2, + }, + textColor: textColor, + }; +} + /** * Draws a shield with a pill-shaped background * @@ -466,22 +512,6 @@ export function loadShields(shieldImages) { ], }; - // Octagon shields - let octagonShieldGreen = { - backgroundImage: [ - shieldImages.shield_oct_green_2, - shieldImages.shield_oct_green_3, - shieldImages.shield_oct_green_4, - ], - textColor: Color.shields.white, - padding: { - left: 3, - right: 3, - top: 5, - bottom: 5, - }, - }; - // Other common shield shapes let badgeShield = { backgroundImage: [shieldImages.shield_badge_2, shieldImages.shield_badge_3], @@ -3360,7 +3390,14 @@ export function loadShields(shieldImages) { ); // Italy - shields["IT:A-road"] = octagonShieldGreen; + shields["IT:A-road"] = octagonShield( + 2, + 10, + Color.shields.green, + Color.shields.white, + Color.shields.white, + 0 + ); // Kosovo shields["XK:motorway"] = hexagonVerticalShield( From b313e8727eb80aa3b4e9bf3622e33ce4c355ba41 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Fri, 30 Dec 2022 16:07:50 -0500 Subject: [PATCH 135/514] format Mexico shield SVG --- icons/shield_mx_mx.svg | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/icons/shield_mx_mx.svg b/icons/shield_mx_mx.svg index 27dc1162b..208fd6a19 100644 --- a/icons/shield_mx_mx.svg +++ b/icons/shield_mx_mx.svg @@ -1,17 +1,4 @@ - - - - - - - image/svg+xml - - - - - - - - - + + + From 0503e21f82ac54b8871edc344d98de5b5bfd74bc Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Fri, 30 Dec 2022 19:07:05 -0500 Subject: [PATCH 136/514] rename octagon vertical shield function --- src/js/shield_canvas_draw.js | 2 +- src/js/shield_defs.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index cc5e1c6e8..3d27432cc 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -529,7 +529,7 @@ export function hexagonHorizontal( return ctx; } -export function octagon( +export function octagonVertical( offset, angle, fill, diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 83bdc20a6..0c62e4fea 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -304,7 +304,7 @@ function hexagonHorizontalShield( * @param {*} radius - Corner radius of octagon (defaults to 2) * @returns a shield definition object */ -function octagonShield( +function octagonVerticalShield( offset, angle, fillColor, @@ -317,7 +317,7 @@ function octagonShield( radius = radius ?? 2; return { backgroundDraw: (ref) => - ShieldDraw.octagon( + ShieldDraw.octagonVertical( offset, angle, fillColor, @@ -3390,7 +3390,7 @@ export function loadShields(shieldImages) { ); // Italy - shields["IT:A-road"] = octagonShield( + shields["IT:A-road"] = octagonVerticalShield( 2, 10, Color.shields.green, From 13a3d8944a31c7164315ee14cd351a977fd54f29 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 1 Jan 2023 23:22:02 -0500 Subject: [PATCH 137/514] Increase water prominence --- src/americana.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/americana.js b/src/americana.js index e8bb49180..9bef7290c 100644 --- a/src/americana.js +++ b/src/americana.js @@ -231,14 +231,16 @@ function buildLayers() { layers.push( //The labels at the end of the list draw on top of the layers at the beginning. lyrWater.waterwayLabel, - lyrWater.waterLabel, - lyrWater.waterPointLabel, lyrTransportationLabel.bridgeSpacer, lyrTransportationLabel.label, lyrPark.label, lyrPark.parkLabel, + + lyrWater.waterLabel, + lyrWater.waterPointLabel, + /* The ref label shows up at lower zoom levels and when the long name doesn't fit */ lyrAeroway.airportRefLabel, lyrAeroway.minorAirportRefLabel, From 3f9cdbdc8bc94b58d9ace0fbc71c3cf87dfac530 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Mon, 2 Jan 2023 13:40:38 -0500 Subject: [PATCH 138/514] Restore green hexagon shield to test page --- src/shieldtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shieldtest.js b/src/shieldtest.js index f3ed838c4..e96a40124 100644 --- a/src/shieldtest.js +++ b/src/shieldtest.js @@ -69,7 +69,7 @@ let networks = [ "ID:national", "AU:QLD:MR", - "GR:national", + "GR:motorway", "my:federal", "TR:motorway", "US:NY", From a7339b595c9363c8b664fd29d6145b417ac8c1b3 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Sat, 31 Dec 2022 19:54:23 -0500 Subject: [PATCH 139/514] add pentagon shield draw function --- icons/shield_pent_3.svg | 3 - icons/shield_pent_blue_yellow_2.svg | 3 - icons/shield_pent_blue_yellow_3.svg | 3 - icons/shield_pent_brown_2.svg | 3 - icons/shield_pent_brown_3.svg | 3 - icons/shield_pent_green_2.svg | 3 - icons/shield_pent_green_3.svg | 3 - icons/shield_pent_purple_yellow_3.svg | 3 - src/js/shield_canvas_draw.js | 79 ++-- src/js/shield_defs.js | 515 ++++++++++++++++++++------ 10 files changed, 456 insertions(+), 162 deletions(-) delete mode 100644 icons/shield_pent_3.svg delete mode 100644 icons/shield_pent_blue_yellow_2.svg delete mode 100644 icons/shield_pent_blue_yellow_3.svg delete mode 100644 icons/shield_pent_brown_2.svg delete mode 100644 icons/shield_pent_brown_3.svg delete mode 100644 icons/shield_pent_green_2.svg delete mode 100644 icons/shield_pent_green_3.svg delete mode 100644 icons/shield_pent_purple_yellow_3.svg diff --git a/icons/shield_pent_3.svg b/icons/shield_pent_3.svg deleted file mode 100644 index 33a4ceea1..000000000 --- a/icons/shield_pent_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_pent_blue_yellow_2.svg b/icons/shield_pent_blue_yellow_2.svg deleted file mode 100644 index c76f906b8..000000000 --- a/icons/shield_pent_blue_yellow_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_pent_blue_yellow_3.svg b/icons/shield_pent_blue_yellow_3.svg deleted file mode 100644 index 20d5d07c8..000000000 --- a/icons/shield_pent_blue_yellow_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_pent_brown_2.svg b/icons/shield_pent_brown_2.svg deleted file mode 100644 index e54b0cc07..000000000 --- a/icons/shield_pent_brown_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_pent_brown_3.svg b/icons/shield_pent_brown_3.svg deleted file mode 100644 index e43e29dc3..000000000 --- a/icons/shield_pent_brown_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_pent_green_2.svg b/icons/shield_pent_green_2.svg deleted file mode 100644 index c2aed24a3..000000000 --- a/icons/shield_pent_green_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_pent_green_3.svg b/icons/shield_pent_green_3.svg deleted file mode 100644 index 75eaeb56b..000000000 --- a/icons/shield_pent_green_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_pent_purple_yellow_3.svg b/icons/shield_pent_purple_yellow_3.svg deleted file mode 100644 index ea907ca8d..000000000 --- a/icons/shield_pent_purple_yellow_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index 3d27432cc..08d36f3f5 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -317,15 +317,24 @@ export function diamond(fill, outline, ref, radius, outlineWidth, rectWidth) { return ctx; } -export function homePlate( +export function pentagon( + pointUp, offset, + angle, fill, outline, ref, - radius, + radius1, + radius2, outlineWidth, rectWidth ) { + let angleInRadians = (angle * Math.PI) / 180; + let angleSign = pointUp ? -1 : 1; + let sine = Math.sin(angleSign * angleInRadians); + let cosine = Math.cos(angleInRadians); + let tangent = Math.tan(angleSign * angleInRadians); + if (rectWidth == null) { var shieldWidth = ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR; @@ -341,39 +350,51 @@ export function homePlate( let lineThick = outlineWidth * PXR; let lineWidth = lineThick / 2; - let drawRadius = radius * PXR; - let drawOffset = Math.abs(offset) * PXR; + let drawRadius1 = radius1 * PXR; + let drawRadius2 = radius2 * PXR; + let drawOffset = offset * PXR; let x0 = lineWidth; - let x4 = width - lineWidth; - let y0 = lineWidth; - let y4 = CS - lineWidth; + let x12 = width - lineWidth; + let y0 = pointUp ? CS - lineWidth : lineWidth; + let y5 = pointUp ? lineWidth : CS - lineWidth; - let x1 = x0 + drawRadius; - let x2 = (x0 + x4) / 2; - let x3 = x4 - drawRadius; - let y1 = y0 + drawRadius; - let y3 = y4 - drawOffset; + let y3 = y5 - angleSign * drawOffset; - let drawOffsetTangent = - drawRadius * Math.tan(Math.PI / 4 - Math.asin(drawOffset / (x2 - x0)) / 2); - let y2 = y3 - drawOffsetTangent; - - if (offset < 0) { - y0 = CS - y0; - y1 = CS - y1; - y2 = CS - y2; - y3 = CS - y3; - y4 = CS - y4; - } + let x4 = x0 + (y3 - y0) * tangent; + let x6 = (x0 + x12) / 2; + let x8 = x12 - (y3 - y0) * tangent; + let y1 = y0 + angleSign * radius2 * (1 - sine); + + let offsetAngle = Math.atan(drawOffset / (x6 - x0)); + let offsetSine = Math.sin(offsetAngle); + let offsetCosine = Math.cos(offsetAngle); + + let halfComplementAngle1 = (Math.PI / 2 - offsetAngle + angleInRadians) / 2; + let halfComplementTangent1 = Math.tan(halfComplementAngle1); + + let halfComplementAngle2 = (Math.PI / 2 - angleInRadians) / 2; + let halfComplementTangent2 = Math.tan(halfComplementAngle2); + + let x1 = x0 + drawRadius1 * halfComplementTangent1 * sine; + let x2 = x0 + drawRadius1 * halfComplementTangent1 * offsetCosine; + let x5 = x4 + drawRadius2 * halfComplementTangent2; + let x7 = x8 - drawRadius2 * halfComplementTangent2; + let x10 = x12 - drawRadius1 * halfComplementTangent1 * offsetCosine; + let x11 = x12 - drawRadius1 * halfComplementTangent1 * sine; + let y2 = y3 - angleSign * drawRadius1 * halfComplementTangent1 * cosine; + let y4 = y3 + angleSign * drawRadius1 * halfComplementTangent1 * offsetSine; + + let x3 = x5 - drawRadius2 * sine; + let x9 = x7 + drawRadius2 * sine; ctx.beginPath(); - ctx.moveTo(x2, y4); - ctx.arcTo(x0, y3, x0, y2, drawRadius); - ctx.arcTo(x0, y0, x1, y0, drawRadius); - ctx.lineTo(x3, y0); - ctx.arcTo(x4, y0, x4, y2, drawRadius); - ctx.arcTo(x4, y3, x2, y4, drawRadius); + ctx.moveTo(x6, y5); + ctx.arcTo(x0, y3, x1, y2, drawRadius1); + ctx.arcTo(x4, y0, x5, y0, drawRadius2); + ctx.lineTo(x7, y0); + ctx.arcTo(x8, y0, x11, y2, drawRadius2); + ctx.arcTo(x12, y3, x6, y5, drawRadius1); ctx.closePath(); ctx.lineWidth = lineThick; diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 66c919662..c2f4cd2d7 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -161,6 +161,58 @@ function diamondShield(fillColor, strokeColor, textColor, radius, rectWidth) { }; } +/** + * Draws a shield with a pentagon background + * + * @param {*} offset - Height of diagonal edges + * @param {*} angle - Angle (in degrees) at which sides deviate from vertical + * @param {*} fillColor - Color of pentagon background fill + * @param {*} strokeColor - Color of pentagon outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of pentagon (defaults to variable-width) + * @param {*} radius1 - Corner radius of pointed side of pentagon (defaults to 2) + * @param {*} radius2 - Corner radius of flat side of pentagon (defaults to 2) + * @returns a shield definition object + */ +function pentagonUpShield( + offset, + angle, + fillColor, + strokeColor, + textColor, + radius1, + radius2, + rectWidth +) { + textColor = textColor ?? strokeColor; + radius1 = radius1 ?? 2; + radius2 = radius2 ?? 2; + return { + backgroundDraw: (ref) => + ShieldDraw.pentagon( + true, + offset, + angle, + fillColor, + strokeColor, + ref, + radius1, + radius2, + 1, + rectWidth + ), + textLayoutConstraint: (spaceBounds, textBounds) => + ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius2), + padding: { + left: 2 + 10 * Math.sin((angle * Math.PI) / 180), + right: 2 + 10 * Math.sin((angle * Math.PI) / 180), + top: 1 + offset / 2, + bottom: 4, + }, + textColor: textColor, + }; +} + /** * Draws a shield with a home plate background * @@ -169,37 +221,43 @@ function diamondShield(fillColor, strokeColor, textColor, radius, rectWidth) { * @param {*} strokeColor - Color of home plate outline stroke * @param {*} textColor - Color of text (defaults to strokeColor) * @param {*} rectWidth - Width of home plate (defaults to variable-width) - * @param {*} radius - Corner radius of home plate (defaults to 2) + * @param {*} radius1 - Corner radius of pointed side of home plate (defaults to 2) + * @param {*} radius2 - Corner radius of flat side of home plate (defaults to 2) * @returns a shield definition object */ -function homePlateShield( +function homePlateDownShield( offset, fillColor, strokeColor, textColor, - radius, + radius1, + radius2, rectWidth ) { textColor = textColor ?? strokeColor; - radius = radius ?? 2; + radius1 = radius1 ?? 2; + radius2 = radius2 ?? 2; return { backgroundDraw: (ref) => - ShieldDraw.homePlate( + ShieldDraw.pentagon( + false, offset, + 0, fillColor, strokeColor, ref, - radius, + radius1, + radius2, 1, rectWidth ), textLayoutConstraint: (spaceBounds, textBounds) => - ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius), + ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius2), padding: { left: 2, right: 2, - top: 2 + (offset < 0 ? -offset - 1 : 0), - bottom: 2 + (offset > 0 ? offset - 1 : 0), + top: 2, + bottom: 1 + offset, }, textColor: textColor, }; @@ -468,50 +526,6 @@ export function loadShields(shieldImages) { }, }; - // Pentagon shields - let pentagonShield = { - backgroundImage: shieldImages.shield_pent_3, - textLayoutConstraint: ShieldText.ellipseTextConstraint, - textColor: Color.shields.black, - padding: { - left: 2, - right: 2, - top: 2, - bottom: 5, - }, - }; - - let pentagonShieldBlueYellow = { - ...pentagonShield, - backgroundImage: [ - shieldImages.shield_pent_blue_yellow_2, - shieldImages.shield_pent_blue_yellow_3, - ], - textColor: Color.shields.yellow, - }; - - let pentagonShieldPurpleYellow = { - ...pentagonShieldBlueYellow, - backgroundImage: shieldImages.shield_pent_purple_yellow_3, - }; - - let pentagonShieldGreen = { - ...pentagonShield, - backgroundImage: [ - shieldImages.shield_pent_green_2, - shieldImages.shield_pent_green_3, - ], - textColor: Color.shields.white, - }; - - let pentagonShieldBrown = { - ...pentagonShieldGreen, - backgroundImage: [ - shieldImages.shield_pent_brown_2, - shieldImages.shield_pent_brown_3, - ], - }; - // Other common shield shapes let badgeShield = { backgroundImage: [shieldImages.shield_badge_2, shieldImages.shield_badge_3], @@ -616,7 +630,7 @@ export function loadShields(shieldImages) { }; // Alberta - shields["CA:AB:primary"] = homePlateShield( + shields["CA:AB:primary"] = homePlateDownShield( 5, Color.shields.white, Color.shields.black @@ -641,7 +655,7 @@ export function loadShields(shieldImages) { }; // Manitoba - shields["CA:MB:PTH"] = homePlateShield( + shields["CA:MB:PTH"] = homePlateDownShield( 5, Color.shields.white, Color.shields.black @@ -883,7 +897,7 @@ export function loadShields(shieldImages) { }; // Saskatchewan - shields["CA:SK:primary"] = homePlateShield( + shields["CA:SK:primary"] = homePlateDownShield( 5, Color.shields.blue, Color.shields.white @@ -898,7 +912,7 @@ export function loadShields(shieldImages) { bottom: 2, }, }; - shields["CA:SK:tertiary"] = homePlateShield( + shields["CA:SK:tertiary"] = homePlateDownShield( 5, Color.shields.white, Color.shields.blue @@ -1111,7 +1125,16 @@ export function loadShields(shieldImages) { "Wilcox", "Winston", ].forEach( - (county) => (shields[`US:AL:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:AL:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); // Arkansas @@ -1153,7 +1176,16 @@ export function loadShields(shieldImages) { "Washington", "Woodruff", ].forEach( - (county) => (shields[`US:AR:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:AR:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); ["Lee", "Izard"].forEach( (county) => @@ -1192,7 +1224,16 @@ export function loadShields(shieldImages) { shields["US:AZ:Loop"] = banneredShield(shields["US:AZ"], ["LOOP"]); shields["US:AZ:Business"] = banneredShield(shields["US:AZ"], ["BUS"]); ["Coconino", "Mohave", "Yavapai"].forEach( - (county) => (shields[`US:AZ:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:AZ:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); shields["US:AZ:Apache"] = roundedRectShield( Color.shields.white, @@ -1211,7 +1252,15 @@ export function loadShields(shieldImages) { }, }; shields["US:CA:Business"] = banneredShield(shields["US:CA"], ["BUS"]); - shields["US:CA:CR"] = pentagonShieldBlueYellow; + shields["US:CA:CR"] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + ); shields["US:CA:Mendocino"] = roundedRectShield( Color.shields.green, Color.shields.white @@ -1260,7 +1309,16 @@ export function loadShields(shieldImages) { "San_Juan", "Teller", ].forEach( - (county) => (shields[`US:CO:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:CO:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); ["Fremont", "Ouray", "Routt"].forEach( (county) => @@ -1269,7 +1327,15 @@ export function loadShields(shieldImages) { Color.shields.white )) ); - shields["US:CO:Douglas"] = pentagonShieldGreen; + shields["US:CO:Douglas"] = pentagonUpShield( + 3, + 15, + Color.shields.green, + Color.shields.white, + Color.shields.white, + 2, + 0 + ); // Connecticut shields["US:CT"] = roundedRectShield( @@ -1320,7 +1386,15 @@ export function loadShields(shieldImages) { norefImage: shieldImages.shield_us_fl_turnpike, notext: true, }; - shields["US:FL:CR"] = pentagonShieldBlueYellow; + shields["US:FL:CR"] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + ); // Georgia shields["US:GA"] = { @@ -1362,7 +1436,15 @@ export function loadShields(shieldImages) { // Iowa shields["US:IA"] = pillShield(Color.shields.white, Color.shields.black); - shields["US:IA:CR"] = pentagonShieldBlueYellow; + shields["US:IA:CR"] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + ); // Idaho shields["US:ID"] = { @@ -1424,7 +1506,16 @@ export function loadShields(shieldImages) { "Winnebago", "Woodford", ].forEach( - (county) => (shields[`US:IL:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:IL:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); shields["US:IL:Cook:Chicago:Skyway"] = { norefImage: shieldImages.shield_us_il_skyway, @@ -1470,7 +1561,16 @@ export function loadShields(shieldImages) { "Riley", "Sheridan", ].forEach( - (county) => (shields[`US:KS:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:KS:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); // Kentucky @@ -1521,7 +1621,16 @@ export function loadShields(shieldImages) { "Webster", "Winn", ].forEach( - (parish) => (shields[`US:LA:${parish}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:LA:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); // Massachusetts @@ -1568,7 +1677,16 @@ export function loadShields(shieldImages) { 24 ); ["CR", "Benzie", "Gogebic", "Kalkaska", "Montcalm", "Roscommon"].forEach( - (county) => (shields[`US:MI:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:MI:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); ["Delta", "Manistee"].forEach( (county) => @@ -1691,10 +1809,15 @@ export function loadShields(shieldImages) { shields[`US:MN:${county}:CR`], shields[`US:MN:${county}:Park_Access`], ] = [ - { - ...pentagonShieldBlueYellow, - textColor: Color.shields.white, - }, + pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.white, + 2, + 0 + ), roundedRectShield(Color.shields.white, Color.shields.black), trapezoidShield( 10, @@ -1740,7 +1863,16 @@ export function loadShields(shieldImages) { "Taney", "Wayne", ].forEach( - (county) => (shields[`US:MO:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:MO:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); shields["US:MO:Lewis"] = roundedRectShield( Color.shields.brown, @@ -1774,7 +1906,16 @@ export function loadShields(shieldImages) { "Union", "Yalobusha", ].forEach( - (county) => (shields[`US:MS:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:MS:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); // Montana @@ -1793,7 +1934,16 @@ export function loadShields(shieldImages) { }, }; ["Dawson", "Richland"].forEach( - (county) => (shields[`US:MT:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:MT:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); // North Carolina @@ -1807,7 +1957,15 @@ export function loadShields(shieldImages) { shields["US:NC:Bypass"] = banneredShield(shields["US:NC"], ["BYP"]); shields["US:NC:Business"] = banneredShield(shields["US:NC"], ["BUS"]); shields["US:NC:Truck"] = banneredShield(shields["US:NC"], ["TRK"]); - shields["US:NC:Mecklenburg:Charlotte"] = pentagonShieldGreen; + shields["US:NC:Mecklenburg:Charlotte"] = pentagonUpShield( + 3, + 15, + Color.shields.green, + Color.shields.white, + Color.shields.white, + 2, + 0 + ); // North Dakota shields["US:ND"] = { @@ -1859,7 +2017,16 @@ export function loadShields(shieldImages) { "Wells", "Williams", ].forEach( - (county) => (shields[`US:ND:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:ND:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); ["Eddy", "Kidder"].forEach( (county) => @@ -1938,7 +2105,16 @@ export function loadShields(shieldImages) { "Union", "Warren", ].forEach( - (county) => (shields[`US:NJ:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:NJ:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); shields["US:NJ:Bergen"] = roundedRectShield( Color.shields.white, @@ -1983,13 +2159,26 @@ export function loadShields(shieldImages) { "Torrance", "Union", ].forEach( - (county) => (shields[`US:NM:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:NM:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) + ); + shields["US:NM:San_Juan:NCM"] = pentagonUpShield( + 3, + 15, + Color.shields.white, + Color.shields.pink, + Color.shields.pink, + 2, + 0 ); - shields["US:NM:San_Juan:NCM"] = { - ...pentagonShield, - colorLighten: Color.shields.pink, - textColor: Color.shields.pink, - }; // Nevada shields["US:NV"] = { @@ -2003,7 +2192,16 @@ export function loadShields(shieldImages) { }, }; ["Clark", "Washoe"].forEach( - (county) => (shields[`US:NV:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:NV:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); // New York @@ -2089,7 +2287,16 @@ export function loadShields(shieldImages) { "Washington", "Yates", ].forEach( - (county) => (shields[`US:NY:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:NY:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); // Ohio @@ -2110,7 +2317,16 @@ export function loadShields(shieldImages) { // Ohio county and township roads ["COL", "JEF", "MAH", "OTT", "SEN", "STA", "SUM", "TUS"].forEach( - (county) => (shields[`US:OH:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:OH:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); [ "CAR", @@ -2310,7 +2526,16 @@ export function loadShields(shieldImages) { }; shields["US:OR:Business"] = banneredShield(shields["US:OR"], ["BUS"]); ["Douglas", "Grant", "Lake", "Lane", "Morrow"].forEach( - (county) => (shields[`US:OR:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:OR:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); // Pennsylvania @@ -2346,7 +2571,15 @@ export function loadShields(shieldImages) { // Puerto Rico shields["US:PR:primary"] = escutcheonShieldBlue; shields["US:PR:primary_urban"] = escutcheonShield; - shields["US:PR:secondary"] = pentagonShieldBlueYellow; + shields["US:PR:secondary"] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + ); shields["US:PR:tertiary"] = ovalShield( Color.shields.white, Color.shields.black @@ -2419,7 +2652,16 @@ export function loadShields(shieldImages) { "Union", "Yankton", ].forEach( - (county) => (shields[`US:SD:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:SD:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); ["Brown", "Tripp"].forEach( (county) => @@ -2462,7 +2704,15 @@ export function loadShields(shieldImages) { shields["US:TN:secondary"], ["TRK"] ); - shields["US:TN:McMinn"] = pentagonShieldBlueYellow; + shields["US:TN:McMinn"] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + ); // Texas shields["US:TX"] = roundedRectShield( @@ -2519,7 +2769,7 @@ export function loadShields(shieldImages) { shields["US:TX:CTRMA:Express"] = banneredShield(shields["US:TX:CTRMA"], [ "EXPR", ]); - shields["US:TX:Montgomery:MCTRA"] = homePlateShield( + shields["US:TX:Montgomery:MCTRA"] = homePlateDownShield( 5, Color.shields.blue, Color.shields.red, @@ -2535,7 +2785,15 @@ export function loadShields(shieldImages) { bottom: 8, }, }; - shields["US:TX:Harris:HCTRA"] = pentagonShieldPurpleYellow; + shields["US:TX:Harris:HCTRA"] = pentagonUpShield( + 3, + 15, + Color.shields.purple, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + ); // Texas county roads [ @@ -2558,7 +2816,16 @@ export function loadShields(shieldImages) { "Uvalde", "Winkler", ].forEach( - (county) => (shields[`US:TX:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:TX:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); ["Brazoria", "Brown", "Burleson", "Colorado", "Comanche", "Houston"].forEach( (county) => @@ -2606,7 +2873,15 @@ export function loadShields(shieldImages) { bottom: 5, }, }; - shields["US:UT:Wayne"] = pentagonShieldBlueYellow; + shields["US:UT:Wayne"] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + ); // Virginia shields["US:VA"] = escutcheonShieldRounded; @@ -2792,7 +3067,16 @@ export function loadShields(shieldImages) { "Washakie", "Weston", ].forEach( - (county) => (shields[`US:WY:${county}`] = pentagonShieldBlueYellow) + (county) => + (shields[`US:WY:${county}`] = pentagonUpShield( + 3, + 15, + Color.shields.blue, + Color.shields.yellow, + Color.shields.yellow, + 2, + 0 + )) ); // SOUTH AMERICA @@ -2833,7 +3117,11 @@ export function loadShields(shieldImages) { }; // Uruguay - shields["UY"] = homePlateShield(5, Color.shields.blue, Color.shields.white); + shields["UY"] = homePlateDownShield( + 5, + Color.shields.blue, + Color.shields.white + ); // Venezuela [ @@ -3153,12 +3441,12 @@ export function loadShields(shieldImages) { ); // Philippines - shields["PH:N"] = homePlateShield( + shields["PH:N"] = homePlateDownShield( 5, Color.shields.white, Color.shields.black ); - shields["PH:E"] = homePlateShield( + shields["PH:E"] = homePlateDownShield( 5, Color.shields.yellow, Color.shields.black @@ -3383,13 +3671,14 @@ export function loadShields(shieldImages) { ); // Hungary - shields["HU:national"] = homePlateShield( - 4, + shields["HU:national"] = homePlateDownShield( + 3, Color.shields.blue, Color.shields.white, Color.shields.white, - 2, - 30 + 4, + 0, + 26 ); // Iceland @@ -3653,16 +3942,24 @@ export function loadShields(shieldImages) { shields[`AU:${state_or_territory}:ALT_S`], ] = [ roundedRectShield(Color.shields.green, Color.shields.yellow), - homePlateShield(5, Color.shields.green, Color.shields.yellow), - homePlateShield(5, Color.shields.white, Color.shields.black), + homePlateDownShield(5, Color.shields.green, Color.shields.yellow), + homePlateDownShield(5, Color.shields.white, Color.shields.black), fishheadShieldBlue, - pentagonShieldBrown, + pentagonUpShield( + 3, + 15, + Color.shields.brown, + Color.shields.white, + Color.shields.white, + 2, + 0 + ), banneredShield( roundedRectShield(Color.shields.green, Color.shields.yellow), ["ALT"] ), banneredShield( - homePlateShield(5, Color.shields.white, Color.shields.black), + homePlateDownShield(5, Color.shields.white, Color.shields.black), ["ALT"] ), banneredShield(fishheadShieldBlue, ["ALT"]), @@ -3684,7 +3981,7 @@ export function loadShields(shieldImages) { // New Zealand shields["NZ:SH"] = fishheadShieldRed; - shields["NZ:UR"] = homePlateShield( + shields["NZ:UR"] = homePlateDownShield( 5, Color.shields.white, Color.shields.black From ea81de2e32352ea8c4fe99df1a9d12f7d9e1e13e Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Mon, 2 Jan 2023 14:58:05 -0500 Subject: [PATCH 140/514] refactor to do math in radians --- src/js/shield_canvas_draw.js | 56 +++++----- src/js/shield_defs.js | 202 ++++++++++++++++++++++++++++++----- 2 files changed, 204 insertions(+), 54 deletions(-) diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index 08d36f3f5..d029a888d 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -181,6 +181,7 @@ export function roundedRectangle( } export function trapezoid( + shortSideUp, angle, fill, outline, @@ -189,11 +190,10 @@ export function trapezoid( outlineWidth, rectWidth ) { - let angleInRadians = (angle * Math.PI) / 180; - let angleSign = Math.sign(angle); - let sine = Math.sin(Math.abs(angleInRadians)); - let cosine = Math.cos(angleInRadians); - let tangent = Math.tan(angleInRadians); + let angleSign = shortSideUp ? -1 : 1; + let sine = Math.sin(angle); + let cosine = Math.cos(angle); + let tangent = Math.tan(angle); if (rectWidth == null) { var shieldWidth = @@ -215,22 +215,23 @@ export function trapezoid( let x0 = lineWidth; let x11 = width - lineWidth; - let y0 = angle > 0 ? lineWidth : CS - lineWidth; - let y3 = angle > 0 ? CS - lineWidth : lineWidth; + let y0 = shortSideUp ? CS - lineWidth : lineWidth; + let y3 = shortSideUp ? lineWidth : CS - lineWidth; let y1 = y0 + angleSign * drawRadius * (1 + sine); let y2 = y3 - angleSign * drawRadius * (1 - sine); let x1 = x0 + (y1 - y0) * tangent; let x2 = x1 + drawRadius * cosine; - let x3 = x0 + (y2 - y0) * tangent; - let x4 = x0 + (y3 - y0) * tangent; - let x5 = x3 + drawRadius * cosine; + let x3 = x0 + angleSign * (y2 - y0) * tangent; + let x4 = x0 + angleSign * (y3 - y0) * tangent; + let x5 = x3 + angleSign * drawRadius * cosine; // let x6 = width - x5; let x7 = width - x4; let x8 = width - x3; let x9 = width - x2; // let x10 = width - x1; + console.log([x0, x1, x2, x3, x4, x5, x7, x8, x9, x11]); ctx.beginPath(); ctx.moveTo(x9, y0); @@ -329,11 +330,10 @@ export function pentagon( outlineWidth, rectWidth ) { - let angleInRadians = (angle * Math.PI) / 180; let angleSign = pointUp ? -1 : 1; - let sine = Math.sin(angleSign * angleInRadians); - let cosine = Math.cos(angleInRadians); - let tangent = Math.tan(angleSign * angleInRadians); + let sine = Math.sin(angleSign * angle); + let cosine = Math.cos(angle); + let tangent = Math.tan(angleSign * angle); if (rectWidth == null) { var shieldWidth = @@ -370,10 +370,10 @@ export function pentagon( let offsetSine = Math.sin(offsetAngle); let offsetCosine = Math.cos(offsetAngle); - let halfComplementAngle1 = (Math.PI / 2 - offsetAngle + angleInRadians) / 2; + let halfComplementAngle1 = (Math.PI / 2 - offsetAngle + angle) / 2; let halfComplementTangent1 = Math.tan(halfComplementAngle1); - let halfComplementAngle2 = (Math.PI / 2 - angleInRadians) / 2; + let halfComplementAngle2 = (Math.PI / 2 - angle) / 2; let halfComplementTangent2 = Math.tan(halfComplementAngle2); let x1 = x0 + drawRadius1 * halfComplementTangent1 * sine; @@ -483,11 +483,10 @@ export function hexagonHorizontal( outlineWidth, rectWidth ) { - let angleInRadians = (angle * Math.PI) / 180; - let sine = Math.sin(Math.abs(angleInRadians)); - let cosine = Math.cos(angleInRadians); - let tangent = Math.tan(angleInRadians); - let halfComplementTangent = Math.tan(Math.PI / 4 - angleInRadians / 2); + let sine = Math.sin(angle); + let cosine = Math.cos(angle); + let tangent = Math.tan(angle); + let halfComplementTangent = Math.tan(Math.PI / 4 - angle / 2); if (rectWidth == null) { var shieldWidth = @@ -560,10 +559,9 @@ export function octagonVertical( outlineWidth, rectWidth ) { - let angleInRadians = (angle * Math.PI) / 180; - let sine = Math.sin(angleInRadians); - let cosine = Math.cos(angleInRadians); - let tangent = Math.tan(angleInRadians); + let sine = Math.sin(angle); + let cosine = Math.cos(angle); + let tangent = Math.tan(angle); if (rectWidth == null) { var shieldWidth = @@ -604,15 +602,13 @@ export function octagonVertical( let offsetSine = Math.sin(offsetAngle); let offsetCosine = Math.cos(offsetAngle); - let halfComplementAngle = (Math.PI / 2 - angleInRadians - offsetAngle) / 2; + let halfComplementAngle = (Math.PI / 2 - angle - offsetAngle) / 2; let halfComplementCosine = Math.cos(halfComplementAngle); let dx = - (drawRadius * Math.cos(angleInRadians + halfComplementAngle)) / - halfComplementCosine; + (drawRadius * Math.cos(angle + halfComplementAngle)) / halfComplementCosine; let dy = - (drawRadius * Math.sin(angleInRadians + halfComplementAngle)) / - halfComplementCosine; + (drawRadius * Math.sin(angle + halfComplementAngle)) / halfComplementCosine; let x2 = x3 + dx - drawRadius * cosine; let x4 = x3 + dx - drawRadius * offsetSine; diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index c2f4cd2d7..86b9eb3e6 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -91,17 +91,17 @@ function roundedRectShield( } /** - * Draws a shield with a trapezoid background + * Draws a shield with a trapezoid background, with the short side on bottom * * @param {*} angle - Angle (in degrees) at which sides deviate from vertical * @param {*} fillColor - Color of trapezoid background fill * @param {*} strokeColor - Color of trapezoid outline stroke * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius - Corner radius of trapezoid (defaults to 0) * @param {*} rectWidth - Width of trapezoid (defaults to variable-width) - * @param {*} radius - Corner radius of trapezoid (defaults to 2) * @returns a shield definition object */ -function trapezoidShield( +function trapezoidDownShield( angle, fillColor, strokeColor, @@ -109,12 +109,14 @@ function trapezoidShield( radius, rectWidth ) { + let angleInRadians = (angle * Math.PI) / 180; textColor = textColor ?? strokeColor; radius = radius ?? 0; return { backgroundDraw: (ref) => ShieldDraw.trapezoid( - angle, + false, + angleInRadians, fillColor, strokeColor, ref, @@ -125,10 +127,56 @@ function trapezoidShield( textLayoutConstraint: (spaceBounds, textBounds) => ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius), padding: { - left: 3, - right: 3, - top: 3 - Math.sign(angle), - bottom: 3 + Math.sign(angle), + left: 2 + 10 * Math.tan(angleInRadians), + right: 2 + 10 * Math.tan(angleInRadians), + top: 2, + bottom: 4, + }, + textColor: textColor, + }; +} + +/** + * Draws a shield with a trapezoid background, with the short side on top + * + * @param {*} angle - Angle (in degrees) at which sides deviate from vertical + * @param {*} fillColor - Color of trapezoid background fill + * @param {*} strokeColor - Color of trapezoid outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius - Corner radius of trapezoid (defaults to 0) + * @param {*} rectWidth - Width of trapezoid (defaults to variable-width) + * @returns a shield definition object + */ +function trapezoidUpShield( + angle, + fillColor, + strokeColor, + textColor, + radius, + rectWidth +) { + let angleInRadians = (angle * Math.PI) / 180; + textColor = textColor ?? strokeColor; + radius = radius ?? 0; + return { + backgroundDraw: (ref) => + ShieldDraw.trapezoid( + true, + angleInRadians, + fillColor, + strokeColor, + ref, + radius, + 1, + rectWidth + ), + textLayoutConstraint: (spaceBounds, textBounds) => + ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius), + padding: { + left: 2 + 10 * Math.tan(angleInRadians), + right: 2 + 10 * Math.tan(angleInRadians), + top: 4, + bottom: 2, }, textColor: textColor, }; @@ -162,7 +210,60 @@ function diamondShield(fillColor, strokeColor, textColor, radius, rectWidth) { } /** - * Draws a shield with a pentagon background + * Draws a shield with a pentagon background, pointed downward + * + * @param {*} offset - Height of diagonal edges + * @param {*} angle - Angle (in degrees) at which sides deviate from vertical + * @param {*} fillColor - Color of pentagon background fill + * @param {*} strokeColor - Color of pentagon outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of pentagon (defaults to variable-width) + * @param {*} radius1 - Corner radius of pointed side of pentagon (defaults to 2) + * @param {*} radius2 - Corner radius of flat side of pentagon (defaults to 2) + * @returns a shield definition object + */ +function pentagonDownShield( + offset, + angle, + fillColor, + strokeColor, + textColor, + radius1, + radius2, + rectWidth +) { + let angleInRadians = (angle * Math.PI) / 180; + textColor = textColor ?? strokeColor; + radius1 = radius1 ?? 2; + radius2 = radius2 ?? 2; + return { + backgroundDraw: (ref) => + ShieldDraw.pentagon( + false, + offset, + angleInRadians, + fillColor, + strokeColor, + ref, + radius1, + radius2, + 1, + rectWidth + ), + textLayoutConstraint: (spaceBounds, textBounds) => + ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius2), + padding: { + left: 2 + 10 * Math.sin(angleInRadians), + right: 2 + 10 * Math.sin(angleInRadians), + top: 3, + bottom: 2 + offset / 2, + }, + textColor: textColor, + }; +} + +/** + * Draws a shield with a pentagon background, pointed upward * * @param {*} offset - Height of diagonal edges * @param {*} angle - Angle (in degrees) at which sides deviate from vertical @@ -184,6 +285,7 @@ function pentagonUpShield( radius2, rectWidth ) { + let angleInRadians = (angle * Math.PI) / 180; textColor = textColor ?? strokeColor; radius1 = radius1 ?? 2; radius2 = radius2 ?? 2; @@ -192,7 +294,7 @@ function pentagonUpShield( ShieldDraw.pentagon( true, offset, - angle, + angleInRadians, fillColor, strokeColor, ref, @@ -204,8 +306,8 @@ function pentagonUpShield( textLayoutConstraint: (spaceBounds, textBounds) => ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius2), padding: { - left: 2 + 10 * Math.sin((angle * Math.PI) / 180), - right: 2 + 10 * Math.sin((angle * Math.PI) / 180), + left: 2 + 10 * Math.sin(angleInRadians), + right: 2 + 10 * Math.sin(angleInRadians), top: 1 + offset / 2, bottom: 4, }, @@ -214,7 +316,7 @@ function pentagonUpShield( } /** - * Draws a shield with a home plate background + * Draws a shield with a home plate background, pointed downward * * @param {*} offset - Height of diagonal edges * @param {*} fillColor - Color of home plate background fill @@ -263,6 +365,56 @@ function homePlateDownShield( }; } +/** + * Draws a shield with a home plate background, pointed upward + * + * @param {*} offset - Height of diagonal edges + * @param {*} fillColor - Color of home plate background fill + * @param {*} strokeColor - Color of home plate outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} rectWidth - Width of home plate (defaults to variable-width) + * @param {*} radius1 - Corner radius of pointed side of home plate (defaults to 2) + * @param {*} radius2 - Corner radius of flat side of home plate (defaults to 2) + * @returns a shield definition object + */ +function homePlateUpShield( + offset, + fillColor, + strokeColor, + textColor, + radius1, + radius2, + rectWidth +) { + textColor = textColor ?? strokeColor; + radius1 = radius1 ?? 2; + radius2 = radius2 ?? 2; + return { + backgroundDraw: (ref) => + ShieldDraw.pentagon( + true, + offset, + 0, + fillColor, + strokeColor, + ref, + radius1, + radius2, + 1, + rectWidth + ), + textLayoutConstraint: (spaceBounds, textBounds) => + ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius2), + padding: { + left: 2, + right: 2, + top: 1 + offset, + bottom: 2, + }, + textColor: textColor, + }; +} + /** * Draws a shield with a vertically-aligned hexagon background * @@ -326,12 +478,13 @@ function hexagonHorizontalShield( radius, rectWidth ) { + let angleInRadians = (angle * Math.PI) / 180; textColor = textColor ?? strokeColor; radius = radius ?? 2; return { backgroundDraw: (ref) => ShieldDraw.hexagonHorizontal( - angle, + angleInRadians, fillColor, strokeColor, ref, @@ -371,13 +524,14 @@ function octagonVerticalShield( radius, rectWidth ) { + let angleInRadians = (angle * Math.PI) / 180; textColor = textColor ?? strokeColor; radius = radius ?? 2; return { backgroundDraw: (ref) => ShieldDraw.octagonVertical( offset, - angle, + angleInRadians, fillColor, strokeColor, ref, @@ -746,8 +900,8 @@ export function loadShields(shieldImages) { pillShield(Color.shields.white, Color.shields.blue, Color.shields.black), ["ETR"] ); - shields["CA:ON:secondary"] = trapezoidShield( - -10, + shields["CA:ON:secondary"] = trapezoidUpShield( + 10, Color.shields.white, Color.shields.black ); @@ -755,24 +909,24 @@ export function loadShields(shieldImages) { Color.shields.white, Color.shields.black ); - shields["CA:ON:Halton"] = trapezoidShield( + shields["CA:ON:Halton"] = trapezoidDownShield( 10, Color.shields.green, Color.shields.yellow ); - shields["CA:ON:Peel"] = trapezoidShield( + shields["CA:ON:Peel"] = trapezoidDownShield( 10, Color.shields.black, Color.shields.yellow ); - shields["CA:ON:Simcoe"] = trapezoidShield( + shields["CA:ON:Simcoe"] = trapezoidDownShield( 10, Color.shields.white, Color.shields.blue ); ["Grey", "Hamilton", "Niagara"].forEach( (county) => - (shields[`CA:ON:${county}`] = trapezoidShield( + (shields[`CA:ON:${county}`] = trapezoidDownShield( 10, Color.shields.blue, Color.shields.white @@ -823,7 +977,7 @@ export function loadShields(shieldImages) { "York", ].forEach( (countyTownshipOrCity) => - (shields[`CA:ON:${countyTownshipOrCity}`] = trapezoidShield( + (shields[`CA:ON:${countyTownshipOrCity}`] = trapezoidDownShield( 10, Color.shields.white, Color.shields.black @@ -1819,7 +1973,7 @@ export function loadShields(shieldImages) { 0 ), roundedRectShield(Color.shields.white, Color.shields.black), - trapezoidShield( + trapezoidDownShield( 10, Color.shields.brown, Color.shields.white, @@ -2041,7 +2195,7 @@ export function loadShields(shieldImages) { ); // Nebraska - shields["US:NE"] = trapezoidShield( + shields["US:NE"] = trapezoidDownShield( 10, Color.shields.white, Color.shields.black From e616a946ad3ccef3f2cb09bcb7588fb2173f748c Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Mon, 2 Jan 2023 15:22:15 -0500 Subject: [PATCH 141/514] remove default values from function calls --- src/js/shield_defs.js | 191 ++++++++++-------------------------------- src/shieldtest.js | 2 +- 2 files changed, 47 insertions(+), 146 deletions(-) diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 86b9eb3e6..72af84640 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -188,8 +188,8 @@ function trapezoidUpShield( * @param {*} fillColor - Color of diamond background fill * @param {*} strokeColor - Color of diamond outline stroke * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of diamond (defaults to variable-width) * @param {*} radius - Corner radius of diamond (defaults to 2) + * @param {*} rectWidth - Width of diamond (defaults to variable-width) * @returns a shield definition object */ function diamondShield(fillColor, strokeColor, textColor, radius, rectWidth) { @@ -217,9 +217,9 @@ function diamondShield(fillColor, strokeColor, textColor, radius, rectWidth) { * @param {*} fillColor - Color of pentagon background fill * @param {*} strokeColor - Color of pentagon outline stroke * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of pentagon (defaults to variable-width) * @param {*} radius1 - Corner radius of pointed side of pentagon (defaults to 2) - * @param {*} radius2 - Corner radius of flat side of pentagon (defaults to 2) + * @param {*} radius2 - Corner radius of flat side of pentagon (defaults to 0) + * @param {*} rectWidth - Width of pentagon (defaults to variable-width) * @returns a shield definition object */ function pentagonDownShield( @@ -235,7 +235,7 @@ function pentagonDownShield( let angleInRadians = (angle * Math.PI) / 180; textColor = textColor ?? strokeColor; radius1 = radius1 ?? 2; - radius2 = radius2 ?? 2; + radius2 = radius2 ?? 0; return { backgroundDraw: (ref) => ShieldDraw.pentagon( @@ -270,9 +270,9 @@ function pentagonDownShield( * @param {*} fillColor - Color of pentagon background fill * @param {*} strokeColor - Color of pentagon outline stroke * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of pentagon (defaults to variable-width) * @param {*} radius1 - Corner radius of pointed side of pentagon (defaults to 2) - * @param {*} radius2 - Corner radius of flat side of pentagon (defaults to 2) + * @param {*} radius2 - Corner radius of flat side of pentagon (defaults to 0) + * @param {*} rectWidth - Width of pentagon (defaults to variable-width) * @returns a shield definition object */ function pentagonUpShield( @@ -288,7 +288,7 @@ function pentagonUpShield( let angleInRadians = (angle * Math.PI) / 180; textColor = textColor ?? strokeColor; radius1 = radius1 ?? 2; - radius2 = radius2 ?? 2; + radius2 = radius2 ?? 0; return { backgroundDraw: (ref) => ShieldDraw.pentagon( @@ -322,9 +322,9 @@ function pentagonUpShield( * @param {*} fillColor - Color of home plate background fill * @param {*} strokeColor - Color of home plate outline stroke * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of home plate (defaults to variable-width) * @param {*} radius1 - Corner radius of pointed side of home plate (defaults to 2) * @param {*} radius2 - Corner radius of flat side of home plate (defaults to 2) + * @param {*} rectWidth - Width of home plate (defaults to variable-width) * @returns a shield definition object */ function homePlateDownShield( @@ -372,9 +372,9 @@ function homePlateDownShield( * @param {*} fillColor - Color of home plate background fill * @param {*} strokeColor - Color of home plate outline stroke * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of home plate (defaults to variable-width) * @param {*} radius1 - Corner radius of pointed side of home plate (defaults to 2) * @param {*} radius2 - Corner radius of flat side of home plate (defaults to 2) + * @param {*} rectWidth - Width of home plate (defaults to variable-width) * @returns a shield definition object */ function homePlateUpShield( @@ -422,8 +422,8 @@ function homePlateUpShield( * @param {*} fillColor - Color of hexagon background fill * @param {*} strokeColor - Color of hexagon outline stroke * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of hexagon (defaults to variable-width) * @param {*} radius - Corner radius of hexagon (defaults to 2) + * @param {*} rectWidth - Width of hexagon (defaults to variable-width) * @returns a shield definition object */ function hexagonVerticalShield( @@ -466,8 +466,8 @@ function hexagonVerticalShield( * @param {*} fillColor - Color of hexagon background fill * @param {*} strokeColor - Color of hexagon outline stroke * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of hexagon (defaults to variable-width) * @param {*} radius - Corner radius of hexagon (defaults to 2) + * @param {*} rectWidth - Width of hexagon (defaults to variable-width) * @returns a shield definition object */ function hexagonHorizontalShield( @@ -511,8 +511,8 @@ function hexagonHorizontalShield( * @param {*} fillColor - Color of octagon background fill * @param {*} strokeColor - Color of octagon outline stroke * @param {*} textColor - Color of text (defaults to strokeColor) - * @param {*} rectWidth - Width of octagon (defaults to variable-width) * @param {*} radius - Corner radius of octagon (defaults to 2) + * @param {*} rectWidth - Width of octagon (defaults to variable-width) * @returns a shield definition object */ function octagonVerticalShield( @@ -1284,10 +1284,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); @@ -1335,10 +1332,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); ["Lee", "Izard"].forEach( @@ -1383,10 +1377,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); shields["US:AZ:Apache"] = roundedRectShield( @@ -1410,10 +1401,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow ); shields["US:CA:Mendocino"] = roundedRectShield( Color.shields.green, @@ -1468,10 +1456,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); ["Fremont", "Ouray", "Routt"].forEach( @@ -1485,10 +1470,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.green, - Color.shields.white, - Color.shields.white, - 2, - 0 + Color.shields.white ); // Connecticut @@ -1544,10 +1526,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow ); // Georgia @@ -1594,10 +1573,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow ); // Idaho @@ -1665,10 +1641,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); shields["US:IL:Cook:Chicago:Skyway"] = { @@ -1720,10 +1693,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); @@ -1780,10 +1750,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); @@ -1836,10 +1803,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); ["Delta", "Manistee"].forEach( @@ -1968,9 +1932,7 @@ export function loadShields(shieldImages) { 15, Color.shields.blue, Color.shields.yellow, - Color.shields.white, - 2, - 0 + Color.shields.white ), roundedRectShield(Color.shields.white, Color.shields.black), trapezoidDownShield( @@ -2022,10 +1984,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); shields["US:MO:Lewis"] = roundedRectShield( @@ -2065,10 +2024,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); @@ -2093,10 +2049,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); @@ -2115,10 +2068,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.green, - Color.shields.white, - Color.shields.white, - 2, - 0 + Color.shields.white ); // North Dakota @@ -2176,10 +2126,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); ["Eddy", "Kidder"].forEach( @@ -2264,10 +2211,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); shields["US:NJ:Bergen"] = roundedRectShield( @@ -2318,20 +2262,14 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); shields["US:NM:San_Juan:NCM"] = pentagonUpShield( 3, 15, Color.shields.white, - Color.shields.pink, - Color.shields.pink, - 2, - 0 + Color.shields.pink ); // Nevada @@ -2351,10 +2289,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); @@ -2446,10 +2381,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); @@ -2476,10 +2408,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); [ @@ -2685,10 +2614,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); @@ -2729,10 +2655,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow ); shields["US:PR:tertiary"] = ovalShield( Color.shields.white, @@ -2811,10 +2734,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); ["Brown", "Tripp"].forEach( @@ -2862,10 +2782,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow ); // Texas @@ -2946,7 +2863,8 @@ export function loadShields(shieldImages) { Color.shields.yellow, Color.shields.yellow, 2, - 0 + 0, + 28 ); // Texas county roads @@ -2975,10 +2893,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); ["Brazoria", "Brown", "Burleson", "Colorado", "Comanche", "Houston"].forEach( @@ -3031,10 +2946,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow ); // Virginia @@ -3226,10 +3138,7 @@ export function loadShields(shieldImages) { 3, 15, Color.shields.blue, - Color.shields.yellow, - Color.shields.yellow, - 2, - 0 + Color.shields.yellow )) ); @@ -4099,15 +4008,7 @@ export function loadShields(shieldImages) { homePlateDownShield(5, Color.shields.green, Color.shields.yellow), homePlateDownShield(5, Color.shields.white, Color.shields.black), fishheadShieldBlue, - pentagonUpShield( - 3, - 15, - Color.shields.brown, - Color.shields.white, - Color.shields.white, - 2, - 0 - ), + pentagonUpShield(3, 15, Color.shields.brown, Color.shields.white), banneredShield( roundedRectShield(Color.shields.green, Color.shields.yellow), ["ALT"] diff --git a/src/shieldtest.js b/src/shieldtest.js index f3ed838c4..b920f6b35 100644 --- a/src/shieldtest.js +++ b/src/shieldtest.js @@ -61,8 +61,8 @@ let networks = [ "US:CO:Douglas", "US:MN:Aitkin:CSAH", "AU:ACT:T", - "US:TX:Harris:HCTRA", "US:NM:San_Juan:NCM", + "US:TX:Harris:HCTRA", "JP:prefectural", "PK:national", From e6efed71b444094ba4d72e50b0c164681a20ccb7 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Mon, 2 Jan 2023 15:37:29 -0500 Subject: [PATCH 142/514] comment out unused variables --- src/js/shield_canvas_draw.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index d029a888d..93ece9fe7 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -364,7 +364,7 @@ export function pentagon( let x4 = x0 + (y3 - y0) * tangent; let x6 = (x0 + x12) / 2; let x8 = x12 - (y3 - y0) * tangent; - let y1 = y0 + angleSign * radius2 * (1 - sine); + // let y1 = y0 + angleSign * radius2 * (1 - sine); let offsetAngle = Math.atan(drawOffset / (x6 - x0)); let offsetSine = Math.sin(offsetAngle); @@ -377,16 +377,16 @@ export function pentagon( let halfComplementTangent2 = Math.tan(halfComplementAngle2); let x1 = x0 + drawRadius1 * halfComplementTangent1 * sine; - let x2 = x0 + drawRadius1 * halfComplementTangent1 * offsetCosine; + // let x2 = x0 + drawRadius1 * halfComplementTangent1 * offsetCosine; let x5 = x4 + drawRadius2 * halfComplementTangent2; let x7 = x8 - drawRadius2 * halfComplementTangent2; - let x10 = x12 - drawRadius1 * halfComplementTangent1 * offsetCosine; + // let x10 = x12 - drawRadius1 * halfComplementTangent1 * offsetCosine; let x11 = x12 - drawRadius1 * halfComplementTangent1 * sine; let y2 = y3 - angleSign * drawRadius1 * halfComplementTangent1 * cosine; - let y4 = y3 + angleSign * drawRadius1 * halfComplementTangent1 * offsetSine; + // let y4 = y3 + angleSign * drawRadius1 * halfComplementTangent1 * offsetSine; - let x3 = x5 - drawRadius2 * sine; - let x9 = x7 + drawRadius2 * sine; + // let x3 = x5 - drawRadius2 * sine; + // let x9 = x7 + drawRadius2 * sine; ctx.beginPath(); ctx.moveTo(x6, y5); @@ -441,9 +441,9 @@ export function hexagonVertical( let y0 = lineWidth; let y5 = CS - lineWidth; - let x1 = x0 + drawRadius; + // let x1 = x0 + drawRadius; let x2 = (x0 + x4) / 2; - let x3 = x4 - drawRadius; + // let x3 = x4 - drawRadius; let y1 = y0 + drawOffset; let y4 = y5 - drawOffset; From e41198609ef47d0e554479e1eeb9b3bd6a2d80a4 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Mon, 2 Jan 2023 16:48:48 -0500 Subject: [PATCH 143/514] better calculation of trapezoid and pentagon widths --- src/js/shield_canvas_draw.js | 23 ++++++++++++----------- src/js/shield_defs.js | 10 +++++----- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index 93ece9fe7..5ad77864a 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -198,7 +198,7 @@ export function trapezoid( if (rectWidth == null) { var shieldWidth = ShieldText.calculateTextWidth(ref, genericShieldFontSize) + - (2 + (CS * sine) / 2) * PXR; + (2 + ((CS / PXR) * tangent) / 2) * PXR; var width = Math.max( minGenericShieldWidth, Math.min(maxGenericShieldWidth, shieldWidth) @@ -331,13 +331,14 @@ export function pentagon( rectWidth ) { let angleSign = pointUp ? -1 : 1; - let sine = Math.sin(angleSign * angle); + let sine = Math.sin(angle); let cosine = Math.cos(angle); - let tangent = Math.tan(angleSign * angle); + let tangent = Math.tan(angle); if (rectWidth == null) { var shieldWidth = - ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR; + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + + (2 + ((CS / PXR - offset) * tangent) / 2) * PXR; var width = Math.max( minGenericShieldWidth, Math.min(maxGenericShieldWidth, shieldWidth) @@ -361,10 +362,10 @@ export function pentagon( let y3 = y5 - angleSign * drawOffset; - let x4 = x0 + (y3 - y0) * tangent; + let x4 = x0 + angleSign * (y3 - y0) * tangent; let x6 = (x0 + x12) / 2; - let x8 = x12 - (y3 - y0) * tangent; - // let y1 = y0 + angleSign * radius2 * (1 - sine); + let x8 = x12 - angleSign * (y3 - y0) * tangent; + // let y1 = y0 + radius2 * (1 - sine); let offsetAngle = Math.atan(drawOffset / (x6 - x0)); let offsetSine = Math.sin(offsetAngle); @@ -376,17 +377,17 @@ export function pentagon( let halfComplementAngle2 = (Math.PI / 2 - angle) / 2; let halfComplementTangent2 = Math.tan(halfComplementAngle2); - let x1 = x0 + drawRadius1 * halfComplementTangent1 * sine; + let x1 = x0 + angleSign * drawRadius1 * halfComplementTangent1 * sine; // let x2 = x0 + drawRadius1 * halfComplementTangent1 * offsetCosine; let x5 = x4 + drawRadius2 * halfComplementTangent2; let x7 = x8 - drawRadius2 * halfComplementTangent2; // let x10 = x12 - drawRadius1 * halfComplementTangent1 * offsetCosine; - let x11 = x12 - drawRadius1 * halfComplementTangent1 * sine; + let x11 = x12 - angleSign * drawRadius1 * halfComplementTangent1 * sine; let y2 = y3 - angleSign * drawRadius1 * halfComplementTangent1 * cosine; // let y4 = y3 + angleSign * drawRadius1 * halfComplementTangent1 * offsetSine; - // let x3 = x5 - drawRadius2 * sine; - // let x9 = x7 + drawRadius2 * sine; + // let x3 = x5 - angleSign * drawRadius2 * sine; + // let x9 = x7 + angleSign * drawRadius2 * sine; ctx.beginPath(); ctx.moveTo(x6, y5); diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 72af84640..a491c7cb4 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -253,8 +253,8 @@ function pentagonDownShield( textLayoutConstraint: (spaceBounds, textBounds) => ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius2), padding: { - left: 2 + 10 * Math.sin(angleInRadians), - right: 2 + 10 * Math.sin(angleInRadians), + left: 2 + 10 * Math.tan(angleInRadians), + right: 2 + 10 * Math.tan(angleInRadians), top: 3, bottom: 2 + offset / 2, }, @@ -306,10 +306,10 @@ function pentagonUpShield( textLayoutConstraint: (spaceBounds, textBounds) => ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius2), padding: { - left: 2 + 10 * Math.sin(angleInRadians), - right: 2 + 10 * Math.sin(angleInRadians), + left: 2 + ((20 - offset) * Math.tan(angleInRadians)) / 2, + right: 2 + ((20 - offset) * Math.tan(angleInRadians)) / 2, top: 1 + offset / 2, - bottom: 4, + bottom: 3, }, textColor: textColor, }; From c9fab51946f47fab75432a35cada82b1ccdd1697 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Mon, 2 Jan 2023 21:49:08 -0500 Subject: [PATCH 144/514] simply formula --- src/js/shield_canvas_draw.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index 5ad77864a..5513d8aed 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -198,7 +198,8 @@ export function trapezoid( if (rectWidth == null) { var shieldWidth = ShieldText.calculateTextWidth(ref, genericShieldFontSize) + - (2 + ((CS / PXR) * tangent) / 2) * PXR; + 2 * PXR + + (CS * tangent) / 2; var width = Math.max( minGenericShieldWidth, Math.min(maxGenericShieldWidth, shieldWidth) @@ -338,7 +339,8 @@ export function pentagon( if (rectWidth == null) { var shieldWidth = ShieldText.calculateTextWidth(ref, genericShieldFontSize) + - (2 + ((CS / PXR - offset) * tangent) / 2) * PXR; + 2 * PXR + + ((CS - offset * PXR) * tangent) / 2; var width = Math.max( minGenericShieldWidth, Math.min(maxGenericShieldWidth, shieldWidth) From 820a1fe4c206c60cb32a12ea9ff83cef4be94e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 3 Jan 2023 02:35:36 -0800 Subject: [PATCH 145/514] Generate legend on demand (#632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added shield legend * Label networks using Wikidata * Link image instead of definition * Factored out templates, helper functions * Link to Wikidata query * Added place labels to legend * Group shields by country * Sort Other shields to bottom; scroll legend * Refactored symbol legends * Generate legend sections dynamically * Cleaned up legend layout Moved labels to the left of icons. Capped label width. Purge and refresh Wikidata query for network metadata whenever the language changes. * Documented Wikidata network item usage * Added urbanized areas to legend Generalized the symbol layer legending to display a swatch for fill layers. * Added state, country, continent to legend Flattened the label into the label table cell to keep it from overflowing. * Added parks to legend * Added lakes to legend * Added low-zoom lakes to legend * Added waterways to legend * Added ferry lines to legend * Fixed water confusion in legend Ferries are not rivers. * Added airport details to legend * Added buildings to legend * Simplified park layers * Removed debugging * Streamlined feature querying Pushed a layer list into each entry. This requires querying the map separately for each entry but avoids crosstalk. Replaced bespoke property matching with expression-based filters. * Simplified entry matching The entry wasn’t getting cloned anyways. * Added borders to legend * Added disputed borders to legend * Added freeways, toll roads to legend * Added highway exits to legend * Moved international shield category to top * Fixed text halo * Fixed non-capital filter * Added more road classes to legend * Added remaining road classes * Added driveways to legend * Added unpaved roads to legend * Added roads under construction to legend * Added railroads to legend * Moved legend config to separate file * Fixed build errors; sort shield rows Fixed build errors from moving sections into a separate configuration file. Sort shield rows in the same order as in the shield definitions, followed by unrecognized networks. * Added shield variants to legend * Updated documentation * Locale-aware sort countries * Singular descriptions in rail legend entries * Normalize country code case * Sentence-case Wikidata labels * Made colon after country code optional Some countries like Brazil simply use the country code alone for their national route network. * BAB is not a country * More kinds of recreational route networks * Typo in readme * Treat fake OMT network prefixes like real country prefixes * Special-case UK route types * Fetch UK network descriptions from Wikidata * Fixed error when text-field uses string interpolation syntax * Added comments * Italicize labels in legend based on font layout property * Added comments, tests Added inline comments throughout the less straightforward legend methods. Factored out shield image name parsing code and added tests of it. * Link networks to Wikidata * Removed special case for TLA recreational route networks * Removed unused UK label constants * Fixed coalescing of 0 values * Omit fully transparent roads from legend * Fixed typo in comment * Deleted redundant Kosovo shield * Simplified Australian shield assignment --- CONTRIBUTING.md | 11 +- README.md | 4 +- src/americana.js | 33 +- src/index.html | 95 +++++ src/js/legend_config.js | 58 +++ src/js/legend_control.js | 743 ++++++++++++++++++++++++++++++++++++ src/js/shield_defs.js | 61 ++- src/layer/aeroway.js | 19 +- src/layer/boundary.js | 26 +- src/layer/building.js | 9 +- src/layer/construction.js | 9 + src/layer/ferry.js | 7 + src/layer/highway_exit.js | 9 + src/layer/highway_shield.js | 57 ++- src/layer/landuse.js | 7 + src/layer/park.js | 51 +-- src/layer/place.js | 44 +++ src/layer/rail.js | 110 +++++- src/layer/road.js | 99 ++++- src/layer/water.js | 58 +++ test/spec/highway_shield.js | 65 ++++ 21 files changed, 1439 insertions(+), 136 deletions(-) create mode 100644 src/js/legend_config.js create mode 100644 src/js/legend_control.js create mode 100644 test/spec/highway_shield.js 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" /> +

Legend

+
+ + + + + + diff --git a/src/js/legend_config.js b/src/js/legend_config.js new file mode 100644 index 000000000..2b32a7a21 --- /dev/null +++ b/src/js/legend_config.js @@ -0,0 +1,58 @@ +"use strict"; + +import * as PlaceLayers from "../layer/place.js"; +import * as LanduseLayers from "../layer/landuse.js"; +import * as BoundaryLayers from "../layer/boundary.js"; +import * as RoadLayers from "../layer/road.js"; +import * as ConstructionLayers from "../layer/construction.js"; +import * as HighwayExitLayers from "../layer/highway_exit.js"; +import * as RailLayers from "../layer/rail.js"; +import * as AerowayLayers from "../layer/aeroway.js"; +import * as ParkLayers from "../layer/park.js"; +import * as BuildingLayers from "../layer/building.js"; +import * as WaterLayers from "../layer/water.js"; +import * as FerryLayers from "../layer/ferry.js"; + +export const sections = [ + { + name: "Populated places", + entries: PlaceLayers.legendEntries, + }, + { + name: "Borders", + entries: BoundaryLayers.legendEntries, + }, + { + name: "Roads", + entries: [ + ...RoadLayers.legendEntries, + ...ConstructionLayers.legendEntries, + ...HighwayExitLayers.legendEntries, + ], + }, + { + id: "shields", + name: "Route markers", + source: "Wikidata", + }, + { + name: "Railroads", + entries: RailLayers.legendEntries, + }, + { + name: "Aviation", + entries: AerowayLayers.legendEntries, + }, + { + name: "Structures", + entries: BuildingLayers.legendEntries, + }, + { + name: "Land use", + entries: [...LanduseLayers.legendEntries, ...ParkLayers.legendEntries], + }, + { + name: "Water", + entries: [...WaterLayers.legendEntries, ...FerryLayers.legendEntries], + }, +]; diff --git a/src/js/legend_control.js b/src/js/legend_control.js new file mode 100644 index 000000000..dddf60b7a --- /dev/null +++ b/src/js/legend_control.js @@ -0,0 +1,743 @@ +"use strict"; + +import * as ShieldDraw from "./shield_canvas_draw.js"; +import * as Label from "../constants/label.js"; +import * as ShieldDef from "./shield_defs.js"; + +import * as LegendConfig from "./legend_config.js"; +import * as HighwayShieldLayers from "../layer/highway_shield.js"; + +import * as maplibregl from "maplibre-gl"; + +const maxPopupWidth = 30; /* em */ + +export default class LegendControl { + onAdd(map) { + this._map = map; + + this._container = document.createElement("div"); + this._container.className = "maplibregl-ctrl"; + this._container.style.clear = "none"; + + let controlGroup = document.createElement("div"); + controlGroup.className = "maplibregl-ctrl-group"; + this._container.appendChild(controlGroup); + + let button = document.createElement("button"); + button.textContent = "Legend"; + button.style.width = "5em"; + controlGroup.appendChild(button); + + this._popup = new maplibregl.Popup({ + closeOnMove: true, + anchor: "bottom-left", + maxWidth: `${maxPopupWidth}em`, + offset: [0, -4], + }); + button.addEventListener("click", () => { + if (this._popup.isOpen()) { + this.close(); + return; + } + + // A popup is normally anchored on a geographic location, but we just want + // it to point to the button. + let buttonRect = button.getClientRects()[0]; + let anchor = [buttonRect.x, buttonRect.y]; + this.open(anchor); + }); + + return this._container; + } + + onRemove() { + this._container.parentNode.removeChild(this._container); + this._map = undefined; + } + + /** + * Call this method whenever the page's language changes. + */ + onLanguageChange() { + this.close(); + this.purgeNetworkMetadata(); + } + + /** + * Opens the legend popup, positioning it to point at the given anchor point. + * + * @param anchor A screen coordinate for the popup to point to. + */ + open(anchor) { + this.close(); + + let contents = this.getContents(); + let rows = contents.querySelectorAll(".legend-row"); + this._popup.setDOMContent(contents); + + let anchorCoordinate = this._map.unproject(anchor); + this._popup.setLngLat(anchorCoordinate).addTo(this._map); + + this.prettifyNetworkLabels(rows); + + document.getElementById("legend-container").scrollTop = 0; + } + + /** + * Closes the legend popup. + */ + close() { + this._popup.remove(); + } + + /** + * Returns contents of the popup appropriate to the current viewport. + * + * @returns A DOM element representing the full contents of the popup. + */ + getContents() { + let template = document.getElementById("legend").content.cloneNode(true); + + let shieldSection = this.sections.find((s) => s.id === "shields"); + shieldSection.rows = this.getShieldRows(); + shieldSection.sourceURL = `https://query.wikidata.org/embed.html#${encodeURIComponent( + this.getNetworkMetadataQuery() + )}`; + + for (let data of this.sections) { + let section = this.getSection(data); + if (!section) continue; + + let container = template.getElementById("legend-container"); + container.appendChild(section); + + if (data.source) { + let sourceCell = template.querySelector(".legend-source"); + sourceCell.textContent = "Source: "; + + let sourceLink = document.createElement("a"); + sourceLink.href = data.sourceURL; + sourceLink.textContent = data.source; + sourceCell.append(sourceLink); + } + } + + return template; + } + + /** + * Returns the section representing the given data. + */ + getSection(data) { + let template = document + .getElementById("legend-section") + .content.cloneNode(true); + template.querySelector("summary").textContent = data.name; + + let rows = data.rows; + if (!rows && data.entries) { + let entries = data.entries + .map((e) => this.getMatchedEntry(e)) + .filter((m) => m); + rows = entries.map((e) => this.getRowForEntry(e)).filter((r) => r); + } + if (!rows.length) return; + + template.querySelector("tbody").replaceChildren(...rows); + if (!data.source) { + template.querySelector("tfoot").remove(); + } + return template.querySelector(".legend-section"); + } + + /** + * Returns a copy of the given entry populated with a representative visible + * feature. + */ + getMatchedEntry(entry) { + // Query the map for rendered (including transparent) features in the + // current viewport that meet the entry's criteria. + let features = this._map.queryRenderedFeatures({ + layers: entry.layers, + filter: entry.filter, + }); + let mainFeature = features[0]; + if (!mainFeature) return; + + // Copy the entry, adding the first match, which is from the topmost layer. + let matchedEntry = { feature: mainFeature }; + Object.assign(matchedEntry, entry); + + if ( + mainFeature.layer.type === "fill" || + mainFeature.layer.type === "fill-extrusion" + ) { + // Pair a fill (extrusion) layer's polygon with some polygon rendered as + // an outline. + matchedEntry.fill = mainFeature; + matchedEntry.stroke = features.find( + (f) => f.id === mainFeature.id && f.layer.type === "line" + ); + } else if (mainFeature.layer.type === "line") { + // Pair a line layer's linestring with some polygon rendered as a fill. + matchedEntry.fill = features.find( + (f) => + f.id === mainFeature.id && + (f.layer.type === "fill" || f.layer.type === "fill-extrusion") + ); + if (matchedEntry.fill) { + matchedEntry.stroke = mainFeature; + } else { + // Collect the other linestrings needed to render the entry (casing + // etc.). + matchedEntry.lines = []; + let layers = new Set(); + for (let feature of features) { + // Ignore other features that happen to match the criteria so that the + // entry depicts only a single feature. + if (feature.id !== mainFeature.id || feature.layer.type !== "line") + continue; + // If we've already seen the layer, then this feature represents + // another slice of the feature in another tile. + if (layers.has(feature.layer.id)) continue; + layers.add(feature.layer.id); + // Populate the array of lines in reverse order, because SVG renders + // elements according to the painter's algorithm. + matchedEntry.lines.unshift(feature); + } + } + } + + return matchedEntry; + } + + /** + * Returns a table row illustrating the given entry. + */ + getRowForEntry(entry) { + // Choose an HTML template that will display as much information about the + // entry as possible. + let templateID = "legend-row-symbol"; + if (entry.lines) { + templateID = "legend-row-line"; + } else if (entry.fill) { + templateID = "legend-row-swatch"; + } + let template = document.getElementById(templateID).content.cloneNode(true); + let row = template.querySelector("tr"); + + // Populate the template's swatch etc. with an illustration of the matching + // feature. + if (entry.fill) { + let swatchCell = row.querySelector(".swatch"); + Object.assign( + swatchCell.style, + this.getSwatchStyle(entry.fill, entry.stroke) + ); + } else if (entry.lines) { + let lineCell = row.querySelector(".line"); + this.populateLineCell(lineCell, entry.lines); + } else if (entry.feature) { + let labelCell = row.querySelector(".label"); + this.populateTextLabelFromSymbol(labelCell, entry.feature); + + let iconCell = row.querySelector(".icon"); + let img = this.getIconImageFromSymbol(entry.feature); + if (img) { + iconCell.appendChild(img); + } else { + iconCell.remove(); + labelCell.setAttribute("colspan", 2); + } + } else { + return; + } + + let descriptionCell = row.querySelector(".description"); + descriptionCell.textContent = entry.description; + + return row; + } + + /** + * Returns an HTML image element that resembles the icon of the given feature + * from a symbol layer. + */ + getIconImageFromSymbol(symbol) { + let imageName = symbol.layer.layout["icon-image"]?.name; + if (!imageName) return; + + let iconSize = symbol.layer.layout["icon-size"]; + let styleImage = this._map.style.getImage(imageName); + let img = this.getImageFromStyle(styleImage, iconSize); + return img; + } + + /** + * Populates an HTML block element to resemble the text of the given feature + * from a symbol layer. + */ + populateTextLabelFromSymbol(container, symbol) { + let textField = symbol.layer.layout["text-field"]; + if (!textField) return; + + // Assume a formatted text field has only one text section. If this isn't + // formatted text, fall back to string interpolation syntax. + container.textContent = + textField.sections?.[0].text ?? + textField.replace( + /\{(\w+)\}/g, + (match, prop) => symbol.properties[prop] ?? match + ); + + // The fontstack name obscures the original font names. Look for words that + // conventionally indicate a weight or style. + let fontWeight = symbol.layer.layout["text-font"]?.[0]?.match(/\bBold\b/) + ? "bold" + : "normal"; + let fontStyle = symbol.layer.layout["text-font"]?.[0]?.match(/\bItalic\b/) + ? "italic" + : "normal"; + + // Force labels to be right-aligned if paired with an icon, which will be in + // the column to the right. + let justification = symbol.layer.layout["text-justify"] || "center"; + if (symbol.layer.layout["icon-image"]) { + justification = "right"; + } + + // Simulate a text outline by compositing shadows in four directions. + // -webkit-text-stroke won't work because it eats into the text fill. + let shadowOffset = symbol.layer.paint["text-halo-width"] ?? 0; + let haloColor = symbol.layer.paint["text-halo-color"] || "black"; + let haloBlur = symbol.layer.paint["text-halo-blur"] ?? 0; + let textShadows = [-shadowOffset, shadowOffset].flatMap((x) => + [-shadowOffset, shadowOffset].map( + (y) => `${haloColor} ${x}px ${y}px ${haloBlur}px` + ) + ); + + Object.assign(container.style, { + color: symbol.layer.paint["text-color"], + fontWeight, + fontStyle, + fontSize: `${symbol.layer.layout["text-size"] ?? 16}px`, + letterSpacing: `${symbol.layer.layout["text-letter-spacing"]}em`, + lineHeight: `${symbol.layer.layout["text-line-height"] ?? 1.2}em`, + maxWidth: "10vw", // prevent label column from taking over popup + textAlign: justification === "auto" ? "right" : justification, + textShadow: textShadows.join(", "), + textTransform: symbol.layer.layout["text-transform"], + verticalAlign: "middle", + width: `${symbol.layer.layout["text-max-width"] ?? 10}em`, + }); + } + + /** + * Returns style properties resembling the given fill and line. + */ + getSwatchStyle(fill, stroke) { + let fillColor = + fill?.layer.paint["fill-color"] || + fill?.layer.paint["fill-extrusion-color"]; + if (fillColor) { + let opacity = + fill?.layer.paint["fill-opacity"] ?? + fill?.layer.paint["fill-extrusion-opacity"] ?? + fillColor.a ?? + 1; + fillColor = `rgba(${fillColor.r * 255}, ${fillColor.g * 255}, ${ + fillColor.b * 255 + }, ${opacity})`; + } + let borderStyle = "solid"; + if (stroke?.layer.paint["line-dasharray"]) { + // Just give an idea of the outline being dashed. + borderStyle = "dashed"; + } else if (fill?.layer.paint["fill-extrusion-height"]) { + // Assume the only fill extrusion layers are for buildings and that the + // height is fixed to a small value. + borderStyle = "outset"; + } + return { + backgroundColor: fillColor, + borderColor: + stroke?.layer.paint["line-color"] || fillColor || "transparent", + borderStyle: borderStyle, + borderWidth: `${stroke?.layer.paint["line-width"] ?? 1}px`, + }; + } + + /** + * Populates the given table cell with SVG elements depicting a line. + */ + populateLineCell(cell, lineFeatures) { + let getLineWidth = (f) => { + let width = f.layer.paint["line-width"] ?? 1; + let gapWidth = f.layer.paint["line-gap-width"]; + // Round the stroke width up to one point to ensure legibility. + return Math.max( + 1 / ShieldDraw.PXR, + gapWidth ? width * 2 + gapWidth : width + ); + }; + let lineWidths = lineFeatures.map(getLineWidth); + let height = Math.max(...lineWidths); + + let svg = cell.querySelector("svg"); + svg.style.height = `${Math.ceil(height)}px`; + + for (let feature of lineFeatures) { + let line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("y1", `${height / 2}px`); + line.setAttribute("y2", `${height / 2}px`); + line.setAttribute("x2", "100%"); + + // line-dasharray is measured in multiples of line-width, whereas + // stroke-dasharray is measured in pixels. + let simpleLineWidth = feature.layer.paint["line-width"] ?? 1; + let dashArray = feature.layer.paint["line-dasharray"]?.from.map( + (d) => d * simpleLineWidth + ); + + Object.assign(line.style, { + opacity: feature.layer.paint["line-opacity"] ?? 1, + stroke: feature.layer.paint["line-color"] || fillColor, + strokeDasharray: dashArray?.join(" "), + strokeWidth: getLineWidth(feature), + }); + + svg.appendChild(line); + } + } + + /** + * Returns table rows illustrating route shields. + */ + getShieldRows() { + // Query the map for rendered shield symbols in the current viewport. + let shieldFeatures = this._map.queryRenderedFeatures({ + layers: [HighwayShieldLayers.shield.id], + }); + + // Extract all the image sections embedded in the symbols and map them to + // image metadata (image names and parsed networks and route numbers). + let images = shieldFeatures + .flatMap((f) => f.layer.layout["text-field"].sections) + .filter((s) => s.image && s.image) + .map((s) => HighwayShieldLayers.parseImageName(s.image.name)); + + // Unique the images by network. + let imagesByNetwork = {}; + let unrecognizedNetworks = new Set(); + for (let image of images) { + if (!(image.network in imagesByNetwork)) { + imagesByNetwork[image.network] = { overridesByRef: {} }; + } + let networkImages = imagesByNetwork[image.network]; + + let shieldDef = ShieldDef.shields[image.network]; + if (image.ref && shieldDef?.overrideByRef?.[image.ref]) { + // Store a different image for each override in the shield definition. + if (!networkImages.overridesByRef[image.ref]) { + networkImages.overridesByRef[image.ref] = image.imageName; + } + } else if (!networkImages.ref && image.ref) { + // Store the numbered variant of a shield if required by the shield + // definition. + networkImages.ref = image.imageName; + } else if (!networkImages.noRef && !image.ref) { + // Store the unnumbered variant of a shield if required by the shield + // definition. + networkImages.noRef = image.imageName; + } + + if (!shieldDef) { + // Keep all unrecognized networks separate so we don't miss them when + // sorting the networks by the order in the shield definitions. + unrecognizedNetworks.add(image.network); + } + } + + // For each country, populate an array with shield metadata in the same + // order as in the shield definitions, appending all the unrecognized + // networks sorted in alphabetical order. + let networks = [ + ...Object.keys(ShieldDef.shields), + ...[...unrecognizedNetworks.values()].sort(), + ]; + let countries = new Set(); + let shieldRowsByCountry = {}; + let otherShieldRows = []; + for (let network of networks) { + // Skip shield definitions for which no shield is currently visible. + if (!(network in imagesByNetwork)) continue; + + // Get all the relevant images, sorted from generic to specialized. + let images = imagesByNetwork[network]; + let sortedImages = [ + images.noRef, + images.ref, + ...Object.values(images.overridesByRef), + ].filter((i) => i); + + let row = this.getShieldRow(network, sortedImages); + if (!row) continue; + + // Extract an ISO 3166-1 alpha-2 country code from the network. + // OpenMapTiles synthesizes fake networks in some countries. + let country = network + .match(/^(?:omt-)?(\w\w)(?:[-:]|$)/)?.[1] + ?.toUpperCase(); + if (country) { + if (!(country in shieldRowsByCountry)) { + shieldRowsByCountry[country] = []; + } + shieldRowsByCountry[country].push(row); + countries.add(country); + } else { + otherShieldRows.push(row); + } + } + + // Map country codes to localized names and sort the lists of networks by + // those names. + let locales = Label.getLocales(); + let countryNames = new Intl.DisplayNames(locales, { + type: "region", + }); + let sortedCountries = [...countries] + .map((code) => { + let name = countryNames.of(code) ?? code; + return { code, name }; + }) + .sort((a, b) => a.name.localeCompare(b.name, locales[0])); + + // List any network without a country code first as an international + // network. + if (otherShieldRows.length) { + sortedCountries.unshift({ code: "*", name: "International" }); + shieldRowsByCountry["*"] = otherShieldRows; + } + + let shieldRows = []; + for (let country of sortedCountries) { + if (sortedCountries.length > 1) { + let template = document + .getElementById("legend-rowgroup") + .content.cloneNode(true); + let groupRow = template.querySelector("tr"); + groupRow.querySelector("th").textContent = country.name; + shieldRows.push(groupRow); + } + + shieldRows.push(...shieldRowsByCountry[country.code]); + } + return shieldRows; + } + + /** + * Returns a table row representing a route shield. + * + * @param network The `network=*` value associated with the style images. + * @param names An array of style image names. + * @returns An HTML table row representing the route shield, or nothing if the style does not render the given network. + */ + getShieldRow(network, names) { + let images = names + .map((n) => this._map.style.getImage(n)) + .map((i) => this.getImageFromStyle(i)) + .filter((i) => i); + if (!images.length) return; + + let template = document + .getElementById("legend-row-symbol") + .content.cloneNode(true); + let row = template.querySelector("tr"); + row.dataset.network = network; + + row.querySelector(".icon").replaceChildren(...images); + + let descriptionCell = row.querySelector(".description"); + let code = document.createElement("code"); + code.textContent = network; + descriptionCell.appendChild(code); + + return row; + } + + /** + * Returns an HTML image element displaying the given style image. + * + * @param styleImage The style image to display. + * @param iconSize The size factor to apply to the width and height of the image. + * @returns An HTML image element, or nothing if the style image is merely a spacer image. + */ + getImageFromStyle(styleImage, iconSize = 1) { + let userImage = styleImage.userImage; + // Skip spacer images representing unsupported networks. + if (userImage?.width === 1 || userImage?.height === 1) return; + + // Wrap the style image's raw data as an image data buffer. Images generated + // at runtime are stored in a different property than images hard-coded in + // the spritesheet. + let imageData = new ImageData( + userImage?.data || new Uint8ClampedArray(styleImage.data.data), + userImage?.width || styleImage.data.width, + userImage?.height || styleImage.data.height + ); + + // Draw the image onto a canvas of the same size. + let canvas = document.createElement("canvas"); + canvas.width = imageData.width; + canvas.height = imageData.height; + let ctx = canvas.getContext("2d"); + ctx.putImageData(imageData, 0, 0); + + // Embed the canvas in an HTML image of the same size. + let img = new Image( + (imageData.width * iconSize) / ShieldDraw.PXR, + (imageData.height * iconSize) / ShieldDraw.PXR + ); + img.src = canvas.toDataURL("image/png"); + img.className = "shield"; + + return img; + } + + /** + * Inserts human-readable descriptions in each of the given table rows. + * + * @param rows An array of table rows containing placeholders for descriptions. + */ + async prettifyNetworkLabels(rows) { + let networkMetadata = await this.getNetworkMetadata(); + if (!networkMetadata) return; + + // If any synthesized British networks are visible, also query Wikidata for + // descriptions of those networks. + for (let row of rows) { + let network = row.dataset.network; + if (network?.startsWith("omt-gb-")) { + let ukNetworkMetadata = await this.getUKNetworkMetadata(); + Object.assign(networkMetadata, ukNetworkMetadata); + break; + } + } + + let locales = Label.getLocales(); + let languageNames = new Intl.DisplayNames(locales, { + type: "language", + }); + + // Wikidata labels are normally lowercased so that they can appear in any + // context. Convert them to sentence case for consistency with the rest of + // the legend. + let toSentenceCase = (lowerCase, locale) => + lowerCase[0].toLocaleUpperCase(locale) + lowerCase.substring(1); + for (let row of rows) { + let network = row.dataset.network; + let binding = networkMetadata[network]; + if (!binding) continue; + + let descriptionCell = row.querySelector(".description"); + + let link = document.createElement("a"); + link.href = binding.network.value; + link.target = "_blank"; + let locale = binding.networkLabel["xml:lang"]; + link.textContent = toSentenceCase(binding.networkLabel.value, locale); + if (locale) { + link.setAttribute("lang", locale); + descriptionCell.replaceChildren(link); + + if (locale.match(/^\w+/)?.[0] !== locales[0].match(/^\w+/)?.[0]) { + let languageTag = document.createElement("span"); + languageTag.className = "language"; + languageTag.textContent = languageNames.of(locale); + descriptionCell.appendChild(document.createTextNode(" ")); + descriptionCell.appendChild(languageTag); + } + } else { + descriptionCell.querySelector("code").replaceChildren(link); + } + } + } + + /** + * Returns a mapping from `network=*` values to metadata about these values from Wikidata. + */ + async getNetworkMetadata() { + if (this._networkMetadata) { + return this._networkMetadata; + } + + let url = `https://query.wikidata.org/sparql?query=${encodeURIComponent( + this.getNetworkMetadataQuery() + )}&format=json`; + const response = await fetch(url); + const json = await response.json(); + this._networkMetadata = Object.fromEntries( + json.results.bindings.map((binding) => { + return [binding.value.value, binding]; + }) + ); + return this._networkMetadata; + } + + /** + * Returns a mapping from OpenMapTiles synthesized `network=*` values to + * metadata about these values from Wikidata. + */ + async getUKNetworkMetadata() { + if (this._ukNetworkMetadata) { + return this._ukNetworkMetadata; + } + + let url = `https://query.wikidata.org/sparql?query=${encodeURIComponent( + this.getNetworkMetadataQuery("GB") + )}&format=json`; + const response = await fetch(url); + const json = await response.json(); + this._ukNetworkMetadata = Object.fromEntries( + json.results.bindings.map((binding) => { + return [binding.value.value, binding]; + }) + ); + return this._ukNetworkMetadata; + } + + purgeNetworkMetadata() { + delete this._networkMetadata; + delete this._ukNetworkMetadata; + } + + /** + * Returns the Wikidata Query Service SPARQL query for network metadata. + * + * @param region ISO 3166-1 alpha-2 country code. + */ + getNetworkMetadataQuery(region) { + let locales = Label.getLocales().join(","); + let triple, + filter = "", + bind = ""; + if (region === "GB") { + triple = + "?network wdt:P361 wd:Q115856945; p:P528 [ ps:P528 ?value; pq:P972 wd:Q110613756 ]"; + } else { + triple = "?network wdt:P1282 ?tag"; + filter = `FILTER(REGEX(?tag, "^Tag:network="))`; + bind = "BIND(SUBSTR(?tag, 13) AS ?value)"; + } + return ` +SELECT ?value ?network ?networkLabel WHERE { + ${triple}. + ${filter} + ${bind} + SERVICE wikibase:label { bd:serviceParam wikibase:language "${locales},en". } +} +ORDER BY ?value +`; + } +} diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 66c919662..bccf5d8dc 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -3132,7 +3132,7 @@ export function loadShields(shieldImages) { Color.shields.blue ); - // Myanmar + // Malaysia shields["MY:E"] = shields["my:federal"] = hexagonVerticalShield( 3, Color.shields.yellow, @@ -3627,46 +3627,39 @@ export function loadShields(shieldImages) { Color.shields.white ); - // Kosovo - shields["XK:motorway"] = hexagonVerticalShield( - 3, - Color.shields.green, - Color.shields.white, - Color.shields.white, - 0, - 34 - ); - // OCEANIA // Australia ["ACT", "NSW", "NT", "QLD", "SA", "TAS", "VIC", "WA"].forEach( - (state_or_territory) => - ([ - shields[`AU:${state_or_territory}`], - shields[`AU:${state_or_territory}:NH`], - shields[`AU:${state_or_territory}:NR`], - shields[`AU:${state_or_territory}:S`], - shields[`AU:${state_or_territory}:T`], - shields[`AU:${state_or_territory}:ALT`], - shields[`AU:${state_or_territory}:ALT_NR`], - shields[`AU:${state_or_territory}:ALT_S`], - ] = [ + (state_or_territory) => { + shields[`AU:${state_or_territory}`] = roundedRectShield( + Color.shields.green, + Color.shields.yellow + ); + shields[`AU:${state_or_territory}:NH`] = homePlateShield( + 5, + Color.shields.green, + Color.shields.yellow + ); + shields[`AU:${state_or_territory}:NR`] = homePlateShield( + 5, + Color.shields.white, + Color.shields.black + ); + shields[`AU:${state_or_territory}:S`] = fishheadShieldBlue; + shields[`AU:${state_or_territory}:T`] = pentagonShieldBrown; + shields[`AU:${state_or_territory}:ALT`] = banneredShield( roundedRectShield(Color.shields.green, Color.shields.yellow), - homePlateShield(5, Color.shields.green, Color.shields.yellow), + ["ALT"] + ); + shields[`AU:${state_or_territory}:ALT_NR`] = banneredShield( homePlateShield(5, Color.shields.white, Color.shields.black), - fishheadShieldBlue, - pentagonShieldBrown, - banneredShield( - roundedRectShield(Color.shields.green, Color.shields.yellow), - ["ALT"] - ), - banneredShield( - homePlateShield(5, Color.shields.white, Color.shields.black), - ["ALT"] - ), + ["ALT"] + ); + shields[`AU:${state_or_territory}:ALT_S`] = [ banneredShield(fishheadShieldBlue, ["ALT"]), - ]) + ]; + } ); shields["AU:QLD:MR"] = hexagonVerticalShield( diff --git a/src/layer/aeroway.js b/src/layer/aeroway.js index b190e78be..9af7a5859 100644 --- a/src/layer/aeroway.js +++ b/src/layer/aeroway.js @@ -259,11 +259,26 @@ export const airportGate = { }, layout: { visibility: "visible", - "text-field": "{ref}", + "text-field": ["get", "ref"], "text-font": ["OpenHistorical Bold"], "text-size": 10, }, source: "openmaptiles", - metadata: {}, "source-layer": "aeroway", }; + +export const legendEntries = [ + { + description: "Civilian airport", + layers: [airportRefLabel.id, airportLabel.id], + filter: ["!=", ["get", "class"], "military"], + }, + { + description: "Military air base", + layers: [airportRefLabel.id, airportLabel.id], + filter: ["==", ["get", "class"], "military"], + }, + { description: "Runway", layers: [runway.id] }, + { description: "Taxiway", layers: [taxiway.id] }, + { description: "Gate", layers: [airportGate.id] }, +]; diff --git a/src/layer/boundary.js b/src/layer/boundary.js index b9dd9b0bf..74b18d254 100644 --- a/src/layer/boundary.js +++ b/src/layer/boundary.js @@ -31,7 +31,6 @@ export const countyCasing = { type: "line", paint: { "line-color": Color.borderCasing, - "line-dasharray": [1], "line-width": { stops: [ [11, 5], @@ -89,7 +88,6 @@ export const stateCasing = { [7, `hsl(${Color.hueBorderCasing}, 30%, 90%)`], ], }, - "line-dasharray": [1], "line-width": { base: 1.2, stops: [ @@ -243,3 +241,27 @@ export const country = { source: "openmaptiles", "source-layer": "boundary", }; + +export const legendEntries = [ + { + description: "Country or dependency", + layers: [country.id, countryCasing.id], + }, + { + description: "State or province", + layers: [state.id, stateCasing.id], + }, + { + description: "County or county-equivalent", + layers: [county.id, countyCasing.id], + }, + { + description: "City, town, or village", + layers: [city.id], + }, + { + description: "Disputed border", + layers: [countryCasing.id, stateCasing.id, countyCasing.id], + filter: ["==", ["get", "disputed"], 1], + }, +]; diff --git a/src/layer/building.js b/src/layer/building.js index ac772887b..e395baf07 100644 --- a/src/layer/building.js +++ b/src/layer/building.js @@ -16,11 +16,16 @@ export const building = { "fill-extrusion-height": 3, "fill-extrusion-opacity": 0.85, }, - // filter: ["all", ["!=", "intermittent", 1], ["!=", "brunnel", "tunnel"]], layout: { visibility: "visible", }, source: "openmaptiles", - metadata: {}, "source-layer": "building", }; + +export const legendEntries = [ + { + description: "Building", + layers: [building.id], + }, +]; diff --git a/src/layer/construction.js b/src/layer/construction.js index 2aad62338..7f653ba0a 100644 --- a/src/layer/construction.js +++ b/src/layer/construction.js @@ -1,3 +1,5 @@ +"use strict"; + const majorConstruction = [ "match", ["get", "class"], @@ -54,3 +56,10 @@ export const road = { ], }, }; + +export const legendEntries = [ + { + description: "Road under construction", + layers: [road.id], + }, +]; diff --git a/src/layer/ferry.js b/src/layer/ferry.js index 57f123c4f..6aa8a6ca1 100644 --- a/src/layer/ferry.js +++ b/src/layer/ferry.js @@ -24,3 +24,10 @@ export const ferry = { source: "openmaptiles", "source-layer": "transportation", }; + +export const legendEntries = [ + { + description: "Ferry line", + layers: [ferry.id], + }, +]; diff --git a/src/layer/highway_exit.js b/src/layer/highway_exit.js index 8355c7ab7..6f995becc 100644 --- a/src/layer/highway_exit.js +++ b/src/layer/highway_exit.js @@ -1,3 +1,5 @@ +"use strict"; + export const exits = { id: "highway_exit", type: "symbol", @@ -21,3 +23,10 @@ export const exits = { "text-halo-width": 0.75, }, }; + +export const legendEntries = [ + { + description: "Freeway or expressway exit", + layers: [exits.id], + }, +]; diff --git a/src/layer/highway_shield.js b/src/layer/highway_shield.js index 362a666cd..968e024e3 100644 --- a/src/layer/highway_shield.js +++ b/src/layer/highway_shield.js @@ -1,33 +1,48 @@ "use strict"; -function routeConcurrency(num) { +export const namedRouteNetworks = [ + "US:KY:Parkway", + "US:NY:Parkway", + "US:TX:Fort_Bend:FBCTRA", + "US:TX:Harris:HCTRA", +]; + +export function getImageNameExpression(routeIndex) { return [ - "case", - ["!=", ["get", "route_" + num], null], + "concat", + "shield\n", + ["get", "route_" + routeIndex], [ - "image", - [ - "concat", - "shield\n", - ["get", "route_" + num], - [ - "match", - ["get", "route_" + num], - [ - "US:KY:Parkway=", - "US:NY:Parkway=", - "US:TX:Fort_Bend:FBCTRA=", - "US:TX:Harris:HCTRA=", - ], - ["concat", "\n", ["get", "name"]], - "", - ], - ], + "match", + ["get", "route_" + routeIndex], + namedRouteNetworks.map((n) => n + "="), + ["concat", "\n", ["get", "name"]], + "", ], + ]; +} + +function routeConcurrency(routeIndex) { + return [ + "case", + ["!=", ["get", "route_" + routeIndex], null], + ["image", getImageNameExpression(routeIndex)], ["literal", ""], ]; } +/** + * Returns a structured representation of the given image name. + * + * @param name An image name in the format returned by `routeConcurrency`. + */ +export function parseImageName(imageName) { + let lines = imageName.split("\n"); + let [, network, ref] = lines[1].match(/^(.*?)=(.*)/) || []; + let name = lines[2]; + return { imageName, network, ref, name }; +} + let shieldTextField = ["format"]; for (var i = 1; i <= 6; i++) { shieldTextField.push(routeConcurrency(i)); diff --git a/src/layer/landuse.js b/src/layer/landuse.js index 7536dbc99..added57e8 100644 --- a/src/layer/landuse.js +++ b/src/layer/landuse.js @@ -26,3 +26,10 @@ export const urbanizedArea = { source: "openmaptiles", "source-layer": "landuse", }; + +export const legendEntries = [ + { + description: "Urban area", + layers: [urbanizedArea.id], + }, +]; diff --git a/src/layer/park.js b/src/layer/park.js index aaf34cf1f..2c151e342 100644 --- a/src/layer/park.js +++ b/src/layer/park.js @@ -9,11 +9,7 @@ export const fill = { paint: { "fill-color": Color.parkFill, }, - layout: { - visibility: "visible", - }, source: "openmaptiles", - metadata: {}, "source-layer": "park", }; @@ -23,9 +19,6 @@ export const outline = { paint: { "line-color": Color.parkOutline, }, - layout: { - visibility: "visible", - }, source: "openmaptiles", metadata: {}, "source-layer": "park", @@ -42,7 +35,6 @@ export const label = { "text-halo-width": 1, }, layout: { - visibility: "visible", "text-field": Label.localizedName, "text-font": ["OpenHistorical Bold"], "text-size": 10, @@ -53,52 +45,29 @@ export const label = { }; export const parkFill = { + ...fill, id: "park-fill", - type: "fill", filter: ["==", ["get", "subclass"], "park"], - paint: { - "fill-color": Color.parkFill, - }, - layout: { - visibility: "visible", - }, - source: "openmaptiles", - metadata: {}, "source-layer": "landcover", }; export const parkOutline = { + ...outline, id: "park-outline", - type: "line", filter: ["==", ["get", "subclass"], "park"], - paint: { - "line-color": Color.parkOutline, - }, - layout: { - visibility: "visible", - }, - source: "openmaptiles", - metadata: {}, "source-layer": "landcover", }; export const parkLabel = { + ...label, id: "park-label", - type: "symbol", filter: ["==", ["get", "class"], "park"], - paint: { - "text-color": Color.parkLabel, - "text-halo-blur": 1, - "text-halo-color": Color.parkLabelHalo, - "text-halo-width": 1, - }, - layout: { - visibility: "visible", - "text-field": Label.localizedName, - "text-font": ["OpenHistorical Bold"], - "text-size": 10, - "symbol-sort-key": ["get", "rank"], - }, - source: "openmaptiles", "source-layer": "poi", }; + +export const legendEntries = [ + { + description: "Park", + layers: [fill.id, outline.id, parkFill.id, parkOutline.id], + }, +]; diff --git a/src/layer/place.js b/src/layer/place.js index 07519476b..45cd8cd42 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -424,3 +424,47 @@ export const continent = { maxzoom: 1, "source-layer": "place", }; + +const populatedPlaceLayers = [village.id, town.id, city.id]; +const nonCapitalFilter = ["!", ["has", "capital"]]; + +export const legendEntries = [ + { + description: "Continent", + layers: [continent.id], + }, + { + description: "Country or dependency", + layers: [countryOther.id, country3.id, country2.id, country1.id], + }, + { + description: "State or province", + layers: [state.id], + }, + { + description: "Large city", + layers: [city.id], + filter: nonCapitalFilter, + }, + { description: "Town", layers: [town.id], filter: nonCapitalFilter }, + { + description: "Small village", + layers: [village.id], + filter: nonCapitalFilter, + }, + { + description: "National capital", + layers: populatedPlaceLayers, + filter: ["==", ["get", "capital"], 2], + }, + { + description: "Regional capital", + layers: populatedPlaceLayers, + filter: ["==", ["get", "capital"], 3], + }, + { + description: "State or provincial capital", + layers: populatedPlaceLayers, + filter: ["==", ["get", "capital"], 4], + }, +]; diff --git a/src/layer/rail.js b/src/layer/rail.js index 1f4fd3827..a667462e8 100644 --- a/src/layer/rail.js +++ b/src/layer/rail.js @@ -59,6 +59,7 @@ var defRail = { var serviceSelector = ["match", ["get", "service"], ["siding", "spur", "yard"]]; var isService = [...serviceSelector, true, false]; var isNotService = [...serviceSelector, false, true]; +let isNotCrossover = ["!=", ["get", "service"], "crossover"]; var lineColor = [ "match", @@ -200,7 +201,7 @@ class Railway { if (this.constraints != null) { layer.filter.push(this.constraints); } - layer.filter.push(["!=", ["get", "service"], "crossover"]); + layer.filter.push(isNotCrossover); return layer; }; } @@ -422,6 +423,35 @@ class LightRailTramServiceTunnel extends LightRailTramService { } } +export function getLayerSeparatedBridgeLayers(bridgeLayers) { + let layers = []; + + //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" + ) + ) + ); + + return layers; +} + export const railway = new Railway(); export const railwayBridge = new RailwayBridge(); export const railwayTunnel = new RailwayTunnel(); @@ -453,3 +483,81 @@ export const lightRailTramServiceTunnel = new LightRailTramServiceTunnel(); export const funicular = new Funicular(); export const funicularBridge = new FunicularBridge(); export const funicularTunnel = new FunicularTunnel(); + +const isGenericRail = ["==", ["get", "subclass"], "rail"]; +const isStandardGauge = ["!=", ["get", "subclass"], "narrow_gauge"]; +const isNarrowGauge = ["==", ["get", "subclass"], "narrow_gauge"]; +const isSubway = ["==", ["get", "subclass"], "subway"]; +const isLightRail = ["==", ["get", "subclass"], "light_rail"]; +const isTram = ["==", ["get", "subclass"], "tram"]; + +export const legendEntries = [ + { + description: "Mainline track", + layers: [rail.dashes().id, railway.fill().id], + filter: ["all", isGenericRail, isNotService, isNotCrossover], + }, + { + description: "Siding, spur, or yard track", + layers: [railService.dashes().id, railway.fill().id], + filter: ["all", isGenericRail, isService], + }, + { + description: "Narrow-gauge mainline track", + layers: [narrowGauge.dashes().id, railway.fill().id], + filter: ["all", isNarrowGauge, isGenericRail, isNotService, isNotCrossover], + }, + { + description: "Narrow-gauge siding, spur, or yard track", + layers: [narrowGaugeService.dashes().id, railway.fill().id], + filter: ["all", isNarrowGauge, isGenericRail, isService], + }, + { + description: "Subway line", + layers: [railwayTunnel.fill().id, railway.fill().id], + filter: ["all", isSubway, isNotService, isNotCrossover], + }, + { + description: "Subway siding or yard track", + layers: [railwayTunnel.fill().id, railway.fill().id], + filter: ["all", isSubway, isService], + }, + { + description: "Monorail line", + layers: [ + ...getLayerSeparatedBridgeLayers([railwayBridge.fill()]).map((l) => l.id), + railway.fill().id, + ], + filter: [ + "all", + ["==", ["get", "subclass"], "monorail"], + isNotService, + isNotCrossover, + ], + }, + { + description: "Light rail line", + layers: [lightRailTram.dashes().id, railway.fill().id], + filter: ["all", isLightRail, isNotService, isNotCrossover], + }, + { + description: "Light rail siding or yard track", + layers: [lightRailTramService.dashes().id, railway.fill().id], + filter: ["all", isLightRail, isService], + }, + { + description: "Streetcar line", + layers: [lightRailTram.dashes().id, railway.fill().id], + filter: ["all", isTram, isNotService, isNotCrossover], + }, + { + description: "Streetcar siding or yard track", + layers: [lightRailTramService.dashes().id, railway.fill().id], + filter: ["all", isTram, isService], + }, + { + description: "Funicular or inclined elevator", + layers: [funicular.dashes().id, railway.fill().id], + filter: ["==", ["get", "subclass"], "funicular"], + }, +]; diff --git a/src/layer/road.js b/src/layer/road.js index 0c2469af5..c71a5c1c0 100644 --- a/src/layer/road.js +++ b/src/layer/road.js @@ -51,6 +51,7 @@ const smallServiceSelector = [ ["get", "service"], ["parking_aisle", "driveway"], ]; +const isUnpaved = ["==", ["get", "surface"], "unpaved"]; function combineConstraints(constraint1, constraint2) { if (constraint1 == null) { @@ -472,11 +473,7 @@ class Road { Math.max(this.maxZoomCasing, this.maxZoomFill), this.constraints ); - layer.filter = combineConstraints(layer.filter, [ - "==", - ["get", "surface"], - "unpaved", - ]); + layer.filter = combineConstraints(layer.filter, isUnpaved); layer.layout = { "line-cap": "butt", "line-join": "round", @@ -1198,3 +1195,95 @@ export const secondaryLinkBridge = new SecondaryLinkBridge(); export const secondaryLinkTollBridge = new SecondaryLinkTollBridge(); export const tertiaryLinkBridge = new TertiaryLinkBridge(); export const tertiaryLinkTollBridge = new TertiaryLinkTollBridge(); + +const normalRoadLayers = [ + motorway.fill().id, + motorway.casing().id, + trunk.casing().id, + primaryToll.fill().id, + secondaryToll.fill().id, + tertiaryToll.fill().id, + minorToll.fill().id, + roadSimpleCasing.casing().id, +]; + +export const legendEntries = [ + { + description: "Freeway (controlled access, divided)", + layers: [motorway.fill().id, motorway.casing().id], + filter: ["all", isNotToll, [">", opacity, 0]], + }, + { + description: "Expressway (limited access, divided)", + layers: [ + roadSimpleFill.fill().id, + roadSimpleCasing.casing().id, + primaryExpressway.casing().id, + primaryExpressway.casing().id, + secondaryExpressway.casing().id, + tertiaryExpressway.casing().id, + ], + filter: ["all", isExpressway, isNotToll], + }, + { + description: "Principal highway", + layers: [trunk.casing().id], + filter: isNotToll, + }, + { + description: "Major arterial road", + layers: [primary.fill().id, roadSimpleCasing.casing().id], + filter: ["==", getClass, "primary"], + }, + { + description: "Minor arterial road", + layers: [secondary.fill().id, roadSimpleCasing.casing().id], + filter: ["==", getClass, "secondary"], + }, + { + description: "Collector road", + layers: [tertiary.fill().id, roadSimpleCasing.casing().id], + filter: ["==", getClass, "tertiary"], + }, + { + description: "Local road", + layers: [minor.fill().id, roadSimpleCasing.casing().id], + filter: ["match", getClass, ["minor", "service"], true, false], + }, + { + description: "Driveway or parking aisle", + layers: [minor.fill().id, roadSimpleCasing.casing().id], + filter: [ + "all", + ["==", getClass, "service"], + [...smallServiceSelector, true, false], + ], + }, + { + description: "Toll road", + layers: [ + motorway.fill().id, + motorway.casing().id, + trunk.casing().id, + primaryToll.fill().id, + secondaryToll.fill().id, + tertiaryToll.fill().id, + minorToll.fill().id, + roadSimpleCasing.casing().id, + ], + filter: isToll, + }, + { + description: "Unpaved road", + layers: [ + road.surface().id, + trunk.casing().id, + primary.fill().id, + secondary.fill().id, + tertiary.fill().id, + minor.fill().id, + roadSimpleCasing.casing().id, + ], + filter: isUnpaved, + }, +]; diff --git a/src/layer/water.js b/src/layer/water.js index a088ddfc9..89219623f 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -222,3 +222,61 @@ export const waterPointLabel = { }, paint: labelPaintProperties, }; + +export const legendEntries = [ + { + description: "Ocean, sea, or bay", + layers: [water.id, waterLine.id], + filter: ["==", ["get", "class"], "ocean"], + }, + { + description: "Lake or pond", + layers: [water.id, waterLine.id], + filter: [ + "all", + ["==", ["get", "class"], "lake"], + ["!=", ["get", "intermittent"], 1], + ], + }, + { + description: "Intermittent lake or pond", + layers: [water.id, waterLineIntermittent.id], + filter: [ + "all", + ["==", ["get", "class"], "lake"], + ["==", ["get", "intermittent"], 1], + ], + }, + { + description: "River", + layers: [waterway.id], + filter: [ + "all", + ["==", ["get", "class"], "river"], + ["!=", ["get", "intermittent"], 1], + ], + }, + { + description: "Canal", + layers: [waterway.id], + filter: [ + "all", + ["==", ["get", "class"], "canal"], + ["!=", ["get", "intermittent"], 1], + ], + }, + { + description: "Creek", + layers: [waterway.id], + filter: [ + "all", + ["==", ["get", "class"], "stream"], + ["!=", ["get", "intermittent"], 1], + ], + }, + { + description: "Intermittent river or creek", + layers: [waterway.id, waterwayIntermittent.id], + filter: ["==", ["get", "intermittent"], 1], + }, +]; diff --git a/test/spec/highway_shield.js b/test/spec/highway_shield.js new file mode 100644 index 000000000..7f83d28ad --- /dev/null +++ b/test/spec/highway_shield.js @@ -0,0 +1,65 @@ +"use strict"; + +import chai, { expect } from "chai"; +import * as HighwayShieldLayers from "../../src/layer/highway_shield.js"; +import { expression } from "@maplibre/maplibre-gl-style-spec"; + +function expressionContext(properties) { + return { + properties: () => properties, + }; +} + +describe("highway_shield", function () { + describe("#parseImageName", function () { + let evaluatedExpression = (properties) => + expression + .createExpression(HighwayShieldLayers.getImageNameExpression(1)) + .value.expression.evaluate(expressionContext(properties)); + + let expectImageName = (network, ref, name, expectedImageName) => { + let properties = { + route_1: `${network || ""}=${ref || ""}`, + name: name || null, + }; + let evaluated = evaluatedExpression(properties); + let expectedProperties = { + imageName: expectedImageName, + network: network || "", + ref: ref || "", + name: + !ref && HighwayShieldLayers.namedRouteNetworks.includes(network) + ? name + : undefined, + }; + expect(HighwayShieldLayers.parseImageName(evaluated)).to.be.deep.equal( + expectedProperties + ); + }; + + it("parses an image name for a numbered route", function () { + expectImageName("NET", "REF", undefined, "shield\nNET=REF"); + expectImageName("NET", "REF", "NAME", "shield\nNET=REF"); + }); + it("parses an image name for an unnumbered route", function () { + expectImageName("NET", undefined, undefined, "shield\nNET="); + }); + it("parses an image name for a named route", function () { + expectImageName( + "US:KY:Parkway", + undefined, + "NAME", + "shield\nUS:KY:Parkway=\nNAME" + ); + expectImageName( + "US:KY:Parkway", + "REF", + "NAME", + "shield\nUS:KY:Parkway=REF" + ); + }); + it("parses an image name for a network-independent route", function () { + expectImageName(undefined, "REF", "NAME", "shield\n=REF"); + }); + }); +}); From 331ea22e775dc1d19fa67acea1614a65070488ce Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 3 Jan 2023 07:28:44 -0500 Subject: [PATCH 146/514] NE truck/byway --- icons/shield_us_ne_byway_noref.svg | 26 ++++++++++++++++++++++++++ src/js/shield_defs.js | 5 +++++ 2 files changed, 31 insertions(+) create mode 100644 icons/shield_us_ne_byway_noref.svg diff --git a/icons/shield_us_ne_byway_noref.svg b/icons/shield_us_ne_byway_noref.svg new file mode 100644 index 000000000..2a1dc6e11 --- /dev/null +++ b/icons/shield_us_ne_byway_noref.svg @@ -0,0 +1,26 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index bccf5d8dc..5e72298db 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -1883,6 +1883,11 @@ export function loadShields(shieldImages) { shields["US:NE:Link"] = banneredShield(shields["US:NE"], ["LINK"]); shields["US:NE:Rec"] = banneredShield(shields["US:NE"], ["REC"]); shields["US:NE:Spur"] = banneredShield(shields["US:NE"], ["SPUR"]); + shields["US:NE:Truck"] = banneredShield(shields["US:NE"], ["TRK"]); + shields["US:NE:Scenic"] = { + backgroundImage: shieldImages.shield_us_ne_byway_noref, + notext: true, + }; // New Hampshire shields["US:NH"] = { From 484dd81848b47744f6d26fc3f49ed2cab54d21ff Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Tue, 3 Jan 2023 09:08:55 -0500 Subject: [PATCH 147/514] remove commented lines of code --- src/js/shield_canvas_draw.js | 80 +++++++++++++++--------------------- src/js/shield_defs.js | 12 +++++- 2 files changed, 44 insertions(+), 48 deletions(-) diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index 5513d8aed..163c679de 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -215,7 +215,7 @@ export function trapezoid( let drawRadius = radius * PXR; let x0 = lineWidth; - let x11 = width - lineWidth; + let x9 = width - lineWidth; let y0 = shortSideUp ? CS - lineWidth : lineWidth; let y3 = shortSideUp ? lineWidth : CS - lineWidth; @@ -227,19 +227,16 @@ export function trapezoid( let x3 = x0 + angleSign * (y2 - y0) * tangent; let x4 = x0 + angleSign * (y3 - y0) * tangent; let x5 = x3 + angleSign * drawRadius * cosine; - // let x6 = width - x5; - let x7 = width - x4; - let x8 = width - x3; - let x9 = width - x2; - // let x10 = width - x1; - console.log([x0, x1, x2, x3, x4, x5, x7, x8, x9, x11]); + let x6 = width - x4; + let x7 = width - x3; + let x8 = width - x2; ctx.beginPath(); - ctx.moveTo(x9, y0); - ctx.arcTo(x11, y0, x8, y2, drawRadius); - ctx.arcTo(x7, y3, x5, y3, drawRadius); + ctx.moveTo(x8, y0); + ctx.arcTo(x9, y0, x7, y2, drawRadius); + ctx.arcTo(x6, y3, x5, y3, drawRadius); ctx.arcTo(x4, y3, x1, y1, drawRadius); - ctx.arcTo(x0, y0, x9, y0, drawRadius); + ctx.arcTo(x0, y0, x8, y0, drawRadius); ctx.closePath(); ctx.lineWidth = lineThick; @@ -358,18 +355,17 @@ export function pentagon( let drawOffset = offset * PXR; let x0 = lineWidth; - let x12 = width - lineWidth; + let x8 = width - lineWidth; let y0 = pointUp ? CS - lineWidth : lineWidth; - let y5 = pointUp ? lineWidth : CS - lineWidth; + let y3 = pointUp ? lineWidth : CS - lineWidth; - let y3 = y5 - angleSign * drawOffset; + let y2 = y3 - angleSign * drawOffset; - let x4 = x0 + angleSign * (y3 - y0) * tangent; - let x6 = (x0 + x12) / 2; - let x8 = x12 - angleSign * (y3 - y0) * tangent; - // let y1 = y0 + radius2 * (1 - sine); + let x2 = x0 + angleSign * (y2 - y0) * tangent; + let x4 = (x0 + x8) / 2; + let x6 = x8 - angleSign * (y2 - y0) * tangent; - let offsetAngle = Math.atan(drawOffset / (x6 - x0)); + let offsetAngle = Math.atan(drawOffset / (x4 - x0)); let offsetSine = Math.sin(offsetAngle); let offsetCosine = Math.cos(offsetAngle); @@ -380,24 +376,18 @@ export function pentagon( let halfComplementTangent2 = Math.tan(halfComplementAngle2); let x1 = x0 + angleSign * drawRadius1 * halfComplementTangent1 * sine; - // let x2 = x0 + drawRadius1 * halfComplementTangent1 * offsetCosine; - let x5 = x4 + drawRadius2 * halfComplementTangent2; - let x7 = x8 - drawRadius2 * halfComplementTangent2; - // let x10 = x12 - drawRadius1 * halfComplementTangent1 * offsetCosine; - let x11 = x12 - angleSign * drawRadius1 * halfComplementTangent1 * sine; - let y2 = y3 - angleSign * drawRadius1 * halfComplementTangent1 * cosine; - // let y4 = y3 + angleSign * drawRadius1 * halfComplementTangent1 * offsetSine; - - // let x3 = x5 - angleSign * drawRadius2 * sine; - // let x9 = x7 + angleSign * drawRadius2 * sine; + let x3 = x2 + drawRadius2 * halfComplementTangent2; + let x5 = x6 - drawRadius2 * halfComplementTangent2; + let x7 = x8 - angleSign * drawRadius1 * halfComplementTangent1 * sine; + let y1 = y2 - angleSign * drawRadius1 * halfComplementTangent1 * cosine; ctx.beginPath(); - ctx.moveTo(x6, y5); - ctx.arcTo(x0, y3, x1, y2, drawRadius1); - ctx.arcTo(x4, y0, x5, y0, drawRadius2); - ctx.lineTo(x7, y0); - ctx.arcTo(x8, y0, x11, y2, drawRadius2); - ctx.arcTo(x12, y3, x6, y5, drawRadius1); + ctx.moveTo(x4, y3); + ctx.arcTo(x0, y2, x1, y1, drawRadius1); + ctx.arcTo(x2, y0, x3, y0, drawRadius2); + ctx.lineTo(x5, y0); + ctx.arcTo(x6, y0, x7, y1, drawRadius2); + ctx.arcTo(x8, y2, x4, y3, drawRadius1); ctx.closePath(); ctx.lineWidth = lineThick; @@ -440,29 +430,27 @@ export function hexagonVertical( let drawOffset = offset * PXR; let x0 = lineWidth; - let x4 = width - lineWidth; + let x2 = width - lineWidth; let y0 = lineWidth; let y5 = CS - lineWidth; - // let x1 = x0 + drawRadius; - let x2 = (x0 + x4) / 2; - // let x3 = x4 - drawRadius; + let x1 = (x0 + x2) / 2; let y1 = y0 + drawOffset; let y4 = y5 - drawOffset; let drawOffsetTangent = - drawRadius * Math.tan(Math.PI / 4 - Math.asin(drawOffset / (x2 - x0)) / 2); + drawRadius * Math.tan(Math.PI / 4 - Math.asin(drawOffset / (x1 - x0)) / 2); let y2 = y1 + drawOffsetTangent; let y3 = y4 - drawOffsetTangent; ctx.beginPath(); - ctx.moveTo(x2, y5); + ctx.moveTo(x1, y5); ctx.arcTo(x0, y4, x0, y3, drawRadius); - ctx.arcTo(x0, y1, x2, y0, drawRadius); - ctx.lineTo(x2, y0); - ctx.arcTo(x4, y1, x4, y2, drawRadius); - ctx.arcTo(x4, y4, x2, y5, drawRadius); - ctx.lineTo(x2, y5); + ctx.arcTo(x0, y1, x1, y0, drawRadius); + ctx.lineTo(x1, y0); + ctx.arcTo(x2, y1, x2, y2, drawRadius); + ctx.arcTo(x2, y4, x1, y5, drawRadius); + ctx.lineTo(x1, y5); ctx.closePath(); ctx.lineWidth = lineThick; diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index d8591f3c1..2a3351e7e 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -3999,7 +3999,12 @@ export function loadShields(shieldImages) { Color.shields.black ); shields[`AU:${state_or_territory}:S`] = fishheadShieldBlue; - shields[`AU:${state_or_territory}:T`] = pentagonUpShield(3, 15, Color.shields.brown, Color.shields.white); + shields[`AU:${state_or_territory}:T`] = pentagonUpShield( + 3, + 15, + Color.shields.brown, + Color.shields.white + ); shields[`AU:${state_or_territory}:ALT`] = banneredShield( roundedRectShield(Color.shields.green, Color.shields.yellow), ["ALT"] @@ -4008,7 +4013,10 @@ export function loadShields(shieldImages) { homePlateDownShield(5, Color.shields.white, Color.shields.black), ["ALT"] ); - shields[`AU:${state_or_territory}:ALT_S`] = banneredShield(fishheadShieldBlue, ["ALT"]) ; + shields[`AU:${state_or_territory}:ALT_S`] = banneredShield( + fishheadShieldBlue, + ["ALT"] + ); } ); From 29e6d0f7907ba253cd32df94021143a8454b6f22 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 3 Jan 2023 12:37:15 -0500 Subject: [PATCH 148/514] Refactor layers to layers folder --- CONTRIBUTING.md | 6 +- src/americana.js | 222 +------------------------------------------ src/layer/index.js | 231 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+), 223 deletions(-) create mode 100644 src/layer/index.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0d77efa8b..d393fdc28 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,12 +4,12 @@ _'murica!_ The style is located within **src/** and is organized as follows: -- **layer/** - Individual style layers, organized by subject area +- **layer/** - Individual style layers, organized by subject area. The ordering of layers is specified in `index.js`. - **icons/** - SVG icons, which get converted into PNG stylesheets - **constants/** - Style elements that are frequently re-used - **js/** - Dynamic javascript code for highway shields and stylesheet building -- **config.js** - Configuration settings (MapTiler API key, OpenMapTiles URL, etc) -- **americana.js** - OpenMapTiles loader with layer ordering +- **config.js** - Configuration settings (MapTiler API key, OpenMapTiles tile server URL, etc) +- **americana.js** - MapLibre loader and configuration for the demo map - **index.html** - Demonstration map HTML page ## Install Node.js diff --git a/src/americana.js b/src/americana.js index 797eb53c2..86cea8f21 100644 --- a/src/americana.js +++ b/src/americana.js @@ -7,22 +7,7 @@ import * as Label from "./constants/label.js"; import * as Shield from "./js/shield.js"; import * as ShieldDef from "./js/shield_defs.js"; -import * as lyrAeroway from "./layer/aeroway.js"; -import * as lyrBackground from "./layer/background.js"; -import * as lyrBoundary from "./layer/boundary.js"; -import * as lyrConstruction from "./layer/construction.js"; -import * as lyrHighwayShield from "./layer/highway_shield.js"; -import * as lyrLanduse from "./layer/landuse.js"; -import * as lyrOneway from "./layer/oneway.js"; -import * as lyrPark from "./layer/park.js"; -import * as lyrPlace from "./layer/place.js"; -import * as lyrRail from "./layer/rail.js"; -import * as lyrRoad from "./layer/road.js"; -import * as lyrTransportationLabel from "./layer/transportation_label.js"; -import * as lyrWater from "./layer/water.js"; -import * as lyrBuilding from "./layer/building.js"; -import * as lyrHighwayExit from "./layer/highway_exit.js"; -import * as lyrFerry from "./layer/ferry.js"; +import * as Layers from "./layer/index.js"; import * as languageLabel from "./js/language_label.js"; @@ -36,210 +21,7 @@ import SampleControl from "openmapsamples-maplibre/OpenMapSamplesControl.js"; import { default as OpenMapTilesSamples } from "openmapsamples/samples/OpenMapTiles/index.js"; function buildLayers() { - // Layers from bottom to top - let layers = []; - - layers.push( - lyrBackground.base, - lyrLanduse.urbanizedArea, - lyrPark.fill, - lyrAeroway.fill, - lyrPark.parkFill, - - lyrBoundary.countyCasing, - lyrBoundary.stateCasing, - lyrBoundary.countryCasing, - - lyrWater.waterLine, - lyrWater.waterLineIntermittent, - lyrWater.waterway, - lyrWater.waterwayIntermittent, - lyrWater.water, - - lyrPark.outline, - lyrAeroway.outline, - lyrPark.parkOutline, - - lyrBoundary.city, - lyrBoundary.county, - lyrBoundary.state, - lyrBoundary.country, - - lyrBackground.pierArea, - lyrBackground.pierLine, - - lyrRail.railTunnel.dashes(), - lyrRail.railServiceTunnel.dashes(), - - lyrRail.narrowGaugeTunnel.dashes(), - lyrRail.narrowGaugeServiceTunnel.dashes(), - - lyrRail.lightRailTramTunnel.dashes(), - lyrRail.lightRailTramServiceTunnel.dashes(), - - lyrRail.funicularTunnel.dashes(), - - lyrRail.railwayTunnel.fill(), - - lyrConstruction.road, - - lyrRoad.roadTunnel.casing(), - - lyrRoad.roadTunnel.fill(), - - lyrOneway.tunnel, - lyrOneway.tunnelLink, - - lyrFerry.ferry, - - lyrAeroway.runway, - lyrAeroway.runwayArea, - lyrAeroway.taxiway, - lyrAeroway.taxiwayArea, - - lyrRoad.motorwayLink.casing(), - lyrRoad.trunkLink.casing(), - - lyrRoad.roadLinkSimpleCasing.casing(), - - lyrRoad.motorway.casing(), - lyrRoad.trunk.casing(), - lyrRoad.primaryExpressway.casing(), - lyrRoad.secondaryExpressway.casing(), - lyrRoad.tertiaryExpressway.casing(), - - lyrRoad.roadSimpleCasing.casing(), - - lyrRoad.motorwayLink.fill(), - lyrRoad.roadLinkSimpleFill.fill(), - lyrRoad.primaryLink.fill(), - lyrRoad.primaryLinkToll.fill(), - lyrRoad.secondaryLink.fill(), - lyrRoad.secondaryLinkToll.fill(), - lyrRoad.tertiaryLink.fill(), - lyrRoad.tertiaryLinkToll.fill(), - - lyrRoad.minor.fill(), - lyrRoad.minorToll.fill(), - lyrRoad.tertiary.fill(), - lyrRoad.tertiaryToll.fill(), - lyrRoad.secondary.fill(), - lyrRoad.secondaryToll.fill(), - lyrRoad.primary.fill(), - lyrRoad.primaryToll.fill(), - lyrRoad.roadSimpleFill.fill(), - lyrRoad.motorway.fill(), - - lyrRoad.road.surface(), - - lyrRail.rail.dashes(), - lyrRail.railService.dashes(), - - lyrRail.narrowGauge.dashes(), - lyrRail.narrowGaugeService.dashes(), - - lyrRail.lightRailTram.dashes(), - lyrRail.lightRailTramService.dashes(), - - lyrRail.funicular.dashes(), - - lyrRail.railway.fill(), - - lyrOneway.road, - lyrOneway.link - ); - - layers.push(lyrBuilding.building); - - var bridgeLayers = [ - lyrRail.bridgeCasing, - - lyrRoad.trunkLinkBridge.casing(), - lyrRoad.motorwayLinkBridge.casing(), - - lyrRoad.roadLinkSimpleCasingBridge.casing(), - - lyrRoad.tertiaryExpresswayBridge.casing(), - lyrRoad.secondaryExpresswayBridge.casing(), - lyrRoad.primaryExpresswayBridge.casing(), - lyrRoad.trunkBridge.casing(), - lyrRoad.motorwayBridge.casing(), - - lyrRoad.roadSimpleCasingBridge.casing(), - - lyrRoad.tertiaryLinkBridge.fill(), - lyrRoad.tertiaryLinkTollBridge.fill(), - lyrRoad.secondaryLinkBridge.fill(), - lyrRoad.secondaryLinkTollBridge.fill(), - lyrRoad.primaryLinkBridge.fill(), - lyrRoad.primaryLinkTollBridge.fill(), - lyrRoad.roadLinkSimpleFillBridge.fill(), - lyrRoad.motorwayLinkBridge.fill(), - - lyrRoad.minorBridge.fill(), - lyrRoad.minorTollBridge.fill(), - lyrRoad.tertiaryBridge.fill(), - lyrRoad.tertiaryTollBridge.fill(), - lyrRoad.secondaryBridge.fill(), - lyrRoad.secondaryTollBridge.fill(), - lyrRoad.primaryBridge.fill(), - lyrRoad.primaryTollBridge.fill(), - lyrRoad.roadSimpleFillBridge.fill(), - lyrRoad.motorwayBridge.fill(), - - lyrRoad.roadBridge.surface(), - - lyrRail.railBridge.dashes(), - lyrRail.railServiceBridge.dashes(), - - lyrRail.narrowGaugeBridge.dashes(), - lyrRail.narrowGaugeServiceBridge.dashes(), - - lyrRail.lightRailTramBridge.dashes(), - lyrRail.lightRailTramServiceBridge.dashes(), - - lyrRail.funicularBridge.dashes(), - - lyrRail.railwayBridge.fill(), - - lyrOneway.bridge, - lyrOneway.bridgeLink, - ]; - - layers.push(...lyrRail.getLayerSeparatedBridgeLayers(bridgeLayers)); - - layers.push( - //The labels at the end of the list draw on top of the layers at the beginning. - lyrWater.waterwayLabel, - lyrWater.waterLabel, - lyrWater.waterPointLabel, - - lyrTransportationLabel.bridgeSpacer, - lyrTransportationLabel.label, - - lyrPark.label, - lyrPark.parkLabel, - /* The ref label shows up at lower zoom levels and when the long name doesn't fit */ - lyrAeroway.airportRefLabel, - lyrAeroway.minorAirportRefLabel, - lyrAeroway.airportLabel, - lyrAeroway.minorAirportLabel, - lyrAeroway.airportGate, - - lyrHighwayShield.shield, - - lyrHighwayExit.exits, - - lyrPlace.state, - lyrPlace.village, - lyrPlace.town, - lyrPlace.city, - lyrPlace.countryOther, - lyrPlace.country3, - lyrPlace.country2, - lyrPlace.country1, - lyrPlace.continent - ); + let layers = Layers.build(); Label.localizeLayers(layers, Label.getLocales()); diff --git a/src/layer/index.js b/src/layer/index.js new file mode 100644 index 000000000..14d6c5e84 --- /dev/null +++ b/src/layer/index.js @@ -0,0 +1,231 @@ +"use strict"; + +import * as lyrAeroway from "./aeroway.js"; +import * as lyrBackground from "./background.js"; +import * as lyrBoundary from "./boundary.js"; +import * as lyrConstruction from "./construction.js"; +import * as lyrHighwayShield from "./highway_shield.js"; +import * as lyrLanduse from "./landuse.js"; +import * as lyrOneway from "./oneway.js"; +import * as lyrPark from "./park.js"; +import * as lyrPlace from "./place.js"; +import * as lyrRail from "./rail.js"; +import * as lyrRoad from "./road.js"; +import * as lyrTransportationLabel from "./transportation_label.js"; +import * as lyrWater from "./water.js"; +import * as lyrBuilding from "./building.js"; +import * as lyrHighwayExit from "./highway_exit.js"; +import * as lyrFerry from "./ferry.js"; + +/** + * Builds the Americana layers property without localization. + * See: https://maplibre.org/maplibre-gl-js-docs/style-spec/layers/ + */ +export function build() { + // Layers from bottom to top + let layers = []; + + layers.push( + lyrBackground.base, + lyrLanduse.urbanizedArea, + lyrPark.fill, + lyrAeroway.fill, + lyrPark.parkFill, + + lyrBoundary.countyCasing, + lyrBoundary.stateCasing, + lyrBoundary.countryCasing, + + lyrWater.waterLine, + lyrWater.waterLineIntermittent, + lyrWater.waterway, + lyrWater.waterwayIntermittent, + lyrWater.water, + + lyrPark.outline, + lyrAeroway.outline, + lyrPark.parkOutline, + + lyrBoundary.city, + lyrBoundary.county, + lyrBoundary.state, + lyrBoundary.country, + + lyrBackground.pierArea, + lyrBackground.pierLine, + + lyrRail.railTunnel.dashes(), + lyrRail.railServiceTunnel.dashes(), + + lyrRail.narrowGaugeTunnel.dashes(), + lyrRail.narrowGaugeServiceTunnel.dashes(), + + lyrRail.lightRailTramTunnel.dashes(), + lyrRail.lightRailTramServiceTunnel.dashes(), + + lyrRail.funicularTunnel.dashes(), + + lyrRail.railwayTunnel.fill(), + + lyrConstruction.road, + + lyrRoad.roadTunnel.casing(), + + lyrRoad.roadTunnel.fill(), + + lyrOneway.tunnel, + lyrOneway.tunnelLink, + + lyrFerry.ferry, + + lyrAeroway.runway, + lyrAeroway.runwayArea, + lyrAeroway.taxiway, + lyrAeroway.taxiwayArea, + + lyrRoad.motorwayLink.casing(), + lyrRoad.trunkLink.casing(), + + lyrRoad.roadLinkSimpleCasing.casing(), + + lyrRoad.motorway.casing(), + lyrRoad.trunk.casing(), + lyrRoad.primaryExpressway.casing(), + lyrRoad.secondaryExpressway.casing(), + lyrRoad.tertiaryExpressway.casing(), + + lyrRoad.roadSimpleCasing.casing(), + + lyrRoad.motorwayLink.fill(), + lyrRoad.roadLinkSimpleFill.fill(), + lyrRoad.primaryLink.fill(), + lyrRoad.primaryLinkToll.fill(), + lyrRoad.secondaryLink.fill(), + lyrRoad.secondaryLinkToll.fill(), + lyrRoad.tertiaryLink.fill(), + lyrRoad.tertiaryLinkToll.fill(), + + lyrRoad.minor.fill(), + lyrRoad.minorToll.fill(), + lyrRoad.tertiary.fill(), + lyrRoad.tertiaryToll.fill(), + lyrRoad.secondary.fill(), + lyrRoad.secondaryToll.fill(), + lyrRoad.primary.fill(), + lyrRoad.primaryToll.fill(), + lyrRoad.roadSimpleFill.fill(), + lyrRoad.motorway.fill(), + + lyrRoad.road.surface(), + + lyrRail.rail.dashes(), + lyrRail.railService.dashes(), + + lyrRail.narrowGauge.dashes(), + lyrRail.narrowGaugeService.dashes(), + + lyrRail.lightRailTram.dashes(), + lyrRail.lightRailTramService.dashes(), + + lyrRail.funicular.dashes(), + + lyrRail.railway.fill(), + + lyrOneway.road, + lyrOneway.link + ); + + layers.push(lyrBuilding.building); + + var bridgeLayers = [ + lyrRail.bridgeCasing, + + lyrRoad.trunkLinkBridge.casing(), + lyrRoad.motorwayLinkBridge.casing(), + + lyrRoad.roadLinkSimpleCasingBridge.casing(), + + lyrRoad.tertiaryExpresswayBridge.casing(), + lyrRoad.secondaryExpresswayBridge.casing(), + lyrRoad.primaryExpresswayBridge.casing(), + lyrRoad.trunkBridge.casing(), + lyrRoad.motorwayBridge.casing(), + + lyrRoad.roadSimpleCasingBridge.casing(), + + lyrRoad.tertiaryLinkBridge.fill(), + lyrRoad.tertiaryLinkTollBridge.fill(), + lyrRoad.secondaryLinkBridge.fill(), + lyrRoad.secondaryLinkTollBridge.fill(), + lyrRoad.primaryLinkBridge.fill(), + lyrRoad.primaryLinkTollBridge.fill(), + lyrRoad.roadLinkSimpleFillBridge.fill(), + lyrRoad.motorwayLinkBridge.fill(), + + lyrRoad.minorBridge.fill(), + lyrRoad.minorTollBridge.fill(), + lyrRoad.tertiaryBridge.fill(), + lyrRoad.tertiaryTollBridge.fill(), + lyrRoad.secondaryBridge.fill(), + lyrRoad.secondaryTollBridge.fill(), + lyrRoad.primaryBridge.fill(), + lyrRoad.primaryTollBridge.fill(), + lyrRoad.roadSimpleFillBridge.fill(), + lyrRoad.motorwayBridge.fill(), + + lyrRoad.roadBridge.surface(), + + lyrRail.railBridge.dashes(), + lyrRail.railServiceBridge.dashes(), + + lyrRail.narrowGaugeBridge.dashes(), + lyrRail.narrowGaugeServiceBridge.dashes(), + + lyrRail.lightRailTramBridge.dashes(), + lyrRail.lightRailTramServiceBridge.dashes(), + + lyrRail.funicularBridge.dashes(), + + lyrRail.railwayBridge.fill(), + + lyrOneway.bridge, + lyrOneway.bridgeLink, + ]; + + layers.push(...lyrRail.getLayerSeparatedBridgeLayers(bridgeLayers)); + + layers.push( + //The labels at the end of the list draw on top of the layers at the beginning. + lyrWater.waterwayLabel, + lyrWater.waterLabel, + lyrWater.waterPointLabel, + + lyrTransportationLabel.bridgeSpacer, + lyrTransportationLabel.label, + + lyrPark.label, + lyrPark.parkLabel, + /* The ref label shows up at lower zoom levels and when the long name doesn't fit */ + lyrAeroway.airportRefLabel, + lyrAeroway.minorAirportRefLabel, + lyrAeroway.airportLabel, + lyrAeroway.minorAirportLabel, + lyrAeroway.airportGate, + + lyrHighwayShield.shield, + + lyrHighwayExit.exits, + + lyrPlace.state, + lyrPlace.village, + lyrPlace.town, + lyrPlace.city, + lyrPlace.countryOther, + lyrPlace.country3, + lyrPlace.country2, + lyrPlace.country1, + lyrPlace.continent + ); + + return layers; +} From 1d8ede7789c9460b70a278b6b13551339b015082 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 3 Jan 2023 12:38:09 -0500 Subject: [PATCH 149/514] Prettier NE byway --- icons/shield_us_ne_byway_noref.svg | 32 +++++++----------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/icons/shield_us_ne_byway_noref.svg b/icons/shield_us_ne_byway_noref.svg index 2a1dc6e11..13c98be45 100644 --- a/icons/shield_us_ne_byway_noref.svg +++ b/icons/shield_us_ne_byway_noref.svg @@ -1,26 +1,8 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - + + + + + + + From 0e4adb3f2a26c2c4ed82e8663e05a767a6dabab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Wed, 4 Jan 2023 15:39:13 -0800 Subject: [PATCH 150/514] Preserve newlines in labels in legend --- src/index.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index.html b/src/index.html index 4c8d2c26c..ca5c83ac9 100644 --- a/src/index.html +++ b/src/index.html @@ -50,6 +50,9 @@ width: 0; text-align: center; } + .legend-row > .label { + white-space: pre-line; + } .legend-row > .icon { white-space: nowrap; } From fefefb9f38a4d0c6f60dbfcec199b77ed856478b Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Thu, 5 Jan 2023 18:09:40 -0500 Subject: [PATCH 151/514] add escutcheon shield draw function --- icons/shield_escutcheon_2.svg | 3 - icons/shield_escutcheon_3.svg | 3 - icons/shield_escutcheon_blue_2.svg | 3 - icons/shield_escutcheon_blue_3.svg | 3 - icons/shield_escutcheon_rounded_2.svg | 3 - icons/shield_escutcheon_rounded_3.svg | 3 - icons/shield_escutcheon_yellow_2.svg | 3 - src/js/shield_canvas_draw.js | 65 ++++++++++++++++ src/js/shield_defs.js | 106 ++++++++++++++++---------- 9 files changed, 131 insertions(+), 61 deletions(-) delete mode 100644 icons/shield_escutcheon_2.svg delete mode 100644 icons/shield_escutcheon_3.svg delete mode 100644 icons/shield_escutcheon_blue_2.svg delete mode 100644 icons/shield_escutcheon_blue_3.svg delete mode 100644 icons/shield_escutcheon_rounded_2.svg delete mode 100644 icons/shield_escutcheon_rounded_3.svg delete mode 100644 icons/shield_escutcheon_yellow_2.svg diff --git a/icons/shield_escutcheon_2.svg b/icons/shield_escutcheon_2.svg deleted file mode 100644 index bd4ed6f93..000000000 --- a/icons/shield_escutcheon_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_escutcheon_3.svg b/icons/shield_escutcheon_3.svg deleted file mode 100644 index 18bdb4fa1..000000000 --- a/icons/shield_escutcheon_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_escutcheon_blue_2.svg b/icons/shield_escutcheon_blue_2.svg deleted file mode 100644 index ebc14ce93..000000000 --- a/icons/shield_escutcheon_blue_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_escutcheon_blue_3.svg b/icons/shield_escutcheon_blue_3.svg deleted file mode 100644 index 5d677f77e..000000000 --- a/icons/shield_escutcheon_blue_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_escutcheon_rounded_2.svg b/icons/shield_escutcheon_rounded_2.svg deleted file mode 100644 index 10501e578..000000000 --- a/icons/shield_escutcheon_rounded_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_escutcheon_rounded_3.svg b/icons/shield_escutcheon_rounded_3.svg deleted file mode 100644 index 9a63f3372..000000000 --- a/icons/shield_escutcheon_rounded_3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/shield_escutcheon_yellow_2.svg b/icons/shield_escutcheon_yellow_2.svg deleted file mode 100644 index 73372283b..000000000 --- a/icons/shield_escutcheon_yellow_2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index 163c679de..d2bc654d9 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -180,6 +180,71 @@ export function roundedRectangle( return ctx; } +export function escutcheon( + offset, + fill, + outline, + ref, + radius, + outlineWidth, + rectWidth +) { + if (rectWidth == null) { + var shieldWidth = + ShieldText.calculateTextWidth(ref, genericShieldFontSize) + 2 * PXR; + var width = Math.max( + minGenericShieldWidth, + Math.min(maxGenericShieldWidth, shieldWidth) + ); + } else { + var width = rectWidth * PXR; + } + + var ctx = Gfx.getGfxContext({ width: width, height: CS }); + + let lineThick = outlineWidth * PXR; + let lineWidth = lineThick / 2; + let drawRadius = radius * PXR; + let drawOffset = offset * PXR; + + let x0 = lineWidth; + let x5 = width - lineWidth; + + let y0 = lineWidth; + let y5 = CS - lineWidth; + + let x1 = x0 + drawRadius; + let x3 = (x0 + x5) / 2; + let y1 = y0 + drawRadius; + let y2 = y5 - drawOffset; + + let x2 = (x0 + x3) / 2; + let x4 = (x3 + x5) / 2; + let y3 = (y2 + y5) / 2; + + let y4 = (y3 + 3 * y5) / 4; + + ctx.beginPath(); + ctx.moveTo(x3, y5); + ctx.bezierCurveTo(x2, y4, x0, y3, x0, y2); + ctx.arcTo(x0, y0, x1, y0, drawRadius); + ctx.arcTo(x5, y0, x5, y1, drawRadius); + ctx.lineTo(x5, y2); + ctx.bezierCurveTo(x5, y3, x4, y4, x3, y5); + ctx.closePath(); + + ctx.lineWidth = lineThick; + ctx.fillStyle = fill; + ctx.fill(); + + if (outline != null) { + ctx.strokeStyle = outline; + ctx.stroke(); + } + + return ctx; +} + export function trapezoid( shortSideUp, angle, diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 73c55757e..3b540606b 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -90,6 +90,50 @@ function roundedRectShield( }; } +/** + * Draws a shield with an escutcheon background, pointed downward + * + * @param {*} offset - Height of curved portion + * @param {*} fillColor - Color of escutcheon background fill + * @param {*} strokeColor - Color of escutcheon outline stroke + * @param {*} textColor - Color of text (defaults to strokeColor) + * @param {*} radius - Corner radius of escutcheon (defaults to 0) + * @param {*} rectWidth - Width of escutcheon (defaults to variable-width) + * @returns a shield definition object + */ +function escutcheonDownShield( + offset, + fillColor, + strokeColor, + textColor, + radius, + rectWidth +) { + textColor = textColor ?? strokeColor; + radius = radius ?? 0; + return { + backgroundDraw: (ref) => + ShieldDraw.escutcheon( + offset, + fillColor, + strokeColor, + ref, + radius, + 1, + rectWidth + ), + textLayoutConstraint: (spaceBounds, textBounds) => + ShieldText.roundedRectTextConstraint(spaceBounds, textBounds, radius), + padding: { + left: 2, + right: 2, + top: 1, + bottom: 1 + offset / 2, + }, + textColor: textColor, + }; +} + /** * Draws a shield with a trapezoid background, with the short side on bottom * @@ -692,42 +736,6 @@ export function loadShields(shieldImages) { }, }; - let escutcheonShield = { - backgroundImage: [ - shieldImages.shield_escutcheon_2, - shieldImages.shield_escutcheon_3, - ], - textColor: Color.shields.black, - padding: { - left: 2, - right: 2, - top: 2, - bottom: 7, - }, - }; - - let escutcheonShieldRounded = { - ...escutcheonShield, - backgroundImage: [ - shieldImages.shield_escutcheon_rounded_2, - shieldImages.shield_escutcheon_rounded_3, - ], - }; - - let escutcheonShieldYellow = { - ...escutcheonShield, - backgroundImage: shieldImages.shield_escutcheon_yellow_2, - }; - - let escutcheonShieldBlue = { - ...escutcheonShield, - backgroundImage: [ - shieldImages.shield_escutcheon_blue_2, - shieldImages.shield_escutcheon_blue_3, - ], - textColor: Color.shields.white, - }; - let fishheadShieldRed = { backgroundImage: [ shieldImages.shield_fishhead_red_2, @@ -2654,8 +2662,16 @@ export function loadShields(shieldImages) { shields["US:PA:Allegheny:Belt"] = {}; // See ref-specific cases below // Puerto Rico - shields["US:PR:primary"] = escutcheonShieldBlue; - shields["US:PR:primary_urban"] = escutcheonShield; + shields["US:PR:primary"] = escutcheonDownShield( + 10, + Color.shields.blue, + Color.shields.white + ); + shields["US:PR:primary_urban"] = escutcheonDownShield( + 10, + Color.shields.white, + Color.shields.black + ); shields["US:PR:secondary"] = pentagonUpShield( 3, 15, @@ -2955,7 +2971,13 @@ export function loadShields(shieldImages) { ); // Virginia - shields["US:VA"] = escutcheonShieldRounded; + shields["US:VA"] = escutcheonDownShield( + 10, + Color.shields.white, + Color.shields.black, + Color.shields.black, + 2 + ); shields["US:VA:Business"] = banneredShield(shields["US:VA"], ["BUS"]); shields["US:VA:Alternate"] = banneredShield(shields["US:VA"], ["ALT"]); shields["US:VA:Secondary"] = pillShield( @@ -3368,7 +3390,11 @@ export function loadShields(shieldImages) { ); // Hong Kong - shields["HK"] = escutcheonShieldYellow; + shields["HK"] = escutcheonDownShield( + 10, + Color.shields.yellow, + Color.shields.black + ); // Indonesia shields["ID:national"] = { From 527f2fc724af917a418593d1af17617efabbf080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Wed, 4 Jan 2023 15:42:45 -0800 Subject: [PATCH 152/514] Format name lists in labels Added a function that returns an expression to find and replace a fixed number of occurrences of a substring. Added a function to format a list of semicolon-delimited tag values. Separate multiple names by newlines or bullets as necessary. --- src/constants/label.js | 145 ++++++++++++++++++++- src/layer/transportation_label.js | 2 +- test/spec/label.js | 205 ++++++++++++++++++++++++++---- 3 files changed, 322 insertions(+), 30 deletions(-) diff --git a/src/constants/label.js b/src/constants/label.js index 3799d3ad4..13c59b0ea 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -117,13 +117,134 @@ export function localizeLayers(layers, locales) { } /** - * The name in the user's preferred language. + * Returns an expression that replaces a finite number of occurrences of a + * substring expression withing a larger string expression, starting at a given + * index. + * + * This expression nests recursively by the maximum number of replacements. Take + * special care to minimize this limit, which exponentially increases the length + * of a property value in JSON. Excessive nesting causes acute performance + * problems when loading the style. + * + * The returned expression can be complex, so use it only once within a property + * value. To reuse the evaluated value, bind it to a variable in a let + * expression. + * + * @param haystack The overall string expression to search within. + * @param needle The string to search for, or an expression that evaluates to + * this string. + */ +export function replaceExpression( + haystack, + needle, + replacement, + haystackStart, + numReplacements = 1 +) { + let asIs = ["slice", haystack, haystackStart]; + if (numReplacements <= 0) { + return asIs; + } + + let needleStart = ["index-of", needle, haystack, haystackStart]; + let needleLength = + typeof needle === "object" ? ["length", needle] : needle.length; + let needleEnd = ["+", needleStart, needleLength]; + return [ + "case", + [">=", needleStart, 0], + [ + "concat", + ["slice", haystack, haystackStart, needleStart], + replacement, + replaceExpression( + haystack, + needle, + replacement, + needleEnd, + numReplacements - 1 + ), + ], + asIs, + ]; +} + +/** + * Maximum number of values in a semicolon-delimited list of values. + * + * Increasing this constant deepens recursion for replacing delimiters in the + * list, potentially affecting style loading performance. + */ +const maxValueListLength = 9; + +/** + * Returns an expression interpreting the given string as a list of tag values, + * pretty-printing the standard semicolon delimiter with the given separator. + * + * https://wiki.openstreetmap.org/wiki/Semi-colon_value_separator + * + * The returned expression can be complex, so use it only once within a property + * value. To reuse the evaluated value, bind it to a variable in a let + * expression. + * + * @param valueList A semicolon-delimited list of values. + * @param separator A string to insert between each value, or an expression that + * evaluates to this string. + */ +export function listValuesExpression(valueList, separator) { + let maxSeparators = maxValueListLength - 1; + // Replace the ;; escape sequence with a placeholder sequence unlikely to + // legitimately occur inside a value or separator. + const objReplacementChar = "\x91\ufffc\x92"; // https://overpass-turbo.eu/s/1pJx + let safeValueList = replaceExpression( + valueList, + ";;", + objReplacementChar, + 0, + maxSeparators + ); + // Pretty-print the ; delimiter. + let prettyValueList = replaceExpression( + ["var", "safeValueList"], + ";", + separator, + 0, + maxSeparators + ); + // Replace the placeholder sequence with an unescaped semicolon. + let prettySafeValueList = replaceExpression( + ["var", "prettyValueList"], + objReplacementChar, + ";", + 0, + maxSeparators + ); + return [ + "let", + "safeValueList", + safeValueList, + ["let", "prettyValueList", prettyValueList, prettySafeValueList], + ]; +} + +/** + * The names in the user's preferred language, each on a separate line. */ export const localizedName = [ "let", "localizedName", "", - ["var", "localizedName"], + listValuesExpression(["var", "localizedName"], "\n"), +]; + +/** + * The names in the user's preferred language, all on the same line. + */ +export const localizedNameInline = [ + "let", + "localizedName", + "", + listValuesExpression(["var", "localizedName"], " \u2022 "), ]; /** @@ -217,7 +338,7 @@ export const localizedNameWithLocalGloss = [ ["var", "localizedCollator"], ], // ...just pick one. - ["var", "localizedName"], + ["format", listValuesExpression(["var", "localizedName"], "\n")], // If the name in the preferred language is the same as the name in the // local language except for the omission of diacritics and/or the addition // of a suffix (e.g., "City" in English)... @@ -227,7 +348,13 @@ export const localizedNameWithLocalGloss = [ ["var", "diacriticInsensitiveCollator"] ), // ...then replace the common prefix with the local name. - overwritePrefixExpression(["var", "localizedName"], ["get", "name"]), + [ + "format", + overwritePrefixExpression( + ["var", "localizedName"], + listValuesExpression(["get", "name"], "\n") + ), + ], // If the name in the preferred language is the same as the name in the // local language except for the omission of diacritics and/or the addition // of a prefix (e.g., "City of" in English or "Ciudad de" in Spanish)... @@ -237,7 +364,13 @@ export const localizedNameWithLocalGloss = [ ["var", "diacriticInsensitiveCollator"] ), // ...then replace the common suffix with the local name. - overwriteSuffixExpression(["var", "localizedName"], ["get", "name"]), + [ + "format", + overwriteSuffixExpression( + ["var", "localizedName"], + listValuesExpression(["get", "name"], "\n") + ), + ], // Otherwise, gloss the name in the local language if it differs from the // localized name. [ @@ -252,7 +385,7 @@ export const localizedNameWithLocalGloss = [ // bother rendering it. ["concat", ["slice", ["var", "localizedName"], 0, 1], " "], { "font-scale": 0.001 }, - ["get", "name"], + listValuesExpression(["get", "name"], " \u2022 "), { "font-scale": 0.8 }, ["concat", " ", ["slice", ["var", "localizedName"], 0, 1]], { "font-scale": 0.001 }, diff --git a/src/layer/transportation_label.js b/src/layer/transportation_label.js index 0298f0b09..e3f45f4a1 100644 --- a/src/layer/transportation_label.js +++ b/src/layer/transportation_label.js @@ -80,7 +80,7 @@ export const label = { ["literal", ["OpenHistorical Italic"]], ["literal", ["OpenHistorical"]], ], - "text-field": [...Label.localizedName], + "text-field": [...Label.localizedNameInline], "text-max-angle": 20, "symbol-placement": "line", "text-size": [ diff --git a/test/spec/label.js b/test/spec/label.js index 22910f60b..4c97e628e 100644 --- a/test/spec/label.js +++ b/test/spec/label.js @@ -272,9 +272,9 @@ describe("label", function () { let evaluatedLabelAndGloss = (locales, properties) => { let evaluated = evaluatedExpression(locales, properties); if (typeof evaluated === "string") { - return [evaluated, null]; + return [evaluated]; } - return [evaluated.sections[0].text, evaluated.sections[4].text]; + return [evaluated.sections[0].text, evaluated.sections[4]?.text]; }; let expectGloss = ( @@ -295,11 +295,12 @@ describe("label", function () { }; it("puts an unlocalized name by itself", function () { - expect( - evaluatedExpression(["en"], { - name: "Null Island", - }) - ).to.be.eql("Null Island"); + let evaluated = evaluatedExpression(["en"], { + name: "Null Island", + }); + + expect(evaluated.sections.length).to.be.eql(1); + expect(evaluated.sections[0].text).to.be.eql("Null Island"); }); it("glosses an anglicized name with the local name", function () { let evaluated = evaluatedExpression(["en"], { @@ -321,26 +322,19 @@ describe("label", function () { expect(evaluated.sections[5].scale).to.be.below(0.1); }); it("deduplicates matching anglicized and local names", function () { - expectGloss("en", "Null Island", "Null Island", "Null Island", null); - expectGloss("en", "Null Island", "NULL Island", "Null Island", null); - expectGloss("en", "Montreal", "Montréal", "Montréal", null); - expectGloss("en", "Quebec City", "Québec", "Québec City", null); - expectGloss("en", "Da Nang", "Đà Nẵng", "Đà Nẵng", null); - expectGloss("en", "Nūll Island", "Ñüłl Íşlåńđ", "Ñüłl Íşlåńđ", null); - expectGloss("en", "New York City", "New York", "New York City", null); - expectGloss( - "en", - "Washington, D.C.", - "Washington", - "Washington, D.C.", - null - ); + expectGloss("en", "Null Island", "Null Island", "Null Island"); + expectGloss("en", "Null Island", "NULL Island", "Null Island"); + expectGloss("en", "Montreal", "Montréal", "Montréal"); + expectGloss("en", "Quebec City", "Québec", "Québec City"); + expectGloss("en", "Da Nang", "Đà Nẵng", "Đà Nẵng"); + expectGloss("en", "Nūll Island", "Ñüłl Íşlåńđ", "Ñüłl Íşlåńđ"); + expectGloss("en", "New York City", "New York", "New York City"); + expectGloss("en", "Washington, D.C.", "Washington", "Washington, D.C."); expectGloss( "en", "Santiago de Querétaro", "Querétaro", - "Santiago de Querétaro", - null + "Santiago de Querétaro" ); // Suboptimal but expected cases @@ -369,4 +363,169 @@ describe("label", function () { expectGloss("pl", "Jurmała", "Jūrmala", "Jurmała", "Jūrmala"); }); }); + + describe("#replaceExpression", function () { + let evaluatedExpression = ( + haystack, + needle, + replacement, + haystackStart, + numReplacements + ) => + expression + .createExpression( + localizedTextField( + [ + ...Label.replaceExpression( + haystack, + needle, + replacement, + haystackStart, + numReplacements + ), + ], + ["en"] + ) + ) + .value.expression.evaluate(expressionContext({})); + + it("returns the haystack verbatim when there is nothing to replace", function () { + expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, -1)).to.be.eql( + "ABC;DEF;GHI" + ); + expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, 0)).to.be.eql( + "ABC;DEF;GHI" + ); + }); + + it("returns an empty haystack verbatim", function () { + expect(evaluatedExpression("", ";", "*", 0, -1)).to.be.eql(""); + expect(evaluatedExpression("", ";", "*", 0, 0)).to.be.eql(""); + expect(evaluatedExpression("", ";", "*", 0, 1)).to.be.eql(""); + expect(evaluatedExpression("", ";", "*", 0, 2)).to.be.eql(""); + }); + + it("replaces one occurrence", function () { + expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, 1)).to.be.eql( + "ABC*DEF;GHI" + ); + }); + + it("replaces multiple occurrences", function () { + expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, 2)).to.be.eql( + "ABC*DEF*GHI" + ); + expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, 3)).to.be.eql( + "ABC*DEF*GHI" + ); + expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, 10)).to.be.eql( + "ABC*DEF*GHI" + ); + }); + + it("replaces adjacent occurrences", function () { + expect(evaluatedExpression("ABC;;;DEF;GHI", ";", "*", 0, 2)).to.be.eql( + "ABC**;DEF;GHI" + ); + }); + + it("replaces at the beginning of the haystack", function () { + expect(evaluatedExpression(";DEF;GHI", ";", "*", 0, 1)).to.be.eql( + "*DEF;GHI" + ); + }); + + it("replaces at the end of the haystack", function () { + expect(evaluatedExpression("ABC;", ";", "*", 0, 1)).to.be.eql("ABC*"); + }); + + it("replaces the whole haystack", function () { + expect(evaluatedExpression(";", ";", "*", 0, 1)).to.be.eql("*"); + expect(evaluatedExpression(";;;", ";", "*", 0, 3)).to.be.eql("***"); + }); + + it("is case-sensitive", function () { + expect(evaluatedExpression("ABC", "b", "*", 0, 1)).to.be.eql("ABC"); + }); + + it("replaces multiple characters", function () { + expect(evaluatedExpression("ABC;;DEF", ";;", "/", 0, 1)).to.be.eql( + "ABC/DEF" + ); + }); + + it("replaces needle expression", function () { + expect( + evaluatedExpression("ABC;DEF", ["concat", ";"], "*", 0, 1) + ).to.be.eql("ABC*DEF"); + }); + + it("replaces replacement expression", function () { + expect( + evaluatedExpression("ABC;DEF", ";", ["slice", "*", 0], 0, 1) + ).to.be.eql("ABC*DEF"); + }); + }); + + describe("#listValuesExpression", function () { + let evaluatedExpression = (valueList, separator) => + expression + .createExpression( + localizedTextField( + [...Label.listValuesExpression(valueList, separator)], + ["en"] + ) + ) + .value.expression.evaluate(expressionContext({})); + + it("lists an empty list", function () { + expect(evaluatedExpression("", ", ")).to.be.eql(""); + }); + + it("lists a single value", function () { + expect(evaluatedExpression("ABC", ", ")).to.be.eql("ABC"); + }); + + it("lists empty values", function () { + expect(evaluatedExpression(";", ", ")).to.be.eql(", "); + }); + + it("lists multiple values", function () { + expect(evaluatedExpression("ABC;DEF", ", ")).to.be.eql("ABC, DEF"); + expect(evaluatedExpression("ABC;DEF;GHI", ", ")).to.be.eql( + "ABC, DEF, GHI" + ); + }); + + it("ignores an escaped semicolon", function () { + expect(evaluatedExpression("ABC;;DEF", ", ")).to.be.eql("ABC;DEF"); + expect(evaluatedExpression("ABC;;DEF;GHI", ", ")).to.be.eql( + "ABC;DEF, GHI" + ); + expect(evaluatedExpression("ABC;DEF;;GHI", ", ")).to.be.eql( + "ABC, DEF;GHI" + ); + expect(evaluatedExpression("ABC;;DEF;;GHI", ", ")).to.be.eql( + "ABC;DEF;GHI" + ); + }); + + it("lists a maximum number of values", function () { + // https://www.openstreetmap.org/node/9816809799 + expect( + evaluatedExpression( + "马岔河村;菜园村;刘灿东村;后于口村;王石楼村;李岔河村;岔河新村;富康新村;前鱼口村", + "、" + ) + ).to.be.eql( + "马岔河村、菜园村、刘灿东村、后于口村、王石楼村、李岔河村、岔河新村、富康新村、前鱼口村" + ); + expect( + evaluatedExpression( + "one;two;three;four;five;six;seven;eight;nine;ten", + ", " + ) + ).to.be.eql("one, two, three, four, five, six, seven, eight, nine;ten"); + }); + }); }); From 2f96926aae7cbaf85d24a04ccf5edfbaa8eac6b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Wed, 4 Jan 2023 15:45:36 -0800 Subject: [PATCH 153/514] Format highway exit number lists --- src/layer/highway_exit.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/layer/highway_exit.js b/src/layer/highway_exit.js index 6f995becc..00a834a38 100644 --- a/src/layer/highway_exit.js +++ b/src/layer/highway_exit.js @@ -1,5 +1,7 @@ "use strict"; +import * as Label from "../constants/label.js"; + export const exits = { id: "highway_exit", type: "symbol", @@ -12,7 +14,7 @@ export const exits = { "source-layer": "transportation_name", minzoom: 14, layout: { - "text-field": ["get", "ref"], + "text-field": Label.listValuesExpression(["get", "ref"], "\n"), "text-font": ["OpenHistorical Bold"], "text-size": 9, "text-line-height": 1, From cf6c3511bb1c300559f063bf98cf98dedcac5fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 6 Jan 2023 00:20:03 -0800 Subject: [PATCH 154/514] Limit to 3 values separated by 2 semicolons --- src/constants/label.js | 2 +- test/spec/label.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/constants/label.js b/src/constants/label.js index 13c59b0ea..0645e51bd 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -175,7 +175,7 @@ export function replaceExpression( * Increasing this constant deepens recursion for replacing delimiters in the * list, potentially affecting style loading performance. */ -const maxValueListLength = 9; +const maxValueListLength = 3; /** * Returns an expression interpreting the given string as a list of tag values, diff --git a/test/spec/label.js b/test/spec/label.js index 4c97e628e..37ed46d5d 100644 --- a/test/spec/label.js +++ b/test/spec/label.js @@ -518,14 +518,14 @@ describe("label", function () { "、" ) ).to.be.eql( - "马岔河村、菜园村、刘灿东村、后于口村、王石楼村、李岔河村、岔河新村、富康新村、前鱼口村" + "马岔河村、菜园村、刘灿东村;后于口村;王石楼村;李岔河村;岔河新村;富康新村;前鱼口村" ); expect( evaluatedExpression( "one;two;three;four;five;six;seven;eight;nine;ten", ", " ) - ).to.be.eql("one, two, three, four, five, six, seven, eight, nine;ten"); + ).to.be.eql("one, two, three;four;five;six;seven;eight;nine;ten"); }); }); }); From 8847cf6ac157ba195187d0ba515ef73c6c324294 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Fri, 6 Jan 2023 09:57:51 -0500 Subject: [PATCH 155/514] adjust escutcheon shield metrics --- src/js/shield_canvas_draw.js | 6 +++--- src/js/shield_defs.js | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/js/shield_canvas_draw.js b/src/js/shield_canvas_draw.js index d2bc654d9..27b390ca4 100644 --- a/src/js/shield_canvas_draw.js +++ b/src/js/shield_canvas_draw.js @@ -218,11 +218,11 @@ export function escutcheon( let y1 = y0 + drawRadius; let y2 = y5 - drawOffset; - let x2 = (x0 + x3) / 2; - let x4 = (x3 + x5) / 2; + let x2 = (2 * x0 + x3) / 3; + let x4 = (x3 + 2 * x5) / 3; let y3 = (y2 + y5) / 2; - let y4 = (y3 + 3 * y5) / 4; + let y4 = (y3 + 2 * y5) / 3; ctx.beginPath(); ctx.moveTo(x3, y5); diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 3b540606b..f5d3ef01e 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -2663,12 +2663,12 @@ export function loadShields(shieldImages) { // Puerto Rico shields["US:PR:primary"] = escutcheonDownShield( - 10, + 12, Color.shields.blue, Color.shields.white ); shields["US:PR:primary_urban"] = escutcheonDownShield( - 10, + 12, Color.shields.white, Color.shields.black ); @@ -2972,7 +2972,7 @@ export function loadShields(shieldImages) { // Virginia shields["US:VA"] = escutcheonDownShield( - 10, + 12, Color.shields.white, Color.shields.black, Color.shields.black, @@ -3391,7 +3391,7 @@ export function loadShields(shieldImages) { // Hong Kong shields["HK"] = escutcheonDownShield( - 10, + 12, Color.shields.yellow, Color.shields.black ); From f5debab1da16f7df36c724dca1159664abc357c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 6 Jan 2023 17:18:54 -0800 Subject: [PATCH 156/514] Streamlined value list parsing Replaced the general-purpose find-and-replace expression-generating function with a special-purpose string scanner that can only look for semicolons but also accounts for escaping and space padding in the same pass. --- src/constants/label.js | 118 ++++++++++++++++++++--------------------- test/spec/label.js | 113 ++++----------------------------------- 2 files changed, 68 insertions(+), 163 deletions(-) diff --git a/src/constants/label.js b/src/constants/label.js index 0645e51bd..1d42a666d 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -117,9 +117,8 @@ export function localizeLayers(layers, locales) { } /** - * Returns an expression that replaces a finite number of occurrences of a - * substring expression withing a larger string expression, starting at a given - * index. + * Recursively scans a semicolon-delimited value list, replacing a finite number + * of semicolons with a separator, starting from the given index. * * This expression nests recursively by the maximum number of replacements. Take * special care to minimize this limit, which exponentially increases the length @@ -130,42 +129,67 @@ export function localizeLayers(layers, locales) { * value. To reuse the evaluated value, bind it to a variable in a let * expression. * - * @param haystack The overall string expression to search within. - * @param needle The string to search for, or an expression that evaluates to - * this string. + * @param list The overall string expression to search within. + * @param separator A string to insert after the value, or an expression that + * evaluates to this string. + * @param listStart A zero-based index into the list at which the search begins. + * @param numReplacements The maximum number of replacements remaining. */ -export function replaceExpression( - haystack, - needle, - replacement, - haystackStart, - numReplacements = 1 -) { - let asIs = ["slice", haystack, haystackStart]; +function listValueExpression(list, separator, listStart, numReplacements) { + let asIs = ["slice", list, listStart]; if (numReplacements <= 0) { return asIs; } - let needleStart = ["index-of", needle, haystack, haystackStart]; - let needleLength = - typeof needle === "object" ? ["length", needle] : needle.length; - let needleEnd = ["+", needleStart, needleLength]; + let iteration = numReplacements; return [ - "case", - [">=", needleStart, 0], + "let", + "needleStart" + iteration, + ["index-of", ";", list, listStart], + "needleEnd" + iteration, + ["+", ["index-of", ";", list, listStart], 1], [ - "concat", - ["slice", haystack, haystackStart, needleStart], - replacement, - replaceExpression( - haystack, - needle, - replacement, - needleEnd, - numReplacements - 1 - ), + "case", + [">=", ["var", "needleStart" + iteration], 0], + // Found a semicolon. + [ + "concat", + // Start with everything before the semicolon. + ["slice", list, listStart, ["var", "needleStart" + iteration]], + [ + "let", + "lookahead" + iteration, + // Look ahead by one character. + [ + "slice", + list, + ["var", "needleEnd" + iteration], + ["+", ["var", "needleEnd" + iteration], 1], + ], + [ + "concat", + // If the lookahead character is another semicolon, append an unescaped semicolon. + // Otherwise, append the passed-in separator. + ["match", ["var", "lookahead" + iteration], ";", ";", separator], + // Recurse for the next value in the value list. + listValueExpression( + list, + separator, + // Skip past the current value and semicolon for any subsequent searches. + [ + "+", + ["var", "needleEnd" + iteration], + // Also skip past any escaped semicolon or space padding. + ["match", ["var", "lookahead" + iteration], [";", " "], 1, 0], + ], + numReplacements - 1 + ), + ], + ], + ], + // No semicolons left in the string, so stop looking. + asIs, ], - asIs, ]; } @@ -193,37 +217,11 @@ const maxValueListLength = 3; */ export function listValuesExpression(valueList, separator) { let maxSeparators = maxValueListLength - 1; - // Replace the ;; escape sequence with a placeholder sequence unlikely to - // legitimately occur inside a value or separator. - const objReplacementChar = "\x91\ufffc\x92"; // https://overpass-turbo.eu/s/1pJx - let safeValueList = replaceExpression( - valueList, - ";;", - objReplacementChar, - 0, - maxSeparators - ); - // Pretty-print the ; delimiter. - let prettyValueList = replaceExpression( - ["var", "safeValueList"], - ";", - separator, - 0, - maxSeparators - ); - // Replace the placeholder sequence with an unescaped semicolon. - let prettySafeValueList = replaceExpression( - ["var", "prettyValueList"], - objReplacementChar, - ";", - 0, - maxSeparators - ); return [ "let", - "safeValueList", - safeValueList, - ["let", "prettyValueList", prettyValueList, prettySafeValueList], + "valueList", + valueList, + listValueExpression(["var", "valueList"], separator, 0, maxSeparators), ]; } diff --git a/test/spec/label.js b/test/spec/label.js index 37ed46d5d..962567a99 100644 --- a/test/spec/label.js +++ b/test/spec/label.js @@ -364,109 +364,6 @@ describe("label", function () { }); }); - describe("#replaceExpression", function () { - let evaluatedExpression = ( - haystack, - needle, - replacement, - haystackStart, - numReplacements - ) => - expression - .createExpression( - localizedTextField( - [ - ...Label.replaceExpression( - haystack, - needle, - replacement, - haystackStart, - numReplacements - ), - ], - ["en"] - ) - ) - .value.expression.evaluate(expressionContext({})); - - it("returns the haystack verbatim when there is nothing to replace", function () { - expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, -1)).to.be.eql( - "ABC;DEF;GHI" - ); - expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, 0)).to.be.eql( - "ABC;DEF;GHI" - ); - }); - - it("returns an empty haystack verbatim", function () { - expect(evaluatedExpression("", ";", "*", 0, -1)).to.be.eql(""); - expect(evaluatedExpression("", ";", "*", 0, 0)).to.be.eql(""); - expect(evaluatedExpression("", ";", "*", 0, 1)).to.be.eql(""); - expect(evaluatedExpression("", ";", "*", 0, 2)).to.be.eql(""); - }); - - it("replaces one occurrence", function () { - expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, 1)).to.be.eql( - "ABC*DEF;GHI" - ); - }); - - it("replaces multiple occurrences", function () { - expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, 2)).to.be.eql( - "ABC*DEF*GHI" - ); - expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, 3)).to.be.eql( - "ABC*DEF*GHI" - ); - expect(evaluatedExpression("ABC;DEF;GHI", ";", "*", 0, 10)).to.be.eql( - "ABC*DEF*GHI" - ); - }); - - it("replaces adjacent occurrences", function () { - expect(evaluatedExpression("ABC;;;DEF;GHI", ";", "*", 0, 2)).to.be.eql( - "ABC**;DEF;GHI" - ); - }); - - it("replaces at the beginning of the haystack", function () { - expect(evaluatedExpression(";DEF;GHI", ";", "*", 0, 1)).to.be.eql( - "*DEF;GHI" - ); - }); - - it("replaces at the end of the haystack", function () { - expect(evaluatedExpression("ABC;", ";", "*", 0, 1)).to.be.eql("ABC*"); - }); - - it("replaces the whole haystack", function () { - expect(evaluatedExpression(";", ";", "*", 0, 1)).to.be.eql("*"); - expect(evaluatedExpression(";;;", ";", "*", 0, 3)).to.be.eql("***"); - }); - - it("is case-sensitive", function () { - expect(evaluatedExpression("ABC", "b", "*", 0, 1)).to.be.eql("ABC"); - }); - - it("replaces multiple characters", function () { - expect(evaluatedExpression("ABC;;DEF", ";;", "/", 0, 1)).to.be.eql( - "ABC/DEF" - ); - }); - - it("replaces needle expression", function () { - expect( - evaluatedExpression("ABC;DEF", ["concat", ";"], "*", 0, 1) - ).to.be.eql("ABC*DEF"); - }); - - it("replaces replacement expression", function () { - expect( - evaluatedExpression("ABC;DEF", ";", ["slice", "*", 0], 0, 1) - ).to.be.eql("ABC*DEF"); - }); - }); - describe("#listValuesExpression", function () { let evaluatedExpression = (valueList, separator) => expression @@ -495,6 +392,8 @@ describe("label", function () { expect(evaluatedExpression("ABC;DEF;GHI", ", ")).to.be.eql( "ABC, DEF, GHI" ); + expect(evaluatedExpression(";ABC;DEF", ", ")).to.be.eql(", ABC, DEF"); + expect(evaluatedExpression("ABC;DEF;", ", ")).to.be.eql("ABC, DEF, "); }); it("ignores an escaped semicolon", function () { @@ -508,6 +407,14 @@ describe("label", function () { expect(evaluatedExpression("ABC;;DEF;;GHI", ", ")).to.be.eql( "ABC;DEF;GHI" ); + expect(evaluatedExpression("ABC;;;DEF", ", ")).to.be.eql("ABC;, DEF"); + expect(evaluatedExpression("ABC;;;;DEF", ", ")).to.be.eql("ABC;;DEF"); + }); + + it("accepts an expression as the separator", function () { + expect(evaluatedExpression("ABC;DEF", ["concat", ", "])).to.be.eql( + "ABC, DEF" + ); }); it("lists a maximum number of values", function () { From 7ce0b268ec54277831c350edd8dd39ea68bc80c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 6 Jan 2023 18:37:09 -0800 Subject: [PATCH 157/514] Omit redundant name in local-language gloss --- src/constants/label.js | 118 +++++++++++++++++++++++++++++++---------- test/spec/label.js | 23 +++++++- 2 files changed, 111 insertions(+), 30 deletions(-) diff --git a/src/constants/label.js b/src/constants/label.js index 1d42a666d..573fe5c77 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -135,59 +135,110 @@ export function localizeLayers(layers, locales) { * @param listStart A zero-based index into the list at which the search begins. * @param numReplacements The maximum number of replacements remaining. */ -function listValueExpression(list, separator, listStart, numReplacements) { +function listValueExpression( + list, + separator, + valueToOmit, + listStart, + numReplacements +) { let asIs = ["slice", list, listStart]; if (numReplacements <= 0) { return asIs; } let iteration = numReplacements; + let rawSeparator = ";"; return [ "let", "needleStart" + iteration, - ["index-of", ";", list, listStart], - "needleEnd" + iteration, - ["+", ["index-of", ";", list, listStart], 1], + ["index-of", rawSeparator, list, listStart], [ "case", [">=", ["var", "needleStart" + iteration], 0], // Found a semicolon. [ - "concat", - // Start with everything before the semicolon. + "let", + "value" + iteration, ["slice", list, listStart, ["var", "needleStart" + iteration]], + "needleEnd" + iteration, + ["+", ["var", "needleStart" + iteration], rawSeparator.length], [ - "let", - "lookahead" + iteration, - // Look ahead by one character. + "concat", + // Start with everything before the semicolon unless it's the value to + // omit. [ - "slice", - list, - ["var", "needleEnd" + iteration], - ["+", ["var", "needleEnd" + iteration], 1], + "case", + ["==", ["var", "value" + iteration], valueToOmit], + "", + ["var", "value" + iteration], ], [ - "concat", - // If the lookahead character is another semicolon, append an unescaped semicolon. - // Otherwise, append the passed-in separator. - ["match", ["var", "lookahead" + iteration], ";", ";", separator], - // Recurse for the next value in the value list. - listValueExpression( + "let", + "lookahead" + iteration, + // Look ahead by one character. + [ + "slice", list, - separator, - // Skip past the current value and semicolon for any subsequent searches. + ["var", "needleEnd" + iteration], + ["+", ["var", "needleEnd" + iteration], rawSeparator.length], + ], + [ + "let", + // Skip past the current value and semicolon for any subsequent + // searches. + "nextListStart" + iteration, [ "+", ["var", "needleEnd" + iteration], // Also skip past any escaped semicolon or space padding. - ["match", ["var", "lookahead" + iteration], [";", " "], 1, 0], + [ + "match", + ["var", "lookahead" + iteration], + [rawSeparator, " "], + rawSeparator.length, + 0, + ], ], - numReplacements - 1 - ), + [ + "case", + // If the only remaining value is the value to omit, stop + // scanning. + [ + "==", + ["slice", list, ["var", "nextListStart" + iteration]], + valueToOmit, + ], + "", + [ + "concat", + [ + "case", + // If the lookahead character is another semicolon, append + // an unescaped semicolon. + ["==", ["var", "lookahead" + iteration], rawSeparator], + rawSeparator, + // Otherwise, if the value is the value to omit, do nothing. + ["==", ["var", "value" + iteration], valueToOmit], + "", + // Otherwise, append the passed-in separator. + separator, + ], + // Recurse for the next value in the value list. + listValueExpression( + list, + separator, + valueToOmit, + ["var", "nextListStart" + iteration], + numReplacements - 1 + ), + ], + ], + ], ], ], ], - // No semicolons left in the string, so stop looking. + // No semicolons left in the string, so stop looking and append the value as is. asIs, ], ]; @@ -215,13 +266,21 @@ const maxValueListLength = 3; * @param separator A string to insert between each value, or an expression that * evaluates to this string. */ -export function listValuesExpression(valueList, separator) { +export function listValuesExpression(valueList, separator, valueToOmit) { let maxSeparators = maxValueListLength - 1; return [ "let", "valueList", valueList, - listValueExpression(["var", "valueList"], separator, 0, maxSeparators), + "valueToOmit", + valueToOmit || ";", + listValueExpression( + ["var", "valueList"], + separator, + ["var", "valueToOmit"], + 0, + maxSeparators + ), ]; } @@ -383,7 +442,10 @@ export const localizedNameWithLocalGloss = [ // bother rendering it. ["concat", ["slice", ["var", "localizedName"], 0, 1], " "], { "font-scale": 0.001 }, - listValuesExpression(["get", "name"], " \u2022 "), + listValuesExpression(["get", "name"], " \u2022 ", [ + "var", + "localizedName", + ]), { "font-scale": 0.8 }, ["concat", " ", ["slice", ["var", "localizedName"], 0, 1]], { "font-scale": 0.001 }, diff --git a/test/spec/label.js b/test/spec/label.js index 962567a99..1f267a59a 100644 --- a/test/spec/label.js +++ b/test/spec/label.js @@ -365,11 +365,11 @@ describe("label", function () { }); describe("#listValuesExpression", function () { - let evaluatedExpression = (valueList, separator) => + let evaluatedExpression = (valueList, separator, valueToOmit) => expression .createExpression( localizedTextField( - [...Label.listValuesExpression(valueList, separator)], + [...Label.listValuesExpression(valueList, separator, valueToOmit)], ["en"] ) ) @@ -396,6 +396,10 @@ describe("label", function () { expect(evaluatedExpression("ABC;DEF;", ", ")).to.be.eql("ABC, DEF, "); }); + it("ignores a space after a semicolon", function () { + expect(evaluatedExpression("ABC; DEF", ", ")).to.be.eql("ABC, DEF"); + }); + it("ignores an escaped semicolon", function () { expect(evaluatedExpression("ABC;;DEF", ", ")).to.be.eql("ABC;DEF"); expect(evaluatedExpression("ABC;;DEF;GHI", ", ")).to.be.eql( @@ -434,5 +438,20 @@ describe("label", function () { ) ).to.be.eql("one, two, three;four;five;six;seven;eight;nine;ten"); }); + + it("omits a specified value", function () { + expect(evaluatedExpression("ABC;DEF;GHI", ", ", "")).to.be.eql( + "ABC, DEF, GHI" + ); + expect(evaluatedExpression("ABC;DEF;GHI", ", ", "ABC")).to.be.eql( + "DEF, GHI" + ); + expect(evaluatedExpression("ABC;DEF;GHI", ", ", "DEF")).to.be.eql( + "ABC, GHI" + ); + expect(evaluatedExpression("ABC;DEF;GHI", ", ", "GHI")).to.be.eql( + "ABC, DEF" + ); + }); }); }); From 2b35d9a45957da07c2fa255665429ad55bb0a72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 6 Jan 2023 18:37:35 -0800 Subject: [PATCH 158/514] Add local-language gloss to towns --- src/layer/place.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layer/place.js b/src/layer/place.js index 45cd8cd42..deae77f8c 100644 --- a/src/layer/place.js +++ b/src/layer/place.js @@ -135,7 +135,7 @@ export const town = { [11, 0.7], ], }, - "text-field": Label.localizedName, + "text-field": Label.localizedNameWithLocalGloss, "text-anchor": "bottom", "text-variable-anchor": [ "bottom", From 02cd1bc26da68694edc06f5f56f979392ec22bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 6 Jan 2023 23:01:54 -0800 Subject: [PATCH 159/514] Factored out constant for inline separator --- src/constants/label.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/constants/label.js b/src/constants/label.js index 573fe5c77..46c998aea 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -294,6 +294,11 @@ export const localizedName = [ listValuesExpression(["var", "localizedName"], "\n"), ]; +/** + * The separator to use in inline contexts. + */ +const inlineSeparator = " \u2022 "; + /** * The names in the user's preferred language, all on the same line. */ @@ -301,7 +306,7 @@ export const localizedNameInline = [ "let", "localizedName", "", - listValuesExpression(["var", "localizedName"], " \u2022 "), + listValuesExpression(["var", "localizedName"], inlineSeparator), ]; /** @@ -442,7 +447,7 @@ export const localizedNameWithLocalGloss = [ // bother rendering it. ["concat", ["slice", ["var", "localizedName"], 0, 1], " "], { "font-scale": 0.001 }, - listValuesExpression(["get", "name"], " \u2022 ", [ + listValuesExpression(["get", "name"], inlineSeparator, [ "var", "localizedName", ]), From 1b5630ac99a8e4cde140a4d77500fca59bb34c2f Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sat, 7 Jan 2023 11:47:52 -0500 Subject: [PATCH 160/514] Refactore locale hook --- src/americana.js | 10 +--------- src/layer/index.js | 6 +++++- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/americana.js b/src/americana.js index 86cea8f21..b0fc09cdf 100644 --- a/src/americana.js +++ b/src/americana.js @@ -20,14 +20,6 @@ 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"; -function buildLayers() { - let layers = Layers.build(); - - Label.localizeLayers(layers, Label.getLocales()); - - return layers; -} - export function buildStyle() { var getUrl = window.location; var baseUrl = getUrl.protocol + "//" + getUrl.host + getUrl.pathname; @@ -37,7 +29,7 @@ export function buildStyle() { name: "Americana", glyphs: "https://openhistoricalmap.github.io/map-styles/fonts/{fontstack}/{range}.pbf", - layers: buildLayers(), + layers: Layers.build(Label.getLocales()), sources: { openmaptiles: { url: config.OPENMAPTILES_URL, diff --git a/src/layer/index.js b/src/layer/index.js index 14d6c5e84..e70fc1863 100644 --- a/src/layer/index.js +++ b/src/layer/index.js @@ -1,5 +1,7 @@ "use strict"; +import * as Label from "../constants/label.js"; + import * as lyrAeroway from "./aeroway.js"; import * as lyrBackground from "./background.js"; import * as lyrBoundary from "./boundary.js"; @@ -21,7 +23,7 @@ import * as lyrFerry from "./ferry.js"; * Builds the Americana layers property without localization. * See: https://maplibre.org/maplibre-gl-js-docs/style-spec/layers/ */ -export function build() { +export function build(locales) { // Layers from bottom to top let layers = []; @@ -227,5 +229,7 @@ export function build() { lyrPlace.continent ); + Label.localizeLayers(layers, locales); + return layers; } From 7b0fea1e76244d5daab37abb14bbddcbcca193b2 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sat, 7 Jan 2023 12:18:58 -0500 Subject: [PATCH 161/514] Update comment --- src/layer/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layer/index.js b/src/layer/index.js index e70fc1863..8276ac008 100644 --- a/src/layer/index.js +++ b/src/layer/index.js @@ -20,7 +20,7 @@ import * as lyrHighwayExit from "./highway_exit.js"; import * as lyrFerry from "./ferry.js"; /** - * Builds the Americana layers property without localization. + * Builds the Americana layers property. * See: https://maplibre.org/maplibre-gl-js-docs/style-spec/layers/ */ export function build(locales) { From dab92e8e7cd4313768669a88488cea316d4ffd95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 7 Jan 2023 11:13:26 -0800 Subject: [PATCH 162/514] Fixed conversion from fill-opacity to CSS Unmultiply RGB components by the alpha component. If fill-opacity is unspecified, use fill-color directly in CSS. --- src/js/legend_control.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/js/legend_control.js b/src/js/legend_control.js index dddf60b7a..2620d4578 100644 --- a/src/js/legend_control.js +++ b/src/js/legend_control.js @@ -340,15 +340,14 @@ export default class LegendControl { let fillColor = fill?.layer.paint["fill-color"] || fill?.layer.paint["fill-extrusion-color"]; - if (fillColor) { - let opacity = - fill?.layer.paint["fill-opacity"] ?? - fill?.layer.paint["fill-extrusion-opacity"] ?? - fillColor.a ?? - 1; - fillColor = `rgba(${fillColor.r * 255}, ${fillColor.g * 255}, ${ - fillColor.b * 255 - }, ${opacity})`; + let fillOpacity = + fill?.layer.paint["fill-opacity"] ?? + fill?.layer.paint["fill-extrusion-opacity"]; + if (fillColor && fillOpacity) { + let opacity = fillOpacity ?? fillColor.a ?? 1; + fillColor = `rgba(${(fillColor.r * 255) / opacity}, ${ + (fillColor.g * 255) / opacity + }, ${(fillColor.b * 255) / opacity}, ${opacity})`; } let borderStyle = "solid"; if (stroke?.layer.paint["line-dasharray"]) { From c7b5aad64315c70bb49c5eb4a2f20e00ef02ccd2 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Sat, 7 Jan 2023 14:17:07 -0500 Subject: [PATCH 163/514] reorder shields on test page --- src/shieldtest.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/shieldtest.js b/src/shieldtest.js index 40805a1a3..834faf8f6 100644 --- a/src/shieldtest.js +++ b/src/shieldtest.js @@ -43,6 +43,7 @@ let networks = [ "CA:ON:secondary", "US:NE", "US:MN:Hennepin:Park_Access", + "CA:MB:Winnipeg", "US:PA", "US:PA:Turnpike", @@ -80,31 +81,35 @@ let networks = [ // Basic Rounded Shapes "US:GU", - "US:HI", "US:CA", - - "US:OR", + "US:UT", + "US:HI", "JP:national", "TW:expressway", "US:TX:Fort_Bend:FBCTRA", + "US:OR", + "US:VA", "US:PR:primary", "HK", - "PK:motorway", + "CA:QC:A", + "CA:NS:H", + "NZ:SH", "AU:WA:S", - // Basic shapes with more detail "US:I", "US:I:Business:Loop", - - "CA:QC:A", - "CA:NS:H", + "co:national", + "US:BIA", + "US:MT:secondary", "CA:PE", "US:FL:Toll", "CA:BC", + "PK:motorway", + "US:US", "US:US:Historic", "CL:national", @@ -113,15 +118,10 @@ let networks = [ "KR:expressway", "CA:QC:R", "VE:T:AM", - "co:national", - "US:BIA", - "US:MT:secondary", + "US:WI", "TW:freeway", - "US:UT", "US:MP", - "US:WI", - "CA:MB:Winnipeg", "US:AS", // Fancy Rectangles From b588abef245a316f6ea8f76e6539af0647a96e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 7 Jan 2023 11:17:48 -0800 Subject: [PATCH 164/514] Fix conversion of fill-extrusion-opacity to CSS Unlike fill-opacity, fill-extrusion-opacity affects the whole feature, not just its fill. --- src/js/legend_control.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/js/legend_control.js b/src/js/legend_control.js index 2620d4578..3948a943a 100644 --- a/src/js/legend_control.js +++ b/src/js/legend_control.js @@ -340,9 +340,7 @@ export default class LegendControl { let fillColor = fill?.layer.paint["fill-color"] || fill?.layer.paint["fill-extrusion-color"]; - let fillOpacity = - fill?.layer.paint["fill-opacity"] ?? - fill?.layer.paint["fill-extrusion-opacity"]; + let fillOpacity = fill?.layer.paint["fill-opacity"]; if (fillColor && fillOpacity) { let opacity = fillOpacity ?? fillColor.a ?? 1; fillColor = `rgba(${(fillColor.r * 255) / opacity}, ${ @@ -364,6 +362,7 @@ export default class LegendControl { stroke?.layer.paint["line-color"] || fillColor || "transparent", borderStyle: borderStyle, borderWidth: `${stroke?.layer.paint["line-width"] ?? 1}px`, + opacity: fill?.layer.paint["fill-extrusion-opacity"] ?? 1, }; } From 83d975a1af35a8fe89280cebdb1a287caa516d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 6 Jan 2023 01:21:19 -0800 Subject: [PATCH 165/514] Use inline delimiter in waterway labels --- src/layer/water.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layer/water.js b/src/layer/water.js index 89219623f..611849e8b 100644 --- a/src/layer/water.js +++ b/src/layer/water.js @@ -136,7 +136,7 @@ const labelPaintProperties = { const labelLayoutProperties = { "symbol-placement": "line", - "text-field": Label.localizedName, + "text-field": Label.localizedNameInline, "text-font": ["OpenHistorical Italic"], "text-max-angle": 55, }; From 6a9d47ab2e1eb1afbce34b95915107c936bcfd93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 7 Jan 2023 17:51:14 -0800 Subject: [PATCH 166/514] Split localized name on semicolons before gloss --- src/constants/label.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants/label.js b/src/constants/label.js index 46c998aea..a42b9e309 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -437,7 +437,7 @@ export const localizedNameWithLocalGloss = [ // localized name. [ "format", - ["var", "localizedName"], + listValuesExpression(["var", "localizedName"], "\n"), "\n", "(\u200B", { "font-scale": 0.8 }, From d19d2935f94bf2ee97469d85f9def54337f839c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sun, 8 Jan 2023 10:32:43 -0800 Subject: [PATCH 167/514] Refactored glossed label expression to eliminate repetition --- src/constants/label.js | 176 +++++++++++++++++++++++------------------ 1 file changed, 97 insertions(+), 79 deletions(-) diff --git a/src/constants/label.js b/src/constants/label.js index a42b9e309..86e9763f9 100644 --- a/src/constants/label.js +++ b/src/constants/label.js @@ -350,22 +350,27 @@ function overwritePrefixExpression(target, newPrefix) { function endsWithExpression(target, candidateSuffix, collator) { let wordBoundary = " "; return [ - "all", - [ - "==", - ["slice", target, ["-", ["length", target], ["length", candidateSuffix]]], - candidateSuffix, - collator, - ], + "let", + "suffixStart", + ["-", ["length", target], ["length", candidateSuffix]], [ - "==", + "all", [ - "slice", - target, - ["-", ["-", ["length", target], ["length", candidateSuffix]], 1], - ["-", ["length", target], ["length", candidateSuffix]], + "==", + ["slice", target, ["var", "suffixStart"]], + candidateSuffix, + collator, + ], + [ + "==", + [ + "slice", + target, + ["-", ["var", "suffixStart"], 1], + ["var", "suffixStart"], + ], + wordBoundary, ], - wordBoundary, ], ]; } @@ -391,74 +396,87 @@ export const localizedNameWithLocalGloss = [ "diacriticInsensitiveCollator", ["collator", {}], [ - "case", - // If the name in the preferred and local languages match exactly... - [ - "==", - ["var", "localizedName"], - ["get", "name"], - ["var", "localizedCollator"], - ], - // ...just pick one. - ["format", listValuesExpression(["var", "localizedName"], "\n")], - // If the name in the preferred language is the same as the name in the - // local language except for the omission of diacritics and/or the addition - // of a suffix (e.g., "City" in English)... - startsWithExpression( - ["var", "localizedName"], - ["get", "name"], - ["var", "diacriticInsensitiveCollator"] - ), - // ...then replace the common prefix with the local name. - [ - "format", - overwritePrefixExpression( - ["var", "localizedName"], - listValuesExpression(["get", "name"], "\n") - ), - ], - // If the name in the preferred language is the same as the name in the - // local language except for the omission of diacritics and/or the addition - // of a prefix (e.g., "City of" in English or "Ciudad de" in Spanish)... - endsWithExpression( - ["var", "localizedName"], - ["get", "name"], - ["var", "diacriticInsensitiveCollator"] - ), - // ...then replace the common suffix with the local name. + "let", + "localizedNameList", + listValuesExpression(["var", "localizedName"], "\n"), [ - "format", - overwriteSuffixExpression( + "case", + // If the name in the preferred and local languages match exactly... + [ + "==", ["var", "localizedName"], - listValuesExpression(["get", "name"], "\n") - ), - ], - // Otherwise, gloss the name in the local language if it differs from the - // localized name. - [ - "format", - listValuesExpression(["var", "localizedName"], "\n"), - "\n", - "(\u200B", - { "font-scale": 0.8 }, - // GL JS lacks support for bidirectional isolating characters, so use a - // character from the localized name to insulate the parentheses from the - // embedded text's writing direction. Make it so small that GL JS doesn't - // bother rendering it. - ["concat", ["slice", ["var", "localizedName"], 0, 1], " "], - { "font-scale": 0.001 }, - listValuesExpression(["get", "name"], inlineSeparator, [ - "var", - "localizedName", - ]), - { "font-scale": 0.8 }, - ["concat", " ", ["slice", ["var", "localizedName"], 0, 1]], - { "font-scale": 0.001 }, - // A ZWSP prevents GL JS from combining this component with the preceding - // one, which would cause it to vanish along with the faux isolating - // character. - "\u200B)", - { "font-scale": 0.8 }, + ["get", "name"], + ["var", "localizedCollator"], + ], + // ...just pick one. + ["format", ["var", "localizedNameList"]], + [ + "let", + "nameList", + listValuesExpression(["get", "name"], "\n"), + [ + "case", + // If the name in the preferred language is the same as the name in the + // local language except for the omission of diacritics and/or the addition + // of a suffix (e.g., "City" in English)... + startsWithExpression( + ["var", "localizedName"], + ["get", "name"], + ["var", "diacriticInsensitiveCollator"] + ), + // ...then replace the common prefix with the local name. + [ + "format", + overwritePrefixExpression( + ["var", "localizedName"], + ["var", "nameList"] + ), + ], + // If the name in the preferred language is the same as the name in the + // local language except for the omission of diacritics and/or the addition + // of a prefix (e.g., "City of" in English or "Ciudad de" in Spanish)... + endsWithExpression( + ["var", "localizedName"], + ["get", "name"], + ["var", "diacriticInsensitiveCollator"] + ), + // ...then replace the common suffix with the local name. + [ + "format", + overwriteSuffixExpression( + ["var", "localizedName"], + ["var", "nameList"] + ), + ], + // Otherwise, gloss the name in the local language if it differs from the + // localized name. + [ + "format", + ["var", "localizedNameList"], + "\n", + "(\u200B", + { "font-scale": 0.8 }, + // GL JS lacks support for bidirectional isolating characters, so use a + // character from the localized name to insulate the parentheses from the + // embedded text's writing direction. Make it so small that GL JS doesn't + // bother rendering it. + ["concat", ["slice", ["var", "localizedName"], 0, 1], " "], + { "font-scale": 0.001 }, + listValuesExpression(["get", "name"], inlineSeparator, [ + "var", + "localizedName", + ]), + { "font-scale": 0.8 }, + ["concat", " ", ["slice", ["var", "localizedName"], 0, 1]], + { "font-scale": 0.001 }, + // A ZWSP prevents GL JS from combining this component with the preceding + // one, which would cause it to vanish along with the faux isolating + // character. + "\u200B)", + { "font-scale": 0.8 }, + ], + ], + ], ], ], ]; From f970e27a24f72b680a6f6f8a3a56844b075b77d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sun, 8 Jan 2023 14:40:16 -0800 Subject: [PATCH 168/514] Test compound glosses, deduplication --- test/spec/label.js | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/spec/label.js b/test/spec/label.js index 1f267a59a..ef721914b 100644 --- a/test/spec/label.js +++ b/test/spec/label.js @@ -302,6 +302,16 @@ describe("label", function () { expect(evaluated.sections.length).to.be.eql(1); expect(evaluated.sections[0].text).to.be.eql("Null Island"); }); + it("spreads multiple unlocalized names across multiple lines", function () { + let evaluated = evaluatedExpression(["en"], { + name: "Null Island;Insula Nullius", + }); + + expect(evaluated.sections.length).to.be.eql(1); + expect(evaluated.sections[0].text).to.be.eql( + "Null Island\nInsula Nullius" + ); + }); it("glosses an anglicized name with the local name", function () { let evaluated = evaluatedExpression(["en"], { "name:en": "Null Island", @@ -362,6 +372,45 @@ describe("label", function () { expectGloss("pl", "Ryga", "Rīga", "Ryga", "Rīga"); expectGloss("pl", "Jurmała", "Jūrmala", "Jurmała", "Jūrmala"); }); + it("glosses multiple local names", function () { + expectGloss( + "en", + "Null Island", + "Terra Nullius;空虛島", + "Null Island", + "Terra Nullius • 空虛島" + ); + }); + it("deduplicates anglicized name and one of the local names", function () { + expectGloss( + "en", + "Null Island", + "Null Island;Terra Nullius;空虛島", + "Null Island", + "Terra Nullius • 空虛島" + ); + expectGloss( + "en", + "Null Island", + "Terra Nullius;Null Island;空虛島", + "Null Island", + "Terra Nullius • 空虛島" + ); + expectGloss( + "en", + "Null Island", + "Terra Nullius;空虛島;Null Island", + "Null Island", + "Terra Nullius • 空虛島" + ); + expectGloss( + "en", + "Null Island", + "Null Island;Null Island;Null Island", + "Null Island", + "" + ); + }); }); describe("#listValuesExpression", function () { From 8e713b242d271acb3158f0c725c9c735afc6eb9d Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 8 Jan 2023 19:47:00 -0500 Subject: [PATCH 169/514] Script to generate style.json --- .gitignore | 1 + scripts/generate_style.js | 11 +++++++++++ src/americana.js | 29 ++++++----------------------- src/js/style.js | 27 +++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 23 deletions(-) create mode 100644 scripts/generate_style.js create mode 100644 src/js/style.js diff --git a/.gitignore b/.gitignore index f367aed61..df109968a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ rebusurance.zip config.js .parcel-cache dist/ +style.json diff --git a/scripts/generate_style.js b/scripts/generate_style.js new file mode 100644 index 000000000..eeec86a0f --- /dev/null +++ b/scripts/generate_style.js @@ -0,0 +1,11 @@ +"use strict"; + +import * as Style from "../src/js/style.js"; + +let style = Style.build( + "https://6ug7hetxl9.execute-api.us-east-2.amazonaws.com/data/v3.json", + "https://zelonewolf.github.io/openstreetmap-americana/sprites/sprite", + ["mul"] +); + +console.log(JSON.stringify(style)); diff --git a/src/americana.js b/src/americana.js index b0fc09cdf..c490c5019 100644 --- a/src/americana.js +++ b/src/americana.js @@ -3,12 +3,11 @@ import config from "./config.js"; import * as Label from "./constants/label.js"; +import * as Style from "./js/style.js"; import * as Shield from "./js/shield.js"; import * as ShieldDef from "./js/shield_defs.js"; -import * as Layers from "./layer/index.js"; - import * as languageLabel from "./js/language_label.js"; import * as maplibregl from "maplibre-gl"; @@ -23,27 +22,11 @@ import { default as OpenMapTilesSamples } from "openmapsamples/samples/OpenMapTi export function buildStyle() { var getUrl = window.location; var baseUrl = getUrl.protocol + "//" + getUrl.host + getUrl.pathname; - - return { - id: "streets", - name: "Americana", - glyphs: - "https://openhistoricalmap.github.io/map-styles/fonts/{fontstack}/{range}.pbf", - layers: Layers.build(Label.getLocales()), - sources: { - openmaptiles: { - url: config.OPENMAPTILES_URL, - type: "vector", - }, - }, - sprite: new URL("sprites/sprite", baseUrl).href, - light: { - anchor: "viewport", - color: "white", - intensity: 0.12, - }, - version: 8, - }; + return Style.build( + config.OPENMAPTILES_URL, + new URL("sprites/sprite", baseUrl).href, + Label.getLocales() + ); } function upgradeLegacyHash() { diff --git a/src/js/style.js b/src/js/style.js new file mode 100644 index 000000000..b88b0f489 --- /dev/null +++ b/src/js/style.js @@ -0,0 +1,27 @@ +"use strict"; + +import * as Layers from "../layer/index.js"; + +//Generate style.json +export function build(tileURL, spriteURL, locales) { + return { + id: "streets", + name: "Americana", + glyphs: + "https://openhistoricalmap.github.io/map-styles/fonts/{fontstack}/{range}.pbf", + layers: Layers.build(locales), + sources: { + openmaptiles: { + url: tileURL, + type: "vector", + }, + }, + sprite: spriteURL, + light: { + anchor: "viewport", + color: "white", + intensity: 0.12, + }, + version: 8, + }; +} From b5a0d727b1f5a2d6af31375abc8d52e3ee9c4b75 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 8 Jan 2023 20:47:28 -0500 Subject: [PATCH 170/514] Update scripts/generate_style.js Co-authored-by: Josh Lee --- scripts/generate_style.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate_style.js b/scripts/generate_style.js index eeec86a0f..2feb51708 100644 --- a/scripts/generate_style.js +++ b/scripts/generate_style.js @@ -8,4 +8,4 @@ let style = Style.build( ["mul"] ); -console.log(JSON.stringify(style)); +console.log("%j", style); From 457685fbd3b3a477c65e5091087fcf97ed7acf84 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 8 Jan 2023 20:47:48 -0500 Subject: [PATCH 171/514] Update src/js/style.js Co-authored-by: Josh Lee --- src/js/style.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/style.js b/src/js/style.js index b88b0f489..d02822717 100644 --- a/src/js/style.js +++ b/src/js/style.js @@ -2,7 +2,7 @@ import * as Layers from "../layer/index.js"; -//Generate style.json +// Generate style.json export function build(tileURL, spriteURL, locales) { return { id: "streets", From 6237bdbed2ac3d57a908946a42df573b70d03815 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 8 Jan 2023 21:07:10 -0500 Subject: [PATCH 172/514] Link to config --- scripts/generate_style.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/generate_style.js b/scripts/generate_style.js index 2feb51708..308fa7ff5 100644 --- a/scripts/generate_style.js +++ b/scripts/generate_style.js @@ -1,9 +1,10 @@ "use strict"; import * as Style from "../src/js/style.js"; +import config from "../src/config.js"; let style = Style.build( - "https://6ug7hetxl9.execute-api.us-east-2.amazonaws.com/data/v3.json", + config.OPENMAPTILES_URL, "https://zelonewolf.github.io/openstreetmap-americana/sprites/sprite", ["mul"] ); From 3c36f42ccb908162180387887e71f793dc78cb6c Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 8 Jan 2023 21:47:42 -0500 Subject: [PATCH 173/514] Accept language code as input --- scripts/generate_style.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/scripts/generate_style.js b/scripts/generate_style.js index 308fa7ff5..127424744 100644 --- a/scripts/generate_style.js +++ b/scripts/generate_style.js @@ -3,10 +3,24 @@ import * as Style from "../src/js/style.js"; import config from "../src/config.js"; +/** + * Accepts a list of languages as parameters + * + * For example: + * generate_style.js en de + * + * ...will generate a style in English with German fallback + */ + +let languages = process.argv.slice(2); +if (languages.length == 0) { + languages = ["mul"]; +} + let style = Style.build( config.OPENMAPTILES_URL, "https://zelonewolf.github.io/openstreetmap-americana/sprites/sprite", - ["mul"] + languages ); console.log("%j", style); From 9cca82d7a44eb5bad9c3b5ee4a46f2bf0b289faf Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 8 Jan 2023 21:50:33 -0500 Subject: [PATCH 174/514] Document RTL plugin --- scripts/generate_style.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/generate_style.js b/scripts/generate_style.js index 127424744..eff652407 100644 --- a/scripts/generate_style.js +++ b/scripts/generate_style.js @@ -10,6 +10,9 @@ import config from "../src/config.js"; * generate_style.js en de * * ...will generate a style in English with German fallback + * + * Requires mapbox-gl-rtl-text: + * https://github.com/mapbox/mapbox-gl-rtl-text/ */ let languages = process.argv.slice(2); From 576064faa43ecf528c6ddee33a8d072efe1eafb6 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Sun, 8 Jan 2023 21:53:58 -0500 Subject: [PATCH 175/514] Remove unused id parameter --- src/js/style.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/style.js b/src/js/style.js index d02822717..227eb3379 100644 --- a/src/js/style.js +++ b/src/js/style.js @@ -5,7 +5,6 @@ import * as Layers from "../layer/index.js"; // Generate style.json export function build(tileURL, spriteURL, locales) { return { - id: "streets", name: "Americana", glyphs: "https://openhistoricalmap.github.io/map-styles/fonts/{fontstack}/{range}.pbf", From 6298c900d2205f398a6211c030363c15213ddd24 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Mon, 9 Jan 2023 09:21:39 -0500 Subject: [PATCH 176/514] Add National Highway shields of India --- icons/shield_in_nh_2.svg | 3 +++ icons/shield_in_nh_3.svg | 3 +++ icons/shield_in_nh_4.svg | 3 +++ src/js/shield_defs.js | 16 ++++++++++++++++ src/shieldtest.js | 1 + 5 files changed, 26 insertions(+) create mode 100644 icons/shield_in_nh_2.svg create mode 100644 icons/shield_in_nh_3.svg create mode 100644 icons/shield_in_nh_4.svg diff --git a/icons/shield_in_nh_2.svg b/icons/shield_in_nh_2.svg new file mode 100644 index 000000000..1e9651039 --- /dev/null +++ b/icons/shield_in_nh_2.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/shield_in_nh_3.svg b/icons/shield_in_nh_3.svg new file mode 100644 index 000000000..8b3daed05 --- /dev/null +++ b/icons/shield_in_nh_3.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/shield_in_nh_4.svg b/icons/shield_in_nh_4.svg new file mode 100644 index 000000000..415f898da --- /dev/null +++ b/icons/shield_in_nh_4.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 73c55757e..4f99ff5b7 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -3370,6 +3370,22 @@ export function loadShields(shieldImages) { // Hong Kong shields["HK"] = escutcheonShieldYellow; + // India + shields["IN:NH"] = { + backgroundImage: [ + shieldImages.shield_in_nh_2, + shieldImages.shield_in_nh_3, + shieldImages.shield_in_nh_4, + ], + textColor: Color.shields.black, + padding: { + left: 2, + right: 2, + top: 3, + bottom: 5, + }, + }; + // Indonesia shields["ID:national"] = { backgroundImage: [shieldImages.shield_id_national], diff --git a/src/shieldtest.js b/src/shieldtest.js index 40805a1a3..c12c02117 100644 --- a/src/shieldtest.js +++ b/src/shieldtest.js @@ -105,6 +105,7 @@ let networks = [ "CA:PE", "US:FL:Toll", "CA:BC", + "IN:NH", "US:US", "US:US:Historic", "CL:national", From 986011b23b3982bb91931e39bd3569fc1965567b Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Mon, 9 Jan 2023 10:27:34 -0500 Subject: [PATCH 177/514] adjust Indian National Highway shield padding --- src/js/shield_defs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/shield_defs.js b/src/js/shield_defs.js index 4f99ff5b7..eb4fe90b8 100644 --- a/src/js/shield_defs.js +++ b/src/js/shield_defs.js @@ -3382,7 +3382,7 @@ export function loadShields(shieldImages) { left: 2, right: 2, top: 3, - bottom: 5, + bottom: 7, }, }; From 39be4b3f1bc94468aa7f1dfe8cf6b2d1890a1506 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Mon, 9 Jan 2023 11:13:04 -0500 Subject: [PATCH 178/514] add India to shield coverage map --- doc-img/shield_map_world.svg | 1 + 1 file changed, 1 insertion(+) diff --git a/doc-img/shield_map_world.svg b/doc-img/shield_map_world.svg index 18f15d8f7..2c2d241dc 100644 --- a/doc-img/shield_map_world.svg +++ b/doc-img/shield_map_world.svg @@ -128,6 +128,7 @@ See the end of this file for a list of available jurisdictions and their codes. .bd, .cn, .hk, +.in, .id, .ir, .iq, From bffccbc9fe031bb612c51b0dabe029551b5a1543 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Mon, 9 Jan 2023 18:12:56 -0500 Subject: [PATCH 179/514] Remove use strict --- scripts/generate_style.js | 2 -- src/js/style.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/scripts/generate_style.js b/scripts/generate_style.js index eff652407..23ff19b00 100644 --- a/scripts/generate_style.js +++ b/scripts/generate_style.js @@ -1,5 +1,3 @@ -"use strict"; - import * as Style from "../src/js/style.js"; import config from "../src/config.js"; diff --git a/src/js/style.js b/src/js/style.js index 227eb3379..ecce19a0d 100644 --- a/src/js/style.js +++ b/src/js/style.js @@ -1,5 +1,3 @@ -"use strict"; - import * as Layers from "../layer/index.js"; // Generate style.json From a1b136ea6ec93a0a3f7761a8869161a3ad7ddb5c Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Mon, 9 Jan 2023 18:23:52 -0500 Subject: [PATCH 180/514] Better command-line handling --- .gitignore | 1 - scripts/generate_style.js | 29 ++++++++++++++++------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index df109968a..f367aed61 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,3 @@ rebusurance.zip config.js .parcel-cache dist/ -style.json diff --git a/scripts/generate_style.js b/scripts/generate_style.js index 23ff19b00..f78a8b081 100644 --- a/scripts/generate_style.js +++ b/scripts/generate_style.js @@ -1,27 +1,30 @@ import * as Style from "../src/js/style.js"; import config from "../src/config.js"; +import * as fs from "fs"; +import { Command } from "commander"; /** - * Accepts a list of languages as parameters - * - * For example: - * generate_style.js en de - * - * ...will generate a style in English with German fallback - * * Requires mapbox-gl-rtl-text: * https://github.com/mapbox/mapbox-gl-rtl-text/ */ -let languages = process.argv.slice(2); -if (languages.length == 0) { - languages = ["mul"]; -} +const program = new Command(); +program + .option("-l, --locales ", "language codes", ["mul"]) + .option("-o, --outfile ", "output file", "-"); +program.parse(process.argv); + +let opts = program.opts(); +//console.log("%o", program.opts()); let style = Style.build( config.OPENMAPTILES_URL, "https://zelonewolf.github.io/openstreetmap-americana/sprites/sprite", - languages + opts.locales ); -console.log("%j", style); +if (opts.outfile == "-") { + console.log("%j", style); +} else { + fs.writeFileSync(opts.outfile, JSON.stringify(style)); +} From 1c7e06e241fc7be1a1bece852890336d0c096e2c Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Mon, 9 Jan 2023 18:31:29 -0500 Subject: [PATCH 181/514] Add generate_style script to npm --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ac103aec2..95f380ba2 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "serve": "node scripts/serve.js", "build:code": "node scripts/build.js", "build": "run-s clean sprites build:code taginfo status_map", - "test": "mocha" + "test": "mocha", + "style": "node scripts/generate_style.js -o dist/style.json" }, "dependencies": { "@basemaps/sprites": "^6.28.1", From d5f246d15d2556cfee6b8ccd1ed4cc441c832432 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Mon, 9 Jan 2023 18:31:58 -0500 Subject: [PATCH 182/514] Remove commented code --- scripts/generate_style.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/generate_style.js b/scripts/generate_style.js index f78a8b081..6a390ed33 100644 --- a/scripts/generate_style.js +++ b/scripts/generate_style.js @@ -15,7 +15,6 @@ program program.parse(process.argv); let opts = program.opts(); -//console.log("%o", program.opts()); let style = Style.build( config.OPENMAPTILES_URL, From 9e788c487659b4c1a8f1abf834c76eac6269258b Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Mon, 9 Jan 2023 19:35:49 -0500 Subject: [PATCH 183/514] Add commander dependency --- package-lock.json | 34 +++++++++++++++++++++++++--------- package.json | 1 + 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index f74582837..e23071176 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@basemaps/sprites": "^6.28.1", + "commander": "^9.5.0", "glob": "^8.0.3", "npm-run-all": "^4.1.5", "open": "^8.4.0" @@ -642,12 +643,11 @@ } }, "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "engines": { - "node": ">= 10" + "node": "^12.20.0 || >=14" } }, "node_modules/concat-map": { @@ -3376,6 +3376,15 @@ "node": ">=10.13.0" } }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/tar-fs": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", @@ -4176,10 +4185,9 @@ "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==" }, "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==" }, "concat-map": { "version": "0.0.1", @@ -6096,6 +6104,14 @@ "csso": "^4.2.0", "picocolors": "^1.0.0", "stable": "^0.1.8" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } } }, "tar-fs": { diff --git a/package.json b/package.json index 95f380ba2..90965a416 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "dependencies": { "@basemaps/sprites": "^6.28.1", + "commander": "^9.5.0", "glob": "^8.0.3", "npm-run-all": "^4.1.5", "open": "^8.4.0" From dd073f875de6d7aabf36f0494b9f67b517950cf1 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 10 Jan 2023 11:53:55 -0500 Subject: [PATCH 184/514] POI setup --- CONTRIBUTING.md | 33 +++++- dev/americana.gpl | 1 + doc-img/background.svg | 3 + doc-img/pantone_717C.svg | 3 + doc-img/pantone_medium_purple_c.svg | 3 + icons/airport.svg | 3 - icons/poi_health_cross.svg | 4 + icons/poi_hospital.svg | 4 + icons/poi_martini_glass.svg | 5 + ...ary_airport.svg => poi_military_plane.svg} | 2 +- icons/poi_p.svg | 3 + icons/poi_plane.svg | 3 + icons/poi_square_dot.svg | 3 + package-lock.json | 36 ++++++ scripts/taginfo_template.json | 6 +- src/constants/color.js | 18 +++ src/js/legend_config.js | 5 + src/layer/aeroway.js | 4 +- src/layer/index.js | 3 + src/layer/poi.js | 103 ++++++++++++++++++ 20 files changed, 235 insertions(+), 10 deletions(-) create mode 100644 doc-img/background.svg create mode 100644 doc-img/pantone_717C.svg create mode 100644 doc-img/pantone_medium_purple_c.svg delete mode 100644 icons/airport.svg create mode 100644 icons/poi_health_cross.svg create mode 100644 icons/poi_hospital.svg create mode 100644 icons/poi_martini_glass.svg rename icons/{military_airport.svg => poi_military_plane.svg} (63%) create mode 100644 icons/poi_p.svg create mode 100644 icons/poi_plane.svg create mode 100644 icons/poi_square_dot.svg create mode 100644 src/layer/poi.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d393fdc28..9a2ee4dcc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -266,7 +266,7 @@ Additionally, **`refsByWayName`** is an object mapping way names to text that ca `refsByWayName` only works if there is no `ref` tag and the expression in the `routeConcurrency` function in layer/highway_shield.js includes the `name` property in the image name. The network needs to be listed as an input value that causes the `match` expression to append `name` to the image name. -When using `overrideByRef` or `refsByWayName`, make sure to add a line to the bottom section of this page explaining why it is necessary, as they are only intended for use in special cases. +When using `overrideByRef` or `refsByWayName`, make sure to add a line to the Special Cases section of this page explaining why it is necessary, as they are only intended for use in special cases. ### Banners @@ -304,6 +304,37 @@ This style strives to draw representative highway shields wherever they are tagg - **West Virginia County Routes**. The West Virginia Department of Transportation posts County Routes, which can have shields with two stacked numbers. For example, in Mercer County, County Route 460/1 is a branch off U.S. Route 460, and County Route 27/6 is a branch off County Route 27. These routes are correctly tagged `ref=460/1` and `ref=27/6` respectively, and shown on shields with the two numbers stacked vertically. - The [highway classification system of the United Kingdom](https://wiki.openstreetmap.org/wiki/Roads_in_the_United_Kingdom). In the UK, mappers need to and are able to tag the actual official road classifications independently of route networks. The color and style of route signage is based on a strict 1:1 correspondence with the `highway=*` value of the underlying road, and **not** based on M/A/B highway network type. While "M" roads are always motorways with blue route symbology, "A" roads can anything from primary through motorway, and thus may take one of three colors and may change along a single route. Even if mappers were to create route relations containing all roads with the same route number, these relations would not be usable for determining how to render route symbology. Additionally, there are no route concurrencies in the UK; all roads that are `highway=secondary` or higher carry a single `ref` value that can be directly rendered into a shield without pre-processing. There is established data consumers support for this highway classification-based symbology system, most notably OpenMapTiles, which has provided pseudo-network values for UK routes since the project's inception. Therefore, this project consumes the UK pseudo-network scheme established by OpenMapTiles and colors UK route network symbology strictly based on `highway=` consistent with UK signage. +## Points of Interest + +A "point of interest" or POI is any feature on the map represented by an icon on the map. + +### Categories + +POIs are broken down into the following broad categories, in order to constrain the number of colors shown on the map. Some features may not cleanly fit into one category or another. Contributors should consider other POIs in the category to determine which category is the best fit. + +- **Geographic Place Names**: labels associated with `place=` tags, for countries, cities, locations, etc. +- **Infrastructure**: features associated with public infrastructure, health, safety, or government. +- **Consumer**: businesses that provide services to the public, such as shops and restaurants. +- **Outdoor**: parks, nature reserves, and other outdoorsy features. +- **Attraction**: places where people go for entertainment, leisure, or curiosity. +- **Transportation**: places where people can access forms of transportation, such as airports, train stations, bus stops, and other public transit. + +### Color Scheme + +For consistency, POI icons should use the following color palette: + +| Category | Pantone | Color | RGB | Hex triplet | +| ---------------------- | --------------- | --------------------------------------------------------------------------- | ----------- | ----------- | +| Geographic Place Names | N/A | Black | 0 0 0 | #000000 | +| Infrastructure | 294 | Blue | 0 63 135 | #003f87 | +| Consumer | 717 C | Orange | 186 82 5 | #d65c05 | +| Outdoor | | TBD (green?) | | | +| Attraction | | TBD (brown?) | | | +| Transportation | Medium Purple C | Purple | 78 0 142 | #4e008e | +| Knockout | | Lt Grayish Orange | 249 245 240 | #f9f5f0 | + +POIs without a background fill should have a 2px stroke using the "knockout" color above. + ### Shield Test Gallery For testing out changes across a variety of different shield designs and ref lengths there is a shield test gallery available: diff --git a/dev/americana.gpl b/dev/americana.gpl index 0baf61589..9357ccc3c 100644 --- a/dev/americana.gpl +++ b/dev/americana.gpl @@ -10,5 +10,6 @@ Name: americana.gpl 196 214 0 Yellow-Green 223 70 97 Pink 243 143 0 Orange +186 82 5 Consumer POI Orange 255 205 0 Yellow 255 255 255 White diff --git a/doc-img/background.svg b/doc-img/background.svg new file mode 100644 index 000000000..d67350c0c --- /dev/null +++ b/doc-img/background.svg @@ -0,0 +1,3 @@ + + + diff --git a/doc-img/pantone_717C.svg b/doc-img/pantone_717C.svg new file mode 100644 index 000000000..d9f66cc3c --- /dev/null +++ b/doc-img/pantone_717C.svg @@ -0,0 +1,3 @@ + + + diff --git a/doc-img/pantone_medium_purple_c.svg b/doc-img/pantone_medium_purple_c.svg new file mode 100644 index 000000000..524589b04 --- /dev/null +++ b/doc-img/pantone_medium_purple_c.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/airport.svg b/icons/airport.svg deleted file mode 100644 index b43510b3f..000000000 --- a/icons/airport.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/poi_health_cross.svg b/icons/poi_health_cross.svg new file mode 100644 index 000000000..c3a2b88fa --- /dev/null +++ b/icons/poi_health_cross.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/poi_hospital.svg b/icons/poi_hospital.svg new file mode 100644 index 000000000..b866837ed --- /dev/null +++ b/icons/poi_hospital.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/poi_martini_glass.svg b/icons/poi_martini_glass.svg new file mode 100644 index 000000000..5bf4f772a --- /dev/null +++ b/icons/poi_martini_glass.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/military_airport.svg b/icons/poi_military_plane.svg similarity index 63% rename from icons/military_airport.svg rename to icons/poi_military_plane.svg index ded091132..4429823ed 100644 --- a/icons/military_airport.svg +++ b/icons/poi_military_plane.svg @@ -1,3 +1,3 @@ - + diff --git a/icons/poi_p.svg b/icons/poi_p.svg new file mode 100644 index 000000000..33b1c0ff6 --- /dev/null +++ b/icons/poi_p.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_plane.svg b/icons/poi_plane.svg new file mode 100644 index 000000000..2af1cb35d --- /dev/null +++ b/icons/poi_plane.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/poi_square_dot.svg b/icons/poi_square_dot.svg new file mode 100644 index 000000000..0d62cb0e0 --- /dev/null +++ b/icons/poi_square_dot.svg @@ -0,0 +1,3 @@ + + + diff --git a/package-lock.json b/package-lock.json index e23071176..54505e758 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1432,6 +1432,19 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3482,6 +3495,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4674,6 +4695,16 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6195,6 +6226,11 @@ "which-boxed-primitive": "^1.0.2" } }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/scripts/taginfo_template.json b/scripts/taginfo_template.json index c5b07b539..7cb977024 100644 --- a/scripts/taginfo_template.json +++ b/scripts/taginfo_template.json @@ -129,7 +129,7 @@ "object_types": ["node", "area"], "description": "Military air bases are marked by a fighter jet.", "doc_url": "https://openmaptiles.org/schema/#class", - "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/military_airport.svg" + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_military_plane.svg" }, { "key": "aerodrome:type", @@ -144,7 +144,7 @@ "object_types": ["node", "area"], "description": "Military air bases are marked by a fighter jet.", "doc_url": "https://openmaptiles.org/schema/#class", - "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/military_airport.svg" + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_military_plane.svg" }, { "key": "military", @@ -152,7 +152,7 @@ "object_types": ["node", "area"], "description": "Military air bases are marked by a fighter jet.", "doc_url": "https://openmaptiles.org/schema/#class", - "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/military_airport.svg" + "icon_url": "https://raw.githubusercontent.com/ZeLonewolf/openstreetmap-americana/main/icons/poi_military_plane.svg" }, { "key": "aeroway", diff --git a/src/constants/color.js b/src/constants/color.js index bba97b525..b22973c0f 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -25,12 +25,14 @@ export const airportOutline = "hsl(250, 41%, 79%)"; export const airportRunway = "hsl(250, 41%, 79%)"; export const airportLabel = "hsl(250, 71%, 29%)"; +//TODO - rename this variable to "palette" export const shields = { black: "black", blue: "#003f87", // Pantone 294 brown: "#693f23", // Pantone 469 green: "#006747", // Pantone 342 orange: "#f38f00", // Pantone 152 + strong_orange: "#d45d00", // Pantone 717 C pink: "#df4661", // Pantone 198 purple: "#6d2077", // Pantone 259 red: "#bf2033", // Pantone 187 @@ -48,3 +50,19 @@ export const lightRailFill = "hsl(0, 0%, 50%)"; export const tramFill = "hsl(0, 0%, 60%)"; export const monorailFill = "hsl(0, 0%, 50%)"; export const funicularFill = "hsl(0, 0%, 50%)"; + +export const hue = { + tollRoad: 48, + park: 136, + water: 211, + transport: 273, + borderCasing: 281, +}; + +export const poi = { + infrastructure: shields.blue, + consumer: shields.strong_orange, + //outdoor: + //attraction: + transportation: `hsl(${hue.transport}, 100%, 28%)`, +}; diff --git a/src/js/legend_config.js b/src/js/legend_config.js index 2b32a7a21..69e4672c5 100644 --- a/src/js/legend_config.js +++ b/src/js/legend_config.js @@ -6,6 +6,7 @@ import * as BoundaryLayers from "../layer/boundary.js"; import * as RoadLayers from "../layer/road.js"; import * as ConstructionLayers from "../layer/construction.js"; import * as HighwayExitLayers from "../layer/highway_exit.js"; +import * as POILayers from "../layer/poi.js"; import * as RailLayers from "../layer/rail.js"; import * as AerowayLayers from "../layer/aeroway.js"; import * as ParkLayers from "../layer/park.js"; @@ -35,6 +36,10 @@ export const sections = [ name: "Route markers", source: "Wikidata", }, + { + name: "Points of interest", + entries: POILayers.legendEntries, + }, { name: "Railroads", entries: RailLayers.legendEntries, diff --git a/src/layer/aeroway.js b/src/layer/aeroway.js index 9af7a5859..b0232d988 100644 --- a/src/layer/aeroway.js +++ b/src/layer/aeroway.js @@ -15,8 +15,8 @@ const iconLayout = { "match", ["get", "class"], "military", - "military_airport", - "airport", + "poi_military_plane", + "poi_plane", ], "text-anchor": "bottom", "text-variable-anchor": [ diff --git a/src/layer/index.js b/src/layer/index.js index 8276ac008..18d13e407 100644 --- a/src/layer/index.js +++ b/src/layer/index.js @@ -11,6 +11,7 @@ import * as lyrLanduse from "./landuse.js"; import * as lyrOneway from "./oneway.js"; import * as lyrPark from "./park.js"; import * as lyrPlace from "./place.js"; +import * as lyrPoi from "./poi.js"; import * as lyrRail from "./rail.js"; import * as lyrRoad from "./road.js"; import * as lyrTransportationLabel from "./transportation_label.js"; @@ -218,6 +219,8 @@ export function build(locales) { lyrHighwayExit.exits, + lyrPoi.poi, + lyrPlace.state, lyrPlace.village, lyrPlace.town, diff --git a/src/layer/poi.js b/src/layer/poi.js new file mode 100644 index 000000000..95df38fbc --- /dev/null +++ b/src/layer/poi.js @@ -0,0 +1,103 @@ +import * as label from "../constants/label.js"; +import * as Color from "../constants/color.js"; + +var iconDefs = { + bar: ["bar", "beer"], + hospital: "hospital", + medical: ["doctors", "clinic"], + parking: "parking", +}; + +export const poi = { + id: "poi", + type: "symbol", + paint: { + "text-halo-color": Color.backgroundFill, + "text-halo-width": 1.5, + "icon-halo-width": 0.4, + "text-halo-blur": 1, + "icon-halo-blur": 0.2, + "text-color": [ + "match", + ["get", "class"], + ["bar", "beer"], + Color.poi.consumer, + ["hospital", "parking"], + Color.poi.infrastructure, + Color.poi.infrastructure, + ], + }, + filter: [ + ">=", + ["zoom"], + [ + "match", + ["get", "subclass"], + "hospital", + 15, + ["bar", "beer"], + 16, + ["clinic", "doctors", "parking"], + 17, + 99, + ], + ], + layout: { + "text-font": ["OpenHistorical"], + "icon-optional": false, + "text-size": { + base: 1.0, + stops: [ + [15, 10], + [17, 12], + ], + }, + "icon-image": [ + "match", + ["get", "subclass"], + iconDefs.bar, + "poi_martini_glass", + iconDefs.medical, + "poi_health_cross", + iconDefs.hospital, + "poi_hospital", + iconDefs.parking, + "poi_p", + "poi_square_dot", //icon for generic POI, not currently used + ], + "icon-size": 1.0, + "text-field": label.localizedName, + "text-variable-anchor": ["left", "right", "bottom"], + "text-justify": "auto", + "text-radial-offset": 1.2, + "text-max-width": 5, + "icon-padding": 0, + "text-padding": 0, + "icon-allow-overlap": false, + }, + source: "openmaptiles", + "source-layer": "poi", +}; + +export const legendEntries = [ + { + description: "Bar or pub", + layers: [poi.id], + filter: ["in", ["get", "subclass"], ["literal", iconDefs.bar]], + }, + { + description: "Hospital", + layers: [poi.id], + filter: ["==", ["get", "subclass"], iconDefs.hospital], + }, + { + description: "Medical facility", + layers: [poi.id], + filter: ["in", ["get", "subclass"], ["literal", iconDefs.medical]], + }, + { + description: "Parking", + layers: [poi.id], + filter: ["==", ["get", "subclass"], iconDefs.parking], + }, +]; From 107e5be9639a110b0bc584f7957ebfa70a5af2d2 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 10 Jan 2023 11:56:02 -0500 Subject: [PATCH 185/514] Re-order legend --- src/layer/poi.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/layer/poi.js b/src/layer/poi.js index 95df38fbc..19942bd1b 100644 --- a/src/layer/poi.js +++ b/src/layer/poi.js @@ -80,21 +80,21 @@ export const poi = { }; export const legendEntries = [ - { - description: "Bar or pub", - layers: [poi.id], - filter: ["in", ["get", "subclass"], ["literal", iconDefs.bar]], - }, { description: "Hospital", layers: [poi.id], filter: ["==", ["get", "subclass"], iconDefs.hospital], }, { - description: "Medical facility", + description: "Doctor's office or clinic", layers: [poi.id], filter: ["in", ["get", "subclass"], ["literal", iconDefs.medical]], }, + { + description: "Bar or pub", + layers: [poi.id], + filter: ["in", ["get", "subclass"], ["literal", iconDefs.bar]], + }, { description: "Parking", layers: [poi.id], From cf9dbf156c8676dcd4e312a8ec535f8e78f59349 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 10 Jan 2023 11:57:48 -0500 Subject: [PATCH 186/514] Re-order legend sections --- src/js/legend_config.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/js/legend_config.js b/src/js/legend_config.js index 69e4672c5..02e9170e3 100644 --- a/src/js/legend_config.js +++ b/src/js/legend_config.js @@ -6,9 +6,9 @@ import * as BoundaryLayers from "../layer/boundary.js"; import * as RoadLayers from "../layer/road.js"; import * as ConstructionLayers from "../layer/construction.js"; import * as HighwayExitLayers from "../layer/highway_exit.js"; -import * as POILayers from "../layer/poi.js"; import * as RailLayers from "../layer/rail.js"; import * as AerowayLayers from "../layer/aeroway.js"; +import * as POILayers from "../layer/poi.js"; import * as ParkLayers from "../layer/park.js"; import * as BuildingLayers from "../layer/building.js"; import * as WaterLayers from "../layer/water.js"; @@ -36,10 +36,6 @@ export const sections = [ name: "Route markers", source: "Wikidata", }, - { - name: "Points of interest", - entries: POILayers.legendEntries, - }, { name: "Railroads", entries: RailLayers.legendEntries, @@ -48,6 +44,10 @@ export const sections = [ name: "Aviation", entries: AerowayLayers.legendEntries, }, + { + name: "Points of interest", + entries: POILayers.legendEntries, + }, { name: "Structures", entries: BuildingLayers.legendEntries, From 7b4ef041042233503276f9d0f01a9d164cae5d14 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 10 Jan 2023 17:24:56 -0500 Subject: [PATCH 187/514] Refactor color file --- src/constants/color.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/constants/color.js b/src/constants/color.js index b22973c0f..e4b23544e 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -26,7 +26,7 @@ export const airportRunway = "hsl(250, 41%, 79%)"; export const airportLabel = "hsl(250, 71%, 29%)"; //TODO - rename this variable to "palette" -export const shields = { +export const palette = { black: "black", blue: "#003f87", // Pantone 294 brown: "#693f23", // Pantone 469 @@ -41,6 +41,20 @@ export const shields = { yellow_green: "#c4d600", // Pantone 382 }; +export const shields = { + black: palette.black, + blue: palette.blue, // Pantone 294 + brown: palette.brown, // Pantone 469 + green: palette.green, // Pantone 342 + orange: palette.orange, // Pantone 152 + pink: palette.pink, // Pantone 198 + purple: palette.purple, // Pantone 259 + red: palette.red, // Pantone 187 + white: palette.white, + yellow: palette.yellow, // Pantone 116 + yellow_green: palette.yellow_green, // Pantone 382 +}; + export const railwayTunnelFill = "hsl(0, 0%, 90%)"; export const railFill = "hsl(0, 0%, 60%)"; @@ -60,8 +74,8 @@ export const hue = { }; export const poi = { - infrastructure: shields.blue, - consumer: shields.strong_orange, + infrastructure: palette.blue, + consumer: palette.strong_orange, //outdoor: //attraction: transportation: `hsl(${hue.transport}, 100%, 28%)`, From 897573aa6d710dce4295f2bb28f7d82c0b8f7e91 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 10 Jan 2023 22:14:16 -0500 Subject: [PATCH 188/514] Update orange to UTexas --- CONTRIBUTING.md | 42 +++++++++---------- .../{pantone_717C.svg => texas_orange.svg} | 2 +- icons/poi_martini_glass.svg | 6 +-- src/constants/color.js | 4 +- src/layer/poi.js | 8 ++-- 5 files changed, 31 insertions(+), 31 deletions(-) rename doc-img/{pantone_717C.svg => texas_orange.svg} (64%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9a2ee4dcc..624cc913b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -304,6 +304,24 @@ This style strives to draw representative highway shields wherever they are tagg - **West Virginia County Routes**. The West Virginia Department of Transportation posts County Routes, which can have shields with two stacked numbers. For example, in Mercer County, County Route 460/1 is a branch off U.S. Route 460, and County Route 27/6 is a branch off County Route 27. These routes are correctly tagged `ref=460/1` and `ref=27/6` respectively, and shown on shields with the two numbers stacked vertically. - The [highway classification system of the United Kingdom](https://wiki.openstreetmap.org/wiki/Roads_in_the_United_Kingdom). In the UK, mappers need to and are able to tag the actual official road classifications independently of route networks. The color and style of route signage is based on a strict 1:1 correspondence with the `highway=*` value of the underlying road, and **not** based on M/A/B highway network type. While "M" roads are always motorways with blue route symbology, "A" roads can anything from primary through motorway, and thus may take one of three colors and may change along a single route. Even if mappers were to create route relations containing all roads with the same route number, these relations would not be usable for determining how to render route symbology. Additionally, there are no route concurrencies in the UK; all roads that are `highway=secondary` or higher carry a single `ref` value that can be directly rendered into a shield without pre-processing. There is established data consumers support for this highway classification-based symbology system, most notably OpenMapTiles, which has provided pseudo-network values for UK routes since the project's inception. Therefore, this project consumes the UK pseudo-network scheme established by OpenMapTiles and colors UK route network symbology strictly based on `highway=` consistent with UK signage. +### Shield Test Gallery + +For testing out changes across a variety of different shield designs and ref lengths there is a shield test gallery available: + +- In local development: http://localhost:1776/shieldtest.html +- On the public demo site: https://zelonewolf.github.io/openstreetmap-americana/shieldtest.html + +This aims to display a table of all the unique shield designs in the style with some example refs from 1 to 6 characters. The `networks` and `refs` arrays can be modified for testing with a different set of either: + +https://github.com/ZeLonewolf/openstreetmap-americana/blob/581e1e5d97f5745c1bf764689439d93403888505/src/shieldtest.js#L16-L31 +https://github.com/ZeLonewolf/openstreetmap-americana/blob/581e1e5d97f5745c1bf764689439d93403888505/src/shieldtest.js#L203-L218 + +To test with a list of all the supported networks in the style this line can be uncommented: + +https://github.com/ZeLonewolf/openstreetmap-americana/blob/581e1e5d97f5745c1bf764689439d93403888505/src/shieldtest.js#L200-L201 + +This results in a very long page and can be quite slow or even crash the browser tab. + ## Points of Interest A "point of interest" or POI is any feature on the map represented by an icon on the map. @@ -323,32 +341,14 @@ POIs are broken down into the following broad categories, in order to constrain For consistency, POI icons should use the following color palette: -| Category | Pantone | Color | RGB | Hex triplet | +| Category | Scheme | Color | RGB | Hex triplet | | ---------------------- | --------------- | --------------------------------------------------------------------------- | ----------- | ----------- | | Geographic Place Names | N/A | Black | 0 0 0 | #000000 | -| Infrastructure | 294 | Blue | 0 63 135 | #003f87 | -| Consumer | 717 C | Orange | 186 82 5 | #d65c05 | +| Infrastructure | Pantone 294 | Blue | 0 63 135 | #003f87 | +| Consumer | UTexas Orange | Orange | 191 87 0 | #bf5700 | | Outdoor | | TBD (green?) | | | | Attraction | | TBD (brown?) | | | | Transportation | Medium Purple C | Purple | 78 0 142 | #4e008e | | Knockout | | Lt Grayish Orange | 249 245 240 | #f9f5f0 | POIs without a background fill should have a 2px stroke using the "knockout" color above. - -### Shield Test Gallery - -For testing out changes across a variety of different shield designs and ref lengths there is a shield test gallery available: - -- In local development: http://localhost:1776/shieldtest.html -- On the public demo site: https://zelonewolf.github.io/openstreetmap-americana/shieldtest.html - -This aims to display a table of all the unique shield designs in the style with some example refs from 1 to 6 characters. The `networks` and `refs` arrays can be modified for testing with a different set of either: - -https://github.com/ZeLonewolf/openstreetmap-americana/blob/581e1e5d97f5745c1bf764689439d93403888505/src/shieldtest.js#L16-L31 -https://github.com/ZeLonewolf/openstreetmap-americana/blob/581e1e5d97f5745c1bf764689439d93403888505/src/shieldtest.js#L203-L218 - -To test with a list of all the supported networks in the style this line can be uncommented: - -https://github.com/ZeLonewolf/openstreetmap-americana/blob/581e1e5d97f5745c1bf764689439d93403888505/src/shieldtest.js#L200-L201 - -This results in a very long page and can be quite slow or even crash the browser tab. diff --git a/doc-img/pantone_717C.svg b/doc-img/texas_orange.svg similarity index 64% rename from doc-img/pantone_717C.svg rename to doc-img/texas_orange.svg index d9f66cc3c..e4b96620b 100644 --- a/doc-img/pantone_717C.svg +++ b/doc-img/texas_orange.svg @@ -1,3 +1,3 @@ - + diff --git a/icons/poi_martini_glass.svg b/icons/poi_martini_glass.svg index 5bf4f772a..5c4cba00b 100644 --- a/icons/poi_martini_glass.svg +++ b/icons/poi_martini_glass.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/src/constants/color.js b/src/constants/color.js index e4b23544e..e574b411b 100644 --- a/src/constants/color.js +++ b/src/constants/color.js @@ -32,7 +32,7 @@ export const palette = { brown: "#693f23", // Pantone 469 green: "#006747", // Pantone 342 orange: "#f38f00", // Pantone 152 - strong_orange: "#d45d00", // Pantone 717 C + texas_orange: "#bf5700", // UTexas Orange pink: "#df4661", // Pantone 198 purple: "#6d2077", // Pantone 259 red: "#bf2033", // Pantone 187 @@ -75,7 +75,7 @@ export const hue = { export const poi = { infrastructure: palette.blue, - consumer: palette.strong_orange, + consumer: palette.texas_orange, //outdoor: //attraction: transportation: `hsl(${hue.transport}, 100%, 28%)`, diff --git a/src/layer/poi.js b/src/layer/poi.js index 19942bd1b..391afc7f4 100644 --- a/src/layer/poi.js +++ b/src/layer/poi.js @@ -2,7 +2,7 @@ import * as label from "../constants/label.js"; import * as Color from "../constants/color.js"; var iconDefs = { - bar: ["bar", "beer"], + bar: ["bar", "beer", "pub"], hospital: "hospital", medical: ["doctors", "clinic"], parking: "parking", @@ -19,8 +19,8 @@ export const poi = { "icon-halo-blur": 0.2, "text-color": [ "match", - ["get", "class"], - ["bar", "beer"], + ["get", "subclass"], + [...iconDefs.bar], Color.poi.consumer, ["hospital", "parking"], Color.poi.infrastructure, @@ -35,7 +35,7 @@ export const poi = { ["get", "subclass"], "hospital", 15, - ["bar", "beer"], + [...iconDefs.bar], 16, ["clinic", "doctors", "parking"], 17, From 353485f292198fd77211742bf8fd95ebbe7a031d Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Tue, 10 Jan 2023 22:45:59 -0500 Subject: [PATCH 189/514] Coffee shops --- icons/poi_coffee_cup.svg | 4 ++++ src/layer/poi.js | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 icons/poi_coffee_cup.svg diff --git a/icons/poi_coffee_cup.svg b/icons/poi_coffee_cup.svg new file mode 100644 index 000000000..defeb2415 --- /dev/null +++ b/icons/poi_coffee_cup.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/layer/poi.js b/src/layer/poi.js index 391afc7f4..9d090a537 100644 --- a/src/layer/poi.js +++ b/src/layer/poi.js @@ -3,6 +3,7 @@ import * as Color from "../constants/color.js"; var iconDefs = { bar: ["bar", "beer", "pub"], + coffee: ["cafe"], hospital: "hospital", medical: ["doctors", "clinic"], parking: "parking", @@ -20,7 +21,7 @@ export const poi = { "text-color": [ "match", ["get", "subclass"], - [...iconDefs.bar], + [...iconDefs.bar, ...iconDefs.coffee], Color.poi.consumer, ["hospital", "parking"], Color.poi.infrastructure, @@ -35,7 +36,7 @@ export const poi = { ["get", "subclass"], "hospital", 15, - [...iconDefs.bar], + [...iconDefs.bar, ...iconDefs.coffee], 16, ["clinic", "doctors", "parking"], 17, @@ -57,6 +58,8 @@ export const poi = { ["get", "subclass"], iconDefs.bar, "poi_martini_glass", + iconDefs.coffee, + "poi_coffee_cup", iconDefs.medical, "poi_health_cross", iconDefs.hospital, @@ -90,6 +93,11 @@ export const legendEntries = [ layers: [poi.id], filter: ["in", ["get", "subclass"], ["literal", iconDefs.medical]], }, + { + description: "Coffee shop", + layers: [poi.id], + filter: ["in", ["get", "subclass"], ["literal", iconDefs.coffee]], + }, { description: "Bar or pub", layers: [poi.id], From 743cc9ab1d0d9780796e0eaa8f77b795294ee984 Mon Sep 17 00:00:00 2001 From: Clay Smalley Date: Wed, 11 Jan 2023 11:59:29 -0500 Subject: [PATCH 190/514] Format coffee cup SVG --- icons/poi_coffee_cup.svg | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/icons/poi_coffee_cup.svg b/icons/poi_coffee_cup.svg index defeb2415..bf89d622b 100644 --- a/icons/poi_coffee_cup.svg +++ b/icons/poi_coffee_cup.svg @@ -1,4 +1,3 @@ - - - + + From 0a23fbcfbbefcb7c534bff26de42c9fcaf24fd65 Mon Sep 17 00:00:00 2001 From: Josh Lee Date: Wed, 11 Jan 2023 21:31:43 -0500 Subject: [PATCH 191/514] Make the fork me button triangle shaped --- src/index.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/index.html b/src/index.html index ca5c83ac9..d6a5c04a2 100644 --- a/src/index.html +++ b/src/index.html @@ -103,10 +103,8 @@ width="149" height="149" src="https://github.blog/wp-content/uploads/2008/12/forkme_right_green_007200.png?resize=149%2C149" - class="attachment-full size-full" alt="Fork me on GitHub" - data-recalc-dims="1" - style="position: absolute; top: 0; right: 0; border: 0; z-index: 100" + style="position: absolute; top: 0; right: 0; border: 0; z-index: 100; clip-path: polygon(0% 0%, 100% 0%, 100% 100%);" />

Legend

From bb8fccdf847af99073bf64188443b0571e8b7379 Mon Sep 17 00:00:00 2001 From: Brian Sperlongano Date: Wed, 11 Jan 2023 21:41:50 -0500 Subject: [PATCH 192/514] prettier --- src/index.html | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/index.html b/src/index.html index d6a5c04a2..f5cbda37c 100644 --- a/src/index.html +++ b/src/index.html @@ -104,7 +104,14 @@ height="149" src="https://github.blog/wp-content/uploads/2008/12/forkme_right_green_007200.png?resize=149%2C149" alt="Fork me on GitHub" - style="position: absolute; top: 0; right: 0; border: 0; z-index: 100; clip-path: polygon(0% 0%, 100% 0%, 100% 100%);" + style=" + position: absolute; + top: 0; + right: 0; + border: 0; + z-index: 100; + clip-path: polygon(0% 0%, 100% 0%, 100% 100%); + " />