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:
Nat Hillard 2021-06-16 18:16:49 -04:00 committed by GitHub
commit 292c5bc8f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 455 additions and 19 deletions

View file

@ -0,0 +1,3 @@
.howYouCanHelpContainer {
margin: 29px 24px 49px 42px;
}

View 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;

View 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, wed 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;

View file

@ -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>],
]);

View file

@ -0,0 +1,4 @@
.mapContainer {
height: 676px;
margin-bottom: 29px;
}

View 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;

View 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;

View file

@ -0,0 +1,3 @@
.mapControlContainer {
margin: 18.5px 42px 23px 42px;
}

View 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;

View 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;