diff --git a/.github/workflows/deploy_main.yml b/.github/workflows/deploy_main.yml index 171404a7..1d821a69 100644 --- a/.github/workflows/deploy_main.yml +++ b/.github/workflows/deploy_main.yml @@ -39,6 +39,7 @@ jobs: DATA_SOURCE: cdn # TODO: Update main URL when either is back up SITE_URL: https://d29zfl8cj7y1zf.cloudfront.net/ + MAPBOX_STYLES_READ_TOKEN: "${{ secrets.MAPBOX_STYLES_READ_TOKEN }}" - name: Get directory contents run: ls -la public - name: Lint diff --git a/.github/workflows/deploy_staging.yml b/.github/workflows/deploy_staging.yml index 714f293a..586cd24b 100644 --- a/.github/workflows/deploy_staging.yml +++ b/.github/workflows/deploy_staging.yml @@ -38,6 +38,7 @@ jobs: DATA_SOURCE: cdn SITE_URL: "http://usds-geoplatform-justice40-website.s3-website-us-east-1.amazonaws.com/" PATH_PREFIX: "/justice40-tool/${{env.DESTINATION_FOLDER}}" + MAPBOX_STYLES_READ_TOKEN: "${{ secrets.MAPBOX_STYLES_READ_TOKEN }}" - name: Get directory contents run: ls -la public - name: Lint diff --git a/client/.env.development b/client/.env.development index c4001afd..d121e51f 100644 --- a/client/.env.development +++ b/client/.env.development @@ -12,4 +12,7 @@ GATSBY_DATA_PIPELINE_SCORE_PATH=data-pipeline/data/score GATSBY_SCORE_DOWNLOAD_FILE_PATH=downloadable/Screening_Tool_Data.zip GATSBY_MAP_TILES_PATH=tiles -GATSBY_MAPBOX_STYLES_READ_TOKEN=pk.eyJ1IjoianVzdGljZTQwIiwiYSI6ImNreHRub2QxdTV6dnUzMHBmZDdzZXQ4YWMifQ.Fc-my99OtAwP5zEXCgrx_g \ No newline at end of file +# If you want the map to render a MapBox base map (as opposed to the +# open source one from CartoDB), please create your own API TOKEN from +# your MapBox account and add the token here: +# MAPBOX_STYLES_READ_TOKEN='' \ No newline at end of file diff --git a/client/.env.production b/client/.env.production index 5080f265..69f9b1cf 100644 --- a/client/.env.production +++ b/client/.env.production @@ -8,5 +8,3 @@ GATSBY_CDN_TILES_BASE_URL=https://d3jqyw10j8e7p9.cloudfront.net GATSBY_DATA_PIPELINE_SCORE_PATH=data-pipeline/data/score GATSBY_SCORE_DOWNLOAD_FILE_PATH=downloadable/Screening_Tool_Data.zip GATSBY_MAP_TILES_PATH=tiles - -GATSBY_MAPBOX_STYLES_READ_TOKEN=pk.eyJ1IjoianVzdGljZTQwIiwiYSI6ImNreHRub2QxdTV6dnUzMHBmZDdzZXQ4YWMifQ.Fc-my99OtAwP5zEXCgrx_g \ No newline at end of file diff --git a/client/.gitignore b/client/.gitignore index d9f4ee1a..faf790bb 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -5,6 +5,4 @@ public cypress/screenshots/ cypress/videos/ .DS_Store -coverage -.env.development -.env.production \ No newline at end of file +coverage \ No newline at end of file diff --git a/client/gatsby-config.js b/client/gatsby-config.js index d30a0c86..0d6499e6 100644 --- a/client/gatsby-config.js +++ b/client/gatsby-config.js @@ -93,7 +93,7 @@ module.exports = { { resolve: `gatsby-plugin-env-variables`, options: { - allowList: ['DATA_SOURCE'], + allowList: ['DATA_SOURCE', 'MAPBOX_STYLES_READ_TOKEN'], }, }, { diff --git a/client/src/components/J40Map.tsx b/client/src/components/J40Map.tsx index 2bf9d328..2c005ea6 100644 --- a/client/src/components/J40Map.tsx +++ b/client/src/components/J40Map.tsx @@ -29,7 +29,7 @@ import MapSearch from './MapSearch'; import TerritoryFocusControl from './territoryFocusControl'; // Styles and constants -// import {makeMapStyle} from '../data/mapStyle'; +import {getOSBaseMap} from '../data/getOSBaseMap'; import 'maplibre-gl/dist/maplibre-gl.css'; import * as constants from '../data/constants'; import * as styles from './J40Map.module.scss'; @@ -258,14 +258,18 @@ const J40Map = ({location}: IJ40Interface) => { { + return { + 'version': 8, + + /** + * Map Sources + * */ + '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 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, + }, + ], + }; +}; + diff --git a/client/src/data/mapStyle.tsx b/client/src/data/mapStyle.tsx index 54842506..b9c4585d 100644 --- a/client/src/data/mapStyle.tsx +++ b/client/src/data/mapStyle.tsx @@ -1,6 +1,8 @@ import {Style} from 'maplibre-gl'; import * as constants from '../data/constants'; -import {FlagContainer} from '../contexts/FlagContext'; + +// This file is no longer used, however keeping in case we have to revert to explicit styling. There was a +// gradient function that in this file's commit history which could prove useful in the future. // *********** BASE MAP SOURCES *************** const imageSuffix = constants.isMobile ? '' : '@2x'; @@ -22,33 +24,10 @@ const cartoLightBaseLayer = { ], }; -// MapTiler base map source -// Todo: move API key to .env -const getMapTilerBaseLayer = (name:string, API_KEY='KMA4bawPDNtR6zNIAfUH') => { - return [ - `https://api.maptiler.com/maps/${name}/{z}/{x}/{y}${imageSuffix}.png?key=${API_KEY}`, - ]; -}; // Utility function to make map styles according to JSON spec of MapBox // https://docs.mapbox.com/mapbox-gl-js/style-spec/ -export const makeMapStyle = (flagContainer: FlagContainer) : Style => { - // Add flags for various types of MapTiler base maps: - const getBaseMapLayer = () => { - if ('mt-streets' in flagContainer) { - return getMapTilerBaseLayer('streets'); - } else if ('mt-bright' in flagContainer) { - return getMapTilerBaseLayer('bright'); - } else if ('mt-voyager' in flagContainer) { - return getMapTilerBaseLayer('voyager'); - } else if ('mt-osm' in flagContainer) { - return getMapTilerBaseLayer('osm-standard'); - } else { - return cartoLightBaseLayer.noLabels; - }; - }; - - +export const makeMapStyle = () : Style => { return { 'version': 8, @@ -70,7 +49,7 @@ export const makeMapStyle = (flagContainer: FlagContainer) : Style => { */ [constants.BASE_MAP_SOURCE_NAME]: { 'type': 'raster', - 'tiles': getBaseMapLayer(), + 'tiles': cartoLightBaseLayer.noLabels, /** * Attempting to place a direct call to mapbox URL: @@ -101,52 +80,52 @@ export const makeMapStyle = (flagContainer: FlagContainer) : Style => { }, // In the layer (below) where the geo source is used, the layer is invisible - '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, - }, + // '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, + // }, // 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': [ - 'high_tiles' in flagContainer ? - constants.featureURLForTilesetName(flagContainer['high_tiles']) : - constants.FEATURE_TILE_HIGH_ZOOM_URL, - ], - // 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, - }, + // [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': [ + // 'high_tiles' in flagContainer ? + // constants.featureURLForTilesetName(flagContainer['high_tiles']) : + // constants.FEATURE_TILE_HIGH_ZOOM_URL, + // ], + // // 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': [ - 'low_tiles' in flagContainer ? - constants.featureURLForTilesetName(flagContainer['low_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, - }, + // [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': [ + // 'low_tiles' in flagContainer ? + // constants.featureURLForTilesetName(flagContainer['low_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, + // }, // The labels source: 'labels': { @@ -177,87 +156,87 @@ export const makeMapStyle = (flagContainer: FlagContainer) : Style => { }, // The Geo layer adds a geographical layer like mountains and rivers - { - 'id': 'geo', - 'source': 'geo', - 'type': 'raster', - 'layout': { - // Place visibility behind flag: - 'visibility': 'geo' in flagContainer ? 'visible' : 'none', - }, - 'minzoom': constants.GLOBAL_MIN_ZOOM, - 'maxzoom': constants.GLOBAL_MAX_ZOOM, - }, + // { + // 'id': 'geo', + // 'source': 'geo', + // 'type': 'raster', + // 'layout': { + // // Place visibility behind flag: + // 'visibility': 'geo' in flagContainer ? 'visible' : 'none', + // }, + // 'minzoom': constants.GLOBAL_MIN_ZOOM, + // 'maxzoom': constants.GLOBAL_MAX_ZOOM, + // }, /** * High zoom layer - non-prioritized features only */ - { - 'id': constants.HIGH_ZOOM_LAYER_ID, - 'source': constants.HIGH_ZOOM_SOURCE_NAME, - 'source-layer': constants.SCORE_SOURCE_LAYER, - /** - * This shows features where the high score < score boundary threshold. - * In other words, this filter out prioritized features - */ - 'filter': ['all', - ['<', constants.SCORE_PROPERTY_HIGH, constants.SCORE_BOUNDARY_THRESHOLD], - ], + // { + // 'id': constants.HIGH_ZOOM_LAYER_ID, + // 'source': constants.HIGH_ZOOM_SOURCE_NAME, + // 'source-layer': constants.SCORE_SOURCE_LAYER, + // /** + // * This shows features where the high score < score boundary threshold. + // * In other words, this filter out prioritized features + // */ + // 'filter': ['all', + // ['<', constants.SCORE_PROPERTY_HIGH, constants.SCORE_BOUNDARY_THRESHOLD], + // ], - 'type': 'fill', - 'paint': { - 'fill-opacity': constants.NON_PRIORITIZED_FEATURE_FILL_OPACITY, - }, - 'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH, - }, + // 'type': 'fill', + // 'paint': { + // 'fill-opacity': constants.NON_PRIORITIZED_FEATURE_FILL_OPACITY, + // }, + // 'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH, + // }, /** * High zoom layer - prioritized features only */ - { - 'id': constants.PRIORITIZED_HIGH_ZOOM_LAYER_ID, - 'source': constants.HIGH_ZOOM_SOURCE_NAME, - 'source-layer': constants.SCORE_SOURCE_LAYER, - /** - * This shows features where the high score > score boundary threshold. - * In other words, this filter out non-prioritized features - */ - 'filter': ['all', - ['>', constants.SCORE_PROPERTY_HIGH, constants.SCORE_BOUNDARY_THRESHOLD], - ], + // { + // 'id': constants.PRIORITIZED_HIGH_ZOOM_LAYER_ID, + // 'source': constants.HIGH_ZOOM_SOURCE_NAME, + // 'source-layer': constants.SCORE_SOURCE_LAYER, + // /** + // * This shows features where the high score > score boundary threshold. + // * In other words, this filter out non-prioritized features + // */ + // 'filter': ['all', + // ['>', constants.SCORE_PROPERTY_HIGH, constants.SCORE_BOUNDARY_THRESHOLD], + // ], - 'type': 'fill', - 'paint': { - 'fill-color': constants.PRIORITIZED_FEATURE_FILL_COLOR, - 'fill-opacity': constants.PRIORITIZED_FEATURE_FILL_OPACITY, - }, - 'minzoom': constants.GLOBAL_MIN_ZOOM_HIGH, - }, + // '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, + // }, /** * Low zoom layer - 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], - ], + // { + // '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.PRIORITIZED_FEATURE_FILL_OPACITY, - }, - 'minzoom': constants.GLOBAL_MIN_ZOOM_LOW, - 'maxzoom': constants.GLOBAL_MAX_ZOOM_LOW, - }, + // '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, + // }, // A layer for labels only { @@ -265,7 +244,7 @@ export const makeMapStyle = (flagContainer: FlagContainer) : Style => { 'source': 'labels', 'type': 'raster', 'layout': { - 'visibility': 'remove-label-layer' in flagContainer ? 'none' : 'visible', + 'visibility': 'visible', }, 'minzoom': constants.GLOBAL_MIN_ZOOM, 'maxzoom': constants.GLOBAL_MAX_ZOOM, diff --git a/docker-compose.yml b/docker-compose.yml index 5ea93d05..9e5d6c22 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,6 +34,11 @@ services: # See the client readme for more info on environment variables: # https://github.com/usds/justice40-tool/blob/main/client/README.md DATA_SOURCE: local + + # If you want the map to render a MapBox base map (as opposed to the + # open source one from CartoDB), please create your own API TOKEN from + # your MapBox account and add the token here: + MAPBOX_STYLES_READ_TOKEN: "" volumes: - ./client/src:/client/src ports: