org.integratedmodelling.engine.geospace.Geospace Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (C) 2007, 2015:
*
* - Ferdinando Villa - integratedmodelling.org - any
* other authors listed in @author annotations
*
* All rights reserved. This file is part of the k.LAB software suite, meant to enable
* modular, collaborative, integrated development of interoperable data and model
* components. For details, see http://integratedmodelling.org.
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the Affero General Public License Version 3 or any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the Affero General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite
* 330, Boston, MA 02111-1307, USA. The license is also available at:
* https://www.gnu.org/licenses/agpl.html
*******************************************************************************/
package org.integratedmodelling.engine.geospace;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import org.apache.jcs.JCS;
import org.apache.jcs.access.exception.CacheException;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.factory.GeoTools;
import org.geotools.factory.Hints;
import org.geotools.gce.geotiff.GeoTiffFormat;
import org.geotools.metadata.iso.citation.Citations;
import org.geotools.referencing.CRS;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.geotools.referencing.factory.PropertyAuthorityFactory;
import org.geotools.referencing.factory.ReferencingFactoryContainer;
import org.integratedmodelling.api.modelling.IScale;
import org.integratedmodelling.api.modelling.IState;
import org.integratedmodelling.common.configuration.KLAB;
import org.integratedmodelling.engine.geospace.extents.SpaceExtent;
import org.integratedmodelling.engine.geospace.gis.GISOperations;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabIOException;
import org.integratedmodelling.exceptions.KlabRuntimeException;
import org.integratedmodelling.exceptions.KlabValidationException;
import org.opengis.coverage.grid.GridCoverageWriter;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.cs.AxisDirection;
import org.w3c.dom.Node;
// import java.util.ArrayList;
public class Geospace {
/**
* Makes all CRS respect a X,Y arrangement. Without this, things get incredibly messy
* and wrong.
*/
static {
System.setProperty("org.geotools.referencing.forceXY", "true");
}
JCS _wcsCache = null;
JCS _wfsCache = null;
public static final String PREFERRED_CRS_PROPERTY = "geospace.preferred.crs";
// the projection to use if we need meters
public static final String EPSG_PROJECTION_METERS = "EPSG:3005";
public static final String EPSG_PROJECTION_DEFAULT = "EPSG:4326";
public static final String EPSG_PROJECTION_GOOGLE = "EPSG:3857";
// property to add new CRS by hand in property file
public static final String CUSTOM_CRS_PROPERTY = "geospace.crs.";
// projections not in the main repository inserted through properties
static HashMap localCRS = new HashMap();
/*
* if not null, we have a preferred crs in the properties, and we solve all conflicts
* by translating to it.
*/
CoordinateReferenceSystem preferredCRS = null;
CoordinateReferenceSystem metersCRS = null;
CoordinateReferenceSystem googleCRS = null;
private CoordinateReferenceSystem defaultCRS = null;
private Boolean _useSquareCellsM;
static private Geospace _this;
public static Geospace get() {
if (_this == null) {
try {
_this = new Geospace();
} catch (KlabException e) {
throw new KlabRuntimeException(e);
}
}
return _this;
}
void registerAdditionalCRS() throws KlabException {
URL epsg = null;
File epp = new File(KLAB.CONFIG.getDataPath() + File.separator + "epsg.properties");
if (epp.exists()) {
try {
epsg = epp.toURI().toURL();
} catch (MalformedURLException e) {
throw new KlabIOException(e);
}
}
if (epsg != null) {
Hints hints = new Hints(Hints.CRS_AUTHORITY_FACTORY, PropertyAuthorityFactory.class);
ReferencingFactoryContainer referencingFactoryContainer = ReferencingFactoryContainer
.instance(hints);
PropertyAuthorityFactory factory;
try {
factory = new PropertyAuthorityFactory(referencingFactoryContainer, Citations
.fromName("EPSG"), epsg);
ReferencingFactoryFinder.addAuthorityFactory(factory);
} catch (IOException e) {
throw new KlabIOException(e);
}
}
}
public JCS getWFSCache() throws KlabException {
if (_wfsCache == null) {
try {
_wfsCache = JCS.getInstance("wfs");
} catch (CacheException e) {
throw new KlabIOException(e);
}
}
return _wfsCache;
}
public JCS getWCSCache() throws KlabException {
if (_wcsCache == null) {
try {
_wcsCache = JCS.getInstance("wcs");
} catch (CacheException e) {
throw new KlabIOException(e);
}
}
return _wcsCache;
}
private Geospace() throws KlabException {
try {
registerAdditionalCRS();
metersCRS = CRS.decode(EPSG_PROJECTION_METERS, true);
defaultCRS = CRS.decode(EPSG_PROJECTION_DEFAULT, true);
googleCRS = CRS.decode(EPSG_PROJECTION_GOOGLE, true);
// CRSAuthorityFactory factory = CRS.getAuthorityFactory(true);
// geoCRSstraight = factory.createCoordinateReferenceSystem("EPSG:4326");
} catch (Exception e) {
throw new KlabException(e);
}
/*
* create preferred CRS if one is specified. Highly advisable to set one if hybrid
* data are used.
*/
if (KLAB.CONFIG.getProperties().containsKey(PREFERRED_CRS_PROPERTY)) {
preferredCRS = getCRSFromID(KLAB.CONFIG.getProperties().getProperty(PREFERRED_CRS_PROPERTY));
}
}
public CoordinateReferenceSystem getPreferredCRS() {
return preferredCRS;
}
public CoordinateReferenceSystem getGoogleCRS() {
return googleCRS;
}
/**
* The geotools implementation is unclear and doesn't seem to work, so I put this
* function here and we'll only have to fix it in one place.
*
* @param crs
* @param useDefault
* @return crs identifier
*/
public static String getCRSIdentifier(CoordinateReferenceSystem crs, boolean useDefault) {
if (crs != null) {
try {
return CRS.lookupIdentifier(crs, true);
} catch (FactoryException e) {
throw new KlabRuntimeException(e);
}
}
return useDefault ? KLAB.CONFIG.getProperties().getProperty(PREFERRED_CRS_PROPERTY) : null;
}
public CoordinateReferenceSystem getMetersCRS() {
return metersCRS;
}
/**
* Get the appropriate (sort of) UTM projection for the passed WGS84 point.
*
* @param x
* @param y
* @return CRS for meters in region
* @throws KlabValidationException
*/
public CoordinateReferenceSystem getMetersCRS(double x, double y) throws KlabValidationException {
int base_srid = y < 0 ? 32700 : 32600;
int out_srid = x == 180.0 ? base_srid + 60 : base_srid + (int) Math.floor((x + 186.0) / 6.0);
CoordinateReferenceSystem ret = getCRSFromID("EPSG:" + out_srid);
return ret == null ? metersCRS : ret;
}
public boolean handlesFormat(String format) {
// TODO add remaining support formats as necessary
return format.equals("shp") || format.equals("tif") || format.equals("tiff");
}
public Hints getGeotoolsHints() {
// TODO we need to create appropriate hints at initialization, using the plugin's
// properties.
return GeoTools.getDefaultHints();
}
public void notifyConfigurationNode(Node n) {
// TODO Auto-generated method stub
}
public void setPreferredCRS(CoordinateReferenceSystem crs) {
preferredCRS = crs;
}
public CoordinateReferenceSystem getDefaultCRS() {
return defaultCRS;
}
/**
* Returns true if the passed reference system assumes the X axis to be longitude
* (east-west).
*
* @param ccr
* @return true if x is horizontal
*/
public static boolean isLongitudeX(CoordinateReferenceSystem ccr) {
return !ccr.getCoordinateSystem().getAxis(0).getDirection().equals(AxisDirection.NORTH);
}
/**
* This method decides the CRS to use when they differ in merged extents. TODO at the
* moment it just returns the preferred CRS in the plugin, so even equal CRS get
* transformed.
*
* @param crs1
* @param crs2
* @return crs
*/
public static CoordinateReferenceSystem chooseCRS(CoordinateReferenceSystem crs1, CoordinateReferenceSystem crs2) {
CoordinateReferenceSystem ret = Geospace.get().getPreferredCRS();
if (ret == null) {
ret = crs1;
Geospace.get().setPreferredCRS(ret);
}
return ret;
}
/**
* Should be used throughout instead of CRS.decode().
*
* @param crsId
* @return crs
*/
public static CoordinateReferenceSystem getCRSFromID(String crsId) {
CoordinateReferenceSystem ret = localCRS.get(crsId);
if (ret == null) {
try {
ret = CRS.decode(crsId, true);
localCRS.put(crsId, ret);
} catch (Exception e) {
throw new KlabRuntimeException(e);
}
}
return ret;
}
public boolean squareCellsM() {
if (_useSquareCellsM == null) {
_useSquareCellsM = Boolean.parseBoolean(KLAB.CONFIG.getProperties()
.getProperty("square.cells.meters", "false"));
}
return _useSquareCellsM;
}
public CoordinateReferenceSystem getLatLonCRS() {
// TODO improve
return getDefaultCRS();
}
/**
* Persist a spatially distributed (and possibly temporally distributed) state in the
* passed file. Should be a GIS map of suitable format; if time is distributed,
* interpret the file as a directory and create one map per time instant.
*
* @param state
* @param file
* @throws KlabException
*/
public void persistState(IState state, File file, Iterable locators)
throws KlabException {
saveCoverage(state, file, locators);
}
private void saveCoverage(IState state, File file, Iterable locators)
throws KlabException {
SpaceExtent space = (SpaceExtent) state.getSpace();
if (space.getGrid() != null) {
GridCoverage2D coverage = GISOperations.stateToCoverage(state, locators);
if (coverage != null) {
if (!file.toString().endsWith(".tif") && !file.toString().endsWith(".tiff")) {
file = new File(file + ".tif");
}
GridCoverageWriter writer = new GeoTiffFormat().getWriter(file);
try {
writer.write(coverage, null);
} catch (Exception e) {
throw new KlabIOException(e);
}
}
}
}
}