initial commit

This commit is contained in:
Johannes Erwerle 2022-08-05 10:03:57 +02:00
commit 32e68ee635
12 changed files with 3012 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

2185
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

16
Cargo.toml Normal file
View file

@ -0,0 +1,16 @@
[package]
name = "fapra_osm_2"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
geojson = { version = "0.22.3", features = ["geo-types"]}
handlebars = { version = "4.2", features = ["dir_source"] }
clap = { version = "3.1", features = ["derive"] }
rand = {version = "0.8", features = ["alloc"] }
rocket = {version="0.5.0-rc", features=["json"]}
rocket_dyn_templates = {version="0.1.0-rc", features=["handlebars"]}
serde = {version="1.0", features=["derive"]}
osmpbfreader = "0.15.2"

24
src/bin/polygon_print.rs Normal file
View file

@ -0,0 +1,24 @@
use fapra_osm_2::coordinates::RadianCoordinate;
use fapra_osm_2::polygon::Polygon;
use geojson::FeatureCollection;
/// a simple program to show the geojson export of polygons
fn main() {
let nodes = vec![
RadianCoordinate::from_degrees(10.0, 15.0),
RadianCoordinate::from_degrees(20.0, 10.0),
RadianCoordinate::from_degrees(20.0, 25.0),
RadianCoordinate::from_degrees(10.0, 15.0),
];
let outside1 = RadianCoordinate::from_degrees(30.0, 15.0);
let outside2 = RadianCoordinate::from_degrees(-10.0, 15.0);
let polygon =
Polygon::build_polygon(nodes, outside1, outside2).expect("error while building polygon");
let fc: FeatureCollection = polygon.into();
println!("{}", fc.to_string());
}

192
src/coordinates.rs Normal file
View file

