Skip to content

Commit 80d67b3

Browse files
committed
Cleanup Manual TypedEncoders
1 parent 27231a0 commit 80d67b3

File tree

5 files changed

+140
-256
lines changed

5 files changed

+140
-256
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package org.locationtech.rasterframes.encoders
2+
3+
import frameless.{RecordEncoderField, TypedEncoder}
4+
import org.apache.spark.sql.FramelessInternals
5+
import org.apache.spark.sql.catalyst.expressions.objects.{Invoke, InvokeLike, NewInstance, StaticInvoke}
6+
import org.apache.spark.sql.catalyst.expressions.{CreateNamedStruct, Expression, GetStructField, If, IsNull, Literal}
7+
import org.apache.spark.sql.types.{DataType, Metadata, StructField, StructType}
8+
9+
import scala.reflect.{ClassTag, classTag}
10+
11+
/** Can be useful for non Scala types and for complicated case classes with implicits in the constructor. */
12+
object ManualTypedEncoder {
13+
/** Invokes apply from the companion object. */
14+
def staticInvoke[T: ClassTag](
15+
fields: List[RecordEncoderField],
16+
fieldNameModify: String => String = identity,
17+
isNullable: Boolean = true
18+
): TypedEncoder[T] = apply[T](fields, { (classTag, newArgs, jvmRepr) => StaticInvoke(classTag.runtimeClass, jvmRepr, "apply", newArgs, propagateNull = true, returnNullable = false) }, fieldNameModify, isNullable)
19+
20+
/** Invokes object constructor. */
21+
def newInstance[T: ClassTag](
22+
fields: List[RecordEncoderField],
23+
fieldNameModify: String => String = identity,
24+
isNullable: Boolean = true
25+
): TypedEncoder[T] = apply[T](fields, { (classTag, newArgs, jvmRepr) => NewInstance(classTag.runtimeClass, newArgs, jvmRepr, propagateNull = true) }, fieldNameModify, isNullable)
26+
27+
def apply[T: ClassTag](
28+
fields: List[RecordEncoderField],
29+
newInstanceExpression: (ClassTag[T], Seq[Expression], DataType) => InvokeLike,
30+
fieldNameModify: String => String = identity,
31+
isNullable: Boolean = true
32+
): TypedEncoder[T] = make[T](fields, newInstanceExpression, fieldNameModify, isNullable, classTag[T])
33+
34+
private def make[T](
35+
// the catalyst struct
36+
fields: List[RecordEncoderField],
37+
// newInstanceExpression for the fromCatalyst function
38+
newInstanceExpression: (ClassTag[T], Seq[Expression], DataType) => InvokeLike,
39+
// allows to convert the field name into the field name getter
40+
fieldNameModify: String => String,
41+
// is the codec nullable
42+
isNullable: Boolean,
43+
// ClassTag is required for the TypedEncoder constructor
44+
// it is passed explicitly to disambiguate ClassTag passed implicitly as a function argument
45+
// and the one from the TypedEncoder constructor
46+
ct: ClassTag[T]
47+
): TypedEncoder[T] = new TypedEncoder[T]()(ct) {
48+
def nullable: Boolean = isNullable
49+
50+
def jvmRepr: DataType = FramelessInternals.objectTypeFor[T]
51+
52+
def catalystRepr: DataType = {
53+
val structFields = fields.map { field =>
54+
StructField(
55+
name = field.name,
56+
dataType = field.encoder.catalystRepr,
57+
nullable = field.encoder.nullable,
58+
metadata = Metadata.empty
59+
)
60+
}
61+
62+
StructType(structFields)
63+
}
64+
65+
def fromCatalyst(path: Expression): Expression = {
66+
val newArgs: Seq[Expression] = fields.map { field =>
67+
field.encoder.fromCatalyst( GetStructField(path, field.ordinal, Some(field.name)) )
68+
}
69+
val newExpr = newInstanceExpression(classTag, newArgs, jvmRepr)
70+
71+
val nullExpr = Literal.create(null, jvmRepr)
72+
If(IsNull(path), nullExpr, newExpr)
73+
}
74+
75+
def toCatalyst(path: Expression): Expression = {
76+
val nameExprs = fields.map { field => Literal(field.name) }
77+
78+
val valueExprs: Seq[Expression] = fields.map { field =>
79+
val fieldPath = Invoke(path, fieldNameModify(field.name), field.encoder.jvmRepr, Nil)
80+
field.encoder.toCatalyst(fieldPath)
81+
}
82+
83+
// the way exprs are encoded in CreateNamedStruct
84+
val exprs = nameExprs.zip(valueExprs).flatMap { case (nameExpr, valueExpr) => nameExpr :: valueExpr :: Nil }
85+
86+
val createExpr = CreateNamedStruct(exprs)
87+
val nullExpr = Literal.create(null, createExpr.dataType)
88+
If(IsNull(path), nullExpr, createExpr)
89+
}
90+
}
91+
}

core/src/main/scala/org/locationtech/rasterframes/encoders/StandardEncoders.scala

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ package org.locationtech.rasterframes.encoders
2424
import org.locationtech.rasterframes.stats.{CellHistogram, CellStatistics, LocalCellStatistics}
2525
import org.locationtech.jts.geom.Envelope
2626
import geotrellis.proj4.CRS
27-
import geotrellis.raster.{CellSize, CellType, Dimensions, Raster, Tile, TileLayout}
27+
import geotrellis.raster.{CellSize, CellType, Dimensions, Raster, Tile, TileLayout, GridBounds, CellGrid}
2828
import geotrellis.layer._
2929
import geotrellis.vector.{Extent, ProjectedExtent}
3030
import org.apache.spark.sql.catalyst.encoders.ExpressionEncoder
@@ -39,34 +39,22 @@ import java.sql.Timestamp
3939
import scala.reflect.ClassTag
4040
import scala.reflect.runtime.universe._
4141

42-
/**
43-
* TODO: move this overload to GeoTrellis, the reason is in the generic method invocation and Integral in implicits
44-
*/
45-
object DimensionsInt {
46-
def apply(cols: Int, rows: Int): Dimensions[Int] = new Dimensions(cols, rows)
47-
}
48-
49-
object EnvelopeLocal {
50-
def apply(minx: Double, maxx: Double, miny: Double, maxy: Double): Envelope = new Envelope(minx, maxx, miny, miny)
51-
}
52-
53-
/**
54-
* Implicit encoder definitions for RasterFrameLayer types.
55-
*/
5642
trait StandardEncoders extends SpatialEncoders with TypedEncoders {
5743
def expressionEncoder[T: TypeTag]: ExpressionEncoder[T] = ExpressionEncoder()
5844

45+
implicit def optionalEncoder[T: TypedEncoder]: ExpressionEncoder[Option[T]] = typedExpressionEncoder[Option[T]]
46+
47+
implicit lazy val strMapEncoder: ExpressionEncoder[Map[String, String]] = ExpressionEncoder()
5948
implicit lazy val crsExpressionEncoder: ExpressionEncoder[CRS] = ExpressionEncoder()
6049
implicit lazy val projectedExtentEncoder: ExpressionEncoder[ProjectedExtent] = ExpressionEncoder()
6150
implicit lazy val temporalProjectedExtentEncoder: ExpressionEncoder[TemporalProjectedExtent] = ExpressionEncoder()
6251
implicit lazy val timestampEncoder: ExpressionEncoder[Timestamp] = ExpressionEncoder()
63-
implicit lazy val strMapEncoder: ExpressionEncoder[Map[String, String]] = ExpressionEncoder()
6452
implicit lazy val cellStatsEncoder: ExpressionEncoder[CellStatistics] = ExpressionEncoder()
6553
implicit lazy val cellHistEncoder: ExpressionEncoder[CellHistogram] = ExpressionEncoder()
6654
implicit lazy val localCellStatsEncoder: ExpressionEncoder[LocalCellStatistics] = ExpressionEncoder()
67-
implicit lazy val uriEncoder: ExpressionEncoder[URI] = typedExpressionEncoder[URI]
68-
implicit lazy val quantileSummariesEncoder: ExpressionEncoder[QuantileSummaries] = typedExpressionEncoder[QuantileSummaries]
6955

56+
implicit lazy val uriEncoder: ExpressionEncoder[URI] = typedExpressionEncoder[URI]
57+
implicit lazy val quantileSummariesEncoder: ExpressionEncoder[QuantileSummaries] = typedExpressionEncoder[QuantileSummaries]
7058
implicit lazy val envelopeEncoder: ExpressionEncoder[Envelope] = typedExpressionEncoder
7159
implicit lazy val longExtentEncoder: ExpressionEncoder[LongExtent] = typedExpressionEncoder
7260
implicit lazy val extentEncoder: ExpressionEncoder[Extent] = typedExpressionEncoder
@@ -76,18 +64,17 @@ trait StandardEncoders extends SpatialEncoders with TypedEncoders {
7664
implicit lazy val temporalKeyEncoder: ExpressionEncoder[TemporalKey] = typedExpressionEncoder
7765
implicit lazy val spaceTimeKeyEncoder: ExpressionEncoder[SpaceTimeKey] = typedExpressionEncoder
7866
implicit def keyBoundsEncoder[K: TypedEncoder]: ExpressionEncoder[KeyBounds[K]] = typedExpressionEncoder[KeyBounds[K]]
79-
implicit def boundsEncoder[K: TypedEncoder]: ExpressionEncoder[Bounds[K]] = keyBoundsEncoder[KeyBounds[K]].asInstanceOf[ExpressionEncoder[Bounds[K]]]
8067
implicit lazy val cellTypeEncoder: ExpressionEncoder[CellType] = typedExpressionEncoder[CellType]
81-
implicit lazy val dimensionsEncoder: ExpressionEncoder[Dimensions[Int]] = typedExpressionEncoder
68+
implicit def dimensionsEncoder[N: Integral: TypedEncoder]: ExpressionEncoder[Dimensions[N]] = typedExpressionEncoder[Dimensions[N]]
69+
implicit def gridBoundsEncoder[N: Integral: TypedEncoder]: ExpressionEncoder[GridBounds[N]] = typedExpressionEncoder[GridBounds[N]]
8270
implicit lazy val layoutDefinitionEncoder: ExpressionEncoder[LayoutDefinition] = typedExpressionEncoder
8371
implicit def tileLayerMetadataEncoder[K: TypedEncoder: ClassTag]: ExpressionEncoder[TileLayerMetadata[K]] = typedExpressionEncoder[TileLayerMetadata[K]]
8472
implicit lazy val tileContextEncoder: ExpressionEncoder[TileContext] = typedExpressionEncoder
8573
implicit lazy val tileDataContextEncoder: ExpressionEncoder[TileDataContext] = typedExpressionEncoder
8674
implicit lazy val cellContextEncoder: ExpressionEncoder[CellContext] = typedExpressionEncoder
8775

8876
implicit lazy val tileEncoder: ExpressionEncoder[Tile] = typedExpressionEncoder
89-
implicit lazy val optionalTileEncoder: ExpressionEncoder[Option[Tile]] = typedExpressionEncoder
90-
implicit lazy val rasterEncoder: ExpressionEncoder[Raster[Tile]] = typedExpressionEncoder
77+
implicit def rasterEncoder[T <: CellGrid[Int]: TypedEncoder]: ExpressionEncoder[Raster[T]] = typedExpressionEncoder[Raster[T]]
9178
}
9279

9380
object StandardEncoders extends StandardEncoders

0 commit comments

Comments
 (0)