1- import React from 'react' ;
1+ import React , { useEffect , useRef , useState } from 'react' ;
2+ import { connect } from 'react-redux' ;
23import {
34 LineChart , Line , XAxis , YAxis , Legend , Tooltip ,
45} from 'recharts' ;
56import PropTypes from 'prop-types' ;
67import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ;
8+ import OlMap from 'ol/Map' ;
9+ import OlView from 'ol/View' ;
10+ import OlLayerGroup from 'ol/layer/Group' ;
11+ import OlLayerTile from 'ol/layer/Tile' ;
12+ import OlFeature from 'ol/Feature' ;
13+ import { fromExtent } from 'ol/geom/Polygon' ;
14+ import { Vector as OlVectorLayer } from 'ol/layer' ;
15+ import { Vector as OlVectorSource } from 'ol/source' ;
16+ import { getCenter } from 'ol/extent' ;
17+ import { inAndOut } from 'ol/easing' ;
18+ import {
19+ Fill as OlStyleFill ,
20+ Stroke as OlStyleStroke ,
21+ Style as OlStyle ,
22+ } from 'ol/style' ;
23+ import util from '../../util/util' ;
724
825function ChartComponent ( props ) {
926 const {
1027 liveData,
28+ mapView,
29+ createLayer,
30+ overviewMapLayerDef,
1131 } = props ;
1232
33+ const [ errorCollapsed , setErrorCollapsed ] = useState ( true ) ;
34+ const mapInstanceRef = useRef ( null ) ;
35+
1336 const {
1437 data,
1538 unit,
@@ -19,8 +42,13 @@ function ChartComponent (props) {
1942 isTruncated,
2043 title,
2144 numPoints,
45+ coordinates,
46+ errors,
2247 } = liveData ;
2348
49+ const errorDaysArr = errors ?. error_days ?. replaceAll ( / ( ' | \[ | \] ) / gi, '' ) . split ( ', ' ) || [ ] ;
50+ const format = util . getCoordinateFormat ( ) ;
51+
2452 // Arbitrary array of colors to use
2553 const lineColors = [ '#A3905D' , '#82CA9D' , 'orange' , 'pink' , 'green' , 'red' , 'yellow' , 'aqua' , 'maroon' ] ;
2654 const formattedUnit = unit ? ` (${ unit } )` : '' ;
@@ -182,6 +210,75 @@ function ChartComponent (props) {
182210 ) ;
183211 }
184212
213+ useEffect ( ( ) => {
214+ const boxFeature = new OlFeature ( {
215+ geometry : fromExtent ( coordinates ) ,
216+ } ) ;
217+ boxFeature . setStyle ( new OlStyle ( {
218+ stroke : new OlStyleStroke ( {
219+ color : 'rgba(255, 255, 255, .6)' ,
220+ width : 1 ,
221+ } ) ,
222+ fill : new OlStyleFill ( {
223+ color : 'rgba(255, 255, 255, .3)' ,
224+ } ) ,
225+ } ) ) ;
226+ const boxLayer = new OlVectorLayer ( {
227+ source : new OlVectorSource ( {
228+ features : [ boxFeature ] ,
229+ } ) ,
230+ } ) ;
231+
232+ const createLayerWrapper = async ( ) => {
233+ const backgroundLayerGroup = await createLayer ( overviewMapLayerDef ) ;
234+ backgroundLayerGroup . setVisible ( true ) ;
235+
236+ const layersList = [ ] ;
237+ backgroundLayerGroup . getLayers ( ) . getArray ( ) . forEach ( ( layer ) => {
238+ layersList . push ( new OlLayerTile ( {
239+ source : layer . getSource ( ) ,
240+ } ) ) ;
241+ } ) ;
242+ const copiedLayerGroup = new OlLayerGroup ( {
243+ layers : layersList ,
244+ } ) ;
245+
246+ mapInstanceRef . current = new OlMap ( {
247+ view : new OlView ( {
248+ center : mapView . getCenter ( ) ,
249+ zoom : mapView . getZoom ( ) ,
250+ projection : mapView . getProjection ( ) ,
251+ } ) ,
252+ layers : [ copiedLayerGroup , boxLayer ] ,
253+ target : 'charting-minimap-inner' ,
254+ interactions : [ ] ,
255+ } ) ;
256+
257+ const minimapView = mapInstanceRef . current . getView ( ) ;
258+ minimapView . fit ( boxFeature . getGeometry ( ) . getExtent ( ) , { padding : [ 50 , 50 , 50 , 50 ] } ) ;
259+
260+ mapInstanceRef . current . on ( 'moveend' , ( ) => {
261+ const boxCenter = getCenter ( boxFeature . getGeometry ( ) . getExtent ( ) ) ;
262+ const minimapCenter = minimapView . getCenter ( ) ;
263+ if ( boxCenter [ 0 ] === minimapCenter [ 0 ] && boxCenter [ 1 ] === minimapCenter [ 1 ] ) {
264+ return ;
265+ }
266+ minimapView . animate ( {
267+ center : boxCenter ,
268+ duration : 350 ,
269+ easing : inAndOut ,
270+ } ) ;
271+ } ) ;
272+ } ;
273+
274+ createLayerWrapper ( ) ;
275+
276+ return ( ) => {
277+ mapInstanceRef . current . setTarget ( null ) ;
278+ mapInstanceRef . current = null ;
279+ } ;
280+ } , [ overviewMapLayerDef ] ) ;
281+
185282 return (
186283 < div className = "charting-chart-container" >
187284 < div className = "charting-chart-inner" >
@@ -217,56 +314,141 @@ function ChartComponent (props) {
217314 </ LineChart >
218315 </ div >
219316 < div className = "charting-stat-text" >
220- < h3 >
221- < b >
222- Average Statistics
223- { formattedUnit }
224- </ b >
225- </ h3 >
226- < br />
227- { getQuickStatistics ( data ) }
228- </ div >
229- </ div >
230- < div className = "charting-disclaimer" >
231- < strong className = "charting-disclaimer-pre" > Note: </ strong >
232- < span > Numerical analyses performed on imagery should only be used for initial basic exploratory purposes.</ span >
233- { isTruncated
234- && (
235- < div className = "charting-disclaimer-lower" >
236- < FontAwesomeIcon
237- icon = "exclamation-triangle"
238- className = "wv-alert-icon"
239- size = "1x"
240- widthAuto
241- />
242- < i className = "charting-disclaimer-block" >
243- As part of this beta feature release, the number of data points plotted between
317+ < div id = "charting-stats-container" >
318+ < h3 >
244319 < b >
245- { ` ${ startDate } ` }
320+ Average Statistics
321+ { formattedUnit }
246322 </ b >
247- and
248- < b >
249- { ` ${ endDate } ` }
250- </ b >
251- have been reduced from
252- < b >
253- { ` ${ numRangeDays } ` }
254- </ b >
255- to
323+ </ h3 >
324+ < br />
325+ { getQuickStatistics ( data ) }
326+ </ div >
327+ < div id = "charting-minimap-container" >
328+ < div id = "charting-minimap-inner" />
329+ </ div >
330+ < div />
331+ < div id = "charting-coordinates-container" >
332+ < h3 >
256333 < b >
257- { ` ${ numPoints } ` }
334+ Coordinates
258335 </ b >
259- .
260- </ i >
336+ </ h3 >
337+ < br />
338+ < div className = "charting-coordinates-inner" >
339+ < div />
340+ < div className = "coordinate-center coordinate-subheader" > Latitude</ div >
341+ < div className = "coordinate-center coordinate-subheader" > Longitude</ div >
342+ < div > Top Right:</ div >
343+ < div className = "coordinate-mono" > { util . formatCoordinate ( [ coordinates [ 2 ] , coordinates [ 3 ] ] , format ) . split ( ', ' ) [ 0 ] } </ div >
344+ < div className = "coordinate-mono" > { util . formatCoordinate ( [ coordinates [ 2 ] , coordinates [ 3 ] ] , format ) . split ( ', ' ) [ 1 ] } </ div >
345+ < div > Bottom Left:</ div >
346+ < div className = "coordinate-mono" > { util . formatCoordinate ( [ coordinates [ 0 ] , coordinates [ 1 ] ] , format ) . split ( ', ' ) [ 0 ] } </ div >
347+ < div className = "coordinate-mono" > { util . formatCoordinate ( [ coordinates [ 0 ] , coordinates [ 1 ] ] , format ) . split ( ', ' ) [ 1 ] } </ div >
348+ </ div >
261349 </ div >
262- ) }
350+ </ div >
351+ < div className = "charting-disclaimer" >
352+ < strong className = "charting-disclaimer-pre" > Note: </ strong >
353+ < span > Numerical analyses performed on imagery should only be used for initial basic exploratory purposes.</ span >
354+ { isTruncated
355+ && (
356+ < div className = "charting-disclaimer-upper" >
357+ < FontAwesomeIcon
358+ icon = "exclamation-triangle"
359+ className = "wv-alert-icon"
360+ size = "1x"
361+ widthAuto
362+ />
363+ < i className = "charting-disclaimer-block" >
364+ As part of this beta feature release, the number of data points plotted between
365+ < b >
366+ { ` ${ startDate } ` }
367+ </ b >
368+ and
369+ < b >
370+ { ` ${ endDate } ` }
371+ </ b >
372+ have been reduced from
373+ < b >
374+ { ` ${ numRangeDays } ` }
375+ </ b >
376+ to
377+ < b >
378+ { ` ${ numPoints } ` }
379+ </ b >
380+ .
381+ </ i >
382+ </ div >
383+ ) }
384+ { errors && errors . error_count > 0
385+ && (
386+ < div className = "charting-disclaimer-lower" >
387+ < FontAwesomeIcon
388+ icon = "exclamation-triangle"
389+ className = "wv-alert-icon"
390+ size = "1x"
391+ widthAuto
392+ />
393+ < i className = "charting-disclaimer-block" >
394+ { `${ errors . error_count } ` }
395+ dates requested have no data, so are not shown in the chart.
396+ </ i >
397+ { ! errorCollapsed
398+ && (
399+ < div className = "charting-disclaimer-dates" >
400+ < i className = "charting-disclaimer-block" >
401+ { errorDaysArr . map ( ( date , index ) => (
402+ < >
403+ { date . split ( 'T' ) [ 0 ] }
404+ { index < errorDaysArr . length - 1 && ', ' }
405+
406+ </ >
407+ ) ) }
408+ </ i >
409+ </ div >
410+ ) }
411+ < div className = "error-expand-button" >
412+ < span className = "error-expand-button-inner" onClick = { ( ) => setErrorCollapsed ( ! errorCollapsed ) } >
413+ { errorCollapsed ? 'more' : 'less' }
414+ < FontAwesomeIcon
415+ className = "layer-group-collapse"
416+ icon = { ! errorCollapsed ? 'caret-up' : 'caret-down' }
417+ widthAuto
418+ />
419+ </ span >
420+ </ div >
421+ </ div >
422+ ) }
423+ </ div >
263424 </ div >
264425 </ div >
265426 ) ;
266427}
267428
429+ const mapStateToProps = ( state ) => {
430+ const {
431+ map,
432+ layers,
433+ } = state ;
434+
435+ const {
436+ ui,
437+ } = map ;
438+
439+ const layerId = 'Coastlines_15m' ;
440+
441+ return {
442+ mapView : ui . selected . getView ( ) ,
443+ createLayer : ui . createLayer ,
444+ overviewMapLayerDef : layers . layerConfig [ layerId ] ,
445+ } ;
446+ } ;
447+
268448ChartComponent . propTypes = {
269449 liveData : PropTypes . object ,
270450} ;
271451
272- export default ChartComponent ;
452+ export default connect (
453+ mapStateToProps ,
454+ ) ( ChartComponent ) ;
0 commit comments