Adding low zoom level styling (#334)

* Fixes #201 - As an EVCM, I want the map to make sense at varying zoom levels so that I'm not confused . For now uses a usa_low tileset created as part of #209 for zoom levels 3-7. Will need further iteration
* Adding comments
This commit is contained in:
Nat Hillard 2021-07-12 18:05:11 -04:00 committed by GitHub
commit 68c345b950
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 117 additions and 51 deletions

View file

@ -39,7 +39,7 @@ describe('Tests for the Explore the Map page', () => {
return map.getFeatureState( return map.getFeatureState(
{ {
'id': id, 'id': id,
'source': constants.SCORE_SOURCE_NAME, 'source': constants.HIGH_SCORE_SOURCE_NAME,
'sourceLayer': constants.SCORE_SOURCE_LAYER, 'sourceLayer': constants.SCORE_SOURCE_LAYER,
}, },
); );

View file

@ -8,7 +8,6 @@ import maplibregl, {LngLatBoundsLike,
LngLatLike, LngLatLike,
MapboxGeoJSONFeature} from 'maplibre-gl'; MapboxGeoJSONFeature} from 'maplibre-gl';
import mapStyle from '../data/mapStyle'; import mapStyle from '../data/mapStyle';
import ZoomWarning from './zoomWarning';
import PopupContent from './popupContent'; import PopupContent from './popupContent';
import * as constants from '../data/constants'; import * as constants from '../data/constants';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
@ -76,7 +75,7 @@ const J40Map = () => {
const map = e.target; const map = e.target;
const clickedCoord = e.point; const clickedCoord = e.point;
const features = map.queryRenderedFeatures(clickedCoord, { const features = map.queryRenderedFeatures(clickedCoord, {
layers: ['score'], layers: [constants.HIGH_SCORE_LAYER_NAME],
}); });
const feature = features && features[0]; const feature = features && features[0];
if (feature) { if (feature) {
@ -107,10 +106,10 @@ const J40Map = () => {
mapRef.current.on('move', () => { mapRef.current.on('move', () => {
setZoom(mapRef.current.getZoom()); setZoom(mapRef.current.getZoom());
}); });
mapRef.current.on('mouseenter', 'score', () => { mapRef.current.on('mouseenter', constants.HIGH_SCORE_LAYER_NAME, () => {
mapRef.current.getCanvas().style.cursor = 'pointer'; mapRef.current.getCanvas().style.cursor = 'pointer';
}); });
mapRef.current.on('mouseleave', 'score', () => { mapRef.current.on('mouseleave', constants.HIGH_SCORE_LAYER_NAME, () => {
mapRef.current.getCanvas().style.cursor = ''; mapRef.current.getCanvas().style.cursor = '';
}); });
}, [mapRef]); }, [mapRef]);
@ -177,7 +176,6 @@ const J40Map = () => {
</button> </button>
</div> </div>
<div ref={mapContainer} className={styles.mapContainer}/> <div ref={mapContainer} className={styles.mapContainer}/>
<ZoomWarning zoomLevel={zoom} />
</div> </div>
); );
}; };

View file

