use crate::convert::mesh_to_center;
use crate::spatial::range::MeshCodeIterator;
use crate::types::{BoundingBox, Coordinate, MeshCode, MeshLevel};
use crate::utils::distance::{calculate_bbox_offsets, haversine_distance};
pub struct MeshCodeRadiusIterator {
bbox_iter: MeshCodeIterator,
center: Coordinate,
radius_meters: f64,
center_mesh_for_zero_radius: Option<Option<MeshCode>>,
level: MeshLevel,
}
impl MeshCodeRadiusIterator {
pub fn new(center: Coordinate, radius_meters: f64, level: MeshLevel) -> Self {
if radius_meters < 0.0 {
let empty_bbox = BoundingBox::new(center, center);
return MeshCodeRadiusIterator {
bbox_iter: MeshCodeIterator::new(empty_bbox, level),
center,
radius_meters: 0.0,
center_mesh_for_zero_radius: Some(None),
level,
};
}
let (lat_offset, lon_offset) = if radius_meters == 0.0 {
(0.0, 0.0)
} else {
calculate_bbox_offsets(center, radius_meters)
};
let min_lat = (center.lat() - lat_offset).max(20.0);
let max_lat = (center.lat() + lat_offset).min(46.0);
let min_lon = (center.lon() - lon_offset).max(122.0);
let max_lon = (center.lon() + lon_offset).min(154.0);
let sw = Coordinate::new_unchecked(min_lat, min_lon);
let ne = Coordinate::new_unchecked(max_lat, max_lon);
let bbox = BoundingBox::new(sw, ne);
MeshCodeRadiusIterator {
bbox_iter: MeshCodeIterator::new(bbox, level),
center,
radius_meters,
center_mesh_for_zero_radius: None,
level,
}
}
}
impl Iterator for MeshCodeRadiusIterator {
type Item = MeshCode;
fn next(&mut self) -> Option<Self::Item> {
if self.radius_meters == 0.0 {
if self.center_mesh_for_zero_radius.is_none() {
let center_mesh = crate::convert::coord_to_mesh(self.center, self.level).ok();
self.center_mesh_for_zero_radius = Some(center_mesh);
return center_mesh;
}
return None;
}
loop {
let mesh = self.bbox_iter.next()?;
let mesh_center = mesh_to_center(mesh);
let distance = haversine_distance(self.center, mesh_center);
if distance <= self.radius_meters {
return Some(mesh);
}
}
}
}
pub fn mesh_codes_in_radius(
center: Coordinate,
radius_meters: f64,
level: MeshLevel,
) -> MeshCodeRadiusIterator {
MeshCodeRadiusIterator::new(center, radius_meters, level)
}
pub fn mesh_codes_in_radius_from_mesh(
mesh: MeshCode,
radius_meters: f64,
) -> MeshCodeRadiusIterator {
let center = mesh_to_center(mesh);
let level = mesh.level();
MeshCodeRadiusIterator::new(center, radius_meters, level)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::convert::coord_to_mesh;
#[test]
fn test_mesh_codes_in_radius_zero() {
let tokyo = Coordinate::new(35.6812, 139.7671).unwrap();
let meshes: Vec<_> = mesh_codes_in_radius(tokyo, 0.0, MeshLevel::Third).collect();
assert_eq!(meshes.len(), 1);
let expected = coord_to_mesh(tokyo, MeshLevel::Third).unwrap();
assert_eq!(meshes[0], expected);
}
#[test]
fn test_mesh_codes_in_radius_positive() {
let tokyo = Coordinate::new(35.6812, 139.7671).unwrap();
let meshes: Vec<_> = mesh_codes_in_radius(tokyo, 1000.0, MeshLevel::Third).collect();
assert!(meshes.len() > 1);
for mesh in &meshes {
let mesh_center = mesh_to_center(*mesh);
let distance = haversine_distance(tokyo, mesh_center);
assert!(
distance <= 1000.0,
"メッシュ {} の距離 {:.2}m は1000m以内",
mesh,
distance
);
}
}
#[test]
fn test_mesh_codes_in_radius_negative() {
let tokyo = Coordinate::new(35.6812, 139.7671).unwrap();
let meshes: Vec<_> = mesh_codes_in_radius(tokyo, -100.0, MeshLevel::Third).collect();
assert_eq!(meshes.len(), 0);
}
#[test]
fn test_mesh_codes_in_radius_from_mesh() {
let mesh = coord_to_mesh(
Coordinate::new(35.6812, 139.7671).unwrap(),
MeshLevel::Third,
)
.unwrap();
let nearby: Vec<_> = mesh_codes_in_radius_from_mesh(mesh, 1000.0).collect();
assert!(nearby.contains(&mesh));
assert!(nearby.len() > 1);
assert!(nearby.iter().all(|m| m.level() == mesh.level()));
}
#[test]
fn test_mesh_codes_in_radius_increasing_radius() {
let tokyo = Coordinate::new(35.6812, 139.7671).unwrap();
let meshes_500: Vec<_> = mesh_codes_in_radius(tokyo, 500.0, MeshLevel::Third).collect();
let meshes_1000: Vec<_> = mesh_codes_in_radius(tokyo, 1000.0, MeshLevel::Third).collect();
let meshes_2000: Vec<_> = mesh_codes_in_radius(tokyo, 2000.0, MeshLevel::Third).collect();
assert!(meshes_500.len() < meshes_1000.len());
assert!(meshes_1000.len() < meshes_2000.len());
}
#[test]
fn test_mesh_codes_in_radius_different_levels() {
let tokyo = Coordinate::new(35.6812, 139.7671).unwrap();
let first_level: Vec<_> = mesh_codes_in_radius(tokyo, 10000.0, MeshLevel::First).collect();
let second_level: Vec<_> =
mesh_codes_in_radius(tokyo, 10000.0, MeshLevel::Second).collect();
let third_level: Vec<_> = mesh_codes_in_radius(tokyo, 10000.0, MeshLevel::Third).collect();
assert!(first_level.len() < second_level.len());
assert!(second_level.len() < third_level.len());
assert!(first_level.iter().all(|m| m.level() == MeshLevel::First));
assert!(second_level.iter().all(|m| m.level() == MeshLevel::Second));
assert!(third_level.iter().all(|m| m.level() == MeshLevel::Third));
}
}