Web app and RESTful services using MEAN stack and back end with Spark + Scala

I have developed a MEAN stack application which shows the San Francisco Food inspections details.
Source: Food Inspection(Use Food Inspections – LIVES Standard)

I have used Spark, Scala, MongoDB, NodeJs, AngularJs to do this.

My spark job reads the input CSV data contains food inspection details and processes it and stores the data in MongoDB as collections. I have allFoodInspection and filterFoodInspection collections here. The first one has all the data and the second one has the business name, the unique risk category and number of risk’s committed.

My MEAN stack REST layer reads the data from Mongodb and processes and exposes the data and the Web Layer uses the data and display it and use the data for drawing a chart.

Let us see how we can execute this.

  1. Follow the steps given in this post to install scala, sbt and spark in your machine if you are using Ubuntu. Refer my another post to know how to install these. How to install Scala, SBT and Spark in Ubuntu
  2. Clone the git repository https://github.com/dkbalachandar/sf-food-inspection-spark.git and go inside of sf-inspection-spark folder and run ‘sbt assembly’ to create a far jar with all the dependencies.Here I have used spark 2.0.2 and scala 2.11.8 (Spark 2.0.2 version is compatible with scala 2.11.x version).
    If you don’t use the compatible version then you will end up with lots of errors.
  3. Copy the ../sf-food-inspection-spark/target/scala-2.11/sf-food-inspection-spark-assembly-1.0.jar to /usr/local/spark folder
  4. Download Food_Inspections_-_LIVES_Standard.csv from https://data.sfgov.org/browse?q=food+inspection and move it to /usr/local/spark folder
  5. Install Mongodb with the below steps
    
     sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6
     echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list
     sudo apt-get update
     sudo apt-get install -y mongodb-org
     sudo service mongod start
    
    

    Run the spark job with the below command

    
    bin/spark-submit --class com.spark.SFFoodInspectionAnalysis --master local sf-food-inspection-spark-assembly-1.0.jar file:///usr/local/spark/Food_Inspections_-_LIVES_Standard.csv 
    
    
  6. Then check the Mongo Db and check the collections and make sure that the data are getting inserted and availableOpen up a terminal window and type ‘mongo’ and enter. It will open a shell window. Then use the below commands to verify the data
    
      show dbs
      use sfFood
      show collections
      db.allFoodInspection.find()
      db.filterFoodInspection.find()
    
    
  7. Clone the git repository https://github.com/dkbalachandar/sf-food-inspection-web.git and go inside of sf-food-inspection-web folder, then run below commands to build and run the application
    
      npm install
      node server.js
    
    
  8. Open the http://localhost:8081 and check the page. I have used the data and created a table and display a chart with the details.

Please are the some of the screenshots taken from the application

npm install – Cannot find module ‘semver’ issue

I have recently got an error “Cannot find module ‘semver'” on my Ubuntu VM when doing npm install.

+ npm install
...
...
module.js:340
    throw err;
          ^
Error: Cannot find module 'semver'

 

I have tried various things but nothing worked out. Then I have done the below things and it resolved my issue.

sudo apt-get remove nodejs

sudo apt-get remove npm

sudo apt-get update 

sudo apt-get install nodejs

sudo apt-get install npm

sudo ln -s /usr/bin/nodejs /usr/bin/node

npm install

 

Creating a web application to show the health statistics data with NodeJs + Express Js + Google Chart

I had recently participated in a Hackathon conducted by DevPost and created a NodeJs web application and successfully submitted it.

Even though I did not win any prize apart from Participation Prizes which includes T-Shirt and Openshift Credit, It was a good experience as it was my first hackathon and also I learned to create video and draw chart with Google Chart

Inspiration

Basically, the statistics should be displayed in an interactive manner with maps and charts. Otherwise, it’s somewhat difficult to interpret the data by going through each and every row.

While going through the statistics data from kff.org/state-category/health-status/, found out that the data are displayed by adults, by gender and by race. Even though Kff website displays the data in a MAP but lacks to display all the information at a time. Hence I have decided to create an application which has to show the health statistics data in a map and upon selection of any state, the gender wise and race wise stats should be displayed in a pie chart underneath that map. In this way, we can view the information at ease.

What it does

It’s a Node JS web application shows the various health statistics data.

The below statistics are getting displayed here
Smoking
Asthma
Poor mental health
Obesity

For each category, the dataset contains three CSV files. The adults statistics csv file contains the adults percentage per each US states, the gender statistics csv file contains the gender wise percentage per each US states and the race statistics csv file contains the race wise percentage per each US states. So this application displays the US states map with adults percentage stats and upon selection of any of the state will display the statistics by gender and race for that selected state.

Data Source: KCMU analysis of the Centers for Disease Control and Prevention (CDC)’s Behavioral Risk Factor Surveillance System(Year:2014)

How I built it

I have used Node JS for reading the CSV files and exposing couple of RESTful web services to provide those statistics content to front end and used javascript to get the data from NodeJs server and render it with Google chart (Geochart and Pie chart).

The source code is found at https://github.com/dkbalachandar/health-stats-application

Let me explain how my code works.

The server.js is the starting point of my application. I have used express Js here and initialize it and make it to be run on 8080 port then, use routes.js to initialize the routes which is nothing but RESTful services.

server.js



// server.js

// set up ========================
var express = require('express');
var app = express();                        // create our app w/ express
var morgan = require('morgan');                // log requests to the console (express4)
var bodyParser = require('body-parser');         // pull information from HTML POST (express4)
var methodOverride = require('method-override'); // simulate DELETE and PUT (express4)
var port = process.env.PORT || 8080;         // set the port


// configuration ===============================================================
app.use(express.static(__dirname + '/public'));                 // set the static files location /public/img will be /img for users
app.use(morgan('dev'));                                         // log every request to the console
app.use(bodyParser.urlencoded({'extended': 'true    '}));            // parse application/x-www-form-urlencoded
app.use(bodyParser.json());                                     // parse application/json
app.use(bodyParser.json({type: 'application/vnd.api+json'})); // parse application/vnd.api+json as json
app.use(methodOverride());

// routes ======================================================================
require('./app/routes.js')(app);

// listen (start app with node server.js) ======================================
app.listen(port);
console.log("App listening on port : " + port);

The routes.js file contains all my business logic like reading and parsing CSV data and finally exposing the various RESTful services to the front end.

app/routes.js


// app/routes.js
// expose the routes to our app with module.exports

module.exports = function (app) {

    //Smoking Stats
    var csv = require("fast-csv");
    var HashMap = require('hashmap');

    var allSmokingStats = [];
    var genderSmokingStatsMap = new HashMap();
    var raceSmokingStatsMap = new HashMap();

    csv.fromPath("./app/resources/smoking_adults_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['percentage'] = formatData(data.Adults);
            allSmokingStats.push(stats);
        })
        .on("end", function () {
            console.log("smoking adults stats data are loaded");
        });


    csv.fromPath("./app/resources/smoking_gender_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['male'] = formatData(data.Male);
            stats['female'] = formatData(data.Female);
            genderSmokingStatsMap.set(data.Location, stats);
        })
        .on("end", function () {
            console.log("smoking gender stats data are loaded");
        });

    csv.fromPath("./app/resources/smoking_race_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['white'] = formatData(data.White);
            stats['black'] = formatData(data.Black);
            stats['hispanic'] = formatData(data.Hispanic);
            stats['asianHawPac'] = formatData(data["Asian/Native Hawaiian and Pacific Islander"]);
            stats['nativeAmerican'] = formatData(data["American Indian/Alaska Native"]);
            stats['other'] = formatData(data.Other);
            stats['allAdults'] = formatData(data["All Adults"]);
            raceSmokingStatsMap.set(data.Location, stats);
        })
        .on("end", function () {
            console.log("smoking race stats data are loaded");
        });

    //Asthma
    var allAsthmaStats = [];
    var genderAsthmaStatsMap = new HashMap();
    var raceAsthmaStatsMap = new HashMap();

    csv.fromPath("./app/resources/asthma_adults_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['percentage'] = formatData(data["Asthma Prevalence Among Adults"]);
            allAsthmaStats.push(stats);
        })
        .on("end", function () {
            console.log("asthma adults stats data are loaded");
        });


    csv.fromPath("./app/resources/asthma_gender_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['male'] = formatData(data.Male);
            stats['female'] = formatData(data.Female);
            genderAsthmaStatsMap.set(data.Location, stats);
        })
        .on("end", function () {
            console.log("asthma gender stats data are loaded");
        });

    csv.fromPath("./app/resources/asthma_race_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['white'] = formatData(data.White);
            stats['black'] = formatData(data.Black);
            stats['hispanic'] = formatData(data.Hispanic);
            stats['asianHawPac'] = formatData(data["Asian/Native Hawaiian and Pacific Islander"]);
            stats['nativeAmerican'] = formatData(data["American Indian/Alaska Native"]);
            stats['other'] = formatData(data.Other);
            stats['allAdults'] = formatData(data["All Adults"]);
            raceAsthmaStatsMap.set(data.Location, stats);
        })
        .on("end", function () {
            console.log("asthma race stats data are loaded");
        });

    //Mental health stats

    var allPoorMentalHealthStats = [];
    var genderPoorMentalHealthStatsMap = new HashMap();
    var racePoorMentalHealthStatsMap = new HashMap();

    csv.fromPath("./app/resources/mental_health_adults_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['mentalHealth'] = formatData(data['Poor Mental Health Among Adults']);
            allPoorMentalHealthStats.push(stats);
        })
        .on("end", function () {
            console.log("poor mental health adults stats data are loaded");
        });

    csv.fromPath("./app/resources/mental_health_gender_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['male'] = formatData(data.Male);
            stats['female'] = formatData(data.Female);
            genderPoorMentalHealthStatsMap.set(data.Location, stats);
        })
        .on("end", function () {
            console.log("poor mental health gender stats data are loaded");
        });

    csv.fromPath("./app/resources/mental_health_race_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['white'] = formatData(data.White);
            stats['black'] = formatData(data.Black);
            stats['hispanic'] = formatData(data.Hispanic);
            stats['asianHawPac'] = formatData(data["Asian/Native Hawaiian and Pacific Islander"]);
            stats['nativeAmerican'] = formatData(data["American Indian/Alaska Native"]);
            stats['other'] = formatData(data.Other);
            racePoorMentalHealthStatsMap.set(data.Location, stats);
        })
        .on("end", function () {
            console.log("poor mental health race stats data are loaded");
        });

    // Obesity

    var allObesityStats = [];
    var genderObesityStatsMap = new HashMap();
    var raceObesityStatsMap = new HashMap();

    csv.fromPath("./app/resources/obesity_adults_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['percentage'] = formatData(data['Adult Overweight/Obesity Rate']);
            allObesityStats.push(stats);
        })
        .on("end", function () {
            console.log("obesity adults stats data are loaded");
        });

    csv.fromPath("./app/resources/obesity_gender_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['male'] = formatData(data.Male);
            stats['female'] = formatData(data.Female);
            genderObesityStatsMap.set(data.Location, stats);
        })
        .on("end", function () {
            console.log("obesity gender stats data are loaded");
        });

    csv.fromPath("./app/resources/obesity_race_stats.csv", {headers: true})
        .on("data", function (data) {
            var stats = {};
            stats['location'] = data.Location;
            stats['white'] = formatData(data.White);
            stats['black'] = formatData(data.Black);
            stats['hispanic'] = formatData(data.Hispanic);
            stats['asianHawPac'] = formatData(data["Asian/Native Hawaiian and Pacific Islander"]);
            stats['nativeAmerican'] = formatData(data["American Indian/Alaska Native"]);
            stats['other'] = formatData(data.Other);
            stats['allAdults'] = formatData(data["All Adults"]);
            raceObesityStatsMap.set(data.Location, stats);
        })
        .on("end", function () {
            console.log("obesity race stats data are loaded");
        });

    app.get('/api/stats/:type', function (req, res) {

        if ("smoking" == req.params.type.toString()) {
            res.json(allSmokingStats);
        } else if ("asthma" == req.params.type.toString()) {
            res.json(allAsthmaStats);
        } else if ("mentalhealth" == req.params.type.toString()) {
            res.json(allPoorMentalHealthStats);
        } else if ("obesity" == req.params.type.toString()) {
            res.json(allObesityStats);
        } else {
            res.json("Invalid type has been sent");
        }
    });

    app.get('/api/stats/:type/gender/:state', function (req, res) {

        if ("smoking" == req.params.type.toString()) {
            res.json(genderSmokingStatsMap.get(req.params.state));
        } else if ("asthma" == req.params.type.toString()) {
            res.json(genderAsthmaStatsMap.get(req.params.state));
        } else if ("mentalhealth" == req.params.type.toString()) {
            res.json(genderPoorMentalHealthStatsMap.get(req.params.state));
        } else if ("obesity" == req.params.type.toString()) {
            res.json(genderObesityStatsMap.get(req.params.state));
        } else {
            res.json("Invalid type has been sent");
        }
    });

    app.get('/api/stats/:type/race/:state', function (req, res) {
        if ("smoking" == req.params.type.toString()) {
            res.json(raceSmokingStatsMap.get(req.params.state));
        } else if ("asthma" == req.params.type.toString()) {
            res.json(raceAsthmaStatsMap.get(req.params.state));
        } else if ("mentalhealth" == req.params.type.toString()) {
            res.json(racePoorMentalHealthStatsMap.get(req.params.state));
        } else if ("obesity" == req.params.type.toString()) {
            res.json(raceObesityStatsMap.get(req.params.state));
        } else {
            res.json("Invalid type has been sent");
        }
    });
};

var formatData = function (data) {
    if (data == 'NSD') {
        return 0.0;
    }
    return (parseFloat(data) * 100).toFixed(2);
};

Then from the front end, I just access these web services via Ajax call and process the response and render it in Chart

chartstats.js



google.charts.load('current', {'packages': ['geochart', 'corechart']});
google.charts.setOnLoadCallback(drawSmokingRegionsMap);
google.charts.setOnLoadCallback(drawAsthmaRegionsMap);
google.charts.setOnLoadCallback(drawMentalHealthRegionsMap);
google.charts.setOnLoadCallback(drawObesityRegionsMap);

function drawSmokingRegionsMap() {
    var jsonData = $.ajax({
        url: "/api/stats/smoking",
        dataType: "json",
        async: false
    }).responseText;

    var contentAsArray = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'State');
    dataTable.addColumn('number', 'Adults %');

    for (var i = 0; i < contentAsArray.length; i++) {
        dataTable.addRow([contentAsArray[i].location, parseFloat(contentAsArray[i].percentage)]);
    }

    var options = {
        region: 'US',
        resolution: 'provinces',
        backgroundColor: '#81d4fa',
        colorAxis: {colors: ['#00853f', '#e31b23']},
        datalessRegionColor: '#f8bbd0',
        enableRegionInteractivity: true,
        tooltip: {trigger: 'focus'},
        defaultColor: '#f5f5f5'
    };
    var chart = new google.visualization.GeoChart(document.getElementById('smoking-geochart-colors'));
    chart.draw(dataTable, options);
    google.visualization.events.addListener(chart, 'select', selectHandler);

    //Default state is Ohio
    chart.setSelection([{row: 1, column: 1}]);
    $('#smoking-currentState').text('Ohio');
    drawSmokingGenderChart('Ohio');
    drawSmokingRaceChart('Ohio');

    function selectHandler(e) {
        var selection = chart.getSelection();
        var state = '';
        for (var i = 0; i < selection.length; i++) {
            var item = selection[i];
            if (item.row != null && item.column != null) {
                state = dataTable.getFormattedValue(item.row, item.column);
            } else if (item.row != null) {
                state = dataTable.getFormattedValue(item.row, 0);
            } else if (item.column != null) {
                state = dataTable.getFormattedValue(0, item.column);
            }
        }
        if (state == '') {
            state = 'nothing';
        }
        $('#smoking-stats-link').click();
        console.log(state);
        drawSmokingGenderChart(state);
        drawSmokingRaceChart(state);
        $('#smoking-currentState').text(state);
    }
}

function drawSmokingGenderChart(state) {

    var jsonData = $.ajax({
        url: "/api/stats/smoking/gender/" + state,
        dataType: "json",
        async: false
    }).responseText;

    var content = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'Gender');
    dataTable.addColumn('number', 'Gender %');
    dataTable.addRow(['Male', parseFloat(content.male)]);
    dataTable.addRow(['Female', parseFloat(content.female)]);

    var options = {
        title: 'Smoking Stats by Gender',
        is3D: true
    };
    var chart = new google.visualization.PieChart(document.getElementById('smoking-piechart-gender'));
    chart.draw(dataTable, options);
}

function drawSmokingRaceChart(state) {

    var jsonData = $.ajax({
        url: "/api/stats/smoking/race/" + state,
        dataType: "json",
        async: false
    }).responseText;

    var content = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'Race');
    dataTable.addColumn('number', 'Race %');

    dataTable.addRow(['White', parseFloat(content.white)]);
    dataTable.addRow(['Black', parseFloat(content.black)]);
    dataTable.addRow(['Hispanic', parseFloat(content.hispanic)]);
    dataTable.addRow(['Asian/Native Hawaiian and Pacific Islander', parseFloat(content.asianHawPac)]);
    dataTable.addRow(['American Indian/Alaska Native', parseFloat(content.nativeAmerican)]);
    dataTable.addRow(['Other', parseFloat(content.other)]);
    dataTable.addRow(['All Adults', parseFloat(content.allAdults)]);
    var options = {
        title: 'Smoking Stats by Race/Ethnicity',
        sliceVisibilityThreshold: 0,
        is3D: true
    };
    var chart = new google.visualization.PieChart(document.getElementById('smoking-piechart-race'));
    chart.draw(dataTable, options);
}

function drawAsthmaRegionsMap() {
    var jsonData = $.ajax({
        url: "/api/stats/asthma",
        dataType: "json",
        async: false
    }).responseText;

    var contentAsArray = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'State');
    dataTable.addColumn('number', 'Adults %');

    for (var i = 0; i < contentAsArray.length; i++) {
        dataTable.addRow([contentAsArray[i].location, parseFloat(contentAsArray[i].percentage)]);
    }

    var options = {
        region: 'US',
        resolution: 'provinces',
        backgroundColor: '#81d4fa',
        colorAxis: {colors: ['#00853f', '#e31b23']},
        datalessRegionColor: '#f8bbd0',
        enableRegionInteractivity: true,
        tooltip: {trigger: 'focus'},
        defaultColor: '#f5f5f5'
    };
    var chart = new google.visualization.GeoChart(document.getElementById('asthma-geochart-colors'));
    chart.draw(dataTable, options);
    google.visualization.events.addListener(chart, 'select', selectHandler);

    //Default state is Ohio
    $('#asthma-currentState').text('Ohio');
    drawAsthmaGenderChart('Ohio');
    drawAsthmaRaceChart('Ohio');

    function selectHandler(e) {
        var selection = chart.getSelection();
        var state = '';
        for (var i = 0; i < selection.length; i++) {
            var item = selection[i];
            if (item.row != null && item.column != null) {
                state = dataTable.getFormattedValue(item.row, item.column);
            } else if (item.row != null) {
                state = dataTable.getFormattedValue(item.row, 0);
            } else if (item.column != null) {
                state = dataTable.getFormattedValue(0, item.column);
            }
        }
        if (state == '') {
            state = 'nothing';
        }
        $('#asthma-stats-link').click();
        console.log(state);
        drawAsthmaGenderChart(state);
        drawAsthmaRaceChart(state);
        $('#asthma-currentState').text(state);
    }
}

function drawAsthmaGenderChart(state) {

    var jsonData = $.ajax({
        url: "/api/stats/asthma/gender/" + state,
        dataType: "json",
        async: false
    }).responseText;

    var content = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'Gender');
    dataTable.addColumn('number', 'Gender %');
    dataTable.addRow(['Male', parseFloat(content.male)]);
    dataTable.addRow(['Female', parseFloat(content.female)]);

    var options = {
        title: 'Adult Self-Reported Current Asthma Prevalence Rate by Gender',
        is3D: true
    };
    var chart = new google.visualization.PieChart(document.getElementById('asthma-piechart-gender'));
    chart.draw(dataTable, options);
}

function drawAsthmaRaceChart(state) {

    var jsonData = $.ajax({
        url: "/api/stats/asthma/race/" + state,
        dataType: "json",
        async: false
    }).responseText;

    var content = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'Race');
    dataTable.addColumn('number', 'Race %');

    dataTable.addRow(['White', parseFloat(content.white)]);
    dataTable.addRow(['Black', parseFloat(content.black)]);
    dataTable.addRow(['Hispanic', parseFloat(content.hispanic)]);
    dataTable.addRow(['Asian/Native Hawaiian and Pacific Islander', parseFloat(content.asianHawPac)]);
    dataTable.addRow(['American Indian/Alaska Native', parseFloat(content.nativeAmerican)]);
    dataTable.addRow(['Other', parseFloat(content.other)]);
    dataTable.addRow(['All Adults', parseFloat(content.allAdults)]);
    var options = {
        title: 'Adult Self-Reported Current Asthma Prevalence Rate by Race/Ethnicity',
        sliceVisibilityThreshold: 0,
        is3D: true
    };
    var chart = new google.visualization.PieChart(document.getElementById('asthma-piechart-race'));
    chart.draw(dataTable, options);
}


function drawMentalHealthRegionsMap() {
    var jsonData = $.ajax({
        url: "/api/stats/mentalhealth",
        dataType: "json",
        async: false
    }).responseText;

    var contentAsArray = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'State');
    dataTable.addColumn('number', 'Adults %');

    for (var i = 0; i < contentAsArray.length; i++) {
        dataTable.addRow([contentAsArray[i].location, parseFloat(contentAsArray[i].mentalHealth)]);
    }
    var options = {
        region: 'US',
        resolution: 'provinces',
        backgroundColor: '#81d4fa',
        colorAxis: {colors: ['#00853f', '#e31b23']},
        datalessRegionColor: '#f8bbd0',
        enableRegionInteractivity: true,
        tooltip: {trigger: 'focus'},
        defaultColor: '#f5f5f5'
    };
    var chart = new google.visualization.GeoChart(document.getElementById('mentalhealth-geochart-colors'));
    chart.draw(dataTable, options);
    google.visualization.events.addListener(chart, 'select', selectHandler);

    //Default state is Ohio
    //chart.setSelection([{row:1, column:1}]);
    $('#mentalhealth-currentState').text('Ohio');
    drawMentalHealthGenderChart('Ohio');
    drawMentalHealthRaceChart('Ohio');

    function selectHandler(e) {
        var selection = chart.getSelection();
        var state = '';
        for (var i = 0; i < selection.length; i++) {
            var item = selection[i];
            if (item.row != null && item.column != null) {
                state = dataTable.getFormattedValue(item.row, item.column);
            } else if (item.row != null) {
                state = dataTable.getFormattedValue(item.row, 0);
            } else if (item.column != null) {
                state = dataTable.getFormattedValue(0, item.column);
            }
        }
        if (state == '') {
            state = 'nothing';
        }
        $('#mentalhealth-stats-link').click();
        console.log(state);
        drawMentalHealthGenderChart(state);
        drawMentalHealthRaceChart(state);
        $('#mentalhealth-currentState').text(state);
    }
}

function drawMentalHealthGenderChart(state) {

    var jsonData = $.ajax({
        url: "/api/stats/mentalhealth/gender/" + state,
        dataType: "json",
        async: false
    }).responseText;

    var content = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'Gender');
    dataTable.addColumn('number', 'Gender %');
    dataTable.addRow(['Male', parseFloat(content.male)]);
    dataTable.addRow(['Female', parseFloat(content.female)]);

    var options = {
        title: 'Poor Mental Health Stats by Gender',
        is3D: true
    };
    var chart = new google.visualization.PieChart(document.getElementById('mentalhealth-piechart-gender'));
    chart.draw(dataTable, options);
}

function drawMentalHealthRaceChart(state) {

    var jsonData = $.ajax({
        url: "/api/stats/mentalhealth/race/" + state,
        dataType: "json",
        async: false
    }).responseText;

    var content = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'Race');
    dataTable.addColumn('number', 'Race %');

    dataTable.addRow(['White', parseFloat(content.white)]);
    dataTable.addRow(['Black', parseFloat(content.black)]);
    dataTable.addRow(['Hispanic', parseFloat(content.hispanic)]);
    dataTable.addRow(['Asian/Native Hawaiian and Pacific Islander', parseFloat(content.asianHawPac)]);
    dataTable.addRow(['American Indian/Alaska Native', parseFloat(content.nativeAmerican)]);
    dataTable.addRow(['Other', parseFloat(content.other)]);
    var options = {
        title: 'Poor Mental Health Stats by Race/Ethnicity',
        sliceVisibilityThreshold: 0,
        is3D: true
    };
    var chart = new google.visualization.PieChart(document.getElementById('mentalhealth-piechart-race'));
    chart.draw(dataTable, options);
}
function drawObesityRegionsMap() {
    var jsonData = $.ajax({
        url: "/api/stats/obesity",
        dataType: "json",
        async: false
    }).responseText;

    var contentAsArray = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'State');
    dataTable.addColumn('number', 'Adults %');

    for (var i = 0; i < contentAsArray.length; i++) {
        dataTable.addRow([contentAsArray[i].location, parseFloat(contentAsArray[i].percentage)]);
    }

    var options = {
        region: 'US',
        resolution: 'provinces',
        backgroundColor: '#81d4fa',
        colorAxis: {colors: ['#00853f', '#e31b23']},
        datalessRegionColor: '#f8bbd0',
        enableRegionInteractivity: true,
        tooltip: {trigger: 'focus'},
        defaultColor: '#f5f5f5'
    };
    var chart = new google.visualization.GeoChart(document.getElementById('obesity-geochart-colors'));
    chart.draw(dataTable, options);
    google.visualization.events.addListener(chart, 'select', selectHandler);

    //Default state is Ohio
    $('#obesity-currentState').text('Ohio');
    drawObesityGenderChart('Ohio');
    drawObesityRaceChart('Ohio');

    function selectHandler(e) {
        var selection = chart.getSelection();
        var state = '';
        for (var i = 0; i < selection.length; i++) {
            var item = selection[i];
            if (item.row != null && item.column != null) {
                state = dataTable.getFormattedValue(item.row, item.column);
            } else if (item.row != null) {
                state = dataTable.getFormattedValue(item.row, 0);
            } else if (item.column != null) {
                state = dataTable.getFormattedValue(0, item.column);
            }
        }
        if (state == '') {
            state = 'nothing';
        }
        $('#obesity-stats-link').click();
        drawObesityGenderChart(state);
        drawObesityRaceChart(state);
        $('#obesity-currentState').text(state);
    }
}

function drawObesityGenderChart(state) {

    var jsonData = $.ajax({
        url: "/api/stats/obesity/gender/" + state,
        dataType: "json",
        async: false
    }).responseText;

    var content = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'Gender');
    dataTable.addColumn('number', 'Gender %');
    dataTable.addRow(['Male', parseFloat(content.male)]);
    dataTable.addRow(['Female', parseFloat(content.female)]);

    var options = {
        title: 'Obesity Stats by Gender',
        is3D: true
    };
    var chart = new google.visualization.PieChart(document.getElementById('obesity-piechart-gender'));
    chart.draw(dataTable, options);
}

function drawObesityRaceChart(state) {

    var jsonData = $.ajax({
        url: "/api/stats/obesity/race/" + state,
        dataType: "json",
        async: false
    }).responseText;

    var content = JSON.parse(jsonData);
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'Race');
    dataTable.addColumn('number', 'Race %');

    dataTable.addRow(['White', parseFloat(content.white)]);
    dataTable.addRow(['Black', parseFloat(content.black)]);
    dataTable.addRow(['Hispanic', parseFloat(content.hispanic)]);
    dataTable.addRow(['Asian/Native Hawaiian and Pacific Islander', parseFloat(content.asianHawPac)]);
    dataTable.addRow(['American Indian/Alaska Native', parseFloat(content.nativeAmerican)]);
    dataTable.addRow(['Other', parseFloat(content.other)]);
    dataTable.addRow(['All Adults', parseFloat(content.allAdults)]);
    var options = {
        title: 'Obesity Stats by Race/Ethnicity',
        sliceVisibilityThreshold: 0,
        is3D: true
    };
    var chart = new google.visualization.PieChart(document.getElementById('obesity-piechart-race'));
    chart.draw(dataTable, options);
}


 
Pls refer to know more at Health Stats DevPost