use std::collections::HashMap;
use crate::error::{GeoError, Result};
use crate::geometry::{BBox, Geometry, GeometryType};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FieldType {
Integer, Float, Text, Boolean, Blob, Date, DateTime, Json, }
impl FieldType {
pub fn as_str(self) -> &'static str {
match self {
Self::Integer => "Integer",
Self::Float => "Float",
Self::Text => "Text",
Self::Boolean => "Boolean",
Self::Blob => "Blob",
Self::Date => "Date",
Self::DateTime => "DateTime",
Self::Json => "Json",
}
}
}
impl std::fmt::Display for FieldType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct FieldDef {
pub name: String,
pub field_type: FieldType,
pub nullable: bool,
pub width: usize,
pub precision: usize,
}
impl FieldDef {
pub fn new(name: impl Into<String>, field_type: FieldType) -> Self {
Self { name: name.into(), field_type, nullable: true, width: 0, precision: 0 }
}
pub fn not_null(mut self) -> Self { self.nullable = false; self }
pub fn width(mut self, w: usize) -> Self { self.width = w; self }
pub fn precision(mut self, p: usize) -> Self { self.precision = p; self }
}
#[derive(Debug, Clone, PartialEq)]
pub enum FieldValue {
Integer(i64),
Float(f64),
Text(String),
Boolean(bool),
Blob(Vec<u8>),
Date(String),
DateTime(String),
Null,
}
impl FieldValue {
pub fn is_null(&self) -> bool { matches!(self, Self::Null) }
pub fn as_i64(&self) -> Option<i64> {
match self { Self::Integer(v) => Some(*v), Self::Float(v) => Some(*v as i64), _ => None }
}
pub fn as_f64(&self) -> Option<f64> {
match self { Self::Float(v) => Some(*v), Self::Integer(v) => Some(*v as f64), _ => None }
}
pub fn as_str(&self) -> Option<&str> {
match self {
Self::Text(s) | Self::Date(s) | Self::DateTime(s) => Some(s.as_str()),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self { Self::Boolean(b) => Some(*b), _ => None }
}
pub fn as_blob(&self) -> Option<&[u8]> {
match self { Self::Blob(b) => Some(b.as_slice()), _ => None }
}
pub fn widen_type(a: FieldType, b: FieldType) -> FieldType {
if a == b { return a; }
if matches!((a, b), (FieldType::Integer, FieldType::Float) | (FieldType::Float, FieldType::Integer)) {
return FieldType::Float;
}
FieldType::Text
}
}
impl From<i64> for FieldValue { fn from(v: i64) -> Self { Self::Integer(v) } }
impl From<i32> for FieldValue { fn from(v: i32) -> Self { Self::Integer(v as i64) } }
impl From<f64> for FieldValue { fn from(v: f64) -> Self { Self::Float(v) } }
impl From<f32> for FieldValue { fn from(v: f32) -> Self { Self::Float(v as f64) } }
impl From<bool> for FieldValue { fn from(v: bool) -> Self { Self::Boolean(v) } }
impl From<String> for FieldValue { fn from(v: String) -> Self { Self::Text(v) } }
impl From<&str> for FieldValue { fn from(v: &str) -> Self { Self::Text(v.to_owned()) } }
impl std::fmt::Display for FieldValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Integer(v) => write!(f, "{v}"),
Self::Float(v) => write!(f, "{v}"),
Self::Text(v) => write!(f, "{v}"),
Self::Boolean(v) => write!(f, "{v}"),
Self::Blob(v) => write!(f, "<blob {} bytes>", v.len()),
Self::Date(v) => write!(f, "{v}"),
Self::DateTime(v) => write!(f, "{v}"),
Self::Null => write!(f, "NULL"),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Schema {
fields: Vec<FieldDef>,
index: HashMap<String, usize>,
}
impl Schema {
pub fn new() -> Self { Self::default() }
pub fn add_field(&mut self, def: FieldDef) {
if self.index.contains_key(&def.name) { return; }
let i = self.fields.len();
self.index.insert(def.name.clone(), i);
self.fields.push(def);
}
pub fn fields(&self) -> &[FieldDef] { &self.fields }
pub fn len(&self) -> usize { self.fields.len() }
pub fn is_empty(&self) -> bool { self.fields.is_empty() }
pub fn field_index(&self, name: &str) -> Option<usize> { self.index.get(name).copied() }
pub fn field(&self, name: &str) -> Option<&FieldDef> { self.index.get(name).map(|&i| &self.fields[i]) }
}
#[derive(Debug, Clone)]
pub struct Feature {
pub fid: u64,
pub geometry: Option<Geometry>,
pub attributes: Vec<FieldValue>,
}
impl Feature {
pub fn new() -> Self { Self { fid: 0, geometry: None, attributes: Vec::new() } }
pub fn with_geometry(fid: u64, geom: Geometry, n_fields: usize) -> Self {
Self { fid, geometry: Some(geom), attributes: vec![FieldValue::Null; n_fields] }
}
pub fn get_by_index(&self, idx: usize) -> Option<&FieldValue> {
self.attributes.get(idx)
}
pub fn set_by_index(&mut self, idx: usize, val: FieldValue) {
if idx >= self.attributes.len() {
self.attributes.resize(idx + 1, FieldValue::Null);
}
self.attributes[idx] = val;
}
pub fn get(&self, schema: &Schema, name: &str) -> Result<&FieldValue> {
let idx = schema.field_index(name)
.ok_or_else(|| GeoError::FieldNotFound(name.to_owned()))?;
Ok(self.attributes.get(idx).unwrap_or(&FieldValue::Null))
}
pub fn set(&mut self, schema: &Schema, name: &str, val: FieldValue) -> Result<()> {
let idx = schema.field_index(name)
.ok_or_else(|| GeoError::FieldNotFound(name.to_owned()))?;
self.set_by_index(idx, val);
Ok(())
}
pub fn attrs_map<'a>(&'a self, schema: &'a Schema) -> HashMap<&'a str, &'a FieldValue> {
schema.fields().iter().enumerate()
.filter_map(|(i, fd)| self.attributes.get(i).map(|v| (fd.name.as_str(), v)))
.collect()
}
}
impl Default for Feature { fn default() -> Self { Self::new() } }
#[derive(Debug, Clone, Default)]
pub struct Crs {
pub epsg: Option<u32>,
pub wkt: Option<String>,
}
impl Crs {
pub fn new() -> Self { Self::default() }
pub fn with_epsg(mut self, epsg: u32) -> Self { self.epsg = Some(epsg); self }
pub fn with_wkt(mut self, wkt: impl Into<String>) -> Self { self.wkt = Some(wkt.into()); self }
}
#[derive(Debug, Clone)]
pub struct Layer {
pub name: String,
pub geom_type: Option<GeometryType>,
pub crs: Option<Crs>,
pub schema: Schema,
pub features: Vec<Feature>,
pub extent: Option<BBox>,
}
impl Layer {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
geom_type: None,
crs: None,
schema: Schema::new(),
features: Vec::new(),
extent: None,
}
}
pub fn with_geom_type(mut self, gt: GeometryType) -> Self { self.geom_type = Some(gt); self }
pub fn with_epsg(self, epsg: u32) -> Self { self.with_crs_epsg(epsg) }
pub fn with_crs(mut self, crs: Crs) -> Self { self.crs = Some(crs); self }
pub fn with_crs_epsg(mut self, epsg: u32) -> Self {
self.ensure_crs_mut().epsg = Some(epsg);
self
}
pub fn with_crs_wkt(mut self, wkt: impl Into<String>) -> Self {
self.ensure_crs_mut().wkt = Some(wkt.into());
self
}
pub fn crs_epsg(&self) -> Option<u32> {
self.crs.as_ref().and_then(|c| c.epsg)
}
pub fn crs_wkt(&self) -> Option<&str> {
self.crs.as_ref().and_then(|c| c.wkt.as_deref())
}
pub fn set_crs_epsg(&mut self, epsg: Option<u32>) {
if let Some(epsg_code) = epsg {
self.ensure_crs_mut().epsg = Some(epsg_code);
} else if let Some(crs) = self.crs.as_mut() {
crs.epsg = None;
if crs.wkt.is_none() {
self.crs = None;
}
}
}
pub fn set_crs_wkt(&mut self, wkt: Option<String>) {
if let Some(wkt_text) = wkt {
self.ensure_crs_mut().wkt = Some(wkt_text);
} else if let Some(crs) = self.crs.as_mut() {
crs.wkt = None;
if crs.epsg.is_none() {
self.crs = None;
}
}
}
pub fn assign_crs_epsg(&mut self, epsg: u32) {
self.crs = Some(Crs {
epsg: Some(epsg),
wkt: None,
});
}
pub fn assign_crs_wkt(&mut self, wkt: &str) {
self.crs = Some(Crs {
epsg: None,
wkt: Some(wkt.to_string()),
});
}
pub fn reproject_to_epsg(&self, dst_epsg: u32) -> Result<Self> {
crate::reproject::layer_to_epsg(self, dst_epsg)
}
pub fn reproject_to_epsg_with_options(
&self,
dst_epsg: u32,
options: &crate::reproject::VectorReprojectOptions,
) -> Result<Self> {
crate::reproject::layer_to_epsg_with_options(self, dst_epsg, options)
}
pub fn reproject_from_to_epsg(&self, src_epsg: u32, dst_epsg: u32) -> Result<Self> {
crate::reproject::layer_from_to_epsg(self, src_epsg, dst_epsg)
}
pub fn reproject_from_to_epsg_with_options(
&self,
src_epsg: u32,
dst_epsg: u32,
options: &crate::reproject::VectorReprojectOptions,
) -> Result<Self> {
crate::reproject::layer_from_to_epsg_with_options(self, src_epsg, dst_epsg, options)
}
fn ensure_crs_mut(&mut self) -> &mut Crs {
self.crs.get_or_insert_with(Crs::new)
}
pub fn add_field(&mut self, def: FieldDef) { self.schema.add_field(def); }
pub fn push(&mut self, f: Feature) { self.features.push(f); }
pub fn len(&self) -> usize { self.features.len() }
pub fn is_empty(&self) -> bool { self.features.is_empty() }
pub fn iter(&self) -> impl Iterator<Item = &Feature> { self.features.iter() }
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Feature> { self.features.iter_mut() }
pub fn add_feature(
&mut self,
geom: Option<Geometry>,
attrs: &[(&str, FieldValue)],
) -> Result<()> {
let n = self.schema.len();
let fid = self.features.len() as u64;
let mut f = Feature { fid, geometry: geom, attributes: vec![FieldValue::Null; n] };
for (name, val) in attrs { f.set(&self.schema, name, val.clone())?; }
self.features.push(f);
Ok(())
}
pub fn bbox(&mut self) -> Option<BBox> {
if self.extent.is_some() { return self.extent.clone(); }
let mut bb: Option<BBox> = None;
for f in &self.features {
if let Some(g) = &f.geometry {
if let Some(fb) = g.bbox() {
bb = Some(match bb { None => fb, Some(mut e) => { e.expand_to(&fb); e } });
}
}
}
self.extent = bb.clone();
bb
}
pub fn features_in_bbox(&self, query: &BBox) -> Vec<&Feature> {
self.features.iter()
.filter(|f| f.geometry.as_ref()
.and_then(|g| g.bbox())
.map_or(false, |b| b.intersects(query)))
.collect()
}
}
impl std::ops::Index<usize> for Layer {
type Output = Feature;
fn index(&self, i: usize) -> &Feature { &self.features[i] }
}
impl std::ops::IndexMut<usize> for Layer {
fn index_mut(&mut self, i: usize) -> &mut Feature { &mut self.features[i] }
}
#[cfg(test)]
mod tests {
use super::*;
fn make_layer() -> Layer {
let mut l = Layer::new("test").with_geom_type(GeometryType::Point).with_epsg(4326);
l.add_field(FieldDef::new("name", FieldType::Text));
l.add_field(FieldDef::new("value", FieldType::Float));
l.add_feature(
Some(Geometry::point(10.0, 20.0)),
&[("name", "alpha".into()), ("value", 3.14f64.into())],
).unwrap();
l.add_feature(
Some(Geometry::point(11.0, 21.0)),
&[("name", "beta".into()), ("value", 2.72f64.into())],
).unwrap();
l
}
use crate::geometry::Geometry;
#[test] fn layer_len() { assert_eq!(make_layer().len(), 2); }
#[test] fn get_field_by_name() {
let l = make_layer();
let v = l[0].get(&l.schema, "name").unwrap();
assert_eq!(v, &FieldValue::Text("alpha".into()));
}
#[test] fn get_field_by_index() {
let l = make_layer();
assert!(l[0].get_by_index(1).unwrap().as_f64().unwrap() - 3.14 < 1e-9);
}
#[test] fn bbox() {
let mut l = make_layer();
let b = l.bbox().unwrap();
assert_eq!(b.min_x, 10.0); assert_eq!(b.max_x, 11.0);
}
#[test] fn widen_type_int_float() {
assert_eq!(FieldValue::widen_type(FieldType::Integer, FieldType::Float), FieldType::Float);
}
#[test] fn widen_type_mismatch() {
assert_eq!(FieldValue::widen_type(FieldType::Integer, FieldType::Text), FieldType::Text);
}
}