This commit is contained in:
Andras Schmelczer 2026-05-28 21:48:35 +01:00
parent 39ef5c6646
commit c995f12f8b
78 changed files with 4830 additions and 1619 deletions

View file

@ -14,8 +14,9 @@ use crate::consts::PROPERTIES_LIMIT;
use crate::data::{HistoricalPrice, RenovationEvent};
use crate::licensing::{check_license_bounds, resolve_share_code};
use crate::parsing::{
cell_for_row_cached, h3_cell_bounds, needs_parent, parse_filters_with_poi, row_passes_filters,
row_passes_poi_filters, validate_h3_resolution,
cell_for_row_cached, h3_cell_bounds, needs_parent, parse_field_indices_with_poi,
parse_filters_with_poi, row_passes_filters, row_passes_poi_filters, validate_h3_resolution,
ParsedFieldIndices,
};
use crate::state::{AppState, SharedState};
@ -30,6 +31,10 @@ pub struct HexagonPropertiesParams {
/// Optional min:max applies as a filter (exclude properties outside range).
pub travel: Option<String>,
pub offset: Option<usize>,
/// `;;`-separated numeric feature names to include in each property payload.
/// If absent, keeps the legacy behavior and returns all numeric features.
/// If empty, returns only the fixed property card fields.
pub fields: Option<String>,
/// Share-link code; grants bbox-scoped access for unlicensed users.
pub share: Option<String>,
}
@ -106,27 +111,81 @@ fn lookup_enum_value(
}
}
fn insert_feature_value(
features: &mut FxHashMap<String, f32>,
row: usize,
state: &AppState,
feature_names: &[String],
enum_values: &FxHashMap<usize, Vec<String>>,
feat_idx: usize,
) {
if feat_idx >= feature_names.len() || enum_values.contains_key(&feat_idx) {
return;
}
let value = state.data.get_feature(row, feat_idx);
if value.is_finite() {
features.insert(feature_names[feat_idx].clone(), value);
}
}
fn insert_poi_metric_value(
features: &mut FxHashMap<String, f32>,
row: usize,
state: &AppState,
metric_idx: usize,
) {
let Some(metric_name) = state.data.poi_metrics.feature_names.get(metric_idx) else {
return;
};
let value = state.data.poi_metrics.get_for_property_row(row, metric_idx);
if value.is_finite() {
features.insert(metric_name.clone(), value);
}
}
pub fn build_property(
row: usize,
state: &AppState,
feature_names: &[String],
feature_name_to_index: &FxHashMap<String, usize>,
enum_values: &FxHashMap<usize, Vec<String>>,
field_indices: &ParsedFieldIndices,
) -> Property {
let mut features = FxHashMap::default();
for (feat_idx, feat_name) in feature_names.iter().enumerate() {
if enum_values.contains_key(&feat_idx) {
continue;
if let Some(indices) = field_indices.normal.as_deref() {
for &feat_idx in indices {
insert_feature_value(
&mut features,
row,
state,
feature_names,
enum_values,
feat_idx,
);
}
let value = state.data.get_feature(row, feat_idx);
if value.is_finite() {
features.insert(feat_name.clone(), value);
} else {
for feat_idx in 0..feature_names.len() {
insert_feature_value(
&mut features,
row,
state,
feature_names,
enum_values,
feat_idx,
);
}
}
for (metric_idx, metric_name) in state.data.poi_metrics.feature_names.iter().enumerate() {
let value = state.data.poi_metrics.get_for_property_row(row, metric_idx);
if value.is_finite() {
features.insert(metric_name.clone(), value);
if field_indices.normal.is_some() {
for &metric_idx in &field_indices.poi {
insert_poi_metric_value(&mut features, row, state, metric_idx);
}
} else {
for metric_idx in 0..state.data.poi_metrics.feature_names.len() {
insert_poi_metric_value(&mut features, row, state, metric_idx);
}
}
@ -241,6 +300,17 @@ pub async fn get_hexagon_properties(
let has_poi_filters = !parsed_poi_filters.is_empty();
let travel_entries = parse_optional_travel(params.travel.as_deref())
.map_err(|err| (StatusCode::BAD_REQUEST, err).into_response())?;
let field_indices = parse_field_indices_with_poi(
params.fields.as_deref(),
&state.feature_name_to_index,
&state.data.poi_metrics.name_to_index,
)
.map_err(|err| (err.0, err.1).into_response())?;
let fields_count = field_indices
.normal
.as_ref()
.map(|indices| (indices.len() + field_indices.poi.len()) as i32)
.unwrap_or(-1);
let result = tokio::task::spawn_blocking(move || {
let t0 = std::time::Instant::now();
@ -309,6 +379,7 @@ pub async fn get_hexagon_properties(
feature_names,
feature_name_to_index,
enum_values,
&field_indices,
)
})
.collect();
@ -322,6 +393,7 @@ pub async fn get_hexagon_properties(
offset,
filters = num_filters,
filters_raw = filters_str.as_deref().unwrap_or("-"),
fields = fields_count,
travel_entries = travel_entries.len(),
ms = format_args!("{:.1}", elapsed.as_secs_f64() * 1000.0),
"GET /api/hexagon-properties"