gov-uk-dashboards 26.8.0__py3-none-any.whl → 26.26.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gov_uk_dashboards/assets/dashboard.css +177 -0
- gov_uk_dashboards/assets/images/CHASE_icon.svg +17 -0
- gov_uk_dashboards/assets/images/explore_data_logo.svg +87 -0
- gov_uk_dashboards/colours.py +22 -0
- gov_uk_dashboards/components/dash/__init__.py +1 -1
- gov_uk_dashboards/components/dash/banners.py +20 -0
- gov_uk_dashboards/components/dash/context_card.py +472 -53
- gov_uk_dashboards/components/dash/data_quality_banner.py +91 -0
- gov_uk_dashboards/components/dash/download_button.py +0 -30
- gov_uk_dashboards/components/dash/footer.py +72 -29
- gov_uk_dashboards/components/dash/green_button.py +25 -0
- gov_uk_dashboards/components/dash/header.py +44 -0
- gov_uk_dashboards/components/dash/main_content.py +8 -1
- gov_uk_dashboards/components/dash/notification_banner.py +8 -5
- gov_uk_dashboards/components/leaflet/leaflet_choropleth_map.py +66 -27
- gov_uk_dashboards/components/plotly/enums.py +2 -0
- gov_uk_dashboards/components/plotly/stacked_barchart.py +53 -15
- gov_uk_dashboards/components/plotly/time_series_chart.py +140 -15
- gov_uk_dashboards/constants.py +19 -0
- gov_uk_dashboards/formatting/number_formatting.py +7 -0
- gov_uk_dashboards/lib/datetime_functions/datetime_functions.py +85 -0
- gov_uk_dashboards/lib/http_headers.py +3 -2
- gov_uk_dashboards/lib/testing_functions/data_test_assertions.py +3 -3
- gov_uk_dashboards/lib/testing_functions/data_test_helper_functions.py +2 -1
- gov_uk_dashboards/log_kpi.py +37 -0
- gov_uk_dashboards/template.html +1 -1
- gov_uk_dashboards/template.py +6 -0
- {gov_uk_dashboards-26.8.0.dist-info → gov_uk_dashboards-26.26.0.dist-info}/METADATA +2 -2
- {gov_uk_dashboards-26.8.0.dist-info → gov_uk_dashboards-26.26.0.dist-info}/RECORD +32 -27
- {gov_uk_dashboards-26.8.0.dist-info → gov_uk_dashboards-26.26.0.dist-info}/WHEEL +1 -1
- {gov_uk_dashboards-26.8.0.dist-info → gov_uk_dashboards-26.26.0.dist-info}/licenses/LICENSE +0 -0
- {gov_uk_dashboards-26.8.0.dist-info → gov_uk_dashboards-26.26.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""data_quality_banner"""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from dash import html
|
|
6
|
+
from gov_uk_dashboards.components.dash.notification_banner import notification_banner
|
|
7
|
+
from gov_uk_dashboards.formatting.text_functions import create_id_from_string
|
|
8
|
+
from gov_uk_dashboards.constants import (
|
|
9
|
+
NOTIFICATION_STYLE_GREEN,
|
|
10
|
+
NOTIFICATION_STYLE_ORANGE,
|
|
11
|
+
NOTIFICATION_STYLE_RED,
|
|
12
|
+
NOTIFICATION_STYLE_YELLOW,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class DataQualityConfig:
|
|
18
|
+
"""
|
|
19
|
+
Configuration class for defining the display and linking details of a data quality metric.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
title (str): The title of the data quality metric.
|
|
23
|
+
text (str): Descriptive text or explanation of the metric.
|
|
24
|
+
style (str): The visual style or format for displaying the metric.
|
|
25
|
+
title_color (str, optional): Optional color to use for the title. Defaults to None.
|
|
26
|
+
glossary_url (str, optional): Optional URL linking to a glossary entry for this metric.
|
|
27
|
+
If not provided, a URL is automatically generated based on the title.
|
|
28
|
+
|
|
29
|
+
Methods:
|
|
30
|
+
__post_init__(): Automatically generates a glossary URL if none is provided.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
title: str
|
|
34
|
+
text: str
|
|
35
|
+
style: str
|
|
36
|
+
title_color: str = None
|
|
37
|
+
glossary_url: str = None
|
|
38
|
+
|
|
39
|
+
def __post_init__(self):
|
|
40
|
+
if not self.glossary_url:
|
|
41
|
+
slug = create_id_from_string(self.title)
|
|
42
|
+
self.glossary_url = f"/glossary#data-quality-{slug}"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class DataQualityLabels(Enum):
|
|
46
|
+
"""Enumeration of standard labels used to categorize or describe data quality."""
|
|
47
|
+
|
|
48
|
+
OFFICIAL = DataQualityConfig(
|
|
49
|
+
title="OFFICIAL public data",
|
|
50
|
+
text="Use with confidence.",
|
|
51
|
+
style=NOTIFICATION_STYLE_GREEN,
|
|
52
|
+
)
|
|
53
|
+
MI = DataQualityConfig(
|
|
54
|
+
title="Management information",
|
|
55
|
+
text="Use for early insights, but with caution.",
|
|
56
|
+
style=NOTIFICATION_STYLE_YELLOW,
|
|
57
|
+
title_color="black",
|
|
58
|
+
)
|
|
59
|
+
EXPERIMENTAL_MI = DataQualityConfig(
|
|
60
|
+
title="Experimental management information",
|
|
61
|
+
text="Indicative only, requires expert guidance on use.",
|
|
62
|
+
style=NOTIFICATION_STYLE_ORANGE,
|
|
63
|
+
title_color="black",
|
|
64
|
+
)
|
|
65
|
+
OPERATIONAL = DataQualityConfig(
|
|
66
|
+
title="Operational data",
|
|
67
|
+
text="Never use in isolation, always verify independently.",
|
|
68
|
+
style=NOTIFICATION_STYLE_RED,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def data_quality_notification_banner(label: DataQualityLabels):
|
|
73
|
+
"""Return data quality notification banner based on Gov UK Design component notification
|
|
74
|
+
banner component."""
|
|
75
|
+
config = label.value
|
|
76
|
+
text = [
|
|
77
|
+
f"{config.text} Read more in our ",
|
|
78
|
+
html.A(
|
|
79
|
+
"glossary",
|
|
80
|
+
href=config.glossary_url,
|
|
81
|
+
target="_blank",
|
|
82
|
+
rel="noopener noreferrer",
|
|
83
|
+
),
|
|
84
|
+
".",
|
|
85
|
+
]
|
|
86
|
+
return notification_banner(
|
|
87
|
+
title=config.title,
|
|
88
|
+
text=text,
|
|
89
|
+
style=config.style,
|
|
90
|
+
title_color=config.title_color,
|
|
91
|
+
)
|
|
@@ -1,41 +1,11 @@
|
|
|
1
1
|
"""download_button"""
|
|
2
2
|
|
|
3
3
|
from typing import Union
|
|
4
|
-
import warnings
|
|
5
4
|
from dash import html
|
|
6
5
|
|
|
7
6
|
from gov_uk_dashboards.constants import DOWNLOAD_BUTTON_CLASSES
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
def download_button(button_text: str, button_id: str = "download-button"):
|
|
11
|
-
"""
|
|
12
|
-
Return a download button which is aligned to the right
|
|
13
|
-
|
|
14
|
-
Args:
|
|
15
|
-
button_text (str): The text to display on the button.
|
|
16
|
-
button_id: (str, Optional) = id for dropdown, default to "download-button"
|
|
17
|
-
"""
|
|
18
|
-
warnings.warn(
|
|
19
|
-
"Note there is an alternative function to download_button() called "
|
|
20
|
-
"create_download_button_with_icon() which includes a download icon and improved styling.",
|
|
21
|
-
Warning,
|
|
22
|
-
stacklevel=2,
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
return html.Div(
|
|
26
|
-
[
|
|
27
|
-
html.Button(
|
|
28
|
-
button_text,
|
|
29
|
-
id=button_id,
|
|
30
|
-
n_clicks=0,
|
|
31
|
-
className="govuk-button govuk-button--secondary",
|
|
32
|
-
),
|
|
33
|
-
],
|
|
34
|
-
className="govuk-button-group",
|
|
35
|
-
style={"float": "right"},
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
|
|
39
9
|
def create_download_button_with_icon(
|
|
40
10
|
button_text: str,
|
|
41
11
|
button_id_name: str,
|
|
@@ -4,7 +4,7 @@ from typing import Optional
|
|
|
4
4
|
from dash import html
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def footer(footer_links: Optional[list[any]]):
|
|
7
|
+
def footer(footer_links: Optional[list[any]], include_logos: bool = False):
|
|
8
8
|
"""
|
|
9
9
|
HTML component for a Gov.UK standard footer.
|
|
10
10
|
|
|
@@ -30,37 +30,80 @@ def footer(footer_links: Optional[list[any]]):
|
|
|
30
30
|
if footer_links
|
|
31
31
|
else None
|
|
32
32
|
),
|
|
33
|
-
(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
html.
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
html.Div(
|
|
34
|
+
[
|
|
35
|
+
(
|
|
36
|
+
html.Ul(
|
|
37
|
+
children=[
|
|
38
|
+
html.Li(
|
|
39
|
+
item,
|
|
40
|
+
className="govuk-footer__inline-list-item",
|
|
41
|
+
)
|
|
42
|
+
for item in footer_links
|
|
43
|
+
],
|
|
44
|
+
className=(
|
|
45
|
+
"govuk-footer__inline-list "
|
|
46
|
+
"govuk-!-display-none-print"
|
|
47
|
+
),
|
|
39
48
|
)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
className=(
|
|
43
|
-
"govuk-footer__inline-list "
|
|
44
|
-
"govuk-!-display-none-print"
|
|
49
|
+
if footer_links
|
|
50
|
+
else None
|
|
45
51
|
),
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
html.Span(
|
|
53
|
+
[
|
|
54
|
+
"All content is available under the ",
|
|
55
|
+
html.A(
|
|
56
|
+
"Open Government Licence v3.0",
|
|
57
|
+
rel="license",
|
|
58
|
+
href="https://www.nationalarchives.gov.uk/doc/"
|
|
59
|
+
"open-government-licence/version/3/",
|
|
60
|
+
className="govuk-footer__link",
|
|
61
|
+
target="_blank",
|
|
62
|
+
),
|
|
63
|
+
", except where otherwise stated",
|
|
64
|
+
],
|
|
65
|
+
className="govuk-footer__licence-description",
|
|
66
|
+
),
|
|
67
|
+
(
|
|
68
|
+
(
|
|
69
|
+
html.Div(
|
|
70
|
+
[
|
|
71
|
+
html.Img(
|
|
72
|
+
src="assets\\images\\CHASE_icon.svg",
|
|
73
|
+
className="header-image",
|
|
74
|
+
style={"maxWidth": "100px"},
|
|
75
|
+
),
|
|
76
|
+
html.Div(
|
|
77
|
+
[
|
|
78
|
+
html.B(
|
|
79
|
+
"CHASE",
|
|
80
|
+
style={
|
|
81
|
+
"font-size": "36px"
|
|
82
|
+
},
|
|
83
|
+
),
|
|
84
|
+
],
|
|
85
|
+
style={
|
|
86
|
+
"display": "flex",
|
|
87
|
+
"flex-direction": "column",
|
|
88
|
+
"align-items": "left",
|
|
89
|
+
"color": "#707070",
|
|
90
|
+
"maxWidth": "200px",
|
|
91
|
+
"padding-bottom": "10px",
|
|
92
|
+
},
|
|
93
|
+
),
|
|
94
|
+
],
|
|
95
|
+
style={
|
|
96
|
+
"display": "flex",
|
|
97
|
+
"flex-direction": "row",
|
|
98
|
+
"align-items": "center",
|
|
99
|
+
"padding-top": "30px",
|
|
100
|
+
},
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
if include_logos
|
|
104
|
+
else None
|
|
60
105
|
),
|
|
61
|
-
|
|
62
|
-
],
|
|
63
|
-
className="govuk-footer__licence-description",
|
|
106
|
+
]
|
|
64
107
|
),
|
|
65
108
|
],
|
|
66
109
|
className="govuk-footer__meta-item govuk-footer__meta-item--grow",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""green_button"""
|
|
2
|
+
|
|
3
|
+
from dash import html
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def green_button(button_text: str, button_id: str):
|
|
7
|
+
"""
|
|
8
|
+
Return a green button which is aligned to the right
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
button_text (str): The text to display on the button.
|
|
12
|
+
button_id: (str) = id for button
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
return html.Div(
|
|
16
|
+
[
|
|
17
|
+
html.Button(
|
|
18
|
+
button_text,
|
|
19
|
+
id=button_id,
|
|
20
|
+
n_clicks=0,
|
|
21
|
+
className="govuk-button",
|
|
22
|
+
),
|
|
23
|
+
],
|
|
24
|
+
className="govuk-button-group",
|
|
25
|
+
)
|
|
@@ -78,3 +78,47 @@ def header(
|
|
|
78
78
|
| {"borderColor": "#000000", "backgroundColor": "rgb(0,98,94)"},
|
|
79
79
|
**{"data-module": "govuk-header"},
|
|
80
80
|
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def logo_header():
|
|
84
|
+
"""
|
|
85
|
+
Create and return the application header containing the Explore Data logo.
|
|
86
|
+
|
|
87
|
+
This component renders a GOV.UK–styled header with an SVG logo.
|
|
88
|
+
The logo is loaded from the Dash assets directory and constrained
|
|
89
|
+
to a maximum height for consistent layout.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
dash.html.Header
|
|
94
|
+
A Dash Header component containing the styled logo container.
|
|
95
|
+
"""
|
|
96
|
+
return html.Header(
|
|
97
|
+
html.Div(
|
|
98
|
+
html.Div(
|
|
99
|
+
[
|
|
100
|
+
html.Div(
|
|
101
|
+
[
|
|
102
|
+
html.Img(
|
|
103
|
+
src="/assets/images/explore_data_logo.svg",
|
|
104
|
+
className="header-image",
|
|
105
|
+
style={"maxHeight": "100px"},
|
|
106
|
+
),
|
|
107
|
+
],
|
|
108
|
+
style={
|
|
109
|
+
"display": "flex",
|
|
110
|
+
"flexDirection": "column",
|
|
111
|
+
"color": "#00625e",
|
|
112
|
+
"alignItems": "center",
|
|
113
|
+
"paddingBottom": "2px",
|
|
114
|
+
},
|
|
115
|
+
),
|
|
116
|
+
],
|
|
117
|
+
className="govuk-header__content",
|
|
118
|
+
),
|
|
119
|
+
style={"borderBottom": "0px solid #000000", "borderColor": "#f3f2f1"},
|
|
120
|
+
className="govuk-header__container govuk-width-container",
|
|
121
|
+
),
|
|
122
|
+
className="govuk-header",
|
|
123
|
+
style={"backgroundColor": "#f3f2f1", "borderColor": "#f3f2f1"},
|
|
124
|
+
)
|
|
@@ -5,7 +5,7 @@ import re
|
|
|
5
5
|
from dash import html
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def main_content(children, id_fragment=None):
|
|
8
|
+
def main_content(children, id_fragment=None, include_feedback_banner_div: bool = False):
|
|
9
9
|
"""
|
|
10
10
|
Wrapper for the main content of the dashboard, containing visualisations.
|
|
11
11
|
|
|
@@ -29,4 +29,11 @@ def main_content(children, id_fragment=None):
|
|
|
29
29
|
UserWarning,
|
|
30
30
|
stacklevel=2,
|
|
31
31
|
)
|
|
32
|
+
|
|
33
|
+
if not isinstance(children, list):
|
|
34
|
+
children = [children]
|
|
35
|
+
|
|
36
|
+
if include_feedback_banner_div:
|
|
37
|
+
children.append(html.Div(id="feedback-banner"))
|
|
38
|
+
|
|
32
39
|
return html.Main(children, className="main", id=slug, role="main")
|
|
@@ -4,7 +4,11 @@ from dash import html
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def notification_banner(
|
|
7
|
-
text:
|
|
7
|
+
text: list,
|
|
8
|
+
title: str = "Important",
|
|
9
|
+
text_class_name: str = "govuk-notification-banner__heading",
|
|
10
|
+
style: dict = None,
|
|
11
|
+
title_color: str = None,
|
|
8
12
|
):
|
|
9
13
|
"""
|
|
10
14
|
Return Gov UK Design component notification banner component.
|
|
@@ -14,9 +18,10 @@ def notification_banner(
|
|
|
14
18
|
html.Div(
|
|
15
19
|
[
|
|
16
20
|
html.H2(
|
|
17
|
-
[
|
|
21
|
+
[title],
|
|
18
22
|
className="govuk-notification-banner__title",
|
|
19
23
|
id="govuk-notification-banner-title",
|
|
24
|
+
style={"color": title_color} if title_color else None,
|
|
20
25
|
)
|
|
21
26
|
],
|
|
22
27
|
className="govuk-notification-banner__header",
|
|
@@ -24,9 +29,7 @@ def notification_banner(
|
|
|
24
29
|
html.Div(
|
|
25
30
|
[
|
|
26
31
|
html.P(
|
|
27
|
-
|
|
28
|
-
text,
|
|
29
|
-
],
|
|
32
|
+
text,
|
|
30
33
|
className=text_class_name,
|
|
31
34
|
)
|
|
32
35
|
],
|
|
@@ -42,8 +42,9 @@ class LeafletChoroplethMap:
|
|
|
42
42
|
download_chart_button_id: Optional[str] = None,
|
|
43
43
|
download_data_button_id: Optional[str] = None,
|
|
44
44
|
color_scale_is_discrete: bool = True,
|
|
45
|
+
colorbar_title: str = None,
|
|
45
46
|
show_tile_layer: bool = False,
|
|
46
|
-
):
|
|
47
|
+
): # pylint: disable=too-many-locals
|
|
47
48
|
self.geojson_data = geojson
|
|
48
49
|
self.df = df
|
|
49
50
|
self.hover_text_columns = hover_text_columns
|
|
@@ -56,6 +57,7 @@ class LeafletChoroplethMap:
|
|
|
56
57
|
self.download_chart_button_id = download_chart_button_id
|
|
57
58
|
self.download_data_button_id = download_data_button_id
|
|
58
59
|
self.color_scale_is_discrete = color_scale_is_discrete
|
|
60
|
+
self.colorbar_title = self.resolve_colorbar_title(colorbar_title)
|
|
59
61
|
self.show_tile_layer = show_tile_layer
|
|
60
62
|
self._add_data_to_geojson()
|
|
61
63
|
self.instance_number = instance_number
|
|
@@ -198,11 +200,28 @@ class LeafletChoroplethMap:
|
|
|
198
200
|
|
|
199
201
|
def _get_colorscale(self):
|
|
200
202
|
if self.color_scale_is_discrete:
|
|
201
|
-
discrete_colours = ["#217847", "#23BBBE", "#8CCE69", "#FFEA80"]
|
|
202
203
|
if len(self.df[self.column_to_plot].unique()) == 3:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
return ["#080C54", "#1F9EB7", "#CDE594"]
|
|
205
|
+
if len(self.df[self.column_to_plot].unique()) == 4:
|
|
206
|
+
return ["#080C54", "#1F9EB7", "#80C6A3", "#CDE594"]
|
|
207
|
+
if len(self.df[self.column_to_plot].unique()) == 5:
|
|
208
|
+
return [
|
|
209
|
+
"#080C54",
|
|
210
|
+
"#186290",
|
|
211
|
+
"#1F9EB7",
|
|
212
|
+
"#80C6A3",
|
|
213
|
+
"#CDE594",
|
|
214
|
+
]
|
|
215
|
+
if len(self.df[self.column_to_plot].unique()) == 6:
|
|
216
|
+
return [
|
|
217
|
+
"#080C54",
|
|
218
|
+
"#186290",
|
|
219
|
+
"#1F9EB7",
|
|
220
|
+
"#80C6A3",
|
|
221
|
+
"#CDE594",
|
|
222
|
+
"#ffffcc",
|
|
223
|
+
]
|
|
224
|
+
return ["#80C6A3", "#186290"]
|
|
206
225
|
|
|
207
226
|
def _get_color_bar_categories(self):
|
|
208
227
|
return (
|
|
@@ -215,10 +234,10 @@ class LeafletChoroplethMap:
|
|
|
215
234
|
)
|
|
216
235
|
|
|
217
236
|
def _get_colorbar(self):
|
|
237
|
+
top_margin = ("100px" if self.colorbar_title else None,)
|
|
218
238
|
if self.color_scale_is_discrete:
|
|
219
|
-
self._get_color_bar_categories()
|
|
220
239
|
return dlx.categorical_colorbar(
|
|
221
|
-
categories=self._get_color_bar_categories(),
|
|
240
|
+
categories=self._get_color_bar_categories(),
|
|
222
241
|
colorscale=self._get_colorscale()[::-1],
|
|
223
242
|
width=30,
|
|
224
243
|
height=200,
|
|
@@ -228,8 +247,10 @@ class LeafletChoroplethMap:
|
|
|
228
247
|
"backgroundColor": "white",
|
|
229
248
|
"borderRadius": "4px",
|
|
230
249
|
"fontSize": "16px",
|
|
250
|
+
"marginTop": top_margin,
|
|
231
251
|
},
|
|
232
252
|
)
|
|
253
|
+
|
|
233
254
|
min_value = self.df.select(pl.min(self.column_to_plot)).item()
|
|
234
255
|
colorbar_min = min(min_value, 0)
|
|
235
256
|
max_value = self.df.select(pl.max(self.column_to_plot)).item()
|
|
@@ -243,9 +264,19 @@ class LeafletChoroplethMap:
|
|
|
243
264
|
three_quarter_value,
|
|
244
265
|
max_value,
|
|
245
266
|
]
|
|
267
|
+
|
|
268
|
+
tick_text = [format_number_into_thousands_or_millions(x) for x in tick_values]
|
|
269
|
+
|
|
270
|
+
# If duplicates appear in tick_text, change rounding
|
|
271
|
+
if len(set(tick_text)) < len(tick_text):
|
|
272
|
+
tick_text = [
|
|
273
|
+
format_number_into_thousands_or_millions(x, 1) for x in tick_values
|
|
274
|
+
]
|
|
275
|
+
|
|
246
276
|
tick_text = [
|
|
247
|
-
|
|
248
|
-
|
|
277
|
+
str(int(val)) if val < 1000 else text
|
|
278
|
+
for text, val in zip(tick_text, tick_values)
|
|
279
|
+
] # values less than 1000 are ints
|
|
249
280
|
|
|
250
281
|
return dl.Colorbar(
|
|
251
282
|
colorscale=self._get_colorscale(),
|
|
@@ -258,27 +289,35 @@ class LeafletChoroplethMap:
|
|
|
258
289
|
"backgroundColor": "white",
|
|
259
290
|
"padding": "5px",
|
|
260
291
|
"borderRadius": "4px",
|
|
261
|
-
"marginTop":
|
|
292
|
+
"marginTop": top_margin,
|
|
262
293
|
},
|
|
263
294
|
tickValues=tick_values,
|
|
264
295
|
tickText=tick_text, # Optional, makes labels look cleaner
|
|
265
296
|
)
|
|
266
297
|
|
|
267
298
|
def _get_colorbar_title(self, enable_zoom: bool = False):
|
|
268
|
-
if self.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
299
|
+
if self.colorbar_title:
|
|
300
|
+
top = "70px" if enable_zoom is False else "140px"
|
|
301
|
+
return html.Div(
|
|
302
|
+
self.colorbar_title,
|
|
303
|
+
style={
|
|
304
|
+
"position": "absolute",
|
|
305
|
+
"top": top, # Adjusted to place above the colorbar
|
|
306
|
+
"left": "10px", # Align with the left side of the colorbar
|
|
307
|
+
"background": "white",
|
|
308
|
+
"padding": "2px 6px",
|
|
309
|
+
"borderRadius": "5px",
|
|
310
|
+
"fontWeight": "bold",
|
|
311
|
+
"fontSize": "14px",
|
|
312
|
+
"zIndex": "999", # Ensure it appears above map elements
|
|
313
|
+
},
|
|
314
|
+
)
|
|
315
|
+
return None
|
|
316
|
+
|
|
317
|
+
def resolve_colorbar_title(self, colorbar_title: str):
|
|
318
|
+
"""Returns text for colorbar title."""
|
|
319
|
+
if colorbar_title is None:
|
|
320
|
+
return None # exclude title
|
|
321
|
+
if colorbar_title == "default":
|
|
322
|
+
return self.hover_text_columns[0]
|
|
323
|
+
return colorbar_title # custom title
|