Skip to content

Commit

Permalink
Proposed a fix to the tests.
Browse files Browse the repository at this point in the history
Also added first pass version of dividing by N for that number of feature_collections
  • Loading branch information
jonaraphael committed Nov 4, 2023
1 parent 3faae6b commit 35ba939
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 56 deletions.
71 changes: 30 additions & 41 deletions cerulean_cloud/cloud_run_orchestrator/merging.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
"""merging inference from base and offset tiles"""
from typing import List

import geojson
import geopandas as gpd
import networkx as nx
import pandas as pd
from typing import List


def reproject_to_utm(gdf_wgs84):
"""Finds utm projection for a WGS84 gdf"""
utm_crs = gdf_wgs84.estimate_utm_crs(datum_name="WGS 84")
return gdf_wgs84.to_crs(utm_crs)


def merge_inferences(
feature_collections: List[geojson.FeatureCollection], # XXXC >> USING THIS AS A TEST replacement for base_tile_fc and offset_tile_fc
isolated_conf_multiplier: float = None, # THIS IS FURTHER DEFINED ON LINES 39-40 as 1 / len(feature_collections)
feature_collections: List[
geojson.FeatureCollection
], # XXXC >> USING THIS AS A TEST replacement for base_tile_fc and offset_tile_fc
proximity_meters: int = 500,
closing_meters: int = 0,
opening_meters: int = 0,
) -> geojson.FeatureCollection:
"""
Merge base and all offset tile inference.
This function takes a list of geojson FeatureCollections and merges them together. During the merge, the
geometries can be adjusted to incorporate neighboring features based on the proximity setting. The
This function takes a list of geojson FeatureCollections and merges them together. During the merge, the
geometries can be adjusted to incorporate neighboring features based on the proximity setting. The
confidence of isolated features can also be adjusted.
Parameters:
Expand All @@ -35,34 +38,17 @@ def merge_inferences(
Returns:
A merged geojson FeatureCollection.
"""

# Define the isolated_conf_multiplier
if isolated_conf_multiplier is None:
isolated_conf_multiplier = 1 / len(feature_collections)

# Combined GeoDataFrames. Only appended inf all FeatureCollections have at least 1 feature.
# gdfs_for_processing = []

# Check that all FeatureCollections have features. This throws out any detection if it is not present in
# all tiles of the feature_collections.
# if any(len(fc["features"]) == 0 for fc in feature_collections):
# shared_crs = feature_collections[0].crs
# for fc in feature_collections:
# # Convert the fc to a GeoDataFrame
# gdf = gpd.GeoDataFrame.from_features(fc["features"], crs=4326)

# # Reproject both GeoDataFrames to a UTM CRS (for accurate distance calculations)
# gdfs_for_processing.append(gdf)

# gdf_r = reproject_to_utm(gdf)

# else:
# # If one of the FeatureCollections is empty, return an empty FeatureCollection
# return geojson.FeatureCollection(features=[])

# gdfs_for_processing = [gpd.GeoDataFrame.from_features(fc["features"], crs=4326) for fc in feature_collections if fc["features"] else gpd.GeoDataFrame([],crs=4326)]
gdfs_for_processing = [gpd.GeoDataFrame.from_features(fc["features"], crs=4326) if fc["features"] else gpd.GeoDataFrame([], crs=4326) for fc in feature_collections]
gdfs_for_processing = [gdf.to_crs(reproject_to_utm(gdfs_for_processing[0])) for gdf in gdfs_for_processing]
gdfs_for_processing = [
reproject_to_utm(
gpd.GeoDataFrame.from_features(fc["features"], crs=4326).assign(fc_index=i)
)
for i, fc in enumerate(feature_collections)
if fc["features"]
]

if len(gdfs_for_processing) == 0:
# No inferences found in any tiling
return geojson.FeatureCollection(features=[])

# Concat the GeoDataFrames
concat_gdf = pd.concat(gdfs_for_processing, ignore_index=True)
Expand Down Expand Up @@ -94,13 +80,12 @@ def merge_inferences(
final_gdf["group_index"] = final_gdf.index.map(group_mapping)
final_gdf["group_count"] = final_gdf.index.map(group_counts)


# Adjust the confidence value for features that are isolated (not part of a larger group)
# XXXC >> how do we make this a little more dynamic, check against number of tile detections to
# impact the conf_multiplier value
final_gdf.loc[
final_gdf["group_count"] == 1, "machine_confidence"
] *= isolated_conf_multiplier
final_gdf["overlap_factor"] = final_gdf.groupby("group_index")[
"fc_index"
].transform(lambda x: len(x.unique()) / len(feature_collections))

final_gdf["machine_confidence"] *= final_gdf["overlap_factor"]

# Dissolve overlapping features into one based on their group index and calculate the median confidence and maximum inference index
dissolved_gdf = final_gdf.dissolve(
Expand All @@ -109,11 +94,15 @@ def merge_inferences(

# If set, apply a morphological 'closing' operation to the geometries
if closing_meters is not None:
dissolved_gdf["geometry"] = dissolved_gdf.buffer(closing_meters).buffer(-closing_meters)
dissolved_gdf["geometry"] = dissolved_gdf.buffer(closing_meters).buffer(
-closing_meters
)

# If set, apply a morphological 'opening' operation to the geometries
if opening_meters is not None:
dissolved_gdf["geometry"] = dissolved_gdf.buffer(-opening_meters).buffer(opening_meters)
dissolved_gdf["geometry"] = dissolved_gdf.buffer(-opening_meters).buffer(
opening_meters
)

# Reproject the GeoDataFrame back to WGS 84 CRS
result = dissolved_gdf.to_crs(crs=4326)
Expand Down
26 changes: 11 additions & 15 deletions test/test_cerulean_cloud/test_cloud_run_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,15 +427,13 @@ def test_flatten_result():

def test_func_merge_inferences():
with open("test/test_cerulean_cloud/fixtures/base.geojson") as src:
out_fc = dict(geojson.load(src))
base_tile_fc = dict(geojson.load(src))

with open("test/test_cerulean_cloud/fixtures/offset.geojson") as src:
out_fc_offset = dict(geojson.load(src))
offset_tile_fc = dict(geojson.load(src))

merged = merge_inferences(
# base_tile_fc=base_tile_fc,
# offset_tile_fc=offset_tile_fc,
feature_collections = [out_fc, out_fc_offset],
[base_tile_fc, offset_tile_fc],
proximity_meters=500,
closing_meters=100,
opening_meters=100,
Expand All @@ -458,21 +456,19 @@ def test_func_merge_inferences_empty():
with open("test/test_cerulean_cloud/fixtures/offset.geojson") as src:
offset_tile_fc = dict(geojson.load(src))


merged = merge_inferences(
feature_collections = [geojson.FeatureCollection(features=[]),offset_tile_fc]
)
merged = merge_inferences([geojson.FeatureCollection(features=[]), offset_tile_fc])
assert merged["type"] == "FeatureCollection"
assert len(merged["features"]) == 0
assert len(merged["features"]) == 5

merged = merge_inferences(
feature_collections=[offset_tile_fc,geojson.FeatureCollection(features=[])]
)
merged = merge_inferences([offset_tile_fc, geojson.FeatureCollection(features=[])])
assert merged["type"] == "FeatureCollection"
assert len(merged["features"]) == 0
assert len(merged["features"]) == 5

merged = merge_inferences(
feature_collections=[geojson.FeatureCollection(features=[]),geojson.FeatureCollection(features=[])]
[
geojson.FeatureCollection(features=[]),
geojson.FeatureCollection(features=[]),
],
)
assert merged["type"] == "FeatureCollection"
assert len(merged["features"]) == 0
Expand Down

0 comments on commit 35ba939

Please sign in to comment.