From cd82244dcd80fc2e09d423b2ee76d8c65de508cf Mon Sep 17 00:00:00 2001 From: grigo Date: Wed, 24 Jun 2026 10:17:50 +0300 Subject: [PATCH] updated api --- server/static/app.js | 120 +++++++++++++++++++++++++++++++++++++++ server/static/index.html | 2 + 2 files changed, 122 insertions(+) diff --git a/server/static/app.js b/server/static/app.js index 1c96fc3..03f5a0e 100644 --- a/server/static/app.js +++ b/server/static/app.js @@ -1873,6 +1873,91 @@ function renderApiDebugViewshedLayer(geojson) { }); } +function fitApiDebugLayer(layer) { + const bounds = layer?.getBounds?.(); + if (bounds?.isValid?.()) { + setMapViewProgrammatically(() => map.fitBounds(bounds, { padding: [40, 40], maxZoom: 16 })); + } +} + +function renderApiDebugFresnelSlice(data) { + clearApiDebugCoverage(); + clearApiDebugBeam(); + const layers = []; + const pathLayer = data?.path_geojson ? L.geoJSON(data.path_geojson, { + style: { color: '#4fc3f7', weight: 3, opacity: 0.95 } + }) : null; + const fresnelLayer = data?.fresnel_geojson ? L.geoJSON(data.fresnel_geojson, { + style: { + color: '#ffd54f', + weight: 2, + opacity: 0.95, + fillColor: '#ffd54f', + fillOpacity: 0.2 + } + }) : null; + const buildingsLayer = data?.buildings_geojson ? L.geoJSON(data.buildings_geojson, { + style: { + color: '#ff1744', + weight: 1.5, + opacity: 0.95, + fillColor: '#ff1744', + fillOpacity: 0.3 + }, + onEachFeature: (feature, layer) => { + const p = feature.properties || {}; + const parts = []; + if (p.height_m != null) parts.push(`height ${fmtNum(p.height_m, 1, ' m')}`); + if (p.intersection_area_m2 != null) parts.push(`пересечение ${fmtNum(p.intersection_area_m2, 1, ' m²')}`); + if (p.building_type) parts.push(escapeHtml(p.building_type)); + if (parts.length) layer.bindPopup(parts.join(' · ')); + } + }) : null; + [fresnelLayer, buildingsLayer, pathLayer].forEach(layer => { + if (layer) layers.push(layer); + }); + if (!layers.length) { + apiDebugSetSummary('Ответ fresnel-slice не содержит GeoJSON слоёв.'); + return; + } + apiDebugCoverageLayer = L.featureGroup(layers).addTo(map); + fitApiDebugLayer(apiDebugCoverageLayer); + const buildings = data?.buildings_geojson?.features?.length ?? 0; + const profilePoints = data?.profile?.length ?? 0; + apiDebugSetSummary( + `Срез зоны Френеля: ${profilePoints} точек профиля · ${buildings} зданий пересекают область
` + + `Жёлтая область — горизонтальный срез 60% зоны Френеля, красные полигоны — пересекающие здания.` + + formatDataSourcesLine(data?.data_sources) + ); +} + +function renderApiDebugBuildings(data) { + const features = data?.features?.length ?? 0; + const rendered = renderApiDebugGeoJsonLayer(data, { + style: { + color: '#ff1744', + weight: 1.3, + opacity: 0.95, + fillColor: '#ff1744', + fillOpacity: 0.22 + }, + onEachFeature: (feature, layer) => { + const p = feature.properties || {}; + const parts = []; + if (p.height_m != null) parts.push(`height ${fmtNum(p.height_m, 1, ' m')}`); + if (p.levels != null) parts.push(`${p.levels} эт.`); + if (p.building_type) parts.push(escapeHtml(p.building_type)); + if (p.source) parts.push(escapeHtml(p.source)); + if (parts.length) layer.bindPopup(parts.join(' · ')); + } + }); + apiDebugSetSummary( + rendered + ? `Здания вдоль линии: ${features} объектов в буфере 50 m${formatDataSourcesLine(data?.data_sources)}` + : 'Ответ buildings/query не содержит GeoJSON features.' + ); +} + async function fetchApiDebugJobArtifact(jobId) { const f = apiDebugReadForm(); const url = `${f.base}/api/v1/jobs/${encodeURIComponent(jobId)}/artifact`; @@ -2221,6 +2306,23 @@ function buildApiDebugRequest(kind) { } }; } + if (kind === 'fresnelSlice') { + return { + method: 'POST', + path: '/api/v1/terrain/fresnel-slice', + body: { + tx, + rx, + frequency_mhz: f.frequency, + fresnel_fraction: 0.6, + include_buildings: true, + include_canopy: f.includeCanopy, + k_factor: 1.333, + samples: f.samples, + building_limit: 5000 + } + }; + } if (kind === 'budget') { return { method: 'POST', @@ -2237,6 +2339,22 @@ function buildApiDebugRequest(kind) { } }; } + if (kind === 'buildings') { + return { + method: 'POST', + path: '/api/v1/buildings/query', + body: { + path: { + type: 'LineString', + coordinates: [ + [f.tx.lon, f.tx.lat], + [f.rx.lon, f.rx.lat] + ] + }, + buffer_m: 50 + } + }; + } if (kind === 'landcover') { return { method: 'POST', @@ -2429,7 +2547,9 @@ async function runApiDebugDemo(kind) { } if (kind === 'profile') renderApiDebugProfile(data, null, null); else if (kind === 'los') renderApiDebugProfile(null, data, null); + else if (kind === 'fresnelSlice') renderApiDebugFresnelSlice(data); else if (kind === 'budget') apiDebugSetSummary(formatApiDebugBudgetSummary(data)); + else if (kind === 'buildings') renderApiDebugBuildings(data); else if (kind === 'landcover') apiDebugSetSummary(formatApiDebugLandcoverSummary(data)); else if (kind === 'elevation') apiDebugSetSummary(formatApiDebugPointSummary(data)); else if (kind === 'beam') renderApiDebugBeam(data); diff --git a/server/static/index.html b/server/static/index.html index d313763..5a99363 100644 --- a/server/static/index.html +++ b/server/static/index.html @@ -71,7 +71,9 @@ + +