2020-09-03 10:18:28 +01:00
|
|
|
<script src="{{ asset_url('javascripts/leaflet.js') }}"></script>
|
|
|
|
|
<script>
|
|
|
|
|
var polygons = []
|
|
|
|
|
|
Vary bleed amount based on population density
There are basically two kinds of 4G masts:
Frequency | Range | Bandwidth
----------|-------------|----------------------------------
800MHz | Long (500m) | Low (can handle a bit of traffic)
1800Mhz | Short (5km) | High (can handle lots of traffic)
The 1800Mhz masts are better in terms of how much traffic they can
handle and how fast a connection they provide. But because they have
quite short range, it’s only economical to install them in very built up
areas†.
In more rural areas the 800MHz masts are better because they cover a
wider area, and have enough bandwidth for the lower population density.
The net effect of this is that cell broadcasts in rural areas are likely
to bleed further, because the masts they are being broadcast from are
less precise.
We can use population density as a proxy for how likely it is to be
covered by 1800Mhz masts, and therefore how much bleed we should expect.
So this commit varies the amount of bleed shown based on the population
density.
I came up with the formula based on 3 fixed points:
- The most remote areas (for example the Scottish Highlands) should have
the highest average bleed, estimated at 5km
- An town, like Crewe, should have about the same bleed as we were
estimating before (1.5km) – Pete D thinks this is about right based on
his knowledge of the area around his office in Crewe
- The most built up areas, like London boroughs, could have as little as
500m of bleed
Based on these three figures I came up with the following formula, which
roughly gives the right bleed distance (`b`) for each of their population
densities (`d`):
```
b = 5900 - (log10(d) × 1_250)
```
Plotted on a curve it looks like this:
This is based on averages – remember that the UI shows where is _likely_
to receive the alert, based on bleed, not where it’s _possible_ to
receive the alert.
Here’s what it looks like on the map:
---
†There are some additional subtleties which make this not strictly true:
- The 800Mhz masts are also used in built up areas to fill in the gaps
between the areas covered by the 1800Mhz masts
- Switching between masts is inefficient, so if you’re moving fast
through a built up area (for example on a train) your phone will only
use the 800MHz masts so that you have to handoff from one mast to
another less often
2021-03-12 09:17:42 +00:00
|
|
|
{% for polygon in broadcast_message.simple_polygons_with_bleed.as_coordinate_pairs_lat_long %}
|
2020-09-03 10:18:28 +01:00
|
|
|
polygons.push(
|
|
|
|
|
L.polygon({{polygon}}, {
|
2020-09-24 15:52:38 +01:00
|
|
|
opacity: 1,
|
|
|
|
|
color: '#005ea5',
|
|
|
|
|
fillColor: '#2B8CC4',
|
2020-08-26 10:43:55 +01:00
|
|
|
fillOpacity: 0.15,
|
2020-09-24 15:52:38 +01:00
|
|
|
weight: 2,
|
|
|
|
|
dashArray: [6, 7],
|
|
|
|
|
lineCap: 'butt'
|
2020-09-03 10:18:28 +01:00
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
{% endfor %}
|
|
|
|
|
|
|
|
|
|
{% for polygon in broadcast_message.polygons.as_coordinate_pairs_lat_long %}
|
|
|
|
|
polygons.push(
|
|
|
|
|
L.polygon({{polygon}}, {
|
2020-09-24 15:52:38 +01:00
|
|
|
color: '#0b0b0c',
|
2020-10-02 14:15:40 +01:00
|
|
|
fillColor: '#2B8CC4',
|
2020-09-24 15:52:38 +01:00
|
|
|
fillOpacity: 0.3,
|
2020-09-03 10:18:28 +01:00
|
|
|
weight: 2
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
{% endfor %}
|
|
|
|
|
|
2020-09-03 10:55:01 +01:00
|
|
|
|
2020-10-05 13:47:21 +01:00
|
|
|
var mapElement = document.getElementById('area-list-map');
|
2021-09-01 20:26:12 +01:00
|
|
|
var grandparent = mapElement.parentNode.parentNode;
|
|
|
|
|
var isInDetails = grandparent.className.indexOf('govuk-details') !== -1;
|
Add accessible name and description to map
The map is already in the tabbing order, so can be
moved to by tabbing and by programs like screen
readers or speech recognition, but it doesn't have
an accessible name so when assistive tech' that
requires this for identification gets the contents
read out instead, which is confusing.
This adds an accessible name, via aria-label, made
out of the areas the alert targets.
This also adds some help text, explaining how to
use the map via aria-describedby. This is a pretty
common pattern and is used in native UI like
selectboxes where a range of commands are
available to control the UI 'widget'. Using
aria-describedby means the help text is not used
every time the widget is focused but is available
if the user gets stuck. For example, Voiceover
announces it if the widget is focused but
not interacted with for a period of time, or when
a shortcut key is pressed.
Finally, I also added a role of 'region' because
when I tested with the NVDA screen reader, the
accessible name wasn't announced but this fixed
that. I think it's because the div isn't being
recognised as having a role without it being set
explicitly and is therefore ignored.
2021-08-27 11:18:56 +01:00
|
|
|
var areas = document.querySelectorAll('.area-list > .area-list-item');
|
|
|
|
|
var trimRegExp = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
|
|
|
|
|
var details;
|
|
|
|
|
var label;
|
|
|
|
|
|
2020-09-03 10:55:01 +01:00
|
|
|
// if element is inside a details element then to make the map render correctly we open the details element
|
|
|
|
|
// and set up the map before closing the details map after
|
Add accessible name and description to map
The map is already in the tabbing order, so can be
moved to by tabbing and by programs like screen
readers or speech recognition, but it doesn't have
an accessible name so when assistive tech' that
requires this for identification gets the contents
read out instead, which is confusing.
This adds an accessible name, via aria-label, made
out of the areas the alert targets.
This also adds some help text, explaining how to
use the map via aria-describedby. This is a pretty
common pattern and is used in native UI like
selectboxes where a range of commands are
available to control the UI 'widget'. Using
aria-describedby means the help text is not used
every time the widget is focused but is available
if the user gets stuck. For example, Voiceover
announces it if the widget is focused but
not interacted with for a period of time, or when
a shortcut key is pressed.
Finally, I also added a role of 'region' because
when I tested with the NVDA screen reader, the
accessible name wasn't announced but this fixed
that. I think it's because the div isn't being
recognised as having a role without it being set
explicitly and is therefore ignored.
2021-08-27 11:18:56 +01:00
|
|
|
if (isInDetails) {
|
2020-09-03 10:55:01 +01:00
|
|
|
details = grandparent;
|
|
|
|
|
details.open = true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-01 20:26:12 +01:00
|
|
|
function addAriaLabel (mapElement) {
|
|
|
|
|
|
|
|
|
|
function getLabelPrefix (areas) {
|
|
|
|
|
var labelPrefix = ['Map of the United Kingdom, showing the ', ' for'];
|
|
|
|
|
|
|
|
|
|
if (areas.length === 1) {
|
|
|
|
|
return labelPrefix[0] + 'area' + labelPrefix[1];
|
|
|
|
|
} else {
|
|
|
|
|
return labelPrefix[0] + 'areas' + labelPrefix[1];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function getStringOfAreas (areas) {
|
|
|
|
|
var areasLen = areas.length;
|
|
|
|
|
var areaStrings = [];
|
|
|
|
|
var idx;
|
|
|
|
|
|
|
|
|
|
function getAreaName (area) {
|
|
|
|
|
var areaString = '';
|
|
|
|
|
var childNodesLen = area.childNodes.length;
|
|
|
|
|
var childNode;
|
|
|
|
|
|
|
|
|
|
while (childNodesLen--) {
|
|
|
|
|
childNode = area.childNodes[childNodesLen];
|
|
|
|
|
if (childNode.nodeType === 3) { areaString += childNode.nodeValue; } // only use text nodes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return areaString.replace(trimRegExp, '')
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (areasLen === 1) { return getAreaName(areas[0]); }
|
|
|
|
|
|
|
|
|
|
for (idx = 0; idx < areasLen; idx++) {
|
|
|
|
|
areaStrings.push(getAreaName(areas[idx]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// always join last 2 areas with 'and', the rest with commas
|
|
|
|
|
return areaStrings.slice(0, areasLen - 1).join(', ') + ' and ' + areaStrings[areasLen - 1];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
label = getLabelPrefix(areas) + " " + getStringOfAreas(areas);
|
|
|
|
|
mapElement.setAttribute('aria-label', label);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function addAriaDescription (mapElement) {
|
|
|
|
|
|
|
|
|
|
var mapContainer = mapElement.parentNode;
|
|
|
|
|
var description = document.createElement('p');
|
|
|
|
|
var descriptionText = 'Use the arrow keys to move the map. Use the buttons to zoom the map in or out';
|
|
|
|
|
|
|
|
|
|
description.appendChild(
|
|
|
|
|
document.createTextNode(descriptionText)
|
|
|
|
|
);
|
|
|
|
|
description.setAttribute('id', 'area-list-map__description');
|
|
|
|
|
description.className = 'govuk-visually-hidden';
|
|
|
|
|
mapContainer.insertBefore(description, mapElement.nextSibling);
|
|
|
|
|
mapElement.setAttribute('aria-describedby', 'area-list-map__description');
|
|
|
|
|
};
|
|
|
|
|
|
2020-09-03 10:55:01 +01:00
|
|
|
mapElement.style.height = Math.max(320, window.innerHeight - mapElement.offsetTop - 100) + 'px';
|
Add accessible name and description to map
The map is already in the tabbing order, so can be
moved to by tabbing and by programs like screen
readers or speech recognition, but it doesn't have
an accessible name so when assistive tech' that
requires this for identification gets the contents
read out instead, which is confusing.
This adds an accessible name, via aria-label, made
out of the areas the alert targets.
This also adds some help text, explaining how to
use the map via aria-describedby. This is a pretty
common pattern and is used in native UI like
selectboxes where a range of commands are
available to control the UI 'widget'. Using
aria-describedby means the help text is not used
every time the widget is focused but is available
if the user gets stuck. For example, Voiceover
announces it if the widget is focused but
not interacted with for a period of time, or when
a shortcut key is pressed.
Finally, I also added a role of 'region' because
when I tested with the NVDA screen reader, the
accessible name wasn't announced but this fixed
that. I think it's because the div isn't being
recognised as having a role without it being set
explicitly and is therefore ignored.
2021-08-27 11:18:56 +01:00
|
|
|
mapElement.setAttribute('role', 'region');
|
2021-09-01 20:26:12 +01:00
|
|
|
addAriaLabel(mapElement);
|
|
|
|
|
addAriaDescription(mapElement);
|
2020-09-03 10:55:01 +01:00
|
|
|
|
|
|
|
|
var mymap = L.map(
|
2020-10-05 13:47:21 +01:00
|
|
|
'area-list-map',
|
2020-09-03 10:55:01 +01:00
|
|
|
{
|
|
|
|
|
scrollWheelZoom: false
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
|
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
|
|
|
|
}).addTo(mymap);
|
|
|
|
|
|
2020-09-03 10:18:28 +01:00
|
|
|
var polygonGroup = L.featureGroup(polygons).addTo(mymap);
|
|
|
|
|
mymap.fitBounds(
|
|
|
|
|
polygonGroup.getBounds(),
|
|
|
|
|
{padding: [1, 1]}
|
|
|
|
|
);
|
|
|
|
|
|
2020-09-03 10:55:01 +01:00
|
|
|
// if element is inside a details element then close the details element
|
Add accessible name and description to map
The map is already in the tabbing order, so can be
moved to by tabbing and by programs like screen
readers or speech recognition, but it doesn't have
an accessible name so when assistive tech' that
requires this for identification gets the contents
read out instead, which is confusing.
This adds an accessible name, via aria-label, made
out of the areas the alert targets.
This also adds some help text, explaining how to
use the map via aria-describedby. This is a pretty
common pattern and is used in native UI like
selectboxes where a range of commands are
available to control the UI 'widget'. Using
aria-describedby means the help text is not used
every time the widget is focused but is available
if the user gets stuck. For example, Voiceover
announces it if the widget is focused but
not interacted with for a period of time, or when
a shortcut key is pressed.
Finally, I also added a role of 'region' because
when I tested with the NVDA screen reader, the
accessible name wasn't announced but this fixed
that. I think it's because the div isn't being
recognised as having a role without it being set
explicitly and is therefore ignored.
2021-08-27 11:18:56 +01:00
|
|
|
if (isInDetails) {
|
2020-09-03 10:55:01 +01:00
|
|
|
details.open = false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-03 10:18:28 +01:00
|
|
|
</script>
|