[polygons] Adding address_normalizer polygons package
This commit is contained in:
0
scripts/geodata/polygons/__init__.py
Normal file
0
scripts/geodata/polygons/__init__.py
Normal file
138
scripts/geodata/polygons/index.py
Normal file
138
scripts/geodata/polygons/index.py
Normal file
@@ -0,0 +1,138 @@
|
||||
import fiona
|
||||
import os
|
||||
import rtree
|
||||
import ujson as json
|
||||
|
||||
from collections import OrderedDict
|
||||
from shapely.geometry import Point, Polygon, MultiPolygon
|
||||
from shapely.prepared import prep
|
||||
from shapely.geometry.geo import mapping
|
||||
|
||||
DEFAULT_INDEX_FILENAME = 'rtree'
|
||||
DEFAULT_POLYS_FILENAME = 'polygons.geojson'
|
||||
|
||||
|
||||
class RTreePolygonIndex(object):
|
||||
include_only_properties = None
|
||||
|
||||
def __init__(self, index=None, polygons=None, save_dir=None,
|
||||
index_filename=None,
|
||||
include_only_properties=None):
|
||||
if save_dir:
|
||||
self.save_dir = save_dir
|
||||
else:
|
||||
self.save_dir = None
|
||||
|
||||
if index_filename:
|
||||
self.index_path = os.path.join(save_dir or '.', index_filename)
|
||||
else:
|
||||
self.index_path = None
|
||||
|
||||
if not index and self.index_path:
|
||||
self.index = rtree.index.Index(self.index_path)
|
||||
elif not index:
|
||||
self.index = rtree.index.Index()
|
||||
else:
|
||||
self.index = index
|
||||
|
||||
if include_only_properties and hasattr(include_only_properties, '__contains__'):
|
||||
self.include_only_properties = include_only_properties
|
||||
|
||||
if not polygons:
|
||||
self.polygons = []
|
||||
else:
|
||||
self.polygons = polygons
|
||||
|
||||
def index_polygon(self, id, polygon):
|
||||
self.index.insert(id, polygon.bounds)
|
||||
|
||||
def add_polygon(self, poly, properties, simplify_tolerance=0.0001, preserve_topology=True):
|
||||
poly = poly.simplify(simplify_tolerance, preserve_topology=preserve_topology)
|
||||
if self.include_only_properties:
|
||||
properties = {k: v for k, v in properties.iteritems() if k in self.include_only_properties}
|
||||
|
||||
self.polygons.append((properties, prep(poly)))
|
||||
|
||||
@classmethod
|
||||
def create_from_shapefiles(cls, inputs, output_dir,
|
||||
index_filename=DEFAULT_INDEX_FILENAME,
|
||||
polys_filename=DEFAULT_POLYS_FILENAME):
|
||||
index = cls(save_dir=output_dir, index_filename=index_filename)
|
||||
for input_file in inputs:
|
||||
f = fiona.open(input_file)
|
||||
|
||||
i = 0
|
||||
|
||||
for rec in f:
|
||||
if not rec or not rec.get('geometry') or 'type' not in rec['geometry']:
|
||||
continue
|
||||
poly_type = rec['geometry']['type']
|
||||
if poly_type == 'Polygon':
|
||||
poly = Polygon(rec['geometry']['coordinates'][0])
|
||||
index.index_polygon(i, poly)
|
||||
index.add_polygon(poly, rec['properties'])
|
||||
elif poly_type == 'MultiPolygon':
|
||||
polys = []
|
||||
for coords in rec['geometry']['coordinates']:
|
||||
poly = Polygon(coords[0])
|
||||
polys.append(poly)
|
||||
index.index_polygon(i, poly)
|
||||
|
||||
index.add_polygon(MultiPolygon(polys), rec['properties'])
|
||||
i += 1
|
||||
return index
|
||||
|
||||
def save_polygons(self, out_filename):
|
||||
out = open(out_filename, 'w')
|
||||
features = []
|
||||
for props, poly in self.polygons:
|
||||
features.append({
|
||||
'type': 'Feature',
|
||||
'geometry': mapping(poly.context),
|
||||
'properties': props
|
||||
})
|
||||
json.dump({'type': 'FeatureCollection',
|
||||
'features': features},
|
||||
out)
|
||||
|
||||
def save(self, polys_filename=DEFAULT_POLYS_FILENAME):
|
||||
self.save_polygons(os.path.join(self.save_dir, polys_filename))
|
||||
# need to close index before loading it
|
||||
self.index.close()
|
||||
|
||||
@classmethod
|
||||
def load_polygons(cls, filename):
|
||||
feature_collection = json.load(open(filename))
|
||||
polygons = []
|
||||
for feature in feature_collection['features']:
|
||||
poly_type = feature['geometry']['type']
|
||||
|
||||
if poly_type == 'Polygon':
|
||||
poly = Polygon(feature['geometry']['coordinates'][0])
|
||||
polygons.append((feature['properties'], prep(poly)))
|
||||
elif poly_type == 'MultiPolygon':
|
||||
polys = []
|
||||
for coords in feature['geometry']['coordinates']:
|
||||
poly = Polygon(coords[0])
|
||||
polys.append(poly)
|
||||
|
||||
polygons.append((feature['properties'], prep(MultiPolygon(polys))))
|
||||
return polygons
|
||||
|
||||
@classmethod
|
||||
def load(cls, d, index_name=DEFAULT_INDEX_FILENAME, polys_filename=DEFAULT_POLYS_FILENAME):
|
||||
index = rtree.index.Index(os.path.join(d, index_name))
|
||||
polys = cls.load_polygons(os.path.join(d, polys_filename))
|
||||
return cls(index=index, polygons=polys, save_dir=d)
|
||||
|
||||
def get_candidate_polygons(self, lat, lon):
|
||||
return OrderedDict.fromkeys(self.index.intersection((lon, lat, lon, lat))).keys()
|
||||
|
||||
def point_in_poly(self, lat, lon):
|
||||
polys = self.get_candidate_polygons(lat, lon)
|
||||
pt = Point(lon, lat)
|
||||
for i in polys:
|
||||
props, poly = self.polygons[i]
|
||||
if poly.contains(pt):
|
||||
return props
|
||||
return None
|
||||
112
scripts/geodata/polygons/language_polys.py
Normal file
112
scripts/geodata/polygons/language_polys.py
Normal file
@@ -0,0 +1,112 @@
|
||||
import csv
|
||||
import os
|
||||
|
||||
this_dir = os.path.realpath(os.path.dirname(__file__))
|
||||
sys.path.append(os.path.realpath(os.path.join(os.pardir, os.pardir)))
|
||||
|
||||
from geodata.polygons.index import *
|
||||
from geodara.text.languages import *
|
||||
|
||||
country_language_dir = os.path.join(LANGUAGES_DIR, 'countries')
|
||||
regional_language_dir = os.path.join(LANGUAGES_DIR, 'regional')
|
||||
|
||||
|
||||
class LanguagePolygonIndex(RTreePolygonIndex):
|
||||
|
||||
include_only_properties = set([
|
||||
'qs_a0',
|
||||
'qs_iso_cc',
|
||||
'qs_a1',
|
||||
'qs_a1_lc',
|
||||
'qs_a1r',
|
||||
'qs_a1r_lc',
|
||||
'qs_level',
|
||||
'languages',
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def create_from_shapefiles(cls,
|
||||
admin0_shapefile,
|
||||
admin1_shapefile,
|
||||
admin1_region_file,
|
||||
output_dir,
|
||||
index_filename=DEFAULT_INDEX_FILENAME,
|
||||
polys_filename=DEFAULT_POLYS_FILENAME):
|
||||
|
||||
init_languages()
|
||||
index = cls(save_dir=output_dir, index_filename=index_filename)
|
||||
|
||||
i = 0
|
||||
|
||||
'''
|
||||
Ordering of the files is important here as we want to match
|
||||
the most granular admin polygon first for regional languages. Currently
|
||||
most regional languages as they would apply to street signage are regional in
|
||||
terms of an admin 1 level (states, provinces, regions)
|
||||
'''
|
||||
for input_file in (admin0_shapefile, admin1_region_file, admin1_shapefile):
|
||||
f = fiona.open(input_file)
|
||||
|
||||
for rec in f:
|
||||
if not rec or not rec.get('geometry') or 'type' not in rec['geometry']:
|
||||
continue
|
||||
|
||||
country = rec['properties']['qs_iso_cc'].lower()
|
||||
properties = rec['properties']
|
||||
|
||||
admin_level = properties['qs_level']
|
||||
|
||||
if admin_level == 'adm1':
|
||||
name_key = 'qs_a1'
|
||||
code_key = 'qs_a1_lc'
|
||||
elif admin_level == 'adm1_region':
|
||||
name_key = 'qs_a1r'
|
||||
code_key = 'qs_a1r_lc'
|
||||
elif admin_level != 'adm0':
|
||||
continue
|
||||
|
||||
if admin_level != 'adm0':
|
||||
admin1 = properties.get(name_key)
|
||||
admin1_code = properties.get(code_key)
|
||||
|
||||
regional_lang = None
|
||||
is_default = None
|
||||
if name_key:
|
||||
regional_lang, is_default = regional_languages.get((country, name_key, admin1), (None, None))
|
||||
|
||||
if code_key and not regional_lang:
|
||||
regional_lang, is_default = regional_languages.get((country, code_key, admin1_code), (None, None))
|
||||
|
||||
if not regional_lang:
|
||||
continue
|
||||
|
||||
languages = [(lang, is_default) for lang in regional_lang.split(',')]
|
||||
else:
|
||||
languages = official_languages[country].items()
|
||||
overrides = road_language_overrides.get(country)
|
||||
if overrides and overrides.values()[0]:
|
||||
languages = overrides.items()
|
||||
elif overrides:
|
||||
languages.extend(overrides.items())
|
||||
|
||||
properties['languages'] = [{'lang': lang, 'default': default}
|
||||
for lang, default in languages]
|
||||
|
||||
poly_type = rec['geometry']['type']
|
||||
if poly_type == 'Polygon':
|
||||
poly = Polygon(rec['geometry']['coordinates'][0])
|
||||
index.index_polygon(i, poly)
|
||||
index.add_polygon(poly, dict(rec['properties']))
|
||||
elif poly_type == 'MultiPolygon':
|
||||
polys = []
|
||||
for coords in rec['geometry']['coordinates']:
|
||||
poly = Polygon(coords[0])
|
||||
polys.append(poly)
|
||||
index.index_polygon(i, poly)
|
||||
|
||||
index.add_polygon(MultiPolygon(polys), dict(rec['properties']))
|
||||
else:
|
||||
continue
|
||||
|
||||
i += 1
|
||||
return index
|
||||
Reference in New Issue
Block a user