Plotly React keep zoom and rotation between re-rendering - reactjs

I'm using the React version of plot.ly to render a 3D scatter plot, but every time I re-render my component, the plot gets reset to its default configuration.
How can I prevent this, and keep the zoom/rotation configuration?
Below is a minimal code example. To re-iterate, how can I save the zoom and rotation, and how can I set it on the plot?
setPlotData() {
const points = {
1: {x: 1, y: 1, z: 1},
2: {x: 2, y: 1, z: 1},
3: {x: 3, y: 2, z: 1}
}
const x=[], y=[], z=[]
Object.keys(points).forEach((key, index) => {
x.push(points[key].x); y.push(points[key].y); z.push(points[key].z)
})
const data1 = {
x: x, y: y, z: z,
mode: "markers",
marker: {
color: "rgb(16, 52, 166)",
size: 4,
symbol: "circle",
opacity: 1
},
type: "scatter3d"
}
this.setState({
scatterPlotData: [data1]
})
}
render() {
return(
<Plot
data={this.state.scatterPlotData}
layout={{
autosize: true,
l: 0, r: 0, b: 0, t: 0, pad: 0,
showlegend: false,
margin:{l:0,r:0,b:0,t:0}
}}
style={{ width: '100%', height: '100%' }}
/>
)
}

Update the layout to add uirevision parameter
layout={{
autosize: true,
l: 0, r: 0, b: 0, t: 0, pad: 0,
showlegend: false,
margin:{l:0,r:0,b:0,t:0},
xaxis: {
uirevision: 'time',
},
yaxis: {
uirevision: 'time',
},
}}
you can change the uirevision: "time" to whatever x,y axis parameters you use

Related

Multiple Gauge Chart

Is it possible to Draw this type of gauges in the left and right of the circle using Highcharts?
Basically left side gauge is representing the shorts' %age at left, the right side gauge is representing the shorts' %age at right, and the center is one simple round circle with the %age of center shots.
Series variable pie it's looks fit better for this case.
Tooltip we built setting useHTML:true, circle at the center we rendered by SVG renderer.
Highcharts.chart('container', {
chart: {
events: {
render() {
const chart = this;
if (!chart.customCenterCircle) {
chart.customCenterCircle = chart.renderer.circle(
chart.plotLeft + chart.plotSizeX / 2,
chart.plotTop + chart.plotSizeY / 2,
0
).add().toFront();
}
if (!chart.customCenterText) {
chart.customCenterText = chart.renderer.text('50%', 0, 0).css({
fontSize: '24px',
color: '#d4d4d4'
}).add().toFront();
}
chart.customCenterCircle.animate({
x: chart.plotLeft + chart.plotSizeX / 2,
y: chart.plotTop + chart.plotSizeY / 2,
r: chart.series[0].center[3] / 2,
fill: 'rgba(31, 112, 31, 0.5)',
stroke: 'rgba(154, 214, 154, 1)',
'stroke-width': 4
});
chart.customCenterText.attr({
x: chart.plotLeft + chart.plotSizeX / 2 -
chart.customCenterText.getBBox().width / 2,
y: chart.plotTop + chart.plotSizeY / 2 -
chart.customCenterText.getBBox().y -
chart.customCenterText.getBBox().height / 2
});
}
}
},
tooltip: {
backgroundColor: '#ffffff',
borderWidth: 0,
useHTML: true,
formatter: function() {
return `<p><h1 style="color:${this.color};">${this.y}</h1></br>${this.key}</p>`
}
},
series: [{
type: 'variablepie',
minPointSize: 0,
innerSize: '30%',
startAngle: 30,
dataLabels: {
enabled: false
},
borderWidth: 3,
zMin: 0,
data: [{
y: 80,
z: 100,
name: 'other',
color: 'black'
}, {
y: 30,
z: 100,
name: 'missed right',
color: 'red'
}, {
y: 50,
z: 0
}, {
y: 25,
z: 80,
name: 'missed right',
color: 'red'
}, {
y: 75,
z: 80,
name: 'other',
color: 'black'
}, {
y: 60,
z: 0,
name: 'other',
}]
}]
});
Live demo:
https://jsfiddle.net/BlackLabel/sc9vwzjt/2/
API References:
https://www.highcharts.com/docs/chart-and-series-types/variable-radius-pie-chart
https://api.highcharts.com/highcharts/series.variablepie
https://api.highcharts.com/class-reference/Highcharts.SVGRenderer

Rendering vector-tiles using Mapbox using react-mapbox-gl

I have a geoJSON file that I convert into vector.tiles using this npm package.
I use const tileIndex = geojsonvt(geoJSON). The geoJSON file has the following format and it gets converted without any error.
const geoJSON = {
type: 'FeatureCollection',
crs: {
type: 'name',
properties: { name: 'urn:ogc:def:crs:OGC:1.3:CRS84' }
},
features: [
{
properties: [Object],
geometry: [Object],
type: 'Feature',
_id: '5ed7b221a61a4b2970433932'
},
... 1840 more items
]
}
The result (geoJSON vector-tiles) that I get after conversion is following -
const tiles = {
options: {},
tiles: {
'0': {
features: [Array],
numPoints: 540529,
numSimplified: 3,
numFeatures: 1940,
source: null,
x: 0,
y: 0,
z: 0,
transformed: false,
minX: 0.5162953202777778,
minY: 0.316725863688461,
maxX: 0.5338655772222223,
maxY: 0.34955196703359503
},
'1': { ... }
},
tileCoords: [
{ z: 0, x: 0, y: 0 }, { z: 1, x: 1, y: 1 },
{ z: 1, x: 1, y: 0 }, { z: 2, x: 3, y: 1 },
{ z: 2, x: 3, y: 0 }, { z: 2, x: 2, y: 1 },
{ z: 3, x: 5, y: 3 }, { z: 3, x: 5, y: 2 },
{ z: 3, x: 4, y: 3 }, { z: 3, x: 4, y: 2 },
{ z: 4, x: 9, y: 5 }, { z: 4, x: 9, y: 4 },
{ z: 4, x: 8, y: 5 }, { z: 5, x: 17, y: 11 },
{ z: 5, x: 17, y: 10 }, { z: 5, x: 16, y: 11 },
{ z: 5, x: 16, y: 10 }, { z: 4, x: 8, y: 4 },
{ z: 2, x: 2, y: 0 }, { z: 1, x: 0, y: 1 },
{ z: 1, x: 0, y: 0 }
]
}
After converting a huge geoJSON file with 5000 layers into vector tiles, I am sending this data to the client-side wherein I render Map using React.js and Mapbox*. I use following to render the map but I have not been able to figure out what I am doing wrong. The error that I get says error: layers.jsx-layer-0: layer "jsx-layer-0" must specify a "source-layer"
<Source type="vector" tiles={data.tiles} >
<Layer {...dataLayer}/>
</Source>
I went through the documentation of Mapbox for the same but I'm unable to find what I am doing wrong. Any help would be of great help. Thank you very much.
react-mapbox-gl is in many places just a wrapper around mapbox-gl, and if you look at the documentation there:
https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#tiled-sources
You will see that the "tiles" property is only for url sources, where as the "url" property can be used to load a file with tiles:
"url": "http://api.example.com/tilejson.json"
The docs indicate that the source-layer is required field for vector layers.
That said, it certainly opaque as to how this works in a declarative api. Based on example, you might try this to see if it works -
...
const url = 'mapbox://mapbox.mapbox-terrain-v2'
const source = 'my-source';
<Source id={{source}} name={{source}} type="vector" url={url} tiles={data.tiles} >
<Layer source={{source}} {...dataLayer}/>
</Source>
...
rendering a layer with it source so you need to referrer to source id in layer + you need to add a source-layer prop like this:
<Source id='contours' type='vector' url='mapbox://mapbox.mapbox-terrain-v2' tileJsonSource={data.tiles}/>
<Layer
id='contours'
type='line'
source='contours'
source-layer='contour'
paint={{
'line-color': '#877b59',
'line-width': 1
}}
/>
</MapGL>;

React-Grid-Layout elements handle jumps if there are other elements in the app root in react app

My React-Grid-Layout elements handle jumps if there are other elements in the app root in my react app.
If I remove the navbar component above the responsive grid layout I am able to click and drag properly, otherwise, when I try to click on the responsive grid layout element it jumps down.
Here's my code:-
App.js
import React from 'react';
import './App.css';
import '#devexpress/dx-react-grid-bootstrap4/dist/dx-react-grid-bootstrap4.css';
import PyfinNavbar from './components/navbar';
import HomeView from './home';
import PyfinResponsiveGrid from './ResponsiveGridLayout';
function App() {
return (
<div className="App">
<PyfinNavbar></PyfinNavbar>
<HomeView></HomeView>
<PyfinResponsiveGrid></PyfinResponsiveGrid>
</div>
);
}
export default App;
PyfinResponsiveGridLayout.jsx
import React from "react";
import { Responsive as ResponsiveGridLayout } from "react-grid-layout";
import PyfinGrid from "./grid";
let layouts = {
lg: [
{ h: 2, w: 2, x: 0, y: 0 },
{ h: 2, w: 2, x: 1, y: 0 },
{ h: 2, w: 2, x: 2, y: 0 },
{ h: 2, w: 2, x: 3, y: 0 }
],
sm: [
{ h: 2, w: 2, x: 0, y: 0 },
{ h: 2, w: 2, x: 1, y: 0 },
{ h: 2, w: 2, x: 2, y: 0 },
{ h: 2, w: 2, x: 3, y: 0 }
],
md: [
{ h: 2, w: 2, x: 0, y: 0 },
{ h: 2, w: 2, x: 1, y: 0 },
{ h: 2, w: 2, x: 2, y: 0 },
{ h: 2, w: 2, x: 3, y: 0 }
],
xs: [
{ h: 2, w: 2, x: 0, y: 0 },
{ h: 2, w: 2, x: 0, y: 1 },
{ h: 2, w: 2, x: 0, y: 2 },
{ h: 2, w: 2, x: 0, y: 3 }
],
xxs: [
{ h: 2, w: 2, x: 0, y: 0 },
{ h: 2, w: 2, x: 0, y: 1 },
{ h: 2, w: 2, x: 0, y: 2 },
{ h: 2, w: 2, x: 0, y: 3 }
]
};
class PyfinResponsiveGrid extends React.Component {
render() {
return (
<ResponsiveGridLayout
className="layout"
layouts={layouts}
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
>
<div className="widgetborder" key="1">
<PyfinGrid></PyfinGrid>
</div>
</ResponsiveGridLayout>
);
}
}
export default PyfinResponsiveGrid;
grid.jsx
import React from "react";
import { PagingState, IntegratedPaging } from "#devexpress/dx-react-grid";
import {
Grid,
Table,
TableHeaderRow,
PagingPanel
} from "#devexpress/dx-react-grid-bootstrap4";
import customers from "./customers";
const GridView = () => (
<Grid
rows={customers}
columns={[
{ name: "customerID", title: "ID" },
{ name: "companyName", title: "Company" },
{ name: "contactName", title: "Name" },
{ name: "contactTitle", title: "Title" },
{ name: "address", title: "Address" }
]}
>
<PagingState defaultCurrentPage={0} pageSize={1} />
<IntegratedPaging />
<Table />
<TableHeaderRow />
<PagingPanel />
</Grid>
);
class PyfinGrid extends React.Component {
state = {};
render() {
return (
<div>
<GridView></GridView>
</div>
);
}
}
export default PyfinGrid;
Basically I am trying to create something like
https://www.bitmex.com/app/trade/XRPH20
It uses the same gridlayout control but when I use devextreme react grid in the layout the above mentioned problem happens.
This solved the issue for me - see comment.
.react-grid-layout {
position: relative;
}

ReactJS/Nivo Graphs - How to modify the x-axis to be ascending date order when data contains "incomplete" data?

Question: How can I "reformat" the x-axis to be in ascending date, when the data contains a variable amount of dates within it?
I have some data that contains variable amounts of "date" associated with it. For example, Line One contains:
{ x: "2019-05-01", y: 2 },
{ x: "2019-06-01", y: 7 },
{ x: "2020-03-01", y: 1 }
Whereas Line Two contains:
{ x: "2019-05-01", y: 1 },
{ x: "2019-06-01", y: 5 },
{ x: "2020-05-01", y: 5 }
Code box: https://codesandbox.io/s/dreamy-almeida-x7rnd?fontsize=14&hidenavigation=1&theme=dark
The data is "uneven" on the x-axis, hence why it looks weird. I realise that if I were to put in "dummy dates" and data to it, then I can dictate the x-axis to become the format I want, the issue is dummy dates + data, because then it doesn't accurately model my database, so I want to avoid something like this.
What I've done so far is look over the documentation, I've tried adding in the type: "time" format to xscale, but I'm getting error
v.getTime() is not a function.
You have to add format to both ResponsiveLine Component and xScale property in order to use a time scale
xFormat:
Optional formatter for x values.
The formatted value can then be used for labels & tooltips.
If you use a time scale, you must provide a time format as values are converted to Date objects.
Complete config: (Working demo)
import React from "react";
import { ResponsiveLine } from "#nivo/line";
export default function App() {
return (
<div style={{ height: 350 }} className="App">
<ResponsiveLine
data={[
{
id: "LineOne",
data: [
{ x: "2019-05-01", y: 2 },
{ x: "2019-06-01", y: 7 },
{ x: "2020-03-01", y: 1 }
]
},
{
id: "LineTwo",
data: [
{ x: "2019-05-01", y: 1 },
{ x: "2019-06-01", y: 5 },
{ x: "2020-05-01", y: 5 }
]
},
{
id: "LineThree",
data: [
{ x: "2020-02-01", y: 4 },
{ x: "2020-03-01", y: 6 },
{ x: "2020-04-01", y: 1 }
]
}
]}
margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
xScale={{
type: "time",
format: "%Y-%m-%d"
}}
xFormat="time:%Y-%m-%d"
yScale={{
type: "linear",
min: "auto",
max: "auto",
stacked: false,
reverse: false
}}
axisTop={null}
axisRight={null}
axisLeft={{
orient: "left",
tickSize: 5,
tickPadding: 5,
tickRotation: 0,
legend: "count",
legendOffset: -40,
legendPosition: "middle"
}}
axisBottom={{
format: "%b %d",
//tickValues: "every 2 days",
// tickRotation: -90,
legend: "time scale",
legendOffset: -12
}}
colors={{ scheme: "nivo" }}
pointSize={10}
pointColor={{ theme: "background" }}
pointBorderWidth={2}
pointBorderColor={{ from: "serieColor" }}
pointLabel="y"
pointLabelYOffset={-12}
useMesh={true}
legends={[
{
anchor: "bottom-right",
direction: "column",
justify: false,
translateX: 100,
translateY: 0,
itemsSpacing: 0,
itemDirection: "left-to-right",
itemWidth: 80,
itemHeight: 20,
itemOpacity: 0.75,
symbolSize: 12,
symbolShape: "circle",
symbolBorderColor: "rgba(0, 0, 0, .5)",
effects: [
{
on: "hover",
style: {
itemBackground: "rgba(0, 0, 0, .03)",
itemOpacity: 1
}
}
]
}
]}
/>
)
</div>
);
}
modified the following things and tested with random (none sorted data)
xscale type to time with the format
add indexBy="date" property to responsiveline element
add format: "%Y-%m-%d" to your axis bottom,
you can find a working example here
https://codesandbox.io/s/laughing-mestorf-zeozb
export default function App()
{ return (
<div style={{ height: 350 }} className="App">
<ResponsiveLine
data={[
{
id: "LineOne",
data: [
{ x: "2019-05-01", y: 2 },
{ x: "2019-06-01", y: 7 },
{ x: "2020-03-01", y: 1 },
{ x: "2017-09-01", y: 6 }
]
},
{
id: "LineTwo",
data: [
{ x: "2019-05-01", y: 1 },
{ x: "2019-06-01", y: 5 },
{ x: "2020-05-01", y: 5 },
{ x: "2018-09-01", y: 3 }
]
},
{
id: "LineThree",
data: [
{ x: "2020-02-01", y: 4 },
{ x: "2020-03-01", y: 6 },
{ x: "2020-04-01", y: 1 }
]
}
]}
indexBy="date"
margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
xScale={{
type: "time",
format: "%Y-%m-%d",
precision: "day"
}}
yScale={{ type: "linear", stacked: false, min: 0, max: "auto" }}
axisTop={null}
axisRight={null}
axisBottom={{
orient: "bottom",
tickSize: 5,
format: "%Y-%m-%d",
tickPadding: 5,
tickRotation: -65,
legend: "time",
legendOffset: 40,
legendPosition: "middle"
}}
axisLeft={{
orient: "left",
tickSize: 5,
tickPadding: 5,
tickRotation: 0,
legend: "count",
legendOffset: -40,
legendPosition: "middle"
}}
colors={{ scheme: "nivo" }}
pointSize={10}
pointColor={{ theme: "background" }}
pointBorderWidth={2}
pointBorderColor={{ from: "serieColor" }}
pointLabel="y"
pointLabelYOffset={-12}
useMesh={true}
legends={[
{
anchor: "bottom-right",
direction: "column",
justify: false,
translateX: 100,
translateY: 0,
itemsSpacing: 0,
itemDirection: "left-to-right",
itemWidth: 80,
itemHeight: 20,
itemOpacity: 0.75,
symbolSize: 12,
symbolShape: "circle",
symbolBorderColor: "rgba(0, 0, 0, .5)",
effects: [
{
on: "hover",
style: {
itemBackground: "rgba(0, 0, 0, .03)",
itemOpacity: 1
}
}
]
}
]}
/>
)
</div>
);
}
Another thing to note is that in the above answers, the date on the actual chart is -1 from the date in the data. To solve this, add "useUTC: false" to your xScale props.

Angular-nvd3 change animation transitions

I'm using angular-nvd3 and I'm having a lot of trouble figuring out how to change the way things animate.
Below is what currently happens when you toggle a dataset on. As you can see the Expenses animate in from the upper right and the Distances animate in directly from the right. What I would like them both to do is one of two things. Either animate with a basic fade in, or grow upward.
Here is my controller. Interestingly when I added yDomain1: [1, 200] it caused the Distance chart to animate directly from the right, prior to that it was also animating in from the upper right.
controllers.controller("DashboardController", ['$scope','MileageRestService',
($scope,MileageRestService)->
$scope.vehicle_log_data = {}
MileageRestService.dashboard(
(result)->
console.log('dashboard result', result)
$scope.vehicle_log_data = result
distances = result.current_year.chart_data.distances
expenses = result.current_year.chart_data.expenses
$scope.data = [
{
key: "Distances",
color: '#ff7f0e',
area: true,
type: "line",
yAxis: 1,
values: distances
},
{
key: "Expenses",
color: '#7777ff',
area: true,
type: "line",
yAxis: 2,
values: expenses
}
]
$scope.options = {
chart: {
type: 'multiChart',
height: 256,
margin : {
top: 20,
left: 50
},
x: (d)-> d.x,
y: (d)-> (d.y),
showValues: true,
lines1: {
duration: 500
},
lines2: {
duration: 500
},
yDomain1: [1, 200],
transitions: true,
useInteractiveGuideline: true,
clipEdge: false
}
}
(error)->
console.log('dashboard error', error)
)
])
The Distance and Expense data is:
distance = [
{x: 1, y: 100},
{x: 2, y: 25},
{x: 3, y: 150},
{x: 4, y: 110},
{x: 5, y: 0},
{x: 6, y: 175},
{x: 7, y: 0},
{x: 8, y: 0},
{x: 9, y: 0},
{x: 10, y: 0},
{x: 11, y: 0},
{x: 12, y: 0}
]
expenses = [
{x: 1, y: 10},
{x: 2, y: 5},
{x: 3, y: 15},
{x: 4, y: 7.75},
{x: 5, y: 0},
{x: 6, y: 20},
{x: 7, y: 0},
{x: 8, y: 0},
{x: 9, y: 0},
{x: 10, y: 0},
{x: 11, y: 0},
{x: 12, y: 0}
]

Resources