Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
294cb93
Test
Yukthiw Dec 4, 2025
6a46aee
Merge branch 'staging' into staging-debug
Yukthiw Dec 4, 2025
68aab2f
Fix yaml
Yukthiw Dec 4, 2025
7fb4da3
ignore build on staging-debug
Yukthiw Dec 4, 2025
2750c49
Add correct router path
Yukthiw Dec 4, 2025
ec9d6ef
Fixed router path
Yukthiw Dec 16, 2025
3c09e0d
Added basename arg
Yukthiw Dec 16, 2025
f949b05
Prettier
Yukthiw Dec 16, 2025
400cbb0
Revert guthub actions change
Yukthiw Dec 16, 2025
72e37ca
Merge branch 'staging' into staging-debug
Yukthiw Dec 16, 2025
94f7de5
Changed entrypoint path back to being base relative
Yukthiw Dec 16, 2025
cd42aa7
Moving to hashrouter
Yukthiw Dec 25, 2025
1077bde
Trying to fix view alignment issues
Yukthiw Dec 26, 2025
6652b37
Revert "Trying to fix view alignment issues"
Yukthiw Dec 26, 2025
e7d92bf
Revert "Moving to hashrouter"
Yukthiw Dec 26, 2025
3e98a04
Changing entrypoint again
Yukthiw Dec 26, 2025
477c467
Setting homepage variable
Yukthiw Dec 26, 2025
3f62c8c
Added workaround for github pages
Yukthiw Dec 26, 2025
d6702bf
refactored mask modal
Yukthiw Jan 5, 2026
4fcb9ca
Worldefp conversion to routing
Yukthiw Jan 5, 2026
daab827
Fixing local url navigation
Yukthiw Jan 5, 2026
e82cb7e
Added state management and reconstruction from url params
Yukthiw Jan 5, 2026
18ed279
Fixed genedist chart
Yukthiw Jan 8, 2026
5485bda
Added map selector
Yukthiw Jan 9, 2026
f0d7bbd
formatting
Yukthiw Jan 9, 2026
c6f7cf5
Icon change
Yukthiw Jan 9, 2026
028543e
Precipitation Overlay
Yukthiw Apr 12, 2026
ec5e3e5
Climate legends
Yukthiw Apr 16, 2026
1067a95
Fixed rendering of info
Yukthiw Apr 16, 2026
c0222f2
Max zoom capped at 8
Yukthiw Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
branches-ignore:
- main
- staging
- staging-debug
pull_request:
jobs:
build:
Expand Down
4 changes: 2 additions & 2 deletions Eplant/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import InteractionsViewer from './views/InteractionsViewer'
import NavigatorView from './views/NavigatorView'
import PlantEFP from './views/PlantEFP'
import PublicationViewer from './views/PublicationViewer'
// import WorldEFP from './views/WorldEFP'
import WorldEFP from './views/WorldEFP'
import { type ViewMetadata } from './View'

