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

Card/You and your neighbors #363

Merged
merged 19 commits into from Sep 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/2018-disaster-resilience/src/components/App/index.js
Expand Up @@ -20,8 +20,8 @@ const App = () => (
{/* <ViolentShakingAndGroundDeformation /> */}
<SignificantStructuralDamage />
{/* <LifeAlteringEvent /> */}
{/* <p classname="transition">The first step in increasing disaster resiliency for most Portlanders will be to understand what the estimated impact is within their immediate vicinity.</p> */}
{/* <YouAndYourNeighbors /> */}
<p className="transition">The first step in increasing disaster resilience for most Portlanders will be to understand what the estimated impact is within their immediate vicinity.</p>
<YouAndYourNeighbors />
{/* <p className="transition">Preparedness and disaster resiliency go hand-in-hand.</p> */}
<PullQuote
quoteText="Does your family have a plan for earthquake preparedness? Here are the steps to help you get started."
Expand Down
@@ -0,0 +1,44 @@
import React from 'react';

import { ChartTitle } from '@hackoregon/component-library';

import { shakingScale, landslidesScale, liquefactionScale, transformForLandslidesAndLiquefaction } from './shakingScales';
import IntensityDisplay from './IntensityDisplay';

const CoordsShakingInformation = ({ coordsProperties }) => (
<div>
<ChartTitle title='Earthquake Impacts For Your Location' subtitle='Expected outcomes in a wet season magnitude 9.0 Cascadia Subduction Zone earthquake' />
<IntensityDisplay
mean={coordsProperties.pgv_site_mean_mmi}
min={coordsProperties.pgv_site_min_mmi}
max={coordsProperties.pgv_site_max_mmi}
metric={'Shaking Intensity'}
label={'More shaking'}
scale={shakingScale}
domain={[1, 10]}
/>
<IntensityDisplay
mean={parseFloat(coordsProperties.pgd_landslide_wet_mean)}
min={parseFloat(coordsProperties.pgd_landslide_wet_min)}
max={parseFloat(coordsProperties.pgd_landslide_wet_max)}
metric={'Landslide Potential'}
label={'More landslides'}
scale={landslidesScale}
transform={transformForLandslidesAndLiquefaction}
domain={[0, 1180]}
/>
<IntensityDisplay
mean={parseFloat(coordsProperties.pgd_liquefaction_wet_mean)}
min={parseFloat(coordsProperties.pgd_liquefaction_wet_min)}
max={parseFloat(coordsProperties.pgd_liquefaction_wet_max)}
metric={'Liquefaction Potential'}
label={'More liquefaction'}
scale={liquefactionScale}
transform={transformForLandslidesAndLiquefaction}
domain={[0, 1180]}
/>
</div>
);

export default CoordsShakingInformation;

@@ -0,0 +1,88 @@
import React from 'react';
import PropTypes from 'prop-types';
import { css } from 'emotion';

import { GradientScale } from '@hackoregon/component-library';

const emphasis = css`
color: #000;
`;

const gradientLabel = css`
${emphasis};
position: relative;
bottom: -10px;
`;

const rightAlign = css`
display: flex;
justify-content: flex-end;
`;

const noTransform = x => x;

const IntensityDisplay = ({
mean,
min,
max,
metric,
label,
scale,
transform,
domain,
}) => (
<div>
<h3>
<strong>
{transform(min) === transform(max)
? `${metric}: `
: `Range of ${metric}: `}
</strong>
{transform(min) === transform(max)
? scale[transform(mean)].name
: `${scale[transform(min)].name} - ${scale[transform(max)].name}`}
</h3>
<div className={rightAlign}>
<strong className={gradientLabel}>{label}</strong>
</div>
<GradientScale
domain={domain}
primary={mean}
secondary={[min, max]}
height={50}
/>
{transform(min) !== transform(mean) && (
<p>
<strong>Best case: </strong>
{scale[transform(min)].description}
</p>
)}
<p className={emphasis}>
<strong>Most likely case: </strong>
{scale[transform(min)].description}
</p>
{transform(max) !== transform(mean) && (
<p>
<strong>Worst case: </strong>
{scale[transform(max)].description}
</p>
)}
</div>
);

IntensityDisplay.propTypes = {
mean: PropTypes.number,
min: PropTypes.number,
max: PropTypes.number,
metric: PropTypes.string,
label: PropTypes.string,
scale: PropTypes.object,
transform: PropTypes.func,
domain: PropTypes.arrayOf(PropTypes.number),
};

IntensityDisplay.defaultProps = {
transform: noTransform,
};

export default IntensityDisplay;
@@ -1,25 +1,163 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { css } from 'emotion';

import { CivicStoryCard, Placeholder } from '@hackoregon/component-library';
import { CivicStoryCard, BaseMap, IconMap, ChartTitle } from '@hackoregon/component-library';
import CoordsShakingInformation from './CoordsShakingInformation';

import {
fetchYouAndYourNeighbors,
fetchYouAndYourNeighborsCoords,
youAndYourNeighborsSetCoords,
} from '../../state/you-and-your-neighbors/actions';
import {
isYouAndYourNeighborsPending,
catchYouAndYourNeighborsErrors,
getYouAndYourNeighborsData,
isYouAndYourNeighborsCoordsPending,
catchYouAndYourNeighborsCoordsErrors,
getYouAndYourNeighborsCoordsData,
getSelectedCoords,
} from '../../state/you-and-your-neighbors/selectors';
import {
poiIconZoomScale,
poiGetIconColor,
poiIconMapping,
} from './layerStyles';


const mapContainer = css`
display: flex;
justifyContent: center;
width: 500px;
margin: 0 auto;
padding-bottom: 40px;
`;

const LAT = 45.5231;
const LONG = -122.6765;
const ZOOM = 13.5;

const geocoderOptions = {
bbox: [-123.1847001376, 45.2458284187, -122.2151566806, 45.8544896021],
zoom: 13.5,
trackProximity: true,
placeholder: 'Enter your address',
};

const mapGLOptions = {
scrollZoom: false,
dragPan: false,
dragRotate: false,
doubleClickZoom: false,
touchZoom: false,
touchRotate: false,
keyboard: false,
};

export class YouAndYourNeighbors extends React.Component {

componentDidMount() {
// initialize data here
this.props.init();
}

render() {
const {
isLoading,
error,
data,
isCoordsLoading,
coordsError,
coordsData,
selectedCoords,
setCoordinates,
} = this.props;

const geocoderChange = viewport => setCoordinates({ latitude: viewport.latitude, longitude: viewport.longitude });
const coordsProperties = (coordsData && coordsData.features.length > 0) && coordsData.features[0].properties;
const noCoordsData = coordsData && coordsData.features.length < 1;

return (
<CivicStoryCard
title="You and Your Neighbors in the Earthquake"
slug="you-and-your-neighbors-in-the-earthquake"
loading={isLoading}
error={error && 'Error loading data'}
>
<Placeholder issue="153" />
<div>
<p>It will be critical for individuals to understand their location relative to key resources immediately following an earthquake. The <a href="https://www.portlandoregon.gov/pbem/59630" target="_blank" rel="noopener noreferrer">BEECN site</a> is a place to go in Portland after a major earthquake to ask for emergency assistance or report severe damage/injury. Places like hospitals, fire stations and schools will be rallying areas for the community and crucial for recovery efforts. Input your address, or a friend/family member’s address below to generate a personalized map and information about expected impacts for your location.</p>
<ChartTitle title="Your Personalized Earthquake Map" subtitle="BEECN locations, Schools, Hospitals, and Fire Stations" />
<div className={mapContainer}>
<BaseMap
initialLongitude={LONG}
initialLatitude={LAT}
initialZoom={ZOOM}
navigation={false}
geocoder
geocoderOptions={geocoderOptions}
geocoderOnChange={geocoderChange}
mapGLOptions={mapGLOptions}
>
{
data &&
<IconMap
data={data.features}
pickable
opacity={0.5}
iconAtlas="https://i.imgur.com/xgTAROe.png"
iconMapping={poiIconMapping}
iconSizeScale={poiIconZoomScale}
getPosition={f => (f.geometry === null ? [0, 0] : f.geometry.coordinates)}
getIcon={f => f.properties.type}
getSize={f => 11}
getColor={poiGetIconColor}
autoHighlight={false}
highlightColor={[0, 0, 0, 0]}
/>
}
</BaseMap>
</div>
{ noCoordsData && <p>We don't have complete information for your address. <a href='http://www.civicplatform.org/'>Learn more about how your city can work to get their data in Civic.</a></p>}
{
coordsProperties &&
<CoordsShakingInformation coordsProperties={coordsProperties} />
}
</div>
</CivicStoryCard>
);
}
}

YouAndYourNeighbors.displayName = 'YouAndYourNeighbors';
YouAndYourNeighbors.propTypes = {
init: PropTypes.func,
isLoading: PropTypes.bool,
error: PropTypes.object,
data: PropTypes.object,
isCoordsLoading: PropTypes.bool,
coordsError: PropTypes.object,
coordsData: PropTypes.object,
setCoordinates: PropTypes.func,
};

// Connect this to the redux store when necessary
export default YouAndYourNeighbors;
export default connect(
state => ({
isLoading: isYouAndYourNeighborsPending(state),
error: catchYouAndYourNeighborsErrors(state),
data: getYouAndYourNeighborsData(state),
isCoordsLoading: isYouAndYourNeighborsCoordsPending(state),
coordsError: catchYouAndYourNeighborsCoordsErrors(state),
coordsData: getYouAndYourNeighborsCoordsData(state),
selectedCoords: getSelectedCoords(state),
}),
dispatch => ({
init() {
dispatch(fetchYouAndYourNeighbors());
},
setCoordinates(coordinates = {}) {
dispatch(fetchYouAndYourNeighborsCoords(coordinates));
dispatch(youAndYourNeighborsSetCoords(coordinates));
},
}),
)(YouAndYourNeighbors);
@@ -0,0 +1,59 @@
// Slide 016 - points of interest - from packages/civic-sandbox/src/state/layerStyles.js
export const poiIconMapping = {
School: {
x: 0,
y: 0,
width: 250,
height: 250,
mask: true,
},
Hospital: {
x: 250,
y: 0,
width: 250,
height: 250,
mask: true,
},
BEECN: {
x: 500,
y: 0,
width: 250,
height: 250,
mask: true,
},
'Fire Station': {
x: 0,
y: 250,
width: 250,
height: 250,
mask: true,
},
Pin: {
x: 250,
y: 250,
width: 250,
height: 250,
mask: true,
},
COMMCTR: {
x: 500,
y: 250,
width: 250,
height: 250,
mask: true,
},
};

export const poiIconZoomScale = zoom => zoom > 11.5 ? 10 :
zoom > 10.5 ? 8 :
zoom > 9.5 ? 6 :
zoom > 8.5 ? 4 :
zoom > 7.5 ? 2 :
1;

export const poiGetIconColor = f => f.properties.type === 'BEECN' ? [0, 0, 0, 255] :
f.properties.type === 'COMMCTR' ? [114, 29, 124, 255] :
f.properties.type === 'Fire Station' ? [220, 69, 86, 255] :
f.properties.type === 'School' ? [255, 178, 38, 255] :
f.properties.type === 'Hospital' ? [30, 98, 189, 255] :
[0, 0, 0, 255];