Skip to content

Commit

Permalink
Include date histogram and species multi-select in Geo page
Browse files Browse the repository at this point in the history
- Merge #221
- Merge #231

Co-authored-by: Max McCready <75914260+Bluesquare99@users.noreply.github.com>
Co-authored-by: khanzardar <k.zardar@gmail.com>
  • Loading branch information
3 people committed Mar 27, 2023
1 parent b0ab829 commit 8b9c45d
Show file tree
Hide file tree
Showing 10 changed files with 2,049 additions and 711 deletions.
2 changes: 1 addition & 1 deletion .env.development
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
NODE_ENV=development
REACT_APP_API_URL=http://localhost:5000
REACT_APP_API_URL=http://localhost:5000
2,115 changes: 1,546 additions & 569 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"dependencies": {
"@jbrowse/react-linear-genome-view": "^1.3.2",
"axios": "^0.21.2",
"chroma-js": "^2.4.2",
"d3": "^5.16.0",
"d3-scale-chromatic": "^2.0.0",
"fast-xml-parser": "^3.19.0",
Expand All @@ -20,7 +21,8 @@
"react-plotly.js": "^2.5.1",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"react-select": "^3.1.0"
"react-select": "^3.1.0",
"react-windowed-select": "^5.1.0"
},
"scripts": {
"build:css": "postcss src/styles/tailwind.css -o src/styles/main.css",
Expand All @@ -45,13 +47,16 @@
]
},
"devDependencies": {
"@types/chroma-js": "^2.1.4",
"@types/d3": "^6.7.3",
"@types/jest": "^26.0.23",
"@types/react": "^17.0.11",
"@types/react-helmet": "^6.1.0",
"@types/react": "^18.0.14",
"@types/react-dom": "^18.0.5",
"@types/react-helmet": "^6.1.6",
"@types/react-plotly.js": "^2.2.4",
"@types/react-router-dom": "^5.1.7",
"@types/react-select": "^4.0.16",
"@types/react-window": "^1.8.5",
"@typescript-eslint/eslint-plugin": "^4.28.0",
"@typescript-eslint/parser": "^4.28.0",
"autoprefixer": "^9.8.7",
Expand Down
159 changes: 143 additions & 16 deletions src/components/Geo/Geo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,145 @@ import React from 'react'
import { Helmet } from 'react-helmet'
import { MemoizedMapPlot } from './MapPlot'
import { SelectionInfo } from './SelectionInfo'
import { RunData } from './types'
import { RunData, RunDataKey } from './types'
import { helpIcon } from 'common'
import { fetchPagedGeoMatches } from 'components/Explorer/Base/Result/SerratusApiCalls'
import {
getRunDataFromPaginatedData,
transformToMapPlotData,
groupRunDataByKey,
filterRunDataByGroup,
countRunDataByKey,
transformToTimePlotData,
} from './GeoHelpers'
import { SpeciesSelect } from './SpeciesSelect'
import { TimePlot } from './TimePlot'

export const Geo = () => {
const [selectedPoints, setSelectedPoints] = React.useState<RunData[]>()
const [isCollapsed, setIsCollapsed] = React.useState<boolean>(false)
const [isFetching, setIsFetching] = React.useState<boolean>(true)
const [paginatedRunData, setPaginatedRunData] = React.useState<{
[page: string]: RunData[]
}>({})
const [selectedPoints, setSelectedPoints] = React.useState<RunData[]>([])
const [selectedSpecies, setSelectedSpecies] = React.useState<string[]>([])

const headTags = (
<Helmet>
<title>Serratus | Planetary RNA Virome</title>
</Helmet>
)
React.useEffect(() => {
async function onMount() {
fetchRunData()
}
onMount()
}, [])

return (
<div className='mx-4 my-2'>
{headTags}
<div className='text-center text-xl'>The Planetary RNA Virome</div>
function storePaginatedRunData(rows: RunData[], page: number) {
if (!rows.length) {
return
}
setPaginatedRunData((prevState) => ({
...prevState,
[page]: rows,
}))
}

async function fetchRunData() {
setIsFetching(true)

// Fetch first page and get total rows
let page = 1
const perPage = 20000
const searchType = 'rdrp'
const { result, total } = await fetchPagedGeoMatches(searchType, page, perPage)
storePaginatedRunData(result as RunData[], page)

// Batch requests for remaining pages
const totalPages = Math.ceil(total / perPage)
const iterPages = []
for (page = page + 1; page <= totalPages; page++) {
if (!(page in paginatedRunData)) {
iterPages.push(page)
}
}
await Promise.allSettled(
iterPages.map(async (page) => {
const { result } = await fetchPagedGeoMatches(searchType, page as number, perPage)
storePaginatedRunData(result as RunData[], page as number)
})
)
setIsFetching(false)
}

function getSelectedPointIds() {
return selectedPoints.map((point) => point?.[RunDataKey.BiosampleId])
}

const speciesOptions = React.useMemo(() => {
const runData = getRunDataFromPaginatedData(paginatedRunData)
if (runData.length === 0) {
return []
}
let filteredRunData = filterRunDataByGroup(
runData,
getSelectedPointIds(),
RunDataKey.BiosampleId
)
const speciesSet = new Set(filteredRunData.map((d) => d?.[RunDataKey.ScientificName]))
return Array.from(speciesSet).sort()
}, [isFetching, selectedPoints, selectedSpecies])

const mapData = React.useMemo(() => {
const runData = getRunDataFromPaginatedData(paginatedRunData)
if (runData.length === 0) {
return transformToMapPlotData()
}
const filteredRunData = filterRunDataByGroup(
runData,
selectedSpecies,
RunDataKey.ScientificName
)
const groupedRunData =
selectedSpecies.length > 0
? groupRunDataByKey(filteredRunData, RunDataKey.ScientificName)
: {
all: filteredRunData,
}
return transformToMapPlotData(groupedRunData, selectedSpecies, getSelectedPointIds())
}, [isFetching, selectedSpecies])

const timePlotData = React.useMemo(() => {
const runData = getRunDataFromPaginatedData(paginatedRunData)
let filteredRunData = filterRunDataByGroup(
runData,
getSelectedPointIds(),
RunDataKey.BiosampleId
)
filteredRunData = filterRunDataByGroup(
filteredRunData,
selectedSpecies,
RunDataKey.ScientificName
)
const groupedCounter = countRunDataByKey(filteredRunData, RunDataKey.ScientificName)
return transformToTimePlotData(groupedCounter, selectedSpecies)
}, [isFetching, selectedPoints, selectedSpecies])

const rowsToDisplay = React.useMemo(() => {
const hasSelectedPoints = selectedPoints.length > 0
const hasSelectedSpecies = selectedSpecies.length > 0

const initialRows = hasSelectedPoints
? selectedPoints
: getRunDataFromPaginatedData(paginatedRunData)

return hasSelectedSpecies
? initialRows.filter((row) => selectedSpecies.includes(row.scientific_name))
: initialRows
}, [selectedPoints, selectedSpecies])

return (
<div className='mx-14 my-2'>
<Helmet>
<title>Serratus | Planetary RNA Virome</title>
</Helmet>
<div className='text-center text-xl my-4'>The Planetary RNA Virome</div>
<button
className='text-left collapse-button'
onClick={() => setIsCollapsed(!isCollapsed)}>
Expand All @@ -40,17 +161,23 @@ export const Geo = () => {

<p>A 100-meter randomization is applied to all points to prevent overplotting.</p>
</div>

<div className='my-2'>
<MemoizedMapPlot setSelectedPoints={setSelectedPoints} />
<div className='my-4'>
<SpeciesSelect
speciesOptions={speciesOptions}
selectedSpecies={selectedSpecies}
setSelectedSpecies={setSelectedSpecies}
/>
</div>
<div className='my-4'>
<MemoizedMapPlot plotData={mapData} setSelectedPoints={setSelectedPoints} />
</div>

<div className='text-left text-gray-600'>
Use <b>`Shift`</b>-click to select multiple points or the <b>`Box Select`</b> or{' '}
<b>`Lasso Select`</b> icons in the top-right.
</div>

<SelectionInfo selectedPoints={selectedPoints} />
<TimePlot plotData={timePlotData} />
<SelectionInfo rowsToDisplay={rowsToDisplay} />
</div>
)
}
Loading

0 comments on commit 8b9c45d

Please sign in to comment.