Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Geo traits #125

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
508 changes: 194 additions & 314 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions geo-file-loader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ publish = false
[dependencies]
bytes = "1"
time-logger = { path = "../time-logger" }
geo = "0.28"
geo-features = { path = "../geo-features" }
geozero = { version = "0.14", features = ["with-wkt", "with-gpx", "with-shp"] }
thiserror = "1"
geo-projected = { path = "../geo-projected" }
typed-num = { path = "../typed-num" }
# geojson = { version = "0.24", features = ["geo-traits"] }
geojson = { git = "https://github.com/georust/geojson.git", branch = "geo-traits-impl", features = ["geo-traits"] }
wkt = { git = "https://github.com/georust/wkt.git", branch = "kyle/geo-traits", features = ["serde"] }
geo-traits = "0.1"
num-traits = "0.2"
26 changes: 0 additions & 26 deletions geo-file-loader/src/geojson.rs

This file was deleted.

20 changes: 20 additions & 0 deletions geo-file-loader/src/geojson_loader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::LoadedFile;
use geojson::GeoJson;
use std::io;

pub struct GeoJsonSource {
pub bytes: bytes::Bytes,
}

impl crate::FileLoader for GeoJsonSource {
fn from_bytes(bytes: bytes::Bytes) -> Self {
GeoJsonSource { bytes }
}

fn load(self) -> Result<LoadedFile, crate::Error> {
let bytes_cursor = io::Cursor::new(&self.bytes);
let geojson =
GeoJson::from_reader(bytes_cursor).map_err(|e| geojson::Error::MalformedJson(e))?;
Ok(LoadedFile::GeoJson(geojson))
}
}
16 changes: 5 additions & 11 deletions geo-file-loader/src/gpx.rs → geo-file-loader/src/gpx_loader.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
use geo_projected::geometry_wrap;
use geozero::GeozeroDatasource;
use std::io;

// TOOD: create generic file loader for geozero

pub struct GpxSource {
pub bytes: bytes::Bytes,
}
Expand All @@ -13,16 +7,16 @@ impl crate::FileLoader for GpxSource {
GpxSource { bytes }
}

fn load(
self,
) -> Result<geo_features::FeatureCollection<geo_projected::UnprojectedScalar>, crate::Error>
{
fn load(self) -> Result<crate::LoadedFile, crate::Error> {
unimplemented!()
/*
let bytes_cursor = io::Cursor::new(&self.bytes);
let mut gpx_reader = geozero::gpx::GpxReader(bytes_cursor);
let mut geo_writer = geozero::geo_types::GeoWriter::new();
gpx_reader.process(&mut geo_writer)?;
let geometry = geo_writer.take_geometry().ok_or(crate::Error::NoGeometry)?;
let geometry = geometry_wrap::<f64, geo_projected::Unprojected>(geometry);
Ok(geo_features::FeatureCollection::from_geometry(geometry))
Ok(crate::LoadedFile::Geometry(geometry))
*/
}
}
196 changes: 177 additions & 19 deletions geo-file-loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
clippy::expect_used
)]

mod geojson;
mod gpx;
mod shapefile;
mod wkt;
mod geojson_loader;
mod gpx_loader;
mod shapefile_loader;
mod wkt_loader;

pub use crate::geojson::GeoJsonSource;
pub use crate::gpx::GpxSource;
pub use crate::shapefile::ShapefileSource;
pub use crate::wkt::WktSource;
pub use crate::geojson_loader::GeoJsonSource;
pub use crate::gpx_loader::GpxSource;
pub use crate::shapefile_loader::ShapefileSource;
pub use crate::wkt_loader::WktSource;
use enum_delegate::delegate;
use geo_traits::GeometryTrait;

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FileFormat {
Expand All @@ -27,10 +29,8 @@ pub enum FileFormat {
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("{0}")]
Geozero(#[from] geozero::error::GeozeroError),
#[error("{0}")]
Shapefile(#[from] geozero::shp::Error),
#[error("No geometry found in GeoJSON file")]
GeoJson(#[from] geojson::Error),
#[error("No geometry found in file")]
NoGeometry,
}

Expand All @@ -54,10 +54,7 @@ impl FileFormat {
}
}

pub fn load_file(
file_format: FileFormat,
bytes: bytes::Bytes,
) -> Result<geo_features::FeatureCollection<geo_projected::UnprojectedScalar>, Error> {
pub fn load_file(file_format: FileFormat, bytes: bytes::Bytes) -> Result<LoadedFile, Error> {
match file_format {
FileFormat::GeoJson => Ok(GeoJsonSource::from_bytes(bytes).load()?),
FileFormat::Gpx => Ok(GpxSource::from_bytes(bytes).load()?),
Expand All @@ -66,9 +63,170 @@ pub fn load_file(
}
}

// Define "either" type that can be either a GeoJson or Wkt associated types
#[derive(Debug, PartialEq, Copy, Clone, geo_traits::PointTrait)]
pub enum Either<X, Y> {
GeoJson(X),
Wkt(Y),
}

impl<X, Y> PartialOrd for Either<X, Y>
where
X: PartialOrd,
Y: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
todo!()
}
}

impl<X, Y> num_traits::ToPrimitive for Either<X, Y>
where
X: num_traits::ToPrimitive,
Y: num_traits::ToPrimitive,
{
fn to_i64(&self) -> Option<i64> {
todo!()
}

fn to_u64(&self) -> Option<u64> {
todo!()
}
}

impl<X, Y> num_traits::NumCast for Either<X, Y>
where
X: num_traits::NumCast,
Y: num_traits::NumCast,
{
fn from(n: Self) -> Option<f64> {
todo!()
}
}

impl<X, Y> num_traits::Num for Either<X, Y>
where
X: num_traits::Num,
Y: num_traits::Num,
{
type FromStrRadixErr;

fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
todo!()
}
}

impl<X, Y> geo_traits::CoordTrait for Either<X, Y>
where
X: geo_traits::CoordTrait,
Y: geo_traits::CoordTrait,
{
type T = Either<X::T, Y::T>;

fn dim(&self) -> geo_traits::Dimensions {
match self {
Self::GeoJson(geojson) => geojson.dim(),
Self::Wkt(wkt) => wkt.dim(),
}
}

fn x(&self) -> Self::T {
match self {
Self::GeoJson(geojson) => Either::GeoJson(geojson.x()),
Self::Wkt(wkt) => Either::Wkt(wkt.x()),
}
}

fn y(&self) -> Self::T {
match self {
Self::GeoJson(geojson) => Either::GeoJson(geojson.y()),
Self::Wkt(wkt) => Either::Wkt(wkt.y()),
}
}

fn nth_unchecked(&self, n: usize) -> Self::T {
todo!()
}
}

impl<X, Y> geo_traits::PointTrait for Either<X, Y>
where
X: geo_traits::PointTrait,
Y: geo_traits::PointTrait,
{
type T = X::T;
type CoordType<'a>
= Either<X::CoordType<'a>, Y::CoordType<'a>>
where
X: 'a,
Y: 'a;

fn dim(&self) -> geo_traits::Dimensions {
match self {
Self::GeoJson(geojson) => geojson.dim(),
Self::Wkt(wkt) => wkt.dim(),
}
}
fn coord(&self) -> Option<Self::CoordType<'_>> {
match self {
Self::GeoJson(geojson) => geojson.coord().map(Either::GeoJson),
Self::Wkt(wkt) => wkt.coord().map(Either::Wkt),
}
}
}

#[delegate(for(LastName))]
pub enum LoadedFile {
GeoJson(geojson::GeoJson),
Wkt(wkt::Wkt<f64>),
}

impl geo_traits::GeometryTrait for LoadedFile {
type T = f64;
type PointType<'a> = Either<
<geojson::GeoJson as geo_traits::GeometryTrait>::PointType<'a>,
<wkt::Wkt<f64> as geo_traits::GeometryTrait>::PointType<'a>,
>;
type LineStringType<'a> = <geojson::GeoJson as geo_traits::GeometryTrait>::LineStringType<'a>;
type PolygonType<'a> = <geojson::GeoJson as geo_traits::GeometryTrait>::PolygonType<'a>;
type MultiPointType<'a> = <geojson::GeoJson as geo_traits::GeometryTrait>::MultiPointType<'a>;
type MultiLineStringType<'a> =
<geojson::GeoJson as geo_traits::GeometryTrait>::MultiLineStringType<'a>;
type MultiPolygonType<'a> =
<geojson::GeoJson as geo_traits::GeometryTrait>::MultiPolygonType<'a>;
type GeometryCollectionType<'a> =
<geojson::GeoJson as geo_traits::GeometryTrait>::GeometryCollectionType<'a>;
type RectType<'a> = geo_traits::UnimplementedRect<Self::T>;
type TriangleType<'a> = geo_traits::UnimplementedTriangle<Self::T>;
type LineType<'a> = geo_traits::UnimplementedLine<Self::T>;

fn dim(&self) -> geo_traits::Dimensions {
// FIXME
geo_traits::Dimensions::Xy
}
fn as_type(
&self,
) -> geo_traits::GeometryType<
'_,
Self::PointType<'_>,
Self::LineStringType<'_>,
Self::PolygonType<'_>,
Self::MultiPointType<'_>,
Self::MultiLineStringType<'_>,
Self::MultiPolygonType<'_>,
Self::GeometryCollectionType<'_>,
Self::RectType<'_>,
Self::TriangleType<'_>,
Self::LineType<'_>,
> {
match self {
Self::GeoJson(geojson) => geojson.as_type(),
Self::Wkt(wkt) => wkt.as_type(),
}
}
}

