use crate::{
Point, Point3D, VectorFeatureMethods, VectorGeometry, VectorLines3DWithOffset,
VectorLinesWithOffset, VectorPoints, VectorPoints3D,
mapbox::MapboxVectorFeature,
open::{ColumnCacheWriter, FeatureType, encode_value},
weave_2d, weave_3d, zigzag,
};
use alloc::vec::Vec;
use libm::round;
use s2json::{BBOX, BBox, BBox3D, LineStringMValues, Properties, Shape, Value};
pub trait BaseVectorFeatureMethods {
fn get_type(&self) -> FeatureType;
fn properties(&self) -> &Properties;
fn has_bbox(&self) -> bool;
fn has_offsets(&self) -> bool;
fn has_m_values(&self) -> bool;
fn load_geometry(&self) -> VectorGeometry;
fn m_values(&self) -> Option<LineStringMValues>;
fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize;
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct BaseVectorPointsFeature {
pub id: Option<u64>,
pub geometry: VectorPoints,
pub properties: Properties,
pub bbox: Option<BBox>,
}
impl BaseVectorPointsFeature {
pub fn new(
id: Option<u64>,
geometry: VectorPoints,
properties: Properties,
bbox: Option<BBox>,
) -> Self {
Self { id, geometry, properties, bbox }
}
}
impl BaseVectorFeatureMethods for BaseVectorPointsFeature {
fn get_type(&self) -> FeatureType {
FeatureType::Points
}
fn properties(&self) -> &Properties {
&self.properties
}
fn has_bbox(&self) -> bool {
self.bbox.is_some()
}
fn has_offsets(&self) -> bool {
false
}
fn has_m_values(&self) -> bool {
self.geometry.iter().any(|g| g.m.is_some())
}
fn load_geometry(&self) -> VectorGeometry {
VectorGeometry::VectorPoints(self.geometry.clone())
}
fn m_values(&self) -> Option<LineStringMValues> {
if !self.has_m_values() {
return None;
}
Some(
self.geometry
.iter()
.map(|g| {
g.m.clone().unwrap_or_default()
})
.collect(),
)
}
fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
let geometry = &self.geometry;
if geometry.len() == 1 {
let point = &geometry[0];
weave_2d(zigzag(point.x) as u16, zigzag(point.y) as u16) as usize
} else {
let mut indices: Vec<u32> = Vec::new();
indices.push(cache.add_points(geometry.to_vec()) as u32);
if let (Some(m_values), Some(shape)) = (self.m_values(), m_shape) {
for m in m_values {
indices.push(encode_value(&m, shape, cache) as u32);
}
}
cache.add_indices(indices)
}
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct BaseVectorPoints3DFeature {
pub id: Option<u64>,
pub geometry: VectorPoints3D,
pub properties: Properties,
pub bbox: Option<BBox3D>,
}
impl BaseVectorPoints3DFeature {
pub fn new(
id: Option<u64>,
geometry: VectorPoints3D,
properties: Properties,
bbox: Option<BBox3D>,
) -> Self {
Self { id, geometry, properties, bbox }
}
}
impl BaseVectorFeatureMethods for BaseVectorPoints3DFeature {
fn get_type(&self) -> FeatureType {
FeatureType::Points3D
}
fn properties(&self) -> &Properties {
&self.properties
}
fn has_bbox(&self) -> bool {
self.bbox.is_some()
}
fn has_offsets(&self) -> bool {
false
}
fn has_m_values(&self) -> bool {
self.geometry.iter().any(|g| g.m.is_some())
}
fn load_geometry(&self) -> VectorGeometry {
VectorGeometry::VectorPoints3D(self.geometry.clone())
}
fn m_values(&self) -> Option<LineStringMValues> {
if !self.has_m_values() {
return None;
}
Some(self.geometry.iter().map(|g| g.m.clone().unwrap_or_default()).collect())
}
fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
let geometry = &self.geometry;
if geometry.len() == 1 {
let point = &geometry[0];
weave_3d(zigzag(point.x) as u16, zigzag(point.y) as u16, zigzag(point.z) as u16)
as usize
} else {
let mut indices: Vec<u32> = Vec::new();
indices.push(cache.add_points_3d(geometry.to_vec()) as u32);
if let (Some(m_values), Some(shape)) = (self.m_values(), m_shape) {
for m in m_values {
indices.push(encode_value(&m, shape, cache) as u32);
}
}
cache.add_indices(indices)
}
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct BaseVectorLinesFeature {
pub id: Option<u64>,
pub geometry: VectorLinesWithOffset,
pub properties: Properties,
pub bbox: Option<BBox>,
}
impl BaseVectorLinesFeature {
pub fn new(
id: Option<u64>,
geometry: VectorLinesWithOffset,
properties: Properties,
bbox: Option<BBox>,
) -> Self {
Self { id, geometry, properties, bbox }
}
}
impl BaseVectorFeatureMethods for BaseVectorLinesFeature {
fn get_type(&self) -> FeatureType {
FeatureType::Lines
}
fn properties(&self) -> &Properties {
&self.properties
}
fn has_bbox(&self) -> bool {
self.bbox.is_some()
}
fn has_offsets(&self) -> bool {
self.geometry.iter().any(|g| g.has_offset())
}
fn has_m_values(&self) -> bool {
self.geometry.iter().any(|g| g.has_m_values())
}
fn load_geometry(&self) -> VectorGeometry {
VectorGeometry::VectorLines(self.geometry.to_vec())
}
fn m_values(&self) -> Option<LineStringMValues> {
if !self.has_m_values() {
return None;
}
Some(self.geometry.iter().flat_map(|g| g.m_values().unwrap_or_default()).collect())
}
fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
let geometry = &self.geometry;
let mut indices: Vec<u32> = Vec::new();
if geometry.len() != 1 {
indices.push(geometry.len() as u32)
}
for line in geometry {
if line.has_offset() {
indices.push(encode_offset(line.offset));
}
indices.push(cache.add_points(line.geometry.clone()) as u32);
if self.has_m_values() {
if let (Some(m_values), Some(shape)) = (line.m_values(), m_shape) {
for m in m_values {
indices.push(encode_value(&m, shape, cache) as u32);
}
} else if let (None, Some(shape)) = (line.m_values(), m_shape) {
for _ in 0..line.geometry.len() {
indices.push(encode_value(&Value::default(), shape, cache) as u32);
}
}
}
}
cache.add_indices(indices)
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct BaseVectorLines3DFeature {
pub id: Option<u64>,
pub geometry: VectorLines3DWithOffset,
pub properties: Properties,
pub bbox: Option<BBox3D>,
}
impl BaseVectorLines3DFeature {
pub fn new(
id: Option<u64>,
geometry: VectorLines3DWithOffset,
properties: Properties,
bbox: Option<BBox3D>,
) -> Self {
Self { id, geometry, properties, bbox }
}
}
impl BaseVectorFeatureMethods for BaseVectorLines3DFeature {
fn get_type(&self) -> FeatureType {
FeatureType::Lines3D
}
fn properties(&self) -> &Properties {
&self.properties
}
fn has_bbox(&self) -> bool {
self.bbox.is_some()
}
fn has_offsets(&self) -> bool {
self.geometry.iter().any(|g| g.has_offset())
}
fn has_m_values(&self) -> bool {
self.geometry.iter().any(|g| g.has_m_values())
}
fn load_geometry(&self) -> VectorGeometry {
VectorGeometry::VectorLines3D(self.geometry.to_vec())
}
fn m_values(&self) -> Option<LineStringMValues> {
if !self.has_m_values() {
return None;
}
Some(self.geometry.iter().flat_map(|g| g.m_values().unwrap_or_default()).collect())
}
fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
let geometry = &self.geometry;
let mut indices: Vec<u32> = Vec::new();
if geometry.len() != 1 {
indices.push(geometry.len() as u32)
}
for line in geometry {
if line.has_offset() {
indices.push(encode_offset(line.offset));
}
indices.push(cache.add_points_3d(line.geometry.clone()) as u32);
if self.has_m_values() {
if let (Some(m_values), Some(shape)) = (line.m_values(), m_shape) {
for m in m_values {
indices.push(encode_value(&m, shape, cache) as u32);
}
} else if let (None, Some(shape)) = (line.m_values(), m_shape) {
for _ in 0..line.geometry.len() {
indices.push(encode_value(&Value::default(), shape, cache) as u32);
}
}
}
}
cache.add_indices(indices)
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct BaseVectorPolysFeature {
pub id: Option<u64>,
pub geometry: Vec<VectorLinesWithOffset>,
pub properties: Properties,
pub bbox: Option<BBox>,
pub tessellation: Vec<Point>,
pub indices: Vec<u32>,
}
impl BaseVectorPolysFeature {
pub fn new(
id: Option<u64>,
geometry: Vec<VectorLinesWithOffset>,
properties: Properties,
bbox: Option<BBox>,
indices: Vec<u32>,
tessellation: Vec<Point>,
) -> Self {
Self { id, geometry, properties, bbox, indices, tessellation }
}
}
impl BaseVectorFeatureMethods for BaseVectorPolysFeature {
fn get_type(&self) -> FeatureType {
FeatureType::Polygons
}
fn properties(&self) -> &Properties {
&self.properties
}
fn has_bbox(&self) -> bool {
self.bbox.is_some()
}
fn has_offsets(&self) -> bool {
self.geometry.iter().any(|g| g.iter().any(|l| l.has_offset()))
}
fn has_m_values(&self) -> bool {
self.geometry.iter().any(|g| g.iter().any(|l| l.has_m_values()))
}
fn load_geometry(&self) -> VectorGeometry {
VectorGeometry::VectorPolys(self.geometry.iter().map(|line| line.to_vec()).collect())
}
fn m_values(&self) -> Option<LineStringMValues> {
if !self.has_m_values() {
return None;
}
Some(
self.geometry
.iter()
.flat_map(|g| g.iter().flat_map(|l| l.m_values().unwrap_or_default()))
.collect(),
)
}
fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
let geometry = &self.geometry;
let mut indices: Vec<u32> = Vec::new();
if geometry.len() != 1 {
indices.push(geometry.len() as u32)
}
for poly in geometry {
indices.push(poly.len() as u32);
for line in poly {
if line.has_offset() {
indices.push(encode_offset(line.offset));
}
indices.push(cache.add_points(line.geometry.clone()) as u32);
if self.has_m_values() {
if let (Some(m_values), Some(shape)) = (line.m_values(), m_shape) {
for m in m_values {
indices.push(encode_value(&m, shape, cache) as u32);
}
} else if let (None, Some(shape)) = (line.m_values(), m_shape) {
for _ in 0..line.geometry.len() {
indices.push(encode_value(&Value::default(), shape, cache) as u32);
}
}
}
}
}
cache.add_indices(indices)
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct BaseVectorPolys3DFeature {
pub id: Option<u64>,
pub geometry: Vec<VectorLines3DWithOffset>,
pub properties: Properties,
pub bbox: Option<BBox3D>,
pub tessellation: Vec<Point3D>,
pub indices: Vec<u32>,
}
impl BaseVectorPolys3DFeature {
pub fn new(
id: Option<u64>,
geometry: Vec<VectorLines3DWithOffset>,
properties: Properties,
bbox: Option<BBox3D>,
indices: Vec<u32>,
tessellation: Vec<Point3D>,
) -> Self {
Self { id, geometry, properties, bbox, indices, tessellation }
}
}
impl BaseVectorFeatureMethods for BaseVectorPolys3DFeature {
fn get_type(&self) -> FeatureType {
FeatureType::Polygons3D
}
fn properties(&self) -> &Properties {
&self.properties
}
fn has_bbox(&self) -> bool {
self.bbox.is_some()
}
fn has_offsets(&self) -> bool {
self.geometry.iter().any(|g| g.iter().any(|l| l.has_offset()))
}
fn has_m_values(&self) -> bool {
self.geometry.iter().any(|g| g.iter().any(|l| l.has_m_values()))
}
fn load_geometry(&self) -> VectorGeometry {
VectorGeometry::VectorPolys3D(self.geometry.iter().map(|line| line.to_vec()).collect())
}
fn m_values(&self) -> Option<LineStringMValues> {
if !self.has_m_values() {
return None;
}
Some(
self.geometry
.iter()
.flat_map(|g| g.iter().flat_map(|l| l.m_values().unwrap_or_default()))
.collect(),
)
}
fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
let geometry = &self.geometry;
let mut indices: Vec<u32> = Vec::new();
if geometry.len() != 1 {
indices.push(geometry.len() as u32)
}
for poly in geometry {
indices.push(poly.len() as u32);
for line in poly {
if line.has_offset() {
indices.push(encode_offset(line.offset));
}
indices.push(cache.add_points_3d(line.geometry.clone()) as u32);
if self.has_m_values() {
if let (Some(m_values), Some(shape)) = (line.m_values(), m_shape) {
for m in m_values {
indices.push(encode_value(&m, shape, cache) as u32);
}
} else if let (None, Some(shape)) = (line.m_values(), m_shape) {
for _ in 0..line.geometry.len() {
indices.push(encode_value(&Value::default(), shape, cache) as u32);
}
}
}
}
}
cache.add_indices(indices)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TessellationWrapper {
Tessellation(Vec<Point>),
Tessellation3D(Vec<Point3D>),
}
impl TessellationWrapper {
pub fn len(&self) -> usize {
match self {
TessellationWrapper::Tessellation(points) => points.len(),
TessellationWrapper::Tessellation3D(points) => points.len(),
}
}
pub fn is_empty(&self) -> bool {
match self {
TessellationWrapper::Tessellation(points) => points.is_empty(),
TessellationWrapper::Tessellation3D(points) => points.is_empty(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum BaseVectorFeature {
BaseVectorPointsFeature(BaseVectorPointsFeature),
BaseVectorLinesFeature(BaseVectorLinesFeature),
BaseVectorPolysFeature(BaseVectorPolysFeature),
BaseVectorPoints3DFeature(BaseVectorPoints3DFeature),
BaseVectorLines3DFeature(BaseVectorLines3DFeature),
BaseVectorPolys3DFeature(BaseVectorPolys3DFeature),
}
impl BaseVectorFeature {
pub fn single(&self) -> bool {
match self {
BaseVectorFeature::BaseVectorPointsFeature(f) => f.geometry.len() == 1,
BaseVectorFeature::BaseVectorLinesFeature(f) => f.geometry.len() == 1,
BaseVectorFeature::BaseVectorPolysFeature(f) => f.geometry.len() == 1,
BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.geometry.len() == 1,
BaseVectorFeature::BaseVectorLines3DFeature(f) => f.geometry.len() == 1,
BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.geometry.len() == 1,
}
}
pub fn properties(&self) -> &Properties {
match self {
BaseVectorFeature::BaseVectorPointsFeature(f) => f.properties(),
BaseVectorFeature::BaseVectorLinesFeature(f) => f.properties(),
BaseVectorFeature::BaseVectorPolysFeature(f) => f.properties(),
BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.properties(),
BaseVectorFeature::BaseVectorLines3DFeature(f) => f.properties(),
BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.properties(),
}
}
pub fn has_m_values(&self) -> bool {
match self {
BaseVectorFeature::BaseVectorPointsFeature(f) => f.has_m_values(),
BaseVectorFeature::BaseVectorLinesFeature(f) => f.has_m_values(),
BaseVectorFeature::BaseVectorPolysFeature(f) => f.has_m_values(),
BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.has_m_values(),
BaseVectorFeature::BaseVectorLines3DFeature(f) => f.has_m_values(),
BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.has_m_values(),
}
}
pub fn m_values(&self) -> Option<LineStringMValues> {
match self {
BaseVectorFeature::BaseVectorPointsFeature(f) => f.m_values(),
BaseVectorFeature::BaseVectorLinesFeature(f) => f.m_values(),
BaseVectorFeature::BaseVectorPolysFeature(f) => f.m_values(),
BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.m_values(),
BaseVectorFeature::BaseVectorLines3DFeature(f) => f.m_values(),
BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.m_values(),
}
}
pub fn get_type(&self) -> FeatureType {
match self {
BaseVectorFeature::BaseVectorPointsFeature(f) => f.get_type(),
BaseVectorFeature::BaseVectorLinesFeature(f) => f.get_type(),
BaseVectorFeature::BaseVectorPolysFeature(f) => f.get_type(),
BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.get_type(),
BaseVectorFeature::BaseVectorLines3DFeature(f) => f.get_type(),
BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.get_type(),
}
}
pub fn id(&self) -> Option<u64> {
match self {
BaseVectorFeature::BaseVectorPointsFeature(f) => f.id,
BaseVectorFeature::BaseVectorLinesFeature(f) => f.id,
BaseVectorFeature::BaseVectorPolysFeature(f) => f.id,
BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.id,
BaseVectorFeature::BaseVectorLines3DFeature(f) => f.id,
BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.id,
}
}
pub fn indices(&self) -> Option<Vec<u32>> {
match self {
BaseVectorFeature::BaseVectorPolysFeature(f) => Some(f.indices.clone()),
BaseVectorFeature::BaseVectorPolys3DFeature(f) => Some(f.indices.clone()),
_ => None,
}
}
pub fn tessellation(&self) -> Option<TessellationWrapper> {
match self {
BaseVectorFeature::BaseVectorPolysFeature(f) => {
Some(TessellationWrapper::Tessellation(f.tessellation.clone()))
}
BaseVectorFeature::BaseVectorPolys3DFeature(f) => {
Some(TessellationWrapper::Tessellation3D(f.tessellation.clone()))
}
_ => None,
}
}
pub fn bbox(&self) -> Option<BBOX> {
match self {
BaseVectorFeature::BaseVectorPointsFeature(f) => f.bbox.map(BBOX::BBox),
BaseVectorFeature::BaseVectorLinesFeature(f) => f.bbox.map(BBOX::BBox),
BaseVectorFeature::BaseVectorPolysFeature(f) => f.bbox.map(BBOX::BBox),
BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.bbox.map(BBOX::BBox3D),
BaseVectorFeature::BaseVectorLines3DFeature(f) => f.bbox.map(BBOX::BBox3D),
BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.bbox.map(BBOX::BBox3D),
}
}
pub fn has_offsets(&self) -> bool {
match self {
BaseVectorFeature::BaseVectorLinesFeature(f) => f.has_offsets(),
BaseVectorFeature::BaseVectorLines3DFeature(f) => f.has_offsets(),
BaseVectorFeature::BaseVectorPolysFeature(f) => f.has_offsets(),
BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.has_offsets(),
_ => false,
}
}
pub fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
match self {
BaseVectorFeature::BaseVectorPointsFeature(f) => f.encode_to_cache(cache, m_shape),
BaseVectorFeature::BaseVectorLinesFeature(f) => f.encode_to_cache(cache, m_shape),
BaseVectorFeature::BaseVectorPolysFeature(f) => f.encode_to_cache(cache, m_shape),
BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.encode_to_cache(cache, m_shape),
BaseVectorFeature::BaseVectorLines3DFeature(f) => f.encode_to_cache(cache, m_shape),
BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.encode_to_cache(cache, m_shape),
}
}
}
impl From<&mut MapboxVectorFeature> for BaseVectorFeature {
fn from(mvt: &mut MapboxVectorFeature) -> Self {
let id = mvt.id;
let properties: Properties = (&mvt.properties).into();
let indices = mvt.read_indices();
let mut tessellation_floats: Vec<f64> = Vec::new();
mvt.add_tessellation(&mut tessellation_floats, 1.0);
let tessellation = tess_to_points(tessellation_floats);
match mvt.load_geometry() {
VectorGeometry::VectorPoints(geo) => BaseVectorFeature::BaseVectorPointsFeature(
BaseVectorPointsFeature::new(id, geo, properties, None),
),
VectorGeometry::VectorLines(geo) => BaseVectorFeature::BaseVectorLinesFeature(
BaseVectorLinesFeature::new(id, geo, properties, None),
),
VectorGeometry::VectorPolys(geo) => BaseVectorFeature::BaseVectorPolysFeature(
BaseVectorPolysFeature::new(id, geo, properties, None, indices, tessellation),
),
_ => panic!("unexpected geometry type"),
}
}
}
pub fn tess_to_points(tess: Vec<f64>) -> Vec<Point> {
tess.chunks(2).map(|chunk| Point::new(round(chunk[0]) as i32, round(chunk[1]) as i32)).collect()
}
pub fn tess_to_points_3d(tess: Vec<f64>) -> Vec<Point3D> {
tess.chunks(3)
.map(|chunk| {
Point3D::new(round(chunk[0]) as i32, round(chunk[1]) as i32, round(chunk[2]) as i32)
})
.collect()
}
pub fn encode_offset(offset: f64) -> u32 {
round(offset * 1_000.0) as u32
}
pub fn decode_offset(offset: u32) -> f64 {
(offset as f64) / 1_000.0
}