version 1.1

This commit is contained in:
lpoulin 2024-10-09 15:06:56 +02:00
parent d81fb678e3
commit 836a412a6d
536 changed files with 63922 additions and 0 deletions

141
build/release.sh Normal file
View File

@ -0,0 +1,141 @@
#!/bin/bash
#
# Create release of the project, which means a tag on a separate branch, with a fixed version.
# NOTE : we expect this script to be in the following directory : ROOT_POM_DIR/build/
#
# Release version for the tag to create
releaseVersion=
# change SNAPSHOT version after release.
newSnapshot=
# A prefix for the temporary release branch
releaseBranchPrefix="release-"
# Keep original branch to come back to it after release.
sourceBranch=
# Original code from 'http://stackoverflow.com/questions/242538/unix-shell-script-find-out-which-directory-the-script-file-resides'
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=$(readlink -f "$BASH_SOURCE")
# Absolute path this script is in, thus /home/user/bin
SCRIPT_PATH=$(dirname "$SCRIPT")
###################### FUNCTIONS ############################
# Print help on standard output.
function printHelp {
echo "
Create a release tag for the current project. Release number will be something like X.x where X is major release version, and x is minor.
"
echo " WARNING : No check is done to ensure the project is well-formed (no compilation, test, syntax check, etc.)
"
echo " MANDATORY ARGUMENT : "
echo " -r --release-version : Version to set on new release."
echo " OPTIONAL ARGUMENT : "
echo " -s --snapshot-version : If set, and once the tag is done, the version on original branch is changed according to this argument value."
echo " -- help, -h : Print this help."
}
function printError() {
echo "
"
tput setaf 1; tput setab 7; tput bold;
echo "$*"
tput sgr0;
echo "
"
}
# Print the given parameters to standard error output, then exit the script in failure mode.
function error() {
printError "$*"
git checkout $sourceBranch # come back on the initial branch before exit
exit 1
}
# do the job
function execute {
# We place ourself in the script directory. It MUST BE into the git project directory.
cd $SCRIPT_PATH/..
# Save original branch name to go back to it at the end of the release.
sourceBranch="$(git symbolic-ref --short HEAD)"
echo "Original branch is $sourceBranch"
createTag
updateSnapshot
}
# Create the tag itself
function createTag {
echo "Create temporary branch for release $releaseVersion"
tmpReleaseBranch="$releaseBranchPrefix$releaseVersion"
git branch $tmpReleaseBranch || error "Cannot create a temporary branch for release creation."
git checkout $tmpReleaseBranch || error "Cannot access created branch $tmpReleaseBranch"
echo "Perform version update for release $releaseVersion"
mvn versions:set -DnewVersion=$releaseVersion || error "Cannot update project version to $releaseVersion"
echo "Commit release versions"
git commit -m "Project release $releaseVersion" -a || error "Cannot commit updated poms to the temporary release branch"
echo "Create tag named $releaseVersion"
git tag $releaseVersion $tmpReleaseBranch || error "Cannot create the tag $releaseVersion"
echo "Remove temporary release branch"
git checkout $sourceBranch || error "Cannot come back to the source branch $sourceBranch"
git branch -D $tmpReleaseBranch || printError "WARNING : Cannot delete temporary branch $tmpReleaseBranch. You will have to do it yourself ! "
}
# Update project version on initial branch
function updateSnapshot {
if [ ! -z "$releaseVersion" ]
then
echo "Update snapshot version $newSnapshot"
mvn versions:set -DnewVersion=$newSnapshot || error "Cannot update project version to $newSnapshot"
echo "Commit updated snapshot"
git commit -m "Snapshot update $newSnapshot" -a || error "Cannot commit updated poms to the source branch"
fi
}
########################### MAIN #############################
# ARGUMENT CHECK
while [ "$1" != "" ]; do
case $1 in
-r | --release-version ) shift
releaseVersion=$1
;;
-s | --snapshot-version ) shift
newSnapshot=$1
;;
# etc.
-h | --help ) printHelp
exit
;;
* ) printHelp
exit 1
;;
esac
shift
done
if [ -z "$releaseVersion" ]
then echo "ERROR: MINOR RELEASE VERSION IS NOT AN INTEGER. FOUND VALUE --> $releaseMinor" >&2; exit 1
fi
# We must ensure that no tag already exists with the same name.
tagDoublon="$(git tag -l|grep $releaseVersion)"
if [ ! -z "$tagDoublon" ]
then echo "ERROR : A tag already exists for the following version : $releaseVersion" >&2; exit 1
fi
echo "Execute for version $releaseVersion"
# Make release
execute

167
core/pom.xml Normal file
View File

@ -0,0 +1,167 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>fr.cenra.rhomeo</groupId>
<artifactId>rhomeo</artifactId>
<version>1.2-SNAPSHOT</version>
</parent>
<groupId>fr.cenra.rhomeo</groupId>
<artifactId>core</artifactId>
<packaging>jar</packaging>
<name>Core</name>
<dependencies>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- FTP -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<!-- formats -->
<dependency>
<groupId>org.geotoolkit</groupId>
<artifactId>geotk-feature-shapefile</artifactId>
<version>${geotoolkit.version}</version>
</dependency>
<dependency>
<groupId>org.geotoolkit</groupId>
<artifactId>geotk-feature-kml</artifactId>
<version>${geotoolkit.version}</version>
</dependency>
<dependency>
<groupId>org.geotoolkit</groupId>
<artifactId>geotk-feature-geojson</artifactId>
<version>${geotoolkit.version}</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.geotoolkit</groupId>
<artifactId>geotk-feature-store</artifactId>
<version>${geotoolkit.version}</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.geotoolkit</groupId>
<artifactId>geotk-feature-csv</artifactId>
<version>${geotoolkit.version}</version>
</dependency>
<dependency>
<groupId>org.geotoolkit</groupId>
<artifactId>geotk-client-wfs</artifactId>
</dependency>
<dependency>
<groupId>org.geotoolkit</groupId>
<artifactId>geotk-jaxp-xsd</artifactId>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
</dependency>
<!-- JAI -->
<dependency>
<groupId>javax.media</groupId>
<artifactId>jai_core</artifactId>
</dependency>
<dependency>
<groupId>javax.media</groupId>
<artifactId>jai_codec</artifactId>
</dependency>
<dependency>
<groupId>javax.media</groupId>
<artifactId>jai_imageio</artifactId>
</dependency>
<!-- TESTS -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.sis.core</groupId>
<artifactId>sis-utility</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,93 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
/**
*
* @author Alexis Manin (Geomatys)
*/
public class EditableIdentifiedObject implements IdentifiedObject {
protected final SimpleStringProperty nameProperty = new SimpleStringProperty();
protected final SimpleStringProperty remarksProperty = new SimpleStringProperty();
protected ObservableList<String> alias;
@Override
public String getName() {
return nameProperty.get();
}
public void setName(final String newName) {
nameProperty.set(newName);
}
public StringProperty nameProperty() {
return nameProperty;
}
@Override
public ObservableList<String> getAlias() {
if (alias == null) {
alias = FXCollections.observableArrayList();
}
return alias;
}
public void setAlias(final ObservableList<String> alias) {
this.alias = alias;
}
@Override
public String getRemarks() {
return remarksProperty.get();
}
public void setRemarks(final String remarks) {
remarksProperty.set(remarks);
}
public StringProperty remarksProperty() {
return remarksProperty;
}
}

View File

@ -0,0 +1,88 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api;
import java.util.Collection;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotBlank;
/**
* A simplified version of {@link org.opengis.referencing.IdentifiedObject} from
* GeoAPI. The aim is to ease manipulation by working only with Strings.
*
* Note : The {@link #getName() } is the identifier of the object.
* The {@link #getAlias() } is a list of alternative names to use as proper title.
* By default, the {@link #getTitle() } method returns the first alias available,
* or the name of the object if none is available.
*
* @author Alexis Manin (Geomatys)
*/
public interface IdentifiedObject {
/**
*
* @return Identifier of the object.
*/
@NotBlank
String getName();
/**
*
* @return Available titles / alternative names. Can be empty, but should never be null.
*/
@NotNull
Collection<String> getAlias();
/**
*
* @return A short description of the object.
*/
String getRemarks();
/**
*
* @return The most appropriate title from available aliases.
*/
default String getTitle() {
if (getAlias().isEmpty()) {
return getName();
}
return getAlias().iterator().next();
}
}

View File

@ -0,0 +1,123 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api;
import fr.cenra.rhomeo.core.RhomeoCore;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.logging.Level;
/**
* An object able to provide some title and description for given (or default) locales.
*
* This interface provides a default implementation based on the class {@link ResourceBundle}.
*
* According to the default implementation, the title and the description are the values
* of the keys {@link InternationalDescription#LABEL_KEY} and {@link InternationalDescription#DESCRIPTION_KEY} respectively.
*
* @author Samuel Andrés (Geomatys)
*/
public interface InternationalDescription {
/**
* The bundle key of the title value provided by the default implementation.
*/
String LABEL_KEY = "_label";
/**
* The bundle key of the description value provided by the default implementation.
*/
String DESCRIPTION_KEY = "_description";
/**
* Gets a title for the object.
*
* Default implementation returns the value of the {@link InternationalDescription#LABEL_KEY}
* key stored into the class {@link ResourceBundle} for the given {@link Locale}.
*
* @param locale
* @return
*/
default String getLabel(final Locale locale) {
try {
final ResourceBundle bundle = ResourceBundle.getBundle(getClass().getName(), locale);
return bundle.getString(LABEL_KEY);
} catch (Exception e) {
RhomeoCore.LOGGER.log(Level.WARNING, "Cannot find any title for this object.", e);
return "No title available";
}
}
/**
* Gets a description for the object.
*
* Default implementation returns the value of the {@link InternationalDescription#DESCRIPTION_KEY}
* key stored into the class {@link ResourceBundle} for the given {@link Locale}.
*
* @param locale
* @return
*/
default String getDescription(final Locale locale){
try {
final ResourceBundle bundle = ResourceBundle.getBundle(getClass().getName(), locale);
return bundle.getString(DESCRIPTION_KEY);
} catch (Exception e) {
RhomeoCore.LOGGER.log(Level.WARNING, "Cannot find any title for this object.", e);
return "No title available";
}
}
/**
* Shortcut method to avoid to specify locale parameter.
*
* Default implementation maps the {@link Locale#getDefault() } {@link Locale}.
*
* @return
*/
default String getLabel(){return getLabel(Locale.getDefault());}
/**
* Shortcut method to avoid to specify locale parameter.
*
* Default implementation maps the {@link Locale#getDefault() } {@link Locale}.
*
* @return
*/
default String getDescription(){return getDescription(Locale.getDefault());}
}

View File

@ -0,0 +1,249 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api;
import fr.cenra.rhomeo.core.RhomeoCore;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.StringJoiner;
import java.util.logging.Level;
import org.apache.sis.util.ArgumentChecks;
/**
* An object able to provide some resource strings for given (or default) locales.
*
* This interface provides a default implementation based on the class
* {@link ResourceBundle}. So, if you do not redefine its methods, make sure
* the {@link ResourceBundle} associated to the class exists.
*
* @author Samuel Andrés (Geomatys)
*/
public interface InternationalResource {
/**
* Default {@link InternationalResource} behaviour for a given class and a
* given locale, retrieving the associated {@link ResourceBundle}.
*
* The resource can be associated to a simple key or a complex one.
*
* The default implementation assumes the key parts are separated by a dot.
*
* For instance, let's assume the class my.package.MyClass implementq
* {@link InternationalResource} without overriding methods and is
* associated to the following resource bundle : /my/package/MyClass.properties
* which content is below :
*
* keyPart1.keyPart2=resource value
*
* The resource string "resource value" can be retrived from a my.package.MyClass
* instance, calling this method either specifying only one key part
* "keyPart1.keyPart2", or two key parts "keyPart1" and "keyPart2".
*
* @param c
* @param locale
* @param keyParts
* @return
*
* @throws IllegalArgumentException if the class parameter or the locale is null or no key is provided
* @throws MissingResourceException if no resource bundle for the specified class can be found or no object for the key can be found
*/
static String getResourceString(final Class<? extends InternationalResource> c, final Locale locale, final String... keyParts){
ArgumentChecks.ensureNonNull("class", c);
ArgumentChecks.ensureNonNull("locale", locale);
ArgumentChecks.ensureNonNull("keyParts", keyParts);
final ResourceBundle bundle;
try{
bundle = ResourceBundle.getBundle(c.getName(), locale);
}
catch(MissingResourceException e){
// Warns about InternationalResource default behaviour is not fulfilled.
RhomeoCore.LOGGER.log(Level.WARNING, "No resource found for the class {0}. Default {1} behaviour use resource bundle.",
new Object[]{c.getCanonicalName(), InternationalResource.class.getSimpleName()});
// Then, re-throw the exception
throw e;
}
final StringJoiner joiner = new StringJoiner(".");
for(final String keyPart : keyParts){
joiner.add(keyPart);
}
return bundle.getString(joiner.toString());
}
/**
*
* @param c
* @param keyParts
* @return
*
* @see InternationalResource#getResourceString(java.lang.Class, java.util.Locale, java.lang.String...) , with default locale.
*
* @throws IllegalArgumentException if the class parameter is null or no key is provided
* @throws MissingResourceException if no resource bundle for the specified class can be found or no object for the key can be found
*/
static String getResourceString(final Class<? extends InternationalResource> c, final String... keyParts){
return getResourceString(c, Locale.getDefault(), keyParts);
}
/**
* Use the default behaviour to retrieve the property mapping the given key for
* the given class before formating the result using the given arguments.
*
* Note the key cannot be split into dot-separated key parts because of the
* arguments vararg parameter.
*
* @param c
* @param locale
* @param key
* @param arguments
* @return
*
* @see InternationalResource#getResourceString(java.lang.Class, java.util.Locale, java.lang.String...) , with default locale.
*
* @throws IllegalArgumentException if the class parameter or the locale are null or no key is provided
* @throws MissingResourceException if no resource bundle for the specified class can be found or no object for the key can be found
*/
static String getFormatedResourceString(final Class<? extends InternationalResource> c, final Locale locale, final String key, final Object... arguments){
return MessageFormat.format(getResourceString(c, locale, key), arguments);
}
/**
*
* Same behaviour as {@link InternationalResource#getFormatedResourceString(java.lang.Class, java.util.Locale, java.lang.String, java.lang.Object...) },
* with default locale.
*
* @param c
* @param key
* @param arguments
* @return
*
* @see InternationalResource#getResourceString(java.lang.Class, java.lang.String...).
*
* @throws IllegalArgumentException if no key is provided
* @throws MissingResourceException if no object for the key can be found
*/
static String getFormatedResourceString(final Class<? extends InternationalResource> c, final String key, final Object... arguments){
return MessageFormat.format(getResourceString(c, key), arguments);
}
/**
* Retrieves a string resource for the given locale.
*
* The resource can be associated to a simple key or a complex one.
*
* The default implementation assumes the key parts are separated by a dot.
*
* For instance, let's assume the class my.package.MyClass implementq
* {@link InternationalResource} without overriding methods and is
* associated to the following resource bundle : /my/package/MyClass.properties
* which content is below :
*
* keyPart1.keyPart2=resource value
*
* The resource string "resource value" can be retrived from a my.package.MyClass
* instance, calling this method either specifying only one key part
* "keyPart1.keyPart2", or two key parts "keyPart1" and "keyPart2".
*
* @param keyParts
* @param locale
* @return
*
* @throws IllegalArgumentException if the locale is null or no key is provided
* @throws MissingResourceException if no resource bundle can be found for the class of the current object or no object for the key can be found
*/
default String getResourceString(final Locale locale, final String... keyParts) {
return getResourceString(getClass(), locale, keyParts);
}
/**
* Shortcut method to avoid to specify locale parameter.
*
* Default implementation maps the {@link Locale#getDefault() } {@link Locale}.
*
* @param keyParts
* @return
*
* @see InternationalResource#getResourceString(java.util.Locale, java.lang.String...) ...).
*
* @throws IllegalArgumentException if no key is provided
* @throws MissingResourceException if no resource bundle can be found for the class of the current object or no object for the key can be found
*/
default String getResourceString(final String... keyParts) {
return getResourceString(Locale.getDefault(), keyParts);
}
/**
* Retrieves a string resource for the given locale and formats the result
* using the given arguments.
*
* @param locale
* @param key
* @param arguments
* @return
*
* @see InternationalResource#getFormatedResourceString(java.lang.Class, java.util.Locale, java.lang.String, java.lang.Object...)
*
* @throws IllegalArgumentException if the locale is null or no key is provided
* @throws MissingResourceException if no resource bundle for the class can be found for the class of the current object or no object for the key can be found
*/
default String getFormatedResourceString(final Locale locale, final String key, final Object... arguments) {
return getFormatedResourceString(getClass(), locale, key, arguments);
}
/**
* Shortcut method to avoid to specify locale parameter.
*
* Default implementation maps the {@link Locale#getDefault() } {@link Locale}.
*
* @param key
* @param arguments
* @return
*
* @see InternationalResource#getFormatedResourceString(java.util.Locale, java.lang.String, java.lang.Object...) ...).
*
* @throws IllegalArgumentException if no key is provided
* @throws MissingResourceException if no resource bundle for the class can be found for the class of the current object or no object for the key can be found
*/
default String getFormatedResourceString(final String key, final Object... arguments) {
return getFormatedResourceString(Locale.getDefault(), key, arguments);
}
}

View File

@ -0,0 +1,138 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.sis.util.logging.Logging;
/**
* A comparable to hold version values and allow for comparison between them.
* This object will try to split an input string to extract a suite of digits,
* which will compose the version. The weight of each digit in the suit is
* determined by its encounter order in the string. First encountered are priorized
* over the last ones.
*
* Ex : the String "1.2.3" will result in the following digit suite : 1 2 3,
* where 1 is the major version, 2 the minor version, and 3 the update version.
*
* @author Alexis Manin (Geomatys)
*/
public class Version implements Comparable<Version> {
private static final Logger LOGGER = Logging.getLogger("fr.cenra.core");
final String stringVersion;
final int[] version;
public Version(String inputVersion) {
stringVersion = inputVersion == null? "" : inputVersion;
String[] splitted = stringVersion.split("[^\\d]+");
if (splitted.length < 1) {
version = new int[0];
} else {
int[] tmpVersion = new int[splitted.length];
try {
for (int i = 0; i < splitted.length; i++) {
tmpVersion[i] = Integer.parseInt(splitted[i]);
}
} catch (NumberFormatException e) {
LOGGER.log(Level.WARNING, "Input version cannot be split as a suite of numbers : "+ inputVersion, e);
tmpVersion = new int[0];
}
version = tmpVersion;
}
}
/**
* {@inheritDoc}
*/
@Override
public int compareTo(Version o) {
if (o == null) {
return -1;
} else if (version.length < 1 || o.version.length < 1) {
return stringVersion.compareTo(o.stringVersion);
} else {
final int maxIndex = Math.min(version.length, o.version.length);
int comparison;
for (int i = 0 ; i < maxIndex ; i++) {
comparison = version[i] - o.version[i];
if (comparison != 0) {
return comparison;
}
}
// if we arrived here, common version parts are equals. The longest should be the last version.
return version.length - o.version.length;
}
}
/**
* A string representing current application version.
*
* @return current application version.
*/
@Override
public String toString() {
return stringVersion;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Version) {
return compareTo((Version) obj) == 0;
}
return false;
}
@Override
public int hashCode() {
if (version.length < 1) {
return Arrays.hashCode(version);
} else {
return stringVersion.hashCode();
}
}
}

View File

@ -0,0 +1,98 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.annotations;
import fr.cenra.rhomeo.core.validation.ReferenceValidator;
import java.beans.Introspector;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.function.Predicate;
import javax.validation.Constraint;
import javax.validation.Payload;
/**
* Describe a link between the source (annotated) property, and another one,
* located in the specified class. This annotation should be positioned on a
* property getter.
*
* IMPORTANT : we talk about properties in java.beans term, which means a data
* accessible via public getter. The property name is defined by the terms used
* to define the getter name. For more information, see {@link Introspector}
* documentation.
*
* IMPORTANT : This annotation is also a validation flag, to ensure the property
* value is referring to an existing object. However, it tests NEITHER nullity
* NOR blankness.
*
* @author Alexis Manin (Geomatys)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Constraint(validatedBy = ReferenceValidator.class)
@Documented
public @interface RefersTo {
String message() default "Aucune correspondance trouvée";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
*
* @return The class in which is located the property we're refering to.
*/
Class type();
/**
*
* @return The name of the property refered in {@link #type() }.
*/
String property();
/**
* Provides a predicate to keep only objects which respects it.
* @return A class (should not be an interface nor abstract one) from which
* a {@link Predicate} can be created using {@link Class#newInstance() }.
*
*/
Class<? extends Predicate> filterClass() default Predicate.class;
}

View File

@ -0,0 +1,133 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
import fr.cenra.rhomeo.api.process.Indicator;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Abstract implementation of {@link Protocol}.
*
* @author Samuel Andrés (Geomatys)
*/
public abstract class AbstractProtocol implements Protocol {
private final String name;
private final String remarks;
protected final Class<? extends Statement> dataType;
protected final Set<ReferenceDescription> referenceTypes = new HashSet<>();
@Autowired // Need all subclasses to be Spring components.
protected List<Indicator> indicators;
public AbstractProtocol(final String name, final String remarks, final Class<? extends Statement> dataType, final ReferenceDescription... referenceTypes){
this.name = name;
this.remarks = remarks;
this.dataType = dataType;
if (referenceTypes != null && referenceTypes.length > 0) {
for (final ReferenceDescription referenceType : referenceTypes) {
if (referenceType != null) {
this.referenceTypes.add(referenceType);
}
}
}
}
@Override
public Class<? extends Statement> getDataType() {
return dataType;
}
@Override
public Set<ReferenceDescription> getReferenceTypes() {
return referenceTypes;
}
@Override
public String getName() {
return name;
}
@Override
public Collection<String> getAlias() {
return Collections.singleton(name);
}
@Override
public String getRemarks() {
return remarks;
}
/**
* Default implementation checking at least one {@link Indicator} exists
* referencing this {@link Protocol} instance and compatible with the given
* {@link Site} zone type code.
*
* @param site
* @return
*/
@Override
public boolean isCompatible(Site site) {
if (site == null) {
return false;
}
for(final Indicator indicator : indicators){
if(indicator.getProtocol()==this && indicator.getZoneTypeCodes() != null
&& indicator.getZoneTypeCodes().contains(site.getZoneType())) return true;
}
return false;
}
@Override
public int compareTo(final Protocol p){
return this.getName().compareTo(p.getName());
}
@Override
public String toString() {
return "AbstractProtocol{" + "name=" + name + ", remarks=" + remarks + '}';
}
}

View File

@ -0,0 +1,132 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
import fr.cenra.rhomeo.api.Version;
import java.time.ZonedDateTime;
import java.util.HashMap;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javax.validation.constraints.NotNull;
import org.apache.sis.util.ArgumentChecks;
/**
* Contains all data from the seizure context :
* - Target Site
* - Input protocol
* - References versions
* - Additional user data
*
* @author Alexis Manin (Geomatys)
*/
public class DataContext {
private final Site site;
private final Protocol protocol;
private final SimpleObjectProperty<ZonedDateTime> dateProperty;
private final ObservableMap<Class<Reference>, Version> references;
private final ObservableMap<String, Object> userData;
public DataContext(Site site, Protocol protocol) {
ArgumentChecks.ensureNonNull("Input site", site);
ArgumentChecks.ensureNonNull("Source protocol", protocol);
this.site = site;
this.protocol = protocol;
dateProperty = new SimpleObjectProperty<>(ZonedDateTime.now());
references = FXCollections.observableMap(new HashMap<>());
userData = FXCollections.observableMap(new HashMap<>());
}
/**
*
* @return Protocol currently used for seizure.
*/
@NotNull
public Protocol getProtocol() {
return protocol;
}
/**
*
* @return Site chosen for current session.
*/
@NotNull
public Site getSite() {
return site;
}
/**
*
* @return References used for this seizure, along with the chosen version for
* each of them.
*/
@NotNull
public ObservableMap<Class<Reference>, Version> getReferences() {
return references;
}
/**
*
* @return References used for this seizure, along with the chosen version for
* each of them.
*/
@NotNull
public ObservableMap<String, Object> getUserData() {
return userData;
}
/**
*
* @return Creation date of this dataset.
*/
public ZonedDateTime getDate() {
return dateProperty.get();
}
/**
* Set given date as creation date of this dataset.
* @param date The date to use as new creation date.
*/
public void setDate(final ZonedDateTime date) {
dateProperty.set(date);
}
}

View File

@ -0,0 +1,589 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
import fr.cenra.rhomeo.core.RhomeoCore;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.logging.Level;
import javafx.beans.Observable;
import javafx.beans.property.Property;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import javafx.collections.transformation.SortedList;
import javafx.util.Callback;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.apache.sis.util.ArgumentChecks;
/**
* Data filled or imported in application.
*
* IMPORTANT : Avoid modifying {@link #getItems() } list outside of FX thread,
* because UI could use filtered / sorted lists based on it.
*
* @author Alexis Manin (Geomatys)
* @param <T> Type of statements pointed by this dataset.
*/
public class Dataset<T extends Statement> {
private final Protocol protocol;
/**
* Keep a set of computed tracking points to speed up research and updates.
*
* TODO : replace with a map whose key is the tracking point, and value is the count of attached statements.
*/
private ObservableSet<TrackingPoint> internalPoints;
/**
* List of tracking points exposed to user.
*/
private ObservableList<TrackingPoint> points;
/**
* Seized statements.
*/
private final ObservableList<T> items;
/**
* When an item date change, this listener update available tracking point list.
*/
private final ChangeListener<LocalDate> dateListener;
/**
* When an item name change, this listener update available tracking point list.
*/
private final ChangeListener<String> nameListener;
/**
* On a change in {@link #getItems() }, this listener check added/removed
* objects to update tracking point list.
*/
private final TrackingPointListener tPointListener;
private SetToListListener mirroring;
public Dataset(final Protocol target) {
ArgumentChecks.ensureNonNull("Target protocol", target);
protocol = target;
final Callback<T, Observable[]> extractor = createCallback();
if (extractor == null) {
items = FXCollections.observableArrayList();
} else {
items = FXCollections.observableArrayList(extractor);
}
dateListener = (obs, oldDate, newDate) -> {
if (obs instanceof Property) {
Object bean = ((Property)obs).getBean();
if (bean instanceof Statement) {
final String name = ((Statement)bean).getTrackingPoint();
if (name != null) {
if (oldDate != null) {
final TrackingPoint p = protocol.createTrackingPoint(name, oldDate);
if (getSubSet(p).isEmpty()) {
internalPoints.remove(p);
}
}
if (newDate != null) {
internalPoints.add(protocol.createTrackingPoint(name, newDate));
}
}
}
}
};
nameListener = (obs, oldName, newName) -> {
if (obs instanceof Property) {
Object bean = ((Property) obs).getBean();
if (bean instanceof Statement) {
final LocalDate date = ((Statement) bean).getDate();
if (date != null) {
if (oldName != null) {
final TrackingPoint p = protocol.createTrackingPoint(oldName, date);
if (getSubSet(p).isEmpty()) {
internalPoints.remove(p);
}
}
if (newName != null) {
internalPoints.add(protocol.createTrackingPoint(newName, date));
}
}
}
}
};
tPointListener = new TrackingPointListener();
}
/**
*
* @return List of associated data. Can be empty, but never null.
*/
@NotNull
@Valid
public synchronized ObservableList<T> getItems() {
return items;
}
@Valid
public Protocol getProtocol() {
return protocol;
}
private Callback<T, Observable[]> createCallback() {
final ArrayList<Method> extractors = new ArrayList<>();
try {
final BeanInfo info = Introspector.getBeanInfo(protocol.getDataType(), Object.class);
MethodDescriptor[] descs = info.getMethodDescriptors();
for (final MethodDescriptor desc : descs) {
final Method method = desc.getMethod();
if (method.getParameterCount() < 1 && Observable.class.isAssignableFrom(method.getReturnType())) {
method.setAccessible(true);
extractors.add(method);
}
}
} catch (Exception e) {
RhomeoCore.LOGGER.log(Level.WARNING, "Cannot analyze datatype for protocol ".concat(protocol.getName()), e);
}
if (extractors.isEmpty()) {
return null;
}
return input -> {
final ArrayList<Object> result = new ArrayList<>(extractors.size());
for (int i = 0 ; i < extractors.size() ; i++) {
try {
result.add((Observable) extractors.get(i).invoke(input));
} catch (ReflectiveOperationException e) {
RhomeoCore.LOGGER.log(Level.WARNING, "Cannot extract a property !", e);
}
}
return result.toArray(new Observable[result.size()]);
};
}
@NotNull
public synchronized ObservableList<TrackingPoint> getTrackingPoints() {
if(points==null) {
final HashSet<TrackingPoint> tmpSet = new HashSet<>();
if (items != null) {
String name;
LocalDate date;
for (final Statement statement : items) {
name = statement.getTrackingPoint();
date = statement.getDate();
if (name != null && statement.getDate() != null) {
final TrackingPoint p = protocol.createTrackingPoint(name, date);
p.setRemarks(statement.getRemarks());
tmpSet.add(p);
}
}
}
internalPoints = FXCollections.synchronizedObservableSet(FXCollections.observableSet(tmpSet));
points = FXCollections.observableArrayList(tmpSet);
removeListeners();
addListeners();
}
return FXCollections.unmodifiableObservableList(points);
}
/**
* Query an unmodifiable view of the items contained in this dataset matching
* the given tracking point. All modifications in {@link #getItems() } will
* be mirrored in the returned view.
*
* If the given point is null, a list of all statements which are not bound
* to any point is returned.
*
* @param filter The tracking point to get data for.
* @return items matching a given tracking point (contained in it).
*/
@NotNull
public synchronized ObservableList<T> getSubSet(final TrackingPoint... filter) {
return getSubSet(filter == null || (filter.length == 1 && filter[0] == null)? null : Arrays.asList(filter));
}
public synchronized ObservableList<T> getSubSet(Collection<TrackingPoint> filter) {
final Predicate<T> predicate;
if (filter == null || filter.isEmpty()) {
predicate = input -> {
return input.getTrackingPoint() == null || input.getDate() == null;
};
} else if (filter.size() == 1) {
final TrackingPoint tp = filter.iterator().next();
final String filterName = tp.getName();
final LocalDate filterStart = tp.getYearStart().minusDays(1);
final LocalDate filterEnd = tp.getYearEnd().plusDays(1);
predicate = input -> {
return filterName.equals(input.getTrackingPoint())
&& input.getDate() != null
&& input.getDate().isAfter(filterStart)
&& input.getDate().isBefore(filterEnd);
};
} else {
// Remove doublons
if (!(filter instanceof Set)) {
filter = new HashSet(filter);
}
// Multiple tracking point filter. For each name, we build a set of
// date range, for each tracking with the same name but different year range.
final HashMap<String, List<LocalDate[]>> filters = new HashMap<>(filter.size());
for (final TrackingPoint tp : filter) {
filters.computeIfAbsent(tp.getName(), str -> new ArrayList())
.add(new LocalDate[]{tp.getYearStart().minusDays(1), tp.getYearEnd().plusDays(1)});
}
predicate = input -> {
if (input.getTrackingPoint() == null || input.getDate() == null)
return false;
final List<LocalDate[]> dates = filters.get(input.getTrackingPoint());
if (dates == null)
return false;
for (final LocalDate[] range : dates) {
if (input.getDate().isAfter(range[0]) &&
input.getDate().isBefore(range[1]))
return true;
}
return false;
};
}
return getItems().filtered(predicate);
}
public boolean deletePoints(final Collection<TrackingPoint> points) {
final ArrayList<TrackingPoint> defCopy = new ArrayList<>(points);
return items.removeIf(item -> {
for (final TrackingPoint tp : defCopy) {
if (tp.getName().equals(item.getTrackingPoint()) &&
item.getDate() != null &&
(item.getDate().isEqual(tp.getYearStart()) || item.getDate().isAfter(tp.getYearStart())) &&
(item.getDate().isEqual(tp.getYearEnd()) || item.getDate().isBefore(tp.getYearEnd()))) {
return true;
}
}
return false;
});
}
/**
* Remove listeners in charge of updating tracking point list.
*/
private void removeListeners() {
if (mirroring != null && internalPoints != null) {
internalPoints.removeListener(mirroring);
}
if (items != null) {
items.removeListener(tPointListener);
items.forEach(item -> {
item.dateProperty().removeListener(dateListener);
item.trackingPointProperty().removeListener(nameListener);
});
}
}
/**
* Add listeners on items and tracking points. The aim is to check statement
* changes to update tracking point list accordingly.
*/
private void addListeners() {
if (items != null && points != null) {
mirroring = new SetToListListener(points);
internalPoints.addListener(mirroring);
items.forEach(item -> {
item.dateProperty().addListener(dateListener);
item.trackingPointProperty().addListener(nameListener);
});
items.addListener(tPointListener);
}
}
/**
* Check changes on statement list, to update available tracking points
* according to dataset content.
*/
private class TrackingPointListener implements ListChangeListener<Statement> {
@Override
public void onChanged(Change<? extends Statement> c) {
final HashSet<TrackingPoint> toRemove = new HashSet<>();
final HashSet<TrackingPoint> toAdd = new HashSet<>();
TrackingPoint point;
while (c.next()) {
if (c.wasRemoved()) {
for (Statement remitem : c.getRemoved()) {
remitem.dateProperty().removeListener(dateListener);
remitem.trackingPointProperty().removeListener(nameListener);
point = createTrackingPoint(remitem);
if (point != null) {
toRemove.add(point);
}
}
}
if (c.wasAdded()) {
for (Statement additem : c.getAddedSubList()) {
additem.dateProperty().addListener(dateListener);
additem.trackingPointProperty().addListener(nameListener);
point = createTrackingPoint(additem);
if (point != null) {
toRemove.remove(point);
toAdd.add(point);
}
}
}
}
// If there's still statements referencing removed statement tracking points, we keep them.
final Iterator<TrackingPoint> it = toRemove.iterator();
while (it.hasNext()) {
point = it.next();
if (!getSubSet(point).isEmpty())
it.remove();
}
internalPoints.removeAll(toRemove);
internalPoints.addAll(toAdd);
}
private TrackingPoint createTrackingPoint(final Statement statement) {
final String name = statement.getTrackingPoint();
final LocalDate date = statement.getDate();
if (name != null && date != null) {
final TrackingPoint p = protocol.createTrackingPoint(name, date);
p.setRemarks(statement.getRemarks());
return p;
} else {
return null;
}
}
}
/**
* Mirrors changes happening in a set to the parameterised list.
*/
private class SetToListListener implements SetChangeListener<TrackingPoint> {
private final List destination;
public SetToListListener(final List destination) {
this.destination = destination;
}
@Override
public void onChanged(Change<? extends TrackingPoint> c) {
if (c.wasRemoved()) {
destination.remove(c.getElementRemoved());
} else if (c.wasAdded()) {
destination.add(c.getElementAdded());
}
}
}
/**
* Search for the tracking point attached to given statement.
* @param data The statement to find parent tracking point for.
* @return A tracking point matching given parameter, or nothing.
*/
public Optional<TrackingPoint> findPoint(final Statement data) {
if (data == null)
return Optional.empty();
return findPoint(data.getTrackingPoint(), data.getDate());
}
/**
* Search for a tracking point whose name is equal to the given one, and validity
* period contains given date.
* @param name The name to get a tracking point for.
* @param date The date to get a tracking point for.
* @return A tracking point matching given parameters, or nothing.
*/
public Optional<TrackingPoint> findPoint(String name, final LocalDate date) {
if (!items.isEmpty() && date != null && name != null && !(name = name.trim()).isEmpty()) {
final ObservableList<TrackingPoint> tps = getTrackingPoints();
if (!tps.isEmpty()) {
for (final TrackingPoint tp : tps) {
if (tp.getName().equals(name) &&
(date.isAfter(tp.getYearStart()) || date.isEqual(tp.getYearStart())) &&
(date.isBefore(tp.getYearEnd()) || date.isEqual(tp.getYearEnd()))) {
return Optional.of(tp);
}
}
}
}
return Optional.empty();
}
public synchronized Map<TrackingPoint, List<T>> groupByPoint() {
final List<TrackingPoint> trackingPoints = getTrackingPoints().sorted((TrackingPoint o1, TrackingPoint o2) -> {
if (o1 == null) {
return o2 == null ? 0 : 1;
} else if (o2 == null) {
return -1;
}
final int nameOrder = o1.getName().compareTo(o2.getName());
if (nameOrder != 0) {
return nameOrder;
}
return o1.getYear() - o2.getYear();
});
final Map<TrackingPoint, List<T>> statements = new HashMap<>();
final SortedList<T> sortedSet = getItems().sorted((Comparator) ORDER_BY_POINT);
final Iterator<TrackingPoint> tit = trackingPoints.iterator();
final Iterator<T> sit = sortedSet.iterator();
if (tit.hasNext()) {
TrackingPoint tp = tit.next();
Predicate predicate = new BelongsToTrackingPoint(tp);
ArrayList<T> ptList = new ArrayList<>();
statements.put(tp, ptList);
while (sit.hasNext()) {
final T st = sit.next();
if (!predicate.test(st)) {
if (tit.hasNext()) {
tp = tit.next();
predicate = new BelongsToTrackingPoint(tp);
ptList = new ArrayList<>();
statements.put(tp, ptList);
} else {
break;
}
}
ptList.add(st);
}
}
return statements;
}
/**
* Check if the given statement belongs to given tracking point.
* @param point The point to test
* @param statement The statement to test against input point.
* @return True if given statement belongs to given tracking point, false otherwise.
*/
public static boolean belongToPoint(final TrackingPoint point, final Statement statement) {
final LocalDate date = statement.getDate();
if (date == null || statement.getTrackingPoint() == null) {
return false;
}
return (point.getName().equals(statement.getTrackingPoint())
&& (date.isAfter(point.getYearStart()) || date.isEqual(point.getYearStart()))
&& (date.isBefore(point.getYearEnd()) || date.isEqual(point.getYearEnd())));
}
private static class BelongsToTrackingPoint implements Predicate<Statement> {
final String name;
final LocalDate filterStart;
final LocalDate filterEnd;
private BelongsToTrackingPoint(final TrackingPoint tp) {
name = tp.getName();
filterStart = tp.getYearStart().minusDays(1);
filterEnd = tp.getYearEnd().plusDays(1);
}
@Override
public boolean test(Statement t) {
return name.equals(t.getTrackingPoint())
&& t.getDate() != null
&& t.getDate().isAfter(filterStart)
&& t.getDate().isBefore(filterEnd);
}
}
private static Comparator<? extends Statement> ORDER_BY_POINT = (s1, s2) -> {
final String t1 = s1.getTrackingPoint();
final String t2 = s2.getTrackingPoint();
int comp;
if (t1 == t2)
comp = 0;
else if (t1 != null)
comp = t1.compareTo(t2);
else
comp = 1;
if (comp == 0) {
final LocalDate d1 = s1.getDate();
final LocalDate d2 = s2.getDate();
if (d1 != d2) {
if (d1 == null)
comp = 1;
else
comp = d1.compareTo(d2);
}
}
return comp;
};
}

View File

@ -0,0 +1,56 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
import java.util.function.Function;
/**
* A simple function designed to wrap an object of type I into another of type
* O.
*
* @author Alexis Manin (Geomatys)
* @param <I> Type of the objects to wrap (parameter).
* @param <O> Type of the wrapper object (returned).
*/
public interface ObjectWrapper<I, O> extends Function<I, O> {
Class<I> getInputType();
Class<O> getOutputType();
}

View File

@ -0,0 +1,104 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
import fr.cenra.rhomeo.api.IdentifiedObject;
import fr.cenra.rhomeo.api.process.Indicator;
import java.time.LocalDate;
import java.util.Set;
import javax.validation.constraints.NotNull;
/**
* Defines a scientific protocol designed to hold a specific set of measures.
*
* @author Alexis Manin (Geomatys)
*/
public interface Protocol extends IdentifiedObject, Comparable<Protocol> {
/**
*
* @return {@link Statement} class defining measure content. Must not be null.
*/
@NotNull
Class<? extends Statement> getDataType();
/**
*
* @return Set of reference types needed to complete seizure / processing of
* the protocol measures. Should never be null, but can be empty.
*/
@NotNull
Set<ReferenceDescription> getReferenceTypes();
/**
*
* @param site A site to analyze.
* @return True if we can process {@link Indicator}s associated to the current
* protocol with given site. False otherwise.
*/
boolean isCompatible(Site site);
/**
* Create a tracking point with the provided name, whose year contains the
* given date. This method is useful when you've got a {@link Statement},
* and want to know which stating year owns it.
*
* @param name The name of the tracking point.
* @param statementDate A date to get matching year for.
* @return A new tracking point. Never null.
*/
default TrackingPoint createTrackingPoint(final String name, final LocalDate statementDate) {
return new TrackingPoint(name, statementDate.getYear());
}
/**
* Create a new tracking point named as specified, on the given year. A year
* is not always a calendar year. It can go between two chosen dates.
*
* Ex : You can specify that year 2015 goes from September, 1st of 2014 to
* August, 31th of 2015
*
* @param name The name to set on the new tracking point.
* @param year The year to affect to the tracking point.
* @return A new tracking point. Never null.
*/
default TrackingPoint createTrackingPoint(final String name, final int year) {
return new TrackingPoint(name, year);
}
}

View File

@ -0,0 +1,46 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
/**
*
* @author Alexis Manin (Geomatys)
*/
public interface Reference {
}

View File

@ -0,0 +1,54 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
import fr.cenra.rhomeo.api.IdentifiedObject;
/**
* Contains generic information about a specific {@link Reference} implementation.
* There should be a unique instance for each type of reference available in application.
*
* @author Alexis Manin (Geomatys)
* @param <T> The type of reference described by this object.
*/
public interface ReferenceDescription<T extends Reference> extends IdentifiedObject {
Class<T> getReferenceType();
}

View File

@ -0,0 +1,66 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
import com.vividsolutions.jts.geom.MultiPolygon;
import fr.cenra.rhomeo.api.IdentifiedObject;
import javax.validation.constraints.NotNull;
/**
*
* @author Alexis Manin (Geomatys)
*/
public interface Site extends IdentifiedObject, Comparable<Site> {
@NotNull
MultiPolygon getGeometry();
String getCountyCode();
String getReferent();
String getOrganization();
String getZoneType();
String getOdonateType();
String getOrthoptereType();
}

View File

@ -0,0 +1,122 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
import fr.cenra.rhomeo.api.InternationalDescription;
import fr.cenra.rhomeo.api.InternationalResource;
import java.time.LocalDate;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javax.validation.constraints.NotNull;
/**
* Represents an entry in a {@link Dataset}. It's an entity containing several
* physical measures.
*
* Note : we force overriding of {@link #equals(java.lang.Object) } and {@link #hashCode()
* } methods, because statements will be compared in application to remove
* potential doublons.
*
* @author Alexis Manin (Geomatys)
*/
public abstract class Statement implements InternationalResource, InternationalDescription {
protected final SimpleStringProperty trackingPoint = new SimpleStringProperty(this, "trackingPoint");
protected final SimpleObjectProperty<LocalDate> date = new SimpleObjectProperty<>(this, "date");
protected final StringProperty remarksProperty = new SimpleStringProperty();
protected Statement() {}
protected Statement(final String trackingPoint, final LocalDate date) {
this.trackingPoint.set(trackingPoint);
this.date.set(date);
}
protected Statement(final String trackingPoint, final LocalDate date, final String remarks) {
this(trackingPoint, date);
this.remarksProperty.set(remarks);
}
@NotNull
public LocalDate getDate() {
return date.get();
}
public void setDate(final LocalDate date) {
this.date.set(date);
}
public ObjectProperty<LocalDate> dateProperty() {
return date;
}
public String getTrackingPoint() {
return trackingPoint.get();
}
public void setTrackingPoint(final String tPoint) {
trackingPoint.set(tPoint);
}
public StringProperty trackingPointProperty() {
return trackingPoint;
}
public String getRemarks() {
return remarksProperty.get();
}
public void setRemarks(final String remarks) {
this.remarksProperty.set(remarks);
}
public StringProperty remarksProperty() {
return remarksProperty;
}
@Override
public abstract boolean equals(Object obj);
@Override
public abstract int hashCode();
}

View File

@ -0,0 +1,210 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
import fr.cenra.rhomeo.api.IdentifiedObject;
import fr.cenra.rhomeo.api.InternationalResource;
import fr.cenra.rhomeo.core.RhomeoCore;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.time.LocalDate;
import java.util.Collection;
import java.util.Collections;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.apache.sis.util.ArgumentChecks;
/**
* A point on which a series of statements have been done. The point is
* identified by a name and the year on which statements have been done.
*
* It's an (almost) immutable object, because its just a reflection of
* statements name and date property, to use grouping. Moreover, introducing
* points with null year would be problematic for computing work flow. The only
* editable attribute is the remarks property, as it's not a defining data.
*
* @author Alexis Manin (Geomatys)
*/
public final class TrackingPoint implements IdentifiedObject, InternationalResource, Externalizable {
protected static final long serialVersionUID = 1L;
private String name;
private int year;
private LocalDate yearStart;
private LocalDate yearEnd;
private final StringProperty remarks;
public TrackingPoint() {
this("default", 2016);
}
public TrackingPoint(final String name, final int year) {
this(name, year, null, null);
}
public TrackingPoint(final String name, final LocalDate yearStart, final LocalDate yearEnd) {
this(
name,
// Try to find a valid year from input dates.
yearEnd != null ? yearEnd.getYear() : yearStart != null ? yearStart.getYear() : LocalDate.now().getYear(),
yearStart,
yearEnd
);
}
public TrackingPoint(String name, final int year, LocalDate yearStart, LocalDate yearEnd) {
if (name == null || (name = name.trim()).isEmpty()) {
throw new IllegalArgumentException("Given name is blank !");
}
this.name = name;
this.remarks = new SimpleStringProperty();
if (yearStart == null) {
if (yearEnd != null) {
yearStart = yearEnd.minusYears(1).plusDays(1);
} else {
yearStart = LocalDate.of(year, 01, 01);
}
}
if (yearEnd == null) {
yearEnd = yearStart.plusYears(1).minusDays(1);
}
if (yearStart.isBefore(yearEnd)) {
this.yearStart = yearStart;
this.yearEnd = yearEnd;
} else {
this.yearStart = yearEnd;
this.yearEnd = yearStart;
}
ArgumentChecks.ensureBetween("Year", this.yearStart.getYear(), this.yearEnd.getYear(), year);
this.year = year;
}
/**
*
* @return Year associated to this point.
*/
public int getYear() {
return year;
}
@Override
public int hashCode() {
int hash = 5;
hash = 71 * hash + this.year;
hash = 71 * hash + this.name.hashCode();
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TrackingPoint other = (TrackingPoint) obj;
return this.year == other.year && this.name.equals(other.name);
}
@Override
public String toString() {
return new StringBuilder("TrackingPoint{ name=").append(name).append(" year=").append(year).append(" }").toString();
}
public LocalDate getYearStart() {
return yearStart;
}
public LocalDate getYearEnd() {
return yearEnd;
}
@Override
public String getName() {
return name;
}
@Override
public String getRemarks() {
return remarks.get();
}
public void setRemarks(String remarks) {
this.remarks.set(remarks);
}
public StringProperty remarksProperty() {
return remarks;
}
@Override
public Collection<String> getAlias() {
return Collections.EMPTY_SET;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(year);
RhomeoCore.writeDate(yearStart, out);
RhomeoCore.writeDate(yearEnd, out);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = in.readUTF();
year = in.readInt();
yearStart = RhomeoCore.readDate(in);
yearEnd = RhomeoCore.readDate(in);
}
}

View File

@ -0,0 +1,100 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.Objects;
import java.util.function.Predicate;
/**
*
* @author Alexis Manin (Geomatys)
*/
public class TrackingPointFilter implements Predicate<Statement>, Serializable {
public TrackingPoint point;
public TrackingPointFilter() {}
public TrackingPointFilter(final TrackingPoint tp) {
point = tp;
}
@Override
public boolean test(Statement t) {
if (t == null)
return false;
if (!Objects.equals(point == null? null : point.getName(), t.getTrackingPoint()))
return false;
return checkDateRange(t.getDate());
}
private boolean checkDateRange(final LocalDate toTest) {
final LocalDate yearStart = (point == null)? null : point.getYearStart();
final LocalDate yearEnd = (point == null)? null : point.getYearEnd();
if (toTest == yearStart || toTest == yearEnd)
return true;
if (toTest == null)
return false;
if (yearStart != null && toTest.isBefore(yearStart))
return false;
return !(yearEnd != null && toTest.isAfter(yearEnd));
}
@Override
public int hashCode() {
int hash = 5;
hash = 89 * hash + Objects.hashCode(this.point);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final TrackingPointFilter other = (TrackingPointFilter) obj;
return Objects.equals(this.point, other.point);
}
}

View File

@ -0,0 +1,51 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.data;
import javax.validation.ConstraintViolation;
import javax.validation.Payload;
/**
* A simple interface defining that the attached {@link ConstraintViolation} is
* a warning, a recoverable error.
* @author Alexis Manin (Geomatys)
*/
public interface Warning extends Payload {
}

View File

@ -0,0 +1,106 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.preferences;
import fr.cenra.rhomeo.api.InternationalDescription;
import static fr.cenra.rhomeo.api.InternationalDescription.DESCRIPTION_KEY;
import fr.cenra.rhomeo.core.RhomeoCore;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.logging.Level;
import static fr.cenra.rhomeo.api.InternationalDescription.LABEL_KEY;
/**
* An interface to be implemented by preference keys which have an international
* description.
*
* This interface provides a default implementation usefull for {@link Enum}
* based implementations, assuming the {@link InternationalPreferenceKey#name() }
* method is implemented.
*
* @author Samuel Andrés (Geomatys)
*/
public interface InternationalPreferenceKey extends InternationalDescription, SimplePreferenceKey {
/**
* The name of the key.
*
* The purpose of this method is to provide a default implementation of
* {@link InternationalPreferenceKey#getLabel(java.util.Locale) } and
* {@link InternationalPreferenceKey#getDescription(java.util.Locale) } for
* {@link Enum} based implementations. In case the default implementation is
* overriden, the InternationalPreferenceKey#name() method implementation is
* free and even posibly useless.
*
* @see Enum#name()
*
* @return
*/
String name();
/**
*
* @param locale
* @return
*/
@Override
default String getDescription(final Locale locale) {
try {
return ResourceBundle.getBundle(getClass().getName(), locale).getString(name() + '.' + DESCRIPTION_KEY);
} catch (Exception e) {
RhomeoCore.LOGGER.log(Level.WARNING, "A bundle key cannot be found !", e);
return name().concat(" (No traduction available)");
}
}
/**
*
* @param locale
* @return
*/
@Override
default String getLabel(final Locale locale) {
try {
return ResourceBundle.getBundle(getClass().getName(), locale).getString(name() + '.' + LABEL_KEY);
} catch (Exception e) {
RhomeoCore.LOGGER.log(Level.WARNING, "A bundle key cannot be found !", e);
return name().concat(" (No traduction available)");
}
}
}

View File

@ -0,0 +1,49 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.preferences;
/**
* A simple interface to identify a {@link SimplePreferenceKey} as holding the
* value of a password. It's useful to adapt preference editors.
*
* @author Alexis Manin (Geomatys)
*/
public interface PasswordKey {
}

View File

@ -0,0 +1,86 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.preferences;
import fr.cenra.rhomeo.api.InternationalDescription;
import java.util.List;
/**
* A group of preferences.
*
* @author Samuel Andrés (Geomatys)
*
* @param <T> The type of preference keys.
*/
public interface PreferenceGroup <T> extends InternationalDescription, Comparable<PreferenceGroup> {
/**
*
* @param key
* @return The value of the preference associated to the given key in the preference group.
*/
String getPreference(T key);
/**
* Sets the value of the preference associated to the given key in the preference group.
*
* @param key
* @param value
*/
void setPreference(T key, String value);
/**
*
* @return The list of the keys of the preference group.
*/
List<T> getKeys();
/**
* Priority of this group, used for comparison.
*
* @return The priority of this group.
*/
int getPriority();
@Override
default int compareTo(PreferenceGroup o) {
return getPriority() - o.getPriority();
}
}

View File

@ -0,0 +1,60 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.preferences;
/**
* A simple interface for preference keys, providing a default value.
*
* @author Samuel Andrés (Geomatys)
*/
public interface SimplePreferenceKey {
/**
*
* @return The key used to retrive a preference value from the storage, for
* a given implementation.
*/
String getKey();
/**
*
* @return The default value associated to this preference key.
*/
String getDefaultValue();
}

View File

@ -0,0 +1,64 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.preferences;
import java.util.prefs.Preferences;
/**
* Abstract {@link PreferenceGroup} using {@link Preferences#userNodeForPackage(java.lang.Class) } storage.
*
* @author Samuel Andrés (Geomatys)
* @param <T>
*/
public abstract class UserPackagePreferenceGroup<T extends SimplePreferenceKey> implements PreferenceGroup<T>{
protected final Preferences prefs = Preferences.userNodeForPackage(getClass());
@Override
public String getPreference(final T key) {
return prefs.get(key.getKey(), key.getDefaultValue());
}
@Override
public void setPreference(final T key, String value){
if (value == null || (value = value.trim()).isEmpty())
prefs.remove(key.getKey());
else prefs.put(key.getKey(), value);
}
}

View File

@ -0,0 +1,128 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.process;
import fr.cenra.rhomeo.api.Version;
import fr.cenra.rhomeo.api.data.DataContext;
import fr.cenra.rhomeo.api.data.Reference;
import fr.cenra.rhomeo.api.data.ReferenceDescription;
import fr.cenra.rhomeo.core.Session;
import fr.cenra.rhomeo.core.data.ReferenceManager;
import fr.cenra.rhomeo.core.list.HumidZoneType;
import java.beans.IntrospectionException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
* @author Samuel Andrés (Geomatys)
*/
public abstract class AbstractIndicator implements Indicator {
private final String name;
private final String remarks;
public AbstractIndicator(final String name, final String remarks){
this.name = name;
this.remarks = remarks;
}
@Override
public String getName() {
return name;
}
@Override
public Collection<String> getAlias() {
return Collections.singleton(name);
}
@Override
public String getRemarks() {
return remarks;
}
/**
* Default implementation. If some {@link Indicator} subclass is not compatible
* with all {@link HumidZoneType}, this method MUST be overriden.
*
* @return a set containing all environment codes because this indicator is
* compatible with all of them.
*/
@Override
public Set<String> getZoneTypeCodes() {
final Set<String> result = new HashSet<>();
for(final HumidZoneType type : HumidZoneType.values()){
result.add(type.getCode());
}
return result;
}
@Override
public int compareTo(final Indicator i){
return this.getName().compareTo(i.getName());
}
@Override
public String toString() {
return "AbstractIndicator{" + "name=" + name + ", remarks=" + remarks + '}';
}
/**
* Get the references relative to the reference description parameter.
*
* => DEPLACER VERS ReferenceManager ?
*
* @param <T>
* @param description
* @return
* @throws IntrospectionException
*/
public static <T extends Reference> List<T> getReferences(final ReferenceDescription<T> description) throws IntrospectionException {
final ReferenceManager<T> referenceManager = ReferenceManager.getOrCreate(description);
final DataContext dc = Session.getInstance().getDataContext();
final Version versionSel = dc.getReferences().get(description.getReferenceType());
return referenceManager.getValues(versionSel != null ? versionSel : referenceManager.getInstalledVersions().iterator().next());
}
}

View File

@ -0,0 +1,69 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.process;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javafx.concurrent.Task;
/**
* Note : this process is made for the need of "complementary values" provided
* along indicator indices. However, this is still an abstract concept for now,
* and will need to be completed later.
*
* @author Alexis Manin (Geomatys)
*/
public interface AdditionalProcessSpi {
/**
*
* @return set of indicators for which this
*/
Set<Indicator> getTargets();
/**
*
* @param results Set of processes finished and succeeded, sorted by indicator.
*
* @return A task to compute complementary values over the given indicators,
* or nothing if we cannot extract enough data from input parameters.
*/
Optional<Task> prepareProcess(final Map<Indicator, Set<Process>> results);
}

View File

@ -0,0 +1,101 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.process;
import fr.cenra.rhomeo.api.IdentifiedObject;
import fr.cenra.rhomeo.api.data.Dataset;
import fr.cenra.rhomeo.api.data.Protocol;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.api.result.Index;
import fr.cenra.rhomeo.core.list.HumidZoneType;
import java.util.Set;
import javax.validation.constraints.NotNull;
/**
*
* @author Alexis Manin (Geomatys)
*/
public interface Indicator extends IdentifiedObject, Comparable<Indicator> {
/**
*
* @return Source protocol to work with. Defines input {@link Statement} type.
*/
@NotNull
Protocol getProtocol();
/**
* Create all processes needed to complete {@link Index} computation over
* current session {@link Dataset}.
* The statements inside the {@link Dataset} must match the type given by
* {@link Protocol#getDataType()} from this indicator {@link #getProtocol() }.
*
* Each process is designed to compute part or all of this indicator indices,
* on part or all of the input dataset. Which means developper is completely
* free to split his processes by data or by result. The only important point
* is that once all processes are over and succeeded, the union of their
* results represents all possible indices on the entire input dataset.
*
* @param ctx The context to use as input for the newly created processes.
*
* @return A set of processes ready to be run. No insurance is done on
* execution order nor on parallelisation. The processes returned here should
* be independants.
*/
@NotNull
Set<Process> createProcesses(ProcessContext ctx);
/**
*
* @return Name of the index considered as the main result for processes of
* this indicator.
*/
String getDefaultIndex();
/**
*
* @return The compatible environment codes to determine if the indicator is
* applicable to potential sites.
*
* @see Site
* @see HumidZoneType
*
*/
Set<String> getZoneTypeCodes();
}

View File

@ -0,0 +1,79 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.process;
import fr.cenra.rhomeo.api.data.TrackingPoint;
import fr.cenra.rhomeo.api.result.Index;
import java.util.Set;
import javafx.concurrent.Task;
import javax.validation.constraints.NotNull;
import org.apache.sis.util.ArgumentChecks;
/**
* A Calculation over a {@link TrackingPoint}. This class represents a process
* created from an {@link Indicator}.
*
* Note : For process independent of any indicator, you should prefer implementing
* {@link AdditionalProcessSpi}.
*
* @author Alexis Manin (Geomatys)
*/
public abstract class Process extends Task<Set<? extends Index>> {
protected final ProcessContext ctx;
protected Process(final ProcessContext ctx) {
ArgumentChecks.ensureNonNull("Process context", ctx);
this.ctx = ctx;
}
/**
* @return The indicator bound to this process. Should never be null.
*/
@NotNull
public abstract Indicator getIndicator();
/**
* @return The processing context which provide input data. Never null.
*/
@NotNull
public ProcessContext getContext() {
return ctx;
}
}

View File

@ -0,0 +1,214 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.process;
import fr.cenra.rhomeo.api.data.DataContext;
import fr.cenra.rhomeo.api.data.Dataset;
import fr.cenra.rhomeo.api.data.Statement;
import fr.cenra.rhomeo.api.data.TrackingPoint;
import fr.cenra.rhomeo.api.result.Index;
import java.io.Externalizable;
import java.io.Serializable;
import java.util.function.Predicate;
import javafx.beans.Observable;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javax.validation.constraints.NotNull;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import org.apache.sis.util.ArgumentChecks;
/**
* Contains all source needed for {@link Index} processing.
*
* IMPORTANT : Application attempts to save restore state of this object in case
* of unexpected shutdown. For the {@link #getFilter() } property to be saved,
* it MUST be {@link Serializable} or {@link Externalizable}.
*
* @author Alexis Manin (Geomatys)
*/
public class ProcessContext {
private final Dataset source;
private final DataContext dataCtx;
private final ObservableSet<Indicator> targetIndicators = FXCollections.observableSet();
private Dataset processData;
/**
* A filter which allow user to specify a filter for source to take in
* account.
*/
private final ObjectProperty<Predicate<? extends Statement>> filterProperty = new SimpleObjectProperty<>(this, "filter", null);
public ProcessContext(Dataset data, DataContext dataCtx) {
ArgumentChecks.ensureNonNull("Input data", data);
ArgumentChecks.ensureNonNull("Input data context", dataCtx);
this.source = data;
this.dataCtx = dataCtx;
targetIndicators.addListener((SetChangeListener) this::indicatorsChanged);
filterProperty.addListener(this::filterChanged);
}
/**
* Dataset to use for indicator computing. It contains imported data that
* has been selected by user for processing.
*
* Note : User selection is reflected by {@link #filterProperty}. But if it
* changes, previously returned dataset won't be updated. To get an up to
* date dataset, you must acquire a new dataset by calling this method
* again.
*
* @return A dataset built from user selected data. Every time this
* method is called, the same dataset is returned, until {@link #filterProperty()}
* change. At this time a new dataset is generated, updating contained data
* to match new filter.
*/
@NotNull
public synchronized Dataset getData() {
if (processData == null) {
if (filterProperty.get() == null)
processData = source;
else {
processData = new Dataset(source.getProtocol());
processData.getItems().addAll(source.getItems().filtered(filterProperty.get()));
}
}
return processData;
}
/**
* Return data seized by user as total dataset. This dataset should not be
* used for processing, as it represents all data imported, not the
* selection made after it.
*
* WARNING : Do not use this dataset for processing indicators. Use {@link #getData()
* }
* instead.
*
* @return Dataset which has been used for building process data.
*/
@NotNull
public Dataset getSourceDataset() {
return source;
}
/**
*
* @return Seizure context. Contains chosen site and protocol for computing.
*/
@NotNull
public DataContext getDataCtx() {
return dataCtx;
}
/**
*
* @return a set of tracking points, never {@code null}.
*/
@NotNull
public ObservableList<TrackingPoint> getTrackingPoints() {
return FXCollections.unmodifiableObservableList(getData().getTrackingPoints());
}
@NotNull
public ObservableSet<Indicator> getIndicators() {
return targetIndicators;
}
/**
* Allow user to filter source dataset. The filter will be applied when
* calling {@link #getData() }. It serves to decimate data used by indicator
* processes.
*
* @return Currently configured filter, or null if no filter is defined.
*/
public Predicate<? extends Statement> getFilter() {
return filterProperty.get();
}
/**
* Allow user to specify a filter to apply on top of tracking point
* decimation. If any, previous, configured filter is completely forgotten
* after this call. To chain previously configured filter with your new one,
* you can do something like :
* {@code
* final Predicate myFilter = createFilter(); // Prepare your stuff
* final Predicate previous = processContext.getFilter();
* processContext.setAdditionalFilter(previous.andThen(myFilter));
* }
*
* @param filter Filter to configure for current processing context.
*/
public void setFilter(Predicate<? extends Statement> filter) {
this.filterProperty.set(filter);
}
public ObjectProperty<Predicate<? extends Statement>> filterProperty() {
return filterProperty;
}
/**
* A listener which check added values for {@link #targetIndicators}. It
* automatically remove indicators not compatible with source site.
*
* @param c
*/
private void indicatorsChanged(final SetChangeListener.Change<Indicator> c) {
if (c.wasAdded() && !c.getElementAdded().getZoneTypeCodes().contains(getDataCtx().getSite().getZoneType())) {
c.getSet().remove(c.getElementAdded());
}
}
/**
* Trigger data refresh when filter property change. The process data will
* be regenerated on next call to {@link #getData() }
*
* @param obs
* @param oldFilter
* @param newFilter
*/
private synchronized void filterChanged(final Observable obs) {
processData = null;
}
}

View File

@ -0,0 +1,122 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.result;
import fr.cenra.rhomeo.api.data.TrackingPoint;
import java.util.Objects;
/**
* Abstract {@link Index} implementation which contains a value for a given entity
* (for instance a {@link TrackingPoint}) and references a specific {@link IndexSpi}.
*
* @author Samuel Andrés (Geomatys)
*
* @param <V> The type of index value.
* @param <B> The type of the index base the index value is associated with (for instance, a {@link TrackingPoint}).
*/
public abstract class AbstractParametrableIndex<V extends Number, B> implements Index<V> {
protected final V value;
protected final IndexSpi<V, B> indexSpi;
protected final B base;
protected final Class<B> baseClass;
public AbstractParametrableIndex(final V value, final IndexSpi<V, B> indexSpi, final B base, final Class<B> baseClass){
this.value = value;
this.indexSpi = indexSpi;
this.base = base;
this.baseClass = baseClass;
}
@Override
public V getValue() {
return value;
}
public B getBase(){
return base;
}
public Class<B> getBaseClass(){return baseClass;}
@Override
public abstract int getYear();
@Override
public IndexSpi<V, B> getSpi() {
return indexSpi;
}
@Override
public int hashCode() {
int hash = 3;
hash = 83 * hash + this.getYear();
hash = 83 * hash + Objects.hashCode(this.value);
hash = 83 * hash + Objects.hashCode(this.indexSpi);
hash = 83 * hash + Objects.hashCode(this.base);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final AbstractParametrableIndex<V, B> other = (AbstractParametrableIndex<V, B>) obj;
if (this.getYear() != other.getYear()) {
return false;
}
if (!Objects.equals(this.value, other.value)) {
return false;
}
if (!Objects.equals(this.indexSpi, other.indexSpi)) {
return false;
}
if (!Objects.equals(this.base, other.base)) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,48 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.result;
/**
* Complementary data computed from protocol data and indicator indices.
*
* @author Alexis Manin (Geomatys)
*/
public interface AdditionalValue {
}

View File

@ -0,0 +1,76 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.result;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Set;
/**
* A service whose aim is to serialize / deserialize {@link AdditionalValue }
* from file-system.
*
* @author Alexis Manin (Geomatys)
*/
public interface AdditionalValueMapper {
/**
* Read additional data contained in given folder, if any.
*
* @param folder The directory containing data to read.
* @return Set of read values. Can be empty, but should not be null.
* @throws java.io.IOException If values have been found, but an error
* occurred while reading them.
*/
Set<AdditionalValue> readValues(final Path folder) throws IOException;
/**
* Write a given set of {@link AdditionalValue} in a specific folder, if the
* current service is designed to treat input data type. Do nothing
* otherwise.
*
* @param folder To put data into.
* @param toWrite Set of values to serialise.
* @return True if current service is able to write input values (and has
* succeeded in doing so). False otherwise.
* @throws java.io.IOException If an error occurs while writing given
* values.
*/
boolean writeValues(final Path folder, final Set<AdditionalValue> toWrite) throws IOException;
}

View File

@ -0,0 +1,124 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.result;
/**
* POJO representing a result item in the dashboard table.
*
* @author Cédric Briançon (Geomatys)
*/
public class DashboardResultItem {
private int year;
private String site;
private String indicator;
private Double value;
private boolean published;
public DashboardResultItem() {
}
public DashboardResultItem(int year, String site, String indicator, Double value, boolean published) {
this.year = year;
this.site = site;
this.indicator = indicator;
this.value = value;
this.published = published;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public String getSite() {
return site;
}
public void setSite(String site) {
this.site = site;
}
public String getIndicator() {
return indicator;
}
public void setIndicator(String indicator) {
this.indicator = indicator;
}
public Double getValue() {
return value;
}
public void setValue(Double value) {
this.value = value;
}
public boolean isPublished() {
return published;
}
public void setPublished(boolean published) {
this.published = published;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DashboardResultItem that = (DashboardResultItem) o;
if (year != that.year) return false;
if (!site.equals(that.site)) return false;
return indicator.equals(that.indicator);
}
@Override
public int hashCode() {
int result = year;
result = 31 * result + site.hashCode();
result = 31 * result + indicator.hashCode();
return result;
}
}

View File

@ -0,0 +1,112 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.result;
import java.util.Objects;
/**
* {@link Index} implementation which contains a value for a given year
* and references a specific {@link IndexSpi}.
*
* @author Samuel Andrés (Geomatys)
*
* @param <V> The type of the {@link Index} value.
*/
public class DefaultIndex<V extends Number> implements Index<V> {
private final V value;
private final int year;
private final IndexSpi indexSpi;
/**
* @param value
* @param indexSpi
* @param year
*/
public DefaultIndex(final V value, final IndexSpi<V, Integer> indexSpi, final int year){
this.value = value;
this.indexSpi = indexSpi;
this.year = year;
}
@Override
public V getValue() {
return value;
}
@Override
public int getYear() {
return year;
}
@Override
public IndexSpi<V, Integer> getSpi() {
return indexSpi;
}
@Override
public int hashCode() {
int hash = 3;
hash = 83 * hash + this.year;
hash = 83 * hash + Objects.hashCode(this.value);
hash = 83 * hash + Objects.hashCode(this.indexSpi);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DefaultIndex<?> other = (DefaultIndex<?>) obj;
if (this.year != other.year) {
return false;
}
if (!Objects.equals(this.value, other.value)) {
return false;
}
return Objects.equals(this.indexSpi, other.indexSpi);
}
}

View File

@ -0,0 +1,107 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.result;
import fr.cenra.rhomeo.api.data.TrackingPoint;
import java.util.Objects;
import org.apache.sis.util.ArgumentChecks;
/**
* Holds a value for a specific indicator, at a given tracking point.
*
* @param <V> The type of the {@link Index} value.
* @author Alexis Manin (Geomatys)
*/
public class DefaultTrackingPointIndex<V extends Number> implements TrackingPointIndex<V> {
private final V value;
private final TrackingPoint location;
private final IndexSpi spi;
public DefaultTrackingPointIndex(final V value, final TrackingPoint location, final IndexSpi spi) {
ArgumentChecks.ensureNonNull("Value", value);
ArgumentChecks.ensureNonNull("Tracking point", location);
ArgumentChecks.ensureNonNull("Index spi", spi);
this.value = value;
this.location = location;
this.spi = spi;
}
@Override
public V getValue() {
return value;
}
@Override
public int getYear() {
return location.getYear();
}
@Override
public IndexSpi getSpi() {
return spi;
}
@Override
public TrackingPoint getPoint() {
return location;
}
@Override
public int hashCode() {
int hash = 5;
hash = 47 * hash + Objects.hashCode(this.value);
hash = 47 * hash + Objects.hashCode(this.location);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!TrackingPointIndex.class.isAssignableFrom(obj.getClass()))
return false;
final TrackingPointIndex other = (TrackingPointIndex) obj;
return Objects.equals(this.value, other.getValue())
&& Objects.equals(this.location, other.getPoint());
}
}

View File

@ -0,0 +1,71 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.result;
import fr.cenra.rhomeo.api.process.Indicator;
import javax.validation.constraints.NotNull;
/**
* An index computed from an {@link Indicator}. Should be immutable, as it
* describes a result of a finished process.
*
* @author Alexis Manin (Geomatys)
* @param <T> Type of number value (double, int, etc.).
*/
public interface Index<T extends Number> {
/**
*
* @return Computed value. Should never be null.
*/
T getValue();
/**
*
* @return The year for which this index is valid.
*/
int getYear();
/**
*
* @return Thee SPI associated with this value.
*/
@NotNull
IndexSpi getSpi();
}

View File

@ -0,0 +1,122 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.result;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* An utility class to map association between instances from a given base
* class and some indexed numeric values.
*
* The equality of two {@link IndexMapper} is based ont the equality of their
* included "key" instances.
*
* @param <C> The type of the base class.
*
* @author Samuel Andrés (Geomatys)
* @author Cédric Briançon (Geomatys)
*/
public class IndexMapper<C> {
private final ObjectProperty<C> keyProperty = new SimpleObjectProperty<>();
private final Map<String, ObjectProperty<Number>> map = new HashMap<>();
/**
*
* @param key
*/
public IndexMapper(final C key){
this.keyProperty.set(key);
}
public C getKey(){return keyProperty.get();}
public ReadOnlyObjectProperty<C> keyProperty(){return keyProperty;}
public ReadOnlyObjectProperty valueProperty(final String k){return map.get(k);}
public void setValue(final String k, final Number v){
if(valueProperty(k)==null){
map.put(k, new SimpleObjectProperty<>());
}
map.get(k).set(v);
}
public Collection<ObjectProperty<Number>> getValues() {
return map.values();
}
public Set<String> getValueAccessors(){
return map.keySet();
}
@Override
public int hashCode() {
int hash = 3;
hash = 97 * hash + Objects.hashCode(this.keyProperty);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final IndexMapper<?> other = (IndexMapper<?>) obj;
if (!Objects.equals(this.keyProperty, other.keyProperty)) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,106 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.result;
import fr.cenra.rhomeo.api.IdentifiedObject;
import fr.cenra.rhomeo.api.data.TrackingPoint;
import fr.cenra.rhomeo.api.process.Indicator;
import javax.measure.unit.Unit;
import javax.validation.constraints.NotNull;
/**
* Describe a particular type of index, including its name, description, unit, etc.
*
* Note : Must be a Spring component
*
* @author Alexis Manin (Geomatys)
*
* @param <V> Type of index value
* @param <B> Type of index base (Integer for a year, {@link TrackingPoint})
*/
public interface IndexSpi<V extends Number, B> extends IdentifiedObject, Comparable<IndexSpi> {
/**
*
* @return The unit of this index value. Should never be null. If no unit is
* defined for the value, {@link Unit#ONE} should be returned.
*/
@NotNull
Unit<?> getUnit();
/**
*
* @return The indicator associated to this index.
*/
@NotNull
Indicator getIndicator();
/**
* Create an index initialized with given value.
* @param value The value to set on created index.
* @param entity The base the index is associated with. For instance an integer to represent a year, a {@link TrackingPoint}
* @return The created index.
*/
@NotNull
Index<V> createIndex(final V value, final B entity);
/**
* Denotes this index as a major indicator, or a minor indicator. It differs
* from {@link Indicator#getDefaultIndex() } by the fact that multiple
* indice types can be primary for an indicator, but only one is the default
* index shown on dashboard.
*
* @return True if this spi indices are considered as main results for the
* target indicator (see {@link #getIndicator() }. False if it describes a
* complementary index.
*/
default boolean isPrimary() {
return getName().equals(getIndicator().getDefaultIndex());
}
@Override
default int compareTo(IndexSpi o) {
if (o == null) {
return -1;
}
return (getIndicator() == o.getIndicator())? 0 :
getIndicator() == null? 1 : getIndicator().compareTo(o.getIndicator());
}
}

View File

@ -0,0 +1,121 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.result;
import java.util.HashMap;
import java.util.Map;
/**
* POJO regrouping indicator values for each year.
*
* @author Cédric Briançon (Geomatys)
*/
public class IndicatorValuesByYearItem implements Comparable<IndicatorValuesByYearItem> {
private final int year;
private final Map<String, PublishedValue> indicatorValues = new HashMap<>();
public IndicatorValuesByYearItem(final int year) {
this.year = year;
}
public int getYear() {
return year;
}
/**
* Get the value for the given indicator. May return {@code null} if not found.
*
* @param indicator Indicator name.
* @return The value or {@code null} if not defined for this indicator
*/
public PublishedValue getValueForIndicator(final String indicator) {
return indicatorValues.get(indicator);
}
public void addValueForIndicator(final String indicator, final Double value, final boolean published) {
indicatorValues.put(indicator, new PublishedValue(value, published));
}
@Override
public int compareTo(IndicatorValuesByYearItem o) {
if (o == null) {
return -1;
}
return year < o.getYear() ? -1 : year == o.getYear() ? 0 : 1;
}
/**
* Try to find if the value for this indicator is published.
*
* @param indicatorName
* @return True or false depending if the value has been published or not for this indicator,
* or {@code null} if not defined for this indicator.
*/
public Boolean isPublished(final String indicatorName) {
final PublishedValue pubVal = indicatorValues.get(indicatorName);
if (pubVal == null) {
return null;
}
return pubVal.published;
}
public static final class PublishedValue {
private Double value;
private boolean published;
PublishedValue(final Double value, final boolean published) {
this.value = value;
this.published = published;
}
public Double getValue() {
return value;
}
public boolean isPublished() {
return published;
}
public void setPublished(boolean published) {
this.published = published;
}
}
}

View File

@ -0,0 +1,59 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.result;
import fr.cenra.rhomeo.api.data.TrackingPoint;
import javax.validation.constraints.NotNull;
/**
* Describe an index computed on a single {@link TrackingPoint}. Should be
* immutable, as it describes a result of a finished process.
*
* @author Alexis Manin (Geomatys)
* @param <T> Type of number value (double, int, etc.).
*/
public interface TrackingPointIndex<T extends Number> extends Index<T> {
/**
*
* @return The point on which this index has been computed on.
*/
@NotNull
TrackingPoint getPoint();
}

View File

@ -0,0 +1,57 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.ui;
import fr.cenra.rhomeo.api.data.Protocol;
import fr.cenra.rhomeo.api.result.AdditionalValue;
import java.util.Collection;
import java.util.Optional;
import javafx.scene.Node;
/**
* An SPI providing view component to display a set of additional results.
*
* @author Alexis Manin (Geomatys)
*/
public interface AdditionalResultSpi {
Protocol getProtocol();
Optional<Node> getResultNode(final Collection<AdditionalValue> values);
}

View File

@ -0,0 +1,59 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.ui;
import fr.cenra.rhomeo.api.result.IndicatorValuesByYearItem;
import java.util.Optional;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableView;
import org.springframework.stereotype.Component;
/**
* Describe a menu item which an be added to the context menu of the dashboard
* result table.
*
* Note : implementations should be {@link Component}s. This way, the dashboard
* will be able to inject all available items.
*
* @author Alexis Manin (Geomatys)
*/
public interface DashboardMenuItem {
public Optional<MenuItem> createItem(final TableView<IndicatorValuesByYearItem> target);
}

View File

@ -0,0 +1,58 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.ui;
import fr.cenra.rhomeo.api.data.Statement;
import java.util.function.Predicate;
import javafx.beans.property.ObjectProperty;
import javafx.scene.control.TableView;
/**
*
* @author Alexis Manin (Geomatys)
* @param <T> Type of filter given by this component.
*/
public abstract class FilterTable<T extends Predicate> extends TableView<T> {
public abstract Predicate<? super Statement> getPreFilter();
public abstract void setPreFilter(final Predicate<Statement> filter);
public abstract ObjectProperty<Predicate<? super Statement>> preFilterProperty();
}

View File

@ -0,0 +1,56 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.ui;
import fr.cenra.rhomeo.api.data.Protocol;
import java.util.function.Predicate;
import javafx.scene.control.TableView;
/**
* A simple pojo capable to provide a {@link TableView} to display information
* of filters o apply on a specific dataset.
*
* @author Alexis Manin (Geomatys)
*/
public interface FilterTableSpi {
Protocol getProtocol();
FilterTable<Predicate> createEmptyTable();
}

View File

@ -0,0 +1,70 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.ui;
import fr.cenra.rhomeo.api.preferences.PreferenceGroup;
import javafx.scene.Node;
/**
*
* A simple interface which allows to discover JavaFX components able to edit
* a specific {@link PreferenceGroup}.
*
* @author Samuel Andrés (Geomatys)
* @param <T>
*/
public interface PreferencePane<T> {
/**
* Saves preferences edited by the pane.
*/
void save();
/**
*
* @return
*/
PreferenceGroup<T> getPreferenceGroup();
/**
*
* @return The editor node to include in the scene.
*/
Node getEditor();
}

View File

@ -0,0 +1,71 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.ui;
import fr.cenra.rhomeo.api.data.Protocol;
import fr.cenra.rhomeo.core.WorkflowStep;
import java.util.Set;
import javafx.scene.Node;
/**
* Define edition rules for a specific protocol. For example, it allows to
* replace a {@link WorkflowStep} panel by one chosen here.
*
* @author Alexis Manin (Geomatys)
*/
public interface ProtocolEditorSpi {
Protocol getProtocol();
/**
*
* @param step The step user required to display.
* @return A node allowing to edit protocol related data for the given step.
* Can be null, in which case application considers it can use a default
* editor.
*/
Node getEditor(final WorkflowStep step);
/**
*
* @return A set of steps which must be ignored for given protocol. Can be
* empty, but should never be null.
*/
Set<WorkflowStep> getStepsToIgnore();
}

View File

@ -0,0 +1,81 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.ui;
import fr.cenra.rhomeo.api.data.Statement;
import javafx.beans.property.ObjectProperty;
import javafx.collections.ObservableList;
/**
* An editor created from {@link StatementEditorSpi#createEditor() }.
* @author Alexis Manin (Geomatys)
* @param <T> Manageable data type.
*/
public interface StatementEditor<T extends Statement> {
/**
*
* @return Data type manageable by current editor.
*/
Class<T> getDataType();
/**
* Asks the editor to focus on a particular item. Optionally, user can also
* give a property name to focus on into input object.
*
* @param item The item to focus on in the editor.
* @param propertyName If not null, this represents a property of the given
* item to focus on.
*/
void focusOn(final T item, final String propertyName);
/**
*
* @return List of items contained (to edit) in this component.
*/
ObservableList<T> getItems();
/**
*
* @param items Configure this component to work with given set of items.
*/
void setItems(ObservableList<T> items);
ObjectProperty<ObservableList<T>> itemsProperty();
}

View File

@ -0,0 +1,65 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api.ui;
import fr.cenra.rhomeo.api.data.Statement;
import javax.validation.constraints.NotNull;
/**
* A provider which can return back an editor to fill objects of a specific type.
*
* @author Alexis Manin (Geomatys)
* @param <T> Manageable data type.
*/
public interface StatementEditorSpi<T extends Statement> {
/**
*
* @return A new editor for data seizure. Never null.
*/
@NotNull
StatementEditor<T> createEditor();
/**
*
* @return Data type manageable by current editor provider.
*/
@NotNull
Class<T> getDataType();
}

View File

@ -0,0 +1,98 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core;
import org.geotoolkit.data.bean.BeanFeature;
import org.geotoolkit.feature.type.FeatureType;
import org.opengis.feature.Feature;
import org.opengis.filter.identity.FeatureId;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
/**
*
* @author Alexis Manin (Geomatys)
*/
public class BeanUtils {
/**
* Build a feature type corresponding to given bean class, i.e which reproduce
* its properties.
*
* @param beanClass The class to build a feature type for.
* @return A feature type to map pojo properties into {@link Feature} API.
*/
public static FeatureType createFeatureType(final Class beanClass) {
return createMapping(beanClass).featureType;
}
public static BeanFeature.Mapping createMapping(final Class beanClass) {
return new Mapping(beanClass, "no namespace", null, null);
}
public static BeanFeature.Mapping createMapping(final Class beanClass, final String namespace) {
return new Mapping(beanClass, namespace, null, null);
}
private static class Mapping extends BeanFeature.Mapping {
public Mapping(Class clazz, String namespace, CoordinateReferenceSystem crs, String idField) {
super(clazz, namespace, crs, idField);
}
@Override
public FeatureId buildId(Object bean) {
if (idAccessor == null) {
return new FeatureId() {
@Override
public String getID() {
return "";
}
@Override
public boolean matches(Object o) {
return false;
}
};
} else {
return super.buildId(bean);
}
}
}
}

View File

@ -0,0 +1,426 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.regex.Pattern;
import org.apache.sis.util.Numbers;
import org.apache.sis.util.ObjectConverter;
import org.apache.sis.util.ObjectConverters;
import org.geotoolkit.util.collection.CloseableIterator;
/**
* Read a given CSV file, trying to create a list of pojo of the given type to hold
* its data.
*
* @author Alexis Manin (Geomatys)
*/
public class CSVDecoder<T> extends CSVMapper<T> {
/**
* At runtime, we detect doubled double-quote in escaped strings, to replace
* them by single ones.
*/
static final Pattern DOUBLED_DOUBLE_QUOTE = Pattern.compile("\"\"");
static final String DOUBLE_QUOTE_REPLACEMENT = "\"";
/**
* Contains all cell value decoders. For each line read, we take each column
* value, and give it to an appropriate converter which will put them into
* the right properties of a target Pojo.
*/
private final BiConsumer<T, String>[] propertySetters;
/**
* Create a new decoder whose aim is to read data hold by input file, and
* return it as a list of pojos of the given type.
* @param input The file to read. MUST BE READABLE
* @param beanClass The class defining POJO object to use as data container.
* @throws IOException If we cannot analyze input file header.
* @throws IntrospectionException If we cannot analyze input pojo type.
*/
public CSVDecoder(Path input, Class<T> beanClass) throws IOException, IntrospectionException {
this(input, beanClass, null);
}
/**
* Create a new decoder whose aim is to read data hold by input file, and
* return it as a list of pojos of the given type.
* @param input The file to read. MUST BE READABLE
* @param beanClass The class defining POJO object to use as data container.
* to read.
* @param fileEncoding Encoding to use for reading.
* @throws IOException If we cannot analyze input file header.
* @throws IntrospectionException If we cannot analyze input pojo type.
*/
public CSVDecoder(Path input, Class<T> beanClass, Charset fileEncoding) throws IOException, IntrospectionException {
this(input, beanClass, fileEncoding, null);
}
public CSVDecoder(Path input, Class<T> beanClass, Charset fileEncoding, Set<String> ignorableProperties) throws IOException, IntrospectionException {
this(input, beanClass, null, fileEncoding, ignorableProperties);
}
public CSVDecoder(Path input, Class<T> beanClass, final Character separator, final Charset fileEncoding, final Set<String> ignorableProperties) throws IOException, IntrospectionException {
super(input, beanClass, separator, fileEncoding, ignorableProperties);
if (!Files.isReadable(input)) {
throw new IOException("Given file is not readable : ".concat(input.toString()));
}
/* Prepare data conversion. For each property present in input bean type,
* we ensure that a read method is available. Then, we analyze its return
* type. We try to find an appropriate converter to get a string
* representation writable in CSV file.
*/
final PropertyDescriptor[] pDescriptors = beanInfo.getPropertyDescriptors();
propertySetters = new BiConsumer[pDescriptors.length];
for (int i = 0; i < pDescriptors.length; i++) {
final PropertyDescriptor p = pDescriptors[i];
final Method writeMethod = p.getWriteMethod();
if (writeMethod == null)
throw new IllegalArgumentException(
new StringBuilder("Property ")
.append(p.getName())
.append(" from class ")
.append(beanClass.getCanonicalName())
.append("is not writable.")
.toString());
writeMethod.setAccessible(true);
final Class propertyType = p.getPropertyType();
final Function<String, ?> conv = createPropertyConverter(propertyType);
final BiConsumer<T, Object> setter;
setter = (pojo, value) -> {
try {
writeMethod.invoke(pojo, value);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RhomeoRuntimeException(e);
}
};
if (conv == null) {
propertySetters[i] = (BiConsumer) setter;
} else {
propertySetters[i] = (pojo, strValue) -> setter.accept(pojo, conv.apply(strValue));
}
}
}
/**
* Read given string (must be a line in the csv, not header) and map its
* data into a POJO.
*
* @param line A line of the CSV file.
* @param lineNumber Number of the read line. It serves to build error messages.
* @return A Pojo containing line data. If input line is blank, a null value is returned.
* @throws InstantiationException
* @throws IllegalAccessException
* @throws IOException
*/
private T decodeLine(final String line, int lineNumber) throws InstantiationException, IllegalAccessException, IOException {
final List<CharBuffer> groups = splitByProperty(line);
if (groups.isEmpty())
return null;
final T newInstance = dataType.newInstance();
CharBuffer buffer;
for (int i = 0; i < groups.size(); i++) {
buffer = groups.get(i);
try {
if (buffer.hasRemaining() && propertyOrder[i] >= 0) {
propertySetters[propertyOrder[i]].accept(newInstance, unescape(buffer));
}
} catch (Exception e) {
throwWithDescription(lineNumber, i, buffer, e);
}
}
return newInstance;
}
/**
*
* @return A list of POJO, each one containing data of a line. Pojos are
* sorted according to line number in the source file.
*
* @throws IOException Reading fails.
* @throws InstantiationException Cannot create a new pojo (Mostly no default builder found).
* @throws IllegalAccessException A property of the pojo type cannot be used by the decoder.
*/
public List<T> decode() throws IOException, InstantiationException, IllegalAccessException {
try (final BufferedReader reader = Files.newBufferedReader(target, charset)) {
// No data ...
String line = reader.readLine();
if (line == null) {
return new ArrayList<>();
} else {
updatePropertyOrder(line);
}
// Do not check header content. It has been done at built.
final List<T> result = new ArrayList<>();
int lineCount = 1;
T read;
while ((line = reader.readLine()) != null) {
read = decodeLine(line, lineCount++);
if (read != null)
result.add(read);
}
return result;
}
}
/**
* /!\ You MUST CLOSE the iterator once you're done with it.
* @return An iterator which will decode lines of the source file as we iterate
* on them.
*/
public CloseableIterator<T> decodeLazy() {
return new CloseableIterator<T>() {
BufferedReader fileReader;
T next;
int lineCount = 0;
@Override
public boolean hasNext() {
if (next == null) {
try {
// first time, check header.
if (fileReader == null) {
fileReader = Files.newBufferedReader(target, charset);
final String header = fileReader.readLine();
lineCount++;
// check header
if (header == null)
return false;
else
updatePropertyOrder(header);
}
// read next line
String line;
while (next == null) {
line = fileReader.readLine();
if (line == null)
break;
next = decodeLine(line, ++lineCount);
}
} catch (IOException e) {
throw new RhomeoRuntimeException(e.getMessage(), e);
} catch (InstantiationException | IllegalAccessException ex) {
RhomeoCore.LOGGER.log(Level.WARNING, "Cannot set output properties.", ex);
}
}
return next != null;
}
@Override
public T next() {
if (next == null) {
throw new IllegalStateException("No more element available !");
}
try {
return next;
} finally {
next = null;
}
}
@Override
public void close() {
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException ex) {
RhomeoCore.LOGGER.log(Level.WARNING, "A reader cannot be closed on file ".concat(target.toString()), ex);
}
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
close();
}
};
}
/**
* Analyze a line of input CSV file, and split its character by column. To
* find columns, we search for separators which are not enclosed in an
* escaped character sequence.
*
* NOTE : return char buffers are already trimmed, but are still escaped (if any).
*
* @param line input line to find property indices for.
* @return A list of buffer. Each represents a part of the original string.
* They're sorted by order of appearance in the input string.
*/
private List<CharBuffer> splitByProperty(final String line) {
final CharBuffer cLine = CharBuffer.wrap(line);
final ArrayList<CharBuffer> groups = new ArrayList<>();
int lastGroup = cLine.position();
boolean escapeMode = false;
char c;
for (int i = cLine.position(); i < cLine.limit(); i++) {
c = cLine.get(i);
if (c == '"') {
escapeMode = !escapeMode;// If two double quotes follows, escape mode is reactivated.
} else if (!escapeMode) {
if (c == separator) {
groups.add(cLine.subSequence(lastGroup, i));
lastGroup = i + 1;
} else if (Character.isWhitespace(c) && i == lastGroup) {
lastGroup++; // remove trailing spaces
} else if (c == COMMENT_CHAR) {
break; // A comment have been found.
}
}
}
// last value on the line
if (lastGroup < cLine.limit()) {
groups.add(cLine.subSequence(lastGroup, cLine.remaining()));
}
return groups;
}
/**
*
* @param input A char buffer representing one cell data.
* @return A string which is the cell data, from which we remove trailing
* double-quotes and doubled ones if needed.
*/
private String unescape(final CharBuffer input) {
if (!input.hasRemaining()) {
return "";
}
if (input.get(input.position()) == '"' && input.get(input.limit() -1) == '"') {
if (input.remaining() < 3) // Empty escaped sequence.
return "";
return DOUBLED_DOUBLE_QUOTE.matcher(input.subSequence(1, input.remaining() -1)).replaceAll(DOUBLE_QUOTE_REPLACEMENT);
}
return input.toString();
}
/**
* Search for an appropriate converter between given type and string.
*
* Note : for numbers and primitives, we use direct conversion using wrapping
* class utilities. We make a distinction between primitive and wrapping class
* case, as the first one cannot hold null value information.
*
* @param inputType Type of object to convert.
* @return A ready-to-use converter, or null if we do not need any.
*/
private Function<String, ?> createPropertyConverter(final Class inputType) {
final Function<String, ?> result;
if (CharSequence.class.isAssignableFrom(inputType)) {
return null;
// boolean
} else if (Boolean.class.isAssignableFrom(inputType)) {
result = input -> Boolean.valueOf(input);
} else if (Boolean.TYPE.isAssignableFrom(inputType)) {
result = input -> Boolean.parseBoolean(input);
// character
} else if (Character.class.isAssignableFrom(inputType)) {
result = input -> input == null || input.isEmpty()? null : input.charAt(0);
} else if (Character.TYPE.isAssignableFrom(inputType)) {
result = input -> input == null || input.isEmpty()? '?' : input.charAt(0);
// bytes
} else if (Byte.class.isAssignableFrom(inputType)) {
result = input -> Byte.valueOf(input);
} else if (Byte.TYPE.isAssignableFrom(inputType)) {
result = input -> input == null || input.isEmpty()? 0 : Byte.parseByte(input);
// short
} else if (Short.class.isAssignableFrom(inputType)) {
result = input -> Short.valueOf(input);
} else if (Short.TYPE.isAssignableFrom(inputType)) {
result = input -> input == null || input.isEmpty()? 0 : Short.parseShort(input);
// integer
} else if (Integer.class.isAssignableFrom(inputType)) {
result = input -> Integer.valueOf(input);
} else if (Integer.TYPE.isAssignableFrom(inputType)) {
result = input -> input == null || input.isEmpty()? 0 : Integer.parseInt(input);
// long
} else if (Long.class.isAssignableFrom(inputType)) {
result = input -> Long.valueOf(input);
} else if (Long.TYPE.isAssignableFrom(inputType)) {
result = input -> input == null || input.isEmpty()? 0 : Long.parseLong(input);
// float
} else if (Float.class.isAssignableFrom(inputType)) {
result = input -> Float.valueOf(input);
} else if (Float.TYPE.isAssignableFrom(inputType)) {
result = input -> input == null || input.isEmpty()? Float.NaN : Float.parseFloat(input);
// double
} else if (Double.class.isAssignableFrom(inputType)) {
result = input -> Double.valueOf(input);
} else if (Double.TYPE.isAssignableFrom(inputType)) {
result = input -> input == null || input.isEmpty()? Double.NaN : Double.parseDouble(input);
} else {
final Class tmpClass = Numbers.primitiveToWrapper(inputType);
final ObjectConverter<? super String, ?> sisConverter = ObjectConverters.find(String.class, tmpClass);
result = input -> (input == null || input.isEmpty())? null : sisConverter.apply(input);
}
return result;
}
}

View File

@ -0,0 +1,298 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.regex.Pattern;
import org.apache.sis.util.Numbers;
import org.apache.sis.util.ObjectConverter;
import org.apache.sis.util.ObjectConverters;
/**
* Encode data holded by POJO of a specific type into a CSV file.
*
* @author Alexis Manin (Geomatys)
*/
public class CSVEncoder<T> extends CSVMapper<T> {
static final Pattern DOUBLE_QUOTE = Pattern.compile("\"");
static final String DOUBLE_QUOTE_REPLACEMENT = "\"\"";
private final Function[] converters;
/**
* Create a new encoder whose aim is to write pojo properties in the given file.
* @param output The file to write. If not exists, we'll try to create it.
* However, its parent directory MUST exist already.
* @param beanClass The class defining POJO object we'll have to extract data from.
* @throws IOException If we cannot create the file, or it already exists and we cannot analyze its header.
* @throws IntrospectionException If we cannot analyze input pojo type.
*/
public CSVEncoder(Path output, Class<T> beanClass) throws IOException, IntrospectionException {
this(output, beanClass, null);
}
/**
* Create a new encoder whose aim is to write pojo preoprties in the given file.
* @param output The file to write. If not exists, we'll try to create it.
* However, its parent directory MUST exist already.
* @param beanClass The class defining POJO object we'll have to extract data from.
* to read.
* @param fileEncoding Encoding to use for writing.
* @throws IOException If we cannot create the file, or it already exists and we cannot analyze its header.
* @throws IntrospectionException If we cannot analyze input pojo type.
*/
public CSVEncoder(Path output, Class<T> beanClass, Charset fileEncoding) throws IOException, IntrospectionException {
this(output, beanClass, fileEncoding, null);
}
public CSVEncoder(Path output, Class<T> beanClass, Charset fileEncoding, Set<String> ignorableProperties) throws IOException, IntrospectionException {
this(output, beanClass, null, fileEncoding, ignorableProperties);
}
public CSVEncoder(final Path output, final Class<T> beanClass, final Character separator, final Charset fileEncoding, final Set<String> ignorableProperties) throws IOException, IntrospectionException {
super(output, beanClass, separator, fileEncoding, ignorableProperties);
/* Prepare data conversion. For each property present in input bean type,
* we ensure that a read method is available. Then, we analyze its return
* type. We try to find an appropriate converter to get a string
* representation writable in CSV file.
*/
final PropertyDescriptor[] pDescriptors = beanInfo.getPropertyDescriptors();
converters = new Function[pDescriptors.length];
for (int i = 0 ; i < pDescriptors.length; i++) {
final PropertyDescriptor p = pDescriptors[i];
final Method readMethod = p.getReadMethod();
if (readMethod == null)
throw new IllegalArgumentException(
new StringBuilder("Property ")
.append(p.getName())
.append(" from class ")
.append(beanClass.getCanonicalName())
.append("is not readable.")
.toString());
readMethod.setAccessible(true);
final Class returnType = readMethod.getReturnType();
Function conv = createPropertyConverter(returnType);
final Function getter = in -> {
try {
return readMethod.invoke(in);
} catch (IllegalAccessException|InvocationTargetException e) {
throw new RhomeoRuntimeException(e);
}
};
if (conv ==null) {
converters[i] = getter;
} else {
converters[i] = getter.andThen(conv);
}
}
}
/**
* Create string representation of a given object.
* @param data the object to encode in CSV.
* @return A line to put in CSV file.
*/
private String encode(T data) {
final StringBuilder builder = new StringBuilder();
Object value = getPropertyValue(data, 0);
if (value != null) {
builder.append(trimAndEscape(value));
}
for (int i = 1; i < converters.length; i++) {
builder.append(this.separator);
value = getPropertyValue(data, i);
if (value != null) {
builder.append(trimAndEscape(value));
}
}
return builder.toString();
}
/**
* Extract value of a property from input pojo, the one located on the given
* column index in the file.
* @param data The pojo to extract value from.
* @param colIndex Index of the property to extract in target file columns.
* @return The extracted value or a default one if input object property is
* null or does not exists. Can return a null value.
*/
private Object getPropertyValue(final T data, final int colIndex) {
Object value;
final int pIndex = propertyOrder[colIndex];
if (pIndex < 0)
value = null;
else
value = converters[pIndex].apply(data);
return value;
}
/**
* Write data pointed by given iterator.
* @param data An iterator which provides objects to write.
* @param replaceExisting If true, data previously written will be erased.
* Otherwise, we'll append data at the end of the file.
* @throws IOException If an error occurs while writing data.
*/
public synchronized void encode(Iterator<T> data, final boolean replaceExisting) throws IOException {
if (!Files.exists(target)) {
Files.createFile(target);
}
/* Check target file header. If we're in append mode, but no header exists,
* we set a flag to ensure it will be created. If it is here but is not
* compatible with our data type, we raise an exception.
*/
boolean emptyHeader = false;
try (final BufferedReader reader = Files.newBufferedReader(target, charset)) {
final String firstLine = reader.readLine();
if (firstLine == null) {
emptyHeader = true;
} else if (!replaceExisting) {
if (isValidHeader(firstLine)) {
updatePropertyOrder(firstLine);
} else {
throw new IllegalArgumentException("Given file already exists, but its header is not complient with given data type.");
}
}
}
try (BufferedWriter writer = Files.newBufferedWriter(target, charset,
StandardOpenOption.CREATE,
replaceExisting ? StandardOpenOption.TRUNCATE_EXISTING : StandardOpenOption.APPEND)) {
if (replaceExisting || emptyHeader) {
writer.write(buildHeader());
}
while (data.hasNext()) {
writer.newLine();
writer.write(encode(data.next()));
}
}
}
/**
* Write data pointed by given collection.
* @param data A collection which provides objects to write.
* @param replaceExisting If true, data previously written will be erased.
* Otherwise, we'll append data at the end of the file.
* @throws IOException If an error occurs while writing data.
*/
public void encode(Iterable<T> data, final boolean replaceExisting) throws IOException {
final Iterator<T> it = data.iterator();
try {
encode(it, replaceExisting);
} finally {
if (it instanceof AutoCloseable) {
try {
((AutoCloseable)it).close();
} catch (Exception ex) {
RhomeoCore.LOGGER.log(Level.WARNING, "Cannot close a resource !", ex);
}
}
}
}
/**
* Search for an appropriate converter between given type and string.
* @param <I> Type of object to convert.
* @param inputType Type of object to convert.
* @return A ready-to-use converter, or null if we don't need any.
*/
private <I> Function<I, String> createPropertyConverter(final Class<I> inputType) {
final Function<I, String> result;
if (inputType.isPrimitive()
|| Number.class.isAssignableFrom(inputType)
|| CharSequence.class.isAssignableFrom(inputType)
|| Boolean.class.isAssignableFrom(inputType)) {
result = null;
} else {
final Class tmpClass = Numbers.primitiveToWrapper(inputType);
ObjectConverter<? super I, ? extends String> sisConverter = ObjectConverters.find(tmpClass, String.class);
result = input -> {
return input == null? null : sisConverter.apply(input);
};
}
return result;
}
/**
* Analyze input string, and modify it if the CSV separator character is
* found inside. First, we remove blank spaces at start and end of the
* string. Then, we'll search for provided separator character. If we find
* it, we escape the string using '"' character. If the string also contains
* '"' characters, we double them.
*
* @param input String to analyze.
* @return Input string if we don't have to escape it. A new one otherwise.
*/
private String trimAndEscape(final Object input) {
String result = input.toString().trim();
if (result.contains(String.valueOf(separator)) || result.contains(String.valueOf(COMMENT_CHAR))) {
result = new StringBuilder(result.length() +2)
.append('"')
.append(DOUBLE_QUOTE.matcher(result).replaceAll(DOUBLE_QUOTE_REPLACEMENT))
.append('"')
.toString();
}
return result;
}
}

View File

@ -0,0 +1,286 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.sis.util.ArgumentChecks;
/**
* TODO : Create a builder, to manage encoder / decoder creation.
*
* @author Alexis Manin (Geomatys)
* @param <T> Type of java bean to map to the CSV file.
*/
public abstract class CSVMapper<T> {
public static final char COMMENT_CHAR = '#';
public static final char DEFAULT_SEPARATOR = ';';
/**
* Expected objects in input or output of the file.
*/
protected final Class<T> dataType;
/**
* Information about {@link #dataType}.
*/
protected final BeanInfo beanInfo;
/**
* Separator between columns.
*/
protected final char separator;
/**
* Charset to use with given file.
*/
protected final Charset charset;
/**
* Path to the CSV file to read or write.
*/
protected final Path target;
/**
* For each column of the file, give the property index corresponding in
* {@link #beanInfo}.
*/
protected int[] propertyOrder;
/**
* List properties that can be missing in the read / written file.
*/
private final Set<String> ignorable;
protected CSVMapper(Path target, Class<T> beanClass, Character separator, Charset fileEncoding) throws IOException, IntrospectionException {
this(target, beanClass, separator, fileEncoding, null);
}
protected CSVMapper(Path target, Class<T> beanClass, Character separator, Charset fileEncoding, Set<String> ignorableProperties) throws IOException, IntrospectionException {
ArgumentChecks.ensureNonNull("Output path", target);
ArgumentChecks.ensureNonNull("Data type", beanClass);
if (ignorableProperties != null) {
ignorable = Collections.unmodifiableSet(ignorableProperties);
} else {
ignorable = Collections.EMPTY_SET;
}
// Check global information
beanInfo = Introspector.getBeanInfo(beanClass, Object.class);
this.dataType = beanClass;
this.separator = separator == null ? DEFAULT_SEPARATOR : separator;
charset = fileEncoding == null ? Charset.defaultCharset() : fileEncoding;
// If input file exists, we check its header is compatible with given data type.
String firstLine;
if (Files.isReadable(target)) {
try (final BufferedReader reader = Files.newBufferedReader(target, charset)) {
// We read lines until we find the first not empty / commented line.
firstLine = reader.readLine();
while (firstLine != null && ((firstLine = firstLine.trim()).isEmpty() || firstLine.charAt(0) == COMMENT_CHAR))
firstLine = reader.readLine();
if (firstLine != null && !isValidHeader(firstLine)) {
throw new IllegalArgumentException("L'entête du fichier n'est pas compatible avec le type de donnée associé.");
}
} catch (CharacterCodingException e) {
throw new IllegalArgumentException("Le jeu de caractères du fichier est invalide. Attendu : ".concat(charset.displayName()));
}
} else if (Files.exists(target)) {
throw new IllegalArgumentException("Le fichier source est illisible !");
} else {
firstLine = null;
}
updatePropertyOrder(firstLine);
this.target = target;
}
public Class<T> getDataType() {
return dataType;
}
/**
* Create a string displaying names of all properties (except the transient
* and {@link Object} inherited ones) defined by given bean information.
*
* @return A string which is the list of found properties, splitted by a
* given separator.
*/
public String buildHeader() {
final StringBuilder builder = new StringBuilder();
final PropertyDescriptor[] pDs = beanInfo.getPropertyDescriptors();
for (int i = 0; i < propertyOrder.length; i++) {
final PropertyDescriptor p = pDs[propertyOrder[i]];
// Ignore properties inherited from object.
if (p.getReadMethod() == null || p.getReadMethod().getDeclaringClass().equals(Object.class))
continue;
builder.append(p.getName()).append(separator);
}
return builder.substring(0, builder.length() - 1);
}
/**
* Check if given header lists all properties defined in given bean type.
*
* @param firstLine The header to check.
* @return True if the file header describes the current reference type,
* false otherwise.
*/
public final boolean isValidHeader(final String firstLine) {
return isValidHeader(firstLine, separator, beanInfo, ignorable);
}
/**
* Check if given header lists all properties defined in given bean type.
*
* @param firstLine The header to check.
* @param separator The character which is used as separator between
* columns.
* @param beanInfo Bean information to check header against.
* @return True if the file header describes the current reference type,
* false otherwise.
*/
public static boolean isValidHeader(final String firstLine, final char separator, final BeanInfo beanInfo) {
return isValidHeader(firstLine, separator, beanInfo, null);
}
public static boolean isValidHeader(final String firstLine, final char separator, final BeanInfo beanInfo, final Set<String> ignorable) {
final String[] splitted = firstLine.split(new StringBuilder("\\s*").append(separator).append("\\s*").toString());
final Set<String> headerNames = new HashSet<>(Arrays.asList(splitted));
if (ignorable != null)
headerNames.addAll(ignorable);
for (final PropertyDescriptor p : beanInfo.getPropertyDescriptors()) {
// Ignore properties unreadable or inherited from object.
if (p.getReadMethod() == null || p.getReadMethod().getDeclaringClass().equals(Object.class))
continue;
if (!headerNames.remove(p.getName())) {
return false;
}
}
/* If we arrive here, it means all mandatory properties have been found
* in input header. If we're in read-only mode, we set it as valid,
* because for columns not present in java model, we can ignore them.
* On the contrary, if we must write into the file, we cannot allow
* losing data contained in it, so we ensure each column of the header
* can be bound to the java model.
*/
return true;
}
/**
* Index property columns.
*
* @param header the header of the file.
*/
protected final void updatePropertyOrder(String header) {
// No header. Natural order initialized.
if (header == null) {
propertyOrder = new int[beanInfo.getPropertyDescriptors().length];
for (int i = 0; i < propertyOrder.length; i++) {
propertyOrder[i] = i;
}
} else {
final String[] splitted = header.split(new StringBuilder("\\s*").append(separator).append("\\s*").toString());
propertyOrder = new int[splitted.length];
Arrays.fill(propertyOrder, -1);
final Map<String, Integer> columns = new HashMap<>(splitted.length);
int i = 0;
for (final String columnName : splitted) {
columns.put(columnName, i++);
}
i = 0;
for (final PropertyDescriptor p : beanInfo.getPropertyDescriptors()) {
final Integer colIndex = columns.get(p.getName());
if (colIndex != null)
propertyOrder[colIndex] = i;
i++;
}
}
}
/**
* Throw a new IOException whose message describe location (line, column) of
* the error.
*
* @param line Number of the line where error happened. Must be > 0.
* @param column Index of the field (not character column) on which error occurred. Must be > 0.
* @param token The field value we failed against.
* @param cause Original exception raised.
* @throws IOException In all cases.
*/
protected static void throwWithDescription(final int line, int column, Object token, final Exception cause) throws IOException {
final StringBuilder msg = new StringBuilder("Erreur : ");
if (line > 0) {
msg.append("ligne ").append(line).append(", ");
}
if (column > 0) {
msg.append("colonne ").append(column).append(", ");
}
msg.append("valeur ").append(token);
throw new IOException(msg.toString(), cause);
}
}

View File

@ -0,0 +1,79 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
/**
*
* @author Alexis Manin (Geomatys)
*/
public class CSVMappingBuilder {
Path target;
Charset charset;
char separator = CSVMapper.DEFAULT_SEPARATOR;
public CSVMappingBuilder forPath(final Path target) {
if (Files.isDirectory(target)) {
throw new IllegalArgumentException("Provided path is a directory !");
}
this.target = target;
return this;
}
public CSVMappingBuilder withEncoding(final Charset charset) {
this.charset = charset;
return this;
}
public CSVMappingBuilder withSeparator(final char separator) {
this.separator = separator;
return this;
}
public <T> TypedCSVMappingBuilder<T> forType(final Class<T> dataType) {
return new TypedCSVMappingBuilder<>(this, dataType);
}
}

View File

@ -0,0 +1,554 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core;
import com.fasterxml.jackson.databind.ObjectMapper;
import fr.cenra.rhomeo.api.Version;
import fr.cenra.rhomeo.core.data.UpdateInfo;
import fr.cenra.rhomeo.core.preferences.net.NetPreferences;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.text.NumberFormat;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.apache.sis.referencing.CRS;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.factory.Hints;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.util.FactoryException;
import fr.cenra.rhomeo.core.list.CodeValue;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.spi.FileSystemProvider;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.scene.control.TableCell;
import javafx.util.Callback;
import javafx.util.StringConverter;
/**
* Regroups generic methods / constants used by the whole application.
* JavaFX features should be deported to fr.cenra.rhomeo.Rhomeo class,
* in the desktop module, not here.
*
* @author Cédric Briançon (Geomatys)
*/
public class RhomeoCore {
/**
* Generic logger to use in the whole application.
*/
public static final Logger LOGGER = Logging.getLogger("fr.cenra.rhomeo");
/**
* Title to display for all popups in the application.
*/
public static final String APPLICATION_TITLE = "Calculette RhoMeo";
/**
* Application main folder, in the user home.
*/
public static final String NAME = "rhomeo";
/**
* Key for the environment variable for the .rhomeo folder.
*/
public static final String ENV_RHOMEO_PATH_KEY = "rhomeo.path";
/**
* Path for the configuration app main folder.
*/
public static final Path CONFIGURATION_PATH;
static {
final String rhomeoPath = System.getProperty(ENV_RHOMEO_PATH_KEY) != null ? System.getProperty(ENV_RHOMEO_PATH_KEY) : NAME;
Path tmpPath = Paths.get(System.getProperty("user.home"), "."+ rhomeoPath);
if (!Files.isDirectory(tmpPath)) {
try {
Files.createDirectory(tmpPath);
} catch (IOException ex) {
try {
tmpPath = Files.createTempDirectory(NAME);
} catch (IOException ex1) {
ex.addSuppressed(ex1);
throw new ExceptionInInitializerError(ex);
}
}
}
CONFIGURATION_PATH = tmpPath;
}
/**
* System preference for the home directory of derby.
*/
public static final String DERBY_HOME_KEY = "derby.system.home";
/**
* Spring application context path in the resources.
*/
public static final String SPRING_CONTEXT_XML = "/fr/cenra/rhomeo/spring/spring-context.xml";
/**
* Path of the EPSG database.
*/
public static final Path EPSG_PATH = CONFIGURATION_PATH.resolve("EPSG");
/**
* Path to the folder which will contain reference lists.
*/
public static final Path REFERENCE_PATH = CONFIGURATION_PATH.resolve("references");
/**
* Path to the folder which will contain geographic reference datas.
* Path content will look like :
* ./site_id/dataname_year/*.shp
*/
public static final Path REFERENCIELS_GEO_PATH = CONFIGURATION_PATH.resolve("referential_geo");
/**
* Sites folder and shp name.
*/
public static final String SITES = "sites";
/**
* Path of the sites folder.
*/
public static final Path SITES_PATH = CONFIGURATION_PATH.resolve(SITES);
/**
* Path of the sites folder.
*/
public static final Path SITES_SHP_PATH = SITES_PATH.resolve(SITES +".shp");
/**
* Path of the CSV file for dashboard published results.
*/
public static final Path DASHBOARD_PUBLISHED_RESULTS = CONFIGURATION_PATH.resolve("dashboard_results.csv");
/**
* Path of the JSON file for data contexts for sites / protocols.
*/
public static final Path DATA_CONTEXT_SITE_PATH = CONFIGURATION_PATH.resolve("data_contexts.json");
public static final Path RESULT_PATH = CONFIGURATION_PATH.resolve("results");
/**
* Path to the context restoration folder.
*/
public static final Path RESTORATION_FOLDER = CONFIGURATION_PATH.resolve("state");
/**
* Number of millis for opening the connection before timeout.
*/
public static final int CONNECTION_TIMEOUT = 5000;
/**
* Number of millis to wait after launching a timeout if no data has been transferred.
*/
public static final int READ_TIMEOUT = 20000;
private static CoordinateReferenceSystem SITES_CRS;
private static final int SRID = 2154;
/**
* Check for word character at the start of a text.
*/
public static final Pattern CODE_PATTERN = Pattern.compile("^[\\w\\.]+");
public enum FT_PROPERTIES {
the_geom, NAME, COUNTY, ODONATE, ORTHOPTERE, REFERENT, ORG, TYPE, REMARKS
}
/**
* A regex to detect file extensions.
*/
public static final Pattern EXT_PATTERN = Pattern.compile("\\.\\w+$");
public static final DecimalFormat SIMPLE_DECIMAL_FORMAT = new DecimalFormat("0.##");
public static final DecimalFormat SECOND_DECIMAL_FORMAT = new DecimalFormat("0.00");
/**
* Initializes the EPSG database, generating it if not yet present on the computer.
*
* @throws FactoryException
* @throws IOException
*/
public static void initEpsgDB() throws FactoryException, IOException {
initEpsgDB(true);
}
/**
* Initialize properties needed by EPSG database. If input boolean is true,
* we'll fully load the database.
*
* @param checkInit Set to true if you want to ensure EPSG database is fully
* initialized.
*
* @throws FactoryException
* @throws IOException
*/
public static void initEpsgDB(final boolean checkInit) throws FactoryException, IOException {
// create a database in user directory
Files.createDirectories(RhomeoCore.EPSG_PATH);
// work in lazy mode, do your best for lenient datum shift
Hints.putSystemDefault(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE);
String derbyProp = null;
try {
derbyProp = System.getProperty(DERBY_HOME_KEY);
} finally {
if (derbyProp == null) {
System.setProperty(DERBY_HOME_KEY, EPSG_PATH.toAbsolutePath().toString());
}
}
// force loading epsg
if (checkInit) {
getSiteCRS();
}
}
public static CoordinateReferenceSystem getSiteCRS() throws FactoryException {
if (SITES_CRS == null) {
SITES_CRS = CRS.forCode("EPSG:"+ SRID);
}
return SITES_CRS;
}
/**
* Get current application version.
*
* @return The application version, may be empty or null if not found in the application packaging.
*/
public static String getVersion() {
return RhomeoCore.class.getPackage().getImplementationVersion();
}
/**
* Verify the presence of a new update for the application.
*
* @return The new version to install if an update exists, {@code null} if no updates found
* or no need to update.
*/
public static UpdateInfo existingUpdate() {
final String version = getVersion();
if (version == null || version.isEmpty()) {
LOGGER.log(Level.INFO, "Invalid or not found version for this application");
return null;
}
final Version localVersion = new Version(version);
final NetPreferences netPrefs = new NetPreferences();
final String updateUrl = netPrefs.getPreference(NetPreferences.UPDATE_URL);
try (final InputStream stream = netPrefs.openConnection(netPrefs.getUpdateURL()).getInputStream()) {
final UpdateInfo update = new ObjectMapper().readValue(stream, UpdateInfo.class);
if (update.getVersion() != null && update.getVersion().compareTo(localVersion) > 0)
return update;
} catch (IOException|GeneralSecurityException e) {
LOGGER.log(Level.WARNING, "Cannot access update service : ".concat(updateUrl), e);
}
return null;
}
/**
* Convert byte number given in parameter in a human readable string. It tries
* to fit the best unit. Ex : if you've got a number higher than a thousand,
* input byte number will be expressed in kB. If you've got more than a million,
* you've got it as MB
* @param byteNumber Byte quantity to display
* @return A string displaying byte number converted in fitting unit, along with unit symbol.
*/
public static String toReadableSize(final long byteNumber) {
final NumberFormat format = NumberFormat.getNumberInstance();
format.setMinimumFractionDigits(2);
format.setMaximumFractionDigits(2);
if (byteNumber < 0) {
return "inconnu";
} else if (byteNumber < 1e3) {
return "" + byteNumber + " octets";
} else if (byteNumber < 1e6) {
return "" + format.format(byteNumber / 1e3) + " ko";
} else if (byteNumber < 1e9) {
return "" + format.format(byteNumber / 1e6) + " Mo";
} else if (byteNumber < 1e12) {
return "" + format.format(byteNumber / 1e9) + " Go";
} else {
return "" + (byteNumber / 1e12) + " To";
}
}
/**
*
* @param first First string to test.
* @param second Second string to test.
* @return True if both strings are null or blank. False if at least one of
* them contains something else than spaces.
*/
public static boolean bothEmpty(final String first, final String second) {
return (first == null || first.trim().isEmpty()) && (second == null || second.trim().isEmpty());
}
/**
*
* @param first First string to test.
* @param second Second string to test.
* @return True if both strings are equal or blank. False if one of them is
* not blank, or they're not equal.
*/
public static boolean equivalent(final String first, final String second) {
if (bothEmpty(first, second)) {
return true;
}
return first == null || second == null? false : first.equals(second);
}
/**
* Try to find an object of given type with the given code.
* Note : Works only with enums.
*
* @param <T> Type of target coded list.
* @param dataType Type of enum to search into
* @param code Code of the object to return.
* @return An object of the queried type with queried code, or nothing if we
* cannot find any object matching.
*/
public static <T extends CodeValue> Optional<T> getByCode(final Class<T> dataType, Object code) {
if (dataType.isEnum() && code != null) {
for (final T candid : dataType.getEnumConstants()) {
if (candid.getCode().equals(code)) {
return Optional.of(candid);
}
}
}
return Optional.empty();
}
/**
* Compute the quantile of a given data suite. For more details, see
* https://en.wikipedia.org/wiki/Quantile
*
* Note : we differ from above wiki definition on even computing. Instead of
* arbitrary ceiling computed position, we get the average of its two
* ascending value in the data suite.
*
* @param data Data suite to find a quantile for. If input array is null or
* empty, the method will miserably fail, so be careful.IMPORTANT : IT MUST
* BE SORTED BY ASCENDING ORDER !
* @param factor Quantile factor. It must be between 0 and 1 (otherwise, the
* algorithm will end up very badly) representing which portion of the data
* we want.
* @return The queried quantile
*/
public static float quantile(final float[] data, final float factor) {
final float position = (data.length -1) * factor;
final double floor = StrictMath.floor(position);
final int floorInt = (int)floor;
if (position == floor) {
// We take directly the pointed value.
return data[floorInt];
}
final double ratio = position - floor;
final double extraValue = (data[floorInt + 1] - data[floorInt]) * ratio;
return (float) (data[floorInt] + extraValue);
}
/**
* Compute the quantile of a given data suite. For more details, see
* https://en.wikipedia.org/wiki/Quantile
*
* Note : we differ from above wiki definition on even computing. Instead of
* arbitrary ceiling computed position, we get the average of its two
* ascending value in the data suite.
*
* @param data Data suite to find a quantile for. If input array is null or
* empty, the method will miserably fail, so be careful.IMPORTANT : IT MUST
* BE SORTED BY ASCENDING ORDER !
* @param factor Quantile factor. It must be between 0 and 1 (otherwise, the
* algorithm will end up very badly) representing which portion of the data
* we want.
* @return The queried quantile
*/
public static double quantile(final double[] data, final float factor) {
final float position = (data.length -1) * factor;
final double floor = StrictMath.floor(position);
final int floorInt = (int) floor;
if (position == floor) {
// We take directly the pointed value.
return data[floorInt];
}
final double ratio = position - floor;
final double extraValue = (data[floorInt + 1] - data[floorInt]) * ratio;
return data[floorInt] + extraValue;
}
/**
* Create a new file system, allowing to view the content of a zip file as
* a file system.
* /!\ Close the returned {@link FileSystem} once you're done with it !
*
* @param zipPath A path pointing on a zip file. If it does not exist, it
* will be created.
* @return A file system pointing on given zip file.
* @throws URISyntaxException If we cannot create a valid URI from given
* path.
* @throws IOException If the file occurs while accessing zip file.
*/
public static FileSystem toZipFileSystem(final Path zipPath) throws URISyntaxException, IOException {
// Don't call FileSystems.newFileSystems(URI, Map), because of a bug which
// encode twice URIs in jar file system : https://bugs.openjdk.java.net/browse/JDK-8131067
final List<FileSystemProvider> installedProviders = FileSystemProvider.installedProviders();
for (final FileSystemProvider fsp : installedProviders) {
if ("jar".equals(fsp.getScheme())) {
Map<String, String> properties = new HashMap<>();
properties.put("encoding", StandardCharsets.UTF_8.name());
properties.put("create", "true");
return fsp.newFileSystem(zipPath, properties);
}
}
throw new IOException("No file system found for ZIP encoding.");
}
/**
* Ensure input dates are either strictly equals, or represent the same moment.
* @param first
* @param second
* @return True if the 2 dates are same reference, or respect {@link LocalDate#isEqual(java.time.chrono.ChronoLocalDate) }.
*/
public static boolean checkEquality(final LocalDate first, final LocalDate second) {
return first == second || first != null && second != null && first.isEqual(second);
}
/**
* Write given local date into the specified object output. If input is null,
* a special flag is written, so a null value will be returned by {@link #readDate(java.io.ObjectInput) }.
* @param toWrite Local date to serialize.
* @param destination Output to write into.
* @throws IOException If an error occcurs while writing in output stream.
*/
public static void writeDate(final LocalDate toWrite, final ObjectOutput destination) throws IOException {
if (toWrite == null) {
destination.writeBoolean(true);
} else {
destination.writeBoolean(false);
destination.writeShort(toWrite.getYear());
destination.write(toWrite.getMonthValue());
destination.write(toWrite.getDayOfMonth());
}
}
/**
* Read date contained in given source at current position.
* IMPORTANT : Only read dates written using {@link #writeDate(java.time.LocalDate, java.io.ObjectOutput) }
* method.
* @param source Object stream to read from.
* @return Read date, or null.
* @throws IOException
*/
public static LocalDate readDate(final ObjectInput source) throws IOException {
if (source.readBoolean())
return null;
return LocalDate.of(source.readShort(), source.readByte(), source.readByte());
}
public static final Callback ROUNDING_CELL_FACTORY = value -> {
return new TableCell<Object, Number>() {
@Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
setText(SIMPLE_DECIMAL_FORMAT.format(item));
}
}
};
};
public static final StringConverter DOUBLE_CONVERTER = new StringConverter<Double>() {
@Override
public String toString(Double object) {
if (object == null)
return "";
else
return SECOND_DECIMAL_FORMAT.format(object);
}
@Override
public Double fromString(String string) {
if (string == null || (string = string.trim()).isEmpty()) {
return 0d;
} else
try {
return SECOND_DECIMAL_FORMAT.parse(string).doubleValue();
} catch (ParseException ex) {
try {
return Double.valueOf(string);
} catch (Exception ex2) {
ex.addSuppressed(ex2);
RhomeoCore.LOGGER.log(Level.FINE, "Unconvertible string.", ex);
}
}
return 0d;
}
};
}

View File

@ -0,0 +1,58 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core;
/**
* @author Cédric Briançon (Geomatys)
*/
public class RhomeoRuntimeException extends RuntimeException {
public RhomeoRuntimeException() {}
public RhomeoRuntimeException(String message) {
super(message);
}
public RhomeoRuntimeException(String message, Throwable cause) {
super(message, cause);
}
public RhomeoRuntimeException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,533 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core;
import fr.cenra.rhomeo.api.Version;
import fr.cenra.rhomeo.api.data.DataContext;
import fr.cenra.rhomeo.api.data.Dataset;
import fr.cenra.rhomeo.api.data.ObjectWrapper;
import fr.cenra.rhomeo.api.data.Protocol;
import fr.cenra.rhomeo.api.data.Reference;
import fr.cenra.rhomeo.api.data.ReferenceDescription;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.api.data.Statement;
import fr.cenra.rhomeo.api.process.ProcessContext;
import fr.cenra.rhomeo.api.result.Index;
import fr.cenra.rhomeo.api.ui.StatementEditor;
import fr.cenra.rhomeo.api.process.Indicator;
import fr.cenra.rhomeo.api.result.AdditionalValue;
import fr.cenra.rhomeo.api.ui.StatementEditorSpi;
import fr.cenra.rhomeo.core.data.DashboardResultsManager;
import fr.cenra.rhomeo.core.data.DataContextManager;
import fr.cenra.rhomeo.core.data.ReferenceManager;
import fr.cenra.rhomeo.core.preferences.ftp.FTPPreferences;
import java.beans.IntrospectionException;
import java.io.IOException;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import fr.cenra.rhomeo.core.util.SerializableDataContext;
import java.util.HashMap;
import java.util.HashSet;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javax.validation.constraints.NotNull;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.sis.util.ArgumentChecks;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.stereotype.Component;
/**
* Represents the application session for a user.
*
* @author Alexis Manin (Geomatys)
*/
@Component
public class Session implements ApplicationContextAware {
private static Session INSTANCE;
private ApplicationContext ctx;
private Dataset dataset;
private DataContext dataContext;
private ProcessContext processContext;
private Set<Index> results;
private final Set<AdditionalValue> additionalValues = new HashSet<>();
@Autowired
private List<Protocol> protocols;
@Autowired
private List<Indicator> indicators;
@Autowired
private DataContextManager contextManager;
@Autowired
private DashboardResultsManager resultsManager;
private final ReadOnlyObjectWrapper<WorkflowStep> workflowStep = new ReadOnlyObjectWrapper<>();
public static Session getInstance() throws IllegalStateException {
if (INSTANCE == null) {
throw new IllegalStateException("Application context not initialized !");
}
return INSTANCE;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (applicationContext != null) {
ctx = applicationContext;
INSTANCE = this;
} else {
INSTANCE = null;
}
}
/**
* Close the session, releasing all resources.
*/
public void close() {
if (ctx instanceof AbstractApplicationContext) {
((AbstractApplicationContext)ctx).close();
}
INSTANCE = null;
}
/**
* Try to create a new instance for given bean name.
*
* @param <T> Type of the wanted bean.
* @param qualifier Name of the wanted prototype.
* @param beanClass The class to use to create Spring prototype.
* @return A new bean corresponding to the prototype registered for given qualifier.
* @throws IllegalArgumentException If given bean definition is not a prototype.
*/
public <T> T createPrototype(final String qualifier, final Class<T> beanClass) throws IllegalArgumentException {
if (ctx.getAutowireCapableBeanFactory().isPrototype(qualifier)) {
return ctx.getBean(qualifier, beanClass);
} else {
throw new IllegalArgumentException("Given qualifier does not represent a prototype object !");
}
}
/**
* Try to create a new instance for given bean name.
*
* @param qualifier Name of the wanted prototype.
* @return A new bean corresponding to the prototype registered for given qualifier.
* @throws IllegalArgumentException If given bean definition is not a prototype.
*/
public Object createPrototype(final String qualifier) throws IllegalArgumentException {
if (ctx.getAutowireCapableBeanFactory().isPrototype(qualifier)) {
return ctx.getBean(qualifier);
} else {
throw new IllegalArgumentException("Given qualifier does not represent a prototype object !");
}
}
/**
* Try to create a new instance of given type.
*
* @param <T> Type of the wanted bean.
* @param beanClass The class to use to find Spring prototype definition.
* @param constructorArgs Possible list of constructor arguments for the requiered prototype.
* @return A new bean of given type.
* @throws IllegalArgumentException If given bean definition is not a prototype.
*/
public <T> T createPrototype(final Class<T> beanClass, final Object... constructorArgs) throws BeansException {
return ctx.getBean(beanClass, constructorArgs);
}
/**
* Create a new connection over the FTP service providing {@link Reference}
* data. The provided {@link FTPClient} must be closed using {@link FTPClient#disconnect() }
* once you've finished your work.
*
* @return A client to exchange data with FTP service which contains reference files.
*
* @throws IOException If we cannot connect to distant service.
* @throws java.security.GeneralSecurityException If an authentication is
* required, but we cannot access to password.
*/
@NotNull
public FTPClient connectReferenceFTP() throws IOException, GeneralSecurityException {
return ctx.getBean(FTPPreferences.class).getAccess(FTPPreferences.ACCESS_POINTS.REFERENCES).createClient();
}
/**
* Create a new connection over the FTP service providing result data.
* data. The provided {@link FTPClient} must be closed using {@link FTPClient#disconnect() }
* once you've finished your work.
*
* @return A client to exchange data with FTP service which contains result files.
*
* @throws UnknownHostException If we cannot connect to distant service.
* @throws java.security.GeneralSecurityException If an authentication is
* required, but we cannot access to password.
*/
@NotNull
public FTPClient connectResultFTP() throws IOException, GeneralSecurityException {
return ctx.getBean(FTPPreferences.class).getAccess(FTPPreferences.ACCESS_POINTS.RESULTS).createClient();
}
/**
* Try to find a singleton or prototype for the given class.
*
* @param <T> Type of the wanted bean.
* @param beanClass The class to use to find Spring prototype definition.
* @return A new bean of given type.
*/
public <T> T getBean(final Class<T> beanClass) {
return ctx.getBean(beanClass);
}
/**
* Try to find a singleton or prototype for the given class.
*
* @param beanName The name of a bean to find Spring prototype definition.
* @return A new bean of given type.
*/
public Object getBean(final String beanName) {
return ctx.getBean(beanName);
}
public DataContext getDataContext() {
return dataContext;
}
public Dataset getDataset() {
return dataset;
}
/**
* Request a new edition context for given site and protocol.
*
* /!\ Use with care ! Any call to this method will reset previously seized
* data.
*
* @param site The site to start edition for.
* @param protocol The protocol defining data type to seize.
*/
public void startEdition(final Site site, final Protocol protocol) {
ArgumentChecks.ensureNonNull("Site", site);
ArgumentChecks.ensureNonNull("Protocol", protocol);
if (!protocol.isCompatible(site)) {
throw new IllegalArgumentException("Target site is not compatible with chosen protocol !");
}
final SerializableDataContext srDataContext;
try {
srDataContext = contextManager.loadContext(site, protocol);
} catch (IOException ex) {
throw new RhomeoRuntimeException(ex);
}
if (srDataContext != null) {
final DataContext context = srDataContext.toDataContext(this);
if (context != null) {
this.dataContext = context;
} else {
this.dataContext = new DataContext(site, protocol);
}
} else {
this.dataContext = new DataContext(site, protocol);
}
// Now that we've got the data context, we'll check that its reference
// versions are installed. If not, we'll remove them from data context.
checkDataContext(dataContext);
this.dataset = new Dataset(protocol);
}
private static void checkDataContext(final DataContext ctx) {
for (final ReferenceDescription description : ctx.getProtocol().getReferenceTypes()) {
final ReferenceManager manager;
try {
manager = ReferenceManager.getOrCreate(description);
} catch (IntrospectionException ex) {
throw new RhomeoRuntimeException(ex);
}
if (!manager.getInstalledVersions().contains(ctx.getReferences().get(description.getReferenceType()))) {
ctx.getReferences().remove(description.getReferenceType());
}
}
}
/**
* A site name has been updated so update this old name in all serialized files
* working on the old name.
*
* @param site New site containing new name.
* @param oldName Old site name.
*/
public void serializeUpdateSiteName(Site site, String oldName) throws IOException, IntrospectionException, ReflectiveOperationException {
resultsManager.updateDashboardResultsSiteName(site, oldName);
contextManager.updateReferencesSiteName(oldName, site.getName());
}
/**
* A site has been deleted so delete all references of this site name in previously
* serialized files for this site.
*
* @param siteName
*/
public void serializeDeleteSite(final String siteName) throws IOException, IntrospectionException, ReflectiveOperationException {
resultsManager.removeDashboardResultsForSite(siteName);
contextManager.removeReferencesForSite(siteName);
}
/**
* Get results for the current {@linkplain DataContext data context}.
*
* @return A set of results, may be {@code null} or empty.
*/
public Set<Index> getResults() {
return results;
}
/**
* Set results for the current {@linkplain DataContext data context}.
*
* @param results a set of results.
*/
public void setResults(final Set<Index> results) {
this.results = results;
}
public Set<AdditionalValue> getAdditionalValues() {
return additionalValues;
}
/**
* Get the process context.
*
* @return process context, may be {@code null}.
*/
public ProcessContext getProcessContext() {
return processContext;
}
public void setProcessContext(ProcessContext processContext) {
this.processContext = processContext;
}
/**
*
* @return Observable property describing current application state.
*/
public ReadOnlyObjectProperty<WorkflowStep> workflowStepProperty() {
return workflowStep.getReadOnlyProperty();
}
/**
*
* @return application current working step. If null, it means application is
* waiting for the user to choose a protocol to operate on.
*/
public WorkflowStep getWorkflowStep() {
return workflowStep.get();
}
/**
* Request application to focus on given workflow step. This method will
* first ensure that all needed data can be found. If not, the step is left
* unchanged.
*
* @param newStep The step we want to go to.
* @return True if session succeeded to update the application mode. False
* if it cannot perform queried operation (due to missing data).
*/
public boolean requestWorkflowStep(final WorkflowStep newStep) {
if (newStep == null) {
clearWorkflow();
} else {
switch (newStep) {
case DATASET:
if (dataContext != null)
workflowStep.set(newStep);
break;
case PROCESS:
if (processContext != null && dataContext != null) {
checkDataContext(dataContext);
if (dataContext.getReferences().size() >= dataContext.getProtocol().getReferenceTypes().size()) {
workflowStep.set(newStep);
}
}
break;
case RESULT:
case FINALIZATION:
if (results != null)
workflowStep.set(newStep);
}
}
return newStep == workflowStep.get();
}
private void clearWorkflow() {
dataContext = null;
dataset = null;
processContext = null;
results = null;
additionalValues.clear();
workflowStep.set(null);
}
/**
* Get all protocols found.
*
* @return All protocols.
*/
public List<Protocol> getProtocols() {
return protocols;
}
/**
* Get all indicators found.
*
* @return All indicators.
*/
public List<Indicator> getIndicators() {
return indicators;
}
/**
* Return the description object corresponding to given {@link Reference} class.
* @param <T> Type of reference to get a description for.
* @param refType Type of reference to get a description for.
* @return Description object for requested reference class.
*/
public <T extends Reference> ReferenceDescription<T> getDescription(final Class<T> refType) {
final Map<String, ReferenceDescription> beans = ctx.getBeansOfType(ReferenceDescription.class);
for (final ReferenceDescription desc : beans.values()) {
if (desc.getReferenceType().equals(refType)) {
return desc;
}
}
return null;
}
/**
* Analyze the given object, and inject autowired fields according to the
* current application context content.
* @param o The object to inject dependencies into.
*/
public void injectDependencies(Object o) {
ctx.getAutowireCapableBeanFactory().autowireBean(o);
}
public Optional<StatementEditor> findEditor(Class<? extends Statement> dataType) {
ArgumentChecks.ensureNonNull("Data type", dataType);
final Map<String, StatementEditorSpi> beans = ctx.getBeansOfType(StatementEditorSpi.class);
StatementEditorSpi fallback = null;
Class<? extends Statement> editorType;
for (final StatementEditorSpi spi : beans.values()) {
editorType = spi.getDataType();
if (dataType.equals(editorType)) {
return Optional.of(spi.createEditor());
} else if (editorType.isAssignableFrom(dataType)) {
if (fallback == null || fallback.getDataType().isAssignableFrom(editorType)) {
fallback = spi;
}
}
}
return fallback == null? Optional.empty() : Optional.of(fallback.createEditor());
}
/**
* Find and return the first found wrapper for given data type, if any.
*
* @param <T> Data type to wrap.
* @param dataType The type we want to export.
* @return First found wrapper for input class, or an empty optional if no
* wrapper can be found.
*/
public <T> Optional<ObjectWrapper<T, ?>> getWrapper(final Class<T> dataType) {
if (dataType == null)
return Optional.empty();
final Map<String, ObjectWrapper> beans = ctx.getBeansOfType(ObjectWrapper.class);
for (final ObjectWrapper wrapper : beans.values()) {
if (wrapper.getInputType().equals(dataType))
return Optional.of(wrapper);
}
return Optional.empty();
}
/**
* Create a new map whose keys are description for given reference type.
* Values are not changed.
*
* We need this to convert reference versions provided by data context into
* something usable for metadata export.
*
* @param input The map to get values and keys to transform from.
* @return Copy of input map, with transformed keys.
*/
public Map<ReferenceDescription, Version> transform(final Map<Class<Reference>, Version> input) {
if (input == null || input.isEmpty())
return (Map)input;
final Map<ReferenceDescription, Version> result = new HashMap<>(input.size());
for (final Map.Entry<Class<Reference>, Version> entry : ((Map<Class<Reference>, Version>) input).entrySet()) {
result.put(getDescription(entry.getKey()), entry.getValue());
}
return result;
}
}

View File

@ -0,0 +1,100 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core;
import java.beans.IntrospectionException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.HashSet;
/**
*
* @author Alexis Manin (Geomatys)
*/
public class TypedCSVMappingBuilder<T> extends CSVMappingBuilder {
private final Class<T> type;
private final HashSet<String> ignorable = new HashSet<>();
public TypedCSVMappingBuilder(final CSVMappingBuilder builder, final Class<T> type) {
this.target = builder.target;
this.charset = builder.charset;
this.separator = builder.separator;
this.type = type;
}
public TypedCSVMappingBuilder<T> setIgnorable(String propertyName) {
if (propertyName != null && !(propertyName = propertyName.trim()).isEmpty()) {
ignorable.add(propertyName);
}
return this;
}
public final CSVEncoder<T> acquireEncoder() throws IOException, IntrospectionException {
return new CSVEncoder<>(target, type, separator, charset, ignorable);
}
public final CSVDecoder<T> acquireDecoder() throws IOException, IntrospectionException {
return new CSVDecoder<>(target, type, separator, charset, ignorable);
}
@Override
public TypedCSVMappingBuilder withSeparator(char separator) {
super.withSeparator(separator);
return this;
}
@Override
public TypedCSVMappingBuilder withEncoding(Charset charset) {
super.withEncoding(charset);
return this;
}
@Override
public TypedCSVMappingBuilder forPath(Path target) {
super.forPath(target);
return this;
}
}

View File

@ -0,0 +1,55 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core;
/**
* Describes possible states for the application. More accurately, it represents
* each step of the working process a user must go through to extract / publish
* results. In apparition order it is :
*
* DATASET : Data seizure / import / edition.
* PROCESS : Process parameterisation and execution.
* RESULT : Result compilation and consultation.
* FINALIZATION : Options of publication / extraction of the computed results.
*
* @author Alexis Manin (Geomatys)
*/
public enum WorkflowStep {
DATASET, PROCESS, RESULT, FINALIZATION;
}

View File

@ -0,0 +1,560 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data;
import com.fasterxml.jackson.core.JsonEncoding;
import fr.cenra.rhomeo.api.data.Protocol;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.api.process.Indicator;
import fr.cenra.rhomeo.api.result.DashboardResultItem;
import fr.cenra.rhomeo.api.result.Index;
import fr.cenra.rhomeo.api.result.IndicatorValuesByYearItem;
import fr.cenra.rhomeo.core.CSVDecoder;
import fr.cenra.rhomeo.core.CSVEncoder;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.RhomeoRuntimeException;
import fr.cenra.rhomeo.core.Session;
import fr.cenra.rhomeo.core.result.ResultStorage;
import fr.cenra.rhomeo.core.util.ExportUtils;
import fr.cenra.rhomeo.core.util.GeometryUtils;
import fr.cenra.rhomeo.core.data.site.SiteRepositoryImpl;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.sis.storage.DataStoreException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.beans.IntrospectionException;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.GeneralSecurityException;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.geotoolkit.data.geojson.GeoJSONStreamWriter;
import org.geotoolkit.nio.IOUtilities;
/**
* Handle results displayed on the dashboard.
*
* @author Cédric Briançon (Geomatys)
*/
@Component
public class DashboardResultsManager {
protected static final String HISTORY_LOG = "history.log";
@Autowired
private Session session;
@Autowired
private ResultStorage resultStorage;
/**
* Get all previously-published result items in the dashboard.
*
* @return A list of results items.
* @throws IOException
* @throws ReflectiveOperationException
* @throws IntrospectionException
*/
private List<DashboardResultItem> getDashboardResultsItems() throws IOException, ReflectiveOperationException, IntrospectionException {
if (!Files.exists(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS)) {
return new ArrayList<>();
}
final CSVDecoder<DashboardResultItem> decoder = new CSVDecoder<>(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS, DashboardResultItem.class);
return decoder.decode();
}
/**
* Regroups results for indicator by year.
*
* @param site Site to use. Should not be {@code null}.
* @return Results, never {@code null}
* @throws IntrospectionException
* @throws ReflectiveOperationException
* @throws IOException
*/
public ObservableList<IndicatorValuesByYearItem> getDashboardResultsByYear(final Site site) throws IntrospectionException, ReflectiveOperationException, IOException {
final ObservableList<DashboardResultItem> items = getDashboardResultsForSite(site);
final Map<Integer, IndicatorValuesByYearItem> indicatorsByYear = new HashMap<>();
for (final DashboardResultItem item : items) {
final int year = item.getYear();
if (indicatorsByYear.containsKey(year)) {
final IndicatorValuesByYearItem itemToUpdate = indicatorsByYear.get(year);
itemToUpdate.addValueForIndicator(item.getIndicator(), item.getValue(), item.isPublished());
} else {
final IndicatorValuesByYearItem itemToAdd = new IndicatorValuesByYearItem(year);
itemToAdd.addValueForIndicator(item.getIndicator(), item.getValue(), item.isPublished());
indicatorsByYear.put(year, itemToAdd);
}
}
return FXCollections.observableArrayList(indicatorsByYear.values());
}
/**
* Get previously-published result items for the specified site.
*
* @param site the selected site.
* @return A list of results items, never {@code null}
* @throws IOException
* @throws ReflectiveOperationException
* @throws IntrospectionException
*/
public ObservableList<DashboardResultItem> getDashboardResultsForSite(final Site site) throws IOException, ReflectiveOperationException, IntrospectionException {
final List<DashboardResultItem> allRes = getDashboardResultsItems();
final ObservableList<DashboardResultItem> resForSite = FXCollections.observableArrayList();
resForSite.addAll(allRes.stream()
.filter(item -> site.getName() != null && site.getName().equals(item.getSite()))
.collect(Collectors.toList()));
return resForSite;
}
/**
* Add or replace a list of result items.
*
* @param itemsToAdd A list of items to serialize.
* @throws IOException
* @throws ReflectiveOperationException
* @throws IntrospectionException
*/
public void addDashboardResultsItems(final List<DashboardResultItem> itemsToAdd) throws IOException, ReflectiveOperationException, IntrospectionException {
// Ensures the CSV file to write will exist
if (!Files.exists(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS)) {
Files.createFile(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS);
}
// First get existing results
final List<DashboardResultItem> existingResults = getDashboardResultsItems();
// Merge new / updates results into existing results.
for (final DashboardResultItem itemToAdd : itemsToAdd) {
boolean toAdd = true;
for (final DashboardResultItem existing : existingResults) {
if (existing.getSite().equals(itemToAdd.getSite()) &&
existing.getIndicator().equals(itemToAdd.getIndicator()) &&
existing.getYear() == itemToAdd.getYear())
{
// Same site, indicator and year, so just update the value
existing.setValue(itemToAdd.getValue());
existing.setPublished(itemToAdd.isPublished());
toAdd = false;
break;
}
}
// Not found for update, so just add it.
if (toAdd) {
existingResults.add(itemToAdd);
}
}
// Serialize them
final CSVEncoder<DashboardResultItem> encoder = new CSVEncoder<>(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS, DashboardResultItem.class);
encoder.encode(existingResults, true);
}
/**
* Rename site name in dashboard results. Use it when a site is renamed.
*
* @param site New site containing new name.
* @param oldName Old site name.
* @throws IntrospectionException
* @throws ReflectiveOperationException
* @throws IOException
*/
public void updateDashboardResultsSiteName(final Site site, final String oldName) throws IntrospectionException, ReflectiveOperationException, IOException {
if (!Files.exists(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS)) {
return;
}
// First get existing results
final List<DashboardResultItem> existingResults = getDashboardResultsItems();
existingResults.stream().filter(item -> item.getSite().equals(oldName))
.forEach(item -> item.setSite(site.getName()));
// Serialize them
final CSVEncoder<DashboardResultItem> encoder = new CSVEncoder<>(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS, DashboardResultItem.class);
encoder.encode(existingResults, true);
}
/**
* Update existing results, for published and value properties.
*
* @param itemsToUpdate
* @throws IntrospectionException
* @throws ReflectiveOperationException
* @throws IOException
*/
public void updateResults(final List<DashboardResultItem> itemsToUpdate) throws IntrospectionException, ReflectiveOperationException, IOException {
if (itemsToUpdate == null || itemsToUpdate.isEmpty()) {
return;
}
final CSVEncoder<DashboardResultItem> encoder = new CSVEncoder<>(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS, DashboardResultItem.class);
// Ensures the CSV file to write will exist. If not, create it and directly serialize them
if (!Files.exists(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS)) {
Files.createFile(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS);
// Serialize them
encoder.encode(itemsToUpdate, true);
return;
}
// First get existing results
final List<DashboardResultItem> existingResults = getDashboardResultsItems();
for (final DashboardResultItem itemToUp : itemsToUpdate) {
final int index = existingResults.indexOf(itemToUp);
if (index > -1) {
existingResults.set(index, itemToUp);
}
}
encoder.encode(existingResults, true);
}
/**
* Remove results entries for the specified site name.
*
* @param siteName
* @throws IntrospectionException
* @throws ReflectiveOperationException
* @throws IOException
*/
public void removeDashboardResultsForSite(final String siteName) throws IntrospectionException, ReflectiveOperationException, IOException {
if (siteName == null) {
return;
}
if (!Files.exists(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS)) {
return;
}
// First get existing results
final List<DashboardResultItem> existingResults = getDashboardResultsItems();
final List<DashboardResultItem> filteredList = existingResults.stream()
.filter(item -> !item.getSite().equals(siteName))
.collect(Collectors.toList());
// Delete all result files associated to the site
resultStorage.deleteIfExists(siteName);
// Serialize them
final CSVEncoder<DashboardResultItem> encoder = new CSVEncoder<>(RhomeoCore.DASHBOARD_PUBLISHED_RESULTS, DashboardResultItem.class);
encoder.encode(filteredList, true);
}
/**
* Send results currently contained in the session to the FTP service for
* result publication.
*
* Structure on the FTP is:
* <pre>
* results
* |- county code
* |--|- SHA1 geometry site
* |--|--|- protocol
* |--|--|--|- UUID
* |--|--|--|--|- site.geojson
* |--|--|--|--|- metadata.json
* |--|--|--|--|- result.csv
* ...
* </pre>
*/
public void publishResults() throws IOException, DataStoreException, IntrospectionException, GeneralSecurityException {
// Move to the right directory
final AtomicReference dirRef = new AtomicReference();
resultStorage.publish(() -> {
FTPClient ftpClient = null;
try {
ftpClient = session.connectResultFTP();
final String rootDir = ftpClient.printWorkingDirectory();
prepareSiteDirectory(session.getDataContext().getSite(), ftpClient);
prepareResultDirectory(session.getDataContext().getProtocol(), ftpClient);
dirRef.set(ftpClient.printWorkingDirectory().replace(rootDir, ""));
return ftpClient;
} catch (Exception e) {
if (ftpClient != null)
try {
ftpClient.disconnect();
} catch (IOException ex) {
e.addSuppressed(ex);
}
throw new RhomeoRuntimeException(e);
}
});
// log publication
final Object bundlePath = dirRef.get();
if (bundlePath != null) {
Optional<Log> log = fromSession(session, bundlePath.toString());
if (log.isPresent()) {
log(session.connectResultFTP(), log.get());
}
}
}
/**
* Publish site results on the FTP.
* Structure on the FTP is:
* <pre>
* results
* |- county code
* |--|- SHA1 geometry site
* |--|--|- protocol
* |--|--|--|- UUID
* |--|--|--|--|- site.geojson
* |--|--|--|--|- metadata.json
* |--|--|--|--|- result.csv
* ...
* </pre>
*
* @param site
* @param resultsForSite
* @param ftpClient
* @throws IOException
* @throws IntrospectionException
* @throws ReflectiveOperationException
*/
public void publishResults(final Site site, final List<DashboardResultItem> resultsForSite, final FTPClient ftpClient)
throws IOException, IntrospectionException, ReflectiveOperationException, DataStoreException
{
final Path tempFolder = Files.createTempDirectory("site");
final Path siteJsonPath = ExportUtils.writeGeoJson(site, tempFolder);
final ArrayList<DashboardResultItem> reallyPublished = new ArrayList<>(resultsForSite.size());
final ArrayList<Log> logs = new ArrayList<>(resultsForSite.size());
final String rootDir = ftpClient.printWorkingDirectory();
try {
prepareSiteDirectory(site, ftpClient);
for (final DashboardResultItem item : resultsForSite) {
final Object prototypeIndic = session.getBean(item.getIndicator());
if (prototypeIndic instanceof Indicator) {
final Indicator indicator = (Indicator) prototypeIndic;
prepareResultDirectory(indicator.getProtocol(), ftpClient);
try (final InputStream in = Files.newInputStream(siteJsonPath, StandardOpenOption.READ)) {
ftpClient.storeFile("site.geojson", in);
}
resultStorage.publish(item, ftpClient);
item.setPublished(true);
reallyPublished.add(item);
logs.add(new Log(site, ftpClient.printWorkingDirectory().replace(rootDir, ""), new int[]{item.getYear()}, indicator));
if (!ftpClient.changeToParentDirectory()) {
throw new IOException("Unable to change to parent directory");
}
if (!ftpClient.changeToParentDirectory()) {
throw new IOException("Unable to change to parent directory");
}
}
}
} finally {
try {
IOUtilities.deleteRecursively(tempFolder);
} catch (Exception e) {
RhomeoCore.LOGGER.log(Level.WARNING, "Cannot clear temporary files.", e);
}
if (!logs.isEmpty() && ftpClient.changeToParentDirectory() && ftpClient.changeToParentDirectory()) {
log(ftpClient, logs.toArray(new Log[logs.size()]));
}
// Update results publication state.
updateResults(reallyPublished);
}
}
/**
* Create (if needed) folders in which input site results will be put.
* Working directory is changed to the created/detected directory.
* @param site The site we want to post results for.
* @param ftpClient The FTP connection to work with.
*/
private static void prepareSiteDirectory(final Site site, final FTPClient ftpClient) throws IOException, DataStoreException {
// Move to county code ftp folder
String department = site.getCountyCode();
if (department == null || department.trim().isEmpty())
department = "00";
ftpClient.makeDirectory(department);
if (!ftpClient.changeWorkingDirectory(department)) {
throw new IOException("Unable to change of directory to " + department);
}
// Move to geom SHA1 ftp folder
final String sha1geom = GeometryUtils.getSha1(site.getGeometry());
ftpClient.makeDirectory(sha1geom);
if (!ftpClient.changeWorkingDirectory(sha1geom)) {
throw new IOException("Unable to change of directory to " + sha1geom);
}
if (ftpClient.listFiles(ExportUtils.SITE_JSON).length < 1) {
try (final OutputStream out = ftpClient.storeFileStream(ExportUtils.SITE_JSON)) {
GeoJSONStreamWriter.writeSingleFeature(out, SiteRepositoryImpl.toFeature(site), JsonEncoding.UTF8, 2, true);
}
ftpClient.completePendingCommand();
}
}
/**
* Check directories for result publication for a specific protocol. It will
* find or create, and then move to a folder named as input protocol, and
* then create and move to a folder whose name is an UUID.
*
* Note : For publication, you should already be in the site folder, i.e
* have called {@link #prepareSiteDirectory(fr.cenra.rhomeo.api.data.Site, org.apache.commons.net.ftp.FTPClient) }.
*
* @param p The protocol to post results for.
* @param ftpClient The FTP connection to work with.
*/
public static void prepareResultDirectory(final Protocol p, final FTPClient ftpClient) throws IOException {
final String protocolName = p.getName();
ftpClient.makeDirectory(protocolName);
if (!ftpClient.changeWorkingDirectory(protocolName)) {
throw new IOException("Unable to change of directory to " + protocolName);
}
final String uuid = UUID.randomUUID().toString();
ftpClient.makeDirectory(uuid);
if (!ftpClient.changeWorkingDirectory(uuid)) {
throw new IOException("Unable to change of directory to " + uuid);
}
}
private static void log(final FTPClient ftpClient, Log... toLog) throws IOException {
try (OutputStream out = ftpClient.appendFileStream(HISTORY_LOG);
final OutputStreamWriter outWriter = new OutputStreamWriter(out, StandardCharsets.UTF_8);
final BufferedWriter writer = new BufferedWriter(outWriter)) {
for (final Log log : toLog) {
writer.newLine();
writer.write(log.toString());
}
} finally {
ftpClient.completePendingCommand();
}
}
private static Optional<Log> fromSession(final Session session, final String bundlePath) {
if (session.getDataContext() == null || session.getResults() == null || session.getResults().isEmpty())
return Optional.empty();
final Set<Integer> years = new HashSet<>();
final Set<Indicator> indicators = new HashSet<>();
for (final Index i : session.getResults()) {
years.add(i.getYear());
indicators.add(i.getSpi().getIndicator());
}
if (years.isEmpty() || indicators.isEmpty())
return Optional.empty();
final int[] primYears = new int[years.size()];
int count = 0;
for (Integer year : years)
primYears[count++] = year;
return Optional.of(new Log(session.getDataContext().getSite(), bundlePath, primYears, indicators.toArray(new Indicator[indicators.size()])));
}
private static class Log {
final String siteName;
final String referent;
final String organisation;
final String targetBundle;
final String indicateurs;
final String annees;
public Log(final Site site, final String targetBundle, final int[] years, final Indicator... indicators) {
this(targetBundle, site.getName(), site.getReferent(), site.getOrganization(), years, indicators);
}
public Log(final String target, final String siteName, final String ref, final String org, final int[] years, final Indicator... indicators) {
this.siteName = siteName;
referent = ref;
organisation = org;
targetBundle = target;
Arrays.sort(years);
StringJoiner joiner = new StringJoiner(", ");
for (final int year : years)
joiner.add(String.valueOf(year));
this.annees = joiner.toString();
Arrays.sort(indicators);
joiner = new StringJoiner(", ");
for (final Indicator i : indicators)
joiner.add(i.getName());
indicateurs = joiner.toString();
}
@Override
public String toString() {
return new StringBuilder(256)
.append(ZonedDateTime.now().toString()).append(" : ")
.append(siteName).append(", ")
.append(referent == null || referent.isEmpty()? "referent inconnu" : referent)
.append(" (").append(organisation == null || organisation.isEmpty()? "organisation inconnue" : organisation).append(")")
.append(" publie ").append(targetBundle)
.append(" pour ").append(indicateurs)
.append(" en ").append(annees)
.toString();
}
}
}

View File

@ -0,0 +1,178 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data;
import com.fasterxml.jackson.databind.ObjectMapper;
import fr.cenra.rhomeo.api.data.DataContext;
import fr.cenra.rhomeo.api.data.Protocol;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.util.SerializableDataContext;
import org.apache.sis.util.ArgumentChecks;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Handle data context serialization.
*
* @author Cédric Briançon (Geomatys)
*/
@Component
public class DataContextManager {
private final ObjectMapper jsonMapper;
public DataContextManager() {
jsonMapper = new ObjectMapper();
}
/**
* Write data context
* @param dc Data context to serialize.
* @throws IOException
*/
public void writeDataContext(final DataContext dc) throws IOException {
SerializableDataContext[] contexts = getContexts();
final SerializableDataContext srDataContext = SerializableDataContext.fromDataContext(dc);
boolean found = false;
for (int i=0; i<contexts.length; i++) {
SerializableDataContext candidate = contexts[i];
if (candidate.protocolName.equals(srDataContext.protocolName) && candidate.siteName.equals(srDataContext.siteName)) {
found = true;
contexts[i] = srDataContext;
break;
}
}
if (!found) {
contexts = Arrays.copyOf(contexts, contexts.length + 1);
contexts[contexts.length - 1] = srDataContext;
}
final Path contextsPath = RhomeoCore.DATA_CONTEXT_SITE_PATH;
if (Files.notExists(contextsPath)) {
Files.createFile(contextsPath);
}
try (final BufferedWriter writer = Files.newBufferedWriter(contextsPath, StandardCharsets.UTF_8)) {
jsonMapper.writeValue(writer, contexts);
}
}
/**
* Get the data context for the given site and protocol.
*
* @param site Site to consider, must not be {@code null}
* @param protocol Protocol to consider, must not be {@code null}
* @return A data context stored previously or {@code null} if not found.
* @throws IOException
*/
public SerializableDataContext loadContext(final Site site, final Protocol protocol) throws IOException {
ArgumentChecks.ensureNonNull("Site", site);
ArgumentChecks.ensureNonNull("Protocol", protocol);
final SerializableDataContext[] contexts = getContexts();
for (final SerializableDataContext context : contexts) {
if (context.siteName.equals(site.getName()) && context.protocolName.equals(protocol.getName())) {
return context;
}
}
return null;
}
private SerializableDataContext[] getContexts() throws IOException {
final Path contextsPath = RhomeoCore.DATA_CONTEXT_SITE_PATH;
if (Files.notExists(contextsPath)) {
return new SerializableDataContext[0];
}
try (final BufferedReader reader = Files.newBufferedReader(contextsPath, StandardCharsets.UTF_8)) {
return jsonMapper.readValue(reader, SerializableDataContext[].class);
}
}
public void removeReferencesForSite(final String siteName) throws IOException {
final SerializableDataContext[] contexts = getContexts();
if (contexts.length == 0) {
return;
}
final List<SerializableDataContext> finalContexts =
Stream.of(contexts).filter(context -> !context.siteName.equals(siteName))
.collect(Collectors.toList());
// Something to serialize if we have less contexts in the end
if (contexts.length != finalContexts.size()) {
final Path contextsPath = RhomeoCore.DATA_CONTEXT_SITE_PATH;
try (final BufferedWriter writer = Files.newBufferedWriter(contextsPath, StandardCharsets.UTF_8)) {
jsonMapper.writeValue(writer, finalContexts.toArray());
}
}
}
public void updateReferencesSiteName(final String oldName, final String newName) throws IOException {
final SerializableDataContext[] contexts = getContexts();
if (contexts.length == 0) {
return;
}
boolean aChange = false;
final List<SerializableDataContext> finalContexts = new ArrayList<>();
for (final SerializableDataContext context : contexts) {
if (context.siteName.equals(oldName)) {
aChange = true;
context.siteName = newName;
}
finalContexts.add(context);
}
// Something to serialize if we have less contexts in the end
if (aChange) {
final Path contextsPath = RhomeoCore.DATA_CONTEXT_SITE_PATH;
try (final BufferedWriter writer = Files.newBufferedWriter(contextsPath, StandardCharsets.UTF_8)) {
jsonMapper.writeValue(writer, finalContexts.toArray());
}
}
}
}

View File

@ -0,0 +1,508 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data;
import fr.cenra.rhomeo.api.Version;
import fr.cenra.rhomeo.api.data.Reference;
import fr.cenra.rhomeo.api.data.ReferenceDescription;
import fr.cenra.rhomeo.core.CSVDecoder;
import fr.cenra.rhomeo.core.CSVMapper;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.RhomeoRuntimeException;
import fr.cenra.rhomeo.core.Session;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javafx.concurrent.Task;
import javax.validation.constraints.NotNull;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.collection.Cache;
/**
* Contains the list of available and installed version of a specific reference
* type. Also allow to get data contained in each installed version.
*
* @author Alexis Manin (Geomatys)
* @param <T> Type of reference managed.
*/
public class ReferenceManager<T extends Reference> {
/**
* A regex to detect CSV files containing a version number composed of dot separated digits .
*/
private static final Pattern REF_FILE_PATTERN = Pattern.compile("(?i)(\\d+(?:\\.\\d+)*)(.csv)?$");
/**
* We register created managers here.
*/
private static final Cache<String, ReferenceManager> INSTANCES = new Cache<>(13, 13, true);
/**
*
* @param <R> The type of reference to get a manager for.
* @param descriptor A descriptor for the reference list to check.
* @return The manager corresponding to the given reference description.
* @throws java.beans.IntrospectionException If we cannot analyze given reference type.
*/
public static <R extends Reference> ReferenceManager<R> getOrCreate(final ReferenceDescription<R> descriptor) throws IntrospectionException {
try {
return INSTANCES.getOrCreate(descriptor.getName(), () -> new ReferenceManager<>(descriptor));
} catch (IntrospectionException | RuntimeException ex) {
throw ex;
} catch (Exception e) {
throw new RhomeoRuntimeException(e);
}
}
/**
* Target (managed) reference type.
*/
private final ReferenceDescription<T> refType;
/**
* Information about reference pojo (properties, methods, etc.).
*/
private final BeanInfo refInfo;
/**
* Directory where installed versions should be located.
*/
private final Path refDir;
/**
* A map containing installed versions we found on {@link #refresh() }.
*/
private final ObservableMap<Version, Path> installed;
/**
* A map containing versions found in FTP server on {@link #refresh() }.
*/
private final ObservableMap<Version, FTPFile> distant;
private final Cache<Version, List<T>> loadedVersions = new Cache(2, 0, false);
private WeakReference<List<T>> lastLoaded;
private final Set<Version> operations;
private Task refreshTask;
private ReferenceManager(@NotNull ReferenceDescription<T> refType) throws IntrospectionException {
this.refType = refType;
final String refName = refType.getName();
ArgumentChecks.ensureNonEmpty("Reference type name", refName);
refInfo = Introspector.getBeanInfo(refType.getReferenceType(), Object.class);
refDir = RhomeoCore.REFERENCE_PATH.resolve(refName);
installed = FXCollections.observableMap(new HashMap<>());
distant = FXCollections.observableMap(new HashMap<>());
operations = new HashSet<>();
}
/**
* Force this manager to rebuild its data from scratch. The local files and
* FTP service will be scanned anew to find available and installed reference
* lists.
*
* Note : local data is checked first, so if the distant service does not
* provide correct answers and the task fails, we can still access installed
* versions.
* @return A task which refresh data. If a refresh is ready to run or
* already running, the corresponding task is returned. Otherwise, a new
* task is returned which must be launched by the caller.
*/
public synchronized Task<ReferenceManager> refresh() {
if (refreshTask == null || refreshTask.isDone()) {
refreshTask = new Task() {
@Override
protected Object call() throws Exception {
updateTitle("Recherche de versions : ".concat(refType.getTitle()));
updateMessage("Vérification des versions installées");
installed.clear();
Files.createDirectories(refDir);
Files.walkFileTree(refDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
final Matcher matcher = REF_FILE_PATTERN.matcher(file.getFileName().toString());
if (matcher.find()) {
if (isValid(file)) {
installed.put(new Version(matcher.group(1)), file);
}
}
return super.visitFile(file, attrs);
}
});
updateMessage("Vérification des versions disponibles sur le FTP");
distant.clear();
final FTPClient ftp = Session.getInstance().connectReferenceFTP();
try {
if (ftp.changeWorkingDirectory(refType.getName())) {
ftp.listFiles(".", file -> {
if (file.isFile() && file.getSize() > 0) {
final Matcher matcher = REF_FILE_PATTERN.matcher(file.getName());
if (matcher.find()) {
try (final InputStream retStream = ftp.retrieveFileStream(file.getName())) {
if (isValid(retStream)) {
distant.put(new Version(matcher.group(1)), file);
return true;
}
} catch (IOException e) {
RhomeoCore.LOGGER.log(Level.WARNING, "An error occurred while reading an FTP file : ".concat(file.getName()), e);
} finally {
try {
// Ensures pending command are finished
if (!ftp.completePendingCommand()) {
final int replyCode = ftp.getReplyCode();
final String replyMessage = ftp.getReplyString();
final Supplier<String> msgSupplier = () -> {
final StringBuilder builder = new StringBuilder("Error on pending command completion.");
builder.append(System.lineSeparator()).append("File : ").append(file.getName());
builder.append(System.lineSeparator()).append("Reply code : ").append(replyCode);
builder.append(System.lineSeparator()).append("Reply message : ").append(replyMessage);
return builder.toString();
};
RhomeoCore.LOGGER.log(Level.INFO, msgSupplier);
}
} catch (IOException ex) {
RhomeoCore.LOGGER.log(Level.WARNING, "An error occurred while trying to complete pending command for FTP file : ".concat(file.getName()), ex);
}
}
}
}
return false;
});
}
} finally {
try {
ftp.disconnect();
} catch (IOException e) {
RhomeoCore.LOGGER.log(Level.WARNING, "An FTP client cannot be disconnected : References.", e);
}
}
return this;
}
};
}
return refreshTask;
}
/**
*
* @return Set of versions available on FTP server. Unmodifiable. The Set is
* automatically updated when the manager is refreshed.
*/
public Set<Version> getDistantVersions() {
return distant.keySet();
}
/**
*
* @return Set of versions installed locally. Unmodifiable. The Set is
* automatically updated when the manager is refreshed.
*/
public Set<Version> getInstalledVersions() {
return installed.keySet();
}
/**
* Create a task in charge of the installation of a new reference version.
* @param toInstall Version of the reference list to install.
* @return A task (not submitted yet) to run to install given version.
* @throws IllegalStateException If an installation or uninstallation procedure
* is already running for the version.
*/
public Task install(@NotNull final Version toInstall) throws IllegalStateException {
synchronized (operations) {
if (operations.contains(toInstall)) {
throw new IllegalStateException(
new StringBuilder("An operation is already running for version ")
.append(toInstall.toString())
.append(" of list ")
.append(refType.getName())
.toString()
);
}
}
return new Task() {
@Override
protected Object call() throws Exception {
final boolean alreadyRunning;
synchronized (operations) {
alreadyRunning = !operations.add(toInstall);
}
try {
if (alreadyRunning) {
updateMessage(
new StringBuilder("An operation is already running for version ")
.append(toInstall.toString())
.append(" of list ")
.append(refType.getName())
.toString()
);
cancel();
} else if (installed.containsKey(toInstall)) {
updateMessage("Queried version is already installed : ".concat(toInstall.toString()));
cancel();
} else {
updateTitle("Téléchargement ".concat(toInstall.toString()));
FTPFile ftpFile = distant.get(toInstall);
if (distant == null) {
throw new IllegalArgumentException("Queried version is not available : ".concat(toInstall.toString()));
}
final Path newRef = refDir.resolve(toInstall.toString().concat(".csv"));
final FTPClient ftp = Session.getInstance().connectReferenceFTP();
ftp.changeWorkingDirectory(refType.getName());
try (final OutputStream out = Files.newOutputStream(newRef)) {
ftp.retrieveFile(ftpFile.getName(), out);
} finally {
try {
ftp.disconnect();
} catch (IOException e) {
RhomeoCore.LOGGER.log(Level.WARNING, "An FTP client cannot be disconnected : References.", e);
}
}
installed.put(toInstall, newRef);
return newRef;
}
} finally {
synchronized (operations) {
if (!alreadyRunning) {
operations.remove(toInstall);
}
}
}
return false;
}
};
}
/**
* Create a task in charge of removing a local reference version.
* @param toUninstall Version of the reference list to uninstall.
* @return A task (not submitted yet) to run to uninstall given version.
* @throws IllegalStateException If an installation or uninstallation procedure
* is already running for the version.
*/
public Task uninstall(@NotNull final Version toUninstall) throws IllegalStateException {
synchronized (operations) {
if (operations.contains(toUninstall)) {
throw new IllegalStateException(
new StringBuilder("An operation is already running for version ")
.append(toUninstall.toString())
.append(" of list ")
.append(refType.getName())
.toString()
);
}
}
return new Task() {
@Override
protected Object call() throws Exception {
final boolean alreadyRunning;
synchronized (operations) {
alreadyRunning = !operations.add(toUninstall);
}
try {
if (alreadyRunning) {
updateMessage(
new StringBuilder("An operation is already running for version ")
.append(toUninstall.toString())
.append(" of list ")
.append(refType.getName())
.toString()
);
cancel();
} else if (!installed.containsKey(toUninstall)) {
updateMessage("Queried version is already uninstalled : ".concat(toUninstall.toString()));
cancel();
} else {
updateTitle("Suppression ".concat(toUninstall.toString()));
final Path toDelete = installed.get(toUninstall);
Files.delete(toDelete);
return installed.remove(toUninstall);
}
} finally {
synchronized (operations) {
if (!alreadyRunning) {
operations.remove(toUninstall);
}
}
}
return false;
}
};
}
/**
* Check if given file header lists all properties defined in reference
* type.
*
* @param file The file to check.
* @return True if the file header describes the current reference type,
* false otherwise.
* @throws IOException If we cannot read the file.
* @throws IntrospectionException If we cannot extract property description
* from current reference type.
*/
private boolean isValid(@NotNull final Path file) throws IOException {
final String line;
try (final BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
line = reader.readLine();
}
return isValidHeader(line);
}
/**
* Check if given file header lists all properties defined in reference
* type.
*
* @param file The file to check.
* @return True if the file header describes the current reference type,
* false otherwise.
* @throws IOException If we cannot read the file.
* @throws IntrospectionException If we cannot extract property description
* from current reference type.
*/
private boolean isValid(@NotNull final InputStream file) throws IOException {
final String line;
try (final InputStreamReader in = new InputStreamReader(file, StandardCharsets.UTF_8);
final BufferedReader reader = new BufferedReader(in)) {
line = reader.readLine();
}
return isValidHeader(line);
}
/**
* Check if given header lists all properties defined in reference type.
*
* @param firstLine The header to check.
* @return True if the file header describes the current reference type,
* false otherwise.
*/
private boolean isValidHeader(@NotNull final String firstLine) {
return CSVMapper.isValidHeader(firstLine, CSVMapper.DEFAULT_SEPARATOR, refInfo);
}
/**
* Try to load reference values for the given version. Before calling this
* method, you MUST have called
* {@link #refresh() } at least once, and checked the queried version is
* already installed.
* @param toLoad The version of the list to load.
* @return A read-only list containing all values for the queried references.
* @throws IllegalArgumentException If the queried version is not installed,
* or the manager has not been refreshed yet.
* @throws RhomeoRuntimeException If an error occurs while reading list file.
*/
public synchronized List<T> getValues(final Version toLoad) throws IllegalArgumentException, RhomeoRuntimeException {
try {
final List<T> result = loadedVersions.getOrCreate(toLoad, () -> {
final Path p = installed.get(toLoad);
if (p == null) {
throw new IllegalArgumentException("Queried version is not installed, or the manager has not been refreshed yet !");
}
return Collections.unmodifiableList(
new CSVDecoder<>(p, refType.getReferenceType(), StandardCharsets.UTF_8).decode());
});
lastLoaded = new WeakReference(result);
return result;
} catch (Exception ex) {
throw new RhomeoRuntimeException(ex);
}
}
/**
*
* @return The list of references which have been loaded the last time {@link #getValues(fr.cenra.rhomeo.api.Version) }
* has been called, if any.
*/
public synchronized Optional<List<T>> getLastLoaded() {
if (lastLoaded == null)
return Optional.empty();
else return Optional.ofNullable(lastLoaded.get());
}
}

View File

@ -0,0 +1,75 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data;
import fr.cenra.rhomeo.api.data.TrackingPoint;
/**
* @author Cédric Briançon (Geomatys)
*/
public class TrackingPointValue implements Comparable<TrackingPointValue> {
private final TrackingPoint trackingPoint;
private final Double value;
public TrackingPointValue(final TrackingPoint trackingPoint, final Double value) {
this.trackingPoint = trackingPoint;
this.value = value;
}
public TrackingPoint getTrackingPoint() {
return trackingPoint;
}
public Double getValue() {
return value;
}
@Override
public int compareTo(TrackingPointValue o) {
if (value == null) {
return 1;
}
if (o == null || o.getValue() == null) {
return -1;
}
return getValue().compareTo(o.getValue());
}
}

View File

@ -0,0 +1,163 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import fr.cenra.rhomeo.api.Version;
import fr.cenra.rhomeo.core.util.VersionDeserializer;
import fr.cenra.rhomeo.core.util.VersionSerializer;
import fr.cenra.rhomeo.core.util.ZonedDateTimeDeserializer;
import fr.cenra.rhomeo.core.util.ZonedDateTimeSerializer;
import java.net.URL;
import java.time.ZonedDateTime;
/**
* A simple pojo which contains information about application available package
*
* @author Alexis Manin (Geomatys)
*/
public class UpdateInfo {
private Version version;
private ZonedDateTime date;
private String[] releaseNote;
private URL win32;
private String win32MD5;
private URL deb64;
private String deb64MD5;
private URL rpm64;
private String rpm64MD5;
private URL macOS64;
private String macOS64MD5;
@JsonSerialize(using = VersionSerializer.class)
public Version getVersion() {
return version;
}
@JsonDeserialize(using = VersionDeserializer.class)
public void setVersion(Version version) {
this.version = version;
}
public URL getWin32() {
return win32;
}
@JsonSerialize(using = ZonedDateTimeSerializer.class)
public ZonedDateTime getDate() {
return date;
}
@JsonDeserialize(using = ZonedDateTimeDeserializer.class)
public void setDate(ZonedDateTime date) {
this.date = date;
}
public String[] getReleaseNote() {
return releaseNote;
}
public void setReleaseNote(String[] releaseNote) {
this.releaseNote = releaseNote;
}
public void setWin32(URL win32) {
this.win32 = win32;
}
public URL getDeb64() {
return deb64;
}
public void setDeb64(URL deb64) {
this.deb64 = deb64;
}
public URL getRpm64() {
return rpm64;
}
public void setRpm64(URL rpm64) {
this.rpm64 = rpm64;
}
public URL getMacOS64() {
return macOS64;
}
public void setMacOS64(URL macOS64) {
this.macOS64 = macOS64;
}
public String getWin32MD5() {
return win32MD5;
}
public void setWin32MD5(String win32MD5) {
this.win32MD5 = win32MD5;
}
public String getDeb64MD5() {
return deb64MD5;
}
public void setDeb64MD5(String deb64MD5) {
this.deb64MD5 = deb64MD5;
}
public String getRpm64MD5() {
return rpm64MD5;
}
public void setRpm64MD5(String rpm64MD5) {
this.rpm64MD5 = rpm64MD5;
}
public String getMacOS64MD5() {
return macOS64MD5;
}
public void setMacOS64MD5(String macOS64MD5) {
this.macOS64MD5 = macOS64MD5;
}
}

View File

@ -0,0 +1,89 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.county;
import org.apache.sis.util.ArgumentChecks;
/**
* A simple POJO to display the name and code of a county.
*
* @author Alexis Manin (Geomatys)
*/
public class County implements Comparable<County> {
/**
* Code of the county. An Integer most of the time, except for Corse (2A, 2B).
*/
protected final String code;
/**
* Name of the county (Corse, Isère, etc.).
*/
protected final String name;
/**
* Build a new county object.
* @param code The code of the county. Not null
* @param name The name of the county. Not null.
*/
protected County(final String code, final String name) {
ArgumentChecks.ensureNonNull("County code", code);
ArgumentChecks.ensureNonNull("County name", name);
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
@Override
public String toString() {
return new StringBuilder(code).append(' ').append(name).toString();
}
@Override
public int compareTo(County o) {
return o == null? -1 : code.compareTo(o.code);
}
}

View File

@ -0,0 +1,255 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.county;
import com.vividsolutions.jts.geom.Geometry;
import fr.cenra.rhomeo.core.RhomeoCore;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.regex.Matcher;
import javafx.util.StringConverter;
import javax.annotation.PreDestroy;
import org.apache.sis.storage.DataStoreException;
import org.geotoolkit.data.FeatureCollection;
import org.geotoolkit.data.FeatureReader;
import org.geotoolkit.data.query.QueryBuilder;
import org.geotoolkit.data.shapefile.ShapefileFeatureStore;
import org.geotoolkit.factory.FactoryFinder;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.feature.Feature;
import org.geotoolkit.nio.IOUtilities;
import org.geotoolkit.referencing.CRS;
import org.opengis.filter.FilterFactory2;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.util.FactoryException;
import org.opengis.util.GenericName;
import org.springframework.stereotype.Component;
/**
* A repository to load / search for counties.
*
* @author Alexis Manin (Geomatys)
*/
@Component
public final class CountyRepository implements AutoCloseable {
/**
* List of resources to load to read County data.
*/
private static final String[] RESOURCES = new String[]{
"DEPARTEMENT.shp",
"DEPARTEMENT.shx",
"DEPARTEMENT.dbf",
"DEPARTEMENT.prj"
};
/**
* Attributes to read into the shapefile embedding county data.
*/
private static final String COUNTY_CODE = "CODE_DEPT";
private static final String COUNTY_NAME = "NOM_DEPT";
private final ShapefileFeatureStore store;
private final GenericName name;
private final StringConverter converter;
private final CoordinateReferenceSystem siteCRS;
private final FilterFactory2 ff;
private final Map<String, County> counties;
private final Path dataDir;
CountyRepository() throws IOException, URISyntaxException, DataStoreException, FactoryException {
// Copy resources on local filesystem
dataDir = Files.createTempDirectory("countyShape");
for (final String str : RESOURCES) {
try (final InputStream tmpStream = County.class.getResourceAsStream(str)) {
Files.copy(tmpStream, dataDir.resolve(str));
}
}
siteCRS = RhomeoCore.getSiteCRS();
store = new ShapefileFeatureStore(dataDir.resolve(RESOURCES[0]).toUri());
name = store.getName();
converter = new Converter();
final Hints hints = new Hints();
hints.put(Hints.FILTER_FACTORY, FilterFactory2.class);
ff = (FilterFactory2) FactoryFinder.getFilterFactory(hints);
counties = readCounties();
}
/**
* Find a county for the given code.
* @param code The code of the county. Ex : 34 for Hérault.
* @return The matching county, or null if we cannot find any.
*/
public County get(final String code) {
return code == null? null : counties.get(code);
}
/**
*
* @param geom The geometry to find counties for.
* @return List of counties intersecting the input geometry. Never null, but
* can be empty.
*/
public List<County> get(final Geometry geom) {
final ArrayList<County> result = new ArrayList<>();
if (geom == null || geom.isEmpty())
return result;
try {
final QueryBuilder builder = new QueryBuilder(name);
final CoordinateReferenceSystem fCRS = store.getFeatureType(name).getCoordinateReferenceSystem();
if (fCRS != null && !CRS.equalsApproximatively(fCRS, siteCRS)) {
builder.setCRS(siteCRS);
}
final GenericName geomName = store.getFeatureType().getGeometryDescriptor().getName();
builder.setProperties(new String[]{COUNTY_CODE, geomName.tip().toString()});
builder.setFilter(ff.intersects(ff.property(geomName), ff.literal(geom)));
try (FeatureReader reader = store.getFeatureReader(builder.buildQuery())) {
while (reader.hasNext()) {
result.add(counties.get(reader.next().getPropertyValue(COUNTY_CODE)));
}
}
} catch (Exception e) {
RhomeoCore.LOGGER.log(Level.FINE, "Cannot perform intersection on counties !", e);
}
return result;
}
/**
*
* @return Complete list of metropolitan France counties, indexed by code.
*/
public Map<String, County> getAll() {
return counties;
}
public FeatureCollection getFeatures() {
return store.createSession(false).getFeatureCollection(QueryBuilder.all(name));
}
/**
* Load all counties in memory.
* @return An unmodifiable map of available counties. Key is code, value is
* county.
* @throws DataStoreException
* @throws FactoryException
*/
private Map<String, County> readCounties() throws DataStoreException, FactoryException {
final QueryBuilder builder = new QueryBuilder(name);
final CoordinateReferenceSystem fCRS = store.getFeatureType(name).getCoordinateReferenceSystem();
if (fCRS != null && !CRS.equalsApproximatively(fCRS, siteCRS)) {
builder.setCRS(siteCRS);
}
builder.setProperties(new String[]{COUNTY_CODE, COUNTY_NAME});
// read and convert data.
Map<String, County> tmpCounties = new HashMap();
try (FeatureReader reader = store.getFeatureReader(builder.buildQuery())) {
Feature dep;
County county;
while (reader.hasNext()) {
dep = reader.next();
county = new County(
dep.getPropertyValue(COUNTY_CODE).toString(),
dep.getPropertyValue(COUNTY_NAME).toString()
);
tmpCounties.put(county.code, county);
}
}
return Collections.unmodifiableMap(tmpCounties);
}
@PreDestroy
@Override
public void close() throws Exception {
try {
store.close();
} finally {
IOUtilities.deleteRecursively(dataDir);
}
}
public StringConverter<County> getStringConverter() {
return converter;
}
private class Converter extends StringConverter<County> {
@Override
public String toString(County object) {
if (object != null)
return object.toString();
return null;
}
@Override
public County fromString(String string) {
if (string == null || (string = string.trim()).isEmpty()) {
return null;
}
final Matcher matcher = RhomeoCore.CODE_PATTERN.matcher(string);
if (matcher.find()) {
return get(matcher.group());
}
return null;
}
}
}

View File

@ -0,0 +1,140 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import fr.cenra.rhomeo.api.data.Reference;
import javax.validation.constraints.NotNull;
/**
* @author Cédric Briançon (Geomatys)
*/
public class FloreBassinReference implements Reference {
/**
* Primary key.
*/
private int cd_nom;
private int cd_ref;
private String nom;
private Integer nutriment;
private Integer humidite;
private Integer cc;
public FloreBassinReference() {
}
public FloreBassinReference(@NotNull int cdNom, String nom, Integer nutriment, Integer humidite, Integer cc) {
this.cd_nom = cdNom;
this.nom = nom;
this.nutriment = nutriment;
this.humidite = humidite;
this.cc = cc;
}
public @NotNull int getCd_nom() {
return cd_nom;
}
public void setCd_nom(@NotNull int cd_nom) {
this.cd_nom = cd_nom;
}
public @NotNull int getCd_ref() {
return cd_ref;
}
public void setCd_ref(@NotNull int cd_ref) {
this.cd_ref = cd_ref;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public Integer getNutriment() {
return nutriment;
}
public void setNutriment(Integer nutriment) {
this.nutriment = nutriment;
}
public Integer getHumidite() {
return humidite;
}
public void setHumidite(Integer humidite) {
this.humidite = humidite;
}
public Integer getCc() {
return cc;
}
public void setCc(Integer cc) {
this.cc = cc;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FloreBassinReference that = (FloreBassinReference) o;
return cd_nom == that.cd_nom;
}
@Override
public int hashCode() {
return cd_nom;
}
@Override
public String toString() {
return nom;
}
}

View File

@ -0,0 +1,72 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import fr.cenra.rhomeo.api.data.ReferenceDescription;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.Collections;
/**
* @author Cédric Briançon (Geomatys)
*/
@Component
public class FloreBassinReferenceDescription implements ReferenceDescription<FloreBassinReference> {
@Override
public Class<FloreBassinReference> getReferenceType() {
return FloreBassinReference.class;
}
@Override
public String getName() {
return "flore_bassin";
}
@Override
public Collection<String> getAlias() {
return Collections.singletonList("Valeurs indicatrices des espèces floristiques");
}
@Override
public String getRemarks() {
return "Référentiel flore agrégeant l'ensemble des taxons du bassin RMC (cf. Gilles PACHE)";
}
}

View File

@ -0,0 +1,325 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import com.vividsolutions.jts.geom.Geometry;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.util.GeometryUtils;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javafx.beans.binding.BooleanExpression;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.storage.DataStoreException;
import org.geotoolkit.data.FeatureCollection;
import org.geotoolkit.data.FeatureIterator;
import org.geotoolkit.data.FeatureStore;
import org.geotoolkit.data.query.QueryBuilder;
import org.geotoolkit.data.session.Session;
import org.geotoolkit.filter.DefaultPropertyName;
import org.geotoolkit.geometry.jts.JTS;
import org.geotoolkit.storage.DataStores;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.FilterFactory2;
import org.opengis.util.GenericName;
/**
*
* @author Johann Sorel (Geomatys)
*/
public class GeoReferential implements Comparable<GeoReferential>, AutoCloseable{
private final Site site;
private final int year;
private final SimpleBooleanProperty zoneHydro = new SimpleBooleanProperty();
private final SimpleBooleanProperty tacheUrbaine = new SimpleBooleanProperty();
private final SimpleBooleanProperty tacheArtif = new SimpleBooleanProperty();
private final SimpleBooleanProperty rpg = new SimpleBooleanProperty();
private final SimpleBooleanProperty wfszoneHydro = new SimpleBooleanProperty();
private final SimpleBooleanProperty wfstacheUrbaine = new SimpleBooleanProperty();
private final SimpleBooleanProperty wfstacheArtif = new SimpleBooleanProperty();
private final SimpleBooleanProperty wfsrpg = new SimpleBooleanProperty();
private final SimpleBooleanProperty download = new SimpleBooleanProperty();
private FeatureStore store;
public GeoReferential(Site site, int year) {
this.site = site;
this.year = year;
}
public int getYear() {
return year;
}
public BooleanProperty zoneHydroLocal() {
return zoneHydro;
}
public BooleanProperty tacheUrbaineLocal() {
return tacheUrbaine;
}
public BooleanProperty tacheArtifLocal() {
return tacheArtif;
}
public BooleanProperty rpgLocal() {
return rpg;
}
public BooleanProperty zoneHydroWFS() {
return wfszoneHydro;
}
public BooleanProperty tacheUrbaineWFS() {
return wfstacheUrbaine;
}
public BooleanProperty tacheArtifWFS() {
return wfstacheArtif;
}
public BooleanProperty rpgWFS() {
return wfsrpg;
}
public BooleanProperty toDownloadProperty() {
return download;
}
public boolean hasLocalTypes(String ... types){
for(String type : types){
switch(type.toLowerCase()){
case GeoReferentials.TYPE_RPG:
if(!rpg.get()) return false;
break;
case GeoReferentials.TYPE_ZONE_HYDRO:
if(!zoneHydro.get()) return false;
break;
case GeoReferentials.TYPE_TACHE_ARTIF:
if(!tacheArtif.get()) return false;
break;
case GeoReferentials.TYPE_TACHE_URBAINE:
if(!tacheUrbaine.get()) return false;
break;
}
}
return true;
}
public boolean hasWFSTypes(String ... types){
for(String type : types){
switch(type.toLowerCase()){
case GeoReferentials.TYPE_RPG:
if(!wfsrpg.get()) return false;
break;
case GeoReferentials.TYPE_ZONE_HYDRO:
if(!wfszoneHydro.get()) return false;
break;
case GeoReferentials.TYPE_TACHE_ARTIF:
if(!wfstacheArtif.get()) return false;
break;
case GeoReferentials.TYPE_TACHE_URBAINE:
if(!wfstacheUrbaine.get()) return false;
break;
}
}
return true;
}
/**
* Check if given types are available either locally or on configured WFS service.
* @param types Name of the types to check presence. If null, empty, or if
* no valid name is present, we consider that predicate cannot be tested, so
* we return false.
* @return True if all given names can be found locally or on WFS service.
* False otherwise, or if input data is null or empty.
*/
public boolean areAvailable(final String... types) {
if (types == null || types.length < 1)
return false;
boolean validNameFound = false;
for (final String type : types) {
if (type != null && !type.isEmpty()) {
validNameFound = true;
break;
}
}
if (!validNameFound)
return false; // No valid name to test.
for (final String type : types) {
if (type == null || type.isEmpty())
continue;
if (!(hasLocalTypes(type) || hasWFSTypes(type)))
return false; // One of the given names is not available.
}
return true;
}
/**
*
* @param typeName
* @return
* @throws DataStoreException
*/
public FeatureCollection getFeatureCollection(String typeName, Geometry intersectGeom) throws DataStoreException{
if (store == null) {
Path folder = RhomeoCore.REFERENCIELS_GEO_PATH.resolve(GeometryUtils.getSha1(site.getGeometry())).resolve(String.valueOf(year));
if (!Files.isDirectory(folder)) {
folder = RhomeoCore.REFERENCIELS_GEO_PATH.resolve(site.getName()).resolve(String.valueOf(year));
}
final Map parameters = new HashMap();
parameters.put("identifier", "shapefile-folder");
parameters.put("path", folder.toUri());
parameters.put("namespace", "no namespace");
store = (FeatureStore) DataStores.open(parameters);
}
Set<GenericName> names = store.getNames();
if (names == null || names.isEmpty())
throw new DataStoreException("No geo-reference available !");
GenericName name = null;
final String lcTypeName = typeName.toLowerCase();
for (final GenericName tmpName : names) {
if (tmpName.tip().toString().toLowerCase().startsWith(lcTypeName)) {
name = tmpName;
}
}
if (name == null)
throw new IllegalArgumentException("No reference available for name ".concat(typeName));
final Session session = store.createSession(true);
final QueryBuilder qb = new QueryBuilder();
qb.setTypeName(name);
if(intersectGeom!=null){
final FilterFactory2 ff = ((FilterFactory2)DefaultFactories.forBuildin(FilterFactory.class));
qb.setFilter(ff.intersects(new DefaultPropertyName("the_geom"), ff.literal(intersectGeom)));
}
return session.getFeatureCollection(qb.buildQuery());
}
/**
* Compute a geometry which is the union of all geometries in specified
* referential which intersect the geometry given as input.
* @param typeName Name of the geo-referential to use to get geometies to merge.
* @param source Filter geometry.
* @return Computed union, or nothing if we cannot find any geometry intercepting given source.
* @throws IllegalArgumentException If given type name is not available locally.
* @throws DataStoreException If an error occurs while accessing reference data.
*/
public Optional<Geometry> unionIntersecting(final String typeName, final Geometry source) throws DataStoreException {
final FeatureCollection refData = getFeatureCollection(GeoReferentials.TYPE_ZONE_HYDRO, source);
Geometry union = null;
try (FeatureIterator ite = refData.iterator()) {
if (ite.hasNext())
while (ite.hasNext()) {
final Geometry tmp = (Geometry) ite.next().getDefaultGeometryProperty().getValue();
union = union==null ? tmp : tmp.union(union);
}
}
if (union != null) {
JTS.setCRS(union, refData.getFeatureType().getCoordinateReferenceSystem());
}
return Optional.ofNullable(union);
}
@Override
public int compareTo(GeoReferential o) {
return year - o.year;
}
@Override
public void close() throws Exception {
if(store!=null){
store.close();
}
}
public BooleanExpression completed(final String... types) {
if (types == null || types.length < 1)
return new ReadOnlyBooleanWrapper(false).getReadOnlyProperty();
final ArrayList<BooleanProperty> deps = new ArrayList<>(types.length);
for (final String type : types) {
if (type == null || type.isEmpty())
continue;
switch (type.toLowerCase()) {
case GeoReferentials.TYPE_RPG:
deps.add(rpg);
break;
case GeoReferentials.TYPE_ZONE_HYDRO:
deps.add(zoneHydro);
break;
case GeoReferentials.TYPE_TACHE_ARTIF:
deps.add(tacheArtif);
break;
case GeoReferentials.TYPE_TACHE_URBAINE:
deps.add(tacheUrbaine);
break;
}
}
if (deps.isEmpty())
return new ReadOnlyBooleanWrapper(false).getReadOnlyProperty();
BooleanExpression result = deps.get(0);
for (int i = 1 ; i < deps.size(); i++) {
result = result.and(deps.get(i));
}
return result;
}
}

View File

@ -0,0 +1,531 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import com.vividsolutions.jts.geom.Geometry;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.preferences.net.NetPreferences;
import fr.cenra.rhomeo.core.util.GeometryUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import org.apache.sis.storage.DataStoreException;
import org.geotoolkit.data.FeatureReader;
import org.geotoolkit.data.FeatureStoreRuntimeException;
import org.geotoolkit.data.FeatureWriter;
import org.geotoolkit.data.query.Query;
import org.geotoolkit.data.query.QueryBuilder;
import org.geotoolkit.data.shapefile.ShapefileFeatureStore;
import org.geotoolkit.data.wfs.GetFeatureRequest;
import org.geotoolkit.data.wfs.WebFeatureClient;
import org.geotoolkit.data.wfs.WebFeatureException;
import org.geotoolkit.display2d.GO2Utilities;
import org.geotoolkit.feature.Feature;
import org.geotoolkit.feature.FeatureTypeBuilder;
import org.geotoolkit.feature.FeatureUtilities;
import org.geotoolkit.feature.type.AttributeDescriptor;
import org.geotoolkit.feature.type.FeatureType;
import org.geotoolkit.feature.type.PropertyDescriptor;
import org.geotoolkit.feature.xml.XmlFeatureReader;
import org.geotoolkit.feature.xml.jaxp.JAXPStreamFeatureReader;
import org.geotoolkit.filter.DefaultPropertyName;
import org.geotoolkit.map.FeatureMapLayer;
import org.geotoolkit.map.MapBuilder;
import org.geotoolkit.map.MapItem;
import org.geotoolkit.nio.IOUtilities;
import org.geotoolkit.wfs.xml.WFSVersion;
import org.opengis.filter.Filter;
import org.opengis.util.FactoryException;
import org.opengis.util.GenericName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Manage geographic referential.
*
* @author Johann Sorel (Geomatys)
*/
@Component
public class GeoReferentials {
public static final String TYPE_ZONE_HYDRO = "zonehydro";
public static final String TYPE_RPG = "rpg";
public static final String TYPE_TACHE_ARTIF = "tacheartif";
public static final String TYPE_TACHE_URBAINE = "tacheurbaine";
private static final List<String> NAMES = Arrays.asList(TYPE_ZONE_HYDRO,TYPE_RPG,TYPE_TACHE_ARTIF,TYPE_TACHE_URBAINE);
@Autowired
private NetPreferences prefs;
//cache WFS client
private WebFeatureClient wfs;
private synchronized WebFeatureClient getWFSClient() throws MalformedURLException, DataStoreException, WebFeatureException{
if(wfs==null){
String url = prefs.getPreference(NetPreferences.WFS_URL);
if (url == null || (url = url.trim()).isEmpty())
throw new IllegalStateException("Aucune URL disponible pour les référentiels géographiques");
wfs = new WebFeatureClient(new URL(url), null, WFSVersion.v110, true);
wfs.getNames();
}
return wfs;
}
/**
* Combine local and server referentials.
*
* @param site
* @return
*/
public ObservableList<GeoReferential> getReferentials(Site site) throws IllegalArgumentException, IOException, DataStoreException{
final ObservableList<GeoReferential> refs = getLocalReferentials(site);
try {
ObservableList<GeoReferential> wfsReferentials = getWFSReferentials();
loop:
for(GeoReferential gr : wfsReferentials){
for(GeoReferential r : refs){
if(r.compareTo(gr)==0){
r.tacheArtifWFS().set(gr.tacheArtifWFS().get());
r.tacheUrbaineWFS().set(gr.tacheUrbaineWFS().get());
r.zoneHydroWFS().set(gr.zoneHydroWFS().get());
r.rpgWFS().set(gr.rpgWFS().get());
continue loop;
}
}
refs.add(gr);
}
} catch (Exception ex) {
if(refs.isEmpty()){
throw ex;
}else{
RhomeoCore.LOGGER.log(Level.WARNING, null, ex);
}
}
//sort by year
Collections.sort(refs);
return refs;
}
/**
* List available local referentials for given site.
*
* @param site
* @param year
* @return
*/
public GeoReferential getLocalReferential(Site site, int year) throws IllegalArgumentException, IOException {
Path folder = RhomeoCore.REFERENCIELS_GEO_PATH.resolve(GeometryUtils.getSha1(site.getGeometry()));
if (!Files.isDirectory(folder)) {
folder = RhomeoCore.REFERENCIELS_GEO_PATH.resolve(site.getName());
}
final String strYear = String.valueOf(year);
for(Path p : IOUtilities.listChildren(folder)){
if(Files.isDirectory(p)){
if(p.getFileName().toString().equals(strYear)){
final GeoReferential ref = new GeoReferential(site, year);
for(Path tp : IOUtilities.listChildren(p)){
final String tName = tp.getFileName().toString().toLowerCase();
if(tName.startsWith(TYPE_ZONE_HYDRO)){
ref.zoneHydroLocal().set(true);
}else if(tName.startsWith(TYPE_TACHE_URBAINE)){
ref.tacheUrbaineLocal().set(true);
}else if(tName.startsWith(TYPE_TACHE_ARTIF)){
ref.tacheArtifLocal().set(true);
}else if(tName.startsWith(TYPE_RPG)){
ref.rpgLocal().set(true);
}
}
return ref;
}
}
}
throw new IOException("Referential for year "+year+" not found");
}
/**
* List available local referentials for given site.
*
* @param site
* @return
*/
public ObservableList<GeoReferential> getLocalReferentials(Site site) throws IllegalArgumentException, IOException {
final ObservableList<GeoReferential> lst = FXCollections.observableArrayList();
Path folder = RhomeoCore.REFERENCIELS_GEO_PATH.resolve(GeometryUtils.getSha1(site.getGeometry()));
if (!Files.isDirectory(folder)) {
folder = RhomeoCore.REFERENCIELS_GEO_PATH.resolve(site.getName());
}
if(Files.isDirectory(folder)){
for(Path p : IOUtilities.listChildren(folder)) {
if (Files.isDirectory(p)) {
final int year;
// Ignore directories not matching year names.
try {
year = Integer.valueOf(p.getFileName().toString());
} catch (NumberFormatException e) {
continue;
}
final GeoReferential ref = new GeoReferential(site,year);
for(Path tp : IOUtilities.listChildren(p)){
final String tName = tp.getFileName().toString().toLowerCase();
if(tName.startsWith(TYPE_ZONE_HYDRO)){
ref.zoneHydroLocal().set(true);
}else if(tName.startsWith(TYPE_TACHE_URBAINE)){
ref.tacheUrbaineLocal().set(true);
}else if(tName.startsWith(TYPE_TACHE_ARTIF)){
ref.tacheArtifLocal().set(true);
}else if(tName.startsWith(TYPE_RPG)){
ref.rpgLocal().set(true);
}
}
lst.add(ref);
}
}
}
return lst;
}
/**
* List server referentials.
*
* @return
* @throws DataStoreException
* @throws MalformedURLException
*/
public ObservableList<GeoReferential> getWFSReferentials() throws DataStoreException, MalformedURLException, WebFeatureException{
final Map<String,GeoReferential> count = new HashMap<>();
try (WebFeatureClient client = getWFSClient()) {
final Set<GenericName> names = client.getNames();
for(GenericName name : names){
final String[] parts = name.tip().toString().toLowerCase().split("_");
if(parts.length==2 && NAMES.contains(parts[0])){
GeoReferential ref = count.get(parts[1]);
if(ref==null) ref = new GeoReferential(null, Integer.valueOf(parts[1]));
switch(parts[0]){
case TYPE_ZONE_HYDRO : ref.zoneHydroWFS().set(true); break;
case TYPE_RPG : ref.rpgWFS().set(true); break;
case TYPE_TACHE_URBAINE : ref.tacheUrbaineWFS().set(true); break;
case TYPE_TACHE_ARTIF : ref.tacheArtifWFS().set(true); break;
}
count.put(parts[1], ref);
}
}
}
return FXCollections.observableArrayList(count.values());
}
public MapItem getWFSLayers(final Optional<Filter> filter, final String... referentialNames) throws DataStoreException, MalformedURLException {
MapItem result = MapBuilder.createItem();
result.setName("WFS layers");
try (WebFeatureClient client = getWFSClient()) {
final Set<GenericName> names = client.getNames().stream()
.filter(name -> {
if (referentialNames == null || referentialNames.length < 1)
return true;
for (final String refName : referentialNames) {
if (name.tip().toString().toLowerCase().startsWith(refName.toLowerCase()))
return true;
}
return false;
})
.collect(Collectors.toSet());
for (GenericName name : names) {
Query q = filter
.map(f -> {
final QueryBuilder builder = new QueryBuilder(name);
builder.setFilter(f);
builder.setProperties(new String[]{"geom"});
return builder.buildQuery();
})
.orElseGet(() -> QueryBuilder.all(name));
final FeatureMapLayer layer = MapBuilder.createFeatureLayer(
client.createSession(false).getFeatureCollection(q)
);
layer.setName(name.tip().toString());
layer.setVisible(false);
result.items().add(layer);
}
}
return result;
}
/**
* Download from WFS service the referential for specified year.
*
* @param site analyze site
* @param year georeferential year
* @throws IllegalArgumentException If we cannot find any hydrographic zone
* intersecting site buffer.
* @throws Exception Any other exception is a read/write error.
*/
public void downloadReferential(Site site, int year) throws Exception {
final Path localFolder = RhomeoCore.REFERENCIELS_GEO_PATH.resolve(GeometryUtils.getSha1(site.getGeometry())).resolve(String.valueOf(year));
//extract only data on site envelope
//not : this will be replace by hydro area after it has been downloaded
final Geometry buffer = GeometryUtils.computeBuffer(site.getGeometry());
//connect to WFS service
try (WebFeatureClient client = getWFSClient()) {
// First, we download hydrology data, because there's a special treatment over it.
downloadReferential(TYPE_ZONE_HYDRO + '_' + year, localFolder, buffer, false, client);
Geometry[] zhs = getLocalReferential(site, year).getFeatureCollection(TYPE_ZONE_HYDRO, null).stream()
.map(feat -> feat.getDefaultGeometryProperty().getValue())
.filter(geom -> geom instanceof Geometry)
.toArray(size -> new Geometry[size]);
Geometry zhUnion = GO2Utilities.JTS_FACTORY.createGeometryCollection(zhs).union();
// Get other referentials
final Set<String> names = new HashSet<>(NAMES);
names.remove(TYPE_ZONE_HYDRO);
for (String name : names) {
//some layers may be missing, P08 and P09 use different refentials.
try {
downloadReferential(name + '_' + year, localFolder, zhUnion, true, client);
} catch (IllegalArgumentException e) {
RhomeoCore.LOGGER.log(Level.FINE, "Cannot download referential", e);
}
}
} catch (Exception ex) {
//erase local folder if an error occurs
IOUtilities.deleteRecursively(localFolder);
throw ex;
}
}
/**
* Download a single referential using given parameters.
* @param refName Name of the layer to request.
* @param targetDir Folder to put downloaded data into.
* @param zone Geometry delimiting the area to download (intersection filter performed).
* @param cut
* @param client Client to use to query WFS service.
* @throws DataStoreException Error on reading / writing.
* @throws IOException Error on reading / writing.
* @throws FactoryException Error on reading / writing.
* @throws XMLStreamException Error on reading / writing.
* @throws IllegalArgumentException If we cannot find given layer name on
* the server, or no data intersects given zone.
*/
protected void downloadReferential(final String refName, final Path targetDir, final Geometry zone, final boolean cut, final WebFeatureClient client)
throws DataStoreException, IOException, FactoryException, XMLStreamException {
//find feature type full name
GenericName fullName = null;
for (GenericName n : client.getNames()) {
if (n.tip().toString().equalsIgnoreCase(refName)) {
fullName = n;
break;
}
}
if (fullName == null) { // TODO : throw illegalArgumentException managed above.
throw new IllegalArgumentException("No layer found for name ".concat(refName));
}
//prepare query
final FeatureType featureType = client.getFeatureType(fullName);
final FeatureTypeBuilder outputBuilder = new FeatureTypeBuilder();
outputBuilder.setName(fullName);
final QueryBuilder qb = new QueryBuilder(fullName);
// We are only able to store simple features, so we remove complex
// structure from WFS data.
final List<String> props = new ArrayList<>();
for (PropertyDescriptor desc : featureType.getDescriptors()) {
if (isSimpleAttribute(desc)) {
props.add(desc.getName().toString());
outputBuilder.add(desc);
}
}
qb.setProperties(props.toArray(new String[0]));
qb.setCRS(RhomeoCore.getSiteCRS());
//extract only data on site envelope
qb.setFilter(GO2Utilities.FILTER_FACTORY.intersects(
new DefaultPropertyName(featureType.getGeometryDescriptor().getName().tip().toString()),
GO2Utilities.FILTER_FACTORY.literal(zone)
));
//copy data to local folder
Files.createDirectories(targetDir);
try (final FeatureReader reader = getStreamingReader(client, qb.buildQuery());
final ShapefileFeatureStore outStore = new ShapefileFeatureStore(targetDir.resolve(refName + ".shp").toUri())) {
if (!reader.hasNext())
throw new IllegalArgumentException("No data intersects given zone for layer ".concat(fullName.toString()));
outStore.createFeatureType(fullName, outputBuilder.buildSimpleFeatureType());
try (final FeatureWriter writer = outStore.getFeatureWriterAppend(fullName)) {
if (cut) {
while (reader.hasNext()) {
final Feature next = reader.next();
Object value = next.getDefaultGeometryProperty().getValue();
if (value instanceof Geometry)
next.getDefaultGeometryProperty().setValue(zone.intersection((Geometry) value));
FeatureUtilities.copy(next, writer.next(), false);
}
} else {
while (reader.hasNext()) {
FeatureUtilities.copy(reader.next(), writer.next(), false);
}
}
}
}
}
private boolean isSimpleAttribute(final PropertyDescriptor property) {
return property.getMinOccurs() == 1 &&
property.getMaxOccurs() == 1 &&
property instanceof AttributeDescriptor;
}
/**
* Perform a getFeature on the distant server, browing its content lazily.
* @param client The client to use for request.
* @param query The query which contains reading parameters.
* @return A reader to stream service response.
* @throws DataStoreException
* @throws IOException
* @throws XMLStreamException
*/
private FeatureReader getStreamingReader(final WebFeatureClient client, final Query query) throws DataStoreException, IOException, XMLStreamException {
final GetFeatureRequest request = client.createGetFeature();
request.setTypeName(new QName(query.getTypeName().tip().toString()));
final Filter filter = query.getFilter();
if (filter == null) {
request.setFilter(Filter.INCLUDE);
} else {
request.setFilter(filter);
}
final Integer max = query.getMaxFeatures();
if (max != null) {
request.setMaxFeatures(max);
}
request.setPropertyNames(query.getPropertyNames());
XmlFeatureReader reader = null;
reader = new JAXPStreamFeatureReader(client.getFeatureType(query.getTypeName()));
reader.getProperties().put(JAXPStreamFeatureReader.SKIP_UNEXPECTED_PROPERTY_TAGS, true);
final InputStream stream;
if (client.getUsePost()) {
stream = request.getResponseStream();
} else {
final URL url = request.getURL();
stream = url.openStream();
}
// If reader creation fails, stream isn't hanging on.
try {
return new StreamingFeatureReader(reader.readAsStream(stream), stream);
} catch (Throwable t) {
try {
stream.close();
} catch (Throwable t1) {
t.addSuppressed(t1);
}
throw t;
}
}
private static class StreamingFeatureReader implements FeatureReader {
final FeatureReader source;
final InputStream dataSource;
public StreamingFeatureReader(final FeatureReader source, final InputStream dataSource) {
this.source = source;
this.dataSource = dataSource;
}
@Override
public FeatureType getFeatureType() {
return source.getFeatureType();
}
@Override
public Feature next() throws FeatureStoreRuntimeException {
return source.next();
}
@Override
public boolean hasNext() throws FeatureStoreRuntimeException {
return source.hasNext();
}
@Override
public void close() {
try {
source.close();
} finally {
try {
dataSource.close();
} catch (IOException ex) {
RhomeoCore.LOGGER.log(Level.WARNING, "An error occurred while closing data stream.", ex);
}
}
}
}
}

View File

@ -0,0 +1,218 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import fr.cenra.rhomeo.api.data.Reference;
import java.util.Objects;
/**
* Odonata presence by department.
*
* Colonne | Type | Modificateurs
* ---------+------------------------+---------------
* espece | character varying(255) |
* dept | character varying(255) |
* valeur | double precision |
* id_dept | character varying(2) |
* cd_nom | integer |
* cd_ref | integer |
*
* (Source : base issue du script RhoMéO_Geomatys.backup)
*
* Note : There is no explicit primary key, but it seems logic for the key to be
* (cd_nom, id_dept).
*
* Remarques :
*
* L'absence de contrainte d'intégrité rend l'analyse sémantique de la table non
* triviale.
*
* Sémantique
* ==========
*
* Même si logiquement la clef devrait être (cd_nom, id_dept), il
* semble que les combinaisons (cd_ref, id_dept) soient également "sémantiquement" uniques
* car en nombre identique.
*
* select count(*) from (select distinct cd_ref, id_dept from odo_especes_par_dept) as tab;
* count
*-------
* 2436
*
* select count(*) from (select distinct cd_nom, id_dept from odo_especes_par_dept) as tab;
* count
*-------
* 2436
*
* Toutefois, on ne peut pas en déduire l'identité entre cd_nom et cd_ref dans la
* table (ce qui est logique). Nous avons d'ailleurs :
*
* select distinct cd_nom, cd_ref from odo_especes_par_dept where cd_nom<>cd_ref;
* cd_nom | cd_ref
*--------+--------
* 820003 | 645873
*
*
* Creation of the reference list from the database
* ================================================
*
* Export csv
* ----------
* copy (
* select * from odo_especes_par_dept
* )
* to '//odo_especes_par_dept.csv' delimiter ';' csv header;
*
* Dumps
* -----
* pg_dump -t referentiels_non_geo.odo_especes_par_dept rhomeo > //odo_especes_par_dept.copy.sql
* pg_dump --inserts -t referentiels_non_geo.odo_especes_par_dept rhomeo > //odo_especes_par_dept.inserts.sql
*
*
*
* @author Samuel Andrés (Geomatys) <samuel.andres at geomatys.com>
*/
public class OdonataDepartmentReference implements Reference {
// META-INFORMATION : ATTRIBUTE NAMES
// MUST BE CONSISTENT WITH CLASS ATTRIBUTES
public static final String ATT_CD_NOM = "cd_nom";
public static final String ATT_CD_REF = "cd_ref";
public static final String ATT_ESPECE = "espece";
public static final String ATT_ID_DEPT = "id_dept";
public static final String ATT_DEPT = "dept";
public static final String ATT_VALEUR = "valeur";
// Species attributes
private int cd_nom;
private int cd_ref;
private String espece;
// Department attributes
private String id_dept;
private String dept;
private double valeur;
public int getCd_nom() {
return cd_nom;
}
public void setCd_nom(int cd_nom) {
this.cd_nom = cd_nom;
}
public int getCd_ref() {
return cd_ref;
}
public void setCd_ref(int cd_ref) {
this.cd_ref = cd_ref;
}
public String getEspece() {
return espece;
}
public void setEspece(String espece) {
this.espece = espece;
}
public String getId_dept() {
return id_dept;
}
public void setId_dept(String id_dept) {
this.id_dept = id_dept;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public double getValeur() {
return valeur;
}
public void setValeur(double valeur) {
this.valeur = valeur;
}
@Override
public int hashCode() {
int hash = 7;
hash = 89 * hash + this.cd_nom; // same cd_nom => same cd_ref and same espece (interpretation)
hash = 89 * hash + Objects.hashCode(this.id_dept); // same id_dept => same dept
hash = 89 * hash + (int) (Double.doubleToLongBits(this.valeur) ^ (Double.doubleToLongBits(this.valeur) >>> 32));
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final OdonataDepartmentReference other = (OdonataDepartmentReference) obj;
if (this.cd_nom != other.cd_nom) {
return false;
} // same cd_nom => same cd_ref and same espece (interpretation)
if (Double.doubleToLongBits(this.valeur) != Double.doubleToLongBits(other.valeur)) {
return false;
}
if (!Objects.equals(this.id_dept, other.id_dept)) {
return false;
} // same id_dept => same dept
return true;
}
@Override
public String toString() {
return new StringBuilder(Integer.toString(cd_nom)).append('|').append(espece).append('|').append(dept).append('|').append(valeur).toString();
}
}

View File

@ -0,0 +1,75 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import fr.cenra.rhomeo.api.data.ReferenceDescription;
import java.util.Collection;
import java.util.Collections;
import org.springframework.stereotype.Component;
/**
*
* @author Samuel Andrés (Geomatys) <samuel.andres at geomatys.com>
*/
@Component
public class OdonataDepartmentReferenceDescription implements ReferenceDescription<OdonataDepartmentReference> {
public static final String ODONATA_DEPARTMENT_DESCRIPTION_NAME = "odo_especes_par_dept";
@Override
public Class<OdonataDepartmentReference> getReferenceType() {
return OdonataDepartmentReference.class;
}
@Override
public String getName() {
return ODONATA_DEPARTMENT_DESCRIPTION_NAME;
}
@Override
public Collection<String> getAlias() {
return Collections.singleton("Répartition des odonates par département");
}
@Override
public String getRemarks() {
return null;
}
}

View File

@ -0,0 +1,208 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import fr.cenra.rhomeo.api.data.Reference;
import java.util.Objects;
/**
*
* Affinity of Odonata species with different kinds of odonatologic habitat.
*
* Value range is from 1 to 4.
*
* Colonne | Type | Modificateurs
*---------+------------------------+---------------
* espece | character varying(255) |
* biogeo | character varying(255) |
* habitat | character varying(255) |
* valeur | character varying(255) |
* zbio | character varying(255) |
* cd_nom | integer |
* cd_ref | integer |
*
* (Source : base issue du script RhoMéO_Geomatys.backup)
*
* Note : There is no explicit primary key, but it seems logic for the key to be
* (cd_nom, zbio, habitat).
*
* Creation of the reference list from the database
* ================================================
*
* Export csv
* ----------
* copy (
* select * from odo_dependance_habitat
* )
* to '//odo_dependance_habitat.csv' delimiter ';' csv header;
*
* Dumps
* -----
* pg_dump -t referentiels_non_geo.odo_dependance_habitat rhomeo > //odo_dependance_habitat.copy.sql
* pg_dump --inserts -t referentiels_non_geo.odo_dependance_habitat rhomeo > //odo_dependance_habitat.inserts.sql
*
* @author Samuel Andrés (Geomatys) <samuel.andres at geomatys.com>
*/
public class OdonataHabitatReference implements Reference {
// META-INFORMATION : ATTRIBUTE NAMES
// MUST BE CONSISTENT WITH CLASS ATTRIBUTES
public static final String ATT_CD_NOM = "cd_nom";
public static final String ATT_CD_REF = "cd_ref";
public static final String ATT_ESPECE = "espece";
public static final String ATT_ZBIO = "zbio";
public static final String ATT_BIOGEO = "biogeo";
public static final String ATT_HABITAT = "habitat";
public static final String ATT_VALEUR = "valeur";
// Species attributes
private int cd_nom;
private int cd_ref;
private String espece;
// biozone attributes
private String zbio;
private String biogeo;
// Habitat
private String habitat;
// Value for a association between a species, a biozone and an habitat.
private int valeur;
public int getCd_nom() {
return cd_nom;
}
public void setCd_nom(int cd_nom) {
this.cd_nom = cd_nom;
}
public int getCd_ref() {
return cd_ref;
}
public void setCd_ref(int cd_ref) {
this.cd_ref = cd_ref;
}
public String getEspece() {
return espece;
}
public void setEspece(String espece) {
this.espece = espece;
}
public String getZbio() {
return zbio;
}
public void setZbio(String zbio) {
this.zbio = zbio;
}
public String getBiogeo() {
return biogeo;
}
public void setBiogeo(String biogeo) {
this.biogeo = biogeo;
}
public String getHabitat() {
return habitat;
}
public void setHabitat(String habitat) {
if (habitat != null && habitat.startsWith("0"))
habitat = habitat.substring(1);
this.habitat = habitat;
}
public int getValeur() {
return valeur;
}
public void setValeur(int valeur) {
this.valeur = valeur;
}
@Override
public int hashCode() {
int hash = 7;
hash = 59 * hash + this.cd_nom; // same cd_nom => same cd_ref and same espece (interpretation)
hash = 59 * hash + Objects.hashCode(this.zbio); // same zbio => same biogeo (interpretation)
hash = 59 * hash + Objects.hashCode(this.habitat);
hash = 61 * hash + this.valeur;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final OdonataHabitatReference other = (OdonataHabitatReference) obj;
if (this.cd_nom != other.cd_nom) {
return false;
} // same cd_nom => same cd_ref and same espece (interpretation)
if (!Objects.equals(this.zbio, other.zbio)) {
return false;
} // same zbio => same biogeo (interpretation)
if (!Objects.equals(this.habitat, other.habitat)) {
return false;
}
if (this.valeur != other.valeur) {
return false;
}
return true;
}
@Override
public String toString() {
return new StringBuilder(Integer.toString(cd_nom)).append('|').append(cd_ref).append('|').append(espece).append('|').append(habitat).append('|').append(valeur).toString();
}
}

View File

@ -0,0 +1,75 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import fr.cenra.rhomeo.api.data.ReferenceDescription;
import java.util.Collection;
import java.util.Collections;
import org.springframework.stereotype.Component;
/**
*
* @author Samuel Andrés (Geomatys) <samuel.andres at geomatys.com>
*/
@Component
public class OdonataHabitatReferenceDescription implements ReferenceDescription<OdonataHabitatReference> {
public static final String ODONATA_HABITAT_DESCRIPTION_NAME = "odo_dependance_habitat";
@Override
public Class<OdonataHabitatReference> getReferenceType() {
return OdonataHabitatReference.class;
}
@Override
public String getName() {
return ODONATA_HABITAT_DESCRIPTION_NAME;
}
@Override
public Collection<String> getAlias() {
return Collections.singleton("Liste des affinités des odonates par habitat odonatologique");
}
@Override
public String getRemarks() {
return null;
}
}

View File

@ -0,0 +1,245 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import fr.cenra.rhomeo.api.data.Reference;
/**
* Orthopterans indicator reference representation.
*
* Colonne | Type | Modificateurs
* -------------------+------------------------+---------------
* cd_nom | integer | non NULL
* cd_ref | integer |
* lb_nom | character varying(255) |
* pres_lr | integer |
* pres_ra | integer |
* pres_paca | integer |
* hs_code | integer |
* hs_val | integer |
* dm_code | integer |
* dm_val | integer |
* mediterraneen1_ml | integer |
* mediterraneen1_ma | integer |
* mediterraneen2_ma | integer |
* mediterraneen2_tb | integer |
* alpin_ma | integer |
* alpin_tb | integer |
* continental_ma | integer |
* continental_tb | integer |
*
* (Source : base issue du script RhoMéO_Geomatys.backup)
*
* @author Samuel Andrés (Geomatys)
*/
public class OrthopteraIndicatorReference implements Reference {
/**
* CD_REF of the reference taxon.
*/
private int cd_ref;
private int mediterraneen1_ml;// Pourquoi pas booleen ?
private int mediterraneen1_ma;// Pourquoi pas booleen ?
private int mediterraneen2_ma;// Pourquoi pas booleen ?
private int mediterraneen2_tb;// Pourquoi pas booleen ?
private int alpin_ma;// Pourquoi pas booleen ?
private int alpin_tb;// Pourquoi pas booleen ?
private int continental_ma;// Pourquoi pas booleen ?
private int continental_tb;// Pourquoi pas booleen ?
/**
* Name of the taxon.
*/
private String lb_nom;
private int hs_code;
/**
* Environment humidity
*/
private int hs_val;
private int dm_code;
/**
* Sedimentary dynamics
*/
private int dm_val;
public int getCd_ref() {
return cd_ref;
}
public void setCd_ref(int cd_ref) {
this.cd_ref = cd_ref;
}
public int getMediterraneen1_ml() {
return mediterraneen1_ml;
}
public void setMediterraneen1_ml(int mediterraneen1_ml) {
this.mediterraneen1_ml = mediterraneen1_ml;
}
public int getMediterraneen1_ma() {
return mediterraneen1_ma;
}
public void setMediterraneen1_ma(int mediterraneen1_ma) {
this.mediterraneen1_ma = mediterraneen1_ma;
}
public int getMediterraneen2_ma() {
return mediterraneen2_ma;
}
public void setMediterraneen2_ma(int mediterraneen2_ma) {
this.mediterraneen2_ma = mediterraneen2_ma;
}
public int getMediterraneen2_tb() {
return mediterraneen2_tb;
}
public void setMediterraneen2_tb(int mediterraneen2_tb) {
this.mediterraneen2_tb = mediterraneen2_tb;
}
public int getAlpin_ma() {
return alpin_ma;
}
public void setAlpin_ma(int alpin_ma) {
this.alpin_ma = alpin_ma;
}
public int getAlpin_tb() {
return alpin_tb;
}
public void setAlpin_tb(int alpin_tb) {
this.alpin_tb = alpin_tb;
}
public int getContinental_ma() {
return continental_ma;
}
public void setContinental_ma(int continental_ma) {
this.continental_ma = continental_ma;
}
public int getContinental_tb() {
return continental_tb;
}
public void setContinental_tb(int continental_tb) {
this.continental_tb = continental_tb;
}
public String getLb_nom() {
return lb_nom;
}
public void setLb_nom(String lb_nom) {
this.lb_nom = lb_nom;
}
public int getHs_code() {
return hs_code;
}
public void setHs_code(int hs_code) {
this.hs_code = hs_code;
}
public int getHs_val() {
return hs_val;
}
public void setHs_val(int hs_val) {
this.hs_val = hs_val;
}
public int getDm_code() {
return dm_code;
}
public void setDm_code(int dm_code) {
this.dm_code = dm_code;
}
public int getDm_val() {
return dm_val;
}
public void setDm_val(int dm_val) {
this.dm_val = dm_val;
}
@Override
public int hashCode() {
return cd_ref;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final OrthopteraIndicatorReference other = (OrthopteraIndicatorReference) obj;
return getCd_ref() == other.getCd_ref()
&& getHs_val() == other.getHs_val()
&& getDm_val() == other.getDm_val();
}
@Override
public String toString() {
return new StringBuilder().append(cd_ref).append(' ').append(lb_nom).toString();
}
}

View File

@ -0,0 +1,75 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import fr.cenra.rhomeo.api.data.ReferenceDescription;
import java.util.Collection;
import java.util.Collections;
import org.springframework.stereotype.Component;
/**
*
* @author Samuel Andrés (Geomatys) <samuel.andres at geomatys.com>
*/
@Component
public class OrthopteraIndicatorReferenceDescription implements ReferenceDescription<OrthopteraIndicatorReference> {
public static final String ORTHOPTERA_INDICATOR_DESCRIPTION_NAME = "ortho_indicateurs";
@Override
public Class<OrthopteraIndicatorReference> getReferenceType() {
return OrthopteraIndicatorReference.class;
}
@Override
public String getName() {
return ORTHOPTERA_INDICATOR_DESCRIPTION_NAME;
}
@Override
public Collection<String> getAlias() {
return Collections.singleton("Liste des valeurs indicatrices des orthoptères");
}
@Override
public String getRemarks() {
return null;
}
}

View File

@ -0,0 +1,202 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import fr.cenra.rhomeo.api.data.Reference;
import java.util.Objects;
/**
* Taxref taxonomic referential representation.
*
*
* Colonne | Type | Modificateurs
* --------------+----------------------+---------------
* regne | text |
* phylum | text |
* classe | text |
* ordre | text |
* famille | text |
* cd_nom | character varying(6) | non NULL
* cd_taxsup | character varying(6) |
* cd_ref | character varying(6) |
* rang | text |
* lb_nom | text |
* lb_auteur | text |
* nom_complet | text |
* nom_valide | text |
* nom_vern | text |
* nom_vern_eng | text |
* habitat | text |
* fr | text |
* gf | text |
* mar | text |
* gua | text |
* sm | text |
* sb | text |
* spm | text |
* may | text |
* epa | text |
* reu | text |
* taaf | text |
* pf | text |
* nc | text |
* wf | text |
* cli | text |
* aphia_id | text |
*
* (Source : base issue du script RhoMéO_Geomatys.backup)
*
* @author Samuel Andrés
*
* @see TaxrefDescription
*/
public class Taxref implements Reference {
// META-INFORMATION : ATTRIBUTE NAMES
// MUST BE CONSISTENT WITH CLASS ATTRIBUTES
public static final String ATT_CD_NOM = "cd_nom";
public static final String ATT_CD_TAXSUP = "cd_taxsup";
public static final String ATT_CD_REF = "cd_ref";
public static final String ATT_RANG = "rang";
public static final String ATT_NOM_COMPLET = "nomComplet";
// Other constants
public static final String RANG_SSES = "SSES";
/**
* Primary key.
*/
private int cd_nom;
private int cd_taxsup;
private int cd_ref;
private String rang;
private String lb_nom;
private String ordre;
private String classe;
public int getCd_nom() {
return cd_nom;
}
public void setCd_nom(int cd_nom) {
this.cd_nom = cd_nom;
}
public int getCd_taxsup() {
return cd_taxsup;
}
public void setCd_taxsup(int cd_taxsup) {
this.cd_taxsup = cd_taxsup;
}
public int getCd_ref() {
return cd_ref;
}
public void setCd_ref(int cd_ref) {
this.cd_ref = cd_ref;
}
public String getRang() {
return rang;
}
public void setRang(String rang) {
this.rang = rang;
}
public String getLb_nom() {
return lb_nom;
}
public void setLb_nom(String nomComplet) {
this.lb_nom = nomComplet;
}
public String getOrdre() {
return ordre;
}
public void setOrdre(String ordre) {
this.ordre = ordre;
}
public String getClasse() {
return classe;
}
public void setClasse(String classe) {
this.classe = classe;
}
@Override
public int hashCode() {
return cd_nom;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Taxref other = (Taxref) obj;
if (!Objects.equals(this.cd_nom, other.cd_nom)) {
return false;
}
return true;
}
@Override
public String toString() {
return lb_nom;
}
}

View File

@ -0,0 +1,76 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.reference;
import fr.cenra.rhomeo.api.data.ReferenceDescription;
import java.util.Collection;
import java.util.Collections;
import org.springframework.stereotype.Component;
/**
* Description of the {@link Taxref} reference type.
*
* @author Samuel Andrés (Geomatys)
*/
@Component
public class TaxrefDescription implements ReferenceDescription<Taxref> {
private static final String TAXREF_DESCRIPTION_NAME = "taxref";
@Override
public Class<Taxref> getReferenceType() {
return Taxref.class;
}
@Override
public String getName() {
return TAXREF_DESCRIPTION_NAME;
}
@Override
public Collection<String> getAlias() {
return Collections.singletonList("Référentiel taxonomique");
}
@Override
public String getRemarks() {
return null;
}
}

View File

@ -0,0 +1,55 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.site;
/**
* Exception to launch when trying to use the same key for several items in the same collection.
*
* @author Cédric Briançon (Geomatys)
*/
public class DuplicatedKeyException extends Exception {
public DuplicatedKeyException() {
super();
}
public DuplicatedKeyException(final String message) {
super(message);
}
}

View File

@ -0,0 +1,103 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.site;
import fr.cenra.rhomeo.api.IdentifiedObject;
import javafx.collections.ObservableList;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotBlank;
/**
* Generic data access methods all project repositories should have.
*
* @author Cédric Briançon (Geomatys)
* @param <I>
*/
public interface RhomeoRepository<I extends IdentifiedObject> {
/**
* Find the item for the given key.
*
* @param key An item key
* @return The matching item, or {@code null} if not found.
*/
I findOne(@NotBlank String key);
/**
* Create an item in the data source if the item key is not already present in the store,
* otherwise throws {@link DuplicatedKeyException}.
*
* @param object The new item to create, should not be {@code null}
* @throws DuplicatedKeyException if an object already exists with the same key
*/
void create(@NotNull I object) throws DuplicatedKeyException;
/**
* Find all items in the data source.
*
* @return A list of items stored, eventually an empty list, never {@code null}
*/
ObservableList<I> findAll();
/**
* Update an existing item in the data source. Its key should be the same in the
* given object and in the stored version in order to know which item has to be updated.
* If the given object key is not known in the store, then do nothing.
*
* @param object Item to update, with new values. Should not be {@code null}.
*/
void update(@NotNull I object);
/**
* Update an existing item in the data source. The given object may have a new key specified,
* so use the old key parameter to know which item should be updated.
*
* @param object Item to update, with new values. Should not be {@code null}.
* @param oldKey Existing item key to update.
* @throws DuplicatedKeyException if the given key in the object parameter is already used.
*/
void update(@NotNull I object, String oldKey) throws DuplicatedKeyException;
/**
* Delete an existing item in the data source.
*
* @param key Key of the item to delete.
*/
void delete(@NotBlank String key);
}

View File

@ -0,0 +1,229 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.site;
import com.vividsolutions.jts.geom.MultiPolygon;
import fr.cenra.rhomeo.api.InternationalResource;
import fr.cenra.rhomeo.api.annotations.RefersTo;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.list.HumidZoneType;
import fr.cenra.rhomeo.core.list.OdonateZoneBio;
import fr.cenra.rhomeo.core.list.OrthoptereZoneBio;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
/**
* POJO representing a site. No intelligence in here.
*
* @author Cédric Briançon (Geomatys)
*/
public class SiteImpl implements Site, Cloneable, InternationalResource {
private MultiPolygon geometry;
private String countyCode;
private String referent;
private String organization;
private String zoneType;
private String odonateType;
private String orthoptereType;
private String name;
private String remarks;
private SiteImpl() {}
public SiteImpl(MultiPolygon geometry) {
this.geometry = geometry;
}
public SiteImpl(MultiPolygon geometry, String countyCode, String referent, String organization, String zoneType,
String odonateType, String orthoptereType, String name, String remarks) {
this.geometry = geometry;
this.countyCode = countyCode;
this.referent = referent;
this.organization = organization;
this.zoneType = zoneType;
this.odonateType = odonateType;
this.orthoptereType = orthoptereType;
this.name = name;
this.remarks = remarks;
}
@Override
public MultiPolygon getGeometry() {
return geometry;
}
public void setGeometry(MultiPolygon geometry) {
this.geometry = geometry;
}
@Override
public String getCountyCode() {
return countyCode;
}
public void setCountyCode(final String county) {
this.countyCode = county;
}
@Override
public String getReferent() {
return referent;
}
public void setReferent(String referent) {
this.referent = referent;
}
@Override
public String getOrganization() {
return organization;
}
public void setOrganization(String organization) {
this.organization = organization;
}
@RefersTo(type = HumidZoneType.class, property = "code")
@Override
public String getZoneType() {
return zoneType;
}
public void setZoneType(String zoneType) {
this.zoneType = zoneType;
}
@RefersTo(type = OdonateZoneBio.class, property = "code")
@Override
public String getOdonateType() {
return odonateType;
}
public void setOdonateType(String odonateType) {
this.odonateType = odonateType;
}
@Override
public SiteImpl clone() {
final SiteImpl newSite = new SiteImpl(geometry);
newSite.setCountyCode(countyCode);
newSite.setName(name);
newSite.setOdonateType(odonateType);
newSite.setOrthoptereType(orthoptereType);
newSite.setOrganization(organization);
newSite.setReferent(referent);
newSite.setRemarks(remarks);
newSite.setZoneType(zoneType);
return newSite;
}
@RefersTo(type= OrthoptereZoneBio.class, property = "code")
@Override
public String getOrthoptereType() {
return orthoptereType;
}
public void setOrthoptereType(String orthoptereType) {
this.orthoptereType = orthoptereType;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getRemarks() {
return remarks;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
@Override
public Collection<String> getAlias() {
return Collections.singleton(name);
}
@Override
public String toString() {
return name;
}
@Override
public boolean equals(Object o) {
if (o == null)
return false;
if (getClass() != o.getClass())
return false;
final SiteImpl other = (SiteImpl) o;
return Objects.equals(this.geometry, other.geometry) &&
RhomeoCore.equivalent(this.countyCode, other.countyCode) &&
RhomeoCore.equivalent(this.referent, other.referent) &&
RhomeoCore.equivalent(this.organization, other.organization) &&
RhomeoCore.equivalent(this.zoneType, other.zoneType) &&
RhomeoCore.equivalent(this.odonateType, other.odonateType) &&
RhomeoCore.equivalent(this.orthoptereType, other.orthoptereType) &&
RhomeoCore.equivalent(this.name, other.name) &&
RhomeoCore.equivalent(this.remarks, other.remarks);
}
@Override
public int hashCode() {
int result = geometry.hashCode();
result = 31 * result + name.hashCode();
return result;
}
@Override
public int compareTo(Site o) {
return o == null ? -1 : getName().compareTo(o.getName());
}
}

View File

@ -0,0 +1,52 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.site;
import fr.cenra.rhomeo.api.data.Site;
import javafx.collections.ObservableList;
/**
* For spring injection.
*
* @author Cédric Briançon (Geomatys)
*/
public interface SiteRepository extends RhomeoRepository<Site>, AutoCloseable {
ObservableList<String> findNames();
}

View File

@ -0,0 +1,483 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.data.site;
import com.vividsolutions.jts.geom.MultiPolygon;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.core.RhomeoCore;
import static fr.cenra.rhomeo.core.RhomeoCore.SITES;
import static fr.cenra.rhomeo.core.RhomeoCore.SITES_SHP_PATH;
import fr.cenra.rhomeo.core.RhomeoRuntimeException;
import fr.cenra.rhomeo.core.data.site.SiteImpl;
import java.lang.ref.WeakReference;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javax.annotation.PreDestroy;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.collection.Cache;
import org.apache.sis.util.iso.Names;
import org.geotoolkit.data.FeatureCollection;
import org.geotoolkit.data.FeatureIterator;
import org.geotoolkit.data.FeatureReader;
import org.geotoolkit.data.FeatureWriter;
import org.geotoolkit.data.query.Query;
import org.geotoolkit.data.query.QueryBuilder;
import org.geotoolkit.data.shapefile.ShapefileFeatureStore;
import org.geotoolkit.data.shapefile.ShapefileFeatureStoreFactory;
import org.geotoolkit.factory.FactoryFinder;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.feature.Feature;
import org.geotoolkit.feature.FeatureBuilder;
import org.geotoolkit.feature.FeatureTypeBuilder;
import org.geotoolkit.feature.type.FeatureType;
import org.geotoolkit.parameter.Parameters;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.util.FactoryException;
import org.opengis.util.LocalName;
import org.springframework.stereotype.Repository;
import org.springframework.validation.annotation.Validated;
/**
* Access point to sites stored in the file system.
* An {@linkplain ObservableList observable list} can be followed for listening
* changes via the {@link #findAll()} method.
*
* @author Cédric Briançon (Geomatys)
*/
@Repository
@Validated
public class SiteRepositoryImpl implements SiteRepository {
private static final LocalName NAME = Names.createLocalName(null, ":", SITES);
private static FeatureType SITE_TYPE;
private final FilterFactory2 ff;
/**
* Cache result of {@link #findNames() ()} method, to avoid reading the
* shapefile each time a site is updated, created or deleted.
*/
private WeakReference<ObservableList<String>> allNames;
/**
* Cache loaded sites, to ensure their uniqueness in memory.
*/
private final Cache<String, Site> cache;
/**
* Feature store to use.
*/
private final ShapefileFeatureStore store;
/**
* Get or create the {@linkplain #store feature store}, creating all folders needed
* if necessary.
* @throws java.lang.Exception If an error occurs while initializing shapefile.
*/
public SiteRepositoryImpl() throws Exception {
Files.createDirectories(RhomeoCore.SITES_PATH);
final ParameterValueGroup parameters = ShapefileFeatureStoreFactory.PARAMETERS_DESCRIPTOR.createValue();
Parameters.getOrCreate(ShapefileFeatureStoreFactory.PATH, parameters)
.setValue(SITES_SHP_PATH.toUri());
Parameters.getOrCreate(ShapefileFeatureStoreFactory.NAMESPACE, parameters)
.setValue("no namespace");
final ShapefileFeatureStoreFactory fact = new ShapefileFeatureStoreFactory();
if (Files.exists(SITES_SHP_PATH)) {
store = (ShapefileFeatureStore) fact.open(parameters);
} else {
store = (ShapefileFeatureStore) fact.create(parameters);
store.createFeatureType(NAME, getSiteType());
}
// Empty or corrupted file
if (store.getName() == null || !store.getName().equals(NAME)) {
store.createFeatureType(NAME, getSiteType());
}
final Hints hints = new Hints();
hints.put(Hints.FILTER_FACTORY, FilterFactory2.class);
ff = (FilterFactory2) FactoryFinder.getFilterFactory(hints);
cache = new Cache(4, 0, false);
}
/**
*
* @return Cached data, or null if cache has not been initialized yet.
*/
private synchronized ObservableList<String> getCachedNames() {
return allNames == null ? null : allNames.get();
}
/**
* Create a new filter which keeps only sites whose name is equal to given one.
* @param nameValue The name to filter against.
* @return A new filter.
*/
private Filter newNameFilter(final String nameValue) {
return ff.equals(ff.property(RhomeoCore.FT_PROPERTIES.NAME.name()), ff.literal(nameValue));
}
/**
*
* @param name The name to filter against.
* @return A collection of sites whose names are equal to given one.
*/
private FeatureCollection filteredByName(final String name) {
ArgumentChecks.ensureNonEmpty("Name to search", name.trim());
return store.createSession(false).getFeatureCollection(
QueryBuilder.filtered(this.NAME, newNameFilter(name))
);
}
/**
*
* @param siteName A name to find sites for.
* @return True if at least one site with the same name already exists.
*/
private synchronized boolean exists(final String siteName) {
ArgumentChecks.ensureNonEmpty("Name to search", siteName.trim());
// Do not use cache here, because if an existing site name has been modified,
// it would appear in cache.
return !filteredByName(siteName).isEmpty();
}
/**
* {@inheritDoc}
*/
@Override
public synchronized Site findOne(final String name) {
if (name == null || name.isEmpty())
return null;
try {
return cache.getOrCreate(name, () -> {
final FeatureCollection col = filteredByName(name);
if (col.isEmpty()) {
return null;
}
try (final FeatureIterator it = col.iterator()) {
return toSite(it.next());
}
});
} catch (Exception e) {
throw new RhomeoRuntimeException("Cannot load site named ".concat(name), e);
}
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void create(final Site site) throws DuplicatedKeyException {
ArgumentChecks.ensureNonNull("Site", site);
if (exists(site.getName())) {
throw new DuplicatedKeyException();
}
try {
final Feature toAdd = toFeature(site);
store.addFeatures(NAME, Collections.singleton(toAdd));
} catch (DataStoreException e) {
throw new RhomeoRuntimeException(e);
}
ObservableList<String> cached = getCachedNames();
if (cached != null) {
cached.add(site.getName());
Collections.sort(cached);
}
cache.put(site.getName(), site);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized ObservableList<Site> findAll() {
throw new UnsupportedOperationException("Cannot load full site list in memory !");
}
public synchronized ObservableList<String> findNames() {
ObservableList<String> result = getCachedNames();
if (result == null) {
final List<String> sites = new ArrayList<>();
final Query query;
try {
final QueryBuilder builder = new QueryBuilder(store.getName());
builder.setProperties(new String[]{RhomeoCore.FT_PROPERTIES.NAME.name()});
query = builder.buildQuery();
try (final FeatureReader reader = store.getFeatureReader(query)) {
while (reader.hasNext()) {
sites.add(reader.next().getPropertyValue(RhomeoCore.FT_PROPERTIES.NAME.name()).toString());
}
}
} catch (DataStoreException ex) {
throw new RhomeoRuntimeException(ex);
}
Collections.sort(sites);
result = FXCollections.observableList(sites);
allNames = new WeakReference<>(result);
}
return FXCollections.unmodifiableObservableList(result);
}
/**
* Update the first encountered feature matching given filter.
*
* @param filter
* @param site
* @throws DataStoreException
*/
private synchronized void updateFeature(final Filter filter, final Site site) throws DataStoreException {
// There is a bug in the shp method getFeatureWriter(name, filter), it never uses the filter parameter!
// Consequently we have to test each filter afterwards, via filter.evaluate(feature)
boolean found = false;
try(final FeatureWriter writer = store.getFeatureWriter(NAME, Filter.INCLUDE)) {
while (writer.hasNext()) {
final Feature f = writer.next();
if (filter.evaluate(f)) {
toFeature(f, site);
writer.write();
found = true;
}
}
}
if (!found) {
// If we've got until here, no element has been found for given filter.
throw new IllegalArgumentException("Cannot update unexisting site !");
}
}
/**
* {@inheritDoc}
*/
@Override
public void update(final Site site) {
try {
update(site, null);
} catch (DuplicatedKeyException ex) {
throw new RhomeoRuntimeException(ex); // should not happen, as key is not modified.
}
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void update(final Site site, String oldName) throws DuplicatedKeyException {
ArgumentChecks.ensureNonNull("Site", site);
// Defines if we need to sort the sites list in cache or not.
// If user updated the site name, then it is required to ensure keeping this list sorted.
boolean needsToSort = false;
if (oldName == null || oldName.trim().isEmpty() || oldName.equals(site.getName())) {
oldName = site.getName();
} else {
// Here we want to rename the current site name, and its position in the list might be impacted.
needsToSort = true;
}
ArgumentChecks.ensureNonNull("Name to update", oldName);
if (!oldName.equals(site.getName()) && exists(site.getName())) {
throw new DuplicatedKeyException("Cannot change site name because another one already has the same name !");
}
// Persist change
try {
// Ok not already used key, we can update the matching feature
updateFeature(newNameFilter(oldName), site);
} catch (Exception e) {
throw new RhomeoRuntimeException(e);
}
// update caches
if (needsToSort) {
final ObservableList<String> cached = getCachedNames();
if (cached != null)
cached.set(cached.indexOf(oldName), site.getName());
Collections.sort(cached);
}
if (needsToSort) {
cache.remove(oldName);
cache.put(site.getName(), site);
} else {
cache.replace(oldName, site);
}
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void delete(final String name) {
ArgumentChecks.ensureNonEmpty("Site name", name.trim());
// There is a bug in the shp method getFeatureWriter(name, filter), it never uses the filter parameter!
// Consequently we have to test each filter afterwards, via filter.evaluate(feature)
final Filter filter = newNameFilter(name);
try (final FeatureWriter writer = store.getFeatureWriter(this.NAME, Filter.INCLUDE)) {
while (writer.hasNext()) {
final Feature f = writer.next();
if (filter.evaluate(f)) {
writer.remove();
}
}
} catch (DataStoreException e) {
throw new RhomeoRuntimeException(e);
}
ObservableList<String> cached = getCachedNames();
if (cached != null) {
cached.removeAll(Collections.singleton(name));
}
cache.remove(name);
}
/**
* {@inheritDoc}
*/
@PreDestroy
@Override
public synchronized void close() throws Exception {
allNames = null;
store.close();
}
public static final FeatureType getSiteType() {
if (SITE_TYPE == null) {
final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
ftb.setName(NAME);
try {
ftb.add(RhomeoCore.FT_PROPERTIES.the_geom.name(), MultiPolygon.class, RhomeoCore.getSiteCRS());
} catch (FactoryException ex) {
throw new RhomeoRuntimeException(ex);
}
ftb.add(RhomeoCore.FT_PROPERTIES.NAME.name(), String.class);
ftb.add(RhomeoCore.FT_PROPERTIES.REMARKS.name(), String.class);
ftb.add(RhomeoCore.FT_PROPERTIES.COUNTY.name(), String.class);
ftb.add(RhomeoCore.FT_PROPERTIES.REFERENT.name(), String.class);
ftb.add(RhomeoCore.FT_PROPERTIES.ORG.name(), String.class);
ftb.add(RhomeoCore.FT_PROPERTIES.TYPE.name(), String.class);
ftb.add(RhomeoCore.FT_PROPERTIES.ODONATE.name(), String.class);
ftb.add(RhomeoCore.FT_PROPERTIES.ORTHOPTERE.name(), String.class);
ftb.setDefaultGeometry(RhomeoCore.FT_PROPERTIES.the_geom.name());
SITE_TYPE = ftb.buildFeatureType();
}
return SITE_TYPE;
}
/**
* Converts a {@link Site} into a {@link Feature}.
*
* @param site Site to convert.
* @return A new feature matching this site, never {@code null}
* @throws DataStoreException
*/
public static Feature toFeature(final Site site) throws DataStoreException {
final FeatureBuilder sfb = new FeatureBuilder(getSiteType());
sfb.setPropertyValue(RhomeoCore.FT_PROPERTIES.the_geom.name(), site.getGeometry());
sfb.setPropertyValue(RhomeoCore.FT_PROPERTIES.COUNTY.name(), site.getCountyCode());
sfb.setPropertyValue(RhomeoCore.FT_PROPERTIES.NAME.name(), site.getName());
sfb.setPropertyValue(RhomeoCore.FT_PROPERTIES.ODONATE.name(), site.getOdonateType());
sfb.setPropertyValue(RhomeoCore.FT_PROPERTIES.ORTHOPTERE.name(), site.getOrthoptereType());
sfb.setPropertyValue(RhomeoCore.FT_PROPERTIES.ORG.name(), site.getOrganization());
sfb.setPropertyValue(RhomeoCore.FT_PROPERTIES.REFERENT.name(), site.getReferent());
sfb.setPropertyValue(RhomeoCore.FT_PROPERTIES.REMARKS.name(), site.getRemarks());
sfb.setPropertyValue(RhomeoCore.FT_PROPERTIES.TYPE.name(), site.getZoneType());
return sfb.buildFeature(site.getName());
}
/**
* Apply site values into an existing feature.
*
* @param feature existing feature to update
* @param site new values to extract
* @throws DataStoreException
*/
public static void toFeature(final Feature feature, final Site site) throws DataStoreException {
feature.setPropertyValue(RhomeoCore.FT_PROPERTIES.the_geom.name(), site.getGeometry());
feature.setPropertyValue(RhomeoCore.FT_PROPERTIES.COUNTY.name(), site.getCountyCode());
feature.setPropertyValue(RhomeoCore.FT_PROPERTIES.NAME.name(), site.getName());
feature.setPropertyValue(RhomeoCore.FT_PROPERTIES.ODONATE.name(), site.getOdonateType());
feature.setPropertyValue(RhomeoCore.FT_PROPERTIES.ORTHOPTERE.name(), site.getOrthoptereType());
feature.setPropertyValue(RhomeoCore.FT_PROPERTIES.ORG.name(), site.getOrganization());
feature.setPropertyValue(RhomeoCore.FT_PROPERTIES.REFERENT.name(), site.getReferent());
feature.setPropertyValue(RhomeoCore.FT_PROPERTIES.REMARKS.name(), site.getRemarks());
feature.setPropertyValue(RhomeoCore.FT_PROPERTIES.TYPE.name(), site.getZoneType());
}
/**
* Converts a {@link Feature} into a {@link Site}.
*
* @param feature Feature to convert into a site.
* @return A new instance of site, never {@code null}
*/
public static Site toSite(final Feature feature) {
final SiteImpl newSite = new SiteImpl((MultiPolygon)feature.getProperty(RhomeoCore.FT_PROPERTIES.the_geom.name()).getValue());
newSite.setCountyCode(feature.getProperty(RhomeoCore.FT_PROPERTIES.COUNTY.name()).getValue().toString());
newSite.setName(feature.getProperty(RhomeoCore.FT_PROPERTIES.NAME.name()).getValue().toString());
newSite.setOdonateType(feature.getProperty(RhomeoCore.FT_PROPERTIES.ODONATE.name()).getValue().toString());
newSite.setOrthoptereType(feature.getProperty(RhomeoCore.FT_PROPERTIES.ORTHOPTERE.name()).getValue().toString());
newSite.setOrganization(feature.getProperty(RhomeoCore.FT_PROPERTIES.ORG.name()).getValue().toString());
newSite.setReferent(feature.getProperty(RhomeoCore.FT_PROPERTIES.REFERENT.name()).getValue().toString());
newSite.setRemarks(feature.getProperty(RhomeoCore.FT_PROPERTIES.REMARKS.name()).getValue().toString());
newSite.setZoneType(feature.getProperty(RhomeoCore.FT_PROPERTIES.TYPE.name()).getValue().toString());
return newSite;
}
}

View File

@ -0,0 +1,49 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.list;
/**
*
* @author Alexis Manin (Geomatys)
*/
public interface CodeValue {
Object getCode();
Object getValue();
}

View File

@ -0,0 +1,103 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.list;
/**
* @author Cédric Briançon (Geomatys)
*/
public enum HumidZoneType implements CodeValue {
GRANDS_ESTUAIRES ("1", "Grands estuaires"), // Indicateurs : 1, 2, 3, 5, 6, 8, 12, 13
BAIES_ESTUAIRES ("2", "Baies et estuaires moyens plats"), // Indicateurs : 1, 2, 3, 5, 6, 8, 12, 13
MARAIS_LAGUNES ("3.1", "Marais et lagunes côtiers - lagunes"), // Indicateurs : 1, 2, 3, 5, 6, 8, 9, 10, 11, 12, 13
MARAIS_LAGUNES_PERI_LAGUN ("3.2", "Marais et lagunes côtiers - péri-lagunaire"), // Indicateurs : 1, 2, 3, 5, 6, 8, 9, 10, 11, 12, 13
MARAIS_LAGUNES_PERI_LAGUN_EAU("3.3", "Marais et lagunes côtiers - péri-lagunaire avec rapport d'eau"), // Indicateurs : 1, 2, 3, 5, 6, 8, 9, 10, 11, 12, 13
MARAIS_SAUMATRES ("4", "Marais saumâtres aménagés"), // Indicateurs : 1, 2, 3, 6, 8, 9, 10, 11, 12, 13
BORDURES_COURS_EAU ("5", "Bordures de cours d'eau"), // Indicateurs : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
PLAINES_ALLUVIALES ("6", "Plaines alluviales"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
ZH_BAS_FOND_ALTITUDE ("7.1", "Zones humides de bas-fonds en tête de BV - altitude"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13
ZH_BAS_FOND_TOURB_ACIDE ("7.2", "Zones humides de bas-fonds en tête de BV - tourbière acide"), // Indicateurs : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
ZH_BAS_FOND_TOURB_ALCALINE ("7.3", "Zones humides de bas-fonds en tête de BV - tourbière alcaline"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
ZH_BAS_FOND_PENTES ("7.4", "Zones humides de bas-fonds en tête de BV - pentes et sources"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
ZH_BAS_FOND_COMBES ("7.5", "Zones humides de bas-fonds en tête de BV - combes et bordure de ruisseau"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
REGIONS_ETANGS ("8", "Régions d'étangs"), // Indicateurs : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
BORDURES_PLAN_EAU_ACIDE ("9.1", "Bordures de plan d'eau (lac) - ZH acide"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
BORDURES_PLAN_EAU_ALCALINE ("9.2", "Bordures de plan d'eau (lac) - ZH alcaline"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
MARAIS_LANDES_TOURB ("10.1", "Marais et landes humides de plaine - tourbière de plaine"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
MARAIS_LANDES_PRAIRIES ("10.2", "Marais et landes humides de plaine - prairies humides"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
MARAIS_LANDES_PRES ("10.3", "Marais et landes humides de plaine - Prés salés continentaux"), // Indicateurs : 1, 2, 3, 6, 8, 9, 10, 11, 12, 13
ZH_PONCT_MARE_SAUMATRE ("11.11", "Zones humides ponctuelles - Mare temporaire saumâtre"), // Indicateurs : 1, 2, 3, 6, 8, 9, 10, 11, 12, 13
ZH_PONCT_MARE_ALCALINE ("11.12", "Zones humides ponctuelles - Mare temporaire alcaline"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
ZH_PONCT_MARE_ACIDE ("11.13", "Zones humides ponctuelles - Mare temporaire acide"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
ZH_PONCT_MARE_PERMANENTE ("11.2", "Zones humides ponctuelles - Mare permanente"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
MARAIS_AMENAGES_AGRICOLE ("12", "Marais aménagés dans un but agricole"), // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
ZH_ARTIFICELLES ("13", "Zones humides artificielles"); // Indicateurs : 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13
private final String code;
private final String value;
HumidZoneType(final String code, final String value) {
this.code = code;
this.value = value;
}
@Override
public String getCode() {
return code;
}
@Override
public String getValue() {
return value;
}
public static HumidZoneType getByCode(final String code) {
for (final HumidZoneType candid : values()) {
if (candid.getCode().equals(code)) {
return candid;
}
}
return null;
}
@Override
public String toString() {
return code +" "+ value;
}
}

View File

@ -0,0 +1,130 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.list;
import fr.cenra.rhomeo.core.data.reference.OdonataHabitatReference;
/**
* L_zone_bio_odonate.
*
* id_zone_bio_odo nom_zone_bio_odo
* -------------------------------------
* 1 Alpin
* 2 Continental
* 3 Méditerranéen occidental
* 4 Méditerranéen oriental
* 5 Pyrénéen
*
* (Source : Champs_et_Listes.xlsx)
*
* We add the column "zbio" to join {@link OdonataHabitatReference} and a new
* entry to represent 'zglobal' value.
*
* <code>select distinct zbio, biogeo from odo_dependance_habitat;</code>
*
* zbio | biogeo
*--------------------------+--------------------------
* mediterraneen oriental | Méditerranéen oriental
* pyreneen | pyreneen
* continental | Continental
* alpin | Alpin
* mediterraneen occidental | Méditerranéen occidental
* zglobal | Z Global
*
* (Source : RhoMéO_Geomatys.backup)
*
* @author Cédric Briançon (Geomatys)
* @author Samuel Andrés (Geomatys)
*/
public enum OdonateZoneBio implements CodeValue {
ALPIN ("1", "Alpin", "alpin"),
CONTINENTAL ("2", "Continental", "continental"),
MED_OCCIDENTAL("3", "Méditerranéen occidental", "mediterraneen occidental"),
MED_ORIENTAL ("4", "Méditerranéen oriental", "mediterraneen oriental"),
PYRENEEN ("5", "Pyrénéen", "pyreneen"),
ZGLOBAL ("", "Z Global", "zglobal");
private final String code;
private final String value;
private final String zbio;
OdonateZoneBio(final String code, final String value, final String zbio) {
this.code = code;
this.value = value;
this.zbio = zbio;
}
@Override
public String getCode() {
return code;
}
@Override
public String getValue() {
return value;
}
public String getZbio() {
return zbio;
}
public static OdonateZoneBio getByCode(final String code) {
for (final OdonateZoneBio candid : values()) {
if (candid.getCode().equals(code)) {
return candid;
}
}
return null;
}
public static OdonateZoneBio getByZbio(final String code) {
for (final OdonateZoneBio candid : values()) {
if (candid.getZbio().equals(code)) {
return candid;
}
}
return null;
}
@Override
public String toString() {
return code +" "+ value;
}
}

View File

@ -0,0 +1,97 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.list;
/**
* L_zone_bio_orthoptere.
*
* id_zone_bio_ortho nom_zone_bio_ortho
* ---------------------------------------
* 1 Alpin_marais_alluvial
* 2 Alpin_tourbière
* 3 Mediterranen1_marais_alluvial
* 4 Mediterranen1_marais_littoral
* 5 Mediterranen2_tourbière
* 6 Mediterranen2_marais_alluvial
*
* (Source : Champs_et_Listes.xlsx)
*
* @author Cédric Briançon (Geomatys)
*/
public enum OrthoptereZoneBio implements CodeValue {
ALPIN_MARAIS_ALLUVIAL("1", "Alpin_marais_alluvial"),
ALPIN_TOURBIERE ("2", "Alpin_tourbière"),
MED1_MARAIS_ALLUVIAL ("3", "Mediterranen1_marais_alluvial"),
MED1_MARAIS_LITTORAL ("4", "Mediterranen1_marais_littoral"),
MED2_TOURBIERE ("5", "Mediterranen2_tourbière"),
MED2_MARAIS_ALLUVIAL ("6", "Mediterranen2_marais_alluvial");
private final String code;
private final String value;
OrthoptereZoneBio(final String code, final String value) {
this.code = code;
this.value = value;
}
@Override
public String getCode() {
return code;
}
@Override
public String getValue() {
return value;
}
public static OrthoptereZoneBio getByCode(final String code) {
for (final OrthoptereZoneBio candid : values()) {
if (candid.getCode().equals(code)) {
return candid;
}
}
return null;
}
@Override
public String toString() {
return code +" "+ value;
}
}

View File

@ -0,0 +1,76 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.list;
/**
*
* @author Alexis Manin (Geomatys)
*/
public enum VonPost implements CodeValue {
H1 ("Eau limpide"),
H2 ("eau peu colorée"),
H3 ("eau trouble pâle"),
H4 ("eau trouble foncée"),
H5 ("eau trouble et particules"),
H6 ("1/3 du matériel passe entre les doigts"),
H7 ("1/2 du matériel passe entre les doigts"),
H8 ("2/3 du matériel passe entre les doigts"),
H9 ("Presque tout le matériel"),
H10("Tout le matériel");
private final String value;
private VonPost(final String value) {
this.value = value;
}
@Override
public String getCode() {
return name();
}
@Override
public String getValue() {
return value;
}
@Override
public String toString() {
return new StringBuilder(name()).append(" : ").append(value).toString();
}
}

View File

@ -0,0 +1,206 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.preferences.ftp;
import fr.cenra.rhomeo.core.util.EncryptionResult;
import fr.cenra.rhomeo.core.util.SecretGenerator;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.prefs.Preferences;
import javax.validation.constraints.NotNull;
import org.apache.commons.net.ftp.FTPClient;
/**
* Holds connection information over a single FTP service. The service is defined
* by its host name, but also by a login, password and optionally a root directory.
*
* NOTE : If, for some reason, security context cannot initialize, passwords become
* unavailable.
*
* @author Alexis Manin (Geomatys)
*/
public class FTPAccess {
private static enum PROPERTIES {
ADRESS,
PORT,
LOGIN,
PASSWORD,
WORKING_DIR
}
private final Preferences node;
/**
* Encryption manager. Can be null if an error occurred at initialisation. In
* this case, no password will be read or written.
*/
private final SecretGenerator cipher;
protected FTPAccess(@NotNull final Preferences infoContainer) {
this.node = infoContainer;
cipher = SecretGenerator.getInstance().orElse(null);
}
private String getProperty(final PROPERTIES p) {
return node.get(p.name(), null);
}
private void setProperty(final PROPERTIES p, final String value) {
if (value == null || value.trim().isEmpty()) {
node.remove(p.name());
} else {
node.put(p.name(), value);
}
}
public String getAdress() {
return getProperty(PROPERTIES.ADRESS);
}
public void setAdress(final String newAdress) {
setProperty(PROPERTIES.ADRESS, newAdress);
}
public int getPort() {
return node.getInt(PROPERTIES.PORT.name(), 21);
}
public void setPort(final int port) {
node.putInt(PROPERTIES.PORT.name(), port);
}
public String getLogin() {
return getProperty(PROPERTIES.LOGIN);
}
public void setLogin(final String newLogin) {
setProperty(PROPERTIES.LOGIN, newLogin);
}
public String getWorkingDir() {
return getProperty(PROPERTIES.WORKING_DIR);
}
public void setWorkingDir(final String newDir) {
setProperty(PROPERTIES.WORKING_DIR, newDir);
}
public synchronized String getPassword() throws GeneralSecurityException {
final String tmpValue = getProperty(PROPERTIES.PASSWORD);
if (tmpValue == null || tmpValue.isEmpty()) { // No pw stored.
return "";
}
if (cipher == null) {
throw new GeneralSecurityException("no security manager available. Passwords cannot be read or written from preferences.");
}
byte[] sk = node.getByteArray("ftp_access_skey", null);
byte[] sr = node.getByteArray("ftp_access_sran", null);
if (sk == null || sr == null) {
throw new GeneralSecurityException("no security manager available. Passwords cannot be read or written from preferences.");
}
return cipher.decrypt(tmpValue, sk, sr);
}
public synchronized void setPassword(final String newPassword) throws GeneralSecurityException {
if (cipher == null) {
throw new GeneralSecurityException("no security manager available. Passwords cannot be read or written from preferences.");
}
// Empty value, no encryption needed.
if (newPassword == null || newPassword.isEmpty()) {
setProperty(PROPERTIES.PASSWORD, "");
return;
}
final EncryptionResult encrypted = cipher.encrypt(newPassword);
setProperty(PROPERTIES.PASSWORD, encrypted.output);
node.putByteArray("ftp_access_skey", encrypted.getKey());
node.putByteArray("ftp_access_sran", encrypted.getSecureRandom());
}
/**
* Create a new connection over the FTP service provided by holded information.
* The provided {@link FTPClient} must be closed using {@link FTPClient#disconnect() }
* once you've finished your work.
*
* @return A client to exchange data with FTP service which contains
* reference files.
*
* @throws IOException If we cannot connect to distant service.
* @throws java.security.GeneralSecurityException If an authentication is
* required, but we cannot access to password.
*/
public FTPClient createClient() throws IOException, GeneralSecurityException {
return createClient(getAdress(), getPort(), getWorkingDir(), getLogin(), getPassword());
}
public static FTPClient createClient(final String adress, final int port, final String workDir, final String login, final String password) throws IOException, GeneralSecurityException {
if (adress == null) {
throw new IllegalStateException("No adress configured for FTP access !");
}
final FTPClient client = new FTPClient();
client.enterLocalActiveMode();
if (port < 1) {
client.connect(adress);
} else {
client.connect(adress, port);
}
if (login != null) {
if (!client.login(login, password)) {
throw new GeneralSecurityException("Unable to authenticate with the login "+ login);
}
}
if (workDir != null) {
if (!client.changeWorkingDirectory(workDir)) {
throw new IOException("Unable to change of working directory to:"+ workDir);
}
}
return client;
}
}

View File

@ -0,0 +1,75 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.preferences.ftp;
import fr.cenra.rhomeo.api.preferences.PreferenceGroup;
import javax.validation.constraints.NotNull;
/**
*
* @author Alexis Manin (Geomatys)
*/
public interface FTPPreferences extends PreferenceGroup<Object> {
public static enum ACCESS_POINTS {
REFERENCES("Références"),
RESULTS("Résultats");
private final String title;
ACCESS_POINTS(final String value) {
this.title = value;
}
public String getTitle() {
return title;
}
}
/**
* Give all information about a specific FTP end point.
*
* @param ap The FTP service to retrieve.
*
* @return an object capable to open a connection over the queried FTP service.
*/
@NotNull
FTPAccess getAccess(@NotNull final ACCESS_POINTS ap);
}

View File

@ -0,0 +1,188 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.preferences.ftp;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.preferences.net.NetPreferences;
import fr.cenra.rhomeo.core.util.SecretGenerator;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.prefs.Preferences;
import javax.annotation.PostConstruct;
import javax.validation.constraints.NotNull;
import org.apache.sis.util.ArgumentChecks;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
/**
* Holds preferences over FTP connections. You can retrieve information about a
* specific FTP connection by using {@link #getAccess(fr.cenra.rhomeo.core.preferences.ftp.FTPPreferences.ACCESS_POINTS)
* }.
*
* Known connections are listed in {@link ACCESS_POINTS}.
*
* @author Alexis Manin (Geomatys)
*/
@Component
@Qualifier(FTPPreferencesImpl.NAME)
@Validated
public class FTPPreferencesImpl implements FTPPreferences {
public static final String NAME = "FTPPreferencesImpl";
private final Preferences root;
public FTPPreferencesImpl() {
root = Preferences.userNodeForPackage(FTPPreferencesImpl.class);
}
/**
* DO NOT USE IT !
*
* @param key DO NOT USE IT !
* @return DO NOT USE IT !
*/
@Override
public String getPreference(Object key) {
throw new UnsupportedOperationException("No value stored on root.");
}
/**
* DO NOT USE IT !
*
* @param key DO NOT USE IT !
* @param value DO NOT USE IT !
*/
@Override
public void setPreference(Object key, String value) {
throw new UnsupportedOperationException("No value stored on root.");
}
@Override
public List getKeys() {
return Collections.EMPTY_LIST;
}
/**
* Give all information about a specific FTP end point.
*
* @param ap The FTP service to retrieve.
*
* @return an object capable to open a connection over the queried FTP
* service.
*/
@NotNull
@Override
public FTPAccess getAccess(@NotNull final ACCESS_POINTS ap) {
ArgumentChecks.ensureNonNull("Input access point", ap);
return new FTPAccess(root.node(ap.name()));
}
@Override
public int getPriority() {
return 0;
}
/**
* If no FTP account has been parameterised, a default one is set for result
* publication and reference data.
*/
@PostConstruct
private void checkDefaultAccesses() {
FTPAccess results = getAccess(FTPPreferences.ACCESS_POINTS.RESULTS);
if (setDefaultAccess(results)) {
results.setWorkingDir("results");
}
FTPAccess references = getAccess(FTPPreferences.ACCESS_POINTS.REFERENCES);
if (setDefaultAccess(references)) {
references.setWorkingDir("references");
}
}
private boolean setDefaultAccess(final FTPAccess access) {
if ((access.getAdress() == null || access.getAdress().trim().isEmpty())
&& (access.getLogin() == null || access.getLogin().trim().isEmpty())
&& (access.getWorkingDir() == null || access.getWorkingDir().trim().isEmpty())) {
access.setAdress("constellation.cenra-outils.org");
access.setLogin("rhomeoftp");
final SecretGenerator gen = SecretGenerator.getInstance().orElse(null);
if (gen == null)
return false;
try {
access.setPassword(gen.decrypt("4Iru5sD2uzixbWHhu7R72s658w/l3yCz", getDefaultKey(), getDefaultSR()));
return true;
} catch (IOException | GeneralSecurityException ex) {
RhomeoCore.LOGGER.log(Level.WARNING, null, ex);
}
}
return false;
}
private byte[] getDefaultKey() throws IOException {
final byte[] buf = new byte[16];
int read = 0;
try (final InputStream stream = NetPreferences.class.getResourceAsStream("defaultKey")) {
do {
read += stream.read(buf, read, buf.length - read);
} while (read < buf.length - 1);
}
return buf;
}
private byte[] getDefaultSR() throws IOException {
final byte[] buf = new byte[8];
int read = 0;
try (final InputStream stream = NetPreferences.class.getResourceAsStream("defaultSR")) {
do {
read += stream.read(buf, read, 8 - read);
} while (read < 7);
}
return buf;
}
}

View File

@ -0,0 +1,77 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.preferences.net;
import fr.cenra.rhomeo.api.preferences.InternationalPreferenceKey;
import org.apache.sis.util.ArgumentChecks;
/**
*
* @author Alexis Manin (Geomatys)
*/
public class NetKey implements InternationalPreferenceKey {
private final String keyId;
private final String defaultValue;
public NetKey(final String netKey) {
this(netKey, null);
}
public NetKey(final String netKey, final String defaultValue) {
ArgumentChecks.ensureNonNull("Key", netKey);
this.keyId = netKey;
this.defaultValue = defaultValue;
}
@Override
public String name() {
return keyId;
}
@Override
public String getKey() {
return keyId;
}
@Override
public String getDefaultValue() {
return defaultValue;
}
}

View File

@ -0,0 +1,172 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.preferences.net;
import fr.cenra.rhomeo.api.preferences.UserPackagePreferenceGroup;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.RhomeoRuntimeException;
import fr.cenra.rhomeo.core.util.EncryptionResult;
import fr.cenra.rhomeo.core.util.SecretGenerator;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import org.springframework.stereotype.Component;
/**
*
* @author Alexis Manin (Geomatys)
*/
@Component
public class NetPreferences extends UserPackagePreferenceGroup<NetKey> {
public static final NetKey UPDATE_URL = new NetKey("update_url", "http://updates-bao.cenra-outils.org/updates/update.json");
public static final NetKey UPDATE_USER = new NetKey("update_user", "rhomeo");
public static final NetKey UPDATE_PASSWORD = new PassNetKey("update_pass", null);
public static final NetKey WFS_URL = new NetKey("wfs_url", "http://constellation.cenra-outils.org/WS/wfs/referential");
private static final List<NetKey> KEYS = Collections.unmodifiableList(Arrays.asList(new NetKey[] {
UPDATE_URL, UPDATE_USER, UPDATE_PASSWORD, WFS_URL
}));
private static final String UPDATE_KEY_PREF = "net_update_pass_key";
private static final String UPDATE_SR_PREF = "net_update_pass_sr";
private final SecretGenerator gen;
public NetPreferences() {
gen = SecretGenerator.getInstance().orElse(null);
// Default values for password and secret key.
if (getPreference(UPDATE_PASSWORD) == null) {
prefs.put(UPDATE_PASSWORD.getKey(), "4Iru5sD2uzixbWHhu7R72s658w/l3yCz");
try {
final byte[] buf = new byte[16];
int read = 0;
try (final InputStream stream = NetPreferences.class.getResourceAsStream("defaultKey")) {
do {
read += stream.read(buf, read, buf.length - read);
} while (read < buf.length -1);
prefs.putByteArray(UPDATE_KEY_PREF, buf);
}
read = 0;
try (final InputStream stream = NetPreferences.class.getResourceAsStream("defaultSR")) {
do {
read += stream.read(buf, read, 8 - read);
} while (read < 7);
prefs.putByteArray(UPDATE_SR_PREF, Arrays.copyOf(buf, 8));
}
} catch (IOException ex) {
RhomeoCore.LOGGER.log(Level.FINE, "cannot set default values", ex);
}
}
}
@Override
public List<NetKey> getKeys() {
return KEYS;
}
@Override
public void setPreference(NetKey key, String value) {
if (UPDATE_PASSWORD.equals(key) && value != null && !(value = value.trim()).isEmpty()) {
if (gen != null) {
try {
EncryptionResult encrypted = gen.encrypt(value);
prefs.putByteArray(UPDATE_KEY_PREF, encrypted.getKey());
prefs.putByteArray(UPDATE_SR_PREF, encrypted.getSecureRandom());
value = encrypted.getOutput();
} catch (GeneralSecurityException ex) {
throw new RhomeoRuntimeException(ex);
}
} else {
throw new RhomeoRuntimeException("no security manager available. Passwords cannot be written into preferences.");
}
}
super.setPreference(key, value);
}
@Override
public int getPriority() {
return 1;
}
public URL getUpdateURL() throws MalformedURLException {
return new URL(getPreference(UPDATE_URL));
}
public URLConnection openConnection(final URL toReach) throws GeneralSecurityException, IOException {
final URL updateURL = getUpdateURL();
String user = getPreference(UPDATE_USER);
if (toReach.getHost().equals(updateURL.getHost()) && toReach.getUserInfo() == null && user != null && !(user = user.trim()).isEmpty()) {
final String pass = getPreference(UPDATE_PASSWORD);
URLConnection con = toReach.openConnection();
String dpw = null;
if (pass !=null && !pass.isEmpty()) {
final byte[] key = prefs.getByteArray(UPDATE_KEY_PREF, null);
final byte[] sr = prefs.getByteArray(UPDATE_SR_PREF, null);
if (key == null && sr == null) { // No encryption parameter
dpw = pass;
} else if (gen == null) { // No cipher
throw new RhomeoRuntimeException("no security manager available. Passwords cannot be read from preferences.");
} else {
dpw = gen.decrypt(pass, key, sr);
}
}
final StringBuilder authInfo = new StringBuilder(user);
if (dpw != null) {
authInfo.append(':').append(dpw);
}
final String b64 = Base64.getEncoder().withoutPadding().encodeToString(
authInfo.toString().getBytes()
);
con.addRequestProperty("Authorization", "Basic ".concat(b64));
return con;
} else {
return toReach.openConnection();
}
}
}

View File

@ -0,0 +1,56 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.preferences.net;
import fr.cenra.rhomeo.api.preferences.PasswordKey;
/**
*
* @author Alexis Manin (Geomatys)
*/
public class PassNetKey extends NetKey implements PasswordKey {
public PassNetKey(String netKey) {
super(netKey);
}
public PassNetKey(String netKey, String defaultValue) {
super(netKey, defaultValue);
}
}

View File

@ -0,0 +1,341 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.result;
import com.fasterxml.jackson.databind.ObjectMapper;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.api.process.Indicator;
import fr.cenra.rhomeo.api.result.DashboardResultItem;
import fr.cenra.rhomeo.api.result.Index;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.RhomeoRuntimeException;
import fr.cenra.rhomeo.core.Session;
import fr.cenra.rhomeo.core.util.ExportUtils;
import fr.cenra.rhomeo.core.util.GeometryUtils;
import fr.cenra.rhomeo.core.data.site.SiteRepository;
import fr.cenra.rhomeo.core.util.SerializableDataContext;
import java.beans.IntrospectionException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.logging.Level;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.collection.IntegerList;
import org.geotoolkit.nio.IOUtilities;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Component in charge of storing indicator results locally.
*
* @author Alexis Manin (Geomatys)
*/
@Component
public class ResultStorage {
@Autowired
private ResultWriter writer;
@Autowired
private Session session;
@Autowired
private SiteRepository siteRepo;
@Autowired
private ResultWriter resultWriter;
final String[] expectedFiles = new String[]{ExportUtils.METADATA_JSON, ResultWriter.RESULT_FILE_NAME, ResultWriter.ADDITIONAL_RESULT_FILE_NAME};
private ResultStorage() {
Arrays.sort(expectedFiles);
}
public void store() throws IOException, IntrospectionException {
if (session.getResults() == null || session.getResults().isEmpty() || session.getDataContext() == null)
return;
final String sha1 = GeometryUtils.getSha1(session.getDataContext().getSite().getGeometry());
final Path root = RhomeoCore.RESULT_PATH.resolve(sha1);
Files.createDirectories(root);
/* Sort results by indicator and year, as publication / export via dashboard
* has to be able to export results only for a specific indicator at a
* specific year. We iterate on indices and not on the tracking points
* related to process context, because some protocols appear to use no
* tracking point at all.
*/
final IntegerList years = new IntegerList(10, Short.MAX_VALUE);
for (final Index i : session.getResults()) {
if (years.occurrence(i.getYear()) < 1)
years.add(i.getYear());
}
for (final Indicator indic : session.getProcessContext().getIndicators()) {
final Path indicPath = root.resolve(indic.getName());
Files.createDirectories(indicPath);
for (final int year : years) {
final Path yearPath = indicPath.resolve(Integer.toString(year));
IOUtilities.deleteRecursively(yearPath);
Files.createDirectory(yearPath);
writer.prepareWriting(yearPath)
.writeMetadataJSON()
.writeResults((i) -> i.getYear() == year && i.getSpi().getIndicator().equals(indic));
}
}
}
/**
* Notify the result storage manager that a site had its name changed.
* @param oldSiteName
* @param newSiteName
* @throws IOException
*/
// public void siteNameChanged(final String oldSiteName, final String newSiteName) throws IOException {
// final Base64.Encoder encoder = Base64.getEncoder().withoutPadding();
// String oldName = encoder.encodeToString(oldSiteName.getBytes(StandardCharsets.UTF_8));
// final Path oldPath = RhomeoCore.RESULT_PATH.resolve(oldName);
// if (!Files.exists(oldPath))
// return; // Nothing to move
//
// final String newName = encoder.encodeToString(newSiteName.getBytes(StandardCharsets.UTF_8));
// Files.move(oldPath, RhomeoCore.RESULT_PATH.resolve(newName));
// }
/**
* Remove results associated to the input site, If any. Otherwise, do nothing.
* @param siteName Name of the site to delete results for.
*
* @throws IOException If an error occurs while deleting files.
*/
public void deleteIfExists(String siteName) throws IOException {
if (siteName == null || (siteName = siteName.trim()).isEmpty())
return;
deleteIfExists(siteRepo.findOne(siteName));
}
/**
* Remove results associated to the input site, If any. Otherwise, do nothing.
* @param site The site to delete results for.
*
* @throws IOException If an error occurs while deleting files.
*/
public void deleteIfExists(final Site site) throws IOException {
if (site == null)
return;
final String sha1 = GeometryUtils.getSha1(site.getGeometry());
IOUtilities.deleteRecursively(RhomeoCore.RESULT_PATH.resolve(sha1));
}
/**
* Copy the results associated to the input dashboard item in the FTP pointed
* by given client.
*
* Note : All files are uploaded on the current working directory of the FTP client.
*
* @param target Dashboard item to upload results for.
* @param ftp Connection to the FTP.
* @return true if we succeeded
* @throws IOException
*/
public boolean publish(final DashboardResultItem target, final FTPClient ftp) throws IOException {
Optional<Path> directory = getDirectory(target);
if (!directory.isPresent())
return false;
final Path root = directory.get();
try {
Files.list(root).forEach(p -> {
final String fileName = p.getFileName().toString();
if (Files.isRegularFile(p) && Arrays.binarySearch(expectedFiles, fileName) >= 0) {
try (final InputStream stream = Files.newInputStream(p)) {
ftp.storeFile(fileName, stream);
} catch (IOException e) {
throw new RhomeoRuntimeException(e);
}
}
});
} catch (RhomeoRuntimeException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
throw e;
}
}
return true;
}
/**
* Publish results currently contained in the session into the current
* working directory of the input client.
*
* @param ftpSupplier Provides an FTP client positioned in the directory we
* want to send data to. The FTP CLient is closed internally.
*
* @throws IOException
* @throws DataStoreException
* @throws IntrospectionException
*/
public void publish(final Supplier<FTPClient> ftpSupplier) throws IOException, DataStoreException, IntrospectionException {
Path tmpDir = Files.createTempDirectory("results");
try {
resultWriter.prepareWriting(tmpDir).writeMetadataJSON().writeResults().writeAdditionalValues();
final FTPClient ftp = ftpSupplier.get();
try {
Files.list(tmpDir).forEach(p -> {
final String fileName = p.getFileName().toString();
if (Files.isRegularFile(p)) {
try (final InputStream stream = Files.newInputStream(p)) {
if (!ftp.storeFile(fileName, stream))
throw new RhomeoRuntimeException("A file cannot be copied via FTP : ".concat(fileName));
} catch (IOException e) {
throw new RhomeoRuntimeException(e);
}
}
});
} finally {
ftp.disconnect();
}
} catch (RhomeoRuntimeException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
throw e;
}
} finally {
// If the task fails, it does not halt publication process.
try {
IOUtilities.deleteRecursively(tmpDir);
} catch (Exception e) {
RhomeoCore.LOGGER.log(Level.WARNING, "Cannot clear temporary data", e);
}
}
}
/**
* Return a path to the CSV file containing main indices related to the
* given dashboard item.
* @param item
* @return A path to the requested results, or an empty optional if there's
* no result available, or they're not accessible.
* @throws java.io.IOException If an error occurs while checking result path.
*/
public Optional<Path> getMainResults(final DashboardResultItem item) throws IOException {
Optional<Path> directory = getDirectory(item);
if (directory.isPresent()) {
final Path root = directory.get();
// Read main results
Path results = root.resolve(ResultWriter.RESULT_FILE_NAME);
if (Files.isReadable(results)) {
return Optional.of(results);
}
}
return Optional.empty();
}
/**
* Return a path to the CSV file containing additional indices related to
* the given dashboard item.
* @param item
* @return A path to the requested results, or an empty optional if there's
* no result available, or they're not accessible.
* @throws java.io.IOException If an error occurs while checking result path.
*/
public Optional<Path> getAdditionalResults(final DashboardResultItem item) throws IOException {
Optional<Path> directory = getDirectory(item);
if (directory.isPresent()) {
final Path root = directory.get();
// Read main results
Path results = root.resolve(ResultWriter.ADDITIONAL_RESULT_FILE_NAME);
if (Files.isReadable(results)) {
return Optional.of(results);
}
}
return Optional.empty();
}
public Optional<SerializableDataContext> readMetadata(final DashboardResultItem item) throws IOException {
Optional<Path> directory = getDirectory(item);
if (directory.isPresent()) {
final Path root = directory.get();
// Read main results
Path results = root.resolve(ExportUtils.METADATA_JSON);
if (Files.isReadable(results)) {
try (final BufferedReader reader = Files.newBufferedReader(results, StandardCharsets.UTF_8)) {
return Optional.of(new ObjectMapper().readValue(reader, SerializableDataContext.class));
}
}
}
return Optional.empty();
}
private Optional<Path> getDirectory(final DashboardResultItem target) throws IOException {
if (target.getSite() == null || target.getSite().isEmpty()
|| target.getIndicator() == null || target.getIndicator().isEmpty())
return Optional.empty();
final Site site = siteRepo.findOne(target.getSite());
if (site == null)
return Optional.empty();
final String sha1 = GeometryUtils.getSha1(site.getGeometry());
final Path root = RhomeoCore.RESULT_PATH.resolve(sha1).resolve(target.getIndicator()).resolve(Integer.toString(target.getYear()));
if (!Files.isDirectory(root) || !Files.list(root).findAny().isPresent())
return Optional.empty();
return Optional.of(root);
}
}

View File

@ -0,0 +1,281 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.result;
import fr.cenra.rhomeo.api.Version;
import fr.cenra.rhomeo.api.data.DataContext;
import fr.cenra.rhomeo.api.data.Dataset;
import fr.cenra.rhomeo.api.data.Reference;
import fr.cenra.rhomeo.api.data.ReferenceDescription;
import fr.cenra.rhomeo.api.data.TrackingPoint;
import fr.cenra.rhomeo.api.data.Warning;
import fr.cenra.rhomeo.api.process.Indicator;
import fr.cenra.rhomeo.api.result.AdditionalValue;
import fr.cenra.rhomeo.api.result.AdditionalValueMapper;
import fr.cenra.rhomeo.api.result.Index;
import fr.cenra.rhomeo.api.result.TrackingPointIndex;
import fr.cenra.rhomeo.core.CSVEncoder;
import fr.cenra.rhomeo.core.Session;
import fr.cenra.rhomeo.core.util.ExportUtils;
import fr.cenra.rhomeo.core.util.SerializableDataContext;
import java.beans.IntrospectionException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.ArgumentChecks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
*
* @author Alexis Manin (Geomatys)
*/
@Component
public class ResultWriter {
static final String ADDITIONAL_RESULT_FILE_NAME = "additionalResults.csv";
static final String RESULT_FILE_NAME = "results.csv";
@Autowired
Session session;
@Autowired(required = false)
Set<AdditionalValueMapper> avMappers;
public WritingContext prepareWriting(final Path root) throws IOException {
return new WritingContext(root, session, avMappers);
}
/**
* An object independent from {@link Session} variations, designed to write
* a snapshot of results stored in session at a precise time (creation of
* an instance of this class).
*/
public static class WritingContext {
final Path root;
final DataContext dataContext;
final Map<ReferenceDescription, Version> referenceVersions;
final Set<Indicator> indicators;
final Set<TrackingPoint> trackingPoints;
final List<Index> indices;
final Validator validator;
final Dataset ds;
final Set<AdditionalValue> additional;
final Set<AdditionalValueMapper> mappers;
private WritingContext(final Path root, final Session session, final Set<AdditionalValueMapper> avMappers) throws IOException {
ArgumentChecks.ensureNonNull("Root path", root);
ArgumentChecks.ensureNonNull("Session", session);
if (!Files.isDirectory(root)) {
Files.createDirectories(root);
}
this.root = root;
final DataContext dc = session.getDataContext();
ArgumentChecks.ensureNonNull("Data context", dc);
ArgumentChecks.ensureNonNull("Site", dc.getSite());
ArgumentChecks.ensureNonNull("Protocol", dc.getProtocol());
dataContext = dc;
referenceVersions = new HashMap<>();
if (dc.getReferences() != null) {
for (final Map.Entry<Class<Reference>, Version> entry : dc.getReferences().entrySet()) {
for (final ReferenceDescription refDescriptor : dc.getProtocol().getReferenceTypes()) {
if (refDescriptor.getReferenceType().equals(entry.getKey()))
referenceVersions.put(refDescriptor, entry.getValue());
}
}
}
indicators = new HashSet<>(session.getProcessContext().getIndicators());
trackingPoints = new HashSet<>(session.getProcessContext().getTrackingPoints());
indices = new ArrayList<>(session.getResults());
// #40 : Sort results.
indices.sort(new IndexComparator());
validator = session.getBean(Validator.class);
ds = session.getDataset();
mappers = avMappers;
if (mappers == null || mappers.isEmpty()) // If no writer is available, there's no need to keep a reference of additional values.
additional = Collections.EMPTY_SET;
else additional = session.getAdditionalValues();
}
public WritingContext writeSite() throws IOException, DataStoreException {
ExportUtils.writeGeoJson(dataContext.getSite(), root);
return this;
}
public WritingContext writeMetadataJSON() throws IOException {
ExportUtils.writeMetadata(SerializableDataContext.fromDataContext(dataContext), root);
return this;
}
public WritingContext writeMetadataText() throws IOException {
final Set<ConstraintViolation<Dataset>> datasetErrors = validator.validate(ds);
datasetErrors.removeIf(cv -> cv.getConstraintDescriptor().getPayload().contains(Warning.class));
ExportUtils.writeMetadataText(root.resolve("metadata.txt"), dataContext.getSite(), dataContext.getProtocol(), referenceVersions, indicators, dataContext.getUserData(), datasetErrors);
return this;
}
/**
* Write all results in CSV files (results.csv for main ones,
* additionalResults.csv for others).
*
* @return This context.
* @throws IOException
* @throws IntrospectionException
*/
public WritingContext writeResults() throws IOException, IntrospectionException {
return writeResults(null);
}
/**
* Write results accepted by given predicate in CSV files (results.csv
* for main ones, additionalResults.csv for others).
*
* @param filter A predicate to filter results.
* @return This context.
* @throws IOException
* @throws IntrospectionException
*/
public WritingContext writeResults(final Predicate<Index> filter) throws IOException, IntrospectionException {
final ArrayList<SimpleLocationIndex> mainResults = new ArrayList();
final ArrayList<SimpleLocationIndex> additionalResults = new ArrayList();
boolean primaryHasLocation = false;
boolean additionalHasLocation = false;
for (final Index i : indices) {
if (filter != null && !filter.test(i))
continue;
boolean isPrimary = i.getSpi().isPrimary();
if (isPrimary) {
mainResults.add(new SimpleLocationIndex(i));
if (i instanceof TrackingPointIndex)
primaryHasLocation = true;
} else {
additionalResults.add(new SimpleLocationIndex(i));
if (i instanceof TrackingPointIndex)
additionalHasLocation = true;
}
}
// Write main results
if (!mainResults.isEmpty()) {
final Path mainResultPath = root.resolve(RESULT_FILE_NAME);
Files.deleteIfExists(mainResultPath);
new CSVEncoder(
mainResultPath,
primaryHasLocation ? SimpleLocationIndex.class : SimpleYearIndex.class,
StandardCharsets.UTF_8
).encode(mainResults, true);
}
// Write additional results.
if (!additionalResults.isEmpty()) {
final Path additionalResultPath = root.resolve(ADDITIONAL_RESULT_FILE_NAME);
Files.deleteIfExists(additionalResultPath);
new CSVEncoder(
additionalResultPath,
additionalHasLocation ? SimpleLocationIndex.class : SimpleYearIndex.class,
StandardCharsets.UTF_8
).encode(additionalResults, true);
}
return this;
}
public void writeAdditionalValues() throws IOException {
if (mappers == null || mappers.isEmpty())
return;
for (final AdditionalValueMapper mapper : mappers)
if (mapper.writeValues(root, additional))
return;
}
}
/**
* Sort index by type, year, eventually tracking point, and finally by value.
*/
private static class IndexComparator implements Comparator<Index> {
@Override
public int compare(Index o1, Index o2) {
int comparison = o1.getSpi().compareTo(o2.getSpi());
if (comparison == 0) {
comparison = o1.getYear() - o2.getYear();
if (comparison == 0) {
if (o1 instanceof TrackingPointIndex) {
if (o2 instanceof TrackingPointIndex) {
comparison = ((TrackingPointIndex) o1).getPoint().getName().compareTo(((TrackingPointIndex) o2).getPoint().getName());
} else {
comparison = 1;
}
} else if (o2 instanceof TrackingPointIndex) {
comparison = -1;
}
}
}
return comparison == 0 ? Double.compare(o1.getValue().doubleValue(), o2.getValue().doubleValue()) : comparison;
}
}
}

View File

@ -0,0 +1,71 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.result;
import fr.cenra.rhomeo.api.result.Index;
import fr.cenra.rhomeo.api.result.TrackingPointIndex;
/**
*
* @author Alexis Manin (Geomatys)
*/
public class SimpleLocationIndex extends SimpleYearIndex {
private String location;
public SimpleLocationIndex(){}
public SimpleLocationIndex(Index source) {
super(source);
if (source instanceof TrackingPointIndex)
location = ((TrackingPointIndex)source).getPoint().getName();
else location = null;
}
public String getLocation() {
return location;
}
public void setLocation(final String location){this.location=location;}
@Override
public String toString() {
return "SimpleLocationIndex{" + "name=" + name + ", year=" + year + ", location=" + location + ", value=" + value +'}';
}
}

View File

@ -0,0 +1,81 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.result;
import fr.cenra.rhomeo.api.result.Index;
/**
*
* @author Alexis Manin (Geomatys)
*/
public class SimpleYearIndex {
protected String name;
protected int year;
protected Number value;
public SimpleYearIndex(){}
protected SimpleYearIndex(final Index source) {
name = source.getSpi().getTitle();
year = source.getYear();
value = source.getValue();
}
public String getName() {
return name;
}
public int getYear() {
return year;
}
public Number getValue() {
return value;
}
public void setName(final String name){this.name=name;}
public void setYear(final int year){this.year=year;}
public void setValue(final Number value){this.value=value;}
@Override
public String toString() {
return "SimpleYearIndex{" + "name=" + name + ", year=" + year + ", value=" + value + '}';
}
}

View File

@ -0,0 +1,282 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.state;
import com.fasterxml.jackson.databind.ObjectMapper;
import fr.cenra.rhomeo.api.data.DataContext;
import fr.cenra.rhomeo.api.data.Dataset;
import fr.cenra.rhomeo.api.data.Statement;
import fr.cenra.rhomeo.core.CSVDecoder;
import fr.cenra.rhomeo.core.CSVEncoder;
import fr.cenra.rhomeo.core.Session;
import fr.cenra.rhomeo.core.WorkflowStep;
import fr.cenra.rhomeo.core.util.SerializableDataContext;
import java.beans.IntrospectionException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.logging.Level;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Listens given dataset and data context over time, to save them on change.
*
* @author Alexis Manin (Geomatys)
*/
@Component
class DatasetManager implements StepStateManager {
private static final String CONTEXT_FILE = "context.json";
private static final String DATASET_FILE = "dataset.csv";
@Autowired
private Session session;
@Autowired
private StateManager manager;
private Path target;
private DataContext context;
private Dataset dataset;
private final ObjectMapper jsonMapper;
private final InvalidationListener datasetListener;
private final InvalidationListener contextListener;
private StateManager.Trigger contextTrigger;
private StateManager.Trigger datasetTrigger;
/**
*
*/
public DatasetManager() {
jsonMapper = new ObjectMapper();
contextListener = this::contextChanged;
datasetListener = this::datasetChanged;
}
@Override
public WorkflowStep getStep() {
return WorkflowStep.DATASET;
}
@Override
public synchronized void setBackupFolder(Path toWriteInto) {
target = toWriteInto;
}
@Override
public synchronized void startSaving() {
if (session.getDataContext() == context && session.getDataset() == dataset)
return; // Already listening on changes.
if (context != null || dataset != null)
stopSaving();
context = session.getDataContext();
context.getReferences().addListener(contextListener);
contextTrigger = manager.prepareTask(this::writeContext, "Data context state");
// If data context does not exists, we create it now
if (target != null && !Files.exists(target.resolve(CONTEXT_FILE))) {
contextChanged(null);
}
dataset = session.getDataset();
dataset.getItems().addListener(datasetListener);
datasetTrigger = manager.prepareTask(this::writeDataset, "Dataset state");
// If dataset does not exists, we create it now
if (target != null && !Files.exists(target.resolve(DATASET_FILE))) {
datasetChanged(null);
}
}
@PreDestroy
@Override
public synchronized void stopSaving() {
if (dataset != null) {
datasetTrigger.close();
dataset.getItems().removeListener(datasetListener);
datasetTrigger = null;
dataset = null;
}
if (context != null) {
contextTrigger.close();
context.getReferences().removeListener(contextListener);
contextTrigger = null;
context = null;
}
}
@Override
public boolean restoreStep() {
if (dataset != null || context != null) {
throw new IllegalStateException("Cannot restore dataset while backup is running !");
}
if (target == null) {
return false;
}
/*
* We start restoration with data context, as no dataset can be
* initialized without it. The dataset is read once we're sure we've
* loaded a valid context.
*/
try {
final SerializableDataContext ctx = readContext();
if (ctx == null || !ctx.restoreDataContext(session))
return false;
final List<? extends Statement> data = readDataset();
if (data != null) {
session.getDataset().getItems().addAll(data);
}
return true;
} catch (Exception e) {
StateManager.LOGGER.log(Level.WARNING, "Cannot restore dataset from folder ".concat(target.toString()), e);
return false;
}
}
/**
*
* @return A data context loaded from backup folder. Null if no context is
* available.
* @throws IOException If a backup file is present, but something goes wrong
* at reading.
*/
private SerializableDataContext readContext() throws IOException {
final Path ctxFile = target.resolve(CONTEXT_FILE);
if (Files.isReadable(ctxFile)) {
try (final BufferedReader reader = Files.newBufferedReader(ctxFile, StandardCharsets.UTF_8)) {
return jsonMapper.readValue(reader, SerializableDataContext.class);
}
}
return null;
}
/**
*
* @return A dataset loaded from backup folder. Null if none is available.
* @throws IntrospectionException If a backup file is present, but something
* goes wrong at reading.
* @throws IOException If a backup file is present, but something goes wrong
* at reading.
* @throws ReflectiveOperationException If an error happens with the read
* datatype.
*/
private List<? extends Statement> readDataset() throws IntrospectionException, IOException, ReflectiveOperationException {
final Path datafile = target.resolve(DATASET_FILE);
if (Files.isReadable(datafile)) {
return new CSVDecoder(datafile, session.getDataContext().getProtocol().getDataType(), StandardCharsets.UTF_8).decode();
}
return null;
}
/**
*
* @return
* @throws IOException
* @throws IntrospectionException
*/
private boolean writeDataset() throws IOException, IntrospectionException {
final Path tmpFile = Files.createTempFile("tmpDataset", ".csv");
new CSVEncoder(tmpFile, context.getProtocol().getDataType(), StandardCharsets.UTF_8).encode(dataset.getItems(), true);
Files.move(tmpFile, target.resolve(DATASET_FILE), StandardCopyOption.REPLACE_EXISTING);
return true;
}
/**
* Save current data context. First, we write it in a temporary file. We replace
* the backup only once we're sure the context has been successfully written.
* @throws IOException
*/
private boolean writeContext() throws IOException {
final Path tmpFile = Files.createTempFile("tmpContext", ".json");
// Prepare a serializable object.
final SerializableDataContext ctx = SerializableDataContext.fromDataContext(context);
try (final BufferedWriter writer = Files.newBufferedWriter(tmpFile, StandardCharsets.UTF_8)) {
jsonMapper.writeValue(writer, ctx);
}
Files.move(tmpFile, target.resolve(CONTEXT_FILE), StandardCopyOption.REPLACE_EXISTING);
return true;
}
/**
* Notify writer that we want it to store the current context state. We both
* set trigger value and notify it, so if the writer is waiting, it will
* be notified, and if it is already running, it will immediately be aware
* that a new saving is required.
*
* @param obs
*/
private void contextChanged(final Observable obs) {
contextTrigger.arm();
}
/**
* Notify writer that we want it to store the current dataset state. We both
* set trigger value and notify it, so if the writer is waiting, it will
* be notified, and if it is already running, it will immediately be aware
* that a new saving is required.
*
* @param obs
*/
private void datasetChanged(final Observable obs) {
datasetTrigger.arm();
}
}

View File

@ -0,0 +1,305 @@
/**
* Copyright © CENRA (2016)
*
* remi.clement@espaces-naturels.fr
*
* This software is a multi-platform and portable application based on
* the scientifical toolbox to monitor wetlands. With this toolbox its
* possible to calculate indicators «RhoMéO» to monitor quality,
* fonctionalities and urban and agricultural pressures of wetlands.
* Indicators are calculated using naturalist data entered and/or imported,
* and geographic data downloaded from a geographic server.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.state;
import fr.cenra.rhomeo.api.process.Indicator;
import fr.cenra.rhomeo.api.process.ProcessContext;
import fr.cenra.rhomeo.core.Session;
import fr.cenra.rhomeo.core.WorkflowStep;
import java.beans.IntrospectionException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.logging.Level;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.MapChangeListener;
import javafx.collections.SetChangeListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
*
* @author Alexis Manin (Geomatys)
*/
@Component
public class ProcessManager implements StepStateManager {
private static final String INDICATOR_FILE = "indicators.txt";
private static final String FILTER_FILE = "filter";
private static final String USER_DATA_FILE = "userData";
@Autowired
private Session session;
@Autowired
private StateManager manager;
private Path target;
private ProcessContext ctx;
private StateManager.Trigger indicatorTrigger;
private StateManager.Trigger filterTrigger;
private StateManager.Trigger userDataTrigger;
private final SetChangeListener<Indicator> indicatorListener = this::indicatorsChanged;
private final ChangeListener<Predicate> filterListener = this::filterChanged;
private final MapChangeListener userDataListener = this::userDataChanged;
@Override
public void setBackupFolder(Path toWriteInto) {
target = toWriteInto;
}
@Override
public WorkflowStep getStep() {
return WorkflowStep.PROCESS;
}
@Override
public void startSaving() {
if (ctx != null)
stopSaving();
ctx = session.getProcessContext();
// Submit backup tasks.
filterTrigger = manager.prepareTask(this::writeFilter, "Process context filter");
indicatorTrigger = manager.prepareTask(this::writeIndicators, "Process context indicators");
userDataTrigger = manager.prepareTask(this::writeUserData, "Process context indicators");
// Force writing process context in current state.
filterTrigger.arm();
indicatorTrigger.arm();
userDataTrigger.arm();
// listens on process context change.
ctx.filterProperty().addListener(filterListener);
ctx.getIndicators().addListener(indicatorListener);
ctx.getDataCtx().getUserData().addListener(userDataListener);
}
@Override
public void stopSaving() {
if (ctx != null) {
ctx.filterProperty().removeListener(filterListener);
filterTrigger.close();
filterTrigger = null;
ctx.getIndicators().removeListener(indicatorListener);
indicatorTrigger.close();
indicatorTrigger = null;
ctx.getDataCtx().getUserData().removeListener(userDataListener);
userDataTrigger.close();
userDataTrigger = null;
ctx = null;
}
}
@Override
public boolean restoreStep() {
final ProcessContext ctx = readProcessContext();
if (ctx != null) {
session.setProcessContext(ctx);
return true;
}
return false;
}
ProcessContext readProcessContext() {
// Cannot restore process context if no data has been prepared before hand.
if (target == null || session.getDataContext() == null || session.getDataset() == null)
return null;
final Predicate filter;
final Collection<Indicator> indics;
final Map userData;
try {
filter = readFilter();
indics = readIndicators();
userData = readUserData();
} catch (Exception e) {
StateManager.LOGGER.log(Level.WARNING, "Cannot restore process context.", e);
return null;
}
final boolean noIndic = indics == null || indics.isEmpty();
final boolean noUserData = userData == null || userData.isEmpty();
if (filter == null && noIndic && noUserData) {
return null;
}
final ProcessContext context = new ProcessContext(session.getDataset(), session.getDataContext());
if (filter != null) {
context.setFilter(filter);
}
if (!noIndic) {
context.getIndicators().addAll(indics);
}
if (!noUserData) {
context.getDataCtx().getUserData().putAll(userData);
}
return context;
}
/**
* @return Tracking points saved in backup file.
*/
private Predicate readFilter() throws IOException, ClassNotFoundException {
final Path file;
if (target == null || !Files.isReadable((file = target.resolve(FILTER_FILE))))
return null;
try (final InputStream source = Files.newInputStream(file);
final ObjectInputStream input = new ObjectInputStream(source)) {
final Object read = input.readObject();
if (read instanceof Predicate)
return (Predicate) read;
}
return null;
}
private Collection<Indicator> readIndicators() throws IOException {
final Path file;
if (target == null || !Files.isReadable((file = target.resolve(INDICATOR_FILE))))
return null;
final HashSet<Indicator> indicators = new HashSet<>();
try (final BufferedReader r = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
r.lines().forEach(line -> {
final Optional<Indicator> optIndicator = session.getIndicators().stream()
.filter(indicator -> line.equals(indicator.getName()))
.findFirst();
optIndicator.ifPresent(indicators::add);
});
}
return indicators;
}
private Map readUserData() throws IOException, ClassNotFoundException {
final Path file;
if (target == null || !Files.isReadable((file = target.resolve(USER_DATA_FILE))))
return null;
try (final InputStream source = Files.newInputStream(file);
final ObjectInputStream input = new ObjectInputStream(source)) {
final Object read = input.readObject();
if (read instanceof Map)
return (Map) read;
}
return null;
}
private boolean writeFilter() throws IOException, IntrospectionException {
if (ctx.getFilter() == null)
return false;
final Path tmpFile = Files.createTempFile(target, FILTER_FILE, ".tmp");
try (final OutputStream destination = Files.newOutputStream(tmpFile);
final ObjectOutputStream output = new ObjectOutputStream(destination)) {
output.writeObject(ctx.getFilter());
}
Files.move(tmpFile, target.resolve(FILTER_FILE), StandardCopyOption.REPLACE_EXISTING);
return true;
}
private boolean writeIndicators() throws IOException {
if (ctx.getIndicators().isEmpty())
return false;
final Path tmpFile = Files.createTempFile(target, INDICATOR_FILE, ".tmp");
try (final BufferedWriter w = Files.newBufferedWriter(tmpFile, StandardCharsets.UTF_8)) {
for (final Indicator i : ctx.getIndicators()) {
w.write(i.getName());
w.newLine();
}
}
Files.move(tmpFile, target.resolve(INDICATOR_FILE), StandardCopyOption.REPLACE_EXISTING);
return true;
}
private boolean writeUserData() throws IOException {
// Defensive copy. Moreover, Serialization does not work with observable map.
final Map<String, Object> userData = new HashMap(ctx.getDataCtx().getUserData());
if (userData.isEmpty())
return false;
final Path tmpFile = Files.createTempFile(target, USER_DATA_FILE, ".tmp");
try (final OutputStream destination = Files.newOutputStream(tmpFile);
final ObjectOutputStream output = new ObjectOutputStream(destination)) {
output.writeObject(userData);
}
Files.move(tmpFile, target.resolve(USER_DATA_FILE), StandardCopyOption.REPLACE_EXISTING);
return true;
}
private void filterChanged(final ObservableValue<? extends Predicate> obs, Predicate oldValue, Predicate newValue) {
filterTrigger.arm();
}
private void indicatorsChanged(final SetChangeListener.Change c) {
indicatorTrigger.arm();
}
private void userDataChanged(final MapChangeListener.Change c) {
userDataTrigger.arm();
}
}

Some files were not shown because too many files have changed in this diff Show More