Skip to content
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

ScalaJS support #11

Merged
merged 7 commits into from
Jun 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 49 additions & 9 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@

import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}


lazy val commonSettings = Seq(
organization := "com.monsanto.labs",

Expand Down Expand Up @@ -34,31 +37,68 @@ lazy val scalacVersionOptions: Map[String, Seq[String]] = Map(
"2.12" -> Seq.empty
)

lazy val breeze = "0.13"
lazy val circe = "0.9.3"

lazy val mwundo = project.in(file("."))
.aggregate(`mwundo-core`, `mwundo-spray`, `mwundo-circe`)
.aggregate(`mwundo-coreJVM`,`mwundo-coreJS`,`mwundo-spray`, `mwundo-circeJVM`,`mwundo-circeJS`)
.settings(commonSettings)
.settings(Seq(
packagedArtifacts := Map.empty,
publishArtifact := false,
publish := {}
))

lazy val `mwundo-core` = project.in(file("core"))
lazy val `mwundo-core` = crossProject(JSPlatform, JVMPlatform).in(file("core"))
// .crossType(CrossType.Pure)
.settings(commonSettings)
.settings(Seq(
libraryDependencies ++= Dependencies.core ++ Dependencies.test
))
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.0.1" % "test",
"org.scalacheck" %% "scalacheck" % "1.13.4" % "test",
"org.scalamock" %% "scalamock-scalatest-support" % "3.5.0" % "test",
"org.pegdown" % "pegdown" % "1.4.2" % "test"
)
)).jvmSettings(Seq(
libraryDependencies ++= Seq(
"org.scalanlp" %% "breeze" % breeze,
"org.scalanlp" %% "breeze-natives" % breeze,
"com.vividsolutions" % "jts-core" % "1.14.0")))

lazy val `mwundo-coreJVM`= `mwundo-core`.jvm
lazy val `mwundo-coreJS`= `mwundo-core`.js

lazy val `mwundo-spray` = project.in(file("spray"))
.dependsOn(`mwundo-core`)
.dependsOn(`mwundo-coreJVM`)
.settings(commonSettings)
.settings(Seq(
libraryDependencies ++= Dependencies.spray ++ Dependencies.test
libraryDependencies ++= Seq(
"io.spray" %% "spray-json" % "1.3.2",
"org.scalatest" %% "scalatest" % "3.0.1" % "test",
"org.scalacheck" %% "scalacheck" % "1.13.4" % "test",
"org.scalamock" %% "scalamock-scalatest-support" % "3.5.0" % "test",
"org.pegdown" % "pegdown" % "1.4.2" % "test"
)
))

lazy val `mwundo-circe` = project.in(file("circe"))

lazy val `mwundo-circe` = crossProject(JSPlatform, JVMPlatform)
.in(file("circe"))
// .crossType(CrossType.Pure)
.dependsOn(`mwundo-core`)
.settings(commonSettings)
.settings(Seq(
libraryDependencies ++= Dependencies.circe ++ Dependencies.test
))
libraryDependencies ++= Seq(
"io.circe" %%% "circe-core" % circe,
"io.circe" %%% "circe-generic" % circe,
"io.circe" %%% "circe-parser" % circe,
"org.scalatest" %% "scalatest" % "3.0.1" % "test",
"org.scalacheck" %% "scalacheck" % "1.13.4" % "test",
"org.scalamock" %% "scalamock-scalatest-support" % "3.5.0" % "test",
"org.pegdown" % "pegdown" % "1.4.2" % "test"
)
)
)

lazy val `mwundo-circeJVM`= `mwundo-circe`.jvm
lazy val `mwundo-circeJS`= `mwundo-circe`.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,22 @@ class GeoJsonCodecTest extends FunSpec with Matchers with ParallelTestExecution

extractGeometryTypeString(json) should be(mulitPolygonCollection.`type`)
}

it("does something") {
val shape1json =
"""
|{ "type": "Feature", "properties": { "id": "1234" }, "geometry": { "type": "Polygon", "coordinates":
|[ [ [ 127.083301191389481, 56.37455697640776 ], [ 127.083301191389481, 56.444169331058909 ],
|[ 127.122110418348996, 56.443626544667872 ], [ 127.123603080924354, 56.479179053281129 ],
|[ 127.165126239839083, 56.479043356683363 ],
|[ 127.161733824895066, 56.403053261937472 ], [ 127.138122616884729, 56.401153509568822],
|[ 127.137172740700407, 56.376185335580885 ], [ 127.083301191389481, 56.37455697640776 ] ] ] } }
""".stripMargin

val json = io.circe.parser.parse(shape1json).right.get
val f = json.as[Feature[Polygon,Map[String,String]]]
assert(f.isRight)
}
}

describe("GeoJson support"){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@ package com.monsanto.labs.mwundo
import com.vividsolutions.jts.geom._
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence

/**
* Created by Ryan Richt on 10/26/15
*/

trait JTSGeoFormat[G] {
def toJTSGeo(g: G, gf: GeometryFactory): Geometry
def fromJTSGeo(geo: Geometry): G
}

object JTSGeoFormat {

implicit def SeqConverter[G : JTSGeoFormat] = new JTSGeoFormat[Seq[G]] {
Expand Down Expand Up @@ -49,7 +46,7 @@ object JTSGeoFormat {
val polys = Seq.tabulate(geo.getNumGeometries)(i => geo.getGeometryN(i).asInstanceOf[Polygon])
val all = polys.map(p =>
Seq(p.getExteriorRing.getCoordinates.map(toGeoJsonCoord).toSeq) ++
Seq.tabulate(p.getNumInteriorRing)(i => p.getInteriorRingN(i).getCoordinates.map(toGeoJsonCoord).toSeq)
Seq.tabulate(p.getNumInteriorRing)(i => p.getInteriorRingN(i).getCoordinates.map(toGeoJsonCoord).toSeq)
)

GeoJson.MultiPolygon(all)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.monsanto.labs.mwundo

import com.vividsolutions.jts.geom.Geometry

object JTSGeometryImplicits {
implicit class RichJTSGeometry(geom: Geometry){
def as[G <: GeoJson.Geometry : JTSGeoFormat]: G = implicitly[JTSGeoFormat[G]].fromJTSGeo(geom)
}

// implicit class RichGeometryCollection[G <: Geometry](geomC: GeometryCollection)
// extends Traversable[G]
// with TraversableLike[G, RichGeometryCollection[G]]
// with GenericTraversableTemplate[G, RichGeometryCollection]{
//
// override def companion: GenericCompanion[RichGeometryCollection] = RichGeometryCollection
//
// override def foreach[U](f: (G) => U): Unit =
// Seq.tabulate(geomC.getNumGeometries)(i => geomC.getGeometryN(i)).foreach(x => f(x.asInstanceOf[G]))
// }
// object RichGeometryCollection extends TraversableFactory[RichGeometryCollection]{
//
// val geoFac = new GeometryFactory()
//
// implicit def canBuildFrom[G]: CanBuildFrom[Coll, G, RichGeometryCollection[G]] = new GenericCanBuildFrom[G]
//
// def newBuilder[G <: Geometry : ClassTag]: mutable.Builder[G, RichGeometryCollection[G]] = new mutable.Builder[G, RichGeometryCollection[G]] {
//
// var contents = Seq.empty[G]
// def +=(elem: G): this.type = { contents :+ elem ; this }
// def result(): RichGeometryCollection[G] = RichGeometryCollection( new GeometryCollection(contents.toArray, geoFac) )
// def clear(): Unit = {contents = Seq.empty[G]}
// }
// }
}
48 changes: 48 additions & 0 deletions core/jvm/src/main/scala/com/monsanto/labs/mwundo/JTSUtils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.monsanto.labs.mwundo

import com.vividsolutions.jts.geom._
import collection.JavaConverters._

class JTSUtils {

//TODO: this might not work for envelopes with convexities?
def clip(geom: Geometry, clipEnv: Envelope): GeometryCollection = {
val clipPoly: Geometry = geom.getFactory.toGeometry(clipEnv)

val geos = Seq.tabulate(geom.getNumGeometries)(i => geom.getGeometryN(i))

// LineStrings are from clipped polygons that no longer have any area, discard them
val clippedGeos = geos.collect{
case g: Geometry if ! g.isInstanceOf[LineString] && clipEnv.contains(g.getEnvelopeInternal) => g
case g: Geometry if ! g.isInstanceOf[LineString] && clipEnv.intersects(g.getEnvelopeInternal) =>
val intermedResult = clipPoly.intersection(g)
intermedResult.setUserData(g.getUserData)
intermedResult
// case completely outside of the clipping envelope, do not include in output list
}.collect{
case g: Geometry if g.isInstanceOf[Polygon] => g
}

geom.getFactory.createGeometryCollection(GeometryFactory.toGeometryArray(clippedGeos.asJavaCollection))
}

//TODO: this might not work for envelopes with convexities?
def clip(geom: Geometry, clipPoly: Geometry): GeometryCollection = {

val geos = Seq.tabulate(geom.getNumGeometries)(i => geom.getGeometryN(i))

// LineStrings are from clipped polygons that no longer have any area, discard them
val clippedGeos = geos.collect{
case g: Geometry if ! g.isInstanceOf[LineString] && clipPoly.contains(g) => g
case g: Geometry if ! g.isInstanceOf[LineString] && clipPoly.intersects(g) =>
val intermedResult = clipPoly.intersection(g)
intermedResult.setUserData(g.getUserData)
intermedResult
// case completely outside of the clipping envelope, do not include in output list
}.collect {
case g: Geometry if g.isInstanceOf[Polygon] => g
}

geom.getFactory.createGeometryCollection(GeometryFactory.toGeometryArray(clippedGeos.asJavaCollection))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import java.awt.geom.Point2D

import com.vividsolutions.jts.awt.PolygonShape

/**
* Created by Ryan Richt on 10/26/15
*/

trait Java2Dable[G] {
def toJava2D(g: G): Seq[java.awt.Shape]
}

object Java2Dable {

private val addToRing = classOf[PolygonShape].getDeclaredMethod("addToRing", classOf[Point2D])
Expand All @@ -19,10 +18,10 @@ object Java2Dable {

implicit object MultiPolygonJava2D extends Java2Dable[GeoJson.MultiPolygon] {
/**
* converts GeoJson MultiPolygon into JTS MultiPolygon
* @param g
* @return
*/
* converts GeoJson MultiPolygon into JTS MultiPolygon
* @param g
* @return
*/
def toJava2D(g: GeoJson.MultiPolygon) = {
g.coordinates.map { polygon =>
val outPolygon = new PolygonShape()
Expand All @@ -40,10 +39,10 @@ object Java2Dable {
implicit object PolygonJava2D extends Java2Dable[GeoJson.Polygon] {

/**
* converts GeoJson Polygon into JTS Polygon
* @param polygon
* @return
*/
* converts GeoJson Polygon into JTS Polygon
* @param polygon
* @return
*/
def toJava2D(polygon: GeoJson.Polygon) = {
val outPolygon = new PolygonShape()
polygon.coordinates.foreach { ring =>
Expand Down
36 changes: 36 additions & 0 deletions core/shared/src/main/scala/com/monsanto/labs/mwundo/Utils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.monsanto.labs.mwundo

object Utils {

private val meanRadiusOfEarthKm = 6371.0

def surfaceAreaOfSphere(r: Double): Double = {
latLongRectangleArea(-90D,-180D,90D,180D,r)
}

def latLongRectangleArea(lat1: Double, long1: Double, lat2: Double, long2: Double, r: Double = meanRadiusOfEarthKm) :Double =
Math.pow(r, 2.0) *
Math.abs(
Math.sin(Math.toRadians(lat1)) - Math.sin( Math.toRadians(lat2))
) *
Math.abs(
Math.toRadians(long1) - Math.toRadians(long2)
)

def haversineDistance(lat1: Double, lng1: Double, lat2: Double, lng2: Double, r: Double = meanRadiusOfEarthKm) :Double = {
val dLat = Math.toRadians(lat2 - lat1)
val dLon = Math.toRadians(lng2 - lng1)
val a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2)
val c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
val d = r * c
d
}

def haversineDistanceFromCoordinates(coordinate1: GeoJson.Coordinate, coordinate2: GeoJson.Coordinate, r: Double = meanRadiusOfEarthKm): Double = {
val (c1long, c1lat) = (coordinate1.x.toDouble, coordinate1.y.toDouble)
val (c2long, c2lat) = (coordinate2.x.toDouble, coordinate2.y.toDouble)
haversineDistance(c1lat, c1long, c2lat, c2long, r)
}

}

This file was deleted.

Loading