I'm currently trying to map a sphere with squares using d3.js and GeoJon. I've found a script which generate a grid using MultiPolygon : https://jsfiddle.net/94dhdmnw/
{
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'MultiPolygon',
coordinates: [coordinates]
}
}]
}
Nonetheless, I need an id for each square of my grid. That's why I want to map my sphere using Polygons instead of MultiPolygons. Thus, I've adapted the script in order to get a Polygon mapping : https://jsfiddle.net/et3s6p33/
Yet, when I map my sphere using the file obtained through the Polygon script, I have only half of my sphere which is mapped - not even with the right squares' size.
Sphere with MultiPolygon mapping :
Sphere with Polygon mapping :
Any ideas why?
I am using this script to map my sphere : http://jsfiddle.net/6n230b3w
Edit : Or maybe there is a way to set a property for each square of my MultiPolygon?
Related
In the application I develop I am trying to combine the closest dots of the same color, dots created from a 3D coordinates vector and using Points, by using a BufferGeometry with custom positions, positions set by using the setAttribute command of the geometry and giving it a Float32BufferAttribute(positionArray, 3) object. The problem I encountered is that I have a lot of such geometries (tens of thousands usually) and since I add each one separately to the group, I have big performance issues.
So I tried to merge the buffer geometries in a single one to draw all of them at once using BufferGeometryUtils.mergeBufferGeometries, but that didn't work.
How it looks without merging the geometries
How it looks with merged geometries
This is how I create the geometries:
const newGeometry = baseGeometry.clone();
newGeometry.setAttribute(
'position',
new Float32BufferAttribute(geometryPositions, 3)
);
newGeometry.setAttribute(
'color',
new Float32BufferAttribute(geometryColors, 3)
);
newGeometry.computeBoundingSphere();
geometryArray.push(newGeometry);
And I add them like this to my group.
geometryArray.forEach((e) => {
this.group.add(new Mesh(e, baseMaterial));
});
This is how I merge them and add them to the group.
const merged = BufferGeometryUtils.mergeBufferGeometries(geometryArray);
this.group.add(new Mesh(merged, baseMaterial));
As you can see the geometries use the same material in all cases, the color being defined in the colors attribute of each geometry and vertexColors is set to true on the MeshBasicMaterial.
For a single geometry in the array, the position/color data looks like this. The sizes can be random, and the array may or may not be empty depending if the points have neighbors. The format is 3D coordinates [x1,y1,z1,x2,y2,z2,.....].
const positions = {
itemSize: 3,
type: 'Float32Array',
array: [
4118.44775390625, -839.14404296875, 845.7374877929688, 4125.9306640625,
-808.6709594726562, 856.7002563476562, 4118.44775390625,
-839.14404296875, 845.7374877929688, 4129.93017578125, -870.6640625,
828.08154296875,
],
normalized: false,
};
const colors = {
itemSize: 3,
type: 'Float32Array',
array: [
0.9725490212440491, 0.5960784554481506, 0.03529411926865578,
0.9725490212440491, 0.5960784554481506, 0.03529411926865578,
0.9725490212440491, 0.5960784554481506, 0.03529411926865578,
0.9725490212440491, 0.5960784554481506, 0.03529411926865578,
],
normalized: false,
};
How could I improve the performance of the code above and keeping the custom positions and colors of each geometry intact?
I'm doing something like this, How to create a custom square in a-frame but with custom shapes (i.e. drawing around an image to make a hotspot to make part of that image interactive)
I've got the line working and I'm now trying to convert this into a fill.
this._mesh = make('a-plane', {
geometry:"buffer: false"
}, this.el)
this._mesh.addEventListener('loaded', e => {
this._mesh.getObject3D('mesh').geometry.vertices = this._points
this._mesh.getObject3D('mesh').geometry.computeFaceNormals()
this._mesh.getObject3D('mesh').geometry.computeVertexNormals()
})
I'm getting close but it's only showing one triangle i.e. something like this
How do i get the shape to fill the whole area? I have done this before with a ConvexGeometry and Quick hull but it seems cumbersome.
I got the idea for updating the vertices of a plane from the above post.
If you create an array with Vector2 objects representing the contour of your shape in CCW order, you can use an instance of Shape and ShapeBufferGeometry to achieve the intended result. Just pass the array of points to the ctor of Shape. The following official three.js example demonstrates this approach:
https://threejs.org/examples/webgl_geometry_shapes
BTW: Instead of defining the contour by an array of points, you can also use the API of Shape to define shapes. A simple triangle would look like so:
var triangleShape = new THREE.Shape()
.moveTo( 80, 20 )
.lineTo( 40, 80 )
.lineTo( 120, 80 )
.lineTo( 80, 20 );
var geometry = new THREE.ShapeBufferGeometry( shape );
var mesh = new THREE.Mesh( geometry, material );
three.js R113
For a dataviz project, I have some datas in an array. I need to use these datas to render a wave in ThreeJS like this:
The height of each peak depends on the given size and must be in its circle depending on the year.
I thought about creating a plan and deforming it with a vertex shader based on the data. Unfortunately, it seems that this is not possible. I'm a bit lost and I clearly need advices about how to do this.
The array looks like this:
[
{
"year": 2016,
"dimension": 28.400 // hectares
},
{
"year": 1995,
"dimension": 12.200
}
]
There is a "displacementMap" texture property on the THREE.Materials.. this will displace the vertices vertically based on the brightness of the pixel in the texture.
You can make a plane with a bunch of subdivisions, like new THREE.Mesh(new THREE.PlaneGeometry(10,10, 30,30),new THREE.MeshStandardMaterial({displacementMap:yourDisplacementTexture})
For the displacementMap texture, you can either use an external image, or create a canvas, draw your height data into that, and then create a THREE.Texture( thecanvas ).
Yet another option is to create the subdivided plane using THREE.PlaneGeometry()
then get the geometry.vertices, and modify them by setting the .z value in each vertex.. followed up with geometry.verticesNeedUpdate = true (to tell the renderer to send the modifications to the GPU).
The answer below on drawing a polygon works well for a single polygon. However, what if there are more than one polygon? Simply adding an additional polygon with points seems not to work even though using "select all" would seem to indicate that it would be OK to add a couple more polygons without much problem..
We have an array of polygons, each of which has an attribute Points which is an array of points.
The first array with polygon should obviously be mapped and the point arrays of each member processed as described. But how to spedify this two-level structure with d3?
Proper format for drawing polygon data in D3
The answer is simple and straightaway. Just pass the array of polygons as data to d3 selection.
In your case it seems that you are using an array of polygons which are composite objects, each having a key called 'Points'. I assume it looks something like this-
var arrayOfPolygons = [{
"name": "polygon 1",
"points":[
{"x":0.0, "y":25.0},
{"x":8.5,"y":23.4},
{"x":13.0,"y":21.0},
{"x":19.0,"y":15.5}
]
},
{
"name": "polygon 2",
"points":[
{"x":0.0, "y":50.0},
{"x":15.5,"y":23.4},
{"x":18.0,"y":30.0},
{"x":20.0,"y":16.5}
]
},
... etc.
];
You will just have to use d.Points instead of d when writing the equivalent map function, which can be written as follows-
vis.selectAll("polygon")
.data(arrayOfPolygons)
.enter().append("polygon")
.attr("points",function(d) {
return d.points.map(function(d) {
return [scaleX(d.x),scale(d.y)].join(",");
}).join(" ");
})
.attr("stroke","black")
.attr("stroke-width",2);
You can check the following working JSFiddle to verify.
EDIT- The same example as above with convex hull implementation for rendering complete polygons. http://jsfiddle.net/arunkjn/EpBCH/1/ Note the difference in polygon#4
I am trying to do some online mapping with d3, but running into a problem when I try to plot a line between two points.
I have calculated the centroid of two polygons (source and target)
In the code:
var projection = d3.geo.mercator()
.scale(width)
.translate([0, 0]);
var path = d3.geo.path()
.projection(projection);
From the JS console:
> path({type: "LineString",
coordinates: [path.centroid(source_country), path.centroid(target_country)]});
"M277.05056877663407,121.67976219138909L-694.1792414247936,NaN"
Yet, the centroid calculations seem to be working fine (plotting those points shows them on the map)
> [path.centroid(source_country), path.centroid(target_country)]
[
Array[2]
0: 103.89396329123777
1: -41.453727169465765
length: 2
__proto__: Array[0]
,
Array[2]
0: -260.3172155342976
1: -245.57309459883245
length: 2
__proto__: Array[0]
Any ideas why that NaN is appearing at the end of the path generated for my LineString?
The problem here is that you're projecting the lat/lon coordinates twice. The path() operator expects to take lat/lon and project to pixels; the path.centroid() method also expects a lat/lon geometry, and also produces a pixel-based projection.
So when you call path on [path.centroid(...), path.centroid(...)], you're trying to project already-projected coordinates. You get at NaN because the y-position of the pixel coordinates, -245, is out of bounds for a longitude value.
The easiest way to fix this is probably to use d3.svg.line to create the centroid-centroid path. I haven't tested this, but I think it would look like:
var line = d3.svg.line();
line([path.centroid(source_country), path.centroid(target_country)]);
OK just now I met the same error,
for anyone who meet NAN problem:
the format of coordinate must be correct. e.g. for type Polygon, the coordinate must have a 3-level nested array. e.g. [[[1,2],[2,3]]]
coordinates must be float/integer, but not string (e.g. 1 correct, "1" error )
you can inspect the detailed content of the error result, e.g. M...L...Z... and find out where the error is.