I had a look at Material Fade, but it was not working well and I had to try to find the direction of the polyline. Not sure if Fade even works with polyline with 20 points...
Is there a better way to change the color between each polyline point?
Or a working example of using Material Fade is much appreciated!
The PerInstanceColorAppearance sounds like what I'm looking for... but I have no idea how to implement this for the Polyline in the PolylineCollection...
How does a polyline collection with several polylines that is added to the primitives translate into something like this from the example http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Geometry%20and%20Appearances.html
positions = [];
colors = [];
for (i = 0; i < 40; ++i) {
positions.push(ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-100.0 + i, 9.0)));
colors.push(Cesium.Color.fromRandom({alpha : 1.0}));
}
primitives.add(new Cesium.Primitive({
geometryInstances : new Cesium.GeometryInstance({
geometry : new Cesium.PolylineGeometry({
positions : positions,
width : 10.0,
vertexFormat : Cesium.PolylineColorAppearance.VERTEX_FORMAT,
colors : colors,
colorsPerVertex : true
})
}),
appearance : new Cesium.PolylineColorAppearance()
}));
I've also added a life-duration attribute to each polyline to determine when they need to be removed from the polyline collection. Potentially I have a lot of polylines that need to be added and removed per second and each polyline has different points.
Related
This question is related to these two:
Cesium how to scale a polygon to match Lat-Lon positions while zoom-in/zoom-out
Cesium - using camera to scale a polygon to match Lat-Lon positions while zoom-in/zoom-out
The sample code I am following to get lat-lon-alt positions from the camera is located in the gold standard that appears to be baked into the existing camera controller. With this code I can retrieve lat-lon-alt positions from the distance of the camera to get values that are almost exact to the original lat-lon position selected and a height above the surface of the earth. Perfect!
All examples and documentation show polygon creation using degrees or points from degrees.
Now what? Maybe I'm missing something but the intent I thought was to be able to create the polygon using specific x, y, z coordinates so the polygon would "stick" to the top of my house on zoom-in, zoom-out, and camera movement. Now that I have those values, what is the secret to drawing the polygon with those values?
FYI, these are the value I currently have:
=========================NEW INFORMATION===========================
The code for the redPolygon works:
var redPolygon = viewer.entities.add({
name : 'Red polygon on surface',
polygon : {
hierarchy : Cesium.Cartesian3.fromDegreesArray([-115.0, 37.0,
-115.0, 32.0,
-102.0, 31.0,
-102.0, 35.0,
-102.0, 35.0]),
material : Cesium.Color.RED
}
});
viewer.flyTo(redPolygon);
The code for the bluePolygon does not work:
var bluePolygon = viewer.entities.add({
name : 'Blue polygon on surface',
polygon : {
//hierarchy: collection.latlonalt,
hierarchy: Cesium.Cartesian3.fromArray(collection.latlonalt),
material : Cesium.Color.BLUE
}
});
viewer.flyTo(bluePolygon);
If I use hierarchy: collection.latlonalt, I receive the following error:
So I changed the code to hierarchy: Cesium.Cartesian3.fromArray(collection.latlonalt), where collection.latlonalt is my Cartesian3 array:
But nothing gets drawn. No errors. This is what I see in the console:
Just for test, I tried adding a z position to the redPolygon and changing .fromDegreesArray to .fromArray like this:
var redPolygon = viewer.entities.add({
name : 'Red polygon on surface',
polygon : {
hierarchy : Cesium.Cartesian3.fromArray([-115.0, 37.0, 10.0,
-115.0, 32.0, 10.0,
-102.0, 31.0, 10.0,
-102.0, 35.0, 10.0,
-102.0, 35.0, 10.0]),
material : Cesium.Color.RED
}
});
viewer.flyTo(redPolygon);
That didn't work either.
Cesium has helper functions like Cartesian3.fromDegreesArray that are used by the Polygon Demo, but, these helper functions are not needed now that you've got your hands on actual Cartesian3 values.
For example, the polygon demo code looks like this:
var redPolygon = viewer.entities.add({
name : 'Red polygon on surface',
polygon : {
hierarchy : Cesium.Cartesian3.fromDegreesArray([-115.0, 37.0,
-115.0, 32.0,
-107.0, 33.0,
-102.0, 31.0,
-102.0, 35.0]),
material : Cesium.Color.RED
}
});
In the above code, fromDegreesArray in this case just takes a list of 5 lot/lan value pairs, and converts them into a JavaScript array of 5 instances of the Cartesian3 class. This array of 5 Cartesian3s is then stored as the value of hierarchy in the polygon definition. If you inspect that definition at runtime, you'll find the original lon/lat values have been discarded, replaced by the actual Cartesian3s, thanks to the helper function.
So in your code, you'll need an array of Cartesian3s that the user has clicked on thus far. This starts as the empty array, and you'll need to gather at least three clicks, converting each click into a Cartesian3 as you've shown works in your question above, and push that value into the array. Once the array has accumulated 3 or more clicks, you can then pass that array as the hierarchy field of the polygon definition.
In this manner, you've avoided calling fromDegreesArray because your click handler is doing the more detailed work of gathering an exact Cartesian position per click. This gathering has to happen at the time of each click, in case the camera is moved between clicks. So, the array "in progress" has to survive between clicks, until all the clicks have been gathered and a polygon can be created.
EDIT: Here's an example of the code structure I'm trying to describe. I don't show the actual click handlers here, since you seem to have Cartesian3 values coming out of your mouse clicks already. Instead, I show three such values being used to create a polygon.
var viewer = new Cesium.Viewer('cesiumContainer');
// Create an empty array of click positions at the start.
var clickPositions = [];
// When the first mouse click is received, convert to Cartesian3, and push it into the array.
var click1 = new Cesium.Cartesian3(-2155350.2, -4622163.4, 3817393.1);
clickPositions.push(click1);
// Later, more mouse clicks are received and pushed into the array.
var click2 = new Cesium.Cartesian3(-2288079.8, -4906803.1, 3360431.4);
clickPositions.push(click2);
var click3 = new Cesium.Cartesian3(-1087466.8, -5116129.4, 3637866.9);
clickPositions.push(click3);
// Finally, draw the polygon.
var redPolygon = viewer.entities.add({
name : 'Red polygon on surface',
polygon : {
hierarchy : clickPositions,
material : Cesium.Color.RED
}
});
Notice nothing happens to clickPositions when it's assigned to hierarchy. The array of Cartesian3 values is already in the form needed by Cesium here.
I create a tetrahedron of radius 3
// create a tetrahedron
var tetGeometry = new THREE.TetrahedronGeometry(3);
var tetMaterial = new THREE.MeshLambertMaterial(
{color: 0x20f020, transparent:true, opacity:0.6});
tet = new THREE.Mesh(tetGeometry, tetMaterial);
tet.name='tet';
tet.castShadow = true;
Later, I want the tetrahedron to grow:
// change hedron
scene.getObjectByName('tet').radius = control.hedronRadius;
That doesn't work.
// change vertices
scene.getObjectByName('tet').detail = control.hedronVertices;
That doesn't work either.
scene.getObjectByName('tet').verticesNeedUpdate;
And this doesn't help.
So how do I change the radius of a tetrahedron (or any Geometry) and how do I change the vertices.
In the documentation I see references to:
Geometry
.dynamic
.morph
.verticesNeedUpdate
.scale
And also references to bones and skeletons and skinned meshes used to animate geometries.
How do I change these aspects of Geometries in general?
What's the most reasonable, suggested way then to grow the radius of a Tetrahedron, or change the number of vertices show it becomes a different number polyhedron?
To change geometry you need to use:
morphTargets: true
I've prepared an example using a tetrahedron as you mention in jsfiddle.
Use sliders to change geometry.
To make some custom vertices and "fill" them by faces, you need to understand a lot of things from math, like; point, vector, etc.
I've done 2 simple flat objects, triangle and square in jsfiddle.
I hope that you'll easy understand how it works in general.
I'm trying to figure out how to draw an interactive hex grid on top of Google Maps by using d3.js, but I can't seem to find the documentation necessary for this particular implementation.
Current demo that I'm working with
This is a combination of the ionic-starter-maps project with a d3.js geometry overlay demo mixed in (code for this video is here). I plan to utilize some hex grid information from this article.
The two heavy lifting (I think) functions are here:
function d3init() {
width = map.getDiv().offsetWidth;
height = map.getDiv().offsetHeight;
projection = d3.geo.equirectangular()
.translate([0,0])
.scale(57.29578)
.precision(.1)
context = new PolyLineContext();
path = d3.geo.path().projection(projection).context(context);
equator = {type: 'LineString', coordinates: [[-180,20],[-90,0], [0,-20], [90,0], [180,20] ] }
render();
}
function render() {
polyline.setOptions({
strokeColor: 'red',
strokeWeight: 2
});
context.setCurrent(polyline.getPath());
path(equator);
}
Most of the examples I find use the SVG method to draw shapes, but I would like to have lots of these hexes so it sounds like canvas might be better suited to the task.
How can I cover the map with a hex grid? What is the best method for adding geometry to the overlay, and should I be looking at equator = {type: 'LineString',... as the place to add more coordinates for the hex grid?
So, I'm using cesium and I want to add a polygon or line to represent a property boundary on a terrain surface.
My polygon works fine on the flat/Ellipsoid surface, unfortunately however the polygon doesn't automagically drape over the surface when the terrain layer is shown.
Fair enough, I don't actually have the z/height values - so I'm using the sampleTerrain.js promise method to interpolate the height values based on the terrain. This part works fine, I get my height values. But then what?
I've tried creating a polygon entity with my height-laden positions, but to no avail - it just ignores the height values. When I read the docs, I can really see any reference to height values being ingested - all the "positions" array are two dimensional?
The only reference to height values being considered is in the PolygonOutlineGeometry, which has a promising looking property called perPositionHeight.
This is essentially what I want - I don't want to set the height of the whole poly, I want every points height value to be used..
Here's one of my unsuccessful attempts:
Entity/Polygon:
var entity = viewer.entities.add({
polygon : {
hierarchy : cartesianPositions, //array of positions with z values
outline : true,
outlineColor : Cesium.Color.RED,
outlineWidth : 9,
material : Cesium.Color.BLUE.withAlpha(0.0),
}
});
Bottom line: I just want a polygon or polyline entity that sits nicely on the surface of the terrain.
EDIT:
Using the Orange Polygon example in the comments of the accepted answer combined with sampleTerrain.js, I've been able to simulate 'draping' a polygon onto terrain, with a list of positions that did not have z values, here's a crude example:
var positions = []; // xy position array
var cesiumTerrainProvider = new Cesium.CesiumTerrainProvider({
url : '//assets.agi.com/stk-terrain/world'
});
viewer.terrainProvider = cesiumTerrainProvider;
// go off and sample the terrain layer to get interpolated z values for each position..
var promise = Cesium.sampleTerrain(cesiumTerrainProvider, 11, positions);
Cesium.when(promise, function(updatedPositions) {
var cartesianPositions = Cesium.Ellipsoid.WGS84.cartographicArrayToCartesianArray(updatedPositions);
var entity = viewer.entities.add({
polygon : {
hierarchy : cartesianPositions,
outline : true,
outlineColor : Cesium.Color.RED,
outlineWidth : 9,
perPositionHeight: true,
material : Cesium.Color.BLUE.withAlpha(0.0),
}
});
viewer.flyTo(entity);
});
As of version 1.13 cesium now supports GroundPrimitives. They will drape over terrain.
It looks like this: http://cesiumjs.org/images/2015/09-01/groundPrimitives.gif
This the example Cesium gives:
var rectangleInstance = new Cesium.GeometryInstance({
geometry : new Cesium.RectangleGeometry({
rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0)
}),
id : 'rectangle',
attributes : {
color : new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5)
}
});
scene.primitives.add(new Cesium.GroundPrimitive({
geometryInstance : rectangleInstance
}));
Cesium does not support vector data on terrain yet. It is being actively worked on and most features should start to appear in Cesium 1.10 which will be out on June 1st. Anything that doesn't make that release should be in 1.11 on July 1st.
For Polygons specifically, you can follow along with the GitHub pull request: https://github.com/AnalyticalGraphicsInc/cesium/pull/2618
For Billboards and Labels, check out: https://github.com/AnalyticalGraphicsInc/cesium/pull/2653
Polylines haven't been started yet but will be as soon as the above two are finished.
I am using Three.js to generate a polyhedron with differing colors and text on each face, generated from a canvas element. For now, I'm sticking with polyhedra for which Three.js includes native classes, but at some point, I'd like to branch out into more irregular shapes.
There are a number of examples available online (including StackOverflow posts, like Three.js cube with different texture on each face) that explain how to do this with cubes. I haven't succeeded in finding any samples that show the same technique applied to non-cubes, but for the most part, the same process that works for CubeGeometry also works for TetrahedronGeometry and so forth.
Here's a simplified version of the code I'm using to generate the polyhedron:
switch (shape) {
case "ICOSAHEDRON" :
// Step 1: Create the appropriate geometry.
geometry = new THREE.IcosahedronGeometry(PolyHeatMap.GEOMETRY_CIRCUMRADIUS);
// Step 2: Create one material for each face, and combine them into one big
// MeshFaceMaterial.
material = new THREE.MeshFaceMaterial(createMaterials(20, textArray));
// Step 3: Pair each face with one of the materials.
for (x = 0; face = geometry.faces[x]; x++)
{
face.materialIndex = x;
}
break;
// And so on, for other shapes.
}
function createTexture (title, color) {
var canvas = document.createElement("canvas");
// Magical canvas generation happens here.
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return new THREE.MeshLambertMaterial({ map : texture });
}
function createMaterials (numFacets, textArray)
{
var materialsArray = [],
material;
for (var x = 0, xl = numFacets; x < xl; x++)
{
material = createTexture(textArray[x], generateColor(textArray[x]));
material.side = THREE.DoubleSide;
materials.push(oMaterial);
}
return materials;
}
Cubes render perfectly using this technique, but with other polyhedra, the textures do not behave as expected:
It's hard to explain precisely what's happening here. Essentially, each face is displaying the correct texture, but the texture itself has been stretched and shifted as if to cover the entire polyhedron. In other words - looking at the shape dead-on - the upper-left face is only showing the upper-left portion of its texture, the upper-right face is only showing the upper-right portion, and so on.
The faces on the opposite side of the polyhedron shows no texture detail at all; only colors.
I had no experience with 3D rendering prior to experimenting with Three.js, so I imagine that there's some step I'm missing that is handled automatically by CubeGeometry but not its sister classes. I'd refer to other examples that have been posted, but most examples are rendering cubes, and those that don't are usually using solid colors.
What needs to happen for the textures on the non-cube shapes to be scaled and centered properly?
You need to set new UVs.
I made a simple example how to do it, don't know if it's the best way.
jsFiddle example
Update
geometry.faceVertexUvs[0] = [];
for(var i = 0; i < geometry.faces.length; i++){
// set new coordinates, all faces will have same mapping.
geometry.faceVertexUvs[0].push([
new THREE.Vector2( 0,0 ),
new THREE.Vector2( 0,1 ),
new THREE.Vector2( 1,1),
]);
}