Skip to content

Commit

Permalink
support GeoJSON/KML for multiple areas (issue mysociety#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
chris48s committed May 21, 2015
1 parent 1ac8818 commit 83e67bb
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 19 deletions.
25 changes: 6 additions & 19 deletions mapit/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from mapit.managers import Manager, GeoManager
from mapit import countries
from mapit.djangopatch import GetQuerySetMetaclass
from mapit.outputformatter import OutputFormatter
from django.utils import six


Expand Down Expand Up @@ -343,25 +344,11 @@ def export(self,
if kml_type == "polygon":
out = all_areas.kml
elif kml_type == "full":
out = '''<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<Style id="ourPolygonStyle">
<LineStyle>
<color>%s</color>
<width>2</width>
</LineStyle>
<PolyStyle>
<color>%s</color>
</PolyStyle>
</Style>
<Placemark>
<styleUrl>#ourPolygonStyle</styleUrl>
<name>%s</name>
%s
</Placemark>
</Document>
</kml>''' % (line_colour, fill_colour, escape(self.name), all_areas.kml)
formatter = OutputFormatter()
out = ''
out += formatter.kml_header % (line_colour, fill_colour)
out += formatter.kml_placemark % (escape(self.name), all_areas.kml)
out += formatter.kml_footer
else:
raise Exception("Unknown kml_type: '%s'" % (kml_type,))
content_type = 'application/vnd.google-earth.kml+xml'
Expand Down
52 changes: 52 additions & 0 deletions mapit/outputformatter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# OutputFormatter
# class for merging kml/geojson output

from django.utils.html import escape


class OutputFormatter:

def __init__(self):
self.kml_header =\
"""<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<Style id="ourPolygonStyle">
<LineStyle>
<color>%s</color>
<width>2</width>
</LineStyle>
<PolyStyle>
<color>%s</color>
</PolyStyle>
</Style>"""
self.kml_placemark =\
"""
<Placemark>
<styleUrl>#ourPolygonStyle</styleUrl>
<name>%s</name>
%s
</Placemark>"""
self.kml_footer =\
"""
</Document>
</kml>"""


# [(kml, name)] -> kml
def merge_kml(self, kml_arr, line_colour="70ff0000", fill_colour="3dff5500"):
output = self.kml_header % (line_colour, fill_colour)
for kml in kml_arr:
output += self.kml_placemark % (escape(kml[1]), kml[0])
output += self.kml_footer
return output

# [(geojson, name)] -> geojson
def merge_geojson(self, geojson_arr):
output = '{ "type": "FeatureCollection", "features": ['
for geojson in geojson_arr:
output += '{ "properties": { "name": "%s" }, "type": "Feature", "geometry": %s },'\
% (escape(geojson[1]), geojson[0])
output = output[:-1]
output += "] }"
return output
2 changes: 2 additions & 0 deletions mapit/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
url(r'^nearest/(?P<srid>[0-9]+)/(?P<x>[0-9.-]+),(?P<y>[0-9.-]+)%s$' % format_end, postcodes.nearest),

url(r'^areas/(?P<area_ids>[0-9]+(?:,[0-9]+)*)%s$' % format_end, areas.areas),
url(r'^areas/(?P<area_ids>[0-9]+(?:,[0-9]+)*)\.(?P<format>kml|geojson)$', areas.areas_polygon),
url(r'^areas/(?P<srid>[0-9]+)/(?P<area_ids>[0-9]+(?:,[0-9]+)*)\.(?P<format>kml|geojson)$', areas.areas_polygon),
url(r'^areas/(?P<area_ids>[0-9]+(?:,[0-9]+)*)/geometry$', areas.areas_geometry),
url(r'^areas/(?P<type>[A-Z0-9,]*[A-Z0-9]+)%s$' % format_end, areas.areas_by_type),
url(r'^areas/(?P<name>.+?)%s$' % format_end, areas.areas_by_name),
Expand Down
45 changes: 45 additions & 0 deletions mapit/views/areas.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from mapit.ratelimitcache import ratelimit
from mapit import countries
from mapit.iterables import iterdict
from mapit.outputformatter import OutputFormatter


def add_codes(areas):
Expand Down Expand Up @@ -253,6 +254,50 @@ def areas_by_name(request, name, format='json'):
return output_areas(request, _('Areas starting with %s') % name, format, areas)


@ratelimit(minutes=3, requests=100)
def areas_polygon(request, area_ids, srid='', format='kml'):

if not srid:
srid = 4326 if format in ('kml', 'json', 'geojson') else settings.MAPIT_AREA_SRID
srid = int(srid)

try:
simplify_tolerance = float(request.GET.get('simplify_tolerance', 0))
except ValueError:
raise ViewException(format, _('Badly specified tolerance'), 400)

area_ids = area_ids.split(',')
polygons = []
for area_id in area_ids:

if not re.match('\d+$', area_id):
raise ViewException(format, _('Bad area ID specified'), 400)
area = get_object_or_404(Area, id=area_id)

try:
polygon, content_type = area.export(srid, format, simplify_tolerance=simplify_tolerance, kml_type='polygon')
polygons.append((polygon, area.name))

if polygon is None:
return output_json({'error': _('One or more polygons not found')}, code=404)
except TransformError as e:
return output_json({'error': e.args[0]}, code=400)

response = HttpResponse(content_type='%s; charset=utf-8' % content_type)
response['Access-Control-Allow-Origin'] = '*'
response['Cache-Control'] = 'max-age=2419200' # 4 weeks

formatter = OutputFormatter()
output = ''
if format == 'kml':
output = formatter.merge_kml(polygons)
if format == 'geojson':
output = formatter.merge_geojson(polygons)

response.write(output)
return response


@ratelimit(minutes=3, requests=100)
def area_geometry(request, area_id):
area = _area_geometry(area_id)
Expand Down

0 comments on commit 83e67bb

Please sign in to comment.