Skip to main content

Aim Guide

Operators reported two things about the 60° image on the location page: it is hard to tell exactly where the centre is, and after a double-click there is no sense of how far the camera went. The image has no centre marker at all — the heading ruler (MeasurementTickOverlay) is switched off there — so people were judging the centre by eye.

The aim guide is an opt-in reticle drawn over the photo. It is off by default, lives behind the Aim guide button in the camera control group (on a portrait phone, the Show aim guide row of the more-options drawer), and is remembered per browser.

What it draws​

At rest — an open-centre crosshair. The arms are 30% of the image height in each direction and stop short of an 8px ring, so the exact centre pixel is never covered. The arms carry tick marks. No chip at rest: the frame already prints its pan in the heading tick on the top edge and its full pose in the stamp along the bottom.

While aiming (operators with control rights, hover-capable devices) — the major ticks get their degree labels, a gold target ring follows the pointer with a dashed vector from the centre, and a chip beside the pointer reads the pan and tilt a double-click there would move the camera to: → 144.70° · 3.90°. The frame's own stamp shows where the centre is, the pointer chip shows where it would go, and the ticks between them give the distance. Both print two decimals.

While a move is in flight — the arm segments light gold out to the target's column and row, the ghost ring holds at the target with a moving to 140.40° · 1.00° chip, and the whole thing fades once the new frame lands. The ghost is projected through the live centre, so when the camera reports its new position (before the new frame arrives) the ring and the gold segments glide into the centre. The chip names the absolute target for the same reason: a signed offset would read 0.0° the moment the position updates. For the zoom tool the box being drawn gets a gold outline with its centre marked, since that centre is where the camera will point.

While a destination is typed in Go to position — a dashed gold ring where it lands in the frame with a go to … chip, or a chevron on the frame edge in the direction of the move with the words for how far (turn right 85.00° · tilt up 12.00°) when it is outside the frame. The preview goes away when the form closes.

Arrow keys (operators, guide on) — each press moves the camera by one major tick of the guide in that direction; Shift makes it one minor tick. The ticks are what the guide is showing, so the move is exactly the distance the operator is looking at. Never while typing, while a move is in flight, while a modal or the Go to position form is open, or while Peak Finder's fine-tune owns the arrow keys.

Why the ticks are true angles​

Every tick is placed with directionToPixel, the same pinhole model the double-click handler uses, so a tick labelled 5° sits exactly where a double-click would move the camera by 5°. A "pixels per degree" ruler would be ~1.5° off mid-frame at the wide end, which is worse than the whole point of the feature.

The tick step adapts to zoom and to the frame's on-screen width (pickAimGuideTickStep in util/cameras/aimGuide.ts): the major step is the finest of 1°, 2°, 5°, 10° that leaves at least 28px between ticks at the current field of view, and its minor (0.2°, 0.5°, 1°, 2° respectively) appears only with 12px of room. Fully zoomed out on a desktop frame you see 2° ticks (5° on a phone-width one); fully zoomed in, 1° ticks with 0.2° minors. The ruler never becomes a comb.

Where it is wired​

PieceRole
util/cameras/aimGuide.tsPure geometry: tick step, tick positions, target projection, chip formatting
stores/aimGuideStore.tsThe on/off choice, persisted in localStorage (aim-guide-storage)
AimGuideToggleThe button in CameraControlButtons, next to the pan/zoom toggle; gold when on, like the Peak Finder toggle. Signed-in users only, on desktop and in the phone drawer; a signed-out visitor never sees the toggle or the reticle, even if the browser remembers it as on
AimGuideOverlayPresentational layer rendered by XYMouseTrackerOverlay for the 60° view
goToPositionStore.previewThe typed destination the overlay previews while the Go to position form is open
lib/featureFlags.ts — enable-aim-guideRollout gate; off by default, on for localhost

XYMouseTrackerOverlay already owns everything the reticle needs — the image box it sizes itself to, the camera's centre, the pointer's position in pixels and degrees, the pending PTZ update and the zoom-box rectangle — so the overlay is fed from there and holds no state of its own except the ghost's fade.

Guard rails​

  • The layer is pointer-events: none: double-click, the zoom box and the locate click behave exactly as before.
  • It is not drawn in playback mode or on the 360° strip, and the hover layer never shows on touch devices (ViewportContext.canHover), so taps are never swallowed.
  • Every mark is white over a dark halo, no filled backgrounds, so it reads on sky, haze and night frames.
  • Off means off: nothing is drawn, no chip, no ghost.