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,
|
"zoom": 15,
|
||||||
"longitude": 7.8302,
|
"longitude": 7.8302,
|
||||||
"latitude": 47.9755
|
"latitude": 47.9755
|
||||||
}
|
},
|
||||||
|
"obsMapSource": "http://localhost:3002/data/v3.json"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -10,3 +10,7 @@
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fullScreen {
|
||||||
|
margin: none;
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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 />
|
||||||
|
|
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 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'
|
||||||
|
|
Loading…
Reference in a new issue