Skip to content

Commit

Permalink
[feat-5691]: wfs objects detail panel (#2842)
Browse files Browse the repository at this point in the history
* refactor map-data-to-selectable-feature

* Fix test case

* add mapper for caterpillar object data

* add test cases

* fix test

* add test case map-to-selectable-feature

* add test cases

* remove selectable component when feature is selected

* show description of status in detail panel

* fix status on detail panel

* re-add datapunt key

* fix typo

* Fix types
  • Loading branch information
vdegraaf authored Apr 30, 2024
1 parent 11622be commit ea8a07f
Show file tree
Hide file tree
Showing 23 changed files with 620 additions and 335 deletions.
29 changes: 6 additions & 23 deletions app.amsterdam.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"showVulaanControls": true,
"useProjectenSignalType": false,
"showContactEdit": true

},
"head": {
"androidIcon": "/icon_192x192.png",
Expand Down Expand Up @@ -71,19 +70,10 @@
"municipality": "amsterdam \"ouder-amstel\" weesp",
"options": {
"crs": "EPSG:28992",
"center": [
52.3731081,
4.8932945
],
"center": [52.3731081, 4.8932945],
"maxBounds": [
[
52.25168,
4.64034
],
[
52.50536,
5.10737
]
[52.25168, 4.64034],
[52.50536, 5.10737]
],
"maxNumberOfAssets": {
"straatverlichting": 3,
Expand All @@ -100,22 +90,15 @@
"minZoom": 7,
"zoom": 7
},
"optionsAreaMap": {
"optionsAreaMap": {
"focusRadiusMeters": 100,
"zoom": 13
},
"tiles": {
"args": [
"https://{s}.data.amsterdam.nl/topo_rd/{z}/{x}/{y}.png"
],
"args": ["https://{s}.data.amsterdam.nl/topo_rd/{z}/{x}/{y}.png"],
"options": {
"attribution": "<span aria-hidden='true'>Kaartgegevens CC-BY-4.0 Gemeente Amsterdam</span>",
"subdomains": [
"t1",
"t2",
"t3",
"t4"
],
"subdomains": ["t1", "t2", "t3", "t4"],
"tms": true
}
},
Expand Down
1 change: 1 addition & 0 deletions src/shared/services/map-location/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export {
pointWithinBounds,
serviceResultToAddress,
wktPointToLocation,
sanitizeCoordinates,
} from './map-location'
export type {
PdokResponse,
Expand Down
2 changes: 1 addition & 1 deletion src/shared/services/map-location/map-location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { Incident } from 'types/api/incident'
import type { Geometrie, Location } from 'types/incident'
import type { RevGeo, Doc } from 'types/pdok/revgeo'

const sanitizeCoordinates = (coordinates: LatLngTuple): LatLngTuple =>
export const sanitizeCoordinates = (coordinates: LatLngTuple): LatLngTuple =>
coordinates.sort((a, b) => (a > b ? 1 : -1)).reverse() as LatLngTuple

export const coordinatesToFeature = ({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (C) 2023 Gemeente Amsterdam
import { useSelectionProps } from './hooks/useSelectionProps'
import { StyledLabel, ListItem } from './styled'
import { StyledLabel, ListItem, StyledStatusDescription } from './styled'
import { IconListItem } from '../../../../../../../components/IconList'
import type { FeatureType, Item, FeatureStatusType } from '../../types'
import type { SelectableFeature } from '../../types'
Expand Down Expand Up @@ -38,7 +38,14 @@ export const AssetListItemSelectable = ({
item={item}
checked={false}
>
<StyledLabel>{item.label}</StyledLabel>
<StyledLabel>
{item.label}
{featureStatusType?.description && (
<StyledStatusDescription status={featureStatusType.typeValue}>
{featureStatusType?.description}
</StyledStatusDescription>
)}
</StyledLabel>
</IconListItem>
</ListItem>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@ export const useSelectionProps = ({
const { setItem } = useContext(AssetSelectContext)
const { maxAssetWarning } = useSelector(makeSelectMaxAssetWarning)
const featureStatusType = featureStatusTypes.find(
({ typeValue }) => typeValue === status
({ typeValue }) => typeValue === feature.status
)
const item: Item = {
...feature,
address: undefined,
status: featureStatusType?.typeValue,
}

if (selection?.find((item) => item.id === feature.id)) return null
if (selection?.find((item) => item.id?.toString() === feature.id.toString()))
return null

const { icon }: Partial<FeatureType> =
featureTypes?.find(({ typeValue }) => typeValue === feature.type) ?? {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import type { FunctionComponent, ReactNode } from 'react'
import { useContext } from 'react'

import { Map } from '@amsterdam/react-maps'
import { act, render, screen } from '@testing-library/react'
import { act, render, screen, waitFor } from '@testing-library/react'
import type { FeatureCollection } from 'geojson'
import type { FetchMock } from 'jest-fetch-mock'
import type { MapOptions } from 'leaflet'
import type { LatLngTuple, MapOptions } from 'leaflet'

import configuration from 'shared/services/configuration/configuration'
import MAP_OPTIONS from 'shared/services/configuration/map-options'
import { sanitizeCoordinates } from 'shared/services/map-location'
import assetsJson from 'utils/__tests__/fixtures/assets.json'

import WfsDataContext, { NO_DATA } from './context'
Expand Down Expand Up @@ -100,9 +101,24 @@ describe('src/signals/incident/components/form/AssetSelect/WfsLayer', () => {
)
)

await screen.findByTestId('map-test')
expect(setContextData).toHaveBeenCalledWith(assetsJson)
expect(fetchMock).toHaveBeenCalledTimes(1)
const sanitizedAssetsJson = {
...assetsJson,
features: assetsJson.features.map((feature) => {
return {
...feature,
geometry: {
...feature.geometry,
coordinates: sanitizeCoordinates(
feature.geometry.coordinates as LatLngTuple
),
},
}
}),
}

await waitFor(() => {
expect(setContextData).toHaveBeenCalledWith(sanitizedAssetsJson)
})
})

it('should not render when an AbortError occurs in the wfs call', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ import AssetSelectContext from 'signals/incident/components/form/MapSelectors/As
import type { DataLayerProps } from 'signals/incident/components/form/MapSelectors/types'

import { NO_DATA, WfsDataProvider } from './context'
import { mapDataToSelectableFeature } from './utils'
import { isCaterpillarCategory } from './utils/is-caterpillar-category'
import { mapCaterpillarFeatures } from './utils/map-caterpillar-features'
import { mapDataToSelectableFeature } from './utils/map-data-to-selectable-feature'
import useBoundingBox from '../../../hooks/useBoundingBox'
import useLayerVisible from '../../../hooks/useLayerVisible'

export const SRS_NAME = 'EPSG:4326'

export interface WfsLayerProps {
Expand Down Expand Up @@ -89,14 +92,21 @@ const WfsLayer: FunctionComponent<WfsLayerProps> = ({
// eslint-disable-next-line no-console
console.error('Unhandled Error in wfs call', result.error.message)
} else {
setData(result)
let mappedResult = result

if (isCaterpillarCategory(result.features[0])) {
mappedResult = mapCaterpillarFeatures(result)
}

setData(mappedResult)

const mappedResult = mapDataToSelectableFeature(
result.features,
meta.featureTypes
const selectableFeatures = mapDataToSelectableFeature(
mappedResult.features,
meta.featureTypes,
meta.featureStatusTypes
)

setSelectableFeatures(mappedResult)
setSelectableFeatures(selectableFeatures)
}
return null
})
Expand All @@ -123,6 +133,7 @@ const WfsLayer: FunctionComponent<WfsLayerProps> = ({
filter,
setSelectableFeatures,
meta.featureTypes,
meta.featureStatusTypes,
])

return <WfsDataProvider value={data}>{children}</WfsDataProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (C) 2023 Gemeente Amsterdam
import { getFeatureType } from './get-feature-type'
import { mockContainerFeatureTypes } from './test/mock-feature-types'
import { mockGlasContainer } from './test/mock-objects'
import {
mockContainerFeatureTypes,
mockCaterpillarFeatureTypes,
} from './test/mock-feature-types'
import { mockContainers, mockCaterpillarFeature } from './test/mock-objects'

describe('getFeatureType', () => {
it('should return the correct feature type', () => {
const result = getFeatureType(mockGlasContainer, mockContainerFeatureTypes)
it('should return the container feature type', () => {
const result = getFeatureType(mockContainers[0], mockContainerFeatureTypes)

expect(result).toEqual(mockContainerFeatureTypes[2])
expect(result).toEqual(mockContainerFeatureTypes[1])
})

it('should return the caterpillar feature type', () => {
const result = getFeatureType(
mockCaterpillarFeature[0],
mockCaterpillarFeatureTypes
)

expect(result).toEqual(mockCaterpillarFeatureTypes[0])
})
})
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (C) 2023 Gemeente Amsterdam
import type { Feature } from 'geojson'
// Copyright (C) 2024 Gemeente Amsterdam
import type { Feature } from 'signals/incident/components/form/MapSelectors/types'

import type { FeatureType } from '../../../../types'

export const getFeatureType = (
feature: Feature,
featureTypes: FeatureType[]
): FeatureType =>
featureTypes.find(
({ typeField, typeValue }) => feature.properties?.[typeField] === typeValue
) as FeatureType
export const getFeatureType = (feature: Feature, featureTypes: FeatureType[]) =>
featureTypes.find(({ typeField, typeValue }) => {
return feature.properties?.[typeField] === typeValue
})

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { isCaterpillarCategory } from './is-caterpillar-category'
import { mockCaterpillarFeatureGeo } from './test/mock-objects'

describe('isCaterpillarCategory', () => {
it('should return true when feature id is a number and species is in properties', () => {
expect(isCaterpillarCategory(mockCaterpillarFeatureGeo[0])).toBe(true)
})

it('should return true when feature id is a number and species is in properties array', () => {
expect(isCaterpillarCategory(mockCaterpillarFeatureGeo[1])).toBe(true)
})

it('should return false when feature id is not a number', () => {
expect(
isCaterpillarCategory({ ...mockCaterpillarFeatureGeo[0], id: undefined })
).toBe(false)
})

it('should return false when species is not in properties', () => {
expect(
isCaterpillarCategory({
...mockCaterpillarFeatureGeo[0],
properties: [{ type: 'tree' }],
})
).toBe(false)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { Feature as FeatureGeo } from 'geojson'

export const isCaterpillarCategory = (feature: FeatureGeo) => {
return (
typeof feature.id === 'number' &&
feature.properties &&
// properties is an object or an array with a species
('species' in feature.properties || 'species' in feature.properties[0])
)
}
Loading

0 comments on commit ea8a07f

Please sign in to comment.