Skip to content

Commit

Permalink
Flter map features by deployment and platform
Browse files Browse the repository at this point in the history
  • Loading branch information
willemarcel committed Dec 12, 2023
1 parent b709501 commit ad52cf2
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 91 deletions.
54 changes: 54 additions & 0 deletions src/components/timeline/__tests__/map.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from "react"
import renderer from "react-test-renderer"
import { act } from "react-dom/test-utils"

import { MapLegend } from "../map"
import { LineIcon, CircleIcon } from "../../../icons"

describe("MapLegend", () => {
it("render options", () => {
const fn = jest.fn()
const element = renderer.create(
<MapLegend
platforms={[
{ name: "Falcon", type: "Jet" },
{ name: "Campaign FS", type: "static" },
]}
selectedPlatform={""}
setSelectedPlatform={fn}
/>
)
const instance = element.root
expect(instance.findAllByType("button").length).toBe(2)
const b1 = instance.findAllByType("button")[0]
expect(
instance.findAllByType("button").every(i => !i.props.selected)
).toBeTruthy()
act(() => b1.props.onClick())
expect(fn).toHaveBeenCalledTimes(1)
expect(instance.findByType(LineIcon).props.size).toBe("text")
expect(instance.findByType(CircleIcon).props.size).toBe("extra-tiny")
})
it("render option with one selected", () => {
const fn = jest.fn()
const element = renderer.create(
<MapLegend
platforms={[
{ name: "Falcon", type: "Jet" },
{ name: "Campaign FS", type: "static" },
]}
selectedPlatform={"Falcon"}
setSelectedPlatform={fn}
/>
)
const instance = element.root
expect(instance.findAllByType("button").length).toBe(2)
const [b1, b2] = instance.findAllByType("button")
expect(b1.children[0].props.selected).toBeTruthy()
expect(b2.children[0].props.selected).toBeFalsy()
act(() => b1.props.onClick())
expect(fn).toHaveBeenCalledTimes(1)
act(() => b2.props.onClick())
expect(fn).toHaveBeenCalledTimes(2)
})
})
183 changes: 102 additions & 81 deletions src/components/timeline/map.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react"
import React, { useEffect, useState } from "react"
import PropTypes from "prop-types"
import styled from "styled-components"

Expand All @@ -7,8 +7,16 @@ import Source from "../map/source"
import Layer from "../map/layer"
import { getUniquePlatforms } from "../../utils/get-unique-platforms"
import { LineIcon, CircleIcon } from "../../icons"
import { mapLayerFilter } from "../../utils/filter-utils"
import { colors } from "../../theme"

