parent
225af8fef3
commit
9390b15d81
@ -1,130 +0,0 @@
|
|||||||
import { Layer, Source } from "react-map-gl";
|
|
||||||
import { gridLayer } from "./layers-config";
|
|
||||||
import { useGridSize } from "../stores/useGridSize";
|
|
||||||
import { useLayersVisibility } from "../stores/useLayersVisibility";
|
|
||||||
import { useMemo } from "react";
|
|
||||||
import { useRateExpression } from "./useRateExpression";
|
|
||||||
import { useModel } from "../stores/useModel";
|
|
||||||
|
|
||||||
const useGridColorScale = () => {
|
|
||||||
const rate = useRateExpression();
|
|
||||||
const { model } = useModel();
|
|
||||||
|
|
||||||
if (model === "ml") {
|
|
||||||
return [
|
|
||||||
"interpolate",
|
|
||||||
["linear"],
|
|
||||||
rate,
|
|
||||||
0,
|
|
||||||
"rgba(255,0,0,0.78)",
|
|
||||||
10,
|
|
||||||
"rgb(255,137,52)",
|
|
||||||
20,
|
|
||||||
"rgb(255,197,52)",
|
|
||||||
30,
|
|
||||||
"rgb(233,250,0)",
|
|
||||||
40,
|
|
||||||
"rgb(92,164,3)",
|
|
||||||
80,
|
|
||||||
"rgb(7,112,3)",
|
|
||||||
100,
|
|
||||||
"rgb(2,72,1)",
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
"interpolate",
|
|
||||||
["linear"],
|
|
||||||
rate,
|
|
||||||
0,
|
|
||||||
"rgb(204,34,34)",
|
|
||||||
10,
|
|
||||||
"rgb(255,221,52)",
|
|
||||||
20,
|
|
||||||
"rgb(30,131,42)",
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Grid = ({ rate: rateRange }) => {
|
|
||||||
const { gridSize } = useGridSize();
|
|
||||||
const {
|
|
||||||
isVisible: { grid },
|
|
||||||
} = useLayersVisibility();
|
|
||||||
|
|
||||||
const rate = useRateExpression();
|
|
||||||
const colorScale = useGridColorScale();
|
|
||||||
|
|
||||||
const filter = useMemo(() => {
|
|
||||||
return ["all", [">=", rate, rateRange[0]], ["<=", rate, rateRange[1]]];
|
|
||||||
}, [rate, rateRange]);
|
|
||||||
|
|
||||||
const paintConfig = {
|
|
||||||
...gridLayer.paint,
|
|
||||||
"fill-color": colorScale,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Source
|
|
||||||
id="grid3"
|
|
||||||
type="vector"
|
|
||||||
tiles={[
|
|
||||||
`https://postamates.spatiality.website/martin/public.net_3/{z}/{x}/{y}.pbf`,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Layer
|
|
||||||
{...gridLayer}
|
|
||||||
id={"grid3"}
|
|
||||||
source={"grid3"}
|
|
||||||
source-layer={"public.net_3"}
|
|
||||||
layout={{
|
|
||||||
...gridLayer.layout,
|
|
||||||
visibility: grid && gridSize === "net_3" ? "visible" : "none",
|
|
||||||
}}
|
|
||||||
paint={paintConfig}
|
|
||||||
filter={filter}
|
|
||||||
/>
|
|
||||||
</Source>
|
|
||||||
<Source
|
|
||||||
id="grid4"
|
|
||||||
type="vector"
|
|
||||||
tiles={[
|
|
||||||
`https://postamates.spatiality.website/martin/public.net_4/{z}/{x}/{y}.pbf`,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Layer
|
|
||||||
{...gridLayer}
|
|
||||||
id={"grid4"}
|
|
||||||
source={"grid4"}
|
|
||||||
source-layer={"public.net_4"}
|
|
||||||
layout={{
|
|
||||||
...gridLayer.layout,
|
|
||||||
visibility: grid && gridSize === "net_4" ? "visible" : "none",
|
|
||||||
}}
|
|
||||||
filter={filter}
|
|
||||||
paint={paintConfig}
|
|
||||||
/>
|
|
||||||
</Source>
|
|
||||||
<Source
|
|
||||||
id="grid5"
|
|
||||||
type="vector"
|
|
||||||
tiles={[
|
|
||||||
`https://postamates.spatiality.website/martin/public.net_5/{z}/{x}/{y}.pbf`,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Layer
|
|
||||||
{...gridLayer}
|
|
||||||
id={"grid5"}
|
|
||||||
source={"grid5"}
|
|
||||||
source-layer={"public.net_5"}
|
|
||||||
layout={{
|
|
||||||
...gridLayer.layout,
|
|
||||||
visibility: grid && gridSize === "net_5" ? "visible" : "none",
|
|
||||||
}}
|
|
||||||
filter={filter}
|
|
||||||
paint={paintConfig}
|
|
||||||
/>
|
|
||||||
</Source>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
import { useMemo } from "react";
|
|
||||||
import { useFactors } from "../stores/useFactors";
|
|
||||||
import { useModel } from "../stores/useModel";
|
|
||||||
|
|
||||||
const getWeightedValueExpression = (factor, weight) => {
|
|
||||||
return ["*", ["to-number", ["get", factor]], weight];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useRateExpression = () => {
|
|
||||||
const { factors } = useFactors();
|
|
||||||
const { model } = useModel();
|
|
||||||
|
|
||||||
const result = useMemo(() => {
|
|
||||||
if (model === "ml") {
|
|
||||||
return ["get", "model"];
|
|
||||||
}
|
|
||||||
|
|
||||||
const weightSum = Object.entries(factors).reduce(
|
|
||||||
(acc, [_factor, weight]) => {
|
|
||||||
acc += weight;
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
const weightedValuesSum = Object.entries(factors).reduce(
|
|
||||||
(acc, [factor, weight]) => {
|
|
||||||
acc.push(getWeightedValueExpression(factor, weight));
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
["+"]
|
|
||||||
);
|
|
||||||
|
|
||||||
return ["/", weightedValuesSum, weightSum];
|
|
||||||
}, [factors, model]);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
import { Radio } from "antd";
|
|
||||||
import { Title } from "../../components/Title";
|
|
||||||
import { useGridSize } from "../../stores/useGridSize";
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{ label: "3 мин", value: "net_3" },
|
|
||||||
{ label: "4 мин", value: "net_4" },
|
|
||||||
{ label: "5 мин", value: "net_5" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const GridSizeSelect = () => {
|
|
||||||
const { gridSize, setGridSize } = useGridSize();
|
|
||||||
const onChange = ({ target: { value } }) => {
|
|
||||||
setGridSize(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={"flex flex-col items-center"}>
|
|
||||||
<Title text={"Зона пешей доступности"} />
|
|
||||||
<Radio.Group
|
|
||||||
options={options}
|
|
||||||
onChange={onChange}
|
|
||||||
value={gridSize}
|
|
||||||
optionType="button"
|
|
||||||
buttonStyle={"solid"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
import { Select } from "antd";
|
|
||||||
import { Title } from "../../components/Title";
|
|
||||||
import { useModel } from "../../stores/useModel";
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{ value: "statistic", label: "Обычная" },
|
|
||||||
{ value: "ml", label: "Основанная на ML" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const ModelSelect = () => {
|
|
||||||
const { model, setModel } = useModel();
|
|
||||||
const handleChange = (newValue) => setModel(newValue);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={"flex flex-col items-center"}>
|
|
||||||
<Title text={"Модель расчета"} />
|
|
||||||
<Select
|
|
||||||
className={"w-full"}
|
|
||||||
value={model}
|
|
||||||
onChange={handleChange}
|
|
||||||
options={options}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { SliderComponent as Slider } from "../../components/SliderComponent";
|
||||||
|
import { useFilters } from "../../stores/useFilters";
|
||||||
|
|
||||||
|
export const PredictionSlider = () => {
|
||||||
|
const { filters, setPrediction } = useFilters();
|
||||||
|
|
||||||
|
const handleAfterChange = (range) => setPrediction(range);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slider
|
||||||
|
title={"Прогнозный трафик, чел."}
|
||||||
|
value={filters.prediction}
|
||||||
|
onAfterChange={handleAfterChange}
|
||||||
|
min={1}
|
||||||
|
max={100}
|
||||||
|
range
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,31 +0,0 @@
|
|||||||
import { SliderComponent as Slider } from "../../components/SliderComponent";
|
|
||||||
import { useFactors } from "../../stores/useFactors";
|
|
||||||
import { factorsNameMapper } from "../../config";
|
|
||||||
import { useModel } from "../../stores/useModel";
|
|
||||||
|
|
||||||
export const Settings = () => {
|
|
||||||
const { factors, setWeight } = useFactors();
|
|
||||||
const { model } = useModel();
|
|
||||||
|
|
||||||
const handleAfterChange = (factor, value) => {
|
|
||||||
setWeight(factor, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={"space-y-2 min-w-[300px]"}>
|
|
||||||
{Object.entries(factors).map(([field, value]) => {
|
|
||||||
return (
|
|
||||||
<Slider
|
|
||||||
title={factorsNameMapper[field]}
|
|
||||||
value={value}
|
|
||||||
key={field}
|
|
||||||
max={1}
|
|
||||||
step={0.01}
|
|
||||||
onAfterChange={(value) => handleAfterChange(field, value)}
|
|
||||||
disabled={model === "ml"}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
import create from "zustand";
|
|
||||||
import { immer } from "zustand/middleware/immer";
|
|
||||||
|
|
||||||
const store = (set) => ({
|
|
||||||
activeTypes: [],
|
|
||||||
setActiveTypes: (type) =>
|
|
||||||
set((state) => {
|
|
||||||
if (Array.isArray(type)) {
|
|
||||||
state.activeTypes = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state.activeTypes.includes(type)) {
|
|
||||||
state.activeTypes = state.activeTypes.filter((id) => id !== type);
|
|
||||||
} else {
|
|
||||||
state.activeTypes.push(type);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useActiveTypes = create(immer(store));
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
import create from "zustand";
|
|
||||||
import { immer } from "zustand/middleware/immer";
|
|
||||||
import { factorsNameMapper } from "../config";
|
|
||||||
|
|
||||||
const DEFAULT_WEIGHT = 0.5;
|
|
||||||
|
|
||||||
const INITIAL_STATE = Object.keys(factorsNameMapper).reduce((acc, field) => {
|
|
||||||
acc[field] = DEFAULT_WEIGHT;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const store = (set) => ({
|
|
||||||
factors: INITIAL_STATE,
|
|
||||||
setWeight: (factor, value) =>
|
|
||||||
set((state) => {
|
|
||||||
state.factors[factor] = value;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useFactors = create(immer(store));
|
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
import { create } from "zustand";
|
||||||
|
import { immer } from "zustand/middleware/immer";
|
||||||
|
|
||||||
|
const INITIAL = {
|
||||||
|
prediction: [50, 100],
|
||||||
|
categories: [],
|
||||||
|
region: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const store = (set) => ({
|
||||||
|
filters: INITIAL,
|
||||||
|
setPrediction: (value) => {
|
||||||
|
set((state) => {
|
||||||
|
state.filters.prediction = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setCategories: (category) =>
|
||||||
|
set((state) => {
|
||||||
|
if (Array.isArray(category)) {
|
||||||
|
state.filters.categories = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.filters.categories.includes(category)) {
|
||||||
|
state.filters.categories = state.filters.categories.filter(
|
||||||
|
(id) => id !== category
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
state.filters.categories.push(category);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
setRegion: (value) =>
|
||||||
|
set((state) => {
|
||||||
|
state.filters.region = value;
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useFilters = create(immer(store));
|
||||||
@ -1,12 +0,0 @@
|
|||||||
import create from "zustand";
|
|
||||||
import { immer } from "zustand/middleware/immer";
|
|
||||||
|
|
||||||
const store = (set) => ({
|
|
||||||
gridSize: "net_5",
|
|
||||||
setGridSize: (value) =>
|
|
||||||
set((state) => {
|
|
||||||
state.gridSize = value;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useGridSize = create(immer(store));
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
import create from "zustand";
|
|
||||||
import { immer } from "zustand/middleware/immer";
|
|
||||||
|
|
||||||
const store = (set) => ({
|
|
||||||
model: "statistic",
|
|
||||||
setModel: (value) =>
|
|
||||||
set((state) => {
|
|
||||||
state.model = value;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useModel = create(immer(store));
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
import create from "zustand";
|
|
||||||
import { immer } from "zustand/middleware/immer";
|
|
||||||
|
|
||||||
const store = (set) => ({
|
|
||||||
rate: [1, 100],
|
|
||||||
setRate: (value) =>
|
|
||||||
set((state) => {
|
|
||||||
state.rate = value;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useRating = create(immer(store));
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
import create from "zustand";
|
|
||||||
import { immer } from "zustand/middleware/immer";
|
|
||||||
|
|
||||||
const store = (set) => ({
|
|
||||||
region: null,
|
|
||||||
setRegion: (value) =>
|
|
||||||
set((state) => {
|
|
||||||
state.region = value;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useRegion = create(immer(store));
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
import create from "zustand";
|
|
||||||
import { immer } from "zustand/middleware/immer";
|
|
||||||
|
|
||||||
const store = (set) => ({
|
|
||||||
geometry: null,
|
|
||||||
setRegionGeometry: (value) =>
|
|
||||||
set((state) => {
|
|
||||||
state.geometry = value;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useRegionGeometry = create(immer(store));
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
|
"allowJs": false,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": false,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
Loading…
Reference in new issue