Skip to content

Commit

Permalink
Merge pull request #231 from TurtIeSocks/various-formatting-fixes
Browse files Browse the repository at this point in the history
more consistent usage of precision trait & other formatting refactors
  • Loading branch information
TurtIeSocks authored Jul 30, 2024
2 parents d5d7a4a + 0b873d7 commit beda72e
Show file tree
Hide file tree
Showing 18 changed files with 147 additions and 93 deletions.
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

0 comments on commit beda72e

Please sign in to comment.