deeplearning4j/deeplearning4j-ui-parent/deeplearning4j-ui-components/src/main/typescript/org/deeplearning4j/ui/components/chart/ChartScatter.ts
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* * License for the specific language governing permissions and limitations
* * under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
class ChartScatter extends Chart implements Renderable {
private xData:number[][];
private yData:number[][];
private seriesNames:string[];
constructor(jsonStr:string) {
super(ComponentType.ChartScatter, jsonStr);
var json = JSON.parse(jsonStr);
if(!json["componentType"]) json = json[ComponentType[ComponentType.ChartScatter]];
this.xData = json['x'];
this.yData = json['y'];
this.seriesNames = json['seriesNames'];
}
render = (appendToObject:JQuery) => {
var nSeries:number = (!this.xData ? 0 : this.xData.length);
var s:StyleChart = this.getStyle();
var margin:Margin = Style.getMargins(s);
// Set the ranges
var xScale:d3.scale.Linear<number,number> = d3.scale.linear().range([0, margin.widthExMargins]);
var yScale:d3.scale.Linear<number,number> = d3.scale.linear().range([margin.heightExMargins, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(xScale)
.innerTickSize(-margin.heightExMargins) //used as grid line
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(yScale)
.innerTickSize(-margin.widthExMargins) //used as grid line
.orient("left").ticks(5);
if (this.suppressAxisHorizontal === true) xAxis.tickValues([]);
if (this.suppressAxisVertical === true) yAxis.tickValues([]);
// Adds the svg canvas
//TODO don't hardcode these colors/attributes...
var svg = d3.select("#" + appendToObject.attr("id"))
.append("svg")
.style("stroke-width", ( s && s.getStrokeWidth() ? s.getStrokeWidth() : 1))
.style("fill", "none")
.attr("width", s.getWidth())
.attr("height", s.getHeight())
.attr("padding", "20px")
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Scale the range of the chart
var xMin:number;
var xMax:number;
var yMin:number;
var yMax:number;
if (this.setXMin) xMin = this.setXMin;
else xMin = (this.xData ? TSUtils.min(this.xData) : 0);
if (this.setXMax) xMax = this.setXMax;
else xMax = (this.xData ? TSUtils.max(this.xData) : 1);
if (this.setYMin) yMin = this.setYMin;
else yMin = (this.yData ? TSUtils.min(this.yData) : 0);
if (this.setYMax) yMax = this.setYMax;
else yMax = (this.yData ? TSUtils.max(this.yData) : 1);
xScale.domain([xMin, xMax]);
yScale.domain([yMin, yMax]);
// Add the valueline path.
var defaultColor:Ordinal<string,string> = d3.scale.category10();
for (var i = 0; i < nSeries; i++) {
var xVals = this.xData[i];
var yVals = this.yData[i];
var data = xVals.map(function (d, i) {
return {'xPos': xVals[i], 'yPos': yVals[i]};
});
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.style("fill", (s && s.getSeriesColor(i) ? s.getSeriesColor(i) : defaultColor(String(i))))
.attr("r", (s && s.getPointSize() ? s.getPointSize() : ChartConstants.DEFAULT_CHART_POINT_SIZE))
.attr("cx", function (d) {
return xScale(d['xPos']);
})
.attr("cy", function (d) {
return yScale(d['yPos']);
});
}
// Add the X Axis
var xAxisNode = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + margin.heightExMargins + ")")
.style("stroke", "#000")
.style("stroke-width", (s != null && s.getAxisStrokeWidth() != null ? s.getAxisStrokeWidth() : ChartConstants.DEFAULT_AXIS_STROKE_WIDTH))
.style("fill", "none")
.call(xAxis);
xAxisNode.selectAll('text').style("stroke-width",0).style("fill","#000000");
if (this.gridVerticalStrokeWidth != null) xAxisNode.selectAll('.axis line').style({'stroke-width': this.gridVerticalStrokeWidth});
// Add the Y Axis
var yAxisNode = svg.append("g")
.attr("class", "y axis")
.style("stroke", "#000")
.style("stroke-width", (s != null && s.getAxisStrokeWidth() != null ? s.getAxisStrokeWidth() : ChartConstants.DEFAULT_AXIS_STROKE_WIDTH))
.style("fill", "none")
.call(yAxis);
yAxisNode.selectAll('text').style("stroke-width",0).style("fill","#000000");
if (this.gridHorizontalStrokeWidth != null) yAxisNode.selectAll('.axis line').style({'stroke-width': this.gridHorizontalStrokeWidth});
//Add legend (if present)
if (this.seriesNames && this.showLegend === true) {
var legendSpace = margin.widthExMargins / i;
for (var i = 0; i < nSeries; i++) {
var values = this.xData[i];
var yValues = this.yData[i];
var lastX = values[values.length - 1];
var lastY = yValues[yValues.length - 1];
var toDisplay;
if (!lastX || !lastY) toDisplay = this.seriesNames[i] + " (no data)";
else toDisplay = this.seriesNames[i] + " (" + lastX.toPrecision(5) + "," + lastY.toPrecision(5) + ")";
svg.append("text")
.attr("x", (legendSpace / 2) + i * legendSpace) // spacing
.attr("y", margin.heightExMargins + (margin.bottom / 2) + 5)
.attr("class", "legend") // style the legend
.style("fill", (s && s.getSeriesColor(i) ? s.getSeriesColor(i) : defaultColor(String(i))))
.text(toDisplay);
}
}
//Add title (if present)
if (this.title) {
var titleStyle: StyleText;
if(this.style) titleStyle = this.style.getTitleStyle();
Chart.appendTitle(svg, this.title, margin, titleStyle);
}
}
}