diff --git a/resq/frontend/src/components/DisasterMap.js b/resq/frontend/src/components/DisasterMap.js index cd364b76..adec2d8e 100644 --- a/resq/frontend/src/components/DisasterMap.js +++ b/resq/frontend/src/components/DisasterMap.js @@ -1,22 +1,42 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { Map, Marker, ZoomControl } from 'pigeon-maps'; import { type_colors } from "../Colors"; import { AnnotationIcon, MarkerIcon } from "./MapIcons"; -const MAPBOX_TOKEN = "pk.eyJ1IjoiaWxnYXplciIsImEiOiJjbG80Nzg4Z3gwMjZ4MmtxcTR3bGI5enR3In0.QdNftxZYpJ79K0M0DfYHUw" -const MAPBOX_STYLE = "mapbox/streets-v12" +const MAPBOX_TOKEN = "pk.eyJ1IjoiaWxnYXplciIsImEiOiJjbG80Nzg4Z3gwMjZ4MmtxcTR3bGI5enR3In0.QdNftxZYpJ79K0M0DfYHUw"; +const MAPBOX_STYLE = "mapbox/streets-v12"; function mapboxProvider(x, y, z, dpr) { - return `https://api.mapbox.com/styles/v1/${MAPBOX_STYLE - }/tiles/512/${z}/${x}/${y}${dpr >= 2 ? '@2x' : ''}?access_token=${MAPBOX_TOKEN - }`; + return `https://api.mapbox.com/styles/v1/${MAPBOX_STYLE}/tiles/512/${z}/${x}/${y}${dpr >= 2 ? '@2x' : ''}?access_token=${MAPBOX_TOKEN}`; } -const marker_order = ["Annotation", "Request", "Resource"] +const marker_order = ["Annotation", "Request", "Resource"]; -export default function DisasterMap({ onPointSelected, markers = [], mapCenter, setMapCenter, onBoundsChanged }) { +export default function DisasterMap({ onPointSelected, markers = [], mapCenter, setMapCenter }) { const [zoom, setZoom] = useState(6.5); + useEffect(() => { + // Calculate bounds to encompass all markers + if (markers.length > 0) { + const bounds = markers.reduce((acc, marker) => { + return [ + Math.min(acc[0], marker.latitude), + Math.min(acc[1], marker.longitude), + Math.max(acc[2], marker.latitude), + Math.max(acc[3], marker.longitude), + ]; + }, [markers[0].latitude, markers[0].longitude, markers[0].latitude, markers[0].longitude]); + + // Calculate center and zoom level based on bounds + const center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2]; + const zoom = getZoomLevel(bounds); + + // Set map center and zoom + setMapCenter(center); + setZoom(zoom); + } + }, [markers]); + const renderMarker = (marker) => { return ( { onPointSelected(marker); - event.preventDefault() + event.preventDefault(); }} > {marker.type === "Annotation" ? : @@ -34,6 +54,29 @@ export default function DisasterMap({ onPointSelected, markers = [], mapCenter, ); }; + const getZoomLevel = (bounds) => { + // Calculate zoom level based on the bounds and map size + const WORLD_DIM = { height: 256, width: 256 }; + const ZOOM_MAX = 19; + + const latRad1 = (bounds[0] * Math.PI) / 180; + const latRad2 = (bounds[2] * Math.PI) / 180; + const lngRad1 = (bounds[1] * Math.PI) / 180; + const lngRad2 = (bounds[3] * Math.PI) / 180; + + const x1 = lngRad1; + const y1 = Math.log(Math.tan(Math.PI / 4 + latRad1 / 2)); + const x2 = lngRad2; + const y2 = Math.log(Math.tan(Math.PI / 4 + latRad2 / 2)); + + const scaleX = WORLD_DIM.width / (x2 - x1); + const scaleY = WORLD_DIM.height / (y2 - y1); + const scale = Math.min(scaleX, scaleY); + const zoom = ZOOM_MAX - Math.log(scale) / Math.log(2); + + return zoom; + }; + return (
@@ -44,13 +87,9 @@ export default function DisasterMap({ onPointSelected, markers = [], mapCenter, zoom={zoom} onClick={({ event }) => { onPointSelected(null); - event.preventDefault() + event.preventDefault(); }} - onBoundsChanged={({ center, zoom, bounds }) => { - setMapCenter(center) - setZoom(zoom) - onBoundsChanged(bounds) - }}> + > {markers .sort(({ type }) => -marker_order.indexOf(type)) diff --git a/resq/frontend/src/pages/MapDemo.js b/resq/frontend/src/pages/MapDemo.js index e0d24115..c060984f 100644 --- a/resq/frontend/src/pages/MapDemo.js +++ b/resq/frontend/src/pages/MapDemo.js @@ -1,4 +1,3 @@ -// noinspection JSUnusedLocalSymbols import * as React from 'react'; import { useEffect, useState } from 'react'; @@ -144,20 +143,29 @@ const mock_markers = [ quantity: 500, }, ], - } + }, + ...[...Array(20).keys()].map(i => + [...Array(20).keys()].map(j => ( + { + type: "Request", + latitude: 37 + 0.5 * i, + longitude: 31 + 0.5 * j, + requester: { + name: "Müslüm", + surname: "Ertürk" + }, + urgency: "HIGH", + needs: [] + }))).flat() ] function getAllCategories(item) { - // Extract categories based on the type of item switch (item.type) { case "Annotation": - // Annotations may have a single category return [item?.category]; case "Resource": - // Resources may have multiple categories return item.resources.map(resource => resource?.category); case "Request": - // Requests may have multiple needs, each with its own category return item.needs.map(need => need?.category); default: return []; @@ -213,7 +221,6 @@ const makeFilterByBounds = ({ ne: [ne_lat, ne_lng], sw: [sw_lat, sw_lng] }) => export default function MapDemo() { - // eslint-disable-next-line no-unused-vars const [allMarkers, setAllMarkers] = useState(mock_markers) const [shownMarkers, setShownMarkers] = useState(allMarkers) const [selectedPoint, setSelectedPoint] = useState(null) @@ -270,7 +277,6 @@ export default function MapDemo() { .filter(makeFilterByBounds(mapBounds)) ), [allMarkers, amountFilter, categoryFilter, dateFromFilter, dateToFilter, mapBounds, typeFilter]) - // noinspection JSValidateTypes return ( @@ -323,13 +329,11 @@ export default function MapDemo() { {shownMarkers.filter(marker => marker.type !== 'Annotation').map((marker, index) => { const SelectedCard = cards[marker.type]; const locationName = locationNames[`${marker.latitude},${marker.longitude}`] || 'Loading...'; - return ( // Add this return statement - setSelectedPoint(marker)} - key={`${marker.type}-${index}`} - /> - ); + setSelectedPoint(marker)} + key={`${marker.type}-${index}`} + /> })}