Skip to content

Commit 10885ed

Browse files
committed
Merge branch 'release/0.9.1'
2 parents 768b31d + f6ed467 commit 10885ed

File tree

73 files changed

+1153
-1639
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1153
-1639
lines changed

.circleci/config.yml

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@ orbs:
66
executors:
77
default:
88
docker:
9-
- image: docker.pkg.github.com/locationtech/rasterframes/miniconda-gdal:latest
10-
auth:
11-
username: $GITHUB_USERNAME
12-
password: $GITHUB_PASSWORD
13-
resource_class: medium
9+
- image: s22s/miniconda-gdal:latest
1410
working_directory: ~/repo
1511
environment:
1612
SBT_VERSION: 1.3.8
@@ -46,7 +42,8 @@ orbs:
4642
steps:
4743
- run:
4844
name: Install requirements
49-
command: pip3 install --progress-bar=off --user -r pyrasterframes/src/main/python/requirements.txt
45+
command: /opt/conda/bin/conda install -c conda-forge --yes --file pyrasterframes/src/main/python/requirements-condaforge.txt
46+
5047

5148
rasterframes:
5249
commands:
@@ -121,6 +118,7 @@ jobs:
121118
- checkout
122119
- sbt/setup
123120
- python/setup
121+
- python/requirements
124122
- rasterframes/setup
125123
- rasterframes/restore-cache
126124
- sbt/compile
@@ -140,7 +138,6 @@ jobs:
140138
- run:
141139
name: "Create PyRasterFrames package"
142140
command: |-
143-
python -m pip install --user pyspark==2.4.5
144141
sbt -v -batch pyrasterframes/package
145142
146143
- run:
@@ -171,7 +168,6 @@ jobs:
171168

172169
it:
173170
executor: sbt/default
174-
resource_class: large
175171
steps:
176172
- checkout
177173
- sbt/setup
@@ -189,7 +185,6 @@ jobs:
189185

190186
it-no-gdal:
191187
executor: sbt/default
192-
resource_class: large
193188
steps:
194189
- checkout
195190
- sbt/setup
@@ -214,31 +209,27 @@ workflows:
214209
version: 2
215210
all:
216211
jobs:
217-
- test:
218-
context: rasterframes
212+
- test
219213

220214
- it:
221-
context: rasterframes
222-
# requires:
223-
# - test
215+
requires:
216+
- test
224217
filters:
225218
branches:
226219
only:
227220
- /feature\/.*-it.*/
228221
- /it\/.*/
229222

230223
- it-no-gdal:
231-
context: rasterframes
232-
# requires:
233-
# - test
224+
requires:
225+
- test
234226
filters:
235227
branches:
236228
only:
237229
- /feature\/.*-it.*/
238230
- /it\/.*/
239231

240232
- docs:
241-
context: rasterframes
242233
filters:
243234
branches:
244235
only:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ tour/*.tiff
2727
scoverage-report*
2828

2929
zz-*
30+
rf-notebook/src/main/notebooks/.ipython

.java-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.8

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ Please see the [Getting Started](http://rasterframes.io/getting-started.html) se
1717
* [Gitter Channel](https://gitter.im/locationtech/rasterframes)
1818
* [Submit an Issue](https://github.com/locationtech/rasterframes/issues)
1919

20-
2120
## Contributing
2221

2322
Community contributions are always welcome. To get started, please review our [contribution guidelines](https://github.com/locationtech/rasterframes/blob/develop/CONTRIBUTING.md), [code of conduct](https://github.com/locationtech/rasterframes/blob/develop/CODE_OF_CONDUCT.md), and reach out to us on [gitter](https://gitter.im/locationtech/rasterframes) so the community can help you get started!
@@ -62,6 +61,11 @@ Additional, Python sepcific build instruction may be found at [pyrasterframes/sr
6261

6362
## Copyright and License
6463

65-
RasterFrames is released under the Apache 2.0 License, copyright Astraea, Inc. 2017-2019.
64+
RasterFrames is released under the commercial-friendly Apache 2.0 License, copyright Astraea, Inc. 2017-2021.
65+
66+
## Commercial Support
67+
68+
As the sponsors and developers of RasterFrames, [Astraea, Inc.](https://astraea.earth/) is uniquely positioned to expand its capabilities. If you need additional functionality or just some architectural guidance to get your project off to the right start, we can provide a full range of [consulting and development services](https://astraea.earth/services/) around RasterFrames. We can be reached at [[email protected]](mailto:[email protected]).
69+
6670

6771

RELEASE.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@
88
a. `clean`
99
b. `test it:test`
1010
c. `makeSite`
11-
d. `rf-notebook/publishLocal`
12-
e. `publishSigned` (LocationTech credentials required)
13-
f. `sonatypeReleaseAll`. It can take a while, but should eventually show up [here](https://search.maven.org/search?q=g:org.locationtech.rasterframes).
14-
g. `docs/ghpagesPushSite`
15-
h. `rf-notebook/publish`
11+
d. `publishSigned` (LocationTech credentials required)
12+
e. `sonatypeReleaseAll`. It can take a while, but should eventually show up [here](https://search.maven.org/search?q=g:org.locationtech.rasterframes).
13+
f. `docs/ghpagesPushSite`
14+
g. `rf-notebook/publish`
1615
6. `cd pyrasterframes/target/python/dist`
1716
7. `python3 -m twine upload pyrasterframes-x.y.z-py2.py3-none-any.whl`
1817
8. Commit any changes that were necessary.

core/src/main/resources/reference.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
rasterframes {
22
nominal-tile-size = 256
3-
prefer-gdal = true
3+
prefer-gdal = false
44
showable-tiles = false
55
showable-max-cells = 20
66
max-truncate-row-element-length = 40

core/src/main/scala/org/locationtech/rasterframes/expressions/localops/Resample.scala

Lines changed: 136 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,53 +22,157 @@
2222
package org.locationtech.rasterframes.expressions.localops
2323

2424
import geotrellis.raster.Tile
25-
import geotrellis.raster.resample.NearestNeighbor
25+
import geotrellis.raster.resample._
26+
import geotrellis.raster.resample.{ResampleMethod GTResampleMethod, Max RMax, Min RMin}
2627
import org.apache.spark.sql.Column
2728
import org.apache.spark.sql.catalyst.InternalRow
29+
import org.apache.spark.sql.catalyst.analysis.TypeCheckResult
30+
import org.apache.spark.sql.catalyst.analysis.TypeCheckResult.{TypeCheckFailure, TypeCheckSuccess}
2831
import org.apache.spark.sql.catalyst.expressions.codegen.CodegenFallback
29-
import org.apache.spark.sql.catalyst.expressions.{Expression, ExpressionDescription}
32+
import org.apache.spark.sql.catalyst.expressions.{Expression, ExpressionDescription, Literal, TernaryExpression}
3033
import org.apache.spark.sql.functions.lit
31-
import org.locationtech.rasterframes.expressions.BinaryLocalRasterOp
32-
import org.locationtech.rasterframes.expressions.DynamicExtractors.tileExtractor
34+
import org.apache.spark.sql.rf.TileUDT
35+
import org.apache.spark.sql.types.{DataType, StringType}
36+
import org.apache.spark.unsafe.types.UTF8String
37+
import org.locationtech.rasterframes.util.ResampleMethod
38+
import org.locationtech.rasterframes.encoders.CatalystSerializer._
39+
import org.locationtech.rasterframes.expressions.{fpTile, row}
40+
import org.locationtech.rasterframes.expressions.DynamicExtractors._
41+
42+
43+
abstract class ResampleBase(left: Expression, right: Expression, method: Expression)
44+
extends TernaryExpression
45+
with CodegenFallback with Serializable {
3346

34-
@ExpressionDescription(
35-
usage = "_FUNC_(tile, factor) - Resample tile to different size based on scalar factor or tile whose dimension to match. Scalar less than one will downsample tile; greater than one will upsample. Uses nearest-neighbor value.",
36-
arguments = """
37-
Arguments:
38-
* tile - tile
39-
* rhs - scalar or tile to match dimension""",
40-
examples = """
41-
Examples:
42-
> SELECT _FUNC_(tile, 2.0);
43-
...
44-
> SELECT _FUNC_(tile1, tile2);
45-
..."""
46-
)
47-
case class Resample(left: Expression, right: Expression) extends BinaryLocalRasterOp
48-
with CodegenFallback {
4947
override val nodeName: String = "rf_resample"
50-
override protected def op(left: Tile, right: Tile): Tile = left.resample(right.cols, right.rows, NearestNeighbor)
51-
override protected def op(left: Tile, right: Double): Tile = left.resample((left.cols * right).toInt,
52-
(left.rows * right).toInt, NearestNeighbor)
53-
override protected def op(left: Tile, right: Int): Tile = op(left, right.toDouble)
48+
override def dataType: DataType = left.dataType
49+
override def children: Seq[Expression] = Seq(left, right, method)
50+
51+
def targetFloatIfNeeded(t: Tile, method: GTResampleMethod): Tile =
52+
method match {
53+
case NearestNeighbor | Mode | RMax | RMin | Sum t
54+
case _ fpTile(t)
55+
}
56+
57+
// These methods define the core algorithms to be used.
58+
def op(left: Tile, right: Tile, method: GTResampleMethod): Tile =
59+
op(left, right.cols, right.rows, method)
60+
61+
def op(left: Tile, right: Double, method: GTResampleMethod): Tile =
62+
op(left, (left.cols * right).toInt, (left.rows * right).toInt, method)
63+
64+
def op(tile: Tile, newCols: Int, newRows: Int, method: GTResampleMethod): Tile =
65+
targetFloatIfNeeded(tile, method).resample(newCols, newRows, method)
66+
67+
override def checkInputDataTypes(): TypeCheckResult = {
68+
// copypasta from BinaryLocalRasterOp
69+
if (!tileExtractor.isDefinedAt(left.dataType)) {
70+
TypeCheckFailure(s"Input type '${left.dataType}' does not conform to a raster type.")
71+
}
72+
else if (!tileOrNumberExtractor.isDefinedAt(right.dataType)) {
73+
TypeCheckFailure(s"Input type '${right.dataType}' does not conform to a compatible type.")
74+
} else method.dataType match {
75+
case StringType TypeCheckSuccess
76+
case _ TypeCheckFailure(s"Cannot interpret value of type `${method.dataType.simpleString}` for resampling method; please provide a String method name.")
77+
}
78+
}
79+
80+
override def nullSafeEval(input1: Any, input2: Any, input3: Any): Any = {
81+
// more copypasta from BinaryLocalRasterOp
82+
implicit val tileSer = TileUDT.tileSerializer
83+
84+
val (leftTile, leftCtx) = tileExtractor(left.dataType)(row(input1))
85+
val methodString = input3.asInstanceOf[UTF8String].toString
86+
87+
val resamplingMethod = methodString match {
88+
case ResampleMethod(mm) => mm
89+
case _ => throw new IllegalArgumentException("Unrecognized resampling method specified")
90+
}
91+
92+
val result: Tile = tileOrNumberExtractor(right.dataType)(input2) match {
93+
// in this case we expect the left and right contexts to vary. no warnings raised.
94+
case TileArg(rightTile, _) op(leftTile, rightTile, resamplingMethod)
95+
case DoubleArg(d) op(leftTile, d, resamplingMethod)
96+
case IntegerArg(i) op(leftTile, i.toDouble, resamplingMethod)
97+
}
98+
99+
// reassemble the leftTile with its context. Note that this operation does not change Extent and CRS
100+
leftCtx match {
101+
case Some(ctx) ctx.toProjectRasterTile(result).toInternalRow
102+
case None result.toInternalRow
103+
}
104+
}
54105

55106
override def eval(input: InternalRow): Any = {
56107
if(input == null) null
57108
else {
58109
val l = left.eval(input)
59110
val r = right.eval(input)
60-
if (l == null && r == null) null
61-
else if (l == null) r
62-
else if (r == null && tileExtractor.isDefinedAt(right.dataType)) l
63-
else if (r == null) null
64-
else nullSafeEval(l, r)
111+
val m = method.eval(input)
112+
if (m == null) null // no method, return null
113+
else if (l == null) null // no l tile, return null
114+
else if (r == null) l // no target tile or factor, return l without changin it
115+
else nullSafeEval(l, r, m)
65116
}
66117
}
118+
67119
}
68-
object Resample{
69-
def apply(left: Column, right: Column): Column =
70-
new Column(Resample(left.expr, right.expr))
120+
121+
@ExpressionDescription(
122+
usage = "_FUNC_(tile, factor, method_name) - Resample tile to different dimension based on scalar `factor` or a tile whose dimension to match. Scalar less than one will downsample tile; greater than one will upsample. Uses resampling method named in the `method_name`." +
123+
"Methods average, mode, median, max, min, and sum aggregate over cells when downsampling",
124+
arguments = """
125+
Arguments:
126+
* tile - tile
127+
* factor - scalar or tile to match dimension
128+
* method_name - one the following options: nearest_neighbor, bilinear, cubic_convolution, cubic_spline, lanczos, average, mode, median, max, min, sum
129+
This option can be CamelCase as well
130+
""",
131+
examples = """
132+
Examples:
133+
> SELECT _FUNC_(tile, 0.2, median);
134+
...
135+
> SELECT _FUNC_(tile1, tile2, lit("cubic_spline"));
136+
..."""
137+
)
138+
case class Resample(left: Expression, factor: Expression, method: Expression)
139+
extends ResampleBase(left, factor, method)
140+
141+
object Resample {
142+
def apply(left: Column, right: Column, methodName: String): Column =
143+
new Column(Resample(left.expr, right.expr, lit(methodName).expr))
144+
145+
def apply(left: Column, right: Column, method: Column): Column =
146+
new Column(Resample(left.expr, right.expr, method.expr))
147+
148+
def apply[N: Numeric](left: Column, right: N, method: String): Column = new Column(Resample(left.expr, lit(right).expr, lit(method).expr))
149+
150+
def apply[N: Numeric](left: Column, right: N, method: Column): Column = new Column(Resample(left.expr, lit(right).expr, method.expr))
151+
152+
}
153+
154+
@ExpressionDescription(
155+
usage = "_FUNC_(tile, factor) - Resample tile to different size based on scalar factor or tile whose dimension to match. Scalar less than one will downsample tile; greater than one will upsample. Uses nearest-neighbor value.",
156+
arguments = """
157+
Arguments:
158+
* tile - tile
159+
* rhs - scalar or tile to match dimension""",
160+
examples = """
161+
Examples:
162+
> SELECT _FUNC_(tile, 2.0);
163+
...
164+
> SELECT _FUNC_(tile1, tile2);
165+
...""")
166+
case class ResampleNearest(tile: Expression, target: Expression)
167+
extends ResampleBase(tile, target, Literal("nearest")) {
168+
override val nodeName: String = "rf_resample_nearest"
169+
}
170+
object ResampleNearest {
171+
def apply(tile: Column, target: Column): Column =
172+
new Column(ResampleNearest(tile.expr, target.expr))
71173

72174
def apply[N: Numeric](tile: Column, value: N): Column =
73-
new Column(Resample(tile.expr, lit(value).expr))
175+
new Column(ResampleNearest(tile.expr, lit(value).expr))
74176
}
177+
178+

core/src/main/scala/org/locationtech/rasterframes/expressions/package.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ package object expressions {
109109
registry.registerExpression[ExpM1]("rf_expm1")
110110
registry.registerExpression[Sqrt]("rf_sqrt")
111111
registry.registerExpression[Resample]("rf_resample")
112+
registry.registerExpression[ResampleNearest]("rf_resample_nearest")
112113
registry.registerExpression[TileToArrayDouble]("rf_tile_to_array_double")
113114
registry.registerExpression[TileToArrayInt]("rf_tile_to_array_int")
114115
registry.registerExpression[DataCells]("rf_data_cells")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ case class XZ2Indexer(left: Expression, right: Expression, indexResolution: Shor
8383

8484
val index = indexer.index(
8585
env.getMinX, env.getMinY, env.getMaxX, env.getMaxY,
86-
lenient = false
86+
lenient = true
8787
)
8888
index
8989
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ case class Z2Indexer(left: Expression, right: Expression, indexResolution: Short
8080
trans(coord)
8181
}
8282

83-
indexer.index(pt.getX, pt.getY, lenient = false).z
83+
indexer.index(pt.getX, pt.getY, lenient = true).z
8484
}
8585
}
8686

0 commit comments

Comments
 (0)