Leaflet: Placing markers/clusters in right side of map - d3.js

I am working with a dataset where I am given time spent in locations or forms of transportation - I am trying to attach the marker clusters for forms of transportation on a line below each other on the right side of the map, such that one can see how much time is spent on each kind of transport.
I tried doing this by finding the latlong of the different pixel positions, each time the map moves, like so:
var centerPoint = map.getSize().divideBy(2);
map.on('move', function(e) {
var name_array = [];
var multiplier_x = 0.95;
var multiplier_y = 0.8;
var index = 0;
layer_group.eachLayer(function (layer) {
if (layer.feature.properties.Name == 'Cycling' || layer.feature.properties.Name == 'Walking' || layer.feature.properties.Name == 'Driving' || layer.feature.properties.Name == 'Boating' || layer.feature.properties.Name == 'On a bus' || layer.feature.properties.Name == 'On a train'){
if(!(layer.feature.properties.Name in name_array)){
var targetPoint = centerPoint.add([centerPoint.x*multiplier_x, -centerPoint.y*(multiplier_y-index)]),
targetLatLng = map.containerPointToLatLng(targetPoint);
name_array[layer.feature.properties.Name] = targetLatLng;
index += 0.2;
}
layer.setLatLng(name_array[layer.feature.properties.Name]);
}
});
});
The problem is that eventhough each transportation form is given a different location, they seem to cluster together anyway, and change between different clusters when the map is moved. Additionally, when moving the brush the map seems to move further and further to the left, as I am trying to fit the map to the bounds.
I don't know if it is possible to achieve what I am trying to - or if there maybe is another way of doing it, maybe by creating a seperate cluster group for the transportation, which I guess at least would make sure that the transportation isn't clustered with the locations?
Code: http://bl.ocks.org/skov94/e44fcebd282fe5eb4c708e8ba0af95d6

To plot them at a given pixel, based on a given latlng:
...
var topLeft = map._getNewPixelOrigin(map.getCenter(), map.getZoom());
var targetPoint = map.project(map.getCenter(), map.getZoom()).subtract(topLeft);
var targetLatLng = map.unproject(targetPoint);
...
By constantly calculating the new pixel origin, this ensures that the pixels and latLngs stay aligned even when panning

Related

AmCharts 4 : Show totals of clustered and stacked charts, for individual clusters

Background:
Following the example of stacked and clustered charts as shown here I have created a chart with clustered and stacked columns. I am also attempting to display the total of the individual stacks on the top of the column as shown here
Issue faced:
The main is I am facing is the total value is displayed for all the data in the ValueAxis and not for individual stacks. Please help in displaying individual totals for the clustered columns.
Sample code:
Note the inbuilt APIs used to calculate and display totals
// Fully working example in the CodePen
// Enabling totals calculation
let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.calculateTotals = true;
// Displaying the total in the LabelBullet
let totalBullet = totalSeries.bullets.push(new am4charts.LabelBullet());
totalBullet.label.text = "{valueY.sum}";
Codepen depicting the issue (note the totals on top of the stacked columns)
Alternate approaches considered:
Adding a field in the data with the totals for the stacks and displaying it instead (Problem: Unable to deselect using the Label - the value would be static and won't change)
Other help from StackOverflow:
Found samples of showing totals on stacked charts, but unable to find help on dealing with grouping
Although this is 7months old, I didn't find an answer either for this anywhere. If you haven't found a solution yet I was able to work around this by creating two Y axes and assigning them the series individually, then adding in the 'none' series for the totals bullet as well.
I adapted your Codepen example here in jsfiddle (codepen freezes for me for some reason)
Essentially though you create the two axes, and hide the labels for the second one.
let valueAxis0 = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis0.min = 0;
valueAxis0.calculateTotals = true;
valueAxis0.title.text = "Expenditure (M)";
var valueAxis1 = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis1.renderer.grid.template.disabled = true;
valueAxis1.calculateTotals = true;
valueAxis1.syncWithAxis = chart.yAxes.getIndex(0);
valueAxis1.min = 0;
valueAxis1.renderer.labels.template.disabled = true;
Then select it when creating the series and assign to the appropriate axis with series.yAxis = valueAxis;
function createSeries(field, name, stacked, yAxes) {
var valueAxis;
if (yAxes == 0) {
valueAxis = valueAxis0;
} else if (yAxes == 1) {
valueAxis = valueAxis1;
}
let series = chart.series.push(new am4charts.ColumnSeries());
...
series.yAxis = valueAxis;
Then the same goes for the TotalSeries

Balloon in amMap country map

I'm using amMap country map and want to show all country names in balloons without rollover or hover on that country. Means all balloons are visible on map load.
Showing more than one rollover balloon, or making it appear without hovering on a country is not possible, I'm afraid.
What you can do is to display a "label" on each of those countries. A label is an instance of MapImage with a text label and no actual image.
Placing an image/label for each country using longitude/latitude coordinate can be quite tedious work. Therefore you can use the following example which places area title in the geometrical center of each area automatically.
http://codepen.io/amcharts/pen/5687a78507081e1c8779cf067b762de7
The above map is for the United States, but you can use the code in it to process labels on just about any map.
The following code is responsible for that:
map.addListener("init", function () {
// set up a longitude exceptions for certain areas
var longitude = {
"US-CA": -130,
"US-FL": 120,
"US-TX": 1,
"US-LA": 40
};
var latitude = {
"US-AK": -85
};
setTimeout(function () {
// iterate through areas and put a label over center of each
map.dataProvider.images = [];
for( x in map.dataProvider.areas ) {
var area = map.dataProvider.areas[ x ];
area.groupId = area.id;
var image = new AmCharts.MapImage();
image.latitude = latitude[ area.id ] || map.getAreaCenterLatitude( area );
image.longitude = longitude[ area.id ] || map.getAreaCenterLongitude( area );
image.label = area.id.split('-').pop();
image.title = area.title;
image.linkToObject = area;
image.groupId = area.id;
map.dataProvider.images.push( image );
}
map.validateData();
console.log(map.dataProvider);
}, 100)
});
Please note, that sometimes country/state shapes will not make the dead center a perfect logical place to place a label. For those situations use the longitude and latitude overrides to adjust position of the label for the particular country.

Is it possible to loop through a sprite group in three.js with a tween that ends in a different position for each sprite?

I'm confused.
I've made a group of 10 sprites and added them to a THREE.Object3D() called fireworkGroup. I have another Object3D called explode. The tween loops through the sprites changing them from their initial position to explode.position.
for ( var i = 0; i < fireworkGroup.children.length; i ++ )
{
explode.position.x =(Math.random() - 0.5)*4;
explode.position.y =4;
explode.position.z =2;
var tweenLaunch = new TWEEN.Tween(fireworkGroup.children[i].position).to(explode.position, 4000).easing( TWEEN.Easing.Quartic.In);
tweenLaunch.start();
}
The tween is moving all the sprites from their start position to the same end position. So I thought this might be because "tweenLaunch" is being overwritten with a different explode.position each time as the tween is rendered so I'm only seeing the last one created in the loop. When I refresh the page they do all move to a different position, consistent with the Math.random().
But then why do all the sprites move to the explode.position? If "tweenLaunch" is being overwritten then why is it not moving only the last sprite?
Is it possible to have a loop with a tween in it that also changes?
Many Thanks.
I've managed to work out what was wrong by reading around the subject on Stackoverflow questions and answers, looking at a great particle example by stemkoski then trial and error.
view-source:http://stemkoski.github.io/Three.js/Particles.html
I used console.log to look at explode.position that I was using as the second position in the tween. It wasn't holding the values I wanted (a different Math.random on each loop).
So I created fireworkAttributes:
fireworkAttributes = { startPosition: [], explodePosition: [], nextPosition: [] };
and then cloned the sprite position in the function that created the sprites using:
fireworkAttributes.explodePosition.push( sprite.position.clone() );
then looped it in it's own function:
for (var i = 0; i < fireworkGroup.children.length; i++)
{
fireworkAttributes.explodePosition[i].x = (Math.random() - 0.5)*4;
fireworkAttributes.explodePosition[i].y = 4;
fireworkAttributes.explodePosition[i].z = 2;
}
then changed the code in the original question to:
for ( var a = 0; a < fireworkGroup.children.length; a ++ )
{
//need to use this new object as tweening to fireworkAttributes.explodePosition[a] does not work
explodeSite.position = fireworkAttributes.explodePosition[a];
var tweenLaunch = new TWEEN.Tween(fireworkGroup.children[a].position).to(explodeSite.position, 4000).easing( TWEEN.Easing.Quartic.In);
tweenLaunch.start();
}
There may be a more elegant way to do this and I will be working to clean up the code where possible but this does work.

d3 ordinal scale that adjusts to cluster size

How can I shift the start point of each nested group according to how much space that group requires in each cluster to mainatin a constant bar width? The ordinal scale allocates the same space for each cluster and then decreases the bar width to accomodate more bars but I want to adjust in a linear fashion to maintain the bar size. Any ideas?
Thanks
Have you tried swapping the 'ordinal' with 'linear'.. because you can specify multiple domain and range values just like in ordinal scales and d3 will use all of them like an ordinal scale, but keeps the smooth linear interpolation.. Check out how this behaves.
var scale = d3.scale.linear().domain([1,2,3,4]).range([1,20,300,4000])
Ok so after gaining more insight, it looks like you do have to copy the original scale and alter it to make a new scale for each object that has a different... umm, scale. Funny that obvious clue never entered my mind till just now.
I copied that d3 example into jsfiddle and altered it a little and seems to work. Was this the effect you were going for?
http://jsfiddle.net/2ktVC/2/
I mostly just replaced the harmless forEach loop in the example with these here guns.
data.forEach(function(d,i){/*-=======================*/
var myNames = d3.keys(d).filter(function(key) { ////
if(key !== "State" && d[key]){ /*-==============-*/
allAgeNames[key]
= true;
return true;
} return false;
//==========
xScales[i] = d3.scale.ordinal();/*==========*/
d.ages = myNames.reduce(function(arr, name) {
if(+d[name]) arr.push({
value:+d[name],
x:xScales[i],
name: name
});/*===*/
return arr;
},[]);/*
//||||||||||||||||||||||||||==========================----*/
xScales[i].domain(myNames).rangeRoundBands([0, x.rangeBand()], 0);
d.x = /*|_|_|_|_|_*/
xScales[i];
d.ages.x =
xScales[i]
});
Im kidding, stack overflow wont let me link to jsfiddle without some reference code.... so out come the jokes.

How to save (preserve) the zoom state of flot graphs on Update with AJAX

I have a simple application which polls database for data every minute. When new data is fetched, I am updating the graph using ajax. However, whenever I update the graph (re-plot it with new values added to plot data) the current state of zoom is lost. Before updating the graph, I want to preserve the latest zoom position. After updating the graph, I want to zoom the graph to its saved position. This is important because re-zooming every minute is irritating. Is this possible?
I tried this answer from Ozan and I couldn't get the block for copying the zoom to work so I just used plot.getOptions() and used that to draw the graph.
Like this:
var oldOptions = plot.getOptions();
plot = $.plot($("#graph"), data, oldOptions);
This way you can dynamically change your view and the auto-update will update without changing your view.
Here is the answer by Joshua Varner 1
When you get your new data before you re plot get the current zoom and
add it to the options on update.
// Get the current zoom
var zoom = plot.getAxes();
// Add the zoom to standard options
var zoomed = {};
$.extend(zoomed,options);
zoomed.xaxis.min = zoom.xaxis.min;
zoomed.xaxis.max = zoom.xaxis.max;
zoomed.yaxis.min = zoom.yaxis.min;
zoomed.yaxis.max = zoom.yaxis.max;
// draw/save the plot
plot = $.plot($("#graph"), d, zoomed);
You should get the information about the state of the current axes, merge it with your initial options and than provide them to the new plot.
// Deep copy of the options object so that we can keep it unchanged
// for when we don't want to preserve the zoom.
var copyOptions = $.extend(true, {}, options);
if (plot != null && preserveZoom) {
// There might be more than one Y axis
var zoomY = plot.getYAxes();
for (var i = 0; i < zoomY.length; i++) {
copyOptions.yaxes[i].min = zoomY[i].min;
copyOptions.yaxes[i].max = zoomY[i].max;
}
// Considering only one X axis, in case of more than one
// you should use the same method as for the Y axis.
copyOptions.xaxis.min = plot.getXAxes()[0].min;
copyOptions.xaxis.max = plot.getXAxes()[0].max;
}
plot = $.plot("#placeholder", data, copyOptions);

Resources