geotrellis.raster.vectorize.Polygonizer.scala Maven / Gradle / Ivy
package geotrellis.raster.vectorize
import geotrellis.raster._
import geotrellis.vector._
import com.vividsolutions.jts.geom
import scala.collection.mutable
/**
* Created by eugene on 2/16/16.
*/
/* Rules:
* Start with top left. Mark point on Top Left corner
* Check adjacent cells in counter clockwise starting from left:
* - left, down, right, up
*
* On move, if the previous move is in the same direction, make no marks.
* If the previous move and this move make a right hand turn,
* a mark is made on the border of this cell and the previous cell
* at the corner of the turn.
* If they make a left hand turn, a mark is made on the edge opposite of
* the border of this cell and the previous cell at the corner of the turn.
* If the previous move was in the opposite direction, mark the two
* corners of the edge opposite of the border of this cell and the
* previous cell.
*
* Marks are signified by TL, TR, BL, BR based on previous cell.
*
* TL = Top Left TR = Top Right BL = Bottom Left BR = Bottom Right
* D = Down U = Up L = Left R = Right
* PM = Previous Move LHT = Left Hand Turn RHT = Right Hand Turn
* RT = Reverse Turn
*
* Move PM RHT Mark PM LHT Mark PM RT Mark
* ------ -------------- --------------- -------------
* D R BL L TL U TL, TR
* R U BR D BL L TL, BL
* U L TR R BR D BL, BR
* L D TL U TR R BR, TR
*
*/
class Polygonizer(val r: Tile, rasterExtent: RasterExtent) {
val cols = r.cols
val rows = r.rows
val halfCellWidth = rasterExtent.cellwidth / 2.0
val halfCellHeight = rasterExtent.cellheight / 2.0
val jtsFactory = new geom.GeometryFactory()
// Directions
val NOTFOUND = -1
val LEFT = 0
val DOWN = 1
val RIGHT = 2
val UP = 3
// Marks
val TOPLEFT = 0
val BOTTOMLEFT = 1
val BOTTOMRIGHT = 2
val TOPRIGHT = 3
def sd(d: Int) =
d match {
case NOTFOUND => "NOTFOUND"
case LEFT => "LEFT"
case DOWN => "DOWN"
case RIGHT => "RIGHT"
case UP => "UP"
case _ => "BAD"
}
def sm(d: Int) =
d match {
case TOPLEFT => "TOPLEFT"
case BOTTOMLEFT => "BOTTOMLEFT"
case BOTTOMRIGHT => "BOTTOMRIGHT"
case TOPRIGHT => "TOPRIGHT"
case _ => "BAD"
}
def mark(col: Int, row: Int, m: Int): geom.Coordinate = {
val mapX = rasterExtent.gridColToMap(col)
val mapY = rasterExtent.gridRowToMap(row)
if(m == TOPLEFT) {
new geom.Coordinate(mapX - halfCellWidth, mapY + halfCellHeight)
} else if(m == BOTTOMLEFT) {
new geom.Coordinate(mapX - halfCellWidth, mapY - halfCellHeight)
} else if(m == BOTTOMRIGHT) {
new geom.Coordinate(mapX + halfCellWidth, mapY - halfCellHeight)
} else if(m == TOPRIGHT) {
new geom.Coordinate(mapX + halfCellWidth, mapY + halfCellHeight)
} else {
sys.error("Bad Mark Integer")
}
}
/* Move PM RHT Mark PM LHT Mark PM RT Mark
* ------ -------------- --------------- -------------
* D R BL L TL U TL, TR
* R U BR D BL L BL, TL
* U L TR R BR D BR, BL
* L D TL U TR R TR, BR
*/
def makeMarks(points: mutable.ArrayBuffer[geom.Coordinate],
col: Int, row: Int, d: Int, pd: Int) = {
if(d == DOWN) {
if(pd != DOWN) {
if(pd == RIGHT) { //RHT
points += mark(col, row - 1, BOTTOMLEFT)
} else if(pd == LEFT) { //LHT
points += mark(col, row - 1, TOPLEFT)
} else { //RT
points += mark(col, row - 1, TOPRIGHT)
points += mark(col, row - 1, TOPLEFT)
}
}
} else if(d == RIGHT) {
if(pd != RIGHT) {
if(pd == UP) { //RHT
points += mark(col - 1, row, BOTTOMRIGHT)
} else if(pd == DOWN) { //LHT
points += mark(col - 1, row, BOTTOMLEFT)
} else { //RT
points += mark(col - 1, row, TOPLEFT)
points += mark(col - 1, row, BOTTOMLEFT)
}
}
} else if(d == UP) {
if(pd != UP) {
if(pd == LEFT) { //RHT
points += mark(col, row + 1, TOPRIGHT)
} else if(pd == RIGHT) { //LHT
points += mark(col, row + 1, BOTTOMRIGHT)
} else { //RT
points += mark(col, row + 1, BOTTOMLEFT)
points += mark(col, row + 1, BOTTOMRIGHT)
}
}
} else if(d == LEFT) {
if(pd != LEFT) {
if(pd == DOWN) { //RHT
points += mark(col + 1, row, TOPLEFT)
} else if(pd == UP) { //LHT
points += mark(col + 1, row, TOPRIGHT)
} else { //RT
points += mark(col + 1, row, BOTTOMRIGHT)
points += mark(col + 1, row, TOPRIGHT)
}
}
} else { sys.error(s"Unknown direction $d") }
}
def findNextDirection(col: Int, row: Int, d: Int, targetValue: Int): Int = {
var i = d + 3
while(i < d + 7) {
val m = i % 4
if(m == 0) {
// Check left
if(col > 0) {
if(r.get(col - 1, row) == targetValue) {
return LEFT
}
}
}
else if(m == 1) {
// Check down
if(row + 1 < rows) {
if(r.get(col, row + 1) == targetValue) {
return DOWN
}
}
}
else if(m == 2) {
// Check right
if(col + 1 < cols) {
if(r.get(col + 1, row) == targetValue) {
return RIGHT
}
}
}
else if(m == 3) {
// Check up
if(row > 0) {
if(r.get(col, row - 1) == targetValue) {
return UP
}
}
}
i += 1
}
return NOTFOUND
}
def getLinearRing[T](targetValue: Int, startPoint: (Int, Int)): geom.LinearRing = {
val points = mutable.ArrayBuffer[geom.Coordinate]()
val startCol = startPoint._1
val startRow = startPoint._2
// First check down and right of first.
var direction = NOTFOUND
if(startRow + 1 < rows) {
if(r.get(startCol, startRow + 1) == targetValue) {
direction = DOWN
}
}
if(direction == NOTFOUND && startCol < cols) {
if(r.get(startCol + 1, startRow) == targetValue) {
direction = RIGHT
}
}
if(direction == NOTFOUND) {
// Single cell polygon.
points += mark(startCol, startRow, TOPLEFT)
points += mark(startCol, startRow, BOTTOMLEFT)
points += mark(startCol, startRow, BOTTOMRIGHT)
points += mark(startCol, startRow, TOPRIGHT)
points += mark(startCol, startRow, TOPLEFT)
} else {
points += mark(startCol, startRow, TOPLEFT)
if(direction == RIGHT) { points += mark(startCol, startRow, BOTTOMLEFT) }
var previousDirection = direction
var col = startCol
var row = startRow
var break = false
while(!break) {
// Move in the direction.
if(direction == DOWN) {
row += 1
} else if(direction == RIGHT) {
col += 1
} else if(direction == UP) {
row -= 1
} else if(direction == LEFT) {
col -= 1
}
makeMarks(points, col, row, direction, previousDirection)
previousDirection = direction
direction = findNextDirection(col, row, direction, targetValue)
//println(s"PREVIOUS ${sd(previousDirection)} NEXT ${sd(direction)} ($col, $row)")
if(col == startCol && row == startRow) {
if(previousDirection == LEFT || previousDirection == DOWN) {
break = true
} else if((previousDirection == UP || previousDirection == RIGHT) &&
(direction == DOWN || direction == LEFT)) {
break = true
}
}
}
// Make end marks
if(previousDirection == UP) { points += mark(col, row, TOPRIGHT) }
points += mark(startCol, startRow, TOPLEFT) // Completes the ring
}
jtsFactory.createLinearRing(points.toArray)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy