Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Equity maps; various other UI enhancements #141

Merged
merged 27 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2c0c09a
add heat equity layer to default map. #113
davemfish Aug 28, 2024
ba83e4c
style the equity layer. #113
davemfish Aug 28, 2024
69a69af
Merge remote-tracking branch 'upstream/develop' into feature/113-equi…
davemfish Aug 29, 2024
7bd7993
added component for legend to equity layers. #113
davemfish Aug 29, 2024
e162646
styling equity layer legend. #113.
davemfish Aug 29, 2024
3d21038
use a layer group for the equity layer and lulc. #113
davemfish Aug 30, 2024
567fbec
fixing visibility of legends. 113.
davemfish Aug 31, 2024
1f45247
added zoom to default extent control. #113
davemfish Sep 3, 2024
7a54ff0
debugging legend visibility. #113
davemfish Sep 3, 2024
aa74baf
new landing tab for first-time users. #113
davemfish Sep 4, 2024
1171f4a
different behavior for start button when a study area already exists.…
davemfish Sep 5, 2024
dcf7e35
add multiple equity layers to the map. #113.
davemfish Sep 5, 2024
4d7f15a
add borders to census blocks. #113
davemfish Sep 5, 2024
767370e
cleaning up legends. #113
davemfish Sep 5, 2024
dd2c5ca
fix the legend for heat-income equity. #113.
davemfish Sep 5, 2024
dd1f15b
debugging. #113
davemfish Sep 5, 2024
1829198
make sure first-time visitors cannot switch to scenario tab with usin…
davemfish Sep 6, 2024
3cf8d05
remove study area layer from map when it is has no features. #113
davemfish Sep 6, 2024
2183ace
parcel deleted from study area should lose hover-style. #113
davemfish Sep 6, 2024
e191bf4
add gif to demonstrate selecting a parcel
davemfish Sep 6, 2024
0a203e2
some cleanup in stylesheet. #113
davemfish Sep 6, 2024
8493152
cleanup layout in scenario builder panel. #113
davemfish Sep 6, 2024
c995946
changed a heading
davemfish Sep 6, 2024
34ce792
some code cleanup. #113
davemfish Sep 6, 2024
94b239d
move the colormap into the js file. #113
davemfish Sep 6, 2024
42adfba
update tests. #113
davemfish Sep 6, 2024
140b9b5
some comments and cleanup. #113
davemfish Sep 9, 2024
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
Binary file added appdata/parcel_select_crop.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 5 additions & 8 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,24 @@
"name": "urban-es-modeler",
"private": true,
"version": "0.0.0",
"scripts":
{
"scripts": {
"start": "vite",
"start-docker": "cross-env MODE=docker vite --host 0.0.0.0 --port 3000",
"build": "vite build",
"serve": "vite preview",
"test": "vitest run --dir tests --coverage",
"test-e2e": "vitest run --dir tests-e2e"
},
"dependencies":
{
"@blueprintjs/core": "^4.4.0",
"dependencies": {
"@blueprintjs/core": "^5.12.0",
"dotenv": "^16.0.0",
"jsts": "^2.9.3",
"localforage": "^1.10.0",
"ol": "^6.15.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies":
{
"devDependencies": {
"@babel/eslint-parser": "^7.18.2",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
Expand All @@ -43,4 +40,4 @@
"vite": "^4.4.9",
"vitest": "^0.34.0"
}
}
}
25 changes: 25 additions & 0 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ export default function App() {
const [patternSamplingMode, setPatternSamplingMode] = useState(false);
const [patternSampleWKT, setPatternSampleWKT] = useState(null);
const [selectedScenario, setSelectedScenario] = useState(null);
const [selectedEquityLayer, setSelectedEquityLayer] = useState(null);
const [servicesheds, setServicesheds] = useState({});
const [activeTab, setActiveTab] = useState('explore');
const [start, setStart] = useState(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's start tracking?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking if the "Get Started" button was clicked.

const [firstVisit, setFirstVisit] = useState(true);

const switchStudyArea = async (id) => {
let area;
Expand Down Expand Up @@ -67,10 +71,21 @@ export default function App() {
setPatternSamplingMode((mode) => !mode);
};

const startBuilding = () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be useful to have a docstring / comment for the purpose of this function.

// If the session already contains a study area
// it means the user already knows how to use the app
// and we should skip the starting map scene.
if (!studyArea.parcels.length) {
setStart((start) => start + 1);
}
setActiveTab('scenarios');
};

useEffect(() => {
(async () => {
let SID = localStorage.getItem('sessionID');
if (SID) {
setFirstVisit(false);
const session = await getSession(SID);
if (session && session.id) {
setSessionID(SID);
Expand All @@ -91,6 +106,7 @@ export default function App() {
area.parcels.length > 0
));
if (areas.length) {
setActiveTab('scenarios');
setSavedStudyAreas(areas);
await switchStudyArea(areas[0].id); // TODO: switch to most recently created
} else {
Expand Down Expand Up @@ -120,9 +136,14 @@ export default function App() {
setPatternSampleWKT={setPatternSampleWKT}
scenarios={scenarios}
selectedScenario={selectedScenario}
setSelectedEquityLayer={setSelectedEquityLayer}
selectedEquityLayer={selectedEquityLayer}
servicesheds={servicesheds}
activeTab={activeTab}
start={start}
/>
<EditMenu
firstVisit={firstVisit}
key={studyArea.id}
sessionID={sessionID}
studyArea={studyArea}
Expand All @@ -138,6 +159,10 @@ export default function App() {
patternSampleWKT={patternSampleWKT}
setSelectedScenario={setSelectedScenario}
setServicesheds={setServicesheds}
selectedEquityLayer={selectedEquityLayer}
setActiveTab={setActiveTab}
activeTab={activeTab}
startBuilding={startBuilding}
/>
</div>
</div>
Expand Down
37 changes: 33 additions & 4 deletions frontend/src/edit/edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';

import {
FocusStyleManager,
Section,
Tab,
Tabs,
} from '@blueprintjs/core';
Expand All @@ -13,7 +14,9 @@ import StudyAreaTable from './studyAreaTable';
import InputStudyAreaName from './inputStudyAreaName';
import InvestRunner from './investRunner';
import Results from './results';
import Explore from './explore';
import { getInvestResults } from '../requests';
import { publicUrl } from '../utils';

import nlcdLookup from '../../../appdata/nlcd_colormap.json';
import nludLookup from '../../../appdata/nlud_colormap.json';
Expand All @@ -29,6 +32,7 @@ FocusStyleManager.onlyShowFocusOnTabs();

export default function EditMenu(props) {
const {
firstVisit,
nameStudyArea,
refreshStudyArea,
refreshScenarios,
Expand All @@ -43,9 +47,12 @@ export default function EditMenu(props) {
savedStudyAreas,
setSelectedScenario,
setServicesheds,
selectedEquityLayer,
setActiveTab,
activeTab,
startBuilding,
} = props;

const [activeTab, setActiveTab] = useState('scenarios');
const [results, setResults] = useState({});
const [scenarioDescriptions, setScenarioDescriptions] = useState(null);

Expand Down Expand Up @@ -114,9 +121,20 @@ export default function EditMenu(props) {
onChange={(tabID) => setActiveTab(tabID)}
selectedTabId={activeTab}
>
<Tab
id="explore"
title="explore"
panel={(
<Explore
startBuilding={startBuilding}
equityLayerTitle={selectedEquityLayer}
/>
)}
/>
<Tab
id="scenarios"
title="scenarios"
disabled={firstVisit}
panel={(
<div>
{
Expand Down Expand Up @@ -148,9 +166,20 @@ export default function EditMenu(props) {
/>
)
: (
<p className="sidebar-subheading">
<span>Click on the map to add parcels</span>
</p>
<Section
title="Get Started"
>
<ol>
<li>
<p>Click on the map to select a parcel</p>
<img
src={publicUrl('/opt/appdata/parcel_select_crop.gif')}
width="100"
/>
</li>
<li>Add any number of parcels to a study area</li>
</ol>
</Section>
)
}
<ScenarioBuilder
Expand Down
56 changes: 56 additions & 0 deletions frontend/src/edit/explore.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { useState, useEffect } from 'react';

import {
Button,
Section,
SectionCard,
} from '@blueprintjs/core';

import EquityLegend from '../map/equityLegend';

const INTRO_TEXT = `
San Antonio, TX faces the challenge of staying cool in its hot climate,
but not all neighborhoods receive equal help from nature. Some areas benefit
more from natural cooling than others, highlighting environmental inequities,
especially in neighborhoods with higher concentrations of Black, Indigenous,
and People of Color (BIPOC) and lower income levels. Explore how these
disparities affect the city’s ability to cope with rising temperatures.`;

const ECO_TEXT = `
Characteristics of the natural and built environment influence
the urban heat island. Buildings and dark-colored pavement absorb heat.
Trees provide shade, and vegetation provides some natural air-conditioning
through the cooling effect of evapotranspiration.`;

const SCENARIO_TEXT = `
This tool is designed to let you build scenarios of different
landcover, tree-cover, and landuse management. And then evaluate
how these scenarios impact urban heating, access to nature, and
carbon sequestration.`;

export default function Explore(props) {
const {
startBuilding,
equityLayerTitle,
} = props;
return (
<div id="explore" data-testid="explore">
<Section>
<SectionCard>{INTRO_TEXT}</SectionCard>
<SectionCard>
<EquityLegend
show={true}
equityLayerTitle={equityLayerTitle}
/>
</SectionCard>
<SectionCard>{ECO_TEXT}</SectionCard>
<SectionCard>
{SCENARIO_TEXT}
</SectionCard>
<SectionCard>
<Button onClick={startBuilding}>Start building scenarios</Button>
</SectionCard>
</Section>
</div>
);
}
28 changes: 16 additions & 12 deletions frontend/src/edit/lulcMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,37 +73,41 @@ export default function LulcMenu(props) {
return (
<div id="landuse-select-form">
<label className="lulc-select">
<p>Landuse type:</p>
Landuse type:
<HTMLSelect
onChange={(event) => setNlud2(event.target.value)}
value={nlud2}
iconName="caret-down"
>
{nlud2Options.map((name) => <option key={name} value={name}>{name}</option>)}
</HTMLSelect>
<label id="nlud-subtype">
subtype:
<HTMLSelect
onChange={(event) => setNlud3(event.target.value)}
value={nlud3}
>
{nlud3Options.map((name) => <option key={name} value={name}>{name}</option>)}
</HTMLSelect>
</label>
</label>
<label className="lulc-select" id="nlud-subtype">
Landuse subtype:
<HTMLSelect
onChange={(event) => setNlud3(event.target.value)}
value={nlud3}
iconName="caret-down"
>
{nlud3Options.map((name) => <option key={name} value={name}>{name}</option>)}
</HTMLSelect>
</label>
<label className="lulc-select">
<p>Landcover:</p>
Landcover:
<HTMLSelect
onChange={(event) => setNlcd(event.target.value)}
value={nlcd}
iconName="caret-down"
>
{nlcdOptions.map((name) => <option key={name} value={name}>{name}</option>)}
</HTMLSelect>
</label>
<label className="lulc-select">
<p>Tree Cover:</p>
Tree Cover:
<HTMLSelect
onChange={(event) => setTree(event.target.value)}
value={tree}
iconName="caret-down"
>
{treeOptions.map((name) => <option key={name} value={name}>{name}</option>)}
</HTMLSelect>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/edit/results.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function ResultsDescription(props) {
return (
<ul>
<li>
<Icon icon="Flash" />
<Icon icon="flash" />
<p>
<span>
The average daytime high <b>temperature</b> during August is expected to
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/edit/resultsDemographics.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default function ResultsDemographics(props) {
<div>
<h2 id="demographics-header">
<Icon icon="people" size="30"/>
Demographics of the impacted area:
Demographics of the area influenced by the change:
</h2>
<div id="demographics-body">
{populationTable}
Expand Down
12 changes: 3 additions & 9 deletions frontend/src/edit/scenarioBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import React, { useState } from 'react';
import {
Button,
InputGroup,
Radio,
RadioGroup,
Spinner
Spinner,
} from '@blueprintjs/core';

import useInterval from '../hooks/useInterval';
Expand Down Expand Up @@ -41,7 +39,6 @@ export default function ScenarioBuilder(props) {
useInterval(async () => {
// There are sometimes two jobs submitted concurrently.
// They are in a priority queue, so for now monitor the lower priority one.
console.log('checking status for job', jobID);
const status = await getJobStatus(jobID);
if (status === 'success') {
refreshScenarios();
Expand Down Expand Up @@ -83,15 +80,15 @@ export default function ScenarioBuilder(props) {
Create a scenario:
</label>
<p><em>choose new landuse and landcover for the study area</em></p>
<RadioGroup
{/*<RadioGroup
className="conversion-radio"
inline
onChange={(event) => setConversionOption(event.target.value)}
selectedValue={conversionOption}
>
<Radio key="fill" value="fill" label="fill" />
<Radio key="wallpaper" value="wallpaper" label="wallpaper" disabled/>
</RadioGroup>
</RadioGroup>*/}
<div>
{
(conversionOption === 'fill')
Expand All @@ -113,9 +110,6 @@ export default function ScenarioBuilder(props) {
}
</div>
<div id="scenario-input-label">
{/*TODO: This subscript is a note to self, related to
https://github.com/natcap/urban-online-workflow/issues/124*/}
<sub>{singleLULC}</sub>
{
(jobID)
? <Spinner size="20" />
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/edit/studyAreaTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default function StudyAreaTable(props) {
const [lulcType, setLulcType] = useState('nlcd');

const deleteParcel = async (parcelID) => {
setHoveredParcel(null);
await removeParcel(parcelID, studyAreaID);
refreshStudyArea();
};
Expand All @@ -57,7 +58,7 @@ export default function StudyAreaTable(props) {
<tr key="header">
<td>
<Button
icon={hiddenRowClass ? 'Maximize' : 'Minimize'}
icon={hiddenRowClass ? 'maximize' : 'minimize'}
onClick={toggleRows}
/>
</td>
Expand Down
Loading
Loading