Skip to content

Commit 9c47d0e

Browse files
committedOct 1, 2021
updated intersect and median
1 parent 681db60 commit 9c47d0e

File tree

3 files changed

+125
-69
lines changed

3 files changed

+125
-69
lines changed
 

‎src/intersect-polygon/intersect-polygon.module.js

+22-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import _ from 'underscore';
22
import getDepth from 'get-depth';
33
import get from '../get';
4+
import load from '../load';
45
import utils from '../utils';
56

67
const {
@@ -25,20 +26,14 @@ const getEdgesForPolygon = polygon => {
2526
return edges;
2627
};
2728

28-
const intersectPolygon = (georaster, geom, perPixelFunction) => {
29-
29+
const intersectPolygonCore = ({ georaster, geom, perPixelFunction, latlngBbox, imageBands }) => {
3030
const cellWidth = georaster.pixelWidth;
3131
const cellHeight = georaster.pixelHeight;
3232

3333
const { noDataValue } = georaster;
3434

3535
const imageWidth = georaster.width;
3636

37-
// get values in a bounding box around the geometry
38-
const latlngBbox = utils.getBoundingBox(geom);
39-
40-
const imageBands = get(georaster, latlngBbox);
41-
4237
// set origin points of bbox of geometry in image space
4338
const lat0 = latlngBbox.ymax + ((georaster.ymax - latlngBbox.ymax) % cellHeight);
4439
const lng0 = latlngBbox.xmin - ((latlngBbox.xmin - georaster.xmin) % cellWidth);
@@ -246,4 +241,24 @@ const intersectPolygon = (georaster, geom, perPixelFunction) => {
246241
});
247242
};
248243

244+
const intersectPolygon = (georaster, geom, perPixelFunction) => {
245+
// get values in a bounding box around the geometry
246+
const latlngBbox = utils.getBoundingBox(geom);
247+
248+
if (typeof georaster === "string") {
249+
return load(georaster).then(loaded => {
250+
return get(georaster, latlngBbox).then(imageBands => (
251+
intersectPolygonCore({ georaster: loaded, geom, perPixelFunction, latlngBbox, imageBands })
252+
));
253+
});
254+
} else if (!georaster.values) {
255+
return get(georaster, latlngBbox).then(imageBands => (
256+
intersectPolygonCore({ georaster, geom, perPixelFunction, latlngBbox, imageBands })
257+
));
258+
} else {
259+
const imageBands = get(georaster, latlngBbox);
260+
return intersectPolygonCore({ georaster, geom, perPixelFunction, latlngBbox, imageBands });
261+
}
262+
};
263+
249264
export default intersectPolygon;

‎src/median/median.module.js

+72-57
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,39 @@
11
import _ from 'underscore';
2+
import fasterMedian from 'faster-median';
23
import get from '../get';
34
import utils from '../utils';
45
import convertGeometry from '../convert-geometry';
56
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);
2034
}
21-
};
35+
return medians;
36+
}
2237

2338
const getMedianForRaster = (georaster, geom) => {
2439

@@ -32,54 +47,46 @@ const getMedianForRaster = (georaster, geom) => {
3247
geom = convertGeometry('bbox', geom);
3348
}
3449

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));
6355
}
64-
return medians;
6556

6657
} else if (utils.isPolygon(geom)) {
6758
geom = convertGeometry('polygon', geom);
6859
const values = [];
6960

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-
});
8061

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+
}
8390

8491
} else {
8592
throw 'Non-Bounding Box geometries are currently not supported.';
@@ -90,4 +97,12 @@ const getMedianForRaster = (georaster, geom) => {
9097
}
9198
};
9299

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;

‎src/median/median.test.js

+31-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import test from 'flug';
2+
import fetch from 'cross-fetch';
23
import load from '../load';
34
import median from './median.module';
45

@@ -30,28 +31,53 @@ const polygon = [[
3031
[85.078125, 21.166483858206583], [86.044921875, 20.838277806058933], [86.98974609375, 22.49225722008518],
3132
[85.58349609375, 24.54712317973075], [84.6826171875, 23.36242859340884], [83.12255859375, 22.49225722008518]
3233
]];
33-
const expectedPolygonValue = 2750.5;
34+
const expectedPolygonValue = 1573.3;
3435

3536
test('Get Median from Bounding Box', async ({ eq }) => {
36-
const georaster = await load(url);
37+
const georaster = await fetch(url).then(r => r.arrayBuffer()).then(load);
3738
const value = Number(median(georaster, bbox)[0].toFixed(2));
3839
eq(value, expectedBboxValue);
3940
});
4041

4142
test('Get Median from Bounding Box (GeoJSON)', async ({ eq }) => {
42-
const georaster = await load(url);
43+
const georaster = await fetch(url).then(r => r.arrayBuffer()).then(load);
4344
const value = Number(median(georaster, bboxGeojson)[0].toFixed(2));
4445
eq(value, expectedBboxGeojsonValue);
4546
});
4647

4748
test('Get Median from Polygon', async ({ eq }) => {
48-
const georaster = await load(url);
49+
const georaster = await fetch(url).then(r => r.arrayBuffer()).then(load);
4950
const value = Number(median(georaster, polygon)[0].toFixed(2));
5051
eq(value, expectedPolygonValue);
5152
});
5253

5354
test('Get Median from Whole Raster', async ({ eq }) => {
54-
const georaster = await load(url);
55+
const georaster = await fetch(url).then(r => r.arrayBuffer()).then(load);
5556
const value = median(georaster)[0];
5657
eq(value, 0);
5758
});
59+
60+
61+
test('Get Median from Bounding Box from url', async ({ eq }) => {
62+
const result = await median(url, bbox);
63+
const value = Number(result[0].toFixed(2));
64+
eq(value, expectedBboxValue);
65+
});
66+
67+
test('Get Median from Bounding Box (GeoJSON) from url', async ({ eq }) => {
68+
const result = await median(url, bboxGeojson);
69+
const value = Number(result[0].toFixed(2));
70+
eq(value, expectedBboxGeojsonValue);
71+
});
72+
73+
test('Get Median from Polygon from url', async ({ eq }) => {
74+
const result = await median(url, polygon);
75+
const value = Number(result[0].toFixed(2));
76+
eq(value, expectedPolygonValue);
77+
});
78+
79+
test('Get Median from Whole Raster from url', async ({ eq }) => {
80+
const result = await median(url);
81+
const value = result[0];
82+
eq(value, 0);
83+
});

0 commit comments

Comments
 (0)
Please sign in to comment.