How to use JSON file with Cluster Force - json

I wish to use Cluster Force Layout as described by Mike here: https://bl.ocks.org/mbostock/7882658
The example works fine for me, however, the problem is when I change the data source to a JSON file, which uses a different cluster name, things stop working. No errors, but does not display.
The goal is to group the names in each division into a cluster.
The JSON file is in the html... not sure if you can upload data for jsfiddle
Any direction here much appreciated.
Fiddle: https://jsfiddle.net/xbme6ekf/
This is where I try to recreate the nodes. The nodes appear in console.log, but never make it to the screen.
var nodes = d3.json("/r.json", function(error, data) {
for (var i = 0; i < data.length; i++) {
var obj = data[i];
for (var key in obj){
var rating = obj['rating']; // rating
var r = rating * 20; // radius
var n = obj['name']; // name
var div = obj['division']; // division
// d = {cluster: div, radius: r, name: n, division: div, rating: rating};
d = {cluster: div, radius: r};
// console.log(key+"="+obj[key]);
}
if (!clusters[i] || (r > clusters[i].radius)) clusters[i] = d;
// console.log(d);
}
return d;
});
Thanks
Kevin

First, you can use plunkr to add json file if you want to play with extra data file.
Second, I copied your code from fiddle to plunkr with json file, the console.log(nodes) didn't print out the data, it's because the code retrieving data here:
var nodes = d3.json("/r.json", function(error, data) {...})
is not exactly the same as in the example, because this is asynchronous request, so this line of code won't work:
var force = d3.layout.force()
.nodes(nodes) // data for nodes is not retrieved yet
Third, after I put d3 code into the request callback, there is circles in svg but not visible, I think it is because of the svg size setting (width, height) is not quite fit for the cx, cy of the circles, so I changed the svg to a smaller size, and it's visible. It depends on what you want to achieve at the end, but adjusting the position parameter for the circles can be helpful.
Working plunkr here. Hope this can help.

Related

How can I scale my map to fit my svg size with d3 and geojson path data

I am trying to create a d3 SVG that draws a map of New York State and scale it so that it fits the size of my SVG, the issue I am having is that when I use .fitSize([height, width], mapObject) it only returns a NaN error in the console.
the topoJSON file of NYS I am using
I am able to get the map to display without scaling but of course, it is not optimized and needs to be scaled
I have attempted what is said in this post but I have not figured out the correct solution
var map = d3.json('./ny.json')
Promise.all([map]).then(data => {
var height = 800;
var width = 800;
var mapData = data[0]
// original geoJSON to that works without scaling
// var geoData = topojson.feature(mapData, mapData.objects["cb_2015_new_york_county_20m"]).features
//
var geoData = topojson.feature(mapData, {
type:"GeometryCollection",
geometries: mapData.objects["cb_2015_new_york_county_20m"].geometries,
})
var projection = d3.geoMercator()
.fitSize([width, height], geoData)
var path = d3.geoPath()
.projection(projection)
d3.select('svg')
.attr('height', height)
.attr('width', width)
.selectAll('.county')
.data(geoData)
.enter()
.append('path')
.classed('.county', true)
.attr('d', path)
})
I am pretty sure this is a formatting error on my part, but I am unsure of what data .fitSize() or .fitExtent() is trying to compare against.
right now the way the code site I receive no error outputted to the console but I also have no data append to the SVG
The issue is that fitSize takes a geojson object while selectAll.data() takes an array, you are using one of these two for both in geoData. This leaves two solutions:
Solution 1:
If we use
var geoData = topojson.feature(mapData, mapData.objects["cb_2015_new_york_county_20m"]).features
var projection = d3.geoMercator()
.fitSize([width, height], geoData)
We get NaN errors because the projection is not set properly as we aren't passing a geojson object, just an array of geojson objects. We could solve this by making a feature collection with geoData and passing that to fitSize:
var geoData = topojson.feature(mapData, mapData.objects["cb_2015_new_york_county_20m"]).features
var projection = d3.geoMercator()
.fitSize([width, height], {type:"FeatureCollection", features: geoData})
Now we are passing a geojson feature collection to fitSize, we're all go on the projection, and since geoData is still an array, we can pass that to selectAll.data() unchanged.
Here's a block.
Solution 2:
If we use:
var geoData = topojson.feature(mapData, {
type:"GeometryCollection",
geometries: mapData.objects["cb_2015_new_york_county_20m"].geometries,
})
We get a geojson object, projection.fitSize works, but selectAll().data(geoData) doesn't add any features as it isn't an array - the enter selection is empty. We can substitute in selectAll().data(geoData.features) to solve this and enter one path per feature (alternatively we could use .data([geoData]) to enter one feature for all the paths).
Here's a block.
Both blocks are drawn at the correct scale - the map exceeds the block bounds as I didn't alter your 800x800 dimensions

