Skip to content

Commit

Permalink
Map and data mvp
Browse files Browse the repository at this point in the history
  • Loading branch information
nofurtherinformation committed Feb 7, 2024
1 parent 4507b6b commit 29dad6c
Show file tree
Hide file tree
Showing 14 changed files with 86,276 additions and 26 deletions.
8 changes: 7 additions & 1 deletion components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import React from 'react';
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import {
HamburgerMenuIcon,
DotFilledIcon,
CheckIcon,
ChevronRightIcon,
} from '@radix-ui/react-icons';
import './styles.css';
export interface DropdownProps {
children: React.ReactNode;
Expand All @@ -13,7 +19,7 @@ export const DropdownMenuDemo: React.FC<DropdownProps> = ({
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<button className="IconButton" aria-label="Customise options">
<p>Year</p>
<HamburgerMenuIcon />
</button>
</DropdownMenu.Trigger>

Expand Down
2 changes: 1 addition & 1 deletion components/Dropdown/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ button {
.IconButton {
font-family: inherit;
height: 35px;
width: 4rem;
width: 35px;
display: inline-flex;
font-weight: bold;
align-items: center;
Expand Down
135 changes: 114 additions & 21 deletions components/Map/Map.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client"
import { store, useAppDispatch, useAppSelector } from "utils/state/store"
import { setYear } from "utils/state/map"
import { setCurrentColumn, setTooltipInfo, setYear } from "utils/state/map"
import { Provider, useSelector } from "react-redux"
import { MapboxOverlay, MapboxOverlayProps } from "@deck.gl/mapbox/typed"
import GlMap, { NavigationControl, useControl } from "react-map-gl"
Expand All @@ -9,6 +9,55 @@ import React, { useRef } from "react"
import { MVTLayer } from "@deck.gl/geo-layers/typed"
import DropdownMenuDemo from "components/Dropdown/Dropdown"
import * as DropdownMenu from "@radix-ui/react-dropdown-menu"
import { useDataService } from "utils/hooks/useDataService"
import "./styles.css"
import config from "utils/data/config"
import { SelectMenu } from "components/Select/Select"
import * as Select from "@radix-ui/react-select"
import { CheckIcon } from "@radix-ui/react-icons"
import { DataService } from "utils/data/service"

const BreakText: React.FC<{ breaks: number[]; index: number; colors: number[][] }> = ({ breaks, index, colors }) => {
let text = ""
if (index === 0) {
text = `<${breaks[0]}`
} else if (index === breaks.length) {
text = `>${breaks[breaks.length - 1]}`
} else {
text = `${breaks[index - 1]}-${breaks[index]}`
}
return (
<div className="ColorRow">
{/* @ts-ignore */}
<span style={{ background: `rgb(${colors[index].join(",")})` }}></span>
<p>{text}</p>
</div>
)
}

const Tooltip: React.FC<{ dataService: DataService }> = ({ dataService }) => {
const tooltip = useAppSelector((state) => state.map.tooltip)
if (!tooltip) {
return null
}
const { x, y, id } = tooltip
const datasets = Object.keys(dataService.data)
const data = datasets.map((d) => dataService.data[d]?.[id]).filter(Boolean)

return (
<div style={{ background: "white", position: "fixed", left: x, top: y, padding:'1rem', zIndex:1001 }}>
{data.map((d) =>
Object.entries(d!).map(([k, v]) => {
return (
<p>
{k}: {v}
</p>
)
})
)}
</div>
)
}

export default function MapOuter() {
return (
Expand All @@ -29,19 +78,41 @@ const INITIAL_VIEW_STATE = {

const years = Array.from({ length: 25 }, (_, i) => 1997 + i)
export const Map = () => {
const { isReady, data, colorFunc, colors, ds, breaks, currentColumnSpec, currentDataSpec } = useDataService()
const getElementColor = (element: GeoJSON.Feature<GeoJSON.Polygon, GeoJSON.GeoJsonProperties>) => {
if (!isReady) {
return [0, 0, 0, 120]
}
const id = element?.properties?.GEOID
const d = data?.[+id]
if (id === undefined || d === undefined) {
return [0, 0, 0, 120]
}
// @ts-ignore
return colorFunc(d)
}
const layers = [
new MVTLayer({
data: `/api/tiles/tracts/{z}/{x}/{y}`,
minZoom: 0,
maxZoom: 14,
getLineColor: [192, 192, 192],
getFillColor: [140, 170, 180],
getLineColor: [192, 192, 192, 50],
getFillColor: getElementColor,
updateTriggers: {
getFillColor: [isReady, currentColumnSpec?.column],
},
onHover: (info: any) => {
if (info?.x !== -1 && info?.y !== -1) {
dispatch(setTooltipInfo({ x: info.x, y: info.y, id: info.object?.properties?.GEOID }))
} else {
// dispatch(setTooltipInfo(null))
}
},
lineWidthMinPixels: 1,
filled: true,
stroked: true,
beforeId: "water",
pickable: true,
onClick: (info) => console.log(info),
}),
]
const mapRef = useRef(null)
Expand All @@ -50,29 +121,52 @@ export const Map = () => {
const handleYearChange = (year: number) => {
dispatch(setYear(year))
}
const handleSetColumn = (col: string | number) => {
dispatch(setCurrentColumn(col))
}

return (
<div style={{ width: "100vw", height: "100vh", position: "relative", top: 0, left: 0 }}>
<div style={{ position: "absolute", top: "1rem", left: "1rem", zIndex: 1000 }}>
<div style={{ position: "relative", top: 0, left: 0, zIndex: 1000 }}>
<DropdownMenuDemo>
<>
{years.map((buttonYear) => (
<DropdownMenu.Item
className={`DropdownMenuItem ${year === buttonYear ? "selected" : ""}`}
key={`${buttonYear}-button`}
onClick={() => handleYearChange(buttonYear)}
>
{buttonYear}
</DropdownMenu.Item>
))}
</>
</DropdownMenuDemo>
<div style={{ position: "absolute", bottom: "2rem", right: "1rem", zIndex: 1000 }}>
<div className="ColorLegend">
<h3>{currentColumnSpec?.name}</h3>
{!!(colors.length && breaks.length) &&
colors.map((_, i) => <BreakText colors={colors} breaks={breaks} index={i} />)}
<p style={{ maxWidth: "35ch", fontSize: "0.75rem" }}>
<i>
Data source InfoGroup Refernce USA. Concentration index (HHI) includes grocery, superstore, and dollar
stores.
</i>
</p>
</div>
</div>
<div style={{ position: "absolute", top: "1rem", left: "1rem", zIndex: 999 }}>
<DropdownMenuDemo>
<>
<SelectMenu
title="Choose a column"
value={currentColumnSpec?.name || "Choose a column"}
onValueChange={handleSetColumn}
>
<>
{currentDataSpec?.columns.map((c) => (
<Select.Item className="SelectItem" value={c.column as string}>
<Select.ItemText>{c.name}</Select.ItemText>
<Select.ItemIndicator className="SelectItemIndicator">
<CheckIcon />
</Select.ItemIndicator>
</Select.Item>
))}
</>
</SelectMenu>
</>
</DropdownMenuDemo>
</div>
<Tooltip dataService={ds} />
<GlMap
hash={true}
mapboxAccessToken={process.env.NEXT_PUBLIC_MAPBOX_TOKEN}
mapStyle="mapbox://styles/mapbox/light-v9"
mapStyle="mapbox://styles/dhalpern/clsb432ya02pi01pf1o813uwa"
initialViewState={INITIAL_VIEW_STATE}
// @ts-ignore
ref={mapRef}
Expand All @@ -84,7 +178,6 @@ export const Map = () => {
</div>
)
}

function DeckGLOverlay(
props: MapboxOverlayProps & {
interleaved?: boolean
Expand Down
35 changes: 35 additions & 0 deletions components/Map/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@import '@radix-ui/colors/black-alpha.css';
@import '@radix-ui/colors/mauve.css';
@import '@radix-ui/colors/violet.css';

/* reset */
button {
all: unset;
}

.ColorLegend {
background-color: white;
border-radius: 6px;
padding: 12px;
box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2);
animation-duration: 400ms;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
will-change: transform, opacity;
}

.ColorLegend h3 {
font-weight: bold;
}

.ColorRow {
display: flex;
flex-direction: row;
}

.ColorRow span {
width: 20px;
height: 20px;
margin-right: 8px;
margin-bottom:4px;
border:1px solid rgba(0,0,0,0.5);
}
33 changes: 33 additions & 0 deletions components/Select/Select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from "react"
import * as Select from "@radix-ui/react-select"
import classnames from "classnames"
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "@radix-ui/react-icons"
import "./styles.css"

type SelectProps = {
title: string
value: string
children: React.ReactNode,
onValueChange: (e: any) => void
}
export const SelectMenu: React.FC<SelectProps> = ({ title, value, children, onValueChange }) => (
<Select.Root onValueChange={onValueChange}>
<Select.Trigger className="SelectTrigger" aria-label="Food">
<Select.Value placeholder={value || title} />
<Select.Icon className="SelectIcon">
<ChevronDownIcon />
</Select.Icon>
</Select.Trigger>
<Select.Portal>
<Select.Content className="SelectContent">
<Select.ScrollUpButton className="SelectScrollButton">
<ChevronUpIcon />
</Select.ScrollUpButton>
<Select.Viewport className="SelectViewport">{children}</Select.Viewport>
<Select.ScrollDownButton className="SelectScrollButton">
<ChevronDownIcon />
</Select.ScrollDownButton>
</Select.Content>
</Select.Portal>
</Select.Root>
)
101 changes: 101 additions & 0 deletions components/Select/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
@import '@radix-ui/colors/black-alpha.css';
@import '@radix-ui/colors/mauve.css';
@import '@radix-ui/colors/violet.css';

/* reset */
button {
all: unset;
}

.SelectTrigger {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
padding: 0 15px;
font-size: 13px;
line-height: 1;
height: 35px;
gap: 5px;
background-color: white;
color: var(--violet-11);
/* box-shadow: 0 2px 10px var(--black-a7); */
}
.SelectTrigger:hover {
background-color: var(--mauve-3);
}
.SelectTrigger:focus {
box-shadow: 0 0 0 2px black;
}
.SelectTrigger[data-placeholder] {
color: var(--violet-9);
}

.SelectIcon {
color: Var(--violet-11);
}

.SelectContent {
overflow: hidden;
background-color: white;
border-radius: 6px;
box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2);
}

.SelectViewport {
padding: 5px;
}

.SelectItem {
font-size: 13px;
line-height: 1;
color: var(--violet-11);
border-radius: 3px;
display: flex;
align-items: center;
height: 25px;
padding: 0 35px 0 25px;
position: relative;
user-select: none;
}
.SelectItem[data-disabled] {
color: var(--mauve-8);
pointer-events: none;
}
.SelectItem[data-highlighted] {
outline: none;
background-color: var(--violet-9);
color: var(--violet-1);
}

.SelectLabel {
padding: 0 25px;
font-size: 12px;
line-height: 25px;
color: var(--mauve-11);
}

.SelectSeparator {
height: 1px;
background-color: var(--violet-6);
margin: 5px;
}

.SelectItemIndicator {
position: absolute;
left: 0;
width: 25px;
display: inline-flex;
align-items: center;
justify-content: center;
}

.SelectScrollButton {
display: flex;
align-items: center;
justify-content: center;
height: 25px;
background-color: white;
color: var(--violet-11);
cursor: default;
}
Loading

0 comments on commit 29dad6c

Please sign in to comment.