org.ut.biolab.medsavant.client.view.genetics.charts.SummaryChart.java Source code

Java tutorial

Introduction

Here is the source code for org.ut.biolab.medsavant.client.view.genetics.charts.SummaryChart.java

Source

/**
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.ut.biolab.medsavant.client.view.genetics.charts;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.rmi.RemoteException;
import java.sql.SQLException;
import java.util.List;
import java.util.*;
import javax.swing.*;

import com.jidesoft.chart.Chart;
import com.jidesoft.chart.ChartType;
import com.jidesoft.chart.Legend;
import com.jidesoft.chart.PointShape;
import com.jidesoft.chart.axis.Axis;
import com.jidesoft.chart.axis.CategoryAxis;
import com.jidesoft.chart.model.ChartCategory;
import com.jidesoft.chart.model.ChartPoint;
import com.jidesoft.chart.model.DefaultChartModel;
import com.jidesoft.chart.model.Highlight;
import com.jidesoft.chart.model.InvertibleTransform;
import com.jidesoft.chart.render.AbstractPieSegmentRenderer;
import com.jidesoft.chart.render.DefaultBarRenderer;
import com.jidesoft.chart.render.DefaultPieSegmentRenderer;
import com.jidesoft.chart.render.LinePieLabelRenderer;
import com.jidesoft.chart.style.ChartStyle;
import com.jidesoft.range.CategoryRange;
import com.jidesoft.range.NumericRange;
import java.text.NumberFormat;
import net.ericaro.surfaceplotter.JSurfacePanel;
import net.ericaro.surfaceplotter.Mapper;
import net.ericaro.surfaceplotter.ProgressiveSurfaceModel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.ut.biolab.medsavant.MedSavantClient;
import org.ut.biolab.medsavant.client.filter.FilterController;
import org.ut.biolab.medsavant.client.filter.SearchBar;
import org.ut.biolab.medsavant.client.filter.WhichTable;
import org.ut.biolab.medsavant.shared.format.BasicPatientColumns;
import org.ut.biolab.medsavant.shared.format.BasicVariantColumns;
import org.ut.biolab.medsavant.client.view.login.LoginController;
import org.ut.biolab.medsavant.shared.model.ScatterChartEntry;
import org.ut.biolab.medsavant.shared.model.ScatterChartMap;
import org.ut.biolab.medsavant.client.project.ProjectController;
import org.ut.biolab.medsavant.client.reference.ReferenceController;
import org.ut.biolab.medsavant.client.util.ClientMiscUtils;
import org.ut.biolab.medsavant.client.util.MedSavantExceptionHandler;
import org.ut.biolab.medsavant.client.util.MedSavantWorker;
import org.ut.biolab.medsavant.client.util.ThreadController;
import org.ut.biolab.medsavant.client.view.util.ViewUtil;
import org.ut.biolab.medsavant.client.view.component.WaitPanel;
import org.ut.biolab.medsavant.client.view.genetics.QueryUtils;
import org.ut.biolab.medsavant.shared.model.SessionExpiredException;
import org.ut.biolab.medsavant.client.query.QueryViewController;

/**
 *
 * @author mfiume
 */
public class SummaryChart extends JLayeredPane implements BasicPatientColumns, BasicVariantColumns {

    private static final Log LOG = LogFactory.getLog(SummaryChart.class);

    public boolean doesCompareToOriginal() {
        return this.showComparedToOriginal;
    }

    void setDoesCompareToOriginal(boolean b) {
        this.showComparedToOriginal = b;
        updateDataAndDrawChart();
    }

    public static enum ChartAxis {

        X, Y
    };

    private boolean isLogScaleY = false;
    private boolean isLogScaleX = false;
    private boolean showComparedToOriginal = false;
    private boolean isPie = false;
    private boolean isSorted = false;
    private MedSavantWorker mapWorker;
    private ChartMapGenerator mapGenerator;
    private ChartMapGenerator mapGeneratorScatter;
    private boolean isSortedKaryotypically;
    private String pageName;
    private final Object updateLock = new Object();
    private boolean updateRequired = false;
    private GridBagConstraints c;
    private WaitPanel waitPanel = new WaitPanel("Getting chart data");
    private boolean isScatter = false;
    //Most of the time this is mapGenerator.getName(), except when the field
    //is from a non-standard annotation format (e..g Ensembl)
    private String chartName;

    public SummaryChart(final String pageName) {
        this.pageName = pageName;
        setLayout(new GridBagLayout());

        c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 0;
        c.gridwidth = 1;
        c.gridheight = 1;
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1.0;
        c.weighty = 1.0;

        this.add(waitPanel, c, JLayeredPane.MODAL_LAYER);
    }

    void setChartName(String s) {
        this.chartName = s;
    }

    public void setIsLogScale(boolean isLogScale, ChartAxis axis) {
        if (!isLogScale) {
            isLogScaleY = false;
            isLogScaleX = false;
        } else {
            isLogScaleY = (axis == ChartAxis.Y);
            isLogScaleX = (axis == ChartAxis.X);
        }
        updateDataAndDrawChart();
    }

    public void setIsSorted(boolean isSorted) {
        this.isSorted = isSorted;
        updateDataAndDrawChart();
    }

    public void setIsPie(boolean b) {
        this.isPie = b;
        updateDataAndDrawChart();
    }

    public boolean isLogScaleY() {
        return isLogScaleY;
    }

    public boolean isLogScaleX() {
        return isLogScaleX;
    }

    public boolean isPie() {
        return isPie;
    }

    public boolean isSorted() {
        return isSorted;
    }

    public boolean isSortedKaryotypically() {
        return isSortedKaryotypically;
    }

    public void setChartMapGenerator(ChartMapGenerator cmg) {
        this.mapGenerator = cmg;
    }

    public void setScatterChartMapGenerator(ChartMapGenerator cmg) {
        mapGeneratorScatter = cmg;
    }

    public void setIsScatterChart(boolean scatter) {
        isScatter = scatter;
    }

    public boolean isScatterChart() {
        return isScatter;
    }

    public void updateIfRequired() {
        synchronized (updateLock) {
            if (updateRequired) {
                updateRequired = false;
                updateDataAndDrawChart();
            }
        }
    }

    public void setUpdateRequired(boolean required) {
        synchronized (updateLock) {
            updateRequired = required;
        }
    }

    private void updateDataAndDrawChart() {

        removeAll();
        add(waitPanel, c, JLayeredPane.MODAL_LAYER);
        waitPanel.setVisible(true);
        setLayer(waitPanel, JLayeredPane.MODAL_LAYER);

        //begin creating chart
        mapWorker = isScatter ? new ScatterChartMapWorker() : new ChartMapWorker();
        mapWorker.execute();
    }

    private ChartStyle barStyle(Paint fill) {
        ChartStyle style = new ChartStyle();
        style.setBarsVisible(true);
        style.setBarPaint(fill);
        return style;
    }

