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,
"longitude": 7.8302,
"latitude": 47.9755
}
},
"obsMapSource": "http://localhost:3002/data/v3.json"
}

View file

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

View file

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

View file

@ -10,3 +10,7 @@
margin-left: 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'
export default function Page({small, children}: {small?: boolean, children: ReactNode}) {
export default function Page({small, children, fullScreen}: {small?: boolean, children: ReactNode, fullScreen?: boolean}) {
return (
<main className={classnames(styles.page, small && styles.small)}>
<Container>{children}</Container>
<main className={classnames({page: true, small, fullScreen})}>
{fullScreen ? children : <Container>{children}</Container>}
</main>
)
}

View file

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

View file

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

View file

@ -12,33 +12,32 @@ import {useConfig} from 'config'
import {TrackListItem} from './TracksPage'
import styles from './HomePage.module.scss'
import 'ol/ol.css';
import 'ol/ol.css'
import {obsRoads} from '../mapstyles'
import ReactMapGl from 'react-map-gl'
function WelcomeMap() {
function WelcomeMap({mapSource}: {mapSource: string}) {
const mapStyle = React.useMemo(() => obsRoads(mapSource), [mapSource])
const config = useConfig()
const mapStyle = React.useMemo(() => obsRoads(), [])
const [viewport, setViewport] = React.useState({
longitude: 0,
latitude: 0,
zoom: 0,
});
})
React.useEffect(() => {
if (config?.mapHome) {
setViewport(config.mapHome)
if (config?.mapHome) {
setViewport(config.mapHome)
}
}, [config])
return (
<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>
)
}
function MostRecentTrack() {
const track: Track | null = useObservable(
() =>
@ -68,12 +67,14 @@ function MostRecentTrack() {
}
export default function HomePage() {
const {obsMapSource: mapSource} = useConfig() || {}
return (
<Page>
<Grid stackable>
<Grid.Row>
<Grid.Column width={10}>
<WelcomeMap />
{mapSource ? <WelcomeMap {...{mapSource}} /> : null}
</Grid.Column>
<Grid.Column width={6}>
<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 LoginRedirectPage} from './LoginRedirectPage'
export {default as LogoutPage} from './LogoutPage'
export {default as MapPage} from './MapPage'
export {default as NotFoundPage} from './NotFoundPage'
export {default as SettingsPage} from './SettingsPage'
export {default as TrackEditor} from './TrackEditor'