CesiumGS / cesium

An open-source JavaScript library for world-class 3D globes and maps :earth_americas:
https://cesium.com/cesiumjs/
Apache License 2.0
12.8k stars 3.46k forks source link

Pass the imageryCache from ImageryLayer to its Provider #9056

Closed icemagno closed 4 years ago

icemagno commented 4 years ago

Hi,

I'm creating a new Provider to read GeoJSON from a WFS service. In the requestImage I just give a blank canvas to the return and take the bounding box of the tile.

So I can ask the Geoserver for the Vector Features inside this "tile" and convert it to an extruded polygon. Then I create an Entity and add to the viewer.

All is working fine but I need to implement a kind of discard procedure to avoid keep the entities forever. I know the canvas I gave was turned into an Imagery and added to the imageryCache from ImageryLayer.

My idea is just create a parallel cache inside the Provider to store my Polygon Entities using the same key (x,y,level) since the Imagery canvas have no utility to me.

But I need to have access to ImageryLayer from inside the Provider to gain access to the cache and check if the Imagery was sent to trash so I can discard the correspondent Polygon Entity.

My suggestion is change ImageryLayerCollection to call a new method in the new Provider:


ImageryLayerCollection.prototype.addImageryProvider = function (
  imageryProvider,
  index
) {
  if (!defined(imageryProvider)) {
    throw new DeveloperError("imageryProvider is required.");
  }
  var layer = new ImageryLayer(imageryProvider);

imageryProvider._layer = layer;   <<<--- This way

  this.add(layer, index);
  return layer;
};

Now I can access the cache from inside my provider: this._layer._imageryCache[ key ]

I don't know if there any better way to do that I'm trying to do but NO, I don't want 3DTiles.

Thanks.

OmarShehata commented 4 years ago

I'm not sure that using an imagery provider to create entities in this way is the best approach. It sounds like your goal here is to essentially extend 3D Tiles to use vector data, which is on the roadmap as I believe you're aware (see https://github.com/CesiumGS/cesium/issues/2132).

In the meantime, if you're implementing this yourself it may be easier to write your own custom primitive that implements this system instead of trying to fit it into the imagery architecture. Feel free to post about your progress/questions on the community forum: https://community.cesium.com/.

icemagno commented 4 years ago

Hi @OmarShehata , yes, I have no idea how heavy will be the solution a long term, but I have good results until now. I can load standard GeoJSON from a Java service endpoint or Geoserver providing OSM data with easy.

My concern now is just know how to discard the Entities I've created when the user move alway from the area to not eat memory.

Now I'm using the same concept to load small point cloud data from a PGPointCloud database enabled (It is the same concept but using points ). Yes ... I know pointcloud is not so easy but it will cover my current problem. In this case my concern is the Point itself in cesium. Even small Ellipsoids are heavy to load until now and freezes the map somehow.

Check it here: https://github.com/icemagno/cesium-providers/tree/master/buildingsprovider I don't know hot to "implement my custom primitive". Can you show me a direction?

Criticisms and suggestions will make me happy.

screen

icemagno commented 4 years ago

I've found a temporary solution anyway:

First I need to pass the viewer to the Provider:

var buildingsProvider = new MagnoBuildingsProvider({
  debugTiles : false,
  viewer : viewer,
  activationLevel : 17,
  sourceUrl : url,
  featuresPerTile : 200,
  whenFeaturesAcquired : function( entities ){
    console.log( entities.length + " buildings received." );
  }
});

Now I made it obligatory in Provider and gave it a name:

var MagnoBuildingsProvider = function MagnoBuildingsProvider(options) {

    if ( !Cesium.defined(options) ) {
        throw new DeveloperError("options is required.");
    }    

    if ( !Cesium.defined( options.viewer ) ) {
        throw new DeveloperError("options.viewer is required.");
    }    

    if ( !Cesium.defined( options.sourceUrl ) ) {
        throw new DeveloperError("options.sourceUrl is required.");
    }    

   this._name = "MagnoBuildingsProvider";
    ...
}

Now just check if I already have a cache. If don't, search for the ImageryLayer one and take its reference:

if( !this._imageryCache ){
    for( x=0; x < this._viewer.imageryLayers.length; x++){
        var layer = this._viewer.imageryLayers.get(x);
        var provider = layer.imageryProvider;
        if( provider.name === this._name ){
            this._imageryCache = layer._imageryCache;
            break;
        }
    }
} 

Now I can know the tiles in cache. Just need to create a discard method when my local cached x+y+level key is not in ImageryLayer cache (managed by Cesium). Cesium send it to the trash so I have to do the same with my Primitives inside that square.

I think a callback function would by handy. You can propagate it from up ( when you discard the Imagery from cache ) down to the Provider just to notify it (you can send just the key). Normal situations it may not be useful because the Provider don't need to concern about it. This kind of Provider only create 256x256 canvas and send them to the Layer who takes care about them from now on. But .... who knows ?

icemagno commented 4 years ago

https://community.cesium.com/t/yet-another-topic-about-vector-tiles/10352

icemagno commented 4 years ago

Here is my pointcloud first try ( only GeoJSON vectors ): https://www.youtube.com/watch?v=OEbfyxOjb7g

screen