def query_args(request, format, type=None): try: generation = int(request.GET.get('generation', 0)) except ValueError: raise ViewException(format, 'Bad generation specified', 400) if not generation: generation = Generation.objects.current().id try: min_generation = int(request.GET.get('min_generation', 0)) except ValueError: raise ViewException(format, 'Bad min_generation specified', 400) if not min_generation: min_generation = generation if type is None: type = request.GET.get('type', '') args = {} if min_generation > -1: args = { 'generation_low__lte': generation, 'generation_high__gte': min_generation, } if ',' in type: args['type__code__in'] = type.split(',') elif type: args['type__code'] = type return args
def area_intersect(query_type, title, request, area_id, format): area = get_object_or_404(Area, format=format, id=area_id) if not area.polygons.count(): raise ViewException(format, 'No polygons found', 404) generation = Generation.objects.current() types = filter(None, request.REQUEST.get('type', '').split(',')) set_timeout(format) try: # Cast to list so that it's evaluated here, and add_codes doesn't get # confused with a RawQuerySet areas = list( Area.objects.intersect(query_type, area, types, generation)) areas = add_codes(areas) except QueryCanceledError: raise ViewException( format, 'That query was taking too long to compute - try restricting to a specific type, if you weren\'t already doing so.', 500) except DatabaseError, e: # Django 1.2+ catches QueryCanceledError and throws its own DatabaseError instead if 'canceling statement due to statement timeout' not in e.args[0]: raise raise ViewException( format, 'That query was taking too long to compute - try restricting to a specific type, if you weren\'t already doing so.', 500)
def area_intersect(query_type, title, request, area_id, format): area = get_object_or_404(Area, format=format, id=area_id) if not area.polygons.count(): raise ViewException(format, _('No polygons found'), 404) generation = Generation.objects.current() types = [_f for _f in request.GET.get('type', '').split(',') if _f] set_timeout(format) try: # Cast to list so that it's evaluated here, and output_areas doesn't get # confused with a RawQuerySet areas = list( Area.objects.intersect(query_type, area, types, generation)) except DatabaseError as e: if 'canceling statement due to statement timeout' not in e.args[0]: raise raise ViewException( format, _('That query was taking too long to compute - ' 'try restricting to a specific type, if you weren\'t already doing so.' ), 500) except InternalError: raise ViewException( format, _('There was an internal error performing that query.'), 500) title = title % ('<a href="%sarea/%d.html">%s</a>' % (reverse('mapit_index'), area.id, area.name)) return output_areas(request, title, format, areas, norobots=True)
def area_polygon(request, srid='', area_id='', format='kml'): if not srid and hasattr(countries, 'area_code_lookup'): resp = countries.area_code_lookup(request, area_id, format) if resp: return resp if not re.match('\d+$', area_id): raise ViewException(format, 'Bad area ID specified', 400) if not srid: srid = 4326 if format in ('kml', 'json', 'geojson') else settings.MAPIT_AREA_SRID srid = int(srid) area = get_object_or_404(Area, id=area_id) try: simplify_tolerance = float(request.GET.get('simplify_tolerance', 0)) except ValueError: raise ViewException(format, 'Badly specified tolerance', 400) try: output, content_type = area.export(srid, format, simplify_tolerance=simplify_tolerance) if output is None: return output_json({'error': 'No polygons 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 response.write(output) return response
def nearest(request, srid, x, y, format='json'): location = Point(float(x), float(y), srid=int(srid)) set_timeout(format) try: postcode = Postcode.objects.filter( location__distance_gte=(location, D( mi=0))).distance(location).order_by('distance')[0] except DatabaseError as e: if 'Cannot find SRID' in e.args[0]: raise ViewException(format, e.args[0], 400) if 'canceling statement due to statement timeout' not in e.args[0]: raise raise ViewException(format, 'That query was taking too long to compute.', 500) except: raise ViewException(format, 'No postcode found near %s,%s (%s)' % (x, y, srid), 404) if format == 'html': return render(request, 'mapit/postcode.html', { 'postcode': postcode.as_dict(), 'json_view': 'mapit-postcode', }) pc = postcode.as_dict() pc['distance'] = round(postcode.distance.m) return output_json({ 'postcode': pc, })
def partial_postcode(request, postcode, format='json'): postcode = re.sub(r'\s+', '', postcode.upper()) if is_valid_postcode(postcode): postcode = re.sub(r'\d[A-Z]{2}$', '', postcode) if not is_valid_partial_postcode(postcode): raise ViewException(format, "Partial postcode '%s' is not valid." % postcode, 400) location = Postcode.objects.filter(postcode__startswith=postcode).extra( where=['length(postcode) = %d' % (len(postcode) + 3)]).aggregate( Collect('location'))['location__collect'] if not location: raise ViewException(format, 'Postcode not found', 404) postcode = Postcode(postcode=postcode, location=location.centroid) if format == 'html': return render( request, 'mapit/postcode.html', { 'postcode': postcode.as_dict(), 'json_view': 'mapit-postcode-partial', }) return output_json(postcode.as_dict())
def area_polygon(request, srid='', area_id='', format='kml'): if not srid and hasattr(countries, 'area_code_lookup'): resp = countries.area_code_lookup(area_id, format) if resp: return resp if not re.match('\d+$', area_id): raise ViewException(format, 'Bad area ID specified', 400) if not srid: srid = 4326 if format in ('kml', 'json', 'geojson') else settings.MAPIT_AREA_SRID srid = int(srid) area = get_object_or_404(Area, id=area_id) all_areas = area.polygons.all() if len(all_areas) > 1: all_areas = all_areas.collect() elif len(all_areas) == 1: all_areas = all_areas[0].polygon else: return output_json({ 'error': 'No polygons found' }, code=404) if srid != settings.MAPIT_AREA_SRID: all_areas.transform(srid) try: simplify_tolerance = float(request.GET.get('simplify_tolerance', 0)) except: raise ViewException(format, 'Badly specified tolerance', 400) if simplify_tolerance: all_areas = all_areas.simplify(simplify_tolerance) if format=='kml': out = '''<?xml version="1.0" encoding="UTF-8"?> <kml xmlns="http://www.opengis.net/kml/2.2"> <Style id="transBluePoly"> <LineStyle> <color>70ff0000</color> <width>2</width> </LineStyle> <PolyStyle> <color>3dff5500</color> </PolyStyle> </Style> <Placemark> <styleUrl>#transBluePoly</styleUrl> <name>%s</name> %s </Placemark> </kml>''' % (escape(area.name), all_areas.kml) content_type = 'application/vnd.google-earth.kml+xml' elif format in ('json', 'geojson'): out = all_areas.json content_type = 'application/json' elif format=='wkt': out = all_areas.wkt content_type = 'text/plain' return HttpResponse(out, content_type='%s; charset=utf-8' % content_type)
def nearest(request, srid, x, y, format='json'): location = Point(float(x), float(y), srid=int(srid)) set_timeout(format) try: postcode = Postcode.objects.filter(location__distance_gte=( location, D(mi=0) )).distance(location).order_by('distance')[0] except QueryCanceledError: raise ViewException(format, 'That query was taking too long to compute.', 500) except DatabaseError, e: if 'canceling statement due to statement timeout' not in e.args[0]: raise raise ViewException(format, 'That query was taking too long to compute.', 500)
def area_from_code(request, code_type, code_value, format='json'): args = query_args(request, format) args['codes__type__code'] = code_type args['codes__code'] = code_value try: area = Area.objects.get(**args) except Area.DoesNotExist: message = 'No areas were found that matched code %s = %s.' % (code_type, code_value) raise ViewException(format, message, 404) except Area.MultipleObjectsReturned: message = 'There were multiple areas that matched code %s = %s.' % (code_type, code_value) raise ViewException(format, message, 500) return HttpResponseRedirect("/area/%d%s" % (area.id, '.%s' % format if format else ''))
def area_from_code(request, code_type, code_value, format='json'): q = query_args(request, format) q &= Q(codes__type__code=code_type, codes__code=code_value) try: area = Area.objects.get(q) except Area.DoesNotExist: message = _('No areas were found that matched code {0} = {1}.').format(code_type, code_value) raise ViewException(format, message, 404) except Area.MultipleObjectsReturned: message = _('There were multiple areas that matched code {0} = {1}.').format(code_type, code_value) raise ViewException(format, message, 500) area_kwargs = {'area_id': area.id} if format: area_kwargs['format'] = format return HttpResponseRedirect(reverse('area', kwargs=area_kwargs))
def example_postcode_for_area(request, area_id, format='json'): area = get_object_or_404(Area, format=format, id=area_id) try: pc = Postcode.objects.filter(areas=area).order_by()[0] except: set_timeout(format) try: pc = Postcode.objects.filter_by_area(area).order_by()[0] except QueryCanceledError: raise ViewException(format, 'That query was taking too long to compute.', 500) except DatabaseError, e: if 'canceling statement due to statement timeout' not in e.args[0]: raise raise ViewException(format, 'That query was taking too long to compute.', 500) except: pc = None
def query_args_polygon(request, format, srid, area_ids): args = {} if not srid: srid = 4326 if format in ('kml', 'json', 'geojson') else settings.MAPIT_AREA_SRID args['srid'] = int(srid) try: args['simplify_tolerance'] = float(request.GET.get('simplify_tolerance', 0)) except ValueError: raise ViewException(format, _('Badly specified tolerance'), 400) for area_id in area_ids: if not re.match('\d+$', area_id): raise ViewException(format, _('Bad area ID specified'), 400) return args
def areas_by_point(request, srid, x, y, bb=False, format=''): location = Point(float(x), float(y), srid=int(srid)) use_exceptions() try: location.transform(settings.MAPIT_AREA_SRID, clone=True) except: raise ViewException(format, _('Point outside the area geometry'), 400) method = 'box' if bb and bb != 'polygon' else 'polygon' q = query_args(request, format) if method == 'box': q &= Q(polygons__polygon__bbcontains=location) else: q &= Q(polygons__polygon__contains=location) areas = Area.objects.filter(q).distinct() return output_areas(request, _('Areas covering the point ({0},{1})').format(x, y), format, areas, indent_areas=True)
def postcode(request, postcode, format=None): if hasattr(countries, 'canonical_postcode'): canon_postcode = countries.canonical_postcode(postcode) postcode = canon_postcode # if (postcode != canon_postcode and format is None) or format == 'json': # return redirect('mapit.views.postcodes.postcode', postcode=canon_postcode) if format is None: format = 'json' if not is_valid_postcode(postcode): raise ViewException(format, "Postcode '%s' is not valid." % postcode, 400) postcode = get_object_or_404(Postcode, format=format, postcode=postcode) try: generation = int(request.GET['generation']) except: generation = Generation.objects.current() if not hasattr(countries, 'is_special_postcode') or not countries.is_special_postcode( postcode.postcode): areas = list(add_codes(Area.objects.by_postcode(postcode, generation))) else: areas = [] # Shortcuts shortcuts = {} for area in areas: if area.type.code in ('COP', 'LBW', 'LGE', 'MTW', 'UTE', 'UTW'): shortcuts['ward'] = area.id shortcuts['council'] = area.parent_area_id elif area.type.code == 'CED': shortcuts.setdefault('ward', {})['county'] = area.id shortcuts.setdefault('council', {})['county'] = area.parent_area_id elif area.type.code == 'DIW': shortcuts.setdefault('ward', {})['district'] = area.id shortcuts.setdefault('council', {})['district'] = area.parent_area_id elif area.type.code in ('WMC', ): # XXX Also maybe 'EUR', 'NIE', 'SPC', 'SPE', 'WAC', 'WAE', 'OLF', 'OLG', 'OMF', 'OMG'): shortcuts[area.type.code] = area.id # Add manual enclosing areas. extra = [] for area in areas: if area.type.code in enclosing_areas.keys(): extra.extend(enclosing_areas[area.type.code]) areas = itertools.chain(areas, Area.objects.filter(id__in=extra)) if format == 'html': return render( request, 'mapit/postcode.html', { 'postcode': postcode.as_dict(), 'areas': areas, 'json_view': 'mapit-postcode', }) out = postcode.as_dict() out['areas'] = dict((area.id, area.as_dict()) for area in areas) if shortcuts: out['shortcuts'] = shortcuts return output_json(out)
def areas_by_point(request, srid, x, y, bb=False, format='json'): type = request.REQUEST.get('type', '') generation = request.REQUEST.get('generation', Generation.objects.current()) if not generation: generation = Generation.objects.current() location = Point(float(x), float(y), srid=int(srid)) gdal.UseExceptions() try: location.transform(settings.MAPIT_AREA_SRID, clone=True) except: raise ViewException(format, 'Point outside the area geometry', 400) method = 'box' if bb and bb != 'polygon' else 'polygon' args = { 'generation_low__lte': generation, 'generation_high__gte': generation } if ',' in type: args['type__code__in'] = type.split(',') elif type: args['type__code'] = type if type and method == 'polygon': args = dict(("area__%s" % k, v) for k, v in args.items()) # So this is odd. It doesn't matter if you specify types, PostGIS will # do the contains test on all the geometries matching the bounding-box # index, even if it could be much quicker to filter some out first # (ie. the EUR ones). args['polygon__bbcontains'] = location shapes = Geometry.objects.filter(**args).defer('polygon') areas = [] for shape in shapes: try: areas.append( Area.objects.get(polygons__id=shape.id, polygons__polygon__contains=location)) except: pass else: if method == 'box': args['polygons__polygon__bbcontains'] = location else: geoms = list( Geometry.objects.filter( polygon__contains=location).defer('polygon')) args['polygons__in'] = geoms areas = Area.objects.filter(**args) areas = add_codes(areas) if format == 'html': return output_html(request, 'Areas covering the point (%s,%s)' % (x, y), areas, indent_areas=True) return output_json(dict((area.id, area.as_dict()) for area in areas))
def area_from_code(request, code_type, code_value, format='json'): args = query_args(request, format) args['codes__type__code'] = code_type args['codes__code'] = code_value try: area = Area.objects.get(**args) except Area.DoesNotExist, e: message = 'No areas were found that matched code %s = %s.' % (code_type, code_value) raise ViewException(format, message, 404)
def query_args(request, format, type=None): try: generation = int(request.GET.get('generation', 0)) except ValueError: raise ViewException(format, _('Bad generation specified'), 400) if not generation: generation = Generation.objects.current().id try: min_generation = int(request.GET.get('min_generation', 0)) except ValueError: raise ViewException(format, _('Bad min_generation specified'), 400) if not min_generation: min_generation = generation if type is None: type = request.GET.get('type', '') country_code = request.GET.get('country', '') args = {} if min_generation > -1: args = { 'generation_low__lte': generation, 'generation_high__gte': min_generation, } query = Q(**args) for attr, value in [ (['type'], type), (['country', 'countries'], country_code), ]: q = Q() for a in attr: if ',' in value: q |= Q(**{a + '__code__in': value.split(',')}) elif value: q |= Q(**{a + '__code': value}) query &= q return query
def nearest(request, srid, x, y, format='json'): location = Point(float(x), float(y), srid=int(srid)) set_timeout(format) try: # Transform to database SRID for comparison (GeometryCentroidDistance does not yet do this) location.transform(4326) except: raise ViewException(format, _('Point outside the area geometry'), 400) try: # Ordering will be in 'degrees', so fetch a few and sort by actual distance postcodes = Postcode.objects.annotate( centroid_distance=GeometryCentroidDistance( 'location', location)).annotate(distance=Distance( 'location', location)).order_by('centroid_distance')[:100] postcodes = sorted(postcodes, key=attrgetter('distance')) postcode = postcodes[0] except DatabaseError as e: if 'Cannot find SRID' in e.args[0]: raise ViewException(format, e.args[0], 400) if 'canceling statement due to statement timeout' not in e.args[0]: raise raise ViewException(format, 'That query was taking too long to compute.', 500) except: raise ViewException(format, 'No postcode found near %s,%s (%s)' % (x, y, srid), 404) if format == 'html': return render(request, 'mapit/postcode.html', { 'postcode': postcode.as_dict(), 'json_view': 'mapit-postcode', }) pc = postcode.as_dict() pc['distance'] = round(postcode.distance.m) return output_json({ 'postcode': pc, })
def area_from_code(request, code_type, code_value, format='json'): generation = request.REQUEST.get('generation', Generation.objects.current()) if not generation: generation = Generation.objects.current() try: area = Area.objects.get(codes__type__code=code_type, codes__code=code_value, generation_low__lte=generation, generation_high__gte=generation) except Area.DoesNotExist, e: message = 'No areas were found that matched code %s = %s.' % (code_type, code_value) raise ViewException(format, message, 404)
def areas_by_point(request, srid, x, y, bb=False, format='json'): location = Point(float(x), float(y), srid=int(srid)) use_exceptions() try: location.transform(settings.MAPIT_AREA_SRID, clone=True) except: raise ViewException(format, _('Point outside the area geometry'), 400) method = 'box' if bb and bb != 'polygon' else 'polygon' args = query_args(request, format) type = request.GET.get('type', '') if type and method == 'polygon': args = dict(("area__%s" % k, v) for k, v in args.items()) # So this is odd. It doesn't matter if you specify types, PostGIS will # do the contains test on all the geometries matching the bounding-box # index, even if it could be much quicker to filter some out first # (ie. the EUR ones). args['polygon__bbcontains'] = location shapes = Geometry.objects.filter(**args).defer('polygon') areas = [] for shape in shapes: try: areas.append( Area.objects.get(polygons__id=shape.id, polygons__polygon__contains=location)) except: pass else: if method == 'box': args['polygons__polygon__bbcontains'] = location else: geoms = list( Geometry.objects.filter( polygon__contains=location).defer('polygon')) args['polygons__in'] = geoms areas = Area.objects.filter(**args) return output_areas(request, _('Areas covering the point ({0},{1})').format(x, y), format, areas, indent_areas=True)
def example_postcode_for_area(request, area_id, format='json'): area = get_object_or_404(Area, format=format, id=area_id) try: pc = Postcode.objects.filter(areas=area).order_by()[0] except: set_timeout(format) try: pc = Postcode.objects.filter_by_area(area).order_by()[0] except DatabaseError as e: if 'canceling statement due to statement timeout' not in e.args[0]: raise raise ViewException(format, 'That query was taking too long to compute.', 500) except: pc = None if pc: pc = pc.get_postcode_display() if format == 'html': return render(request, 'mapit/example-postcode.html', {'area': area, 'postcode': pc}) return output_json(pc)
def area(request, area_id, format=''): if hasattr(countries, 'area_code_lookup'): resp = countries.area_code_lookup(request, area_id, format) if resp: return resp if not re.match(r'\d+$', area_id): raise ViewException(format, _('Bad area ID specified'), 400) area = get_object_or_404(Area, format=format, id=area_id) codes = [] for code_type, code in sorted(area.all_codes.items()): code_link = None if code_type in ('osm', 'osm_rel'): code_link = 'http://www.openstreetmap.org/browse/relation/' + code elif code_type == 'osm_way': code_link = 'http://www.openstreetmap.org/browse/way/' + code codes.append((code_type, code, code_link)) # Sort any alternative names by the description of the name (the # English name of the language for global MapIt) and exclude the # default OSM name, since if that exists, it'll already be # displayed as the page title. names = Name.objects.filter(area=area).select_related() alternative_names = sorted((n.type.description, n.name) for n in names if n.type.code != "default") geotype = {} if hasattr(countries, 'restrict_geo_html'): geotype = countries.restrict_geo_html(area) if format == 'html': return render( request, 'mapit/area.html', { 'area': area, 'codes': codes, 'alternative_names': alternative_names, 'geotype': geotype, }) return output_json(area.as_dict(names))
set_timeout(format) try: postcode = Postcode.objects.filter( location__distance_gte=(location, D( mi=0))).distance(location).order_by('distance')[0] except QueryCanceledError: raise ViewException(format, 'That query was taking too long to compute.', 500) except DatabaseError, e: if 'canceling statement due to statement timeout' not in e.args[0]: raise raise ViewException(format, 'That query was taking too long to compute.', 500) except: raise ViewException(format, 'No postcode found near %s,%s (%s)' % (x, y, srid), 404) if format == 'html': return render( request, 'mapit/postcode.html', { 'postcode': postcode.as_dict(), 'json_view': 'mapit.views.postcodes.postcode', }) pc = postcode.as_dict() pc['distance'] = round(postcode.distance.m) return output_json({ 'postcode': pc, })
areas = add_codes(areas) except QueryCanceledError: raise ViewException( format, 'That query was taking too long to compute - try restricting to a specific type, if you weren\'t already doing so.', 500) except DatabaseError, e: # Django 1.2+ catches QueryCanceledError and throws its own DatabaseError instead if 'canceling statement due to statement timeout' not in e.args[0]: raise raise ViewException( format, 'That query was taking too long to compute - try restricting to a specific type, if you weren\'t already doing so.', 500) except InternalError: raise ViewException( format, 'There was an internal error performing that query.', 500) if format == 'html': return output_html(request, title % ('<a href="%sarea/%d.html">%s</a>' % (reverse('mapit_index'), area.id, area.name)), areas, norobots=True) return output_json(dict((a.id, a.as_dict()) for a in areas)) @ratelimit(minutes=3, requests=100) def area_touches(request, area_id, format='json'): return area_intersect('touches', 'Areas touching %s', request, area_id, format)
def area_touches(request, area_id, format='json'): # XXX Exempt an error that throws a GEOS Exception if area_id == '2658': raise ViewException(format, 'There was an internal error performing that query.', 500) return area_intersect('touches', 'Areas touching %s', request, area_id, format)
def check_area_ids(format, area_ids): for area_id in area_ids: if not re.match(r'\d+$', area_id): raise ViewException(format, _('Bad area ID specified'), 400)
def get_object_or_404(klass, format='json', *args, **kwargs): try: return orig_get_object_or_404(klass, *args, **kwargs) except http.Http404 as e: from mapit.middleware import ViewException raise ViewException(format, str(e), 404)
def convert_address(request, format='json'): address = request.GET.get('address') if not address: raise ViewException(format, 'No address was provided.', 400) converter = AddressConverter() locations = converter.resolve_address(address, partial=bool( request.GET.get('partial'))) # this is a copy from mapit.views.areas.areas_by_point # because it's hard to reuse their code :( if PYGDAL: from osgeo import gdal gdal.UseExceptions() # we find areas for every lat/long coord we got back areas = [] type = request.GET.get('type', '') for coords in locations: location = Point(float(coords['lng']), float(coords['lat']), srid=4326) try: location.transform(settings.MAPIT_AREA_SRID, clone=True) except: raise ViewException(format, _('Point outside the area geometry'), 400) args = query_args(request, format) if type: args = dict(("area__%s" % k, v) for k, v in args.items()) # So this is odd. It doesn't matter if you specify types, PostGIS will # do the contains test on all the geometries matching the bounding-box # index, even if it could be much quicker to filter some out first # (ie. the EUR ones). coords['areas'] = [] args['polygon__bbcontains'] = location shapes = Geometry.objects.filter(**args).defer('polygon') for shape in shapes: try: area = Area.objects.get( polygons__id=shape.id, polygons__polygon__contains=location) coords['areas'].append(str(area.id)) areas.append(area) except: pass else: geoms = list( Geometry.objects.filter( polygon__contains=location).defer('polygon')) args['polygons__in'] = geoms matches = Area.objects.filter(**args).all() coords['areas'] = [str(m.id) for m in matches] areas.extend(matches) areas = add_codes(areas) if format == 'html': return output_html( request, _("Areas matching the address '{0}'").format(address), areas, indent_areas=True) # hack to include the geocoded addresses in the results data = iterdict( chain(((area.id, area.as_dict()) for area in areas), [("addresses", locations)])) return output_json(data)