///|
pub fn regular_sample_design(
extent : GridExtent,
rows : Int,
cols : Int,
) -> SampleDesign {
let safe_rows = Int::max(1, rows)
let safe_cols = Int::max(1, cols)
let lat_step = if safe_rows == 1 {
0.0
} else {
(extent.max_lat - extent.min_lat) / Double::from_int(safe_rows - 1)
}
let lon_step = if safe_cols == 1 {
0.0
} else {
(extent.max_lon - extent.min_lon) / Double::from_int(safe_cols - 1)
}
let points : Array[GeoPoint] = []
for row in 0.. Array[GeoPoint] {
regular_sample_design(extent, rows, cols).points
}
///|
pub fn sample_design_grid(
design : SampleDesign,
values : ArrayView[Double],
) -> Grid {
let output : Array[Double] = []
for i in 0.. Array[GeoPoint] {
profile_points(start, finish, Int::max(0, count))
}
///|
pub fn transect_sample_by_spacing(
start : GeoPoint,
finish : GeoPoint,
spacing_m : Double,
) -> Array[GeoPoint] {
geodesic_polyline(start, finish, spacing_m)
}
///|
pub fn radial_sample_points(
center : GeoPoint,
radius_m : Double,
count : Int,
) -> Array[GeoPoint] {
let output : Array[GeoPoint] = []
let safe = Int::max(0, count)
if safe > 0 {
for i in 0.. Array[GeoPoint] {
let output : Array[GeoPoint] = [center]
for radius in radii_m {
for point in radial_sample_points(center, radius, count_per_ring) {
output.push(point)
}
}
output
}
///|
pub fn fibonacci_sample_points(
center : GeoPoint,
radius_m : Double,
count : Int,
) -> Array[GeoPoint] {
let output : Array[GeoPoint] = []
let safe = Int::max(0, count)
let golden_angle = 137.50776405003785
for i in 0.. Array[GeoPoint] {
points.filter(fn(point) {
point.lat >= extent.min_lat &&
point.lat <= extent.max_lat &&
point.lon >= extent.min_lon &&
point.lon <= extent.max_lon
})
}
///|
pub fn sample_points_near(
points : ArrayView[GeoPoint],
center : GeoPoint,
radius_m : Double,
) -> Array[GeoPoint] {
points.filter(fn(point) {
radius_m <= 0.0 || haversine_distance_m(center, point) <= radius_m
})
}
///|
pub fn sample_points_deduplicate(
points : ArrayView[GeoPoint],
tolerance_m : Double,
) -> Array[GeoPoint] {
let output : Array[GeoPoint] = []
for point in points {
let mut duplicate = false
for existing in output {
if point_approximately_equal(point, existing, tolerance_m) {
duplicate = true
}
}
if !duplicate {
output.push(point)
}
}
output
}
///|
pub fn sample_points_sort_by_distance(
points : ArrayView[GeoPoint],
origin : GeoPoint,
) -> Array[GeoPoint] {
let output = points.to_owned()
output.sort_by(fn(left, right) {
haversine_distance_m(origin, left).compare(
haversine_distance_m(origin, right),
)
})
output
}
///|
pub fn sample_point_distances(
origin : GeoPoint,
points : ArrayView[GeoPoint],
) -> Array[Double] {
points.map(fn(point) { haversine_distance_m(origin, point) })
}
///|
pub fn route_distances(points : ArrayView[GeoPoint]) -> Array[Double] {
let output : Array[Double] = []
let mut total = 0.0
if points.length() > 0 {
output.push(0.0)
for i in 1.. Double {
match route_distances(points).last() {
Some(value) => value
None => 0.0
}
}
///|
pub fn route_bearings(points : ArrayView[GeoPoint]) -> Array[Double] {
let output : Array[Double] = []
if points.length() > 0 {
output.push(0.0)
for i in 1.. Array[GeoPoint] {
let edge = grid.edge_strength()
let output : Array[GeoPoint] = []
for cell in edge.cells() {
if is_finite(cell.value) && cell.value >= threshold {
output.push(cell.point)
}
}
output
}
///|
pub fn sample_points_from_grid_percentile(
grid : Grid,
fraction : Double,
) -> Array[GeoPoint] {
let threshold = percentile(grid.values, fraction)
let output : Array[GeoPoint] = []
for cell in grid.cells() {
if is_finite(cell.value) && cell.value >= threshold {
output.push(cell.point)
}
}
output
}
///|
pub fn samples_to_design(
points : ArrayView[GeoPoint],
rows : Int,
cols : Int,
) -> SampleDesign {
let safe_rows = Int::max(1, rows)
let safe_cols = Int::max(1, cols)
let extent = if points.length() == 0 {
{ min_lat: 0.0, max_lat: 0.0, min_lon: 0.0, max_lon: 0.0 }
} else {
let mut min_lat = points[0].lat
let mut max_lat = points[0].lat
let mut min_lon = points[0].lon
let mut max_lon = points[0].lon
for point in points {
min_lat = Double::min(min_lat, point.lat)
max_lat = Double::max(max_lat, point.lat)
min_lon = Double::min(min_lon, point.lon)
max_lon = Double::max(max_lon, point.lon)
}
{ min_lat, max_lat, min_lon, max_lon }
}
regular_sample_design(extent, safe_rows, safe_cols)
}
///|
pub fn sample_design_extent(design : SampleDesign) -> GridExtent {
if design.points.length() == 0 {
{ min_lat: 0.0, max_lat: 0.0, min_lon: 0.0, max_lon: 0.0 }
} else {
let first = design.points[0]
let last = design.points[design.points.length() - 1]
{
min_lat: Double::min(first.lat, last.lat),
max_lat: Double::max(first.lat, last.lat),
min_lon: Double::min(first.lon, last.lon),
max_lon: Double::max(first.lon, last.lon),
}
}
}
///|
pub fn sample_design_is_rectangular(design : SampleDesign) -> Bool {
design.rows > 0 &&
design.cols > 0 &&
design.points.length() == design.rows * design.cols
}
///|
pub fn sample_design_points_at_row(
design : SampleDesign,
row : Int,
) -> Array[GeoPoint] {
if row < 0 || row >= design.rows {
[]
} else {
let output : Array[GeoPoint] = []
for col in 0.. Array[GeoPoint] {
if col < 0 || col >= design.cols {
[]
} else {
let output : Array[GeoPoint] = []
for row in 0.. SampleDesign {
{
..design,
points: design.points.map(fn(point) {
from_local_enu(point, { x: east_m, y: north_m, z: 0.0 })
}),
}
}
///|
pub fn sample_design_to_samples(
design : SampleDesign,
values : ArrayView[Double],
name : String,
) -> Array[FieldSample] {
let output : Array[FieldSample] = []
for i in 0..