Add date-range filters to map
This commit is contained in:
parent
7716da8844
commit
e0cb36565a
|
@ -1,5 +1,7 @@
|
||||||
from gzip import decompress
|
from gzip import decompress
|
||||||
from sqlite3 import connect
|
from sqlite3 import connect
|
||||||
|
|
||||||
|
import dateutil.parser
|
||||||
from sanic.exceptions import Forbidden
|
from sanic.exceptions import Forbidden
|
||||||
from sanic.response import raw
|
from sanic.response import raw
|
||||||
|
|
||||||
|
@ -46,6 +48,10 @@ async def tiles(req, zoom: int, x: int, y: str):
|
||||||
raise Forbidden()
|
raise Forbidden()
|
||||||
user_id = req.ctx.user.id
|
user_id = req.ctx.user.id
|
||||||
|
|
||||||
|
parse_date = lambda s: dateutil.parser.parse(s)
|
||||||
|
start = req.ctx.get_single_arg("start", default=None, convert=parse_date)
|
||||||
|
end = req.ctx.get_single_arg("end", default=None, convert=parse_date)
|
||||||
|
|
||||||
tile = await req.ctx.db.scalar(
|
tile = await req.ctx.db.scalar(
|
||||||
text(
|
text(
|
||||||
f"select data from getmvt(:zoom, :x, :y, :user_id, :min_time, :max_time) as b(data, key);"
|
f"select data from getmvt(:zoom, :x, :y, :user_id, :min_time, :max_time) as b(data, key);"
|
||||||
|
@ -54,8 +60,8 @@ async def tiles(req, zoom: int, x: int, y: str):
|
||||||
x=int(x),
|
x=int(x),
|
||||||
y=int(y),
|
y=int(y),
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
min_time=None,
|
min_time=start,
|
||||||
max_time=None,
|
max_time=end,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,12 @@ const ROAD_ATTRIBUTE_OPTIONS = [
|
||||||
"zone",
|
"zone",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const DATE_FILTER_MODES = [
|
||||||
|
{ value: "none", key: "none", text: "All time" },
|
||||||
|
{ value: "range", key: "range", text: "Start and end range" },
|
||||||
|
{ value: "threshold", key: "threshold", text: "Before/after comparison" },
|
||||||
|
];
|
||||||
|
|
||||||
type User = Object;
|
type User = Object;
|
||||||
|
|
||||||
function LayerSidebar({
|
function LayerSidebar({
|
||||||
|
@ -48,7 +54,13 @@ function LayerSidebar({
|
||||||
baseMap: { style },
|
baseMap: { style },
|
||||||
obsRoads: { show: showRoads, showUntagged, attribute, maxCount },
|
obsRoads: { show: showRoads, showUntagged, attribute, maxCount },
|
||||||
obsEvents: { show: showEvents },
|
obsEvents: { show: showEvents },
|
||||||
filters: { currentUser: filtersCurrentUser },
|
filters: {
|
||||||
|
currentUser: filtersCurrentUser,
|
||||||
|
dateMode,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
thresholdAfter,
|
||||||
|
},
|
||||||
} = mapConfig;
|
} = mapConfig;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -209,11 +221,18 @@ function LayerSidebar({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Header as="h4" style={{ marginBottom: 8 }}>
|
<Header as="h4">Filters</Header>
|
||||||
Filter
|
</List.Item>
|
||||||
</Header>
|
|
||||||
{login && (
|
{login && (
|
||||||
|
<>
|
||||||
|
<List.Item>
|
||||||
|
<Header as="h5">User data</Header>
|
||||||
|
</List.Item>
|
||||||
|
|
||||||
|
<List.Item>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
toggle
|
toggle
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -224,9 +243,91 @@ function LayerSidebar({
|
||||||
}
|
}
|
||||||
label="Show only my own data"
|
label="Show only my own data"
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{!login && <div>No filters available without login.</div>}
|
|
||||||
</List.Item>
|
</List.Item>
|
||||||
|
|
||||||
|
<List.Item>
|
||||||
|
<Header as="h5">Date range</Header>
|
||||||
|
</List.Item>
|
||||||
|
|
||||||
|
<List.Item>
|
||||||
|
<Select
|
||||||
|
id="filters.dateMode"
|
||||||
|
options={DATE_FILTER_MODES}
|
||||||
|
value={dateMode ?? "none"}
|
||||||
|
onChange={(_e, { value }) =>
|
||||||
|
setMapConfigFlag("filters.dateMode", value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
|
||||||
|
{dateMode == "range" && (
|
||||||
|
<List.Item>
|
||||||
|
<Input
|
||||||
|
type="date"
|
||||||
|
size="small"
|
||||||
|
id="filters.startDate"
|
||||||
|
onChange={(_e, { value }) =>
|
||||||
|
setMapConfigFlag("filters.startDate", value)
|
||||||
|
}
|
||||||
|
value={startDate ?? null}
|
||||||
|
label="Start"
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{dateMode == "range" && (
|
||||||
|
<List.Item>
|
||||||
|
<Input
|
||||||
|
type="date"
|
||||||
|
size="small"
|
||||||
|
id="filters.endDate"
|
||||||
|
onChange={(_e, { value }) =>
|
||||||
|
setMapConfigFlag("filters.endDate", value)
|
||||||
|
}
|
||||||
|
value={endDate ?? null}
|
||||||
|
label="End"
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{dateMode == "threshold" && (
|
||||||
|
<List.Item>
|
||||||
|
<Input
|
||||||
|
type="date"
|
||||||
|
size="small"
|
||||||
|
id="filters.startDate"
|
||||||
|
value={startDate ?? null}
|
||||||
|
onChange={(_e, { value }) =>
|
||||||
|
setMapConfigFlag("filters.startDate", value)
|
||||||
|
}
|
||||||
|
label="Threshold"
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{dateMode == "threshold" && (
|
||||||
|
<List.Item>
|
||||||
|
<span>
|
||||||
|
Before{" "}
|
||||||
|
<Checkbox
|
||||||
|
toggle
|
||||||
|
size="small"
|
||||||
|
checked={thresholdAfter ?? false}
|
||||||
|
onChange={() =>
|
||||||
|
setMapConfigFlag(
|
||||||
|
"filters.thresholdAfter",
|
||||||
|
!thresholdAfter
|
||||||
|
)
|
||||||
|
}
|
||||||
|
id="filters.thresholdAfter"
|
||||||
|
/>{" "}
|
||||||
|
After
|
||||||
|
</span>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{!login && <List.Item>No filters available without login.</List.Item>}
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -160,9 +160,29 @@ function MapPage({ login }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const tiles = obsMapSource?.tiles?.map(
|
const tiles = obsMapSource?.tiles?.map(
|
||||||
(tileUrl: string) =>
|
(tileUrl: string) => {
|
||||||
tileUrl +
|
const query = new URLSearchParams()
|
||||||
(login && mapConfig.filters.currentUser ? `?user=${login.username}` : "")
|
if (login) {
|
||||||
|
if (mapConfig.filters.currentUser) {
|
||||||
|
query.append('user', login.username)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapConfig.filters.dateMode === "range") {
|
||||||
|
if (mapConfig.filters.startDate) {
|
||||||
|
query.append('start', mapConfig.filters.startDate)
|
||||||
|
}
|
||||||
|
if (mapConfig.filters.endDate) {
|
||||||
|
query.append('end', mapConfig.filters.endDate)
|
||||||
|
}
|
||||||
|
} else if (mapConfig.filters.dateMode === "threshold") {
|
||||||
|
if (mapConfig.filters.startDate) {
|
||||||
|
query.append(mapConfig.filters.thresholdAfter ? 'start' : 'end', mapConfig.filters.startDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const queryString = String(query)
|
||||||
|
return tileUrl + (queryString ? '?' : '') + queryString
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -29,6 +29,10 @@ export type MapConfig = {
|
||||||
};
|
};
|
||||||
filters: {
|
filters: {
|
||||||
currentUser: boolean;
|
currentUser: boolean;
|
||||||
|
dateMode: "none" | "range" | "threshold";
|
||||||
|
startDate?: null | string;
|
||||||
|
endDate?: null | string;
|
||||||
|
thresholdAfter?: null | boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,6 +51,10 @@ export const initialState: MapConfig = {
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
currentUser: false,
|
currentUser: false,
|
||||||
|
dateMode: "none",
|
||||||
|
startDate: null,
|
||||||
|
endDate: null,
|
||||||
|
thresholdAfter: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue