frontend: add MapPage and make OBS data vector tiles URL configurable
This commit is contained in:
parent
ec60fc3873
commit
79f3469df8
|
@ -17,5 +17,6 @@
|
|||
"zoom": 15,
|
||||
"longitude": 7.8302,
|
||||
"latitude": 47.9755
|
||||
}
|
||||
},
|
||||
"obsMapSource": "http://localhost:3002/data/v3.json"
|
||||
}
|
||||
|
|
|
@ -17,5 +17,6 @@
|
|||
"zoom": 15,
|
||||
"longitude": 9.1797,
|
||||
"latitude": 48.7784
|
||||
}
|
||||
},
|
||||
"obsMapSource": "http://api.example.com/tileserver/data/v3.json"
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -10,3 +10,7 @@
|
|||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.fullScreen {
|
||||
margin: none;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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 />
|
||||
|
|
5
frontend/src/pages/MapPage.module.scss
Normal file
5
frontend/src/pages/MapPage.module.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
.mapContainer {
|
||||
height: calc(100vh - 60px);
|
||||
background: red;
|
||||
position: relative;
|
||||
}
|
47
frontend/src/pages/MapPage.tsx
Normal file
47
frontend/src/pages/MapPage.tsx
Normal 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>
|
||||
)
|
||||
}
|
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue