├── .gitignore ├── README.md ├── benchmark └── src │ └── main │ └── scala │ └── jtscala │ └── benchmark │ └── Main.scala ├── core └── src │ ├── main │ └── scala │ │ └── jtscala │ │ ├── Factory.scala │ │ ├── Geometry.scala │ │ ├── GeometryCollection.scala │ │ ├── GeometrySet.scala │ │ ├── Line.scala │ │ ├── LineSet.scala │ │ ├── Main.scala │ │ ├── Point.scala │ │ ├── PointSet.scala │ │ ├── Polygon.scala │ │ ├── PolygonSet.scala │ │ ├── Results.scala │ │ └── package.scala │ └── test │ └── scala │ └── jtscala │ ├── check │ ├── Generators.scala │ └── jts │ │ ├── Generators.scala │ │ ├── LineStringCheck.scala │ │ ├── MultiLineStringCheck.scala │ │ ├── MultiPointCheck.scala │ │ ├── PointCheck.scala │ │ └── PolygonCheck.scala │ └── spec │ ├── LineSpec.scala │ ├── PointSpec.scala │ └── PolygonSpec.scala ├── project ├── Build.scala └── build.properties └── sbt /.gitignore: -------------------------------------------------------------------------------- 1 | project/boot 2 | project/plugins/project 3 | project/plugins/target 4 | project/target 5 | target 6 | .ensime 7 | TAGS 8 | \#*# 9 | *~ 10 | .#* 11 | .lib 12 | *.aux.xml 13 | 14 | *.pyc 15 | .project 16 | .classpath 17 | .cache 18 | .settings 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JTScala 2 | 3 | This is a scala wrapper around the [Java Topology Suite](http://www.vividsolutions.com/jts/JTSHome.htm) 4 | that aims to provide an idiomatic scala interface around JTS, as well 5 | as implement a more type-safe way of interacting with JTS. 6 | -------------------------------------------------------------------------------- /benchmark/src/main/scala/jtscala/benchmark/Main.scala: -------------------------------------------------------------------------------- 1 | package jtscala.benchmark 2 | 3 | import jtscala.check.jts.Generators 4 | 5 | import org.scalacheck.{Prop,Test,Gen,Arbitrary} 6 | 7 | import com.vividsolutions.jts.geom._ 8 | 9 | import java.lang.System.currentTimeMillis 10 | import scala.collection.mutable 11 | 12 | object Bench { 13 | def bench[T](times:Int,body: T => Unit, v:T):Long = { 14 | var i = 0 15 | val start = currentTimeMillis() 16 | while(i < times) { 17 | body(v) 18 | i += 1 19 | } 20 | val duration = currentTimeMillis() - start 21 | duration 22 | } 23 | 24 | def bench[T1,T2](times:Int,body: (T1,T2)=> Unit, v1:T1,v2:T2):Long = { 25 | var i = 0 26 | val start = currentTimeMillis() 27 | while(i < times) { 28 | body(v1,v2) 29 | i += 1 30 | } 31 | val duration = currentTimeMillis() - start 32 | duration 33 | } 34 | 35 | def benchmark[T,K](gen:Gen[T])(f:T=>K)(body:T=>Unit): List[(K,Int,Long)] = { 36 | val params = Test.Parameters.default 37 | import params._ 38 | 39 | val genPrms = new Gen.Parameters.Default { override val rng = params.rng } 40 | 41 | val iterations = math.ceil(minSuccessfulTests) 42 | val sizeStep = (maxSize-minSize) / iterations 43 | 44 | var n = 0 45 | var skipped = 0 46 | 47 | val results = mutable.ListBuffer[(K,Int,Long)]() 48 | while(n < iterations && skipped < iterations) { 49 | val size = (minSize: Double) + (sizeStep * n) 50 | val propPrms = genPrms.resize(size.round.toInt) 51 | gen(propPrms) match { 52 | case None => skipped += 1 53 | case Some(value) => 54 | val key = f(value) 55 | for (times <- Seq(1,10,50)) yield { 56 | if(n == 0) // warmup 57 | bench(times,body,value) 58 | else 59 | results += ((key,times,bench(times,body,value))) 60 | } 61 | } 62 | } 63 | results.toList 64 | } 65 | 66 | def benchmark[T1,T2,K](gen1:Gen[T1],gen2:Gen[T2])(f:(T1,T2)=>K)(body:(T1,T2)=>Unit):List[(K,Int,Long)] = { 67 | val params = Test.Parameters.default 68 | import params._ 69 | 70 | val genPrms = new Gen.Parameters.Default { override val rng = params.rng } 71 | 72 | val iterations = math.ceil(minSuccessfulTests) 73 | val sizeStep = (maxSize-minSize) / iterations 74 | 75 | var n = 0 76 | var skipped = 0 77 | 78 | val results = mutable.ListBuffer[(K,Int,Long)]() 79 | while(n < iterations && skipped < iterations) { 80 | val size = (minSize: Double) + (sizeStep * n) 81 | val propPrms = genPrms.resize(size.round.toInt) 82 | gen1(propPrms) match { 83 | case None => skipped += 1 84 | case Some(value1) => 85 | gen2(propPrms) match { 86 | case None => skipped += 1 87 | case Some(value2) => 88 | val key = f(value1,value2) 89 | for (times <- Seq(1,2)) yield { 90 | if(n == 0) // warmup 91 | bench(times,body,value1,value2) 92 | else 93 | results += ((key,times,bench(times,body,value1,value2))) 94 | } 95 | n += 1 96 | } 97 | } 98 | } 99 | results.toList 100 | } 101 | 102 | def benchmarkArb[T,K](f:T=>K)(body:T=>Unit)(implicit arb:Arbitrary[T]): List[(K,Int,Long)] = 103 | benchmark(arb.arbitrary)(f)(body) 104 | 105 | def benchmarkArb[T1,T2,K](f:(T1,T2)=>K)(body:(T1,T2)=>Unit)(implicit arb1:Arbitrary[T1],arb2:Arbitrary[T2]): List[(K,Int,Long)] = 106 | benchmark(arb1.arbitrary,arb2.arbitrary)(f)(body) 107 | } 108 | 109 | object MultiBenchmark { 110 | import Generators._ 111 | import Bench._ 112 | 113 | def countMulti(m:MultiLineString):Int = m.getNumGeometries 114 | 115 | def do1() = { 116 | val results = 117 | benchmarkArb((ml:MultiLineString,p:Point) => countMulti(ml))({ (ml: MultiLineString,p:Point) => 118 | p.intersection(ml)//.intersection(p) 119 | }) 120 | 121 | val (times,duration) = 122 | results 123 | .map { case (count,times,duration) => (count*times,duration) } 124 | .reduce { (t1,t2) => (t1._1+t2._1,t1._2+t2._2) } 125 | println(s"Average for line string point intersection: ${times/duration.toDouble}") 126 | } 127 | 128 | def do2() = { 129 | val results = 130 | benchmarkArb((ml:List[LineString],p:Point) => ml.size)({ (ml: List[LineString],p:Point) => 131 | val v = 132 | ml.map(_.intersection(p)).find(!_.isEmpty) match { 133 | case Some(p) => p 134 | case None => null 135 | } 136 | }) 137 | 138 | val (times,duration) = 139 | results 140 | .map { case (count,times,duration) => (count*times,duration) } 141 | .reduce { (t1,t2) => (t1._1+t2._1,t1._2+t2._2) } 142 | println(s"Average for line string Seq point intersection: ${times/duration.toDouble}") 143 | } 144 | 145 | lazy val genListLineString:Gen[List[LineString]] = 146 | Gen.choose(1,20).flatMap(Gen.containerOfN[List,LineString](_,genLineString)) 147 | lazy val arbListLineString:Arbitrary[List[LineString]] = 148 | Arbitrary(genListLineString) 149 | 150 | def main(args:Array[String]):Unit = { 151 | do1() 152 | // do2() 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/Factory.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | 3 | import com.vividsolutions.jts.geom 4 | 5 | private[jtscala] object GeomFactory { 6 | val factory = new geom.GeometryFactory() 7 | } 8 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/Geometry.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | 3 | import com.vividsolutions.jts.{geom => jts} 4 | 5 | trait Geometry { 6 | val geom:jts.Geometry 7 | 8 | //def area:Double = geom.getArea 9 | def centroid:Point = Point(geom.getCentroid) 10 | def interiorPoint:Point = Point(geom.getInteriorPoint) 11 | 12 | def coveredBy(other:Geometry) = 13 | geom.coveredBy(other.geom) 14 | 15 | def covers(other:Geometry) = 16 | geom.covers(other.geom) 17 | 18 | def intersects(other:Geometry) = 19 | geom.intersects(other.geom) 20 | 21 | def disjoint(other:Geometry) = 22 | geom.disjoint(other.geom) 23 | 24 | def touches(other:Geometry) = 25 | geom.touches(other.geom) 26 | 27 | def distance(other:Geometry) = 28 | geom.distance(other.geom) 29 | 30 | // Curious to benchmark this against .distance < d, 31 | // JTS implements it as a different op, I'm assuming 32 | // for speed. 33 | def withinDistance(other:Geometry,d:Double) = 34 | geom.isWithinDistance(other.geom,d) 35 | 36 | // TO BE IMPLEMENTED ON A PER TYPE BASIS 37 | 38 | // union ( | ) 39 | 40 | // crosses 41 | // difference ( - ) 42 | // equal (with tolerance?) 43 | // equalExact (huh?) 44 | // boundary 45 | // vertices 46 | // envelope 47 | // boundingBox 48 | // length 49 | // perimeter 50 | 51 | // isSimple 52 | // isValid ( don't allow invalid? ) 53 | // normalize (hmmm) 54 | // overlaps (needs interior to be same dimension as geoms, geom dims ==) 55 | // symDifference 56 | 57 | // within 58 | // something with relate if it's fast (benchmark) 59 | 60 | /**IMPLEMENTED**/ 61 | // buffer - None on collections, always a polygon. (wait maybe on Multli's) 62 | // contains - Not on collections (wait maybe on Multli's) - if not, then other Geometry methods don't belong. 63 | // isRectangle (polygon) 64 | // def area:Double = geom.getArea (not for points?) 65 | 66 | 67 | // def boundary = jts.getBoundary 68 | // def boundaryDimension = jts.getBoundaryDimension 69 | // def centriod = jts.getCentroid 70 | // def coordinate:(Double,Double) = jts.getCoordinate 71 | // def coordinates:Seq[(Double,Double)] = jts.getCoordinates 72 | // def dimension = jts.getDimension 73 | } 74 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/GeometryCollection.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | import GeomFactory._ 3 | 4 | import com.vividsolutions.jts.{geom=>jts} 5 | import scala.collection.mutable 6 | 7 | case class GeometryCollection(points:Set[Point],lines:Set[Line],polygons:Set[Polygon],gc:jts.GeometryCollection) { 8 | val geom = factory.createGeometryCollection((points ++ lines ++ polygons).map(_.geom).toArray) 9 | } 10 | 11 | object GeometryCollection { 12 | def apply(points:Set[Point],lines:Set[Line],polygons:Set[Polygon]):GeometryCollection = { 13 | val geom = factory.createGeometryCollection((points ++ lines ++ polygons).map(_.geom).toArray) 14 | GeometryCollection(points,lines,polygons,geom) 15 | } 16 | 17 | implicit def jtsToGeometryCollection(gc:jts.GeometryCollection):GeometryCollection = { 18 | val (points,lines,polygons) = collectGeometries(gc) 19 | GeometryCollection(points,lines,polygons,gc) 20 | } 21 | 22 | @inline final private 23 | def collectGeometries(gc:jts.GeometryCollection):(Set[Point],Set[Line],Set[Polygon]) = { 24 | val points = mutable.Set[Point]() 25 | val lines = mutable.Set[Line]() 26 | val polygons = mutable.Set[Polygon]() 27 | 28 | val len = gc.getNumGeometries 29 | for(i <- 0 until len) { 30 | gc.getGeometryN(i) match { 31 | case p:jts.Point => points += p 32 | case mp:jts.MultiPoint => points ++= mp 33 | case l:jts.LineString => lines += l 34 | case ml:jts.MultiLineString => lines ++= ml 35 | case p:jts.Polygon => polygons += p 36 | case mp:jts.MultiPolygon => polygons ++= mp 37 | case gc:jts.GeometryCollection => 38 | val (ps,ls,polys) = collectGeometries(gc) 39 | points ++= ps 40 | lines ++= ls 41 | polygons ++= polys 42 | } 43 | } 44 | (points.toSet,lines.toSet,polygons.toSet) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/GeometrySet.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | 3 | import com.vividsolutions.jts.{geom=>jts} 4 | import GeomFactory._ 5 | 6 | trait GeometrySet extends Geometry { 7 | def &(p:Point) = intersection(p) 8 | def intersection(p:Point):PointIntersectionResult = 9 | p.intersection(this) 10 | 11 | def &(ps:PointSet) = intersection(ps) 12 | def intersection(ps:PointSet):PointSetIntersectionResult = 13 | geom.intersection(ps.geom) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/Line.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | 3 | import com.vividsolutions.jts.{geom=>jts} 4 | import GeomFactory._ 5 | 6 | object Line { 7 | implicit def jtsToLine(geom:jts.LineString):Line = apply(geom) 8 | 9 | def apply(geom:jts.LineString):Line = 10 | Line(geom,geom.getCoordinates.map(c => Point(c.x,c.y)).toList) 11 | 12 | def apply(points:Seq[Point]):Line = 13 | apply(points.toList) 14 | def apply(points:Array[Point]):Line = 15 | apply(points.toList) 16 | def apply(points:List[Point]):Line = { 17 | if(points.length < 2) { sys.error("Invalid line: Requires 2 or more points.") } 18 | Line(factory.createLineString(points.map(_.geom.getCoordinate).toArray),points) 19 | } 20 | } 21 | 22 | case class Line(geom:jts.LineString,points:List[Point]) extends Geometry { 23 | assert(!geom.isEmpty) 24 | 25 | def &(p:Point) = intersection(p) 26 | def intersection(p:Point):PointIntersectionResult = p.intersection(this) 27 | 28 | def &(l:Line) = intersection(l) 29 | def intersection(l:Line):LineLineIntersectionResult = 30 | geom.intersection(l.geom) 31 | 32 | def &(p:Polygon) = intersection(p) 33 | def intersection(p:Polygon):PolygonLineIntersectionResult = 34 | geom.intersection(p.geom) 35 | 36 | def &(ps:PointSet) = intersection(ps) 37 | def intersection(ps:PointSet):PointSetIntersectionResult = ps.intersection(this) 38 | 39 | def &(ls:LineSet) = intersection(ls) 40 | def intersection(ls:LineSet):LineSetIntersectionResult = ls.intersection(this) 41 | 42 | def &(ps:PolygonSet) = intersection(ps) 43 | def intersection(ps:PolygonSet):LineSetIntersectionResult = ps.intersection(this) 44 | 45 | def |(p:Point) = union(p) 46 | def union(p:Point):LinePointUnionResult = 47 | geom.union(p.geom) 48 | 49 | def |(l:Line) = union(l) 50 | def union(l:Line):LineLineUnionResult = 51 | geom.union(l.geom) 52 | 53 | def |(p:Polygon) = union(p) 54 | def union(p:Polygon):PolygonXUnionResult = p.union(this) 55 | 56 | // Not sure what to do about LinearString, if it really 57 | // needs to be around...will make construction of Polys 58 | // tougher maybe. 59 | def isClosed = geom.isClosed 60 | 61 | def crosses(g:Geometry) = geom.crosses(g.geom) 62 | 63 | def contains(p:Point) = geom.contains(p.geom) 64 | def contains(l:Line) = geom.contains(l.geom) 65 | def within(l:Line) = geom.within(l.geom) 66 | def within(p:Polygon) = geom.within(p.geom) 67 | 68 | def difference(g:Geometry):Set[Line] = 69 | geom.difference(g.geom) match { 70 | case ml:jts.MultiLineString => 71 | ml 72 | case l:jts.LineString => 73 | Set(Line(l)) 74 | case x => 75 | assert(x.isEmpty) 76 | Set() 77 | } 78 | 79 | def buffer(d:Double):Polygon = 80 | geom.buffer(d).asInstanceOf[Polygon] 81 | } 82 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/LineSet.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | 3 | import com.vividsolutions.jts.{geom=>jts} 4 | import GeomFactory._ 5 | 6 | case class LineSet(ls:Set[Line]) extends GeometrySet { 7 | val geom = factory.createMultiLineString(ls.map(_.geom).toArray) 8 | 9 | def &(l:Line) = intersection(l) 10 | def intersection(l:Line):LineSetIntersectionResult = 11 | geom.intersection(l.geom) 12 | 13 | def &(p:Polygon) = intersection(p) 14 | def intersection(p:Polygon):LineSetIntersectionResult = 15 | geom.intersection(p.geom) 16 | 17 | def &(ls:LineSet) = intersection(ls) 18 | def intersection(ls:LineSet):LineSetIntersectionResult = 19 | geom.intersection(ls.geom) 20 | 21 | def &(ps:PolygonSet) = intersection(ps) 22 | def intersection(ps:PolygonSet):LineSetIntersectionResult = 23 | geom.intersection(ps.geom) 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/Main.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | 3 | import com.vividsolutions.jts.geom 4 | 5 | abstract sealed class GeometryType 6 | 7 | object GeometryType { 8 | implicit def jtsToGeometryType(g:geom.Geometry) = 9 | stringToGeometryType(g.getGeometryType) 10 | 11 | implicit def stringToGeometryType(s:String) = 12 | s match { 13 | case "GeometryCollection" => GeometryCollectionType 14 | case "Point" => PointType 15 | case "LineString" => LineStringType 16 | case "LinearRing" => LinearRingType 17 | case "Polygon" => PolygonType 18 | case "MultiPoint" => MultiPointType 19 | case "MultiLineString" => MultiLineStringType 20 | case "MultiPolygon" => MultiPolygonType 21 | 22 | } 23 | } 24 | 25 | case object GeometryCollectionType extends GeometryType 26 | case object PointType extends GeometryType 27 | case object LineStringType extends GeometryType 28 | case object LinearRingType extends GeometryType 29 | case object PolygonType extends GeometryType 30 | case object MultiPointType extends GeometryType 31 | case object MultiLineStringType extends GeometryType 32 | case object MultiPolygonType extends GeometryType 33 | 34 | object Main { 35 | def main(args:Array[String]) = { 36 | 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/Point.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | 3 | import com.vividsolutions.jts.{geom=>jts} 4 | import GeomFactory._ 5 | 6 | case class Point(geom:jts.Point) extends Geometry { 7 | assert(!geom.isEmpty) 8 | val x = geom.getX 9 | val y = geom.getY 10 | 11 | def &(other:Geometry) = intersection(other) 12 | def intersection(other:Geometry):PointIntersectionResult = 13 | geom.intersection(other.geom) 14 | 15 | def |(p:Point) = union(p) 16 | def union(p:Point):PointPointUnionResult = 17 | geom.union(p.geom) 18 | 19 | def |(l:Line) = union(l) 20 | def union(l:Line):LinePointUnionResult = l.union(this) 21 | 22 | def |(p:Polygon) = union(p) 23 | def union(p:Polygon):PolygonXUnionResult = p.union(this) 24 | 25 | def buffer(d:Double):Polygon = 26 | geom.buffer(d).asInstanceOf[Polygon] 27 | 28 | def within(l:Line) = geom.within(l.geom) 29 | def within(p:Polygon) = geom.within(p.geom) 30 | } 31 | 32 | object Point { 33 | def apply(x:Double,y:Double):Point = 34 | Point(factory.createPoint(new jts.Coordinate(x,y))) 35 | 36 | implicit def jts2Point(geom:jts.Point):Point = apply(geom) 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/PointSet.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | 3 | import com.vividsolutions.jts.{geom=>jts} 4 | import GeomFactory._ 5 | 6 | case class PointSet(ps:Set[Point]) extends GeometrySet { 7 | val geom = factory.createMultiPoint(ps.map(_.geom).toArray) 8 | 9 | def &(l:Line) = intersection(l) 10 | def intersection(l:Line):PointSetIntersectionResult = 11 | geom.intersection(l.geom) 12 | 13 | def &(p:Polygon) = intersection(p) 14 | def intersection(p:Polygon):PointSetIntersectionResult = 15 | geom.intersection(p.geom) 16 | 17 | def &(ls:LineSet) = intersection(ls) 18 | def intersection(ls:LineSet):PointSetIntersectionResult = ls.intersection(this) 19 | 20 | def &(ps:PolygonSet) = intersection(ps) 21 | def intersection(ps:PolygonSet):PointSetIntersectionResult = ps.intersection(this) 22 | 23 | def convexHull:Polygon = 24 | geom.convexHull.asInstanceOf[jts.Polygon] 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/Polygon.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | 3 | import com.vividsolutions.jts.{geom=>jts} 4 | import GeomFactory._ 5 | 6 | object Polygon { 7 | implicit def jtsToPolygon(geom:jts.Polygon):Polygon = Polygon(geom) 8 | 9 | def apply(exterior:Line):Polygon = 10 | apply(exterior,Set()) 11 | 12 | def apply(exterior:Line,holes:Set[Line]):Polygon = { 13 | if(!exterior.isClosed) { 14 | sys.error(s"Cannot create a polygon with unclosed exterior: $exterior") 15 | } 16 | if(exterior.points.length < 4) { 17 | sys.error(s"Cannot create a polygon with exterior with less that 4 points: $exterior") 18 | } 19 | val extGeom = factory.createLinearRing(exterior.geom.getCoordinates) 20 | val holeGeoms = 21 | (for(hole <- holes) yield { 22 | if(!hole.isClosed) { 23 | sys.error(s"Cannot create a polygon with an unclosed hole: $hole") 24 | } else { 25 | if(hole.points.length < 4) 26 | sys.error(s"Cannot create a polygon with a hole with less that 4 points: $hole") 27 | else 28 | factory.createLinearRing(hole.geom.getCoordinates) 29 | } 30 | }).toArray 31 | factory.createPolygon(extGeom,holeGeoms) 32 | } 33 | } 34 | 35 | case class Polygon(geom:jts.Polygon) extends Geometry { 36 | assert(!geom.isEmpty) 37 | 38 | lazy val exterior = Line(geom.getExteriorRing) 39 | 40 | def &(p:Point) = intersection(p) 41 | def intersection(p:Point):PointIntersectionResult = p.intersection(this) 42 | 43 | def &(l:Line) = intersection(l) 44 | def intersection(l:Line):PolygonLineIntersectionResult = l.intersection(this) 45 | 46 | def &(p:Polygon) = intersection(p) 47 | def intersection(p:Polygon):PolygonPolygonIntersectionResult = 48 | geom.intersection(p.geom) 49 | 50 | def &(ps:PointSet) = intersection(ps) 51 | def intersection(ps:PointSet):PointSetIntersectionResult = ps.intersection(this) 52 | 53 | def &(ls:LineSet) = intersection(ls) 54 | def intersection(ls:LineSet):LineSetIntersectionResult = ls.intersection(this) 55 | 56 | def &(ps:PolygonSet) = intersection(ps) 57 | def intersection(ps:PolygonSet):PolygonSetIntersectionResult = ps.intersection(this) 58 | 59 | def |(p:Point) = union(p) 60 | def union(p:Point):PolygonXUnionResult = 61 | geom.union(p.geom) 62 | 63 | def |(l:Line) = union(l) 64 | def union(l:Line):PolygonXUnionResult = 65 | geom.union(l.geom) 66 | 67 | def |(p:Polygon) = union(p) 68 | def union(p:Polygon):PolygonPolygonUnionResult = 69 | geom.union(p.geom) 70 | 71 | def buffer(d:Double):Polygon = 72 | geom.buffer(d).asInstanceOf[Polygon] 73 | 74 | def contains(p:Point) = geom.contains(p.geom) 75 | def contains(l:Line) = geom.contains(l.geom) 76 | def contains(p:Polygon) = geom.contains(p.geom) 77 | 78 | def within(p:Polygon) = geom.within(p.geom) 79 | 80 | lazy val isRectangle = geom.isRectangle 81 | 82 | lazy val area = geom.getArea 83 | } 84 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/PolygonSet.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | 3 | import com.vividsolutions.jts.{geom=>jts} 4 | import GeomFactory._ 5 | 6 | case class PolygonSet(ps:Set[Polygon]) extends GeometrySet { 7 | val geom = factory.createMultiPolygon(ps.map(_.geom).toArray) 8 | 9 | def &(l:Line) = intersection(l) 10 | def intersection(l:Line):LineSetIntersectionResult = 11 | geom.intersection(l.geom) 12 | 13 | def &(p:Polygon) = intersection(p) 14 | def intersection(p:Polygon):PolygonSetIntersectionResult = 15 | geom.intersection(p.geom) 16 | 17 | def &(ls:LineSet):LineSetIntersectionResult = intersection(ls) 18 | def intersection(ls:LineSet):LineSetIntersectionResult = ls.intersection(this) 19 | 20 | def &(ps:PolygonSet) = intersection(ps) 21 | def intersection(ps:PolygonSet):PolygonSetIntersectionResult = 22 | geom.intersection(ps.geom) 23 | 24 | def |(p:Point) = union(p) 25 | def union(p:Point):PolygonSetUnionResult = 26 | geom.union(p.geom) 27 | 28 | def |(l:Line) = union(l) 29 | def union(l:Line):PolygonSetUnionResult = 30 | geom.union(l.geom) 31 | 32 | def |(p:Polygon) = union(p) 33 | def union(p:Polygon):PolygonPolygonUnionResult = 34 | geom.union(p.geom) 35 | 36 | lazy val area:Double = geom.getArea 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/Results.scala: -------------------------------------------------------------------------------- 1 | package jtscala 2 | 3 | import com.vividsolutions.jts.{geom=>jts} 4 | 5 | abstract sealed trait Result 6 | object Result { 7 | implicit def jtsToResult(geom:jts.Geometry):Result = 8 | geom match { 9 | case p:jts.Point => PointResult(p) 10 | case l:jts.LineString => LineResult(l) 11 | case p:jts.Polygon => PolygonResult(p) 12 | case ps:jts.MultiPoint => PointSetResult(ps) 13 | case ps:jts.MultiPolygon => PolygonSetResult(ps) 14 | case ls:jts.MultiLineString => LineSetResult(ls) 15 | case gc:jts.GeometryCollection => GeometryCollectionResult(gc) 16 | case _ => NoResult 17 | } 18 | } 19 | 20 | abstract sealed trait PointIntersectionResult 21 | object PointIntersectionResult { 22 | implicit def jtsToResult(geom:jts.Geometry):PointIntersectionResult = 23 | geom match { 24 | case p:jts.Point => PointResult(p) 25 | case _ => NoResult 26 | } 27 | } 28 | 29 | abstract sealed trait LineLineIntersectionResult 30 | object LineLineIntersectionResult { 31 | implicit def jtsToResult(geom:jts.Geometry):LineLineIntersectionResult = 32 | geom match { 33 | case p:jts.Point => PointResult(p) 34 | case l:jts.LineString => LineResult(l) 35 | case _ => NoResult 36 | } 37 | } 38 | 39 | abstract sealed trait PolygonLineIntersectionResult 40 | object PolygonLineIntersectionResult { 41 | implicit def jtsToResult(geom:jts.Geometry):PolygonLineIntersectionResult = 42 | geom match { 43 | case p:jts.Point => PointResult(p) 44 | case l:jts.LineString => LineResult(l) 45 | case l:jts.MultiLineString => LineSetResult(l) 46 | case _ => NoResult 47 | } 48 | } 49 | 50 | abstract sealed trait PolygonPolygonIntersectionResult 51 | object PolygonPolygonIntersectionResult { 52 | implicit def jtsToResult(geom:jts.Geometry):PolygonPolygonIntersectionResult = 53 | geom match { 54 | case p:jts.Point => PointResult(p) 55 | case l:jts.LineString => LineResult(l) 56 | case p:jts.Polygon => PolygonResult(p) 57 | case ps:jts.MultiPoint => PointSetResult(ps) 58 | case ps:jts.MultiPolygon => PolygonSetResult(ps) 59 | case ls:jts.MultiLineString => LineSetResult(ls) 60 | case gc:jts.GeometryCollection => GeometryCollectionResult(gc) 61 | case _ => NoResult 62 | } 63 | } 64 | 65 | abstract sealed trait PointSetIntersectionResult 66 | object PointSetIntersectionResult { 67 | implicit def jtsToResult(geom:jts.Geometry):PointSetIntersectionResult = 68 | geom match { 69 | case p:jts.Point => if(p.isEmpty) NoResult else PointResult(p) 70 | case mp:jts.MultiPoint => PointSetResult(mp) 71 | case x => 72 | sys.error(s"Unexpected result for PointSet intersection: ${geom.getGeometryType}") 73 | } 74 | } 75 | 76 | abstract sealed trait LineSetIntersectionResult 77 | object LineSetIntersectionResult { 78 | implicit def jtsToResult(geom:jts.Geometry): LineSetIntersectionResult = 79 | geom match { 80 | case p: jts.Point => PointResult(p) 81 | case l: jts.LineString => if(l.isEmpty) NoResult else LineResult(l) 82 | case mp: jts.MultiPoint => PointSetResult(mp) 83 | case ml: jts.MultiLineString => LineSetResult(ml) 84 | case gc: jts.GeometryCollection => GeometryCollectionResult(gc) 85 | case x => 86 | sys.error(s"Unexpected result for LineSet intersection: ${geom.getGeometryType}") 87 | } 88 | } 89 | 90 | abstract sealed trait PolygonSetIntersectionResult 91 | object PolygonSetIntersectionResult { 92 | implicit def jtsToResult(geom:jts.Geometry): PolygonSetIntersectionResult = 93 | geom match { 94 | case p: jts.Point => PointResult(p) 95 | case l: jts.LineString => LineResult(l) 96 | case p: jts.Polygon => if(p.isEmpty) NoResult else PolygonResult(p) 97 | case mp: jts.MultiPoint => PointSetResult(mp) 98 | case ml: jts.MultiLineString => LineSetResult(ml) 99 | case mp: jts.MultiPolygon => PolygonSetResult(mp) 100 | case gc: jts.GeometryCollection => GeometryCollectionResult(gc) 101 | case _ => 102 | sys.error(s"Unexpected result for PolygonSet intersection: ${geom.getGeometryType}") 103 | } 104 | } 105 | 106 | // Union 107 | 108 | abstract sealed trait PointPointUnionResult 109 | object PointPointUnionResult { 110 | implicit def jtsToResult(geom: jts.Geometry): PointPointUnionResult = 111 | geom match { 112 | case l:jts.Point => PointResult(l) 113 | case gc:jts.MultiPoint => PointSetResult(gc) 114 | case _ => 115 | sys.error(s"Unexpected result for Point Point union: ${geom.getGeometryType}") 116 | } 117 | } 118 | 119 | abstract sealed trait LinePointUnionResult 120 | object LinePointUnionResult { 121 | implicit def jtsToResult(geom: jts.Geometry): LinePointUnionResult = 122 | geom match { 123 | case l:jts.LineString => LineResult(l) 124 | case gc:jts.GeometryCollection => GeometryCollectionResult(gc) 125 | case _ => 126 | sys.error(s"Unexpected result for Line Point union: ${geom.getGeometryType}") 127 | } 128 | } 129 | 130 | abstract sealed trait LineLineUnionResult 131 | object LineLineUnionResult { 132 | implicit def jtsToResult(geom: jts.Geometry): LineLineUnionResult = 133 | geom match { 134 | case l:jts.LineString => LineResult(l) 135 | case ml:jts.MultiLineString => LineSetResult(ml) 136 | case _ => 137 | sys.error(s"Unexpected result for Line Line union: ${geom.getGeometryType}") 138 | } 139 | } 140 | 141 | abstract sealed trait PolygonXUnionResult 142 | object PolygonXUnionResult { 143 | implicit def jtsToResult(geom:jts.Geometry): PolygonXUnionResult = 144 | geom match { 145 | case gc:jts.GeometryCollection => GeometryCollectionResult(gc) 146 | case p:jts.Polygon => PolygonResult(Polygon(p)) 147 | case _ => 148 | sys.error(s"Unexpected result for Polygon union: ${geom.getGeometryType}") 149 | } 150 | } 151 | 152 | abstract sealed trait PolygonPolygonUnionResult 153 | object PolygonPolygonUnionResult { 154 | implicit def jtsToResult(geom:jts.Geometry): PolygonPolygonUnionResult = 155 | geom match { 156 | case p:jts.Polygon => PolygonResult(p) 157 | case mp:jts.MultiPolygon => PolygonSetResult(mp) 158 | case _ => 159 | sys.error(s"Unexpected result for Polygon-Polygon union: ${geom.getGeometryType}") 160 | } 161 | } 162 | 163 | abstract sealed trait PolygonSetUnionResult 164 | object PolygonSetUnionResult { 165 | implicit def jtsToResult(geom:jts.Geometry): PolygonSetUnionResult = 166 | geom match { 167 | case p:jts.Polygon => PolygonResult(p) 168 | case mp:jts.MultiPolygon => PolygonSetResult(mp) 169 | case gc:jts.GeometryCollection => GeometryCollectionResult(gc) 170 | case _ => 171 | sys.error(s"Unexpected result for Polygon set union: ${geom.getGeometryType}") 172 | } 173 | } 174 | 175 | // Boundary 176 | 177 | abstract sealed trait LineBoundaryResult 178 | object LineBoundaryResult { 179 | implicit def jtsToResult(geom:jts.Geometry): LineBoundaryResult = 180 | geom match { 181 | case p:jts.Point => PointResult(p) 182 | case mp:jts.MultiPoint => PointSetResult(mp) 183 | case _ => NoResult 184 | } 185 | } 186 | 187 | case object NoResult 188 | extends Result 189 | with PointIntersectionResult 190 | with LineLineIntersectionResult 191 | with PolygonLineIntersectionResult 192 | with PolygonPolygonIntersectionResult 193 | with PointSetIntersectionResult 194 | with LineSetIntersectionResult 195 | with PolygonSetIntersectionResult 196 | with LineBoundaryResult 197 | 198 | case class PointResult(p:Point) 199 | extends Result 200 | with PointIntersectionResult 201 | with LineLineIntersectionResult 202 | with PolygonLineIntersectionResult 203 | with PolygonPolygonIntersectionResult 204 | with PointSetIntersectionResult 205 | with LineSetIntersectionResult 206 | with PolygonSetIntersectionResult 207 | with PointPointUnionResult 208 | with LineBoundaryResult 209 | 210 | case class LineResult(l:Line) 211 | extends Result 212 | with LineLineIntersectionResult 213 | with PolygonLineIntersectionResult 214 | with PolygonPolygonIntersectionResult 215 | with LineSetIntersectionResult 216 | with PolygonSetIntersectionResult 217 | with LinePointUnionResult 218 | with LineLineUnionResult 219 | 220 | case class PolygonResult(p:Polygon) 221 | extends Result 222 | with PolygonPolygonIntersectionResult 223 | with PolygonXUnionResult 224 | with PolygonPolygonUnionResult 225 | with PolygonSetIntersectionResult 226 | with PolygonSetUnionResult 227 | 228 | case class PointSetResult(ls:Set[Point]) 229 | extends Result 230 | with PolygonPolygonIntersectionResult 231 | with PointSetIntersectionResult 232 | with LineSetIntersectionResult 233 | with PolygonSetIntersectionResult 234 | with PointPointUnionResult 235 | with LineBoundaryResult 236 | 237 | case class LineSetResult(ls:Set[Line]) 238 | extends Result 239 | with PolygonLineIntersectionResult 240 | with PolygonPolygonIntersectionResult 241 | with LineSetIntersectionResult 242 | with PolygonSetIntersectionResult 243 | with LineLineUnionResult 244 | 245 | case class PolygonSetResult(ps:Set[Polygon]) 246 | extends Result 247 | with PolygonPolygonIntersectionResult 248 | with PolygonPolygonUnionResult 249 | with PolygonSetIntersectionResult 250 | with PolygonSetUnionResult 251 | 252 | case class GeometryCollectionResult(gc:GeometryCollection) 253 | extends Result 254 | with PolygonPolygonIntersectionResult 255 | with LineSetIntersectionResult 256 | with PolygonSetIntersectionResult 257 | with LinePointUnionResult 258 | with PolygonXUnionResult 259 | with PolygonSetUnionResult 260 | -------------------------------------------------------------------------------- /core/src/main/scala/jtscala/package.scala: -------------------------------------------------------------------------------- 1 | import com.vividsolutions.jts.{geom=>jts} 2 | 3 | import scala.collection.mutable 4 | 5 | package object jtscala { 6 | implicit def tupleToPoint(t:(Double,Double)):Point = 7 | Point(t._1,t._2) 8 | 9 | implicit def coordinateToPoint(c:jts.Coordinate):Point = 10 | Point(c.x,c.y) 11 | 12 | implicit def tupleSetToPointSet(ts:Set[(Double,Double)]):Set[Point] = 13 | ts map(t => Point(t._1,t._2)) 14 | 15 | implicit def tupleListToPointList(tl:List[(Double,Double)]):List[Point] = 16 | tl map(t => Point(t._1,t._2)) 17 | 18 | // implicit def tupleArrayToPointList(tl:Array[(Double,Double)]):List[Point] = 19 | // tl map(t => Point(t._1,t._2)) toList 20 | 21 | implicit def pointListToCoordinateArray(ps:List[Point]):Array[jts.Coordinate] = 22 | ps map(p => new jts.Coordinate(p.x,p.y)) toArray 23 | 24 | implicit def multiPointToSetPoint(mp:jts.MultiPoint):Set[Point] = { 25 | val len = mp.getNumGeometries 26 | (for(i <- 0 until len) yield { 27 | Point(mp.getGeometryN(i).asInstanceOf[jts.Point]) 28 | }).toSet 29 | } 30 | 31 | implicit def multiLineToSetLine(ml:jts.MultiLineString):Set[Line] = { 32 | val len = ml.getNumGeometries 33 | (for(i <- 0 until len) yield { 34 | Line(ml.getGeometryN(i).asInstanceOf[jts.LineString]) 35 | }).toSet 36 | } 37 | 38 | implicit def multiPolygonToSetPolygon(mp:jts.MultiPolygon):Set[Polygon] = { 39 | val len = mp.getNumGeometries 40 | (for(i <- 0 until len) yield { 41 | Polygon(mp.getGeometryN(i).asInstanceOf[jts.Polygon]) 42 | }).toSet 43 | } 44 | 45 | implicit def geometryCollectionToSetGeometry(gc:jts.GeometryCollection):Set[Geometry] = { 46 | val len = gc.getNumGeometries 47 | (for(i <- 0 until len) yield { 48 | gc.getGeometryN(i) match { 49 | case p:jts.Point => Set[Geometry](Point(p)) 50 | case mp:jts.MultiPoint => multiPointToSetPoint(mp) 51 | case l:jts.LineString => Set[Geometry](Line(l)) 52 | case ml:jts.MultiLineString => multiLineToSetLine(ml) 53 | case p:jts.Polygon => Set[Geometry](Polygon(p)) 54 | case mp:jts.MultiPolygon => multiPolygonToSetPolygon(mp) 55 | case gc:jts.GeometryCollection => geometryCollectionToSetGeometry(gc) 56 | } 57 | }).toSet.flatten 58 | } 59 | 60 | implicit def seqPointToPointSet(ps:Set[Point]):PointSet = PointSet(ps) 61 | implicit def seqLineToLineSet(ps:Set[Line]):LineSet = LineSet(ps) 62 | implicit def seqPolygonToPolygonSet(ps:Set[Polygon]):PolygonSet = PolygonSet(ps) 63 | 64 | implicit def seqGeometryToGeometryCollection(gs:Set[Geometry]):GeometryCollection = { 65 | val points = mutable.Set[Point]() 66 | val lines = mutable.Set[Line]() 67 | val polygons = mutable.Set[Polygon]() 68 | for(g <- gs) { 69 | g match { 70 | case p:Point => points += p 71 | case l:Line => lines += l 72 | case p:Polygon => polygons += p 73 | case _ => sys.error(s"Unknown Geometry type: $g") 74 | } 75 | } 76 | GeometryCollection(points.toSet,lines.toSet,polygons.toSet) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /core/src/test/scala/jtscala/check/Generators.scala: -------------------------------------------------------------------------------- 1 | package jtscala.check 2 | 3 | import jtscala._ 4 | 5 | import org.scalacheck._ 6 | import Prop._ 7 | import Gen._ 8 | import Arbitrary._ 9 | 10 | object Generators { 11 | lazy val genPoint: Gen[Point] = 12 | for { 13 | x <- choose(-99999999999999999999.0,99999999999999999999.0) 14 | y <- choose(-99999999999999999999.0,99999999999999999999.0) 15 | } yield Point(x, y) 16 | 17 | lazy val genLine:Gen[Line] = 18 | for { 19 | size <-Gen.choose(2,40) 20 | points <- Gen.containerOfN[Set,Point](size,genPoint) 21 | } yield Line(points.toList) 22 | 23 | // Doesn't yet deal with interior rings 24 | lazy val genPolygon:Gen[Polygon] = 25 | for { 26 | size <-Gen.choose(6,50) 27 | shareSize <- Gen.choose(3,size) 28 | subSize1 <- Gen.choose(3,size) 29 | subSize2 <- Gen.choose(3,size) 30 | fullSet <- Gen.containerOfN[Set,Point](size,genPoint) 31 | sharedSet <- Gen.pick(shareSize,fullSet).map(_.toSet) 32 | subSet1 <- Gen.pick(subSize1,fullSet).map(_.toSet) 33 | subSet2 <- Gen.pick(subSize2,fullSet).map(_.toSet) 34 | } yield { 35 | val polyOne = (subSet1 ++ sharedSet).convexHull 36 | val polyTwo = (subSet2 ++ sharedSet).convexHull 37 | polyOne | polyTwo match { 38 | case PolygonResult(p) => p 39 | case _ => sys.error("Should have resulted in a polygon.") 40 | } 41 | } 42 | 43 | implicit lazy val arbPoint: Arbitrary[Point] = 44 | Arbitrary(genPoint) 45 | 46 | implicit lazy val arbLine: Arbitrary[Line] = 47 | Arbitrary(genLine) 48 | 49 | implicit lazy val arbPolygon: Arbitrary[Polygon] = 50 | Arbitrary(genPolygon) 51 | } 52 | -------------------------------------------------------------------------------- /core/src/test/scala/jtscala/check/jts/Generators.scala: -------------------------------------------------------------------------------- 1 | package jtscala.check.jts 2 | 3 | import com.vividsolutions.jts.geom._ 4 | 5 | import org.scalacheck._ 6 | import Prop._ 7 | import Gen._ 8 | import Arbitrary._ 9 | 10 | object Generators { 11 | val factory = new GeometryFactory() 12 | 13 | lazy val genCoordinate: Gen[Coordinate] = 14 | for { 15 | x <- choose(-99999999999999999999.0,99999999999999999999.0) 16 | y <- choose(-99999999999999999999.0,99999999999999999999.0) 17 | } yield new Coordinate(x, y) 18 | 19 | lazy val genShortCoordinates: Gen[Coordinate] = 20 | for { 21 | x <- choose(-1.0,1.0) 22 | y <- choose(-1.0,1.0) 23 | } yield new Coordinate(x, y) 24 | 25 | lazy val genPoint:Gen[Point] = 26 | genCoordinate.map(factory.createPoint(_)) 27 | 28 | lazy val genMultiPoint:Gen[MultiPoint] = 29 | for { 30 | size <- Gen.choose(1,100) 31 | coords <- Gen.containerOfN[Set,Coordinate](size,genCoordinate) 32 | } yield { factory.createMultiPoint(coords.toArray) } 33 | 34 | lazy val genLongLineString:Gen[LineString] = 35 | for { 36 | size <-Gen.choose(2,40) 37 | s <- Gen.containerOfN[Set,Coordinate](size,genCoordinate) 38 | } yield { factory.createLineString(s.toArray) } 39 | 40 | lazy val genShortLineString:Gen[LineString] = 41 | for { 42 | size <-Gen.choose(2,40) 43 | s <- Gen.containerOfN[Set,Coordinate](size,genCoordinate) 44 | } yield { factory.createLineString(s.toArray) } 45 | 46 | lazy val genLinearRing:Gen[LinearRing] = 47 | genPolygon.map { p => 48 | factory.createLinearRing(p.getExteriorRing.getCoordinates)//.asInstanceOf[LinearRing] 49 | } 50 | 51 | lazy val genLineString:Gen[LineString] = 52 | Gen.frequency((1,genLongLineString),(1,genShortLineString),(1,genLinearRing)) 53 | 54 | lazy val genMultiLineString:Gen[MultiLineString] = 55 | for { 56 | size <- Gen.choose(1,20) 57 | lineStrings <- Gen.containerOfN[Set,LineString](size,genLineString) 58 | } yield { factory.createMultiLineString(lineStrings.toArray) } 59 | 60 | // Doesn't yet deal with interior rings 61 | lazy val genPolygon:Gen[Polygon] = 62 | for { 63 | size <-Gen.choose(6,50) 64 | shareSize <- Gen.choose(3,size) 65 | subSize1 <- Gen.choose(3,size) 66 | subSize2 <- Gen.choose(3,size) 67 | fullSet <- Gen.containerOfN[Set,Coordinate](size,genCoordinate) 68 | sharedSet <- Gen.pick(shareSize,fullSet) 69 | subSet1 <- Gen.pick(subSize1,fullSet) 70 | subSet2 <- Gen.pick(subSize2,fullSet) 71 | } yield { 72 | val polyOne = 73 | factory.createMultiPoint((subSet1 ++ sharedSet).toArray).convexHull.asInstanceOf[Polygon] 74 | val polyTwo = 75 | factory.createMultiPoint((subSet2 ++ sharedSet).toArray).convexHull.asInstanceOf[Polygon] 76 | polyOne.intersection(polyTwo).asInstanceOf[Polygon] 77 | } 78 | 79 | implicit lazy val arbCoordinate: Arbitrary[Coordinate] = 80 | Arbitrary(genCoordinate) 81 | 82 | implicit lazy val arbPoint: Arbitrary[Point] = 83 | Arbitrary(genPoint) 84 | 85 | implicit lazy val arbMultiPoint: Arbitrary[MultiPoint] = 86 | Arbitrary(genMultiPoint) 87 | 88 | implicit lazy val arbLineString: Arbitrary[LineString] = 89 | Arbitrary(genLineString) 90 | 91 | implicit lazy val arbMultiLineString: Arbitrary[MultiLineString] = 92 | Arbitrary(genMultiLineString) 93 | 94 | implicit lazy val arbPolygon: Arbitrary[Polygon] = 95 | Arbitrary(genPolygon) 96 | 97 | // MultiPoint with a set of lines arbitrarily made up of the points 98 | // of the MultiPoint 99 | case class LineInMultiPoint(mp:MultiPoint,ls:LineString) 100 | lazy val genLineInMultiPoint:Gen[LineInMultiPoint] = 101 | for { 102 | size <- Gen.choose(2,100) 103 | lineSize <- Gen.choose(2,size) 104 | coords <- Gen.containerOfN[Set,Coordinate](size,genCoordinate) 105 | lineCoords <- Gen.pick(lineSize,coords) 106 | } yield { 107 | val mp = factory.createMultiPoint(coords.toArray) 108 | val l = factory.createLineString(lineCoords.toArray) 109 | LineInMultiPoint(mp,l) 110 | } 111 | 112 | implicit lazy val arbLineInMultiPoint:Arbitrary[LineInMultiPoint] = 113 | Arbitrary(genLineInMultiPoint) 114 | 115 | case class ClosedLineString(ls:LineString) 116 | lazy val genClosedLineString:Gen[ClosedLineString] = 117 | Gen.frequency((1,genLongLineString),(1,genShortLineString)) 118 | .map { l => 119 | val coords = l.getCoordinates 120 | ClosedLineString(factory.createLineString((coords(coords.length-1) ::coords.toList).toArray)) 121 | } 122 | 123 | implicit lazy val arbClosedRing:Arbitrary[ClosedLineString] = 124 | Arbitrary(genClosedLineString) 125 | 126 | } 127 | -------------------------------------------------------------------------------- /core/src/test/scala/jtscala/check/jts/LineStringCheck.scala: -------------------------------------------------------------------------------- 1 | package jtscala.check.jts 2 | 3 | import com.vividsolutions.jts.geom._ 4 | 5 | import org.scalacheck._ 6 | import Prop._ 7 | 8 | object LineStringCheck extends Properties("Line") { 9 | import Generators._ 10 | 11 | // SLOW! 12 | // property("buffer => Polygon") = 13 | // forAll { (l:LineString,d:Double) => 14 | // l.buffer(d) match { 15 | // case _:Polygon => true 16 | // case x => 17 | // println(s"FAILED WITH $x") 18 | // false 19 | // } 20 | // } 21 | 22 | property("difference[self]") = forAll { (l:LineString) => 23 | l.difference(l).isEmpty 24 | } 25 | 26 | property("difference[self] => Empty") = 27 | forAll { (l:LineString) => 28 | l.difference(l).isEmpty 29 | } 30 | 31 | property("difference[point] => (MultiLineString,LineString)") = 32 | forAll { (l:LineString,p:Point) => 33 | l.difference(p) match { 34 | case _:MultiLineString => true 35 | case _:LineString => true 36 | case x => 37 | println(s"FAILED WITH $x") 38 | false 39 | } 40 | } 41 | 42 | property("difference[other line] => (MultiLineString,LineString)") = 43 | forAll { (l:LineString,l2:LineString) => 44 | l.difference(l2) match { 45 | case _:MultiLineString => true 46 | case _:LineString => true 47 | case x => 48 | println(s"FAILED WITH $x") 49 | false 50 | } 51 | } 52 | 53 | property("difference[polygon] => (MultiLineString,LineString,Empty)") = 54 | forAll { (l:LineString,p:Polygon) => 55 | l.difference(p) match { 56 | case _:MultiLineString => true 57 | case _:LineString => true 58 | case x => 59 | if(x.isEmpty) { 60 | p.contains(l) 61 | } else { 62 | println(s"FAILED WITH $x") 63 | false 64 | } 65 | } 66 | } 67 | 68 | property("intersection[point] => Point") = 69 | forAll { (l:LineString,p:Point) => 70 | l.intersection(p) match { 71 | case _:Point => true 72 | case x => 73 | if(x.isEmpty) { 74 | l.disjoint(p) 75 | } else { 76 | println(s"FAILED WITH $x") 77 | false 78 | } 79 | } 80 | } 81 | 82 | property("intersection[line] => (Point,LineString)") = 83 | forAll { (l:LineString,p:Point) => 84 | l.intersection(p) match { 85 | case _:Point => true 86 | case _:LineString => true 87 | case x => 88 | if(x.isEmpty) { 89 | l.disjoint(p) 90 | } else { 91 | println(s"FAILED WITH $x") 92 | false 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /core/src/test/scala/jtscala/check/jts/MultiLineStringCheck.scala: -------------------------------------------------------------------------------- 1 | package jtscala.check.jts 2 | 3 | import com.vividsolutions.jts.geom._ 4 | 5 | import org.scalacheck._ 6 | import Prop._ 7 | import Arbitrary._ 8 | 9 | import java.lang.System.currentTimeMillis 10 | import scala.collection.mutable 11 | 12 | object MultiLineStringCheck extends Properties("MultiLineString") { 13 | import Generators._ 14 | 15 | // property("buffer => EMPTY") = 16 | // forAll { (mp: MultiLineString) => 17 | // mp.buffer(1.0).isEmpty 18 | // } 19 | 20 | property("intersection[point] => (Point)") = 21 | forAll { (ml: MultiLineString,p:Point) => 22 | ml.intersection(p) match { 23 | case _:Point => true 24 | case x => 25 | println(s"FAILED WITH $x") 26 | false 27 | } 28 | } 29 | 30 | property("intersection[line] => (Point,LineString,MultiLineString,GeometryCollection)") = 31 | forAll { (ml: MultiLineString,l: LineString) => 32 | ml.intersection(l) match { 33 | case p: Point => !p.isEmpty 34 | case l: LineString => if(l.isEmpty) !ml.intersects(l) else true 35 | case mp: MultiPoint => !mp.isEmpty 36 | case ml: MultiLineString => !ml.isEmpty 37 | case x => 38 | println(s"FAILED WITH $x") 39 | false 40 | } 41 | } 42 | 43 | // property("intersection[lineOfPoints] => (MultiLineString)") = forAll { (limp: LineInMultiLineString) => 44 | // val LineInMultiLineString(mp,l) = limp 45 | // mp.intersection(l) match { 46 | // case _:MultiLineString => true 47 | // case x => 48 | // println(s"FAILED WITH $x") 49 | // false 50 | // } 51 | // } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/test/scala/jtscala/check/jts/MultiPointCheck.scala: -------------------------------------------------------------------------------- 1 | package jtscala.check.jts 2 | 3 | import com.vividsolutions.jts.geom._ 4 | 5 | import org.scalacheck._ 6 | import Prop._ 7 | import Arbitrary._ 8 | 9 | object MultiPointCheck extends Properties("MultiPoint") { 10 | import Generators._ 11 | 12 | property("buffer => EMPTY") = 13 | forAll { (mp: MultiPoint) => 14 | mp.buffer(1.0).isEmpty 15 | } 16 | 17 | property("intersection[line] => (Point,MultiPoint)") = 18 | forAll { (mp: MultiPoint,l:LineString) => 19 | mp.intersection(l) match { 20 | case _:Point => true 21 | case _:MultiPoint => true 22 | case x => 23 | println(s"FAILED WITH $x") 24 | false 25 | } 26 | } 27 | 28 | property("intersection[lineOfPoints] => (MultiPoint)") = forAll { (limp: LineInMultiPoint) => 29 | val LineInMultiPoint(mp,l) = limp 30 | mp.intersection(l) match { 31 | case _:MultiPoint => true 32 | case x => 33 | println(s"FAILED WITH $x") 34 | false 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/test/scala/jtscala/check/jts/PointCheck.scala: -------------------------------------------------------------------------------- 1 | package jtscala.check.jts 2 | 3 | import com.vividsolutions.jts.geom._ 4 | 5 | import org.scalacheck._ 6 | import Prop._ 7 | 8 | object PointCheck extends Properties("Point") { 9 | import Generators._ 10 | 11 | property("getEnvelope") = forAll { (p: Point) => 12 | p.getEnvelope match { 13 | case x:Point => true 14 | case _ => false 15 | } 16 | } 17 | 18 | property("within[itself]") = forAll { (p: Point) => 19 | p.within(p) 20 | } 21 | 22 | property("contains[itself]") = forAll { (p: Point) => 23 | p.contains(p) 24 | } 25 | 26 | property("buffer") = forAll { (p: Point, d: Double) => 27 | p.buffer(d) match { 28 | case x:Polygon => true 29 | case _ => false 30 | } 31 | } 32 | 33 | property("convexHull") = forAll { (p: Point) => 34 | p.convexHull match { 35 | case x:Point => true 36 | case _ => false 37 | } 38 | } 39 | 40 | property("covers[itself]") = forAll { (p: Point) => 41 | p.covers(p) 42 | } 43 | 44 | property("covers[others]") = forAll { (p1: Point,p2: Point) => 45 | !p1.covers(p2) || (p1 == p2) 46 | } 47 | 48 | property("getInteriorPoint") = forAll { (p:Point) => 49 | p.getInteriorPoint == p 50 | } 51 | 52 | property("getDimension") = forAll { (p:Point) => 53 | p.getDimension == 0 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /core/src/test/scala/jtscala/check/jts/PolygonCheck.scala: -------------------------------------------------------------------------------- 1 | package jtscala.check.jts 2 | 3 | import com.vividsolutions.jts.geom._ 4 | 5 | import org.scalacheck._ 6 | import Prop._ 7 | 8 | object PolygonCheck extends Properties("Polygon") { 9 | import Generators._ 10 | 11 | property("union[polygon] => (Polygon,Multipolygon)") = forAll { (p1:Polygon,p2:Polygon) => 12 | p1.union(p2) match { 13 | case _:MultiPolygon => true 14 | case _:Polygon => true 15 | case x => 16 | println(s"FAILED WITH $x") 17 | false 18 | } 19 | } 20 | 21 | property("union[line] => (Polygon,GeometryCollection)") = forAll { (p:Polygon,l:LineString) => 22 | p.union(l) match { 23 | case _:GeometryCollection => true 24 | case _:Polygon => true 25 | case x => 26 | println(s"FAILED WITH $x") 27 | false 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/test/scala/jtscala/spec/LineSpec.scala: -------------------------------------------------------------------------------- 1 | package jtscala.spec 2 | 3 | import jtscala._ 4 | 5 | import com.vividsolutions.jts.{geom=>jts} 6 | 7 | import org.scalatest.FunSpec 8 | import org.scalatest.matchers._ 9 | 10 | class LineSpec extends FunSpec with ShouldMatchers { 11 | describe("Line") { 12 | it("should be a closed Line if constructed with l(0) == l(-1)") { 13 | val l = Line(List[(Double,Double)]((0,0),(1,0),(1,1),(0,1),(0,0))) 14 | l.isClosed should be (true) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/test/scala/jtscala/spec/PointSpec.scala: -------------------------------------------------------------------------------- 1 | package jtscala.spec 2 | 3 | import jtscala._ 4 | 5 | import org.scalatest.FunSpec 6 | import org.scalatest.matchers._ 7 | 8 | class PointSpec extends FunSpec with ShouldMatchers { 9 | describe("Point") { 10 | it("should return true for comparing points with equal x and y") { 11 | val x = 123.321 12 | val y = -0.4343434 13 | Point(x,y) should be (Point(x,y)) 14 | } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /core/src/test/scala/jtscala/spec/PolygonSpec.scala: -------------------------------------------------------------------------------- 1 | package jtscala.spec 2 | 3 | import jtscala._ 4 | 5 | import com.vividsolutions.jts.{geom=>jts} 6 | 7 | import org.scalatest.FunSpec 8 | import org.scalatest.matchers._ 9 | 10 | class PolygonSpec extends FunSpec with ShouldMatchers { 11 | describe("Polygon") { 12 | it("should be a closed Polygon if constructed with l(0) == l(-1)") { 13 | val p = Polygon(Line(List[(Double,Double)]((0,0),(1,0),(1,1),(0,1),(0,0)))) 14 | p.exterior.isClosed should be (true) 15 | } 16 | 17 | it("should throw if attempt to construct with unclosed line") { 18 | intercept[Exception] { 19 | val p = Polygon(Line(List[(Double,Double)]((0,0),(1,0),(1,1),(0,1)))) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /project/Build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import sbt.Keys._ 3 | 4 | object JTScalaBuild extends Build { 5 | 6 | // Default settings 7 | override lazy val settings = super.settings ++ 8 | Seq( 9 | version := "0.1.0-SNAPSHOT", 10 | scalaVersion := "2.10.3", 11 | organization := "com.azavea.geotrellis", 12 | 13 | // disable annoying warnings about 2.10.x 14 | conflictWarning in ThisBuild := ConflictWarning.disable, 15 | scalacOptions ++= 16 | Seq("-deprecation", 17 | "-unchecked", 18 | "-Yclosure-elim", 19 | "-Yinline-warnings", 20 | "-optimize", 21 | "-language:implicitConversions", 22 | "-language:postfixOps", 23 | "-language:existentials", 24 | "-feature"), 25 | 26 | publishMavenStyle := true, 27 | 28 | publishTo <<= version { (v: String) => 29 | val nexus = "https://oss.sonatype.org/" 30 | if (v.trim.endsWith("SNAPSHOT")) 31 | Some("snapshots" at nexus + "content/repositories/snapshots") 32 | else 33 | Some("releases" at nexus + "service/local/staging/deploy/maven2") 34 | }, 35 | 36 | publishArtifact in Test := false, 37 | 38 | pomIncludeRepository := { _ => false }, 39 | licenses := Seq("Apache 2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0.html")), 40 | homepage := Some(url("http://geotrellis.github.io/")), 41 | 42 | pomExtra := ( 43 | 44 | 45 | git@github.com:geotrellis/geotrellis.git 46 | scm:git:git@github.com:geotrellis/geotrellis.git 47 | 48 | 49 | 50 | lossyrob 51 | Rob Emanuele 52 | http://github.com/lossyrob/ 53 | 54 | 55 | ) 56 | ) 57 | 58 | // Project: jtscala 59 | 60 | lazy val jtscala = 61 | Project("jtscala", file("core")) 62 | .settings(jtscalaSettings:_*) 63 | 64 | lazy val jtscalaSettings = 65 | Seq( 66 | name := "jtscala", 67 | libraryDependencies ++= Seq( 68 | "org.scalatest" % "scalatest_2.10" % "2.0.M5b" % "test", 69 | "org.scalacheck" %% "scalacheck" % "1.11.1" % "test", 70 | "com.vividsolutions" % "jts" % "1.13" 71 | ) 72 | ) 73 | 74 | // Project: benchmark 75 | 76 | lazy val jtscala_benchmark = 77 | Project("benchmark", file("benchmark")) 78 | .settings(jtscalaBenchmarkSettings:_*) 79 | .dependsOn(jtscala % "compile->test") 80 | 81 | lazy val jtscalaBenchmarkSettings = 82 | Seq( 83 | name := "jtscala-benchmark", 84 | libraryDependencies ++= Seq( 85 | "org.scalatest" % "scalatest_2.10" % "2.0.M5b" % "test", 86 | "org.scalacheck" %% "scalacheck" % "1.11.1" % "test", 87 | "com.vividsolutions" % "jts" % "1.13" 88 | ) 89 | ) 90 | } 91 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.12.0 -------------------------------------------------------------------------------- /sbt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # A more capable sbt runner, coincidentally also called sbt. 4 | # Author: Paul Phillips 5 | 6 | # todo - make this dynamic 7 | declare -r sbt_release_version=0.13.0 8 | 9 | declare sbt_jar sbt_dir sbt_create sbt_launch_dir 10 | declare scala_version java_home sbt_explicit_version 11 | declare verbose debug quiet noshare batch trace_level log_level 12 | declare sbt_saved_stty 13 | 14 | echoerr () { [[ -z $quiet ]] && echo "$@" >&2; } 15 | vlog () { [[ -n "$verbose$debug" ]] && echoerr "$@"; } 16 | dlog () { [[ -n $debug ]] && echoerr "$@"; } 17 | 18 | # we'd like these set before we get around to properly processing arguments 19 | for arg in "$@"; do 20 | case $arg in 21 | -q|-quiet) quiet=true ;; 22 | -d|-debug) debug=true ;; 23 | -v|-verbose) verbose=true ;; 24 | *) ;; 25 | esac 26 | done 27 | 28 | build_props_sbt () { 29 | if [[ -r project/build.properties ]]; then 30 | versionLine=$(grep ^sbt.version project/build.properties | tr -d ' \r') 31 | versionString=${versionLine##sbt.version=} 32 | echo "$versionString" 33 | fi 34 | } 35 | 36 | update_build_props_sbt () { 37 | local ver="$1" 38 | local old=$(build_props_sbt) 39 | 40 | if [[ $ver == $old ]]; then 41 | return 42 | elif [[ -r project/build.properties ]]; then 43 | perl -pi -e "s/^sbt\.version[ ]*=.*\$/sbt.version=${ver}/" project/build.properties 44 | grep -q '^sbt.version[ ]*=' project/build.properties || printf "\nsbt.version=${ver}\n" >> project/build.properties 45 | 46 | echoerr !!! 47 | echoerr !!! Updated file project/build.properties setting sbt.version to: $ver 48 | echoerr !!! Previous value was: $old 49 | echoerr !!! 50 | fi 51 | } 52 | 53 | sbt_version () { 54 | if [[ -n $sbt_explicit_version ]]; then 55 | echo $sbt_explicit_version 56 | else 57 | local v=$(build_props_sbt) 58 | if [[ -n $v ]]; then 59 | echo $v 60 | else 61 | echo $sbt_release_version 62 | fi 63 | fi 64 | } 65 | 66 | # restore stty settings (echo in particular) 67 | onSbtRunnerExit() { 68 | [[ -n $sbt_saved_stty ]] || return 69 | dlog "" 70 | dlog "restoring stty: $sbt_saved_stty" 71 | stty $sbt_saved_stty 72 | unset sbt_saved_stty 73 | } 74 | 75 | # save stty and trap exit, to ensure echo is reenabled if we are interrupted. 76 | trap onSbtRunnerExit EXIT 77 | sbt_saved_stty=$(stty -g 2>/dev/null) 78 | dlog "Saved stty: $sbt_saved_stty" 79 | 80 | # this seems to cover the bases on OSX, and someone will 81 | # have to tell me about the others. 82 | get_script_path () { 83 | local path="$1" 84 | [[ -L "$path" ]] || { echo "$path" ; return; } 85 | 86 | local target=$(readlink "$path") 87 | if [[ "${target:0:1}" == "/" ]]; then 88 | echo "$target" 89 | else 90 | echo "$(dirname $path)/$target" 91 | fi 92 | } 93 | 94 | die() { 95 | echo "Aborting: $@" 96 | exit 1 97 | } 98 | 99 | make_url () { 100 | version="$1" 101 | 102 | echo "$sbt_launch_repo/org.scala-sbt/sbt-launch/$version/sbt-launch.jar" 103 | } 104 | 105 | readarr () { 106 | while read ; do 107 | eval "$1+=(\"$REPLY\")" 108 | done 109 | } 110 | 111 | init_default_option_file () { 112 | local overriding_var=${!1} 113 | local default_file=$2 114 | if [[ ! -r "$default_file" && $overriding_var =~ ^@(.*)$ ]]; then 115 | local envvar_file=${BASH_REMATCH[1]} 116 | if [[ -r $envvar_file ]]; then 117 | default_file=$envvar_file 118 | fi 119 | fi 120 | echo $default_file 121 | } 122 | 123 | declare -r cms_opts="-XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC" 124 | declare -r jit_opts="-XX:ReservedCodeCacheSize=256m -XX:+TieredCompilation" 125 | declare -r default_jvm_opts="-Dfile.encoding=UTF8 -XX:MaxPermSize=384m -Xms512m -Xmx1536m -Xss2m $jit_opts $cms_opts" 126 | declare -r noshare_opts="-Dsbt.global.base=project/.sbtboot -Dsbt.boot.directory=project/.boot -Dsbt.ivy.home=project/.ivy" 127 | declare -r latest_28="2.8.2" 128 | declare -r latest_29="2.9.3" 129 | declare -r latest_210="2.10.3" 130 | declare -r latest_211="2.11.0-M5" 131 | 132 | declare -r script_path=$(get_script_path "$BASH_SOURCE") 133 | declare -r script_dir="$(dirname $script_path)" 134 | declare -r script_name="$(basename $script_path)" 135 | 136 | # some non-read-onlies set with defaults 137 | declare java_cmd=java 138 | declare sbt_opts_file=$(init_default_option_file SBT_OPTS .sbtopts) 139 | declare jvm_opts_file=$(init_default_option_file JVM_OPTS .jvmopts) 140 | declare sbt_launch_repo="http://typesafe.artifactoryonline.com/typesafe/ivy-releases" 141 | 142 | # pull -J and -D options to give to java. 143 | declare -a residual_args 144 | declare -a java_args 145 | declare -a scalac_args 146 | declare -a sbt_commands 147 | 148 | # args to jvm/sbt via files or environment variables 149 | declare -a extra_jvm_opts extra_sbt_opts 150 | 151 | # if set, use JAVA_HOME over java found in path 152 | [[ -e "$JAVA_HOME/bin/java" ]] && java_cmd="$JAVA_HOME/bin/java" 153 | 154 | # directory to store sbt launchers 155 | declare sbt_launch_dir="$HOME/.sbt/launchers" 156 | [[ -d "$sbt_launch_dir" ]] || mkdir -p "$sbt_launch_dir" 157 | [[ -w "$sbt_launch_dir" ]] || sbt_launch_dir="$(mktemp -d -t sbt_extras_launchers)" 158 | 159 | build_props_scala () { 160 | if [[ -r project/build.properties ]]; then 161 | versionLine=$(grep ^build.scala.versions project/build.properties) 162 | versionString=${versionLine##build.scala.versions=} 163 | echo ${versionString%% .*} 164 | fi 165 | } 166 | 167 | execRunner () { 168 | # print the arguments one to a line, quoting any containing spaces 169 | [[ $verbose || $debug ]] && echo "# Executing command line:" && { 170 | for arg; do 171 | if [[ -n "$arg" ]]; then 172 | if printf "%s\n" "$arg" | grep -q ' '; then 173 | printf "\"%s\"\n" "$arg" 174 | else 175 | printf "%s\n" "$arg" 176 | fi 177 | fi 178 | done 179 | echo "" 180 | } 181 | 182 | if [[ -n $batch ]]; then 183 | exec /dev/null; then 212 | curl --fail --silent "$url" --output "$jar" 213 | elif which wget >/dev/null; then 214 | wget --quiet -O "$jar" "$url" 215 | fi 216 | } && [[ -r "$jar" ]] 217 | } 218 | 219 | acquire_sbt_jar () { 220 | for_sbt_version="$(sbt_version)" 221 | sbt_url="$(jar_url $for_sbt_version)" 222 | sbt_jar="$(jar_file $for_sbt_version)" 223 | 224 | [[ -r "$sbt_jar" ]] || download_url "$sbt_url" "$sbt_jar" 225 | } 226 | 227 | usage () { 228 | cat < display stack traces with a max of frames (default: -1, traces suppressed) 236 | -no-colors disable ANSI color codes 237 | -sbt-create start sbt even if current directory contains no sbt project 238 | -sbt-dir path to global settings/plugins directory (default: ~/.sbt/) 239 | -sbt-boot path to shared boot directory (default: ~/.sbt/boot in 0.11+) 240 | -ivy path to local Ivy repository (default: ~/.ivy2) 241 | -no-share use all local caches; no sharing 242 | -offline put sbt in offline mode 243 | -jvm-debug Turn on JVM debugging, open at the given port. 244 | -batch Disable interactive mode 245 | -prompt Set the sbt prompt; in expr, 's' is the State and 'e' is Extracted 246 | 247 | # sbt version (default: from project/build.properties if present, else latest release) 248 | !!! The only way to accomplish this pre-0.12.0 if there is a build.properties file which 249 | !!! contains an sbt.version property is to update the file on disk. That's what this does. 250 | -sbt-version use the specified version of sbt (default: $sbt_release_version) 251 | -sbt-jar use the specified jar as the sbt launcher 252 | -sbt-launch-dir directory to hold sbt launchers (default: $sbt_launch_dir) 253 | -sbt-launch-repo repo url for downloading sbt launcher jar (default: $sbt_launch_repo) 254 | 255 | # scala version (default: as chosen by sbt) 256 | -28 use $latest_28 257 | -29 use $latest_29 258 | -210 use $latest_210 259 | -211 use $latest_211 260 | -scala-home use the scala build at the specified directory 261 | -scala-version use the specified version of scala 262 | -binary-version use the specified scala version when searching for dependencies 263 | 264 | # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) 265 | -java-home alternate JAVA_HOME 266 | 267 | # passing options to the jvm - note it does NOT use JAVA_OPTS due to pollution 268 | # The default set is used if JVM_OPTS is unset and no -jvm-opts file is found 269 | $default_jvm_opts 270 | JVM_OPTS environment variable holding either the jvm args directly, or 271 | the reference to a file containing jvm args if given path is prepended by '@' (e.g. '@/etc/jvmopts') 272 | Note: "@"-file is overridden by local '.jvmopts' or '-jvm-opts' argument. 273 | -jvm-opts file containing jvm args (if not given, .jvmopts in project root is used if present) 274 | -Dkey=val pass -Dkey=val directly to the jvm 275 | -J-X pass option -X directly to the jvm (-J is stripped) 276 | 277 | # passing options to sbt, OR to this runner 278 | SBT_OPTS environment variable holding either the sbt args directly, or 279 | the reference to a file containing sbt args if given path is prepended by '@' (e.g. '@/etc/sbtopts') 280 | Note: "@"-file is overridden by local '.sbtopts' or '-sbt-opts' argument. 281 | -sbt-opts file containing sbt args (if not given, .sbtopts in project root is used if present) 282 | -S-X add -X to sbt's scalacOptions (-S is stripped) 283 | EOM 284 | } 285 | 286 | addJava () { 287 | dlog "[addJava] arg = '$1'" 288 | java_args=( "${java_args[@]}" "$1" ) 289 | } 290 | addSbt () { 291 | dlog "[addSbt] arg = '$1'" 292 | sbt_commands=( "${sbt_commands[@]}" "$1" ) 293 | } 294 | addScalac () { 295 | dlog "[addScalac] arg = '$1'" 296 | scalac_args=( "${scalac_args[@]}" "$1" ) 297 | } 298 | addResidual () { 299 | dlog "[residual] arg = '$1'" 300 | residual_args=( "${residual_args[@]}" "$1" ) 301 | } 302 | addResolver () { 303 | addSbt "set resolvers += $1" 304 | } 305 | addDebugger () { 306 | addJava "-Xdebug" 307 | addJava "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" 308 | } 309 | setScalaVersion () { 310 | [[ "$1" == *-SNAPSHOT ]] && addResolver 'Resolver.sonatypeRepo("snapshots")' 311 | addSbt "++ $1" 312 | } 313 | 314 | process_args () 315 | { 316 | require_arg () { 317 | local type="$1" 318 | local opt="$2" 319 | local arg="$3" 320 | 321 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then 322 | die "$opt requires <$type> argument" 323 | fi 324 | } 325 | while [[ $# -gt 0 ]]; do 326 | case "$1" in 327 | -h|-help) usage; exit 1 ;; 328 | -v|-verbose) verbose=true && log_level=Info && shift ;; 329 | -d|-debug) debug=true && log_level=Debug && shift ;; 330 | -q|-quiet) quiet=true && log_level=Error && shift ;; 331 | 332 | -trace) require_arg integer "$1" "$2" && trace_level=$2 && shift 2 ;; 333 | -ivy) require_arg path "$1" "$2" && addJava "-Dsbt.ivy.home=$2" && shift 2 ;; 334 | -no-colors) addJava "-Dsbt.log.noformat=true" && shift ;; 335 | -no-share) noshare=true && shift ;; 336 | -sbt-boot) require_arg path "$1" "$2" && addJava "-Dsbt.boot.directory=$2" && shift 2 ;; 337 | -sbt-dir) require_arg path "$1" "$2" && sbt_dir="$2" && shift 2 ;; 338 | -debug-inc) addJava "-Dxsbt.inc.debug=true" && shift ;; 339 | -offline) addSbt "set offline := true" && shift ;; 340 | -jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;; 341 | -batch) batch=true && shift ;; 342 | -prompt) require_arg "expr" "$1" "$2" && addSbt "set shellPrompt in ThisBuild := (s => { val e = Project.extract(s) ; $2 })" && shift 2 ;; 343 | 344 | -sbt-create) sbt_create=true && shift ;; 345 | -sbt-jar) require_arg path "$1" "$2" && sbt_jar="$2" && shift 2 ;; 346 | -sbt-version) require_arg version "$1" "$2" && sbt_explicit_version="$2" && shift 2 ;; 347 | -sbt-launch-dir) require_arg path "$1" "$2" && sbt_launch_dir="$2" && shift 2 ;; 348 | -sbt-launch-repo) require_arg path "$1" "$2" && sbt_launch_repo="$2" && shift 2 ;; 349 | -scala-version) require_arg version "$1" "$2" && setScalaVersion "$2" && shift 2 ;; 350 | -binary-version) require_arg version "$1" "$2" && addSbt "set scalaBinaryVersion in ThisBuild := \"$2\"" && shift 2 ;; 351 | -scala-home) require_arg path "$1" "$2" && addSbt "set every scalaHome := Some(file(\"$2\"))" && shift 2 ;; 352 | -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;; 353 | -sbt-opts) require_arg path "$1" "$2" && sbt_opts_file="$2" && shift 2 ;; 354 | -jvm-opts) require_arg path "$1" "$2" && jvm_opts_file="$2" && shift 2 ;; 355 | 356 | -D*) addJava "$1" && shift ;; 357 | -J*) addJava "${1:2}" && shift ;; 358 | -S*) addScalac "${1:2}" && shift ;; 359 | -28) setScalaVersion $latest_28 && shift ;; 360 | -29) setScalaVersion $latest_29 && shift ;; 361 | -210) setScalaVersion $latest_210 && shift ;; 362 | -211) setScalaVersion $latest_211 && shift ;; 363 | 364 | *) addResidual "$1" && shift ;; 365 | esac 366 | done 367 | } 368 | 369 | # process the direct command line arguments 370 | process_args "$@" 371 | 372 | # skip #-styled comments 373 | readConfigFile() { 374 | while read line; do echo ${line/\#*/} | grep -vE '^\s*$'; done < $1 375 | } 376 | 377 | # if there are file/environment sbt_opts, process again so we 378 | # can supply args to this runner 379 | if [[ -r "$sbt_opts_file" ]]; then 380 | vlog "Using sbt options defined in file $sbt_opts_file" 381 | readarr extra_sbt_opts < <(readConfigFile "$sbt_opts_file") 382 | elif [[ -n "$SBT_OPTS" && !($SBT_OPTS =~ ^@.*) ]]; then 383 | vlog "Using sbt options defined in variable \$SBT_OPTS" 384 | extra_sbt_opts=( $SBT_OPTS ) 385 | else 386 | vlog "No extra sbt options have been defined" 387 | fi 388 | 389 | [[ -n $extra_sbt_opts ]] && process_args "${extra_sbt_opts[@]}" 390 | 391 | # reset "$@" to the residual args 392 | set -- "${residual_args[@]}" 393 | argumentCount=$# 394 | 395 | # only exists in 0.12+ 396 | setTraceLevel() { 397 | case $(sbt_version) in 398 | 0.{7,10,11}.*) echoerr "Cannot set trace level in sbt version $(sbt_version)" ;; 399 | *) addSbt "set every traceLevel := $trace_level" ;; 400 | esac 401 | } 402 | 403 | # set scalacOptions if we were given any -S opts 404 | [[ ${#scalac_args[@]} -eq 0 ]] || addSbt "set scalacOptions in ThisBuild += \"${scalac_args[@]}\"" 405 | 406 | # Update build.properties on disk to set explicit version - sbt gives us no choice 407 | [[ -n "$sbt_explicit_version" ]] && update_build_props_sbt "$sbt_explicit_version" 408 | vlog "Detected sbt version $(sbt_version)" 409 | 410 | [[ -n "$scala_version" ]] && echoerr "Overriding scala version to $scala_version" 411 | 412 | # no args - alert them there's stuff in here 413 | (( $argumentCount > 0 )) || { 414 | vlog "Starting $script_name: invoke with -help for other options" 415 | residual_args=( shell ) 416 | } 417 | 418 | # verify this is an sbt dir or -create was given 419 | [[ -r ./build.sbt || -d ./project || -n "$sbt_create" ]] || { 420 | cat <