diff --git a/pom.xml b/pom.xml
index ccf9cb5..eede2a8 100755
--- a/pom.xml
+++ b/pom.xml
@@ -60,8 +60,8 @@
maven-compiler-plugin
3.6.0
- 1.7
- 1.7
+ 1.8
+ 1.8
true
UTF-8
diff --git a/src/main/java/org/locationtech/proj4j/CRSFactory.java b/src/main/java/org/locationtech/proj4j/CRSFactory.java
index 5b9cc02..88b138a 100755
--- a/src/main/java/org/locationtech/proj4j/CRSFactory.java
+++ b/src/main/java/org/locationtech/proj4j/CRSFactory.java
@@ -18,6 +18,8 @@
import org.locationtech.proj4j.io.Proj4FileReader;
import org.locationtech.proj4j.parser.Proj4Parser;
+import java.io.IOException;
+
/**
* A factory which can create {@link CoordinateReferenceSystem}s
* from a variety of ways
@@ -39,7 +41,6 @@ public class CRSFactory {
// TODO: add method to allow reading from arbitrary PROJ4 CS file
-
/**
* Gets the {@link Registry} used by this factory.
*
@@ -126,9 +127,39 @@ public CoordinateReferenceSystem createFromParameters(String name, String[] para
return parser.parse(name, params);
}
+ /**
+ * Finds a EPSG Code
+ * from a PROJ.4 projection parameter string.
+ *
+ * An example of a valid PROJ.4 projection parameter string is:
+ *
+ * +proj=aea +lat_1=50 +lat_2=58.5 +lat_0=45 +lon_0=-126 +x_0=1000000 +y_0=0 +ellps=GRS80 +units=m
+ *
+ *
+ * @param paramStr a PROJ.4 projection parameter string
+ * @return the specified {@link CoordinateReferenceSystem}
+ * @throws IOException if there was an issue in reading EPSG file
+ */
+ public String readEpsgFromParameters(String paramStr) throws IOException {
+ return readEpsgFromParameters(splitParameters(paramStr));
+ }
+
+ /**
+ * Finds a EPSG Code
+ * defined by an array of PROJ.4 projection parameters.
+ * PROJ.4 parameters are generally of the form
+ * "+name=value".
+ *
+ * @param params an array of PROJ.4 projection parameters
+ * @return s String EPSG code
+ * @throws IOException if there was an issue in reading EPSG file
+ */
+ public String readEpsgFromParameters(String[] params) throws IOException {
+ return csReader.readEpsgCodeFromFile(params);
+ }
+
private static String[] splitParameters(String paramStr) {
String[] params = paramStr.split("\\s+");
return params;
}
-
}
diff --git a/src/main/java/org/locationtech/proj4j/CoordinateReferenceSystem.java b/src/main/java/org/locationtech/proj4j/CoordinateReferenceSystem.java
index 8242bfe..cc0b993 100755
--- a/src/main/java/org/locationtech/proj4j/CoordinateReferenceSystem.java
+++ b/src/main/java/org/locationtech/proj4j/CoordinateReferenceSystem.java
@@ -22,6 +22,8 @@
import org.locationtech.proj4j.units.Unit;
import org.locationtech.proj4j.units.Units;
+import java.util.Arrays;
+
/**
* Represents a projected or geodetic geospatial coordinate system,
* to which coordinates may be referenced.
@@ -119,4 +121,16 @@ public CoordinateReferenceSystem createGeographic() {
public String toString() {
return name;
}
+
+ @Override
+ public boolean equals(Object that) {
+ if (this == that) {
+ return true;
+ }
+ if (that instanceof CoordinateReferenceSystem) {
+ CoordinateReferenceSystem cr = (CoordinateReferenceSystem) that;
+ return name.equals(cr.name) && datum.isEqual(cr.getDatum()) && Arrays.equals(params, cr.params);
+ }
+ return false;
+ }
}
diff --git a/src/main/java/org/locationtech/proj4j/io/Proj4FileReader.java b/src/main/java/org/locationtech/proj4j/io/Proj4FileReader.java
index c50f723..3f0d63a 100755
--- a/src/main/java/org/locationtech/proj4j/io/Proj4FileReader.java
+++ b/src/main/java/org/locationtech/proj4j/io/Proj4FileReader.java
@@ -15,12 +15,15 @@
*/
package org.locationtech.proj4j.io;
+import org.locationtech.proj4j.util.Pair;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
public class Proj4FileReader {
@@ -71,49 +74,14 @@ private StreamTokenizer createTokenizer(BufferedReader reader) {
return t;
}
- private String[] readFile(BufferedReader reader, String name)
- throws IOException {
+ private String[] readFile(BufferedReader reader, String name) throws IOException {
StreamTokenizer t = createTokenizer(reader);
t.nextToken();
while (t.ttype == '<') {
- t.nextToken();
- if (t.ttype != StreamTokenizer.TT_WORD)
- throw new IOException(t.lineno() + ": Word expected after '<'");
- String crsName = t.sval;
- t.nextToken();
- if (t.ttype != '>')
- throw new IOException(t.lineno() + ": '>' expected");
- t.nextToken();
- List v = new ArrayList();
-
- while (t.ttype != '<') {
- if (t.ttype == '+')
- t.nextToken();
- if (t.ttype != StreamTokenizer.TT_WORD)
- throw new IOException(t.lineno() + ": Word expected after '+'");
- String key = t.sval;
- t.nextToken();
-
-
- // parse =arg, if any
- if (t.ttype == '=') {
- t.nextToken();
- //Removed check to allow for proj4 hack +nadgrids=@null
- //if ( t.ttype != StreamTokenizer.TT_WORD )
- // throw new IOException( t.lineno()+": Value expected after '='" );
- String value = t.sval;
- t.nextToken();
- addParam(v, key, value);
- } else {
- // add param with no value
- addParam(v, key, null);
- }
- }
- t.nextToken();
- if (t.ttype != '>')
- throw new IOException(t.lineno() + ": '<>' expected");
- t.nextToken();
+ Pair pair = parseTokenizer(t);
+ String crsName = pair.fst();
+ List v = pair.snd();
// found requested CRS?
if (crsName.equals(name)) {
@@ -156,4 +124,68 @@ public String[] getParameters(String crsName) {
return null;
}
+ public String readEpsgCodeFromFile(String[] params) throws IOException {
+ InputStream inStr = Proj4FileReader.class.getClassLoader().getResourceAsStream("proj4/nad/epsg");
+
+ if (inStr == null) {
+ throw new IllegalStateException("Unable to access CRS file: EPSG");
+ }
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inStr));
+
+ StreamTokenizer t = createTokenizer(reader);
+
+ t.nextToken();
+ while (t.ttype == '<') {
+ Pair pair = parseTokenizer(t);
+ String crsName = pair.fst();
+ List v = pair.snd();
+
+ String[] paramsParsed = (String[]) v.toArray(new String[0]);
+
+ if(Arrays.equals(params, paramsParsed)) return crsName;
+ }
+ return null;
+ }
+
+ private static Pair parseTokenizer(StreamTokenizer t) throws IOException {
+ t.nextToken();
+ if (t.ttype != StreamTokenizer.TT_WORD)
+ throw new IOException(t.lineno() + ": Word expected after '<'");
+ String crsName = t.sval;
+ t.nextToken();
+ if (t.ttype != '>')
+ throw new IOException(t.lineno() + ": '>' expected");
+ t.nextToken();
+ List v = new ArrayList();
+
+ while (t.ttype != '<') {
+ if (t.ttype == '+')
+ t.nextToken();
+ if (t.ttype != StreamTokenizer.TT_WORD)
+ throw new IOException(t.lineno() + ": Word expected after '+'");
+ String key = t.sval;
+ t.nextToken();
+
+
+ // parse =arg, if any
+ if (t.ttype == '=') {
+ t.nextToken();
+ //Removed check to allow for proj4 hack +nadgrids=@null
+ //if ( t.ttype != StreamTokenizer.TT_WORD )
+ // throw new IOException( t.lineno()+": Value expected after '='" );
+ String value = t.sval;
+ t.nextToken();
+ addParam(v, key, value);
+ } else {
+ // add param with no value
+ addParam(v, key, null);
+ }
+ }
+ t.nextToken();
+ if (t.ttype != '>')
+ throw new IOException(t.lineno() + ": '<>' expected");
+ t.nextToken();
+
+ return Pair.create(crsName, v);
+ }
}
diff --git a/src/main/java/org/locationtech/proj4j/util/CRSCache.java b/src/main/java/org/locationtech/proj4j/util/CRSCache.java
index 84c5482..57eecd5 100755
--- a/src/main/java/org/locationtech/proj4j/util/CRSCache.java
+++ b/src/main/java/org/locationtech/proj4j/util/CRSCache.java
@@ -15,31 +15,64 @@
*/
package org.locationtech.proj4j.util;
-import java.util.HashMap;
-import java.util.Map;
-
import org.locationtech.proj4j.*;
-public class CRSCache {
-
- private static Map projCache = new HashMap();
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+public class CRSCache {
private static CRSFactory crsFactory = new CRSFactory();
-// TODO: provide limit on number of items in cache (LRU)
+ private ConcurrentHashMap crsCache = new ConcurrentHashMap<>();
+ private ConcurrentHashMap epsgCache = new ConcurrentHashMap<>();
- public CRSCache() {
- super();
+ public CRSCache CRSCache() {
+ crsCache = new ConcurrentHashMap<>();
+ epsgCache = new ConcurrentHashMap<>();
+ return this;
+ }
+
+ public CRSCache CRSCache(ConcurrentHashMap crsCache, ConcurrentHashMap epsgCache) {
+ this.crsCache = crsCache;
+ this.epsgCache = epsgCache;
+ return this;
}
public CoordinateReferenceSystem createFromName(String name)
throws UnsupportedParameterException, InvalidValueException, UnknownAuthorityCodeException {
- CoordinateReferenceSystem proj = (CoordinateReferenceSystem) projCache.get(name);
- if (proj == null) {
- proj = crsFactory.createFromName(name);
- projCache.put(name, proj);
- }
- return proj;
+ CoordinateReferenceSystem res = crsCache.get(name);
+ if(res != null) return res;
+ return crsCache.computeIfAbsent(name, k -> crsFactory.createFromName(name));
}
+ public CoordinateReferenceSystem createFromParameters(String name, String paramStr)
+ throws UnsupportedParameterException, InvalidValueException {
+ String nonNullName = name == null ? "" : name;
+ String key = nonNullName + paramStr;
+ CoordinateReferenceSystem res = crsCache.get(key);
+ if(res != null) return res;
+ return crsCache.computeIfAbsent(key, k -> crsFactory.createFromParameters(name, paramStr));
+ }
+
+ public CoordinateReferenceSystem createFromParameters(String name, String[] params)
+ throws UnsupportedParameterException, InvalidValueException {
+ String nonNullName = name == null ? "" : name;
+ String key = nonNullName + String.join(" ", params);
+ CoordinateReferenceSystem res = crsCache.get(key);
+ if(res != null) return res;
+ return crsCache.computeIfAbsent(key, k -> crsFactory.createFromParameters(name, params));
+ }
+
+ public String readEpsgFromParameters(String paramStr) {
+ String res = epsgCache.get(paramStr);
+ if(res != null) return res;
+ return epsgCache.computeIfAbsent(paramStr, k -> { try { return crsFactory.readEpsgFromParameters(paramStr); } catch (IOException e) { return null; } });
+ }
+
+ public String readEpsgFromParameters(String[] params) {
+ String paramStr = String.join(" ", params);
+ String res = epsgCache.get(paramStr);
+ if(res != null) return res;
+ return epsgCache.computeIfAbsent(paramStr, k -> { try { return crsFactory.readEpsgFromParameters(params); } catch (IOException e) { return null; } });
+ }
}
diff --git a/src/main/java/org/locationtech/proj4j/util/Pair.java b/src/main/java/org/locationtech/proj4j/util/Pair.java
new file mode 100644
index 0000000..3e86447
--- /dev/null
+++ b/src/main/java/org/locationtech/proj4j/util/Pair.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright 2009, 2017 Martin Davis
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.locationtech.proj4j.util;
+
+public class Pair {
+
+ private A first;
+ private B second;
+
+ public Pair() {
+ super();
+ }
+
+ public Pair(A first, B second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ public A fst() {
+ return first;
+ }
+
+ public void setFirst(A first) {
+ this.first = first;
+ }
+
+ public B snd() {
+ return second;
+ }
+
+ public void setSecond(B second) {
+ this.second = second;
+ }
+
+ @Override
+ public String toString() {
+ return "<" + first + "," + second + ">";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ final Pair, ?> other = (Pair, ?>) obj;
+ if (first == null) {
+ if (other.first != null)
+ return false;
+ } else if (!first.equals(other.first))
+ return false;
+ if (second == null) {
+ if (other.second != null)
+ return false;
+ } else if (!second.equals(other.second))
+ return false;
+ return true;
+ }
+
+ public static Pair create(A first, B second) {
+ return new Pair(first, second);
+ }
+}