Skip to content

Commit 258a067

Browse files
committed
Exposed spatial_index resolution parameter.
1 parent cffa289 commit 258a067

File tree

5 files changed

+47
-14
lines changed

5 files changed

+47
-14
lines changed

core/src/main/scala/org/locationtech/rasterframes/RasterFunctions.scala

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,21 @@ trait RasterFunctions {
5959
/** Extracts the bounding box from a RasterSource or ProjectedRasterTile */
6060
def rf_extent(col: Column): TypedColumn[Any, Extent] = GetExtent(col)
6161

62-
/** Constructs a XZ2 index in WGS84 from either a Geometry, Extent, ProjectedRasterTile, or RasterSource and its CRS */
63-
def rf_spatial_index(targetExtent: Column, targetCRS: Column) = XZ2Indexer(targetExtent, targetCRS)
62+
/** Constructs a XZ2 index in WGS84 from either a Geometry, Extent, ProjectedRasterTile, or RasterSource and its CRS
63+
* For details: https://www.geomesa.org/documentation/user/datastores/index_overview.html */
64+
def rf_spatial_index(targetExtent: Column, targetCRS: Column, indexResolution: Short) = XZ2Indexer(targetExtent, targetCRS, indexResolution)
6465

65-
/** Constructs a XZ2 index in WGS84 from either a ProjectedRasterTile or RasterSource */
66-
def rf_spatial_index(targetExtent: Column) = XZ2Indexer(targetExtent)
66+
/** Constructs a XZ2 index in WGS84 from either a Geometry, Extent, ProjectedRasterTile, or RasterSource and its CRS
67+
* For details: https://www.geomesa.org/documentation/user/datastores/index_overview.html */
68+
def rf_spatial_index(targetExtent: Column, targetCRS: Column) = XZ2Indexer(targetExtent, targetCRS, 18: Short)
69+
70+
/** Constructs a XZ2 index with level 18 resolution in WGS84 from either a ProjectedRasterTile or RasterSource
71+
* For details: https://www.geomesa.org/documentation/user/datastores/index_overview.html */
72+
def rf_spatial_index(targetExtent: Column, indexResolution: Short) = XZ2Indexer(targetExtent, indexResolution)
73+
74+
/** Constructs a XZ2 index with level 18 resolution in WGS84 from either a ProjectedRasterTile or RasterSource
75+
* For details: https://www.geomesa.org/documentation/user/datastores/index_overview.html */
76+
def rf_spatial_index(targetExtent: Column) = XZ2Indexer(targetExtent, 18: Short)
6777

6878
/** Extracts the CRS from a RasterSource or ProjectedRasterTile */
6979
def rf_crs(col: Column): TypedColumn[Any, CRS] = GetCRS(col)

core/src/main/scala/org/locationtech/rasterframes/expressions/transformers/XZ2Indexer.scala

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import org.locationtech.rasterframes.expressions.accessors.GetCRS
4444

4545
/**
4646
* Constructs a XZ2 index in WGS84 from either a Geometry, Extent, ProjectedRasterTile, or RasterSource
47-
*
47+
* See: https://www.geomesa.org/documentation/user/datastores/index_overview.html
4848
* @param left geometry-like column
4949
* @param right CRS column
5050
* @param indexResolution resolution level of the space filling curve -
@@ -59,7 +59,7 @@ import org.locationtech.rasterframes.expressions.accessors.GetCRS
5959
* crs - the native CRS of the `geom` column
6060
"""
6161
)
62-
case class XZ2Indexer(left: Expression, right: Expression, indexResolution: Short = 18)
62+
case class XZ2Indexer(left: Expression, right: Expression, indexResolution: Short)
6363
extends BinaryExpression with CodegenFallback {
6464

6565
override def nodeName: String = "rf_spatial_index"
@@ -75,6 +75,7 @@ case class XZ2Indexer(left: Expression, right: Expression, indexResolution: Shor
7575
}
7676

7777
private lazy val indexer = XZ2SFC(indexResolution)
78+
@transient
7879
private lazy val gf = new GeometryFactory()
7980

8081
override protected def nullSafeEval(leftInput: Any, rightInput: Any): Any = {
@@ -121,8 +122,10 @@ case class XZ2Indexer(left: Expression, right: Expression, indexResolution: Shor
121122

122123
object XZ2Indexer {
123124
import org.locationtech.rasterframes.encoders.SparkBasicEncoders.longEnc
125+
def apply(targetExtent: Column, targetCRS: Column, indexResolution: Short): TypedColumn[Any, Long] =
126+
new Column(new XZ2Indexer(targetExtent.expr, targetCRS.expr, indexResolution)).as[Long]
124127
def apply(targetExtent: Column, targetCRS: Column): TypedColumn[Any, Long] =
125-
new Column(new XZ2Indexer(targetExtent.expr, targetCRS.expr)).as[Long]
126-
def apply(targetExtent: Column): TypedColumn[Any, Long] =
127-
new Column(new XZ2Indexer(targetExtent.expr, GetCRS(targetExtent.expr))).as[Long]
128+
new Column(new XZ2Indexer(targetExtent.expr, targetCRS.expr, 18)).as[Long]
129+
def apply(targetExtent: Column, indexResolution: Short = 18): TypedColumn[Any, Long] =
130+
new Column(new XZ2Indexer(targetExtent.expr, GetCRS(targetExtent.expr), indexResolution)).as[Long]
128131
}

core/src/test/scala/org/locationtech/rasterframes/expressions/XZ2IndexerSpec.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,21 @@ class XZ2IndexerSpec extends TestEnvironment with Inspectors {
101101

102102
}
103103
it("should work when CRS is LatLng") {
104-
105104
val df = testExtents.map(Tuple1.apply).toDF("extent")
106105
val crs: CRS = LatLng
107106
val indexes = df.select(rf_spatial_index($"extent", serialized_literal(crs))).collect()
108107

108+
forEvery(indexes.zip(expected)) { case (i, e) =>
109+
i should be (e)
110+
}
111+
}
112+
it("should support custom resolution") {
113+
val sfc = XZ2SFC(3)
114+
val expected = testExtents.map(e => sfc.index(e.xmin, e.ymin, e.xmax, e.ymax))
115+
val df = testExtents.map(Tuple1.apply).toDF("extent")
116+
val crs: CRS = LatLng
117+
val indexes = df.select(rf_spatial_index($"extent", serialized_literal(crs), 3)).collect()
118+
109119
forEvery(indexes.zip(expected)) { case (i, e) =>
110120
i should be (e)
111121
}

pyrasterframes/src/main/python/pyrasterframes/rasterfunctions.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -586,12 +586,16 @@ def rf_geometry(proj_raster_col):
586586
return _apply_column_function('rf_geometry', proj_raster_col)
587587

588588

589-
def rf_spatial_index(geom_col, crs_col=None):
590-
"""Constructs a XZ2 index in WGS84 from either a Geometry, Extent, ProjectedRasterTile, or RasterSource and its CRS"""
589+
def rf_spatial_index(geom_col, crs_col=None, index_resolution = 18):
590+
"""Constructs a XZ2 index in WGS84 from either a Geometry, Extent, ProjectedRasterTile, or RasterSource and its CRS.
591+
For details: https://www.geomesa.org/documentation/user/datastores/index_overview.html """
592+
593+
jfcn = RFContext.active().lookup('rf_spatial_index')
594+
591595
if crs_col is not None:
592-
return _apply_column_function('rf_spatial_index', geom_col, crs_col)
596+
return Column(jfcn(_to_java_column(geom_col), _to_java_column(crs_col), index_resolution))
593597
else:
594-
return _apply_column_function('rf_spatial_index', geom_col)
598+
return Column(jfcn(_to_java_column(geom_col), index_resolution))
595599

596600
# ------ GeoMesa Functions ------
597601

pyrasterframes/src/main/python/tests/VectorTypesTests.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,9 @@ def test_spatial_index(self):
163163
indexes = {x[0] for x in df.collect()}
164164
self.assertSetEqual(indexes, expected)
165165

166+
# Custom resolution
167+
df = self.df.select(rf_spatial_index(self.df.poly_geom, rf_crs(lit("EPSG:4326")), 3).alias('index'))
168+
expected = {21, 36}
169+
indexes = {x[0] for x in df.collect()}
170+
self.assertSetEqual(indexes, expected)
171+

0 commit comments

Comments
 (0)