MapBox not drawing Polygon at all
- Dominant language
- TypeScript
- Stars
- 12.4k
- Forks
- 2.4k
- PR merge metrics
- No merged PRs in 30d
Description
I tried both `react-map-gl` and `mapbox-gl` both of them are rendering everything perfectly as show below.
**mapbox-gl-js version**: 3.10.0
**browser**: Edge, Safari Both tested and having issue.
### Steps to Trigger Behavior
1. Can't say what could be the issue as there is no error everything is perfect. just polygon are not getting drawn.
### Link to Demonstration
/*
* IMPORTS
*/
import 'mapbox-gl/dist/mapbox-gl.css' // Mapbox CSS.
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css' // Mapbox Draw CSS.
import * as turf from '@turf/turf' // Turf for geospatial calculations.
import React, { useEffect, useRef, useState } from 'react' // React hooks.
import PropTypes from 'prop-types' // React hooks.
import MapboxDraw from '@mapbox/mapbox-gl-draw' // Mapbox Draw plugin.
import mapboxgl from 'mapbox-gl' // Mapbox GL JS.
import _ from 'underscore' // Utility module.
import { MdGraphicEq } from 'react-icons/md' // React icons.
import { toast } from 'react-toastify' // Npm: Toastify library..
import { HiMapPin } from 'react-icons/hi2' // React icons.
import {
Box,
Button,
Flex,
FormLabel,
RangeSlider,
RangeSliderFilledTrack,
RangeSliderThumb,
RangeSliderTrack,
Text,
Tooltip
} from '@chakra-ui/react' // Chakra UI components.
/*
* PACKAGES
*/
import { MemoizedInput, MemoizedSearchSelect } from 'components/MemoizedInput'
/*
* OBJECTS
*/
const Index = ({ markersToShow }) => {
// Const assignment.
const _selectionMethods = ['Free Hand Geofencing', 'Radius Based Geofencing', 'Coordinates Based Geofencing']
// Hook assignment.
const [selectedMethod, setSelectedMethod] = useState(_selectionMethods[0])
const [geoFencingRadius, setGeoFencingRadius] = useState(10)
const [searchQuery, setSearchQuery] = useState('')
const [options, setOptions] = useState([])
const [markers, setMarkers] = useState([])
const [drawnPolygon, setDrawnPolygon] = useState(null)
const [calculatedArea, setCalculatedArea] = useState(null)
const [coordinates, setCoordinates] = useState([])
const _mapRef = useRef(void 0)
const _drawRef = useRef(void 0)
// Object assignment.
const _UpdateArea = () => {
// Get all features.
const data = _drawRef.current.getAll()
// If there are features, calculate area.
if (0 < data.features.length) {
// Const assignment.
const area = turf.area(data)
const roundedArea = Math.round(area * 100) / 100
// Set calculated area.
setCalculatedArea(roundedArea)
setDrawnPolygon(data)
} else {
// Set calculated area to null.
setCalculatedArea(null)
setDrawnPolygon(null)
}
}
const _FetchSuggestions = async query => {
// If no query is provided, return.
if (!query) return
// Error handling.
try {
// Fetch location suggestions.
const response = await fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(query)}.json?access_token=${process.env.REACT_APP_MAP_KEY}`)
// Parse response.
const data = await response.json()
// If there are features, set options.
if (data.features && 0 < data.features.length) {
// Set options.
setOptions(
data.features.map(feature => ({
'value': feature.place_name,
'label': feature.place_name,
'coordinates': feature.center
}))
)
} else {
// Set options to empty array.
setOptions([])
}
} catch (error) {
// Report failure.
toast({
'title': 'Location permission denied.',
'description': 'Please grant location permission to use this feature.',
'status': 'error',
'duration': 3000,
'isClosable': true
})
}
}
const _HandleSearch = selectedOption => {
// If no option is selected, return.
if (!selectedOption) return
// Find selected option.
const selected = options.find(j => j.value === selectedOption)
// If no coordinates are found, return.
if (!selected || !selected.coordinates) return
// Fly to the marker.
if (_mapRef.current) _mapRef.current.flyTo({ 'center': selected.coordinates })
// Add marker.
setMarkers(prevMarkers => [...prevMarkers, [selected.coordinates, 0, selected.label]])
}
const _FlyToLastCoordinates = () =>
_mapRef.current && 0 < markers.length && _mapRef.current.flyTo({
'center': markers[markers.length - 1][0],
'speed': 1.2,
'curve': 1.42,
'essential': true
})
const _HandleRadiusGeofencing = () => {
// Make sure there is at least one marker.
if (0 < markers.length && _mapRef.current) {
// Const assignment.
const [latitude, longitude] = markers[markers.length - 1][0]
const center = turf.point([longitude, latitude])
const radius = geoFencingRadius
const buffered = turf.buffer(center, radius, { 'units': 'miles' })
// Set drawn polygon.
setDrawnPolygon(buffered)
setCalculatedArea(turf.area(buffered))
}
}
const _HandleCoordinatesGeofencing = () => {
// Handle Coordinates Based Geofencing.
if (4 === coordinates.length) {
// Const assignment.
const _polygon = turf.polygon([[...coordinates, coordinates[0]]])
// Set drawn polygon.
setDrawnPolygon(_polygon)
setCalculatedArea(turf.area(_polygon))
} else {
// Show error toast.
toast({
'title': 'Invalid Coordinates',
'description': 'Please provide exactly 4 coordinates to define a polygon.',
'status': 'error',
'duration': 3000,
'isClosable': true
})
}
}
const _AskForLocationPermission = () =>
navigator.geolocation.getCurrentPosition(
position => {
console.log('===>', position)
// If user grants permission, fly to the location.
if (position.coords) {
/*
* Fly to the location.
* Make sure not to fly to the current location.
*/
if (
_mapRef.current && position.coords.longitude !== _.last(markers)?.[0]?.[0] && position.coords.latitude !== _.last(markers)?.[0]?.[1]
) {
// Const assignment.
const _locationCoordinates = [position.coords.longitude, position.coords.latitude]
// Update markers.
setMarkers(prevMarkers => [...prevMarkers, [_locationCoordinates, 0, 'Current Location']])
// Fly to the location.
_mapRef.current.flyTo({
'center': _locationCoordinates,
'speed': 1.2,
'curve': 1.42,
'essential': true
})
}
}
},
() => {
// Report failure for location permission.
toast({
'title': 'Location permission denied.',
'description': 'Please grant location permission to use this feature.',
'status': 'error',
'duration': 3000,
'isClosable': true
})
}
)
// Event handlers.
useEffect(() => {
// Handle Radius Based Geofencing.
if (selectedMethod === _selectionMethods[1]) _HandleRadiusGeofencing()
}, [geoFencingRadius, markers])
useEffect(() => {
// Handle Coordinates Based Geofencing.
if (selectedMethod === _selectionMethods[2]) _HandleCoordinatesGeofencing()
}, [coordinates])
useEffect(() => {
// Initialize map.
mapboxgl.accessToken = process.env.REACT_APP_MAP_KEY
// Update map reference.
_mapRef.current = new mapboxgl.Map({
'container': 'LocationPickerMap',
'style': 'mapbox://styles/mapbox/satellite-v9',
'zoom': 10,
'center': [-91.874, 42.76]
})
// Add navigation controls.
_mapRef.current.addControl(new mapboxgl.NavigationControl())
// Const assignment.
const _Draw = new MapboxDraw({
'displayControlsDefault': false,
'controls': {
'polygon': true,
'trash': true
},
'defaultMode': 'draw_polygon'
})
// Add event listeners.
_mapRef.current.on('draw.create', _UpdateArea)
_mapRef.current.on('draw.delete', _UpdateArea)
_mapRef.current.on('draw.update', _UpdateArea)
// Set draw ref.
_drawRef.current = _Draw
// Add draw control.
_mapRef.current.addControl(_Draw)
// Create a marker for the center of the map.
const _centerMarker = document.createElement('div')
// Update marker styles.
_centerMarker.className = 'center-marker'
_centerMarker.innerHTML = ''
_centerMarker.style.width = '25px'
_centerMarker.style.height = '25px'
_centerMarker.style.backgroundColor = 'rgba(255, 255, 255, 0.83)'
_centerMarker.style.borderRadius = '50%'
_centerMarker.style.display = 'flex'
_centerMarker.style.alignItems = 'center'
_centerMarker.style.justifyContent = 'center'
_centerMarker.style.backdropFilter = 'blur(25px)'
// Add marker to map.
new mapboxgl.Marker(_centerMarker).setLngLat(_mapRef.current.getCenter()).addTo(_mapRef.current)
// Add markersToShow to the map.
if (markersToShow && 0 < markersToShow.length) {
// Loop through markers.
markersToShow.forEach(marker => {
// Const assignment.
const el = document.createElement('div')
// Add custom marker styles.
el.className = 'marker'
el.style.backgroundImage = `url(${marker.thumnailStoredAt})`
el.style.width = '26px'
el.style.height = '26px'
el.style.backgroundPosition = '50% 50%'
el.style.backgroundSize = 'contain'
el.style.borderRadius = '12px'
el.style.backgroundRepeat = 'no-repeat'
el.style.backgroundColor = 'rgba(255, 255, 255, 0.53)'
el.style.backdropFilter = 'blur(25px)'
// Add custom marker to map.
new mapboxgl.Marker(el)
.setLngLat([marker.longitude, marker.latitude])
.addTo(_mapRef.current)
})
}
// Clean up on unmount.
return () => {
// Clean up map.
if (_mapRef.current) _mapRef.current.remove()
}
}, [])
React.useEffect(() => {
if (_mapRef.current && drawnPolygon) {
// Add the polygon source and layer to the map.
_mapRef.current.addSource('geofencing', {
'type': 'geojson',
'data': drawnPolygon
})
_mapRef.current.addLayer({
'id': 'geofence-fill',
'type': 'fill',
'source': 'geofencing',
'paint': {
'fill-color': '#088',
'fill-opacity': 0.5
}
})
_mapRef.current.addLayer({
'id': 'geofence-border',
'type': 'line',
'source': 'geofencing',
'paint': {
'line-color': '#ff0000',
'line-width': 2
}
})
// Clean up the layers and source when the component unmounts or the polygon changes.
return () => {
if (_mapRef.current.getLayer('geofence-fill')) {
_mapRef.current.removeLayer('geofence-fill')
}
if (_mapRef.current.getLayer('geofence-border')) {
_mapRef.current.removeLayer('geofence-border')
}
if (_mapRef.current.getSource('geofencing')) {
_mapRef.current.removeSource('geofencing')
}
}
}
}, [drawnPolygon])
console.log('===drawn Polygon', drawnPolygon)
// Return component.
return (
{
// Update search query.
setSearchQuery(e?.target?.value)
// Fetch suggestions.
_FetchSuggestions(e?.target?.value)
}}
onSelect={e => {
// Update search query.
setSearchQuery(e?.target?.value)
// Handle search.
_HandleSearch(e?.target?.value)
}}
options={options?.map(j => j?.value)}
/>
Geographical Area Bounding Box Selection based on the followings.
{_selectionMethods?.map(r => (
setSelectedMethod(r)}
bg={selectedMethod === r ? 'gray.300' : 'gray.50'}
color={selectedMethod === r ? 'gray.600' : 'gray.400'}
key={r}
>
{r}
))}
{selectedMethod === _selectionMethods[1] && (
Radius (in Miles)
1 Mile
100 Miles
setGeoFencingRadius(_.first(e))}
>
)}
{selectedMethod === _selectionMethods[2] && (
Enter 4 coordinates to define a polygon:
{[...Array(4)].map((__, index) => (
{
const updatedCoordinates = [...coordinates]
updatedCoordinates[index] = [parseFloat(e.target.value), coordinates[index]?.[1] || 0]
setCoordinates(updatedCoordinates)
}}
/>
))}
)}
Map {selectedMethod === _selectionMethods[2] ? `(Showing exact latitude & longitude)` : `(Showing inside ${geoFencingRadius} Miles)`}
{calculatedArea && (
AREA {calculatedArea?.toFixed(2)} .sq.m.
)}
)
}
/*
* PROPTYPES
*/
Index.propTypes = {
'markersToShow': PropTypes.array
}
/*
* EXPORTS
*/
export default Index
### Expected Behavior
Should render polygon as intendted.
### Actual Behavior
Polygon cursor to start drawing appears..everything in toolbox appears..but when i click to start drawing then nothing appears on map..no polygon no line etc.
Contributor guide
Research direction
Start with the supplied React component and reproduce the issue using mapbox-gl-js 3.10.0 in Edge or Safari. Trace the drawnPolygon state through the Mapbox source and layers, and verify whether the polygon is added after the map is ready. Done means the polygon renders visibly without console errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, react
- Domain
- frontend, web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100