• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OpenRefine / OpenRefine / 29980747856

23 Jul 2026 04:54AM UTC coverage: 70.949% (+21.0%) from 49.945%
29980747856

Pull #7668

github

web-flow
Merge 7ad09430e into 68176bf1d
Pull Request #7668: Fix scatterplot facet. Fixes #4926

3439 of 5573 branches covered (61.71%)

Branch coverage included in aggregate %.

2 of 5 new or added lines in 1 file covered. (40.0%)

9998 of 13366 relevant lines covered (74.8%)

3.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

15.93
/main/src/com/google/refine/commands/browsing/GetScatterplotCommand.java
1
/*
2

3
Copyright 2010, Google Inc.
4
All rights reserved.
5

6
Redistribution and use in source and binary forms, with or without
7
modification, are permitted provided that the following conditions are
8
met:
9

10
    * Redistributions of source code must retain the above copyright
11
notice, this list of conditions and the following disclaimer.
12
    * Redistributions in binary form must reproduce the above
13
copyright notice, this list of conditions and the following disclaimer
14
in the documentation and/or other materials provided with the
15
distribution.
16
    * Neither the name of Google Inc. nor the names of its
17
contributors may be used to endorse or promote products derived from
18
this software without specific prior written permission.
19

20
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,           
27
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY           
28
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31

32
*/
33

34
package com.google.refine.commands.browsing;
35

36
import java.awt.Color;
37
import java.awt.image.BufferedImage;
38
import java.io.IOException;
39
import java.io.OutputStream;
40

41
import javax.imageio.ImageIO;
42
import javax.servlet.ServletException;
43
import javax.servlet.ServletOutputStream;
44
import javax.servlet.http.HttpServletRequest;
45
import javax.servlet.http.HttpServletResponse;
46

47
import com.fasterxml.jackson.annotation.JsonProperty;
48
import org.slf4j.Logger;
49
import org.slf4j.LoggerFactory;
50

51
import com.google.refine.browsing.Engine;
52
import com.google.refine.browsing.FilteredRows;
53
import com.google.refine.browsing.facets.ScatterplotDrawingRowVisitor;
54
import com.google.refine.browsing.facets.ScatterplotFacet;
55
import com.google.refine.browsing.util.NumericBinIndex;
56
import com.google.refine.commands.Command;
57
import com.google.refine.expr.Evaluable;
58
import com.google.refine.expr.MetaParser;
59
import com.google.refine.expr.ParsingException;
60
import com.google.refine.model.Column;
61
import com.google.refine.model.Project;
62
import com.google.refine.util.ParsingUtilities;
63