export type EplantConfig = {
Expand All @@ -34,7 +34,7 @@ const userViewMetadata = [
PlantEFP,
CellEFP,
ExperimentEFP,
// WorldEFP,
WorldEFP,
ChromosomeViewerObject,
NavigatorView,
InteractionsViewer,
Expand Down
104 changes: 57 additions & 47 deletions Eplant/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,58 +16,68 @@ import { InteractionsViewObject } from './views/InteractionsViewer/InteractionsV
import { NavigatorViewObject } from './views/NavigatorView/NavigatorView'
import { PlantEFPView } from './views/PlantEFP/PlantEFP'
import { PublicationsViewer } from './views/PublicationViewer/PublicationsView'
import { WorldEFPView } from './views/WorldEFP/WorldEFP'
import { Config, defaultConfig } from './config'
import Eplant from './Eplant'

import './css/index.css'
const router = createBrowserRouter([
const router = createBrowserRouter(
[
{
path: '/',
element: <Eplant />,
children: [
{
element: <Navigate to={'gene-info/'} replace={true}></Navigate>,
},
{
path: 'cell-efp/:geneid?',
element: <CellEFPView></CellEFPView>,
},
{
path: 'publications/:geneid?',
element: <PublicationsViewer></PublicationsViewer>,
},
{
path: 'chromosome/:geneid?',
element: <ChromosomeView></ChromosomeView>,
},
{
path: 'plant-efp/:geneid?',
element: <PlantEFPView></PlantEFPView>,
},
{
path: 'experiment-efp/:geneid?',
element: <ExperimentEFPView></ExperimentEFPView>,
},
{
path: 'gene-info/:geneid?',
element: <GeneInfoView></GeneInfoView>,
},
{
path: 'get-started/:geneid?',
element: <GetStartedView></GetStartedView>,
},
{
path: 'navigator-view/:geneid?',
element: <NavigatorViewObject></NavigatorViewObject>,
},
{
path: 'interactions-view/:geneid?',
element: <InteractionsViewObject></InteractionsViewObject>,
},
{
path: 'world-efp/:geneid?',
element: <WorldEFPView></WorldEFPView>,
},
],
errorElement: <ErrorBoundary></ErrorBoundary>,
},
],
{
path: '/',
element: <Eplant />,
children: [
{
element: <Navigate to={'gene-info/'} replace={true}></Navigate>,
},
{
path: 'cell-efp/:geneid?',
element: <CellEFPView></CellEFPView>,
},
{
path: 'publications/:geneid?',
element: <PublicationsViewer></PublicationsViewer>,
},
{
path: 'chromosome/:geneid?',
element: <ChromosomeView></ChromosomeView>,
},
{
path: 'plant-efp/:geneid?',
element: <PlantEFPView></PlantEFPView>,
},
{
path: 'experiment-efp/:geneid?',
element: <ExperimentEFPView></ExperimentEFPView>,
},
{
path: 'gene-info/:geneid?',
element: <GeneInfoView></GeneInfoView>,
},
{
path: 'get-started/:geneid?',
element: <GetStartedView></GetStartedView>,
},
{
path: 'navigator-view/:geneid?',
element: <NavigatorViewObject></NavigatorViewObject>,
},
{
path: 'interactions-view/:geneid?',
element: <InteractionsViewObject></InteractionsViewObject>,
},
],
errorElement: <ErrorBoundary></ErrorBoundary>,
},
])
basename: import.meta.env.BASE_URL ?? '/',
}
)

export const queryClient = new QueryClient()

Expand Down
104 changes: 104 additions & 0 deletions Eplant/views/WorldEFP/ClimateOverlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { useEffect, useRef } from 'react'

import { useQuery } from '@tanstack/react-query'
import { useMap } from '@vis.gl/react-google-maps'

import { fetchOverlayTiles, OverlayTileData } from './overlayTiles'
import { OverlayType } from './types'

const PLACEHOLDER_TILE = '/temp_world_efp/tile-placeholder.png'

/**
* Normalizes tile coordinates per Google Maps conventions.
* - Does NOT repeat across the y-axis (returns null for out-of-bounds y)
* - Wraps across the x-axis (world repeats horizontally)
*
* Ported from the original WorldView implementation.
*/
function normalizeTileCoord(
coord: google.maps.Point,
zoom: number
): { x: number; y: number } | null {
const tileRange = 1 << zoom
const y = coord.y
let x = coord.x

if (y < 0 || y >= tileRange) return null
if (x < 0 || x >= tileRange) {
x = ((x % tileRange) + tileRange) % tileRange
}
return { x, y }
}

interface ClimateOverlayProps {
overlay: OverlayType
}

const ClimateOverlay = ({ overlay }: ClimateOverlayProps) => {
const map = useMap('WorldEFP')

// Tile data is fetched independently — doesn't block the map from rendering.
const { data: tileData } = useQuery({
queryKey: ['overlay-tiles', overlay],
queryFn: () =>
fetchOverlayTiles(overlay as Exclude<OverlayType, OverlayType.None>),
enabled: overlay !== OverlayType.None,
})

// Keep a ref to the latest tile data so the getTileUrl closure always reads
// the most recent data without needing to recreate the ImageMapType layer.
const tileDataRef = useRef<OverlayTileData | null>(null)
useEffect(() => {
tileDataRef.current = tileData ?? null
}, [tileData])

// Keep a ref to the active layer so we can force a tile refresh when data loads.
const layerRef = useRef<google.maps.ImageMapType | null>(null)

// Register / unregister the ImageMapType layer whenever the overlay or map changes.
useEffect(() => {
if (!map || !window.google?.maps || overlay === OverlayType.None) return

const layer = new window.google.maps.ImageMapType({
getTileUrl: (coord: google.maps.Point, zoom: number): string | null => {
if (zoom > 8) return null

const normalized = normalizeTileCoord(coord, zoom)
if (!normalized) return null

const { x, y } = normalized
const tileMap = tileDataRef.current?.tileMap
const url = tileMap?.[`${zoom}_${x}_${y}`]
return url ?? PLACEHOLDER_TILE
},
tileSize: new window.google.maps.Size(256, 256),
opacity: 0.7,
name: overlay,
})

layerRef.current = layer
map.overlayMapTypes.push(layer)

return () => {
const index = map.overlayMapTypes.getArray().indexOf(layer)
if (index !== -1) map.overlayMapTypes.removeAt(index)
layerRef.current = null
}
}, [map, overlay])

// When tile data finishes loading, force a tile refresh by removing and
// re-inserting the layer at the same position so grey placeholders are replaced.
useEffect(() => {
if (!tileData || !map || !layerRef.current) return
const layer = layerRef.current
const index = map.overlayMapTypes.getArray().indexOf(layer)
if (index !== -1) {
map.overlayMapTypes.removeAt(index)
map.overlayMapTypes.insertAt(index, layer)
}
}, [tileData, map])

return null
}

export default ClimateOverlay
9 changes: 8 additions & 1 deletion Eplant/views/WorldEFP/InfoContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ const InfoContent = ({ id, mean, std, sampleSize }: InfoContentProps) => {
return (
<StyledInfoContent>
<p>
<strong>{id}</strong>
<strong>
{id.split(/<br\s*\/?>/i).map((line, i, arr) => (
<span key={i}>
{line}
{i < arr.length - 1 && <br />}
</span>
))}
</strong>
</p>
<p>Mean: {mean.toFixed(2)}</p>
<p>Standard error: {std.toFixed(2)}</p>
Expand Down
Loading
Loading