Skip to content

Commit

Permalink
Issue 103 SVM accepts soundings from bathymetric lidar
Browse files Browse the repository at this point in the history
  • Loading branch information
gwlucastrig committed Jan 13, 2024
1 parent 883a15f commit 271f708
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 14 deletions.
141 changes: 141 additions & 0 deletions svm/src/main/java/org/tinfour/svm/SvmLasFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/* --------------------------------------------------------------------
* Copyright (C) 2024 Gary W. Lucas.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ---------------------------------------------------------------------
*/

/*
* -----------------------------------------------------------------------
*
* Revision History:
* Date Name Description
* ------ --------- -------------------------------------------------
* 01/2024 G. Lucas Created
*
* Notes:
*
* -----------------------------------------------------------------------
*/
package org.tinfour.svm;

import org.tinfour.gis.las.ILasRecordFilter;
import org.tinfour.gis.las.LasPoint;

/**
* Implements logic for filtering sample points from LAS/LAZ files when
* they are used for the Simple Volumetric Model (SVM).
*/
public class SvmLasFilter implements ILasRecordFilter {

boolean allRecordsAccepted;
boolean filterFirst;
boolean filterLast;
boolean[] cFlag = new boolean[256];

/**
* Construct a filter that accepts all records except those
* that are explicitly marked as withheld.
*/
public SvmLasFilter() {
allRecordsAccepted = true;
}

/**
* Construct a filter and optionally apply the rules specified in
* the input string.
*
* @param specification an option string, potentially null or blank.
*/
public SvmLasFilter(String specification) {
if (specification == null || specification.isBlank()) {
allRecordsAccepted = true;
return;
}
String[] a = specification.split(",");
for (int i = 0; i < a.length; i++) {
String s = a[i].trim();
if ("z".equalsIgnoreCase(s)) {
// This option supports the legacy setting
allRecordsAccepted = true;
} else if ("first".equalsIgnoreCase(s)) {
filterFirst = true;
} else if ("last".equalsIgnoreCase(s)) {
filterLast = true;
} else if (s.indexOf('-') > 0) {
String[] b = s.split("-");
int x0 = parseClassification(b[0]);
int x1 = parseClassification(b[1]);
if (x1 < x0) {
throw new IllegalArgumentException(
"Out-of-order entry for LAZ/LAS classification range: " + s);
}
for (int x = x0; x <= x1; x++) {
cFlag[x] = true;
}
} else {
int x = parseClassification(s);
cFlag[x] = true;
}
}
}

private int parseClassification(String sClassification) {
if (sClassification == null || sClassification.isBlank()) {
throw new IllegalArgumentException("Blank entry for LAZ/LAS classification");
}
String s = sClassification.trim();

try {
int x = Integer.parseInt(s);
if (x < 0 || x > 255) {
throw new IllegalArgumentException(
"LAZ/LAZ classification out-of-range [0..255]: " + s);
}
return x;
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException(
"Invalid numeric specification where LAS/LAZ classification expected: " + s);
}
}

@Override
public boolean accept(LasPoint record) {
if (allRecordsAccepted) {
return record.withheld ^ true;
}
if (filterFirst && record.returnNumber == 1) {
return record.withheld ^ true;
}
if (filterLast && record.returnNumber == record.numberOfReturns) {
return record.withheld ^ true;
}
// The AND operation ensures array index in bounds.
if (cFlag[record.classification & 0xff]) {
return record.withheld ^ true;
}
return false;
}

/**
* Indicates that the filter will accept all records except those that
* are explicitly marked as withheld.
*
* @return true if all records are accepted; otherwise, false
*/
public boolean areAllRecordsAccepted() {
return allRecordsAccepted;
}


}
5 changes: 0 additions & 5 deletions svm/src/main/java/org/tinfour/svm/SvmRasterGeoTiff.java
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,4 @@ private boolean writeAuxiliaryFiles(

return true;
}

