In my project, we are using the highchart version: Highcharts JS v3.0.10.
What should be equivalent code or property which is suitable for allowOverlap = false according to the newer version?
How to achieve alloOverlap= false in the older version?
Link: https://api.highcharts.com/highcharts/series.timeline.dataLabels.allowOverlap
The code that is responsible for hiding the overlapping data labels is currently in Highcharts core: https://code.highcharts.com/highcharts.src.js, but previously it was a separate module: https://code.highcharts.com/5/modules/overlapping-datalabels.src.js
However, to make it work you would have to make many changes in your code. I think using the plugin below will be the best solution for you:
(function(H) {
var each = H.each,
extend = H.extend;
/**
* Hide overlapping labels. Labels are moved and faded in and out on zoom to provide a smooth
* visual imression.
*/
H.Series.prototype.hideOverlappingDataLabels = function() {
var points = this.points,
len = points.length,
i,
j,
label1,
label2,
intersectRect = function(pos1, pos2, size1, size2) {
return !(
pos2.x > pos1.x + size1.width ||
pos2.x + size2.width < pos1.x ||
pos2.y > pos1.y + size1.height ||
pos2.y + size2.height < pos1.y
);
};
// Mark with initial opacity
each(points, function(point, label) {
label = point.dataLabel;
if (label) {
label.oldOpacity = label.opacity;
label.newOpacity = 1;
}
});
// Detect overlapping labels
for (i = 0; i < len - 1; ++i) {
label1 = points[i].dataLabel;
for (j = i + 1; j < len; ++j) {
label2 = points[j].dataLabel;
if (label1 && label2 && label1.newOpacity !== 0 && label2.newOpacity !== 0 &&
intersectRect(label1.alignAttr, label2.alignAttr, label1, label2)) {
(points[i].labelrank < points[j].labelrank ? label1 : label2).newOpacity = 0;
}
}
}
// Hide or show
each(points, function(point, label) {
label = point.dataLabel;
if (label) {
if (label.oldOpacity !== label.newOpacity) {
label[label.isOld ? 'animate' : 'attr'](extend({
opacity: label.newOpacity
}, label.alignAttr));
}
label.isOld = true;
}
});
};
H.wrap(H.Series.prototype, 'drawDataLabels', function(proceed) {
proceed.call(this);
this.hideOverlappingDataLabels();
});
}(Highcharts));
Live demo: http://jsfiddle.net/BlackLabel/6s5ycrwa/
Related
I'm working with google maps api v3 and I'm trying to use snap to road with more than 100 points but in addition end up with just one polyline with the whole route that I can put a small animation. The view is a html.erb.
var apiKey = any_key;
var map = handler.getMap();
var drawingManager;
var placeIdArray = [];
var snappedCoordinates = [];
var path = <%= raw(#locations) %>
var markers = <%= raw(#markers) %>
var centerOn = path[0].split(',');
function breadCrumbsGrapher(path) {
handler.removeMarkers(Gmaps.store.markers);
for(var i = 0; i < polylines.length; i++) {
polylines[i].setMap(null);
}
var divided = handlePath(path);
if (typeof divided[0] == 'object') {
for(var i = 0; i < divided.length; i++) {
runSnapToRoad(divided[i]);
}
} else {
runSnapToRoad(path);
}
}
function waypointsLimiter(path) {
var path_loc_size = path.length;
var limited = [];
if(path_loc_size > 30) {
var stepper = Math.ceil(path_loc_size/30);
for(var i = stepper; i < path_loc_size; i += stepper) {
limited.push(path[i]);
}
if(limited.indexOf(path[path_loc_size-1]) == -1) {
limited.push(path[path_loc_size-1]);
}
} else {
limited = path;
}
return limited;
}
function handlePath(path) {
var i = 0;
var j = path.length;
if (j > 100) {
var newArray = [],
chunk = j/2;
if (j >= 200) {
chunk = j/3;
} else if (j >= 300) {
chunk = j/4;
} else if (j >= 400) {
chunk = j/5;
} else if (j >= 500 ) {
chunk = j/6;
} else if (j >= 600) {
chunk = j/7;
} else if (j >= 700 || j <= 799) {
chunk = j/8;
} else {
alert('La ruta no puede ser mostrada');
}
for (i, j; i < j; i+=chunk) {
newArray.push(path.slice(i,i+chunk+1));
}
return newArray;
} else {
return path;
}
}
// Snap a user-created polyline to roads and draw the snapped path
function runSnapToRoad(path) {
var path = path.join('|');
$.get('https://roads.googleapis.com/v1/snapToRoads', {
interpolate: true,
key: apiKey,
path: path,
}, function(data) {
processSnapToRoadResponse(data);
drawSnappedPolyline();
});
}
// Store snapped polyline returned by the snap-to-road service.
function processSnapToRoadResponse(data) {
snappedCoordinates = [];
placeIdArray = [];
for (var i = 0; i < data.snappedPoints.length; i++) {
var latlng = new google.maps.LatLng(
data.snappedPoints[i].location.latitude,
data.snappedPoints[i].location.longitude);
snappedCoordinates.push(latlng);
placeIdArray.push(data.snappedPoints[i].placeId);
}
}
// Draws the snapped polyline (after processing snap-to-road response).
function drawSnappedPolyline() {
var symbol = {
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
scale: 3,
strokeColor: '#3B16B3'
};
var snappedPolyline = new google.maps.Polyline({
path: snappedCoordinates,
strokeColor: '#E51E25',
strokeWeight: 3,
icons: [{
icon: symbol,
offset: '0%'
}]
});
snappedPolyline.setMap(map);
animate(snappedPolyline);
zoomToObject(snappedPolyline);
polylines.push(snappedPolyline);
}
function zoomToObject(obj){
var bounds = new google.maps.LatLngBounds();
var points = obj.getPath().getArray();
for (var n = 0; n < points.length ; n++){
bounds.extend(points[n]);
}
map.fitBounds(bounds);
}
function animate(line) {
var count = 0;
window.setInterval(function() {
count = (count + 1) % 600;
var icons = line.get('icons');
icons[0].offset = (count / 6) + '%';
line.set('icons', icons);
}, 70);
}
breadCrumbsGrapher(path);
Also I've tried declaring a variable outside so I can concat all of the coordinates and generate a polyline with it but doesn't seem to work. Actualy that big array ends up being of 2000+ points.
The result that I have with the provided code
After all of that the issue is that I don't know how to merge the polylines to have just one line and being able to animate just that one line. If there's more than 100 coordinates I plot more polylines. In the image you can see that there's 3 icons (one for each polyline) and I need just to draw one line and have 1 icon.
To reproduce the issue just add a key and if you want use this set of coordinates:
https://drive.google.com/file/d/1jLb7Djv5DiSdR3k4QZRSatXBwrohlxcI/view?usp=sharing
function breadCrumbsGrapher(path) {
//mapMarkers();
snappedCoordinates = [];
handler.removeMarkers(Gmaps.store.markers);
for(var i = 0; i < polylines.length; i++) {
polylines[i].setMap(null);
}
var divided = handlePath(path);
if (typeof divided[0] == 'object') {
for(var i = 0; i < divided.length; i++) {
runSnapToRoad(divided[i]);
}
} else {
runSnapToRoad(path);
}
console.log(snappedCoordinates);
drawSnappedPolyline();
}
function runSnapToRoad(path) {
var path = path.join('|');
$.get('https://roads.googleapis.com/v1/snapToRoads', {
interpolate: true,
key: apiKey,
path: path,
}, function(data) {
processSnapToRoadResponse(data);
//drawSnappedPolyline();
});
}
I've changed the code but it doesn't work, even though I end up with a 2,557 coordinates array.
I've tried also tried this thinking that this could give me the time to have all coordinates:
async function breadCrumbsGrapher(path) {
//mapMarkers();
snappedCoordinates = [];
handler.removeMarkers(Gmaps.store.markers);
for(var i = 0; i < polylines.length; i++) {
polylines[i].setMap(null);
}
var divided = handlePath(path);
if (typeof divided[0] == 'object') {
for(var i = 0; i < divided.length; i++) {
await runSnapToRoad(divided[i]);
}
} else {
await runSnapToRoad(path);
}
console.log(snappedCoordinates);
drawSnappedPolyline();
}
and:
async function runSnapToRoad(path) {
var path = path.join('|');
await $.get('https://roads.googleapis.com/v1/snapToRoads', {
interpolate: true,
key: apiKey,
path: path,
}, function(data) {
processSnapToRoadResponse(data);
});
}
Thank you in advance.
You are using $.get() to query the Roads API which is an asynchronous call so when you call drawSnappedPolyline() from within your breadCrumbsGrapher function, you most probably don't (yet) have all coordinates in return from your AJAX call(s).
If you have 550 coordinates in your original path, you then know you must call the Roads API 6 times (5 times with 100 points + 1 time with 50 points). You should then be able to set a counter somewhere, to only draw your (complete) Polyline, once you got your 6 responses from the Roads API.
Or you could base that on the length of your final array (compared to the original path) although I don't know what would happen in case some points can't be "snapped".
I am right now using Ol3 to display static image(like http://openlayers.org/en/latest/examples/static-image.html) . Although when I am zoomed in and start panning/dragging i can see white space as seen in example. I don't want to see that.
Is it possible to pan only till the end of image so that white space does not appear?
You can restrict the dragging of the image by restricting its extent.
Here's an inline link to fiddle.
map.on('moveend', function(evt){
console.log(view.getZoom());
if(view.getZoom()<=2){
view.setZoom(3);
}
});
var constrainPan = function() {
console.log("move");
var extents=[0, 0, 1024, 968]
var visible = view.calculateExtent(map.getSize());
var centre = view.getCenter();
var delta;
var adjust = false;
if ((delta = extents[0] - visible[0]) > 0) {
adjust = true;
//console.log(delta);
centre[0] += delta;
} else if ((delta = extents[2] - visible[2]) < 0) {
adjust = true;
centre[0] += delta;
}
if ((delta = extents[1] - visible[1]) > 0) {
adjust = true;
centre[1] += delta;
} else if ((delta = extents[3] - visible[3]) < 0) {
adjust = true;
centre[1] += delta;
}
if (adjust) {
view.setCenter(centre);
}
};
view.on('change:resolution', constrainPan);
view.on('change:center', constrainPan);
The focus moves to the next input field before the event is fired. Can anyone help me find the bug, or figure out how to find it myself?
The goal is to catch the keyup event, verify that it is tab or shift+tab, and then tab as though it were tabbing through a table. When the focus gets to the last input that is visible, the three rows (see fiddle for visual) should move together to reveal hidden inputs. Once to the end of the inputs in that row, the three rows will slide back down to the beginning again, kind of like a carriage return on a typewriter, or tabbing into a different row in a table.
Right now, the tab event is moving just the row that holds the focus, and it is moving it before my script even starts to run. I just need to know why this is happening so that I can research how to resolve it.
Any help you can offer is appreciated. Please let me know if you need more information.
P.S. Using jquery 1.9.1
Link to Fiddle
jQuery(document).ready(function ($) {
// bind listeners to time input fields
//$('.timeBlock').blur(validateHrs);
$('.timeBlock').keyup(function () {
var caller = $(this);
var obj = new LayoutObj(caller);
if (event.keyCode === 9) {
if (event.shiftKey) {
obj.dir = 'prev';
}
obj.navDates();
}
});
// bind listeners to prev/next buttons
$('.previous, .next').on('click', function () {
var str = $(this).attr('class');
var caller = $(this);
var obj = new LayoutObj(caller);
obj.src = 'pg';
if (str === 'previous') {
obj.dir = 'prev';
}
obj.navDates();
});
});
function LayoutObj(input) {
var today = new Date();
var thisMonth = today.getMonth();
var thisDate = today.getDate();
var dateStr = '';
var fullDates = $('.dateNum');
var splitDates = new Array();
this.currIndex = 0; //currIndex defaults to 0
this.todayIndex;
fullDates.each(function (index) {
splitDates[index] = $(this).text().split('/');
});
//traverse the list of dates in the pay period, compare values and stop when/if you find today
for (var i = 0; i < splitDates.length; i++) {
if (thisMonth === (parseInt(splitDates[i][0], 10) - 1) && thisDate === parseInt(splitDates[i][1], 10)) {
thisMonth += 1;
thisMonth += '';
thisDate += '';
if (thisMonth.length < 2) {
dateStr = "0" + thisMonth + "/";
}
else {
dateStr = thisMonth + "/";
}
if (thisDate.length < 2) {
dateStr += "0" + thisDate;
}
else {
dateStr += thisDate;
}
fullDates[i].parentNode.setAttribute('class', 'date today');
this.todayIndex = i;
break;
}
}
//grab all of the lists & the inputs
this.window = $('div.timeViewList');
this.allLists = $('.timeViewList ul');
this.inputs = $('.timeBlock');
//if input`isn't null, set currIndex to match index of caller
if (input !== null) {
this.currIndex = this.inputs.index(input);
}
//else if today is in the pay period, set currIndex to todayIndex
else if (this.todayIndex !== undefined) {
this.currIndex = this.todayIndex;
}
//(else default = 0)
//grab the offsets for the cell, parent, and lists.
this.winOffset = this.window.offset().left;
this.cellOffset = this.inputs.eq(this.currIndex).offset().left;
this.listOffset = this.inputs.offset().left;
//grab the width of a cell, the parent, and lists
this.cellWidth = this.inputs.outerWidth();
this.listWidth = this.inputs.last().offset().left + this.cellWidth - this.inputs.eq(0).offset().left;
this.winWidth = this.window.outerWidth();
//calculate the maximum (left) offset between the lists and the parents
this.offsetMax = (this.listWidth - this.winWidth);
//set default scroll direction as fwd, and default nav as tab
this.dir = 'next';
this.src = 'tab';
//grab the offsets for the cell, parent, and lists.
this.cellOffset = this.inputs.eq(this.currIndex).offset().left;
this.listOffset = this.inputs.eq(0).offset().left;
this.winOffset = this.allLists.parent().offset().left;
//calculate the maximum (left) offset between the lists and the parents
this.offsetMax = (this.listWidth - this.winWidth);
}
LayoutObj.prototype.focusDate = function () {
this.inputs.eq(this.currIndex).focus();
};
LayoutObj.prototype.slideLists = function (num) {
this.listOffset += num;
this.allLists.offset({ left: this.listOffset });
};
LayoutObj.prototype.navDates = function () {
if (!this.inWindow()) {
var slide = 0;
switch (this.src) {
case 'pg':
slide = this.winWidth - this.cellWidth;
break;
case 'tab':
slide = this.cellWidth + 1;
break;
default:
break;
}
if (this.dir === 'next') {
slide = -slide;
}
this.slideLists(slide);
}
this.focusDate();
};
LayoutObj.prototype.inWindow = function () {
//detects if cell intended for focus is visible in the parent div
if ((this.cellOffset > this.winOffset) && ((this.cellOffset + this.cellWidth) < (this.winOffset + this.winWidth))) {
return true;
}
else {
return false;
}
}
All it needed was 'keydown()' instead of 'keyup().'
I'm trying to link two jQuery UI sliders so they'll add up to 100%. I've found the perfect solution for three sliders here on SO, but for some reason I am unable to get the math to add up correctly when modifying this jsFiddle example to strip out the third slider: http://jsfiddle.net/gWbMp/3/
Can anyone help me out in forking this to simply include two sliders instead of three?
Here's the (close) javascript I've ended up with but it's not quite right:
var min = 0;
var max = 100;
$("input").change(function(){
console.log("a");
var index = $(this).attr('class').substring(0,1);
$("#slider_"+ index).slider('value', this.value);
refreshSliders( index - 0 );
});
$('.selector').slider({
animate : true
}, {
min : min
}, {
max : max
}, {
change : function(event, ui) {
totalvalue = $("#slider_1").slider("value") + $("#slider_2").slider("value");
$('.1percent').val($("#slider_1").slider("value"));
$('.2percent').val($("#slider_2").slider("value"));
}
}, {
slide : function(event, ui) {
$('.1percent').val($("#slider_1").slider("value"));
$('.2percent').val($("#slider_2").slider("value"));
}
});
$("#slider_1").slider('value', 10);
$("#slider_2").slider('value', 90);
$('.1percent').val($("#slider_1").slider("value"));
$('.2percent').val($("#slider_2").slider("value"));
function refreshSliders(slidermainin) {
var value1 = $("#slider_1").slider("option", "value");
var value2 = $("#slider_2").slider("option", "value");
var valuechange = (value1 + value2) - 100;
var valuemain = 0, valueother1 = 0;
switch(slidermainin) {
case 1:
slidermain = "#slider_1";
sliderother1 = "#slider_2";
valuemain = value1;
valueother1 = value2;
break;
case 2:
slidermain = "#slider_2";
sliderother1 = "#slider_1";
valuemain = value2;
valueother1 = value1;
break;
}
if (valueother1 === 0) {
if (valueother1 === 0) {
if (valuechange <= 0) {
$(sliderother1).slider('value', valueother1 - (valuechange / 2));
}
} else {
if (valuechange <= 0) {
$(sliderother1).slider('value', valueother1 - (valuechange / 2));
} else {
$(sliderother1).slider('value', valueother1 - valuechange);
}
}
} else {
$(sliderother1).slider('value', valueother1 - (valuechange / 2));
}
}
var bindSliders = function(selector, value) {
$(selector).bind("slidechange slide", function(event, ui) {
event.originalEvent && (event.originalEvent.type == 'mousemove' || event.originalEvent.type == 'mouseup' || event.originalEvent.type == 'keydown') && refreshSliders(value);
});
};
bindSliders("#slider_1", 1);
bindSliders("#slider_2", 2);
I think this can be done much shorter for two sliders
You can rewrite refreshSliders function to calculate second value on the basis of max value
And call it directly on slider "change" and "slide" (or even just second one)
function refreshSliders(thisSlider, ui){
var thisNum = $(thisSlider).attr("id").replace("slider_", "");
var otherNum = (thisNum==1)?2:1;
$('.'+thisNum+'percent').val(ui.value);
if ($("#slider_"+otherNum).slider("value")!=max-ui.value){
$("#slider_"+otherNum).slider("value", max-ui.value);
$('.'+otherNum+'percent').val(max-ui.value);
}
}
have a look at this jsfiddle, i forked it from original one and adjusted a bit: http://jsfiddle.net/paulitto/fBxCm/1/
I am using the following code to tween an movieclip once _global.choiceMade equals 1...
onClipEvent (load) {
import mx.transitions.Tween;
import mx.transitions.easing.*;
}
onClipEvent (enterFrame) {
if (_global.choiceMade == 1) {
var myTweenX:Tween = new Tween(this, "_x", mx.transitions.easing.Back.easeOut, this._x, -349, 0.5, True);
}
}
This is contained within each frame of the main timeline. The first time it runs fine but on the next frame it runs incredibly slow (takes about 12 seconds not 0.5 and is very choppy), if I then return to the first frame and run it again now this time it is extremely slow.
I can't work out why its doing this my CPU stays around 6-15% while its running so it can't be too demanding.
Updated to show rest of my code:
On main timeline have a frame each containing a movieclip. On the timeline of each of these movieclips contains:
//tint an object with a color just like Effect panel
//r, g, b between 0 and 255; amount between 0 and 100
Color.prototype.setTint = function(r, g, b, amount) {
var percent = 100-amount;
var trans = new Object();
trans.ra = trans.ga=trans.ba=percent;
var ratio = amount/100;
trans.rb = r*ratio;
trans.gb = g*ratio;
trans.bb = b*ratio;
this.setTransform(trans);
};//Robert Penner June 2001 - http://www.robertpenner.com
MovieClip.prototype.scaleXY = function(to){
this.onEnterFrame = function(){
this._alpha = to-(to-this._alpha)/1.2;
if(this._alpha > to-1 && this._alpha < to+1){
this._alpha = to;
delete this.onEnterFrame
}
}
}
scoreUpdated = 0;
Answer = 1;
_global.choiceMade = 0;
Buttons = new Array(this.buttonHolder.True, this.buttonHolder.False);
Answers = new Array(this.Correct, this.Wrong);
for (i=0; i<Answers.length; i++) {
Answers[i]._alpha = 0;
}
for (b=0; b<Buttons.length; b++) {
Buttons[b].thisValue = b;
}
In this movieclip there are two movieclip buttons (True and False) containing this code:
onClipEvent (enterFrame) {
this.onRollOver = function() {
this.gotoAndStop("over");
};
this.onRollOut = function() {
this.gotoAndStop("up");
};
this.onPress = function() {
this.gotoAndStop("down");
};
this.onReleaseOutside = function() {
this.gotoAndStop("up");
};
this.onRelease = function() {
this.gotoAndStop("down");
whichChoice = this;
_global.choiceMade = 1;
counter = 0;
};
if (_global.choiceMade == 1) {
this.enabled = false;
this._parent.scoreNow = _global.score;
this._parent.scoreOutOf = (this._parent._parent._currentframe)- 1 + ( _global.choiceMade);
if (thisValue == this._parent._parent.Answer && whichChoice == this) {
myColor = new Color(this);
myColor.setTint(0,204,0,13);
this._parent._parent.Answers[0]._alpha = 100;
this._parent._parent.Answers[0].scaleXY(100);
this.tick.swapDepths(1000);
if (counter == 0) {
_global.score++;
counter++;
}
}
else if (thisValue == this._parent._parent.Answer) {
myColor = new Color(this);
myColor.setTint(0,204,0,13);
this.tick.swapDepths(1000);
}
else if (whichChoice == this) {
this._parent._parent.Answers[1]._alpha = 100;
this._parent._parent.Answers[1].scaleXY(100);
myColor = new Color(this);
myColor.setTint(255,0,0,13);
this.cross.swapDepths(1000);
}
else {
myColor = new Color(this);
myColor.setTint(255,0,0,13);
myColor.setTint(255,0,0,13);
this.cross.swapDepths(1000);
}
}
}
The script at the top is on a movieclip these buttons are contained in called buttonHolder which does what it says, and tweens the buttons across the screen to reveal a next button once an answer is chosen.
From what I can see, as long as you have choiceMade == 1 you create a new effect! which is not ok. because in 1 sec at 15 fps you will have 15 tweens running :(
try yo set choiceMade = 0 or somehting else than 1
onClipEvent (enterFrame)
{
if (_global.choiceMade == 1)
{
_global.choiceMade = -1;
var myTweenX:Tween = new Tween(this, "_x", mx.transitions.easing.Back.easeOut, this._x, -349, 0.5, True);
}
}
Without seeing the rest of your code it's hard to see exactly what's going on. But it looks like you never change choiceMade and it continuously recreates the tween.
onClipEvent (enterFrame) {
if (_global.choiceMade == 1) {
var myTweenX:Tween = new Tween(this, "_x", mx.transitions.easing.Back.easeOut, this._x, -349, 0.5, True);
_global.choiceMade = 0;
}
}