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

Improve CRS Caching, add CoordinateReferenceSystem.equals overload #33

Merged
merged 3 commits into from
Jul 31, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 7 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
</properties>

<dependencies>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
pomadchin marked this conversation as resolved.
Show resolved Hide resolved
<artifactId>caffeine</artifactId>
<version>2.6.2</version>
pomadchin marked this conversation as resolved.
Show resolved Hide resolved
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand All @@ -60,8 +65,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
<debug>true</debug>
<encoding>UTF-8</encoding>
</configuration>
Expand Down
35 changes: 33 additions & 2 deletions src/main/java/org/locationtech/proj4j/CRSFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
*
Expand Down Expand Up @@ -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.
* <p>
* An example of a valid PROJ.4 projection parameter string is:
* <pre>
* +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
* </pre>
*
* @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
* "<tt>+name=value</tt>".
*
* @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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
}
57 changes: 57 additions & 0 deletions src/main/java/org/locationtech/proj4j/io/Proj4FileReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Proj4FileReader {
Expand Down Expand Up @@ -156,4 +157,60 @@ 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 == '<') {
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();

String[] paramsParsed = (String[]) v.toArray(new String[0]);

if(Arrays.equals(params, paramsParsed)) return crsName;
}
return null;
}
}
48 changes: 34 additions & 14 deletions src/main/java/org/locationtech/proj4j/util/CRSCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,51 @@
*/
package org.locationtech.proj4j.util;

import java.util.HashMap;
import java.util.Map;

import org.locationtech.proj4j.*;

public class CRSCache {
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

private static Map<String, CoordinateReferenceSystem> projCache = new HashMap<String, CoordinateReferenceSystem>();
import java.io.IOException;

public class CRSCache {
private static CRSFactory crsFactory = new CRSFactory();
private Cache<String, CoordinateReferenceSystem> crsCache = Caffeine.newBuilder().build();
private Cache<String, String> epsgCache = Caffeine.newBuilder().build();

// TODO: provide limit on number of items in cache (LRU)
public CRSCache CRSCache() {
crsCache = Caffeine.newBuilder().build();
return this;
}

public CRSCache() {
super();
public CRSCache CRSCache(Cache<String, CoordinateReferenceSystem> crsCache, Cache<String, String> 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;
return crsCache.get(name, k -> crsFactory.createFromName(name));
}

public CoordinateReferenceSystem createFromParameters(String name, String paramStr)
throws UnsupportedParameterException, InvalidValueException {
String nonNullName = name == null ? "" : name;
return crsCache.get(nonNullName + paramStr, k -> crsFactory.createFromParameters(name, paramStr));
}

public CoordinateReferenceSystem createFromParameters(String name, String[] params)
throws UnsupportedParameterException, InvalidValueException {
String nonNullName = name == null ? "" : name;
return crsCache.get(nonNullName + String.join(" ", params), k -> crsFactory.createFromParameters(name, params));
}

public String readEpsgFromParameters(String paramStr) {
return epsgCache.get(paramStr, k -> { try { return crsFactory.readEpsgFromParameters(paramStr); } catch (IOException e) { return null; } });
}

public String readEpsgFromParameters(String[] params) {
return epsgCache.get(String.join(" ", params), k -> { try { return crsFactory.readEpsgFromParameters(params); } catch (IOException e) { return null; } });
}
}