def location_survey_procedure() -> cgp.ProcedureSequence:
"""Client-side capture procedure for the GPS survey."""
root = cgp.GetRootDirectory()
session = root.new_session("LocationSurvey")
# Show instructions
instructions = cgp.show_instructions(
title="Location Survey",
text="Walk to the guided location and record a 360° spin video.",
)
# Guide user to server-assigned location
target_location = root.get_location_file().load(
or_else=cgp.NullProcedure(return_type=cgp.PLocation)
)
check_location = cgp.GuideToLocation(
target=target_location, threshold_meters=LOCATION_SIGMA_M
)
# Capture panoramic video
video = cgp.CaptureVideo(label="Record 360° Spin Video")
save_video = session.new_file("spin_video").save(video)
return cgp.ProcedureSequence(
label="Location Survey Capture",
procedures=[instructions, check_location, save_video],
)
def __capture_location__(user_id, target, persistence) -> cg.Location:
"""Compute optimal location using void-and-cluster on GPS coordinates."""
sessions = target.LocationSurvey
distance_fn = cgsh.distance.combine(
location=cgsh.distance.location(sigma_m=10.0)
)
store = cgsh.organize.BatchedDistanceStore(
persistence=persistence,
sessions=sessions,
distance_fn=distance_fn,
threshold=1.0,
)
if store.is_empty():
# Generate candidates on grid within bounds
candidates = cg.List([])
candidates.location = cgsh.forecast.locations_area(
bounds=BOUNDS, resolution_meters=1.0,
)
# Select batch using void-and-cluster (maximize distance)
slots = cgsh.select_sessions(
potential_sessions=candidates,
previous_sessions=sessions,
distance_fn=distance_fn,
energy_fn=lambda d: -d,
energy_mode="max",
selections=8,
)
store.set_batch(list(slots))
slot = store.request_slot(user_id)
return slot.location if slot else BOUNDS[0]