react-meteo-chart
Version:
pre alpha of meteo chart
740 lines (703 loc) • 25.3 kB
JSX
import React from "react";
import { AmsChart } from "react-meteo-chart";
import { Query, ApolloProvider } from "react-apollo";
import moment from "moment";
import ApolloClient from "apollo-boost";
import ReactLoading from "react-loading";
import Select from "react-select";
import gql from "graphql-tag";
import "./style.css";
const day = 86400000;
const windDirectionHandler = (msgs, minmax, height) => {
let arr = [];
let dy = minmax[1] - minmax[0];
let y_dir = minmax[0] - dy / 5;
arr = msgs
.filter((e, i) => e[1] !== null)
.map((point) => {
switch (point[1]) {
case 0:
return {
x: point[0] * 1000,
y: y_dir,
customComponent: "circle",
size: 10,
style: { stroke: "black", fill: "white" },
z: 0,
};
case 99:
return {
x: point[0],
y: y_dir,
customComponent: "circle",
size: 20,
style: { stroke: "black", fill: "white" },
z: 0,
};
default:
const val = point[1];
let velocity = point[2];
let triNum = Math.floor(velocity / 25);
let longfeathNum = Math.floor((velocity % 25) / 5);
let leftVelocity =
velocity > 1 ? velocity - triNum * 25 - longfeathNum * 5 : 0;
let translate = 0;
let things = [];
if (velocity <= 1)
if (val <= 180)
things.push(
<path
d="M489.33 178.76C489.33 191.29 479.56 201.46 467.52 201.46C455.48 201.46 445.7 191.29 445.7 178.76C445.7 166.24 455.48 156.07 467.52 156.07C479.56 156.07 489.33 166.24 489.33 178.76Z"
key={`${val}${point[0]}${translate}`}
/>
);
else
things.push(
<path
d="M443.63 344C443.63 356.53 433.86 366.7 421.82 366.7C409.78 366.7 400 356.53 400 344C400 331.47 409.78 321.3 421.82 321.3C433.86 321.3 443.63 331.47 443.63 344Z"
key={`${val}${point[0]}${translate}`}
/>
);
for (let i = 0; i < triNum; i++) {
if (val <= 180)
things.push(
<path
transform={`translate(${-translate})`}
d="M389.38 126.04L257.46 257.96L389.38 257.96L389.38 126.04Z"
key={`${val}${point[0]}${translate}`}
/>
);
else
things.push(
<path
transform={`translate(${-translate})`}
d="M389.59 409.96L257.68 278.04L389.59 278.04L389.59 409.96Z"
key={`${val}${point[0]}${translate}`}
/>
);
translate += 120;
}
for (let i = 0; i < longfeathNum; i++) {
if (val <= 180)
things.push(
<path
transform={`translate(${-translate})`}
d="M514.03 149.92L387.53 281.28L373.08 267.36L499.58 136L514.03 149.92Z"
key={`${val}${point[0]}${translate}`}
/>
);
else
things.push(
<path
transform={`translate(${-translate})`}
d="M520 391.41L389.6 256L375.15 269.92L505.55 405.33L520 391.41Z"
key={`${val}${point[0]}${translate}`}
/>
);
translate += 60;
}
if (leftVelocity) {
if (leftVelocity < 4) {
if (val <= 180)
things.push(
<path
transform={`translate(${-translate})`}
d="M460.19 207.58L390.45 280L376 266.08L445.74 193.66L460.19 207.58Z"
key={`${val}${point[0]}${translate}`}
/>
);
else
things.push(
<path
transform={`translate(${-translate})`}
d="M460.19 328.42L390.45 256L376 269.92L445.74 342.34L460.19 328.42Z"
key={`${val}${point[0]}${translate}`}
/>
);
} else if (leftVelocity < 5) {
if (val <= 180)
things.push(
<path
transform={`translate(${-translate})`}
d="M514.03 149.92L387.53 281.28L373.08 267.36L499.58 136L514.03 149.92Z"
key={`${val}${point[0]}${translate}`}
/>
);
else
things.push(
<path
transform={`translate(${-translate})`}
d="M520 391.41L389.6 256L375.15 269.92L505.55 405.33L520 391.41Z"
key={`${val}${point[0]}${translate}`}
/>
);
}
}
return {
x: point[0] * 1000,
y: y_dir,
customComponent: (row, positionInPixels) => {
return (
<g
// transform={` scale(0.10) `}
transform={` scale(0.080) rotate(${val - 90} 270 270)`}
// transformOrigin={"2000% 50%"}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
x="0"
y="0"
viewBox="0 0 512 512"
style={{ fill: "#50C8EF" }}
// style="enable-background:new 0 0 512 512;"
>
<path
d="M24 255.18L389.57 255.18L389.57 280L24 280L24 255.18Z"
key={`${point[0]}-1`}
/>
<path
d="M50.34 267.54L114.23 306.46L89.08 321.79L46.62 295.92L46.58 295.94L0 267.56L0.03 267.54L0 267.53L46.58 239.15L46.61 239.17L89.08 213.3L114.22 228.62L50.34 267.54Z"
key={`${point[0]}-2`}
/>
{things.map((d) => d)}
{/* <rect
y="227.558"
style={{ fill: "#50C8EF" }}
width="455.117"
height="56.883"
/>
<polygon
style={{ fill: "#50C8EF" }}
points="278.566,390.774 250.117,341.507 398.226,256 250.117,170.491 278.566,121.226 512,256 "
/> */}
</g>
);
},
z: val,
};
}
});
arr.push({
x: arr[0] ? arr[0].x : null,
y: minmax[0] - dy / 1.5,
customComponent: "circle",
size: 0,
style: { stroke: "black", fill: "white" },
z: 0,
});
return arr;
};
const currentStation = gql`
query amsPlotData(
$token: String!
$stationId: String!
$since: DateTime
$to: DateTime
$typeName: String!
$maxPoints: Int
) {
amsPlotData(
token: $token
stationId: $stationId
typeName: $typeName
since: $since
to: $to
maxPoints: $maxPoints
)
}
`;
const ME = gql`
query($token: String!) {
me(token: $token) {
lastLogin
}
}
`;
const selecterStyle = {
control: (base, state) => ({
...base,
backgroundColor: "ccccff",
opacity: 1,
height: "12px",
width: "100px",
border: state.isFocused ? 0 : 0,
boxShadow: state.isFocused ? 0 : 0,
"&:hover": {
border: state.isFocused ? 0 : 0,
},
}),
dropdownIndicator: (e) => ({
...e,
color: "#646464",
transform: "rotate(180deg)",
}),
menu: (e) => ({
...e,
color: "#00000",
width: "100px",
backgroundColor: "#ffFffF",
}),
};
const zooms = [
[1, "d", "День"],
[3, "d", "3 Дня"],
[7, "d", "Неделя"],
[14, "d", "2 Недели"],
];
const selectValues = [
{ label: "День", value: 0 },
{ label: "3 Дня", value: 1 },
{ label: "Неделя", value: 2 },
{ label: "2 Недели", value: 3 },
];
const zoomsvar = [3, 6, 12, 24];
// const selectedId = "22829";
// let dateFrom = "2019-03-23T06:30:00";
// let dateTo = "2019-03-25T06:30:00";
const types = ["ff", "TTT1", "tend", "RRR6", "dd_ff", "P0P0P0P03", "PPPP4"];
let minmax = [0, 0];
class AmsChartGen extends React.Component {
constructor(props) {
super(props);
this.state = {
chunks: [],
isLoadingChunks: [],
chunksLength: [],
zoom: 0,
since: this.props.dateFrom
? moment(this.props.dateFrom).format("YYYY-MM-DDTHH:mm:ss")
: null,
to: moment(this.props.dateTo).format("YYYY-MM-DDTHH:mm:ss"),
deep: true,
wind: [],
dataGap: this.props.dateTo,
stationId: this.props.stationId,
loadMessage:
this.props.loadMessage !== undefined ? this.props.loadMessage : true,
};
this.deep = true;
this.deepDarkFantasy = true;
this.apollo = new ApolloClient({
uri: this.props.client,
});
}
componentDidUpdate() {
if (
this.state.to !==
moment(this.props.dateTo).utc().format("YYYY-MM-DDTHH:mm:ss")
) {
this.setState({
to: moment(this.props.dateTo).utc().format("YYYY-MM-DDTHH:mm:ss"),
});
if (!this.props.animated) this.deep = true;
}
if (this.props.stationId !== this.state.stationId) {
this.setState({
stationId: this.props.stationId,
});
this.deep = true;
}
}
dataTransformer = (data, indx, minmax) => {
let chunk = [];
switch (indx) {
case 0:
chunk = data.data.amsPlotData.map((s) => ({
x: s[0] * 1000,
y: s[1] !== null ? s[1] : "",
}));
break;
case 3:
data.data.amsPlotData.forEach((d) => {
chunk = data.data.amsPlotData.map((s) => ({
x0: s[0] * 1000,
x: s[1] * 1000,
y: s[2] !== null ? s[2] : "",
}));
});
break;
case 4:
chunk = windDirectionHandler(
data.data.amsPlotData,
minmax,
this.props.height
);
break;
case 2:
data.data.amsPlotData.forEach((d) => {
chunk = data.data.amsPlotData
.filter((d) => d[2] !== null)
.map((s) => ({
x: (s[0] * 1000 + s[1] * 1000) / 2,
y: (s[4] + s[5]) / 2,
yLow: s[2],
yHigh: s[3],
yOpen: s[4] > s[5] ? s[4] : s[5],
yClose: s[4] > s[5] ? s[5] : s[4],
color: s[4] > s[5] ? "#dd0000dd" : "#00dd00dd",
}));
});
break;
default:
chunk = data.data.amsPlotData.map((s) => ({
x: s[0] * 1000,
y: s[1] !== null ? s[1] : "",
}));
}
return chunk;
};
getTend = (variables) => {
if (!this.props.dateFrom) return `${zoomsvar[this.state.zoom]}tend`;
else {
let gap =
+moment(this.state.to).format("X") -
+moment(this.state.since).format("X");
if (gap <= 86400) return "3tend";
else if (gap <= 86400 * 3) return "6tend";
else if (gap <= 86400 * 7) return "12tend";
else if (gap <= 86400 * 14) return "24tend";
return "48tend";
}
};
render() {
let variables = {
token: this.props.token,
stationId: this.state.stationId,
since: this.state.since
? this.state.since
: moment(this.props.dateTo)
.add(-zooms[this.state.zoom][0], zooms[this.state.zoom][1])
.format("YYYY-MM-DDTHH:mm:ss"),
to: moment(this.props.dateTo).format("YYYY-MM-DDTHH:mm:ss"),
dataSince: moment(this.state.dataGap).add(
-zooms[this.state.zoom][0],
zooms[this.state.zoom][1]
),
dataTo: moment(this.state.dataGap),
maxPoints: this.props.maxPoints,
};
// if (this.state.chunks[0] && this.state.chunks[4]) {
// this.windDirectionHandler()
// }
if (this.props.doneLoading) {
this.props.doneLoading(
this.state.isLoadingChunks.filter((d) => d == false).length == 0 &&
this.state.isLoadingChunks.length > 0
);
}
let superArr = this.state.chunks.map((d) =>
d
.sort((a, b) => a.x > b.x)
.filter(
(s) =>
s.x >= +moment.utc(variables.since).format("X") * 1000 &&
s.x <= +moment.utc(variables.to).format("X") * 1000
)
);
// console.log("since :", moment.utc(variables.since).format("X"));
// console.log("to :", moment.utc(variables.to).format("X"));
// console.log("superArr :", superArr);
// TODO: Сделать нормальную проверку границ
if (
+moment(variables.since).format("X") < +variables.dataSince.format("X") &&
this.props.dir == -1
) {
// this.deepDarkFantasy = false;
this.preFetch(this.props.dir);
}
if (
+moment(variables.to).format("X") > +variables.dataTo.format("X") &&
this.props.dir == 1
) {
// this.deepDarkFantasy = false;
this.preFetch(this.props.dir);
}
return (
<ApolloProvider client={this.apollo}>
<div className="App">
{this.state.isLoadingChunks.filter((d) => d == false).length > 0 &&
this.state.loadMessage && (
<div
style={{
display: "flex",
justifyContent: "center",
alignContent: "center",
alignItems: "center",
flexDirection: "column",
fontSize: "17px",
height: "100%",
}}
>
<ReactLoading
type="balls"
color="#333333"
height={25}
width={50}
/>{" "}
<p> Загрузка графиков </p>
</div>
)}
<Query query={ME} variables={{ token: variables.token }}>
{({ loading, error, data, client }) => {
if (loading) return <div />;
if (error) return <p>Ошибка...</p>;
if (!this.client) this.client = client;
let req = [];
{
this.deep &&
types.forEach((t, indx) => {
req[indx] = client.query({
query: currentStation,
variables: {
token: variables.token,
stationId: variables.stationId,
typeName: indx == 2 ? this.getTend(variables) : t,
since: variables.since,
to: variables.to,
maxPoints:
indx == 4
? this.props.maxPoints / 10
: this.props.maxPoints,
},
});
setTimeout(() =>
this.setState((state) => {
let chunk = false;
const isLoadingChunks = (state.isLoadingChunks[
indx
] = chunk);
return isLoadingChunks;
})
);
if (indx == 4)
Promise.all([req[0], req[4]]).then((data) => {
let filtredData = data[0].data.amsPlotData.map(
(d) => d[1]
);
let minmax = [
Math.min(...filtredData),
Math.max(...filtredData),
];
setTimeout(() =>
this.setState((state) => {
let chunk = this.dataTransformer(
data[1],
indx,
minmax
);
state.chunksLength[indx] = chunk.length;
state.chunks[indx] = chunk;
})
);
setTimeout(() =>
this.setState((state) => {
let chunk = true;
const isLoadingChunks = (state.isLoadingChunks[
indx
] = chunk);
return isLoadingChunks;
})
);
});
else {
req[indx].then((data) => {
setTimeout(() =>
this.setState((state) => {
let chunk = this.dataTransformer(data, indx);
state.chunksLength[indx] = chunk.length;
state.chunks[indx] = chunk;
})
);
setTimeout(() =>
this.setState((state) => {
let chunk = true;
const isLoadingChunks = (state.isLoadingChunks[
indx
] = chunk);
return isLoadingChunks;
})
);
});
}
});
this.deep = false;
}
this.preFetch = async (dir) => {
let req = [];
types.forEach((t, indx) => {
req[indx] = client.query({
query: currentStation,
variables: {
token: variables.token,
stationId: variables.stationId,
typeName:
indx == 2 ? `${zoomsvar[this.state.zoom]}${t}` : t,
since: moment(variables.dataSince)
.add(dir, "d")
.format("YYYY-MM-DDTHH:mm"),
to: moment(variables.dataTo)
.add(dir, "d")
.format("YYYY-MM-DDTHH:mm"),
maxPoints:
indx == 4
? this.props.maxPoints / 10
: this.props.maxPoints,
},
});
setTimeout(() =>
this.setState((state) => {
let chunk = false;
const isLoadingChunks = (state.isLoadingChunks[
indx
] = chunk);
return isLoadingChunks;
})
);
if (indx == 4)
Promise.all([req[0], req[4]]).then((data) => {
let filtredData = data[0].data.amsPlotData.map(
(d) => d[1]
);
let minmax = [
Math.min(...filtredData),
Math.max(...filtredData),
];
setTimeout(() =>
this.setState((state) => {
let chunk = this.dataTransformer(
data[1],
indx,
minmax
);
if (dir == -1)
state.chunks[indx] = [
...chunk,
...state.chunks[indx].slice(
0,
this.state.chunksLength[indx]
),
];
if (dir == 1)
state.chunks[indx] = [
...state.chunks[indx].slice(
state.chunks[indx].length -
this.state.chunksLength[indx],
state.chunks[indx].length
),
...chunk,
];
state.chunksLength[indx] = chunk.length;
})
);
setTimeout(() =>
this.setState((state) => {
let chunk = true;
const isLoadingChunks = (state.isLoadingChunks[
indx
] = chunk);
return isLoadingChunks;
})
);
});
else {
req[indx].then((data) => {
setTimeout(() =>
this.setState((state) => {
let chunk = this.dataTransformer(data, indx);
if (dir == -1)
state.chunks[indx] = [
...chunk,
...state.chunks[indx].slice(
0,
this.state.chunksLength[indx]
),
];
if (dir == 1)
state.chunks[indx] = [
...state.chunks[indx].slice(
state.chunks[indx].length -
this.state.chunksLength[indx],
state.chunks[indx].length
),
...chunk,
];
state.chunksLength[indx] = chunk.length;
})
);
setTimeout(() =>
this.setState((state) => {
let chunk = true;
const isLoadingChunks = (state.isLoadingChunks[
indx
] = chunk);
return isLoadingChunks;
})
);
});
}
});
this.setState({
dataGap: moment(this.state.dataGap)
.add(dir, "d")
.format("YYYY-MM-DDTHH:mm"),
});
this.deepDarkFantasy = true;
};
return null;
}}
</Query>
<AmsChart
opacity={this.props.opacity}
superArr={superArr}
plotHeight={this.props.height}
dateFrom={variables.since}
dateTo={variables.to}
isAdmin={this.props.isAdmin}
animated={this.props.animated}
// arrows={true}
/>
<div
style={{
marginTop: "-40px",
width: "100px",
}}
>
{!this.props.isAdmin && (
<Select
theme={(theme) => ({
...theme,
borderRadius: 1,
spacing: {
baseUnit: 2,
controlHeight: 30,
menuGutter: 2,
},
colors: {
...theme.colors,
primary: "#cee0eb",
neutral0: "#000000",
},
})}
isSearchable={false}
// opacity={10}
onChange={(option) =>
this.state.zoom !== option.value
? (this.setState({ zoom: option.value }),
(this.deep = true))
: ""
}
placeholder={""}
value={selectValues[this.state.zoom]}
menuPlacement="top"
menuPosition="fixed"
styles={selecterStyle}
options={selectValues}
/>
)}
</div>
</div>
</ApolloProvider>
);
}
}
export default AmsChartGen;