mecode4food
mecode4food

Reputation: 320

Mapbox GL JS does not render layers after a certain zoom/distance

I have a map rendered with mapbox-gl with only two layers, a marker and a circle centered on a point. the distance is dynamic as it is based on a predefined distance in meters.

However after zooming in and moving away from the center, the layer abruptly disappears from the map render. I have tried to implement maxzoom and set tolerance to 0 on the geojson source, but the layers seem to still disappear after a certain zoom/distance from the center.

GIF to show what happens when you pan: https://i.sstatic.net/RIC5A.jpg

Have I missed out some attribute to improve/enlarge the rendering distance? I don't intend to use many points, so performance should not be an issue for this use case.

<template>
  <div id="mapContainer" class="basemap"></div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import mapboxgl, { CircleLayer } from 'mapbox-gl'

export default defineComponent({
  name: 'Map',
  mounted: function () {
    mapboxgl.accessToken =
      'abc'
    const map: mapboxgl.Map = new mapboxgl.Map({
      container: 'mapContainer',
      style: 'mapbox://styles/mapbox/streets-v11',
      center: [101.58165, 3.03837],
      zoom: 13,
      maxZoom: 17
    })

    const geofence = [
      {
        name: 'Location 1',
        lat: 3.037479466090297,
        lng: 101.58102250675641,
        distance: 1000
      }
    ]

    map.on('load', function () {
      map.loadImage('https://docs.mapbox.com/mapbox-gl-js/assets/custom_marker.png', function (error, image) {
        if (error) throw error
        if (image) {
          map.addImage('custom-marker', image)

          map.addSource('points', {
            type: 'geojson',
            data: {
              type: 'Feature',
              geometry: {
                type: 'Point',
                coordinates: [geofence[0].lng, geofence[0].lat]
              },
              properties: {
                title: geofence[0].name,
                distance: geofence[0].distance
              }
            },
            maxzoom: 22,
            tolerance: 0
          })

          map.addLayer({
            id: 'points',
            type: 'symbol',
            source: 'points',
            layout: {
              'icon-image': 'custom-marker',
              'text-field': ['get', 'title'],
              'text-font': ['Open Sans Semibold'],
              'text-offset': [0, 1.25],
              'text-anchor': 'top'
            }
          })

          const circleLayer: CircleLayer = {
            id: 'geofence',
            type: 'circle',
            source: 'points',
            paint: {
              'circle-radius': {
                stops: [
                  [0, 0],
                  [21, metersToPixelsAtMaxZoom(geofence[0].distance, geofence[0].lat)]
                ],
                base: 2
              },
              'circle-opacity': 0.5,
              'circle-color': 'blue',
              'circle-stroke-width': 2,
              'circle-stroke-color': 'black'
            }
          }
          map.addLayer(circleLayer)
        }
      })
    })
  }
})

const metersToPixelsAtMaxZoom = (meters: any, latitude: any) => meters / 0.075 / Math.cos((latitude * Math.PI) / 180)
</script>

<style lang="scss">
#mapContainer {
  width: auto;
  height: 400px;
}
</style>

Code shown is in TypeScript, but removing the types would render it just as well I am sure.

Upvotes: 1

Views: 1206

Answers (1)

Steve Bennett
Steve Bennett

Reputation: 126235

I think you're seeing an edge case that Mapbox GL JS doesn't really support.

The better way to handle this would be to use TurfJS to create a polygon with circle() and add that as a geojson layer.

Upvotes: 1

Related Questions