/* * 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.io.las; import java.io.File; import java.util.ArrayList; import java.util.List; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.feature.DefaultFeatureCollection; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.geometry.jts.ReferencedEnvelope3D; import org.jgrasstools.gears.io.las.core.LasRecord; import org.jgrasstools.gears.io.las.index.LasIndexer; import org.jgrasstools.gears.utils.geometry.GeometryUtilities; import org.jgrasstools.gears.utils.math.NumericsUtilities; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; /** * Abstract las data manager class. * * <p>This is used to create the data manager, being it * from single las file or index folder. * * @author Andrea Antonello (www.hydrologis.com) */ public abstract class ALasDataManager implements AutoCloseable { protected GeometryFactory gf = GeometryUtilities.gf(); protected double[] intensityRange; protected double[] impulses; protected int impulsesNum = -1; protected double[] classes; protected boolean hasConstraint = false; protected CoordinateReferenceSystem crs; /** * Factory method to create {@link ALasDataManager}. * * @param lasFile the las file or las folder index file. * @param inDem a dem to normalize the elevation. If <code>!null</code>, the height over the dtm is added as {@link LasRecord#groundElevation}. * @param elevThreshold a threshold to use for the elevation normalization. * @param inCrs the data {@link org.opengis.referencing.crs.CoordinateReferenceSystem}. if null, the one of the dem is read, if available. */ public static ALasDataManager getDataManager( File dataFile, GridCoverage2D inDem, double elevThreshold, CoordinateReferenceSystem inCrs ) { String lcName = dataFile.getName().toLowerCase(); if (lcName.endsWith(".las")) { return new LasFileDataManager(dataFile, inDem, elevThreshold, inCrs); } else if (lcName.equals(LasIndexer.INDEX_LASFOLDER)) { return new LasFolderIndexDataManager(dataFile, inDem, elevThreshold, inCrs); } else { throw new IllegalArgumentException("Can only read .las and " + LasIndexer.INDEX_LASFOLDER + " files."); } } /** * @return the file representing the dataset. */ public abstract File getFile(); /** * Open the main folder file and read the main index. * * @throws Exception */ public abstract void open() throws Exception; public void setIntensityConstraint( double[] minMax ) { if (minMax == null) return; intensityRange = minMax; hasConstraint = true; } public void setImpulsesConstraint( double[] impulsesToKeep ) { if (impulsesToKeep == null) return; impulses = impulsesToKeep; hasConstraint = true; } public void setImpulsesNumConstraint( int impulsesNumToKeep ) { impulsesNum = impulsesNumToKeep; hasConstraint = true; } public void setClassesConstraint( double[] classesToKeep ) { if (classesToKeep == null) return; classes = classesToKeep; hasConstraint = true; } /** * Get points inside a given geometry boundary. * * @param checkGeom the {@link com.vividsolutions.jts.geom.Geometry} to use to check. * @param doOnlyEnvelope check for the geom envelope instead of a intersection with it. * @return the list of points contained in the supplied geometry. * @throws Exception */ public abstract List<LasRecord> getPointsInGeometry( Geometry checkGeom, boolean doOnlyEnvelope ) throws Exception; /** * Retrieve all the trees envelopes that intersect the geometry. * * @param checkGeom the {@link com.vividsolutions.jts.geom.Geometry} to use to check. * @param doOnlyEnvelope check for the geom envelope instead of a intersection with it. * @param minMaxZ an array to be filled with the min and max z to be used as style. * @return the list of envelopes contained in the supplied geometry. * @throws Exception */ public abstract List<Geometry> getEnvelopesInGeometry( Geometry checkGeom, boolean doOnlyEnvelope, double[] minMaxZ ) throws Exception; /** * Get the overall envelope of the las folder. * * <p>This reads the data from the index.</p> * * @return the {@link org.geotools.geometry.jts.ReferencedEnvelope} of the data. * @throws Exception */ public abstract ReferencedEnvelope getOverallEnvelope() throws Exception; /** * Getter for the list of envelopes of all las files. * * @return the list of {@link org.geotools.geometry.jts.ReferencedEnvelope}s. * @throws Exception */ public abstract List<ReferencedEnvelope> getEnvelopeList() throws Exception; /** * Get the overall envelope 3d of the las folder. * * <p>Warning: this needs to open all involved readers. * * @return the {@link org.geotools.geometry.jts.ReferencedEnvelope3D} of the data. * @throws Exception */ public abstract ReferencedEnvelope3D getEnvelope3D() throws Exception; /** * Creates a polygon {@link org.geotools.data.simple.SimpleFeatureCollection} for all las files. * * @return the features of teh las file bounds. * @throws Exception */ public abstract SimpleFeatureCollection getOverviewFeatures() throws Exception; /** * Retrieve all the envelope features that intersect the geometry. * * <p>an elev attribute is added with the max elev contained in the envelope. * * @param checkGeom the {@link com.vividsolutions.jts.geom.Geometry} to use to check. * @param doOnlyEnvelope check for the geom envelope instead of a intersection with it. * @param minMaxZI an array to be filled with the [minz,maxz, minintensity, maxintensity] to be used as style. * @param doPoints if <code>true</code>, create points instead of polygons. * @return the features of the envelopes contained in the supplied geometry. * @throws Exception */ public synchronized SimpleFeatureCollection getEnvelopeFeaturesInGeometry( Geometry checkGeom, boolean doOnlyEnvelope, double[] minMaxZI, boolean doPoints ) throws Exception { List<Geometry> envelopesInGeometry = getEnvelopesInGeometry(checkGeom, doOnlyEnvelope, null); SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); b.setName("overview"); b.setCRS(crs); if (!doPoints) { b.add("the_geom", Polygon.class); } else { b.add("the_geom", Point.class); } b.add("elev", Double.class); b.add("intensity", Double.class); SimpleFeatureType type = b.buildFeatureType(); SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type); double minZ = Double.POSITIVE_INFINITY; double maxZ = Double.NEGATIVE_INFINITY; double minI = Double.POSITIVE_INFINITY; double maxI = Double.NEGATIVE_INFINITY; DefaultFeatureCollection newFeatures = new DefaultFeatureCollection(); for( int i = 0; i < envelopesInGeometry.size(); i++ ) { Geometry geom = envelopesInGeometry.get(i); if (doPoints) { Envelope envelope = geom.getEnvelopeInternal(); Coordinate centre = envelope.centre(); geom = gf.createPoint(centre); } double elev = -9999.0; double intens = -9999.0; Object userData = geom.getUserData(); if (userData instanceof double[]) { double[] data = (double[]) userData; elev = data[0]; intens = data[1]; } if (minMaxZI != null) { minZ = Math.min(minZ, elev); maxZ = Math.max(maxZ, elev); minI = Math.min(minI, intens); maxI = Math.max(maxI, intens); } Object[] objs = new Object[]{geom, elev, intens}; builder.addAll(objs); SimpleFeature feature = builder.buildFeature(null); newFeatures.add(feature); } if (minMaxZI != null) { minMaxZI[0] = minZ; minMaxZI[1] = maxZ; minMaxZI[2] = minI; minMaxZI[3] = maxI; } return newFeatures; } /** * Check the point for constraints. * * @param lasDot the point to check. * @return <code>true</code> if the point is accepted. */ protected boolean doAccept( LasRecord lasDot ) { if (!hasConstraint) { return true; } boolean takeIt = true; if (intensityRange != null) { short intensity = lasDot.intensity; if (intensity >= intensityRange[0] && intensity <= intensityRange[1]) { takeIt = true; } else { return false; } } if (impulses != null) { int impulse = lasDot.returnNumber; takeIt = false; for( final double imp : impulses ) { if (impulse == (int) imp) { takeIt = true; break; } } if (!takeIt) return false; } if (impulsesNum != -1) { int numOfReturns = lasDot.numberOfReturns; if (numOfReturns != (int) impulsesNum) { return false; } } if (classes != null) { int classification = lasDot.classification; takeIt = false; for( final double classs : classes ) { if (classification == (int) classs) { // since it is the last checked, if it is true, accept it return true; } } return false; } return true; } /** * Extracts the points contained inside a vertical range from the supplied list of points. * * @param pointsList the list os {@link org.jgrasstools.gears.io.las.core.LasRecord points}. * @param min the min value of the range. * @param max the max value of the range. * @param isGroundElev if <code>true</code>, ground elevation is used instead of z. * @return the points contained in the range. */ public static List<LasRecord> getPointsInVerticalRange( List<LasRecord> pointsList, double min, double max, boolean isGroundElev ) { ArrayList<LasRecord> pointsListInVertical = new ArrayList<LasRecord>(); if (!isGroundElev) { for( LasRecord lasRecord : pointsList ) { if (NumericsUtilities.isBetween(lasRecord.z, min, max)) { pointsListInVertical.add(lasRecord); } } } else { for( LasRecord lasRecord : pointsList ) { if (NumericsUtilities.isBetween(lasRecord.groundElevation, min, max)) { pointsListInVertical.add(lasRecord); } } } return pointsListInVertical; } /** * Extracts the points contained inside a height from ground range from the supplied list of points. * * <p>No check is done on the existence of the ground height value. * * @param pointsList the list os {@link org.jgrasstools.gears.io.las.core.LasRecord points}. * @param min the min value of the range. * @param max the max value of the range. * @return the points contained in the range. */ public static List<LasRecord> getPointsInHeightRange( List<LasRecord> pointsList, double min, double max ) { ArrayList<LasRecord> pointsListInVertical = new ArrayList<LasRecord>(); for( LasRecord lasRecord : pointsList ) { if (NumericsUtilities.isBetween(lasRecord.groundElevation, min, max)) { pointsListInVertical.add(lasRecord); } } return pointsListInVertical; } }