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
parent 842312f69f
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(
{
'id': id,
'source': constants.SCORE_SOURCE_NAME,
'source': constants.HIGH_SCORE_SOURCE_NAME,
'sourceLayer': constants.SCORE_SOURCE_LAYER,
},
);

View file

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

View file

@ -1,13 +1,22 @@
// 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
export const PERFORMANCE_MARKER_MAP_IDLE = 'MAP_IDLE';
// 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 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
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_MAX_ZOOM = 22;
export const GLOBAL_MIN_ZOOM_LOW = 3;
export const GLOBAL_MAX_ZOOM_LOW = 9;
export const GLOBAL_MIN_ZOOM_HIGH = 9;
export const GLOBAL_MAX_ZOOM_LOW = 7;
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;
// Bounds
@ -74,3 +85,13 @@ export const MIN_COLOR = '#FFFFFF';
export const MED_COLOR = '#D1DAE6';
export const MAX_COLOR = '#768FB3';
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;
}
const imageSuffix = constants.isMobile ? '' : '@2x';
const mapStyle : Style = {
'version': 8,
'sources': {
'carto': {
'type': 'raster',
'tiles': [
'https://a.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
'https://b.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
'https://c.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
'https://d.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
'tiles':
[
`https://a.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}${imageSuffix}.png`,
`https://b.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}${imageSuffix}.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': {
'type': 'raster',
'tiles': [
'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',
// Our current tippecanoe command does not set an id.
// The below line promotes the GEOID10 property to the ID
'promoteId': 'GEOID10',
'promoteId': constants.GEOID_PROPERTY,
'tiles': [
`${constants.FEATURE_TILE_BASE_URL}/{z}/{x}/{y}.pbf`,
// For local development, use:
// 'http://localhost:8080/data/tl_2010_bg_with_data/{z}/{x}/{y}.pbf',
constants.FEATURE_TILE_HIGH_ZOOM_URL,
],
// Seeting maxzoom here enables 'overzooming'
// e.g. continued zooming beyond the max bounds.
// More here: https://docs.mapbox.com/help/glossary/overzoom/
'minzoom': constants.GLOBAL_MIN_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': {
'type': 'raster',
'tiles': [
'https://cartodb-basemaps-a.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}@2x.png',
'https://cartodb-basemaps-c.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}@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}${imageSuffix}.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}${imageSuffix}.png`,
],
},
},
@ -92,42 +115,60 @@ const mapStyle : Style = {
'id': 'carto',
'source': 'carto',
'type': 'raster',
'minzoom': constants.GLOBAL_MIN_ZOOM - 1,
'minzoom': constants.GLOBAL_MIN_ZOOM,
'maxzoom': constants.GLOBAL_MAX_ZOOM,
},
{
'id': 'geo',
'source': 'geo',
'type': 'raster',
'minzoom': constants.GLOBAL_MIN_ZOOM - 1,
'layout': {
// Make the layer visible by default.
// Make the layer invisible by default.
'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,
'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',
'source': 'score',
'source': constants.HIGH_SCORE_SOURCE_NAME,
'source-layer': constants.SCORE_SOURCE_LAYER,
'type': 'line',
'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH,
'layout': {
'visibility': 'visible',
'line-join': 'round',
@ -138,13 +179,15 @@ const mapStyle : Style = {
'line-width': 0.8,
'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
// highlights the border of the selected region if true
// "score-border-highlight" is used to highlight
// the currently-selected feature
'id': 'score-border-highlight',
'type': 'line',
'source': 'score',
'source': constants.HIGH_SCORE_SOURCE_NAME,
'source-layer': constants.SCORE_SOURCE_LAYER,
'layout': {},
'paint': {
@ -152,16 +195,20 @@ const mapStyle : Style = {
'line-width': [
'case',
['boolean', ['feature-state', 'selected'], false],
5.0,
constants.HIGHLIGHT_BORDER_WIDTH,
0,
],
},
'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH,
'maxzoom': constants.GLOBAL_MAX_ZOOM_HIGH,
},
{
// We put labels last to ensure prominence
'id': 'labels-only',
'type': 'raster',
'source': 'labels',
'minzoom': constants.GLOBAL_MIN_ZOOM,
'maxzoom': constants.GLOBAL_MAX_ZOOM,
},
],
};