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