frontend: add MapPage and make OBS data vector tiles URL configurable

This commit is contained in:
Paul Bienkowski 2021-10-27 17:32:07 +02:00
parent ec60fc3873
commit 79f3469df8
11 changed files with 86 additions and 15 deletions

View file

@ -17,5 +17,6 @@
"zoom": 15, "zoom": 15,
"longitude": 7.8302, "longitude": 7.8302,
"latitude": 47.9755 "latitude": 47.9755
} },
"obsMapSource": "http://localhost:3002/data/v3.json"
} }

View file

@ -17,5 +17,6 @@
"zoom": 15, "zoom": 15,
"longitude": 9.1797, "longitude": 9.1797,
"latitude": 48.7784 "latitude": 48.7784
} },
"obsMapSource": "http://api.example.com/tileserver/data/v3.json"
} }

View file

@ -14,6 +14,7 @@ import {
LoginRedirectPage, LoginRedirectPage,
LogoutPage, LogoutPage,
NotFoundPage, NotFoundPage,
MapPage,
SettingsPage, SettingsPage,
TrackEditor, TrackEditor,
TrackPage, TrackPage,
@ -49,6 +50,10 @@ const App = connect((state) => ({login: state.login}))(function App({login}) {
OpenBikeSensor OpenBikeSensor
</Link> </Link>
{config?.obsMapSource && <Link component={MenuItemForLink} to="/map" as="a">
Map
</Link>}
<Link component={MenuItemForLink} to="/tracks" as="a"> <Link component={MenuItemForLink} to="/tracks" as="a">
Tracks Tracks
</Link> </Link>
@ -76,6 +81,9 @@ const App = connect((state) => ({login: state.login}))(function App({login}) {
<Route path="/" exact> <Route path="/" exact>
<HomePage /> <HomePage />
</Route> </Route>
<Route path="/map" exact>
<MapPage />
</Route>
<Route path="/tracks" exact> <Route path="/tracks" exact>
<TracksPage /> <TracksPage />
</Route> </Route>

View file

@ -10,3 +10,7 @@
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
.fullScreen {
margin: none;
}

View file

@ -4,10 +4,10 @@ import {Container} from 'semantic-ui-react'
import styles from './Page.module.scss' import styles from './Page.module.scss'
export default function Page({small, children}: {small?: boolean, children: ReactNode}) { export default function Page({small, children, fullScreen}: {small?: boolean, children: ReactNode, fullScreen?: boolean}) {
return ( return (
<main className={classnames(styles.page, small && styles.small)}> <main className={classnames({page: true, small, fullScreen})}>
<Container>{children}</Container> {fullScreen ? children : <Container>{children}</Container>}
</main> </main>
) )
} }

View file

@ -1,12 +1,13 @@
import React from 'react' import React from 'react'
interface Config { export interface Config {
apiUrl: string apiUrl: string
mapHome: { mapHome: {
latitude: number latitude: number
longitude: number longitude: number
zoom: number zoom: number
} }
obsMapSource?: string
} }
async function loadConfig(): Promise<Config> { async function loadConfig(): Promise<Config> {

View file

@ -6,6 +6,8 @@ import 'semantic-ui-less/semantic.less'
import './index.css' import './index.css'
import App from './App' import App from './App'
import 'maplibre-gl/dist/maplibre-gl.css'
import {Provider} from 'react-redux' import {Provider} from 'react-redux'
import store from './store' import store from './store'

View file

@ -12,33 +12,32 @@ import {useConfig} from 'config'
import {TrackListItem} from './TracksPage' import {TrackListItem} from './TracksPage'
import styles from './HomePage.module.scss' import styles from './HomePage.module.scss'
import 'ol/ol.css'; import 'ol/ol.css'
import {obsRoads} from '../mapstyles' import {obsRoads} from '../mapstyles'
import ReactMapGl from 'react-map-gl' import ReactMapGl from 'react-map-gl'
function WelcomeMap() { function WelcomeMap({mapSource}: {mapSource: string}) {
const mapStyle = React.useMemo(() => obsRoads(mapSource), [mapSource])
const config = useConfig() const config = useConfig()
const mapStyle = React.useMemo(() => obsRoads(), [])
const [viewport, setViewport] = React.useState({ const [viewport, setViewport] = React.useState({
longitude: 0, longitude: 0,
latitude: 0, latitude: 0,
zoom: 0, zoom: 0,
}); })
React.useEffect(() => { React.useEffect(() => {
if (config?.mapHome) { if (config?.mapHome) {
setViewport(config.mapHome) setViewport(config.mapHome)
} }
}, [config]) }, [config])
return ( return (
<div className={styles.welcomeMap}> <div className={styles.welcomeMap}>
<ReactMapGl mapStyle={mapStyle} width="100%" height="100%" onViewportChange={setViewport } {...viewport } /> <ReactMapGl mapStyle={mapStyle} width="100%" height="100%" onViewportChange={setViewport} {...viewport} />
</div> </div>
) )
} }
function MostRecentTrack() { function MostRecentTrack() {
const track: Track | null = useObservable( const track: Track | null = useObservable(
() => () =>
@ -68,12 +67,14 @@ function MostRecentTrack() {
} }
export default function HomePage() { export default function HomePage() {
const {obsMapSource: mapSource} = useConfig() || {}
return ( return (
<Page> <Page>
<Grid stackable> <Grid stackable>
<Grid.Row> <Grid.Row>
<Grid.Column width={10}> <Grid.Column width={10}>
<WelcomeMap /> {mapSource ? <WelcomeMap {...{mapSource}} /> : null}
</Grid.Column> </Grid.Column>
<Grid.Column width={6}> <Grid.Column width={6}>
<Stats /> <Stats />

View file

@ -0,0 +1,5 @@
.mapContainer {
height: calc(100vh - 60px);
background: red;
position: relative;
}

View file

@ -0,0 +1,47 @@
import React from 'react'
// import {Grid, Loader, Header, Item} from 'semantic-ui-react'
// import api from 'api'
import {Page} from 'components'
import {useConfig, Config} from 'config'
import styles from './MapPage.module.scss'
import 'ol/ol.css'
import {obsRoads} from '../mapstyles'
import ReactMapGl from 'react-map-gl'
function BigMap({mapSource, config}: {mapSource: string ,config: Config}) {
const mapStyle = React.useMemo(() => obsRoads(mapSource), [mapSource])
const [viewport, setViewport] = React.useState({
longitude: 0,
latitude: 0,
zoom: 0,
})
React.useEffect(() => {
if (config?.mapHome) {
setViewport(config.mapHome)
}
}, [config])
return (
<div className={styles.mapContainer}>
<ReactMapGl mapStyle={mapStyle} width="100%" height="100%" onViewportChange={setViewport} {...viewport} />
</div>
)
}
export default function MapPage() {
const config = useConfig() || {}
if (!config) return null;
const {obsMapSource: mapSource} = config
if (!mapSource) return null;
return (
<Page fullScreen>
<BigMap {...{mapSource, config}} />
</Page>
)
}

View file

@ -1,6 +1,7 @@
export {default as HomePage} from './HomePage' export {default as HomePage} from './HomePage'
export {default as LoginRedirectPage} from './LoginRedirectPage' export {default as LoginRedirectPage} from './LoginRedirectPage'
export {default as LogoutPage} from './LogoutPage' export {default as LogoutPage} from './LogoutPage'
export {default as MapPage} from './MapPage'
export {default as NotFoundPage} from './NotFoundPage' export {default as NotFoundPage} from './NotFoundPage'
export {default as SettingsPage} from './SettingsPage' export {default as SettingsPage} from './SettingsPage'
export {default as TrackEditor} from './TrackEditor' export {default as TrackEditor} from './TrackEditor'