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

Commit

Permalink
Add/envelope improvements (#10)
Browse files Browse the repository at this point in the history
- Update envelope calcs to use results from
https://github.com/guidorice/modcon23-contest
- Add envelope equality check methods.
  • Loading branch information
guidorice authored Nov 27, 2023
1 parent 90b95f5 commit fffd763
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 109 deletions.
32 changes: 13 additions & 19 deletions geo_features/geom/envelope.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ alias EnvelopeM = Envelope[dims=4, dtype=DType.float64]
alias EnvelopeZM = Envelope[dims=4, dtype=DType.float64]



@value
@register_passable("trivial")
struct Envelope[dims: Int = 2, dtype: DType = DType.float64]:
Expand Down Expand Up @@ -53,20 +54,15 @@ struct Envelope[dims: Int = 2, dtype: DType = DType.float64]:
"""
return Self(line_string.data)

fn __init__(data: Layout[coord_dtype=dtype], num_workers: Int = 0) -> Self:
fn __init__(data: Layout[coord_dtype=dtype]) -> Self:
"""
Construct Envelope from memory Layout.
"""
# TODO: autotune for number of workers (oversubscribing dims across dims workers is a guess)?
# TODO: autotune for nelts (simdwidthof[dtype] is a guess)?
# TODO: autotune for new param: parallelize_length_cutoff- with less than a few thousand coordinates, it's faster on single core)
# See bench_envelope.mojo
alias n = 2 * dims
alias nelts = simdbitwidth()
var coords = SIMD[dtype, 2 * dims]()

# fill initial values of with inf/neginf at each position in the 2*n array

let n = 2 * dims
var coords = Tensor[dtype](n, 1)

@unroll
for d in range(dims):
coords[d] = Self.Inf # min (southwest) values, start from inf.
Expand All @@ -75,12 +71,11 @@ struct Envelope[dims: Int = 2, dtype: DType = DType.float64]:
for d in range(dims, n):
coords[d] = Self.NegInf # max (northeast) values, start from neginf

alias nelts = simdwidthof[dtype]()
let num_features = data.coordinates.shape()[1]

# vectorized load and min/max calculation for each of the dims
@parameter
fn min_max_task(dim: Int):
@unroll
for dim in range(dims):
@parameter
fn min_max_simd[simd_width: Int](feature_idx: Int):
let index = Index(dim, feature_idx)
Expand All @@ -94,14 +89,13 @@ struct Envelope[dims: Int = 2, dtype: DType = DType.float64]:

vectorize[nelts, min_max_simd](num_features)

if num_workers > 0:
parallelize[min_max_task](dims, num_workers)
else:
for d in range(dims):
min_max_task(d)
return Self {coords: coords}

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

let result_coords: Self.CoordsT = coords.simd_load[2 * dims]()
return Self {coords: result_coords}
fn __ne__(self, other: Self) -> Bool:
return not self == other

fn __repr__(self) -> String:
var res = "Envelope[" + String(dims) + ", " + dtype.__str__() + "]("
Expand Down
2 changes: 2 additions & 0 deletions geo_features/geom/point.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ struct Point[simd_dims: Int = 2, dtype: DType = DType.float64]:

var result = Self()
let n = len(coords_list)
debug_assert(n <= simd_dims, "coords_list length is longer than simd_dims parameter")

for i in range(n):
if i >= simd_dims:
break
Expand Down
162 changes: 78 additions & 84 deletions geo_features/test/geom/test_envelope.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,16 @@ fn test_envelope() raises:
test_constructors()
test_repr()
test_min_max()
# test_southwesterly_point()
# test_northeasterly_point()
# test_parallelization()
# test_with_geos()
test_southwesterly_point()
test_northeasterly_point()
test_with_geos()
test_equality_ops()

# test_equality_ops()
# test_getters()
# test_wkt()
# test_json()
# test_static_methods()
# test_from_json()
# test_from_wkt()

print()


fn test_constructors() raises:
let test = MojoTest("constructors, aliases")
Expand Down Expand Up @@ -162,78 +157,77 @@ fn test_min_max() raises:
test.assert_true(e_of_ls4.max_m() == measure + 0.05, "max_m")


# fn test_southwesterly_point() raises:
# print("# southwesterly_point")

# let e = Envelope(Point2(lon, lat))
# let sw_pt = e.southwesterly_point()
# test.assert_true(sw_pt.x() == lon, "southwesterly_point")
# test.assert_true(sw_pt.y() == lat, "southwesterly_point")


# fn test_northeasterly_point() raises:
# print("# northeasterly_point")
# let e = Envelope(Point2(lon, lat))
# let sw_pt = e.northeasterly_point()
# test.assert_true(sw_pt.x() == lon, "northeasterly_point")
# test.assert_true(sw_pt.y() == lat, "northeasterly_point")


# fn test_with_geos() raises:
# """
# Check envelope of complex features using shapely's envelope function.
# """

# print("# shapely/geos")

# let json = Python.import_module("orjson")
# let builtins = Python.import_module("builtins")
# let shapely = Python.import_module("shapely")
# let envelope = shapely.envelope
# let shape = shapely.geometry.shape
# let mapping = shapely.geometry.mapping

# # LineString

# let path = Path("geo_features/test/fixtures/geojson/line_string")
# let fixtures = VariadicList("curved.geojson", "straight.geojson", "zigzag.geojson")
# for i in range(len(fixtures)):
# let file = path / fixtures[i]
# with open(file, "r") as f:
# let geojson = f.read()
# let geojson_dict = json.loads(geojson)
# let geometry = shape(geojson_dict)
# let expect_bounds = geometry.bounds
# let lstr = LineString.from_json(geojson_dict)
# let env = Envelope(lstr)
# for i in range(4):
# test.assert_true(
# env.coords[i].cast[DType.float64]()
# == expect_bounds[i].to_float64(),
# "envelope index:" + String(i),
# )


# fn test_parallelization() raises:
# """
# Verify envelope calcs are the same with and without parallelization.
# """
# print("# parallelize envelope calcs")
# let num_coords = 10000
# var layout = Layout(num_coords)
# layout.coordinates = rand[DType.float64](4, num_coords)

# let e_parallelized7 = Envelope(layout, num_workers=7)
# let e_parallelized3 = Envelope(layout, num_workers=3)
# let e_serial = Envelope(layout, num_workers=0)
# let e_default = Envelope(layout)

# test.assert_true(
# e_parallelized7.coords == e_parallelized3.coords,
# "e_parallelized7 envelope calcs failed.",
# )
# test.assert_true(
# e_parallelized3.coords == e_serial.coords,
# "e_parallelized2 envelope calcs failed.",
# )
# test.assert_true(e_serial.coords == e_default.coords, "e_serial envelope calcs failed.")
fn test_southwesterly_point() raises:
let test = MojoTest("southwesterly_point")
let e = Envelope(Point2(lon, lat))
let sw_pt = e.southwesterly_point()
test.assert_true(sw_pt.x() == lon, "southwesterly_point")
test.assert_true(sw_pt.y() == lat, "southwesterly_point")


fn test_northeasterly_point() raises:
let test = MojoTest("northeasterly_point")
let e = Envelope(Point2(lon, lat))
let sw_pt = e.northeasterly_point()
test.assert_true(sw_pt.x() == lon, "northeasterly_point")
test.assert_true(sw_pt.y() == lat, "northeasterly_point")


fn test_with_geos() raises:
"""
Check envelope of complex features using shapely's envelope function.
"""
let test = MojoTest("shapely/geos")

let json = Python.import_module("orjson")
let builtins = Python.import_module("builtins")
let shapely = Python.import_module("shapely")

let envelope = shapely.envelope
let shape = shapely.geometry.shape
let mapping = shapely.geometry.mapping

# LineString

let path = Path("geo_features/test/fixtures/geojson/line_string")
let fixtures = VariadicList("curved.geojson", "straight.geojson", "zigzag.geojson")
for i in range(len(fixtures)):
let file = path / fixtures[i]
with open(file, "r") as f:
let geojson = f.read()
let geojson_dict = json.loads(geojson)
let geometry = shape(geojson_dict)
let expect_bounds = geometry.bounds
let lstr = LineString.from_json(geojson_dict)
let env = Envelope(lstr)
for i in range(4):
test.assert_true(
env.coords[i].cast[DType.float64]()
== expect_bounds[i].to_float64(),
"envelope index:" + String(i),
)


fn test_equality_ops() raises:
"""
Test __eq__ and __ne__ methods.
"""
let test = MojoTest("equality ops")

let e2 = Envelope(Point2(lon, lat))
let e2_eq = Envelope(Point2(lon, lat))
let e2_ne = Envelope(Point2(lon + 0.01, lat - 0.02))
test.assert_true(e2 == e2_eq, "__eq__")
test.assert_true(e2 != e2_ne, "__ne__")

let e3 = Envelope(PointZ(lon, lat, height))
let e3_eq = Envelope(PointZ(lon, lat, height))
let e3_ne = Envelope(PointZ(lon, lat, height * 2))
test.assert_true(e3 == e3_eq, "__eq__")
test.assert_true(e3 != e3_ne, "__ne__")

let e4 = Envelope(PointZM(lon, lat, height, measure))
let e4_eq = Envelope(PointZM(lon, lat, height, measure))
let e4_ne = Envelope(PointZM(lon, lat, height, measure * 2))
test.assert_true(e4 == e4_eq, "__eq__")
test.assert_true(e4 != e4_ne, "__ne__")
2 changes: 0 additions & 2 deletions geo_features/test/geom/test_line_string.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ fn main() raises:
# TODO: https://github.com/modularml/mojo/issues/1160
# test_from_wkt()

print()


fn test_constructors() raises:
var test = MojoTest("variadic list constructor")
Expand Down
2 changes: 0 additions & 2 deletions geo_features/test/geom/test_multi_point.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ fn test_multi_point() raises:
test_from_json()
test_from_wkt()

print()


fn test_constructors() raises:
var test = MojoTest("variadic list constructor")
Expand Down
2 changes: 0 additions & 2 deletions geo_features/test/geom/test_point.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ fn main() raises:
fn test_has_height() raises:
let test = MojoTest("has_height")
let pt_z = PointZ(lon, lat, height)
print(pt_z.coords)
test.assert_true(pt_z.has_height(), "has_height")


Expand Down Expand Up @@ -188,7 +187,6 @@ fn test_getters() raises:
# _ = pt_z.m()

let pt_m = PointM(lon, lat, measure)
print(pt_m.coords)
test.assert_true(pt_m.x() == lon, "pt_m.x() == lon")
test.assert_true(pt_m.y() == lat, "pt_m.y() == lat")
test.assert_true(pt_m.m() == measure, "pt_m.m() == measure")
Expand Down

0 comments on commit fffd763

Please sign in to comment.