Skip to content

Commit

Permalink
comments and reformatting
Browse files Browse the repository at this point in the history
  • Loading branch information
dbarkowsky committed Oct 7, 2024
1 parent e61d9c1 commit 83980de
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 23 deletions.
22 changes: 16 additions & 6 deletions express-api/src/services/properties/propertiesServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ const getPropertiesForMap = async (filter?: MapFilter) => {
});

if (filter.Polygon) {
return filterPropertiesByPolygon(filter.Polygon, properties);
return filterPropertiesByMultiPolygon(JSON.parse(filter.Polygon), properties);
}
return properties;
}
Expand All @@ -263,18 +263,28 @@ const getPropertiesForMap = async (filter?: MapFilter) => {
},
});
if (filter.Polygon) {
return filterPropertiesByPolygon(filter.Polygon, properties);
return filterPropertiesByMultiPolygon(JSON.parse(filter.Polygon), properties);
}
return properties;
};

const filterPropertiesByPolygon = (polygon: string, properties: MapProperties[]) => {
const polygonArray = JSON.parse(polygon);
const formattedPolygons = (polygonArray as number[][][]).map((polygon) =>
/**
* Filters a list of properties based on whether they are within the given polygons.
*
* @param multiPolygon - The polygon coordinates in a 3D array.
* @param properties - An array of MapProperties objects to filter.
* @returns An array of MapProperties objects that fall within the specified polygon.
*/
const filterPropertiesByMultiPolygon = (
multiPolygon: number[][][],
properties: MapProperties[],
) => {
const formattedPolygons: { x: number; y: number }[][] = multiPolygon.map((polygon) =>
polygon.map((point) => ({ x: point.at(1), y: point.at(0) })),
);
// TODO: memoize this so we don't check the same coordinates twice
return properties.filter((property) =>
isPointInMultiPolygon(property.Location, formattedPolygons as { x: number; y: number }[][]),
isPointInMultiPolygon(property.Location, formattedPolygons),
);
};

Expand Down
42 changes: 25 additions & 17 deletions express-api/src/utilities/polygonMath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ const EPSILON = 1e-10; // A small tolerance for floating-point comparison
* @param y2 - Y component of vector 2
* @returns The cross product (a scalar value).
*/
function crossProduct(x1: number, y1: number, x2: number, y2: number): number {
const crossProduct = (x1: number, y1: number, x2: number, y2: number): number => {
return x1 * y2 - y1 * x2;
}
};

/**
* Helper function to calculate the dot product of two vectors.
Expand All @@ -20,9 +20,9 @@ function crossProduct(x1: number, y1: number, x2: number, y2: number): number {
* @param y2 - Y component of vector 2
* @returns The dot product (a scalar value).
*/
function dotProduct(x1: number, y1: number, x2: number, y2: number): number {
const dotProduct = (x1: number, y1: number, x2: number, y2: number): number => {
return x1 * x2 + y1 * y2;
}
};

/**
* Helper function to calculate the angle between two vectors.
Expand All @@ -31,33 +31,41 @@ function dotProduct(x1: number, y1: number, x2: number, y2: number): number {
* @param v2 - Second vertex of the polygon edge.
* @returns The angle between the vectors formed by the point and the two vertices.
*/
function angleBetweenVectors(
const angleBetweenVectors = (
p: { x: number; y: number },
v1: { x: number; y: number },
v2: { x: number; y: number },
): number {
): number => {
// Get the difference between the point and vectors towards the two points
const x1 = v1.x - p.x,
y1 = v1.y - p.y;
const x2 = v2.x - p.x,
y2 = v2.y - p.y;

const cross = crossProduct(x1, y1, x2, y2);
const dot = dotProduct(x1, y1, x2, y2);
const cross = crossProduct(x1, y1, x2, y2); // Gives rotation (clockwise, counterclockwise) and sign of angle
const dot = dotProduct(x1, y1, x2, y2); // Gives magnitude and cosine of angle

// atan2 calculates angle in radians
// atan2 gives the signed angle between two vectors in radians
// https://en.wikipedia.org/wiki/Atan2
return Math.atan2(cross, dot);
}
};

export function isPointInPolygon(
/**
* Checks if a point is inside a polygon using the Winding Number Algorithm.
* @param point The point to check.
* @param polygon The polygon defined as an array of points.
* @returns True if the point is inside the polygon, false otherwise.
*/
export const isPointInPolygon = (
point: { x: number; y: number },
polygonPoints: { x: number; y: number }[],
): boolean {
): boolean => {
let windingNumber = 0;
const n = polygonPoints.length;
// TODO: Have we seen this point before? Memoize this
for (let i = 0; i < n; i++) {

// Travel around the polygon, taking pairs of points
for (let i = 0; i < polygonPoints.length; i++) {
const v1 = polygonPoints[i];
const v2 = polygonPoints[(i + 1) % n];
const v2 = polygonPoints[(i + 1) % polygonPoints.length];

// Compute the angle between vectors (point -> v1) and (point -> v2)
const angle = angleBetweenVectors(point, v1, v2);
Expand All @@ -66,7 +74,7 @@ export function isPointInPolygon(

// If the total winding number is not zero, the point is inside
return Math.abs(windingNumber) > EPSILON; // Use tolerance for floating-point errors
}
};

/**
* Checks if a point is inside any of the polygons in a multipolygon.
Expand Down

0 comments on commit 83980de

Please sign in to comment.