Skip to content

Commit

Permalink
fix: reduce flicker when zooming between tilesets
Browse files Browse the repository at this point in the history
  • Loading branch information
joaquimds committed Jan 20, 2025
1 parent 60a41b1 commit ffbf906
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
useMapBounds,
} from '@/lib/map'
import { debounce } from 'lodash'
import React, { Fragment, useEffect } from 'react'
import { FillLayerSpecification } from 'mapbox-gl'
import React, { Fragment, useEffect, useState } from 'react'
import { Layer, Source } from 'react-map-gl'
import { addCountByGssToMapboxLayer } from '../../addCountByGssToMapboxLayer'
import {
Expand Down Expand Up @@ -46,17 +47,17 @@ const PoliticalChoropleths: React.FC<PoliticalChoroplethsProps> = ({
? 'visible'
: 'none'

const [mapBounds, setMapBounds] = useMapBounds()
// Store the choropleth fills when they are calculated for a layer
// to avoid flicker when zooming in and out.
const [fillsByLayer, setFillsByLayer] = useState<
Record<string, FillLayerSpecification['paint']>
>({})

const { activeTileset, setActiveTileset } = useActiveTileset(boundaryType)
const useDataByBoundaryResult = useDataByBoundary({
const { data } = useDataByBoundary({
report,
tileset: activeTileset,
})
const { data, fetchMore } = useDataByBoundaryResult
console.log(
'PoliticalChoropleths.useDataByBoundaryResult',
useDataByBoundaryResult
)
const dataByBoundary = data?.choroplethDataForSource || []

const boundaryNameVisibility =
Expand All @@ -74,6 +75,8 @@ const PoliticalChoropleths: React.FC<PoliticalChoroplethsProps> = ({
const [explorer, setExplorer] = useExplorerState()
useHoverOverBoundaryEvents(areasVisible === 'visible' ? activeTileset : null)

// Update map bounds and active tileset on pan/zoom
const [, setMapBounds] = useMapBounds()
useEffect(() => {
const onMoveEnd = debounce(() => {
const zoom = map.loadedMap?.getZoom() || 0
Expand All @@ -100,8 +103,16 @@ const PoliticalChoropleths: React.FC<PoliticalChoroplethsProps> = ({
activeTileset.sourceLayerId,
map.loadedMap
)
// Calculate the choropleth fill for the layer and store it,
// to reduce flicker when zooming between layers
const fill = getChoroplethFill(
dataByBoundary,
report.displayOptions,
shaderVisibility === 'visible'
)
setFillsByLayer({ ...fillsByLayer, [activeTileset.mapboxSourceId]: fill })
}
}, [map.loaded, activeTileset, dataByBoundary, report])
}, [map.loaded, activeTileset, data, report])

if (!map.loaded) return null
if (!data || !tilesets) return null
Expand All @@ -127,11 +138,7 @@ const PoliticalChoropleths: React.FC<PoliticalChoroplethsProps> = ({
source={tileset.mapboxSourceId}
source-layer={tileset.sourceLayerId}
type="fill"
paint={getChoroplethFill(
dataByBoundary,
report.displayOptions,
shaderVisibility === 'visible'
)}
paint={fillsByLayer[tileset.mapboxSourceId] || {}}
minzoom={tileset.minZoom}
maxzoom={tileset.maxZoom}
/>
Expand Down Expand Up @@ -179,6 +186,8 @@ const PoliticalChoropleths: React.FC<PoliticalChoroplethsProps> = ({
id={`${tileset.mapboxSourceId}-area-count`}
type="geojson"
data={getAreaGeoJSON(dataByBoundary)}
minzoom={tileset.minZoom}
maxzoom={tileset.maxZoom}
>
<Layer
id={`${tileset.mapboxSourceId}-area-count`}
Expand Down
3 changes: 3 additions & 0 deletions nextjs/src/app/reports/[id]/(components)/ReportPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export default function ReportPage() {

const [mapBounds] = useMapBounds()

// Fetch more data when the map bounds change
// This has to be here for the loading indicator to work
// (fetchMore only triggers loading: true in its local hook)
useEffect(() => {
if (activeTileset.useBoundsInDataQuery) {
fetchMore({
Expand Down
15 changes: 7 additions & 8 deletions nextjs/src/app/reports/[id]/addCountByGssToMapboxLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ export function addCountByGssToMapboxLayer(
if (!mapbox?.loaded) throw new Error('loaded map is required')
if (!sourceLayerId) throw new Error('sourceLayerId is required')

// Remove previously set data from all areas
if (mapbox?.getSource(mapboxSourceId)) {
mapbox.removeFeatureState({
source: mapboxSourceId,
sourceLayer: sourceLayerId,
})
}

setTimeout(() => {
// Remove previously set data from all areas
if (mapbox?.getSource(mapboxSourceId)) {
mapbox.removeFeatureState({
source: mapboxSourceId,
sourceLayer: sourceLayerId,
})
}
data.forEach((d) => {
if (!d.gss) return
try {
Expand Down

0 comments on commit ffbf906

Please sign in to comment.