feat(nspd): get_feature_info FEATURE_COUNT=10 — multi-feature per point (#1067)
NSPD WMS GetFeatureInfo по умолчанию отдаёт 1 фичу/точку → перекрывающиеся зоны одного слоя теряются (на ЕКБ 37581 «иные ЗОУИТ» в точке возвращает 2). Портал шлёт FEATURE_COUNT=10. Добавлен параметр feature_count (default 10) → полный overlay-ингест. Tile-геометрия (256/128) не тронута — low-regression. Refs #1067 (ИРД foundation A2).
This commit is contained in:
parent
38d948f078
commit
f900096359
2 changed files with 49 additions and 1 deletions
|
|
@ -370,13 +370,21 @@ class NSPDClient:
|
||||||
lon: float,
|
lon: float,
|
||||||
lat: float,
|
lat: float,
|
||||||
buffer_m: int = 100,
|
buffer_m: int = 100,
|
||||||
|
feature_count: int = 10,
|
||||||
) -> list[NSPDFeature]:
|
) -> list[NSPDFeature]:
|
||||||
"""WMS GetFeatureInfo на точке — какие feature'ы layer'а покрывают (lon, lat).
|
"""WMS GetFeatureInfo на точке — какие feature'ы layer'а покрывают (lon, lat).
|
||||||
|
|
||||||
Использует микро-bbox вокруг точки в EPSG:3857 (WMS требует bbox + I/J
|
Использует микро-bbox вокруг точки в EPSG:3857 (WMS требует bbox + I/J
|
||||||
пиксель). Tile 256x256, I=128, J=128 — центр.
|
пиксель). Tile 256x256, I=128, J=128 — центр.
|
||||||
|
|
||||||
Returns features (может быть 0+).
|
Args:
|
||||||
|
feature_count: макс. число фич в ответе (WMS FEATURE_COUNT). Default 10
|
||||||
|
(паритет с порталом nspd.gov.ru). Без него NSPD по умолчанию отдаёт
|
||||||
|
ТОЛЬКО 1 фичу на точку — а на ЕКБ в одной точке часто пересекаются
|
||||||
|
несколько зон одного слоя (напр. 37581 «иные ЗОУИТ» вернул 2). Для
|
||||||
|
overlay-ингеста (#1067) нужно >1.
|
||||||
|
|
||||||
|
Returns features (может быть 0+, до feature_count).
|
||||||
|
|
||||||
Use cases:
|
Use cases:
|
||||||
- ВРИ конкретного участка (layer_id=875838 territorial_zones)
|
- ВРИ конкретного участка (layer_id=875838 territorial_zones)
|
||||||
|
|
@ -393,6 +401,9 @@ class NSPDClient:
|
||||||
"QUERY_LAYERS": str(layer_id),
|
"QUERY_LAYERS": str(layer_id),
|
||||||
"LAYERS": str(layer_id),
|
"LAYERS": str(layer_id),
|
||||||
"INFO_FORMAT": "application/json",
|
"INFO_FORMAT": "application/json",
|
||||||
|
# FEATURE_COUNT: NSPD по умолчанию отдаёт 1 фичу/точку → теряем
|
||||||
|
# перекрывающиеся зоны. Портал шлёт 10. Без этого overlay-ингест неполон.
|
||||||
|
"FEATURE_COUNT": str(feature_count),
|
||||||
# STYLES обязателен (пустой), иначе INFO_FORMAT игнорируется per NSPD WAF
|
# STYLES обязателен (пустой), иначе INFO_FORMAT игнорируется per NSPD WAF
|
||||||
"STYLES": "",
|
"STYLES": "",
|
||||||
"I": "128",
|
"I": "128",
|
||||||
|
|
|
||||||
|
|
@ -232,10 +232,47 @@ def test_get_feature_info_parses_response(monkeypatch: pytest.MonkeyPatch) -> No
|
||||||
assert "INFO_FORMAT=application%2Fjson" in url
|
assert "INFO_FORMAT=application%2Fjson" in url
|
||||||
assert "STYLES=" in url # required per NSPD WAF
|
assert "STYLES=" in url # required per NSPD WAF
|
||||||
assert "CRS=EPSG%3A3857" in url
|
assert "CRS=EPSG%3A3857" in url
|
||||||
|
assert "FEATURE_COUNT=10" in url # default >1 — иначе NSPD отдаёт 1 фичу/точку
|
||||||
# bbox should reflect 100m buffer (200m side)
|
# bbox should reflect 100m buffer (200m side)
|
||||||
assert "BBOX=" in url
|
assert "BBOX=" in url
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_feature_info_returns_multiple_overlapping_features(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
"""FEATURE_COUNT>1 → несколько перекрывающихся зон в одной точке (как 37581 на ЕКБ)."""
|
||||||
|
captured_urls: list[str] = []
|
||||||
|
|
||||||
|
def fake_http(url: str, **kwargs: Any) -> dict[str, Any]:
|
||||||
|
captured_urls.append(url)
|
||||||
|
return {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": [
|
||||||
|
{"id": "z1", "properties": {"reg_numb_border": "66:41-6.4125"}},
|
||||||
|
{"id": "z2", "properties": {"reg_numb_border": "66:41-6.914"}},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
monkeypatch.setattr("app.services.scrapers.nspd_client._http_get_json", fake_http)
|
||||||
|
feats = NSPDClient().get_feature_info(37581, lon=60.576, lat=56.839, feature_count=10)
|
||||||
|
assert len(feats) == 2
|
||||||
|
assert {f.properties["reg_numb_border"] for f in feats} == {"66:41-6.4125", "66:41-6.914"}
|
||||||
|
assert "FEATURE_COUNT=10" in captured_urls[0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_feature_info_custom_feature_count(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
"""feature_count прокидывается в URL (override default)."""
|
||||||
|
captured_urls: list[str] = []
|
||||||
|
|
||||||
|
def fake_http(url: str, **kwargs: Any) -> dict[str, Any]:
|
||||||
|
captured_urls.append(url)
|
||||||
|
return {"features": []}
|
||||||
|
|
||||||
|
monkeypatch.setattr("app.services.scrapers.nspd_client._http_get_json", fake_http)
|
||||||
|
NSPDClient().get_feature_info(875838, lon=60.605, lat=56.838, feature_count=25)
|
||||||
|
assert "FEATURE_COUNT=25" in captured_urls[0]
|
||||||
|
|
||||||
|
|
||||||
def test_get_features_in_bbox_url_builds_correctly(monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_get_features_in_bbox_url_builds_correctly(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
"""get_features_in_bbox использует переданный bbox без преобразования."""
|
"""get_features_in_bbox использует переданный bbox без преобразования."""
|
||||||
captured_urls: list[str] = []
|
captured_urls: list[str] = []
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue