What are the steps to creating milestone lines in a GANTT chart using the highcharts library?

Here is an example of a GANTT chart created with Highcharts: http://jsfiddle.net/gh/get/jquery/1.7.2/highslide-software/highcharts.com/tree/master/samples/highcharts/studies/xrange-series/

I am currently working on implementing a similar example that includes milestones.

This is how my code looks right now:

$(function () {

    /**
     * Highcharts X-range series plugin
     */ (function (H) {
        var defaultPlotOptions = H.getOptions().plotOptions,
            columnType = H.seriesTypes.column,
            each = H.each;

        defaultPlotOptions.xrange = H.merge(defaultPlotOptions.column, {});
        H.seriesTypes.xrange = H.extendClass(columnType, {
            type: 'xrange',
            parallelArrays: ['x', 'x2', 'y'],
            animate: H.seriesTypes.line.prototype.animate,

            // More code here...

        });

        /**
         * Max x2 should be considered in xAxis extremes
         */
        H.wrap(H.Axis.prototype, 'getSeriesExtremes', function (proceed) {
            var axis = this,
                dataMax = Number.MIN_VALUE;

            proceed.call(this);
            if (this.isXAxis) {
                each(this.series, function (series) {
                    // Logic for adjusting axis extremes based on x2 values
                });
                if (dataMax > Number.MIN_VALUE) {
                    axis.dataMax = dataMax;
                }
            }
        });
    }(Highcharts));

    // THE CHART
    $('#container').highcharts({
        chart: {
            type: 'xrange'
        },
        title: {
            text: 'Highcharts X-range study'
        },
        // Chart configuration options...
        
    });
});

In the edit, I have provided a mock-up example of the desired look:
https://i.sstatic.net/W80lj.jpg
I intend to pull lines from a Sharepoint list to display bank holidays and freeze periods on the GANTT chart.

Answer №1

To efficiently identify bank holidays or weekends, it is advised to utilize plotBands in the chart settings. For highlighting specific milestones, plotLines can be employed. Below is a demonstration showcasing both functionalities implemented in your chart.

http://jsfiddle.net/u8zvpaum/

$(function () {

/**
 * Highcharts X-range series plugin
 */ (function (H) {
    var defaultPlotOptions = H.getOptions().plotOptions,
        columnType = H.seriesTypes.column,
        each = H.each;

    defaultPlotOptions.xrange = H.merge(defaultPlotOptions.column, {});
    H.seriesTypes.xrange = H.extendClass(columnType, {
        type: 'xrange',
        parallelArrays: ['x', 'x2', 'y'],
        animate: H.seriesTypes.line.prototype.animate,

        /**
         * Utilize the column series metrics with swapped axes for enhanced functionality.
         */
        getColumnMetrics: function () {
            var metrics,
            chart = this.chart,
                swapAxes = function () {
                    each(chart.series, function (s) {
                        var xAxis = s.xAxis;
                        s.xAxis = s.yAxis;
                        s.yAxis = xAxis;
                    });
                };

            swapAxes();

            this.yAxis.closestPointRange = 1;
            metrics = columnType.prototype.getColumnMetrics.call(this);

            swapAxes();

            return metrics;
        },
        translate: function () {
            columnType.prototype.translate.apply(this, arguments);
            var series = this,
                xAxis = series.xAxis,
                yAxis = series.yAxis,
                metrics = series.columnMetrics;

            H.each(series.points, function (point) {
                barWidth = xAxis.translate(H.pick(point.x2, point.x + (point.len || 0))) - point.plotX;
                point.shapeArgs = {
                    x: point.plotX,
                    y: point.plotY + metrics.offset,
                    width: barWidth,
                    height: metrics.width
                };
                point.tooltipPos[0] += barWidth / 2;
                point.tooltipPos[1] -= metrics.width / 2;
            });
        }
    });

    /**
     * Consider max value of x2 in xAxis extremes
     */
    H.wrap(H.Axis.prototype, 'getSeriesExtremes', function (proceed) {
        var axis = this,
          dataMax = Number.MIN_VALUE;

        proceed.call(this);
        if (this.isXAxis) {
            each(this.series, function (series) {
                each(series.x2Data || [], function (val) {
                    if (val > dataMax) {
                        dataMax = val;
                    }
                });
            });
            if (dataMax > Number.MIN_VALUE) {
                axis.dataMax = dataMax;
            }
        }
    });
}(Highcharts));


// CHART SETUP
$('#container').highcharts({
    chart: {
        type: 'xrange'
    },
    title: {
        text: 'Highcharts X-range study'
    },
    xAxis: {
        type: 'datetime',

        /* START plotBands AND plotLines EDITS */

        pointInterval: 24 * 3600 * 1000, // one day,   
        plotLines: [{ // mark milestone date with vertical line
            color: '#F45B5B',
            width: '2',
            value: Date.UTC(2014, 11, 6),
            label: {
                useHTML: true,
                text: '<span style="color:#F45B5B"">Dec 6, 2014</span>'
            }
        }],
        plotBands: [{ // visualize the weekend or other range of dates
            from: Date.UTC(2014, 11, 2),
            to: Date.UTC(2014, 11, 5),
            color: 'rgba(68, 170, 213, .2)'
        }]

        /* END plotBands AND plotLines EDITS */

    },
    yAxis: {
        title: '',
        categories: ['Prototyping', 'Development', 'Testing'],
        min: 0,
        max: 2
    },
    series: [{
        name: 'Project 1',
        borderRadius: 5,
        pointWidth: 10,
        data: [{
            x: Date.UTC(2014, 11, 1),
            x2: Date.UTC(2014, 11, 2),
            y: 0
        }, {
            x: Date.UTC(2014, 11, 2),
            x2: Date.UTC(2014, 11, 5),
            y: 1
        }, {
            x: Date.UTC(2014, 11, 8),
            x2: Date.UTC(2014, 11, 9),
            y: 2
        }, {
            x: Date.UTC(2014, 11, 9),
            x2: Date.UTC(2014, 11, 19),
            y: 1
        }, {
            x: Date.UTC(2014, 11, 10),
            x2: Date.UTC(2014, 11, 23),
            y: 2
        }]
    }]

});
});

Answer №2

Thank you Joshwa for providing an excellent response.

If we consider the scenario of having 2 projects, each consisting of 3 sections ('Prototyping', 'Development', 'Testing'), we would represent them with 2 bars stacked on top of each other for easy comparison.

$(function () {

/**
 * Highcharts X-range series plugin
 */ (function (H) {
    var defaultPlotOptions = H.getOptions().plotOptions,
        columnType = H.seriesTypes.column,
        each = H.each;

    defaultPlotOptions.xrange = H.merge(defaultPlotOptions.column, {});
    H.seriesTypes.xrange = H.extendClass(columnType, {
        type: 'xrange',
        parallelArrays: ['x', 'x2', 'y'],
        animate: H.seriesTypes.line.prototype.animate,

        /**
         * Taking inspiration from the column series metrics but with exchanged axes to leverage attributes like groupPadding, grouping, pointWidth etc.
         */
        getColumnMetrics: function () {
            var metrics,
                chart = this.chart,
                swapAxes = function () {
                    each(chart.series, function (s) {
                        var xAxis = s.xAxis;
                        s.xAxis = s.yAxis;
                        s.yAxis = xAxis;
                    });
                };

            swapAxes();

            this.yAxis.closestPointRange = 1;
            metrics = columnType.prototype.getColumnMetrics.call(this);

            swapAxes();

            return metrics;
        },
        translate: function () {
            columnType.prototype.translate.apply(this, arguments);
            var series = this,
                xAxis = series.xAxis,
                yAxis = series.yAxis,
                metrics = series.columnMetrics;

            H.each(series.points, function (point) {
                barWidth = xAxis.translate(H.pick(point.x2, point.x + (point.len || 0))) - point.plotX;
                point.shapeArgs = {
                    x: point.plotX,
                    y: point.plotY + metrics.offset,
                    width: barWidth,
                    height: metrics.width
                };
                point.tooltipPos[0] += barWidth / 2;
                point.tooltipPos[1] -= metrics.width / 2;
            });
        }
    });

    /**
     * The maximum x2 value should be factored into xAxis extremes
     */
    H.wrap(H.Axis.prototype, 'getSeriesExtremes', function (proceed) {
        var axis = this,
            dataMax = Number.MIN_VALUE;

        proceed.call(this);
        if (this.isXAxis) {
            each(this.series, function (series) {
                each(series.x2Data || [], function (val) {
                    if (val > dataMax) {
                        dataMax = val;
                    }
                });
            });
            if (dataMax > Number.MIN_VALUE) {
                axis.dataMax = dataMax;
            }
        }
    });
}(Highcharts));


// THE CHART
$('#container').highcharts({
    chart: {
        type: 'xrange'
    },
    title: {
        text: 'Analysis using Highcharts X-range'
    },
    xAxis: {
        type: 'datetime',
        pointInterval: 24 * 3600 * 1000, // representing one day   
    },
    yAxis: {
        title: '',
        categories: ['Prototyping', 'Development', 'Testing'],
        min: 0,
        max: 2
    },
    series: [{
        name: 'Project 1',
        borderRadius: 5,
        pointWidth: 10,
        data: [{
            x: Date.UTC(2014, 11, 1),
            x2: Date.UTC(2014, 11, 2),
            y: 0
        },{
            x: Date.UTC(2014, 11, 3),
            x2: Date.UTC(2014, 11, 9),
            y: 0
        }, {
            x: Date.UTC(2014, 11, 2),
            x2: Date.UTC(2014, 11, 5),
            y: 1
        }, {
            x: Date.UTC(2014, 11, 1),
            x2: Date.UTC(2014, 11, 9),
            y: 2
        }, {
            x: Date.UTC(2014, 11, 9),
            x2: Date.UTC(2014, 11, 19),
            y: 1
        }, {
            x: Date.UTC(2014, 11, 10),
            x2: Date.UTC(2014, 11, 23),
            y: 2
        }]
    },{
        name: 'Project 2',
        borderRadius: 5,
        pointWidth: 10,
        data: [{
            x: Date.UTC(2014, 11, 1),
            x2: Date.UTC(2014, 11, 2),
            y: 0
        }, {
            x: Date.UTC(2014, 11, 2),
            x2: Date.UTC(2014, 11, 5),
            y: 1
        }, {
            x: Date.UTC(2014, 11, 8),
            x2: Date.UTC(2014, 11, 9),
            y: 2
        }, {
            x: Date.UTC(2014, 11, 9),
            x2: Date.UTC(2014, 11, 19),
            y: 1
        }, {
            x: Date.UTC(2014, 11, 13),
            x2: Date.UTC(2014, 11, 18),
            y: 2
        }]
    }]

});
});

Answer №3

If you want to display multiple series on a chart, including milestones data, simply add a second axis. Here is an example where a line chart is overlaid on top of a column chart: View Example

$(function () {
    $('#container').highcharts({
        chart: {
            zoomType: 'xy'
        },
        title: {
            text: 'Average Monthly Temperature and Rainfall in Tokyo'
        },
        subtitle: {
            text: 'Source: WorldClimate.com'
        },
        xAxis: [{
            categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
            crosshair: true
        }],
        yAxis: [{ // Primary yAxis
            labels: {
                format: '{value}°C',
                style: {
                    color: Highcharts.getOptions().colors[1]
                }
            },
            title: {
                text: 'Temperature',
                style: {
                    color: Highcharts.getOptions().colors[1]
                }
            }
        }, { // Secondary yAxis
            title: {
                text: 'Rainfall',
                style: {
                    color: Highcharts.getOptions().colors[0]
                }
            },
            labels: {
                format: '{value} mm',
                style: {
                    color: Highcharts.getOptions().colors[0]
                }
            },
            opposite: true
        }],
        tooltip: {
            shared: true
        },
        legend: {
            layout: 'vertical',
            align: 'left',
            x: 120,
            verticalAlign: 'top',
            y: 100,
            floating: true,
            backgroundColor: (Highcharts.theme && Highcharts.theme.legendBackgroundColor) || '#FFFFFF'
        },
        series: [{
            name: 'Rainfall',
            type: 'column',
            yAxis: 1,
            data: [49.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4],
            tooltip: {
                valueSuffix: ' mm'
            }

        }, {
            name: 'Temperature',
            type: 'spline',
            data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6],
            tooltip: {
                valueSuffix: '°C'
            }
        }]
    });
});

Answer №4

To create lines on the x-axis, simply utilize the plotLines feature.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Button missing post ajax loading

I've encountered a problem with my code that populates a table and contains buttons, which trigger an AJAX load. The content is loaded into a DIV with the ID 'DIV1', but when I try to access the buttons in that DIV1 by clicking on them, they ...

Search a location database using the user's current coordinates

Currently, I am working on a project that involves a database containing locations specified by longitude and latitude. Upon loading the index page, my goal is to fetch the user's location and then identify every point within a certain distance radius ...

The event handler is not being triggered when selecting the tag

Currently working on a project where I am utilizing vanilla HTML/CSS/JS. My goal is to hide all items on a page initially, set a default option in a select tag, display only the element with the selected ID, and allow the user to choose different time peri ...

Encounters a fault while processing the php output

Displaying an error in the query The browser is showing the correct answer. $.ajax({ type: "POST", url: url, async: true, contentType: " charset=utf-8", dataType: "XMLHttpRequest", success: func ...

React Native encounters issues with removing the reference to the callback attribute upon unmounting

Utilizing a component that I place in an array (of various sizes) and controlling individual components through refs, while adding refs to an object to keep track of each separately. constructor(props){ super(props); this.stamps = []; this.get ...

Ensuring form field accuracy through server-side validation using Ajax - Mastering the art of Ajax

I am in need of implementing Ajax to validate my form fields, currently I have a javascript validation set up. Is it possible to reuse my existing javascript code and integrate Ajax for form validation? HTML Form <form method="post" action="ajax/valid ...

Height and Width Dilemma in Visuals

Can you help with changing image resolution using jQuery? I have a background image applied to the body using CSS/PHP. My goal is to make the image fill the entire window by applying the screen height and width, without using repeat style. The code I am c ...

The event listener fails to function properly in asynchronous JavaScript

The reason for the error is due to the asynchronous nature of the code, where the button is not loaded yet. Is there a solution to this issue? It's difficult to explain in words but essentially, when the window is loaded, the button is not present as ...

Customizing the size of the selectInput widget in R/Shiny

I'm trying to adjust the size of the selectInput() widget in shiny using selectize.js. I've attempted to tweak various attributes listed on this page (https://github.com/selectize/selectize.js/blob/master/dist/css/selectize.css) but I can't ...

"Hiding elements with display: none still occupies space on the

For some reason, my Pagination nav is set to display:none but causing an empty space where it shouldn't be. Even after trying overflow:hidden, visibility:none, height:0, the issue persists. I suspect it might have something to do with relative and ab ...

iPad2 always displays UIWebView in 'portrait' orientation

I have a sophisticated HTML5 website that includes JavaScript. It is displayed in a UIWebView. The JavaScript on the page is supposed to determine whether the iPad is in Portrait or Landscape mode. Unfortunately, I have encountered an issue. Regardless of ...

Is it possible for ng-change to invoke another controller?

Can someone help me with the following HTML code? <div ng-controller="select"> <select ng-options="n for n in names" ng-model="selected.item" ng-change="change()"> </select> </div> <div ng-co ...

Is there a way to use JavaScript/jQuery to randomly resize images every time the page loads

Instead of generating thumbnails for my images, I am interested in displaying the original images on my page. However, I still need to resize these images for optimal viewing. Instead of applying a fixed width of 200px, I would like each image to be displa ...

"Clicking on one item in the Bootstrap submenu doesn't close the other items,

I have this Javascript code snippet that deals with expanding and collapsing submenu items: <script type="text/javascript> jQuery(function(){ jQuery(".dropdown-menu > li > a.trigger").on("click",function(e){ var current ...

What is the best way to implement Redux within Next.js 13?

Currently, I am using Next JS 13 with Redux. In Next.js 12, I was able to wrap my entire application with Provider inside ./pages/_app. However, how can I achieve this in Next JS 13? Here is the code from my layout.js: import "../styles/globals.css&q ...

Choose JSON information and modify it utilizing NODE.js with identical data

Feeling stuck.. I have a JSON file with some data and I need to manipulate it. Take a look at my JSON structure: [{ "method": "GET", "path": "/", "aliases": "", "name": "rootPath", "handler": "generatedApps/avion01/actions.HomeHandler" }, { "method": "GET ...

I am encountering an issue with identifying a directory in Node.js

This is my HTML code <div id="done_work_1" class="logo-slide-track"> </div> <script>$.ajax({ url: "/static/home/done/", success: function (data) { $(data).find("a").attr("href&q ...

Dynamically enhance dropdownlist options in webforms using JavaScript

I am currently working on an asp.net webforms project where I have a page with 2 dropdown lists. Whenever a user selects an option from ddl1, I want to dynamically add that selected element to ddl2 using JavaScript. Initially, when the page loads, ddl1 h ...

Can I use MuiThemeProvider in my component without any restrictions?

I have a unique component called View: import React from 'react'; import { AppBar, Toolbar } from 'material-ui'; import { Typography } from 'material-ui'; import { MuiThemeProvider, createMuiTheme } from 'material-ui/st ...

Issues encountered when executing unit tests using karma

I encountered these issues in the logs. Seeking advice on how to proceed. Thank you. I've attempted uninstalling and reinstalling phantomjs, clearing out my node modules and bower component directories. Everything was functioning as expected before, a ...