Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Find most logical closest feature #26

Merged
merged 2 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 4 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ pickLayer=
**/*.ts
**/*.qm
**/*.ui

[options.entry_points]
qgis_plugin_tools =
pickLayer = pickLayer
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
Loading