maplibre / maplibre-gl-js

MapLibre GL JS - Interactive vector tile maps in the browser
https://maplibre.org/maplibre-gl-js/docs/
Other
6.29k stars 685 forks source link

Camera follow moving feature, feature will shaking #4455

Closed WuMianzhi closed 1 month ago

WuMianzhi commented 1 month ago

I want to use a camera to follow a moving marker, and the marker should behave as if it is in a fixed position on the screen. However, it always shakes slightly, like this:

https://github.com/user-attachments/assets/c6dae8ad-cf4b-42ca-8fec-f78a3231c489

Here is a reproduction of my work.

<!DOCTYPE html>
<html lang="en">

<head>
  <title>Display buildings in 3D</title>
  <meta property="og:description" content="Use extrusions to display buildings' height in 3D." />
  <meta charset='utf-8'>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel='stylesheet' href='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css' />
  <script src='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js'></script>
  <style>
    body {
      margin: 0;
      padding: 0;
    }

    html,
    body,
    #map {
      height: 100%;
    }
  </style>
</head>

<body>
  <div id="map"></div>
  <script>
    const MAPTILER_KEY = 'get_your_own_OpIi9ZULNHzrESv6T2vL';
    const map = new maplibregl.Map({
      style: `https://api.maptiler.com/maps/basic-v2/style.json?key=${MAPTILER_KEY}`,
      center: [-77.035915, 38.889814],
      zoom: 12,
      pitch: 45,
      bearing: -23.2,
      container: 'map',
      antialias: true
    });

    let i = 0
    let step = 0

    map.showTileBoundaries = true

    // The 'building' layer in the streets vector source contains building-height
    // data from OpenStreetMap.
    map.on('load', () => {
      // Insert the layer beneath any symbol layer.
      const layers = map.getStyle().layers;

      let labelLayerId;
      for (let i = 0; i < layers.length; i++) {
        if (layers[i].type === 'symbol' && layers[i].layout['text-field']) {
          labelLayerId = layers[i].id;
          break;
        }
      }

      map.addSource('openmaptiles', {
        url: `https://api.maptiler.com/tiles/v3/tiles.json?key=${MAPTILER_KEY}`,
        type: 'vector',
      });

      const marker = new maplibregl.Marker()
        .setLngLat([-77.035915, 38.889814])
        .addTo(map);

      const move = () => {
        marker.setLngLat([-77.035915, 38.889814 + step * 0.001])
        map.jumpTo({ center: [-77.035915, 38.889814 + step * 0.001] })
        step++;

        requestAnimationFrame(move)
      }
      move()
    });
  </script>
</body>

</html>
HarelM commented 1 month ago

There is a routing of the market position at move end: https://github.com/maplibre/maplibre-gl-js/blob/163a5132a96759d8a412ce63f83ae43b65938957/src/ui/marker.ts#L604

This is probably what's causing this shake. Not sure I know how to solve this.

WuMianzhi commented 1 month ago

There is a routing of the market position at move end:

https://github.com/maplibre/maplibre-gl-js/blob/163a5132a96759d8a412ce63f83ae43b65938957/src/ui/marker.ts#L604

This is probably what's causing this shake. Not sure I know how to solve this.

Thanks for you reply, i also tried use geojson source to add symbol or circle , and also has the same issue, maybe its due to the same reason?

WuMianzhi commented 1 month ago

https://github.com/user-attachments/assets/b43ecde8-6f57-4fe4-a41f-c012323ad1c7

i think in the way of set geojson may due to async, my code again

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Display buildings in 3D</title>
    <meta
      property="og:description"
      content="Use extrusions to display buildings' height in 3D."
    />
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
      rel="stylesheet"
      href="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css"
    />
    <script src="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js"></script>
    <style>
      body {
        margin: 0;
        padding: 0;
      }

      html,
      body,
      #map {
        height: 100%;
      }
    </style>
  </head>

  <body>
    <div id="map"></div>
    <script>
      const MAPTILER_KEY = "get_your_own_OpIi9ZULNHzrESv6T2vL";
      const map = new maplibregl.Map({
        style: `https://api.maptiler.com/maps/basic-v2/style.json?key=${MAPTILER_KEY}`,
        center: [-77.035915, 38.889814],
        zoom:10,
        pitch: 45,
        bearing: -23.2,
        container: "map",
        antialias: true,
      });

      let i = 0;
      let step = 0;

      map.showTileBoundaries = true;

      // The 'building' layer in the streets vector source contains building-height
      // data from OpenStreetMap.
      map.on("load", async () => {
        // Insert the layer beneath any symbol layer.
        const layers = map.getStyle().layers;

        let labelLayerId;
        for (let i = 0; i < layers.length; i++) {
          if (layers[i].type === "symbol" && layers[i].layout["text-field"]) {
            labelLayerId = layers[i].id;
            break;
          }
        }

        map.addSource("openmaptiles", {
          url: `https://api.maptiler.com/tiles/v3/tiles.json?key=${MAPTILER_KEY}`,
          type: "vector",
        });

        image = await map.loadImage(
          "https://upload.wikimedia.org/wikipedia/commons/7/7c/201408_cat.png"
        );

        map.addImage("cat", image.data);
        const testSource = map.addSource("point", {
          type: "geojson",
          data: {
            type: "FeatureCollection",
            features: [
              {
                type: "Feature",
                geometry: {
                  type: "Point",
                  coordinates: [-77.035915, 38.889814],
                },
              },
            ],
          },
        });

        map.addLayer({
          id: "points",
          type: "symbol",
          source: "point",
          layout: {
            "icon-image": "cat",
            "icon-size": 0.125,
          },
        });

        const marker = new maplibregl.Marker()
          .setLngLat([-77.035915, 38.889814])
          .addTo(map);

        const move = () => {
          map.getSource("point").setData({
            type: "FeatureCollection",
            features: [
              {
                type: "Feature",
                geometry: {
                  type: "Point",
                  coordinates: [-77.035915, 38.889814 + step * 0.0001],
                },
              },
            ],
          });
          marker.setLngLat([-77.035915, 38.889814 + step * 0.0001])
          map.jumpTo({ center: [-77.035915, 38.889814 + step * 0.0001] });
          step++;

          requestAnimationFrame(move);
        };

        move();
      });
    </script>
  </body>
</html>
HarelM commented 1 month ago

Is this solved by #4458?

WuMianzhi commented 1 month ago

I think this is fixed in the latest version, by setSubpixelPositioning(true) thanks for your teams contribute

https://github.com/user-attachments/assets/33bd7e72-08b2-458b-9f92-2405a0fd335e

HarelM commented 1 month ago

Fixed by #4458.