Transform GeoJson Linestring to Polyline?

I have GeoJson file with roads data as Linestrings. I want to load it in Cesium Sandcastle and make a custom line styles. In order to do so, line has to be Polyline, not Linestring (https://cesiumjs.org/Cesium/Build/Documentation/Entity.html?classFilter=enti ). Is there a way to transform Linestring to Polyline for GeoJson format? Otherwise, how can I style GeoJson data as Linestring?

Code:

//Add terrain
var viewer = new Cesium.Viewer(‘cesiumContainer’);
var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({
    url : ‘https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles’,
    requestWaterMask : true,
    requestVertexNormals : true
});
viewer.terrainProvider = cesiumTerrainProviderMeshes;
viewer.scene.globe.depthTestAgainstTerrain = true;

//Load with default styling
Sandcastle.addToolbarButton('Default styling', function() {
    Cesium.GeoJsonDataSource.clampToGround = true;
    var dataRoads = Cesium.GeoJsonDataSource.load('../../SampleData/ceste_rab_okvir.geojson');
    viewer.dataSources.add(dataRoads);
    viewer.zoomTo(dataRoads);
});

//Apply custom graphics after load
Sandcastle.addToolbarButton('Custom styling', function() {
    //Seed the random number generator for repeatable results
    Cesium.Math.setRandomNumberSeed(0);

    Cesium.GeoJsonDataSource.clampToGround = true;
    var promise = Cesium.GeoJsonDataSource.load('../../SampleData/ceste_rab_okvir.geojson'); //data loaded from local folder
    promise.then(function(dataSource) {
        viewer.dataSources.add(dataSource);
        viewer.zoomTo(promise);

        //Get the array of entities
        var entities = dataSource.entities.values;

        var colorHash = {};
        for (var i = 0; i < entities.length; i++) {
            //For each entity, create a random color based on the road class
            var entity = entities[i];
            var roadClass = entity.fclass;
            var color = colorHash[roadClass];
            if (!color) {
                color = Cesium.Color.fromRandom({
                    alpha : 1.0
                });
                colorHash[roadClass] = color;
            }

            //Set the polygon material to our random color.
            //THIS IS THE PROBLEM
            entity.polyline.material = color;
        }
    }).otherwise(function(error) {
        //Display any errrors encountered while loading
        window.alert(error);
    });
});

//Reset the scene when switching demos.
Sandcastle.reset = function() {
    viewer.dataSources.removeAll();
};

I have seen examples of line styling (color, alpha etc.) and in examples, lines are hard-coded, but I have loaded data from local folder (which are Openstreetmap roads data in GeoJson format).

I loaded roads data:
    Cesium.GeoJsonDataSource.load('../../SampleData/xxx');
clamped them to ground (stk terrain):
    Cesium.GeoJsonDataSource.clampToGround = true;
and roads show nicely on terrain when in default style.

Then I tried changing linestyles according to attribute, but nothing shows on terrain and "entity.polyline.material = color;" throws error probably because GeoJson is linestring not polyline.

I want to color the roads differently according to road class (highway, residential, unclassified,...).
Is there a solution for this?

Hi there,

The issue is that we don’t support polylines clamped to terrain. See this thread for more information: https://groups.google.com/d/msg/cesium-dev/JlR4KgkndlI/lPdzriSjxAEJ

As a workaround when you enable ground clamping with a linestring, we convert it to a corridor. Try applying your styling to

entity.corridor

``

. Here’s an example: http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Corridor.html&label=Geometries

Hope that helps,

  • Rachel

Hi Rachel,

thank you so much! It works with corridor.
But now, there's few other issue.
1) I don't see if there's something like corridorVolume? Like polylineVolume.
   Corridor is fine for roads, but I will have to make pipeline cylinders with volume beneath the ground level (under terrain) from my local data. Is it possible?
2) Is it possible to read coordinates from loaded GeoJson file? I've seen an example with Cartesian3.fromDegreesArrayHeights but coordinates are hardcoded.

Thanks,
Paula

Hi Paula,

  1. Yes, you can extrude a corrdior as shown here: http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Corridor.html&label=

  2. You can read from the corridor positions, and convert from cartesian3 to degrees using Cartographic.fromCartesian()

Hope that helps,

  • Rachel

