From 8f70ba1f62ab6c94bad31b808ac5f799789d9d2b Mon Sep 17 00:00:00 2001 From: ebocher Date: Fri, 30 Aug 2019 09:34:06 +0200 Subject: [PATCH 1/3] Add an utility class to extract UTM informations from latitude and longitude --- pom.xml | 2 +- src/main/java/org/cts/CRSFactory.java | 8 +- src/main/java/org/cts/CRSHelper.java | 3 + .../cts/op/projection/TransverseMercator.java | 1 - .../UniversalTransverseMercator.java | 1 - src/main/java/org/cts/util/UTMUtils.java | 351 ++++++++++++++++++ .../java/org/cts/op/EPSGTransformTest.java | 14 + src/test/java/org/cts/util/UTMUtilsTest.java | 63 ++++ 8 files changed, 439 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/cts/util/UTMUtils.java create mode 100644 src/test/java/org/cts/util/UTMUtilsTest.java diff --git a/pom.xml b/pom.xml index 7b5e552a..9b89b7d8 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ org.orbisgis orbisparent - 1.0.1 + 1.0.3-SNAPSHOT cts diff --git a/src/main/java/org/cts/CRSFactory.java b/src/main/java/org/cts/CRSFactory.java index bb7c7bb8..b73323cd 100644 --- a/src/main/java/org/cts/CRSFactory.java +++ b/src/main/java/org/cts/CRSFactory.java @@ -168,7 +168,9 @@ public CoordinateReferenceSystem createFromPrj(String prjString) throws CRSExcep * * @param stream the input stream of bytes defining the OGC WKT String * @param encoding the charset used to read the input stream + * @return a CoordinateReferenceSystem * @throws IOException + * @throws org.cts.crs.CRSException */ public CoordinateReferenceSystem createFromPrj(InputStream stream, Charset encoding) throws IOException, CRSException { BufferedReader r = new BufferedReader(new InputStreamReader(stream, encoding)); @@ -184,7 +186,9 @@ public CoordinateReferenceSystem createFromPrj(InputStream stream, Charset encod * (PRJ). * * @param stream the input stream of bytes defining the OGC WKT String + * @return a CoordinateReferenceSystem * @throws IOException + * @throws org.cts.crs.CRSException */ public CoordinateReferenceSystem createFromPrj(InputStream stream) throws IOException, CRSException { return createFromPrj(stream, Charset.defaultCharset()); @@ -193,9 +197,11 @@ public CoordinateReferenceSystem createFromPrj(InputStream stream) throws IOExce /** * Creates a {@link CoordinateReferenceSystem} defined by an OGC WKT String * (PRJ). - * + * @return a CoordinateReferenceSystem * @param file containing the OGC WKT String that defined the desired CRS + * @return * @throws IOException if there is a problem reading the file + * @throws org.cts.crs.CRSException */ public CoordinateReferenceSystem createFromPrj(File file) throws IOException, CRSException { InputStream i = null; diff --git a/src/main/java/org/cts/CRSHelper.java b/src/main/java/org/cts/CRSHelper.java index 6b521acc..bc4884ef 100644 --- a/src/main/java/org/cts/CRSHelper.java +++ b/src/main/java/org/cts/CRSHelper.java @@ -767,6 +767,9 @@ private static Projection getProjection(String projectionName, Ellipsoid ell, } } + + + /** * A simple cache to manage {@link AbstractCoordinateOperation} */ diff --git a/src/main/java/org/cts/op/projection/TransverseMercator.java b/src/main/java/org/cts/op/projection/TransverseMercator.java index 8a6ff683..d97836a8 100644 --- a/src/main/java/org/cts/op/projection/TransverseMercator.java +++ b/src/main/java/org/cts/op/projection/TransverseMercator.java @@ -28,7 +28,6 @@ import org.cts.CoordinateDimensionException; import org.cts.Identifier; import org.cts.datum.Ellipsoid; -import org.cts.op.NonInvertibleOperationException; import org.cts.units.Measure; import org.cts.util.Complex; diff --git a/src/main/java/org/cts/op/projection/UniversalTransverseMercator.java b/src/main/java/org/cts/op/projection/UniversalTransverseMercator.java index d2e23e80..75256118 100644 --- a/src/main/java/org/cts/op/projection/UniversalTransverseMercator.java +++ b/src/main/java/org/cts/op/projection/UniversalTransverseMercator.java @@ -30,7 +30,6 @@ import org.cts.Identifier; import org.cts.Parameter; import org.cts.datum.Ellipsoid; -import org.cts.op.NonInvertibleOperationException; import org.cts.units.Measure; import org.cts.units.Unit; import org.cts.util.Complex; diff --git a/src/main/java/org/cts/util/UTMUtils.java b/src/main/java/org/cts/util/UTMUtils.java new file mode 100644 index 00000000..a4ffeadc --- /dev/null +++ b/src/main/java/org/cts/util/UTMUtils.java @@ -0,0 +1,351 @@ +/* + * Coordinate Transformations Suite (abridged CTS) is a library developped to + * perform Coordinate Transformations using well known geodetic algorithms + * and parameter sets. + * Its main focus are simplicity, flexibility, interoperability, in this order. + * + * This library has been originally developed by Michaël Michaud under the JGeod + * name. It has been renamed CTS in 2009 and shared to the community from + * the OrbisGIS code repository. + * + * CTS is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License. + * + * CTS 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 GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * CTS. If not, see . + * + * For more information, please consult: + */ +package org.cts.util; + +import java.util.HashMap; +import java.util.Map; + +/** + * Utility class to get UTM informations from latitude and longitude coordinates + * + * @author Erwan Bocher + */ +public class UTMUtils { + + private static Map utmEpsg =null; + + private static Map prepareMap() { + utmEpsg = new HashMap<>(); + utmEpsg.put("1S", 32701); + utmEpsg.put("2S", 32702); + utmEpsg.put("3S", 32703); + utmEpsg.put("4S", 32704); + utmEpsg.put("5S", 32705); + utmEpsg.put("6S", 32706); + utmEpsg.put("7S", 32707); + utmEpsg.put("8S", 32708); + utmEpsg.put("9S", 32709); + utmEpsg.put("10S", 32710); + utmEpsg.put("11S", 32711); + utmEpsg.put("12S", 32712); + utmEpsg.put("13S", 32713); + utmEpsg.put("14S", 32714); + utmEpsg.put("15S", 32715); + utmEpsg.put("16S", 32716); + utmEpsg.put("17S", 32717); + utmEpsg.put("18S", 32718); + utmEpsg.put("19S", 32719); + utmEpsg.put("20S", 32720); + utmEpsg.put("21S", 32721); + utmEpsg.put("22S", 32722); + utmEpsg.put("23S", 32723); + utmEpsg.put("24S", 32724); + utmEpsg.put("25S", 32725); + utmEpsg.put("26S", 32726); + utmEpsg.put("27S", 32727); + utmEpsg.put("28S", 32728); + utmEpsg.put("29S", 32729); + utmEpsg.put("30S", 32730); + utmEpsg.put("31S", 32731); + utmEpsg.put("32S", 32732); + utmEpsg.put("33S", 32733); + utmEpsg.put("34S", 32734); + utmEpsg.put("35S", 32735); + utmEpsg.put("36S", 32736); + utmEpsg.put("37S", 32737); + utmEpsg.put("38S", 32738); + utmEpsg.put("39S", 32739); + utmEpsg.put("40S", 32740); + utmEpsg.put("41S", 32741); + utmEpsg.put("42S", 32742); + utmEpsg.put("43S", 32743); + utmEpsg.put("44S", 32744); + utmEpsg.put("45S", 32745); + utmEpsg.put("46S", 32746); + utmEpsg.put("47S", 32747); + utmEpsg.put("48S", 32748); + utmEpsg.put("49S", 32749); + utmEpsg.put("50S", 32750); + utmEpsg.put("51S", 32751); + utmEpsg.put("52S", 32752); + utmEpsg.put("53S", 32753); + utmEpsg.put("54S", 32754); + utmEpsg.put("55S", 32755); + utmEpsg.put("56S", 32756); + utmEpsg.put("57S", 32757); + utmEpsg.put("58S", 32758); + utmEpsg.put("59S", 32759); + utmEpsg.put("60S", 32760); + utmEpsg.put("1N", 32601); + utmEpsg.put("2N", 32602); + utmEpsg.put("3N", 32603); + utmEpsg.put("4N", 32604); + utmEpsg.put("5N", 32605); + utmEpsg.put("6N", 32606); + utmEpsg.put("7N", 32607); + utmEpsg.put("8N", 32608); + utmEpsg.put("9N", 32609); + utmEpsg.put("10N", 32610); + utmEpsg.put("11N", 32611); + utmEpsg.put("12N", 32612); + utmEpsg.put("13N", 32613); + utmEpsg.put("14N", 32614); + utmEpsg.put("15N", 32615); + utmEpsg.put("16N", 32616); + utmEpsg.put("17N", 32617); + utmEpsg.put("18N", 32618); + utmEpsg.put("19N", 32619); + utmEpsg.put("20N", 32620); + utmEpsg.put("21N", 32621); + utmEpsg.put("22N", 32622); + utmEpsg.put("23N", 32623); + utmEpsg.put("24N", 32624); + utmEpsg.put("25N", 32625); + utmEpsg.put("26N", 32626); + utmEpsg.put("27N", 32627); + utmEpsg.put("28N", 32628); + utmEpsg.put("29N", 32629); + utmEpsg.put("30N", 32630); + utmEpsg.put("31N", 32631); + utmEpsg.put("32N", 32632); + utmEpsg.put("33N", 32633); + utmEpsg.put("34N", 32634); + utmEpsg.put("35N", 4037); + utmEpsg.put("36N", 4038); + utmEpsg.put("37N", 32637); + utmEpsg.put("38N", 32638); + utmEpsg.put("39N", 32639); + utmEpsg.put("40N", 32640); + utmEpsg.put("41N", 32641); + utmEpsg.put("42N", 32642); + utmEpsg.put("43N", 32643); + utmEpsg.put("44N", 32644); + utmEpsg.put("45N", 32645); + utmEpsg.put("46N", 32646); + utmEpsg.put("47N", 32647); + utmEpsg.put("48N", 32648); + utmEpsg.put("49N", 32649); + utmEpsg.put("50N", 32650); + utmEpsg.put("51N", 32651); + utmEpsg.put("52N", 32652); + utmEpsg.put("53N", 32653); + utmEpsg.put("54N", 32654); + utmEpsg.put("55N", 32655); + utmEpsg.put("56N", 32656); + utmEpsg.put("57N", 32657); + utmEpsg.put("58N", 32658); + utmEpsg.put("59N", 32659); + utmEpsg.put("60N", 32660); + return utmEpsg; + } + + /* + *Minimum value for latitude + */ + public static final int MIN_LATITUDE = -90; + + /* + *Maximum value for latitude + */ + public static final int MAX_LATITUDE = +90; + + /* + *Minimum value for longitude + */ + public static final int MIN_LONGITUDE = -180; + + /* + *Maximum usual value for longitude + */ + public static final int MAX_LONGITUDE = +180; + + /** + * The diameter of the Earth used in calculations + */ + public static float EARTH_DIAMETER = Float.valueOf("12756.274"); + + /* + *UTM north border + */ + public static final int UTM_NORTH_MAX = 84; + + /* + *UTM min latitude for Norway grid exception + */ + public static final int NORWAY_MIN_LATITUDE =56; + + /* + *UTM max latitude for Norway grid exception + */ + public static final int NORWAY_MAX_LATITUDE =64; + + /* + *UTM min latitude for Svalbard grid exception + */ + public static final int SVALBARD_MIN_LATITUDE=72; + + + /** + * Check if the latitude is valid + * + * @param latitude the latitude to check is valid + * + * @return true if the latitude is within the MIN and MAX latitude + */ + public static boolean isValidLatitude(float latitude) { + if (latitude >= MIN_LATITUDE && latitude <= MAX_LATITUDE) { + return true; + } else { + return false; + } + } + + /** + * Check if the longitude is valid + * + * @param longitude the longitude to check + * + * @return true if the longitude is between the MIN and MAX longitude + */ + public static boolean isValidLongitude(float longitude) { + if (longitude >= MIN_LONGITUDE && longitude <= MAX_LONGITUDE) { + return true; + } else { + return false; + } + } + + /** + * Return the EPSG UTM code from the tuple latitude and longitude + * + * @param latitude a latitude in the desired UTM + * @param longitude a longitude in the desired UTM + * @return + */ + public static int getEPSGCode(float latitude, float longitude) { + String[] utmInfo = getZoneHemisphere(latitude, longitude); + if(utmEpsg==null){ + utmEpsg = prepareMap(); + } + return utmEpsg.get(utmInfo[0]+utmInfo[1]); + } + + /** + * Check if the float value is between min and max + * @param value + * @param minValue + * @param maxValue + * @return true if the value is in the range + */ + private static boolean isBetween(float value, int minValue, int maxValue){ + return value >= minValue && value < maxValue; + } + + + /** + * Return the zone number of grid plus its hemisphere (N for North, + * S for South) + * @param latitude + * @param longitude + * @return a String array with two values, the zone number + * and the hemisphere + */ + public static String[] getZoneHemisphere(float latitude, float longitude){ + if (isValidLatitude(latitude) && isValidLongitude(longitude)) { + int zone = (int) Math.floor(longitude / 6 + 31); + String hemisphere; + if (latitude < 0) { + hemisphere = "S"; + } else { + hemisphere = "N"; + } + /*Workarround for southwest coast of Norway + *and region around Svalbard + */ + switch (zone) { + //Norway case + case 31: + if (isBetween(latitude, NORWAY_MIN_LATITUDE, NORWAY_MAX_LATITUDE)) { + if (longitude >= 3) { + zone++; + } + } + break; + //Svalbard case + case 32: + if (isBetween(latitude,SVALBARD_MIN_LATITUDE,UTM_NORTH_MAX)) { + if (longitude >= 9) { + zone++; + } else { + zone--; + } + } + break; + //Svalbard case + case 34: + if (isBetween(latitude, SVALBARD_MIN_LATITUDE, UTM_NORTH_MAX)) { + if (longitude >= 21) { + zone++; + } else { + zone--; + } + } + break; + //Svalbard case + case 36: + if (isBetween(latitude,SVALBARD_MIN_LATITUDE,UTM_NORTH_MAX)) { + if (longitude >= 33) { + zone++; + } else { + zone--; + } + } + break; + default: + break; + } + return new String[]{String.valueOf(zone),hemisphere}; + } else { + throw new IllegalArgumentException("Please set valid latitude and longitude values"); + } + } + + /** + * Return the UTM proj representation from the tuple latitude and longitude + * e.g. : + * +proj=utm +zone=31 +datum=WGS84 +units=m +no_defs + * @param latitude a latitude in the desired UTM + * @param longitude a longitude in the desired UTM + * @return + */ + public static String getProj(float latitude, float longitude) { + String[] utmInfo = getZoneHemisphere(latitude, longitude); + if(utmInfo[1].equals("S")){ + return String.format("+proj=utm +zone=%s +south +datum=WGS84 +units=m +no_defs", utmInfo[0]); + } + else{ + return String.format("+proj=utm +zone=%s +datum=WGS84 +units=m +no_defs", utmInfo[0]); + } + } +} diff --git a/src/test/java/org/cts/op/EPSGTransformTest.java b/src/test/java/org/cts/op/EPSGTransformTest.java index cf9db979..3c93e18a 100644 --- a/src/test/java/org/cts/op/EPSGTransformTest.java +++ b/src/test/java/org/cts/op/EPSGTransformTest.java @@ -77,4 +77,18 @@ void testFrenchEPSGCodeFrom27572To3857() throws Exception { double[] result = transform((GeodeticCRS) inputCRS, (GeodeticCRS) outputCRS, pointSource); assertTrue(checkEquals2D("EPSG:27572 to EPSG:3857", result, pointDest, tolerance)); } + + @Test + void testFrenchEPSGCodeFrom4326To4299() throws Exception { + String csNameSrc = "EPSG:4326"; //Input EPSG + double[] pointSource = new double[]{-7.899170,52.831312}; + String csNameDest = "EPSG:4299"; //Target EPSG + double[] pointDest = new double[]{-7.89842402505289,52.8310168494995}; + double tolerance = 0.0000001; + CoordinateReferenceSystem inputCRS = cRSFactory.getCRS(csNameSrc); + CoordinateReferenceSystem outputCRS = cRSFactory.getCRS(csNameDest); + verbose = true; + double[] result = transform((GeodeticCRS) inputCRS, (GeodeticCRS) outputCRS, pointSource); + assertTrue(checkEquals2D("EPSG:4326 to EPSG:4299", result, pointDest, tolerance)); + } } diff --git a/src/test/java/org/cts/util/UTMUtilsTest.java b/src/test/java/org/cts/util/UTMUtilsTest.java new file mode 100644 index 00000000..157b32ed --- /dev/null +++ b/src/test/java/org/cts/util/UTMUtilsTest.java @@ -0,0 +1,63 @@ +/* + * Coordinate Transformations Suite (abridged CTS) is a library developped to + * perform Coordinate Transformations using well known geodetic algorithms + * and parameter sets. + * Its main focus are simplicity, flexibility, interoperability, in this order. + * + * This library has been originally developed by Michaël Michaud under the JGeod + * name. It has been renamed CTS in 2009 and shared to the community from + * the OrbisGIS code repository. + * + * CTS is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License. + * + * CTS 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 GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * CTS. If not, see . + * + * For more information, please consult: + */ +package org.cts.util; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Tests for UTMUtils + * @author Erwan Bocher + */ +public class UTMUtilsTest { + + @Test + void utmInfoTest() { + assertArrayEquals(new String[]{"32","N"}, UTMUtils.getZoneHemisphere(59.04f, 3.68f)); + assertArrayEquals(new String[]{"17","S"}, UTMUtils.getZoneHemisphere(-10.8469f, -81.0351f)); + assertArrayEquals(new String[]{"34","N"}, UTMUtils.getZoneHemisphere(68.948f, 20.939f)); + assertArrayEquals(new String[]{"36","N"}, UTMUtils.getZoneHemisphere(66.682f, 32.2119f)); + assertArrayEquals(new String[]{"36","S"}, UTMUtils.getZoneHemisphere(-66.682f,32.2119f)); + } + + @Test + void utmEpsgTest() { + assertEquals(32632, UTMUtils.getEPSGCode(59.04f, 3.68f)); + assertEquals(32717, UTMUtils.getEPSGCode(-10.8469f, -81.0351f)); + assertEquals(32634, UTMUtils.getEPSGCode(68.948f, 20.939f)); + assertEquals(4038, UTMUtils.getEPSGCode(66.682f, 32.2119f)); + assertEquals(32736, UTMUtils.getEPSGCode(-66.682f,32.2119f)); + } + + @Test + void utmProjTest() { + assertEquals("+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs", UTMUtils.getProj(59.04f, 3.68f)); + assertEquals("+proj=utm +zone=17 +south +datum=WGS84 +units=m +no_defs", UTMUtils.getProj(-10.8469f, -81.0351f)); + assertEquals("+proj=utm +zone=34 +datum=WGS84 +units=m +no_defs", UTMUtils.getProj(68.948f, 20.939f)); + assertEquals("+proj=utm +zone=36 +datum=WGS84 +units=m +no_defs", UTMUtils.getProj(66.682f, 32.2119f)); + assertEquals("+proj=utm +zone=36 +south +datum=WGS84 +units=m +no_defs", UTMUtils.getProj(-66.682f,32.2119f)); + } +} From 1707d95c83dc87eb2fa097f2ad154212a0e61945 Mon Sep 17 00:00:00 2001 From: ebocher Date: Fri, 30 Aug 2019 12:00:59 +0200 Subject: [PATCH 2/3] Add proj4 parser --- src/main/java/org/cts/CRSFactory.java | 26 ++++++ .../org/cts/parser/proj4/Proj4Parser.java | 86 +++++++++++++++++++ src/main/java/org/cts/util/UTMUtils.java | 6 +- src/test/java/org/cts/CRSFactoryTest.java | 15 ++++ .../org/cts/parser/proj4/Proj4ParserTest.java | 73 ++++++++++++++++ 5 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/cts/parser/proj4/Proj4Parser.java create mode 100644 src/test/java/org/cts/parser/proj4/Proj4ParserTest.java diff --git a/src/main/java/org/cts/CRSFactory.java b/src/main/java/org/cts/CRSFactory.java index b73323cd..dc4bdc28 100644 --- a/src/main/java/org/cts/CRSFactory.java +++ b/src/main/java/org/cts/CRSFactory.java @@ -25,6 +25,7 @@ import java.io.*; import java.nio.charset.Charset; +import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -33,6 +34,7 @@ import org.cts.crs.CoordinateReferenceSystem; import org.cts.parser.prj.PrjKeyParameters; import org.cts.parser.prj.PrjParser; +import org.cts.parser.proj4.Proj4Parser; import org.cts.registry.Registry; import org.cts.registry.RegistryException; import org.cts.registry.RegistryManager; @@ -221,10 +223,34 @@ public CoordinateReferenceSystem createFromPrj(File file) throws IOException, CR * Return a list of supported codes according an registryName. * * @param registryName (ex : EPSG, IGNF, ESRI) + * @return List of supported codes + * @throws org.cts.registry.RegistryException */ public Set getSupportedCodes(String registryName) throws RegistryException { return getRegistryManager().getRegistry(registryName).getSupportedCodes(); } + + + /** + * Creates a {@link CoordinateReferenceSystem} defined by a proj4 string + * representation + * + * @param prj4String the proj4 string defining the CRS + * @return + * @throws org.cts.crs.CRSException + */ + public CoordinateReferenceSystem createFromPrj4(String prj4String) throws CRSException { + Map prjParameters = Proj4Parser.readParameters(prj4String); + String zone = prjParameters.get("zone"); + String crsName; + if (zone != null) { + crsName = prjParameters.get("south") == null ? String.format("UTM %s %s", zone, "NORTH") : String.format("UTM %s %s", zone, "SOUTH"); + } + else{ + crsName = String.format("Unknown CRS %s",System.currentTimeMillis()); + } + return CRSHelper.createCoordinateReferenceSystem(new Identifier(CoordinateReferenceSystem.class, crsName), prjParameters); + } /** * A simple cache to manage {@link CoordinateReferenceSystem} diff --git a/src/main/java/org/cts/parser/proj4/Proj4Parser.java b/src/main/java/org/cts/parser/proj4/Proj4Parser.java new file mode 100644 index 00000000..5ac841f9 --- /dev/null +++ b/src/main/java/org/cts/parser/proj4/Proj4Parser.java @@ -0,0 +1,86 @@ +/* + * Coordinate Transformations Suite (abridged CTS) is a library developped to + * perform Coordinate Transformations using well known geodetic algorithms + * and parameter sets. + * Its main focus are simplicity, flexibility, interoperability, in this order. + * + * This library has been originally developed by Michaël Michaud under the JGeod + * name. It has been renamed CTS in 2009 and shared to the community from + * the OrbisGIS code repository. + * + * CTS is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License. + * + * CTS 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 GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * CTS. If not, see . + * + * For more information, please consult: + */ +package org.cts.parser.proj4; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; +import org.cts.parser.proj.ProjKeyParameters; + +/** + * A simple Proj4 parser to return a list of parameters used to build a + * CoordinateSystem + * + * @author Erwan Bocher, CNRS + */ +public class Proj4Parser { + + /** + * The regex that must be used to parse. + */ + static final Pattern regex = Pattern.compile("[ ]\\+|\\s<>"); + + public static Map readParameters(String projText) { + if (projText == null || projText.isEmpty()) { + throw new IllegalArgumentException("Please set a correct proj4 representation"); + } + Map params = new HashMap(); + + String[] tokens = regex.split(projText); + + if (tokens[0].startsWith("+proj")) { + for (String token : tokens) { + String[] keyValue = token.split("="); + if (keyValue.length == 2) { + String key = formatKey(keyValue[0]); + ProjKeyParameters.checkUnsupported(key); + params.put(key, keyValue[1]); + } else { + String key = formatKey(token); + ProjKeyParameters.checkUnsupported(key); + params.put(key, null); + } + } + return params; + } else { + throw new IllegalArgumentException("The proj4 representation must startwith +proj"); + } + } + + /** + * Remove + char if exists + * + * @param key + */ + private static String formatKey(String key) { + String formatKey = key; + if (key.startsWith("+")) { + formatKey = key.substring(1); + } + return formatKey; + } + + + +} diff --git a/src/main/java/org/cts/util/UTMUtils.java b/src/main/java/org/cts/util/UTMUtils.java index a4ffeadc..4b787b4a 100644 --- a/src/main/java/org/cts/util/UTMUtils.java +++ b/src/main/java/org/cts/util/UTMUtils.java @@ -29,12 +29,16 @@ /** * Utility class to get UTM informations from latitude and longitude coordinates * - * @author Erwan Bocher + * @author Erwan Bocher CNRS */ public class UTMUtils { private static Map utmEpsg =null; + /** + * List of EPSG code corresponding to the UTM zone and hemisphere + * @return + */ private static Map prepareMap() { utmEpsg = new HashMap<>(); utmEpsg.put("1S", 32701); diff --git a/src/test/java/org/cts/CRSFactoryTest.java b/src/test/java/org/cts/CRSFactoryTest.java index 1a80789d..fb23288c 100644 --- a/src/test/java/org/cts/CRSFactoryTest.java +++ b/src/test/java/org/cts/CRSFactoryTest.java @@ -101,4 +101,19 @@ void testOGC_WKT_27572_CRS_SPACE() throws Exception { assertEquals("EPSG", crs.getAuthorityName()); assertEquals("27572", crs.getAuthorityKey()); } + + + @Test + void testCRSFromProj4() throws Exception { + String prj = "+proj=tmerc +lat_0=0 +lon_0=106 +k=1 +x_0=500000 +y_0=0 +ellps=krass +towgs84=-17.51,-108.32,-62.39,0,0,0,0 +units=m +no_defs"; + CoordinateReferenceSystem crs = cRSFactory.createFromPrj4(prj); + assertNotNull(crs); + } + + @Test + void testCRSFromProj4UTM() throws Exception { + String prj = "+proj=utm +zone=32 +south +datum=WGS84 +units=m +no_defs"; + CoordinateReferenceSystem crs = cRSFactory.createFromPrj4(prj); + assertEquals("UTM 32 NORTH", crs.getName()); + } } diff --git a/src/test/java/org/cts/parser/proj4/Proj4ParserTest.java b/src/test/java/org/cts/parser/proj4/Proj4ParserTest.java new file mode 100644 index 00000000..370e19ba --- /dev/null +++ b/src/test/java/org/cts/parser/proj4/Proj4ParserTest.java @@ -0,0 +1,73 @@ +/* + * Coordinate Transformations Suite (abridged CTS) is a library developped to + * perform Coordinate Transformations using well known geodetic algorithms + * and parameter sets. + * Its main focus are simplicity, flexibility, interoperability, in this order. + * + * This library has been originally developed by Michaël Michaud under the JGeod + * name. It has been renamed CTS in 2009 and shared to the community from + * the OrbisGIS code repository. + * + * CTS is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License. + * + * CTS 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 GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * CTS. If not, see . + * + * For more information, please consult: + */ +package org.cts.parser.proj4; + +import java.util.Map; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import org.junit.jupiter.api.Test; + +/** + * Test for proj4 parser + * @author Erwan Bocher CNRS + */ +public class Proj4ParserTest { + + @Test + void testParseUTMNorth() { + Map params = Proj4Parser.readParameters("+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs"); + assertEquals(params.get("proj"), "utm"); + assertEquals(params.get("zone"), "32"); + assertEquals(params.get("datum"), "WGS84"); + assertEquals(params.get("units"), "m"); + assertNull(params.get("no_defs")); + } + + @Test + void testParseUTMSouth() { + Map params = Proj4Parser.readParameters("+proj=utm +zone=32 +south +datum=WGS84 +units=m +no_defs"); + assertEquals(params.get("proj"), "utm"); + assertEquals(params.get("zone"), "32"); + assertEquals(params.get("datum"), "WGS84"); + assertEquals(params.get("units"), "m"); + assertNull(params.get("south")); + assertNull(params.get("no_defs")); + } + + @Test + void testParse() { + Map params = Proj4Parser.readParameters("+proj=tmerc +lat_0=0 +lon_0=106 +k=1 +x_0=500000 +y_0=0 +ellps=krass +towgs84=-17.51,-108.32,-62.39,0,0,0,0 +units=m +no_defs"); + assertEquals(params.get("proj"), "tmerc"); + assertEquals(params.get("lat_0"), "0"); + assertEquals(params.get("lon_0"), "106"); + assertEquals(params.get("k"), "1"); + assertEquals(params.get("x_0"), "500000"); + assertEquals(params.get("y_0"), "0"); + assertEquals(params.get("towgs84"), "-17.51,-108.32,-62.39,0,0,0,0"); + assertEquals(params.get("ellps"), "krass"); + assertEquals(params.get("units"), "m"); + assertNull(params.get("no_defs")); + } + +} From 01ea60be76d1247d91fc50a61ae2c0d818143266 Mon Sep 17 00:00:00 2001 From: ebocher Date: Fri, 30 Aug 2019 12:33:32 +0200 Subject: [PATCH 3/3] Add ubuntu version --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index a392aa2f..43c2b584 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +dist: trusty language: java jdk: - oraclejdk8