Skip to content

Commit

Permalink
added
Browse files Browse the repository at this point in the history
  • Loading branch information
Yashgupta9330 committed Mar 4, 2025
1 parent 2360b30 commit e33a663
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 18 deletions.
84 changes: 66 additions & 18 deletions frontend/src/components/ChapterMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,25 @@ import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
import 'leaflet.markercluster'
import { GeoLocDataAlgolia, GeoLocDataGraphQL } from 'types/chapter'

const getDistance = (lat1: number, lng1: number, lat2: number, lng2: number) => {
const R = 6371
const dLat = ((lat2 - lat1) * Math.PI) / 180
const dLng = ((lng2 - lng1) * Math.PI) / 180
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) *
Math.sin(dLng / 2) * Math.sin(dLng / 2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
return R * c
}

const ChapterMap = ({
geoLocData,
userLocation,
style,
}: {
geoLocData: GeoLocDataGraphQL[] | GeoLocDataAlgolia[]
userLocation: { lat: number; lng: number } | null
style: React.CSSProperties
}) => {
const mapRef = useRef<L.Map | null>(null)
Expand All @@ -24,27 +38,37 @@ const ChapterMap = ({
}))
}, [geoLocData])

//for reference: https://leafletjs.com/reference.html#map-example
const nearestChapters = useMemo(() => {
if (!userLocation) return normalizedData

// Sort chapters by distance from user location
return normalizedData
.map((chapter) => ({
...chapter,
distance: getDistance(userLocation.lat, userLocation.lng, chapter.lat, chapter.lng),
}))
.sort((a, b) => a.distance - b.distance)
.slice(0, 5)
}, [userLocation, normalizedData])

useEffect(() => {
if (!mapRef.current) {
mapRef.current = L.map('chapter-map', {
worldCopyJump: false, // Prevents the map from wrapping around the world
worldCopyJump: false,
maxBounds: [
[-90, -180], // Southwest corner of the map bounds (latitude, longitude)
[90, 180], // Northeast corner of the map bounds (latitude, longitude)
[-90, -180],
[90, 180],
],
maxBoundsViscosity: 1.0,
}).setView([20, 0], 2)

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
className: 'map-tiles',
}).addTo(mapRef.current)
}

const map = mapRef.current

// Remove previous markers
map.eachLayer((layer) => {
if (layer instanceof L.Marker || layer instanceof L.LayerGroup) {
map.removeLayer(layer)
Expand All @@ -53,19 +77,30 @@ const ChapterMap = ({

const markerClusterGroup = L.markerClusterGroup()
const bounds: [number, number][] = []
normalizedData.forEach((chapter) => {

// Validate and filter out invalid coordinates
const validChapters = normalizedData.filter(chapter =>
chapter.lat !== null &&
chapter.lng !== null &&
!isNaN(chapter.lat) &&
!isNaN(chapter.lng) &&
chapter.lat >= -90 &&
chapter.lat <= 90 &&
chapter.lng >= -180 &&
chapter.lng <= 180
)

validChapters.forEach((chapter) => {
const markerIcon = new L.Icon({
iconAnchor: [12, 41], // Anchor point
iconAnchor: [12, 41],
iconRetinaUrl: '/img/marker-icon-2x.png',
iconSize: [25, 41], // Default size for Leaflet markers
iconSize: [25, 41],
iconUrl: '/img/marker-icon.png',
popupAnchor: [1, -34], // Popup position relative to marker
shadowSize: [41, 41], // Shadow size
popupAnchor: [1, -34],
shadowSize: [41, 41],
shadowUrl: '/img/marker-shadow.png',
})
const marker = L.marker([chapter.lat, chapter.lng], {
icon: markerIcon,
})
const marker = L.marker([chapter.lat, chapter.lng], { icon: markerIcon })
const popup = L.popup()
const popupContent = document.createElement('div')
popupContent.className = 'popup-content'
Expand All @@ -81,12 +116,25 @@ const ChapterMap = ({

map.addLayer(markerClusterGroup)

if (bounds.length > 0) {
map.fitBounds(bounds as L.LatLngBoundsExpression, { maxZoom: 10 })
// Add fallback for fitting bounds
try {
if (userLocation && nearestChapters.length > 0) {
const nearestBounds = nearestChapters.map((chapter) => [chapter.lat, chapter.lng] as [number, number])
if (nearestBounds.length > 0) {
map.fitBounds(nearestBounds, { maxZoom: 10 })
} else if (bounds.length > 0) {
map.fitBounds(bounds)
}
} else if (bounds.length > 0) {
map.fitBounds(bounds)
}
} catch{
// Fallback to default view if bounds fitting fails
map.setView([20, 0], 2)
}
}, [normalizedData])
}, [normalizedData, nearestChapters, userLocation])

return <div id="chapter-map" className="rounded-lg dark:bg-[#212529]" style={style} />
return <div id="chapter-map" style={style} />
}

export default ChapterMap
19 changes: 19 additions & 0 deletions frontend/src/pages/Chapters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import SearchPageLayout from 'components/SearchPageLayout'

const ChaptersPage = () => {
const [geoLocData, setGeoLocData] = useState<ChapterTypeAlgolia[]>([])
const [userLocation, setUserLocation] = useState<{ lat: number; lng: number } | null>(null)

const {
items: chapters,
isLoaded,
Expand All @@ -27,6 +29,7 @@ const ChaptersPage = () => {
pageTitle: 'OWASP Chapters',
})

// Fetch chapter data and user location
useEffect(() => {
const fetchData = async () => {
const searchParams = {
Expand All @@ -43,7 +46,22 @@ const ChaptersPage = () => {
)
setGeoLocData(data.hits)
}

const fetchUserLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
setUserLocation({
lat: position.coords.latitude,
lng: position.coords.longitude,
})
},
)
}
}

fetchData()
fetchUserLocation()
}, [])

const navigate = useNavigate()
Expand Down Expand Up @@ -92,6 +110,7 @@ const ChaptersPage = () => {
{chapters.length > 0 && (
<ChapterMap
geoLocData={searchQuery ? chapters : geoLocData}
userLocation={userLocation}
style={{ height: '400px', width: '100%', zIndex: '0' }}
/>
)}
Expand Down

0 comments on commit e33a663

Please sign in to comment.