Translate a model after load (move origin)

My company is using the latest version that supports multiple models for federation, the problem that we are facing is that sometimes the models don't quite line up correctly. I'm aware of the load option globalOffset but even with that in place, they don't line up.
I'm therefore looking for a way to move the model after it's been loaded, so that I can then store this new offset in the database, so that it loads correctly next time.
Is this possible at the moment?
If you models haven't set up with co-origin or in share coordinates before, then they won't be aligned with globalOffset option.
And yes, the model can be moved after loaded. You can check out this awesome extension, Viewing.Extension.Transform, written by our cool colleague Philippe and the translation tool is here.
Here is a sample showing how to move the whole model -100 units in the x-direction. Its key concept is applying your model offsets to each Forge fragments as below code snippet.
const fragCount = viewer.model.getFragmentList().fragments.fragId2dbId.length;
// Move whole model -100 units in the x-direction
const offset = new THREE.Vector3( -100, 0 , 0 );
for( let fragId = 0; fragId < fragCount; ++fragId ) {
const fragProxy = viewer.impl.getFragmentProxy( model, fragId );
fragProxy.getAnimTransform();
const position = new THREE.Vector3(
fragProxy.position.x + offset.x,
fragProxy.position.y + offset.y,
fragProxy.position.z + offset.z
);
fragProxy.position = position;
fragProxy.updateAnimTransform();
}
viewer.impl.sceneUpdated( true );

AS3 Clicking in "zones" of a picture

I have searched and simply cannot find what I need (if it exists).
A window will have a large picture.
The picture will be divided into zones (such as border lines that separate states on a map).
When a person clicks within a zone, then I will raise the appropriate event.
I've used AS3 with MXML to create a database program. All is working great except for this last step. I cannot figure out how the user is within a particular area of the picture when he clicks or when he touches.
I've read and tried to come up with an approach, and there must be (hopefully so) an easier way than the muddled nonsense I'm coming up with.
Thanks
VL
Are you drawing it in flash professional CS6? If so then why can't you just have the picture as a symbol and then just self divide the lines and make those divided areas into symbols that are children of the picture symbol. You could keep the individual state symbols right where they so that they stay true to the overall picture.
A first thought would be to make an instance of this picture symbol through the code, and then loop though all the children of that picture and add a click event to each one.
var picture:Picture=new Picture();
for(var i:int=0; i<picture.numChildren-1; i++){
picture.getChildAt(i).addEventListener(MouseEvent.CLICK, mouseEventHandler);
}
Please comment if I am missing something, or this does not work.
EDIT
Well, if you know the dimensions of the image, you could divide its width and height by 3(your number of rows and columns) and this is your zone dimensions. You then could take the mouse's click point relative to the top left of your picture and then divide its width by the zone with, and its height by the zone height, and then get its integer floor value, you could get which region it is. Code it below:
//This is all for a constat region list (like a window, or floor tiles, not things irregular)
import flash.display.Sprite;
var regionsX:int = 3; //Your number of windows across the row
var regionsY:int = 3; // across the column
var regions:Array = new Array(); // an array to hold the values that you will get from where the user clicks
// All of this used a 2D array method
for(var x:int = 0; x < regionX; x++) {
regions[regionsX] = new Array();
for(var y:int = 0; y < regionY; y++) {
regions[regionsX][regionsY] = "region(".concat(x).concat(",").concat(y);
// Here you make this equal to anything you want to get a value of,
//once the correct region is found (I just have a string version here for an example)
}
}
... // other stuff..
var picture:Picture = new Picture(); // your window picture
var regionWidth:Number = picture.width / regionsX; // Gets each region's width
var regionHeight:Number = picture.height / regionsY; // Get each regoin's height
...
picture.addEventListener(MouseEvent.CLICK, mouseEventListener); // add a click listener to the picture
function mouseEventListener(event:MouseEvent):void{
var mouseX:Number = picture.globalToLocal(event.stageX); // gets where the user clicked, and then converts it
//to the picture's cordinate space. ( 50,100 acording to the stage, could be (25,200) to the picture)
var mouseY:Number = picture.globalToLocal(event.stageY); // same for the Y
var regionIntX:Number = Math.floor(mouseX / regionWidth); // Dives the point by each region's width, and then
// converts it to a while integer. (For instance, if a region's width is 100 and you click at 288, then if you do the
// math, you clicked in the 3rd region, but it returns 2... why? (becaue the array counter starts at 0, so 0 is the 1st
// region, 1 is the second and so on...
var regionIntY:Number = Math.floor(mouseY / regionHeight); // Same for Y
var yourValue:String = regions[regionIntX][regionIntY]; // This returns that you initialy put into your 2d array
// by using the regionIntX and regionIntY for the array values. You have to decide what is stored in this array...
}
The simplest solution would be to add an event listener for MouseEvent.CLICK to the picture and in the handler check properties mouseX and mouseY of the picture. Define the bounds of each area in an XML or similar and check against current mouseX/Y to see which area has been clicked.

