Skip to content

Repaired non-robust Delaunay triangulator. - #1699

Merged
lossyrob merged 6 commits into
locationtech:masterfrom
jpolchlo:feature/Voronoi
Nov 2, 2016
Merged

lossyrob merged 6 commits into
locationtech:masterfrom
jpolchlo:feature/Voronoi

Conversation

@jpolchlo

@jpolchlo jpolchlo commented Oct 25, 2016 •

Copy link
Copy Markdown
Contributor

This PR replaces the custom, non-robust Delaunay triangulator with wrapped version of the JTS triangulator. The old triangulator did not implement robust predicates for collinearity/cocircularity which could cause failures in some real-world data sets.

The JTS triangulator was wrapped to allow a more idiomatic Scala usage. Namely, allowing for PointFeatures to be used more easily, which would facilitate the use of a triangulation for the purposes of, say, interpolating a field of numbers defined over a sample of points. This wrapped version also replaces the quad-edge structure used by JTS for a lighter-weight half-edge structure. Documentation for that structure can be found in docs/vector/voronoi.md.

This PR closes issue #1683.

@lossyrob

Copy link
Copy Markdown
Member

@jpolchlo you made benchmarks for the old voronio, right? Is it possible to rerun those benchmarks, against the two different versions, to see if we're losing a lot of speed?

@jpolchlo

Copy link
Copy Markdown
Contributor Author

You and I sat down to look at benchmarks, but I didn't write them myself,
so I'm not 100% on how to proceed. With some pointers, I'm sure I could do
it. But I wonder what you have in mind here, since the version that got
replaced will fail on a certain class of inputs, and surely, if I were to
make my original code robust, it will necessarily slow down based on the
additional operations needed to ensure correctness.

On Tue, Oct 25, 2016 at 11:08 AM, Rob Emanuele [email protected]
wrote:

@jpolchlo https://github.com/jpolchlo you made benchmarks for the old
voronio, right? Is it possible to rerun those benchmarks, against the two
different versions, to see if we're losing a lot of speed?

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#1699 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ARjt0aoZ-AZkd9adAEvp0GmdZEsPb57Dks5q3htngaJpZM4KgAI8
.

@lossyrob

lossyrob commented Oct 26, 2016 •

Copy link
Copy Markdown
Member

@jpolchlo the idea is to have a sense of how much things have slowed down. And to have an idea of how efficient this operation is...I remember that you were calculating triangulations on the nickel dataset in about 200ms. If this turns out to be a 2x vs a 10x slowdown, that would be good to understand. If it's a serious slowdown, I'd want to either try to optimize before release or at least put in an issue for future optimization.

@jpolchlo

Copy link
Copy Markdown
Contributor Author

Roger, boss. I'll see what I can figure out.

On Tue, Oct 25, 2016 at 10:47 PM, Rob Emanuele [email protected]
wrote:

@jpolchlo https://github.com/jpolchlo the idea is to have a sense of
how much things have slowed down. And to have an idea of how efficient this
operation is...I remember that you were calculating complex triangulations
on the nickel dataset in about 200ms. If this turns out to be a 2x vs a 10x
slowdown, that would be good to understand. If it's a serious slowdown, I'd
want to either try to optimize before release or at least put in an issue
for future optimization.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#1699 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ARjt0WyJPoijxi0-UCG5rQR9NBuSpUBeks5q3r9EgaJpZM4KgAI8
.

@lossyrob lossyrob added this to the 1.0 milestone Oct 26, 2016
val cm = ColorMap(scala.collection.immutable.Map(1 -> 0x000000ff, 255 -> 0xffffffff))
tile.renderPng(cm).write("delaunay.png")
}
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's going on with all the commented code here?

}

it("should work on a real dataset with many collinear points") {
val allcities = ResourceReader.asString("populous_cities.txt")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't normally read test data out of resources packed in the JAR - is there an advantage you see of doing it this way, other then adding to test files similar to other tests? We would put it here https://github.com/geotrellis/geotrellis/tree/master/vector-test/data

import java.io._

object ResourceReader {
def asString(filename: String): String = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for test functionality, not sure it's worth putting in the core utils JAR

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had talked to Eugene about this. He suggested putting it in util, but it's not super valuable, so I'll nuke it.

* boundary(.next)*.face == None.
*/
val boundary = triangulate(0, vIx.length-1)
val triangles = _triangles.map{ poly => Polygon.jtsToPolygon(poly.asInstanceOf[JTSPolygon]) }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can just use Polygon(poly.asInstanceOf[JTSPolygon]


import org.scalatest.{FunSpec, Matchers}

class DelaunaySpec extends FunSpec with Matchers {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we really scrap all these tests? I think still testing our JTS backed Delaunay class makes sense.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As much as I would like to provide some unit tests here, I'm just at a loss for what should be tested. It seems that most anything that I could test for tests JTS's feature set, and not the features of this package. I'm not sure if that's appropriate. As it is, there is an indirect test of Delaunay and Voronoi in the EuclideanDistanceTileSpec. I'd be happy for some suggestions about what should be tested for in these classes!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some discussion, we've opted to re-insert some tests essentially as regression tests.

}
private val _triangles = {
val tris = subd.getTriangles(gf)
for ( i <- 0 until tris.getNumGeometries ) yield tris.getGeometryN(i)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a cfor loop for performance...also do we need the private val?

import spire.syntax.cfor._

val triangles: Seq[Polygon] = {
  val arr = Array.ofDim[Polygon](tris.getNumGeometries)
  val len = arr.length
   cfor(0)(_ < len, _ + 1) { i =>
    arr(i) = Polygon(tris.getGeometryN(i).asInstanceOf[JTSPolygon])
  }
  arr
}

@lossyrob
lossyrob merged commit 5ca4a8b into locationtech:master Nov 2, 2016
@lossyrob

lossyrob commented Nov 2, 2016

Copy link
Copy Markdown
Member

@jpolchlo "This PR closes issue #1683." doesn't kick off the auto close. Has to be "Fixes #1683".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants