77 Skeleton ,
88 Button ,
99 Typography ,
10+ Fab ,
1011} from '@mui/material' ;
12+ import { Link } from 'react-router-dom' ;
1113import MapIcon from '@mui/icons-material/Map' ;
1214import TravelExploreIcon from '@mui/icons-material/TravelExplore' ;
1315import { ContentBox } from './ContentBox' ;
@@ -28,6 +30,10 @@ import { computeBoundingBox } from '../screens/Feed/Feed.functions';
2830import { displayFormattedDate } from '../utils/date' ;
2931import { useSelector } from 'react-redux' ;
3032import { selectAutodiscoveryGbfsVersion } from '../store/feed-selectors' ;
33+ import ModeOfTravelIcon from '@mui/icons-material/ModeOfTravel' ;
34+ import { GtfsVisualizationMap } from './GtfsVisualizationMap' ;
35+ import ZoomOutMapIcon from '@mui/icons-material/ZoomOutMap' ;
36+ import { useRemoteConfig } from '../context/RemoteConfigProvider' ;
3137
3238interface CoveredAreaMapProps {
3339 boundingBox ?: LatLngExpression [ ] ;
@@ -50,22 +56,26 @@ export const fetchGeoJson = async (
5056 return await response . json ( ) ;
5157} ;
5258
59+ type MapViews =
60+ | 'boundingBoxView'
61+ | 'detailedCoveredAreaView'
62+ | 'gtfsVisualizationView' ;
63+
5364const CoveredAreaMap : React . FC < CoveredAreaMapProps > = ( {
5465 boundingBox,
5566 latestDataset,
5667 feed,
5768} ) => {
5869 const { t } = useTranslation ( 'feeds' ) ;
5970 const theme = useTheme ( ) ;
71+ const { config } = useRemoteConfig ( ) ;
6072
6173 const [ geoJsonData , setGeoJsonData ] = useState <
6274 GeoJSONData | GeoJSONDataGBFS | null
6375 > ( null ) ;
6476 const [ geoJsonError , setGeoJsonError ] = useState ( false ) ;
6577 const [ geoJsonLoading , setGeoJsonLoading ] = useState ( false ) ;
66- const [ view , setView ] = useState <
67- 'boundingBoxView' | 'detailedCoveredAreaView'
68- > ( 'detailedCoveredAreaView' ) ;
78+ const [ view , setView ] = useState < MapViews > ( 'detailedCoveredAreaView' ) ;
6979 const latestGbfsVersion = useSelector ( selectAutodiscoveryGbfsVersion ) ;
7080
7181 const getAndSetGeoJsonData = ( urlToExtract : string ) : void => {
@@ -112,7 +122,7 @@ const CoveredAreaMap: React.FC<CoveredAreaMapProps> = ({
112122
113123 const handleViewChange = (
114124 _ : React . MouseEvent < HTMLElement > ,
115- newView : 'boundingBoxView' | 'detailedCoveredAreaView' | null ,
125+ newView : MapViews | null ,
116126 ) : void => {
117127 if ( newView !== null ) setView ( newView ) ;
118128 } ;
@@ -132,29 +142,51 @@ const CoveredAreaMap: React.FC<CoveredAreaMapProps> = ({
132142 const renderMap = ( ) : JSX . Element => {
133143 const displayBoundingBoxMap =
134144 view === 'boundingBoxView' && feed ?. data_type === 'gtfs' ;
145+
146+ const displayGtfsVisualizationView =
147+ view === 'gtfsVisualizationView' &&
148+ feed ?. data_type === 'gtfs' &&
149+ config . enableGtfsVisualizationMap ;
135150 let gbfsBoundingBox : LatLngExpression [ ] = [ ] ;
136151 if ( feed ?. data_type === 'gbfs' ) {
137152 gbfsBoundingBox = computeBoundingBox ( geoJsonData ) ?? [ ] ;
138153 if ( gbfsBoundingBox . length === 0 ) {
139154 setGeoJsonError ( true ) ;
140155 }
141156 }
142- return (
143- < >
144- { displayBoundingBoxMap ? (
145- < Map polygon = { boundingBox ?? [ ] } />
146- ) : (
147- < MapGeoJSON
148- geoJSONData = { geoJsonData }
149- polygon = { boundingBox ?? gbfsBoundingBox }
150- displayMapDetails = { feed ?. data_type === 'gtfs' }
157+
158+ if ( displayBoundingBoxMap ) {
159+ return < Map polygon = { boundingBox ?? [ ] } /> ;
160+ }
161+
162+ if ( displayGtfsVisualizationView ) {
163+ return (
164+ < >
165+ < Fab
166+ size = 'small'
167+ sx = { { position : 'absolute' , top : 16 , right : 16 } }
168+ component = { Link }
169+ to = './map'
170+ >
171+ < ZoomOutMapIcon > </ ZoomOutMapIcon >
172+ </ Fab >
173+ < GtfsVisualizationMap
174+ polygon = { boundingBox ?? [ ] }
175+ latestDataset = { latestDataset }
151176 />
152- ) }
153- </ >
177+ </ >
178+ ) ;
179+ }
180+
181+ return (
182+ < MapGeoJSON
183+ geoJSONData = { geoJsonData }
184+ polygon = { boundingBox ?? gbfsBoundingBox }
185+ displayMapDetails = { feed ?. data_type === 'gtfs' }
186+ />
154187 ) ;
155188 } ;
156189
157- const mapDisplayError = boundingBox == undefined && geoJsonError ;
158190 const latestAutodiscoveryUrl = getGbfsLatestVersionVisualizationUrl (
159191 feed as GBFSFeedType ,
160192 ) ;
@@ -166,73 +198,95 @@ const CoveredAreaMap: React.FC<CoveredAreaMapProps> = ({
166198 flexDirection : 'column' ,
167199 maxHeight : {
168200 xs : '100%' ,
169- md : '70vh' ,
201+ md : '70vh' , // TODO: optimize this
170202 } ,
171203 minHeight : '50vh' ,
172204 } }
173- title = { mapDisplayError ? '' : t ( 'coveredAreaTitle' ) + ' - ' + t ( view ) }
205+ title = { t ( 'coveredAreaTitle' ) + ' - ' + t ( view ) }
174206 width = { { xs : '100%' } }
175207 outlineColor = { theme . palette . primary . dark }
176208 padding = { 2 }
177- action = {
178- < >
179- { feed ?. data_type === 'gbfs' ? (
180- < Box sx = { { textAlign : 'right' } } >
181- { latestAutodiscoveryUrl != undefined && (
182- < Button
183- href = { latestAutodiscoveryUrl }
184- target = '_blank'
185- rel = 'noreferrer'
186- endIcon = { < OpenInNew /> }
187- >
188- { t ( 'viewRealtimeVisualization' ) }
189- </ Button >
190- ) }
191- { ( geoJsonData as GeoJSONDataGBFS ) ?. extracted_at != undefined && (
192- < Typography
193- variant = 'caption'
194- color = 'text.secondary'
195- sx = { { display : 'block' , px : 1 } }
196- >
197- { t ( 'common:updated' ) } :{ ' ' }
198- { displayFormattedDate (
199- ( geoJsonData as GeoJSONDataGBFS ) . extracted_at ,
200- ) }
201- </ Typography >
202- ) }
203- </ Box >
204- ) : (
205- < ToggleButtonGroup
206- value = { view }
207- color = 'primary'
208- exclusive
209- aria-label = 'map view selection'
210- onChange = { handleViewChange }
211- >
212- < Tooltip title = { t ( 'detailedCoveredAreaViewTooltip' ) } >
213- < ToggleButton
214- value = 'detailedCoveredAreaView'
215- disabled = {
216- geoJsonLoading || geoJsonError || boundingBox === undefined
217- }
218- aria-label = 'Detailed Covered Area View'
219- >
220- < TravelExploreIcon />
221- </ ToggleButton >
222- </ Tooltip >
223- < Tooltip title = { t ( 'boundingBoxViewTooltip' ) } >
209+ >
210+ < Box
211+ display = { 'flex' }
212+ justifyContent = {
213+ view === 'gtfsVisualizationView' ? 'space-between' : 'flex-end'
214+ }
215+ mb = { 1 }
216+ alignItems = { 'center' }
217+ >
218+ { view === 'gtfsVisualizationView' &&
219+ config . enableGtfsVisualizationMap && (
220+ < Button component = { Link } to = './map' >
221+ Open Full Map with Filters
222+ </ Button >
223+ ) }
224+ { feed ?. data_type === 'gbfs' ? (
225+ < Box sx = { { textAlign : 'right' } } >
226+ { latestAutodiscoveryUrl != undefined && (
227+ < Button
228+ href = { latestAutodiscoveryUrl }
229+ target = '_blank'
230+ rel = 'noreferrer'
231+ endIcon = { < OpenInNew /> }
232+ >
233+ { t ( 'viewRealtimeVisualization' ) }
234+ </ Button >
235+ ) }
236+ { ( geoJsonData as GeoJSONDataGBFS ) ?. extracted_at != undefined && (
237+ < Typography
238+ variant = 'caption'
239+ color = 'text.secondary'
240+ sx = { { display : 'block' , px : 1 } }
241+ >
242+ { t ( 'common:updated' ) } :{ ' ' }
243+ { displayFormattedDate (
244+ ( geoJsonData as GeoJSONDataGBFS ) . extracted_at ,
245+ ) }
246+ </ Typography >
247+ ) }
248+ </ Box >
249+ ) : (
250+ < ToggleButtonGroup
251+ value = { view }
252+ color = 'primary'
253+ exclusive
254+ aria-label = 'map view selection'
255+ onChange = { handleViewChange }
256+ >
257+ < Tooltip title = { t ( 'detailedCoveredAreaViewTooltip' ) } >
258+ < ToggleButton
259+ value = 'detailedCoveredAreaView'
260+ disabled = {
261+ geoJsonLoading || geoJsonError || boundingBox === undefined
262+ }
263+ aria-label = 'Detailed Covered Area View'
264+ >
265+ < TravelExploreIcon />
266+ </ ToggleButton >
267+ </ Tooltip >
268+ < Tooltip title = { t ( 'boundingBoxViewTooltip' ) } >
269+ < ToggleButton
270+ value = 'boundingBoxView'
271+ aria-label = 'Bounding Box View'
272+ >
273+ < MapIcon />
274+ </ ToggleButton >
275+ </ Tooltip >
276+ { config . enableGtfsVisualizationMap && (
277+ < Tooltip title = { t ( 'gtfsVisualizationTooltip' ) } >
224278 < ToggleButton
225- value = 'boundingBoxView'
279+ value = 'gtfsVisualizationView'
280+ disabled = { ! config . enableGtfsVisualizationMap }
226281 aria-label = 'Bounding Box View'
227282 >
228- < MapIcon />
283+ < ModeOfTravelIcon />
229284 </ ToggleButton >
230285 </ Tooltip >
231- </ ToggleButtonGroup >
232- ) }
233- </ >
234- }
235- >
286+ ) }
287+ </ ToggleButtonGroup >
288+ ) }
289+ </ Box >
236290 { feed ?. data_type === 'gtfs' &&
237291 boundingBox === undefined &&
238292 view === 'boundingBoxView' && (
0 commit comments