frontend: distance column

This commit is contained in:
Johannes Lötzsch 2022-03-14 21:08:55 +01:00
parent 687e79b3c4
commit 0870fd1b85
6 changed files with 50 additions and 10 deletions

View File

@ -2,6 +2,7 @@ const groups = [
{ name: 'contactStatus', header: 'Contact Status' }, { name: 'contactStatus', header: 'Contact Status' },
{ name: 'offerStatus', header: 'Offer Status' }, { name: 'offerStatus', header: 'Offer Status' },
{ name: 'locationCoarse', header: 'Location' }, { name: 'locationCoarse', header: 'Location' },
{ name: 'distance', group: 'locationCoarse', header: 'Distance' },
{ name: 'time', header: 'Time' }, { name: 'time', header: 'Time' },
{ name: 'features', header: 'Limitations / Features' }, { name: 'features', header: 'Limitations / Features' },
{ name: 'animals', group: 'features', header: 'Animals' }, { name: 'animals', group: 'features', header: 'Animals' },

View File

@ -32,6 +32,13 @@ const columnsRaw: ColumnRaw[] = [
"editable": true, "editable": true,
"defaultWidth": 400 "defaultWidth": 400
}, },
{
"name": "place_distance",
"group": "distance",
"header": "km",
"type": "number",
"defaultWidth": 105
},
{ {
"name": "place_country", "name": "place_country",
"group": "locationCoarse", "group": "locationCoarse",

View File

@ -6,9 +6,14 @@ import { useTranslation } from 'react-i18next'
import { Login, useAuthStore } from '../Login' import { Login, useAuthStore } from '../Login'
import { useLeafletStore } from './LeafletStore' import { useLeafletStore } from './LeafletStore'
import { filterUndefOrNull } from '../util/notEmpty' import { filterUndefOrNull } from '../util/notEmpty'
import { haversine_distance } from '../util/distance'
type HostOfferLookupWrapperProps = Partial<HostOfferLookupTableProps> type HostOfferLookupWrapperProps = Partial<HostOfferLookupTableProps>
function floor(v: number|undefined) {
return v && Math.floor(v)
}
const HostOfferLookupWrapper = (props: HostOfferLookupWrapperProps) => { const HostOfferLookupWrapper = (props: HostOfferLookupWrapperProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const auth = useAuthStore() const auth = useAuthStore()
@ -20,17 +25,20 @@ const HostOfferLookupWrapper = (props: HostOfferLookupWrapperProps) => {
const {data: data_ro} = queryResult_ro const {data: data_ro} = queryResult_ro
const {data: data_rw} = queryResult_rw const {data: data_rw} = queryResult_rw
const leafletStore = useLeafletStore() const {setMarkers, center} = useLeafletStore()
useEffect(() => { useEffect(() => {
const markers = data_ro?.get_offers?.map(row => (row.id && row.place_lon && row.place_lat const markers = data_ro?.get_offers?.map(row => (row.id && row.place_lon && row.place_lat
&& ({id: row.id, && ({id: row.id,
lat: row.place_lat, lat: row.place_lat,
lng: row.place_lon, lng: row.place_lon,
radius: 1000, // TODO radius: 1000, // TODO
content: 'TODO'}) || undefined)) content: 'TODO'}) || undefined))
leafletStore.setMarkers(filterUndefOrNull(markers)) setMarkers(filterUndefOrNull(markers))
}, [data_ro]) }, [data_ro])
const data_ro_withDistance = data_ro?.get_offers?.map(r => ({...r,
place_distance: floor(haversine_distance(center?.lat, center?.lng, r.place_lat, r.place_lon))}))
return <> return <>
<Box sx={{ <Box sx={{
display: 'flex', display: 'flex',
@ -48,7 +56,7 @@ const HostOfferLookupWrapper = (props: HostOfferLookupWrapperProps) => {
style={{flex: '1 1', height: '100%'}}> style={{flex: '1 1', height: '100%'}}>
<HostOfferLookupTable <HostOfferLookupTable
{...props} {...props}
data_ro={data_ro.get_offers} data_ro={data_ro_withDistance}
data_rw={data_rw?.get_rw} data_rw={data_rw?.get_rw}
refetch_rw={queryResult_rw.refetch} refetch_rw={queryResult_rw.refetch}
/> />

View File

@ -20,10 +20,15 @@ import { useLeafletStore } from './LeafletStore'
type LeafletMapProps = {onBoundsChange?: (bounds: L.LatLngBounds) => void} type LeafletMapProps = {onBoundsChange?: (bounds: L.LatLngBounds) => void}
const BoundsChangeListener = ({onBoundsChange}: {onBoundsChange?: (bounds: L.LatLngBounds) => void}) => { const BoundsChangeListener = ({onBoundsChange}: {onBoundsChange?: (bounds: L.LatLngBounds) => void}) => {
const leafletStore = useLeafletStore()
const map = useMap() const map = useMap()
const updateBounds = useCallback( const updateBounds = useCallback(
() => { () => {
leafletStore.setCenter(map.getCenter())
// onBoundsChange is unused at the moment
onBoundsChange && onBoundsChange( onBoundsChange && onBoundsChange(
map.getBounds() map.getBounds()
) )

View File

@ -1,19 +1,24 @@
import create from 'zustand' import create from 'zustand'
import * as L from 'leaflet'
export interface Marker export interface Marker {
{id: string, id: string,
lat: number, lat: number,
lng: number, lng: number,
radius: number, // in meters radius: number, // in meters
content: string // TODO react-child? content: string // TODO react-child?
} }
export interface LeafletState { export interface LeafletState {
markers: Marker[] center: L.LatLng|null // at the moment not used for setting the maps center, but only for distance calculation onBoundsChange
setMarkers: (markers: Marker[]) => void setCenter: (center: L.LatLng) => void
markers: Marker[]
setMarkers: (markers: Marker[]) => void
} }
export const useLeafletStore = create<LeafletState>(set => ({ export const useLeafletStore = create<LeafletState>(set => ({
center: null,
setCenter: center => set( _orig => ({center}) ),
markers: [], markers: [],
setMarkers: (markers: Marker[]) => set( _orig => ({markers}) ) setMarkers: markers => set( _orig => ({markers}) )
})) }))

View File

@ -0,0 +1,14 @@
export type lon=number|null|undefined
export type lat=number|null|undefined
/** optimization by https://stackoverflow.com/questions/5260423/torad-javascript-function-throwing-error/21623256#21623256 **/
export function haversine_distance(lat1:lat, lon1: lon, lat2:lat, lon2:lon) {
if(lat1 && lon1 && lat2 && lon2) {
var R = 6371 // Radius of the earth in km
var dLat = (lat2 - lat1) * Math.PI / 180 // deg2rad below
var dLon = (lon2 - lon1) * Math.PI / 180
var a = 0.5 - Math.cos(dLat)/2 +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * (1 - Math.cos(dLon))/2
return R * 2 * Math.asin(Math.sqrt(a))
}
}