Add exports page
This commit is contained in:
parent
8135d4ed51
commit
a884ac88d8
4 changed files with 182 additions and 0 deletions
|
@ -1,5 +1,11 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.4.1 (TBD)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add page for exporting data through web frontend
|
||||||
|
|
||||||
## 0.4.0
|
## 0.4.0
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {useConfig} from 'config'
|
||||||
import styles from './App.module.less'
|
import styles from './App.module.less'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ExportPage,
|
||||||
HomePage,
|
HomePage,
|
||||||
LoginRedirectPage,
|
LoginRedirectPage,
|
||||||
LogoutPage,
|
LogoutPage,
|
||||||
|
@ -84,6 +85,10 @@ const App = connect((state) => ({login: state.login}))(function App({login}) {
|
||||||
Tracks
|
Tracks
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<Link component={MenuItemForLink} to="/export" as="a">
|
||||||
|
Export
|
||||||
|
</Link>
|
||||||
|
|
||||||
<Menu.Menu position="right">
|
<Menu.Menu position="right">
|
||||||
{login ? (
|
{login ? (
|
||||||
<>
|
<>
|
||||||
|
@ -127,6 +132,9 @@ const App = connect((state) => ({login: state.login}))(function App({login}) {
|
||||||
<Route path={`/tracks/:slug/edit`} exact>
|
<Route path={`/tracks/:slug/edit`} exact>
|
||||||
<TrackEditor />
|
<TrackEditor />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/export" exact>
|
||||||
|
<ExportPage />
|
||||||
|
</Route>
|
||||||
<Route path="/redirect" exact>
|
<Route path="/redirect" exact>
|
||||||
<LoginRedirectPage />
|
<LoginRedirectPage />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
167
frontend/src/pages/ExportPage/index.tsx
Normal file
167
frontend/src/pages/ExportPage/index.tsx
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
import React, { useState, useCallback, useMemo } from "react";
|
||||||
|
import { Source, Layer } from "react-map-gl";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { Button, Form, Dropdown, Header } from "semantic-ui-react";
|
||||||
|
|
||||||
|
import { useConfig } from "config";
|
||||||
|
import { Page, Map } from "components";
|
||||||
|
|
||||||
|
const BoundingBoxSelector = React.forwardRef(
|
||||||
|
({ value, name, onChange }, ref) => {
|
||||||
|
const [pointNum, setPointNum] = useState(0);
|
||||||
|
const [point0, setPoint0] = useState(null);
|
||||||
|
const [point1, setPoint1] = useState(null);
|
||||||
|
|
||||||
|
const onClick = (e) => {
|
||||||
|
if (pointNum == 0) {
|
||||||
|
setPoint0(e.lngLat);
|
||||||
|
} else {
|
||||||
|
setPoint1(e.lngLat);
|
||||||
|
}
|
||||||
|
setPointNum(1 - pointNum);
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!point0 || !point1) return;
|
||||||
|
const bbox = `${point0[0]},${point0[1]},${point1[0]},${point1[1]}`;
|
||||||
|
if (bbox !== value) {
|
||||||
|
onChange(bbox);
|
||||||
|
}
|
||||||
|
}, [point0, point1]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const [p00, p01, p10, p11] = value
|
||||||
|
.split(",")
|
||||||
|
.map((v) => Number.parseFloat(v));
|
||||||
|
if (!point0 || point0[0] != p00 || point0[1] != p01)
|
||||||
|
setPoint0([p00, p01]);
|
||||||
|
if (!point1 || point1[0] != p10 || point1[1] != p11)
|
||||||
|
setPoint1([p10, p11]);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Form.Input label="Bounding box" {...{ name, value, onChange }} />
|
||||||
|
|
||||||
|
<div style={{ height: 400, position: "relative", marginBottom: 16 }}>
|
||||||
|
<Map onClick={onClick}>
|
||||||
|
<Source
|
||||||
|
id="bbox"
|
||||||
|
type="geojson"
|
||||||
|
data={
|
||||||
|
point0 && point1
|
||||||
|
? {
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
type: "Feature",
|
||||||
|
geometry: {
|
||||||
|
type: "Polygon",
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[point0[0], point0[1]],
|
||||||
|
[point1[0], point0[1]],
|
||||||
|
[point1[0], point1[1]],
|
||||||
|
[point0[0], point1[1]],
|
||||||
|
[point0[0], point0[1]],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Layer
|
||||||
|
id="bbox"
|
||||||
|
type="line"
|
||||||
|
paint={{
|
||||||
|
"line-width": 4,
|
||||||
|
"line-color": "#F06292",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Source>
|
||||||
|
</Map>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const MODES = [
|
||||||
|
{
|
||||||
|
key: "events",
|
||||||
|
text: "Events",
|
||||||
|
value: "events",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const FORMATS = [
|
||||||
|
{
|
||||||
|
key: "geojson",
|
||||||
|
text: "GeoJSON",
|
||||||
|
value: "geojson",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "shapefile",
|
||||||
|
text: "Shapefile (ZIP)",
|
||||||
|
value: "shapefile",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function ExportPage() {
|
||||||
|
const [mode, setMode] = useState("events");
|
||||||
|
const [bbox, setBbox] = useState("8.294678,49.651182,9.059601,50.108249");
|
||||||
|
const [fmt, setFmt] = useState("geojson");
|
||||||
|
const config = useConfig();
|
||||||
|
const exportUrl = `${config?.apiUrl}/export/events?bbox=${bbox}&fmt=${fmt}`;
|
||||||
|
return (
|
||||||
|
<Page>
|
||||||
|
<Header as="h2">Export</Header>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page allows you to export parts of the public dataset. Please note
|
||||||
|
the license.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Form>
|
||||||
|
<Form.Field>
|
||||||
|
<label>Mode</label>
|
||||||
|
<Dropdown
|
||||||
|
placeholder="Select mode"
|
||||||
|
fluid
|
||||||
|
selection
|
||||||
|
options={MODES}
|
||||||
|
value={mode}
|
||||||
|
onChange={(_e, { value }) => setMode(value)}
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
|
||||||
|
<Form.Field>
|
||||||
|
<label>Format</label>
|
||||||
|
<Dropdown
|
||||||
|
placeholder="Select format"
|
||||||
|
fluid
|
||||||
|
selection
|
||||||
|
options={FORMATS}
|
||||||
|
value={fmt}
|
||||||
|
onChange={(_e, { value }) => setFmt(value)}
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
|
||||||
|
<BoundingBoxSelector value={bbox} onChange={setBbox} />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
primary
|
||||||
|
as="a"
|
||||||
|
href={exportUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
>
|
||||||
|
Export
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
export {default as ExportPage} from './ExportPage'
|
||||||
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'
|
||||||
|
|
Loading…
Add table
Reference in a new issue