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
|
|
|
|
from math import isclose
|
|
|
|
|
|
|
2020-08-10 10:46:31 +01:00
|
|
|
|
import pytest
|
2021-03-18 23:02:32 +00:00
|
|
|
|
from custom_polygons import BRISTOL, SKYE
|
2020-08-10 10:46:31 +01:00
|
|
|
|
|
2020-08-06 13:22:32 +01:00
|
|
|
|
from app.broadcast_areas import (
|
2020-07-24 12:54:22 +01:00
|
|
|
|
BroadcastAreasRepository,
|
2021-03-18 23:02:32 +00:00
|
|
|
|
CustomBroadcastArea,
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
broadcast_area_libraries,
|
|
|
|
|
|
)
|
2020-09-16 14:14:41 +01:00
|
|
|
|
from app.broadcast_areas.populations import (
|
|
|
|
|
|
CITY_OF_LONDON,
|
|
|
|
|
|
estimate_number_of_smartphones_for_population,
|
|
|
|
|
|
)
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
def close_enough(a, b):
|
|
|
|
|
|
return isclose(a, b, rel_tol=0.001) # Within 0.1% difference
|
|
|
|
|
|
|
|
|
|
|
|
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
def test_loads_libraries():
|
|
|
|
|
|
assert [
|
2020-07-31 13:21:21 +01:00
|
|
|
|
(library.id, library.name, library.is_group) for library in sorted(broadcast_area_libraries)
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
] == [
|
|
|
|
|
|
(
|
2020-08-13 12:25:22 +01:00
|
|
|
|
'ctry19',
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
'Countries',
|
2020-07-31 13:21:21 +01:00
|
|
|
|
False,
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
),
|
2020-07-24 16:47:12 +01:00
|
|
|
|
(
|
add counties page
What was previously ward -> local authority is now a ward -> local
authority -> county. County only covers rural counties and not
metropolitan boroughs and other unitary authorities. Previously, there
was a page full of local authorities (unitary authorities and
districts), and each one of those would have a list of electoral wards.
However, now there are counties that contain a list of districts - so
this needs a new page - a checkbox for "select the county" and then a
list of links to district pages.
If you want to select multiple districts, you'll need to go into each
one of those sub-sections in turn and click select all.
Needed to tweak the query to retrieve the list of areas in a list for a
library. Previously, it just returned anything at top level (ie: didn't
have a parent). However, rural districts now have parents (the rural
counties themselves). So the query now returns "everything that isn't a
leaf node", or in more specific terms, everything that has at least
other row referring to it as a parent. So no electoral wards, since
they dont have any children, but yes to districts and counties.
2020-09-04 15:22:32 +01:00
|
|
|
|
'wd20-lad20-ctyua19',
|
2020-08-13 12:25:22 +01:00
|
|
|
|
'Local authorities',
|
2020-07-31 13:21:21 +01:00
|
|
|
|
True,
|
2020-07-24 16:47:12 +01:00
|
|
|
|
),
|
2021-02-19 10:43:24 +00:00
|
|
|
|
(
|
|
|
|
|
|
'test',
|
|
|
|
|
|
'Test areas',
|
|
|
|
|
|
False,
|
|
|
|
|
|
),
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_loads_areas_from_library():
|
|
|
|
|
|
assert [
|
|
|
|
|
|
(area.id, area.name) for area in sorted(
|
2020-08-13 12:25:22 +01:00
|
|
|
|
broadcast_area_libraries.get('ctry19')
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
)
|
|
|
|
|
|
] == [
|
2020-08-13 12:25:22 +01:00
|
|
|
|
('ctry19-E92000001', 'England'),
|
|
|
|
|
|
('ctry19-N92000002', 'Northern Ireland'),
|
|
|
|
|
|
('ctry19-S92000003', 'Scotland'),
|
|
|
|
|
|
('ctry19-W92000004', 'Wales'),
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_examples():
|
2020-08-13 12:25:22 +01:00
|
|
|
|
countries = broadcast_area_libraries.get('ctry19').get_examples()
|
2020-09-09 17:35:17 +01:00
|
|
|
|
assert countries == 'England, Northern Ireland, Scotland and Wales'
|
2020-08-11 11:33:27 +01:00
|
|
|
|
|
add counties page
What was previously ward -> local authority is now a ward -> local
authority -> county. County only covers rural counties and not
metropolitan boroughs and other unitary authorities. Previously, there
was a page full of local authorities (unitary authorities and
districts), and each one of those would have a list of electoral wards.
However, now there are counties that contain a list of districts - so
this needs a new page - a checkbox for "select the county" and then a
list of links to district pages.
If you want to select multiple districts, you'll need to go into each
one of those sub-sections in turn and click select all.
Needed to tweak the query to retrieve the list of areas in a list for a
library. Previously, it just returned anything at top level (ie: didn't
have a parent). However, rural districts now have parents (the rural
counties themselves). So the query now returns "everything that isn't a
leaf node", or in more specific terms, everything that has at least
other row referring to it as a parent. So no electoral wards, since
they dont have any children, but yes to districts and counties.
2020-09-04 15:22:32 +01:00
|
|
|
|
wards = broadcast_area_libraries.get('wd20-lad20-ctyua19').get_examples()
|
2020-09-09 17:35:17 +01:00
|
|
|
|
assert wards == 'Aberdeen City, Aberdeenshire, Adur and 391 more…'
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('id', (
|
2020-08-13 12:25:22 +01:00
|
|
|
|
'ctry19-E92000001',
|
|
|
|
|
|
'ctry19-N92000002',
|
|
|
|
|
|
'ctry19-S92000003',
|
|
|
|
|
|
'ctry19-W92000004',
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
pytest.param('mercia', marks=pytest.mark.xfail(raises=KeyError)),
|
|
|
|
|
|
))
|
|
|
|
|
|
def test_loads_areas_from_libraries(id):
|
|
|
|
|
|
assert (
|
2020-08-13 12:25:22 +01:00
|
|
|
|
broadcast_area_libraries.get('ctry19').get(id)
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
) == (
|
|
|
|
|
|
broadcast_area_libraries.get_areas(id)[0]
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_get_names_of_areas():
|
|
|
|
|
|
areas = broadcast_area_libraries.get_areas(
|
2020-08-13 12:25:22 +01:00
|
|
|
|
'ctry19-W92000004',
|
|
|
|
|
|
'lad20-W06000014',
|
|
|
|
|
|
'ctry19-E92000001',
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
)
|
|
|
|
|
|
assert [area.name for area in sorted(areas)] == [
|
2020-08-13 12:27:55 +01:00
|
|
|
|
'England', 'Vale of Glamorgan', 'Wales',
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-07-09 19:10:51 +01:00
|
|
|
|
def test_get_areas_accepts_lists():
|
|
|
|
|
|
areas_from_list = broadcast_area_libraries.get_areas(
|
2020-08-10 13:19:16 +01:00
|
|
|
|
[
|
2020-08-13 12:25:22 +01:00
|
|
|
|
'ctry19-W92000004',
|
|
|
|
|
|
'ctry19-E92000001',
|
2020-08-10 13:19:16 +01:00
|
|
|
|
]
|
2020-07-09 19:10:51 +01:00
|
|
|
|
)
|
|
|
|
|
|
areas_from_args = broadcast_area_libraries.get_areas(
|
2020-08-13 12:25:22 +01:00
|
|
|
|
'ctry19-W92000004',
|
|
|
|
|
|
'ctry19-E92000001',
|
2020-07-09 19:10:51 +01:00
|
|
|
|
)
|
2020-08-13 12:27:55 +01:00
|
|
|
|
assert len(areas_from_args) == len(areas_from_list) == 2
|
2020-07-09 19:10:51 +01:00
|
|
|
|
assert areas_from_args == areas_from_list
|
|
|
|
|
|
|
|
|
|
|
|
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
def test_has_polygons():
|
|
|
|
|
|
|
2020-08-25 16:55:09 +01:00
|
|
|
|
england = broadcast_area_libraries.get_areas('ctry19-E92000001')[0]
|
|
|
|
|
|
scotland = broadcast_area_libraries.get_areas('ctry19-S92000003')[0]
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
|
2020-08-25 16:55:09 +01:00
|
|
|
|
assert len(england.polygons) == 35
|
|
|
|
|
|
assert len(scotland.polygons) == 195
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
|
2020-08-25 16:55:09 +01:00
|
|
|
|
assert england.polygons.as_coordinate_pairs_lat_long[0][0] == [
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
55.811085, -2.034358 # https://goo.gl/maps/wsf2LUWzYinwydMk8
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-08-25 16:55:09 +01:00
|
|
|
|
def test_polygons_are_enclosed():
|
2020-08-13 12:25:22 +01:00
|
|
|
|
england = broadcast_area_libraries.get('ctry19').get('ctry19-E92000001')
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
|
2020-08-25 16:55:09 +01:00
|
|
|
|
first_polygon = england.polygons.as_coordinate_pairs_lat_long[0]
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
assert first_polygon[0] != first_polygon[1] != first_polygon[2]
|
|
|
|
|
|
assert first_polygon[0] == first_polygon[-1]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_lat_long_order():
|
|
|
|
|
|
|
2020-08-25 16:55:09 +01:00
|
|
|
|
england = broadcast_area_libraries.get_areas('ctry19-E92000001')[0]
|
|
|
|
|
|
|
|
|
|
|
|
lat_long = england.polygons.as_coordinate_pairs_lat_long
|
|
|
|
|
|
long_lat = england.polygons.as_coordinate_pairs_long_lat
|
|
|
|
|
|
|
Add broadcast area model, loading from GeoJSON
This commit adds a new model class which can be used by any app to
interact with a broadcast area. A broadcast area is one or more polygons
representing geographical areas.
It also adds some models that make browsing collections of these areas
more straightforward. So the hierarchy looks like:
> **BroadcastAreaLibraries*
> Contains multiple libraries of broadcast area
> > **BroadcastAreaLibrary**
> > A collection of geographic areas, all of the same type, for example
> > counties or electoral wards
> > **BroadcastArea**
> > Contains one or more shapes that make up an area, for example
> > England
> > > **BroadcastArea.polygons[n]**
> > > A single shape, for example the Isle of Wight or Lindisfarne
> > > > **BroadcastArea.polygons[n][o]**
> > > > A single coordinate along a polygons
The classes support iteration, so all the areas in a library can be
looped over, for example if `countries` is an instance of
`BroadcastAreaLibrary` you can do:
```python
for country in countries:
print(country.name)
```
The `BroadcastAreaLibraries` class also provides some useful methods for
quickly getting the polygons for an area or areas, for example to
render them on a map. So if `libraries` is an instance of
`BroadcastAreaLibraries` you can do:
```python
libraries.get_polygons_for_areas_long_lat('england', 'wales')
```
This will give polygons for the Welsh mainland, the Isle of Wight,
Anglesey, etc.
The models load data from GeoJSON files, which is an open standard for
serialising geographic data. I’ve added a few example files taken from
http://geoportal.statistics.gov.uk to show how it works.
2020-07-06 10:53:40 +01:00
|
|
|
|
assert len(lat_long[0]) == len(long_lat[0]) == 2082 # Coordinates in polygon
|
|
|
|
|
|
assert len(lat_long[0][0]) == len(long_lat[0][0]) == 2 # Axes in coordinates
|
|
|
|
|
|
assert lat_long[0][0] == list(reversed(long_lat[0][0]))
|
2020-07-24 12:54:22 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_includes_electoral_wards():
|
|
|
|
|
|
|
2020-08-13 12:25:22 +01:00
|
|
|
|
areas = broadcast_area_libraries.get_areas(['wd20-E05009289'])
|
2020-07-24 12:54:22 +01:00
|
|
|
|
assert len(areas) == 1
|
2020-07-24 16:47:12 +01:00
|
|
|
|
|
|
|
|
|
|
|
2020-07-31 14:30:42 +01:00
|
|
|
|
def test_electoral_wards_are_groupable_cardiff():
|
2020-08-13 12:25:22 +01:00
|
|
|
|
areas = broadcast_area_libraries.get_areas(['lad20-W06000015'])
|
2020-07-31 14:30:42 +01:00
|
|
|
|
assert len(areas) == 1
|
|
|
|
|
|
cardiff = areas[0]
|
|
|
|
|
|
assert len(cardiff.sub_areas) == 29
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_electoral_wards_are_groupable_ealing():
|
2020-08-13 12:25:22 +01:00
|
|
|
|
areas = broadcast_area_libraries.get_areas(['lad20-E09000009'])
|
2020-07-31 14:30:42 +01:00
|
|
|
|
assert len(areas) == 1
|
|
|
|
|
|
ealing = areas[0]
|
|
|
|
|
|
assert len(ealing.sub_areas) == 23
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-07-24 16:47:12 +01:00
|
|
|
|
def test_repository_has_all_libraries():
|
|
|
|
|
|
repo = BroadcastAreasRepository()
|
|
|
|
|
|
libraries = repo.get_libraries()
|
|
|
|
|
|
|
2021-02-19 10:43:24 +00:00
|
|
|
|
assert len(libraries) == 3
|
2020-07-24 16:47:12 +01:00
|
|
|
|
assert [
|
2020-08-13 17:33:58 +01:00
|
|
|
|
('Countries', 'country'),
|
2021-02-19 10:43:24 +00:00
|
|
|
|
('Test areas', 'test area'),
|
2020-08-13 17:33:58 +01:00
|
|
|
|
('Local authorities', 'local authority'),
|
|
|
|
|
|
] == [(name, name_singular) for _, name, name_singular, _is_group in libraries]
|
2020-09-16 11:20:22 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('library', (
|
|
|
|
|
|
broadcast_area_libraries
|
|
|
|
|
|
))
|
|
|
|
|
|
def test_every_area_has_count_of_phones(library):
|
|
|
|
|
|
for area in library:
|
2021-02-19 10:43:24 +00:00
|
|
|
|
if library.id == 'test':
|
|
|
|
|
|
assert area.count_of_phones == 0
|
|
|
|
|
|
else:
|
|
|
|
|
|
assert area.count_of_phones > 0
|
2020-09-16 11:20:22 +01:00
|
|
|
|
|
|
|
|
|
|
|
2020-09-17 11:00:17 +01:00
|
|
|
|
@pytest.mark.parametrize('area_id, area_name, expected_count', (
|
|
|
|
|
|
|
|
|
|
|
|
# Unitary authority
|
|
|
|
|
|
('ctyua19-E10000014', 'Hampshire', 853_594.48),
|
|
|
|
|
|
|
|
|
|
|
|
# District
|
|
|
|
|
|
('lad20-E07000087', 'Fareham', 81_970.06),
|
|
|
|
|
|
|
|
|
|
|
|
# Ward
|
|
|
|
|
|
('wd20-E05004516', 'Fareham East', 5_684.9),
|
|
|
|
|
|
|
|
|
|
|
|
# Unitary authority
|
|
|
|
|
|
('lad20-E09000012', 'Hackney', 222_578.0),
|
|
|
|
|
|
|
|
|
|
|
|
# Ward
|
|
|
|
|
|
('wd20-E05009373', 'Hackney Downs', 11_321.169999999998),
|
|
|
|
|
|
|
|
|
|
|
|
# Special case: ward with hard-coded population
|
|
|
|
|
|
('wd20-E05011090', 'Bryher', 76.44),
|
|
|
|
|
|
|
|
|
|
|
|
# Areas with missing data
|
|
|
|
|
|
('lad20-E07000008', 'Cambridge', 0),
|
|
|
|
|
|
('lad20-E07000084', 'Basingstoke and Deane', 0),
|
|
|
|
|
|
('lad20-E07000118', 'Chorley', 0),
|
|
|
|
|
|
('lad20-E07000178', 'Oxford', 0),
|
|
|
|
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
def test_count_of_phones_for_all_levels(area_id, area_name, expected_count):
|
|
|
|
|
|
area = broadcast_area_libraries.get_areas(area_id)[0]
|
|
|
|
|
|
assert area.name == area_name
|
|
|
|
|
|
assert area.count_of_phones == expected_count
|
2020-09-16 11:20:22 +01:00
|
|
|
|
|
|
|
|
|
|
|
2020-09-17 11:09:52 +01:00
|
|
|
|
def test_city_of_london_counts_are_not_derived_from_population():
|
2020-09-16 11:20:22 +01:00
|
|
|
|
city_of_london = broadcast_area_libraries.get_areas('lad20-E09000001')[0]
|
|
|
|
|
|
|
|
|
|
|
|
assert city_of_london.name == 'City of London'
|
|
|
|
|
|
assert len(city_of_london.sub_areas) == len(CITY_OF_LONDON.WARDS) == 25
|
|
|
|
|
|
|
|
|
|
|
|
for ward in city_of_london.sub_areas:
|
|
|
|
|
|
# The population of the whole City of London is 9,401, so an
|
|
|
|
|
|
# average of 300 per ward. What we’re asserting here is that the
|
|
|
|
|
|
# count of phones is much larger, because it isn’t derived from
|
|
|
|
|
|
# the resident population.
|
|
|
|
|
|
assert ward.count_of_phones > 5_000
|
2020-09-16 14:14:41 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('population, expected_estimate', (
|
|
|
|
|
|
# Upper and lower bounds of each age range
|
|
|
|
|
|
(
|
|
|
|
|
|
[(0, 100)], 50,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(16, 100)], 100
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(24, 100)], 100,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(25, 100)], 97,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(34, 100)], 97,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(35, 100)], 91
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(44, 100)], 91,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(45, 100)], 88
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(54, 100)], 88,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(55, 100)], 73
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(64, 100)], 73,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(65, 100)], 40
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(999, 100)], 40,
|
|
|
|
|
|
),
|
|
|
|
|
|
# Multiple different ages in a single popualtion
|
|
|
|
|
|
(
|
|
|
|
|
|
[(16, 100), (54, 100)], 188
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
[(1, 1000), (66, 100)], 540
|
|
|
|
|
|
),
|
|
|
|
|
|
))
|
|
|
|
|
|
def test_estimate_number_of_smartphones_for_population(
|
|
|
|
|
|
population, expected_estimate,
|
|
|
|
|
|
):
|
|
|
|
|
|
assert estimate_number_of_smartphones_for_population(
|
|
|
|
|
|
population
|
|
|
|
|
|
) == expected_estimate
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('area, expected_phones_per_square_mile', (
|
|
|
|
|
|
(
|
|
|
|
|
|
# Islington (most dense in UK)
|
|
|
|
|
|
'lad20-E09000019', 21_348
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
# Cordwainer Ward (City of London)
|
|
|
|
|
|
# This is higher than Islington because we inflate the
|
|
|
|
|
|
# popualtion to account for daytime workers
|
|
|
|
|
|
'wd20-E05009300', 310_674
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
# Crewe East
|
|
|
|
|
|
'wd20-E05008621', 2_078),
|
|
|
|
|
|
(
|
|
|
|
|
|
# Eden (Cumbria, least dense in England)
|
|
|
|
|
|
'lad20-E07000030', 25.57
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
# Highland (least dense in UK)
|
|
|
|
|
|
'lad20-S12000017', 4.40
|
|
|
|
|
|
),
|
|
|
|
|
|
))
|
|
|
|
|
|
def test_phone_density(
|
|
|
|
|
|
area, expected_phones_per_square_mile,
|
|
|
|
|
|
):
|
|
|
|
|
|
assert close_enough(
|
|
|
|
|
|
broadcast_area_libraries.get_areas(area)[0].phone_density,
|
|
|
|
|
|
expected_phones_per_square_mile,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('area, expected_bleed_in_m, expected_bleed_in_degrees', (
|
|
|
|
|
|
(
|
|
|
|
|
|
# Islington (most dense in UK)
|
2021-03-19 15:28:13 +00:00
|
|
|
|
'lad20-E09000019', 500, 0.00449
|
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
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
# Cordwainer Ward (City of London)
|
|
|
|
|
|
# Special case because of inflated daytime population
|
|
|
|
|
|
'wd20-E05009300', 500, 0.00449
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
# Crewe East
|
|
|
|
|
|
'wd20-E05008621', 1_752, 0.01574
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
# Eden (Cumbria, least dense in England)
|
|
|
|
|
|
'lad20-E07000030', 4_140, 0.0372
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
# Highland (least dense in UK)
|
2021-03-19 15:28:13 +00:00
|
|
|
|
'lad20-S12000017', 5_000, 0.0449
|
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
|
|
|
|
),
|
2021-03-19 15:36:25 +00:00
|
|
|
|
(
|
|
|
|
|
|
# No population data available
|
2021-03-31 09:36:40 +01:00
|
|
|
|
'test-santa-claus-village-rovaniemi-a', 1_500, 0.01347
|
2021-03-19 15:36:25 +00:00
|
|
|
|
)
|
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
|
|
|
|
))
|
|
|
|
|
|
def test_estimated_bleed(
|
|
|
|
|
|
area, expected_bleed_in_m, expected_bleed_in_degrees,
|
|
|
|
|
|
):
|
|
|
|
|
|
assert close_enough(
|
|
|
|
|
|
broadcast_area_libraries.get_areas(area)[0].estimated_bleed_in_m,
|
|
|
|
|
|
expected_bleed_in_m,
|
|
|
|
|
|
)
|
|
|
|
|
|
assert close_enough(
|
|
|
|
|
|
broadcast_area_libraries.get_areas(area)[0].estimated_bleed_in_degrees,
|
|
|
|
|
|
expected_bleed_in_degrees,
|
|
|
|
|
|
)
|
2021-03-18 23:02:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('polygon, expected_possible_overlaps, expected_count_of_phones', (
|
|
|
|
|
|
(
|
|
|
|
|
|
BRISTOL,
|
|
|
|
|
|
[
|
|
|
|
|
|
'Ashley',
|
|
|
|
|
|
'Bedminster',
|
|
|
|
|
|
'Central',
|
|
|
|
|
|
'Clifton',
|
|
|
|
|
|
'Clifton Down',
|
|
|
|
|
|
'Cotham',
|
|
|
|
|
|
'Hotwells and Harbourside',
|
|
|
|
|
|
'Knowle',
|
|
|
|
|
|
'Lawrence Hill',
|
|
|
|
|
|
'Southville',
|
|
|
|
|
|
'Stoke Bishop',
|
|
|
|
|
|
'Windmill Hill',
|
|
|
|
|
|
],
|
|
|
|
|
|
73_496,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
SKYE,
|
|
|
|
|
|
[
|
|
|
|
|
|
'Caol and Mallaig',
|
|
|
|
|
|
'Eilean á Chèo',
|
|
|
|
|
|
'Na Hearadh agus Ceann a Deas nan Loch',
|
|
|
|
|
|
'Wester Ross, Strathpeffer and Lochalsh',
|
|
|
|
|
|
],
|
|
|
|
|
|
3_517,
|
|
|
|
|
|
),
|
|
|
|
|
|
))
|
|
|
|
|
|
def test_count_of_phones_for_custom_area(
|
|
|
|
|
|
polygon,
|
|
|
|
|
|
expected_possible_overlaps,
|
|
|
|
|
|
expected_count_of_phones,
|
|
|
|
|
|
):
|
|
|
|
|
|
area = CustomBroadcastArea(
|
|
|
|
|
|
name='Example',
|
|
|
|
|
|
polygons=[polygon],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
assert sorted(
|
|
|
|
|
|
overlap.name for overlap in area.overlapping_areas
|
|
|
|
|
|
) == expected_possible_overlaps
|
|
|
|
|
|
|
|
|
|
|
|
assert close_enough(area.count_of_phones, expected_count_of_phones)
|