Add OS map functionality

- split source/layers between Map*Layers.tsx and getOSBaseMap file
- update getOSBaseMap to return eithe tribal or tracts layers/sources
This commit is contained in:
Vim USDS 2022-08-15 19:00:53 -07:00
parent bac8c85a6c
commit 31a9bf51d5
6 changed files with 156 additions and 21 deletions

View file

@ -27,6 +27,7 @@ import AreaDetail from './AreaDetail';
import MapInfoPanel from './mapInfoPanel'; import MapInfoPanel from './mapInfoPanel';
import MapSearch from './MapSearch'; import MapSearch from './MapSearch';
import MapTractLayers from './MapTractLayers/MapTractLayers'; import MapTractLayers from './MapTractLayers/MapTractLayers';
import MapTribalLayer from './MapTribalLayers/MapTribalLayers';
import LayerSelector from './LayerSelector'; import LayerSelector from './LayerSelector';
import TerritoryFocusControl from './territoryFocusControl'; import TerritoryFocusControl from './territoryFocusControl';
import {getOSBaseMap} from '../data/getOSBaseMap'; import {getOSBaseMap} from '../data/getOSBaseMap';
@ -35,7 +36,6 @@ import {getOSBaseMap} from '../data/getOSBaseMap';
import 'maplibre-gl/dist/maplibre-gl.css'; import 'maplibre-gl/dist/maplibre-gl.css';
import * as constants from '../data/constants'; import * as constants from '../data/constants';
import * as styles from './J40Map.module.scss'; import * as styles from './J40Map.module.scss';
import MapTribalLayer from './MapTribalLayer/MapTribalLayer';
declare global { declare global {
interface Window { interface Window {
Cypress?: object; Cypress?: object;
@ -373,9 +373,8 @@ const J40Map = ({location}: IJ40Interface) => {
data-cy={'reactMapGL'} data-cy={'reactMapGL'}
> >
{/* Load either the Tribal layer or census layer */} {/* Load either the Tribal layer or Census layer depending on the censusSelected state variable */}
{ {
// Todo: may not need both props...
censusSelected ? censusSelected ?
<MapTractLayers <MapTractLayers
selectedFeature={selectedFeature} selectedFeature={selectedFeature}

View file

@ -56,13 +56,28 @@ export const featureURLForTilesetName = (tilesetName: string): string => {
} }
}; };
/**
* This component will return the appropriate source and layers for the census layer on the
* map.
*
* There are two use cases here, eg, when the MapBox token is or isn't provided. When the token
* is not provided, the open-source map will be rendered. When the open-source map is rendered
* 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
* @return {Style}
*/
const MapTractLayers = ({ const MapTractLayers = ({
selectedFeatureId, selectedFeatureId,
selectedFeature, selectedFeature,
}: IMapTractLayers) => { }: IMapTractLayers) => {
const filter = useMemo(() => ['in', constants.GEOID_PROPERTY, selectedFeatureId], [selectedFeature]); const filter = useMemo(() => ['in', constants.GEOID_PROPERTY, selectedFeatureId], [selectedFeature]);
return ( return process.env.MAPBOX_STYLES_READ_TOKEN ? (
// In this case the MapBox token is found and All source(s)/layer(s) are returned.
<> <>
<Source <Source
id={constants.LOW_ZOOM_SOURCE_NAME} id={constants.LOW_ZOOM_SOURCE_NAME}
@ -87,9 +102,7 @@ const MapTractLayers = ({
/> />
</Source> </Source>
{/** {/* The high zoom source */}
* The high zoom source
*/}
<Source <Source
id={constants.HIGH_ZOOM_SOURCE_NAME} id={constants.HIGH_ZOOM_SOURCE_NAME}
type="vector" type="vector"
@ -152,6 +165,34 @@ const MapTractLayers = ({
/> />
</Source> </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 - 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,3 +0,0 @@
import MapTribalLayer from './MapTribalLayer';
export default MapTribalLayer;

View file

@ -24,13 +24,29 @@ export const tribalURL = (): string => {
].join('/'); ].join('/');
}; };
/**
* This component will return the appropriate source and layers for the tribal layer on the
* map.
*
* There are two use cases here, eg, when the MapBox token is or isn't provided. When the token
* is not provided, the open-source map will be rendered. When the open-source map is rendered
* 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
* @return {Style}
*/
const MapTribalLayer = ({ const MapTribalLayer = ({
selectedFeatureId, selectedFeatureId,
selectedFeature, selectedFeature,
}: IMapTribalLayers) => { }: IMapTribalLayers) => {
const tribalSelectionFilter = useMemo(() => ['in', constants.TRIBAL_ID, selectedFeatureId], [selectedFeature]); const tribalSelectionFilter = useMemo(() => ['in', constants.TRIBAL_ID, selectedFeatureId], [selectedFeature]);
return ( return process.env.MAPBOX_STYLES_READ_TOKEN ? (
// In this case the MapBox token is found and ALL source(s)/layer(s) are returned.
<Source <Source
id={constants.TRIBAL_SOURCE_NAME} id={constants.TRIBAL_SOURCE_NAME}
type="vector" type="vector"
@ -44,7 +60,6 @@ const MapTribalLayer = ({
<Layer <Layer
id={constants.TRIBAL_LAYER_ID} id={constants.TRIBAL_LAYER_ID}
source-layer={constants.TRIBAL_SOURCE_LAYER} source-layer={constants.TRIBAL_SOURCE_LAYER}
// filter={['>', constants.SCORE_PROPERTY_LOW, constants.SCORE_BOUNDARY_THRESHOLD]}
type='fill' type='fill'
paint={{ paint={{
'fill-color': constants.TRIBAL_FILL_COLOR, 'fill-color': constants.TRIBAL_FILL_COLOR,
@ -56,7 +71,7 @@ const MapTribalLayer = ({
{/* Tribal layer - controls the border between features */} {/* Tribal layer - controls the border between features */}
<Layer <Layer
id={constants.FEATURE_BORDER_LAYER_ID} id={constants.FEATURE_BORDER_LAYER_ID}
source-layer={constants.SCORE_SOURCE_LAYER} source-layer={constants.TRIBAL_SOURCE_LAYER}
type='line' type='line'
paint={{ paint={{
'line-color': constants.FEATURE_BORDER_COLOR, 'line-color': constants.FEATURE_BORDER_COLOR,
@ -95,8 +110,35 @@ const MapTribalLayer = ({
maxzoom={constants.TRIBAL_MAX_ZOOM} maxzoom={constants.TRIBAL_MAX_ZOOM}
/> />
</Source> </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 */}
<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}
/>
</Source>
); );
}; };
export default MapTribalLayer; export default MapTribalLayer;
//

View file

@ -0,0 +1,3 @@
import MapTribalLayers from './MapTribalLayers';
export default MapTribalLayers;

View file

@ -1,9 +1,11 @@
import {Style} from 'maplibre-gl'; import {Style} from 'maplibre-gl';
import * as constants from '../data/constants';
import {featureURLForTilesetName} from '../components/MapTractLayers/MapTractLayers';
import {tribalURL} from '../components/MapTribalLayer/MapTribalLayer';
// *********** BASE MAP SOURCES *************** import {featureURLForTilesetName} from '../components/MapTractLayers/MapTractLayers';
import {tribalURL} from '../components/MapTribalLayers/MapTribalLayers';
import * as constants from '../data/constants';
// *********** OPEN SOURCE BASE MAP CONSTANTS ***************
const imageSuffix = constants.isMobile ? '' : '@2x'; const imageSuffix = constants.isMobile ? '' : '@2x';
// Original "light" Base layer // Original "light" Base layer
@ -24,9 +26,21 @@ const cartoLightBaseLayer = {
}; };
// Utility function to get OpenSource base maps that are in accordance to JSON spec of MapBox // *********** OPEN SOURCE STATIC MAP STYLES ***************
// https://docs.mapbox.com/mapbox-gl-js/style-spec/ /**
export const getOSBaseMap = (censusSelected: boolean) : Style => { * 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. We currently
* have two sources, either the census tracts or the tribal layer.
* *
* This function returns a Style in accordance to JSON spec of MapBox
* https://docs.mapbox.com/mapbox-gl-js/style-spec/
*
* @param {boolean} censusSelected
* @return {Style}
*/
export const getOSBaseMap = (
censusSelected: boolean,
) : Style => {
return !censusSelected ? { return !censusSelected ? {
/** /**
@ -59,6 +73,12 @@ export const getOSBaseMap = (censusSelected: boolean) : Style => {
'minzoom': constants.TRIBAL_MIN_ZOOM, 'minzoom': constants.TRIBAL_MIN_ZOOM,
'maxzoom': constants.TRIBAL_MAX_ZOOM, 'maxzoom': constants.TRIBAL_MAX_ZOOM,
}, },
// The labels source:
'labels': {
'type': 'raster',
'tiles': cartoLightBaseLayer.labelsOnly,
},
}, },
@ -91,6 +111,39 @@ export const getOSBaseMap = (censusSelected: boolean) : Style => {
'minzoom': constants.TRIBAL_MIN_ZOOM, 'minzoom': constants.TRIBAL_MIN_ZOOM,
'maxzoom': constants.TRIBAL_MAX_ZOOM, 'maxzoom': constants.TRIBAL_MAX_ZOOM,
}, },
/**
* Tribal layer - controls the border between features
*/
{
'id': constants.FEATURE_BORDER_LAYER_ID,
'source': constants.TRIBAL_SOURCE_NAME,
'source-layer': constants.TRIBAL_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.TRIBAL_MIN_ZOOM,
'maxzoom': constants.TRIBAL_MAX_ZOOM,
},
/**
* Alaska layer
*/
{
'id': constants.TRIBAL_ALASKA_POINTS_LAYER_ID,
'source': constants.TRIBAL_SOURCE_NAME,
'source-layer': constants.TRIBAL_SOURCE_LAYER,
'type': 'circle',
'filter': ['==', ['geometry-type'], 'Point'],
'paint': {
'circle-radius': constants.TRIBAL_ALASKA_CIRCLE_RADIUS,
'circle-color': constants.TRIBAL_ALASKA_CIRCLE_FILL_COLOR,
},
'minzoom': constants.TRIBAL_MIN_ZOOM,
'maxzoom': constants.TRIBAL_MAX_ZOOM,
},
], ],
} : } :
{ {