/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.common.instance.geometry.impl;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.geotools.gml2.SrsSyntax;
import org.geotools.referencing.CRS;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import eu.esdihumboldt.hale.common.schema.geometry.CRSDefinition;
/**
* CRS definition based on a code
*
* @author Simon Templer
*/
public class CodeDefinition implements CRSDefinition {
private static final long serialVersionUID = -7637649402983702957L;
private final String code;
private final boolean longitudeFirst;
private CoordinateReferenceSystem crs;
private static final LoadingCache<String, CoordinateReferenceSystem> CRS_CACHE = CacheBuilder
.newBuilder().maximumSize(100).expireAfterAccess(1, TimeUnit.HOURS)
.build(new CacheLoader<String, CoordinateReferenceSystem>() {
@Override
public CoordinateReferenceSystem load(String code) throws Exception {
return CRS.decode(code);
}
});
private static final LoadingCache<String, CoordinateReferenceSystem> LONGITUDE_FIRST_CRS_CACHE = CacheBuilder
.newBuilder().maximumSize(100).expireAfterAccess(1, TimeUnit.HOURS)
.build(new CacheLoader<String, CoordinateReferenceSystem>() {
@Override
public CoordinateReferenceSystem load(String code) throws Exception {
return CRS.decode(code, true);
}
});
/**
* Constructor
*
* @param code the CRS code (e.g. EPSG:4326)
* @param crs the coordinate reference system, may be <code>null</code>
*/
public CodeDefinition(String code, CoordinateReferenceSystem crs) {
this.code = code;
this.crs = crs;
this.longitudeFirst = false;
}
/**
* Create a code definition with only a code.
*
* @param code the code
*/
public CodeDefinition(String code) {
this(code, false);
}
/**
* Create a code definition with only a code.
*
* @param code the code
* @param longitudeFirst if the axis order should be assumed as longitude
* first
*/
public CodeDefinition(String code, boolean longitudeFirst) {
this.code = code;
this.crs = null;
this.longitudeFirst = longitudeFirst;
}
/**
* @see CRSDefinition#getCRS()
*/
@Override
public CoordinateReferenceSystem getCRS() {
if (crs == null) {
try {
if (longitudeFirst) {
crs = LONGITUDE_FIRST_CRS_CACHE.get(code);
}
else {
crs = CRS_CACHE.get(code);
}
} catch (Exception e) {
throw new IllegalStateException("Invalid CRS code", e);
}
}
return crs;
}
/**
* Get the CRS code
*
* @return the code
*/
public String getCode() {
return code;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((code == null) ? 0 : code.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CodeDefinition other = (CodeDefinition) obj;
if (code == null) {
if (other.code != null)
return false;
}
else if (!code.equals(other.code))
return false;
return true;
}
// helpers
/**
* Extract EPSG code number from a given CRS code.
*
* @param candidate the CRS code
* @return the EPSG code as string or <code>null</code> if it cannot be
* identified
*/
@Nullable
public static String extractEPSGCode(String candidate) {
for (SrsSyntax srsSyntax : SrsSyntax.values()) {
String code = extractCode(candidate, srsSyntax.getPrefix());
if (code != null) {
return code;
}
}
// other syntax that may occur
String prefix = "urn:ogc:def:crs:EPSG:";
String code = extractCode(candidate, prefix);
if (code != null) {
return code;
}
return null;
}
/**
* Extract code number from a given CRS code for a specified prefix.
*
* @param candidate the CRS code
* @param prefix the allowed prefix / authority
* @return the CRS code part w/o prefix or <code>null</code>
*/
@Nullable
public static String extractCode(String candidate, String prefix) {
if (candidate.length() > prefix.length()) {
String authPart = candidate.substring(0, prefix.length());
String codePart = candidate.substring(prefix.length());
try {
// ignore anything before the last colon
int colonIndex = codePart.lastIndexOf(':');
if (colonIndex >= 0) {
codePart = codePart.substring(colonIndex + 1);
}
// check if codePart represents an integer
Integer.parseInt(codePart);
if (authPart.equalsIgnoreCase(prefix)) {
return codePart;
}
} catch (NumberFormatException e) {
// invalid
}
}
return null;
}
}