d3.js: why is d3.geo.path() giving NaN? - d3.js

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.

Related

ArcGIS Runtime : How to convert a point's unit from degree to meter

I have two geometries with the same coordinate system (Wgs84), but their data units are different, one is degree and the other is meter.
I need to perform some operations on them, like:
var g1 = GeometryEngine.Difference(geometry1, geometry2);
But I got an error:
System.ArgumentException:'Invalid argument: geometry1 and geometry2 must have equivalent spatial references.'
So I want to convert the data in degrees to the data in meters, I don’t know how to do it.
The data in meters comes from the shp file. This shp file is loaded into SceneView.
The data in degrees comes from the PreviewMouseLeftButtonDown event of SceneView:
// Get the mouse position.
Point cursorSceenPoint = mouseEventArgs.GetPosition(MySceneView);
// Get the corresponding MapPoint.
MapPoint onMapLocation = MySceneView.ScreenToBaseSurface(cursorSceenPoint);
Then I thought about whether the unit can be modified by setting SceneView.SpatialReference.Unit, but it is read-only.
A .NET solution is the best, and other languages are also acceptable.
Most geometry engine operations requires all geometries to be in the same spatial reference. As the error points to, that is not the case. Before performing any geometry engine operation, you could use the following code to bring geometry2 over to match the spatial reference of geometry1 (or vise-versa):
if (!geometry1.SpatialReference.IsEqual(geometry2.SpatialReference))
geometry2 = GeometryEngine.Project(geometry2, geometry1.SpatialReference);
The SceneView always returns coordinates in wgs84 lat/long.
var point1 = ...;
var point2= GeometryEngine.Project(point1, YourNewSpatialReference) as MapPoint;
public static Geometry? Project(Geometry geometry, SpatialReference outputSpatialReference);
public static Geometry? Project(Geometry geometry, SpatialReference outputSpatialReference, DatumTransformation? datumTransformation);

X,Y,Z coordinates of an object in forge viewer

I am trying to draw svg/pointcloud points on individual elements in Autodesk Forge. How to get (x,y,z) coordinates of elements.
Is there a way to extract positions of individual object so that we have the position vector in world space.
What I tried so far:
I cant seem to understand how to use the positions array as described in this thread using
frags = viewer.impl.getRenderProxy( viewer.model, fragId )
positions = frags.geometry.vb
Autodesk.ADN.Viewing.Extension.MeshData.js gives me the vertices of triangles (meshes/fragments) the element is made of.
I've created a self-contained extension that you can add to your viewer and illustrates how to get the component position:
Check this repo and the TransformationExplorerExtension
The code responsible for extracting the transformation matrix is:
getFragmentWorldMatrixByNodeId(nodeId) {
let result = {
fragId: [],
matrix: [],
};
let viewer = this.viewer;
this.tree.enumNodeFragments(nodeId, function (frag) {
let fragProxy = viewer.impl.getFragmentProxy(viewer.model, frag);
let matrix = new THREE.Matrix4();
fragProxy.getWorldMatrix(matrix);
result.fragId.push(frag);
result.matrix.push(matrix);
});
return result;
}

in d3.geo MultiPoint how can I provide different shapes for different poins?

I have some geoJson data that I am charting using d3.geo.
When I write something like
d3.select("svg")
...
.attr("d", function(d) {
return path({
type:"MultiPoint",
coordinates: get_activity_coords_(d.activities)
});
})
I always get a circle for each coordinate. The coordinates represent locations of various stopping points of a journey. What I would prefer is a different shape for the first and the last coordinate.
Is it possible to do this using MultiPoint, is there an example that I can follow? I could draw the points one by one, but I recall reading that MultiPoint is far faster. Plus, the code would be much clearer to read.
Thanks a lot.
You can't do different shapes for MultiPoint geoJSON with d3.geo.path. You can change the radius based on a function, but it looks like you can only set it per feature and not per point, so you'd have to break your set of points into multiple features and lose any performance benefit from using the single element.
However, there are other ways to go about doing this.
One option, as you mentioned, is to create a nested selection with a separate <path> element for each point, and draw each path using a d3.svg.symbol() function. You can then customize the symbol function to be based on data or index.
var trips = d3.select("svg").selectAll("g.trips")
.data(/*The data you were currently using for each path,
now gets to a group of paths */)
.attr("class", "trips");
//also set any other properties for the each trip as a whole
var pointSymbol = d3.svg.symbol().type(function(d,i){
if (i === 0)
//this is the first point within its groups
return "cross";
if ( this === this.parentNode.querySelector("path:last-of-type") )
//this is the last point within its group
return "square";
//else:
return "circle";
});
var points = trips.selectAll("path")
.data(function(d) {
return get_activity_coords_(d.activities);
//return the array of point objects
})
.attr("transform", function(d){
/* calculate the position of the point using
your projection function directly */
})
.attr("d", pointSymbol);
Another option, which allows you to set custom shapes for the first and last point (but all intermediary points would be the same) is to connect the points as the vertices of a single, invisible <path> element and use line markers to draw the point symbols.
Your approach would be:
Create a <defs> element within your SVG (either hard-coded or dynamically with d3), and define the start, middle and end marker points within them. (You can use d3.svg.symbol() functions to draw the paths, or make your own, or use images, it's up to you.)
Use a d3.svg.line() function to create the path's "d" attribute based on your array of point coordinates; the x and y accessor functions for the line should use the projection function that you're using for the map to get the x/y position from the coordinates of that point. To avoid calculating the projection twice, you can save the projected coordinates in the data object:
var multipointLine = d3.svg.line()
.x(function(d,i) {
d.projectedCoords = projection(d);
return d.projectedCoords[0];
})
.y(function(d){ return d.projectedCoords[1];});
(You can't use your d3.geo.path() function to draw the lines as a map feature, because it will break the line into curves to match the curves of longitude and latitude lines in your map projection; to get the line markers to work, the path needs to be just a simple straight-line connection between points.)
Set the style on that path to be no stroke and no fill, so the line itself doesn't show up, but then set the marker-start, marker-mid and marker-end properties on the line to reference the id values of the correct marker element.
To get you started, here's an example using d3 to dynamically-generate line markers:
Is it possible to use d3.svg.symbol along with svg.marker

how to make a radial line segment using D3.js

I'd like to create a line using polar coordinates.
Example:
A line whose centre is at cx=0, cy=0, at an angle of pi/4, but only drawn from a start radius of 4 and end radius of 7.
I could use maths and do the job myself, but d3.js appears to have a radial line generator, but I'm finding the documentation hard to grasp, being a d3 noob.
Using the line generator is relatively straightforward. You can use it with the default options and specify everything in the data like this:
d3.svg.line.radial()([[4,Math.PI/4],[7,Math.PI/4]]);
Alternatively, you could only provide the data that changes and everything else as a default:
var line = d3.svg.line.radial()
.angle(Math.PI/4)
.radius(function(d) { return d; });
line([4,7]);

Google maps polygon optimization

I extracted country outline data from somewhere and successfully managed to convert it into an array of lat-lng coordinates that I can feed to Google maps API to draw polyline or polygons.
The problem is that that there are about 1200+ points in that shape. It renders perfectly in Google maps but I need to reduce the number of points from 1200 to less than 100. I don't need a very smooth outline, i just need to throw away the points that I can live without. Any algorithm or an online tool that can help me reduce the number of points is needed.
Found this simple javascript by Bill Chadwick. Just feed in the LatLng to an array and pass in to the source arguments in a function here Douglas Peucker line simplification routine
it will output an array with less points for polygon.
var ArrayforPolygontoUse= GDouglasPeucker(theArrayofLatLng,2000)
var polygon=new google.maps.Polygon({
path:ArrayforPolygontoUse,
geodesic:true,
strokeColor:"#0000FF",
strokeOpacity:0.8,
strokeWeight:2,
fillColor:"#0000FF",
fillOpacity:0.4,
editable:true
});
theArrayofLatLng is an array of latlng that you collected using google maps api.
The 2000 value is kink in metres. My assumption is, the higher the value, more points will be deleted as an output.
For real beginners:
Make sure you declare the js file on your html page before using it. :)
<script type="text/javascript" src="js/GDouglasPeucker.js"></script>
I think MapShaper can do this online
Otherwise, implement some algorithm
If you can install postgis which i think is easy as they provide an installer then you can import the data and execute snaptogrid() or st_simplify() for which i cannot find an equivalent in mysql.If you decide to go with postgis which i recommend cause it will help you down the road i can provide you with the details.
Now for an easy custom solution you can reduce size by cutting or rounding some of the last digits of the coords and then merge the same coords resulting actually in a simple snaptogrid().
Hope it helps
I was looking for exactly the same thing and found Simplify.js. It does exactly what you want and is incredibly easy to use. You simply pass in your coordinates and it will remove all excess points.
simplify(points, tolerance, highQuality)
The points argument should contain an array of your coordinates formatted as {x: 123, y: 123}. (Afterwards you can convert it back to the format you wish.)
The tolerance should be the precision in decimal degrees. E.g. 0.0001 for 11 meters. Increasing this number will reduce the output size.
Set highQuality to true for better results if you don't mind waiting a few milliseconds longer.
Mostly likely what you want to divide the points into 2 half and want to try my Javascript function:
function shortenAndShow ( polyline, color ) {
var dist = 0, copyPoints = Array ( );
for ( var n = 0, var end = polyline.getVertexCount ( ) - 1; n < end ; n++ ) {
dist += polyline.getVertex ( n ).distanceFrom ( polyline.getVertex ( n +1 ) );
copyPoints.push ( polyline.getVertex (n) );
}
var lastPoint = copyPoints [copyPoints.length-1];
var newLine = new GPolyline (copyPoints, color, 2, 1);
gmap2.addOverlay ( newLine );
}
I agree the Unreason's anwser,The website support GeoJson,I used it in my website,and it cut down my geoJson ,But I think you also need this world country geo Json

Resources