stimmenfryslan/stimmen/geojson.py

81 lines
3.0 KiB
Python

from pygeoif.geometry import mapping
from shapely.geometry import shape
from shapely.geometry.point import Point
def merge_features(geojson, condition, aggregate={}):
"""Merge the geometries using shapely's union for all the geojson's features that
meet the condition, condition get's passed an item of feature. Then aggregate the properties
in the aggregate dict using a function that get as input the list of property values off alle
matched features. Operates inplace."""
indices = [index for index, feature in enumerate(geojson['features']) if condition(feature)]
if len(indices) == 0: # also if there is one index, we
return geojson
properties = {
prop: agg([
properties_[prop]
for index in indices
for properties_ in [geojson['features'][index]['properties']]
if prop in properties_
])
for prop, agg in aggregate.items()
}
properties.update({
key: value
for index in indices
for key, value in geojson['features'][index]['properties'].items()
if key not in aggregate
})
if len(indices) == 1:
geojson['features'][indices[0]]['properties'] = properties
return geojson
union = shape(geojson['features'][indices[0]]['geometry'])
for index in indices[1:]:
union = union.union(shape(geojson['features'][index]['geometry']))
for index in indices[::-1]: # reverse, such that the 'todo' indices willnot change.
del geojson['features'][index]
geojson['features'].append({
'geometry': mapping(union),
'properties': properties
})
return geojson
def inject_geojson_regions_into_dataframe(
geojson, dataframe,
latitude_column='latitude', longitude_column='longitude',
region_name_property='name',
region_name_column='region'
):
"""adds a region_name_column column to the dataframe with the region name as specified
in the region_name_property of the geojson, by checking which geojson feature geometrically
contains the longitude and latitude of the dataframe's row. This allows for faster cross
reference between the geojson and the dataframe compared to always checking shape-point
containment when cross referencing. Operates in place."""
shapes = {
feature['properties'][region_name_property]: shape(feature['geometry'])
for feature in geojson['features']
}
def get_region_name(point):
nonlocal shapes
for region_name, region_shape in shapes.items():
if region_shape.contains(point):
return region_name
point_to_region_name = {
(latitude, longitude): get_region_name(point)
for latitude, longitude in set(zip(dataframe[latitude_column], dataframe[longitude_column]))
for point in [Point(longitude, latitude)] # alias
}
dataframe[region_name_column] = [
point_to_region_name[(latitude, longitude)]
for latitude, longitude in zip(dataframe[latitude_column], dataframe[longitude_column])
]
return dataframe