-
Notifications
You must be signed in to change notification settings - Fork 362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add union to merge methods #3482
base: master
Are you sure you want to change the base?
Changes from 1 commit
5e8c0f9
bfdc085
d22e985
f9cc308
2f64ec1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -154,4 +154,69 @@ trait SinglebandTileMergeMethods extends TileMergeMethods[Tile] { | |
case _ => | ||
self | ||
} | ||
|
||
def union(extent: Extent, otherExtent: Extent, other: Tile, method: ResampleMethod, unionF: (Option[Double], Option[Double]) => Double): Tile = { | ||
val unionInt = (l: Option[Double], r: Option[Double]) => unionF(l, r).toInt | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, mb the signature |
||
|
||
val combinedExtent = otherExtent combine extent | ||
val re = RasterExtent(extent, self.cols, self.rows) | ||
val gridBounds = re.gridBoundsFor(combinedExtent, false) | ||
val targetCS = CellSize(combinedExtent, gridBounds.width, gridBounds.height) | ||
val targetRE = RasterExtent(combinedExtent, targetCS) | ||
val mutableTile = ArrayTile.empty(self.cellType, targetRE.cols, targetRE.rows) | ||
|
||
self.cellType match { | ||
case BitCellType | ByteCellType | UByteCellType | ShortCellType | UShortCellType | IntCellType => | ||
val interpolateLeft: (Double, Double) => Int = Resample(method, self, extent, targetCS).resample _ | ||
val interpolateRight: (Double, Double) => Int = Resample(method, other, otherExtent, targetCS).resample _ | ||
// Assume 0 as the transparent value | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it really an assumption here? In the original merge logic we checked it to merge new pixels into the new tile in case it's no data. |
||
cfor(0)(_ < targetRE.rows, _ + 1) { row => | ||
cfor(0)(_ < targetRE.cols, _ + 1) { col => | ||
val (x,y) = targetRE.gridToMap(col, row) | ||
val (l,r) = (interpolateLeft(x, y), interpolateRight(x, y)) | ||
mutableTile.set(col, row, unionInt(Some(l.toDouble), Some(r.toDouble))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Set only if val v = unionInt(l.some, r.some)
if(isData(v)) mutableTile.set(col, row, v) It's actually a debatable question, but the NoData handling for Doubles and Ints is different and can be hard to capture for users with the |
||
} | ||
} | ||
case FloatCellType | DoubleCellType => | ||
val interpolateLeft: (Double, Double) => Double = Resample(method, self, extent, targetCS).resampleDouble _ | ||
val interpolateRight: (Double, Double) => Double = Resample(method, other, otherExtent, targetCS).resampleDouble _ | ||
|
||
// Assume 0.0 as the transparent value | ||
cfor(0)(_ < targetRE.rows, _ + 1) { row => | ||
cfor(0)(_ < targetRE.cols, _ + 1) { col => | ||
val (x,y) = targetRE.gridToMap(col, row) | ||
val (l,r) = (interpolateLeft(x, y), interpolateRight(x, y)) | ||
mutableTile.setDouble(col, row, unionF(Some(l), Some(r))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
case x if x.isFloatingPoint => | ||
val interpolateLeft: (Double, Double) => Double = Resample(method, self, extent, targetCS).resampleDouble _ | ||
val interpolateRight: (Double, Double) => Double = Resample(method, other, otherExtent, targetCS).resampleDouble _ | ||
cfor(0)(_ < targetRE.rows, _ + 1) { row => | ||
cfor(0)(_ < targetRE.cols, _ + 1) { col => | ||
val (x,y) = targetRE.gridToMap(col, row) | ||
val l = interpolateLeft(x, y) | ||
val r = interpolateRight(x, y) | ||
val maybeL = if (isNoData(l)) None else Some(l) | ||
val maybeR = if (isNoData(r)) None else Some(r) | ||
mutableTile.setDouble(col, row, unionF(maybeL, maybeR)) | ||
} | ||
} | ||
case _ => | ||
val interpolateLeft: (Double, Double) => Int = Resample(method, self, extent, targetCS).resample _ | ||
val interpolateRight: (Double, Double) => Int = Resample(method, other, otherExtent, targetCS).resample _ | ||
cfor(0)(_ < targetRE.rows, _ + 1) { row => | ||
cfor(0)(_ < targetRE.cols, _ + 1) { col => | ||
val (x,y) = targetRE.gridToMap(col, row) | ||
val l = interpolateLeft(x, y) | ||
val r = interpolateRight(x, y) | ||
val maybeL = if (isNoData(l)) None else Some(l.toDouble) | ||
val maybeR = if (isNoData(r)) None else Some(r.toDouble) | ||
mutableTile.set(col, row, unionInt(maybeL, maybeR)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should not there be extra |
||
//if (l!=r) println(s"x => ${x}, y => ${y}, col => ${col}, row => ${row} | l,r => ${l}, ${r}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A forgotten comment? |
||
} | ||
} | ||
} | ||
mutableTile | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,4 +31,13 @@ abstract class TileFeatureMergeMethods[ | |
|
||
def merge(extent: Extent, otherExtent: Extent, other: TileFeature[T, D], method: ResampleMethod): TileFeature[T, D] = | ||
TileFeature(self.tile.merge(extent, otherExtent, other.tile, method), Semigroup[D].combine(self.data, other.data)) | ||
|
||
def union( | ||
extent: Extent, | ||
otherExtent: Extent, | ||
other: TileFeature[T, D], | ||
method: ResampleMethod, | ||
unionF: (Option[Double], Option[Double]) => Double | ||
): TileFeature[T, D] = | ||
TileFeature(self.tile.union(extent, otherExtent, other.tile, method, unionF), Semigroup[D].combine(self.data, other.data)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really realted to this PR, but it looks like it's the time to import |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
* Copyright 2016 Azavea | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package geotrellis.raster.merge | ||
|
||
import geotrellis.raster._ | ||
import geotrellis.raster.testkit._ | ||
import geotrellis.raster.resample.NearestNeighbor | ||
import geotrellis.vector.Extent | ||
|
||
import org.scalatest.matchers.should.Matchers | ||
import org.scalatest.funspec.AnyFunSpec | ||
|
||
class TileUnionMethodsSpec extends AnyFunSpec | ||
with Matchers | ||
with TileBuilders | ||
with RasterMatchers { | ||
describe("SinglebandTileMergeMethods") { | ||
|
||
it("should union two tiles such that the extent of the output is equal to the minimum extent which covers both") { | ||
val cellTypes: Seq[CellType] = | ||
Seq( | ||
BitCellType, | ||
ByteCellType, | ||
ByteConstantNoDataCellType, | ||
ByteUserDefinedNoDataCellType(1.toByte), | ||
UByteCellType, | ||
UByteConstantNoDataCellType, | ||
UByteUserDefinedNoDataCellType(1.toByte), | ||
ShortCellType, | ||
ShortConstantNoDataCellType, | ||
ShortUserDefinedNoDataCellType(1.toShort), | ||
UShortCellType, | ||
UShortConstantNoDataCellType, | ||
UShortUserDefinedNoDataCellType(1.toShort), | ||
IntCellType, | ||
IntConstantNoDataCellType, | ||
IntUserDefinedNoDataCellType(1), | ||
FloatCellType, | ||
FloatConstantNoDataCellType, | ||
FloatUserDefinedNoDataCellType(1.0f), | ||
DoubleCellType, | ||
DoubleConstantNoDataCellType, | ||
DoubleUserDefinedNoDataCellType(1.0) | ||
) | ||
|
||
for(ct <- cellTypes) { | ||
val arr = Array.ofDim[Double](100).fill(5.0) | ||
arr(50) = 1.0 | ||
arr(55) = 0.0 | ||
arr(60) = Double.NaN | ||
|
||
val tile1 = | ||
DoubleArrayTile(arr, 10, 10).convert(ct) | ||
val e1 = Extent(0, 0, 1, 1) | ||
val tile2 = | ||
tile1.prototype(ct, tile1.cols, tile1.rows) | ||
val e2 = Extent(1, 1, 2, 2) | ||
val unioned = tile1.union(e1, e2, tile2, NearestNeighbor, (d1, d2) => d1.getOrElse(4)) | ||
withClue(s"Failing on cell type $ct: ") { | ||
unioned.rows shouldBe (20) | ||
unioned.cols shouldBe (20) | ||
} | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
untionF
=>unionFunc
(it took me a while to understand what F stands for here) :D