Skip to content

Commit

Permalink
added support for near resampling
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielJDufour committed Jun 4, 2021
1 parent 679057a commit 5a5f146
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 75 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const result = geowarp({

// method to use to sample the pixels
// current supported methods are:
// "max", "mean", "median", "min", "mode", "mode-max", "mode-mean", "mode-median", and "mode-min"
// "near", "max", "mean", "median", "min", "mode", "mode-max", "mode-mean", "mode-median", and "mode-min"
method: 'median',

// round output pixel values to closest integer
Expand Down
164 changes: 94 additions & 70 deletions geowarp.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,83 +140,107 @@ const geowarp = ({

// iterate over pixels in the out box
const rows = [];
let top, left, bottom, right;
bottom = out_ymax;
for (let r = 0; r < out_height; r++) {
const row = [];
top = bottom;
bottom = top - out_pixel_height;
right = out_xmin;
for (let c = 0; c < out_width; c++) {
left = right;
right = left + out_pixel_width;
// top, left, bottom, right is the sample area in the coordinate system of the output

// convert to bbox of input coordinate system
const bbox_in_srs = sameSRS ? [left, bottom, right, top] : [...reproject([left, bottom]), ...reproject([right, top])];
if (debug_level >= 3) console.log("bbox_in_srs:", bbox_in_srs);
const [xmin_in_srs, ymin_in_srs, xmax_in_srs, ymax_in_srs] = bbox_in_srs;

// convert bbox in input srs to raster pixels
const leftInRasterPixels = (xmin_in_srs - in_xmin) / in_pixel_width;
const rightInRasterPixels = (xmax_in_srs - in_xmin) / in_pixel_width;
const topInRasterPixels = (in_ymax - ymax_in_srs) / in_pixel_height;
const bottomInRasterPixels = (in_ymax - ymin_in_srs) / in_pixel_height;
// console.log({xmin_in_srs, in_xmin, leftInRasterPixels, rightInRasterPixels, topInRasterPixels, bottomInRasterPixels});

const pixel = [];
const leftSample = Math.round(leftInRasterPixels);
const rightSample = Math.round(rightInRasterPixels);
const topSample = Math.round(topInRasterPixels);
const bottomSample = Math.round(bottomInRasterPixels);
for (let b = 0; b < num_bands; b++) {
const band = in_data[b];
// const values = new band.constructor((bottomSample - topSample + 1) * (rightSample - leftSample + 1));
const values = [];
for (let y = topSample, i = 0; y <= bottomSample; y++) {
const start = y * in_width;
for (let x = leftSample; x <= rightSample; x++) {
// assuming flattened data by band
// values[i++] = band[start + x];
values.push(band[start + x]);
}

if (method === "near") {
for (let r = 0; r < out_height; r++) {
const row = [];
const y = out_ymax - out_pixel_height * r;
for (let c = 0; c < out_width; c++) {
const x = out_xmin + out_pixel_width * c;
const pt_out_srs = [x, y];
const [x_in_srs, y_in_srs] = sameSRS ? pt_out_srs : reproject(pt_out_srs);
const xInRasterPixels = Math.round((x_in_srs - in_xmin) / in_pixel_width);
const yInRasterPixels = Math.round((in_ymax - y_in_srs) / in_pixel_height);
const i = yInRasterPixels * in_width + xInRasterPixels;
const pixel = [];
for (let b = 0; b < num_bands; b++) {
let pixelBandValue = in_data[b][i];
if (round) pixelBandValue = Math.round(pixelBandValue);
pixel.push(pixelBandValue);
}
// console.log("values:", JSON.stringify(values));

let pixelBandValue = null;
if (method === "max") {
pixelBandValue = max({ nums: values, in_no_data, out_no_data, theoretical_max: undefined });
} else if (method === "mean") {
pixelBandValue = mean(values, in_no_data, out_no_data);
} else if (method === "median") {
pixelBandValue = median({ nums: values, in_no_data, out_no_data });
} else if (method === "min") {
pixelBandValue = min({ nums: values, in_no_data, out_no_data, theoretical_min: undefined });
} else if (method.startsWith("mode")) {
const modes = mode(values);
const len = modes.length;
if (len === 1) {
pixelBandValue = modes[0];
} else {
if (method === "mode") {
row.push(pixel);
}
rows.push(row);
}
} else {
let top, left, bottom, right;
bottom = out_ymax;
for (let r = 0; r < out_height; r++) {
const row = [];
top = bottom;
bottom = top - out_pixel_height;
right = out_xmin;
for (let c = 0; c < out_width; c++) {
left = right;
right = left + out_pixel_width;
// top, left, bottom, right is the sample area in the coordinate system of the output

// convert to bbox of input coordinate system
const bbox_in_srs = sameSRS ? [left, bottom, right, top] : [...reproject([left, bottom]), ...reproject([right, top])];
if (debug_level >= 3) console.log("bbox_in_srs:", bbox_in_srs);
const [xmin_in_srs, ymin_in_srs, xmax_in_srs, ymax_in_srs] = bbox_in_srs;

// convert bbox in input srs to raster pixels
const leftInRasterPixels = (xmin_in_srs - in_xmin) / in_pixel_width;
const rightInRasterPixels = (xmax_in_srs - in_xmin) / in_pixel_width;
const topInRasterPixels = (in_ymax - ymax_in_srs) / in_pixel_height;
const bottomInRasterPixels = (in_ymax - ymin_in_srs) / in_pixel_height;
// console.log({xmin_in_srs, in_xmin, leftInRasterPixels, rightInRasterPixels, topInRasterPixels, bottomInRasterPixels});

const pixel = [];
const leftSample = Math.round(leftInRasterPixels);
const rightSample = Math.round(rightInRasterPixels);
const topSample = Math.round(topInRasterPixels);
const bottomSample = Math.round(bottomInRasterPixels);
for (let b = 0; b < num_bands; b++) {
const band = in_data[b];
// const values = new band.constructor((bottomSample - topSample + 1) * (rightSample - leftSample + 1));
const values = [];
for (let y = topSample, i = 0; y <= bottomSample; y++) {
const start = y * in_width;
for (let x = leftSample; x <= rightSample; x++) {
// assuming flattened data by band
// values[i++] = band[start + x];
values.push(band[start + x]);
}
}
// console.log("values:", JSON.stringify(values));

let pixelBandValue = null;
if (method === "max") {
pixelBandValue = max({ nums: values, in_no_data, out_no_data, theoretical_max: undefined });
} else if (method === "mean") {
pixelBandValue = mean(values, in_no_data, out_no_data);
} else if (method === "median") {
pixelBandValue = median({ nums: values, in_no_data, out_no_data });
} else if (method === "min") {
pixelBandValue = min({ nums: values, in_no_data, out_no_data, theoretical_min: undefined });
} else if (method.startsWith("mode")) {
const modes = mode(values);
const len = modes.length;
if (len === 1) {
pixelBandValue = modes[0];
} else if (method === "mode-max") {
pixelBandValue = max({ nums: values });
} else if (method === "mode-mean") {
pixelBandValue = mean(values);
} else if (method === "mode-median") {
pixelBandValue = median({ nums: values });
} else if (method === "mode-min") {
pixelBandValue = min({ nums: values });
} else {
if (method === "mode") {
pixelBandValue = modes[0];
} else if (method === "mode-max") {
pixelBandValue = max({ nums: values });
} else if (method === "mode-mean") {
pixelBandValue = mean(values);
} else if (method === "mode-median") {
pixelBandValue = median({ nums: values });
} else if (method === "mode-min") {
pixelBandValue = min({ nums: values });
}
}
}
if (round) pixelBandValue = Math.round(pixelBandValue);
pixel.push(pixelBandValue);
}
if (round) pixelBandValue = Math.round(pixelBandValue);
pixel.push(pixelBandValue);
row.push(pixel);
}
row.push(pixel);
rows.push(row);
}
rows.push(row);
}

if (debug_level) console.log("[geowarp] finishing");
Expand Down
11 changes: 7 additions & 4 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ const runTileTests = async ({ x, y, z, filename, methods, sizes = [64, 256, 512]
const top = Object.entries(counts).sort((a, b) => Math.sign(b - a))[0][0];
eq(most_common_pixels.includes(top), true);

writePNGSync({ h: size, w: size, data: result.data, filepath: `./test-data/${testName}` });
if (process.env.GEOWARP_WRITE_PNG) {
writePNGSync({ h: size, w: size, data: result.data, filepath: `./test-data/${testName}` });
}
});
});
});
Expand All @@ -118,7 +120,7 @@ const runTileTests = async ({ x, y, z, filename, methods, sizes = [64, 256, 512]
z: 8,
sizes: [64, 256, 512],
filename: "wildfires.tiff",
methods: ["max", "mean", "median", "min", "mode", "mode-mean", "mode-max", "mode-min"],
methods: ["near", "max", "mean", "median", "min", "mode", "mode-mean", "mode-max", "mode-min"],
most_common_pixels: ["0,0,0", "18,26,12", "13,18,9", "22,30,17"],
},
{
Expand All @@ -128,7 +130,7 @@ const runTileTests = async ({ x, y, z, filename, methods, sizes = [64, 256, 512]
sizes: [64, 256, 512],
// sizes: [256],
filename: "SkySat_Freeport_s03_20170831T162740Z3.tif",
methods: ["max", "mean", "median", "min", "mode", "mode-mean", "mode-max", "mode-min"],
methods: ["near", "max", "mean", "median", "min", "mode", "mode-mean", "mode-max", "mode-min"],
most_common_pixels: [
"121,110,99",
"132,127,125",
Expand All @@ -141,6 +143,7 @@ const runTileTests = async ({ x, y, z, filename, methods, sizes = [64, 256, 512]
"146,141,139",
"147,140,136",
"147,141,137",
"152,146,142",
"152,146,143",
"153,133,143",
"157,152,150",
Expand All @@ -149,7 +152,7 @@ const runTileTests = async ({ x, y, z, filename, methods, sizes = [64, 256, 512]
},
].forEach(runTileTests);

["min", "max", "median"].forEach(method => {
["near", "min", "max", "median"].forEach(method => {
test(method + " performance", async ({ eq }) => {
const info = await readTile({ x: 3853, y: 6815, z: 14, filename: "SkySat_Freeport_s03_20170831T162740Z3.tif" });

Expand Down

0 comments on commit 5a5f146

Please sign in to comment.