-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WorldEFP View --------- Co-authored-by: MikeY <[email protected]>
- Loading branch information
Showing
15 changed files
with
599 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { styled, Theme } from '@mui/material' | ||
|
||
interface InfoContentProps { | ||
id: string | ||
mean: number | ||
std: number | ||
sampleSize: number | ||
} | ||
|
||
const InfoContent = ({ id, mean, std, sampleSize }: InfoContentProps) => { | ||
return ( | ||
<StyledInfoContent> | ||
<p> | ||
<strong>{id}</strong> | ||
</p> | ||
<p>Mean: {mean.toFixed(2)}</p> | ||
<p>Standard error: {std.toFixed(2)}</p> | ||
<p>Sample size: {sampleSize}</p> | ||
</StyledInfoContent> | ||
) | ||
} | ||
|
||
const StyledInfoContent = styled('div')(() => ({ | ||
wordWrap: 'break-word', | ||
maxWidth: '300px', | ||
'& p': { | ||
margin: '5px 0', | ||
color: 'black', | ||
}, | ||
'& strong': { | ||
display: 'block', | ||
marginBottom: '10px', | ||
}, | ||
})) | ||
|
||
export default InfoContent |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { useCallback } from 'react' | ||
|
||
import { ViewDispatch } from '@eplant/View' | ||
import { useTheme } from '@mui/material' | ||
import { | ||
APIProvider, | ||
Map, | ||
MapCameraChangedEvent, | ||
MapEvent, | ||
useMap, | ||
} from '@vis.gl/react-google-maps' | ||
|
||
import { getColor } from '../eFP/svg' | ||
import GeneDistributionChart from '../eFP/Viewer/GeneDistributionChart' | ||
import Legend from '../eFP/Viewer/legend' | ||
|
||
import MapMarker from './MapMarker' | ||
import { WorldEFPAction, WorldEFPData, WorldEFPState } from './types' | ||
|
||
interface MapContainerProps { | ||
activeData: WorldEFPData | ||
state: WorldEFPState | ||
dispatch: ViewDispatch<WorldEFPAction> | ||
} | ||
const MapContainer = ({ activeData, state, dispatch }: MapContainerProps) => { | ||
const theme = useTheme() | ||
const map = useMap('WorldEFP') | ||
|
||
const hangleDragEnd = (event: MapEvent) => { | ||
const mapPos = map?.getCenter() | ||
if (!mapPos) return | ||
|
||
const coords = { lat: mapPos.lat(), lng: mapPos.lng() } | ||
dispatch({ | ||
type: 'set-map-position', | ||
position: coords, | ||
}) | ||
} | ||
|
||
const handleZoom = (event: MapCameraChangedEvent) => { | ||
dispatch({ | ||
type: 'set-map-zoom', | ||
zoom: event.detail.zoom, | ||
}) | ||
} | ||
|
||
return ( | ||
<Map | ||
defaultCenter={state.position} | ||
defaultZoom={2} | ||
mapId={import.meta.env.VITE_MAP_ID} | ||
streetViewControl={false} | ||
mapTypeId={'roadmap'} | ||
mapTypeControl={false} | ||
onDragend={hangleDragEnd} | ||
onZoomChanged={handleZoom} | ||
id='WorldEFP' | ||
> | ||
{activeData.positions.map((pos, index) => { | ||
const color = getColor( | ||
activeData.efpData.groups[index].mean, | ||
activeData.efpData.groups[index], | ||
1, | ||
theme, | ||
state.colorMode, | ||
activeData.efpData.groups[index].std, | ||
state.maskThreshold, | ||
state.maskingEnabled | ||
) | ||
return ( | ||
<MapMarker | ||
theme={theme} | ||
key={index} | ||
color={color} | ||
data={activeData.efpData.groups[index]} | ||
position={pos} | ||
></MapMarker> | ||
) | ||
})} | ||
<Legend | ||
sx={(theme) => ({ | ||
position: 'absolute', | ||
left: theme.spacing(2), | ||
bottom: theme.spacing(4), | ||
zIndex: 10, | ||
})} | ||
colorMode={'absolute'} | ||
data={activeData.efpData} | ||
></Legend> | ||
<GeneDistributionChart data={activeData.efpData} /> | ||
</Map> | ||
) | ||
} | ||
|
||
export default MapContainer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { useEffect, useState } from 'react' | ||
|
||
import Modal from '@eplant/UI/Modal' | ||
import { Theme, useTheme } from '@mui/material' | ||
import { | ||
AdvancedMarker, | ||
InfoWindow, | ||
useAdvancedMarkerRef, | ||
} from '@vis.gl/react-google-maps' | ||
|
||
import { EFPGroup } from '../eFP/types' | ||
|
||
import WorldEFPIcon from './icon' | ||
import InfoContent from './InfoContent' | ||
import { ModalContent } from './ModalContent' | ||
import { Coordinates } from './types' | ||
|
||
interface MapMarkerProps { | ||
theme: Theme | ||
color: string | ||
position: Coordinates | ||
data: EFPGroup | ||
} | ||
|
||
const MapMarker = ({ theme, data, color, position }: MapMarkerProps) => { | ||
const [showPopup, setShowPopup] = useState(false) | ||
const [showModal, setShowModal] = useState(false) | ||
const [markerRef, marker] = useAdvancedMarkerRef() | ||
const handleMouseOver = () => { | ||
setShowPopup(true) | ||
} | ||
|
||
const handleMouseOut = () => { | ||
setShowPopup(false) | ||
} | ||
|
||
const handleClick = () => { | ||
setShowPopup(false) | ||
setShowModal(true) | ||
} | ||
|
||
useEffect(() => { | ||
if (marker) { | ||
marker.addListener('gmp-click', () => {}) // Need this to have mouseover logic work for some reason | ||
marker.content?.addEventListener('mouseover', handleMouseOver) | ||
marker.content?.addEventListener('mouseout', handleMouseOut) | ||
|
||
return () => { | ||
marker.content?.removeEventListener('mouseover', handleMouseOver) | ||
marker.content?.removeEventListener('mouseout', handleMouseOut) | ||
} | ||
} | ||
}, [data, marker]) | ||
|
||
return ( | ||
<> | ||
<AdvancedMarker | ||
ref={markerRef} | ||
position={position} | ||
clickable={true} | ||
onClick={handleClick} | ||
> | ||
<WorldEFPIcon sx={{ fill: color }} /> | ||
</AdvancedMarker> | ||
{showPopup && ( | ||
<InfoWindow | ||
anchor={marker} | ||
maxWidth={300} | ||
disableAutoPan={true} | ||
headerDisabled={true} | ||
> | ||
<InfoContent | ||
id={data.name} | ||
mean={data.mean} | ||
std={data.std} | ||
sampleSize={data.samples} | ||
></InfoContent> | ||
</InfoWindow> | ||
)} | ||
<Modal | ||
open={showModal} | ||
onClose={() => { | ||
setShowModal(false) | ||
}} | ||
> | ||
<ModalContent | ||
theme={theme} | ||
id={data.name} | ||
mean={data.mean} | ||
std={data.std} | ||
sampleSize={data.samples} | ||
></ModalContent> | ||
</Modal> | ||
</> | ||
) | ||
} | ||
|
||
export default MapMarker |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { Box, Theme } from '@mui/material' | ||
import { styled } from '@mui/material' | ||
|
||
interface ModalContentProps { | ||
theme: Theme | ||
id: string | ||
mean: number | ||
std: number | ||
sampleSize: number | ||
} | ||
export const ModalContent = ({ | ||
theme, | ||
id, | ||
mean, | ||
std, | ||
sampleSize, | ||
}: ModalContentProps) => { | ||
return ( | ||
<StyledBox theme={theme}> | ||
<p> | ||
<strong>{id}</strong> | ||
</p> | ||
<p>Mean: {mean.toFixed(2)}</p> | ||
<p>Standard error: {std.toFixed(2)}</p> | ||
<p>Sample size: {sampleSize}</p> | ||
</StyledBox> | ||
) | ||
} | ||
|
||
const StyledBox = styled(Box)(({ theme }: { theme: any }) => ({ | ||
top: '50%', | ||
left: '50%', | ||
width: 400, | ||
borderRadius: '24px', | ||
padding: theme.spacing(4), | ||
backgroundColor: theme.palette.background, | ||
'& p': { | ||
margin: '5px 0', | ||
color: theme.palette.secondary.contrastText, | ||
}, | ||
'& strong': { | ||
display: 'block', | ||
marginBottom: '10px', | ||
}, | ||
})) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { styled } from '@mui/material' | ||
export default styled((props) => { | ||
return ( | ||
<svg | ||
width='24' | ||
height='24' | ||
viewBox='0 0 24 24' | ||
xmlns='http://www.w3.org/2000/svg' | ||
{...props} | ||
> | ||
<path d='M20.2573 0.500008C18.0687 0.496985 15.941 1.37872 14.4035 2.99125C14.4034 2.99125 14.4034 2.99144 14.4034 2.99144C12.5067 4.98218 11.7899 7.80009 12.4965 10.4301C11.7218 11.733 11.1167 13.1451 10.767 14.7081C10.6077 14.3831 10.4356 14.0661 10.2527 13.756C10.8507 11.4709 10.2219 9.02883 8.57598 7.30109V7.30128C8.57598 7.30109 8.57579 7.30109 8.57579 7.30109C7.23324 5.89301 5.37531 5.12312 3.46446 5.12593C2.92944 5.12668 2.3904 5.18789 1.85726 5.31278L1.87294 5.31108V5.31089C1.74125 5.33866 1.61939 5.40101 1.51983 5.49151C1.42026 5.58201 1.34658 5.69744 1.30634 5.82592C0.571027 8.19455 1.18164 10.7752 2.89956 12.5778C2.89956 12.5778 2.89975 12.5778 2.89975 12.578C4.48277 14.2383 6.78243 15.0109 9.03982 14.6765C9.81668 16.0351 10.3368 17.5229 10.4706 19.2325V22.7645L10.4708 22.7643C10.4708 22.9654 10.5505 23.1581 10.6928 23.3003C10.8348 23.4424 11.0276 23.5223 11.2286 23.5223C11.4298 23.5223 11.6225 23.4424 11.7646 23.3003C11.9068 23.1581 11.9867 22.9654 11.9867 22.7643V16.7747C12.1458 14.7333 12.7744 12.9617 13.71 11.347C16.3223 11.7616 18.9925 10.8781 20.8251 8.9567L20.8249 8.95689V8.9567C22.7923 6.89263 23.4911 3.93872 22.6492 1.22732V1.22751C22.609 1.09904 22.5353 0.983598 22.4357 0.893101C22.3362 0.802603 22.2143 0.740254 22.0826 0.712294L22.0983 0.714183C21.4877 0.571164 20.8701 0.501073 20.2574 0.500125L20.2573 0.500008ZM20.2548 2.01599C20.6083 2.01637 20.9637 2.04547 21.3179 2.10366C21.8042 4.16942 21.2185 6.34681 19.7279 7.91097C18.4043 9.29903 16.5353 10.0211 14.6388 9.92213C15.7714 8.35589 17.1743 6.91869 18.6728 5.48981L18.6726 5.49C18.8181 5.35133 18.9026 5.16032 18.9073 4.95947C18.9122 4.75846 18.8368 4.56385 18.6982 4.41836C18.5436 4.25606 18.3252 4.17067 18.1015 4.18484C17.9237 4.19618 17.7556 4.26986 17.6265 4.39266C16.2473 5.70764 14.8984 7.07848 13.7478 8.58731C13.6991 6.92584 14.3112 5.28632 15.5009 4.03751C16.7465 2.73105 18.475 2.01388 20.2549 2.01615L20.2548 2.01599ZM3.46746 6.64177C4.96966 6.63969 6.428 7.24504 7.47885 8.34706C8.40931 9.32381 8.92074 10.5829 8.9574 11.8774C8.00575 10.669 6.91316 9.56318 5.80074 8.50256V8.50275C5.6717 8.37975 5.50355 8.30607 5.32577 8.29473C5.10208 8.28056 4.88367 8.36615 4.72912 8.52825C4.59045 8.67373 4.51507 8.86832 4.51998 9.06937C4.5247 9.27039 4.60916 9.46121 4.75463 9.5999C5.99005 10.7779 7.14609 11.9587 8.09286 13.2349C6.5612 13.2602 5.06771 12.6545 3.99741 11.5319C2.75673 10.2302 2.2604 8.42625 2.64128 6.70548C2.91674 6.66373 3.19294 6.64238 3.46766 6.642L3.46746 6.64177Z' /> | ||
</svg> | ||
) | ||
})(({ theme }) => ({ | ||
fill: theme.palette.text.primary, | ||
})) | ||
|
||
// This icon is used in the dropdown view selection menu |
Oops, something went wrong.