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

Add Impact Layers & Refactor App Redux Structure #55

Merged
merged 22 commits into from
May 26, 2020
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
895bece
Added WebStorm files to gitignore
bencpeters May 18, 2020
dc78ac2
Changed unused vars to warning
bencpeters May 19, 2020
df50299
Working POC of averaging pipeline
bencpeters May 19, 2020
3b5a07e
Moved layers under subdirectory
bencpeters May 19, 2020
faf5449
Refactored Layer types to get more specific type definitions, better …
bencpeters May 19, 2020
296f09c
Added types, config, scaling, and median functionality to the distrac…
bencpeters May 19, 2020
c09a022
Added utility functions for downloading WCS data from server
bencpeters May 20, 2020
1d2d85b
Refactored app to remove immutable.js (in favor of built-in immmer fo…
bencpeters May 21, 2020
4aba326
Moved layer data state to Redux
bencpeters May 21, 2020
db160be
Refactored layers to store data in Redux
bencpeters May 22, 2020
9bd2ef1
Added loading animation
bencpeters May 22, 2020
96c3c37
Updated test snapshots
bencpeters May 22, 2020
629a4b3
Merge remote-tracking branch 'origin/master' into geotiff-playground
bencpeters May 22, 2020
52c866c
Fixed eslint errors
bencpeters May 22, 2020
1effc45
Add additionali impact layers (#58)
ericboucher May 24, 2020
66dcdd0
Added date support to impact layers & added logic to load available d…
bencpeters May 25, 2020
09941ed
Excluded layersData from immutable state checks to improve dev mode p…
bencpeters May 26, 2020
78e4523
Added display for no districts meeting threshold, tweaked impact calc…
bencpeters May 26, 2020
7f5ebf1
Don't load impact layer until a date is selected
bencpeters May 26, 2020
47ff5df
Merge branch 'master' into add-impact-layers-refactor-redux
ericboucher May 26, 2020
3e93e8d
Added above/below to impact layer threshold definitions
bencpeters May 26, 2020
2d08b56
Merge branch 'add-impact-layers-refactor-redux' of github.com:oviohub…
bencpeters May 26, 2020
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
9 changes: 7 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,13 @@
]
}
],
"no-unused-vars": ["error", { "argsIgnorePattern": "^props$" }],
"no-console": ["warn", { "allow": ["warn", "error"] }]
"no-console": ["warn", { "allow": ["warn", "error"] }],
"no-unused-vars": ["warn", {
"argsIgnorePattern": "^props$",
"ignoreRestSiblings": true
}],
"lines-between-class-members": "off",
"max-classes-per-file": "off"
},
"settings": {
"import/resolver": {
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ yarn-error.log*
.firebase/

# Editors
.vscode/
.vscode/
.idea/
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"@turf/bbox": "^6.0.1",
"@turf/boolean-point-in-polygon": "^6.0.1",
"@turf/helpers": "^6.1.4",
"@types/d3": "^5.7.2",
"@types/geojson": "^7946.0.7",
"@types/jest": "^24.0.0",
"@types/lodash": "^4.14.149",
Expand All @@ -50,6 +54,7 @@
"@typescript-eslint/eslint-plugin": "^2.23.0",
"@typescript-eslint/parser": "^2.25.0",
"babel-eslint": "10.0.3",
"d3-fetch": "^1.1.2",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"enzyme-to-json": "^3.4.4",
Expand All @@ -65,8 +70,8 @@
"eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^1.7",
"geojson": "^0.5.0",
"geotiff": "^1.0.0-beta.11",
"husky": "^4.2.3",
"immutable": "^4.0.0-rc.12",
"lint-staged": "^10.0.8",
"lodash": "^4.17.15",
"mapbox-gl": "^1.8.1",
Expand All @@ -81,7 +86,7 @@
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.0",
"redux": "^4.0.5",
"redux-async-initial-state": "^0.3.0",
"reflect-metadata": "^0.1.13",
"typescript": "~3.8.2",
"url": "^0.11.0",
"xml-js": "^1.6.11"
Expand Down
2 changes: 2 additions & 0 deletions src/components/MapView/DateSelector/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { store } from '../../../context/store';
test('renders as expected', () => {
const realDateNow = Date.now.bind(global.Date);
const dateNowStub = jest.fn(() => 1530518207007);
// eslint-disable-next-line fp/no-mutation
global.Date.now = dateNowStub;

const { container } = render(
Expand All @@ -17,5 +18,6 @@ test('renders as expected', () => {
);
expect(container).toMatchSnapshot();

// eslint-disable-next-line fp/no-mutation
global.Date.now = realDateNow;
});
14 changes: 3 additions & 11 deletions src/components/MapView/DateSelector/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useState, useEffect, Fragment, forwardRef, Ref } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import moment from 'moment';
import { Map } from 'immutable';
import {
Divider,
Grid,
Expand All @@ -18,13 +17,11 @@ import {
faAngleDoubleLeft,
} from '@fortawesome/free-solid-svg-icons';
import 'react-datepicker/dist/react-datepicker.css';

import {
dateRangeSelector,
updateDateRange,
} from '../../../context/mapStateSlice';
import { months, getMonthStartAndEnd, isAvailableMonth } from './utils';
import { AvailableDates } from '../../../config/types';

interface InputProps {
value?: string;
Expand All @@ -41,7 +38,7 @@ const Input = forwardRef(
},
);

function DateSelector({ availableDates = Map(), classes }: DateSelectorProps) {
function DateSelector({ availableDates = [], classes }: DateSelectorProps) {
const dispatch = useDispatch();
const { startDate: stateStartDate } = useSelector(dateRangeSelector);
const stateStartDateYear = moment(stateStartDate).year();
Expand Down Expand Up @@ -91,12 +88,7 @@ function DateSelector({ availableDates = Map(), classes }: DateSelectorProps) {
showYearDropdown
dropdownMode="select"
customInput={<Input />}
includeDates={
availableDates
.valueSeq()
.flatten()
.toJS() as Date[]
}
includeDates={availableDates.map(d => new Date(d))}
/>
</Grid>

Expand Down Expand Up @@ -183,7 +175,7 @@ const styles = (theme: Theme) =>
});

export interface DateSelectorProps extends WithStyles<typeof styles> {
availableDates?: AvailableDates;
availableDates: number[];
}

export default withStyles(styles)(DateSelector);
14 changes: 5 additions & 9 deletions src/components/MapView/DateSelector/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import moment from 'moment';
import { List } from 'immutable';
import { AvailableDates } from '../../../config/types';

export const months = [
'Jan',
Expand Down Expand Up @@ -42,12 +40,10 @@ export function getMonthStartAndEnd(month: number, year: number) {
export function findAvailableDayInMonth(
month: number,
year: number,
availableDates: AvailableDates,
availableDates: number[],
) {
const date = new Date(year, month);
const filterDates = (dates: List<number>) =>
dates.filter(dateIt => moment(dateIt).isSame(date, 'month'));
return availableDates.map(filterDates).filter(layerDates => layerDates.size);
const reference = new Date(year, month);
return availableDates.filter(d => moment(d).isSame(reference, 'month'));
}

/**
Expand All @@ -60,7 +56,7 @@ export function findAvailableDayInMonth(
export function isAvailableMonth(
month: number,
year: number,
availableDates: AvailableDates,
availableDates: number[],
) {
return !findAvailableDayInMonth(month, year, availableDates).isEmpty();
return findAvailableDayInMonth(month, year, availableDates).length > 0;
}
70 changes: 0 additions & 70 deletions src/components/MapView/GroundstationLayers/index.tsx

This file was deleted.

60 changes: 60 additions & 0 deletions src/components/MapView/Layers/GroundstationLayer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useEffect } from 'react';
import { get } from 'lodash';
import { GeoJSONLayer } from 'react-mapbox-gl';
import * as MapboxGL from 'mapbox-gl';
import { useSelector, useDispatch } from 'react-redux';
import { legendToStops } from '../layer-utils';
import { GroundstationLayerProps } from '../../../../config/types';
import { layerDataSelector } from '../../../../context/mapStateSlice';
import {
LayerData,
loadLayerData,
} from '../../../../context/layers/layer-data';

function onClickCircle(evt: any) {
// eslint-disable-next-line
console.log(
get(evt.features[0], 'properties.index'),
get(evt.features[0], 'properties.aimagname'),
get(evt.features[0], 'properties.sumname'),
get(evt.features[0], 'properties.rasterheight'),
);
}

function GroundstationLayers({ layer }: { layer: GroundstationLayerProps }) {
const layerData = useSelector(layerDataSelector(layer.id)) as
| LayerData<GroundstationLayerProps>
| undefined;
const dispatch = useDispatch();

const { data } = layerData || {};

useEffect(() => {
if (!data) {
dispatch(loadLayerData({ layer }));
}
}, [data, dispatch, layer]);

if (!data) {
return null;
}

const circleLayout: MapboxGL.CircleLayout = { visibility: 'visible' };
const circlePaint: MapboxGL.CirclePaint = {
'circle-color': {
property: 'rasterheight',
stops: legendToStops(layer.legend),
},
};

return (
<GeoJSONLayer
data={data}
circleLayout={circleLayout}
circlePaint={circlePaint}
circleOnClick={onClickCircle}
/>
);
}

export default GroundstationLayers;
61 changes: 61 additions & 0 deletions src/components/MapView/Layers/ImpactLayer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { GeoJSONLayer } from 'react-mapbox-gl';
import { FillPaint, LinePaint } from 'mapbox-gl';
import { Extent } from '../raster-utils';
import { legendToStops } from '../layer-utils';
import { ImpactLayerProps } from '../../../../config/types';
import { loadLayerData } from '../../../../context/layers/layer-data';
import {
mapSelector,
layerDataSelector,
dateRangeSelector,
} from '../../../../context/mapStateSlice';

const linePaint: LinePaint = {
'line-color': 'grey',
'line-width': 1,
'line-opacity': 0.3,
};

export const ImpactLayer = ({ layer }: { layer: ImpactLayerProps }) => {
const map = useSelector(mapSelector);
const { startDate: selectedDate } = useSelector(dateRangeSelector);
const { data: features, date } =
useSelector(layerDataSelector(layer.id, selectedDate)) || {};
const dispatch = useDispatch();

const bounds = map && map.getBounds();
const minX = bounds ? bounds.getWest() : 0;
const maxX = bounds ? bounds.getEast() : 0;
const minY = bounds ? bounds.getSouth() : 0;
const maxY = bounds ? bounds.getNorth() : 0;

useEffect(() => {
// For now, assume that if we have layer data, we don't need to refetch. This could change down the line if we
// want to dynamically re-fetch data based on changing map bounds.
// Only fetch once we actually know the extent
if ((!features || date !== selectedDate) && minX !== 0 && maxX !== 0) {
const extent: Extent = [minX, minY, maxX, maxY];
dispatch(loadLayerData({ layer, extent, date: selectedDate }));
}
}, [dispatch, layer, maxX, maxY, minX, minY, features, selectedDate, date]);

if (!features) {
return null;
}

const fillPaint: FillPaint = {
'fill-opacity': layer.opacity || 0.1,
'fill-color': {
property: 'impactValue',
stops: legendToStops(layer.legend),
},
};

return (
<GeoJSONLayer data={features} linePaint={linePaint} fillPaint={fillPaint} />
);
};

export default ImpactLayer;
Loading