Introduction
Which known location is each of these observations closest to, and how far away is it? FindNearestPointOfInterest answers that for every row in a DataFrame of observations against a separate DataFrame of reference points — facilities, checkpoints, or prior sightings. It's a companion to CalculateHaversineDistance: where that function computes every pairwise distance within a single set of points, this one indexes the reference points in a BallTree with the haversine metric, so nearest-neighbor lookups scale efficiently even against a large reference set (thousands of facilities or checkpoints), rather than computing a full observation-by-reference distance matrix.
Nearest-point analysis is one of the most common bridges between raw location data and an analytic judgment. A list of coordinates — a sensor ping, a reported sighting, a delivery address — means little on its own; it becomes useful the moment it is related to something known, like "2.3 km from the nearest checkpoint" or "closest to the Northgate warehouse." That relationship is what turns a bare coordinate into context an analyst can reason about: flagging observations that fall suspiciously far from any known facility, assigning each observation to its nearest service area, or measuring how access to a resource (a clinic, a polling place, a water source) varies across a population.
The BallTree with a haversine metric is the standard efficient structure for this kind of query on the sphere. A naive approach would compute the distance from every observation to every reference point, which becomes prohibitively slow as both sets grow. A BallTree instead organizes the reference points hierarchically so that most candidates can be ruled out without ever computing their exact distance, which is why it is the same machinery used by ConductClusterAnalysis's DBSCAN pass.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
dataframerequired | pd.DataFrame | — | A pandas DataFrame of observations, with one row per point, that you want to match to the nearest reference point(s). |
points_of_interestrequired | pd.DataFrame | — | A pandas DataFrame of reference points (e.g., facilities, checkpoints, prior sightings) to search for the nearest match. |
longitude_column | str | 'lon' | Name of the column in dataframe containing longitude values, in decimal degrees. Defaults to 'lon'. |
latitude_column | str | 'lat' | Name of the column in dataframe containing latitude values, in decimal degrees. Defaults to 'lat'. |
poi_longitude_column | str | 'lon' | Name of the column in points_of_interest containing longitude values, in decimal degrees. Defaults to 'lon'. |
poi_latitude_column | str | 'lat' | Name of the column in points_of_interest containing latitude values, in decimal degrees. Defaults to 'lat'. |
id_column | str | None | Optional name of a column in dataframe that uniquely identifies each observation. If provided, these labels are carried into the output. If None, the DataFrame's index is used instead. Defaults to None. |
poi_id_column | str | None | Optional name of a column in points_of_interest that labels each reference point (e.g., a facility name). If provided, this label is returned alongside each match. If None, the points_of_interest index is used instead. Defaults to None. |
number_of_neighbors | int | 1 | How many nearest reference points to return for each observation, ranked closest first. Defaults to 1. |
distance_unit | str | 'km' | Unit for the returned distances. One of 'km' (kilometers), 'mi' (miles), or 'nm' (nautical miles). Defaults to 'km'. |
map_matches | bool | False | Whether to generate and display an interactive Folium map showing each observation connected by a line to its single nearest reference point. Only applies when number_of_neighbors=1. Defaults to False. |
Returns
A copy of dataframe with number_of_neighbors additional rows per observation (one per ranked match), plus columns for the matched reference point's identifier, its coordinates, the distance to it (named distance_km, distance_mi, or distance_nm to match distance_unit), and neighbor_rank (1 = nearest).
Example
from analysistoolbox.geospatial_analysis import FindNearestPointOfInterest
import pandas as pd
# Match field observations to the nearest known facility
observations_df = pd.DataFrame({
'sighting_id': ['S1', 'S2', 'S3'],
'lat': [34.0522, 34.1478, 33.9425],
'lon': [-118.2437, -118.1445, -118.4081]
})
facilities_df = pd.DataFrame({
'facility_name': ['Northgate Warehouse', 'Southside Depot'],
'lat': [34.0600, 33.9500],
'lon': [-118.2500, -118.4000]
})
matches_df = FindNearestPointOfInterest(
observations_df,
facilities_df,
id_column='sighting_id',
poi_id_column='facility_name'
)
# One row per sighting, each paired with its closest facility and the
# distance to it in kilometers.