/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2014, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * 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 * Lesser General Public License for more details. */ package org.geotools.process.spatialstatistics.transformation; import java.util.NoSuchElementException; import java.util.logging.Logger; import org.geotools.data.collection.ListFeatureCollection; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.feature.collection.SubFeatureCollection; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.geometry.jts.JTSFactoryFinder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.process.spatialstatistics.core.DataUtils; import org.geotools.process.spatialstatistics.core.FeatureTypes; import org.geotools.process.spatialstatistics.core.StatisticsVisitor; import org.geotools.process.spatialstatistics.core.StatisticsVisitorResult; import org.geotools.util.logging.Logging; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.filter.Filter; import org.opengis.filter.expression.Expression; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateList; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; /** * Windrose SimpleFeatureCollection Implementation * * @author Minpa Lee, MangoSystem * * @source $URL$ */ public class WindroseFeatureCollection extends GXTSimpleFeatureCollection { protected static final Logger LOGGER = Logging.getLogger(WindroseFeatureCollection.class); static final int SEG = 32; static String[] FIELDS = { "uid", "count", "min", "max", "sum", "mean", "std_dev", "var" }; final static GeometryFactory gf = JTSFactoryFinder.getGeometryFactory(null); private Expression weightExp; private Point center; private double radius; private SimpleFeatureType schema; public WindroseFeatureCollection(SimpleFeatureCollection delegate, String weightField, Point center) { super(delegate); if (weightField == null || weightField.isEmpty()) { this.weightExp = ff.literal(1); } else { this.weightExp = ff.property(weightField); } ReferencedEnvelope bounds = delegate.getBounds(); double xF = Math.pow(bounds.getMaxX() - bounds.getMinX(), 2.0); double yF = Math.pow(bounds.getMaxY() - bounds.getMinY(), 2.0); this.radius = (Math.pow(xF + yF, 0.5)) / 1.98; this.center = center; if (center == null) { this.center = new GeometryFactory().createPoint(bounds.centre()); } // create schema CoordinateReferenceSystem crs = delegate.getSchema().getCoordinateReferenceSystem(); schema = FeatureTypes.getDefaultType("windrose", Polygon.class, crs); schema = FeatureTypes.add(schema, FIELDS[0], Integer.class, 38); schema = FeatureTypes.add(schema, FIELDS[1], Integer.class, 38); for (int i = 2; i < FIELDS.length; i++) { schema = FeatureTypes.add(schema, FIELDS[i], Double.class, 38); } } @Override public SimpleFeatureIterator features() { return new WindroseFeatureIterator(delegate, getSchema(), weightExp, center, radius); } @Override public SimpleFeatureType getSchema() { return schema; } @Override public SimpleFeatureCollection subCollection(Filter filter) { if (filter == Filter.INCLUDE) { return this; } return new SubFeatureCollection(this, filter); } @Override public ReferencedEnvelope getBounds() { return delegate.getBounds(); } @Override public int size() { return SEG; } static class WindroseFeatureIterator implements SimpleFeatureIterator { private SimpleFeatureCollection delegate; private Expression weightExp; private Coordinate center; private double radius; private SimpleFeatureIterator iter; public WindroseFeatureIterator(SimpleFeatureCollection delegate, SimpleFeatureType schema, Expression weightExp, Point center, double radius) { // use SpatialIndexFeatureCollection this.delegate = DataUtils.toSpatialIndexFeatureCollection(delegate); this.weightExp = weightExp; this.center = center.getCoordinate(); this.radius = radius; this.init(new SimpleFeatureBuilder(schema)); } private void init(SimpleFeatureBuilder builder) { String the_geom = delegate.getSchema().getGeometryDescriptor().getLocalName(); String typeName = builder.getFeatureType().getTypeName(); ListFeatureCollection result = new ListFeatureCollection(builder.getFeatureType()); double minValue = Double.MAX_VALUE; double maxValue = Double.MIN_VALUE; double stepAngle = 360.0 / SEG; double halfStep = stepAngle / 2.0; // pre process for (int index = 0; index < SEG; index++) { double startDeg = (index * stepAngle) - halfStep; double endDeg = ((index + 1) * stepAngle) - halfStep; Geometry cell = createCell(center, startDeg, endDeg, radius); StatisticsVisitor visitor = new StatisticsVisitor(weightExp, null); visitor.visit(delegate.subCollection(getIntersectsFilter(the_geom, cell))); StatisticsVisitorResult ret = visitor.getResult(); // { "uid", "count", "min", "max", "sum", "mean", "std_dev", "var" }; SimpleFeature feature = builder.buildFeature(buildID(typeName, index)); feature.setDefaultGeometry(cell); feature.setAttribute(FIELDS[0], index); feature.setAttribute(FIELDS[1], ret.getCount()); if (ret.getCount() == 0) { feature.setAttribute(FIELDS[2], 0.0); feature.setAttribute(FIELDS[3], 0.0); feature.setAttribute(FIELDS[4], 0.0); feature.setAttribute(FIELDS[5], 0.0); feature.setAttribute(FIELDS[6], 0.0); feature.setAttribute(FIELDS[7], 0.0); } else { feature.setAttribute(FIELDS[2], ret.getMinimum()); feature.setAttribute(FIELDS[3], ret.getMaximum()); feature.setAttribute(FIELDS[4], ret.getSum()); feature.setAttribute(FIELDS[5], ret.getMean()); feature.setAttribute(FIELDS[6], ret.getStandardDeviation()); feature.setAttribute(FIELDS[7], ret.getVariance()); } minValue = Math.min(minValue, ret.getSum()); maxValue = Math.max(maxValue, ret.getSum()); result.add(feature); } // post process SimpleFeatureIterator featureIter = result.features(); try { double diff = maxValue - minValue; while (featureIter.hasNext()) { SimpleFeature feature = featureIter.next(); int index = (Integer) feature.getAttribute(FIELDS[0]); double value = (Double) feature.getAttribute(FIELDS[4]); double adjustedRadius = ((value - minValue) / diff) * radius; if (adjustedRadius == 0) { adjustedRadius = radius * 0.001; } double startDeg = (index * stepAngle) - halfStep; double endDeg = ((index + 1) * stepAngle) - halfStep; feature.setDefaultGeometry(createCell(center, startDeg, endDeg, adjustedRadius)); } } finally { featureIter.close(); } this.iter = result.features(); } public void close() { this.iter.close(); } public boolean hasNext() { return this.iter.hasNext(); } public SimpleFeature next() throws NoSuchElementException { return this.iter.next(); } private Geometry createCell(Coordinate centroid, double from_deg, double to_deg, double radius) { CoordinateList coordinates = new CoordinateList(); coordinates.add(centroid, false); double step = Math.abs(to_deg - from_deg) / SEG; for (int i = 0; i <= SEG; i++) { double radian = Math.toRadians(from_deg + (i * step)); coordinates.add(createPoint(centroid, radian, radius), false); } coordinates.add(centroid, false); return gf.createPolygon(coordinates.toCoordinateArray()); } private Coordinate createPoint(Coordinate centroid, double radian, double radius) { double dx = Math.cos(radian) * radius; double dy = Math.sin(radian) * radius; return new Coordinate(centroid.x + dx, centroid.y + dy); } } }