diff --git a/jbossmodule/no/bioforsk/vips/VIPSCommon/main/module.xml b/jbossmodule/no/bioforsk/vips/VIPSCommon/main/module.xml index 508273e5068d3dbc1a644dbd3ba8894cc02e554d..a93e7dcf606bee5b5ffb480b0db8bea378689a3b 100755 --- a/jbossmodule/no/bioforsk/vips/VIPSCommon/main/module.xml +++ b/jbossmodule/no/bioforsk/vips/VIPSCommon/main/module.xml @@ -19,6 +19,7 @@ <module name="javax.servlet.api" export="false"/> <module name="org.apache.commons.io" export="false"/> <module name="org.apache.commons.codec" export="false"/> + <module name="com.vividsolutions.jts" export="false"/> </dependencies> </module> diff --git a/pom.xml b/pom.xml index 3e07131b1bbc9315176f76716baf4267e48fdf13..f02d7e9247a08b10a51d0679293aebd2af6ea211 100755 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> - + <dependencies> <dependency> <groupId>junit</groupId> @@ -26,7 +26,7 @@ <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> - <version>2.4.1</version> + <version>2.8.10</version> </dependency> <dependency> <groupId>commons-validator</groupId> @@ -37,12 +37,12 @@ <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> - <version>2.4.1</version> + <version>2.8.10</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> - <version>2.4.1</version> + <version>2.8.10</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> @@ -101,6 +101,12 @@ <artifactId>jts</artifactId> <version>1.13</version> </dependency> +<dependency> + <groupId>com.github.bjornharrtell</groupId> + <!--groupId>org.wololo</groupId--> + <artifactId>jts2geojson</artifactId> + <version>0.10.0</version> + </dependency> </dependencies> <repositories> <repository> @@ -108,6 +114,10 @@ <name>bedatadriven public repo</name> <url>https://nexus.bedatadriven.com/content/groups/public/</url> </repository> + <repository> + <id>jitpack.io</id> + <url>https://jitpack.io</url> + </repository> </repositories> <build> <plugins> diff --git a/src/main/java/no/nibio/vips/gis/GISUtil.java b/src/main/java/no/nibio/vips/gis/GISUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..8b828319701bf65f7a3461574b295222af75812a --- /dev/null +++ b/src/main/java/no/nibio/vips/gis/GISUtil.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSCommon. + * VIPSCommon is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSCommon is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSCommon. If not, see <http://www.nibio.no/licenses/>. + * + */ + +package no.nibio.vips.gis; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.Point; +import com.vividsolutions.jts.geom.PrecisionModel; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.wololo.geojson.Feature; +import org.wololo.geojson.FeatureCollection; +import org.wololo.geojson.GeoJSONFactory; +import org.wololo.jts2geojson.GeoJSONReader; +import org.wololo.jts2geojson.GeoJSONWriter; + +/** + * Handy tools for GIS operations + * @copyright 2017 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +public class GISUtil { + + /** + * Calculates the coordinates of a nearby destination + * @param origin WGS84 where to start from + * @param distance Distance in km + * @param initialBearing The initial bearing, given as degrees clockwise starting from North + * @return + */ + public Coordinate getNearbyDestination(Coordinate origin, Double distance, Double initialBearing) + { + // Constant + Double R = 6371.0; // The average radius of the earth in km + // Convert to radians + Double bearing = this.toRadians(initialBearing); + Double longitude = this.toRadians(origin.x); + Double latitude = this.toRadians(origin.y); + + // Latitude is seemingly easy + Double angularDistance = distance/R; + Double deltaLat = angularDistance * Math.cos(bearing); + Double newLat = latitude + deltaLat; + + // check for some daft bugger going past the pole, normalize latitude if so + if(Math.abs(newLat) > Math.PI/2) + { + newLat = newLat > 0 ? Math.PI - newLat : -Math.PI - newLat; + } + + // Longitude + Double projectedLatitudeDifference = Math.log(Math.tan(newLat/2 + Math.PI/4)/Math.tan(latitude/2 + Math.PI/4)); + Double q = Math.abs(projectedLatitudeDifference) > 10e-12 ? deltaLat/projectedLatitudeDifference : Math.cos(latitude); // E-W course becomes ill-conditioned with 0/0 + Double deltaLong = angularDistance * Math.sin(bearing)/q; + Double newLong = longitude + deltaLong; + + return new Coordinate(this.toDegrees(newLong), this.toDegrees(newLat)); + } + + /** + * + * @param degrees + * @return radian value + */ + public Double toRadians(Double degrees) + { + return degrees * Math.PI / 180; + } + + /** + * + * @param radians + * @return degree value + */ + public Double toDegrees(Double radians) + { + return radians * 180 / Math.PI; + } + + /** + * Parses all features from GeoJSON string, returns as JTS Geometry objects + * @param json + * @return + */ + public List<Geometry> getGeometriesFromGeoJSON(String json) + { + FeatureCollection featureCollection = (FeatureCollection) GeoJSONFactory.create(json); + GeoJSONReader reader = new GeoJSONReader(); + List<Geometry> retVal = new ArrayList<>(); + for(Feature feature: featureCollection.getFeatures()) + { + retVal.add(reader.read(feature.getGeometry())); + } + return retVal; + } + + /** + * Hand in your JTS Geometry objects, get GEOJSON back + * @param geometries + * @param properties these props will be attached to all features + * @return + */ + public String getGeoJSONFromGeometries(List<Geometry> geometries, Map<String, Object> properties) + { + if(geometries == null || geometries.isEmpty()) + { + return ""; + } + List<Feature> features = new ArrayList<>(); + GeoJSONWriter writer = new GeoJSONWriter(); + geometries.stream().forEach((geometry)->features.add(new Feature(writer.write(geometry), properties))); + FeatureCollection json = writer.write(features); + return json.toString(); + } + + /** + * Convert one JTS Geometry object into a GeoJSON feature + * @param geometry + * @param properties + * @return + */ + public String getGeoJSONFromGeometry(Geometry geometry,Map<String, Object> properties) + { + if(geometry == null) + { + return ""; + } + List<Feature> features = new ArrayList<>(); + GeoJSONWriter writer = new GeoJSONWriter(); + + features.add(new Feature(writer.write(geometry), properties)); + + FeatureCollection json = writer.write(features); + return json.toString(); + } + + /** + * If you have WGS84 coordinates, th + * @param coordinate in WGS84 system + * @return + */ + public Point createPointWGS84(Coordinate coordinate) + { + GeometryFactory gf = new GeometryFactory(new PrecisionModel(), 4326); + return gf.createPoint(coordinate); + } + +} diff --git a/src/test/java/no/nibio/vips/gis/GISUtilTest.java b/src/test/java/no/nibio/vips/gis/GISUtilTest.java new file mode 100644 index 0000000000000000000000000000000000000000..81559a42cb91d0fab98c92b35b00baffd46a8c83 --- /dev/null +++ b/src/test/java/no/nibio/vips/gis/GISUtilTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2017 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSCommon. + * VIPSCommon is free software: you can redistribute it and/or modify + * it under the terms of the NIBIO Open Source License as published by + * NIBIO, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSCommon is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSCommon. If not, see <http://www.nibio.no/licenses/>. + * + */ +package no.nibio.vips.gis; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.Point; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import junit.framework.TestCase; + +/** + * + * @author treinar + */ +public class GISUtilTest extends TestCase { + + public GISUtilTest(String testName) { + super(testName); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Test of getNearbyDestination method, of class GISUtil. + * + public void testGetNearbyDestination() { + System.out.println("getNearbyDestination"); + Coordinate origin = null; + Double distance = null; + Double initialBearing = null; + GISUtil instance = new GISUtil(); + Coordinate expResult = null; + Coordinate result = instance.getNearbyDestination(origin, distance, initialBearing); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of toRadians method, of class GISUtil. + * + public void testToRadians() { + System.out.println("toRadians"); + Double degrees = null; + GISUtil instance = new GISUtil(); + Double expResult = null; + Double result = instance.toRadians(degrees); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of toDegrees method, of class GISUtil. + * + public void testToDegrees() { + System.out.println("toDegrees"); + Double radians = null; + GISUtil instance = new GISUtil(); + Double expResult = null; + Double result = instance.toDegrees(radians); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of getGeometriesFromGeoJSON method, of class GISUtil. + * + public void testGetGeometriesFromGeoJSON() { + System.out.println("getGeometriesFromGeoJSON"); + String json = ""; + GISUtil instance = new GISUtil(); + List<Geometry> expResult = null; + List<Geometry> result = instance.getGeometriesFromGeoJSON(json); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of getGeoJSONFromGeometries method, of class GISUtil. + * + public void testGetGeoJSONFromGeometries() { + System.out.println("getGeoJSONFromGeometries"); + List<Geometry> geometries = null; + Map<String, Object> properties = null; + GISUtil instance = new GISUtil(); + String expResult = ""; + String result = instance.getGeoJSONFromGeometries(geometries, properties); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of getGeoJSONFromGeometry method, of class GISUtil. + * + public void testGetGeoJSONFromGeometry() { + System.out.println("getGeoJSONFromGeometry"); + Geometry geometry = null; + Map<String, Object> properties = null; + GISUtil instance = new GISUtil(); + String expResult = ""; + String result = instance.getGeoJSONFromGeometry(geometry, properties); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of createPointWGS84 method, of class GISUtil. + * + public void testCreatePointWGS84() { + System.out.println("createPointWGS84"); + Coordinate coordinate = null; + GISUtil instance = new GISUtil(); + Point expResult = null; + Point result = instance.createPointWGS84(coordinate); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + /** + * Test of getGeometriesFromGeoJSON method, of class GISEntityUtil. + */ + + public void testRoundtrip() { + System.out.println("testRoundtrip"); + String json = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[10.669097900390623,59.753628151319106],[10.612792968749998,59.70309199431276],[10.726776123046873,59.705863076677105],[10.669097900390623,59.753628151319106]]]},\"properties\":{\"observationId\":12}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[10.704803466796873,59.64831609639066]},\"properties\":{\"observationId\":12}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[10.649871826171871,59.67051458978321],[10.791320800781248,59.67328836837126]]},\"properties\":{\"observationId\":12}}]}"; + System.out.println("JSON=" + json); + GISUtil instance = new GISUtil(); + //List<Geometry> expResult = null; + List<com.vividsolutions.jts.geom.Geometry> geometries = instance.getGeometriesFromGeoJSON(json); + Map<String, Object> properties = new HashMap<>(); + properties.put("observationId", 12); + String result = instance.getGeoJSONFromGeometries(geometries, properties); + //System.out.println(result); + assertEquals(json, result); + + + } + +}