PixiOverlay on leaflet, cant add another image because it is already had an entry

Viewed 188

I'm using Pixi with PixiOverlay on leaflet. I have the following jsfiddle for a dummy simulation. The objective: once you click Add Image 2 - it adds a picture of a hamster randomly on the map.

It (almost) work.

the problem:

Error message: "BaseTexture added to the cache with an id [hamster] that already had an entry"

I couldn't figure our where to put the loader and how to integrate it properly in terms of code organization: (do I need to use it only once?) what if I have other layers to add? So I assume my challenge is here:

this.loader.load((loader, resources) => {...}

Minor: how to reduce the size of the hamster :-)

enter image description here

my JS code (also on jsfiddle)

class Simulation
{
    constructor()
    {
          // center of the map
    var center = [1.8650,  51.2094];

    // Create the map
    this.map = L.map('map').setView(center, 2);

    // Set up the OSM layer
    L.tileLayer(
      'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 18
      }).addTo(this.map);
    
    this.imagesLayer = new L.layerGroup(); 
    this.imagesLayer.addTo(this.map);
    
    
}

_getRandomCoord()
{
        var randLat = Math.floor(Math.random() * 90);
        randLat *=  Math.round(Math.random()) ? 1 : -1;

    var randLon = Math.floor(Math.random() * 180);
    randLon *=  Math.round(Math.random()) ? 1 : -1;
    
    return [randLat,randLon]
}

addImage2()
{
                
    this.loader = new PIXI.Loader()
    this.loader.add('hamster', 'https://cdn-icons-png.flaticon.com/512/196/196817.png')
    this.loader.load((loader, resources) => {
    
        let markerTexture = resources.hamster.texture
        let markerLatLng = this._getRandomCoord()
        let marker = new PIXI.Sprite(markerTexture)
        marker.anchor.set(0.5, 1)

        let pixiContainer = new PIXI.Container()
        pixiContainer.addChild(marker)

        let firstDraw = true
        let prevZoom

        let pixiOverlay = L.pixiOverlay(utils => {
          let zoom = utils.getMap().getZoom()
          let container = utils.getContainer()
          let renderer = utils.getRenderer()
          let project = utils.latLngToLayerPoint
          let scale = utils.getScale()

          if (firstDraw) {
            let markerCoords = project(markerLatLng)
            marker.x = markerCoords.x
            marker.y = markerCoords.y
          }

          if (firstDraw || prevZoom !== zoom) {
            marker.scale.set(1 / scale)
          }

          firstDraw = true
          prevZoom = zoom
          renderer.render(container)
        }, pixiContainer)

        this.imagesLayer.addLayer(pixiOverlay);
  })
}   

addTriangle()
{
            console.log("Trinalge")   
    var polygonLatLngs = [
        [51.509, -0.08],
        [51.503, -0.06],
        [51.51, -15.047],
        [21.509, -0.08]
    ];
    var projectedPolygon;
    var triangle = new PIXI.Graphics();

    var pixiContainer = new PIXI.Container();
    pixiContainer.addChild(triangle);

    var firstDraw = true;
    var prevZoom;

    var pixiOverlay = L.pixiOverlay(function(utils) {
        var zoom = utils.getMap().getZoom();
        var container = utils.getContainer();
        var renderer = utils.getRenderer();
        var project = utils.latLngToLayerPoint;
        var scale = utils.getScale();

        if (firstDraw) {
            projectedPolygon = polygonLatLngs.map(function(coords) {return project(coords);});
        }
        if (firstDraw || prevZoom !== zoom) {
            triangle.clear();
            triangle.lineStyle(3 / scale, 0x3388ff, 1);
            triangle.beginFill(0x3388ff, 0.2);
            projectedPolygon.forEach(function(coords, index) {
                if (index == 0) triangle.moveTo(coords.x, coords.y);
                else triangle.lineTo(coords.x, coords.y);
            });
            triangle.endFill();
        }
        firstDraw = false;
        prevZoom = zoom;
        renderer.render(container);
    }.bind(this), pixiContainer);
    
            this.imagesLayer.addLayer(pixiOverlay)
}
    
removeLayer()
{
            this.imagesLayer.clearLayers();
}   
}

var simulation = new Simulation();
1 Answers

TLDR: Updated jsfiddle:

https://jsfiddle.net/gbsdfm97/

more info below:

First problem: loading resources (textures)

There was error in console because you loaded hamster image on each click:

    addImage2()
    {
                    
        this.loader = new PIXI.Loader()
        this.loader.add('hamster', 'https://cdn-icons-png.flaticon.com/512/196/196817.png')
        this.loader.load((loader, resources) => {
        ...

Better approach is to load image (resource) once at beginning and then just reuse what is loaded in memory:

    constructor()
    {
...
        
        this.markerTexture = null;
        this._loadPixiResources();
    }
    
...
    
    _loadPixiResources()
    {
        this.loader = new PIXI.Loader()
        this.loader.add('hamster', 'https://cdn-icons-png.flaticon.com/512/196/196817.png')
        this.loader.load((loader, resources) => {
            this.markerTexture = resources.hamster.texture;
      })
    }

...
    
    addImage2()
    {
        ...
        let marker = new PIXI.Sprite(this.markerTexture);

Second problem: size of hamsters :)

Scale was set like this:

marker.scale.set(1 / scale)

Which was too big - so changed it to:

// affects size of hamsters:
this.scaleFactor = 0.05;
...
marker.scale.set(this.scaleFactor / scale);

Scale of hamsters (not triangles!) is now updated when zoom changes - so when user uses mouse scroll wheel etc.

Third problem: too many layers in pixiOverlay

Previously on each click on Add Image 2 or Add Triangle button there was added new pixiContainer and new pixiOverlay which was added as new layer: this.imagesLayer.addLayer(pixiOverlay);

New version is a bit simplified: there is only one pixiContainer and one pixiOverlay created at beginning:

    constructor()
    {
        ...
        
        // Create one Pixi container for pixiOverlay in which we will keep hamsters and triangles:
        this.pixiContainer = new PIXI.Container();
        
        let prevZoom;
        
        // Create one pixiOverlay:
        this.pixiOverlay = L.pixiOverlay((utils, data) => {
          ...
        }, this.pixiContainer)

        this.imagesLayer.addLayer(this.pixiOverlay);
    }
  • this.pixiOverlay is added as one layer
  • then in rest of program we reuse this.pixiOverlay
  • also we reuse this.pixiContainer because it is returned from utils - see:
let container = utils.getContainer()  // <-- this is our "this.pixiContainer"
...
container.addChild(marker)
renderer.render(container)

Bonus: Triangles

Now you can add many triangles - one per each click.

Note: triangles do not change scale - this is a difference compared to hamsters.

Related