Skip to content

Commit

Permalink
connect frontend and backend, known issue
Browse files Browse the repository at this point in the history
  • Loading branch information
dbarkowsky committed Oct 4, 2024
1 parent ba97a8f commit 5901fea
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const getLinkedProjects = async (req: Request, res: Response) => {
*/
export const getPropertiesForMap = async (req: Request, res: Response) => {
// parse for filter
console.log(req.query);
const filter = MapFilterSchema.safeParse(req.query);
if (filter.success == false) {
return res.status(400).send(filter.error);
Expand Down
11 changes: 1 addition & 10 deletions express-api/src/controllers/properties/propertiesSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,7 @@ 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(),
Polygon: z.string().optional(),
});

export type MapFilter = z.infer<typeof MapFilterSchema>;
Expand Down
23 changes: 17 additions & 6 deletions express-api/src/services/properties/propertiesServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
constructFindOptionFromQuerySingleSelect,
} from '@/utilities/helperFunctions';
import userServices from '../users/usersServices';
import { Brackets, FindOptionsWhere, ILike, In, QueryRunner, Raw } from 'typeorm';
import { Brackets, FindOptionsWhere, ILike, In, QueryRunner } from 'typeorm';
import { PropertyType } from '@/constants/propertyType';
import { exposedProjectStatuses, ProjectStatus } from '@/constants/projectStatus';
import { ProjectProperty } from '@/typeorm/Entities/ProjectProperty';
Expand Down Expand Up @@ -212,7 +212,6 @@ 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 @@ -247,10 +246,9 @@ const getPropertiesForMap = async (filter?: MapFilter) => {
],
});

if (filter.Polygon)
return properties.filter((property) =>
isPointInMultiPolygon(property.Location, filter.Polygon as { x: number; y: number }[][]),
);
if (filter.Polygon) {
return filterPropertiesByPolygon(filter.Polygon, properties);
}
return properties;
}
/**
Expand All @@ -264,9 +262,22 @@ const getPropertiesForMap = async (filter?: MapFilter) => {
AgencyId: filter.AgencyIds ? In(filter.AgencyIds) : undefined,
},
});
if (filter.Polygon) {
return filterPropertiesByPolygon(filter.Polygon, properties);
}
return properties;
};

const filterPropertiesByPolygon = (polygon: string, properties: MapProperties[]) => {
const polygonArray = JSON.parse(polygon);
const formattedPolygons = (polygonArray as number[][][]).map((polygon) =>
polygon.map((point) => ({ x: point.at(1), y: point.at(0) })),
);
return properties.filter((property) =>
isPointInMultiPolygon(property.Location, formattedPolygons as { x: number; y: number }[][]),
);
};

const numberOrNull = (value: any) => {
if (value == '' || value == null) return null;
return typeof value === 'number' ? value : Number(value.replace?.(/-/g, ''));
Expand Down
7 changes: 6 additions & 1 deletion express-api/src/utilities/polygonMath.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// FIXME: isPointInPolygon fails in some cases due to floating point math...

/**
* Checks if a point is inside a polygon using the Ray-Casting algorithm.
* @param point The point to check.
Expand All @@ -20,7 +22,10 @@ export const isPointInPolygon = (

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

if (intersect) isInside = !isInside;
if (intersect) {
isInside = !isInside;
return isInside;
}
}

return isInside;
Expand Down
21 changes: 21 additions & 0 deletions express-api/tests/unit/utilities/polygonMath.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { isPointInPolygon } from '@/utilities/polygonMath';

describe('isPointInPolygon', () => {
const polygon = [
{ x: -124.12628173828126, y: 48.721773219750666 },
{ x: -123.89007568359376, y: 48.84664340683584 },
{ x: -124.13177490234376, y: 48.93152205931365 },
{ x: -124.26361083984376, y: 48.828565527993234 },
];
it('should return true if the point is outside the polygon', () => {
const point = {
x: -124.0548,
y: 48.8206,
};
expect(isPointInPolygon(point, polygon)).toEqual(true);
});
it('should return false if the point is outside the polygon', () => {
const point = { x: -125.135, y: 48.8274 };
expect(isPointInPolygon(point, polygon)).toEqual(false);
});
});
12 changes: 9 additions & 3 deletions react-app/src/components/map/ParcelMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import MapSidebar from '@/components/map/sidebar/MapSidebar';
import ClusterPopup, { PopupState } from '@/components/map/clusterPopup/ClusterPopup';
import { ParcelLayerFeature } from '@/hooks/api/useParcelLayerApi';
import PolygonQuery, { LeafletMultiPolygon } from '@/components/map/polygonQuery/PolygonQuery';
import { Position } from 'geojson';

type ParcelMapProps = {
height: string;
Expand Down Expand Up @@ -79,15 +78,22 @@ const ParcelMap = (props: ParcelMapProps) => {
const [tileLayerName, setTileLayerName] = useState<string>('Street Map');
const [polygonQueryShape, setPolygonQueryShape] = useState<LeafletMultiPolygon>({
type: 'MultiPolygon',
coordinates: [] as Position[][][],
coordinates: [],
leafletIds: [],
});
const [mapEventsDisabled, setMapEventsDisabled] = useState<boolean>(false);

// When drawn multipolygon changes, query the new area
useEffect(() => {
console.log(polygonQueryShape);
}, [polygonQueryShape]);
const polygonCoordinates = polygonQueryShape.coordinates.map((polygon) =>
polygon.map((point) => [point.lat, point.lng]),
);
setFilter({
...filter,
Polygon: polygonCoordinates.length ? JSON.stringify(polygonCoordinates) : undefined,
});
}, [polygonQueryShape, setFilter]);

// Get properties for map.
const { data, refreshData, isLoading } = useDataLoader(() =>
Expand Down
9 changes: 6 additions & 3 deletions react-app/src/components/map/polygonQuery/PolygonQuery.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { MultiPolygon } from 'geojson';
import React, { Dispatch, SetStateAction } from 'react';
import { FeatureGroup, Popup } from 'react-leaflet';
import { EditControl } from 'react-leaflet-draw';
Expand All @@ -8,8 +7,10 @@ interface PolygonQueryProps {
setMapEventsDisabled: Dispatch<SetStateAction<boolean>>;
}

export interface LeafletMultiPolygon extends MultiPolygon {
export interface LeafletMultiPolygon {
leafletIds: number[];
coordinates: { lat: number; lng: number }[][];
type: 'MultiPolygon';
}

const PolygonQuery = (props: PolygonQueryProps) => {
Expand Down Expand Up @@ -57,7 +58,9 @@ const PolygonQuery = (props: PolygonQueryProps) => {
// Find the index of the original polygon
const originalIndex = original.leafletIds.findIndex((id) => id === layer._leaflet_id);
// Use original index to replace with new coordinates
replacementCoordinates[originalIndex] = layer._latlngs;
replacementCoordinates[originalIndex] = (
layer._latlngs as [[{ lat: number; lng: number }]]
)[0];
});
return {
...original,
Expand Down
1 change: 1 addition & 0 deletions react-app/src/hooks/api/usePropertiesApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface MapFilter {
PropertyTypeIds?: number[];
RegionalDistrictIds?: number[];
Name?: string;
Polygon?: string;
}

export interface PropertyUnion {
Expand Down

0 comments on commit 5901fea

Please sign in to comment.