Hint about filters not being applied in road info popover
This commit is contained in:
parent
1a3b971a71
commit
c3ed4f24dd
|
@ -1,6 +1,14 @@
|
||||||
import React, { useState, useCallback } from "react";
|
import React, { useState, useCallback } from "react";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { Segment, Menu, Header, Label, Icon, Table } from "semantic-ui-react";
|
import {
|
||||||
|
Segment,
|
||||||
|
Menu,
|
||||||
|
Header,
|
||||||
|
Label,
|
||||||
|
Icon,
|
||||||
|
Table,
|
||||||
|
Message,
|
||||||
|
} from "semantic-ui-react";
|
||||||
import { Layer, Source } from "react-map-gl";
|
import { Layer, Source } from "react-map-gl";
|
||||||
import { of, from, concat } from "rxjs";
|
import { of, from, concat } from "rxjs";
|
||||||
import { useObservable } from "rxjs-hooks";
|
import { useObservable } from "rxjs-hooks";
|
||||||
|
@ -9,8 +17,9 @@ import { Chart } from "components";
|
||||||
import { pairwise } from "utils";
|
import { pairwise } from "utils";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import api from 'api'
|
import type { Location } from "types";
|
||||||
import {colorByDistance, borderByZone} from 'mapstyles'
|
import api from "api";
|
||||||
|
import { colorByDistance, borderByZone } from "mapstyles";
|
||||||
|
|
||||||
import styles from "./styles.module.less";
|
import styles from "./styles.module.less";
|
||||||
|
|
||||||
|
@ -88,15 +97,20 @@ function RoadStatsTable({ data }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function HistogramChart({ bins, counts, zone }) {
|
function HistogramChart({ bins, counts, zone }) {
|
||||||
const diff = bins[1] - bins[0]
|
const diff = bins[1] - bins[0];
|
||||||
const colortype = zone === "rural" ? 3 : 5;
|
const colortype = zone === "rural" ? 3 : 5;
|
||||||
const data = _.zip(
|
const data = _.zip(
|
||||||
bins.slice(0, bins.length - 1).map((v) => v + diff / 2),
|
bins.slice(0, bins.length - 1).map((v) => v + diff / 2),
|
||||||
counts
|
counts
|
||||||
).map((value) => ({
|
).map((value) => ({
|
||||||
value,
|
value,
|
||||||
itemStyle: {color: selectFromColorMap(colorByDistance()[3][colortype].slice(2), value[0]),},
|
itemStyle: {
|
||||||
}))
|
color: selectFromColorMap(
|
||||||
|
colorByDistance()[3][colortype].slice(2),
|
||||||
|
value[0]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Chart
|
<Chart
|
||||||
|
@ -123,7 +137,13 @@ function HistogramChart({ bins, counts, zone }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RoadInfo({ clickLocation }) {
|
export default function RoadInfo({
|
||||||
|
clickLocation,
|
||||||
|
hasFilters,
|
||||||
|
}: {
|
||||||
|
clickLocation: Location | null;
|
||||||
|
hasFilters: boolean;
|
||||||
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [direction, setDirection] = useState("forwards");
|
const [direction, setDirection] = useState("forwards");
|
||||||
|
|
||||||
|
@ -183,6 +203,15 @@ export default function RoadInfo({ clickLocation }) {
|
||||||
: info?.road.name || t("MapPage.roadInfo.unnamedWay")}
|
: info?.road.name || t("MapPage.roadInfo.unnamedWay")}
|
||||||
</Header>
|
</Header>
|
||||||
|
|
||||||
|
{hasFilters && (
|
||||||
|
<Message info icon>
|
||||||
|
<Icon name="info circle" small />
|
||||||
|
<Message.Content>
|
||||||
|
{t("MapPage.roadInfo.hintFiltersNotApplied")}
|
||||||
|
</Message.Content>
|
||||||
|
</Message>
|
||||||
|
)}
|
||||||
|
|
||||||
{info?.road.zone && (
|
{info?.road.zone && (
|
||||||
<Label size="small" color={ZONE_COLORS[info?.road.zone]}>
|
<Label size="small" color={ZONE_COLORS[info?.road.zone]}>
|
||||||
{t(`general.zone.${info.road.zone}`)}
|
{t(`general.zone.${info.road.zone}`)}
|
||||||
|
@ -220,9 +249,9 @@ export default function RoadInfo({ clickLocation }) {
|
||||||
|
|
||||||
{info?.[direction]?.distanceOvertaker?.histogram && (
|
{info?.[direction]?.distanceOvertaker?.histogram && (
|
||||||
<>
|
<>
|
||||||
<Header as="h5">
|
<Header as="h5">
|
||||||
{t("MapPage.roadInfo.overtakerDistanceDistribution")}
|
{t("MapPage.roadInfo.overtakerDistanceDistribution")}
|
||||||
</Header>
|
</Header>
|
||||||
<HistogramChart
|
<HistogramChart
|
||||||
{...info[direction]?.distanceOvertaker?.histogram}
|
{...info[direction]?.distanceOvertaker?.histogram}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -5,10 +5,17 @@ import { Button } from "semantic-ui-react";
|
||||||
import { Layer, Source } from "react-map-gl";
|
import { Layer, Source } from "react-map-gl";
|
||||||
import produce from "immer";
|
import produce from "immer";
|
||||||
|
|
||||||
import {Page, Map} from 'components'
|
import type { Location } from "types";
|
||||||
import {useConfig} from 'config'
|
import { Page, Map } from "components";
|
||||||
import {colorByDistance, colorByCount, borderByZone, reds, isValidAttribute} from 'mapstyles'
|
import { useConfig } from "config";
|
||||||
import {useMapConfig} from 'reducers/mapConfig'
|
import {
|
||||||
|
colorByDistance,
|
||||||
|
colorByCount,
|
||||||
|
borderByZone,
|
||||||
|
reds,
|
||||||
|
isValidAttribute,
|
||||||
|
} from "mapstyles";
|
||||||
|
import { useMapConfig } from "reducers/mapConfig";
|
||||||
|
|
||||||
import RoadInfo from "./RoadInfo";
|
import RoadInfo from "./RoadInfo";
|
||||||
import LayerSidebar from "./LayerSidebar";
|
import LayerSidebar from "./LayerSidebar";
|
||||||
|
@ -43,20 +50,19 @@ const untaggedRoadsLayer = {
|
||||||
|
|
||||||
const getUntaggedRoadsLayer = (colorAttribute, maxCount) =>
|
const getUntaggedRoadsLayer = (colorAttribute, maxCount) =>
|
||||||
produce(untaggedRoadsLayer, (draft) => {
|
produce(untaggedRoadsLayer, (draft) => {
|
||||||
draft.filter = ['!', isValidAttribute(colorAttribute)]
|
draft.filter = ["!", isValidAttribute(colorAttribute)];
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
const getRoadsLayer = (colorAttribute, maxCount) =>
|
const getRoadsLayer = (colorAttribute, maxCount) =>
|
||||||
produce(untaggedRoadsLayer, (draft) => {
|
produce(untaggedRoadsLayer, (draft) => {
|
||||||
draft.id = "obs_roads_normal";
|
draft.id = "obs_roads_normal";
|
||||||
draft.filter = isValidAttribute(colorAttribute)
|
draft.filter = isValidAttribute(colorAttribute);
|
||||||
draft.paint["line-width"][6] = 6; // scale bigger on zoom
|
draft.paint["line-width"][6] = 6; // scale bigger on zoom
|
||||||
draft.paint["line-color"] = colorAttribute.startsWith("distance_")
|
draft.paint["line-color"] = colorAttribute.startsWith("distance_")
|
||||||
? colorByDistance(colorAttribute)
|
? colorByDistance(colorAttribute)
|
||||||
: colorAttribute.endsWith("_count")
|
: colorAttribute.endsWith("_count")
|
||||||
? colorByCount(colorAttribute, maxCount)
|
? colorByCount(colorAttribute, maxCount)
|
||||||
: colorAttribute.endsWith('zone')
|
: colorAttribute.endsWith("zone")
|
||||||
? borderByZone()
|
? borderByZone()
|
||||||
: "#DDD";
|
: "#DDD";
|
||||||
draft.paint["line-opacity"][3] = 12;
|
draft.paint["line-opacity"][3] = 12;
|
||||||
|
@ -105,10 +111,7 @@ const getEventsTextLayer = () => ({
|
||||||
|
|
||||||
function MapPage({ login }) {
|
function MapPage({ login }) {
|
||||||
const { obsMapSource } = useConfig() || {};
|
const { obsMapSource } = useConfig() || {};
|
||||||
const [clickLocation, setClickLocation] = useState<{
|
const [clickLocation, setClickLocation] = useState<Location | null>(null);
|
||||||
longitude: number;
|
|
||||||
latitude: number;
|
|
||||||
} | null>(null);
|
|
||||||
|
|
||||||
const mapConfig = useMapConfig();
|
const mapConfig = useMapConfig();
|
||||||
|
|
||||||
|
@ -135,9 +138,12 @@ function MapPage({ login }) {
|
||||||
|
|
||||||
const layers = [];
|
const layers = [];
|
||||||
|
|
||||||
const untaggedRoadsLayerCustom = useMemo(() => getUntaggedRoadsLayer(attribute), [attribute])
|
const untaggedRoadsLayerCustom = useMemo(
|
||||||
|
() => getUntaggedRoadsLayer(attribute),
|
||||||
|
[attribute]
|
||||||
|
);
|
||||||
if (mapConfig.obsRoads.show && mapConfig.obsRoads.showUntagged) {
|
if (mapConfig.obsRoads.show && mapConfig.obsRoads.showUntagged) {
|
||||||
layers.push(untaggedRoadsLayerCustom)
|
layers.push(untaggedRoadsLayerCustom);
|
||||||
}
|
}
|
||||||
|
|
||||||
const roadsLayer = useMemo(
|
const roadsLayer = useMemo(
|
||||||
|
@ -159,31 +165,36 @@ function MapPage({ login }) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tiles = obsMapSource?.tiles?.map(
|
const tiles = obsMapSource?.tiles?.map((tileUrl: string) => {
|
||||||
(tileUrl: string) => {
|
const query = new URLSearchParams();
|
||||||
const query = new URLSearchParams()
|
if (login) {
|
||||||
if (login) {
|
if (mapConfig.filters.currentUser) {
|
||||||
if (mapConfig.filters.currentUser) {
|
query.append("user", login.username);
|
||||||
query.append('user', login.username)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (mapConfig.filters.dateMode === "range") {
|
if (mapConfig.filters.dateMode === "range") {
|
||||||
if (mapConfig.filters.startDate) {
|
if (mapConfig.filters.startDate) {
|
||||||
query.append('start', mapConfig.filters.startDate)
|
query.append("start", mapConfig.filters.startDate);
|
||||||
}
|
}
|
||||||
if (mapConfig.filters.endDate) {
|
if (mapConfig.filters.endDate) {
|
||||||
query.append('end', mapConfig.filters.endDate)
|
query.append("end", mapConfig.filters.endDate);
|
||||||
}
|
}
|
||||||
} else if (mapConfig.filters.dateMode === "threshold") {
|
} else if (mapConfig.filters.dateMode === "threshold") {
|
||||||
if (mapConfig.filters.startDate) {
|
if (mapConfig.filters.startDate) {
|
||||||
query.append(mapConfig.filters.thresholdAfter ? 'start' : 'end', mapConfig.filters.startDate)
|
query.append(
|
||||||
}
|
mapConfig.filters.thresholdAfter ? "start" : "end",
|
||||||
|
mapConfig.filters.startDate
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const queryString = String(query)
|
|
||||||
return tileUrl + (queryString ? '?' : '') + queryString
|
|
||||||
}
|
}
|
||||||
);
|
const queryString = String(query);
|
||||||
|
return tileUrl + (queryString ? "?" : "") + queryString;
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasFilters: boolean =
|
||||||
|
login &&
|
||||||
|
(mapConfig.filters.currentUser || mapConfig.filters.dateMode !== "none");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page fullScreen title="Map">
|
<Page fullScreen title="Map">
|
||||||
|
@ -212,7 +223,7 @@ function MapPage({ login }) {
|
||||||
))}
|
))}
|
||||||
</Source>
|
</Source>
|
||||||
|
|
||||||
<RoadInfo {...{ clickLocation }} />
|
<RoadInfo {...{ clickLocation, hasFilters }} />
|
||||||
</Map>
|
</Map>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -220,6 +231,4 @@ function MapPage({ login }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default connect((state) => ({ login: state.login }))(MapPage);
|
||||||
(state) => ({login: state.login}),
|
|
||||||
)(MapPage);
|
|
||||||
|
|
|
@ -188,6 +188,7 @@ MapPage:
|
||||||
mean: Durchschnitt
|
mean: Durchschnitt
|
||||||
|
|
||||||
overtakerDistanceDistribution: Verteilung der Überholabstände
|
overtakerDistanceDistribution: Verteilung der Überholabstände
|
||||||
|
hintFiltersNotApplied: Filter aus der Seiteleiste werden (noch) nicht auf diese Daten angewandt.
|
||||||
|
|
||||||
cardinalDirections:
|
cardinalDirections:
|
||||||
unknown: unbekannt
|
unknown: unbekannt
|
||||||
|
|
|
@ -192,6 +192,7 @@ MapPage:
|
||||||
mean: Average
|
mean: Average
|
||||||
|
|
||||||
overtakerDistanceDistribution: Overtaker distance distribution
|
overtakerDistanceDistribution: Overtaker distance distribution
|
||||||
|
hintFiltersNotApplied: Filters from the sidebar are not (yet) applied on this data.
|
||||||
|
|
||||||
cardinalDirections:
|
cardinalDirections:
|
||||||
unknown: unknown
|
unknown: unknown
|
||||||
|
|
|
@ -44,3 +44,8 @@ export type TrackComment = {
|
||||||
createdAt: string
|
createdAt: string
|
||||||
author: UserProfile
|
author: UserProfile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Location {
|
||||||
|
longitude: number;
|
||||||
|
latitude: number;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue