Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Icon image does not appear in legend unless it's shown on map #442

Open
rdewit opened this issue Jun 24, 2023 · 6 comments
Open

Icon image does not appear in legend unless it's shown on map #442

rdewit opened this issue Jun 24, 2023 · 6 comments
Labels
bug Something isn't working

Comments

@rdewit
Copy link

rdewit commented Jun 24, 2023

Bug

Describe the bug
When a style contains an Icon with an image, the legend will not show it unless it's also visible on the map.

To Reproduce
Steps to reproduce the behavior:

  1. Go to https://geostyler.github.io/geostyler-demo/
  2. Click on 'Display: Legend' in the right panel
  3. Observe the red line for 'Rule 1' in the legend
  4. Then click on the symbolizer preview for 'Rule 1' in the Graphical Editor
  5. Change the 'Kind' to 'Icon'
  6. Observe a now empty legend image for 'Rule 1'
  7. Now click on 'Preview Map' and then back to 'Legend'
  8. Observe the legend image now showing

Expected behavior
The legend image(s) should be visible even when the feature has not been drawn on the map (yet).

Screenshots
The screenshot shows the problem in our application: the icons for 'Accessible parking' and 'Off-leash area 6am to 9am only' do not show because the features that have those icons are not visible on the map. When we redraw the legend when those features are visible, they show as expected.

geostyler-legend-not-drawing-icons

Note: the icons in the screenshot are cut off because geostyler-legend does not yet support offsets (which are used in this style). The style of these layers has 3 SVG icons: one for the 'bubble', one for the contents of the bubble and a separate shadow icon. The latter is showing for all styles in the screenshot because it's showing for at least one visible feature.

Environment:
This happens both on the latest versions of Firefox and Chrome and both on Linux and Windows.

Additional context
It seems that something is preventing the icon image(s) from loading (correctly) in the legend code. When looking at the network tab in the dev tools, I see the image being fetched. I have had similar issues in the past with our custom (non-geostyler) legend where when an image is first loaded without crossOrigin: anonymous and then again without crossOrigin, the map would refuse to show the image due to the browser serving the image from the cache that was stored without crossOrigin and tainting the canvas. I wonder if this might be something similar.

@rdewit rdewit added the bug Something isn't working label Jun 24, 2023
@KaiVolland
Copy link
Contributor

Hey @rdewit, thanks for the report.

@rdewit
Copy link
Author

rdewit commented Jul 18, 2023

Hopefully it helps finding out what's causing it, @KaiVolland . I've tried all sorts of things in the app side of things (like pre-loading the SVGs) but none of them worked. Happy to help out where I can.

@KaiVolland
Copy link
Contributor

Hey @rdewit, actually a timing/loading issue with the svg was my first guess too. Maybe i'll find some time in the next two weeks to have a closer look at this. But i can't promise. :/

@rdewit
Copy link
Author

rdewit commented Oct 14, 2024

I finally found out what's causing this: in getRuleIcon, the Icon images that are part of the styleItem from olStyle, do not automatically get loaded. The problem is similar to this OpenLayers issue.

Calling styleItem.getImage().load() just before renderer.setStyle(styleItem) improves the behaviour somewhat but in order to guarantee that the image is loaded, it will be necessary to wait for the load event before proceeding.

Unfortunately, I do not have the time to provide a PR at the moment. If someone wants to tackle this, I can provide my POC code for inspiration.

@jansule
Copy link
Contributor

jansule commented Oct 17, 2024

Thanks for the diving into the issue for such a long time @rdewit. It would be great, if you could share your POC code here so that there is a point to start from.

@rdewit
Copy link
Author

rdewit commented Oct 18, 2024

Thanks, @jansule. Below is the diff agains master. It's a bit of a clumsy way of sharing the POC but it shows where the image loading was not happening and a possible way for triggering and waiting for that to happen.

The problem comes down to ol only triggering the loading of the icon image when a feature gets rendered. See: https://openlayers.org/en/latest/apidoc/module-ol_style_Icon-Icon.html#load

Unfortunately ol does not seem to have an easy way to wait for an Icon image to load (i.e. no promise), making the detection of when an image has completed loading cumbersome. Hopefully I have overlooked something obvious.

It's relatively hard to reproduce this solution in geostyler-demo: one has to disable both the code editor and the map preview as they themselves trigger the icon images to load (due to both adding the icon to a map).

I've tried creating a test for detecting when the image is loaded but even with trying to load inline images, the tests times out, so I must be doing something wrong there.

Hopefully this gives a bit more of an insight. Happy to help out where I can.

diff --git a/src/LegendRenderer/LegendRenderer.ts b/src/LegendRenderer/LegendRenderer.ts
index 1863f9d..218063f 100644
--- a/src/LegendRenderer/LegendRenderer.ts
+++ b/src/LegendRenderer/LegendRenderer.ts
@@ -3,6 +3,7 @@ import OlGeometry from 'ol/geom/Geometry';
 import OlGeomPoint from 'ol/geom/Point';
 import OlGeomPolygon from 'ol/geom/Polygon';
 import OlGeomLineString from 'ol/geom/LineString';
+import OlImageState from 'ol/ImageState';
 import OlStyle from 'ol/style/Style';
 import Renderer from 'ol/render/canvas/Immediate';
 import { create as createTransform } from 'ol/transform';
@@ -174,6 +175,7 @@ export class LegendRenderer {
         symbolizers: rule.symbolizers
       }]
     };
+
     return new Promise(async (resolve, reject) => {
       function drawGeoms(){
         geoms.forEach((geom: OlGeometry) => renderer.drawGeometry(geom));
@@ -187,11 +189,17 @@ export class LegendRenderer {
           olStyle = <OlStyle | OlStyle[]>olStyle(new OlFeature(geoms[0]), 1);
         }
         if (Array.isArray(olStyle)) {
-          olStyle.forEach((styleItem: OlStyle) => {
+          // Trigger loading of all images and wait for them to complete loading
+          await Promise.all(olStyle.map((styleItem) => this.loadStyleImage(styleItem)));
+
+          olStyle.forEach(async (styleItem: OlStyle) => {
             renderer.setStyle(styleItem);
             drawGeoms();
           });
         } else {
+          // Trigger loading of image and wait for it to complete loading
+          await this.loadStyleImage(olStyle);
+
           // @ts-expect-error TODO fix type errors
           renderer.setStyle(olStyle);
           drawGeoms();
@@ -203,6 +211,34 @@ export class LegendRenderer {
     });
   };
 
+  loadStyleImage = (styleItem?: OlStyle) => new Promise(async (resolve, reject) => {
+    const imgLoaded = () => resolve(true);
+    const imgNotLoaded = () => resolve(false);
+
+    const styleItemImage = styleItem?.getImage();
+    if (!styleItemImage) {
+      return imgNotLoaded();
+    }
+
+    // Image is already loaded
+    if (styleItemImage.getImageState() === OlImageState.LOADED) {
+      return imgLoaded();
+    }
+
+    // Get the image element
+    const img = styleItemImage.getImage(1);
+    if (!img) {
+      return imgNotLoaded();
+    }
+
+    // @ts-expect-error: TODO: Handle ImageBitmaps
+    img.addEventListener?.('error', imgNotLoaded);
+    // @ts-expect-error: TODO: Handle ImageBitmaps
+    img.addEventListener?.('load', imgLoaded);
+
+    styleItemImage.load();
+  });
+
   /**
    * Render a single legend.
    * @param {LegendConfiguration} config the legend config

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
No open projects
Status: Todo
Development

No branches or pull requests

3 participants