openlayers / ol-mapbox-style

Use Mapbox Style objects with OpenLayers
https://unpkg.com/ol-mapbox-style/dist/examples/index.html
BSD 2-Clause "Simplified" License
337 stars 119 forks source link

Declutter of labels in vector tiles #1177

Closed MarcelGeo closed 1 month ago

MarcelGeo commented 1 month ago

Hi ol-mapbox-style team. We are prototyping vector tiles rendering in openlayers. There's problem in ol-mapbox-style, where labels are overlapping (countries labels).

If I use declutter: true in VectorTileLayer, there are no labels anymore :(

image

If I disable declutter, labels are overlapping and It is strange...the same style.json in QGIS and everything is ok.

image

We are fetching style from url and then using stylefunction:

stylefunction(
        layer,
        data, // fetched from style.json
        data.layers?.filter((l) => Boolean(l.source))?.map((l) => l.id)
      )
ahocevar commented 1 month ago

Hard to say without seeing more of your code, and the style.json.

MarcelGeo commented 1 month ago

Hard to say without seeing more of your code, and the style.json.

Style: https://tiles.dev.merginmaps.com/styles/default.json

Definition of layer:

const layer = new VectorTileLayer({
        source: new VectorTileSource({
          format: new MVT(),
          url: `${tileUrl}/data/default/{z}/{x}/{y}.pbf`,
          tileGrid: tileGrid
        }),
        zIndex: 0,
        declutter: false,
        visible: true
      })

Is it enough?

ahocevar commented 1 month ago

Unfortunately no. The resources referenced in the style do not point to a public url, and the context of the layer is not clear. Is that the only layer of the map?

MarcelGeo commented 1 month ago

Hi @ahocevar ...

      const tileUrl = "https://tiles.dev.merginmaps.com"
      const tileGrid = createXYZ({
        extent: getProjection('EPSG:3857').getExtent(),
        tileSize: 512,
        maxZoom: 14
      })
      const layer = new VectorTileLayer({
        source: new VectorTileSource({
          format: new MVT(),
          url: `${tileUrl}/data/default/{z}/{x}/{y}.pbf`,
          tileGrid: tileGrid
        }),
        zIndex: 0,
        declutter: false,
        visible: true
      })
      const mousePositionControl = new MousePosition({
        coordinateFormat: createStringXY(4),
        projection: 'EPSG:4326'
      })
      const map = new Map({
        layers: [layer],
        view: new View({
          center: [0, 0],
          zoom: 0,
          projection: this.proj,
          extent: [-20026376.39, -20048966.1, 20026376.39, 20048966.1],
          maxZoom: 25,
          constrainResolution: true
        }),
        controls: defaultControls().extend([mousePositionControl])
      })

      map.getView().fit([-20026376.39, -20048966.1, 20026376.39, 20048966.1], {
        maxZoom: 16,
        nearest: false
      })

      // Then apply style from defaul.json
      const baseUrl = "https://tiles.dev.merginmaps.com"
     const api = axios.create({ withCredentials: false })
      const response = await api.get(`${baseUrl}/styles/default.json`)
      const { data } = response
      await applyBackground(map, data)
      stylefunction(
        layer,
        data,
        data.layers?.filter((l) => Boolean(l.source))?.map((l) => l.id)
      )

Other layers are added after some business logic is done ... thats are ordinary WMTS layers in different grid

          const wmtsLayer = new TileLayer({
            visible: false,
            properties: {
              title: l.name,
              projectsLayer: true
            },
            zIndex: 1,
            source: new WMTS({
              url: `SOME URL`,
              layer: 'Some name',
              matrixSet: 'EPSG:3857',
              format: 'image/png',
              style: 'default',
              wrapX: true,
              tileGrid
            })
          })

I'm currently not able to resolve issue with resources in style.json ... Map is having one background map (vector tiles from defined source) and others are custom WMTS layers.

ahocevar commented 1 month ago

There are still unaccessible resources referenced from the style, and the server does not send CORS headers.

I'm wondering why you chose to use the lowest level API, instead of applyStyle for the VectorTile layer. See https://github.com/openlayers/ol-mapbox-style/blob/main/examples/apply-layergroup.js.

MarcelGeo commented 1 month ago

As I mentioned before ... I am not able to update resources now. Tiles base url is the same as for styles.

I can try to use applyStyle / apply . Let me try it. But I think the backend for stylefunction and apply style should be the same? The same style would be applied in both functions.

ahocevar commented 1 month ago

Yes, I'm just trying to reproduce your problem, and I'd be surprised if this is a bug in ol-mapbox-style, because we're doing the exact same thing over and over, and it works. Just take a look at the examples, e.g. https://openlayers.org/ol-mapbox-style/examples/tilejson-vectortile.html.

MarcelGeo commented 1 month ago

Definitely ... I don't see any overlapping labels there ... image

Is there any dependency on this attribute in openlayers and decluttering: text-allow-overlap false

ahocevar commented 1 month ago

Yes, it requires v9 of OpenLayers to work properly.

MarcelGeo commented 1 month ago

Yes, it requires v9 of OpenLayers to work properly.

I am using newest OL versions

MarcelGeo commented 1 month ago

Hi @ahocevar . I tried to use following higher level methods

!(layer instanceof VectorLayer || layer instanceof VectorTileLayer)

My example modified with applyStyle

import VectorTileLayer from 'ol/layer/VectorTile'

      const tileUrl = "https://tiles.dev.merginmaps.com"
      const tileGrid = createXYZ({
        extent: getProjection('EPSG:3857').getExtent(),
        tileSize: 512,
        maxZoom: 14
      })
      const layer = new VectorTileLayer({
        source: new VectorTileSource({
          format: new MVT(),
          url: `${tileUrl}/data/default/{z}/{x}/{y}.pbf`,
          tileGrid: tileGrid
        }),
        zIndex: 0,
        declutter: false,
        visible: true
      })
      const mousePositionControl = new MousePosition({
        coordinateFormat: createStringXY(4),
        projection: 'EPSG:4326'
      })
      const map = new Map({
        layers: [layer],
        view: new View({
          center: [0, 0],
          zoom: 0,
          projection: this.proj,
          extent: [-20026376.39, -20048966.1, 20026376.39, 20048966.1],
          maxZoom: 25,
          constrainResolution: true
        }),
        controls: defaultControls().extend([mousePositionControl])
      })

      map.getView().fit([-20026376.39, -20048966.1, 20026376.39, 20048966.1], {
        maxZoom: 16,
        nearest: false
      })

      // Then apply style from defaul.json
      const baseUrl = "https://tiles.dev.merginmaps.com"
      const api = axios.create({ withCredentials: false })
      const response = await api.get(`${baseUrl}/styles/default.json`)
      const { data } = response
      await applyBackground(map, data)
      applyStyle(
        layer,
        data,
      )

Exception:

Uncaught (in promise) Error: Can only apply to VectorLayer or VectorTileLayer
ahocevar commented 1 month ago

Either you're using an old version of ol-mapbox-style or OpenLayers, or you need to dedupe OpenLayers. What is the output of npm ls ol?

MarcelGeo commented 1 month ago

mhh

ol-mapbox-style@12.3.4
  │ └── ol@9.2.4 deduped
  └── ol@9.2.4

yarn list snippet

ol-mapbox-style@12.3.4
│  ├─ @mapbox/mapbox-gl-style-spec@^13.23.1
│  └─ mapbox-to-css-font@^2.4.1
├─ ol@9.2.4
│  ├─ color-rgba@^3.0.0
│  ├─ color-space@^2.0.1
│  ├─ earcut@^2.2.3
│  ├─ geotiff@^2.0.7
│  ├─ pbf@3.2.1
│  └─ rbush@^3.0.1
ahocevar commented 1 month ago

Sorry, if you cannot provide runnable code that others can debug, I'm afraid we won't be able to solve this here.

MarcelGeo commented 1 month ago

Ok @ahocevar ... thank you for your help . It's very cool, that there is so fast response from ol-mapbox-style team :fire: :fire:

We have to fix issues in style.json and then try to use higher level apply function. applyStyle (with updateSource: false) is still not possible to use, because instanceof error in vite. I'm not sure why.

I'm not sure If it help us to use higher level functions to avoid labels overlaps. I'll be back If something will change.

MarcelGeo commented 1 month ago

@ahocevar ... example with openlayers and applyStyle function (overlapping labels). Let me know If it's running for you.

If I change declutter to true, then labels are not overlapping :+1: :+1: ... It's interesting, that stylefunction does the same job... I have to revisit our current implementation

https://codesandbox.io/p/devbox/mapbox-vector-tiles-forked-zc2c5v?welcome=true

Update: In our codebase was problem with imports, we imported Map and View directly

import { Map, View } from 'ol'

decluttering is running properly

import Map from 'ol/Map'
ahocevar commented 1 month ago

Glad you were able to make it work, the way you import Map and View should not make a difference though.

Can we close this?

MarcelGeo commented 1 month ago

We migrated from old openlayers versions. Definitely It was issue with import there (I don't understand why). in vite are any issues with using ol-mapbox-style. It's not possible to use some functions because instanceof issues in lib. instanceof tests are not running properly in ol-mapbox-style :)

Of course I'm closing this :)

ahocevar commented 1 month ago

If instanceof checks don't work properly, ol might not be deduped properly in these setups. When you run npm ls ol, all occurrences of ol should be the same version. If that's the case, do you have an example for an instanceof Check that still fails?

MarcelGeo commented 1 month ago

If instanceof checks don't work properly, ol might not be deduped properly in these setups. When you run npm ls ol, all occurrences of ol should be the same version. If that's the case, do you have an example for an instanceof Check that still fails?

There is result of npm ls ol https://github.com/openlayers/ol-mapbox-style/issues/1177#issuecomment-2249699388

I think these issues are based on vite dependencies handling. I tried also

optimizeDeps: { exclude: ['ol', 'ol-mapbox-style']}

There are also issues in vite directly:

But, I'm not sure what's the problem. In codesandbox I'm not able to simulate this situation.

ahocevar commented 1 month ago

Strange. I'm using vite in all my recent projects as well, and never had instanceof check problems with ol and ol-mapbox-style.

I also cannot reproduce your problem with a repository created with

npx create-ol-app my-test-app

which uses vite.