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

Add a map view for viewing incidents #104

Merged
merged 1 commit into from
Aug 21, 2023
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
3 changes: 3 additions & 0 deletions packages/pwa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@
"@saferplace/api": "0.0.15",
"i18next": "^23.4.4",
"i18next-browser-languagedetector": "^7.1.0",
"leaflet": "^1.9.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^13.1.2",
"react-leaflet": "^4.2.0",
"react-router-dom": "^6.15.0"
},
"devDependencies": {
"@types/leaflet": "^1.9.0",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@vitejs/plugin-basic-ssl": "^1.0.1",
Expand Down
1 change: 1 addition & 0 deletions packages/pwa/src/locale/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default {
useEmail: "Use Email",
useBackend: "Use Backend",
viewIncidents: "View Incidents",
viewIncident: "View Incident",
submitReport: "Submit Report",
},
phrases: {
Expand Down
1 change: 1 addition & 0 deletions packages/pwa/src/locale/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type TranslationFile = {
useEmail: string
useBackend: string
viewIncidents: string
viewIncident: string
submitReport: string
}>,
phrases: Partial<{
Expand Down
1 change: 1 addition & 0 deletions packages/pwa/src/locale/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default {
useEmail: "Użyj e-mail",
useBackend: "Użyj Backendu",
viewIncidents: "Zobacz Zdarzenia",
viewIncident: "Zobacz Zdarzenie",
submitReport: "Zgłos Zdarzenie",
},
phrases: {
Expand Down
4 changes: 4 additions & 0 deletions packages/pwa/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Incident from './routes/incident/single'
import { incidentLoader, incidentsInRadiusLoader } from './routes/incident/loaders'
import Report from './routes/report'
import { reportLoader } from './routes/loaders'
import Map from './routes/incident/map'

const router = createBrowserRouter([
{
Expand All @@ -40,6 +41,9 @@ const router = createBrowserRouter([
}, {
path: '/login',
Component: Login,
}, {
path: '/map',
Component: Map,
}
])

Expand Down
1 change: 1 addition & 0 deletions packages/pwa/src/routes/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default function Home() {
<Stack>
<Link component={RouterLink} to='/incidents'>{t('action:viewIncidents')}</Link>
<Link component={RouterLink} to='/report'>{t('action:submitReport')}</Link>
<Link component={RouterLink} to='/map'>{t('action:viewMap')}</Link>
</Stack>
)
}
111 changes: 111 additions & 0 deletions packages/pwa/src/routes/incident/map.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { Coordinates, Incident } from "@saferplace/api/incident/v1/incident_pb"
import { MapContainer, Marker, TileLayer, LayerGroup, Popup } from 'react-leaflet'
import { useMapEvents } from 'react-leaflet/hooks'
import { Box, Button, Skeleton } from "@mui/material"
import React from 'react'
import { usePosition } from "../../hooks/position"
import useClient from "../../hooks/client"
import { ViewerService } from "@saferplace/api/viewer/v1/viewer_connect"
import 'leaflet/dist/leaflet.css'
import { useTranslation } from "react-i18next"
import { useNavigate } from "react-router-dom"

export type Props = {
center?: Coordinates
setCenter: (coordinates: Coordinates) => void
setZoom: (zoom: number) => void
}

export default function Map() {
const [ initialPosition ] = usePosition()
const [ incidents, setIncidents ] = React.useState<Incident[]>([])
const [ center, setCenter ] = React.useState<Coordinates | undefined>()
const { t } = useTranslation()
const navigate = useNavigate()

const [ zoom, setZoom ] = React.useState<number>(13)
const client = useClient(ViewerService)

React.useEffect(() => {
if (!initialPosition) {
return
}
setCenter(new Coordinates(initialPosition))
}, [initialPosition])

React.useEffect(() => {
if (!center) {
return
}
console.debug(`viewing incident at ${center.lat}, ${center.lon} at zoom ${zoom} with radius ${zoomToRadius(center.lat, zoom)}m`)
client.viewInRadius({
radius: zoomToRadius(center.lat, zoom), // Static until we know how to convert zoom to radius
center,
})
.then(resp => setIncidents(resp.incidents))
.catch(err => console.error(err))
}, [center])

Check warning on line 47 in packages/pwa/src/routes/incident/map.tsx

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/pwa/src/routes/incident/map.tsx#L47

[react-hooks/exhaustive-deps] React Hook React.useEffect has missing dependencies: 'client' and 'zoom'. Either include them or remove the dependency array.

return (
<Box sx={{
'.leaflet-container': {
height: '100vh',
width: '100vw',
},
}}>
{ center ? (
<MapContainer center={latlon(center)} zoom={zoom}>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<LayerGroup>
{ incidents.map(incident => (
<Marker key={incident.id} position={latlon(incident.coordinates)}>
<Popup>
<Button
onClick={() => navigate(`/incident/${incident.id}`)}
>
{t('action:viewIncident')}
</Button>
</Popup>
</Marker>
)) }
{/* Enable for debugging */}
{/* <Circle center={latlon(center)} radius={zoomToRadius(center?.lat ?? 0, zoom)} /> */}
</LayerGroup>
<MapDescendant
center={new Coordinates(initialPosition)}
setCenter={setCenter}
setZoom={setZoom}
/>
</MapContainer>
) : (
<Skeleton />
)}

</Box>

)
}

function MapDescendant({setCenter, setZoom }: Props) {
const map = useMapEvents({
zoomend: () => {
setZoom(map.getZoom())
},
moveend: () => {
const {lat, lng} = map.getCenter()
setCenter(new Coordinates({lat, lon: lng}))
},
})
return null
}

function latlon(coords: Coordinates | undefined): [number, number] {
return [coords?.lat || 0, coords?.lon || 0]
}

function zoomToRadius(lat: number, zoom: number): number {
return ((80_000_000*Math.cos(lat * Math.PI / 180)) / Math.pow(2, zoom)) * 2
}
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

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

Loading