diff --git a/nextjs/src/app/reports/[id]/(components)/MapLayers/PoliticalChoropleths.tsx b/nextjs/src/app/reports/[id]/(components)/MapLayers/PoliticalChoropleths.tsx index a9abeae9b..fae5391a7 100644 --- a/nextjs/src/app/reports/[id]/(components)/MapLayers/PoliticalChoropleths.tsx +++ b/nextjs/src/app/reports/[id]/(components)/MapLayers/PoliticalChoropleths.tsx @@ -112,9 +112,20 @@ const PoliticalChoropleths: React.FC = ({ visibility, }} paint={{ + 'text-opacity': [ + 'interpolate', + ['exponential', 1], + ['zoom'], + // + 7.5, + 0, + // + 7.8, + 1, + ], 'text-color': 'white', - 'text-halo-color': 'black', - 'text-halo-width': 0.3, + 'text-halo-color': '#24262b', + 'text-halo-width': 1.5, }} /> = ({ }} paint={{ 'text-color': 'white', - 'text-opacity': 0.9, - 'text-halo-color': 'black', - 'text-halo-width': 0.3, + 'text-opacity': [ + 'interpolate', + ['exponential', 1], + ['zoom'], + // + 6.5, + 0, + // + 7, + 1, + ], + 'text-halo-color': '#24262b', + 'text-halo-width': 1.5, }} /> diff --git a/nextjs/src/app/reports/[id]/addCountByGssToMapboxLayer.ts b/nextjs/src/app/reports/[id]/addCountByGssToMapboxLayer.ts index 087ff0a0b..4f3853d1e 100644 --- a/nextjs/src/app/reports/[id]/addCountByGssToMapboxLayer.ts +++ b/nextjs/src/app/reports/[id]/addCountByGssToMapboxLayer.ts @@ -1,4 +1,3 @@ -import { GroupedDataCount } from '@/__generated__/graphql' import { MAPBOX_LOAD_INTERVAL } from '@/lib/map/useLoadedMap' import { MapRef } from 'react-map-gl' @@ -6,7 +5,7 @@ import { MapRef } from 'react-map-gl' // used in the UK to reference geographic areas for statistical purposes. // The data prop needs to contain the gss code and the count export function addCountByGssToMapboxLayer( - data: GroupedDataCount[], + data: { gss?: string | null; count: number }[], mapboxSourceId: string, sourceLayerId?: string, mapbox?: MapRef | null diff --git a/nextjs/src/app/reports/[id]/mapboxStyles.ts b/nextjs/src/app/reports/[id]/mapboxStyles.ts index eaf1cc52e..98059fd26 100644 --- a/nextjs/src/app/reports/[id]/mapboxStyles.ts +++ b/nextjs/src/app/reports/[id]/mapboxStyles.ts @@ -1,4 +1,3 @@ -import { GroupedDataCount } from '@/__generated__/graphql' import { scaleLinear, scaleSequential } from 'd3-scale' import { interpolateBlues } from 'd3-scale-chromatic' import { @@ -7,6 +6,7 @@ import { SymbolLayerSpecification, } from 'mapbox-gl' import { Tileset } from './types' +import { BoundaryAnalytics } from './useBoundaryAnalytics' export function getChoroplethFill( data: { count: number }[] @@ -39,6 +39,7 @@ export function getChoroplethFill( const colourScale = scaleSequential() .domain([min, max]) .interpolator(interpolateBlues) + .interpolator((t) => interpolateBlues(1 - t)) let steps = Math.min(max, 30) // Max 30 steps steps = Math.max(steps, 3) // Min 3 steps (for valid Mapbox fill-color rule) @@ -65,17 +66,28 @@ export function getChoroplethFill( export function getChoroplethEdge(): LineLayerSpecification['paint'] { return { 'line-color': 'white', - 'line-gap-width': [ + 'line-opacity': [ 'interpolate', ['exponential', 1], ['zoom'], + // 8, - 0, + 0.3, + // 12, - 3, + 1, + ], + 'line-width': [ + 'interpolate', + ['exponential', 1], + ['zoom'], + // + 8, + 0.3, + // + 12, + 2, ], - 'line-opacity': 0.5, - 'line-width': ['interpolate', ['exponential', 1], ['zoom'], 8, 0.1, 12, 1], } } @@ -86,12 +98,13 @@ export function getSelectedChoroplethEdge(): LineLayerSpecification['paint'] { } } export const getChoroplethFillFilter = ( - data: GroupedDataCount[], + data: BoundaryAnalytics, tileset: Tileset ) => { return [ 'in', ['get', tileset.promoteId], + // @ts-ignore ['literal', data.map((d) => d.gss || '')], ] } @@ -103,11 +116,13 @@ export const getSelectedChoroplethFillFilter = ( return ['==', ['get', tileset.promoteId], selectedGss] } -export function getAreaGeoJSON(data: GroupedDataCount[]) { +export function getAreaGeoJSON(data: BoundaryAnalytics) { return { type: 'FeatureCollection', features: data + // @ts-ignore .filter((d) => d.gssArea?.point?.geometry) + // @ts-ignore .map((d) => ({ type: 'Feature', geometry: d.gssArea?.point?.geometry! as GeoJSON.Point, @@ -119,14 +134,16 @@ export function getAreaGeoJSON(data: GroupedDataCount[]) { } } -function getStatsForData(data: GroupedDataCount[]) { +function getStatsForData(data: BoundaryAnalytics) { let min = data.reduce( + // @ts-ignore (min, p) => (p?.count! < min ? p?.count! : min), data?.[0]?.count! ) || 0 let max = data.reduce( + // @ts-ignore (max, p) => (p?.count! > max ? p?.count! : max), data?.[0]?.count! ) || 1 @@ -146,7 +163,7 @@ function getStatsForData(data: GroupedDataCount[]) { } export const getAreaCountLayout = ( - data: GroupedDataCount[] + data: BoundaryAnalytics ): SymbolLayerSpecification['layout'] => { const { min, max, textScale } = getStatsForData(data) @@ -156,14 +173,30 @@ export const getAreaCountLayout = ( 'text-size': [ 'interpolate', ['linear'], - ['get', 'count'], - min, - textScale(min) * 17, - max, - textScale(max) * 17, + ['zoom'], + 1, + [ + 'max', + ['*', ['/', ['get', 'count'], max], textScale(max) * 9], + textScale(min) * 10, + ], + 12, + [ + 'max', + ['*', ['/', ['get', 'count'], max], textScale(max) * 18], + textScale(min) * 20, + ], ], 'symbol-placement': 'point', - 'text-offset': [0, -0.5], + 'text-offset': [ + 'interpolate', + ['linear'], + ['zoom'], + 1, + [0, -0.1], + 12, + [0, -1], + ], 'text-allow-overlap': true, 'text-ignore-placement': true, 'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'], @@ -171,7 +204,7 @@ export const getAreaCountLayout = ( } export const getAreaLabelLayout = ( - data: GroupedDataCount[] + data: BoundaryAnalytics ): SymbolLayerSpecification['layout'] => { const { min, max, textScale } = getStatsForData(data) @@ -181,11 +214,19 @@ export const getAreaLabelLayout = ( 'text-size': [ 'interpolate', ['linear'], - ['get', 'count'], - min, - textScale(min) * 9, - max, - textScale(max) * 9, + ['zoom'], + 1, + [ + 'max', + ['*', ['/', ['get', 'count'], max], textScale(max) * 9], + textScale(min) * 10, + ], + 12, + [ + 'max', + ['*', ['/', ['get', 'count'], max], textScale(max) * 18], + textScale(min) * 20, + ], ], 'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'], 'symbol-placement': 'point', diff --git a/nextjs/src/app/reports/[id]/useBoundaryAnalytics.ts b/nextjs/src/app/reports/[id]/useBoundaryAnalytics.ts index ea362ee37..7489cf25f 100644 --- a/nextjs/src/app/reports/[id]/useBoundaryAnalytics.ts +++ b/nextjs/src/app/reports/[id]/useBoundaryAnalytics.ts @@ -1,8 +1,4 @@ -import { - AnalyticalAreaType, - GroupedDataCount, - MapReportConstituencyStatsQuery, -} from '@/__generated__/graphql' +import { AnalyticalAreaType } from '@/__generated__/graphql' import { useQuery } from '@apollo/client' import { useEffect, useState } from 'react' import { @@ -11,6 +7,8 @@ import { } from './gql_queries' import { MapReportExtended } from './reportContext' +export type BoundaryAnalytics = ReturnType + const useBoundaryAnalytics = ( report: MapReportExtended | undefined, boundaryType: AnalyticalAreaType @@ -27,7 +25,7 @@ const useBoundaryAnalytics = ( let variables: any = { reportID: report?.id, } - let dataOutputKey: keyof MapReportConstituencyStatsQuery['mapReport'] + let dataOutputKey // TODO: This is where we can implement arithmetic operations on data from multiple // sources, such as the sum of member count per political boundary from two different @@ -41,16 +39,12 @@ const useBoundaryAnalytics = ( dataOutputKey = 'importedDataCountByConstituency' } else if (boundaryType === 'admin_ward') { query = MAP_REPORT_WARD_STATS - // @ts-ignore — asserting here that importedDataCountByWard will also return GroupedDataCount[] dataOutputKey = 'importedDataCountByWard' } else throw new Error('Invalid boundary type') - const boundaryAnalytics = useQuery(query, { - variables, - skip: !canQuery, - }) + const boundaryAnalytics = useQuery(query, { variables, skip: !canQuery }) - return boundaryAnalytics.data?.mapReport[dataOutputKey] as GroupedDataCount[] + return boundaryAnalytics.data?.mapReport[dataOutputKey] } export default useBoundaryAnalytics diff --git a/nextjs/src/components/LocalisedMap.tsx b/nextjs/src/components/LocalisedMap.tsx index 0df998bd9..1bd4a5617 100644 --- a/nextjs/src/components/LocalisedMap.tsx +++ b/nextjs/src/components/LocalisedMap.tsx @@ -35,7 +35,7 @@ const LocalisedMap: React.FC = ({ mapStyle={ showStreetDetails ? 'mapbox://styles/commonknowledge/clubx087l014y01mj1bv63yg8' - : 'mapbox://styles/commonknowledge/cm4cjnvff01mx01sdcmpbfuz5/draft' + : `mapbox://styles/commonknowledge/cm4cjnvff01mx01sdcmpbfuz5${process.env.NODE_ENV !== 'production' ? '/draft' : ''}` } transformRequest={mapboxTransformRequest} >