WebGLVectorLayer style filter does not work with large numeric values using style variables.
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
- 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,
// },
// },
],
});
Expected behavior filter should be applied correctly using style variables .
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>
Please have a look at #16257
@Nidhi-219 also it would be very helpful if you could use correct formatting for the code you're pasting, thanks!
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.
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.
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.
Thanks @Nidhi-219, I could indeed reproduce the filtering issue; looking into it
@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
Thanks for reply.
How this long values can be handled ?
You should be able to implement it like I described in https://github.com/openlayers/openlayers/issues/16257#issuecomment-2393987647.