# Feature: Candidate Retrieval

## Description
Query satellite database and retrieve ranked tile candidates. Orchestrates the full place recognition pipeline: descriptor computation → database query → candidate ranking. Supports both single-image and chunk-based retrieval for "kidnapped robot" recovery.

## Component APIs Implemented
- `query_database(descriptor: np.ndarray, top_k: int) -> List[DatabaseMatch]`
- `rank_candidates(candidates: List[TileCandidate]) -> List[TileCandidate]`
- `retrieve_candidate_tiles(image: np.ndarray, top_k: int) -> List[TileCandidate]`
- `retrieve_candidate_tiles_for_chunk(chunk_images: List[np.ndarray], top_k: int) -> List[TileCandidate]`

## External Tools and Services
- **H04 Faiss Index Manager**: For `search()` operation
- **F04 Satellite Data Manager**: For tile metadata retrieval (gps_center, bounds) after Faiss returns indices
- **F12 Route Chunk Manager**: Provides chunk images for chunk-based retrieval

## Internal Methods

### `_distance_to_similarity(distance: float) -> float`
Converts L2 distance to normalized similarity score [0, 1].

### `_retrieve_tile_metadata(indices: List[int]) -> List[TileMetadata]`
Fetches GPS center and bounds for tile indices from F04 Satellite Data Manager.

### `_apply_spatial_reranking(candidates: List[TileCandidate], dead_reckoning_estimate: Optional[GPSPoint]) -> List[TileCandidate]`
Re-ranks candidates based on proximity to dead-reckoning estimate if available.

### `_apply_trajectory_reranking(candidates: List[TileCandidate], previous_trajectory: Optional[List[GPSPoint]]) -> List[TileCandidate]`
Favors tiles that continue the previous trajectory direction.

### `_filter_by_geofence(candidates: List[TileCandidate], geofence: Optional[BoundingBox]) -> List[TileCandidate]`
Removes candidates outside operational geofence.

## Unit Tests

### query_database

#### Test: Returns Top-K Matches
- Input: Valid descriptor, top_k=5
- Verify: Returns exactly 5 DatabaseMatch objects

#### Test: Ordered by Distance
- Input: Any descriptor
- Verify: Matches sorted by ascending distance

#### Test: Similarity Score Range
- Input: Any query
- Verify: All similarity_score values in [0, 1]

#### Test: Empty Database
- Input: Query when no index loaded
- Verify: Returns empty list (not exception)

#### Test: Query Performance
- Input: Large database (10,000 tiles)
- Verify: Query completes in <50ms

### rank_candidates

#### Test: Preserves Order Without Heuristics
- Input: Candidates without dead-reckoning estimate
- Verify: Order unchanged (similarity-based)

#### Test: Spatial Reranking Applied
- Input: Candidates + dead-reckoning estimate
- Verify: Closer tile promoted in ranking

#### Test: Tie Breaking
- Input: Two candidates with similar similarity scores
- Verify: Spatial proximity breaks tie

#### Test: Geofence Filtering
- Input: Candidates with some outside geofence
- Verify: Out-of-bounds candidates removed

### retrieve_candidate_tiles

#### Test: End-to-End Single Image
- Input: UAV image, top_k=5
- Verify: Returns 5 TileCandidate with valid gps_center

#### Test: Correct Tile in Top-5
- Input: UAV image with known location
- Verify: Correct tile appears in top-5 (Recall@5 test)

#### Test: Performance Budget
- Input: FullHD UAV image
- Verify: Total time <200ms (descriptor ~150ms + query ~50ms)

### retrieve_candidate_tiles_for_chunk

#### Test: End-to-End Chunk
- Input: 10 chunk images, top_k=5
- Verify: Returns 5 TileCandidate

#### Test: Chunk More Accurate Than Single
- Input: Featureless terrain images
- Verify: Chunk retrieval finds correct tile where single-image fails

#### Test: Recall@5 > 90%
- Input: Various chunk scenarios
- Verify: Correct tile in top-5 at least 90% of test cases

## Integration Tests

### Test: Faiss Manager Integration
- Verify: query_database correctly delegates to H04 Faiss Index Manager

### Test: Satellite Data Manager Integration
- Verify: Tile metadata correctly retrieved from F04 after Faiss query

### Test: Full Pipeline Single Image
- Setup: Load index, prepare UAV image
- Action: retrieve_candidate_tiles()
- Verify: Returns valid candidates with GPS coordinates

### Test: Full Pipeline Chunk
- Setup: Load index, prepare chunk images
- Action: retrieve_candidate_tiles_for_chunk()
- Verify: Returns valid candidates, more robust than single-image

### Test: Season Invariance
- Setup: Satellite tiles from summer, UAV image from autumn
- Action: retrieve_candidate_tiles()
- Verify: Correct match despite appearance change

### Test: Recall@5 Benchmark
- Input: Test dataset of 100 UAV images with ground truth
- Verify: Recall@5 > 85% for single-image, > 90% for chunk

