@@ -27,6 +27,114 @@ import {
2727} from 'ol/style' ;
2828import util from '../../util/util' ;
2929
30+ // Gets the indices of the tick positions so that they are evenly spaced
31+ function getTickPositions ( dataLength ) {
32+ // If dataLength is too small, just show first and last tick
33+ if ( dataLength < 8 ) return [ 0 , dataLength - 1 ] ;
34+
35+ const numGaps = dataLength < 15 ? 4 : 5 ;
36+ const gapsArr = Array ( numGaps ) . fill ( Math . floor ( dataLength / numGaps ) ) ;
37+
38+ // Last gap must be at least 3 to give extra room for end-aligned label
39+ gapsArr [ gapsArr . length - 1 ] = Math . max ( Math . floor ( dataLength / 4 ) , 3 ) ;
40+
41+ const gapsTotal = gapsArr . reduce ( ( a , b ) => a + b , 0 ) ;
42+ let leftoverGap = ( dataLength - 1 ) - gapsTotal ;
43+
44+ let i = 0 ;
45+ // Reduce gaps that are too large due to last gap size
46+ while ( leftoverGap < 0 && i < numGaps - 1 ) {
47+ gapsArr [ i ] -= 1 ;
48+ leftoverGap += 1 ;
49+ i = ( i + 1 ) % ( numGaps - 1 ) ;
50+ }
51+
52+ i = 0 ;
53+ // Distribute extra gaps across existing gaps
54+ while ( leftoverGap > 0 && i < numGaps - 1 ) {
55+ gapsArr [ i ] += 1 ;
56+ leftoverGap -= 1 ;
57+ i = ( i + 1 ) % ( numGaps - 1 ) ;
58+ }
59+
60+ // Build final array of tick positions based on calculated gaps
61+ const tickPosArr = [ 0 ] ;
62+ for ( let j = 0 ; j < gapsArr . length ; j += 1 ) {
63+ tickPosArr . push ( tickPosArr [ tickPosArr . length - 1 ] + gapsArr [ j ] ) ;
64+ }
65+ tickPosArr [ tickPosArr . length - 1 ] = dataLength - 1 ;
66+
67+ return tickPosArr ;
68+ }
69+
70+
71+ function CustomXAxisTick ( props ) {
72+ const {
73+ x, y, fill, textAnchor, visibleTicksCount, index, payload, data,
74+ } = props ;
75+ const tickPositions = getTickPositions ( data . length ) ;
76+ const anchorPos = index === visibleTicksCount - 1 ? 'end' : textAnchor ;
77+ const isLabeled = tickPositions . includes ( index ) ;
78+ if ( isLabeled ) {
79+ return (
80+ < g transform = { `translate(${ x } , ${ y } )` } >
81+ < line x1 = "0" y1 = "0" x2 = "0" y2 = "-8" stroke = { fill } />
82+ < text x = { anchorPos === 'end' ? 10 : 0 } y = { 0 } dy = { 16 } textAnchor = { anchorPos } fill = { fill } >
83+ { payload . value }
84+ </ text >
85+ </ g >
86+ ) ;
87+ }
88+ return (
89+ < g transform = { `translate(${ x } , ${ y } )` } >
90+ < line x1 = "0" y1 = "-4" x2 = "0" y2 = "-8" stroke = { fill } />
91+ </ g >
92+ ) ;
93+ }
94+
95+ function formatToThreeDigits ( str ) {
96+ if ( parseFloat ( str ) . toFixed ( 3 ) . split ( '.' ) [ 0 ] . length > 4 ) {
97+ return Number ( parseFloat ( str ) . toFixed ( 3 ) ) . toPrecision ( 3 ) ;
98+ }
99+ return parseFloat ( str ) . toFixed ( 3 ) ;
100+ }
101+
102+ function CustomTooltip ( {
103+ active, payload, label, unit,
104+ } ) {
105+ const formattedUnit = unit ? ` (${ unit } )` : '' ;
106+ if ( active && payload && payload . length ) {
107+ if ( ! Number . isNaN ( payload [ 0 ] . value ) ) {
108+ return (
109+ < div className = "custom-tooltip" >
110+ < p className = "label" style = { { color : 'gray' } } >
111+ { label }
112+ </ p >
113+ < p className = "label" style = { { color : '#000' } } >
114+ < span className = "custom-data-rect" style = { { backgroundColor : payload [ 0 ] . color } } />
115+ { `${ payload [ 0 ] . name } ${ formattedUnit } : ` }
116+ < b >
117+ { formatToThreeDigits ( payload [ 0 ] . value ) }
118+ </ b >
119+ </ p >
120+ </ div >
121+ ) ;
122+ }
123+ return (
124+ < div className = "custom-tooltip" >
125+ < p className = "label" style = { { color : 'gray' } } >
126+ { label }
127+ </ p >
128+ < p className = "label" style = { { color : '#000' } } >
129+ No data
130+ </ p >
131+ </ div >
132+ ) ;
133+ }
134+
135+ return null ;
136+ }
137+
30138function ChartComponent ( props ) {
31139 const {
32140 liveData,
@@ -95,12 +203,7 @@ function ChartComponent (props) {
95203 const lineColors = [ '#A3905D' , '#82CA9D' , 'orange' , 'pink' , 'green' , 'red' , 'yellow' , 'aqua' , 'maroon' ] ;
96204 const formattedUnit = unit ? ` (${ unit } )` : '' ;
97205
98- function formatToThreeDigits ( str ) {
99- if ( parseFloat ( str ) . toFixed ( 3 ) . split ( '.' ) [ 0 ] . length > 4 ) {
100- return Number ( parseFloat ( str ) . toFixed ( 3 ) ) . toPrecision ( 3 ) ;
101- }
102- return parseFloat ( str ) . toFixed ( 3 ) ;
103- }
206+
104207
105208 /**
106209 * Return an array of provided min & max values buffered by 10%
@@ -133,103 +236,10 @@ function ChartComponent (props) {
133236 return bufferYAxisMinAndMax ( lowestMin , highestMax ) ;
134237 }
135238
136- function CustomTooltip ( { active, payload, label } ) {
137- if ( active && payload && payload . length ) {
138- if ( ! Number . isNaN ( payload [ 0 ] . value ) ) {
139- return (
140- < div className = "custom-tooltip" >
141- < p className = "label" style = { { color : 'gray' } } >
142- { label }
143- </ p >
144- < p className = "label" style = { { color : '#000' } } >
145- < span className = "custom-data-rect" style = { { backgroundColor : payload [ 0 ] . color } } />
146- { `${ payload [ 0 ] . name } ${ formattedUnit } : ` }
147- < b >
148- { formatToThreeDigits ( payload [ 0 ] . value ) }
149- </ b >
150- </ p >
151- </ div >
152- ) ;
153- }
154- return (
155- < div className = "custom-tooltip" >
156- < p className = "label" style = { { color : 'gray' } } >
157- { label }
158- </ p >
159- < p className = "label" style = { { color : '#000' } } >
160- No data
161- </ p >
162- </ div >
163- ) ;
164- }
165-
166- return null ;
167- }
168-
169- // Gets the indices of the tick positions so that they are evenly spaced
170- function getTickPositions ( dataLength ) {
171- // If dataLength is too small, just show first and last tick
172- if ( dataLength < 8 ) return [ 0 , dataLength - 1 ] ;
173-
174- const numGaps = dataLength < 15 ? 4 : 5 ;
175- const gapsArr = Array ( numGaps ) . fill ( Math . floor ( dataLength / numGaps ) ) ;
176239
177- // Last gap must be at least 3 to give extra room for end-aligned label
178- gapsArr [ gapsArr . length - 1 ] = Math . max ( Math . floor ( dataLength / 4 ) , 3 ) ;
179240
180- const gapsTotal = gapsArr . reduce ( ( a , b ) => a + b , 0 ) ;
181- let leftoverGap = ( dataLength - 1 ) - gapsTotal ;
182241
183- let i = 0 ;
184- // Reduce gaps that are too large due to last gap size
185- while ( leftoverGap < 0 && i < numGaps - 1 ) {
186- gapsArr [ i ] -= 1 ;
187- leftoverGap += 1 ;
188- i = ( i + 1 ) % ( numGaps - 1 ) ;
189- }
190-
191- i = 0 ;
192- // Distribute extra gaps across existing gaps
193- while ( leftoverGap > 0 && i < numGaps - 1 ) {
194- gapsArr [ i ] += 1 ;
195- leftoverGap -= 1 ;
196- i = ( i + 1 ) % ( numGaps - 1 ) ;
197- }
198242
199- // Build final array of tick positions based on calculated gaps
200- const tickPosArr = [ 0 ] ;
201- for ( let i = 0 ; i < gapsArr . length ; i += 1 ) {
202- tickPosArr . push ( tickPosArr [ tickPosArr . length - 1 ] + gapsArr [ i ] ) ;
203- }
204- tickPosArr [ tickPosArr . length - 1 ] = dataLength - 1 ;
205-
206- return tickPosArr ;
207- }
208-
209- const tickPositions = getTickPositions ( data . length ) ;
210-
211- function CustomXAxisTick ( obj ) {
212- const {
213- x, y, fill, textAnchor, visibleTicksCount, index, payload,
214- } = obj ;
215- const anchorPos = index === visibleTicksCount - 1 ? 'end' : textAnchor ;
216- const isLabeled = tickPositions . includes ( index ) ;
217- if ( isLabeled ) {
218- return (
219- < g transform = { `translate(${ x } , ${ y } )` } >
220- < line x1 = "0" y1 = "0" x2 = "0" y2 = "-8" stroke = { fill } />
221- < text x = { anchorPos === 'end' ? 10 : 0 } y = { 0 } dy = { 16 } textAnchor = { anchorPos } fill = { fill } >
222- { payload . value }
223- </ text >
224- </ g >
225- ) ;
226- }
227- return (
228- < g transform = { `translate(${ x } , ${ y } )` } >
229- < line x1 = "0" y1 = "-4" x2 = "0" y2 = "-8" stroke = { fill } />
230- </ g >
231- ) ;
232- }
233243
234244 const yAxisValuesArr = getYAxisValues ( data ) ;
235245
@@ -415,10 +425,10 @@ function ChartComponent (props) {
415425 bottom : 10 ,
416426 } }
417427 >
418- < Tooltip content = { CustomTooltip } />
428+ < Tooltip content = { < CustomTooltip unit = { unit } /> } />
419429 { ' ' }
420430 { getLineChart ( data ) }
421- < XAxis dataKey = "name" stroke = "#a6a5a6" interval = { 0 } tick = { < CustomXAxisTick /> } tickLine = { false } />
431+ < XAxis dataKey = "name" stroke = "#a6a5a6" interval = { 0 } tick = { < CustomXAxisTick data = { data } /> } tickLine = { false } />
422432 < YAxis
423433 type = "number"
424434 stroke = "#a6a5a6"
@@ -574,6 +584,24 @@ ChartComponent.propTypes = {
574584 overviewMapLayerDef : PropTypes . object ,
575585} ;
576586
587+ CustomXAxisTick . propTypes = {
588+ x : PropTypes . number ,
589+ y : PropTypes . number ,
590+ fill : PropTypes . string ,
591+ textAnchor : PropTypes . string ,
592+ visibleTicksCount : PropTypes . number ,
593+ index : PropTypes . number ,
594+ payload : PropTypes . object ,
595+ data : PropTypes . array ,
596+ } ;
597+
598+ CustomTooltip . propTypes = {
599+ active : PropTypes . bool ,
600+ payload : PropTypes . array ,
601+ label : PropTypes . string ,
602+ unit : PropTypes . string ,
603+ } ;
604+
577605export default connect (
578606 mapStateToProps ,
579607) ( ChartComponent ) ;
0 commit comments