diff --git a/app/broadcast_areas/__init__.py b/app/broadcast_areas/__init__.py index 5e2df3292..c9e40ec89 100644 --- a/app/broadcast_areas/__init__.py +++ b/app/broadcast_areas/__init__.py @@ -1,3 +1,5 @@ +import math + from notifications_utils.formatters import formatted_list from notifications_utils.polygons import Polygons from notifications_utils.serialised_model import SerialisedModelCollection @@ -49,6 +51,10 @@ class BroadcastArea(SortableMixin): BroadcastAreasRepository().get_simple_polygons_for_area(self.id) ) + @cached_property + def simple_polygons_with_bleed(self): + return self.simple_polygons.bleed_by(self.estimated_bleed_in_degrees) + @cached_property def sub_areas(self): return [ @@ -68,6 +74,20 @@ class BroadcastArea(SortableMixin): # https://www.pivotaltracker.com/story/show/174837293 return self._count_of_phones or 0 + @property + def phone_density(self): + return self.count_of_phones / self.polygons.estimated_area + + @property + def estimated_bleed_in_m(self): + if self.id.endswith(CITY_OF_LONDON.WARDS): + return 500 + return 5_900 - (math.log(self.phone_density, 10) * 1_250) + + @property + def estimated_bleed_in_degrees(self): + return self.estimated_bleed_in_m / Polygons.approx_metres_to_degree + @cached_property def parents(self): return list(filter(None, self._parents_iterator)) @@ -109,6 +129,13 @@ class CustomBroadcastArea: simple_polygons = polygons + @cached_property + def simple_polygons_with_bleed(self): + # We don’t yet have a way of working out the population density + # of a custom area, so for now we have to use an average number + # to estimate the amount of bleed + return self.simple_polygons.bleed_by(Polygons.approx_bleed_in_degrees) + class CustomBroadcastAreas(SerialisedModelCollection): model = CustomBroadcastArea diff --git a/app/models/broadcast_message.py b/app/models/broadcast_message.py index 763fc1e3c..e59e5f791 100644 --- a/app/models/broadcast_message.py +++ b/app/models/broadcast_message.py @@ -117,6 +117,17 @@ class BroadcastMessage(JSONModel): def simple_polygons(self): return self.get_simple_polygons(areas=self.areas) + @cached_property + def simple_polygons_with_bleed(self): + polygons = Polygons( + list(itertools.chain(*( + area.simple_polygons_with_bleed for area in self.areas + ))) + ) + # If we’ve added multiple areas then we need to re-simplify the + # combined shapes to keep the point count down + return polygons.smooth.simplify if len(self.areas) > 1 else polygons + @property def reference(self): if self.template_id: @@ -163,7 +174,7 @@ class BroadcastMessage(JSONModel): @property def count_of_phones_likely(self): area_estimate = self.simple_polygons.estimated_area - bleed_area_estimate = self.simple_polygons.bleed.estimated_area - area_estimate + bleed_area_estimate = self.simple_polygons_with_bleed.estimated_area - area_estimate return round_to_significant_figures( self.count_of_phones + (self.count_of_phones * bleed_area_estimate / area_estimate), 1 diff --git a/app/templates/views/broadcast/macros/area-map.html b/app/templates/views/broadcast/macros/area-map.html index 737cd24c0..04645eb2a 100644 --- a/app/templates/views/broadcast/macros/area-map.html +++ b/app/templates/views/broadcast/macros/area-map.html @@ -11,7 +11,7 @@