我正在努力解决显示每个x包含最大值的额外系列的问题。像这样:
我能够解析所有系列以聚合点,并找到交叉点以包含它们。目前我的问题是交叉点(A)低于其中一个系列。 (小提琴中的解决方案A)我正在考虑计算线并检查点A是否属于该线。这将解决方案,但会导致B点的问题,它不属于计算线,但应包括在系列中。
有人能指出我正确的方向吗?
var chart = Highcharts.chart('container', {
chart: {
type: 'area',
zoomType: 'x',
backgroundColor:null,
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: {
month: '%b %e',
year: '%b'
},
},
plotOptions: {
series: {
fillOpacity: 0.1,
marker: {
enabled: false
}
}
}
});
chart.addSeries({"id":0,"connectNulls":true,"data":[[1524469020000,30],[1524469080000,30],[1524469140000,30],[1524469200000,30],[1524469260000,30],[1524469320000,30],[1524469380000,30],[1524469440000,30],[1524469500000,30],[1524469560000,58],[1524469620000,4],[1524469680000,4],[1524469740000,4],[1524469800000,4],[1524469860000,4],[1524469920000,4],[1524469980000,4],[1524470040000,4],[1524470100000,4],[1524470160000,4],[1524470220000,4],[1524470280000,4],[1524470340000,4],[1524470400000,4],[1524470460000,4],[1524470520000,22],[1524470580000,22],[1524470640000,22],[1524470700000,22]],"name":"Serie A","color":"#30e430","yAxis":0});
chart.addSeries({"id":1,"connectNulls":true,"data":[[1524469020000,35],[1524469080000,35],[1524469140000,35],[1524469200000,35],[1524469260000,35],[1524469320000,35],[1524469380000,35],[1524469440000,35],[1524469500000,35],[1524469560000,25],[1524469620000,25],[1524469680000,25],[1524469740000,25],[1524469800000,25],[1524469860000,25],[1524469920000,25],[1524469980000,59],[1524470040000,59],[1524470100000,59],[1524470160000,59],[1524470220000,59],[1524470280000,59],[1524470340000,59],[1524470400000,59],[1524470460000,59],[1524470520000,59],[1524470580000,59],[1524470640000,59],[1524470700000,59]],"name":"Serie B","color":"#0cb5ed","yAxis":0});
chart.addSeries({"id":2,"connectNulls":true,"data":[[1524469020000,18],[1524469080000,18],[1524469140000,18],[1524469200000,18],[1524469260000,18],[1524469320000,18],[1524469380000,18],[1524469440000,18],[1524469500000,18],[1524469560000,18],[1524469620000,18],[1524469680000,18],[1524469740000,18],[1524469800000,18],[1524469860000,18],[1524469920000,18],[1524469980000,18],[1524470040000,18],[1524470100000,18],[1524470160000,18],[1524470220000,18],[1524470280000,18],[1524470340000,18],[1524470400000,18],[1524470460000,18],[1524470520000,18],[1524470580000,18],[1524470640000,18],[1524470700000,18]],"name":"Serie C","color":"#e8ad23","yAxis":0});
$('#button').click(function () {
var results = getChartPointValues();
var data = prepareSummarySeries(results);
//clean previously added virutal series
for(var i in chart.series){
var serie = chart.series[i];
if(serie.userOptions.is_virtual == true)
serie.remove();
}
chart.addSeries({
id: 'virtual_max_b',
is_virtual: true,
'connectNulls': true,
'data' : data.max_b,
'name' : 'Solution A',
'color' : '#000',
'yAxis': 0,
});
chart.addSeries({
id: 'virtual_max_a',
is_virtual: true,
'connectNulls': true,
'data' : data.max_a,
'name' : 'Base Solution',
'color' : '#ff0000',
'yAxis': 0,
});
});
/*
* Calculate max values for every point
*/
var prepareSummarySeries = function(data){
var tmp_keys = Object.keys(data); ///sort
tmp_keys = tmp_keys.sort();
var counter = tmp_keys.length;
var results = {
'max_a': [],
'max_b': [],
};
for(var k = 0; k < counter; k++){
var key = tmp_keys[k];
var x_pos = parseFloat(key);
if(x_pos % 1 !== 0)
{
var prev_point = results.max_b.slice(-1)[0];
var current_point = [x_pos, data[key][0]];
var next_point = [ parseFloat(tmp_keys[k+1]), Math.max.apply(null, data[tmp_keys[k+1]] )];
if( checkIfPointBelongsToChart(prev_point, current_point, next_point) ){
results.max_b.push([ x_pos, current_point[1] ]);
}
} else {
results.max_b.push([ x_pos, Math.max.apply(null, data[key]) ]);
}
results.max_a.push([ x_pos, Math.max.apply(null, data[key]) ]);
}
return results;
};
var get_line_intersection = function(p0,p1,p2,p3){
var p0_x = p0.x;
var p0_y = p0.y;
var p1_x = p1.x;
var p1_y = p1.y;
var p2_x = p2.x;
var p2_y = p2.y;
var p3_x = p3.x;
var p3_y = p3.y;
var s1_x, s1_y, s2_x, s2_y;
s1_x = p1_x - p0_x; s1_y = p1_y - p0_y;
s2_x = p3_x - p2_x; s2_y = p3_y - p2_y;
var s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
var t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
{
return [p0_x + (t * s1_x),p0_y + (t * s1_y)];
}
return false;
};
var checkIfPointBelongsToChart = function(prev_point, current_point, next_point)
{
var slope = (next_point[1] - prev_point[1]) / (next_point[0] - prev_point[0]);
var b = prev_point[1] - slope * prev_point[0];
var tmp_y = slope * current_point[0] + b;
return (tmp_y == current_point[1])? true : false;
};
// create array with every x point with every possible y value
// im not taking only max here bc later we could need min instead of max
var getChartPointValues = function(){
var results = {}
var self = this;
var points = [];
var checked_series = [];
for(var k =0; k < chart.series.length; k++)
{
var entry = chart.series[k];
if(entry.userOptions.is_virtual == true || entry.visible == false)
continue;
var s1_points = entry.data;
var c1 = s1_points.length;
//add all points from serie
for(var i = 0; i < c1; i++)
{
if(s1_points[i] == undefined || !s1_points[i].isInside)
continue;
if(points[s1_points[i].x] == undefined){
points[s1_points[i].x] = [];
}
points[s1_points[i].x].push(s1_points[i].y);
}
//check all points in all charts for crossing points
for(var s in chart.series){
var serie = chart.series[s];
if(serie.userOptions.is_virtual == true || serie.visible == false)
continue;
//skip serie if combination of series was already checked
var current_check = entry.userOptions.id + '_' + serie.userOptions.id;
if(checked_series.indexOf(current_check) != -1 || serie.userOptions.is_virtual == true)
continue;
checked_series.push(current_check);
checked_series.push(serie.userOptions.id + '_' + entry.userOptions.id);
if(serie.index != entry.index){
var s2_points = serie.data;
var c2 = s2_points.length;
for(var i = 0; i < c1; i++)
{
if(s1_points[i] == undefined || !s1_points[i].isInside)
continue;
for(var j = 0; j < c2; j++)
{
if(s2_points[j] == undefined || !s2_points[j].isInside)
continue;
var cross = [];
if(s1_points[i-1] != undefined && s2_points[j-1] != undefined){
if(cross = get_line_intersection(s1_points[i-1], s1_points[i], s2_points[j-1], s2_points[j])){
if(points[cross[0]] == undefined){
points[cross[0]] = [];
}
points[cross[0]].push(cross[1])
}
}
}
}
}
};
};
return points;
}
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<button id="button">Add Maximum series</button>
<div id="container" style="height: 400px"></div>
答案 0 :(得分:2)
我试图用稍微不同的方法来解决这个问题。它的要点是:
这样做有一点需要注意,稀疏系列有能力破坏图形,如下所示:https://jsfiddle.net/ewolden/3koe86sx/15/ 解决这个问题会很繁琐,要求你找到所有的x点,对于没有所有点的线,要填写它们的值。
话虽如此,只要该系列的点数大致相同,这应该会很好。
var get_line_intersection = function(p0, p1, p2, p3) {
var p0_x = p0.x;
var p0_y = p0.y;
var p1_x = p1.x;
var p1_y = p1.y;
var p2_x = p2.x;
var p2_y = p2.y;
var p3_x = p3.x;
var p3_y = p3.y;
var s1_x, s1_y, s2_x, s2_y;
s1_x = p1_x - p0_x;
s1_y = p1_y - p0_y;
s2_x = p3_x - p2_x;
s2_y = p3_y - p2_y;
var s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
var t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
return [p0_x + (t * s1_x), p0_y + (t * s1_y)];
}
return false;
};
//Gets the next point
function getNextPoint(series, current_x, current_y) {
nextPoint = {
next_x: 0,
next_y: 0,
x: 0,
y: -1,
prev_x: 0,
prev_y: 0
}
for (var i = 0; i < series.length; i++) {
for (var j = 1; j < series[i].length; j++) {
if (series[i][j].x > current_x) { //Goes one step past current timestamp
if (series[i][j].y > nextPoint.y || nextPoint.y == -1) { //Checks that this is the max number, could be changed to find min as well
if (j < series[i].length - 1) { //Checks if this is the last point (to avoid going past last index)
nextPoint = {
next_x: series[i][j + 1].x,
next_y: series[i][j + 1].y,
x: series[i][j].x,
y: series[i][j].y,
prev_x: series[i][j - 1].x,
prev_y: series[i][j - 1].y,
}
} else {
nextPoint = {
x: series[i][j].x,
y: series[i][j].y,
prev_x: series[i][j - 1].x,
prev_y: series[i][j - 1].y,
}
}
}
break;
}
}
}
return nextPoint
}
function getAllSeries(chart) {
var allSeries = []
for (var i = 0; i < chart.series.length; i++) {
allSeries.push(chart.series[i].data)
}
return allSeries
}
var chart = Highcharts.chart('container', {
chart: {
type: 'area',
zoomType: 'x',
backgroundColor: null,
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: {
month: '%b %e',
year: '%b'
},
},
plotOptions: {
series: {
fillOpacity: 0.1,
marker: {
enabled: false
}
}
}
});
chart.addSeries({
"id": 0,
"connectNulls": true,
"data": [
[1524469020000, 30],
[1524469080000, 30],
[1524469140000, 30],
[1524469200000, 30],
[1524469260000, 30],
[1524469320000, 30],
[1524469380000, 30],
[1524469440000, 30],
[1524469500000, 30],
[1524469560000, 58],
[1524469620000, 4],
[1524469680000, 4],
[1524469740000, 4],
[1524469800000, 4],
[1524469860000, 4],
[1524469920000, 4],
[1524469980000, 4],
[1524470040000, 4],
[1524470100000, 4],
[1524470160000, 4],
[1524470220000, 4],
[1524470280000, 4],
[1524470340000, 4],
[1524470400000, 4],
[1524470460000, 4],
[1524470520000, 22],
[1524470580000, 22],
[1524470640000, 22],
[1524470700000, 22]
],
"name": "Serie A",
"color": "#30e430",
"yAxis": 0
});
chart.addSeries({
"id": 1,
"connectNulls": true,
"data": [
[1524469020000, 35],
[1524469080000, 35],
[1524469140000, 35],
[1524469200000, 35],
[1524469260000, 35],
[1524469320000, 35],
[1524469380000, 35],
[1524469440000, 35],
[1524469500000, 35],
[1524469560000, 25],
[1524469620000, 25],
[1524469680000, 25],
[1524469740000, 25],
[1524469800000, 25],
[1524469860000, 25],
[1524469920000, 25],
[1524469980000, 59],
[1524470040000, 59],
[1524470100000, 59],
[1524470160000, 59],
[1524470220000, 59],
[1524470280000, 59],
[1524470340000, 59],
[1524470400000, 59],
[1524470460000, 59],
[1524470520000, 59],
[1524470580000, 59],
[1524470640000, 59],
[1524470700000, 59]
],
"name": "Serie B",
"color": "#0cb5ed",
"yAxis": 0
});
chart.addSeries({
"id": 2,
"connectNulls": true,
"data": [
[1524469020000, 18],
[1524469080000, 18],
[1524469140000, 18],
[1524469200000, 18],
[1524469260000, 18],
[1524469320000, 18],
[1524469380000, 18],
[1524469440000, 18],
[1524469500000, 18],
[1524469560000, 18],
[1524469620000, 18],
[1524469680000, 18],
[1524469740000, 18],
[1524469800000, 18],
[1524469860000, 18],
[1524469920000, 18],
[1524469980000, 18],
[1524470040000, 18],
[1524470100000, 18],
[1524470130000, 80],
[1524470160000, 18],
[1524470220000, 18],
[1524470280000, 18],
[1524470340000, 18],
[1524470400000, 18],
[1524470460000, 18],
[1524470520000, 18],
[1524470580000, 18],
[1524470640000, 18],
[1524470700000, 18]
],
"name": "Serie C",
"color": "#e8ad23",
"yAxis": 0
});
$('#button').click(function() {
series = getAllSeries(chart)
var currentPoint = {
next_x: 0,
next_y: 0,
x: -1,
y: 0,
prev_x: 0,
prev_y: 0
}
var max_x = 0;
//finds the first point
for (var i = 0; i < series.length; i++) {
if (currentPoint.y < series[i][0].y || currentPoint.x == -1) { //makes sure this is the largest point
currentPoint = {
prev_x: series[i][0].x,
prev_y: series[i][0].y,
x: series[i][0].x,
y: series[i][0].y,
next_x: series[i][1].x,
next_y: series[i][1].y
}
}
if (max_x < series[i][series[i].length - 1].x) {
max_x = series[i][series[i].length - 1].x;
}
}
result = []
result.push({ //since the first point comes from the above code, we need to add it explicitly
x: currentPoint.x,
y: currentPoint.y
})
while (currentPoint.x != max_x) { //loop through all points
nextPoint = getNextPoint(series, currentPoint.x, currentPoint.y);
let intersect = get_line_intersection({
x: nextPoint.prev_x,
y: nextPoint.prev_y
}, {
x: nextPoint.x,
y: nextPoint.y
}, {
x: currentPoint.x,
y: currentPoint.y
}, {
x: currentPoint.next_x,
y: currentPoint.next_y
})
if (intersect != false) { //if there is an intersect point, make sure to add it
result.push({
x: intersect[0],
y: intersect[1]
})
}
result.push({
x: nextPoint.x,
y: nextPoint.y
});
currentPoint = nextPoint
}
chart.addSeries({
name: 'Max Points',
lineColor: 'red',
//dashStyle: 'LongDash',
data: result
})
})
&#13;
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<button id="button">Add Maximum series</button>
<div id="container" style="height: 400px"></div>
&#13;
jsfiddle示例: https://jsfiddle.net/ewolden/3koe86sx/14/