diff --git a/client/src/components/MapLegend.tsx b/client/src/components/MapLegend.tsx deleted file mode 100644 index af22b225..00000000 --- a/client/src/components/MapLegend.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; - -const MapLegend = () => { - return ( - <> -
-

Color Key

-
 
-
Prioritized community
-
 
-
Threshold community
-
$nbsp;
-
Non-Prioritized community
-
- - ); -}; - -export default MapLegend; diff --git a/client/src/components/mapLegend.module.scss b/client/src/components/mapLegend.module.scss new file mode 100644 index 00000000..1ae6a5ab --- /dev/null +++ b/client/src/components/mapLegend.module.scss @@ -0,0 +1,41 @@ +$min-color: #fafaf8; +$med-color: rgba(26, 68, 128, 0.2); +$max-color: rgba(26, 68, 128, 0.6); + +.legendContainer { + margin-top: 19px; + font-size: 0.8em; +} + +.swatchContainer { + display: flex; + flex-direction: row; + justify-content: flex-start; +} + +.legendItem { + display: flex; + flex-direction: row; + align-items: center; + margin-right: 29px; +} + +.colorSwatch { + box-sizing: border-box; + height: 20px; + width: 20px; + border: 1px solid #1a4480; + margin-right: 10px; +} + +#prioritized { + background-color: $max-color; +} + +#threshold { + background-color: $med-color; +} + +#nonPrioritized { + background-color: $min-color; +} diff --git a/client/src/components/mapLegend.module.scss.d.ts b/client/src/components/mapLegend.module.scss.d.ts new file mode 100644 index 00000000..852606f9 --- /dev/null +++ b/client/src/components/mapLegend.module.scss.d.ts @@ -0,0 +1,19 @@ +declare namespace HowYouCanHelpModuleScssNamespace { + export interface IHowYouCanHelpModuleScss { + legendContainer: string; + legendHeader: string; + swatchContainer: string; + colorSwatch: string; + prioritized: string, + threshold: string, + nonPrioritized: string, + legendItem: 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; diff --git a/client/src/components/mapLegend.tsx b/client/src/components/mapLegend.tsx new file mode 100644 index 00000000..a9f9b7e8 --- /dev/null +++ b/client/src/components/mapLegend.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import * as styles from './mapLegend.module.scss'; + +const MapLegend = () => { + return ( +
+

COLOR KEY

+
+
+
+ Prioritized Community +
+
+
+ Threshold Community +
+
+
+ Non-Prioritized Community +
+
+
+ ); +}; + +export default MapLegend; diff --git a/client/src/components/mapWrapper.tsx b/client/src/components/mapWrapper.tsx index 30c4bff9..41c9b28d 100644 --- a/client/src/components/mapWrapper.tsx +++ b/client/src/components/mapWrapper.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import {useFlags} from '../contexts/FlagContext'; import MapboxMap from './mapboxMap'; import OpenLayersMap from './openlayersMap'; -import * as constants from '../data/constants'; const MapWrapper = () => { const flags = useFlags(); @@ -13,7 +12,6 @@ const MapWrapper = () => { : } -

Current Score Property: {constants.SCORE_PROPERTY}

); }; diff --git a/client/src/components/mapboxMap.module.scss b/client/src/components/mapboxMap.module.scss index f1681ae9..e423ba90 100644 --- a/client/src/components/mapboxMap.module.scss +++ b/client/src/components/mapboxMap.module.scss @@ -3,31 +3,12 @@ $sidebar-color: #ffffff; .mapContainer { height: 676px; - margin-bottom: 29px; - max-width: revert; - margin-top: 50px; -} - -.sidebar { - background-color: $sidebar-background; - color: $sidebar-color; - padding: 6px 12px; - font-family: monospace; - z-index: 1; - position: absolute; - top: 300px; - left: 0; - margin: 12px; - border-radius: 4px; } .j40Popup { max-height: 300px; - max-width: 300px; - overflow: scroll; + min-width: 36%; + max-width: 36%; + overflow-y: scroll; pointer-events: all !important; } - -.j40Popup .mapboxgl-popup-content { - pointer-events: all; -} diff --git a/client/src/components/mapboxMap.module.scss.d.ts b/client/src/components/mapboxMap.module.scss.d.ts index ad779b56..15389f65 100644 --- a/client/src/components/mapboxMap.module.scss.d.ts +++ b/client/src/components/mapboxMap.module.scss.d.ts @@ -1,6 +1,5 @@ declare namespace MapboxMapModuleScssNamespace { export interface IMapboxMapModuleScss { - sidebar: string; mapContainer: string; j40Popup: string; } diff --git a/client/src/components/mapboxMap.tsx b/client/src/components/mapboxMap.tsx index 9a6f77da..eb17cb65 100644 --- a/client/src/components/mapboxMap.tsx +++ b/client/src/components/mapboxMap.tsx @@ -9,10 +9,10 @@ import {LngLatBoundsLike, import mapStyle from '../data/mapStyle'; import ZoomWarning from './zoomWarning'; import PopupContent from './popupContent'; -import * as styles from './mapboxMap.module.scss'; import * as constants from '../data/constants'; import ReactDOM from 'react-dom'; import 'mapbox-gl/dist/mapbox-gl.css'; +import * as styles from './mapboxMap.module.scss'; type ClickEvent = mapboxgl.MapMouseEvent & mapboxgl.EventData; @@ -34,8 +34,14 @@ const MapboxMap = () => { maxZoom: constants.GLOBAL_MAX_ZOOM, maxBounds: constants.GLOBAL_MAX_BOUNDS as LngLatBoundsLike, }); + // disable map rotation using right click + drag + initialMap.dragRotate.disable(); + + // disable map rotation using touch rotation gesture + initialMap.touchZoomRotate.disableRotation(); + initialMap.on('click', handleClick); - initialMap.addControl(new NavigationControl()); + initialMap.addControl(new NavigationControl({showCompass: false}), 'top-left'); map.current = initialMap; }); @@ -43,7 +49,7 @@ const MapboxMap = () => { const map = e.target; const clickedCoord = e.point; const features = map.queryRenderedFeatures(clickedCoord, { - layers: ['score-low'], + layers: ['score'], }); if (features.length && features[0].properties) { @@ -52,11 +58,11 @@ const MapboxMap = () => { const options : PopupOptions = { offset: [0, 0], className: styles.j40Popup, + focusAfterOpen: false, }; new Popup(options) .setLngLat(e.lngLat) .setDOMContent(placeholder) - .setMaxWidth('300px') .addTo(map); } }; @@ -66,10 +72,10 @@ const MapboxMap = () => { map.current.on('move', () => { setZoom(map.current.getZoom()); }); - map.current.on('mouseenter', 'score-low', () => { + map.current.on('mouseenter', 'score', () => { map.current.getCanvas().style.cursor = 'pointer'; }); - map.current.on('mouseleave', 'score-low', () => { + map.current.on('mouseleave', 'score', () => { map.current.getCanvas().style.cursor = ''; }); }); diff --git a/client/src/components/openlayersMap.tsx b/client/src/components/openlayersMap.tsx index cfb44cba..19dd21de 100644 --- a/client/src/components/openlayersMap.tsx +++ b/client/src/components/openlayersMap.tsx @@ -10,7 +10,7 @@ import {Coordinate} from 'ol/coordinate'; import olms from 'ol-mapbox-style'; import mapStyle from '../data/mapStyle'; import ZoomWarning from './zoomWarning'; -import MapPopup from './mapPopup'; +import OpenlayersPopup from './openlayersPopup'; import {transformExtent} from 'ol/src/proj'; import * as styles from './openlayersMap.module.scss'; import * as constants from '../data/constants'; @@ -121,7 +121,7 @@ const MapWrapper = ({features}: IMapWrapperProps) => { <>
{map? - : + : '' } diff --git a/client/src/components/mapPopup.module.scss b/client/src/components/openlayersPopup.module.scss similarity index 100% rename from client/src/components/mapPopup.module.scss rename to client/src/components/openlayersPopup.module.scss diff --git a/client/src/components/mapPopup.module.scss.d.ts b/client/src/components/openlayersPopup.module.scss.d.ts similarity index 100% rename from client/src/components/mapPopup.module.scss.d.ts rename to client/src/components/openlayersPopup.module.scss.d.ts diff --git a/client/src/components/mapPopup.tsx b/client/src/components/openlayersPopup.tsx similarity index 88% rename from client/src/components/mapPopup.tsx rename to client/src/components/openlayersPopup.tsx index 1d9c1a66..4edd0b72 100644 --- a/client/src/components/mapPopup.tsx +++ b/client/src/components/openlayersPopup.tsx @@ -1,18 +1,18 @@ import React, {useRef, useEffect, useState} from 'react'; -import * as styles from './mapPopup.module.scss'; +import * as styles from './openlayersPopup.module.scss'; import Overlay from 'ol/Overlay'; import {Coordinate} from 'ol/Coordinate'; import Map from 'ol/Map'; import {FeatureLike} from 'ol/Feature'; import PopupContent from './popupContent'; -interface IMapPopupProps { +interface IOpenlayersPopupProps { map: Map, selectedFeature: FeatureLike; position: Coordinate; } -const MapPopup = ({map, selectedFeature, position}: IMapPopupProps) => { +const OpenlayersPopup = ({map, selectedFeature, position}: IOpenlayersPopupProps) => { const popupContainerElement = useRef(null); const popupCloserElement = useRef(null); const popupContentElement = useRef(null); @@ -55,4 +55,4 @@ const MapPopup = ({map, selectedFeature, position}: IMapPopupProps) => { ); }; -export default MapPopup; +export default OpenlayersPopup; diff --git a/client/src/components/popupContent.module.scss b/client/src/components/popupContent.module.scss new file mode 100644 index 00000000..6d26b20b --- /dev/null +++ b/client/src/components/popupContent.module.scss @@ -0,0 +1,16 @@ +.popupContentTable { + min-width: 400px; +} + +.popupContentTable > thead { + background-color: #edeff0; +} + +.titleContainer { + display: flex; + flex-direction: column; +} + +.titleIndicatorName { + font-weight: bold; +} diff --git a/client/src/components/popupContent.module.scss.d.ts b/client/src/components/popupContent.module.scss.d.ts new file mode 100644 index 00000000..677c298e --- /dev/null +++ b/client/src/components/popupContent.module.scss.d.ts @@ -0,0 +1,15 @@ +declare namespace MapModuleScssNamespace { + export interface IMapModuleScss { + popupContainer: string; + popupContentTable:string; + titleContainer:string; + titleIndicatorName: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; diff --git a/client/src/components/popupContent.tsx b/client/src/components/popupContent.tsx index 49c765ab..48083d44 100644 --- a/client/src/components/popupContent.tsx +++ b/client/src/components/popupContent.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import {Table} from '@trussworks/react-uswds'; import * as constants from '../data/constants'; +import * as styles from './popupContent.module.scss'; interface IPopupContentProps { properties: constants.J40Properties, @@ -9,7 +9,7 @@ interface IPopupContentProps { const PopupContent = ({properties}:IPopupContentProps) => { const readablePercent = (percent: number) => { - return `${(percent * 100).toFixed(2)}%`; + return `${(percent * 100).toFixed(2)}`; }; const getCategorization = (percentile: number) => { @@ -28,28 +28,32 @@ const PopupContent = ({properties}:IPopupContentProps) => { const blockGroup = properties[constants.GEOID_PROPERTY]; const score = properties[constants.SCORE_PROPERTY] as number; return ( - - - - - - - - - - - - - - - -
Census Block Group:{blockGroup}
Just Progress Categorization:{getCategorization(score)}
Cumulative Index Score:{readablePercent(score)}
+
+
+ Census Block Group: + {blockGroup} +
+
+ Just Progress Categorization: + {getCategorization(score)} +
+
+ Cumulative Index Score: + {readablePercent(score)} +
+
); }; const getBodyContent = (properties: constants.J40Properties) => { const rows = []; - for (const [key, value] of Object.entries(properties)) { + const sortedKeys = Object.entries(properties).sort(); + for (let [key, value] of sortedKeys) { + // We should only format floats + if (typeof value === 'number' && value % 1 !== 0) { + value = readablePercent(value); + } + // Filter out all caps if (!key.match(/^[A-Z0-9]+$/)) { rows.push( @@ -65,19 +69,19 @@ const PopupContent = ({properties}:IPopupContentProps) => { return ( <> {properties ? -
+
{getTitleContent(properties)} - +
- - + + {getBodyContent(properties)} -
IndicatorPercentile(0-100)INDICATORVALUE
+
: '' } diff --git a/client/src/components/zoomWarning.module.scss b/client/src/components/zoomWarning.module.scss index ae8ad5fb..29999723 100644 --- a/client/src/components/zoomWarning.module.scss +++ b/client/src/components/zoomWarning.module.scss @@ -1,6 +1,6 @@ .zoomWarning { background-color: #953a10; - height: 5.5%; + height: 4.3%; width: 66%; margin: auto; color: white; @@ -9,7 +9,7 @@ justify-content: center; position: absolute; top: 50%; - left: 20%; + left: 15%; } .zoomWarning > img { diff --git a/client/src/data/constants.tsx b/client/src/data/constants.tsx index 7b4d3ab0..94ae3840 100644 --- a/client/src/data/constants.tsx +++ b/client/src/data/constants.tsx @@ -1,11 +1,26 @@ +// Properties export const SCORE_PROPERTY = 'Score D (percentile)'; export const GEOID_PROPERTY = 'GEOID10'; +export type J40Properties = { [key: string]: any }; + + +// Zoom export const GLOBAL_MIN_ZOOM = 3; -export const GLOBAL_MAX_ZOOM = 11; +export const GLOBAL_MAX_ZOOM = 22; export const GLOBAL_MIN_ZOOM_LOW = 3; -export const GLOBAL_MAX_ZOOM_LOW = 11; +export const GLOBAL_MAX_ZOOM_LOW = 9; export const GLOBAL_MIN_ZOOM_HIGH = 9; -export const GLOBAL_MAX_ZOOM_HIGH = 11; +export const GLOBAL_MAX_ZOOM_HIGH = 12; + +// Bounds export const GLOBAL_MAX_BOUNDS = [[-167.276413, 5.499550], [-52.233040, 83.162102]]; export const DEFAULT_CENTER = [32.4687126, -86.502136]; -export type J40Properties = { [key: string]: any }; + +// Opacity +export const DEFAULT_LAYER_OPACITY = 0.6; + +// Colors +export const DEFAULT_OUTLINE_COLOR = '#4EA5CF'; +export const MIN_COLOR = '#FFFFFF'; +export const MED_COLOR = '#D1DAE6'; +export const MAX_COLOR = '#768FB3'; diff --git a/client/src/data/mapStyle.tsx b/client/src/data/mapStyle.tsx index 4b3993e2..bfefbfe1 100644 --- a/client/src/data/mapStyle.tsx +++ b/client/src/data/mapStyle.tsx @@ -22,7 +22,7 @@ function makePaint({ minRamp, medRamp, maxRamp, - high, + high = true, }: { field: string; minRamp: number; @@ -30,52 +30,20 @@ function makePaint({ maxRamp: number; high: boolean; }): FillPaint { - const minColor = 'white'; // '232, 88%, 100%'; - const medColor = '#D1DAE6'; - const maxColor = '#768FB3'; // '0, 98%, 56%'; - return { + const paintDescriptor : FillPaint = { 'fill-color': [ - 'interpolate', - ['linear'], - ['zoom'], - high ? 9 : 0, - [ - 'step', - ['get', field], - hexToHSLA(minColor, high ? 0 : 0.5 ), - minRamp, - hexToHSLA(minColor, high ? 0 : 0.5 ), - medRamp, - hexToHSLA(medColor, high ? 0 : 0.5 ), - maxRamp, - hexToHSLA(maxColor, high ? 0 : 0.5 ), - ], - high ? 11 : 9, - [ - 'step', - ['get', field], - hexToHSLA(minColor, high ? 0.5 : 0.5 ), - minRamp, - hexToHSLA(minColor, high ? 0.5 : 0.5 ), - medRamp, - hexToHSLA(medColor, high ? 0.5 : 0.5 ), - maxRamp, - hexToHSLA(maxColor, high ? 0.5 : 0.5 ), - ], - high ? 22 : 11, - [ - 'step', - ['get', field], - hexToHSLA(minColor, high ? 0.5 : 0 ), - minRamp, - hexToHSLA(minColor, high ? 0.5 : 0 ), - medRamp, - hexToHSLA(medColor, high ? 0.5 : 0 ), - maxRamp, - hexToHSLA(maxColor, high ? 0.5 : 0 ), - ], + 'step', + ['get', field], + hexToHSLA(constants.MIN_COLOR, constants.DEFAULT_LAYER_OPACITY ), + minRamp, + hexToHSLA(constants.MIN_COLOR, constants.DEFAULT_LAYER_OPACITY ), + medRamp, + hexToHSLA(constants.MED_COLOR, constants.DEFAULT_LAYER_OPACITY ), + maxRamp, + hexToHSLA(constants.MAX_COLOR, constants.DEFAULT_LAYER_OPACITY ), ], }; + return paintDescriptor; } const mapStyle : Style = { @@ -96,7 +64,7 @@ const mapStyle : Style = { 'https://mt0.google.com/vt/lyrs=p&hl=en&x={x}&y={y}&z={z}', ], }, - 'custom': { + 'score': { 'type': 'vector', 'tiles': [ 'https://d2zjid6n5ja2pt.cloudfront.net/0629_demo/{z}/{x}/{y}.pbf', @@ -134,8 +102,8 @@ const mapStyle : Style = { }, }, { - 'id': 'score-low', - 'source': 'custom', + 'id': 'score', + 'source': 'score', 'source-layer': 'blocks', 'type': 'fill', 'filter': ['all', @@ -147,29 +115,28 @@ const mapStyle : Style = { minRamp: 0, medRamp: 0.6, maxRamp: 0.75, - high: false, - }), - 'minzoom': constants.GLOBAL_MIN_ZOOM_LOW, - 'maxzoom': constants.GLOBAL_MAX_ZOOM_LOW, - }, - { - 'id': 'score-high', - 'source': 'custom', - 'source-layer': 'blocks', - 'type': 'fill', - 'filter': ['all', - ['>', constants.SCORE_PROPERTY, 0.6], - // ['in', 'STATEFP10', '01', '30', '34', '35', '36'], - ], - 'paint': makePaint({ - field: constants.SCORE_PROPERTY, - minRamp: 0, - medRamp: 0.6, - maxRamp: 1.0, high: true, }), + 'minzoom': constants.GLOBAL_MIN_ZOOM, + 'maxzoom': constants.GLOBAL_MAX_ZOOM, + }, + { + 'id': 'score-highlights', + 'source': 'score', + 'source-layer': 'blocks', + 'type': 'line', 'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH, 'maxzoom': constants.GLOBAL_MAX_ZOOM_HIGH, + 'layout': { + 'visibility': 'visible', + 'line-join': 'round', + 'line-cap': 'round', + }, + 'paint': { + 'line-color': constants.DEFAULT_OUTLINE_COLOR, + 'line-width': 0.8, + 'line-opacity': 0.5, + }, }, { 'id': 'labels-only', diff --git a/client/src/pages/cejst.tsx b/client/src/pages/cejst.tsx index 81847a2c..2e7e5f2d 100644 --- a/client/src/pages/cejst.tsx +++ b/client/src/pages/cejst.tsx @@ -1,10 +1,9 @@ import React from 'react'; import Layout from '../components/layout'; -// import MapWrapper from '../components/map'; import MapWrapper from '../components/mapWrapper'; import HowYouCanHelp from '../components/HowYouCanHelp'; +import MapLegend from '../components/mapLegend'; import * as styles from './cejst.module.scss'; -import MapLegend from '../components/MapLegend'; interface IMapPageProps { @@ -35,8 +34,8 @@ const CEJSTPage = ({location}: IMapPageProps) => {

Explore the Tool

- - + + ); diff --git a/client/src/styles/global.scss b/client/src/styles/global.scss index 34afa685..33433940 100644 --- a/client/src/styles/global.scss +++ b/client/src/styles/global.scss @@ -93,7 +93,7 @@ $j40-max-width: 80ex; } .byline { - font-size: .4em; + font-size: 0.3em; font-weight: normal; } } @@ -192,35 +192,17 @@ $j40-max-width: 80ex; } } -.j40-maplegend { - dt { - width: 20px; - height: 20px; - display: inline-block; - overflow: hidden; - border: 1px solid #282828; - } +// Mapbox overrides +// Note that these need to be here to properly override defaults - dd { - vertical-align: top; - padding-top: 0.1em; - - display: inline-block; - width: 12em; - margin: 0 0 0 1em; - } - - .mapsquare-a { - background-color: #1A4480; - color: #1A4480; - } - .mapsquare-b { - background-color: #a9a9aa; - color: #a9a9aa; - } - - .mapsquare-c { - background-color: #ecececff; - color: #ecececff; - } +.mapboxgl-popup-close-button { + font-size: 3em; + margin-right: 12px; + margin-top: 15px; +} + +.mapboxgl-popup-content { + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5) !important; + border-radius: 8px !important; + pointer-events: all !important; }