mapbox / mapbox/mapbox-gl-js

MapBox not drawing Polygon at all

Open
#13,418 0 comments 0 reactions 0 assignees View on GitHub
auto-triaged bug :lady_beetle:
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.

Image

**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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.