@ -1,13 +1,22 @@
// URLS // URLS
export const FEATURE_TILE_BASE_URL = 'https://d2zjid6n5ja2pt.cloudfront.net/0629_demo'; export const FEATURE_TILE_BASE_URL = 'https://d2zjid6n5ja2pt.cloudfront.net';
const XYZ_SUFFIX = '{z}/{x}/{y}.pbf';
export const FEATURE_TILE_HIGH_ZOOM_URL = `${FEATURE_TILE_BASE_URL}/0629_demo/${XYZ_SUFFIX}`;
export const FEATURE_TILE_LOW_ZOOM_URL = `${FEATURE_TILE_BASE_URL}/tiles_low/${XYZ_SUFFIX}`;
// Performance markers // Performance markers
export const PERFORMANCE_MARKER_MAP_IDLE = 'MAP_IDLE'; export const PERFORMANCE_MARKER_MAP_IDLE = 'MAP_IDLE';
// Properties // Properties
export const SCORE_PROPERTY = 'Score D (percentile)'; export const SCORE_PROPERTY_HIGH = 'Score D (percentile)';
export const SCORE_PROPERTY_LOW = 'D_SCORE';
export const GEOID_PROPERTY = 'GEOID10'; export const GEOID_PROPERTY = 'GEOID10';
export const SCORE_SOURCE_NAME = 'score'; export const HIGH_SCORE_SOURCE_NAME = 'score-high';
export const HIGH_SCORE_LAYER_NAME = 'score-high-layer';
export const LOW_SCORE_SOURCE_NAME = 'score-low';
export const LOW_SCORE_LAYER_NAME = 'score-low-layer';
// The name of the layer within the tiles that contains the score // The name of the layer within the tiles that contains the score
export const SCORE_SOURCE_LAYER = 'blocks'; export const SCORE_SOURCE_LAYER = 'blocks';
@ -18,8 +27,10 @@ export type J40Properties = { [key: string]: any };
export const GLOBAL_MIN_ZOOM = 3; export const GLOBAL_MIN_ZOOM = 3;
export const GLOBAL_MAX_ZOOM = 22; export const GLOBAL_MAX_ZOOM = 22;
export const GLOBAL_MIN_ZOOM_LOW = 3; export const GLOBAL_MIN_ZOOM_LOW = 3;
export const GLOBAL_MAX_ZOOM_LOW = 9; export const GLOBAL_MAX_ZOOM_LOW = 7;
export const GLOBAL_MIN_ZOOM_HIGH = 9; export const GLOBAL_MIN_ZOOM_HIGHLIGHT = 9;
export const GLOBAL_MAX_ZOOM_HIGHLIGHT = 22;
export const GLOBAL_MIN_ZOOM_HIGH = 7;
export const GLOBAL_MAX_ZOOM_HIGH = 11; export const GLOBAL_MAX_ZOOM_HIGH = 11;
// Bounds // Bounds
@ -74,3 +85,13 @@ export const MIN_COLOR = '#FFFFFF';
export const MED_COLOR = '#D1DAE6'; export const MED_COLOR = '#D1DAE6';
export const MAX_COLOR = '#768FB3'; export const MAX_COLOR = '#768FB3';
export const BORDER_HIGHLIGHT_COLOR = '#00BDE3'; export const BORDER_HIGHLIGHT_COLOR = '#00BDE3';
// Widths
export const HIGHLIGHT_BORDER_WIDTH = 5.0;
// Score boundaries
export const SCORE_BOUNDARY_LOW = 0.0;
export const SCORE_BOUNDARY_THRESHOLD = 0.6;
export const SCORE_BOUNDARY_PRIORITIZED = 0.75;
export const isMobile = typeof window !== 'undefined' && (window.innerWidth < 400);

View file

@ -43,47 +43,70 @@ function makePaint({
return paintDescriptor; return paintDescriptor;
} }
const imageSuffix = constants.isMobile ? '' : '@2x';
const mapStyle : Style = { const mapStyle : Style = {
'version': 8, 'version': 8,
'sources': { 'sources': {
'carto': { 'carto': {
'type': 'raster', 'type': 'raster',
'tiles': [ 'tiles':
'https://a.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', [
'https://b.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', `https://a.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}${imageSuffix}.png`,
'https://c.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', `https://b.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}${imageSuffix}.png`,
'https://d.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', `https://c.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}${imageSuffix}.png`,
`https://d.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}${imageSuffix}.png`,
], ],
'minzoom': constants.GLOBAL_MIN_ZOOM,
'maxzoom': constants.GLOBAL_MAX_ZOOM,
}, },
'geo': { 'geo': {
'type': 'raster', 'type': 'raster',
'tiles': [ 'tiles': [
'https://mt0.google.com/vt/lyrs=p&hl=en&x={x}&y={y}&z={z}', 'https://mt0.google.com/vt/lyrs=p&hl=en&x={x}&y={y}&z={z}',
], ],
'minzoom': constants.GLOBAL_MIN_ZOOM,
'maxzoom': constants.GLOBAL_MAX_ZOOM,
}, },
'score': { 'score-high': {
// "Score-high" represents the full set of data
// at the census block group level. It is only shown
// at high zoom levels to avoid performance issues at lower zooms
'type': 'vector', 'type': 'vector',
// Our current tippecanoe command does not set an id. // Our current tippecanoe command does not set an id.
// The below line promotes the GEOID10 property to the ID // The below line promotes the GEOID10 property to the ID
'promoteId': 'GEOID10', 'promoteId': constants.GEOID_PROPERTY,
'tiles': [ 'tiles': [
`${constants.FEATURE_TILE_BASE_URL}/{z}/{x}/{y}.pbf`, constants.FEATURE_TILE_HIGH_ZOOM_URL,
// For local development, use:
// 'http://localhost:8080/data/tl_2010_bg_with_data/{z}/{x}/{y}.pbf',
], ],
// Seeting maxzoom here enables 'overzooming' // Seeting maxzoom here enables 'overzooming'
// e.g. continued zooming beyond the max bounds. // e.g. continued zooming beyond the max bounds.
// More here: https://docs.mapbox.com/help/glossary/overzoom/ // More here: https://docs.mapbox.com/help/glossary/overzoom/
'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH,
'maxzoom': constants.GLOBAL_MAX_ZOOM_HIGH, 'maxzoom': constants.GLOBAL_MAX_ZOOM_HIGH,
}, },
'score-low': {
// "Score-low" represents a tileset at the level of bucketed tracts.
// census block group information is `dissolve`d into tracts, then
// each tract is `dissolve`d into one of ten buckets. It is meant
// to give us a favorable tradeoff between performance and fidelity.
'type': 'vector',
'promoteId': constants.GEOID_PROPERTY,
'tiles': [
constants.FEATURE_TILE_LOW_ZOOM_URL,
// For local development, use:
// 'http://localhost:8080/data/tl_2010_bg_with_data/{z}/{x}/{y}.pbf',
],
'minzoom': constants.GLOBAL_MIN_ZOOM_LOW,
'maxzoom': constants.GLOBAL_MAX_ZOOM_LOW,
},
'labels': { 'labels': {
'type': 'raster', 'type': 'raster',
'tiles': [ 'tiles': [
'https://cartodb-basemaps-a.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}@2x.png', `https://cartodb-basemaps-a.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}${imageSuffix}.png`,
'https://cartodb-basemaps-b.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}@2x.png', `https://cartodb-basemaps-b.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}${imageSuffix}.png`,
'https://cartodb-basemaps-c.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}@2x.png', `https://cartodb-basemaps-c.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}${imageSuffix}.png`,
'https://cartodb-basemaps-d.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}@2x.png', `https://cartodb-basemaps-d.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}${imageSuffix}.png`,
], ],
}, },
}, },
@ -92,42 +115,60 @@ const mapStyle : Style = {
'id': 'carto', 'id': 'carto',
'source': 'carto', 'source': 'carto',
'type': 'raster', 'type': 'raster',
'minzoom': constants.GLOBAL_MIN_ZOOM - 1, 'minzoom': constants.GLOBAL_MIN_ZOOM,
'maxzoom': constants.GLOBAL_MAX_ZOOM,
}, },
{ {
'id': 'geo', 'id': 'geo',
'source': 'geo', 'source': 'geo',
'type': 'raster', 'type': 'raster',
'minzoom': constants.GLOBAL_MIN_ZOOM - 1,
'layout': { 'layout': {
// Make the layer visible by default. // Make the layer invisible by default.
'visibility': 'none', 'visibility': 'none',
}, },
},
{
'id': 'score',
'source': constants.SCORE_SOURCE_NAME,
'source-layer': constants.SCORE_SOURCE_LAYER,
'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: 0.75,
}),
'minzoom': constants.GLOBAL_MIN_ZOOM, 'minzoom': constants.GLOBAL_MIN_ZOOM,
'maxzoom': constants.GLOBAL_MAX_ZOOM, 'maxzoom': constants.GLOBAL_MAX_ZOOM,
}, },
{ {
'id': constants.HIGH_SCORE_LAYER_NAME,
'source': constants.HIGH_SCORE_SOURCE_NAME,
'source-layer': constants.SCORE_SOURCE_LAYER,
'type': 'fill',
'filter': ['all',
['>', constants.SCORE_PROPERTY_HIGH, constants.SCORE_BOUNDARY_THRESHOLD],
],
'paint': makePaint({
field: constants.SCORE_PROPERTY_HIGH,
minRamp: constants.SCORE_BOUNDARY_LOW,
medRamp: constants.SCORE_BOUNDARY_THRESHOLD,
maxRamp: constants.SCORE_BOUNDARY_PRIORITIZED,
}),
'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH,
},
{
'id': constants.LOW_SCORE_LAYER_NAME,
'source': constants.LOW_SCORE_SOURCE_NAME,
'source-layer': constants.SCORE_SOURCE_LAYER,
'type': 'fill',
'filter': ['all',
['>', constants.SCORE_PROPERTY_LOW, constants.SCORE_BOUNDARY_THRESHOLD],
],
'paint': makePaint({
field: constants.SCORE_PROPERTY_LOW,
minRamp: constants.SCORE_BOUNDARY_LOW,
medRamp: constants.SCORE_BOUNDARY_THRESHOLD,
maxRamp: constants.SCORE_BOUNDARY_PRIORITIZED,
}),
'minzoom': constants.GLOBAL_MIN_ZOOM_LOW,
'maxzoom': constants.GLOBAL_MAX_ZOOM_LOW,
},
{
// "Score-highlights" represents the border
// around given tiles that appears at higher zooms
'id': 'score-highlights', 'id': 'score-highlights',
'source': 'score', 'source': constants.HIGH_SCORE_SOURCE_NAME,
'source-layer': constants.SCORE_SOURCE_LAYER, 'source-layer': constants.SCORE_SOURCE_LAYER,
'type': 'line', 'type': 'line',
'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH,
'layout': { 'layout': {
'visibility': 'visible', 'visibility': 'visible',
'line-join': 'round', 'line-join': 'round',
@ -138,13 +179,15 @@ const mapStyle : Style = {
'line-width': 0.8, 'line-width': 0.8,
'line-opacity': 0.5, 'line-opacity': 0.5,
}, },
'minzoom': constants.GLOBAL_MIN_ZOOM_HIGHLIGHT,
'maxzoom': constants.GLOBAL_MAX_ZOOM_HIGHLIGHT,
}, },
{ {
// This layer queries the feature-state property "selected" and // "score-border-highlight" is used to highlight
// highlights the border of the selected region if true // the currently-selected feature
'id': 'score-border-highlight', 'id': 'score-border-highlight',
'type': 'line', 'type': 'line',
'source': 'score', 'source': constants.HIGH_SCORE_SOURCE_NAME,
'source-layer': constants.SCORE_SOURCE_LAYER, 'source-layer': constants.SCORE_SOURCE_LAYER,
'layout': {}, 'layout': {},
'paint': { 'paint': {
@ -152,16 +195,20 @@ const mapStyle : Style = {
'line-width': [ 'line-width': [
'case', 'case',
['boolean', ['feature-state', 'selected'], false], ['boolean', ['feature-state', 'selected'], false],
5.0, constants.HIGHLIGHT_BORDER_WIDTH,
0, 0,
], ],
}, },
'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH,
'maxzoom': constants.GLOBAL_MAX_ZOOM_HIGH,
}, },
{ {
// We put labels last to ensure prominence
'id': 'labels-only', 'id': 'labels-only',
'type': 'raster', 'type': 'raster',
'source': 'labels', 'source': 'labels',
'minzoom': constants.GLOBAL_MIN_ZOOM, 'minzoom': constants.GLOBAL_MIN_ZOOM,
'maxzoom': constants.GLOBAL_MAX_ZOOM,
}, },
], ],
}; };