trait FileLoader {
fn from_bytes(bytes: bytes::Bytes) -> Self;
fn load(
self,
) -> Result<geo_features::FeatureCollection<geo_projected::UnprojectedScalar>, Error>;
fn load(self) -> Result<LoadedFile, Error>;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
use geo_projected::geometry_wrap;
use std::io;

pub struct ShapefileSource {
pub bytes: bytes::Bytes,
}
Expand All @@ -10,16 +7,16 @@ impl crate::FileLoader for ShapefileSource {
ShapefileSource { bytes }
}

fn load(
self,
) -> Result<geo_features::FeatureCollection<geo_projected::UnprojectedScalar>, crate::Error>
{
fn load(self) -> Result<crate::LoadedFile, crate::Error> {
unimplemented!()
/*
let mut bytes_cursor = io::Cursor::new(&self.bytes);
let shapefile_reader = geozero::shp::ShpReader::new(&mut bytes_cursor)?;
let mut geo_writer = geozero::geo_types::GeoWriter::new();
for _ in shapefile_reader.iter_geometries(&mut geo_writer) {}
let geometry = geo_writer.take_geometry().ok_or(crate::Error::NoGeometry)?;
let geometry = geometry_wrap::<f64, geo_projected::Unprojected>(geometry);
Ok(geo_features::FeatureCollection::from_geometry(geometry))
Ok(crate::LoadedFile::Geometry(geometry))
*/
}
}
22 changes: 6 additions & 16 deletions geo-file-loader/src/wkt.rs → geo-file-loader/src/wkt_loader.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
use geo_projected::geometry_wrap;
use std::io;

use geozero::GeozeroDatasource;

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("{0}")]
Geozero(#[from] geozero::error::GeozeroError),
}

pub struct WktSource {
pub bytes: bytes::Bytes,
}
Expand All @@ -18,17 +7,18 @@ impl crate::FileLoader for WktSource {
WktSource { bytes }
}

fn load(
self,
) -> Result<geo_features::FeatureCollection<geo_projected::UnprojectedScalar>, crate::Error>
{
fn load(self) -> Result<crate::LoadedFile, crate::Error> {
let wkt = wkt::deserialize_wkt::<T = f64>(&self.bytes)?;
Ok(crate::LoadedFile::Wkt(wkt))
/*
let mut bytes_cursor = io::Cursor::new(&self.bytes);
let mut wkt_reader = geozero::wkt::WktReader(&mut bytes_cursor);
let mut geo_writer = geozero::geo_types::GeoWriter::new();
wkt_reader.process(&mut geo_writer)?;
let geometry = geometry_wrap::<f64, geo_projected::Unprojected>(
geo_writer.take_geometry().ok_or(crate::Error::NoGeometry)?,
);
Ok(geo_features::FeatureCollection::from_geometry(geometry))
Ok(crate::LoadedFile::Geometry(geometry))
*/
}
}
3 changes: 2 additions & 1 deletion rgis-file-loader/src/jobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ impl bevy_jobs::Job for LoadFileJob {

fn perform(self, _: bevy_jobs::Context) -> bevy_jobs::AsyncReturn<Self::Outcome> {
Box::pin(async move {
let loaded_file = geo_file_loader::load_file(self.file_format, self.bytes)?;
Ok(LoadFileJobOutcome {
feature_collection: geo_file_loader::load_file(self.file_format, self.bytes)?,
feature_collection: loaded_file.into(),
name: self.name,
source_crs_epsg_code: self.source_crs_epsg_code,
})
Expand Down
Loading