From 271f708a348235bfb56342ce5ace98a7ad1b1323 Mon Sep 17 00:00:00 2001 From: gwlucastrig Date: Sat, 13 Jan 2024 14:13:53 -0500 Subject: [PATCH] Issue 103 SVM accepts soundings from bathymetric lidar --- .../java/org/tinfour/svm/SvmLasFilter.java | 141 ++++++++++++++++++ .../org/tinfour/svm/SvmRasterGeoTiff.java | 5 - .../svm/properties/SvmFileSpecification.java | 44 +++++- .../tinfour/svm/properties/SvmProperties.java | 2 +- .../org/tinfour/svm/SvmTemplate.properties | 13 ++ 5 files changed, 191 insertions(+), 14 deletions(-) create mode 100644 svm/src/main/java/org/tinfour/svm/SvmLasFilter.java diff --git a/svm/src/main/java/org/tinfour/svm/SvmLasFilter.java b/svm/src/main/java/org/tinfour/svm/SvmLasFilter.java new file mode 100644 index 00000000..38a774c4 --- /dev/null +++ b/svm/src/main/java/org/tinfour/svm/SvmLasFilter.java @@ -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; + } + + +} diff --git a/svm/src/main/java/org/tinfour/svm/SvmRasterGeoTiff.java b/svm/src/main/java/org/tinfour/svm/SvmRasterGeoTiff.java index 66f74b52..5b25efd9 100644 --- a/svm/src/main/java/org/tinfour/svm/SvmRasterGeoTiff.java +++ b/svm/src/main/java/org/tinfour/svm/SvmRasterGeoTiff.java @@ -390,9 +390,4 @@ private boolean writeAuxiliaryFiles( return true; } - - private boolean approxEquals(double v, double d) { - return Math.abs(v-d)<0.01; - } - } diff --git a/svm/src/main/java/org/tinfour/svm/properties/SvmFileSpecification.java b/svm/src/main/java/org/tinfour/svm/properties/SvmFileSpecification.java index e285b7f0..91e931c2 100644 --- a/svm/src/main/java/org/tinfour/svm/properties/SvmFileSpecification.java +++ b/svm/src/main/java/org/tinfour/svm/properties/SvmFileSpecification.java @@ -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; @@ -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; + } } } @@ -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) * diff --git a/svm/src/main/java/org/tinfour/svm/properties/SvmProperties.java b/svm/src/main/java/org/tinfour/svm/properties/SvmProperties.java index 691560b7..79af173b 100644 --- a/svm/src/main/java/org/tinfour/svm/properties/SvmProperties.java +++ b/svm/src/main/java/org/tinfour/svm/properties/SvmProperties.java @@ -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; diff --git a/svm/src/main/resources/org/tinfour/svm/SvmTemplate.properties b/svm/src/main/resources/org/tinfour/svm/SvmTemplate.properties index 5655294f..3a5c6d07 100644 --- a/svm/src/main/resources/org/tinfour/svm/SvmTemplate.properties +++ b/svm/src/main/resources/org/tinfour/svm/SvmTemplate.properties @@ -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. @@ -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