UNPKG

react-meteo-chart

Version:

pre alpha of meteo chart

740 lines (703 loc) 25.3 kB
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;