mirror of
https://github.com/DOI-DO/j40-cejst-2.git
synced 2025-09-30 02:13:18 -07:00
CEJST Map (#139)
* styles prettier fix * Addresses issue #100 from the frontend: * Creates new cejst page and related OL components * temporarily loads census-derived tileserver at higher zoom levels * lays out cejst page : TODO : remove aside * temporarily removing license check - TODO: fix jsonlint * review comments
This commit is contained in:
parent
244b3663d1
commit
292c5bc8f5
19 changed files with 455 additions and 19 deletions
3
client/src/components/HowYouCanHelp.module.scss
Normal file
3
client/src/components/HowYouCanHelp.module.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
.howYouCanHelpContainer {
|
||||
margin: 29px 24px 49px 42px;
|
||||
}
|
12
client/src/components/HowYouCanHelp.module.scss.d.ts
vendored
Normal file
12
client/src/components/HowYouCanHelp.module.scss.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
declare namespace HowYouCanHelpModuleScssNamespace {
|
||||
export interface IHowYouCanHelpModuleScss {
|
||||
howYouCanHelpContainer: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare const HowYouCanHelpModuleScssModule: HowYouCanHelpModuleScssNamespace.IHowYouCanHelpModuleScss & {
|
||||
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
|
||||
locals: HowYouCanHelpModuleScssNamespace.IHowYouCanHelpModuleScss;
|
||||
};
|
||||
|
||||
export = HowYouCanHelpModuleScssModule;
|
19
client/src/components/HowYouCanHelp.tsx
Normal file
19
client/src/components/HowYouCanHelp.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import React from 'react';
|
||||
import * as styles from './HowYouCanHelp.module.scss';
|
||||
|
||||
const HowYouCanHelp = () => {
|
||||
return (
|
||||
<div className={styles.howYouCanHelpContainer}>
|
||||
<h2>How You Can Help Improve the Tool</h2>
|
||||
<ul>
|
||||
<li>If you have information that could help, we’d love to hear from you.</li>
|
||||
<li>View our full set of data sources and methodology
|
||||
where you can add or download sources and check statuses on our data roadmap.</li>
|
||||
<li>Check out our timeline and send feedback or attend relevant events.</li>
|
||||
<li>Contact us and share the stories of your community.</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HowYouCanHelp;
|
|
@ -39,9 +39,8 @@ const J40Header = () => {
|
|||
className={'j40-header'}>Timeline</Link>],
|
||||
['cejst',
|
||||
<Link
|
||||
to={'/#cejst'}
|
||||
to={'/cejst'}
|
||||
key={'cejst'}
|
||||
target={'_blank'}
|
||||
className={'j40-header'}>CEJST</Link>],
|
||||
]);
|
||||
|
||||
|
|
4
client/src/components/map.module.scss
Normal file
4
client/src/components/map.module.scss
Normal file
|
@ -0,0 +1,4 @@
|
|||
.mapContainer {
|
||||
height: 676px;
|
||||
margin-bottom: 29px;
|
||||
}
|
12
client/src/components/map.module.scss.d.ts
vendored
Normal file
12
client/src/components/map.module.scss.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
declare namespace MapModuleScssNamespace {
|
||||
export interface IMapModuleScss {
|
||||
mapContainer: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare const MapModuleScssModule: MapModuleScssNamespace.IMapModuleScss & {
|
||||
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
|
||||
locals: MapModuleScssNamespace.IMapModuleScss;
|
||||
};
|
||||
|
||||
export = MapModuleScssModule;
|
92
client/src/components/map.tsx
Normal file
92
client/src/components/map.tsx
Normal file
|
@ -0,0 +1,92 @@
|
|||
import React, {useState, useEffect, useRef} from 'react';
|
||||
import Map from 'ol/Map';
|
||||
import View from 'ol/View';
|
||||
import Feature from 'ol/Feature';
|
||||
import Geometry from 'ol/geom/Geometry';
|
||||
import TileLayer from 'ol/layer/Tile';
|
||||
import VectorTileSource from 'ol/source/VectorTile';
|
||||
import VectorTileLayer from 'ol/layer/VectorTile';
|
||||
import MVT from 'ol/format/MVT';
|
||||
import VectorLayer from 'ol/layer/Vector';
|
||||
import VectorSource from 'ol/source/Vector';
|
||||
import XYZ from 'ol/source/XYZ';
|
||||
import {fromLonLat} from 'ol/proj';
|
||||
import * as styles from './map.module.scss';
|
||||
|
||||
|
||||
interface IMapWrapperProps {
|
||||
features: Feature<Geometry>[],
|
||||
};
|
||||
|
||||
// The below adapted from
|
||||
// https://taylor.callsen.me/using-openlayers-with-react-functional-components/
|
||||
const MapWrapper = ({features}: IMapWrapperProps) => {
|
||||
const [map, setMap] = useState<Map>();
|
||||
const [featuresLayer, setFeaturesLayer] = useState<VectorLayer>();
|
||||
|
||||
const mapElement = useRef() as
|
||||
React.MutableRefObject<HTMLInputElement>;
|
||||
|
||||
useEffect( () => {
|
||||
// create and add initial vector source layer, to be replaced layer
|
||||
const initialFeaturesLayer = new VectorLayer({
|
||||
source: new VectorSource(),
|
||||
});
|
||||
|
||||
const censusBlockGroupTileLayer = new VectorTileLayer({
|
||||
source: new VectorTileSource({
|
||||
format: new MVT(),
|
||||
url: 'https://gis.data.census.gov/arcgis/rest/services/Hosted/VT_2019_150_00_PY_D1/VectorTileServer/tile/{z}/{y}/{x}.mvt',
|
||||
}),
|
||||
});
|
||||
|
||||
const initialMap = new Map({
|
||||
target: mapElement.current,
|
||||
layers: [
|
||||
|
||||
new TileLayer({
|
||||
source: new XYZ({
|
||||
url: 'https://{1-4}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png',
|
||||
}),
|
||||
}),
|
||||
censusBlockGroupTileLayer,
|
||||
initialFeaturesLayer,
|
||||
],
|
||||
view: new View({
|
||||
projection: 'EPSG:3857',
|
||||
center: fromLonLat([-95.7129, 37.0902]),
|
||||
zoom: 3,
|
||||
}),
|
||||
controls: [],
|
||||
});
|
||||
|
||||
setMap(initialMap);
|
||||
setFeaturesLayer(initialFeaturesLayer);
|
||||
}, []);
|
||||
|
||||
|
||||
// update map if features prop changes
|
||||
useEffect( () => {
|
||||
if (features.length) { // may be empty on first render
|
||||
// set features to map
|
||||
featuresLayer?.setSource(
|
||||
new VectorSource({
|
||||
features: features,
|
||||
}),
|
||||
);
|
||||
const extent = featuresLayer?.getSource().getExtent();
|
||||
if (extent != null) {
|
||||
// fit map to feature extent (with 100px of padding)
|
||||
map?.getView().fit(extent, {
|
||||
padding: [100, 100, 100, 100],
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [features]);
|
||||
|
||||
return (
|
||||
<div ref={mapElement} className={styles.mapContainer}></div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MapWrapper;
|
3
client/src/components/mapControls.module.scss
Normal file
3
client/src/components/mapControls.module.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
.mapControlContainer {
|
||||
margin: 18.5px 42px 23px 42px;
|
||||
}
|
12
client/src/components/mapControls.module.scss.d.ts
vendored
Normal file
12
client/src/components/mapControls.module.scss.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
declare namespace MapControlModuleScssNamespace {
|
||||
export interface IMapControlModuleScss {
|
||||
mapControlContainer: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare const MapControlModuleScssModule: MapControlModuleScssNamespace.IMapControlModuleScss & {
|
||||
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
|
||||
locals: MapControlModuleScssNamespace.IMapControlModuleScss;
|
||||
};
|
||||
|
||||
export = MapControlModuleScssModule;
|
26
client/src/components/mapControls.tsx
Normal file
26
client/src/components/mapControls.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
import React from 'react';
|
||||
import {Button, ButtonGroup} from '@trussworks/react-uswds';
|
||||
import Feature from 'ol/Feature';
|
||||
import Geometry from 'ol/geom/Geometry';
|
||||
import * as styles from './mapControls.module.scss';
|
||||
|
||||
interface IMapControlsProps {
|
||||
setFeatures: (arg0: Feature<Geometry>[]) => void;
|
||||
}
|
||||
|
||||
const MapControls = ({setFeatures}: IMapControlsProps) => {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.mapControlContainer}>
|
||||
<h2>Explore the Tool</h2>
|
||||
<ButtonGroup type="segmented">
|
||||
<Button type="button">Combined</Button>
|
||||
<Button type="button" outline={true}>Poverty</Button>
|
||||
<Button type="button" outline={true}>Linguistic Isolation</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MapControls;
|
5
client/src/pages/cejst.module.scss
Normal file
5
client/src/pages/cejst.module.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
.disclaimer {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 21px;
|
||||
margin-left: 49px;
|
||||
}
|
12
client/src/pages/cejst.module.scss.d.ts
vendored
Normal file
12
client/src/pages/cejst.module.scss.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
declare namespace CejstModuleScssNamespace {
|
||||
export interface ICejstModuleScss {
|
||||
disclaimer: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare const CejstModuleScssModule: CejstModuleScssNamespace.ICejstModuleScss & {
|
||||
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
|
||||
locals: CejstModuleScssNamespace.ICejstModuleScss;
|
||||
};
|
||||
|
||||
export = CejstModuleScssModule;
|
59
client/src/pages/cejst.tsx
Normal file
59
client/src/pages/cejst.tsx
Normal file
|
@ -0,0 +1,59 @@
|
|||
import React, {useState} from 'react';
|
||||
import Layout from '../components/layout';
|
||||
import MapWrapper from '../components/map';
|
||||
import MapControls from '../components/mapControls';
|
||||
import HowYouCanHelp from '../components/HowYouCanHelp';
|
||||
import Feature from 'ol/Feature';
|
||||
import Geometry from 'ol/geom/Geometry';
|
||||
import {Alert} from '@trussworks/react-uswds';
|
||||
import * as styles from './cejst.module.scss';
|
||||
|
||||
|
||||
interface IMapPageProps {
|
||||
location: Location;
|
||||
}
|
||||
|
||||
const CEJSTPage = ({location}: IMapPageProps) => {
|
||||
const [features, setFeatures] = useState<Feature<Geometry>[]>([]);
|
||||
return (
|
||||
<Layout location={location}>
|
||||
<main id="main-content" role="main">
|
||||
<p className={styles.disclaimer}>
|
||||
The Climate and Economic Justice Screening Tool helps
|
||||
identify and prioritize communities across the United
|
||||
States and US territories that have been historically
|
||||
overburdened and underserved so that they may receive
|
||||
40% of the benefits from investments in six key areas as
|
||||
outlined in the <a
|
||||
href={'https://www.whitehouse.gov/briefing-room/' +
|
||||
'presidential-actions/2021/01/27/' +
|
||||
'executive-order-on-tackling-the-climate-' +
|
||||
'crisis-at-home-and-abroad/'}
|
||||
target={'_blank'}
|
||||
rel={'noreferrer'}>
|
||||
Executive Order on Tackling the Climate Crisis at Home and
|
||||
Abroad</a>.
|
||||
Explore the map below or learn
|
||||
more about the methodology and data indicators used to
|
||||
prioritize Justice40 communities.
|
||||
</p>
|
||||
<Alert
|
||||
type="warning"
|
||||
heading="Limited Data Sources">
|
||||
<p>
|
||||
In this tool, we are using data sources that our
|
||||
combined by our cumulative impact methodology.
|
||||
Our sources were selected because sit amet,
|
||||
consectetur adipiscing. See all the sources we
|
||||
are investigating on our data roadmap.
|
||||
</p>
|
||||
</Alert>
|
||||
<MapControls setFeatures={setFeatures}/>
|
||||
<MapWrapper features={features} />
|
||||
<HowYouCanHelp />
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default CEJSTPage;
|
|
@ -19,6 +19,7 @@ import pollutionIcon // @ts-ignore
|
|||
// @ts-ignore
|
||||
import washIcon from '/node_modules/uswds/dist/img/usa-icons/wash.svg';
|
||||
|
||||
|
||||
interface IndexPageProps {
|
||||
location: Location;
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
These are necessary for the image and font urls referenced in the source
|
||||
files to resolve correctly.
|
||||
*/
|
||||
$theme-image-path: '../../node_modules/uswds/src/img';
|
||||
$theme-font-path: '../../node_modules/uswds/src/fonts';
|
||||
$theme-image-path: "../../node_modules/uswds/src/img";
|
||||
$theme-font-path: "../../node_modules/uswds/src/fonts";
|
||||
|
||||
/*
|
||||
Example:
|
||||
|
@ -21,7 +21,7 @@ $theme-show-notifications: false;
|
|||
|
||||
$theme-font-role-heading: "sans";
|
||||
|
||||
@import '../../node_modules/uswds';
|
||||
@import "../../node_modules/uswds";
|
||||
|
||||
@import "~@trussworks/react-uswds/lib/index.css";
|
||||
|
||||
|
@ -66,13 +66,15 @@ $j40-max-width: 80ex;
|
|||
}
|
||||
}
|
||||
|
||||
.j40-header, .j40-primary-nav, .j40-header > li > a {
|
||||
.j40-header,
|
||||
.j40-primary-nav,
|
||||
.j40-header > li > a {
|
||||
background-color: #112f4e; /* todo: move color to theme */
|
||||
color: white !important;
|
||||
z-index: 5;
|
||||
|
||||
.usa-nav-container {
|
||||
max-width: ($j40-max-width*2);
|
||||
max-width: ($j40-max-width * 2);
|
||||
}
|
||||
|
||||
span {
|
||||
|
@ -86,7 +88,8 @@ $j40-max-width: 80ex;
|
|||
font-family: "serif";
|
||||
}
|
||||
|
||||
.usa-current::after, :hover::after {
|
||||
.usa-current::after,
|
||||
:hover::after {
|
||||
background-color: #2491ff !important;
|
||||
}
|
||||
|
||||
|
@ -173,11 +176,16 @@ $j40-max-width: 80ex;
|
|||
|
||||
// NOTE: uswds `.usa-prose` defines these all as Merriweather Web via $theme-font-role-heading
|
||||
.usa-prose {
|
||||
h1, h2, h3, h4 {
|
||||
font-family: Source Sans Pro Web, Helvetica Neue, Helvetica, Roboto, Arial, sans-serif;
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
font-family: Source Sans Pro Web, Helvetica Neue, Helvetica, Roboto, Arial,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
p, div {
|
||||
p,
|
||||
div {
|
||||
max-width: $j40-max-width;
|
||||
}
|
||||
}
|
||||
|
@ -194,4 +202,3 @@ $j40-max-width: 80ex;
|
|||
margin-top: 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue