frontend: distance column
This commit is contained in:
parent
687e79b3c4
commit
0870fd1b85
|
@ -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' },
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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()
|
||||||
)
|
)
|
||||||
|
|
|
@ -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}) )
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue