/* * This file is part of JGrasstools (http://www.jgrasstools.org) * (C) HydroloGIS - www.hydrologis.com * * JGrasstools is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.jgrasstools.gears.utils.features; import java.awt.geom.AffineTransform; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.Serializable; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.media.jai.JAI; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.iterator.RandomIter; import org.geotools.coverage.grid.GridCoordinates2D; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.data.DataUtilities; import org.geotools.data.DefaultTransaction; import org.geotools.data.Transaction; import org.geotools.data.shapefile.ShapefileDataStore; import org.geotools.data.shapefile.ShapefileDataStoreFactory; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.data.simple.SimpleFeatureStore; import org.geotools.feature.DefaultFeatureCollection; import org.geotools.feature.FeatureCollection; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.gce.grassraster.JGrassConstants; import org.geotools.geometry.DirectPosition2D; import org.geotools.geometry.Envelope2D; import org.jaitools.media.jai.vectorize.VectorizeDescriptor; import org.jgrasstools.dbs.spatialite.QueryResult; import org.jgrasstools.gears.libs.exceptions.ModelsIOException; import org.jgrasstools.gears.libs.monitor.IJGTProgressMonitor; import org.jgrasstools.gears.utils.RegionMap; import org.jgrasstools.gears.utils.coverage.CoverageUtilities; import org.jgrasstools.gears.utils.geometry.EGeometryType; import org.jgrasstools.gears.utils.geometry.GeometryUtilities; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.metadata.spatial.PixelOrientation; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateList; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.prep.PreparedGeometry; import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory; import com.vividsolutions.jts.geom.util.AffineTransformation; import com.vividsolutions.jts.index.strtree.STRtree; public class FeatureUtilities { /** * Order the geometries of a list to be all directed in the same direction * * @param geometryList the list of geometries to be ordered * @param thresHold a scalar value that defines the max distance between two points to be the * same * @return a list of ordered coordinates */ @SuppressWarnings("unchecked") public static CoordinateList orderLineGeometries( List<Geometry> geometryList, double thresHold ) { /* * first search the feature that is one of the two external points */ Geometry firstFeature = null; boolean foundFirst = true; boolean foundSecond = true; for( Geometry feature : geometryList ) { foundFirst = true; foundSecond = true; Coordinate[] coords = feature.getCoordinates(); Coordinate first = coords[0]; Coordinate last = coords[coords.length - 1]; for( Geometry compareFeature : geometryList ) { if (compareFeature.equals(feature)) continue; Coordinate[] compareCoords = compareFeature.getCoordinates(); Coordinate comparefirst = compareCoords[0]; Coordinate comparelast = compareCoords[compareCoords.length - 1]; /* * check if the next point is far away */ if (first.distance(comparefirst) < thresHold || first.distance(comparelast) < thresHold) { foundFirst = false; } if (last.distance(comparefirst) < thresHold || last.distance(comparelast) < thresHold) { foundSecond = false; } } if (foundFirst || foundSecond) { firstFeature = feature; break; } } if (firstFeature == null) { throw new RuntimeException(); } CoordinateList coordinateList = new CoordinateList(); Coordinate[] coords = firstFeature.getCoordinates(); if (foundSecond) { for( int i = 0; i < coords.length; i++ ) { coordinateList.add(coords[coords.length - i - 1]); } } else { for( int i = 0; i < coords.length; i++ ) { coordinateList.add(coords[i]); } } // if (foundFirst) { // addCoordsInProperDirection(foundFirst, coordinateList, coords, true, // 0); // }else{ // addCoordsInProperDirection(foundSecond, coordinateList, coords, true, // 0); // } geometryList.remove(firstFeature); Coordinate currentCoordinate = coordinateList.getCoordinate(coordinateList.size() - 1); while( geometryList.size() != 0 ) { for( int j = 0; j < geometryList.size(); j++ ) { System.out.println(j); Geometry compareGeom = geometryList.get(j); Coordinate[] compareCoords = compareGeom.getCoordinates(); Coordinate comparefirst = compareCoords[0]; Coordinate comparelast = compareCoords[compareCoords.length - 1]; // System.out.println(j + " " // + currentCoordinate.distance(comparefirst) + " " // + currentCoordinate.distance(comparelast)); /* * check if the next point is far away */ if (currentCoordinate.distance(comparefirst) < thresHold) { for( int i = 0; i < compareCoords.length; i++ ) { coordinateList.add(compareCoords[i]); } currentCoordinate = new Coordinate(comparelast); geometryList.remove(compareGeom); break; } else if (currentCoordinate.distance(comparelast) < thresHold) { for( int i = 0; i < compareCoords.length; i++ ) { coordinateList.add(compareCoords[compareCoords.length - i - 1]); } currentCoordinate = new Coordinate(comparefirst); geometryList.remove(compareGeom); break; } } } return coordinateList; } /** * Create a featurecollection from a vector of features * * @param features - the vectore of features * @return the created featurecollection */ public static SimpleFeatureCollection createFeatureCollection( SimpleFeature... features ) { DefaultFeatureCollection fcollection = new DefaultFeatureCollection(); for( SimpleFeature feature : features ) { fcollection.add(feature); } return fcollection; } /** * <p> * Convert a csv file to a FeatureCollection. * <b>This for now supports only point geometries</b>.<br> * For different crs it also performs coor transformation. * </p> * <p> * <b>NOTE: this doesn't support date attributes</b> * </p> * * @param csvFile the csv file. * @param crs the crs to use. * @param fieldsAndTypes the {@link Map} of filed names and {@link JGrassConstants#CSVTYPESARRAY types}. * @param pm progress monitor. * @param separatorthe separator to use, if null, comma is used. * @return the created {@link FeatureCollection} * @throws Exception */ @SuppressWarnings("nls") public static SimpleFeatureCollection csvFileToFeatureCollection( File csvFile, CoordinateReferenceSystem crs, LinkedHashMap<String, Integer> fieldsAndTypesIndex, String separator, IJGTProgressMonitor pm ) throws Exception { GeometryFactory gf = new GeometryFactory(); Map<String, Class< ? >> typesMap = JGrassConstants.CSVTYPESCLASSESMAP; String[] typesArray = JGrassConstants.CSVTYPESARRAY; if (separator == null) { separator = ","; } SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); b.setName("csvimport"); b.setCRS(crs); b.add("the_geom", Point.class); int xIndex = -1; int yIndex = -1; Set<String> fieldNames = fieldsAndTypesIndex.keySet(); String[] fieldNamesArray = (String[]) fieldNames.toArray(new String[fieldNames.size()]); for( int i = 0; i < fieldNamesArray.length; i++ ) { String fieldName = fieldNamesArray[i]; Integer typeIndex = fieldsAndTypesIndex.get(fieldName); if (typeIndex == 0) { xIndex = i; } else if (typeIndex == 1) { yIndex = i; } else { Class< ? > class1 = typesMap.get(typesArray[typeIndex]); b.add(fieldName, class1); } } SimpleFeatureType featureType = b.buildFeatureType(); DefaultFeatureCollection newCollection = new DefaultFeatureCollection(); Collection<Integer> orderedTypeIndexes = fieldsAndTypesIndex.values(); Integer[] orderedTypeIndexesArray = (Integer[]) orderedTypeIndexes.toArray(new Integer[orderedTypeIndexes.size()]); BufferedReader bR = null; try { bR = new BufferedReader(new FileReader(csvFile)); String line = null; int featureId = 0; pm.beginTask("Importing raw data", -1); while( (line = bR.readLine()) != null ) { pm.worked(1); if (line.startsWith("#")) { continue; } SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType); Object[] values = new Object[fieldNames.size() - 1]; String[] lineSplit = line.split(separator); double x = Double.parseDouble(lineSplit[xIndex]); double y = Double.parseDouble(lineSplit[yIndex]); Point point = gf.createPoint(new Coordinate(x, y)); values[0] = point; int objIndex = 1; for( int i = 0; i < lineSplit.length; i++ ) { if (i == xIndex || i == yIndex) { continue; } String value = lineSplit[i]; int typeIndex = orderedTypeIndexesArray[i]; String typeName = typesArray[typeIndex]; if (typeName.equals(typesArray[3])) { values[objIndex] = value; } else if (typeName.equals(typesArray[4])) { values[objIndex] = new Double(value); } else if (typeName.equals(typesArray[5])) { values[objIndex] = new Integer(value); } else { throw new IllegalArgumentException("An undefined value type was found"); } objIndex++; } builder.addAll(values); SimpleFeature feature = builder.buildFeature(featureType.getTypeName() + "." + featureId); featureId++; newCollection.add(feature); } } finally { if (bR != null) bR.close(); } pm.done(); return newCollection; } /** * <p> * The easy way to create a shapefile from attributes and geometries * </p> * <p> * <b>NOTE: this doesn't support date attributes</b> * </p> * * @param shapeFilePath the shapefile name * @param crs the destination crs * @param fet the featurecollection * @throws IOException */ public static boolean collectionToShapeFile( String shapeFilePath, CoordinateReferenceSystem crs, SimpleFeatureCollection fet ) throws IOException { // Create the file you want to write to File file = null; if (shapeFilePath.toLowerCase().endsWith(".shp")) { //$NON-NLS-1$ file = new File(shapeFilePath); } else { file = new File(shapeFilePath + ".shp"); //$NON-NLS-1$ } ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory(); Map<String, Serializable> create = new HashMap<String, Serializable>(); create.put("url", file.toURI().toURL()); ShapefileDataStore newDataStore = (ShapefileDataStore) factory.createNewDataStore(create); newDataStore.createSchema(fet.getSchema()); if (crs != null) newDataStore.forceSchemaCRS(crs); Transaction transaction = new DefaultTransaction(); SimpleFeatureStore featureStore = (SimpleFeatureStore) newDataStore.getFeatureSource(); featureStore.setTransaction(transaction); try { featureStore.addFeatures(fet); transaction.commit(); } catch (Exception problem) { problem.printStackTrace(); transaction.rollback(); } finally { transaction.close(); } return true; } /** * @param name the shapefile name * @param fieldsSpec to create other fields you can use a string like : <br> * "geom:MultiLineString,FieldName:java.lang.Integer" <br> * field name can not be over 10 characters use a ',' between each field <br> * field types can be : java.lang.Integer, java.lang.Long, // java.lang.Double, * java.lang.String or java.util.Date * @return * @throws Exception */ @SuppressWarnings("nls") public static ShapefileDataStore createShapeFileDatastore( String name, String fieldsSpec, CoordinateReferenceSystem crs ) throws Exception { // Create the file you want to write to File file = null; if (name.toLowerCase().endsWith(".shp")) { file = new File(name); } else { file = new File(name + ".shp"); } ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory(); Map<String, Serializable> create = new HashMap<String, Serializable>(); create.put("url", file.toURI().toURL()); ShapefileDataStore myData = (ShapefileDataStore) factory.createNewDataStore(create); // Tell this shapefile what type of data it will store // Shapefile handle only : Point, MultiPoint, MultiLineString, // MultiPolygon SimpleFeatureType featureType = DataUtilities.createType(name, fieldsSpec); // Create the Shapefile (empty at this point) myData.createSchema(featureType); // Tell the DataStore what type of Coordinate Reference System (CRS) // to use myData.forceSchemaCRS(crs); return myData; } /** * Extracts features from a {@link FeatureCollection} into an {@link ArrayList}. * * @param collection the feature collection. * @return the list with the features or an empty list if no features present. */ public static List<SimpleFeature> featureCollectionToList( SimpleFeatureCollection collection ) { List<SimpleFeature> featuresList = new ArrayList<SimpleFeature>(); if (collection == null) { return featuresList; } SimpleFeatureIterator featureIterator = collection.features(); while( featureIterator.hasNext() ) { SimpleFeature feature = featureIterator.next(); featuresList.add(feature); } featureIterator.close(); return featuresList; } /** * Extracts features from a {@link FeatureCollection} into an {@link STRtree}. * * @param collection the feature collection. * @return the tree containing the features. */ public static STRtree featureCollectionToSTRtree( SimpleFeatureCollection collection ) { STRtree tree = new STRtree(); SimpleFeatureIterator featureIterator = collection.features(); while( featureIterator.hasNext() ) { SimpleFeature feature = featureIterator.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); tree.insert(geometry.getEnvelopeInternal(), feature); } featureIterator.close(); return tree; } /** * Extracts features from a {@link FeatureCollection} into an {@link ArrayList} of {@link FeatureMate}s. * * @param collection the feature collection. * @return the list with the features or an empty list if no features present. */ public static List<FeatureMate> featureCollectionToMatesList( SimpleFeatureCollection collection ) { List<FeatureMate> featuresList = new ArrayList<FeatureMate>(); if (collection == null) { return featuresList; } SimpleFeatureIterator featureIterator = collection.features(); while( featureIterator.hasNext() ) { SimpleFeature feature = featureIterator.next(); featuresList.add(new FeatureMate(feature)); } featureIterator.close(); return featuresList; } /** * Extracts features from a {@link FeatureCollection} into an {@link ArrayList} of its geometries. * * @param collection the feature collection. * @param doSubGeoms split the geometries in single geometries (ex. MultiLines in Lines). * @param userDataField if not <code>null</code>, the data in the field are put in the userData * field of the geometry. * @return the list with the geometries or an empty list if no features present. */ public static List<Geometry> featureCollectionToGeometriesList( SimpleFeatureCollection collection, boolean doSubGeoms, String userDataField ) { List<Geometry> geometriesList = new ArrayList<Geometry>(); if (collection == null) { return geometriesList; } SimpleFeatureIterator featureIterator = collection.features(); while( featureIterator.hasNext() ) { SimpleFeature feature = featureIterator.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); if (geometry != null) if (doSubGeoms) { int numGeometries = geometry.getNumGeometries(); for( int i = 0; i < numGeometries; i++ ) { Geometry geometryN = geometry.getGeometryN(i); geometriesList.add(geometryN); if (userDataField != null) { Object attribute = feature.getAttribute(userDataField); geometryN.setUserData(attribute); } } } else { geometriesList.add(geometry); if (userDataField != null) { Object attribute = feature.getAttribute(userDataField); geometry.setUserData(attribute); } } } featureIterator.close(); return geometriesList; } /** * Make a {@link SimpleFeatureCollection} from a set of {@link Geometry}. * * <p>This is a fast utility and adds no attributes.</p> * * @param crs The {@link CoordinateReferenceSystem}. * @param geometries the set of {@link Geometry} to add. * @return the features wrapping the geoms. */ public static SimpleFeatureCollection featureCollectionFromGeometry( CoordinateReferenceSystem crs, Geometry... geometries ) { SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); b.setName("simplegeom"); b.setCRS(crs); DefaultFeatureCollection newCollection = new DefaultFeatureCollection(); EGeometryType geometryType = EGeometryType.forGeometry(geometries[0]); b.add("the_geom", geometryType.getClazz()); Object userData = geometries[0].getUserData(); int userDataSize = 1; if (userData != null) { if (userData instanceof String[]) { String[] string = (String[]) userData; userDataSize = string.length; for( int i = 0; i < userDataSize; i++ ) { b.add("data" + i, String.class); } } else { b.add("userdata", userData.getClass()); } } SimpleFeatureType type = b.buildFeatureType(); SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type); for( Geometry g : geometries ) { Object[] values; if (userData == null) { values = new Object[]{g}; } else { Object tmpUserData = g.getUserData(); if (tmpUserData instanceof String[]) { String[] string = (String[]) tmpUserData; values = new Object[userDataSize + 1]; values[0] = g; for( int i = 0; i < string.length; i++ ) { values[i + 1] = string[i]; } } else { values = new Object[]{g, tmpUserData}; } } builder.addAll(values); SimpleFeature feature = builder.buildFeature(null); newCollection.add(feature); } return newCollection; } /** * Getter for attributes of a feature. * * <p>If the attribute is not found, checks are done in non * case sensitive mode. * * @param feature the feature from which to get the attribute. * @param field the name of the field. * @return the attribute or null if none found. */ public static Object getAttributeCaseChecked( SimpleFeature feature, String field ) { Object attribute = feature.getAttribute(field); if (attribute == null) { attribute = feature.getAttribute(field.toLowerCase()); if (attribute != null) return attribute; attribute = feature.getAttribute(field.toUpperCase()); if (attribute != null) return attribute; // alright, last try, search for it SimpleFeatureType featureType = feature.getFeatureType(); field = findAttributeName(featureType, field); if (field != null) { return feature.getAttribute(field); } } return attribute; } /** * Find the name of an attribute, case insensitive. * * @param featureType the feature type to check. * @param field the case insensitive field name. * @return the real name of the field, or <code>null</code>, if none found. */ public static String findAttributeName( SimpleFeatureType featureType, String field ) { List<AttributeDescriptor> attributeDescriptors = featureType.getAttributeDescriptors(); for( AttributeDescriptor attributeDescriptor : attributeDescriptors ) { String name = attributeDescriptor.getLocalName(); if (name.toLowerCase().equals(field.toLowerCase())) { return name; } } return null; } /** * Create a {@link Polygon} from an {@link Envelope}. * * @param envelope the envelope to convert. * @return the created polygon. */ public static Polygon envelopeToPolygon( Envelope2D envelope ) { double w = envelope.getMinX(); double e = envelope.getMaxX(); double s = envelope.getMinY(); double n = envelope.getMaxY(); Coordinate[] coords = new Coordinate[5]; coords[0] = new Coordinate(w, n); coords[1] = new Coordinate(e, n); coords[2] = new Coordinate(e, s); coords[3] = new Coordinate(w, s); coords[4] = new Coordinate(w, n); GeometryFactory gf = GeometryUtilities.gf(); LinearRing linearRing = gf.createLinearRing(coords); Polygon polygon = gf.createPolygon(linearRing, null); return polygon; } /** * Create a {@link Polygon} from an {@link Envelope}. * * @param envelope the envelope to convert. * @return the created polygon. */ public static Polygon envelopeToPolygon( Envelope envelope ) { double w = envelope.getMinX(); double e = envelope.getMaxX(); double s = envelope.getMinY(); double n = envelope.getMaxY(); Coordinate[] coords = new Coordinate[5]; coords[0] = new Coordinate(w, n); coords[1] = new Coordinate(e, n); coords[2] = new Coordinate(e, s); coords[3] = new Coordinate(w, s); coords[4] = new Coordinate(w, n); GeometryFactory gf = GeometryUtilities.gf(); LinearRing linearRing = gf.createLinearRing(coords); Polygon polygon = gf.createPolygon(linearRing, null); return polygon; } /** * Extracts a list of polygons from the cell bounds of a given {@link GridCoverage2D coverage}. * * <p><b>Note that the cells are added in a rows * and cols order (for each row evaluate each column).</b></p> * * @param coverage the coverage to use. * @return the list of envelope geometries. */ public static List<Polygon> gridcoverageToCellPolygons( GridCoverage2D coverage ) { RegionMap regionMap = CoverageUtilities.getRegionParamsFromGridCoverage(coverage); double west = regionMap.getWest(); double north = regionMap.getNorth(); double xres = regionMap.getXres(); double yres = regionMap.getYres(); int cols = regionMap.getCols(); int rows = regionMap.getRows(); List<Polygon> polygons = new ArrayList<Polygon>(); for( int r = 0; r < rows; r++ ) { for( int c = 0; c < cols; c++ ) { double w = west + xres * c; double e = w + xres; double n = north - yres * r; double s = n - yres; Coordinate[] coords = new Coordinate[5]; coords[0] = new Coordinate(w, n); coords[1] = new Coordinate(e, n); coords[2] = new Coordinate(e, s); coords[3] = new Coordinate(w, s); coords[4] = new Coordinate(w, n); GeometryFactory gf = GeometryUtilities.gf(); LinearRing linearRing = gf.createLinearRing(coords); Polygon polygon = gf.createPolygon(linearRing, null); polygons.add(polygon); } } return polygons; } /** * Helper function to run the Vectorize operation with given parameters and * retrieve the vectors. * * @param src the source {@link GridCoverage2D}. * @param args a {@code Map} of parameter names and values or <code>null</code>. * * @return the generated vectors as JTS Polygons */ @SuppressWarnings("unchecked") public static Collection<Polygon> doVectorize( GridCoverage2D src, Map<String, Object> args ) { if (args == null) { args = new HashMap<String, Object>(); } ParameterBlockJAI pb = new ParameterBlockJAI("Vectorize"); pb.setSource("source0", src.getRenderedImage()); // Set any parameters that were passed in for( Entry<String, Object> e : args.entrySet() ) { pb.setParameter(e.getKey(), e.getValue()); } // Get the desintation image: this is the unmodified source image data // plus a property for the generated vectors RenderedOp dest = JAI.create("Vectorize", pb); // Get the vectors Collection<Polygon> polygons = (Collection<Polygon>) dest.getProperty(VectorizeDescriptor.VECTOR_PROPERTY_NAME); RegionMap regionParams = CoverageUtilities.getRegionParamsFromGridCoverage(src); double xRes = regionParams.getXres(); double yRes = regionParams.getYres(); final AffineTransform mt2D = (AffineTransform) src.getGridGeometry().getGridToCRS2D(PixelOrientation.CENTER); final AffineTransformation jtsTransformation = new AffineTransformation(mt2D.getScaleX(), mt2D.getShearX(), mt2D.getTranslateX() - xRes / 2.0, mt2D.getShearY(), mt2D.getScaleY(), mt2D.getTranslateY() + yRes / 2.0); for( Polygon polygon : polygons ) { polygon.apply(jtsTransformation); } return polygons; } public static SimpleFeature toDummyFeature( Geometry geom, CoordinateReferenceSystem crs ) { SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); b.setName("dummy"); if (crs != null) b.setCRS(crs); b.add("the_geom", geom.getClass()); SimpleFeatureType type = b.buildFeatureType(); SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type); Object[] values = new Object[]{geom}; builder.addAll(values); SimpleFeature feature = builder.buildFeature(null); return feature; } /** * Extracts the positions and values of a polygon mapped on a raster(rasterization). * * <p><b>Bound checks (polygon completely covering the raster) need to be * made before running this method.</b></p> * * @param coverageIterator the raster from which to extract the values. * @param cols the cols of the raster. * @param rows the rows of the raster. * @param xRes the resolution of the raster. * @param gridGeometry the {@link GridGeometry2D} of the raster. * @param polygon the polygon to extract. * @param defaultValue a defaultvalue in case no raster is supplied. * @return the list of coordinates, with the z values set to the raster or default value. * @throws Exception */ public static List<Coordinate> extractPolygonOnCoverage( RandomIter coverageIterator, int cols, int rows, double xRes, GridGeometry2D gridGeometry, Polygon polygon, double defaultValue ) throws Exception { GeometryFactory gf = GeometryUtilities.gf(); PreparedGeometry preparedGeometry = PreparedGeometryFactory.prepare(polygon); double delta = xRes / 4.0; List<Coordinate> coordinatesList = new ArrayList<Coordinate>(); for( int r = 0; r < rows; r++ ) { // do scan line to fill the polygon double[] westPos = gridGeometry.gridToWorld(new GridCoordinates2D(0, r)).getCoordinate(); double[] eastPos = gridGeometry.gridToWorld(new GridCoordinates2D(cols - 1, r)).getCoordinate(); Coordinate west = new Coordinate(westPos[0], westPos[1]); Coordinate east = new Coordinate(eastPos[0], eastPos[1]); LineString line = gf.createLineString(new Coordinate[]{west, east}); if (preparedGeometry.intersects(line)) { Geometry internalLines = polygon.intersection(line); int lineNums = internalLines.getNumGeometries(); for( int l = 0; l < lineNums; l++ ) { Coordinate[] coords = internalLines.getGeometryN(l).getCoordinates(); if (coords.length == 2) { for( int j = 0; j < coords.length; j = j + 2 ) { Coordinate startC = new Coordinate(coords[j].x + delta, coords[j].y); Coordinate endC = new Coordinate(coords[j + 1].x - delta, coords[j + 1].y); DirectPosition2D startDP; DirectPosition2D endDP; if (startC.x < endC.x) { startDP = new DirectPosition2D(startC.x, startC.x); endDP = new DirectPosition2D(endC.x, endC.x); } else { startDP = new DirectPosition2D(endC.x, endC.x); endDP = new DirectPosition2D(startC.x, startC.x); } GridCoordinates2D startGridCoord = gridGeometry.worldToGrid(startDP); GridCoordinates2D endGridCoord = gridGeometry.worldToGrid(endDP); /* * the part in between has to be filled */ for( int k = startGridCoord.x; k <= endGridCoord.x; k++ ) { double v = defaultValue; if (coverageIterator != null) { v = coverageIterator.getSampleDouble(k, r, 0); } double[] xy = gridGeometry.gridToWorld(new GridCoordinates2D(k, r)).getCoordinate(); Coordinate coordinate = new Coordinate(xy[0], xy[1], v); coordinatesList.add(coordinate); } } } else { if (coords.length == 1) { throw new ModelsIOException( MessageFormat.format("Found a cusp in: {0}/{1}", coords[0].x, coords[0].y), "FeatureUtilities"); } else { throw new ModelsIOException(MessageFormat .format("Found intersection with more than 2 points in: {0}/{1}", coords[0].x, coords[0].y), "FeatureUtilities"); } } } } } return coordinatesList; } /** * Calculate the avg of a value in a list of {@link SimpleFeature}s. * * <p>Empty records are ignored. * * @param features the features. * @param field the field to consider. * @return the avg. */ public static double avg( List<SimpleFeature> features, String field ) { double sum = 0; int count = 0; for( SimpleFeature feature : features ) { Object attribute = feature.getAttribute(field); if (attribute instanceof Number) { sum = sum + ((Number) attribute).doubleValue(); count++; } } double avg = sum / count; return avg; } /** * Calculate the histogram of a list of {@link SimpleFeature}s. * * @param features the list of features. * @param field the field to consider. * @param bins the number of bins. * @return the histogram as matrix of rows num like bins and 3 columns for [binCenter, count, cumulated-normalize-count]. */ public static double[][] histogram( List<SimpleFeature> features, String field, int bins ) { double min = Double.POSITIVE_INFINITY; double max = Double.NEGATIVE_INFINITY; for( SimpleFeature feature : features ) { Object attribute = feature.getAttribute(field); if (attribute instanceof Number) { double value = ((Number) attribute).doubleValue(); min = Math.min(min, value); max = Math.max(max, value); } } double range = max - min; double step = range / bins; double[][] histogram = new double[bins][3]; for( int i = 0; i < histogram.length; i++ ) { histogram[i][0] = min + step * (i + 1); } for( SimpleFeature feature : features ) { Object attribute = feature.getAttribute(field); if (attribute instanceof Number) { double value = ((Number) attribute).doubleValue(); for( int j = 0; j < histogram.length; j++ ) { if (value <= histogram[j][0]) { histogram[j][1] = histogram[j][1] + 1; break; } } } } double cumulatedMax = 0; for( int i = 0; i < histogram.length; i++ ) { if (i == 0) { histogram[i][2] = histogram[i][1]; } else { histogram[i][2] = (histogram[i - 1][2] + histogram[i][1]); } cumulatedMax = histogram[i][2]; } for( int i = 0; i < histogram.length; i++ ) { histogram[i][2] = histogram[i][2] / cumulatedMax; // and move the bin markers to their centers histogram[i][0] = histogram[i][0] - step / 2.0; } return histogram; } public static LinkedHashMap<String, String> feature2AlphanumericToHashmap( SimpleFeature feature ) { LinkedHashMap<String, String> attributes = new LinkedHashMap<>(); List<AttributeDescriptor> attributeDescriptors = feature.getFeatureType().getAttributeDescriptors(); int index = 0; for( AttributeDescriptor attributeDescriptor : attributeDescriptors ) { if (!(attributeDescriptor instanceof GeometryDescriptor)) { String fieldName = attributeDescriptor.getLocalName(); Object attribute = feature.getAttribute(index); if (attribute == null) { attribute = ""; } String value = attribute.toString(); attributes.put(fieldName, value); } index++; } return attributes; } /** * Utility to convert a featurecollection to a queryresult format. * * @param featureCollection the collection to convert. * @return the queryresult object. */ public static QueryResult featureCollection2QueryResult( SimpleFeatureCollection featureCollection ) { List<AttributeDescriptor> attributeDescriptors = featureCollection.getSchema().getAttributeDescriptors(); QueryResult queryResult = new QueryResult(); int count = 0; for( AttributeDescriptor attributeDescriptor : attributeDescriptors ) { if (!(attributeDescriptor instanceof GeometryDescriptor)) { String fieldName = attributeDescriptor.getLocalName(); String type = attributeDescriptor.getType().getBinding().toString(); queryResult.names.add(fieldName); queryResult.types.add(type); count++; } } SimpleFeatureIterator featureIterator = featureCollection.features(); while( featureIterator.hasNext() ) { SimpleFeature f = featureIterator.next(); Geometry geometry = (Geometry) f.getDefaultGeometry(); queryResult.geometries.add(geometry); Object[] dataRow = new Object[count]; int index = 0; for( AttributeDescriptor attributeDescriptor : attributeDescriptors ) { if (!(attributeDescriptor instanceof GeometryDescriptor)) { String fieldName = attributeDescriptor.getLocalName(); Object attribute = f.getAttribute(fieldName); if (attribute == null) { attribute = ""; } dataRow[index++] = attribute; } } queryResult.data.add(dataRow); } return queryResult; } }