Use discrete colors for distances, with greens only above 1.5m
This commit is contained in:
parent
6893d7b56f
commit
1c52ce7de9
|
@ -1,9 +1,64 @@
|
||||||
import {useMemo} from "react";
|
import React, {useMemo} from "react";
|
||||||
|
|
||||||
type ColorMap = [number, string][]
|
type ColorMap = [number, string][]
|
||||||
|
|
||||||
import styles from './ColorMapLegend.module.less'
|
import styles from './ColorMapLegend.module.less'
|
||||||
|
|
||||||
|
function* pairs(arr) {
|
||||||
|
for (let i = 1; i < arr.length; i++) {
|
||||||
|
yield [arr[i - 1], arr[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function* zip(...arrs) {
|
||||||
|
const l = Math.min(...arrs.map(a => a.length));
|
||||||
|
for (let i = 0; i < l; i++) {
|
||||||
|
yield arrs.map(a => a[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DiscreteColorMapLegend({map}: {map: ColorMap}) {
|
||||||
|
const colors: string[] = map.filter((x, i) => i % 2 == 0) as any
|
||||||
|
const stops: number[] = map.filter((x, i) => i % 2 == 1) as any
|
||||||
|
let min = stops[0]
|
||||||
|
let max = stops[stops.length - 1]
|
||||||
|
const buffer = (max - min) / (stops.length - 1) / 2
|
||||||
|
min -= buffer
|
||||||
|
max += buffer
|
||||||
|
const normalizeValue = (v) => (v - min) / (max - min)
|
||||||
|
const stopPairs = Array.from(pairs([min, ...stops, max]));
|
||||||
|
|
||||||
|
const gradientId = useMemo(() => `gradient${Math.floor(Math.random() * 1000000)}`, []);
|
||||||
|
const gradientUrl = `url(#${gradientId})`;
|
||||||
|
|
||||||
|
const parts = Array.from(zip(stopPairs, colors))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.colorMapLegend}>
|
||||||
|
<svg width="100%" height="20" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id={gradientId} x1="0" x2="1" y1="0" y2="0">
|
||||||
|
{parts.map(([[left, right], color]) => (
|
||||||
|
<React.Fragment key={left}>
|
||||||
|
<stop offset={normalizeValue(left) * 100 + '%'} stopColor={color} />
|
||||||
|
<stop offset={normalizeValue(right) * 100 + '%'} stopColor={color} />
|
||||||
|
</React.Fragment>
|
||||||
|
|
||||||
|
))}
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect id="rect1" x="0" y="0" width="100%" height="100%" fill={gradientUrl} />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{stops.map((value) => (
|
||||||
|
<span className={styles.tick} key={value} style={{left: normalizeValue(value) * 100 + '%'}}>
|
||||||
|
{value.toFixed(2)}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function ColorMapLegend({map, twoTicks = false}: {map: ColorMap, twoTicks?: boolean}) {
|
export default function ColorMapLegend({map, twoTicks = false}: {map: ColorMap, twoTicks?: boolean}) {
|
||||||
const min = map[0][0]
|
const min = map[0][0]
|
||||||
const max = map[map.length - 1][0]
|
const max = map[map.length - 1][0]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export {default as Avatar} from './Avatar'
|
export {default as Avatar} from './Avatar'
|
||||||
export {default as ColorMapLegend} from './ColorMapLegend'
|
export {default as ColorMapLegend, DiscreteColorMapLegend} from './ColorMapLegend'
|
||||||
export {default as FileDrop} from './FileDrop'
|
export {default as FileDrop} from './FileDrop'
|
||||||
export {default as FileUploadField} from './FileUploadField'
|
export {default as FileUploadField} from './FileUploadField'
|
||||||
export {default as FormattedDate} from './FormattedDate'
|
export {default as FormattedDate} from './FormattedDate'
|
||||||
|
|
|
@ -53,13 +53,13 @@ export function colorByDistance(attribute = 'distance_overtaker_mean', fallback
|
||||||
['!', ['to-boolean', ['get', attribute]]],
|
['!', ['to-boolean', ['get', attribute]]],
|
||||||
fallback,
|
fallback,
|
||||||
[
|
[
|
||||||
'interpolate-hcl',
|
'step',
|
||||||
['linear'],
|
|
||||||
['get', attribute],
|
['get', attribute],
|
||||||
1,
|
'rgba(150, 0, 0, 1)',
|
||||||
|
1.1,
|
||||||
'rgba(255, 0, 0, 1)',
|
'rgba(255, 0, 0, 1)',
|
||||||
1.3,
|
1.3,
|
||||||
'rgba(255, 200, 0, 1)',
|
'rgba(255, 220, 0, 1)',
|
||||||
1.5,
|
1.5,
|
||||||
'rgba(67, 200, 0, 1)',
|
'rgba(67, 200, 0, 1)',
|
||||||
1.7,
|
1.7,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
initialState as defaultMapConfig,
|
initialState as defaultMapConfig,
|
||||||
} from 'reducers/mapConfig'
|
} from 'reducers/mapConfig'
|
||||||
import {colorByDistance, colorByCount, viridisSimpleHtml} from 'mapstyles'
|
import {colorByDistance, colorByCount, viridisSimpleHtml} from 'mapstyles'
|
||||||
import {ColorMapLegend} from 'components'
|
import {ColorMapLegend, DiscreteColorMapLegend} from 'components'
|
||||||
|
|
||||||
const BASEMAP_STYLE_OPTIONS = [
|
const BASEMAP_STYLE_OPTIONS = [
|
||||||
{value: 'positron', key: 'positron', text: 'Positron'},
|
{value: 'positron', key: 'positron', text: 'Positron'},
|
||||||
|
@ -98,7 +98,7 @@ function LayerSidebar({
|
||||||
) :
|
) :
|
||||||
(
|
(
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<ColorMapLegend map={_.chunk(colorByDistance('distance_overtaker')[3].slice(3), 2)} />
|
<DiscreteColorMapLegend map={colorByDistance('distance_overtaker')[3].slice(2)} />
|
||||||
</List.Item>
|
</List.Item>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -120,7 +120,7 @@ function LayerSidebar({
|
||||||
{showEvents && (
|
{showEvents && (
|
||||||
<>
|
<>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<ColorMapLegend map={_.chunk(colorByDistance('distance_overtaker')[3].slice(3), 2)} />
|
<DiscreteColorMapLegend map={colorByDistance('distance_overtaker')[3].slice(2)} />
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
Loading…
Reference in a new issue