openlayers icon indicating copy to clipboard operation
openlayers copied to clipboard

WebGLVectorLayer style filter does not work with large numeric values using style variables.

Open Nidhi-219 opened this issue 9 months ago • 10 comments

Describe the bug We use WebGLVectorLayer to create layer on map and added style filter variables to filter data based on user input values ., But filtered results on map does not match with actual data values.

To Reproduce

  1. create WebGLVectorLayer having numeric property having large values nearly 1744266040756 and apply between filter on that property using style variable
const variables = {
  mincount: 1744266033756,
  maxcount: 1744266040756,
};

const format = new GeoJSON();
const dataLayer = new WebGLVectorLayer({
  source: new VectorSource({
    url: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_regions_points.geojson',
    format,
  }),
  variables: variables,
  style: [
    {
      filter: [
        'between',
        ['get', 'count'],
        ['var', 'mincount'],
        ['var', 'maxcount'],
      ],
      style: {
        //'text-value': '1',

        'text-font': '16px sans-serif',
        'text-fill-color': 'red',
        'text-stroke-color': 'gray',
        'text-stroke-width': 4,
        'circle-radius': 10,
        'circle-fill-color': 'red',
        'circle-stroke-color': 'white',
        'circle-stroke-width': 0.5,
      },
      variables: variables,
    },
    // {
    //   //filter: ['>', ['get', 'count']],
    //   style: {
    //     'text-value': '2',

    //     'text-font': '16px sans-serif',
    //     'text-fill-color': 'red',
    //     'text-stroke-color': 'gray',
    //     'text-stroke-width': 2,
    //   },
    // },
  ],
});

sand box link :

Expected behavior filter should be applied correctly using style variables .

Nidhi-219 avatar Apr 10 '25 08:04 Nidhi-219

code main.js file:

import Map from 'ol/Map.js';
import View from 'ol/View.js';
import GeoJSON from 'ol/format/GeoJSON.js';
import Layer from 'ol/layer/Vector.js';
import Source from 'ol/source/Vector.js';
import WebGLVectorLayer from 'ol/layer/WebGLVector.js';
import VectorSource from 'ol/source/Vector.js';
const filterValue = 1744265078655;

const variables = {
  mincount: 1744266033756,
  maxcount: 1744266040756,
};

const format = new GeoJSON();
const dataLayer = new WebGLVectorLayer({
  source: new VectorSource({
    url: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_regions_points.geojson',
    format,
  }),
  variables: variables,
  style: [
    {
      filter: [
        'between',
        ['get', 'count'],
        ['var', 'mincount'],
        ['var', 'maxcount'],
      ],
      style: {
        //'text-value': '1',

        'text-font': '16px sans-serif',
        'text-fill-color': 'red',
        'text-stroke-color': 'gray',
        'text-stroke-width': 4,
        'circle-radius': 10,
        'circle-fill-color': 'red',
        'circle-stroke-color': 'white',
        'circle-stroke-width': 0.5,
      },
      variables: variables,
    },
    // {
    //   //filter: ['>', ['get', 'count']],
    //   style: {
    //     'text-value': '2',

    //     'text-font': '16px sans-serif',
    //     'text-fill-color': 'red',
    //     'text-stroke-color': 'gray',
    //     'text-stroke-width': 2,
    //   },
    // },
  ],
});

const map = new Map({
  layers: [
    new Layer({
      background: '#1a2b39',
      source: new Source({
        url: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_110m_land.geojson',
        format,
      }),
      style: {
        'fill-color': 'darkgray',
      },
    }),
    // new Layer({
    //   source: new Source({
    //     url: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_geography_regions_points.geojson',
    //     format,
    //   }),
    //   style: {
    //     'circle-radius': 10,
    //     'circle-fill-color': 'green',
    //     'circle-stroke-color': 'white',
    //     'circle-stroke-width': 0.5,
    //   },
    // }),
    dataLayer,
  ],
  target: 'map',
  view: new View({
    center: [0, 0],
    zoom: 1,
  }),
});

const info = document.getElementById('info');