Hi Rachel,

I don't see the way to make corridor to be round like cylinder. If I extrude it, it just extrudes top side and the edges are sharp not round. I need to make cylinder pipeline from lines I've loaded so that the lines are referent centerline from which I extrude cylinder with a certain radius. Do you have any idea how to do that?

Also, I tried doing it with this code, but every time it runs it shows error at this line -> shape: computeCircle(6),
the error says: "An error occurred while rendering. Rendering has stopped.
out of memory"

var viewer = new Cesium.Viewer('cesiumContainer');

function computeCircle(radius) {
    var positions = ;
    for (var i = 0; i < 360; i++) {
        var radians = Cesium.Math.toRadians(i);
        positions.push(new Cesium.Cartesian2(radius * Math.cos(radians), radius * Math.sin(radians)));
    }
    return positions;
}

viewer.zoomTo(viewer.entities);

var promise = Cesium.GeoJsonDataSource.load('../../SampleData/Ceste_izbrisano.geojson', {
    clampToGround : false,
    //extrudedHeight : 300.0,
    width : 10.0,
    cornerType: Cesium.CornerType.MITERED,
    material : Cesium.Color.GREEN
});

console.log('adding data');
promise.then(function(dataSource) {
  viewer.dataSources.add(dataSource);
  //Get the array of entities
  var entities = dataSource.entities.values;

  for (var i = 0; i < entities.length; i++)
  {
    //Set the height and the material of each polyline
    var entity = entities[i];
    
    entity.polylineVolume = new Cesium.PolylineVolumeGraphics({
      positions: entity.polyline.positions,
      shape: computeCircle(6),
      material: Cesium.Color.RED
    });
    
    if (entity.properties.Layer === 'CESTA') {
      entity.polyline.material = Cesium.Color.YELLOW;
      entity.polylineVolume.material = Cesium.Color.YELLOW;
    }
    if (entity.properties.Type === 'CESTA_KAO_GRANICA_POKROVA_ZEMLJISTA') {
      entity.polyline.material = Cesium.Color.DEEPSKYBLUE;
      entity.polylineVolume.material = Cesium.Color.DEEPSKYBLUE;
    }
        
    viewer.zoomTo(entities);
  }

}).otherwise(function(error){
    //Display any errors encountered while loading.
    window.alert(error);
});

Hi Paula,

In order to create a cylinder volume, use a PolylineVolumeGeometry (https://cesiumjs.org/Cesium/Build/Documentation/PolylineVolumeGeometry.html). However, we don’t have support for under terrain/subsurface, but it has been often requested and is definitely on our radar. See this issue in the Cesium GitHub Repo: https://github.com/AnalyticalGraphicsInc/cesium/issues/5665

Here’s a Sandcastle example showing off the options: https://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Polyline%20Volume.html&label=Geometries

Thanks!

Gabby

Hi Gabby,

thank you for help.

I have an alternative for subsurface visualization. I have made a geojson surface that represents terrain and it looks great and I have managed to create pipes with PolylineVolume that are under the surface.

Now, I have another issue which I don't know how to solve:
I want to show buildings on this surface. They are also geojson with defined 3D coordinates.
The problem is when I set perPositionHeight = false, bottom sides of buildings lower to the ellipsoid. But when I set perPositionHeight = true, bottom sides of buildings are okay on the surface but side "walls" of buildings dissapear.
Do you know what am I doing wrong and how can I fix this issue?

This is part of the code:

//TERRAIN SURFACE
var dataSourcePloha = Cesium.GeoJsonDataSource.load('../../SampleData/Ploha.geojson');
viewer.dataSources.add(dataSourcePloha);
viewer.zoomTo(dataSourcePloha);

//BUILDINGS
Cesium.Math.setRandomNumberSeed(1234);
var promiseBuildings = Cesium.GeoJsonDataSource.load('../../SampleData/Gradjevine_3d_wgs84_v2.json');
promiseBuildings.then(function(dataSourceBuildings) {
  viewer.dataSources.add(dataSourceBuildings);
  viewer.zoomTo(promiseBuildings);
    
    //Get the array of entities
  var entitiesBuildings = dataSourceBuildings.entities.values;
    
    var colorHash = {};
  for (var i = 0; i < entitiesBuildings.length; i++) {
    var entity = entitiesBuildings[i];
    var AttributeBuildings = entity.properties.Visina;
    var color = colorHash[AttributeBuildings];
    if (!color) {
      color = Cesium.Color.fromRandom({
        alpha: 1.0
      });
      colorHash[AttributeBuildings] = color;
    }
        
    entity.polygon.material = color;
        entity.polygon.fill = true;
    entity.polygon.outline = false;
        entity.polygon.perPositionHeight = true;
        //Extrude the polygon based on the height. Each entity stores the properties for the GeoJSON feature it was created from
    entity.polygon.extrudedHeight = entity.properties.ukupna_visina;
    }
    
    viewer.zoomTo(entitiesBuildings);
    
}).otherwise(function(error) {
  //Display any errors encountered while loading.
  window.alert(error);
});

This is how it looks when entity.polygon.perPositionHeight = false;

and entity.polygon.perPositionHeight = true;

This is explained in the PolygonGraphics.extrudedHeight docs:

If PolygonGraphics#perPositionHeight is false, the volume starts at PolygonGraphics#height and ends at this altitude. If PolygonGraphics#perPositionHeight is true, the volume starts at the height of each PolygonGraphics#hierarchy position and ends at this altitude.

So make sure you are specifying the height property for the base location of the building and use perPositionHeight accordingly.

Hi Gabby,

I'm sorry I haven't explained the problem well, I think you didn't understand me.
I understand what exstrudedHeight means, I know it is an elevation above the elipsoid, that's why I had defined it as (height of terrain + height of building) as an attribute in my geojson file for each building.
In this two pictures https://ibb.co/nJB81k and https://ibb.co/jXkU7Q I just wanted to ask why are there missing/dissapearing walls on 2nd picture, but on 1st the walls are all there. I don't think it should be connected with height because the heights are just fine: they start at height which is defined in geojson and end at extruded height, and that's what I need. But walls are dissapearing as I rotate camera.
Do you know why is that so and can I fix it or is it some kind of a bug?

Thanks again for your help.

Hi Paula,

Sorry for the misunderstanding!

I think the culprit may be what Hannah explains here: https://github.com/AnalyticalGraphicsInc/cesium/issues/5203#issuecomment-293661265

I added the problem you are having to that issue so we can take a look at it. If you have anymore info that may help, please add it there.

Thanks,

Gabby

Hi Gabby,

I appreciate your help and effort!
I have managed to partially overcome this issue. I've noticed that when I set color transparency with alpha, the walls aren't dissapearing. Also, if outline = true it looks much better.

It looks like this: http://imgur.com/a/sxLNp

And this is the code if it may help:

//BUILDINGS
Cesium.Math.setRandomNumberSeed(0);
var promiseBuildings = Cesium.GeoJsonDataSource.load('../../SampleData/Gradjevine_3d_wgs84_v2.json');
promiseBuildings.then(function(dataSourceBuildings) {
  viewer.dataSources.add(dataSourceBuildings);
    
    //Get the array of entities
  var entitiesBuildings = dataSourceBuildings.entities.values;
    
    var colorHash = {};
  for (var i = 0; i < entitiesBuildings.length; i++) {
    var entity = entitiesBuildings[i];
    var AttributeBuildings = entity.properties.Visina;
    var color = colorHash[AttributeBuildings];
    if (!color) {
      color = Cesium.Color.fromRandom({
        alpha: 0.9
      });
      colorHash[AttributeBuildings] = color;
    }
        
        entity.polygon.distanceDisplayCondition = new Cesium.DistanceDisplayCondition(0, 2000);
    entity.polygon.show = true;
        entity.polygon.fill = true;
        entity.polygon.material = color;
        entity.polygon.outline = true;
        entity.polygon.outlineColor = Cesium.Color.BLACK;
        entity.polygon.closeTop = true;
        entity.polygon.closeBottom = true;
        entity.polygon.perPositionHeight = true;
        //Extrude the polygon based on the height. Each entity stores the properties for the GeoJSON feature it was created from
    entity.polygon.extrudedHeight = entity.properties.ukupna_visina;
    }
}).otherwise(function(error) {
  window.alert(error);
});

I’m glad to hear you have a workaround for now! We’ll keep this thread updated if there’s a change to that issue.

Thanks!