3

I'm using OpenLayers display a map which can include vector layers with dynamic features (ie, the lines/points change frequently). These vector layers use GeoJSON in a vector source and use javascript promises to query/return the new GeoJSON features.

This was all working fine in OpenLayers v3.x but stopped working after upgrading to v6.5. If I swap v3 back in, then it all works correctly again, so I am 100% confident that the problem is related to some difference between v3 and v6. I can't see anything in the documentation that has changed enough to break this, but I'm sure I must have missed something.

With v6, these vector layers simply never appear on the map at all, and no errors are logged.

Below is the code that works fine in v3 but no longer works in v6 of OpenLayers.

Calling the redrawLayer() function is what triggers the vector layer to refresh its source.

Any ideas on what I'm doing wrong here, or what has changed between v3 and v6 that causes this to no longer work?

UPDATE ONE - Another notable oddity: When one of these vector layers is present in the map in v6, even the basemap raster tile layer (eg, OSM) misbehaves. Some (random?) tiles don't load! When none of these vector layers are present, all the raster tiles load fine. So is there something larger at play here that I've missed that could be beyond simply the vector layer/source? Anybody seen behaviour like this before?

UPDATE TWO - Fixed in JS Fiddle but not in app: The "ADDED FEATURES FROM PROMISED JSON:..." message in the Vector Source loader function is now being logged correctly, after uncommenting the redraw function's refresh line and commenting out the other two lines (as per comments below). See actual JSON content in an example full log message below.

However, the layer still never appears on the map. (The JS Fiddle at jsfiddle.net/nsands/sxfh2k09/20 DOES work, so I'm still trying to figure out the relevant differences.)

UPDATE THREE - added previously excluded relevant code after resolving the app issue:

As is often the case, it turns out that the cause of the problem was not where I thought it was, so the actual problem code was not included in the original version of this post. I've updated the code now, to include all relevant portions (now that I've solved it and know what the relevant parts actually are).

I'll also post my solution as an answer, in case it helps anybody else upgrading from OLv3 to OLv6.

var map = new ol.Map({
    layers: [
        new ol.layer.Tile({
            source: new ol.source.OSM(),
        }),
    ],
    target: 'map',
    view: new ol.View({
        center: [-13583700, 4486300],
        zoom: 17,
    }),
});

var vectorLayers = {};
var dpi = 72;

function mapScale() {
    var unit = map.getView().getProjection().getUnits();
    var resolution = map.getView().getResolution();
    var inchesPerMetre = 39.37;
    
    return resolution * ol.proj.METERS_PER_UNIT[unit] * inchesPerMetre * dpi;
}

var layerStyles = function (feature, resolution) {
    if ( mapScale() < 1000000 ) {
        return [
    new ol.style.Style({stroke: new ol.style.Stroke({color: '#000000', width: 6.000000})}),
new ol.style.Style({stroke: new ol.style.Stroke({color: '#FFFF00', width: 2.000000})}),
    new ol.style.Style({text: new ol.style.Text({text: feature.get('label')['1000000'], font: ' 20pt sans-serif', fill: new ol.style.Fill({color: '#FFFF00'}), stroke: new ol.style.Stroke({color: '#000000', width: 4.0}), offsetX: 14, textAlign: 'start', textBaseline: 'alphabetic'})})
]
    }
};

var json0 = '{"type":"FeatureCollection","features":[]}'
var json1 = '{"type":"FeatureCollection","features":[{"geometry":{"type":"LineString","coordinates":[[-122.02320166,37.337513659999999,0],[-122.02327787,37.337692109999999,0]]},"type":"Feature","properties":{"label":{"1000000":""}}}]}'
var json2 = '{"type":"FeatureCollection","features":[{"geometry":{"type":"LineString","coordinates":[[-122.02320166,37.337513659999999,0],[-122.02327787,37.337692109999999,0],[-122.02363136,37.33769392,0],[-122.02421372000001,37.337723150000002,0]]},"type":"Feature","properties":{"label":{"1000000":""}}}]}'
var json3 = '{"type":"FeatureCollection","features":[{"geometry":{"type":"LineString","coordinates":[[-122.02320166,37.337513659999999,0],[-122.02327787,37.337692109999999,0],[-122.02363136,37.33769392,0],[-122.02421372000001,37.337723150000002,0],[-122.02439493,37.337724039999998,0]]},"type":"Feature","properties":{"label":{"1000000":""}}}]}'

var testJSON = json0

function addVectorLayer(layerIndex, layerProj, styles) {
//  var geoJSONFormat = new ol.format.GeoJSON({defaultDataProjection: layerProj});
    var geoJSONFormat = new ol.format.GeoJSON({dataProjection: layerProj});
    var newLayer = new ol.layer.Vector({
        style: styles,
        source: new ol.source.Vector({
            strategy: ol.loadingstrategy.bbox,
            loader: function(extent, resolution, proj) {
                var convertedExtent = ol.proj.transformExtent(extent, proj, layerProj);
                var vectorSource = this
//              var promise = jsonPromise(layerIndex, convertedExtent)
//              promise.then(function(jsonString, errorString) {
//                  if ( jsonString ) {
                jsonString = testJSON;
                        var features = [];
                        try {
                            features = geoJSONFormat.readFeatures(jsonString, {
                                featureProjection: proj,
                            });
                            if ( features.length ) {
                                vectorSource.addFeatures(features);
                                console.log("ADDED FEATURES FROM PROMISED JSON: " + jsonString)
                            }
                        } catch (err) {
                            console.log("Failed to add features from JSON:");
                            console.log(jsonString);
                            console.log(err);
                        }
//                  } else if ( errorString ) {
//                      console.log(errorString)
//                  }
//              }, function(err) {
//                  console.log(err)
//              });
            },
        }),
    });
    vectorLayers[layerIndex] = newLayer;  //  NB:  Integer is automatically converted to string here for dictionary indexing

    map.addLayer(newLayer);

    return newLayer;
}

function redrawLayer(layer) {
    console.log("REDRAWING LAYER");
    layer.getSource().clear(true);
    layer.getSource().changed();
//  layer.getSource().refresh();
}

var vectorLayer = addVectorLayer('0', 'EPSG:4326', layerStyles);

ADDED FEATURES FROM PROMISED JSON: {"type":"FeatureCollection","features":[{"geometry":{"type":"LineString","coordinates":[[-122.03478142,37.332149149999999,0],[-122.03479307000001,37.331697120000001,0],[-122.0347866,37.3312174,0],[-122.03473296,37.331086509999999,0]]},"properties":{"label":{"1000000":""}},"type":"Feature"}]}

8
  • In the initial version of the question iOS and WKWebView were mentioned. Does omission of this component in the edited question mean that behavior is the same in usual browsers?
    – TomazicM
    Commented May 27, 2021 at 7:44
  • I don't think that the browser and OS are relevant, but have yet to confirm. So I removed that part of the question for now to avoid the potential for (what I believe would be) false leads. I'm still trying to figure out a good stand-alone browser test case that includes the JS Promise aspect. Commented May 27, 2021 at 8:06
  • Your code is working with OL6 for me jsfiddle.net/j5frnLzb/1 Is your view in a custom projection?
    – Mike
    Commented May 27, 2021 at 11:17
  • My experience with triggered functions in OpenLayers is that it depends a lot on environment where they are fired. Many times when trigger function was called within event processing function, I had to call it through setTimeout function with small delay to make it work correctly.
    – TomazicM
    Commented May 27, 2021 at 16:04
  • If you comment out lines layer.getSource().clear(true); and layer.getSource().changed(); and uncomment line layer.getSource().refresh(), it seems to work OK.
    – TomazicM
    Commented May 28, 2021 at 7:46

1 Answer 1

1

As it turns out, the problem code was not initially included in the original question. Now that I've figured out where the problem really was, I've updated the OP to include all relevant code. One of the reasons that it was not included was that I was trying to post the smallest version of the code that would reproduce the problem. However, in this case there were TWO items that both caused problems that resulted in the layer not appearing.

FIRSTLY, as pointed out by @TomazicM, I needed to use layer.getSource().refresh() in OLv6 to tell the vector layer to update. Although I had tried this earlier, this did not resolve the problem in my app, but it DID resolve it in a simplified JS Fiddle which was being used to emulate the issue.

But this was really just obscuring a more critical issue...

SECONDLY, the relevant parts of code that were excluded from the OP were all related to the layer style which is injected dynamically in the app, and is often a complex set of styles in a style function. The function also checks the current map scale, and selects which style components to use depending on map scale. One of the ways that OpenLayers v3 used to be able to determine map scale included making use of ol.proj.METERS_PER_UNIT[unit]. THIS IS WHAT NO LONGER WORKS IN OpenLayers v6 !!! I think there may be a similar ol.proj.unit.METERS_PER_UNIT[unit] option that I could use in v6, but I opted to use map.getView().getProjection().getMetersPerUnit() now, instead.

So to sum up, the solution was to replace:

    return resolution * ol.proj.METERS_PER_UNIT[unit] * inchesPerMetre * dpi;

with:

    return resolution * map.getView().getProjection().getMetersPerUnit() * inchesPerMetre * dpi;

A working JS Fiddle that closely resembles what my app does is at: https://jsfiddle.net/nsands/sxfh2k09/36/ . This is where I really solved the problem eventually. (Click on the buttons in order to simulate the app updating the layer.)

Thank you very much to those who helped me out along the way (or attempted to). My humble apologies for not including the relevant code originally. Sometimes it can be difficult to figure out what the relevant code actually is. (One of my excuses is that it is quite difficult to access console messages in my app sometimes. The app attempts to hijack the JavaScript console and redirect it to STDOUT, but that doesn't always work reliably.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.