Skip to content

Commit

Permalink
Task/wg 403 create asset geometry component (#295)
Browse files Browse the repository at this point in the history
  • Loading branch information
sophia-massie authored Jan 8, 2025
1 parent 4cad581 commit dc02685
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 39 deletions.
14 changes: 6 additions & 8 deletions react/src/components/AssetDetail/AssetDetail.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
.middleSection {
flex: 1 1 auto;
overflow: hidden;
min-height: 0;
min-height: 20%;
display: flex;
text-align: center;
flex-direction: column;
Expand All @@ -41,26 +41,25 @@
}
.bottomSection {
display: block;
flex: 0 0 auto;
flex: 0 1 auto;
overflow-x: hidden;
justify-items: flex-end;
align-items: flex-end;
width: 100%;
}
.metadataTable {
flex-grow: 1;
width: 100%;
}
.metadataTable table {
width: 100%;
table-layout: fixed;
border-collapse: collapse;
margin: 5px;
margin-top: 5px;
margin-bottom: 5px;
padding: 5px;
}
.metadataTable thead,
th {
background: #d0d0d0;
background: var(--global-color-primary--light);
text-emphasis: var(--global-color-primary--dark);
}
.metadataTable tbody {
display: block;
Expand All @@ -81,5 +80,4 @@ th {
word-wrap: break-word;
word-break: break-word;
white-space: normal;
text-overflow: clip;
}
11 changes: 9 additions & 2 deletions react/src/components/AssetDetail/AssetDetail.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import AssetDetail from './AssetDetail';
import { mockImgFeature } from '@hazmapper/__fixtures__/featuresFixture';

Expand All @@ -10,6 +10,12 @@ jest.mock('@hazmapper/hooks', () => ({
}),
}));

jest.mock('./AssetGeometry', () => {
return function AssetGeometry() {
return <div data-testid="asset-geometry">Geometry Details</div>;
};
});

describe('AssetDetail', () => {
const AssetModalProps = {
onClose: jest.fn(),
Expand All @@ -19,10 +25,11 @@ describe('AssetDetail', () => {

it('renders all main components', () => {
const { getByText } = render(<AssetDetail {...AssetModalProps} />);
const assetGeometry = screen.getByTestId('asset-geometry');
// Check for title, button, and tables
expect(getByText('Photo 4.jpg')).toBeDefined();
expect(getByText('Download')).toBeDefined();
expect(getByText('Metadata')).toBeDefined();
expect(getByText('Geometry')).toBeDefined();
expect(assetGeometry).toBeDefined();
});
});
34 changes: 5 additions & 29 deletions react/src/components/AssetDetail/AssetDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { Suspense } from 'react';
import _ from 'lodash';
import AssetGeometry from './AssetGeometry';
import { useAppConfiguration } from '@hazmapper/hooks';
import { FeatureTypeNullable, Feature, getFeatureType } from '@hazmapper/types';
import { FeatureIcon } from '@hazmapper/components/FeatureIcon';
Expand Down Expand Up @@ -97,7 +98,9 @@ const AssetDetail: React.FC<AssetModalProps> = ({
<table>
<thead>
<tr>
<th colSpan={2}>Metadata</th>
<th colSpan={2} className="text-center">
Metadata
</th>
</tr>
</thead>
<tbody>
Expand Down Expand Up @@ -125,34 +128,7 @@ const AssetDetail: React.FC<AssetModalProps> = ({
)}
</tbody>
</table>
<table>
<thead>
<tr>
<th colSpan={2}>Geometry</th>
</tr>
</thead>
<tbody>
{selectedFeature?.geometry &&
Object.entries(selectedFeature.geometry).map(
([propKey, propValue]) =>
propValue &&
propValue !== undefined &&
propValue.toString().trim() !== '' &&
propValue.toString() !== 'null' && (
<tr key={propKey}>
<td>{_.trim(_.startCase(propKey.toString()), '"')}</td>
<td>
{' '}
{Array.isArray(propValue) && propValue.length === 2
? `Latitude: ${propValue[0].toString()},
Longitude: ${propValue[1].toString()}`
: _.trim(JSON.stringify(propValue), '"')}
</td>
</tr>
)
)}
</tbody>
</table>
<AssetGeometry selectedFeature={selectedFeature} />
</div>
</div>
</div>
Expand Down
28 changes: 28 additions & 0 deletions react/src/components/AssetDetail/AssetGeometry.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { render } from '@testing-library/react';
import GeometryAsset from './AssetGeometry';
import { mockImgFeature } from '@hazmapper/__fixtures__/featuresFixture';

jest.mock('@turf/turf', () => ({
...jest.requireActual('@turf/turf'),
area: jest.fn(() => 1234.56),
length: jest.fn(() => 5678.9),
bbox: jest.fn(() => [10, 20, 30, 40]),
}));

jest.mock('@hazmapper/hooks', () => ({
useFeatureSelection: jest.fn(),
}));

describe('AssetGeometry', () => {
const GeometryAssetProps = {
selectedFeature: mockImgFeature,
};

it('renders geometry for point on an image', () => {
const { getByText } = render(<GeometryAsset {...GeometryAssetProps} />);
expect(getByText('Geometry: Point')).toBeDefined();
expect(getByText('Latitude')).toBeDefined();
expect(getByText('Longitude')).toBeDefined();
});
});
118 changes: 118 additions & 0 deletions react/src/components/AssetDetail/AssetGeometry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React from 'react';
import _ from 'lodash';
import * as turf from '@turf/turf';
import { Feature, FeatureType } from '@hazmapper/types';

interface AssetGeometryProps {
selectedFeature: Feature;
}

const AssetGeometry: React.FC<AssetGeometryProps> = ({ selectedFeature }) => {
if (!selectedFeature?.geometry) return null;

const bbox =
selectedFeature.geometry.type !== 'Point'
? turf.bbox(selectedFeature)
: null;

const geometryType = selectedFeature.geometry.type;

return (
<>
{geometryType === FeatureType.Point && (
<table>
<thead>
<tr>
<th className="text-center" colSpan={2}>
Geometry: {_.startCase(selectedFeature.geometry.type)}
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Latitude</td>
<td>{turf.bbox(selectedFeature.geometry)[0]}</td>
</tr>
<tr>
<td>Longitude</td>
<td>{turf.bbox(selectedFeature.geometry)[1]}</td>
</tr>
</tbody>
</table>
)}
{(geometryType === FeatureType.Polygon ||
geometryType === FeatureType.MultiPolygon) && (
<table>
<thead>
<tr>
<th className="text-center" colSpan={2}>
Geometry: {_.startCase(selectedFeature.geometry.type)}
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Area (m²)</td>
<td>{turf.area(selectedFeature as turf.Feature).toFixed(2)}</td>
</tr>
</tbody>
</table>
)}
{(geometryType === FeatureType.LineString ||
geometryType === FeatureType.MultiLineString) && (
<table>
<thead>
<tr>
<th className="text-center" colSpan={2}>
Geometry: {_.startCase(selectedFeature.geometry.type)}
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Length (m)</td>
<td>{turf.length(selectedFeature as turf.Feature).toFixed(2)}</td>
</tr>
</tbody>
</table>
)}
{geometryType !== FeatureType.Point && bbox && (
<table>
<thead>
{selectedFeature.geometry.type === 'GeometryCollection' && (
<tr>
<th className="text-center" colSpan={2}>
Geometry: {_.startCase(selectedFeature.geometry.type)}
</th>
</tr>
)}
<tr>
<th colSpan={3} className="text-center">
Bounding Box
</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>Latitude</td>
<td>Longitude</td>
</tr>
<tr>
<td>Minimum</td>
<td>{turf.bbox(selectedFeature.geometry)[1]}</td>
<td>{turf.bbox(selectedFeature.geometry)[0]}</td>
</tr>
<tr>
<td>Maximum</td>
<td>{turf.bbox(selectedFeature.geometry)[3]}</td>
<td>{turf.bbox(selectedFeature.geometry)[2]}</td>
</tr>
</tbody>
</table>
)}
</>
);
};

export default AssetGeometry;

0 comments on commit dc02685

Please sign in to comment.