    private synchronized Chart drawChart(ChartFrequencyMap[] chartMaps) {

        ChartFrequencyMap filteredChartMap = chartMaps[0];
        ChartFrequencyMap unfilteredChartMap = null;

        DefaultChartModel filteredChartModel = new DefaultChartModel();
        DefaultChartModel unfilteredChartModel = null;

        if (this.showComparedToOriginal) {
            unfilteredChartMap = ChartFrequencyMap.subtract(chartMaps[1], filteredChartMap, isLogScaleY());
            unfilteredChartModel = new DefaultChartModel();
        }

        final Chart chart = new Chart();

        JPanel panel = new JPanel();
        Legend legend = new Legend(chart, 0);
        panel.add(legend);
        legend.addChart(chart);

        boolean multiColor = !mapGenerator.isNumeric() || isPie;

        chart.setRolloverEnabled(true);
        chart.setSelectionEnabled(true);
        chart.setSelectionShowsOutline(true);
        chart.setSelectionShowsExplodedSegments(true);
        chart.setAntiAliasing(true);
        chart.setBarGap(5);
        chart.setBorder(ViewUtil.getBigBorder());
        chart.setLabellingTraces(true);

        chart.setAnimateOnShow(false);

        chart.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e)) {
                    JPopupMenu popup = createPopup(chart);
                    popup.show(e.getComponent(), e.getX(), e.getY());
                }
            }
        });

        AbstractPieSegmentRenderer rpie = new DefaultPieSegmentRenderer();
        chart.setPieSegmentRenderer(rpie);

        //Makes a box with fill color 255,255,255,0 and put a label with a black
        //font in that box.  The box is positioned directly over the corresponding pie slice.
        /*
        SimplePieLabelRenderer plr = new SimplePieLabelRenderer();
        plr.setLabelColor(Color.BLACK);
        plr.setBackground(new Color(0,0,0,0));
        rpie.setPieLabelRenderer(plr);
        */

        //....alternatively, the below draws a line from the pie wedge to the label.
        //see http://www.jidesoft.com/javadoc/com/jidesoft/chart/render/LinePieLabelRenderer.html
        LinePieLabelRenderer plr = new LinePieLabelRenderer();
        plr.setLabelColor(Color.black);
        plr.setLineColor(Color.black); //see also plr.setLineStroke        
        rpie.setPieLabelRenderer(plr);

        DefaultBarRenderer rbar = new DefaultBarRenderer();
        chart.setBarRenderer(rbar);

        rpie.setSelectionColor(Color.gray);
        rbar.setSelectionColor(Color.gray);

        if (isSortedKaryotypically()) {
            filteredChartMap.sortKaryotypically();
            if (this.showComparedToOriginal) {
                unfilteredChartMap.sortKaryotypically();
            }
        }

        if (isSorted() && !mapGenerator.isNumeric() && !isSortedKaryotypically()) {
            filteredChartMap.sortNumerically();
            if (this.showComparedToOriginal) {
                chartMaps[1].sortNumerically();
            }
        } else {
            filteredChartMap.undoSortNumerically();
            if (this.showComparedToOriginal) {
                chartMaps[1].undoSortNumerically();
            }
        }

        long max = filteredChartMap.getMax();
        List<ChartCategory> chartCategories;
        if (this.showComparedToOriginal) {
            chartCategories = chartMaps[1].getCategories();
            max = chartMaps[1].getMax();
        } else {
            chartCategories = filteredChartMap.getCategories();
        }

        CategoryRange<String> range = new CategoryRange<String>();
        List<Highlight> highlights = new ArrayList<Highlight>();

        Color color = new Color(72, 181, 249);
        Highlight h;
        int catNum = 0;
        int totalCats = filteredChartMap.getEntries().size();

        for (ChartCategory category : chartCategories) {
            range.add(category);
            if (multiColor) {
                color = ViewUtil.getColor(catNum++, totalCats);
            }
            h = new Highlight(category.getName());
            highlights.add(h);
            chart.setHighlightStyle(h, barStyle(color));
        }

        final CategoryAxis xaxis = new CategoryAxis(range, "Category");
        chart.setXAxis(xaxis);
        if (this.isLogScaleY()) {
            chart.setYAxis(new Axis(new NumericRange(0, Math.log10(max) * 1.1), "log(Frequency)"));
        } else {
            chart.setYAxis(new Axis(new NumericRange(0, max * 1.1), "Frequency"));
        }

        addEntriesToChart(filteredChartModel, filteredChartMap, chartCategories, highlights);
        if (this.showComparedToOriginal) {
            addEntriesToChart(unfilteredChartModel, unfilteredChartMap, chartCategories, null);
        }

        chart.getXAxis().getLabel().setFont(ViewUtil.getMediumTitleFont());
        chart.getYAxis().getLabel().setFont(ViewUtil.getMediumTitleFont());

        // rotate 90 degrees (using radians)
        chart.getXAxis().setTickLabelRotation(1.57079633);

        if (isPie) {
            System.out.println("Setting chart type to pie");
            chart.setChartType(ChartType.PIE);
            chart.getXAxis().getLabel().setColor(Color.BLUE);
        }

        // This adds zooming cababilities to bar charts, not great though
        /*else {
         RubberBandZoomer rubberBand = new RubberBandZoomer(chart);
         chart.addDrawable(rubberBand);
         chart.addMouseListener(rubberBand);
         chart.addMouseMotionListener(rubberBand);
            
         rubberBand.addZoomListener(new ZoomListener() {
         public void zoomChanged(ChartSelectionEvent event) {
         if (event instanceof RectangleSelectionEvent) {
         Range<?> currentXRange = chart.getXAxis().getOutputRange();
         Range<?> currentYRange = chart.getYAxis().getOutputRange();
         ZoomFrame frame = new ZoomFrame(currentXRange, currentYRange);
         zoomStack.push(frame);
         Rectangle selection = (Rectangle) event.getLocation();
         Point topLeft = selection.getLocation();
         topLeft.x = (int) Math.floor(frame.getXRange().minimum());
         Point bottomRight = new Point(topLeft.x + selection.width, topLeft.y + selection.height);
         bottomRight.x = (int) Math.ceil(frame.getXRange().maximum());
         assert bottomRight.x >= topLeft.x;
         Point2D rp1 = chart.calculateUserPoint(topLeft);
         Point2D rp2 = chart.calculateUserPoint(bottomRight);
         if (rp1 != null && rp2 != null) {
         assert rp2.getX() >= rp1.getX();
         Range<?> xRange = new NumericRange(rp1.getX(), rp2.getX());
         assert rp1.getY() >= rp2.getY();
         Range<?> yRange = new NumericRange(rp2.getY(), rp1.getY());
         //chart.getXAxis().setRange(xRange);
         chart.getYAxis().setRange(yRange);
         }
         } else if (event instanceof PointSelectionEvent) {
         if (zoomStack.size() > 0) {
         ZoomFrame frame = zoomStack.pop();
         Range<?> xRange = frame.getXRange();
         Range<?> yRange = frame.getYRange();
         //chart.getXAxis().setRange(xRange);
         chart.getYAxis().setRange(yRange);
         }
         }
         }
         });
         }
         *
         */

        for (int i = 1; i < this.getComponentCount(); i++) {
            this.remove(i);
        }

        chart.addModel(filteredChartModel, new ChartStyle().withBars());
        if (this.showComparedToOriginal) {
            chart.addModel(unfilteredChartModel, new ChartStyle(new Color(10, 10, 10, 100)).withBars());
        }

        return chart;
    }

    private void addEntriesToChart(DefaultChartModel chartModel, ChartFrequencyMap chartMap,
            List<ChartCategory> chartCategories, List<Highlight> highlights) {

        boolean addHighlights = highlights != null;

        int index = 0;
        for (ChartCategory cat : chartCategories) {

            FrequencyEntry fe = chartMap.getEntry(cat.getName());
            long value = 0;
            if (fe != null) {
                value = fe.getFrequency();
            }

            ChartPoint p = new ChartPoint(cat, value);
            ChartPoint logp = new ChartPoint(cat, Math.log10(value));

            if (addHighlights) {
                p.setHighlight(highlights.get(index));
                logp.setHighlight(highlights.get(index));
            }

            if (this.isLogScaleY()) {
                chartModel.addPoint(logp);
            } else {
                chartModel.addPoint(p);
            }

            index++;
        }
    }

    private synchronized void drawScatterChart(ScatterChartMap entries) {

        Chart chart = new Chart(new Dimension(200, 200));
        Legend legend = new Legend(chart);

        //Create x axis
        int max = 0;
        CategoryRange<String> range = new CategoryRange<String>();
        for (int i = 0; i < entries.getNumX(); i++) {
            range.add(new ChartCategory(entries.getXValueAt(i)));
        }

        //create models
        DefaultChartModel[] models = new DefaultChartModel[entries.getNumY()];
        for (int i = 0; i < entries.getNumY(); i++) {
            models[i] = new DefaultChartModel();
        }

        for (int i = 0; i < entries.getNumX(); i++) {
            //ScatterChartEntry[] x = entries[i];
            for (int j = 0; j < entries.getNumY(); j++) {
                ScatterChartEntry entry = entries.getValueAt(i, j);
                if (entry != null) {
                    max = Math.max(max, entry.getFrequency());
                    models[j].addPoint(new ChartPoint(range.getCategoryValues().get(i), entry.getFrequency()));
                    models[j].setName(entry.getYRange());
                } else {
                    models[j].addPoint(new ChartPoint(range.getCategoryValues().get(i), 0));
                }
            }
        }

        //add models
        for (int i = 0; i < models.length; i++) {
            Color translucentGreen = ViewUtil.getColor(i, models.length);
            ChartStyle style = new ChartStyle(translucentGreen, PointShape.DISC);
            style.setPointSize(10);
            chart.addModel(models[i], style);
        }

        //add axes
        CategoryAxis xaxis = new CategoryAxis(range, mapGenerator.getName());
        chart.setXAxis(xaxis);
        chart.setYAxis(new Axis(new NumericRange(0, max * 1.1), "Frequency"));

        //add chart
        add(chart, c, JLayeredPane.DEFAULT_LAYER);

        //add legend in scrollpane
        JScrollPane scroll = new JScrollPane(legend);
        scroll.setPreferredSize(new Dimension(150, 100));
        c.gridx = 1;
        c.weightx = 0;
        add(scroll, c, JLayeredPane.DEFAULT_LAYER);
        c.weightx = 1;
        c.gridx = 0;

    }

    private synchronized void drawSurface(final ScatterChartMap entries) {

        String[] firstX = entries.getXValueAt(0).split("[^(\\d|\\.|E)]");
        String[] firstY = entries.getYValueAt(0).split("[^(\\d|\\.|E)]");
        String[] lastX = entries.getXValueAt(entries.getNumX() - 1).split("[^(\\d|\\.|E)]");
        String[] lastY = entries.getYValueAt(entries.getNumY() - 1).split("[^(\\d|\\.|E)]");

        float startX = Float.parseFloat(firstX[0]);
        float endX = Float.parseFloat(lastX[lastX.length - 1]);
        final float binSizeX = Float.parseFloat(firstX[firstX.length - 1]) - startX;

        float startY = Float.parseFloat(firstY[0]);
        float endY = Float.parseFloat(lastY[lastY.length - 1]);
        final float binSizeY = Float.parseFloat(firstY[firstY.length - 1]) - startY;

        JSurfacePanel panel = new JSurfacePanel();
        panel.setTitleText("");

        ProgressiveSurfaceModel model = new ProgressiveSurfaceModel();
        panel.setModel(model);
        model.setXMin(startX);
        model.setXMax(endX);
        model.setYMin(startY);
        model.setYMax(endY);
        model.setZMin(0);
        model.setZMax(entries.getMaxFrequency() + 1);
        model.setDisplayXY(true);
        model.setDisplayZ(true);

        model.setMapper(new Mapper() {
            @Override
            public float f1(float x, float y) {

                try {
                    int binX = (int) (x / binSizeX);
                    int binY = (int) (y / binSizeY);
                    ScatterChartEntry entry = entries.getValueAt(binX, binY);
                    if (entry == null) {
                        return 0;
                    } else {
                        return entry.getFrequency();
                    }
                } catch (Exception ex) {
                    LOG.error("Exception thrown by mapper.", ex);
                }
                return x;
            }

            @Override
            public float f2(float x, float y) {
                return 0;
            }
        });
        model.plot().execute();

        add(panel, c, JLayeredPane.DEFAULT_LAYER);
    }

    void setIsSortedKaryotypically(boolean b) {
        this.isSortedKaryotypically = b;
    }

    public class ChartMapWorker extends MedSavantWorker<ChartFrequencyMap[]> {

        ChartMapWorker() {
            super(pageName);
            if (mapWorker != null) {
                mapWorker.cancel(true);
            }
        }

        @Override
        protected ChartFrequencyMap[] doInBackground() throws Exception {
            if (mapGenerator == null) {
                return null;
            }
            ChartFrequencyMap[] result;

            if (showComparedToOriginal) {
                result = new ChartFrequencyMap[2];
                result[1] = mapGenerator.generateChartMap(false, isLogScaleX && mapGenerator.isNumeric());
            } else {
                result = new ChartFrequencyMap[1];
            }

            result[0] = mapGenerator.generateChartMap(true, isLogScaleX && mapGenerator.isNumeric());
            return result;
        }

        @Override
        public void showSuccess(ChartFrequencyMap[] result) {
            if (result != null && result[0] != null && result[0].getEntries().size() < 200
                    && !result[0].getEntries().isEmpty()) {
                Chart chart = drawChart(result);
                add(chart, c, JLayeredPane.DEFAULT_LAYER);
            } else if (result != null && result[0] != null && result[0].getEntries().isEmpty()) {
                add(ViewUtil.getMessagePanelBig("No variants pass query"), c);
            } else if (result != null && result[0] != null && result[0].getEntries().size() >= 200) {
                add(ViewUtil.getMessagePanelBig("Too many values to display chart"), c);
            } else {
                add(ViewUtil.getMessagePanelBig("Error creating chart"), c);
            }

            waitPanel.setVisible(false);
            revalidate();
        }

        @Override
        public void showProgress(double prog) {
            if (prog == 1.0) {
                mapWorker = null;
                //removeAll();        // Clear away the WaitPanel.
            }
        }
    }

    public class ScatterChartMapWorker extends MedSavantWorker<ScatterChartMap> {

        ScatterChartMapWorker() {
            super(pageName);
            if (mapWorker != null) {
                mapWorker.cancel(true);
            }
        }

        private ScatterChartMap mapPatientField(ScatterChartMap scatterMap, ChartMapGenerator generator,
                boolean isX) throws SQLException, RemoteException {
            Map<Object, List<String>> map;
            try {
                map = MedSavantClient.PatientManager.getDNAIDsForValues(LoginController.getSessionID(),
                        ProjectController.getInstance().getCurrentProjectID(), generator.getFilterId());
            } catch (SessionExpiredException ex) {
                MedSavantExceptionHandler.handleSessionExpiredException(ex);
                return null;
            }
            if (generator.getFilterId().equals(GENDER.getColumnName())) {
                map = ClientMiscUtils.modifyGenderMap(map);
            }

            List<String> rangesA = new ArrayList<String>();
            for (Object o : map.keySet()) {
                rangesA.add(o.toString());
            }
            List<String> rangesB = (isX ? scatterMap.getYRanges() : scatterMap.getXRanges());

            List<ScatterChartEntry> entries = new ArrayList<ScatterChartEntry>();
            for (Object a : map.keySet()) {
                List<Integer> indices = new ArrayList<Integer>();
                for (String dnaId : map.get(a)) {
                    int index = (isX ? scatterMap.getIndexOnX(dnaId) : scatterMap.getIndexOnY(dnaId));
                    if (index != -1) {
                        indices.add(index);
                    }
                }
                for (int b = 0; b < rangesB.size(); b++) {
                    int sum = 0;
                    for (Integer index : indices) {
                        if ((isX ? scatterMap.getValueAt(index, b) : scatterMap.getValueAt(b, index)) != null) {
                            sum += (isX ? scatterMap.getValueAt(index, b) : scatterMap.getValueAt(b, index))
                                    .getFrequency();
                        }
                    }
                    entries.add((isX ? new ScatterChartEntry(a.toString(), rangesB.get(b), sum)
                            : new ScatterChartEntry(rangesB.get(b), a.toString(), sum)));
                }
            }
            return (isX ? new ScatterChartMap(rangesA, rangesB, entries)
                    : new ScatterChartMap(rangesB, rangesA, entries));
        }

        @Override
        protected ScatterChartMap doInBackground() throws Exception {
            if (mapGenerator == null) {
                return null;
            }

            //get column names
            String columnX = mapGenerator.getFilterId();
            String columnY = mapGeneratorScatter.getFilterId();
            if (mapGenerator.getTable() == WhichTable.PATIENT) {
                columnX = DNA_ID.getColumnName();
            }
            if (mapGeneratorScatter.getTable() == WhichTable.PATIENT) {
                columnY = DNA_ID.getColumnName();
            }

            ScatterChartMap scatterMap = MedSavantClient.VariantManager.getFilteredFrequencyValuesForScatter(
                    LoginController.getSessionID(), ProjectController.getInstance().getCurrentProjectID(),
                    ReferenceController.getInstance().getCurrentReferenceID(),
                    FilterController.getInstance().getAllFilterConditions(), columnX, columnY,
                    !mapGenerator.isNumeric() || mapGenerator.getTable() == WhichTable.PATIENT,
                    !mapGeneratorScatter.isNumeric() || mapGeneratorScatter.getTable() == WhichTable.PATIENT,
                    isSortedKaryotypically());

            //TODO: re-mapping below works only for categorical patient fields. Generalize for numeric/categorical.

            //map for patient field
            if (mapGenerator.getTable() == WhichTable.PATIENT) {
                scatterMap = mapPatientField(scatterMap, mapGenerator, true);
            }

            if (mapGeneratorScatter.getTable() == WhichTable.PATIENT) {
                scatterMap = mapPatientField(scatterMap, mapGeneratorScatter, false);
            }

            return scatterMap;
        }

        @Override
        public void showSuccess(ScatterChartMap result) {
            if (mapGenerator.isNumeric() && mapGeneratorScatter.isNumeric()
                    && !mapGenerator.getFilterId().equals(GENDER.getColumnName())
                    && !mapGeneratorScatter.getFilterId().equals(GENDER.getColumnName())) {
                drawSurface(result);
            } else {
                drawScatterChart(result);
            }
            waitPanel.setVisible(false);
            revalidate();
        }

        @Override
        public void showProgress(double prog) {
            if (prog == 1.0) {
                mapWorker = null;
            }
        }
    }

    static class ValueComparator implements Comparator {

        Map base;

        public ValueComparator(Map base) {
            this.base = base;
        }

        @Override
        public int compare(Object a, Object b) {

            if ((Integer) base.get(a) < (Integer) base.get(b)) {
                return 1;
            } else if ((Integer) base.get(a) == (Integer) base.get(b)) {
                return 0;
            } else {
                return -1;
            }
        }
    }

    class LogTransform implements InvertibleTransform<Double> {

        @Override
        public Double transform(Double pos) {
            return Math.log10(pos);
        }

        @Override
        public Double inverseTransform(Double t) {
            return Math.pow(10, t);
        }
    }

    private JPopupMenu createPopup(final Chart chart) {

        JPopupMenu menu = new JPopupMenu();

        //Filter by selections
        JMenuItem filter1Item = new JMenuItem("Filter by Selection" + (mapGenerator.isNumeric() ? "" : "(s)"));
        filter1Item.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                ThreadController.getInstance().cancelWorkers(pageName);

                boolean isGender = mapGenerator.getName().equalsIgnoreCase(BasicPatientColumns.GENDER.getAlias());
                try {
                    List<String> values = new ArrayList<String>();
                    ListSelectionModel selectionModel = chart.getSelectionsForModel(chart.getModel(0));
                    QueryViewController qvc = SearchBar.getInstance().getQueryViewController();
                    for (int i = selectionModel.getMinSelectionIndex(); i <= selectionModel
                            .getMaxSelectionIndex(); i++) {
                        if (selectionModel.isSelectedIndex(i)) {
                            String v = ((ChartPoint) chart.getModel().getPoint(i)).getHighlight().name();
                            values.add(v);
                            if (mapGenerator.isNumeric() && !isGender) {
                                double low = 0;
                                double high = 0;

                                String[] s = v.split(" - ");
                                if (s.length < 2) {
                                    LOG.error("Invalid range detected for numeric condition "
                                            + mapGenerator.getName() + " val=" + v);
                                }
                                NumberFormat format = NumberFormat.getInstance();
                                low = format.parse(s[0]).doubleValue();
                                high = format.parse(s[1]).doubleValue();

                                QueryUtils.addNumericQuery(chartName, low, high, false);
                            }
                        }
                    }
                    if (values.isEmpty()) {
                        return;
                    }

                    if (!mapGenerator.isNumeric() || isGender) {
                        QueryUtils.addMultiStringQuery(chartName, values);
                    }

                } catch (Exception ex) {
                    ClientMiscUtils.reportError("Error filtering by selection: %s", ex);
                }
            }
        });
        menu.add(filter1Item);

        return menu;
    }
}