64
public class GetScatterplotCommand extends Command {
3✔
65

66
    final static Logger logger = LoggerFactory.getLogger("get-scatterplot_command");
4✔
67

68
    @Override
69
    public void doGet(HttpServletRequest request, HttpServletResponse response)
70
            throws ServletException, IOException {
71
        // This command triggers evaluation expression and therefore requires CSRF-protection.
72
        if (!hasValidCSRFTokenAsGET(request)) {
4!
73
            respondCSRFError(response);
2✔
74
            return;
1✔
75
        }
76

77
        try {
78
            long start = System.currentTimeMillis();
×
79

80
            Project project = getProject(request);
×
81
            Engine engine = getEngine(request, project);
×
82
            PlotterConfig conf = ParsingUtilities.mapper.readValue(
×
83
                    request.getParameter("plotter"),
×
84
                    PlotterConfig.class);
85

86
            response.setHeader("Content-Type", "image/png");
×
87

NEW
88
            try (ServletOutputStream sos = response.getOutputStream()) {
×
89
                draw(sos, project, engine, conf);
×
90
            }
91

92
            logger.trace("Drawn scatterplot in {} ms", Long.toString(System.currentTimeMillis() - start));
×
93
        } catch (Exception e) {
×
94
            respondException(response, e);
×
95
        }
×
96
    }
×
97

98
    protected static class PlotterConfig {
2✔
99

100
        @JsonProperty(ScatterplotFacet.SIZE)
3✔
101
        public int size = 100;
102
        @JsonProperty(ScatterplotFacet.DOT)
3✔
103
        double dot = 100;
104
        @JsonProperty(ScatterplotFacet.DIM_X)
3✔
105
        public ScatterplotFacet.LinLog dim_x = ScatterplotFacet.LinLog.LIN;
106
        @JsonProperty(ScatterplotFacet.DIM_Y)
3✔
107
        public ScatterplotFacet.LinLog dim_y = ScatterplotFacet.LinLog.LIN;
108
        @JsonProperty(ScatterplotFacet.ROTATION)
3✔
109
        public ScatterplotFacet.Rotation rotation = ScatterplotFacet.Rotation.NO_ROTATION;
110
        @JsonProperty(ScatterplotFacet.COLOR)
3✔
111
        public String color_str = "000000"; // TODO: Can this be simplified to set color directly (if we keep it)?
112
        @JsonProperty(ScatterplotFacet.BASE_COLOR)
3✔
113
        public String base_color_str = null;
114
        @JsonProperty(ScatterplotFacet.X_COLUMN_NAME)
3✔
115
        public String columnName_x = "";
116
        @JsonProperty(ScatterplotFacet.X_EXPRESSION)
3✔
117
        public String expression_x = "value";
118
        @JsonProperty(ScatterplotFacet.Y_COLUMN_NAME)
3✔
119
        public String columnName_y = "";
120
        @JsonProperty(ScatterplotFacet.Y_EXPRESSION)
4✔
121
        public String expression_y = "value";
122

123
    }
124

125
    public void draw(OutputStream output, Project project, Engine engine, PlotterConfig o) throws IOException {
126

127
        double min_x = 0;
×
128
        double min_y = 0;
×
129
        double max_x = 0;
×
130
        double max_y = 0;
×
131

132
        int columnIndex_x = 0;
×
133
        int columnIndex_y = 0;
×
134

135
        Evaluable eval_x = null;
×
136
        Evaluable eval_y = null;
×
137

138
        Color color = new Color(Integer.parseInt(o.color_str, 16));
×
139

140
        Color base_color = o.base_color_str != null ? new Color(Integer.parseInt(o.base_color_str, 16)) : null;
×
141

NEW
142
        if (!o.columnName_x.isEmpty()) {
×
143
            Column x_column = project.columnModel.getColumnByName(o.columnName_x);
×
144
            if (x_column != null) {
×
145
                columnIndex_x = x_column.getCellIndex();
×
146
            }
147
        } else {
×
148
            columnIndex_x = -1;
×
149
        }
150

151
        try {
152
            eval_x = MetaParser.parse(o.expression_x);
×
153
        } catch (ParsingException e) {
×
154
            logger.warn("error parsing expression", e);
×
155
        }
×
156

NEW
157
        if (!o.columnName_y.isEmpty()) {
×
158
            Column y_column = project.columnModel.getColumnByName(o.columnName_y);
×
159
            if (y_column != null) {
×
160
                columnIndex_y = y_column.getCellIndex();
×
161
            }
162
        } else {
×
163
            columnIndex_y = -1;
×
164
        }
165

166
        try {
167
            eval_y = MetaParser.parse(o.expression_y);
×
168
        } catch (ParsingException e) {
×
169
            logger.warn("error parsing expression", e);
×
170
        }
×
171

172
        NumericBinIndex index_x = null;
×
173
        NumericBinIndex index_y = null;
×
174

175
        Column column_x = project.columnModel.getColumnByName(o.columnName_x);
×
176
        if (column_x != null) {
×
177
            columnIndex_x = column_x.getCellIndex();
×
178
            index_x = ScatterplotFacet.getBinIndex(project, column_x, eval_x, o.expression_x);
×
179
            min_x = index_x.getMin();
×
180
            max_x = index_x.getMax();
×
181
        }
182

183
        Column column_y = project.columnModel.getColumnByName(o.columnName_y);
×
184
        if (column_y != null) {
×
185
            columnIndex_y = column_y.getCellIndex();
×
186
            index_y = ScatterplotFacet.getBinIndex(project, column_y, eval_y, o.expression_y);
×
187
            min_y = index_y.getMin();
×
188
            max_y = index_y.getMax();
×
189
        }
190

191
        if (index_x != null && index_y != null && index_x.isNumeric() && index_y.isNumeric()) {
×
192
            ScatterplotDrawingRowVisitor drawer = new ScatterplotDrawingRowVisitor(
×
193
                    columnIndex_x, columnIndex_y, min_x, max_x, min_y, max_y,
194
                    o.size, o.dim_x, o.dim_y, o.rotation, o.dot, color);
195

196
            if (base_color != null) {
×
197
                drawer.setColor(base_color);
×
198

199
                FilteredRows filteredRows = engine.getAllRows();
×
200
                filteredRows.accept(project, drawer);
×
201

202
                drawer.setColor(color);
×
203
            }
204

205
            {
206
                FilteredRows filteredRows = engine.getAllFilteredRows();
×
207
                filteredRows.accept(project, drawer);
×
208
            }
209

210
            ImageIO.write(drawer.getImage(), "png", output);
×
211
        } else {
×
212
            ImageIO.write(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR), "png", output);
×
213
        }
214

215
    }
×
216

217
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc