Skip to content

Commit

Permalink
Merge pull request #149 from commonknowledge/border-styles
Browse files Browse the repository at this point in the history
Border styles [MAP-661]
  • Loading branch information
ev-sc authored Dec 11, 2024
2 parents d18e7c8 + 3cda2f4 commit 4a396ab
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,20 @@ const PoliticalChoropleths: React.FC<PoliticalChoroplethsProps> = ({
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,
}}
/>
<Layer
Expand All @@ -126,9 +137,19 @@ const PoliticalChoropleths: React.FC<PoliticalChoroplethsProps> = ({
}}
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,
}}
/>
</Source>
Expand Down
3 changes: 1 addition & 2 deletions nextjs/src/app/reports/[id]/addCountByGssToMapboxLayer.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { GroupedDataCount } from '@/__generated__/graphql'
import { MAPBOX_LOAD_INTERVAL } from '@/lib/map/useLoadedMap'
import { MapRef } from 'react-map-gl'

// GSS (Geographic Statistical System) codes are unique identifiers
// 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
Expand Down
85 changes: 63 additions & 22 deletions nextjs/src/app/reports/[id]/mapboxStyles.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { GroupedDataCount } from '@/__generated__/graphql'
import { scaleLinear, scaleSequential } from 'd3-scale'
import { interpolateBlues } from 'd3-scale-chromatic'
import {
Expand All @@ -7,6 +6,7 @@ import {
SymbolLayerSpecification,
} from 'mapbox-gl'
import { Tileset } from './types'
import { BoundaryAnalytics } from './useBoundaryAnalytics'

export function getChoroplethFill(
data: { count: number }[]
Expand Down Expand Up @@ -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)
Expand All @@ -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],
}
}

Expand All @@ -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 || '')],
]
}
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -146,7 +163,7 @@ function getStatsForData(data: GroupedDataCount[]) {
}

export const getAreaCountLayout = (
data: GroupedDataCount[]
data: BoundaryAnalytics
): SymbolLayerSpecification['layout'] => {
const { min, max, textScale } = getStatsForData(data)

Expand All @@ -156,22 +173,38 @@ 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'],
}
}

export const getAreaLabelLayout = (
data: GroupedDataCount[]
data: BoundaryAnalytics
): SymbolLayerSpecification['layout'] => {
const { min, max, textScale } = getStatsForData(data)

Expand All @@ -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',
Expand Down
18 changes: 6 additions & 12 deletions nextjs/src/app/reports/[id]/useBoundaryAnalytics.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -11,6 +7,8 @@ import {
} from './gql_queries'
import { MapReportExtended } from './reportContext'

export type BoundaryAnalytics = ReturnType<typeof useBoundaryAnalytics>

const useBoundaryAnalytics = (
report: MapReportExtended | undefined,
boundaryType: AnalyticalAreaType
Expand All @@ -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
Expand All @@ -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<MapReportConstituencyStatsQuery>(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
2 changes: 1 addition & 1 deletion nextjs/src/components/LocalisedMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const LocalisedMap: React.FC<LocalisedMapProps> = ({
mapStyle={
showStreetDetails
? 'mapbox://styles/commonknowledge/clubx087l014y01mj1bv63yg8'
: 'mapbox://styles/commonknowledge/cm4cjnvff01mx01sdcmpbfuz5/draft'
: `mapbox://styles/commonknowledge/cm4cjnvff01mx01sdcmpbfuz5${process.env.NODE_ENV !== 'production' ? '/draft' : ''}`
}
transformRequest={mapboxTransformRequest}
>
Expand Down

0 comments on commit 4a396ab

Please sign in to comment.