Skip to content

Commit

Permalink
feat: calculate distance to find most logical closest feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Joonalai committed Oct 30, 2023
1 parent b3f09db commit 0f489f1
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 11 deletions.
33 changes: 22 additions & 11 deletions pickLayer/core/set_active_layer_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,20 +141,24 @@ def _get_default_search_radius(self) -> float:
* context.mapToPixel().mapUnitsPerPixel()
)

def _get_distance_to_feature_on_layer(
def _get_distances_to_feature_on_layer(
self,
layer: QgsVectorLayer,
feature: QgsFeature,
origin_map_point: QgsPointXY,
) -> float:
) -> tuple[float, float]:
# for unknown reasons saving all geoms to variables avoids fatal exs
origin_layer_point = self.toLayerCoordinates(layer, origin_map_point)
origin_geom = QgsGeometry.fromPointXY(origin_layer_point)
feature_geom = feature.geometry()
closest_geom = feature_geom.nearestPoint(origin_geom)
closest_layer_point = closest_geom.asPoint()
closest_map_point = self.toMapCoordinates(layer, closest_layer_point)
return origin_map_point.distance(closest_map_point)
distance_to_feature = origin_map_point.distance(closest_map_point)
centroid_geom = feature_geom.centroid().asPoint()
centroid_map_point = self.toMapCoordinates(layer, centroid_geom)
distance_to_centroid = centroid_map_point.distance(closest_map_point)
return distance_to_feature, distance_to_centroid

def _choose_layer_from_identify_results(
self,
Expand All @@ -170,32 +174,39 @@ def _choose_layer_from_identify_results(
best_match: Optional[QgsVectorLayer] = None
best_match_geom_type_preference = 0
best_match_distance = 0.0
best_match_distance_to_centroid = 0.0

for result in results:
if not isinstance(result.mLayer, QgsVectorLayer):
continue

(
distance_to_feature,
distance_to_centroid,
) = self._get_distances_to_feature_on_layer(
result.mLayer, result.mFeature, origin_map_coordinates
)
if (
best_match is None
or geom_type_preference.get(result.mLayer.geometryType(), 99)
< best_match_geom_type_preference
or (
geom_type_preference.get(result.mLayer.geometryType(), 99)
== best_match_geom_type_preference
and self._get_distance_to_feature_on_layer(
result.mLayer, result.mFeature, origin_map_coordinates
and (
(distance_to_feature < best_match_distance)
or (
distance_to_feature <= best_match_distance
and distance_to_centroid < best_match_distance_to_centroid
)
)
< best_match_distance
)
):
best_match = result.mLayer
best_match_geom_type_preference = geom_type_preference.get(
result.mLayer.geometryType(), 99
)
best_match_distance = self._get_distance_to_feature_on_layer(
result.mLayer,
result.mFeature,
origin_map_coordinates,
)
best_match_distance = distance_to_feature
best_match_distance_to_centroid = distance_to_centroid

return best_match
32 changes: 32 additions & 0 deletions test/unit/test_set_active_layer_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,38 @@ def test_top_polygon_chosen_from_multiple_nested_even_if_top_not_closest(
assert call_layer.name() == "polygon-0-10"


def test_bottom_polygon_chosen_from_multiple_nested_when_bottom_closest(
map_tool: SetActiveLayerTool,
mocker: MockerFixture,
qgis_iface: QgisInterface,
qgis_new_project,
):
results = create_identify_result(
[
("POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))", "EPSG:3067", "polygon-0-10"),
("POLYGON((3 3, 3 5, 5 5, 5 3, 3 3))", "EPSG:3067", "polygon-3-5"),
]
)

QgsProject.instance().setCrs(QgsCoordinateReferenceSystem("EPSG:3067"))
map_tool.canvas().setDestinationCrs(QgsCoordinateReferenceSystem("EPSG:3067"))

mocker.patch.object(map_tool, "identify", return_value=results)

m_set_active_layer = mocker.patch.object(
qgis_iface, "setActiveLayer", return_value=None
)

# emulate click near the center of 3-5
map_tool.set_active_layer_using_closest_feature(QgsPointXY(4.2, 4.2))

m_set_active_layer.assert_called_once()

args, _ = m_set_active_layer.call_args_list[0]
call_layer = args[0]
assert call_layer.name() == "polygon-3-5"


def test_only_raster_present_no_change_active_layer(
map_tool: SetActiveLayerTool,
mocker: MockerFixture,
Expand Down

0 comments on commit 0f489f1

Please sign in to comment.