export default function DeploymentMap({ geojson, deployments, bounds }) {
export function DeploymentMap({
geojson,
deployments,
bounds,
selectedDeployment,
}) {
const [selectedPlatform, setSelectedPlatform] = useState("")
const platforms = getUniquePlatforms(
deployments.flatMap(d => d.collectionPeriods)
).map(i => ({ name: i.item.shortname, type: i.item.platformType.shortname }))
Expand All @@ -27,34 +35,25 @@ export default function DeploymentMap({ geojson, deployments, bounds }) {
data: geojson,
}}
>
<Layer
<DeploymentLayer
config={{
id: "flights",
type: "line",
source: "deployment",
paint: {
"line-color": "#1B9E77",
"line-width": 2,
"line-opacity": 0.6,
"line-opacity": 0.9,
},
visible: true,
}}
onLoad={map => map.fitBounds(bounds, { padding: 20 })}
selectedPlatform={selectedPlatform}
selectedDeployment={
selectedDeployment ? selectedDeployment.longname : ""
}
/>
<Layer
config={{
id: "highlighted-platform",
type: "line",
source: "deployment",
paint: {
"line-color": "#1B9E77",
"line-width": 2,
"line-opacity": 1,
},
filter: ["==", "platform_name", ""],
}}
/>
<Layer
<DeploymentLayer
config={{
id: "static-locations",
type: "circle",
Expand All @@ -64,7 +63,7 @@ export default function DeploymentMap({ geojson, deployments, bounds }) {
"circle-opacity": {
base: 1.5,
stops: [
[10, 0.45],
[10, 0.65],
[14, 0.85],
],
},
Expand All @@ -77,75 +76,94 @@ export default function DeploymentMap({ geojson, deployments, bounds }) {
],
},
"circle-stroke-width": 1,
"circle-stroke-opacity": 0.9,
"circle-stroke-color": "#E8E845",
},
filter: ["==", "$type", "Point"],
}}
/>
<Layer
config={{
id: "highlighted-static-locations",
type: "circle",
source: "deployment",
paint: {
"circle-color": "#E8E845",
"circle-opacity": 1,
"circle-radius": {
base: 3,
stops: [
[10, 8],
[16, 16],
[20, 20],
],
},
"circle-stroke-width": 1,
"circle-stroke-opacity": 0.9,
"circle-stroke-opacity": 0.1,
"circle-stroke-color": "#E8E845",
},
filter: [
"all",
["==", "$type", "Point"],
["==", "platform_name", ""],
],
filter: ["all", ["==", "$type", "Point"]],
}}
selectedPlatform={selectedPlatform}
selectedDeployment={
selectedDeployment ? selectedDeployment.longname : ""
}
/>
</Source>
)}
<MapLegend platforms={platforms} />
<MapLegend
platforms={platforms}
selectedPlatform={selectedPlatform}
setSelectedPlatform={setSelectedPlatform}
/>
</Map>
)
}

const MapLegend = ({ map, platforms = [] }) => {
const [highlightedPlatform, setHighlightedPlatform] = useState("")
const highlight = platform => {
if (highlightedPlatform !== platform) {
setHighlightedPlatform(platform)
map.setFilter("highlighted-platform", ["==", "platform_name", platform])
map.setPaintProperty("flights", "line-opacity", 0.1)
map.setFilter("highlighted-static-locations", [
"all",
["==", "$type", "Point"],
["==", "platform_name", platform],
])
} else {
setHighlightedPlatform("")
map.setFilter("highlighted-platform", ["==", "platform_name", ""])
map.setPaintProperty("flights", "line-opacity", 0.6)
map.setFilter("highlighted-static-locations", [
"all",
["==", "$type", "Point"],
["==", "platform_name", ""],
])
DeploymentMap.propTypes = {
geojson: PropTypes.object,
deployments: PropTypes.array,
bounds: PropTypes.array,
selectedDeployment: PropTypes.object,
}

const DeploymentLayer = ({
map,
config,
selectedDeployment,
selectedPlatform,
onLoad,
}) => {
const mapHasStarted = map !== undefined

useEffect(() => {
if (mapHasStarted) {
const newFilter = mapLayerFilter(
map.getFilter(config.id),
"deployment",
selectedDeployment
)
map.setFilter(config.id, newFilter)
}
}
}, [selectedDeployment, mapHasStarted])

useEffect(() => {
if (mapHasStarted) {
const newFilter = mapLayerFilter(
map.getFilter(config.id),
"platform_name",
selectedPlatform
)
map.setFilter(config.id, newFilter)
}
}, [selectedPlatform, mapHasStarted])

return <Layer config={config} onLoad={onLoad} map={map} />
}

DeploymentLayer.propTypes = {
map: PropTypes.object,
config: PropTypes.object,
selectedDeployment: PropTypes.string,
selectedPlatform: PropTypes.string,
onLoad: PropTypes.func,
}

export const MapLegend = ({
platforms = [],
setSelectedPlatform,
selectedPlatform,
}) => {
return (
<LegendBox>
<h3>Platforms</h3>
{platforms.map(platform => (
<button key={platform.name} onClick={() => highlight(platform.name)}>
<LegendText selected={platform.name === highlightedPlatform}>
<button
key={platform.name}
onClick={() =>
platform.name === selectedPlatform
? setSelectedPlatform("")
: setSelectedPlatform(platform.name)
}
>
<LegendText selected={platform.name === selectedPlatform}>
{platform.type === "Jet" ? (
<LineIcon color="#1B9E77" size="text" />
) : (
Expand All @@ -160,8 +178,9 @@ const MapLegend = ({ map, platforms = [] }) => {
}

MapLegend.propTypes = {
map: PropTypes.object,
platforms: PropTypes.array,
setSelectedPlatform: PropTypes.func,
selectedPlatform: PropTypes.string,
}

const LegendText = styled.span`
Expand All @@ -178,23 +197,25 @@ const LegendText = styled.span`
const LegendBox = styled.div`
display: inline-block;
text-align: left;
min-width: 10rem;
position: absolute;
right: 5px;
margin-top: 5px;
margin-right: 5px;
padding: 8px;
color: #000;
color: ${colors.lightTheme.text};
background-color: rgba(255, 255, 255, 0.6);
> button {
font-family: "Titillium Web", sans-serif;
display: block;
cursor: pointer;
background: transparent;
border: none;
}
> h3 {
margin: 0 0 8px;
color: ${colors.lightTheme.text};
font-size: 1.1rem;
font-weight: 600;
}
`

DeploymentMap.propTypes = {
geojson: PropTypes.object,
deployments: PropTypes.array,
bounds: PropTypes.array,
}
20 changes: 12 additions & 8 deletions src/components/timeline/timeline-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { occlusion } from "./occlusion"
import { Deployment } from "./deployment"
import { Disclosure } from "@reach/disclosure"
import { DeploymentPanel } from "./deployment-panel"
import DeploymentMap from "./map"
import { DeploymentMap } from "./map"

const chartSettings = {
marginTop: 1,
Expand All @@ -37,7 +37,7 @@ export const Swatch = styled.div`
background-color: ${({ color }) => color};
`

export const TimelineChart = ({ deployments, bounds }) => {
export const TimelineChart = ({ deployments, bounds, campaignName }) => {
const [containerRef, dms] = useChartDimensions(chartSettings)

const minDateString = d3
Expand Down Expand Up @@ -93,7 +93,7 @@ export const TimelineChart = ({ deployments, bounds }) => {
const fetchData = async () => {
try {
const response = await fetch(
`https://proxy.willemarcel.workers.dev/?https://6567b4707abd461c463f3c7c--visionary-sopapillas-62534f.netlify.app/OLYMPEX.geojson`,
`https://proxy.willemarcel.workers.dev/?https://65721c8ff4a4f70099bd9ee0--visionary-sopapillas-62534f.netlify.app/${campaignName}.geojson `,
{
method: "GET",
headers: {
Expand Down Expand Up @@ -140,11 +140,14 @@ export const TimelineChart = ({ deployments, bounds }) => {

return (
<Disclosure open={!!selectedDeployment}>
<DeploymentMap
geojson={geojson}
deployments={deployments}
bounds={bounds}
/>
{geojson?.features?.length && (
<DeploymentMap
geojson={geojson}
deployments={deployments}
bounds={bounds}
selectedDeployment={selectedDeployment}
/>
)}
<div
ref={containerRef}
css={`
Expand Down Expand Up @@ -331,4 +334,5 @@ TimelineChart.propTypes = {
})
),
bounds: PropTypes.array,
campaignName: PropTypes.string.isRequired,
}
1 change: 1 addition & 0 deletions src/templates/campaign/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const CampaignTemplate = ({ data: { campaign }, path }) => {
nav: "Deployment & Events",
component: TimelineSection,
props: {
campaignName: campaign.shortname,
deployments: campaign.deployments,
bounds: aggregatedBounds,
},
Expand Down
Loading

0 comments on commit ad52cf2

Please sign in to comment.