Tiled source of GeoJSON - Streaming 3D Buildings

All:

I have been investigating integrating a geojson tile service to add streaming 3d buildings from OSM or other sources. Some precedents are below:

https://mapzen.com/tangram
http://vizicities.apps.rawk.es/demo.html#51.503580000000014,-0.01924
http://osmbuildings.org/?lat=52.52111&lon=13.40988&zoom=16

So far, I have developed a script to accept load tiles within a bounding box, or load json tiles dynamically (but not yet unloading) when map zoom level is 16. Both approaches use either Mapzen and/or OSM Buildings API.

Is there any recommendations on the best way to accomplish a streaming 3d geojson tiling service? Has something like this been accomplished with Cesium?

Any feedback is greatly appreciated.

Thanks

Current approach example are outlined below:

//--- helper functions to get tiles numbers with lat/lon

function lon2tilex(lon, zoom) {return Math.round((lon + 180) / 360 * Math.pow(2, zoom));}
function lat2tiley(lat, zoom) {var lata = lat * Math.PI / 180;return Math.round((1 - Math.log(Math.tan(lata) + (1 / Math.cos(lata))) / Math.PI) / 2 * Math.pow(2, zoom));}

//--- get tiles within a bounding box

function returnTiles(min_lon, min_lat, max_lon, max_lat) {
    var zoom = 16; // required for building API sources
    var tiles = ;
    var txmin = lon2tilex(min_lon, zoom);
    var txmax = lon2tilex(max_lon, zoom);
    var tymin = lat2tiley(max_lat, zoom);
    var tymax = lat2tiley(min_lat, zoom);
    for (var tx = txmin; tx <= txmax; tx++) {
        for (var ty = tymin; ty <= tymax; ty++) {
            tiles.push([tx, ty, zoom]);
        }
    }
    return tiles;
}

//--- recursive function to load from a list of tiles (eg. generated by a bounding box formulation)

var tilesLoaded = {}
var tileCount = 0;
function loadTileData(k) {
    var data = “dataSource” + String(k);
    tilesLoaded[data] = new Cesium.GeoJsonDataSource();
    cesiumWidget.dataSources.add(tilesLoaded[data]);
    // 3D building APIs
        // http://data.osmbuildings.org/0.2/rkc8ywdl/tile/
        // http://vector.mapzen.com/osm/buildings/
    tilesLoaded[data].loadUrl(‘http://vector.mapzen.com/osm/buildings/’ + tilesLoad[k][2] + ‘/’ + tilesLoad[k][0] + ‘/’ + tilesLoad[k][1] + ‘.json’).then(function() {
        var entities = tilesLoaded[data].entities.entities;
        for (var i = 0; i < entities.length; i++) {
            var entity = entities[i];
            var name = entity.name;
            entity.polygon.material = Cesium.ColorMaterialProperty.fromColor(new Cesium.Color(1.0, 1.0, 1.0, 0));
            entity.polygon.outlineColor = new Cesium.ConstantProperty(new Cesium.Color(1, 1, 1, 0.48));
            entity.polygon.outlineWidth = new Cesium.ConstantProperty(0.5);
            if (entity.properties.height != undefined) {
                entity.polygon.extrudedHeight = new Cesium.ConstantProperty(entity.properties.height);
            }
            else if (entity.properties.levels != undefined) {
                entity.polygon.extrudedHeight = new Cesium.ConstantProperty(entity.properties.levels * 5);
            }
            else {
                // randomly extrude if height=0
                var max = 3 * 5
                var min = 1 * 5;
                entity.polygon.extrudedHeight = new Cesium.ConstantProperty(Math.random() * (max - min) + min);
            }
            entity.building = true;
        }
        if (k < tilesLoad.length) {
            loadTileData(k + 1);
        }
        else {
            console.log(‘all done loading’);
        }
    });
}
loadTileData(0);

//--- or function to load tiles directly from x, y and zoom

function loadTile(x, y, z) {
    var data = “dataSource” + String(tileCount);
    tileCount = tileCount + 1;
    tilesLoaded[data] = new Cesium.GeoJsonDataSource();
    cesiumWidget.dataSources.add(tilesLoaded[data]);
    tilesLoaded[data].loadUrl(‘http://data.osmbuildings.org/0.2/rkc8ywdl/tile/’ + z + ‘/’ + x + ‘/’ + y + ‘.json’).then(function() {
        // 3D building APIs
        // http://data.osmbuildings.org/0.2/rkc8ywdl/tile/
        // http://vector.mapzen.com/osm/buildings/
        var entities = tilesLoaded[data].entities.entities;
        for (var i = 0; i < entities.length; i++) {
            var entity = entities[i];
            var name = entity.name;
            entity.polygon.material = Cesium.ColorMaterialProperty.fromColor(new Cesium.Color(1.0, 1.0, 1.0, 0));
            entity.polygon.outlineColor = new Cesium.ConstantProperty(new Cesium.Color(1, 1, 1, 0.48));
            entity.polygon.outlineWidth = new Cesium.ConstantProperty(0.5);
            if (entity.properties.height != undefined) {
                entity.polygon.extrudedHeight = new Cesium.ConstantProperty(entity.properties.height);
            }
            else if (entity.properties.levels != undefined) {
                entity.polygon.extrudedHeight = new Cesium.ConstantProperty(entity.properties.levels * 5);
            }
            else {
                // randomly extrude if height=0
                var max = 5 * 5
                var min = 1 * 5;
                entity.polygon.extrudedHeight = new Cesium.ConstantProperty(Math.random() * (max - min) + min);
            }
            entity.building = true;
        }
    });
}

//--- example imagery provider calling loadTiles when zoom=16

Cesium.<XYZ>ImageryProvider.prototype.requestImage = function (x, y, level) {
        var url = this._url.replace('{s}', this.getCDNSubdomain(x, y)).replace('{z}', level).replace('{x}', x).replace('{y}', y);
        if (level==16){
            loadTile(x,y,level);
            // need to dynamically unload the feature tiles?
        }
        return Cesium.ImageryProvider.loadImage(this, url);
    };

Hi Matthew,

We actually have something for this on the horizon. Look for an announcement here shortly. There’s a bit more info here: https://groups.google.com/forum/#!topic/cesium-dev/Z_RE-krc0HY

In the meantime, code up whatever you’d like.

Patrick

Great looking forward to an open solution and in the meantime will keep trying out different things.