From 56d5a1330cfee95a887419f819a6af008b1ad65f Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Fri, 9 Feb 2024 15:00:00 -0500 Subject: [PATCH 01/39] in progress: adding new metrics --- src/silvimetric/resources/metric.py | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 1079024..3f9cb13 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -89,6 +89,51 @@ def m_max(data): def m_stddev(data): return np.std(data) +def m_variance(data): + return np.var(data) + +def m_cv(data): + return np.std(data) / np.mean(data) + +# TODO check performance of other methods +# syntax may be wrong...need np. somewhere? +def m_abovemean(data): + return (data > np.mean(data)).sum() / len(data) + +# TODO check performance of other methods +# syntax may be wrong...need np. somewhere? +def m_abovemode(data): + return (data > stats.mode(data).mode).sum() / len(data) + +def m_skewness(data): + return stats.skew(data) + +def m_kurtosis(data): + return stats.kurtosis(data) + +def m_aad(data) + m = np.mean(data) + return np.mean(np.absolute(data - m)) + +def m_madmedian(data) + return stats.median_abs_deviation(data) + +def m_madmean(data) + return stats.median_abs_deviation(data, center=mp.mean) + +def m_madmode(data) + return stats.median_abs_deviation(data, center=stats.mode) + +# TODO test various methods for interpolation=: I think the default +# matches FUSION method +def m_percentiles(data) + return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) + +# TODO test various methods for interpolation=: I think the default +# matches FUSION method +def m_iq(data) + return stats.iqr(data) + #TODO change to correct dtype Metrics = { 'mean' : Metric('mean', np.float32, m_mean), From 7001c93cc030a55a046b95a738b4913f79ce61e1 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Fri, 9 Feb 2024 15:11:30 -0500 Subject: [PATCH 02/39] in progress: fixing syntax error --- src/silvimetric/resources/metric.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 3f9cb13..f6d279c 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -111,27 +111,27 @@ def m_skewness(data): def m_kurtosis(data): return stats.kurtosis(data) -def m_aad(data) +def m_aad(data): m = np.mean(data) return np.mean(np.absolute(data - m)) -def m_madmedian(data) +def m_madmedian(data): return stats.median_abs_deviation(data) -def m_madmean(data) - return stats.median_abs_deviation(data, center=mp.mean) +def m_madmean(data): + return stats.median_abs_deviation(data, center=np.mean) -def m_madmode(data) +def m_madmode(data): return stats.median_abs_deviation(data, center=stats.mode) -# TODO test various methods for interpolation=: I think the default +# TODO test various methods for interpolation=... I think the default # matches FUSION method -def m_percentiles(data) +def m_percentiles(data): return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) -# TODO test various methods for interpolation=: I think the default +# TODO test various methods for interpolation=... I think the default # matches FUSION method -def m_iq(data) +def m_iq(data): return stats.iqr(data) #TODO change to correct dtype From 0d7403c65f132451b7fd3f0376774d3207bede6a Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Fri, 9 Feb 2024 15:16:44 -0500 Subject: [PATCH 03/39] added base FUSION metrics --- src/silvimetric/resources/metric.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index f6d279c..63af013 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -134,6 +134,14 @@ def m_percentiles(data): def m_iq(data): return stats.iqr(data) +def m_90m10(data): + p = np.percentile(data, [10,90]) + return p[1] - p[0] + +def m_95m05(data): + p = np.percentile(data, [5,95]) + return p[1] - p[0] + #TODO change to correct dtype Metrics = { 'mean' : Metric('mean', np.float32, m_mean), From 6c0a362f2dc8286f47f2e477566a98709694b47f Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Fri, 9 Feb 2024 15:22:20 -0500 Subject: [PATCH 04/39] comments --- src/silvimetric/resources/metric.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 63af013..3cd0c2f 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -126,6 +126,7 @@ def m_madmode(data): # TODO test various methods for interpolation=... I think the default # matches FUSION method +# not sure how an array of metrics will be ingested by shatter def m_percentiles(data): return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) From bbdbbe536a649fefbdebe4e68faf21b5282b4fc8 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Mon, 12 Feb 2024 15:03:27 -0500 Subject: [PATCH 05/39] added percentiles as separate functions --- src/silvimetric/resources/metric.py | 89 ++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 8 deletions(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 3cd0c2f..fd1d4a9 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -89,6 +89,7 @@ def m_max(data): def m_stddev(data): return np.std(data) +# start of new metrics to match FUSION def m_variance(data): return np.var(data) @@ -96,12 +97,10 @@ def m_cv(data): return np.std(data) / np.mean(data) # TODO check performance of other methods -# syntax may be wrong...need np. somewhere? def m_abovemean(data): return (data > np.mean(data)).sum() / len(data) # TODO check performance of other methods -# syntax may be wrong...need np. somewhere? def m_abovemode(data): return (data > stats.mode(data).mode).sum() / len(data) @@ -124,12 +123,6 @@ def m_madmean(data): def m_madmode(data): return stats.median_abs_deviation(data, center=stats.mode) -# TODO test various methods for interpolation=... I think the default -# matches FUSION method -# not sure how an array of metrics will be ingested by shatter -def m_percentiles(data): - return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) - # TODO test various methods for interpolation=... I think the default # matches FUSION method def m_iq(data): @@ -143,7 +136,60 @@ def m_95m05(data): p = np.percentile(data, [5,95]) return p[1] - p[0] +# TODO test various methods for interpolation=... I think the default +# matches FUSION method +# not sure how an array of metrics will be ingested by shatter +# so do these as 15 separate metrics. may be slower than doing all in one call +#def m_percentiles(data): +# return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) + +def m_p01(data): + return(np.percentile(data, 1)) + +def m_p05(data): + return(np.percentile(data, 5)) + +def m_p10(data): + return(np.percentile(data, 10)) + +def m_p20(data): + return(np.percentile(data, 20)) + +def m_p25(data): + return(np.percentile(data, 25)) + +def m_p30(data): + return(np.percentile(data, 30)) + +def m_p40(data): + return(np.percentile(data, 40)) + +def m_p50(data): + return(np.percentile(data, 50)) + +def m_p60(data): + return(np.percentile(data, 60)) + +def m_p70(data): + return(np.percentile(data, 70)) + +def m_p75(data): + return(np.percentile(data, 75)) + +def m_p80(data): + return(np.percentile(data, 80)) + +def m_p90(data): + return(np.percentile(data, 90)) + +def m_p95(data): + return(np.percentile(data, 95)) + +def m_p99(data): + return(np.percentile(data, 99)) + #TODO change to correct dtype +#TODO not sure what to do with percentiles since it is an array of values instead of a single value Metrics = { 'mean' : Metric('mean', np.float32, m_mean), 'mode' : Metric('mode', np.float32, m_mode), @@ -151,4 +197,31 @@ def m_95m05(data): 'min' : Metric('min', np.float32, m_min), 'max' : Metric('max', np.float32, m_max), 'stddev' : Metric('stddev', np.float32, m_stddev), + 'variance' : Metric('variance', np.float32, m_variance), + 'cv' : Metric('cv', np.float32, m_cv), + 'abovemean' : Metric('abovemean', np.float32, m_abovemean), + 'abovemode' : Metric('abovemode', np.float32, m_abovemode), + 'skewness' : Metric('skewness', np.float32, m_skewness), + 'kurtosis' : Metric('kurtosis', np.float32, m_kurtosis), + 'aad' : Metric('aad', np.float32, m_aad), + 'madmedian' : Metric('madmedian', np.float32, m_madmedian), + 'madmode' : Metric('madmode', np.float32, m_madmode), + 'iq' : Metric('iq', np.float32, m_iq, + '90m10' : Metric('90m10', np.float32, m_90m10), + '95m05' : Metric('95m05', np.float32, m_95m05), + 'p01' : Metric('p01', np.float32, m_p01), + 'p05' : Metric('p05', np.float32, m_p05), + 'p10' : Metric('p10', np.float32, m_p10), + 'p20' : Metric('p20', np.float32, m_p20), + 'p25' : Metric('p25', np.float32, m_p25), + 'p30' : Metric('p30', np.float32, m_p30), + 'p40' : Metric('p40', np.float32, m_p40), + 'p50' : Metric('p50', np.float32, m_p50), + 'p60' : Metric('p60', np.float32, m_p60), + 'p70' : Metric('p70', np.float32, m_p70), + 'p75' : Metric('p75', np.float32, m_p75), + 'p80' : Metric('p80', np.float32, m_p80), + 'p90' : Metric('p90', np.float32, m_p90), + 'p95' : Metric('p95', np.float32, m_p95), + 'p99' : Metric('p99', np.float32, m_p99), } \ No newline at end of file From 97beefa660bd19844b5f56f75f7538fc9cbe7828 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Mon, 12 Feb 2024 16:29:05 -0500 Subject: [PATCH 06/39] Corrected syntax metrics.py line 209 --- src/silvimetric/resources/metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index fd1d4a9..e8c816f 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -206,7 +206,7 @@ def m_p99(data): 'aad' : Metric('aad', np.float32, m_aad), 'madmedian' : Metric('madmedian', np.float32, m_madmedian), 'madmode' : Metric('madmode', np.float32, m_madmode), - 'iq' : Metric('iq', np.float32, m_iq, + 'iq' : Metric('iq', np.float32, m_iq), '90m10' : Metric('90m10', np.float32, m_90m10), '95m05' : Metric('95m05', np.float32, m_95m05), 'p01' : Metric('p01', np.float32, m_p01), From 43cd86682c8d71d5e5ca7d69f049c5dd5dfda6b0 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Tue, 13 Feb 2024 16:42:18 -0500 Subject: [PATCH 07/39] Added L-moment metrics and profile area --- src/silvimetric/resources/lmom4.py | 29 +++++++++ src/silvimetric/resources/metric.py | 96 +++++++++++++++++++++++++++-- 2 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 src/silvimetric/resources/lmom4.py diff --git a/src/silvimetric/resources/lmom4.py b/src/silvimetric/resources/lmom4.py new file mode 100644 index 0000000..2f0253c --- /dev/null +++ b/src/silvimetric/resources/lmom4.py @@ -0,0 +1,29 @@ +import numpy as np + +# adapted from: https://xiaoganghe.github.io/python-climate-visuals/chapters/data-analytics/scipy-basic.html +# added b3 and l4 from: +# Vogel, R. M. and Fennessey, N. M.: L moment diagrams should replace product moment diagrams, +# Water Resour. Res., 29, 1745–1752, https://doi.org/10.1029/93WR00341, 1993. +def lmom4(data): + # lmom4 returns the first four L-moments of data + # data is the 1-d array + # n is the total number of points in data, j is the j_th point + # + # j range in for loops starts with 1 so we need to subtract 1 for all b# equations + + n = len(data) + # sort in descending order + data = np.sort(data.reshape(n))[::-1] + b0 = np.mean(data) + b1 = np.array([(n - j - 1) * data[j] / n / (n - 1) + for j in range(n)]).sum() + b2 = np.array([(n - j - 1) * (n - j - 2) * data[j] / n / (n - 1) / (n - 2) + for j in range(n - 1)]).sum() + b3 = np.array([(n - j - 1) * (n - j - 2) * (n - j - 3) * data[j] / n / (n - 1) / (n - 2) / (n - 3) + for j in range(n - 2)]).sum() + l1 = b0 + l2 = 2 * b1 - b0 + l3 = 6 * (b2 - b1) + b0 + l4 = 20 * b3 - 30 * b2 + 12 * b1 - b0 + + return l1, l2, l3, l4 diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index e8c816f..97584c2 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -10,6 +10,7 @@ import dill from .entry import Attribute, Entry +from .lmom4 import lmom4 MetricFn = Callable[[np.ndarray, Optional[Union[Any, None]]], np.ndarray] @@ -123,8 +124,8 @@ def m_madmean(data): def m_madmode(data): return stats.median_abs_deviation(data, center=stats.mode) -# TODO test various methods for interpolation=... I think the default -# matches FUSION method +# TODO test various methods for interpolation=... for all percentile-related metrics +# I think the default matches FUSION method but need to test def m_iq(data): return stats.iqr(data) @@ -136,9 +137,54 @@ def m_95m05(data): p = np.percentile(data, [5,95]) return p[1] - p[0] -# TODO test various methods for interpolation=... I think the default -# matches FUSION method -# not sure how an array of metrics will be ingested by shatter +def m_crr(data): + return (np.mean(data) - np.min(data)) / (np.max(data) - np-min(data)) + +def m_sqmean(data): + return np.sqrt(np.mean(np.square(data))) + +def m_cumean(data): + return np.cbrt(np.mean(np.power(np.absolute(data), 3))) + +# TODO compute L-moments. These are done separately because we only add +# a single element to TileDB. This is very inefficient since we have to +# compute all L-moments at once. Ideally, we would have a single metric +# function that returns an array with 7 values + +# added code to compute first 4 l-moments in lmom4.py. There is a package, +# lmoments3 that can compute the same values but I wanted to avoid the +# package as it has some IBM copyright requirements (need to include copyright +# statement with derived works) + +# L1 is same as mean...compute using np.mean for speed +def m_l1(data): + return np.mean(data) + +def m_l2(data): + l = lmom4(data) + return l[1] + +def m_l3(data): + l = lmom4(data) + return l[2] + +def m_l4(data): + l = lmom4(data) + return l[3] + +def m_lcv(data): + l = lmom4(data) + return l[1] / l[0] + +def m_lskewness(data): + l = lmom4(data) + return l[2] / l[1] + +def m_lkurtosis(data): + l = lmom4(data) + return l[3] / l[1] + +# not sure how an array of metrics can be ingested by shatter # so do these as 15 separate metrics. may be slower than doing all in one call #def m_percentiles(data): # return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) @@ -188,6 +234,34 @@ def m_p95(data): def m_p99(data): return(np.percentile(data, 99)) +def m_profilearea(data): + # sanity check...must have valid heights/elevations + if np.max(data) <= 0: + return -9999.0 + + + p = np.percentile(data, range(1, 99)) + p0 = max(np.min(data), 0.0) + + # second sanity check...99th percentile must be > 0 + if p[98] > 0.0: + # compute area under normalized percentile height curve using composite trapeziod rule + pa = p0 / p[98] + for ip in p[:97]: + pa += 2.0 * ip / p[98] + pa += 1.0 + + return pa * 0.5 + else: + return -9999.0 + + +# TODO example for cover using all returns and a height threshold +# the threshold must be a parameter and not hardcoded +def m_cover(data): + threshold = 2 + return (data > threshold).sum() / len(data) + #TODO change to correct dtype #TODO not sure what to do with percentiles since it is an array of values instead of a single value Metrics = { @@ -207,6 +281,16 @@ def m_p99(data): 'madmedian' : Metric('madmedian', np.float32, m_madmedian), 'madmode' : Metric('madmode', np.float32, m_madmode), 'iq' : Metric('iq', np.float32, m_iq), + 'crr' : Metric('crr', np.float32, m_crr), + 'sqmean' : Metric('sqmean', np.float32, m_sqmean), + 'cumean' : Metric('cumean', np.float32, m_cumean), + 'l1' : Metric('l1', np.float32, m_l1), + 'l2' : Metric('l2', np.float32, m_l2), + 'l3' : Metric('l3', np.float32, m_l3), + 'l4' : Metric('l4', np.float32, m_l4), + 'lcv' : Metric('iq', np.float32, m_lcv), + 'lskewness' : Metric('lskewness', np.float32, m_lskewness), + 'lkurtosis' : Metric('lkurtosis', np.float32, m_lkurtosis), '90m10' : Metric('90m10', np.float32, m_90m10), '95m05' : Metric('95m05', np.float32, m_95m05), 'p01' : Metric('p01', np.float32, m_p01), @@ -224,4 +308,6 @@ def m_p99(data): 'p90' : Metric('p90', np.float32, m_p90), 'p95' : Metric('p95', np.float32, m_p95), 'p99' : Metric('p99', np.float32, m_p99), + 'cover' : Metric('cover', np.float32, m_cover), + 'profilearea' : Metric('profilearea', np.float32, m_profilearea), } \ No newline at end of file From 7e25d700590ad330df9997db38cd7d0ce8c4f894 Mon Sep 17 00:00:00 2001 From: kylemann16 Date: Thu, 15 Feb 2024 09:24:07 -0600 Subject: [PATCH 08/39] Fixing a few metrics, removing errant breakpoint, adding test for metrics --- src/silvimetric/commands/scan.py | 1 - src/silvimetric/resources/metric.py | 15 ++++--- tests/test_metrics.py | 69 +++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 tests/test_metrics.py diff --git a/src/silvimetric/commands/scan.py b/src/silvimetric/commands/scan.py index 1de56ea..66ef3de 100644 --- a/src/silvimetric/commands/scan.py +++ b/src/silvimetric/commands/scan.py @@ -17,7 +17,6 @@ def scan(tdb_dir, pointcloud, bounds, point_count=600000, resolution=100, if filter: chunks = extents.chunk(data, resolution, point_count, depth) - breakpoint() cell_counts = [ch.cell_count for ch in chunks] else: diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 97584c2..b5501f1 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -21,7 +21,7 @@ class Metric(Entry): def __init__(self, name: str, dtype: np.dtype, method: MetricFn, deps: list[Attribute]=None): super().__init__() self.name = name - self.dtype = dtype + self.dtype = dtype = np.float32 self.dependencies = deps self._method = method @@ -122,7 +122,8 @@ def m_madmean(data): return stats.median_abs_deviation(data, center=np.mean) def m_madmode(data): - return stats.median_abs_deviation(data, center=stats.mode) + stats_mode = lambda v,axis : stats.mode(v,axis).mode + return stats.median_abs_deviation(data, center=stats_mode) # TODO test various methods for interpolation=... for all percentile-related metrics # I think the default matches FUSION method but need to test @@ -138,7 +139,7 @@ def m_95m05(data): return p[1] - p[0] def m_crr(data): - return (np.mean(data) - np.min(data)) / (np.max(data) - np-min(data)) + return (np.mean(data) - np.min(data)) / (np.max(data) - np.min(data)) def m_sqmean(data): return np.sqrt(np.mean(np.square(data))) @@ -147,12 +148,12 @@ def m_cumean(data): return np.cbrt(np.mean(np.power(np.absolute(data), 3))) # TODO compute L-moments. These are done separately because we only add -# a single element to TileDB. This is very inefficient since we have to +# a single element to TileDB. This is very inefficient since we have to # compute all L-moments at once. Ideally, we would have a single metric # function that returns an array with 7 values # added code to compute first 4 l-moments in lmom4.py. There is a package, -# lmoments3 that can compute the same values but I wanted to avoid the +# lmoments3 that can compute the same values but I wanted to avoid the # package as it has some IBM copyright requirements (need to include copyright # statement with derived works) @@ -240,7 +241,7 @@ def m_profilearea(data): return -9999.0 - p = np.percentile(data, range(1, 99)) + p = np.percentile(data, range(1, 100)) p0 = max(np.min(data), 0.0) # second sanity check...99th percentile must be > 0 @@ -288,7 +289,7 @@ def m_cover(data): 'l2' : Metric('l2', np.float32, m_l2), 'l3' : Metric('l3', np.float32, m_l3), 'l4' : Metric('l4', np.float32, m_l4), - 'lcv' : Metric('iq', np.float32, m_lcv), + 'lcv' : Metric('lcv', np.float32, m_lcv), 'lskewness' : Metric('lskewness', np.float32, m_lskewness), 'lkurtosis' : Metric('lkurtosis', np.float32, m_lkurtosis), '90m10' : Metric('90m10', np.float32, m_90m10), diff --git a/tests/test_metrics.py b/tests/test_metrics.py new file mode 100644 index 0000000..3d9ca99 --- /dev/null +++ b/tests/test_metrics.py @@ -0,0 +1,69 @@ +import pytest +import numpy as np +import pandas as pd + +from silvimetric.resources import Metrics, Log, ShatterConfig, StorageConfig, Storage +from silvimetric.commands import shatter +from silvimetric import __version__ as svversion + +@pytest.fixture(scope='function') +def metric_input(): + yield np.array([1,1,1,2,3,4,5,6,7,8,9,10]) + +@pytest.fixture(scope='function') +def metric_st_config(tdb_filepath, bounds, resolution, crs, attrs, metrics): + log = Log(20) + yield StorageConfig(tdb_dir = tdb_filepath, + log = log, + crs = crs, + root = bounds, + resolution = resolution, + attrs = attrs, + version = svversion) + +@pytest.fixture(scope="function") +def metric_storage(metric_st_config) -> Storage: + yield Storage.create(metric_st_config) + +@pytest.fixture(scope='function') +def metric_shatter(tdb_filepath, copc_filepath, metric_st_config, bounds, app_config, metric_storage, date): + log = Log(20) # INFO + s = ShatterConfig(tdb_dir = tdb_filepath, + log = log, + filename = copc_filepath, + attrs = metric_st_config.attrs, + metrics = metric_st_config.metrics, + bounds=bounds, + debug = True, + date=date) + + yield s + +class Test_Metrics(object): + + def test_input_output(self, metric_input): + for m in Metrics: + try: + out = Metrics[m]._method(metric_input) + msg = f"Metric {Metrics[m].name} does not output a valid data type."\ + f" Interpretted type: {type(out)}" + assert not any([ + isinstance(out, np.ndarray), + isinstance(out, list), + isinstance(out, tuple), + isinstance(out, pd.Series), + isinstance(out, pd.DataFrame), + isinstance(out, str) + ]), msg + assert any(( + isinstance(out, np.number), + isinstance(out, int), + isinstance(out, float), + isinstance(out, complex), + isinstance(out, bool) + )), msg + except: + print('yi') + + def test_metric_shatter(self, metric_shatter): + shatter.shatter(metric_shatter) \ No newline at end of file From 8afcf4e722acbb36826b6c1d97500af1adafc440 Mon Sep 17 00:00:00 2001 From: kylemann16 Date: Thu, 15 Feb 2024 14:43:59 -0600 Subject: [PATCH 09/39] fixing metric test, removing mode method for now --- src/silvimetric/resources/metric.py | 106 ++++++++++++++-------------- tests/test_metrics.py | 53 +++++++++----- 2 files changed, 91 insertions(+), 68 deletions(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index b5501f1..b9bb80a 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -6,7 +6,6 @@ from tiledb import Attr import dask import base64 -import pickle import dill from .entry import Attribute, Entry @@ -18,12 +17,13 @@ ## TODO should create list of metrics as classes that derive from Metric? class Metric(Entry): - def __init__(self, name: str, dtype: np.dtype, method: MetricFn, deps: list[Attribute]=None): + def __init__(self, name: str, method: MetricFn, dtype: np.dtype=np.float32, + deps: list[Attribute]=None): super().__init__() self.name = name - self.dtype = dtype = np.float32 - self.dependencies = deps self._method = method + self.dtype = dtype + self.dependencies = deps def schema(self, attr: Attribute): entry_name = self.entry_name(attr.name) @@ -56,7 +56,7 @@ def from_string(data: Union[str, dict]): dependencies = j['dependencies'] method = dill.loads(base64.b64decode(j['method'].encode())) - return Metric(name, dtype, method, dependencies) + return Metric(name=name, method=method, dtype=dtype, deps=dependencies) def __eq__(self, other): return (self.name == other.name and @@ -75,8 +75,10 @@ def __repr__(self) -> str: def m_mean(data): return np.mean(data) -def m_mode(data): - return stats.mode(data).mode +# TODO this currently returns a list if multiple modes exist, disable until +# we support lists +# def m_mode(data): + # return stats.mode(data).mode def m_median(data): return np.median(data) @@ -266,49 +268,49 @@ def m_cover(data): #TODO change to correct dtype #TODO not sure what to do with percentiles since it is an array of values instead of a single value Metrics = { - 'mean' : Metric('mean', np.float32, m_mean), - 'mode' : Metric('mode', np.float32, m_mode), - 'median' : Metric('median', np.float32, m_median), - 'min' : Metric('min', np.float32, m_min), - 'max' : Metric('max', np.float32, m_max), - 'stddev' : Metric('stddev', np.float32, m_stddev), - 'variance' : Metric('variance', np.float32, m_variance), - 'cv' : Metric('cv', np.float32, m_cv), - 'abovemean' : Metric('abovemean', np.float32, m_abovemean), - 'abovemode' : Metric('abovemode', np.float32, m_abovemode), - 'skewness' : Metric('skewness', np.float32, m_skewness), - 'kurtosis' : Metric('kurtosis', np.float32, m_kurtosis), - 'aad' : Metric('aad', np.float32, m_aad), - 'madmedian' : Metric('madmedian', np.float32, m_madmedian), - 'madmode' : Metric('madmode', np.float32, m_madmode), - 'iq' : Metric('iq', np.float32, m_iq), - 'crr' : Metric('crr', np.float32, m_crr), - 'sqmean' : Metric('sqmean', np.float32, m_sqmean), - 'cumean' : Metric('cumean', np.float32, m_cumean), - 'l1' : Metric('l1', np.float32, m_l1), - 'l2' : Metric('l2', np.float32, m_l2), - 'l3' : Metric('l3', np.float32, m_l3), - 'l4' : Metric('l4', np.float32, m_l4), - 'lcv' : Metric('lcv', np.float32, m_lcv), - 'lskewness' : Metric('lskewness', np.float32, m_lskewness), - 'lkurtosis' : Metric('lkurtosis', np.float32, m_lkurtosis), - '90m10' : Metric('90m10', np.float32, m_90m10), - '95m05' : Metric('95m05', np.float32, m_95m05), - 'p01' : Metric('p01', np.float32, m_p01), - 'p05' : Metric('p05', np.float32, m_p05), - 'p10' : Metric('p10', np.float32, m_p10), - 'p20' : Metric('p20', np.float32, m_p20), - 'p25' : Metric('p25', np.float32, m_p25), - 'p30' : Metric('p30', np.float32, m_p30), - 'p40' : Metric('p40', np.float32, m_p40), - 'p50' : Metric('p50', np.float32, m_p50), - 'p60' : Metric('p60', np.float32, m_p60), - 'p70' : Metric('p70', np.float32, m_p70), - 'p75' : Metric('p75', np.float32, m_p75), - 'p80' : Metric('p80', np.float32, m_p80), - 'p90' : Metric('p90', np.float32, m_p90), - 'p95' : Metric('p95', np.float32, m_p95), - 'p99' : Metric('p99', np.float32, m_p99), - 'cover' : Metric('cover', np.float32, m_cover), - 'profilearea' : Metric('profilearea', np.float32, m_profilearea), + 'mean' : Metric('mean', m_mean), + # 'mode' : Metric('mode', m_mode), + 'median' : Metric('median', m_median), + 'min' : Metric('min', m_min), + 'max' : Metric('max', m_max), + 'stddev' : Metric('stddev', m_stddev), + 'variance' : Metric('variance', m_variance), + 'cv' : Metric('cv', m_cv), + 'abovemean' : Metric('abovemean', m_abovemean), + 'abovemode' : Metric('abovemode', m_abovemode), + 'skewness' : Metric('skewness', m_skewness), + 'kurtosis' : Metric('kurtosis', m_kurtosis), + 'aad' : Metric('aad', m_aad), + 'madmedian' : Metric('madmedian', m_madmedian), + 'madmode' : Metric('madmode', m_madmode), + 'iq' : Metric('iq', m_iq), + 'crr' : Metric('crr', m_crr), + 'sqmean' : Metric('sqmean', m_sqmean), + 'cumean' : Metric('cumean', m_cumean), + 'l1' : Metric('l1', m_l1), + 'l2' : Metric('l2', m_l2), + 'l3' : Metric('l3', m_l3), + 'l4' : Metric('l4', m_l4), + 'lcv' : Metric('lcv', m_lcv), + 'lskewness' : Metric('lskewness', m_lskewness), + 'lkurtosis' : Metric('lkurtosis', m_lkurtosis), + '90m10' : Metric('90m10', m_90m10), + '95m05' : Metric('95m05', m_95m05), + 'p01' : Metric('p01', m_p01), + 'p05' : Metric('p05', m_p05), + 'p10' : Metric('p10', m_p10), + 'p20' : Metric('p20', m_p20), + 'p25' : Metric('p25', m_p25), + 'p30' : Metric('p30', m_p30), + 'p40' : Metric('p40', m_p40), + 'p50' : Metric('p50', m_p50), + 'p60' : Metric('p60', m_p60), + 'p70' : Metric('p70', m_p70), + 'p75' : Metric('p75', m_p75), + 'p80' : Metric('p80', m_p80), + 'p90' : Metric('p90', m_p90), + 'p95' : Metric('p95', m_p95), + 'p99' : Metric('p99', m_p99), + 'cover' : Metric('cover', m_cover), + 'profilearea' : Metric('profilearea', m_profilearea), } \ No newline at end of file diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 3d9ca99..16be781 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -1,41 +1,61 @@ +import os import pytest import numpy as np import pandas as pd +import dask -from silvimetric.resources import Metrics, Log, ShatterConfig, StorageConfig, Storage +from silvimetric.resources import Metrics, Log, ShatterConfig, StorageConfig +from silvimetric.resources import Storage, Bounds, Extents from silvimetric.commands import shatter from silvimetric import __version__ as svversion @pytest.fixture(scope='function') def metric_input(): - yield np.array([1,1,1,2,3,4,5,6,7,8,9,10]) + # randomly generated int array + yield np.array([54, 64, 54, 18, 76, 82, 85, 12, 7, 32, 89, 79, 78, 58, 17, 5, 77, + 20, 63, 87, 37, 27, 7, 22, 34, 61, 52, 64, 65, 90, 76, 88, 0, 98, + 78, 26, 88, 44, 78, 54, 38, 28, 94, 74, 90, 84, 30, 7, 81, 75, 22, + 12, 67, 10, 46, 62, 79, 52, 24, 88, 4, 73, 75, 35, 75, 16, 66, 43, + 28, 72, 15, 80, 53, 1, 28, 70, 20, 42, 83, 80, 82, 2, 41, 38, 23, + 17, 18, 19, 43, 30, 88, 41, 60, 29, 93, 27, 1, 13, 93, 82], np.int32) + +@pytest.fixture(scope='session') +def autzen_filepath() -> str: + path = os.path.join(os.path.dirname(__file__), "data", + "autzen-small.copc.laz") + assert os.path.exists(path) + yield os.path.abspath(path) + +@pytest.fixture(scope='function') +def autzen_bounds(): + yield Bounds(635579.2, 848884.83, 639003.73, 853536.21) @pytest.fixture(scope='function') -def metric_st_config(tdb_filepath, bounds, resolution, crs, attrs, metrics): +def metric_st_config(tdb_filepath, autzen_bounds, resolution, crs, attrs, metrics): log = Log(20) yield StorageConfig(tdb_dir = tdb_filepath, log = log, crs = crs, - root = bounds, + root = autzen_bounds, resolution = resolution, attrs = attrs, version = svversion) -@pytest.fixture(scope="function") -def metric_storage(metric_st_config) -> Storage: - yield Storage.create(metric_st_config) - @pytest.fixture(scope='function') -def metric_shatter(tdb_filepath, copc_filepath, metric_st_config, bounds, app_config, metric_storage, date): +def metric_shatter(tdb_filepath, autzen_filepath, metric_st_config, autzen_bounds, app_config, date): log = Log(20) # INFO + Storage.create(metric_st_config) + e = Extents(autzen_bounds, 30, autzen_bounds) + e1 = e.split()[1] s = ShatterConfig(tdb_dir = tdb_filepath, - log = log, - filename = copc_filepath, - attrs = metric_st_config.attrs, - metrics = metric_st_config.metrics, - bounds=bounds, - debug = True, - date=date) + log=log, + filename=autzen_filepath, + attrs=metric_st_config.attrs, + metrics=metric_st_config.metrics, + bounds=e1.bounds, + debug=True, + date=date, + tile_size=700) yield s @@ -66,4 +86,5 @@ def test_input_output(self, metric_input): print('yi') def test_metric_shatter(self, metric_shatter): + dask.config.set(scheduler='processes') shatter.shatter(metric_shatter) \ No newline at end of file From be8d7f9ca534d9244b9402b1e28cb25fe11f7622 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Tue, 5 Mar 2024 15:39:45 -0500 Subject: [PATCH 10/39] Fixing divide by zero errors in metrics --- .gitignore | 3 +++ src/silvimetric/resources/metric.py | 42 ++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 11eefa1..51d213c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,5 +18,8 @@ stats/ **/tifs_test/** autzen-classified.copc.laz +#sample metrics +metrics/ +metrics_aligned/ .DS_Store diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index b9bb80a..0fa6c5d 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -78,7 +78,7 @@ def m_mean(data): # TODO this currently returns a list if multiple modes exist, disable until # we support lists # def m_mode(data): - # return stats.mode(data).mode +# return stats.mode(data).mode def m_median(data): return np.median(data) @@ -141,7 +141,12 @@ def m_95m05(data): return p[1] - p[0] def m_crr(data): - return (np.mean(data) - np.min(data)) / (np.max(data) - np.min(data)) + maxv = np.max(data) + minv = np.min(data) + if minv == maxv: + return -9999.0 + + return (np.mean(data) - minv) / (maxv - minv) def m_sqmean(data): return np.sqrt(np.mean(np.square(data))) @@ -161,30 +166,61 @@ def m_cumean(data): # L1 is same as mean...compute using np.mean for speed def m_l1(data): + if len(data) < 4: + return -9999.0 + return np.mean(data) def m_l2(data): + if len(data) < 4: + return -9999.0 + l = lmom4(data) return l[1] def m_l3(data): + if len(data) < 4: + return -9999.0 + l = lmom4(data) return l[2] def m_l4(data): + if len(data) < 4: + return -9999.0 + l = lmom4(data) return l[3] def m_lcv(data): + if len(data) < 4: + return -9999.0 + l = lmom4(data) + + if l[0] == 0.0: + return -9999.0 + return l[1] / l[0] def m_lskewness(data): + if len(data) < 4: + return -9999.0 + l = lmom4(data) + if l[1] == 0.0: + return -9999.0 + return l[2] / l[1] def m_lkurtosis(data): + if len(data) < 4: + return -9999.0 + l = lmom4(data) + if l[1] == 0.0: + return -9999.0 + return l[3] / l[1] # not sure how an array of metrics can be ingested by shatter @@ -313,4 +349,4 @@ def m_cover(data): 'p99' : Metric('p99', m_p99), 'cover' : Metric('cover', m_cover), 'profilearea' : Metric('profilearea', m_profilearea), -} \ No newline at end of file +} From 74697d3ad281fa374f685142386b6a3d65610c0b Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Tue, 5 Mar 2024 16:47:23 -0500 Subject: [PATCH 11/39] Added simple count and check on number of points for skewness and kurtosis --- src/silvimetric/resources/metric.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 0fa6c5d..06f8320 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -72,6 +72,9 @@ def __repr__(self) -> str: #TODO add all metrics from https://github.com/hobuinc/silvimetric/issues/5 +def m_count(data): + return len(data) + def m_mean(data): return np.mean(data) @@ -108,9 +111,15 @@ def m_abovemode(data): return (data > stats.mode(data).mode).sum() / len(data) def m_skewness(data): + if len(data) < 4: + return -9999.0 + return stats.skew(data) def m_kurtosis(data): + if len(data) < 4: + return -9999.0 + return stats.kurtosis(data) def m_aad(data): From f77ff71c1424961e83229f8b5f0b0b9dc74574e6 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Wed, 6 Mar 2024 16:01:04 -0500 Subject: [PATCH 12/39] added htthreshhold and coverthreshold to initialize cli --- docs/source/cli/initialize.rst | 2 ++ docs/source/tutorial.rst | 8 +++++--- src/silvimetric/cli/cli.py | 11 ++++++++-- src/silvimetric/resources/config.py | 6 ++++++ src/silvimetric/resources/metric.py | 32 +++++++++++++++++++++-------- tests/conftest.py | 14 ++++++++++++- 6 files changed, 59 insertions(+), 14 deletions(-) diff --git a/docs/source/cli/initialize.rst b/docs/source/cli/initialize.rst index 97de933..6b8fe1f 100644 --- a/docs/source/cli/initialize.rst +++ b/docs/source/cli/initialize.rst @@ -28,6 +28,8 @@ Synopsis -a, --attributes ATTRS List of attributes to include in Database -m, --metrics METRICS List of metrics to include in Database --resolution FLOAT Summary pixel resolution + --htthreshold FLOAT Height threshold for all metrics + --coverthreshold FLOAT Height threshold for cover metrics --help Show this message and exit. diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index 6a380ab..b09c789 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -140,6 +140,8 @@ Usage: -a, --attributes ATTRS List of attributes to include in Database -m, --metrics METRICS List of metrics to include in Database --resolution FLOAT Summary pixel resolution + --htthreshold FLOAT Height threshold for all metrics + --coverthreshold FLOAT Height threshold for cover metrics --help Show this message and exit. @@ -258,9 +260,9 @@ Shatter SilviMetric will take all the previously defined variables like the bounds, resolution, and our tile size, and it will split all data values up into their respective bins. From here, SilviMetric will perform each `Metric` previously -defined in :ref:`initialize` over the data in each cell. At the end of all that, -this data will be written to a `SparseArray` in `TileDB`, where it will be much -easier to access. +defined in :ref:`initialize` over the data in each cell, applying the --htthreshold +and coverthreshold. At the end of all that, this data will be written to a +`SparseArray` in `TileDB`, where it will be much easier to access. Usage: diff --git a/src/silvimetric/cli/cli.py b/src/silvimetric/cli/cli.py index 7cd246c..4bc512b 100644 --- a/src/silvimetric/cli/cli.py +++ b/src/silvimetric/cli/cli.py @@ -102,9 +102,14 @@ def scan_cmd(app, resolution, point_count, pointcloud, bounds, depth, filter): help="List of metrics to include in Database") @click.option("--resolution", type=float, default=30.0, help="Summary pixel resolution") +@click.option("--htthreshold", type=float, default=2.0, + help="Height threshold for all metrics") +@click.option("--coverthreshold", type=float, default=2.0, + help="Height threshold for cover metrics") @click.pass_obj def initialize_cmd(app: ApplicationConfig, bounds: Bounds, crs: pyproj.CRS, - attributes: list[Attribute], resolution: float, metrics: list[Metric]): + attributes: list[Attribute], resolution: float, htthreshold: float, + coverthreshold: float, metrics: list[Metric]): import itertools """Initialize silvimetrics DATABASE """ @@ -114,7 +119,9 @@ def initialize_cmd(app: ApplicationConfig, bounds: Bounds, crs: pyproj.CRS, crs = crs, attrs = attributes, metrics = list(itertools.chain(*metrics)), - resolution = resolution) + resolution = resolution, + htthreshold = htthreshold, + coverthreshold = coverthreshold) return initialize.initialize(storageconfig) diff --git a/src/silvimetric/resources/config.py b/src/silvimetric/resources/config.py index 5d5b801..d21f8b1 100644 --- a/src/silvimetric/resources/config.py +++ b/src/silvimetric/resources/config.py @@ -53,6 +53,8 @@ class StorageConfig(Config): root: Bounds crs: pyproj.CRS resolution: float = 30.0 + htthreshold: float = 2.0 + coverthreshold: float = 2.0 attrs: list[Attribute] = field(default_factory=lambda: [ Attribute(a, Attributes[a].dtype) @@ -122,6 +124,8 @@ def from_string(cls, data: str): root = root, log = Log(**x['log']), resolution = x['resolution'], + htthreshold = x['htthreshold'], + coverthreshold = x['coverthreshold'], attrs = attrs, crs = crs, metrics = ms, @@ -245,6 +249,8 @@ def __post_init__(self) -> None: p.mkdir(parents=True, exist_ok=True) self.resolution: float = config.resolution + self.htthreshold: float = config.htthreshold + self.coverthreshold: float = config.coverthreshold self.crs: pyproj.CRS = config.crs def to_json(self): diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 06f8320..65918ba 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -78,10 +78,25 @@ def m_count(data): def m_mean(data): return np.mean(data) -# TODO this currently returns a list if multiple modes exist, disable until -# we support lists -# def m_mode(data): -# return stats.mode(data).mode +# mode is somewhat undefined for floating point values. FUSION logic is to +# partition data into 64 bins then find the bin with the highest count. +# This approach has the disadvantage that the bin size varies depending on +# the Z range. +# Before binning, subtract min value. Computing the scaled value involves +# the bin number * bin width (max - min / #bins) + min. +def m_mode(data): + nbins = 64 + maxv = np.max(data) + minv = np.min(data) + if minv == maxv: + return minv + + bins = np.histogram(data, bins = nbins, density = False) + + thebin = np.argmax(data, keepdims=False) + + # compute the height and return...bins - 1 is to get the bottom Z of the bin + return minv + thebin * (maxv - minv) / (nbins - 1) def m_median(data): return np.median(data) @@ -108,7 +123,7 @@ def m_abovemean(data): # TODO check performance of other methods def m_abovemode(data): - return (data > stats.mode(data).mode).sum() / len(data) + return (data > m_mode(data)).sum() / len(data) def m_skewness(data): if len(data) < 4: @@ -133,7 +148,7 @@ def m_madmean(data): return stats.median_abs_deviation(data, center=np.mean) def m_madmode(data): - stats_mode = lambda v,axis : stats.mode(v,axis).mode + stats_mode = m_mode(data) return stats.median_abs_deviation(data, center=stats_mode) # TODO test various methods for interpolation=... for all percentile-related metrics @@ -306,13 +321,14 @@ def m_profilearea(data): # TODO example for cover using all returns and a height threshold # the threshold must be a parameter and not hardcoded -def m_cover(data): +def m_allcover(data): threshold = 2 return (data > threshold).sum() / len(data) #TODO change to correct dtype #TODO not sure what to do with percentiles since it is an array of values instead of a single value Metrics = { + 'count' : Metric('count', m_count), 'mean' : Metric('mean', m_mean), # 'mode' : Metric('mode', m_mode), 'median' : Metric('median', m_median), @@ -356,6 +372,6 @@ def m_cover(data): 'p90' : Metric('p90', m_p90), 'p95' : Metric('p95', m_p95), 'p99' : Metric('p99', m_p99), - 'cover' : Metric('cover', m_cover), + 'allcover' : Metric('allcover', m_allcover), 'profilearea' : Metric('profilearea', m_profilearea), } diff --git a/tests/conftest.py b/tests/conftest.py index 306c52a..106a814 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,13 +24,15 @@ def threaded_dask(): dask.config.set(scheduler="threads") @pytest.fixture(scope='function') -def storage_config(tdb_filepath, bounds, resolution, crs, attrs, metrics): +def storage_config(tdb_filepath, bounds, resolution, htthreshold, coverthreshold, crs, attrs, metrics): log = Log(20) yield StorageConfig(tdb_dir = tdb_filepath, log = log, crs = crs, root = bounds, resolution = resolution, + htthreshold = htthreshold, + coverthreshold = coverthreshold, attrs = attrs, metrics = metrics, version = svversion) @@ -68,6 +70,8 @@ def uneven_storage_config(tdb_filepath, bounds, crs, attrs, metrics): crs = crs, root = bounds, resolution = 7, + htthreshold = 2, + coverthreshold = 2, attrs = attrs, metrics = metrics, version = svversion) @@ -152,6 +156,14 @@ def dims(): def resolution() -> int: yield 30 +@pytest.fixture(scope='class') +def htthreshold() -> int: + yield 2 + +@pytest.fixture(scope='class') +def coverthreshold() -> int: + yield 2 + @pytest.fixture(scope='class') def test_point_count() -> int: yield 90000 From ca522f4f7afba09a062e478fee7b59454f601d3e Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Wed, 6 Mar 2024 16:34:54 -0500 Subject: [PATCH 13/39] continuing with adding thresholds to metric functions --- src/silvimetric/commands/shatter.py | 2 +- src/silvimetric/resources/metric.py | 99 ++++++++++++++--------------- 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/silvimetric/commands/shatter.py b/src/silvimetric/commands/shatter.py index c3a3389..f1c9642 100644 --- a/src/silvimetric/commands/shatter.py +++ b/src/silvimetric/commands/shatter.py @@ -74,7 +74,7 @@ def get_metrics(data_in, attrs: list[str], storage: Storage): # doing dask compute inside the dict array because it was too fine-grained # when it was outside metric_data = { - f'{m.entry_name(attr)}': [m(cell_data) for cell_data in data[attr]] + f'{m.entry_name(attr)}': [m(cell_data, storage.config.htthreshold, storage.config.coverthreshold) for cell_data in data[attr]] for attr in attrs for m in storage.config.metrics } data_out = data | metric_data diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 65918ba..b2f6d51 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -11,7 +11,7 @@ from .entry import Attribute, Entry from .lmom4 import lmom4 -MetricFn = Callable[[np.ndarray, Optional[Union[Any, None]]], np.ndarray] +MetricFn = Callable[[np.ndarray, float, float, Optional[Union[Any, None]]], np.ndarray] # Derived information about a cell of points @@ -72,10 +72,10 @@ def __repr__(self) -> str: #TODO add all metrics from https://github.com/hobuinc/silvimetric/issues/5 -def m_count(data): +def m_count(data, htthreshold, coverthreshold): return len(data) -def m_mean(data): +def m_mean(data, htthreshold, coverthreshold): return np.mean(data) # mode is somewhat undefined for floating point values. FUSION logic is to @@ -84,7 +84,7 @@ def m_mean(data): # the Z range. # Before binning, subtract min value. Computing the scaled value involves # the bin number * bin width (max - min / #bins) + min. -def m_mode(data): +def m_mode(data, htthreshold, coverthreshold): nbins = 64 maxv = np.max(data) minv = np.min(data) @@ -98,73 +98,73 @@ def m_mode(data): # compute the height and return...bins - 1 is to get the bottom Z of the bin return minv + thebin * (maxv - minv) / (nbins - 1) -def m_median(data): +def m_median(data, htthreshold, coverthreshold): return np.median(data) -def m_min(data): +def m_min(data, htthreshold, coverthreshold): return np.min(data) -def m_max(data): +def m_max(data, htthreshold, coverthreshold): return np.max(data) -def m_stddev(data): +def m_stddev(data, htthreshold, coverthreshold): return np.std(data) # start of new metrics to match FUSION -def m_variance(data): +def m_variance(data, htthreshold, coverthreshold): return np.var(data) -def m_cv(data): +def m_cv(data, htthreshold, coverthreshold): return np.std(data) / np.mean(data) # TODO check performance of other methods -def m_abovemean(data): +def m_abovemean(data, htthreshold, coverthreshold): return (data > np.mean(data)).sum() / len(data) # TODO check performance of other methods -def m_abovemode(data): +def m_abovemode(data, htthreshold, coverthreshold): return (data > m_mode(data)).sum() / len(data) -def m_skewness(data): +def m_skewness(data, htthreshold, coverthreshold): if len(data) < 4: return -9999.0 return stats.skew(data) -def m_kurtosis(data): +def m_kurtosis(data, htthreshold, coverthreshold): if len(data) < 4: return -9999.0 return stats.kurtosis(data) -def m_aad(data): +def m_aad(data, htthreshold, coverthreshold): m = np.mean(data) return np.mean(np.absolute(data - m)) -def m_madmedian(data): +def m_madmedian(data, htthreshold, coverthreshold): return stats.median_abs_deviation(data) -def m_madmean(data): +def m_madmean(data, htthreshold, coverthreshold): return stats.median_abs_deviation(data, center=np.mean) -def m_madmode(data): +def m_madmode(data, htthreshold, coverthreshold): stats_mode = m_mode(data) return stats.median_abs_deviation(data, center=stats_mode) # TODO test various methods for interpolation=... for all percentile-related metrics # I think the default matches FUSION method but need to test -def m_iq(data): +def m_iq(data, htthreshold, coverthreshold): return stats.iqr(data) -def m_90m10(data): +def m_90m10(data, htthreshold, coverthreshold): p = np.percentile(data, [10,90]) return p[1] - p[0] -def m_95m05(data): +def m_95m05(data, htthreshold, coverthreshold): p = np.percentile(data, [5,95]) return p[1] - p[0] -def m_crr(data): +def m_crr(data, htthreshold, coverthreshold): maxv = np.max(data) minv = np.min(data) if minv == maxv: @@ -172,10 +172,10 @@ def m_crr(data): return (np.mean(data) - minv) / (maxv - minv) -def m_sqmean(data): +def m_sqmean(data, htthreshold, coverthreshold): return np.sqrt(np.mean(np.square(data))) -def m_cumean(data): +def m_cumean(data, htthreshold, coverthreshold): return np.cbrt(np.mean(np.power(np.absolute(data), 3))) # TODO compute L-moments. These are done separately because we only add @@ -189,34 +189,34 @@ def m_cumean(data): # statement with derived works) # L1 is same as mean...compute using np.mean for speed -def m_l1(data): +def m_l1(data, htthreshold, coverthreshold): if len(data) < 4: return -9999.0 return np.mean(data) -def m_l2(data): +def m_l2(data, htthreshold, coverthreshold): if len(data) < 4: return -9999.0 l = lmom4(data) return l[1] -def m_l3(data): +def m_l3(data, htthreshold, coverthreshold): if len(data) < 4: return -9999.0 l = lmom4(data) return l[2] -def m_l4(data): +def m_l4(data, htthreshold, coverthreshold): if len(data) < 4: return -9999.0 l = lmom4(data) return l[3] -def m_lcv(data): +def m_lcv(data, htthreshold, coverthreshold): if len(data) < 4: return -9999.0 @@ -227,7 +227,7 @@ def m_lcv(data): return l[1] / l[0] -def m_lskewness(data): +def m_lskewness(data, htthreshold, coverthreshold): if len(data) < 4: return -9999.0 @@ -237,7 +237,7 @@ def m_lskewness(data): return l[2] / l[1] -def m_lkurtosis(data): +def m_lkurtosis(data, htthreshold, coverthreshold): if len(data) < 4: return -9999.0 @@ -252,52 +252,52 @@ def m_lkurtosis(data): #def m_percentiles(data): # return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) -def m_p01(data): +def m_p01(data, htthreshold, coverthreshold): return(np.percentile(data, 1)) -def m_p05(data): +def m_p05(data, htthreshold, coverthreshold): return(np.percentile(data, 5)) -def m_p10(data): +def m_p10(data, htthreshold, coverthreshold): return(np.percentile(data, 10)) -def m_p20(data): +def m_p20(data, htthreshold, coverthreshold): return(np.percentile(data, 20)) -def m_p25(data): +def m_p25(data, htthreshold, coverthreshold): return(np.percentile(data, 25)) -def m_p30(data): +def m_p30(data, htthreshold, coverthreshold): return(np.percentile(data, 30)) -def m_p40(data): +def m_p40(data, htthreshold, coverthreshold): return(np.percentile(data, 40)) -def m_p50(data): +def m_p50(data, htthreshold, coverthreshold): return(np.percentile(data, 50)) -def m_p60(data): +def m_p60(data, htthreshold, coverthreshold): return(np.percentile(data, 60)) -def m_p70(data): +def m_p70(data, htthreshold, coverthreshold): return(np.percentile(data, 70)) -def m_p75(data): +def m_p75(data, htthreshold, coverthreshold): return(np.percentile(data, 75)) -def m_p80(data): +def m_p80(data, htthreshold, coverthreshold): return(np.percentile(data, 80)) -def m_p90(data): +def m_p90(data, htthreshold, coverthreshold): return(np.percentile(data, 90)) -def m_p95(data): +def m_p95(data, htthreshold, coverthreshold): return(np.percentile(data, 95)) -def m_p99(data): +def m_p99(data, htthreshold, coverthreshold): return(np.percentile(data, 99)) -def m_profilearea(data): +def m_profilearea(data, htthreshold, coverthreshold): # sanity check...must have valid heights/elevations if np.max(data) <= 0: return -9999.0 @@ -321,9 +321,8 @@ def m_profilearea(data): # TODO example for cover using all returns and a height threshold # the threshold must be a parameter and not hardcoded -def m_allcover(data): - threshold = 2 - return (data > threshold).sum() / len(data) +def m_allcover(data, htthreshold, coverthreshold): + return (data > coverthreshold).sum() / len(data) #TODO change to correct dtype #TODO not sure what to do with percentiles since it is an array of values instead of a single value From 064fbdb105e53ad990a9998429d51b0dacafd826 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Wed, 6 Mar 2024 16:48:06 -0500 Subject: [PATCH 14/39] fixing __call__ --- src/silvimetric/resources/metric.py | 4 +- t.json | 2136 +++++++++++++++++++++++++++ t.txt | 2136 +++++++++++++++++++++++++++ 3 files changed, 4274 insertions(+), 2 deletions(-) create mode 100644 t.json create mode 100644 t.txt diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index b2f6d51..f4d2301 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -64,8 +64,8 @@ def __eq__(self, other): self.dependencies == other.dependencies and self._method == other._method) - def __call__(self, data: np.ndarray) -> np.ndarray: - return self._method(data) + def __call__(self, data: np.ndarray, htthreshold: float, coverthreshold: float) -> np.ndarray: + return self._method(data, htthreshold, coverthreshold) def __repr__(self) -> str: return json.dumps(self.to_json()) diff --git a/t.json b/t.json new file mode 100644 index 0000000..135b4f0 --- /dev/null +++ b/t.json @@ -0,0 +1,2136 @@ +2024-03-06 16:44:18,393 - silvimetric - INFO - info:153 - { + "attributes": [ + { + "name": "Z", + "dtype": " np.mean(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" + }, + { + "name": "abovemode", + "dtype": " m_mode(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" + }, + { + "name": "skewness", + "dtype": " coverthreshold).sum() / len(data)\n", + "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" + }, + { + "name": "profilearea", + "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", + "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" + } + ], + "version": "0.0.1", + "capacity": 1000000 + }, + "history": [] +} +2024-03-06 16:44:18,393 - silvimetric - INFO - info:153 - { + "attributes": [ + { + "name": "Z", + "dtype": " np.mean(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" + }, + { + "name": "abovemode", + "dtype": " m_mode(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" + }, + { + "name": "skewness", + "dtype": " coverthreshold).sum() / len(data)\n", + "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" + }, + { + "name": "profilearea", + "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", + "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" + } + ], + "version": "0.0.1", + "capacity": 1000000 + }, + "history": [] +} +2024-03-06 16:44:18,393 - silvimetric - INFO - info:153 - { + "attributes": [ + { + "name": "Z", + "dtype": " np.mean(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" + }, + { + "name": "abovemode", + "dtype": " m_mode(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" + }, + { + "name": "skewness", + "dtype": " coverthreshold).sum() / len(data)\n", + "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" + }, + { + "name": "profilearea", + "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", + "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" + } + ], + "version": "0.0.1", + "capacity": 1000000 + }, + "history": [] +} +2024-03-06 16:44:18,393 - silvimetric - INFO - info:153 - { + "attributes": [ + { + "name": "Z", + "dtype": " np.mean(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" + }, + { + "name": "abovemode", + "dtype": " m_mode(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" + }, + { + "name": "skewness", + "dtype": " coverthreshold).sum() / len(data)\n", + "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" + }, + { + "name": "profilearea", + "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", + "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" + } + ], + "version": "0.0.1", + "capacity": 1000000 + }, + "history": [] +} diff --git a/t.txt b/t.txt new file mode 100644 index 0000000..dedb2da --- /dev/null +++ b/t.txt @@ -0,0 +1,2136 @@ +2024-03-06 16:42:15,228 - silvimetric - INFO - info:153 - { + "attributes": [ + { + "name": "Z", + "dtype": " np.mean(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" + }, + { + "name": "abovemode", + "dtype": " m_mode(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" + }, + { + "name": "skewness", + "dtype": " coverthreshold).sum() / len(data)\n", + "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" + }, + { + "name": "profilearea", + "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", + "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" + } + ], + "version": "0.0.1", + "capacity": 1000000 + }, + "history": [] +} +2024-03-06 16:42:15,228 - silvimetric - INFO - info:153 - { + "attributes": [ + { + "name": "Z", + "dtype": " np.mean(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" + }, + { + "name": "abovemode", + "dtype": " m_mode(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" + }, + { + "name": "skewness", + "dtype": " coverthreshold).sum() / len(data)\n", + "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" + }, + { + "name": "profilearea", + "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", + "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" + } + ], + "version": "0.0.1", + "capacity": 1000000 + }, + "history": [] +} +2024-03-06 16:42:15,228 - silvimetric - INFO - info:153 - { + "attributes": [ + { + "name": "Z", + "dtype": " np.mean(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" + }, + { + "name": "abovemode", + "dtype": " m_mode(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" + }, + { + "name": "skewness", + "dtype": " coverthreshold).sum() / len(data)\n", + "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" + }, + { + "name": "profilearea", + "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", + "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" + } + ], + "version": "0.0.1", + "capacity": 1000000 + }, + "history": [] +} +2024-03-06 16:42:15,228 - silvimetric - INFO - info:153 - { + "attributes": [ + { + "name": "Z", + "dtype": " np.mean(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" + }, + { + "name": "abovemode", + "dtype": " m_mode(data)).sum() / len(data)\n", + "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" + }, + { + "name": "skewness", + "dtype": " coverthreshold).sum() / len(data)\n", + "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" + }, + { + "name": "profilearea", + "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", + "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" + } + ], + "version": "0.0.1", + "capacity": 1000000 + }, + "history": [] +} From 0cabd0eb76e49aa399868ead2e075079dbb1997d Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Wed, 6 Mar 2024 16:51:17 -0500 Subject: [PATCH 15/39] fixing m_mode call in m_abovemode --- src/silvimetric/resources/metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index f4d2301..968eeb0 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -123,7 +123,7 @@ def m_abovemean(data, htthreshold, coverthreshold): # TODO check performance of other methods def m_abovemode(data, htthreshold, coverthreshold): - return (data > m_mode(data)).sum() / len(data) + return (data > m_mode(data, htthreshold, coverthreshold)).sum() / len(data) def m_skewness(data, htthreshold, coverthreshold): if len(data) < 4: From 598b6415bb35afacdf8c1896e789e0d9c4cff724 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Wed, 6 Mar 2024 16:53:14 -0500 Subject: [PATCH 16/39] fixing m_mode call in m_madmode --- src/silvimetric/resources/metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 968eeb0..f108e51 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -148,7 +148,7 @@ def m_madmean(data, htthreshold, coverthreshold): return stats.median_abs_deviation(data, center=np.mean) def m_madmode(data, htthreshold, coverthreshold): - stats_mode = m_mode(data) + stats_mode = m_mode(data, htthreshold, coverthreshold) return stats.median_abs_deviation(data, center=stats_mode) # TODO test various methods for interpolation=... for all percentile-related metrics From 32ccaf6a1486db180ee68537b4eaebf244b57e89 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Wed, 6 Mar 2024 16:55:51 -0500 Subject: [PATCH 17/39] skipping m_madmode --- src/silvimetric/resources/metric.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index f108e51..93c6c7f 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -147,9 +147,10 @@ def m_madmedian(data, htthreshold, coverthreshold): def m_madmean(data, htthreshold, coverthreshold): return stats.median_abs_deviation(data, center=np.mean) -def m_madmode(data, htthreshold, coverthreshold): - stats_mode = m_mode(data, htthreshold, coverthreshold) - return stats.median_abs_deviation(data, center=stats_mode) +# TODO needs work +#def m_madmode(data, htthreshold, coverthreshold): +# stats_mode = m_mode(data, htthreshold, coverthreshold) +# return stats.median_abs_deviation(data, center=stats_mode) # TODO test various methods for interpolation=... for all percentile-related metrics # I think the default matches FUSION method but need to test From e54cc2db4ec6291de4da5af90c0f1839018ef3eb Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Wed, 6 Mar 2024 16:56:57 -0500 Subject: [PATCH 18/39] omitting m_madmode --- src/silvimetric/resources/metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 93c6c7f..35225d6 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -343,7 +343,7 @@ def m_allcover(data, htthreshold, coverthreshold): 'kurtosis' : Metric('kurtosis', m_kurtosis), 'aad' : Metric('aad', m_aad), 'madmedian' : Metric('madmedian', m_madmedian), - 'madmode' : Metric('madmode', m_madmode), +# 'madmode' : Metric('madmode', m_madmode), 'iq' : Metric('iq', m_iq), 'crr' : Metric('crr', m_crr), 'sqmean' : Metric('sqmean', m_sqmean), From a6c4e1f8e077b30c62d7eb6dfe2ba23d25c91940 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Thu, 7 Mar 2024 08:47:23 -0500 Subject: [PATCH 19/39] adding use of htthreshold --- .gitignore | 1 + src/silvimetric/resources/metric.py | 151 +++++++++++++++------------- tests/conftest.py | 4 +- 3 files changed, 84 insertions(+), 72 deletions(-) diff --git a/.gitignore b/.gitignore index 51d213c..aeecde4 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,6 @@ autzen-classified.copc.laz #sample metrics metrics/ metrics_aligned/ +autzen-aligned.tdb/ .DS_Store diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 35225d6..b1df9ee 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -73,10 +73,10 @@ def __repr__(self) -> str: #TODO add all metrics from https://github.com/hobuinc/silvimetric/issues/5 def m_count(data, htthreshold, coverthreshold): - return len(data) + return (data > htthreshold).sum() def m_mean(data, htthreshold, coverthreshold): - return np.mean(data) + return np.mean(data[data > htthreshold]) # mode is somewhat undefined for floating point values. FUSION logic is to # partition data into 64 bins then find the bin with the highest count. @@ -86,98 +86,107 @@ def m_mean(data, htthreshold, coverthreshold): # the bin number * bin width (max - min / #bins) + min. def m_mode(data, htthreshold, coverthreshold): nbins = 64 - maxv = np.max(data) - minv = np.min(data) + d = data[data > htthreshold] + maxv = np.max(d) + minv = np.min(d) if minv == maxv: return minv - bins = np.histogram(data, bins = nbins, density = False) + bins = np.histogram(d - minv, bins = nbins, density = False) - thebin = np.argmax(data, keepdims=False) + thebin = np.argmax(bins, keepdims=False) # compute the height and return...bins - 1 is to get the bottom Z of the bin return minv + thebin * (maxv - minv) / (nbins - 1) def m_median(data, htthreshold, coverthreshold): - return np.median(data) + return np.median(data)[data > htthreshold] def m_min(data, htthreshold, coverthreshold): - return np.min(data) + return np.min(data[data > htthreshold]) def m_max(data, htthreshold, coverthreshold): - return np.max(data) + return np.max(data[data > htthreshold]) def m_stddev(data, htthreshold, coverthreshold): - return np.std(data) + return np.std(data[data > htthreshold]) # start of new metrics to match FUSION def m_variance(data, htthreshold, coverthreshold): - return np.var(data) + return np.var(data[data > htthreshold]) def m_cv(data, htthreshold, coverthreshold): - return np.std(data) / np.mean(data) + d = data[data > htthreshold] + return np.std(d) / np.mean(d) # TODO check performance of other methods def m_abovemean(data, htthreshold, coverthreshold): - return (data > np.mean(data)).sum() / len(data) + d = data[data > htthreshold] + return (d > np.mean(d)).sum() / len(d) # TODO check performance of other methods def m_abovemode(data, htthreshold, coverthreshold): - return (data > m_mode(data, htthreshold, coverthreshold)).sum() / len(data) + d = data[data > htthreshold] + return (d > m_mode(d, htthreshold, coverthreshold)).sum() / len(d) def m_skewness(data, htthreshold, coverthreshold): - if len(data) < 4: + d = data[data > htthreshold] + if len(d) < 4: return -9999.0 - return stats.skew(data) + return stats.skew(d) def m_kurtosis(data, htthreshold, coverthreshold): - if len(data) < 4: + d = data[data > htthreshold] + if len(d) < 4: return -9999.0 - return stats.kurtosis(data) + return stats.kurtosis(d) def m_aad(data, htthreshold, coverthreshold): - m = np.mean(data) - return np.mean(np.absolute(data - m)) + d = data[data > htthreshold] + m = m_mean(d, htthreshold, coverthreshold) + return np.mean(np.absolute(d - m)) def m_madmedian(data, htthreshold, coverthreshold): - return stats.median_abs_deviation(data) + return stats.median_abs_deviation(data[data > htthreshold]) def m_madmean(data, htthreshold, coverthreshold): - return stats.median_abs_deviation(data, center=np.mean) + return stats.median_abs_deviation(data[data > htthreshold], center=np.mean) # TODO needs work -#def m_madmode(data, htthreshold, coverthreshold): -# stats_mode = m_mode(data, htthreshold, coverthreshold) -# return stats.median_abs_deviation(data, center=stats_mode) +def m_madmode(data, htthreshold, coverthreshold): + d = data[data > htthreshold] + m = m_mode(d, htthreshold, coverthreshold) + return np.median(np.absolute(d - m)) # TODO test various methods for interpolation=... for all percentile-related metrics # I think the default matches FUSION method but need to test def m_iq(data, htthreshold, coverthreshold): - return stats.iqr(data) + return stats.iqr(data[data > htthreshold]) def m_90m10(data, htthreshold, coverthreshold): - p = np.percentile(data, [10,90]) + p = np.percentile(data[data > htthreshold], [10,90]) return p[1] - p[0] def m_95m05(data, htthreshold, coverthreshold): - p = np.percentile(data, [5,95]) + p = np.percentile(data[data > htthreshold], [5,95]) return p[1] - p[0] def m_crr(data, htthreshold, coverthreshold): - maxv = np.max(data) - minv = np.min(data) + d = data[data > htthreshold] + maxv = np.max(d) + minv = np.min(d) if minv == maxv: return -9999.0 - return (np.mean(data) - minv) / (maxv - minv) + return (np.mean(d) - minv) / (maxv - minv) def m_sqmean(data, htthreshold, coverthreshold): - return np.sqrt(np.mean(np.square(data))) + return np.sqrt(np.mean(np.square(data[data > htthreshold]))) def m_cumean(data, htthreshold, coverthreshold): - return np.cbrt(np.mean(np.power(np.absolute(data), 3))) + return np.cbrt(np.mean(np.power(np.absolute(data[data > htthreshold]), 3))) # TODO compute L-moments. These are done separately because we only add # a single element to TileDB. This is very inefficient since we have to @@ -191,37 +200,42 @@ def m_cumean(data, htthreshold, coverthreshold): # L1 is same as mean...compute using np.mean for speed def m_l1(data, htthreshold, coverthreshold): - if len(data) < 4: + d = data[data > htthreshold] + if len(d) < 4: return -9999.0 - return np.mean(data) + return np.mean(d) def m_l2(data, htthreshold, coverthreshold): - if len(data) < 4: + d = data[data > htthreshold] + if len(d) < 4: return -9999.0 - l = lmom4(data) + l = lmom4(d) return l[1] def m_l3(data, htthreshold, coverthreshold): - if len(data) < 4: + d = data[data > htthreshold] + if len(d) < 4: return -9999.0 - l = lmom4(data) + l = lmom4(d) return l[2] def m_l4(data, htthreshold, coverthreshold): - if len(data) < 4: + d = data[data > htthreshold] + if len(d) < 4: return -9999.0 - l = lmom4(data) + l = lmom4(d) return l[3] def m_lcv(data, htthreshold, coverthreshold): - if len(data) < 4: + d = data[data > htthreshold] + if len(d) < 4: return -9999.0 - l = lmom4(data) + l = lmom4(d) if l[0] == 0.0: return -9999.0 @@ -229,20 +243,22 @@ def m_lcv(data, htthreshold, coverthreshold): return l[1] / l[0] def m_lskewness(data, htthreshold, coverthreshold): - if len(data) < 4: + d = data[data > htthreshold] + if len(d) < 4: return -9999.0 - l = lmom4(data) + l = lmom4(d) if l[1] == 0.0: return -9999.0 return l[2] / l[1] def m_lkurtosis(data, htthreshold, coverthreshold): - if len(data) < 4: + d = data[data > htthreshold] + if len(d) < 4: return -9999.0 - l = lmom4(data) + l = lmom4(d) if l[1] == 0.0: return -9999.0 @@ -254,49 +270,49 @@ def m_lkurtosis(data, htthreshold, coverthreshold): # return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) def m_p01(data, htthreshold, coverthreshold): - return(np.percentile(data, 1)) + return(np.percentile(data[data > htthreshold], 1)) def m_p05(data, htthreshold, coverthreshold): - return(np.percentile(data, 5)) + return(np.percentile(data[data > htthreshold], 5)) def m_p10(data, htthreshold, coverthreshold): - return(np.percentile(data, 10)) + return(np.percentile(data[data > htthreshold], 10)) def m_p20(data, htthreshold, coverthreshold): - return(np.percentile(data, 20)) + return(np.percentile(data[data > htthreshold], 20)) def m_p25(data, htthreshold, coverthreshold): - return(np.percentile(data, 25)) + return(np.percentile(data[data > htthreshold], 25)) def m_p30(data, htthreshold, coverthreshold): - return(np.percentile(data, 30)) + return(np.percentile(data[data > htthreshold], 30)) def m_p40(data, htthreshold, coverthreshold): - return(np.percentile(data, 40)) + return(np.percentile(data[data > htthreshold], 40)) def m_p50(data, htthreshold, coverthreshold): - return(np.percentile(data, 50)) + return(np.percentile(data[data > htthreshold], 50)) def m_p60(data, htthreshold, coverthreshold): - return(np.percentile(data, 60)) + return(np.percentile(data[data > htthreshold], 60)) def m_p70(data, htthreshold, coverthreshold): - return(np.percentile(data, 70)) + return(np.percentile(data[data > htthreshold], 70)) def m_p75(data, htthreshold, coverthreshold): - return(np.percentile(data, 75)) + return(np.percentile(data[data > htthreshold], 75)) def m_p80(data, htthreshold, coverthreshold): - return(np.percentile(data, 80)) + return(np.percentile(data[data > htthreshold], 80)) def m_p90(data, htthreshold, coverthreshold): - return(np.percentile(data, 90)) + return(np.percentile(data[data > htthreshold], 90)) def m_p95(data, htthreshold, coverthreshold): - return(np.percentile(data, 95)) + return(np.percentile(data[data > htthreshold], 95)) def m_p99(data, htthreshold, coverthreshold): - return(np.percentile(data, 99)) + return(np.percentile(data[data > htthreshold], 99)) def m_profilearea(data, htthreshold, coverthreshold): # sanity check...must have valid heights/elevations @@ -319,18 +335,13 @@ def m_profilearea(data, htthreshold, coverthreshold): else: return -9999.0 - -# TODO example for cover using all returns and a height threshold -# the threshold must be a parameter and not hardcoded def m_allcover(data, htthreshold, coverthreshold): - return (data > coverthreshold).sum() / len(data) + return (data > coverthreshold).sum() / len(data) * 100.0 -#TODO change to correct dtype -#TODO not sure what to do with percentiles since it is an array of values instead of a single value Metrics = { 'count' : Metric('count', m_count), 'mean' : Metric('mean', m_mean), - # 'mode' : Metric('mode', m_mode), + 'mode' : Metric('mode', m_mode), 'median' : Metric('median', m_median), 'min' : Metric('min', m_min), 'max' : Metric('max', m_max), @@ -343,7 +354,7 @@ def m_allcover(data, htthreshold, coverthreshold): 'kurtosis' : Metric('kurtosis', m_kurtosis), 'aad' : Metric('aad', m_aad), 'madmedian' : Metric('madmedian', m_madmedian), -# 'madmode' : Metric('madmode', m_madmode), + 'madmode' : Metric('madmode', m_madmode), 'iq' : Metric('iq', m_iq), 'crr' : Metric('crr', m_crr), 'sqmean' : Metric('sqmean', m_sqmean), diff --git a/tests/conftest.py b/tests/conftest.py index 106a814..7163d4f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -102,8 +102,8 @@ def s3_uri(s3_bucket): yield f"s3://{s3_bucket}/test_silvimetric/{uuid}" @pytest.fixture(scope="function") -def s3_storage_config(s3_uri, bounds, resolution, crs, attrs, metrics): - yield StorageConfig(bounds, crs, resolution, attrs, metrics, +def s3_storage_config(s3_uri, bounds, resolution, htthreshold, coverthreshold, crs, attrs, metrics): + yield StorageConfig(bounds, crs, resolution, htthreshold, coverthreshold, attrs, metrics, svversion, tdb_dir=s3_uri) @pytest.fixture(scope='function') From 77d6dcb23599e4c478c2d8a98646f43acd188956 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Thu, 7 Mar 2024 09:10:39 -0500 Subject: [PATCH 20/39] fixing m_mode --- src/silvimetric/resources/metric.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index b1df9ee..0700794 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -94,9 +94,9 @@ def m_mode(data, htthreshold, coverthreshold): bins = np.histogram(d - minv, bins = nbins, density = False) - thebin = np.argmax(bins, keepdims=False) + thebin = np.argmax(bins) - # compute the height and return...bins - 1 is to get the bottom Z of the bin + # compute the height and return...nbins - 1 is to get the bottom Z of the bin return minv + thebin * (maxv - minv) / (nbins - 1) def m_median(data, htthreshold, coverthreshold): From 2455dac616e89723c89068444dc9c84e42eb9694 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Thu, 7 Mar 2024 09:31:20 -0500 Subject: [PATCH 21/39] fixing m_mode again --- src/silvimetric/resources/metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 0700794..8f30aa7 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -92,7 +92,7 @@ def m_mode(data, htthreshold, coverthreshold): if minv == maxv: return minv - bins = np.histogram(d - minv, bins = nbins, density = False) + bins = np.histogram(np.subtract(d, minv), bins = nbins, density = False) thebin = np.argmax(bins) From 65d119fa9900607591fbe447b9163e5998673f02 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Thu, 7 Mar 2024 09:39:29 -0500 Subject: [PATCH 22/39] m_mode again --- src/silvimetric/resources/metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 8f30aa7..e10cab4 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -92,7 +92,7 @@ def m_mode(data, htthreshold, coverthreshold): if minv == maxv: return minv - bins = np.histogram(np.subtract(d, minv), bins = nbins, density = False) + bins = np.histogram(d, bins = nbins, density = False) thebin = np.argmax(bins) From ba3a69bb633e6e9b96258b82a1ccf9f5dba002ac Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Thu, 7 Mar 2024 09:41:33 -0500 Subject: [PATCH 23/39] m_mode again --- src/silvimetric/resources/metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index e10cab4..ce93864 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -94,7 +94,7 @@ def m_mode(data, htthreshold, coverthreshold): bins = np.histogram(d, bins = nbins, density = False) - thebin = np.argmax(bins) + thebin = np.argmax(bins, keepdims=False) # compute the height and return...nbins - 1 is to get the bottom Z of the bin return minv + thebin * (maxv - minv) / (nbins - 1) From a57f7bbae7f04769ec207ef0980acf2419b59ec5 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Thu, 7 Mar 2024 10:00:15 -0500 Subject: [PATCH 24/39] m_mode --- src/silvimetric/resources/metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index ce93864..2ef291a 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -94,7 +94,7 @@ def m_mode(data, htthreshold, coverthreshold): bins = np.histogram(d, bins = nbins, density = False) - thebin = np.argmax(bins, keepdims=False) + thebin = np.argmax(bins, axis = -1) # compute the height and return...nbins - 1 is to get the bottom Z of the bin return minv + thebin * (maxv - minv) / (nbins - 1) From c88230e5c705b9c8b75c8b515f2b576540e6e77a Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Thu, 7 Mar 2024 10:06:54 -0500 Subject: [PATCH 25/39] dropping m_mode (temp) --- src/silvimetric/resources/metric.py | 42 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 2ef291a..c0a90c7 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -84,20 +84,20 @@ def m_mean(data, htthreshold, coverthreshold): # the Z range. # Before binning, subtract min value. Computing the scaled value involves # the bin number * bin width (max - min / #bins) + min. -def m_mode(data, htthreshold, coverthreshold): - nbins = 64 - d = data[data > htthreshold] - maxv = np.max(d) - minv = np.min(d) - if minv == maxv: - return minv +# def m_mode(data, htthreshold, coverthreshold): +# nbins = 64 +# d = data[data > htthreshold] +# maxv = np.max(d) +# minv = np.min(d) +# if minv == maxv: +# return minv - bins = np.histogram(d, bins = nbins, density = False) +# bins = np.histogram(d, bins = nbins, density = False) - thebin = np.argmax(bins, axis = -1) +# thebin = np.argmax(bins, axis = -1) - # compute the height and return...nbins - 1 is to get the bottom Z of the bin - return minv + thebin * (maxv - minv) / (nbins - 1) +# # compute the height and return...nbins - 1 is to get the bottom Z of the bin +# return minv + thebin * (maxv - minv) / (nbins - 1) def m_median(data, htthreshold, coverthreshold): return np.median(data)[data > htthreshold] @@ -125,9 +125,9 @@ def m_abovemean(data, htthreshold, coverthreshold): return (d > np.mean(d)).sum() / len(d) # TODO check performance of other methods -def m_abovemode(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - return (d > m_mode(d, htthreshold, coverthreshold)).sum() / len(d) +# def m_abovemode(data, htthreshold, coverthreshold): +# d = data[data > htthreshold] +# return (d > m_mode(d, htthreshold, coverthreshold)).sum() / len(d) def m_skewness(data, htthreshold, coverthreshold): d = data[data > htthreshold] @@ -155,10 +155,10 @@ def m_madmean(data, htthreshold, coverthreshold): return stats.median_abs_deviation(data[data > htthreshold], center=np.mean) # TODO needs work -def m_madmode(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - m = m_mode(d, htthreshold, coverthreshold) - return np.median(np.absolute(d - m)) +# def m_madmode(data, htthreshold, coverthreshold): +# d = data[data > htthreshold] +# m = m_mode(d, htthreshold, coverthreshold) +# return np.median(np.absolute(d - m)) # TODO test various methods for interpolation=... for all percentile-related metrics # I think the default matches FUSION method but need to test @@ -341,7 +341,7 @@ def m_allcover(data, htthreshold, coverthreshold): Metrics = { 'count' : Metric('count', m_count), 'mean' : Metric('mean', m_mean), - 'mode' : Metric('mode', m_mode), +# 'mode' : Metric('mode', m_mode), 'median' : Metric('median', m_median), 'min' : Metric('min', m_min), 'max' : Metric('max', m_max), @@ -349,12 +349,12 @@ def m_allcover(data, htthreshold, coverthreshold): 'variance' : Metric('variance', m_variance), 'cv' : Metric('cv', m_cv), 'abovemean' : Metric('abovemean', m_abovemean), - 'abovemode' : Metric('abovemode', m_abovemode), +# 'abovemode' : Metric('abovemode', m_abovemode), 'skewness' : Metric('skewness', m_skewness), 'kurtosis' : Metric('kurtosis', m_kurtosis), 'aad' : Metric('aad', m_aad), 'madmedian' : Metric('madmedian', m_madmedian), - 'madmode' : Metric('madmode', m_madmode), +# 'madmode' : Metric('madmode', m_madmode), 'iq' : Metric('iq', m_iq), 'crr' : Metric('crr', m_crr), 'sqmean' : Metric('sqmean', m_sqmean), From 538a8f2cc26350807e652e4e0c2138557fa76470 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Thu, 7 Mar 2024 10:09:00 -0500 Subject: [PATCH 26/39] fixing typos --- src/silvimetric/resources/metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index c0a90c7..778dea7 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -100,7 +100,7 @@ def m_mean(data, htthreshold, coverthreshold): # return minv + thebin * (maxv - minv) / (nbins - 1) def m_median(data, htthreshold, coverthreshold): - return np.median(data)[data > htthreshold] + return np.median(data[data > htthreshold]) def m_min(data, htthreshold, coverthreshold): return np.min(data[data > htthreshold]) From 1629cd42ba804af7daac78fa51ddea8072a36746 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Thu, 7 Mar 2024 13:54:43 -0500 Subject: [PATCH 27/39] backing out application of ht and cover thresholds from metrics functions --- src/silvimetric/resources/metric.py | 141 +- t.json | 2136 --------------------------- t.txt | 2136 --------------------------- 3 files changed, 63 insertions(+), 4350 deletions(-) delete mode 100644 t.json delete mode 100644 t.txt diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 778dea7..0ea51c1 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -73,10 +73,10 @@ def __repr__(self) -> str: #TODO add all metrics from https://github.com/hobuinc/silvimetric/issues/5 def m_count(data, htthreshold, coverthreshold): - return (data > htthreshold).sum() + return len(data) def m_mean(data, htthreshold, coverthreshold): - return np.mean(data[data > htthreshold]) + return np.mean(data) # mode is somewhat undefined for floating point values. FUSION logic is to # partition data into 64 bins then find the bin with the highest count. @@ -86,7 +86,7 @@ def m_mean(data, htthreshold, coverthreshold): # the bin number * bin width (max - min / #bins) + min. # def m_mode(data, htthreshold, coverthreshold): # nbins = 64 -# d = data[data > htthreshold] +# d = data # maxv = np.max(d) # minv = np.min(d) # if minv == maxv: @@ -100,93 +100,85 @@ def m_mean(data, htthreshold, coverthreshold): # return minv + thebin * (maxv - minv) / (nbins - 1) def m_median(data, htthreshold, coverthreshold): - return np.median(data[data > htthreshold]) + return np.median(data) def m_min(data, htthreshold, coverthreshold): - return np.min(data[data > htthreshold]) + return np.min(data) def m_max(data, htthreshold, coverthreshold): - return np.max(data[data > htthreshold]) + return np.max(data) def m_stddev(data, htthreshold, coverthreshold): - return np.std(data[data > htthreshold]) + return np.std(data) # start of new metrics to match FUSION def m_variance(data, htthreshold, coverthreshold): - return np.var(data[data > htthreshold]) + return np.var(data) def m_cv(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - return np.std(d) / np.mean(d) + return np.std(data) / np.mean(data) # TODO check performance of other methods def m_abovemean(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - return (d > np.mean(d)).sum() / len(d) + return (data > np.mean(data)).sum() / len(data) # TODO check performance of other methods -# def m_abovemode(data, htthreshold, coverthreshold): -# d = data[data > htthreshold] -# return (d > m_mode(d, htthreshold, coverthreshold)).sum() / len(d) +def m_abovemode(data, htthreshold, coverthreshold): + return (d > m_mode(data, htthreshold, coverthreshold)).sum() / len(data) def m_skewness(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - if len(d) < 4: + if len(data) < 4: return -9999.0 - return stats.skew(d) + return stats.skew(data) def m_kurtosis(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - if len(d) < 4: + if len(data) < 4: return -9999.0 - return stats.kurtosis(d) + return stats.kurtosis(data) def m_aad(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - m = m_mean(d, htthreshold, coverthreshold) - return np.mean(np.absolute(d - m)) + m = m_mean(data=, htthreshold, coverthreshold) + return np.mean(np.absolute(data - m)) def m_madmedian(data, htthreshold, coverthreshold): - return stats.median_abs_deviation(data[data > htthreshold]) + return stats.median_abs_deviation(data) def m_madmean(data, htthreshold, coverthreshold): - return stats.median_abs_deviation(data[data > htthreshold], center=np.mean) + return stats.median_abs_deviation(data, center=np.mean) # TODO needs work -# def m_madmode(data, htthreshold, coverthreshold): -# d = data[data > htthreshold] -# m = m_mode(d, htthreshold, coverthreshold) -# return np.median(np.absolute(d - m)) +def m_madmode(data, htthreshold, coverthreshold): + m = m_mode(data, htthreshold, coverthreshold) + return np.median(np.absolute(data - m)) # TODO test various methods for interpolation=... for all percentile-related metrics # I think the default matches FUSION method but need to test def m_iq(data, htthreshold, coverthreshold): - return stats.iqr(data[data > htthreshold]) + return stats.iqr(data) def m_90m10(data, htthreshold, coverthreshold): - p = np.percentile(data[data > htthreshold], [10,90]) + p = np.percentile(data, [10,90]) return p[1] - p[0] def m_95m05(data, htthreshold, coverthreshold): - p = np.percentile(data[data > htthreshold], [5,95]) + p = np.percentile(data, [5,95]) return p[1] - p[0] def m_crr(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - maxv = np.max(d) - minv = np.min(d) + maxv = np.max(data) + minv = np.min(data) if minv == maxv: return -9999.0 - return (np.mean(d) - minv) / (maxv - minv) + return (np.mean(data) - minv) / (maxv - minv) def m_sqmean(data, htthreshold, coverthreshold): - return np.sqrt(np.mean(np.square(data[data > htthreshold]))) + return np.sqrt(np.mean(np.square(data))) def m_cumean(data, htthreshold, coverthreshold): - return np.cbrt(np.mean(np.power(np.absolute(data[data > htthreshold]), 3))) + return np.cbrt(np.mean(np.power(np.absolute(data), 3))) # TODO compute L-moments. These are done separately because we only add # a single element to TileDB. This is very inefficient since we have to @@ -200,42 +192,37 @@ def m_cumean(data, htthreshold, coverthreshold): # L1 is same as mean...compute using np.mean for speed def m_l1(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - if len(d) < 4: + if len(data) < 4: return -9999.0 - return np.mean(d) + return np.mean(data) def m_l2(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - if len(d) < 4: + if len(data) < 4: return -9999.0 - l = lmom4(d) + l = lmom4(data) return l[1] def m_l3(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - if len(d) < 4: + if len(data) < 4: return -9999.0 - l = lmom4(d) + l = lmom4(data) return l[2] def m_l4(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - if len(d) < 4: + if len(data) < 4: return -9999.0 - l = lmom4(d) + l = lmom4(data) return l[3] def m_lcv(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - if len(d) < 4: + if len(data) < 4: return -9999.0 - l = lmom4(d) + l = lmom4(data) if l[0] == 0.0: return -9999.0 @@ -243,22 +230,20 @@ def m_lcv(data, htthreshold, coverthreshold): return l[1] / l[0] def m_lskewness(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - if len(d) < 4: + if len(data) < 4: return -9999.0 - l = lmom4(d) + l = lmom4(data) if l[1] == 0.0: return -9999.0 return l[2] / l[1] def m_lkurtosis(data, htthreshold, coverthreshold): - d = data[data > htthreshold] - if len(d) < 4: + if len(data) < 4: return -9999.0 - l = lmom4(d) + l = lmom4(data) if l[1] == 0.0: return -9999.0 @@ -270,49 +255,49 @@ def m_lkurtosis(data, htthreshold, coverthreshold): # return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) def m_p01(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 1)) + return(np.percentile(data, 1)) def m_p05(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 5)) + return(np.percentile(data, 5)) def m_p10(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 10)) + return(np.percentile(data, 10)) def m_p20(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 20)) + return(np.percentile(data, 20)) def m_p25(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 25)) + return(np.percentile(data, 25)) def m_p30(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 30)) + return(np.percentile(data, 30)) def m_p40(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 40)) + return(np.percentile(data, 40)) def m_p50(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 50)) + return(np.percentile(data, 50)) def m_p60(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 60)) + return(np.percentile(data, 60)) def m_p70(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 70)) + return(np.percentile(data, 70)) def m_p75(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 75)) + return(np.percentile(data, 75)) def m_p80(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 80)) + return(np.percentile(data, 80)) def m_p90(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 90)) + return(np.percentile(data, 90)) def m_p95(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 95)) + return(np.percentile(data, 95)) def m_p99(data, htthreshold, coverthreshold): - return(np.percentile(data[data > htthreshold], 99)) + return(np.percentile(data, 99)) def m_profilearea(data, htthreshold, coverthreshold): # sanity check...must have valid heights/elevations @@ -341,7 +326,7 @@ def m_allcover(data, htthreshold, coverthreshold): Metrics = { 'count' : Metric('count', m_count), 'mean' : Metric('mean', m_mean), -# 'mode' : Metric('mode', m_mode), + 'mode' : Metric('mode', m_mode), 'median' : Metric('median', m_median), 'min' : Metric('min', m_min), 'max' : Metric('max', m_max), @@ -349,12 +334,12 @@ def m_allcover(data, htthreshold, coverthreshold): 'variance' : Metric('variance', m_variance), 'cv' : Metric('cv', m_cv), 'abovemean' : Metric('abovemean', m_abovemean), -# 'abovemode' : Metric('abovemode', m_abovemode), + 'abovemode' : Metric('abovemode', m_abovemode), 'skewness' : Metric('skewness', m_skewness), 'kurtosis' : Metric('kurtosis', m_kurtosis), 'aad' : Metric('aad', m_aad), 'madmedian' : Metric('madmedian', m_madmedian), -# 'madmode' : Metric('madmode', m_madmode), + 'madmode' : Metric('madmode', m_madmode), 'iq' : Metric('iq', m_iq), 'crr' : Metric('crr', m_crr), 'sqmean' : Metric('sqmean', m_sqmean), diff --git a/t.json b/t.json deleted file mode 100644 index 135b4f0..0000000 --- a/t.json +++ /dev/null @@ -1,2136 +0,0 @@ -2024-03-06 16:44:18,393 - silvimetric - INFO - info:153 - { - "attributes": [ - { - "name": "Z", - "dtype": " np.mean(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" - }, - { - "name": "abovemode", - "dtype": " m_mode(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" - }, - { - "name": "skewness", - "dtype": " coverthreshold).sum() / len(data)\n", - "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" - }, - { - "name": "profilearea", - "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", - "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" - } - ], - "version": "0.0.1", - "capacity": 1000000 - }, - "history": [] -} -2024-03-06 16:44:18,393 - silvimetric - INFO - info:153 - { - "attributes": [ - { - "name": "Z", - "dtype": " np.mean(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" - }, - { - "name": "abovemode", - "dtype": " m_mode(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" - }, - { - "name": "skewness", - "dtype": " coverthreshold).sum() / len(data)\n", - "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" - }, - { - "name": "profilearea", - "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", - "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" - } - ], - "version": "0.0.1", - "capacity": 1000000 - }, - "history": [] -} -2024-03-06 16:44:18,393 - silvimetric - INFO - info:153 - { - "attributes": [ - { - "name": "Z", - "dtype": " np.mean(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" - }, - { - "name": "abovemode", - "dtype": " m_mode(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" - }, - { - "name": "skewness", - "dtype": " coverthreshold).sum() / len(data)\n", - "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" - }, - { - "name": "profilearea", - "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", - "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" - } - ], - "version": "0.0.1", - "capacity": 1000000 - }, - "history": [] -} -2024-03-06 16:44:18,393 - silvimetric - INFO - info:153 - { - "attributes": [ - { - "name": "Z", - "dtype": " np.mean(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" - }, - { - "name": "abovemode", - "dtype": " m_mode(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" - }, - { - "name": "skewness", - "dtype": " coverthreshold).sum() / len(data)\n", - "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" - }, - { - "name": "profilearea", - "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", - "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" - } - ], - "version": "0.0.1", - "capacity": 1000000 - }, - "history": [] -} diff --git a/t.txt b/t.txt deleted file mode 100644 index dedb2da..0000000 --- a/t.txt +++ /dev/null @@ -1,2136 +0,0 @@ -2024-03-06 16:42:15,228 - silvimetric - INFO - info:153 - { - "attributes": [ - { - "name": "Z", - "dtype": " np.mean(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" - }, - { - "name": "abovemode", - "dtype": " m_mode(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" - }, - { - "name": "skewness", - "dtype": " coverthreshold).sum() / len(data)\n", - "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" - }, - { - "name": "profilearea", - "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", - "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" - } - ], - "version": "0.0.1", - "capacity": 1000000 - }, - "history": [] -} -2024-03-06 16:42:15,228 - silvimetric - INFO - info:153 - { - "attributes": [ - { - "name": "Z", - "dtype": " np.mean(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" - }, - { - "name": "abovemode", - "dtype": " m_mode(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" - }, - { - "name": "skewness", - "dtype": " coverthreshold).sum() / len(data)\n", - "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" - }, - { - "name": "profilearea", - "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", - "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" - } - ], - "version": "0.0.1", - "capacity": 1000000 - }, - "history": [] -} -2024-03-06 16:42:15,228 - silvimetric - INFO - info:153 - { - "attributes": [ - { - "name": "Z", - "dtype": " np.mean(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" - }, - { - "name": "abovemode", - "dtype": " m_mode(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" - }, - { - "name": "skewness", - "dtype": " coverthreshold).sum() / len(data)\n", - "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" - }, - { - "name": "profilearea", - "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", - "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" - } - ], - "version": "0.0.1", - "capacity": 1000000 - }, - "history": [] -} -2024-03-06 16:42:15,228 - silvimetric - INFO - info:153 - { - "attributes": [ - { - "name": "Z", - "dtype": " np.mean(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbWVhbpSTlC4=" - }, - { - "name": "abovemode", - "dtype": " m_mode(data)).sum() / len(data)\n", - "method": "gASVMAAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAttX2Fib3ZlbW9kZZSTlC4=" - }, - { - "name": "skewness", - "dtype": " coverthreshold).sum() / len(data)\n", - "method": "gASVLwAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjAptX2FsbGNvdmVylJOULg==" - }, - { - "name": "profilearea", - "dtype": " 0\n if p[98] > 0.0:\n # compute area under normalized percentile height curve using composite trapeziod rule\n pa = p0 / p[98]\n for ip in p[:97]:\n pa += 2.0 * ip / p[98]\n pa += 1.0\n\n return pa * 0.5\n else:\n return -9999.0\n", - "method": "gASVMgAAAAAAAACMHHNpbHZpbWV0cmljLnJlc291cmNlcy5tZXRyaWOUjA1tX3Byb2ZpbGVhcmVhlJOULg==" - } - ], - "version": "0.0.1", - "capacity": 1000000 - }, - "history": [] -} From 04c4e5d399319b248a3dae92ff02521c497be66a Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Thu, 7 Mar 2024 14:06:25 -0500 Subject: [PATCH 28/39] exploring ht filtering --- src/silvimetric/commands/shatter.py | 2 ++ src/silvimetric/resources/metric.py | 27 +++++++++++++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/silvimetric/commands/shatter.py b/src/silvimetric/commands/shatter.py index f1c9642..76ea859 100644 --- a/src/silvimetric/commands/shatter.py +++ b/src/silvimetric/commands/shatter.py @@ -71,6 +71,8 @@ def get_metrics(data_in, attrs: list[str], storage: Storage): if not np.any(data['count']): return None + storage.config.log.info(data[attr]) + # doing dask compute inside the dict array because it was too fine-grained # when it was outside metric_data = { diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 0ea51c1..8bdfa18 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -84,20 +84,19 @@ def m_mean(data, htthreshold, coverthreshold): # the Z range. # Before binning, subtract min value. Computing the scaled value involves # the bin number * bin width (max - min / #bins) + min. -# def m_mode(data, htthreshold, coverthreshold): -# nbins = 64 -# d = data -# maxv = np.max(d) -# minv = np.min(d) -# if minv == maxv: -# return minv +def m_mode(data, htthreshold, coverthreshold): + nbins = 64 + maxv = np.max(data) + minv = np.min(data) + if minv == maxv: + return minv -# bins = np.histogram(d, bins = nbins, density = False) - -# thebin = np.argmax(bins, axis = -1) + bins = np.histogram(data, bins = nbins, density = False) + + thebin = np.argmax(bins, axis = -1) -# # compute the height and return...nbins - 1 is to get the bottom Z of the bin -# return minv + thebin * (maxv - minv) / (nbins - 1) + # compute the height and return...nbins - 1 is to get the bottom Z of the bin + return minv + thebin * (maxv - minv) / (nbins - 1) def m_median(data, htthreshold, coverthreshold): return np.median(data) @@ -124,7 +123,7 @@ def m_abovemean(data, htthreshold, coverthreshold): # TODO check performance of other methods def m_abovemode(data, htthreshold, coverthreshold): - return (d > m_mode(data, htthreshold, coverthreshold)).sum() / len(data) + return (data > m_mode(data, htthreshold, coverthreshold)).sum() / len(data) def m_skewness(data, htthreshold, coverthreshold): if len(data) < 4: @@ -139,7 +138,7 @@ def m_kurtosis(data, htthreshold, coverthreshold): return stats.kurtosis(data) def m_aad(data, htthreshold, coverthreshold): - m = m_mean(data=, htthreshold, coverthreshold) + m = m_mean(data, htthreshold, coverthreshold) return np.mean(np.absolute(data - m)) def m_madmedian(data, htthreshold, coverthreshold): From 0978d21e7676b6f9c585b7dd0c3b8ac04126c976 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Mon, 11 Mar 2024 04:15:13 -0400 Subject: [PATCH 29/39] removed debug printing --- src/silvimetric/commands/shatter.py | 2 -- src/silvimetric/resources/metric.py | 30 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/silvimetric/commands/shatter.py b/src/silvimetric/commands/shatter.py index 76ea859..f1c9642 100644 --- a/src/silvimetric/commands/shatter.py +++ b/src/silvimetric/commands/shatter.py @@ -71,8 +71,6 @@ def get_metrics(data_in, attrs: list[str], storage: Storage): if not np.any(data['count']): return None - storage.config.log.info(data[attr]) - # doing dask compute inside the dict array because it was too fine-grained # when it was outside metric_data = { diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 8bdfa18..40173b5 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -1,4 +1,5 @@ import json +# import warnings import numpy as np from typing import Callable, Optional, Any, Union from scipy import stats @@ -11,6 +12,10 @@ from .entry import Attribute, Entry from .lmom4 import lmom4 +# trap warnings as errors...mainly for scipy.stats.skew and scipy.stats.kurtosis +# looks like this causes trouble for dask +# warnings.filterwarnings("error") + MetricFn = Callable[[np.ndarray, float, float, Optional[Union[Any, None]]], np.ndarray] # Derived information about a cell of points @@ -91,9 +96,9 @@ def m_mode(data, htthreshold, coverthreshold): if minv == maxv: return minv - bins = np.histogram(data, bins = nbins, density = False) + bins, binedges = np.histogram(data, bins = nbins, density = False) - thebin = np.argmax(bins, axis = -1) + thebin = np.argmax(bins) # compute the height and return...nbins - 1 is to get the bottom Z of the bin return minv + thebin * (maxv - minv) / (nbins - 1) @@ -125,17 +130,34 @@ def m_abovemean(data, htthreshold, coverthreshold): def m_abovemode(data, htthreshold, coverthreshold): return (data > m_mode(data, htthreshold, coverthreshold)).sum() / len(data) +# both stats.skew and stats.kurtosis throw a warning: +# Precision loss occurred in moment calculation due to catastrophic cancellation. +# This occurs when the data are nearly identical. Results may be unreliable. +# +# I think this is because values are very similar...end up close to the mean +# GridMetrics computes these using a different formula that may also have trouble +# with numeric stability. def m_skewness(data, htthreshold, coverthreshold): if len(data) < 4: return -9999.0 - return stats.skew(data) + try: + s = stats.skew(data) + except: + s = -9999.0 + + return s def m_kurtosis(data, htthreshold, coverthreshold): if len(data) < 4: return -9999.0 - return stats.kurtosis(data) + try: + k = stats.kurtosis(data) + except: + k = -9999.0 + + return k def m_aad(data, htthreshold, coverthreshold): m = m_mean(data, htthreshold, coverthreshold) From 8e6ef2b7c0213018456a1a3d7b48e507ceef6f4e Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Mon, 25 Mar 2024 15:22:46 -0400 Subject: [PATCH 30/39] added example docstrings for m_mean and m_mode --- src/silvimetric/resources/metric.py | 51 +++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 40173b5..23d22ce 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -81,15 +81,52 @@ def m_count(data, htthreshold, coverthreshold): return len(data) def m_mean(data, htthreshold, coverthreshold): + """ + Return the arithmetic mean. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + htthreshold: float + Height threshold for points used to compute metric + coverthreshold: float + Height threshold for cover calculations + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return np.mean(data) -# mode is somewhat undefined for floating point values. FUSION logic is to -# partition data into 64 bins then find the bin with the highest count. -# This approach has the disadvantage that the bin size varies depending on -# the Z range. -# Before binning, subtract min value. Computing the scaled value involves -# the bin number * bin width (max - min / #bins) + min. def m_mode(data, htthreshold, coverthreshold): + """ + Return the mode (most common value). + + Mode is somewhat undefined for floating point values. FUSION's logic is to + partition data into 64 bins then find the bin with the highest count. + This approach has the disadvantage that the bin size varies depending on + the Z range. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + htthreshold: float + Height threshold for points used to compute metric + coverthreshold: float + Height threshold for cover calculations + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + nbins = 64 maxv = np.max(data) minv = np.min(data) @@ -100,7 +137,7 @@ def m_mode(data, htthreshold, coverthreshold): thebin = np.argmax(bins) - # compute the height and return...nbins - 1 is to get the bottom Z of the bin + # compute the height and return...nbins - 1 is to get the bottom value of the bin return minv + thebin * (maxv - minv) / (nbins - 1) def m_median(data, htthreshold, coverthreshold): From ecd0395e00b25424fa77f683aa32884526e649ad Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Tue, 2 Apr 2024 09:53:57 -0400 Subject: [PATCH 31/39] Added constants for NODATA and height thresholds --- src/silvimetric/resources/constants.py | 7 +++++ src/silvimetric/resources/metric.py | 37 +++++++++++++------------- 2 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 src/silvimetric/resources/constants.py diff --git a/src/silvimetric/resources/constants.py b/src/silvimetric/resources/constants.py new file mode 100644 index 0000000..048e166 --- /dev/null +++ b/src/silvimetric/resources/constants.py @@ -0,0 +1,7 @@ +# constants.py + +"""This module defines project-level constants.""" + +NODATA = -9999.0 +DEFAULT_HT_THRESHOLD = 2.0 +DEFAULT_COVER_HT_THRESHOLD = 2.0 diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 23d22ce..0a4e364 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -10,7 +10,8 @@ import dill from .entry import Attribute, Entry -from .lmom4 import lmom4 +from . import lmom4 +from . import constants # trap warnings as errors...mainly for scipy.stats.skew and scipy.stats.kurtosis # looks like this causes trouble for dask @@ -176,23 +177,23 @@ def m_abovemode(data, htthreshold, coverthreshold): # with numeric stability. def m_skewness(data, htthreshold, coverthreshold): if len(data) < 4: - return -9999.0 + return NODATA try: s = stats.skew(data) except: - s = -9999.0 + s = NODATA return s def m_kurtosis(data, htthreshold, coverthreshold): if len(data) < 4: - return -9999.0 + return NODATA try: k = stats.kurtosis(data) except: - k = -9999.0 + k = NODATA return k @@ -228,7 +229,7 @@ def m_crr(data, htthreshold, coverthreshold): maxv = np.max(data) minv = np.min(data) if minv == maxv: - return -9999.0 + return NODATA return (np.mean(data) - minv) / (maxv - minv) @@ -251,59 +252,59 @@ def m_cumean(data, htthreshold, coverthreshold): # L1 is same as mean...compute using np.mean for speed def m_l1(data, htthreshold, coverthreshold): if len(data) < 4: - return -9999.0 + return NODATA return np.mean(data) def m_l2(data, htthreshold, coverthreshold): if len(data) < 4: - return -9999.0 + return NODATA l = lmom4(data) return l[1] def m_l3(data, htthreshold, coverthreshold): if len(data) < 4: - return -9999.0 + return NODATA l = lmom4(data) return l[2] def m_l4(data, htthreshold, coverthreshold): if len(data) < 4: - return -9999.0 + return NODATA l = lmom4(data) return l[3] def m_lcv(data, htthreshold, coverthreshold): if len(data) < 4: - return -9999.0 + return NODATA l = lmom4(data) if l[0] == 0.0: - return -9999.0 + return NODATA return l[1] / l[0] def m_lskewness(data, htthreshold, coverthreshold): if len(data) < 4: - return -9999.0 + return NODATA l = lmom4(data) if l[1] == 0.0: - return -9999.0 + return NODATA return l[2] / l[1] def m_lkurtosis(data, htthreshold, coverthreshold): if len(data) < 4: - return -9999.0 + return NODATA l = lmom4(data) if l[1] == 0.0: - return -9999.0 + return NODATA return l[3] / l[1] @@ -360,7 +361,7 @@ def m_p99(data, htthreshold, coverthreshold): def m_profilearea(data, htthreshold, coverthreshold): # sanity check...must have valid heights/elevations if np.max(data) <= 0: - return -9999.0 + return NODATA p = np.percentile(data, range(1, 100)) @@ -376,7 +377,7 @@ def m_profilearea(data, htthreshold, coverthreshold): return pa * 0.5 else: - return -9999.0 + return NODATA def m_allcover(data, htthreshold, coverthreshold): return (data > coverthreshold).sum() / len(data) * 100.0 From 8a2978746d6d218ad4da21122b5e3a7727542970 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Tue, 2 Apr 2024 11:39:04 -0400 Subject: [PATCH 32/39] Backed out use of htthreshold and coverthreshold --- docs/source/cli/initialize.rst | 2 - docs/source/tutorial.rst | 7 +- src/silvimetric/cli/cli.py | 11 +-- src/silvimetric/commands/shatter.py | 2 +- src/silvimetric/resources/config.py | 9 +- src/silvimetric/resources/constants.py | 1 + src/silvimetric/resources/metric.py | 120 ++++++++++++------------- tests/conftest.py | 18 +--- 8 files changed, 68 insertions(+), 102 deletions(-) diff --git a/docs/source/cli/initialize.rst b/docs/source/cli/initialize.rst index 6b8fe1f..97de933 100644 --- a/docs/source/cli/initialize.rst +++ b/docs/source/cli/initialize.rst @@ -28,8 +28,6 @@ Synopsis -a, --attributes ATTRS List of attributes to include in Database -m, --metrics METRICS List of metrics to include in Database --resolution FLOAT Summary pixel resolution - --htthreshold FLOAT Height threshold for all metrics - --coverthreshold FLOAT Height threshold for cover metrics --help Show this message and exit. diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index b09c789..3769847 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -140,8 +140,6 @@ Usage: -a, --attributes ATTRS List of attributes to include in Database -m, --metrics METRICS List of metrics to include in Database --resolution FLOAT Summary pixel resolution - --htthreshold FLOAT Height threshold for all metrics - --coverthreshold FLOAT Height threshold for cover metrics --help Show this message and exit. @@ -260,9 +258,8 @@ Shatter SilviMetric will take all the previously defined variables like the bounds, resolution, and our tile size, and it will split all data values up into their respective bins. From here, SilviMetric will perform each `Metric` previously -defined in :ref:`initialize` over the data in each cell, applying the --htthreshold -and coverthreshold. At the end of all that, this data will be written to a -`SparseArray` in `TileDB`, where it will be much easier to access. +defined in :ref:`initialize` over the data in each cell. At the end of all that, +this data will be written to a `SparseArray` in `TileDB`, where it will be much easier to access. Usage: diff --git a/src/silvimetric/cli/cli.py b/src/silvimetric/cli/cli.py index 4bc512b..7cd246c 100644 --- a/src/silvimetric/cli/cli.py +++ b/src/silvimetric/cli/cli.py @@ -102,14 +102,9 @@ def scan_cmd(app, resolution, point_count, pointcloud, bounds, depth, filter): help="List of metrics to include in Database") @click.option("--resolution", type=float, default=30.0, help="Summary pixel resolution") -@click.option("--htthreshold", type=float, default=2.0, - help="Height threshold for all metrics") -@click.option("--coverthreshold", type=float, default=2.0, - help="Height threshold for cover metrics") @click.pass_obj def initialize_cmd(app: ApplicationConfig, bounds: Bounds, crs: pyproj.CRS, - attributes: list[Attribute], resolution: float, htthreshold: float, - coverthreshold: float, metrics: list[Metric]): + attributes: list[Attribute], resolution: float, metrics: list[Metric]): import itertools """Initialize silvimetrics DATABASE """ @@ -119,9 +114,7 @@ def initialize_cmd(app: ApplicationConfig, bounds: Bounds, crs: pyproj.CRS, crs = crs, attrs = attributes, metrics = list(itertools.chain(*metrics)), - resolution = resolution, - htthreshold = htthreshold, - coverthreshold = coverthreshold) + resolution = resolution) return initialize.initialize(storageconfig) diff --git a/src/silvimetric/commands/shatter.py b/src/silvimetric/commands/shatter.py index f1c9642..c3a3389 100644 --- a/src/silvimetric/commands/shatter.py +++ b/src/silvimetric/commands/shatter.py @@ -74,7 +74,7 @@ def get_metrics(data_in, attrs: list[str], storage: Storage): # doing dask compute inside the dict array because it was too fine-grained # when it was outside metric_data = { - f'{m.entry_name(attr)}': [m(cell_data, storage.config.htthreshold, storage.config.coverthreshold) for cell_data in data[attr]] + f'{m.entry_name(attr)}': [m(cell_data) for cell_data in data[attr]] for attr in attrs for m in storage.config.metrics } data_out = data | metric_data diff --git a/src/silvimetric/resources/config.py b/src/silvimetric/resources/config.py index d21f8b1..c2fba36 100644 --- a/src/silvimetric/resources/config.py +++ b/src/silvimetric/resources/config.py @@ -15,6 +15,7 @@ from .extents import Bounds from .metric import Metric, Metrics from .entry import Attribute, Attributes +from . import constants from . import __version__ @@ -52,9 +53,7 @@ def __repr__(self): class StorageConfig(Config): root: Bounds crs: pyproj.CRS - resolution: float = 30.0 - htthreshold: float = 2.0 - coverthreshold: float = 2.0 + resolution: float = DEFAULT_RESOLUTION attrs: list[Attribute] = field(default_factory=lambda: [ Attribute(a, Attributes[a].dtype) @@ -124,8 +123,6 @@ def from_string(cls, data: str): root = root, log = Log(**x['log']), resolution = x['resolution'], - htthreshold = x['htthreshold'], - coverthreshold = x['coverthreshold'], attrs = attrs, crs = crs, metrics = ms, @@ -249,8 +246,6 @@ def __post_init__(self) -> None: p.mkdir(parents=True, exist_ok=True) self.resolution: float = config.resolution - self.htthreshold: float = config.htthreshold - self.coverthreshold: float = config.coverthreshold self.crs: pyproj.CRS = config.crs def to_json(self): diff --git a/src/silvimetric/resources/constants.py b/src/silvimetric/resources/constants.py index 048e166..1393fde 100644 --- a/src/silvimetric/resources/constants.py +++ b/src/silvimetric/resources/constants.py @@ -5,3 +5,4 @@ NODATA = -9999.0 DEFAULT_HT_THRESHOLD = 2.0 DEFAULT_COVER_HT_THRESHOLD = 2.0 +DEFAULT_RESOLUTION = 30.0 diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 0a4e364..2d756ae 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -70,18 +70,18 @@ def __eq__(self, other): self.dependencies == other.dependencies and self._method == other._method) - def __call__(self, data: np.ndarray, htthreshold: float, coverthreshold: float) -> np.ndarray: - return self._method(data, htthreshold, coverthreshold) + def __call__(self, data: np.ndarray) -> np.ndarray: + return self._method(data) def __repr__(self) -> str: return json.dumps(self.to_json()) #TODO add all metrics from https://github.com/hobuinc/silvimetric/issues/5 -def m_count(data, htthreshold, coverthreshold): +def m_count(data): return len(data) -def m_mean(data, htthreshold, coverthreshold): +def m_mean(data): """ Return the arithmetic mean. @@ -89,10 +89,6 @@ def m_mean(data, htthreshold, coverthreshold): ---------- data : numpy.ndarray Data for dimension - htthreshold: float - Height threshold for points used to compute metric - coverthreshold: float - Height threshold for cover calculations Returns ------- @@ -103,11 +99,11 @@ def m_mean(data, htthreshold, coverthreshold): return np.mean(data) -def m_mode(data, htthreshold, coverthreshold): +def m_mode(data): """ Return the mode (most common value). - Mode is somewhat undefined for floating point values. FUSION's logic is to + Mode is practically undefined for floating point values. FUSION's logic is to partition data into 64 bins then find the bin with the highest count. This approach has the disadvantage that the bin size varies depending on the Z range. @@ -116,10 +112,6 @@ def m_mode(data, htthreshold, coverthreshold): ---------- data : numpy.ndarray Data for dimension - htthreshold: float - Height threshold for points used to compute metric - coverthreshold: float - Height threshold for cover calculations Returns ------- @@ -141,32 +133,32 @@ def m_mode(data, htthreshold, coverthreshold): # compute the height and return...nbins - 1 is to get the bottom value of the bin return minv + thebin * (maxv - minv) / (nbins - 1) -def m_median(data, htthreshold, coverthreshold): +def m_median(data): return np.median(data) -def m_min(data, htthreshold, coverthreshold): +def m_min(data): return np.min(data) -def m_max(data, htthreshold, coverthreshold): +def m_max(data): return np.max(data) -def m_stddev(data, htthreshold, coverthreshold): +def m_stddev(data): return np.std(data) # start of new metrics to match FUSION -def m_variance(data, htthreshold, coverthreshold): +def m_variance(data): return np.var(data) -def m_cv(data, htthreshold, coverthreshold): +def m_cv(data): return np.std(data) / np.mean(data) # TODO check performance of other methods -def m_abovemean(data, htthreshold, coverthreshold): +def m_abovemean(data): return (data > np.mean(data)).sum() / len(data) # TODO check performance of other methods -def m_abovemode(data, htthreshold, coverthreshold): - return (data > m_mode(data, htthreshold, coverthreshold)).sum() / len(data) +def m_abovemode(data): + return (data > m_mode(data)).sum() / len(data) # both stats.skew and stats.kurtosis throw a warning: # Precision loss occurred in moment calculation due to catastrophic cancellation. @@ -175,7 +167,7 @@ def m_abovemode(data, htthreshold, coverthreshold): # I think this is because values are very similar...end up close to the mean # GridMetrics computes these using a different formula that may also have trouble # with numeric stability. -def m_skewness(data, htthreshold, coverthreshold): +def m_skewness(data): if len(data) < 4: return NODATA @@ -186,7 +178,7 @@ def m_skewness(data, htthreshold, coverthreshold): return s -def m_kurtosis(data, htthreshold, coverthreshold): +def m_kurtosis(data): if len(data) < 4: return NODATA @@ -197,35 +189,35 @@ def m_kurtosis(data, htthreshold, coverthreshold): return k -def m_aad(data, htthreshold, coverthreshold): - m = m_mean(data, htthreshold, coverthreshold) +def m_aad(data): + m = m_mean(data) return np.mean(np.absolute(data - m)) -def m_madmedian(data, htthreshold, coverthreshold): +def m_madmedian(data): return stats.median_abs_deviation(data) -def m_madmean(data, htthreshold, coverthreshold): +def m_madmean(data): return stats.median_abs_deviation(data, center=np.mean) # TODO needs work -def m_madmode(data, htthreshold, coverthreshold): - m = m_mode(data, htthreshold, coverthreshold) +def m_madmode(data): + m = m_mode(data) return np.median(np.absolute(data - m)) # TODO test various methods for interpolation=... for all percentile-related metrics # I think the default matches FUSION method but need to test -def m_iq(data, htthreshold, coverthreshold): +def m_iq(data): return stats.iqr(data) -def m_90m10(data, htthreshold, coverthreshold): +def m_90m10(data): p = np.percentile(data, [10,90]) return p[1] - p[0] -def m_95m05(data, htthreshold, coverthreshold): +def m_95m05(data): p = np.percentile(data, [5,95]) return p[1] - p[0] -def m_crr(data, htthreshold, coverthreshold): +def m_crr(data): maxv = np.max(data) minv = np.min(data) if minv == maxv: @@ -233,10 +225,10 @@ def m_crr(data, htthreshold, coverthreshold): return (np.mean(data) - minv) / (maxv - minv) -def m_sqmean(data, htthreshold, coverthreshold): +def m_sqmean(data): return np.sqrt(np.mean(np.square(data))) -def m_cumean(data, htthreshold, coverthreshold): +def m_cumean(data): return np.cbrt(np.mean(np.power(np.absolute(data), 3))) # TODO compute L-moments. These are done separately because we only add @@ -250,34 +242,34 @@ def m_cumean(data, htthreshold, coverthreshold): # statement with derived works) # L1 is same as mean...compute using np.mean for speed -def m_l1(data, htthreshold, coverthreshold): +def m_l1(data): if len(data) < 4: return NODATA return np.mean(data) -def m_l2(data, htthreshold, coverthreshold): +def m_l2(data): if len(data) < 4: return NODATA l = lmom4(data) return l[1] -def m_l3(data, htthreshold, coverthreshold): +def m_l3(data): if len(data) < 4: return NODATA l = lmom4(data) return l[2] -def m_l4(data, htthreshold, coverthreshold): +def m_l4(data): if len(data) < 4: return NODATA l = lmom4(data) return l[3] -def m_lcv(data, htthreshold, coverthreshold): +def m_lcv(data): if len(data) < 4: return NODATA @@ -288,7 +280,7 @@ def m_lcv(data, htthreshold, coverthreshold): return l[1] / l[0] -def m_lskewness(data, htthreshold, coverthreshold): +def m_lskewness(data): if len(data) < 4: return NODATA @@ -298,7 +290,7 @@ def m_lskewness(data, htthreshold, coverthreshold): return l[2] / l[1] -def m_lkurtosis(data, htthreshold, coverthreshold): +def m_lkurtosis(data): if len(data) < 4: return NODATA @@ -313,52 +305,52 @@ def m_lkurtosis(data, htthreshold, coverthreshold): #def m_percentiles(data): # return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) -def m_p01(data, htthreshold, coverthreshold): +def m_p01(data): return(np.percentile(data, 1)) -def m_p05(data, htthreshold, coverthreshold): +def m_p05(data): return(np.percentile(data, 5)) -def m_p10(data, htthreshold, coverthreshold): +def m_p10(data): return(np.percentile(data, 10)) -def m_p20(data, htthreshold, coverthreshold): +def m_p20(data): return(np.percentile(data, 20)) -def m_p25(data, htthreshold, coverthreshold): +def m_p25(data): return(np.percentile(data, 25)) -def m_p30(data, htthreshold, coverthreshold): +def m_p30(data): return(np.percentile(data, 30)) -def m_p40(data, htthreshold, coverthreshold): +def m_p40(data): return(np.percentile(data, 40)) -def m_p50(data, htthreshold, coverthreshold): +def m_p50(data): return(np.percentile(data, 50)) -def m_p60(data, htthreshold, coverthreshold): +def m_p60(data): return(np.percentile(data, 60)) -def m_p70(data, htthreshold, coverthreshold): +def m_p70(data): return(np.percentile(data, 70)) -def m_p75(data, htthreshold, coverthreshold): +def m_p75(data): return(np.percentile(data, 75)) -def m_p80(data, htthreshold, coverthreshold): +def m_p80(data): return(np.percentile(data, 80)) -def m_p90(data, htthreshold, coverthreshold): +def m_p90(data): return(np.percentile(data, 90)) -def m_p95(data, htthreshold, coverthreshold): +def m_p95(data): return(np.percentile(data, 95)) -def m_p99(data, htthreshold, coverthreshold): +def m_p99(data): return(np.percentile(data, 99)) -def m_profilearea(data, htthreshold, coverthreshold): +def m_profilearea(data): # sanity check...must have valid heights/elevations if np.max(data) <= 0: return NODATA @@ -379,8 +371,10 @@ def m_profilearea(data, htthreshold, coverthreshold): else: return NODATA -def m_allcover(data, htthreshold, coverthreshold): - return (data > coverthreshold).sum() / len(data) * 100.0 +# not sure how this will be computed once height and cover thresholds are +# sorted out +#def m_allcover(data): +# return (data > coverthreshold).sum() / len(data) * 100.0 Metrics = { 'count' : Metric('count', m_count), @@ -427,6 +421,6 @@ def m_allcover(data, htthreshold, coverthreshold): 'p90' : Metric('p90', m_p90), 'p95' : Metric('p95', m_p95), 'p99' : Metric('p99', m_p99), - 'allcover' : Metric('allcover', m_allcover), +# 'allcover' : Metric('allcover', m_allcover), 'profilearea' : Metric('profilearea', m_profilearea), } diff --git a/tests/conftest.py b/tests/conftest.py index 7163d4f..306c52a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,15 +24,13 @@ def threaded_dask(): dask.config.set(scheduler="threads") @pytest.fixture(scope='function') -def storage_config(tdb_filepath, bounds, resolution, htthreshold, coverthreshold, crs, attrs, metrics): +def storage_config(tdb_filepath, bounds, resolution, crs, attrs, metrics): log = Log(20) yield StorageConfig(tdb_dir = tdb_filepath, log = log, crs = crs, root = bounds, resolution = resolution, - htthreshold = htthreshold, - coverthreshold = coverthreshold, attrs = attrs, metrics = metrics, version = svversion) @@ -70,8 +68,6 @@ def uneven_storage_config(tdb_filepath, bounds, crs, attrs, metrics): crs = crs, root = bounds, resolution = 7, - htthreshold = 2, - coverthreshold = 2, attrs = attrs, metrics = metrics, version = svversion) @@ -102,8 +98,8 @@ def s3_uri(s3_bucket): yield f"s3://{s3_bucket}/test_silvimetric/{uuid}" @pytest.fixture(scope="function") -def s3_storage_config(s3_uri, bounds, resolution, htthreshold, coverthreshold, crs, attrs, metrics): - yield StorageConfig(bounds, crs, resolution, htthreshold, coverthreshold, attrs, metrics, +def s3_storage_config(s3_uri, bounds, resolution, crs, attrs, metrics): + yield StorageConfig(bounds, crs, resolution, attrs, metrics, svversion, tdb_dir=s3_uri) @pytest.fixture(scope='function') @@ -156,14 +152,6 @@ def dims(): def resolution() -> int: yield 30 -@pytest.fixture(scope='class') -def htthreshold() -> int: - yield 2 - -@pytest.fixture(scope='class') -def coverthreshold() -> int: - yield 2 - @pytest.fixture(scope='class') def test_point_count() -> int: yield 90000 From dd6053e19920a1acd9fbbc20121041169bcf2a12 Mon Sep 17 00:00:00 2001 From: bmcgaughey1 Date: Tue, 2 Apr 2024 12:34:33 -0400 Subject: [PATCH 33/39] Added docstrings to all metrics functions --- src/silvimetric/resources/metric.py | 670 +++++++++++++++++++++++++++- 1 file changed, 667 insertions(+), 3 deletions(-) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 2d756ae..6fc5355 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -134,30 +134,151 @@ def m_mode(data): return minv + thebin * (maxv - minv) / (nbins - 1) def m_median(data): + """ + Return the median. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return np.median(data) def m_min(data): + """ + Return the minimum value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return np.min(data) def m_max(data): + """ + Return the maximum value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return np.max(data) def m_stddev(data): + """ + Return the standard deviation of values. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return np.std(data) # start of new metrics to match FUSION def m_variance(data): + """ + Return the variance of values. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return np.var(data) def m_cv(data): + """ + Return the coefficient of variation of values. + Standard deviation / mean. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return np.std(data) / np.mean(data) # TODO check performance of other methods def m_abovemean(data): + """ + Return the proportion of values above the mean. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return (data > np.mean(data)).sum() / len(data) # TODO check performance of other methods def m_abovemode(data): + """ + Return the proportion of values above the mode. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return (data > m_mode(data)).sum() / len(data) # both stats.skew and stats.kurtosis throw a warning: @@ -168,6 +289,21 @@ def m_abovemode(data): # GridMetrics computes these using a different formula that may also have trouble # with numeric stability. def m_skewness(data): + """ + Return the skewness of values. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + if len(data) < 4: return NODATA @@ -179,6 +315,21 @@ def m_skewness(data): return s def m_kurtosis(data): + """ + Return the kurtosis of values. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + if len(data) < 4: return NODATA @@ -190,34 +341,155 @@ def m_kurtosis(data): return k def m_aad(data): + """ + Return the average absolute deviation from the mean. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + m = m_mean(data) return np.mean(np.absolute(data - m)) def m_madmedian(data): + """ + Return the median absolute difference from the median value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return stats.median_abs_deviation(data) def m_madmean(data): + """ + Return the median absolute difference from the mean value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return stats.median_abs_deviation(data, center=np.mean) # TODO needs work def m_madmode(data): + """ + Return the median absolute difference from the mode value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + m = m_mode(data) return np.median(np.absolute(data - m)) # TODO test various methods for interpolation=... for all percentile-related metrics # I think the default matches FUSION method but need to test def m_iq(data): + """ + Return the interquartile difference. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return stats.iqr(data) def m_90m10(data): + """ + Return the 90th percentile minus the 10th percentile. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + p = np.percentile(data, [10,90]) return p[1] - p[0] def m_95m05(data): + """ + Return the 95th percentile minnus the 5th percentile. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + p = np.percentile(data, [5,95]) return p[1] - p[0] def m_crr(data): + """ + Return the cannopy relief ratio. + (mean - min) / (max - min). + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + maxv = np.max(data) minv = np.min(data) if minv == maxv: @@ -226,10 +498,40 @@ def m_crr(data): return (np.mean(data) - minv) / (maxv - minv) def m_sqmean(data): - return np.sqrt(np.mean(np.square(data))) + """ + Return the square root of the average squared value. -def m_cumean(data): - return np.cbrt(np.mean(np.power(np.absolute(data), 3))) + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + + return np.sqrt(np.mean(np.square(data))) + +def m_cumean(data): + """ + Return the cube root of the average cubed value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + + return np.cbrt(np.mean(np.power(np.absolute(data), 3))) # TODO compute L-moments. These are done separately because we only add # a single element to TileDB. This is very inefficient since we have to @@ -243,12 +545,42 @@ def m_cumean(data): # L1 is same as mean...compute using np.mean for speed def m_l1(data): + """ + Return the first L-moment (mean). + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + if len(data) < 4: return NODATA return np.mean(data) def m_l2(data): + """ + Return the second L-moment. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + if len(data) < 4: return NODATA @@ -256,6 +588,21 @@ def m_l2(data): return l[1] def m_l3(data): + """ + Return the third L-moment. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + if len(data) < 4: return NODATA @@ -263,6 +610,21 @@ def m_l3(data): return l[2] def m_l4(data): + """ + Return the fourth L-moment. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + if len(data) < 4: return NODATA @@ -270,6 +632,21 @@ def m_l4(data): return l[3] def m_lcv(data): + """ + Return the L-moment coefficient of variation (L2 / mean). + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + if len(data) < 4: return NODATA @@ -281,6 +658,21 @@ def m_lcv(data): return l[1] / l[0] def m_lskewness(data): + """ + Return the L-moment skewness (L3 / L2). + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + if len(data) < 4: return NODATA @@ -291,6 +683,21 @@ def m_lskewness(data): return l[2] / l[1] def m_lkurtosis(data): + """ + Return the L-moment kurtosis (L4 / L2). + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + if len(data) < 4: return NODATA @@ -306,51 +713,308 @@ def m_lkurtosis(data): # return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) def m_p01(data): + """ + Return the 1st percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 1)) def m_p05(data): + """ + Return the 5th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 5)) def m_p10(data): + """ + Return the 10th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 10)) def m_p20(data): + """ + Return the 20th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 20)) def m_p25(data): + """ + Return the 25th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 25)) def m_p30(data): + """ + Return the 30th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 30)) def m_p40(data): + """ + Return the 40th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 40)) def m_p50(data): + """ + Return the 50th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 50)) def m_p60(data): + """ + Return the 60th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 60)) def m_p70(data): + """ + Return the 70th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 70)) def m_p75(data): + """ + Return the 75th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 75)) def m_p80(data): + """ + Return the 80th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 80)) def m_p90(data): + """ + Return the 90th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 90)) def m_p95(data): + """ + Return the 95th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 95)) def m_p99(data): + """ + Return the 99th percentile value. + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + return(np.percentile(data, 99)) def m_profilearea(data): + """ + Return the profile area. + + Profile area was first described in + + Hu, Tianyu, Qin Ma, Yanjun Su, John J. Battles, Brandon M. Collins, + Scott L. Stephens, Maggi Kelly, Qinghua Guo. 2019. A simple and + integrated approach for fire severity assessment using bi-temporal + airborne LiDAR data. Int J. Appl Earth Obs Geoinformation, 78 (2019): 25-38. + + as the area under the height percentile profile or curve. They found the + metric useful to compare pre- and post-fire canopy structure at both the + individual tree and pixel scales. The implementation in CloudMetrics and + GridMetrics varies from that described in Hu et al. 2019. Heights are + normalized using the 99th percentile height instead of the maximum height + to eliminate problems related to high outliers and the area under the + percentile curve is computed directly from 1 percent slices instead of + fitting a polynomial to the percentile heights and computing the area + under the polynomial + + Parameters + ---------- + data : numpy.ndarray + Data for dimension + + Returns + ------- + float + Value for metric computed using values in data. Returns + NODATA value when metric cannot be computed + """ + # sanity check...must have valid heights/elevations if np.max(data) <= 0: return NODATA From 9a6240bf4b360bf3fb0542ad4e6abdcb8bd3e916 Mon Sep 17 00:00:00 2001 From: Kyle Mann Date: Mon, 16 Sep 2024 09:09:37 -0500 Subject: [PATCH 34/39] Kmann/metrics (#91) Merging kmann/metrics - Adds metric dependencies - Adds metric graphs - Adds filters for metrics - Changes to lmoments3 python package for L moments - Moves to dataframes for metric manipulation - Updated base SilviMetric package --- .gitignore | 5 +- README.md | 26 +- docs/create.sh | 4 + docs/requirements.txt | 4 + docs/source/api/commands/delete.rst | 4 + docs/source/api/commands/extract.rst | 7 + docs/source/api/commands/index.rst | 11 + docs/source/api/commands/info.rst | 7 + docs/source/api/commands/initialize.rst | 7 + docs/source/api/commands/restart.rst | 4 + docs/source/api/commands/resume.rst | 4 + docs/source/api/commands/scan.rst | 7 + docs/source/api/commands/shatter.rst | 7 + docs/source/{api.rst => api/index.rst} | 18 +- docs/source/api/resources/bounds.rst | 6 + docs/source/api/resources/config.rst | 5 + docs/source/api/resources/data.rst | 7 + docs/source/api/resources/entry.rst | 15 + docs/source/api/resources/extents.rst | 5 + docs/source/api/resources/index.rst | 10 + docs/source/api/resources/log.rst | 7 + docs/source/api/resources/storage.rst | 6 + docs/source/cli/extract.rst | 16 +- docs/source/cli/index.rst | 38 +- docs/source/cli/info.rst | 22 +- docs/source/cli/initialize.rst | 13 +- docs/source/cli/scan.rst | 21 +- docs/source/cli/shatter.rst | 22 +- docs/source/conf.py | 18 + docs/source/index.rst | 4 +- docs/source/quickstart.rst | 115 +- docs/source/substitutions.txt | 1 + docs/source/tutorial.rst | 2 +- environment.yml | 6 +- src/silvimetric/__init__.py | 21 +- src/silvimetric/cli/cli.py | 136 +- src/silvimetric/cli/common.py | 120 +- src/silvimetric/commands/extract.py | 185 +-- src/silvimetric/commands/info.py | 51 +- src/silvimetric/commands/initialize.py | 13 +- src/silvimetric/commands/manage.py | 54 + src/silvimetric/commands/scan.py | 93 +- src/silvimetric/commands/shatter.py | 320 +++-- src/silvimetric/resources/__init__.py | 10 - src/silvimetric/resources/array_extensions.py | 115 ++ src/silvimetric/resources/attribute.py | 84 ++ src/silvimetric/resources/bounds.py | 81 +- src/silvimetric/resources/config.py | 177 ++- src/silvimetric/resources/data.py | 130 +- src/silvimetric/resources/entry.py | 76 - src/silvimetric/resources/extents.py | 131 +- src/silvimetric/resources/lmom4.py | 29 - src/silvimetric/resources/log.py | 21 +- src/silvimetric/resources/metric.py | 1259 +++-------------- src/silvimetric/resources/metrics/__init__.py | 13 + src/silvimetric/resources/metrics/aad.py | 24 + src/silvimetric/resources/metrics/filters.py | 4 + .../resources/metrics/l_moments.py | 142 ++ .../resources/metrics/p_moments.py | 31 + .../resources/metrics/percentiles.py | 78 + src/silvimetric/resources/metrics/stats.py | 128 ++ src/silvimetric/resources/storage.py | 342 +++-- tests/conftest.py | 187 +-- tests/create_test_data.py | 6 +- tests/data/df_sample | Bin 0 -> 24569 bytes tests/data/test_data.copc.laz | Bin 399984 -> 402013 bytes tests/fixtures/chunk_fixtures.py | 26 + tests/fixtures/cli_fixtures.py | 15 + tests/fixtures/command_fixtures.py | 36 + tests/fixtures/dask_fixtures.py | 40 + tests/fixtures/data_fixtures.py | 28 + tests/fixtures/extract_fixtures.py | 39 + tests/fixtures/fake_metrics.py | 13 + tests/fixtures/fusion_fixtures.py | 6 + tests/fixtures/metric_fixtures.py | 81 ++ tests/fixtures/shatter_fixtures.py | 96 ++ tests/fixtures/western_fixtures.py | 64 + tests/test_cli.py | 148 ++ tests/test_commands.py | 103 +- tests/test_configuration.py | 54 +- tests/test_data.py | 60 +- tests/{test_chunk.py => test_extents.py} | 24 +- tests/test_extract.py | 71 +- tests/test_fusion.py | 5 + tests/test_metrics.py | 183 +-- tests/test_shatter.py | 115 +- tests/test_storage.py | 35 +- tests/test_western.py | 66 +- 88 files changed, 3557 insertions(+), 2266 deletions(-) create mode 100755 docs/create.sh create mode 100644 docs/source/api/commands/delete.rst create mode 100644 docs/source/api/commands/extract.rst create mode 100644 docs/source/api/commands/index.rst create mode 100644 docs/source/api/commands/info.rst create mode 100644 docs/source/api/commands/initialize.rst create mode 100644 docs/source/api/commands/restart.rst create mode 100644 docs/source/api/commands/resume.rst create mode 100644 docs/source/api/commands/scan.rst create mode 100644 docs/source/api/commands/shatter.rst rename docs/source/{api.rst => api/index.rst} (55%) create mode 100644 docs/source/api/resources/bounds.rst create mode 100644 docs/source/api/resources/config.rst create mode 100644 docs/source/api/resources/data.rst create mode 100644 docs/source/api/resources/entry.rst create mode 100644 docs/source/api/resources/extents.rst create mode 100644 docs/source/api/resources/index.rst create mode 100644 docs/source/api/resources/log.rst create mode 100644 docs/source/api/resources/storage.rst create mode 100644 src/silvimetric/commands/manage.py create mode 100644 src/silvimetric/resources/array_extensions.py create mode 100644 src/silvimetric/resources/attribute.py delete mode 100644 src/silvimetric/resources/entry.py delete mode 100644 src/silvimetric/resources/lmom4.py create mode 100644 src/silvimetric/resources/metrics/__init__.py create mode 100644 src/silvimetric/resources/metrics/aad.py create mode 100644 src/silvimetric/resources/metrics/filters.py create mode 100644 src/silvimetric/resources/metrics/l_moments.py create mode 100644 src/silvimetric/resources/metrics/p_moments.py create mode 100644 src/silvimetric/resources/metrics/percentiles.py create mode 100644 src/silvimetric/resources/metrics/stats.py create mode 100644 tests/data/df_sample create mode 100644 tests/fixtures/chunk_fixtures.py create mode 100644 tests/fixtures/cli_fixtures.py create mode 100644 tests/fixtures/command_fixtures.py create mode 100644 tests/fixtures/dask_fixtures.py create mode 100644 tests/fixtures/data_fixtures.py create mode 100644 tests/fixtures/extract_fixtures.py create mode 100644 tests/fixtures/fake_metrics.py create mode 100644 tests/fixtures/fusion_fixtures.py create mode 100644 tests/fixtures/metric_fixtures.py create mode 100644 tests/fixtures/shatter_fixtures.py create mode 100644 tests/fixtures/western_fixtures.py create mode 100644 tests/test_cli.py rename tests/{test_chunk.py => test_extents.py} (89%) create mode 100644 tests/test_fusion.py diff --git a/.gitignore b/.gitignore index aeecde4..85d76b8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,12 @@ __pycache__/ build/ dist/ stats/ -*.html .env +*.html +**/html/** +**/_build/** + #tiledb **/__fragments/** **/__commits/** diff --git a/README.md b/README.md index 0bde116..d7be137 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ ## SilviMetric -SilviMetric is an open source library and set of command line utilities for extracting point cloud metrics -into a TileDB database. See https://silvimetric.com for documentation and tutorials. +SilviMetric is an open source library and set of command line utilities for extracting point cloud metrics into a TileDB database. See https://silvimetric.com for documentation and tutorials. [](https://silvimetric.com/) @@ -10,3 +9,26 @@ into a TileDB database. See https://silvimetric.com for documentation and tutori GitHub hosts the project at https://github.com/hobuinc/silvimetric + +### Installation +These scripts will install `Silvimetric` dependencies as python libraries to the conda environment silvimetric. + +`Silvimetric` requires that we install some packages from `conda` (`TileDB`, and `PDAL`) so it's usually easier to use only `conda` to handle your environment. If this is unavailable to you, you will need to install `TileDB` and `PDAL` from source before installing the python packages that are dependent on those (`python-pdal` and `tiledb-py`). + +##### Pip and Conda + +``` +conda env create -f https://raw.githubusercontent.com/hobuinc/silvimetric/main/environment.yml +conda activate silvimetric +pip install silvimetric +``` + +##### Source + +``` +git clone https://github.com/hobuinc/silvimetric.git && cd silvimetric +conda env create -f environment.yml && conda activate silvimetric +pip install . +# To install for development: +# pip install -e . +``` \ No newline at end of file diff --git a/docs/create.sh b/docs/create.sh new file mode 100755 index 0000000..6bf92bd --- /dev/null +++ b/docs/create.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +# This assumes you've already done pip install -r requirements.txt in docs dir +python -m sphinx -T -b html -d _build/doctrees -D language=en source html \ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt index 7cf05da..a06057b 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,3 +3,7 @@ sphinxcontrib-bibtex sphinx-rtd-theme sphinxcontrib-spelling sphinx-notfound-page +numpy +tiledb +dask +. \ No newline at end of file diff --git a/docs/source/api/commands/delete.rst b/docs/source/api/commands/delete.rst new file mode 100644 index 0000000..8e53fca --- /dev/null +++ b/docs/source/api/commands/delete.rst @@ -0,0 +1,4 @@ +Delete +====== + +.. autofunction:: silvimetric.commands.manage.delete \ No newline at end of file diff --git a/docs/source/api/commands/extract.rst b/docs/source/api/commands/extract.rst new file mode 100644 index 0000000..031f5eb --- /dev/null +++ b/docs/source/api/commands/extract.rst @@ -0,0 +1,7 @@ +Extract +============================= + +.. automodule:: silvimetric.commands.extract + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/api/commands/index.rst b/docs/source/api/commands/index.rst new file mode 100644 index 0000000..6f491b7 --- /dev/null +++ b/docs/source/api/commands/index.rst @@ -0,0 +1,11 @@ +.. toctree:: + :maxdepth: 1 + + initialize + scan + shatter + info + extract + delete + resume + restart \ No newline at end of file diff --git a/docs/source/api/commands/info.rst b/docs/source/api/commands/info.rst new file mode 100644 index 0000000..4d21ef0 --- /dev/null +++ b/docs/source/api/commands/info.rst @@ -0,0 +1,7 @@ +Info +=========================== + +.. automodule:: silvimetric.commands.info + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/api/commands/initialize.rst b/docs/source/api/commands/initialize.rst new file mode 100644 index 0000000..796c612 --- /dev/null +++ b/docs/source/api/commands/initialize.rst @@ -0,0 +1,7 @@ +Initialize +======================== + +.. automodule:: silvimetric.commands.initialize + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/api/commands/restart.rst b/docs/source/api/commands/restart.rst new file mode 100644 index 0000000..585e6fa --- /dev/null +++ b/docs/source/api/commands/restart.rst @@ -0,0 +1,4 @@ +Restart +======= + +.. autofunction:: silvimetric.commands.manage.restart \ No newline at end of file diff --git a/docs/source/api/commands/resume.rst b/docs/source/api/commands/resume.rst new file mode 100644 index 0000000..03a7aa3 --- /dev/null +++ b/docs/source/api/commands/resume.rst @@ -0,0 +1,4 @@ +Resume +====== + +.. autofunction:: silvimetric.commands.manage.resume \ No newline at end of file diff --git a/docs/source/api/commands/scan.rst b/docs/source/api/commands/scan.rst new file mode 100644 index 0000000..7e7b4ed --- /dev/null +++ b/docs/source/api/commands/scan.rst @@ -0,0 +1,7 @@ +Scan +=========================== + +.. automodule:: silvimetric.commands.scan + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/api/commands/shatter.rst b/docs/source/api/commands/shatter.rst new file mode 100644 index 0000000..9804b84 --- /dev/null +++ b/docs/source/api/commands/shatter.rst @@ -0,0 +1,7 @@ +Shatter +============================ + +.. automodule:: silvimetric.commands.shatter + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/api.rst b/docs/source/api/index.rst similarity index 55% rename from docs/source/api.rst rename to docs/source/api/index.rst index 910969b..28962d2 100644 --- a/docs/source/api.rst +++ b/docs/source/api/index.rst @@ -4,7 +4,19 @@ API ================================================================================ -.. autosummary:: - :toctree: generated - silvimetric +Resources +--------- + +.. toctree:: + :maxdepth: 1 + + resources/index + +Commands +-------- + +.. toctree:: + :maxdepth: 1 + + commands/index \ No newline at end of file diff --git a/docs/source/api/resources/bounds.rst b/docs/source/api/resources/bounds.rst new file mode 100644 index 0000000..674efa2 --- /dev/null +++ b/docs/source/api/resources/bounds.rst @@ -0,0 +1,6 @@ +Bounds +========================== + +.. automodule:: silvimetric.resources.bounds + :members: + :undoc-members: \ No newline at end of file diff --git a/docs/source/api/resources/config.rst b/docs/source/api/resources/config.rst new file mode 100644 index 0000000..0210924 --- /dev/null +++ b/docs/source/api/resources/config.rst @@ -0,0 +1,5 @@ +Config +============================== + +.. automodule:: silvimetric.resources.config + :members: \ No newline at end of file diff --git a/docs/source/api/resources/data.rst b/docs/source/api/resources/data.rst new file mode 100644 index 0000000..7beb989 --- /dev/null +++ b/docs/source/api/resources/data.rst @@ -0,0 +1,7 @@ +Data +===================== + +.. automodule:: silvimetric.resources.data + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/api/resources/entry.rst b/docs/source/api/resources/entry.rst new file mode 100644 index 0000000..c9445ef --- /dev/null +++ b/docs/source/api/resources/entry.rst @@ -0,0 +1,15 @@ +Attribute +---------------------------------- + +.. automodule:: silvimetric.resources.attribute + :members: + :undoc-members: + :show-inheritance: + +Metric +---------------------------------- + +.. automodule:: silvimetric.resources.metric + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/api/resources/extents.rst b/docs/source/api/resources/extents.rst new file mode 100644 index 0000000..ae87329 --- /dev/null +++ b/docs/source/api/resources/extents.rst @@ -0,0 +1,5 @@ +Extents +============================ + +.. autoclass:: silvimetric.resources.extents.Extents + :members: \ No newline at end of file diff --git a/docs/source/api/resources/index.rst b/docs/source/api/resources/index.rst new file mode 100644 index 0000000..8f01c8a --- /dev/null +++ b/docs/source/api/resources/index.rst @@ -0,0 +1,10 @@ +.. toctree:: + :maxdepth: 1 + + config + bounds + data + log + storage + entry + extents \ No newline at end of file diff --git a/docs/source/api/resources/log.rst b/docs/source/api/resources/log.rst new file mode 100644 index 0000000..613e790 --- /dev/null +++ b/docs/source/api/resources/log.rst @@ -0,0 +1,7 @@ +Log +====================== + +.. automodule:: silvimetric.resources.log + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/api/resources/storage.rst b/docs/source/api/resources/storage.rst new file mode 100644 index 0000000..f661c07 --- /dev/null +++ b/docs/source/api/resources/storage.rst @@ -0,0 +1,6 @@ +Storage +------------------------------------ + +.. automodule:: silvimetric.resources.storage + :members: + :undoc-members: \ No newline at end of file diff --git a/docs/source/cli/extract.rst b/docs/source/cli/extract.rst index a4703a0..cf07872 100644 --- a/docs/source/cli/extract.rst +++ b/docs/source/cli/extract.rst @@ -16,16 +16,22 @@ Synopsis .. code-block:: - Usage: silvimetric DATABASE extract [OPTIONS] + Usage: silvimetric [OPTIONS] extract [OPTIONS] Extract silvimetric metrics from DATABASE Options: - -a, --attributes ATTRS List of attributes to include in Database - -m, --metrics METRICS List of metrics to include in Database - --bounds BOUNDS - -o, --outdir TEXT [required] + -a, --attributes ATTRS List of attributes to include output + -m, --metrics METRICS List of metrics to include in output + --bounds BOUNDS Bounds for data to include in output + -o, --outdir PATH Output directory. [required] --help Show this message and exit. +Example +------- + +.. code-block:: + + silvimetric -d test.tdb extract -o test_tifs/ .. include:: ../substitutions.txt diff --git a/docs/source/cli/index.rst b/docs/source/cli/index.rst index b35a57f..57f6f4e 100755 --- a/docs/source/cli/index.rst +++ b/docs/source/cli/index.rst @@ -4,8 +4,44 @@ Command Line Interface ================================================================================ + +.. only:: html + + The command line interface (CLI) will facilitate your interaction with + Silvimetric from the terminal. + +.. code-block:: + + Usage: silvimetric [OPTIONS] COMMAND [ARGS]... + + Options: + -d, --database PATH Database path + --debug Changes logging level from INFO to DEBUG. + --log-dir TEXT Directory for log output + --progress BOOLEAN Report progress + --workers INTEGER Number of workers for Dask + --threads INTEGER Number of threads per worker for Dask + --watch Open dask diagnostic page in default web + browser. + --dasktype [threads|processes] What Dask uses for parallelization. For + moreinformation see here https://docs.dask.o + rg/en/stable/scheduling.html#local-threads + --scheduler [distributed|local|single-threaded] + Type of dask scheduler. Both are local, but + are run with different dask libraries. See + more here https://docs.dask.org/en/stable/sc + heduling.html. + --help Show this message and exit. + + Commands: + extract Extract silvimetric metrics from DATABASE + info Retrieve information on current state of DATABASE + initialize Create an empty DATABASE + scan Scan point cloud and determine the optimal tile size. + shatter Shatter point clouds into DATABASE cells. + .. toctree:: - :maxdepth: 2 + :maxdepth: 1 :caption: Commands extract diff --git a/docs/source/cli/info.rst b/docs/source/cli/info.rst index 4120556..d232041 100644 --- a/docs/source/cli/info.rst +++ b/docs/source/cli/info.rst @@ -6,7 +6,8 @@ info .. only:: html - Info provides detailed information about a SilviMetrics database + Info will query the database and output information about the `Shatter` + processes that been run as well as the database schema. .. Index:: info @@ -16,13 +17,26 @@ Synopsis .. code-block:: - Usage: silvimetric DATABASE info [OPTIONS] + Usage: silvimetric [OPTIONS] info [OPTIONS] Print info about Silvimetric database Options: - --history - --help Show this message and exit. + --bounds BOUNDS Bounds to filter by + --date [%Y-%m-%d|%Y-%m-%dT%H:%M:%SZ] + Select processes with this date + --history Show the history section of the output. + --metadata Show the metadata section of the output. + --attributes Show the attributes section of the output. + --dates ... Select processes within this date range + --name TEXT Select processes with this name + --help Show this message and exit. + +Example +------- +.. code-block:: + + silvimetric -d test.tdb info .. include:: ../substitutions.txt \ No newline at end of file diff --git a/docs/source/cli/initialize.rst b/docs/source/cli/initialize.rst index 97de933..4785829 100644 --- a/docs/source/cli/initialize.rst +++ b/docs/source/cli/initialize.rst @@ -20,15 +20,26 @@ Synopsis .. code-block:: - Usage: silvimetric DATABASE initialize [OPTIONS] BOUNDS CRS + Usage: silvimetric [OPTIONS] initialize [OPTIONS] Initialize silvimetrics DATABASE Options: + --bounds BOUNDS Root bounds that encapsulates all data [required] + --crs CRS Coordinate system of data [required] -a, --attributes ATTRS List of attributes to include in Database -m, --metrics METRICS List of metrics to include in Database --resolution FLOAT Summary pixel resolution --help Show this message and exit. +Example +------- + +.. code-block:: + + silvimetric --database test.tdb initialize --crs "EPSG:3857" \ + --bounds '[300, 300, 600, 600]' + + .. include:: ../substitutions.txt \ No newline at end of file diff --git a/docs/source/cli/scan.rst b/docs/source/cli/scan.rst index 3bf37ab..ae46644 100644 --- a/docs/source/cli/scan.rst +++ b/docs/source/cli/scan.rst @@ -6,7 +6,8 @@ scan .. only:: html - Scan provides detailed information about a SilviMetrics database + This will inspect a point cloud file with the database as a reference and + supply an estimate of what tile size you could use for `Shatter` operations. .. Index:: scan @@ -16,8 +17,24 @@ Synopsis .. code-block:: - Usage: silvimetric DATABASE scan [OPTIONS] + silvimetric [OPTIONS] scan [OPTIONS] POINTCLOUD + Options: + --resolution FLOAT Summary pixel resolution + --filter Remove empty space in computation. Will take extra + time. + --point_count INTEGER Point count threshold. + --depth INTEGER Quadtree depth threshold. + --bounds BOUNDS Bounds to scan. + --help Show this message and exit. + + +Usage +----- + +.. code-block:: + + silvimetric --database test.tdb scan tests/data/test_data.copc.laz .. include:: ../substitutions.txt diff --git a/docs/source/cli/shatter.rst b/docs/source/cli/shatter.rst index c50e9c9..fbd3662 100644 --- a/docs/source/cli/shatter.rst +++ b/docs/source/cli/shatter.rst @@ -15,19 +15,27 @@ Synopsis .. code-block:: - Usage: silvimetric DATABASE shatter [OPTIONS] POINTCLOUD + Usage: silvimetric [OPTIONS] shatter [OPTIONS] POINTCLOUD Insert data provided by POINTCLOUD into the silvimetric DATABASE Options: - --workers INTEGER - --bounds BOUNDS - --tilesize INTEGER - --threads INTEGER - --watch - --dasktype [cluster|threads|processes|single-threaded] + --bounds BOUNDS Bounds for data to include in processing + --tilesize INTEGER Number of cells to include per tile + --report Whether or not to write a report of the + process, useful for debugging + --date [%Y-%m-%d|%Y-%m-%dT%H:%M:%SZ] + Date the data was produced. + --dates ... Date range the data was produced during --help Show this message and exit. +Example +------- + +.. code-block:: + + silvimetric -d test.tdb shatter --date 2023-1-1 tests/data/test_data.copc.laz + .. include:: ../substitutions.txt \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 8aea005..2e72f22 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -10,6 +10,7 @@ import sys, os, re import time import datetime + if os.environ.get('SOURCE_DATE_EPOCH'): year = datetime.datetime.utcfromtimestamp(int(os.environ.get('SOURCE_DATE_EPOCH', time.gmtime()))).year today = datetime.datetime.utcfromtimestamp(int(os.environ.get('SOURCE_DATE_EPOCH', time.gmtime()))).strftime('%B %d, %Y') @@ -75,3 +76,20 @@ def read_version(filename): 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', ] + +autodoc_default_options = { + 'member-order': 'groupwise' +} + +autodoc_mock_imports = [ + "pyproj", + "osgeo", + "distributed", + "dask.dataframe", + "scipy", + "dill", + "shapely", + "pdal", + "gdal", + "pandas" +] \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index df3af13..cd59787 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -15,12 +15,12 @@ Find out more about SilviMetric by visiting :ref:`about`. A slide deck about SilviMetric is also available on `Google Slides `__. .. toctree:: - :maxdepth: 2 :caption: Contents + :maxdepth: 2 about cli/index - api + api/index QuickStart development tutorial diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index 1ad7260..9b87a19 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -67,7 +67,7 @@ need a bounds and a coordinate reference system. .. code-block:: shell-session pdal info https://s3.amazonaws.com/hobu-lidar/autzen-classified.copc.laz \ - --readers.copc.resolution=10 | jq -c '.stats.bbox.native.bbox' + --readers.copc.resolution=1 | jq -c '.stats.bbox.native.bbox' Our boundary is emitted in expanded form. @@ -124,26 +124,125 @@ need a bounds and a coordinate reference system. Be careful with your shell's quote escaping rules! +Scan +-------------------------------------------------------------------------------- + +The `scan` command will tell us information about the pointcloud with respect +to the database we already created, including a best guess at the correct number +of cells per tile, or `tile size`. + + .. code-block:: shell-session + + silvimetric -d ${db_name} scan ${pointcloud} + + +We should see output like the output below, recommending we use a `tile size` +of 185. + +:: + + silvimetric - INFO - info:156 - Pointcloud information: + silvimetric - INFO - info:156 - Storage Bounds: [635579.2, 848884.83, 639003.73, 853536.21] + silvimetric - INFO - info:156 - Pointcloud Bounds: [635577.79, 848882.15, 639003.73, 853537.66] + silvimetric - INFO - info:156 - Point Count: 10653336 + silvimetric - INFO - info:156 - Tiling information: + silvimetric - INFO - info:156 - Mean tile size: 91.51758793969849 + silvimetric - INFO - info:156 - Std deviation: 94.31396536316173 + silvimetric - INFO - info:156 - Recommended split size: 185 + Shatter -------------------------------------------------------------------------------- -We can now insert data into the SMDB +We can now insert data into the SMDB. + +If we run this command without the argument `--tilesize`, `Silvimetric` will +determine a tile size for you. The method will be the same as the `Scan` method, +but will filter out the tiles that have no data in them. .. code-block:: shell-session - silvimetric autzen-smdb.tdb \ + silvimetric -d autzen-smdb.tdb \ + --threads 4 \ + --workers 4 \ + --watch \ shatter \ - https://s3.amazonaws.com/hobu-lidar/autzen-classified.copc.laz \ - --threads 10 \ - --watch + --date 2008-12-01 \ + https://s3.amazonaws.com/hobu-lidar/autzen-classified.copc.laz + +If we grab the tile size from the `scan` that we ran earlier, we'll skip the +filtering step. + + .. code-block:: shell-session + + silvimetric -d autzen-smdb.tdb \ + --threads 4 \ + --workers 4 \ + --watch \ + shatter \ + --tilesize 185 \ + --date 2008-12-01 \ + https://s3.amazonaws.com/hobu-lidar/autzen-classified.copc.laz Extract -------------------------------------------------------------------------------- -After data is inserted, we can extract +After data is inserted, we can extract it into different rasters. When we created +the database we gave it a list of `Attributes` and `Metrics`. When we ran +`Shatter`, we filled in the values for those in each cell. If we have a database +with the `Attributes` Intensity and Z, in combination with the `Metrics` min and +max, each cell will contain values for `min_Intensity`, `max_Intensity`, +`min_Z`, and `max_Z`. This is also the list of available rasters we can extract. + + .. code-block:: shell-session + + silvimetric -d autzen-smdb.tdb extract -o output-directory + +Info +-------------------------------------------------------------------------------- + +We can query past shatter processes and the schema for the database with the +Info call. + + .. code-block:: shell-session + + silvimetric -d autzen-smdb.tdb info --history + +This will print out a JSON object containing information about the current state +of the database. We can find the `name` key here, which necessary for +`Delete`, `Restart`, and `Resume`. For the following commands we will have +copied the value of the `name` key in the variable `uuid`. + +Delete +-------------------------------------------------------------------------------- + +We can also remove a `shatter` process by using the `delete` command. This will +remove all data associated with that shatter process from the database, but +will leave an updated config of it in the database config should you want to +reference it later. + + .. code-block:: shell-session + + silvimetric -d autzen-smdb.tdb delete --id $uuid + +Restart +-------------------------------------------------------------------------------- + +If you would like to rerun a `Shatter` process, whether or not it was previously +finished, you can use the `restart` command. This will call the `delete` method +and use the config from that to re-run the `shatter` process. + + .. code-block:: shell-session + + silvimetric -d autzen-smdb.tdb restart --id $uuid + +Resume +-------------------------------------------------------------------------------- + +If a `Shatter` process is cancelled partway through, we can pick up where we +left off with the `Resume` command. .. code-block:: shell-session - silvimetric autzen-smdb.tdb extract -o output-directory + silvimetric -d autzen-smdb.tdb resume --id $uuid .. include:: ./substitutions.txt \ No newline at end of file diff --git a/docs/source/substitutions.txt b/docs/source/substitutions.txt index 52763fc..09e60ea 100644 --- a/docs/source/substitutions.txt +++ b/docs/source/substitutions.txt @@ -13,3 +13,4 @@ .. |CondaForge| replace:: `Conda Forge `__ .. |S3| replace:: `S3 `__ .. |FUSION| replace:: `FUSION `__ +.. |Click| replace:: `Click `__ diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index 3769847..435a500 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -189,7 +189,7 @@ Example: $ METRIC_PATH="./path/to/python_metrics.py" $ silvimetric --database $DB_NAME initialize --bounds "$BOUNDS" \ --crs "EPSG:$EPSG" \ - -m $METRIC_PATH -m min -m max -m mean + -m "${METRIC_PATH},min,max,mean" .. warning:: diff --git a/environment.yml b/environment.yml index 6674d0a..62257ac 100644 --- a/environment.yml +++ b/environment.yml @@ -3,7 +3,9 @@ channels: - conda-forge dependencies: - python + - pip - tiledb-py + - pdal - python-pdal - numpy - shapely @@ -15,5 +17,5 @@ dependencies: - websocket-client - python-json-logger - dill - - pip: - - line-profiler + - pandas + - lmoments3 \ No newline at end of file diff --git a/src/silvimetric/__init__.py b/src/silvimetric/__init__.py index 1f356cc..23c8d91 100644 --- a/src/silvimetric/__init__.py +++ b/src/silvimetric/__init__.py @@ -1 +1,20 @@ -__version__ = '1.0.0' +__version__ = '1.2.1' + +from .resources.bounds import Bounds +from .resources.extents import Extents +from .resources.storage import Storage +from .resources.metric import Metric, run_metrics +from .resources.metrics import grid_metrics, l_moments, percentiles, statistics, all_metrics +from .resources.metrics import product_moments +from .resources.log import Log +from .resources.data import Data +from .resources.attribute import Attribute, Pdal_Attributes, Attributes +from .resources.config import StorageConfig, ShatterConfig, ExtractConfig +from .resources.config import ApplicationConfig + +from .commands.shatter import shatter +from .commands.extract import extract +from .commands.info import info +from .commands.scan import scan +from .commands.initialize import initialize +from .commands.manage import delete, resume, restart diff --git a/src/silvimetric/cli/cli.py b/src/silvimetric/cli/cli.py index 7cd246c..19d1c8b 100644 --- a/src/silvimetric/cli/cli.py +++ b/src/silvimetric/cli/cli.py @@ -4,35 +4,40 @@ import logging -from ..resources import Bounds, Log -from ..resources import Attribute, Metric -from ..resources import StorageConfig, ShatterConfig, ExtractConfig, ApplicationConfig -from ..commands import shatter, extract, scan, info, initialize +from .. import __version__ +from .. import Attribute, Metric, Bounds, Log +from .. import StorageConfig, ShatterConfig, ExtractConfig, ApplicationConfig +from ..commands import shatter, extract, scan, info, initialize, manage from .common import BoundsParamType, CRSParamType, AttrParamType, MetricParamType -from .common import dask_handle +from .common import dask_handle, close_dask @click.group() @click.option("--database", '-d', type=click.Path(exists=False), help="Database path") -@click.option("--debug", is_flag=True, default=False, help="Print debug messages?") -@click.option("--log-level", default="INFO", help="Log level (INFO/DEBUG)") +@click.option("--debug", is_flag=True, default=False, help="Changes logging level from INFO to DEBUG.") @click.option("--log-dir", default=None, help="Directory for log output", type=str) -@click.option("--progress", default=True, type=bool, help="Report progress") -@click.option("--workers", type=int, default=12, help="Number of workers for Dask") -@click.option("--threads", type=int, default=4, help="Number of threads per worker for Dask") +@click.option("--progress", is_flag=True, default=True, type=bool, help="Report progress") +@click.option("--workers", type=int, help="Number of workers for Dask") +@click.option("--threads", type=int, help="Number of threads per worker for Dask") @click.option("--watch", is_flag=True, default=False, type=bool, help="Open dask diagnostic page in default web browser.") @click.option("--dasktype", default='processes', type=click.Choice(['threads', 'processes']), help="What Dask uses for parallelization. For more" "information see here https://docs.dask.org/en/stable/scheduling.html#local-threads") -@click.option("--scheduler", default='distributed', type=click.Choice(['distributed', +@click.option("--scheduler", default='local', type=click.Choice(['distributed', 'local', 'single-threaded']), help="Type of dask scheduler. Both are " "local, but are run with different dask libraries. See more here " "https://docs.dask.org/en/stable/scheduling.html.") +@click.version_option(__version__) @click.pass_context -def cli(ctx, database, debug, log_level, log_dir, progress, dasktype, scheduler, +def cli(ctx, database, debug, log_dir, progress, dasktype, scheduler, workers, threads, watch): # Set up logging + if debug: + log_level = 'DEBUG' + else: + log_level = 'INFO' + numeric_level = getattr(logging, log_level.upper(), None) if not isinstance(numeric_level, int): raise ValueError(f"Invalid log level: {log_level}") @@ -42,9 +47,13 @@ def cli(ctx, database, debug, log_level, log_dir, progress, dasktype, scheduler, log=log, debug=debug, progress=progress, - scheduler=scheduler) - dask_handle(dasktype, scheduler, workers, threads, watch, app.log) + scheduler=scheduler, + dasktype=dasktype, + workers=workers, + threads=threads, + watch=watch) ctx.obj = app + ctx.call_on_close(close_dask) @cli.command("info") @@ -52,6 +61,12 @@ def cli(ctx, database, debug, log_level, log_dir, progress, dasktype, scheduler, help="Bounds to filter by") @click.option("--date", type=click.DateTime(['%Y-%m-%d','%Y-%m-%dT%H:%M:%SZ']), help="Select processes with this date") +@click.option("--history", type=bool, is_flag=True, default=False, + help="Show the history section of the output.") +@click.option("--metadata", type=bool, is_flag=True, default=False, + help="Show the metadata section of the output.") +@click.option("--attributes", type=bool, is_flag=True, default=False, + help="Show the attributes section of the output.") @click.option("--dates", type=click.Tuple([ click.DateTime(['%Y-%m-%d','%Y-%m-%dT%H:%M:%SZ']), click.DateTime(['%Y-%m-%d','%Y-%m-%dT%H:%M:%SZ'])]), nargs=2, @@ -59,17 +74,34 @@ def cli(ctx, database, debug, log_level, log_dir, progress, dasktype, scheduler, @click.option("--name", type=str, default=None, help="Select processes with this name") @click.pass_obj -def info_cmd(app, bounds, date, dates, name): +def info_cmd(app, bounds, date, dates, name, history, metadata, attributes): import json if date is not None and dates is not None: - app.log.warning("Both 'date' and 'dates' specified. Prioritizing 'dates'") + app.log.warning("Both 'date' and 'dates' specified. Prioritizing" + "'dates'") start_date = dates[0] if dates else date end_date = dates[1] if dates else date i = info.info(app.tdb_dir, bounds=bounds, start_time=start_date, end_time=end_date, name=name) - app.log.info(json.dumps(i, indent=2)) + + if any([history, metadata, attributes]): + filtered = { } + if history: + filtered['history'] = i['history'] + if metadata: + filtered['metadata'] = i['metadata'] + if attributes: + filtered['attributes'] = i['attributes'] + + app.log.info(json.dumps(filtered, indent=2)) + + else: + app.log.info(json.dumps(i, indent=2)) + return + + @cli.command("scan") @@ -86,9 +118,12 @@ def info_cmd(app, bounds, date, dates, name): help="Bounds to scan.") @click.pass_obj def scan_cmd(app, resolution, point_count, pointcloud, bounds, depth, filter): - """Scan point cloud and determine the optimal tile size.""" + """Scan point cloud, output information on it, and determine the optimal + tile size.""" + dask_handle(app.dasktype, app.scheduler, app.workers, app.threads, + app.watch, app.log) return scan.scan(app.tdb_dir, pointcloud, bounds, point_count, resolution, - depth, filter) + depth, filter, log=app.log) @cli.command('initialize') @@ -98,22 +133,21 @@ def scan_cmd(app, resolution, point_count, pointcloud, bounds, depth, filter): help="Coordinate system of data") @click.option("--attributes", "-a", multiple=True, type=AttrParamType(), help="List of attributes to include in Database") -@click.option("--metrics", "-m", multiple=True, type=MetricParamType(), - help="List of metrics to include in Database") +@click.option("--metrics", "-m", type=MetricParamType(), default=[], + help="List of metrics to include in output, eg. '-m stats,percentiles'") @click.option("--resolution", type=float, default=30.0, help="Summary pixel resolution") @click.pass_obj def initialize_cmd(app: ApplicationConfig, bounds: Bounds, crs: pyproj.CRS, attributes: list[Attribute], resolution: float, metrics: list[Metric]): - import itertools - """Initialize silvimetrics DATABASE - """ + """Initialize silvimetrics DATABASE""" + storageconfig = StorageConfig(tdb_dir = app.tdb_dir, log = app.log, root = bounds, crs = crs, attrs = attributes, - metrics = list(itertools.chain(*metrics)), + metrics = metrics, resolution = resolution) return initialize.initialize(storageconfig) @@ -134,6 +168,10 @@ def initialize_cmd(app: ApplicationConfig, bounds: Bounds, crs: pyproj.CRS, help="Date range the data was produced during") @click.pass_obj def shatter_cmd(app, pointcloud, bounds, report, tilesize, date, dates): + """Insert data provided by POINTCLOUD into the silvimetric DATABASE""" + + dask_handle(app.dasktype, app.scheduler, app.workers, app.threads, + app.watch, app.log) if date is not None and dates is not None: app.log.warning("Both 'date' and 'dates' specified. Prioritizing 'dates'") @@ -141,31 +179,33 @@ def shatter_cmd(app, pointcloud, bounds, report, tilesize, date, dates): if date is None and dates is None: raise ValueError("One of '--date' or '--dates' must be provided.") - """Insert data provided by POINTCLOUD into the silvimetric DATABASE""" - config = ShatterConfig(tdb_dir = app.tdb_dir, + config = ShatterConfig(tdb_dir=app.tdb_dir, date=dates if dates else tuple([date]), - log = app.log, - filename = pointcloud, - bounds = bounds, + log=app.log, + filename=pointcloud, + bounds=bounds, tile_size=tilesize) if report: if app.scheduler != 'distributed': + from dask.diagnostics import ProgressBar app.log.warning('Report option is incompatible with scheduler' '{scheduler}, skipping.') - report_path = f'reports/{config.name}.html' - with performance_report(report_path) as pr: shatter.shatter(config) - print(f'Writing report to {report_path}.') + else: + report_path = f'reports/{config.name}.html' + with performance_report(report_path) as pr: + shatter.shatter(config) + print(f'Writing report to {report_path}.') else: shatter.shatter(config) @cli.command('extract') -@click.option("--attributes", "-a", multiple=True, type=AttrParamType(), +@click.option("--attributes", "-a", multiple=True, type=AttrParamType(), default=[], help="List of attributes to include output") -@click.option("--metrics", "-m", multiple=True, type=MetricParamType(), - help="List of metrics to include in output") +@click.option("--metrics", "-m", type=MetricParamType(), default=[], + help="List of metrics to include in output, eg. '-m stats,percentiles'") @click.option("--bounds", type=BoundsParamType(), default=None, help="Bounds for data to include in output") @click.option("--outdir", "-o", type=click.Path(exists=False), required=True, @@ -176,6 +216,8 @@ def extract_cmd(app, attributes, metrics, outdir, bounds): #TODO only allow metrics and attributes to be added if they're present # in the storage config. + dask_handle(app.dasktype, app.scheduler, app.workers, app.threads, + app.watch, app.log) config = ExtractConfig(tdb_dir = app.tdb_dir, log = app.log, @@ -185,6 +227,26 @@ def extract_cmd(app, attributes, metrics, outdir, bounds): bounds = bounds) extract.extract(config) +@cli.command('delete') +@click.option('--id', type=click.UUID, required=True, + help="Shatter Task UUID.") +@click.pass_obj +def delete_cmd(app, id): + manage.delete(app.tdb_dir, id) + +@cli.command('restart') +@click.option('--id', type=click.UUID, required=True, + help="Shatter Task UUID.") +@click.pass_obj +def restart_cmd(app, id): + manage.restart(tdb_dir=app.tdb_dir, name=id) + +@cli.command('resume') +@click.option('--id', type=click.UUID, required=True, + help="Shatter Task UUID.") +@click.pass_obj +def resume_cmd(app, id): + manage.resume(tdb_dir=app.tdb_dir, name=id) if __name__ == "__main__": - cli() + cli() \ No newline at end of file diff --git a/src/silvimetric/cli/common.py b/src/silvimetric/cli/common.py index a247d0c..a21cf62 100644 --- a/src/silvimetric/cli/common.py +++ b/src/silvimetric/cli/common.py @@ -5,8 +5,10 @@ import dask from dask.diagnostics import ProgressBar from dask.distributed import Client, LocalCluster +from ..resources.metrics import l_moments, percentiles, statistics, product_moments +from ..resources.metrics import aad, grid_metrics, all_metrics -from ..resources import Bounds, Attribute, Metric, Attributes, Metrics, Log +from .. import Bounds, Attribute, Metric, Attributes, Log class BoundsParamType(click.ParamType): @@ -22,7 +24,7 @@ def convert(self, value, param, ctx): class CRSParamType(click.ParamType): name = "CRS" - def convert(self, value, param, ctx): + def convert(self, value, param, ctx) -> pyproj.CRS: try: crs = pyproj.CRS.from_user_input(value) return crs @@ -33,46 +35,79 @@ class AttrParamType(click.ParamType): name="Attrs" #TODO add import similar to metrics def convert(self, value, param, ctx) -> list[Attribute]: - try: - return [Attributes[a] for a in value] - except Exception as e: - self.fail(f"{value!r} is not available in Attributes, {e}", param, ctx) - -class MetricParamType(click.ParamType): - name="Metrics" - def convert(self, value, param, ctx) -> list[Metric]: - if '.py' in value: + if isinstance(value, list): try: - import importlib.util - import os - from pathlib import Path - - cwd = os.getcwd() - p = Path(cwd, value) - if not p.exists(): - self.fail("Failed to find import file for metrics at" - f" {str(p)}", param, ctx) - - spec = importlib.util.spec_from_file_location('user_metrics', str(p)) - user_metrics = importlib.util.module_from_spec(spec) - spec.loader.exec_module(user_metrics) - ms = user_metrics.metrics() + return [Attributes[a] for a in value] except Exception as e: - self.fail(f"Failed to import metrics from {str(p)} with error {e}", - param, ctx) - - for m in ms: - if not isinstance(m, Metric): - self.fail(f"Invalid Metric supplied: {m}") - return user_metrics.metrics() + self.fail(f"{value!r} is not available in Attributes, {e}", param, ctx) + elif isinstance(value, str): + return Attributes[value] + else: + self.fail(f"{value!r} is of an invalid type, {e}", param, ctx) - try: - return [ Metrics[value] ] - except Exception as e: - self.fail(f"{value!r} is not available in Metrics, {e}", param, ctx) +class MetricParamType(click.ParamType): + name="metrics" + def convert(self, value, param, ctx) -> list[Metric]: + if value is None or not value: + return list(all_metrics.values()) + parsed_values = value.split(',') + metrics: set[Metric] = set() + for val in parsed_values: + if '.py' in val: + # user imported metrics from external file + try: + import importlib.util + import os + from pathlib import Path + + cwd = os.getcwd() + p = Path(cwd, val) + if not p.exists(): + self.fail("Failed to find import file for metrics at" + f" {str(p)}", param, ctx) + + spec = importlib.util.spec_from_file_location('user_metrics', str(p)) + user_metrics = importlib.util.module_from_spec(spec) + spec.loader.exec_module(user_metrics) + ms = user_metrics.metrics() + except Exception as e: + self.fail(f"Failed to import metrics from {str(p)} with error {e}", + param, ctx) + + for m in ms: + if not isinstance(m, Metric): + self.fail(f"Invalid Metric supplied: {m}") + + metrics.update(list(user_metrics.metrics())) + else: + # SilviMetric defined metrics + try: + if val == 'stats': + metrics.update(list(statistics.values())) + elif val == 'p_moments': + metrics.update(list(product_moments.values())) + elif val == 'l_moments': + metrics.update(list(l_moments.values())) + elif val == 'percentiles': + metrics.update(list(percentiles.values())) + elif val == 'aad': + metrics.update(list(aad.aad.values())) + elif val == 'grid_metrics': + metrics.update(list(grid_metrics.values())) + elif val == 'all': + metrics.update(list(all_metrics.values())) + else: + m = all_metrics[val] + if isinstance(m, Metric): + metrics.add(m) + else: + metrics.udpate(list(m)) + except Exception as e: + self.fail(f"{val!r} is not available in Metrics", param, ctx) + return list(metrics) def dask_handle(dasktype: str, scheduler: str, workers: int, threads: int, - watch: bool, log: Log): + watch: bool, log: Log) -> None: dask_config = { } if dasktype == 'threads': @@ -83,9 +118,6 @@ def dask_handle(dasktype: str, scheduler: str, workers: int, threads: int, dask_config['threads_per_worker'] = threads if scheduler == 'local': - if scheduler != 'distributed': - log.warning("Selected scheduler type does not support continuously" - "updated config information.") # fall back to dask type to determine the scheduler type dask_config['scheduler'] = dasktype if watch: @@ -108,9 +140,11 @@ def dask_handle(dasktype: str, scheduler: str, workers: int, threads: int, webbrowser.open(client.cluster.dashboard_link) elif scheduler == 'single-threaded': - if scheduler != 'distributed': - log.warning("""Selected scheduler type does not support continuously\ - updated config information.""") dask_config['scheduler'] = scheduler dask.config.set(dask_config) + +def close_dask() -> None: + client = dask.config.get('distributed.client') + if isinstance(client, Client): + client.close() \ No newline at end of file diff --git a/src/silvimetric/commands/extract.py b/src/silvimetric/commands/extract.py index ceb047c..04dafeb 100644 --- a/src/silvimetric/commands/extract.py +++ b/src/silvimetric/commands/extract.py @@ -1,13 +1,15 @@ +from pathlib import Path +from itertools import chain + + +from typing import Union from osgeo import gdal, osr +import dask import numpy as np -from pathlib import Path import pandas as pd -import dask -import dask.dataframe as dd -import dask.bag as db -from ..resources import Storage, ExtractConfig, Metric, Attribute -from ..resources import Extents + +from .. import Storage, Extents, ExtractConfig np_to_gdal_types = { np.dtype(np.byte).str: gdal.GDT_Byte, @@ -23,17 +25,25 @@ } def write_tif(xsize: int, ysize: int, data:np.ndarray, name: str, - config: ExtractConfig): + config: ExtractConfig) -> None: + """ + Write out a raster with GDAL + + :param xsize: Length of X plane. + :param ysize: Length of Y plane. + :param data: Data to write to raster. + :param name: Name of raster to write. + :param config: ExtractConfig. + """ osr.UseExceptions() path = Path(config.out_dir) / f'{name}.tif' crs = config.crs srs = osr.SpatialReference() srs.ImportFromWkt(crs.to_wkt()) - # transform = [x, res, 0, y, 0, res] b = config.bounds transform = [b.minx, config.resolution, 0, - b.maxy, 0, -1* config.resolution] + b.maxy, 0, -1*config.resolution] driver = gdal.GetDriverByName("GTiff") gdal_type = np_to_gdal_types[np.dtype(data.dtype).str] @@ -45,31 +55,46 @@ def write_tif(xsize: int, ysize: int, data:np.ndarray, name: str, tif.FlushCache() tif = None -def get_metrics(data_in, config: ExtractConfig): +def get_metrics(data_in: pd.DataFrame, storage: Storage) -> Union[None, pd.DataFrame]: + """ + Reruns a metric over this cell. Only called if there is overlapping data. + + :param data_in: Dataframe to be rerun. + :param storage: Base storage object. + :return: Combined dict of attribute and newly derived metric data. + """ + + #TODO should just use the metric calculation methods from shatter if data_in is None: return None - # make sure it's not empty. No empty writes - if not np.any(data_in['count']): - return None + def expl(x): + return x.explode() + + attrs = [a.name for a in storage.config.attrs if a.name not in ['X','Y']] + + # set index so we can apply to the whole dataset without needing to skip X and Y + # then reset in the index because that's what metric.do expects + exploded = data_in.set_index(['X','Y']).apply(expl)[attrs].reset_index() + metric_data = dask.persist(*[ m.do(exploded) for m in storage.config.metrics ]) - # doing dask compute inside the dict array because it was too fine-grained - # when it was outside - #TODO move this operation to a dask bag and convert to dataframe and then - # to a dict for better efficiency? - metric_data = { - f'{m.entry_name(attr.name)}': [m(cell_data) for cell_data in data_in[attr.name]] - for attr in config.attrs for m in config.metrics - } - # md = dask.compute(metric_data)[0] - data_out = data_in | metric_data + data_out = data_in.set_index(['X','Y']).join([m for m in metric_data]) return data_out -def handle_overlaps(config: ExtractConfig, storage: Storage, indices: np.ndarray): - ma_list = [ m.entry_name(a.name) for m in config.metrics for a in - config.attrs ] - att_list = [ a.name for a in config.attrs ] + [ 'count' ] - out_list = [ *ma_list, 'X', 'Y' ] +def handle_overlaps(config: ExtractConfig, storage: Storage, indices: np.ndarray) -> pd.DataFrame: + """ + Handle cells that have overlapping data. We have to re-run metrics over these + cells as there's no other accurate way to determined metric values. If there + are no overlaps, this will do nothing. + + :param config: ExtractConfig. + :param storage: Database storage object. + :param indices: Indices with overlap. + :return: Dataframe of rerun data. + """ + + ma_list = storage.getDerivedNames() + att_list = [ a.name for a in config.attrs ] minx = indices['x'].min() maxx = indices['x'].max() @@ -84,81 +109,63 @@ def handle_overlaps(config: ExtractConfig, storage: Storage, indices: np.ndarray att_meta[a.name] = a.dtype with storage.open("r") as tdb: - data = tdb.query(attrs=ma_list, order='F', coords=True).df[minx:maxx, - miny:maxy] - data['X'] = data['X'] - data['X'].min() - data['Y'] = data['Y'] - data['Y'].min() - - # 1. should find values that are not unique, meaning they have multiple - # entries - recarray = data.to_records() - xys = recarray[['X', 'Y']] - unq, idx, counts = np.unique(xys, return_index=True, return_counts=True) - redos = np.where(counts >= 2) - - if not np.any(redos): - return data - - leaves = data.loc[idx[np.where(counts == 1)]] - - # 2. then should combine the values of those attribute/cell combos - ridx = np.unique(unq[redos]) - rxs = list(ridx['X']) - rys = list(ridx['Y']) - if np.any(rxs): - redo = pd.DataFrame(tdb.query(attrs=att_list).multi_index[[rxs], [rys]]) - redo['X'] = redo['X'] - minx - redo['Y'] = redo['Y'] - miny - - parts = min(int(redo['X'].max() * redo['Y'].max()), 1000) - r = dd.from_pandas(redo, npartitions=parts, name='MetricDataFrame') - def squash(x): - def s2(vals): - if vals.dtype == object: - return np.array([*vals], vals.dtype).reshape(-1) - elif vals.name == 'X' or vals.name == 'Y': - return vals.values[0] - else: - return sum(vals) - val = x.aggregate(s2) - p = pd.DataFrame(val).T - return p - - recs = r.groupby(['X','Y']).apply(squash, meta=att_meta) - rs = recs.compute().to_dict(orient='list') - - # 3. Should rerun the metrics over them - metrics = get_metrics(rs, config) - ms = pd.DataFrame.from_dict(metrics)[out_list] - return pd.concat((ms, leaves[out_list])) - -def extract(config: ExtractConfig): + # TODO this can be more efficient. Use count to find indices, then work + # with that smaller set from there. Working as is for now, but slow. + dit = tdb.query(attrs=[*att_list, *ma_list], order='F', coords=True, + return_incomplete=True, use_arrow=False).df[minx:maxx, miny:maxy] + data = pd.DataFrame() + + storage.config.log.info('Collecting database information...') + for d in dit: + if data.empty: + data = d + else: + data = pd.concat([data, d]) + + + # should find values that are not unique, meaning they have multiple entries + data = data.set_index(['X','Y']) + redo_indices = data.index[data.index.duplicated(keep='first')] + if redo_indices.empty: + return data.reset_index() + + # data with overlaps + redo_data = data.loc[redo_indices][att_list].groupby(['X','Y']).agg(lambda x: list(chain(*x))) + # data that has no overlaps + clean_data = data.loc[data.index[~data.index.duplicated(False)]] + + storage.config.log.warning('Overlapping data detected. Rerunning metrics over these cells...') + new_metrics = get_metrics(redo_data.reset_index(), storage) + return pd.concat([clean_data, new_metrics]).reset_index() + +def extract(config: ExtractConfig) -> None: + """ + Pull data from database for each desired metric and output them to rasters + + :param config: ExtractConfig. + """ dask.config.set({"dataframe.convert-string": False}) - ma_list = [ m.entry_name(a.name) for m in config.metrics for a in - config.attrs ] - - storage = Storage.from_db(config.tdb_dir) + ma_list = storage.getDerivedNames() + config.log.debug(f'Extracting metrics {[m for m in ma_list]}') root_bounds=storage.config.root e = Extents(config.bounds, config.resolution, root=root_bounds) i = e.get_indices() - minx = i['x'].min() - maxx = i['x'].max() - miny = i['y'].min() - maxy = i['y'].max() - x1 = maxx - minx + 1 - y1 = maxy - miny + 1 + xsize = e.x2 + ysize = e.y2 + # figure out if there are any overlaps and handle them final = handle_overlaps(config, storage, i).sort_values(['Y', 'X']) # output metric data to tifs for ma in ma_list: - m_data = np.full(shape=(y1,x1), fill_value=np.nan, dtype=final[ma].dtype) + # TODO should output in sections so we don't run into memory problems + m_data = np.full(shape=(ysize,xsize), fill_value=np.nan, dtype=final[ma].dtype) a = final[['X','Y',ma]].to_numpy() for x,y,md in a[:]: m_data[int(y)][int(x)] = md - write_tif(x1, y1, m_data, ma, config) \ No newline at end of file + write_tif(xsize, ysize, m_data, ma, config) \ No newline at end of file diff --git a/src/silvimetric/commands/info.py b/src/silvimetric/commands/info.py index 721b871..48dcf61 100644 --- a/src/silvimetric/commands/info.py +++ b/src/silvimetric/commands/info.py @@ -1,9 +1,25 @@ from datetime import datetime from uuid import UUID +from typing import Union -from ..resources import Storage, Bounds +from .. import Storage, Bounds -def check_values(start_time, end_time, bounds, name): +def check_values(start_time: datetime, end_time: datetime, bounds: Bounds, + name: Union[UUID, str]): + """ + Validate arguments for info command. + + :param start_time: Starting datetime object. + :param end_time: Ending datetime object. + :param bounds: Bounds to query by. + :param name: Name to query by. + :raises TypeError: Incorrect type of start_time argument. + :raises TypeError: Incorrect type of end_time argument. + :raises TypeError: Incorrect type of bounds argument. + :raises TypeError: Incorrect type of name argument. + :raises TypeError: Incorrect type of name argument. + :meta public: + """ if start_time is not None and not isinstance(start_time, datetime): raise TypeError(f'Incorrect type of "start_time" argument.') if end_time is not None and not isinstance(end_time, datetime): @@ -11,14 +27,28 @@ def check_values(start_time, end_time, bounds, name): if bounds is not None and not isinstance(bounds, Bounds): raise TypeError(f'Incorrect type of "bounds" argument.') if name is not None: - try: - UUID(name) - except: + if isinstance(name, UUID): + pass + elif isinstance(name, str): + try: + UUID(name) + except: + raise TypeError(f'Incorrect type of "name" argument.') + else: raise TypeError(f'Incorrect type of "name" argument.') -def info(tdb_dir, start_time:datetime=None, end_time:datetime=None, - bounds:Bounds=None, name:str=None): - """Print info about Silvimetric database""" +def info(tdb_dir:str, start_time:datetime=None, end_time:datetime=None, + bounds:Bounds=None, name:Union[str, UUID]=None) -> dict: + """ + Collect information about database in current state + + :param tdb_dir: TileDB database directory path. + :param start_time: Process starting time query, defaults to None + :param end_time: Process ending time query, defaults to None + :param bounds: Bounds query, defaults to None + :param name: Name query, defaults to None + :return: Returns json object containing information on database. + """ check_values(start_time, end_time, bounds, name) with Storage.from_db(tdb_dir) as tdb: @@ -29,7 +59,8 @@ def info(tdb_dir, start_time:datetime=None, end_time:datetime=None, if bounds is None: bounds = tdb.config.root if name is not None: - name = UUID(name) + if isinstance(name, str): + name = UUID(name) # We always have these meta = tdb.getConfig() @@ -54,4 +85,4 @@ def info(tdb_dir, start_time:datetime=None, end_time:datetime=None, except KeyError: history = {} - return info \ No newline at end of file + return info \ No newline at end of file diff --git a/src/silvimetric/commands/initialize.py b/src/silvimetric/commands/initialize.py index cda309a..fccdd97 100644 --- a/src/silvimetric/commands/initialize.py +++ b/src/silvimetric/commands/initialize.py @@ -1,16 +1,13 @@ -from silvimetric.resources import Storage, StorageConfig +from .. import Storage, StorageConfig def initialize(storage: StorageConfig): """ - Initialize a Silvimetric TileDB instance for a given StorageConfig instance - - Parameters - ---------- - StorageConfig : StorageConfig + Initialize a Silvimetric TileDB instance for a given StorageConfig instance. + :param storage: :class:`silvimetric.resources.config.StorageConfig`. + :return: :class:`silvimetric.resources.storage.Storage` database object. """ - storage.log.debug(f"Initializing SilviMetric Database at '{storage.tdb_dir}'") - s = Storage.create(storage) + storage.log.info(f"Initialized SilviMetric Database at '{storage.tdb_dir}'") return s diff --git a/src/silvimetric/commands/manage.py b/src/silvimetric/commands/manage.py new file mode 100644 index 0000000..cace6d4 --- /dev/null +++ b/src/silvimetric/commands/manage.py @@ -0,0 +1,54 @@ +from .. import Storage, ShatterConfig +from .info import info +from .shatter import shatter + +def delete(tdb_dir: str, name:str) -> ShatterConfig: + """ + Delete Shatter process from database and return config for that process. + + :param tdb_dir: TileDB database directory path. + :param name: UUID name of the Shatter process. + :raises KeyError: Shatter process with ID does not exist. + :raises ValueError: Shatter process with ID is missing a time reservation + :return: Config of process that was deleted. + """ + res = info(tdb_dir=tdb_dir, name=name) + + try: + config = ShatterConfig.from_dict(res['history'][0]) + except LookupError: + raise KeyError(f'Shatter process with ID {name} does not exist') + + try: + time_slot = config.time_slot + except KeyError: + raise ValueError(f'Shatter process with ID {name} is missing a time reservation.') + + storage = Storage.from_db(tdb_dir) + return storage.delete(time_slot) + +def restart(tdb_dir: str, name: str) -> int: + """ + Delete shatter process from database and run it again with the same config. + + :param tdb_dir: TileDB database directory path. + :param name: UUID name of Shatter process. + :return: Point count of the restarted shatter process. + """ + + cfg = delete(tdb_dir, name) + return shatter(cfg) + +def resume(tdb_dir: str, name: str) -> int: + """ + Resume partially completed shatter process. Process must partially completed + and have an already established time slot. + + :param tdb_dir: TileDB database directory path. + :param name: UUID name of Shatter process. + :return: Point count of the restarted shatter process. + """ + res = info(tdb_dir=tdb_dir, name=name) + assert len(res['history']) == 1 + config = res['history'][0] + return shatter(config) \ No newline at end of file diff --git a/src/silvimetric/commands/scan.py b/src/silvimetric/commands/scan.py index 66ef3de..a17987f 100644 --- a/src/silvimetric/commands/scan.py +++ b/src/silvimetric/commands/scan.py @@ -3,17 +3,41 @@ import dask.bag as db import dask import math +import json + +from .. import Storage, Data, Extents, Bounds, Log + +def scan(tdb_dir: str, pointcloud: str, bounds: Bounds, point_count:int=600000, resolution:float=100, + depth:int=6, filter:bool=False, log: Log=None): + """ + Scan pointcloud and determine appropriate tile sizes. + + :param tdb_dir: TileDB database directory. + :param pointcloud: Path to point cloud. + :param bounds: Bounding box to filter by. + :param point_count: Point count threshold., defaults to 600000 + :param resolution: Resolution threshold., defaults to 100 + :param depth: Tree depth threshold., defaults to 6 + :param filter: Remove empty Extents. This takes longer, but is more accurage., defaults to False + :return: Returns list of point counts. + """ + + # TODO Scan should output other information about a file like bounds, pc, avg points per cell + with Storage.from_db(tdb_dir) as tdb: -from ..resources import Storage, Data, Extents, Bounds -def scan(tdb_dir, pointcloud, bounds, point_count=600000, resolution=100, - depth=6, filter=False): + if log is None: + logger = logging.getLogger('silvimetric') + else: + logger = log + data = Data(pointcloud, tdb.config, bounds) - logger = logging.getLogger('silvimetric') - with Storage.from_db(tdb_dir) as tdb: + thresholds = dict(thresholds=dict(resolution=resolution, point_count=point_count, depth=depth)) + logger.debug(json.dumps(thresholds, indent=2)) - data = Data(pointcloud, tdb.config, bounds) extents = Extents.from_sub(tdb_dir, data.bounds) + count = dask.delayed(data.estimate_count)(extents.bounds).persist() + if filter: chunks = extents.chunk(data, resolution, point_count, depth) @@ -21,22 +45,42 @@ def scan(tdb_dir, pointcloud, bounds, point_count=600000, resolution=100, else: cell_counts = extent_handle(extents, data, resolution, point_count, - depth) + depth, log) + num_cells = np.sum(cell_counts).item() std = np.std(cell_counts) mean = np.mean(cell_counts) rec = int(mean + std) - logger.info(f'Tiling information:') - logger.info(f' Mean tile size: {mean}') - logger.info(f' Std deviation: {std}') - logger.info(f' Recommended split size: {rec}') + pc_info = dict(pc_info=dict(storage_bounds=tdb.config.root.to_json(), + data_bounds=data.bounds.to_json(), count=dask.compute(count))) + tiling_info = dict(tile_info=dict(num_cells=num_cells, + num_tiles=len(cell_counts), mean=mean, std_dev=std, recommended=rec)) - return rec + final_info = pc_info | tiling_info + logger.info(json.dumps(final_info, indent=2)) + return final_info -def extent_handle(extent: Extents, data: Data, res_threshold=100, - pc_threshold=600000, depth_threshold=6): + +def extent_handle(extent: Extents, data: Data, res_threshold:int=100, + pc_threshold:int=600000, depth_threshold:int=6, log: Log = None) -> list[int]: + """ + Recurisvely iterate through quad tree of this Extents object with given + threshold parameters. + + :param extent: Current Extent. + :param data: Data object created from point cloud file. + :param res_threshold: Resolution threshold., defaults to 100 + :param pc_threshold: Point count threshold., defaults to 600000 + :param depth_threshold: Tree depth threshold., defaults to 6 + :return: Returns list of Extents that fit thresholds. + """ + + if log is None: + logger = logging.getLogger('silvimetric') + else: + logger = log if extent.root is not None: bminx, bminy, bmaxx, bmaxy = extent.root.get() @@ -58,12 +102,12 @@ def extent_handle(extent: Extents, data: Data, res_threshold=100, curr = db.from_delayed(tile_info(chunk, data, res_threshold, pc_threshold, depth_threshold)) - logger = logging.getLogger('silvimetric') a = [ ] curr_depth = 0 while curr.npartitions > 0: - logger.info(f'Chunking {curr.npartitions} tiles at depth {curr_depth}') + logger = logging.getLogger('silvimetric') + logger.debug(f'Chunking {curr.npartitions} tiles at depth {curr_depth}') n = curr.compute() to_add = [ x for x in n if isinstance(x, int) ] a = a + to_add @@ -77,9 +121,20 @@ def extent_handle(extent: Extents, data: Data, res_threshold=100, @dask.delayed -def tile_info(extent: Extents, data: Data, res_threshold=100, - pc_threshold=600000, depth_threshold=6, depth=0): - +def tile_info(extent: Extents, data: Data, res_threshold:int=100, + pc_threshold:int=600000, depth_threshold:int=6, depth:int=0): + """ + Recursively explore current extents, use thresholds to determine when to + stop searching. + + :param extent: Current Extent. + :param data: Data object created from point cloud file. + :param res_threshold: Resolution threshold., defaults to 100 + :param pc_threshold: Point count threshold., defaults to 600000 + :param depth_threshold: Tree depth threshold., defaults to 6 + :param depth: Current Tree depth., defaults to 0 + :return: Returns list of Extents that fit thresholds. + """ pc = data.estimate_count(extent.bounds) target_pc = pc_threshold diff --git a/src/silvimetric/commands/shatter.py b/src/silvimetric/commands/shatter.py index c3a3389..bd9213c 100644 --- a/src/silvimetric/commands/shatter.py +++ b/src/silvimetric/commands/shatter.py @@ -1,160 +1,234 @@ import numpy as np -import gc -import copy import signal import datetime +import copy +from typing import Generator +import pandas as pd +import tiledb -import dask -from dask.distributed import Client, as_completed, futures_of, CancelledError +from dask.distributed import as_completed, futures_of, CancelledError +from distributed.client import _get_global_client as get_client +from dask.delayed import Delayed import dask.array as da import dask.bag as db +from dask.diagnostics import ProgressBar -from tiledb import SparseArray - -from ..resources import Extents, Storage, ShatterConfig, Data +from .. import Extents, Storage, Data, ShatterConfig, run_metrics def get_data(extents: Extents, filename: str, storage: Storage): - #TODO look at making a record array here + """ + Execute pipeline and retrieve point cloud data for this extent + + :param extents: :class:`silvimetric.resources.extents.Extents` being operated on. + :param filename: Path to either PDAL pipeline or point cloud. + :param storage: :class:`silvimetric.resources.storage.Storage` database object. + :return: Point data array from PDAL. + """ data = Data(filename, storage.config, bounds = extents.bounds) + p = data.pipeline data.execute() - return data.array + return p.get_dataframe(0) + +def arrange(points: pd.DataFrame, leaf, attrs: list[str]): + """ + Arrange data to fit key-value TileDB input format. + + :param data: Tuple of indices and point data array (xis, yis, data). + :param leaf: :class:`silvimetric.resources.extents.Extent` being operated on. + :param attrs: List of attribute names. + :raises Exception: Missing attribute error. + :return: None if no work is done, or a tuple of indices and rearranged data. + """ + if points is None: + return None + if points.size == 0: + return None -def cell_indices(xpoints, ypoints, x, y): - return da.logical_and(xpoints == x, ypoints == y) + points = points.loc[points.Y < leaf.bounds.maxy] + points = points.loc[points.Y >= leaf.bounds.miny] + points = points.loc[points.X >= leaf.bounds.minx] + points = points.loc[points.X < leaf.bounds.maxx, [*attrs, 'xi', 'yi']] -def get_atts(points: np.ndarray, leaf: Extents, attrs: list[str]): if points.size == 0: return None - xis = da.floor(points[['xi']]['xi']) - yis = da.floor(points[['yi']]['yi']) + points.loc[:, 'xi'] = da.floor(points.xi) + # ceil for y because origin is at top left + points.loc[:, 'yi'] = da.ceil(points.yi) - att_view = points[:][attrs] - idx = leaf.get_indices() - l = [att_view[cell_indices(xis, yis, x, y)] for x,y in idx] - return l + return points -def arrange(data: tuple[np.ndarray, np.ndarray, np.ndarray], leaf: Extents, attrs): - if data is None: +def get_metrics(data_in, storage: Storage): + """ + Run DataFrames through metric processes + """ + if data_in is None: return None - di = data - dd = {} - for att in attrs: - try: - dd[att] = np.fromiter([*[np.array(col[att], col[att].dtype) for col in di], None], dtype=object)[:-1] - except Exception as e: - raise Exception(f"Missing attribute {att}: {e}") - counts = np.array([z.size for z in dd['Z']], np.int32) - - ## remove empty indices - empties = np.where(counts == 0)[0] - dd['count'] = counts - idx = leaf.get_indices() - if bool(empties.size): - for att in dd: - dd[att] = np.delete(dd[att], empties) - idx = np.delete(idx, empties) - - dx = idx['x'] - dy = idx['y'] - return (dx, dy, dd) - -def get_metrics(data_in, attrs: list[str], storage: Storage): + return run_metrics(data_in, storage.config.metrics) + +def agg_list(data_in): + """ + Make variable-length point data attributes into lists + """ if data_in is None: return None - ## data comes in as [dx, dy, { 'att': [data] }] - dx, dy, data = data_in + # grouped = data_in.groupby(['xi','yi']) + # grouped = grouped.agg(list) + + # return grouped.assign(count=lambda x: [len(z) for z in grouped.Z]) + + # # coerce datatypes to object so we can store np arrays as cell values + old_dtypes = data_in.dtypes + xyi_dtypes = { 'xi': np.float64, 'yi': np.float64 } + o = np.dtype('O') + col_dtypes = { a: o for a in data_in.columns if a not in ['xi','yi'] } - # make sure it's not empty. No empty writes - if not np.any(data['count']): + + coerced = data_in.astype(col_dtypes | xyi_dtypes) + a = coerced.groupby(['xi','yi']).agg(lambda x: np.array(x, old_dtypes[x.name])) + return a.assign(count=lambda x: [len(z) for z in a.Z]) + +def join(list_data: pd.DataFrame, metric_data): + """ + Join the list data and metric DataFrames together. + """ + if list_data is None or metric_data is None: return None - # doing dask compute inside the dict array because it was too fine-grained - # when it was outside - metric_data = { - f'{m.entry_name(attr)}': [m(cell_data) for cell_data in data[attr]] - for attr in attrs for m in storage.config.metrics - } - data_out = data | metric_data - return (dx, dy, data_out) + if isinstance(metric_data, Delayed): + metric_data = metric_data.compute() -def write(data_in, tdb): + return list_data.join(metric_data).reset_index() - if data_in is None: - return 0 +def write(data_in, storage, timestamp): + """ + Write cell data to database + + :param data_in: Data to be written to database. + :param tdb: TileDB write stream. + :return: Number of points written. + """ + + # data_in = data_in.reset_index() + data_in = data_in.rename(columns={'xi':'X','yi':'Y'}) - dx, dy, dd = data_in - tdb[dx,dy] = dd - pc = int(dd['count'].sum()) + attr_dict = {f'{a.name}': a.dtype for a in storage.config.attrs} + xy_dict = { 'X': data_in.X.dtype, 'Y': data_in.Y.dtype } + metr_dict = {f'{m.name}': m.dtype for m in storage.config.metrics} + dtype_dict = attr_dict | xy_dict | metr_dict + + varlen_types = {a.dtype for a in storage.config.attrs} + + tiledb.from_pandas(uri=storage.config.tdb_dir, sparse=True, + dataframe=data_in, mode='append', timestamp=timestamp, + column_types=dtype_dict, varlen_types=varlen_types) + + pc = data_in['count'].sum().item() p = copy.deepcopy(pc) - del pc, data_in + del pc, data_in return p -def run(leaves: db.Bag, config: ShatterConfig, storage: Storage, - tdb: SparseArray): +Leaves = Generator[Extents, None, None] +def get_processes(leaves: Leaves, config: ShatterConfig, storage: Storage) -> db.Bag: + """ Create dask bags and the order of operations. """ + + ## Handle dask bag transitions through work states + attrs = [a.name for a in config.attrs] + timestamp = (config.time_slot, config.time_slot) + + # remove any extents that have already been done, only skip if full overlap + leaf_bag: db.Bag = db.from_sequence(leaves) + if config.mbr: + def mbr_filter(one: Extents): + return all(one.disjoint_by_mbr(m) for m in config.mbr) + leaf_bag = leaf_bag.filter(mbr_filter) + + def pc_filter(d: pd.DataFrame): + if d is None: + return False + return not d.empty - start_time = config.start_time - ## Handle a kill signal + points: db.Bag = leaf_bag.map(get_data, config.filename, storage) + arranged: db.Bag = points.map(arrange, leaf_bag, attrs) + filtered = arranged.filter(pc_filter) + metrics: db.Bag = filtered.map(run_metrics, storage.config.metrics) + lists: db.Bag = filtered.map(agg_list) + joined: db.Bag = lists.map(join, metrics) + writes: db.Bag = joined.map(write, storage, timestamp) + + return writes + +def run(leaves: Leaves, config: ShatterConfig, storage: Storage) -> int: + """ + Coordinate running of shatter process and handle any interruptions + + :param leaves: Generator of Leaf nodes. + :param config: :class:`silvimetric.resources.config.ShatterConfig` + :param storage: :class:`silvimetric.resources.storage.Storage` + :return: Number of points processed. + """ + + # Process kill handler. Make sure we write out a config even if we fail. def kill_gracefully(signum, frame): - client = dask.config.get('distributed.client') + client = get_client() if client is not None: client.close() end_time = datetime.datetime.now().timestamp() * 1000 + + storage.consolidate_shatter(config.time_slot) config.end_time = end_time - with storage.open(timestamp=(start_time, end_time)) as a: - config.nonempty_domain = a.nonempty_domain() - config.finished=False + config.mbrs = storage.mbrs(config.time_slot) + config.finished=False + config.log.info('Saving config before quitting...') - config.log.info('Saving config before quitting...') - tdb.meta['shatter'] = str(config) - config.log.info('Quitting.') + storage.saveMetadata('shatter', str(config), config.time_slot) + config.log.info('Quitting.') signal.signal(signal.SIGINT, kill_gracefully) - ## Handle dask bag transitions through work states - attrs = [a.name for a in config.attrs] + processes = get_processes(leaves, config, storage) - leaf_bag: db.Bag = db.from_sequence(leaves) - points: db.Bag = leaf_bag.map(get_data, config.filename, storage) - att_data: db.Bag = points.map(get_atts, leaf_bag, attrs) - arranged: db.Bag = att_data.map(arrange, leaf_bag, attrs) - metrics: db.Bag = arranged.map(get_metrics, attrs, storage) - writes: db.Bag = metrics.map(write, tdb) - - ## If dask is distributed, use the futures feature - if dask.config.get('scheduler') == 'distributed': - pc_futures = futures_of(writes.persist()) + ## If dask is distributed, use the futures feature + dc = get_client() + if dc is not None: + pc_futures = futures_of(processes.persist()) for batch in as_completed(pc_futures, with_results=True).batches(): for future, pack in batch: if isinstance(pack, CancelledError): continue for pc in pack: config.point_count = config.point_count + pc + del pc end_time = datetime.datetime.now().timestamp() * 1000 config.end_time = end_time config.finished = True - a = storage.open(timestamp=(start_time, end_time)) - return config.point_count - - ## Handle non-distributed dask scenarios - pc = sum(writes) - end_time = datetime.datetime.now().timestamp() * 1000 - with storage.open(timestamp=(start_time, end_time)) as a: - - config.end_time = end_time - config.finished=True - config.point_count = pc - config.nonempty_domain = a.nonempty_domain() - tdb.meta['shatter'] = str(config) - - return pc - - -def shatter(config: ShatterConfig): + else: + # Handle non-distributed dask scenarios + with ProgressBar() as p: + config.point_count = sum(processes) + + # modify config to reflect result of shattter process + config.mbr = storage.mbrs(config.time_slot) + config.log.debug('Saving shatter metadata') + config.end_time = datetime.datetime.now().timestamp() * 1000 + config.finished = True + + storage.saveMetadata('shatter', str(config), config.time_slot) + return config.point_count + +def shatter(config: ShatterConfig) -> int: + """ + Handle setup and running of shatter process. + Will look for a config that has already been run before and needs to be + resumed. + + :param config: :class:`silvimetric.resources.config.ShatterConfig`. + :return: Number of points processed. + """ # get start time in milliseconds config.start_time = datetime.datetime.now().timestamp() * 1000 # set up tiledb @@ -162,27 +236,31 @@ def shatter(config: ShatterConfig): data = Data(config.filename, storage.config, config.bounds) extents = Extents.from_sub(config.tdb_dir, data.bounds) + config.log.info('Beginning shatter process...') + config.log.debug(f'Shatter Config: {config}') + config.log.debug(f'Data: {str(data)}') + config.log.debug(f'Extents: {str(extents)}') - with storage.open('w') as tdb: + if get_client() is None: + config.log.warning("Selected scheduler type does not support" + "continuously updated config information.") - ## shatter should still output a config if it's interrupted - # TODO add slices yet to be finished so we can pick up this process - # in the future + if not config.time_slot: # defaults to 0, which is reserved for storage cfg + config.time_slot = storage.reserve_time_slot() - if config.bounds is None: - config.bounds = extents.bounds + if config.bounds is None: + config.bounds = extents.bounds - config.log.debug('Grabbing leaf nodes...') - if config.tile_size is not None: - leaves = extents.get_leaf_children(config.tile_size) - else: - leaves = extents.chunk(data, 100) + config.log.debug('Grabbing leaf nodes...') + if config.tile_size is not None: + leaves = extents.get_leaf_children(config.tile_size) + else: + leaves = extents.chunk(data, 100) - # Begin main operations - config.log.debug('Fetching and arranging data...') - pc = run(leaves, config, storage, tdb) - config.point_count = int(pc) + # Begin main operations + config.log.debug('Fetching and arranging data...') + pc = run(leaves, config, storage) - config.log.debug('Saving shatter metadata') - tdb.meta['shatter'] = str(config) - return config.point_count + #consolidate the fragments in this time slot down to just one + storage.consolidate_shatter(config.time_slot) + return pc \ No newline at end of file diff --git a/src/silvimetric/resources/__init__.py b/src/silvimetric/resources/__init__.py index a8c2dbe..e69de29 100644 --- a/src/silvimetric/resources/__init__.py +++ b/src/silvimetric/resources/__init__.py @@ -1,10 +0,0 @@ -__version__ = '0.0.1' - -from .bounds import Bounds -from .extents import Extents -from .storage import Storage -from .metric import Metric, Metrics -from .log import Log -from .data import Data -from .entry import Attribute, Pdal_Attributes, Attributes -from .config import StorageConfig, ShatterConfig, ExtractConfig, ApplicationConfig \ No newline at end of file diff --git a/src/silvimetric/resources/array_extensions.py b/src/silvimetric/resources/array_extensions.py new file mode 100644 index 0000000..36b07c0 --- /dev/null +++ b/src/silvimetric/resources/array_extensions.py @@ -0,0 +1,115 @@ +from __future__ import annotations + +from typing import Sequence + +import re + +import numpy as np +import pandas as pd + +@pd.api.extensions.register_extension_dtype +class AttributeDtype(pd.api.extensions.ExtensionDtype): + type = np.generic + + def __init__(self, subtype=np.float32): + self._subtype = np.dtype(subtype) + + def __str__(self) -> str: + return f'AttributeDtype[{self.subtype}]' + + def __repr__(self) -> str: + return f'AttributeDtype[{self.subtype}]' + + # TestDtypeTests + def __hash__(self) -> int: + return hash(str(self)) + + + @property + def subtype(self): + return self._subtype + + @property + def name(self): + return f"AttributeDtype[{self.subtype}]" + + @classmethod + def construct_array_type(cls): + return AttributeArray + + @classmethod + def construct_from_string(cls, string): + if string.lower() == "attribute": + return cls() + match = re.match(r"^attribute\[(\w+)\]$", string, re.IGNORECASE) + if match: + return cls(match.group(1)) + raise TypeError(f"Cannot construct a 'AttributeDtype' from '{string}'") + + +class AttributeArray(pd.api.extensions.ExtensionArray): + def __init__(self, arrays, dtype): + assert isinstance(dtype, AttributeDtype) + self._dtype = dtype + # self._data = np.array([np.array(array, dtype=dtype.subtype) for array in arrays], dtype=dtype.subtype) + self._data = [np.asarray(array, dtype=dtype.subtype) for array in arrays] + + @classmethod + def _from_sequence(cls, scalars, dtype=None, copy=False): + if dtype is None and bool(scalars.dtype): + return cls(scalars, AttributeDtype(scalars.dtype)) + elif dtype is None: + cls(scalars, AttributeDtype(np.generic)) + return cls(scalars, dtype) + + def __len__(self): + return len(self._data) + + def __getitem__(self, i): + return self._data[i] + + @property + def dtype(self): + return self._dtype + + def copy(self): + return type(self)(self._data, self._dtype) + + def take(self, indices, allow_fill=False, fill_value=None): + """ Take elements from an array. """ + + if allow_fill and fill_value is None: + fill_value = self.dtype.na_value + + result = pd.core.algorithms.take(self._data, indices, allow_fill=allow_fill, + fill_value=fill_value) + return self._from_sequence(result) + + @classmethod + def _from_factorized(cls, uniques: np.ndarray, original: AttributeArray): + """ Reconstruct an AttributeArray after factorization. """ + return cls(uniques, AttributeDtype(uniques.dtype)) + + @pd.core.ops.unpack_zerodim_and_defer('__eq__') + def __eq__(self, other): + return self._apply_operator('__eq__', other, recast=False) + + def nbytes(self): + return self._data.nbytes + + def isna(self): + """ A 1-D array indicating if each value is missing. """ + return pd.isnull(self._data) + + def copy(self): + """ Return a copy of the array. """ + copied = self._data.copy() + return type(self)(copied, self.dtype) + + @classmethod + def _concat_same_type(cls, to_concat: Sequence[AttributeArray]) -> AttributeArray: + """ + Concatenate multiple AngleArrays. + """ + return cls(np.concatenate(to_concat)) + diff --git a/src/silvimetric/resources/attribute.py b/src/silvimetric/resources/attribute.py new file mode 100644 index 0000000..05dff52 --- /dev/null +++ b/src/silvimetric/resources/attribute.py @@ -0,0 +1,84 @@ +import json +import numpy as np +import pdal +from tiledb import Attr +from .array_extensions import AttributeArray, AttributeDtype + +class Attribute(): + """Represents point data from a PDAL execution that has been binned, and + provides the information necessary to transfer that data to the database.""" + + def __init__(self, name: str, dtype) -> None: + self.name = name + """Name of the attribute, eg. Intensity.""" + self.dtype: AttributeDtype + """SilviMetric representation of array of numpy dtype""" + if isinstance(dtype, AttributeDtype): + self.dtype = dtype + else: + # AttributeDtype takes any dtype that can be passed to np.dtype + try: + self.dtype = AttributeDtype(subtype=dtype) + except Exception as e: + raise AttributeError(f"Invalid dtype passed to Attribute: {dtype}") from e + + def make_array(self, data, copy=False): + return AttributeArray(data=data, copy=copy) + + def entry_name(self) -> str: + """Return TileDB attribute name.""" + return self.name + + def __eq__(self, other): + if self.dtype != other.dtype: + return False + elif self.name != other.name: + return False + else: + return True + + def __hash__(self): + return hash(('name', self.name, 'dtype', self.dtype)) + + + def schema(self) -> Attr: + """ + Create the tiledb schema for this attribute. + :return: TileDB attribute schema + """ + return Attr(name=self.name, dtype=self.dtype.subtype, var=True) + + def to_json(self) -> object: + return { + 'name': self.name, + 'dtype': np.dtype(self.dtype.subtype).str + } + + @staticmethod + def from_dict(data: dict) -> "Attribute": + """ + Make an Attribute from a JSON like object + """ + name = data['name'] + dtype = data['dtype'] + return Attribute(name, dtype) + + @staticmethod + def from_string(data: str) -> "Attribute": + """ + Create Attribute from string or dict version of it. + + :param data: Stringified or json object of attribute + :raises TypeError: Incorrect type of incoming data, must be string or dict + :return: Return derived Attribute + """ + j = json.loads(data) + return Attribute.from_dict(j) + + + def __repr__(self) -> str: + return json.dumps(self.to_json()) + +# A list of pdal dimensions can be found here https://pdal.io/en/2.6.0/dimensions.html +Pdal_Attributes = { d['name']: Attribute(d['name'], d['dtype']) for d in pdal.dimensions } +Attributes = Pdal_Attributes \ No newline at end of file diff --git a/src/silvimetric/resources/bounds.py b/src/silvimetric/resources/bounds.py index afd8242..d8a500c 100644 --- a/src/silvimetric/resources/bounds.py +++ b/src/silvimetric/resources/bounds.py @@ -1,12 +1,19 @@ import json import ast +#TODO should these bounds have a buffer? class Bounds(dict): #for JSON serializing + """Simple class to represent a 2 or 3-dimensional bounding box that can be + generated from both JSON or PDAL bounds form.""" def __init__(self, minx: float, miny: float, maxx: float, maxy: float): self.minx = float(minx) + """minimum X Plane""" self.miny = float(miny) + """minimum Y plane""" self.maxx = float(maxx) + """maximum X plane""" self.maxy = float(maxy) + """maximum Y plane""" def __eq__(self, other): return other.minx == self.minx and \ @@ -19,18 +26,24 @@ def __ne__(self, other): def __bool__(self): return True + def __repr__(self) -> str: + return str(self.get()) @staticmethod def from_string(bbox_str: str): - """ - Accepts bounds from strings in the form: + """Create Bounds object from a PDAL bounds string in the form: "([1,101],[2,102],[3,103])" "{\"minx\": 1,\"miny\": 2,\"maxx\": 101,\"maxy\": 102}" "[1,2,101,102]" "[1,2,3,101,102,103]" + :param bbox_str: Bounds string + :raises Exception: Unable to load Bounds via json or PDAL bounds type + :raises Exception: Bounding boxes must have either 4 or 6 elements + :return: Bounds object """ + try: bbox = json.loads(bbox_str) except json.decoder.JSONDecodeError as e: @@ -67,20 +80,33 @@ def from_string(bbox_str: str): def get(self) -> list[float]: - return [self.minx, self.miny, self.maxx, self.maxy] - + """Return Bounds as a list of floats - def __repr__(self) -> str: - return str(self.get()) + :return: list of floats in form [minx, miny, maxx, maxy] + """ + return [self.minx, self.miny, self.maxx, self.maxy] def to_string(self) -> str: + """Return string representation of Bounds + + :return: string of a list of floats in form [minx, miny, maxx, maxy] + """ return self.__repr__() - def to_json(self) -> str: - return json.dumps(self.get()) + + def to_json(self) -> list[float]: + """Return object as a json serializable list + + :return: list of floats in form [minx, miny, maxx, maxy] + """ + return self.get() def bisect(self): + """Bisects the current Bounds + + :yield: 4 child bounds + """ centerx = self.minx + ((self.maxx - self.minx)/ 2) centery = self.miny + ((self.maxy - self.miny)/ 2) yield from [ @@ -91,8 +117,47 @@ def bisect(self): ] def disjoint(self, other): + """Determine if two bounds are disjointed + + :param other: Bounds this object is being compared to + :return: True if this box shares no point with the other box, otherwise False + """ if other.minx > self.maxx or other.maxx < self.minx: return True if other.miny > self.maxy or other.maxy < self.miny: return True return False + + def adjust_to_cell_lines(self, resolution): + xmindif = self.minx % resolution + xmaxdif = self.maxx % resolution + ymaxdif = self.maxy % resolution + ymindif = self.miny % resolution + + if xmindif: + self.minx = self.minx - xmindif + if xmaxdif: + self.maxx = self.maxx + (resolution - xmaxdif) + if ymindif: + self.miny = self.miny - ymindif + if ymaxdif: + self.maxy = self.maxy + (resolution - ymaxdif) + + @staticmethod + def shared_bounds(first, second): + """Find the Bounds that is shared between two Bounds. + + :param first: First Bounds object for comparison. + :param second: Second Bounds object for comparison. + :returns: None if there is no overlap, otherwise the shared Bounds + """ + + if first.disjoint(second): + return None + + minx = max(first.minx, second.minx) + maxx = min(first.maxx, second.maxx) + miny = max(first.miny, second.miny) + maxy = min(first.maxy, second.maxy) + + return Bounds(minx, miny, maxx, maxy) diff --git a/src/silvimetric/resources/config.py b/src/silvimetric/resources/config.py index c2fba36..ce0a81e 100644 --- a/src/silvimetric/resources/config.py +++ b/src/silvimetric/resources/config.py @@ -13,10 +13,10 @@ from .log import Log from .extents import Bounds -from .metric import Metric, Metrics -from .entry import Attribute, Attributes -from . import constants -from . import __version__ +from .metric import Metric +from .metrics import grid_metrics +from .attribute import Attribute, Attributes +from .. import __version__ class SilviMetricJSONEncoder(json.JSONEncoder): @@ -25,9 +25,13 @@ def default(self, o): @dataclass(kw_only=True) class Config(ABC): + """Base config""" tdb_dir: str + """Path to TileDB directory to use.""" log: Log = field(default_factory = lambda: Log("INFO")) + """Log object.""" debug: bool = field(default=False) + """Debug flag.""" def to_json(self): keys = self.__dataclass_fields__.keys() @@ -38,6 +42,10 @@ def to_json(self): d['log'] = d['log'].to_json() return d + @classmethod + def from_json(self, data: str): + return self.from_string(json.dumps(data)) + @classmethod @abstractmethod def from_string(self, data: str): @@ -51,18 +59,30 @@ def __repr__(self): @dataclass class StorageConfig(Config): + """ Config for constructing a Storage object """ root: Bounds + """Root project bounding box""" crs: pyproj.CRS - resolution: float = DEFAULT_RESOLUTION + """Coordinate reference system, same for all data in a project""" + resolution: float = 30.0 + """Resolution of cells, same for all data in a project, defaults to 30.0""" attrs: list[Attribute] = field(default_factory=lambda: [ Attribute(a, Attributes[a].dtype) for a in [ 'Z', 'NumberOfReturns', 'ReturnNumber', 'Intensity' ]]) - metrics: list[Metric] = field(default_factory=lambda: [ Metrics[m] - for m in Metrics.keys() ]) + """List of :class:`silvimetric.resources.attribute.Attribute` attributes that + represent point data, defaults to Z, NumberOfReturns, ReturnNumber, Intensity""" + metrics: list[Metric] = field(default_factory=lambda: [ grid_metrics[m] + for m in grid_metrics.keys() ]) + """List of :class:`silvimetric.resources.metrics.grid_metrics` grid_metrics that + represent derived data, defaults to values in grid_metrics object""" version: str = __version__ + """Silvimetric version""" capacity: int = 1000000 - + """TileDB Capacity, defaults to 1000000""" + next_time_slot: int = 1 + """Next time slot to be allocated to a shatter process. Increment after + use., defaults to 1""" def __post_init__(self) -> None: @@ -77,12 +97,14 @@ def __post_init__(self) -> None: self.attrs = [Attributes[a] for a in [ 'Z', 'NumberOfReturns', 'ReturnNumber', 'Intensity' ]] if not len(self.metrics): - self.metrics = [ Metrics[m] for m in Metrics.keys() ] + self.metrics = [ grid_metrics[m] for m in grid_metrics.keys() ] if not self.crs.is_projected: - raise Exception(f"Given coordinate system is not a rectilinear projected coordinate system") + raise Exception("Given coordinate system is not a rectilinear" + " projected coordinate system") - self.metric_definitions = { m.name: str(m) for m in self.metrics} + self.metric_definitions = { m.name: str(m) for m in + self.metrics} def __eq__(self, other): @@ -100,7 +122,8 @@ def to_json(self): d['attrs'] = [a.to_json() for a in self.attrs] d['metrics'] = [m.to_json() for m in self.metrics] d['crs'] = json.loads(self.crs.to_json()) - d['root'] = json.loads(self.root.to_json()) + d['root'] = self.root.to_json() + return d @classmethod @@ -108,17 +131,18 @@ def from_string(cls, data: str): x = json.loads(data) root = Bounds(*x['root']) if 'metrics' in x: - ms = [ Metric.from_string(m) for m in x['metrics']] + ms = [ Metric.from_dict(m) for m in x['metrics']] else: - ms = None + ms = [ ] if 'attrs' in x: - attrs = [ Attribute.from_string(a) for a in x['attrs']] + attrs = [ Attribute.from_dict(a) for a in x['attrs']] else: - attrs = None + attrs = [ ] if 'crs' in x: crs = pyproj.CRS.from_user_input(json.dumps(x['crs'])) else: crs = None + n = cls(tdb_dir = x['tdb_dir'], root = root, log = Log(**x['log']), @@ -126,18 +150,36 @@ def from_string(cls, data: str): attrs = attrs, crs = crs, metrics = ms, - capacity = x['capacity']) + capacity = x['capacity'], + version = x['version']) return n def __repr__(self): - return json.dumps(self.to_json()) + j = self.to_json() + return json.dumps(j) @dataclass class ApplicationConfig(Config): + """ Base application config """ + debug: bool = False, + """Debug mode, defaults to False""" progress: bool = False, + """Should processes display progress bars, defaults to False""" + + # Dask configuration + dasktype: str = 'processes' + """Dask parallelization type. For information see + https://docs.dask.org/en/stable/scheduling.html#local-threads """ scheduler: str = 'distributed' + """Dask scheduler, defaults to 'distributed'""" + workers: int = 12 + """Number of dask workers""" + threads: int = 4 + """Number of threads per dask worker""" + watch: bool = False + """Open dask diagnostic page in default web browser""" def to_json(self): d = super().to_json() @@ -148,31 +190,63 @@ def from_string(cls, data: str): x = json.loads(data) n = cls(tdb_dir = x['tdb_dir'], debug = x['debug'], - progress = x['progress']) + progress = x['progress'], + dasktype = x['dasktype'], + scheduler = x['scheduler'], + workers = x['workers'], + threads = x['threads'], + watch = x['watch']) return n def __repr__(self): return json.dumps(self.to_json()) +Mbr = tuple[tuple[tuple[int,int], tuple[int,int]], ...] @dataclass class ShatterConfig(Config): + """Config for Shatter process""" filename: str + """Input filename referencing a PDAL pipeline or point cloud file.""" date: Union[datetime, Tuple[datetime, datetime]] + """A date or date range representing data collection times.""" attrs: list[Attribute] = field(default_factory=list) + """List of attributes to use in shatter. If this is not set it will be + filled by the attributes in the database instance.""" metrics: list[Metric] = field(default_factory=list) + """A list of metrics to use in shatter. If this is not set it will be filled + by the metrics in the database instance.""" bounds: Union[Bounds, None] = field(default=None) + """The bounding box of the shatter process., defaults to None""" name: uuid.UUID = field(default=uuid.uuid4()) + """UUID representing this shatter process and will be generated if not + provided., defaults to uuid.uuid()""" tile_size: Union[int, None] = field(default=None) + """The number of cells to include in a tile., defaults to None""" start_time:float = 0 + """The process starting time in seconds since Jan 1 1970., defaults to 0""" end_time:float = 0 + """The process ending time in seconds since Jan 1 1970., defaults to 0""" point_count: int = 0 - nonempty_domain: tuple[tuple[int, int], ...] = () + """The number of points that has been processed so far., defaults to 0""" + mbr: Mbr = field(default_factory=tuple) + """The minimum bounding rectangle derived from TileDB array fragments. + This will be used to for resuming shatter processes and making sure it + doesn't repeat work., defaults to tuple()""" finished: bool = False + """Finished flag for shatter process., defaults to False""" + time_slot: int = 0 + """The time slot that has been reserved for this shatter process. Will be + used as the timestamp in tiledb writes to better organize and manage + processes., defaults to 0""" def __post_init__(self) -> None: from .storage import Storage s = Storage.from_db(self.tdb_dir) + if isinstance(self.tile_size, float): + self.tile_size = int(self.tile_size) + self.log.warning(f'Truncating tile size to integer({self.tile_size})') + pass if not self.attrs: self.attrs = s.getAttributes() if not self.metrics: @@ -184,10 +258,11 @@ def to_json(self): d = super().to_json() d['name'] = str(self.name) - d['bounds'] = json.loads(self.bounds.to_json()) if self.bounds is not None else None + d['time_slot'] = self.time_slot + d['bounds'] = self.bounds.to_json() if self.bounds is not None else None d['attrs'] = [a.to_json() for a in self.attrs] d['metrics'] = [m.to_json() for m in self.metrics] - d['nonempty_domain'] = [ list(a) for a in self.nonempty_domain] + d['mbr'] = list(self.mbr) if isinstance(self.date, tuple): d['date'] = [ dt.strftime('%Y-%m-%dT%H:%M:%SZ') for dt in self.date] @@ -198,26 +273,37 @@ def to_json(self): @classmethod def from_string(cls, data: str): x = json.loads(data) + return cls.from_dict(x) - ms = list([ Metric.from_string(m) for m in x['metrics']]) - attrs = list([ Attribute.from_string(a) for a in x['attrs']]) + @classmethod + def from_dict(cls, data: dict): + x = data + + ms = list([ Metric.from_dict(m) for m in x['metrics']]) + attrs = list([ Attribute.from_dict(a) for a in x['attrs']]) if isinstance(x['date'], list): date = tuple(( datetime.strptime(d, '%Y-%m-%dT%H:%M:%SZ') for d in x['date'])) else: date = datetime.strptime(x['date'], '%Y-%m-%dT%H:%M:%SZ') - nonempty_domains = tuple(tuple(ned) for ned in x['nonempty_domain']) + mbr = tuple(tuple(tuple(mb) for mb in m) for m in x['mbr']) # TODO key error if these aren't there. If we're calling from_string # then these keys need to exist. - n = cls(tdb_dir = x['tdb_dir'], - filename = x['filename'], - attrs = attrs, - metrics = ms, - debug = x['debug'], - name = uuid.UUID(x['name']), + n = cls(tdb_dir=x['tdb_dir'], + filename=x['filename'], + attrs=attrs, + metrics=ms, + debug=x['debug'], + name=uuid.UUID(x['name']), bounds=Bounds(*x['bounds']), - nonempty_domain=nonempty_domains, - date= date) + tile_size=x['tile_size'], + start_time=x['start_time'], + end_time=x['end_time'], + point_count=x['point_count'], + mbr=mbr, + date=date, + time_slot=x['time_slot'], + finished=x['finished']) return n @@ -227,10 +313,17 @@ def __repr__(self): @dataclass class ExtractConfig(Config): + """Config for the Extract process.""" out_dir: str + """The directory where derived rasters should be written.""" attrs: list[Attribute] = field(default_factory=list) + """List of attributes to use in shatter. If this is not set it + will be filled by the attributes in the database instance.""" metrics: list[Metric] = field(default_factory=list) + """A list of metrics to use in shatter. If this is not set it + will be filled by the metrics in the database instance.""" bounds: Bounds = field(default=None) + """The bounding box of the shatter process., defaults to None""" def __post_init__(self) -> None: from .storage import Storage @@ -251,20 +344,28 @@ def __post_init__(self) -> None: def to_json(self): d = super().to_json() - d['metrics'] = [a.to_json() for a in self.attrs] + d['attrs'] = [a.to_json() for a in self.attrs] d['metrics'] = [m.to_json() for m in self.metrics] d['crs'] = json.loads(self.crs.to_json()) - d['bounds'] = json.loads(self.bounds.to_json()) + d['bounds'] = self.bounds.to_json() return d @classmethod def from_string(cls, data: str): x = json.loads(data) if 'metrics' in x: - ms = [ Metric.from_string(m) for m in x['metrics']] + ms = [ Metric.from_dict(m) for m in x['metrics']] if 'attrs' in x: - attrs = [ Attribute.from_string(a) for a in x['attrs']] - n = cls(x['out_dir'], attrs, ms, x['debug']) + attrs = [ Attribute.from_dict(a) for a in x['attrs']] + if 'bounds' in x: + bounds = Bounds(*x['bounds']) + if 'log' in x: + l = x['log'] + log = Log(l['log_level'], l['logdir'], l['logtype'], l['logfilename']) + else: + log = Log("INFO") + n = cls(tdb_dir=x['tdb_dir'], out_dir=x['out_dir'], attrs=attrs, + metrics=ms, debug=x['debug'], bounds=bounds, log=log) return n diff --git a/src/silvimetric/resources/data.py b/src/silvimetric/resources/data.py index ac2e03b..d17fd2e 100644 --- a/src/silvimetric/resources/data.py +++ b/src/silvimetric/resources/data.py @@ -1,6 +1,7 @@ -from . import Bounds +from .bounds import Bounds from .config import StorageConfig import numpy as np +import copy import pdal @@ -8,25 +9,52 @@ import json class Data: + """Represents a point cloud or PDAL pipeline, and performs essential operations + necessary to understand and execute a Shatter process.""" def __init__(self, filename: str, storageconfig: StorageConfig, bounds: Bounds = None): self.filename = filename + """Path to either PDAL pipeline or point cloud file""" + self.bounds = bounds + """Bounds of this section of data""" self.reader_thread_count = 2 + """Thread count for PDAL reader. Keep to 2 so we don't hog threads""" - self.storageconfig = storageconfig self.reader = self.get_reader() - self.pipeline = self.get_pipeline() + """PDAL reader""" + if self.bounds is None: self.bounds = Data.get_bounds(self.reader) + # adjust bounds if necessary + self.bounds.adjust_to_cell_lines(storageconfig.resolution) + self.bounds = Bounds.shared_bounds(self.bounds, storageconfig.root) + + self.storageconfig = storageconfig + """:class:`silvimetric.resources.StorageConfig`""" + self.pipeline = self.get_pipeline() + """PDAL pipeline""" + + self.log = storageconfig.log + + def to_json(self): + j = dict(filename=self.filename, bounds=self.bounds.get(), + pipeline=json.loads(self.pipeline.pipeline), is_pipeline=self.is_pipeline()) + return j + + def __repr__(self): + return json.dumps(self.to_json(), indent=2) def is_pipeline(self) -> bool: - """Does this instance represent a pdal.Pipeline or a simple filename""" + """Does this instance represent a pdal.Pipeline or a simple filename + + :return: Return true if input is a pipeline + """ p = pathlib.Path(self.filename) if p.suffix == '.json' and p.name != 'ept.json': @@ -35,26 +63,37 @@ def is_pipeline(self) -> bool: def make_pipeline(self) -> pdal.Pipeline: - """Take a COPC or EPT endpoint and generate a PDAL pipeline for it""" + """Take a COPC or EPT endpoint and generate a PDAL pipeline for it + + :return: Return PDAL pipeline + """ reader = pdal.Reader(self.filename, tag='reader') reader._options['threads'] = self.reader_thread_count if self.bounds: reader._options['bounds'] = str(self.bounds) - class_zero = pdal.Filter.assign(value="Classification = 0") - rn = pdal.Filter.assign(value="ReturnNumber = 1 WHERE ReturnNumber < 1") - nor = pdal.Filter.assign(value="NumberOfReturns = 1 WHERE NumberOfReturns < 1") - ferry = pdal.Filter.ferry(dimensions="X=>xi, Y=>yi") - # assign_x = pdal.Filter.assign( - # value=f"xi = (X - {self.root.minx}) / {resolution}") - # assign_y = pdal.Filter.assign( - # value=f"yi = ({self.root.maxy} - Y) / {resolution}") - return reader | class_zero | rn | nor #| ferry | assign_x | assign_y #| smrf | hag + return reader.pipeline() + ## TODO + ## Remove these filters.assign stages for now + ## Waiting for a pdal/pdal-python fix to this + ## https://github.com/PDAL/python/issues/174 - def get_pipeline(self): - """Fetch the pipeline for the instance""" + # class_zero = pdal.Filter.assign(value="Classification = 0") + # rn = pdal.Filter.assign(value="ReturnNumber = 1 WHERE ReturnNumber < 1") + # nor = pdal.Filter.assign(value="NumberOfReturns = 1 WHERE NumberOfReturns < 1") + # ferry = pdal.Filter.ferry(dimensions="X=>xi, Y=>yi") + + # return reader | class_zero | rn | nor + + def get_pipeline(self) -> pdal.Pipeline: + """Fetch the pipeline for the instance + + :raises Exception: File type isn't COPC or EPT + :raises Exception: More than one reader detected + :return: Return PDAL pipline + """ # If we are a pipeline, read and parse it. If we # aren't, go make_pipeline using some options that @@ -84,7 +123,15 @@ def get_pipeline(self): # we only answer to copc or ept readers if stage_kind in allowed_readers: if self.bounds: - stage._options['bounds'] = str(self.bounds) + res = self.storageconfig.resolution + collar = Bounds( + self.bounds.minx - res, + self.bounds.miny - res, + self.bounds.maxx + res, + self.bounds.maxy + res + ) + stage._options['bounds'] = str(collar) + # stage._options['bounds'] = str(self.bounds) # We strip off any writers from the pipeline that were # given to us and drop them on the floor @@ -100,30 +147,46 @@ def get_pipeline(self): # Add xi and yi – only need this for PDAL < 2.6 ferry = pdal.Filter.ferry(dimensions="X=>xi, Y=>yi") assign_x = pdal.Filter.assign(value=f"xi = (X - {self.storageconfig.root.minx}) / {resolution}") - assign_y = pdal.Filter.assign(value=f"yi = ({self.storageconfig.root.maxy} - Y) / {resolution}") + assign_y = pdal.Filter.assign(value=f"yi = (({self.storageconfig.root.maxy} - Y) / {resolution}) - 1") + # hag = pdal.Filter.hag_nn() + stages.append(ferry) stages.append(assign_x) stages.append(assign_y) + # stages.append(hag) # return our pipeline return pdal.Pipeline(stages) def execute(self): + """Execute PDAL pipeline + + :raises Exception: PDAL error message passed from execution + """ try: self.pipeline.execute() - # self.storageconfig.log.debug(f"PDAL log: {self.pipeline.log}") + if self.pipeline.log and self.pipeline.log is not None: + self.log.debug(f"PDAL log: {self.pipeline.log}") except Exception as e: + if self.pipeline.log and self.pipeline.log is not None: + self.log.debug(f"PDAL log: {self.pipeline.log}") print(self.pipeline.pipeline, e) raise e def get_array(self) -> np.ndarray: - """Fetch the array from the execute()'d pipeline""" + """Fetch the array from the execute()'d pipeline + + :return: get data as a numpy ndarray + """ return self.pipeline.arrays[0] array = property(get_array) def get_reader(self) -> pdal.Reader: - """Grab or make the reader for this instance so we can use it to do things like - get the count()""" + """Grab or make the reader for this instance so we can use it to do things + like get the count() + + :return: get PDAL reader for input + """ if self.is_pipeline(): p = pathlib.Path(self.filename) j = p.read_bytes().decode('utf-8') @@ -140,12 +203,23 @@ def get_reader(self) -> pdal.Reader: @staticmethod def get_bounds(reader: pdal.Reader) -> Bounds: + """Get the bounding box of a point cloud from PDAL. + + :param reader: PDAL Reader representing input data + :return: bounding box of point cloud + """ p = reader.pipeline() qi = p.quickinfo[reader.type] return Bounds.from_string(json.dumps(qi['bounds'])) def estimate_count(self, bounds: Bounds) -> int: - """For the provided bounds, estimate the maximum number of points that could be inside them for this instance.""" + """For the provided bounds, estimate the maximum number of points that + could be inside them for this instance. + + :param bounds: query bounding box + :return: estimated point count + """ + reader = self.get_reader() if bounds: reader._options['bounds'] = str(bounds) @@ -156,8 +230,14 @@ def estimate_count(self, bounds: Bounds) -> int: return pc def count(self, bounds: Bounds) -> int: - """For the provided bounds, read and count the number of points that are inside them for this instance.""" - reader = self.get_reader() + """For the provided bounds, read and count the number of points that are + inside them for this instance. + + :param bounds: query bounding box + :return: point count + """ + + reader = copy.deepcopy(self.get_reader()) if bounds: reader._options['bounds'] = str(bounds) pipeline = reader.pipeline() diff --git a/src/silvimetric/resources/entry.py b/src/silvimetric/resources/entry.py deleted file mode 100644 index bdbf6e6..0000000 --- a/src/silvimetric/resources/entry.py +++ /dev/null @@ -1,76 +0,0 @@ -import json -import numpy as np -import pdal -from typing import Self, Union -from abc import ABC, abstractmethod -from tiledb import Attr - -class Entry(ABC): - - def __eq__(self, other: Self): - return self.name == other.name and \ - self.dtype == other.dtype and \ - self.dependencies == other.dependencies - - @abstractmethod - def entry_name(self) -> str: - raise NotImplementedError - - @abstractmethod - def schema(self) -> Attr: - raise NotImplementedError - - @abstractmethod - def to_json(self) -> object: - raise NotImplementedError - - @staticmethod - @abstractmethod - def from_string(data: str) -> Self: - raise NotImplementedError - - @abstractmethod - def __repr__(self): - raise NotImplementedError - - -class Attribute(Entry): - - def __init__(self, name: str, dtype: np.dtype, deps: list[Self]=None): - super().__init__() - self.name = name - self.dtype = dtype - self.dependencies = deps - - def entry_name(self) -> str: - return self.name - - def schema(self) -> Attr: - return Attr(name=self.name, dtype=self.dtype, var=True) - - def to_json(self) -> object: - return { - 'name': self.name, - 'dtype': np.dtype(self.dtype).str, - 'dependencies': self.dependencies, - } - - @staticmethod - def from_string(data: Union[str, dict]) -> Self: - if isinstance(data, str): - j = json.loads(data) - elif isinstance(data, dict): - j = data - else: - raise TypeError(f'Type of data, {data} is not str or dict') - name = j['name'] - dtype = j['dtype'] - deps = j['dependencies'] - return Attribute(name, dtype, deps) - - def __repr__(self): - return json.dumps(self.to_json()) - -# A list of pdal dimensions can be found here https://pdal.io/en/2.6.0/dimensions.html -Pdal_Attributes = { d['name']: Attribute(d['name'], d['dtype']) for d in pdal.dimensions } -Attributes = Pdal_Attributes \ No newline at end of file diff --git a/src/silvimetric/resources/extents.py b/src/silvimetric/resources/extents.py index e9c164b..d3d0959 100644 --- a/src/silvimetric/resources/extents.py +++ b/src/silvimetric/resources/extents.py @@ -5,14 +5,18 @@ import dask import dask.bag as db -from typing import Self from math import ceil +from .log import Log from .bounds import Bounds from .storage import Storage from .data import Data +IndexDomain = tuple[float, float] +IndexDomainList = tuple[IndexDomain, IndexDomain] + class Extents(object): + """Handles bounds operations for point cloud data.""" def __init__(self, bounds: Bounds, @@ -20,29 +24,87 @@ def __init__(self, root: Bounds): self.bounds = bounds + """Bounding box of this section of data.""" self.root = root + """Root bounding box of the database.""" + # adjust bounds so they're matching up with cell lines + self.bounds.adjust_to_cell_lines(resolution) minx, miny, maxx, maxy = self.bounds.get() + self.rangex = maxx - minx + """Range of X Indices""" self.rangey = maxy - miny + """Range of Y indices""" self.resolution = resolution + """Resolution of database.""" self.cell_count = int((self.rangex * self.rangey) / self.resolution ** 2) + """Number of cells in this Extents""" self.x1 = math.floor((minx - self.root.minx) / resolution) - self.y1 = math.floor((self.root.maxy - maxy) / resolution) + """Minimum X index""" self.x2 = math.ceil((maxx - self.root.minx) / resolution) + """Maximum X index""" + + self.y1 = math.ceil((self.root.maxy - maxy) / resolution) + """Minimum Y index, or maximum Y value in point cloud""" self.y2 = math.ceil((self.root.maxy - miny) / resolution) + """Maximum Y index, or minimum Y value in point cloud""" + self.domain: IndexDomainList = ((self.x1, self.x2), (self.y1, self.y2)) + """Minimum bounding rectangle of this Extents""" def get_indices(self): + """ + Create indices for this section of the database relative to the root + bounds. + + :return: Indices of this bounding box + """ return np.array( [(i,j) for i in range(self.x1, self.x2) for j in range(self.y1, self.y2)], dtype=[('x', np.int32), ('y', np.int32)] ) + def disjoint_by_mbr(self, mbr): + """ + Determine if this Extents shares any points with a minimum bounding + rectangle. + + :param mbr: Minimum bounding rectangle as defined by TileDB. + :return: True if no shared points, false otherwise. + """ + xs, ys = mbr + x1, x2 = xs + y1, y2 = ys + if x1 > self.x2 or x2 < self.x1: + return True + if y1 > self.y2 or y2 < self.y1: + return True + return False + + def disjoint(self, other): + """ + Determined if this Extents shares any points with another Extents object. + + :param other: Extents object to compare against. + :return: True if no shared points, false otherwise. + """ + return self.bounds.disjoint(other.bounds) + def chunk(self, data: Data, res_threshold=100, pc_threshold=600000, - depth_threshold=6, scan=False): + depth_threshold=6): + """ + Split up a dataset into tiles based on the given thresholds. Unlike Scan + this will filter out any tiles that contain no points. + + :param data: Incoming Data object to oeprate on. + :param res_threshold: Resolution threshold., defaults to 100 + :param pc_threshold: Point count threshold., defaults to 600000 + :param depth_threshold: Tree depth threshold., defaults to 6 + :return: Return list of Extents that fit the criteria + """ if self.root is not None: bminx, bminy, bmaxx, bmaxy = self.root.get() r = self.root @@ -53,6 +115,7 @@ def chunk(self, data: Data, res_threshold=100, pc_threshold=600000, # make bounds in scale with the desired resolution minx = bminx + (self.x1 * self.resolution) maxx = bminx + (self.x2 * self.resolution) + miny = bmaxy - (self.y2 * self.resolution) maxy = bmaxy - (self.y1 * self.resolution) @@ -62,13 +125,14 @@ def chunk(self, data: Data, res_threshold=100, pc_threshold=600000, self.root = chunk.bounds filtered = [] - curr = db.from_delayed(chunk.filter(data, res_threshold, pc_threshold, depth_threshold)) - curr_depth = 0 + curr = db.from_delayed([ch.filter(data, res_threshold, pc_threshold, + depth_threshold, 1) for ch in chunk.split()]) + curr_depth = 1 - logger = logging.getLogger('silvimetric') + logger = data.storageconfig.log while curr.npartitions > 0: - logger.info(f'Filtering {curr.npartitions} tiles at depth {curr_depth}') + logger.debug(f'Filtering {curr.npartitions} tiles at depth {curr_depth}') n = curr.compute() to_add = [ne for ne in n if isinstance(ne, Extents)] if to_add: @@ -81,27 +145,41 @@ def chunk(self, data: Data, res_threshold=100, pc_threshold=600000, def split(self): + """ + Split this extent into 4 children along the cell lines + + :return: Returns 4 child extents + """ minx, miny, maxx, maxy = self.bounds.get() x_adjusted = math.floor((maxx - minx) / 2 / self.resolution) - y_adjusted = math.floor((maxy - miny) / 2 / self.resolution) + y_adjusted = math.ceil((maxy - miny) / 2 / self.resolution) midx = minx + (x_adjusted * self.resolution) midy = maxy - (y_adjusted * self.resolution) - return [ + exts = [ Extents(Bounds(minx, miny, midx, midy), self.resolution, self.root), #lower left Extents(Bounds(midx, miny, maxx, midy), self.resolution, self.root), #lower right Extents(Bounds(minx, midy, midx, maxy), self.resolution, self.root), #top left Extents(Bounds(midx, midy, maxx, maxy), self.resolution, self.root) #top right ] + return exts - # create quad tree of chunks for this bounds, run pdal quickinfo over this - # chunk to determine if there are any points available in this - # set a bottom resolution of ~1km @dask.delayed - def filter(self, data: Data, res_threshold=100, pc_threshold=600000, depth_threshold=6, depth=0) -> Self: - + def filter(self, data: Data, res_threshold=100, pc_threshold=600000, depth_threshold=6, depth=0): + """ + Creates quad tree of chunks for this bounds, runs pdal quickinfo over + this to determine if there are any points available. Uses a bottom resolution + of 1km. + + :param data: Data object containing point cloud details. + :param res_threshold: Resolution threshold., defaults to 100 + :param pc_threshold: Point count threshold., defaults to 600000 + :param depth_threshold: Tree depth threshold., defaults to 6 + :param depth: Current tree depth., defaults to 0 + :return: Returns a list of Extents. + """ pc = data.estimate_count(self.bounds) target_pc = pc_threshold @@ -133,6 +211,12 @@ def filter(self, data: Data, res_threshold=100, pc_threshold=600000, depth_thres def _find_dims(self, tile_size): + """ + Find most square-like Extents given the number of cells per tile. + + :param tile_size: Number of cells per tile. + :return: Returns x and y coordinates in a list. + """ s = math.sqrt(tile_size) if int(s) == s: return [s, s] @@ -144,6 +228,12 @@ def _find_dims(self, tile_size): return [x, y] def get_leaf_children(self, tile_size): + """ + Get children Extents with given number of cells per tile. + + :param tile_size: Cells per tile. + :yield: Yield from list of child extents. + """ res = self.resolution xnum, ynum = self._find_dims(tile_size) @@ -167,13 +257,25 @@ def get_leaf_children(self, tile_size): @staticmethod def from_storage(tdb_dir: str): + """ + Create Extents from information stored in database. + + :param tdb_dir: TileDB database directory. + :return: Returns resulting Extents. + """ storage = Storage.from_db(tdb_dir) meta = storage.getConfig() return Extents(meta.root, meta.resolution, meta.root) @staticmethod def from_sub(tdb_dir: str, sub: Bounds): + """ + Create an Extents that is less than the overall extents of the database. + :param tdb_dir: TileDB database directory. + :param sub: Desired bounding box. + :return: Returns resulting Extents. + """ storage = Storage.from_db(tdb_dir) meta = storage.getConfig() @@ -183,6 +285,7 @@ def from_sub(tdb_dir: str, sub: Bounds): return Extents(sub, res, base) + def __repr__(self): minx, miny, maxx, maxy = self.bounds.get() return f"([{minx:.2f},{maxx:.2f}],[{miny:.2f},{maxy:.2f}])" diff --git a/src/silvimetric/resources/lmom4.py b/src/silvimetric/resources/lmom4.py deleted file mode 100644 index 2f0253c..0000000 --- a/src/silvimetric/resources/lmom4.py +++ /dev/null @@ -1,29 +0,0 @@ -import numpy as np - -# adapted from: https://xiaoganghe.github.io/python-climate-visuals/chapters/data-analytics/scipy-basic.html -# added b3 and l4 from: -# Vogel, R. M. and Fennessey, N. M.: L moment diagrams should replace product moment diagrams, -# Water Resour. Res., 29, 1745–1752, https://doi.org/10.1029/93WR00341, 1993. -def lmom4(data): - # lmom4 returns the first four L-moments of data - # data is the 1-d array - # n is the total number of points in data, j is the j_th point - # - # j range in for loops starts with 1 so we need to subtract 1 for all b# equations - - n = len(data) - # sort in descending order - data = np.sort(data.reshape(n))[::-1] - b0 = np.mean(data) - b1 = np.array([(n - j - 1) * data[j] / n / (n - 1) - for j in range(n)]).sum() - b2 = np.array([(n - j - 1) * (n - j - 2) * data[j] / n / (n - 1) / (n - 2) - for j in range(n - 1)]).sum() - b3 = np.array([(n - j - 1) * (n - j - 2) * (n - j - 3) * data[j] / n / (n - 1) / (n - 2) / (n - 3) - for j in range(n - 2)]).sum() - l1 = b0 - l2 = 2 * b1 - b0 - l3 = 6 * (b2 - b1) + b0 - l4 = 20 * b3 - 30 * b2 + 12 * b1 - b0 - - return l1, l2, l3, l4 diff --git a/src/silvimetric/resources/log.py b/src/silvimetric/resources/log.py index 0f5f00f..4b99be1 100644 --- a/src/silvimetric/resources/log.py +++ b/src/silvimetric/resources/log.py @@ -7,10 +7,10 @@ """ import logging import pathlib -import os +import sys + from typing import Any from typing import Dict -from typing import TYPE_CHECKING try: import websocket @@ -62,15 +62,11 @@ def __init__(self, """ Creates logging formatting and structure - Parameters - ---------- - config: - Application config representing the runtime config + :param config: Application config representing the runtime config """ + # need to be careful not to pull logging from previous runs self.logger = logging.getLogger("silvimetric") - self.logger.setLevel(log_level) - self.log_level = log_level self.logdir = logdir if logdir: self.logtype = 'file' @@ -78,6 +74,13 @@ def __init__(self, self.logtype = logtype self.logfilename = logfilename + # do not recreate handlers if they're already present + if self.logger.handlers: + self.log_level = self.logger.level + return + + self.log_level = log_level + self.logger.setLevel(log_level) # File Handler for Logging log_format = logging.Formatter( @@ -121,7 +124,7 @@ def __init__(self, else: # if the user didn't specify a log dir, we just do the StreamHandler if not self.logdir: - log_handler = logging.StreamHandler() + log_handler = logging.StreamHandler(stream=sys.stdout) log_handler.setFormatter(log_format) self.logger.addHandler(log_handler) diff --git a/src/silvimetric/resources/metric.py b/src/silvimetric/resources/metric.py index 6fc5355..15790df 100644 --- a/src/silvimetric/resources/metric.py +++ b/src/silvimetric/resources/metric.py @@ -1,1090 +1,257 @@ import json -# import warnings -import numpy as np -from typing import Callable, Optional, Any, Union -from scipy import stats -from inspect import getsource -from tiledb import Attr -import dask import base64 -import dill -from .entry import Attribute, Entry -from . import lmom4 -from . import constants +from typing import Callable, Optional, Any, Union, List, Self +from uuid import uuid4 +from functools import reduce + +from tiledb import Attr +import numpy as np +import dill +import pandas as pd -# trap warnings as errors...mainly for scipy.stats.skew and scipy.stats.kurtosis -# looks like this causes trouble for dask -# warnings.filterwarnings("error") +import dask +from dask.delayed import Delayed +from distributed import Future +from .attribute import Attribute -MetricFn = Callable[[np.ndarray, float, float, Optional[Union[Any, None]]], np.ndarray] +MetricFn = Callable[[pd.DataFrame, Any], pd.DataFrame] +FilterFn = Callable[[pd.DataFrame, Optional[Union[Any, None]]], pd.DataFrame] # Derived information about a cell of points - ## TODO should create list of metrics as classes that derive from Metric? -class Metric(Entry): - def __init__(self, name: str, method: MetricFn, dtype: np.dtype=np.float32, - deps: list[Attribute]=None): - super().__init__() +class Metric(): + """ + A Metric is a TileDB entry representing derived cell data. There is a base set of + metrics available through Silvimetric, or you can create your own. A Metric + object has all the information necessary to facilitate the derivation of + data as well as its insertion into the database. + """ + def __init__(self, name: str, dtype: np.dtype, method: MetricFn, + dependencies: list[Self]=[], filters: List[FilterFn]=[], + attributes: List[Attribute]=[]) -> None: + + #TODO make deps, filters, attrs into tuples or sets, not lists so they're hashable + self.name = name + """Metric name. eg. mean""" + self.dtype = np.dtype(dtype).str + """Numpy data type.""" + self.dependencies = dependencies + """Metrics this is dependent on.""" self._method = method - self.dtype = dtype - self.dependencies = deps + """The method that processes this data.""" + self.filters = filters + """List of user-defined filters to perform before performing method.""" + self.attributes = attributes + """List of Attributes this Metric applies to. If empty it's used for all + Attributes""" - def schema(self, attr: Attribute): + def __eq__(self, other): + if self.name != other.name: + return False + elif self.dtype != other.dtype: + return False + elif self.dependencies != other.dependencies: + return False + elif self._method != other._method: + return False + elif self.filters != other.filters: + return False + elif self.attributes != other.attributes: + return False + else: + return True + + def __hash__(self): + return hash(( + 'name', self.name, + 'dtype', self.dtype, + 'dependencies', frozenset(self.dependencies), + 'method', base64.b64encode(dill.dumps(self._method)).decode(), + 'filters', frozenset(base64.b64encode(dill.dumps(f)).decode() for f in self.filters), + 'attrs', frozenset(self.attributes))) + + def schema(self, attr: Attribute) -> Any: + """ + Create schema for TileDB creation. + + :param attr: :class:`silvimetric.resources.entry.Atttribute` + :return: TileDB Attribute + """ entry_name = self.entry_name(attr.name) return Attr(name=entry_name, dtype=self.dtype) - # common name, storage name def entry_name(self, attr: str) -> str: + """Name for use in TileDB and extract file generation.""" return f'm_{attr}_{self.name}' - @dask.delayed - def delayed(self, data: np.ndarray) -> np.ndarray: - return self._method(data) + def sanitize_and_run(self, d, *args): + # args come in as a value wrapped in one index of a 2D dataframe + # we need to remove the wrapping and pass the values to the method + # to make things easier + a = tuple( a.values[0][0] for a in args) + return self._method(d, *a) + + def do(self, data: pd.DataFrame, *args) -> pd.DataFrame: + """Run metric and filters. Use previously run metrics to avoid running + the same thing multiple times.""" + + # the index columns are determined by where this data is coming from + # if it has xi and yi, then it's coming from shatter + # if it has X and Y, then it's coming from extract as a rerun of a cell + if isinstance(data, Future): + data = data.result() + + idx = ['xi','yi'] + if any([i not in data.columns for i in idx]): + idx = ['X','Y'] + + if self.attributes: + attrs = [*[a.name for a in self.attributes],*idx] + data = data[attrs] + + data = self.run_filters(data) + gb = data.groupby(idx) + + # create map of current column name to tuple of new column name and metric method + cols = data.columns + runner = lambda d: self.sanitize_and_run(d, *args) + + new_cols = { + c: [ (self.entry_name(c), runner) ] + for c in cols if c not in idx + } + + val = gb.aggregate(new_cols, args=args) + + #remove hierarchical columns + val.columns = val.columns.droplevel(0) + return val + + #TODO make dict with key for each Attribute effected? {att: [fn]} + # for now these filters apply to all Attributes + def add_filter(self, fn: FilterFn, desc: str): + """ + Add filter method to list of filters to run before calling main method. + """ + self.filters.append(fn) + + def run_filters(self, data: pd.DataFrame) -> pd.DataFrame: + for f in self.filters: + ndf = f(data) + #TODO should this check be here? + if not isinstance(ndf, pd.DataFrame): + raise TypeError('Filter outputs must be a DataFrame. ' + f'Type detected: {type(ndf)}') + data = ndf + return data + def to_json(self) -> dict[str, any]: return { 'name': self.name, 'dtype': np.dtype(self.dtype).str, - 'dependencies': self.dependencies, - 'method_str': getsource(self._method), - 'method': base64.b64encode(dill.dumps(self._method)).decode() + 'dependencies': [d.to_json() for d in self.dependencies], + 'method': base64.b64encode(dill.dumps(self._method)).decode(), + 'filters': [base64.b64encode(dill.dumps(f)).decode() for f in self.filters], + 'attributes': [a.to_json() for a in self.attributes] } - def from_string(data: Union[str, dict]): - if isinstance(data, str): - j = json.loads(data) - elif isinstance(data, dict): - j = data - name = j['name'] - dtype = np.dtype(j['dtype']) - dependencies = j['dependencies'] - method = dill.loads(base64.b64decode(j['method'].encode())) - - return Metric(name=name, method=method, dtype=dtype, deps=dependencies) - - def __eq__(self, other): + @staticmethod + def from_dict(data: dict) -> "Metric": + name = data['name'] + dtype = np.dtype(data['dtype']) + method = dill.loads(base64.b64decode(data['method'].encode())) + + if 'dependencies' in data.keys() and \ + data['dependencies'] and \ + data['dependencies'] is not None: + dependencies = [ Metric.from_dict(d) for d in data['dependencies'] ] + else: + dependencies = [ ] + + if 'attributes' in data.keys() and \ + data['attributes'] and \ + data['attributes'] is not None: + attributes = [ Attribute.from_dict(a) for a in data['attributes']] + else: + attributes = [ ] + + if 'filters' in data.keys() and \ + data['filters'] and \ + data['filters'] is not None: + filters = [ dill.loads(base64.b64decode(f)) for f in data['filters'] ] + else: + filters = [ ] + + return Metric(name, dtype, method, dependencies, filters, attributes) + + @staticmethod + def from_string(data: str) -> Self: + j = json.loads(data) + return Metric.from_dict(j) + + def __eq__(self, other) -> tuple: return (self.name == other.name and self.dtype == other.dtype and self.dependencies == other.dependencies and - self._method == other._method) + self._method == other._method, + self.attributes == other.attributes, + self.filters == other.filters) - def __call__(self, data: np.ndarray) -> np.ndarray: - return self._method(data) + def __call__(self, data: pd.DataFrame) -> pd.DataFrame: + return self.do(data) def __repr__(self) -> str: - return json.dumps(self.to_json()) - -#TODO add all metrics from https://github.com/hobuinc/silvimetric/issues/5 - -def m_count(data): - return len(data) - -def m_mean(data): - """ - Return the arithmetic mean. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return np.mean(data) - -def m_mode(data): - """ - Return the mode (most common value). - - Mode is practically undefined for floating point values. FUSION's logic is to - partition data into 64 bins then find the bin with the highest count. - This approach has the disadvantage that the bin size varies depending on - the Z range. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - nbins = 64 - maxv = np.max(data) - minv = np.min(data) - if minv == maxv: - return minv - - bins, binedges = np.histogram(data, bins = nbins, density = False) - - thebin = np.argmax(bins) - - # compute the height and return...nbins - 1 is to get the bottom value of the bin - return minv + thebin * (maxv - minv) / (nbins - 1) - -def m_median(data): - """ - Return the median. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return np.median(data) - -def m_min(data): - """ - Return the minimum value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return np.min(data) - -def m_max(data): - """ - Return the maximum value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return np.max(data) - -def m_stddev(data): - """ - Return the standard deviation of values. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return np.std(data) - -# start of new metrics to match FUSION -def m_variance(data): - """ - Return the variance of values. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return np.var(data) - -def m_cv(data): - """ - Return the coefficient of variation of values. - Standard deviation / mean. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return np.std(data) / np.mean(data) - -# TODO check performance of other methods -def m_abovemean(data): - """ - Return the proportion of values above the mean. + return f"Metric_{self.name}" - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return (data > np.mean(data)).sum() / len(data) - -# TODO check performance of other methods -def m_abovemode(data): - """ - Return the proportion of values above the mode. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return (data > m_mode(data)).sum() / len(data) - -# both stats.skew and stats.kurtosis throw a warning: -# Precision loss occurred in moment calculation due to catastrophic cancellation. -# This occurs when the data are nearly identical. Results may be unreliable. -# -# I think this is because values are very similar...end up close to the mean -# GridMetrics computes these using a different formula that may also have trouble -# with numeric stability. -def m_skewness(data): +def get_methods(data: Union[pd.DataFrame, Delayed], metrics: Metric | list[Metric], + uuid=None) -> list[Delayed]: """ - Return the skewness of values. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - if len(data) < 4: - return NODATA - - try: - s = stats.skew(data) - except: - s = NODATA - - return s - -def m_kurtosis(data): + Create Metric dependency graph by iterating through desired metrics and + their dependencies, creating Delayed objects that can be run later. """ - Return the kurtosis of values. + # identitity for this graph, can be created before or during this method + # call, but needs to be the same across this graph, and unique compared + # to other graphs + if uuid is None: + uuid = uuid4() - Parameters - ---------- - data : numpy.ndarray - Data for dimension + # don't duplicate a delayed object + if not isinstance(data, Delayed): + data = dask.delayed(data) - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ + if isinstance(metrics, Metric): + metrics = [ metrics ] - if len(data) < 4: - return NODATA + # iterate through metrics and their dependencies. + # uuid here will help guide metrics to use the same dependency method + # calls from dask + seq = [] + for m in metrics: + if not isinstance(m, Metric): + continue + ddeps = get_methods(data, m.dependencies, uuid) + dd = dask.delayed(m.do)(data, *ddeps, + dask_key_name=f'{m.name}-{str(uuid)}') + seq.append(dd) - try: - k = stats.kurtosis(data) - except: - k = NODATA + return seq - return k - -def m_aad(data): +def run_metrics(data: pd.DataFrame, metrics: Union[Metric, list[Metric]]) -> pd.DataFrame: """ - Return the average absolute deviation from the mean. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - m = m_mean(data) - return np.mean(np.absolute(data - m)) - -def m_madmedian(data): - """ - Return the median absolute difference from the median value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return stats.median_abs_deviation(data) - -def m_madmean(data): - """ - Return the median absolute difference from the mean value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return stats.median_abs_deviation(data, center=np.mean) - -# TODO needs work -def m_madmode(data): - """ - Return the median absolute difference from the mode value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed + Collect Metric dependency graph and run it, then merge the results together. """ + graph = get_methods(data, metrics) - m = m_mode(data) - return np.median(np.absolute(data - m)) - -# TODO test various methods for interpolation=... for all percentile-related metrics -# I think the default matches FUSION method but need to test -def m_iq(data): - """ - Return the interquartile difference. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return stats.iqr(data) - -def m_90m10(data): - """ - Return the 90th percentile minus the 10th percentile. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - p = np.percentile(data, [10,90]) - return p[1] - p[0] - -def m_95m05(data): - """ - Return the 95th percentile minnus the 5th percentile. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - p = np.percentile(data, [5,95]) - return p[1] - p[0] - -def m_crr(data): - """ - Return the cannopy relief ratio. - (mean - min) / (max - min). - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - maxv = np.max(data) - minv = np.min(data) - if minv == maxv: - return NODATA - - return (np.mean(data) - minv) / (maxv - minv) - -def m_sqmean(data): - """ - Return the square root of the average squared value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return np.sqrt(np.mean(np.square(data))) - -def m_cumean(data): - """ - Return the cube root of the average cubed value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return np.cbrt(np.mean(np.power(np.absolute(data), 3))) - -# TODO compute L-moments. These are done separately because we only add -# a single element to TileDB. This is very inefficient since we have to -# compute all L-moments at once. Ideally, we would have a single metric -# function that returns an array with 7 values - -# added code to compute first 4 l-moments in lmom4.py. There is a package, -# lmoments3 that can compute the same values but I wanted to avoid the -# package as it has some IBM copyright requirements (need to include copyright -# statement with derived works) - -# L1 is same as mean...compute using np.mean for speed -def m_l1(data): - """ - Return the first L-moment (mean). - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - if len(data) < 4: - return NODATA - - return np.mean(data) - -def m_l2(data): - """ - Return the second L-moment. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - if len(data) < 4: - return NODATA - - l = lmom4(data) - return l[1] - -def m_l3(data): - """ - Return the third L-moment. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - if len(data) < 4: - return NODATA - - l = lmom4(data) - return l[2] - -def m_l4(data): - """ - Return the fourth L-moment. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - if len(data) < 4: - return NODATA - - l = lmom4(data) - return l[3] - -def m_lcv(data): - """ - Return the L-moment coefficient of variation (L2 / mean). - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - if len(data) < 4: - return NODATA - - l = lmom4(data) - - if l[0] == 0.0: - return NODATA - - return l[1] / l[0] - -def m_lskewness(data): - """ - Return the L-moment skewness (L3 / L2). - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - if len(data) < 4: - return NODATA - - l = lmom4(data) - if l[1] == 0.0: - return NODATA - - return l[2] / l[1] - -def m_lkurtosis(data): - """ - Return the L-moment kurtosis (L4 / L2). - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - if len(data) < 4: - return NODATA - - l = lmom4(data) - if l[1] == 0.0: - return NODATA - - return l[3] / l[1] - -# not sure how an array of metrics can be ingested by shatter -# so do these as 15 separate metrics. may be slower than doing all in one call -#def m_percentiles(data): -# return(np.percentile(data, [1,5,10,20,25,30,40,50,60,70,75,80,90,95,99])) - -def m_p01(data): - """ - Return the 1st percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 1)) - -def m_p05(data): - """ - Return the 5th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 5)) - -def m_p10(data): - """ - Return the 10th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 10)) - -def m_p20(data): - """ - Return the 20th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 20)) - -def m_p25(data): - """ - Return the 25th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 25)) - -def m_p30(data): - """ - Return the 30th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 30)) - -def m_p40(data): - """ - Return the 40th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 40)) - -def m_p50(data): - """ - Return the 50th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 50)) - -def m_p60(data): - """ - Return the 60th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 60)) - -def m_p70(data): - """ - Return the 70th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 70)) - -def m_p75(data): - """ - Return the 75th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 75)) - -def m_p80(data): - """ - Return the 80th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 80)) - -def m_p90(data): - """ - Return the 90th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 90)) - -def m_p95(data): - """ - Return the 95th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 95)) - -def m_p99(data): - """ - Return the 99th percentile value. - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - return(np.percentile(data, 99)) - -def m_profilearea(data): - """ - Return the profile area. - - Profile area was first described in - - Hu, Tianyu, Qin Ma, Yanjun Su, John J. Battles, Brandon M. Collins, - Scott L. Stephens, Maggi Kelly, Qinghua Guo. 2019. A simple and - integrated approach for fire severity assessment using bi-temporal - airborne LiDAR data. Int J. Appl Earth Obs Geoinformation, 78 (2019): 25-38. - - as the area under the height percentile profile or curve. They found the - metric useful to compare pre- and post-fire canopy structure at both the - individual tree and pixel scales. The implementation in CloudMetrics and - GridMetrics varies from that described in Hu et al. 2019. Heights are - normalized using the 99th percentile height instead of the maximum height - to eliminate problems related to high outliers and the area under the - percentile curve is computed directly from 1 percent slices instead of - fitting a polynomial to the percentile heights and computing the area - under the polynomial - - Parameters - ---------- - data : numpy.ndarray - Data for dimension - - Returns - ------- - float - Value for metric computed using values in data. Returns - NODATA value when metric cannot be computed - """ - - # sanity check...must have valid heights/elevations - if np.max(data) <= 0: - return NODATA - - - p = np.percentile(data, range(1, 100)) - p0 = max(np.min(data), 0.0) - - # second sanity check...99th percentile must be > 0 - if p[98] > 0.0: - # compute area under normalized percentile height curve using composite trapeziod rule - pa = p0 / p[98] - for ip in p[:97]: - pa += 2.0 * ip / p[98] - pa += 1.0 + # try returning just the graph and see if that can speed thigns up + # return graph - return pa * 0.5 - else: - return NODATA + computed_list = dask.persist(*graph, optimize_graph=True) -# not sure how this will be computed once height and cover thresholds are -# sorted out -#def m_allcover(data): -# return (data > coverthreshold).sum() / len(data) * 100.0 + def merge(x, y): + return x.merge(y, on=['xi','yi']) + merged = reduce(merge, computed_list) -Metrics = { - 'count' : Metric('count', m_count), - 'mean' : Metric('mean', m_mean), - 'mode' : Metric('mode', m_mode), - 'median' : Metric('median', m_median), - 'min' : Metric('min', m_min), - 'max' : Metric('max', m_max), - 'stddev' : Metric('stddev', m_stddev), - 'variance' : Metric('variance', m_variance), - 'cv' : Metric('cv', m_cv), - 'abovemean' : Metric('abovemean', m_abovemean), - 'abovemode' : Metric('abovemode', m_abovemode), - 'skewness' : Metric('skewness', m_skewness), - 'kurtosis' : Metric('kurtosis', m_kurtosis), - 'aad' : Metric('aad', m_aad), - 'madmedian' : Metric('madmedian', m_madmedian), - 'madmode' : Metric('madmode', m_madmode), - 'iq' : Metric('iq', m_iq), - 'crr' : Metric('crr', m_crr), - 'sqmean' : Metric('sqmean', m_sqmean), - 'cumean' : Metric('cumean', m_cumean), - 'l1' : Metric('l1', m_l1), - 'l2' : Metric('l2', m_l2), - 'l3' : Metric('l3', m_l3), - 'l4' : Metric('l4', m_l4), - 'lcv' : Metric('lcv', m_lcv), - 'lskewness' : Metric('lskewness', m_lskewness), - 'lkurtosis' : Metric('lkurtosis', m_lkurtosis), - '90m10' : Metric('90m10', m_90m10), - '95m05' : Metric('95m05', m_95m05), - 'p01' : Metric('p01', m_p01), - 'p05' : Metric('p05', m_p05), - 'p10' : Metric('p10', m_p10), - 'p20' : Metric('p20', m_p20), - 'p25' : Metric('p25', m_p25), - 'p30' : Metric('p30', m_p30), - 'p40' : Metric('p40', m_p40), - 'p50' : Metric('p50', m_p50), - 'p60' : Metric('p60', m_p60), - 'p70' : Metric('p70', m_p70), - 'p75' : Metric('p75', m_p75), - 'p80' : Metric('p80', m_p80), - 'p90' : Metric('p90', m_p90), - 'p95' : Metric('p95', m_p95), - 'p99' : Metric('p99', m_p99), -# 'allcover' : Metric('allcover', m_allcover), - 'profilearea' : Metric('profilearea', m_profilearea), -} + return merged diff --git a/src/silvimetric/resources/metrics/__init__.py b/src/silvimetric/resources/metrics/__init__.py new file mode 100644 index 0000000..c53e3e0 --- /dev/null +++ b/src/silvimetric/resources/metrics/__init__.py @@ -0,0 +1,13 @@ +from ..metric import Metric +from .percentiles import percentiles +from .l_moments import l_moments +from .stats import statistics +from .p_moments import product_moments + +#TODO make each one of these have a version with the NumberOfReturns>2 filter + +grid_metrics: dict[str, Metric] = dict(percentiles | l_moments | statistics | + product_moments) + +all_metrics: dict[str, Metric] = dict(percentiles | l_moments | statistics | + product_moments) \ No newline at end of file diff --git a/src/silvimetric/resources/metrics/aad.py b/src/silvimetric/resources/metrics/aad.py new file mode 100644 index 0000000..5ef2c6c --- /dev/null +++ b/src/silvimetric/resources/metrics/aad.py @@ -0,0 +1,24 @@ +import numpy as np +from scipy import stats +from ..metric import Metric +from .p_moments import product_moments + +def m_aad(data, *args): + mean = args[0] + return np.mean(np.absolute(data - mean)) + +def m_madmedian(data, *args): + return stats.median_abs_deviation(data) + +def m_madmean(data, *args): + return stats.median_abs_deviation(data, center=np.mean) + +def m_madmode(data): + def mode_center(data, axis): + return stats.mode(data, axis=axis).mode + return stats.median_abs_deviation(data, center=mode_center) + +aad: dict[str, Metric] = {} +aad['aad'] = Metric('aad', np.float32, m_aad, product_moments['mean']) +aad['madmedian'] = Metric('madmedian', np.float32, m_madmedian) +aad['madmode'] = Metric('madmode', np.float32, m_madmode) \ No newline at end of file diff --git a/src/silvimetric/resources/metrics/filters.py b/src/silvimetric/resources/metrics/filters.py new file mode 100644 index 0000000..51ca10c --- /dev/null +++ b/src/silvimetric/resources/metrics/filters.py @@ -0,0 +1,4 @@ +from numpy import np + +def f_2plus(data): + return data[data['HeightAboveGround'] > 2] \ No newline at end of file diff --git a/src/silvimetric/resources/metrics/l_moments.py b/src/silvimetric/resources/metrics/l_moments.py new file mode 100644 index 0000000..7d927ec --- /dev/null +++ b/src/silvimetric/resources/metrics/l_moments.py @@ -0,0 +1,142 @@ +from ..metric import Metric +import numpy as np +from .p_moments import mean +from lmoments3 import lmom_ratios +from lmoments3 import distr + +import warnings +# suppress warnings from dividing by 0, these are handled in the metric creation +warnings.filterwarnings( + action='ignore', + category=RuntimeWarning, + module='lmoments3' +) + +def lmom4(data): + n = data.count() + try: + paras = distr.gam.lmom_fit(data) + if n < 1: + return [np.nan, np.nan, np.nan, np.nan] + elif n < 2: + return [data.mean, np.nan, np.nan, np.nan] + elif n < 3: + return [*distr.gam.lmom(nmom=2, **paras), np.nan, np.nan, np.nan] + elif n < 4: + return [*distr.gam.lmom(nmom=3, **paras), np.nan, np.nan, np.nan] + return [*distr.gam.lmom(nmom=4, **paras)] + except: + return [data.mean(), np.nan, np.nan, np.nan] + +# L1 is same as mean...compute using np.mean for speed +def m_l1(data, *args): + return args[0][0] + +def m_l2(data, *args): + return args[0][1] + +def m_l3(data, *args): + return args[0][2] + +def m_l4(data, *args): + return args[0][3] + +def m_lcv(data, *args): + l: tuple[float, float, float, float] = args[0] + + try: + return l[1] / l[0] + except ZeroDivisionError as e: + return np.nan + +def m_lskewness(data, *args): + l: tuple[float, float, float, float] = args[0] + try: + return l[2] / l[1] + except ZeroDivisionError as e: + return np.nan + +def m_lkurtosis(data, *args): + l: tuple[float, float, float, float] = args[0] + try: + return l[3] / l[1] + except ZeroDivisionError as e: + return np.nan + +# intermediate metric, not intended for insertion into db +l_mom_base = Metric('lmombase', object, lmom4, []) + + +l1 = Metric('l1', np.float32, m_l1, [l_mom_base]) +l2 = Metric('l2', np.float32, m_l2, [l_mom_base]) +l3 = Metric('l3', np.float32, m_l3, [l_mom_base]) +l4 = Metric('l4', np.float32, m_l4, [l_mom_base]) +lcv = Metric('lcv', np.float32, m_lcv, [l_mom_base]) +lskewness = Metric('lskewness', np.float32, m_lskewness, + [l_mom_base]) +lkurtosis = Metric('lkurtosis', np.float32, m_lkurtosis, + [l_mom_base]) + +l_moments: dict[str, Metric] = { + 'l1': l1, + 'l2': l2, + 'l3': l3, + 'l4': l4, + 'lcv': lcv, + 'lskewness': lskewness, + 'lkurtosis': lkurtosis, +} + +# adapted from: https://xiaoganghe.github.io/python-climate-visuals/chapters/data-analytics/scipy-basic.html +# added b3 and l4 from: +# Vogel, R. M. and Fennessey, N. M.: L moment diagrams should replace product moment diagrams, +# Water Resour. Res., 29, 1745–1752, https://doi.org/10.1029/93WR00341, 1993. +# def lmom4_og(data, *args): +# # lmom4 returns the first four L-moments of data +# # data is the 1-d array +# # n is the total number of points in data, j is the j_th point +# # +# # j range in for loops starts with 1 so we need to subtract 1 for all b# equations + +# data = data.values +# n = len(data) +# # sort in descending order +# data = np.sort(data.reshape(n))[::-1] + +# b0 = args[0] +# l1: float = b0 + +# # cannot compute l-moments greater than the number of points +# if n < 2: +# l2 = np.nan +# else: +# b1 = np.array([(n - j - 1) * data[j] / n / (n - 1) +# for j in range(n)]).sum() +# l2: float = 2 * b1 - b0 + +# if n < 3: +# l3 = np.nan +# else: +# b2 = np.array([(n - j - 1) * (n - j - 2) * data[j] / n / (n - 1) / (n - 2) +# for j in range(n - 1)]).sum() +# l3: float = 6 * (b2 - b1) + b0 + +# if n < 4: +# l4 = np.nan +# else: +# b3 = np.array([(n - j - 1) * (n - j - 2) * (n - j - 3) * data[j] / n / (n - 1) / (n - 2) / (n - 3) +# for j in range(n - 2)]).sum() +# l4: float = 20 * b3 - 30 * b2 + 12 * b1 - b0 + + +# return l1, l2, l3, l4 + +# TODO compute L-moments. These are done separately because we only add +# a single element to TileDB. This is very inefficient since we have to +# compute all L-moments at once. Ideally, we would have a single metric +# function that returns an array with 7 values + +# added code to compute first 4 l-moments in lmom4.py. There is a package, +# lmoments3 that can compute the same values but I wanted to avoid the +# package as it has some IBM copyright requirements (need to include copyright +# statement with derived works) \ No newline at end of file diff --git a/src/silvimetric/resources/metrics/p_moments.py b/src/silvimetric/resources/metrics/p_moments.py new file mode 100644 index 0000000..2913c97 --- /dev/null +++ b/src/silvimetric/resources/metrics/p_moments.py @@ -0,0 +1,31 @@ +import numpy as np +from scipy.stats import moment + +from ..metric import Metric + +def m_moments(data, *args): + mean = args[0] + return moment(data, center=mean, order=[2,3,4], nan_policy='omit').tolist() + +def m_mean(data, *args): + return np.mean(data) + +def m_variance(data, *args): + return args[0][0] + +def m_skewness(data, *args): + return args[0][1] + +def m_kurtosis(data, *args): + return args[0][2] + +mean = Metric(name='mean', dtype=np.float32, method=m_mean) +moment_base = Metric(name='moment_base', dtype=object, method=m_moments, dependencies=[mean]) +variance = Metric(name='variance', dtype=np.float32, method=m_variance, dependencies=[moment_base]) +skewness = Metric(name='skewness', dtype=np.float32, method=m_skewness, dependencies=[moment_base]) +kurtosis = Metric(name='kurtosis', dtype=np.float32, method=m_kurtosis, dependencies=[moment_base]) + +product_moments: dict[str, Metric] = dict(mean=mean, + variance=variance, + skewness=skewness, + kurtosis=kurtosis) \ No newline at end of file diff --git a/src/silvimetric/resources/metrics/percentiles.py b/src/silvimetric/resources/metrics/percentiles.py new file mode 100644 index 0000000..0ae46dc --- /dev/null +++ b/src/silvimetric/resources/metrics/percentiles.py @@ -0,0 +1,78 @@ +import numpy as np +import pandas as pd +from ..metric import Metric + +def percentile_base(data: pd.DataFrame): + return np.percentile(data, range(100)).tolist() + +def m_p01(data: pd.DataFrame, *args): + return args[0][1] + +def m_p05(data: pd.DataFrame, *args): + return args[0][5] + +def m_p10(data: pd.DataFrame, *args): + return args[0][10] + +def m_p20(data: pd.DataFrame, *args): + return args[0][20] + +def m_p25(data: pd.DataFrame, *args): + return args[0][25] + +def m_p30(data: pd.DataFrame, *args): + return args[0][30] + +def m_p40(data: pd.DataFrame, *args): + return args[0][40] + +def m_p50(data: pd.DataFrame, *args): + return args[0][50] + +def m_p60(data: pd.DataFrame, *args): + return args[0][60] + +def m_p70(data: pd.DataFrame, *args): + return args[0][70] + +def m_p75(data: pd.DataFrame, *args): + return args[0][75] + +def m_p80(data: pd.DataFrame, *args): + return args[0][80] + +def m_p90(data: pd.DataFrame, *args): + return args[0][90] + +def m_p95(data: pd.DataFrame, *args): + return args[0][95] + +def m_p99(data: pd.DataFrame, *args): + return args[0][99] + +def m_90m10(data, *args): + return args[0][90] - args[0][10] + +def m_95m05(data, *args): + return args[0][95] - args[0][5] + +pct_base = Metric('pct_base', object, percentile_base) + +percentiles: dict[str, Metric] = { } +percentiles['p01'] = Metric('p01', np.float32, m_p01, [pct_base]) +percentiles['p05'] = Metric('p05', np.float32, m_p05, [pct_base]) +percentiles['p10'] = Metric('p10', np.float32, m_p10, [pct_base]) +percentiles['p20'] = Metric('p20', np.float32, m_p20, [pct_base]) +percentiles['p25'] = Metric('p25', np.float32, m_p25, [pct_base]) +percentiles['p30'] = Metric('p30', np.float32, m_p30, [pct_base]) +percentiles['p40'] = Metric('p40', np.float32, m_p40, [pct_base]) +percentiles['p50'] = Metric('p50', np.float32, m_p50, [pct_base]) +percentiles['p60'] = Metric('p60', np.float32, m_p60, [pct_base]) +percentiles['p70'] = Metric('p70', np.float32, m_p70, [pct_base]) +percentiles['p75'] = Metric('p75', np.float32, m_p75, [pct_base]) +percentiles['p80'] = Metric('p80', np.float32, m_p80, [pct_base]) +percentiles['p90'] = Metric('p90', np.float32, m_p90, [pct_base]) +percentiles['p95'] = Metric('p95', np.float32, m_p95, [pct_base]) +percentiles['p99'] = Metric('p99', np.float32, m_p99, [pct_base]) +percentiles['90m10'] = Metric('90m10', np.float32, m_90m10, [pct_base]) +percentiles['95m05'] = Metric('95m05', np.float32, m_95m05, [pct_base]) \ No newline at end of file diff --git a/src/silvimetric/resources/metrics/stats.py b/src/silvimetric/resources/metrics/stats.py new file mode 100644 index 0000000..98cc968 --- /dev/null +++ b/src/silvimetric/resources/metrics/stats.py @@ -0,0 +1,128 @@ +import numpy as np +from scipy import stats +from ..metric import Metric +from .p_moments import mean +from .percentiles import pct_base + +import warnings +# suppress warnings from dividing by 0, these are handled in the metric creation +warnings.filterwarnings( + action='ignore', + category=RuntimeWarning, + module='lmoments3' +) + +def m_mode(data): + u, c = np.unique(data, return_counts=True) + i = np.where(c == c.max()) + v = u[i[0][0]] + return v + +def m_median(data, *args): + return np.median(data) + +def m_min(data, *args): + return np.min(data) + +def m_max(data, *args): + return np.max(data) + +def m_stddev(data, *args): + return np.std(data) + +def m_cv(data, *args): + stddev, mean = args + if mean == 0: + return np.nan + return stddev / mean + +# TODO check performance of other methods +def m_abovemean(data, *args): + mean = args[0] + l = len(data) + if l == 0: + return np.nan + return (data > mean).sum() / l + +# TODO check performance of other methods +def m_abovemode(data, *args): + mode = args[0] + l = len(data) + if l == 0: + return np.nan + return (data > mode).sum() / l + +def m_iq(data): + return stats.iqr(data) + +def m_crr(data, *args): + mean, minimum, maximum = args + den = (maximum - minimum) + if den == 0: + return np.nan + return (mean - minimum) / den + +def m_sqmean(data): + return np.sqrt(np.mean(np.square(data))) + +def m_cumean(data): + return np.cbrt(np.mean(np.power(np.absolute(data), 3))) + +def m_profilearea(data, *args): + # sanity check...must have valid heights/elevations + dmax, dmin , p = args + if dmax <= 0: + return -9999.0 + + # p = np.percentile(data, range(1, 100)) + p0 = max(dmin, 0.0) + + # second sanity check...99th percentile must be > 0 + p99 = p[99] + if p99 > 0.0: + # compute area under normalized percentile height curve using composite trapeziod rule + pcts = np.array(p[:98]) + areas = pcts * 2 / p99 + pa = p0/p99 + areas.sum() + 1 + + return pa * 0.5 + else: + return -9999.0 + +# # TODO example for cover using all returns and a height threshold +# # the threshold must be a parameter and not hardcoded +# def m_cover(data): +# threshold = 2 +# return (data > threshold).sum() / len(data) + +mode = Metric('mode', np.float32, m_mode) +median = Metric('median', np.float32, m_median) +# TODO better names? +sm_min = Metric('min', np.float32, m_min) +sm_max = Metric('max', np.float32, m_max) +stddev = Metric('stddev', np.float32, m_stddev) +cv = Metric('cv', np.float32, m_cv, [ mean, stddev ]) +abovemean = Metric('abovemean', np.float32, m_abovemean, [ mean ]) +abovemode = Metric('abovemode', np.float32, m_abovemode, [ mode ]) +iq = Metric('iq', np.float32, m_iq) +crr = Metric('crr', np.float32, m_crr, [ mean, sm_min, sm_max ]) +sqmean = Metric('sqmean', np.float32, m_sqmean) +cumean = Metric('cumean', np.float32, m_cumean) +profilearea = Metric('profilearea', np.float32, m_profilearea, [ sm_max, sm_min, pct_base ]) + +statistics: dict[str, Metric] = dict( + mode=mode, + median=median, + min=sm_min, + max=sm_max, + stddev=stddev, + cv=cv, + abovemean=abovemean, + abovemode=abovemode, + iq=iq, + crr=crr, + sqmean=sqmean, + cumean=cumean, + profilearea=profilearea, +) +# statistics['cover'] = Metric('cover', np.float32, m_cover) diff --git a/src/silvimetric/resources/storage.py b/src/silvimetric/resources/storage.py index 798349b..60eb3eb 100644 --- a/src/silvimetric/resources/storage.py +++ b/src/silvimetric/resources/storage.py @@ -1,25 +1,23 @@ +import os import tiledb import numpy as np from datetime import datetime import pathlib import contextlib import json -from typing import Any +import urllib +from typing import Generator from math import floor -from .config import StorageConfig +from .config import StorageConfig, ShatterConfig from .metric import Metric, Attribute -from ..resources import Bounds +from .bounds import Bounds class Storage: """ Handles storage of shattered data in a TileDB Database. """ def __init__(self, config: StorageConfig, ctx:tiledb.Ctx=None): - # if not ctx: - # self.ctx = tiledb.default_ctx() - # else: - # self.ctx = ctx if not tiledb.object_type(config.tdb_dir) == "array": raise Exception(f"Given database directory '{config.tdb_dir}' does not exist") @@ -61,6 +59,9 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): if not ctx: ctx = tiledb.default_ctx() + # adjust cell bounds if necessary + config.root.adjust_to_cell_lines(config.resolution) + # dims = { d['name']: d['dtype'] for d in pdal.dimensions if d['name'] in config.attrs } xi = floor((config.root.maxx - config.root.minx) / float(config.resolution)) yi = floor((config.root.maxy - config.root.miny) / float(config.resolution)) @@ -72,13 +73,22 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): count_att = tiledb.Attr(name="count", dtype=np.int32) dim_atts = [attr.schema() for attr in config.attrs] - metric_atts = [m.schema(a) for m in config.metrics for a in config.attrs] + metric_atts = [m.schema(a) for m in config.metrics for a in config.attrs if a in m.attributes or not m.attributes] + + # Check that all attributes required for metric usage are available + att_list = [a.name for a in config.attrs] + required_atts = [d.name for m in config.metrics for d in m.dependencies + if isinstance(d, Attribute)] + for ra in required_atts: + if ra not in att_list: + raise ValueError(f'Missing required dependency, {ra}.') # allows_duplicates lets us insert multiple values into each cell, # with each value representing a set of values from a shatter process # https://docs.tiledb.com/main/how-to/performance/performance-tips/summary-of-factors#allows-duplicates schema = tiledb.ArraySchema(domain=domain, sparse=True, - attrs=[count_att, *dim_atts, *metric_atts], allows_duplicates=True) + attrs=[count_att, *dim_atts, *metric_atts], allows_duplicates=True, + capacity=1000) schema.check() tiledb.SparseArray.create(config.tdb_dir, schema) @@ -87,23 +97,17 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): a.meta['config'] = meta s = Storage(config, ctx) + s.saveConfig() return s @staticmethod def from_db(tdb_dir: str): """ - Create Storage class from previously created storage path - - Parameters - ---------- - tdb_dir : str - Path to storage directory + Create Storage object from information stored in a database. - Returns - ------- - Storage - Return the generated storage + :param tdb_dir: TileDB database directory. + :return: Returns the derived storage. """ with tiledb.open(tdb_dir, 'r') as a: s = a.meta['config'] @@ -114,19 +118,15 @@ def from_db(tdb_dir: str): def saveConfig(self) -> None: """ Save StorageConfig to the Database - """ - with self.open('w') as a: - a.meta['config'] = str(self.config) + with self.open('w') as w: + w.meta['config'] = str(self.config) def getConfig(self) -> StorageConfig: """ - Get the StorageConfig currently in use by the Storage + Get the StorageConfig currently in use by the Storage. - Returns - ------- - StorageConfig - StorageConfig object + :return: StorageConfig representing this object. """ with self.open('r') as a: s = a.meta['config'] @@ -134,95 +134,79 @@ def getConfig(self) -> StorageConfig: return config - def getMetadata(self, key: str, default=None) -> str: + def getMetadata(self, key: str, timestamp: int) -> str: """ - Return metadata at given key - - Parameters - ---------- - key : str - Metadata key + Return metadata at given key. - Returns - ------- - str - Metadata value found in storage + :param key: Key to look for in metadata. + :param timestamp: Time stamp for querying database. + :return: Metadata value found in storage. """ - with self.open('r') as r: - try: - val = r.meta[key] - except KeyError as e: - if default is not None: - return default - raise(e) + + with self.open('r', (timestamp, timestamp)) as r: + val = r.meta[f'{key}'] return val - def saveMetadata(self, key: str, data: Any) -> None: + + def saveMetadata(self, key: str, data: str, timestamp: int) -> None: """ - Save metadata to storage + Save metadata to storage. - Parameters - ---------- - key : str - Metadata key - data : any - Metadata value. Must be translatable to and from string. + :param key: Metadata key to save to. + :param data: Data to save to metadata. """ - with self.open('w') as w: - w.meta[key] = data + with self.open('w', (timestamp, timestamp)) as w: + w.meta[f'{key}'] = data + return def getAttributes(self) -> list[Attribute]: """ - Return list of attribute names from storage config + Find list of attribute names from storage config. - Returns - ------- - list[str] - List of attribute names + :return: List of attribute names. """ return self.getConfig().attrs def getMetrics(self) -> list[Metric]: """ - Return List of metric names from storage config + Find List of metric names from storage config - Returns - ------- - list[str] - List of metric names + :return: List of metric names. """ return self.getConfig().metrics + def getDerivedNames(self) -> list[str]: + # if no attributes are set in the metric, use all + return [m.entry_name(a.name) for m in self.config.metrics + for a in self.config.attrs if not m.attributes + or a.name in [ma.name for ma in m.attributes]] + @contextlib.contextmanager - def open(self, mode:str='r', timestamp=None) -> tiledb.SparseArray: + def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, None, None]: + """ + Open stream for TileDB database in given mode and at given timestamp. + + :param mode: Mode to open TileDB stream in. Valid options are + 'w', 'r', 'm', 'd'., defaults to 'r'. + :param timestamp: Timestamp to open database at., defaults to None. + :raises Exception: Incorrect Mode was given, only valid modes are 'w' and 'r'. + :raises Exception: Path exists and is not a TileDB array. + :raises Exception: Path does not exist. + :yield: TileDB array context manager. """ - Open either a read or write stream for TileDB database - Parameters - ---------- - mode : str, optional - Stream mode. Valid options are 'r' and 'w', by default 'r' + # tiledb and dask have bad interaction with opening an array if + # other threads present - Raises - ------ - Exception - Incorrect Mode was given, only valid modes are 'w' and 'r' - Exception - Path exists and is not a TileDB array - Exception - Path does not exist - """ if tiledb.object_type(self.config.tdb_dir) == "array": - if mode == 'w': - tdb = tiledb.open(self.config.tdb_dir, 'w', timestamp=timestamp) - elif mode == 'r': - tdb = tiledb.open(self.config.tdb_dir, 'r', timestamp=timestamp) + if mode in ['w', 'r', 'd', 'm']: + tdb = tiledb.open(self.config.tdb_dir, mode, timestamp=timestamp) else: raise Exception(f"Given open mode '{mode}' is not valid") elif pathlib.Path(self.config.tdb_dir).exists(): raise Exception(f"Path {self.config.tdb_dir} already exists and is not" + - " initialized for TileDB access.") + " initialized for TileDB access.") else: raise Exception(f"Path {self.config.tdb_dir} does not exist") @@ -231,99 +215,143 @@ def open(self, mode:str='r', timestamp=None) -> tiledb.SparseArray: finally: tdb.close() + def reserve_time_slot(self) -> int: + """ + Increment time slot in database and reserve that spot for a new + shatter process. + + :param config: Shatter config will be written as metadata to reserve + time slot. + + :return: Time slot. + """ + with tiledb.open(self.config.tdb_dir, 'r') as r: + latest = json.loads(r.meta['config']) + + time = latest['next_time_slot'] + latest['next_time_slot'] = time + 1 + + with tiledb.open(self.config.tdb_dir, 'w') as w: + w.meta['config'] = json.dumps(latest) + + return time + def get_history(self, start_time: datetime, end_time: datetime, bounds: Bounds, name:str=None): + """ + Retrieve history of the database at current point in time. - from ..resources import ShatterConfig + :param start_time: Query parameter, starting datetime of process. + :param end_time: Query parameter, ending datetime of process. + :param bounds: Query parameter, bounds to query by. + :param name: Query paramter, shatter process uuid., by default None + :return: Returns list of array fragments that meet query parameters. + """ af = tiledb.array_fragments(self.config.tdb_dir, True) m = [ ] for idx in range(len(af)): - # if processes are too short, begin and end times can be the same - # so it's necessary to adjust time bounds so we can grab information + time_range = af[idx].timestamp_range + # all shatter processes should be input as a point in time, eg (1,1) + if isinstance(time_range, tuple): + time_range = time_range[0] - if idx == 0: - begin = 0 + try: + s_str = self.getMetadata('shatter', time_range) + except KeyError: + continue + s = ShatterConfig.from_string(s_str) + if s.bounds.disjoint(bounds): + continue + + # filter name + if name is not None and name != s.name: + continue + + # filter dates + if isinstance(s.date, tuple) and len(s.date) == 2: + if s.date[1] < start_time or s.date[0] > end_time: + continue + elif isinstance(s.date, tuple) and len(s.date) == 1: + if s.date[0] < start_time or s.date[0] > end_time: + continue else: - begin = af[idx-1].timestamp_range[1] - - end = af[idx].timestamp_range[1] - - with self.open('r', (begin, end)) as r: - try: - if not bool(r.meta['shatter']): - continue - - s = ShatterConfig.from_string(r.meta['shatter']) - - # filter bounds - if s.bounds.disjoint(bounds): - continue - - # filter name - if name is not None and name != s.name: - continue - - # filter dates - if isinstance(s.date, tuple) and len(s.date) == 2: - if s.date[1] < start_time or s.date[0] > end_time: - continue - elif isinstance(s.date, tuple) and len(s.date) == 1: - if s.date[0] < start_time or s.date[0] > end_time: - continue - else: - if s.date < start_time or s.date > end_time: - continue - - m.append(json.loads(r.meta['shatter'])) - except KeyError: + if s.date < start_time or s.date > end_time: continue + m.append(s.to_json()) + return m - #TODO what are we reading? queries are probably going to be specific - def read(self, xs: np.ndarray, ys: np.ndarray) -> np.ndarray: + def mbrs(self, proc_num: int): """ - Read from the Database - Parameters - ---------- - xs : np.ndarray - X index - ys : np.ndarray - Y index + Get minimum bounding rectangle of a given shatter process. If this process + has been finished and consolidated the mbr will be much less granulated + than if the fragments are still intact. Mbrs are represented as tuples + in the form of ((minx, maxx), (miny, maxy)) - Returns - ------- - np.ndarray - Items found at the indicated cell + :param proc_num: Process number or time slot of the shatter process. + :return: Returns mbrs that match the given process number. """ - with self.open('r') as tdb: - data = tdb[xs, ys] - return data + af_all = self.get_fragments_by_time(proc_num) + mbrs_list = tuple(mbrs for af in af_all for mbrs in af.mbrs) + mbrs = tuple(tuple(tuple(a.item() for a in mb) for mb in m) for m in mbrs_list) + return mbrs - def write(self, xs: np.ndarray, ys: np.ndarray, data: np.ndarray) -> None: + def get_fragments_by_time(self, proc_num: int) -> list[tiledb.FragmentInfo]: """ - Write data to TileDB database + Get TileDB array fragments from the time slot specified. - Parameters - ---------- - xs : np.ndarray - X cell indices - ys : np.ndarray - Y cell indices - data : np.ndarray - Numpy object of data values for attributes in each index pairing + :param proc_num: Requested time slot. + :return: Array fragments from time slot. + """ + af = tiledb.array_fragments(self.config.tdb_dir, include_mbrs=True) + return [a for a in af if a.timestamp_range == (proc_num, proc_num)] + + def delete(self, proc_num: int) -> ShatterConfig: + """ + Delete Shatter process and all associated data from database. + + :param proc_num: Shatter process time slot + :return: Config of deleted Shatter process """ - with self.open('w') as tdb: - # data = {k: v.astype(np.dtype(v.dtype)) for k,v in data.items()} + self.config.log.debug(f'Deleting time slot {proc_num}...') + with self.open('r', (proc_num, proc_num)) as r: + sh_cfg: ShatterConfig = ShatterConfig.from_string(r.meta['shatter']) + sh_cfg.mbr = () + sh_cfg.finished = False + + self.config.log.debug('Deleting fragments...') + tiledb.Array.delete_fragments(self.config.tdb_dir, + timestamp_start=proc_num, timestamp_end=proc_num) + self.config.log.debug('Rewriting config.') + with self.open('w', (proc_num, proc_num)) as w: + w.meta['shatter'] = json.dumps(sh_cfg.to_json()) + return sh_cfg + + def consolidate_shatter(self, proc_num: int, retries=0) -> None: + """ + Consolidate the fragments from a shatter process into one fragment. + This makes the database perform better, but reduces the granularity of + time traveling. - # if self.config.app.debug: - # tiledb.stats_reset() - # tiledb.stats_enable() - tdb[xs, ys] = data + :param proc_num: Time slot associated with shatter process. + """ + try: + afs = self.get_fragments_by_time(proc_num) + uris = [ os.path.split(urllib.parse.urlparse(f.uri).path)[-1] for f in afs ] + tiledb.consolidate(self.config.tdb_dir, fragment_uris=uris) + c = tiledb.Config({"sm.vacuum.mode": "fragments"}) + tiledb.vacuum(self.config.tdb_dir, c) + self.config.log.info(f"Consolidated time slot {proc_num}.") + except Exception as e: + if retries >= 3: + self.config.log.warning("Failed to consolidate time slot " + f"{proc_num} {retries} time(s). Stopping.") + raise e + self.config.log.warning("Failed to consolidate time slot " + f"{proc_num} {retries+1} time. Retrying...") + self.consolidate_shatter(proc_num, retries+1) - # if self.config.app.debug: - # tiledb.stats_dump() - # tiledb.stats_reset() diff --git a/tests/conftest.py b/tests/conftest.py index 306c52a..9849d70 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,181 +1,136 @@ import pytest import os -import dask import pdal -from uuid import uuid4 +import copy + from datetime import datetime +from typing import Generator -from silvimetric.resources import Extents, Bounds, Metrics, Attribute, Storage, Log, Data -from silvimetric.resources.config import ShatterConfig, StorageConfig, ApplicationConfig +from silvimetric import Extents, Bounds, Attribute, Storage +from silvimetric import grid_metrics +from silvimetric import Log, Metric, ShatterConfig, StorageConfig +from silvimetric import ApplicationConfig, ExtractConfig from silvimetric import __version__ as svversion - -@pytest.fixture(scope="session", autouse=True) -def configure_dask(): - dask.config.set(scheduler="single-threaded") - -@pytest.fixture(scope='function') -def tdb_filepath(tmp_path_factory) -> str: - path = tmp_path_factory.mktemp("test_tdb") - yield os.path.abspath(path) - -@pytest.fixture(scope="function") -def threaded_dask(): - dask.config.set(scheduler="threads") +# pull together fixtures +pytest_plugins=[ + 'fixtures.shatter_fixtures', 'fixtures.extract_fixtures', + 'fixtures.command_fixtures', 'fixtures.chunk_fixtures', + 'fixtures.western_fixtures', 'fixtures.data_fixtures', + 'fixtures.cli_fixtures', 'fixtures.fusion_fixtures', + 'fixtures.metric_fixtures', 'fixtures.dask_fixtures' +] @pytest.fixture(scope='function') -def storage_config(tdb_filepath, bounds, resolution, crs, attrs, metrics): - log = Log(20) - yield StorageConfig(tdb_dir = tdb_filepath, - log = log, - crs = crs, - root = bounds, - resolution = resolution, - attrs = attrs, - metrics = metrics, - version = svversion) - -@pytest.fixture(scope="function") -def storage(storage_config) -> Storage: - yield Storage.create(storage_config) +def tdb_filepath(storage_config) -> Generator[str, None, None]: + yield storage_config.tdb_dir @pytest.fixture(scope='function') -def app_config(tdb_filepath, debug=True): +def app_config(tdb_filepath, debug=True) -> Generator[ApplicationConfig, None, None]: log = Log(20) # INFO app = ApplicationConfig(tdb_dir = tdb_filepath, log = log) yield app @pytest.fixture(scope='function') -def shatter_config(tdb_filepath, copc_filepath, storage_config, bounds, app_config, storage, date): - log = Log(20) # INFO - s = ShatterConfig(tdb_dir = tdb_filepath, - log = log, - filename = copc_filepath, - attrs = storage_config.attrs, - metrics = storage_config.metrics, - bounds=bounds, - debug = True, - date=date) - - yield s +def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> Generator[StorageConfig, None, None]: + path = tmp_path_factory.mktemp("test_tdb") + p = os.path.abspath(path) + log = Log('DEBUG') -@pytest.fixture(scope='function') -def uneven_storage_config(tdb_filepath, bounds, crs, attrs, metrics): - log = Log(20) - yield StorageConfig(tdb_dir = tdb_filepath, + sc = StorageConfig(tdb_dir = p, log = log, crs = crs, root = bounds, - resolution = 7, + resolution = resolution, attrs = attrs, metrics = metrics, version = svversion) + Storage.create(sc) + yield sc @pytest.fixture(scope='function') -def uneven_storage(uneven_storage_config): - yield Storage.create(uneven_storage_config) +def storage(storage_config): + yield Storage.from_db(storage_config.tdb_dir) @pytest.fixture(scope='function') -def uneven_shatter_config(tdb_filepath, copc_filepath, uneven_storage_config, storage, date): - log = Log(20) # INFO - s = ShatterConfig(tdb_dir = tdb_filepath, +def shatter_config(copc_filepath, storage_config, bounds, date) -> Generator[ShatterConfig, None, None]: + log = Log('INFO') # INFO + s = ShatterConfig(tdb_dir = storage_config.tdb_dir, log = log, filename = copc_filepath, - attrs = uneven_storage_config.attrs, - metrics = uneven_storage_config.metrics, + attrs = storage_config.attrs, + metrics = storage_config.metrics, + bounds = bounds, debug = True, - date=date) - yield s + date = date, tile_size=10) -@pytest.fixture(scope="function") -def s3_bucket(): - yield "silvimetric" + yield s @pytest.fixture(scope='function') -def s3_uri(s3_bucket): - uuid = uuid4() - yield f"s3://{s3_bucket}/test_silvimetric/{uuid}" +def extract_config(tif_filepath, metrics, shatter_config, extract_attrs): + from silvimetric.commands import shatter + tdb_dir = shatter_config.tdb_dir + shatter.shatter(shatter_config) + log = Log(20) + c = ExtractConfig(tdb_dir = tdb_dir, + log = log, + out_dir = tif_filepath, + attrs = extract_attrs, + metrics = metrics) + yield c -@pytest.fixture(scope="function") -def s3_storage_config(s3_uri, bounds, resolution, crs, attrs, metrics): - yield StorageConfig(bounds, crs, resolution, attrs, metrics, - svversion, tdb_dir=s3_uri) @pytest.fixture(scope='function') -def s3_storage(s3_storage_config): - import subprocess - yield Storage.create(s3_storage_config) - subprocess.call(["aws", "s3", "rm", "--recursive", s3_storage_config.tdb_dir]) - -@pytest.fixture(scope="function") -def s3_shatter_config(s3_storage, copc_filepath, attrs, metrics, date): - config = s3_storage.config - yield ShatterConfig(filename=copc_filepath, attrs=attrs, metrics=metrics, - debug=True, tdb_dir=config.tdb_dir, date=date) - -@pytest.fixture(scope='session') -def metrics(): - yield [Metrics['mean'], Metrics['median']] - -@pytest.fixture(scope='session') -def copc_filepath() -> str: - path = os.path.join(os.path.dirname(__file__), "data", - "test_data.copc.laz") - assert os.path.exists(path) - yield os.path.abspath(path) +def metrics() -> Generator[list[Metric], None, None]: + yield [copy.deepcopy(grid_metrics['mean']), copy.deepcopy(grid_metrics['median'])] @pytest.fixture(scope='function') -def copc_data(copc_filepath, storage_config) -> Data: - d = Data(copc_filepath, storage_config) - yield d - -@pytest.fixture(scope='class') -def bounds(minx, maxx, miny, maxy) -> Bounds: +def bounds(minx, maxx, miny, maxy) -> Generator[Bounds, None, None]: b = Bounds(minx, miny, maxx, maxy) yield b -@pytest.fixture(scope='class') -def extents(resolution, bounds) -> Extents: +@pytest.fixture(scope='function') +def extents(resolution, bounds) -> Generator[Extents, None, None]: yield Extents(bounds,resolution,bounds) -@pytest.fixture(scope="session") -def attrs(dims) -> list[str]: +@pytest.fixture(scope="function") +def attrs(dims) -> Generator[list[Attribute], None, None]: yield [Attribute(a, dims[a]) for a in ['Z', 'NumberOfReturns', 'ReturnNumber', 'Intensity']] @pytest.fixture(scope="session") -def dims(): +def dims() -> Generator[dict, None, None]: yield { d['name']: d['dtype'] for d in pdal.dimensions } -@pytest.fixture(scope='class') -def resolution() -> int: +@pytest.fixture(scope='session') +def resolution() -> Generator[int, None, None]: yield 30 -@pytest.fixture(scope='class') -def test_point_count() -> int: +@pytest.fixture(scope='session') +def test_point_count() -> Generator[int, None, None]: yield 90000 -@pytest.fixture(scope='class') -def minx() -> float: +@pytest.fixture(scope='session') +def minx() -> Generator[float, None, None]: yield 300 -@pytest.fixture(scope='class') -def miny() -> float: +@pytest.fixture(scope='session') +def miny() -> Generator[float, None, None]: yield 300 -@pytest.fixture(scope='class') -def maxx() -> float: +@pytest.fixture(scope='session') +def maxx() -> Generator[float, None, None]: yield 600 -@pytest.fixture(scope='class') -def maxy() -> float: +@pytest.fixture(scope='session') +def maxy() -> Generator[float, None, None]: yield 600 -@pytest.fixture(scope="class") -def crs(): +@pytest.fixture(scope='session') +def crs() -> Generator[str, None, None]: yield "EPSG:5070" -@pytest.fixture(scope='class') -def date(): +@pytest.fixture(scope='session') +def date() -> Generator[datetime, None, None]: yield datetime(2011, 1, 1) \ No newline at end of file diff --git a/tests/create_test_data.py b/tests/create_test_data.py index 56b9304..57b4a0a 100644 --- a/tests/create_test_data.py +++ b/tests/create_test_data.py @@ -1,6 +1,6 @@ import pdal import numpy as np -from math import sqrt, ceil +from math import sqrt, floor # Create a pointcloud dataset that follows expected paramters for data testing @@ -28,10 +28,10 @@ diff_maker = 0 x_pos = np.arange(minx, maxx, interval, dtype=np.float32) -y_pos = np.arange(maxy, miny, -1*interval, dtype=np.float32) +y_pos = np.arange(miny, maxy, interval, dtype=np.float32) # positions = pos[np.where(pos % cell_size != 0)] -alg = lambda y: ceil(y/cell_size) + diff_maker +alg = lambda y: floor(y/cell_size) + diff_maker data = np.array([(x, y, alg(y), alg(y), alg(y), alg(y)) for x in x_pos for y in y_pos], diff --git a/tests/data/df_sample b/tests/data/df_sample new file mode 100644 index 0000000000000000000000000000000000000000..dad54c61faf32f5456eb57dd54c61cb59b4466e8 GIT binary patch literal 24569 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

l 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

_D`eh4bs1ohV^B&QyTD#aY{F2u+KIJ&_PxD;Nl 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�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≤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 Generator[List[Extents], None, None]: + yield list(extents.chunk(copc_data, 1)) + +@pytest.fixture(scope='function') +def unfiltered(extents: Extents) -> Generator[List[Extents], None, None]: + yield list(extents.get_leaf_children(30)) + +@pytest.fixture(scope='session') +def copc_filepath() -> Generator[str, None, None]: + path = os.path.join(os.path.dirname(__file__), "..", "data", + "test_data.copc.laz") + assert os.path.exists(path) + yield os.path.abspath(path) + +@pytest.fixture(scope='function') +def copc_data(copc_filepath, storage_config) -> Generator[Data, None, None]: + d = Data(copc_filepath, storage_config) + yield d \ No newline at end of file diff --git a/tests/fixtures/cli_fixtures.py b/tests/fixtures/cli_fixtures.py new file mode 100644 index 0000000..d4514e1 --- /dev/null +++ b/tests/fixtures/cli_fixtures.py @@ -0,0 +1,15 @@ +import pytest +from typing import Generator +from click.testing import CliRunner + +from silvimetric.commands import shatter + +@pytest.fixture(scope='session') +def runner(): + return CliRunner() + +@pytest.fixture +def pre_shatter(shatter_config) -> Generator[int, None, None]: + shatter_config.tile_size=1 + shatter_config.mbr = (((0,4), (0,4)),) + yield shatter.shatter(shatter_config) \ No newline at end of file diff --git a/tests/fixtures/command_fixtures.py b/tests/fixtures/command_fixtures.py new file mode 100644 index 0000000..9ef5959 --- /dev/null +++ b/tests/fixtures/command_fixtures.py @@ -0,0 +1,36 @@ +import uuid +import pytest + +from time import sleep +from typing import List, Generator +from datetime import datetime + +from silvimetric.commands import shatter +from silvimetric import ShatterConfig + +@pytest.fixture(scope='function') +def config_split(shatter_config: ShatterConfig) -> Generator[List[ShatterConfig], None, None]: + sc = shatter_config + config_split = [] + day = 1 + for b in sc.bounds.bisect(): + day += 1 + date = datetime(2011, 1, day) + + config_split.append(ShatterConfig( + filename=sc.filename, + date=date, + attrs=sc.attrs, + metrics=sc.metrics, + bounds=b, + name=uuid.uuid4(), + tile_size=4, + tdb_dir=sc.tdb_dir, + log=sc.log + )) + + for s in config_split: + shatter.shatter(s) + sleep(1) + + yield config_split \ No newline at end of file diff --git a/tests/fixtures/dask_fixtures.py b/tests/fixtures/dask_fixtures.py new file mode 100644 index 0000000..0594744 --- /dev/null +++ b/tests/fixtures/dask_fixtures.py @@ -0,0 +1,40 @@ +import pytest +import os +from typing import Generator +import dask + +@pytest.fixture(scope="session", autouse=True) +def configure_dask() -> None: + dask.config.set(scheduler="single-threaded") + +@pytest.fixture(scope="function") +def threaded_dask() -> Generator[None, None, None]: + dask.config.set(scheduler="threads") + + yield None + dask.config.set(scheduler="single-threaded") + +@pytest.fixture(scope="function") +def process_dask() -> Generator[None, None, None]: + dask.config.set(scheduler="processes") + + yield None + dask.config.set(scheduler="single-threaded") + +@pytest.fixture(scope='function') +def dask_proc_client(process_dask) -> Generator[None, None, None]: + client = dask.distributed.Client() + dask.config.set({'distributed.client': client}) + + yield None + dask.config.set({'distributed.client': None}) + client.close() + +@pytest.fixture(scope='function') +def dask_thread_client(threaded_dask) -> Generator[None, None, None]: + client = dask.distributed.Client() + dask.config.set({'distributed.client': client}) + + yield None + dask.config.set({'distributed.client': None}) + client.close() \ No newline at end of file diff --git a/tests/fixtures/data_fixtures.py b/tests/fixtures/data_fixtures.py new file mode 100644 index 0000000..78c0c9a --- /dev/null +++ b/tests/fixtures/data_fixtures.py @@ -0,0 +1,28 @@ +import pytest +import os +from typing import Generator + +@pytest.fixture(scope='session') +def no_cell_line_pc() -> Generator[int, None, None]: + yield 84100 + +@pytest.fixture(scope='session') +def no_cell_line_path() -> Generator[str, None, None]: + path = os.path.join(os.path.dirname(__file__), "..", "data", + "test_data_2.copc.laz") + assert os.path.exists(path) + yield os.path.abspath(path) + +@pytest.fixture(scope='session') +def no_cell_line_pipeline() -> Generator[str, None, None]: + path = os.path.join(os.path.dirname(__file__), "..", "data", + "test_data_2.json") + assert os.path.exists(path) + yield os.path.abspath(path) + +@pytest.fixture(scope='session') +def autzen_filepath() -> Generator[str, None, None]: + path = os.path.join(os.path.dirname(__file__), "..", "data", + "autzen-small.copc.laz") + assert os.path.exists(path) + yield os.path.abspath(path) \ No newline at end of file diff --git a/tests/fixtures/extract_fixtures.py b/tests/fixtures/extract_fixtures.py new file mode 100644 index 0000000..1104d42 --- /dev/null +++ b/tests/fixtures/extract_fixtures.py @@ -0,0 +1,39 @@ +import pytest +import os +import copy +import uuid +from typing import Generator + +from silvimetric.commands.shatter import shatter +from silvimetric import Attribute, ExtractConfig, Log, Bounds + +@pytest.fixture(scope='function') +def tif_filepath(tmp_path_factory) -> Generator[str, None, None]: + path = tmp_path_factory.mktemp("test_tifs") + yield os.path.abspath(path) + +@pytest.fixture(scope='function') +def extract_attrs(dims)->Generator[list[str], None, None]: + yield [Attribute('Z', dtype=dims['Z']), Attribute('Intensity', dtype=dims['Intensity'])] + + +@pytest.fixture(scope='function') +def multivalue_config(tif_filepath, metric_shatter_config): + + shatter(metric_shatter_config) + + # reset config + second_config = copy.deepcopy(metric_shatter_config) + second_config.name = uuid.uuid4() + second_config.bounds = Bounds(300,300,450,450) + second_config.point_count = 0 + second_config.time_slot = 0 + second_config.mbr = () + + shatter(second_config) + log = Log(20) + tdb_dir = metric_shatter_config.tdb_dir + c = ExtractConfig(tdb_dir = tdb_dir, + log = log, + out_dir = tif_filepath) + yield c diff --git a/tests/fixtures/fake_metrics.py b/tests/fixtures/fake_metrics.py new file mode 100644 index 0000000..05fd4bb --- /dev/null +++ b/tests/fixtures/fake_metrics.py @@ -0,0 +1,13 @@ +import numpy as np +from silvimetric import Metric + +def count(data): + return data.count() + +def has_data(data): + return data.any() + +def metrics() -> list[Metric]: + m1 = Metric('count', np.float32, count) + m2 = Metric('exists', '?', has_data) + return [m1, m2] \ No newline at end of file diff --git a/tests/fixtures/fusion_fixtures.py b/tests/fixtures/fusion_fixtures.py new file mode 100644 index 0000000..5f61c16 --- /dev/null +++ b/tests/fixtures/fusion_fixtures.py @@ -0,0 +1,6 @@ +import pytest + +@pytest.fixture(scope='function') +def fusion_data_path(): + + pass \ No newline at end of file diff --git a/tests/fixtures/metric_fixtures.py b/tests/fixtures/metric_fixtures.py new file mode 100644 index 0000000..79fbdc5 --- /dev/null +++ b/tests/fixtures/metric_fixtures.py @@ -0,0 +1,81 @@ +import os +import pytest +from typing import Generator +import pandas as pd +import numpy as np + +from silvimetric import Log, StorageConfig, ShatterConfig, Storage, Data, Bounds +from silvimetric import __version__ as svversion + +@pytest.fixture(scope='function') +def autzen_storage(tmp_path_factory: pytest.TempPathFactory) -> Generator[StorageConfig, None, None]: + path = tmp_path_factory.mktemp("test_tdb") + p = os.path.abspath(path) + + srs = """PROJCS[\"NAD83 / Oregon GIC Lambert (ft)\", +GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\", +6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6269\"]], +PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\", +0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4269\"]], +PROJECTION[\"Lambert_Conformal_Conic_2SP\"],PARAMETER[\"latitude_of_origin\", +41.75],PARAMETER[\"central_meridian\",-120.5],PARAMETER[\"standard_parallel_1\", +43],PARAMETER[\"standard_parallel_2\",45.5],PARAMETER[\"false_easting\", +1312335.958],PARAMETER[\"false_northing\",0],UNIT[\"foot\",0.3048, +AUTHORITY[\"EPSG\",\"9002\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH], +AUTHORITY[\"EPSG\",\"2992\"]]""" + b = Bounds(635579.2,848884.83,639003.73,853536.21) + sc = StorageConfig(b, srs, 10, tdb_dir=p) + Storage.create(sc) + yield sc + +@pytest.fixture(scope='function') +def autzen_data(autzen_filepath: str, autzen_storage: StorageConfig) -> Generator[Data, None, None]: + d = Data(autzen_filepath, autzen_storage) + yield d + +@pytest.fixture(scope='function') +def metric_data(autzen_data: Data) -> Generator[pd.DataFrame, None, None]: + p = autzen_data.pipeline + autzen_data.execute() + points = p.get_dataframe(0) + points.loc[:, 'xi'] = np.floor(points.xi) + points.loc[:, 'yi'] = np.ceil(points.yi) + points = points.loc[points.xi == 1] + points = points.loc[points.yi == 437] + yield points[['Z', 'xi', 'yi']] + +@pytest.fixture(scope='function') +def metric_shatter_config(tmp_path_factory, copc_filepath, attrs, metrics, bounds, + date, crs, resolution) -> Generator[pd.Series, None, None]: + + path = tmp_path_factory.mktemp("test_tdb") + p = os.path.abspath(path) + log = Log('DEBUG') + + def dummy_fn(df: pd.DataFrame) -> pd.DataFrame: + assert isinstance(df, pd.DataFrame) + ndf = df[df['NumberOfReturns'] >= 1] + assert isinstance(ndf, pd.DataFrame) + return ndf + + metrics[0].add_filter(dummy_fn, 'This is a function.') + metrics[0].attributes=attrs + + """Make output""" + st_config=StorageConfig(tdb_dir=p, + log=log, + crs=crs, + root=bounds, + resolution=resolution, + attrs=attrs, + metrics=metrics, + version=svversion) + + s = Storage.create(st_config) + sh_config = ShatterConfig(tdb_dir=p, + log=log, + filename=copc_filepath, + bounds=bounds, + debug=True, + date=date) + yield sh_config \ No newline at end of file diff --git a/tests/fixtures/shatter_fixtures.py b/tests/fixtures/shatter_fixtures.py new file mode 100644 index 0000000..8f9cff3 --- /dev/null +++ b/tests/fixtures/shatter_fixtures.py @@ -0,0 +1,96 @@ +import pytest +from typing import Generator +from uuid import uuid4 +import os + +from silvimetric import __version__ as svversion +from silvimetric import StorageConfig, ShatterConfig, Storage, Log, Bounds + +@pytest.fixture(scope="function") +def s3_bucket() -> Generator[str, None, None]: + yield "silvimetric" + +@pytest.fixture(scope='function') +def s3_uri(s3_bucket) -> Generator[str, None, None]: + uuid = uuid4() + yield f"s3://{s3_bucket}/test_silvimetric/{uuid}" + +@pytest.fixture(scope="function") +def s3_storage_config(s3_uri, bounds, resolution, crs, attrs, metrics) -> Generator[StorageConfig, None, None]: + yield StorageConfig(bounds, crs, resolution, attrs, metrics, + svversion, tdb_dir=s3_uri) + +@pytest.fixture(scope='function') +def s3_storage(s3_storage_config) -> Generator[Storage, None, None]: + import subprocess + yield Storage.create(s3_storage_config) + subprocess.call(["aws", "s3", "rm", "--recursive", s3_storage_config.tdb_dir]) + +@pytest.fixture(scope="function") +def s3_shatter_config(s3_storage, copc_filepath, attrs, metrics, date) -> Generator[ShatterConfig, None, None]: + config = s3_storage.config + yield ShatterConfig(filename=copc_filepath, attrs=attrs, metrics=metrics, + debug=True, tdb_dir=config.tdb_dir, date=date) + +@pytest.fixture(scope='function') +def uneven_storage_config(tmp_path_factory, bounds, crs, attrs, metrics) -> Generator[StorageConfig, None, None]: + log = Log('INFO') + path = tmp_path_factory.mktemp("test_tdb") + p = os.path.abspath(path) + + log = Log('DEBUG') + sc = StorageConfig(tdb_dir = p, + log = log, + crs = crs, + root = bounds, + resolution = 7, + attrs = attrs, + metrics = metrics, + version = svversion) + Storage.create(sc) + yield sc + +@pytest.fixture(scope='function') +def uneven_shatter_config(copc_filepath, uneven_storage_config, date) -> Generator[ShatterConfig, None, None]: + tdb_dir = uneven_storage_config.tdb_dir + log = uneven_storage_config.log + + s = ShatterConfig(tdb_dir = tdb_dir, + log = log, + filename = copc_filepath, + attrs = uneven_storage_config.attrs, + metrics = uneven_storage_config.metrics, + debug = True, + date=date) + yield s + +@pytest.fixture(scope='function') +def partial_storage_config(tmp_path_factory, crs, attrs, metrics) -> Generator[StorageConfig, None, None]: + path = tmp_path_factory.mktemp("test_tdb") + p = os.path.abspath(path) + log = Log('DEBUG') + + bounds = Bounds(300,300,450,450) + sc = StorageConfig(tdb_dir = p, + log = log, + crs = crs, + root = bounds, + resolution = 30, + attrs = attrs, + metrics = metrics, + version = svversion) + Storage.create(sc) + yield sc + +@pytest.fixture(scope='function') +def partial_shatter_config(copc_filepath, date, partial_storage_config) -> Generator[ShatterConfig, None, None]: + tdb_dir = partial_storage_config.tdb_dir + psc: StorageConfig = partial_storage_config + log = Log('INFO') # INFO + yield ShatterConfig(tdb_dir=tdb_dir, + log=log, + attrs=psc.attrs, + metrics=psc.metrics, + filename=copc_filepath, + debug=True, + date=date) \ No newline at end of file diff --git a/tests/fixtures/western_fixtures.py b/tests/fixtures/western_fixtures.py new file mode 100644 index 0000000..8fc991f --- /dev/null +++ b/tests/fixtures/western_fixtures.py @@ -0,0 +1,64 @@ +import tempfile +from typing import Generator + +from silvimetric import Storage, ShatterConfig, Log, Bounds, StorageConfig +from silvimetric import __version__ as svversion +import os, shutil +import pytest + +@pytest.fixture(scope="class") +def WebMercator(): + yield "EPSG:3857" + +@pytest.fixture(scope='class') +def WesternBounds() -> Generator[Bounds, None, None]: + b = Bounds(-14100053.268191, 3058230.975702, -11138180.816218, 6368599.176434) + yield b + +@pytest.fixture(scope='function') +def western_filepath(tmp_path_factory) -> Generator[str, None, None]: + + temp_name = next(tempfile._get_candidate_names()) + path = tmp_path_factory.mktemp(temp_name) + yield os.path.abspath(path) + shutil.rmtree(path) + + +@pytest.fixture(scope='function') +def western_config(western_filepath, WesternBounds, resolution, WebMercator, + attrs, metrics) -> Generator[StorageConfig, None, None]: + log = Log(20) + yield StorageConfig(tdb_dir = western_filepath, + log = log, + crs = WebMercator, + root = WesternBounds, + resolution = resolution, + attrs = attrs, + metrics = metrics, + version = svversion) + +@pytest.fixture(scope='function') +def western_storage(western_config) -> Generator[Storage, None, None]: + yield Storage.create(western_config) + +@pytest.fixture(scope='function') +def western_pipeline() -> Generator[str, None, None]: + path = os.path.join(os.path.dirname(__file__), "data", + "western_us.json") + assert os.path.exists(path) + yield os.path.abspath(path) + +@pytest.fixture(scope='function') +def western_shatter_config(western_pipeline, western_storage, bounds, date) -> Generator[ShatterConfig, None, None]: + log = Log(20) # INFO + st = western_storage.config + + s = ShatterConfig(tdb_dir = st.tdb_dir, + log = log, + filename = western_pipeline, + attrs = st.attrs, + metrics = st.metrics, + bounds=bounds, + debug = True, + date=date) + yield s \ No newline at end of file diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..a6e524b --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,148 @@ +from click.testing import CliRunner +import os +from datetime import datetime + +import numpy as np +from pytest import TempPathFactory + +from silvimetric.cli import cli +from silvimetric.commands import shatter, info +from silvimetric import ShatterConfig, Bounds, StorageConfig, Storage, ExtractConfig + +class TestCli(object): + def test_cli_init(self, tmp_path_factory: TempPathFactory, + runner: CliRunner, bounds: Bounds) -> None: + path = tmp_path_factory.mktemp("test_tdb") + p = os.path.abspath(path) + res = runner.invoke(cli.cli, args=["-d", p, "--debug", + "--scheduler", "single-threaded", "initialize", "--resolution", + '10', '--crs', 'EPSG:3857', '--bounds', str(bounds)], + catch_exceptions=False) + assert res.exit_code == 0 + + def test_cli_metric_groups(self, tmp_path_factory: TempPathFactory, + runner: CliRunner, bounds: Bounds) -> None: + path = tmp_path_factory.mktemp("test_tdb") + p = os.path.abspath(path) + res = runner.invoke(cli.cli, args=["-d", p, "--debug", "--scheduler", + "single-threaded", "initialize", "--resolution", "10", "--crs", "EPSG:3857", + "--bounds", str(bounds), "-m", "stats,p_moments"], catch_exceptions=False) + assert res.exit_code == 0 + + def test_cli_metric_file(self, tmp_path_factory: TempPathFactory, + runner: CliRunner, bounds: Bounds, date: datetime, copc_filepath: str) -> None: + # test that we can pass in a file containing metrics and then run shatter over it + path = tmp_path_factory.mktemp("test_tdb") + p = os.path.abspath(path) + fakes = os.path.abspath('tests/fixtures/fake_metrics.py') + res = runner.invoke(cli.cli, args=["-d", p, "--debug", + "--scheduler", "single-threaded", "initialize", + "--resolution", "10", "--crs", "EPSG:3857", "--bounds", + str(bounds), "-m", f"{fakes}"], + catch_exceptions=False) + assert res.exit_code == 0 + res2 = runner.invoke(cli.cli, args=["-d", p, + "--scheduler", "single-threaded", + "shatter", copc_filepath, + "--date", date.isoformat()+'Z', + "--tilesize", '10'], catch_exceptions=False) + assert res2.exit_code == 0 + storage = Storage.from_db(p) + with storage.open('r') as a: + exists_vals = a[:,:]['m_Z_exists'] + assert all(exists_vals) + count_vals = a[:,:]['m_Z_count'] + assert all(count_vals == 100) + + + + def test_cli_shatter(self, runner: CliRunner, maxy: float, date: datetime, + tdb_filepath: str, copc_filepath: str, storage: shatter.Storage) -> None: + + res = runner.invoke(cli.cli, args=["-d", tdb_filepath, + "--scheduler", "single-threaded", + "shatter", copc_filepath, + "--date", date.isoformat()+'Z', + "--tilesize", '10'], catch_exceptions=False) + assert res.exit_code == 0 + + with storage.open('r') as a: + assert a[:,:]['Z'].shape[0] == 100 + xdom = a.schema.domain.dim('X').domain[1] + ydom = a.schema.domain.dim('Y').domain[1] + assert xdom == 10 + assert ydom == 10 + + for xi in range(xdom): + for yi in range(ydom): + a[xi, yi]['Z'].size == 1 + a[xi, yi]['Z'][0].size == 900 + # this should have all indices from 0 to 9 filled. + # if oob error, it's not this test's fault + assert bool(np.all( a[xi, yi]['Z'][0] == ( + (maxy/storage.config.resolution) - (yi + 1)) )) + + def test_cli_scan(self, runner: CliRunner, copc_filepath: str, + storage_config: StorageConfig) -> None: + tdb_dir = storage_config.tdb_dir + res = runner.invoke(cli.cli, args=['-d', tdb_dir, '--scheduler', + 'single-threaded', 'scan', copc_filepath]) + print(res.output) + assert res.exit_code == 0 + + def test_cli_info(self, tdb_filepath: str, runner: CliRunner, + shatter_config: ShatterConfig) -> None: + shatter.shatter(shatter_config) + res = runner.invoke(cli.cli, args=['-d', tdb_filepath, + '--scheduler', 'single-threaded', 'info']) + assert res.exit_code == 0 + + def test_cli_extract(self, runner: CliRunner, extract_config: ExtractConfig, + storage: Storage): + atts = [] + for a in extract_config.attrs: + atts.append('-a') + atts.append(a.name) + ms = [] + for m in extract_config.metrics: + ms.append('-m') + ms.append(m.name) + out_dir = extract_config.out_dir + tdb_dir = extract_config.tdb_dir + + res = runner.invoke(cli.cli, args=['-d', tdb_dir, '--scheduler', + 'single-threaded', 'extract', *atts, *ms, '--outdir' ,out_dir]) + + assert res.exit_code == 0 + + def test_cli_delete(self, runner: CliRunner, shatter_config: ShatterConfig, + pre_shatter: int) -> None: + i = shatter_config.name + res = runner.invoke(cli.cli, ['-d', shatter_config.tdb_dir, + '--scheduler', 'single-threaded', 'delete', '--id', i]) + assert res.exit_code == 0 + + i = info.info(shatter_config.tdb_dir) + assert len(i['history']) == 0 + + def test_cli_restart(self, runner: CliRunner, shatter_config: ShatterConfig, + pre_shatter: int) -> None: + + i = shatter_config.name + res = runner.invoke(cli.cli, ['-d', shatter_config.tdb_dir, + '--scheduler', 'single-threaded', 'restart', '--id', i]) + assert res.exit_code == 0 + i = info.info(shatter_config.tdb_dir) + assert len(i['history']) == 1 + assert ShatterConfig.from_dict(i['history'][0]) + + def test_cli_resume(self, runner: CliRunner, shatter_config: ShatterConfig, + pre_shatter: int) -> None: + + i = shatter_config.name + res = runner.invoke(cli.cli, ['-d', shatter_config.tdb_dir, + '--scheduler', 'single-threaded', 'restart', '--id', i]) + assert res.exit_code == 0 + i = info.info(shatter_config.tdb_dir) + assert len(i['history']) == 1 + assert ShatterConfig.from_dict(i['history'][0]) \ No newline at end of file diff --git a/tests/test_commands.py b/tests/test_commands.py index 4e5fd92..bcabe8a 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1,50 +1,17 @@ -import uuid -from time import sleep -from typing import List -from datetime import datetime - -from silvimetric.commands import scan, info, shatter -from silvimetric.resources import ShatterConfig - -import pytest -import conftest - -@pytest.fixture(scope='function') -def config_split(shatter_config: ShatterConfig) -> List[ShatterConfig]: - sc = shatter_config - config_split = [] - day = 1 - for b in sc.bounds.bisect(): - day += 1 - date = datetime(2011, 1, day) - - config_split.append(ShatterConfig( - sc.filename, - date, - sc.attrs, - sc.metrics, - b, - uuid.uuid4(), - sc.tile_size, - tdb_dir=sc.tdb_dir, - log=sc.log - )) - - for s in config_split: - shatter.shatter(s) - sleep(1) - - return config_split +import json +from silvimetric.commands import scan, info, shatter, manage +from silvimetric import ShatterConfig, Storage class TestCommands(object): def test_scan(self, shatter_config): s = shatter_config res = scan.scan(s.tdb_dir, s.filename, s.bounds, 10, 10, 5) - assert res == 1 + assert res['tile_info']['recommended'] == 1 + res = scan.scan(s.tdb_dir, s.filename, s.bounds, depth=5) - assert res == 100 + assert res['tile_info']['recommended'] == 100 def test_info(self, tdb_filepath, config_split): i = info.info(tdb_filepath) @@ -67,4 +34,60 @@ def test_info(self, tdb_filepath, config_split): d = config_split[1].date i = info.info(tdb_filepath, start_time=d, end_time=d) assert len(i['history']) == 1 - assert i['history'][0] == config_split[1].to_json() \ No newline at end of file + assert i['history'][0] == config_split[1].to_json() + + def test_delete(self, tdb_filepath, config_split): + ids = [c.name for c in config_split] + + for i in range(1,len(ids)+1): + h = info.info(tdb_dir=tdb_filepath) + + assert bool(h['history']) + assert len(h['history']) == 5-i + idx = i - 1 + manage.delete(tdb_filepath, ids[idx]) + + h = info.info(tdb_dir=tdb_filepath) + assert not bool(h['history']) + + def test_311_failure(self, shatter_config, dask_proc_client): + # make sure we handle tiledb contexts correctly within dask + + tdb_dir = shatter_config.tdb_dir + shatter.shatter(shatter_config) + + i = info.info(tdb_dir) + history = i['history'][-1] + + finished_config = ShatterConfig.from_dict(history) + sh_id = finished_config.name + manage.delete(tdb_dir, sh_id) + + def test_restart(self, tdb_filepath, config_split): + ids = [c.name for c in config_split] + + for i in range(1,len(ids)+1): + h = info.info(tdb_dir=tdb_filepath) + + assert bool(h['history']) + assert len(h['history']) == len(ids) + manage.restart(tdb_filepath, ids[i-1]) + + h = info.info(tdb_dir=tdb_filepath) + assert bool(h['history']) + assert len(h['history']) == 4 + + def test_resume(self, shatter_config: ShatterConfig, test_point_count): + # test that shatter only operates on cells not touched by nonempty_domain + storage = Storage.from_db(shatter_config.tdb_dir) + + # modify shatter config + shatter_config.tile_size=1 + shatter_config.mbr = (((0,4), (0,4)),) + # send to shatter + pc = shatter.shatter(shatter_config) + assert pc == test_point_count - (test_point_count / 4) + # check output config to see what the nonempthy_domain is + # - should be only only values outside of that tuple + m = json.loads(storage.getMetadata('shatter', 1)) + assert len(m['mbr']) == 75 diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 9f81dd0..6c27838 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1,40 +1,28 @@ -import pytest -import json -import os -import dataclasses - - -from silvimetric.resources import StorageConfig, Bounds, Log, Metric - -@pytest.fixture(scope='function') -def tdb_filepath(tmp_path_factory) -> str: - path = tmp_path_factory.mktemp("test_tdb") - yield os.path.abspath(path) - -@pytest.fixture(scope="function") -def config(tdb_filepath, resolution, attrs, minx, maxx, miny, maxy, - crs) -> StorageConfig: - - b = Bounds(minx, miny, maxx, maxy) - log = Log(20) - config = StorageConfig(tdb_dir = tdb_filepath, - log = log, - root = b, - resolution = resolution, - crs = crs, - attrs = attrs) - yield config - +from silvimetric import StorageConfig, ShatterConfig, ExtractConfig class Test_Configuration(object): - def test_serialization(self, config: StorageConfig): - - j = str(config) + def test_serialization(self, storage_config, shatter_config, extract_config): + # storage + j = str(storage_config) c = StorageConfig.from_string(j) + mean = [ m for m in c.metrics if m.name == 'mean'] assert len(mean) == 1 - assert int(mean[0]([2,2,2,2])) == 2 - assert config == c - + assert int(mean[0]._method([2,2,2,2])) == 2 + cd = c.to_json() + scd = storage_config.to_json() + cd.pop('log') + scd.pop('log') + assert scd == cd + + # shatter + sh_str = str(shatter_config) + sh_cfg = ShatterConfig.from_string(sh_str) + assert shatter_config == sh_cfg + + # extract + ex_str = str(extract_config) + ex_cfg = ExtractConfig.from_string(ex_str) + assert extract_config == ex_cfg \ No newline at end of file diff --git a/tests/test_data.py b/tests/test_data.py index e32054e..6a5d106 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -1,70 +1,46 @@ -import pytest -import os - -from silvimetric.resources import Data -from silvimetric.resources import Storage -from silvimetric.resources import Bounds - -import conftest - -@pytest.fixture(scope='session') -def no_cell_line_pc(): - yield 84100 - -@pytest.fixture(scope='session') -def no_cell_line_path(): - path = os.path.join(os.path.dirname(__file__), "data", - "test_data_2.copc.laz") - assert os.path.exists(path) - yield os.path.abspath(path) - -@pytest.fixture(scope='session') -def no_cell_line_pipeline(): - path = os.path.join(os.path.dirname(__file__), "data", - "test_data_2.json") - assert os.path.exists(path) - yield os.path.abspath(path) - -@pytest.fixture(scope='session') -def autzen_filepath() -> str: - path = os.path.join(os.path.dirname(__file__), "data", - "autzen-small.copc.laz") - assert os.path.exists(path) - yield os.path.abspath(path) +from silvimetric import Data, Storage, Bounds class Test_Data(object): - def test_filepath(self, no_cell_line_path, storage: Storage, no_cell_line_pc, bounds): + def test_filepath(self, no_cell_line_path, storage_config, no_cell_line_pc, bounds): """Check open a COPC file""" - data = Data(no_cell_line_path, storage.config) + data = Data(no_cell_line_path, storage_config) assert data.is_pipeline() == False data.execute() assert len(data.array) == no_cell_line_pc assert data.estimate_count(bounds) == no_cell_line_pc - def test_pipeline(self, no_cell_line_pipeline, bounds, storage: Storage, no_cell_line_pc): + def test_pipeline(self, no_cell_line_pipeline, bounds, storage_config, no_cell_line_pc): """Check open a pipeline""" - data = Data(no_cell_line_pipeline, storage.config) + data = Data(no_cell_line_pipeline, storage_config) assert data.is_pipeline() == True data.execute() assert len(data.array) == no_cell_line_pc assert data.estimate_count(bounds) == no_cell_line_pc - def test_pipeline_bounds(self, no_cell_line_pipeline, bounds, storage: Storage, no_cell_line_pc): + def test_pipeline_bounds(self, no_cell_line_pipeline, bounds, storage_config, no_cell_line_pc): """Check open a pipeline with our own bounds""" ll = list(bounds.bisect())[0] - data = Data(no_cell_line_pipeline, storage.config, bounds = ll) + + #data will be collared upon execution, extra data will be grabbed + minx, miny, maxx, maxy = ll.get() + collared = Bounds(minx - 30, miny - 30, maxx + 30, maxy + 30) + + data = Data(no_cell_line_pipeline, storage_config, bounds = ll) assert data.is_pipeline() == True data.execute() - assert len(data.array) == no_cell_line_pc / 4 + + collared_count = data.count(collared) + assert len(data.array) == collared_count + assert data.estimate_count(ll) == no_cell_line_pc assert data.count(ll) == no_cell_line_pc / 4 class Test_Autzen(object): - def test_filepath(self, autzen_filepath, storage: Storage): + def test_filepath(self, autzen_filepath, storage_config): """Check open Autzen """ - data = Data(autzen_filepath, storage.config) + data = Data(autzen_filepath, storage_config) assert data.is_pipeline() == False data.execute() assert len(data.array) == 577637 diff --git a/tests/test_chunk.py b/tests/test_extents.py similarity index 89% rename from tests/test_chunk.py rename to tests/test_extents.py index e46da03..5efd829 100644 --- a/tests/test_chunk.py +++ b/tests/test_extents.py @@ -1,12 +1,14 @@ import numpy as np -import dask -import pdal -import pytest import datetime -from silvimetric.resources import Extents +from silvimetric import Extents from silvimetric.commands.shatter import run +def check_for_overlap(leaves: list[Extents], chunk: Extents): + idx = [i for l in leaves for i in l.get_indices() ] + u, c = np.unique(idx, return_counts=True) + assert not np.any(np.where(c > 1)) + def check_for_holes(leaves: list[Extents], chunk: Extents): ind = np.array([], dtype=chunk.get_indices().dtype) for leaf in leaves: @@ -83,19 +85,13 @@ def check_indexing(extents: Extents, leaf_list): class TestExtents(object): - @pytest.fixture(scope='function', autouse=True) - def filtered(self, copc_data, extents: Extents): - return list(extents.chunk(copc_data, 1)) - - @pytest.fixture(scope='function') - def unfiltered(self, extents: Extents): - return list(extents.get_leaf_children(30)) - def test_indexing(self, extents, filtered, unfiltered): check_indexing(extents, filtered) check_for_holes(filtered, extents) + check_for_overlap(filtered, extents) check_indexing(extents, unfiltered) check_for_holes(unfiltered, extents) + check_for_overlap(unfiltered, extents) # def test_cells(self, copc_filepath, unfiltered, resolution): # flag = False @@ -122,8 +118,8 @@ def test_pointcount(self, filtered, unfiltered, test_point_count, shatter_config with storage.open('w') as tdb: shatter_config.start_time = datetime.datetime.now().timestamp() * 1000 - fc = run(filtered, shatter_config, storage, tdb) - ufc = run(unfiltered, shatter_config, storage, tdb) + fc = run(filtered, shatter_config, storage) + ufc = run(unfiltered, shatter_config, storage) assert fc == ufc, f""" Filtered and unfiltered point counts don't match. diff --git a/tests/test_extract.py b/tests/test_extract.py index ccad6e9..f16726d 100644 --- a/tests/test_extract.py +++ b/tests/test_extract.py @@ -1,80 +1,43 @@ -import pytest -import os from pathlib import Path from osgeo import gdal from pyproj import CRS -import copy -import uuid -from silvimetric.commands.shatter import shatter -from silvimetric.commands.extract import extract -from silvimetric.resources import Metrics, Attribute, ExtractConfig, Extents, Storage, Log - -@pytest.fixture(scope='function') -def tif_filepath(tmp_path_factory) -> str: - path = tmp_path_factory.mktemp("test_tifs") - yield os.path.abspath(path) - -@pytest.fixture(scope='function') -def extract_attrs(dims)->list[str]: - yield [Attribute('Z', dtype=dims['Z']), Attribute('Intensity', dtype=dims['Intensity'])] - -@pytest.fixture(scope='function') -def extract_config(tdb_filepath, tif_filepath, metrics, shatter_config, extract_attrs): - shatter(shatter_config) - log = Log(20) - c = ExtractConfig(tdb_dir = tdb_filepath, - log = log, - out_dir = tif_filepath, - attrs = extract_attrs, - metrics = metrics) - yield c - -@pytest.fixture(scope='function') -def multivalue_config(tdb_filepath, tif_filepath, metrics, shatter_config, extract_attrs): - - shatter(shatter_config) - e = Extents.from_storage(shatter_config.tdb_dir) - b: Extents = e.split()[0] - - second_config = copy.deepcopy(shatter_config) - second_config.bounds = b.bounds - second_config.name = uuid.uuid4() - second_config.point_count = 0 - - shatter(second_config) - log = Log(20) - c = ExtractConfig(tdb_dir = tdb_filepath, - log = log, - out_dir = tif_filepath, - attrs = extract_attrs, - metrics = metrics) - yield c +from silvimetric import grid_metrics, ExtractConfig, Extents, Log, extract, Storage def tif_test(extract_config): minx, miny, maxx, maxy = extract_config.bounds.get() resolution = extract_config.resolution - filenames = [Metrics[m.name].entry_name(a.name) + filenames = [grid_metrics[m.name].entry_name(a.name) for m in extract_config.metrics for a in extract_config.attrs] - e = Extents.from_storage(extract_config.tdb_dir) - yimin = e.get_indices()['y'].min() + storage = Storage.from_db(extract_config.tdb_dir) + e = Extents(extract_config.bounds, extract_config.resolution, storage.config.root) + root_maxy = storage.config.root.maxy for f in filenames: + if 'Return' in f: + continue path = Path(extract_config.out_dir) / f'{f}.tif' assert path.exists() raster: gdal.Dataset = gdal.Open(str(path)) derived = CRS.from_user_input(raster.GetProjection()) + rminx, xres, xskew, rmaxy, yskew, yres = raster.GetGeoTransform() + assert rminx == minx + assert rmaxy == maxy + assert xres == resolution + assert -yres == resolution + + assert derived == extract_config.crs - xsize = int((maxx - minx) / resolution) - ysize = int((maxy - miny) / resolution) + xsize = e.x2 + ysize = e.y2 assert raster.RasterXSize == xsize assert raster.RasterYSize == ysize r = raster.ReadAsArray() - assert all([ r[y,x] == ((maxy/resolution)-(y+yimin)) for y in range(ysize) for x in range(xsize)]) + assert all([ r[y,x] == ((root_maxy/resolution)-y-1) for y in range(e.y1, e.y2) for x in range(e.x1, e.x2)]) class Test_Extract(object): diff --git a/tests/test_fusion.py b/tests/test_fusion.py new file mode 100644 index 0000000..838724f --- /dev/null +++ b/tests/test_fusion.py @@ -0,0 +1,5 @@ + + +# Test against Bob's FUSION data +class TestFusion(): + pass \ No newline at end of file diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 16be781..d2d273b 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -1,90 +1,97 @@ -import os -import pytest import numpy as np import pandas as pd -import dask - -from silvimetric.resources import Metrics, Log, ShatterConfig, StorageConfig -from silvimetric.resources import Storage, Bounds, Extents -from silvimetric.commands import shatter -from silvimetric import __version__ as svversion - -@pytest.fixture(scope='function') -def metric_input(): - # randomly generated int array - yield np.array([54, 64, 54, 18, 76, 82, 85, 12, 7, 32, 89, 79, 78, 58, 17, 5, 77, - 20, 63, 87, 37, 27, 7, 22, 34, 61, 52, 64, 65, 90, 76, 88, 0, 98, - 78, 26, 88, 44, 78, 54, 38, 28, 94, 74, 90, 84, 30, 7, 81, 75, 22, - 12, 67, 10, 46, 62, 79, 52, 24, 88, 4, 73, 75, 35, 75, 16, 66, 43, - 28, 72, 15, 80, 53, 1, 28, 70, 20, 42, 83, 80, 82, 2, 41, 38, 23, - 17, 18, 19, 43, 30, 88, 41, 60, 29, 93, 27, 1, 13, 93, 82], np.int32) - -@pytest.fixture(scope='session') -def autzen_filepath() -> str: - path = os.path.join(os.path.dirname(__file__), "data", - "autzen-small.copc.laz") - assert os.path.exists(path) - yield os.path.abspath(path) - -@pytest.fixture(scope='function') -def autzen_bounds(): - yield Bounds(635579.2, 848884.83, 639003.73, 853536.21) - -@pytest.fixture(scope='function') -def metric_st_config(tdb_filepath, autzen_bounds, resolution, crs, attrs, metrics): - log = Log(20) - yield StorageConfig(tdb_dir = tdb_filepath, - log = log, - crs = crs, - root = autzen_bounds, - resolution = resolution, - attrs = attrs, - version = svversion) - -@pytest.fixture(scope='function') -def metric_shatter(tdb_filepath, autzen_filepath, metric_st_config, autzen_bounds, app_config, date): - log = Log(20) # INFO - Storage.create(metric_st_config) - e = Extents(autzen_bounds, 30, autzen_bounds) - e1 = e.split()[1] - s = ShatterConfig(tdb_dir = tdb_filepath, - log=log, - filename=autzen_filepath, - attrs=metric_st_config.attrs, - metrics=metric_st_config.metrics, - bounds=e1.bounds, - debug=True, - date=date, - tile_size=700) - - yield s - -class Test_Metrics(object): - - def test_input_output(self, metric_input): - for m in Metrics: - try: - out = Metrics[m]._method(metric_input) - msg = f"Metric {Metrics[m].name} does not output a valid data type."\ - f" Interpretted type: {type(out)}" - assert not any([ - isinstance(out, np.ndarray), - isinstance(out, list), - isinstance(out, tuple), - isinstance(out, pd.Series), - isinstance(out, pd.DataFrame), - isinstance(out, str) - ]), msg - assert any(( - isinstance(out, np.number), - isinstance(out, int), - isinstance(out, float), - isinstance(out, complex), - isinstance(out, bool) - )), msg - except: - print('yi') - - def test_metric_shatter(self, metric_shatter): - dask.config.set(scheduler='processes') - shatter.shatter(metric_shatter) \ No newline at end of file + +from silvimetric import shatter, Storage, grid_metrics, run_metrics, Metric +from silvimetric import all_metrics as s +from silvimetric import l_moments +from silvimetric.resources.attribute import Attribute + +class TestMetrics(): + + def test_metrics(self, metric_data): + ms: pd.DataFrame = run_metrics(metric_data, + list(grid_metrics.values())).compute() + assert isinstance(ms, pd.DataFrame) + adjusted = [k.split('_')[-1] for k in ms.keys()] + + assert all(a in grid_metrics.keys() for a in adjusted) + + + def test_intermediate_metric(self, metric_data): + ms = list(l_moments.values()) + computed = run_metrics(metric_data, ms).compute() + + assert computed.m_Z_l1.any() + assert computed.m_Z_l2.any() + assert computed.m_Z_l3.any() + assert computed.m_Z_l4.any() + assert computed.m_Z_lcv.any() + assert computed.m_Z_lskewness.any() + assert computed.m_Z_lkurtosis.any() + + def test_dependencies(self, metric_data): + # should be able to create a dependency graph + cv = s['cv'] + mean = s['mean'] + stddev = s['stddev'] + median = s['median'] + + mean.dependencies = [ median ] + stddev.dependencies = [ median ] + cv.dependencies = [ mean, stddev ] + + b = run_metrics(metric_data, [cv, mean]).compute() + # cv/mean should be there + assert b.m_Z_cv.any() + assert b.m_Z_mean.any() + + #and median/stddev should not + assert not any(x in b.dtypes for x in ['m_Z_median', 'm_Z_stddev']) + + def test_filter(self, metric_shatter_config, test_point_count, maxy, + resolution): + + m = metric_shatter_config.metrics[0] + assert len(m.filters) == 1 + + pc = shatter(metric_shatter_config) + assert pc == test_point_count + + s = Storage.from_db(metric_shatter_config.tdb_dir) + with s.open('r') as a: + q = a.query(coords=False, use_arrow=False).df + + nor_mean = q[:]['m_NumberOfReturns_mean'] + nor = q[:]['NumberOfReturns'] + assert not nor_mean.isna().any() + assert nor.notna().any() + + assert a[:,:]['Z'].shape[0] == 100 + xdom = a.schema.domain.dim('X').domain[1] + ydom = a.schema.domain.dim('Y').domain[1] + assert xdom == 10 + assert ydom == 10 + + data = a.query(attrs=['Z'], coords=True, use_arrow=False).df[:] + data = data.set_index(['X','Y']) + + for xi in range(xdom): + for yi in range(ydom): + curr = data.loc[xi,yi] + curr.size == 1 + curr.iloc[0].size == 900 + # this should have all indices from 0 to 9 filled. + # if oob error, it's probably not this test's fault + assert bool(np.all( curr.iloc[0] == ((maxy/resolution) - (yi + 1)) )) + + def test_custom(self, metric_data: pd.DataFrame, attrs: list[Attribute]) -> None: + def m_over500(data): + return data[data >= 500].count() + z_att = attrs[0] + m_cust = Metric(name='over500', dtype=np.float32, method=m_over500, + attributes=[z_att]) + b = run_metrics(metric_data, [m_cust]).compute() + + assert b.m_Z_over500.any() + assert b.m_Z_over500.values[0] == 3 + diff --git a/tests/test_shatter.py b/tests/test_shatter.py index 44aff3d..cc10bad 100644 --- a/tests/test_shatter.py +++ b/tests/test_shatter.py @@ -4,15 +4,13 @@ import dask import json import uuid -import platform +from silvimetric import Extents, Log, info, shatter, Bounds, Storage +from silvimetric import StorageConfig, ShatterConfig -from silvimetric.commands.shatter import shatter -from silvimetric.commands.info import info -from silvimetric.resources import Storage, Extents, ShatterConfig, Log @dask.delayed -def write(x,y,val, s:Storage, attrs, dims, metrics): +def write(x, y, val, s:Storage, attrs, dims, metrics): m_list = [m.entry_name(a.name) for m in metrics for a in attrs] data = { a.name: np.array([np.array([val], dims[a.name]), None], object)[:-1] for a in attrs } @@ -26,35 +24,64 @@ def write(x,y,val, s:Storage, attrs, dims, metrics): class Test_Shatter(object): - def test_shatter(self, shatter_config, storage: Storage, maxy): + def test_command(self, shatter_config, storage: Storage, maxy): shatter(shatter_config) with storage.open('r') as a: - y = a[:,0]['Z'].shape[0] - x = a[0,:]['Z'].shape[0] - assert y == 10 - assert x == 10 - for xi in range(x): - for yi in range(y): + assert a[:,:]['Z'].shape[0] == 100 + xdom = a.schema.domain.dim('X').domain[1] + ydom = a.schema.domain.dim('Y').domain[1] + assert xdom == 10 + assert ydom == 10 + + for xi in range(xdom): + for yi in range(ydom): a[xi, yi]['Z'].size == 1 - assert bool(np.all(a[xi, yi]['Z'][0] == ((maxy/storage.config.resolution)-yi))) + a[xi, yi]['Z'][0].size == 900 + # this should have all indices from 0 to 9 filled. + # if oob error, it's not this test's fault + assert bool(np.all( a[xi, yi]['Z'][0] == ((maxy/storage.config.resolution) - (yi + 1)) )) + def test_multiple(self, shatter_config, storage: Storage, maxy): + shatter(shatter_config) + with storage.open('r') as a: + assert a[:,:]['Z'].shape[0] == 100 + xdom = a.schema.domain.dim('X').domain[1] + ydom = a.schema.domain.dim('Y').domain[1] + assert xdom == 10 + assert ydom == 10 + + for xi in range(xdom): + for yi in range(ydom): + a[xi, yi]['Z'].size == 1 + a[xi, yi]['Z'][0].size == 900 + # this should have all indices from 0 to 9 filled. + # if oob error, it's not this test's fault + assert bool(np.all( a[xi, yi]['Z'][0] == ((maxy/storage.config.resolution) - (yi + 1)) )) + + # change attributes to make it a new run shatter_config.name = uuid.uuid4() + shatter_config.mbr = () + shatter_config.time_slot = 2 + shatter(shatter_config) with storage.open('r') as a: # querying flattens to 20, there will 10 pairs of values + assert a[:,:]['Z'].shape[0] == 200 assert a[:,0]['Z'].shape[0] == 20 assert a[0,:]['Z'].shape[0] == 20 # now test that the second set is the same as the first set # and test that this value is still the same as the original # which was set at ((maxy/resolution)-yindex) - for xi in range(x): - for yi in range(y): + for xi in range(xdom): + for yi in range(ydom): a[xi, yi]['Z'].size == 2 + a[xi, yi]['Z'][0].size == 900 + a[xi, yi]['Z'][1].size == 900 assert bool(np.all(a[xi, yi]['Z'][1] == a[xi,yi]['Z'][0])) - assert bool(np.all(a[xi, yi]['Z'][1] == ((maxy/storage.config.resolution)-yi))) + assert bool(np.all(a[xi, yi]['Z'][1] == ((maxy/storage.config.resolution) - (yi + 1)))) - m = info(storage.config.tdb_dir) - assert len(m['history']) == 2 + m = info(storage.config.tdb_dir) + assert len(m['history']) == 2 def test_parallel(self, storage, attrs, dims, threaded_dask, metrics): # test that writing in parallel doesn't affect ordering of values @@ -75,7 +102,7 @@ def test_parallel(self, storage, attrs, dims, threaded_dask, metrics): def test_config(self, shatter_config, storage, test_point_count): shatter(shatter_config) try: - meta = storage.getMetadata('shatter') + meta = storage.getMetadata('shatter', shatter_config.time_slot) except BaseException as e: pytest.fail("Failed to retrieve 'shatter' metadata key." + e.args) meta_j = json.loads(meta) @@ -83,7 +110,7 @@ def test_config(self, shatter_config, storage, test_point_count): assert pc == test_point_count @pytest.mark.parametrize('sh_cfg', ['shatter_config', 'uneven_shatter_config']) - def test_sub_bounds(self, sh_cfg, test_point_count, request): + def test_sub_bounds(self, sh_cfg, test_point_count, request, maxy, resolution): s = request.getfixturevalue(sh_cfg) storage = Storage.from_db(s.tdb_dir) e = Extents.from_storage(s.tdb_dir) @@ -91,6 +118,7 @@ def test_sub_bounds(self, sh_cfg, test_point_count, request): pc = 0 for b in e.split(): log = Log(20) + time_slot = storage.reserve_time_slot() sc = ShatterConfig(tdb_dir = s.tdb_dir, log = log, filename = s.filename, @@ -99,30 +127,59 @@ def test_sub_bounds(self, sh_cfg, test_point_count, request): metrics = s.metrics, debug = s.debug, bounds = b.bounds, - date=s.date) + date=s.date, + time_slot=time_slot) pc = pc + shatter(sc) history = info(s.tdb_dir)['history'] + assert len(history) == 4 assert isinstance(history, list) pcs = [ h['point_count'] for h in history ] assert sum(pcs) == test_point_count assert pc == test_point_count + with storage.open('r') as a: + xdom = a.schema.domain.dim('X').domain[1] + ydom = a.schema.domain.dim('Y').domain[1] + + data = a.query(attrs=['Z'], coords=True, use_arrow=False).df[:] + data = data.set_index(['X','Y']) + + for xi in range(xdom): + for yi in range(ydom): + curr = data.loc[xi,yi] + # check that each cell only has one allocation + curr.size == 1 + + def test_partial_overlap(self, partial_shatter_config, test_point_count): + pc = shatter(partial_shatter_config) + assert pc == test_point_count / 4 + @pytest.mark.skipif( os.environ.get('AWS_SECRET_ACCESS_KEY') is None or os.environ.get('AWS_ACCESS_KEY_ID') is None, reason='Missing necessary AWS environment variables' ) def test_remote_creation(self, s3_shatter_config, s3_storage): + # need processes scheduler to accurately test bug fix dask.config.set(scheduler="processes") resolution = s3_storage.config.resolution maxy = s3_storage.config.root.maxy shatter(s3_shatter_config) with s3_storage.open('r') as a: - y = a[:,0]['Z'].shape[0] - x = a[0,:]['Z'].shape[0] - assert y == 10 - assert x == 10 - for xi in range(x): - for yi in range(y): - a[xi, yi]['Z'].size == 1 - assert bool(np.all(a[xi, yi]['Z'][0] == ((maxy/resolution)-yi))) + assert a[:,:]['Z'].shape[0] == 100 + xdom = a.schema.domain.dim('X').domain[1] + ydom = a.schema.domain.dim('Y').domain[1] + assert xdom == 10 + assert ydom == 10 + # get all data local so we're not hittin s3 all the time + data = a.query(attrs=['Z'], coords=True, use_arrow=False).df[:] + data = data.set_index(['X','Y']) + + for xi in range(xdom): + for yi in range(ydom): + curr = data.loc[xi,yi] + curr.size == 1 + curr.iloc[0].size == 900 + # this should have all indices from 0 to 9 filled. + # if oob error, it's not this test's fault + assert bool(np.all( curr.iloc[0] == ((maxy/resolution) - (yi + 1)) )) \ No newline at end of file diff --git a/tests/test_storage.py b/tests/test_storage.py index f358caa..791145e 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -1,7 +1,10 @@ import tiledb import numpy as np +import pytest +import os +import copy -from silvimetric.resources import Storage, Metrics, Attribute +from silvimetric import Storage, grid_metrics, Attribute, Attributes, StorageConfig, Log from silvimetric import __version__ as svversion class Test_Storage(object): @@ -16,6 +19,11 @@ def test_schema(self, storage: Storage, attrs: list[Attribute]): assert s.has_attr(a.name) # assert s.attr(a.name) == a.schema() + def test_time_reserve(self, storage): + for x in range(5): + time_slot = storage.reserve_time_slot() + assert time_slot == x + 1 + def test_local(self, storage: Storage, attrs: list[Attribute]): with storage.open('r') as st: sc = st.schema @@ -36,6 +44,27 @@ def test_config(self, storage: Storage): assert config.crs == storage.config.crs assert storage.config.version == svversion + def test_metric_dependencies(self, tmp_path_factory, metrics, crs, resolution, attrs, bounds): + ms = copy.deepcopy(metrics) + + path = tmp_path_factory.mktemp("test_tdb") + p = os.path.abspath(path) + log = Log('DEBUG') + + ms[0].dependencies = [Attributes['HeightAboveGround']] + sc = StorageConfig(tdb_dir=p, crs=crs, resolution=resolution, + attrs=attrs, metrics=ms, root=bounds) + + with pytest.raises(ValueError) as e: + Storage.create(sc) + assert str(e.value) == 'Missing required dependency, HeightAboveGround.' + + ms[0].dependencies = [Attributes['NumberOfReturns']] + s = Storage.create(sc) + assert isinstance(s, Storage) + + ms[0].dependencies = [] + def test_metrics(self, storage: Storage): m_list = storage.getMetrics() a_list = storage.getAttributes() @@ -43,9 +72,9 @@ def test_metrics(self, storage: Storage): with storage.open('r') as st: s: tiledb.ArraySchema = st.schema for m in m_list: - assert m.name in Metrics.keys() + assert m.name in grid_metrics.keys() def e_name(att): return s.attr(m.entry_name(att.name)) def schema(att): - return Metrics[m.name].schema(att) + return grid_metrics[m.name].schema(att) assert all([e_name(a) == schema(a) for a in a_list]) diff --git a/tests/test_western.py b/tests/test_western.py index 81e305c..ef103a0 100644 --- a/tests/test_western.py +++ b/tests/test_western.py @@ -1,69 +1,6 @@ import tiledb -import tempfile import numpy as np -from silvimetric.resources import Storage, StorageConfig, Metrics, Attribute, Bounds - -from silvimetric.commands.shatter import shatter -from silvimetric.resources import Storage, Extents, ShatterConfig, Log -from silvimetric import __version__ as svversion -import os, shutil -import pytest - -@pytest.fixture(scope="class") -def WebMercator(): - yield "EPSG:3857" - -@pytest.fixture(scope='class') -def WesternBounds() -> Bounds: - b = Bounds(-14100053.268191, 3058230.975702, -11138180.816218, 6368599.176434) - yield b - -@pytest.fixture(scope='function') -def western_filepath(tmp_path_factory): - - temp_name = next(tempfile._get_candidate_names()) - path = tmp_path_factory.mktemp(temp_name) - yield os.path.abspath(path) - shutil.rmtree(path) - - -@pytest.fixture(scope='function') -def western_config(western_filepath, WesternBounds, resolution, WebMercator, attrs, metrics): - log = Log(20) - yield StorageConfig(tdb_dir = western_filepath, - log = log, - crs = WebMercator, - root = WesternBounds, - resolution = resolution, - attrs = attrs, - metrics = metrics, - version = svversion) - -@pytest.fixture(scope='function') -def western_storage(western_config) -> Storage: - yield Storage.create(western_config) - -@pytest.fixture(scope='function') -def western_pipeline(): - path = os.path.join(os.path.dirname(__file__), "data", - "western_us.json") - assert os.path.exists(path) - yield os.path.abspath(path) - -@pytest.fixture(scope='function') -def western_shatter_config(western_pipeline, western_storage): - log = Log(20) # INFO - st = western_storage.config - - s = ShatterConfig(tdb_dir = st.tdb_dir, - log = log, - filename = western_pipeline, - attrs = st.attrs, - metrics = st.metrics, - bounds=bounds, - debug = True, - date=date) - yield s +from silvimetric import Storage class Test_Western(object): @@ -72,4 +9,3 @@ def test_schema(self, western_storage: Storage): s:tiledb.ArraySchema = st.schema assert s.has_attr('count') assert s.attr('count').dtype == np.int32 - From 9f0e2d5bdb898bd06fe009cfc7a3661fad412106 Mon Sep 17 00:00:00 2001 From: kylemann16 Date: Mon, 16 Sep 2024 09:30:26 -0500 Subject: [PATCH 35/39] fix merge mistake --- src/silvimetric/resources/storage.py | 148 --------------------------- 1 file changed, 148 deletions(-) diff --git a/src/silvimetric/resources/storage.py b/src/silvimetric/resources/storage.py index 4b1d0ec..60eb3eb 100644 --- a/src/silvimetric/resources/storage.py +++ b/src/silvimetric/resources/storage.py @@ -1,5 +1,4 @@ import os -import os import tiledb import numpy as np from datetime import datetime @@ -8,16 +7,12 @@ import json import urllib from typing import Generator -import urllib -from typing import Generator from math import floor -from .config import StorageConfig, ShatterConfig from .config import StorageConfig, ShatterConfig from .metric import Metric, Attribute from .bounds import Bounds -from .bounds import Bounds class Storage: """ Handles storage of shattered data in a TileDB Database. """ @@ -67,9 +62,6 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): # adjust cell bounds if necessary config.root.adjust_to_cell_lines(config.resolution) - # adjust cell bounds if necessary - config.root.adjust_to_cell_lines(config.resolution) - # dims = { d['name']: d['dtype'] for d in pdal.dimensions if d['name'] in config.attrs } xi = floor((config.root.maxx - config.root.minx) / float(config.resolution)) yi = floor((config.root.maxy - config.root.miny) / float(config.resolution)) @@ -83,15 +75,6 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): metric_atts = [m.schema(a) for m in config.metrics for a in config.attrs if a in m.attributes or not m.attributes] - # Check that all attributes required for metric usage are available - att_list = [a.name for a in config.attrs] - required_atts = [d.name for m in config.metrics for d in m.dependencies - if isinstance(d, Attribute)] - for ra in required_atts: - if ra not in att_list: - raise ValueError(f'Missing required dependency, {ra}.') - metric_atts = [m.schema(a) for m in config.metrics for a in config.attrs if a in m.attributes or not m.attributes] - # Check that all attributes required for metric usage are available att_list = [a.name for a in config.attrs] required_atts = [d.name for m in config.metrics for d in m.dependencies @@ -106,8 +89,6 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): schema = tiledb.ArraySchema(domain=domain, sparse=True, attrs=[count_att, *dim_atts, *metric_atts], allows_duplicates=True, capacity=1000) - attrs=[count_att, *dim_atts, *metric_atts], allows_duplicates=True, - capacity=1000) schema.check() tiledb.SparseArray.create(config.tdb_dir, schema) @@ -117,7 +98,6 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): s = Storage(config, ctx) s.saveConfig() - s.saveConfig() return s @@ -125,10 +105,7 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): def from_db(tdb_dir: str): """ Create Storage object from information stored in a database. - Create Storage object from information stored in a database. - :param tdb_dir: TileDB database directory. - :return: Returns the derived storage. :param tdb_dir: TileDB database directory. :return: Returns the derived storage. """ @@ -144,15 +121,11 @@ def saveConfig(self) -> None: """ with self.open('w') as w: w.meta['config'] = str(self.config) - with self.open('w') as w: - w.meta['config'] = str(self.config) def getConfig(self) -> StorageConfig: """ Get the StorageConfig currently in use by the Storage. - Get the StorageConfig currently in use by the Storage. - :return: StorageConfig representing this object. :return: StorageConfig representing this object. """ with self.open('r') as a: @@ -161,55 +134,36 @@ def getConfig(self) -> StorageConfig: return config - def getMetadata(self, key: str, timestamp: int) -> str: def getMetadata(self, key: str, timestamp: int) -> str: """ Return metadata at given key. - Return metadata at given key. :param key: Key to look for in metadata. :param timestamp: Time stamp for querying database. :return: Metadata value found in storage. """ - with self.open('r', (timestamp, timestamp)) as r: - val = r.meta[f'{key}'] - :param key: Key to look for in metadata. - :param timestamp: Time stamp for querying database. - :return: Metadata value found in storage. - """ - with self.open('r', (timestamp, timestamp)) as r: val = r.meta[f'{key}'] return val - def saveMetadata(self, key: str, data: str, timestamp: int) -> None: - def saveMetadata(self, key: str, data: str, timestamp: int) -> None: """ Save metadata to storage. - Save metadata to storage. - :param key: Metadata key to save to. - :param data: Data to save to metadata. :param key: Metadata key to save to. :param data: Data to save to metadata. """ with self.open('w', (timestamp, timestamp)) as w: w.meta[f'{key}'] = data return - with self.open('w', (timestamp, timestamp)) as w: - w.meta[f'{key}'] = data - return def getAttributes(self) -> list[Attribute]: """ Find list of attribute names from storage config. - Find list of attribute names from storage config. - :return: List of attribute names. :return: List of attribute names. """ return self.getConfig().attrs @@ -217,9 +171,7 @@ def getAttributes(self) -> list[Attribute]: def getMetrics(self) -> list[Metric]: """ Find List of metric names from storage config - Find List of metric names from storage config - :return: List of metric names. :return: List of metric names. """ return self.getConfig().metrics @@ -231,7 +183,6 @@ def getDerivedNames(self) -> list[str]: or a.name in [ma.name for ma in m.attributes]] @contextlib.contextmanager - def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, None, None]: def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, None, None]: """ Open stream for TileDB database in given mode and at given timestamp. @@ -248,23 +199,7 @@ def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, No # tiledb and dask have bad interaction with opening an array if # other threads present - Open stream for TileDB database in given mode and at given timestamp. - - :param mode: Mode to open TileDB stream in. Valid options are - 'w', 'r', 'm', 'd'., defaults to 'r'. - :param timestamp: Timestamp to open database at., defaults to None. - :raises Exception: Incorrect Mode was given, only valid modes are 'w' and 'r'. - :raises Exception: Path exists and is not a TileDB array. - :raises Exception: Path does not exist. - :yield: TileDB array context manager. - """ - - # tiledb and dask have bad interaction with opening an array if - # other threads present - if tiledb.object_type(self.config.tdb_dir) == "array": - if mode in ['w', 'r', 'd', 'm']: - tdb = tiledb.open(self.config.tdb_dir, mode, timestamp=timestamp) if mode in ['w', 'r', 'd', 'm']: tdb = tiledb.open(self.config.tdb_dir, mode, timestamp=timestamp) else: @@ -272,7 +207,6 @@ def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, No elif pathlib.Path(self.config.tdb_dir).exists(): raise Exception(f"Path {self.config.tdb_dir} already exists and is not" + " initialized for TileDB access.") - " initialized for TileDB access.") else: raise Exception(f"Path {self.config.tdb_dir} does not exist") @@ -302,41 +236,11 @@ def reserve_time_slot(self) -> int: return time - def reserve_time_slot(self) -> int: - """ - Increment time slot in database and reserve that spot for a new - shatter process. - - :param config: Shatter config will be written as metadata to reserve - time slot. - - :return: Time slot. - """ - with tiledb.open(self.config.tdb_dir, 'r') as r: - latest = json.loads(r.meta['config']) - - time = latest['next_time_slot'] - latest['next_time_slot'] = time + 1 - - with tiledb.open(self.config.tdb_dir, 'w') as w: - w.meta['config'] = json.dumps(latest) - - return time - def get_history(self, start_time: datetime, end_time: datetime, bounds: Bounds, name:str=None): """ Retrieve history of the database at current point in time. - :param start_time: Query parameter, starting datetime of process. - :param end_time: Query parameter, ending datetime of process. - :param bounds: Query parameter, bounds to query by. - :param name: Query paramter, shatter process uuid., by default None - :return: Returns list of array fragments that meet query parameters. - """ - """ - Retrieve history of the database at current point in time. - :param start_time: Query parameter, starting datetime of process. :param end_time: Query parameter, ending datetime of process. :param bounds: Query parameter, bounds to query by. @@ -353,18 +257,6 @@ def get_history(self, start_time: datetime, end_time: datetime, if isinstance(time_range, tuple): time_range = time_range[0] - try: - s_str = self.getMetadata('shatter', time_range) - except KeyError: - continue - s = ShatterConfig.from_string(s_str) - if s.bounds.disjoint(bounds): - continue - time_range = af[idx].timestamp_range - # all shatter processes should be input as a point in time, eg (1,1) - if isinstance(time_range, tuple): - time_range = time_range[0] - try: s_str = self.getMetadata('shatter', time_range) except KeyError: @@ -374,23 +266,10 @@ def get_history(self, start_time: datetime, end_time: datetime, continue # filter name - if name is not None and name != s.name: - continue - # filter name if name is not None and name != s.name: continue # filter dates - if isinstance(s.date, tuple) and len(s.date) == 2: - if s.date[1] < start_time or s.date[0] > end_time: - continue - elif isinstance(s.date, tuple) and len(s.date) == 1: - if s.date[0] < start_time or s.date[0] > end_time: - continue - else: - if s.date < start_time or s.date > end_time: - continue - # filter dates if isinstance(s.date, tuple) and len(s.date) == 2: if s.date[1] < start_time or s.date[0] > end_time: continue @@ -402,7 +281,6 @@ def get_history(self, start_time: datetime, end_time: datetime, continue m.append(s.to_json()) - m.append(s.to_json()) return m @@ -431,32 +309,6 @@ def get_fragments_by_time(self, proc_num: int) -> list[tiledb.FragmentInfo]: af = tiledb.array_fragments(self.config.tdb_dir, include_mbrs=True) return [a for a in af if a.timestamp_range == (proc_num, proc_num)] - def delete(self, proc_num: int) -> ShatterConfig: - def mbrs(self, proc_num: int): - """ - Get minimum bounding rectangle of a given shatter process. If this process - has been finished and consolidated the mbr will be much less granulated - than if the fragments are still intact. Mbrs are represented as tuples - in the form of ((minx, maxx), (miny, maxy)) - - :param proc_num: Process number or time slot of the shatter process. - :return: Returns mbrs that match the given process number. - """ - af_all = self.get_fragments_by_time(proc_num) - mbrs_list = tuple(mbrs for af in af_all for mbrs in af.mbrs) - mbrs = tuple(tuple(tuple(a.item() for a in mb) for mb in m) for m in mbrs_list) - return mbrs - - def get_fragments_by_time(self, proc_num: int) -> list[tiledb.FragmentInfo]: - """ - Get TileDB array fragments from the time slot specified. - - :param proc_num: Requested time slot. - :return: Array fragments from time slot. - """ - af = tiledb.array_fragments(self.config.tdb_dir, include_mbrs=True) - return [a for a in af if a.timestamp_range == (proc_num, proc_num)] - def delete(self, proc_num: int) -> ShatterConfig: """ Delete Shatter process and all associated data from database. From 87cdbb8540c0c831f4f915e791c460447db4b024 Mon Sep 17 00:00:00 2001 From: kylemann16 Date: Mon, 16 Sep 2024 09:31:33 -0500 Subject: [PATCH 36/39] fixing merge mistake --- tests/conftest.py | 66 ----------------------------------------------- 1 file changed, 66 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 0a66416..9849d70 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,11 +3,8 @@ import pdal import copy -import copy - from datetime import datetime from typing import Generator -from typing import Generator from silvimetric import Extents, Bounds, Attribute, Storage from silvimetric import grid_metrics @@ -24,18 +21,6 @@ 'fixtures.metric_fixtures', 'fixtures.dask_fixtures' ] -@pytest.fixture(scope='function') -def tdb_filepath(storage_config) -> Generator[str, None, None]: - yield storage_config.tdb_dir -# pull together fixtures -pytest_plugins=[ - 'fixtures.shatter_fixtures', 'fixtures.extract_fixtures', - 'fixtures.command_fixtures', 'fixtures.chunk_fixtures', - 'fixtures.western_fixtures', 'fixtures.data_fixtures', - 'fixtures.cli_fixtures', 'fixtures.fusion_fixtures', - 'fixtures.metric_fixtures', 'fixtures.dask_fixtures' -] - @pytest.fixture(scope='function') def tdb_filepath(storage_config) -> Generator[str, None, None]: yield storage_config.tdb_dir @@ -46,11 +31,6 @@ def app_config(tdb_filepath, debug=True) -> Generator[ApplicationConfig, None, N app = ApplicationConfig(tdb_dir = tdb_filepath, log = log) yield app -def app_config(tdb_filepath, debug=True) -> Generator[ApplicationConfig, None, None]: - log = Log(20) # INFO - app = ApplicationConfig(tdb_dir = tdb_filepath, - log = log) - yield app @pytest.fixture(scope='function') def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> Generator[StorageConfig, None, None]: @@ -58,12 +38,6 @@ def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> p = os.path.abspath(path) log = Log('DEBUG') - sc = StorageConfig(tdb_dir = p, -def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> Generator[StorageConfig, None, None]: - path = tmp_path_factory.mktemp("test_tdb") - p = os.path.abspath(path) - log = Log('DEBUG') - sc = StorageConfig(tdb_dir = p, log = log, crs = crs, @@ -74,19 +48,12 @@ def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> version = svversion) Storage.create(sc) yield sc - Storage.create(sc) - yield sc @pytest.fixture(scope='function') def storage(storage_config): yield Storage.from_db(storage_config.tdb_dir) -def storage(storage_config): - yield Storage.from_db(storage_config.tdb_dir) @pytest.fixture(scope='function') -def shatter_config(copc_filepath, storage_config, bounds, date) -> Generator[ShatterConfig, None, None]: - log = Log('INFO') # INFO - s = ShatterConfig(tdb_dir = storage_config.tdb_dir, def shatter_config(copc_filepath, storage_config, bounds, date) -> Generator[ShatterConfig, None, None]: log = Log('INFO') # INFO s = ShatterConfig(tdb_dir = storage_config.tdb_dir, @@ -95,18 +62,12 @@ def shatter_config(copc_filepath, storage_config, bounds, date) -> Generator[Sha attrs = storage_config.attrs, metrics = storage_config.metrics, bounds = bounds, - bounds = bounds, debug = True, date = date, tile_size=10) - date = date, tile_size=10) yield s @pytest.fixture(scope='function') -def extract_config(tif_filepath, metrics, shatter_config, extract_attrs): - from silvimetric.commands import shatter - tdb_dir = shatter_config.tdb_dir - shatter.shatter(shatter_config) def extract_config(tif_filepath, metrics, shatter_config, extract_attrs): from silvimetric.commands import shatter tdb_dir = shatter_config.tdb_dir @@ -119,26 +80,16 @@ def extract_config(tif_filepath, metrics, shatter_config, extract_attrs): metrics = metrics) yield c - c = ExtractConfig(tdb_dir = tdb_dir, - log = log, - out_dir = tif_filepath, - attrs = extract_attrs, - metrics = metrics) - yield c - @pytest.fixture(scope='function') def metrics() -> Generator[list[Metric], None, None]: yield [copy.deepcopy(grid_metrics['mean']), copy.deepcopy(grid_metrics['median'])] @pytest.fixture(scope='function') -def bounds(minx, maxx, miny, maxy) -> Generator[Bounds, None, None]: def bounds(minx, maxx, miny, maxy) -> Generator[Bounds, None, None]: b = Bounds(minx, miny, maxx, maxy) yield b -@pytest.fixture(scope='function') -def extents(resolution, bounds) -> Generator[Extents, None, None]: @pytest.fixture(scope='function') def extents(resolution, bounds) -> Generator[Extents, None, None]: yield Extents(bounds,resolution,bounds) @@ -149,54 +100,37 @@ def attrs(dims) -> Generator[list[Attribute], None, None]: ['Z', 'NumberOfReturns', 'ReturnNumber', 'Intensity']] @pytest.fixture(scope="session") -def dims() -> Generator[dict, None, None]: def dims() -> Generator[dict, None, None]: yield { d['name']: d['dtype'] for d in pdal.dimensions } -@pytest.fixture(scope='session') -def resolution() -> Generator[int, None, None]: @pytest.fixture(scope='session') def resolution() -> Generator[int, None, None]: yield 30 -@pytest.fixture(scope='session') -def test_point_count() -> Generator[int, None, None]: @pytest.fixture(scope='session') def test_point_count() -> Generator[int, None, None]: yield 90000 -@pytest.fixture(scope='session') -def minx() -> Generator[float, None, None]: @pytest.fixture(scope='session') def minx() -> Generator[float, None, None]: yield 300 -@pytest.fixture(scope='session') -def miny() -> Generator[float, None, None]: @pytest.fixture(scope='session') def miny() -> Generator[float, None, None]: yield 300 -@pytest.fixture(scope='session') -def maxx() -> Generator[float, None, None]: @pytest.fixture(scope='session') def maxx() -> Generator[float, None, None]: yield 600 -@pytest.fixture(scope='session') -def maxy() -> Generator[float, None, None]: @pytest.fixture(scope='session') def maxy() -> Generator[float, None, None]: yield 600 -@pytest.fixture(scope='session') -def crs() -> Generator[str, None, None]: @pytest.fixture(scope='session') def crs() -> Generator[str, None, None]: yield "EPSG:5070" -@pytest.fixture(scope='session') -def date() -> Generator[datetime, None, None]: @pytest.fixture(scope='session') def date() -> Generator[datetime, None, None]: yield datetime(2011, 1, 1) \ No newline at end of file From 91d300f94d02fc5020e7207bade91feb7dc80f40 Mon Sep 17 00:00:00 2001 From: kylemann16 Date: Mon, 16 Sep 2024 11:08:35 -0500 Subject: [PATCH 37/39] Revert "fixing merge mistake" This reverts commit 87cdbb8540c0c831f4f915e791c460447db4b024. --- tests/conftest.py | 66 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 9849d70..0a66416 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,8 +3,11 @@ import pdal import copy +import copy + from datetime import datetime from typing import Generator +from typing import Generator from silvimetric import Extents, Bounds, Attribute, Storage from silvimetric import grid_metrics @@ -21,6 +24,18 @@ 'fixtures.metric_fixtures', 'fixtures.dask_fixtures' ] +@pytest.fixture(scope='function') +def tdb_filepath(storage_config) -> Generator[str, None, None]: + yield storage_config.tdb_dir +# pull together fixtures +pytest_plugins=[ + 'fixtures.shatter_fixtures', 'fixtures.extract_fixtures', + 'fixtures.command_fixtures', 'fixtures.chunk_fixtures', + 'fixtures.western_fixtures', 'fixtures.data_fixtures', + 'fixtures.cli_fixtures', 'fixtures.fusion_fixtures', + 'fixtures.metric_fixtures', 'fixtures.dask_fixtures' +] + @pytest.fixture(scope='function') def tdb_filepath(storage_config) -> Generator[str, None, None]: yield storage_config.tdb_dir @@ -31,6 +46,11 @@ def app_config(tdb_filepath, debug=True) -> Generator[ApplicationConfig, None, N app = ApplicationConfig(tdb_dir = tdb_filepath, log = log) yield app +def app_config(tdb_filepath, debug=True) -> Generator[ApplicationConfig, None, None]: + log = Log(20) # INFO + app = ApplicationConfig(tdb_dir = tdb_filepath, + log = log) + yield app @pytest.fixture(scope='function') def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> Generator[StorageConfig, None, None]: @@ -38,6 +58,12 @@ def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> p = os.path.abspath(path) log = Log('DEBUG') + sc = StorageConfig(tdb_dir = p, +def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> Generator[StorageConfig, None, None]: + path = tmp_path_factory.mktemp("test_tdb") + p = os.path.abspath(path) + log = Log('DEBUG') + sc = StorageConfig(tdb_dir = p, log = log, crs = crs, @@ -48,12 +74,19 @@ def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> version = svversion) Storage.create(sc) yield sc + Storage.create(sc) + yield sc @pytest.fixture(scope='function') def storage(storage_config): yield Storage.from_db(storage_config.tdb_dir) +def storage(storage_config): + yield Storage.from_db(storage_config.tdb_dir) @pytest.fixture(scope='function') +def shatter_config(copc_filepath, storage_config, bounds, date) -> Generator[ShatterConfig, None, None]: + log = Log('INFO') # INFO + s = ShatterConfig(tdb_dir = storage_config.tdb_dir, def shatter_config(copc_filepath, storage_config, bounds, date) -> Generator[ShatterConfig, None, None]: log = Log('INFO') # INFO s = ShatterConfig(tdb_dir = storage_config.tdb_dir, @@ -62,12 +95,18 @@ def shatter_config(copc_filepath, storage_config, bounds, date) -> Generator[Sha attrs = storage_config.attrs, metrics = storage_config.metrics, bounds = bounds, + bounds = bounds, debug = True, date = date, tile_size=10) + date = date, tile_size=10) yield s @pytest.fixture(scope='function') +def extract_config(tif_filepath, metrics, shatter_config, extract_attrs): + from silvimetric.commands import shatter + tdb_dir = shatter_config.tdb_dir + shatter.shatter(shatter_config) def extract_config(tif_filepath, metrics, shatter_config, extract_attrs): from silvimetric.commands import shatter tdb_dir = shatter_config.tdb_dir @@ -80,16 +119,26 @@ def extract_config(tif_filepath, metrics, shatter_config, extract_attrs): metrics = metrics) yield c + c = ExtractConfig(tdb_dir = tdb_dir, + log = log, + out_dir = tif_filepath, + attrs = extract_attrs, + metrics = metrics) + yield c + @pytest.fixture(scope='function') def metrics() -> Generator[list[Metric], None, None]: yield [copy.deepcopy(grid_metrics['mean']), copy.deepcopy(grid_metrics['median'])] @pytest.fixture(scope='function') +def bounds(minx, maxx, miny, maxy) -> Generator[Bounds, None, None]: def bounds(minx, maxx, miny, maxy) -> Generator[Bounds, None, None]: b = Bounds(minx, miny, maxx, maxy) yield b +@pytest.fixture(scope='function') +def extents(resolution, bounds) -> Generator[Extents, None, None]: @pytest.fixture(scope='function') def extents(resolution, bounds) -> Generator[Extents, None, None]: yield Extents(bounds,resolution,bounds) @@ -100,37 +149,54 @@ def attrs(dims) -> Generator[list[Attribute], None, None]: ['Z', 'NumberOfReturns', 'ReturnNumber', 'Intensity']] @pytest.fixture(scope="session") +def dims() -> Generator[dict, None, None]: def dims() -> Generator[dict, None, None]: yield { d['name']: d['dtype'] for d in pdal.dimensions } +@pytest.fixture(scope='session') +def resolution() -> Generator[int, None, None]: @pytest.fixture(scope='session') def resolution() -> Generator[int, None, None]: yield 30 +@pytest.fixture(scope='session') +def test_point_count() -> Generator[int, None, None]: @pytest.fixture(scope='session') def test_point_count() -> Generator[int, None, None]: yield 90000 +@pytest.fixture(scope='session') +def minx() -> Generator[float, None, None]: @pytest.fixture(scope='session') def minx() -> Generator[float, None, None]: yield 300 +@pytest.fixture(scope='session') +def miny() -> Generator[float, None, None]: @pytest.fixture(scope='session') def miny() -> Generator[float, None, None]: yield 300 +@pytest.fixture(scope='session') +def maxx() -> Generator[float, None, None]: @pytest.fixture(scope='session') def maxx() -> Generator[float, None, None]: yield 600 +@pytest.fixture(scope='session') +def maxy() -> Generator[float, None, None]: @pytest.fixture(scope='session') def maxy() -> Generator[float, None, None]: yield 600 +@pytest.fixture(scope='session') +def crs() -> Generator[str, None, None]: @pytest.fixture(scope='session') def crs() -> Generator[str, None, None]: yield "EPSG:5070" +@pytest.fixture(scope='session') +def date() -> Generator[datetime, None, None]: @pytest.fixture(scope='session') def date() -> Generator[datetime, None, None]: yield datetime(2011, 1, 1) \ No newline at end of file From 65943002bdc33b702654e84712002a880558d6f3 Mon Sep 17 00:00:00 2001 From: kylemann16 Date: Mon, 16 Sep 2024 11:08:51 -0500 Subject: [PATCH 38/39] Revert "fix merge mistake" This reverts commit 9f0e2d5bdb898bd06fe009cfc7a3661fad412106. --- src/silvimetric/resources/storage.py | 148 +++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/src/silvimetric/resources/storage.py b/src/silvimetric/resources/storage.py index 60eb3eb..4b1d0ec 100644 --- a/src/silvimetric/resources/storage.py +++ b/src/silvimetric/resources/storage.py @@ -1,4 +1,5 @@ import os +import os import tiledb import numpy as np from datetime import datetime @@ -7,12 +8,16 @@ import json import urllib from typing import Generator +import urllib +from typing import Generator from math import floor +from .config import StorageConfig, ShatterConfig from .config import StorageConfig, ShatterConfig from .metric import Metric, Attribute from .bounds import Bounds +from .bounds import Bounds class Storage: """ Handles storage of shattered data in a TileDB Database. """ @@ -62,6 +67,9 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): # adjust cell bounds if necessary config.root.adjust_to_cell_lines(config.resolution) + # adjust cell bounds if necessary + config.root.adjust_to_cell_lines(config.resolution) + # dims = { d['name']: d['dtype'] for d in pdal.dimensions if d['name'] in config.attrs } xi = floor((config.root.maxx - config.root.minx) / float(config.resolution)) yi = floor((config.root.maxy - config.root.miny) / float(config.resolution)) @@ -75,6 +83,15 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): metric_atts = [m.schema(a) for m in config.metrics for a in config.attrs if a in m.attributes or not m.attributes] + # Check that all attributes required for metric usage are available + att_list = [a.name for a in config.attrs] + required_atts = [d.name for m in config.metrics for d in m.dependencies + if isinstance(d, Attribute)] + for ra in required_atts: + if ra not in att_list: + raise ValueError(f'Missing required dependency, {ra}.') + metric_atts = [m.schema(a) for m in config.metrics for a in config.attrs if a in m.attributes or not m.attributes] + # Check that all attributes required for metric usage are available att_list = [a.name for a in config.attrs] required_atts = [d.name for m in config.metrics for d in m.dependencies @@ -89,6 +106,8 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): schema = tiledb.ArraySchema(domain=domain, sparse=True, attrs=[count_att, *dim_atts, *metric_atts], allows_duplicates=True, capacity=1000) + attrs=[count_att, *dim_atts, *metric_atts], allows_duplicates=True, + capacity=1000) schema.check() tiledb.SparseArray.create(config.tdb_dir, schema) @@ -98,6 +117,7 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): s = Storage(config, ctx) s.saveConfig() + s.saveConfig() return s @@ -105,7 +125,10 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): def from_db(tdb_dir: str): """ Create Storage object from information stored in a database. + Create Storage object from information stored in a database. + :param tdb_dir: TileDB database directory. + :return: Returns the derived storage. :param tdb_dir: TileDB database directory. :return: Returns the derived storage. """ @@ -121,11 +144,15 @@ def saveConfig(self) -> None: """ with self.open('w') as w: w.meta['config'] = str(self.config) + with self.open('w') as w: + w.meta['config'] = str(self.config) def getConfig(self) -> StorageConfig: """ Get the StorageConfig currently in use by the Storage. + Get the StorageConfig currently in use by the Storage. + :return: StorageConfig representing this object. :return: StorageConfig representing this object. """ with self.open('r') as a: @@ -134,36 +161,55 @@ def getConfig(self) -> StorageConfig: return config + def getMetadata(self, key: str, timestamp: int) -> str: def getMetadata(self, key: str, timestamp: int) -> str: """ Return metadata at given key. + Return metadata at given key. :param key: Key to look for in metadata. :param timestamp: Time stamp for querying database. :return: Metadata value found in storage. """ + with self.open('r', (timestamp, timestamp)) as r: + val = r.meta[f'{key}'] + :param key: Key to look for in metadata. + :param timestamp: Time stamp for querying database. + :return: Metadata value found in storage. + """ + with self.open('r', (timestamp, timestamp)) as r: val = r.meta[f'{key}'] return val + def saveMetadata(self, key: str, data: str, timestamp: int) -> None: + def saveMetadata(self, key: str, data: str, timestamp: int) -> None: """ Save metadata to storage. + Save metadata to storage. + :param key: Metadata key to save to. + :param data: Data to save to metadata. :param key: Metadata key to save to. :param data: Data to save to metadata. """ with self.open('w', (timestamp, timestamp)) as w: w.meta[f'{key}'] = data return + with self.open('w', (timestamp, timestamp)) as w: + w.meta[f'{key}'] = data + return def getAttributes(self) -> list[Attribute]: """ Find list of attribute names from storage config. + Find list of attribute names from storage config. + :return: List of attribute names. :return: List of attribute names. """ return self.getConfig().attrs @@ -171,7 +217,9 @@ def getAttributes(self) -> list[Attribute]: def getMetrics(self) -> list[Metric]: """ Find List of metric names from storage config + Find List of metric names from storage config + :return: List of metric names. :return: List of metric names. """ return self.getConfig().metrics @@ -183,6 +231,7 @@ def getDerivedNames(self) -> list[str]: or a.name in [ma.name for ma in m.attributes]] @contextlib.contextmanager + def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, None, None]: def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, None, None]: """ Open stream for TileDB database in given mode and at given timestamp. @@ -199,7 +248,23 @@ def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, No # tiledb and dask have bad interaction with opening an array if # other threads present + Open stream for TileDB database in given mode and at given timestamp. + + :param mode: Mode to open TileDB stream in. Valid options are + 'w', 'r', 'm', 'd'., defaults to 'r'. + :param timestamp: Timestamp to open database at., defaults to None. + :raises Exception: Incorrect Mode was given, only valid modes are 'w' and 'r'. + :raises Exception: Path exists and is not a TileDB array. + :raises Exception: Path does not exist. + :yield: TileDB array context manager. + """ + + # tiledb and dask have bad interaction with opening an array if + # other threads present + if tiledb.object_type(self.config.tdb_dir) == "array": + if mode in ['w', 'r', 'd', 'm']: + tdb = tiledb.open(self.config.tdb_dir, mode, timestamp=timestamp) if mode in ['w', 'r', 'd', 'm']: tdb = tiledb.open(self.config.tdb_dir, mode, timestamp=timestamp) else: @@ -207,6 +272,7 @@ def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, No elif pathlib.Path(self.config.tdb_dir).exists(): raise Exception(f"Path {self.config.tdb_dir} already exists and is not" + " initialized for TileDB access.") + " initialized for TileDB access.") else: raise Exception(f"Path {self.config.tdb_dir} does not exist") @@ -236,11 +302,41 @@ def reserve_time_slot(self) -> int: return time + def reserve_time_slot(self) -> int: + """ + Increment time slot in database and reserve that spot for a new + shatter process. + + :param config: Shatter config will be written as metadata to reserve + time slot. + + :return: Time slot. + """ + with tiledb.open(self.config.tdb_dir, 'r') as r: + latest = json.loads(r.meta['config']) + + time = latest['next_time_slot'] + latest['next_time_slot'] = time + 1 + + with tiledb.open(self.config.tdb_dir, 'w') as w: + w.meta['config'] = json.dumps(latest) + + return time + def get_history(self, start_time: datetime, end_time: datetime, bounds: Bounds, name:str=None): """ Retrieve history of the database at current point in time. + :param start_time: Query parameter, starting datetime of process. + :param end_time: Query parameter, ending datetime of process. + :param bounds: Query parameter, bounds to query by. + :param name: Query paramter, shatter process uuid., by default None + :return: Returns list of array fragments that meet query parameters. + """ + """ + Retrieve history of the database at current point in time. + :param start_time: Query parameter, starting datetime of process. :param end_time: Query parameter, ending datetime of process. :param bounds: Query parameter, bounds to query by. @@ -257,6 +353,18 @@ def get_history(self, start_time: datetime, end_time: datetime, if isinstance(time_range, tuple): time_range = time_range[0] + try: + s_str = self.getMetadata('shatter', time_range) + except KeyError: + continue + s = ShatterConfig.from_string(s_str) + if s.bounds.disjoint(bounds): + continue + time_range = af[idx].timestamp_range + # all shatter processes should be input as a point in time, eg (1,1) + if isinstance(time_range, tuple): + time_range = time_range[0] + try: s_str = self.getMetadata('shatter', time_range) except KeyError: @@ -266,10 +374,23 @@ def get_history(self, start_time: datetime, end_time: datetime, continue # filter name + if name is not None and name != s.name: + continue + # filter name if name is not None and name != s.name: continue # filter dates + if isinstance(s.date, tuple) and len(s.date) == 2: + if s.date[1] < start_time or s.date[0] > end_time: + continue + elif isinstance(s.date, tuple) and len(s.date) == 1: + if s.date[0] < start_time or s.date[0] > end_time: + continue + else: + if s.date < start_time or s.date > end_time: + continue + # filter dates if isinstance(s.date, tuple) and len(s.date) == 2: if s.date[1] < start_time or s.date[0] > end_time: continue @@ -281,6 +402,7 @@ def get_history(self, start_time: datetime, end_time: datetime, continue m.append(s.to_json()) + m.append(s.to_json()) return m @@ -309,6 +431,32 @@ def get_fragments_by_time(self, proc_num: int) -> list[tiledb.FragmentInfo]: af = tiledb.array_fragments(self.config.tdb_dir, include_mbrs=True) return [a for a in af if a.timestamp_range == (proc_num, proc_num)] + def delete(self, proc_num: int) -> ShatterConfig: + def mbrs(self, proc_num: int): + """ + Get minimum bounding rectangle of a given shatter process. If this process + has been finished and consolidated the mbr will be much less granulated + than if the fragments are still intact. Mbrs are represented as tuples + in the form of ((minx, maxx), (miny, maxy)) + + :param proc_num: Process number or time slot of the shatter process. + :return: Returns mbrs that match the given process number. + """ + af_all = self.get_fragments_by_time(proc_num) + mbrs_list = tuple(mbrs for af in af_all for mbrs in af.mbrs) + mbrs = tuple(tuple(tuple(a.item() for a in mb) for mb in m) for m in mbrs_list) + return mbrs + + def get_fragments_by_time(self, proc_num: int) -> list[tiledb.FragmentInfo]: + """ + Get TileDB array fragments from the time slot specified. + + :param proc_num: Requested time slot. + :return: Array fragments from time slot. + """ + af = tiledb.array_fragments(self.config.tdb_dir, include_mbrs=True) + return [a for a in af if a.timestamp_range == (proc_num, proc_num)] + def delete(self, proc_num: int) -> ShatterConfig: """ Delete Shatter process and all associated data from database. From 0239aade4ac20fa9c0b623a4d109f18ccc4ff743 Mon Sep 17 00:00:00 2001 From: kylemann16 Date: Mon, 16 Sep 2024 11:11:23 -0500 Subject: [PATCH 39/39] Revert "Merge remote-tracking branch 'origin/main' into metric_merge" This reverts commit 4d3cfc9af8f0d062b69b8321a98a554682f8b9e9, reversing changes made to 9a6240bf4b360bf3fb0542ad4e6abdcb8bd3e916. --- docs/source/conf.py | 3 - docs/source/index.rst | 4 +- environment.yml | 2 +- src/silvimetric/__init__.py | 2 +- src/silvimetric/cli/common.py | 9 -- src/silvimetric/commands/shatter.py | 53 ---------- src/silvimetric/resources/config.py | 1 + src/silvimetric/resources/storage.py | 148 --------------------------- tests/conftest.py | 66 ------------ tests/test_storage.py | 29 ------ 10 files changed, 4 insertions(+), 313 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index aabc459..2e72f22 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -37,7 +37,6 @@ # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "") html_theme = "sphinx_rtd_theme" html_static_path = ['_static'] html_context = { @@ -49,8 +48,6 @@ 'conf_py_path': '/docs/source/' } -if os.environ.get("READXTHEDOCS", "") == "True": - html_context["READTHEDOCS"] = True def read_version(filename): diff --git a/docs/source/index.rst b/docs/source/index.rst index 936e7e5..cd59787 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -12,9 +12,7 @@ SilviMetric is an open source library and set of utilities from data into raster and raster-like products. Find out more about SilviMetric by visiting :ref:`about`. A slide deck about -SilviMetric is also available on `Google Slides `__, -and examples are available for viewing in `Google Colab `__. - +SilviMetric is also available on `Google Slides `__. .. toctree:: :caption: Contents diff --git a/environment.yml b/environment.yml index 09923ed..62257ac 100644 --- a/environment.yml +++ b/environment.yml @@ -18,4 +18,4 @@ dependencies: - python-json-logger - dill - pandas - - lmoments3 + - lmoments3 \ No newline at end of file diff --git a/src/silvimetric/__init__.py b/src/silvimetric/__init__.py index 4035619..23c8d91 100644 --- a/src/silvimetric/__init__.py +++ b/src/silvimetric/__init__.py @@ -1,4 +1,4 @@ -__version__ = '1.2.3' +__version__ = '1.2.1' from .resources.bounds import Bounds from .resources.extents import Extents diff --git a/src/silvimetric/cli/common.py b/src/silvimetric/cli/common.py index aef04a1..a21cf62 100644 --- a/src/silvimetric/cli/common.py +++ b/src/silvimetric/cli/common.py @@ -44,15 +44,6 @@ def convert(self, value, param, ctx) -> list[Attribute]: return Attributes[value] else: self.fail(f"{value!r} is of an invalid type, {e}", param, ctx) - if isinstance(value, list): - try: - return [Attributes[a] for a in value] - except Exception as e: - self.fail(f"{value!r} is not available in Attributes, {e}", param, ctx) - elif isinstance(value, str): - return Attributes[value] - else: - self.fail(f"{value!r} is of an invalid type, {e}", param, ctx) class MetricParamType(click.ParamType): name="metrics" diff --git a/src/silvimetric/commands/shatter.py b/src/silvimetric/commands/shatter.py index 19037a7..bd9213c 100644 --- a/src/silvimetric/commands/shatter.py +++ b/src/silvimetric/commands/shatter.py @@ -1,7 +1,3 @@ -import dask.diagnostics -import dask.distributed -import dask.diagnostics -import dask.distributed import numpy as np import signal import datetime @@ -9,10 +5,6 @@ from typing import Generator import pandas as pd import tiledb -import copy -from typing import Generator -import pandas as pd -import tiledb from dask.distributed import as_completed, futures_of, CancelledError from distributed.client import _get_global_client as get_client @@ -34,24 +26,9 @@ def get_data(extents: Extents, filename: str, storage: Storage): """ data = Data(filename, storage.config, bounds = extents.bounds) p = data.pipeline - p = data.pipeline data.execute() return p.get_dataframe(0) -def arrange(points: pd.DataFrame, leaf, attrs: list[str]): - """ - Arrange data to fit key-value TileDB input format. - - :param data: Tuple of indices and point data array (xis, yis, data). - :param leaf: :class:`silvimetric.resources.extents.Extent` being operated on. - :param attrs: List of attribute names. - :raises Exception: Missing attribute error. - :return: None if no work is done, or a tuple of indices and rearranged data. - """ - if points is None: - return None - return p.get_dataframe(0) - def arrange(points: pd.DataFrame, leaf, attrs: list[str]): """ Arrange data to fit key-value TileDB input format. @@ -151,7 +128,6 @@ def write(data_in, storage, timestamp): pc = data_in['count'].sum().item() p = copy.deepcopy(pc) - del pc, data_in return p @@ -202,8 +178,6 @@ def kill_gracefully(signum, frame): client.close() end_time = datetime.datetime.now().timestamp() * 1000 - storage.consolidate_shatter(config.time_slot) - storage.consolidate_shatter(config.time_slot) config.end_time = end_time config.mbrs = storage.mbrs(config.time_slot) @@ -212,16 +186,9 @@ def kill_gracefully(signum, frame): storage.saveMetadata('shatter', str(config), config.time_slot) config.log.info('Quitting.') - config.mbrs = storage.mbrs(config.time_slot) - config.finished=False - config.log.info('Saving config before quitting...') - - storage.saveMetadata('shatter', str(config), config.time_slot) - config.log.info('Quitting.') signal.signal(signal.SIGINT, kill_gracefully) - processes = get_processes(leaves, config, storage) processes = get_processes(leaves, config, storage) ## If dask is distributed, use the futures feature @@ -235,7 +202,6 @@ def kill_gracefully(signum, frame): for pc in pack: config.point_count = config.point_count + pc del pc - del pc end_time = datetime.datetime.now().timestamp() * 1000 config.end_time = end_time @@ -270,10 +236,6 @@ def shatter(config: ShatterConfig) -> int: data = Data(config.filename, storage.config, config.bounds) extents = Extents.from_sub(config.tdb_dir, data.bounds) - config.log.info('Beginning shatter process...') - config.log.debug(f'Shatter Config: {config}') - config.log.debug(f'Data: {str(data)}') - config.log.debug(f'Extents: {str(extents)}') config.log.info('Beginning shatter process...') config.log.debug(f'Shatter Config: {config}') config.log.debug(f'Data: {str(data)}') @@ -283,22 +245,13 @@ def shatter(config: ShatterConfig) -> int: config.log.warning("Selected scheduler type does not support" "continuously updated config information.") - if not config.time_slot: # defaults to 0, which is reserved for storage cfg - config.time_slot = storage.reserve_time_slot() if not config.time_slot: # defaults to 0, which is reserved for storage cfg config.time_slot = storage.reserve_time_slot() - if config.bounds is None: - config.bounds = extents.bounds if config.bounds is None: config.bounds = extents.bounds config.log.debug('Grabbing leaf nodes...') - if config.tile_size is not None: - leaves = extents.get_leaf_children(config.tile_size) - else: - leaves = extents.chunk(data, 100) - config.log.debug('Grabbing leaf nodes...') if config.tile_size is not None: leaves = extents.get_leaf_children(config.tile_size) else: @@ -307,13 +260,7 @@ def shatter(config: ShatterConfig) -> int: # Begin main operations config.log.debug('Fetching and arranging data...') pc = run(leaves, config, storage) - # Begin main operations - config.log.debug('Fetching and arranging data...') - pc = run(leaves, config, storage) - #consolidate the fragments in this time slot down to just one - storage.consolidate_shatter(config.time_slot) - return pc #consolidate the fragments in this time slot down to just one storage.consolidate_shatter(config.time_slot) return pc \ No newline at end of file diff --git a/src/silvimetric/resources/config.py b/src/silvimetric/resources/config.py index 19f02fe..ce0a81e 100644 --- a/src/silvimetric/resources/config.py +++ b/src/silvimetric/resources/config.py @@ -123,6 +123,7 @@ def to_json(self): d['metrics'] = [m.to_json() for m in self.metrics] d['crs'] = json.loads(self.crs.to_json()) d['root'] = self.root.to_json() + return d @classmethod diff --git a/src/silvimetric/resources/storage.py b/src/silvimetric/resources/storage.py index 4b1d0ec..60eb3eb 100644 --- a/src/silvimetric/resources/storage.py +++ b/src/silvimetric/resources/storage.py @@ -1,5 +1,4 @@ import os -import os import tiledb import numpy as np from datetime import datetime @@ -8,16 +7,12 @@ import json import urllib from typing import Generator -import urllib -from typing import Generator from math import floor -from .config import StorageConfig, ShatterConfig from .config import StorageConfig, ShatterConfig from .metric import Metric, Attribute from .bounds import Bounds -from .bounds import Bounds class Storage: """ Handles storage of shattered data in a TileDB Database. """ @@ -67,9 +62,6 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): # adjust cell bounds if necessary config.root.adjust_to_cell_lines(config.resolution) - # adjust cell bounds if necessary - config.root.adjust_to_cell_lines(config.resolution) - # dims = { d['name']: d['dtype'] for d in pdal.dimensions if d['name'] in config.attrs } xi = floor((config.root.maxx - config.root.minx) / float(config.resolution)) yi = floor((config.root.maxy - config.root.miny) / float(config.resolution)) @@ -83,15 +75,6 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): metric_atts = [m.schema(a) for m in config.metrics for a in config.attrs if a in m.attributes or not m.attributes] - # Check that all attributes required for metric usage are available - att_list = [a.name for a in config.attrs] - required_atts = [d.name for m in config.metrics for d in m.dependencies - if isinstance(d, Attribute)] - for ra in required_atts: - if ra not in att_list: - raise ValueError(f'Missing required dependency, {ra}.') - metric_atts = [m.schema(a) for m in config.metrics for a in config.attrs if a in m.attributes or not m.attributes] - # Check that all attributes required for metric usage are available att_list = [a.name for a in config.attrs] required_atts = [d.name for m in config.metrics for d in m.dependencies @@ -106,8 +89,6 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): schema = tiledb.ArraySchema(domain=domain, sparse=True, attrs=[count_att, *dim_atts, *metric_atts], allows_duplicates=True, capacity=1000) - attrs=[count_att, *dim_atts, *metric_atts], allows_duplicates=True, - capacity=1000) schema.check() tiledb.SparseArray.create(config.tdb_dir, schema) @@ -117,7 +98,6 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): s = Storage(config, ctx) s.saveConfig() - s.saveConfig() return s @@ -125,10 +105,7 @@ def create( config:StorageConfig, ctx:tiledb.Ctx=None): def from_db(tdb_dir: str): """ Create Storage object from information stored in a database. - Create Storage object from information stored in a database. - :param tdb_dir: TileDB database directory. - :return: Returns the derived storage. :param tdb_dir: TileDB database directory. :return: Returns the derived storage. """ @@ -144,15 +121,11 @@ def saveConfig(self) -> None: """ with self.open('w') as w: w.meta['config'] = str(self.config) - with self.open('w') as w: - w.meta['config'] = str(self.config) def getConfig(self) -> StorageConfig: """ Get the StorageConfig currently in use by the Storage. - Get the StorageConfig currently in use by the Storage. - :return: StorageConfig representing this object. :return: StorageConfig representing this object. """ with self.open('r') as a: @@ -161,55 +134,36 @@ def getConfig(self) -> StorageConfig: return config - def getMetadata(self, key: str, timestamp: int) -> str: def getMetadata(self, key: str, timestamp: int) -> str: """ Return metadata at given key. - Return metadata at given key. :param key: Key to look for in metadata. :param timestamp: Time stamp for querying database. :return: Metadata value found in storage. """ - with self.open('r', (timestamp, timestamp)) as r: - val = r.meta[f'{key}'] - :param key: Key to look for in metadata. - :param timestamp: Time stamp for querying database. - :return: Metadata value found in storage. - """ - with self.open('r', (timestamp, timestamp)) as r: val = r.meta[f'{key}'] return val - def saveMetadata(self, key: str, data: str, timestamp: int) -> None: - def saveMetadata(self, key: str, data: str, timestamp: int) -> None: """ Save metadata to storage. - Save metadata to storage. - :param key: Metadata key to save to. - :param data: Data to save to metadata. :param key: Metadata key to save to. :param data: Data to save to metadata. """ with self.open('w', (timestamp, timestamp)) as w: w.meta[f'{key}'] = data return - with self.open('w', (timestamp, timestamp)) as w: - w.meta[f'{key}'] = data - return def getAttributes(self) -> list[Attribute]: """ Find list of attribute names from storage config. - Find list of attribute names from storage config. - :return: List of attribute names. :return: List of attribute names. """ return self.getConfig().attrs @@ -217,9 +171,7 @@ def getAttributes(self) -> list[Attribute]: def getMetrics(self) -> list[Metric]: """ Find List of metric names from storage config - Find List of metric names from storage config - :return: List of metric names. :return: List of metric names. """ return self.getConfig().metrics @@ -231,7 +183,6 @@ def getDerivedNames(self) -> list[str]: or a.name in [ma.name for ma in m.attributes]] @contextlib.contextmanager - def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, None, None]: def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, None, None]: """ Open stream for TileDB database in given mode and at given timestamp. @@ -248,23 +199,7 @@ def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, No # tiledb and dask have bad interaction with opening an array if # other threads present - Open stream for TileDB database in given mode and at given timestamp. - - :param mode: Mode to open TileDB stream in. Valid options are - 'w', 'r', 'm', 'd'., defaults to 'r'. - :param timestamp: Timestamp to open database at., defaults to None. - :raises Exception: Incorrect Mode was given, only valid modes are 'w' and 'r'. - :raises Exception: Path exists and is not a TileDB array. - :raises Exception: Path does not exist. - :yield: TileDB array context manager. - """ - - # tiledb and dask have bad interaction with opening an array if - # other threads present - if tiledb.object_type(self.config.tdb_dir) == "array": - if mode in ['w', 'r', 'd', 'm']: - tdb = tiledb.open(self.config.tdb_dir, mode, timestamp=timestamp) if mode in ['w', 'r', 'd', 'm']: tdb = tiledb.open(self.config.tdb_dir, mode, timestamp=timestamp) else: @@ -272,7 +207,6 @@ def open(self, mode:str='r', timestamp=None) -> Generator[tiledb.SparseArray, No elif pathlib.Path(self.config.tdb_dir).exists(): raise Exception(f"Path {self.config.tdb_dir} already exists and is not" + " initialized for TileDB access.") - " initialized for TileDB access.") else: raise Exception(f"Path {self.config.tdb_dir} does not exist") @@ -302,41 +236,11 @@ def reserve_time_slot(self) -> int: return time - def reserve_time_slot(self) -> int: - """ - Increment time slot in database and reserve that spot for a new - shatter process. - - :param config: Shatter config will be written as metadata to reserve - time slot. - - :return: Time slot. - """ - with tiledb.open(self.config.tdb_dir, 'r') as r: - latest = json.loads(r.meta['config']) - - time = latest['next_time_slot'] - latest['next_time_slot'] = time + 1 - - with tiledb.open(self.config.tdb_dir, 'w') as w: - w.meta['config'] = json.dumps(latest) - - return time - def get_history(self, start_time: datetime, end_time: datetime, bounds: Bounds, name:str=None): """ Retrieve history of the database at current point in time. - :param start_time: Query parameter, starting datetime of process. - :param end_time: Query parameter, ending datetime of process. - :param bounds: Query parameter, bounds to query by. - :param name: Query paramter, shatter process uuid., by default None - :return: Returns list of array fragments that meet query parameters. - """ - """ - Retrieve history of the database at current point in time. - :param start_time: Query parameter, starting datetime of process. :param end_time: Query parameter, ending datetime of process. :param bounds: Query parameter, bounds to query by. @@ -353,18 +257,6 @@ def get_history(self, start_time: datetime, end_time: datetime, if isinstance(time_range, tuple): time_range = time_range[0] - try: - s_str = self.getMetadata('shatter', time_range) - except KeyError: - continue - s = ShatterConfig.from_string(s_str) - if s.bounds.disjoint(bounds): - continue - time_range = af[idx].timestamp_range - # all shatter processes should be input as a point in time, eg (1,1) - if isinstance(time_range, tuple): - time_range = time_range[0] - try: s_str = self.getMetadata('shatter', time_range) except KeyError: @@ -374,23 +266,10 @@ def get_history(self, start_time: datetime, end_time: datetime, continue # filter name - if name is not None and name != s.name: - continue - # filter name if name is not None and name != s.name: continue # filter dates - if isinstance(s.date, tuple) and len(s.date) == 2: - if s.date[1] < start_time or s.date[0] > end_time: - continue - elif isinstance(s.date, tuple) and len(s.date) == 1: - if s.date[0] < start_time or s.date[0] > end_time: - continue - else: - if s.date < start_time or s.date > end_time: - continue - # filter dates if isinstance(s.date, tuple) and len(s.date) == 2: if s.date[1] < start_time or s.date[0] > end_time: continue @@ -402,7 +281,6 @@ def get_history(self, start_time: datetime, end_time: datetime, continue m.append(s.to_json()) - m.append(s.to_json()) return m @@ -431,32 +309,6 @@ def get_fragments_by_time(self, proc_num: int) -> list[tiledb.FragmentInfo]: af = tiledb.array_fragments(self.config.tdb_dir, include_mbrs=True) return [a for a in af if a.timestamp_range == (proc_num, proc_num)] - def delete(self, proc_num: int) -> ShatterConfig: - def mbrs(self, proc_num: int): - """ - Get minimum bounding rectangle of a given shatter process. If this process - has been finished and consolidated the mbr will be much less granulated - than if the fragments are still intact. Mbrs are represented as tuples - in the form of ((minx, maxx), (miny, maxy)) - - :param proc_num: Process number or time slot of the shatter process. - :return: Returns mbrs that match the given process number. - """ - af_all = self.get_fragments_by_time(proc_num) - mbrs_list = tuple(mbrs for af in af_all for mbrs in af.mbrs) - mbrs = tuple(tuple(tuple(a.item() for a in mb) for mb in m) for m in mbrs_list) - return mbrs - - def get_fragments_by_time(self, proc_num: int) -> list[tiledb.FragmentInfo]: - """ - Get TileDB array fragments from the time slot specified. - - :param proc_num: Requested time slot. - :return: Array fragments from time slot. - """ - af = tiledb.array_fragments(self.config.tdb_dir, include_mbrs=True) - return [a for a in af if a.timestamp_range == (proc_num, proc_num)] - def delete(self, proc_num: int) -> ShatterConfig: """ Delete Shatter process and all associated data from database. diff --git a/tests/conftest.py b/tests/conftest.py index 0a66416..9849d70 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,11 +3,8 @@ import pdal import copy -import copy - from datetime import datetime from typing import Generator -from typing import Generator from silvimetric import Extents, Bounds, Attribute, Storage from silvimetric import grid_metrics @@ -24,18 +21,6 @@ 'fixtures.metric_fixtures', 'fixtures.dask_fixtures' ] -@pytest.fixture(scope='function') -def tdb_filepath(storage_config) -> Generator[str, None, None]: - yield storage_config.tdb_dir -# pull together fixtures -pytest_plugins=[ - 'fixtures.shatter_fixtures', 'fixtures.extract_fixtures', - 'fixtures.command_fixtures', 'fixtures.chunk_fixtures', - 'fixtures.western_fixtures', 'fixtures.data_fixtures', - 'fixtures.cli_fixtures', 'fixtures.fusion_fixtures', - 'fixtures.metric_fixtures', 'fixtures.dask_fixtures' -] - @pytest.fixture(scope='function') def tdb_filepath(storage_config) -> Generator[str, None, None]: yield storage_config.tdb_dir @@ -46,11 +31,6 @@ def app_config(tdb_filepath, debug=True) -> Generator[ApplicationConfig, None, N app = ApplicationConfig(tdb_dir = tdb_filepath, log = log) yield app -def app_config(tdb_filepath, debug=True) -> Generator[ApplicationConfig, None, None]: - log = Log(20) # INFO - app = ApplicationConfig(tdb_dir = tdb_filepath, - log = log) - yield app @pytest.fixture(scope='function') def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> Generator[StorageConfig, None, None]: @@ -58,12 +38,6 @@ def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> p = os.path.abspath(path) log = Log('DEBUG') - sc = StorageConfig(tdb_dir = p, -def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> Generator[StorageConfig, None, None]: - path = tmp_path_factory.mktemp("test_tdb") - p = os.path.abspath(path) - log = Log('DEBUG') - sc = StorageConfig(tdb_dir = p, log = log, crs = crs, @@ -74,19 +48,12 @@ def storage_config(tmp_path_factory, bounds, resolution, crs, attrs, metrics) -> version = svversion) Storage.create(sc) yield sc - Storage.create(sc) - yield sc @pytest.fixture(scope='function') def storage(storage_config): yield Storage.from_db(storage_config.tdb_dir) -def storage(storage_config): - yield Storage.from_db(storage_config.tdb_dir) @pytest.fixture(scope='function') -def shatter_config(copc_filepath, storage_config, bounds, date) -> Generator[ShatterConfig, None, None]: - log = Log('INFO') # INFO - s = ShatterConfig(tdb_dir = storage_config.tdb_dir, def shatter_config(copc_filepath, storage_config, bounds, date) -> Generator[ShatterConfig, None, None]: log = Log('INFO') # INFO s = ShatterConfig(tdb_dir = storage_config.tdb_dir, @@ -95,18 +62,12 @@ def shatter_config(copc_filepath, storage_config, bounds, date) -> Generator[Sha attrs = storage_config.attrs, metrics = storage_config.metrics, bounds = bounds, - bounds = bounds, debug = True, date = date, tile_size=10) - date = date, tile_size=10) yield s @pytest.fixture(scope='function') -def extract_config(tif_filepath, metrics, shatter_config, extract_attrs): - from silvimetric.commands import shatter - tdb_dir = shatter_config.tdb_dir - shatter.shatter(shatter_config) def extract_config(tif_filepath, metrics, shatter_config, extract_attrs): from silvimetric.commands import shatter tdb_dir = shatter_config.tdb_dir @@ -119,26 +80,16 @@ def extract_config(tif_filepath, metrics, shatter_config, extract_attrs): metrics = metrics) yield c - c = ExtractConfig(tdb_dir = tdb_dir, - log = log, - out_dir = tif_filepath, - attrs = extract_attrs, - metrics = metrics) - yield c - @pytest.fixture(scope='function') def metrics() -> Generator[list[Metric], None, None]: yield [copy.deepcopy(grid_metrics['mean']), copy.deepcopy(grid_metrics['median'])] @pytest.fixture(scope='function') -def bounds(minx, maxx, miny, maxy) -> Generator[Bounds, None, None]: def bounds(minx, maxx, miny, maxy) -> Generator[Bounds, None, None]: b = Bounds(minx, miny, maxx, maxy) yield b -@pytest.fixture(scope='function') -def extents(resolution, bounds) -> Generator[Extents, None, None]: @pytest.fixture(scope='function') def extents(resolution, bounds) -> Generator[Extents, None, None]: yield Extents(bounds,resolution,bounds) @@ -149,54 +100,37 @@ def attrs(dims) -> Generator[list[Attribute], None, None]: ['Z', 'NumberOfReturns', 'ReturnNumber', 'Intensity']] @pytest.fixture(scope="session") -def dims() -> Generator[dict, None, None]: def dims() -> Generator[dict, None, None]: yield { d['name']: d['dtype'] for d in pdal.dimensions } -@pytest.fixture(scope='session') -def resolution() -> Generator[int, None, None]: @pytest.fixture(scope='session') def resolution() -> Generator[int, None, None]: yield 30 -@pytest.fixture(scope='session') -def test_point_count() -> Generator[int, None, None]: @pytest.fixture(scope='session') def test_point_count() -> Generator[int, None, None]: yield 90000 -@pytest.fixture(scope='session') -def minx() -> Generator[float, None, None]: @pytest.fixture(scope='session') def minx() -> Generator[float, None, None]: yield 300 -@pytest.fixture(scope='session') -def miny() -> Generator[float, None, None]: @pytest.fixture(scope='session') def miny() -> Generator[float, None, None]: yield 300 -@pytest.fixture(scope='session') -def maxx() -> Generator[float, None, None]: @pytest.fixture(scope='session') def maxx() -> Generator[float, None, None]: yield 600 -@pytest.fixture(scope='session') -def maxy() -> Generator[float, None, None]: @pytest.fixture(scope='session') def maxy() -> Generator[float, None, None]: yield 600 -@pytest.fixture(scope='session') -def crs() -> Generator[str, None, None]: @pytest.fixture(scope='session') def crs() -> Generator[str, None, None]: yield "EPSG:5070" -@pytest.fixture(scope='session') -def date() -> Generator[datetime, None, None]: @pytest.fixture(scope='session') def date() -> Generator[datetime, None, None]: yield datetime(2011, 1, 1) \ No newline at end of file diff --git a/tests/test_storage.py b/tests/test_storage.py index d5899be..791145e 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -3,9 +3,6 @@ import pytest import os import copy -import pytest -import os -import copy from silvimetric import Storage, grid_metrics, Attribute, Attributes, StorageConfig, Log from silvimetric import __version__ as svversion @@ -27,11 +24,6 @@ def test_time_reserve(self, storage): time_slot = storage.reserve_time_slot() assert time_slot == x + 1 - def test_time_reserve(self, storage): - for x in range(5): - time_slot = storage.reserve_time_slot() - assert time_slot == x + 1 - def test_local(self, storage: Storage, attrs: list[Attribute]): with storage.open('r') as st: sc = st.schema @@ -73,27 +65,6 @@ def test_metric_dependencies(self, tmp_path_factory, metrics, crs, resolution, a ms[0].dependencies = [] - def test_metric_dependencies(self, tmp_path_factory, metrics, crs, resolution, attrs, bounds): - ms = copy.deepcopy(metrics) - - path = tmp_path_factory.mktemp("test_tdb") - p = os.path.abspath(path) - log = Log('DEBUG') - - ms[0].dependencies = [Attributes['HeightAboveGround']] - sc = StorageConfig(tdb_dir=p, crs=crs, resolution=resolution, - attrs=attrs, metrics=ms, root=bounds) - - with pytest.raises(ValueError) as e: - Storage.create(sc) - assert str(e.value) == 'Missing required dependency, HeightAboveGround.' - - ms[0].dependencies = [Attributes['NumberOfReturns']] - s = Storage.create(sc) - assert isinstance(s, Storage) - - ms[0].dependencies = [] - def test_metrics(self, storage: Storage): m_list = storage.getMetrics() a_list = storage.getAttributes()

CE;10yP2zE&SgYP) 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