map.on('loadend', function (evt) {
  let count = 1744266040756;
  dataLayer
    .getSource()
    .getFeatures()
    .forEach((feature) => {
      feature.set('count', count);
      count = count - 1000;
    });

  // dataLayer.getSource().refresh();
  const filteredFeatures = dataLayer
    .getSource()
    .getFeatures()
    .filter(
      (data) =>
        data.values_.count >= variables.mincount &&
        data.values_.count <= variables.maxcount,
    );

  info.style.visibility = 'visible';
  info.innerText = ' filtered feature ' + filteredFeatures.length + '\n';
  info.innerText = info.innerText + 'count values  \n';
  filteredFeatures.forEach((feature) => {
    info.innerText = info.innerText + feature.get('count') + '\n';
  });
});

let currentFeature;
const displayFeatureInfo = function (pixel, target) {
  const feature = target.closest('.ol-control')
    ? undefined
    : map.forEachFeatureAtPixel(pixel, function (feature) {
        return feature;
      });
  if (feature) {
    info1.style.left = pixel[0] + 'px';
    info1.style.top = pixel[1] + 'px';
    if (feature !== currentFeature) {
      info1.style.visibility = 'visible';
      info1.innerText = 'count : ' + feature.get('count');
    }
  } else {
    info1.style.visibility = 'hidden';
  }
  currentFeature = feature;
};

map.on('pointermove', function (evt) {
  if (evt.dragging) {
    info1.style.visibility = 'hidden';
    currentFeature = undefined;
    return;
  }
  displayFeatureInfo(evt.pixel, evt.originalEvent.target);
});

================================================================================== index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Style Expressions</title>
    <link rel="stylesheet" href="node_modules/ol/ol.css" />
    <style>
      .map {
        width: 100%;
        height: 400px;
      }

      #info1 {
        position: absolute;
        display: inline-block;
        height: auto;
        width: auto;
        z-index: 100;
        background-color: #333;
        color: #fff;
        text-align: center;
        border-radius: 4px;
        padding: 5px;
        left: 50%;
        transform: translateX(3%);
        visibility: hidden;
        pointer-events: none;
      }
    </style>
  </head>
  <body>
    <div id="map" class="map">
      <div id="info1"></div>
    </div>
    <div id="info"></div>
    <script type="module" src="main.js"></script>
  </body>
</html>

Nidhi-219 avatar Apr 10 '25 08:04 Nidhi-219

Please have a look at #16257

M393 avatar Apr 10 '25 08:04 M393

@Nidhi-219 also it would be very helpful if you could use correct formatting for the code you're pasting, thanks!

jahow avatar Apr 10 '25 08:04 jahow

Hi @M393 Thanks for reply.

I had look at issue #16257 , and based on your comment The filter works with float values, not double, so all three timestamps will be 1.72797e9f (=1727965568)

But i would like to know how timestamp filter is working in below example: https://openlayers.org/en/latest/examples/filter-webgl-line.html

Also , Is there any way to handle large values without converting them in lower values.

Nidhi-219 avatar Apr 15 '25 06:04 Nidhi-219

If you have precision issues, you need to somehow make it so that the variation between values is higher than the precision threshold. In the example you link, the low and high values used for interpolation are 1303200000 and 1303240000, which offers sufficient variation to overcome the precision issue.

jahow avatar Apr 15 '25 07:04 jahow

Hi @jahow

Its actually not a precision issue. I want filter layer data with long numbers using style variables.

sandbox : [https://codesandbox.io/p/sandbox/style-expressions-forked-4gwz5w]

In the example above i can see its able to filter long value as well but in my code it doesn't work.

Nidhi-219 avatar Apr 22 '25 12:04 Nidhi-219

Thanks @Nidhi-219, I could indeed reproduce the filtering issue; looking into it

jahow avatar Apr 24 '25 09:04 jahow

@Nidhi-219 So this is indeed a precision issue. If I change the min value from 1744266033756 to 266033756 (and all other values similarly), the filter works properly.

This means that because the numbers are so big, comparing with them in the shader does not give consistent results.

The values you use are in this order of magnitude:

1_744_266_033_756

In the line filter example, this is the order of magnitude:

    1_303_200_000

So your values are 10³ higher and probably past the precision threshold for the WebGL shaders.

jahow avatar Apr 24 '25 09:04 jahow

@jahow

Thanks for reply.

How this long values can be handled ?

Nidhi-219 avatar May 02 '25 07:05 Nidhi-219

You should be able to implement it like I described in https://github.com/openlayers/openlayers/issues/16257#issuecomment-2393987647.

M393 avatar May 02 '25 07:05 M393