private boolean approxEquals(double v, double d) {
return Math.abs(v-d)<0.01;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public String toString() {
final String key;
final File file;
final String field;
final String lasFilter;
final IVerticalCoordinateTransform verticalTransform;
final SvmBathymetryModel bathymetryModel;

Expand Down Expand Up @@ -116,19 +117,33 @@ public String toString() {
file = new File(folder, path);
}

String name = file.getName().toLowerCase();
if((name.endsWith(".las") || name.endsWith("laz")) && list.size()>1){
String temp = list.get(1).trim();
if(temp.isBlank()){
lasFilter = temp;
}else{
lasFilter = null;
}
}else{
lasFilter = null;
}

// see if the specification supplies a fixed value for the
// vertical coordinate. In such a case, the fixedValue variable
// will be set to a valid floating point value. Otherwise,
// it will remain as NaN indicating that standard processing is required.
double fixedValue = Double.NaN;
String s = list.get(1).trim();
if (s.length() > 0 && !Character.isAlphabetic(s.charAt(0))) {
// see if it's a numeric giving a fixed value specification
try {
fixedValue = Double.parseDouble(s);
} catch (NumberFormatException dontCare) {
// it's not a fixed value
fixedValue = Double.NaN;
if (list.size() > 1) {
String s = list.get(1).trim();
if (s.length() > 0 && !Character.isAlphabetic(s.charAt(0))) {
// see if it's a numeric giving a fixed value specification
try {
fixedValue = Double.parseDouble(s);
} catch (NumberFormatException dontCare) {
// it's not a fixed value
fixedValue = Double.NaN;
}
}
}

Expand Down Expand Up @@ -217,13 +232,26 @@ public File getFile() {

/**
* Get the named data field String from the specification (if supplied)
* or the LAS/LAZ filter specification (if supplied).
*
* @return if supplied, a valid, non-empty string; otherwise, a null.
*/
public String getField() {
if(lasFilter!=null){
return lasFilter;
}
return field;
}

/**
* Gets a data filter specification if provided for for LAS or LAZ files
* @return if specified, a valid string; otherwise, a null.
*/
public String getLasFilter(){
return lasFilter;
}


/**
* Get the vertical coordinate transform from the specification (if supplied)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,7 @@ public boolean isGeoTiffDataCompressionEnabled() {
try {
return Boolean.parseBoolean(s.trim());
} catch (NumberFormatException nfe) {
// no action required
throw new IllegalArgumentException("Invalid boolean specification for GeoTIFF data compression: "+s);
}
}
return false;
Expand Down
13 changes: 13 additions & 0 deletions svm/src/main/resources/org/tinfour/svm/SvmTemplate.properties
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ capacityGraphTitle = SVM Analysis for Alan Henry Reservoir
# The input source for SVM is usually Shapefiles, though the
# point-based "samples" and "supplement" settings may be specified
# for tab-delimited text and comma-separated-value (CSV) files.
# Bathymetric lidar (las, laz) files are also supported.
# For Shapefiles, the input may specify the field from the
# associated DBF file to be used as an elevation source. The field
# name is given by a "pipe" character folloed by the name of the field.
Expand Down Expand Up @@ -188,6 +189,18 @@ capacityGraphTitle = SVM Analysis for Alan Henry Reservoir
# samples = depth_points.shp | depth | -1 2220
# This setting would cause depth values to be subtracted from the offset 2220.
#
# When input data is from a lidar source, the input samples may be filtered
# by classification or first/last return. Filter specifications are given in
# the form of a comma-separated list of accepted elements. If no filter
# specification is provided, all samples are accepted. Examples include the following:
#
# samples = bathy_lidar.las accept all samples
# samples = bathy_lidar.las | first accept first returns only
# samples = bathy_lidar.las | 2 samples with classification 2 (ground points)
# samples = bathy_lidar.las | first, 2, 3, 10-12
# accept first returns,
# and classifications 2, 3 and 10 through 12

# Coordinate Systems for Input Data
# To obtain correct results, it is critical that the horizontal and vertical
# coordinate systems for the input data be consistent. The horizontal
Expand Down

0 comments on commit 271f708

Please sign in to comment.