81 lines
3.0 KiB
Python
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
|
|
|