/*
* JGrass - Free Open Source Java GIS http://www.jgrass.org
* (C) HydroloGIS - www.hydrologis.com
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Library General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option) any
* later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more
* details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; if not, write to the Free Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige;
import static org.jgrasstools.gears.libs.modules.JGTConstants.isNovalue;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_AUTHORCONTACTS;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_AUTHORNAMES;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_KEYWORDS;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_LABEL;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_LICENSE;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_NAME;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_STATUS;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_doLog_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_fMonpointid_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inDams_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inDamsdata_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inDuffyInput_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inEtp_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inHillslope_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inHydrometerdata_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inHydrometers_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inHymodInput_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inNetwork_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inOfftakes_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inOfftakesdata_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inRain_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inTributary_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_inTributarydata_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_outDischarge_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_outSubdischarge_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_pPfafids_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_pRainduration_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_pRainintensity_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_tEnd_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_tStart_DESCRIPTION;
import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSADIGE_tTimestep_DESCRIPTION;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import oms3.annotations.Author;
import oms3.annotations.Description;
import oms3.annotations.Execute;
import oms3.annotations.In;
import oms3.annotations.Keywords;
import oms3.annotations.Label;
import oms3.annotations.License;
import oms3.annotations.Name;
import oms3.annotations.Out;
import oms3.annotations.Status;
import oms3.annotations.Unit;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.jgrasstools.gears.libs.exceptions.ModelsIllegalargumentException;
import org.jgrasstools.gears.libs.exceptions.ModelsRuntimeException;
import org.jgrasstools.gears.libs.modules.JGTConstants;
import org.jgrasstools.gears.libs.modules.JGTModel;
import org.jgrasstools.gears.utils.features.FeatureExtender;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.core.Dams;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.core.HillSlopeDuffy;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.core.Hydrometers;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.core.IDischargeContributor;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.core.IHillSlope;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.core.Offtakes;
import org.jgrasstools.hortonmachine.modules.network.PfafstetterNumber;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.core.Tributaries;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.duffy.DuffyAdigeEngine;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.duffy.DuffyInputs;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.hymod.HymodAdigeEngine;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.hymod.HymodInputs;
import org.jgrasstools.hortonmachine.modules.hydrogeomorphology.adige.utils.AdigeUtilities;
import org.jgrasstools.hortonmachine.modules.network.networkattributes.NetworkChannel;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.opengis.feature.simple.SimpleFeature;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory;
@Description(OMSADIGE_DESCRIPTION)
@Author(name = OMSADIGE_AUTHORNAMES, contact = OMSADIGE_AUTHORCONTACTS)
@Keywords(OMSADIGE_KEYWORDS)
@Label(OMSADIGE_LABEL)
@Name(OMSADIGE_NAME)
@Status(OMSADIGE_STATUS)
@License(OMSADIGE_LICENSE)
public class OmsAdige extends JGTModel {
@Description(OMSADIGE_inHillslope_DESCRIPTION)
@In
public SimpleFeatureCollection inHillslope;
@Description(OMSADIGE_pRainintensity_DESCRIPTION)
@Unit("mm/h")
@In
@Out
public double pRainintensity = -1.0;
@Description(OMSADIGE_pRainduration_DESCRIPTION)
@Unit("min")
@In
@Out
public int pRainduration = -1;
@Description(OMSADIGE_inRain_DESCRIPTION)
@In
public HashMap<Integer, double[]> inRain;
@Description(OMSADIGE_inHydrometers_DESCRIPTION)
@In
public SimpleFeatureCollection inHydrometers;
@Description(OMSADIGE_inHydrometerdata_DESCRIPTION)
@In
public HashMap<Integer, double[]> inHydrometerdata;
@Description(OMSADIGE_inDams_DESCRIPTION)
@In
public SimpleFeatureCollection inDams;
@Description(OMSADIGE_inDamsdata_DESCRIPTION)
@In
public HashMap<Integer, double[]> inDamsdata;
@Description(OMSADIGE_inTributary_DESCRIPTION)
@In
public SimpleFeatureCollection inTributary;
@Description(OMSADIGE_inTributarydata_DESCRIPTION)
@In
public HashMap<Integer, double[]> inTributarydata;
@Description(OMSADIGE_inOfftakes_DESCRIPTION)
@In
public SimpleFeatureCollection inOfftakes;
@Description(OMSADIGE_inOfftakesdata_DESCRIPTION)
@In
public HashMap<Integer, double[]> inOfftakesdata;
@Description(OMSADIGE_pPfafids_DESCRIPTION)
@In
public String pPfafids = null;
@Description(OMSADIGE_fMonpointid_DESCRIPTION)
@In
public String fMonpointid = null;
@Description(OMSADIGE_inNetwork_DESCRIPTION)
@In
public SimpleFeatureCollection inNetwork;
@Description(OMSADIGE_inEtp_DESCRIPTION)
@In
public HashMap<Integer, double[]> inEtp;
@Description(OMSADIGE_doLog_DESCRIPTION)
@In
public boolean doLog = false;
@Description(OMSADIGE_tTimestep_DESCRIPTION)
@In
public int tTimestep = 0;
@Description(OMSADIGE_tStart_DESCRIPTION)
@In
public String tStart = null;
@Description(OMSADIGE_tEnd_DESCRIPTION)
@In
public String tEnd = null;
@Description(OMSADIGE_inDuffyInput_DESCRIPTION)
@In
public DuffyInputs inDuffyInput = null;
@Description(OMSADIGE_inHymodInput_DESCRIPTION)
@In
public HymodInputs inHymodInput = null;
@Description(OMSADIGE_outDischarge_DESCRIPTION)
@Out
public HashMap<Integer, double[]> outDischarge;
@Description(OMSADIGE_outSubdischarge_DESCRIPTION)
@Out
public HashMap<Integer, double[]> outSubdischarge;
// public String startDateArg = null;
// public String endDateArg = null;
// public double deltaTArg = null;
/*
* ATTRIBUTES FIELDS
*/
// private Date startDate;
// private Date endDate;
/** the running rain array */
private double[] rainArray = null;
private double[] etpArray;
/** the running discharge array, which at the begin holds the initial conditions */
private double[] initialConditions = null;
private IAdigeEngine adigeEngine = null;
private List<PfafstetterNumber> netPfaffsList;
// hydrometers
private IDischargeContributor hydrometersHandler;
private HashMap<String, Integer> hydrometer_pfaff2idMap;
// dams
private IDischargeContributor damsHandler;
private HashMap<String, Integer> dams_pfaff2idMap;
// tributaries
private IDischargeContributor tributaryHandler;
private HashMap<String, Integer> tributary_pfaff2idMap;
// offtakes
private IDischargeContributor offtakesHandler;
private HashMap<String, Integer> offtakes_pfaff2idMap;
private HashMap<Integer, Integer> basinid2Index;
private HashMap<Integer, Integer> index2Basinid;
private int hillsSlopeNum;
private int outletHillslopeId = -1;
private HashMap<String, Integer> pfaff2Index;
private List<IHillSlope> orderedHillslopes;
public static DateTimeFormatter adigeFormatter = JGTConstants.utcDateFormatterYYYYMMDDHHMM;
private DateTime startTimestamp;
private DateTime endTimestamp;
private DateTime currentTimstamp;
private DateTime rainEndTimestamp;
private List<String> pfaffsList;
@SuppressWarnings("nls")
@Execute
public void process() throws Exception {
checkNull(inHillslope, inNetwork);
if (startTimestamp == null) {
outDischarge = new HashMap<Integer, double[]>();
outSubdischarge = new HashMap<Integer, double[]>();
startTimestamp = adigeFormatter.parseDateTime(tStart);
if (tEnd != null)
endTimestamp = adigeFormatter.parseDateTime(tEnd);
currentTimstamp = startTimestamp;
if (pRainintensity != -1) {
if (pRainduration != -1) {
rainEndTimestamp = startTimestamp.plusMinutes(pRainduration);
} else {
throw new ModelsIllegalargumentException(
"In the case of usage of a constant rainintensity it is necessary to define also its duration.\nCheck your arguments, probably the --rainduration flag is missing.",
this, pm);
}
}
if (pfaffsList == null) {
// first time link basins with network
linkBasinWithNetwork();
}
prepareMonitoringPoints();
hillsSlopeNum = inHillslope.size();
// at the first round create the hillslopes and network hierarchy
orderedHillslopes = AdigeUtilities.generateHillSlopes(inNetwork, inHillslope, pm);
if (inDuffyInput != null) {
List<IHillSlope> duffyHillslopes = new ArrayList<IHillSlope>();
for( IHillSlope hillSlope : orderedHillslopes ) {
IHillSlope newHS = new HillSlopeDuffy(hillSlope, inDuffyInput);
duffyHillslopes.add(newHS);
}
orderedHillslopes = duffyHillslopes;
}
IHillSlope outletHillSlope = orderedHillslopes.get(0);
outletHillslopeId = outletHillSlope.getHillslopeId();
netPfaffsList = new ArrayList<PfafstetterNumber>();
pfaff2Index = new HashMap<String, Integer>();
basinid2Index = new HashMap<Integer, Integer>();
index2Basinid = new HashMap<Integer, Integer>();
pm.beginTask("Analaysing hillslopes and calculating distribution curves...", orderedHillslopes.size());
for( int i = 0; i < orderedHillslopes.size(); i++ ) {
IHillSlope hillSlope = orderedHillslopes.get(i);
PfafstetterNumber pfafstetterNumber = hillSlope.getPfafstetterNumber();
netPfaffsList.add(pfafstetterNumber);
int hillslopeId = hillSlope.getHillslopeId();
basinid2Index.put(hillslopeId, i);
index2Basinid.put(i, hillslopeId);
pfaff2Index.put(pfafstetterNumber.toString(), i);
// the distributor
pm.worked(1);
}
pm.done();
if (pPfafids == null) {
pPfafids = outletHillSlope.getPfafstetterNumber().toString();
}
if (pfaffsList == null) {
String[] split = pPfafids.split(",");
for( int i = 0; i < split.length; i++ ) {
split[i] = split[i].trim();
}
pfaffsList = Arrays.asList(split);
}
if (inDuffyInput != null) {
initialConditions = new double[hillsSlopeNum * 4];
adigeEngine = new DuffyAdigeEngine(orderedHillslopes, inDuffyInput, pm, doLog, initialConditions, basinid2Index,
index2Basinid, pfaffsList, pfaff2Index, outDischarge, outSubdischarge, startTimestamp, endTimestamp,
tTimestep);
} else if (inHymodInput != null) {
initialConditions = null;
adigeEngine = new HymodAdigeEngine(inHymodInput, orderedHillslopes, index2Basinid, outDischarge, outSubdischarge,
pfaffsList, doLog, doLog, pm);
} else {
throw new ModelsIllegalargumentException("No parameters for any model were defined. Check your syntax.", this, pm);
}
if (hydrometersHandler != null) {
adigeEngine.addDischargeContributor(hydrometersHandler);
}
if (damsHandler != null) {
adigeEngine.addDischargeContributor(damsHandler);
}
if (tributaryHandler != null) {
adigeEngine.addDischargeContributor(tributaryHandler);
}
if (offtakesHandler != null) {
adigeEngine.addDischargeContributor(offtakesHandler);
}
} else {
currentTimstamp = currentTimstamp.plusMinutes(tTimestep);
}
if (inHydrometerdata != null) {
hydrometersHandler.setCurrentData(inHydrometerdata);
}
if (inDamsdata != null) {
damsHandler.setCurrentData(inDamsdata);
}
if (inOfftakesdata != null) {
offtakesHandler.setCurrentData(inOfftakesdata);
}
if (inTributarydata != null) {
tributaryHandler.setCurrentData(inTributarydata);
}
// deal with rain
if (pRainintensity != -1) {
/*
* in the case of constant rain the array is build once and then used every time.
* The only thing that changes, is that after the rainEndDate, the rain intensity is
* set to 0.
*/
rainArray = new double[netPfaffsList.size()];
if (currentTimstamp.isBefore(rainEndTimestamp)) {
Arrays.fill(rainArray, pRainintensity);
} else {
Arrays.fill(rainArray, 0);
}
} else {
// read rainfall from input link scalar set and transform into a rainfall intensity
// [mm/h]
rainArray = new double[hillsSlopeNum];
etpArray = new double[hillsSlopeNum];
setDataArray(inRain, rainArray);
if (inEtp != null) {
setDataArray(inEtp, etpArray);
}
}
// long runningDateInMinutes = currentTimstamp.getMillis() / 1000L / 60L;
// double intervalStartTimeInMinutes = runningDateInMinutes;
// double intervalEndTimeInMinutes = runningDateInMinutes + tTimestep;
initialConditions = adigeEngine.solve(currentTimstamp, tTimestep, 1, initialConditions, rainArray, etpArray);
}
private void linkBasinWithNetwork() throws Exception {
FeatureExtender fExt = new FeatureExtender(inNetwork.getSchema(), new String[]{NetworkChannel.NETNUMNAME},
new Class[]{Integer.class});
DefaultFeatureCollection newCollection = new DefaultFeatureCollection();
SimpleFeatureIterator hillslopeFeatures = inHillslope.features();
while( hillslopeFeatures.hasNext() ) {
SimpleFeature hFeature = hillslopeFeatures.next();
Object netNum = hFeature.getAttribute(NetworkChannel.NETNUMNAME);
Geometry hGeometry = (Geometry) hFeature.getDefaultGeometry();
PreparedGeometry preparedHGeometry = PreparedGeometryFactory.prepare(hGeometry);
SimpleFeatureIterator netFeatures = inNetwork.features();
while( netFeatures.hasNext() ) {
SimpleFeature nFeature = netFeatures.next();
Geometry geometry = (Geometry) nFeature.getDefaultGeometry();
if (geometry.getNumGeometries() != 1) {
throw new ModelsRuntimeException("The network geometries have to be single lines.", this);
}
LineString nLine = (LineString) geometry.getGeometryN(0);
Point startPoint = nLine.getStartPoint();
if (preparedHGeometry.contains(startPoint)) {
SimpleFeature extendFeature = fExt.extendFeature(nFeature, new Object[]{netNum});
newCollection.add(extendFeature);
break;
}
}
}
inNetwork = newCollection;
}
private void prepareMonitoringPoints() {
if (inHydrometers != null || inDams != null || inTributary != null || inOfftakes != null) {
if (fMonpointid == null || fMonpointid.length() < 1) {
throw new ModelsIllegalargumentException("Missing monitoring point id attribute name.", this.getClass()
.getSimpleName(), pm);
}
}
// hydrometers
if (inHydrometers != null && inHydrometerdata != null) {
if (hydrometersHandler == null) {
pm.message("Reading hydrometers geometries and mapping them to the network...");
hydrometer_pfaff2idMap = new HashMap<String, Integer>();
hydrometersHandler = new Hydrometers(hydrometer_pfaff2idMap);
FeatureIterator<SimpleFeature> hydrometersIterator = inHydrometers.features();
while( hydrometersIterator.hasNext() ) {
SimpleFeature hydrometer = hydrometersIterator.next();
String pNumberStr = (String) hydrometer.getAttribute(NetworkChannel.PFAFNAME);
int id = ((Number) hydrometer.getAttribute(fMonpointid)).intValue();
hydrometer_pfaff2idMap.put(pNumberStr, id);
}
}
}
// dams
if (inDams != null && inDamsdata != null) {
if (damsHandler == null) {
pm.message("Reading dams geometries and mapping them to the network...");
dams_pfaff2idMap = new HashMap<String, Integer>();
damsHandler = new Dams(dams_pfaff2idMap);
FeatureIterator<SimpleFeature> damsIterator = inDams.features();
while( damsIterator.hasNext() ) {
SimpleFeature dam = damsIterator.next();
String pNumberStr = (String) dam.getAttribute(NetworkChannel.PFAFNAME);
int id = ((Number) dam.getAttribute(fMonpointid)).intValue();
dams_pfaff2idMap.put(pNumberStr, id);
}
}
}
// tributary
if (inTributary != null && inTributarydata != null) {
if (tributaryHandler == null) {
pm.message("Reading tributary geometries and mapping them to the network...");
tributary_pfaff2idMap = new HashMap<String, Integer>();
tributaryHandler = new Tributaries(tributary_pfaff2idMap);
FeatureIterator<SimpleFeature> tributaryIterator = inTributary.features();
while( tributaryIterator.hasNext() ) {
SimpleFeature tributary = tributaryIterator.next();
String pNumberStr = (String) tributary.getAttribute(NetworkChannel.PFAFNAME);
int id = ((Number) tributary.getAttribute(fMonpointid)).intValue();
tributary_pfaff2idMap.put(pNumberStr, id);
}
}
}
// offtakes
if (inOfftakes != null && inOfftakesdata != null) {
if (offtakesHandler == null) {
pm.message("Reading offtakes geometries and mapping them to the network...");
offtakes_pfaff2idMap = new HashMap<String, Integer>();
offtakesHandler = new Offtakes(offtakes_pfaff2idMap, pm);
FeatureIterator<SimpleFeature> offtakesIterator = inOfftakes.features();
while( offtakesIterator.hasNext() ) {
SimpleFeature offtakes = offtakesIterator.next();
String pNumberStr = (String) offtakes.getAttribute(NetworkChannel.PFAFNAME);
int id = ((Number) offtakes.getAttribute(fMonpointid)).intValue();
offtakes_pfaff2idMap.put(pNumberStr, id);
}
}
}
}
private void setDataArray( HashMap<Integer, double[]> dataMap, double[] endArray ) {
Set<Entry<Integer, double[]>> entries = dataMap.entrySet();
for( Entry<Integer, double[]> entry : entries ) {
Integer id = entry.getKey();
double[] value = entry.getValue();
Integer index = basinid2Index.get(id);
if (index == null) {
continue;
}
if (isNovalue(value[0])) {
value[0] = 0.0;
}
// endArray[index] = value[0] / (tTimestep / 60.0);
endArray[index] = value[0];
}
}
}