use crate::{
simplify,
types::{VtFeature, VtGeometry, VtLineString, VtLinearRing, VtPoint},
};
use geojson::{Feature, FeatureCollection, Geometry, Value, feature::Id};
use std::rc::Rc;
pub fn convert(fc: FeatureCollection, tolerance: f64, generate_id: bool) -> Vec<VtFeature> {
let mut vt_features: Vec<VtFeature> = Vec::with_capacity(fc.features.len());
let mut gen_id: u64 = 0;
for feature in fc.features {
if feature.geometry.is_none() {
continue;
}
let mut id = feature.id.clone();
if generate_id {
id = Some(Id::Number(gen_id.into()));
gen_id += 1;
}
let vt_feature = convert_feature(feature, tolerance, id);
if let Some(vt_feature) = vt_feature {
vt_features.push(vt_feature);
}
}
vt_features
}
pub fn convert_feature(feature: Feature, tolerance: f64, id: Option<Id>) -> Option<VtFeature> {
let geometry = feature.geometry.as_ref()?;
let vt_geometry = convert_geometry(geometry, tolerance)?;
Some(VtFeature::new(vt_geometry, Rc::new(feature.properties), id))
}
fn convert_geometry(geometry: &Geometry, tolerance: f64) -> Option<VtGeometry> {
match &geometry.value {
Value::Point(coords) => {
if coords.is_empty() {
None
} else {
Some(VtGeometry::Point(convert_coords(coords)))
}
}
Value::MultiPoint(coords) => {
if coords.is_empty() {
None
} else {
Some(VtGeometry::MultiPoint(
coords.iter().map(|p| convert_coords(p)).collect(),
))
}
}
Value::LineString(coords) => {
if coords.is_empty() {
None
} else {
Some(VtGeometry::LineString(convert_line_string(
coords, tolerance,
)))
}
}
Value::MultiLineString(coords) => {
if coords.is_empty() {
None
} else {
Some(VtGeometry::MultiLineString(
coords
.iter()
.map(|coords| convert_line_string(coords, tolerance))
.collect(),
))
}
}
Value::Polygon(coords) => {
if coords.is_empty() {
None
} else {
Some(VtGeometry::Polygon(
coords
.iter()
.map(|coords| convert_line_ring(coords, tolerance))
.collect(),
))
}
}
Value::MultiPolygon(coords) => {
if coords.is_empty() {
None
} else {
Some(VtGeometry::MultiPolygon(
coords
.iter()
.map(|coords| {
coords
.iter()
.map(|coords| convert_line_ring(coords, tolerance))
.collect()
})
.collect(),
))
}
}
Value::GeometryCollection(geometries) => {
let geometries = geometries
.iter()
.filter_map(|geometry| convert_geometry(geometry, tolerance))
.collect::<Vec<_>>();
if geometries.is_empty() {
None
} else {
Some(VtGeometry::GeometryCollection(geometries))
}
}
}
}
fn convert_line_string(coords: &Vec<Vec<f64>>, tolerance: f64) -> VtLineString {
let mut dist = 0.;
let mut elements = coords
.iter()
.map(|coord| convert_coords(coord))
.collect::<Vec<_>>();
for w in elements.windows(2) {
let a = w[0];
let b = w[1];
dist += (b.x - a.x).hypot(b.y - a.y);
}
simplify::simplify(&mut elements, tolerance);
VtLineString {
elements,
dist,
seg_start: 0.,
seg_end: 0.,
}
}
fn convert_line_ring(coords: &Vec<Vec<f64>>, tolerance: f64) -> VtLinearRing {
let mut area = 0.;
let mut elements = coords
.iter()
.map(|coord| convert_coords(coord))
.collect::<Vec<_>>();
for w in elements.windows(2) {
let a = w[0];
let b = w[1];
area += a.x * b.y - b.x * a.y;
}
simplify::simplify(&mut elements, tolerance);
area = (area / 2.).abs();
VtLinearRing { elements, area }
}
fn convert_coords(coords: &[f64]) -> VtPoint {
let x = lng_to_mercator_x(coords[0]);
let y = lat_to_mercator_y(coords[1]);
VtPoint::from_xy(x, y)
}
#[inline]
fn lng_to_mercator_x(lng: f64) -> f64 {
lng / 360. + 0.5
}
#[inline]
fn lat_to_mercator_y(lat: f64) -> f64 {
let sin = lat.to_radians().sin();
let y = 0.5 - 0.25 * ((1. + sin) / (1. - sin)).ln() / std::f64::consts::PI;
y.clamp(0., 1.)
}