@ -0,0 +1,192 @@
use geojson::{Position, Value};
use std::convert::From;
use std::f64::consts::{FRAC_PI_2, PI, TAU};
use crate::ACCURACY_BOUNDARY;
/// Spherical coordinates in radians.
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct RadianCoordinate {
pub lat: f64,
pub lon: f64,
}
/// Spherical coordinates in degrees.
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct DegreeCoordinate {
pub lat: f64,
pub lon: f64,
}
impl From<DegreeCoordinate> for RadianCoordinate {
fn from(other: DegreeCoordinate) -> Self {
RadianCoordinate::from_degrees(other.lat, other.lon)
}
}
impl From<DegreeCoordinate> for geojson::Value {
fn from(coordinate: DegreeCoordinate) -> Self {
Value::Point(vec![coordinate.lat, coordinate.lon])
}
}
impl From<RadianCoordinate> for Position {
fn from(coordinate: RadianCoordinate) -> Self {
let coordinate = DegreeCoordinate::from(coordinate);
vec![coordinate.lat, coordinate.lon]
}
}
impl From<RadianCoordinate> for geojson::Value {
fn from(coordinate: RadianCoordinate) -> Self {
let degrees: DegreeCoordinate = coordinate.into();
degrees.into()
}
}
impl From<RadianCoordinate> for DegreeCoordinate {
fn from(other: RadianCoordinate) -> Self {
DegreeCoordinate::from_radians(other.lat, other.lon)
}
}
impl RadianCoordinate {
/// Builds a RadianCoordinate from latitude and longitude given in
/// degrees.
pub fn from_degrees(lat: f64, lon: f64) -> Self {
let lat = lat / 90.0 * FRAC_PI_2;
let lon = normalize_lon(lon / 180.0 * PI);
RadianCoordinate { lat, lon }
}
/// gives a normalizes version of the Coordinate
pub fn normalize(&self) -> RadianCoordinate {
RadianCoordinate {
lat: self.lat,
lon: normalize_lon(self.lon),
}
}
/// returns the longitude of a point when the coordinate system is
/// transformed, such that `north_pole` is the north pole of that system.
pub fn get_transformed_longitude(&self, north_pole: &RadianCoordinate) -> f64 {
if self.lat == FRAC_PI_2 {
return self.lon
};
let top = (self.lon- north_pole.lon).sin() * self.lat.cos();
let bottom = (self.lat.sin() * north_pole.lat.cos())
- (self.lat.cos() * north_pole.lat.sin() * (self.lon - north_pole.lon).cos());
bottom.atan2(top)
}
/// returns `true` when the point is on a great circle with point `a` and `b`
/// or numerically very close to that
pub fn on_great_circle(&self, a: &RadianCoordinate, b: &RadianCoordinate) -> bool {
let lat_a = a.get_transformed_longitude(&self);
let lat_b = b.get_transformed_longitude(&self);
(lat_a - lat_b).abs().rem_euclid(PI) < ACCURACY_BOUNDARY
}
/// calculates whether `other` is antipodal to this point.
pub fn antipodal(&self, other: &RadianCoordinate) -> bool {
// if the distance between both points is very close to PI, they are
// antipodal
let c = self.distance_to(other);
let diff = (c - PI).abs();
diff < ACCURACY_BOUNDARY
}
/// returns the shortest distance to an other RadianCoordinate along
/// the surface of the unit-sphere in radians.
pub fn distance_to(&self, other: &RadianCoordinate) -> f64 {
// using the haversine formula
// if the distance between both points is very close to PI, they are
// antipodal
let delta_lat = other.lat - self.lat;
let delta_lon = other.lon - self.lon;
let a = (delta_lat / 2.0).sin().powi(2)
+ self.lat.cos() * other.lat.cos() * (delta_lon / 2.0).sin().powi(2);
2.0 * a.sqrt().atan2((1.0_f64 - a).sqrt())
}
/// checks whether the strike is between the shorter angle between the points
/// `a` and `b` while using `pole` as the north pole.
pub fn strike_between(
&self,
pole: &RadianCoordinate,
a: &RadianCoordinate,
b: &RadianCoordinate,
) -> bool {
let mut lon = self.get_transformed_longitude(pole).rem_euclid(2.0 * PI);
let a_lon = a.get_transformed_longitude(pole).rem_euclid(2.0 * PI);
let b_lon = b.get_transformed_longitude(pole).rem_euclid(2.0 * PI);
// select the start boundary of the smaller circle segment in the
// positive direction
let start;
let mut end;
if (b_lon - a_lon) > PI {
start = b_lon;
end = a_lon;
} else {
start = a_lon;
end = b_lon;
}
// rotate the values such that the start is interpreted as 0 and
// handle the wrap around.
// The start of the interval is now at 0 and the end should be smaller
// than PI.
end = (end - start).rem_euclid(2.0 * PI);
lon = (lon - start).rem_euclid(2.0 * PI);
0_f64 <= lon && lon <= end
}
}
impl DegreeCoordinate {
/// Builds a DegreeCoordinate from latitude and longitude given in
/// radians.
pub fn from_radians(lat: f64, lon: f64) -> Self {
let lat = lat * 90.0 / FRAC_PI_2;
let lon = normalize_lon(lon) * 180.0 / PI;
DegreeCoordinate { lat, lon }
}
}
/// normalizes longitude values given in radians to the range (-PI, PI]
pub fn normalize_lon(lon: f64) -> f64 {
// restrict values to -/+ TAU
let mut lon = lon % (TAU);
if lon <= -PI {
lon = TAU + lon;
}
if lon > PI {
lon = -TAU + lon;
}
lon
}
#[test]
fn test_normalize_lon() {
assert!((normalize_lon(0.0) - 0.0).abs() < 10e-12);
assert!((normalize_lon(PI) - PI).abs() < 10e-12);
assert!((normalize_lon(-PI) - PI).abs() < 10e-12);
assert!((normalize_lon(TAU) - 0.0).abs() < 10e-12);
assert!((normalize_lon(PI + FRAC_PI_2) - (-FRAC_PI_2)).abs() < 10e-12);
assert!((normalize_lon(-PI - FRAC_PI_2) - (FRAC_PI_2)).abs() < 10e-12);
}

6
src/lib.rs Normal file
View file

@ -0,0 +1,6 @@
pub mod coordinates;
pub mod polygon;
#[cfg(test)]
pub mod tests;
const ACCURACY_BOUNDARY: f64 = 1e-15;

415
src/polygon.rs Normal file
View file

