Skip to content

Commit

Permalink
initial backend attempt, no postGIS
Browse files Browse the repository at this point in the history
  • Loading branch information
dbarkowsky committed Oct 4, 2024
1 parent 7546e09 commit ba97a8f
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 1 deletion.
10 changes: 10 additions & 0 deletions express-api/src/controllers/properties/propertiesSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ export const MapFilterSchema = z.object({
Name: z.string().optional(),
RegionalDistrictIds: arrayFromString(numberSchema),
UserAgencies: z.array(z.number().int()).optional(),
Polygon: z
.array(
z.array(
z.object({
x: z.number(),
y: z.number(),
}),
),
)
.optional(),
});

export type MapFilter = z.infer<typeof MapFilterSchema>;
Expand Down
1 change: 1 addition & 0 deletions express-api/src/routes/propertiesRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const {
router.route('/search/fuzzy').get(userAuthCheck(), catchErrors(getPropertiesFuzzySearch));

router.route('/search/geo').get(userAuthCheck(), catchErrors(getPropertiesForMap)); // Formerly wfs route
// router.route('/search/geo/polygon').get(userAuthCheck(), catchErrors()) // TODO: Write Swagger for this route

router.route('/search/linkedProjects').get(userAuthCheck(), catchErrors(getLinkedProjects));

Expand Down
9 changes: 8 additions & 1 deletion express-api/src/services/properties/propertiesServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ import {
constructFindOptionFromQuerySingleSelect,
} from '@/utilities/helperFunctions';
import userServices from '../users/usersServices';
import { Brackets, FindOptionsWhere, ILike, In, QueryRunner } from 'typeorm';
import { Brackets, FindOptionsWhere, ILike, In, QueryRunner, Raw } from 'typeorm';
import { PropertyType } from '@/constants/propertyType';
import { exposedProjectStatuses, ProjectStatus } from '@/constants/projectStatus';
import { ProjectProperty } from '@/typeorm/Entities/ProjectProperty';
import { ProjectStatus as ProjectStatusEntity } from '@/typeorm/Entities/ProjectStatus';
import { parentPort } from 'worker_threads';
import { ErrorWithCode } from '@/utilities/customErrors/ErrorWithCode';
import { PimsRequestUser } from '@/middleware/userAuthCheck';
import { isPointInMultiPolygon } from '@/utilities/polygonMath';

/**
* Perform a fuzzy search for properties based on the provided keyword.
Expand Down Expand Up @@ -211,6 +212,7 @@ const getPropertiesForMap = async (filter?: MapFilter) => {
Name: filter.Name ? ILike(`%${filter.Name}%`) : undefined,
PropertyTypeId: filter.PropertyTypeIds ? In(filter.PropertyTypeIds) : undefined,
RegionalDistrictId: filter.RegionalDistrictIds ? In(filter.RegionalDistrictIds) : undefined,
Location: filter.Polygon ? Raw(``) : undefined,
};

/**
Expand Down Expand Up @@ -244,6 +246,11 @@ const getPropertiesForMap = async (filter?: MapFilter) => {
},
],
});

if (filter.Polygon)
return properties.filter((property) =>
isPointInMultiPolygon(property.Location, filter.Polygon as { x: number; y: number }[][]),
);
return properties;
}
/**
Expand Down
45 changes: 45 additions & 0 deletions express-api/src/utilities/polygonMath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Checks if a point is inside a polygon using the Ray-Casting algorithm.
* @param point The point to check.
* @param polygonPoints 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 => {
let isInside = false;
const { x, y } = point;
const n = polygonPoints.length;

for (let i = 0, j = n - 1; i < n; j = i++) {
const xi = polygonPoints[i].x,
yi = polygonPoints[i].y;
const xj = polygonPoints[j].x,
yj = polygonPoints[j].y;

const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;

if (intersect) isInside = !isInside;
}

return isInside;
};

/**
* Checks if a point is inside any of the polygons in a multipolygon.
* @param point The point to check.
* @param multiPolygon The multipolygon, an array of polygons.
* @returns True if the point is inside any polygon, false otherwise.
*/
export const isPointInMultiPolygon = (
point: { x: number; y: number },
multiPolygon: { x: number; y: number }[][],
): boolean => {
for (const polygon of multiPolygon) {
if (isPointInPolygon(point, polygon)) {
return true;
}
}
return false;
};

0 comments on commit ba97a8f

Please sign in to comment.