import json import os import re import requests from urllib.parse import urlencode from unicodedata import normalize from django.db.models import Q from django.contrib.gis.geos import Point, GEOSGeometry from .models import Address LINZ_API_KEY = os.getenv("LINZ_API_KEY") LINZ_WFS_ENDPOINT = f"https://data.linz.govt.nz/services;key={LINZ_API_KEY}/wfs" PROPERTY_TILE_LAYER = "layer-50804" PROPERTY_INFO_LAYER = "layer-53353" search_num = re.compile(r'((? 0: return features[0] def get_address(polygon): """Fetch the address string that intersects with the specified polygon""" # Don't perform request if the input polygon WKT is too long to fit into URL # Limit is 2048 characters if len(str(polygon)) > 1500: return None cql_filter = f"Within(shape,{polygon})" json = linz_wfs_getfeature( typeNames=PROPERTY_INFO_LAYER, cql_filter=cql_filter, count=1) features = json.get("features") if len(features) > 0: feature = features[0] return feature['properties'] def get_address_from_coordinates(coordinates): propertyPolygon = intersecting_property(coordinates) if propertyPolygon is not None: prop_geom = GEOSGeometry(json.dumps(propertyPolygon['geometry'])) return get_address(prop_geom) def search_address(address): # normalize accent characters etc. address = normalize( "NFKD", address.strip().lower(), ).encode("ascii", "ignore").decode() nums = search_num.findall(address) strings = search_str.findall(address) num_filter = Q() str_filter = Q() for n in nums: num_filter |= Q(address_number=n) for s in strings: str_filter |= ( Q(full_road_name_ascii__istartswith=s) | Q(town_city_ascii__istartswith=s) | Q(suburb_locality_ascii__istartswith=s) ) return [ { 'coordinates': (addr.wkb_geometry.x, addr.wkb_geometry.y), 'address': addr.full_address, } for addr in Address.objects.filter(num_filter & str_filter).distinct()[:10] ]