Connecting blocks together but prevent intersection

EDIT I have had a rethink: I am going to use a much easier to implement tree structure with a grid so that each block can have up to 4 neighbours.
I am trying to attach blocks on to the stalk (series of blocks) of this flower. Green blocks are the existing stalk and the blue block is the one attached to the mouse.
The block attached to the mouse will correctly snap to the nearest edges (I'm allowing diagonals for now) however the blocks will also be able to go 'inside' the stalk (image 2) by snapping to a block above or below.
My question is, how can I stop this? I considered
Iterating the list again but ignoring the block I just tried to attach to; I think this will just result in the same problem really by attaching to another block 'inside' the stalk.
Find which block I am intersecting and get another connection point from it, repeat until there are no intersections; This seems the better option but could be very messy when intersecting more than 1 block at a time
I should note that I plan on having a smoother snap, not just arbitrarily to an edge, so a grid is pretty much out of the question. I am pretty confident there must be an elegant solution, I'm just not seeing it!
Here is my snapping code as it currently stands
var mousePos:Point = new Point(mouseX, mouseY);// new Point(e.stageX, e.stageY);
var nearestPoint:Point = null;
var nearestDistance:Number = 0;
for (var i:int = 0; i < mPlant.length; ++i) {
var part:PlantPart = mPlant[i];
if (part is Stalk) {
var connectionPoint:Point = (part as Stalk).getNearestConnectionPoint(mousePos);
var distance:Number = Point.distance(mousePos, connectionPoint);
if (nearestPoint == null || distance < nearestDistance) {
nearestPoint = connectionPoint;
nearestDistance = distance;
}
}
}
if (nearestPoint != null) {
mMousePointer.x = nearestPoint.x;
mMousePointer.y = nearestPoint.y;
}
You can always pre-calculate a bounding Rectangle for the stalk, and then do the snapping check against that Rectangle.
The Rectangle can be calculated using something like this (un-tested code, though)
var _bounding:Rectangle = new Rectangle(int.MAX_VALUE, int.MAX_VALUE,0,0);
for each( var part:PlantPart in mPlant)
{
if(part is Stalk)
{
_bounding.width = part.width; // Width will always be the same
_bounding.height = Math.max( _bounding.height, part.y );
_bounding.x = Math.min( _bounding.x, part.x );
_bounding.y = Math.min( _bounding.y, part.y );
}
}

find closest point to mouse position

I've got a grid of sprites.
Now I would like to drag an image on a grid-element.
Therefore I need to know which x/y of the grid-element is the closest point to the mouse-position.
All the grid-elements are stored in an array.
How can I achieve that?
You must loop through all the elements and find the smallest distance to the mouse. Then store the array index of the element. Try something like this:
// Setup variables outside of loop.
var mousePoint:Point = new Point(mouseX, mouseY);
var elementPoint:Point = new Point();
var element:Sprite;
var closestIndex:uint = 0;
var closestDist:Number;
// Loop through elements
for (var i:int = 0; i < gridElements.length; i++)
{
element = gridElements[i] as Sprite;
// Set the elementPoint's x and y rather than creating a new Point object.
elementPoint.x = element.x;
elementPoint.y = element.y;
// Find distance from mouse to element.
var dist:Number = Point.distance(mousePoint, elementPoint);
// Update closestIndex and closestDist if it's the closest.
if (i == 0 || dist < closestDist)
{
closestDist = dist;
closestIndex = i;
}
}
// Can now use closestIndex to get the element from the array.
trace('The closest element is at index', closestIndex, ', with a distance of', closestDist);
Of course, this will only get you the first closest element, so you must decide what you want to happen if there are two elements an equal distance away.
You will also need to allow for the origins of your elements. The will probably have there origins set to there top left, so you need to allow for this in your distance calculation.
You could also check for a minimum distance. So if the user is dragging too far away from any of the elements then do nothing.
If all you need is the x,y of the closest grid then all you have to do is.
var gridX:int = Math.floor(mouseX / NumberOfColumns);
var gridY:int = Math.floor(mouseY / NumberOfRows);
This will convert your mouse coordinates to your grid coordinates.
Now comes the problem. If your storing them in a 2d array then you have your x/y if your storing them in a flat array (1d) you need to look it up just like you created it.
var myObject:Object = my2dArray[gridX, gridY];
var myObject:Object = myFlatArray[(gridX * NumberOfRows) + gridY];
If you have taken care of how you create your array and push the items in it, it should be no problem to retrieve stuff without searching it.