Skip to content

Commit

Permalink
Include date histogram and species multi-select in Geo page (#239)
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 authored Mar 29, 2023
1 parent 04a7fa4 commit e83d46b
Show file tree
Hide file tree
Showing 10 changed files with 2,144 additions and 795 deletions.
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
162 changes: 141 additions & 21 deletions src/components/Geo/Geo.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,139 @@
import React from 'react'
import { Helmet } from 'react-helmet'
import { MemoizedMapPlot } from './MapPlot'
import { SelectionInfo } from './SelectionInfo'
import { RunData } from './types'
import { MapPlot } from './MapPlot'
import { ResultsTable } from './ResultsTable'
import { RunData, RunDataKey } from './types'
import { helpIcon } from 'common'
import { fetchPagedGeoMatches } from 'components/Explorer/Base/Result/SerratusApiCalls'
import {
countRunDataByDateAndKey,
filterRunDataByGroup,
getBioIdsFromRunData,
getRunDataFromPaginatedData,
groupRunDataByKey,
transformToMapPlotData,
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)
}

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

const filteredAndSelectedRows = React.useMemo(() => {
const runData = getRunDataFromPaginatedData(paginatedRunData)
if (runData.length === 0) {
return []
}
const filteredBySpecies = filterRunDataByGroup(
runData,
selectedSpecies,
RunDataKey.ScientificName
)
return filterRunDataByGroup(
filteredBySpecies,
getBioIdsFromRunData(selectedPoints),
RunDataKey.BiosampleId
)
}, [isFetching, selectedSpecies, selectedPoints])

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

const timePlotData = React.useMemo(() => {
const groupedCounter = countRunDataByDateAndKey(
filteredAndSelectedRows,
RunDataKey.ScientificName
)
return transformToTimePlotData(groupedCounter, selectedSpecies)
}, [filteredAndSelectedRows])

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 +154,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'>
<MapPlot 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.
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. <b>Double-click</b> to deselect
points.
</div>

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

0 comments on commit e83d46b

Please sign in to comment.