diff --git a/pickLayer/core/set_active_layer_tool.py b/pickLayer/core/set_active_layer_tool.py index 862c6ba..f7c26c0 100644 --- a/pickLayer/core/set_active_layer_tool.py +++ b/pickLayer/core/set_active_layer_tool.py @@ -141,12 +141,12 @@ 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) @@ -154,7 +154,11 @@ def _get_distance_to_feature_on_layer( 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, @@ -170,11 +174,18 @@ 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) @@ -182,20 +193,20 @@ def _choose_layer_from_identify_results( 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 diff --git a/setup.cfg b/setup.cfg index 6127fe2..1686876 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,3 +38,7 @@ pickLayer= **/*.ts **/*.qm **/*.ui + +[options.entry_points] +qgis_plugin_tools = + pickLayer = pickLayer diff --git a/test/unit/test_set_active_layer_tool.py b/test/unit/test_set_active_layer_tool.py index b3db3c6..b5efcc8 100644 --- a/test/unit/test_set_active_layer_tool.py +++ b/test/unit/test_set_active_layer_tool.py @@ -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,