1
1
import _ from 'underscore' ;
2
+ import fasterMedian from 'faster-median' ;
2
3
import get from '../get' ;
3
4
import utils from '../utils' ;
4
5
import convertGeometry from '../convert-geometry' ;
5
6
import intersectPolygon from '../intersect-polygon' ;
6
-
7
- const getMedian = values => {
8
-
9
- // sort values
10
- values . sort ( ) ;
11
- const valuesLength = values . length ;
12
-
13
- // pull middle value from sorted array
14
- if ( valuesLength % 2 !== 0 ) {
15
- const middle = Math . floor ( valuesLength / 2 ) ;
16
- return values [ middle ] ;
17
- } else {
18
- const middle = valuesLength / 2 ;
19
- return ( values [ middle - 1 ] + values [ middle ] ) / 2 ;
7
+ import load from "../load" ;
8
+
9
+ const getMedianForRect = ( values , noDataValue ) => {
10
+ // median values
11
+ const medians = [ ] ;
12
+ for ( let bandIndex = 0 ; bandIndex < values . length ; bandIndex ++ ) {
13
+ const band = values [ bandIndex ] ;
14
+ const counts = utils . countValuesInTable ( band , noDataValue ) ;
15
+ const numCellsWithValue = utils . sum ( _ . values ( counts ) ) ;
16
+ const sortedCounts = _ . pairs ( counts ) . sort ( ( pair1 , pair2 ) => Number ( pair1 [ 0 ] ) - Number ( pair2 [ 0 ] ) ) ;
17
+ //console.log("sortedCounts:", sortedCounts);
18
+ const middle = numCellsWithValue / 2 ;
19
+ let runningCount = 0 ;
20
+ for ( let i = 0 ; i < sortedCounts . length ; i ++ ) {
21
+ const sortedCount = sortedCounts [ i ] ;
22
+ const value = Number ( sortedCount [ 0 ] ) ;
23
+ const count = sortedCount [ 1 ] ;
24
+ runningCount += count ;
25
+ if ( runningCount > middle ) {
26
+ medians . push ( value ) ;
27
+ break ;
28
+ } else if ( runningCount === middle ) {
29
+ medians . push ( ( value + Number ( sortedCounts [ i + 1 ] ) ) / 2 ) ;
30
+ break ;
31
+ }
32
+ }
33
+ //console.log("medians:", medians);
20
34
}
21
- } ;
35
+ return medians ;
36
+ }
22
37
23
38
const getMedianForRaster = ( georaster , geom ) => {
24
39
@@ -32,54 +47,46 @@ const getMedianForRaster = (georaster, geom) => {
32
47
geom = convertGeometry ( 'bbox' , geom ) ;
33
48
}
34
49
35
- const values = get ( georaster , geom ) ;
36
-
37
- const { noDataValue } = georaster ;
38
-
39
- // median values
40
- const medians = [ ] ;
41
- for ( let bandIndex = 0 ; bandIndex < values . length ; bandIndex ++ ) {
42
- const band = values [ bandIndex ] ;
43
- const counts = utils . countValuesInTable ( band , noDataValue ) ;
44
- const numCellsWithValue = utils . sum ( _ . values ( counts ) ) ;
45
- const sortedCounts = _ . pairs ( counts ) . sort ( ( pair1 , pair2 ) => Number ( pair1 [ 0 ] ) - Number ( pair2 [ 0 ] ) ) ;
46
- //console.log("sortedCounts:", sortedCounts);
47
- const middle = numCellsWithValue / 2 ;
48
- let runningCount = 0 ;
49
- for ( let i = 0 ; i < sortedCounts . length ; i ++ ) {
50
- const sortedCount = sortedCounts [ i ] ;
51
- const value = Number ( sortedCount [ 0 ] ) ;
52
- const count = sortedCount [ 1 ] ;
53
- runningCount += count ;
54
- if ( runningCount > middle ) {
55
- medians . push ( value ) ;
56
- break ;
57
- } else if ( runningCount === middle ) {
58
- medians . push ( ( value + Number ( sortedCounts [ i + 1 ] ) ) / 2 ) ;
59
- break ;
60
- }
61
- }
62
- //console.log("medians:", medians);
50
+ if ( georaster . values ) {
51
+ const values = get ( georaster , geom ) ;
52
+ return getMedianForRect ( values , georaster . noDataValue ) ;
53
+ } else {
54
+ return Promise . resolve ( get ( georaster , geom ) ) . then ( values => getMedianForRect ( values , georaster . noDataValue ) ) ;
63
55
}
64
- return medians ;
65
56
66
57
} else if ( utils . isPolygon ( geom ) ) {
67
58
geom = convertGeometry ( 'polygon' , geom ) ;
68
59
const values = [ ] ;
69
60
70
- // the third argument of this function is a function which
71
- // runs for every pixel in the polygon. Here we add them to
72
- // an array to run through the getMedian function
73
- intersectPolygon ( georaster , geom , ( value , bandIndex ) => {
74
- if ( values [ bandIndex ] ) {
75
- values [ bandIndex ] . push ( value ) ;
76
- } else {
77
- values [ bandIndex ] = [ value ] ;
78
- }
79
- } ) ;
80
61
81
- if ( values . length > 0 ) return values . map ( getMedian ) ;
82
- else throw 'No Values were found in the given geometry' ;
62
+ if ( georaster . values ) {
63
+ // the third argument of this function is a function which
64
+ // runs for every pixel in the polygon. Here we add them to
65
+ // an array to run through the getMedian function
66
+ intersectPolygon ( georaster , geom , ( value , bandIndex ) => {
67
+ if ( values [ bandIndex ] ) {
68
+ values [ bandIndex ] . push ( value ) ;
69
+ } else {
70
+ values [ bandIndex ] = [ value ] ;
71
+ }
72
+ } ) ;
73
+
74
+ // intersectPolygon already filtered out no data, so don't need to set fasterMedian({ no_data })
75
+ if ( values . length > 0 ) return values . map ( bands => fasterMedian ( { nums : bands } ) ) ;
76
+ else throw 'No Values were found in the given geometry' ;
77
+ } else {
78
+ return intersectPolygon ( georaster , geom , ( value , bandIndex ) => {
79
+ if ( values [ bandIndex ] ) {
80
+ values [ bandIndex ] . push ( value ) ;
81
+ } else {
82
+ values [ bandIndex ] = [ value ] ;
83
+ }
84
+ } ) . then ( ( ) => {
85
+ // intersectPolygon already filtered out no data, so don't need to set fasterMedian({ no_data })
86
+ if ( values . length > 0 ) return values . map ( bands => fasterMedian ( { nums : bands } ) ) ;
87
+ else throw 'No Values were found in the given geometry' ;
88
+ } ) ;
89
+ }
83
90
84
91
} else {
85
92
throw 'Non-Bounding Box geometries are currently not supported.' ;
@@ -90,4 +97,12 @@ const getMedianForRaster = (georaster, geom) => {
90
97
}
91
98
} ;
92
99
93
- export default getMedianForRaster ;
100
+ const getMedianWrapper = ( georaster , geom ) => {
101
+ if ( typeof georaster === "string" ) {
102
+ return load ( georaster ) . then ( loaded => getMedianForRaster ( loaded , geom ) ) ;
103
+ } else {
104
+ return getMedianForRaster ( georaster , geom ) ;
105
+ }
106
+ }
107
+
108
+ export default getMedianWrapper ;
0 commit comments