
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.knowledge.IConcept;
import org.integratedmodelling.api.modelling.IScale;
import org.integratedmodelling.api.modelling.IState;
import org.integratedmodelling.api.modelling.ISubject;
import org.integratedmodelling.common.configuration.KLAB;
import org.integratedmodelling.common.vocabulary.NS;
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 class NS {
//
// public static final String ELEVATION = "im.geo:ElevationSeaLevel";
// public static final String DEGREE_SLOPE = "im.geo:DegreeSlope";
// public static final String PERCENT_SLOPE = "im.geo:PercentSlope";
// public static final String RADIANS_SLOPE = "im.geo:RadiansSlope";
// }
private IConcept shapeType;
private IConcept pointType;
private IConcept lineStringType;
private IConcept polygonType;
private IConcept multiPointType;
private IConcept multiLineStringType;
private IConcept multiPolygonType;
// private ISemanticObject> areaLocationInstance;
// private ISemanticObject> rasterGridInstance;
// private ISemanticObject> spatialCoverageInstance;
private IConcept arealLocationType;
private IConcept rasterGridObservable;
private IConcept spatialCoverageObservable;
private IConcept subdividedSpaceObservable;
// private IConcept spaceObservable;
private IConcept gridClassifierType;
private IConcept spaceDomain;
private static String hasBoundingBoxPropertyID;
private static String hasCentroidPropertyID;
private static IConcept rasterSpaceType;
static final public String PLUGIN_ID = "org.integratedmodelling.thinklab.geospace";
public static final String X_RANGE_OFFSET = "geospace:hasXRangeOffset";
public static final String X_RANGE_MAX = "geospace:hasXRangeMax";
public static final String Y_RANGE_OFFSET = "geospace:hasYRangeOffset";
public static final String Y_RANGE_MAX = "geospace:hasYRangeMax";
public static final String LAT_LOWER_BOUND = "geospace:hasLatLowerBound";
public static final String LON_LOWER_BOUND = "geospace:hasLonLowerBound";
public static final String LAT_UPPER_BOUND = "geospace:hasLatUpperBound";
public static final String LON_UPPER_BOUND = "geospace:hasLonUpperBound";
public static final String CRS_CODE = "geospace:hasCoordinateReferenceSystem";
public static final String COVERAGE_SOURCE_URL = "geospace:hasSourceURL";
public static final String RASTER_GRID_OBSERVABLE = "geospace:ContinuousRegularSpatialGrid";
public static final String SHAPE_COVERAGE_OBSERVABLE = "geospace:PolygonSpatialCoverage";
public static final String PREFERRED_CRS_PROPERTY = "geospace.preferred.crs";
public static final String HAS_VALUE_ATTRIBUTE = "geospace:hasValueAttribute";
public static final String HAS_SOURCE_LINK_ATTRIBUTE = "geospace:hasSourceLinkAttribute";
public static final String HAS_TARGET_LINK_ATTRIBUTE = "geospace:hasTargetLinkAttribute";
public static final String HAS_TRANSFORMATION_EXPRESSION = "geospace:hasTransformation";
public static final String HAS_ATTRIBUTE_URL = "geospace:hasAttributeUrl";
public static final String RASTER_GRID = "geospace:RasterGrid";
public static final String AREAL_LOCATION = "geospace:RasterGrid";
public static final String GRID_CLASSIFIER = "geospace:GridClassifier";
public static final String CLASSIFIED_GRID = "geospace:ClassifiedGrid";
public static final String GRID_CLASSIFICATION_MODEL = "geospace:GridClassification";
public static final String HAS_FILTER_PROPERTY = "geospace:hasFilter";
// 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;
// CoordinateReferenceSystem geoCRSstraight = null;
// /*
// * we maintain a collection of gazetteers that plugins can install. The lookupFeature() function
// * will search all of them.
// */
// HashMap gazetteers = new HashMap();
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 {
/*
* TODO put all these class names into global strings in NS
*/
spaceDomain = KLAB.KM.getConcept(NS.SPACE_DOMAIN);
pointType = KLAB.KM.getConcept("geospace:Point");
lineStringType = KLAB.KM.getConcept("geospace:LineString");
polygonType = KLAB.KM.getConcept("geospace:Polygon");
multiPointType = KLAB.KM.getConcept("geospace:MultiPoint");
multiLineStringType = KLAB.KM.getConcept("geospace:MultiLineString");
multiPolygonType = KLAB.KM.getConcept("geospace:MultiPolygon");
arealLocationType = KLAB.KM.getConcept("geospace:ArealLocation");
rasterGridObservable = KLAB.KM.getConcept(RASTER_GRID_OBSERVABLE);
spatialCoverageObservable = KLAB.KM.getConcept(SHAPE_COVERAGE_OBSERVABLE);
gridClassifierType = KLAB.KM.getConcept(GRID_CLASSIFIER);
shapeType = KLAB.KM.getConcept("geospace:SpatialRecord");
hasBoundingBoxPropertyID = "geospace:hasBoundingBox";
hasCentroidPropertyID = "geospace:hasCentroid";
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;
}
// /**
// * Return a lat/lon CRS that is guaranteed to transform data to coordinates that have longitude on
// * the X axis.
// *
// * @return
// */
// public CoordinateReferenceSystem getStraightGeoCRS() {
// return geoCRSstraight;
// }
/**
* 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 IConcept Point() {
return pointType;
}
public IConcept LineString() {
return lineStringType;
}
public IConcept Polygon() {
return polygonType;
}
public IConcept MultiPoint() {
return multiPointType;
}
public IConcept MultiLineString() {
return multiLineStringType;
}
public IConcept MultiPolygon() {
return multiPolygonType;
}
public IConcept Shape() {
return shapeType;
}
// public ISemanticObject> absoluteArealLocationInstance(IOntology session) {
// return areaLocationInstance;
// }
//
// public ISemanticObject> absoluteRasterGridInstance(IOntology session) {
// return rasterGridInstance;
// }
public IConcept ArealLocation() {
return arealLocationType;
}
public static String hasBoundingBox() {
return hasBoundingBoxPropertyID;
}
public static String hasCentroid() {
return hasCentroidPropertyID;
}
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 static IConcept RasterObservationSpace() {
return rasterSpaceType;
}
// public ISemanticObject> RasterGridObservable() {
//
// if (rasterGridInstance == null) {
// try {
// rasterGridInstance = Thinklab.get().entify(PolyList.list(rasterGridObservable));
// } catch (ThinklabException e) {
// throw new ThinklabRuntimeException(e);
// }
// }
// return rasterGridInstance;
// }
public IConcept SubdividedSpaceObservable() {
return subdividedSpaceObservable;
}
//
// public IConcept SpaceObservable() {
// return spaceObservable;
// }
public void setPreferredCRS(CoordinateReferenceSystem crs) {
preferredCRS = crs;
}
// public ISemanticObject> SpatialCoverageObservable() {
//
// if (spatialCoverageInstance == null) {
// try {
// spatialCoverageInstance = Thinklab.get().entify(PolyList.list(spatialCoverageObservable));
// } catch (ThinklabException e) {
// throw new ThinklabRuntimeException(e);
// }
// }
// return spatialCoverageInstance;
// }
public IConcept GridClassifier() {
return gridClassifierType;
}
// /**
// * Add a gazetteer to the collection.
// * @param g
// */
// public void addGazetteer(String id, IGazetteer g) {
// gazetteers.put(id,g);
// }
public CoordinateReferenceSystem getDefaultCRS() {
return defaultCRS;
}
/**
* Lookup a feature name through all existing gazetteers. If stopWhenFound is true, return
* after the first lookup that succeeds.
*
* @param name
* @return
* @throws ThinklabException
*/
// public IQueryResult lookupFeature(String name)
// throws ThinklabException {
//
// MultipleQueryResult ret =
// new MultipleQueryResult(new QueryString(name));
//
// // sort them every time, we're not going to have a million of these.
// IGazetteer[] gazz = new IGazetteer[gazetteers.size()];
// int i = 0;
// for (IGazetteer g : gazetteers.values())
// gazz[i++] = g;
//
// Arrays.sort(gazz, new Comparator() {
// @Override
// public int compare(IGazetteer o1, IGazetteer o2) {
// return o1.getPriority() - o2.getPriority();
// }
// });
//
// for (IGazetteer g : gazz) {
// ret.add(g.query(g.parseQuery(name)));
// }
//
// return ret;
// }
// public Collection listKnownFeatures() {
//
// ArrayList ret = new ArrayList();
// //
// // for (IGazetteer g : gazetteers.values()) {
// // g.getKnownNames(ret);
// // }
//
// return ret;
// }
//
// /**
// * Get your engine here, passing the necessary configuration properties.
// *
// * @param subject
// * @param properties
// * @return
// * @throws ThinklabException
// */
// public void createGazetteer(Extension ext, Properties properties) throws ThinklabException {
// String id = getParameter(ext, "id");
//
// /*
// * find the declaring plugin so we can find data and files in its classpath
// */
// ThinklabPlugin resourceFinder = null;
// try {
// resourceFinder =
// (ThinklabPlugin)getManager().getPlugin(ext.getDeclaringPluginDescriptor().getId());
// } catch (PluginLifecycleException e) {
// throw new ThinklabValidationException("can't determine the plugin that created the gazetteer "+ id);
// }
//
// Properties p = new Properties();
// p.putAll(properties);
//
// /*
// * may point to a property file in the config dir
// */
// String pfile = getParameter(ext, "property-file");
// if (pfile != null) {
// File f = new File(resourceFinder.getConfigPath() + "/" + pfile);
// if (!f.exists()) {
//
// /*
// * exit silently, just print a warning
// */
// logger().warn("gazetteer " + id + " cannot find the property file " +
// pfile + "; initialization aborted");
// return;
// }
// Properties pp = new Properties();
// try {
// pp.load(new FileInputStream(f));
// } catch (Exception e) {
// throw new ThinklabIOException(e);
// }
// p.putAll(pp);
// }
//
// /*
// * can also specify properties inline
// */
// for (Extension.Parameter aext : ext.getParameters("property")) {
// String name = aext.getSubParameter("name").valueAsString();
// String value = aext.getSubParameter("value").valueAsString();
// p.setProperty(name, value);
// }
//
// log.info("creating gazetteer " + id);
// IGazetteer ret = (IGazetteer) getHandlerInstance(ext, "class");
// ret.initialize(p);
// gazetteers.put(id, ret);
// }
/**
* Load the gazetteers specified in the passed plugin and set them in the
* engine repository. In addition, look in plugin data dir for subdirectories
* of gazetters/. Any subdir in it is supposed to contain a gazetteer.properties
* file that will be used to initialize a PostgisGazetteer, and (optionally)
* a set of shapefiles that will be loaded if their
* creation date is more recent than the date of last access to the
* gazetteer.
*
* Must be called explicitly by plugins declaring gazetteers.
*
* @param pluginId
* @throws ThinklabException
* @throws PluginLifecycleException
*/
// public void loadGazetteers(String pluginId) throws ThinklabException {
// //
// // for (Extension ext : getPluginExtensions(pluginId, PLUGIN_ID, "gazetteer")) {
// // createGazetteer(ext, getProperties());
// // }
// }
//
// public void loadGazetteersFromDirectory(File dir) throws ThinklabException {
//
// File pdir = new File(dir + File.separator + "gazetteers");
//
// if (!(pdir.exists() && pdir.isDirectory()))
// return;
//
// String[] files = pdir.list();
//
// for (String file : files) {
// File gdir = new File(pdir + File.separator + file);
// if (gdir.isDirectory()) {
// IGazetteer gaz = readGazetteerFromDirectory(gdir);
// if (gaz != null) {
// gazetteers.put(file, gaz);
// }
// }
// }
// }
// private IGazetteer readGazetteerFromDirectory(File gdir) throws ThinklabException {
//
// // File props = new File(gdir + File.separator + "gazetteer.properties");
// IGazetteer ret = null;
//
// if (props.exists()) {
//
// Properties pp = new Properties();
// try {
// pp.load(new FileInputStream(props));
// } catch (Exception e) {
// throw new ThinklabIOException(e);
// }
//
// logger().info("loading gazetteer from " + gdir);
//
// ret = new PostgisGazetteer();
// ret.initialize(pp);
//
// long mtime = new Date().getTime();
// File lock = new File(gdir + File.separator + ".last_access");
//
// if (lock.exists()) {
// mtime = lock.lastModified();
// }
//
// for (String ff : gdir.list()) {
// if (ff.endsWith(".shp")) {
//
// File shp = new File(gdir + File.separator + ff);
// File lck = new File(MiscUtilities.changeExtension(shp.toString(), "lck"));
//
// if (!lck.exists()) {
//
// logger().info("adding gazetteer source " + ff);
//
// try {
// ret.importLocations(shp.toURI().toURL().toString(), null);
// } catch (MalformedURLException e) {
// throw new ThinklabValidationException(e);
// }
//
// try {
// FileUtils.touch(lck);
// } catch (IOException e) {
// throw new ThinklabIOException(e);
// }
// }
// }
// }
//
// try {
// FileUtils.touch(lock);
// } catch (IOException e) {
// throw new ThinklabIOException(e);
// }
// }
//
// return ret;
// }
// /**
// * Find a specific gazetteer by name, complain if not found
// *
// * @param id
// * @return
// * @throws ThinklabResourceNotFoundException
// */
// public IGazetteer requireGazetteer(String id) throws ThinklabResourceNotFoundException {
// IGazetteer ret = gazetteers.get(id);
// if (ret == null)
// throw new ThinklabResourceNotFoundException("gazetteer " + id + " is not registered");
// return ret;
// }
// /**
// * Find a specific gazetteer by name
// *
// * @param id
// * @return
// * @throws ThinklabResourceNotFoundException
// */
// public IGazetteer retrieveGazetteer(String id) {
// return gazetteers.get(id);
// }
/**
* 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;
}
/**
* Get the concept that will identify the domain of all spatial extents in this implementation.
* This type should be returned by IExtent.getDomainConcept() in all spatial extents capable of
* mediating to each other.
*
* @return space domain
*/
public IConcept SpatialDomain() {
return spaceDomain;
}
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);
}
}
}
}
public void persistSubject(ISubject obs, File file) {
// TODO Auto-generated method stub
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy