1+ const fastMax = require ( "fast-max" ) ;
2+ const fastMin = require ( "fast-min" ) ;
3+ const getTheoreticalMax = require ( "typed-array-ranges/get-max" ) ;
4+ const getTheoreticalMin = require ( "typed-array-ranges/get-min" ) ;
5+ const fasterMedian = require ( "faster-median" ) ;
6+
17const forEach = ( nums , no_data , cb ) => {
28 const len = nums . length ;
39 if ( no_data ) {
@@ -12,12 +18,14 @@ const forEach = (nums, no_data, cb) => {
1218 }
1319} ;
1420
15- const max = ( nums , in_no_data , out_no_data ) => {
16- let result = - Infinity ;
17- forEach ( nums , in_no_data , n => {
18- if ( n > result ) result = n ;
19- } ) ;
20- return result === - Infinity ? out_no_data : result ;
21+ const median = ( { nums, in_no_data, out_no_data } ) => {
22+ const result = fasterMedian ( { nums, no_data : in_no_data } ) ;
23+ return result === undefined ? out_no_data : result ;
24+ } ;
25+
26+ const max = ( { nums, in_no_data, out_no_data, theoretical_max } ) => {
27+ const result = fastMax ( nums , { no_data : in_no_data , theoretical_max } ) ;
28+ return result === undefined ? out_no_data : result ;
2129} ;
2230
2331const mean = ( nums , in_no_data , out_no_data ) => {
@@ -30,29 +38,9 @@ const mean = (nums, in_no_data, out_no_data) => {
3038 return count === 0 ? out_no_data : running_sum / count ;
3139} ;
3240
33- const min = ( nums , in_no_data , out_no_data ) => {
34- let result = Infinity ;
35- forEach ( nums , in_no_data , n => {
36- if ( n < result ) result = n ;
37- } ) ;
38- return result === Infinity ? out_no_data : result ;
39- } ;
40-
41- const median = ( nums , in_no_data , out_no_data ) => {
42- nums = nums . filter ( n => n !== in_no_data ) . sort ( ) ;
43- switch ( nums . length ) {
44- case 0 :
45- return out_no_data ;
46- case 1 :
47- return nums [ 0 ] ;
48- default :
49- const mid = nums . length / 2 ;
50- if ( nums . length % 2 === 0 ) {
51- return ( nums [ mid - 1 ] + nums [ mid ] ) / 2 ;
52- } else {
53- return nums [ Math . floor ( mid ) ] ;
54- }
55- }
41+ const min = ( { nums, in_no_data, out_no_data, theoretical_min } ) => {
42+ const result = fastMin ( nums , { no_data : in_no_data , theoretical_min } ) ;
43+ return result === undefined ? out_no_data : result ;
5644} ;
5745
5846const mode = ( nums , no_data ) => {
@@ -98,10 +86,13 @@ const geowarp = ({
9886 out_no_data = null ,
9987 method = "median" ,
10088 round = false , // whether to round output
89+ theoretical_min, // minimum theoretical value (e.g., 0 for unsigned integer arrays)
90+ theoretical_max, // maximum values (e.g., 255 for 8-bit unsigned integer arrays)
10191} ) => {
10292 if ( debug_level ) console . log ( "[geowarp] starting" ) ;
10393
10494 const sameSRS = in_srs === out_srs ;
95+ if ( debug_level ) console . log ( "[geowarp] input and output srs are the same:" , sameSRS ) ;
10596
10697 if ( ! sameSRS && typeof reproject !== "function" ) {
10798 throw new Error ( "[geowarp] you must specify a reproject function" ) ;
@@ -113,6 +104,7 @@ const geowarp = ({
113104 const num_bands = in_data . length ;
114105 if ( debug_level ) console . log ( "[geowarp] number of bands in source data:" , num_bands ) ;
115106
107+ if ( debug_level ) console . log ( "[geowarp] method:" , method ) ;
116108 const [ in_xmin , in_ymin , in_xmax , in_ymax ] = in_bbox ;
117109
118110 const in_pixel_height = ( in_ymax - in_ymin ) / in_height ;
@@ -121,9 +113,30 @@ const geowarp = ({
121113 if ( debug_level ) console . log ( "[geowarp] pixel width of source data:" , in_pixel_width ) ;
122114
123115 const [ out_xmin , out_ymin , out_xmax , out_ymax ] = out_bbox ;
116+ if ( debug_level ) console . log ( "[geowarp] out_xmin:" , out_xmin ) ;
117+ if ( debug_level ) console . log ( "[geowarp] out_ymin:" , out_ymin ) ;
118+ if ( debug_level ) console . log ( "[geowarp] out_xmax:" , out_xmax ) ;
119+ if ( debug_level ) console . log ( "[geowarp] out_ymax:" , out_ymax ) ;
124120
125121 const out_pixel_height = ( out_ymax - out_ymin ) / out_height ;
126122 const out_pixel_width = ( out_xmax - out_xmin ) / out_width ;
123+ if ( debug_level ) console . log ( "[geowarp] out_pixel_height:" , out_pixel_height ) ;
124+ if ( debug_level ) console . log ( "[geowarp] out_pixel_width:" , out_pixel_width ) ;
125+
126+ if ( theoretical_min === undefined || theoretical_max === undefined ) {
127+ try {
128+ const data_constructor = in_data [ 0 ] . constructor . name ;
129+ if ( debug_level ) console . log ( "[geowarp] data_constructor:" , data_constructor ) ;
130+ if ( theoretical_min === undefined ) theoretical_min = getTheoreticalMin ( data_constructor ) ;
131+ if ( theoretical_max === undefined ) theoretical_max = getTheoreticalMax ( data_constructor ) ;
132+ if ( debug_level ) console . log ( "[geowarp] theoretical_min:" , theoretical_min ) ;
133+ if ( debug_level ) console . log ( "[geowarp] theoretical_max:" , theoretical_max ) ;
134+ } catch ( error ) {
135+ // we want to log an error if it happens
136+ // even if we don't strictly need it to succeed
137+ console . error ( error ) ;
138+ }
139+ }
127140
128141 // iterate over pixels in the out box
129142 const rows = [ ] ;
@@ -141,7 +154,7 @@ const geowarp = ({
141154
142155 // convert to bbox of input coordinate system
143156 const bbox_in_srs = sameSRS ? [ left , bottom , right , top ] : [ ...reproject ( [ left , bottom ] ) , ...reproject ( [ right , top ] ) ] ;
144- // console.log({bbox} );
157+ if ( debug_level >= 3 ) console . log ( "bbox_in_srs:" , bbox_in_srs ) ;
145158 const [ xmin_in_srs , ymin_in_srs , xmax_in_srs , ymax_in_srs ] = bbox_in_srs ;
146159
147160 // convert bbox in input srs to raster pixels
@@ -158,25 +171,27 @@ const geowarp = ({
158171 const bottomSample = Math . round ( bottomInRasterPixels ) ;
159172 for ( let b = 0 ; b < num_bands ; b ++ ) {
160173 const band = in_data [ b ] ;
174+ // const values = new band.constructor((bottomSample - topSample + 1) * (rightSample - leftSample + 1));
161175 const values = [ ] ;
162- for ( let y = topSample ; y <= bottomSample ; y ++ ) {
176+ for ( let y = topSample , i = 0 ; y <= bottomSample ; y ++ ) {
163177 const start = y * in_width ;
164178 for ( let x = leftSample ; x <= rightSample ; x ++ ) {
165179 // assuming flattened data by band
166- const value = band [ start + x ] ;
167- values . push ( value ) ;
180+ // values[i++] = band[start + x];
181+ values . push ( band [ start + x ] ) ;
168182 }
169183 }
184+ // console.log("values:", JSON.stringify(values));
170185
171186 let pixelBandValue = null ;
172187 if ( method === "max" ) {
173- pixelBandValue = max ( values , in_no_data , out_no_data ) ;
188+ pixelBandValue = max ( { nums : values , in_no_data, out_no_data, theoretical_max : undefined } ) ;
174189 } else if ( method === "mean" ) {
175190 pixelBandValue = mean ( values , in_no_data , out_no_data ) ;
176191 } else if ( method === "median" ) {
177- pixelBandValue = median ( values , in_no_data , out_no_data ) ;
192+ pixelBandValue = median ( { nums : values , in_no_data, out_no_data } ) ;
178193 } else if ( method === "min" ) {
179- pixelBandValue = min ( values , in_no_data , out_no_data ) ;
194+ pixelBandValue = min ( { nums : values , in_no_data, out_no_data, theoretical_min : undefined } ) ;
180195 } else if ( method . startsWith ( "mode" ) ) {
181196 const modes = mode ( values ) ;
182197 const len = modes . length ;
@@ -186,13 +201,13 @@ const geowarp = ({
186201 if ( method === "mode" ) {
187202 pixelBandValue = modes [ 0 ] ;
188203 } else if ( method === "mode-max" ) {
189- pixelBandValue = max ( values ) ;
204+ pixelBandValue = max ( { nums : values } ) ;
190205 } else if ( method === "mode-mean" ) {
191206 pixelBandValue = mean ( values ) ;
192207 } else if ( method === "mode-median" ) {
193- pixelBandValue = median ( values ) ;
208+ pixelBandValue = median ( { nums : values } ) ;
194209 } else if ( method === "mode-min" ) {
195- pixelBandValue = min ( values ) ;
210+ pixelBandValue = min ( { nums : values } ) ;
196211 }
197212 }
198213 }
0 commit comments