Skip to content

Commit 8b80e7d

Browse files
committedOct 4, 2021
modernized intersect-polygon
1 parent ac9ffbd commit 8b80e7d

File tree

3 files changed

+110
-72
lines changed

3 files changed

+110
-72
lines changed
 

‎src/intersect-polygon/index.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
import intersectPolygon from './intersect-polygon.module';
1+
/**
2+
* @prettier
3+
*/
4+
import intersectPolygon from "./intersect-polygon.module";
25

36
export default intersectPolygon;

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

+33-54
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
1-
import _ from 'underscore';
2-
import getDepth from 'get-depth';
3-
import get from '../get';
4-
import load from '../load';
5-
import utils from '../utils';
6-
7-
const {
8-
categorizeIntersection,
9-
clusterLineSegments,
10-
couple,
11-
forceWithin,
12-
mergeRanges,
13-
getLineFromPoints,
14-
getIntersectionOfTwoLines,
15-
} = utils;
1+
/**
2+
* @prettier
3+
*/
4+
import _ from "underscore";
5+
import getDepth from "get-depth";
6+
import get from "../get";
7+
import wrap from "../wrap-func";
8+
import utils from "../utils";
9+
10+
const { categorizeIntersection, clusterLineSegments, couple, forceWithin, mergeRanges, getLineFromPoints, getIntersectionOfTwoLines } = utils;
1611

1712
const getEdgesForPolygon = polygon => {
1813
const edges = [];
@@ -46,7 +41,6 @@ const intersectPolygonCore = ({ georaster, geom, perPixelFunction, latlngBbox, i
4641
if (numRows === 0) return;
4742

4843
for (let y = 0; y < numRows; y++) {
49-
5044
const lat = lat0 - cellHeight * y - cellHeight / 2;
5145

5246
// use that point, plus another point along the same latitude to
@@ -60,22 +54,20 @@ const intersectPolygonCore = ({ georaster, geom, perPixelFunction, latlngBbox, i
6054
// collapse geometry down to a list of edges
6155
// necessary for multi-part geometries
6256
const depth = getDepth(geom);
63-
const polygonEdges = depth === 4 ? geom.map(getEdgesForPolygon) : [getEdgesForPolygon(geom)];
57+
const polygonEdges = depth === 4 ? geom.map(getEdgesForPolygon) : [getEdgesForPolygon(geom)];
6458

6559
polygonEdges.forEach((edges, edgesIndex) => {
6660
// iterate through the list of polygon vertices, convert them to
6761
// lines, and compute the intersections with each image row
6862
const intersectionsByRow = _.range(numRows).map(row => []);
6963
const numberOfEdges = edges.length;
7064
for (let i = 0; i < numberOfEdges; i++) {
71-
72-
7365
// get vertices that make up an edge and convert that to a line
7466
const edge = edges[i];
7567

7668
const [startPoint, endPoint] = edge;
77-
const [ x1, y1 ] = startPoint;
78-
const [ x2, y2 ] = endPoint;
69+
const [x1, y1] = startPoint;
70+
const [x2, y2] = endPoint;
7971

8072
const direction = Math.sign(y2 - y1);
8173
const horizontal = y1 === y2;
@@ -90,18 +82,18 @@ const intersectPolygonCore = ({ georaster, geom, perPixelFunction, latlngBbox, i
9082

9183
let startLng, startLat, endLat, endLng;
9284
if (x1 < x2) {
93-
[ startLng, startLat ] = startPoint;
94-
[ endLng, endLat ] = endPoint;
85+
[startLng, startLat] = startPoint;
86+
[endLng, endLat] = endPoint;
9587
} else {
96-
[ startLng, startLat ] = endPoint;
97-
[ endLng, endLat ] = startPoint;
88+
[startLng, startLat] = endPoint;
89+
[endLng, endLat] = startPoint;
9890
}
9991

100-
if (startLng === undefined) throw Error('startLng is ' + startLng);
92+
if (startLng === undefined) throw Error("startLng is " + startLng);
10193

10294
// find the y values in the image coordinate space
103-
const imageY1 = Math.round((lat0 - .5*cellHeight - startLat ) / cellHeight);
104-
const imageY2 = Math.round((lat0 - .5*cellHeight - endLat) / cellHeight);
95+
const imageY1 = Math.round((lat0 - 0.5 * cellHeight - startLat) / cellHeight);
96+
const imageY2 = Math.round((lat0 - 0.5 * cellHeight - endLat) / cellHeight);
10597

10698
// make sure to set the start and end points so that we are
10799
// incrementing upwards through rows
@@ -122,11 +114,10 @@ const intersectPolygonCore = ({ georaster, geom, perPixelFunction, latlngBbox, i
122114
for (let j = rowStart; j < rowEnd + 1; j++) {
123115
const imageLine = imageLines[j];
124116

125-
126117
if (imageLine === undefined) {
127-
console.error('j:', j);
128-
console.error('imageLines:', imageLines);
129-
throw Error('imageLines');
118+
console.error("j:", j);
119+
console.error("imageLines:", imageLines);
120+
throw Error("imageLines");
130121
}
131122

132123
// because you know x is zero in ax + by = c, so by = c and b = -1, so -1 * y = c or y = -1 * c
@@ -183,7 +174,7 @@ const intersectPolygonCore = ({ georaster, geom, perPixelFunction, latlngBbox, i
183174
vertical,
184175
xmin: xminOnLine,
185176
xmax: xmaxOnLine,
186-
imageLineY,
177+
imageLineY
187178
});
188179
}
189180
}
@@ -193,15 +184,15 @@ const intersectPolygonCore = ({ georaster, geom, perPixelFunction, latlngBbox, i
193184
if (segmentsInRow.length > 0) {
194185
const clusters = clusterLineSegments(segmentsInRow, numberOfEdges);
195186
const categorized = clusters.map(categorizeIntersection);
196-
const [ throughs, nonthroughs ] = _.partition(categorized, item => item.through);
187+
const [throughs, nonthroughs] = _.partition(categorized, item => item.through);
197188

198189
if (throughs.length % 2 === 1) {
199-
throw Error('throughs.length for ' + rowIndex + ' is odd with ' + throughs.length);
190+
throw Error("throughs.length for " + rowIndex + " is odd with " + throughs.length);
200191
}
201192

202193
let insides = nonthroughs.map(intersection => [intersection.xmin, intersection.xmax]);
203194

204-
const sortedThroughs = _.sortBy(throughs, 'xmin');
195+
const sortedThroughs = _.sortBy(throughs, "xmin");
205196

206197
const couples = couple(sortedThroughs).map(couple => {
207198
const [left, right] = couple;
@@ -217,12 +208,11 @@ const intersectPolygonCore = ({ georaster, geom, perPixelFunction, latlngBbox, i
217208
insides = mergeRanges(insides);
218209

219210
insides.forEach(pair => {
220-
221211
const [xmin, xmax] = pair;
222212

223213
//convert left and right to image pixels
224-
const left = Math.round((xmin - (lng0 + .5*cellWidth)) / cellWidth);
225-
const right = Math.round((xmax - (lng0 + .5*cellWidth)) / cellWidth);
214+
const left = Math.round((xmin - (lng0 + 0.5 * cellWidth)) / cellWidth);
215+
const right = Math.round((xmax - (lng0 + 0.5 * cellWidth)) / cellWidth);
226216

227217
const startColumnIndex = Math.max(left, 0);
228218
const endColumnIndex = Math.min(right, imageWidth);
@@ -245,20 +235,9 @@ const intersectPolygon = (georaster, geom, perPixelFunction) => {
245235
// get values in a bounding box around the geometry
246236
const latlngBbox = utils.getBoundingBox(geom);
247237

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-
}
238+
const values = get(georaster, latlngBbox);
239+
240+
return utils.callAfterResolveArgs(imageBands => intersectPolygonCore({ georaster, geom, perPixelFunction, latlngBbox, imageBands }), values);
262241
};
263242

264-
export default intersectPolygon;
243+
export default wrap(intersectPolygon);
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
1-
import test from 'flug';
2-
import fetch from 'cross-fetch';
3-
import load from '../load';
4-
import utils from '../utils';
5-
import convertGeometry from '../convert-geometry';
6-
import intersectPolygon from '../intersect-polygon';
7-
import bboxPolygon from '@turf/bbox-polygon';
1+
/**
2+
* @prettier
3+
*/
4+
import test from "flug";
5+
import { serve } from "srvd";
6+
import fetch from "cross-fetch";
7+
import get from "../get";
8+
import load from "../load";
9+
import parse from "../parse";
10+
import utils from "../utils";
11+
import convertGeometry from "../convert-geometry";
12+
import intersectPolygon from "../intersect-polygon";
13+
import bboxPolygon from "@turf/bbox-polygon";
814

9-
const urlToGeojson = 'http://localhost:3000/data/gadm/geojsons/Akrotiri and Dhekelia.geojson';
15+
if (require.main === module) serve({ debug: true, port: 3000, wait: 3 });
1016

11-
const urlToData = 'http://localhost:3000/data/';
17+
const urlToGeojson = "http://localhost:3000/data/gadm/geojsons/Akrotiri and Dhekelia.geojson";
1218

13-
const url = urlToData + 'test.tiff';
19+
const urlToData = "http://localhost:3000/data/";
1420

15-
test('Testing intersection of box', async ({ eq }) => {
21+
const url = urlToData + "test.tiff";
22+
23+
test("(Legacy) Testing intersection of box", async ({ eq }) => {
1624
const georaster = await load(url);
25+
const values = await get(georaster);
1726

1827
let expectedNumberOfIntersectingPixels = 0;
19-
georaster.values.forEach(band => {
28+
values.forEach(band => {
2029
band.forEach(row => {
2130
row.forEach(value => {
2231
if (value != georaster.noDataValue) {
@@ -30,20 +39,67 @@ test('Testing intersection of box', async ({ eq }) => {
3039
const pixelWidth = georaster.pixelWidth;
3140

3241
// minX, minY, maxX, maxY
33-
const geom = bboxPolygon([georaster.xmin + .5 * pixelWidth, georaster.ymin + .5 * pixelHeight, georaster.xmax - .5 * pixelWidth, georaster.ymax - .5 * pixelHeight]);
42+
const geom = bboxPolygon([
43+
georaster.xmin + 0.5 * pixelWidth,
44+
georaster.ymin + 0.5 * pixelHeight,
45+
georaster.xmax - 0.5 * pixelWidth,
46+
georaster.ymax - 0.5 * pixelHeight
47+
]);
3448
const coordinates = utils.getGeojsonCoors(geom);
3549
let numberOfIntersectingPixels = 0;
3650
intersectPolygon(georaster, coordinates, () => numberOfIntersectingPixels++);
3751
eq(numberOfIntersectingPixels, expectedNumberOfIntersectingPixels);
3852
});
3953

40-
41-
test('Test intersection/sum calculations for Country with Multiple Rings', async ({ eq }) => {
42-
const georaster = await load(urlToData + 'ghsl/tiles/GHS_POP_GPW42015_GLOBE_R2015A_54009_1k_v1_0_4326_30_40.tif');
54+
test("(Legacy) Test intersection/sum calculations for Country with Multiple Rings", async ({ eq }) => {
55+
const georaster = await load(urlToData + "ghsl/tiles/GHS_POP_GPW42015_GLOBE_R2015A_54009_1k_v1_0_4326_30_40.tif");
4356
const response = await fetch(urlToGeojson);
4457
const country = await response.json();
4558
let numberOfIntersectingPixels = 0;
46-
const geom = convertGeometry('polygon', country);
59+
const geom = convertGeometry("polygon", country);
4760
intersectPolygon(georaster, geom, () => numberOfIntersectingPixels++);
4861
eq(numberOfIntersectingPixels, 281);
4962
});
63+
64+
// MODERN
65+
66+
test("(Modern) Testing intersection of box", async ({ eq }) => {
67+
const georaster = await parse(url);
68+
const values = await get(georaster);
69+
70+
let expectedNumberOfIntersectingPixels = 0;
71+
values.forEach(band => {
72+
band.forEach(row => {
73+
row.forEach(value => {
74+
if (value != georaster.noDataValue) {
75+
expectedNumberOfIntersectingPixels++;
76+
}
77+
});
78+
});
79+
});
80+
81+
const pixelHeight = georaster.pixelHeight;
82+
const pixelWidth = georaster.pixelWidth;
83+
84+
// minX, minY, maxX, maxY
85+
const geom = bboxPolygon([
86+
georaster.xmin + 0.5 * pixelWidth,
87+
georaster.ymin + 0.5 * pixelHeight,
88+
georaster.xmax - 0.5 * pixelWidth,
89+
georaster.ymax - 0.5 * pixelHeight
90+
]);
91+
const coordinates = utils.getGeojsonCoors(geom);
92+
let numberOfIntersectingPixels = 0;
93+
await intersectPolygon(georaster, coordinates, () => numberOfIntersectingPixels++);
94+
eq(numberOfIntersectingPixels, expectedNumberOfIntersectingPixels);
95+
});
96+
97+
test("(Modern) Test intersection/sum calculations for Country with Multiple Rings", async ({ eq }) => {
98+
const georaster = await parse(urlToData + "ghsl/tiles/GHS_POP_GPW42015_GLOBE_R2015A_54009_1k_v1_0_4326_30_40.tif");
99+
const response = await fetch(urlToGeojson);
100+
const country = await response.json();
101+
let numberOfIntersectingPixels = 0;
102+
const geom = convertGeometry("polygon", country);
103+
await intersectPolygon(georaster, geom, () => numberOfIntersectingPixels++);
104+
eq(numberOfIntersectingPixels, 281);
105+
});

0 commit comments

Comments
 (0)
Please sign in to comment.