/*
* 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.ui.common.crs;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import org.geotools.metadata.iso.citation.Citations;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.geotools.referencing.factory.AbstractAuthorityFactory;
import org.opengis.metadata.citation.Citation;
import org.opengis.referencing.AuthorityFactory;
import org.opengis.referencing.Factory;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CRSAuthorityFactory;
import org.opengis.referencing.crs.CRSFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.crs.ProjectedCRS;
import org.opengis.util.InternationalString;
import de.fhg.igd.slf4jplus.ALogger;
import de.fhg.igd.slf4jplus.ALoggerFactory;
/**
* CRS factory based on WKT stored in Java preferences
*
* @author Simon Templer
* @partner 01 / Fraunhofer Institute for Computer Graphics Research
*/
public class WKTPreferencesCRSFactory extends AbstractAuthorityFactory implements
CRSAuthorityFactory {
private static final ALogger _log = ALoggerFactory.getLogger(WKTPreferencesCRSFactory.class);
/**
* The authority
*/
public static final String AUTHORITY = "EPSG"; //$NON-NLS-1$
/**
* The authority prefix
*/
public static final String AUTHORITY_PREFIX = "EPSG:"; //$NON-NLS-1$
/**
* The one and only factory instance
*/
protected static WKTPreferencesCRSFactory INSTANCE;
/**
* Preferences node
*/
private final Preferences node = Preferences.userNodeForPackage(WKTPreferencesCRSFactory.class)
.node(AUTHORITY);
/**
* CRS factory
*/
protected CRSFactory crsFactory;
/**
* Cache of parsed {@link CoordinateReferenceSystem}s
*/
private final Map<String, CoordinateReferenceSystem> cache = new HashMap<String, CoordinateReferenceSystem>();
/**
* Creates a new instance
*/
protected WKTPreferencesCRSFactory() {
this(ReferencingFactoryFinder.getCRSFactory(null));
}
/**
* Create a new instance, use the given CRS factory
*
* @param factory the CRS factory to use
*/
protected WKTPreferencesCRSFactory(final CRSFactory factory) {
super(MAXIMUM_PRIORITY); // allow overriding CRS definitions in the
// database
// MINIMUM_PRIORITY); // Select other factories first
this.crsFactory = factory;
}
/**
* Get the factory instance
*
* @return the factory instance
*/
public synchronized static WKTPreferencesCRSFactory getInstance() {
if (INSTANCE == null) {
INSTANCE = new WKTPreferencesCRSFactory();
}
return INSTANCE;
}
/**
* Install the factory with the {@link ReferencingFactoryFinder}
*/
public synchronized static void install() {
ReferencingFactoryFinder.addAuthorityFactory(getInstance());
}
/**
* Register a WKT with the factory
*
* @param code the CRS code (e.g. 4326 or EPSG:4326)
* @param wkt the CRS well known text
*/
public synchronized static void registerWKT(String code, String wkt) {
getInstance().addWKT(code, wkt);
}
/**
* Add a WKT
*
* @param code the CRS code (e.g. 4326 or EPSG:4326)
* @param wkt the CRS well known text
*/
public void addWKT(String code, String wkt) {
if (code.startsWith(AUTHORITY_PREFIX)) {
code = code.substring(AUTHORITY_PREFIX.length());
}
node.put(code, wkt);
try {
node.sync();
} catch (BackingStoreException e) {
_log.warn("Error saving preferences", e); //$NON-NLS-1$
}
// XXX maybe have to reinstall it
}
/**
* Add a WKT
*
* @param code the CRS code (e.g. 4326 or EPSG:4326) that the WKT is
* associated to
*/
public void removeWKT(String code) {
if (code.startsWith(AUTHORITY_PREFIX)) {
code = code.substring(AUTHORITY_PREFIX.length());
}
node.remove(code);
try {
node.sync();
} catch (BackingStoreException e) {
_log.warn("Error saving preferences", e); //$NON-NLS-1$
}
// XXX maybe have to reinstall it
}
/**
* Get the WKT for the given code
*
* @param code the CRS code (e.g. 4326 or EPSG:4326) that the WKT is
* associated to
* @return the WKT or <code>null</code>
*/
public String getWKT(String code) {
if (code.startsWith(AUTHORITY_PREFIX)) {
code = code.substring(AUTHORITY_PREFIX.length());
}
return node.get(code, null);
}
/**
* Get the available CRS codes (with the authority prefix)
*
* @return the CRS codes
*/
public List<String> getCodes() {
try {
String[] keys = node.keys();
List<String> result = new ArrayList<String>();
for (String key : keys) {
result.add(AUTHORITY_PREFIX + key);
}
return result;
} catch (BackingStoreException e) {
_log.warn("Error accessing preferences", e); //$NON-NLS-1$
return new ArrayList<String>();
}
}
/**
* @see CRSAuthorityFactory#createCoordinateReferenceSystem(String)
*/
@Override
public synchronized CoordinateReferenceSystem createCoordinateReferenceSystem(String code)
throws FactoryException {
if (code == null) {
return null;
}
if (!code.startsWith(AUTHORITY_PREFIX)) {
throw new NoSuchAuthorityCodeException(
"This factory only understands EPSG codes", AUTHORITY, code); //$NON-NLS-1$
}
final String epsgNumber = code.substring(code.indexOf(':') + 1).trim();
if (cache.containsKey(epsgNumber)) {
CoordinateReferenceSystem value = cache.get(epsgNumber);
if (value != null) {
// CRS was already created
return value;
}
}
try {
node.sync();
} catch (BackingStoreException e) {
_log.warn("Error synchronizing preferences", e); //$NON-NLS-1$
}
String wkt = node.get(epsgNumber, null);
if (wkt == null) {
throw new NoSuchAuthorityCodeException("Unknown EPSG code", AUTHORITY, code); //$NON-NLS-1$
}
if (wkt.indexOf(epsgNumber) == -1) {
wkt = wkt.trim();
wkt = wkt.substring(0, wkt.length() - 1);
wkt += ",AUTHORITY[\"EPSG\",\"" + epsgNumber + "\"]]"; //$NON-NLS-1$ //$NON-NLS-2$
_log.warn("EPSG:" + epsgNumber + " lacks a proper identifying authority in its Well-Known Text. It is being added programmatically."); //$NON-NLS-1$ //$NON-NLS-2$
}
try {
CoordinateReferenceSystem crs = crsFactory.createFromWKT(wkt);
cache.put(epsgNumber, crs);
return crs;
} catch (FactoryException fex) {
throw fex;
}
}
/**
* @see AuthorityFactory#createObject(String)
*/
@Override
public IdentifiedObject createObject(String code) throws FactoryException {
return createCoordinateReferenceSystem(code);
}
/**
* @see CRSAuthorityFactory#createProjectedCRS(String)
*/
@Override
public ProjectedCRS createProjectedCRS(String code) throws FactoryException {
return (ProjectedCRS) createCoordinateReferenceSystem(code);
}
/**
* @see CRSAuthorityFactory#createGeographicCRS(String)
*/
@Override
public GeographicCRS createGeographicCRS(String code) throws FactoryException {
return (GeographicCRS) createCoordinateReferenceSystem(code);
}
/**
* @see AuthorityFactory#getAuthority()
*/
@Override
public Citation getAuthority() {
return Citations.EPSG;
}
/**
* @see AuthorityFactory#getAuthorityCodes(Class)
*
* The following implementation filters the set of codes based on the
* "PROJCS" and "GEOGCS" at the start of the WKT strings. It is assumed
* that we only have GeographicCRS and ProjectedCRS's here.
*/
@Override
public Set<String> getAuthorityCodes(Class<? extends IdentifiedObject> clazz)
throws FactoryException {
Set<String> all = new HashSet<String>();
try {
if (clazz.getName().equalsIgnoreCase(CoordinateReferenceSystem.class.getName())) {
for (String number : node.keys()) {
all.add(AUTHORITY_PREFIX + number);
}
}
else if (clazz.getName().equalsIgnoreCase(GeographicCRS.class.getName())) {
for (String number : node.keys()) {
String wkt = node.get(number, null);
if (wkt != null && wkt.startsWith("GEOGCS")) { //$NON-NLS-1$
all.add(AUTHORITY_PREFIX + number);
}
}
}
else if (clazz.getName().equalsIgnoreCase(ProjectedCRS.class.getName())) {
for (String number : node.keys()) {
String wkt = node.get(number, null);
if (wkt != null && wkt.startsWith("PROJCS")) { //$NON-NLS-1$
all.add(AUTHORITY_PREFIX + number);
}
}
}
} catch (BackingStoreException e) {
throw new RuntimeException("Could not access preferences", e); //$NON-NLS-1$
}
return all;
}
/**
* @see Factory#getVendor()
*/
@Override
public Citation getVendor() {
return Citations.GEOTOOLS; // XXX
}
/**
* @see AuthorityFactory#getDescriptionText(String)
*/
@Override
public InternationalString getDescriptionText(String code) throws FactoryException {
if (code == null) {
return null;
}
if (code.startsWith("EPSG:")) { //$NON-NLS-1$
code = code.substring(5);
}
code = code.trim();
String wkt = node.get(code, null);
if (wkt == null) {
throw new FactoryException("Unknown EPSG code: '" + code + "'"); //$NON-NLS-1$ //$NON-NLS-2$
}
wkt = wkt.trim();
int start = wkt.indexOf('"');
int end = wkt.indexOf('"', start + 1);
return new org.geotools.util.SimpleInternationalString(wkt.substring(start + 1, end));
}
/**
* @see CRSAuthorityFactory#createCompoundCRS(String)
*/
@Override
public org.opengis.referencing.crs.CompoundCRS createCompoundCRS(String str)
throws FactoryException {
throw new FactoryException("Not implemented"); //$NON-NLS-1$
}
/**
* @see CRSAuthorityFactory#createDerivedCRS(String)
*/
@Override
public org.opengis.referencing.crs.DerivedCRS createDerivedCRS(String str)
throws FactoryException {
throw new FactoryException("Not implemented"); //$NON-NLS-1$
}
/**
* @see CRSAuthorityFactory#createEngineeringCRS(String)
*/
@Override
public org.opengis.referencing.crs.EngineeringCRS createEngineeringCRS(String str)
throws FactoryException {
throw new FactoryException("Not implemented"); //$NON-NLS-1$
}
/**
* @see CRSAuthorityFactory#createGeocentricCRS(String)
*/
@Override
public org.opengis.referencing.crs.GeocentricCRS createGeocentricCRS(String str)
throws FactoryException {
throw new FactoryException("Not implemented"); //$NON-NLS-1$
}
/**
* @see CRSAuthorityFactory#createImageCRS(String)
*/
@Override
public org.opengis.referencing.crs.ImageCRS createImageCRS(String str) throws FactoryException {
throw new FactoryException("Not implemented"); //$NON-NLS-1$
}
/**
* @see CRSAuthorityFactory#createTemporalCRS(String)
*/
@Override
public org.opengis.referencing.crs.TemporalCRS createTemporalCRS(String str)
throws FactoryException {
throw new FactoryException("Not implemented"); //$NON-NLS-1$
}
/**
* @see CRSAuthorityFactory#createVerticalCRS(String)
*/
@Override
public org.opengis.referencing.crs.VerticalCRS createVerticalCRS(String str)
throws FactoryException {
throw new FactoryException("Not implemented"); //$NON-NLS-1$
}
}