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
|
|
|
import itertools
|
|
|
|
|
|
|
|
|
|
from notifications_utils.serialised_model import SerialisedModelCollection
|
|
|
|
|
from notifications_utils.safe_string import make_string_safe_for_id
|
|
|
|
|
|
2020-07-24 16:45:41 +01:00
|
|
|
from .repo import BroadcastAreasRepository
|
2020-07-24 12:54:22 +01:00
|
|
|
|
|
|
|
|
|
2020-07-24 16:47:12 +01:00
|
|
|
class IdentifiableMixin:
|
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
|
|
|
def __repr__(self):
|
|
|
|
|
return f'{self.__class__.__name__}(<{self.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
|
|
|
|
2020-07-24 16:47:12 +01:00
|
|
|
def __lt__(self, other):
|
|
|
|
|
# Implementing __lt__ means any classes inheriting from this
|
|
|
|
|
# method are sortable
|
|
|
|
|
return self.id < other.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
|
|
|
|
|
|
|
|
|
|
|
|
|
class IdFromNameMixin:
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def id(self):
|
|
|
|
|
return make_string_safe_for_id(self.name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class GetItemByIdMixin:
|
|
|
|
|
def get(self, id):
|
|
|
|
|
for item in self:
|
|
|
|
|
if item.id == id:
|
|
|
|
|
return item
|
|
|
|
|
raise KeyError(id)
|
|
|
|
|
|
|
|
|
|
|
2020-07-24 16:47:12 +01:00
|
|
|
class BroadcastArea(IdentifiableMixin):
|
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
|
|
|
def __init__(self, row):
|
|
|
|
|
id, name, feature = row
|
|
|
|
|
|
|
|
|
|
self.id = id
|
|
|
|
|
self.name = name
|
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
|
|
|
self.feature = feature
|
|
|
|
|
|
|
|
|
|
for coordinates in self.polygons:
|
|
|
|
|
if coordinates[0] != coordinates[-1]:
|
|
|
|
|
# The CAP XML format requires shapes to be closed
|
|
|
|
|
raise ValueError(
|
|
|
|
|
f'Area {self.name} is not a closed shape '
|
|
|
|
|
f'({coordinates[0]}, {coordinates[-1]})'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
|
return self.id == other.id
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def polygons(self):
|
|
|
|
|
if self.feature['geometry']['type'] == 'MultiPolygon':
|
|
|
|
|
return [
|
|
|
|
|
polygons[0]
|
|
|
|
|
for polygons in self.feature['geometry']['coordinates']
|
|
|
|
|
]
|
|
|
|
|
if self.feature['geometry']['type'] == 'Polygon':
|
|
|
|
|
return [
|
|
|
|
|
self.feature['geometry']['coordinates'][0]
|
|
|
|
|
]
|
|
|
|
|
raise TypeError(
|
|
|
|
|
f'Unknown geometry type {self.feature["geometry"]["type"]} '
|
|
|
|
|
f'in {self.__class__.__name} {self.name}'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def unenclosed_polygons(self):
|
|
|
|
|
# Some mapping tools require shapes to be unenclosed, i.e. the
|
|
|
|
|
# last point joins the first point implicitly
|
|
|
|
|
return [
|
|
|
|
|
coordinates[:-1] for coordinates in self.polygons
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2020-07-24 16:47:12 +01:00
|
|
|
class BroadcastAreaLibrary(SerialisedModelCollection, IdentifiableMixin, IdFromNameMixin, GetItemByIdMixin):
|
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
|
|
|
|
|
|
|
|
model = BroadcastArea
|
|
|
|
|
|
2020-07-24 16:47:12 +01:00
|
|
|
def __init__(self, library_name):
|
|
|
|
|
self.name = library_name
|
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
|
|
|
def get_examples(self):
|
|
|
|
|
return BroadcastAreasRepository().get_library_description(self.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
|
|
|
|
2020-07-24 16:47:12 +01:00
|
|
|
@property
|
|
|
|
|
def items(self):
|
|
|
|
|
return BroadcastAreasRepository().get_all_areas_for_library(self.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
|
|
|
|
|
|
|
|
|
|
|
|
|
class BroadcastAreaLibraries(SerialisedModelCollection, GetItemByIdMixin):
|
|
|
|
|
|
|
|
|
|
model = BroadcastAreaLibrary
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
2020-07-24 16:47:12 +01:00
|
|
|
self.libraries = BroadcastAreasRepository().get_libraries()
|
|
|
|
|
self.items = self.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
|
|
|
|
|
|
|
|
def get_areas(self, *area_ids):
|
2020-07-09 19:10:51 +01:00
|
|
|
# allow people to call `get_areas('a', 'b') or get_areas(['a', 'b'])`
|
|
|
|
|
if len(area_ids) == 1 and isinstance(area_ids[0], list):
|
|
|
|
|
area_ids = area_ids[0]
|
|
|
|
|
|
2020-07-24 16:47:12 +01:00
|
|
|
areas = BroadcastAreasRepository().get_areas(area_ids)
|
|
|
|
|
return [BroadcastArea(area) for area in areas]
|
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 get_polygons_for_areas_long_lat(self, *area_ids):
|
|
|
|
|
return list(itertools.chain(*(
|
|
|
|
|
area.polygons
|
|
|
|
|
for area in self.get_areas(*area_ids)
|
|
|
|
|
)))
|
|
|
|
|
|
|
|
|
|
def get_polygons_for_areas_lat_long(self, *area_ids):
|
|
|
|
|
return [
|
|
|
|
|
[[long, lat] for lat, long in polygon]
|
|
|
|
|
for polygon in self.get_polygons_for_areas_long_lat(*area_ids)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
broadcast_area_libraries = BroadcastAreaLibraries()
|