const {useState: useStudioState, useRef: useStudioRef, useEffect: useStudioEffect} = React;
function BuildingSearch({onSelect}) {
const [lat,setLat]=useStudioState('44.6488'), [lon,setLon]=useStudioState('-63.5752');
const [radius,setRadius]=useStudioState('150'), [rows,setRows]=useStudioState([]), [selected,setSelected]=useStudioState(null);
const [height,setHeight]=useStudioState(9), [busy,setBusy]=useStudioState(false), [message,setMessage]=useStudioState('Halifax coordinates are preselected. Adjust them to your project site.');
const controller=useStudioRef(null), cache=useStudioRef(new Map());
useStudioEffect(() => () => controller.current?.abort(), []);
async function search(event) {
event.preventDefault(); setRows([]); setSelected(null);
try {
const box=BuildingData.searchBounds(lat,lon,radius), key=box.join(',');
setBusy(true); setMessage('Searching only the submitted area…');
let results=cache.current.get(key);
if (!results) {
controller.current = new AbortController();
const timeout=setTimeout(()=>controller.current.abort(),25000);
try {
const response=await fetch('https://overpass-api.de/api/interpreter',{method:'POST',body:new URLSearchParams({data:`[out:json][timeout:20][maxsize:8388608];way["building"](${key})->.buildings;.buildings out geom 151;(node["addr:housenumber"](${key});node["addr:full"](${key});node(w.buildings)["addr:housenumber"];);out body 2001;`}), signal:controller.current.signal});
if (!response.ok) throw new Error(`Building service unavailable (${response.status}). Try again shortly or upload a floorplan.`);
const data=await response.json();
if (data.remark) throw new Error('Building service could not complete the query. Reduce the area and retry.');
results=BuildingData.describeBuildings(data.elements || [],Number(lat),Number(lon)); cache.current.set(key,results);
} finally {clearTimeout(timeout);}
}
setRows(results.slice(0,150)); setMessage(results.length ? `${Math.min(results.length,150)} buildings found.${results.length>150?' Result limit reached; narrow the area.':''} Select an address below; closest buildings are listed first.` : 'No supported building footprints in this area. Try nearby coordinates or upload a floorplan.');
} catch(error) {setMessage(error.name==='AbortError'?'Search cancelled or timed out. Reduce the area and retry.':error.message);}
finally {setBusy(false);}
}
const ring=selected?.geometry || [];
const xy=ring.map(p=>[(p.lon-Number(lon))*Math.cos(Number(lat)*Math.PI/180),-p.lat]);
const xs=xy.map(p=>p[0]),ys=xy.map(p=>p[1]);
const w=Math.max(...xs)-Math.min(...xs)||1,h=Math.max(...ys)-Math.min(...ys)||1;
const span=Math.max(w,h), points=xy.map(p=>`${25+(p[0]-Math.min(...xs))/span*200},${25+(p[1]-Math.min(...ys))/span*200}`).join(' ');
return
Small bounding-box search around your coordinates. No region-wide model download. Addresses come from mapped buildings and address points inside their footprints. Missing addresses are marked explicitly. Supports closed building ways; complex multipolygon buildings are not included.
{message}
{rows.map(row=>)}
{selected&&
{selected.label}View building on map ↗{BuildingData.heightInfo(selected.tags).source}. Exterior only; confirm height and materials.