feat: show tribal layer on open-source map

This commit is contained in:
Chris Alfano 2024-12-10 11:35:54 -05:00 committed by Carlos Felix
commit bc9a58f342
12 changed files with 111 additions and 361 deletions

View file

@ -1,5 +1,8 @@
# WARNING:
# THIS FILE IS CHECKED INTO VERSION CONTROL! DO NOT ADD ANY SECRET INFO. IF SECRETS ARE ADDED, PLEASE ADD TO # GIT IGNORE FILE
#
# Create an adjacent local .env file to override any settings
#
# Feature Tiles env variables:
# The TILES_BASE_URL will be determined by the DATA_SOURCE env variable
@ -7,13 +10,14 @@ GATSBY_CDN_TILES_BASE_URL=https://dig0wsohit6js.cloudfront.net
GATSBY_LOCAL_TILES_BASE_URL=http://localhost:5000/data/data-pipeline
GATSBY_DATA_PIPELINE_SCORE_PATH_LOCAL=data_pipeline/data/score
GATSBY_DATA_PIPELINE_TRIBAL_PATH=data-pipeline/data/tribal
GATSBY_DATA_PIPELINE_TRIBAL_PATH_LOCAL=data_pipeline/data/tribal
GATSBY_2_0_TRIBAL_PATH=data-versions/2.0/data/tribal
GATSBY_BETA_SCORE_PATH = data-versions/beta/data/score
GATSBY_2_0_SCORE_PATH = data-versions/2.0/data/score
GATSBY_BETA_SCORE_PATH=data-versions/beta/data/score
GATSBY_2_0_SCORE_PATH=data-versions/2.0/data/score
GATSBY_DATA_PIPELINE_SEARCH_PATH_LOCAL = data_pipeline/data/score/search/tracts.json
GATSBY_2_0_MAP_TRACT_SEARCH_PATH = data-versions/2.0/data/score/search/tracts.json
GATSBY_DATA_PIPELINE_SEARCH_PATH_LOCAL=data_pipeline/data/score/search/tracts.json
GATSBY_2_0_MAP_TRACT_SEARCH_PATH=data-versions/2.0/data/score/search/tracts.json
GATSBY_FILE_DL_PATH_BETA_COMMUNITIES_LIST_XLS=downloadable/beta-communities.xlsx
GATSBY_FILE_DL_PATH_BETA_COMMUNITIES_LIST_CSV=downloadable/beta-communities.csv

View file

@ -1,16 +1,19 @@
# WARNING:
# THIS FILE IS CHECKED INTO VERSION CONTROL! DO NOT ADD ANY SECRET INFO. IF SECRETS ARE ADDED, PLEASE ADD TO # GIT IGNORE FILE
#
# Create an adjacent local .env file to override any settings
#
# Feature Tiles env variables:
# The TILES_BASE_URL will always point to the CDN
GATSBY_CDN_TILES_BASE_URL=https://dig0wsohit6js.cloudfront.net
GATSBY_DATA_PIPELINE_TRIBAL_PATH=data-pipeline/data/tribal
GATSBY_2_0_TRIBAL_PATH=data-versions/2.0/data/tribal
GATSBY_BETA_SCORE_PATH = data-versions/beta/data/score
GATSBY_2_0_SCORE_PATH = data-versions/2.0/data/score
GATSBY_BETA_SCORE_PATH=data-versions/beta/data/score
GATSBY_2_0_SCORE_PATH=data-versions/2.0/data/score
GATSBY_2_0_MAP_TRACT_SEARCH_PATH = data-versions/2.0/data/score/search/tracts.json
GATSBY_2_0_MAP_TRACT_SEARCH_PATH=data-versions/2.0/data/score/search/tracts.json
GATSBY_FILE_DL_PATH_BETA_COMMUNITIES_LIST_XLS=downloadable/beta-communities.xlsx
GATSBY_FILE_DL_PATH_BETA_COMMUNITIES_LIST_CSV=downloadable/beta-communities.csv

View file

@ -1,13 +1,16 @@
require('dotenv').config({
// NODE_ENV is automatically set to
// 'development' when the app is launched via 'npm start' or 'npm develop'
// 'production' when the app is launched via 'npm build'
const dotenv = require('dotenv');
// Depending on the node environment, the app will then use
// .env.production or .env.development for application
// env variables.
path: `.env.${process.env.NODE_ENV}`,
});
// load .env first so any local settings take precedence over environmental defaults loaded next
dotenv.config();
// NODE_ENV is automatically set to
// 'development' when the app is launched via 'npm start' or 'npm develop'
// 'production' when the app is launched via 'npm build'
// Depending on the node environment, the app will then use
// .env.production or .env.development for application
// env variables.
dotenv.config({path: `.env.${process.env.NODE_ENV}`});
module.exports = {
siteMetadata: {

View file

@ -1,6 +1,10 @@
path = require('path');
// https://github.com/maplibre/maplibre-gl-js/issues/83#issuecomment-877012839
// This is require to use react-map-gl 6.x with maplibre
// See: https://github.com/visgl/react-map-gl/blob/v6.1.21/docs/get-started/get-started.md#using-with-a-mapbox-gl-fork
//
// In react-map-gl 7.x this is no longer needed: https://visgl.github.io/react-map-gl/docs/get-started
//
exports.onCreateWebpackConfig = ({stage, loaders, actions}) => {
actions.setWebpackConfig({
devtool: 'eval-source-map',

View file

@ -14093,36 +14093,6 @@
"object-visit": "^1.0.0"
}
},
"mapbox-gl": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.13.2.tgz",
"integrity": "sha512-CPjtWygL+f7naL+sGHoC2JQR0DG7u+9ik6WdkjjVmz2uy0kBC2l+aKfdi3ZzUR7VKSQJ6Mc/CeCN+6iVNah+ww==",
"requires": {
"@mapbox/geojson-rewind": "^0.5.0",
"@mapbox/geojson-types": "^1.0.2",
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
"@mapbox/mapbox-gl-supported": "^1.5.0",
"@mapbox/point-geometry": "^0.1.0",
"@mapbox/tiny-sdf": "^1.1.1",
"@mapbox/unitbezier": "^0.0.0",
"@mapbox/vector-tile": "^1.3.1",
"@mapbox/whoots-js": "^3.1.0",
"csscolorparser": "~1.0.3",
"earcut": "^2.2.2",
"geojson-vt": "^3.2.1",
"gl-matrix": "^3.2.1",
"grid-index": "^1.1.0",
"minimist": "^1.2.5",
"murmurhash-js": "^1.0.0",
"pbf": "^3.2.1",
"potpack": "^1.0.1",
"quickselect": "^2.0.0",
"rw": "^1.3.3",
"supercluster": "^7.1.0",
"tinyqueue": "^2.0.3",
"vt-pbf": "^3.1.1"
}
},
"maplibre-gl": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-1.14.0.tgz",

View file

@ -87,7 +87,6 @@
"gatsby-plugin-robots-txt": "^1.7.0",
"gatsby-plugin-sitemap": "^4.10.0",
"js-search": "^2.0.1",
"mapbox-gl": "^1.13.2",
"maplibre-gl": "^1.14.0",
"query-string": "^7.1.3",
"react": "^17.0.2",

View file

@ -2,7 +2,7 @@
/* eslint-disable no-unused-vars */
// External Libs:
import React, {useRef, useState} from 'react';
import {Map, MapboxGeoJSONFeature, LngLatBoundsLike} from 'maplibre-gl';
import {Map, MapGeoJSONFeature, LngLatBoundsLike} from 'maplibre-gl';
import ReactMapGL, {
MapEvent,
ViewportProps,
@ -28,9 +28,8 @@ import AreaDetail from './AreaDetail';
import MapInfoPanel from './mapInfoPanel';
import MapSearch from './MapSearch';
import MapTractLayers from './MapTractLayers/MapTractLayers';
// import MapTribalLayer from './MapTribalLayers/MapTribalLayers';
import MapTribalLayer from './MapTribalLayers/MapTribalLayers';
import TerritoryFocusControl from './territoryFocusControl';
import {getOSBaseMap} from '../data/getOSBaseMap';
// Styles and constants
import 'maplibre-gl/dist/maplibre-gl.css';
@ -87,7 +86,7 @@ const J40Map = ({location}: IJ40Interface) => {
zoom: zoom && parseFloat(zoom) ? parseFloat(zoom) : constants.GLOBAL_MIN_ZOOM,
});
const [selectedFeature, setSelectedFeature] = useState<MapboxGeoJSONFeature>();
const [selectedFeature, setSelectedFeature] = useState<MapGeoJSONFeature>();
const [detailViewData, setDetailViewData] = useState<IDetailViewInterface>();
const [transitionInProgress, setTransitionInProgress] = useState<boolean>(false);
const [geolocationInProgress, setGeolocationInProgress] = useState<boolean>(false);
@ -303,11 +302,6 @@ const J40Map = ({location}: IJ40Interface) => {
setGeolocationInProgress(true);
};
const mapBoxBaseLayer = {
customColorsWithUpdatedTribal: `mapbox://styles/justice40/cl9g30qh7000p15l9cp1ftw16`,
streetsWithUpdatedTribal: `mapbox://styles/justice40/cl98rlidr002c14obpsvz6zzs`,
};
return (
<>
@ -347,8 +341,11 @@ const J40Map = ({location}: IJ40Interface) => {
// ****** Map state props: ******
// http://visgl.github.io/react-map-gl/docs/api-reference/interactive-map#map-state
{...viewport}
mapStyle={process.env.MAPBOX_STYLES_READ_TOKEN ?
mapBoxBaseLayer.customColorsWithUpdatedTribal : getOSBaseMap()}
mapStyle={
process.env.MAPBOX_STYLES_READ_TOKEN ?
'mapbox://styles/justice40/cl9g30qh7000p15l9cp1ftw16' :
'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json'
}
width="100%"
// Ajusting this height with a conditional statement will not render the map on staging.
// The reason for this issue is unknown. Consider styling the parent container via SASS.
@ -382,6 +379,13 @@ const J40Map = ({location}: IJ40Interface) => {
data-cy={'reactMapGL'}
>
{ /* Tribal layer is baked into Mapbox source,
* only render here if we're not using that
**/
process.env.MAPBOX_STYLES_READ_TOKEN ||
<MapTribalLayer />
}
<MapTractLayers
selectedFeature={selectedFeature}
selectedFeatureId={selectedFeatureId}

View file

@ -1,6 +1,6 @@
import React, {useMemo} from 'react';
import {Source, Layer} from 'react-map-gl';
import {AnyLayer} from 'mapbox-gl';
import {MapGeoJSONFeature} from 'maplibre-gl';
// Contexts:
import {useFlags} from '../../contexts/FlagContext';
@ -9,8 +9,8 @@ import * as constants from '../../data/constants';
import * as COMMON_COPY from '../../data/copy/common';
interface IMapTractLayers {
selectedFeatureId: AnyLayer,
selectedFeature: AnyLayer,
selectedFeatureId: string | number,
selectedFeature: MapGeoJSONFeature | undefined,
}
/**
@ -60,8 +60,8 @@ export const featureURLForTilesetName = (tilesetName: string): string => {
* only the interactive layers are returned from this component. The reason being is that the
* other layers are supplied by he getOSBaseMap function.
*
* @param {AnyLayer} selectedFeatureId
* @param {AnyLayer} selectedFeature
* @param {string | number} selectedFeatureId
* @param {MapGeoJSONFeature | undefined} selectedFeature
* @return {Style}
*/
const MapTractLayers = ({
@ -70,9 +70,7 @@ const MapTractLayers = ({
}: IMapTractLayers) => {
const filter = useMemo(() => ['in', constants.GEOID_PROPERTY, selectedFeatureId], [selectedFeature]);
return process.env.MAPBOX_STYLES_READ_TOKEN ? (
// In this case the MapBox token is found and All source(s)/layer(s) are returned.
return (
<>
<Source
id={constants.LOW_ZOOM_SOURCE_NAME}
@ -160,34 +158,6 @@ const MapTractLayers = ({
/>
</Source>
</>
): (
/**
* In this case the MapBox token is NOT found and ONLY interactive source(s)/layer(s) are returned
* In this case, the other layers (non-interactive) are provided by getOSBaseMap
*/
<Source
id={constants.HIGH_ZOOM_SOURCE_NAME}
type="vector"
promoteId={constants.GEOID_PROPERTY}
tiles={[featureURLForTilesetName('high')]}
maxzoom={constants.GLOBAL_MAX_ZOOM_HIGH}
minzoom={constants.GLOBAL_MIN_ZOOM_HIGH}
>
{/* High zoom layer (dynamic) - border styling around the selected feature */}
<Layer
id={constants.SELECTED_FEATURE_BORDER_LAYER_ID}
source-layer={constants.SCORE_SOURCE_LAYER}
filter={filter} // This filter filters out all other features except the selected feature.
type='line'
paint={{
'line-color': constants.SELECTED_FEATURE_BORDER_COLOR,
'line-width': constants.SELECTED_FEATURE_BORDER_WIDTH,
}}
minzoom={constants.GLOBAL_MIN_ZOOM_HIGH}
/>
</Source>
);
};

View file

@ -1,24 +1,20 @@
import React, {useMemo} from 'react';
import React from 'react';
import {Source, Layer} from 'react-map-gl';
import {AnyLayer} from 'mapbox-gl';
import * as constants from '../../data/constants';
interface IMapTribalLayers {
selectedFeatureId: AnyLayer,
selectedFeature: AnyLayer,
}
/**
* This function will determine the URL for the tribal tiles.
* @return {string}
*/
export const tribalURL = (): string => {
const featureTileBaseURL = constants.TILE_BASE_URL;
const featureTilePath = constants.GATSBY_DATA_PIPELINE_TRIBAL_PATH;
const XYZ_SUFFIX = '{z}/{x}/{y}.pbf';
return [
process.env.GATSBY_CDN_TILES_BASE_URL,
process.env.GATSBY_DATA_PIPELINE_TRIBAL_PATH,
featureTileBaseURL,
featureTilePath,
process.env.GATSBY_MAP_TILES_PATH,
XYZ_SUFFIX,
].join('/');
@ -34,17 +30,12 @@ export const tribalURL = (): string => {
* only the interactive layers are returned from this component. The reason being is that the
* other layers are supplied by he getOSBaseMap function.
*
* @param {AnyLayer} selectedFeatureId
* @param {AnyLayer} selectedFeature
* @param {string | number} selectedFeatureId
* @param {MapGeoJSONFeature | undefined} selectedFeature
* @return {Style}
*/
const MapTribalLayer = ({
selectedFeatureId,
selectedFeature,
}: IMapTribalLayers) => {
const tribalSelectionFilter = useMemo(() => ['in', constants.TRIBAL_ID, selectedFeatureId], [selectedFeature]);
return process.env.MAPBOX_STYLES_READ_TOKEN ? (
const MapTribalLayer = () => {
return (
// In this case the MapBox token is found and ALL source(s)/layer(s) are returned.
<Source
@ -52,8 +43,6 @@ const MapTribalLayer = ({
type="vector"
promoteId={constants.TRIBAL_ID}
tiles={[tribalURL()]}
minzoom={constants.TRIBAL_MIN_ZOOM}
maxzoom={constants.TRIBAL_MAX_ZOOM}
>
{/* Tribal layer */}
@ -82,19 +71,6 @@ const MapTribalLayer = ({
maxzoom={constants.TRIBAL_MAX_ZOOM}
/>
{/* Tribal layer - border styling around the selected feature */}
<Layer
id={constants.SELECTED_TRIBAL_FEATURE_BORDER_LAYER_ID}
source-layer={constants.TRIBAL_SOURCE_LAYER}
filter={tribalSelectionFilter}
type='line'
paint={{
'line-color': constants.SELECTED_FEATURE_BORDER_COLOR,
'line-width': constants.SELECTED_FEATURE_BORDER_WIDTH,
}}
minzoom={constants.TRIBAL_MIN_ZOOM}
/>
{/* Alaska layer */}
<Layer
id={constants.TRIBAL_ALASKA_POINTS_LAYER_ID}
@ -103,38 +79,45 @@ const MapTribalLayer = ({
type='circle'
paint={{
'circle-radius': constants.TRIBAL_ALASKA_CIRCLE_RADIUS,
'circle-color': constants.PRIORITIZED_FEATURE_FILL_COLOR,
'circle-color': constants.TRIBAL_ALASKA_CIRCLE_FILL_COLOR,
'circle-opacity': constants.TRIBAL_FEATURE_FILL_OPACITY,
'circle-stroke-color': constants.TRIBAL_BORDER_COLOR,
'circle-stroke-width': constants.ALAKSA_POINTS_STROKE_WIDTH,
'circle-stroke-opacity': constants.FEATURE_BORDER_OPACITY,
}}
minzoom={constants.TRIBAL_MIN_ZOOM}
maxzoom={constants.TRIBAL_MAX_ZOOM}
minzoom={constants.ALASKA_MIN_ZOOM}
maxzoom={constants.ALASKA_MAX_ZOOM}
/>
</Source>
) : (
/**
* In this case the MapBox token is NOT found and ONLY INTERACTIVE source(s)/layer(s) are returned.
* In this case, the other layers (non-interactive) are provided by getOSBaseMap
*/
<Source
id={constants.TRIBAL_SOURCE_NAME}
type="vector"
promoteId={constants.TRIBAL_ID}
tiles={[tribalURL()]}
minzoom={constants.TRIBAL_MIN_ZOOM}
maxzoom={constants.TRIBAL_MAX_ZOOM}
>
{/* Tribal layer - border styling around the selected feature */}
{/* Tribal labels layer */}
<Layer
id={constants.SELECTED_TRIBAL_FEATURE_BORDER_LAYER_ID}
id={constants.TRIBAL_LABELS_LAYER_ID}
source-layer={constants.TRIBAL_SOURCE_LAYER}
filter={tribalSelectionFilter}
type='line'
type='symbol'
layout={{
'text-field': [
'case',
['in', ' LAR', ['get', constants.LAND_AREA_NAME]],
['slice', ['get', constants.LAND_AREA_NAME], 0, ['-', ['length', ['get', constants.LAND_AREA_NAME]], 4]],
['in', ' IRA', ['get', constants.LAND_AREA_NAME]],
['slice', ['get', constants.LAND_AREA_NAME], 0, ['-', ['length', ['get', constants.LAND_AREA_NAME]], 4]],
['in', ' TSA', ['get', constants.LAND_AREA_NAME]],
['slice', ['get', constants.LAND_AREA_NAME], 0, ['-', ['length', ['get', constants.LAND_AREA_NAME]], 4]],
['get', constants.LAND_AREA_NAME],
],
'text-anchor': 'top',
'text-offset': [0, 1],
'text-size': 12,
'text-allow-overlap': false,
'text-ignore-placement': false,
}}
paint={{
'line-color': constants.SELECTED_FEATURE_BORDER_COLOR,
'line-width': constants.SELECTED_FEATURE_BORDER_WIDTH,
'text-color': '#333333',
'text-halo-color': '#FFFFFF',
'text-halo-width': 1.5,
}}
minzoom={constants.TRIBAL_MIN_ZOOM}
maxzoom={constants.TRIBAL_MAX_ZOOM}
/>
</Source>
);

View file

@ -261,10 +261,10 @@ export const LOW_ZOOM_LAYER_ID = "low-zoom-layer-id";
export const FEATURE_BORDER_LAYER_ID = "feature-border-layer-id";
export const SELECTED_FEATURE_BORDER_LAYER_ID =
"selected-feature-border-layer-id";
export const TRIBAL_LAYER_ID = "tribal-layer-id";
export const SELECTED_TRIBAL_FEATURE_BORDER_LAYER_ID =
"selected-feature-tribal-border-layer-id";
export const TRIBAL_ALASKA_POINTS_LAYER_ID = "tribal-alaska-points-layer-id";
export const TRIBAL_LABELS_LAYER_ID = "tribal-labels-layer-id";
// Used in layer filters:
export const SCORE_PROPERTY_LOW = "SCORE";
@ -282,8 +282,10 @@ export const GLOBAL_MAX_ZOOM_HIGH = 11;
export const GLOBAL_MIN_ZOOM_FEATURE_BORDER = 5;
export const GLOBAL_MAX_ZOOM_FEATURE_BORDER = 22;
export const TRIBAL_MIN_ZOOM = 3;
export const TRIBAL_MIN_ZOOM = 6.6;
export const TRIBAL_MAX_ZOOM = 22;
export const ALASKA_MIN_ZOOM = 3;
export const ALASKA_MAX_ZOOM = 22;
// Opacity
export const FEATURE_BORDER_OPACITY = 0.5;
@ -297,7 +299,7 @@ export const FEATURE_BORDER_COLOR = "#4EA5CF";
export const SELECTED_FEATURE_BORDER_COLOR = "#1A4480";
export const PRIORITIZED_FEATURE_FILL_COLOR = "#768FB3";
export const TRIBAL_BORDER_COLOR = "##4EA5CF";
export const TRIBAL_BORDER_COLOR = "#4EA5CF";
export const SELECTED_TRIBAL_BORDER_COLOR = "#1A4480";
export const TRIBAL_FILL_COLOR = "#768FB3";
export const TRIBAL_ALASKA_CIRCLE_FILL_COLOR = "#768FB3";
@ -395,3 +397,7 @@ process.env.GATSBY_2_0_SCORE_PATH;
export const MAP_TRACT_SEARCH_PATH = process.env.DATA_SOURCE === "local" ?
process.env.GATSBY_DATA_PIPELINE_SEARCH_PATH_LOCAL :
process.env.GATSBY_2_0_MAP_TRACT_SEARCH_PATH;
export const GATSBY_DATA_PIPELINE_TRIBAL_PATH = process.env.DATA_SOURCE === "local" ?
process.env.GATSBY_DATA_PIPELINE_TRIBAL_PATH_LOCAL :
process.env.GATSBY_2_0_TRIBAL_PATH;

View file

@ -1,196 +0,0 @@
import {Style} from 'maplibre-gl';
import {featureURLForTilesetName} from '../components/MapTractLayers/MapTractLayers';
import * as constants from '../data/constants';
// *********** OPEN SOURCE BASE MAP CONSTANTS ***************
const imageSuffix = constants.isMobile ? '' : '@2x';
// Original "light" Base layer
// Additional layers found here: https://carto.com/help/building-maps/basemap-list/#carto-vector-basemaps
const cartoLightBaseLayer = {
noLabels: [
`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`,
],
labelsOnly: [
`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`,
],
};
// *********** OPEN SOURCE STATIC MAP STYLES ***************
/**
* This function will be called when there is no MapBox token found. This function will
* return the open source base map along with styles for the chosen source.
* *
* This function returns a Style in accordance to JSON spec of MapBox
* https://docs.mapbox.com/mapbox-gl-js/style-spec/
*
* @return {Style}
*/
export const getOSBaseMap = (): Style => {
return {
'version': 8,
/**
* Census Tract Source
* */
'sources': {
/**
* The base map source source allows us to define where the tiles can be fetched from.
*/
[constants.BASE_MAP_SOURCE_NAME]: {
'type': 'raster',
'tiles': cartoLightBaseLayer.noLabels,
'minzoom': constants.GLOBAL_MIN_ZOOM,
'maxzoom': constants.GLOBAL_MAX_ZOOM,
},
// The High zoom source:
[constants.HIGH_ZOOM_SOURCE_NAME]: {
// 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': constants.GEOID_PROPERTY,
'tiles': [featureURLForTilesetName('high')],
// Setting 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,
},
// The Low zoom source:
[constants.LOW_ZOOM_SOURCE_NAME]: {
// "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': [featureURLForTilesetName('low')],
'minzoom': constants.GLOBAL_MIN_ZOOM_LOW,
'maxzoom': constants.GLOBAL_MAX_ZOOM_LOW,
},
// The labels source:
'labels': {
'type': 'raster',
'tiles': cartoLightBaseLayer.labelsOnly,
},
},
/**
* Each object in the layers array references it's source via the source key.
*/
'layers': [
// The baseMapLayer
{
'id': constants.BASE_MAP_LAYER_ID,
'source': constants.BASE_MAP_SOURCE_NAME,
'type': 'raster',
'minzoom': constants.GLOBAL_MIN_ZOOM,
'maxzoom': constants.GLOBAL_MAX_ZOOM,
},
// A layer for labels only
{
'id': 'labels-only-layer',
'source': 'labels',
'type': 'raster',
'layout': {
'visibility': 'visible',
},
'minzoom': constants.GLOBAL_MIN_ZOOM,
'maxzoom': constants.GLOBAL_MAX_ZOOM,
},
// Low zoom layer (static) - prioritized features only
{
'id': constants.LOW_ZOOM_LAYER_ID,
'source': constants.LOW_ZOOM_SOURCE_NAME,
'source-layer': constants.SCORE_SOURCE_LAYER,
/**
* This shows features where the low score > score boundary threshold.
* In other words, this filter out non-prioritized features
*/
'filter': ['all',
['>', constants.SCORE_PROPERTY_LOW, constants.SCORE_BOUNDARY_THRESHOLD],
],
'type': 'fill',
'paint': {
'fill-color': constants.PRIORITIZED_FEATURE_FILL_COLOR,
'fill-opacity': constants.LOW_ZOOM_PRIORITIZED_FEATURE_FILL_OPACITY,
},
'minzoom': constants.GLOBAL_MIN_ZOOM_LOW,
'maxzoom': constants.GLOBAL_MAX_ZOOM_LOW,
},
// High zoom layer (static) - non-prioritized features only
{
'id': constants.HIGH_ZOOM_LAYER_ID,
'source': constants.HIGH_ZOOM_SOURCE_NAME,
'source-layer': constants.SCORE_SOURCE_LAYER,
/**
* The SCORE_PROPERTY_HIGH is a boolean value. True for
* prioritized and false for non-priorirized
*/
'filter': ['all',
['==', constants.SCORE_PROPERTY_HIGH, false],
],
'type': 'fill',
'paint': {
'fill-opacity': constants.NON_PRIORITIZED_FEATURE_FILL_OPACITY,
},
'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH,
},
// High zoom layer (static) - prioritized features only
{
'id': constants.PRIORITIZED_HIGH_ZOOM_LAYER_ID,
'source': constants.HIGH_ZOOM_SOURCE_NAME,
'source-layer': constants.SCORE_SOURCE_LAYER,
/**
* The SCORE_PROPERTY_HIGH is a boolean value. True for
* prioritized and false for non-priorirized
*/
'filter': ['all',
['==', constants.SCORE_PROPERTY_HIGH, true],
],
'type': 'fill',
'paint': {
'fill-color': constants.PRIORITIZED_FEATURE_FILL_COLOR,
'fill-opacity': constants.HIGH_ZOOM_PRIORITIZED_FEATURE_FILL_OPACITY,
},
'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH,
},
// High zoom layer (static) - controls the border between features
{
'id': constants.FEATURE_BORDER_LAYER_ID,
'source': constants.HIGH_ZOOM_SOURCE_NAME,
'source-layer': constants.SCORE_SOURCE_LAYER,
'type': 'line',
'paint': {
'line-color': constants.FEATURE_BORDER_COLOR,
'line-width': constants.FEATURE_BORDER_WIDTH,
'line-opacity': constants.FEATURE_BORDER_OPACITY,
},
'minzoom': constants.GLOBAL_MIN_ZOOM_FEATURE_BORDER,
'maxzoom': constants.GLOBAL_MAX_ZOOM_FEATURE_BORDER,
},
],
};
};

View file

@ -1,6 +1,6 @@
// other CSS libraries:
@import "~@trussworks/react-uswds/lib/index.css";
@import "../../node_modules/mapbox-gl/dist/mapbox-gl.css";
@import "../../node_modules/maplibre-gl/dist/maplibre-gl.css";
/*
According to the fundamental usage of USWDS: