Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more consistent usage of precision trait & other formatting refactors #231

Merged
merged 3 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions client/src/components/buttons/Download.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import Button, { type ButtonProps } from '@mui/material/Button'

export default function DownloadBtn({
data,
name,
...props
}: ButtonProps & { data: object | string }) {
}: ButtonProps & { data: object | string; name?: string }) {
const safeName = name || 'geojson.json'
return (
<Button
variant="outlined"
Expand All @@ -18,7 +20,12 @@ export default function DownloadBtn({
typeof data === 'string' ? data : JSON.stringify(data, null, 2),
)}`,
)
el.setAttribute('download', 'geojson.json')
el.setAttribute(
'download',
(safeName.endsWith('.json') ? safeName : `${safeName}.json`)
.replaceAll(' ', '-')
.toLocaleLowerCase(),
)
el.style.display = 'none'
document.body.appendChild(el)
el.click()
Expand Down
16 changes: 13 additions & 3 deletions client/src/components/dialogs/ImportExport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import Button from '@mui/material/Button'
import Dialog from '@mui/material/Dialog'
import DialogContent from '@mui/material/DialogContent'
import Grid2 from '@mui/material/Unstable_Grid2/Grid2'
import useDeepCompareEffect from 'use-deep-compare-effect'

import SplitMultiPolygonsBtn from '@components/buttons/SplitMultiPolygons'
import MultiOptions from '@components/drawer/inputs/MultiOptions'
Expand Down Expand Up @@ -32,6 +31,8 @@ export function ImportExportDialog({
const feature = useImportExport((s) => s.feature)
const code = useImportExport((s) => s.code)
const error = useImportExport((s) => s.error)
const fileName = useImportExport((s) => s.fileName)

const { reset, setCode, fireConvert, updateStats } =
useImportExport.getState()

Expand All @@ -46,7 +47,7 @@ export function ImportExportDialog({
}
}, [polygonExportMode, open, code, simplifyPolygons])

useDeepCompareEffect(() => {
React.useEffect(() => {
if (open) {
if (shape === 'Route') {
updateStats(mode === 'Export')
Expand Down Expand Up @@ -106,7 +107,16 @@ export function ImportExportDialog({
style={{ flexGrow: 1, display: 'flex', justifyContent: 'flex-end' }}
>
<ClipboardButton text={code} />
<DownloadBtn data={code} variant="text" color="primary" />
<DownloadBtn
data={code}
variant="text"
color="primary"
name={
fileName ||
(feature.type === 'Feature' &&
(feature.properties?.__name || feature.properties?.name))
}
/>
<Button
disabled={!!error}
onClick={() => {
Expand Down
94 changes: 45 additions & 49 deletions client/src/hooks/useImportExport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/* eslint-disable @typescript-eslint/ban-types */
import { create } from 'zustand'
import distance from '@turf/distance'
import type { Position } from 'geojson'

import {
KojiResponse,
Expand All @@ -25,7 +26,7 @@ const getProperties = (feature: Feature) => {
const parsedId =
typeof feature?.id === 'string'
? useDbCache.getState().parseKojiKey(feature.id)
: DEFAULT
: { ...DEFAULT }
const name =
(feature.properties?.__geofence_id
? Object.values(geofence).find(
Expand All @@ -37,11 +38,14 @@ const getProperties = (feature: Feature) => {
const mode = feature?.properties?.__mode || parsedId.mode || 'unset'
return { name, mode, source: parsedId.source, id: parsedId.id }
}

export interface UseImportExport {
code: string
error: string
open: 'importPolygon' | 'importRoute' | 'exportPolygon' | 'exportRoute' | ''
feature: Feature | FeatureCollection
skipSend: boolean
fileName: string
stats: {
max: number
total: number
Expand Down Expand Up @@ -76,6 +80,8 @@ const DEFAULTS: Omit<
type: 'FeatureCollection',
features: [],
},
fileName: '',
skipSend: false,
stats: {
max: 0,
total: 0,
Expand All @@ -88,10 +94,11 @@ const DEFAULTS: Omit<
export const useImportExport = create<UseImportExport>((set, get) => ({
...DEFAULTS,
exportConvert: async () => {
const { feature, setCode } = get()
const { feature } = get()
const { polygonExportMode, simplifyPolygons } = usePersist.getState()
convert(feature, polygonExportMode, simplifyPolygons).then((newCode) => {
setCode(
const newCode = await convert(feature, polygonExportMode, simplifyPolygons)
set({
code:
typeof newCode === 'string'
? newCode
: JSON.stringify(
Expand All @@ -103,7 +110,7 @@ export const useImportExport = create<UseImportExport>((set, get) => ({
null,
2,
),
)
skipSend: true,
})
},
importConvert: async (geometry) => {
Expand All @@ -117,24 +124,23 @@ export const useImportExport = create<UseImportExport>((set, get) => ({
: cleanCode,
)
: cleanCode
await convert<FeatureCollection>(
const geojson = await convert<FeatureCollection>(
parsed,
'featureCollection',
usePersist.getState().simplifyPolygons,
geometry,
).then((geojson) => {
if (geojson.type === 'FeatureCollection') {
set({
feature: {
...geojson,
features: geojson.features.map((f) => ({
...f,
// id: f.id ?? getKey(),
})),
},
})
}
})
)
if (geojson.type === 'FeatureCollection') {
set({
feature: {
...geojson,
features: geojson.features.map((f) => ({
...f,
// id: f.id ?? getKey(),
})),
},
})
}
set({ error: '' })
} catch (e) {
if (e instanceof Error) {
Expand All @@ -143,11 +149,14 @@ export const useImportExport = create<UseImportExport>((set, get) => ({
}
},
fireConvert: async (mode, geometry) => {
const { skipSend } = get()
if (skipSend) return
if (mode === 'Export') {
await get().exportConvert()
} else {
await get().importConvert(geometry)
}
set({ skipSend: false })
},
updateStats: async (writeCode) => {
const { feature, code } = get()
Expand All @@ -162,42 +171,29 @@ export const useImportExport = create<UseImportExport>((set, get) => ({
let maxLon = -Infinity

const points: [number, number][] = []

const update = (pos: Position, j: number, coordinates: Position[]) => {
const next = j ? coordinates[j + 1] : coordinates.at(-1)
if (next) {
const dis = distance(pos, next, { units: 'meters' })
if (dis > max) max = dis
total += dis
}
points.push([pos[1], pos[0]])
if (pos[0] < minLon) minLon = pos[0]
if (pos[0] > maxLon) maxLon = pos[0]
if (pos[1] < minLat) minLat = pos[1]
if (pos[1] > maxLat) maxLat = pos[1]
count++
}
if (feature.type === 'Feature') {
if (feature.geometry.type === 'MultiPoint') {
const { coordinates } = feature.geometry
coordinates.forEach((point, j) => {
const next = j ? coordinates[j + 1] : coordinates.at(-1)
if (next) {
const dis = distance(point, next, { units: 'meters' })
if (dis > max) max = dis
total += dis
}
points.push([point[1], point[0]])
if (point[0] < minLon) minLon = point[0]
if (point[0] > maxLon) maxLon = point[0]
if (point[1] < minLat) minLat = point[1]
if (point[1] > maxLat) maxLat = point[1]
count++
})
feature.geometry.coordinates.forEach(update)
}
} else {
feature.features.forEach((f) => {
if (f.geometry.type === 'MultiPoint') {
const { coordinates } = f.geometry
coordinates.forEach((point, j) => {
const next = j ? coordinates[j + 1] : coordinates.at(-1)
if (next) {
const dis = distance(point, next, { units: 'meters' })
if (dis > max) max = dis
total += dis
}
points.push([point[1], point[0]])
if (point[0] < minLon) minLon = point[0]
if (point[0] > maxLon) maxLon = point[0]
if (point[1] < minLat) minLat = point[1]
if (point[1] > maxLat) maxLat = point[1]
count++
})
f.geometry.coordinates.forEach(update)
}
})
}
Expand Down
18 changes: 16 additions & 2 deletions client/src/pages/admin/actions/Export.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import {
// eslint-disable-next-line import/no-extraneous-dependencies
import { useQuery } from 'react-query'

import { BasicKojiEntry, Feature, KojiResponse } from '@assets/types'
import type {
BasicKojiEntry,
Feature,
FeatureCollection,
KojiResponse,
} from '@assets/types'
import { fetchWrapper } from '@services/fetches'
import { useImportExport } from '@hooks/useImportExport'

Expand Down Expand Up @@ -39,7 +44,10 @@ export function ExportButton<T extends BasicKojiEntry>({
const record = useRecordContext<T>()
const { refetch } = useQuery(
`export-${resource}-${record.id}`,
() => fetchWrapper<KojiResponse<Feature>>(getUrl(resource, record.id)),
() =>
fetchWrapper<KojiResponse<Feature | FeatureCollection>>(
getUrl(resource, record.id),
),
{
enabled: false,
},
Expand All @@ -54,6 +62,12 @@ export function ExportButton<T extends BasicKojiEntry>({
useImportExport.setState({
open: 'exportPolygon',
feature: res.data.data,
fileName:
res.data.data.type === 'Feature'
? res.data.data.properties?.__name ||
res.data.data.properties?.name ||
''
: record.name,
})
}
})
Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/map/popups/Point.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ export function PointPopup({ id, lat, lon, type: geoType, dbRef }: Props) {

return id !== undefined ? (
<div>
Lat: {lat.toFixed(6)}
Lat: {lat}
<br />
Lng: {lon.toFixed(6)}
Lng: {lon}
<br />
{process.env.NODE_ENV === 'development' && (
<>
Expand Down
6 changes: 3 additions & 3 deletions server/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "koji"
version = "1.4.2"
version = "1.5.1"
edition = "2021"

[workspace]
Expand Down
2 changes: 1 addition & 1 deletion server/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "api"
version = "1.5.0"
version = "1.5.1"
edition = "2021"
publish = false

Expand Down
7 changes: 4 additions & 3 deletions server/api/src/public/v1/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use model::{
FeatureHelpers, GeometryHelpers, ToCollection, ToFeature,
},
db::sea_orm_active_enums::Type,
utils::TrimPrecision,
};

#[post("/data")]
Expand All @@ -21,11 +22,11 @@ async fn convert_data(payload: web::Json<Args>) -> Result<HttpResponse, Error> {
..
} = payload.into_inner().init(Some("convert_data"));

let area = if arg_simplify { area.simplify() } else { area };
let area = area
let area = if arg_simplify { area.simplify() } else { area }
.into_iter()
.map(|feat| feat.remove_internal_props())
.collect();
.collect::<FeatureCollection>()
.trim_precision(6);

Ok(utils::response::send(
area,
Expand Down
2 changes: 1 addition & 1 deletion server/model/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "model"
version = "1.5.0"
version = "1.5.1"
edition = "2021"
publish = false

Expand Down
8 changes: 6 additions & 2 deletions server/model/src/api/collection.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use utils::TrimPrecision;

use self::utils::sql_raw;

use super::{args::UnknownId, multi_vec::MultiVec, *};
Expand Down Expand Up @@ -39,13 +41,15 @@ impl GeometryHelpers for FeatureCollection {
})
.collect()
}
}

fn to_f32(self) -> Self {
impl TrimPrecision for FeatureCollection {
fn trim_precision(self, precision: u32) -> Self {
self.into_iter()
.map(|feat| {
if let Some(geometry) = feat.geometry {
Feature {
geometry: Some(geometry.to_f32()),
geometry: Some(geometry.trim_precision(precision)),
..feat
}
} else {
Expand Down
Loading
Loading