Skip to content

Commit

Permalink
Add unit test for aoi
Browse files Browse the repository at this point in the history
  • Loading branch information
hanbyul-here committed Nov 26, 2024
1 parent 07f890d commit 2b7a016
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import {

Check failure on line 1 in app/scripts/components/common/map/controls/hooks/use-custom-aoi.test.ts

View workflow job for this annotation

GitHub Actions / lint

Trailing spaces not allowed
getNumPoints,

Check failure on line 2 in app/scripts/components/common/map/controls/hooks/use-custom-aoi.test.ts

View workflow job for this annotation

GitHub Actions / lint

Trailing spaces not allowed
validateGeometryType,

Check failure on line 3 in app/scripts/components/common/map/controls/hooks/use-custom-aoi.test.ts

View workflow job for this annotation

GitHub Actions / lint

Trailing spaces not allowed
extractPolygonsFromGeojson,

Check failure on line 4 in app/scripts/components/common/map/controls/hooks/use-custom-aoi.test.ts

View workflow job for this annotation

GitHub Actions / lint

Trailing spaces not allowed
validateFeatureCount,

Check failure on line 5 in app/scripts/components/common/map/controls/hooks/use-custom-aoi.test.ts

View workflow job for this annotation

GitHub Actions / lint

Trailing spaces not allowed
removePolygonHoles,

Check failure on line 6 in app/scripts/components/common/map/controls/hooks/use-custom-aoi.test.ts

View workflow job for this annotation

GitHub Actions / lint

Trailing spaces not allowed
simplifyFeatures,

Check failure on line 7 in app/scripts/components/common/map/controls/hooks/use-custom-aoi.test.ts

View workflow job for this annotation

GitHub Actions / lint

Trailing spaces not allowed
getAoiAppropriateFeatures,
PolygonGeojson,
eachFeatureMaxPointNum,
maxPolygonNum
} from './use-custom-aoi';
import { Feature, MultiPolygon, Polygon } from '@turf/helpers';

Check failure on line 13 in app/scripts/components/common/map/controls/hooks/use-custom-aoi.test.ts

View workflow job for this annotation

GitHub Actions / lint

`@turf/helpers` import should occur before import of `./use-custom-aoi`

// Mock data
const mockPolygon: Feature<Polygon> = {
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [[[0, 0], [1, 1], [2, 2], [0, 0]]]
},
properties: {}
};

const mockMultiPolygon: Feature<MultiPolygon> = {
type: 'Feature',
geometry: {
type: 'MultiPolygon',
coordinates: [[[[0, 0], [1, 1], [2, 2], [0, 0]]]]
},
properties: {}
};

const mockGeojson = {
type: 'FeatureCollection',
features: [mockPolygon, mockMultiPolygon]
} as PolygonGeojson;

describe('getNumPoints', () => {
it('should return the number of points in a polygon', () => {
const numPoints = getNumPoints(mockPolygon);
expect(numPoints).toBe(4);
});
});

describe('validateGeometryType', () => {
it('should validate that all features are polygons or multipolygons', () => {
const isValid = validateGeometryType(mockGeojson);
expect(isValid).toBe(true);
});

it('should return false for invalid geometry types', () => {
const invalidGeojson = {
...mockGeojson,
features: [{ type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: {} }]
};
// @ts-expect-error

Check failure on line 57 in app/scripts/components/common/map/controls/hooks/use-custom-aoi.test.ts

View workflow job for this annotation

GitHub Actions / lint

Include a description after the "@ts-expect-error" directive to explain why the @ts-expect-error is necessary. The description must be 3 characters or longer
const isValid = validateGeometryType(invalidGeojson);
expect(isValid).toBe(false);
});
});

describe('extractPolygonsFromGeojson', () => {
it('should extract all polygons from GeoJSON, including from MultiPolygons', () => {
const polygons = extractPolygonsFromGeojson(mockGeojson);
expect(polygons).toHaveLength(2);
expect(polygons[0].geometry.type).toBe('Polygon');
});
});

describe('validateFeatureCount', () => {
it('should validate that the number of features is within the allowed limit', () => {
const isValid = validateFeatureCount([mockPolygon]);
expect(isValid).toBe(true);
});

it('should return false if feature count exceeds the limit', () => {
const tooManyFeatures = Array(maxPolygonNum + 1).fill(mockPolygon);
const isValid = validateFeatureCount(tooManyFeatures);
expect(isValid).toBe(false);
});
});

describe('removePolygonHoles', () => {
it('should remove holes from polygons and return warnings if holes are removed', () => {
const polygonWithHole = {
...mockPolygon,
geometry: {
...mockPolygon.geometry,
coordinates: [[[0, 0], [1, 1], [2, 2], [0, 0]], [[0.1, 0.1], [0.2, 0.2], [0.3, 0.3], [0.1, 0.1]]]
}
};
const { simplifiedFeatures, warnings } = removePolygonHoles([polygonWithHole]);
expect(simplifiedFeatures[0].geometry.coordinates).toHaveLength(1);
expect(warnings).toContain('Polygons with rings are not supported and were simplified to remove them');
});
});

describe('simplifyFeatures', () => {
it('should simplify features to reduce the number of points if necessary', () => {
const largePolygon = {
...mockPolygon,
geometry: {
...mockPolygon.geometry,
coordinates: [
Array.from({ length: 600 }, (_, i) => [Math.cos(i * (2 * Math.PI / 600)), Math.sin(i * (2 * Math.PI / 600))])
.concat([[1, 0]]) // Ensure the ring is closed
]
}
};
const { simplifiedFeatures, warnings } = simplifyFeatures([largePolygon]);
expect(getNumPoints(simplifiedFeatures[0])).toBeLessThanOrEqual(eachFeatureMaxPointNum);
expect(warnings).toContainEqual(expect.stringContaining('simplified'));
});
});

describe('getAoiAppropriateFeatures', () => {
it('should process GeoJSON and return simplified features with warnings', () => {
const result = getAoiAppropriateFeatures(mockGeojson);
expect(result.simplifiedFeatures).toHaveLength(2);
expect(result.warnings).toEqual(expect.any(Array));
});

it('should throw an error if geometry type validation fails', () => {
const invalidGeojson = {
...mockGeojson,
features: [{ type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: {} }]
};
// @ts-expect-error

Check failure on line 129 in app/scripts/components/common/map/controls/hooks/use-custom-aoi.test.ts

View workflow job for this annotation

GitHub Actions / lint

Include a description after the "@ts-expect-error" directive to explain why the @ts-expect-error is necessary. The description must be 3 characters or longer
expect(() => getAoiAppropriateFeatures(invalidGeojson)).toThrow('Wrong geometry type. Only polygons or multi polygons are accepted.');
});

it('should throw an error if feature count exceeds the allowed limit', () => {
const tooManyFeatures = {
...mockGeojson,
features: Array(maxPolygonNum + 1).fill(mockPolygon)
};
expect(() => getAoiAppropriateFeatures(tooManyFeatures)).toThrow('Only files with up to 200 polygons are accepted.');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { multiPolygonToPolygons } from '../../utils';
import { round } from '$utils/format';

const extensions = ['geojson', 'json', 'zip'];
const eachFeatureMaxPointNum = 500;
export const eachFeatureMaxPointNum = 500;
const maxTolerance = 5;
const maxPolygonNum = 200;
export const maxPolygonNum = 200;
export const acceptExtensions = extensions.map((ext) => `.${ext}`).join(', ');

export interface FileInfo {
Expand All @@ -18,25 +18,25 @@ export interface FileInfo {
type: 'Shapefile' | 'GeoJSON';
}

interface PolygonGeojson {
export interface PolygonGeojson {
type: 'FeatureCollection';
features: Feature<Polygon | MultiPolygon>[];
}

function getNumPoints(feature: Feature<Polygon>): number {
export function getNumPoints(feature: Feature<Polygon>): number {
return feature.geometry.coordinates.reduce((acc, current) => {
return acc + current.length;
}, 0);
}

function validateGeometryType(geojson: PolygonGeojson): boolean {
export function validateGeometryType(geojson: PolygonGeojson): boolean {
const hasInvalidGeometry = geojson.features.some(
(feature) => !['MultiPolygon', 'Polygon'].includes(feature.geometry.type)
);
return !hasInvalidGeometry;
}

function extractPolygonsFromGeojson(
export function extractPolygonsFromGeojson(
geojson: PolygonGeojson
): Feature<Polygon>[] {
return geojson.features.reduce(
Expand All @@ -52,11 +52,11 @@ function extractPolygonsFromGeojson(
);
}

function validateFeatureCount(features: Feature<Polygon>[]): boolean {
export function validateFeatureCount(features: Feature<Polygon>[]): boolean {
return features.length <= maxPolygonNum;
}

function removePolygonHoles(features: Feature<Polygon>[]): {
export function removePolygonHoles(features: Feature<Polygon>[]): {
simplifiedFeatures: Feature<Polygon>[];
warnings: string[];
} {
Expand Down Expand Up @@ -84,7 +84,7 @@ function removePolygonHoles(features: Feature<Polygon>[]): {
return { simplifiedFeatures, warnings };
}

function simplifyFeatures(features: Feature<Polygon>[]): {
export function simplifyFeatures(features: Feature<Polygon>[]): {
simplifiedFeatures: Feature<Polygon>[];
warnings: string[];
} {
Expand Down

0 comments on commit 2b7a016

Please sign in to comment.