All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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);
                }
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy