Mercurial > dive4elements > river
diff flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java @ 3814:8083f6384023
merged flys-artifacts/pre2.6-2012-01-04
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:56 +0200 |
parents | 368040e5c400 |
children | cbeeaaad1056 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java Fri Sep 28 12:14:56 2012 +0200 @@ -0,0 +1,738 @@ +package de.intevation.flys.artifacts.states; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.LineString; +import com.vividsolutions.jts.geom.Polygon; + +import org.apache.log4j.Logger; + +import org.opengis.feature.simple.SimpleFeature; +import org.opengis.feature.simple.SimpleFeatureType; + +import org.geotools.feature.FeatureCollection; +import org.geotools.feature.FeatureCollections; +import org.geotools.feature.simple.SimpleFeatureBuilder; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.GlobalContext; + +import de.intevation.artifacts.common.utils.FileTools; + +import de.intevation.artifactdatabase.state.Facet; + +import de.intevation.flys.model.CrossSectionTrack; +import de.intevation.flys.model.DGM; +import de.intevation.flys.model.Floodplain; +import de.intevation.flys.model.RiverAxis; + +import de.intevation.flys.artifacts.FLYSArtifact; +import de.intevation.flys.artifacts.context.FLYSContext; +import de.intevation.flys.artifacts.model.CalculationMessage; +import de.intevation.flys.artifacts.model.CalculationResult; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.artifacts.model.WSPLGENCalculation; +import de.intevation.flys.artifacts.model.WSPLGENJob; +import de.intevation.flys.artifacts.model.WSPLGENReportFacet; +import de.intevation.flys.artifacts.resources.Resources; +import de.intevation.flys.artifacts.states.DefaultState.ComputeType; +import de.intevation.flys.exports.WstWriter; +import de.intevation.flys.utils.FLYSUtils; +import de.intevation.flys.utils.MapfileGenerator; +import de.intevation.flys.utils.GeometryUtils; +import de.intevation.flys.wsplgen.FacetCreator; +import de.intevation.flys.wsplgen.JobObserver; +import de.intevation.flys.wsplgen.Scheduler; + + +public class FloodMapState +extends DefaultState +implements FacetTypes +{ + /** The logger that is used in this state. */ + private static Logger logger = Logger.getLogger(FloodMapState.class); + + + public static final String KEEP_ARTIFACT_DIR = + System.getProperty("flys.uesk.keep.artifactsdir", "false"); + + + public static final String WSP_ARTIFACT = "wsp"; + + public static final String WINFO_WSP_STATE_ID = "state.winfo.waterlevel"; + + public static final String WSPLGEN_PARAMETER_FILE = "wsplgen.par"; + public static final String WSPLGEN_BARRIERS_LINES = "barrier_lines.shp"; + public static final String WSPLGEN_BARRIERS_POLY = "barrier_polygons.shp"; + public static final String WSPLGEN_AXIS = "axis.shp"; + public static final String WSPLGEN_QPS = "qps.shp"; + public static final String WSPLGEN_FLOODPLAIN = "talaue.shp"; + public static final String WSPLGEN_WSP_FILE = "waterlevel.wst"; + public static final String WSPLGEN_OUTPUT_FILE = "wsplgen.shp"; + + public static final int WSPLGEN_DEFAULT_OUTPUT = 0; + + + + @Override + public Object computeAdvance( + FLYSArtifact artifact, + String hash, + CallContext context, + List<Facet> facets, + Object old + ) { + logger.debug("FloodMapState.computeAdvance"); + + File artifactDir = getDirectory(artifact); + + if (artifactDir == null) { + logger.error("Could not create directory for WSPLGEN results!"); + return null; + } + + WSPLGENCalculation calculation = new WSPLGENCalculation(); + + FacetCreator facetCreator = new FacetCreator( + artifact, context, hash, getID(), facets); + + WSPLGENJob job = prepareWSPLGENJob( + artifact, + facetCreator, + artifactDir, + context, + calculation); + + CalculationResult res = new CalculationResult(null, calculation); + WSPLGENReportFacet report= new WSPLGENReportFacet( + ComputeType.ADVANCE, hash, getID(), res); + + facets.add(report); + + if (job == null) { + if (KEEP_ARTIFACT_DIR.equals("false")) { + removeDirectory(artifact); + } + + calculation.addError(-1, Resources.getMsg( + context.getMeta(), + "wsplgen.job.error", + "wsplgen.job.error")); + + logger.error("No WSPLGEN processing has been started!"); + + return null; + } + + context.afterCall(CallContext.BACKGROUND); + context.addBackgroundMessage(new CalculationMessage( + JobObserver.STEPS.length, + 0, + Resources.getMsg( + context.getMeta(), + "wsplgen.job.queued", + "wsplgen.job.queued") + )); + + GlobalContext gc = (GlobalContext) context.globalContext(); + Scheduler scheduler = (Scheduler) gc.get(FLYSContext.SCHEDULER); + scheduler.addJob(job); + + return null; + } + + + /** + * Returns (and creates if not existing) the directory for storing WSPLEN + * data for the owner artifact. + * + * @param artifact The owner Artifact. + * + * @return the directory for WSPLEN data. + */ + protected File getDirectory(FLYSArtifact artifact) { + String shapePath = FLYSUtils.getXPathString( + FLYSUtils.XPATH_SHAPEFILE_DIR); + + File artifactDir = FileTools.getDirectory( + shapePath, artifact.identifier()); + + return artifactDir; + } + + + /** + * Removes the directory and all its content where the required data and the + * results of WSPLGEN are stored. Should be called in endOfLife(). + */ + protected void removeDirectory(FLYSArtifact artifact) { + String shapePath = FLYSUtils.getXPathString( + FLYSUtils.XPATH_SHAPEFILE_DIR); + + File artifactDir = new File(shapePath, artifact.identifier()); + + if (artifactDir.exists()) { + logger.info("Delete directory: " + artifactDir.getAbsolutePath()); + boolean success = FileTools.deleteRecursive(artifactDir); + } + else { + logger.debug("There is no directory to remove."); + } + } + + + @Override + public void endOfLife(Artifact artifact, Object callContext) { + logger.info("FloodMapState.endOfLife: " + artifact.identifier()); + + FLYSArtifact flys = (FLYSArtifact) artifact; + removeDirectory(flys); + + Scheduler scheduler = Scheduler.getInstance(); + scheduler.cancelJob(flys.identifier()); + + MapfileGenerator.getInstance().update(); + } + + + protected WSPLGENJob prepareWSPLGENJob( + FLYSArtifact artifact, + FacetCreator facetCreator, + File artifactDir, + CallContext context, + WSPLGENCalculation calculation + ) { + logger.debug("FloodMapState.prepareWSPLGENJob"); + + WSPLGENJob job = new WSPLGENJob( + artifact, + artifactDir, + facetCreator, + context, + calculation); + + File paraFile = new File(artifactDir, WSPLGEN_PARAMETER_FILE); + + setOut(artifact, job); + setRange(artifact, job); + setDelta(artifact, job); + setGel(artifact, job); + setDist(artifact, job); + setLine(artifact, facetCreator, artifactDir, job); + setAxis(artifact, artifactDir, job); + setPro(artifact, artifactDir, job); + setDgm(artifact, job); + setArea(artifact, artifactDir, job); + setOutFile(artifact, job); + setWsp(artifact, context, artifactDir, job); // WSP + + // TODO + // setWspTag(artifact, job); + + try { + job.toFile(paraFile); + + return job; + } + catch (IOException ioe) { + logger.warn("Cannot write PAR file: " + ioe.getMessage()); + } + catch (IllegalArgumentException iae) { + logger.warn("Cannot write PAR file: " + iae.getMessage()); + } + + return null; + } + + + protected void setOut(FLYSArtifact artifact, WSPLGENJob job) { + job.setOut(WSPLGEN_DEFAULT_OUTPUT); + } + + + protected void setRange(FLYSArtifact artifact, WSPLGENJob job) { + double[] range = FLYSUtils.getKmRange(artifact); + + job.setStart(range[0]); + job.setEnd(range[1]); + } + + + protected void setDelta(FLYSArtifact artifact, WSPLGENJob job) { + String from = artifact.getDataAsString("diff_from"); + String to = artifact.getDataAsString("diff_to"); + String diff = artifact.getDataAsString("diff_diff"); + + try { + job.setFrom(Double.parseDouble(from)); + } + catch (NumberFormatException nfe) { + } + + try { + job.setTo(Double.parseDouble(to)); + } + catch (NumberFormatException nfe) { + } + + try { + job.setDiff(Double.parseDouble(diff)); + } + catch (NumberFormatException nfe) { + } + } + + + protected void setGel(FLYSArtifact artifact, WSPLGENJob job) { + String gel = artifact.getDataAsString("scenario"); + + logger.debug("Selected gel = '" + gel + "'"); + + if (gel == null || gel.length() == 0) { + job.setGel(WSPLGENJob.GEL_NOSPERRE); + } + else if (gel.equals("scenario.current")) { + job.setGel(WSPLGENJob.GEL_SPERRE); + } + else if (gel.equals("scenario.scenario")) { + job.setGel(WSPLGENJob.GEL_SPERRE); + } + else { + job.setGel(WSPLGENJob.GEL_NOSPERRE); + } + } + + + protected void setDist(FLYSArtifact artifact, WSPLGENJob job) { + String dist = artifact.getDataAsString("profile_distance"); + + try { + job.setDist(Double.parseDouble(dist)); + } + catch (NumberFormatException nfe) { + // nothing to do here + } + } + + + protected void setLine( + FLYSArtifact artifact, + FacetCreator facetCreator, + File dir, + WSPLGENJob job + ) { + String geoJSON = artifact.getDataAsString("uesk.barriers"); + String srid = FLYSUtils.getRiverSrid(artifact); + String srs = "EPSG:" + srid; + + if (geoJSON == null || geoJSON.length() == 0) { + logger.debug("No barrier features in parameterization existing."); + return; + } + + SimpleFeatureType ft = getBarriersFeatureType( + "barriers", srs, Geometry.class); + + List<SimpleFeature> features = GeometryUtils.parseGeoJSON(geoJSON, ft); + if (features == null || features.size() == 0) { + logger.debug("No barrier features extracted."); + return; + } + + FeatureCollection[] fcs = splitLinesAndPolygons(features); + + File shapeLines = new File(dir, WSPLGEN_BARRIERS_LINES); + File shapePolys = new File(dir, WSPLGEN_BARRIERS_POLY); + + Object[][] obj = new Object[][] { + new Object[] { "typ", String.class } + }; + + String scenario = job.getGel(); + + boolean l = GeometryUtils.writeShapefile( + shapeLines, + GeometryUtils.buildFeatureType("lines", srs, LineString.class, obj), + fcs[0]); + + if (l) { + logger.debug( + "Successfully created barrier line shapefile. " + + "Write shapefile path into WSPLGEN job."); + + if (scenario.equals(WSPLGENJob.GEL_NOSPERRE)) { + logger.debug("WSPLGEN will not use barrier features."); + } + else { + job.addLin(shapeLines.getAbsolutePath()); + } + } + + boolean p = GeometryUtils.writeShapefile( + shapePolys, + GeometryUtils.buildFeatureType("polygons", srs, Polygon.class, obj), + fcs[1]); + + if (p) { + logger.debug( + "Successfully created barrier polygon shapefile. " + + "Write shapefile path into WSPLGEN job."); + + if (scenario.equals(WSPLGENJob.GEL_NOSPERRE)) { + logger.debug("WSPLGEN will not use barrier features."); + } + else { + job.addLin(shapePolys.getAbsolutePath()); + } + } + + if (p || l) { + facetCreator.createBarrierFacet(); + } + } + + + protected SimpleFeatureType getBarriersFeatureType( + String name, + String srs, + Class type + ) { + Object[][] attrs = new Object[3][]; + attrs[0] = new Object[] { "typ", String.class }; + attrs[1] = new Object[] { "elevation", Double.class }; + attrs[2] = new Object[] { "mark.selected", Integer.class }; + + return GeometryUtils.buildFeatureType(name, srs, type, attrs); + } + + + protected FeatureCollection[] splitLinesAndPolygons(List<SimpleFeature> f) { + FeatureCollection lines = FeatureCollections.newCollection(); + FeatureCollection polygons = FeatureCollections.newCollection(); + + for (SimpleFeature feature: f) { + Geometry geom = (Geometry) feature.getDefaultGeometry(); + + + if (geom instanceof LineString) { + geom = applyElevationAttribute(feature, (LineString) geom); + lines.add(feature); + } + else if (geom instanceof Polygon) { + geom = applyElevationAttribute(feature, (Polygon) geom); + polygons.add(feature); + } + else { + logger.warn("Feature not supported: " + geom.getClass()); + } + } + + logger.debug("Found " + lines.size() + " barrier lines."); + logger.debug("Found " + polygons.size() + " barrier polygons."); + + return new FeatureCollection[] { lines, polygons }; + } + + + protected static Geometry applyElevationAttribute( + SimpleFeature feature, + Geometry geom + ) { + logger.debug("Apply elevations for: " + geom.getClass()); + + List<Double> elevations = extractElevations(feature); + int numPoints = geom.getNumPoints(); + int numElevation = elevations.size(); + + String typ = (String) feature.getAttribute("typ"); + + if (numPoints > numElevation) { + logger.warn("More vertices in Geometry than elevations given."); + } + + Coordinate[] c = geom.getCoordinates(); + for (int i = 0; i < numPoints; i++) { + if (i < numElevation) { + c[i].z = elevations.get(i); + } + else if (typ != null && typ.equals("Graben")) { + c[i].z = -9999d; + } + else { + c[i].z = 9999d; + } + } + + return geom; + } + + + protected static List<Double> extractElevations(SimpleFeature feature) { + String tmp = (String) feature.getAttribute("elevation"); + String typ = (String) feature.getAttribute("typ"); + + String[] elevations = tmp == null ? null : tmp.split(" "); + + int num = elevations != null ? elevations.length : 0; + + List<Double> list = new ArrayList<Double>(num); + + for (int i = 0; i < num; i++) { + try { + list.add(Double.parseDouble(elevations[i])); + } + catch (NumberFormatException nfe) { + logger.warn("Error while parsing elevation at pos: " + i); + if (typ != null && typ.equals("Graben")) { + list.add(new Double(-9999.0)); + } + else { + list.add(new Double(9999.0)); + } + } + } + + return list; + } + + + protected void setAxis(FLYSArtifact artifact, File dir, WSPLGENJob job) { + String river = artifact.getDataAsString("river"); + String srid = FLYSUtils.getRiverSrid(artifact); + String srs = "EPSG:" + srid; + + RiverAxis axis = RiverAxis.getRiverAxis(river); + if (axis == null) { + logger.warn("Could not find river axis for: '" + river + "'"); + return; + } + + Geometry geom = axis.getGeom(); + + SimpleFeatureType ft = GeometryUtils.buildFeatureType( + "axis", srs, LineString.class); + + SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft); + builder.add(geom); + + FeatureCollection collection = FeatureCollections.newCollection(); + collection.add(builder.buildFeature("0")); + + File axisShape = new File(dir, WSPLGEN_AXIS); + + boolean a = GeometryUtils.writeShapefile( + axisShape, + GeometryUtils.buildFeatureType("axis", srs, LineString.class), + collection); + + if (a) { + job.setAxis(axisShape.getAbsolutePath()); + } + } + + + protected void setPro(FLYSArtifact artifact, File dir, WSPLGENJob job) { + String river = artifact.getDataAsString("river"); + String srid = FLYSUtils.getRiverSrid(artifact); + String srs = "EPSG:" + srid; + + List<CrossSectionTrack> cst = + CrossSectionTrack.getCrossSectionTrack(river); + + logger.debug("Found " + cst.size() + " CrossSectionTracks."); + + Object[][] attrs = new Object[2][]; + attrs[0] = new Object[] { "ELEVATION", Double.class }; + attrs[1] = new Object[] { "KILOMETER", Double.class }; + + SimpleFeatureType ft = GeometryUtils.buildFeatureType( + "qps", srs, LineString.class, attrs); + + SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft); + FeatureCollection collection = FeatureCollections.newCollection(); + + int i = 0; + for (CrossSectionTrack track: cst) { + builder.reset(); + builder.add(track.getGeom()); + builder.add(track.getZ().doubleValue()); + builder.add(track.getKm().doubleValue()); + + collection.add(builder.buildFeature(String.valueOf(i++))); + } + + File qpsShape = new File(dir, WSPLGEN_QPS); + + boolean q = GeometryUtils.writeShapefile( + qpsShape, + GeometryUtils.buildFeatureType("qps", srs, LineString.class, attrs), + collection); + + if (q) { + job.setPro(qpsShape.getAbsolutePath()); + } + } + + + protected void setDgm(FLYSArtifact artifact, WSPLGENJob job) { + String dgm_id = artifact.getDataAsString("dgm"); + + int id = -1; + try { + id = Integer.parseInt(dgm_id); + } + catch (NumberFormatException nfe) { /* do nothing */ } + + DGM dgm = DGM.getDGM(id); + + if (dgm == null) { + logger.warn("Could not find specified DGM."); + + return; + } + + job.setDgm(dgm.getPath()); + } + + + protected void setArea(FLYSArtifact artifact, File dir, WSPLGENJob job) { + String useFloodplain = artifact.getDataAsString("use_floodplain"); + if (!Boolean.valueOf(useFloodplain)) { + logger.debug("WSPLGEN will not use floodplain."); + return; + } + + String river = artifact.getDataAsString("river"); + String srid = FLYSUtils.getRiverSrid(artifact); + String srs = "EPSG:" + srid; + + Floodplain plain = Floodplain.getFloodplain(river); + + SimpleFeatureType ft = GeometryUtils.buildFeatureType( + "talaue", srs, Polygon.class); + + SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft); + builder.add(plain.getGeom()); + + FeatureCollection collection = FeatureCollections.newCollection(); + collection.add(builder.buildFeature("0")); + + File talaueShape = new File(dir, WSPLGEN_FLOODPLAIN); + + boolean t = GeometryUtils.writeShapefile( + talaueShape, + GeometryUtils.buildFeatureType("talaue", srs, Polygon.class), + collection); + + if (t) { + job.setArea(talaueShape.getAbsolutePath()); + } + } + + + protected void setOutFile(FLYSArtifact artifact, WSPLGENJob job) { + job.setOutFile(WSPLGEN_OUTPUT_FILE); + } + + + protected WQKms getWQKms(FLYSArtifact flys, CallContext cc) { + String wspString = flys.getDataAsString(WSP_ARTIFACT); + String[] parts = wspString.split(";"); + + String otherArtifact = parts[0]; + + int idx = -1; + try { + idx = Integer.parseInt(parts[2]); + } + catch (NumberFormatException nfe) { /* do nothing */ } + + FLYSArtifact src = otherArtifact != null + ? FLYSUtils.getArtifact(otherArtifact, cc) + : flys; + + logger.debug("Use waterlevel provided by Artifact: " + src.identifier()); + + CalculationResult rawData = (CalculationResult) src.compute( + cc, + null, + WINFO_WSP_STATE_ID, + ComputeType.ADVANCE, + false); + + WQKms[] wqkms = (WQKms[]) rawData.getData(); + + return wqkms == null || idx == -1 || idx >= wqkms.length + ? null + : wqkms[idx]; + } + + + protected void setWsp( + FLYSArtifact artifact, + CallContext context, + File dir, + WSPLGENJob job) + { + logger.debug("FloodMapState.setWsp"); + + WQKms data = getWQKms(artifact, context); + + if (data == null) { + logger.warn("No WST data found!"); + return; + } + + WstWriter writer = new WstWriter(1); + + // TODO REMOVE job.setWspTag(...) This is only used until the user is + // able to select the WSP column himself! + boolean writeWspTag = true; + + double[] buf = new double[4]; + logger.debug("Add WST column: " + data.getName()); + writer.addColumn(data.getName()); + + if (writeWspTag) { + job.setWspTag(data.getName()); + writeWspTag = false; + } + + for (int i = 0, num = data.size(); i < num; i++) { + data.get(i, buf); + writer.add(buf); + } + + FileOutputStream fout = null; + + try { + File wspFile = new File(dir, WSPLGEN_WSP_FILE); + fout = new FileOutputStream(wspFile); + + writer.write(fout); + + job.setWsp(wspFile.getAbsolutePath()); + } + catch (FileNotFoundException fnfe) { + logger.warn("Error while writing wsp file: " + fnfe.getMessage()); + } + finally { + if (fout != null) { + try { + fout.close(); + } + catch (IOException ioe) { /* do nothing */ } + } + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :