diff --git a/app/__init__.py b/app/__init__.py index ff1880710..f6a137446 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -76,6 +76,7 @@ from app.formatters import ( recipient_count, recipient_count_label, round_to_significant_figures, + square_metres_to_square_miles, valid_phone_number, ) from app.models.organisation import Organisation @@ -572,6 +573,7 @@ def add_template_filters(application): message_count_noun, format_mobile_network, format_yes_no, + square_metres_to_square_miles, ]: application.add_template_filter(fn) diff --git a/app/broadcast_areas/broadcast-areas.sqlite3 b/app/broadcast_areas/broadcast-areas.sqlite3 index c991753f1..eab6b281b 100644 Binary files a/app/broadcast_areas/broadcast-areas.sqlite3 and b/app/broadcast_areas/broadcast-areas.sqlite3 differ diff --git a/app/broadcast_areas/create-broadcast-areas-db.py b/app/broadcast_areas/create-broadcast-areas-db.py index 3a9c39ee0..fbcc143df 100755 --- a/app/broadcast_areas/create-broadcast-areas-db.py +++ b/app/broadcast_areas/create-broadcast-areas-db.py @@ -44,14 +44,30 @@ def simplify_geometry(feature): def clean_up_invalid_polygons(polygons, indent=" "): + """ + This function expects a list of lists of coordinates defined in degrees + """ for index, polygon in enumerate(polygons): shapely_polygon = Polygon(polygon) - if shapely_polygon.is_valid: + # Some of our data has points which are incredibly close + # together. In some cases they are close enough to be duplicates + # at a given precision, which makes an invalid topology. In + # other cases they are close enough that, when converting from + # one coordinate system to another, they shift about enough to + # create self-intersection. The fix in both cases is to reduce + # the precision of the coordinates and then apply simplification + # with a tolerance of 0. + simplified_polygon = wkt.loads(wkt.dumps( + shapely_polygon, + rounding_precision=Polygons.output_precision_in_decimal_places - 1 + )).simplify(0) + + if simplified_polygon.is_valid: print( # noqa: T001 f"{indent}Polygon {index + 1}/{len(polygons)} is valid" ) - yield polygon + yield simplified_polygon else: invalid_polygons.append(shapely_polygon) @@ -59,7 +75,7 @@ def clean_up_invalid_polygons(polygons, indent=" "): # We’ve found polygons where all the points line up, so they # don’t have an area. They wouldn’t contribute to a broadcast # so we can ignore them. - if shapely_polygon.area == 0: + if simplified_polygon.area == 0: print( # noqa: T001 f"{indent}Polygon {index + 1}/{len(polygons)} has 0 area, skipping" ) @@ -69,18 +85,6 @@ def clean_up_invalid_polygons(polygons, indent=" "): f"{indent}Polygon {index + 1}/{len(polygons)} needs fixing..." ) - # The simplest kind of invalid polygon is one that has two - # duplicate points in a row at a given precision, so the - # first thing to try is removing those points using - # simplification with a tolerance of 0 - polygon_with_duplicate_points_removed = wkt.loads( - wkt.dumps(shapely_polygon, rounding_precision=5) - ).simplify(0) - - if polygon_with_duplicate_points_removed.is_valid: - yield polygon_with_duplicate_points_removed - continue - # Buffering with a size of 0 is a trick to make valid # geometries from polygons that self intersect buffered = shapely_polygon.buffer(0) @@ -101,7 +105,7 @@ def clean_up_invalid_polygons(polygons, indent=" "): # Make sure the polygon is now valid, and that we haven’t # drastically transformed the polygon by ‘fixing’ it assert fixed_polygon.is_valid - assert isclose(fixed_polygon.area, polygon.area, rel_tol=0.001) + assert isclose(fixed_polygon.area, shapely_polygon.area, rel_tol=0.001) print( # noqa: T001 f"{indent}Polygon {index + 1}/{len(polygons)} fixed!" @@ -115,14 +119,22 @@ def polygons_and_simplified_polygons(feature): # cheat and shortcut out return [], [] - polygons = Polygons(simplify_geometry(feature)) - polygons = list(clean_up_invalid_polygons(polygons)) - polygons = Polygons(polygons) + raw_polygons = simplify_geometry(feature) + clean_raw_polygons = [ + [[x, y] for x, y in polygon.exterior.coords] + for polygon in clean_up_invalid_polygons(raw_polygons) + ] + polygons = Polygons(clean_raw_polygons) full_resolution = polygons.remove_too_small smoothed = full_resolution.smooth simplified = smoothed.simplify + if not (len(full_resolution) or len(simplified)): + raise RuntimeError( + 'Polygon of 0 size found' + ) + print( # noqa: T001 f' Original:{full_resolution.point_count: >5} points' f' Smoothed:{smoothed.point_count: >5} points' @@ -138,17 +150,17 @@ def polygons_and_simplified_polygons(feature): 'Polygons.perimeter_to_buffer_ratio)' ) - output = ( + output = [ full_resolution.as_coordinate_pairs_long_lat, simplified.as_coordinate_pairs_long_lat, - ) + ] # Check that the simplification process hasn’t introduced bad data for dataset in output: for polygon in dataset: assert Polygon(polygon).is_valid - return output + return output + [simplified.utm_crs] def estimate_number_of_smartphones_in_area(country_or_ward_code): @@ -256,13 +268,14 @@ def add_test_areas(): print() # noqa: T001 print(f_name) # noqa: T001 - feature, _ = polygons_and_simplified_polygons( + feature, _, utm_crs = polygons_and_simplified_polygons( feature["geometry"] ) areas_to_add.append([ f'{dataset_id}-{f_id}', f_name, dataset_id, None, feature, feature, + utm_crs, 0, ]) @@ -288,7 +301,7 @@ def add_demo_areas(): print() # noqa: T001 print(f_name) # noqa: T001 - feature, _ = polygons_and_simplified_polygons( + feature, _, utm_crs = polygons_and_simplified_polygons( feature["geometry"] ) @@ -298,6 +311,7 @@ def add_demo_areas(): f'{dataset_id}-{f_id}', f_name, dataset_id, None, feature, feature, + utm_crs, f_count_of_phones, ]) @@ -322,13 +336,14 @@ def add_countries(): print() # noqa: T001 print(f_name) # noqa: T001 - feature, simple_feature = ( + feature, simple_feature, utm_crs = ( polygons_and_simplified_polygons(feature["geometry"]) ) areas_to_add.append([ f'ctry19-{f_id}', f_name, dataset_id, None, feature, simple_feature, + utm_crs, estimate_number_of_smartphones_in_area(f_id), ]) @@ -364,7 +379,7 @@ def _add_electoral_wards(dataset_id): try: la_id = "lad20-" + ward_code_to_la_id_mapping[ward_code] - feature, simple_feature = ( + feature, simple_feature, utm_crs = ( polygons_and_simplified_polygons(feature["geometry"]) ) @@ -375,6 +390,7 @@ def _add_electoral_wards(dataset_id): ward_id, ward_name, dataset_id, la_id, feature, simple_feature, + utm_crs, estimate_number_of_smartphones_in_area(ward_code), ]) @@ -397,7 +413,7 @@ def _add_local_authorities(dataset_id): group_id = "lad20-" + la_id - feature, simple_feature = ( + feature, simple_feature, utm_crs = ( polygons_and_simplified_polygons(feature["geometry"]) ) @@ -409,6 +425,7 @@ def _add_local_authorities(dataset_id): 'ctyua19-' + ctyua_id if ctyua_id else None, feature, simple_feature, + utm_crs, None, ]) repo.insert_broadcast_areas(areas_to_add, keep_old_polygons) @@ -427,7 +444,7 @@ def _add_counties_and_unitary_authorities(dataset_id): group_id = "ctyua19-" + ctyua_id - feature, simple_feature = ( + feature, simple_feature, utm_crs = ( polygons_and_simplified_polygons(feature["geometry"]) ) @@ -435,6 +452,7 @@ def _add_counties_and_unitary_authorities(dataset_id): group_id, group_name, dataset_id, None, feature, simple_feature, + utm_crs, None, ]) diff --git a/app/broadcast_areas/models.py b/app/broadcast_areas/models.py index ddd163a53..d9286988a 100644 --- a/app/broadcast_areas/models.py +++ b/app/broadcast_areas/models.py @@ -7,6 +7,8 @@ from notifications_utils.serialised_model import SerialisedModelCollection from rtreelib import Rect from werkzeug.utils import cached_property +from app.formatters import square_metres_to_square_miles + from .populations import CITY_OF_LONDON from .repo import BroadcastAreasRepository, rtree_index @@ -55,13 +57,13 @@ class BaseBroadcastArea(ABC): @cached_property def simple_polygons_with_bleed(self): - return self.simple_polygons.bleed_by(self.estimated_bleed_in_degrees) + return self.simple_polygons.bleed_by(self.estimated_bleed_in_m) @cached_property def phone_density(self): - if not self.polygons.estimated_area: + if not self.simple_polygons.estimated_area: return 0 - return self.count_of_phones / self.polygons.estimated_area + return self.count_of_phones / square_metres_to_square_miles(self.simple_polygons.estimated_area) @property def estimated_bleed_in_m(self): @@ -72,14 +74,10 @@ class BaseBroadcastArea(ABC): range masts, so the typical bleed will be high (up to 5,000m). ''' if self.phone_density < 1: - return Polygons.approx_bleed_in_degrees * Polygons.approx_metres_to_degree + return Polygons.approx_bleed_in_m estimated_bleed = 5_900 - (math.log(self.phone_density, 10) * 1_250) return max(500, min(estimated_bleed, 5000)) - @property - def estimated_bleed_in_degrees(self): - return self.estimated_bleed_in_m / Polygons.approx_metres_to_degree - class BroadcastArea(BaseBroadcastArea, SortableMixin): @@ -97,20 +95,27 @@ class BroadcastArea(BaseBroadcastArea, SortableMixin): @classmethod def from_row_with_simple_polygons(cls, row): instance = cls(row[:4]) - instance.simple_polygons = Polygons(row[4]) + instance.simple_polygons = Polygons( + row[4], + utm_crs=row[5], + ) return instance @cached_property def polygons(self): + polygons, utm_crs = BroadcastAreasRepository().get_polygons_for_area(self.id) return Polygons( - BroadcastAreasRepository().get_polygons_for_area(self.id) + polygons, utm_crs=utm_crs ) @cached_property def simple_polygons(self): - return Polygons( + simple_polygons, utm_crs = ( BroadcastAreasRepository().get_simple_polygons_for_area(self.id) ) + return Polygons( + simple_polygons, utm_crs=utm_crs + ).utm_polygons @cached_property def sub_areas(self): @@ -123,7 +128,7 @@ class BroadcastArea(BaseBroadcastArea, SortableMixin): def count_of_phones(self): if self.id.endswith(CITY_OF_LONDON.WARDS): return CITY_OF_LONDON.DAYTIME_POPULATION * ( - self.polygons.estimated_area / CITY_OF_LONDON.AREA_SQUARE_MILES + self.polygons.estimated_area / CITY_OF_LONDON.AREA_SQUARE_METRES ) if self.sub_areas: return sum(area.count_of_phones for area in self.sub_areas) @@ -162,14 +167,19 @@ class CustomBroadcastArea(BaseBroadcastArea): @classmethod def from_polygon_objects(cls, polygon_objects): - return cls(name=None, polygons=polygon_objects.as_coordinate_pairs_lat_long) + instance = cls(name=None) + instance.polygons = polygon_objects + return instance - @property + @cached_property def polygons(self): return Polygons( # Polygons in the DB are stored with the coordinate pair # order flipped – this flips them back again - Polygons(self._polygons).as_coordinate_pairs_lat_long + [ + [[lat, long] for long, lat in polygon] + for polygon in self._polygons + ] ) simple_polygons = polygons @@ -188,7 +198,7 @@ class CustomBroadcastArea(BaseBroadcastArea): return broadcast_area_libraries.get_areas_with_simple_polygons([ # We only index electoral wards in the RTree overlap.data for overlap in rtree_index.query( - Rect(*self.polygons.bounds) + Rect(*self.simple_polygons.bounds) ) ]) diff --git a/app/broadcast_areas/populations.py b/app/broadcast_areas/populations.py index d10b2d8f1..39db91786 100644 --- a/app/broadcast_areas/populations.py +++ b/app/broadcast_areas/populations.py @@ -31,8 +31,9 @@ class CITY_OF_LONDON: ) # https://data.london.gov.uk/blog/daytime-population-of-london-2014/ DAYTIME_POPULATION = 553_000 - # Approx area of the polygons we’re storing, not the actual area - AREA_SQUARE_MILES = 1.78 + # Exact area of the polygons we’re storing, which matches the 2.9km² + # given by https://en.wikipedia.org/wiki/City_of_London + AREA_SQUARE_METRES = 2_885_598 class BRYHER: diff --git a/app/broadcast_areas/repo.py b/app/broadcast_areas/repo.py index b18a46456..155004805 100644 --- a/app/broadcast_areas/repo.py +++ b/app/broadcast_areas/repo.py @@ -54,7 +54,8 @@ class BroadcastAreasRepository(object): CREATE TABLE broadcast_area_polygons ( id TEXT PRIMARY KEY, polygons TEXT NOT NULL, - simple_polygons TEXT NOT NULL + simple_polygons TEXT NOT NULL, + utm_crs TEXT NOT NULL )""") conn.execute(""" @@ -98,19 +99,19 @@ class BroadcastAreasRepository(object): features_q = """ INSERT INTO broadcast_area_polygons ( id, - polygons, simple_polygons + polygons, simple_polygons, utm_crs ) - VALUES (?, ?, ?) + VALUES (?, ?, ?, ?) """ with self.conn() as conn: - for id, name, area_id, group, polygons, simple_polygons, count_of_phones in areas: + for id, name, area_id, group, polygons, simple_polygons, utm_crs, count_of_phones in areas: conn.execute(areas_q, ( id, name, area_id, group, count_of_phones )) if not keep_old_features: conn.execute(features_q, ( - id, json.dumps(polygons), json.dumps(simple_polygons), + id, json.dumps(polygons), json.dumps(simple_polygons), utm_crs )) def query(self, sql, *args): @@ -143,7 +144,7 @@ class BroadcastAreasRepository(object): def get_areas_with_simple_polygons(self, area_ids): q = """ - SELECT broadcast_areas.id, name, count_of_phones, broadcast_area_library_id, simple_polygons + SELECT broadcast_areas.id, name, count_of_phones, broadcast_area_library_id, simple_polygons, utm_crs FROM broadcast_areas JOIN broadcast_area_polygons on broadcast_area_polygons.id = broadcast_areas.id WHERE broadcast_areas.id IN ({}) @@ -152,7 +153,7 @@ class BroadcastAreasRepository(object): results = self.query(q, *area_ids) areas = [ - (row[0], row[1], row[2], row[3], json.loads(row[4])) + (row[0], row[1], row[2], row[3], json.loads(row[4]), row[5]) for row in results ] @@ -231,22 +232,22 @@ class BroadcastAreasRepository(object): def get_polygons_for_area(self, area_id): q = """ - SELECT polygons + SELECT polygons, utm_crs FROM broadcast_area_polygons WHERE id = ? """ results = self.query(q, area_id) - return json.loads(results[0][0]) + return json.loads(results[0][0]), results[0][1] def get_simple_polygons_for_area(self, area_id): q = """ - SELECT simple_polygons + SELECT simple_polygons, utm_crs FROM broadcast_area_polygons WHERE id = ? """ results = self.query(q, area_id) - return json.loads(results[0][0]) + return json.loads(results[0][0]), results[0][1] diff --git a/app/broadcast_areas/rtree.pickle b/app/broadcast_areas/rtree.pickle index 1b547e04b..58dc22f9e 100644 Binary files a/app/broadcast_areas/rtree.pickle and b/app/broadcast_areas/rtree.pickle differ diff --git a/app/formatters.py b/app/formatters.py index aa8d42663..c0dda4738 100644 --- a/app/formatters.py +++ b/app/formatters.py @@ -524,3 +524,7 @@ def format_yes_no(value, yes='Yes', no='No', none='No'): if value is None: return none return yes if value else no + + +def square_metres_to_square_miles(area): + return area * 3.86e-7 diff --git a/app/models/broadcast_message.py b/app/models/broadcast_message.py index e6d8be55b..4a2e2544b 100644 --- a/app/models/broadcast_message.py +++ b/app/models/broadcast_message.py @@ -88,7 +88,7 @@ class BroadcastMessage(JSONModel): broadcast_message_id=broadcast_message_id, )) - @property + @cached_property def areas(self): if 'ids' in self._dict['areas']: library_areas = self.get_areas(self.area_ids) @@ -221,7 +221,8 @@ class BroadcastMessage(JSONModel): polygons = Polygons( list(itertools.chain(*( getattr(area, area_attribute) for area in self.areas - ))) + ))), + utm_crs=self.areas[0].simple_polygons.utm_polygons.utm_crs, ) if area_attribute != 'polygons' and len(self.areas) > 1: # We’re combining simplified polygons from multiple areas so we diff --git a/app/templates/views/broadcast/macros/area-map.html b/app/templates/views/broadcast/macros/area-map.html index ceef09776..6fdd9c1dd 100644 --- a/app/templates/views/broadcast/macros/area-map.html +++ b/app/templates/views/broadcast/macros/area-map.html @@ -6,7 +6,7 @@ - An area of {{ (broadcast_message.simple_polygons.estimated_area)|round_to_significant_figures(1)|format_thousands }} square miles  + An area of {{ (broadcast_message.simple_polygons.estimated_area)|square_metres_to_square_miles|round_to_significant_figures(1)|format_thousands }} square miles  Will get @@ -20,7 +20,7 @@ - An extra area of {{ (broadcast_message.simple_polygons_with_bleed.estimated_area - broadcast_message.simple_polygons.estimated_area)|round_to_significant_figures(1)|format_thousands }} square miles is  + An extra area of {{ (broadcast_message.simple_polygons_with_bleed.estimated_area - broadcast_message.simple_polygons.estimated_area)|square_metres_to_square_miles|round_to_significant_figures(1)|format_thousands }} square miles is  Likely to get diff --git a/requirements.in b/requirements.in index 7fea7cd4a..138d8d730 100644 --- a/requirements.in +++ b/requirements.in @@ -31,7 +31,7 @@ pyproj==3.2.1 awscli-cwlogs>=1.4,<1.5 itsdangerous==1.1.0 # pyup: <2 -notifications-utils @ git+https://github.com/alphagov/notifications-utils.git@48.0.0 +notifications-utils @ git+https://github.com/alphagov/notifications-utils.git@49.0.0 govuk-frontend-jinja @ git+https://github.com/alphagov/govuk-frontend-jinja.git@v0.5.8-alpha # cryptography 3.4+ incorporates Rust code, which isn't supported on PaaS diff --git a/requirements.txt b/requirements.txt index 44cb309fb..23c0b431d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -123,7 +123,7 @@ mistune==0.8.4 # via notifications-utils notifications-python-client==6.3.0 # via -r requirements.in -notifications-utils @ git+https://github.com/alphagov/notifications-utils.git@48.0.0 +notifications-utils @ git+https://github.com/alphagov/notifications-utils.git@49.0.0 # via -r requirements.in openpyxl==3.0.7 # via pyexcel-xlsx @@ -165,7 +165,9 @@ pyparsing==2.4.7 pypdf2==1.26.0 # via notifications-utils pyproj==3.2.1 - # via -r requirements.in + # via + # -r requirements.in + # notifications-utils python-dateutil==2.8.1 # via # awscli-cwlogs diff --git a/tests/app/broadcast_areas/custom_polygons.py b/tests/app/broadcast_areas/custom_polygons.py index e79360649..0ef23bd08 100644 --- a/tests/app/broadcast_areas/custom_polygons.py +++ b/tests/app/broadcast_areas/custom_polygons.py @@ -15,11 +15,11 @@ SKYE = [ ] SANTA_A = [ - [25.8890, 66.5500], - [25.8890, 66.551], - [25.8910, 66.551], - [25.8910, 66.5500], - [25.889, 66.55000], + [66.5500, 25.8890], + [66.551, 25.8890], + [66.551, 25.8910], + [66.5500, 25.8910], + [66.55000, 25.889], ] BURFORD = [ diff --git a/tests/app/broadcast_areas/test_broadcast_area.py b/tests/app/broadcast_areas/test_broadcast_area.py index ef862f10d..a5d5090b5 100644 --- a/tests/app/broadcast_areas/test_broadcast_area.py +++ b/tests/app/broadcast_areas/test_broadcast_area.py @@ -101,7 +101,7 @@ def test_has_polygons(): assert len(scotland.polygons) == 195 assert england.polygons.as_coordinate_pairs_lat_long[0][0] == [ - 55.811085, -2.034358 # https://goo.gl/maps/wsf2LUWzYinwydMk8 + 55.81108, -2.03436 # https://goo.gl/maps/HMFHGogohXdh9ggo8 ] @@ -276,24 +276,24 @@ def test_estimate_number_of_smartphones_for_population( @pytest.mark.parametrize('area, expected_phones_per_square_mile', ( ( # Islington (most dense in UK) - 'lad20-E09000019', 21_348 + 'lad20-E09000019', 32_662 ), ( # Cordwainer Ward (City of London) # This is higher than Islington because we inflate the # popualtion to account for daytime workers - 'wd20-E05009300', 310_674 + 'wd20-E05009300', 392_870 ), ( # Crewe East - 'wd20-E05008621', 2_078), + 'wd20-E05008621', 3_289), ( # Eden (Cumbria, least dense in England) - 'lad20-E07000030', 25.57 + 'lad20-E07000030', 43.41 ), ( # Highland (least dense in UK) - 'lad20-S12000017', 4.40 + 'lad20-S12000017', 6.97 ), )) def test_phone_density( @@ -305,44 +305,40 @@ def test_phone_density( ) -@pytest.mark.parametrize('area, expected_bleed_in_m, expected_bleed_in_degrees', ( +@pytest.mark.parametrize('area, expected_bleed_in_m', ( ( # Islington (most dense in UK) - 'lad20-E09000019', 500, 0.00449 + 'lad20-E09000019', 500 ), ( # Cordwainer Ward (City of London) # Special case because of inflated daytime population - 'wd20-E05009300', 500, 0.00449 + 'wd20-E05009300', 500 ), ( # Crewe East - 'wd20-E05008621', 1_752, 0.01574 + 'wd20-E05008621', 1_504 ), ( # Eden (Cumbria, least dense in England) - 'lad20-E07000030', 4_140, 0.0372 + 'lad20-E07000030', 3_852 ), ( # Highland (least dense in UK) - 'lad20-S12000017', 5_000, 0.0449 + 'lad20-S12000017', 4_846 ), ( # No population data available - 'test-santa-claus-village-rovaniemi-a', 1_500, 0.01347 + 'test-santa-claus-village-rovaniemi-a', 1_500 ) )) def test_estimated_bleed( - area, expected_bleed_in_m, expected_bleed_in_degrees, + area, expected_bleed_in_m ): 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, - ) @pytest.mark.parametrize('polygon, expected_possible_overlaps, expected_count_of_phones', ( @@ -362,7 +358,7 @@ def test_estimated_bleed( 'Stoke Bishop', 'Windmill Hill', ], - 73_119, + 72_817, ), ( SKYE, @@ -372,7 +368,7 @@ def test_estimated_bleed( 'Na Hearadh agus Ceann a Deas nan Loch', 'Wester Ross, Strathpeffer and Lochalsh', ], - 3_534, + 3_413, ), )) def test_count_of_phones_for_custom_area( diff --git a/tests/app/broadcast_areas/test_models.py b/tests/app/broadcast_areas/test_models.py index 23b9933d2..0437ac561 100644 --- a/tests/app/broadcast_areas/test_models.py +++ b/tests/app/broadcast_areas/test_models.py @@ -5,7 +5,7 @@ from tests.app.broadcast_areas.custom_polygons import BRISTOL, SANTA_A, SKYE @pytest.mark.parametrize(('simple_polygon', 'expected_wards_length'), [ - (SKYE, 2), + (SKYE, 1), (BRISTOL, 12), (SANTA_A, 0) # does not overlap with UK ]) diff --git a/tests/app/main/views/test_broadcast.py b/tests/app/main/views/test_broadcast.py index dcdec0f9c..21e2f815d 100644 --- a/tests/app/main/views/test_broadcast.py +++ b/tests/app/main/views/test_broadcast.py @@ -837,8 +837,8 @@ def test_broadcast_page( 'England Remove England', 'Scotland Remove Scotland', ], [ - 'An area of 200,000 square miles Will get the alert', - 'An extra area of 8,000 square miles is Likely to get the alert', + 'An area of 100,000 square miles Will get the alert', + 'An extra area of 6,000 square miles is Likely to get the alert', '40,000,000 phones estimated', ]), ([ @@ -854,8 +854,8 @@ def test_broadcast_page( 'Penrith South Remove Penrith South', 'Penrith West Remove Penrith West', ], [ - 'An area of 6 square miles Will get the alert', - 'An extra area of 20 square miles is Likely to get the alert', + 'An area of 4 square miles Will get the alert', + 'An extra area of 10 square miles is Likely to get the alert', '9,000 to 10,000 phones', ]), ([ @@ -863,17 +863,17 @@ def test_broadcast_page( ], [ 'Islington Remove Islington', ], [ - 'An area of 10 square miles Will get the alert', - 'An extra area of 5 square miles is Likely to get the alert', - '200,000 to 500,000 phones', + 'An area of 6 square miles Will get the alert', + 'An extra area of 4 square miles is Likely to get the alert', + '200,000 to 600,000 phones', ]), ([ 'ctyua19-E10000019', ], [ 'Lincolnshire Remove Lincolnshire', ], [ - 'An area of 4,000 square miles Will get the alert', - 'An extra area of 700 square miles is Likely to get the alert', + 'An area of 2,000 square miles Will get the alert', + 'An extra area of 500 square miles is Likely to get the alert', '500,000 to 600,000 phones', ]), ([ @@ -882,8 +882,8 @@ def test_broadcast_page( ], [ 'Lincolnshire Remove Lincolnshire', 'North Yorkshire Remove North Yorkshire', ], [ - 'An area of 10,000 square miles Will get the alert', - 'An extra area of 2,000 square miles is Likely to get the alert', + 'An area of 6,000 square miles Will get the alert', + 'An extra area of 1,000 square miles is Likely to get the alert', '1,000,000 phones estimated', ]), )) @@ -936,7 +936,7 @@ def test_preview_broadcast_areas_page( [[7, 8], [9, 10], [11, 12]], ], [ - 'An area of 700 square miles Will get the alert', + 'An area of 1,000 square miles Will get the alert', 'An extra area of 1,000 square miles is Likely to get the alert', 'Unknown number of phones', ] @@ -944,17 +944,17 @@ def test_preview_broadcast_areas_page( ( [BRISTOL], [ - 'An area of 7 square miles Will get the alert', - 'An extra area of 6 square miles is Likely to get the alert', + 'An area of 4 square miles Will get the alert', + 'An extra area of 3 square miles is Likely to get the alert', '70,000 to 100,000 phones', ] ), ( [SKYE], [ - 'An area of 3,000 square miles Will get the alert', - 'An extra area of 800 square miles is Likely to get the alert', - '4,000 phones estimated', + 'An area of 2,000 square miles Will get the alert', + 'An extra area of 600 square miles is Likely to get the alert', + '3,000 to 4,000 phones', ] ), )) diff --git a/tests/app/models/test_broadcast_message.py b/tests/app/models/test_broadcast_message.py index d59800842..b964899ae 100644 --- a/tests/app/models/test_broadcast_message.py +++ b/tests/app/models/test_broadcast_message.py @@ -45,7 +45,7 @@ def test_simple_polygons(): # Because the areas are close to each other, the simplification # and unioning process results in a single polygon with fewer # total coordinates - [55], + [57], ]