Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
guidorice committed Dec 15, 2023
1 parent b5a2bbf commit 1d83dea
Show file tree
Hide file tree
Showing 13 changed files with 413 additions and 234 deletions.
45 changes: 34 additions & 11 deletions geo_features/geom/enums.mojo
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@value
@register_passable("trivial")
struct CoordDims(Stringable):
struct CoordDims(Stringable, Sized):
"""
Enum for encoding the OGC/WKT variants of Points.
"""
Expand All @@ -8,39 +9,61 @@ struct CoordDims(Stringable):

var value: SIMD[DType.uint8, 1]

alias Point = CoordDims(252)
alias Point = CoordDims(100)
"""
2 dimensional Point.
"""
alias PointZ = CoordDims(253)
alias PointZ = CoordDims(101)
"""
3 dimensional Point, has height or altitude (Z).
"""
alias PointM = CoordDims(254)
alias PointM = CoordDims(102)
"""
3 dimensional Point, has measure (M).
"""
alias PointZM = CoordDims(255)
alias PointZM = CoordDims(103)
"""
4 dimensional Point, has height and measure (ZM)
"""

fn __init__(value: Int) -> Self:
return Self {value: value}
alias PointND = CoordDims(104)
"""
N-dimensional Point, number of dimensions from constructor.
"""

fn __eq__(self, other: Self) -> Bool:
return self.value == other.value

fn __ne__(self, other: Self) -> Bool:
return not self.__eq__(other)

fn __str__(self) -> String:
"""
Convert to string, using WKT point variants.
"""
if self == CoordDims.Point:
return "Point"
if self == CoordDims.PointZ:
elif self == CoordDims.PointZ:
return "Point Z"
if self == CoordDims.PointM:
elif self == CoordDims.PointM:
return "Point M"
if self == CoordDims.PointZM:
elif self == CoordDims.PointZM:
return "Point ZM"
return "Point"
else:
return "Point ND"

fn __len__(self) -> Int:
if self == CoordDims.Point:
return 2
elif self == CoordDims.PointM or self == CoordDims.PointZ:
return 3
elif self == CoordDims.PointZM:
return 4
else:
return self.value.to_int()

fn has_height(self) -> Bool:
return (self == CoordDims.PointZ) or (self == CoordDims.PointZM)

fn has_measure(self) -> Bool:
return (self == CoordDims.PointM) or (self == CoordDims.PointZM)
31 changes: 26 additions & 5 deletions geo_features/geom/envelope.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,24 @@ from geo_features.geom.point import Point
from geo_features.geom.enums import CoordDims
from geo_features.geom.layout import Layout
from geo_features.geom.traits import Geometric, Emptyable
from geo_features.geom.empty import empty_value, is_empty

from geo_features.serialization import (
WKTParser,
WKTable,
JSONParser,
JSONable,
Geoarrowable,
)

@value
@register_passable("trivial")
struct Envelope[dtype: DType](
Geometric,
CollectionElement,
Emptyable,
Geometric,
# JSONable,
Sized,
Stringable,
# WKTable,
):
"""
Envelope aka Bounding Box.
Expand Down Expand Up @@ -131,6 +141,11 @@ struct Envelope[dtype: DType](
res += ")"
return res

fn __len__(self) -> Int:
return self.dims()

fn __str__(self) -> String:
return self.__repr__()
#
# Getters
#
Expand Down Expand Up @@ -182,8 +197,8 @@ struct Envelope[dtype: DType](
let i = Self.point_simd_dims + Self.m_index
return self.coords[i]

fn dims(self) -> SIMD[DType.uint8, 1]:
pass
fn dims(self) -> Int:
return len(self.ogc_dims)

fn has_height(self) -> Bool:
return (self.ogc_dims == CoordDims.PointZ) or (
Expand All @@ -198,6 +213,12 @@ struct Envelope[dtype: DType](
fn is_empty(self) -> Bool:
return is_empty[dtype](self.coords)

fn envelope[dtype: DType = dtype](self) -> Self:
"""
Geometric trait.
"""
return self

fn wkt(self) -> String:
"""
TODO: wkt.
Expand Down
7 changes: 3 additions & 4 deletions geo_features/geom/layout.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ struct Layout[dtype: DType = DType.float64, offset_dtype: DType = DType.uint32](

fn __init__(
inout self,
dims: Int = 2,
ogc_dims: CoordDims = CoordDims.Point,
coords_size: Int = 0,
geoms_size: Int = 0,
Expand All @@ -44,11 +43,11 @@ struct Layout[dtype: DType = DType.float64, offset_dtype: DType = DType.uint32](
offset_dtype,
coords_size,
)
self.coordinates = Tensor[dtype](dims, coords_size)
self.ogc_dims = ogc_dims
self.coordinates = Tensor[dtype](len(ogc_dims), coords_size)
self.geometry_offsets = Tensor[offset_dtype](geoms_size)
self.part_offsets = Tensor[offset_dtype](parts_size)
self.ring_offsets = Tensor[offset_dtype](rings_size)
self.ogc_dims = ogc_dims

fn __eq__(self, other: Self) -> Bool:
"""
Expand All @@ -75,7 +74,7 @@ struct Layout[dtype: DType = DType.float64, offset_dtype: DType = DType.uint32](
"""
return self.coordinates.shape()[self.features_idx]

fn dims(self) -> SIMD[DType.uint8, 1]:
fn dims(self) -> Int:
"""
Num dimensions (X, Y, Z, M, etc). (constructor's `dims` argument).
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,70 +4,115 @@ from utils.vector import DynamicVector
from memory import memcmp

from geo_features.serialization import WKTParser, JSONParser
from .point import Point
from .layout import Layout
from .empty import is_empty, empty_value



struct MultiPoint[simd_dims: Int, dtype: DType](Sized, Stringable):
from geo_features.geom.layout import Layout
from geo_features.geom.empty import is_empty, empty_value
from geo_features.geom.traits import Geometric, Emptyable
from geo_features.geom.point import Point
from geo_features.geom.enums import CoordDims
from geo_features.serialization import (
WKTParser,
WKTable,
JSONParser,
JSONable,
Geoarrowable,
)


@value
struct MultiPoint[dtype: DType = DType.float64](
CollectionElement,
Emptyable,
Geoarrowable,
Geometric,
JSONable,
Sized,
Stringable,
WKTable,
):
"""
Models an OGC-style MultiPoint. Any collection of Points is a valid MultiPoint,
except [heterogeneous dimension multipoints](https://geoarrow.org/format) which are unsupported.
"""

var data: Layout[coord_dtype=dtype]
var data: Layout[dtype]

fn __init__(inout self):
"""
Create empty MultiPoint.
"""
self.data = Layout[coord_dtype=dtype]()
self.data = Layout[dtype]()

fn __init(inout self, data: Layout[dtype]):
self.data = data

fn __init__(inout self, *points: Point[simd_dims, dtype]):
fn __init__(inout self, *points: Point[dtype]):
"""
Create MultiPoint from a variadic (var args) list of Points.
"""
let n = len(points)
var v = DynamicVector[Point[simd_dims, dtype]](n)
for i in range(n):
v.push_back(points[i])
self.__init__(v)
let dims = len(points[0])
self.data = Layout[dtype](coords_size=n)
for y in range(dims):
for x in range(n):
self.data.coordinates[Index(y, x)] = points[x].coords[y]

fn __init__(inout self, points: DynamicVector[Point[simd_dims, dtype]]):
fn __init__(inout self, points: DynamicVector[Point[dtype]]):
"""
Create MultiPoint from a vector of Points.
"""
let n = len(points)
if len(points) == 0:
# create empty Multipoint
self.data = Layout[coord_dtype=dtype]()
self.data = Layout[dtype]()
return

let sample_pt = points[0]
let dims = len(sample_pt)
self.data = Layout[dtype](
dims=dims, coords_size=n, geoms_size=0, parts_size=0, rings_size=0
)
self.data = Layout[dtype](coords_size=n)
for y in range(dims):
for x in range(len(points)):
self.data.coordinates[Index(y, x)] = points[x].coords[y]

fn __copyinit__(inout self, other: Self):
self.data = other.data
for x in range(n):
let value = points[x].coords[y]
self.data.coordinates[Index(y, x)] = value

@staticmethod
fn from_json(json_dict: PythonObject) raises -> Self:
""" """
# TODO: impl from_json
raise Error("not implemented")

@staticmethod
fn from_json(json_str: String) raises -> Self:
# TODO: impl from_json
raise Error("not implemented")

@staticmethod
fn from_wkt(wkt: String) raises -> Self:
""" """
# TODO: impl from_wkt
let geometry_sequence = WKTParser.parse(wkt)
let n = geometry_sequence.geoms.__len__().to_float64().to_int()
if n == 0:
return Self()
let sample_pt = geometry_sequence.geoms[0]
let coords_tuple = sample_pt.coords[0]
let dims = coords_tuple.__len__().to_float64().to_int()
let ogc_dims = CoordDims.PointZ if dims == 3 else CoordDims.Point
var data = Layout[dtype](ogc_dims, coords_size=n)
for y in range(dims):
for x in range(n):
let geom = geometry_sequence.geoms[x]
let coords_tuple = geom.coords[0]
let value = coords_tuple[y].to_float64().cast[dtype]()
data.coordinates[Index(y, x)] = value
return Self(data)

@staticmethod
fn from_geoarrow(table: PythonObject) raises -> Self:
"""
Create Point from geoarrow / pyarrow table with geometry column.
"""
raise Error("not implemented")

@staticmethod
fn empty(ogc_dims: CoordDims = CoordDims.Point) -> Self:
return Self()

fn __len__(self) -> Int:
"""
Returns the number of point elements.
Expand All @@ -81,25 +126,31 @@ struct MultiPoint[simd_dims: Int, dtype: DType](Sized, Stringable):
return not self.__eq__(other)

fn __repr__(self) -> String:
let dims = self.data.dims()
return (
"MultiPoint["
+ String(dims)
"MultiPoint ["
+ str(self.data.ogc_dims)
+ ", "
+ str(dtype)
+ "]("
+ String(len(self))
+ " points)"
)

fn __getitem__(self: Self, feature_index: Int) -> Point[simd_dims, dtype]:
fn dims(self) -> Int:
return len(self.data.ogc_dims)

fn has_height(self) -> Bool:
return self.data.has_height()

fn has_measure(self) -> Bool:
return self.data.has_measure()

fn __getitem__(self: Self, feature_index: Int) -> Point[dtype]:
"""
Get Point from MultiPoint at index.
"""
let dims = self.data.dims()
var point = Point[simd_dims, dtype]()

for dim_index in range(dims):
var point = Point[dtype]()
for dim_index in range(self.dims()):
point.coords[dim_index] = self.data.coordinates[
Index(dim_index, feature_index)
]
Expand All @@ -116,7 +167,7 @@ struct MultiPoint[simd_dims: Int, dtype: DType](Sized, Stringable):
return point

fn __str__(self) -> String:
return self.wkt()
return self.__repr__()

fn json(self) -> String:
"""
Expand Down Expand Up @@ -166,7 +217,7 @@ struct MultiPoint[simd_dims: Int, dtype: DType](Sized, Stringable):
if self.is_empty():
return "MULTIPOINT EMPTY"
let dims = self.data.dims()
var res = String("MULTIPOINT(")
var res = String("MULTIPOINT (")
let n = len(self)
for i in range(n):
let pt = self[i]
Expand All @@ -181,3 +232,7 @@ struct MultiPoint[simd_dims: Int, dtype: DType](Sized, Stringable):

fn is_empty(self) -> Bool:
return len(self) == 0

fn geoarrow(self) -> PythonObject:
# TODO: geoarrow
return PythonObject()
Loading

0 comments on commit 1d83dea

Please sign in to comment.