vibes
This commit is contained in:
parent
39ef5c6646
commit
c995f12f8b
78 changed files with 4830 additions and 1619 deletions
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue