/* * 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.networktools.epanet; import static java.lang.Math.abs; import static java.lang.Math.pow; import static java.lang.Math.sqrt; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_AUTHORCONTACTS; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_AUTHORNAMES; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_KEYWORDS; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_LABEL; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_LICENSE; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_NAME; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_STATUS; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_inElev_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_inJunctions_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_inPipes_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_inPumps_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_inReservoirs_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_inTanks_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_inValves_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_outWarning_DESCRIPTION; import static org.jgrasstools.hortonmachine.i18n.HortonMessages.OMSEPANETFEATURESSYNCHRONIZER_pTol_DESCRIPTION; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; import java.util.TreeSet; 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 org.geotools.coverage.grid.GridCoverage2D; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.feature.DefaultFeatureCollection; import org.jgrasstools.gears.libs.exceptions.ModelsIllegalargumentException; import org.jgrasstools.gears.libs.modules.JGTModel; import org.jgrasstools.gears.utils.features.FeatureUtilities; import org.jgrasstools.hortonmachine.modules.networktools.epanet.core.EpanetConstants; import org.jgrasstools.hortonmachine.modules.networktools.epanet.core.EpanetFeatureTypes.Junctions; import org.jgrasstools.hortonmachine.modules.networktools.epanet.core.EpanetFeatureTypes.Pipes; import org.jgrasstools.hortonmachine.modules.networktools.epanet.core.EpanetFeatureTypes.Pumps; import org.jgrasstools.hortonmachine.modules.networktools.epanet.core.EpanetFeatureTypes.Reservoirs; import org.jgrasstools.hortonmachine.modules.networktools.epanet.core.EpanetFeatureTypes.Tanks; import org.jgrasstools.hortonmachine.modules.networktools.epanet.core.EpanetFeatureTypes.Valves; import org.opengis.feature.simple.SimpleFeature; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; @Description(OMSEPANETFEATURESSYNCHRONIZER_DESCRIPTION) @Author(name = OMSEPANETFEATURESSYNCHRONIZER_AUTHORNAMES, contact = OMSEPANETFEATURESSYNCHRONIZER_AUTHORCONTACTS) @Keywords(OMSEPANETFEATURESSYNCHRONIZER_KEYWORDS) @Label(OMSEPANETFEATURESSYNCHRONIZER_LABEL) @Name(OMSEPANETFEATURESSYNCHRONIZER_NAME) @Status(OMSEPANETFEATURESSYNCHRONIZER_STATUS) @License(OMSEPANETFEATURESSYNCHRONIZER_LICENSE) public class OmsEpanetFeaturesSynchronizer extends JGTModel { @Description(OMSEPANETFEATURESSYNCHRONIZER_inJunctions_DESCRIPTION) @In @Out public SimpleFeatureCollection inJunctions = null; @Description(OMSEPANETFEATURESSYNCHRONIZER_inTanks_DESCRIPTION) @In @Out public SimpleFeatureCollection inTanks = null; @Description(OMSEPANETFEATURESSYNCHRONIZER_inReservoirs_DESCRIPTION) @In @Out public SimpleFeatureCollection inReservoirs = null; @Description(OMSEPANETFEATURESSYNCHRONIZER_inPumps_DESCRIPTION) @In @Out public SimpleFeatureCollection inPumps = null; @Description(OMSEPANETFEATURESSYNCHRONIZER_inValves_DESCRIPTION) @In @Out public SimpleFeatureCollection inValves = null; @Description(OMSEPANETFEATURESSYNCHRONIZER_inPipes_DESCRIPTION) @In @Out public SimpleFeatureCollection inPipes = null; @Description(OMSEPANETFEATURESSYNCHRONIZER_inElev_DESCRIPTION) @In public GridCoverage2D inElev = null; @Description(OMSEPANETFEATURESSYNCHRONIZER_pTol_DESCRIPTION) @In public double pTol = 0.0001; @Description(OMSEPANETFEATURESSYNCHRONIZER_outWarning_DESCRIPTION) @Out public String outWarning = ""; private StringBuilder warningBuilder = new StringBuilder(); private String junctionElevatioAttributeName; private String tanksElevationAttributeName; private String reservoirHeadAttributeName; private String pipesStartNodeAttributeName; private String pipesEndNodeAttributeName; private String pipesIdAttributeName; private String lengthAttributeName; private String pumpsStartNodeAttributeName; private String pumpsEndNodeAttributeName; private String pumpsIdAttributeName; private String valvesStartNodeAttributeName; private String valvesEndNodeAttributeName; private String valvesIdAttributeName; @Execute @SuppressWarnings("unchecked") public void process() throws Exception { checkNull(inJunctions, inPipes); List<SimpleFeature> junctionsList = toList(inJunctions); List<SimpleFeature> pipesList = toList(inPipes); if (junctionsList.size() == 0 || pipesList.size() == 0) { throw new ModelsIllegalargumentException("For the module to run pipes and junctions have to be available.", this); } List<SimpleFeature> tanksList = new ArrayList<SimpleFeature>(); if (inTanks != null) tanksList = toList(inTanks); if (tanksList.size() == 0) { inTanks = null; } List<SimpleFeature> reservoirsList = new ArrayList<SimpleFeature>(); if (inReservoirs != null) reservoirsList = toList(inReservoirs); if (reservoirsList.size() == 0) { inReservoirs = null; } List<SimpleFeature> pumpsList = new ArrayList<SimpleFeature>(); if (inPumps != null) pumpsList = toList(inPumps); if (pumpsList.size() == 0) { inPumps = null; } List<SimpleFeature> valvesList = new ArrayList<SimpleFeature>(); if (inValves != null) valvesList = toList(inValves); if (valvesList.size() == 0) { inValves = null; } /* * check field names */ junctionElevatioAttributeName = FeatureUtilities.findAttributeName(inJunctions.getSchema(), Junctions.ELEVATION.getAttributeName()); String junctionIDAttributeName = FeatureUtilities.findAttributeName(inJunctions.getSchema(), Junctions.ID.getAttributeName()); pipesStartNodeAttributeName = FeatureUtilities.findAttributeName(inPipes.getSchema(), Pipes.START_NODE.getAttributeName()); pipesEndNodeAttributeName = FeatureUtilities.findAttributeName(inPipes.getSchema(), Pipes.END_NODE.getAttributeName()); pipesIdAttributeName = FeatureUtilities.findAttributeName(inPipes.getSchema(), Pipes.ID.getAttributeName()); lengthAttributeName = FeatureUtilities.findAttributeName(inPipes.getSchema(), Pipes.LENGTH.getAttributeName()); String tanksIDAttributeName = null; if (inTanks != null) { tanksElevationAttributeName = FeatureUtilities.findAttributeName(inTanks.getSchema(), Tanks.BOTTOM_ELEVATION.getAttributeName()); tanksIDAttributeName = FeatureUtilities.findAttributeName(inTanks.getSchema(), Tanks.ID.getAttributeName()); } String reservoirIDAttributeName = null; if (inReservoirs != null) { reservoirHeadAttributeName = FeatureUtilities.findAttributeName(inReservoirs.getSchema(), Reservoirs.HEAD.getAttributeName()); reservoirIDAttributeName = FeatureUtilities.findAttributeName(inReservoirs.getSchema(), Reservoirs.ID.getAttributeName()); } if (inPumps != null) { pumpsStartNodeAttributeName = FeatureUtilities.findAttributeName(inPumps.getSchema(), Pumps.START_NODE.getAttributeName()); pumpsEndNodeAttributeName = FeatureUtilities.findAttributeName(inPumps.getSchema(), Pumps.END_NODE.getAttributeName()); pumpsIdAttributeName = FeatureUtilities.findAttributeName(inPumps.getSchema(), Pumps.ID.getAttributeName()); } if (inValves != null) { valvesStartNodeAttributeName = FeatureUtilities.findAttributeName(inValves.getSchema(), Valves.START_NODE.getAttributeName()); valvesEndNodeAttributeName = FeatureUtilities.findAttributeName(inValves.getSchema(), Valves.END_NODE.getAttributeName()); valvesIdAttributeName = FeatureUtilities.findAttributeName(inValves.getSchema(), Valves.ID.getAttributeName()); } /* * check that no ids are double */ checkIds(junctionsList, junctionIDAttributeName, "Found two junctions with the same ID. Check your data."); checkIds(pipesList, pipesIdAttributeName, "Found two pipes with the same ID. Check your data."); if (inPumps != null) checkIds(pumpsList, pumpsIdAttributeName, "Found two pumpes with the same ID. Check your data."); if (inTanks != null) checkIds(tanksList, tanksIDAttributeName, "Found two tanks with the same ID. Check your data."); if (inValves != null) checkIds(valvesList, valvesIdAttributeName, "Found two valves with the same ID. Check your data."); if (inReservoirs != null) checkIds(reservoirsList, reservoirIDAttributeName, "Found two reservoirs with the same ID. Check your data."); /* * elevations for junctions and tanks on dem */ if (inElev != null) { pm.beginTask("Extracting elevations from dem...", junctionsList.size() + tanksList.size() + reservoirsList.size()); inJunctions = new DefaultFeatureCollection(); for( SimpleFeature junction : junctionsList ) { Geometry geometry = (Geometry) junction.getDefaultGeometry(); Coordinate coordinate = geometry.getCoordinate(); double[] dest = new double[]{-9999.0}; try { inElev.evaluate(new Point2D.Double(coordinate.x, coordinate.y), dest); junction.setAttribute(junctionElevatioAttributeName, dest[0]); } catch (Exception e) { appendWarning("No elevation available for junction: ", (String) junction.getAttribute(junctionElevatioAttributeName)); } ((DefaultFeatureCollection) inJunctions).add(junction); pm.worked(1); } inTanks = new DefaultFeatureCollection(); for( SimpleFeature tank : tanksList ) { Geometry geometry = (Geometry) tank.getDefaultGeometry(); Coordinate coordinate = geometry.getCoordinate(); double[] dest = new double[]{-9999.0}; try { inElev.evaluate(new Point2D.Double(coordinate.x, coordinate.y), dest); tank.setAttribute(tanksElevationAttributeName, dest[0]); } catch (Exception e) { appendWarning("No elevation available for tank: ", (String) tank.getAttribute(tanksElevationAttributeName)); } ((DefaultFeatureCollection) inTanks).add(tank); pm.worked(1); } inReservoirs = new DefaultFeatureCollection(); for( SimpleFeature reservoir : reservoirsList ) { Geometry geometry = (Geometry) reservoir.getDefaultGeometry(); Coordinate coordinate = geometry.getCoordinate(); double[] dest = new double[]{-9999.0}; try { inElev.evaluate(new Point2D.Double(coordinate.x, coordinate.y), dest); reservoir.setAttribute(reservoirHeadAttributeName, dest[0]); } catch (Exception e) { appendWarning("No elevation available for reservoir: ", (String) reservoir.getAttribute(reservoirHeadAttributeName)); } ((DefaultFeatureCollection) inReservoirs).add(reservoir); pm.worked(1); } pm.done(); } /* * handle pipes and links to the junctions-tanks-reservoirs */ pm.beginTask("Extracting pipe-nodes links...", pipesList.size()); for( SimpleFeature pipe : pipesList ) { Geometry geometry = (Geometry) pipe.getDefaultGeometry(); Coordinate[] coordinates = geometry.getCoordinates(); if (coordinates.length < 2) { Object attribute = FeatureUtilities.getAttributeCaseChecked(pipe, Pipes.ID.getAttributeName()); appendWarning("Found pipe with less than 2 coordinates: ", attribute.toString()); continue; } Coordinate first = coordinates[0]; Coordinate last = coordinates[coordinates.length - 1]; SimpleFeature nearestFirst = findWithinTolerance(first, junctionsList, tanksList, reservoirsList); if (nearestFirst != null) { Object attribute = FeatureUtilities.getAttributeCaseChecked(nearestFirst, Junctions.ID.getAttributeName()); pipe.setAttribute(pipesStartNodeAttributeName, attribute); } else { Object attribute = pipe.getAttribute(pipesIdAttributeName); appendWarning("No start node found for pipe: ", attribute.toString()); } SimpleFeature nearestLast = findWithinTolerance(last, junctionsList, tanksList, reservoirsList); if (nearestLast != null) { Object attribute = FeatureUtilities.getAttributeCaseChecked(nearestLast, Junctions.ID.getAttributeName()); pipe.setAttribute(pipesEndNodeAttributeName, attribute); } else { Object attribute = pipe.getAttribute(pipesIdAttributeName); appendWarning("No end node found for pipe: ", attribute.toString()); } if (nearestFirst != null && nearestLast != null) { Object elev1Obj = getElevation(nearestFirst); Object elev2Obj = getElevation(nearestLast); double length = geometry.getLength(); if (elev1Obj != null && elev2Obj != null) { if (elev1Obj instanceof Double) { double elev1 = (Double) elev1Obj; double elev2 = (Double) elev2Obj; double length3d = sqrt(pow(abs(elev2 - elev1), 2.0) + pow(length, 2.0)); pipe.setAttribute(lengthAttributeName, length3d); } } else { // 2D pipe.setAttribute(lengthAttributeName, length); } } pm.worked(1); } pm.done(); int dummyIndex = 0; /* * handle pumps */ pm.beginTask("Extracting pumps attributes...", pumpsList.size()); inPumps = new DefaultFeatureCollection(); for( SimpleFeature pump : pumpsList ) { Geometry geometry = (Geometry) pump.getDefaultGeometry(); Geometry buffer = geometry.buffer(pTol); boolean gotIt = false; for( SimpleFeature pipe : pipesList ) { Geometry pipeGeom = (Geometry) pipe.getDefaultGeometry(); if (pipeGeom.intersects(buffer)) { // pump is on pipe Object startNode = pipe.getAttribute(pipesStartNodeAttributeName); pump.setAttribute(pumpsStartNodeAttributeName, startNode); Object endNode = pipe.getAttribute(pipesEndNodeAttributeName); pump.setAttribute(pumpsEndNodeAttributeName, endNode); String dummy = EpanetConstants.DUMMYPIPE.toString() + dummyIndex++; pipe.setAttribute(pipesIdAttributeName, dummy); gotIt = true; } } if (!gotIt) { appendWarning("Pump ", (String) pump.getAttribute(pumpsIdAttributeName), " could not be placed on any pipe"); } ((DefaultFeatureCollection) inPumps).add(pump); pm.worked(1); } pm.done(); /* * handle valves */ pm.beginTask("Extracting valves attributes...", valvesList.size()); inValves = new DefaultFeatureCollection(); for( SimpleFeature valve : valvesList ) { Geometry geometry = (Geometry) valve.getDefaultGeometry(); Geometry buffer = geometry.buffer(pTol); boolean gotIt = false; for( SimpleFeature pipe : pipesList ) { Geometry pipeGeom = (Geometry) pipe.getDefaultGeometry(); if (pipeGeom.intersects(buffer)) { // pump is on pipe Object startNode = pipe.getAttribute(pipesStartNodeAttributeName); valve.setAttribute(valvesStartNodeAttributeName, startNode); Object endNode = pipe.getAttribute(pipesEndNodeAttributeName); valve.setAttribute(valvesEndNodeAttributeName, endNode); // mark pipe as dummy String dummy = EpanetConstants.DUMMYPIPE.toString() + dummyIndex++; pipe.setAttribute(pipesIdAttributeName, dummy); gotIt = true; } } if (!gotIt) { appendWarning("Valve ", (String) valve.getAttribute(valvesIdAttributeName), " could not be placed on any pipe"); } ((DefaultFeatureCollection) inValves).add(valve); pm.worked(1); } pm.done(); inPipes = new DefaultFeatureCollection(); for( SimpleFeature pipe : pipesList ) { ((DefaultFeatureCollection) inPipes).add(pipe); } outWarning = warningBuilder.toString(); } private void checkIds( List<SimpleFeature> featureList, String attributesName, String msg ) { TreeSet<Object> checkTree = new TreeSet<Object>(); for( SimpleFeature sF : featureList ) { Object id = sF.getAttribute(attributesName); if (!checkTree.add(id)) { throw new ModelsIllegalargumentException(msg + "(" + id + ")", this, pm); } } } private Object getElevation( SimpleFeature nearestFirst ) { Object elevObj = nearestFirst.getAttribute(junctionElevatioAttributeName); if (elevObj == null) { // try tank elevObj = nearestFirst.getAttribute(tanksElevationAttributeName); } if (elevObj == null) { // try elevObj = nearestFirst.getAttribute(reservoirHeadAttributeName); } return elevObj; } private List<SimpleFeature> toList( SimpleFeatureCollection fc ) { List<SimpleFeature> list = new ArrayList<SimpleFeature>(); if (fc != null) list = FeatureUtilities.featureCollectionToList(fc); return list; } private SimpleFeature findWithinTolerance( Coordinate c, List<SimpleFeature>... nodesLists ) { for( List<SimpleFeature> nodeList : nodesLists ) { for( SimpleFeature node : nodeList ) { Geometry geometry = (Geometry) node.getDefaultGeometry(); Coordinate coord = geometry.getCoordinate(); if (coord.distance(c) <= pTol) { return node; } } } return null; } private void appendWarning( String... msgs ) { for( String msg : msgs ) { warningBuilder.append(msg); } warningBuilder.append("\n"); } }