@ -0,0 +1,415 @@
use crate::coordinates::{RadianCoordinate, normalize_lon};
use geojson::{Feature, FeatureCollection, Position, Value};
use std::convert::From;
use std::f64;
use std::f64::consts::{PI, TAU};
/// A spherical polygon
/// the first and last node have to be identical.
#[derive(Debug, Default, PartialEq, Clone)]
pub struct Polygon {
pub nodes: Vec<RadianCoordinate>,
pub outside_point_1: RadianCoordinate,
pub outside_point_2: RadianCoordinate,
pub bbox: BoundingBox,
}
/// A simple bounding box.
#[derive(Debug, Default, PartialEq, Clone)]
pub struct BoundingBox {
pub lat_min: f64,
pub lat_max: f64,
pub lon_min: f64,
pub lon_max: f64,
}
#[derive(PartialEq, Debug)]
pub enum PolygonVerificationError {
Antipodal,
OutSide1SameCircle,
OutSide2SameCircle,
}
#[derive(PartialEq, Debug)]
pub enum ContainmentError {
Antipodal,
}
impl BoundingBox {
/// checks whether a given point is in the bounding box.
/// being on the border counts as being inside.
pub fn contains(&self, point: RadianCoordinate) -> bool {
let point = point.normalize();
self.lat_min <= point.lat
&& point.lat <= self.lat_max
&& self.lon_min <= point.lon
&& point.lon <= self.lon_max
}
/// builds a bounding box around the given coordinates.
pub fn from_nodes(nodes: &Vec<RadianCoordinate>) -> BoundingBox {
let mut bbox = BoundingBox {
lat_min: f64::MAX,
lat_max: f64::MIN,
lon_min: f64::MAX,
lon_max: f64::MIN,
};
for node in nodes.iter() {
bbox.lat_min = bbox.lat_min.min(node.lat);
bbox.lat_max = bbox.lat_max.max(node.lat);
bbox.lon_min = bbox.lon_min.min(node.lon);
bbox.lon_max = bbox.lon_max.max(node.lon);
}
bbox
}
}
impl From<BoundingBox> for Value {
fn from(bbox: BoundingBox) -> Self {
Value::Polygon(vec![vec![
RadianCoordinate {
lon: bbox.lon_min,
lat: bbox.lat_min,
}
.into(),
RadianCoordinate {
lon: bbox.lon_min,
lat: bbox.lat_max,
}
.into(),
RadianCoordinate {
lon: bbox.lon_max,
lat: bbox.lat_max,
}
.into(),
RadianCoordinate {
lon: bbox.lon_max,
lat: bbox.lat_min,
}
.into(),
RadianCoordinate {
lon: bbox.lon_min,
lat: bbox.lat_min,
}
.into(),
]])
}
}
impl From<Polygon> for FeatureCollection {
fn from(polygon: Polygon) -> Self {
let mut features = FeatureCollection {
bbox: None,
features: vec![],
foreign_members: None,
};
let mut values: Vec<Value> = Vec::default();
values.push(polygon.bbox.into());
values.push(polygon.outside_point_1.into());
values.push(polygon.outside_point_2.into());
// build the polygon
let mut nodes: Vec<Position> = Vec::default();
for node in polygon.nodes.into_iter() {
nodes.push(node.into())
}
values.push(Value::Polygon(vec![nodes]));
for value in values.into_iter() {
features.features.push(Feature::from(value));
}
features
}
}
impl Polygon {
/// Builds a Polygon from the given nodes and 2 points that are outside of
/// the polygon.
///
pub fn build_polygon(
nodes: Vec<RadianCoordinate>,
point1: RadianCoordinate,
point2: RadianCoordinate,
) -> Result<Polygon, PolygonVerificationError> {
let bbox = BoundingBox::from_nodes(&nodes);
let polygon = Polygon {
bbox,
nodes,
outside_point_1: point1,
outside_point_2: point2,
};
polygon.verify()?;
Ok(polygon)
}
pub fn verify(&self) -> Result<(), PolygonVerificationError> {
if self.outside_point_1.antipodal(&(self.outside_point_2)) {
return Err(PolygonVerificationError::Antipodal);
}
for point in 0..(self.nodes.len() - 1) {
let a = self.nodes[point];
let b = self.nodes[point + 1];
if self.outside_point_1.on_great_circle(&a, &b) {
return Err(PolygonVerificationError::OutSide1SameCircle);
}
if self.outside_point_2.on_great_circle(&a, &b) {
return Err(PolygonVerificationError::OutSide2SameCircle);
}
}
return Ok(());
}
/// Checks if `point` is inside the polygon.
/// Being on the border of the polygon counts as being inside.
/// If the polygon does not have enough nodes, nothing can be inside
/// the polygon, so false is returned.
pub fn contains(&self, point: &RadianCoordinate) -> bool {
let first_check = self.contains_internal(point, &(self.outside_point_1));
match first_check {
Ok(result) => result,
Err(ContainmentError::Antipodal) => self
.contains_internal(point, &(self.outside_point_2))
.unwrap(),
}
}
fn contains_internal(
&self,
point: &RadianCoordinate,
outside: &RadianCoordinate,
) -> Result<bool, ContainmentError> {
if point.antipodal(outside) {
return Err(ContainmentError::Antipodal);
}
if point == outside {
return Ok(false);
}
let mut crossings = 0;
for i in 0..(self.nodes.len()-1) {
let point_a = self.nodes[i];
// check whether the point is the vertex
if *point == point_a {
return Ok(true);
}
let point_b = self.nodes[i + 1];
println!("working on arc from {:?} to {:?}", point_a, point_b);
// is the great circle from outside to point crossing through
// the current vertex?
if point.on_great_circle(outside, &point_a) {
println!("we are on a great circle");
// point c is the previous vertex.
// the second -1 is needed, because the polygon has explict
// start/end points
let point_c = if i == 0 {
self.nodes[self.nodes.len() -2]
} else {
self.nodes[i - 1]
};
let lon_p = point.get_transformed_longitude(&point_a);
let mut lon_b = point_b.get_transformed_longitude(&point_a);
let mut lon_c = point_c.get_transformed_longitude(&point_a);
// the sign tell us on which side of the great circle from
// a to point we are
lon_b = (lon_b - lon_p) % (TAU);
lon_c = (lon_c - lon_p) % (TAU);
if lon_b.signum() != lon_c.signum() {
crossings += 1;
continue;
};
}
if point.strike_between(outside, &point_a, &point_b) {
println!("we have a matching strike");
let lon_b = point_b.get_transformed_longitude(&point_a);
let mut lon_point = point.get_transformed_longitude(&point_a);
let mut lon_outside = outside.get_transformed_longitude(&point_a);
println!("lon_b: {}", lon_b);
println!("initial lon_point: {}", lon_point);
println!("initial lon_outside: {}", lon_outside);
// the sign tells us on which side of the great circle from
// a to b we are
lon_point = lon_point - lon_b;
lon_outside = lon_outside - lon_b;
println!("lon_point: {}", lon_point);
println!("lon_outside: {}", lon_outside);
lon_point = normalize_lon(lon_point);
lon_outside = normalize_lon(lon_outside);
println!("lon_point: {}", lon_point);
println!("lon_outside: {}", lon_outside);
if lon_point.signum() != lon_outside.signum() {
crossings += 1;
};
}
}
Ok(crossings % 2 == 1)
}
}
#[cfg(test)]
mod tests {
use crate::coordinates::RadianCoordinate;
use crate::polygon::{Polygon, PolygonVerificationError};
use std::f64::consts::{FRAC_PI_2, FRAC_PI_4, FRAC_PI_8, PI};
#[test]
fn verify() {
let nodes = vec![
RadianCoordinate { lat: 0.0, lon: 0.0 },
RadianCoordinate {
lat: FRAC_PI_8,
lon: 0.0,
},
RadianCoordinate {
lat: 0.0,
lon: FRAC_PI_8,
},
];
//p1 and p2 are antipodal
//both are on a great circle with the first segment
let p1 = RadianCoordinate { lat: 0.5, lon: 0.0 };
let p2 = RadianCoordinate { lat: -0.5, lon: PI };
// p3 is neither antipodal nor on a great circle with any of the segments
let p3 = RadianCoordinate {
lat: 0.2,
lon: -0.2,
};
let p4 = RadianCoordinate {
lat: 0.3,
lon: -0.3,
};
assert_eq!(
Polygon::build_polygon(nodes.clone(), p1, p2),
Err(PolygonVerificationError::Antipodal)
);
assert_eq!(
Polygon::build_polygon(nodes.clone(), p1, p3),
Err(PolygonVerificationError::OutSide1SameCircle)
);
assert_eq!(
Polygon::build_polygon(nodes.clone(), p3, p2),
Err(PolygonVerificationError::OutSide2SameCircle)
);
assert!(Polygon::build_polygon(nodes.clone(), p3, p4).is_ok());
}
#[test]
fn test_from_degrees() {
let test = RadianCoordinate::from_degrees(45.0, 90.0);
assert!((test.lat - FRAC_PI_4).abs() < 1e-10);
assert!((test.lon - FRAC_PI_2).abs() < 1e-10);
}
#[test]
fn point_in_polygon() {
let polygon = Polygon::build_polygon(
vec![
RadianCoordinate::from_degrees(0.0, 0.0),
RadianCoordinate::from_degrees(22.5, 0.0),
RadianCoordinate::from_degrees(0.0, 22.5),
RadianCoordinate::from_degrees(0.0, 0.0),
],
RadianCoordinate::from_degrees(60.0, 20.0),
RadianCoordinate::from_degrees(18.104087015773956, -39.08935546875),
);
assert!(polygon.is_ok());
let polygon = polygon.unwrap();
use geojson::FeatureCollection;
let features = FeatureCollection::from(polygon.clone());
println!("{}", features.to_string());
let in_point = RadianCoordinate::from_degrees(5.0, 5.0);
let out_point = RadianCoordinate {
lat: -0.1,
lon: 0.1,
};
let out_point = RadianCoordinate::from_degrees(-17.0, 15.0);
let antipodal_point = RadianCoordinate {
lat: -0.1,
lon: -PI + 0.1,
};
let outside_vertex_intersect = RadianCoordinate {
lat: -0.1,
lon: 0.1,
};
let outside_vertex_intersect_2 = RadianCoordinate {
lat: -0.1,
lon: -0.1,
};
//assert_eq!(polygon.contains(&in_point), true);
assert_eq!(polygon.contains(&out_point), false);
assert_eq!(polygon.contains(&antipodal_point), false);
assert_eq!(
polygon.contains_internal(&outside_vertex_intersect, &(polygon.outside_point_1)),
Ok(false)
);
assert_eq!(
polygon.contains_internal(&in_point, &outside_vertex_intersect_2),
Ok(true)
);
}
#[test]
fn point_in_polygon2() {
let polygon = Polygon::build_polygon(
vec![
RadianCoordinate::from_degrees(0.0, 180.0),
RadianCoordinate::from_degrees(45.0, 180.),
RadianCoordinate::from_degrees(45.0, 90.0),
RadianCoordinate::from_degrees(0.0, 180.0),
],
RadianCoordinate::from_degrees(1.0, -1.0),
RadianCoordinate::from_degrees(2.0, -1.0),
);
assert!(polygon.is_ok());
let polygon = polygon.unwrap();
let in_point = RadianCoordinate::from_degrees(40.0, 135.0);
assert_eq!(polygon.contains(&in_point), true);
}
}

43
src/tests/bounding_box.rs Normal file
View file

@ -0,0 +1,43 @@
use crate::coordinates::RadianCoordinate;
use crate::polygon::BoundingBox;
#[test]
pub fn test_contains() {
let bbox = BoundingBox {
lat_min: -1.0,
lat_max: 1.0,
lon_min: -1.0,
lon_max: 1.0,
};
assert!(bbox.contains(RadianCoordinate { lat: 0.0, lon: 0.0 }));
assert!(bbox.contains(RadianCoordinate { lat: 1.0, lon: 0.0 }));
assert!(!bbox.contains(RadianCoordinate { lat: 2.0, lon: 0.0 }));
assert!(!bbox.contains(RadianCoordinate { lat: 0.0, lon: 2.0 }));
assert!(!bbox.contains(RadianCoordinate {
lat: -2.0,
lon: 2.0
}));
}
#[test]
pub fn create_bbox() {
let nodes = vec![
RadianCoordinate { lat: 1.0, lon: 1.0 },
RadianCoordinate {
lat: -1.5,
lon: 0.0,
},
RadianCoordinate {
lat: -0.5,
lon: -3.0,
},
];
let bbox = BoundingBox::from_nodes(&nodes);
assert_eq!(bbox.lat_max, 1.0);
assert_eq!(bbox.lat_min, -1.5);
assert_eq!(bbox.lon_max, 1.0);
assert_eq!(bbox.lon_min, -3.0);
}

122
src/tests/coordinates.rs Normal file
View file

@ -0,0 +1,122 @@
use crate::coordinates::RadianCoordinate;
use std::f64::consts::{PI, FRAC_PI_2, FRAC_PI_4, FRAC_PI_8};
#[test]
fn on_great_circle() {
let a = RadianCoordinate { lat: 0.0, lon: 0.0 };
let b = RadianCoordinate { lat: 0.0, lon: 1.0 };
// on the circle
let p1 = RadianCoordinate { lat: 0.0, lon: 0.5 };
let p2 = RadianCoordinate {
lat: 0.0,
lon: -3.0,
};
// not on the circle
let p3 = RadianCoordinate { lat: 0.4, lon: 0.1 };
assert!(p1.on_great_circle(&a, &b), "p1 not on great circle");
assert!(p2.on_great_circle(&a, &b), "p2 not on great circle");
assert!(!p3.on_great_circle(&a, &b), "p3 on great circle");
}
#[test]
fn antipodal() {
let point1 = RadianCoordinate { lat: 0.0, lon: 0.0 };
let point2 = RadianCoordinate { lat: 0.0, lon: PI };
let point3 = RadianCoordinate {
lat: FRAC_PI_2,
lon: 0.0,
};
let point4 = RadianCoordinate {
lat: -FRAC_PI_2,
lon: 0.0,
};
let point5 = RadianCoordinate {
lat: -FRAC_PI_2,
lon: 42.0,
};
let point6 = RadianCoordinate{lat: -0.1, lon: 0.1};
let point7 = RadianCoordinate{lat: 0.1, lon: -PI+0.1};
assert!(point1.antipodal(&point2));
assert!(point3.antipodal(&point4));
assert!(point3.antipodal(&point5));
assert!(!point1.antipodal(&point3));
assert!(!point1.antipodal(&point4));
assert!(point6.antipodal(&point7));
}
#[test]
fn transformed_longitude() {
// test with regular north pole
let point0 = RadianCoordinate { lat: 0.0, lon: 0.0 };
let north_pole = RadianCoordinate {
lat: FRAC_PI_2,
lon: 0.0,
};
let difference = (point0.get_transformed_longitude(&north_pole) - point0.lon).abs() % PI;
assert!(difference < 1e-10);
// example with 4 points, where the X->P is between X->A and X->B
let point_x = RadianCoordinate {
lat: 0.0,
lon: -0.1,
};
let point_a = RadianCoordinate { lat: 0.1, lon: 0.0 };
let point_p = RadianCoordinate { lat: 0.0, lon: 0.1 };
let point_b = RadianCoordinate {
lat: -0.1,
lon: 0.0,
};
assert!(
point_a.get_transformed_longitude(&point_x)
< point_p.get_transformed_longitude(&point_x)
);
assert!(
point_p.get_transformed_longitude(&point_x)
< point_b.get_transformed_longitude(&point_x)
);
}
#[test]
fn strike_check() {
let pole = RadianCoordinate {
lat: FRAC_PI_4,
lon: 0.0,
};
let a = RadianCoordinate { lat: 0.0, lon: 0.1 };
let b = RadianCoordinate {
lat: 0.0,
lon: -0.1,
};
// Point that is between a and b and crosses the line between a and b
let p1 = RadianCoordinate {
lat: -FRAC_PI_8,
lon: 0.0,
};
// Point that is between a and b and does not cross the line between a and b
let p2 = RadianCoordinate {
lat: FRAC_PI_8,
lon: 0.0,
};
// Point that is left of a and b
let p3 = RadianCoordinate {
lat: 0.0,
lon: -0.2,
};
// Point that is right of a and b
let p4 = RadianCoordinate { lat: 0.0, lon: 0.2 };
assert!(p1.strike_between(&pole, &a, &b));
assert!(p2.strike_between(&pole, &a, &b));
assert!(!p3.strike_between(&pole, &a, &b));
assert!(!p4.strike_between(&pole, &a, &b));
}

2
src/tests/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod bounding_box;
pub mod coordinates;

View file

@ -0,0 +1,6 @@
use crate::polygon::Polygon;
#[test]
fn test_point_in_polygon() {
}

0
src/tests/polygon.rs Normal file
View file