/*
* Copyright (C) 2014 Dr. John Lindsay <jlindsay@uoguelph.ca>
*
* This program 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 whitebox.geospatialfiles;
import java.awt.Color;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.stream.IntStream;
import whitebox.interfaces.MapLayer;
import whitebox.structures.BoundingBox;
import whitebox.structures.BooleanBitArray1D;
import whitebox.geospatialfiles.LASReader.PointRecord;
import whitebox.structures.XYPoint;
//import whitebox.interfaces.WhiteboxPluginHost;
/**
*
* @author Dr. John Lindsay
*/
public class LasLayerInfo implements MapLayer {
public final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private String fileName;
private LASReader lasFile;
String layerTitle = "";
private BoundingBox fullExtent = null;
private BoundingBox currentExtent = null;
private boolean visible = true;
private int overlayNumber;
private boolean visibleInLegend = true;
private int alpha = 255;
private String paletteFile = "";
private String pathSep;
private BooleanBitArray1D selectedFeatures;
private String paletteDirectory = "";
private boolean filledWithOneColour = false;
private int selectedFeatureNumber = -1;
private double minimumValue = -32768.0;
private double maximumValue = -32768.0;
private double displayMinValue = -32768.0;
private double displayMaxValue = -32768.0;
private Color fillColour = Color.red;
private float markerSize = 2.5f;
private long numPointRecords = -1;
private double[][] xyData;
private double[] zData;
private int[] intensityData;
private byte[] scanAngleData;
private double[] gpsTimeData;
private int[] rgbData;
private byte[] classData;
private byte fillCriterion = 0; // z value
private int numPaletteEntries = 0;
private int[] paletteData = null;
// private WhiteboxPluginHost host;
// Constructors
public LasLayerInfo() {
}
public LasLayerInfo(String fileName, String paletteDirectory, int alpha, int overlayNumber) { //, WhiteboxPluginHost host) {
this.fileName = fileName;
File file = new File(fileName);
if (!file.exists()) {
System.out.println("File not found.");
}
this.paletteDirectory = paletteDirectory;
this.layerTitle = file.getName().replace(".las", "");
this.alpha = alpha;
this.overlayNumber = overlayNumber;
// this.host = host;
lasFile = new LASReader(fileName);
numPointRecords = lasFile.getNumPointRecords();
currentExtent = new BoundingBox(lasFile.getMinX(), lasFile.getMinY(),
lasFile.getMaxX(), lasFile.getMaxY());
fullExtent = currentExtent.clone();
pathSep = File.separator;
paletteFile = paletteDirectory + "spectrum.pal";
selectedFeatures = new BooleanBitArray1D((int) lasFile.getNumPointRecords() + 1);
}
public LASReader getLASFile() {
return lasFile;
}
// public WhiteboxPluginHost getHost() {
// return host;
// }
//
// public void setHost(WhiteboxPluginHost host) {
// this.host = host;
// }
@Override
public String getLayerTitle() {
return layerTitle;
}
@Override
public void setLayerTitle(String title) {
layerTitle = title;
}
@Override
public MapLayerType getLayerType() {
return MapLayer.MapLayerType.LAS;
}
@Override
public BoundingBox getFullExtent() {
return fullExtent.clone();
}
@Override
public BoundingBox getCurrentExtent() {
return currentExtent.clone();
}
@Override
public void setCurrentExtent(BoundingBox bb) {
if (!bb.equals(currentExtent)) {
currentExtent = bb.clone();
getXYDataInExtent();
}
}
@Override
public boolean isVisible() {
return visible;
}
@Override
public void setVisible(boolean value) {
visible = value;
}
@Override
public int getOverlayNumber() {
return overlayNumber;
}
@Override
public void setOverlayNumber(int value) {
overlayNumber = value;
}
@Override
public boolean isVisibleInLegend() {
return visibleInLegend;
}
@Override
public void setVisibleInLegend(boolean value) {
this.visibleInLegend = value;
}
public float getMarkerSize() {
return markerSize;
}
public void setMarkerSize(float markerSize) {
this.markerSize = markerSize;
}
public int getAlpha() {
return alpha;
}
public void setAlpha(int value) {
if (value < 0) {
value = 0;
}
if (value > 255) {
value = 255;
}
alpha = value;
//setRecordsColourData();
}
public boolean isFilledWithOneColour() {
return filledWithOneColour;
}
public void setFilledWithOneColour(boolean filledWithOneColour) {
this.filledWithOneColour = filledWithOneColour;
}
public String getFillCriterion() {
switch (fillCriterion) {
case 0:
return "z-value";
case 1:
return "intensity";
case 2:
return "class";
case 3:
return "scan angle";
case 4:
return "gps time";
default:
return "RGB data";
}
}
public void setFillCriterion(String value) {
filledWithOneColour = false;
if (value.toLowerCase().contains("z")) {
fillCriterion = 0; // elevation
paletteFile = paletteDirectory + "spectrum.pal";
//readPalette();
} else if (value.toLowerCase().contains("i")) {
fillCriterion = 1; // intensity
paletteFile = paletteDirectory + "grey.pal";
//readPalette();
} else if (value.toLowerCase().contains("class")) {
fillCriterion = 2; // classification
paletteFile = paletteDirectory + "qual.pal";
} else if (value.toLowerCase().contains("scan")) {
fillCriterion = 3; // scan angle
paletteFile = paletteDirectory + "blue_red.pal";
} else if (value.toLowerCase().contains("time")) {
fillCriterion = 4; // gps time
paletteFile = paletteDirectory + "spectrum.pal";
} else if (value.toLowerCase().contains("rgb")) {
fillCriterion = 5; // RGB data
paletteFile = paletteDirectory + "RGB_red.pal";
}
setRecordsColourData();
}
public void updateMinAndMaxValsForCriterion(String criterion) {
if (criterion.toLowerCase().contains("z")) {
fillCriterion = 0; // elevation
if (zData == null) {
readZData();
} else {
minimumValue = lasFile.getMinZ();
maximumValue = lasFile.getMaxZ();
double range = maximumValue - minimumValue;
binSize = range / numPaletteEntries;
histo = new int[numPaletteEntries + 1];
int bin;
for (int i = 0; i < numPointRecords; i++) {
bin = (int) ((zData[i] - minimumValue) / binSize);
histo[bin]++;
}
int onePercent = (int) (numPointRecords * 0.01);
int sum = 0;
for (int i = histo.length - 1; i >= 0; i--) {
sum += histo[i];
if (sum >= onePercent) {
displayMaxValue = (i * binSize) + minimumValue;
break;
}
}
sum = 0;
for (int i = 0; i < histo.length; i++) {
sum += histo[i];
if (sum >= onePercent) {
displayMinValue = (i * binSize) + minimumValue;
break;
}
}
}
} else if (criterion.toLowerCase().contains("intensity")) {
fillCriterion = 1; // intensity
if (intensityData == null) {
readIntensityData();
} else {
int minIntensity = Integer.MAX_VALUE;
int maxIntensity = Integer.MIN_VALUE;
for (int i = 0; i < intensityData.length; i++) {
int intensity = intensityData[i];
if (intensity < minIntensity) {
minIntensity = intensity;
}
if (intensity > maxIntensity) {
maxIntensity = intensity;
}
}
minimumValue = minIntensity;
maximumValue = maxIntensity;
int range = (int) (maximumValue - minimumValue) + 1;
histo = new int[range];
binSize = 1;
int min = (int) minimumValue;
for (int i = 0; i < numPointRecords; i++) {
histo[intensityData[i] - min]++;
}
int onePercent = (int) (numPointRecords * 0.01);
int sum = 0;
for (int i = 0; i < histo.length; i++) {
sum += histo[i];
if (sum >= onePercent) {
displayMinValue = i + min;
break;
}
}
sum = 0;
for (int i = histo.length - 1; i >= 0; i--) {
sum += histo[i];
if (sum >= onePercent) {
displayMaxValue = i + min;
break;
}
}
}
} else if (criterion.toLowerCase().contains("class")) {
fillCriterion = 2; // classification
if (classData == null) {
readClassData();
} else {
byte minClass = Byte.MAX_VALUE;
byte maxClass = Byte.MIN_VALUE;
for (int i = 0; i < classData.length; i++) {
byte classVal = classData[i];
if (classVal < minClass) {
minClass = classVal;
}
if (classVal > maxClass) {
maxClass = classVal;
}
}
minimumValue = minClass;
maximumValue = maxClass;
displayMinValue = minimumValue;
displayMaxValue = maximumValue;
}
} else if (criterion.toLowerCase().contains("scan")) {
fillCriterion = 3; // scan angle
if (scanAngleData == null) {
readScanAngleData();
} else {
byte minScanAngle = Byte.MAX_VALUE;
byte maxScanAngle = Byte.MIN_VALUE;
for (int i = 0; i < scanAngleData.length; i++) {
byte scanAngle = scanAngleData[i];
if (scanAngle < minScanAngle) {
minScanAngle = scanAngle;
}
if (scanAngle > maxScanAngle) {
maxScanAngle = scanAngle;
}
}
minimumValue = minScanAngle;
maximumValue = maxScanAngle;
int range = (int) (maximumValue - minimumValue) + 1;
histo = new int[range];
binSize = 1;
int min = (int) minimumValue;
for (int i = 0; i < numPointRecords; i++) {
histo[scanAngleData[i] - min]++;
}
int onePercent = (int) (numPointRecords * 0.01);
int sum = 0;
for (int i = 0; i < histo.length; i++) {
sum += histo[i];
if (sum >= onePercent) {
displayMinValue = i + min;
break;
}
}
sum = 0;
for (int i = histo.length - 1; i >= 0; i--) {
sum += histo[i];
if (sum >= onePercent) {
displayMaxValue = i + min;
break;
}
}
}
} else if (criterion.toLowerCase().contains("time")) {
fillCriterion = 4; // gps time
if (gpsTimeData == null) {
readGpsTimeData();
} else {
double minGpsTime = Double.MAX_VALUE;
double maxGpsTime = Double.MIN_VALUE;
for (int i = 0; i < gpsTimeData.length; i++) {
double gpsTime = gpsTimeData[i];
if (gpsTime < minGpsTime) {
minGpsTime = gpsTime;
}
if (gpsTime > maxGpsTime) {
maxGpsTime = gpsTime;
}
}
minimumValue = minGpsTime;
maximumValue = maxGpsTime;
displayMinValue = minimumValue;
displayMaxValue = maximumValue;
}
} else if (criterion.toLowerCase().contains("rgb")) {
fillCriterion = 5; // RGB data
if (rgbData == null) {
readRgbData();
} else {
int minVal = Integer.MAX_VALUE;
int maxVal = Integer.MIN_VALUE;
for (int i = 0; i < rgbData.length; i++) {
int val = rgbData[i];
if (val < minVal) {
minVal = val;
}
if (val > maxVal) {
maxVal = val;
}
}
minimumValue = minVal;
maximumValue = maxVal;
int range = (int) (maximumValue - minimumValue) + 1;
histo = new int[range];
binSize = 1;
int min = (int) minimumValue;
for (int i = 0; i < numPointRecords; i++) {
histo[scanAngleData[i] - min]++;
}
int onePercent = (int) (numPointRecords * 0.01);
int sum = 0;
for (int i = 0; i < histo.length; i++) {
sum += histo[i];
if (sum >= onePercent) {
displayMinValue = i + min;
break;
}
}
sum = 0;
for (int i = histo.length - 1; i >= 0; i--) {
sum += histo[i];
if (sum >= onePercent) {
displayMaxValue = i + min;
break;
}
}
}
}
}
private void findPaletteDirectory(File dir) {
File[] files = dir.listFiles();
for (int x = 0; x < files.length; x++) {
if (files[x].isDirectory()) {
if (files[x].toString().endsWith(pathSep + "palettes")) {
paletteDirectory = files[x].toString() + pathSep;
break;
} else {
findPaletteDirectory(files[x]);
}
}
}
}
/**
* Clears all of the selected features from a LAS layer.
*/
public void clearSelectedFeatures() {
selectedFeatures = new BooleanBitArray1D((int) lasFile.getNumPointRecords() + 1);
selectedFeatureNumbers.clear();
// int oldValue = this.selectedFeatureNumber;
this.selectedFeatureNumber = -2;
this.pcs.firePropertyChange("selectedFeatureNumber", -1, selectedFeatureNumber);
}
/**
* Determines if a specified record number is selected.
*
* @param recordNumber The one-based record ID.
* @return boolean A boolean value.
*/
public boolean isFeatureSelected(int recordNumber) {
if (recordNumber >= selectedFeatures.getLength()) {
return false;
}
return selectedFeatures.getValue(recordNumber);
}
private ArrayList<Integer> selectedFeatureNumbers = new ArrayList<>();
/**
* Gets a list of all selected feature record numbers.
*
* @return ArrayList of selected feature numbers.
*/
public ArrayList<Integer> getSelectedFeatureNumbers() {
return selectedFeatureNumbers;
}
public Color getFillColour() {
return fillColour;
}
public void setFillColour(Color fillColour) {
this.fillColour = fillColour;
setRecordsColourData();
}
public String getPaletteFile() {
return paletteFile;
}
public void setPaletteFile(String fileName) {
if (!paletteFile.equals(fileName)) {
paletteFile = fileName;
}
}
public double getMaximumValue() {
return maximumValue;
}
public void setMaximumValue(double value) {
this.maximumValue = value;
}
public double getMinimumValue() {
return minimumValue;
}
public void setMinimumValue(double value) {
this.minimumValue = value;
}
public double getDisplayMaxValue() {
return displayMaxValue;
}
public void setDisplayMaxValue(double value) {
this.displayMaxValue = value;
}
public double getDisplayMinValue() {
return displayMinValue;
}
public void setDisplayMinValue(double value) {
this.displayMinValue = value;
}
ArrayList<XYPoint> pointXYData;
public ArrayList<XYPoint> getPointXYData() {
if (pointXYData == null) {
getXYDataInExtent();
}
return pointXYData;
}
private void readXYData() {
xyData = new double[(int) numPointRecords][2];
PointRecord rec;
if (isFilledWithOneColour()) {
//int oldProgress = -1;
//int progress = 0;
for (int i = 0; i < numPointRecords; i++) {
rec = lasFile.getPointRecord(i);
if (!rec.isPointWithheld()) {
xyData[i][0] = rec.getX();
xyData[i][1] = rec.getY();
}
// progress = (int) (100d * i / (numPointRecords + 1));
// if (progress != oldProgress) {
// oldProgress = progress;
// if (host != null) {
// host.updateProgress("Reading data:", progress);
// }
// }
}
// if (host != null) {
// host.updateProgress(0);
// }
} else if (fillCriterion == 0) { // elevation is the fill criterion
// If you're going to have to read every record in the file, you
// may as well grab the elevation data at the same time.
minimumValue = lasFile.getMinZ();
maximumValue = lasFile.getMaxZ();
zData = new double[(int) numPointRecords];
for (int i = 0; i < numPointRecords; i++) {
rec = lasFile.getPointRecord(i);
if (!rec.isPointWithheld()) {
xyData[i][0] = rec.getX();
xyData[i][1] = rec.getY();
zData[i] = rec.getZ();
}
}
double range = maximumValue - minimumValue;
readPalette();
binSize = range / numPaletteEntries;
histo = new int[numPaletteEntries + 1];
int bin;
for (int i = 0; i < numPointRecords; i++) {
bin = (int) ((zData[i] - minimumValue) / binSize);
if (bin < 0) { bin = 0; }
if (bin > numPaletteEntries) { bin = numPaletteEntries; }
histo[bin]++;
}
int onePercent = (int) (numPointRecords * 0.01);
int sum = 0;
for (int i = histo.length - 1; i >= 0; i--) {
sum += histo[i];
if (sum >= onePercent) {
displayMaxValue = (i * binSize) + minimumValue;
break;
}
}
sum = 0;
for (int i = 0; i < histo.length; i++) {
sum += histo[i];
if (sum >= onePercent) {
displayMinValue = (i * binSize) + minimumValue;
break;
}
}
} else if (fillCriterion == 1) { // intensity is the fill criterion
int intensity;
int minIntensity = Integer.MAX_VALUE;
int maxIntensity = Integer.MIN_VALUE;
intensityData = new int[(int) numPointRecords];
for (int i = 0; i < numPointRecords; i++) {
rec = lasFile.getPointRecord(i);
if (!rec.isPointWithheld()) {
xyData[i][0] = rec.getX();
xyData[i][1] = rec.getY();
intensity = rec.getIntensity();
intensityData[i] = intensity;
if (intensity < minIntensity) {
minIntensity = intensity;
}
if (intensity > maxIntensity) {
maxIntensity = intensity;
}
}
}
minimumValue = minIntensity;
maximumValue = maxIntensity;
int range = (int) (maximumValue - minimumValue) + 1;
histo = new int[range];
binSize = 1;
int min = (int) minimumValue;
for (int i = 0; i < numPointRecords; i++) {
histo[intensityData[i] - min]++;
}
int onePercent = (int) (numPointRecords * 0.01);
int sum = 0;
for (int i = 0; i < histo.length; i++) {
sum += histo[i];
if (sum >= onePercent) {
displayMinValue = i + min;
break;
}
}
sum = 0;
for (int i = histo.length - 1; i >= 0; i--) {
sum += histo[i];
if (sum >= onePercent) {
displayMaxValue = i + min;
break;
}
}
}
}
int[] histo;
double binSize;
private void readZData() {
minimumValue = lasFile.getMinZ();
maximumValue = lasFile.getMaxZ();
zData = new double[(int) numPointRecords];
PointRecord rec;
// int oldProgress = -1;
// int progress = 0;
for (int i = 0; i < numPointRecords; i++) {
rec = lasFile.getPointRecord(i);
if (!rec.isPointWithheld()) {
zData[i] = rec.getZ();
}
// progress = (int) (100d * i / (numPointRecords + 1));
// if (progress != oldProgress) {
// oldProgress = progress;
// if (host != null) {
// host.updateProgress("Reading data:", progress);
// }
// }
}
// if (host != null) {
// host.updateProgress(0);
// }
double range = maximumValue - minimumValue;
binSize = range / numPaletteEntries;
histo = new int[numPaletteEntries + 1];
int bin;
for (int i = 0; i < numPointRecords; i++) {
bin = (int) ((zData[i] - minimumValue) / binSize);
histo[bin]++;
}
int onePercent = (int) (numPointRecords * 0.01);
int sum = 0;
for (int i = histo.length - 1; i >= 0; i--) {
sum += histo[i];
if (sum >= onePercent) {
displayMaxValue = (i * binSize) + minimumValue;
break;
}
}
sum = 0;
for (int i = 0; i < histo.length; i++) {
sum += histo[i];
if (sum >= onePercent) {
displayMinValue = (i * binSize) + minimumValue;
break;
}
}
}
private void readIntensityData() {
int intensity;
int minIntensity = Integer.MAX_VALUE;
int maxIntensity = Integer.MIN_VALUE;
intensityData = new int[(int) numPointRecords];
PointRecord rec;
// int oldProgress = -1;
// int progress = 0;
for (int i = 0; i < numPointRecords; i++) {
rec = lasFile.getPointRecord(i);
if (!rec.isPointWithheld()) {
intensity = rec.getIntensity();
intensityData[i] = intensity;
if (intensity < minIntensity) {
minIntensity = intensity;
}
if (intensity > maxIntensity) {
maxIntensity = intensity;
}
}
// progress = (int) (100d * i / (numPointRecords + 1));
// if (progress != oldProgress) {
// oldProgress = progress;
// if (host != null) {
// host.updateProgress("Reading data:", progress);
// }
// }
}
// if (host != null) {
// host.updateProgress(0);
// }
minimumValue = minIntensity;
maximumValue = maxIntensity;
int range = (int) (maximumValue - minimumValue) + 1;
histo = new int[range];
binSize = 1;
int min = (int) minimumValue;
for (int i = 0; i < numPointRecords; i++) {
histo[intensityData[i] - min]++;
}
int onePercent = (int) (numPointRecords * 0.01);
int sum = 0;
for (int i = 0; i < histo.length; i++) {
sum += histo[i];
if (sum >= onePercent) {
displayMinValue = i + min;
break;
}
}
sum = 0;
for (int i = histo.length - 1; i >= 0; i--) {
sum += histo[i];
if (sum >= onePercent) {
displayMaxValue = i + min;
break;
}
}
}
private void readScanAngleData() {
byte scanAngle;
byte minScanAngle = Byte.MAX_VALUE;
byte maxScanAngle = Byte.MIN_VALUE;
scanAngleData = new byte[(int) numPointRecords];
PointRecord rec;
for (int i = 0; i < numPointRecords; i++) {
rec = lasFile.getPointRecord(i);
if (!rec.isPointWithheld()) {
scanAngle = rec.getScanAngle();
scanAngleData[i] = scanAngle;
if (scanAngle < minScanAngle) {
minScanAngle = scanAngle;
}
if (scanAngle > maxScanAngle) {
maxScanAngle = scanAngle;
}
}
}
minimumValue = minScanAngle;
maximumValue = maxScanAngle;
int range = (int) (maximumValue - minimumValue) + 1;
histo = new int[range];
binSize = 1;
int min = (int) minimumValue;
for (int i = 0; i < numPointRecords; i++) {
histo[scanAngleData[i] - min]++;
}
int onePercent = (int) (numPointRecords * 0.01);
int sum = 0;
for (int i = 0; i < histo.length; i++) {
sum += histo[i];
if (sum >= onePercent) {
displayMinValue = i + min;
break;
}
}
sum = 0;
for (int i = histo.length - 1; i >= 0; i--) {
sum += histo[i];
if (sum >= onePercent) {
displayMaxValue = i + min;
break;
}
}
}
private void readGpsTimeData() {
double gpsTime;
double minGpsTime = Double.MAX_VALUE;
double maxGpsTime = Double.MIN_VALUE;
gpsTimeData = new double[(int) numPointRecords];
PointRecord rec;
for (int i = 0; i < numPointRecords; i++) {
rec = lasFile.getPointRecord(i);
if (!rec.isPointWithheld()) {
gpsTime = rec.getGPSTime();
gpsTimeData[i] = gpsTime;
if (gpsTime < minGpsTime) {
minGpsTime = gpsTime;
}
if (gpsTime > maxGpsTime) {
maxGpsTime = gpsTime;
}
}
}
minimumValue = minGpsTime;
maximumValue = maxGpsTime;
displayMinValue = minimumValue;
displayMaxValue = maximumValue;
// int range = (int) (maximumValue - minimumValue) + 1;
// histo = new int[range];
// binSize = 1;
// int bin;
// int min = (int) minimumValue;
// for (int i = 0; i < numPointRecords; i++) {
// bin = (int) ((gpsTimeData[i] - minimumValue) / binSize);
// histo[bin]++;
// }
// int onePercent = (int) (numPointRecords * 0.01);
// int sum = 0;
// for (int i = 0; i < histo.length; i++) {
// sum += histo[i];
// if (sum >= onePercent) {
// displayMinValue = i + min;
// break;
// }
// }
//
// sum = 0;
// for (int i = histo.length - 1; i >= 0; i--) {
// sum += histo[i];
// if (sum >= onePercent) {
// displayMaxValue = i + min;
// break;
// }
// }
}
private void readRgbData() {
byte scanAngle;
byte minScanAngle = Byte.MAX_VALUE;
byte maxScanAngle = Byte.MIN_VALUE;
scanAngleData = new byte[(int) numPointRecords];
PointRecord rec;
for (int i = 0; i < numPointRecords; i++) {
rec = lasFile.getPointRecord(i);
if (!rec.isPointWithheld()) {
scanAngle = rec.getScanAngle();
scanAngleData[i] = scanAngle;
if (scanAngle < minScanAngle) {
minScanAngle = scanAngle;
}
if (scanAngle > maxScanAngle) {
maxScanAngle = scanAngle;
}
}
}
minimumValue = minScanAngle;
maximumValue = maxScanAngle;
int range = (int) (maximumValue - minimumValue) + 1;
histo = new int[range];
binSize = 1;
int min = (int) minimumValue;
for (int i = 0; i < numPointRecords; i++) {
histo[scanAngleData[i] - min]++;
}
int onePercent = (int) (numPointRecords * 0.01);
int sum = 0;
for (int i = 0; i < histo.length; i++) {
sum += histo[i];
if (sum >= onePercent) {
displayMinValue = i + min;
break;
}
}
sum = 0;
for (int i = histo.length - 1; i >= 0; i--) {
sum += histo[i];
if (sum >= onePercent) {
displayMaxValue = i + min;
break;
}
}
}
public void clipLowerTailForDisplayMinimum(double percent) {
int target = (int) (numPointRecords * percent / 100d);
int sum = 0;
for (int i = 0; i < histo.length; i++) {
sum += histo[i];
if (sum >= target) {
displayMinValue = (i * binSize) + minimumValue;
break;
}
}
}
public void clipUpperTailForDisplayMaximum(double percent) {
int target = (int) (numPointRecords * percent / 100d);
int sum = 0;
for (int i = histo.length - 1; i >= 0; i--) {
sum += histo[i];
if (sum >= target) {
displayMaxValue = (i * binSize) + minimumValue;
break;
}
}
}
private void readClassData() {
classData = new byte[(int) numPointRecords];
PointRecord rec;
byte minClass = Byte.MAX_VALUE;
byte maxClass = Byte.MIN_VALUE;
for (int i = 0; i < numPointRecords; i++) {
rec = lasFile.getPointRecord(i);
if (!rec.isPointWithheld()) {
classData[i] = rec.getClassification();
byte classVal = classData[i];
if (classVal < minClass) {
minClass = classVal;
}
if (classVal > maxClass) {
maxClass = classVal;
}
}
}
minimumValue = minClass;
maximumValue = maxClass;
displayMinValue = minimumValue;
displayMaxValue = maximumValue;
}
int maxNumDisplayedPoints = 50000;
private void getXYDataInExtent() {
if (xyData == null) {
readXYData();
}
if (colourData == null) {
setRecordsColourData();
}
double minXbb = currentExtent.getMinX();
double minYbb = currentExtent.getMinY();
double maxXbb = currentExtent.getMaxX();
double maxYbb = currentExtent.getMaxY();
double x, y;
pointXYData = new ArrayList<>();
PointRecord rec;
if (filledWithOneColour) {
colourDataOfExtent = new ArrayList<>();
colourDataOfExtent.add(colourData[0]);
for (int i = 0; i < xyData.length; i++) {
x = xyData[i][0];
y = xyData[i][1];
if (maxYbb < y || maxXbb < x || minYbb > y || minXbb > x) {
// do nothing it's outside the bounds
} else {
pointXYData.add(new XYPoint(x, y));
}
}
} else {
colourDataOfExtent = new ArrayList<>();
for (int i = 0; i < xyData.length; i++) {
x = xyData[i][0];
y = xyData[i][1];
if (maxYbb < y || maxXbb < x || minYbb > y || minXbb > x) {
// do nothing it's outside the bounds
} else {
pointXYData.add(new XYPoint(x, y));
colourDataOfExtent.add(colourData[i]);
}
}
}
// ArrayList<XYPoint> ret = new ArrayList<>();
// PointRecord rec;
// for (int i = 0; i < numPointRecords; i++) {
// rec = lasFile.getPointRecord(i);
// if (!rec.isPointWithheld()) {
// x = rec.getX();
// y = rec.getY();
// if (maxYbb < y || maxXbb < x || minYbb > y || minXbb > x) {
// // do nothing it's outside the bounds
// } else {
// ret.add(new XYPoint(x, y));
// }
// }
// }
// int numPointsInExtent = ret.size();
// if (numPointsInExtent > maxNumDisplayedPoints) {
// int skipVal = (int)(Math.ceil(numPointsInExtent / maxNumDisplayedPoints));
// pointXYData = new ArrayList<>(numPointsInExtent / skipVal);
// PointRecord point;
// for (int i = 0; i < numPointsInExtent; i += skipVal) {
// pointXYData.add(ret.get(i));
// }
//
// } else {
// pointXYData = new ArrayList<>(ret);
// }
}
/**
* Returns the number of features that are currently selected.
*
* @return int of the number of selected features.
*/
public int getNumSelectedFeatures() {
return selectedFeatureNumbers.size();
}
private Color[] colourData;
private ArrayList<Color> colourDataOfExtent;
public ArrayList<Color> getColourData() {
if (colourData == null) {
setRecordsColourData();
}
if (colourDataOfExtent == null) {
getXYDataInExtent();
}
return colourDataOfExtent;
}
public void setRecordsColourData() {
colourDataOfExtent = null;
if (filledWithOneColour) {
colourData = new Color[1];
colourData[0] = new Color(fillColour.getRed(), fillColour.getGreen(), fillColour.getBlue(), alpha);
} else {
readPalette();
colourData = new Color[(int) numPointRecords];
int a1 = this.getAlpha();
double value, range;
int numPaletteEntriesLessOne = numPaletteEntries - 1;
int entryNum;
double gamma = 1;
Color clr;
switch (fillCriterion) {
case 0: // elevation
if (zData == null) {
readZData();
}
range = displayMaxValue - displayMinValue;
IntStream.range(0, (int) numPointRecords - 1).parallel().forEach(i
-> colourData[i] = getColourFromElevation(i, range));
break;
case 1: // intensity
if (intensityData == null) {
readIntensityData();
}
range = displayMaxValue - displayMinValue;
IntStream.range(0, (int) numPointRecords - 1).parallel().forEach(i
-> colourData[i] = getColourFromIntensity(i, range));
// for (int i = 0; i < (int) numPointRecords; i++) {
// value = intensityData[i];
// if (gamma == 1) {
// entryNum = (int) ((value - displayMinValue) / range * numPaletteEntriesLessOne);
// } else {
// entryNum = (int) (Math.pow((value - displayMinValue) / range, gamma) * numPaletteEntriesLessOne);
// }
//
// //entryNum = (int) (((value - minimumValue) / range) * (numPaletteEntries - 1));
// if (entryNum < 0) {
// entryNum = 0;
// }
// if (entryNum > numPaletteEntries - 1) {
// entryNum = numPaletteEntries - 1;
// }
// clr = new Color(paletteData[entryNum]);
// colourData[i] = new Color(clr.getRed(), clr.getGreen(), clr.getBlue(), a1);
// }
break;
case 2: // classification
if (classData == null) {
readClassData();
}
IntStream.range(0, (int) numPointRecords - 1).parallel().forEach(i
-> colourData[i] = getColourFromClass(i));
break;
case 3: // scan angle
if (scanAngleData == null) {
readScanAngleData();
}
range = displayMaxValue - displayMinValue;
IntStream.range(0, (int) numPointRecords - 1).parallel().forEach(i
-> colourData[i] = getColourFromScanAngle(i, range));
break;
case 4: // GPS Time
if (gpsTimeData == null) {
readGpsTimeData();
}
range = displayMaxValue - displayMinValue;
IntStream.range(0, (int) numPointRecords - 1).parallel().forEach(i
-> colourData[i] = getColourFromGpsTime(i, range));
break;
}
}
}
private Color getColourFromElevation(int i, double range) {
int entryNum;
double value = zData[i];
entryNum = (int) ((value - displayMinValue) / range * (numPaletteEntries - 1));
if (entryNum < 0) {
entryNum = 0;
}
if (entryNum > numPaletteEntries - 1) {
entryNum = numPaletteEntries - 1;
}
Color clr = new Color(paletteData[entryNum]);
return new Color(clr.getRed(), clr.getGreen(), clr.getBlue(), alpha);
}
private Color getColourFromIntensity(int i, double range) {
int entryNum;
double value = intensityData[i];
entryNum = (int) ((value - displayMinValue) / range * (numPaletteEntries - 1));
if (entryNum < 0) {
entryNum = 0;
}
if (entryNum > numPaletteEntries - 1) {
entryNum = numPaletteEntries - 1;
}
Color clr = new Color(paletteData[entryNum]);
return new Color(clr.getRed(), clr.getGreen(), clr.getBlue(), alpha);
}
private Color getColourFromClass(int i) {
int entryNum;
double value = classData[i];
entryNum = (int) (value - displayMinValue);
if (entryNum < 0) {
entryNum = 0;
}
if (entryNum > numPaletteEntries - 1) {
entryNum = numPaletteEntries - 1;
}
Color clr = new Color(paletteData[entryNum]);
return new Color(clr.getRed(), clr.getGreen(), clr.getBlue(), alpha);
}
private Color getColourFromScanAngle(int i, double range) {
int entryNum;
double value = scanAngleData[i];
entryNum = (int) ((value - displayMinValue) / range * (numPaletteEntries - 1));
if (entryNum < 0) {
entryNum = 0;
}
if (entryNum > numPaletteEntries - 1) {
entryNum = numPaletteEntries - 1;
}
Color clr = new Color(paletteData[entryNum]);
return new Color(clr.getRed(), clr.getGreen(), clr.getBlue(), alpha);
}
private Color getColourFromGpsTime(int i, double range) {
int entryNum;
double value = gpsTimeData[i];
entryNum = (int) ((value - displayMinValue) / range * (numPaletteEntries - 1));
if (entryNum < 0) {
entryNum = 0;
}
if (entryNum > numPaletteEntries - 1) {
entryNum = numPaletteEntries - 1;
}
Color clr = new Color(paletteData[entryNum]);
return new Color(clr.getRed(), clr.getGreen(), clr.getBlue(), alpha);
}
private void readPalette() {
RandomAccessFile rIn = null;
ByteBuffer buf = null;
int i;
try {
// see if the file exists, if not, set it to the default palette.
File file = new File(paletteFile);
if (!file.exists()) {
return;
}
numPaletteEntries = (int) (file.length() / 4);
buf = ByteBuffer.allocate(numPaletteEntries * 4);
rIn = new RandomAccessFile(paletteFile, "r");
FileChannel inChannel = rIn.getChannel();
inChannel.position(0);
inChannel.read(buf);
// Check the byte order.
buf.order(ByteOrder.LITTLE_ENDIAN);
// Read the data.
buf.rewind();
IntBuffer ib = buf.asIntBuffer();
paletteData = new int[numPaletteEntries];
ib.get(paletteData);
ib = null;
// Update the palette for the alpha value.
if (alpha < 255) {
int r, g, b, val;
for (i = 0; i < numPaletteEntries; i++) {
val = paletteData[i];
r = (val >> 16) & 0xFF;
g = (val >> 8) & 0xFF;
b = val & 0xFF;
paletteData[i] = (alpha << 24) | (r << 16) | (g << 8) | b;
}
}
} catch (Exception e) {
System.err.println("Caught exception: " + e.toString());
System.err.println(e.getStackTrace());
} finally {
if (rIn != null) {
try {
rIn.close();
} catch (Exception e) {
}
}
}
}
}